@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.
- package/dist/auto-update.d.ts +3 -0
- package/dist/auto-update.js +15 -0
- package/dist/capabilities.d.ts +31 -6
- package/dist/capabilities.js +48 -26
- package/dist/claude-models.d.ts +71 -0
- package/dist/claude-models.js +330 -0
- package/dist/claude-print.d.ts +15 -0
- package/dist/claude-print.js +16 -0
- package/dist/codex-models.d.ts +81 -0
- package/dist/codex-models.js +385 -0
- package/dist/codex-spawn.d.ts +14 -8
- package/dist/codex-spawn.js +30 -10
- package/dist/command-runner.js +31 -9
- package/dist/desktop/docker.js +2 -0
- package/dist/desktop/manager.d.ts +2 -1
- package/dist/desktop/manager.js +51 -1
- package/dist/desktop/provider.d.ts +1 -1
- package/dist/desktop/provider.js +2 -0
- package/dist/desktop/relay-client.js +7 -0
- package/dist/desktop/spec.d.ts +2 -0
- package/dist/direct/aios-bridge.d.ts +13 -0
- package/dist/direct/aios-bridge.js +177 -0
- package/dist/direct/aios-mcp.d.ts +2 -0
- package/dist/direct/aios-mcp.js +132 -0
- package/dist/direct/aios-tools.d.ts +351 -0
- package/dist/direct/aios-tools.js +120 -0
- package/dist/direct/board-chat-provider.d.ts +64 -0
- package/dist/direct/board-chat-provider.js +151 -0
- package/dist/direct/board-chat.d.ts +40 -0
- package/dist/direct/board-chat.js +414 -0
- package/dist/direct/config.d.ts +2 -0
- package/dist/direct/config.js +60 -0
- package/dist/direct/launch.d.ts +14 -0
- package/dist/direct/launch.js +121 -0
- package/dist/direct/protocol.d.ts +46 -0
- package/dist/direct/protocol.js +84 -0
- package/dist/direct/relay-main.d.ts +1 -0
- package/dist/direct/relay-main.js +21 -0
- package/dist/direct/relay.d.ts +26 -0
- package/dist/direct/relay.js +334 -0
- package/dist/direct/sessions.d.ts +61 -0
- package/dist/direct/sessions.js +368 -0
- package/dist/direct/supervisor.d.ts +9 -0
- package/dist/direct/supervisor.js +137 -0
- package/dist/direct/terminal.d.ts +1 -0
- package/dist/direct/terminal.js +106 -0
- package/dist/direct/worker.d.ts +1 -0
- package/dist/direct/worker.js +278 -0
- package/dist/entitybox/container-action.cjs +85 -0
- package/dist/entitybox/container-watchdog.cjs +70 -0
- package/dist/entitybox/model-entry.d.ts +3 -0
- package/dist/entitybox/model-entry.js +233 -0
- package/dist/entitybox/model-mcp-proxy.d.ts +3 -0
- package/dist/entitybox/model-mcp-proxy.js +85 -0
- package/dist/entitybox-application-boundary.d.ts +59 -0
- package/dist/entitybox-application-boundary.js +161 -0
- package/dist/entitybox-application-guard-core.d.ts +44 -0
- package/dist/entitybox-application-guard-core.js +164 -0
- package/dist/entitybox-application-guard-process.d.ts +1 -0
- package/dist/entitybox-application-guard-process.js +83 -0
- package/dist/entitybox-application-guard.d.ts +46 -0
- package/dist/entitybox-application-guard.js +239 -0
- package/dist/entitybox-capacity-health-host.d.ts +12 -0
- package/dist/entitybox-capacity-health-host.js +80 -0
- package/dist/entitybox-capacity-health-worker.d.ts +22 -0
- package/dist/entitybox-capacity-health-worker.js +168 -0
- package/dist/entitybox-capacity-host.d.ts +17 -0
- package/dist/entitybox-capacity-host.js +155 -0
- package/dist/entitybox-capacity-owner-process.d.ts +1 -0
- package/dist/entitybox-capacity-owner-process.js +32 -0
- package/dist/entitybox-capacity-owner.d.ts +16 -0
- package/dist/entitybox-capacity-owner.js +90 -0
- package/dist/entitybox-capacity-programs.d.ts +7 -0
- package/dist/entitybox-capacity-programs.js +49 -0
- package/dist/entitybox-capacity-provisioner.d.ts +82 -0
- package/dist/entitybox-capacity-provisioner.js +404 -0
- package/dist/entitybox-capacity-publication.d.ts +29 -0
- package/dist/entitybox-capacity-publication.js +199 -0
- package/dist/entitybox-capacity-registration.d.ts +36 -0
- package/dist/entitybox-capacity-registration.js +75 -0
- package/dist/entitybox-capacity-worker.d.ts +29 -0
- package/dist/entitybox-capacity-worker.js +113 -0
- package/dist/entitybox-computer-task.d.ts +69 -0
- package/dist/entitybox-computer-task.js +176 -0
- package/dist/entitybox-controller-recovery.d.ts +40 -0
- package/dist/entitybox-controller-recovery.js +156 -0
- package/dist/entitybox-desktop-action-server.d.ts +34 -0
- package/dist/entitybox-desktop-action-server.js +176 -0
- package/dist/entitybox-desktop-admission.d.ts +32 -0
- package/dist/entitybox-desktop-admission.js +79 -0
- package/dist/entitybox-display-binding.d.ts +13 -0
- package/dist/entitybox-display-binding.js +31 -0
- package/dist/entitybox-execution-container.d.ts +82 -0
- package/dist/entitybox-execution-container.js +450 -0
- package/dist/entitybox-execution-host.d.ts +40 -0
- package/dist/entitybox-execution-host.js +168 -0
- package/dist/entitybox-execution-lease.d.ts +81 -0
- package/dist/entitybox-execution-lease.js +332 -0
- package/dist/entitybox-file-inputs.d.ts +51 -0
- package/dist/entitybox-file-inputs.js +182 -0
- package/dist/entitybox-file-session.d.ts +38 -0
- package/dist/entitybox-file-session.js +82 -0
- package/dist/entitybox-host-process.d.ts +12 -0
- package/dist/entitybox-host-process.js +120 -0
- package/dist/entitybox-install.d.ts +10 -0
- package/dist/entitybox-install.js +40 -0
- package/dist/entitybox-mcp-broker.d.ts +77 -0
- package/dist/entitybox-mcp-broker.js +420 -0
- package/dist/entitybox-mcp-contract.d.ts +10 -0
- package/dist/entitybox-mcp-contract.js +24 -0
- package/dist/entitybox-mcp-guard.d.ts +3 -0
- package/dist/entitybox-mcp-guard.js +167 -0
- package/dist/entitybox-model-artifact.d.ts +9 -0
- package/dist/entitybox-model-artifact.js +83 -0
- package/dist/entitybox-native-services.d.ts +33 -0
- package/dist/entitybox-native-services.js +172 -0
- package/dist/entitybox-operation.d.ts +4 -0
- package/dist/entitybox-operation.js +34 -0
- package/dist/entitybox-provider-broker.d.ts +41 -0
- package/dist/entitybox-provider-broker.js +149 -0
- package/dist/entitybox-queue-request.d.ts +32 -0
- package/dist/entitybox-queue-request.js +141 -0
- package/dist/entitybox-recovery-host.d.ts +12 -0
- package/dist/entitybox-recovery-host.js +80 -0
- package/dist/entitybox-recovery-store.d.ts +33 -0
- package/dist/entitybox-recovery-store.js +150 -0
- package/dist/entitybox-recovery-worker.d.ts +30 -0
- package/dist/entitybox-recovery-worker.js +134 -0
- package/dist/entitybox-vault-provider.d.ts +27 -0
- package/dist/entitybox-vault-provider.js +127 -0
- package/dist/harness-registry.js +3 -1
- package/dist/index.js +72 -38
- package/dist/install.d.ts +14 -2
- package/dist/install.js +115 -10
- package/dist/model-cache.d.ts +38 -0
- package/dist/model-cache.js +137 -0
- package/dist/request-pump.d.ts +3 -0
- package/dist/request-pump.js +21 -7
- package/dist/session-runner.d.ts +8 -1
- package/dist/session-runner.js +48 -3
- package/dist/supabase-client.js +4 -1
- package/package.json +5 -1
- package/scripts/copy-assets.js +4 -2
package/dist/command-runner.js
CHANGED
|
@@ -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);
|
package/dist/desktop/docker.js
CHANGED
|
@@ -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
|
package/dist/desktop/manager.js
CHANGED
|
@@ -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
|
-
|
|
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[];
|
package/dist/desktop/provider.js
CHANGED
|
@@ -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
|
package/dist/desktop/spec.d.ts
CHANGED
|
@@ -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,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
|
+
}
|