@vtxmacro/cli 2026.10.4 → 2026.10.6

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/bin/vtx-mcp.js CHANGED
@@ -1,43 +1,118 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __vtxCreateRequire } from 'node:module'; const require = __vtxCreateRequire(import.meta.url);
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __esm = (fn, res, err) => function __init() {
6
+ if (err) throw err[0];
7
+ try {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ } catch (e) {
10
+ throw err = [e], e;
11
+ }
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
2
17
 
3
- // lib/agent-core/client.ts
4
- import { randomUUID } from "node:crypto";
18
+ // <define:__VTX_EXO_POLICY__>
19
+ var init_define_VTX_EXO_POLICY = __esm({
20
+ "<define:__VTX_EXO_POLICY__>"() {
21
+ }
22
+ });
23
+
24
+ // <define:__VTX_GROK_POLICY__>
25
+ var init_define_VTX_GROK_POLICY = __esm({
26
+ "<define:__VTX_GROK_POLICY__>"() {
27
+ }
28
+ });
29
+
30
+ // <define:__VTX_PI_MODEL_POLICY__>
31
+ var init_define_VTX_PI_MODEL_POLICY = __esm({
32
+ "<define:__VTX_PI_MODEL_POLICY__>"() {
33
+ }
34
+ });
35
+
36
+ // agent-cli-release.json
37
+ var agent_cli_release_default;
38
+ var init_agent_cli_release = __esm({
39
+ "agent-cli-release.json"() {
40
+ agent_cli_release_default = { package_name: "@vtxmacro/cli", package_version: "2026.10.6", codex_package_name: "@openai/codex", codex_version: "0.155.0", copilot_sdk_package_name: "@github/copilot-sdk", copilot_sdk_version: "1.0.0-beta.8", copilot_cli_package_name: "@github/copilot", copilot_cli_version: "1.0.80", deepseek_harness_version: "0.1.1-rc.2", deepseek_harness_packages: ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-agent-instructions", "@deepseek-ai/dsh-agent-loop", "@deepseek-ai/dsh-agent-spine-demo", "@deepseek-ai/dsh-anonymous-user-id", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-attachment", "@deepseek-ai/dsh-brand", "@deepseek-ai/dsh-code-runtime", "@deepseek-ai/dsh-credentials", "@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-goal", "@deepseek-ai/dsh-goal-round-driver", "@deepseek-ai/dsh-home-paths", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-jobs", "@deepseek-ai/dsh-jobs-local", "@deepseek-ai/dsh-launch-environment", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-deepseek", "@deepseek-ai/dsh-llm-retry", "@deepseek-ai/dsh-output-retention", "@deepseek-ai/dsh-sandbox", "@deepseek-ai/dsh-sandbox-policy", "@deepseek-ai/dsh-scope", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-session-checkpoint-policy", "@deepseek-ai/dsh-session-persistence", "@deepseek-ai/dsh-session-persistence-jsonl", "@deepseek-ai/dsh-session-projection", "@deepseek-ai/dsh-session-title", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-shell", "@deepseek-ai/dsh-shell-env", "@deepseek-ai/dsh-skill", "@deepseek-ai/dsh-skill-filesystem", "@deepseek-ai/dsh-subprocess", "@deepseek-ai/dsh-system-prompt", "@deepseek-ai/dsh-timeout", "@deepseek-ai/dsh-tool-bash", "@deepseek-ai/dsh-tool-goal", "@deepseek-ai/dsh-tool-jobs", "@deepseek-ai/dsh-tool-skill", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-typert-protocol", "@deepseek-ai/dsh-user-approval"], deepseek_cordis_package_name: "@deepseek-ai/cordis", deepseek_cordis_version: "4.0.1", deepseek_support_packages: { "@deepseek-ai/cordis-plugin-timer": "1.1.3" }, platforms: { "linux-x64": { package_name: "@openai/codex-linux-x64", target: "x86_64-unknown-linux-musl", binary_name: "codex", sha256: "660e159a49e823ac8e5986cb238f73158ce4b957d40d9292f8de90862644b501" }, "win32-x64": { package_name: "@openai/codex-win32-x64", target: "x86_64-pc-windows-msvc", binary_name: "codex.exe", sha256: "e4c11374bd9de8ad5c3b7617fd4654bb7839901edb0863f9930666863c7a021b" } }, pi_coding_agent_package_name: "@earendil-works/pi-coding-agent", pi_ai_package_name: "@earendil-works/pi-ai", pi_version: "0.85.1", pi_support_packages: ["@earendil-works/pi-agent-core", "@earendil-works/pi-telemetry", "@earendil-works/pi-tui", "@earendil-works/chord"], grok_version: "1.0.30", grok_platforms: { "linux-x64": { package_name: "@xai-official/grok-linux-x64", binary_name: "grok", sha256: "504dd6546ab991b75d36698242875ce461489cd1f8cd84285873cb55bd5c7d54" }, "win32-x64": { package_name: "@xai-official/grok-win32-x64", binary_name: "grok.exe", sha256: "48736d1a4342a801ea26d0ecde4c516b653f791399065adaa55973f83abd8867" } } };
41
+ }
42
+ });
43
+
44
+ // lib/agent-core/config.ts
45
+ import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
46
+ import { dirname, join } from "node:path";
47
+ import { homedir } from "node:os";
48
+ function defaultBaseDir() {
49
+ return join(homedir(), ".vtx");
50
+ }
51
+ function resolveAgentCliConfig(env = process.env) {
52
+ const baseDir = String(env.VTX_HOME || "").trim() || defaultBaseDir();
53
+ const rawProfile = String(env.VTX_PROFILE_ID || "").trim();
54
+ const parsedProfile = rawProfile ? Number(rawProfile) : NaN;
55
+ const explicitStatePath = String(env.VTX_RUNTIME_STATE_PATH || "").trim();
56
+ return {
57
+ apiUrl: String(env.VTX_API_URL || "http://localhost:8000").replace(/\/+$/, ""),
58
+ tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join(baseDir, "token.json"),
59
+ statePath: explicitStatePath || join(baseDir, "runtime-state.json"),
60
+ runtimeStateDir: explicitStatePath ? null : baseDir,
61
+ runtimeDeviceId: String(env.VTX_RUNTIME_DEVICE_ID || "").trim() || null,
62
+ activeProfileId: Number.isFinite(parsedProfile) && parsedProfile > 0 ? parsedProfile : null,
63
+ outputJson: String(env.VTX_OUTPUT || "").trim().toLowerCase() === "json"
64
+ };
65
+ }
66
+ async function readStoredAgentAuth(path) {
67
+ try {
68
+ const raw = await readFile(path, "utf8");
69
+ if (!raw.trim()) {
70
+ throw new Error(`VTX token file is empty at ${path}. Run "vtx auth login" or remove the file and retry.`);
71
+ }
72
+ const parsed = JSON.parse(raw);
73
+ const token = String(parsed.token || "").trim();
74
+ return token ? { token } : null;
75
+ } catch (error) {
76
+ if (error.code === "ENOENT") {
77
+ return null;
78
+ }
79
+ if (error instanceof SyntaxError) {
80
+ throw new Error(`VTX token file is not valid JSON at ${path}. Run "vtx auth login" or remove the file and retry.`);
81
+ }
82
+ throw error;
83
+ }
84
+ }
85
+ var init_config = __esm({
86
+ "lib/agent-core/config.ts"() {
87
+ "use strict";
88
+ init_define_VTX_EXO_POLICY();
89
+ init_define_VTX_GROK_POLICY();
90
+ init_define_VTX_PI_MODEL_POLICY();
91
+ }
92
+ });
5
93
 
6
94
  // lib/agent-core/types.ts
7
- var AgentClientError = class extends Error {
8
- constructor(payload) {
9
- super(payload.message);
10
- this.name = "AgentClientError";
11
- this.status = payload.status;
12
- this.code = payload.code;
13
- this.detail = payload.detail;
95
+ var AgentClientError;
96
+ var init_types = __esm({
97
+ "lib/agent-core/types.ts"() {
98
+ "use strict";
99
+ init_define_VTX_EXO_POLICY();
100
+ init_define_VTX_GROK_POLICY();
101
+ init_define_VTX_PI_MODEL_POLICY();
102
+ AgentClientError = class extends Error {
103
+ constructor(payload) {
104
+ super(payload.message);
105
+ this.name = "AgentClientError";
106
+ this.status = payload.status;
107
+ this.code = payload.code;
108
+ this.detail = payload.detail;
109
+ }
110
+ };
14
111
  }
15
- };
112
+ });
16
113
 
17
114
  // lib/agent-core/client.ts
18
- var SECRET_RESPONSE_FIELDS = /* @__PURE__ */ new Set([
19
- "provider_key_slots",
20
- "hyperliquid_signing_key",
21
- "local_ai_api_key",
22
- "openai_compatible_api_key",
23
- "openai_api_key",
24
- "anthropic_api_key",
25
- "gemini_api_key",
26
- "grok_api_key",
27
- "groq_api_key",
28
- "openrouter_api_key",
29
- "venice_api_key",
30
- "deepseek_api_key",
31
- "mistral_api_key",
32
- "together_api_key",
33
- "fireworks_api_key",
34
- "byteplus_api_key",
35
- "cerebras_api_key",
36
- "moonshot_api_key",
37
- "alibaba_cloud_model_studio_api_key",
38
- "amazon_bedrock_api_key",
39
- "lightning_api_key"
40
- ]);
115
+ import { randomUUID as randomUUID2 } from "node:crypto";
41
116
  function normalizeApiUrl(value) {
42
117
  const parsed = String(value || "").trim();
43
118
  if (!parsed) {
@@ -83,768 +158,459 @@ function redactSecretResponse(value) {
83
158
  }
84
159
  return redacted;
85
160
  }
86
- var VtxAgentClient = class _VtxAgentClient {
87
- constructor(config) {
88
- this.apiUrl = normalizeApiUrl(config.apiUrl);
89
- this.token = String(config.token || "").trim() || null;
90
- this.activeProfileId = config.activeProfileId ?? null;
91
- this.fetchImpl = config.fetchImpl ?? fetch;
92
- }
93
- async request(path, options = {}) {
94
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
95
- const headers = {
96
- Accept: "application/json"
97
- };
98
- if (this.token) {
99
- headers.Authorization = `Bearer ${this.token}`;
100
- }
101
- const profileId = options.profileId ?? this.activeProfileId;
102
- if (profileId != null) {
103
- headers["x-profile-id"] = String(profileId);
104
- }
105
- if (options.idempotencyKey) {
106
- headers["x-idempotency-key"] = options.idempotencyKey;
107
- }
108
- if (options.headers) {
109
- for (const [key, value] of Object.entries(options.headers)) {
110
- if (value) {
111
- headers[key] = value;
161
+ function agentCliVersionHeaders() {
162
+ return { "X-VTX-CLI-Version": agent_cli_release_default.package_version };
163
+ }
164
+ function createAgentClient(config) {
165
+ return new VtxAgentClient(config);
166
+ }
167
+ var SECRET_RESPONSE_FIELDS, VtxAgentClient;
168
+ var init_client = __esm({
169
+ "lib/agent-core/client.ts"() {
170
+ "use strict";
171
+ init_define_VTX_EXO_POLICY();
172
+ init_define_VTX_GROK_POLICY();
173
+ init_define_VTX_PI_MODEL_POLICY();
174
+ init_agent_cli_release();
175
+ init_types();
176
+ SECRET_RESPONSE_FIELDS = /* @__PURE__ */ new Set([
177
+ "provider_key_slots",
178
+ "hyperliquid_signing_key",
179
+ "local_ai_api_key",
180
+ "openai_compatible_api_key",
181
+ "openai_api_key",
182
+ "anthropic_api_key",
183
+ "gemini_api_key",
184
+ "grok_api_key",
185
+ "groq_api_key",
186
+ "openrouter_api_key",
187
+ "venice_api_key",
188
+ "deepseek_api_key",
189
+ "mistral_api_key",
190
+ "together_api_key",
191
+ "fireworks_api_key",
192
+ "byteplus_api_key",
193
+ "cerebras_api_key",
194
+ "moonshot_api_key",
195
+ "alibaba_cloud_model_studio_api_key",
196
+ "amazon_bedrock_api_key",
197
+ "lightning_api_key"
198
+ ]);
199
+ VtxAgentClient = class _VtxAgentClient {
200
+ constructor(config) {
201
+ this.apiUrl = normalizeApiUrl(config.apiUrl);
202
+ this.token = String(config.token || "").trim() || null;
203
+ this.activeProfileId = config.activeProfileId ?? null;
204
+ this.fetchImpl = config.fetchImpl ?? fetch;
205
+ }
206
+ /** How shared web runtime code (lib/api.ts) reaches the API as this client. */
207
+ apiTransport() {
208
+ const token = this.token;
209
+ return {
210
+ baseUrl: this.apiUrl,
211
+ authorization: () => token ? `Bearer ${token}` : null,
212
+ // The shared runtime's requests name this CLI's version, as the agent
213
+ // client's own requests do.
214
+ headers: agentCliVersionHeaders
215
+ };
216
+ }
217
+ async request(path, options = {}) {
218
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
219
+ const headers = {
220
+ Accept: "application/json",
221
+ ...agentCliVersionHeaders()
222
+ };
223
+ if (this.token) {
224
+ headers.Authorization = `Bearer ${this.token}`;
112
225
  }
226
+ const profileId = options.profileId ?? this.activeProfileId;
227
+ if (profileId != null) {
228
+ headers["x-profile-id"] = String(profileId);
229
+ }
230
+ if (options.idempotencyKey) {
231
+ headers["x-idempotency-key"] = options.idempotencyKey;
232
+ }
233
+ if (options.headers) {
234
+ for (const [key, value] of Object.entries(options.headers)) {
235
+ if (value) {
236
+ headers[key] = value;
237
+ }
238
+ }
239
+ }
240
+ let body;
241
+ if (options.body !== void 0) {
242
+ headers["Content-Type"] = "application/json";
243
+ body = JSON.stringify(options.body);
244
+ }
245
+ const response = await this.fetchImpl(`${this.apiUrl}${normalizedPath}`, {
246
+ method: options.method ?? "GET",
247
+ headers,
248
+ body
249
+ });
250
+ if (!response.ok) {
251
+ throw await parseError(response, `VTX API request failed: ${response.status}`);
252
+ }
253
+ if (response.status === 204) {
254
+ return void 0;
255
+ }
256
+ return await response.json();
113
257
  }
114
- }
115
- let body;
116
- if (options.body !== void 0) {
117
- headers["Content-Type"] = "application/json";
118
- body = JSON.stringify(options.body);
119
- }
120
- const response = await this.fetchImpl(`${this.apiUrl}${normalizedPath}`, {
121
- method: options.method ?? "GET",
122
- headers,
123
- body
124
- });
125
- if (!response.ok) {
126
- throw await parseError(response, `VTX API request failed: ${response.status}`);
127
- }
128
- if (response.status === 204) {
129
- return void 0;
130
- }
131
- return await response.json();
132
- }
133
- whoami() {
134
- return this.request("/auth/agent-tokens/whoami");
135
- }
136
- listTokens() {
137
- return this.request("/auth/agent-tokens");
138
- }
139
- createToken(payload) {
140
- return this.request("/auth/agent-tokens", { method: "POST", body: payload });
141
- }
142
- startBrowserLogin(payload) {
143
- return this.request("/auth/agent-tokens/login/start", {
144
- method: "POST",
145
- body: payload
146
- });
147
- }
148
- pollBrowserLogin(loginCode, verifier) {
149
- return this.request("/auth/agent-tokens/login/poll", {
150
- method: "POST",
151
- body: { login_code: loginCode, verifier }
152
- });
153
- }
154
- revokeToken(tokenId, reason = "cli_revoked") {
155
- return this.request(`/auth/agent-tokens/${tokenId}`, {
156
- method: "DELETE",
157
- body: { reason }
158
- });
159
- }
160
- listProfiles() {
161
- return this.request("/auth/me/profiles");
162
- }
163
- createProfile(profileId) {
164
- return this.request("/auth/me/profiles", {
165
- method: "POST",
166
- profileId
167
- });
168
- }
169
- updateProfile(profileId, payload) {
170
- return this.request("/auth/me", {
171
- method: "PATCH",
172
- profileId,
173
- body: payload
174
- });
175
- }
176
- deleteProfile(profileId) {
177
- return this.request(`/auth/me/profiles/${profileId}`, {
178
- method: "DELETE"
179
- });
180
- }
181
- setProfileExecutionMode(profileId, executionMode) {
182
- return this.request("/preferences", {
183
- method: "PUT",
184
- profileId,
185
- body: { preferences: { execution_mode: executionMode } }
186
- });
187
- }
188
- updatePreferences(profileId, preferences) {
189
- return this.request("/preferences", {
190
- method: "PUT",
191
- profileId,
192
- body: { preferences }
193
- });
194
- }
195
- getBillingStatus() {
196
- return this.request("/billing/balance");
197
- }
198
- getBillingPrices(profileId) {
199
- return this.request("/billing/prices", { profileId });
258
+ whoami() {
259
+ return this.request("/auth/agent-tokens/whoami");
260
+ }
261
+ listTokens() {
262
+ return this.request("/auth/agent-tokens");
263
+ }
264
+ createToken(payload) {
265
+ return this.request("/auth/agent-tokens", { method: "POST", body: payload });
266
+ }
267
+ startBrowserLogin(payload) {
268
+ return this.request("/auth/agent-tokens/login/start", {
269
+ method: "POST",
270
+ body: payload
271
+ });
272
+ }
273
+ pollBrowserLogin(loginCode, verifier) {
274
+ return this.request("/auth/agent-tokens/login/poll", {
275
+ method: "POST",
276
+ body: { login_code: loginCode, verifier }
277
+ });
278
+ }
279
+ revokeToken(tokenId, reason = "cli_revoked") {
280
+ return this.request(`/auth/agent-tokens/${tokenId}`, {
281
+ method: "DELETE",
282
+ body: { reason }
283
+ });
284
+ }
285
+ listProfiles() {
286
+ return this.request("/auth/me/profiles");
287
+ }
288
+ createProfile(profileId) {
289
+ return this.request("/auth/me/profiles", {
290
+ method: "POST",
291
+ profileId
292
+ });
293
+ }
294
+ updateProfile(profileId, payload) {
295
+ return this.request("/auth/me", {
296
+ method: "PATCH",
297
+ profileId,
298
+ body: payload
299
+ });
300
+ }
301
+ deleteProfile(profileId) {
302
+ return this.request(`/auth/me/profiles/${profileId}`, {
303
+ method: "DELETE"
304
+ });
305
+ }
306
+ setProfileExecutionMode(profileId, executionMode) {
307
+ return this.request("/preferences", {
308
+ method: "PUT",
309
+ profileId,
310
+ body: { preferences: { execution_mode: executionMode } }
311
+ });
312
+ }
313
+ updatePreferences(profileId, preferences) {
314
+ return this.request("/preferences", {
315
+ method: "PUT",
316
+ profileId,
317
+ body: { preferences }
318
+ });
319
+ }
320
+ getBillingStatus() {
321
+ return this.request("/billing/balance");
322
+ }
323
+ getBillingPrices(profileId) {
324
+ return this.request("/billing/prices", { profileId });
325
+ }
326
+ getRuntimeStatus(profileId) {
327
+ return this.request("/trading/ai/status", { profileId });
328
+ }
329
+ getRuntimeEvents(profileId, options = {}) {
330
+ const params = new URLSearchParams();
331
+ if (options.limit !== void 0) {
332
+ params.set("limit", String(options.limit));
333
+ }
334
+ if (options.eventType) {
335
+ params.set("event_type", options.eventType);
336
+ }
337
+ if (options.clientEventType) {
338
+ params.set("client_event_type", options.clientEventType);
339
+ }
340
+ const query = params.toString();
341
+ return this.request(`/trading/ai/runtime/events${query ? `?${query}` : ""}`, { profileId });
342
+ }
343
+ getRuntimeBootstrap(profileId, leaseToken) {
344
+ return this.request("/trading/ai/runtime/bootstrap", {
345
+ profileId,
346
+ headers: { "x-client-runtime-lease": leaseToken }
347
+ });
348
+ }
349
+ getLlmConfig(profileId) {
350
+ return this.request("/llm/config", { profileId });
351
+ }
352
+ heartbeatRuntime(profileId, payload, leaseToken) {
353
+ return this.request("/trading/ai/runtime/heartbeat", {
354
+ method: "POST",
355
+ profileId,
356
+ headers: { "x-client-runtime-lease": leaseToken },
357
+ body: payload
358
+ });
359
+ }
360
+ getTradeHistory(profileId) {
361
+ return this.request("/trading/ai/history", { profileId });
362
+ }
363
+ placeMarketOrder(profileId, payload) {
364
+ return this.request("/trading/market-order", {
365
+ method: "POST",
366
+ profileId,
367
+ idempotencyKey: randomUUID2(),
368
+ body: payload
369
+ });
370
+ }
371
+ placeLimitOrder(profileId, payload) {
372
+ return this.request("/trading/limit-order", {
373
+ method: "POST",
374
+ profileId,
375
+ idempotencyKey: randomUUID2(),
376
+ body: payload
377
+ });
378
+ }
379
+ cancelOrder(profileId, payload) {
380
+ return this.request("/trading/cancel-order", {
381
+ method: "POST",
382
+ profileId,
383
+ idempotencyKey: randomUUID2(),
384
+ body: payload
385
+ });
386
+ }
387
+ getSecretStatus(profileId) {
388
+ return this.request("/auth/me", { profileId });
389
+ }
390
+ async setRuntimeSecret(profileId, fieldName, value) {
391
+ const response = await this.request("/auth/me/client-runtime-secrets", {
392
+ method: "PUT",
393
+ profileId,
394
+ body: { [fieldName]: value }
395
+ });
396
+ return redactSecretResponse(response);
397
+ }
398
+ startBot(profileId, payload = {}) {
399
+ return this.request("/trading/ai/start", {
400
+ method: "POST",
401
+ profileId,
402
+ idempotencyKey: randomUUID2(),
403
+ body: payload
404
+ });
405
+ }
406
+ stopBot(profileId) {
407
+ return this.request("/trading/ai/stop", {
408
+ method: "POST",
409
+ profileId,
410
+ idempotencyKey: randomUUID2(),
411
+ body: {}
412
+ });
413
+ }
414
+ startAssistant(profileId) {
415
+ return this.request("/trading/ai/assistant/start", {
416
+ method: "POST",
417
+ profileId,
418
+ idempotencyKey: randomUUID2(),
419
+ body: {}
420
+ });
421
+ }
422
+ stopAssistant(profileId) {
423
+ return this.request("/trading/ai/assistant/stop", {
424
+ method: "POST",
425
+ profileId,
426
+ idempotencyKey: randomUUID2(),
427
+ body: {}
428
+ });
429
+ }
430
+ startRuntime(profileId, payload) {
431
+ return this.request("/trading/ai/runtime/session/start", {
432
+ method: "POST",
433
+ profileId,
434
+ idempotencyKey: randomUUID2(),
435
+ body: payload
436
+ });
437
+ }
438
+ stopRuntime(profileId, payload, leaseToken) {
439
+ const headersLease = String(leaseToken || "").trim();
440
+ const previousFetch = this.fetchImpl;
441
+ if (!headersLease) {
442
+ return this.request("/trading/ai/runtime/session/stop", {
443
+ method: "POST",
444
+ profileId,
445
+ idempotencyKey: randomUUID2(),
446
+ body: payload
447
+ });
448
+ }
449
+ const withLeaseFetch = async (input, init) => {
450
+ const headers = new Headers(init?.headers);
451
+ headers.set("x-client-runtime-lease", headersLease);
452
+ return previousFetch(input, { ...init, headers });
453
+ };
454
+ return new _VtxAgentClient({
455
+ apiUrl: this.apiUrl,
456
+ token: this.token,
457
+ activeProfileId: this.activeProfileId,
458
+ fetchImpl: withLeaseFetch
459
+ }).request("/trading/ai/runtime/session/stop", {
460
+ method: "POST",
461
+ profileId,
462
+ idempotencyKey: randomUUID2(),
463
+ body: payload
464
+ });
465
+ }
466
+ };
200
467
  }
201
- getRuntimeStatus(profileId) {
202
- return this.request("/trading/ai/status", { profileId });
468
+ });
469
+
470
+ // lib/runtime/hyperliquid-market-symbol.ts
471
+ var configuredCaseSensitiveSymbols, canonicalBaseSymbol, normalizeHyperliquidMarketSymbol;
472
+ var init_hyperliquid_market_symbol = __esm({
473
+ "lib/runtime/hyperliquid-market-symbol.ts"() {
474
+ "use strict";
475
+ init_define_VTX_EXO_POLICY();
476
+ init_define_VTX_GROK_POLICY();
477
+ init_define_VTX_PI_MODEL_POLICY();
478
+ configuredCaseSensitiveSymbols = [];
479
+ canonicalBaseSymbol = (base) => {
480
+ const trimmed = base.trim();
481
+ if (!trimmed) return "";
482
+ const upper = trimmed.toUpperCase();
483
+ for (const candidate of configuredCaseSensitiveSymbols) {
484
+ if (candidate.toUpperCase() === upper) {
485
+ return candidate;
486
+ }
487
+ }
488
+ return upper;
489
+ };
490
+ normalizeHyperliquidMarketSymbol = (value) => {
491
+ const raw = String(value ?? "").trim();
492
+ if (!raw) return "";
493
+ if (!raw.includes(":")) return canonicalBaseSymbol(raw);
494
+ const [dex, ...rest] = raw.split(":");
495
+ const base = rest.join(":").trim();
496
+ const normalizedDex = dex.trim().toLowerCase();
497
+ if (!normalizedDex || !base) return "";
498
+ return `${normalizedDex}:${canonicalBaseSymbol(base)}`;
499
+ };
203
500
  }
204
- getRuntimeEvents(profileId, options = {}) {
205
- const params = new URLSearchParams();
206
- if (options.limit !== void 0) {
207
- params.set("limit", String(options.limit));
208
- }
209
- if (options.eventType) {
210
- params.set("event_type", options.eventType);
211
- }
212
- if (options.clientEventType) {
213
- params.set("client_event_type", options.clientEventType);
214
- }
215
- const query = params.toString();
216
- return this.request(`/trading/ai/runtime/events${query ? `?${query}` : ""}`, { profileId });
501
+ });
502
+
503
+ // lib/agent-mcp/server.ts
504
+ function requirePositiveNumber(input, key) {
505
+ const value = Number(input[key]);
506
+ if (!Number.isFinite(value) || value <= 0) {
507
+ throw new Error(`${key} must be a positive number`);
217
508
  }
218
- getRuntimeBootstrap(profileId, leaseToken) {
219
- return this.request("/trading/ai/runtime/bootstrap", {
220
- profileId,
221
- headers: { "x-client-runtime-lease": leaseToken }
222
- });
509
+ return value;
510
+ }
511
+ function optionalPositiveNumber(input, key) {
512
+ if (input[key] === void 0 || input[key] === null || input[key] === "") {
513
+ return void 0;
223
514
  }
224
- getRuntimeSecrets(profileId, leaseToken, options = {}) {
225
- const params = new URLSearchParams();
226
- if (options.activeProvider) {
227
- params.set("active_provider", options.activeProvider);
228
- }
229
- if (typeof options.includeAllProviderKeys === "boolean") {
230
- params.set("include_all_provider_keys", String(options.includeAllProviderKeys));
515
+ return requirePositiveNumber(input, key);
516
+ }
517
+ function optionalString(input, key) {
518
+ const value = String(input[key] || "").trim();
519
+ return value || null;
520
+ }
521
+ function botStartPayload(input) {
522
+ const payload = {};
523
+ for (const key of ["symbol", "timeframe", "size"]) {
524
+ const value = optionalString(input, key);
525
+ if (value) {
526
+ payload[key] = value;
231
527
  }
232
- const query = params.toString();
233
- return this.request(`/auth/me/client-runtime-secrets${query ? `?${query}` : ""}`, {
234
- profileId,
235
- headers: { "x-client-runtime-lease": leaseToken }
236
- });
237
- }
238
- getRuntimePromptContractMetadata(profileId, payload, leaseToken) {
239
- return this.request("/trading/ai/runtime/prompt-contract/metadata", {
240
- method: "POST",
241
- profileId,
242
- headers: { "x-client-runtime-lease": leaseToken },
243
- body: payload
244
- });
245
528
  }
246
- getRuntimePromptContractDerivedContext(profileId, payload, leaseToken) {
247
- return this.request("/trading/ai/runtime/prompt-contract/derived-context", {
248
- method: "POST",
249
- profileId,
250
- headers: { "x-client-runtime-lease": leaseToken },
251
- body: payload
252
- });
529
+ const leverage = optionalPositiveNumber(input, "leverage");
530
+ if (leverage !== void 0) {
531
+ payload.leverage = leverage;
253
532
  }
254
- getRuntimePrompt(profileId, payload, leaseToken) {
255
- return this.request("/trading/ai/runtime/prompt", {
256
- method: "POST",
257
- profileId,
258
- headers: { "x-client-runtime-lease": leaseToken },
259
- body: payload
260
- });
533
+ const interval = optionalPositiveNumber(input, "interval");
534
+ if (interval !== void 0) {
535
+ payload.interval = interval;
261
536
  }
262
- getLlmConfig(profileId) {
263
- return this.request("/llm/config", { profileId });
537
+ return payload;
538
+ }
539
+ function botConfigurePreferences(input) {
540
+ const preferences = {};
541
+ const trade = {};
542
+ const symbol = optionalString(input, "symbol");
543
+ if (symbol) {
544
+ const normalizedSymbol = normalizeHyperliquidMarketSymbol(symbol);
545
+ preferences.trading_symbols = [normalizedSymbol];
546
+ trade.tradingSymbol = normalizedSymbol;
264
547
  }
265
- heartbeatRuntime(profileId, payload, leaseToken) {
266
- return this.request("/trading/ai/runtime/heartbeat", {
267
- method: "POST",
268
- profileId,
269
- headers: { "x-client-runtime-lease": leaseToken },
270
- body: payload
271
- });
548
+ const timeframe = optionalString(input, "timeframe");
549
+ if (timeframe) {
550
+ preferences.trading_timeframes = [timeframe.trim()];
551
+ trade.chartInterval = timeframe.trim();
272
552
  }
273
- reportRuntimeDecision(profileId, payload, leaseToken) {
274
- return this.request("/trading/ai/runtime/decision", {
275
- method: "POST",
276
- profileId,
277
- idempotencyKey: randomUUID(),
278
- headers: { "x-client-runtime-lease": leaseToken },
279
- body: payload
280
- });
553
+ const size = optionalString(input, "size");
554
+ if (size) {
555
+ preferences.ai_trade_amount = Number(size);
556
+ trade.orderSizeUSDC = size;
281
557
  }
282
- // Client Mode history pages this host read for the server's heartbeat asks.
283
- reportRuntimeHistoryEvidence(profileId, payload, leaseToken) {
284
- return this.request("/trading/ai/runtime/history-evidence", {
285
- method: "POST",
286
- profileId,
287
- headers: { "x-client-runtime-lease": leaseToken },
288
- body: payload
289
- });
558
+ const leverage = optionalPositiveNumber(input, "leverage");
559
+ if (leverage !== void 0) {
560
+ trade.leverage = leverage;
290
561
  }
291
- reportRuntimeTradeSync(profileId, payload, leaseToken) {
292
- return this.request("/trading/ai/runtime/trade-sync", {
293
- method: "POST",
294
- profileId,
295
- idempotencyKey: randomUUID(),
296
- headers: { "x-client-runtime-lease": leaseToken },
297
- body: payload
298
- });
562
+ const interval = optionalPositiveNumber(input, "interval");
563
+ if (interval !== void 0) {
564
+ preferences.trading_interval = interval;
299
565
  }
300
- reportRuntimeError(profileId, payload, leaseToken) {
301
- return this.request("/trading/ai/runtime/error", {
302
- method: "POST",
303
- profileId,
304
- idempotencyKey: randomUUID(),
305
- headers: { "x-client-runtime-lease": leaseToken },
306
- body: payload
307
- });
566
+ if (Object.keys(trade).length > 0) {
567
+ preferences.trade = trade;
308
568
  }
309
- getTradeHistory(profileId) {
310
- return this.request("/trading/ai/history", { profileId });
569
+ if (Object.keys(preferences).length === 0) {
570
+ throw new Error("At least one bot configuration field is required");
311
571
  }
312
- placeMarketOrder(profileId, payload) {
313
- return this.request("/trading/market-order", {
314
- method: "POST",
315
- profileId,
316
- idempotencyKey: randomUUID(),
317
- body: payload
318
- });
572
+ return preferences;
573
+ }
574
+ function requireOrderSide(input) {
575
+ const side = optionalString(input, "side");
576
+ if (side !== "buy" && side !== "sell") {
577
+ throw new Error("side must be buy or sell");
319
578
  }
320
- placeLimitOrder(profileId, payload) {
321
- return this.request("/trading/limit-order", {
322
- method: "POST",
323
- profileId,
324
- idempotencyKey: randomUUID(),
325
- body: payload
326
- });
579
+ return side;
580
+ }
581
+ function requireSymbol(input) {
582
+ const symbol = optionalString(input, "symbol");
583
+ if (!symbol) {
584
+ throw new Error("symbol is required");
327
585
  }
328
- cancelOrder(profileId, payload) {
329
- return this.request("/trading/cancel-order", {
330
- method: "POST",
331
- profileId,
332
- idempotencyKey: randomUUID(),
333
- body: payload
334
- });
586
+ return normalizeHyperliquidMarketSymbol(symbol);
587
+ }
588
+ function marketOrderPayload(input) {
589
+ const payload = {
590
+ symbol: requireSymbol(input),
591
+ side: requireOrderSide(input),
592
+ size: requirePositiveNumber(input, "size")
593
+ };
594
+ const slippage = optionalPositiveNumber(input, "slippage");
595
+ if (slippage !== void 0) {
596
+ payload.slippage = slippage;
335
597
  }
336
- getSecretStatus(profileId) {
337
- return this.request("/auth/me", { profileId });
598
+ const estimatedPrice = optionalPositiveNumber(input, "estimatedPrice");
599
+ if (estimatedPrice !== void 0) {
600
+ payload.estimated_price = estimatedPrice;
338
601
  }
339
- async setRuntimeSecret(profileId, fieldName, value) {
340
- const response = await this.request("/auth/me/client-runtime-secrets", {
341
- method: "PUT",
342
- profileId,
343
- body: { [fieldName]: value }
344
- });
345
- return redactSecretResponse(response);
346
- }
347
- startBot(profileId, payload = {}) {
348
- return this.request("/trading/ai/start", {
349
- method: "POST",
350
- profileId,
351
- idempotencyKey: randomUUID(),
352
- body: payload
353
- });
602
+ const estimatedPriceTimestampMs = optionalPositiveNumber(input, "estimatedPriceTimestampMs");
603
+ if (estimatedPriceTimestampMs !== void 0) {
604
+ payload.estimated_price_timestamp_ms = estimatedPriceTimestampMs;
354
605
  }
355
- stopBot(profileId) {
356
- return this.request("/trading/ai/stop", {
357
- method: "POST",
358
- profileId,
359
- idempotencyKey: randomUUID(),
360
- body: {}
361
- });
606
+ if (input.reduceOnly === true) {
607
+ payload.reduce_only = true;
362
608
  }
363
- startAssistant(profileId) {
364
- return this.request("/trading/ai/assistant/start", {
365
- method: "POST",
366
- profileId,
367
- idempotencyKey: randomUUID(),
368
- body: {}
369
- });
370
- }
371
- stopAssistant(profileId) {
372
- return this.request("/trading/ai/assistant/stop", {
373
- method: "POST",
374
- profileId,
375
- idempotencyKey: randomUUID(),
376
- body: {}
377
- });
378
- }
379
- startRuntime(profileId, payload) {
380
- return this.request("/trading/ai/runtime/session/start", {
381
- method: "POST",
382
- profileId,
383
- idempotencyKey: randomUUID(),
384
- body: payload
385
- });
386
- }
387
- stopRuntime(profileId, payload, leaseToken) {
388
- const headersLease = String(leaseToken || "").trim();
389
- const previousFetch = this.fetchImpl;
390
- if (!headersLease) {
391
- return this.request("/trading/ai/runtime/session/stop", {
392
- method: "POST",
393
- profileId,
394
- idempotencyKey: randomUUID(),
395
- body: payload
396
- });
397
- }
398
- const withLeaseFetch = async (input, init) => {
399
- const headers = new Headers(init?.headers);
400
- headers.set("x-client-runtime-lease", headersLease);
401
- return previousFetch(input, { ...init, headers });
402
- };
403
- return new _VtxAgentClient({
404
- apiUrl: this.apiUrl,
405
- token: this.token,
406
- activeProfileId: this.activeProfileId,
407
- fetchImpl: withLeaseFetch
408
- }).request("/trading/ai/runtime/session/stop", {
409
- method: "POST",
410
- profileId,
411
- idempotencyKey: randomUUID(),
412
- body: payload
413
- });
414
- }
415
- };
416
- function createAgentClient(config) {
417
- return new VtxAgentClient(config);
418
- }
419
-
420
- // lib/agent-core/config.ts
421
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
422
- import { dirname, join } from "node:path";
423
- import { homedir } from "node:os";
424
- function defaultBaseDir() {
425
- return join(homedir(), ".vtx");
426
- }
427
- function resolveAgentCliConfig(env = process.env) {
428
- const baseDir = String(env.VTX_HOME || "").trim() || defaultBaseDir();
429
- const rawProfile = String(env.VTX_PROFILE_ID || "").trim();
430
- const parsedProfile = rawProfile ? Number(rawProfile) : NaN;
431
- return {
432
- apiUrl: String(env.VTX_API_URL || "http://localhost:8000").replace(/\/+$/, ""),
433
- tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join(baseDir, "token.json"),
434
- statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join(baseDir, "runtime-state.json"),
435
- runtimeDeviceId: String(env.VTX_RUNTIME_DEVICE_ID || "").trim() || null,
436
- activeProfileId: Number.isFinite(parsedProfile) && parsedProfile > 0 ? parsedProfile : null,
437
- outputJson: String(env.VTX_OUTPUT || "").trim().toLowerCase() === "json"
438
- };
439
- }
440
- async function readStoredAgentAuth(path) {
441
- try {
442
- const raw = await readFile(path, "utf8");
443
- if (!raw.trim()) {
444
- throw new Error(`VTX token file is empty at ${path}. Run "vtx auth login" or remove the file and retry.`);
445
- }
446
- const parsed = JSON.parse(raw);
447
- const token = String(parsed.token || "").trim();
448
- return token ? { token } : null;
449
- } catch (error) {
450
- if (error.code === "ENOENT") {
451
- return null;
452
- }
453
- if (error instanceof SyntaxError) {
454
- throw new Error(`VTX token file is not valid JSON at ${path}. Run "vtx auth login" or remove the file and retry.`);
455
- }
456
- throw error;
457
- }
458
- }
459
-
460
- // lib/runtime/hyperliquid-market-symbol.ts
461
- var configuredCaseSensitiveSymbols = [];
462
- var canonicalBaseSymbol = (base) => {
463
- const trimmed = base.trim();
464
- if (!trimmed) return "";
465
- const upper = trimmed.toUpperCase();
466
- for (const candidate of configuredCaseSensitiveSymbols) {
467
- if (candidate.toUpperCase() === upper) {
468
- return candidate;
469
- }
470
- }
471
- return upper;
472
- };
473
- var normalizeHyperliquidMarketSymbol = (value) => {
474
- const raw = String(value ?? "").trim();
475
- if (!raw) return "";
476
- if (!raw.includes(":")) return canonicalBaseSymbol(raw);
477
- const [dex, ...rest] = raw.split(":");
478
- const base = rest.join(":").trim();
479
- const normalizedDex = dex.trim().toLowerCase();
480
- if (!normalizedDex || !base) return "";
481
- return `${normalizedDex}:${canonicalBaseSymbol(base)}`;
482
- };
483
-
484
- // lib/agent-mcp/server.ts
485
- var VTX_MCP_TOOLS = [
486
- {
487
- name: "vtx_whoami",
488
- description: "Inspect the authenticated VTX user and agent-token scopes.",
489
- inputSchema: { type: "object", properties: {} }
490
- },
491
- {
492
- name: "vtx_profiles_list",
493
- description: "List profiles owned by the authenticated VTX user.",
494
- inputSchema: { type: "object", properties: {} }
495
- },
496
- {
497
- name: "vtx_profiles_create",
498
- description: "Create a profile for the authenticated VTX user. Requires profile:write scope.",
499
- inputSchema: {
500
- type: "object",
501
- properties: { sourceProfileId: { type: "number" } }
502
- }
503
- },
504
- {
505
- name: "vtx_profiles_update",
506
- description: "Update the selected profile handle for the authenticated VTX user. Requires profile:write scope.",
507
- inputSchema: {
508
- type: "object",
509
- required: ["profileId", "name"],
510
- properties: { profileId: { type: "number" }, name: { type: "string" } }
511
- }
512
- },
513
- {
514
- name: "vtx_profiles_delete",
515
- description: "Delete a profile owned by the authenticated VTX user. Requires profile:write scope.",
516
- inputSchema: {
517
- type: "object",
518
- required: ["profileId"],
519
- properties: { profileId: { type: "number" } }
520
- }
521
- },
522
- {
523
- name: "vtx_profiles_set_mode",
524
- description: "Set a profile execution mode. Client Mode runs locally with no VTX platform fee; Server Mode uses normal VTX infrastructure and fees.",
525
- inputSchema: {
526
- type: "object",
527
- required: ["profileId", "mode"],
528
- properties: { profileId: { type: "number" }, mode: { type: "string", enum: ["client", "server"] } }
529
- }
530
- },
531
- {
532
- name: "vtx_bots_start",
533
- description: "Start the Server Mode AI Trader for a profile through the normal backend path. Requires bot:control scope and normal Server Mode billing/caps.",
534
- inputSchema: {
535
- type: "object",
536
- required: ["profileId"],
537
- properties: {
538
- profileId: { type: "number" },
539
- symbol: { type: "string" },
540
- timeframe: { type: "string" },
541
- size: { type: "string" },
542
- leverage: { type: "number" },
543
- interval: { type: "number" }
544
- }
545
- }
546
- },
547
- {
548
- name: "vtx_bots_configure",
549
- description: "Configure AI Trader profile preferences through the normal preferences API before starting a bot. Requires profile:write scope.",
550
- inputSchema: {
551
- type: "object",
552
- required: ["profileId"],
553
- properties: {
554
- profileId: { type: "number" },
555
- symbol: { type: "string" },
556
- timeframe: { type: "string" },
557
- size: { type: "string" },
558
- leverage: { type: "number" },
559
- interval: { type: "number" }
560
- }
561
- }
562
- },
563
- {
564
- name: "vtx_bots_stop",
565
- description: "Stop the Server Mode AI Trader for a profile through the normal backend path. Requires bot:control scope.",
566
- inputSchema: {
567
- type: "object",
568
- required: ["profileId"],
569
- properties: { profileId: { type: "number" } }
570
- }
571
- },
572
- {
573
- name: "vtx_bots_status",
574
- description: "Read bot/runtime status for a profile.",
575
- inputSchema: {
576
- type: "object",
577
- required: ["profileId"],
578
- properties: { profileId: { type: "number" } }
579
- }
580
- },
581
- {
582
- name: "vtx_bots_logs",
583
- description: "Read AI Trader decision history/logs for a profile without executing trades.",
584
- inputSchema: {
585
- type: "object",
586
- required: ["profileId"],
587
- properties: { profileId: { type: "number" } }
588
- }
589
- },
590
- {
591
- name: "vtx_runtime_status",
592
- description: "Read Client Mode runtime status for a profile. Client Mode has no VTX platform fee; Server Mode bots use normal fees.",
593
- inputSchema: {
594
- type: "object",
595
- required: ["profileId"],
596
- properties: { profileId: { type: "number" } }
597
- }
598
- },
599
- {
600
- name: "vtx_runtime_events",
601
- description: "Read Client Mode runtime events for a profile. This is status/history visibility and does not execute local runtime work.",
602
- inputSchema: {
603
- type: "object",
604
- required: ["profileId"],
605
- properties: {
606
- profileId: { type: "number" },
607
- limit: { type: "number" },
608
- eventType: { type: "string" },
609
- clientEventType: { type: "string" }
610
- }
611
- }
612
- },
613
- {
614
- name: "vtx_trade_market_order",
615
- description: "Submit a market order for a profile through the normal Trade page backend path. Requires trading:execute scope and uses the same risk checks, rate limits, and idempotency behavior as a user-submitted order.",
616
- inputSchema: {
617
- type: "object",
618
- required: ["profileId", "symbol", "side", "size"],
619
- properties: {
620
- profileId: { type: "number" },
621
- symbol: { type: "string" },
622
- side: { type: "string", enum: ["buy", "sell"] },
623
- size: { type: "number" },
624
- slippage: { type: "number" },
625
- estimatedPrice: { type: "number" },
626
- estimatedPriceTimestampMs: { type: "number" },
627
- reduceOnly: { type: "boolean" },
628
- protection: {
629
- type: "object",
630
- description: 'Optional fixed TP/SL like the AI page Auto SL/TP: take_profit and/or stop_loss, each { mode: "percent" | "usdc", value }. Placed as reduce-only market triggers from the average fill; not allowed with reduceOnly; no trailing stops.',
631
- properties: {
632
- take_profit: { type: "object", required: ["mode", "value"], properties: { mode: { type: "string", enum: ["percent", "usdc"] }, value: { type: "number" } } },
633
- stop_loss: { type: "object", required: ["mode", "value"], properties: { mode: { type: "string", enum: ["percent", "usdc"] }, value: { type: "number" }, stop_type: { type: "string", enum: ["fixed"] } } }
634
- }
635
- }
636
- }
637
- }
638
- },
639
- {
640
- name: "vtx_trade_limit_order",
641
- description: "Submit a limit order for a profile through the normal Trade page backend path. Requires trading:execute scope and uses the same risk checks, rate limits, and idempotency behavior as a user-submitted order.",
642
- inputSchema: {
643
- type: "object",
644
- required: ["profileId", "symbol", "side", "size", "limitPrice"],
645
- properties: {
646
- profileId: { type: "number" },
647
- symbol: { type: "string" },
648
- side: { type: "string", enum: ["buy", "sell"] },
649
- size: { type: "number" },
650
- limitPrice: { type: "number" },
651
- reduceOnly: { type: "boolean" }
652
- }
653
- }
654
- },
655
- {
656
- name: "vtx_trade_cancel_order",
657
- description: "Cancel an open order for a profile through the normal Trade page backend path. Requires trading:execute scope.",
658
- inputSchema: {
659
- type: "object",
660
- required: ["profileId", "symbol", "oid"],
661
- properties: {
662
- profileId: { type: "number" },
663
- symbol: { type: "string" },
664
- oid: { type: "number" }
665
- }
666
- }
667
- },
668
- {
669
- name: "vtx_tokens_list",
670
- description: "List agent tokens without returning raw token values.",
671
- inputSchema: { type: "object", properties: {} }
672
- },
673
- {
674
- name: "vtx_billing_status",
675
- description: "Read VTX billing balance for the authenticated user.",
676
- inputSchema: { type: "object", properties: {} }
677
- },
678
- {
679
- name: "vtx_billing_prices",
680
- description: "Read VTX pricing visibility, including Server Mode trader call fee and model pricing metadata.",
681
- inputSchema: {
682
- type: "object",
683
- properties: { profileId: { type: "number" } }
684
- }
685
- },
686
- {
687
- name: "vtx_ai_config",
688
- description: "Read the shared AI/runtime configuration contract used by the web app, CLI, MCP, and Client Mode runtime.",
689
- inputSchema: {
690
- type: "object",
691
- properties: { profileId: { type: "number" } }
692
- }
693
- },
694
- {
695
- name: "vtx_secrets_status",
696
- description: "Read secret readiness and masked previews for a profile. Raw stored secret values are not returned.",
697
- inputSchema: {
698
- type: "object",
699
- required: ["profileId"],
700
- properties: { profileId: { type: "number" } }
701
- }
702
- },
703
- {
704
- name: "vtx_secrets_set",
705
- description: "Write or rotate one runtime secret for a profile. The response redacts raw secret values.",
706
- inputSchema: {
707
- type: "object",
708
- required: ["profileId", "field", "value"],
709
- properties: {
710
- profileId: { type: "number" },
711
- field: { type: "string" },
712
- value: { type: "string" }
713
- }
714
- }
715
- }
716
- ];
717
- var WRITABLE_SECRET_FIELDS = /* @__PURE__ */ new Set([
718
- "hyperliquid_signing_key",
719
- "local_ai_api_key",
720
- "openai_compatible_api_key",
721
- "openai_api_key",
722
- "anthropic_api_key",
723
- "gemini_api_key",
724
- "grok_api_key",
725
- "groq_api_key",
726
- "openrouter_api_key",
727
- "venice_api_key",
728
- "deepseek_api_key",
729
- "mistral_api_key",
730
- "together_api_key",
731
- "fireworks_api_key",
732
- "byteplus_api_key",
733
- "cerebras_api_key",
734
- "moonshot_api_key",
735
- "alibaba_cloud_model_studio_api_key",
736
- "lightning_api_key"
737
- ]);
738
- function requirePositiveNumber(input, key) {
739
- const value = Number(input[key]);
740
- if (!Number.isFinite(value) || value <= 0) {
741
- throw new Error(`${key} must be a positive number`);
742
- }
743
- return value;
744
- }
745
- function optionalPositiveNumber(input, key) {
746
- if (input[key] === void 0 || input[key] === null || input[key] === "") {
747
- return void 0;
748
- }
749
- return requirePositiveNumber(input, key);
750
- }
751
- function optionalString(input, key) {
752
- const value = String(input[key] || "").trim();
753
- return value || null;
754
- }
755
- function botStartPayload(input) {
756
- const payload = {};
757
- for (const key of ["symbol", "timeframe", "size"]) {
758
- const value = optionalString(input, key);
759
- if (value) {
760
- payload[key] = value;
761
- }
762
- }
763
- const leverage = optionalPositiveNumber(input, "leverage");
764
- if (leverage !== void 0) {
765
- payload.leverage = leverage;
766
- }
767
- const interval = optionalPositiveNumber(input, "interval");
768
- if (interval !== void 0) {
769
- payload.interval = interval;
770
- }
771
- return payload;
772
- }
773
- function botConfigurePreferences(input) {
774
- const preferences = {};
775
- const trade = {};
776
- const symbol = optionalString(input, "symbol");
777
- if (symbol) {
778
- const normalizedSymbol = normalizeHyperliquidMarketSymbol(symbol);
779
- preferences.trading_symbols = [normalizedSymbol];
780
- trade.tradingSymbol = normalizedSymbol;
781
- }
782
- const timeframe = optionalString(input, "timeframe");
783
- if (timeframe) {
784
- preferences.trading_timeframes = [timeframe.trim()];
785
- trade.chartInterval = timeframe.trim();
786
- }
787
- const size = optionalString(input, "size");
788
- if (size) {
789
- preferences.ai_trade_amount = Number(size);
790
- trade.orderSizeUSDC = size;
791
- }
792
- const leverage = optionalPositiveNumber(input, "leverage");
793
- if (leverage !== void 0) {
794
- trade.leverage = leverage;
795
- }
796
- const interval = optionalPositiveNumber(input, "interval");
797
- if (interval !== void 0) {
798
- preferences.trading_interval = interval;
799
- }
800
- if (Object.keys(trade).length > 0) {
801
- preferences.trade = trade;
802
- }
803
- if (Object.keys(preferences).length === 0) {
804
- throw new Error("At least one bot configuration field is required");
805
- }
806
- return preferences;
807
- }
808
- function requireOrderSide(input) {
809
- const side = optionalString(input, "side");
810
- if (side !== "buy" && side !== "sell") {
811
- throw new Error("side must be buy or sell");
812
- }
813
- return side;
814
- }
815
- function requireSymbol(input) {
816
- const symbol = optionalString(input, "symbol");
817
- if (!symbol) {
818
- throw new Error("symbol is required");
819
- }
820
- return normalizeHyperliquidMarketSymbol(symbol);
821
- }
822
- function marketOrderPayload(input) {
823
- const payload = {
824
- symbol: requireSymbol(input),
825
- side: requireOrderSide(input),
826
- size: requirePositiveNumber(input, "size")
827
- };
828
- const slippage = optionalPositiveNumber(input, "slippage");
829
- if (slippage !== void 0) {
830
- payload.slippage = slippage;
831
- }
832
- const estimatedPrice = optionalPositiveNumber(input, "estimatedPrice");
833
- if (estimatedPrice !== void 0) {
834
- payload.estimated_price = estimatedPrice;
835
- }
836
- const estimatedPriceTimestampMs = optionalPositiveNumber(input, "estimatedPriceTimestampMs");
837
- if (estimatedPriceTimestampMs !== void 0) {
838
- payload.estimated_price_timestamp_ms = estimatedPriceTimestampMs;
839
- }
840
- if (input.reduceOnly === true) {
841
- payload.reduce_only = true;
842
- }
843
- if (input.protection !== void 0) {
844
- if (input.protection === null || typeof input.protection !== "object" || Array.isArray(input.protection)) {
845
- throw new Error("protection must be an object with take_profit and/or stop_loss");
846
- }
847
- payload.protection = input.protection;
609
+ if (input.protection !== void 0) {
610
+ if (input.protection === null || typeof input.protection !== "object" || Array.isArray(input.protection)) {
611
+ throw new Error("protection must be an object with take_profit and/or stop_loss");
612
+ }
613
+ payload.protection = input.protection;
848
614
  }
849
615
  return payload;
850
616
  }
@@ -967,50 +733,320 @@ async function callVtxMcpTool(client, name, input = {}) {
967
733
  }
968
734
  throw new Error(`Unknown VTX MCP tool: ${name}`);
969
735
  }
970
-
971
- // lib/agent-mcp/stdio.ts
972
- var MCP_PROTOCOL_VERSION = "2024-11-05";
973
- function jsonRpcResult(id, result) {
974
- return { jsonrpc: "2.0", id, result };
975
- }
976
- function jsonRpcError(id, code, message, data) {
977
- return { jsonrpc: "2.0", id, error: { code, message, data } };
978
- }
979
- function requestId(request) {
980
- return request.id === void 0 ? null : request.id;
981
- }
982
- function isNotification(request) {
983
- return request.id === void 0;
984
- }
985
- function stringifyToolResult(result) {
986
- if (typeof result === "string") {
987
- return result;
988
- }
989
- return JSON.stringify(result, null, 2);
990
- }
991
- async function handleMcpJsonRpcRequest(request, clientFactory = createDefaultMcpClient) {
992
- const id = requestId(request);
993
- const method = String(request.method || "").trim();
994
- if (!method) {
995
- return jsonRpcError(id, -32600, "Invalid JSON-RPC request.");
996
- }
997
- if (method === "initialize") {
998
- return jsonRpcResult(id, {
999
- protocolVersion: MCP_PROTOCOL_VERSION,
1000
- capabilities: {
1001
- tools: {}
736
+ var VTX_MCP_TOOLS, WRITABLE_SECRET_FIELDS;
737
+ var init_server = __esm({
738
+ "lib/agent-mcp/server.ts"() {
739
+ "use strict";
740
+ init_define_VTX_EXO_POLICY();
741
+ init_define_VTX_GROK_POLICY();
742
+ init_define_VTX_PI_MODEL_POLICY();
743
+ init_client();
744
+ init_config();
745
+ init_hyperliquid_market_symbol();
746
+ VTX_MCP_TOOLS = [
747
+ {
748
+ name: "vtx_whoami",
749
+ description: "Inspect the authenticated VTX user and agent-token scopes.",
750
+ inputSchema: { type: "object", properties: {} }
1002
751
  },
1003
- serverInfo: {
1004
- name: "vtx-macro",
1005
- version: "2026.6.23"
1006
- }
1007
- });
1008
- }
1009
- if (method === "notifications/initialized") {
1010
- return null;
1011
- }
1012
- if (method === "ping") {
1013
- return jsonRpcResult(id, {});
752
+ {
753
+ name: "vtx_profiles_list",
754
+ description: "List profiles owned by the authenticated VTX user.",
755
+ inputSchema: { type: "object", properties: {} }
756
+ },
757
+ {
758
+ name: "vtx_profiles_create",
759
+ description: "Create a profile for the authenticated VTX user. Requires profile:write scope.",
760
+ inputSchema: {
761
+ type: "object",
762
+ properties: { sourceProfileId: { type: "number" } }
763
+ }
764
+ },
765
+ {
766
+ name: "vtx_profiles_update",
767
+ description: "Update the selected profile handle for the authenticated VTX user. Requires profile:write scope.",
768
+ inputSchema: {
769
+ type: "object",
770
+ required: ["profileId", "name"],
771
+ properties: { profileId: { type: "number" }, name: { type: "string" } }
772
+ }
773
+ },
774
+ {
775
+ name: "vtx_profiles_delete",
776
+ description: "Delete a profile owned by the authenticated VTX user. Requires profile:write scope.",
777
+ inputSchema: {
778
+ type: "object",
779
+ required: ["profileId"],
780
+ properties: { profileId: { type: "number" } }
781
+ }
782
+ },
783
+ {
784
+ name: "vtx_profiles_set_mode",
785
+ description: "Set a profile execution mode. Client Mode runs locally with no VTX platform fee; Server Mode uses normal VTX infrastructure and fees.",
786
+ inputSchema: {
787
+ type: "object",
788
+ required: ["profileId", "mode"],
789
+ properties: { profileId: { type: "number" }, mode: { type: "string", enum: ["client", "server"] } }
790
+ }
791
+ },
792
+ {
793
+ name: "vtx_bots_start",
794
+ description: "Start the Server Mode AI Trader for a profile through the normal backend path. Requires bot:control scope and normal Server Mode billing/caps.",
795
+ inputSchema: {
796
+ type: "object",
797
+ required: ["profileId"],
798
+ properties: {
799
+ profileId: { type: "number" },
800
+ symbol: { type: "string" },
801
+ timeframe: { type: "string" },
802
+ size: { type: "string" },
803
+ leverage: { type: "number" },
804
+ interval: { type: "number" }
805
+ }
806
+ }
807
+ },
808
+ {
809
+ name: "vtx_bots_configure",
810
+ description: "Configure AI Trader profile preferences through the normal preferences API before starting a bot. Requires profile:write scope.",
811
+ inputSchema: {
812
+ type: "object",
813
+ required: ["profileId"],
814
+ properties: {
815
+ profileId: { type: "number" },
816
+ symbol: { type: "string" },
817
+ timeframe: { type: "string" },
818
+ size: { type: "string" },
819
+ leverage: { type: "number" },
820
+ interval: { type: "number" }
821
+ }
822
+ }
823
+ },
824
+ {
825
+ name: "vtx_bots_stop",
826
+ description: "Stop the Server Mode AI Trader for a profile through the normal backend path. Requires bot:control scope.",
827
+ inputSchema: {
828
+ type: "object",
829
+ required: ["profileId"],
830
+ properties: { profileId: { type: "number" } }
831
+ }
832
+ },
833
+ {
834
+ name: "vtx_bots_status",
835
+ description: "Read bot/runtime status for a profile.",
836
+ inputSchema: {
837
+ type: "object",
838
+ required: ["profileId"],
839
+ properties: { profileId: { type: "number" } }
840
+ }
841
+ },
842
+ {
843
+ name: "vtx_bots_logs",
844
+ description: "Read AI Trader decision history/logs for a profile without executing trades.",
845
+ inputSchema: {
846
+ type: "object",
847
+ required: ["profileId"],
848
+ properties: { profileId: { type: "number" } }
849
+ }
850
+ },
851
+ {
852
+ name: "vtx_runtime_status",
853
+ description: "Read Client Mode runtime status for a profile. Client Mode has no VTX platform fee; Server Mode bots use normal fees.",
854
+ inputSchema: {
855
+ type: "object",
856
+ required: ["profileId"],
857
+ properties: { profileId: { type: "number" } }
858
+ }
859
+ },
860
+ {
861
+ name: "vtx_runtime_events",
862
+ description: "Read Client Mode runtime events for a profile. This is status/history visibility and does not execute local runtime work.",
863
+ inputSchema: {
864
+ type: "object",
865
+ required: ["profileId"],
866
+ properties: {
867
+ profileId: { type: "number" },
868
+ limit: { type: "number" },
869
+ eventType: { type: "string" },
870
+ clientEventType: { type: "string" }
871
+ }
872
+ }
873
+ },
874
+ {
875
+ name: "vtx_trade_market_order",
876
+ description: "Submit a market order for a profile through the normal Trade page backend path. Requires trading:execute scope and uses the same risk checks, rate limits, and idempotency behavior as a user-submitted order.",
877
+ inputSchema: {
878
+ type: "object",
879
+ required: ["profileId", "symbol", "side", "size"],
880
+ properties: {
881
+ profileId: { type: "number" },
882
+ symbol: { type: "string" },
883
+ side: { type: "string", enum: ["buy", "sell"] },
884
+ size: { type: "number" },
885
+ slippage: { type: "number" },
886
+ estimatedPrice: { type: "number" },
887
+ estimatedPriceTimestampMs: { type: "number" },
888
+ reduceOnly: { type: "boolean" },
889
+ protection: {
890
+ type: "object",
891
+ description: 'Optional fixed TP/SL like the AI page Auto SL/TP: take_profit and/or stop_loss, each { mode: "percent" | "usdc", value }. Placed as reduce-only market triggers from the average fill; not allowed with reduceOnly; no trailing stops.',
892
+ properties: {
893
+ take_profit: { type: "object", required: ["mode", "value"], properties: { mode: { type: "string", enum: ["percent", "usdc"] }, value: { type: "number" } } },
894
+ stop_loss: { type: "object", required: ["mode", "value"], properties: { mode: { type: "string", enum: ["percent", "usdc"] }, value: { type: "number" }, stop_type: { type: "string", enum: ["fixed"] } } }
895
+ }
896
+ }
897
+ }
898
+ }
899
+ },
900
+ {
901
+ name: "vtx_trade_limit_order",
902
+ description: "Submit a limit order for a profile through the normal Trade page backend path. Requires trading:execute scope and uses the same risk checks, rate limits, and idempotency behavior as a user-submitted order.",
903
+ inputSchema: {
904
+ type: "object",
905
+ required: ["profileId", "symbol", "side", "size", "limitPrice"],
906
+ properties: {
907
+ profileId: { type: "number" },
908
+ symbol: { type: "string" },
909
+ side: { type: "string", enum: ["buy", "sell"] },
910
+ size: { type: "number" },
911
+ limitPrice: { type: "number" },
912
+ reduceOnly: { type: "boolean" }
913
+ }
914
+ }
915
+ },
916
+ {
917
+ name: "vtx_trade_cancel_order",
918
+ description: "Cancel an open order for a profile through the normal Trade page backend path. Requires trading:execute scope.",
919
+ inputSchema: {
920
+ type: "object",
921
+ required: ["profileId", "symbol", "oid"],
922
+ properties: {
923
+ profileId: { type: "number" },
924
+ symbol: { type: "string" },
925
+ oid: { type: "number" }
926
+ }
927
+ }
928
+ },
929
+ {
930
+ name: "vtx_tokens_list",
931
+ description: "List agent tokens without returning raw token values.",
932
+ inputSchema: { type: "object", properties: {} }
933
+ },
934
+ {
935
+ name: "vtx_billing_status",
936
+ description: "Read VTX billing balance for the authenticated user.",
937
+ inputSchema: { type: "object", properties: {} }
938
+ },
939
+ {
940
+ name: "vtx_billing_prices",
941
+ description: "Read VTX pricing visibility, including Server Mode trader call fee and model pricing metadata.",
942
+ inputSchema: {
943
+ type: "object",
944
+ properties: { profileId: { type: "number" } }
945
+ }
946
+ },
947
+ {
948
+ name: "vtx_ai_config",
949
+ description: "Read the shared AI/runtime configuration contract used by the web app, CLI, MCP, and Client Mode runtime.",
950
+ inputSchema: {
951
+ type: "object",
952
+ properties: { profileId: { type: "number" } }
953
+ }
954
+ },
955
+ {
956
+ name: "vtx_secrets_status",
957
+ description: "Read secret readiness and masked previews for a profile. Raw stored secret values are not returned.",
958
+ inputSchema: {
959
+ type: "object",
960
+ required: ["profileId"],
961
+ properties: { profileId: { type: "number" } }
962
+ }
963
+ },
964
+ {
965
+ name: "vtx_secrets_set",
966
+ description: "Write or rotate one runtime secret for a profile. The response redacts raw secret values.",
967
+ inputSchema: {
968
+ type: "object",
969
+ required: ["profileId", "field", "value"],
970
+ properties: {
971
+ profileId: { type: "number" },
972
+ field: { type: "string" },
973
+ value: { type: "string" }
974
+ }
975
+ }
976
+ }
977
+ ];
978
+ WRITABLE_SECRET_FIELDS = /* @__PURE__ */ new Set([
979
+ "hyperliquid_signing_key",
980
+ "local_ai_api_key",
981
+ "openai_compatible_api_key",
982
+ "openai_api_key",
983
+ "anthropic_api_key",
984
+ "gemini_api_key",
985
+ "grok_api_key",
986
+ "groq_api_key",
987
+ "openrouter_api_key",
988
+ "venice_api_key",
989
+ "deepseek_api_key",
990
+ "mistral_api_key",
991
+ "together_api_key",
992
+ "fireworks_api_key",
993
+ "byteplus_api_key",
994
+ "cerebras_api_key",
995
+ "moonshot_api_key",
996
+ "alibaba_cloud_model_studio_api_key",
997
+ "lightning_api_key"
998
+ ]);
999
+ }
1000
+ });
1001
+
1002
+ // lib/agent-mcp/stdio.ts
1003
+ var stdio_exports = {};
1004
+ __export(stdio_exports, {
1005
+ encodeMcpFrame: () => encodeMcpFrame,
1006
+ handleMcpJsonRpcRequest: () => handleMcpJsonRpcRequest,
1007
+ runVtxMcpStdioServer: () => runVtxMcpStdioServer
1008
+ });
1009
+ function jsonRpcResult(id, result) {
1010
+ return { jsonrpc: "2.0", id, result };
1011
+ }
1012
+ function jsonRpcError(id, code, message, data) {
1013
+ return { jsonrpc: "2.0", id, error: { code, message, data } };
1014
+ }
1015
+ function requestId(request) {
1016
+ return request.id === void 0 ? null : request.id;
1017
+ }
1018
+ function isNotification(request) {
1019
+ return request.id === void 0;
1020
+ }
1021
+ function stringifyToolResult(result) {
1022
+ if (typeof result === "string") {
1023
+ return result;
1024
+ }
1025
+ return JSON.stringify(result, null, 2);
1026
+ }
1027
+ async function handleMcpJsonRpcRequest(request, clientFactory = createDefaultMcpClient) {
1028
+ const id = requestId(request);
1029
+ const method = String(request.method || "").trim();
1030
+ if (!method) {
1031
+ return jsonRpcError(id, -32600, "Invalid JSON-RPC request.");
1032
+ }
1033
+ if (method === "initialize") {
1034
+ return jsonRpcResult(id, {
1035
+ protocolVersion: MCP_PROTOCOL_VERSION,
1036
+ capabilities: {
1037
+ tools: {}
1038
+ },
1039
+ serverInfo: {
1040
+ name: "vtx-macro",
1041
+ version: agent_cli_release_default.package_version
1042
+ }
1043
+ });
1044
+ }
1045
+ if (method === "notifications/initialized") {
1046
+ return null;
1047
+ }
1048
+ if (method === "ping") {
1049
+ return jsonRpcResult(id, {});
1014
1050
  }
1015
1051
  if (method === "tools/list") {
1016
1052
  return jsonRpcResult(id, { tools: VTX_MCP_TOOLS });
@@ -1138,6 +1174,2416 @@ async function runVtxMcpStdioServer(input = process.stdin, output = process.stdo
1138
1174
  }
1139
1175
  }
1140
1176
  }
1177
+ var MCP_PROTOCOL_VERSION;
1178
+ var init_stdio = __esm({
1179
+ "lib/agent-mcp/stdio.ts"() {
1180
+ "use strict";
1181
+ init_define_VTX_EXO_POLICY();
1182
+ init_define_VTX_GROK_POLICY();
1183
+ init_define_VTX_PI_MODEL_POLICY();
1184
+ init_agent_cli_release();
1185
+ init_server();
1186
+ MCP_PROTOCOL_VERSION = "2024-11-05";
1187
+ }
1188
+ });
1141
1189
 
1142
1190
  // bin/vtx-mcp.ts
1143
- await runVtxMcpStdioServer();
1191
+ init_define_VTX_EXO_POLICY();
1192
+ init_define_VTX_GROK_POLICY();
1193
+ init_define_VTX_PI_MODEL_POLICY();
1194
+
1195
+ // lib/cli-release/updater.ts
1196
+ init_define_VTX_EXO_POLICY();
1197
+ init_define_VTX_GROK_POLICY();
1198
+ init_define_VTX_PI_MODEL_POLICY();
1199
+ init_agent_cli_release();
1200
+ import { spawn as spawn2 } from "node:child_process";
1201
+ import { createHash, randomUUID } from "node:crypto";
1202
+ import { access, appendFile, chmod, lstat as lstat2, mkdir as mkdir3, readFile as readFile3, readdir as readdir3, realpath as realpath2, rename as rename2, rm as rm3 } from "node:fs/promises";
1203
+ import { constants as osConstants, homedir as homedir3 } from "node:os";
1204
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, relative as relative2, resolve as resolve2, win32 as windowsPath2 } from "node:path";
1205
+
1206
+ // lib/runtime-policy.generated.json
1207
+ var runtime_policy_generated_default = {
1208
+ ui: {
1209
+ performance_defaults: {
1210
+ default_timeframe: "30d"
1211
+ },
1212
+ execution_metadata_retry: {
1213
+ max_attempts: 3,
1214
+ base_delay_milliseconds: 2e3,
1215
+ max_delay_milliseconds: 3e4
1216
+ },
1217
+ auth_bootstrap_retry: {
1218
+ max_attempts: 3,
1219
+ jitter_milliseconds: 750,
1220
+ base_delay_milliseconds: 500,
1221
+ min_delay_milliseconds: 250,
1222
+ max_delay_milliseconds: 1e4
1223
+ },
1224
+ trade_refresh_policy: {
1225
+ server_config_stream_timeout_ms: 1e4,
1226
+ config_retry_interval_ms: 5e3,
1227
+ copy_trading_status_poll_ms: 5e3,
1228
+ post_execution_refresh_delay_ms: 3e3,
1229
+ account_read_backoff_min_ms: 2e3,
1230
+ account_read_backoff_max_ms: 6e4
1231
+ },
1232
+ deployment_reload_observability: {
1233
+ bootstrap_max_error_events_per_page: 4,
1234
+ bootstrap_max_profiles: 10
1235
+ }
1236
+ },
1237
+ ai_dashboard: {
1238
+ market_news_bounds: {
1239
+ max_items_min: 1,
1240
+ max_items_max: 100,
1241
+ lookback_hours_min: 1,
1242
+ lookback_hours_max: 168,
1243
+ max_items_steps: [
1244
+ 1,
1245
+ 2,
1246
+ 3,
1247
+ 4,
1248
+ 5,
1249
+ 6,
1250
+ 7,
1251
+ 8,
1252
+ 9,
1253
+ 10,
1254
+ 15,
1255
+ 20,
1256
+ 25,
1257
+ 30,
1258
+ 40,
1259
+ 50,
1260
+ 60,
1261
+ 75,
1262
+ 90,
1263
+ 100
1264
+ ],
1265
+ lookback_hours_steps: [
1266
+ 1,
1267
+ 2,
1268
+ 4,
1269
+ 8,
1270
+ 12,
1271
+ 24,
1272
+ 48,
1273
+ 72,
1274
+ 168
1275
+ ]
1276
+ }
1277
+ },
1278
+ trading: {
1279
+ engine: {
1280
+ client_runtime: {
1281
+ reconcile: {
1282
+ tick_interval_seconds: 30,
1283
+ wake_jitter_max_ms: 1e3,
1284
+ network_backoff_max_seconds: 300,
1285
+ steal_ack_timeout_ms: 5e3,
1286
+ explicit_claim_max_waits: 3,
1287
+ local_ai_preflight_timeout_ms: 3e3
1288
+ },
1289
+ observability: {
1290
+ deployment_reload_stall_warning_ms: 15e3
1291
+ },
1292
+ protective_actions: {
1293
+ adoption_request_timeout_milliseconds: 2e4,
1294
+ idle_recheck_seconds: 60,
1295
+ attention_refresh_seconds: 60,
1296
+ evidence_max_items: 32,
1297
+ evidence_shared_read_seconds: 10
1298
+ },
1299
+ history_evidence: {
1300
+ device_rounds_per_beat: 6,
1301
+ device_retry_seconds: 60
1302
+ },
1303
+ panel_reads: {
1304
+ device_open_orders_reuse_seconds: 2
1305
+ },
1306
+ account_stream: {
1307
+ fill_forward_interval_ms: 1e3,
1308
+ fill_forward_max_pending: 500
1309
+ },
1310
+ hyperliquid_snapshot: {
1311
+ max_fills_per_execution: 20,
1312
+ max_fill_payload_bytes: 32768
1313
+ }
1314
+ },
1315
+ server_runtime: {
1316
+ prompt_rescue: {
1317
+ request_ttl_seconds: 20,
1318
+ snapshot_max_age_seconds: 5,
1319
+ snapshot_component_max_skew_seconds: 2,
1320
+ client_claim_http_timeout_milliseconds: 5e3,
1321
+ client_claim_retry_delay_milliseconds: 1e3,
1322
+ client_failure_report_timeout_milliseconds: 2e3,
1323
+ client_presence_heartbeat_interval_milliseconds: 3e4,
1324
+ client_presence_http_timeout_milliseconds: 5e3
1325
+ }
1326
+ }
1327
+ },
1328
+ history_reads: {
1329
+ max_requests_per_read: 64,
1330
+ history_start_ms: 16409952e5,
1331
+ device_read_deadline_seconds: 180,
1332
+ device_recent_window_hours: 24
1333
+ }
1334
+ },
1335
+ agent_cli: {
1336
+ update_check_interval_ms: 144e5,
1337
+ update_check_timeout_ms: 5e3,
1338
+ stage_timeout_ms: 6e5,
1339
+ startup_ready_timeout_ms: 12e4
1340
+ },
1341
+ leaderboard: {
1342
+ device_portfolio: {
1343
+ device_retry_seconds: 300
1344
+ }
1345
+ },
1346
+ external_inference: {
1347
+ service: {
1348
+ update_check_interval_ms: 3e5,
1349
+ update_registry_timeout_ms: 15e3,
1350
+ bootstrap_readiness_timeout_ms: 6e5,
1351
+ bootstrap_stop_timeout_ms: 1e4,
1352
+ startup_max_attempts: 40,
1353
+ startup_poll_interval_ms: 250,
1354
+ restart_stable_uptime_ms: 6e4,
1355
+ restart_backoff_base_ms: 1e3,
1356
+ restart_backoff_max_ms: 3e4,
1357
+ restart_exponent_max: 5
1358
+ },
1359
+ claim_diagnostics: {
1360
+ event_loop_sample_interval_ms: 100
1361
+ }
1362
+ },
1363
+ desktop: {
1364
+ runtime_watchdog: {
1365
+ ping_interval_milliseconds: 15e3,
1366
+ telemetry_interval_milliseconds: 3e5
1367
+ },
1368
+ updates: {
1369
+ auto_check_interval_seconds: 21600
1370
+ }
1371
+ },
1372
+ screener: {
1373
+ resource_scheduler: {
1374
+ client_lock_ttl_milliseconds: 12e4,
1375
+ client_poll_interval_milliseconds: 250
1376
+ }
1377
+ },
1378
+ deployment: {
1379
+ cutover_buffer: {
1380
+ target_timeout_ms: 3e3,
1381
+ retired_timeout_ms: 45e3,
1382
+ max_body_bytes: 33554432,
1383
+ max_inflight: 16,
1384
+ max_queue: 64
1385
+ }
1386
+ },
1387
+ exchange: {
1388
+ hyperliquid: {
1389
+ client_public_read_budget: {
1390
+ max_weight: 1200,
1391
+ window_seconds: 60,
1392
+ ui_max_weight_percent: 75,
1393
+ background_max_weight_percent: 50,
1394
+ cooldown_seconds: 60,
1395
+ metadata_ttl_seconds: 300,
1396
+ user_fees_ttl_seconds: 300,
1397
+ account_mode_ttl_seconds: 30,
1398
+ default_request_weight: 20,
1399
+ cache_max_entries: 512
1400
+ },
1401
+ read_budget: {
1402
+ weights: {
1403
+ exchange: 1,
1404
+ exchangeStatus: 2,
1405
+ clearinghouseState: 2,
1406
+ spotClearinghouseState: 2,
1407
+ l2Book: 2,
1408
+ allMids: 2,
1409
+ userFills: 120,
1410
+ userFillsByTime: 120,
1411
+ historicalOrders: 120,
1412
+ twapHistory: 120,
1413
+ userTwapSliceFills: 120,
1414
+ userFunding: 45,
1415
+ userNonFundingLedgerUpdates: 45,
1416
+ candleSnapshot: 104,
1417
+ userRole: 60,
1418
+ userAbstraction: 20,
1419
+ userDexAbstraction: 20,
1420
+ activeAssetData: 20,
1421
+ openOrders: 20,
1422
+ orderStatus: 2,
1423
+ portfolio: 20,
1424
+ userFees: 20,
1425
+ metaAndAssetCtxs: 20,
1426
+ meta: 20,
1427
+ perpDexs: 20,
1428
+ spotMeta: 20,
1429
+ approvedAgents: 20,
1430
+ authorizedAgents: 20,
1431
+ agents: 20,
1432
+ apiWallets: 20
1433
+ },
1434
+ exchange_action_batch_size: 40
1435
+ },
1436
+ public_recovery: {
1437
+ fallback_initial_seconds: 2,
1438
+ fallback_max_seconds: 60,
1439
+ permit_lease_seconds: 60,
1440
+ recovery_initial_concurrency: 1,
1441
+ recovery_max_concurrency: 8,
1442
+ recovery_successes_per_step: 4,
1443
+ recovery_min_spacing_ms: 100,
1444
+ queue_poll_ms: 50,
1445
+ recovery_queue_wait_seconds: 10,
1446
+ recovery_quiet_seconds: 30
1447
+ },
1448
+ http: {
1449
+ exchange: {
1450
+ request_timeout_seconds: 10,
1451
+ pool_timeout_seconds: 10
1452
+ },
1453
+ public_critical: {
1454
+ request_timeout_seconds: 10,
1455
+ pool_timeout_seconds: 5
1456
+ }
1457
+ },
1458
+ public_request_priority: {
1459
+ trading_reduce: 1,
1460
+ trading_reconcile: 2,
1461
+ trading_execute: 3,
1462
+ trading_analysis: 4,
1463
+ ui: 5,
1464
+ background: 6
1465
+ },
1466
+ account_stream_backoff: {
1467
+ initial_seconds: 1,
1468
+ max_seconds: 15,
1469
+ multiplier: 2,
1470
+ jitter_seconds: 2,
1471
+ reset_after_seconds: 60
1472
+ },
1473
+ websocket: {
1474
+ ping_interval_seconds: 20,
1475
+ account_open_timeout_seconds: 10
1476
+ }
1477
+ },
1478
+ cache: {
1479
+ order_history_ttl_seconds: 30,
1480
+ twap_history_ttl_seconds: 30,
1481
+ twap_fills_ttl_seconds: 30,
1482
+ funding_history_ttl_seconds: 300
1483
+ }
1484
+ },
1485
+ data_sync: {
1486
+ trade_chain_history: {
1487
+ range_page_limit: 500
1488
+ }
1489
+ }
1490
+ };
1491
+
1492
+ // lib/cli-release/updater.ts
1493
+ init_config();
1494
+
1495
+ // lib/inference-host/config.ts
1496
+ init_define_VTX_EXO_POLICY();
1497
+ init_define_VTX_GROK_POLICY();
1498
+ init_define_VTX_PI_MODEL_POLICY();
1499
+ import { randomBytes } from "node:crypto";
1500
+ import { spawn } from "node:child_process";
1501
+ import { constants } from "node:fs";
1502
+ import { lstat, mkdir as mkdir2, open, readdir as readdir2, readFile as readFile2, realpath, rename, rm as rm2 } from "node:fs/promises";
1503
+ import { homedir as homedir2 } from "node:os";
1504
+ import { basename, dirname as dirname2, isAbsolute, join as join2, relative, resolve, win32 as windowsPath } from "node:path";
1505
+
1506
+ // node_modules/smol-toml/dist/index.js
1507
+ init_define_VTX_EXO_POLICY();
1508
+ init_define_VTX_GROK_POLICY();
1509
+ init_define_VTX_PI_MODEL_POLICY();
1510
+
1511
+ // node_modules/smol-toml/dist/parse.js
1512
+ init_define_VTX_EXO_POLICY();
1513
+ init_define_VTX_GROK_POLICY();
1514
+ init_define_VTX_PI_MODEL_POLICY();
1515
+
1516
+ // node_modules/smol-toml/dist/struct.js
1517
+ init_define_VTX_EXO_POLICY();
1518
+ init_define_VTX_GROK_POLICY();
1519
+ init_define_VTX_PI_MODEL_POLICY();
1520
+
1521
+ // node_modules/smol-toml/dist/primitive.js
1522
+ init_define_VTX_EXO_POLICY();
1523
+ init_define_VTX_GROK_POLICY();
1524
+ init_define_VTX_PI_MODEL_POLICY();
1525
+
1526
+ // node_modules/smol-toml/dist/date.js
1527
+ init_define_VTX_EXO_POLICY();
1528
+ init_define_VTX_GROK_POLICY();
1529
+ init_define_VTX_PI_MODEL_POLICY();
1530
+
1531
+ // node_modules/smol-toml/dist/error.js
1532
+ init_define_VTX_EXO_POLICY();
1533
+ init_define_VTX_GROK_POLICY();
1534
+ init_define_VTX_PI_MODEL_POLICY();
1535
+
1536
+ // node_modules/smol-toml/dist/util.js
1537
+ init_define_VTX_EXO_POLICY();
1538
+ init_define_VTX_GROK_POLICY();
1539
+ init_define_VTX_PI_MODEL_POLICY();
1540
+
1541
+ // node_modules/smol-toml/dist/extract.js
1542
+ init_define_VTX_EXO_POLICY();
1543
+ init_define_VTX_GROK_POLICY();
1544
+ init_define_VTX_PI_MODEL_POLICY();
1545
+
1546
+ // node_modules/smol-toml/dist/stringify.js
1547
+ init_define_VTX_EXO_POLICY();
1548
+ init_define_VTX_GROK_POLICY();
1549
+ init_define_VTX_PI_MODEL_POLICY();
1550
+
1551
+ // lib/inference-host/mcp-transport-config.ts
1552
+ init_define_VTX_EXO_POLICY();
1553
+ init_define_VTX_GROK_POLICY();
1554
+ init_define_VTX_PI_MODEL_POLICY();
1555
+
1556
+ // lib/inference-host/config.ts
1557
+ var InferenceHostProcessLockBusyError = class extends Error {
1558
+ constructor() {
1559
+ super("Another inference host process already owns this local host.");
1560
+ this.name = "InferenceHostProcessLockBusyError";
1561
+ }
1562
+ };
1563
+ var MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
1564
+ var WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
1565
+ $ProgressPreference='SilentlyContinue'
1566
+ while(($line=[Console]::In.ReadLine()) -ne $null) {
1567
+ $requestId=$null
1568
+ $failureStage='request_validation'
1569
+ try {
1570
+ if($line.Length -lt 2 -or $line.Length -gt 65536) { throw 'invalid request size' }
1571
+ $request=$line | ConvertFrom-Json
1572
+ $names=@($request.PSObject.Properties.Name | Sort-Object)
1573
+ if(($names -join ',') -ne 'id,kind,mode,path_b64') { throw 'invalid request shape' }
1574
+ if($request.id -isnot [long] -and $request.id -isnot [int]) { throw 'invalid request id' }
1575
+ $requestId=[long]$request.id
1576
+ if($requestId -lt 1) { throw 'invalid request id' }
1577
+ $kind=[string]$request.kind
1578
+ $mode=[string]$request.mode
1579
+ if($kind -ne 'directory' -and $kind -ne 'file') { throw 'invalid path kind' }
1580
+ if($mode -ne 'harden' -and $mode -ne 'verify' -and $mode -ne 'verify-owner') { throw 'invalid ACL mode' }
1581
+ $path=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String([string]$request.path_b64))
1582
+ if([string]::IsNullOrWhiteSpace($path) -or $path.Length -gt 32767 -or $path.Contains([char]0)) { throw 'invalid path' }
1583
+ $failureStage='path_lookup'
1584
+ if(-not (Test-Path -LiteralPath $path)) {
1585
+ [Console]::Out.WriteLine((@{id=$requestId;status='missing'} | ConvertTo-Json -Compress))
1586
+ continue
1587
+ }
1588
+ $failureStage='principal_resolution'
1589
+ $currentIdentity=[Security.Principal.WindowsIdentity]::GetCurrent()
1590
+ $current=$currentIdentity.User
1591
+ $system=New-Object Security.Principal.SecurityIdentifier('S-1-5-18')
1592
+ $admins=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')
1593
+ $currentIsAdmin=(New-Object Security.Principal.WindowsPrincipal($currentIdentity)).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
1594
+ $allowed=@($current.Value,$system.Value,$admins.Value)
1595
+ if($mode -eq 'harden') {
1596
+ $failureStage='acl_read'
1597
+ $acl=Get-Acl -LiteralPath $path
1598
+ $originalAccess=$acl.GetSecurityDescriptorSddlForm([Security.AccessControl.AccessControlSections]::Access)
1599
+ $failureStage='owner_validation'
1600
+ $existingOwner=(New-Object Security.Principal.NTAccount($acl.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
1601
+ if($existingOwner -ne $current.Value -and ($existingOwner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
1602
+ $failureStage='acl_mutation'
1603
+ $acl.SetAccessRuleProtection($true,$false)
1604
+ foreach($existingRule in @($acl.Access)) { [void]$acl.RemoveAccessRuleSpecific($existingRule) }
1605
+ $inherit=if($kind -eq 'directory') {
1606
+ [Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
1607
+ } else { [Security.AccessControl.InheritanceFlags]::None }
1608
+ foreach($sid in @($current,$system,$admins)) {
1609
+ $rule=New-Object Security.AccessControl.FileSystemAccessRule(
1610
+ $sid,
1611
+ [Security.AccessControl.FileSystemRights]::FullControl,
1612
+ $inherit,
1613
+ [Security.AccessControl.PropagationFlags]::None,
1614
+ [Security.AccessControl.AccessControlType]::Allow
1615
+ )
1616
+ [void]$acl.AddAccessRule($rule)
1617
+ }
1618
+ $failureStage='acl_write'
1619
+ if($originalAccess -ne $acl.GetSecurityDescriptorSddlForm([Security.AccessControl.AccessControlSections]::Access)) {
1620
+ (Get-Item -LiteralPath $path).SetAccessControl($acl)
1621
+ }
1622
+ }
1623
+ $failureStage='acl_readback'
1624
+ $actual=Get-Acl -LiteralPath $path
1625
+ $failureStage='acl_verification'
1626
+ $owner=(New-Object Security.Principal.NTAccount($actual.Owner)).Translate([Security.Principal.SecurityIdentifier]).Value
1627
+ if($owner -ne $current.Value -and ($owner -ne $admins.Value -or -not $currentIsAdmin)) { throw 'private ACL owner is invalid' }
1628
+ if($mode -ne 'verify-owner') {
1629
+ if(-not $actual.AreAccessRulesProtected) { throw 'private ACL inheritance is invalid' }
1630
+ $userFull=$false
1631
+ foreach($rule in $actual.Access) {
1632
+ $sid=$rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
1633
+ if($rule.IsInherited -or $allowed -notcontains $sid -or $rule.AccessControlType -ne [Security.AccessControl.AccessControlType]::Allow) {
1634
+ throw 'private ACL contains an unauthorized access rule'
1635
+ }
1636
+ if($sid -eq $current.Value -and (($rule.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -eq [Security.AccessControl.FileSystemRights]::FullControl)) { $userFull=$true }
1637
+ }
1638
+ if(-not $userFull) { throw 'private ACL does not grant the current user full control' }
1639
+ }
1640
+ [Console]::Out.WriteLine((@{id=$requestId;status='ok'} | ConvertTo-Json -Compress))
1641
+ } catch {
1642
+ [Console]::Out.WriteLine((@{id=$requestId;status='error';error_code=$failureStage} | ConvertTo-Json -Compress))
1643
+ }
1644
+ }`;
1645
+ var WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS = 15e3;
1646
+ var WINDOWS_PRIVATE_ACL_BROKER_STARTUP_TIMEOUT_MS = 9e4;
1647
+ var SMALL_IDENTITY_COMMAND_TIMEOUT_MS = 5e3;
1648
+ var windowsPowerShellEnvironment = (extra = {}) => {
1649
+ const sanitizedExtra = { ...extra };
1650
+ delete sanitizedExtra.PSModulePath;
1651
+ const windowsRoot = process.env.SystemRoot ?? process.env.WINDIR;
1652
+ return {
1653
+ ...sanitizedExtra,
1654
+ NODE_ENV: process.env.NODE_ENV,
1655
+ SystemRoot: process.env.SystemRoot,
1656
+ WINDIR: process.env.WINDIR,
1657
+ ComSpec: process.env.ComSpec,
1658
+ PATHEXT: process.env.PATHEXT,
1659
+ PSModulePath: windowsRoot ? windowsPath.join(windowsRoot, "System32", "WindowsPowerShell", "v1.0", "Modules") : void 0,
1660
+ TEMP: process.env.TEMP,
1661
+ TMP: process.env.TMP
1662
+ };
1663
+ };
1664
+ var windowsPrivateAclBrokerInvocation = () => ({
1665
+ args: [
1666
+ "-NoLogo",
1667
+ "-NoProfile",
1668
+ "-NonInteractive",
1669
+ "-EncodedCommand",
1670
+ Buffer.from(WINDOWS_PRIVATE_ACL_BROKER_SCRIPT, "utf16le").toString("base64")
1671
+ ],
1672
+ env: windowsPowerShellEnvironment()
1673
+ });
1674
+ var WindowsPrivateAclBroker = class {
1675
+ constructor(dependencies = {}) {
1676
+ this.child = null;
1677
+ this.requestSequence = 0;
1678
+ this.queue = Promise.resolve();
1679
+ this.pending = null;
1680
+ this.outputBuffer = "";
1681
+ this.idleTimer = null;
1682
+ this.run = async (path, kind, mode) => {
1683
+ const operation = this.queue.then(
1684
+ () => this.dispatch(path, kind, mode),
1685
+ () => this.dispatch(path, kind, mode)
1686
+ );
1687
+ this.queue = operation.then(() => void 0, () => void 0);
1688
+ await operation;
1689
+ };
1690
+ this.requestTimeoutMs = dependencies.requestTimeoutMs ?? 15e3;
1691
+ this.startupRequestTimeoutMs = dependencies.startupRequestTimeoutMs ?? WINDOWS_PRIVATE_ACL_BROKER_STARTUP_TIMEOUT_MS;
1692
+ this.idleShutdownMs = dependencies.idleShutdownMs ?? 3e4;
1693
+ this.spawnProcess = dependencies.spawnProcess ?? (() => {
1694
+ const invocation = windowsPrivateAclBrokerInvocation();
1695
+ return spawn("powershell.exe", [...invocation.args], {
1696
+ env: invocation.env,
1697
+ windowsHide: true,
1698
+ stdio: ["pipe", "pipe", "pipe"]
1699
+ });
1700
+ });
1701
+ }
1702
+ scheduleIdleShutdown() {
1703
+ if (this.idleTimer) clearTimeout(this.idleTimer);
1704
+ this.idleTimer = setTimeout(() => {
1705
+ this.idleTimer = null;
1706
+ const child = this.child;
1707
+ if (!child || this.pending) return;
1708
+ this.child = null;
1709
+ this.outputBuffer = "";
1710
+ child.stdin.end();
1711
+ const force = setTimeout(() => child.kill("SIGKILL"), 1e3);
1712
+ force.unref();
1713
+ child.once("exit", () => clearTimeout(force));
1714
+ }, this.idleShutdownMs);
1715
+ this.idleTimer.unref();
1716
+ }
1717
+ failChild(child, error) {
1718
+ if (this.child !== child) return;
1719
+ this.child = null;
1720
+ this.outputBuffer = "";
1721
+ if (this.idleTimer) {
1722
+ clearTimeout(this.idleTimer);
1723
+ this.idleTimer = null;
1724
+ }
1725
+ const pending = this.pending;
1726
+ this.pending = null;
1727
+ if (pending) {
1728
+ clearTimeout(pending.timer);
1729
+ pending.reject(error);
1730
+ }
1731
+ child.kill("SIGKILL");
1732
+ }
1733
+ acceptLine(child, line) {
1734
+ const pending = this.pending;
1735
+ if (!pending || !line || line.length > 4096) {
1736
+ this.failChild(child, new Error("Windows private ACL broker returned malformed output."));
1737
+ return;
1738
+ }
1739
+ let response;
1740
+ try {
1741
+ const parsed = JSON.parse(line);
1742
+ const status = String(parsed.status);
1743
+ const expectedKeys = status === "error" ? "error_code\0id\0status" : "id\0status";
1744
+ if (Object.keys(parsed).sort().join("\0") !== expectedKeys || parsed.id !== pending.id || !["ok", "missing", "error"].includes(status) || status === "error" && ![
1745
+ "request_validation",
1746
+ "path_lookup",
1747
+ "principal_resolution",
1748
+ "acl_read",
1749
+ "owner_validation",
1750
+ "acl_mutation",
1751
+ "acl_write",
1752
+ "acl_readback",
1753
+ "acl_verification"
1754
+ ].includes(String(parsed.error_code))) throw new Error("invalid response");
1755
+ response = parsed;
1756
+ } catch {
1757
+ this.failChild(child, new Error("Windows private ACL broker returned malformed output."));
1758
+ return;
1759
+ }
1760
+ this.pending = null;
1761
+ clearTimeout(pending.timer);
1762
+ if (response.status === "ok") {
1763
+ pending.resolve();
1764
+ } else if (response.status === "missing") {
1765
+ const error = new Error("Windows private path no longer exists.");
1766
+ error.code = "ENOENT";
1767
+ pending.reject(error);
1768
+ } else {
1769
+ pending.reject(new Error(
1770
+ `Windows private ACL ${pending.mode} failed (${response.error_code}).`
1771
+ ));
1772
+ }
1773
+ this.scheduleIdleShutdown();
1774
+ }
1775
+ ensureChild() {
1776
+ if (this.child && this.child.exitCode === null && !this.child.killed) {
1777
+ return { child: this.child, startedNow: false };
1778
+ }
1779
+ const child = this.spawnProcess();
1780
+ this.child = child;
1781
+ this.outputBuffer = "";
1782
+ child.stdout.setEncoding("utf8");
1783
+ child.stdout.on("data", (chunk) => {
1784
+ if (this.child !== child) return;
1785
+ this.outputBuffer += chunk;
1786
+ if (this.outputBuffer.length > 65536) {
1787
+ this.failChild(child, new Error("Windows private ACL broker exceeded its output limit."));
1788
+ return;
1789
+ }
1790
+ while (this.child === child) {
1791
+ const newline = this.outputBuffer.indexOf("\n");
1792
+ if (newline < 0) break;
1793
+ const line = this.outputBuffer.slice(0, newline).trim();
1794
+ this.outputBuffer = this.outputBuffer.slice(newline + 1);
1795
+ this.acceptLine(child, line);
1796
+ }
1797
+ });
1798
+ child.stderr.on("data", () => void 0);
1799
+ child.once("error", () => {
1800
+ this.failChild(child, new Error("Windows private ACL broker failed to start."));
1801
+ });
1802
+ child.once("exit", () => {
1803
+ this.failChild(child, new Error("Windows private ACL broker exited before confirmation."));
1804
+ });
1805
+ child.unref();
1806
+ for (const stream of [child.stdin, child.stdout, child.stderr]) {
1807
+ stream.unref?.();
1808
+ }
1809
+ return { child, startedNow: true };
1810
+ }
1811
+ async dispatch(path, kind, mode) {
1812
+ if (this.idleTimer) {
1813
+ clearTimeout(this.idleTimer);
1814
+ this.idleTimer = null;
1815
+ }
1816
+ const id = ++this.requestSequence;
1817
+ const request = `${JSON.stringify({
1818
+ id,
1819
+ kind,
1820
+ mode,
1821
+ path_b64: Buffer.from(path, "utf8").toString("base64")
1822
+ })}
1823
+ `;
1824
+ if (Buffer.byteLength(request, "utf8") > 65536) {
1825
+ throw new Error("Windows private ACL broker request exceeds its input limit.");
1826
+ }
1827
+ const { child, startedNow } = this.ensureChild();
1828
+ const timeoutMs = startedNow ? this.startupRequestTimeoutMs : this.requestTimeoutMs;
1829
+ await new Promise((resolvePromise, reject) => {
1830
+ const timer = setTimeout(() => {
1831
+ this.failChild(child, new Error("Windows private ACL broker request timed out."));
1832
+ }, timeoutMs);
1833
+ this.pending = { id, mode, resolve: resolvePromise, reject, timer };
1834
+ child.stdin.write(request, "utf8", (error) => {
1835
+ if (error) {
1836
+ this.failChild(child, new Error("Windows private ACL broker request failed."));
1837
+ }
1838
+ });
1839
+ });
1840
+ }
1841
+ close() {
1842
+ if (this.idleTimer) clearTimeout(this.idleTimer);
1843
+ this.idleTimer = null;
1844
+ const child = this.child;
1845
+ this.child = null;
1846
+ this.outputBuffer = "";
1847
+ if (child) child.stdin.end();
1848
+ }
1849
+ };
1850
+ var defaultWindowsPrivateAclBroker = new WindowsPrivateAclBroker();
1851
+ var runWindowsPrivateAcl = async (path, kind, mode) => await defaultWindowsPrivateAclBroker.run(path, kind, mode);
1852
+ var windowsAclCache = /* @__PURE__ */ new Map();
1853
+ var windowsAclIdentity = async (path) => {
1854
+ const metadata = await lstat(path, { bigint: true });
1855
+ return {
1856
+ dev: metadata.dev,
1857
+ ino: metadata.ino,
1858
+ ctimeNs: metadata.ctimeNs,
1859
+ mtimeNs: metadata.mtimeNs,
1860
+ mode: metadata.mode,
1861
+ size: metadata.size
1862
+ };
1863
+ };
1864
+ var aclCacheKey = (path, kind, mode) => `${kind}:${mode}:${resolve(path)}`;
1865
+ var secureWindowsPrivatePath = async (path, kind, mode, platform = process.platform, runner = runWindowsPrivateAcl) => {
1866
+ if (platform !== "win32") return;
1867
+ const key = aclCacheKey(path, kind, mode);
1868
+ const before = await windowsAclIdentity(path);
1869
+ const cached = windowsAclCache.get(key);
1870
+ if (cached && cached.dev === before.dev && cached.ino === before.ino && cached.ctimeNs === before.ctimeNs && cached.mtimeNs === before.mtimeNs && cached.mode === before.mode && cached.size === before.size) return;
1871
+ await runner(path, kind, mode);
1872
+ windowsAclCache.set(key, await windowsAclIdentity(path));
1873
+ };
1874
+ var invalidateWindowsAclCache = (path) => {
1875
+ for (const kind of ["file", "directory"]) {
1876
+ for (const mode of ["harden", "verify", "verify-owner"]) {
1877
+ windowsAclCache.delete(aclCacheKey(path, kind, mode));
1878
+ }
1879
+ }
1880
+ };
1881
+ var rebindHardenedWindowsAclAfterRename = async (source, destination, kind, expected) => {
1882
+ const resolvedSource = resolve(source);
1883
+ const resolvedDestination = resolve(destination);
1884
+ if (resolvedSource === resolvedDestination || dirname2(resolvedSource) !== dirname2(resolvedDestination)) {
1885
+ throw new Error("Windows private ACL cache rename boundary is invalid.");
1886
+ }
1887
+ const published = await windowsAclIdentity(destination);
1888
+ if (published.dev !== expected.dev || published.ino !== expected.ino || published.mode !== expected.mode || published.mtimeNs !== expected.mtimeNs || published.size !== expected.size) {
1889
+ invalidateWindowsAclCache(source);
1890
+ invalidateWindowsAclCache(destination);
1891
+ throw new Error("Windows private file identity changed during atomic publication.");
1892
+ }
1893
+ invalidateWindowsAclCache(source);
1894
+ invalidateWindowsAclCache(destination);
1895
+ for (const mode of ["harden", "verify", "verify-owner"]) {
1896
+ windowsAclCache.set(aclCacheKey(destination, kind, mode), published);
1897
+ }
1898
+ };
1899
+ var rebindHardenedWindowsDirectoryAfterOwnedMutation = async (path, expected) => {
1900
+ const current = await windowsAclIdentity(path);
1901
+ if (current.dev !== expected.dev || current.ino !== expected.ino || current.mode !== expected.mode) {
1902
+ invalidateWindowsAclCache(path);
1903
+ throw new Error("Windows private directory identity changed during atomic publication.");
1904
+ }
1905
+ invalidateWindowsAclCache(path);
1906
+ for (const mode of ["harden", "verify", "verify-owner"]) {
1907
+ windowsAclCache.set(aclCacheKey(path, "directory", mode), current);
1908
+ }
1909
+ };
1910
+ var isDefaultWindowsPrivatePath = (path, root = resolve(homedir2(), ".vtx", "inference-host")) => {
1911
+ const child = relative(root, resolve(path));
1912
+ return child === "" || !child.startsWith("..") && !child.includes(":");
1913
+ };
1914
+ var secureExistingWindowsPath = async (path, kind, platform = process.platform, runner = runWindowsPrivateAcl, defaultRoot = resolve(homedir2(), ".vtx", "inference-host")) => {
1915
+ if (platform !== "win32") return;
1916
+ if (!isDefaultWindowsPrivatePath(path, defaultRoot)) {
1917
+ await secureWindowsPrivatePath(path, kind, "verify", platform, runner);
1918
+ return;
1919
+ }
1920
+ await secureWindowsPrivatePath(path, kind, "harden", platform, runner);
1921
+ };
1922
+ var linuxProcessIdentity = async (pid) => {
1923
+ try {
1924
+ const [bootId, statLine] = await Promise.all([
1925
+ readFile2("/proc/sys/kernel/random/boot_id", "utf8"),
1926
+ readFile2(`/proc/${pid}/stat`, "utf8")
1927
+ ]);
1928
+ const fields = statLine.slice(statLine.lastIndexOf(")") + 2).trim().split(/\s+/u);
1929
+ const startTicks = fields[19];
1930
+ return startTicks ? `linux:${bootId.trim()}:${startTicks}` : null;
1931
+ } catch {
1932
+ return null;
1933
+ }
1934
+ };
1935
+ var windowsProcessIdentityInvocation = (pid) => {
1936
+ const script = `$ErrorActionPreference='Stop';$p=[Diagnostics.Process]::GetProcessById([int]$env:VTX_PROCESS_PID);[Console]::Out.Write($p.StartTime.ToUniversalTime().ToFileTimeUtc())`;
1937
+ return {
1938
+ args: [
1939
+ "-NoLogo",
1940
+ "-NoProfile",
1941
+ "-NonInteractive",
1942
+ "-EncodedCommand",
1943
+ Buffer.from(script, "utf16le").toString("base64")
1944
+ ],
1945
+ env: windowsPowerShellEnvironment({ VTX_PROCESS_PID: String(pid) }),
1946
+ timeoutMs: WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS
1947
+ };
1948
+ };
1949
+ var windowsBootIdentityInvocation = () => {
1950
+ const script = `$ErrorActionPreference='Stop';$s=[System.Management.ManagementObjectSearcher]::new('SELECT LastBootUpTime FROM Win32_OperatingSystem');try{$c=$s.Get();$e=$c.GetEnumerator();if(-not $e.MoveNext()){throw 'boot identity unavailable'};$raw=[string]$e.Current['LastBootUpTime'];$v=[System.Management.ManagementDateTimeConverter]::ToDateTime($raw).ToUniversalTime().ToFileTimeUtc();[Console]::Out.Write($v)}finally{if($c){$c.Dispose()};$s.Dispose()}`;
1951
+ return {
1952
+ args: [
1953
+ "-NoLogo",
1954
+ "-NoProfile",
1955
+ "-NonInteractive",
1956
+ "-EncodedCommand",
1957
+ Buffer.from(script, "utf16le").toString("base64")
1958
+ ],
1959
+ env: windowsPowerShellEnvironment(),
1960
+ timeoutMs: WINDOWS_IDENTITY_COMMAND_TIMEOUT_MS
1961
+ };
1962
+ };
1963
+ var windowsProcessIdentity = async (pid) => await new Promise((resolvePromise) => {
1964
+ const invocation = windowsProcessIdentityInvocation(pid);
1965
+ const child = spawn("powershell.exe", [...invocation.args], {
1966
+ windowsHide: true,
1967
+ stdio: ["ignore", "pipe", "ignore"],
1968
+ env: invocation.env
1969
+ });
1970
+ const output = [];
1971
+ let settled = false;
1972
+ const settle = (value) => {
1973
+ if (settled) return;
1974
+ settled = true;
1975
+ clearTimeout(timer);
1976
+ resolvePromise(value);
1977
+ };
1978
+ const timer = setTimeout(() => {
1979
+ child.kill("SIGKILL");
1980
+ settle(null);
1981
+ }, invocation.timeoutMs);
1982
+ child.stdout.on("data", (chunk) => {
1983
+ if (output.reduce((total, item) => total + item.byteLength, 0) + Buffer.byteLength(chunk) > 1024) {
1984
+ child.kill("SIGKILL");
1985
+ settle(null);
1986
+ return;
1987
+ }
1988
+ output.push(Buffer.from(chunk));
1989
+ });
1990
+ child.once("error", () => settle(null));
1991
+ child.once("exit", (code) => {
1992
+ const started = Buffer.concat(output).toString("utf8").trim();
1993
+ settle(code === 0 && /^\d+$/u.test(started) ? `win32:${started}` : null);
1994
+ });
1995
+ });
1996
+ var darwinProcessIdentity = async (pid) => await new Promise((resolvePromise) => {
1997
+ const child = spawn("ps", ["-p", String(pid), "-o", "lstart="], {
1998
+ stdio: ["ignore", "pipe", "ignore"]
1999
+ });
2000
+ const output = [];
2001
+ let settled = false;
2002
+ const settle = (value) => {
2003
+ if (settled) return;
2004
+ settled = true;
2005
+ clearTimeout(timer);
2006
+ resolvePromise(value);
2007
+ };
2008
+ const timer = setTimeout(() => {
2009
+ child.kill("SIGKILL");
2010
+ settle(null);
2011
+ }, 5e3);
2012
+ child.stdout.on("data", (chunk) => output.push(Buffer.from(chunk)));
2013
+ child.once("error", () => settle(null));
2014
+ child.once("exit", (code) => {
2015
+ const started = Date.parse(Buffer.concat(output).toString("utf8").trim());
2016
+ settle(code === 0 && Number.isFinite(started) ? `darwin:${Math.floor(started / 1e3)}` : null);
2017
+ });
2018
+ });
2019
+ var runSmallIdentityCommand = async (command, args, env, timeoutMs = SMALL_IDENTITY_COMMAND_TIMEOUT_MS) => await new Promise((resolvePromise) => {
2020
+ const child = spawn(command, [...args], {
2021
+ windowsHide: true,
2022
+ stdio: ["ignore", "pipe", "ignore"],
2023
+ env
2024
+ });
2025
+ const output = [];
2026
+ let bytes = 0;
2027
+ let settled = false;
2028
+ const settle = (value) => {
2029
+ if (settled) return;
2030
+ settled = true;
2031
+ clearTimeout(timer);
2032
+ resolvePromise(value);
2033
+ };
2034
+ const timer = setTimeout(() => {
2035
+ child.kill("SIGKILL");
2036
+ settle(null);
2037
+ }, timeoutMs);
2038
+ child.stdout.on("data", (chunk) => {
2039
+ bytes += Buffer.byteLength(chunk);
2040
+ if (bytes > 4096) {
2041
+ child.kill("SIGKILL");
2042
+ settle(null);
2043
+ return;
2044
+ }
2045
+ output.push(Buffer.from(chunk));
2046
+ });
2047
+ child.once("error", () => settle(null));
2048
+ child.once("exit", (code) => settle(
2049
+ code === 0 ? Buffer.concat(output).toString("utf8").trim() || null : null
2050
+ ));
2051
+ });
2052
+ var cachedSystemBootIdentity = null;
2053
+ var readInferenceSystemBootIdentity = async (platform = process.platform) => {
2054
+ if (platform === process.platform && cachedSystemBootIdentity) {
2055
+ return await cachedSystemBootIdentity;
2056
+ }
2057
+ const read = async () => {
2058
+ let identity = null;
2059
+ if (platform === "linux") {
2060
+ try {
2061
+ identity = (await readFile2("/proc/sys/kernel/random/boot_id", "utf8")).trim();
2062
+ } catch {
2063
+ identity = null;
2064
+ }
2065
+ } else if (platform === "win32") {
2066
+ const invocation = windowsBootIdentityInvocation();
2067
+ identity = await runSmallIdentityCommand(
2068
+ "powershell.exe",
2069
+ invocation.args,
2070
+ invocation.env,
2071
+ invocation.timeoutMs
2072
+ );
2073
+ } else if (platform === "darwin") {
2074
+ identity = await runSmallIdentityCommand("sysctl", ["-n", "kern.boottime"], void 0);
2075
+ }
2076
+ if (!identity || /[\r\n\0]/u.test(identity)) {
2077
+ throw new Error("Inference host could not establish the current system boot identity.");
2078
+ }
2079
+ return `${platform}:${identity}`;
2080
+ };
2081
+ const pending = read();
2082
+ if (platform === process.platform) cachedSystemBootIdentity = pending;
2083
+ try {
2084
+ return await pending;
2085
+ } catch (error) {
2086
+ if (platform === process.platform) cachedSystemBootIdentity = null;
2087
+ throw error;
2088
+ }
2089
+ };
2090
+ var defaultProcessIdentity = async (pid) => {
2091
+ if (process.platform === "linux") return await linuxProcessIdentity(pid);
2092
+ if (process.platform === "win32") return await windowsProcessIdentity(pid);
2093
+ if (process.platform === "darwin") return await darwinProcessIdentity(pid);
2094
+ return null;
2095
+ };
2096
+ var cachedCurrentProcessIdentity = null;
2097
+ var defaultCurrentProcessIdentity = async () => {
2098
+ if (!cachedCurrentProcessIdentity) {
2099
+ cachedCurrentProcessIdentity = (async () => {
2100
+ const identity = await defaultProcessIdentity(process.pid);
2101
+ if (!identity) throw new Error("Inference host could not establish its process-start identity.");
2102
+ return identity;
2103
+ })();
2104
+ }
2105
+ const pending = cachedCurrentProcessIdentity;
2106
+ try {
2107
+ return await pending;
2108
+ } catch (error) {
2109
+ if (cachedCurrentProcessIdentity === pending) cachedCurrentProcessIdentity = null;
2110
+ throw error;
2111
+ }
2112
+ };
2113
+ var processLockObservationCache = /* @__PURE__ */ new Map();
2114
+ var inferenceProcessIdentitiesMatch = (stored, observed) => stored === observed;
2115
+ function inferenceBaseDir(env) {
2116
+ const configured = String(env.VTX_INFERENCE_HOST_HOME || "").trim();
2117
+ return configured || join2(homedir2(), ".vtx", "inference-host");
2118
+ }
2119
+ var resolveInferenceHostApiUrl = (env) => String(env.VTX_API_URL || "https://api.vtxmacro.com").replace(/\/+$/, "");
2120
+ var inferenceHostSupervisorServiceManifestPath = (env) => `${join2(inferenceBaseDir(env), "state.json")}.service.json`;
2121
+ async function ensureInferencePrivateDirectory(path, dependencies = {}) {
2122
+ const platform = dependencies.platform ?? process.platform;
2123
+ const runner = dependencies.runner ?? runWindowsPrivateAcl;
2124
+ const defaultRoot = dependencies.defaultRoot ?? resolve(homedir2(), ".vtx", "inference-host");
2125
+ let created = false;
2126
+ try {
2127
+ const before = await lstat(path);
2128
+ if (before.isSymbolicLink() || !before.isDirectory()) {
2129
+ throw new Error("Inference host state directory is unsafe.");
2130
+ }
2131
+ } catch (error) {
2132
+ if (error.code !== "ENOENT") throw error;
2133
+ await mkdir2(path, { recursive: true, mode: 448 });
2134
+ created = true;
2135
+ }
2136
+ const after = await lstat(path);
2137
+ if (after.isSymbolicLink() || !after.isDirectory()) {
2138
+ throw new Error("Inference host state directory is unsafe.");
2139
+ }
2140
+ if (platform === "win32") {
2141
+ if (created) {
2142
+ await secureWindowsPrivatePath(path, "directory", "harden", platform, runner);
2143
+ } else {
2144
+ await secureExistingWindowsPath(
2145
+ path,
2146
+ "directory",
2147
+ platform,
2148
+ runner,
2149
+ defaultRoot
2150
+ );
2151
+ }
2152
+ return;
2153
+ }
2154
+ if ((after.mode & 63) !== 0) {
2155
+ throw new Error("Inference host state directory must have mode 0700.");
2156
+ }
2157
+ if (typeof process.getuid === "function" && after.uid !== process.getuid()) {
2158
+ throw new Error("Inference host state directory must be owned by the current user.");
2159
+ }
2160
+ const canonical = await realpath(path);
2161
+ if (canonical !== resolve(path)) {
2162
+ throw new Error("Inference host state directory may not traverse symbolic links.");
2163
+ }
2164
+ }
2165
+ async function syncInferenceDirectory(path, platform = process.platform) {
2166
+ if (platform === "win32") return;
2167
+ const handle = await open(path, constants.O_RDONLY | constants.O_DIRECTORY);
2168
+ try {
2169
+ await handle.sync();
2170
+ } finally {
2171
+ await handle.close();
2172
+ }
2173
+ }
2174
+ var WINDOWS_PRIVATE_FILE_REPLACE_ERROR_CODES = /* @__PURE__ */ new Set([
2175
+ "EACCES",
2176
+ "EBUSY",
2177
+ "EEXIST",
2178
+ "ENOTEMPTY",
2179
+ "EPERM"
2180
+ ]);
2181
+ async function replaceAtomicInferencePrivateFile(temporaryPath, destinationPath, options = {}) {
2182
+ const platform = options.platform ?? process.platform;
2183
+ const renameFile = options.renameFile ?? rename;
2184
+ const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve3) => setTimeout(resolve3, milliseconds)));
2185
+ const maxAttempts = Math.max(1, options.maxAttempts ?? 40);
2186
+ const retryDelayMs = Math.max(0, options.retryDelayMs ?? 25);
2187
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
2188
+ try {
2189
+ await renameFile(temporaryPath, destinationPath);
2190
+ return;
2191
+ } catch (error) {
2192
+ const code = String(error.code ?? "");
2193
+ if (platform !== "win32" || !WINDOWS_PRIVATE_FILE_REPLACE_ERROR_CODES.has(code) || attempt === maxAttempts - 1) {
2194
+ throw error;
2195
+ }
2196
+ await sleep(retryDelayMs);
2197
+ }
2198
+ }
2199
+ }
2200
+ async function writeAtomicInferencePrivateFile(path, contents, dependencies = {}) {
2201
+ const platform = dependencies.platform ?? process.platform;
2202
+ const runner = dependencies.runner ?? runWindowsPrivateAcl;
2203
+ const defaultRoot = dependencies.defaultRoot ?? resolve(homedir2(), ".vtx", "inference-host");
2204
+ const directory = dirname2(path);
2205
+ await ensureInferencePrivateDirectory(directory, dependencies);
2206
+ const hardenedDirectoryIdentity = platform === "win32" && isDefaultWindowsPrivatePath(directory, defaultRoot) ? windowsAclCache.get(aclCacheKey(directory, "directory", "harden")) : void 0;
2207
+ if (platform === "win32" && isDefaultWindowsPrivatePath(directory, defaultRoot) && !hardenedDirectoryIdentity) {
2208
+ throw new Error("Windows private directory ACL hardening was not durably verified.");
2209
+ }
2210
+ const temporary = join2(
2211
+ directory,
2212
+ `.${process.pid}.${randomBytes(12).toString("hex")}.tmp`
2213
+ );
2214
+ const handle = await open(
2215
+ temporary,
2216
+ constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW,
2217
+ 384
2218
+ );
2219
+ try {
2220
+ await handle.writeFile(contents, "utf8");
2221
+ await handle.sync();
2222
+ } catch (error) {
2223
+ await handle.close().catch(() => void 0);
2224
+ await rm2(temporary, { force: true }).catch(() => void 0);
2225
+ throw error;
2226
+ }
2227
+ await handle.close();
2228
+ await secureWindowsPrivatePath(temporary, "file", "harden", platform, runner);
2229
+ const hardenedIdentity = platform === "win32" ? windowsAclCache.get(aclCacheKey(temporary, "file", "harden")) : void 0;
2230
+ if (platform === "win32" && !hardenedIdentity) {
2231
+ await rm2(temporary, { force: true }).catch(() => void 0);
2232
+ throw new Error("Windows private ACL hardening was not durably verified.");
2233
+ }
2234
+ try {
2235
+ invalidateWindowsAclCache(path);
2236
+ await replaceAtomicInferencePrivateFile(temporary, path, {
2237
+ platform,
2238
+ renameFile: dependencies.renameFile,
2239
+ sleep: dependencies.sleep,
2240
+ maxAttempts: dependencies.replaceMaxAttempts,
2241
+ retryDelayMs: dependencies.replaceRetryDelayMs
2242
+ });
2243
+ if (platform === "win32" && hardenedIdentity) {
2244
+ await rebindHardenedWindowsAclAfterRename(
2245
+ temporary,
2246
+ path,
2247
+ "file",
2248
+ hardenedIdentity
2249
+ );
2250
+ } else {
2251
+ invalidateWindowsAclCache(temporary);
2252
+ }
2253
+ await syncInferenceDirectory(directory, platform);
2254
+ if (platform === "win32" && hardenedDirectoryIdentity) {
2255
+ await rebindHardenedWindowsDirectoryAfterOwnedMutation(
2256
+ directory,
2257
+ hardenedDirectoryIdentity
2258
+ );
2259
+ }
2260
+ } catch (error) {
2261
+ await rm2(temporary, { force: true }).catch(() => void 0);
2262
+ invalidateWindowsAclCache(temporary);
2263
+ throw error;
2264
+ }
2265
+ }
2266
+ var PROCESS_LOCK_PUBLICATION_GRACE_MS = 1e4;
2267
+ var PROCESS_LOCK_MAX_RECORD_BYTES = 64 * 1024;
2268
+ var PROCESS_LOCK_GUARD_ELECTION_LEASE_MS = 5e3;
2269
+ var PROCESS_LOCK_GUARD_ELECTION_STALE_MS = 3e4;
2270
+ var PROCESS_LOCK_GUARD_ELECTION_ATTEMPTS = 8;
2271
+ var PROCESS_LOCK_GUARD_ELECTION_BACKOFF_MS = 10;
2272
+ var PROCESS_LOCK_LABEL = "Inference host process lock";
2273
+ var PROCESS_LOCK_GUARD_LABEL = "Inference host process lock reclamation guard";
2274
+ var sameProcessLockFile = (left, right) => left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
2275
+ var openExclusiveProcessLockFile = async (path) => {
2276
+ try {
2277
+ return await open(
2278
+ path,
2279
+ constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW,
2280
+ 384
2281
+ );
2282
+ } catch (error) {
2283
+ if (error.code === "EEXIST") return null;
2284
+ throw error;
2285
+ }
2286
+ };
2287
+ var inspectProcessLockFile = async (path, label, probes) => {
2288
+ let before;
2289
+ try {
2290
+ before = await lstat(path);
2291
+ } catch (error) {
2292
+ if (error.code === "ENOENT") return { state: "missing" };
2293
+ throw error;
2294
+ }
2295
+ if (before.isSymbolicLink() || !before.isFile() || process.platform !== "win32" && (before.mode & 63) !== 0 || typeof process.getuid === "function" && before.uid !== process.getuid()) {
2296
+ throw new Error(`${label} is unsafe.`);
2297
+ }
2298
+ let handle;
2299
+ try {
2300
+ await secureExistingWindowsPath(path, "file");
2301
+ handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
2302
+ } catch (error) {
2303
+ if (error.code === "ENOENT") return { state: "missing" };
2304
+ throw error;
2305
+ }
2306
+ let stale = false;
2307
+ try {
2308
+ const opened = await handle.stat();
2309
+ if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino) {
2310
+ return { state: "busy" };
2311
+ }
2312
+ const raw = opened.size > PROCESS_LOCK_MAX_RECORD_BYTES ? "" : await handle.readFile("utf8");
2313
+ const stat = await handle.stat();
2314
+ if (!sameProcessLockFile(opened, stat)) return { state: "busy" };
2315
+ let record = null;
2316
+ try {
2317
+ const parsed = JSON.parse(raw);
2318
+ if (parsed && typeof parsed === "object") record = parsed;
2319
+ } catch {
2320
+ record = null;
2321
+ }
2322
+ if (!record || record.schema_version !== 2 || !Number.isSafeInteger(record.pid) || Number(record.pid) < 1 || typeof record.owner_token !== "string" || !record.owner_token || typeof record.process_identity !== "string" || !record.process_identity || typeof record.boot_identity !== "string" || !record.boot_identity) {
2323
+ if (Date.now() - stat.mtimeMs < PROCESS_LOCK_PUBLICATION_GRACE_MS) return { state: "busy" };
2324
+ stale = true;
2325
+ return { state: "stale", handle, stat };
2326
+ }
2327
+ const ownerPid = Number(record.pid);
2328
+ const storedIdentity = record.process_identity;
2329
+ const cacheKey = resolve(path);
2330
+ if (await probes.currentBootIdentity() === record.boot_identity && probes.isProcessAlive(ownerPid)) {
2331
+ const cached = processLockObservationCache.get(cacheKey);
2332
+ const observedIdentity = cached && cached.dev === stat.dev && cached.ino === stat.ino && cached.pid === ownerPid && cached.storedIdentity === storedIdentity && Date.now() - cached.observedAtMs < 5e3 ? cached.observedIdentity : await probes.observeIdentity(ownerPid);
2333
+ if (!observedIdentity) {
2334
+ throw new Error("Inference host could not verify the existing lock owner identity.");
2335
+ }
2336
+ processLockObservationCache.set(cacheKey, {
2337
+ dev: stat.dev,
2338
+ ino: stat.ino,
2339
+ pid: ownerPid,
2340
+ storedIdentity,
2341
+ observedIdentity,
2342
+ observedAtMs: Date.now()
2343
+ });
2344
+ if (inferenceProcessIdentitiesMatch(storedIdentity, observedIdentity)) {
2345
+ return { state: "busy" };
2346
+ }
2347
+ }
2348
+ stale = true;
2349
+ return { state: "stale", handle, stat };
2350
+ } finally {
2351
+ if (!stale) await handle.close().catch(() => void 0);
2352
+ }
2353
+ };
2354
+ var removeVerifiedStaleProcessLockFile = async (path, inspection, assertExclusive) => {
2355
+ try {
2356
+ await assertExclusive();
2357
+ let current;
2358
+ try {
2359
+ current = await lstat(path);
2360
+ } catch (error) {
2361
+ if (error.code === "ENOENT") return;
2362
+ throw error;
2363
+ }
2364
+ const opened = await inspection.handle.stat();
2365
+ if (current.isSymbolicLink() || !sameProcessLockFile(current, opened) || !sameProcessLockFile(opened, inspection.stat)) {
2366
+ throw new InferenceHostProcessLockBusyError();
2367
+ }
2368
+ if (process.platform === "win32") await inspection.handle.close();
2369
+ try {
2370
+ await rm2(path);
2371
+ } catch (error) {
2372
+ if (error.code !== "ENOENT") throw error;
2373
+ }
2374
+ } finally {
2375
+ await inspection.handle.close().catch(() => void 0);
2376
+ }
2377
+ processLockObservationCache.delete(resolve(path));
2378
+ invalidateWindowsAclCache(path);
2379
+ await syncInferenceDirectory(dirname2(path));
2380
+ };
2381
+ var removeStaleProcessLockReclaimGuard = async (lockPath, guardPath, inspection) => {
2382
+ const directory = dirname2(lockPath);
2383
+ const prefix = `${basename(lockPath)}.reclaim-ballot.`;
2384
+ try {
2385
+ for (let attempt = 0; attempt < PROCESS_LOCK_GUARD_ELECTION_ATTEMPTS; attempt += 1) {
2386
+ if (attempt > 0) {
2387
+ await new Promise((resolvePromise) => setTimeout(
2388
+ resolvePromise,
2389
+ Math.floor(Math.random() * PROCESS_LOCK_GUARD_ELECTION_BACKOFF_MS * 2 ** Math.min(attempt, 4))
2390
+ ));
2391
+ let current;
2392
+ try {
2393
+ current = await lstat(guardPath);
2394
+ } catch (error) {
2395
+ if (error.code === "ENOENT") return;
2396
+ throw error;
2397
+ }
2398
+ if (!sameProcessLockFile(current, inspection.stat)) return;
2399
+ }
2400
+ const ballotName = `${prefix}${randomBytes(16).toString("hex")}`;
2401
+ const ballotPath = join2(directory, ballotName);
2402
+ const ballot = await openExclusiveProcessLockFile(ballotPath);
2403
+ if (!ballot) continue;
2404
+ await ballot.close();
2405
+ const electedAt = performance.now();
2406
+ try {
2407
+ let contested = false;
2408
+ for (const name of await readdir2(directory)) {
2409
+ if (!name.startsWith(prefix) || name === ballotName) continue;
2410
+ const otherPath = join2(directory, name);
2411
+ let other;
2412
+ try {
2413
+ other = await lstat(otherPath);
2414
+ } catch (error) {
2415
+ if (error.code === "ENOENT") continue;
2416
+ throw error;
2417
+ }
2418
+ if (!other.isDirectory() && Date.now() - other.mtimeMs > PROCESS_LOCK_GUARD_ELECTION_STALE_MS) {
2419
+ await rm2(otherPath, { force: true });
2420
+ continue;
2421
+ }
2422
+ contested = true;
2423
+ }
2424
+ if (contested) continue;
2425
+ await removeVerifiedStaleProcessLockFile(guardPath, inspection, async () => {
2426
+ if (performance.now() - electedAt > PROCESS_LOCK_GUARD_ELECTION_LEASE_MS) {
2427
+ throw new InferenceHostProcessLockBusyError();
2428
+ }
2429
+ });
2430
+ return;
2431
+ } finally {
2432
+ await rm2(ballotPath, { force: true }).catch(() => void 0);
2433
+ }
2434
+ }
2435
+ throw new InferenceHostProcessLockBusyError();
2436
+ } finally {
2437
+ await inspection.handle.close().catch(() => void 0);
2438
+ }
2439
+ };
2440
+ var acquireProcessLockReclaimGuard = async (lockPath, ownerRecord, probes) => {
2441
+ const guardPath = `${lockPath}.reclaim`;
2442
+ const guardToken = randomBytes(24).toString("base64url");
2443
+ for (let attempt = 0; attempt < 2; attempt += 1) {
2444
+ const handle = await openExclusiveProcessLockFile(guardPath);
2445
+ if (handle) {
2446
+ try {
2447
+ await handle.writeFile(ownerRecord(guardToken), "utf8");
2448
+ } catch (error) {
2449
+ await handle.close().catch(() => void 0);
2450
+ await rm2(guardPath, { force: true }).catch(() => void 0);
2451
+ throw error;
2452
+ }
2453
+ await handle.close();
2454
+ await secureWindowsPrivatePath(guardPath, "file", "harden");
2455
+ const isHeld = async () => {
2456
+ let reader;
2457
+ try {
2458
+ reader = await open(guardPath, constants.O_RDONLY | constants.O_NOFOLLOW);
2459
+ } catch (error) {
2460
+ if (error.code === "ENOENT") return false;
2461
+ throw error;
2462
+ }
2463
+ try {
2464
+ const [opened, current] = await Promise.all([reader.stat(), lstat(guardPath)]);
2465
+ if (current.isSymbolicLink() || current.dev !== opened.dev || current.ino !== opened.ino) {
2466
+ return false;
2467
+ }
2468
+ const parsed = JSON.parse(await reader.readFile("utf8"));
2469
+ return parsed.owner_token === guardToken;
2470
+ } catch {
2471
+ return false;
2472
+ } finally {
2473
+ await reader.close().catch(() => void 0);
2474
+ }
2475
+ };
2476
+ return {
2477
+ assertHeld: async () => {
2478
+ if (!await isHeld()) throw new InferenceHostProcessLockBusyError();
2479
+ },
2480
+ // Best effort: a guard left behind carries this live process identity
2481
+ // and becomes reclaimable once this process exits.
2482
+ release: async () => {
2483
+ try {
2484
+ if (!await isHeld()) return;
2485
+ await rm2(guardPath);
2486
+ processLockObservationCache.delete(resolve(guardPath));
2487
+ invalidateWindowsAclCache(guardPath);
2488
+ } catch {
2489
+ }
2490
+ }
2491
+ };
2492
+ }
2493
+ if (attempt > 0) break;
2494
+ const inspection = await inspectProcessLockFile(guardPath, PROCESS_LOCK_GUARD_LABEL, probes);
2495
+ if (inspection.state === "busy") break;
2496
+ if (inspection.state === "stale") {
2497
+ await removeStaleProcessLockReclaimGuard(lockPath, guardPath, inspection);
2498
+ }
2499
+ }
2500
+ throw new InferenceHostProcessLockBusyError();
2501
+ };
2502
+ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
2503
+ const directory = dirname2(path);
2504
+ await ensureInferencePrivateDirectory(directory);
2505
+ const ownerToken = randomBytes(24).toString("base64url");
2506
+ const probes = {
2507
+ currentBootIdentity: dependencies.currentBootIdentity ?? (() => readInferenceSystemBootIdentity()),
2508
+ observeIdentity: dependencies.processIdentity ?? defaultProcessIdentity,
2509
+ isProcessAlive: dependencies.processAlive ?? ((pid) => {
2510
+ try {
2511
+ process.kill(pid, 0);
2512
+ return true;
2513
+ } catch (error) {
2514
+ if (error.code === "ESRCH") return false;
2515
+ throw error;
2516
+ }
2517
+ })
2518
+ };
2519
+ const [ownerIdentity, bootIdentity] = await Promise.all([
2520
+ (dependencies.currentProcessIdentity ?? defaultCurrentProcessIdentity)(),
2521
+ probes.currentBootIdentity()
2522
+ ]);
2523
+ const ownerRecord = (token) => `${JSON.stringify({
2524
+ schema_version: 2,
2525
+ pid: process.pid,
2526
+ owner_token: token,
2527
+ process_identity: ownerIdentity,
2528
+ boot_identity: bootIdentity
2529
+ })}
2530
+ `;
2531
+ let handle = await openExclusiveProcessLockFile(path);
2532
+ if (!handle) {
2533
+ const inspection = await inspectProcessLockFile(path, PROCESS_LOCK_LABEL, probes);
2534
+ if (inspection.state === "busy") throw new InferenceHostProcessLockBusyError();
2535
+ if (inspection.state === "stale") {
2536
+ await inspection.handle.close();
2537
+ const guard = await acquireProcessLockReclaimGuard(path, ownerRecord, probes);
2538
+ try {
2539
+ const guarded = await inspectProcessLockFile(path, PROCESS_LOCK_LABEL, probes);
2540
+ if (guarded.state === "busy") throw new InferenceHostProcessLockBusyError();
2541
+ if (guarded.state === "stale") {
2542
+ await removeVerifiedStaleProcessLockFile(path, guarded, guard.assertHeld);
2543
+ }
2544
+ handle = await openExclusiveProcessLockFile(path);
2545
+ } finally {
2546
+ await guard.release();
2547
+ }
2548
+ } else {
2549
+ handle = await openExclusiveProcessLockFile(path);
2550
+ }
2551
+ if (!handle) throw new InferenceHostProcessLockBusyError();
2552
+ }
2553
+ try {
2554
+ await handle.writeFile(ownerRecord(ownerToken), "utf8");
2555
+ await handle.sync();
2556
+ } catch (error) {
2557
+ const created = await handle.stat().catch(() => null);
2558
+ await handle.close().catch(() => void 0);
2559
+ const current = await lstat(path).catch(() => null);
2560
+ if (created && current && current.dev === created.dev && current.ino === created.ino) {
2561
+ await rm2(path, { force: true }).catch(() => void 0);
2562
+ }
2563
+ throw error;
2564
+ }
2565
+ await handle.close();
2566
+ await secureWindowsPrivatePath(path, "file", "harden");
2567
+ await syncInferenceDirectory(directory);
2568
+ let released = false;
2569
+ return {
2570
+ path,
2571
+ ownerToken,
2572
+ release: async () => {
2573
+ if (released) return;
2574
+ const metadata = await lstat(path);
2575
+ if (metadata.isSymbolicLink() || !metadata.isFile()) {
2576
+ throw new Error("Inference host process lock is unsafe.");
2577
+ }
2578
+ const parsed = JSON.parse(await readFile2(path, "utf8"));
2579
+ if (parsed.owner_token !== ownerToken) {
2580
+ throw new Error("Inference host process lock ownership changed.");
2581
+ }
2582
+ await rm2(path);
2583
+ processLockObservationCache.delete(resolve(path));
2584
+ await syncInferenceDirectory(directory);
2585
+ released = true;
2586
+ }
2587
+ };
2588
+ }
2589
+
2590
+ // lib/inference-host/routing.ts
2591
+ init_define_VTX_EXO_POLICY();
2592
+ init_define_VTX_GROK_POLICY();
2593
+ init_define_VTX_PI_MODEL_POLICY();
2594
+ var INFERENCE_HOST_VALUE_OPTIONS = /* @__PURE__ */ new Set([
2595
+ "--display-name",
2596
+ "--max-concurrency",
2597
+ "--adapter",
2598
+ "--model",
2599
+ "--model-label",
2600
+ "--effort",
2601
+ "--wait-seconds",
2602
+ "--lines",
2603
+ "--service-manifest",
2604
+ "--instance"
2605
+ ]);
2606
+ var isInferenceHostCliInvocation = (argv) => {
2607
+ for (let index = 0; index < argv.length; index += 1) {
2608
+ const argument = argv[index];
2609
+ if (INFERENCE_HOST_VALUE_OPTIONS.has(argument)) {
2610
+ index += 1;
2611
+ continue;
2612
+ }
2613
+ if (argument.startsWith("--")) {
2614
+ continue;
2615
+ }
2616
+ return argument === "inference-host";
2617
+ }
2618
+ return false;
2619
+ };
2620
+
2621
+ // lib/cli-release/updater.ts
2622
+ var CLI_PACKAGE_NAME = "@vtxmacro/cli";
2623
+ var OFFICIAL_NPM_REGISTRY = "https://registry.npmjs.org";
2624
+ var CLI_UPDATE_COMMAND = `npm install -g ${CLI_PACKAGE_NAME}@latest`;
2625
+ var parseCliReleaseVersion = (value) => {
2626
+ const match = /^(\d{4})\.(\d{1,2})\.(\d{1,3})$/u.exec(value);
2627
+ if (!match) return null;
2628
+ const values = match.slice(1).map(Number);
2629
+ if (values.some((entry) => !Number.isSafeInteger(entry) || entry < 0)) return null;
2630
+ return values;
2631
+ };
2632
+ var compareCliReleaseVersions = (left, right) => {
2633
+ const leftParts = parseCliReleaseVersion(left);
2634
+ const rightParts = parseCliReleaseVersion(right);
2635
+ if (!leftParts || !rightParts) {
2636
+ throw new Error("CLI release version is invalid.");
2637
+ }
2638
+ for (let index = 0; index < leftParts.length; index += 1) {
2639
+ const difference = leftParts[index] - rightParts[index];
2640
+ if (difference !== 0) return difference;
2641
+ }
2642
+ return 0;
2643
+ };
2644
+ var isPathWithin = (parent, child) => {
2645
+ const childRelative = relative2(resolve2(parent), resolve2(child));
2646
+ return childRelative !== "" && !childRelative.startsWith("..") && !isAbsolute2(childRelative);
2647
+ };
2648
+ var defaultRunCliCommand = async (command, args, options = {}) => await new Promise((resolvePromise) => {
2649
+ const child = spawn2(command, [...args], {
2650
+ windowsHide: true,
2651
+ stdio: ["ignore", "pipe", "pipe"],
2652
+ ...options.signal ? { signal: options.signal, killSignal: "SIGKILL" } : {}
2653
+ });
2654
+ const stdout = [];
2655
+ const stderr = [];
2656
+ let settled = false;
2657
+ const settle = (result) => {
2658
+ if (settled) return;
2659
+ settled = true;
2660
+ resolvePromise(result);
2661
+ };
2662
+ child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk)));
2663
+ child.stderr.on("data", (chunk) => stderr.push(Buffer.from(chunk)));
2664
+ child.once("error", (error) => settle({ exitCode: 1, stdout: "", stderr: error.message }));
2665
+ child.once("exit", (code) => settle({
2666
+ exitCode: code ?? 1,
2667
+ stdout: Buffer.concat(stdout).toString("utf8"),
2668
+ stderr: Buffer.concat(stderr).toString("utf8")
2669
+ }));
2670
+ });
2671
+ var runCliReleaseNpmCommand = async (executable, args, platform = process.platform, runCommand = defaultRunCliCommand) => {
2672
+ if (platform !== "win32") return await runCommand(join3(dirname3(executable), "npm"), args);
2673
+ const npmScript = windowsPath2.join(
2674
+ windowsPath2.dirname(executable),
2675
+ "node_modules",
2676
+ "npm",
2677
+ "bin",
2678
+ "npm-cli.js"
2679
+ );
2680
+ return await runCommand(executable, [npmScript, ...args]);
2681
+ };
2682
+ var fetchLatestCliRelease = async (fetchImpl, options) => {
2683
+ const registry = (options.registry ?? OFFICIAL_NPM_REGISTRY).replace(/\/+$/u, "");
2684
+ const response = await fetchImpl(`${registry}/%40vtxmacro%2Fcli/latest`, {
2685
+ signal: AbortSignal.any([
2686
+ AbortSignal.timeout(options.timeoutMs),
2687
+ ...options.signal ? [options.signal] : []
2688
+ ])
2689
+ });
2690
+ if (!response.ok) throw new Error("registry_request_failed");
2691
+ const value = await response.json();
2692
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2693
+ throw new Error("registry_metadata_invalid");
2694
+ }
2695
+ const record = value;
2696
+ const distribution = record.dist;
2697
+ if (record.name !== CLI_PACKAGE_NAME || typeof record.version !== "string" || !parseCliReleaseVersion(record.version) || !distribution || typeof distribution !== "object" || Array.isArray(distribution) || typeof distribution.integrity !== "string") throw new Error("registry_metadata_invalid");
2698
+ return {
2699
+ version: record.version,
2700
+ integrity: distribution.integrity
2701
+ };
2702
+ };
2703
+ var verifyCliReleaseIntegrity = async (tarballPath, integrity) => {
2704
+ const candidates = integrity.split(/\s+/u).map((entry) => {
2705
+ const separator = entry.indexOf("-");
2706
+ return separator > 0 ? { algorithm: entry.slice(0, separator), digest: entry.slice(separator + 1) } : null;
2707
+ }).filter((entry) => entry !== null);
2708
+ const supported = ["sha512", "sha384", "sha256"].map((algorithm) => candidates.find((candidate) => candidate.algorithm === algorithm)).find(Boolean);
2709
+ if (!supported) throw new Error("release_integrity_unsupported");
2710
+ const actual = createHash(supported.algorithm).update(await readFile3(tarballPath)).digest("base64");
2711
+ if (actual !== supported.digest) throw new Error("release_integrity_mismatch");
2712
+ };
2713
+ var verifyInstalledCliRelease = async (prefix, targetVersion, executable, platform, runCommand) => {
2714
+ const packagePath = platform === "win32" ? windowsPath2.join(prefix, "node_modules", "@vtxmacro", "cli") : join3(prefix, "lib", "node_modules", "@vtxmacro", "cli");
2715
+ if (!isPathWithin(prefix, packagePath)) throw new Error("release_install_invalid");
2716
+ const packageJson = JSON.parse(await readFile3(join3(packagePath, "package.json"), "utf8"));
2717
+ if (!packageJson || typeof packageJson !== "object" || Array.isArray(packageJson)) {
2718
+ throw new Error("release_install_invalid");
2719
+ }
2720
+ const packageRecord = packageJson;
2721
+ const bin = packageRecord.bin;
2722
+ const binPath = bin && typeof bin === "object" && !Array.isArray(bin) ? bin.vtx : null;
2723
+ if (packageRecord.name !== CLI_PACKAGE_NAME || packageRecord.version !== targetVersion || typeof binPath !== "string") throw new Error("release_install_invalid");
2724
+ const script = resolve2(packagePath, binPath);
2725
+ if (!isPathWithin(packagePath, script)) throw new Error("release_install_invalid");
2726
+ await access(script);
2727
+ const verified = await runCommand(executable, [script, "--version"]);
2728
+ if (verified.exitCode !== 0 || verified.stdout.trim() !== targetVersion) {
2729
+ throw new Error("release_version_verification_failed");
2730
+ }
2731
+ return script;
2732
+ };
2733
+ var cleanupInterruptedReleaseStaging = async (releasesPath) => {
2734
+ const entries = await readdir3(releasesPath, { withFileTypes: true });
2735
+ for (const entry of entries) {
2736
+ if (entry.isDirectory() && /^\.(?:staging|download)-\d{4}\.\d{1,2}\.\d{1,3}-[0-9a-f-]{36}$/u.test(entry.name)) {
2737
+ await rm3(resolve2(releasesPath, entry.name), { recursive: true, force: true });
2738
+ }
2739
+ }
2740
+ };
2741
+ var ensureUpdaterPrivateDirectory = async (path, root, dependencies = {}) => {
2742
+ const platform = dependencies.platform ?? process.platform;
2743
+ if (platform !== "win32" && (resolve2(path) === resolve2(root) || isPathWithin(root, path))) {
2744
+ const existing = await lstat2(path).catch(() => null);
2745
+ if (existing && existing.isDirectory() && !existing.isSymbolicLink() && (existing.mode & 63) !== 0 && (typeof process.getuid !== "function" || existing.uid === process.getuid())) {
2746
+ await chmod(path, 448);
2747
+ }
2748
+ }
2749
+ await ensureInferencePrivateDirectory(path, { ...dependencies, defaultRoot: root });
2750
+ };
2751
+ var stageCliRelease = async (input) => {
2752
+ const { releasesPath, target, executable, platform } = input;
2753
+ const registry = input.registry ?? OFFICIAL_NPM_REGISTRY;
2754
+ const deadline = new AbortController();
2755
+ let reason = "release_stage_timeout";
2756
+ const timer = setTimeout(() => deadline.abort(), input.timeoutMs);
2757
+ const onCancel = () => {
2758
+ reason = "release_stage_cancelled";
2759
+ deadline.abort();
2760
+ };
2761
+ if (input.signal?.aborted) onCancel();
2762
+ input.signal?.addEventListener("abort", onCancel, { once: true });
2763
+ const timedOut = () => new Error(reason);
2764
+ const runCommand = async (command, args) => {
2765
+ if (deadline.signal.aborted) throw timedOut();
2766
+ return await new Promise((resolveCommand, rejectCommand) => {
2767
+ const onAbort = () => rejectCommand(timedOut());
2768
+ deadline.signal.addEventListener("abort", onAbort, { once: true });
2769
+ input.runCommand(command, args, { signal: deadline.signal }).then(resolveCommand, rejectCommand).finally(() => deadline.signal.removeEventListener("abort", onAbort));
2770
+ });
2771
+ };
2772
+ try {
2773
+ return await stageCliReleaseWithin({ ...input, registry, runCommand });
2774
+ } finally {
2775
+ clearTimeout(timer);
2776
+ input.signal?.removeEventListener("abort", onCancel);
2777
+ }
2778
+ };
2779
+ var stageCliReleaseWithin = async (input) => {
2780
+ const { releasesPath, target, executable, platform, runCommand, registry } = input;
2781
+ const finalPath = join3(releasesPath, target.version);
2782
+ const stagingPath = join3(releasesPath, `.staging-${target.version}-${randomUUID()}`);
2783
+ const downloadPath = join3(releasesPath, `.download-${target.version}-${randomUUID()}`);
2784
+ await ensureUpdaterPrivateDirectory(input.privateRoot, input.privateRoot, { platform });
2785
+ await ensureUpdaterPrivateDirectory(releasesPath, input.privateRoot, { platform });
2786
+ await cleanupInterruptedReleaseStaging(releasesPath);
2787
+ try {
2788
+ await access(finalPath);
2789
+ const existingScript = await verifyInstalledCliRelease(
2790
+ finalPath,
2791
+ target.version,
2792
+ executable,
2793
+ platform,
2794
+ runCommand
2795
+ );
2796
+ return { package_version: target.version, integrity: target.integrity, script: existingScript };
2797
+ } catch (error) {
2798
+ if (error.code !== "ENOENT") throw error;
2799
+ }
2800
+ await mkdir3(stagingPath, { recursive: true, mode: 448 });
2801
+ await mkdir3(downloadPath, { recursive: true, mode: 448 });
2802
+ try {
2803
+ const packed = await runCliReleaseNpmCommand(executable, [
2804
+ "pack",
2805
+ `${CLI_PACKAGE_NAME}@${target.version}`,
2806
+ "--pack-destination",
2807
+ downloadPath,
2808
+ "--registry",
2809
+ registry,
2810
+ "--json"
2811
+ ], platform, runCommand);
2812
+ if (packed.exitCode !== 0) throw new Error("release_download_failed");
2813
+ let filename;
2814
+ try {
2815
+ const parsed = JSON.parse(packed.stdout);
2816
+ filename = String(parsed[0]?.filename ?? "");
2817
+ } catch {
2818
+ throw new Error("release_download_invalid");
2819
+ }
2820
+ const tarballPath = resolve2(downloadPath, filename);
2821
+ if (!filename || !isPathWithin(downloadPath, tarballPath)) {
2822
+ throw new Error("release_download_invalid");
2823
+ }
2824
+ await verifyCliReleaseIntegrity(tarballPath, target.integrity);
2825
+ const installed = await runCliReleaseNpmCommand(executable, [
2826
+ "install",
2827
+ "--global",
2828
+ "--prefix",
2829
+ stagingPath,
2830
+ tarballPath,
2831
+ "--no-audit",
2832
+ "--no-fund",
2833
+ "--registry",
2834
+ registry
2835
+ ], platform, runCommand);
2836
+ if (installed.exitCode !== 0) throw new Error("release_install_failed");
2837
+ const stagedScript = await verifyInstalledCliRelease(
2838
+ stagingPath,
2839
+ target.version,
2840
+ executable,
2841
+ platform,
2842
+ runCommand
2843
+ );
2844
+ try {
2845
+ await rename2(stagingPath, finalPath);
2846
+ } catch (error) {
2847
+ if (!["EEXIST", "ENOTEMPTY"].includes(String(error.code))) throw error;
2848
+ throw new Error("release_path_already_exists");
2849
+ }
2850
+ return {
2851
+ package_version: target.version,
2852
+ integrity: target.integrity,
2853
+ script: resolve2(finalPath, relative2(stagingPath, stagedScript))
2854
+ };
2855
+ } finally {
2856
+ await rm3(stagingPath, { recursive: true, force: true }).catch(() => void 0);
2857
+ await rm3(downloadPath, { recursive: true, force: true }).catch(() => void 0);
2858
+ }
2859
+ };
2860
+ var readCliReleaseRejection = async (path) => {
2861
+ try {
2862
+ const value = JSON.parse(await readFile3(path, "utf8"));
2863
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2864
+ const record = value;
2865
+ if (record.schema_version !== "vtx_inference_service_release_rejection_v1" || typeof record.package_version !== "string" || !parseCliReleaseVersion(record.package_version) || typeof record.rejected_at !== "string" || !Number.isFinite(Date.parse(record.rejected_at)) || typeof record.reason !== "string" || !/^[a-z0-9_]{1,96}$/u.test(record.reason)) return null;
2866
+ return record;
2867
+ } catch {
2868
+ return null;
2869
+ }
2870
+ };
2871
+ var writeCliReleaseRejection = async (path, rejection, privateRoot) => {
2872
+ if (privateRoot) await ensureUpdaterPrivateDirectory(privateRoot, privateRoot);
2873
+ await writeAtomicInferencePrivateFile(
2874
+ path,
2875
+ `${JSON.stringify(rejection, null, 2)}
2876
+ `,
2877
+ privateRoot ? { defaultRoot: privateRoot } : {}
2878
+ );
2879
+ };
2880
+ var CLI_BACKGROUND_STAGE_ARGUMENT = "--vtx-internal-stage-cli-update";
2881
+ var READY_MESSAGE_TYPE = "vtx-cli-ready-v1";
2882
+ var CHILD_ENV = "VTX_CLI_SELF_UPDATE_CHILD";
2883
+ var READY_PROTOCOL_ENV = "VTX_CLI_READY_PROTOCOL";
2884
+ var EMPTY_STATE = {
2885
+ schema_version: "vtx_cli_update_state_v1",
2886
+ checked_at: null,
2887
+ latest: null,
2888
+ minimum_supported_version: null,
2889
+ minimum_api_url: null,
2890
+ active: null,
2891
+ previous: null
2892
+ };
2893
+ var resolveCliUpdatePaths = (env) => {
2894
+ const home = String(env.VTX_HOME || "").trim() || join3(homedir3(), ".vtx");
2895
+ const root = join3(home, "cli-update");
2896
+ return {
2897
+ root,
2898
+ statePath: join3(root, "state.json"),
2899
+ releasesPath: join3(root, "releases"),
2900
+ lockPath: join3(root, "update.lock"),
2901
+ logPath: join3(root, "update.log"),
2902
+ rejectionPath: join3(root, "release-rejection.json")
2903
+ };
2904
+ };
2905
+ var cliAutoUpdateDisabled = (env) => /^(?:0|false|off|no)$/iu.test(String(env.VTX_CLI_AUTO_UPDATE ?? "").trim());
2906
+ var resolveRegistry = (env) => {
2907
+ const configured = String(env.VTX_CLI_UPDATE_REGISTRY || "").trim().replace(/\/+$/u, "");
2908
+ if (!configured) return OFFICIAL_NPM_REGISTRY;
2909
+ let url;
2910
+ try {
2911
+ url = new URL(configured);
2912
+ } catch {
2913
+ throw new Error("VTX_CLI_UPDATE_REGISTRY is not a URL.");
2914
+ }
2915
+ const loopback = ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname);
2916
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
2917
+ throw new Error("VTX_CLI_UPDATE_REGISTRY must be https, or http on loopback.");
2918
+ }
2919
+ return configured;
2920
+ };
2921
+ var validSelection = async (value, releasesPath) => {
2922
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2923
+ const record = value;
2924
+ if (typeof record.package_version !== "string" || !parseCliReleaseVersion(record.package_version) || typeof record.integrity !== "string" || typeof record.script !== "string" || !isPathWithin(join3(releasesPath, record.package_version), record.script) || typeof record.selected_at !== "string" || record.confirmed_at !== null && typeof record.confirmed_at !== "string") return null;
2925
+ try {
2926
+ await access(record.script);
2927
+ } catch {
2928
+ return null;
2929
+ }
2930
+ return record;
2931
+ };
2932
+ var readState = async (paths) => {
2933
+ let value;
2934
+ try {
2935
+ value = JSON.parse(await readFile3(paths.statePath, "utf8"));
2936
+ } catch {
2937
+ return EMPTY_STATE;
2938
+ }
2939
+ if (!value || typeof value !== "object" || Array.isArray(value)) return EMPTY_STATE;
2940
+ const record = value;
2941
+ if (record.schema_version !== "vtx_cli_update_state_v1") return EMPTY_STATE;
2942
+ const latest = record.latest && typeof record.latest === "object" && typeof record.latest.version === "string" && parseCliReleaseVersion(String(record.latest.version)) && typeof record.latest.integrity === "string" ? record.latest : null;
2943
+ const minimum = typeof record.minimum_supported_version === "string" && parseCliReleaseVersion(record.minimum_supported_version) ? record.minimum_supported_version : null;
2944
+ return {
2945
+ schema_version: "vtx_cli_update_state_v1",
2946
+ checked_at: typeof record.checked_at === "string" && Number.isFinite(Date.parse(record.checked_at)) ? record.checked_at : null,
2947
+ latest,
2948
+ minimum_supported_version: minimum,
2949
+ minimum_api_url: typeof record.minimum_api_url === "string" ? record.minimum_api_url : null,
2950
+ active: await validSelection(record.active, paths.releasesPath),
2951
+ previous: await validSelection(record.previous, paths.releasesPath)
2952
+ };
2953
+ };
2954
+ var writeState = async (paths, state) => {
2955
+ await ensureUpdaterPrivateDirectory(paths.root, paths.root);
2956
+ await writeAtomicInferencePrivateFile(
2957
+ paths.statePath,
2958
+ `${JSON.stringify(state, null, 2)}
2959
+ `,
2960
+ { defaultRoot: paths.root }
2961
+ );
2962
+ };
2963
+ var appendCliUpdateLog = async (paths, event, fields = {}) => {
2964
+ try {
2965
+ await ensureUpdaterPrivateDirectory(paths.root, paths.root);
2966
+ await appendFile(
2967
+ paths.logPath,
2968
+ `${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), event, ...fields })}
2969
+ `,
2970
+ { encoding: "utf8", mode: 384 }
2971
+ );
2972
+ } catch {
2973
+ }
2974
+ };
2975
+ var isSkippedInvocation = (argv, env) => (
2976
+ // A re-run on a selected release, or a process the service updater owns.
2977
+ env[CHILD_ENV] === "1" || env.VTX_INFERENCE_HOST_SERVICE_CHILD === "1" || env.VTX_INFERENCE_HOST_LAUNCHER_PROTOCOL === "1" || isInferenceHostCliInvocation(argv) && argv.includes("run-internal")
2978
+ );
2979
+ var isVersionQuery = (argv) => argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v");
2980
+ var policyApiUrl = (entry, argv, env) => entry === "vtx" && isInferenceHostCliInvocation(argv) ? resolveInferenceHostApiUrl(env) : resolveAgentCliConfig(env).apiUrl;
2981
+ var fetchMinimumSupportedVersion = async (fetchImpl, apiUrl, timeoutMs, signal) => {
2982
+ const response = await fetchImpl(`${apiUrl}/status/agent-cli`, {
2983
+ headers: { Accept: "application/json" },
2984
+ signal: AbortSignal.any([AbortSignal.timeout(timeoutMs), ...signal ? [signal] : []])
2985
+ });
2986
+ if (!response.ok) throw new Error("policy_request_failed");
2987
+ const value = await response.json();
2988
+ const minimum = value?.minimum_supported_version;
2989
+ if (value?.package_name !== CLI_PACKAGE_NAME || typeof minimum !== "string" || !parseCliReleaseVersion(minimum)) {
2990
+ throw new Error("policy_invalid");
2991
+ }
2992
+ return minimum;
2993
+ };
2994
+ var classifyCliInstall = async (script, platform = process.platform, env = process.env) => {
2995
+ try {
2996
+ const real = await realpath2(script);
2997
+ const releasesPath = resolveCliUpdatePaths(env).releasesPath;
2998
+ if (isPathWithin(releasesPath, real) || isPathWithin(await realpath2(releasesPath).catch(() => releasesPath), real) || /\.service-releases[\\/]/u.test(real)) return "managed";
2999
+ const pathApi = platform === "win32" ? windowsPath2 : { dirname: dirname3, join: join3, resolve: resolve2 };
3000
+ const packageRoot = pathApi.dirname(pathApi.dirname(real));
3001
+ const modulesRoot = pathApi.dirname(pathApi.dirname(packageRoot));
3002
+ if (!packageRoot.endsWith(pathApi.join("node_modules", "@vtxmacro", "cli")) || /[\\/]_npx[\\/]/u.test(real)) return "managed";
3003
+ if (platform === "win32") {
3004
+ await access(windowsPath2.join(windowsPath2.dirname(modulesRoot), "vtx.cmd"));
3005
+ return "global";
3006
+ }
3007
+ if (!modulesRoot.endsWith(join3("lib", "node_modules"))) return "managed";
3008
+ const binLink = join3(dirname3(dirname3(modulesRoot)), "bin", "vtx");
3009
+ return await realpath2(binLink) === await realpath2(join3(packageRoot, "bin", "vtx.js")) ? "global" : "managed";
3010
+ } catch {
3011
+ return "managed";
3012
+ }
3013
+ };
3014
+ var refusal = (current, minimum, reason) => `VTX CLI ${current} is below the minimum supported version ${minimum} and ${reason}. Update it with: ${CLI_UPDATE_COMMAND}`;
3015
+ var inferenceHostServiceManifestPaths = async (env) => {
3016
+ const rootManifest = inferenceHostSupervisorServiceManifestPath(env);
3017
+ const instancesPath = join3(dirname3(rootManifest), "instances");
3018
+ const instances = await readdir3(instancesPath, { withFileTypes: true }).catch(() => []);
3019
+ return [
3020
+ rootManifest,
3021
+ ...instances.filter((entry) => entry.isDirectory()).map((entry) => join3(instancesPath, entry.name, "state.json.service.json"))
3022
+ ];
3023
+ };
3024
+ var prune = async (paths, keep, env) => {
3025
+ const retained = new Set(keep.filter((item) => item !== null).map((item) => item.package_version));
3026
+ for (const manifestPath of await inferenceHostServiceManifestPaths(env)) {
3027
+ try {
3028
+ const manifest = JSON.parse(await readFile3(manifestPath, "utf8"));
3029
+ if (typeof manifest.script === "string" && isPathWithin(paths.releasesPath, manifest.script)) {
3030
+ retained.add(relative2(paths.releasesPath, manifest.script).split(/[\\/]/u)[0]);
3031
+ }
3032
+ } catch {
3033
+ }
3034
+ }
3035
+ let entries;
3036
+ try {
3037
+ entries = await readdir3(paths.releasesPath, { withFileTypes: true });
3038
+ } catch {
3039
+ return;
3040
+ }
3041
+ for (const entry of entries) {
3042
+ if (entry.isDirectory() && parseCliReleaseVersion(entry.name) && !retained.has(entry.name)) {
3043
+ await rm3(join3(paths.releasesPath, entry.name), { recursive: true, force: true }).catch(() => void 0);
3044
+ }
3045
+ }
3046
+ };
3047
+ var stageAndSelect = async (paths, target, input) => {
3048
+ let lock;
3049
+ try {
3050
+ await ensureUpdaterPrivateDirectory(paths.root, paths.root, { platform: input.platform });
3051
+ } catch (error) {
3052
+ const reason = `update_directory_unsafe: ${error instanceof Error ? error.message.slice(0, 120) : "unknown"}`;
3053
+ await appendCliUpdateLog(paths, "cli_update_failed", { package_version: target.version, reason });
3054
+ return { kind: "failed", reason };
3055
+ }
3056
+ try {
3057
+ lock = await acquireInferenceHostProcessLock(paths.lockPath);
3058
+ } catch {
3059
+ return { kind: "busy" };
3060
+ }
3061
+ try {
3062
+ const staged = await stageCliRelease({
3063
+ releasesPath: paths.releasesPath,
3064
+ target,
3065
+ executable: input.executable,
3066
+ platform: input.platform,
3067
+ runCommand: input.runCommand,
3068
+ registry: resolveRegistry(input.env),
3069
+ privateRoot: paths.root,
3070
+ timeoutMs: runtime_policy_generated_default.agent_cli.stage_timeout_ms,
3071
+ signal: input.signal
3072
+ });
3073
+ const state = await readState(paths);
3074
+ const selection = {
3075
+ ...staged,
3076
+ selected_at: input.now().toISOString(),
3077
+ confirmed_at: null
3078
+ };
3079
+ const previous = state.active && state.active.package_version !== selection.package_version ? state.active : state.previous;
3080
+ await writeState(paths, { ...state, active: selection, previous });
3081
+ await prune(paths, [selection, previous], input.env);
3082
+ await appendCliUpdateLog(paths, "cli_release_staged", { package_version: selection.package_version });
3083
+ return { kind: "staged", selection };
3084
+ } catch (error) {
3085
+ const reason = error instanceof Error ? error.message.slice(0, 160) : "unknown";
3086
+ await appendCliUpdateLog(paths, "cli_update_failed", { package_version: target.version, reason });
3087
+ return { kind: "failed", reason };
3088
+ } finally {
3089
+ await lock.release().catch(() => void 0);
3090
+ }
3091
+ };
3092
+ var planCliEntryUpdate = async (entry, argv, env, script, dependencies = {}) => {
3093
+ if (isSkippedInvocation(argv, env) || !script) return { kind: "run" };
3094
+ const paths = resolveCliUpdatePaths(env);
3095
+ const current = dependencies.currentVersion ?? agent_cli_release_default.package_version;
3096
+ const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
3097
+ const executable = dependencies.executable ?? process.execPath;
3098
+ const platform = dependencies.platform ?? process.platform;
3099
+ const runCommand = dependencies.runCommand ?? defaultRunCliCommand;
3100
+ const fetchImpl = dependencies.fetch ?? fetch;
3101
+ const policy = runtime_policy_generated_default.agent_cli;
3102
+ const disabled = cliAutoUpdateDisabled(env);
3103
+ const launcher = resolve2(script);
3104
+ try {
3105
+ let state = await readState(paths);
3106
+ const rejection = await readCliReleaseRejection(paths.rejectionPath);
3107
+ let knownKind = null;
3108
+ const installKind = async () => knownKind ??= await classifyCliInstall(launcher, platform, env);
3109
+ const apiUrl = policyApiUrl(entry, argv, env);
3110
+ const selected = !disabled && state.active && compareCliReleaseVersions(state.active.package_version, current) > 0 && rejection?.package_version !== state.active.package_version && await installKind() === "global" ? state.active : null;
3111
+ const effective = selected?.package_version ?? current;
3112
+ if (isVersionQuery(argv)) return selected ? { kind: "reexec", selection: selected } : { kind: "run" };
3113
+ const cachedMinimum = state.minimum_api_url === apiUrl ? state.minimum_supported_version : null;
3114
+ const checkedAt = state.checked_at ? Date.parse(state.checked_at) : Number.NaN;
3115
+ const due = !Number.isFinite(checkedAt) || now().getTime() - checkedAt >= policy.update_check_interval_ms || now().getTime() < checkedAt || cachedMinimum !== null && compareCliReleaseVersions(effective, cachedMinimum) < 0;
3116
+ let refreshed = false;
3117
+ if (due) {
3118
+ refreshed = true;
3119
+ const [minimumResult, latestResult] = await Promise.allSettled([
3120
+ fetchMinimumSupportedVersion(fetchImpl, apiUrl, policy.update_check_timeout_ms, dependencies.signal),
3121
+ disabled ? Promise.reject(new Error("disabled")) : fetchLatestCliRelease(fetchImpl, {
3122
+ registry: resolveRegistry(env),
3123
+ timeoutMs: policy.update_check_timeout_ms,
3124
+ signal: dependencies.signal
3125
+ })
3126
+ ]);
3127
+ state = {
3128
+ ...state,
3129
+ checked_at: now().toISOString(),
3130
+ latest: latestResult.status === "fulfilled" ? latestResult.value : state.latest,
3131
+ minimum_supported_version: minimumResult.status === "fulfilled" ? minimumResult.value : state.minimum_supported_version,
3132
+ minimum_api_url: minimumResult.status === "fulfilled" ? apiUrl : state.minimum_api_url
3133
+ };
3134
+ await writeState(paths, state).catch(() => void 0);
3135
+ }
3136
+ const minimum = state.minimum_api_url === apiUrl ? state.minimum_supported_version : null;
3137
+ const belowMinimum = minimum !== null && compareCliReleaseVersions(effective, minimum) < 0;
3138
+ const runSelected = (warning) => selected ? { kind: "reexec", selection: selected } : { kind: "run", ...warning ? { warning } : {} };
3139
+ if (disabled) {
3140
+ return belowMinimum ? { kind: "refuse", message: refusal(current, minimum, "automatic updates are off (VTX_CLI_AUTO_UPDATE)") } : { kind: "run" };
3141
+ }
3142
+ const latest = state.latest;
3143
+ const updateAvailable = latest !== null && compareCliReleaseVersions(latest.version, effective) > 0 && rejection?.package_version !== latest.version;
3144
+ if ((refreshed || belowMinimum) && updateAvailable) {
3145
+ if (await installKind() !== "global") {
3146
+ return belowMinimum ? { kind: "refuse", message: refusal(effective, minimum, "this install is managed outside the VTX CLI updater") } : { kind: "run" };
3147
+ }
3148
+ if (belowMinimum && compareCliReleaseVersions(latest.version, minimum) < 0) {
3149
+ return { kind: "refuse", message: refusal(effective, minimum, `npm latest (${latest.version}) does not meet it`) };
3150
+ }
3151
+ const stageInput = { env, executable, platform, runCommand, now, signal: dependencies.signal };
3152
+ if (entry === "vtx-mcp" && !belowMinimum) {
3153
+ (dependencies.spawnBackgroundStage ?? spawnBackgroundStage)(executable, launcher, env);
3154
+ return runSelected();
3155
+ }
3156
+ (dependencies.stderr ?? process.stderr).write(
3157
+ `Updating VTX CLI ${effective} to ${latest.version}...
3158
+ `
3159
+ );
3160
+ const outcome = await stageAndSelect(paths, latest, stageInput);
3161
+ if (outcome.kind === "staged") return { kind: "reexec", selection: outcome.selection };
3162
+ const reason = outcome.kind === "busy" ? "another update is in progress" : `the update failed (${outcome.reason})`;
3163
+ if (belowMinimum) return { kind: "refuse", message: refusal(effective, minimum, reason) };
3164
+ return runSelected(`VTX CLI update to ${latest.version} did not complete: ${reason}; continuing on ${effective}.`);
3165
+ }
3166
+ if (belowMinimum) {
3167
+ const reason = latest === null ? "the npm registry could not be reached" : rejection?.package_version === latest.version ? `release ${latest.version} failed to start here` : "no newer release is available";
3168
+ return { kind: "refuse", message: refusal(effective, minimum, reason) };
3169
+ }
3170
+ return runSelected();
3171
+ } catch (error) {
3172
+ await appendCliUpdateLog(paths, "cli_update_check_failed", {
3173
+ reason: error instanceof Error ? error.message.slice(0, 160) : "unknown"
3174
+ });
3175
+ return { kind: "run" };
3176
+ }
3177
+ };
3178
+ var spawnBackgroundStage = (executable, script, env) => {
3179
+ try {
3180
+ const child = spawn2(executable, [script, CLI_BACKGROUND_STAGE_ARGUMENT], {
3181
+ detached: true,
3182
+ stdio: "ignore",
3183
+ windowsHide: true,
3184
+ env
3185
+ });
3186
+ child.on("error", () => void 0);
3187
+ child.unref();
3188
+ } catch {
3189
+ }
3190
+ };
3191
+ var runCliBackgroundStage = async (env = process.env, dependencies = {}) => {
3192
+ const paths = resolveCliUpdatePaths(env);
3193
+ const state = await readState(paths);
3194
+ const current = dependencies.currentVersion ?? agent_cli_release_default.package_version;
3195
+ const rejection = await readCliReleaseRejection(paths.rejectionPath);
3196
+ if (cliAutoUpdateDisabled(env) || !state.latest || compareCliReleaseVersions(state.latest.version, current) <= 0 || rejection?.package_version === state.latest.version || state.active && compareCliReleaseVersions(state.active.package_version, state.latest.version) >= 0) return 0;
3197
+ const outcome = await stageAndSelect(paths, state.latest, {
3198
+ env,
3199
+ executable: dependencies.executable ?? process.execPath,
3200
+ platform: dependencies.platform ?? process.platform,
3201
+ runCommand: dependencies.runCommand ?? defaultRunCliCommand,
3202
+ now: dependencies.now ?? (() => /* @__PURE__ */ new Date())
3203
+ });
3204
+ return outcome.kind === "failed" ? 1 : 0;
3205
+ };
3206
+ var installStopTracker = () => {
3207
+ const stopped = new AbortController();
3208
+ const tracker = {
3209
+ stops: 0,
3210
+ child: null,
3211
+ stopped: stopped.signal,
3212
+ uninstall: () => void 0
3213
+ };
3214
+ const onInterrupt = () => {
3215
+ tracker.stops += 1;
3216
+ stopped.abort();
3217
+ if (process.platform === "win32") return;
3218
+ try {
3219
+ tracker.child?.kill("SIGTERM");
3220
+ } catch {
3221
+ }
3222
+ };
3223
+ const forward = (signal) => () => {
3224
+ tracker.stops += 1;
3225
+ stopped.abort();
3226
+ try {
3227
+ tracker.child?.kill(signal);
3228
+ } catch {
3229
+ }
3230
+ };
3231
+ const forwarded = ["SIGTERM", "SIGHUP"].map((signal) => [signal, forward(signal)]);
3232
+ process.on("SIGINT", onInterrupt);
3233
+ for (const [signal, handler] of forwarded) process.on(signal, handler);
3234
+ tracker.uninstall = () => {
3235
+ process.removeListener("SIGINT", onInterrupt);
3236
+ for (const [signal, handler] of forwarded) process.removeListener(signal, handler);
3237
+ };
3238
+ return tracker;
3239
+ };
3240
+ var runReleaseProcess = async (executable, script, argv, env, readyTimeoutMs, onReady, supervisorStops) => await new Promise((resolvePromise) => {
3241
+ const child = spawn2(executable, [script, ...argv], {
3242
+ stdio: ["inherit", "inherit", "inherit", "ipc"],
3243
+ env: { ...env, [CHILD_ENV]: "1", [READY_PROTOCOL_ENV]: "1" }
3244
+ });
3245
+ let ready = false;
3246
+ let timedOut = false;
3247
+ const stops = supervisorStops ?? installStopTracker();
3248
+ const stopsBefore = supervisorStops ? supervisorStops.stops : 0;
3249
+ stops.child = child;
3250
+ const timer = setTimeout(() => {
3251
+ if (ready) return;
3252
+ timedOut = true;
3253
+ try {
3254
+ child.kill("SIGKILL");
3255
+ } catch {
3256
+ }
3257
+ }, readyTimeoutMs);
3258
+ child.on("message", (message) => {
3259
+ if (message && typeof message === "object" && message.type === READY_MESSAGE_TYPE) {
3260
+ if (ready) return;
3261
+ ready = true;
3262
+ clearTimeout(timer);
3263
+ onReady();
3264
+ }
3265
+ });
3266
+ const finish = (result) => {
3267
+ clearTimeout(timer);
3268
+ stops.child = null;
3269
+ if (!supervisorStops) stops.uninstall();
3270
+ resolvePromise(result);
3271
+ };
3272
+ child.once("error", () => finish({ kind: "not_ready", reason: "startup_exited_before_ready", exitCode: 1 }));
3273
+ child.once("exit", (code, signal) => {
3274
+ if (ready) {
3275
+ finish({ kind: "exited", exitCode: code ?? 1, signal });
3276
+ return;
3277
+ }
3278
+ if (stops.stops > stopsBefore || signal !== null && !timedOut) {
3279
+ finish({ kind: "interrupted", exitCode: code ?? 1, signal });
3280
+ return;
3281
+ }
3282
+ finish({
3283
+ kind: "not_ready",
3284
+ reason: timedOut ? "startup_ready_timeout" : "startup_exited_before_ready",
3285
+ exitCode: code ?? 1
3286
+ });
3287
+ });
3288
+ });
3289
+ var reportCliEntryReady = async () => {
3290
+ if (process.env[READY_PROTOCOL_ENV] !== "1" || typeof process.send !== "function") return;
3291
+ delete process.env[READY_PROTOCOL_ENV];
3292
+ await new Promise((resolvePromise) => {
3293
+ try {
3294
+ process.send({ type: READY_MESSAGE_TYPE, package_version: agent_cli_release_default.package_version }, void 0, {}, () => {
3295
+ resolvePromise();
3296
+ });
3297
+ } catch {
3298
+ resolvePromise();
3299
+ }
3300
+ });
3301
+ try {
3302
+ process.disconnect?.();
3303
+ } catch {
3304
+ }
3305
+ };
3306
+ var markConfirmed = async (paths, selection, now) => {
3307
+ try {
3308
+ const state = await readState(paths);
3309
+ if (state.active?.package_version !== selection.package_version || state.active.confirmed_at) return;
3310
+ await writeState(paths, { ...state, active: { ...state.active, confirmed_at: now().toISOString() } });
3311
+ await appendCliUpdateLog(paths, "cli_release_confirmed", { package_version: selection.package_version });
3312
+ } catch {
3313
+ }
3314
+ };
3315
+ var ROLLBACK_SKIPPED = /* @__PURE__ */ Symbol("rollback-skipped");
3316
+ var rollBack = async (paths, selection, reason, now, env) => {
3317
+ await ensureUpdaterPrivateDirectory(paths.root, paths.root).catch(() => void 0);
3318
+ const lock = await acquireInferenceHostProcessLock(paths.lockPath).catch(() => null);
3319
+ try {
3320
+ const current = await readState(paths);
3321
+ if (current.active?.package_version === selection.package_version && current.active.confirmed_at) {
3322
+ await appendCliUpdateLog(paths, "cli_release_rollback_skipped", {
3323
+ package_version: selection.package_version,
3324
+ reason
3325
+ });
3326
+ return ROLLBACK_SKIPPED;
3327
+ }
3328
+ await writeCliReleaseRejection(paths.rejectionPath, {
3329
+ schema_version: "vtx_inference_service_release_rejection_v1",
3330
+ package_version: selection.package_version,
3331
+ rejected_at: now().toISOString(),
3332
+ reason
3333
+ }, paths.root).catch(() => void 0);
3334
+ const state = await readState(paths);
3335
+ const fallback = state.active?.package_version === selection.package_version ? state.previous : state.active;
3336
+ await writeState(paths, { ...state, active: fallback, previous: null }).catch(() => void 0);
3337
+ if (lock) await prune(paths, [fallback], env);
3338
+ return await finishRollBack(paths, selection, reason, fallback);
3339
+ } finally {
3340
+ await lock?.release().catch(() => void 0);
3341
+ }
3342
+ };
3343
+ var finishRollBack = async (paths, selection, reason, fallback) => {
3344
+ await appendCliUpdateLog(paths, "cli_release_rolled_back", {
3345
+ package_version: selection.package_version,
3346
+ restored_version: fallback?.package_version ?? null,
3347
+ reason
3348
+ });
3349
+ return fallback?.package_version ?? null;
3350
+ };
3351
+ var INTERRUPTED_EXIT_CODE = 130;
3352
+ var superviseRuntime = async (argv, dependencies, env, launcher) => {
3353
+ const stderr = dependencies.stderr ?? process.stderr;
3354
+ const paths = resolveCliUpdatePaths(env);
3355
+ const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
3356
+ const current = dependencies.currentVersion ?? agent_cli_release_default.package_version;
3357
+ const supervisorEnv = {
3358
+ ...env,
3359
+ [RUNTIME_RESUME_FILE_ENV]: join3(paths.root, "runtime-resume", `${process.pid}-${randomUUID()}.json`)
3360
+ };
3361
+ const stops = installStopTracker();
3362
+ const endAfterStop = async (exitCode) => {
3363
+ if (!await readRuntimeResume(supervisorEnv) || !dependencies.stopRuntimeBetweenChildren) return exitCode;
3364
+ const stopExitCode = await dependencies.stopRuntimeBetweenChildren(supervisorEnv);
3365
+ return stopExitCode === 0 ? exitCode : stopExitCode;
3366
+ };
3367
+ let resuming = false;
3368
+ let resumeRecord = null;
3369
+ let failedStarts = 0;
3370
+ try {
3371
+ for (; ; ) {
3372
+ if (stops.stops > 0) return resuming ? await endAfterStop(INTERRUPTED_EXIT_CODE) : INTERRUPTED_EXIT_CODE;
3373
+ const plan = await planCliEntryUpdate("vtx", argv, env, launcher, { ...dependencies, signal: stops.stopped });
3374
+ if (stops.stops > 0) return resuming ? await endAfterStop(INTERRUPTED_EXIT_CODE) : INTERRUPTED_EXIT_CODE;
3375
+ if (plan.kind === "refuse") {
3376
+ stderr.write(`${plan.message}
3377
+ `);
3378
+ return 1;
3379
+ }
3380
+ if (plan.kind === "run" && plan.warning) stderr.write(`${plan.warning}
3381
+ `);
3382
+ const childScript = plan.kind === "reexec" ? await releaseEntryScript(plan.selection, "vtx").catch(() => null) : launcher;
3383
+ const childEnv = {
3384
+ ...supervisorEnv,
3385
+ [RUNTIME_SUPERVISED_ENV]: "1",
3386
+ [RUNTIME_SUPERVISOR_PID_ENV]: String(process.pid)
3387
+ };
3388
+ if (resuming) childEnv[RUNTIME_RESUME_ENV] = "1";
3389
+ else delete childEnv[RUNTIME_RESUME_ENV];
3390
+ const childArgv = resuming && resumeRecord && !argv.includes("--profile") ? [...argv, "--profile", String(resumeRecord.profileId)] : argv;
3391
+ let confirmation = null;
3392
+ if (stops.stops > 0) return resuming ? await endAfterStop(INTERRUPTED_EXIT_CODE) : INTERRUPTED_EXIT_CODE;
3393
+ const result = childScript === null ? { kind: "not_ready", reason: "startup_exited_before_ready", exitCode: 1 } : await (dependencies.runRelease ?? runReleaseProcess)(
3394
+ dependencies.executable ?? process.execPath,
3395
+ childScript,
3396
+ childArgv,
3397
+ childEnv,
3398
+ dependencies.readyTimeoutMs ?? runtime_policy_generated_default.agent_cli.startup_ready_timeout_ms,
3399
+ () => {
3400
+ if (plan.kind === "reexec" && !plan.selection.confirmed_at) {
3401
+ confirmation = markConfirmed(paths, plan.selection, now);
3402
+ }
3403
+ },
3404
+ stops
3405
+ );
3406
+ await confirmation;
3407
+ if (result.kind === "exited" && result.signal === null && result.exitCode === RUNTIME_RESTART_FOR_UPDATE_EXIT_CODE) {
3408
+ resumeRecord = await readRuntimeResume(supervisorEnv);
3409
+ if (!resumeRecord) {
3410
+ stderr.write("VTX CLI: the runtime ended for an update without a resume record; it was not restarted.\n");
3411
+ return 1;
3412
+ }
3413
+ resuming = true;
3414
+ failedStarts = 0;
3415
+ continue;
3416
+ }
3417
+ if (result.kind === "exited" || result.kind === "interrupted") {
3418
+ const childExitCode = result.signal ? 128 + (osConstants.signals[result.signal] ?? 0) : result.exitCode;
3419
+ return stops.stops > 0 ? await endAfterStop(childExitCode) : childExitCode;
3420
+ }
3421
+ if (plan.kind !== "reexec" || plan.selection.confirmed_at || failedStarts >= 2) {
3422
+ return result.exitCode;
3423
+ }
3424
+ failedStarts += 1;
3425
+ const restored = await rollBack(paths, plan.selection, result.reason, now, env);
3426
+ if (restored !== ROLLBACK_SKIPPED) {
3427
+ stderr.write(
3428
+ `VTX CLI ${plan.selection.package_version} failed to start (${result.reason}); rolled back to ${restored ?? current}.
3429
+ `
3430
+ );
3431
+ }
3432
+ }
3433
+ } finally {
3434
+ stops.uninstall();
3435
+ await clearRuntimeResume(supervisorEnv).catch(() => void 0);
3436
+ }
3437
+ };
3438
+ var releaseEntryScript = async (selection, entry) => {
3439
+ if (entry === "vtx") return selection.script;
3440
+ const packageRoot = dirname3(dirname3(selection.script));
3441
+ const packageJson = JSON.parse(await readFile3(join3(packageRoot, "package.json"), "utf8"));
3442
+ const binPath = packageJson.bin?.[entry];
3443
+ if (typeof binPath !== "string") throw new Error("release_entry_missing");
3444
+ const script = resolve2(packageRoot, binPath);
3445
+ if (!isPathWithin(packageRoot, script)) throw new Error("release_entry_missing");
3446
+ await access(script);
3447
+ return script;
3448
+ };
3449
+ var runCliEntry = async (entry, argv, runHere, dependencies = {}, env = process.env, script = process.argv[1] ?? "") => {
3450
+ const stderr = dependencies.stderr ?? process.stderr;
3451
+ if (argv[0] === CLI_BACKGROUND_STAGE_ARGUMENT) {
3452
+ return await runCliBackgroundStage(env, dependencies).catch(() => 1);
3453
+ }
3454
+ if (entry === "vtx" && isSupervisableRuntimeCommand(argv) && !isSkippedInvocation(argv, env) && !cliAutoUpdateDisabled(env) && script && await classifyCliInstall(resolve2(script), dependencies.platform ?? process.platform, env) === "global") {
3455
+ return await superviseRuntime(argv, dependencies, env, resolve2(script));
3456
+ }
3457
+ const paths = resolveCliUpdatePaths(env);
3458
+ const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
3459
+ const current = dependencies.currentVersion ?? agent_cli_release_default.package_version;
3460
+ for (let attempt = 0; attempt < 3; attempt += 1) {
3461
+ const plan = await planCliEntryUpdate(entry, argv, env, script, dependencies);
3462
+ if (plan.kind === "refuse") {
3463
+ stderr.write(`${plan.message}
3464
+ `);
3465
+ return 1;
3466
+ }
3467
+ if (plan.kind === "run") {
3468
+ if (plan.warning) stderr.write(`${plan.warning}
3469
+ `);
3470
+ return await runHere();
3471
+ }
3472
+ let confirmation = null;
3473
+ const entryScript = await releaseEntryScript(plan.selection, entry).catch(() => null);
3474
+ const result = entryScript === null ? { kind: "not_ready", reason: "startup_exited_before_ready", exitCode: 1 } : await (dependencies.runRelease ?? runReleaseProcess)(
3475
+ dependencies.executable ?? process.execPath,
3476
+ entryScript,
3477
+ argv,
3478
+ env,
3479
+ dependencies.readyTimeoutMs ?? runtime_policy_generated_default.agent_cli.startup_ready_timeout_ms,
3480
+ () => {
3481
+ if (!plan.selection.confirmed_at) confirmation = markConfirmed(paths, plan.selection, now);
3482
+ }
3483
+ );
3484
+ await confirmation;
3485
+ if (result.kind === "exited" || result.kind === "interrupted") {
3486
+ if (result.signal) {
3487
+ process.kill(process.pid, result.signal);
3488
+ }
3489
+ return result.exitCode;
3490
+ }
3491
+ if (plan.selection.confirmed_at) {
3492
+ return result.exitCode;
3493
+ }
3494
+ const restored = await rollBack(paths, plan.selection, result.reason, now, env);
3495
+ if (restored === ROLLBACK_SKIPPED) continue;
3496
+ stderr.write(
3497
+ `VTX CLI ${plan.selection.package_version} failed to start (${result.reason}); rolled back to ${restored ?? current}.
3498
+ `
3499
+ );
3500
+ }
3501
+ return await runHere();
3502
+ };
3503
+ var RUNTIME_RESTART_FOR_UPDATE_EXIT_CODE = 75;
3504
+ var RUNTIME_SUPERVISED_ENV = "VTX_CLI_RUNTIME_SUPERVISED";
3505
+ var RUNTIME_RESUME_ENV = "VTX_CLI_RUNTIME_RESUME";
3506
+ var RUNTIME_SUPERVISOR_PID_ENV = "VTX_CLI_RUNTIME_SUPERVISOR_PID";
3507
+ var RUNTIME_RESUME_FILE_ENV = "VTX_CLI_RUNTIME_RESUME_FILE";
3508
+ var runtimeResumeFile = (env) => String(env[RUNTIME_RESUME_FILE_ENV] ?? "").trim() || null;
3509
+ var parseRuntimeResumeRecord = (value) => {
3510
+ const record = value && typeof value === "object" && !Array.isArray(value) ? value : null;
3511
+ const profileId = Number(record?.profileId);
3512
+ const text = (field) => typeof record?.[field] === "string" ? String(record[field]).trim() : "";
3513
+ if (!Number.isSafeInteger(profileId) || profileId <= 0) return null;
3514
+ if (!text("runtimeSessionId") || !text("deviceId") || !text("handoffKey")) return null;
3515
+ return {
3516
+ profileId,
3517
+ runtimeSessionId: text("runtimeSessionId"),
3518
+ deviceId: text("deviceId"),
3519
+ leaseToken: text("leaseToken") || null,
3520
+ handoffKey: text("handoffKey")
3521
+ };
3522
+ };
3523
+ var readRuntimeResume = async (env = process.env) => {
3524
+ const path = runtimeResumeFile(env);
3525
+ if (!path) return null;
3526
+ try {
3527
+ return parseRuntimeResumeRecord(JSON.parse(await readFile3(path, "utf8")));
3528
+ } catch {
3529
+ return null;
3530
+ }
3531
+ };
3532
+ var clearRuntimeResume = async (env = process.env) => {
3533
+ const path = runtimeResumeFile(env);
3534
+ if (path) await rm3(path, { force: true });
3535
+ };
3536
+ var isSupervisableRuntimeCommand = (argv) => {
3537
+ if (argv.includes("--once") || isInferenceHostCliInvocation(argv)) return false;
3538
+ const words = argv.filter((item) => !item.startsWith("--"));
3539
+ const at = words.indexOf("runtime");
3540
+ if (at < 0) return false;
3541
+ return words[at + 1] === "run" || words[at + 1] === "start" && argv.includes("--follow");
3542
+ };
3543
+
3544
+ // bin/vtx-mcp.ts
3545
+ process.exitCode = await runCliEntry("vtx-mcp", process.argv.slice(2), async () => {
3546
+ const { runVtxMcpStdioServer: runVtxMcpStdioServer2 } = await Promise.resolve().then(() => (init_stdio(), stdio_exports));
3547
+ await reportCliEntryReady();
3548
+ await runVtxMcpStdioServer2();
3549
+ return typeof process.exitCode === "number" ? process.exitCode : 0;
3550
+ });
3551
+ /*! Bundled license information:
3552
+
3553
+ smol-toml/dist/date.js:
3554
+ smol-toml/dist/error.js:
3555
+ smol-toml/dist/util.js:
3556
+ smol-toml/dist/primitive.js:
3557
+ smol-toml/dist/extract.js:
3558
+ smol-toml/dist/struct.js:
3559
+ smol-toml/dist/parse.js:
3560
+ smol-toml/dist/stringify.js:
3561
+ smol-toml/dist/index.js:
3562
+ (*!
3563
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
3564
+ * SPDX-License-Identifier: BSD-3-Clause
3565
+ *
3566
+ * Redistribution and use in source and binary forms, with or without
3567
+ * modification, are permitted provided that the following conditions are met:
3568
+ *
3569
+ * 1. Redistributions of source code must retain the above copyright notice, this
3570
+ * list of conditions and the following disclaimer.
3571
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
3572
+ * this list of conditions and the following disclaimer in the
3573
+ * documentation and/or other materials provided with the distribution.
3574
+ * 3. Neither the name of the copyright holder nor the names of its contributors
3575
+ * may be used to endorse or promote products derived from this software without
3576
+ * specific prior written permission.
3577
+ *
3578
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
3579
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3580
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3581
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
3582
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3583
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3584
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
3585
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3586
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3587
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3588
+ *)
3589
+ */