Skip to content

Commit d953987

Browse files
authored
Scope the Codex plugin probe to the 37signals marketplace (#995)
* Model a signed-in Codex in the setup and doctor tests A signed-in Codex answers `plugin list --available --json` only after it fetches the remote openai-curated catalog, which on a cold cache takes about 35 seconds. The Codex stub now answers only the listing scoped to the 37signals marketplace, and these tests fail the way a new machine does: setup installs the plugin, then reports "Cannot query Codex plugins". * Scope the Codex plugin probe to the 37signals marketplace On a signed-in Codex, `codex plugin list --available --json` first fetches the remote openai-curated catalog: about 35 seconds and 10,000+ plugins on a cold cache. The probe's 5 second deadline killed it before Codex wrote that cache, so setup verification and doctor failed with "Cannot query Codex plugins" on every run, even though the plugin had installed. Asking for the 37signals marketplace alone answers in tens of milliseconds and returns both plugin IDs the checks read. `--marketplace` has been on `codex plugin list` since before `--available`, so every Codex that ran the old probe runs this one.
1 parent df22084 commit d953987

4 files changed

Lines changed: 16 additions & 10 deletions

File tree

‎internal/commands/doctor_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ func TestCheckCodexIntegrationIncludesVersion(t *testing.T) {
949949
}
950950
}
951951
require.Equal(t, []string{"Codex Plugin", "Codex Plugin Version"}, names)
952-
assert.Equal(t, 1, strings.Count(readCodexSetupCalls(t, logPath), "plugin list --available --json"))
952+
assert.Equal(t, 1, strings.Count(readCodexSetupCalls(t, logPath), "plugin list --available --json --marketplace 37signals"))
953953
}
954954

955955
func TestCheckCodexIntegrationWarnsOnVersionMismatch(t *testing.T) {

‎internal/commands/wizard_codex_test.go‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestSetupCodexFreshInstallCommandOrder(t *testing.T) {
4444
assertCallOrder(t, calls,
4545
"plugin marketplace add basecamp/claude-plugins --json",
4646
"plugin add basecamp-cli@37signals --json",
47-
"plugin list --available --json",
47+
"plugin list --available --json --marketplace 37signals",
4848
)
4949
}
5050

@@ -96,7 +96,7 @@ func TestSetupCodexReplacesLegacyCLIPlugin(t *testing.T) {
9696
assertCallOrder(t, readCodexSetupCalls(t, logPath),
9797
"plugin add basecamp-cli@37signals --json",
9898
"plugin remove basecamp@37signals --json",
99-
"plugin list --available --json",
99+
"plugin list --available --json --marketplace 37signals",
100100
)
101101
}
102102

@@ -324,7 +324,10 @@ func installCodexStub(t *testing.T, options codexStubOptions) string {
324324
" \"plugin remove basecamp@37signals --json\")\n" +
325325
" if [ " + boolShell(options.legacyRemoveFailure) + " = 1 ]; then echo 'remove failure' >&2; exit 1; fi\n" +
326326
" echo removed > \"" + legacyPath + "\"; echo '{}'; exit 0 ;;\n" +
327-
" \"plugin list --available --json\")\n" +
327+
// Unfiltered, a signed-in Codex also fetches the remote openai-curated
328+
// catalog, which on a cold cache outlasts every probe deadline, so the
329+
// stub answers only the listing scoped to our marketplace.
330+
" \"plugin list --available --json --marketplace 37signals\")\n" +
328331
" enabled=true; if [ " + boolShell(options.pluginDisabled) + " = 1 ]; then enabled=false; fi\n" +
329332
" legacy_state=''; [ -f \"" + legacyPath + "\" ] && read -r legacy_state < \"" + legacyPath + "\"\n" +
330333
" if [ \"$legacy_state\" = installed ]; then legacy='{\"pluginId\":\"basecamp@37signals\",\"version\":\"0.11.0\",\"installed\":true,\"enabled\":true},'; else legacy=''; fi\n" +

‎internal/harness/codex.go‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,10 @@ func queryCodexPlugin(parent context.Context) (codexPluginState, bool, error) {
299299
}
300300
ctx, cancel := context.WithTimeout(parent, codexQueryTimeout)
301301
defer cancel()
302-
data, err := runCodexCommand(ctx, path, "plugin", "list", "--available", "--json")
302+
// Unscoped, a signed-in Codex first fetches the remote openai-curated
303+
// catalog, about 35s on a cold cache. The deadline kills it before the
304+
// cache is written, so every later probe would time out too.
305+
data, err := runCodexCommand(ctx, path, "plugin", "list", "--available", "--json", "--marketplace", CodexMarketplaceName)
303306
if err != nil {
304307
return codexPluginState{}, false, fmt.Errorf("query Codex plugins: %w", err)
305308
}
@@ -362,7 +365,7 @@ func codexQueryFailure(name string, err error) *StatusCheck {
362365
Name: name,
363366
Status: "fail",
364367
Message: "Cannot query Codex plugins",
365-
Hint: "Run `codex plugin list --available --json`, then: basecamp setup codex",
368+
Hint: "Run `codex plugin list --available --json --marketplace " + CodexMarketplaceName + "`, then: basecamp setup codex",
366369
}
367370
}
368371
message := "Cannot query Codex plugins"
@@ -373,6 +376,6 @@ func codexQueryFailure(name string, err error) *StatusCheck {
373376
Name: name,
374377
Status: "fail",
375378
Message: message,
376-
Hint: "Run `codex plugin list --available --json`, then: basecamp setup codex",
379+
Hint: "Run `codex plugin list --available --json --marketplace " + CodexMarketplaceName + "`, then: basecamp setup codex",
377380
}
378381
}

‎internal/harness/codex_test.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func TestCheckCodexPluginCommandFailure(t *testing.T) {
9090

9191
assert.Equal(t, "fail", check.Status)
9292
assert.Contains(t, check.Message, "Cannot query")
93-
assert.Contains(t, check.Hint, "codex plugin list --available --json")
93+
assert.Contains(t, check.Hint, "codex plugin list --available --json --marketplace 37signals")
9494
}
9595

9696
func TestCheckCodexPluginVersionMatching(t *testing.T) {
@@ -157,7 +157,7 @@ func TestCheckCodexPluginUsesSupportedJSONCommand(t *testing.T) {
157157

158158
assert.Equal(t, "pass", check.Status)
159159
assert.Equal(t, "/usr/local/bin/codex", gotPath)
160-
assert.Equal(t, []string{"plugin", "list", "--available", "--json"}, gotArgs)
160+
assert.Equal(t, []string{"plugin", "list", "--available", "--json", "--marketplace", "37signals"}, gotArgs)
161161
}
162162

163163
func TestCodexAgentInfoWiring(t *testing.T) {
@@ -198,7 +198,7 @@ func stubCodexList(t *testing.T, output string, commandErr error) {
198198
original := runCodexCommand
199199
runCodexCommand = func(_ context.Context, path string, args ...string) ([]byte, error) {
200200
assert.Equal(t, "/usr/local/bin/codex", path)
201-
assert.Equal(t, []string{"plugin", "list", "--available", "--json"}, args)
201+
assert.Equal(t, []string{"plugin", "list", "--available", "--json", "--marketplace", "37signals"}, args)
202202
return []byte(output), commandErr
203203
}
204204
t.Cleanup(func() { runCodexCommand = original })

0 commit comments

Comments
 (0)