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
@@ -1,10 +1,4 @@
1
1
  "use strict";
2
- // First-run install of the OpenCode engine for the Codie harness.
3
- //
4
- // Staged then renamed: npm writes into a sibling staging directory and only an
5
- // install that actually produced the engine binary gets promoted to the real
6
- // runtime path. An interrupted or half-finished download therefore never
7
- // leaves a directory that resolveCodieRuntime() would report as "ready".
8
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
3
  if (k2 === undefined) k2 = k;
10
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -39,9 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
39
33
  };
40
34
  })();
41
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.IGNORE_SCRIPTS = void 0;
42
37
  exports.buildRuntimeInstallArgs = buildRuntimeInstallArgs;
43
38
  exports.installCodieRuntime = installCodieRuntime;
44
39
  exports.ensureCodieRuntimeBinary = ensureCodieRuntimeBinary;
40
+ exports.linkFailure = linkFailure;
41
+ exports.windowsCommand = windowsCommand;
42
+ exports.isEngineOnDisk = isEngineOnDisk;
43
+ exports.npmIgnoresScripts = npmIgnoresScripts;
44
+ exports.linkEngineOnDisk = linkEngineOnDisk;
45
45
  exports.installCodieRuntimeOnDisk = installCodieRuntimeOnDisk;
46
46
  exports.defaultStagingRoot = defaultStagingRoot;
47
47
  const fs = __importStar(require("fs"));
@@ -50,8 +50,6 @@ const path = __importStar(require("path"));
50
50
  const child_process_1 = require("child_process");
51
51
  const codieRuntime_1 = require("./codieRuntime");
52
52
  function buildRuntimeInstallArgs(stagingDir, spec) {
53
- // No --registry / --proxy: inherit whatever npm config the developer's
54
- // machine already has so an internal mirror keeps working.
55
53
  return [
56
54
  "install",
57
55
  "--prefix",
@@ -64,7 +62,7 @@ function buildRuntimeInstallArgs(stagingDir, spec) {
64
62
  ];
65
63
  }
66
64
  function installCodieRuntime(deps) {
67
- const { runtime, makeStagingDir, run, exists, rename, remove, log } = deps;
65
+ const { runtime, makeStagingDir, run, exists, rename, remove, log, isEngine = () => true, linkEngine } = deps;
68
66
  const staging = makeStagingDir();
69
67
  log(`Installing the Codie engine (${runtime.spec}). This runs once, and only for \`codie\`.`);
70
68
  let result;
@@ -79,56 +77,141 @@ function installCodieRuntime(deps) {
79
77
  remove(staging);
80
78
  throw installFailure(result.stderr?.trim() || `npm exited with ${result.status}`);
81
79
  }
82
- // Trust the artifact, not the exit code — a misconfigured mirror can succeed
83
- // with nothing useful in the tarball.
84
80
  const stagedBinary = path.join(staging, "node_modules", "opencode-ai", "bin", "opencode.exe");
85
81
  if (!exists(stagedBinary)) {
86
82
  remove(staging);
87
83
  throw new Error(`The Codie engine install did not produce a runtime binary. ` +
88
84
  `Check that ${runtime.spec} is available from your npm registry.`);
89
85
  }
86
+ if (!isEngine(stagedBinary)) {
87
+ let linked = { ok: false };
88
+ try {
89
+ linked = linkEngine?.(path.dirname(path.dirname(stagedBinary))) ?? { ok: false };
90
+ }
91
+ catch (error) {
92
+ linked = { ok: false, detail: error instanceof Error ? error.message : String(error) };
93
+ }
94
+ if (!linked.ok || !isEngine(stagedBinary)) {
95
+ if (linked.detail === exports.IGNORE_SCRIPTS) {
96
+ try {
97
+ rename(staging, runtime.installDir);
98
+ }
99
+ finally {
100
+ remove(staging);
101
+ }
102
+ }
103
+ else {
104
+ remove(staging);
105
+ }
106
+ throw new Error(linkFailure(linked, path.join(runtime.installDir, "node_modules", "opencode-ai")));
107
+ }
108
+ }
90
109
  try {
91
110
  rename(staging, runtime.installDir);
92
111
  }
93
112
  finally {
94
- // No-op after a successful promote (rmSync is force: true); after a failed
95
- // one it keeps a ~132MB staging dir from being orphaned — mkdtemp mints a
96
- // fresh sibling every attempt, so leaks would accumulate.
97
113
  remove(staging);
98
114
  }
99
115
  }
100
- // Resolve the engine, downloading it once if this is the first `codie` run.
101
- async function ensureCodieRuntimeBinary({ resolve, install, confirmUpdate = async () => false, }) {
116
+ async function ensureCodieRuntimeBinary({ resolve, install, confirmUpdate = async () => false, repair, }) {
102
117
  const runtime = resolve();
103
118
  if (runtime.kind === "ready")
104
119
  return runtime.binary;
105
- if (runtime.kind === "outdated" && !(await confirmUpdate(runtime)))
120
+ if (runtime.kind === "outdated" && !(await confirmUpdate(runtime))) {
121
+ if (runtime.linked !== false || !repair || !runtime.packageDir)
122
+ return runtime.binary;
123
+ if (repair({ kind: "unlinked", installDir: runtime.installDir, binary: runtime.binary, spec: runtime.spec, packageDir: runtime.packageDir })) {
124
+ return runtime.binary;
125
+ }
126
+ install(runtime);
106
127
  return runtime.binary;
128
+ }
129
+ if (runtime.kind === "unlinked" && repair?.(runtime)) {
130
+ const after = resolve();
131
+ if (after.kind === "ready")
132
+ return after.binary;
133
+ }
107
134
  install(runtime);
108
135
  return runtime.binary;
109
136
  }
137
+ function linkFailure(linked, packageDir) {
138
+ if (linked.detail === exports.IGNORE_SCRIPTS) {
139
+ const script = packageDir ? `"${path.join(packageDir, "postinstall.mjs")}"` : "postinstall.mjs in the opencode-ai package";
140
+ return (`The Codie engine installed, but npm is set to ignore install scripts (ignore-scripts=true), ` +
141
+ `so codie won't run opencode-ai's setup step. Run \`node ${script}\` yourself, ` +
142
+ `or set CODIEDEV_OPENCODE_BINARY to an opencode binary.`);
143
+ }
144
+ return (`The Codie engine installed, but its setup step (opencode-ai's postinstall) failed, so there is no runnable engine` +
145
+ (linked.detail ? `: ${linked.detail}` : ".") +
146
+ ` Set CODIEDEV_OPENCODE_BINARY to an opencode binary to use a pre-provisioned engine.`);
147
+ }
148
+ exports.IGNORE_SCRIPTS = "ignore-scripts";
110
149
  function installFailure(detail) {
111
150
  return new Error(`Could not download the Codie engine: ${detail}\n` +
112
151
  `If you are behind a proxy or an internal registry, make sure npm can reach it. ` +
113
152
  `To use a pre-provisioned engine instead, set CODIEDEV_OPENCODE_BINARY to its path.`);
114
153
  }
115
- // ── Real-world wiring ───────────────────────────────────────────────────
116
- // Kept separate from the pure logic above so tests never shell out to npm.
154
+ function windowsCommand(command, args) {
155
+ const quote = (a) => (/[\s"&|<>^()%!]/.test(a) ? `"${a.replace(/"/g, '""')}"` : a);
156
+ return [command, ...args.map(quote)].join(" ");
157
+ }
158
+ function isEngineOnDisk(candidate) {
159
+ try {
160
+ return (0, codieRuntime_1.isEngineSize)(fs.statSync(candidate).size);
161
+ }
162
+ catch {
163
+ return false;
164
+ }
165
+ }
166
+ function npmIgnoresScripts(env = process.env) {
167
+ const fromEnv = env.npm_config_ignore_scripts ?? env.NPM_CONFIG_IGNORE_SCRIPTS;
168
+ if (fromEnv !== undefined)
169
+ return !["", "false", "0", "null", "no"].includes(fromEnv.trim().toLowerCase());
170
+ const opts = { encoding: "utf8", timeout: 10_000, windowsHide: true };
171
+ const result = process.platform === "win32"
172
+ ? (0, child_process_1.spawnSync)("npm.cmd config get ignore-scripts", { ...opts, shell: true })
173
+ : (0, child_process_1.spawnSync)("npm", ["config", "get", "ignore-scripts"], opts);
174
+ return result.status === 0 && (result.stdout || "").trim() === "true";
175
+ }
176
+ const LINK_TIMEOUT_MS = 120_000;
177
+ function linkEngineOnDisk(packageDir, { ignoreScripts = npmIgnoresScripts } = {}) {
178
+ const script = path.join(packageDir, "postinstall.mjs");
179
+ if (!fs.existsSync(script))
180
+ return { ok: false, detail: "opencode-ai has no postinstall.mjs" };
181
+ if (ignoreScripts())
182
+ return { ok: false, detail: exports.IGNORE_SCRIPTS };
183
+ const result = (0, child_process_1.spawnSync)(process.execPath, [script], {
184
+ cwd: packageDir,
185
+ encoding: "utf8",
186
+ stdio: ["ignore", "ignore", "pipe"],
187
+ timeout: LINK_TIMEOUT_MS,
188
+ windowsHide: true,
189
+ });
190
+ if (result.error || result.signal) {
191
+ return { ok: false, detail: result.error ? result.error.message : `timed out after ${LINK_TIMEOUT_MS / 1000}s` };
192
+ }
193
+ if (result.status !== 0)
194
+ return { ok: false, detail: (result.stderr || "").trim().split("\n").slice(-3).join(" ") || `exit ${result.status}` };
195
+ return isEngineOnDisk(path.join(packageDir, "bin", "opencode.exe"))
196
+ ? { ok: true }
197
+ : { ok: false, detail: "postinstall ran but left no engine" };
198
+ }
117
199
  function installCodieRuntimeOnDisk(runtime, stateDir) {
118
200
  installCodieRuntime({
119
201
  runtime,
120
202
  makeStagingDir: () => {
121
- // Sibling of the final dir so the promote is a same-filesystem rename.
122
203
  fs.mkdirSync(path.dirname((0, codieRuntime_1.codieRuntimeDir)(stateDir)), { recursive: true });
123
204
  return fs.mkdtempSync(path.join(path.dirname((0, codieRuntime_1.codieRuntimeDir)(stateDir)), ".runtime-staging-"));
124
205
  },
125
206
  run: (args) => {
126
- const npm = process.platform === "win32" ? "npm.cmd" : "npm";
127
- const result = (0, child_process_1.spawnSync)(npm, args, {
207
+ const opts = {
128
208
  encoding: "utf8",
129
209
  stdio: ["ignore", "inherit", "pipe"],
130
210
  env: { ...process.env, npm_config_yes: "true" },
131
- });
211
+ };
212
+ const result = process.platform === "win32"
213
+ ? (0, child_process_1.spawnSync)(windowsCommand("npm.cmd", args), { ...opts, shell: true, windowsHide: true })
214
+ : (0, child_process_1.spawnSync)("npm", args, opts);
132
215
  if (result.error)
133
216
  throw result.error;
134
217
  return { status: result.status, stderr: result.stderr || "" };
@@ -140,6 +223,8 @@ function installCodieRuntimeOnDisk(runtime, stateDir) {
140
223
  },
141
224
  remove: (target) => fs.rmSync(target, { recursive: true, force: true }),
142
225
  log: (message) => console.error(message),
226
+ isEngine: isEngineOnDisk,
227
+ linkEngine: linkEngineOnDisk,
143
228
  });
144
229
  }
145
230
  function defaultStagingRoot() {
@@ -3,9 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.refreshCodieSecurityPolicy = refreshCodieSecurityPolicy;
4
4
  const securityFeature_1 = require("./securityFeature");
5
5
  const securityPolicy_1 = require("./securityPolicy");
6
- /** Refresh the endpoint policy before Codie hands the terminal to OpenCode.
7
- * Enforcement remains available offline because hooks keep using the last
8
- * verified company-scoped policy when this best-effort refresh cannot land. */
9
6
  async function refreshCodieSecurityPolicy(config, syncPolicy = securityPolicy_1.syncEndpointSecurityPolicy) {
10
7
  if (!(0, securityFeature_1.isAgentSecurityEnabled)(config))
11
8
  return { status: "disabled" };
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatUsageLine = formatUsageLine;
4
+ exports.usageTone = usageTone;
5
+ exports.formatSessionCostLine = formatSessionCostLine;
6
+ exports.fetchCodieUsage = fetchCodieUsage;
7
+ const USAGE_TIMEOUT_MS = 4000;
8
+ function cleanBaseUrl(value) {
9
+ return String(value || "https://gateway.codiedev.com/api/v1").replace(/\/+$/, "");
10
+ }
11
+ function money(value) {
12
+ return `$${value.toFixed(2)}`;
13
+ }
14
+ function budgetMoney(value) {
15
+ return Number.isInteger(value) ? `$${value}` : money(value);
16
+ }
17
+ function percent(spent, budget) {
18
+ if (!(budget > 0))
19
+ return null;
20
+ const share = (spent / budget) * 100;
21
+ if (share > 0 && share < 1)
22
+ return "<1%";
23
+ return `${Math.round(share)}%`;
24
+ }
25
+ function formatUsageLine(usage) {
26
+ if (!usage || !usage.enabled)
27
+ return "";
28
+ const { budget, inFlight } = usage;
29
+ const segments = [];
30
+ if (usage.user?.name)
31
+ segments.push(usage.user.name);
32
+ if (typeof budget.userBudgetUsd === "number") {
33
+ segments.push(`${money(budget.userSpentUsd)} / ${budgetMoney(budget.userBudgetUsd)}`);
34
+ const share = percent(budget.userSpentUsd, budget.userBudgetUsd);
35
+ if (share)
36
+ segments.push(share);
37
+ if (poolBindsFirst(budget) && typeof budget.companyBudgetUsd === "number"
38
+ && budget.companyBudgetUsd > 0
39
+ && budget.companySpentUsd / budget.companyBudgetUsd >= 0.75) {
40
+ const share = percent(budget.companySpentUsd, budget.companyBudgetUsd);
41
+ if (share)
42
+ segments.push(`team pool ${share}`);
43
+ }
44
+ }
45
+ else {
46
+ segments.push(money(budget.userSpentUsd));
47
+ if (typeof budget.companyBudgetUsd === "number") {
48
+ const share = percent(budget.companySpentUsd, budget.companyBudgetUsd);
49
+ if (share)
50
+ segments.push(`team ${share}`);
51
+ }
52
+ }
53
+ if (typeof budget.companyBudgetUsd === "number" && budget.companyBudgetUsd > 0
54
+ && budget.companySpentUsd >= budget.companyBudgetUsd) {
55
+ segments.push("team pool used up");
56
+ }
57
+ if (inFlight.requests > 0)
58
+ segments.push(`${inFlight.requests} in flight`);
59
+ if (inFlight.staleHeldUsd > 0)
60
+ segments.push(`${money(inFlight.staleHeldUsd)} held stale`);
61
+ return segments.join(" · ");
62
+ }
63
+ function poolBindsFirst(budget) {
64
+ if (typeof budget.companyBudgetUsd !== "number" || typeof budget.userBudgetUsd !== "number")
65
+ return false;
66
+ const poolLeft = Math.max(0, budget.companyBudgetUsd - budget.companySpentUsd);
67
+ const userLeft = Math.max(0, budget.userBudgetUsd - budget.userSpentUsd);
68
+ return budget.userBudgetUsd > 0 && poolLeft < userLeft;
69
+ }
70
+ function usageTone(usage) {
71
+ if (!usage || !usage.enabled)
72
+ return "normal";
73
+ const { budget } = usage;
74
+ const ratios = [];
75
+ if (typeof budget.userBudgetUsd === "number" && budget.userBudgetUsd > 0) {
76
+ ratios.push(budget.userSpentUsd / budget.userBudgetUsd);
77
+ }
78
+ if (typeof budget.companyBudgetUsd === "number" && budget.companyBudgetUsd > 0
79
+ && (typeof budget.userBudgetUsd !== "number" || poolBindsFirst(budget))) {
80
+ ratios.push(budget.companySpentUsd / budget.companyBudgetUsd);
81
+ }
82
+ const worst = ratios.length ? Math.max(...ratios) : 0;
83
+ if (worst >= 0.9)
84
+ return "critical";
85
+ if (worst >= 0.75)
86
+ return "warn";
87
+ return "normal";
88
+ }
89
+ function tokens(total) {
90
+ if (total >= 1_000_000)
91
+ return `${Math.round(total / 100_000) / 10}M`;
92
+ if (total >= 1_000)
93
+ return `${Math.round(total / 1_000)}k`;
94
+ return String(total);
95
+ }
96
+ function formatSessionCostLine(usage) {
97
+ const since = readSince(usage?.since);
98
+ if (!since || since.requests <= 0)
99
+ return "";
100
+ const cost = since.costUsd > 0 && since.costUsd < 0.005 ? "<$0.01" : money(since.costUsd);
101
+ const requests = `${since.requests} ${since.requests === 1 ? "request" : "requests"}`;
102
+ return `This session: ${cost} · ${requests} · ${tokens(since.promptTokens + since.completionTokens)} tokens`;
103
+ }
104
+ function readSince(value) {
105
+ if (!value || typeof value !== "object" || Array.isArray(value))
106
+ return undefined;
107
+ const { costUsd, promptTokens, completionTokens, requests } = value;
108
+ const finite = (candidate) => typeof candidate === "number" && Number.isFinite(candidate);
109
+ if (!finite(costUsd) || !finite(promptTokens) || !finite(completionTokens) || !finite(requests)) {
110
+ return undefined;
111
+ }
112
+ return { costUsd, promptTokens, completionTokens, requests };
113
+ }
114
+ function readNumber(value, fallback = 0) {
115
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
116
+ }
117
+ function readBudgetCap(value) {
118
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
119
+ }
120
+ function readUser(value) {
121
+ if (!value || typeof value !== "object")
122
+ return null;
123
+ const { name, email, role } = value;
124
+ if (typeof name !== "string" || !name)
125
+ return null;
126
+ return {
127
+ name,
128
+ email: typeof email === "string" ? email : "",
129
+ role: typeof role === "string" ? role : "",
130
+ };
131
+ }
132
+ async function fetchCodieUsage({ baseUrl, key, sinceMs, fetchImpl = fetch, }) {
133
+ const controller = new AbortController();
134
+ const timeout = setTimeout(() => controller.abort(), USAGE_TIMEOUT_MS);
135
+ try {
136
+ const query = typeof sinceMs === "number" && sinceMs > 0 ? `?since=${sinceMs}` : "";
137
+ const response = await fetchImpl(`${cleanBaseUrl(baseUrl)}/me/usage${query}`, {
138
+ headers: { Authorization: `Bearer ${key}` },
139
+ signal: controller.signal,
140
+ });
141
+ if (!response.ok)
142
+ return null;
143
+ const payload = (await response.json());
144
+ const budget = payload?.budget;
145
+ if (!budget || typeof budget !== "object")
146
+ return null;
147
+ const raw = budget;
148
+ const flight = (payload?.inFlight ?? {});
149
+ return {
150
+ enabled: payload?.enabled === true,
151
+ user: readUser(payload?.user),
152
+ budget: {
153
+ companyBudgetUsd: readBudgetCap(raw.companyBudgetUsd),
154
+ companySpentUsd: readNumber(raw.companySpentUsd),
155
+ companyHeldUsd: readNumber(raw.companyHeldUsd),
156
+ userBudgetUsd: readBudgetCap(raw.userBudgetUsd),
157
+ userSpentUsd: readNumber(raw.userSpentUsd),
158
+ userHeldUsd: readNumber(raw.userHeldUsd),
159
+ keyBudgetUsd: readBudgetCap(raw.keyBudgetUsd),
160
+ keySpentUsd: readNumber(raw.keySpentUsd),
161
+ keyHeldUsd: typeof raw.keyHeldUsd === "number" ? raw.keyHeldUsd : null,
162
+ },
163
+ inFlight: {
164
+ requests: readNumber(flight.requests),
165
+ heldUsd: readNumber(flight.heldUsd),
166
+ staleRequests: readNumber(flight.staleRequests),
167
+ staleHeldUsd: readNumber(flight.staleHeldUsd),
168
+ },
169
+ ...(() => {
170
+ const since = readSince(payload?.since);
171
+ return since ? { since } : {};
172
+ })(),
173
+ };
174
+ }
175
+ catch {
176
+ return null;
177
+ }
178
+ finally {
179
+ clearTimeout(timeout);
180
+ }
181
+ }
@@ -3,9 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runAsk = runAsk;
4
4
  const shared_1 = require("./shared");
5
5
  function parseArgs(args) {
6
- // Everything after the command, joined back into a question string. Allows
7
- // both quoted and unquoted forms: `codiedev ask "why pg-bouncer?"` or
8
- // `codiedev ask why did we drop pg-bouncer`.
9
6
  const question = args
10
7
  .filter((a) => !a.startsWith("--"))
11
8
  .join(" ")
@@ -123,7 +123,6 @@ async function runCaptureCommand(args, depsOverride = {}) {
123
123
  return;
124
124
  }
125
125
  if (subcommand === "ignored") {
126
- // `ignored` lists everything; `ignored <path>` answers "is this ignored?".
127
126
  const query = args.slice(1).join(" ").trim();
128
127
  if (query) {
129
128
  reportCheck(query, current, deps);
@@ -139,7 +138,6 @@ async function runCaptureCommand(args, depsOverride = {}) {
139
138
  deps.log(` ${value}`);
140
139
  return;
141
140
  }
142
- // Back-compat: the earlier `codiedev capture check <path>` form.
143
141
  if (subcommand === "check") {
144
142
  reportCheck(requirePath(args), current, deps);
145
143
  return;
@@ -100,7 +100,6 @@ async function runCreateTicket(args) {
100
100
  await runBare(config, parsed.prompt);
101
101
  return;
102
102
  }
103
- // submit mode
104
103
  if (!parsed.submitSource) {
105
104
  console.error("--submit requires a path or '-' for stdin");
106
105
  process.exit(1);
@@ -114,7 +113,6 @@ async function runCreateTicket(args) {
114
113
  console.error(`Failed to read plan: ${err.message}`);
115
114
  process.exit(1);
116
115
  }
117
- // Translate fullNames → IDs before submitting
118
116
  let parsedPlan;
119
117
  try {
120
118
  parsedPlan = JSON.parse(raw);
@@ -141,14 +139,6 @@ async function runCreateTicket(args) {
141
139
  }
142
140
  await runSubmit(config, JSON.stringify(parsedPlan));
143
141
  }
144
- /**
145
- * Bare-mode entrypoint. Fetches the protocol body from the backend so we can
146
- * iterate on it without shipping a new CLI, substitutes the user's prompt,
147
- * and writes the result to stdout for the agent to follow. Hard-errors on
148
- * fetch failure rather than falling back to a stale local copy — the whole
149
- * point of moving the protocol server-side is that the freshest version is
150
- * the only version.
151
- */
152
142
  async function runBare(config, prompt) {
153
143
  let res;
154
144
  try {