@addai/node 0.30.13 → 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 +17 -3
  4. package/dist/capabilities.js +18 -8
  5. package/dist/claude-models.d.ts +4 -12
  6. package/dist/claude-models.js +12 -88
  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
@@ -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
+ }