codiedev 0.10.0-beta.3 → 0.10.0-beta.5
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/README.md +7 -1
- package/assets/tui-brand-plugin.tsx +73 -0
- package/dist/backup/cache.js +1 -5
- package/dist/backup/match.js +0 -6
- package/dist/backup/run.js +3 -35
- package/dist/captureExclusions.js +0 -12
- package/dist/captureLog.js +0 -28
- package/dist/captureReadiness.js +0 -58
- package/dist/captureSignals.js +66 -3
- package/dist/cli.js +41 -18
- package/dist/codexHookTrust.js +61 -5
- package/dist/codexLimitScan.js +2 -109
- package/dist/codie.js +41 -56
- package/dist/codieBrandInstall.js +80 -0
- package/dist/codieLauncher.js +59 -59
- package/dist/codieRuntime.js +19 -23
- package/dist/codieRuntimeInstall.js +108 -23
- package/dist/codieStartup.js +0 -3
- package/dist/codieUsage.js +181 -0
- package/dist/commands/ask.js +0 -3
- package/dist/commands/capture.js +0 -2
- package/dist/commands/createTicket.js +0 -10
- package/dist/commands/doctor.js +41 -82
- package/dist/commands/inbox.js +0 -2
- package/dist/commands/library.js +0 -3
- package/dist/commands/pack.js +573 -0
- package/dist/commands/ping.js +0 -2
- package/dist/commands/pull.js +6 -10
- package/dist/commands/push.js +2 -12
- package/dist/commands/react.js +0 -2
- package/dist/commands/reverseTicket.js +0 -33
- package/dist/commands/search.js +0 -4
- package/dist/commands/send.js +0 -5
- package/dist/commands/share.js +0 -4
- package/dist/commands/shared.js +0 -35
- package/dist/commands/traces.js +280 -0
- package/dist/configDir.js +0 -10
- package/dist/connect.js +21 -77
- package/dist/connectFlow.js +0 -9
- package/dist/copilotHooks.js +395 -0
- package/dist/copilotInstallation.js +64 -0
- package/dist/cursorHooks.js +40 -17
- package/dist/cursorLifecycle.js +109 -0
- package/dist/detection.js +0 -30
- package/dist/harnessDirs.js +73 -0
- package/dist/hook.js +193 -316
- package/dist/inboxFormat.js +0 -24
- package/dist/launchContext.js +150 -0
- package/dist/launcher.js +140 -0
- package/dist/limitEvent.js +41 -35
- package/dist/limitEventReporter.js +1 -1
- package/dist/mcp.js +3 -77
- package/dist/nativeSteering.js +24 -0
- package/dist/packs/codie.js +182 -0
- package/dist/packs/harnesses.js +374 -0
- package/dist/packs/hookGate.js +256 -0
- package/dist/packs/install.js +285 -0
- package/dist/packs/manifest.js +126 -0
- package/dist/packs/paths.js +81 -0
- package/dist/packs/shim.js +214 -0
- package/dist/packs/store.js +155 -0
- package/dist/packs/sync.js +170 -0
- package/dist/prUrl.js +0 -5
- package/dist/repoResolver.js +0 -19
- package/dist/secret.js +0 -3
- package/dist/security.js +0 -20
- package/dist/securityDeepScan.js +1 -14
- package/dist/securityDoctor.js +0 -2
- package/dist/securityFeature.js +0 -1
- package/dist/securityInstall.js +6 -13
- package/dist/securityInventory.js +23 -13
- package/dist/securityPilot.js +0 -2
- package/dist/securityPolicy.js +0 -7
- package/dist/selfUpdate.js +331 -0
- package/dist/skillMarker.js +23 -0
- package/dist/subagents.js +135 -0
- package/dist/teamSkillContext.js +10 -0
- package/dist/transcriptModels.js +6 -8
- package/dist/upgradeNudge.js +0 -30
- package/dist/utils.js +286 -733
- package/dist/version.js +0 -10
- package/dist/versionCheck.js +0 -33
- package/package.json +8 -7
- package/dist/backup/cache.d.ts +0 -14
- package/dist/backup/match.d.ts +0 -2
- package/dist/backup/run.d.ts +0 -25
- package/dist/captureExclusions.d.ts +0 -30
- package/dist/captureLog.d.ts +0 -20
- package/dist/captureReadiness.d.ts +0 -65
- package/dist/captureSignals.d.ts +0 -19
- package/dist/cli.d.ts +0 -16
- package/dist/codexHookTrust.d.ts +0 -34
- package/dist/codexLimitScan.d.ts +0 -64
- package/dist/codie.d.ts +0 -2
- package/dist/codieLauncher.d.ts +0 -53
- package/dist/codieRuntime.d.ts +0 -29
- package/dist/codieRuntimeInstall.d.ts +0 -28
- package/dist/codieStartup.d.ts +0 -26
- package/dist/commands/ask.d.ts +0 -1
- package/dist/commands/capture.d.ts +0 -14
- package/dist/commands/createTicket.d.ts +0 -27
- package/dist/commands/delete.d.ts +0 -1
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/inbox.d.ts +0 -3
- package/dist/commands/library.d.ts +0 -2
- package/dist/commands/note.d.ts +0 -1
- package/dist/commands/ping.d.ts +0 -1
- package/dist/commands/promote.d.ts +0 -1
- package/dist/commands/pull.d.ts +0 -2
- package/dist/commands/push.d.ts +0 -33
- package/dist/commands/react.d.ts +0 -2
- package/dist/commands/reverseTicket.d.ts +0 -2
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/security.d.ts +0 -1
- package/dist/commands/send.d.ts +0 -2
- package/dist/commands/share.d.ts +0 -2
- package/dist/commands/shared.d.ts +0 -64
- package/dist/configDir.d.ts +0 -1
- package/dist/connect.d.ts +0 -1
- package/dist/connectFlow.d.ts +0 -13
- package/dist/cursorHooks.d.ts +0 -23
- package/dist/detection.d.ts +0 -30
- package/dist/hook.d.ts +0 -6
- package/dist/inboxFormat.d.ts +0 -38
- package/dist/limitEvent.d.ts +0 -16
- package/dist/limitEventReporter.d.ts +0 -21
- package/dist/mcp.d.ts +0 -12
- package/dist/prUrl.d.ts +0 -9
- package/dist/repoResolver.d.ts +0 -17
- package/dist/secret.d.ts +0 -1
- package/dist/security.d.ts +0 -102
- package/dist/securityDeepScan.d.ts +0 -41
- package/dist/securityDoctor.d.ts +0 -32
- package/dist/securityFeature.d.ts +0 -5
- package/dist/securityInstall.d.ts +0 -63
- package/dist/securityInventory.d.ts +0 -54
- package/dist/securityPilot.d.ts +0 -19
- package/dist/securityPolicy.d.ts +0 -33
- package/dist/securitySession.d.ts +0 -6
- package/dist/transcriptModels.d.ts +0 -18
- package/dist/upgradeNudge.d.ts +0 -25
- package/dist/utils.d.ts +0 -346
- package/dist/version.d.ts +0 -1
- package/dist/versionCheck.d.ts +0 -68
package/dist/connect.js
CHANGED
|
@@ -49,26 +49,13 @@ const shared_1 = require("./commands/shared");
|
|
|
49
49
|
const securityInstall_1 = require("./securityInstall");
|
|
50
50
|
const securityPolicy_1 = require("./securityPolicy");
|
|
51
51
|
const securityFeature_1 = require("./securityFeature");
|
|
52
|
-
|
|
53
|
-
// underlying judicious-falcon-861.convex.site URL keeps serving, so configs
|
|
54
|
-
// written by older CLI versions stay valid without a re-connect.
|
|
52
|
+
const harnessDirs_1 = require("./harnessDirs");
|
|
55
53
|
const BACKEND_URL = process.env.CODIEDEV_URL || "https://cli.codiedev.com";
|
|
56
|
-
/**
|
|
57
|
-
* Reads a line from stdin without echoing the typed/pasted characters —
|
|
58
|
-
* for tokens, passwords, anything that shouldn't end up in terminal
|
|
59
|
-
* scrollback or screenshots. Echoes `*` per character so the user gets
|
|
60
|
-
* visual feedback that input is being received.
|
|
61
|
-
*
|
|
62
|
-
* Falls back to the regular `prompt()` if stdin isn't a TTY (e.g., piped
|
|
63
|
-
* input in CI). Without a TTY there's no echo to suppress anyway, and
|
|
64
|
-
* raw mode would error.
|
|
65
|
-
*/
|
|
66
54
|
function promptHidden(question) {
|
|
67
55
|
return new Promise((resolve) => {
|
|
68
56
|
const stdin = process.stdin;
|
|
69
57
|
const stdout = process.stdout;
|
|
70
58
|
if (!stdin.isTTY) {
|
|
71
|
-
// CI / piped input — no terminal to mute. Fall back to readline.
|
|
72
59
|
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
73
60
|
rl.question(question, (answer) => {
|
|
74
61
|
rl.close();
|
|
@@ -85,23 +72,18 @@ function promptHidden(question) {
|
|
|
85
72
|
};
|
|
86
73
|
const onData = (chunk) => {
|
|
87
74
|
const key = chunk.toString("utf8");
|
|
88
|
-
// Iterate so a single `chunk` containing a multi-char paste still
|
|
89
|
-
// masks each character individually.
|
|
90
75
|
for (const ch of key) {
|
|
91
|
-
// Ctrl+C — abort like a normal terminal interrupt.
|
|
92
76
|
if (ch === "") {
|
|
93
77
|
cleanup();
|
|
94
78
|
stdout.write("\n");
|
|
95
79
|
process.exit(130);
|
|
96
80
|
}
|
|
97
|
-
// Enter — finalize.
|
|
98
81
|
if (ch === "\r" || ch === "\n") {
|
|
99
82
|
cleanup();
|
|
100
83
|
stdout.write("\n");
|
|
101
84
|
resolve(buf.trim());
|
|
102
85
|
return;
|
|
103
86
|
}
|
|
104
|
-
// Backspace / DEL — erase one masked char.
|
|
105
87
|
if (ch === "" || ch === "\b") {
|
|
106
88
|
if (buf.length > 0) {
|
|
107
89
|
buf = buf.slice(0, -1);
|
|
@@ -109,7 +91,6 @@ function promptHidden(question) {
|
|
|
109
91
|
}
|
|
110
92
|
continue;
|
|
111
93
|
}
|
|
112
|
-
// Ignore other control bytes (escape sequences from arrow keys, etc.)
|
|
113
94
|
if (ch.charCodeAt(0) < 0x20)
|
|
114
95
|
continue;
|
|
115
96
|
buf += ch;
|
|
@@ -165,11 +146,6 @@ function postJson(url, body, extraHeaders = {}) {
|
|
|
165
146
|
});
|
|
166
147
|
}
|
|
167
148
|
async function runConnect(args = []) {
|
|
168
|
-
// `--force` (or `-f`) re-prompts for a token even if `~/.codiedev/config.json`
|
|
169
|
-
// already has one. Default behavior reuses the saved token so users can
|
|
170
|
-
// re-run `connect` (e.g., after installing Claude Code) without having to
|
|
171
|
-
// mint a fresh token from the portal — which is what bit Sonifi's first
|
|
172
|
-
// engineer when he interpreted "no visible token" as "the old one is gone".
|
|
173
149
|
const force = args.includes("--force") || args.includes("-f");
|
|
174
150
|
console.log("\nWelcome to Codiedev CLI\n");
|
|
175
151
|
await (0, upgradeNudge_1.maybePrintUpgradeNudge)();
|
|
@@ -183,8 +159,6 @@ async function runConnect(args = []) {
|
|
|
183
159
|
console.log(`Reusing API token from ~/.codiedev/config.json (run with --force to use a new one).\n`);
|
|
184
160
|
}
|
|
185
161
|
else {
|
|
186
|
-
// Hidden prompt — masks the token with `*` as it's typed/pasted so it
|
|
187
|
-
// doesn't end up in terminal scrollback or screenshots.
|
|
188
162
|
token = await promptHidden("Enter your Codiedev API token: ");
|
|
189
163
|
if (!token) {
|
|
190
164
|
console.error("Error: API token is required.");
|
|
@@ -202,7 +176,6 @@ async function runConnect(args = []) {
|
|
|
202
176
|
console.error(`\nError: Failed to validate token — ${err.message}`);
|
|
203
177
|
process.exit(1);
|
|
204
178
|
}
|
|
205
|
-
// Extract fields from response
|
|
206
179
|
const companyId = responseData.companyId;
|
|
207
180
|
const companyName = responseData.companyName;
|
|
208
181
|
const agentSecurityEnabled = responseData.agentSecurityEnabled === true;
|
|
@@ -230,15 +203,7 @@ async function runConnect(args = []) {
|
|
|
230
203
|
console.error(`\nError: Failed to save config — ${err.message}`);
|
|
231
204
|
process.exit(1);
|
|
232
205
|
}
|
|
233
|
-
// Once we have a valid token + config, every subsequent step posts a
|
|
234
|
-
// per-step row to /api/telemetry/install-event so the admin page can show
|
|
235
|
-
// a timeline when a customer says "it connected but it doesn't work."
|
|
236
|
-
// Fire-and-forget — never blocks the connect flow.
|
|
237
206
|
(0, shared_1.recordInstallEvent)(config, { step: "started", ok: true });
|
|
238
|
-
// One-way device status sync. Local config remains authoritative: a network
|
|
239
|
-
// failure here never removes or weakens an ignored path. This also registers
|
|
240
|
-
// a freshly connected device with an empty list so the portal can distinguish
|
|
241
|
-
// "connected, no exclusions" from "old CLI has never reported status".
|
|
242
207
|
try {
|
|
243
208
|
await postJson(`${BACKEND_URL}/api/cli/capture-exclusions`, {
|
|
244
209
|
deviceId: config.deviceId,
|
|
@@ -249,17 +214,6 @@ async function runConnect(args = []) {
|
|
|
249
214
|
catch (err) {
|
|
250
215
|
console.warn(`Warning: Local capture settings are active, but portal status did not sync — ${err.message}`);
|
|
251
216
|
}
|
|
252
|
-
// Migration: strip MCP server entries written by pre-CLI-only versions
|
|
253
|
-
// of `codiedev connect`. These pointed at `npx codiedev-mcp`, a binary
|
|
254
|
-
// that was removed in 0.3.4 and that 0.7.11+ no longer ships. Without
|
|
255
|
-
// cleanup, customers upgrading from any prior version see persistent
|
|
256
|
-
// "MCP server failed to start" errors in their agent UIs even though
|
|
257
|
-
// the CLI works fine. Each helper is idempotent and a no-op when the
|
|
258
|
-
// relevant config file doesn't exist.
|
|
259
|
-
//
|
|
260
|
-
// Each call is wrapped because the user's upgrade path matters more
|
|
261
|
-
// than the cleanup. A locked / wrong-owner config file (rare but real)
|
|
262
|
-
// shouldn't block the install loop or strand them on an old version.
|
|
263
217
|
for (const [label, fn] of [
|
|
264
218
|
["Claude Code", utils_1.cleanupClaudeCodeMcp],
|
|
265
219
|
["Codex", utils_1.cleanupCodexMcp],
|
|
@@ -278,29 +232,24 @@ async function runConnect(args = []) {
|
|
|
278
232
|
const hasCodex = (0, utils_1.codexInstalled)();
|
|
279
233
|
const hasCursor = (0, utils_1.cursorInstalled)();
|
|
280
234
|
const hasOpenCode = (0, utils_1.openCodeInstalled)();
|
|
235
|
+
const hasCopilot = (0, utils_1.copilotInstalled)();
|
|
281
236
|
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "claude-code", ok: hasClaude });
|
|
282
237
|
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "codex", ok: hasCodex });
|
|
283
238
|
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "cursor", ok: hasCursor });
|
|
284
239
|
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "opencode", ok: hasOpenCode });
|
|
240
|
+
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "copilot", ok: hasCopilot });
|
|
285
241
|
const installed = [];
|
|
286
|
-
// Machine-readable target keys reported back to the backend via
|
|
287
|
-
// /api/cli/markInstalled so the portal's "Connected" state reflects
|
|
288
|
-
// what's actually wired up on this machine, not just "token validated."
|
|
289
242
|
const installedTargets = [];
|
|
290
243
|
const home = os.homedir();
|
|
291
244
|
const securityConfigPaths = [
|
|
292
|
-
path.join(
|
|
293
|
-
path.join(
|
|
245
|
+
path.join((0, harnessDirs_1.claudeConfigDir)(), "settings.json"),
|
|
246
|
+
path.join((0, harnessDirs_1.codexHome)(), "hooks.json"),
|
|
294
247
|
path.join(home, ".cursor", "hooks.json"),
|
|
295
|
-
path.join(
|
|
296
|
-
path.join(
|
|
248
|
+
path.join((0, harnessDirs_1.openCodeConfigDir)(), "plugins", "codiedev.js"),
|
|
249
|
+
path.join((0, harnessDirs_1.codexHome)(), "config.toml"),
|
|
250
|
+
(0, utils_1.copilotHooksPath)(),
|
|
297
251
|
];
|
|
298
|
-
// Snapshot before any hook update. Rollback is intentionally not automatic:
|
|
299
|
-
// an administrator can inspect these 0600 copies before restoring them.
|
|
300
252
|
const securitySnapshot = (0, securityInstall_1.createSecurityInstallSnapshot)(securityConfigPaths);
|
|
301
|
-
// Wrap each per-target write so we emit one install_target row each. The
|
|
302
|
-
// existing user-visible behavior (console.warn on failure, continue) is
|
|
303
|
-
// unchanged — telemetry just observes the outcome.
|
|
304
253
|
const runInstall = (target, fn, label, targetKey) => {
|
|
305
254
|
try {
|
|
306
255
|
fn();
|
|
@@ -320,24 +269,22 @@ async function runConnect(args = []) {
|
|
|
320
269
|
}
|
|
321
270
|
};
|
|
322
271
|
if (hasClaude) {
|
|
323
|
-
runInstall("claude-code-hook", () => (0, utils_1.installHook)(agentSecurityEnabled),
|
|
324
|
-
runInstall("claude-code-instructions", utils_1.installClaudeCodeInstructions,
|
|
272
|
+
runInstall("claude-code-hook", () => (0, utils_1.installHook)(agentSecurityEnabled), `Claude Code SessionEnd (capture) + UserPromptSubmit (skill discovery) + Stop (auto-backup) hooks (${(0, harnessDirs_1.displayPath)(path.join((0, harnessDirs_1.claudeConfigDir)(), "settings.json"))})`, "claudeCode-hook");
|
|
273
|
+
runInstall("claude-code-instructions", utils_1.installClaudeCodeInstructions, `Claude Code agent instructions (${(0, harnessDirs_1.displayPath)(path.join((0, harnessDirs_1.claudeConfigDir)(), "CLAUDE.md"))})`, "claudeCode-instructions");
|
|
325
274
|
}
|
|
326
275
|
if (hasCodex) {
|
|
327
|
-
runInstall("codex-hook", () => (0, utils_1.installCodexHook)(agentSecurityEnabled),
|
|
328
|
-
runInstall("codex-instructions", utils_1.installCodexInstructions,
|
|
276
|
+
runInstall("codex-hook", () => (0, utils_1.installCodexHook)(agentSecurityEnabled), `Codex Stop (capture + auto-backup) hooks (${(0, harnessDirs_1.displayPath)(path.join((0, harnessDirs_1.codexHome)(), "hooks.json"))})`, "codex-hook");
|
|
277
|
+
runInstall("codex-instructions", utils_1.installCodexInstructions, `Codex agent instructions (${(0, harnessDirs_1.displayPath)(path.join((0, harnessDirs_1.codexHome)(), "AGENTS.md"))})`, "codex-instructions");
|
|
329
278
|
}
|
|
330
279
|
if (hasCursor) {
|
|
331
280
|
runInstall("cursor-hook", () => (0, utils_1.installCursorHook)(agentSecurityEnabled), "Cursor sessionStart + prompt security + stop backup + sessionEnd capture hooks (~/.cursor/hooks.json)", "cursor-hook");
|
|
332
281
|
runInstall("cursor-instructions", utils_1.installCursorInstructions, "Cursor agent instructions (~/.cursor/rules/codiedev.mdc)", "cursor-instructions");
|
|
333
282
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
// This supplies health/integrity checks without requiring a package publish
|
|
340
|
-
// or trusting whichever `node` happens to be first on PATH later.
|
|
283
|
+
if (hasCopilot) {
|
|
284
|
+
runInstall("copilot-hook", () => (0, utils_1.installCopilotHook)(agentSecurityEnabled), `GitHub Copilot (VS Code + CLI) sessionStart + agentStop backup + sessionEnd capture hooks (${(0, utils_1.copilotHooksPath)()})`, "copilot-hook");
|
|
285
|
+
runInstall("copilot-instructions", utils_1.installCopilotInstructions, "GitHub Copilot CLI team artifact instructions", "copilot-instructions");
|
|
286
|
+
}
|
|
287
|
+
runInstall("opencode-capture-adapter", () => (0, utils_1.installOpenCodeCapturePlugin)(agentSecurityEnabled), `OpenCode session capture adapter (${(0, harnessDirs_1.displayPath)(path.join((0, harnessDirs_1.openCodeConfigDir)(), "plugins", "codiedev.js"))})`, "opencode-capture");
|
|
341
288
|
try {
|
|
342
289
|
const hookScriptPath = path.join(__dirname, "hook.js");
|
|
343
290
|
if (agentSecurityEnabled && fs.existsSync(hookScriptPath)) {
|
|
@@ -348,6 +295,8 @@ async function runConnect(args = []) {
|
|
|
348
295
|
targets.push({ surface: "codex", configPath: securityConfigPaths[1] });
|
|
349
296
|
if (installedTargets.includes("cursor-hook"))
|
|
350
297
|
targets.push({ surface: "cursor", configPath: securityConfigPaths[2] });
|
|
298
|
+
if (installedTargets.includes("copilot-hook"))
|
|
299
|
+
targets.push({ surface: "copilot", configPath: (0, utils_1.copilotHooksPath)() });
|
|
351
300
|
if (installedTargets.includes("opencode-capture"))
|
|
352
301
|
targets.push({ surface: "codie-harness", configPath: securityConfigPaths[3] });
|
|
353
302
|
(0, securityInstall_1.writeSecurityInstallManifest)({ hookScriptPath, snapshot: securitySnapshot, targets });
|
|
@@ -356,8 +305,6 @@ async function runConnect(args = []) {
|
|
|
356
305
|
catch (err) {
|
|
357
306
|
console.warn(`Warning: Security hooks installed, but integrity manifest could not be written — ${err.message}`);
|
|
358
307
|
}
|
|
359
|
-
// Prime the local cache after installation. Hooks read this file only, so
|
|
360
|
-
// an endpoint decision never depends on live network latency.
|
|
361
308
|
if (agentSecurityEnabled) {
|
|
362
309
|
try {
|
|
363
310
|
await (0, securityPolicy_1.syncEndpointSecurityPolicy)(config);
|
|
@@ -366,10 +313,6 @@ async function runConnect(args = []) {
|
|
|
366
313
|
console.warn(`Warning: Agent-security policy will use safe defaults until sync succeeds — ${err.message}`);
|
|
367
314
|
}
|
|
368
315
|
}
|
|
369
|
-
// Tell the backend which targets we wired up so the portal's onboarding
|
|
370
|
-
// card flips to "Connected" only when something actually installed —
|
|
371
|
-
// not on bare token validation. Best-effort: a network failure here
|
|
372
|
-
// doesn't undo the local install, and `codiedev doctor` still works.
|
|
373
316
|
if (installedTargets.length > 0) {
|
|
374
317
|
try {
|
|
375
318
|
await postJson(`${BACKEND_URL}/api/cli/markInstalled`, {
|
|
@@ -388,7 +331,7 @@ async function runConnect(args = []) {
|
|
|
388
331
|
});
|
|
389
332
|
}
|
|
390
333
|
}
|
|
391
|
-
if (!hasClaude && !hasCodex && !hasCursor && !hasOpenCode) {
|
|
334
|
+
if (!hasClaude && !hasCodex && !hasCursor && !hasOpenCode && !hasCopilot) {
|
|
392
335
|
console.warn("\nNo existing AI client detected; the Codie/OpenCode adapter is ready for a later install.");
|
|
393
336
|
console.warn("If you just installed Claude Code, launch it once (so ~/.claude is created),");
|
|
394
337
|
console.warn("then re-run `npx codiedev connect` to wire up the capture hook.");
|
|
@@ -404,6 +347,7 @@ async function runConnect(args = []) {
|
|
|
404
347
|
}
|
|
405
348
|
console.log();
|
|
406
349
|
console.log("Run `codiedev doctor` to verify everything's wired up.");
|
|
350
|
+
console.log("If `codiedev` is not on PATH, run `npx codiedev@latest doctor` instead.");
|
|
407
351
|
if ((0, securityFeature_1.isAgentSecurityEnabled)(config)) {
|
|
408
352
|
console.log("Run `codiedev security pilot` to validate blocking safely.");
|
|
409
353
|
}
|
package/dist/connectFlow.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Pure helper for `npx codiedev connect` — decides where the API token
|
|
3
|
-
// comes from. Splitting this out lets us TDD the policy (re-use vs. re-prompt)
|
|
4
|
-
// without touching readline or fs at all.
|
|
5
|
-
//
|
|
6
|
-
// Motivation: Jackson hit a confusing flow where he had a working token in
|
|
7
|
-
// `~/.codiedev/config.json`, re-ran `connect` (to pick up Claude Code that
|
|
8
|
-
// he'd just installed), and was forced to paste a token again. Since the
|
|
9
|
-
// portal only displays the plaintext on creation, his only option was to
|
|
10
|
-
// generate a new one — leaving him thinking the old one had been lost.
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.resolveConnectToken = resolveConnectToken;
|
|
13
4
|
function resolveConnectToken(input) {
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.COPILOT_VSCODE_CLIENT = void 0;
|
|
37
|
+
exports.copilotHome = copilotHome;
|
|
38
|
+
exports.copilotSessionDir = copilotSessionDir;
|
|
39
|
+
exports.copilotTranscriptPath = copilotTranscriptPath;
|
|
40
|
+
exports.parseCopilotWorkspace = parseCopilotWorkspace;
|
|
41
|
+
exports.readCopilotWorkspace = readCopilotWorkspace;
|
|
42
|
+
exports.copilotLaunch = copilotLaunch;
|
|
43
|
+
exports.normalizeCopilotHookInput = normalizeCopilotHookInput;
|
|
44
|
+
exports.copilotPreToolBlockResponse = copilotPreToolBlockResponse;
|
|
45
|
+
exports.isCopilotTranscriptEmpty = isCopilotTranscriptEmpty;
|
|
46
|
+
exports.hasCopilotShutdown = hasCopilotShutdown;
|
|
47
|
+
exports.normalizeCopilotTranscript = normalizeCopilotTranscript;
|
|
48
|
+
const fs = __importStar(require("node:fs"));
|
|
49
|
+
const os = __importStar(require("node:os"));
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
function copilotHome(env = process.env, home = os.homedir()) {
|
|
52
|
+
const override = env.COPILOT_HOME?.trim();
|
|
53
|
+
return override ? path.resolve(override) : path.join(home, ".copilot");
|
|
54
|
+
}
|
|
55
|
+
const SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
56
|
+
function copilotSessionDir(sessionId, home = copilotHome()) {
|
|
57
|
+
return SESSION_ID.test(sessionId) ? path.join(home, "session-state", sessionId) : undefined;
|
|
58
|
+
}
|
|
59
|
+
function copilotTranscriptPath(sessionId, home = copilotHome()) {
|
|
60
|
+
const dir = sessionId ? copilotSessionDir(sessionId, home) : undefined;
|
|
61
|
+
return dir ? path.join(dir, "events.jsonl") : undefined;
|
|
62
|
+
}
|
|
63
|
+
function parseCopilotWorkspace(yaml) {
|
|
64
|
+
const fields = new Map();
|
|
65
|
+
for (const line of yaml.split(/\r?\n/)) {
|
|
66
|
+
const match = line.match(/^([a-z_]+):\s*(.*)$/);
|
|
67
|
+
if (!match)
|
|
68
|
+
continue;
|
|
69
|
+
let value = match[2].trim();
|
|
70
|
+
if (/^(["']).*\1$/.test(value))
|
|
71
|
+
value = value.slice(1, -1);
|
|
72
|
+
if (value)
|
|
73
|
+
fields.set(match[1], value);
|
|
74
|
+
}
|
|
75
|
+
const pick = (key) => fields.get(key);
|
|
76
|
+
return {
|
|
77
|
+
...(pick("id") ? { id: pick("id") } : {}),
|
|
78
|
+
...(pick("cwd") ? { cwd: pick("cwd") } : {}),
|
|
79
|
+
...(pick("git_root") ? { gitRoot: pick("git_root") } : {}),
|
|
80
|
+
...(pick("branch") ? { branch: pick("branch") } : {}),
|
|
81
|
+
...(pick("client_name") ? { clientName: pick("client_name") } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function readCopilotWorkspace(transcriptPathOrDir) {
|
|
85
|
+
if (!transcriptPathOrDir)
|
|
86
|
+
return {};
|
|
87
|
+
const dir = transcriptPathOrDir.endsWith(".jsonl") ? path.dirname(transcriptPathOrDir) : transcriptPathOrDir;
|
|
88
|
+
try {
|
|
89
|
+
return parseCopilotWorkspace(fs.readFileSync(path.join(dir, "workspace.yaml"), "utf8"));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.COPILOT_VSCODE_CLIENT = "vscode-agent-host";
|
|
96
|
+
const isVSCodeClient = (name) => name === exports.COPILOT_VSCODE_CLIENT || name === "vscode";
|
|
97
|
+
function copilotLaunch(clientName, detected) {
|
|
98
|
+
if (isVSCodeClient(clientName))
|
|
99
|
+
return "copilot:vscode";
|
|
100
|
+
return detected;
|
|
101
|
+
}
|
|
102
|
+
const firstString = (...values) => values.find((value) => typeof value === "string" && value.trim().length > 0);
|
|
103
|
+
function normalizeCopilotHookInput(input, home = copilotHome()) {
|
|
104
|
+
const sessionId = firstString(input.session_id, input.sessionId);
|
|
105
|
+
const transcriptPath = firstString(input.transcript_path, input.transcriptPath)
|
|
106
|
+
?? copilotTranscriptPath(sessionId, home);
|
|
107
|
+
const calls = copilotToolCalls(input);
|
|
108
|
+
const first = calls[0];
|
|
109
|
+
const toolResult = input.toolResult ?? input.tool_response;
|
|
110
|
+
return {
|
|
111
|
+
...input,
|
|
112
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
113
|
+
...(transcriptPath ? { transcript_path: transcriptPath } : {}),
|
|
114
|
+
...(first ? { tool_name: first.name, tool_input: first.input } : {}),
|
|
115
|
+
...(first?.id ? { tool_use_id: first.id } : {}),
|
|
116
|
+
...(toolResult !== undefined ? { tool_response: toolResult } : {}),
|
|
117
|
+
...(calls.length > 1 ? { copilotToolCalls: calls } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function copilotToolCalls(input) {
|
|
121
|
+
const parse = (args) => {
|
|
122
|
+
if (typeof args !== "string")
|
|
123
|
+
return args;
|
|
124
|
+
try {
|
|
125
|
+
return JSON.parse(args);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return args;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
if (Array.isArray(input.toolCalls)) {
|
|
132
|
+
return input.toolCalls.flatMap((raw) => {
|
|
133
|
+
const call = raw;
|
|
134
|
+
const name = firstString(call?.name, call?.toolName);
|
|
135
|
+
return name
|
|
136
|
+
? [{ ...(typeof call.id === "string" ? { id: call.id } : {}), name, input: parse(call.args ?? call.toolArgs) }]
|
|
137
|
+
: [];
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
const name = firstString(input.toolName, input.tool_name);
|
|
141
|
+
return name ? [{ name, input: parse(input.toolArgs ?? input.tool_input) }] : [];
|
|
142
|
+
}
|
|
143
|
+
function copilotPreToolBlockResponse(reason) {
|
|
144
|
+
return { permissionDecision: "deny", permissionDecisionReason: reason };
|
|
145
|
+
}
|
|
146
|
+
function isCopilotTranscriptEmpty(content) {
|
|
147
|
+
return content.trim().length === 0;
|
|
148
|
+
}
|
|
149
|
+
function hasCopilotShutdown(content) {
|
|
150
|
+
return content.includes('"session.shutdown"');
|
|
151
|
+
}
|
|
152
|
+
function claudeTool(name, args) {
|
|
153
|
+
const a = (args && typeof args === "object" ? args : {});
|
|
154
|
+
const lower = name.toLowerCase();
|
|
155
|
+
const file = firstString(a.path, a.file_path, a.filePath);
|
|
156
|
+
if (lower === "bash" || lower === "shell" || lower === "powershell") {
|
|
157
|
+
return { name: "Bash", input: { ...a, command: firstString(a.command, a.cmd) ?? "" } };
|
|
158
|
+
}
|
|
159
|
+
if ((lower === "edit" || lower === "str_replace" || lower === "str_replace_editor" || lower === "apply_patch") && file) {
|
|
160
|
+
return { name: "Edit", input: { ...a, file_path: file } };
|
|
161
|
+
}
|
|
162
|
+
if ((lower === "create" || lower === "write") && file)
|
|
163
|
+
return { name: "Write", input: { ...a, file_path: file } };
|
|
164
|
+
if (lower === "view" && file)
|
|
165
|
+
return { name: "Read", input: { ...a, file_path: file } };
|
|
166
|
+
if (lower === "skill")
|
|
167
|
+
return { name: "skill", input: {} };
|
|
168
|
+
return { name, input: args ?? {} };
|
|
169
|
+
}
|
|
170
|
+
const num = (value) => (typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0);
|
|
171
|
+
function copilotUserContent(data) {
|
|
172
|
+
const content = [];
|
|
173
|
+
if (typeof data.content === "string" && data.content)
|
|
174
|
+
content.push({ type: "text", text: data.content });
|
|
175
|
+
if (Array.isArray(data.attachments)) {
|
|
176
|
+
for (const attachment of data.attachments) {
|
|
177
|
+
if (!attachment || typeof attachment !== "object")
|
|
178
|
+
continue;
|
|
179
|
+
const { displayName, mimeType, type } = attachment;
|
|
180
|
+
const name = typeof displayName === "string" ? displayName : "unnamed attachment";
|
|
181
|
+
const kind = typeof mimeType === "string" ? mimeType : typeof type === "string" ? type : "attachment";
|
|
182
|
+
content.push({ type: "text", text: `[Attachment: ${name} (${kind}); attachment contents unavailable in captured transcript]` });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return content.length ? content : [{ type: "text", text: "" }];
|
|
186
|
+
}
|
|
187
|
+
function resultText(data) {
|
|
188
|
+
const result = data.result;
|
|
189
|
+
if (typeof result === "string")
|
|
190
|
+
return result;
|
|
191
|
+
if (result && typeof result === "object") {
|
|
192
|
+
const { content, detailedContent } = result;
|
|
193
|
+
return [content, detailedContent].filter((v) => typeof v === "string").join("\n");
|
|
194
|
+
}
|
|
195
|
+
return typeof data.error?.message === "string" ? data.error.message : "";
|
|
196
|
+
}
|
|
197
|
+
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
198
|
+
function premiumOf(data, at) {
|
|
199
|
+
return {
|
|
200
|
+
premiumRequests: count(data.totalPremiumRequests),
|
|
201
|
+
nanoAiu: count(data.totalNanoAiu),
|
|
202
|
+
...(at ? { at } : {}),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function sumAgentMetrics(agents) {
|
|
206
|
+
const totals = {};
|
|
207
|
+
for (const metrics of agents) {
|
|
208
|
+
for (const [model, metric] of Object.entries(metrics ?? {})) {
|
|
209
|
+
const into = (totals[model] ??= {});
|
|
210
|
+
for (const [key, value] of Object.entries(metric?.usage ?? {}))
|
|
211
|
+
into[key] = (into[key] ?? 0) + num(value);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return Object.fromEntries(Object.entries(totals).map(([model, usage]) => [model, { usage }]));
|
|
215
|
+
}
|
|
216
|
+
function normalizeCopilotTranscript(raw, opts = {}) {
|
|
217
|
+
const out = [];
|
|
218
|
+
const subagentCalls = new Set();
|
|
219
|
+
let shutdownAgents = 0;
|
|
220
|
+
let assistantModel = false;
|
|
221
|
+
let premium;
|
|
222
|
+
let checkpointModels = [];
|
|
223
|
+
let final = false;
|
|
224
|
+
for (const line of raw.split("\n")) {
|
|
225
|
+
if (!line.trim())
|
|
226
|
+
continue;
|
|
227
|
+
let event;
|
|
228
|
+
try {
|
|
229
|
+
event = JSON.parse(line);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const data = event?.data ?? {};
|
|
235
|
+
const timestamp = typeof event.timestamp === "string" ? event.timestamp : undefined;
|
|
236
|
+
const uuid = typeof event.id === "string" ? event.id : undefined;
|
|
237
|
+
const base = { ...(timestamp ? { timestamp } : {}), ...(uuid ? { uuid } : {}) };
|
|
238
|
+
switch (event.type) {
|
|
239
|
+
case "session.start": {
|
|
240
|
+
const context = data.context ?? {};
|
|
241
|
+
out.push(JSON.stringify({
|
|
242
|
+
type: "system",
|
|
243
|
+
subtype: "copilot_session_start",
|
|
244
|
+
...base,
|
|
245
|
+
...(typeof data.sessionId === "string" ? { sessionId: data.sessionId } : {}),
|
|
246
|
+
...(typeof context.cwd === "string" ? { cwd: context.cwd } : {}),
|
|
247
|
+
...(typeof context.branch === "string" ? { gitBranch: context.branch } : {}),
|
|
248
|
+
...(typeof data.copilotVersion === "string" ? { version: data.copilotVersion } : {}),
|
|
249
|
+
...(isVSCodeClient(opts.clientName) ? { entrypoint: "copilot:vscode" } : {}),
|
|
250
|
+
}));
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
case "session.error":
|
|
254
|
+
out.push(JSON.stringify({ type: "agent.error", ...base, error: data.error ?? { message: data.message, code: data.errorType } }));
|
|
255
|
+
break;
|
|
256
|
+
case "hook.start":
|
|
257
|
+
if (data.hookType === "sessionEnd" && data.input?.reason === "abort") {
|
|
258
|
+
out.push(JSON.stringify({ type: "agent.interruption", ...base, id: uuid }));
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
case "user.message":
|
|
262
|
+
out.push(JSON.stringify({
|
|
263
|
+
type: "user",
|
|
264
|
+
...base,
|
|
265
|
+
message: { role: "user", content: copilotUserContent(data) },
|
|
266
|
+
}));
|
|
267
|
+
break;
|
|
268
|
+
case "subagent.started":
|
|
269
|
+
if (typeof data.toolCallId === "string")
|
|
270
|
+
subagentCalls.add(data.toolCallId);
|
|
271
|
+
break;
|
|
272
|
+
case "session.usage_checkpoint":
|
|
273
|
+
if (!final)
|
|
274
|
+
premium = premiumOf(data, timestamp);
|
|
275
|
+
if (Array.isArray(data.modelCacheState)) {
|
|
276
|
+
checkpointModels = data.modelCacheState
|
|
277
|
+
.map((m) => (m && typeof m === "object" ? m.modelId : undefined))
|
|
278
|
+
.filter((m) => typeof m === "string" && m.length > 0);
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
case "assistant.message": {
|
|
282
|
+
if (typeof data.model === "string" && data.model)
|
|
283
|
+
assistantModel = true;
|
|
284
|
+
const tools = Array.isArray(data.toolRequests) ? data.toolRequests : [];
|
|
285
|
+
const content = [];
|
|
286
|
+
if (typeof data.content === "string" && data.content)
|
|
287
|
+
content.push({ type: "text", text: data.content });
|
|
288
|
+
for (const request of tools) {
|
|
289
|
+
if (typeof request?.name !== "string")
|
|
290
|
+
continue;
|
|
291
|
+
const mapped = claudeTool(request.name, request.arguments);
|
|
292
|
+
content.push({ type: "tool_use", id: request.toolCallId, name: mapped.name, input: mapped.input });
|
|
293
|
+
}
|
|
294
|
+
out.push(JSON.stringify({
|
|
295
|
+
type: "assistant",
|
|
296
|
+
...base,
|
|
297
|
+
tokensAvailable: false,
|
|
298
|
+
message: {
|
|
299
|
+
id: firstString(data.apiCallId, data.messageId, uuid),
|
|
300
|
+
role: "assistant",
|
|
301
|
+
content,
|
|
302
|
+
...(typeof data.model === "string" && data.model
|
|
303
|
+
? {
|
|
304
|
+
model: data.model,
|
|
305
|
+
usage: { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 },
|
|
306
|
+
}
|
|
307
|
+
: {}),
|
|
308
|
+
},
|
|
309
|
+
}));
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case "tool.execution_complete":
|
|
313
|
+
if (data.error?.code === "user_denied") {
|
|
314
|
+
out.push(JSON.stringify({ type: "agent.rejection", ...base, id: data.toolCallId ?? uuid }));
|
|
315
|
+
}
|
|
316
|
+
out.push(JSON.stringify({
|
|
317
|
+
type: "user",
|
|
318
|
+
...base,
|
|
319
|
+
message: {
|
|
320
|
+
role: "user",
|
|
321
|
+
content: [{
|
|
322
|
+
type: "tool_result",
|
|
323
|
+
tool_use_id: data.toolCallId,
|
|
324
|
+
content: resultText(data),
|
|
325
|
+
...(data.success === false ? { is_error: true } : {}),
|
|
326
|
+
}],
|
|
327
|
+
},
|
|
328
|
+
}).replace('"content":[{', '"content":[ {'));
|
|
329
|
+
break;
|
|
330
|
+
case "skill.invoked":
|
|
331
|
+
if (typeof data.name === "string" && data.name) {
|
|
332
|
+
out.push(JSON.stringify({ type: "copilot.skill_invoked", ...base, skill: data.name }));
|
|
333
|
+
}
|
|
334
|
+
break;
|
|
335
|
+
case "session.shutdown": {
|
|
336
|
+
final = true;
|
|
337
|
+
premium = premiumOf(data, timestamp);
|
|
338
|
+
const agents = Object.entries(data.agentMetrics ?? {});
|
|
339
|
+
shutdownAgents = agents.filter(([agent]) => agent !== "main").length;
|
|
340
|
+
const metrics = data.modelMetrics ?? sumAgentMetrics(agents.map(([, agent]) => agent?.modelMetrics));
|
|
341
|
+
for (const [model, metric] of Object.entries(metrics)) {
|
|
342
|
+
const usage = metric?.usage ?? {};
|
|
343
|
+
if (![usage.inputTokens, usage.outputTokens, usage.cacheReadTokens, usage.cacheWriteTokens].some(value => typeof value === "number" && Number.isFinite(value)))
|
|
344
|
+
continue;
|
|
345
|
+
const cacheRead = num(usage.cacheReadTokens);
|
|
346
|
+
out.push(JSON.stringify({
|
|
347
|
+
type: "copilot.usage",
|
|
348
|
+
...base,
|
|
349
|
+
requests: 0,
|
|
350
|
+
message: {
|
|
351
|
+
id: `copilot-usage:${model}`,
|
|
352
|
+
model,
|
|
353
|
+
usage: {
|
|
354
|
+
input_tokens: Math.max(0, num(usage.inputTokens) - cacheRead),
|
|
355
|
+
output_tokens: num(usage.outputTokens),
|
|
356
|
+
cache_read_input_tokens: cacheRead,
|
|
357
|
+
cache_creation_input_tokens: num(usage.cacheWriteTokens),
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
default:
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (!assistantModel) {
|
|
369
|
+
for (const model of checkpointModels) {
|
|
370
|
+
out.push(JSON.stringify({
|
|
371
|
+
type: "copilot.model",
|
|
372
|
+
tokensAvailable: false,
|
|
373
|
+
requests: 0,
|
|
374
|
+
message: {
|
|
375
|
+
id: `copilot-model:${model}`,
|
|
376
|
+
model,
|
|
377
|
+
usage: { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 },
|
|
378
|
+
},
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (premium && (premium.premiumRequests !== undefined || premium.nanoAiu !== undefined)) {
|
|
383
|
+
out.push(JSON.stringify({
|
|
384
|
+
type: "copilot.usage_summary",
|
|
385
|
+
...(premium.at ? { timestamp: premium.at } : {}),
|
|
386
|
+
...(premium.premiumRequests !== undefined ? { premiumRequests: premium.premiumRequests } : {}),
|
|
387
|
+
...(premium.nanoAiu !== undefined ? { nanoAiu: premium.nanoAiu } : {}),
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
content: out.join("\n") + (out.length ? "\n" : ""),
|
|
392
|
+
subagents: { count: Math.max(subagentCalls.size, shutdownAgents), truncated: false },
|
|
393
|
+
final,
|
|
394
|
+
};
|
|
395
|
+
}
|