@hypercli.com/sdk 2026.4.13-3 → 2026.4.13-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/agent.d.ts +350 -0
  2. package/dist/agent.d.ts.map +1 -0
  3. package/dist/agent.js +620 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/agents.d.ts +433 -0
  6. package/dist/agents.d.ts.map +1 -0
  7. package/dist/agents.js +1140 -0
  8. package/dist/agents.js.map +1 -0
  9. package/dist/billing.d.ts +72 -0
  10. package/dist/billing.d.ts.map +1 -0
  11. package/dist/billing.js +93 -0
  12. package/dist/billing.js.map +1 -0
  13. package/dist/browser.d.ts +25 -0
  14. package/dist/browser.d.ts.map +1 -0
  15. package/dist/browser.js +33 -0
  16. package/dist/browser.js.map +1 -0
  17. package/dist/client.d.ts +67 -0
  18. package/dist/client.d.ts.map +1 -0
  19. package/dist/client.js +100 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/config.d.ts +46 -0
  22. package/dist/config.d.ts.map +1 -0
  23. package/dist/config.js +235 -0
  24. package/dist/config.js.map +1 -0
  25. package/dist/errors.d.ts +9 -0
  26. package/dist/errors.d.ts.map +1 -0
  27. package/dist/errors.js +15 -0
  28. package/dist/errors.js.map +1 -0
  29. package/dist/files.d.ts +60 -0
  30. package/dist/files.d.ts.map +1 -0
  31. package/dist/files.js +128 -0
  32. package/dist/files.js.map +1 -0
  33. package/dist/gateway.d.ts +2 -0
  34. package/dist/gateway.d.ts.map +1 -0
  35. package/dist/gateway.js +2 -0
  36. package/dist/gateway.js.map +1 -0
  37. package/dist/http.d.ts +42 -0
  38. package/dist/http.d.ts.map +1 -0
  39. package/dist/http.js +238 -0
  40. package/dist/http.js.map +1 -0
  41. package/dist/index.d.ts +26 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +42 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/instances.d.ts +94 -0
  46. package/dist/instances.d.ts.map +1 -0
  47. package/dist/instances.js +197 -0
  48. package/dist/instances.js.map +1 -0
  49. package/dist/job/base.d.ts +80 -0
  50. package/dist/job/base.d.ts.map +1 -0
  51. package/dist/job/base.js +200 -0
  52. package/dist/job/base.js.map +1 -0
  53. package/dist/job/comfyui.d.ts +75 -0
  54. package/dist/job/comfyui.d.ts.map +1 -0
  55. package/dist/job/comfyui.js +454 -0
  56. package/dist/job/comfyui.js.map +1 -0
  57. package/dist/job/gradio.d.ts +48 -0
  58. package/dist/job/gradio.d.ts.map +1 -0
  59. package/dist/job/gradio.js +90 -0
  60. package/dist/job/gradio.js.map +1 -0
  61. package/dist/jobs.d.ts +153 -0
  62. package/dist/jobs.d.ts.map +1 -0
  63. package/dist/jobs.js +352 -0
  64. package/dist/jobs.js.map +1 -0
  65. package/dist/keys.d.ts +83 -0
  66. package/dist/keys.d.ts.map +1 -0
  67. package/dist/keys.js +69 -0
  68. package/dist/keys.js.map +1 -0
  69. package/dist/logs.d.ts +54 -0
  70. package/dist/logs.d.ts.map +1 -0
  71. package/dist/logs.js +256 -0
  72. package/dist/logs.js.map +1 -0
  73. package/dist/models.d.ts +15 -0
  74. package/dist/models.d.ts.map +1 -0
  75. package/dist/models.js +19 -0
  76. package/dist/models.js.map +1 -0
  77. package/dist/openclaw/gateway.d.ts +269 -0
  78. package/dist/openclaw/gateway.d.ts.map +1 -0
  79. package/dist/openclaw/gateway.js +2021 -0
  80. package/dist/openclaw/gateway.js.map +1 -0
  81. package/dist/renders.d.ts +187 -0
  82. package/dist/renders.d.ts.map +1 -0
  83. package/dist/renders.js +360 -0
  84. package/dist/renders.js.map +1 -0
  85. package/dist/user.d.ts +49 -0
  86. package/dist/user.d.ts.map +1 -0
  87. package/dist/user.js +57 -0
  88. package/dist/user.js.map +1 -0
  89. package/dist/voice.d.ts +31 -0
  90. package/dist/voice.d.ts.map +1 -0
  91. package/dist/voice.js +36 -0
  92. package/dist/voice.js.map +1 -0
  93. package/dist/x402.d.ts +79 -0
  94. package/dist/x402.d.ts.map +1 -0
  95. package/dist/x402.js +295 -0
  96. package/dist/x402.js.map +1 -0
  97. package/package.json +2 -1
package/dist/agents.js ADDED
@@ -0,0 +1,1140 @@
1
+ /**
2
+ * HyperClaw agents API - typed agent lifecycle, files, exec, and OpenClaw access.
3
+ */
4
+ import { randomFillSync } from 'node:crypto';
5
+ import { getAgentsApiBaseUrl, getConfigValue } from './config.js';
6
+ import { APIError } from './errors.js';
7
+ import { HTTPClient } from './http.js';
8
+ import { GatewayClient, } from './openclaw/gateway.js';
9
+ const AGENTS_API_BASE = 'https://api.hypercli.com/agents';
10
+ const DEV_AGENTS_API_BASE = 'https://api.dev.hypercli.com/agents';
11
+ const DEPLOYMENTS_API_PREFIX = '/deployments';
12
+ const AGENTS_WS_URL = 'wss://api.agents.hypercli.com/ws';
13
+ const DEV_AGENTS_WS_URL = 'wss://api.agents.dev.hypercli.com/ws';
14
+ export const DEFAULT_OPENCLAW_IMAGE = 'ghcr.io/hypercli/hypercli-openclaw:prod';
15
+ const LAUNCH_CONFIG_KEYS = new Set(['image', 'env', 'routes', 'ports', 'command', 'entrypoint', 'sync_root', 'sync_enabled', 'registry_url', 'registry_auth']);
16
+ const DEFAULT_OPENCLAW_SYNC_ROOT = '/home/node';
17
+ function parseDate(value) {
18
+ if (typeof value !== 'string' || !value)
19
+ return null;
20
+ return new Date(value.replace('Z', '+00:00'));
21
+ }
22
+ function deepMergeConfig(base, patch) {
23
+ const merged = structuredClone(base);
24
+ for (const [key, value] of Object.entries(patch)) {
25
+ if (value &&
26
+ typeof value === 'object' &&
27
+ !Array.isArray(value) &&
28
+ merged[key] &&
29
+ typeof merged[key] === 'object' &&
30
+ !Array.isArray(merged[key])) {
31
+ merged[key] = deepMergeConfig(merged[key], value);
32
+ }
33
+ else {
34
+ merged[key] = structuredClone(value);
35
+ }
36
+ }
37
+ return merged;
38
+ }
39
+ function sleep(ms) {
40
+ return new Promise((resolve) => setTimeout(resolve, ms));
41
+ }
42
+ function isDirectoryListingPayload(value) {
43
+ if (!value || typeof value !== 'object')
44
+ return false;
45
+ const payload = value;
46
+ return (payload.type === 'directory' &&
47
+ Array.isArray(payload.directories) &&
48
+ Array.isArray(payload.files));
49
+ }
50
+ function toWsBaseUrl(baseUrl) {
51
+ const base = (baseUrl || '').replace(/\/+$/, '');
52
+ if (!base)
53
+ return '';
54
+ if (base.startsWith('https://'))
55
+ return `wss://${base.slice('https://'.length)}`;
56
+ if (base.startsWith('http://'))
57
+ return `ws://${base.slice('http://'.length)}`;
58
+ return base;
59
+ }
60
+ function normalizeAgentsWsUrl(url) {
61
+ const base = toWsBaseUrl(url);
62
+ if (!base)
63
+ return '';
64
+ return base.endsWith('/ws') ? base : `${base}/ws`;
65
+ }
66
+ export function resolveAgentsApiBase(apiBase) {
67
+ const raw = (apiBase || '').trim();
68
+ if (!raw)
69
+ return AGENTS_API_BASE;
70
+ const parsed = new URL(raw.includes('://') ? raw : `https://${raw}`);
71
+ const normalizedPath = parsed.pathname.replace(/\/+$/, '');
72
+ const host = parsed.host.toLowerCase();
73
+ if (normalizedPath.endsWith('/agents')) {
74
+ return `${parsed.origin}${normalizedPath}`;
75
+ }
76
+ if (normalizedPath.endsWith('/api')) {
77
+ if (host === 'api.agents.hypercli.com') {
78
+ return AGENTS_API_BASE;
79
+ }
80
+ if (host === 'api.agents.dev.hypercli.com') {
81
+ return DEV_AGENTS_API_BASE;
82
+ }
83
+ return `${parsed.origin}${normalizedPath.slice(0, -4)}/agents`;
84
+ }
85
+ if (host === 'api.agents.hypercli.com' || host === 'api.hypercli.com' || host === 'api.hyperclaw.app') {
86
+ return AGENTS_API_BASE;
87
+ }
88
+ if (host === 'api.agents.dev.hypercli.com' ||
89
+ host === 'api.dev.hypercli.com' ||
90
+ host === 'api.dev.hyperclaw.app' ||
91
+ host === 'dev-api.hyperclaw.app') {
92
+ return DEV_AGENTS_API_BASE;
93
+ }
94
+ const normalized = raw.replace(/\/$/, '');
95
+ return `${normalized}/agents`;
96
+ }
97
+ function defaultAgentsWsUrl(apiBase) {
98
+ const resolvedApiBase = resolveAgentsApiBase(apiBase);
99
+ const parsed = new URL(resolvedApiBase.includes('://') ? resolvedApiBase : `https://${resolvedApiBase}`);
100
+ const host = parsed.host.toLowerCase();
101
+ if (host === 'api.agents.hypercli.com' || host === 'api.hypercli.com' || host === 'api.hyperclaw.app')
102
+ return AGENTS_WS_URL;
103
+ if (host === 'api.agents.dev.hypercli.com' ||
104
+ host === 'api.dev.hypercli.com' ||
105
+ host === 'api.dev.hyperclaw.app' ||
106
+ host === 'dev-api.hyperclaw.app') {
107
+ return DEV_AGENTS_WS_URL;
108
+ }
109
+ return normalizeAgentsWsUrl(resolvedApiBase);
110
+ }
111
+ function randomHexToken(bytes) {
112
+ const buffer = new Uint8Array(bytes);
113
+ if (globalThis.crypto?.getRandomValues) {
114
+ globalThis.crypto.getRandomValues(buffer);
115
+ }
116
+ else {
117
+ randomFillSync(buffer);
118
+ }
119
+ return Array.from(buffer, (value) => value.toString(16).padStart(2, '0')).join('');
120
+ }
121
+ function encodeFilePath(path) {
122
+ return path
123
+ .replace(/^\/+/, '')
124
+ .split('/')
125
+ .filter(Boolean)
126
+ .map((part) => encodeURIComponent(part))
127
+ .join('/');
128
+ }
129
+ function decodeUtf8(content) {
130
+ return new TextDecoder().decode(content);
131
+ }
132
+ function encodeUtf8(content) {
133
+ return new TextEncoder().encode(content);
134
+ }
135
+ function toUint8Array(content) {
136
+ if (typeof content === 'string')
137
+ return encodeUtf8(content);
138
+ if (content instanceof Uint8Array)
139
+ return content;
140
+ return new Uint8Array(content);
141
+ }
142
+ function execResultFromDict(data) {
143
+ return {
144
+ exitCode: data.exit_code ?? -1,
145
+ stdout: data.stdout || '',
146
+ stderr: data.stderr || '',
147
+ };
148
+ }
149
+ function agentStateFromDict(data) {
150
+ return {
151
+ id: data.id ?? '',
152
+ userId: data.user_id ?? '',
153
+ podId: data.pod_id ?? '',
154
+ podName: data.pod_name ?? '',
155
+ state: data.state ?? 'unknown',
156
+ name: data.name ?? null,
157
+ cpu: data.cpu ?? 0,
158
+ memory: data.memory ?? 0,
159
+ hostname: data.hostname ?? null,
160
+ tags: Array.isArray(data.tags) ? data.tags : [],
161
+ jwtToken: data.jwt_token ?? null,
162
+ jwtExpiresAt: parseDate(data.jwt_expires_at),
163
+ startedAt: parseDate(data.started_at),
164
+ stoppedAt: parseDate(data.stopped_at),
165
+ lastError: data.last_error ?? null,
166
+ createdAt: parseDate(data.created_at),
167
+ updatedAt: parseDate(data.updated_at),
168
+ launchConfig: data.launch_config ?? null,
169
+ meta: data.meta?.ui ? { ui: structuredClone(data.meta.ui) } : null,
170
+ routes: data.routes ?? {},
171
+ command: data.command ?? [],
172
+ entrypoint: data.entrypoint ?? [],
173
+ ports: data.ports ?? [],
174
+ dryRun: Boolean(data.dry_run),
175
+ };
176
+ }
177
+ export function buildAgentConfig(config = {}, options = {}) {
178
+ const preparedConfig = { ...config };
179
+ const nestedLaunchKeys = Object.keys(preparedConfig).filter((key) => LAUNCH_CONFIG_KEYS.has(key));
180
+ if (nestedLaunchKeys.length) {
181
+ throw new Error(`Launch settings must be top-level fields, not nested under config: ${nestedLaunchKeys.join(', ')}`);
182
+ }
183
+ const env = { ...(options.env ?? {}) };
184
+ let gatewayToken = options.gatewayToken?.trim() || env.OPENCLAW_GATEWAY_TOKEN?.trim() || '';
185
+ if (!gatewayToken) {
186
+ gatewayToken = randomHexToken(32);
187
+ }
188
+ env.OPENCLAW_GATEWAY_TOKEN = gatewayToken;
189
+ const prepared = {};
190
+ if (Object.keys(preparedConfig).length > 0)
191
+ prepared.config = preparedConfig;
192
+ if (Object.keys(env).length > 0)
193
+ prepared.env = env;
194
+ if (options.ports !== undefined && options.ports !== null)
195
+ prepared.ports = options.ports;
196
+ if (options.routes !== undefined && options.routes !== null)
197
+ prepared.routes = options.routes;
198
+ if (options.command !== undefined && options.command !== null)
199
+ prepared.command = options.command;
200
+ if (options.entrypoint !== undefined && options.entrypoint !== null)
201
+ prepared.entrypoint = options.entrypoint;
202
+ if (options.image !== undefined && options.image !== null)
203
+ prepared.image = options.image;
204
+ if (options.syncRoot !== undefined && options.syncRoot !== null)
205
+ prepared.sync_root = options.syncRoot;
206
+ if (options.syncEnabled !== undefined && options.syncEnabled !== null)
207
+ prepared.sync_enabled = options.syncEnabled;
208
+ if (options.registryUrl !== undefined && options.registryUrl !== null)
209
+ prepared.registry_url = options.registryUrl;
210
+ if (options.registryAuth !== undefined && options.registryAuth !== null)
211
+ prepared.registry_auth = options.registryAuth;
212
+ return { config: prepared, gatewayToken };
213
+ }
214
+ function defaultOpenClawImage(image) {
215
+ if (image !== undefined && image !== null)
216
+ return image;
217
+ return DEFAULT_OPENCLAW_IMAGE;
218
+ }
219
+ export function buildOpenClawRoutes(options = {}) {
220
+ const routes = {};
221
+ if (options.includeGateway ?? true) {
222
+ routes.openclaw = {
223
+ port: options.gatewayPort ?? 18789,
224
+ auth: options.gatewayAuth ?? false,
225
+ prefix: options.gatewayPrefix ?? '',
226
+ };
227
+ }
228
+ if (options.includeDesktop ?? true) {
229
+ routes.desktop = {
230
+ port: options.desktopPort ?? 3000,
231
+ auth: options.desktopAuth ?? true,
232
+ prefix: options.desktopPrefix ?? 'desktop',
233
+ };
234
+ }
235
+ return routes;
236
+ }
237
+ async function getFsPromises() {
238
+ return import('node:fs/promises');
239
+ }
240
+ function bindAgent(agent, deployments) {
241
+ agent._deployments = deployments;
242
+ return agent;
243
+ }
244
+ export class Agent {
245
+ id;
246
+ userId;
247
+ podId;
248
+ podName;
249
+ state;
250
+ name;
251
+ cpu;
252
+ memory;
253
+ hostname;
254
+ tags;
255
+ jwtToken;
256
+ jwtExpiresAt;
257
+ startedAt;
258
+ stoppedAt;
259
+ lastError;
260
+ createdAt;
261
+ updatedAt;
262
+ launchConfig;
263
+ meta;
264
+ routes;
265
+ command;
266
+ entrypoint;
267
+ ports;
268
+ dryRun;
269
+ _deployments = null;
270
+ constructor(fields) {
271
+ this.id = fields.id;
272
+ this.userId = fields.userId;
273
+ this.podId = fields.podId;
274
+ this.podName = fields.podName;
275
+ this.state = fields.state;
276
+ this.name = fields.name ?? null;
277
+ this.cpu = fields.cpu;
278
+ this.memory = fields.memory;
279
+ this.hostname = fields.hostname ?? null;
280
+ this.tags = [...(fields.tags ?? [])];
281
+ this.jwtToken = fields.jwtToken ?? null;
282
+ this.jwtExpiresAt = fields.jwtExpiresAt ?? null;
283
+ this.startedAt = fields.startedAt ?? null;
284
+ this.stoppedAt = fields.stoppedAt ?? null;
285
+ this.lastError = fields.lastError ?? null;
286
+ this.createdAt = fields.createdAt ?? null;
287
+ this.updatedAt = fields.updatedAt ?? null;
288
+ this.launchConfig = fields.launchConfig ?? null;
289
+ this.meta = fields.meta ? structuredClone(fields.meta) : null;
290
+ this.routes = { ...fields.routes };
291
+ this.command = [...fields.command];
292
+ this.entrypoint = [...fields.entrypoint];
293
+ this.ports = [...fields.ports];
294
+ this.dryRun = fields.dryRun;
295
+ }
296
+ static fromDict(data) {
297
+ return new Agent(agentStateFromDict(data));
298
+ }
299
+ get publicUrl() {
300
+ return this.hostname ? `https://${this.hostname}` : null;
301
+ }
302
+ routePrefix(routeName, defaultPrefix = null) {
303
+ const route = this.routes[routeName] ?? {};
304
+ const prefix = route.prefix;
305
+ if (typeof prefix === 'undefined' || prefix === null) {
306
+ return defaultPrefix;
307
+ }
308
+ return String(prefix);
309
+ }
310
+ routeUrl(routeName, defaultPrefix = null) {
311
+ if (!this.hostname)
312
+ return null;
313
+ const prefix = this.routePrefix(routeName, defaultPrefix);
314
+ if (prefix === null)
315
+ return null;
316
+ return prefix === '' ? `https://${this.hostname}` : `https://${prefix}-${this.hostname}`;
317
+ }
318
+ get desktopUrl() {
319
+ return this.routeUrl('desktop', 'desktop');
320
+ }
321
+ get vncUrl() {
322
+ return this.desktopUrl;
323
+ }
324
+ get shellUrl() {
325
+ return this.routeUrl('shell');
326
+ }
327
+ get executorUrl() {
328
+ return this.shellUrl;
329
+ }
330
+ get isRunning() {
331
+ return this.state.toLowerCase() === 'running';
332
+ }
333
+ requireDeployments() {
334
+ if (!this._deployments) {
335
+ throw new Error('Agent is not bound to a Deployments client');
336
+ }
337
+ return this._deployments;
338
+ }
339
+ routeRequiresAuth(routeName, defaultValue = true) {
340
+ const route = this.routes[routeName];
341
+ if (!route || typeof route.auth === 'undefined') {
342
+ return defaultValue;
343
+ }
344
+ return Boolean(route.auth);
345
+ }
346
+ async refreshToken() {
347
+ const data = await this.requireDeployments().refreshToken(this.id);
348
+ this.jwtToken = data.token ?? data.jwt ?? null;
349
+ this.jwtExpiresAt = parseDate(data.expires_at);
350
+ return data;
351
+ }
352
+ async waitRunning(timeoutMs = 300_000, pollIntervalMs = 5_000) {
353
+ return this.requireDeployments().waitRunning(this.id, timeoutMs, pollIntervalMs);
354
+ }
355
+ async update(options) {
356
+ return this.requireDeployments().update(this.id, options);
357
+ }
358
+ async resize(options) {
359
+ return this.requireDeployments().resize(this.id, options);
360
+ }
361
+ async env() {
362
+ const data = await this.requireDeployments().env(this.id);
363
+ return data.env ?? {};
364
+ }
365
+ async exec(command, options = {}) {
366
+ return this.requireDeployments().exec(this, command, options);
367
+ }
368
+ async health() {
369
+ return this.requireDeployments().health(this);
370
+ }
371
+ async filesList(path = '') {
372
+ return this.requireDeployments().filesList(this, path);
373
+ }
374
+ async fileReadBytes(path) {
375
+ return this.requireDeployments().fileReadBytes(this, path);
376
+ }
377
+ async fileRead(path) {
378
+ return decodeUtf8(await this.fileReadBytes(path));
379
+ }
380
+ async fileWriteBytes(path, content) {
381
+ return this.requireDeployments().fileWriteBytes(this, path, content);
382
+ }
383
+ async fileWrite(path, content) {
384
+ return this.requireDeployments().fileWrite(this, path, content);
385
+ }
386
+ async fileDelete(path, options = {}) {
387
+ return this.requireDeployments().fileDelete(this, path, options);
388
+ }
389
+ async cpTo(localPath, remotePath) {
390
+ return this.requireDeployments().cpTo(this, localPath, remotePath);
391
+ }
392
+ async cpFrom(remotePath, localPath) {
393
+ return this.requireDeployments().cpFrom(this, remotePath, localPath);
394
+ }
395
+ async shellConnect(shell) {
396
+ return this.requireDeployments().shellConnect(this.id, shell);
397
+ }
398
+ }
399
+ export class OpenClawAgent extends Agent {
400
+ gatewayUrl;
401
+ gatewayToken;
402
+ constructor(fields) {
403
+ super(fields);
404
+ this.gatewayUrl = fields.gatewayUrl ?? null;
405
+ this.gatewayToken = fields.gatewayToken ?? null;
406
+ }
407
+ static fromDict(data) {
408
+ return new OpenClawAgent({
409
+ ...agentStateFromDict(data),
410
+ gatewayUrl: data.openclaw_url ?? data.gateway_url ?? (data.hostname ? `wss://${data.hostname}` : null),
411
+ gatewayToken: data.gateway_token ?? null,
412
+ });
413
+ }
414
+ /**
415
+ * Resolve the gateway token. If not set locally (e.g. page refresh),
416
+ * fetches from the pod's runtime env via the backend.
417
+ */
418
+ async resolveGatewayToken() {
419
+ if (this.gatewayToken)
420
+ return this.gatewayToken;
421
+ const tokenData = await this.requireDeployments().inferenceToken(this.id);
422
+ this.gatewayToken = tokenData.gateway_token ?? null;
423
+ this.gatewayUrl = tokenData.openclaw_url ?? this.gatewayUrl;
424
+ return this.gatewayToken;
425
+ }
426
+ gateway(options = {}) {
427
+ if (!this.gatewayUrl) {
428
+ throw new Error('Agent has no OpenClaw gateway URL');
429
+ }
430
+ const deployments = this.requireDeployments();
431
+ return new GatewayClient({
432
+ url: this.gatewayUrl,
433
+ token: undefined,
434
+ gatewayToken: options.gatewayToken ?? this.gatewayToken ?? undefined,
435
+ deploymentId: options.deploymentId ?? this.id,
436
+ apiKey: options.apiKey ?? deployments.agentApiKey,
437
+ apiBase: options.apiBase ?? deployments.agentApiBase,
438
+ autoApprovePairing: options.autoApprovePairing ?? true,
439
+ clientId: options.clientId,
440
+ clientMode: options.clientMode,
441
+ clientDisplayName: options.clientDisplayName,
442
+ clientVersion: options.clientVersion,
443
+ platform: options.platform,
444
+ instanceId: options.instanceId,
445
+ caps: options.caps,
446
+ origin: options.origin,
447
+ timeout: options.timeout,
448
+ onHello: options.onHello,
449
+ onClose: options.onClose,
450
+ onGap: options.onGap,
451
+ onPairing: options.onPairing,
452
+ });
453
+ }
454
+ async connect(options = {}) {
455
+ // Auto-resolve gateway token if missing
456
+ if (!this.gatewayToken && !options.gatewayToken) {
457
+ await this.resolveGatewayToken();
458
+ }
459
+ const client = this.gateway(options);
460
+ await client.connect();
461
+ return client;
462
+ }
463
+ async gatewayStatus(options = {}) {
464
+ const client = await this.connect(options);
465
+ try {
466
+ return await client.status();
467
+ }
468
+ finally {
469
+ client.close();
470
+ }
471
+ }
472
+ async waitReady(timeoutMs = 300_000, options = {}) {
473
+ const client = this.gateway(options);
474
+ try {
475
+ return await client.waitReady(timeoutMs, {
476
+ retryIntervalMs: options.retryIntervalMs,
477
+ probe: options.probe,
478
+ });
479
+ }
480
+ finally {
481
+ client.close();
482
+ }
483
+ }
484
+ async configGet(options = {}) {
485
+ const client = await this.connect(options);
486
+ try {
487
+ return await client.configGet();
488
+ }
489
+ finally {
490
+ client.close();
491
+ }
492
+ }
493
+ async configSchema(options = {}) {
494
+ const client = await this.connect(options);
495
+ try {
496
+ return await client.configSchema();
497
+ }
498
+ finally {
499
+ client.close();
500
+ }
501
+ }
502
+ async configPatch(patch, options = {}) {
503
+ const client = await this.connect(options);
504
+ try {
505
+ await client.configPatch(patch);
506
+ }
507
+ finally {
508
+ client.close();
509
+ }
510
+ }
511
+ async configApply(config, options = {}) {
512
+ const client = await this.connect(options);
513
+ try {
514
+ await client.configApply(config);
515
+ }
516
+ finally {
517
+ client.close();
518
+ }
519
+ }
520
+ async modelsList(options = {}) {
521
+ const client = await this.connect(options);
522
+ try {
523
+ return await client.modelsList();
524
+ }
525
+ finally {
526
+ client.close();
527
+ }
528
+ }
529
+ async sessionsList(options = {}) {
530
+ const client = await this.connect(options);
531
+ try {
532
+ return await client.sessionsList();
533
+ }
534
+ finally {
535
+ client.close();
536
+ }
537
+ }
538
+ async *chatSend(message, sessionKey, options = {}) {
539
+ const client = await this.connect(options);
540
+ try {
541
+ for await (const event of client.chatSend(message, sessionKey, options.attachments)) {
542
+ yield event;
543
+ }
544
+ }
545
+ finally {
546
+ client.close();
547
+ }
548
+ }
549
+ async channelsStatus(options = {}) {
550
+ const client = await this.connect(options);
551
+ try {
552
+ return await client.channelsStatus(options.probe ?? false, options.timeoutMs);
553
+ }
554
+ finally {
555
+ client.close();
556
+ }
557
+ }
558
+ async channelsLogout(channel, accountId, options = {}) {
559
+ const client = await this.connect(options);
560
+ try {
561
+ return await client.channelsLogout(channel, accountId);
562
+ }
563
+ finally {
564
+ client.close();
565
+ }
566
+ }
567
+ async webLoginStart(options = {}) {
568
+ const client = await this.connect(options);
569
+ try {
570
+ return await client.webLoginStart({
571
+ force: options.force,
572
+ timeoutMs: options.timeoutMs,
573
+ verbose: options.verbose,
574
+ accountId: options.accountId,
575
+ });
576
+ }
577
+ finally {
578
+ client.close();
579
+ }
580
+ }
581
+ async webLoginWait(options = {}) {
582
+ const client = await this.connect(options);
583
+ try {
584
+ return await client.webLoginWait({
585
+ timeoutMs: options.timeoutMs,
586
+ accountId: options.accountId,
587
+ });
588
+ }
589
+ finally {
590
+ client.close();
591
+ }
592
+ }
593
+ async workspaceFiles(options = {}) {
594
+ const client = await this.connect(options);
595
+ try {
596
+ const agents = await client.agentsList();
597
+ const agentId = agents[0]?.id ?? 'main';
598
+ const files = await client.filesList(agentId);
599
+ return { agentId, files };
600
+ }
601
+ finally {
602
+ client.close();
603
+ }
604
+ }
605
+ async fileGet(name, agentId, options = {}) {
606
+ const client = await this.connect(options);
607
+ try {
608
+ let resolvedAgentId;
609
+ if (agentId) {
610
+ resolvedAgentId = agentId;
611
+ }
612
+ else {
613
+ const agents = await client.agentsList();
614
+ resolvedAgentId = agents[0]?.id ?? 'main';
615
+ }
616
+ return await client.fileGet(resolvedAgentId, name);
617
+ }
618
+ finally {
619
+ client.close();
620
+ }
621
+ }
622
+ async fileSet(name, content, agentId, options = {}) {
623
+ const client = await this.connect(options);
624
+ try {
625
+ let resolvedAgentId;
626
+ if (agentId) {
627
+ resolvedAgentId = agentId;
628
+ }
629
+ else {
630
+ const agents = await client.agentsList();
631
+ resolvedAgentId = agents[0]?.id ?? 'main';
632
+ }
633
+ await client.fileSet(resolvedAgentId, name, content);
634
+ }
635
+ finally {
636
+ client.close();
637
+ }
638
+ }
639
+ async chatHistory(sessionKey, limit = 50, options = {}) {
640
+ const client = await this.connect(options);
641
+ try {
642
+ return await client.chatHistory(sessionKey, limit);
643
+ }
644
+ finally {
645
+ client.close();
646
+ }
647
+ }
648
+ async chatSendMessage(message, options = {}) {
649
+ const client = await this.connect(options);
650
+ try {
651
+ return await client.sendChat(message, options.sessionKey ?? 'main', options.agentId, options.attachments);
652
+ }
653
+ finally {
654
+ client.close();
655
+ }
656
+ }
657
+ async mutateConfig(mutator, options = {}) {
658
+ const config = structuredClone(await this.configGet(options));
659
+ await mutator(config);
660
+ await this.configApply(config, options);
661
+ return config;
662
+ }
663
+ async providerUpsert(providerId, providerConfig, gatewayOptions = {}) {
664
+ const { api, baseUrl, apiKey, models, ...extra } = providerConfig;
665
+ const config = await this.mutateConfig((next) => {
666
+ const modelsCfg = (next.models ??= {});
667
+ const providers = (modelsCfg.providers ??= {});
668
+ const provider = { ...(providers[providerId] ?? {}) };
669
+ provider.api = api;
670
+ provider.baseUrl = baseUrl;
671
+ if (apiKey !== undefined)
672
+ provider.apiKey = apiKey;
673
+ if (models !== undefined)
674
+ provider.models = structuredClone(models);
675
+ Object.assign(provider, extra);
676
+ providers[providerId] = provider;
677
+ }, gatewayOptions);
678
+ return config.models?.providers?.[providerId] ?? {};
679
+ }
680
+ async providerRemove(providerId, options = {}) {
681
+ const config = await this.mutateConfig((next) => {
682
+ if (next.models?.providers) {
683
+ delete next.models.providers[providerId];
684
+ }
685
+ }, options);
686
+ return config.models?.providers ?? {};
687
+ }
688
+ async modelUpsert(providerId, modelId, modelConfig = {}, gatewayOptions = {}) {
689
+ const config = await this.mutateConfig((next) => {
690
+ const providers = ((next.models ??= {}).providers ??= {});
691
+ const provider = { ...(providers[providerId] ?? {}) };
692
+ const models = Array.isArray(provider.models)
693
+ ? provider.models.map((entry) => ({ ...entry }))
694
+ : [];
695
+ let model = models.find((entry) => entry.id === modelId);
696
+ if (!model) {
697
+ model = { id: modelId };
698
+ models.push(model);
699
+ }
700
+ Object.assign(model, modelConfig);
701
+ provider.models = models;
702
+ providers[providerId] = provider;
703
+ }, gatewayOptions);
704
+ return (config.models?.providers?.[providerId]?.models?.find((entry) => entry.id === modelId) ??
705
+ {});
706
+ }
707
+ async modelRemove(providerId, modelId, options = {}) {
708
+ const config = await this.mutateConfig((next) => {
709
+ const providers = ((next.models ??= {}).providers ??= {});
710
+ const provider = { ...(providers[providerId] ?? {}) };
711
+ provider.models = Array.isArray(provider.models)
712
+ ? provider.models.filter((entry) => entry.id !== modelId)
713
+ : [];
714
+ providers[providerId] = provider;
715
+ }, options);
716
+ return config.models?.providers?.[providerId]?.models ?? [];
717
+ }
718
+ async setDefaultModel(providerId, modelId, options = {}) {
719
+ const primary = `${providerId}/${modelId}`;
720
+ await this.mutateConfig((next) => {
721
+ const defaults = ((next.agents ??= {}).defaults ??= {});
722
+ const model = (defaults.model ??= {});
723
+ model.primary = primary;
724
+ }, options);
725
+ return primary;
726
+ }
727
+ async setMemorySearch(memorySearchConfig, gatewayOptions = {}) {
728
+ const { provider, model, baseUrl, apiKey, ...extra } = memorySearchConfig;
729
+ const config = await this.mutateConfig((next) => {
730
+ const defaults = ((next.agents ??= {}).defaults ??= {});
731
+ const memorySearch = { ...(defaults.memorySearch ?? {}) };
732
+ memorySearch.provider = provider;
733
+ memorySearch.model = model;
734
+ const remote = { ...(memorySearch.remote ?? {}) };
735
+ if (baseUrl !== undefined)
736
+ remote.baseUrl = baseUrl;
737
+ if (apiKey !== undefined)
738
+ remote.apiKey = apiKey;
739
+ if (Object.keys(remote).length > 0)
740
+ memorySearch.remote = remote;
741
+ Object.assign(memorySearch, extra);
742
+ defaults.memorySearch = memorySearch;
743
+ }, gatewayOptions);
744
+ return config.agents?.defaults?.memorySearch ?? {};
745
+ }
746
+ async channelUpsert(channelId, channelConfig, options = {}) {
747
+ const { accountId, ...gatewayOptions } = options;
748
+ const config = await this.mutateConfig((next) => {
749
+ const channels = (next.channels ??= {});
750
+ const current = channels[channelId] && typeof channels[channelId] === 'object'
751
+ ? structuredClone(channels[channelId])
752
+ : {};
753
+ if (accountId) {
754
+ const accounts = current.accounts && typeof current.accounts === 'object'
755
+ ? structuredClone(current.accounts)
756
+ : {};
757
+ const currentAccount = accounts[accountId] && typeof accounts[accountId] === 'object'
758
+ ? accounts[accountId]
759
+ : {};
760
+ accounts[accountId] = deepMergeConfig(currentAccount, channelConfig);
761
+ current.accounts = accounts;
762
+ channels[channelId] = current;
763
+ return;
764
+ }
765
+ channels[channelId] = deepMergeConfig(current, channelConfig);
766
+ }, gatewayOptions);
767
+ const channel = config.channels?.[channelId] ?? {};
768
+ if (accountId) {
769
+ return channel.accounts?.[accountId] ?? {};
770
+ }
771
+ return channel;
772
+ }
773
+ async channelPatch(channelId, patch, options = {}) {
774
+ return await this.channelUpsert(channelId, patch, options);
775
+ }
776
+ async telegramUpsert(channelConfig, options = {}) {
777
+ return await this.channelUpsert('telegram', channelConfig, options);
778
+ }
779
+ async slackUpsert(channelConfig, options = {}) {
780
+ return await this.channelUpsert('slack', channelConfig, options);
781
+ }
782
+ async discordUpsert(channelConfig, options = {}) {
783
+ return await this.channelUpsert('discord', channelConfig, options);
784
+ }
785
+ async cronList(options = {}) {
786
+ const client = await this.connect(options);
787
+ try {
788
+ return await client.cronList();
789
+ }
790
+ finally {
791
+ client.close();
792
+ }
793
+ }
794
+ }
795
+ export class Deployments {
796
+ http;
797
+ apiKey;
798
+ apiBase;
799
+ agentsWsUrl;
800
+ agentHttp;
801
+ constructor(http, agentApiKey, agentApiBase, agentsWsUrl) {
802
+ this.http = http;
803
+ this.apiKey = agentApiKey || http.apiKey;
804
+ this.apiBase = resolveAgentsApiBase(agentApiBase || getAgentsApiBaseUrl());
805
+ this.agentsWsUrl = normalizeAgentsWsUrl(agentsWsUrl || getConfigValue('AGENTS_WS_URL') || defaultAgentsWsUrl(this.apiBase));
806
+ this.agentHttp = http instanceof HTTPClient ? new HTTPClient(this.apiBase, this.apiKey) : http;
807
+ }
808
+ get agentApiKey() {
809
+ return this.apiKey;
810
+ }
811
+ get agentApiBase() {
812
+ return this.apiBase;
813
+ }
814
+ hydrateAgent(data) {
815
+ const agent = data.openclaw_url || data.gateway_url
816
+ ? OpenClawAgent.fromDict(data)
817
+ : Agent.fromDict(data);
818
+ return bindAgent(agent, this);
819
+ }
820
+ agentIdFor(target) {
821
+ return typeof target === 'string' ? target : target.id;
822
+ }
823
+ async fetchRaw(path, init = {}) {
824
+ const headers = new Headers(init.headers ?? {});
825
+ headers.set('Authorization', `Bearer ${this.apiKey}`);
826
+ const contentType = headers.get('Content-Type');
827
+ const body = init.body && contentType?.includes('application/json') && typeof init.body !== 'string'
828
+ ? JSON.stringify(init.body)
829
+ : init.body;
830
+ const response = await fetch(`${this.apiBase}${path}`, {
831
+ ...init,
832
+ headers,
833
+ body,
834
+ });
835
+ if (!response.ok) {
836
+ let detail = response.statusText;
837
+ try {
838
+ const payload = await response.clone().json();
839
+ detail = typeof payload.detail === 'string' ? payload.detail : response.statusText;
840
+ }
841
+ catch {
842
+ const text = await response.text();
843
+ detail = text || response.statusText;
844
+ }
845
+ throw new APIError(response.status, detail);
846
+ }
847
+ return response;
848
+ }
849
+ async create(options = {}) {
850
+ const { config, gatewayToken } = buildAgentConfig(options.config ?? {}, options);
851
+ const body = { ...config, start: options.start ?? true };
852
+ if (options.dryRun)
853
+ body.dry_run = true;
854
+ if (options.name)
855
+ body.name = options.name;
856
+ if (options.size)
857
+ body.size = options.size;
858
+ if (options.meta?.ui)
859
+ body.meta = { ui: structuredClone(options.meta.ui) };
860
+ if (options.tags?.length)
861
+ body.tags = [...options.tags];
862
+ const data = await this.agentHttp.post(DEPLOYMENTS_API_PREFIX, body);
863
+ const agent = this.hydrateAgent(data);
864
+ if (agent instanceof OpenClawAgent) {
865
+ agent.gatewayToken = gatewayToken;
866
+ }
867
+ agent.launchConfig = config;
868
+ agent.command = [...(config.command ?? [])];
869
+ agent.entrypoint = [...(config.entrypoint ?? [])];
870
+ return agent;
871
+ }
872
+ async createOpenClaw(options = {}) {
873
+ const effectiveOptions = { ...options };
874
+ effectiveOptions.env = { ...(options.env ?? {}) };
875
+ if (options.routes === undefined) {
876
+ effectiveOptions.routes = buildOpenClawRoutes(options.openClawRoutes ?? {});
877
+ }
878
+ effectiveOptions.image = defaultOpenClawImage(options.image);
879
+ if (effectiveOptions.syncRoot === undefined)
880
+ effectiveOptions.syncRoot = DEFAULT_OPENCLAW_SYNC_ROOT;
881
+ if (effectiveOptions.syncEnabled === undefined)
882
+ effectiveOptions.syncEnabled = true;
883
+ return this.create(effectiveOptions);
884
+ }
885
+ async budget() {
886
+ return this.agentHttp.get(`${DEPLOYMENTS_API_PREFIX}/budget`);
887
+ }
888
+ async metrics(agentId) {
889
+ return this.agentHttp.get(`${DEPLOYMENTS_API_PREFIX}/${agentId}/metrics`);
890
+ }
891
+ async list() {
892
+ const data = await this.agentHttp.get(DEPLOYMENTS_API_PREFIX);
893
+ const items = Array.isArray(data) ? data : data.items ?? [];
894
+ return items.map((item) => this.hydrateAgent(item));
895
+ }
896
+ async get(agentId) {
897
+ const data = await this.agentHttp.get(`${DEPLOYMENTS_API_PREFIX}/${agentId}`);
898
+ return this.hydrateAgent(data);
899
+ }
900
+ async waitRunning(agentId, timeoutMs = 300_000, pollIntervalMs = 5_000) {
901
+ const deadline = Date.now() + timeoutMs;
902
+ let lastState = '';
903
+ while (Date.now() < deadline) {
904
+ const agent = await this.get(agentId);
905
+ lastState = String(agent.state || '');
906
+ if (lastState.toLowerCase() === 'running') {
907
+ return agent;
908
+ }
909
+ if (lastState.toLowerCase() === 'failed' || lastState.toLowerCase() === 'error') {
910
+ throw new Error(`Agent entered ${lastState} while waiting for RUNNING`);
911
+ }
912
+ await sleep(pollIntervalMs);
913
+ }
914
+ throw new Error(`Timed out waiting for agent ${agentId} to reach RUNNING (last=${lastState})`);
915
+ }
916
+ async start(agentId, options = {}) {
917
+ const { config, gatewayToken } = buildAgentConfig(options.config ?? {}, options);
918
+ const body = { ...config };
919
+ if (options.dryRun)
920
+ body.dry_run = true;
921
+ const data = await this.agentHttp.post(`${DEPLOYMENTS_API_PREFIX}/${agentId}/start`, body);
922
+ const agent = this.hydrateAgent(data);
923
+ if (agent instanceof OpenClawAgent) {
924
+ agent.gatewayToken = gatewayToken;
925
+ }
926
+ agent.launchConfig = config;
927
+ agent.command = [...(config.command ?? [])];
928
+ agent.entrypoint = [...(config.entrypoint ?? [])];
929
+ return agent;
930
+ }
931
+ async startOpenClaw(agentId, options = {}) {
932
+ const effectiveOptions = { ...options };
933
+ effectiveOptions.env = { ...(options.env ?? {}) };
934
+ if (options.routes === undefined) {
935
+ effectiveOptions.routes = buildOpenClawRoutes(options.openClawRoutes ?? {});
936
+ }
937
+ effectiveOptions.image = defaultOpenClawImage(options.image);
938
+ if (effectiveOptions.syncRoot === undefined)
939
+ effectiveOptions.syncRoot = DEFAULT_OPENCLAW_SYNC_ROOT;
940
+ if (effectiveOptions.syncEnabled === undefined)
941
+ effectiveOptions.syncEnabled = true;
942
+ return this.start(agentId, effectiveOptions);
943
+ }
944
+ async update(agentId, options = {}) {
945
+ const body = {};
946
+ if (options.name !== undefined)
947
+ body.name = options.name;
948
+ if (options.size !== undefined)
949
+ body.size = options.size;
950
+ if (options.refreshFromLagoon !== undefined)
951
+ body.refresh_from_lagoon = options.refreshFromLagoon;
952
+ if (options.lastError !== undefined)
953
+ body.last_error = options.lastError;
954
+ const data = await this.agentHttp.patch(`${DEPLOYMENTS_API_PREFIX}/${agentId}`, body);
955
+ return this.hydrateAgent(data);
956
+ }
957
+ async resize(agentId, options) {
958
+ return this.update(agentId, options);
959
+ }
960
+ async stop(agentId) {
961
+ const data = await this.agentHttp.post(`${DEPLOYMENTS_API_PREFIX}/${agentId}/stop`);
962
+ return this.hydrateAgent(data);
963
+ }
964
+ async delete(agentId) {
965
+ return this.agentHttp.delete(`${DEPLOYMENTS_API_PREFIX}/${agentId}`);
966
+ }
967
+ async refreshToken(agentId) {
968
+ return this.agentHttp.get(`${DEPLOYMENTS_API_PREFIX}/${agentId}/token`);
969
+ }
970
+ async inferenceToken(agentId) {
971
+ return this.agentHttp.get(`${DEPLOYMENTS_API_PREFIX}/${agentId}/inference/token`);
972
+ }
973
+ async createScopedKey(agentId, name) {
974
+ const payload = {};
975
+ if (name)
976
+ payload.name = name;
977
+ return this.agentHttp.post(`${DEPLOYMENTS_API_PREFIX}/${agentId}/keys`, Object.keys(payload).length ? payload : undefined);
978
+ }
979
+ async logsToken(agentId) {
980
+ return this.agentHttp.post(`${DEPLOYMENTS_API_PREFIX}/${agentId}/logs/token`);
981
+ }
982
+ async env(agentId) {
983
+ return this.agentHttp.get(`${DEPLOYMENTS_API_PREFIX}/${agentId}/env`);
984
+ }
985
+ async exec(target, command, options = {}) {
986
+ const agentId = this.agentIdFor(target);
987
+ const payload = {
988
+ command,
989
+ timeout: options.timeout ?? 30,
990
+ };
991
+ if (options.dryRun)
992
+ payload.dry_run = true;
993
+ const data = await this.agentHttp.post(`${DEPLOYMENTS_API_PREFIX}/${agentId}/exec`, payload);
994
+ return execResultFromDict(data);
995
+ }
996
+ async health(target) {
997
+ if (!target.executorUrl) {
998
+ throw new Error('Agent has no executor URL');
999
+ }
1000
+ const headers = {};
1001
+ if (target.jwtToken) {
1002
+ headers.Authorization = `Bearer ${target.jwtToken}`;
1003
+ headers.Cookie = `${target.podName}-token=${target.jwtToken}`;
1004
+ }
1005
+ const response = await fetch(`${target.executorUrl}/health`, { headers });
1006
+ if (!response.ok) {
1007
+ throw new Error(`Agent health failed: ${response.status} ${response.statusText}`);
1008
+ }
1009
+ return (await response.json());
1010
+ }
1011
+ async filesList(target, path = '') {
1012
+ const encodedPath = encodeFilePath(path);
1013
+ const suffix = encodedPath ? `/${encodedPath}` : '';
1014
+ const response = await this.fetchRaw(`${DEPLOYMENTS_API_PREFIX}/${this.agentIdFor(target)}/files${suffix}`);
1015
+ const payload = (await response.json());
1016
+ return [...(payload.directories ?? []), ...(payload.files ?? [])];
1017
+ }
1018
+ async fileReadBytes(target, path) {
1019
+ const encodedPath = encodeFilePath(path);
1020
+ const response = await this.fetchRaw(`${DEPLOYMENTS_API_PREFIX}/${this.agentIdFor(target)}/files/${encodedPath}`, {
1021
+ redirect: 'follow',
1022
+ });
1023
+ const bytes = new Uint8Array(await response.arrayBuffer());
1024
+ const contentType = response.headers.get('content-type') || '';
1025
+ if (contentType.includes('application/json')) {
1026
+ try {
1027
+ const payload = JSON.parse(decodeUtf8(bytes));
1028
+ if (isDirectoryListingPayload(payload)) {
1029
+ throw new Error(`Path is a directory: ${path}. Use filesList(path) instead.`);
1030
+ }
1031
+ }
1032
+ catch (error) {
1033
+ if (error instanceof Error && error.message.startsWith('Path is a directory:')) {
1034
+ throw error;
1035
+ }
1036
+ }
1037
+ }
1038
+ return bytes;
1039
+ }
1040
+ async fileRead(target, path) {
1041
+ return decodeUtf8(await this.fileReadBytes(target, path));
1042
+ }
1043
+ async fileWriteBytes(target, path, content) {
1044
+ const response = await this.fetchRaw(`${DEPLOYMENTS_API_PREFIX}/${this.agentIdFor(target)}/files/${encodeFilePath(path)}`, {
1045
+ method: 'PUT',
1046
+ headers: { 'Content-Type': 'application/octet-stream' },
1047
+ body: toUint8Array(content),
1048
+ });
1049
+ return (await response.json());
1050
+ }
1051
+ async fileWrite(target, path, content) {
1052
+ return this.fileWriteBytes(target, path, content);
1053
+ }
1054
+ async fileDelete(target, path, options = {}) {
1055
+ const encodedPath = encodeFilePath(path);
1056
+ const params = new URLSearchParams();
1057
+ if (options.recursive)
1058
+ params.set('recursive', 'true');
1059
+ const suffix = params.toString() ? `?${params.toString()}` : '';
1060
+ const response = await this.fetchRaw(`${DEPLOYMENTS_API_PREFIX}/${this.agentIdFor(target)}/files/${encodedPath}${suffix}`, { method: 'DELETE' });
1061
+ return (await response.json());
1062
+ }
1063
+ async cpTo(target, localPath, remotePath) {
1064
+ const fs = await getFsPromises();
1065
+ const content = await fs.readFile(localPath);
1066
+ return this.fileWriteBytes(target, remotePath, new Uint8Array(content));
1067
+ }
1068
+ async cpFrom(target, remotePath, localPath) {
1069
+ const fs = await getFsPromises();
1070
+ const content = await this.fileReadBytes(target, remotePath);
1071
+ const destination = new URL(`file://${localPath}`).pathname;
1072
+ const parts = destination.split('/');
1073
+ parts.pop();
1074
+ const parent = parts.join('/') || '/';
1075
+ await fs.mkdir(parent, { recursive: true });
1076
+ await fs.writeFile(destination, content);
1077
+ return destination;
1078
+ }
1079
+ async logsConnect(agentId, options = {}) {
1080
+ const tokenData = await this.logsToken(agentId);
1081
+ const container = options.container ?? 'reef';
1082
+ const tailLines = options.tailLines ?? 100;
1083
+ const wsUrl = `${this.agentsWsUrl}/logs/${agentId}` +
1084
+ `?jwt=${encodeURIComponent(tokenData.jwt)}` +
1085
+ `&container=${encodeURIComponent(container)}` +
1086
+ `&tail_lines=${encodeURIComponent(String(tailLines))}`;
1087
+ const ws = new WebSocket(wsUrl);
1088
+ return await new Promise((resolve, reject) => {
1089
+ let settled = false;
1090
+ ws.onopen = () => {
1091
+ settled = true;
1092
+ resolve(ws);
1093
+ };
1094
+ ws.onerror = () => {
1095
+ if (!settled) {
1096
+ reject(new Error('WebSocket connection failed'));
1097
+ }
1098
+ };
1099
+ });
1100
+ }
1101
+ async shellToken(agentId, shell, dryRun = false) {
1102
+ const selectedShell = shell ?? '/bin/bash';
1103
+ const payload = { shell: selectedShell };
1104
+ if (dryRun)
1105
+ payload.dry_run = true;
1106
+ return this.agentHttp.post(`${DEPLOYMENTS_API_PREFIX}/${agentId}/shell/token`, payload);
1107
+ }
1108
+ async shellConnect(agentId, shell) {
1109
+ const connectWithShell = async (requestedShell) => {
1110
+ const tokenData = await this.shellToken(agentId, requestedShell);
1111
+ const baseUrl = `${this.agentsWsUrl}/shell/${agentId}`;
1112
+ const separator = baseUrl.includes("?") ? "&" : "?";
1113
+ const wsUrl = `${baseUrl}${separator}jwt=${encodeURIComponent(tokenData.jwt)}` +
1114
+ `&shell=${encodeURIComponent(tokenData.shell || requestedShell)}`;
1115
+ const ws = new WebSocket(wsUrl);
1116
+ return await new Promise((resolve, reject) => {
1117
+ let settled = false;
1118
+ ws.onopen = () => {
1119
+ settled = true;
1120
+ resolve(ws);
1121
+ };
1122
+ ws.onerror = () => {
1123
+ if (!settled) {
1124
+ reject(new Error('WebSocket connection failed'));
1125
+ }
1126
+ };
1127
+ });
1128
+ };
1129
+ if (shell) {
1130
+ return connectWithShell(shell);
1131
+ }
1132
+ try {
1133
+ return await connectWithShell('/bin/bash');
1134
+ }
1135
+ catch {
1136
+ return connectWithShell('/bin/sh');
1137
+ }
1138
+ }
1139
+ }
1140
+ //# sourceMappingURL=agents.js.map