@addai/node 0.30.12 → 0.30.14

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 (143) hide show
  1. package/dist/auto-update.d.ts +3 -0
  2. package/dist/auto-update.js +15 -0
  3. package/dist/capabilities.d.ts +31 -6
  4. package/dist/capabilities.js +48 -26
  5. package/dist/claude-models.d.ts +71 -0
  6. package/dist/claude-models.js +330 -0
  7. package/dist/claude-print.d.ts +15 -0
  8. package/dist/claude-print.js +16 -0
  9. package/dist/codex-models.d.ts +81 -0
  10. package/dist/codex-models.js +385 -0
  11. package/dist/codex-spawn.d.ts +14 -8
  12. package/dist/codex-spawn.js +30 -10
  13. package/dist/command-runner.js +31 -9
  14. package/dist/desktop/docker.js +2 -0
  15. package/dist/desktop/manager.d.ts +2 -1
  16. package/dist/desktop/manager.js +51 -1
  17. package/dist/desktop/provider.d.ts +1 -1
  18. package/dist/desktop/provider.js +2 -0
  19. package/dist/desktop/relay-client.js +7 -0
  20. package/dist/desktop/spec.d.ts +2 -0
  21. package/dist/direct/aios-bridge.d.ts +13 -0
  22. package/dist/direct/aios-bridge.js +177 -0
  23. package/dist/direct/aios-mcp.d.ts +2 -0
  24. package/dist/direct/aios-mcp.js +132 -0
  25. package/dist/direct/aios-tools.d.ts +351 -0
  26. package/dist/direct/aios-tools.js +120 -0
  27. package/dist/direct/board-chat-provider.d.ts +64 -0
  28. package/dist/direct/board-chat-provider.js +151 -0
  29. package/dist/direct/board-chat.d.ts +40 -0
  30. package/dist/direct/board-chat.js +414 -0
  31. package/dist/direct/config.d.ts +2 -0
  32. package/dist/direct/config.js +60 -0
  33. package/dist/direct/launch.d.ts +14 -0
  34. package/dist/direct/launch.js +121 -0
  35. package/dist/direct/protocol.d.ts +46 -0
  36. package/dist/direct/protocol.js +84 -0
  37. package/dist/direct/relay-main.d.ts +1 -0
  38. package/dist/direct/relay-main.js +21 -0
  39. package/dist/direct/relay.d.ts +26 -0
  40. package/dist/direct/relay.js +334 -0
  41. package/dist/direct/sessions.d.ts +61 -0
  42. package/dist/direct/sessions.js +368 -0
  43. package/dist/direct/supervisor.d.ts +9 -0
  44. package/dist/direct/supervisor.js +137 -0
  45. package/dist/direct/terminal.d.ts +1 -0
  46. package/dist/direct/terminal.js +106 -0
  47. package/dist/direct/worker.d.ts +1 -0
  48. package/dist/direct/worker.js +278 -0
  49. package/dist/entitybox/container-action.cjs +85 -0
  50. package/dist/entitybox/container-watchdog.cjs +70 -0
  51. package/dist/entitybox/model-entry.d.ts +3 -0
  52. package/dist/entitybox/model-entry.js +233 -0
  53. package/dist/entitybox/model-mcp-proxy.d.ts +3 -0
  54. package/dist/entitybox/model-mcp-proxy.js +85 -0
  55. package/dist/entitybox-application-boundary.d.ts +59 -0
  56. package/dist/entitybox-application-boundary.js +161 -0
  57. package/dist/entitybox-application-guard-core.d.ts +44 -0
  58. package/dist/entitybox-application-guard-core.js +164 -0
  59. package/dist/entitybox-application-guard-process.d.ts +1 -0
  60. package/dist/entitybox-application-guard-process.js +83 -0
  61. package/dist/entitybox-application-guard.d.ts +46 -0
  62. package/dist/entitybox-application-guard.js +239 -0
  63. package/dist/entitybox-capacity-health-host.d.ts +12 -0
  64. package/dist/entitybox-capacity-health-host.js +80 -0
  65. package/dist/entitybox-capacity-health-worker.d.ts +22 -0
  66. package/dist/entitybox-capacity-health-worker.js +168 -0
  67. package/dist/entitybox-capacity-host.d.ts +17 -0
  68. package/dist/entitybox-capacity-host.js +155 -0
  69. package/dist/entitybox-capacity-owner-process.d.ts +1 -0
  70. package/dist/entitybox-capacity-owner-process.js +32 -0
  71. package/dist/entitybox-capacity-owner.d.ts +16 -0
  72. package/dist/entitybox-capacity-owner.js +90 -0
  73. package/dist/entitybox-capacity-programs.d.ts +7 -0
  74. package/dist/entitybox-capacity-programs.js +49 -0
  75. package/dist/entitybox-capacity-provisioner.d.ts +82 -0
  76. package/dist/entitybox-capacity-provisioner.js +404 -0
  77. package/dist/entitybox-capacity-publication.d.ts +29 -0
  78. package/dist/entitybox-capacity-publication.js +199 -0
  79. package/dist/entitybox-capacity-registration.d.ts +36 -0
  80. package/dist/entitybox-capacity-registration.js +75 -0
  81. package/dist/entitybox-capacity-worker.d.ts +29 -0
  82. package/dist/entitybox-capacity-worker.js +113 -0
  83. package/dist/entitybox-computer-task.d.ts +69 -0
  84. package/dist/entitybox-computer-task.js +176 -0
  85. package/dist/entitybox-controller-recovery.d.ts +40 -0
  86. package/dist/entitybox-controller-recovery.js +156 -0
  87. package/dist/entitybox-desktop-action-server.d.ts +34 -0
  88. package/dist/entitybox-desktop-action-server.js +176 -0
  89. package/dist/entitybox-desktop-admission.d.ts +32 -0
  90. package/dist/entitybox-desktop-admission.js +79 -0
  91. package/dist/entitybox-display-binding.d.ts +13 -0
  92. package/dist/entitybox-display-binding.js +31 -0
  93. package/dist/entitybox-execution-container.d.ts +82 -0
  94. package/dist/entitybox-execution-container.js +450 -0
  95. package/dist/entitybox-execution-host.d.ts +40 -0
  96. package/dist/entitybox-execution-host.js +168 -0
  97. package/dist/entitybox-execution-lease.d.ts +81 -0
  98. package/dist/entitybox-execution-lease.js +332 -0
  99. package/dist/entitybox-file-inputs.d.ts +51 -0
  100. package/dist/entitybox-file-inputs.js +182 -0
  101. package/dist/entitybox-file-session.d.ts +38 -0
  102. package/dist/entitybox-file-session.js +82 -0
  103. package/dist/entitybox-host-process.d.ts +12 -0
  104. package/dist/entitybox-host-process.js +120 -0
  105. package/dist/entitybox-install.d.ts +10 -0
  106. package/dist/entitybox-install.js +40 -0
  107. package/dist/entitybox-mcp-broker.d.ts +77 -0
  108. package/dist/entitybox-mcp-broker.js +420 -0
  109. package/dist/entitybox-mcp-contract.d.ts +10 -0
  110. package/dist/entitybox-mcp-contract.js +24 -0
  111. package/dist/entitybox-mcp-guard.d.ts +3 -0
  112. package/dist/entitybox-mcp-guard.js +167 -0
  113. package/dist/entitybox-model-artifact.d.ts +9 -0
  114. package/dist/entitybox-model-artifact.js +83 -0
  115. package/dist/entitybox-native-services.d.ts +33 -0
  116. package/dist/entitybox-native-services.js +172 -0
  117. package/dist/entitybox-operation.d.ts +4 -0
  118. package/dist/entitybox-operation.js +34 -0
  119. package/dist/entitybox-provider-broker.d.ts +41 -0
  120. package/dist/entitybox-provider-broker.js +149 -0
  121. package/dist/entitybox-queue-request.d.ts +32 -0
  122. package/dist/entitybox-queue-request.js +141 -0
  123. package/dist/entitybox-recovery-host.d.ts +12 -0
  124. package/dist/entitybox-recovery-host.js +80 -0
  125. package/dist/entitybox-recovery-store.d.ts +33 -0
  126. package/dist/entitybox-recovery-store.js +150 -0
  127. package/dist/entitybox-recovery-worker.d.ts +30 -0
  128. package/dist/entitybox-recovery-worker.js +134 -0
  129. package/dist/entitybox-vault-provider.d.ts +27 -0
  130. package/dist/entitybox-vault-provider.js +127 -0
  131. package/dist/harness-registry.js +3 -1
  132. package/dist/index.js +72 -38
  133. package/dist/install.d.ts +14 -2
  134. package/dist/install.js +115 -10
  135. package/dist/model-cache.d.ts +38 -0
  136. package/dist/model-cache.js +137 -0
  137. package/dist/request-pump.d.ts +3 -0
  138. package/dist/request-pump.js +21 -7
  139. package/dist/session-runner.d.ts +8 -1
  140. package/dist/session-runner.js +48 -3
  141. package/dist/supabase-client.js +4 -1
  142. package/package.json +5 -1
  143. package/scripts/copy-assets.js +4 -2
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- // Executes runtime_commands issued from Entity Studio: install a harness
3
- // (npm i -g) or log one in (PTY-bridged OAuth / API key). Woken by the
4
- // heartbeat's pending_commands hint — no always-on poller. One command
5
- // runs at a time; while one is live we poll its row every ~2s so the
6
- // login conversation (URL out, code back) feels interactive.
7
- //
8
- // Truth contract: login "success" is only reported after a capabilities
9
- // re-probe says authed=true. The CLI's exit code is a hint, the probe is
10
- // the verdict.
11
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
3
  if (k2 === undefined) k2 = k;
13
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -44,6 +35,16 @@ var __importStar = (this && this.__importStar) || (function () {
44
35
  Object.defineProperty(exports, "__esModule", { value: true });
45
36
  exports.setRestartHook = setRestartHook;
46
37
  exports.wake = wake;
38
+ const entitybox_desktop_admission_1 = require("./entitybox-desktop-admission");
39
+ // Executes runtime_commands issued from Entity Studio: install a harness
40
+ // (npm i -g) or log one in (PTY-bridged OAuth / API key). Woken by the
41
+ // heartbeat's pending_commands hint — no always-on poller. One command
42
+ // runs at a time; while one is live we poll its row every ~2s so the
43
+ // login conversation (URL out, code back) feels interactive.
44
+ //
45
+ // Truth contract: login "success" is only reported after a capabilities
46
+ // re-probe says authed=true. The CLI's exit code is a hint, the probe is
47
+ // the verdict.
47
48
  const child_process_1 = require("child_process");
48
49
  const crypto = __importStar(require("crypto"));
49
50
  const fs = __importStar(require("fs"));
@@ -653,6 +654,16 @@ async function runSetupScript(provider, row, onLog) {
653
654
  await (0, creds_1.execInDesktop)(provider, row, ['bash', '/conf/setup.sh'], onLog);
654
655
  }
655
656
  async function runDesktopCommand(cmd) {
657
+ if (!['desktop_create', 'desktop_rebuild', 'desktop_start'].includes(cmd.kind))
658
+ return runDesktopCommandUncoordinated(cmd);
659
+ try {
660
+ await (0, entitybox_desktop_admission_1.entityboxWithDesktopStart)(() => runDesktopCommandUncoordinated(cmd));
661
+ }
662
+ catch {
663
+ await update(cmd.id, 'failed', undefined, 'Desktop admission is unavailable. Try again when the runtime is ready.');
664
+ }
665
+ }
666
+ async function runDesktopCommandUncoordinated(cmd) {
656
667
  if (!cmd.desktop_id) {
657
668
  await update(cmd.id, 'failed', undefined, 'no desktop on command');
658
669
  return;
@@ -677,6 +688,10 @@ async function runDesktopCommand(cmd) {
677
688
  await update(cmd.id, 'failed', undefined, 'desktop row not found');
678
689
  return;
679
690
  }
691
+ if (row.entitybox_managed === true) {
692
+ await update(cmd.id, 'failed', undefined, 'This computer is managed by EntityBox');
693
+ return;
694
+ }
680
695
  let log = '';
681
696
  const onLog = (chunk) => {
682
697
  log = (log + chunk).slice(-LOG_TAIL_CHARS);
@@ -687,6 +702,13 @@ async function runDesktopCommand(cmd) {
687
702
  switch (cmd.kind) {
688
703
  case 'desktop_create':
689
704
  case 'desktop_rebuild': {
705
+ if ((0, entitybox_desktop_admission_1.entityboxAdmissionEnabled)()) {
706
+ const room = await (0, manager_1.desktopStartAllowed)(await (0, manager_1.listDesktops)(), row.id);
707
+ if (!room.allowed) {
708
+ await update(cmd.id, 'failed', undefined, room.reason);
709
+ return;
710
+ }
711
+ }
690
712
  if (cmd.kind === 'desktop_rebuild') {
691
713
  onLog('removing existing container\n');
692
714
  await provider.remove(row);
@@ -42,6 +42,8 @@ class CliProvider {
42
42
  return { id: this.id, version: this.version, running: true };
43
43
  }
44
44
  async create(row, opts, onLog) {
45
+ if (row.entitybox_managed === true)
46
+ throw new Error('This computer is managed by EntityBox');
45
47
  // Pull first so the slow, chatty image download streams into the command
46
48
  // payload as its own phase rather than hiding inside `run`.
47
49
  onLog(`pulling ${row.image}\n`);
@@ -2,7 +2,7 @@ import { DesktopRow } from './spec';
2
2
  export type Action = 'none' | 'start' | 'mark_failed' | 'mark_stopped' | 'mark_running';
3
3
  /** Pure: given what the row claims and what the engine reports, what now?
4
4
  * `actual` is null when no such container exists. */
5
- export declare function reconcileAction(row: Pick<DesktopRow, 'status' | 'autostart'>, actual: {
5
+ export declare function reconcileAction(row: Pick<DesktopRow, 'status' | 'autostart' | 'entitybox_managed'>, actual: {
6
6
  running: boolean;
7
7
  } | null): Action;
8
8
  export declare function listDesktops(): Promise<DesktopRow[]>;
@@ -38,6 +38,7 @@ export declare function ensureDirs(desktopId: string): {
38
38
  workDir: string;
39
39
  };
40
40
  export declare const DEFAULT_MAX_CONCURRENT_DESKTOPS = 2;
41
+ export declare function entityboxDesktopLimitKnown(): boolean;
41
42
  export declare function clampMaxDesktops(value: unknown): number;
42
43
  /** Pushed in by the heartbeat. Lowering it never stops a running desktop —
43
44
  * killing someone's live session to satisfy a number they just typed would
@@ -39,6 +39,7 @@ exports.listDesktops = listDesktops;
39
39
  exports.setStatus = setStatus;
40
40
  exports.allocateVncPort = allocateVncPort;
41
41
  exports.ensureDirs = ensureDirs;
42
+ exports.entityboxDesktopLimitKnown = entityboxDesktopLimitKnown;
42
43
  exports.clampMaxDesktops = clampMaxDesktops;
43
44
  exports.setMaxConcurrentDesktops = setMaxConcurrentDesktops;
44
45
  exports.maxConcurrentDesktops = maxConcurrentDesktops;
@@ -49,6 +50,7 @@ exports.startDesktopManager = startDesktopManager;
49
50
  exports.stopDesktopManager = stopDesktopManager;
50
51
  exports.reconcileAfterWake = reconcileAfterWake;
51
52
  exports.ensureRunning = ensureRunning;
53
+ const entitybox_desktop_admission_1 = require("../entitybox-desktop-admission");
52
54
  // Keeps the desktops the server believes in and the containers that actually
53
55
  // exist in agreement. Modelled on projects.ts: a slow poll, no realtime, and
54
56
  // every transition reported back so a Studio row reflects the machine.
@@ -65,6 +67,8 @@ const POLL_INTERVAL_MS = 15_000;
65
67
  /** Pure: given what the row claims and what the engine reports, what now?
66
68
  * `actual` is null when no such container exists. */
67
69
  function reconcileAction(row, actual) {
70
+ if (row.entitybox_managed === true)
71
+ return 'none';
68
72
  // In-flight and terminal states belong to the command runner, not here -
69
73
  // reconciling mid-create would fight the thing doing the creating.
70
74
  if (row.status === 'creating' || row.status === 'starting'
@@ -177,6 +181,10 @@ function ensureDirs(desktopId) {
177
181
  exports.DEFAULT_MAX_CONCURRENT_DESKTOPS = 2;
178
182
  const MAX_MAX_CONCURRENT_DESKTOPS = 16;
179
183
  let maxDesktops = exports.DEFAULT_MAX_CONCURRENT_DESKTOPS;
184
+ // Only the opt-in EntityBox host requires a server-provided initial limit.
185
+ // Ordinary legacy behavior retains its existing default while unconfigured.
186
+ let entityboxLimitKnown = false;
187
+ function entityboxDesktopLimitKnown() { return entityboxLimitKnown; }
180
188
  function clampMaxDesktops(value) {
181
189
  if (typeof value !== 'number' && typeof value !== 'string')
182
190
  return exports.DEFAULT_MAX_CONCURRENT_DESKTOPS;
@@ -191,6 +199,7 @@ function clampMaxDesktops(value) {
191
199
  * killing someone's live session to satisfy a number they just typed would
192
200
  * be a worse surprise than being one over the cap for a while. */
193
201
  function setMaxConcurrentDesktops(value) {
202
+ entityboxLimitKnown = true;
194
203
  const next = clampMaxDesktops(value);
195
204
  if (next === maxDesktops)
196
205
  return;
@@ -207,6 +216,13 @@ async function runningDesktopCount(rows) {
207
216
  return 0;
208
217
  let count = 0;
209
218
  for (const row of rows) {
219
+ // Reserved/paused dedicated capacity still consumes a slot. Never inspect
220
+ // it through the ordinary one-container naming convention.
221
+ if (row.entitybox_managed === true) {
222
+ if (row.status !== 'pending')
223
+ count++;
224
+ continue;
225
+ }
210
226
  try {
211
227
  const actual = await provider.inspect(row);
212
228
  if (actual?.running)
@@ -236,6 +252,11 @@ function queueOrder(rows) {
236
252
  /** Whether one more may start. Returns the reason when it may not, so the
237
253
  * caller can say something better than "failed". */
238
254
  async function desktopStartAllowed(rows, excludeId) {
255
+ if (excludeId) {
256
+ const admission = await (0, entitybox_desktop_admission_1.entityboxDesktopAdmission)(rows, excludeId);
257
+ if (admission)
258
+ return admission;
259
+ }
239
260
  const running = await runningDesktopCount(rows.filter(r => r.id !== excludeId));
240
261
  if (running < maxDesktops)
241
262
  return { allowed: true };
@@ -258,6 +279,8 @@ let engineStarting = false;
258
279
  let lastEngineAttempt = 0;
259
280
  const ENGINE_RETRY_MS = 3 * 60_000;
260
281
  function wantsToRun(row) {
282
+ if (row.entitybox_managed === true)
283
+ return false;
261
284
  if (row.status === 'running' || row.status === 'queued')
262
285
  return true;
263
286
  return row.status === 'stopped' && !!row.autostart;
@@ -304,7 +327,22 @@ async function maybeStartEngineForDesktops() {
304
327
  }
305
328
  let timer = null;
306
329
  let inflight = false;
330
+ let entityboxTickQueued = false;
307
331
  async function tick() {
332
+ if (entityboxTickQueued)
333
+ return;
334
+ entityboxTickQueued = true;
335
+ try {
336
+ await (0, entitybox_desktop_admission_1.entityboxWithDesktopStart)(() => tickUncoordinated());
337
+ }
338
+ catch {
339
+ console.warn('[desktops] desktop admission unavailable');
340
+ }
341
+ finally {
342
+ entityboxTickQueued = false;
343
+ }
344
+ }
345
+ async function tickUncoordinated() {
308
346
  if (inflight)
309
347
  return;
310
348
  inflight = true;
@@ -323,6 +361,8 @@ async function tick() {
323
361
  }
324
362
  const rows = await listDesktops();
325
363
  for (const row of queueOrder(rows)) {
364
+ if (row.entitybox_managed === true)
365
+ continue;
326
366
  const actual = await provider.inspect(row);
327
367
  const action = reconcileAction(row, actual);
328
368
  if (action === 'none')
@@ -400,7 +440,10 @@ function reconcileAfterWake() {
400
440
  /** Bring a desktop up and wait for it, for the run path. Returns the row when
401
441
  * running, null when it could not be started - the caller then falls back to
402
442
  * the host rather than failing the run. */
403
- async function ensureRunning(desktopId, timeoutMs = 60_000) {
443
+ function ensureRunning(desktopId, timeoutMs = 60_000) {
444
+ return ensureRunningUncoordinated(desktopId, timeoutMs);
445
+ }
446
+ async function ensureRunningUncoordinated(desktopId, timeoutMs = 60_000, admissionHeld = false) {
404
447
  const provider = await (0, docker_1.getProvider)();
405
448
  if (!provider)
406
449
  return null;
@@ -408,11 +451,18 @@ async function ensureRunning(desktopId, timeoutMs = 60_000) {
408
451
  const row = rows.find(r => r.id === desktopId);
409
452
  if (!row)
410
453
  return null;
454
+ if (row.entitybox_managed === true)
455
+ throw new Error('This computer requires EntityBox execution; host fallback is not allowed');
411
456
  const actual = await provider.inspect(row);
412
457
  if (actual?.running)
413
458
  return row;
414
459
  if (!actual)
415
460
  return null; // never created / pruned: cannot start
461
+ // Existing running desktops never wait behind new provisioning. Only an
462
+ // operation that needs to start one joins admission, then re-reads its state.
463
+ if (!admissionHeld && (0, entitybox_desktop_admission_1.entityboxAdmissionEnabled)()) {
464
+ return (0, entitybox_desktop_admission_1.entityboxWithDesktopStart)(() => ensureRunningUncoordinated(desktopId, timeoutMs, true));
465
+ }
416
466
  // At the cap, the run falls back to the host rather than starting an
417
467
  // eleventh container. Returning null is how this function already says
418
468
  // "could not start it" and the caller already handles it.
@@ -39,6 +39,6 @@ export interface DesktopProvider {
39
39
  /** Docker/Podman object names allow [a-zA-Z0-9][a-zA-Z0-9_.-]*. The id suffix
40
40
  * keeps a renamed desktop from colliding with a leftover container of the
41
41
  * same name. */
42
- export declare function containerName(row: Pick<DesktopRow, 'id' | 'name'>): string;
42
+ export declare function containerName(row: Pick<DesktopRow, 'id' | 'name' | 'entitybox_managed'>): string;
43
43
  export declare function buildCreateArgs(row: DesktopRow, opts: CreateOpts): string[];
44
44
  export declare function buildExecArgs(row: DesktopRow, cmd: string[], opts: ExecOpts): string[];
@@ -12,6 +12,8 @@ const spec_1 = require("./spec");
12
12
  * keeps a renamed desktop from colliding with a leftover container of the
13
13
  * same name. */
14
14
  function containerName(row) {
15
+ if (row.entitybox_managed === true)
16
+ throw new Error('This computer is managed by EntityBox');
15
17
  const slug = row.name.toLowerCase()
16
18
  .replace(/[^a-z0-9]+/g, '-')
17
19
  .replace(/^-+|-+$/g, '')
@@ -271,6 +271,13 @@ async function connect() {
271
271
  gone(msg.viewerId, 'desktop is not running');
272
272
  return;
273
273
  }
274
+ // This dedicated display publishes only RFB. Never derive an xpra port
275
+ // that could point at an unrelated loopback service. Ordinary rows keep
276
+ // their existing fixed-offset windows channel.
277
+ if (row.entitybox_managed === true && msg.channel === 'windows') {
278
+ gone(msg.viewerId, 'EntityBox computers support the screen channel only');
279
+ return;
280
+ }
274
281
  // WHICH stream of that desktop.
275
282
  //
276
283
  // 'screen' is the whole framebuffer over RFB, and is what every viewer
@@ -1,4 +1,6 @@
1
1
  export interface DesktopRow {
2
+ /** New dedicated EntityBox capacity has a separate lifecycle owner. */
3
+ entitybox_managed?: boolean;
2
4
  id: string;
3
5
  runtime_id: string;
4
6
  name: string;
@@ -0,0 +1,13 @@
1
+ import { type DesktopCall } from './aios-tools';
2
+ export interface AiosLaunch {
3
+ claudeConfig: string;
4
+ command: string;
5
+ args: string[];
6
+ close(): void;
7
+ }
8
+ /** Private per-process capability; never receives a Supabase, pairing or entity token. */
9
+ export declare function createAiosBridge(call: DesktopCall): AiosLaunch;
10
+ export declare function callAiosBridge(config: {
11
+ endpoint: string;
12
+ token: string;
13
+ }, name: string, args: Record<string, unknown>): Promise<unknown>;
@@ -0,0 +1,177 @@
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.createAiosBridge = createAiosBridge;
37
+ exports.callAiosBridge = callAiosBridge;
38
+ const fs = __importStar(require("node:fs"));
39
+ const os = __importStar(require("node:os"));
40
+ const path = __importStar(require("node:path"));
41
+ const net = __importStar(require("node:net"));
42
+ const node_crypto_1 = require("node:crypto");
43
+ const aios_tools_1 = require("./aios-tools");
44
+ /** Private per-process capability; never receives a Supabase, pairing or entity token. */
45
+ function createAiosBridge(call) {
46
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'addai-codeflows-'));
47
+ fs.chmodSync(directory, 0o700);
48
+ const nonce = (0, node_crypto_1.randomBytes)(24).toString('hex');
49
+ const endpoint = process.platform === 'win32' ? `\\\\.\\pipe\\addai-codeflows-${nonce}` : path.join(directory, 'bridge.sock');
50
+ const token = (0, node_crypto_1.randomBytes)(32).toString('hex');
51
+ const sockets = new Set();
52
+ let closed = false;
53
+ const server = net.createServer(socket => {
54
+ if (closed || sockets.size >= 8) {
55
+ socket.destroy();
56
+ return;
57
+ }
58
+ sockets.add(socket);
59
+ socket.setEncoding('utf8');
60
+ const timer = setTimeout(() => socket.destroy(), aios_tools_1.AIOS_TIMEOUT + 1000);
61
+ socket.on('error', () => { });
62
+ socket.on('close', () => { clearTimeout(timer); sockets.delete(socket); });
63
+ let buffer = '', received = false;
64
+ socket.on('data', chunk => {
65
+ if (received) {
66
+ socket.destroy();
67
+ return;
68
+ }
69
+ buffer += chunk;
70
+ if (Buffer.byteLength(buffer) > aios_tools_1.AIOS_LIMIT) {
71
+ socket.destroy();
72
+ return;
73
+ }
74
+ const end = buffer.indexOf('\n');
75
+ if (end < 0)
76
+ return;
77
+ received = true;
78
+ void (async () => {
79
+ try {
80
+ const q = JSON.parse(buffer.slice(0, end));
81
+ if (typeof q.token !== 'string' || q.token.length !== token.length || !(0, node_crypto_1.timingSafeEqual)(Buffer.from(q.token), Buffer.from(token)))
82
+ throw new Error('+Ai connection denied.');
83
+ (0, aios_tools_1.validateDesktopCall)(q.name, q.args);
84
+ const result = await call(q.name, q.args);
85
+ const reply = JSON.stringify({ ok: true, result });
86
+ if (Buffer.byteLength(reply) > aios_tools_1.AIOS_LIMIT)
87
+ throw new Error('+Ai result is too large.');
88
+ if (!socket.destroyed)
89
+ socket.end(reply + '\n');
90
+ }
91
+ catch (err) {
92
+ if (!socket.destroyed)
93
+ socket.end(JSON.stringify({ ok: false, error: err.message.slice(0, 500) }) + '\n');
94
+ }
95
+ })();
96
+ });
97
+ });
98
+ const config = path.join(directory, 'connection.json');
99
+ const claudeConfig = path.join(directory, 'mcp.json');
100
+ const command = process.execPath, args = [path.join(__dirname, 'aios-mcp.js'), config];
101
+ function close() {
102
+ if (closed)
103
+ return;
104
+ closed = true;
105
+ for (const socket of sockets)
106
+ socket.destroy();
107
+ server.close();
108
+ // Only our fixed files, no recursive deletion or following directory links.
109
+ for (const file of [config, claudeConfig]) {
110
+ try {
111
+ fs.unlinkSync(file);
112
+ }
113
+ catch { }
114
+ }
115
+ if (process.platform !== 'win32') {
116
+ try {
117
+ fs.unlinkSync(endpoint);
118
+ }
119
+ catch { }
120
+ }
121
+ try {
122
+ fs.rmdirSync(directory);
123
+ }
124
+ catch { }
125
+ }
126
+ server.on('error', close); // Failure disables only this optional connection.
127
+ try {
128
+ fs.writeFileSync(config, JSON.stringify({ endpoint, token }), { mode: 0o600, flag: 'wx' });
129
+ fs.writeFileSync(claudeConfig, JSON.stringify({ mcpServers: { addai_codeflows: { type: 'stdio', command, args } } }), { mode: 0o600, flag: 'wx' });
130
+ server.listen(endpoint);
131
+ }
132
+ catch (err) {
133
+ close();
134
+ throw err;
135
+ }
136
+ return { command, args, claudeConfig, close };
137
+ }
138
+ function callAiosBridge(config, name, args) {
139
+ (0, aios_tools_1.validateDesktopCall)(name, args);
140
+ return new Promise((resolve, reject) => {
141
+ const socket = net.createConnection(config.endpoint);
142
+ let buffer = '', settled = false;
143
+ const timer = setTimeout(() => finish(new Error('+Ai desktop did not respond. Reopen CodeFlows and try again.')), aios_tools_1.AIOS_TIMEOUT + 500);
144
+ function finish(error, result) {
145
+ if (settled)
146
+ return;
147
+ settled = true;
148
+ clearTimeout(timer);
149
+ socket.destroy();
150
+ if (error)
151
+ reject(error);
152
+ else
153
+ resolve(result);
154
+ }
155
+ socket.setEncoding('utf8');
156
+ socket.on('connect', () => socket.write(JSON.stringify({ token: config.token, name, args }) + '\n'));
157
+ socket.on('error', () => finish(new Error('+Ai connection is unavailable. Reopen this session in CodeFlows.')));
158
+ socket.on('close', () => finish(new Error('+Ai connection closed.')));
159
+ socket.on('data', chunk => {
160
+ buffer += chunk;
161
+ if (Buffer.byteLength(buffer) > aios_tools_1.AIOS_LIMIT) {
162
+ finish(new Error('+Ai response is too large.'));
163
+ return;
164
+ }
165
+ const end = buffer.indexOf('\n');
166
+ if (end < 0)
167
+ return;
168
+ try {
169
+ const reply = JSON.parse(buffer.slice(0, end));
170
+ finish(reply.ok === true ? undefined : new Error(reply.error || '+Ai request failed.'), reply.result);
171
+ }
172
+ catch {
173
+ finish(new Error('Invalid +Ai response.'));
174
+ }
175
+ });
176
+ });
177
+ }
@@ -0,0 +1,2 @@
1
+ /** Small stdio MCP adapter. stdout is reserved for newline-delimited JSON-RPC. */
2
+ export declare function runAiosMcp(file: string): void;
@@ -0,0 +1,132 @@
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.runAiosMcp = runAiosMcp;
37
+ const fs = __importStar(require("node:fs"));
38
+ const aios_tools_1 = require("./aios-tools");
39
+ const aios_bridge_1 = require("./aios-bridge");
40
+ /** Small stdio MCP adapter. stdout is reserved for newline-delimited JSON-RPC. */
41
+ function runAiosMcp(file) {
42
+ if (fs.statSync(file).size > 8192)
43
+ throw new Error('Invalid connection configuration.');
44
+ const config = JSON.parse(fs.readFileSync(file, 'utf8'));
45
+ if (typeof config.endpoint !== 'string' || typeof config.token !== 'string')
46
+ throw new Error('Invalid connection configuration.');
47
+ let buffer = '', active = 0, initialized = false;
48
+ const send = (message) => process.stdout.write(JSON.stringify(message) + '\n');
49
+ async function handle(q) {
50
+ if (!q || q.jsonrpc !== '2.0' || typeof q.method !== 'string') {
51
+ send({ jsonrpc: '2.0', id: null, error: { code: -32600, message: 'Invalid request.' } });
52
+ return;
53
+ }
54
+ if (q.id === undefined)
55
+ return; // No server-originated work or notifications to process.
56
+ if (!(typeof q.id === 'string' || typeof q.id === 'number'))
57
+ return;
58
+ const reply = (result) => send({ jsonrpc: '2.0', id: q.id, result });
59
+ const error = (code, message) => send({ jsonrpc: '2.0', id: q.id, error: { code, message } });
60
+ if (q.method === 'initialize') {
61
+ initialized = true;
62
+ const supported = ['2024-11-05', '2025-03-26', '2025-06-18', '2025-11-25'];
63
+ reply({ protocolVersion: supported.includes(q.params?.protocolVersion) ? q.params.protocolVersion : '2025-11-25', capabilities: { tools: {} }, serverInfo: { name: 'addai-codeflows', version: '1.0.0' } });
64
+ return;
65
+ }
66
+ if (q.method === 'ping') {
67
+ reply({});
68
+ return;
69
+ }
70
+ if (!initialized) {
71
+ error(-32002, 'Initialize first.');
72
+ return;
73
+ }
74
+ if (q.method === 'tools/list') {
75
+ reply({ tools: aios_tools_1.AIOS_TOOLS });
76
+ return;
77
+ }
78
+ if (q.method !== 'tools/call') {
79
+ error(-32601, 'Method not found.');
80
+ return;
81
+ }
82
+ if (active >= 8) {
83
+ error(-32000, 'Too many pending +Ai calls.');
84
+ return;
85
+ }
86
+ active++;
87
+ try {
88
+ const result = await (0, aios_bridge_1.callAiosBridge)(config, q.params?.name, q.params?.arguments ?? {});
89
+ reply({ content: [{ type: 'text', text: JSON.stringify(result) }] });
90
+ }
91
+ catch (err) {
92
+ reply({ isError: true, content: [{ type: 'text', text: err.message.slice(0, 500) }] });
93
+ }
94
+ finally {
95
+ active--;
96
+ }
97
+ }
98
+ process.stdin.setEncoding('utf8');
99
+ process.stdin.on('data', chunk => {
100
+ buffer += chunk;
101
+ // Bound both incomplete frames and batches delivered in one read.
102
+ if (Buffer.byteLength(buffer) > aios_tools_1.AIOS_LIMIT) {
103
+ process.exitCode = 1;
104
+ process.stdin.destroy();
105
+ return;
106
+ }
107
+ let end;
108
+ while ((end = buffer.indexOf('\n')) >= 0) {
109
+ const line = buffer.slice(0, end);
110
+ buffer = buffer.slice(end + 1);
111
+ if (!line.trim())
112
+ continue;
113
+ try {
114
+ void handle(JSON.parse(line));
115
+ }
116
+ catch {
117
+ send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Invalid JSON.' } });
118
+ }
119
+ }
120
+ });
121
+ process.stdout.on('error', () => process.exit(0));
122
+ process.stdin.on('end', () => process.exit(0));
123
+ }
124
+ if (require.main === module) {
125
+ try {
126
+ runAiosMcp(process.argv[2]);
127
+ }
128
+ catch {
129
+ process.stderr.write('CodeFlows +Ai connection could not start.\n');
130
+ process.exitCode = 1;
131
+ }
132
+ }