@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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readEntityboxCapacity, type EntityboxCapacityConfiguration, type EntityboxCapacityPlan, type EntityboxCapacityReceipt } from './entitybox-capacity-provisioner';
|
|
2
|
+
type Rpc = <T>(name: string, args: Record<string, unknown>) => Promise<T>;
|
|
3
|
+
export interface EntityboxCapacityAllocation {
|
|
4
|
+
id: string;
|
|
5
|
+
runtimeId: string;
|
|
6
|
+
desktopId: string;
|
|
7
|
+
}
|
|
8
|
+
export interface EntityboxCapacityRegistrationConfiguration {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
recoverPublication?: boolean;
|
|
11
|
+
allocation: EntityboxCapacityAllocation;
|
|
12
|
+
imageId: string;
|
|
13
|
+
directory: string;
|
|
14
|
+
dockerContext?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface EntityboxCapacityRegistrationIO {
|
|
17
|
+
pairing: () => {
|
|
18
|
+
runtimeId: string;
|
|
19
|
+
daemonToken: string;
|
|
20
|
+
} | null;
|
|
21
|
+
rpc: Rpc;
|
|
22
|
+
/** Fixture-only injection. Host callers normally use the actual provisioner. */
|
|
23
|
+
create?: (configuration: EntityboxCapacityConfiguration) => {
|
|
24
|
+
plan: Readonly<EntityboxCapacityPlan>;
|
|
25
|
+
provision: () => Promise<Readonly<EntityboxCapacityReceipt>>;
|
|
26
|
+
} | undefined;
|
|
27
|
+
read?: typeof readEntityboxCapacity;
|
|
28
|
+
}
|
|
29
|
+
/** One provisioning owner, with native SQL claim BEFORE Docker work and
|
|
30
|
+
* verified physical readiness BEFORE native publication. No request-pump or
|
|
31
|
+
* timer registration occurs merely by importing this module. A fresh process
|
|
32
|
+
* cannot steal an uncertain claim; setup recovery is a separate contract. */
|
|
33
|
+
export declare function createEntityboxCapacityRegistration(config: EntityboxCapacityRegistrationConfiguration, io: EntityboxCapacityRegistrationIO): {
|
|
34
|
+
provision: () => Promise<Readonly<EntityboxCapacityReceipt>>;
|
|
35
|
+
} | undefined;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEntityboxCapacityRegistration = createEntityboxCapacityRegistration;
|
|
4
|
+
const entitybox_capacity_owner_1 = require("./entitybox-capacity-owner");
|
|
5
|
+
const entitybox_capacity_publication_1 = require("./entitybox-capacity-publication");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const entitybox_capacity_provisioner_1 = require("./entitybox-capacity-provisioner");
|
|
8
|
+
const entitybox_recovery_store_1 = require("./entitybox-recovery-store");
|
|
9
|
+
const entitybox_execution_lease_1 = require("./entitybox-execution-lease");
|
|
10
|
+
const uuid = /^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/, fail = () => new entitybox_execution_lease_1.EntityboxExecutionError('Dedicated computer registration could not be verified');
|
|
11
|
+
/** One provisioning owner, with native SQL claim BEFORE Docker work and
|
|
12
|
+
* verified physical readiness BEFORE native publication. No request-pump or
|
|
13
|
+
* timer registration occurs merely by importing this module. A fresh process
|
|
14
|
+
* cannot steal an uncertain claim; setup recovery is a separate contract. */
|
|
15
|
+
function createEntityboxCapacityRegistration(config, io) {
|
|
16
|
+
if (config.enabled !== true)
|
|
17
|
+
return undefined;
|
|
18
|
+
const allocation = Object.freeze({ id: config.allocation?.id, runtimeId: config.allocation?.runtimeId, desktopId: config.allocation?.desktopId });
|
|
19
|
+
if (!allocation || ![allocation.id, allocation.runtimeId, allocation.desktopId].every(v => uuid.test(v)))
|
|
20
|
+
throw fail();
|
|
21
|
+
const recovery = config.recoverPublication === true, nonce = (0, crypto_1.randomUUID)(), directory = config.directory, context = config.dockerContext ?? 'desktop-linux';
|
|
22
|
+
let password = (0, crypto_1.randomBytes)(6).toString('base64url');
|
|
23
|
+
const candidate = (io.create ?? (recovery ? entitybox_capacity_owner_1.createEntityboxCapacityOwner : entitybox_capacity_provisioner_1.EntityboxCapacityProvisioner.create))({ enabled: true, runtimeId: allocation.runtimeId, desktopId: allocation.desktopId, imageId: config.imageId, directory, dockerContext: context, vncPassword: password });
|
|
24
|
+
if (!candidate)
|
|
25
|
+
throw fail();
|
|
26
|
+
const owner = candidate;
|
|
27
|
+
const plan = JSON.parse((0, entitybox_recovery_store_1.entityboxCanonical)(owner.plan));
|
|
28
|
+
if (plan.runtimeId !== allocation.runtimeId || plan.desktopId !== allocation.desktopId || plan.dockerContext !== context || plan.imageId !== config.imageId)
|
|
29
|
+
throw fail();
|
|
30
|
+
let flight, physical, complete;
|
|
31
|
+
const token = () => { const p = io.pairing(); if (!p || p.runtimeId !== allocation.runtimeId || typeof p.daemonToken !== 'string' || !p.daemonToken)
|
|
32
|
+
throw fail(); return p.daemonToken; };
|
|
33
|
+
function validate(reply, phase) {
|
|
34
|
+
if (!reply || reply.id !== allocation.id || reply.runtimeId !== allocation.runtimeId || reply.desktopId !== allocation.desktopId || reply.phase !== phase
|
|
35
|
+
|| (0, entitybox_recovery_store_1.entityboxCanonical)(reply.plan) !== (0, entitybox_recovery_store_1.entityboxCanonical)(plan))
|
|
36
|
+
throw fail();
|
|
37
|
+
if (phase === 'ready' && (!physical || (0, entitybox_recovery_store_1.entityboxCanonical)(reply.receipt) !== (0, entitybox_recovery_store_1.entityboxCanonical)(physical)))
|
|
38
|
+
throw fail();
|
|
39
|
+
}
|
|
40
|
+
async function run() {
|
|
41
|
+
// Persist the original authority before any SQL claim. Opt-in only; this
|
|
42
|
+
// encrypted record permits read-only publication recovery after a restart.
|
|
43
|
+
if (recovery)
|
|
44
|
+
new entitybox_capacity_publication_1.EntityboxCapacityPublicationStore(directory, true).write({ version: 1, allocation, plan, nonce, password }, token());
|
|
45
|
+
const claimed = await io.rpc('entitybox_native_capacity_claim', { p_token: token(), p_id: allocation.id, p_nonce: nonce, p_plan: plan });
|
|
46
|
+
if (claimed?.phase === 'ready') {
|
|
47
|
+
// A lost publication response may be retried by THIS owner only. Its exact
|
|
48
|
+
// already-created physical receipt must match; nothing is created again.
|
|
49
|
+
validate(claimed, 'ready');
|
|
50
|
+
complete = physical;
|
|
51
|
+
password = '';
|
|
52
|
+
return complete;
|
|
53
|
+
}
|
|
54
|
+
validate(claimed, 'provisioning');
|
|
55
|
+
token();
|
|
56
|
+
physical ??= await owner.provision();
|
|
57
|
+
// A reply lost after Docker startup does not grant permission to re-create.
|
|
58
|
+
// The provisioner's own one-use journal enforces this on retry as well.
|
|
59
|
+
const verified = await (io.read ?? entitybox_capacity_provisioner_1.readEntityboxCapacity)(directory, allocation.desktopId, allocation.runtimeId);
|
|
60
|
+
if ((0, entitybox_recovery_store_1.entityboxCanonical)(verified) !== (0, entitybox_recovery_store_1.entityboxCanonical)(physical))
|
|
61
|
+
throw fail();
|
|
62
|
+
const result = await io.rpc('entitybox_native_capacity_publish', { p_token: token(), p_id: allocation.id, p_nonce: nonce, p_receipt: physical, p_vnc_password: password });
|
|
63
|
+
validate(result, 'ready');
|
|
64
|
+
complete = physical;
|
|
65
|
+
password = '';
|
|
66
|
+
return complete;
|
|
67
|
+
}
|
|
68
|
+
return { provision: () => {
|
|
69
|
+
if (complete)
|
|
70
|
+
return Promise.resolve(complete);
|
|
71
|
+
if (!flight)
|
|
72
|
+
flight = run().catch(() => { throw fail(); }).finally(() => { flight = undefined; });
|
|
73
|
+
return flight;
|
|
74
|
+
} };
|
|
75
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createEntityboxCapacityPublicationRecovery } from './entitybox-capacity-publication';
|
|
2
|
+
import { type EntityboxCapacityRegistrationConfiguration, type EntityboxCapacityRegistrationIO } from './entitybox-capacity-registration';
|
|
3
|
+
import type { EntityboxCapacityReceipt } from './entitybox-capacity-provisioner';
|
|
4
|
+
export interface EntityboxCapacityWorkerConfiguration {
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
recoverPublication?: boolean;
|
|
7
|
+
runtimeId: string;
|
|
8
|
+
imageId: string;
|
|
9
|
+
directory: string;
|
|
10
|
+
dockerContext: string;
|
|
11
|
+
}
|
|
12
|
+
export interface EntityboxCapacityWorkerIO extends EntityboxCapacityRegistrationIO {
|
|
13
|
+
/** Hold the shared native start gate through claim, Docker startup and publish. */
|
|
14
|
+
admit: (id: string, createdAt: string, action: () => Promise<void>, publicationOnly?: boolean) => Promise<boolean>;
|
|
15
|
+
registration?: (config: EntityboxCapacityRegistrationConfiguration, io: EntityboxCapacityRegistrationIO) => {
|
|
16
|
+
provision: () => Promise<Readonly<EntityboxCapacityReceipt>>;
|
|
17
|
+
} | undefined;
|
|
18
|
+
publicationRecovery?: typeof createEntityboxCapacityPublicationRecovery;
|
|
19
|
+
clock?: () => number;
|
|
20
|
+
report?: (state: 'ready' | 'waiting' | 'attention' | 'unavailable', desktopId?: string) => void;
|
|
21
|
+
}
|
|
22
|
+
/** One live owner per allocation. Opt-in restart recovery may republish an
|
|
23
|
+
* exact finished receipt, never recreate an uncertain plan or its resources. */
|
|
24
|
+
export declare function createEntityboxCapacityWorker(config: EntityboxCapacityWorkerConfiguration, io: EntityboxCapacityWorkerIO): {
|
|
25
|
+
tick: () => Promise<void>;
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): void;
|
|
28
|
+
drain(timeoutMs?: number): Promise<boolean>;
|
|
29
|
+
} | undefined;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEntityboxCapacityWorker = createEntityboxCapacityWorker;
|
|
4
|
+
const entitybox_capacity_publication_1 = require("./entitybox-capacity-publication");
|
|
5
|
+
const entitybox_capacity_registration_1 = require("./entitybox-capacity-registration");
|
|
6
|
+
const uuid = /^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;
|
|
7
|
+
/** One live owner per allocation. Opt-in restart recovery may republish an
|
|
8
|
+
* exact finished receipt, never recreate an uncertain plan or its resources. */
|
|
9
|
+
function createEntityboxCapacityWorker(config, io) {
|
|
10
|
+
if (config.enabled !== true)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (!uuid.test(config.runtimeId) || !/^sha256:[a-f0-9]{64}$/.test(config.imageId) || !/^[-a-zA-Z0-9_.]{1,80}$/.test(config.dockerContext) || typeof config.directory !== 'string' || !config.directory)
|
|
13
|
+
throw new Error('Invalid EntityBox capacity worker configuration');
|
|
14
|
+
const configuration = Object.freeze({ ...config }), now = io.clock ?? Date.now;
|
|
15
|
+
const owners = new Map();
|
|
16
|
+
let stopped = false, flight, timer, started = false;
|
|
17
|
+
const token = () => { const p = io.pairing(); if (stopped || !p || p.runtimeId !== configuration.runtimeId || !p.daemonToken)
|
|
18
|
+
throw new Error('Unavailable'); return p.daemonToken; };
|
|
19
|
+
const report = (state, id) => { try {
|
|
20
|
+
io.report?.(state, id);
|
|
21
|
+
}
|
|
22
|
+
catch { } };
|
|
23
|
+
async function inventory() {
|
|
24
|
+
const all = [], seen = new Set();
|
|
25
|
+
let after = null;
|
|
26
|
+
for (let page = 0; page < 10; page++) {
|
|
27
|
+
const rows = await io.rpc('entitybox_native_capacity_inventory', { p_token: token(), p_after: after, p_limit: 50 });
|
|
28
|
+
token();
|
|
29
|
+
if (!Array.isArray(rows) || rows.length > 50)
|
|
30
|
+
throw new Error('Unavailable');
|
|
31
|
+
for (const r of rows) {
|
|
32
|
+
if (!r || ![r.id, r.runtimeId, r.desktopId].every(v => typeof v === 'string' && uuid.test(v)) || r.runtimeId !== configuration.runtimeId || !['reserved', 'provisioning', 'ready'].includes(r.phase) || typeof r.createdAt !== 'string' || !Number.isFinite(Date.parse(r.createdAt)) || seen.has(r.id) || after !== null && r.id <= after)
|
|
33
|
+
throw new Error('Unavailable');
|
|
34
|
+
seen.add(r.id);
|
|
35
|
+
after = r.id;
|
|
36
|
+
all.push({ id: r.id, runtimeId: r.runtimeId, desktopId: r.desktopId, phase: r.phase, createdAt: r.createdAt });
|
|
37
|
+
}
|
|
38
|
+
if (rows.length < 50)
|
|
39
|
+
return all.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt) || a.id.localeCompare(b.id));
|
|
40
|
+
}
|
|
41
|
+
throw new Error('Unavailable');
|
|
42
|
+
}
|
|
43
|
+
async function run() {
|
|
44
|
+
try {
|
|
45
|
+
const rows = await inventory();
|
|
46
|
+
for (const allocation of rows) {
|
|
47
|
+
if (stopped)
|
|
48
|
+
return;
|
|
49
|
+
let retained = owners.get(allocation.id);
|
|
50
|
+
if (retained && (retained.allocation.desktopId !== allocation.desktopId || retained.allocation.runtimeId !== allocation.runtimeId))
|
|
51
|
+
throw new Error('Unavailable');
|
|
52
|
+
if (allocation.phase === 'ready') {
|
|
53
|
+
owners.delete(allocation.id);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (allocation.phase === 'provisioning' && !retained && configuration.recoverPublication !== true) {
|
|
57
|
+
report('attention', allocation.desktopId);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (retained && now() < retained.retryAt)
|
|
61
|
+
continue;
|
|
62
|
+
try {
|
|
63
|
+
const publicationOnly = retained?.publicationOnly ?? allocation.phase === 'provisioning';
|
|
64
|
+
const admitted = await io.admit(allocation.desktopId, allocation.createdAt, async () => {
|
|
65
|
+
token();
|
|
66
|
+
if (!retained) {
|
|
67
|
+
if (owners.size >= 500)
|
|
68
|
+
throw new Error('Unavailable');
|
|
69
|
+
const factory = allocation.phase === 'provisioning' ? (io.publicationRecovery ?? entitybox_capacity_publication_1.createEntityboxCapacityPublicationRecovery) : (io.registration ?? entitybox_capacity_registration_1.createEntityboxCapacityRegistration);
|
|
70
|
+
const owner = factory({ ...configuration, allocation }, { ...io, pairing: () => { token(); return io.pairing(); } });
|
|
71
|
+
if (!owner)
|
|
72
|
+
throw new Error('Unavailable');
|
|
73
|
+
retained = { allocation, owner, failures: 0, retryAt: 0, publicationOnly };
|
|
74
|
+
owners.set(allocation.id, retained);
|
|
75
|
+
}
|
|
76
|
+
await retained.owner.provision();
|
|
77
|
+
token();
|
|
78
|
+
owners.delete(allocation.id);
|
|
79
|
+
report('ready', allocation.desktopId);
|
|
80
|
+
}, publicationOnly);
|
|
81
|
+
if (!admitted)
|
|
82
|
+
report('waiting', allocation.desktopId);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
if (retained) {
|
|
86
|
+
retained.failures++;
|
|
87
|
+
retained.retryAt = now() + Math.min(300000, 5000 * 2 ** Math.min(retained.failures - 1, 6));
|
|
88
|
+
}
|
|
89
|
+
report('attention', allocation.desktopId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
if (!stopped)
|
|
95
|
+
report('unavailable');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function tick() { if (stopped)
|
|
99
|
+
return Promise.resolve(); if (!flight)
|
|
100
|
+
flight = run().finally(() => { flight = undefined; }); return flight; }
|
|
101
|
+
function schedule() { if (stopped)
|
|
102
|
+
return; timer = setTimeout(() => { void tick().finally(schedule); }, 15000); timer.unref?.(); }
|
|
103
|
+
return { tick, start() { if (started || stopped)
|
|
104
|
+
return; started = true; void tick().finally(schedule); }, stop() { stopped = true; clearTimeout(timer); },
|
|
105
|
+
async drain(timeoutMs = 15000) { if (!flight)
|
|
106
|
+
return true; let deadline; try {
|
|
107
|
+
return await Promise.race([flight.then(() => true), new Promise(resolve => { deadline = setTimeout(() => resolve(false), Math.max(0, timeoutMs)); })]);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
clearTimeout(deadline);
|
|
111
|
+
} },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { EntityboxExecutionContainer, type EntityboxExecutionResource } from './entitybox-execution-container';
|
|
2
|
+
import { type EntityboxExecutionLease } from './entitybox-execution-lease';
|
|
3
|
+
import { EntityboxMcpBroker } from './entitybox-mcp-broker';
|
|
4
|
+
import { EntityboxProviderBroker } from './entitybox-provider-broker';
|
|
5
|
+
import type { EntityboxDisplayBinding } from './entitybox-display-binding';
|
|
6
|
+
import type { RuntimeEvent, RuntimePermissionMode, RuntimeEffortLevel } from './types';
|
|
7
|
+
export interface EntityboxComputerTaskOptions {
|
|
8
|
+
/** Trusted request owner observes startup before any resource creation. */
|
|
9
|
+
onTask?: (task: EntityboxComputerTask) => void;
|
|
10
|
+
imageId: string;
|
|
11
|
+
dockerContext?: string;
|
|
12
|
+
display: EntityboxDisplayBinding;
|
|
13
|
+
/** Trusted model/provider selection from the native request adapter. */
|
|
14
|
+
provider: ConstructorParameters<typeof EntityboxProviderBroker>[1];
|
|
15
|
+
connection: Parameters<typeof EntityboxMcpBroker.connect>[1]['connection'];
|
|
16
|
+
desktopTools: readonly string[];
|
|
17
|
+
task: {
|
|
18
|
+
prompt: string;
|
|
19
|
+
model: string;
|
|
20
|
+
systemPrompt?: string;
|
|
21
|
+
permissionMode?: RuntimePermissionMode;
|
|
22
|
+
effort?: RuntimeEffortLevel;
|
|
23
|
+
};
|
|
24
|
+
/** Await durable native event writes. A failed sink fences the task. */
|
|
25
|
+
onEvent: (event: RuntimeEvent) => Promise<void>;
|
|
26
|
+
/** Record created executor identity before any task process can start. */
|
|
27
|
+
onContainerCreated?: (resource: Readonly<EntityboxExecutionResource>) => Promise<void>;
|
|
28
|
+
/** Register this exact owned container before exposing desktop tools. */
|
|
29
|
+
onContainer: (container: EntityboxExecutionContainer) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export interface EntityboxComputerTaskResult {
|
|
32
|
+
code: number;
|
|
33
|
+
taskProcessesStopped: true;
|
|
34
|
+
}
|
|
35
|
+
/** One native CLI/provider/MCP lifecycle. This does not pick requests, resolve
|
|
36
|
+
* credentials, provision capacity or acknowledge the SQL lease. A caller must
|
|
37
|
+
* aggregate any persistent-application/takeover proof before releasing it. */
|
|
38
|
+
export declare class EntityboxComputerTask {
|
|
39
|
+
private readonly lease;
|
|
40
|
+
private readonly options;
|
|
41
|
+
readonly done: Promise<EntityboxComputerTaskResult>;
|
|
42
|
+
readonly artifactSha256: string;
|
|
43
|
+
private container?;
|
|
44
|
+
private bridge?;
|
|
45
|
+
private broker?;
|
|
46
|
+
private readonly connectionReady;
|
|
47
|
+
private readonly startupSettled;
|
|
48
|
+
private startupResolve;
|
|
49
|
+
private connectionResolve;
|
|
50
|
+
private connectionReject;
|
|
51
|
+
private doneResolve;
|
|
52
|
+
private doneReject;
|
|
53
|
+
private eventChain;
|
|
54
|
+
private queuedBytes;
|
|
55
|
+
private output;
|
|
56
|
+
private readonly decoder;
|
|
57
|
+
private completion?;
|
|
58
|
+
private failed;
|
|
59
|
+
private sinkFailed;
|
|
60
|
+
private stopping?;
|
|
61
|
+
private constructor();
|
|
62
|
+
static start(lease: EntityboxExecutionLease, options: EntityboxComputerTaskOptions): Promise<EntityboxComputerTask>;
|
|
63
|
+
get containerId(): string | undefined;
|
|
64
|
+
get mcpProcessId(): number | undefined;
|
|
65
|
+
requestStop(): void;
|
|
66
|
+
private consume;
|
|
67
|
+
private finish;
|
|
68
|
+
stopAndVerify(): Promise<void>;
|
|
69
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EntityboxComputerTask = void 0;
|
|
4
|
+
const entitybox_execution_container_1 = require("./entitybox-execution-container");
|
|
5
|
+
const entitybox_execution_lease_1 = require("./entitybox-execution-lease");
|
|
6
|
+
const entitybox_desktop_action_server_1 = require("./entitybox-desktop-action-server");
|
|
7
|
+
const entitybox_mcp_broker_1 = require("./entitybox-mcp-broker");
|
|
8
|
+
const entitybox_provider_broker_1 = require("./entitybox-provider-broker");
|
|
9
|
+
const entitybox_model_artifact_1 = require("./entitybox-model-artifact");
|
|
10
|
+
const string_decoder_1 = require("string_decoder");
|
|
11
|
+
async function bounded(operation, ms) {
|
|
12
|
+
let timer;
|
|
13
|
+
try {
|
|
14
|
+
return await Promise.race([operation, new Promise((_, reject) => { timer = setTimeout(() => reject(new entitybox_execution_lease_1.EntityboxExecutionError('Native task operation timed out')), ms); })]);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** One native CLI/provider/MCP lifecycle. This does not pick requests, resolve
|
|
21
|
+
* credentials, provision capacity or acknowledge the SQL lease. A caller must
|
|
22
|
+
* aggregate any persistent-application/takeover proof before releasing it. */
|
|
23
|
+
class EntityboxComputerTask {
|
|
24
|
+
lease;
|
|
25
|
+
options;
|
|
26
|
+
done;
|
|
27
|
+
artifactSha256;
|
|
28
|
+
container;
|
|
29
|
+
bridge;
|
|
30
|
+
broker;
|
|
31
|
+
connectionReady;
|
|
32
|
+
startupSettled;
|
|
33
|
+
startupResolve;
|
|
34
|
+
connectionResolve;
|
|
35
|
+
connectionReject;
|
|
36
|
+
doneResolve;
|
|
37
|
+
doneReject;
|
|
38
|
+
eventChain = Promise.resolve();
|
|
39
|
+
queuedBytes = 0;
|
|
40
|
+
output = '';
|
|
41
|
+
decoder = new string_decoder_1.StringDecoder('utf8');
|
|
42
|
+
completion;
|
|
43
|
+
failed = false;
|
|
44
|
+
sinkFailed = false;
|
|
45
|
+
stopping;
|
|
46
|
+
constructor(lease, options, artifactSha256) {
|
|
47
|
+
this.lease = lease;
|
|
48
|
+
this.options = options;
|
|
49
|
+
this.artifactSha256 = artifactSha256;
|
|
50
|
+
this.startupSettled = new Promise(resolve => { this.startupResolve = resolve; });
|
|
51
|
+
this.connectionReady = new Promise((resolve, reject) => { this.connectionResolve = resolve; this.connectionReject = reject; });
|
|
52
|
+
this.connectionReady.catch(() => { });
|
|
53
|
+
this.done = new Promise((resolve, reject) => { this.doneResolve = resolve; this.doneReject = reject; });
|
|
54
|
+
this.done.catch(() => { });
|
|
55
|
+
lease.signal.addEventListener('abort', () => { this.connectionReject(new entitybox_execution_lease_1.EntityboxExecutionError('Computer task ended')); void this.stopAndVerify().catch(() => { }); }, { once: true });
|
|
56
|
+
}
|
|
57
|
+
static async start(lease, options) {
|
|
58
|
+
lease.assertActive();
|
|
59
|
+
const artifact = (0, entitybox_model_artifact_1.readEntityboxModelArtifact)();
|
|
60
|
+
if (!options.display || typeof options.onEvent !== 'function' || typeof options.onContainer !== 'function'
|
|
61
|
+
|| !options.provider.models.includes(options.task.model) || !options.desktopTools.length)
|
|
62
|
+
throw new entitybox_execution_lease_1.EntityboxExecutionError('Invalid native computer task');
|
|
63
|
+
const provider = new entitybox_provider_broker_1.EntityboxProviderBroker(lease, options.provider), task = new EntityboxComputerTask(lease, options, artifact.sha256);
|
|
64
|
+
try {
|
|
65
|
+
options.onTask?.(task);
|
|
66
|
+
task.container = await entitybox_execution_container_1.EntityboxExecutionContainer.start(lease, { imageId: options.imageId, dockerContext: options.dockerContext, memoryMiB: 1536, display: options.display,
|
|
67
|
+
onCreated: options.onContainerCreated, command: ['node', '-e', entitybox_model_artifact_1.ENTITYBOX_MODEL_LOADER], task: { files: artifact.files, task: { ...options.task, desktopMcp: true, allowedTools: options.desktopTools.map(name => 'mcp__entity-desktop-mcp__' + name) } },
|
|
68
|
+
onOutput: data => task.consume(data), onBroker: async (raw, signal, emit) => {
|
|
69
|
+
await task.connectionReady;
|
|
70
|
+
lease.assertActive();
|
|
71
|
+
if (raw?.kind === 'desktop_mcp') {
|
|
72
|
+
if (!task.broker)
|
|
73
|
+
throw new Error();
|
|
74
|
+
await task.broker.handle(raw, signal, emit);
|
|
75
|
+
}
|
|
76
|
+
else
|
|
77
|
+
await provider.handle(raw, signal, emit);
|
|
78
|
+
} });
|
|
79
|
+
await bounded(options.onContainer(task.container), 10000);
|
|
80
|
+
await task.container.whenStarted();
|
|
81
|
+
task.bridge = await entitybox_desktop_action_server_1.EntityboxDesktopActionServer.start(lease, task.container);
|
|
82
|
+
task.broker = await entitybox_mcp_broker_1.EntityboxMcpBroker.connect(lease, { connection: options.connection, allowedTools: options.desktopTools, actions: task.bridge });
|
|
83
|
+
lease.assertActive();
|
|
84
|
+
task.connectionResolve();
|
|
85
|
+
task.startupResolve();
|
|
86
|
+
void task.finish();
|
|
87
|
+
return task;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
task.failed = true;
|
|
91
|
+
task.connectionReject(new entitybox_execution_lease_1.EntityboxExecutionError('Computer task startup failed'));
|
|
92
|
+
lease.fence();
|
|
93
|
+
task.startupResolve();
|
|
94
|
+
try {
|
|
95
|
+
await task.stopAndVerify();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new entitybox_execution_lease_1.EntityboxExecutionError('Computer task startup failed; resource stop remains unverified');
|
|
99
|
+
}
|
|
100
|
+
throw new entitybox_execution_lease_1.EntityboxExecutionError('Computer task could not start');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
get containerId() { return this.container?.id; }
|
|
104
|
+
get mcpProcessId() { return this.broker?.processId; }
|
|
105
|
+
requestStop() { this.lease.fence(); }
|
|
106
|
+
consume(data) {
|
|
107
|
+
try {
|
|
108
|
+
this.output += this.decoder.write(data);
|
|
109
|
+
if (Buffer.byteLength(this.output) > 32 * 1024 * 1024)
|
|
110
|
+
throw new Error();
|
|
111
|
+
let end;
|
|
112
|
+
while ((end = this.output.indexOf('\n')) >= 0) {
|
|
113
|
+
const line = this.output.slice(0, end);
|
|
114
|
+
this.output = this.output.slice(end + 1);
|
|
115
|
+
const event = JSON.parse(line);
|
|
116
|
+
if (!event || typeof event.type !== 'string')
|
|
117
|
+
throw new Error();
|
|
118
|
+
if (event.type === 'error')
|
|
119
|
+
this.failed = true;
|
|
120
|
+
// A model completion event is advisory until owned processes stop and
|
|
121
|
+
// the preceding native event writes have settled successfully.
|
|
122
|
+
if (event.type === 'turn_complete') {
|
|
123
|
+
this.completion = event;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const size = Buffer.byteLength(line);
|
|
127
|
+
this.queuedBytes += size;
|
|
128
|
+
if (this.queuedBytes > 32 * 1024 * 1024)
|
|
129
|
+
throw new Error();
|
|
130
|
+
this.eventChain = this.eventChain.then(async () => { if (!this.sinkFailed)
|
|
131
|
+
await bounded(this.options.onEvent(event), 15000); }).catch(() => { this.sinkFailed = true; this.failed = true; this.lease.fence(); }).finally(() => { this.queuedBytes -= size; });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
this.failed = true;
|
|
136
|
+
this.lease.fence();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async finish() {
|
|
140
|
+
try {
|
|
141
|
+
const code = await this.container.done;
|
|
142
|
+
this.output += this.decoder.end();
|
|
143
|
+
const completed = code === 0 && !this.failed && !this.lease.signal.aborted && !this.output && this.completion?.type === 'turn_complete' && this.completion.stopReason === 'success';
|
|
144
|
+
await this.stopAndVerify();
|
|
145
|
+
await this.eventChain;
|
|
146
|
+
const success = completed && !this.failed;
|
|
147
|
+
if (success && this.completion)
|
|
148
|
+
await bounded(this.options.onEvent(this.completion), 15000);
|
|
149
|
+
this.doneResolve({ code: success ? 0 : 70, taskProcessesStopped: true });
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
this.lease.fence();
|
|
153
|
+
this.doneReject(new entitybox_execution_lease_1.EntityboxExecutionError('Computer task stop could not be confirmed'));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async stopAndVerify() {
|
|
157
|
+
if (this.stopping)
|
|
158
|
+
return this.stopping;
|
|
159
|
+
this.stopping = Promise.resolve().then(async () => {
|
|
160
|
+
await this.startupSettled;
|
|
161
|
+
this.connectionReject(new entitybox_execution_lease_1.EntityboxExecutionError('Computer task ended'));
|
|
162
|
+
const results = await Promise.allSettled([this.container?.stopAndVerify(), this.broker?.stopAndVerify(), this.bridge?.stopAndVerify()]);
|
|
163
|
+
if (results.some(r => r.status === 'rejected'))
|
|
164
|
+
throw new entitybox_execution_lease_1.EntityboxExecutionError('Computer task resources have not stopped');
|
|
165
|
+
});
|
|
166
|
+
this.lease.fence();
|
|
167
|
+
try {
|
|
168
|
+
await this.stopping;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
this.stopping = undefined;
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.EntityboxComputerTask = EntityboxComputerTask;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type EntityboxExecutionIdentity } from './entitybox-execution-lease';
|
|
2
|
+
import { type EntityboxApplicationDocker } from './entitybox-application-boundary';
|
|
3
|
+
import { type EntityboxHostProcess } from './entitybox-host-process';
|
|
4
|
+
import { type EntityboxRecoveryStore } from './entitybox-recovery-store';
|
|
5
|
+
type Rpc = <T>(name: string, args: Record<string, unknown>) => Promise<T>;
|
|
6
|
+
export interface EntityboxControllerRecoveryOptions {
|
|
7
|
+
identity: EntityboxExecutionIdentity;
|
|
8
|
+
store: EntityboxRecoveryStore;
|
|
9
|
+
dockerContext?: string;
|
|
10
|
+
token: () => string;
|
|
11
|
+
rpc: Rpc;
|
|
12
|
+
/** Trusted native test/transport seams, never values from the model. */
|
|
13
|
+
docker?: EntityboxApplicationDocker;
|
|
14
|
+
readProcess?: () => Promise<Readonly<EntityboxHostProcess> | null>;
|
|
15
|
+
assertGone?: (p: EntityboxHostProcess) => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** Retain this object across failed/lost replies: the recovery nonce is private,
|
|
18
|
+
* and retries use the same ID. No timeout hands recovery to another process.
|
|
19
|
+
* A completed attempt returns BEFORE any Docker operation, including replay
|
|
20
|
+
* after a replacement task has resumed the retained application. */
|
|
21
|
+
export declare class EntityboxControllerRecovery {
|
|
22
|
+
private readonly options;
|
|
23
|
+
private readonly id;
|
|
24
|
+
private readonly nonce;
|
|
25
|
+
private readonly identity;
|
|
26
|
+
private readonly context;
|
|
27
|
+
private readonly docker;
|
|
28
|
+
private owner?;
|
|
29
|
+
private pending?;
|
|
30
|
+
private completed?;
|
|
31
|
+
constructor(options: EntityboxControllerRecoveryOptions);
|
|
32
|
+
run(): Promise<string>;
|
|
33
|
+
private call;
|
|
34
|
+
private snapshot;
|
|
35
|
+
private ended;
|
|
36
|
+
private recovery;
|
|
37
|
+
private recover;
|
|
38
|
+
private retire;
|
|
39
|
+
}
|
|
40
|
+
export {};
|