@addai/node 0.30.13 → 0.30.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/auto-update.d.ts +3 -0
  2. package/dist/auto-update.js +15 -0
  3. package/dist/capabilities.d.ts +17 -3
  4. package/dist/capabilities.js +18 -8
  5. package/dist/claude-models.d.ts +4 -12
  6. package/dist/claude-models.js +12 -88
  7. package/dist/claude-print.d.ts +15 -0
  8. package/dist/claude-print.js +16 -0
  9. package/dist/codex-models.d.ts +81 -0
  10. package/dist/codex-models.js +385 -0
  11. package/dist/codex-spawn.d.ts +14 -8
  12. package/dist/codex-spawn.js +30 -10
  13. package/dist/command-runner.js +31 -9
  14. package/dist/desktop/docker.js +2 -0
  15. package/dist/desktop/manager.d.ts +2 -1
  16. package/dist/desktop/manager.js +51 -1
  17. package/dist/desktop/provider.d.ts +1 -1
  18. package/dist/desktop/provider.js +2 -0
  19. package/dist/desktop/relay-client.js +7 -0
  20. package/dist/desktop/spec.d.ts +2 -0
  21. package/dist/direct/aios-bridge.d.ts +13 -0
  22. package/dist/direct/aios-bridge.js +177 -0
  23. package/dist/direct/aios-mcp.d.ts +2 -0
  24. package/dist/direct/aios-mcp.js +132 -0
  25. package/dist/direct/aios-tools.d.ts +351 -0
  26. package/dist/direct/aios-tools.js +120 -0
  27. package/dist/direct/board-chat-provider.d.ts +64 -0
  28. package/dist/direct/board-chat-provider.js +151 -0
  29. package/dist/direct/board-chat.d.ts +40 -0
  30. package/dist/direct/board-chat.js +414 -0
  31. package/dist/direct/config.d.ts +2 -0
  32. package/dist/direct/config.js +60 -0
  33. package/dist/direct/launch.d.ts +14 -0
  34. package/dist/direct/launch.js +121 -0
  35. package/dist/direct/protocol.d.ts +46 -0
  36. package/dist/direct/protocol.js +84 -0
  37. package/dist/direct/relay-main.d.ts +1 -0
  38. package/dist/direct/relay-main.js +21 -0
  39. package/dist/direct/relay.d.ts +26 -0
  40. package/dist/direct/relay.js +334 -0
  41. package/dist/direct/sessions.d.ts +61 -0
  42. package/dist/direct/sessions.js +368 -0
  43. package/dist/direct/supervisor.d.ts +9 -0
  44. package/dist/direct/supervisor.js +137 -0
  45. package/dist/direct/terminal.d.ts +1 -0
  46. package/dist/direct/terminal.js +106 -0
  47. package/dist/direct/worker.d.ts +1 -0
  48. package/dist/direct/worker.js +278 -0
  49. package/dist/entitybox/container-action.cjs +85 -0
  50. package/dist/entitybox/container-watchdog.cjs +70 -0
  51. package/dist/entitybox/model-entry.d.ts +3 -0
  52. package/dist/entitybox/model-entry.js +233 -0
  53. package/dist/entitybox/model-mcp-proxy.d.ts +3 -0
  54. package/dist/entitybox/model-mcp-proxy.js +85 -0
  55. package/dist/entitybox-application-boundary.d.ts +59 -0
  56. package/dist/entitybox-application-boundary.js +161 -0
  57. package/dist/entitybox-application-guard-core.d.ts +44 -0
  58. package/dist/entitybox-application-guard-core.js +164 -0
  59. package/dist/entitybox-application-guard-process.d.ts +1 -0
  60. package/dist/entitybox-application-guard-process.js +83 -0
  61. package/dist/entitybox-application-guard.d.ts +46 -0
  62. package/dist/entitybox-application-guard.js +239 -0
  63. package/dist/entitybox-capacity-health-host.d.ts +12 -0
  64. package/dist/entitybox-capacity-health-host.js +80 -0
  65. package/dist/entitybox-capacity-health-worker.d.ts +22 -0
  66. package/dist/entitybox-capacity-health-worker.js +168 -0
  67. package/dist/entitybox-capacity-host.d.ts +17 -0
  68. package/dist/entitybox-capacity-host.js +155 -0
  69. package/dist/entitybox-capacity-owner-process.d.ts +1 -0
  70. package/dist/entitybox-capacity-owner-process.js +32 -0
  71. package/dist/entitybox-capacity-owner.d.ts +16 -0
  72. package/dist/entitybox-capacity-owner.js +90 -0
  73. package/dist/entitybox-capacity-programs.d.ts +7 -0
  74. package/dist/entitybox-capacity-programs.js +49 -0
  75. package/dist/entitybox-capacity-provisioner.d.ts +82 -0
  76. package/dist/entitybox-capacity-provisioner.js +404 -0
  77. package/dist/entitybox-capacity-publication.d.ts +29 -0
  78. package/dist/entitybox-capacity-publication.js +199 -0
  79. package/dist/entitybox-capacity-registration.d.ts +36 -0
  80. package/dist/entitybox-capacity-registration.js +75 -0
  81. package/dist/entitybox-capacity-worker.d.ts +29 -0
  82. package/dist/entitybox-capacity-worker.js +113 -0
  83. package/dist/entitybox-computer-task.d.ts +69 -0
  84. package/dist/entitybox-computer-task.js +176 -0
  85. package/dist/entitybox-controller-recovery.d.ts +40 -0
  86. package/dist/entitybox-controller-recovery.js +156 -0
  87. package/dist/entitybox-desktop-action-server.d.ts +34 -0
  88. package/dist/entitybox-desktop-action-server.js +176 -0
  89. package/dist/entitybox-desktop-admission.d.ts +32 -0
  90. package/dist/entitybox-desktop-admission.js +79 -0
  91. package/dist/entitybox-display-binding.d.ts +13 -0
  92. package/dist/entitybox-display-binding.js +31 -0
  93. package/dist/entitybox-execution-container.d.ts +82 -0
  94. package/dist/entitybox-execution-container.js +450 -0
  95. package/dist/entitybox-execution-host.d.ts +40 -0
  96. package/dist/entitybox-execution-host.js +168 -0
  97. package/dist/entitybox-execution-lease.d.ts +81 -0
  98. package/dist/entitybox-execution-lease.js +332 -0
  99. package/dist/entitybox-file-inputs.d.ts +51 -0
  100. package/dist/entitybox-file-inputs.js +182 -0
  101. package/dist/entitybox-file-session.d.ts +38 -0
  102. package/dist/entitybox-file-session.js +82 -0
  103. package/dist/entitybox-host-process.d.ts +12 -0
  104. package/dist/entitybox-host-process.js +120 -0
  105. package/dist/entitybox-install.d.ts +10 -0
  106. package/dist/entitybox-install.js +40 -0
  107. package/dist/entitybox-mcp-broker.d.ts +77 -0
  108. package/dist/entitybox-mcp-broker.js +420 -0
  109. package/dist/entitybox-mcp-contract.d.ts +10 -0
  110. package/dist/entitybox-mcp-contract.js +24 -0
  111. package/dist/entitybox-mcp-guard.d.ts +3 -0
  112. package/dist/entitybox-mcp-guard.js +167 -0
  113. package/dist/entitybox-model-artifact.d.ts +9 -0
  114. package/dist/entitybox-model-artifact.js +83 -0
  115. package/dist/entitybox-native-services.d.ts +33 -0
  116. package/dist/entitybox-native-services.js +172 -0
  117. package/dist/entitybox-operation.d.ts +4 -0
  118. package/dist/entitybox-operation.js +34 -0
  119. package/dist/entitybox-provider-broker.d.ts +41 -0
  120. package/dist/entitybox-provider-broker.js +149 -0
  121. package/dist/entitybox-queue-request.d.ts +32 -0
  122. package/dist/entitybox-queue-request.js +141 -0
  123. package/dist/entitybox-recovery-host.d.ts +12 -0
  124. package/dist/entitybox-recovery-host.js +80 -0
  125. package/dist/entitybox-recovery-store.d.ts +33 -0
  126. package/dist/entitybox-recovery-store.js +150 -0
  127. package/dist/entitybox-recovery-worker.d.ts +30 -0
  128. package/dist/entitybox-recovery-worker.js +134 -0
  129. package/dist/entitybox-vault-provider.d.ts +27 -0
  130. package/dist/entitybox-vault-provider.js +127 -0
  131. package/dist/harness-registry.js +3 -1
  132. package/dist/index.js +72 -38
  133. package/dist/install.d.ts +14 -2
  134. package/dist/install.js +115 -10
  135. package/dist/model-cache.d.ts +38 -0
  136. package/dist/model-cache.js +137 -0
  137. package/dist/request-pump.d.ts +3 -0
  138. package/dist/request-pump.js +21 -7
  139. package/dist/session-runner.d.ts +8 -1
  140. package/dist/session-runner.js +48 -3
  141. package/dist/supabase-client.js +4 -1
  142. package/package.json +5 -1
  143. package/scripts/copy-assets.js +4 -2
@@ -0,0 +1,239 @@
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.EntityboxApplicationGuard = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const util_1 = require("util");
39
+ const path = __importStar(require("path"));
40
+ const entitybox_execution_lease_1 = require("./entitybox-execution-lease");
41
+ const entitybox_application_boundary_1 = require("./entitybox-application-boundary");
42
+ const entitybox_host_process_1 = require("./entitybox-host-process");
43
+ const entitybox_recovery_store_1 = require("./entitybox-recovery-store");
44
+ const exec = (0, util_1.promisify)(child_process_1.execFile);
45
+ const error = () => new entitybox_execution_lease_1.EntityboxExecutionError('Independent application ownership could not be verified');
46
+ /** Host-side handle. Production adapters must retain this owner until stop
47
+ * proof and child exit settle. A disappeared guard never grants reuse. */
48
+ class EntityboxApplicationGuard {
49
+ lease;
50
+ binding;
51
+ proc;
52
+ context;
53
+ requireInventory;
54
+ recovery;
55
+ processId;
56
+ closed;
57
+ ready;
58
+ started;
59
+ stopped;
60
+ readyResolve;
61
+ readyReject;
62
+ startResolve;
63
+ startReject;
64
+ stopResolve;
65
+ stopReject;
66
+ proof;
67
+ guardIdentity;
68
+ used = false;
69
+ closing = false;
70
+ didClose = false;
71
+ stopping;
72
+ abort = () => { void this.pauseAndVerify().catch(() => { }); };
73
+ constructor(lease, binding, proc, context, requireInventory, recovery) {
74
+ this.lease = lease;
75
+ this.binding = binding;
76
+ this.proc = proc;
77
+ this.context = context;
78
+ this.requireInventory = requireInventory;
79
+ this.recovery = recovery;
80
+ this.processId = proc.pid ?? 0;
81
+ this.ready = new Promise((resolve, reject) => { this.readyResolve = resolve; this.readyReject = reject; });
82
+ this.ready.catch(() => { });
83
+ this.started = new Promise((resolve, reject) => { this.startResolve = resolve; this.startReject = reject; });
84
+ this.started.catch(() => { });
85
+ this.stopped = new Promise((resolve, reject) => { this.stopResolve = resolve; this.stopReject = reject; });
86
+ this.stopped.catch(() => { });
87
+ this.closed = new Promise(resolve => { proc.once('close', () => { this.didClose = true; lease.signal.removeEventListener('abort', this.abort); this.readyReject(error()); this.startReject(error()); if (!this.proof) {
88
+ this.stopReject(error());
89
+ lease.fence();
90
+ if (this.used && !this.recovery)
91
+ void this.emergencyPause();
92
+ } resolve(); }); });
93
+ proc.on('error', () => { this.readyReject(error()); this.startReject(error()); this.stopReject(error()); lease.fence(); if (this.used && !this.recovery)
94
+ void this.emergencyPause(); });
95
+ proc.on('message', packet => this.message(packet));
96
+ lease.signal.addEventListener('abort', this.abort, { once: true });
97
+ this.send({ type: 'configure', identity: lease.identity, binding: this.binding, context, ...(recovery ? { recovery: { directory: recovery.store.directory, hostId: recovery.store.hostId, controller: recovery.controller } } : {}) });
98
+ if (lease.signal.aborted)
99
+ this.abort();
100
+ }
101
+ static async attach(lease, binding, options = {}) {
102
+ lease.assertActive();
103
+ const context = options.dockerContext ?? 'desktop-linux';
104
+ if (!/^[-a-zA-Z0-9_.]{1,80}$/.test(context))
105
+ throw error();
106
+ if (options.recoveryStore && !options.requireInventory)
107
+ throw error();
108
+ const controller = options.recoveryStore ? await (0, entitybox_host_process_1.readEntityboxHostProcess)() : null;
109
+ if (options.recoveryStore && !controller)
110
+ throw error();
111
+ lease.assertActive();
112
+ if (options.requireInventory)
113
+ await lease.registerResource('application', { ...binding });
114
+ const env = {};
115
+ for (const key of ['PATH', 'Path', 'SystemRoot', 'WINDIR', 'ComSpec', 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'TEMP', 'TMP', 'HOME', 'DOCKER_CONFIG'])
116
+ if (process.env[key] !== undefined)
117
+ env[key] = process.env[key];
118
+ const proc = (0, child_process_1.spawn)(process.execPath, [path.join(__dirname, 'entitybox-application-guard-process.js')], { env, detached: true, windowsHide: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] });
119
+ const owner = new EntityboxApplicationGuard(lease, Object.freeze({ ...binding }), proc, context, options.requireInventory === true, options.recoveryStore && controller ? { store: options.recoveryStore, controller } : undefined);
120
+ try {
121
+ options.onOwner?.(owner);
122
+ await owner.deadline(owner.ready, 10000);
123
+ lease.assertActive();
124
+ if (owner.recovery) {
125
+ if (!owner.guardIdentity)
126
+ throw error();
127
+ await lease.registerRecoveryHost((0, entitybox_recovery_store_1.parseEntityboxRecoveryHost)({ hostId: owner.recovery.store.hostId, dockerContext: context, controller: owner.recovery.controller, guard: owner.guardIdentity }));
128
+ }
129
+ lease.assertActive();
130
+ return owner;
131
+ }
132
+ catch {
133
+ lease.fence();
134
+ void owner.pauseAndVerify().catch(() => { });
135
+ throw error();
136
+ }
137
+ }
138
+ async resume() { if (this.used || this.closing)
139
+ throw error(); if (this.requireInventory)
140
+ this.lease.assertInventorySealed(); this.used = true; this.lease.assertActive(); this.send({ type: 'resume' }); try {
141
+ await this.deadline(this.started, 12000);
142
+ this.lease.assertActive();
143
+ }
144
+ catch {
145
+ this.lease.fence();
146
+ void this.pauseAndVerify().catch(() => { });
147
+ throw error();
148
+ } }
149
+ pauseAndVerify() {
150
+ if (this.stopping)
151
+ return this.stopping;
152
+ this.closing = true;
153
+ this.stopping = Promise.resolve().then(async () => { this.send({ type: 'stop' }); const proof = await this.deadline(this.stopped, 15000); await this.deadline(this.closed, 5000); const state = await (0, entitybox_application_boundary_1.inspectEntityboxApplication)(this.binding, this.lease.identity.scope, args => this.docker(args)); if (state === 'running')
154
+ throw error(); return proof; });
155
+ this.lease.fence();
156
+ return this.stopping;
157
+ }
158
+ async docker(args) { return (await exec('docker', ['--context', this.context, ...args], { windowsHide: true, timeout: 5000, maxBuffer: 1024 * 1024 })).stdout.trim(); }
159
+ async emergencyPause() { try {
160
+ if (await (0, entitybox_application_boundary_1.inspectEntityboxApplication)(this.binding, this.lease.identity.scope, args => this.docker(args)) === 'running')
161
+ await this.docker(['container', 'pause', this.binding.containerId]);
162
+ }
163
+ catch { } /* No proof: an earlier guard mutation may still be outstanding. */ }
164
+ send(packet) { if (!this.proc.connected)
165
+ return; try {
166
+ this.proc.send(packet, e => { if (e) {
167
+ this.lease.fence();
168
+ this.startReject(error());
169
+ } });
170
+ }
171
+ catch {
172
+ this.lease.fence();
173
+ this.startReject(error());
174
+ } }
175
+ message(value) {
176
+ try {
177
+ const p = value;
178
+ if (p?.type === 'ready') {
179
+ if (this.closing)
180
+ throw error();
181
+ if (this.recovery) {
182
+ this.guardIdentity = (0, entitybox_host_process_1.parseEntityboxHostProcess)(p.process);
183
+ if (this.guardIdentity.pid !== this.processId)
184
+ throw error();
185
+ }
186
+ this.readyResolve();
187
+ return;
188
+ }
189
+ if (p?.type === 'challenge' && typeof p.id === 'string' && /^[0-9a-f-]{36}$/.test(p.id) && this.used && !this.closing) {
190
+ void this.grant(p.id);
191
+ return;
192
+ }
193
+ if (p?.type === 'started') {
194
+ this.lease.assertActive();
195
+ if (this.closing)
196
+ throw error();
197
+ this.startResolve();
198
+ return;
199
+ }
200
+ if (p?.type === 'stopped') {
201
+ const r = p.proof;
202
+ if (r?.capacityId !== this.binding.capacityId || r.containerId !== this.binding.containerId || r.attemptId !== this.lease.identity.attemptId || !['paused', 'stopped'].includes(r.state) || !Number.isFinite(Date.parse(r.observedAt)))
203
+ throw error();
204
+ this.proof = Object.freeze({ ...r });
205
+ this.closing = true;
206
+ this.stopResolve(this.proof);
207
+ this.lease.fence();
208
+ return;
209
+ }
210
+ if (p?.type === 'unverified') {
211
+ this.stopReject(error());
212
+ throw error();
213
+ }
214
+ throw error();
215
+ }
216
+ catch {
217
+ this.readyReject(error());
218
+ this.startReject(error());
219
+ this.lease.fence();
220
+ }
221
+ }
222
+ async grant(id) { try {
223
+ await this.lease.authorizeQueuedChallenge();
224
+ this.lease.assertActive();
225
+ if (this.closing || this.didClose)
226
+ throw error();
227
+ this.send({ type: 'grant', id });
228
+ }
229
+ catch {
230
+ this.lease.fence();
231
+ } }
232
+ async deadline(work, ms) { let timer; try {
233
+ return await Promise.race([work, new Promise((_, reject) => { timer = setTimeout(() => reject(error()), ms); })]);
234
+ }
235
+ finally {
236
+ clearTimeout(timer);
237
+ } }
238
+ }
239
+ exports.EntityboxApplicationGuard = EntityboxApplicationGuard;
@@ -0,0 +1,12 @@
1
+ import { type EntityboxCapacityHealthIO } from './entitybox-capacity-health-worker';
2
+ export declare function entityboxCapacityHealthEnvironment(env?: NodeJS.ProcessEnv): {
3
+ enabled: true;
4
+ dockerContext: string;
5
+ directory: string;
6
+ } | undefined;
7
+ /** Independently enabled so health can keep reporting while setup or task
8
+ * execution is disabled. It never starts or modifies a computer. */
9
+ export declare function startEntityboxCapacityHealthHost(io: Omit<EntityboxCapacityHealthIO, 'report'>, env?: NodeJS.ProcessEnv): {
10
+ stop(): void;
11
+ drain(_timeout?: number): Promise<boolean>;
12
+ };
@@ -0,0 +1,80 @@
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.entityboxCapacityHealthEnvironment = entityboxCapacityHealthEnvironment;
37
+ exports.startEntityboxCapacityHealthHost = startEntityboxCapacityHealthHost;
38
+ const path = __importStar(require("path"));
39
+ const paths_1 = require("./paths");
40
+ const entitybox_capacity_health_worker_1 = require("./entitybox-capacity-health-worker");
41
+ function entityboxCapacityHealthEnvironment(env = process.env) {
42
+ if (env.AINODE_ENTITYBOX_CAPACITY_HEALTH_ENABLED !== '1')
43
+ return undefined;
44
+ const dockerContext = env.AINODE_ENTITYBOX_DOCKER_CONTEXT ?? '';
45
+ const directory = env.AINODE_ENTITYBOX_CAPACITY_DIRECTORY ?? path.join(paths_1.RUNTIME_HOME, 'entitybox', 'capacities');
46
+ if (env.DOCKER_HOST || !/^[-a-zA-Z0-9_.]{1,80}$/.test(dockerContext) || !path.isAbsolute(directory) || directory.startsWith('\\\\'))
47
+ throw new Error('Invalid computer health configuration');
48
+ return { enabled: true, dockerContext, directory };
49
+ }
50
+ /** Independently enabled so health can keep reporting while setup or task
51
+ * execution is disabled. It never starts or modifies a computer. */
52
+ function startEntityboxCapacityHealthHost(io, env = process.env) {
53
+ const idle = { stop() { }, async drain(_timeout) { return true; } };
54
+ try {
55
+ const config = entityboxCapacityHealthEnvironment(env);
56
+ if (!config)
57
+ return idle;
58
+ const pairing = io.pairing();
59
+ if (!pairing)
60
+ throw new Error();
61
+ const reported = new Map();
62
+ const worker = (0, entitybox_capacity_health_worker_1.createEntityboxCapacityHealthWorker)({ ...config, runtimeId: pairing.runtimeId }, { ...io, report: (state, id) => {
63
+ const key = id ?? 'worker', at = Date.now(), last = reported.get(key);
64
+ if (last?.state === state && at - last.at < 60000)
65
+ return;
66
+ if (reported.size > 1000)
67
+ reported.clear();
68
+ reported.set(key, { state, at });
69
+ console.log('[entitybox] Computer health ' + state + (id ? ' (' + id + ')' : ''));
70
+ } });
71
+ if (!worker)
72
+ return idle;
73
+ worker.start();
74
+ return worker;
75
+ }
76
+ catch {
77
+ console.warn('[entitybox] Computer health is disabled: host configuration unavailable.');
78
+ return idle;
79
+ }
80
+ }
@@ -0,0 +1,22 @@
1
+ import { observeEntityboxCapacity } from './entitybox-capacity-provisioner';
2
+ import type { EntityboxCapacityRegistrationIO } from './entitybox-capacity-registration';
3
+ export interface EntityboxCapacityHealthConfiguration {
4
+ enabled?: boolean;
5
+ runtimeId: string;
6
+ dockerContext: string;
7
+ directory: string;
8
+ }
9
+ export interface EntityboxCapacityHealthIO extends Pick<EntityboxCapacityRegistrationIO, 'rpc' | 'pairing'> {
10
+ /** Fixture-only observer. Production always uses exact physical inspection. */
11
+ observe?: typeof observeEntityboxCapacity;
12
+ report?: (state: 'paused' | 'running' | 'unavailable', desktopId?: string) => void;
13
+ }
14
+ /** Health grants no lifecycle authority. Each report requires a fresh server
15
+ * challenge followed by physical inspection. Uncertain replies are not replayed
16
+ * as new observations; the next round starts a new challenge and inspects again. */
17
+ export declare function createEntityboxCapacityHealthWorker(config: EntityboxCapacityHealthConfiguration, io: EntityboxCapacityHealthIO): {
18
+ tick: () => Promise<void>;
19
+ start(): void;
20
+ stop(): void;
21
+ drain(timeoutMs?: number): Promise<boolean>;
22
+ } | undefined;
@@ -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.createEntityboxCapacityHealthWorker = createEntityboxCapacityHealthWorker;
37
+ const path = __importStar(require("path"));
38
+ const entitybox_capacity_provisioner_1 = require("./entitybox-capacity-provisioner");
39
+ const entitybox_recovery_store_1 = require("./entitybox-recovery-store");
40
+ const entitybox_operation_1 = require("./entitybox-operation");
41
+ const uuid = /^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;
42
+ /** Health grants no lifecycle authority. Each report requires a fresh server
43
+ * challenge followed by physical inspection. Uncertain replies are not replayed
44
+ * as new observations; the next round starts a new challenge and inspects again. */
45
+ function createEntityboxCapacityHealthWorker(config, io) {
46
+ if (config.enabled !== true)
47
+ return undefined;
48
+ if (!uuid.test(config.runtimeId) || !/^[-a-zA-Z0-9_.]{1,80}$/.test(config.dockerContext)
49
+ || !path.isAbsolute(config.directory) || config.directory.startsWith('\\\\'))
50
+ throw new Error('Invalid computer health configuration');
51
+ const settings = Object.freeze({ ...config });
52
+ let stopped = false, started = false, timer, flight;
53
+ const token = () => {
54
+ const p = io.pairing();
55
+ if (stopped || !p || p.runtimeId !== settings.runtimeId || typeof p.daemonToken !== 'string' || !p.daemonToken)
56
+ throw new Error();
57
+ return p.daemonToken;
58
+ };
59
+ const report = (state, id) => { try {
60
+ io.report?.(state, id);
61
+ }
62
+ catch { } };
63
+ async function run() {
64
+ try {
65
+ const paired = token();
66
+ const current = () => { if (token() !== paired)
67
+ throw new Error(); };
68
+ const rpc = async (name, args) => {
69
+ current();
70
+ const result = await (0, entitybox_operation_1.entityboxOperation)([], 4500, () => io.rpc(name, { p_token: paired, ...args }));
71
+ current();
72
+ return result;
73
+ };
74
+ // Validate every page before observing any computer. Older schemas and
75
+ // disabled workspaces omit health metadata and cause no physical reads.
76
+ const rows = [], ids = new Set(), desktops = new Set();
77
+ let after = null, complete = false;
78
+ for (let page = 0; page < 10; page++) {
79
+ const values = await rpc('entitybox_native_capacity_inventory', { p_after: after, p_limit: 50 });
80
+ if (!Array.isArray(values) || values.length > 50)
81
+ throw new Error();
82
+ for (const value of values) {
83
+ if (!value || ![value.id, value.desktopId, value.runtimeId].every(v => typeof v === 'string' && uuid.test(v))
84
+ || value.runtimeId !== settings.runtimeId || ids.has(value.id) || desktops.has(value.desktopId)
85
+ || after !== null && value.id <= after || !['reserved', 'provisioning', 'ready'].includes(value.phase))
86
+ throw new Error();
87
+ ids.add(value.id);
88
+ desktops.add(value.desktopId);
89
+ after = value.id;
90
+ if (value.health !== undefined && (!value.health || !['unknown', 'stale', 'paused', 'running', 'unavailable'].includes(value.health.state)))
91
+ throw new Error();
92
+ if (value.phase !== 'ready' || value.health === undefined)
93
+ continue;
94
+ const receipt = JSON.parse((0, entitybox_recovery_store_1.entityboxCanonical)(value.receipt));
95
+ if (!receipt || receipt.version !== 1 || receipt.application?.desktopId !== value.desktopId || receipt.application?.runtimeId !== settings.runtimeId
96
+ || receipt.display?.desktopId !== value.desktopId || receipt.display?.runtimeId !== settings.runtimeId)
97
+ throw new Error();
98
+ rows.push({ id: value.id, desktopId: value.desktopId, runtimeId: value.runtimeId, receipt });
99
+ }
100
+ if (values.length < 50) {
101
+ complete = true;
102
+ break;
103
+ }
104
+ }
105
+ if (!complete)
106
+ throw new Error();
107
+ let next = 0;
108
+ async function lane() {
109
+ while (next < rows.length && !stopped) {
110
+ const row = rows[next++];
111
+ try {
112
+ current();
113
+ const challenge = await rpc('entitybox_native_capacity_health_begin', { p_id: row.id, p_receipt: row.receipt });
114
+ if (!challenge || challenge.id !== row.id || challenge.desktopId !== row.desktopId || challenge.runtimeId !== settings.runtimeId
115
+ || typeof challenge.challengeId !== 'string' || !uuid.test(challenge.challengeId))
116
+ throw new Error();
117
+ let state = 'unavailable';
118
+ try {
119
+ const observation = await (io.observe ?? entitybox_capacity_provisioner_1.observeEntityboxCapacity)({ ...settings, desktopId: row.desktopId, receipt: row.receipt });
120
+ if (observation && ['paused', 'running', 'unavailable'].includes(observation.state))
121
+ state = observation.state;
122
+ }
123
+ catch { /* An inconclusive read is unavailable, never healthy. */ }
124
+ current();
125
+ const accepted = await rpc('entitybox_native_capacity_health_report', { p_id: row.id, p_challenge_id: challenge.challengeId, p_state: state });
126
+ if (!accepted || accepted.state !== state || typeof accepted.checkedAt !== 'string' || !Number.isFinite(Date.parse(accepted.checkedAt))
127
+ || typeof accepted.validUntil !== 'string' || Date.parse(accepted.validUntil) - Date.parse(accepted.checkedAt) !== 60000)
128
+ throw new Error();
129
+ report(state, row.desktopId);
130
+ }
131
+ catch {
132
+ if (!stopped)
133
+ report('unavailable', row.desktopId);
134
+ }
135
+ }
136
+ }
137
+ // Bounded concurrency lets one slow engine read coexist with the other
138
+ // computers. Server timestamps expire health even when a lane stalls.
139
+ await Promise.all(Array.from({ length: Math.min(4, rows.length) }, () => lane()));
140
+ }
141
+ catch {
142
+ if (!stopped)
143
+ report('unavailable');
144
+ }
145
+ }
146
+ function tick() { if (stopped)
147
+ return Promise.resolve(); if (!flight)
148
+ flight = run().finally(() => { flight = undefined; }); return flight; }
149
+ function schedule() { if (stopped)
150
+ return; timer = setTimeout(() => { void tick().finally(schedule); }, 15000); timer.unref?.(); }
151
+ return {
152
+ tick,
153
+ start() { if (started || stopped)
154
+ return; started = true; void tick().finally(schedule); },
155
+ stop() { stopped = true; clearTimeout(timer); },
156
+ async drain(timeoutMs = 15000) {
157
+ if (!flight)
158
+ return true;
159
+ let deadline;
160
+ try {
161
+ return await Promise.race([flight.then(() => true), new Promise(resolve => { deadline = setTimeout(() => resolve(false), Math.max(0, timeoutMs)); })]);
162
+ }
163
+ finally {
164
+ clearTimeout(deadline);
165
+ }
166
+ },
167
+ };
168
+ }
@@ -0,0 +1,17 @@
1
+ import { type EntityboxDesktopBudget } from './entitybox-desktop-admission';
2
+ export interface EntityboxCapacityHostConfiguration {
3
+ enabled: true;
4
+ recoverPublication: boolean;
5
+ imageId: string;
6
+ dockerContext: string;
7
+ directory: string;
8
+ budget: EntityboxDesktopBudget;
9
+ }
10
+ export declare function entityboxCapacityEnvironment(env?: NodeJS.ProcessEnv): EntityboxCapacityHostConfiguration | undefined;
11
+ /** Startup integration only. Default-off; no new native queue or host execution
12
+ * route. Budget numbers are explicit operator reservations for AddAI desktops,
13
+ * not claims about other applications or Docker/OS storage quotas. */
14
+ export declare function startEntityboxCapacityHost(env?: NodeJS.ProcessEnv): {
15
+ stop(): void;
16
+ drain(_timeout?: number): Promise<boolean>;
17
+ };