diff --git a/internal/commands/doctor_test.go b/internal/commands/doctor_test.go index 8cf201512..c2ef36e34 100644 --- a/internal/commands/doctor_test.go +++ b/internal/commands/doctor_test.go @@ -949,7 +949,7 @@ func TestCheckCodexIntegrationIncludesVersion(t *testing.T) { } } require.Equal(t, []string{"Codex Plugin", "Codex Plugin Version"}, names) - assert.Equal(t, 1, strings.Count(readCodexSetupCalls(t, logPath), "plugin list --available --json")) + assert.Equal(t, 1, strings.Count(readCodexSetupCalls(t, logPath), "plugin list --available --json --marketplace 37signals")) } func TestCheckCodexIntegrationWarnsOnVersionMismatch(t *testing.T) { diff --git a/internal/commands/wizard_codex_test.go b/internal/commands/wizard_codex_test.go index 522d6ef53..15f77ddbe 100644 --- a/internal/commands/wizard_codex_test.go +++ b/internal/commands/wizard_codex_test.go @@ -44,7 +44,7 @@ func TestSetupCodexFreshInstallCommandOrder(t *testing.T) { assertCallOrder(t, calls, "plugin marketplace add basecamp/claude-plugins --json", "plugin add basecamp-cli@37signals --json", - "plugin list --available --json", + "plugin list --available --json --marketplace 37signals", ) } @@ -96,7 +96,7 @@ func TestSetupCodexReplacesLegacyCLIPlugin(t *testing.T) { assertCallOrder(t, readCodexSetupCalls(t, logPath), "plugin add basecamp-cli@37signals --json", "plugin remove basecamp@37signals --json", - "plugin list --available --json", + "plugin list --available --json --marketplace 37signals", ) } @@ -324,7 +324,10 @@ func installCodexStub(t *testing.T, options codexStubOptions) string { " \"plugin remove basecamp@37signals --json\")\n" + " if [ " + boolShell(options.legacyRemoveFailure) + " = 1 ]; then echo 'remove failure' >&2; exit 1; fi\n" + " echo removed > \"" + legacyPath + "\"; echo '{}'; exit 0 ;;\n" + - " \"plugin list --available --json\")\n" + + // Unfiltered, a signed-in Codex also fetches the remote openai-curated + // catalog, which on a cold cache outlasts every probe deadline, so the + // stub answers only the listing scoped to our marketplace. + " \"plugin list --available --json --marketplace 37signals\")\n" + " enabled=true; if [ " + boolShell(options.pluginDisabled) + " = 1 ]; then enabled=false; fi\n" + " legacy_state=''; [ -f \"" + legacyPath + "\" ] && read -r legacy_state < \"" + legacyPath + "\"\n" + " if [ \"$legacy_state\" = installed ]; then legacy='{\"pluginId\":\"basecamp@37signals\",\"version\":\"0.11.0\",\"installed\":true,\"enabled\":true},'; else legacy=''; fi\n" + diff --git a/internal/harness/codex.go b/internal/harness/codex.go index 9c7776ef2..2d051b468 100644 --- a/internal/harness/codex.go +++ b/internal/harness/codex.go @@ -299,7 +299,10 @@ func queryCodexPlugin(parent context.Context) (codexPluginState, bool, error) { } ctx, cancel := context.WithTimeout(parent, codexQueryTimeout) defer cancel() - data, err := runCodexCommand(ctx, path, "plugin", "list", "--available", "--json") + // Unscoped, a signed-in Codex first fetches the remote openai-curated + // catalog, about 35s on a cold cache. The deadline kills it before the + // cache is written, so every later probe would time out too. + data, err := runCodexCommand(ctx, path, "plugin", "list", "--available", "--json", "--marketplace", CodexMarketplaceName) if err != nil { return codexPluginState{}, false, fmt.Errorf("query Codex plugins: %w", err) } @@ -362,7 +365,7 @@ func codexQueryFailure(name string, err error) *StatusCheck { Name: name, Status: "fail", Message: "Cannot query Codex plugins", - Hint: "Run `codex plugin list --available --json`, then: basecamp setup codex", + Hint: "Run `codex plugin list --available --json --marketplace " + CodexMarketplaceName + "`, then: basecamp setup codex", } } message := "Cannot query Codex plugins" @@ -373,6 +376,6 @@ func codexQueryFailure(name string, err error) *StatusCheck { Name: name, Status: "fail", Message: message, - Hint: "Run `codex plugin list --available --json`, then: basecamp setup codex", + Hint: "Run `codex plugin list --available --json --marketplace " + CodexMarketplaceName + "`, then: basecamp setup codex", } } diff --git a/internal/harness/codex_test.go b/internal/harness/codex_test.go index a37255be5..334836ab0 100644 --- a/internal/harness/codex_test.go +++ b/internal/harness/codex_test.go @@ -90,7 +90,7 @@ func TestCheckCodexPluginCommandFailure(t *testing.T) { assert.Equal(t, "fail", check.Status) assert.Contains(t, check.Message, "Cannot query") - assert.Contains(t, check.Hint, "codex plugin list --available --json") + assert.Contains(t, check.Hint, "codex plugin list --available --json --marketplace 37signals") } func TestCheckCodexPluginVersionMatching(t *testing.T) { @@ -157,7 +157,7 @@ func TestCheckCodexPluginUsesSupportedJSONCommand(t *testing.T) { assert.Equal(t, "pass", check.Status) assert.Equal(t, "/usr/local/bin/codex", gotPath) - assert.Equal(t, []string{"plugin", "list", "--available", "--json"}, gotArgs) + assert.Equal(t, []string{"plugin", "list", "--available", "--json", "--marketplace", "37signals"}, gotArgs) } func TestCodexAgentInfoWiring(t *testing.T) { @@ -198,7 +198,7 @@ func stubCodexList(t *testing.T, output string, commandErr error) { original := runCodexCommand runCodexCommand = func(_ context.Context, path string, args ...string) ([]byte, error) { assert.Equal(t, "/usr/local/bin/codex", path) - assert.Equal(t, []string{"plugin", "list", "--available", "--json"}, args) + assert.Equal(t, []string{"plugin", "list", "--available", "--json", "--marketplace", "37signals"}, args) return []byte(output), commandErr } t.Cleanup(func() { runCodexCommand = original })