codiedev 0.10.0-beta.1 → 0.10.0-beta.11

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 (155) hide show
  1. package/README.md +11 -5
  2. package/assets/opencode-plugin.mjs +6 -0
  3. package/assets/session-key.mjs +48 -0
  4. package/assets/tui-brand-plugin.tsx +73 -0
  5. package/dist/backup/cache.js +1 -5
  6. package/dist/backup/match.js +0 -6
  7. package/dist/backup/run.js +3 -35
  8. package/dist/captureExclusions.js +0 -12
  9. package/dist/captureLog.js +0 -28
  10. package/dist/captureReadiness.js +0 -58
  11. package/dist/captureSignals.js +184 -0
  12. package/dist/cli.js +135 -72
  13. package/dist/cliSession.js +456 -0
  14. package/dist/codexHookTrust.js +125 -6
  15. package/dist/codexLimitScan.js +359 -0
  16. package/dist/codie.js +77 -130
  17. package/dist/codieBrandInstall.js +80 -0
  18. package/dist/codieLauncher.js +76 -79
  19. package/dist/codieRuntime.js +19 -23
  20. package/dist/codieRuntimeInstall.js +108 -23
  21. package/dist/codieUsage.js +181 -0
  22. package/dist/commands/agentTemplates.generated.js +136 -0
  23. package/dist/commands/agents.js +1101 -0
  24. package/dist/commands/agentsSdk.generated.js +7 -0
  25. package/dist/commands/ask.js +0 -3
  26. package/dist/commands/capture.js +0 -2
  27. package/dist/commands/createTicket.js +0 -10
  28. package/dist/commands/doctor.js +53 -120
  29. package/dist/commands/evals.js +187 -0
  30. package/dist/commands/inbox.js +0 -2
  31. package/dist/commands/library.js +0 -3
  32. package/dist/commands/pack.js +573 -0
  33. package/dist/commands/ping.js +0 -2
  34. package/dist/commands/pull.js +6 -10
  35. package/dist/commands/push.js +2 -12
  36. package/dist/commands/search.js +0 -4
  37. package/dist/commands/send.js +0 -5
  38. package/dist/commands/share.js +0 -4
  39. package/dist/commands/shared.js +0 -35
  40. package/dist/commands/traces.js +344 -0
  41. package/dist/commands/tracesLabel.js +348 -0
  42. package/dist/configDir.js +18 -11
  43. package/dist/connect.js +55 -118
  44. package/dist/connectFlow.js +35 -9
  45. package/dist/copilotHooks.js +391 -0
  46. package/dist/copilotInstallation.js +64 -0
  47. package/dist/cursorHooks.js +38 -30
  48. package/dist/cursorLifecycle.js +109 -0
  49. package/dist/detection.js +0 -30
  50. package/dist/harnessDirs.js +77 -0
  51. package/dist/hook.js +256 -445
  52. package/dist/inboxFormat.js +0 -24
  53. package/dist/launchContext.js +150 -0
  54. package/dist/launcher.js +168 -0
  55. package/dist/limitEvent.js +44 -13
  56. package/dist/limitEventReporter.js +1 -1
  57. package/dist/managedPi.js +256 -0
  58. package/dist/mcp.js +5 -384
  59. package/dist/nativeSteering.js +24 -0
  60. package/dist/packs/codie.js +182 -0
  61. package/dist/packs/harnesses.js +411 -0
  62. package/dist/packs/hookGate.js +256 -0
  63. package/dist/packs/install.js +286 -0
  64. package/dist/packs/manifest.js +126 -0
  65. package/dist/packs/paths.js +81 -0
  66. package/dist/packs/shim.js +214 -0
  67. package/dist/packs/store.js +155 -0
  68. package/dist/packs/sync.js +170 -0
  69. package/dist/piHooks.js +216 -0
  70. package/dist/piRuntime.js +120 -0
  71. package/dist/repoResolver.js +0 -19
  72. package/dist/secret.js +0 -3
  73. package/dist/selfUpdate.js +376 -0
  74. package/dist/skillMarker.js +23 -0
  75. package/dist/subagents.js +135 -0
  76. package/dist/teamSkillContext.js +10 -0
  77. package/dist/transcriptModels.js +205 -0
  78. package/dist/upgradeNudge.js +0 -30
  79. package/dist/utils.js +502 -828
  80. package/dist/version.js +0 -10
  81. package/dist/versionCheck.js +0 -33
  82. package/package.json +8 -9
  83. package/dist/backup/cache.d.ts +0 -14
  84. package/dist/backup/match.d.ts +0 -2
  85. package/dist/backup/run.d.ts +0 -25
  86. package/dist/captureExclusions.d.ts +0 -30
  87. package/dist/captureLog.d.ts +0 -20
  88. package/dist/captureReadiness.d.ts +0 -65
  89. package/dist/cli.d.ts +0 -16
  90. package/dist/codexHookTrust.d.ts +0 -34
  91. package/dist/codie.d.ts +0 -2
  92. package/dist/codieLauncher.d.ts +0 -53
  93. package/dist/codieRuntime.d.ts +0 -29
  94. package/dist/codieRuntimeInstall.d.ts +0 -28
  95. package/dist/codieStartup.d.ts +0 -26
  96. package/dist/codieStartup.js +0 -30
  97. package/dist/commands/ask.d.ts +0 -1
  98. package/dist/commands/capture.d.ts +0 -14
  99. package/dist/commands/createTicket.d.ts +0 -27
  100. package/dist/commands/delete.d.ts +0 -1
  101. package/dist/commands/doctor.d.ts +0 -1
  102. package/dist/commands/inbox.d.ts +0 -3
  103. package/dist/commands/library.d.ts +0 -2
  104. package/dist/commands/note.d.ts +0 -1
  105. package/dist/commands/ping.d.ts +0 -1
  106. package/dist/commands/promote.d.ts +0 -1
  107. package/dist/commands/promote.js +0 -53
  108. package/dist/commands/pull.d.ts +0 -2
  109. package/dist/commands/push.d.ts +0 -33
  110. package/dist/commands/react.d.ts +0 -2
  111. package/dist/commands/react.js +0 -40
  112. package/dist/commands/reverseTicket.d.ts +0 -2
  113. package/dist/commands/reverseTicket.js +0 -477
  114. package/dist/commands/search.d.ts +0 -2
  115. package/dist/commands/security.d.ts +0 -1
  116. package/dist/commands/security.js +0 -156
  117. package/dist/commands/send.d.ts +0 -2
  118. package/dist/commands/share.d.ts +0 -2
  119. package/dist/commands/shared.d.ts +0 -64
  120. package/dist/configDir.d.ts +0 -1
  121. package/dist/connect.d.ts +0 -1
  122. package/dist/connectFlow.d.ts +0 -13
  123. package/dist/cursorHooks.d.ts +0 -23
  124. package/dist/detection.d.ts +0 -30
  125. package/dist/hook.d.ts +0 -6
  126. package/dist/inboxFormat.d.ts +0 -38
  127. package/dist/limitEvent.d.ts +0 -16
  128. package/dist/limitEventReporter.d.ts +0 -21
  129. package/dist/mcp.d.ts +0 -12
  130. package/dist/prUrl.d.ts +0 -9
  131. package/dist/prUrl.js +0 -33
  132. package/dist/repoResolver.d.ts +0 -17
  133. package/dist/secret.d.ts +0 -1
  134. package/dist/security.d.ts +0 -102
  135. package/dist/security.js +0 -437
  136. package/dist/securityDeepScan.d.ts +0 -41
  137. package/dist/securityDeepScan.js +0 -248
  138. package/dist/securityDoctor.d.ts +0 -32
  139. package/dist/securityDoctor.js +0 -63
  140. package/dist/securityFeature.d.ts +0 -5
  141. package/dist/securityFeature.js +0 -7
  142. package/dist/securityInstall.d.ts +0 -63
  143. package/dist/securityInstall.js +0 -239
  144. package/dist/securityInventory.d.ts +0 -54
  145. package/dist/securityInventory.js +0 -556
  146. package/dist/securityPilot.d.ts +0 -19
  147. package/dist/securityPilot.js +0 -61
  148. package/dist/securityPolicy.d.ts +0 -33
  149. package/dist/securityPolicy.js +0 -188
  150. package/dist/securitySession.d.ts +0 -6
  151. package/dist/securitySession.js +0 -72
  152. package/dist/upgradeNudge.d.ts +0 -25
  153. package/dist/utils.d.ts +0 -342
  154. package/dist/version.d.ts +0 -1
  155. package/dist/versionCheck.d.ts +0 -68
@@ -0,0 +1,80 @@
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.usageModulePath = usageModulePath;
37
+ exports.openCodeConfigDir = openCodeConfigDir;
38
+ exports.installTuiPlugin = installTuiPlugin;
39
+ const fs = __importStar(require("fs"));
40
+ const os = __importStar(require("os"));
41
+ const path = __importStar(require("path"));
42
+ const codieLauncher_1 = require("./codieLauncher");
43
+ function assetPath(name) {
44
+ return path.join(__dirname, "..", "assets", name);
45
+ }
46
+ function usageModulePath() {
47
+ const candidates = [
48
+ path.join(__dirname, "codieUsage.js"),
49
+ path.join(__dirname, "codieUsage.ts"),
50
+ ];
51
+ const found = candidates.find((candidate) => fs.existsSync(candidate));
52
+ if (!found)
53
+ throw new Error("this install has no Codiedev usage module");
54
+ return found;
55
+ }
56
+ function openCodeConfigDir() {
57
+ return path.join(process.env.XDG_CONFIG_HOME?.trim() || path.join(os.homedir(), ".config"), "opencode");
58
+ }
59
+ function installTuiPlugin() {
60
+ try {
61
+ const configDir = openCodeConfigDir();
62
+ const pluginDir = path.join(configDir, "plugins");
63
+ fs.mkdirSync(pluginDir, { recursive: true });
64
+ const source = (0, codieLauncher_1.stageUsageModule)(fs.readFileSync(assetPath("tui-brand-plugin.tsx"), "utf8"), usageModulePath());
65
+ const destination = path.join(pluginDir, path.basename(codieLauncher_1.BRAND_PLUGIN_SPEC));
66
+ if (!fs.existsSync(destination) || fs.readFileSync(destination, "utf8") !== source) {
67
+ fs.writeFileSync(destination, source);
68
+ }
69
+ const tuiConfigPath = path.join(configDir, "tui.json");
70
+ const existing = fs.existsSync(tuiConfigPath)
71
+ ? JSON.parse(fs.readFileSync(tuiConfigPath, "utf8"))
72
+ : {};
73
+ const { config, changed } = (0, codieLauncher_1.withBrandPlugin)(existing);
74
+ if (changed)
75
+ fs.writeFileSync(tuiConfigPath, `${JSON.stringify(config, null, 2)}\n`);
76
+ }
77
+ catch (error) {
78
+ console.error(`codie: continuing without Codiedev branding (${error instanceof Error ? error.message : error})`);
79
+ }
80
+ }
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- // Pure launcher logic for the Codie harness.
3
- //
4
- // Ported from codiedev-gateway-cli/npm/lib/launcher.js when the harness folded
5
- // into this package so `npm i -g codiedev` is the only install a developer
6
- // needs — Claude Code, Codex, Cursor, and Codie all come from one package and
7
- // share the one token `codiedev connect` stored.
8
- //
9
- // Everything here is pure (no fs/network side effects beyond an injected
10
- // fetch) so the governance rules can be tested without a live gateway.
11
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
3
  if (k2 === undefined) k2 = k;
13
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -42,11 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
42
33
  };
43
34
  })();
44
35
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.HIDDEN_WORDMARK = exports.CODIEDEV_WORDMARK = exports.LEGACY_CODIEDEV_WORDMARK = exports.STOCK_WORDMARK = exports.BRAND_PLUGIN_SPEC = exports.CODIE_SECURITY_BLOCK_MARKER = void 0;
46
- exports.extractCodieSecurityBlock = extractCodieSecurityBlock;
36
+ exports.HIDDEN_WORDMARK = exports.CODIEDEV_WORDMARK = exports.LEGACY_CODIEDEV_WORDMARK = exports.STOCK_WORDMARK = exports.BRAND_PLUGIN_SPEC = exports.USAGE_MODULE_PLACEHOLDER = void 0;
37
+ exports.usableModelCost = usableModelCost;
38
+ exports.usableModelLimit = usableModelLimit;
47
39
  exports.withCodieWorkingDirectory = withCodieWorkingDirectory;
48
40
  exports.bareModel = bareModel;
49
41
  exports.buildCodieConfig = buildCodieConfig;
42
+ exports.stageUsageModule = stageUsageModule;
50
43
  exports.withBrandPlugin = withBrandPlugin;
51
44
  exports.patchBrandWordmark = patchBrandWordmark;
52
45
  exports.resolveCapturePlugin = resolveCapturePlugin;
@@ -55,7 +48,9 @@ exports.parseCodieArgs = parseCodieArgs;
55
48
  exports.validateCodieArgs = validateCodieArgs;
56
49
  const path = __importStar(require("path"));
57
50
  const url_1 = require("url");
51
+ const harnessDirs_1 = require("./harnessDirs");
58
52
  const DEFAULT_NAMES = {
53
+ "openai/gpt-5.6-luna": "GPT-5.6 Luna",
59
54
  "openai/gpt-5.3-codex": "GPT-5.3 Codex",
60
55
  "openai/gpt-5.2-codex": "GPT-5.2 Codex",
61
56
  "openai/gpt-5.1-codex-mini": "GPT-5.1 Codex Mini",
@@ -63,32 +58,30 @@ const DEFAULT_NAMES = {
63
58
  "z-ai/glm-5.2": "GLM 5.2 · Fireworks ZDR",
64
59
  "moonshotai/kimi-k2.7-code": "Kimi K2.7 Code · Fireworks ZDR",
65
60
  };
66
- exports.CODIE_SECURITY_BLOCK_MARKER = "__CODIEDEV_SECURITY_BLOCK__";
67
- /** Extract the private adapter marker used to carry a local block reason
68
- * across OpenCode's generic 500 boundary. The marker never contains prompt or
69
- * tool input — only the deterministic policy reason. */
70
- function extractCodieSecurityBlock(stderr) {
71
- for (const line of stderr.split(/\r?\n/)) {
72
- const markerIndex = line.indexOf(exports.CODIE_SECURITY_BLOCK_MARKER);
73
- if (markerIndex < 0)
74
- continue;
75
- try {
76
- const parsed = JSON.parse(line.slice(markerIndex + exports.CODIE_SECURITY_BLOCK_MARKER.length));
77
- if (typeof parsed?.reason === "string" && parsed.reason.trim())
78
- return parsed.reason.trim();
79
- }
80
- catch {
81
- // Ignore malformed or foreign stderr and preserve the original runtime error.
82
- }
83
- }
84
- return undefined;
61
+ function usableModelCost(value) {
62
+ if (!value || typeof value !== "object")
63
+ return undefined;
64
+ const { input, output } = value;
65
+ if (typeof input !== "number" || typeof output !== "number")
66
+ return undefined;
67
+ if (!Number.isFinite(input) || !Number.isFinite(output))
68
+ return undefined;
69
+ if (input < 0 || output < 0)
70
+ return undefined;
71
+ return { input, output };
72
+ }
73
+ function usableModelLimit(value) {
74
+ if (!value || typeof value !== "object")
75
+ return undefined;
76
+ const { context, output } = value;
77
+ if (typeof context !== "number" || typeof output !== "number")
78
+ return undefined;
79
+ if (!Number.isFinite(context) || !Number.isFinite(output))
80
+ return undefined;
81
+ if (context <= 0 || output <= 0)
82
+ return undefined;
83
+ return { context, output };
85
84
  }
86
- /**
87
- * Pin non-interactive runs to the directory where `codie` was launched.
88
- * OpenCode can reuse a previously-known project instance; without an explicit
89
- * --dir, a real smoke run launched in a throwaway repo resumed the caller's
90
- * main checkout and executed there. TUI mode already owns its cwd directly.
91
- */
92
85
  function withCodieWorkingDirectory(args, cwd) {
93
86
  if (args[0] !== "run")
94
87
  return args;
@@ -110,8 +103,6 @@ function governedModel(value, approved, fallback, label) {
110
103
  return `codiedev/${model}`;
111
104
  }
112
105
  function economicalModel(catalog, primary) {
113
- // Catalog ids arrive already normalized through bareModel(), so the
114
- // gateway's `codiedev/fast` alias appears here as plain "fast".
115
106
  const preferred = ["openai/gpt-5.1-codex-mini", "fast", "z-ai/glm-5.2"];
116
107
  return preferred.find((id) => catalog.some((model) => model.id === id)) || primary;
117
108
  }
@@ -142,10 +133,18 @@ function buildCodieConfig({ existing = {}, catalog, env = {}, plugin, plugins =
142
133
  const planModel = governedModel(requestedPlan, approved, fast, "Plan agent");
143
134
  const existingPlugins = Array.isArray(existing.plugin) ? existing.plugin : [];
144
135
  const requiredPlugins = [...plugins, plugin].filter(Boolean);
145
- const models = Object.fromEntries(catalog.map((model) => [
146
- bareModel(model.id),
147
- { name: model.name || DEFAULT_NAMES[bareModel(model.id)] || bareModel(model.id) },
148
- ]));
136
+ const models = Object.fromEntries(catalog.map((model) => {
137
+ const cost = usableModelCost(model.cost);
138
+ const limit = usableModelLimit(model.limit);
139
+ return [
140
+ bareModel(model.id),
141
+ {
142
+ name: model.name || DEFAULT_NAMES[bareModel(model.id)] || bareModel(model.id),
143
+ ...(cost ? { cost: { ...cost, cache: { read: 0, write: 0 } } } : {}),
144
+ ...(limit ? { limit } : {}),
145
+ },
146
+ ];
147
+ }));
149
148
  return {
150
149
  ...existing,
151
150
  model: governedModel(primary, approved, first, "Primary agent"),
@@ -173,9 +172,14 @@ function buildCodieConfig({ existing = {}, catalog, env = {}, plugin, plugins =
173
172
  plugin: [...existingPlugins.filter((item) => !requiredPlugins.includes(item)), ...requiredPlugins],
174
173
  };
175
174
  }
175
+ exports.USAGE_MODULE_PLACEHOLDER = "__CODIEDEV_USAGE_MODULE__";
176
+ function stageUsageModule(source, modulePath) {
177
+ if (!source.includes(exports.USAGE_MODULE_PLACEHOLDER)) {
178
+ throw new Error(`plugin template is missing the ${exports.USAGE_MODULE_PLACEHOLDER} placeholder`);
179
+ }
180
+ return source.split(exports.USAGE_MODULE_PLACEHOLDER).join(JSON.stringify(modulePath));
181
+ }
176
182
  exports.BRAND_PLUGIN_SPEC = "./plugins/codiedev-brand.tsx";
177
- // Pure merge for ~/.config/opencode/tui.json: TUI plugins load from tui.json,
178
- // not the opencode.json we inject via OPENCODE_CONFIG_CONTENT.
179
183
  function withBrandPlugin(existing) {
180
184
  const config = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
181
185
  const plugins = Array.isArray(config.plugin) ? config.plugin : [];
@@ -184,13 +188,6 @@ function withBrandPlugin(existing) {
184
188
  return { config, changed: false };
185
189
  return { config: { ...config, plugin: [...plugins, exports.BRAND_PLUGIN_SPEC] }, changed: true };
186
190
  }
187
- // ── Engine wordmark suppression ─────────────────────────────────────────
188
- // The OpenCode engine prints a block-letter "opencode" wordmark to terminal
189
- // scrollback around each session (the boot/exit banner). A four-row terminal
190
- // raster cannot reproduce the real Codiedev wordmark cleanly, so leave that
191
- // surface blank and keep the proper brand mark on the plugin-driven home
192
- // screen. The bundled JS table is replaced byte-for-byte so binary offsets do
193
- // not move.
194
191
  function escapeJsSource(row) {
195
192
  let out = "";
196
193
  for (const ch of row) {
@@ -203,7 +200,6 @@ function wordmarkRegion(left, right, { escapeUnicode = true } = {}) {
203
200
  const rows = (list) => list.map((row) => `"${escapeUnicode ? escapeJsSource(row) : row}"`).join(",");
204
201
  return `var vn={left:[${rows(left)}],right:[${rows(right)}]};`;
205
202
  }
206
- // o p e n | c o d e — must match the engine's bundle byte-for-byte.
207
203
  exports.STOCK_WORDMARK = wordmarkRegion([
208
204
  " ",
209
205
  "█▀▀█ █▀▀█ █▀▀█ █▀▀▄",
@@ -215,12 +211,6 @@ exports.STOCK_WORDMARK = wordmarkRegion([
215
211
  "█___ █__█ █__█ █^^^",
216
212
  "▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀",
217
213
  ]);
218
- // c o d i | e d e v, rasterized from public/codiedevlogo.svg at eight vertical
219
- // pixels and packed into four terminal rows with half-block characters. This
220
- // preserves the real mark's round lowercase bowls, linked c/o, ascenders, and
221
- // compact v instead of approximating the letters with OpenCode's square font.
222
- // The engine inserts one column between left and right, so the blank source
223
- // column at the codi/edev boundary is intentionally omitted.
224
214
  const CODIE_LEFT = [
225
215
  " ▄ ▄",
226
216
  " ▄▄█▄▄ ▄▄█▄▄ ▄████ █",
@@ -233,9 +223,6 @@ const CODIE_RIGHT = [
233
223
  "██▀▀█ █ █ ██▄▀█ ▀█▄█ ",
234
224
  "▀▀█▀▀ ▀▀▀▀▀ ▀▀▀▀ ▀▀ ",
235
225
  ];
236
- // Migration source for the first hand-drawn banner shipped during development.
237
- // Recognizing it lets an existing install upgrade in place instead of keeping
238
- // the old square letterforms until OpenCode happens to reinstall its binary.
239
226
  const LEGACY_CODIE_LEFT = [
240
227
  " ▄ ▄ ",
241
228
  "█▀▀▀ █▀▀█ █▀▀█ █ ",
@@ -257,9 +244,6 @@ exports.LEGACY_CODIEDEV_WORDMARK = (() => {
257
244
  return wordmarkRegion(LEGACY_CODIE_LEFT, padded);
258
245
  })();
259
246
  exports.CODIEDEV_WORDMARK = (() => {
260
- // Raw Unicode is valid JavaScript and costs fewer source bytes than \uXXXX.
261
- // Put the remaining byte padding outside the strings so it cannot widen the
262
- // rendered banner or shift its centering.
263
247
  const unpadded = wordmarkRegion(CODIE_LEFT, CODIE_RIGHT, { escapeUnicode: false });
264
248
  const deficit = Buffer.byteLength(exports.STOCK_WORDMARK) - Buffer.byteLength(unpadded);
265
249
  if (deficit < 0)
@@ -281,9 +265,6 @@ exports.HIDDEN_WORDMARK = (() => {
281
265
  }
282
266
  return region;
283
267
  })();
284
- // Pure: locate the stock or an earlier Codiedev wordmark in an engine binary.
285
- // Refuses to touch the file unless exactly one known region appears; an
286
- // already-hidden or reshaped upstream bundle is left alone.
287
268
  function patchBrandWordmark(binary) {
288
269
  const stock = Buffer.from(exports.STOCK_WORDMARK);
289
270
  const candidates = [stock, Buffer.from(exports.LEGACY_CODIEDEV_WORDMARK), Buffer.from(exports.CODIEDEV_WORDMARK)];
@@ -301,29 +282,39 @@ function patchBrandWordmark(binary) {
301
282
  return { changed: true, offset: matches[0], stock, replacement: Buffer.from(exports.HIDDEN_WORDMARK) };
302
283
  }
303
284
  function resolveCapturePlugin({ homeDir, existsSync, }) {
304
- const capturePath = path.join(homeDir, ".config", "opencode", "plugins", "codiedev.js");
285
+ const capturePath = path.join((0, harnessDirs_1.openCodeConfigDir)(process.env, homeDir), "plugins", "codiedev.js");
305
286
  if (!existsSync(capturePath)) {
306
287
  throw new Error("Codiedev session capture is not installed. Run `codiedev connect` first");
307
288
  }
308
289
  return (0, url_1.pathToFileURL)(capturePath).href;
309
290
  }
310
- async function fetchApprovedCatalog({ baseUrl, key, fetchImpl = fetch, }) {
291
+ class CatalogRejected extends Error {
292
+ }
293
+ async function fetchApprovedCatalog({ baseUrl, key, fetchImpl = fetch, retryDelayMs = 1_000, }) {
294
+ try {
295
+ return await fetchCatalogOnce(baseUrl, key, fetchImpl);
296
+ }
297
+ catch (error) {
298
+ if (error instanceof CatalogRejected)
299
+ throw error;
300
+ await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
301
+ return fetchCatalogOnce(baseUrl, key, fetchImpl);
302
+ }
303
+ }
304
+ async function fetchCatalogOnce(baseUrl, key, fetchImpl) {
311
305
  const controller = new AbortController();
312
- const timeout = setTimeout(() => controller.abort(), 5000);
306
+ const timeout = setTimeout(() => controller.abort(), 10_000);
313
307
  try {
314
308
  const response = await fetchImpl(`${cleanBaseUrl(baseUrl)}/models`, {
315
309
  headers: { Authorization: `Bearer ${key}` },
316
310
  signal: controller.signal,
317
311
  });
318
- // The `codie` bin ships to every connected machine, including companies
319
- // whose gateway is still switched off. Those developers must get an
320
- // answer, not an HTTP status.
321
312
  if (response.status === 403) {
322
- throw new Error("The Codie harness is not enabled for your company yet. Ask your Codiedev admin to turn it on — " +
313
+ throw new CatalogRejected("The Codie harness is not enabled for your company yet. Ask your Codiedev admin to turn it on — " +
323
314
  "your session capture keeps working in the meantime.");
324
315
  }
325
316
  if (response.status === 401) {
326
- throw new Error("Your Codiedev token was rejected. Run `codiedev connect` to relink this machine.");
317
+ throw new CatalogRejected("Your Codiedev token was rejected. Run `codiedev connect` to relink this machine.");
327
318
  }
328
319
  if (!response.ok)
329
320
  throw new Error(`model catalog returned ${response.status}`);
@@ -331,10 +322,16 @@ async function fetchApprovedCatalog({ baseUrl, key, fetchImpl = fetch, }) {
331
322
  if (!Array.isArray(payload?.data) || payload.data.length === 0) {
332
323
  throw new Error("model catalog is empty");
333
324
  }
334
- return payload.data.map((model) => ({
335
- id: bareModel(model.id),
336
- name: typeof model.name === "string" && model.name ? model.name : DEFAULT_NAMES[bareModel(model.id)],
337
- }));
325
+ return payload.data.map((model) => {
326
+ const cost = usableModelCost(model.cost);
327
+ const limit = usableModelLimit(model.limit);
328
+ return {
329
+ id: bareModel(model.id),
330
+ name: typeof model.name === "string" && model.name ? model.name : DEFAULT_NAMES[bareModel(model.id)],
331
+ ...(cost ? { cost } : {}),
332
+ ...(limit ? { limit } : {}),
333
+ };
334
+ });
338
335
  }
339
336
  finally {
340
337
  clearTimeout(timeout);
@@ -1,14 +1,4 @@
1
1
  "use strict";
2
- // Where the Codie harness gets its engine.
3
- //
4
- // The engine (`opencode-ai`) is a ~132MB platform binary. It is deliberately
5
- // NOT a dependency of this package: the overwhelming majority of installs are
6
- // capture-only (Claude Code, Codex, Cursor) and must not pay for a harness
7
- // they never launch. So `npm i -g codiedev` stays small, and the first `codie`
8
- // run downloads the engine into the Codiedev state dir.
9
- //
10
- // Resolution is pure and injectable so the precedence can be tested without
11
- // touching the network or a real 132MB install.
12
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
3
  if (k2 === undefined) k2 = k;
14
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -43,20 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
43
33
  };
44
34
  })();
45
35
  Object.defineProperty(exports, "__esModule", { value: true });
46
- exports.CODIE_MIN_NODE_MAJOR = exports.OPENCODE_RUNTIME_VERSION = exports.OPENCODE_RUNTIME_SPEC = void 0;
36
+ exports.ENGINE_MIN_BYTES = exports.CODIE_MIN_NODE_MAJOR = exports.OPENCODE_RUNTIME_VERSION = exports.OPENCODE_RUNTIME_SPEC = void 0;
47
37
  exports.codieRuntimeDir = codieRuntimeDir;
38
+ exports.isEngineSize = isEngineSize;
48
39
  exports.installedRuntimePackageJson = installedRuntimePackageJson;
49
40
  exports.resolveCodieRuntime = resolveCodieRuntime;
50
41
  exports.assertCodieNodeVersion = assertCodieNodeVersion;
51
42
  const path = __importStar(require("path"));
52
- // Pinned exactly, not a range. patchBrandWordmark() only rewrites bundles it
53
- // recognizes byte-for-byte, so a floating version would silently regress the
54
- // terminal branding whenever OpenCode ships a new build.
55
43
  exports.OPENCODE_RUNTIME_SPEC = "opencode-ai@1.18.4";
56
44
  exports.OPENCODE_RUNTIME_VERSION = exports.OPENCODE_RUNTIME_SPEC.slice(exports.OPENCODE_RUNTIME_SPEC.lastIndexOf("@") + 1);
57
- // The harness needs 20; capture does not. package.json `engines` stays >=18 so
58
- // `codiedev connect` still installs for Node 18 users — this floor is enforced
59
- // at `codie` launch instead.
60
45
  exports.CODIE_MIN_NODE_MAJOR = 20;
61
46
  function codieRuntimeDir(stateDir) {
62
47
  return path.join(stateDir, "runtime");
@@ -64,11 +49,14 @@ function codieRuntimeDir(stateDir) {
64
49
  function installedBinary(stateDir) {
65
50
  return path.join(codieRuntimeDir(stateDir), "node_modules", "opencode-ai", "bin", "opencode.exe");
66
51
  }
52
+ exports.ENGINE_MIN_BYTES = 1_000_000;
53
+ function isEngineSize(bytes) {
54
+ return bytes >= exports.ENGINE_MIN_BYTES;
55
+ }
67
56
  function installedRuntimePackageJson(stateDir) {
68
57
  return path.join(codieRuntimeDir(stateDir), "node_modules", "opencode-ai", "package.json");
69
58
  }
70
- function resolveCodieRuntime({ env, stateDir, existsSync, readInstalledVersion, resolveBundled, }) {
71
- // 1. Explicit override — the air-gapped / pre-provisioned escape hatch.
59
+ function resolveCodieRuntime({ env, stateDir, existsSync, readInstalledVersion, resolveBundled, isEngine = () => true, }) {
72
60
  const override = env.CODIEDEV_OPENCODE_BINARY?.trim();
73
61
  if (override) {
74
62
  if (!existsSync(override)) {
@@ -76,15 +64,21 @@ function resolveCodieRuntime({ env, stateDir, existsSync, readInstalledVersion,
76
64
  }
77
65
  return { kind: "ready", binary: override };
78
66
  }
79
- // 2. A bundled engine, if this package was installed with one (dev checkout,
80
- // or a future vendored distribution).
81
67
  const bundled = resolveBundled();
82
- if (bundled)
68
+ if (bundled && isEngine(bundled))
83
69
  return { kind: "ready", binary: bundled };
84
- // 3. The lazily installed engine.
85
70
  const binary = installedBinary(stateDir);
86
71
  if (existsSync(binary)) {
87
72
  const currentVersion = readInstalledVersion?.(installedRuntimePackageJson(stateDir)) ?? null;
73
+ if (!isEngine(binary) && (!currentVersion || currentVersion === exports.OPENCODE_RUNTIME_VERSION)) {
74
+ return {
75
+ kind: "unlinked",
76
+ installDir: codieRuntimeDir(stateDir),
77
+ binary,
78
+ spec: exports.OPENCODE_RUNTIME_SPEC,
79
+ packageDir: path.dirname(installedRuntimePackageJson(stateDir)),
80
+ };
81
+ }
88
82
  if (currentVersion && currentVersion !== exports.OPENCODE_RUNTIME_VERSION) {
89
83
  return {
90
84
  kind: "outdated",
@@ -93,6 +87,8 @@ function resolveCodieRuntime({ env, stateDir, existsSync, readInstalledVersion,
93
87
  spec: exports.OPENCODE_RUNTIME_SPEC,
94
88
  currentVersion,
95
89
  expectedVersion: exports.OPENCODE_RUNTIME_VERSION,
90
+ linked: isEngine(binary),
91
+ packageDir: path.dirname(installedRuntimePackageJson(stateDir)),
96
92
  };
97
93
  }
98
94
  return { kind: "ready", binary };
@@ -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() {