@adhdev/daemon-standalone 0.9.82-rc.167 → 0.9.82-rc.169

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.js CHANGED
@@ -7700,6 +7700,7 @@ var require_dist2 = __commonJS({
7700
7700
  "acquire_write",
7701
7701
  "release_write",
7702
7702
  "get_snapshot",
7703
+ "get_terminal_snapshot",
7703
7704
  "clear_session_buffer",
7704
7705
  "update_session_meta",
7705
7706
  "get_host_diagnostics",
@@ -44755,6 +44756,10 @@ ${lastSnapshot}`;
44755
44756
  "type": "integer",
44756
44757
  "minimum": 1,
44757
44758
  "default": 2
44759
+ },
44760
+ "continuation_lines": {
44761
+ "type": "boolean",
44762
+ "default": false
44758
44763
  }
44759
44764
  }
44760
44765
  }
@@ -46907,6 +46912,7 @@ ${lastSnapshot}`;
46907
46912
  ProviderCliAdapter: () => ProviderCliAdapter,
46908
46913
  ProviderInstanceManager: () => ProviderInstanceManager,
46909
46914
  ProviderLoader: () => ProviderLoader,
46915
+ RawTerminalAttachment: () => RawTerminalAttachment,
46910
46916
  STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS2,
46911
46917
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory2,
46912
46918
  SpecDriver: () => SpecDriver,
@@ -47057,6 +47063,8 @@ ${lastSnapshot}`;
47057
47063
  markSetupComplete: () => markSetupComplete,
47058
47064
  markStaleDirectDispatches: () => markStaleDirectDispatches,
47059
47065
  maybeRunDaemonUpgradeHelperFromEnv: () => maybeRunDaemonUpgradeHelperFromEnv2,
47066
+ namedKeyToAnsi: () => namedKeyToAnsi,
47067
+ namedKeysToAnsi: () => namedKeysToAnsi,
47060
47068
  normalizeActiveChatData: () => normalizeActiveChatData,
47061
47069
  normalizeChatMessage: () => normalizeChatMessage,
47062
47070
  normalizeChatMessageKind: () => normalizeChatMessageKind,
@@ -47137,7 +47145,8 @@ ${lastSnapshot}`;
47137
47145
  validateCliProviderManifest: () => validateCliProviderManifest,
47138
47146
  validateMeshRefineConfig: () => validateMeshRefineConfig,
47139
47147
  validateMeshTaskModeRequest: () => validateMeshTaskModeRequest,
47140
- validateMeshWorktreeBootstrapConfig: () => validateMeshWorktreeBootstrapConfig
47148
+ validateMeshWorktreeBootstrapConfig: () => validateMeshWorktreeBootstrapConfig,
47149
+ withRawTerminalAttachment: () => withRawTerminalAttachment2
47141
47150
  });
47142
47151
  module2.exports = __toCommonJS2(index_exports);
47143
47152
  init_repo_mesh_types();
@@ -54850,6 +54859,11 @@ ${effect.notification.body || ""}`.trim();
54850
54859
  }
54851
54860
  return { index: -1, label: "" };
54852
54861
  }
54862
+ function pickAutoApprovalButton(buttons) {
54863
+ const labels = (buttons || []).map((button) => String(button || "").trim());
54864
+ const index = labels.findIndex(Boolean);
54865
+ return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
54866
+ }
54853
54867
  function formatAutoApprovalMessage(modalMessage, buttonLabel) {
54854
54868
  const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
54855
54869
  const cleanMessage = String(modalMessage || "").trim();
@@ -55422,7 +55436,7 @@ ${effect.notification.body || ""}`.trim();
55422
55436
  }
55423
55437
  this.autoApproveBusy = true;
55424
55438
  try {
55425
- const { label: targetButton } = pickApprovalButton(_chatData?.activeModal?.buttons, this.provider);
55439
+ const { label: targetButton } = pickAutoApprovalButton(_chatData?.activeModal?.buttons);
55426
55440
  const script = scriptFn({ action: "approve", button: targetButton, buttonText: targetButton });
55427
55441
  if (!script) return;
55428
55442
  const now = Date.now();
@@ -61826,6 +61840,10 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61826
61840
  const flags = ref.flags ?? "gm";
61827
61841
  return new RegExp(ref.pattern, flags.includes("g") ? flags : flags + "g");
61828
61842
  }
61843
+ function compileLinePattern(ref) {
61844
+ const flags = (ref.flags ?? "m").replace(/g/g, "");
61845
+ return new RegExp(ref.pattern, flags);
61846
+ }
61829
61847
  function matchState(state, sections, fullScreen, trace) {
61830
61848
  const haystack = sectionText(sections, state.when.section, fullScreen);
61831
61849
  const re = compileRegex(state.when);
@@ -61846,16 +61864,41 @@ ${formatManifestValidationIssues2(validation.issues)}`,
61846
61864
  function extractModal(state, sections, fullScreen, title, trace) {
61847
61865
  if (!state.modal_buttons) return null;
61848
61866
  const hay = sectionText(sections, state.modal_buttons.section, fullScreen);
61849
- const re = compilePattern(state.modal_buttons);
61850
61867
  const buttons = [];
61851
- let m;
61852
- while ((m = re.exec(hay)) !== null) {
61853
- const idx = Number(m[1]);
61854
- const label = String(m[2] ?? "").trim();
61855
- if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
61856
- if (buttons.some((b) => b.index === idx)) continue;
61857
- const key = state.modal_buttons.key_for_index.replace(/\{index\}/g, String(idx));
61858
- buttons.push({ index: idx, label, key });
61868
+ if (state.modal_buttons.continuation_lines) {
61869
+ const re = compileLinePattern(state.modal_buttons);
61870
+ const lines = hay.split("\n");
61871
+ for (let i = 0; i < lines.length; i += 1) {
61872
+ const m = re.exec(lines[i]);
61873
+ if (!m) continue;
61874
+ const idx = Number(m[1]);
61875
+ let label = String(m[2] ?? "").trim();
61876
+ if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
61877
+ let j = i + 1;
61878
+ while (j < lines.length) {
61879
+ const next = lines[j];
61880
+ if (!next.trim()) break;
61881
+ if (re.test(next)) break;
61882
+ if (!/^\s+/.test(next)) break;
61883
+ label += " " + next.trim();
61884
+ j += 1;
61885
+ }
61886
+ if (buttons.some((b) => b.index === idx)) continue;
61887
+ const key = state.modal_buttons.key_for_index.replace(/\{index\}/g, String(idx));
61888
+ buttons.push({ index: idx, label, key });
61889
+ i = j - 1;
61890
+ }
61891
+ } else {
61892
+ const re = compilePattern(state.modal_buttons);
61893
+ let m;
61894
+ while ((m = re.exec(hay)) !== null) {
61895
+ const idx = Number(m[1]);
61896
+ const label = String(m[2] ?? "").trim();
61897
+ if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
61898
+ if (buttons.some((b) => b.index === idx)) continue;
61899
+ const key = state.modal_buttons.key_for_index.replace(/\{index\}/g, String(idx));
61900
+ buttons.push({ index: idx, label, key });
61901
+ }
61859
61902
  }
61860
61903
  buttons.sort((a, b) => a.index - b.index);
61861
61904
  const minCount = state.modal_buttons.min_count ?? 2;
@@ -63351,7 +63394,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
63351
63394
  if (!modal || buttons.length === 0) {
63352
63395
  return autoApproveActive;
63353
63396
  }
63354
- const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(buttons, this.provider);
63397
+ const { index: buttonIndex, label: buttonLabel } = pickAutoApprovalButton(buttons);
63355
63398
  if (buttonIndex < 0) {
63356
63399
  return autoApproveActive;
63357
63400
  }
@@ -73995,9 +74038,9 @@ ${e?.stderr || ""}`
73995
74038
  });
73996
74039
  let node;
73997
74040
  if (meshRecord.inline) {
73998
- const { randomUUID: randomUUID10 } = await import("crypto");
74041
+ const { randomUUID: randomUUID11 } = await import("crypto");
73999
74042
  node = {
74000
- id: `node_${randomUUID10().replace(/-/g, "")}`,
74043
+ id: `node_${randomUUID11().replace(/-/g, "")}`,
74001
74044
  workspace: result.worktreePath,
74002
74045
  repoRoot: result.worktreePath,
74003
74046
  daemonId: sourceNode.daemonId,
@@ -76217,8 +76260,7 @@ ${ptyResult.output.slice(-2e3)}`);
76217
76260
  if (stream?.status === "waiting_approval") {
76218
76261
  const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
76219
76262
  if (autoApprove && resolvedActiveSessionId) {
76220
- const provider = providerLoader.getMeta(stream.agentType);
76221
- const { label: buttonLabel } = pickApprovalButton(stream.activeModal?.buttons, provider);
76263
+ const { label: buttonLabel } = pickAutoApprovalButton(stream.activeModal?.buttons);
76222
76264
  const approved = await agentStreamManager.resolveSessionAction(cdp, resolvedActiveSessionId, "approve", buttonLabel);
76223
76265
  if (approved) {
76224
76266
  const effectId = [
@@ -82310,6 +82352,199 @@ data: ${JSON.stringify(msg.data)}
82310
82352
  });
82311
82353
  }
82312
82354
  };
82355
+ var import_crypto6 = require("crypto");
82356
+ var import_session_host_core4 = require_dist2();
82357
+ var BASE_KEY_SEQUENCES = {
82358
+ enter: "\r",
82359
+ escape: "\x1B",
82360
+ tab: " ",
82361
+ backspace: "\x7F",
82362
+ up: "\x1B[A",
82363
+ down: "\x1B[B",
82364
+ right: "\x1B[C",
82365
+ left: "\x1B[D",
82366
+ home: "\x1B[H",
82367
+ end: "\x1B[F",
82368
+ pageup: "\x1B[5~",
82369
+ pagedown: "\x1B[6~",
82370
+ space: " ",
82371
+ f1: "\x1BOP",
82372
+ f2: "\x1BOQ",
82373
+ f3: "\x1BOR",
82374
+ f4: "\x1BOS",
82375
+ f5: "\x1B[15~",
82376
+ f6: "\x1B[17~",
82377
+ f7: "\x1B[18~",
82378
+ f8: "\x1B[19~",
82379
+ f9: "\x1B[20~",
82380
+ f10: "\x1B[21~",
82381
+ f11: "\x1B[23~",
82382
+ f12: "\x1B[24~"
82383
+ };
82384
+ var SHIFTED_CSI_KEYS = {
82385
+ up: "\x1B[1;2A",
82386
+ down: "\x1B[1;2B",
82387
+ right: "\x1B[1;2C",
82388
+ left: "\x1B[1;2D",
82389
+ home: "\x1B[1;2H",
82390
+ end: "\x1B[1;2F",
82391
+ pageup: "\x1B[5;2~",
82392
+ pagedown: "\x1B[6;2~",
82393
+ f1: "\x1B[1;2P",
82394
+ f2: "\x1B[1;2Q",
82395
+ f3: "\x1B[1;2R",
82396
+ f4: "\x1B[1;2S",
82397
+ f5: "\x1B[15;2~",
82398
+ f6: "\x1B[17;2~",
82399
+ f7: "\x1B[18;2~",
82400
+ f8: "\x1B[19;2~",
82401
+ f9: "\x1B[20;2~",
82402
+ f10: "\x1B[21;2~",
82403
+ f11: "\x1B[23;2~",
82404
+ f12: "\x1B[24;2~"
82405
+ };
82406
+ function isLowercaseLetter(value) {
82407
+ return /^[a-z]$/.test(value);
82408
+ }
82409
+ function encodeControlLetter(letter) {
82410
+ return String.fromCharCode(letter.charCodeAt(0) - 96);
82411
+ }
82412
+ function encodeShiftedKey(key) {
82413
+ if (isLowercaseLetter(key)) return key.toUpperCase();
82414
+ if (key.startsWith("ctrl+") && isLowercaseLetter(key.slice(5))) {
82415
+ return encodeControlLetter(key.slice(5));
82416
+ }
82417
+ if (key.startsWith("alt+") && isLowercaseLetter(key.slice(4))) {
82418
+ return `\x1B${key.slice(4).toUpperCase()}`;
82419
+ }
82420
+ if (key === "tab") return "\x1B[Z";
82421
+ if (key in SHIFTED_CSI_KEYS) return SHIFTED_CSI_KEYS[key];
82422
+ if (key in BASE_KEY_SEQUENCES) return BASE_KEY_SEQUENCES[key];
82423
+ throw new Error(`Unsupported named key: shift+${key}`);
82424
+ }
82425
+ function namedKeyToAnsi(key) {
82426
+ const normalized = String(key || "").trim().toLowerCase();
82427
+ if (normalized in BASE_KEY_SEQUENCES) return BASE_KEY_SEQUENCES[normalized];
82428
+ if (normalized.startsWith("ctrl+") && isLowercaseLetter(normalized.slice(5))) {
82429
+ return encodeControlLetter(normalized.slice(5));
82430
+ }
82431
+ if (normalized.startsWith("alt+") && isLowercaseLetter(normalized.slice(4))) {
82432
+ return `\x1B${normalized.slice(4)}`;
82433
+ }
82434
+ if (normalized.startsWith("shift+")) return encodeShiftedKey(normalized.slice(6));
82435
+ throw new Error(`Unsupported named key: ${key}`);
82436
+ }
82437
+ function namedKeysToAnsi(keys) {
82438
+ if (!Array.isArray(keys)) throw new Error("keys must be an array");
82439
+ return keys.map(namedKeyToAnsi).join("");
82440
+ }
82441
+ var RawTerminalAttachment = class _RawTerminalAttachment {
82442
+ constructor(sessionId, clientId, mode, client) {
82443
+ this.sessionId = sessionId;
82444
+ this.clientId = clientId;
82445
+ this.mode = mode;
82446
+ this.client = client;
82447
+ }
82448
+ closed = false;
82449
+ static async attach(options) {
82450
+ const sessionId = String(options.sessionId || "").trim();
82451
+ if (!sessionId) throw new Error("sessionId is required");
82452
+ const mode = options.mode || "read";
82453
+ const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto6.randomUUID)().slice(0, 8)}`;
82454
+ const client = options.client || new import_session_host_core4.SessionHostClient({ endpoint: options.endpoint });
82455
+ await client.connect();
82456
+ const attachResponse = await client.request({
82457
+ type: "attach_session",
82458
+ payload: {
82459
+ sessionId,
82460
+ clientId,
82461
+ clientType: "web",
82462
+ readOnly: mode === "read"
82463
+ }
82464
+ });
82465
+ if (!attachResponse.success) {
82466
+ await client.close().catch(() => {
82467
+ });
82468
+ throw new Error(attachResponse.error || `Failed to attach terminal session ${sessionId}`);
82469
+ }
82470
+ if (mode === "write") {
82471
+ const ownerResponse = await client.request({
82472
+ type: "acquire_write",
82473
+ payload: {
82474
+ sessionId,
82475
+ clientId,
82476
+ ownerType: "user",
82477
+ force: true
82478
+ }
82479
+ });
82480
+ if (!ownerResponse.success) {
82481
+ await client.request({
82482
+ type: "detach_session",
82483
+ payload: { sessionId, clientId }
82484
+ }).catch(() => ({ success: false }));
82485
+ await client.close().catch(() => {
82486
+ });
82487
+ throw new Error(ownerResponse.error || `Failed to acquire terminal session ${sessionId}`);
82488
+ }
82489
+ }
82490
+ return new _RawTerminalAttachment(sessionId, clientId, mode, client);
82491
+ }
82492
+ async readSnapshot() {
82493
+ const response = await this.client.request({
82494
+ type: "get_terminal_snapshot",
82495
+ payload: { sessionId: this.sessionId }
82496
+ });
82497
+ if (!response.success || !response.result) {
82498
+ throw new Error(response.error || `Terminal screen unavailable for ${this.sessionId}`);
82499
+ }
82500
+ return response.result;
82501
+ }
82502
+ async readScreenText() {
82503
+ return (await this.readSnapshot()).text;
82504
+ }
82505
+ async readState() {
82506
+ return (await this.readSnapshot()).state;
82507
+ }
82508
+ async writeInput(text) {
82509
+ if (this.mode !== "write") throw new Error("Raw terminal attachment is read-only");
82510
+ const response = await this.client.request({
82511
+ type: "send_input",
82512
+ payload: {
82513
+ sessionId: this.sessionId,
82514
+ clientId: this.clientId,
82515
+ data: text
82516
+ }
82517
+ });
82518
+ if (!response.success) throw new Error(response.error || `Failed to write terminal input to ${this.sessionId}`);
82519
+ }
82520
+ async writeKeys(keys) {
82521
+ await this.writeInput(namedKeysToAnsi(keys));
82522
+ }
82523
+ async close() {
82524
+ if (this.closed) return;
82525
+ this.closed = true;
82526
+ if (this.mode === "write") {
82527
+ await this.client.request({
82528
+ type: "release_write",
82529
+ payload: { sessionId: this.sessionId, clientId: this.clientId }
82530
+ }).catch(() => ({ success: false }));
82531
+ }
82532
+ await this.client.request({
82533
+ type: "detach_session",
82534
+ payload: { sessionId: this.sessionId, clientId: this.clientId }
82535
+ }).catch(() => ({ success: false }));
82536
+ await this.client.close().catch(() => {
82537
+ });
82538
+ }
82539
+ };
82540
+ async function withRawTerminalAttachment2(options, operation) {
82541
+ const attachment = await RawTerminalAttachment.attach(options);
82542
+ try {
82543
+ return await operation(attachment);
82544
+ } finally {
82545
+ await attachment.close();
82546
+ }
82547
+ }
82313
82548
  var DEFAULT_SESSION_HOST_APP_NAME = "adhdev";
82314
82549
  var DEFAULT_STANDALONE_SESSION_HOST_APP_NAME = "adhdev-standalone";
82315
82550
  function getReservedStandaloneNamespaceWarning() {
@@ -82339,7 +82574,7 @@ data: ${JSON.stringify(msg.data)}
82339
82574
  function resolveSessionHostAppName(options = {}) {
82340
82575
  return resolveSessionHostAppNameResolution2(options).appName;
82341
82576
  }
82342
- var import_session_host_core4 = require_dist2();
82577
+ var import_session_host_core5 = require_dist2();
82343
82578
  var STARTUP_TIMEOUT_MS = DEFAULT_SESSION_HOST_READY_TIMEOUT_MS2;
82344
82579
  var STARTUP_POLL_MS = 200;
82345
82580
  var SessionHostCompatibilityError = class extends Error {
@@ -82367,7 +82602,7 @@ data: ${JSON.stringify(msg.data)}
82367
82602
  }
82368
82603
  }
82369
82604
  async function canConnect(endpoint, requiredRequestTypes = []) {
82370
- const client = new import_session_host_core4.SessionHostClient({ endpoint });
82605
+ const client = new import_session_host_core5.SessionHostClient({ endpoint });
82371
82606
  try {
82372
82607
  await client.connect();
82373
82608
  await assertRequiredRequestTypes(client, requiredRequestTypes);
@@ -82389,7 +82624,7 @@ data: ${JSON.stringify(msg.data)}
82389
82624
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
82390
82625
  }
82391
82626
  async function ensureSessionHostReady2(options) {
82392
- const endpoint = (0, import_session_host_core4.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
82627
+ const endpoint = (0, import_session_host_core5.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
82393
82628
  const requiredRequestTypes = options.requiredRequestTypes || [];
82394
82629
  if (await canConnect(endpoint, requiredRequestTypes)) return endpoint;
82395
82630
  options.spawnHost();
@@ -82397,7 +82632,7 @@ data: ${JSON.stringify(msg.data)}
82397
82632
  return endpoint;
82398
82633
  }
82399
82634
  async function listHostedCliRuntimes2(endpoint) {
82400
- const client = new import_session_host_core4.SessionHostClient({ endpoint });
82635
+ const client = new import_session_host_core5.SessionHostClient({ endpoint });
82401
82636
  try {
82402
82637
  const response = await client.request({
82403
82638
  type: "list_sessions",
@@ -83396,7 +83631,7 @@ async function ensureSessionHostReady() {
83396
83631
  appName: SESSION_HOST_APP_NAME,
83397
83632
  spawnHost,
83398
83633
  timeoutMs: SESSION_HOST_START_TIMEOUT_MS,
83399
- requiredRequestTypes: ["delete_session"]
83634
+ requiredRequestTypes: ["delete_session", "get_terminal_snapshot"]
83400
83635
  });
83401
83636
  } catch (error48) {
83402
83637
  stopSessionHost();
@@ -83404,7 +83639,7 @@ async function ensureSessionHostReady() {
83404
83639
  appName: SESSION_HOST_APP_NAME,
83405
83640
  spawnHost,
83406
83641
  timeoutMs: SESSION_HOST_START_TIMEOUT_MS,
83407
- requiredRequestTypes: ["delete_session"]
83642
+ requiredRequestTypes: ["delete_session", "get_terminal_snapshot"]
83408
83643
  }).catch((retryError) => {
83409
83644
  const initialMessage = error48 instanceof Error ? error48.message : String(error48);
83410
83645
  const retryMessage = retryError instanceof Error ? retryError.message : String(retryError);
@@ -83805,6 +84040,109 @@ async function getWorkspaceSocketInfo(workspaceName) {
83805
84040
 
83806
84041
  // src/index.ts
83807
84042
  var import_daemon_core4 = __toESM(require_dist3());
84043
+
84044
+ // src/raw-terminal-http.ts
84045
+ function parseRawTerminalPath(pathname) {
84046
+ const match = /^\/api\/v1\/sessions\/([^/]+)\/(screen|state|input|keys)$/.exec(pathname);
84047
+ if (!match) return null;
84048
+ try {
84049
+ const sessionId = decodeURIComponent(match[1]).trim();
84050
+ return sessionId ? { sessionId, action: match[2] } : null;
84051
+ } catch {
84052
+ return null;
84053
+ }
84054
+ }
84055
+ function isRawTerminalApiPath(pathname) {
84056
+ return parseRawTerminalPath(pathname) !== null;
84057
+ }
84058
+ function isLoopbackAddress(address) {
84059
+ if (!address) return false;
84060
+ const normalized = address.toLowerCase().split("%", 1)[0];
84061
+ return normalized === "127.0.0.1" || normalized.startsWith("127.") || normalized === "::1" || normalized === "::ffff:127.0.0.1" || normalized.startsWith("::ffff:127.");
84062
+ }
84063
+ function isLoopbackRequest(req) {
84064
+ return isLoopbackAddress(req.socket.remoteAddress);
84065
+ }
84066
+ async function readJsonBody(req) {
84067
+ return await new Promise((resolve2, reject) => {
84068
+ let body = "";
84069
+ req.setEncoding("utf8");
84070
+ req.on("data", (chunk) => {
84071
+ body += chunk;
84072
+ if (body.length > 64 * 1024) reject(new Error("Request body too large"));
84073
+ });
84074
+ req.on("end", () => {
84075
+ try {
84076
+ resolve2(body ? JSON.parse(body) : {});
84077
+ } catch (error48) {
84078
+ reject(error48);
84079
+ }
84080
+ });
84081
+ req.on("error", reject);
84082
+ });
84083
+ }
84084
+ function writeJson(res, statusCode, value) {
84085
+ res.writeHead(statusCode, { "Content-Type": "application/json" });
84086
+ res.end(JSON.stringify(value));
84087
+ }
84088
+ function errorStatus(error48) {
84089
+ const message = error48 instanceof Error ? error48.message : String(error48);
84090
+ if (/Unknown session/i.test(message)) return 404;
84091
+ if (/not running|unavailable/i.test(message)) return 409;
84092
+ if (/Unsupported named key|keys must|text must|JSON|body too large/i.test(message)) return 400;
84093
+ return 500;
84094
+ }
84095
+ async function handleRawTerminalHttpRequest(options) {
84096
+ const { req, res, parsedUrl, service } = options;
84097
+ const route = parseRawTerminalPath(parsedUrl.pathname);
84098
+ if (!route) return false;
84099
+ if (!isLoopbackRequest(req)) {
84100
+ writeJson(res, 403, { error: "Raw terminal API is available only from localhost." });
84101
+ return true;
84102
+ }
84103
+ const method = req.method || "GET";
84104
+ try {
84105
+ if (route.action === "screen" && method === "GET") {
84106
+ const format = parsedUrl.searchParams.get("format") || "text";
84107
+ if (format !== "text") {
84108
+ writeJson(res, 400, { error: "Phase 2 supports only format=text." });
84109
+ return true;
84110
+ }
84111
+ const text = await service.readScreen(route.sessionId);
84112
+ res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
84113
+ res.end(text);
84114
+ return true;
84115
+ }
84116
+ if (route.action === "state" && method === "GET") {
84117
+ writeJson(res, 200, await service.readState(route.sessionId));
84118
+ return true;
84119
+ }
84120
+ if (route.action === "input" && method === "POST") {
84121
+ const body = await readJsonBody(req);
84122
+ if (typeof body.text !== "string") throw new Error("text must be a string");
84123
+ await service.writeInput(route.sessionId, body.text);
84124
+ writeJson(res, 200, { success: true });
84125
+ return true;
84126
+ }
84127
+ if (route.action === "keys" && method === "POST") {
84128
+ const body = await readJsonBody(req);
84129
+ if (!Array.isArray(body.keys) || !body.keys.every((key) => typeof key === "string")) {
84130
+ throw new Error("keys must be an array of named key strings");
84131
+ }
84132
+ await service.writeKeys(route.sessionId, body.keys);
84133
+ writeJson(res, 200, { success: true });
84134
+ return true;
84135
+ }
84136
+ writeJson(res, 405, { error: "Method not allowed" });
84137
+ return true;
84138
+ } catch (error48) {
84139
+ const message = error48 instanceof Error ? error48.message : String(error48);
84140
+ writeJson(res, errorStatus(error48), { error: message });
84141
+ return true;
84142
+ }
84143
+ }
84144
+
84145
+ // src/index.ts
83808
84146
  var DEFAULT_PORT = 3847;
83809
84147
  var STATUS_INTERVAL = 2e3;
83810
84148
  var CHAT_OUTPUT_ACTIVITY_HOT_MS = import_daemon_core4.DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS;
@@ -84027,6 +84365,27 @@ var StandaloneServer = class {
84027
84365
  devServer = null;
84028
84366
  sessionHostEndpoint = null;
84029
84367
  sessionHostControl = null;
84368
+ rawTerminalService() {
84369
+ const endpoint = this.sessionHostEndpoint || void 0;
84370
+ return {
84371
+ readScreen: (sessionId) => (0, import_daemon_core2.withRawTerminalAttachment)(
84372
+ { endpoint, sessionId, mode: "read" },
84373
+ (attachment) => attachment.readScreenText()
84374
+ ),
84375
+ readState: (sessionId) => (0, import_daemon_core2.withRawTerminalAttachment)(
84376
+ { endpoint, sessionId, mode: "read" },
84377
+ (attachment) => attachment.readState()
84378
+ ),
84379
+ writeInput: (sessionId, text) => (0, import_daemon_core2.withRawTerminalAttachment)(
84380
+ { endpoint, sessionId, mode: "write" },
84381
+ (attachment) => attachment.writeInput(text)
84382
+ ),
84383
+ writeKeys: (sessionId, keys) => (0, import_daemon_core2.withRawTerminalAttachment)(
84384
+ { endpoint, sessionId, mode: "write" },
84385
+ (attachment) => attachment.writeKeys(keys)
84386
+ )
84387
+ };
84388
+ }
84030
84389
  isRecoverableSessionHostError(error48) {
84031
84390
  const message = error48 instanceof Error ? error48.message : String(error48);
84032
84391
  return message.includes("ECONNREFUSED") || message.includes("ENOENT") || message.includes("Session host socket unavailable");
@@ -84589,6 +84948,19 @@ var StandaloneServer = class {
84589
84948
  return;
84590
84949
  }
84591
84950
  const apiPath = url2.startsWith("/api/v1/") ? url2.slice(7) : null;
84951
+ if (isRawTerminalApiPath(parsedUrl.pathname)) {
84952
+ void handleRawTerminalHttpRequest({
84953
+ req,
84954
+ res,
84955
+ parsedUrl,
84956
+ service: this.rawTerminalService()
84957
+ }).catch((error48) => {
84958
+ if (res.headersSent) return;
84959
+ res.writeHead(500, { "Content-Type": "application/json" });
84960
+ res.end(JSON.stringify({ error: error48?.message || String(error48) }));
84961
+ });
84962
+ return;
84963
+ }
84592
84964
  if (apiPath === "/status" && method === "GET") {
84593
84965
  const status = this.getStatus(getSharedSnapshot());
84594
84966
  res.writeHead(200, { "Content-Type": "application/json" });