@hypercli.com/sdk 2026.3.13-2 → 2026.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/agents.js CHANGED
@@ -1,14 +1,39 @@
1
+ /**
2
+ * HyperClaw agents API - typed agent lifecycle, files, exec, and OpenClaw access.
3
+ */
4
+ import { getAgentsApiBaseUrl, getConfigValue } from './config.js';
1
5
  import { GatewayClient, } from './gateway.js';
2
6
  const AGENTS_API_BASE = 'https://api.hypercli.com/agents';
3
7
  const DEV_AGENTS_API_BASE = 'https://api.dev.hypercli.com/agents';
4
8
  const DEPLOYMENTS_API_PREFIX = '/deployments';
5
9
  const AGENTS_WS_URL = 'wss://api.agents.hypercli.com/ws';
6
10
  const DEV_AGENTS_WS_URL = 'wss://api.agents.dev.hypercli.com/ws';
11
+ export const DEFAULT_OPENCLAW_IMAGE = 'ghcr.io/hypercli/hypercli-openclaw:prod';
7
12
  function parseDate(value) {
8
13
  if (typeof value !== 'string' || !value)
9
14
  return null;
10
15
  return new Date(value.replace('Z', '+00:00'));
11
16
  }
17
+ function deepMergeConfig(base, patch) {
18
+ const merged = structuredClone(base);
19
+ for (const [key, value] of Object.entries(patch)) {
20
+ if (value &&
21
+ typeof value === 'object' &&
22
+ !Array.isArray(value) &&
23
+ merged[key] &&
24
+ typeof merged[key] === 'object' &&
25
+ !Array.isArray(merged[key])) {
26
+ merged[key] = deepMergeConfig(merged[key], value);
27
+ }
28
+ else {
29
+ merged[key] = structuredClone(value);
30
+ }
31
+ }
32
+ return merged;
33
+ }
34
+ function sleep(ms) {
35
+ return new Promise((resolve) => setTimeout(resolve, ms));
36
+ }
12
37
  function toWsBaseUrl(baseUrl) {
13
38
  const base = (baseUrl || '').replace(/\/+$/, '');
14
39
  if (!base)
@@ -30,21 +55,42 @@ export function resolveAgentsApiBase(apiBase) {
30
55
  if (!raw)
31
56
  return AGENTS_API_BASE;
32
57
  const parsed = new URL(raw.includes('://') ? raw : `https://${raw}`);
58
+ const normalizedPath = parsed.pathname.replace(/\/+$/, '');
33
59
  const host = parsed.host.toLowerCase();
34
- if (host === 'api.hypercli.com' || host === 'api.hyperclaw.app')
60
+ if (normalizedPath.endsWith('/agents')) {
61
+ return `${parsed.origin}${normalizedPath}`;
62
+ }
63
+ if (normalizedPath.endsWith('/api')) {
64
+ if (host === 'api.agents.hypercli.com') {
65
+ return AGENTS_API_BASE;
66
+ }
67
+ if (host === 'api.agents.dev.hypercli.com') {
68
+ return DEV_AGENTS_API_BASE;
69
+ }
70
+ return `${parsed.origin}${normalizedPath.slice(0, -4)}/agents`;
71
+ }
72
+ if (host === 'api.agents.hypercli.com' || host === 'api.hypercli.com' || host === 'api.hyperclaw.app') {
35
73
  return AGENTS_API_BASE;
36
- if (host === 'api.dev.hypercli.com' || host === 'api.dev.hyperclaw.app' || host === 'dev-api.hyperclaw.app') {
74
+ }
75
+ if (host === 'api.agents.dev.hypercli.com' ||
76
+ host === 'api.dev.hypercli.com' ||
77
+ host === 'api.dev.hyperclaw.app' ||
78
+ host === 'dev-api.hyperclaw.app') {
37
79
  return DEV_AGENTS_API_BASE;
38
80
  }
39
- return raw.replace(/\/$/, '');
81
+ const normalized = raw.replace(/\/$/, '');
82
+ return `${normalized}/agents`;
40
83
  }
41
84
  function defaultAgentsWsUrl(apiBase) {
42
85
  const resolvedApiBase = resolveAgentsApiBase(apiBase);
43
86
  const parsed = new URL(resolvedApiBase.includes('://') ? resolvedApiBase : `https://${resolvedApiBase}`);
44
87
  const host = parsed.host.toLowerCase();
45
- if (host === 'api.hypercli.com' || host === 'api.hyperclaw.app')
88
+ if (host === 'api.agents.hypercli.com' || host === 'api.hypercli.com' || host === 'api.hyperclaw.app')
46
89
  return AGENTS_WS_URL;
47
- if (host === 'api.dev.hypercli.com' || host === 'api.dev.hyperclaw.app' || host === 'dev-api.hyperclaw.app') {
90
+ if (host === 'api.agents.dev.hypercli.com' ||
91
+ host === 'api.dev.hypercli.com' ||
92
+ host === 'api.dev.hyperclaw.app' ||
93
+ host === 'dev-api.hyperclaw.app') {
48
94
  return DEV_AGENTS_WS_URL;
49
95
  }
50
96
  return normalizeAgentsWsUrl(resolvedApiBase);
@@ -135,6 +181,30 @@ export function buildAgentConfig(config = {}, options = {}) {
135
181
  prepared.registry_auth = options.registryAuth;
136
182
  return { config: prepared, gatewayToken };
137
183
  }
184
+ function defaultOpenClawImage(image, config) {
185
+ if (image !== undefined && image !== null)
186
+ return image;
187
+ const configured = String(config?.image ?? '').trim();
188
+ return configured || DEFAULT_OPENCLAW_IMAGE;
189
+ }
190
+ export function buildOpenClawRoutes(options = {}) {
191
+ const routes = {};
192
+ if (options.includeGateway ?? true) {
193
+ routes.openclaw = {
194
+ port: options.gatewayPort ?? 18789,
195
+ auth: options.gatewayAuth ?? false,
196
+ prefix: options.gatewayPrefix ?? '',
197
+ };
198
+ }
199
+ if (options.includeDesktop ?? true) {
200
+ routes.desktop = {
201
+ port: options.desktopPort ?? 3000,
202
+ auth: options.desktopAuth ?? true,
203
+ prefix: options.desktopPrefix ?? 'desktop',
204
+ };
205
+ }
206
+ return routes;
207
+ }
138
208
  async function getFsPromises() {
139
209
  return import('node:fs/promises');
140
210
  }
@@ -196,11 +266,30 @@ export class Agent {
196
266
  get publicUrl() {
197
267
  return this.hostname ? `https://${this.hostname}` : null;
198
268
  }
269
+ routePrefix(routeName, defaultPrefix = null) {
270
+ const route = this.routes[routeName] ?? {};
271
+ const prefix = route.prefix;
272
+ if (typeof prefix === 'undefined' || prefix === null) {
273
+ return defaultPrefix;
274
+ }
275
+ return String(prefix);
276
+ }
277
+ routeUrl(routeName, defaultPrefix = null) {
278
+ if (!this.hostname)
279
+ return null;
280
+ const prefix = this.routePrefix(routeName, defaultPrefix);
281
+ if (prefix === null)
282
+ return null;
283
+ return prefix === '' ? `https://${this.hostname}` : `https://${prefix}-${this.hostname}`;
284
+ }
285
+ get desktopUrl() {
286
+ return this.routeUrl('desktop', 'desktop');
287
+ }
199
288
  get vncUrl() {
200
- return this.publicUrl;
289
+ return this.desktopUrl;
201
290
  }
202
291
  get shellUrl() {
203
- return this.hostname ? `https://shell-${this.hostname}` : null;
292
+ return this.routeUrl('shell');
204
293
  }
205
294
  get executorUrl() {
206
295
  return this.shellUrl;
@@ -214,12 +303,22 @@ export class Agent {
214
303
  }
215
304
  return this._deployments;
216
305
  }
306
+ routeRequiresAuth(routeName, defaultValue = true) {
307
+ const route = this.routes[routeName];
308
+ if (!route || typeof route.auth === 'undefined') {
309
+ return defaultValue;
310
+ }
311
+ return Boolean(route.auth);
312
+ }
217
313
  async refreshToken() {
218
314
  const data = await this.requireDeployments().refreshToken(this.id);
219
315
  this.jwtToken = data.token ?? data.jwt ?? null;
220
316
  this.jwtExpiresAt = parseDate(data.expires_at);
221
317
  return data;
222
318
  }
319
+ async waitRunning(timeoutMs = 300_000, pollIntervalMs = 5_000) {
320
+ return this.requireDeployments().waitRunning(this.id, timeoutMs, pollIntervalMs);
321
+ }
223
322
  async env() {
224
323
  const data = await this.requireDeployments().env(this.id);
225
324
  return data.env ?? {};
@@ -269,7 +368,7 @@ export class OpenClawAgent extends Agent {
269
368
  static fromDict(data) {
270
369
  return new OpenClawAgent({
271
370
  ...agentStateFromDict(data),
272
- gatewayUrl: data.openclaw_url ?? data.gateway_url ?? null,
371
+ gatewayUrl: data.openclaw_url ?? data.gateway_url ?? (data.hostname ? `wss://${data.hostname}` : null),
273
372
  gatewayToken: data.gateway_token ?? null,
274
373
  });
275
374
  }
@@ -288,17 +387,28 @@ export class OpenClawAgent extends Agent {
288
387
  if (!this.gatewayUrl) {
289
388
  throw new Error('Agent has no OpenClaw gateway URL');
290
389
  }
291
- if (!this.jwtToken) {
292
- throw new Error('Agent has no JWT token');
293
- }
390
+ const deployments = this.requireDeployments();
294
391
  return new GatewayClient({
295
392
  url: this.gatewayUrl,
296
- token: this.jwtToken,
393
+ token: undefined,
297
394
  gatewayToken: options.gatewayToken ?? this.gatewayToken ?? undefined,
395
+ deploymentId: options.deploymentId ?? this.id,
396
+ apiKey: options.apiKey ?? deployments.agentApiKey,
397
+ apiBase: options.apiBase ?? deployments.agentApiBase,
398
+ autoApprovePairing: options.autoApprovePairing ?? true,
298
399
  clientId: options.clientId,
299
400
  clientMode: options.clientMode,
401
+ clientDisplayName: options.clientDisplayName,
402
+ clientVersion: options.clientVersion,
403
+ platform: options.platform,
404
+ instanceId: options.instanceId,
405
+ caps: options.caps,
300
406
  origin: options.origin,
301
407
  timeout: options.timeout,
408
+ onHello: options.onHello,
409
+ onClose: options.onClose,
410
+ onGap: options.onGap,
411
+ onPairing: options.onPairing,
302
412
  });
303
413
  }
304
414
  async connect(options = {}) {
@@ -319,6 +429,18 @@ export class OpenClawAgent extends Agent {
319
429
  client.close();
320
430
  }
321
431
  }
432
+ async waitReady(timeoutMs = 300_000, options = {}) {
433
+ const client = this.gateway(options);
434
+ try {
435
+ return await client.waitReady(timeoutMs, {
436
+ retryIntervalMs: options.retryIntervalMs,
437
+ probe: options.probe,
438
+ });
439
+ }
440
+ finally {
441
+ client.close();
442
+ }
443
+ }
322
444
  async configGet(options = {}) {
323
445
  const client = await this.connect(options);
324
446
  try {
@@ -337,6 +459,33 @@ export class OpenClawAgent extends Agent {
337
459
  client.close();
338
460
  }
339
461
  }
462
+ async configPatch(patch, options = {}) {
463
+ const client = await this.connect(options);
464
+ try {
465
+ await client.configPatch(patch);
466
+ }
467
+ finally {
468
+ client.close();
469
+ }
470
+ }
471
+ async configApply(config, options = {}) {
472
+ const client = await this.connect(options);
473
+ try {
474
+ await client.configApply(config);
475
+ }
476
+ finally {
477
+ client.close();
478
+ }
479
+ }
480
+ async modelsList(options = {}) {
481
+ const client = await this.connect(options);
482
+ try {
483
+ return await client.modelsList();
484
+ }
485
+ finally {
486
+ client.close();
487
+ }
488
+ }
340
489
  async sessionsList(options = {}) {
341
490
  const client = await this.connect(options);
342
491
  try {
@@ -357,6 +506,251 @@ export class OpenClawAgent extends Agent {
357
506
  client.close();
358
507
  }
359
508
  }
509
+ async channelsStatus(options = {}) {
510
+ const client = await this.connect(options);
511
+ try {
512
+ return await client.channelsStatus(options.probe ?? false, options.timeoutMs);
513
+ }
514
+ finally {
515
+ client.close();
516
+ }
517
+ }
518
+ async channelsLogout(channel, accountId, options = {}) {
519
+ const client = await this.connect(options);
520
+ try {
521
+ return await client.channelsLogout(channel, accountId);
522
+ }
523
+ finally {
524
+ client.close();
525
+ }
526
+ }
527
+ async webLoginStart(options = {}) {
528
+ const client = await this.connect(options);
529
+ try {
530
+ return await client.webLoginStart({
531
+ force: options.force,
532
+ timeoutMs: options.timeoutMs,
533
+ verbose: options.verbose,
534
+ accountId: options.accountId,
535
+ });
536
+ }
537
+ finally {
538
+ client.close();
539
+ }
540
+ }
541
+ async webLoginWait(options = {}) {
542
+ const client = await this.connect(options);
543
+ try {
544
+ return await client.webLoginWait({
545
+ timeoutMs: options.timeoutMs,
546
+ accountId: options.accountId,
547
+ });
548
+ }
549
+ finally {
550
+ client.close();
551
+ }
552
+ }
553
+ async workspaceFiles(options = {}) {
554
+ const client = await this.connect(options);
555
+ try {
556
+ const agents = await client.agentsList();
557
+ const agentId = agents[0]?.id ?? 'main';
558
+ const files = await client.filesList(agentId);
559
+ return { agentId, files };
560
+ }
561
+ finally {
562
+ client.close();
563
+ }
564
+ }
565
+ async fileGet(name, agentId, options = {}) {
566
+ const client = await this.connect(options);
567
+ try {
568
+ let resolvedAgentId;
569
+ if (agentId) {
570
+ resolvedAgentId = agentId;
571
+ }
572
+ else {
573
+ const agents = await client.agentsList();
574
+ resolvedAgentId = agents[0]?.id ?? 'main';
575
+ }
576
+ return await client.fileGet(resolvedAgentId, name);
577
+ }
578
+ finally {
579
+ client.close();
580
+ }
581
+ }
582
+ async fileSet(name, content, agentId, options = {}) {
583
+ const client = await this.connect(options);
584
+ try {
585
+ let resolvedAgentId;
586
+ if (agentId) {
587
+ resolvedAgentId = agentId;
588
+ }
589
+ else {
590
+ const agents = await client.agentsList();
591
+ resolvedAgentId = agents[0]?.id ?? 'main';
592
+ }
593
+ await client.fileSet(resolvedAgentId, name, content);
594
+ }
595
+ finally {
596
+ client.close();
597
+ }
598
+ }
599
+ async chatHistory(sessionKey, limit = 50, options = {}) {
600
+ const client = await this.connect(options);
601
+ try {
602
+ return await client.chatHistory(sessionKey, limit);
603
+ }
604
+ finally {
605
+ client.close();
606
+ }
607
+ }
608
+ async chatSendMessage(message, options = {}) {
609
+ const client = await this.connect(options);
610
+ try {
611
+ return await client.sendChat(message, options.sessionKey ?? 'main', options.agentId, options.attachments);
612
+ }
613
+ finally {
614
+ client.close();
615
+ }
616
+ }
617
+ async mutateConfig(mutator, options = {}) {
618
+ const config = structuredClone(await this.configGet(options));
619
+ await mutator(config);
620
+ await this.configApply(config, options);
621
+ return config;
622
+ }
623
+ async providerUpsert(providerId, providerConfig, gatewayOptions = {}) {
624
+ const { api, baseUrl, apiKey, models, ...extra } = providerConfig;
625
+ const config = await this.mutateConfig((next) => {
626
+ const modelsCfg = (next.models ??= {});
627
+ const providers = (modelsCfg.providers ??= {});
628
+ const provider = { ...(providers[providerId] ?? {}) };
629
+ provider.api = api;
630
+ provider.baseUrl = baseUrl;
631
+ if (apiKey !== undefined)
632
+ provider.apiKey = apiKey;
633
+ if (models !== undefined)
634
+ provider.models = structuredClone(models);
635
+ Object.assign(provider, extra);
636
+ providers[providerId] = provider;
637
+ }, gatewayOptions);
638
+ return config.models?.providers?.[providerId] ?? {};
639
+ }
640
+ async providerRemove(providerId, options = {}) {
641
+ const config = await this.mutateConfig((next) => {
642
+ if (next.models?.providers) {
643
+ delete next.models.providers[providerId];
644
+ }
645
+ }, options);
646
+ return config.models?.providers ?? {};
647
+ }
648
+ async modelUpsert(providerId, modelId, modelConfig = {}, gatewayOptions = {}) {
649
+ const config = await this.mutateConfig((next) => {
650
+ const providers = ((next.models ??= {}).providers ??= {});
651
+ const provider = { ...(providers[providerId] ?? {}) };
652
+ const models = Array.isArray(provider.models)
653
+ ? provider.models.map((entry) => ({ ...entry }))
654
+ : [];
655
+ let model = models.find((entry) => entry.id === modelId);
656
+ if (!model) {
657
+ model = { id: modelId };
658
+ models.push(model);
659
+ }
660
+ Object.assign(model, modelConfig);
661
+ provider.models = models;
662
+ providers[providerId] = provider;
663
+ }, gatewayOptions);
664
+ return (config.models?.providers?.[providerId]?.models?.find((entry) => entry.id === modelId) ??
665
+ {});
666
+ }
667
+ async modelRemove(providerId, modelId, options = {}) {
668
+ const config = await this.mutateConfig((next) => {
669
+ const providers = ((next.models ??= {}).providers ??= {});
670
+ const provider = { ...(providers[providerId] ?? {}) };
671
+ provider.models = Array.isArray(provider.models)
672
+ ? provider.models.filter((entry) => entry.id !== modelId)
673
+ : [];
674
+ providers[providerId] = provider;
675
+ }, options);
676
+ return config.models?.providers?.[providerId]?.models ?? [];
677
+ }
678
+ async setDefaultModel(providerId, modelId, options = {}) {
679
+ const primary = `${providerId}/${modelId}`;
680
+ await this.mutateConfig((next) => {
681
+ const defaults = ((next.agents ??= {}).defaults ??= {});
682
+ const model = (defaults.model ??= {});
683
+ model.primary = primary;
684
+ }, options);
685
+ return primary;
686
+ }
687
+ async setMemorySearch(memorySearchConfig, gatewayOptions = {}) {
688
+ const { provider, model, baseUrl, apiKey, ...extra } = memorySearchConfig;
689
+ const config = await this.mutateConfig((next) => {
690
+ const defaults = ((next.agents ??= {}).defaults ??= {});
691
+ const memorySearch = { ...(defaults.memorySearch ?? {}) };
692
+ memorySearch.provider = provider;
693
+ memorySearch.model = model;
694
+ const remote = { ...(memorySearch.remote ?? {}) };
695
+ if (baseUrl !== undefined)
696
+ remote.baseUrl = baseUrl;
697
+ if (apiKey !== undefined)
698
+ remote.apiKey = apiKey;
699
+ if (Object.keys(remote).length > 0)
700
+ memorySearch.remote = remote;
701
+ Object.assign(memorySearch, extra);
702
+ defaults.memorySearch = memorySearch;
703
+ }, gatewayOptions);
704
+ return config.agents?.defaults?.memorySearch ?? {};
705
+ }
706
+ async channelUpsert(channelId, channelConfig, options = {}) {
707
+ const { accountId, ...gatewayOptions } = options;
708
+ const config = await this.mutateConfig((next) => {
709
+ const channels = (next.channels ??= {});
710
+ const current = channels[channelId] && typeof channels[channelId] === 'object'
711
+ ? structuredClone(channels[channelId])
712
+ : {};
713
+ if (accountId) {
714
+ const accounts = current.accounts && typeof current.accounts === 'object'
715
+ ? structuredClone(current.accounts)
716
+ : {};
717
+ const currentAccount = accounts[accountId] && typeof accounts[accountId] === 'object'
718
+ ? accounts[accountId]
719
+ : {};
720
+ accounts[accountId] = deepMergeConfig(currentAccount, channelConfig);
721
+ current.accounts = accounts;
722
+ channels[channelId] = current;
723
+ return;
724
+ }
725
+ channels[channelId] = deepMergeConfig(current, channelConfig);
726
+ }, gatewayOptions);
727
+ const channel = config.channels?.[channelId] ?? {};
728
+ if (accountId) {
729
+ return channel.accounts?.[accountId] ?? {};
730
+ }
731
+ return channel;
732
+ }
733
+ async channelPatch(channelId, patch, options = {}) {
734
+ return await this.channelUpsert(channelId, patch, options);
735
+ }
736
+ async telegramUpsert(channelConfig, options = {}) {
737
+ return await this.channelUpsert('telegram', channelConfig, options);
738
+ }
739
+ async slackUpsert(channelConfig, options = {}) {
740
+ return await this.channelUpsert('slack', channelConfig, options);
741
+ }
742
+ async discordUpsert(channelConfig, options = {}) {
743
+ return await this.channelUpsert('discord', channelConfig, options);
744
+ }
745
+ async cronList(options = {}) {
746
+ const client = await this.connect(options);
747
+ try {
748
+ return await client.cronList();
749
+ }
750
+ finally {
751
+ client.close();
752
+ }
753
+ }
360
754
  }
361
755
  export class Deployments {
362
756
  http;
@@ -366,8 +760,14 @@ export class Deployments {
366
760
  constructor(http, agentApiKey, agentApiBase, agentsWsUrl) {
367
761
  this.http = http;
368
762
  this.apiKey = agentApiKey || http.apiKey;
369
- this.apiBase = resolveAgentsApiBase(agentApiBase || process.env.HYPERCLI_API_URL || AGENTS_API_BASE);
370
- this.agentsWsUrl = agentsWsUrl ? normalizeAgentsWsUrl(agentsWsUrl) : defaultAgentsWsUrl(this.apiBase);
763
+ this.apiBase = resolveAgentsApiBase(agentApiBase || getAgentsApiBaseUrl());
764
+ this.agentsWsUrl = normalizeAgentsWsUrl(agentsWsUrl || getConfigValue('AGENTS_WS_URL') || defaultAgentsWsUrl(this.apiBase));
765
+ }
766
+ get agentApiKey() {
767
+ return this.apiKey;
768
+ }
769
+ get agentApiBase() {
770
+ return this.apiBase;
371
771
  }
372
772
  hydrateAgent(data) {
373
773
  const agent = data.openclaw_url || data.gateway_url
@@ -410,6 +810,14 @@ export class Deployments {
410
810
  agent.entrypoint = [...(config.entrypoint ?? [])];
411
811
  return agent;
412
812
  }
813
+ async createOpenClaw(options = {}) {
814
+ const effectiveOptions = { ...options };
815
+ if (options.routes === undefined) {
816
+ effectiveOptions.routes = buildOpenClawRoutes(options.openClawRoutes ?? {});
817
+ }
818
+ effectiveOptions.image = defaultOpenClawImage(options.image, options.config ?? {});
819
+ return this.create(effectiveOptions);
820
+ }
413
821
  async budget() {
414
822
  return this.http.get(`${DEPLOYMENTS_API_PREFIX}/budget`);
415
823
  }
@@ -428,6 +836,22 @@ export class Deployments {
428
836
  const data = await this.http.get(`${DEPLOYMENTS_API_PREFIX}/${agentId}`);
429
837
  return this.hydrateAgent(data);
430
838
  }
839
+ async waitRunning(agentId, timeoutMs = 300_000, pollIntervalMs = 5_000) {
840
+ const deadline = Date.now() + timeoutMs;
841
+ let lastState = '';
842
+ while (Date.now() < deadline) {
843
+ const agent = await this.get(agentId);
844
+ lastState = String(agent.state || '');
845
+ if (lastState.toLowerCase() === 'running') {
846
+ return agent;
847
+ }
848
+ if (lastState.toLowerCase() === 'failed' || lastState.toLowerCase() === 'error') {
849
+ throw new Error(`Agent entered ${lastState} while waiting for RUNNING`);
850
+ }
851
+ await sleep(pollIntervalMs);
852
+ }
853
+ throw new Error(`Timed out waiting for agent ${agentId} to reach RUNNING (last=${lastState})`);
854
+ }
431
855
  async start(agentId, options = {}) {
432
856
  const { config, gatewayToken } = buildAgentConfig(options.config ?? {}, options);
433
857
  const body = { config };
@@ -443,6 +867,14 @@ export class Deployments {
443
867
  agent.entrypoint = [...(config.entrypoint ?? [])];
444
868
  return agent;
445
869
  }
870
+ async startOpenClaw(agentId, options = {}) {
871
+ const effectiveOptions = { ...options };
872
+ if (options.routes === undefined) {
873
+ effectiveOptions.routes = buildOpenClawRoutes(options.openClawRoutes ?? {});
874
+ }
875
+ effectiveOptions.image = defaultOpenClawImage(options.image, options.config ?? {});
876
+ return this.start(agentId, effectiveOptions);
877
+ }
446
878
  async stop(agentId) {
447
879
  const data = await this.http.post(`${DEPLOYMENTS_API_PREFIX}/${agentId}/stop`);
448
880
  return this.hydrateAgent(data);