@wrongstack/cli 0.299.0 → 0.301.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.
- package/dist/{acp-S4LZQB66.js → acp-DATHHPNT.js} +36 -17
- package/dist/acp-server-agent.d.ts +1 -1
- package/dist/{audit-2ZSFGNIF.js → audit-QRCLEHHW.js} +5 -1
- package/dist/{auth-G5YKKSRH.js → auth-JASC4K3F.js} +151 -38
- package/dist/auth-menu/loopback-server.d.ts +41 -55
- package/dist/auth-menu/openai-codex-oauth.d.ts +36 -123
- package/dist/auto-discover-providers-DRW4OZHG.js +8 -0
- package/dist/boot/dispatch-webui.d.ts +3 -0
- package/dist/boot/launch-menu.d.ts +2 -4
- package/dist/boot/tool-registry.d.ts +3 -0
- package/dist/chimera-reviewer-policy.d.ts +10 -3
- package/dist/chimera-work-registry.d.ts +19 -0
- package/dist/{chronicle-QQEQFSSE.js → chronicle-63MFCZN6.js} +26 -5
- package/dist/{chunk-YWUPHRGB.js → chunk-263G3FMB.js} +33 -10
- package/dist/{chunk-2SLSUDYS.js → chunk-2KDOFTTM.js} +312 -211
- package/dist/{chunk-TGDHN4LM.js → chunk-4GVH7V3S.js} +4 -24
- package/dist/chunk-CSAPOCBP.js +24 -0
- package/dist/{chunk-66T43Q4Y.js → chunk-ETOEGL3V.js} +6 -9
- package/dist/{chunk-PEKUKUCH.js → chunk-GUHQQG63.js} +32 -14
- package/dist/chunk-KE7E7DPX.js +38 -0
- package/dist/{chunk-ZNZJ34RF.js → chunk-MHF2IJDE.js} +21 -4
- package/dist/{chunk-4HH3YGG4.js → chunk-MRLYDHQ7.js} +8 -5
- package/dist/{chunk-L5CE52XW.js → chunk-O7XNGCRT.js} +317 -309
- package/dist/chunk-PYTFS4MC.js +238 -0
- package/dist/{chunk-DPJZQCVP.js → chunk-YMXXOOFN.js} +1 -1
- package/dist/{chunk-4U3DMA2Y.js → chunk-ZECLGUTF.js} +28 -21
- package/dist/{cli-main-3MQDFSYK.js → cli-main-XWC57MGE.js} +849 -242
- package/dist/{execution-ANOYMWPO.js → execution-LHMSMDHB.js} +174 -55
- package/dist/execution-chimera-cascade.d.ts +3 -4
- package/dist/execution-chimera-review.d.ts +2 -3
- package/dist/execution-cleanup.d.ts +3 -2
- package/dist/execution.d.ts +1 -1
- package/dist/{export-TPORYVRZ.js → export-DOBGPY23.js} +12 -1
- package/dist/fleet/budget-source.d.ts +18 -0
- package/dist/fleet/host-context.d.ts +1 -1
- package/dist/fleet/host-helpers.d.ts +14 -0
- package/dist/fleet/host-status.d.ts +31 -0
- package/dist/fleet/host-types.d.ts +8 -0
- package/dist/fleet/host.d.ts +5 -0
- package/dist/fleet/status-broadcast.d.ts +1 -1
- package/dist/{hq-BDO56CX6.js → hq-LN5CJHCM.js} +22 -3
- package/dist/hq-server/auth-state.d.ts +29 -1
- package/dist/hq-server/auth.d.ts +16 -0
- package/dist/hq-server/client-address.d.ts +56 -0
- package/dist/hq-server/login-attempt-store.d.ts +30 -10
- package/dist/hq-server/routes/auth-handlers.d.ts +66 -26
- package/dist/hq-server/routes.d.ts +23 -1
- package/dist/hq-server/types.d.ts +10 -0
- package/dist/hq-server/ws.d.ts +17 -6
- package/dist/{hq-server-RJLJJZU7.js → hq-server-S7HZG3PA.js} +3 -2
- package/dist/hq-server.d.ts +19 -0
- package/dist/index.js +69 -104
- package/dist/live-settings-input.d.ts +5 -0
- package/dist/{mcp-CYLPUEHC.js → mcp-OHPJH73U.js} +6 -2
- package/dist/{modeldiag-HENYJ4N7.js → modeldiag-3DOK4BWW.js} +3 -3
- package/dist/{plugin-usage-VTL7ZTVC.js → plugin-usage-YEVLOR33.js} +7 -3
- package/dist/{plugins-ETY6W3DC.js → plugins-K3BAUP4D.js} +3 -3
- package/dist/profile-config-path.d.ts +0 -2
- package/dist/provider-helpers.d.ts +0 -5
- package/dist/{providers-models-GDELBHZK.js → providers-models-WEZ4EGLL.js} +38 -12
- package/dist/{replay-KCTXNMZQ.js → replay-FCBNHW3K.js} +5 -1
- package/dist/{rewind-EJMVFVLS.js → rewind-AOD3JIYX.js} +5 -1
- package/dist/{sessions-config-NLCMK3BY.js → sessions-config-XXN7267N.js} +8 -3
- package/dist/slash-commands/command-context.d.ts +19 -0
- package/dist/subcommands/flags.d.ts +23 -0
- package/dist/terminal-format.d.ts +15 -0
- package/dist/webui-server/setup-events.d.ts +17 -0
- package/dist/webui-server/terminal-log-view.d.ts +29 -12
- package/dist/{webui-server-PCLSXX6L.js → webui-server-DD35QFNO.js} +113 -50
- package/dist/webui-server-options.d.ts +16 -0
- package/dist/wiring/brain-and-orchestration.d.ts +4 -1
- package/dist/wiring/director-setup.d.ts +4 -0
- package/dist/wiring/fallback-gate.d.ts +31 -0
- package/dist/wiring/fleet-command-handlers.d.ts +1 -1
- package/dist/wiring/mailbox-bridge-bootstrap.d.ts +3 -1
- package/dist/wiring/management-tools.d.ts +9 -1
- package/dist/wiring/pipeline.d.ts +5 -5
- package/dist/wiring/plugins.d.ts +3 -10
- package/dist/wiring/provider-utility-tools.d.ts +27 -1
- package/dist/wiring/tools.d.ts +4 -5
- package/package.json +23 -23
- package/dist/chunk-GO3TJICK.js +0 -489
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Registers provider-neutral one-shot, council, and context-summary tools. */
|
|
2
|
-
import type { Config, Provider } from '@wrongstack/core/types';
|
|
2
|
+
import type { Config, CouncilToolConfig, OneShotModelRouter, Provider } from '@wrongstack/core/types';
|
|
3
3
|
import { createContextManagerTool } from '@wrongstack/core/infrastructure';
|
|
4
4
|
import type { FallbackProfileManager } from '@wrongstack/core/agent';
|
|
5
|
+
import { type CouncilPersonaRegistry, type CouncilProfileRegistry } from '@wrongstack/core/execution';
|
|
5
6
|
import type { ProviderModelStatusTracker } from '@wrongstack/core/coordination';
|
|
6
7
|
import type { ToolRegistry } from '@wrongstack/core/registry';
|
|
7
8
|
export interface ProviderUtilityToolsInput {
|
|
@@ -10,6 +11,7 @@ export interface ProviderUtilityToolsInput {
|
|
|
10
11
|
getConfig: () => Config;
|
|
11
12
|
fallbackProfileManager: FallbackProfileManager;
|
|
12
13
|
statusTracker: ProviderModelStatusTracker;
|
|
14
|
+
wrapProviderCall: NonNullable<import('@wrongstack/core/types').OneShotOrchestratorOptions['wrapProviderCall']>;
|
|
13
15
|
compactor: NonNullable<Parameters<typeof createContextManagerTool>[0]>['compactor'];
|
|
14
16
|
}
|
|
15
17
|
export declare function adoptResumedProvider(input: {
|
|
@@ -21,5 +23,29 @@ export declare function adoptResumedProvider(input: {
|
|
|
21
23
|
warn(message: string): void;
|
|
22
24
|
};
|
|
23
25
|
}): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* A router that reads the LIVE config on every pick.
|
|
28
|
+
*
|
|
29
|
+
* `ModelRouter` snapshots `modelMatrix` and the provider list at construction,
|
|
30
|
+
* but `/setmodel` and credential edits change both mid-session. Rebuilding per
|
|
31
|
+
* call keeps role routing consistent with what the user last configured; the
|
|
32
|
+
* router itself does no I/O, so this is a plain object construction.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createLiveModelRouter(getConfig: () => Config): OneShotModelRouter;
|
|
35
|
+
/**
|
|
36
|
+
* Build the Council tool's persona/profile registries from configuration.
|
|
37
|
+
*
|
|
38
|
+
* `createCouncilPersonaRegistry` / `createCouncilProfileRegistry` shipped from
|
|
39
|
+
* day one but no host ever called them, so the tool was pinned to the built-in
|
|
40
|
+
* lenses and panels with no way to add either. A malformed entry must not take
|
|
41
|
+
* the whole tool registration down with it — a bad panel definition degrades to
|
|
42
|
+
* the built-ins and is reported, exactly like an unresolvable Brain pool entry.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildCouncilRegistries(cfg: CouncilToolConfig | undefined): {
|
|
45
|
+
personas?: CouncilPersonaRegistry | undefined;
|
|
46
|
+
profiles?: CouncilProfileRegistry | undefined;
|
|
47
|
+
defaultProfile?: string | undefined;
|
|
48
|
+
maxConcurrency?: number | undefined;
|
|
49
|
+
};
|
|
24
50
|
export declare function registerProviderUtilityTools(input: ProviderUtilityToolsInput): void;
|
|
25
51
|
//# sourceMappingURL=provider-utility-tools.d.ts.map
|
package/dist/wiring/tools.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { TextBlock } from '@wrongstack/core/types';
|
|
2
|
-
import type { Config, MemoryPort } from '@wrongstack/core/types';
|
|
3
|
-
import { type WstackPaths } from '@wrongstack/core/utils';
|
|
4
|
-
import { type DefaultModelsRegistry, DefaultModeStore } from '@wrongstack/core/models';
|
|
5
|
-
import { DefaultSkillLoader } from '@wrongstack/core/execution';
|
|
6
1
|
import { DefaultSystemPromptBuilder } from '@wrongstack/core/agent';
|
|
2
|
+
import { DefaultSkillLoader } from '@wrongstack/core/execution';
|
|
3
|
+
import { type DefaultModelsRegistry, DefaultModeStore } from '@wrongstack/core/models';
|
|
7
4
|
import type { ToolRegistry } from '@wrongstack/core/registry';
|
|
5
|
+
import type { Config, MemoryPort, TextBlock } from '@wrongstack/core/types';
|
|
6
|
+
import { type WstackPaths } from '@wrongstack/core/utils';
|
|
8
7
|
export interface ToolsWiringDeps {
|
|
9
8
|
config: Config;
|
|
10
9
|
toolRegistry: ToolRegistry;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.301.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack CLI — terminal AI coding agent with provider catalog from models.dev. Provides `wrongstack` and `wstack` binaries.",
|
|
6
6
|
"keywords": [
|
|
@@ -42,30 +42,30 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"ws": "^8.21.1",
|
|
45
|
-
"@wrongstack/acp": "0.
|
|
46
|
-
"@wrongstack/
|
|
47
|
-
"@wrongstack/
|
|
48
|
-
"@wrongstack/
|
|
49
|
-
"@wrongstack/
|
|
50
|
-
"@wrongstack/
|
|
51
|
-
"@wrongstack/
|
|
52
|
-
"@wrongstack/
|
|
53
|
-
"@wrongstack/
|
|
54
|
-
"@wrongstack/
|
|
55
|
-
"@wrongstack/
|
|
56
|
-
"@wrongstack/
|
|
57
|
-
"@wrongstack/
|
|
58
|
-
"@wrongstack/
|
|
59
|
-
"@wrongstack/
|
|
60
|
-
"@wrongstack/
|
|
61
|
-
"@wrongstack/
|
|
62
|
-
"@wrongstack/
|
|
63
|
-
"@wrongstack/
|
|
64
|
-
"@wrongstack/webui-
|
|
65
|
-
"@wrongstack/webui-
|
|
45
|
+
"@wrongstack/acp": "0.301.0",
|
|
46
|
+
"@wrongstack/mcp": "0.301.0",
|
|
47
|
+
"@wrongstack/bench": "0.301.0",
|
|
48
|
+
"@wrongstack/plug-lsp": "0.301.0",
|
|
49
|
+
"@wrongstack/kanban": "0.301.0",
|
|
50
|
+
"@wrongstack/providers": "0.301.0",
|
|
51
|
+
"@wrongstack/plugins": "0.301.0",
|
|
52
|
+
"@wrongstack/sdd": "0.301.0",
|
|
53
|
+
"@wrongstack/requirement-intake": "0.301.0",
|
|
54
|
+
"@wrongstack/runtime": "0.301.0",
|
|
55
|
+
"@wrongstack/security-scanner": "0.301.0",
|
|
56
|
+
"@wrongstack/core": "0.301.0",
|
|
57
|
+
"@wrongstack/simpleui": "0.301.0",
|
|
58
|
+
"@wrongstack/techstack": "0.301.0",
|
|
59
|
+
"@wrongstack/sage": "0.301.0",
|
|
60
|
+
"@wrongstack/telegram": "0.301.0",
|
|
61
|
+
"@wrongstack/tui": "0.301.0",
|
|
62
|
+
"@wrongstack/tools": "0.301.0",
|
|
63
|
+
"@wrongstack/webui": "0.301.0",
|
|
64
|
+
"@wrongstack/webui-hq": "0.301.0",
|
|
65
|
+
"@wrongstack/webui-server": "0.301.0"
|
|
66
66
|
},
|
|
67
67
|
"optionalDependencies": {
|
|
68
|
-
"@wrongstack/desktop": "0.
|
|
68
|
+
"@wrongstack/desktop": "0.301.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^26.1.2",
|
package/dist/chunk-GO3TJICK.js
DELETED
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
mutateConfigProviders,
|
|
3
|
-
normalizeKeys,
|
|
4
|
-
nowIso,
|
|
5
|
-
writeKeysBack
|
|
6
|
-
} from "./chunk-PJHET45A.js";
|
|
7
|
-
|
|
8
|
-
// src/auth-menu/openai-codex-oauth.ts
|
|
9
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
10
|
-
|
|
11
|
-
// src/auth-menu/loopback-server.ts
|
|
12
|
-
import { spawn } from "node:child_process";
|
|
13
|
-
import { createServer } from "node:http";
|
|
14
|
-
var ESCAPE_HTML = {
|
|
15
|
-
"&": "&",
|
|
16
|
-
"<": "<",
|
|
17
|
-
">": ">",
|
|
18
|
-
'"': """,
|
|
19
|
-
"'": "'"
|
|
20
|
-
};
|
|
21
|
-
function escapeHtml(s) {
|
|
22
|
-
return s.replace(/[&<>"']/g, (ch) => ESCAPE_HTML[ch] ?? ch);
|
|
23
|
-
}
|
|
24
|
-
function callbackHtml(ok, message) {
|
|
25
|
-
const heading = ok ? "Authentication successful" : "Authentication failed";
|
|
26
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>${escapeHtml(heading)}</title><style>body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;background:#09090b;color:#fafafa;font-family:ui-sans-serif,system-ui,sans-serif;text-align:center}h1{font-size:26px;margin:0 0 8px}p{color:#a1a1aa}</style></head><body><main><h1>${escapeHtml(heading)}</h1><p>${escapeHtml(message)}</p></main></body></html>`;
|
|
27
|
-
}
|
|
28
|
-
function openBrowser(url) {
|
|
29
|
-
try {
|
|
30
|
-
const platform = process.platform;
|
|
31
|
-
const { command, args } = platform === "win32" ? { command: "cmd", args: ["/c", "start", "", url] } : platform === "darwin" ? { command: "open", args: [url] } : { command: "xdg-open", args: [url] };
|
|
32
|
-
const child = spawn(command, args, { stdio: "ignore", windowsHide: true });
|
|
33
|
-
child.on("error", () => {
|
|
34
|
-
});
|
|
35
|
-
child.unref();
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function startLoopbackServer(expectedState, ports, signal, callbackPath = "/auth/callback", redirectHost = "127.0.0.1") {
|
|
40
|
-
let resolveCode = () => {
|
|
41
|
-
};
|
|
42
|
-
const codePromise = new Promise((resolve) => {
|
|
43
|
-
let settled = false;
|
|
44
|
-
resolveCode = (v) => {
|
|
45
|
-
if (settled) return;
|
|
46
|
-
settled = true;
|
|
47
|
-
resolve(v);
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
const server = createServer((req, res) => {
|
|
51
|
-
let url;
|
|
52
|
-
try {
|
|
53
|
-
url = new URL(req.url ?? "", `http://${redirectHost}`);
|
|
54
|
-
} catch {
|
|
55
|
-
res.statusCode = 400;
|
|
56
|
-
res.end();
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (url.pathname !== callbackPath) {
|
|
60
|
-
res.statusCode = 404;
|
|
61
|
-
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
62
|
-
res.setHeader("content-security-policy", "default-src 'none'; style-src 'unsafe-inline'");
|
|
63
|
-
res.setHeader("x-frame-options", "DENY");
|
|
64
|
-
res.setHeader("x-content-type-options", "nosniff");
|
|
65
|
-
res.setHeader("referrer-policy", "no-referrer");
|
|
66
|
-
res.end(callbackHtml(false, "Callback route not found."));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
70
|
-
res.setHeader("content-security-policy", "default-src 'none'; style-src 'unsafe-inline'");
|
|
71
|
-
res.setHeader("x-frame-options", "DENY");
|
|
72
|
-
res.setHeader("x-content-type-options", "nosniff");
|
|
73
|
-
res.setHeader("referrer-policy", "no-referrer");
|
|
74
|
-
const err = url.searchParams.get("error");
|
|
75
|
-
if (err) {
|
|
76
|
-
res.statusCode = 400;
|
|
77
|
-
res.end(callbackHtml(false, `Authorization error: ${err}`));
|
|
78
|
-
resolveCode(null);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const state = url.searchParams.get("state") ?? "";
|
|
82
|
-
if (state !== expectedState) {
|
|
83
|
-
res.statusCode = 400;
|
|
84
|
-
res.end(callbackHtml(false, "State mismatch \u2014 please restart the login."));
|
|
85
|
-
resolveCode(null);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const code = url.searchParams.get("code");
|
|
89
|
-
if (!code) {
|
|
90
|
-
res.statusCode = 400;
|
|
91
|
-
res.end(callbackHtml(false, "Missing authorization code."));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
res.statusCode = 200;
|
|
95
|
-
res.end(callbackHtml(true, "You can close this window and return to the terminal."));
|
|
96
|
-
resolveCode({ code, state });
|
|
97
|
-
});
|
|
98
|
-
const onAbort = () => {
|
|
99
|
-
resolveCode(null);
|
|
100
|
-
try {
|
|
101
|
-
server.close();
|
|
102
|
-
} catch {
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
if (signal) {
|
|
106
|
-
if (signal.aborted) onAbort();
|
|
107
|
-
else signal.addEventListener("abort", onAbort, { once: true });
|
|
108
|
-
}
|
|
109
|
-
return new Promise((resolve) => {
|
|
110
|
-
const close = () => {
|
|
111
|
-
resolveCode(null);
|
|
112
|
-
try {
|
|
113
|
-
server.close();
|
|
114
|
-
} catch {
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
const fail = () => {
|
|
118
|
-
resolveCode(null);
|
|
119
|
-
resolve({
|
|
120
|
-
bound: false,
|
|
121
|
-
port: ports[0] ?? 1455,
|
|
122
|
-
waitForCode: () => Promise.resolve(null),
|
|
123
|
-
close
|
|
124
|
-
});
|
|
125
|
-
};
|
|
126
|
-
let index = 0;
|
|
127
|
-
server.on("error", () => {
|
|
128
|
-
index += 1;
|
|
129
|
-
const nextPort = ports[index];
|
|
130
|
-
if (nextPort !== void 0) {
|
|
131
|
-
server.listen(nextPort, redirectHost);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
fail();
|
|
135
|
-
});
|
|
136
|
-
server.on("listening", () => {
|
|
137
|
-
const address = server.address();
|
|
138
|
-
const port = typeof address === "object" && address ? address.port : ports[index] ?? 1455;
|
|
139
|
-
resolve({
|
|
140
|
-
bound: true,
|
|
141
|
-
port,
|
|
142
|
-
waitForCode: () => codePromise,
|
|
143
|
-
close
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
server.listen(ports[0] ?? 1455, redirectHost);
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// src/auth-menu/openai-codex-oauth.ts
|
|
151
|
-
import { CODEX_MODELS } from "@wrongstack/core/models";
|
|
152
|
-
import { color } from "@wrongstack/core/utils";
|
|
153
|
-
import { FetchError, ParseError } from "@wrongstack/core/types";
|
|
154
|
-
var CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
155
|
-
var AUTH_BASE_URL = "https://auth.openai.com";
|
|
156
|
-
var AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;
|
|
157
|
-
var TOKEN_URL = `${AUTH_BASE_URL}/oauth/token`;
|
|
158
|
-
var REDIRECT_PORT = 1455;
|
|
159
|
-
var FALLBACK_REDIRECT_PORT = 1457;
|
|
160
|
-
var REDIRECT_HOST = "127.0.0.1";
|
|
161
|
-
var REDIRECT_PATH = "/auth/callback";
|
|
162
|
-
var SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
|
|
163
|
-
var JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
164
|
-
var ORIGINATOR = "wrongstack";
|
|
165
|
-
var CODEX_PROVIDER_ID = "openai-codex";
|
|
166
|
-
var CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
167
|
-
function startLoopbackServer2(expectedState, signal) {
|
|
168
|
-
return startLoopbackServer(
|
|
169
|
-
expectedState,
|
|
170
|
-
[REDIRECT_PORT, FALLBACK_REDIRECT_PORT],
|
|
171
|
-
signal,
|
|
172
|
-
REDIRECT_PATH,
|
|
173
|
-
REDIRECT_HOST
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
function base64url(buf) {
|
|
177
|
-
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
178
|
-
}
|
|
179
|
-
function generatePkce() {
|
|
180
|
-
const verifier = base64url(randomBytes(64));
|
|
181
|
-
const challenge = base64url(createHash("sha256").update(verifier).digest());
|
|
182
|
-
return { verifier, challenge };
|
|
183
|
-
}
|
|
184
|
-
function createState() {
|
|
185
|
-
return base64url(randomBytes(32));
|
|
186
|
-
}
|
|
187
|
-
function redirectUri(port) {
|
|
188
|
-
return `http://localhost:${port}${REDIRECT_PATH}`;
|
|
189
|
-
}
|
|
190
|
-
function buildAuthorizeUrl(challenge, state, port = REDIRECT_PORT) {
|
|
191
|
-
const url = new URL(AUTHORIZE_URL);
|
|
192
|
-
url.searchParams.set("response_type", "code");
|
|
193
|
-
url.searchParams.set("client_id", CLIENT_ID);
|
|
194
|
-
url.searchParams.set("redirect_uri", redirectUri(port));
|
|
195
|
-
url.searchParams.set("scope", SCOPE);
|
|
196
|
-
url.searchParams.set("code_challenge", challenge);
|
|
197
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
198
|
-
url.searchParams.set("state", state);
|
|
199
|
-
url.searchParams.set("id_token_add_organizations", "true");
|
|
200
|
-
url.searchParams.set("codex_cli_simplified_flow", "true");
|
|
201
|
-
url.searchParams.set("originator", ORIGINATOR);
|
|
202
|
-
return url.toString();
|
|
203
|
-
}
|
|
204
|
-
function decodeJwtPayload(token) {
|
|
205
|
-
try {
|
|
206
|
-
const parts = token.split(".");
|
|
207
|
-
if (parts.length !== 3) return null;
|
|
208
|
-
const json = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
209
|
-
return JSON.parse(json);
|
|
210
|
-
} catch {
|
|
211
|
-
return null;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
function extractAccountId(token) {
|
|
215
|
-
const payload = decodeJwtPayload(token);
|
|
216
|
-
const auth = payload?.[JWT_CLAIM_PATH];
|
|
217
|
-
const id = auth?.chatgpt_account_id;
|
|
218
|
-
return typeof id === "string" && id.length > 0 ? id : null;
|
|
219
|
-
}
|
|
220
|
-
var FALLBACK_CODEX_MODELS = CODEX_MODELS.map(
|
|
221
|
-
(m) => ({ id: m.id, name: m.name })
|
|
222
|
-
);
|
|
223
|
-
var CODEX_CATALOG_FAMILIES = /* @__PURE__ */ new Set(["gpt-codex", "gpt-codex-spark"]);
|
|
224
|
-
function fallbackCodexModelIds() {
|
|
225
|
-
return FALLBACK_CODEX_MODELS.map((m) => m.id);
|
|
226
|
-
}
|
|
227
|
-
function fallbackCodexProviderModels() {
|
|
228
|
-
return FALLBACK_CODEX_MODELS.map((m) => ({ id: m.id, name: m.name }));
|
|
229
|
-
}
|
|
230
|
-
function filterCurrentCodexModelIds(ids) {
|
|
231
|
-
const available = new Set(ids);
|
|
232
|
-
return FALLBACK_CODEX_MODELS.map((m) => m.id).filter((id) => available.has(id));
|
|
233
|
-
}
|
|
234
|
-
function isCodexCatalogModel(model) {
|
|
235
|
-
return typeof model.family === "string" && CODEX_CATALOG_FAMILIES.has(model.family);
|
|
236
|
-
}
|
|
237
|
-
async function resolveCodexModels(modelsRegistry, accessToken, baseUrl, signal) {
|
|
238
|
-
const token = typeof accessToken === "string" ? accessToken : await accessToken;
|
|
239
|
-
const live = filterCurrentCodexModelIds(await fetchCodexModels(token, baseUrl, signal));
|
|
240
|
-
if (live.length > 0) return live;
|
|
241
|
-
try {
|
|
242
|
-
const openaiProvider = await modelsRegistry.getProvider("openai");
|
|
243
|
-
if (openaiProvider) {
|
|
244
|
-
const catalog = openaiProvider.models.filter(isCodexCatalogModel).map((m) => m.id).filter((id) => typeof id === "string" && id.length > 0);
|
|
245
|
-
const currentCatalog = filterCurrentCodexModelIds(catalog);
|
|
246
|
-
if (currentCatalog.length > 0) return currentCatalog;
|
|
247
|
-
}
|
|
248
|
-
} catch {
|
|
249
|
-
}
|
|
250
|
-
return fallbackCodexModelIds();
|
|
251
|
-
}
|
|
252
|
-
async function fetchCodexModels(accessToken, baseUrl, signal) {
|
|
253
|
-
const url = `${(baseUrl ?? CODEX_BASE_URL).replace(/\/+$/, "")}/models`;
|
|
254
|
-
try {
|
|
255
|
-
const res = await fetch(url, {
|
|
256
|
-
headers: {
|
|
257
|
-
accept: "application/json",
|
|
258
|
-
authorization: `Bearer ${accessToken}`,
|
|
259
|
-
originator: "wrongstack",
|
|
260
|
-
"OpenAI-Beta": "responses=experimental"
|
|
261
|
-
},
|
|
262
|
-
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(8e3)]) : AbortSignal.timeout(8e3)
|
|
263
|
-
});
|
|
264
|
-
if (!res.ok) return [];
|
|
265
|
-
const json = await res.json();
|
|
266
|
-
if (!json) return [];
|
|
267
|
-
const rawList = "data" in json && Array.isArray(json.data) ? json.data : "models" in json && Array.isArray(json.models) ? json.models : [];
|
|
268
|
-
const ids = [];
|
|
269
|
-
for (const entry of rawList) {
|
|
270
|
-
if (!entry || typeof entry !== "object") continue;
|
|
271
|
-
const id = entry.id;
|
|
272
|
-
if (typeof id === "string" && id.length > 0) ids.push(id);
|
|
273
|
-
}
|
|
274
|
-
return ids;
|
|
275
|
-
} catch {
|
|
276
|
-
return [];
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
async function readTokens(res, op, url) {
|
|
280
|
-
if (!res.ok) {
|
|
281
|
-
const text = await res.text().catch(() => "");
|
|
282
|
-
throw new FetchError({
|
|
283
|
-
message: `Codex token ${op} failed (${res.status}): ${text || res.statusText}`,
|
|
284
|
-
status: res.status,
|
|
285
|
-
context: { provider: "openai-codex", op, url }
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
const json = await res.json();
|
|
289
|
-
if (!json?.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
|
|
290
|
-
throw new ParseError({
|
|
291
|
-
message: `Codex token ${op} response missing fields`,
|
|
292
|
-
source: "openai-codex-token-response",
|
|
293
|
-
context: { op }
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
return {
|
|
297
|
-
access: json.access_token,
|
|
298
|
-
refresh: json.refresh_token,
|
|
299
|
-
expires: Date.now() + json.expires_in * 1e3,
|
|
300
|
-
idToken: json.id_token
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
async function exchangeAuthorizationCode(code, verifier, signal, port = REDIRECT_PORT) {
|
|
304
|
-
const res = await fetch(TOKEN_URL, {
|
|
305
|
-
method: "POST",
|
|
306
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
307
|
-
body: new URLSearchParams({
|
|
308
|
-
grant_type: "authorization_code",
|
|
309
|
-
client_id: CLIENT_ID,
|
|
310
|
-
code,
|
|
311
|
-
code_verifier: verifier,
|
|
312
|
-
redirect_uri: redirectUri(port)
|
|
313
|
-
}).toString(),
|
|
314
|
-
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(3e4)]) : AbortSignal.timeout(3e4)
|
|
315
|
-
});
|
|
316
|
-
return readTokens(res, "exchange", TOKEN_URL);
|
|
317
|
-
}
|
|
318
|
-
function parseAuthorizationInput(input) {
|
|
319
|
-
const value = input.trim();
|
|
320
|
-
if (!value) return {};
|
|
321
|
-
try {
|
|
322
|
-
const url = new URL(value);
|
|
323
|
-
return {
|
|
324
|
-
code: url.searchParams.get("code") ?? void 0,
|
|
325
|
-
state: url.searchParams.get("state") ?? void 0
|
|
326
|
-
};
|
|
327
|
-
} catch {
|
|
328
|
-
}
|
|
329
|
-
if (value.includes("code=")) {
|
|
330
|
-
const params = new URLSearchParams(value);
|
|
331
|
-
return {
|
|
332
|
-
code: params.get("code") ?? void 0,
|
|
333
|
-
state: params.get("state") ?? void 0
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
return { code: value };
|
|
337
|
-
}
|
|
338
|
-
async function runCodexOAuthLogin(deps, opts = {}) {
|
|
339
|
-
const providerId = opts.providerId ?? CODEX_PROVIDER_ID;
|
|
340
|
-
const pkce = generatePkce();
|
|
341
|
-
const state = createState();
|
|
342
|
-
const ac = new AbortController();
|
|
343
|
-
let sigintCount = 0;
|
|
344
|
-
const onSig = () => {
|
|
345
|
-
sigintCount += 1;
|
|
346
|
-
if (sigintCount === 1) {
|
|
347
|
-
deps.renderer.write(color.dim("\n Cancelling sign-in\u2026 (press Ctrl+C again to force-quit)\n"));
|
|
348
|
-
ac.abort();
|
|
349
|
-
} else {
|
|
350
|
-
process.exit(130);
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
const external = opts.signal;
|
|
354
|
-
const onExternalAbort = () => ac.abort();
|
|
355
|
-
if (external) {
|
|
356
|
-
if (external.aborted) ac.abort();
|
|
357
|
-
else external.addEventListener("abort", onExternalAbort, { once: true });
|
|
358
|
-
} else {
|
|
359
|
-
process.on("SIGINT", onSig);
|
|
360
|
-
}
|
|
361
|
-
const server = await startLoopbackServer2(state, ac.signal);
|
|
362
|
-
const authorizeUrl = buildAuthorizeUrl(pkce.challenge, state, server.port);
|
|
363
|
-
const osc8 = (url) => `\x1B]8;;${url}\x1B\\${url}\x1B]8;;\x1B\\`;
|
|
364
|
-
deps.renderer.write(
|
|
365
|
-
color.bold(`
|
|
366
|
-
Sign in with ChatGPT \u2014 ${color.cyan(providerId)}
|
|
367
|
-
`) + color.dim(" Uses your ChatGPT Plus/Pro/Team subscription (not an API key).\n") + color.amber(" \u26A0 Using a subscription outside the official Codex client may violate\n") + color.amber(" OpenAI\u2019s Terms \u2014 your account could be rate-limited or banned.\n") + color.dim(" Sanctioned programmatic use = an API key: ") + color.bold("wstack auth openai") + color.dim("\n\n") + color.bold(` ${"\u2500".repeat(56)}
|
|
368
|
-
`) + color.bold(" Open this URL in your browser to sign in:\n") + color.cyan(` ${osc8(authorizeUrl)}
|
|
369
|
-
`) + color.bold(` ${"\u2500".repeat(56)}
|
|
370
|
-
|
|
371
|
-
`)
|
|
372
|
-
);
|
|
373
|
-
if (server.bound) {
|
|
374
|
-
openBrowser(authorizeUrl);
|
|
375
|
-
deps.renderer.write(
|
|
376
|
-
color.dim(" A browser window should open. Waiting for you to finish signing in...\n") + color.dim(` (Listening on ${redirectUri(server.port)} \u2014 press Ctrl+C to cancel.)
|
|
377
|
-
`)
|
|
378
|
-
);
|
|
379
|
-
} else {
|
|
380
|
-
deps.renderer.write(
|
|
381
|
-
color.amber(" \u26A0 Could not start the local callback listener (ports 1455 and 1457 in use).\n") + color.dim(" After signing in, copy the full redirect URL from your browser\n") + color.dim(" (it starts with http://localhost:1455/auth/callback or :1457) and paste it below.\n")
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
let code;
|
|
385
|
-
try {
|
|
386
|
-
if (server.bound) {
|
|
387
|
-
const callback = await server.waitForCode();
|
|
388
|
-
if (ac.signal.aborted) {
|
|
389
|
-
deps.renderer.write(color.dim(" Cancelled.\n"));
|
|
390
|
-
return 1;
|
|
391
|
-
}
|
|
392
|
-
if (callback) code = callback.code;
|
|
393
|
-
}
|
|
394
|
-
if (!code) {
|
|
395
|
-
const input = (await deps.reader.readLine(
|
|
396
|
-
`
|
|
397
|
-
${color.amber("?")} Paste the redirect URL or code ${color.dim("(or q to cancel)")}: `
|
|
398
|
-
)).trim();
|
|
399
|
-
if (input.toLowerCase() === "q" || input === "") {
|
|
400
|
-
deps.renderer.write(color.dim(" Cancelled.\n"));
|
|
401
|
-
return 1;
|
|
402
|
-
}
|
|
403
|
-
const parsed = parseAuthorizationInput(input);
|
|
404
|
-
if (parsed.state && parsed.state !== state) {
|
|
405
|
-
deps.renderer.writeError(" State mismatch \u2014 please restart the login flow.");
|
|
406
|
-
return 1;
|
|
407
|
-
}
|
|
408
|
-
code = parsed.code;
|
|
409
|
-
}
|
|
410
|
-
if (!code) {
|
|
411
|
-
deps.renderer.writeError(" No authorization code received.");
|
|
412
|
-
return 1;
|
|
413
|
-
}
|
|
414
|
-
deps.renderer.write(color.dim("\n Exchanging authorization code for tokens...\n"));
|
|
415
|
-
const tokens = await exchangeAuthorizationCode(code, pkce.verifier, ac.signal, server.port);
|
|
416
|
-
const accountId = extractAccountId(tokens.access) ?? (tokens.idToken ? extractAccountId(tokens.idToken) : null);
|
|
417
|
-
if (!accountId) {
|
|
418
|
-
deps.renderer.writeError(
|
|
419
|
-
" Signed in, but the token has no ChatGPT account id.\n This account may not have Codex/ChatGPT subscription access."
|
|
420
|
-
);
|
|
421
|
-
return 1;
|
|
422
|
-
}
|
|
423
|
-
deps.renderer.write(color.dim(" Fetching available models...\n"));
|
|
424
|
-
const models = await resolveCodexModels(
|
|
425
|
-
deps.modelsRegistry,
|
|
426
|
-
tokens.access,
|
|
427
|
-
CODEX_BASE_URL,
|
|
428
|
-
ac.signal
|
|
429
|
-
);
|
|
430
|
-
const saved = await saveCodexTokens(deps, providerId, tokens, accountId, models);
|
|
431
|
-
if (!saved) return 1;
|
|
432
|
-
const modelHint = models[0] ?? "gpt-5.5";
|
|
433
|
-
deps.renderer.write(color.green("\n \u2713 Signed in with ChatGPT!\n"));
|
|
434
|
-
deps.renderer.writeInfo(
|
|
435
|
-
` Saved as provider ${color.bold(providerId)}${models.length > 0 ? ` (${models.length} models)` : ""}.
|
|
436
|
-
Use: ${color.bold(`wstack --provider ${providerId} --model ${modelHint}`)} "<task>"
|
|
437
|
-
` + color.dim(" Tokens refresh automatically before they expire.\n")
|
|
438
|
-
);
|
|
439
|
-
return 0;
|
|
440
|
-
} catch (err) {
|
|
441
|
-
const msg = err instanceof DOMException && err.name === "AbortError" ? "Login cancelled." : err.message;
|
|
442
|
-
deps.renderer.writeError(` Login failed: ${msg}`);
|
|
443
|
-
return 1;
|
|
444
|
-
} finally {
|
|
445
|
-
server.close();
|
|
446
|
-
if (!external) process.off("SIGINT", onSig);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
async function saveCodexTokens(deps, providerId, tokens, accountId, models) {
|
|
450
|
-
const entry = {
|
|
451
|
-
label: "oauth-default",
|
|
452
|
-
apiKey: tokens.access,
|
|
453
|
-
createdAt: nowIso(),
|
|
454
|
-
authMethod: "oauth",
|
|
455
|
-
expiresAt: new Date(tokens.expires).toISOString(),
|
|
456
|
-
refreshToken: tokens.refresh,
|
|
457
|
-
tokenType: "bearer",
|
|
458
|
-
scope: SCOPE,
|
|
459
|
-
accountId
|
|
460
|
-
};
|
|
461
|
-
try {
|
|
462
|
-
await mutateConfigProviders(deps.profileConfigPath, deps.vault, (all) => {
|
|
463
|
-
const existing = all[providerId];
|
|
464
|
-
const p = existing ? { ...existing } : { type: providerId };
|
|
465
|
-
p.family = "openai-codex";
|
|
466
|
-
if (!p.baseUrl) p.baseUrl = CODEX_BASE_URL;
|
|
467
|
-
p.models = [...models];
|
|
468
|
-
const keys = normalizeKeys(p).filter((k) => k.label !== entry.label);
|
|
469
|
-
keys.push(entry);
|
|
470
|
-
writeKeysBack(p, keys);
|
|
471
|
-
p.activeKey = entry.label;
|
|
472
|
-
all[providerId] = p;
|
|
473
|
-
}, deps.profileConfigPath);
|
|
474
|
-
return true;
|
|
475
|
-
} catch (err) {
|
|
476
|
-
deps.renderer.writeError(` Failed to save tokens: ${err.message}`);
|
|
477
|
-
return false;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
export {
|
|
482
|
-
openBrowser,
|
|
483
|
-
startLoopbackServer,
|
|
484
|
-
fallbackCodexProviderModels,
|
|
485
|
-
filterCurrentCodexModelIds,
|
|
486
|
-
isCodexCatalogModel,
|
|
487
|
-
runCodexOAuthLogin
|
|
488
|
-
};
|
|
489
|
-
//# sourceMappingURL=chunk-GO3TJICK.js.map
|