codiedev 0.10.0-beta.4 → 0.10.0-beta.6

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 (157) hide show
  1. package/README.md +11 -5
  2. package/assets/tui-brand-plugin.tsx +14 -2
  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 +68 -3
  10. package/dist/cli.js +75 -64
  11. package/dist/codexHookTrust.js +125 -6
  12. package/dist/codexLimitScan.js +2 -109
  13. package/dist/codie.js +50 -114
  14. package/dist/codieBrandInstall.js +0 -18
  15. package/dist/codieLauncher.js +20 -92
  16. package/dist/codieRuntime.js +19 -23
  17. package/dist/codieRuntimeInstall.js +108 -23
  18. package/dist/codieUsage.js +38 -41
  19. package/dist/commands/agentTemplates.generated.js +136 -0
  20. package/dist/commands/agents.js +1066 -0
  21. package/dist/commands/agentsSdk.generated.js +7 -0
  22. package/dist/commands/ask.js +0 -3
  23. package/dist/commands/capture.js +0 -2
  24. package/dist/commands/createTicket.js +0 -10
  25. package/dist/commands/doctor.js +53 -120
  26. package/dist/commands/evals.js +187 -0
  27. package/dist/commands/inbox.js +0 -2
  28. package/dist/commands/library.js +0 -3
  29. package/dist/commands/pack.js +573 -0
  30. package/dist/commands/ping.js +0 -2
  31. package/dist/commands/pull.js +6 -10
  32. package/dist/commands/push.js +2 -12
  33. package/dist/commands/search.js +0 -4
  34. package/dist/commands/send.js +0 -5
  35. package/dist/commands/share.js +0 -4
  36. package/dist/commands/shared.js +0 -35
  37. package/dist/commands/traces.js +344 -0
  38. package/dist/commands/tracesLabel.js +348 -0
  39. package/dist/configDir.js +0 -10
  40. package/dist/connect.js +19 -116
  41. package/dist/connectFlow.js +0 -9
  42. package/dist/copilotHooks.js +391 -0
  43. package/dist/copilotInstallation.js +64 -0
  44. package/dist/cursorHooks.js +38 -30
  45. package/dist/cursorLifecycle.js +109 -0
  46. package/dist/detection.js +0 -30
  47. package/dist/harnessDirs.js +77 -0
  48. package/dist/hook.js +182 -469
  49. package/dist/inboxFormat.js +0 -24
  50. package/dist/launchContext.js +150 -0
  51. package/dist/launcher.js +140 -0
  52. package/dist/limitEvent.js +41 -35
  53. package/dist/limitEventReporter.js +1 -1
  54. package/dist/managedPi.js +240 -0
  55. package/dist/mcp.js +3 -384
  56. package/dist/nativeSteering.js +24 -0
  57. package/dist/packs/codie.js +182 -0
  58. package/dist/packs/harnesses.js +411 -0
  59. package/dist/packs/hookGate.js +256 -0
  60. package/dist/packs/install.js +286 -0
  61. package/dist/packs/manifest.js +126 -0
  62. package/dist/packs/paths.js +81 -0
  63. package/dist/packs/shim.js +214 -0
  64. package/dist/packs/store.js +155 -0
  65. package/dist/packs/sync.js +170 -0
  66. package/dist/piHooks.js +216 -0
  67. package/dist/piRuntime.js +120 -0
  68. package/dist/repoResolver.js +0 -19
  69. package/dist/secret.js +0 -3
  70. package/dist/selfUpdate.js +376 -0
  71. package/dist/skillMarker.js +23 -0
  72. package/dist/subagents.js +135 -0
  73. package/dist/teamSkillContext.js +10 -0
  74. package/dist/transcriptModels.js +6 -8
  75. package/dist/upgradeNudge.js +0 -30
  76. package/dist/utils.js +494 -824
  77. package/dist/version.js +0 -10
  78. package/dist/versionCheck.js +0 -33
  79. package/package.json +8 -9
  80. package/dist/backup/cache.d.ts +0 -14
  81. package/dist/backup/match.d.ts +0 -2
  82. package/dist/backup/run.d.ts +0 -25
  83. package/dist/captureExclusions.d.ts +0 -30
  84. package/dist/captureLog.d.ts +0 -20
  85. package/dist/captureReadiness.d.ts +0 -65
  86. package/dist/captureSignals.d.ts +0 -19
  87. package/dist/cli.d.ts +0 -16
  88. package/dist/codexHookTrust.d.ts +0 -34
  89. package/dist/codexLimitScan.d.ts +0 -64
  90. package/dist/codie.d.ts +0 -2
  91. package/dist/codieBrandInstall.d.ts +0 -11
  92. package/dist/codieLauncher.d.ts +0 -86
  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/codieUsage.d.ts +0 -57
  98. package/dist/commands/ask.d.ts +0 -1
  99. package/dist/commands/capture.d.ts +0 -14
  100. package/dist/commands/createTicket.d.ts +0 -27
  101. package/dist/commands/delete.d.ts +0 -1
  102. package/dist/commands/doctor.d.ts +0 -1
  103. package/dist/commands/inbox.d.ts +0 -3
  104. package/dist/commands/library.d.ts +0 -2
  105. package/dist/commands/note.d.ts +0 -1
  106. package/dist/commands/ping.d.ts +0 -1
  107. package/dist/commands/promote.d.ts +0 -1
  108. package/dist/commands/promote.js +0 -53
  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/react.js +0 -40
  113. package/dist/commands/reverseTicket.d.ts +0 -2
  114. package/dist/commands/reverseTicket.js +0 -477
  115. package/dist/commands/search.d.ts +0 -2
  116. package/dist/commands/security.d.ts +0 -1
  117. package/dist/commands/security.js +0 -156
  118. package/dist/commands/send.d.ts +0 -2
  119. package/dist/commands/share.d.ts +0 -2
  120. package/dist/commands/shared.d.ts +0 -64
  121. package/dist/configDir.d.ts +0 -1
  122. package/dist/connect.d.ts +0 -1
  123. package/dist/connectFlow.d.ts +0 -13
  124. package/dist/cursorHooks.d.ts +0 -23
  125. package/dist/detection.d.ts +0 -30
  126. package/dist/hook.d.ts +0 -6
  127. package/dist/inboxFormat.d.ts +0 -38
  128. package/dist/limitEvent.d.ts +0 -16
  129. package/dist/limitEventReporter.d.ts +0 -21
  130. package/dist/mcp.d.ts +0 -12
  131. package/dist/prUrl.d.ts +0 -9
  132. package/dist/prUrl.js +0 -33
  133. package/dist/repoResolver.d.ts +0 -17
  134. package/dist/secret.d.ts +0 -1
  135. package/dist/security.d.ts +0 -102
  136. package/dist/security.js +0 -437
  137. package/dist/securityDeepScan.d.ts +0 -41
  138. package/dist/securityDeepScan.js +0 -248
  139. package/dist/securityDoctor.d.ts +0 -32
  140. package/dist/securityDoctor.js +0 -63
  141. package/dist/securityFeature.d.ts +0 -5
  142. package/dist/securityFeature.js +0 -7
  143. package/dist/securityInstall.d.ts +0 -63
  144. package/dist/securityInstall.js +0 -239
  145. package/dist/securityInventory.d.ts +0 -54
  146. package/dist/securityInventory.js +0 -556
  147. package/dist/securityPilot.d.ts +0 -19
  148. package/dist/securityPilot.js +0 -61
  149. package/dist/securityPolicy.d.ts +0 -33
  150. package/dist/securityPolicy.js +0 -188
  151. package/dist/securitySession.d.ts +0 -6
  152. package/dist/securitySession.js +0 -72
  153. package/dist/transcriptModels.d.ts +0 -18
  154. package/dist/upgradeNudge.d.ts +0 -25
  155. package/dist/utils.d.ts +0 -346
  156. package/dist/version.d.ts +0 -1
  157. package/dist/versionCheck.d.ts +0 -68
@@ -2,32 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatInbox = formatInbox;
4
4
  const shared_1 = require("./commands/shared");
5
- /**
6
- * Render the inbox for BOTH surfaces — `codiedev inbox` and the MCP
7
- * `codiedev_inbox` tool an agent calls for "any messages?". Shared so the
8
- * agent path can never drift back to pings-only while the human path shows
9
- * mentions.
10
- */
11
5
  function formatInbox(opts) {
12
6
  const mentions = opts.mentions ?? [];
13
- // The backend applies `limit` to each source independently, so asking for N
14
- // can return up to 2N — and with no `--limit` it defaults to 50 per source,
15
- // i.e. 100 rows, twice the pre-change maximum. Cap the COMBINED list by
16
- // recency, always (matching the backend's own default), so the header is
17
- // truthful and `--limit` actually holds.
18
- //
19
- // Selected by identity rather than by a `createdAt >= cutoff` filter: rows
20
- // tied on the cutoff millisecond are reachable (Convex freezes Date.now()
21
- // per transaction, and seeded activity carries synthetic stamps) and a
22
- // threshold filter lets every tied row through, overshooting the limit.
23
7
  const DEFAULT_LIMIT = 50;
24
8
  const cap = opts.limit ?? DEFAULT_LIMIT;
25
- // Pings claim the budget first, then mentions fill whatever is left — both
26
- // newest-first. Before this change pings owned the whole budget, so this is
27
- // purely additive and can never hide a ping that used to be visible. A flat
28
- // recency cap would instead let a busy feed evict every direct ping, and
29
- // pings are the actionable half: they carry the id you reply to, and losing
30
- // them all takes the mark-read footer with them.
31
9
  const byRecency = (xs) => [...xs].sort((a, b) => b.createdAt - a.createdAt);
32
10
  const keptPings = byRecency(opts.pings).slice(0, cap);
33
11
  const keptMentions = byRecency(mentions).slice(0, Math.max(0, cap - keptPings.length));
@@ -50,8 +28,6 @@ function formatInbox(opts) {
50
28
  lines.push(` id: ${p.pingId}`);
51
29
  lines.push("");
52
30
  }
53
- // Mentions carry no CLI-side body to reply to — the thread lives in the feed
54
- // — so each row ends in the deep link rather than an id.
55
31
  for (const m of keptMentions) {
56
32
  const dot = m.readAt ? "·" : "●";
57
33
  const from = m.from?.name ?? "someone";
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.codieLaunchFromArgv = codieLaunchFromArgv;
4
+ exports.cursorLaunchFromCommand = cursorLaunchFromCommand;
5
+ exports.copilotLaunchFromCommand = copilotLaunchFromCommand;
6
+ exports.windowsAncestors = windowsAncestors;
7
+ exports.detectCursorLaunch = detectCursorLaunch;
8
+ exports.detectCopilotLaunch = detectCopilotLaunch;
9
+ exports.hookLaunch = hookLaunch;
10
+ const node_child_process_1 = require("node:child_process");
11
+ const VALUE = /^[a-z0-9][a-z0-9_.:-]{0,39}$/;
12
+ const CODIE_SUBCOMMANDS = ["run", "serve", "acp", "web", "attach"];
13
+ function codieLaunchFromArgv(argv) {
14
+ const sub = argv.find((a) => CODIE_SUBCOMMANDS.includes(a));
15
+ return sub ? `codie:${sub}` : "codie:tui";
16
+ }
17
+ function tokenize(command) {
18
+ const tokens = [];
19
+ for (const m of command.matchAll(/"([^"]*)"|(\S+)/g))
20
+ tokens.push(m[1] ?? m[2]);
21
+ return tokens;
22
+ }
23
+ const CURSOR_AGENT_PATH = /(^|[/\\])cursor-agent([/\\]|\.(exe|cmd|ps1)$|$)/i;
24
+ const AGENT_BIN = /(^|[/\\])(cursor-)?agent(\.(exe|cmd|ps1))?$/i;
25
+ function cursorLaunchFromCommand(command) {
26
+ const tokens = tokenize(command);
27
+ const isAgent = tokens.some((t) => CURSOR_AGENT_PATH.test(t) || /[/\\]\.local[/\\]share[/\\]cursor/i.test(t))
28
+ && tokens.some((t) => AGENT_BIN.test(t) || /index\.js$/i.test(t));
29
+ if (isAgent) {
30
+ return cursorFlags(tokens).some((f) => f === "-p" || f === "--print" || f.startsWith("--print="))
31
+ ? "cursor:print"
32
+ : "cursor:cli";
33
+ }
34
+ if (/Cursor\.app|Cursor Helper|[/\\]cursor(\.exe)?(\s|"|$)/i.test(command))
35
+ return "cursor:ide";
36
+ return undefined;
37
+ }
38
+ const CURSOR_VALUE_FLAGS = new Set(["--model", "-m", "--output-format", "--api-key", "--resume", "--workspace", "--mode", "-H", "--header"]);
39
+ function cursorFlags(tokens) {
40
+ let start = tokens.findIndex((t) => /index\.js$/i.test(t));
41
+ if (start < 0)
42
+ start = tokens.findIndex((t) => AGENT_BIN.test(t));
43
+ const flags = [];
44
+ for (let i = (start >= 0 ? start : 0) + 1; i < tokens.length; i++) {
45
+ const t = tokens[i];
46
+ if (!t.startsWith("-"))
47
+ break;
48
+ flags.push(t);
49
+ if (CURSOR_VALUE_FLAGS.has(t))
50
+ i++;
51
+ }
52
+ return flags;
53
+ }
54
+ const COPILOT_BIN = /(^|[/\\])copilot(\.(exe|cmd|ps1|js))?$/i;
55
+ const NODE_BIN = /(^|[/\\])node(\.exe)?$/i;
56
+ function copilotLaunchFromCommand(command) {
57
+ const tokens = tokenize(command);
58
+ const bin = COPILOT_BIN.test(tokens[0] ?? "")
59
+ ? 0
60
+ : NODE_BIN.test(tokens[0] ?? "") && COPILOT_BIN.test(tokens[1] ?? "")
61
+ ? 1
62
+ : -1;
63
+ if (bin < 0)
64
+ return undefined;
65
+ for (let i = bin + 1; i < tokens.length; i++) {
66
+ const t = tokens[i];
67
+ if (t === "-p" || t === "--prompt" || t.startsWith("--prompt="))
68
+ return "copilot:print";
69
+ if (!t.startsWith("-"))
70
+ break;
71
+ if (COPILOT_VALUE_FLAGS.has(t))
72
+ i++;
73
+ }
74
+ return "copilot:cli";
75
+ }
76
+ const COPILOT_VALUE_FLAGS = new Set([
77
+ "--model", "--add-dir", "--allow-tool", "--deny-tool", "--resume", "--log-dir",
78
+ "--log-level", "--agent", "--config-dir", "--allow-url", "--deny-url",
79
+ "--additional-mcp-config", "--stream", "--output-format",
80
+ ]);
81
+ const psPosix = (pid) => {
82
+ const result = (0, node_child_process_1.spawnSync)("ps", ["-o", "ppid=,args=", "-p", String(pid)], { encoding: "utf8", timeout: 500 });
83
+ const line = result.status === 0 ? result.stdout.trim() : "";
84
+ const match = line.match(/^(\d+)\s+(.*)$/s);
85
+ return match ? { ppid: Number(match[1]), command: match[2] } : null;
86
+ };
87
+ function windowsAncestors(startPid, depth) {
88
+ const script = `$p=${Math.trunc(startPid)};$o=@();for($i=0;$i -lt ${Math.trunc(depth)} -and $p -gt 0;$i++){` +
89
+ `$x=Get-CimInstance Win32_Process -Filter "ProcessId=$p" -ErrorAction SilentlyContinue;if(!$x){break};` +
90
+ `$o+=[pscustomobject]@{pid=[int]$p;ppid=[int]$x.ParentProcessId;command=[string]$x.CommandLine};$p=$x.ParentProcessId};` +
91
+ `ConvertTo-Json -InputObject @($o) -Compress`;
92
+ const result = (0, node_child_process_1.spawnSync)("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
93
+ encoding: "utf8",
94
+ timeout: 10000,
95
+ windowsHide: true,
96
+ });
97
+ if (result.status !== 0 || !result.stdout.trim())
98
+ return null;
99
+ try {
100
+ const rows = JSON.parse(result.stdout);
101
+ return rows.map((r) => ({ pid: Number(r.pid), ppid: Number(r.ppid), command: r.command ?? "" }));
102
+ }
103
+ catch {
104
+ return null;
105
+ }
106
+ }
107
+ function detectLaunch(fromCommand, startPid, deps) {
108
+ const depth = deps.depth ?? 5;
109
+ if ((deps.platform ?? process.platform) === "win32" && !deps.ps) {
110
+ const chain = (deps.windows ?? windowsAncestors)(startPid, depth);
111
+ if (!chain)
112
+ return undefined;
113
+ for (const row of chain) {
114
+ const launch = fromCommand(row.command);
115
+ if (launch)
116
+ return launch;
117
+ }
118
+ return undefined;
119
+ }
120
+ const ps = deps.ps ?? psPosix;
121
+ let pid = startPid;
122
+ for (let i = 0; i < depth && pid > 1; i++) {
123
+ const row = ps(pid);
124
+ if (!row)
125
+ return undefined;
126
+ const launch = fromCommand(row.command);
127
+ if (launch)
128
+ return launch;
129
+ pid = row.ppid;
130
+ }
131
+ return undefined;
132
+ }
133
+ function detectCursorLaunch(startPid = process.ppid, deps = {}) {
134
+ return detectLaunch(cursorLaunchFromCommand, startPid, deps);
135
+ }
136
+ function detectCopilotLaunch(startPid = process.ppid, deps = {}) {
137
+ return detectLaunch(copilotLaunchFromCommand, startPid, deps);
138
+ }
139
+ function hookLaunch(hookCommand, input, deps = {}) {
140
+ if (typeof input.launch === "string" && VALUE.test(input.launch))
141
+ return input.launch;
142
+ if (hookCommand?.endsWith("-cursor"))
143
+ return detectCursorLaunch();
144
+ if (hookCommand?.endsWith("-copilot")) {
145
+ if (deps.copilotClientName === "vscode-agent-host")
146
+ return "copilot:vscode";
147
+ return (deps.detectCopilot ?? detectCopilotLaunch)();
148
+ }
149
+ return undefined;
150
+ }
@@ -0,0 +1,140 @@
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.LAUNCHER_FILE = void 0;
37
+ exports.launcherPath = launcherPath;
38
+ exports.usesLauncher = usesLauncher;
39
+ exports.readInstallRecord = readInstallRecord;
40
+ exports.recordCurrentInstall = recordCurrentInstall;
41
+ exports.ensureLauncher = ensureLauncher;
42
+ exports.resetLauncherCacheForTests = resetLauncherCacheForTests;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const configDir_1 = require("./configDir");
46
+ exports.LAUNCHER_FILE = "codiedev-hook-launcher.cjs";
47
+ const LAUNCHER_REVISION = 1;
48
+ function launcherPath() {
49
+ return path.join((0, configDir_1.codiedevStateDir)(), "bin", exports.LAUNCHER_FILE);
50
+ }
51
+ function installRecordPath() {
52
+ return path.join((0, configDir_1.codiedevStateDir)(), "install.json");
53
+ }
54
+ function usesLauncher(cmd) {
55
+ return typeof cmd === "string" && cmd.includes(exports.LAUNCHER_FILE);
56
+ }
57
+ function launcherSource(fallback) {
58
+ return `// codiedev-hook-launcher revision ${LAUNCHER_REVISION}. Managed by codiedev; safe to delete (re-run \`codiedev connect\`).
59
+ "use strict";
60
+ const fs = require("fs");
61
+ const path = require("path");
62
+ const FALLBACK = ${JSON.stringify(fallback)};
63
+ let target = null;
64
+ try {
65
+ const rec = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "install.json"), "utf8"));
66
+ if (rec && typeof rec.hookScript === "string" && fs.existsSync(rec.hookScript)) target = rec.hookScript;
67
+ } catch {}
68
+ if (!target && fs.existsSync(FALLBACK)) target = FALLBACK;
69
+ if (!target) process.exit(0);
70
+ process.env.CODIEDEV_VIA_LAUNCHER = "1";
71
+ process.argv[1] = target;
72
+ require(target);
73
+ `;
74
+ }
75
+ function writeAtomic(target, contents) {
76
+ fs.mkdirSync(path.dirname(target), { recursive: true });
77
+ const tmp = `${target}.${process.pid}.tmp`;
78
+ fs.writeFileSync(tmp, contents, "utf8");
79
+ fs.renameSync(tmp, target);
80
+ }
81
+ function readInstallRecord() {
82
+ try {
83
+ const parsed = JSON.parse(fs.readFileSync(installRecordPath(), "utf8"));
84
+ return typeof parsed.hookScript === "string" ? parsed : null;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ function packageVersionFor(hookScript) {
91
+ try {
92
+ const pkg = JSON.parse(fs.readFileSync(path.join(path.dirname(path.dirname(hookScript)), "package.json"), "utf8"));
93
+ return typeof pkg.version === "string" ? pkg.version : undefined;
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ }
99
+ function recordCurrentInstall(hookScript) {
100
+ try {
101
+ const record = {
102
+ hookScript,
103
+ version: packageVersionFor(hookScript),
104
+ recordedAt: Date.now(),
105
+ };
106
+ writeAtomic(installRecordPath(), JSON.stringify(record, null, 2));
107
+ return true;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ }
113
+ let ensuredFor;
114
+ function ensureLauncher(hookScript) {
115
+ if (ensuredFor !== undefined && ensuredFor === hookScript)
116
+ return launcherPath();
117
+ try {
118
+ const target = launcherPath();
119
+ const source = launcherSource(hookScript);
120
+ let current = null;
121
+ try {
122
+ current = fs.readFileSync(target, "utf8");
123
+ }
124
+ catch {
125
+ }
126
+ if (current !== source)
127
+ writeAtomic(target, source);
128
+ if (readInstallRecord()?.hookScript !== hookScript && !recordCurrentInstall(hookScript)) {
129
+ return null;
130
+ }
131
+ ensuredFor = hookScript;
132
+ return target;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }
138
+ function resetLauncherCacheForTests() {
139
+ ensuredFor = undefined;
140
+ }
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.classifyLimitKind = classifyLimitKind;
4
4
  exports.parseLimitEventFromTranscript = parseLimitEventFromTranscript;
5
5
  exports.parseCodexLimitEventFromTranscript = parseCodexLimitEventFromTranscript;
6
+ exports.parseNativeLimitError = parseNativeLimitError;
7
+ exports.parseNativeLimitEventFromTranscript = parseNativeLimitEventFromTranscript;
6
8
  exports.tailForLimitEvent = tailForLimitEvent;
7
9
  function classifyLimitKind(text) {
8
10
  const t = text.toLowerCase();
@@ -12,7 +14,6 @@ function classifyLimitKind(text) {
12
14
  return "org_spend";
13
15
  return "other";
14
16
  }
15
- // "… · resets 9:50pm (UTC)" → "9:50pm (UTC)". Null when absent.
16
17
  function parseResetText(text) {
17
18
  const m = text.match(/resets\s+(.+?)\s*$/i);
18
19
  return m ? m[1].trim() : null;
@@ -25,8 +26,6 @@ function messageText(entry) {
25
26
  return c.map((x) => (x && typeof x === "object" ? x.text ?? "" : "")).join(" ");
26
27
  return "";
27
28
  }
28
- // Scans transcript JSONL for the LAST top-level `error: "rate_limit"` entry.
29
- // (Validated shape: error/isApiErrorMessage are top-level, not under message.)
30
29
  function parseLimitEventFromTranscript(transcript) {
31
30
  let found = null;
32
31
  for (const line of transcript.split("\n")) {
@@ -51,29 +50,6 @@ function parseLimitEventFromTranscript(transcript) {
51
50
  }
52
51
  return found;
53
52
  }
54
- /**
55
- * Codex's `rate_limit_reached_type` is exactly five values (read off the 0.154
56
- * binary): `rate_limit_reached`, `workspace_{owner,member}_credits_depleted`
57
- * and `workspace_{owner,member}_usage_limit_reached`.
58
- *
59
- * The split that matters to a reader of the dashboard is "someone must act on
60
- * money" vs "this dev is throttled and it will reset" — so map on what the
61
- * snapshot actually asserts, not on the word "credits":
62
- *
63
- * - A member out of their OWN allowance is a rate limit. Real snapshots carry
64
- * `spend_control_reached: null` and `individual_limit: null` — nobody
65
- * configured a budget, the window is simply used up. Calling that personal
66
- * spend tells a manager the dev overspent, which is not what happened.
67
- *
68
- * Deliberately NOT gated on `individual_limit`: it has only ever been
69
- * observed null, so its populated shape and meaning are unknown, and a
70
- * wrong guess reinstates the mislabel this exists to remove. The reason
71
- * string is the causal signal; `individual_limit` is at best correlative.
72
- * - A member whose ADMIN capped them (`usage_limit_reached`) is personal spend:
73
- * there really is a cap, and someone can raise it.
74
- * - Anything owner-scoped is org spend — the org's allowance or credits are
75
- * gone and somebody has to act.
76
- */
77
53
  function codexLimitKind(reason, spendControlReached) {
78
54
  if (spendControlReached || reason.startsWith("workspace_owner_"))
79
55
  return "org_spend";
@@ -94,11 +70,6 @@ function codexResetText(rateLimits) {
94
70
  .sort((a, b) => a - b)[0];
95
71
  return resetAt ? new Date(resetAt * 1000).toISOString() : null;
96
72
  }
97
- /**
98
- * Codex writes a structured RateLimitSnapshot on token_count events. A normal
99
- * turn has `rate_limit_reached_type: null`; a blocked turn names the exact
100
- * boundary. Reading that field avoids locale-dependent terminal copy.
101
- */
102
73
  function parseCodexLimitEventFromTranscript(transcript) {
103
74
  let found = null;
104
75
  for (const line of transcript.split("\n")) {
@@ -133,10 +104,45 @@ function parseCodexLimitEventFromTranscript(transcript) {
133
104
  }
134
105
  return found;
135
106
  }
136
- // Tail of the raw transcript to ship with the limit event so the server can
137
- // summarize "what they were working on" immediately — without waiting for the
138
- // SessionEnd capture (which can lag hours, or drop entirely on Windows).
139
- // Resumes at a line boundary so every JSONL line in the tail still parses.
107
+ function parseNativeLimitError(input, now = Date.now()) {
108
+ const context = input.errorContext ?? input.error_context;
109
+ if (context !== undefined && context !== "model_call")
110
+ return null;
111
+ const error = input.error;
112
+ const fields = error && typeof error === "object" ? error : {};
113
+ const code = String(fields.code ?? fields.name ?? input.error_code ?? "");
114
+ const text = typeof error === "string" ? error
115
+ : typeof fields.message === "string" ? fields.message
116
+ : typeof input.error_message === "string" ? input.error_message : "";
117
+ if (!/^(?:429|rate[_-]?limit(?:ed|_exceeded|_error)?|RateLimitError|quota_exceeded)$/i.test(code)
118
+ && !/\b(?:rate limit (?:exceeded|reached)|too many requests|usage limit (?:exceeded|reached)|premium requests? (?:exhausted|limit reached))\b/i.test(text))
119
+ return null;
120
+ const rawAt = input.timestamp;
121
+ const parsedAt = typeof rawAt === "number" ? rawAt : typeof rawAt === "string" ? Date.parse(rawAt) : NaN;
122
+ return { kind: classifyLimitKind(text), at: Number.isFinite(parsedAt) && parsedAt > 0 ? parsedAt : now,
123
+ resetText: parseResetText(text), reason: code || "rate_limit" };
124
+ }
125
+ function parseNativeLimitEventFromTranscript(transcript) {
126
+ let found = null;
127
+ for (const line of transcript.split("\n")) {
128
+ try {
129
+ const entry = JSON.parse(line);
130
+ if (entry.type === "agent.limit" && Number.isFinite(entry.at) && entry.at > 0
131
+ && ["session", "user_spend", "org_spend", "other"].includes(entry.kind)) {
132
+ found = { kind: entry.kind, at: entry.at, resetText: typeof entry.resetText === "string" ? entry.resetText : null,
133
+ ...(typeof entry.reason === "string" ? { reason: entry.reason } : {}) };
134
+ continue;
135
+ }
136
+ if (!entry.timestamp)
137
+ continue;
138
+ if (entry.type === "agent.error" || entry.type === "session.error") {
139
+ found = parseNativeLimitError({ ...(entry.data ?? entry), timestamp: entry.timestamp }) ?? found;
140
+ }
141
+ }
142
+ catch { }
143
+ }
144
+ return found;
145
+ }
140
146
  const DEFAULT_TAIL_CHARS = 65536;
141
147
  function tailForLimitEvent(transcript, maxChars = DEFAULT_TAIL_CHARS) {
142
148
  if (transcript.length <= maxChars)
@@ -76,7 +76,7 @@ function postLimitEvent(config, payload, timeoutMs = 5_000) {
76
76
  async function reportTranscriptLimitEvent(args) {
77
77
  const parse = args.source === "codex"
78
78
  ? limitEvent_1.parseCodexLimitEventFromTranscript
79
- : limitEvent_1.parseLimitEventFromTranscript;
79
+ : args.source === "cursor" || args.source === "copilot" || args.source === "pi" ? limitEvent_1.parseNativeLimitEventFromTranscript : limitEvent_1.parseLimitEventFromTranscript;
80
80
  const parsed = parse(args.transcript);
81
81
  if (!parsed && !args.emitMinimalOnMiss)
82
82
  return false;