@codeam/shared 2.54.0

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.
package/dist/index.mjs ADDED
@@ -0,0 +1,631 @@
1
+ // src/protocol/constants.ts
2
+ var PROTOCOL_VERSION = "2.0.0";
3
+ var OBSERVER_BRIDGE_PORT = 47832;
4
+ var HEARTBEAT_INTERVAL_MS_DEFAULT = 3e4;
5
+ var SSE_SOCKET_TIMEOUT_MS = 35e3;
6
+
7
+ // src/protocol/renderToLines.ts
8
+ function renderToLines(raw) {
9
+ const screen = [""];
10
+ let row = 0;
11
+ let col = 0;
12
+ function ensureRow() {
13
+ while (screen.length <= row) screen.push("");
14
+ }
15
+ function writeChar(ch) {
16
+ ensureRow();
17
+ if (col < screen[row].length) {
18
+ screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);
19
+ } else {
20
+ while (screen[row].length < col) screen[row] += " ";
21
+ screen[row] += ch;
22
+ }
23
+ col++;
24
+ }
25
+ let i = 0;
26
+ while (i < raw.length) {
27
+ const ch = raw[i];
28
+ if (ch === "\x1B") {
29
+ i++;
30
+ if (i >= raw.length) break;
31
+ if (raw[i] === "[") {
32
+ i++;
33
+ let param = "";
34
+ while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];
35
+ const cmd = raw[i] ?? "";
36
+ const n = parseInt(param) || 1;
37
+ if (cmd === "A") {
38
+ row = Math.max(0, row - n);
39
+ } else if (cmd === "B") {
40
+ row += n;
41
+ ensureRow();
42
+ } else if (cmd === "C") {
43
+ col += n;
44
+ } else if (cmd === "D") {
45
+ col = Math.max(0, col - n);
46
+ } else if (cmd === "G") {
47
+ col = Math.max(0, n - 1);
48
+ } else if (cmd === "H" || cmd === "f") {
49
+ const p = param.split(";");
50
+ row = Math.max(0, (parseInt(p[0] ?? "1") || 1) - 1);
51
+ col = Math.max(0, (parseInt(p[1] ?? "1") || 1) - 1);
52
+ ensureRow();
53
+ } else if (cmd === "J") {
54
+ if (param === "2" || param === "3") {
55
+ screen.length = 1;
56
+ screen[0] = "";
57
+ row = 0;
58
+ col = 0;
59
+ } else if (param === "1") {
60
+ for (let r = 0; r < row; r++) screen[r] = "";
61
+ screen[row] = " ".repeat(col) + screen[row].slice(col);
62
+ } else {
63
+ screen[row] = screen[row].slice(0, col);
64
+ screen.splice(row + 1);
65
+ }
66
+ } else if (cmd === "K") {
67
+ ensureRow();
68
+ if (param === "" || param === "0") screen[row] = screen[row].slice(0, col);
69
+ else if (param === "1") screen[row] = " ".repeat(col) + screen[row].slice(col);
70
+ else if (param === "2") screen[row] = "";
71
+ } else if (cmd === "h" && (param === "?1049" || param === "?47")) {
72
+ screen.length = 1;
73
+ screen[0] = "";
74
+ row = 0;
75
+ col = 0;
76
+ } else if (cmd === "l" && (param === "?1049" || param === "?47")) {
77
+ screen.length = 1;
78
+ screen[0] = "";
79
+ row = 0;
80
+ col = 0;
81
+ }
82
+ } else if (raw[i] === "]") {
83
+ i++;
84
+ while (i < raw.length) {
85
+ if (raw[i] === "\x07") break;
86
+ if (raw[i] === "\x1B" && i + 1 < raw.length && raw[i + 1] === "\\") {
87
+ i++;
88
+ break;
89
+ }
90
+ i++;
91
+ }
92
+ }
93
+ } else if (ch === "\r") {
94
+ if (i + 1 < raw.length && raw[i + 1] === "\n") {
95
+ row++;
96
+ col = 0;
97
+ ensureRow();
98
+ i++;
99
+ } else {
100
+ col = 0;
101
+ }
102
+ } else if (ch === "\n") {
103
+ row++;
104
+ col = 0;
105
+ ensureRow();
106
+ } else if (ch >= " " || ch === " ") {
107
+ writeChar(ch);
108
+ }
109
+ i++;
110
+ }
111
+ return screen;
112
+ }
113
+
114
+ // src/protocol/remote-command.ts
115
+ import { z } from "zod";
116
+ var remoteCommandSchema = z.object({
117
+ id: z.string(),
118
+ sessionId: z.string(),
119
+ pluginId: z.string(),
120
+ type: z.string(),
121
+ // The backend may omit `payload` (or send null) for payload-less commands;
122
+ // clients have always normalized that to `{}` — keep that behavior here.
123
+ payload: z.record(z.string(), z.unknown()).nullish(),
124
+ status: z.string(),
125
+ createdAt: z.number()
126
+ });
127
+ function toRemoteCommand(raw) {
128
+ const parsed = remoteCommandSchema.safeParse(raw);
129
+ if (!parsed.success) return null;
130
+ const { payload, ...rest } = parsed.data;
131
+ return { ...rest, payload: payload ?? {} };
132
+ }
133
+
134
+ // src/models/pricing.ts
135
+ var MODEL_PRICING = {
136
+ // ── Anthropic / Claude ────────────────────────────────────
137
+ // The 4.x rows below cover the model ids actually emitted by the CLI
138
+ // (apps/cli/src/agents/claude/runtime.ts listModels) and the JetBrains
139
+ // fallback catalog (RemoteCommandRouter.kt). Prices are copied from the
140
+ // same-family base rows (claude-opus-4 / claude-sonnet-4 /
141
+ // claude-3-5-haiku) until distinct published rates land.
142
+ "claude-opus-4-7": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
143
+ "claude-opus-4-6": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
144
+ "claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
145
+ // Haiku-tier prices copied from claude-3-5-haiku (closest same-tier
146
+ // sibling in this table) — previously this id matched NO row and was
147
+ // silently billed at sonnet rates via the unknown-model fallback.
148
+ "claude-haiku-4-5": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
149
+ "claude-sonnet-4": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
150
+ "claude-opus-4": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
151
+ "claude-3-5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
152
+ "claude-3-5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
153
+ "claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 },
154
+ // ── Codex / OpenAI ────────────────────────────────────────
155
+ // Phase 2 placeholder pricing: 0 across the board until OpenAI publishes
156
+ // confirmed rates for the GPT-5.x catalog. Sync from
157
+ // developers.openai.com/pricing when available.
158
+ "gpt-5.5": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
159
+ "gpt-5.4": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
160
+ "gpt-5.4-mini": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
161
+ "gpt-5.3-codex": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
162
+ "gpt-5.2": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
163
+ "codex-auto-review": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
164
+ };
165
+ var MODEL_CONTEXT_WINDOW = {
166
+ // ── Anthropic / Claude ────────────────────────────────────
167
+ "claude-opus-4-7": 1e6,
168
+ "claude-opus-4-6": 1e6,
169
+ "claude-sonnet-4-6": 1e6,
170
+ "claude-haiku-4-5": 2e5,
171
+ "claude-opus-4": 1e6,
172
+ "claude-sonnet-4": 1e6,
173
+ "claude-3-5-sonnet": 2e5,
174
+ "claude-3-5-haiku": 2e5,
175
+ "claude-3-haiku": 2e5,
176
+ // ── Codex / OpenAI ────────────────────────────────────────
177
+ "gpt-5.5": 272e3,
178
+ "gpt-5.4": 272e3,
179
+ "gpt-5.4-mini": 272e3,
180
+ "gpt-5.3-codex": 272e3,
181
+ "gpt-5.2": 272e3,
182
+ "codex-auto-review": 272e3
183
+ };
184
+ var DEFAULT_CONTEXT_WINDOW = 2e5;
185
+ function longestPrefixMatch(table, model) {
186
+ let best;
187
+ let bestLen = -1;
188
+ for (const [prefix, value] of Object.entries(table)) {
189
+ if (prefix.length > bestLen && model.startsWith(prefix)) {
190
+ best = value;
191
+ bestLen = prefix.length;
192
+ }
193
+ }
194
+ return best;
195
+ }
196
+ function isKnownModel(model) {
197
+ return longestPrefixMatch(MODEL_PRICING, model) !== void 0;
198
+ }
199
+ function getPricing(model) {
200
+ return longestPrefixMatch(MODEL_PRICING, model) ?? MODEL_PRICING["claude-sonnet-4"];
201
+ }
202
+ function getContextWindow(model) {
203
+ if (!model) return DEFAULT_CONTEXT_WINDOW;
204
+ return longestPrefixMatch(MODEL_CONTEXT_WINDOW, model) ?? DEFAULT_CONTEXT_WINDOW;
205
+ }
206
+
207
+ // src/agents/registry.ts
208
+ var AGENT_REGISTRY = {
209
+ claude: {
210
+ id: "claude",
211
+ displayName: "Claude Code",
212
+ binaryName: "claude",
213
+ enabled: true,
214
+ // Mirrors the backend registry (codeagent-mobile
215
+ // apps/api-v2/src/codespaces/agent.ts — authoritative for auth
216
+ // capabilities). `setup_token` is the bare `sk-ant-oat01-…` from
217
+ // `claude setup-token` → delivered via CLAUDE_CODE_OAUTH_TOKEN.
218
+ supportedAuthKinds: ["setup_token", "oauth_token", "api_key"],
219
+ preferredAuthKind: "setup_token",
220
+ headroomWrappable: true,
221
+ headroomKind: "claude",
222
+ // npm adapter `@agentclientprotocol/claude-agent-acp`.
223
+ acp: true
224
+ },
225
+ codex: {
226
+ id: "codex",
227
+ displayName: "Codex CLI",
228
+ binaryName: "codex",
229
+ enabled: true,
230
+ supportedAuthKinds: ["oauth_token", "api_key"],
231
+ preferredAuthKind: "oauth_token",
232
+ headroomWrappable: true,
233
+ headroomKind: "codex",
234
+ // npm adapter `@agentclientprotocol/codex-acp`.
235
+ acp: true,
236
+ // OAuth device-code flow; the user_code on the OpenAI page IS a real
237
+ // human-typed code — surfaces render it (with a copy affordance).
238
+ deviceFlow: true,
239
+ showsUserCode: true
240
+ },
241
+ copilot: {
242
+ id: "copilot",
243
+ displayName: "GitHub Copilot CLI",
244
+ binaryName: "gh",
245
+ enabled: false,
246
+ supportedAuthKinds: ["oauth_token"],
247
+ preferredAuthKind: "oauth_token",
248
+ // `headroom init --global copilot` exists even though the agent is
249
+ // still disabled here (no runtime builder yet).
250
+ headroomWrappable: true,
251
+ headroomKind: "copilot",
252
+ acp: false
253
+ },
254
+ coderabbit: {
255
+ id: "coderabbit",
256
+ displayName: "CodeRabbit",
257
+ binaryName: "coderabbit",
258
+ enabled: true,
259
+ // Backend registry is authoritative: CodeRabbit links via a real
260
+ // API key only (no OAuth flow exists in api-v2).
261
+ supportedAuthKinds: ["api_key"],
262
+ preferredAuthKind: "api_key",
263
+ headroomWrappable: false,
264
+ // Legacy PTY runtime — no ACP adapter registered.
265
+ acp: false
266
+ },
267
+ cursor: {
268
+ id: "cursor",
269
+ displayName: "Cursor Agent",
270
+ binaryName: "cursor-agent",
271
+ enabled: true,
272
+ // Backend registry is authoritative: since the Cursor OAuth
273
+ // device-flow shipped, new links are oauth_token only (the login
274
+ // blob written to ~/.config/cursor/auth.json). Legacy vaulted
275
+ // api_key rows may still exist server-side, but the link surface
276
+ // no longer offers api_key.
277
+ supportedAuthKinds: ["oauth_token"],
278
+ preferredAuthKind: "oauth_token",
279
+ // `headroom wrap cursor` is "manual/print-only" (IDE settings; the
280
+ // headless cursor-agent CLI has no base-URL override) — runs native.
281
+ headroomWrappable: false,
282
+ // Native ACP server: `cursor-agent acp`.
283
+ acp: true,
284
+ // Reverse-engineered device/poll flow; `userCode` is the secret PKCE
285
+ // verifier echoed back on poll — NEVER human-facing.
286
+ deviceFlow: true,
287
+ showsUserCode: false
288
+ },
289
+ aider: {
290
+ id: "aider",
291
+ displayName: "Aider",
292
+ binaryName: "aider",
293
+ enabled: true,
294
+ // Aider is OAuth-less — auth is via ANTHROPIC_API_KEY / OPENAI_API_KEY
295
+ // / etc. env vars or `~/.aider.conf.yml`. The link flow surfaces
296
+ // this via the existing --api-key escape hatch in commands/link.ts.
297
+ supportedAuthKinds: ["api_key"],
298
+ preferredAuthKind: "api_key",
299
+ headroomWrappable: false,
300
+ // Legacy PTY runtime — no ACP adapter registered.
301
+ acp: false
302
+ },
303
+ gemini: {
304
+ id: "gemini",
305
+ displayName: "Gemini CLI",
306
+ binaryName: "gemini",
307
+ enabled: true,
308
+ // OAuth via `gemini auth login` (captured by `codeam link gemini`
309
+ // from ~/.gemini/oauth_creds.json) AND GEMINI_API_KEY are both
310
+ // accepted by the backend's GeminiProvisioningStrategy and propagated
311
+ // into codespace deploys.
312
+ supportedAuthKinds: ["oauth_token", "api_key"],
313
+ preferredAuthKind: "oauth_token",
314
+ // Not listed by `headroom wrap --help` — runs native.
315
+ headroomWrappable: false,
316
+ // Native ACP server: `gemini --skip-trust --acp`.
317
+ acp: true
318
+ }
319
+ };
320
+ function getEnabledAgents() {
321
+ return Object.values(AGENT_REGISTRY).filter((m) => m.enabled);
322
+ }
323
+ function getAgent(id) {
324
+ const meta = AGENT_REGISTRY[id];
325
+ if (!meta) throw new Error(`Unknown agent id: ${id}`);
326
+ return meta;
327
+ }
328
+ function isKnownAgentId(id) {
329
+ return id in AGENT_REGISTRY;
330
+ }
331
+
332
+ // src/agents/identity.ts
333
+ var HOUSE_AGENT_ID = "house-codeagent-cloud";
334
+ var HOUSE_AGENT_PROVIDER = "codeagent_cloud";
335
+ var HOUSE_AGENT_NAME = "CodeAgent Cloud";
336
+ var HOUSE_AGENT_VENDOR = "CodeAgent";
337
+ var HOUSE_AGENT_SUBTITLE = "Included \u2014 no setup";
338
+ var LINKED_AGENT_IDS = [
339
+ "claude_code",
340
+ "codex",
341
+ "cursor",
342
+ "aider",
343
+ "coderabbit",
344
+ "gemini",
345
+ HOUSE_AGENT_ID
346
+ ];
347
+ function isLinkedAgentId(value) {
348
+ return LINKED_AGENT_IDS.includes(value);
349
+ }
350
+ var PUBLIC_TO_INTERNAL = {
351
+ claude_code: "claude",
352
+ // CLI-side extra: self-hosted deploy payloads may carry the internal id.
353
+ claude: "claude",
354
+ codex: "codex",
355
+ // CLI-side extra: copilot has no public LinkedAgentId (backend doesn't
356
+ // expose it) but the self-hosted path accepts it.
357
+ copilot: "copilot",
358
+ cursor: "cursor",
359
+ aider: "aider",
360
+ coderabbit: "coderabbit",
361
+ gemini: "gemini",
362
+ // The house agent runs Claude Code under the hood (pointed at the
363
+ // MiniMax proxy). Its internal runtime is therefore `claude`.
364
+ [HOUSE_AGENT_ID]: "claude"
365
+ };
366
+ var INTERNAL_TO_PUBLIC = {
367
+ claude: "claude_code",
368
+ codex: "codex",
369
+ cursor: "cursor",
370
+ aider: "aider",
371
+ coderabbit: "coderabbit",
372
+ gemini: "gemini"
373
+ };
374
+ function isPublicToInternalKey(v) {
375
+ return Object.prototype.hasOwnProperty.call(PUBLIC_TO_INTERNAL, v);
376
+ }
377
+ function publicToInternal(publicId) {
378
+ return isPublicToInternalKey(publicId) ? PUBLIC_TO_INTERNAL[publicId] : null;
379
+ }
380
+ function internalToPublic(internal) {
381
+ return INTERNAL_TO_PUBLIC[internal] ?? null;
382
+ }
383
+ var TERMINAL_AGENT_PREFIX = "__terminal__:";
384
+ var AGENT_ID_ALIASES = {
385
+ claude_code: "claude",
386
+ "claude-code": "claude",
387
+ "anthropic.claude-code": "claude",
388
+ "anthropics.claude": "claude",
389
+ "anthropic.claude-ce": "claude",
390
+ "anthropic.claude": "claude",
391
+ "com.anthropic.claudecode": "claude",
392
+ "com.anthropic.claude": "claude",
393
+ "openai.chatgpt": "codex",
394
+ "coderabbitai.coderabbit-vscode": "coderabbit"
395
+ };
396
+ function normalizeAgentId(raw) {
397
+ const value = (raw ?? "").trim().toLowerCase();
398
+ if (!value) return null;
399
+ if (isKnownAgentId(value)) return value;
400
+ const unprefixed = value.startsWith(TERMINAL_AGENT_PREFIX) ? value.slice(TERMINAL_AGENT_PREFIX.length) : value;
401
+ if (isKnownAgentId(unprefixed)) return unprefixed;
402
+ return AGENT_ID_ALIASES[unprefixed] ?? null;
403
+ }
404
+ function headroomKindFor(agentId) {
405
+ const normalized = (agentId ?? "").toLowerCase().replace(/[_-]/g, "");
406
+ if (!normalized) return null;
407
+ for (const meta of Object.values(AGENT_REGISTRY)) {
408
+ if (meta.headroomKind !== void 0 && normalized.startsWith(meta.id)) {
409
+ return meta.headroomKind;
410
+ }
411
+ }
412
+ return null;
413
+ }
414
+ function isHeadroomWrappable(agentId) {
415
+ return headroomKindFor(agentId) !== null;
416
+ }
417
+
418
+ // src/api-url.ts
419
+ var DEFAULT_API_BASE_URL = "https://api.codeagent-mobile.com";
420
+ var DEV_API_BASE_URL = "https://dev-api.codeagent-mobile.com";
421
+ function resolveApiBaseUrl() {
422
+ const env = globalThis.process?.env;
423
+ const explicit = env?.CODEAM_API_URL?.trim();
424
+ if (explicit) return explicit;
425
+ const testFlag = env?.CODEAM_TEST_MODE?.trim();
426
+ if (testFlag === "1" || testFlag?.toLowerCase() === "true") return DEV_API_BASE_URL;
427
+ return DEFAULT_API_BASE_URL;
428
+ }
429
+
430
+ // src/headroom/manifest.ts
431
+ var HEADROOM_PROXY_PORT = 8787;
432
+ var HEADROOM_BACKEND_ENV = {
433
+ HEADROOM_KOMPRESS_BACKEND: "onnx_cpu"
434
+ };
435
+ var HEADROOM_PIP_COMPANIONS = [
436
+ "fastapi",
437
+ "uvicorn",
438
+ "httpx[http2]",
439
+ "websockets",
440
+ "zstandard"
441
+ ];
442
+ var HEADROOM_EXTRAS_BY_SURFACE = {
443
+ codespace: ["proxy", "code"],
444
+ selfHosted: ["proxy", "code"],
445
+ onDemand: ["proxy", "code", "image"]
446
+ };
447
+ function headroomPipPackage(extras) {
448
+ return `headroom-ai[${extras.join(",")}]`;
449
+ }
450
+ var HEADROOM_MODELS = [
451
+ {
452
+ repo: "chopratejas/kompress-v2-base",
453
+ allowPatterns: ["*.json", "onnx/*.onnx", "kompress-int8-wo.onnx"]
454
+ },
455
+ {
456
+ repo: "answerdotai/ModernBERT-base",
457
+ allowPatterns: ["*.json", "tokenizer*", "*.txt", "vocab*", "merges*"]
458
+ }
459
+ ];
460
+ function headroomSnapshotDownloadLine(model, opts = {}) {
461
+ const sep = opts.spaceAfterComma ? ", " : ",";
462
+ const patterns = model.allowPatterns.map((p) => `"${p}"`).join(sep);
463
+ return `snapshot_download("${model.repo}", allow_patterns=[${patterns}])`;
464
+ }
465
+ function headroomModelPredownloadScript(opts = {}) {
466
+ return [
467
+ "from huggingface_hub import snapshot_download",
468
+ ...HEADROOM_MODELS.map((m) => headroomSnapshotDownloadLine(m, opts))
469
+ ].join("\n");
470
+ }
471
+
472
+ // src/types/events.ts
473
+ var USER_EVENTS = {
474
+ PAIRED_SESSION_STATUS: "paired_session_status",
475
+ PAIRED_SESSION_ADDED: "paired_session_added",
476
+ PAIRED_SESSION_REMOVED: "paired_session_removed",
477
+ PAIRED_SESSION_BRANCH_CHANGED: "paired_session_branch_changed",
478
+ SHARED_WITH_ME_ADDED: "shared_with_me_added",
479
+ SHARED_WITH_ME_REVOKED: "shared_with_me_revoked",
480
+ USAGE_CHANGED: "usage_changed",
481
+ TASK_DONE: "task_done",
482
+ HUNK_PENDING_REVIEW_ADDED: "hunk_pending_review_added",
483
+ HUNK_REVIEW_RESOLVED: "hunk_review_resolved",
484
+ FILE_CHANGED: "file_changed",
485
+ FILES_BATCH_CHANGED: "files_batch_changed",
486
+ AGENT_STREAMING_CHUNK: "agent_streaming_chunk",
487
+ AGENT_AWAITING_ANSWER: "agent_awaiting_answer",
488
+ AWAITING_INPUT_ADDED: "awaiting_input_added",
489
+ AGENT_ANSWER_RESOLVED: "agent_answer_resolved",
490
+ TEMPLATE_ADDED: "template_added",
491
+ TEMPLATE_REMOVED: "template_removed",
492
+ TEMPLATE_UPDATED: "template_updated",
493
+ AGENT_TASK_DISPATCHED: "agent_task_dispatched",
494
+ AGENT_TASK_COMPLETED: "agent_task_completed",
495
+ LINKED_AGENT_ADDED: "linked_agent_added",
496
+ QUOTA_REACHED: "quota_reached",
497
+ LINKED_AGENT_LINK_FAILED: "linked_agent_link_failed",
498
+ CODESPACE_AGENT_INSTALLED: "codespace_agent_installed",
499
+ AGENT_CREDENTIALS_REFRESHED: "agent_credentials_refreshed",
500
+ CREDENTIAL_INVALID: "credential_invalid",
501
+ CODESPACE_WAKING: "codespace_waking",
502
+ CODESPACE_BILLING_BLOCKED: "codespace_billing_blocked",
503
+ COST_SAVING_UPDATED: "cost_saving_updated",
504
+ COMMAND_COMPLETED: "command_completed",
505
+ AI_SUMMARY_PENDING: "ai_summary_pending",
506
+ AI_SUMMARY_READY: "ai_summary_ready",
507
+ AI_INSIGHT_PENDING: "ai_insight_pending",
508
+ AI_INSIGHT_READY: "ai_insight_ready",
509
+ PUSH_TOKEN_INVALIDATED: "push_token_invalidated",
510
+ PREVIEW_DETECTION_PENDING: "preview_detection_pending",
511
+ PREVIEW_DETECTION_READY: "preview_detection_ready",
512
+ PREVIEW_STARTING: "preview_starting",
513
+ PREVIEW_READY: "preview_ready",
514
+ PREVIEW_STOPPED: "preview_stopped",
515
+ PREVIEW_ERROR: "preview_error",
516
+ PREVIEW_PROGRESS: "preview_progress",
517
+ BEADS_STATE_CHANGED: "beads_state_changed",
518
+ BEADS_PROVISIONING: "beads_provisioning",
519
+ BEADS_TEAM_MEMORY_CHANGED: "beads_team_memory_changed",
520
+ AUDIT_EVENT_ADDED: "audit_event_added",
521
+ SELF_HOSTED_HOST_ADDED: "self_hosted_host_added",
522
+ SELF_HOSTED_HOST_STATUS: "self_hosted_host_status",
523
+ SELF_HOSTED_HOST_REMOVED: "self_hosted_host_removed",
524
+ SELF_HOSTED_HOST_TELEMETRY: "self_hosted_host_telemetry",
525
+ SELF_HOSTED_HOST_METRICS: "self_hosted_host_metrics",
526
+ SELF_HOSTED_HOST_SESSIONS: "self_hosted_host_sessions",
527
+ SELF_HOSTED_DEPLOY_PROGRESS: "self_hosted_deploy_progress",
528
+ REFERRAL_REWARD_EARNED: "referral_reward_earned",
529
+ HEADROOM_PROGRESS: "headroom_progress",
530
+ HEADROOM_STATUS: "headroom_status",
531
+ BEADS_STATUS: "beads_status",
532
+ LINKED_AGENT_HEADROOM_BUDGET_UPDATED: "linked_agent_headroom_budget_updated",
533
+ CLI_UPDATE_AVAILABLE: "cli_update_available",
534
+ AGENT_INSTALL_PROGRESS: "agent_install_progress",
535
+ AGENT_INSTALL_FAILED: "agent_install_failed",
536
+ CLI_UPDATE_PROGRESS: "cli_update_progress",
537
+ CLI_UPDATE_FAILED: "cli_update_failed"
538
+ };
539
+
540
+ // src/preview-prompts.ts
541
+ var PREVIEW_DETECT_PROMPT = `
542
+ Analyze the project in the current working directory and return how to start
543
+ its development server for in-app preview.
544
+
545
+ Read package.json, Procfile, Dockerfile, docker-compose.yml, manage.py, app.json,
546
+ mix.exs, Cargo.toml, go.mod, requirements.txt, Gemfile, and any other framework
547
+ markers you find at depth <= 2.
548
+
549
+ Return ONLY a JSON object on stdout (no prose, no markdown fences):
550
+
551
+ {
552
+ "framework": "<name, or 'unsupported'>",
553
+ "command": "<executable>",
554
+ "args": ["..."],
555
+ "port": <number>,
556
+ "ready_pattern": "<regex matching the server-ready stdout line>",
557
+ "env": { "HOST": "0.0.0.0" },
558
+ "setup_commands": [{ "cmd": "<executable>", "args": ["..."] }],
559
+ "notes": "<one-line caveat or null>"
560
+ }
561
+
562
+ Rules:
563
+ - Pick the script the developer would run locally to see the app (typically "dev", "start", "serve").
564
+ - Prefer binding to 0.0.0.0 \u2014 most frameworks default to localhost which the tunnel cannot reach.
565
+ - For Expo: framework="Expo", command="npx", args=["expo","start","--tunnel"], port=8081, notes="Scan QR with Expo Go".
566
+ - If no dev server applies (CLI library, lambda, batch script): {"framework":"unsupported","notes":"<reason>"}.
567
+
568
+ CRITICAL \u2014 setup_commands:
569
+ - DO NOT include an install command (npm install, pnpm install, yarn install,
570
+ yarn, bun install) in setup_commands. A lockfile-aware pre-flight installer
571
+ runs BEFORE setup_commands and picks the correct package manager from the
572
+ lockfile present (pnpm-lock.yaml -> pnpm, yarn.lock -> yarn, bun.lockb -> bun,
573
+ else npm). Emitting an install here either duplicates that work or, worse,
574
+ uses the WRONG package manager on top of node_modules just populated by the
575
+ pre-flight, which crashes (e.g. npm errors with "Cannot read properties of
576
+ null (reading 'matches')" when run over pnpm's .pnpm/ layout).
577
+ - ONLY include setup_commands for genuinely non-install work the project needs
578
+ before its dev server can boot: prisma generate, codegen, prebuild scripts,
579
+ database migrations against a local SQLite, etc.
580
+ - Each setup_commands entry MUST be an object {"cmd": "...", "args": ["..."]} \u2014
581
+ e.g. {"cmd": "npx", "args": ["prisma", "generate"]}. NOT a bare string.
582
+ - For most projects, setup_commands should be an empty array [].
583
+
584
+ OUTPUT JSON ONLY. NO MARKDOWN. NO COMMENTARY.
585
+ `.trim();
586
+ export {
587
+ AGENT_REGISTRY,
588
+ DEFAULT_API_BASE_URL,
589
+ DEV_API_BASE_URL,
590
+ HEADROOM_BACKEND_ENV,
591
+ HEADROOM_EXTRAS_BY_SURFACE,
592
+ HEADROOM_MODELS,
593
+ HEADROOM_PIP_COMPANIONS,
594
+ HEADROOM_PROXY_PORT,
595
+ HEARTBEAT_INTERVAL_MS_DEFAULT,
596
+ HOUSE_AGENT_ID,
597
+ HOUSE_AGENT_NAME,
598
+ HOUSE_AGENT_PROVIDER,
599
+ HOUSE_AGENT_SUBTITLE,
600
+ HOUSE_AGENT_VENDOR,
601
+ INTERNAL_TO_PUBLIC,
602
+ LINKED_AGENT_IDS,
603
+ MODEL_CONTEXT_WINDOW,
604
+ MODEL_PRICING,
605
+ OBSERVER_BRIDGE_PORT,
606
+ PREVIEW_DETECT_PROMPT,
607
+ PROTOCOL_VERSION,
608
+ PUBLIC_TO_INTERNAL,
609
+ SSE_SOCKET_TIMEOUT_MS,
610
+ TERMINAL_AGENT_PREFIX,
611
+ USER_EVENTS,
612
+ getAgent,
613
+ getContextWindow,
614
+ getEnabledAgents,
615
+ getPricing,
616
+ headroomKindFor,
617
+ headroomModelPredownloadScript,
618
+ headroomPipPackage,
619
+ headroomSnapshotDownloadLine,
620
+ internalToPublic,
621
+ isHeadroomWrappable,
622
+ isKnownAgentId,
623
+ isKnownModel,
624
+ isLinkedAgentId,
625
+ normalizeAgentId,
626
+ publicToInternal,
627
+ renderToLines,
628
+ resolveApiBaseUrl,
629
+ toRemoteCommand
630
+ };
631
+ //# sourceMappingURL=index.mjs.map