@addai/node 0.30.12 → 0.30.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/auto-update.d.ts +3 -0
  2. package/dist/auto-update.js +15 -0
  3. package/dist/capabilities.d.ts +31 -6
  4. package/dist/capabilities.js +48 -26
  5. package/dist/claude-models.d.ts +71 -0
  6. package/dist/claude-models.js +330 -0
  7. package/dist/claude-print.d.ts +15 -0
  8. package/dist/claude-print.js +16 -0
  9. package/dist/codex-models.d.ts +81 -0
  10. package/dist/codex-models.js +385 -0
  11. package/dist/codex-spawn.d.ts +14 -8
  12. package/dist/codex-spawn.js +30 -10
  13. package/dist/command-runner.js +31 -9
  14. package/dist/desktop/docker.js +2 -0
  15. package/dist/desktop/manager.d.ts +2 -1
  16. package/dist/desktop/manager.js +51 -1
  17. package/dist/desktop/provider.d.ts +1 -1
  18. package/dist/desktop/provider.js +2 -0
  19. package/dist/desktop/relay-client.js +7 -0
  20. package/dist/desktop/spec.d.ts +2 -0
  21. package/dist/direct/aios-bridge.d.ts +13 -0
  22. package/dist/direct/aios-bridge.js +177 -0
  23. package/dist/direct/aios-mcp.d.ts +2 -0
  24. package/dist/direct/aios-mcp.js +132 -0
  25. package/dist/direct/aios-tools.d.ts +351 -0
  26. package/dist/direct/aios-tools.js +120 -0
  27. package/dist/direct/board-chat-provider.d.ts +64 -0
  28. package/dist/direct/board-chat-provider.js +151 -0
  29. package/dist/direct/board-chat.d.ts +40 -0
  30. package/dist/direct/board-chat.js +414 -0
  31. package/dist/direct/config.d.ts +2 -0
  32. package/dist/direct/config.js +60 -0
  33. package/dist/direct/launch.d.ts +14 -0
  34. package/dist/direct/launch.js +121 -0
  35. package/dist/direct/protocol.d.ts +46 -0
  36. package/dist/direct/protocol.js +84 -0
  37. package/dist/direct/relay-main.d.ts +1 -0
  38. package/dist/direct/relay-main.js +21 -0
  39. package/dist/direct/relay.d.ts +26 -0
  40. package/dist/direct/relay.js +334 -0
  41. package/dist/direct/sessions.d.ts +61 -0
  42. package/dist/direct/sessions.js +368 -0
  43. package/dist/direct/supervisor.d.ts +9 -0
  44. package/dist/direct/supervisor.js +137 -0
  45. package/dist/direct/terminal.d.ts +1 -0
  46. package/dist/direct/terminal.js +106 -0
  47. package/dist/direct/worker.d.ts +1 -0
  48. package/dist/direct/worker.js +278 -0
  49. package/dist/entitybox/container-action.cjs +85 -0
  50. package/dist/entitybox/container-watchdog.cjs +70 -0
  51. package/dist/entitybox/model-entry.d.ts +3 -0
  52. package/dist/entitybox/model-entry.js +233 -0
  53. package/dist/entitybox/model-mcp-proxy.d.ts +3 -0
  54. package/dist/entitybox/model-mcp-proxy.js +85 -0
  55. package/dist/entitybox-application-boundary.d.ts +59 -0
  56. package/dist/entitybox-application-boundary.js +161 -0
  57. package/dist/entitybox-application-guard-core.d.ts +44 -0
  58. package/dist/entitybox-application-guard-core.js +164 -0
  59. package/dist/entitybox-application-guard-process.d.ts +1 -0
  60. package/dist/entitybox-application-guard-process.js +83 -0
  61. package/dist/entitybox-application-guard.d.ts +46 -0
  62. package/dist/entitybox-application-guard.js +239 -0
  63. package/dist/entitybox-capacity-health-host.d.ts +12 -0
  64. package/dist/entitybox-capacity-health-host.js +80 -0
  65. package/dist/entitybox-capacity-health-worker.d.ts +22 -0
  66. package/dist/entitybox-capacity-health-worker.js +168 -0
  67. package/dist/entitybox-capacity-host.d.ts +17 -0
  68. package/dist/entitybox-capacity-host.js +155 -0
  69. package/dist/entitybox-capacity-owner-process.d.ts +1 -0
  70. package/dist/entitybox-capacity-owner-process.js +32 -0
  71. package/dist/entitybox-capacity-owner.d.ts +16 -0
  72. package/dist/entitybox-capacity-owner.js +90 -0
  73. package/dist/entitybox-capacity-programs.d.ts +7 -0
  74. package/dist/entitybox-capacity-programs.js +49 -0
  75. package/dist/entitybox-capacity-provisioner.d.ts +82 -0
  76. package/dist/entitybox-capacity-provisioner.js +404 -0
  77. package/dist/entitybox-capacity-publication.d.ts +29 -0
  78. package/dist/entitybox-capacity-publication.js +199 -0
  79. package/dist/entitybox-capacity-registration.d.ts +36 -0
  80. package/dist/entitybox-capacity-registration.js +75 -0
  81. package/dist/entitybox-capacity-worker.d.ts +29 -0
  82. package/dist/entitybox-capacity-worker.js +113 -0
  83. package/dist/entitybox-computer-task.d.ts +69 -0
  84. package/dist/entitybox-computer-task.js +176 -0
  85. package/dist/entitybox-controller-recovery.d.ts +40 -0
  86. package/dist/entitybox-controller-recovery.js +156 -0
  87. package/dist/entitybox-desktop-action-server.d.ts +34 -0
  88. package/dist/entitybox-desktop-action-server.js +176 -0
  89. package/dist/entitybox-desktop-admission.d.ts +32 -0
  90. package/dist/entitybox-desktop-admission.js +79 -0
  91. package/dist/entitybox-display-binding.d.ts +13 -0
  92. package/dist/entitybox-display-binding.js +31 -0
  93. package/dist/entitybox-execution-container.d.ts +82 -0
  94. package/dist/entitybox-execution-container.js +450 -0
  95. package/dist/entitybox-execution-host.d.ts +40 -0
  96. package/dist/entitybox-execution-host.js +168 -0
  97. package/dist/entitybox-execution-lease.d.ts +81 -0
  98. package/dist/entitybox-execution-lease.js +332 -0
  99. package/dist/entitybox-file-inputs.d.ts +51 -0
  100. package/dist/entitybox-file-inputs.js +182 -0
  101. package/dist/entitybox-file-session.d.ts +38 -0
  102. package/dist/entitybox-file-session.js +82 -0
  103. package/dist/entitybox-host-process.d.ts +12 -0
  104. package/dist/entitybox-host-process.js +120 -0
  105. package/dist/entitybox-install.d.ts +10 -0
  106. package/dist/entitybox-install.js +40 -0
  107. package/dist/entitybox-mcp-broker.d.ts +77 -0
  108. package/dist/entitybox-mcp-broker.js +420 -0
  109. package/dist/entitybox-mcp-contract.d.ts +10 -0
  110. package/dist/entitybox-mcp-contract.js +24 -0
  111. package/dist/entitybox-mcp-guard.d.ts +3 -0
  112. package/dist/entitybox-mcp-guard.js +167 -0
  113. package/dist/entitybox-model-artifact.d.ts +9 -0
  114. package/dist/entitybox-model-artifact.js +83 -0
  115. package/dist/entitybox-native-services.d.ts +33 -0
  116. package/dist/entitybox-native-services.js +172 -0
  117. package/dist/entitybox-operation.d.ts +4 -0
  118. package/dist/entitybox-operation.js +34 -0
  119. package/dist/entitybox-provider-broker.d.ts +41 -0
  120. package/dist/entitybox-provider-broker.js +149 -0
  121. package/dist/entitybox-queue-request.d.ts +32 -0
  122. package/dist/entitybox-queue-request.js +141 -0
  123. package/dist/entitybox-recovery-host.d.ts +12 -0
  124. package/dist/entitybox-recovery-host.js +80 -0
  125. package/dist/entitybox-recovery-store.d.ts +33 -0
  126. package/dist/entitybox-recovery-store.js +150 -0
  127. package/dist/entitybox-recovery-worker.d.ts +30 -0
  128. package/dist/entitybox-recovery-worker.js +134 -0
  129. package/dist/entitybox-vault-provider.d.ts +27 -0
  130. package/dist/entitybox-vault-provider.js +127 -0
  131. package/dist/harness-registry.js +3 -1
  132. package/dist/index.js +72 -38
  133. package/dist/install.d.ts +14 -2
  134. package/dist/install.js +115 -10
  135. package/dist/model-cache.d.ts +38 -0
  136. package/dist/model-cache.js +137 -0
  137. package/dist/request-pump.d.ts +3 -0
  138. package/dist/request-pump.js +21 -7
  139. package/dist/session-runner.d.ts +8 -1
  140. package/dist/session-runner.js +48 -3
  141. package/dist/supabase-client.js +4 -1
  142. package/package.json +5 -1
  143. package/scripts/copy-assets.js +4 -2
@@ -0,0 +1,450 @@
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.EntityboxExecutionContainer = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const entitybox_operation_1 = require("./entitybox-operation");
39
+ const util_1 = require("util");
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const crypto_1 = require("crypto");
43
+ const entitybox_execution_lease_1 = require("./entitybox-execution-lease");
44
+ const entitybox_display_binding_1 = require("./entitybox-display-binding");
45
+ const exec = (0, util_1.promisify)(child_process_1.execFile);
46
+ const label = 'ai.add.entitybox.execution';
47
+ /** Owns a new container by its immutable ID. It never enters, mounts, changes
48
+ * or stops a native desktop. External tool actions must acquire their own
49
+ * verified stop boundary before this can serve as a complete task executor. */
50
+ class EntityboxExecutionContainer {
51
+ lease;
52
+ options;
53
+ proc;
54
+ docker;
55
+ watchdogDigest;
56
+ actionSource;
57
+ done;
58
+ id;
59
+ failureCode;
60
+ stopped;
61
+ output = '';
62
+ modelExit;
63
+ closed = false;
64
+ stopping = false;
65
+ ready = false;
66
+ started;
67
+ startedResolve;
68
+ startedReject;
69
+ actions = new Set();
70
+ requests = new Map();
71
+ constructor(lease, options, id, proc, docker, watchdogDigest, actionSource) {
72
+ this.lease = lease;
73
+ this.options = options;
74
+ this.proc = proc;
75
+ this.docker = docker;
76
+ this.watchdogDigest = watchdogDigest;
77
+ this.actionSource = actionSource;
78
+ this.id = id;
79
+ this.started = new Promise((resolve, reject) => { this.startedResolve = resolve; this.startedReject = reject; });
80
+ this.started.catch(() => { });
81
+ this.done = new Promise(resolve => {
82
+ proc.on('error', () => { this.startedReject(new entitybox_execution_lease_1.EntityboxExecutionError('Executor startup ended')); lease.fence(); resolve(70); });
83
+ proc.on('close', code => { this.closed = true; this.startedReject(new entitybox_execution_lease_1.EntityboxExecutionError('Executor startup ended')); for (const value of this.requests.values())
84
+ value.abort.abort(); resolve(code === 0 && this.modelExit === 0 ? 0 : 70); });
85
+ });
86
+ proc.stdin.on('error', () => lease.fence());
87
+ proc.stdout.setEncoding('utf8');
88
+ proc.stdout.on('data', chunk => this.consume(chunk));
89
+ proc.stderr.resume(); // Docker/model stderr is not a transcript or public error.
90
+ lease.signal.addEventListener('abort', () => { void this.stopAndVerify().catch(() => { }); }, { once: true });
91
+ proc.stdin.write(JSON.stringify({ type: 'configure', identity: lease.identity, display: options.display ? ':1' : undefined, command: options.command, task: options.task }) + '\n');
92
+ if (lease.signal.aborted)
93
+ void this.stopAndVerify().catch(() => { });
94
+ }
95
+ static async start(lease, options) {
96
+ lease.assertActive();
97
+ const memory = options.memoryMiB ?? 1536;
98
+ if (!Number.isInteger(memory) || memory < 256 || memory > 4096 || !/^sha256:[0-9a-f]{64}$/.test(options.imageId)
99
+ || (options.dockerContext !== undefined && !/^[a-zA-Z0-9_.-]{1,80}$/.test(options.dockerContext))
100
+ || !Array.isArray(options.command) || !options.command.length || options.command.length > 32
101
+ || !options.command.every(v => typeof v === 'string' && v.length < 16000)
102
+ || Buffer.byteLength(JSON.stringify({ command: options.command, task: options.task })) > 240 * 1024)
103
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Invalid isolated executor configuration');
104
+ const prefix = options.dockerContext ? ['--context', options.dockerContext] : [];
105
+ const docker = async (args) => {
106
+ try {
107
+ return (await exec('docker', [...prefix, ...args], { windowsHide: true, timeout: 15000, maxBuffer: 1024 * 1024 })).stdout.trim();
108
+ }
109
+ catch {
110
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor operation failed');
111
+ }
112
+ };
113
+ const watchdog = fs.readFileSync(path.join(__dirname, 'entitybox', 'container-watchdog.cjs'), 'utf8');
114
+ const actionSource = fs.readFileSync(path.join(__dirname, 'entitybox', 'container-action.cjs'), 'utf8');
115
+ const owner = lease.identity;
116
+ if (options.display) {
117
+ options = { ...options, display: Object.freeze({ ...options.display }) };
118
+ await (0, entitybox_display_binding_1.verifyEntityboxDisplay)(options.display, owner.scope, docker);
119
+ lease.assertActive();
120
+ }
121
+ const name = 'entitybox-execution-' + owner.attemptId;
122
+ const digest = (0, crypto_1.createHash)('sha256').update(watchdog).digest('hex');
123
+ const args = ['create', '-i', '--name', name, '--label', label + '=' + owner.attemptId,
124
+ '--label', label + '.request=' + owner.requestId, '--label', label + '.watchdog=' + digest,
125
+ '--init', '--restart', 'no', '--network', 'none', '--ipc', 'private', '--user', '0:0',
126
+ '--cpus', '1', '--memory', memory + 'm', '--memory-swap', memory + 'm', '--pids-limit', '96',
127
+ '--cap-drop', 'ALL', '--cap-add', 'SETUID', '--cap-add', 'SETGID', '--security-opt', 'no-new-privileges',
128
+ '--read-only', '--tmpfs', '/tmp:rw,noexec,nosuid,nodev,size=64m,mode=1777',
129
+ '--tmpfs', '/run/entitybox:rw,noexec,nosuid,nodev,size=1m,mode=700',
130
+ '--tmpfs', '/work:rw,nosuid,nodev,size=64m,uid=10001,gid=10001,mode=755',
131
+ '--tmpfs', '/home/entitybox:rw,noexec,nosuid,nodev,size=64m,uid=10001,gid=10001,mode=700',
132
+ ...(options.display ? ['--mount', 'type=volume,src=' + (0, entitybox_display_binding_1.entityboxDisplayVolume)(options.display) + ',dst=/tmp/.X11-unix,readonly,volume-nocopy'] : []),
133
+ '--workdir', '/', '--entrypoint', 'node', options.imageId, '-e', watchdog];
134
+ let id;
135
+ try {
136
+ id = await docker(args);
137
+ }
138
+ catch {
139
+ // An ambiguous create may have committed. Leave its never-started record
140
+ // for explicit reconciliation; never adopt an object by name alone.
141
+ lease.fence();
142
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor could not be created');
143
+ }
144
+ if (!/^[0-9a-f]{64}$/.test(id)) {
145
+ lease.fence();
146
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor identity unavailable');
147
+ }
148
+ try {
149
+ const inspected = JSON.parse(await docker(['container', 'inspect', id]))[0];
150
+ if (inspected.Id !== id || inspected.Image !== options.imageId || inspected.Config?.Labels?.[label] !== owner.attemptId
151
+ || inspected.Config?.Labels?.[label + '.request'] !== owner.requestId || inspected.Config?.Labels?.[label + '.watchdog'] !== digest
152
+ || inspected.State?.Running || inspected.State?.Status !== 'created' || inspected.HostConfig?.RestartPolicy?.Name !== 'no')
153
+ throw new Error();
154
+ lease.assertActive();
155
+ if (options.display)
156
+ await (0, entitybox_display_binding_1.verifyEntityboxDisplay)(options.display, owner.scope, docker);
157
+ if (options.onCreated)
158
+ await (0, entitybox_operation_1.entityboxOperation)([lease.signal], 10000, () => options.onCreated(Object.freeze({ containerId: id, imageId: options.imageId, watchdogSha256: digest })));
159
+ lease.assertActive();
160
+ }
161
+ catch {
162
+ lease.fence();
163
+ // The verified create returned this full ID; removing an unstarted
164
+ // container cannot dispatch or interrupt any other task.
165
+ await docker(['container', 'rm', id]).catch(() => { });
166
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor could not be verified');
167
+ }
168
+ const proc = (0, child_process_1.spawn)('docker', [...prefix, 'start', '--attach', '--interactive', id], { windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] });
169
+ return new EntityboxExecutionContainer(lease, options, id, proc, docker, digest, actionSource);
170
+ }
171
+ consume(chunk) {
172
+ try {
173
+ this.output += chunk;
174
+ if (Buffer.byteLength(this.output) > 1024 * 1024)
175
+ throw new Error();
176
+ let end;
177
+ while ((end = this.output.indexOf('\n')) >= 0) {
178
+ const packet = JSON.parse(this.output.slice(0, end));
179
+ this.output = this.output.slice(end + 1);
180
+ if (packet.type === 'challenge' && typeof packet.id === 'string' && /^[0-9a-f-]{36}$/.test(packet.id)) {
181
+ void this.grant(packet.id);
182
+ }
183
+ else if (packet.type === 'broker_request' && typeof packet.id === 'string' && /^[-0-9a-f]{36}$/.test(packet.id)) {
184
+ this.broker(packet.id, packet.request);
185
+ }
186
+ else if (packet.type === 'broker_cancel' && typeof packet.id === 'string') {
187
+ this.requests.get(packet.id)?.abort.abort();
188
+ }
189
+ else if (packet.type === 'output' && typeof packet.data === 'string' && /^[A-Za-z0-9+/]*={0,2}$/.test(packet.data)) {
190
+ this.lease.assertActive();
191
+ this.options.onOutput(Buffer.from(packet.data, 'base64'));
192
+ }
193
+ else if (packet.type === 'model_exit' && Number.isInteger(packet.code)) {
194
+ this.modelExit = packet.code;
195
+ }
196
+ else if (packet.type === 'started') {
197
+ this.ready = true;
198
+ this.startedResolve();
199
+ }
200
+ else
201
+ throw new Error();
202
+ }
203
+ }
204
+ catch {
205
+ this.lease.fence();
206
+ }
207
+ }
208
+ broker(id, request) {
209
+ this.lease.assertActive();
210
+ if (this.requests.size >= 8 || this.requests.has(id))
211
+ throw new Error();
212
+ const abort = new AbortController();
213
+ const send = async (event) => { this.lease.assertActive(); if (abort.signal.aborted || this.closed)
214
+ throw new Error(); const packet = JSON.stringify({ type: 'broker_event', id, event }) + '\n'; if (!this.proc.stdin.write(packet))
215
+ await new Promise((resolve, reject) => { const done = () => { clean(); resolve(); }, ended = () => { clean(); reject(new Error()); }, clean = () => { this.proc.stdin.removeListener('drain', done); abort.signal.removeEventListener('abort', ended); }; this.proc.stdin.once('drain', done); abort.signal.addEventListener('abort', ended, { once: true }); if (abort.signal.aborted)
216
+ ended(); }); };
217
+ const done = Promise.resolve().then(async () => { try {
218
+ if (!this.options.onBroker)
219
+ throw new Error();
220
+ await this.options.onBroker(request, abort.signal, send);
221
+ }
222
+ catch {
223
+ if (!abort.signal.aborted) {
224
+ this.failureCode = 'entitybox_broker_connection_failed';
225
+ this.lease.fence();
226
+ }
227
+ }
228
+ finally {
229
+ this.requests.delete(id);
230
+ } });
231
+ this.requests.set(id, { abort, done });
232
+ }
233
+ async grant(id) {
234
+ try {
235
+ if (this.options.display)
236
+ await (0, entitybox_display_binding_1.verifyEntityboxDisplay)(this.options.display, this.lease.identity.scope, this.docker);
237
+ await this.lease.authorizeQueuedChallenge();
238
+ this.lease.assertActive();
239
+ if (!this.closed && !this.stopping)
240
+ this.proc.stdin.write(JSON.stringify({ type: 'grant', id }) + '\n');
241
+ }
242
+ catch {
243
+ this.lease.fence();
244
+ }
245
+ }
246
+ get scope() { return this.lease.identity.scope; }
247
+ async whenStarted() {
248
+ let timer;
249
+ try {
250
+ await Promise.race([this.started, new Promise((_, reject) => { timer = setTimeout(() => reject(new entitybox_execution_lease_1.EntityboxExecutionError('Executor startup deadline ended')), 10000); })]);
251
+ this.lease.assertActive();
252
+ }
253
+ catch {
254
+ this.lease.fence();
255
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Executor startup ended');
256
+ }
257
+ finally {
258
+ clearTimeout(timer);
259
+ }
260
+ }
261
+ get executionIdentity() { return this.lease.identity; }
262
+ get hasDedicatedDisplay() { return !!this.options.display; }
263
+ async assertActive() {
264
+ this.lease.assertActive();
265
+ if (this.stopping || this.closed || !this.ready)
266
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor is unavailable');
267
+ const c = await this.inspectOwned();
268
+ if (!c.State?.Running || c.State.Pid <= 0)
269
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor ended');
270
+ if (this.options.display)
271
+ await (0, entitybox_display_binding_1.verifyEntityboxDisplay)(this.options.display, this.scope, this.docker);
272
+ this.lease.assertActive();
273
+ if (this.stopping || this.closed)
274
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor ended');
275
+ }
276
+ async inspectOwned() {
277
+ const v = JSON.parse(await this.docker(['container', 'inspect', this.id]))[0];
278
+ if (v.Id !== this.id || v.Config?.Labels?.[label] !== this.lease.identity.attemptId
279
+ || v.Config?.Labels?.[label + '.request'] !== this.lease.identity.requestId
280
+ || v.Config?.Labels?.[label + '.watchdog'] !== this.watchdogDigest || v.Image !== this.options.imageId
281
+ || v.HostConfig?.RestartPolicy?.Name !== 'no')
282
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Executor ownership changed');
283
+ return v;
284
+ }
285
+ /** Commands run only in this task's PID namespace. No native desktop, host
286
+ * execution or remote-queue fallback is permitted here. A lost exec transport
287
+ * fences the entire task: closing a Docker client is not proof of inner stop. */
288
+ async executeAction(options) {
289
+ const timeoutMs = options.timeoutMs ?? 60000;
290
+ if (!Array.isArray(options.argv) || !options.argv.length || options.argv.length > 64 || !options.argv[0]
291
+ || !options.argv.every(v => typeof v === 'string' && v.length <= 32768 && !v.includes('\0'))
292
+ || (options.input !== undefined && !Buffer.isBuffer(options.input))
293
+ || (options.display !== undefined && (options.display !== ':1' || !this.options.display))
294
+ || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 600000)
295
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Invalid isolated action');
296
+ const packet = JSON.stringify({ version: 1, actionId: (0, crypto_1.randomUUID)(), identity: this.lease.identity,
297
+ argv: options.argv, input: (options.input ?? Buffer.alloc(0)).toString('base64'), timeoutMs, display: options.display });
298
+ if (Buffer.byteLength(packet) > 512 * 1024)
299
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated action input is too large');
300
+ this.lease.assertActive();
301
+ if (this.stopping || this.closed || !this.ready || this.actions.size >= 8)
302
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated executor is unavailable');
303
+ if (options.signal?.aborted) {
304
+ this.lease.fence();
305
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated action canceled');
306
+ }
307
+ // Register before the first await, so Stop also owns actions awaiting Docker
308
+ // inspection. Recheck authority after that asynchronous preparation.
309
+ let settle;
310
+ const done = new Promise(resolve => { settle = resolve; });
311
+ this.actions.add(done);
312
+ try {
313
+ const inspected = await this.inspectOwned();
314
+ if (this.options.display)
315
+ await (0, entitybox_display_binding_1.verifyEntityboxDisplay)(this.options.display, this.scope, this.docker);
316
+ this.lease.assertActive();
317
+ if (this.stopping || this.closed || options.signal?.aborted || !inspected.State?.Running || inspected.State.Pid <= 0)
318
+ throw new Error();
319
+ const prefix = this.options.dockerContext ? ['--context', this.options.dockerContext] : [];
320
+ const proc = (0, child_process_1.spawn)('docker', [...prefix, 'exec', '--interactive', '--user', '0:0', this.id, 'node', '-e', this.actionSource], { windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] });
321
+ return await new Promise((resolve, reject) => {
322
+ const stdout = [], stderr = [];
323
+ let raw = '', bytes = 0, code, invalid = false, closed = false;
324
+ const fail = () => {
325
+ if (invalid)
326
+ return;
327
+ invalid = true;
328
+ this.lease.fence();
329
+ // This is the still-owned client object; namespace shutdown is proved
330
+ // separately before any lease acknowledgement can release the screen.
331
+ proc.stdin.destroy();
332
+ if (!closed)
333
+ proc.kill();
334
+ };
335
+ const timer = setTimeout(fail, timeoutMs + 10000);
336
+ this.lease.signal.addEventListener('abort', fail, { once: true });
337
+ options.signal?.addEventListener('abort', fail, { once: true });
338
+ proc.stdin.on('error', fail);
339
+ proc.on('error', fail);
340
+ proc.stderr.resume();
341
+ proc.stdout.setEncoding('utf8');
342
+ proc.stdout.on('data', chunk => {
343
+ try {
344
+ if (invalid)
345
+ return;
346
+ raw += chunk;
347
+ if (Buffer.byteLength(raw) > 1024 * 1024)
348
+ throw new Error();
349
+ let end;
350
+ while ((end = raw.indexOf('\n')) >= 0) {
351
+ const p = JSON.parse(raw.slice(0, end));
352
+ raw = raw.slice(end + 1);
353
+ if (code !== undefined)
354
+ throw new Error();
355
+ if (p.type === 'result' && Number.isInteger(p.code) && p.code >= 0 && p.code <= 255)
356
+ code = p.code;
357
+ else if (['stdout', 'stderr'].includes(p.type) && typeof p.data === 'string' && p.data.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(p.data)) {
358
+ const data = Buffer.from(p.data, 'base64');
359
+ bytes += data.length;
360
+ if (bytes > 32 * 1024 * 1024)
361
+ throw new Error();
362
+ (p.type === 'stdout' ? stdout : stderr).push(data);
363
+ }
364
+ else
365
+ throw new Error();
366
+ }
367
+ }
368
+ catch {
369
+ fail();
370
+ }
371
+ });
372
+ proc.once('close', (exitCode, signal) => {
373
+ closed = true;
374
+ clearTimeout(timer);
375
+ this.lease.signal.removeEventListener('abort', fail);
376
+ options.signal?.removeEventListener('abort', fail);
377
+ if (invalid || signal || exitCode !== 0 || code === undefined || raw || this.stopping || this.lease.signal.aborted) {
378
+ this.lease.fence();
379
+ reject(new entitybox_execution_lease_1.EntityboxExecutionError('Isolated action could not complete'));
380
+ return;
381
+ }
382
+ try {
383
+ this.lease.assertActive();
384
+ resolve({ stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr), code });
385
+ }
386
+ catch {
387
+ reject(new entitybox_execution_lease_1.EntityboxExecutionError('Isolated action authority ended'));
388
+ }
389
+ });
390
+ if (this.lease.signal.aborted || options.signal?.aborted)
391
+ fail();
392
+ else
393
+ proc.stdin.end(packet);
394
+ });
395
+ }
396
+ catch {
397
+ this.lease.fence();
398
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Isolated action could not complete');
399
+ }
400
+ finally {
401
+ this.actions.delete(done);
402
+ settle();
403
+ }
404
+ }
405
+ async stopAndVerify() {
406
+ if (this.stopped)
407
+ return this.stopped;
408
+ this.stopping = true;
409
+ this.stopped = Promise.resolve().then(() => this.stopOwned());
410
+ this.lease.fence();
411
+ try {
412
+ await this.stopped;
413
+ }
414
+ catch (error) {
415
+ this.stopped = undefined;
416
+ throw error;
417
+ }
418
+ }
419
+ async stopOwned() {
420
+ // No more grants or task output are accepted while containment is stopping.
421
+ // Do not recurse through fence()'s abort listener: this method is idempotent.
422
+ const pending = [...this.requests.values()];
423
+ for (const value of pending)
424
+ value.abort.abort();
425
+ const actions = [...this.actions];
426
+ const before = await this.inspectOwned();
427
+ if (before.State.Running)
428
+ await this.docker(['container', 'kill', this.id]);
429
+ const after = await this.inspectOwned();
430
+ if (after.State.Running || after.State.Pid !== 0 || !['exited', 'created', 'dead'].includes(after.State.Status))
431
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Executor has not stopped');
432
+ let timer;
433
+ try {
434
+ await Promise.race([Promise.all([...pending.map(v => v.done), ...actions]), new Promise((_, reject) => { timer = setTimeout(() => reject(new entitybox_execution_lease_1.EntityboxExecutionError('Executor requests have not stopped')), 20000); })]);
435
+ }
436
+ finally {
437
+ clearTimeout(timer);
438
+ }
439
+ this.proc.stdin.destroy();
440
+ if (!this.closed)
441
+ await this.done;
442
+ }
443
+ /** Keep the stopped resource until the caller has recorded its stop receipt.
444
+ * Missing/unverifiable resources cannot themselves prove process shutdown. */
445
+ async removeStopped() {
446
+ await this.stopAndVerify();
447
+ await this.docker(['container', 'rm', this.id]);
448
+ }
449
+ }
450
+ exports.EntityboxExecutionContainer = EntityboxExecutionContainer;
@@ -0,0 +1,40 @@
1
+ import { readEntityboxCapacity } from './entitybox-capacity-provisioner';
2
+ import { createEntityboxNativeServices, type EntityboxNativeServicesTransport } from './entitybox-native-services';
3
+ import type { EntityboxQueueServices } from './entitybox-queue-request';
4
+ export interface EntityboxExecutionHostConfiguration {
5
+ enabled: true;
6
+ imageId: string;
7
+ capacityImageId: string;
8
+ dockerContext: string;
9
+ capacityDirectory: string;
10
+ recoveryDirectory: string;
11
+ fileGatewayUrl?: string;
12
+ }
13
+ export interface EntityboxExecutionHostIO {
14
+ rpc: <T>(name: string, args: Record<string, unknown>) => Promise<T>;
15
+ pairing: () => {
16
+ runtimeId: string;
17
+ daemonToken: string;
18
+ } | null;
19
+ /** Fixture-only transports. Never read from queued requests or models. */
20
+ readCapacity?: typeof readEntityboxCapacity;
21
+ createServices?: typeof createEntityboxNativeServices;
22
+ transport?: EntityboxNativeServicesTransport;
23
+ }
24
+ export declare function entityboxExecutionEnvironment(env?: NodeJS.ProcessEnv): EntityboxExecutionHostConfiguration | undefined;
25
+ /** Reopen only the published, physically verified capacity for this native
26
+ * lease. No cached inventory, ordinary-desktop adoption or host execution. */
27
+ export declare function createEntityboxExecutionHost(config: EntityboxExecutionHostConfiguration | undefined, io: EntityboxExecutionHostIO): {
28
+ services: undefined;
29
+ stop(): void;
30
+ } | {
31
+ services: EntityboxQueueServices;
32
+ stop(): void;
33
+ };
34
+ export declare function startEntityboxExecutionHost(io: EntityboxExecutionHostIO, env?: NodeJS.ProcessEnv): {
35
+ services: undefined;
36
+ stop(): void;
37
+ } | {
38
+ services: EntityboxQueueServices;
39
+ stop(): void;
40
+ };
@@ -0,0 +1,168 @@
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.entityboxExecutionEnvironment = entityboxExecutionEnvironment;
37
+ exports.createEntityboxExecutionHost = createEntityboxExecutionHost;
38
+ exports.startEntityboxExecutionHost = startEntityboxExecutionHost;
39
+ const path = __importStar(require("path"));
40
+ const child_process_1 = require("child_process");
41
+ const util_1 = require("util");
42
+ const paths_1 = require("./paths");
43
+ const entitybox_capacity_provisioner_1 = require("./entitybox-capacity-provisioner");
44
+ const entitybox_native_services_1 = require("./entitybox-native-services");
45
+ const entitybox_recovery_store_1 = require("./entitybox-recovery-store");
46
+ const entitybox_execution_lease_1 = require("./entitybox-execution-lease");
47
+ const entitybox_file_inputs_1 = require("./entitybox-file-inputs");
48
+ const uuid = /^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;
49
+ const image = /^sha256:[a-f0-9]{64}$/;
50
+ const fail = () => new entitybox_execution_lease_1.EntityboxExecutionError('The dedicated computer is not available for this task');
51
+ const exec = (0, util_1.promisify)(child_process_1.execFile);
52
+ const tools = Object.freeze(['click', 'press_key', 'type_text', 'screenshot', 'run_command']);
53
+ function entityboxExecutionEnvironment(env = process.env) {
54
+ if (env.AINODE_ENTITYBOX_EXECUTION_ENABLED !== '1')
55
+ return undefined;
56
+ const config = {
57
+ enabled: true,
58
+ ...(env.AINODE_ENTITYBOX_FILE_INPUTS_ENABLED === '1' ? { fileGatewayUrl: (0, entitybox_file_inputs_1.entityboxFileGatewayUrl)(env.AINODE_ENTITYBOX_FILE_GATEWAY_URL ?? '') } : {}),
59
+ imageId: env.AINODE_ENTITYBOX_EXECUTION_IMAGE ?? '',
60
+ capacityImageId: env.AINODE_ENTITYBOX_CAPACITY_IMAGE ?? '',
61
+ dockerContext: env.AINODE_ENTITYBOX_DOCKER_CONTEXT ?? '',
62
+ capacityDirectory: env.AINODE_ENTITYBOX_CAPACITY_DIRECTORY ?? path.join(paths_1.RUNTIME_HOME, 'entitybox', 'capacities'),
63
+ recoveryDirectory: env.AINODE_ENTITYBOX_RECOVERY_DIRECTORY ?? path.join(paths_1.RUNTIME_HOME, 'entitybox', 'recovery'),
64
+ };
65
+ if (env.DOCKER_HOST)
66
+ throw fail();
67
+ validate(config);
68
+ return config;
69
+ }
70
+ function validate(c) {
71
+ if (c.fileGatewayUrl !== undefined)
72
+ (0, entitybox_file_inputs_1.entityboxFileGatewayUrl)(c.fileGatewayUrl);
73
+ if (!image.test(c.imageId) || !image.test(c.capacityImageId) || !/^[-a-zA-Z0-9_.]{1,80}$/.test(c.dockerContext)
74
+ || ![c.capacityDirectory, c.recoveryDirectory].every(d => typeof d === 'string' && path.isAbsolute(d) && !d.startsWith('\\\\'))
75
+ || path.resolve(c.capacityDirectory) === path.resolve(c.recoveryDirectory))
76
+ throw fail();
77
+ }
78
+ /** Reopen only the published, physically verified capacity for this native
79
+ * lease. No cached inventory, ordinary-desktop adoption or host execution. */
80
+ function createEntityboxExecutionHost(config, io) {
81
+ if (!config?.enabled)
82
+ return { services: undefined, stop() { } };
83
+ validate(config);
84
+ const settings = Object.freeze({ ...config });
85
+ const initial = io.pairing();
86
+ if (!initial || !uuid.test(initial.runtimeId) || !initial.daemonToken)
87
+ throw fail();
88
+ const runtimeId = initial.runtimeId;
89
+ const stop = new AbortController();
90
+ const token = () => {
91
+ const current = io.pairing();
92
+ if (stop.signal.aborted || !current || current.runtimeId !== runtimeId || !current.daemonToken)
93
+ throw fail();
94
+ return current.daemonToken;
95
+ };
96
+ const docker = io.transport?.docker ?? (async (args) => (await exec('docker', ['--context', settings.dockerContext, ...args], { windowsHide: true, timeout: 5000, maxBuffer: 1024 * 1024 })).stdout.trim());
97
+ async function published(desktopId, signal) {
98
+ const seen = new Set(), desktops = new Set();
99
+ let after = null, match;
100
+ for (let page = 0; page < 10; page++) {
101
+ if (signal.aborted)
102
+ throw fail();
103
+ const rows = await io.rpc('entitybox_native_capacity_inventory', { p_token: token(), p_after: after, p_limit: 50 });
104
+ token();
105
+ if (signal.aborted || !Array.isArray(rows) || rows.length > 50)
106
+ throw fail();
107
+ for (const row of rows) {
108
+ if (!row || ![row.id, row.runtimeId, row.desktopId].every(v => typeof v === 'string' && uuid.test(v))
109
+ || row.runtimeId !== runtimeId || !['reserved', 'provisioning', 'ready'].includes(row.phase)
110
+ || seen.has(row.id) || desktops.has(row.desktopId) || after !== null && row.id <= after)
111
+ throw fail();
112
+ seen.add(row.id);
113
+ desktops.add(row.desktopId);
114
+ after = row.id;
115
+ if (row.desktopId === desktopId)
116
+ match = row;
117
+ }
118
+ if (rows.length < 50) {
119
+ if (match?.phase !== 'ready' || match.receipt?.dockerContext !== settings.dockerContext
120
+ || match.receipt.application?.imageId !== settings.capacityImageId || match.receipt.display?.imageId !== settings.capacityImageId)
121
+ throw fail();
122
+ // Snapshot before asynchronous physical reads; transport objects cannot
123
+ // replace the server evidence while those reads are pending.
124
+ const expected = (0, entitybox_recovery_store_1.entityboxCanonical)(match.receipt);
125
+ const receipt = await (io.readCapacity ?? entitybox_capacity_provisioner_1.readEntityboxCapacity)(settings.capacityDirectory, desktopId, runtimeId, docker);
126
+ token();
127
+ if (signal.aborted || (0, entitybox_recovery_store_1.entityboxCanonical)(receipt) !== expected)
128
+ throw fail();
129
+ return receipt;
130
+ }
131
+ }
132
+ throw fail();
133
+ }
134
+ const services = {
135
+ signal: stop.signal,
136
+ prepare: async (request, lease, signal) => {
137
+ try {
138
+ token();
139
+ lease.assertActive();
140
+ if (signal.aborted || request.runtime_id !== runtimeId || request.id !== lease.identity.requestId
141
+ || request.desktop_id !== lease.identity.scope.desktopId || lease.identity.scope.runtimeId !== runtimeId)
142
+ throw fail();
143
+ const capacity = await published(request.desktop_id, signal);
144
+ token();
145
+ lease.assertActive();
146
+ const adapter = (io.createServices ?? entitybox_native_services_1.createEntityboxNativeServices)({ enabled: true, runtimeId,
147
+ imageId: settings.imageId, dockerContext: settings.dockerContext, recoveryDirectory: settings.recoveryDirectory,
148
+ capacities: [capacity], desktopTools: tools, ...(settings.fileGatewayUrl ? { fileGatewayUrl: settings.fileGatewayUrl } : {}) }, { ...io.transport, docker });
149
+ if (!adapter)
150
+ throw fail();
151
+ return await adapter.prepare(request, lease, signal);
152
+ }
153
+ catch {
154
+ throw fail();
155
+ }
156
+ },
157
+ };
158
+ return { services, stop() { stop.abort(); } };
159
+ }
160
+ function startEntityboxExecutionHost(io, env = process.env) {
161
+ try {
162
+ return createEntityboxExecutionHost(entityboxExecutionEnvironment(env), io);
163
+ }
164
+ catch {
165
+ console.warn('[entitybox] Computer task execution is disabled: host configuration unavailable.');
166
+ return { services: undefined, stop() { } };
167
+ }
168
+ }