@wrongstack/cli 0.299.0 → 0.300.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/{acp-S4LZQB66.js → acp-IDNS2YVE.js} +3 -3
  2. package/dist/{auth-G5YKKSRH.js → auth-6T6ZOT2R.js} +5 -5
  3. package/dist/auth-menu/loopback-server.d.ts +41 -55
  4. package/dist/auth-menu/openai-codex-oauth.d.ts +36 -123
  5. package/dist/auto-discover-providers-DRW4OZHG.js +8 -0
  6. package/dist/boot/dispatch-webui.d.ts +3 -0
  7. package/dist/{chunk-PEKUKUCH.js → chunk-3MWUZMOL.js} +19 -12
  8. package/dist/{chunk-TGDHN4LM.js → chunk-4GVH7V3S.js} +4 -24
  9. package/dist/{chunk-4U3DMA2Y.js → chunk-4WJKVQJY.js} +15 -20
  10. package/dist/{chunk-2SLSUDYS.js → chunk-GYDQABMA.js} +269 -180
  11. package/dist/{chunk-4HH3YGG4.js → chunk-MRLYDHQ7.js} +8 -5
  12. package/dist/{chunk-66T43Q4Y.js → chunk-N7ULWBO5.js} +4 -3
  13. package/dist/{chunk-ZNZJ34RF.js → chunk-PEMN4T5O.js} +2 -2
  14. package/dist/chunk-PYTFS4MC.js +238 -0
  15. package/dist/{chunk-L5CE52XW.js → chunk-V76R7DC5.js} +25 -19
  16. package/dist/{cli-main-3MQDFSYK.js → cli-main-7B3G2YSG.js} +528 -156
  17. package/dist/{execution-ANOYMWPO.js → execution-SHGM7BVC.js} +50 -5
  18. package/dist/fleet/budget-source.d.ts +18 -0
  19. package/dist/fleet/host-status.d.ts +31 -0
  20. package/dist/fleet/host-types.d.ts +8 -0
  21. package/dist/fleet/host.d.ts +5 -0
  22. package/dist/{hq-BDO56CX6.js → hq-DVDD4XPV.js} +22 -3
  23. package/dist/hq-server/auth-state.d.ts +29 -1
  24. package/dist/hq-server/auth.d.ts +16 -0
  25. package/dist/hq-server/client-address.d.ts +56 -0
  26. package/dist/hq-server/login-attempt-store.d.ts +30 -10
  27. package/dist/hq-server/routes/auth-handlers.d.ts +66 -26
  28. package/dist/hq-server/routes.d.ts +13 -0
  29. package/dist/hq-server/types.d.ts +10 -0
  30. package/dist/{hq-server-RJLJJZU7.js → hq-server-DQHLNHDL.js} +2 -2
  31. package/dist/hq-server.d.ts +19 -0
  32. package/dist/index.js +20 -20
  33. package/dist/{modeldiag-HENYJ4N7.js → modeldiag-YOQZHBQV.js} +2 -2
  34. package/dist/{plugin-usage-VTL7ZTVC.js → plugin-usage-EOG4ET4S.js} +2 -2
  35. package/dist/{plugins-ETY6W3DC.js → plugins-56CMWMPR.js} +3 -3
  36. package/dist/{providers-models-GDELBHZK.js → providers-models-K6RT7ABI.js} +34 -9
  37. package/dist/slash-commands/command-context.d.ts +19 -0
  38. package/dist/webui-server/setup-events.d.ts +17 -0
  39. package/dist/webui-server/terminal-log-view.d.ts +29 -12
  40. package/dist/{webui-server-PCLSXX6L.js → webui-server-G3EKFXLL.js} +109 -38
  41. package/dist/webui-server-options.d.ts +16 -0
  42. package/dist/wiring/brain-and-orchestration.d.ts +3 -0
  43. package/dist/wiring/director-setup.d.ts +4 -0
  44. package/dist/wiring/fallback-gate.d.ts +31 -0
  45. package/dist/wiring/fleet-command-handlers.d.ts +1 -1
  46. package/dist/wiring/plugins.d.ts +2 -8
  47. package/dist/wiring/provider-utility-tools.d.ts +26 -1
  48. package/package.json +23 -23
  49. package/dist/chunk-GO3TJICK.js +0 -489
@@ -3,8 +3,8 @@ import {
3
3
  loadCachedAcpRegistry,
4
4
  refreshAcpRegistry,
5
5
  setupProvider
6
- } from "./chunk-4HH3YGG4.js";
7
- import "./chunk-GO3TJICK.js";
6
+ } from "./chunk-MRLYDHQ7.js";
7
+ import "./chunk-PYTFS4MC.js";
8
8
  import "./chunk-PJHET45A.js";
9
9
  import "./chunk-7OCVIDC7.js";
10
10
 
@@ -889,4 +889,4 @@ ${renderAcpBenchText(result)}
889
889
  export {
890
890
  acpCmd
891
891
  };
892
- //# sourceMappingURL=acp-S4LZQB66.js.map
892
+ //# sourceMappingURL=acp-IDNS2YVE.js.map
@@ -13,12 +13,12 @@ import {
13
13
  runOAuthLoginKind,
14
14
  runOAuthLoginMenu,
15
15
  validateFamily
16
- } from "./chunk-L5CE52XW.js";
16
+ } from "./chunk-V76R7DC5.js";
17
17
  import {
18
18
  parseAuthFlags
19
- } from "./chunk-ZNZJ34RF.js";
20
- import "./chunk-66T43Q4Y.js";
21
- import "./chunk-GO3TJICK.js";
19
+ } from "./chunk-PEMN4T5O.js";
20
+ import "./chunk-N7ULWBO5.js";
21
+ import "./chunk-PYTFS4MC.js";
22
22
  import {
23
23
  loadConfigProviders,
24
24
  maskedKey,
@@ -501,4 +501,4 @@ async function runAuthRemove(deps, providerId) {
501
501
  export {
502
502
  authCmd
503
503
  };
504
- //# sourceMappingURL=auth-G5YKKSRH.js.map
504
+ //# sourceMappingURL=auth-6T6ZOT2R.js.map
@@ -1,40 +1,29 @@
1
1
  /**
2
- * Shared loopback OAuth server HTTP callback listener for OAuth flows.
2
+ * Terminal-side adapter for the OAuth loopback callback server.
3
3
  *
4
- * Both the Codex ("Sign in with ChatGPT") and Anthropic ("Sign in with Claude")
5
- * OAuth flows start a local HTTP server on a loopback port to receive the
6
- * authorization code redirect from the provider's browser flow. Before this
7
- * module existed, each flow duplicated:
4
+ * The server itself HTTP listener, port fallback, `state` validation, the
5
+ * static callback page and its CSP/anti-framing headers, abort handling lives
6
+ * in `@wrongstack/providers/oauth`. This file used to carry a second, complete
7
+ * implementation of all of it.
8
8
  *
9
- * - `escapeHtml` / `callbackHtml` render success/error HTML pages
10
- * - `openBrowser` best-effort platform browser opener
11
- * - `startLoopbackServer` HTTP server with port fallback, abort handling,
12
- * state validation, and callback URL parsing
9
+ * The copies were not identical, and the difference mattered: this one
10
+ * registered its abort listener with `{ once: true }` and never removed it, so
11
+ * every completed login leaked a listener on the caller's long-lived
12
+ * AbortSignal (the TUI hands the same signal to each attempt). The providers
13
+ * copy had the `detachAbort` cleanup. Neither file had any way to notice the
14
+ * other had drifted.
13
15
  *
14
- * Usage:
15
- * ```ts
16
- * import { startLoopbackServer, callbackHtml, openBrowser } from './loopback-server.js';
16
+ * What legitimately stays here is {@link openBrowser}: spawning the platform
17
+ * browser is terminal-only behaviour, and the headless WebUI flows deliberately
18
+ * hand the authorize URL to the client instead of opening anything.
17
19
  *
18
- * const server = await startLoopbackServer(state, [1455, 1457], signal);
19
- * const code = await server.waitForCode();
20
- * ```
20
+ * The positional signature below is preserved so the three CLI OAuth flows and
21
+ * their tests keep working unchanged.
21
22
  *
22
23
  * @module auth-menu/loopback-server
23
24
  */
24
- /**
25
- * Render a simple dark-themed HTML response for the browser callback page.
26
- * Shared by all loopback-based OAuth flows so the success/error page styling
27
- * is consistent and the HTML generation isn't duplicated.
28
- */
29
- export declare function callbackHtml(ok: boolean, message: string): string;
30
- /**
31
- * Open a URL in the user's default browser. Best-effort — never throws.
32
- *
33
- * - Windows: `cmd /c start "" <url>`
34
- * - macOS: `open <url>`
35
- * - Linux: `xdg-open <url>`
36
- */
37
- export declare function openBrowser(url: string): void;
25
+ import { type LoopbackServer } from '@wrongstack/providers/oauth';
26
+ export { callbackHtml, type LoopbackServer } from '@wrongstack/providers/oauth';
38
27
  /** Result of waiting for the OAuth callback. */
39
28
  export interface LoopbackCallback {
40
29
  /** The authorization code from the provider. */
@@ -42,35 +31,32 @@ export interface LoopbackCallback {
42
31
  /** The OAuth state parameter from the callback (validated against expected). */
43
32
  state: string;
44
33
  }
45
- export interface LoopbackServer {
46
- /**
47
- * Wait for the browser to redirect back with the authorization code.
48
- * Resolves with the code and state, or `null` if cancelled/failed.
49
- */
50
- waitForCode(): Promise<LoopbackCallback | null>;
51
- /** Close the HTTP server and release the port. */
52
- close(): void;
53
- /** True when the server bound to a registered callback port. */
54
- readonly bound: boolean;
55
- /** The actual port the server is listening on (matches the authorize URL redirect_uri). */
56
- readonly port: number;
57
- }
58
34
  /**
59
- * Start a loopback HTTP server for OAuth authorization code callback.
60
- *
61
- * The server listens on the first available port from `ports`. If all ports
62
- * are busy, `bound` is `false` and the caller should fall back to a manual
63
- * paste prompt.
35
+ * Open a URL in the user's default browser. Best-effort — never throws.
64
36
  *
65
- * When `signal` aborts (user pressed Ctrl+C or TUI Esc), the pending
66
- * `waitForCode()` promise resolves to `null` and the server is torn down
67
- * so the login flow can return promptly.
37
+ * - Windows: `cmd /c start "" <url>`
38
+ * - macOS: `open <url>`
39
+ * - Linux: `xdg-open <url>`
68
40
  *
69
- * @param expectedState - The OAuth `state` value to validate against the callback
70
- * @param ports - Ordered list of TCP ports to try (first available wins)
71
- * @param signal - Optional external cancellation signal
72
- * @param callbackPath - The URL path the provider redirects to (default `/auth/callback`)
73
- * @param redirectHost - The hostname to listen on (default `127.0.0.1`)
41
+ * Not `detached`: the CLI flows wait on the loopback callback in the
42
+ * foreground, so the opener is a short-lived handoff. (The WebUI server's
43
+ * `openBrowser` is deliberately different it detaches and registers the pid
44
+ * as protected because it launches a browser the session must outlive.)
45
+ */
46
+ export declare function openBrowser(url: string): void;
47
+ /**
48
+ * Start a loopback HTTP server for the OAuth authorization-code callback.
49
+ *
50
+ * Binds the first available port from `ports`. If all are busy, `bound` is
51
+ * `false` and the caller falls back to a manual paste prompt. When `signal`
52
+ * aborts (Ctrl+C, TUI Esc), the pending `waitForCode()` resolves to `null` and
53
+ * the server is torn down.
54
+ *
55
+ * @param expectedState - the OAuth `state` to validate the callback against
56
+ * @param ports - ordered ports to try; first available wins
57
+ * @param signal - optional external cancellation
58
+ * @param callbackPath - path the provider redirects to (default `/auth/callback`)
59
+ * @param redirectHost - hostname to listen on (default `127.0.0.1`)
74
60
  */
75
61
  export declare function startLoopbackServer(expectedState: string, ports: readonly number[], signal?: AbortSignal, callbackPath?: string, redirectHost?: string): Promise<LoopbackServer>;
76
62
  //# sourceMappingURL=loopback-server.d.ts.map
@@ -1,138 +1,51 @@
1
1
  /**
2
- * OpenAI Codex — "Sign in with ChatGPT" OAuth (Authorization Code + PKCE).
2
+ * OpenAI Codex — "Sign in with ChatGPT", terminal flow.
3
3
  *
4
- * This is the ONLY login flow that actually works for using a ChatGPT
5
- * Plus/Pro/Team subscription programmatically. It mirrors the real Codex CLI:
4
+ * This module is now only the *terminal* half of the login: the rendered
5
+ * instructions, the SIGINT / TUI-signal cancellation contract, the
6
+ * manual-paste prompt, and writing the resulting tokens into the encrypted
7
+ * profile config.
6
8
  *
7
- * 1. Generate a PKCE verifier/challenge (S256) + CSRF state.
8
- * 2. Start a loopback HTTP server on http://localhost:1455/auth/callback.
9
- * 3. Open the browser to https://auth.openai.com/oauth/authorize.
10
- * 4. The browser redirects back to the loopback with `?code=...&state=...`.
11
- * 5. Exchange the code at https://auth.openai.com/oauth/token for an
12
- * access_token + refresh_token (and an id_token carrying the account id).
13
- * 6. Store the tokens in the vault as an `oauth` ProviderApiKey.
9
+ * The protocol itself — client id, endpoints, PKCE, the authorize URL, the
10
+ * token exchange and refresh, the JWT account-id claim, and model discovery —
11
+ * lives in `@wrongstack/providers/oauth`. It used to live here AND in
12
+ * `providers/src/oauth/chatgpt.ts` (the headless WebUI flow) AND in
13
+ * `providers/src/openai-codex.ts` (the runtime refresh path), whose header
14
+ * said outright that it was mirroring this file.
14
15
  *
15
- * The resulting access token is a JWT whose `https://api.openai.com/auth`
16
- * claim contains `chatgpt_account_id`. Requests then go to the ChatGPT
17
- * backend (`chatgpt.com/backend-api/codex/responses`) NOT api.openai.com
18
- * which is what the `openai-codex` wire family handles. The standard
19
- * API-key flow (`family: openai` api.openai.com/chat/completions) is left
20
- * completely untouched.
16
+ * Three copies is what let the OAuth abort-listener leak sit in two of them
17
+ * while a third flow in this same directory was already correct — nothing
18
+ * forced them to agree, so a fix applied to one silently left the others
19
+ * broken. A rotated client id or a changed token-endpoint contract carries the
20
+ * same risk, with the failure being "one surface stops authenticating" rather
21
+ * than a compile error.
21
22
  *
22
- * NOTE: a ChatGPT web *session cookie* is NOT an API token. The old
23
- * device-code + cookie-scraping attempts could never produce a usable
24
- * credential; this module replaces them.
23
+ * The protocol constants live in `providers` (not here) because the dependency
24
+ * edge runs `cli providers core`: the runtime provider needs them to
25
+ * refresh a token and must never import from `cli`.
26
+ *
27
+ * Everything the protocol layer defines is re-exported below, so existing
28
+ * importers (`auth-menu/index.ts`, `wiring/provider.ts`, the CLI tests) keep
29
+ * working against this module unchanged.
25
30
  */
31
+ import { buildCodexAuthorizeUrl, exchangeCodexAuthorizationCode, refreshCodexTokens } from '@wrongstack/providers/oauth';
26
32
  import { type LoopbackServer } from './loopback-server.js';
27
- import { type ModelsRegistry } from '@wrongstack/core/types';
28
33
  import type { AuthMenuDeps } from './types.js';
29
- /** Canonical provider id under which ChatGPT-login credentials are stored. */
30
- export declare const CODEX_PROVIDER_ID = "openai-codex";
31
- /** Default ChatGPT backend base. The wire family appends `/codex/responses`. */
32
- export declare const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
33
- /**
34
- * Codex-specific compatibility wrapper retained for callers that imported the
35
- * helper from this module before the shared OAuth loopback server extraction.
36
- */
37
- export declare function startLoopbackServer(expectedState: string, signal?: AbortSignal): Promise<LoopbackServer>;
38
- /**
39
- * Fallback model list used when BOTH the live backend AND the models.dev
40
- * catalog are unreachable. This should never happen in practice — the
41
- * catalog is cached locally and the only scenario where both fail is a
42
- * fresh install with no network on first login.
43
- *
44
- * The primary resolution order in `resolveCodexModels()` is:
45
- * 1. Live backend (`GET /models`)
46
- * 2. models.dev catalog (`family: gpt-codex*` under `openai` provider)
47
- * 3. This inline fallback
48
- */
49
- export interface CodexTokens {
50
- access: string;
51
- refresh: string;
52
- /** Absolute expiry in epoch milliseconds. */
53
- expires: number;
54
- idToken?: string | undefined;
55
- }
56
- export interface Pkce {
57
- verifier: string;
58
- challenge: string;
59
- }
60
- /** Generate a PKCE verifier + S256 challenge. */
61
- export declare function generatePkce(): Pkce;
62
- /** Build the full authorize URL with all Codex-required query params. */
63
- export declare function buildAuthorizeUrl(challenge: string, state: string, port?: number): string;
64
- /**
65
- * Extract `chatgpt_account_id` from an access/id token JWT. Returns null when
66
- * the token isn't a JWT or lacks the claim (the caller decides whether that
67
- * is fatal — for login it is; for a cached display value it isn't).
68
- */
69
- export declare function extractAccountId(token: string): string | null;
70
- /**
71
- * Recommended Codex models for ChatGPT sign-in. Live backend discovery
72
- * wins; these values are only used when the backend/catalog cannot
73
- * provide a list. Derived from `CODEX_MODELS` in core (the single source of
74
- * truth for Codex id/name/description) — used both by OAuth login
75
- * (resolveCodexModels → tier 3) and by the provider-boot synthesis
76
- * path (wiring/provider.ts when the catalog is unavailable).
77
- */
78
- export declare const FALLBACK_CODEX_MODELS: ReadonlyArray<{
79
- id: string;
80
- name: string;
81
- }>;
82
- /** Families in the models.dev catalog that indicate Codex / Responses API compatibility. */
83
- export declare const CODEX_CATALOG_FAMILIES: Set<string>;
84
- export declare function fallbackCodexModelIds(): string[];
85
- export declare function fallbackCodexProviderModels(): Array<{
86
- id: string;
87
- name: string;
88
- }>;
89
- /**
90
- * Filter a list of available model ids down to the ones that are still
91
- * current for ChatGPT sign-in (i.e. present in FALLBACK_CODEX_MODELS).
92
- * Used by both OAuth login (to drop deprecated ids from the live /models
93
- * response) and by the provider-boot synthesis (to pick current ids out
94
- * of the models.dev catalog).
95
- */
96
- export declare function filterCurrentCodexModelIds(ids: Iterable<string>): string[];
97
- export declare function isCodexCatalogModel(model: {
98
- family?: string | undefined;
99
- }): boolean;
100
- /**
101
- * Resolve the list of available Codex models using a 3-tier fallback chain:
102
- *
103
- * 1. **Live backend** — `fetchCodexModels()` hits `GET <baseUrl>/models`
104
- * and keeps only current Codex model ids.
105
- * 2. **models.dev catalog** — queries the `openai` provider in the cached
106
- * models.dev registry and picks models whose `family` is `gpt-codex` or
107
- * `gpt-codex-spark` and whose ids are current for ChatGPT sign-in.
108
- * 3. **Inline fallback** — a minimal list of documented mainstream models
109
- * (`gpt-5.6-sol`, `gpt-5.5`, `gpt-5.6-terra`, `gpt-5.6-luna`,
110
- * `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex-spark`).
111
- *
112
- * The live/catalog tiers gate account availability, while the shared current
113
- * model list filters out deprecated ChatGPT sign-in ids before config is saved.
114
- */
115
- export declare function resolveCodexModels(modelsRegistry: ModelsRegistry, accessToken: Promise<string> | string, baseUrl?: string | undefined, signal?: AbortSignal): Promise<string[]>;
34
+ export { CODEX_BASE_URL, CODEX_CATALOG_FAMILIES, CODEX_PROVIDER_ID, type CodexTokens, extractAccountId, FALLBACK_CODEX_MODELS, fallbackCodexModelIds, fallbackCodexProviderModels, fetchCodexModels, filterCurrentCodexModelIds, generatePkce, isCodexCatalogModel, parseAuthorizationInput, type Pkce, resolveCodexModels, } from '@wrongstack/providers/oauth';
35
+ /** Build the Codex authorize URL. Alias kept for the historical local name. */
36
+ export declare const buildAuthorizeUrl: typeof buildCodexAuthorizeUrl;
37
+ /** Exchange an authorization code (+ PKCE verifier) for tokens. */
38
+ export declare const exchangeAuthorizationCode: typeof exchangeCodexAuthorizationCode;
116
39
  /**
117
- * Fetch the account's available Codex model ids live from the ChatGPT backend.
118
- *
119
- * Hits `GET <baseUrl>/models` — the standard OpenAI-compatible model listing
120
- * endpoint, which the Codex backend may expose. Best-effort: returns an empty
121
- * array on any failure so login still succeeds with the hardcoded fallback.
40
+ * Refresh an expired access token. Alias kept for the historical local name;
41
+ * `wiring/provider.ts` and the CLI tests import it from here.
122
42
  */
123
- export declare function fetchCodexModels(accessToken: string, baseUrl?: string | undefined, signal?: AbortSignal): Promise<string[]>;
124
- /** Exchange an authorization code (+ PKCE verifier) for tokens. */
125
- export declare function exchangeAuthorizationCode(code: string, verifier: string, signal?: AbortSignal, port?: number): Promise<CodexTokens>;
43
+ export declare const refreshCodexToken: typeof refreshCodexTokens;
126
44
  /**
127
- * Refresh an expired access token using the stored refresh token. Exported for
128
- * the `openai-codex` provider, which calls it transparently before requests.
45
+ * Codex-specific loopback wrapper. Stays CLI-side because this flow pairs it
46
+ * with {@link openBrowser}, which is terminal-only.
129
47
  */
130
- export declare function refreshCodexToken(refreshToken: string, signal?: AbortSignal): Promise<CodexTokens>;
131
- /** Parse a pasted authorization code or full redirect URL into { code, state }. */
132
- export declare function parseAuthorizationInput(input: string): {
133
- code?: string | undefined;
134
- state?: string | undefined;
135
- };
48
+ export declare function startLoopbackServer(expectedState: string, signal?: AbortSignal): Promise<LoopbackServer>;
136
49
  export interface CodexLoginOptions {
137
50
  /** Storage provider id. Defaults to the canonical `openai-codex`. */
138
51
  providerId?: string;
@@ -0,0 +1,8 @@
1
+ import {
2
+ discoverAndMergeProviders
3
+ } from "./chunk-4GVH7V3S.js";
4
+ import "./chunk-7OCVIDC7.js";
5
+ export {
6
+ discoverAndMergeProviders
7
+ };
8
+ //# sourceMappingURL=auto-discover-providers-DRW4OZHG.js.map
@@ -22,6 +22,7 @@ import type { Config, MemoryPort, ModelsRegistry, ModeStore, SessionStore, Sessi
22
22
  import type { MCPRegistry } from '@wrongstack/mcp';
23
23
  import type { TerminalRenderer } from '../renderer.js';
24
24
  import type { AutonomyMode } from '../services/autonomy-mode.js';
25
+ import type { CliWebUIOptions } from '../webui-server-options.js';
25
26
  export interface WebUIDispatchContext {
26
27
  agent: Agent;
27
28
  events: EventBus;
@@ -96,6 +97,8 @@ export interface WebUIDispatchContext {
96
97
  error?: string | undefined;
97
98
  }) => void | Promise<void>) | undefined;
98
99
  }) => Promise<string>) | undefined;
100
+ /** Live fleet budget for WebUI concurrency/spawn gauges (issue #323). */
101
+ getFleetBudget?: CliWebUIOptions['getFleetBudget'];
99
102
  }
100
103
  /**
101
104
  * Run the WebUI server and block until it shuts down.
@@ -2,6 +2,9 @@
2
2
  // src/plugin-management.ts
3
3
  import * as fs from "node:fs/promises";
4
4
  import { atomicWrite } from "@wrongstack/core/utils";
5
+ import {
6
+ resolvePluginEnablement
7
+ } from "@wrongstack/core/plugin";
5
8
  import {
6
9
  PLUGIN_AUDIT_ENTRIES
7
10
  } from "@wrongstack/plugins/plugin-audit-catalog";
@@ -311,6 +314,7 @@ function renderPluginAuditReport(config) {
311
314
  const lines = [
312
315
  "Plugin audit report:",
313
316
  " State shows the effective boot state. Default-active plugins run unless disabled in config.",
317
+ " Source: config = plugins[] entry, ext = extensions.<name>.enabled, feature = features.plugins.",
314
318
  " Built-in audit rows are toggleable unless a future row explicitly marks itself locked.",
315
319
  "",
316
320
  " Built-in audit entries:"
@@ -359,19 +363,22 @@ function pluginMatchesToggleSpec(p, spec) {
359
363
  if (spec === "telegram") return name === "telegram" || name === "@wrongstack/telegram";
360
364
  return name === spec;
361
365
  }
366
+ var AUDIT_STATE_SOURCE_LABEL = {
367
+ "feature-flag": "feature",
368
+ "plugin-entry": "config",
369
+ extension: "ext",
370
+ default: "default"
371
+ };
362
372
  function effectiveAuditState(config, entry) {
363
- const configured = (config.plugins ?? []).find(
364
- (plugin) => pluginMatchesToggleSpec(plugin, entry.name)
365
- );
366
- if (configured) {
367
- return {
368
- state: typeof configured === "object" && configured.enabled === false ? "disabled" : "enabled",
369
- source: "config"
370
- };
371
- }
373
+ const { enabled, source } = resolvePluginEnablement({
374
+ name: entry.name,
375
+ defaultState: entry.defaultState,
376
+ config,
377
+ matches: (spec) => pluginMatchesToggleSpec(spec, entry.name)
378
+ });
372
379
  return {
373
- state: entry.defaultState === "active" ? "enabled" : "disabled",
374
- source: "default"
380
+ state: enabled ? "enabled" : "disabled",
381
+ source: AUDIT_STATE_SOURCE_LABEL[source]
375
382
  };
376
383
  }
377
384
  function lockedReason(entry) {
@@ -490,4 +497,4 @@ export {
490
497
  PLUGIN_AUDIT_ENTRIES,
491
498
  runPluginManagementCommand
492
499
  };
493
- //# sourceMappingURL=chunk-PEKUKUCH.js.map
500
+ //# sourceMappingURL=chunk-3MWUZMOL.js.map
@@ -2,26 +2,7 @@
2
2
  // src/boot/auto-discover-providers.ts
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path from "node:path";
5
- import { COMPATIBLE_PRESETS, discoverOpenAICompatibleModels } from "@wrongstack/providers";
6
- function resolveKey(cfg) {
7
- if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
8
- const active = cfg.activeKey ? cfg.apiKeys.find((k) => k.label === cfg.activeKey) : void 0;
9
- return (active ?? cfg.apiKeys[0])?.apiKey;
10
- }
11
- return cfg.apiKey && cfg.apiKey.length > 0 ? cfg.apiKey : void 0;
12
- }
13
- function eligibleProviders(config) {
14
- const out = [];
15
- for (const [id, cfg] of Object.entries(config.providers ?? {})) {
16
- const preset = COMPATIBLE_PRESETS[id];
17
- const enabled = cfg.autoDiscoverModels ?? preset?.autoDiscover ?? false;
18
- if (!enabled) continue;
19
- const baseUrl = cfg.baseUrl ?? preset?.defaultBaseUrl;
20
- if (!baseUrl) continue;
21
- out.push({ id, cfg, baseUrl, apiKey: resolveKey(cfg) });
22
- }
23
- return out;
24
- }
5
+ import { discoverOpenAICompatibleModels, resolveDiscoveryTargets } from "@wrongstack/providers";
25
6
  async function readCache(file) {
26
7
  try {
27
8
  return JSON.parse(await fs.readFile(file, "utf8"));
@@ -30,14 +11,13 @@ async function readCache(file) {
30
11
  }
31
12
  }
32
13
  async function discoverAndMergeProviders(opts) {
33
- const targets = eligibleProviders(opts.config);
14
+ const targets = resolveDiscoveryTargets(opts.config);
34
15
  if (targets.length === 0) return;
35
16
  const cacheFile = path.join(opts.cacheDir, "discovered-models-cache.json");
36
17
  const cache = await readCache(cacheFile);
37
18
  let cacheDirty = false;
38
19
  await Promise.all(
39
- targets.map(async ({ id, cfg, baseUrl, apiKey }) => {
40
- const cacheKey = `${id}\0${baseUrl}`;
20
+ targets.map(async ({ id, cfg, baseUrl, apiKey, cacheKey }) => {
41
21
  const provider = await discoverOpenAICompatibleModels(id, {
42
22
  baseUrl,
43
23
  apiKey,
@@ -78,4 +58,4 @@ async function discoverAndMergeProviders(opts) {
78
58
  export {
79
59
  discoverAndMergeProviders
80
60
  };
81
- //# sourceMappingURL=chunk-TGDHN4LM.js.map
61
+ //# sourceMappingURL=chunk-4GVH7V3S.js.map
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  PLUGIN_AUDIT_ENTRIES
3
- } from "./chunk-PEKUKUCH.js";
3
+ } from "./chunk-3MWUZMOL.js";
4
4
  import {
5
5
  patchConfig
6
6
  } from "./chunk-TYO2OVAD.js";
7
7
 
8
8
  // src/wiring/plugins.ts
9
9
  import { join } from "node:path";
10
- import { loadPlugins, resolvePluginConfig } from "@wrongstack/core/plugin";
10
+ import { loadPlugins, resolvePluginConfig, resolvePluginEnablement } from "@wrongstack/core/plugin";
11
11
  import { OFFICIAL_PLUGIN_FACTORIES } from "@wrongstack/plugins/factories";
12
12
 
13
13
  // src/plugin-api-factory.ts
@@ -112,30 +112,25 @@ async function setupPlugins(params) {
112
112
  hookRegistry
113
113
  } = params;
114
114
  const builtinPlugins = [];
115
- const disabledBuiltins = new Set(
116
- (config.plugins ?? []).filter(
117
- (p) => typeof p === "object" && p.enabled === false
118
- ).flatMap((p) => {
119
- const builtinName = builtinPluginNameFromSpec(p.name);
120
- return builtinName && builtinName !== p.name ? [p.name, builtinName] : [p.name];
121
- })
122
- );
123
115
  if (paths && config.features?.plugins !== false) {
124
116
  for (const factory of BUILTIN_PLUGIN_FACTORIES) {
125
117
  try {
126
118
  const plugin = await factory();
127
119
  if (!plugin) continue;
128
120
  const auditEntry = PLUGIN_AUDIT_ENTRIES.find((entry) => entry.name === plugin.name);
129
- const explicitlyEnabled = (config.plugins ?? []).some((entry) => {
130
- const spec = typeof entry === "string" ? entry : entry.name;
131
- if (typeof entry === "object" && entry.enabled === false) return false;
132
- return (builtinPluginNameFromSpec(spec) ?? spec) === plugin.name;
121
+ const { enabled, source } = resolvePluginEnablement({
122
+ name: plugin.name,
123
+ aliases: plugin.configAliases,
124
+ // Built-ins outside the audit catalog (prompts, sync, skills, …)
125
+ // are infrastructure — they run unless explicitly turned off.
126
+ defaultState: auditEntry?.defaultState ?? "active",
127
+ config,
128
+ matches: (spec) => (builtinPluginNameFromSpec(spec) ?? spec) === plugin.name
133
129
  });
134
- if (auditEntry?.defaultState === "inactive" && !explicitlyEnabled) {
135
- continue;
136
- }
137
- if (disabledBuiltins.has(plugin.name)) {
138
- log.info(`[setupPlugins] built-in plugin "${plugin.name}" disabled by config`);
130
+ if (!enabled) {
131
+ if (source !== "default") {
132
+ log.info(`[setupPlugins] built-in plugin "${plugin.name}" disabled by ${source}`);
133
+ }
139
134
  continue;
140
135
  }
141
136
  if (warnIfDeprecatedPluginName(plugin.name, log)) continue;
@@ -255,4 +250,4 @@ export {
255
250
  BUILTIN_PLUGIN_FACTORIES,
256
251
  setupPlugins
257
252
  };
258
- //# sourceMappingURL=chunk-4U3DMA2Y.js.map
253
+ //# sourceMappingURL=chunk-4WJKVQJY.js.map