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

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",
@@ -46907,6 +46908,7 @@ ${lastSnapshot}`;
46907
46908
  ProviderCliAdapter: () => ProviderCliAdapter,
46908
46909
  ProviderInstanceManager: () => ProviderInstanceManager,
46909
46910
  ProviderLoader: () => ProviderLoader,
46911
+ RawTerminalAttachment: () => RawTerminalAttachment,
46910
46912
  STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS2,
46911
46913
  SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory2,
46912
46914
  SpecDriver: () => SpecDriver,
@@ -47057,6 +47059,8 @@ ${lastSnapshot}`;
47057
47059
  markSetupComplete: () => markSetupComplete,
47058
47060
  markStaleDirectDispatches: () => markStaleDirectDispatches,
47059
47061
  maybeRunDaemonUpgradeHelperFromEnv: () => maybeRunDaemonUpgradeHelperFromEnv2,
47062
+ namedKeyToAnsi: () => namedKeyToAnsi,
47063
+ namedKeysToAnsi: () => namedKeysToAnsi,
47060
47064
  normalizeActiveChatData: () => normalizeActiveChatData,
47061
47065
  normalizeChatMessage: () => normalizeChatMessage,
47062
47066
  normalizeChatMessageKind: () => normalizeChatMessageKind,
@@ -47137,7 +47141,8 @@ ${lastSnapshot}`;
47137
47141
  validateCliProviderManifest: () => validateCliProviderManifest,
47138
47142
  validateMeshRefineConfig: () => validateMeshRefineConfig,
47139
47143
  validateMeshTaskModeRequest: () => validateMeshTaskModeRequest,
47140
- validateMeshWorktreeBootstrapConfig: () => validateMeshWorktreeBootstrapConfig
47144
+ validateMeshWorktreeBootstrapConfig: () => validateMeshWorktreeBootstrapConfig,
47145
+ withRawTerminalAttachment: () => withRawTerminalAttachment2
47141
47146
  });
47142
47147
  module2.exports = __toCommonJS2(index_exports);
47143
47148
  init_repo_mesh_types();
@@ -73995,9 +74000,9 @@ ${e?.stderr || ""}`
73995
74000
  });
73996
74001
  let node;
73997
74002
  if (meshRecord.inline) {
73998
- const { randomUUID: randomUUID10 } = await import("crypto");
74003
+ const { randomUUID: randomUUID11 } = await import("crypto");
73999
74004
  node = {
74000
- id: `node_${randomUUID10().replace(/-/g, "")}`,
74005
+ id: `node_${randomUUID11().replace(/-/g, "")}`,
74001
74006
  workspace: result.worktreePath,
74002
74007
  repoRoot: result.worktreePath,
74003
74008
  daemonId: sourceNode.daemonId,
@@ -82310,6 +82315,199 @@ data: ${JSON.stringify(msg.data)}
82310
82315
  });
82311
82316
  }
82312
82317
  };
82318
+ var import_crypto6 = require("crypto");
82319
+ var import_session_host_core4 = require_dist2();
82320
+ var BASE_KEY_SEQUENCES = {
82321
+ enter: "\r",
82322
+ escape: "\x1B",
82323
+ tab: " ",
82324
+ backspace: "\x7F",
82325
+ up: "\x1B[A",
82326
+ down: "\x1B[B",
82327
+ right: "\x1B[C",
82328
+ left: "\x1B[D",
82329
+ home: "\x1B[H",
82330
+ end: "\x1B[F",
82331
+ pageup: "\x1B[5~",
82332
+ pagedown: "\x1B[6~",
82333
+ space: " ",
82334
+ f1: "\x1BOP",
82335
+ f2: "\x1BOQ",
82336
+ f3: "\x1BOR",
82337
+ f4: "\x1BOS",
82338
+ f5: "\x1B[15~",
82339
+ f6: "\x1B[17~",
82340
+ f7: "\x1B[18~",
82341
+ f8: "\x1B[19~",
82342
+ f9: "\x1B[20~",
82343
+ f10: "\x1B[21~",
82344
+ f11: "\x1B[23~",
82345
+ f12: "\x1B[24~"
82346
+ };
82347
+ var SHIFTED_CSI_KEYS = {
82348
+ up: "\x1B[1;2A",
82349
+ down: "\x1B[1;2B",
82350
+ right: "\x1B[1;2C",
82351
+ left: "\x1B[1;2D",
82352
+ home: "\x1B[1;2H",
82353
+ end: "\x1B[1;2F",
82354
+ pageup: "\x1B[5;2~",
82355
+ pagedown: "\x1B[6;2~",
82356
+ f1: "\x1B[1;2P",
82357
+ f2: "\x1B[1;2Q",
82358
+ f3: "\x1B[1;2R",
82359
+ f4: "\x1B[1;2S",
82360
+ f5: "\x1B[15;2~",
82361
+ f6: "\x1B[17;2~",
82362
+ f7: "\x1B[18;2~",
82363
+ f8: "\x1B[19;2~",
82364
+ f9: "\x1B[20;2~",
82365
+ f10: "\x1B[21;2~",
82366
+ f11: "\x1B[23;2~",
82367
+ f12: "\x1B[24;2~"
82368
+ };
82369
+ function isLowercaseLetter(value) {
82370
+ return /^[a-z]$/.test(value);
82371
+ }
82372
+ function encodeControlLetter(letter) {
82373
+ return String.fromCharCode(letter.charCodeAt(0) - 96);
82374
+ }
82375
+ function encodeShiftedKey(key) {
82376
+ if (isLowercaseLetter(key)) return key.toUpperCase();
82377
+ if (key.startsWith("ctrl+") && isLowercaseLetter(key.slice(5))) {
82378
+ return encodeControlLetter(key.slice(5));
82379
+ }
82380
+ if (key.startsWith("alt+") && isLowercaseLetter(key.slice(4))) {
82381
+ return `\x1B${key.slice(4).toUpperCase()}`;
82382
+ }
82383
+ if (key === "tab") return "\x1B[Z";
82384
+ if (key in SHIFTED_CSI_KEYS) return SHIFTED_CSI_KEYS[key];
82385
+ if (key in BASE_KEY_SEQUENCES) return BASE_KEY_SEQUENCES[key];
82386
+ throw new Error(`Unsupported named key: shift+${key}`);
82387
+ }
82388
+ function namedKeyToAnsi(key) {
82389
+ const normalized = String(key || "").trim().toLowerCase();
82390
+ if (normalized in BASE_KEY_SEQUENCES) return BASE_KEY_SEQUENCES[normalized];
82391
+ if (normalized.startsWith("ctrl+") && isLowercaseLetter(normalized.slice(5))) {
82392
+ return encodeControlLetter(normalized.slice(5));
82393
+ }
82394
+ if (normalized.startsWith("alt+") && isLowercaseLetter(normalized.slice(4))) {
82395
+ return `\x1B${normalized.slice(4)}`;
82396
+ }
82397
+ if (normalized.startsWith("shift+")) return encodeShiftedKey(normalized.slice(6));
82398
+ throw new Error(`Unsupported named key: ${key}`);
82399
+ }
82400
+ function namedKeysToAnsi(keys) {
82401
+ if (!Array.isArray(keys)) throw new Error("keys must be an array");
82402
+ return keys.map(namedKeyToAnsi).join("");
82403
+ }
82404
+ var RawTerminalAttachment = class _RawTerminalAttachment {
82405
+ constructor(sessionId, clientId, mode, client) {
82406
+ this.sessionId = sessionId;
82407
+ this.clientId = clientId;
82408
+ this.mode = mode;
82409
+ this.client = client;
82410
+ }
82411
+ closed = false;
82412
+ static async attach(options) {
82413
+ const sessionId = String(options.sessionId || "").trim();
82414
+ if (!sessionId) throw new Error("sessionId is required");
82415
+ const mode = options.mode || "read";
82416
+ const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto6.randomUUID)().slice(0, 8)}`;
82417
+ const client = options.client || new import_session_host_core4.SessionHostClient({ endpoint: options.endpoint });
82418
+ await client.connect();
82419
+ const attachResponse = await client.request({
82420
+ type: "attach_session",
82421
+ payload: {
82422
+ sessionId,
82423
+ clientId,
82424
+ clientType: "web",
82425
+ readOnly: mode === "read"
82426
+ }
82427
+ });
82428
+ if (!attachResponse.success) {
82429
+ await client.close().catch(() => {
82430
+ });
82431
+ throw new Error(attachResponse.error || `Failed to attach terminal session ${sessionId}`);
82432
+ }
82433
+ if (mode === "write") {
82434
+ const ownerResponse = await client.request({
82435
+ type: "acquire_write",
82436
+ payload: {
82437
+ sessionId,
82438
+ clientId,
82439
+ ownerType: "user",
82440
+ force: true
82441
+ }
82442
+ });
82443
+ if (!ownerResponse.success) {
82444
+ await client.request({
82445
+ type: "detach_session",
82446
+ payload: { sessionId, clientId }
82447
+ }).catch(() => ({ success: false }));
82448
+ await client.close().catch(() => {
82449
+ });
82450
+ throw new Error(ownerResponse.error || `Failed to acquire terminal session ${sessionId}`);
82451
+ }
82452
+ }
82453
+ return new _RawTerminalAttachment(sessionId, clientId, mode, client);
82454
+ }
82455
+ async readSnapshot() {
82456
+ const response = await this.client.request({
82457
+ type: "get_terminal_snapshot",
82458
+ payload: { sessionId: this.sessionId }
82459
+ });
82460
+ if (!response.success || !response.result) {
82461
+ throw new Error(response.error || `Terminal screen unavailable for ${this.sessionId}`);
82462
+ }
82463
+ return response.result;
82464
+ }
82465
+ async readScreenText() {
82466
+ return (await this.readSnapshot()).text;
82467
+ }
82468
+ async readState() {
82469
+ return (await this.readSnapshot()).state;
82470
+ }
82471
+ async writeInput(text) {
82472
+ if (this.mode !== "write") throw new Error("Raw terminal attachment is read-only");
82473
+ const response = await this.client.request({
82474
+ type: "send_input",
82475
+ payload: {
82476
+ sessionId: this.sessionId,
82477
+ clientId: this.clientId,
82478
+ data: text
82479
+ }
82480
+ });
82481
+ if (!response.success) throw new Error(response.error || `Failed to write terminal input to ${this.sessionId}`);
82482
+ }
82483
+ async writeKeys(keys) {
82484
+ await this.writeInput(namedKeysToAnsi(keys));
82485
+ }
82486
+ async close() {
82487
+ if (this.closed) return;
82488
+ this.closed = true;
82489
+ if (this.mode === "write") {
82490
+ await this.client.request({
82491
+ type: "release_write",
82492
+ payload: { sessionId: this.sessionId, clientId: this.clientId }
82493
+ }).catch(() => ({ success: false }));
82494
+ }
82495
+ await this.client.request({
82496
+ type: "detach_session",
82497
+ payload: { sessionId: this.sessionId, clientId: this.clientId }
82498
+ }).catch(() => ({ success: false }));
82499
+ await this.client.close().catch(() => {
82500
+ });
82501
+ }
82502
+ };
82503
+ async function withRawTerminalAttachment2(options, operation) {
82504
+ const attachment = await RawTerminalAttachment.attach(options);
82505
+ try {
82506
+ return await operation(attachment);
82507
+ } finally {
82508
+ await attachment.close();
82509
+ }
82510
+ }
82313
82511
  var DEFAULT_SESSION_HOST_APP_NAME = "adhdev";
82314
82512
  var DEFAULT_STANDALONE_SESSION_HOST_APP_NAME = "adhdev-standalone";
82315
82513
  function getReservedStandaloneNamespaceWarning() {
@@ -82339,7 +82537,7 @@ data: ${JSON.stringify(msg.data)}
82339
82537
  function resolveSessionHostAppName(options = {}) {
82340
82538
  return resolveSessionHostAppNameResolution2(options).appName;
82341
82539
  }
82342
- var import_session_host_core4 = require_dist2();
82540
+ var import_session_host_core5 = require_dist2();
82343
82541
  var STARTUP_TIMEOUT_MS = DEFAULT_SESSION_HOST_READY_TIMEOUT_MS2;
82344
82542
  var STARTUP_POLL_MS = 200;
82345
82543
  var SessionHostCompatibilityError = class extends Error {
@@ -82367,7 +82565,7 @@ data: ${JSON.stringify(msg.data)}
82367
82565
  }
82368
82566
  }
82369
82567
  async function canConnect(endpoint, requiredRequestTypes = []) {
82370
- const client = new import_session_host_core4.SessionHostClient({ endpoint });
82568
+ const client = new import_session_host_core5.SessionHostClient({ endpoint });
82371
82569
  try {
82372
82570
  await client.connect();
82373
82571
  await assertRequiredRequestTypes(client, requiredRequestTypes);
@@ -82389,7 +82587,7 @@ data: ${JSON.stringify(msg.data)}
82389
82587
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
82390
82588
  }
82391
82589
  async function ensureSessionHostReady2(options) {
82392
- const endpoint = (0, import_session_host_core4.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
82590
+ const endpoint = (0, import_session_host_core5.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
82393
82591
  const requiredRequestTypes = options.requiredRequestTypes || [];
82394
82592
  if (await canConnect(endpoint, requiredRequestTypes)) return endpoint;
82395
82593
  options.spawnHost();
@@ -82397,7 +82595,7 @@ data: ${JSON.stringify(msg.data)}
82397
82595
  return endpoint;
82398
82596
  }
82399
82597
  async function listHostedCliRuntimes2(endpoint) {
82400
- const client = new import_session_host_core4.SessionHostClient({ endpoint });
82598
+ const client = new import_session_host_core5.SessionHostClient({ endpoint });
82401
82599
  try {
82402
82600
  const response = await client.request({
82403
82601
  type: "list_sessions",
@@ -83396,7 +83594,7 @@ async function ensureSessionHostReady() {
83396
83594
  appName: SESSION_HOST_APP_NAME,
83397
83595
  spawnHost,
83398
83596
  timeoutMs: SESSION_HOST_START_TIMEOUT_MS,
83399
- requiredRequestTypes: ["delete_session"]
83597
+ requiredRequestTypes: ["delete_session", "get_terminal_snapshot"]
83400
83598
  });
83401
83599
  } catch (error48) {
83402
83600
  stopSessionHost();
@@ -83404,7 +83602,7 @@ async function ensureSessionHostReady() {
83404
83602
  appName: SESSION_HOST_APP_NAME,
83405
83603
  spawnHost,
83406
83604
  timeoutMs: SESSION_HOST_START_TIMEOUT_MS,
83407
- requiredRequestTypes: ["delete_session"]
83605
+ requiredRequestTypes: ["delete_session", "get_terminal_snapshot"]
83408
83606
  }).catch((retryError) => {
83409
83607
  const initialMessage = error48 instanceof Error ? error48.message : String(error48);
83410
83608
  const retryMessage = retryError instanceof Error ? retryError.message : String(retryError);
@@ -83805,6 +84003,109 @@ async function getWorkspaceSocketInfo(workspaceName) {
83805
84003
 
83806
84004
  // src/index.ts
83807
84005
  var import_daemon_core4 = __toESM(require_dist3());
84006
+
84007
+ // src/raw-terminal-http.ts
84008
+ function parseRawTerminalPath(pathname) {
84009
+ const match = /^\/api\/v1\/sessions\/([^/]+)\/(screen|state|input|keys)$/.exec(pathname);
84010
+ if (!match) return null;
84011
+ try {
84012
+ const sessionId = decodeURIComponent(match[1]).trim();
84013
+ return sessionId ? { sessionId, action: match[2] } : null;
84014
+ } catch {
84015
+ return null;
84016
+ }
84017
+ }
84018
+ function isRawTerminalApiPath(pathname) {
84019
+ return parseRawTerminalPath(pathname) !== null;
84020
+ }
84021
+ function isLoopbackAddress(address) {
84022
+ if (!address) return false;
84023
+ const normalized = address.toLowerCase().split("%", 1)[0];
84024
+ return normalized === "127.0.0.1" || normalized.startsWith("127.") || normalized === "::1" || normalized === "::ffff:127.0.0.1" || normalized.startsWith("::ffff:127.");
84025
+ }
84026
+ function isLoopbackRequest(req) {
84027
+ return isLoopbackAddress(req.socket.remoteAddress);
84028
+ }
84029
+ async function readJsonBody(req) {
84030
+ return await new Promise((resolve2, reject) => {
84031
+ let body = "";
84032
+ req.setEncoding("utf8");
84033
+ req.on("data", (chunk) => {
84034
+ body += chunk;
84035
+ if (body.length > 64 * 1024) reject(new Error("Request body too large"));
84036
+ });
84037
+ req.on("end", () => {
84038
+ try {
84039
+ resolve2(body ? JSON.parse(body) : {});
84040
+ } catch (error48) {
84041
+ reject(error48);
84042
+ }
84043
+ });
84044
+ req.on("error", reject);
84045
+ });
84046
+ }
84047
+ function writeJson(res, statusCode, value) {
84048
+ res.writeHead(statusCode, { "Content-Type": "application/json" });
84049
+ res.end(JSON.stringify(value));
84050
+ }
84051
+ function errorStatus(error48) {
84052
+ const message = error48 instanceof Error ? error48.message : String(error48);
84053
+ if (/Unknown session/i.test(message)) return 404;
84054
+ if (/not running|unavailable/i.test(message)) return 409;
84055
+ if (/Unsupported named key|keys must|text must|JSON|body too large/i.test(message)) return 400;
84056
+ return 500;
84057
+ }
84058
+ async function handleRawTerminalHttpRequest(options) {
84059
+ const { req, res, parsedUrl, service } = options;
84060
+ const route = parseRawTerminalPath(parsedUrl.pathname);
84061
+ if (!route) return false;
84062
+ if (!isLoopbackRequest(req)) {
84063
+ writeJson(res, 403, { error: "Raw terminal API is available only from localhost." });
84064
+ return true;
84065
+ }
84066
+ const method = req.method || "GET";
84067
+ try {
84068
+ if (route.action === "screen" && method === "GET") {
84069
+ const format = parsedUrl.searchParams.get("format") || "text";
84070
+ if (format !== "text") {
84071
+ writeJson(res, 400, { error: "Phase 2 supports only format=text." });
84072
+ return true;
84073
+ }
84074
+ const text = await service.readScreen(route.sessionId);
84075
+ res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
84076
+ res.end(text);
84077
+ return true;
84078
+ }
84079
+ if (route.action === "state" && method === "GET") {
84080
+ writeJson(res, 200, await service.readState(route.sessionId));
84081
+ return true;
84082
+ }
84083
+ if (route.action === "input" && method === "POST") {
84084
+ const body = await readJsonBody(req);
84085
+ if (typeof body.text !== "string") throw new Error("text must be a string");
84086
+ await service.writeInput(route.sessionId, body.text);
84087
+ writeJson(res, 200, { success: true });
84088
+ return true;
84089
+ }
84090
+ if (route.action === "keys" && method === "POST") {
84091
+ const body = await readJsonBody(req);
84092
+ if (!Array.isArray(body.keys) || !body.keys.every((key) => typeof key === "string")) {
84093
+ throw new Error("keys must be an array of named key strings");
84094
+ }
84095
+ await service.writeKeys(route.sessionId, body.keys);
84096
+ writeJson(res, 200, { success: true });
84097
+ return true;
84098
+ }
84099
+ writeJson(res, 405, { error: "Method not allowed" });
84100
+ return true;
84101
+ } catch (error48) {
84102
+ const message = error48 instanceof Error ? error48.message : String(error48);
84103
+ writeJson(res, errorStatus(error48), { error: message });
84104
+ return true;
84105
+ }
84106
+ }
84107
+
84108
+ // src/index.ts
83808
84109
  var DEFAULT_PORT = 3847;
83809
84110
  var STATUS_INTERVAL = 2e3;
83810
84111
  var CHAT_OUTPUT_ACTIVITY_HOT_MS = import_daemon_core4.DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS;
@@ -84027,6 +84328,27 @@ var StandaloneServer = class {
84027
84328
  devServer = null;
84028
84329
  sessionHostEndpoint = null;
84029
84330
  sessionHostControl = null;
84331
+ rawTerminalService() {
84332
+ const endpoint = this.sessionHostEndpoint || void 0;
84333
+ return {
84334
+ readScreen: (sessionId) => (0, import_daemon_core2.withRawTerminalAttachment)(
84335
+ { endpoint, sessionId, mode: "read" },
84336
+ (attachment) => attachment.readScreenText()
84337
+ ),
84338
+ readState: (sessionId) => (0, import_daemon_core2.withRawTerminalAttachment)(
84339
+ { endpoint, sessionId, mode: "read" },
84340
+ (attachment) => attachment.readState()
84341
+ ),
84342
+ writeInput: (sessionId, text) => (0, import_daemon_core2.withRawTerminalAttachment)(
84343
+ { endpoint, sessionId, mode: "write" },
84344
+ (attachment) => attachment.writeInput(text)
84345
+ ),
84346
+ writeKeys: (sessionId, keys) => (0, import_daemon_core2.withRawTerminalAttachment)(
84347
+ { endpoint, sessionId, mode: "write" },
84348
+ (attachment) => attachment.writeKeys(keys)
84349
+ )
84350
+ };
84351
+ }
84030
84352
  isRecoverableSessionHostError(error48) {
84031
84353
  const message = error48 instanceof Error ? error48.message : String(error48);
84032
84354
  return message.includes("ECONNREFUSED") || message.includes("ENOENT") || message.includes("Session host socket unavailable");
@@ -84589,6 +84911,19 @@ var StandaloneServer = class {
84589
84911
  return;
84590
84912
  }
84591
84913
  const apiPath = url2.startsWith("/api/v1/") ? url2.slice(7) : null;
84914
+ if (isRawTerminalApiPath(parsedUrl.pathname)) {
84915
+ void handleRawTerminalHttpRequest({
84916
+ req,
84917
+ res,
84918
+ parsedUrl,
84919
+ service: this.rawTerminalService()
84920
+ }).catch((error48) => {
84921
+ if (res.headersSent) return;
84922
+ res.writeHead(500, { "Content-Type": "application/json" });
84923
+ res.end(JSON.stringify({ error: error48?.message || String(error48) }));
84924
+ });
84925
+ return;
84926
+ }
84592
84927
  if (apiPath === "/status" && method === "GET") {
84593
84928
  const status = this.getStatus(getSharedSnapshot());
84594
84929
  res.writeHead(200, { "Content-Type": "application/json" });