codiedev 0.10.0-beta.1 → 0.10.0-beta.10

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 +1088 -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 +9 -10
  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,359 @@
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.CODEX_LIMIT_STATE_PATH = void 0;
37
+ exports.scanCodexRolloutForLimit = scanCodexRolloutForLimit;
38
+ exports.readTailEndingAt = readTailEndingAt;
39
+ exports.findRecentCodexRollouts = findRecentCodexRollouts;
40
+ exports.sessionIdFromRolloutPath = sessionIdFromRolloutPath;
41
+ exports.codexSessionsDir = codexSessionsDir;
42
+ exports.recordCodexLimitReported = recordCodexLimitReported;
43
+ exports.sweepCodexLimits = sweepCodexLimits;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const limitEvent_1 = require("./limitEvent");
47
+ const configDir_1 = require("./configDir");
48
+ const harnessDirs_1 = require("./harnessDirs");
49
+ const MARKER = Buffer.from("rate_limit_reached_type");
50
+ const SPEND_MARKER = Buffer.from("spend_control_reached");
51
+ const NEWLINE = 0x0a;
52
+ const DEFAULT_CHUNK_SIZE = 256 * 1024;
53
+ const DEFAULT_MAX_SCAN_BYTES = 16 * 1024 * 1024;
54
+ const DEFAULT_TAIL_BYTES = 64 * 1024;
55
+ function scanCodexRolloutForLimit(filePath, options = {}) {
56
+ const chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE;
57
+ const maxScanBytes = options.maxScanBytes ?? DEFAULT_MAX_SCAN_BYTES;
58
+ let size = 0;
59
+ try {
60
+ size = fs.statSync(filePath).size;
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ if (size === 0)
66
+ return null;
67
+ let fd;
68
+ try {
69
+ fd = fs.openSync(filePath, "r");
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ try {
75
+ let pos = size;
76
+ let carry = Buffer.alloc(0);
77
+ let scanned = 0;
78
+ while (pos > 0 && scanned < maxScanBytes) {
79
+ const length = Math.min(chunkSize, pos, maxScanBytes - scanned);
80
+ if (length <= 0)
81
+ break;
82
+ pos -= length;
83
+ const chunk = Buffer.allocUnsafe(length);
84
+ const read = fs.readSync(fd, chunk, 0, length, pos);
85
+ scanned += read;
86
+ const window = Buffer.concat([chunk.subarray(0, read), carry]);
87
+ let completeStart = 0;
88
+ if (pos > 0) {
89
+ const firstNewline = window.indexOf(NEWLINE);
90
+ if (firstNewline < 0) {
91
+ carry = window.length > maxScanBytes ? Buffer.alloc(0) : window;
92
+ continue;
93
+ }
94
+ completeStart = firstNewline + 1;
95
+ carry = window.subarray(0, completeStart);
96
+ }
97
+ else {
98
+ carry = Buffer.alloc(0);
99
+ }
100
+ const region = window.subarray(completeStart);
101
+ if (region.indexOf(MARKER) < 0 && region.indexOf(SPEND_MARKER) < 0)
102
+ continue;
103
+ const bounds = [];
104
+ let lineStart = 0;
105
+ for (let index = 0; index < region.length; index += 1) {
106
+ if (region[index] === NEWLINE) {
107
+ bounds.push([lineStart, index + 1]);
108
+ lineStart = index + 1;
109
+ }
110
+ }
111
+ if (lineStart < region.length)
112
+ bounds.push([lineStart, region.length]);
113
+ for (let index = bounds.length - 1; index >= 0; index -= 1) {
114
+ const [start, end] = bounds[index];
115
+ const event = (0, limitEvent_1.parseCodexLimitEventFromTranscript)(region.subarray(start, end).toString("utf8"));
116
+ if (event)
117
+ return { event, lineEnd: pos + completeStart + end };
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+ catch {
123
+ return null;
124
+ }
125
+ finally {
126
+ try {
127
+ fs.closeSync(fd);
128
+ }
129
+ catch {
130
+ }
131
+ }
132
+ }
133
+ function readTailEndingAt(filePath, lineEnd, maxBytes = DEFAULT_TAIL_BYTES) {
134
+ let end = lineEnd;
135
+ try {
136
+ end = Math.min(lineEnd, fs.statSync(filePath).size);
137
+ }
138
+ catch {
139
+ return "";
140
+ }
141
+ const start = Math.max(0, end - maxBytes);
142
+ const length = end - start;
143
+ if (length <= 0)
144
+ return "";
145
+ let fd;
146
+ try {
147
+ fd = fs.openSync(filePath, "r");
148
+ }
149
+ catch {
150
+ return "";
151
+ }
152
+ try {
153
+ const buffer = Buffer.allocUnsafe(length);
154
+ const read = fs.readSync(fd, buffer, 0, length, start);
155
+ const text = buffer.subarray(0, read).toString("utf8");
156
+ if (start === 0)
157
+ return text;
158
+ const newline = text.indexOf("\n");
159
+ return newline === -1 ? "" : text.slice(newline + 1);
160
+ }
161
+ catch {
162
+ return "";
163
+ }
164
+ finally {
165
+ try {
166
+ fs.closeSync(fd);
167
+ }
168
+ catch {
169
+ }
170
+ }
171
+ }
172
+ const DEFAULT_WITHIN_MS = 24 * 3600_000;
173
+ const DEFAULT_MAX_FILES = 12;
174
+ function findRecentCodexRollouts(sessionsDir, options = {}) {
175
+ const withinMs = options.withinMs ?? DEFAULT_WITHIN_MS;
176
+ const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
177
+ const now = options.now ?? Date.now();
178
+ const found = [];
179
+ const walk = (directory, depth) => {
180
+ if (depth > 5)
181
+ return;
182
+ let entries;
183
+ try {
184
+ entries = fs.readdirSync(directory, { withFileTypes: true });
185
+ }
186
+ catch {
187
+ return;
188
+ }
189
+ for (const entry of entries) {
190
+ const full = path.join(directory, entry.name);
191
+ if (entry.isDirectory()) {
192
+ walk(full, depth + 1);
193
+ continue;
194
+ }
195
+ if (!entry.isFile() || !entry.name.endsWith(".jsonl"))
196
+ continue;
197
+ try {
198
+ const mtime = fs.statSync(full).mtimeMs;
199
+ if (now - mtime > withinMs)
200
+ continue;
201
+ found.push({ file: full, mtime });
202
+ }
203
+ catch {
204
+ }
205
+ }
206
+ };
207
+ walk(sessionsDir, 0);
208
+ return found
209
+ .sort((left, right) => right.mtime - left.mtime)
210
+ .slice(0, maxFiles)
211
+ .map((entry) => entry.file);
212
+ }
213
+ function sessionIdFromRolloutPath(filePath) {
214
+ const match = path.basename(filePath).match(/^rollout-.*?T[\d-]+-(.+)\.jsonl$/);
215
+ return match ? match[1] : undefined;
216
+ }
217
+ function codexSessionsDir() {
218
+ return path.join((0, harnessDirs_1.codexHome)(), "sessions");
219
+ }
220
+ exports.CODEX_LIMIT_STATE_PATH = path.join((0, configDir_1.codiedevStateDir)(), "codex-limit-state.json");
221
+ const MAX_STATE_ENTRIES = 500;
222
+ function readSweepState(statePath) {
223
+ let parsed;
224
+ try {
225
+ parsed = JSON.parse(fs.readFileSync(statePath, "utf8"));
226
+ }
227
+ catch {
228
+ return { seen: {} };
229
+ }
230
+ const seen = {};
231
+ const raw = parsed.seen;
232
+ if (raw && typeof raw === "object") {
233
+ for (const [file, value] of Object.entries(raw)) {
234
+ const record = value;
235
+ if (typeof record?.size !== "number")
236
+ continue;
237
+ seen[file] = {
238
+ size: record.size,
239
+ mtime: typeof record.mtime === "number" ? record.mtime : -1,
240
+ at: typeof record.at === "number" ? record.at : null,
241
+ t: typeof record.t === "number" ? record.t : 0,
242
+ };
243
+ }
244
+ }
245
+ const legacyClean = parsed.clean;
246
+ if (legacyClean && typeof legacyClean === "object") {
247
+ for (const [file, size] of Object.entries(legacyClean)) {
248
+ if (typeof size === "number" && !seen[file])
249
+ seen[file] = { size, mtime: -1, at: null, t: 0 };
250
+ }
251
+ }
252
+ return { seen };
253
+ }
254
+ const PRUNE_AFTER_MS = 7 * 24 * 3600_000;
255
+ function pruneSweepState(state, now) {
256
+ for (const [file, record] of Object.entries(state.seen)) {
257
+ if (now - (record.t ?? 0) < PRUNE_AFTER_MS)
258
+ continue;
259
+ if (!fs.existsSync(file))
260
+ delete state.seen[file];
261
+ }
262
+ const files = Object.keys(state.seen);
263
+ if (files.length <= MAX_STATE_ENTRIES)
264
+ return;
265
+ files
266
+ .sort((left, right) => (state.seen[right].t ?? 0) - (state.seen[left].t ?? 0))
267
+ .slice(MAX_STATE_ENTRIES)
268
+ .forEach((file) => delete state.seen[file]);
269
+ }
270
+ function commitSweepState(statePath, ours, now) {
271
+ const current = readSweepState(statePath);
272
+ for (const [file, record] of Object.entries(ours))
273
+ current.seen[file] = record;
274
+ pruneSweepState(current, now);
275
+ writeSweepState(statePath, current);
276
+ }
277
+ function writeSweepState(statePath, state) {
278
+ const temp = `${statePath}.${process.pid}.tmp`;
279
+ try {
280
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
281
+ fs.writeFileSync(temp, JSON.stringify(state), "utf8");
282
+ fs.renameSync(temp, statePath);
283
+ }
284
+ catch {
285
+ try {
286
+ fs.unlinkSync(temp);
287
+ }
288
+ catch {
289
+ }
290
+ }
291
+ }
292
+ function fingerprint(filePath) {
293
+ try {
294
+ const stat = fs.statSync(filePath);
295
+ return { size: stat.size, mtime: stat.mtimeMs };
296
+ }
297
+ catch {
298
+ return null;
299
+ }
300
+ }
301
+ function recordCodexLimitReported(filePath, at, statePath = exports.CODEX_LIMIT_STATE_PATH) {
302
+ const print = fingerprint(filePath);
303
+ if (!print)
304
+ return;
305
+ commitSweepState(statePath, { [filePath]: { ...print, at, t: Date.now() } }, Date.now());
306
+ }
307
+ const DEFAULT_CANDIDATES = 200;
308
+ const DEFAULT_MAX_SCANS = 64;
309
+ const DEFAULT_DEADLINE_MS = 1_500;
310
+ async function sweepCodexLimits(args) {
311
+ const statePath = args.statePath ?? exports.CODEX_LIMIT_STATE_PATH;
312
+ const state = readSweepState(statePath);
313
+ const files = findRecentCodexRollouts(args.sessionsDir, {
314
+ maxFiles: DEFAULT_CANDIDATES,
315
+ ...args.findOptions,
316
+ });
317
+ const now = Date.now();
318
+ const touched = {};
319
+ let budget = args.maxScans ?? DEFAULT_MAX_SCANS;
320
+ const deadline = now + (args.deadlineMs ?? DEFAULT_DEADLINE_MS);
321
+ const pending = [];
322
+ for (const file of files) {
323
+ const print = fingerprint(file);
324
+ if (!print)
325
+ continue;
326
+ const record = state.seen[file];
327
+ if (record && record.size === print.size && record.mtime === print.mtime)
328
+ continue;
329
+ if (budget <= 0 || Date.now() >= deadline)
330
+ break;
331
+ budget -= 1;
332
+ const hit = scanCodexRolloutForLimit(file, args.scanOptions);
333
+ if (!hit) {
334
+ touched[file] = { ...print, at: null, t: now };
335
+ continue;
336
+ }
337
+ if (record && record.at === hit.event.at) {
338
+ touched[file] = { ...print, at: record.at, t: now };
339
+ continue;
340
+ }
341
+ pending.push({ file, print, hit });
342
+ }
343
+ const results = await Promise.allSettled(pending.map(({ file, hit }) => args.post({
344
+ source: "codex",
345
+ ...hit.event,
346
+ sessionId: sessionIdFromRolloutPath(file),
347
+ transcriptTail: readTailEndingAt(file, hit.lineEnd),
348
+ })));
349
+ let posted = 0;
350
+ results.forEach((result, index) => {
351
+ if (result.status !== "fulfilled")
352
+ return;
353
+ const { file, print, hit } = pending[index];
354
+ touched[file] = { ...print, at: hit.event.at, t: now };
355
+ posted += 1;
356
+ });
357
+ commitSweepState(statePath, touched, now);
358
+ return posted;
359
+ }
package/dist/codie.js CHANGED
@@ -1,15 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- // `codie` — Codiedev's governed coding harness.
4
- //
5
- // Ships from the same package as `codiedev` so a developer runs one install and
6
- // then picks their surface: Claude Code, Codex, and Cursor get session capture
7
- // via `codiedev connect`; `codie` launches the governed harness against the
8
- // same token. OpenCode is the embedded engine; all user-facing identity,
9
- // credentials, models, policy, and capture belong to Codiedev.
10
- //
11
- // The engine is downloaded on first run (see codieRuntime.ts) so capture-only
12
- // users never pay for it.
13
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
4
  if (k2 === undefined) k2 = k;
15
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -51,10 +41,17 @@ const child_process_1 = require("child_process");
51
41
  const url_1 = require("url");
52
42
  const configDir_1 = require("./configDir");
53
43
  const codieLauncher_1 = require("./codieLauncher");
44
+ const codieBrandInstall_1 = require("./codieBrandInstall");
45
+ const codieUsage_1 = require("./codieUsage");
54
46
  const codieRuntime_1 = require("./codieRuntime");
55
47
  const codieRuntimeInstall_1 = require("./codieRuntimeInstall");
56
- const codieStartup_1 = require("./codieStartup");
57
48
  const upgradeNudge_1 = require("./upgradeNudge");
49
+ const codie_1 = require("./packs/codie");
50
+ const paths_1 = require("./packs/paths");
51
+ const managedPi_1 = require("./managedPi");
52
+ const cliSession_1 = require("./cliSession");
53
+ const CODIE_MIN_TOKEN_VALIDITY_MS = 12 * 60 * 60_000;
54
+ const USAGE_SETTLE_MS = 1200;
58
55
  function git(args) {
59
56
  const result = (0, child_process_1.spawnSync)("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
60
57
  return result.status === 0 ? result.stdout.trim() : "";
@@ -83,8 +80,6 @@ function existingInlineConfig() {
83
80
  throw new Error("OPENCODE_CONFIG_CONTENT is not valid JSON");
84
81
  }
85
82
  }
86
- // The one credential. `codiedev connect` wrote it; the gateway validates the
87
- // same userApiTokens row, so there is no second harness token to mint.
88
83
  function connectedCodiedevConfig() {
89
84
  try {
90
85
  return JSON.parse(fs.readFileSync(path.join((0, configDir_1.codiedevStateDir)(), "config.json"), "utf8"));
@@ -93,40 +88,23 @@ function connectedCodiedevConfig() {
93
88
  return {};
94
89
  }
95
90
  }
96
- function assetPath(name) {
97
- // dist/codie.js → ../assets/<name>; src/codie.ts → ../assets/<name>.
98
- return path.join(__dirname, "..", "assets", name);
99
- }
100
- // Stage the home-screen branding plugin. TUI plugins only load from tui.json
101
- // in the OpenCode config dir, so the packaged file is copied next to the
102
- // capture plugin and referenced with a relative spec. Best-effort: any
103
- // failure (e.g. a hand-edited tui.json with comments) leaves the stock logo.
104
- function installBrandPlugin() {
91
+ function withStarterPacks(existing, approved, catalog) {
105
92
  try {
106
- // OpenCode resolves its config dir per the XDG spec, so a customized
107
- // XDG_CONFIG_HOME must be honored or tui.json lands where it never reads.
108
- const configDir = path.join(process.env.XDG_CONFIG_HOME?.trim() || path.join(os.homedir(), ".config"), "opencode");
109
- const pluginDir = path.join(configDir, "plugins");
110
- fs.mkdirSync(pluginDir, { recursive: true });
111
- const source = fs.readFileSync(assetPath("tui-brand-plugin.tsx"), "utf8");
112
- const destination = path.join(pluginDir, "codiedev-brand.tsx");
113
- if (!fs.existsSync(destination) || fs.readFileSync(destination, "utf8") !== source) {
114
- fs.writeFileSync(destination, source);
93
+ const packs = (0, codie_1.codiePackConfig)((0, paths_1.packsRoot)(), { approvedModels: approved, catalog });
94
+ if (process.env.CODIEDEV_DEBUG) {
95
+ for (const note of packs.notes)
96
+ console.error(`codie: starter pack ${note}`);
115
97
  }
116
- const tuiConfigPath = path.join(configDir, "tui.json");
117
- const existing = fs.existsSync(tuiConfigPath) ? JSON.parse(fs.readFileSync(tuiConfigPath, "utf8")) : {};
118
- const { config, changed } = (0, codieLauncher_1.withBrandPlugin)(existing);
119
- if (changed)
120
- fs.writeFileSync(tuiConfigPath, `${JSON.stringify(config, null, 2)}\n`);
98
+ return (0, codie_1.mergeCodiePackConfig)(existing, packs);
121
99
  }
122
100
  catch (error) {
123
- console.error(`codie: continuing without Codiedev branding (${error instanceof Error ? error.message : error})`);
101
+ console.error(`codie: skipping starter packs (${error instanceof Error ? error.message : error})`);
102
+ return existing;
124
103
  }
125
104
  }
126
- // Remove the engine's boot/exit banner wordmark in place (see
127
- // patchBrandWordmark). Best-effort like the TUI plugin: any failure rolls the
128
- // bytes back and the stock banner keeps working. Runs once per install; an
129
- // already-suppressed binary has no known source region and is skipped.
105
+ function assetPath(name) {
106
+ return path.join(__dirname, "..", "assets", name);
107
+ }
130
108
  function installBrandWordmark(runtime) {
131
109
  try {
132
110
  installBrandWordmarkUnsafe(runtime);
@@ -140,10 +118,6 @@ function installBrandWordmarkUnsafe(runtime) {
140
118
  const { changed, offset, replacement } = (0, codieLauncher_1.patchBrandWordmark)(binary);
141
119
  if (!changed || !replacement)
142
120
  return;
143
- // In-place write (not copy+rename): opencode-ai/bin/opencode.exe and the
144
- // platform package's binary are hardlinks to one inode, and this keeps them
145
- // consistent. Restoring the original bytes is a true rollback — identical
146
- // content revalidates the existing macOS signature.
147
121
  const writeAt = (bytes) => {
148
122
  const fd = fs.openSync(runtime, "r+");
149
123
  try {
@@ -156,7 +130,6 @@ function installBrandWordmarkUnsafe(runtime) {
156
130
  const restore = () => writeAt(binary.subarray(offset, offset + replacement.length));
157
131
  writeAt(replacement);
158
132
  if (process.platform === "darwin") {
159
- // A modified Mach-O needs a fresh ad-hoc signature or the OS kills it.
160
133
  const signed = (0, child_process_1.spawnSync)("codesign", ["--force", "--sign", "-", runtime], { stdio: "ignore" });
161
134
  if (signed.status !== 0) {
162
135
  restore();
@@ -186,7 +159,6 @@ async function runNonInteractive(runtime, runtimeArgs, env) {
186
159
  return;
187
160
  }
188
161
  catch {
189
- // Fall back to the direct child if its process group already exited.
190
162
  }
191
163
  }
192
164
  child.kill(signal);
@@ -196,73 +168,8 @@ async function runNonInteractive(runtime, runtimeArgs, env) {
196
168
  process.once("SIGINT", forwardInterrupt);
197
169
  process.once("SIGTERM", forwardTermination);
198
170
  return await new Promise((resolve, reject) => {
199
- let blockReason;
200
- const errorPrefixes = [Buffer.from("\x1b[91m\x1b[1mError:"), Buffer.from("Error: {")];
201
- const retainedPrefixBytes = Math.max(...errorPrefixes.map((prefix) => prefix.length)) - 1;
202
- // OpenCode's generic UnknownError and the adapter's private reason can be
203
- // delivered in either order — and the generic block can arrive on stdout
204
- // or stderr. Gate both streams by content, retaining only a possible final
205
- // error block. Ordinary output stays streaming apart from the few bytes
206
- // needed to recognize a split prefix.
207
- const createOutputGate = (write) => {
208
- let tail = Buffer.alloc(0);
209
- let possibleError;
210
- return {
211
- push(chunk, blocked) {
212
- if (blocked) {
213
- if (possibleError)
214
- possibleError.push(Buffer.from(chunk));
215
- return;
216
- }
217
- if (possibleError) {
218
- possibleError.push(Buffer.from(chunk));
219
- return;
220
- }
221
- const combined = Buffer.concat([tail, chunk]);
222
- let errorStart = -1;
223
- for (const prefix of errorPrefixes) {
224
- const index = combined.indexOf(prefix);
225
- if (index >= 0 && (errorStart < 0 || index < errorStart))
226
- errorStart = index;
227
- }
228
- if (errorStart >= 0) {
229
- if (errorStart > 0)
230
- write(combined.subarray(0, errorStart));
231
- possibleError = [combined.subarray(errorStart)];
232
- tail = Buffer.alloc(0);
233
- return;
234
- }
235
- const flushBytes = Math.max(0, combined.length - retainedPrefixBytes);
236
- if (flushBytes > 0)
237
- write(combined.subarray(0, flushBytes));
238
- tail = combined.subarray(flushBytes);
239
- },
240
- finish(blocked) {
241
- if (tail.length > 0)
242
- write(tail);
243
- const heldError = possibleError ? Buffer.concat(possibleError).toString() : "";
244
- const genericBlockError = heldError.includes("UnknownError") &&
245
- heldError.includes("Unexpected server error");
246
- if (heldError && (!blocked || !genericBlockError))
247
- write(heldError);
248
- },
249
- };
250
- };
251
- const stdoutGate = createOutputGate((chunk) => process.stdout.write(chunk));
252
- const stderrGate = createOutputGate((chunk) => process.stderr.write(chunk));
253
- child.stdout?.on("data", (chunk) => {
254
- stdoutGate.push(Buffer.from(chunk), Boolean(blockReason));
255
- });
256
- child.stderr?.on("data", (chunk) => {
257
- const text = chunk.toString();
258
- const reason = (0, codieLauncher_1.extractCodieSecurityBlock)(text);
259
- if (reason) {
260
- blockReason = reason;
261
- signalChild("SIGTERM");
262
- return;
263
- }
264
- stderrGate.push(Buffer.from(chunk), Boolean(blockReason));
265
- });
171
+ child.stdout?.on("data", (chunk) => process.stdout.write(chunk));
172
+ child.stderr?.on("data", (chunk) => process.stderr.write(chunk));
266
173
  child.once("error", (error) => {
267
174
  if (child.stdin)
268
175
  process.stdin.unpipe(child.stdin);
@@ -275,38 +182,57 @@ async function runNonInteractive(runtime, runtimeArgs, env) {
275
182
  process.stdin.unpipe(child.stdin);
276
183
  process.removeListener("SIGINT", forwardInterrupt);
277
184
  process.removeListener("SIGTERM", forwardTermination);
278
- stdoutGate.finish(Boolean(blockReason));
279
- stderrGate.finish(Boolean(blockReason));
280
- if (blockReason) {
281
- console.error(`Codie Security blocked this action:\n${blockReason}`);
282
- }
283
185
  resolve(status);
284
186
  });
285
187
  });
286
188
  }
287
189
  async function main() {
288
190
  (0, codieRuntime_1.assertCodieNodeVersion)(process.versions.node);
191
+ if (!process.env.CODIEDEV_GATEWAY_KEY && !process.env.CODIEDEV_TOKEN) {
192
+ const fresh = await (0, cliSession_1.ensureFreshToken)({ configPath: path.join((0, configDir_1.codiedevStateDir)(), "config.json") }, CODIE_MIN_TOKEN_VALIDITY_MS);
193
+ if (fresh.status === "signed_out") {
194
+ throw new Error("Your Codiedev sign-in has ended. Run `codiedev connect` to sign in again");
195
+ }
196
+ }
289
197
  const connected = connectedCodiedevConfig();
198
+ if (!process.env.CODIEDEV_GATEWAY_KEY && !process.env.CODIEDEV_TOKEN && connected.auth) {
199
+ process.env.CODIEDEV_SESSION_CONFIG = path.join((0, configDir_1.codiedevStateDir)(), "config.json");
200
+ process.env.CODIEDEV_CLI_JS = path.join(__dirname, "cli.js");
201
+ process.env.CODIEDEV_NODE = process.execPath;
202
+ }
290
203
  const key = process.env.CODIEDEV_GATEWAY_KEY ||
291
204
  process.env.CODIEDEV_TOKEN ||
292
205
  (typeof connected.token === "string" ? connected.token.trim() : "");
293
206
  if (!key) {
294
207
  throw new Error("Connect your Codiedev account first with `codiedev connect`");
295
208
  }
296
- // Before anything paints: once the OpenCode engine owns stdio, a prompt is
297
- // unreadable. A successful in-place upgrade relaunches this command with the
298
- // new package, so the stale process must stop here.
299
209
  const update = await (0, upgradeNudge_1.maybeOfferUpgrade)();
300
210
  if (update.relaunched) {
301
211
  process.exitCode = update.status ?? 0;
302
212
  return;
303
213
  }
304
- const policyRefresh = await (0, codieStartup_1.refreshCodieSecurityPolicy)(connected);
305
- if (policyRefresh.status === "cached") {
306
- console.error(`codie: security policy refresh failed; using the cached policy (${policyRefresh.error})`);
307
- }
308
214
  const gatewayUrl = process.env.CODIEDEV_GATEWAY_URL || "https://gateway.codiedev.com/api/v1";
309
215
  const catalog = await (0, codieLauncher_1.fetchApprovedCatalog)({ baseUrl: gatewayUrl, key });
216
+ if (process.argv[2] === "--pi") {
217
+ let skillPaths = [];
218
+ try {
219
+ const approved = new Set(catalog.map((model) => (0, codieLauncher_1.bareModel)(model.id)).filter(Boolean));
220
+ skillPaths = (0, codie_1.codiePackConfig)((0, paths_1.packsRoot)(), { approvedModels: approved, catalog }).skillPaths;
221
+ }
222
+ catch (error) {
223
+ console.error(`codie: skipping starter packs (${error instanceof Error ? error.message : error})`);
224
+ }
225
+ try {
226
+ process.exitCode = (0, managedPi_1.runManagedPi)({
227
+ args: process.argv.slice(3), key, gatewayUrl, catalog, stateDir: (0, configDir_1.codiedevStateDir)(), skillPaths,
228
+ });
229
+ }
230
+ catch (error) {
231
+ console.error(`codie: ${error instanceof Error ? error.message : String(error)}`);
232
+ process.exitCode = error instanceof managedPi_1.ManagedPiArgError ? 2 : 1;
233
+ }
234
+ return;
235
+ }
310
236
  const parsed = (0, codieLauncher_1.parseCodieArgs)(process.argv.slice(2));
311
237
  if (parsed.args.length === 1 && parsed.args[0] === "models") {
312
238
  console.log("Codiedev approved models\n");
@@ -337,8 +263,18 @@ async function main() {
337
263
  return null;
338
264
  }
339
265
  },
266
+ isEngine: codieRuntimeInstall_1.isEngineOnDisk,
340
267
  }),
341
268
  install: (missing) => (0, codieRuntimeInstall_1.installCodieRuntimeOnDisk)(missing, stateDir),
269
+ repair: (unlinked) => {
270
+ console.error("codie: finishing the engine setup npm skipped (opencode-ai's postinstall)…");
271
+ const linked = (0, codieRuntimeInstall_1.linkEngineOnDisk)(unlinked.packageDir);
272
+ if (!linked.ok && linked.detail === codieRuntimeInstall_1.IGNORE_SCRIPTS)
273
+ throw new Error((0, codieRuntimeInstall_1.linkFailure)(linked, unlinked.packageDir));
274
+ if (!linked.ok)
275
+ console.error(`codie: ${(0, codieRuntimeInstall_1.linkFailure)(linked, unlinked.packageDir)} Reinstalling the engine.`);
276
+ return linked.ok;
277
+ },
342
278
  confirmUpdate: async (outdated) => {
343
279
  console.error("");
344
280
  console.error(`Codie harness ${outdated.expectedVersion} is available (you're on ${outdated.currentVersion}).`);
@@ -352,10 +288,10 @@ async function main() {
352
288
  });
353
289
  const metadataPlugin = (0, url_1.pathToFileURL)(assetPath("opencode-plugin.mjs")).href;
354
290
  const capturePlugin = (0, codieLauncher_1.resolveCapturePlugin)({ homeDir: os.homedir(), existsSync: fs.existsSync });
355
- installBrandPlugin();
291
+ (0, codieBrandInstall_1.installTuiPlugin)();
356
292
  installBrandWordmark(runtime);
357
293
  const config = (0, codieLauncher_1.buildCodieConfig)({
358
- existing: existingInlineConfig(),
294
+ existing: withStarterPacks(existingInlineConfig(), new Set(catalog.map((model) => (0, codieLauncher_1.bareModel)(model.id)).filter(Boolean)), catalog),
359
295
  catalog,
360
296
  env: { ...process.env, ...parsed.overrides, CODIEDEV_GATEWAY_URL: gatewayUrl },
361
297
  plugins: [capturePlugin, metadataPlugin],
@@ -371,10 +307,21 @@ async function main() {
371
307
  };
372
308
  const runtimeArgs = (0, codieLauncher_1.withCodieWorkingDirectory)(parsed.args, process.cwd());
373
309
  const nonInteractiveRun = runtimeArgs[0] === "run";
374
- if (nonInteractiveRun)
375
- env.CODIEDEV_SECURITY_BLOCK_OUTPUT = "marker";
376
310
  if (nonInteractiveRun) {
311
+ const reportCost = Boolean(process.stderr.isTTY);
312
+ const startedAt = Date.now();
313
+ if (reportCost) {
314
+ const line = (0, codieUsage_1.formatUsageLine)(await (0, codieUsage_1.fetchCodieUsage)({ baseUrl: gatewayUrl, key }));
315
+ if (line)
316
+ console.error(line);
317
+ }
377
318
  process.exitCode = (await runNonInteractive(runtime, runtimeArgs, env)) ?? 1;
319
+ if (reportCost) {
320
+ await new Promise((resolve) => setTimeout(resolve, USAGE_SETTLE_MS));
321
+ const cost = (0, codieUsage_1.formatSessionCostLine)(await (0, codieUsage_1.fetchCodieUsage)({ baseUrl: gatewayUrl, key, sinceMs: startedAt }));
322
+ if (cost)
323
+ console.error(cost);
324
+ }
378
325
  return;
379
326
  }
380
327
  const result = (0, child_process_1.spawnSync)(runtime, runtimeArgs, { stdio: "inherit", env });