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.
Files changed (144) hide show
  1. package/README.md +7 -1
  2. package/assets/tui-brand-plugin.tsx +73 -0
  3. package/dist/backup/cache.js +1 -5
  4. package/dist/backup/match.js +0 -6
  5. package/dist/backup/run.js +3 -35
  6. package/dist/captureExclusions.js +0 -12
  7. package/dist/captureLog.js +0 -28
  8. package/dist/captureReadiness.js +0 -58
  9. package/dist/captureSignals.js +66 -3
  10. package/dist/cli.js +41 -18
  11. package/dist/codexHookTrust.js +61 -5
  12. package/dist/codexLimitScan.js +2 -109
  13. package/dist/codie.js +41 -56
  14. package/dist/codieBrandInstall.js +80 -0
  15. package/dist/codieLauncher.js +59 -59
  16. package/dist/codieRuntime.js +19 -23
  17. package/dist/codieRuntimeInstall.js +108 -23
  18. package/dist/codieStartup.js +0 -3
  19. package/dist/codieUsage.js +181 -0
  20. package/dist/commands/ask.js +0 -3
  21. package/dist/commands/capture.js +0 -2
  22. package/dist/commands/createTicket.js +0 -10
  23. package/dist/commands/doctor.js +41 -82
  24. package/dist/commands/inbox.js +0 -2
  25. package/dist/commands/library.js +0 -3
  26. package/dist/commands/pack.js +573 -0
  27. package/dist/commands/ping.js +0 -2
  28. package/dist/commands/pull.js +6 -10
  29. package/dist/commands/push.js +2 -12
  30. package/dist/commands/react.js +0 -2
  31. package/dist/commands/reverseTicket.js +0 -33
  32. package/dist/commands/search.js +0 -4
  33. package/dist/commands/send.js +0 -5
  34. package/dist/commands/share.js +0 -4
  35. package/dist/commands/shared.js +0 -35
  36. package/dist/commands/traces.js +280 -0
  37. package/dist/configDir.js +0 -10
  38. package/dist/connect.js +21 -77
  39. package/dist/connectFlow.js +0 -9
  40. package/dist/copilotHooks.js +395 -0
  41. package/dist/copilotInstallation.js +64 -0
  42. package/dist/cursorHooks.js +40 -17
  43. package/dist/cursorLifecycle.js +109 -0
  44. package/dist/detection.js +0 -30
  45. package/dist/harnessDirs.js +73 -0
  46. package/dist/hook.js +193 -316
  47. package/dist/inboxFormat.js +0 -24
  48. package/dist/launchContext.js +150 -0
  49. package/dist/launcher.js +140 -0
  50. package/dist/limitEvent.js +41 -35
  51. package/dist/limitEventReporter.js +1 -1
  52. package/dist/mcp.js +3 -77
  53. package/dist/nativeSteering.js +24 -0
  54. package/dist/packs/codie.js +182 -0
  55. package/dist/packs/harnesses.js +374 -0
  56. package/dist/packs/hookGate.js +256 -0
  57. package/dist/packs/install.js +285 -0
  58. package/dist/packs/manifest.js +126 -0
  59. package/dist/packs/paths.js +81 -0
  60. package/dist/packs/shim.js +214 -0
  61. package/dist/packs/store.js +155 -0
  62. package/dist/packs/sync.js +170 -0
  63. package/dist/prUrl.js +0 -5
  64. package/dist/repoResolver.js +0 -19
  65. package/dist/secret.js +0 -3
  66. package/dist/security.js +0 -20
  67. package/dist/securityDeepScan.js +1 -14
  68. package/dist/securityDoctor.js +0 -2
  69. package/dist/securityFeature.js +0 -1
  70. package/dist/securityInstall.js +6 -13
  71. package/dist/securityInventory.js +23 -13
  72. package/dist/securityPilot.js +0 -2
  73. package/dist/securityPolicy.js +0 -7
  74. package/dist/selfUpdate.js +331 -0
  75. package/dist/skillMarker.js +23 -0
  76. package/dist/subagents.js +135 -0
  77. package/dist/teamSkillContext.js +10 -0
  78. package/dist/transcriptModels.js +6 -8
  79. package/dist/upgradeNudge.js +0 -30
  80. package/dist/utils.js +286 -733
  81. package/dist/version.js +0 -10
  82. package/dist/versionCheck.js +0 -33
  83. package/package.json +8 -7
  84. package/dist/backup/cache.d.ts +0 -14
  85. package/dist/backup/match.d.ts +0 -2
  86. package/dist/backup/run.d.ts +0 -25
  87. package/dist/captureExclusions.d.ts +0 -30
  88. package/dist/captureLog.d.ts +0 -20
  89. package/dist/captureReadiness.d.ts +0 -65
  90. package/dist/captureSignals.d.ts +0 -19
  91. package/dist/cli.d.ts +0 -16
  92. package/dist/codexHookTrust.d.ts +0 -34
  93. package/dist/codexLimitScan.d.ts +0 -64
  94. package/dist/codie.d.ts +0 -2
  95. package/dist/codieLauncher.d.ts +0 -53
  96. package/dist/codieRuntime.d.ts +0 -29
  97. package/dist/codieRuntimeInstall.d.ts +0 -28
  98. package/dist/codieStartup.d.ts +0 -26
  99. package/dist/commands/ask.d.ts +0 -1
  100. package/dist/commands/capture.d.ts +0 -14
  101. package/dist/commands/createTicket.d.ts +0 -27
  102. package/dist/commands/delete.d.ts +0 -1
  103. package/dist/commands/doctor.d.ts +0 -1
  104. package/dist/commands/inbox.d.ts +0 -3
  105. package/dist/commands/library.d.ts +0 -2
  106. package/dist/commands/note.d.ts +0 -1
  107. package/dist/commands/ping.d.ts +0 -1
  108. package/dist/commands/promote.d.ts +0 -1
  109. package/dist/commands/pull.d.ts +0 -2
  110. package/dist/commands/push.d.ts +0 -33
  111. package/dist/commands/react.d.ts +0 -2
  112. package/dist/commands/reverseTicket.d.ts +0 -2
  113. package/dist/commands/search.d.ts +0 -2
  114. package/dist/commands/security.d.ts +0 -1
  115. package/dist/commands/send.d.ts +0 -2
  116. package/dist/commands/share.d.ts +0 -2
  117. package/dist/commands/shared.d.ts +0 -64
  118. package/dist/configDir.d.ts +0 -1
  119. package/dist/connect.d.ts +0 -1
  120. package/dist/connectFlow.d.ts +0 -13
  121. package/dist/cursorHooks.d.ts +0 -23
  122. package/dist/detection.d.ts +0 -30
  123. package/dist/hook.d.ts +0 -6
  124. package/dist/inboxFormat.d.ts +0 -38
  125. package/dist/limitEvent.d.ts +0 -16
  126. package/dist/limitEventReporter.d.ts +0 -21
  127. package/dist/mcp.d.ts +0 -12
  128. package/dist/prUrl.d.ts +0 -9
  129. package/dist/repoResolver.d.ts +0 -17
  130. package/dist/secret.d.ts +0 -1
  131. package/dist/security.d.ts +0 -102
  132. package/dist/securityDeepScan.d.ts +0 -41
  133. package/dist/securityDoctor.d.ts +0 -32
  134. package/dist/securityFeature.d.ts +0 -5
  135. package/dist/securityInstall.d.ts +0 -63
  136. package/dist/securityInventory.d.ts +0 -54
  137. package/dist/securityPilot.d.ts +0 -19
  138. package/dist/securityPolicy.d.ts +0 -33
  139. package/dist/securitySession.d.ts +0 -6
  140. package/dist/transcriptModels.d.ts +0 -18
  141. package/dist/upgradeNudge.d.ts +0 -25
  142. package/dist/utils.d.ts +0 -346
  143. package/dist/version.d.ts +0 -1
  144. 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
- // cli.codiedev.com is a Convex custom domain on the prod deployment; the
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(home, ".claude", "settings.json"),
293
- path.join(home, ".codex", "hooks.json"),
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(home, ".config", "opencode", "plugins", "codiedev.js"),
296
- path.join(home, ".codex", "config.toml"),
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), "Claude Code SessionEnd (capture) + UserPromptSubmit (skill discovery) + Stop (auto-backup) hooks (~/.claude/settings.json)", "claudeCode-hook");
324
- runInstall("claude-code-instructions", utils_1.installClaudeCodeInstructions, "Claude Code agent instructions (~/.claude/CLAUDE.md)", "claudeCode-instructions");
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), "Codex Stop (capture + auto-backup) hooks (~/.codex/hooks.json)", "codex-hook");
328
- runInstall("codex-instructions", utils_1.installCodexInstructions, "Codex agent instructions (~/.codex/AGENTS.md)", "codex-instructions");
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
- // Always stage the adapter. It is dormant until OpenCode starts, which lets
335
- // a user connect Claude Code today and install the Codie harness tomorrow
336
- // without needing a second setup ritual.
337
- runInstall("opencode-capture-adapter", () => (0, utils_1.installOpenCodeCapturePlugin)(agentSecurityEnabled), "OpenCode session capture adapter (~/.config/opencode/plugins/codiedev.js)", "opencode-capture");
338
- // Record the exact installed hook bytes and the pre-update rollback files.
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
  }
@@ -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
+ }