@qoder-ai/qoder-agent-sdk 1.0.25 → 1.0.27

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.
@@ -18,7 +18,8 @@ import type { PluginDetails } from './plugins.js';
18
18
  import type { SessionStore, SessionStoreFlush } from '../session/session-store.js';
19
19
  import type { ModelPromptPatches } from '../protocol/model-prompt-patches.js';
20
20
  import type { MemoryOptions } from './memory.js';
21
- import type { SDKGoalSnapshot, SDKGoalStatus } from '../protocol/control.js';
21
+ import type { EvolutionOptions } from './skill-evolution.js';
22
+ import type { SDKGoalSnapshot, SDKGoalStatus, SDKPlanModeSnapshot } from '../protocol/control.js';
22
23
  export type { CanUseTool };
23
24
  export type { AuthOptions };
24
25
  export type ElicitationRequest = {
@@ -147,12 +148,15 @@ export type Options = {
147
148
  skills?: string[] | 'all';
148
149
  extensions?: string[];
149
150
  /**
150
- * Proxy URL used by the qodercli child process for outbound network traffic.
151
+ * Proxy URL used by qodercli for its own outbound network traffic.
151
152
  *
152
153
  * Supports the same schemes as qodercli: `http://`, `https://`,
153
- * `socks5://`, and `socks://`. When set, the SDK writes this value to
154
- * `HTTPS_PROXY`, `https_proxy`, `HTTP_PROXY`, and `http_proxy` in the child
155
- * environment.
154
+ * `socks5://`, and `socks://`. The SDK passes this value through qodercli's
155
+ * startup interface without rewriting proxy environment variables.
156
+ *
157
+ * When omitted, SDK-integrated qodercli connects directly and does not
158
+ * discover a proxy from the inherited environment. Environment variables are
159
+ * still preserved for tools and subprocesses launched by the agent.
156
160
  */
157
161
  proxy?: string;
158
162
  /**
@@ -176,6 +180,7 @@ export type Options = {
176
180
  };
177
181
  executable?: 'bun' | 'deno' | 'node';
178
182
  executableArgs?: string[];
183
+ /** Additional qodercli flags. `proxy` is reserved; use `proxy` instead. */
179
184
  extraArgs?: Record<string, string | null>;
180
185
  enableFileCheckpointing?: boolean;
181
186
  toolConfig?: ToolConfig;
@@ -205,6 +210,8 @@ export type Options = {
205
210
  * Functions stay in the SDK process and are bridged through control requests.
206
211
  */
207
212
  memory?: MemoryOptions;
213
+ /** Configure component evolution capabilities for this query. */
214
+ evolution?: EvolutionOptions;
208
215
  onElicitation?: OnElicitation;
209
216
  /**
210
217
  * Called when the SDK detects that authentication has expired or
@@ -245,6 +252,13 @@ export type Options = {
245
252
  */
246
253
  pathToQoderCLIExecutable?: string;
247
254
  permissionMode?: PermissionMode;
255
+ /**
256
+ * Initial Plan Mode state for the main session. Applied after the CLI
257
+ * initialize handshake and before the first user message, without changing
258
+ * the underlying `permissionMode`. Requires the CLI `plan_mode_v1`
259
+ * capability.
260
+ */
261
+ planMode?: boolean;
248
262
  allowDangerouslySkipPermissions?: boolean;
249
263
  /**
250
264
  * MCP tool name to use for permission prompts. Mutually exclusive with
@@ -403,6 +417,14 @@ export interface Query extends AsyncGenerator<SDKMessage, void> {
403
417
  getGoal?(): Promise<SDKGoalSnapshot | null>;
404
418
  /** Clear the session goal; returns whether a goal was cleared. */
405
419
  clearGoal?(): Promise<boolean>;
420
+ /**
421
+ * Enter or leave the main session's independent Plan Mode. The current tool
422
+ * permission mode is preserved. Requires the CLI `plan_mode_v1` capability.
423
+ * Only present on the local CLI transport; absent on cloud-agent queries.
424
+ */
425
+ setPlanMode?(active: boolean): Promise<SDKPlanModeSnapshot>;
426
+ /** Read the authoritative Plan Mode state from the running CLI session. */
427
+ getPlanMode?(): Promise<SDKPlanModeSnapshot>;
406
428
  setPermissionMode(mode: PermissionMode): Promise<void>;
407
429
  setModel(model?: string): Promise<void>;
408
430
  /**
@@ -489,6 +511,8 @@ export interface Query extends AsyncGenerator<SDKMessage, void> {
489
511
  seedReadState(path: string, mtime: number): Promise<void>;
490
512
  /** Wait until pending TurnComplete memory generation has finished. */
491
513
  flushMemory(): Promise<void>;
514
+ /** Wait until pending TurnComplete skill evolution review has finished. */
515
+ flushSkillEvolution(): Promise<void>;
492
516
  /** Reload configured memory consumption files without changing config. */
493
517
  refreshMemory(): Promise<void>;
494
518
  /**
@@ -89,10 +89,18 @@ export type ListSessionsOptions = SessionStoreOptions & {
89
89
  export type GetSessionInfoOptions = SessionStoreOptions & {
90
90
  dir?: string;
91
91
  };
92
+ /** Message projection returned by {@link getSessionMessages}. */
93
+ export type SessionMessageView = 'runtime' | 'historical';
92
94
  export type GetSessionMessagesOptions = SessionStoreOptions & {
93
95
  dir?: string;
94
96
  limit?: number;
95
97
  offset?: number;
98
+ /**
99
+ * `runtime` returns the latest compact summary and post-compact messages.
100
+ * `historical` returns real messages across compact boundaries while hiding
101
+ * compact boundary and summary implementation records. Defaults to `runtime`.
102
+ */
103
+ view?: SessionMessageView;
96
104
  /**
97
105
  * Include `system` entries in the result (compact boundaries, informational
98
106
  * notices). Defaults to false — only user/assistant are returned.
@@ -0,0 +1,41 @@
1
+ import type { SkillEvolutionGateInput, SkillEvolutionGateResult, SkillEvolutionResult, SerializableSkillEvolutionConfig } from '../protocol/skill-evolution.js';
2
+ export type { SkillEvolutionMode } from '../protocol/skill-evolution.js';
3
+ export declare const MAX_SKILL_EVOLUTION_REVIEW_THRESHOLD = 10;
4
+ export type { EffectiveEvolutionConfig, EffectiveSkillEvolutionConfig, SkillEvolutionGateInput, SkillEvolutionGateResult, SkillEvolutionResult, SerializableEvolutionConfig, SerializableSkillEvolutionConfig, SkillEvolutionSuggestion, } from '../protocol/skill-evolution.js';
5
+ export type SkillEvolutionShouldReview = (input: SkillEvolutionGateInput, options: {
6
+ signal: AbortSignal;
7
+ }) => Promise<SkillEvolutionGateResult>;
8
+ export type SkillEvolutionGenerationOptions = {
9
+ enabled?: boolean;
10
+ prompt?: string;
11
+ maxSuggestions?: number;
12
+ turnComplete?: {
13
+ enabled?: boolean;
14
+ /** Integer from 1 to 10. Defaults to 5. */
15
+ reviewThreshold?: number;
16
+ shouldReview?: SkillEvolutionShouldReview;
17
+ timeoutMs?: number;
18
+ onGateError?: 'skip' | 'report_failed';
19
+ };
20
+ onResult?: (result: SkillEvolutionResult) => void | Promise<void>;
21
+ };
22
+ export type SkillEvolutionOptions = {
23
+ mode: 'native';
24
+ generation?: Pick<SkillEvolutionGenerationOptions, 'enabled' | 'onResult'> & {
25
+ turnComplete?: Pick<NonNullable<SkillEvolutionGenerationOptions['turnComplete']>, 'reviewThreshold'>;
26
+ };
27
+ } | {
28
+ mode: 'custom';
29
+ generation?: SkillEvolutionGenerationOptions;
30
+ };
31
+ export type EvolutionOptions = {
32
+ skill?: SkillEvolutionOptions;
33
+ };
34
+ export type SkillEvolutionRuntimeCallbacks = {
35
+ shouldReviewCallbacks: ReadonlyMap<string, SkillEvolutionShouldReview>;
36
+ onResult?: SkillEvolutionGenerationOptions['onResult'];
37
+ };
38
+ export type PreparedSkillEvolutionOptions = {
39
+ config?: SerializableSkillEvolutionConfig;
40
+ callbacks?: SkillEvolutionRuntimeCallbacks;
41
+ };
@@ -26,6 +26,7 @@ export type AgentToolCompletionResult = {
26
26
  outputPath?: string;
27
27
  transcriptPath?: string;
28
28
  worktreePath?: string;
29
+ worktreeBranch?: string;
29
30
  remoteContextId?: string;
30
31
  remoteTaskId?: string;
31
32
  };
@@ -324,23 +325,40 @@ export type ConfigOutput = {
324
325
  error?: string;
325
326
  };
326
327
  export type EnterWorktreeInput = {
328
+ /** Create (or resume) a managed worktree with this name. */
327
329
  name?: string;
330
+ /** Enter an existing registered Git worktree instead of creating one. */
331
+ path?: string;
328
332
  };
333
+ /**
334
+ * Successful EnterWorktree result. Failures use ToolResultBlock.is_error/content.
335
+ *
336
+ * SDK 1.0.25 and earlier declared a snake_case success/error object that was
337
+ * never the qodercli wire shape. This type intentionally follows the actual
338
+ * structured result available with the paired qodercli 1.1.27 or newer.
339
+ */
329
340
  export type EnterWorktreeOutput = {
330
- worktree_path: string;
331
- branch_name: string;
332
- success: boolean;
333
- error?: string;
341
+ worktreePath: string;
342
+ worktreeBranch?: string;
343
+ message: string;
334
344
  };
335
345
  export type ExitWorktreeInput = {
336
346
  action: 'keep' | 'remove';
337
347
  discard_changes?: boolean;
338
348
  };
349
+ /** Successful ExitWorktree result. Failures use ToolResultBlock.is_error/content. */
339
350
  export type ExitWorktreeOutput = {
340
- success: boolean;
341
- error?: string;
342
- uncommitted_files?: string[];
343
- unmerged_commits?: string[];
351
+ action: 'keep' | 'remove';
352
+ originalCwd: string;
353
+ worktreePath: string;
354
+ worktreeBranch?: string;
355
+ /** Reserved for forward compatibility; current qodercli does not populate it. */
356
+ tmuxSessionName?: string;
357
+ /** Reserved for forward compatibility; current qodercli does not populate it. */
358
+ discardedFiles?: number;
359
+ /** Reserved for forward compatibility; current qodercli does not populate it. */
360
+ discardedCommits?: number;
361
+ message: string;
344
362
  };
345
363
  export type TodoWriteInput = {
346
364
  todos: Array<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qoder-ai/qoder-agent-sdk",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "TypeScript SDK for building Qoder-powered coding agents.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "peerDependencies": {
51
51
  "zod": "^3.25.0 || ^4.0.0"
52
52
  },
53
- "qoderCliVersion": "1.1.26",
53
+ "qoderCliVersion": "1.1.31",
54
54
  "qoderSdkBrand": "global",
55
55
  "license": "SEE LICENSE IN LICENSE"
56
56
  }
@@ -9,7 +9,6 @@
9
9
  * - QODER_CLI_MIRROR: CDN base URL. Accepts .../qodercli, .../qodercli/dev, or .../releases
10
10
  * - QODER_WORKER_RUNTIME_URL: explicit worker runtime tgz URL override
11
11
  * - QODER_CLI_VERSION: override package.json qoderCliVersion
12
- * - QODER_CLI_SITE: download site. Supports global and cn
13
12
  * - QODER_CLI_PLATFORM: override target artifact, e.g. darwin-arm64, linux-x64-musl
14
13
  * - QODER_SKIP_CHECKSUM=1|true: skip .sha256 verification
15
14
  */
@@ -30,6 +29,18 @@ const CN_DEV_DOWNLOAD_BASE_URL = 'https://static.qoder.com.cn/qoder-cli-cn/dev/r
30
29
  const PACKAGE_JSON = require('../package.json');
31
30
  const LOG_PREFIX = `[${PACKAGE_JSON.name ?? '@qoder-ai/qoder-agent-sdk'}]`;
32
31
  const WORKER_RUNTIME_ENTRY_NAMES = ['qoder-worker-runtime.obf.mjs', 'qoder-worker-runtime.mjs'];
32
+ const SUPPORTED_CLI_PLATFORM_TARGETS = Object.freeze([
33
+ 'darwin-arm64',
34
+ 'darwin-x64',
35
+ 'linux-arm64',
36
+ 'linux-arm64-musl',
37
+ 'linux-x64',
38
+ 'linux-x64-baseline',
39
+ 'linux-x64-musl',
40
+ 'openharmony-arm64',
41
+ 'windows-arm64',
42
+ 'windows-x64',
43
+ ]);
33
44
 
34
45
  function isTruthy(value) {
35
46
  return value === '1' || value === 'true';
@@ -61,11 +72,11 @@ function normalizeCliSite(value) {
61
72
  const normalized = String(value).trim().toLowerCase();
62
73
  if (normalized === 'global') return 'global';
63
74
  if (normalized === 'cn') return 'cn';
64
- throw new Error(`Unsupported QODER_CLI_SITE "${value}". Supported: global, cn`);
75
+ throw new Error(`Unsupported package.json qoderSdkBrand "${value}". Supported: global, cn`);
65
76
  }
66
77
 
67
78
  function getCliSite() {
68
- return normalizeCliSite(process.env.QODER_CLI_SITE ?? PACKAGE_JSON.qoderSdkBrand ?? 'global');
79
+ return normalizeCliSite(PACKAGE_JSON.qoderSdkBrand ?? 'global');
69
80
  }
70
81
 
71
82
  function getCliArtifactPrefix(site = getCliSite()) {
@@ -86,11 +97,10 @@ function getDownloadBaseUrl(version = getCliVersion()) {
86
97
  return GLOBAL_PROD_DOWNLOAD_BASE_URL;
87
98
  }
88
99
 
89
- function getWorkerRuntimeDownloadBaseUrl(version = getCliVersion()) {
100
+ function getWorkerRuntimeDownloadBaseUrl(version = getCliVersion(), site = getCliSite()) {
90
101
  if (process.env.QODER_CLI_MIRROR) {
91
102
  return process.env.QODER_CLI_MIRROR;
92
103
  }
93
- const site = getCliSite();
94
104
  if (site === 'cn') {
95
105
  return isDevCliVersion(version) ? CN_DEV_DOWNLOAD_BASE_URL : CN_PROD_DOWNLOAD_BASE_URL;
96
106
  }
@@ -134,22 +144,9 @@ function linuxX64HasAvx2() {
134
144
 
135
145
  function normalizePlatformOverride(value) {
136
146
  const normalized = value.replace(/_/g, '-').replace(/-amd64(?=-|$)/, '-x64');
137
- const supported = new Set([
138
- 'darwin-arm64',
139
- 'darwin-x64',
140
- 'linux-arm64',
141
- 'linux-arm64-musl',
142
- 'linux-x64',
143
- 'linux-x64-baseline',
144
- 'linux-x64-musl',
145
- 'openharmony-arm64',
146
- 'windows-arm64',
147
- 'windows-x64',
148
- ]);
149
-
150
- if (!supported.has(normalized)) {
147
+ if (!SUPPORTED_CLI_PLATFORM_TARGETS.includes(normalized)) {
151
148
  throw new Error(
152
- `Unsupported QODER_CLI_PLATFORM "${value}". Supported: ${[...supported].join(', ')}`,
149
+ `Unsupported QODER_CLI_PLATFORM "${value}". Supported: ${SUPPORTED_CLI_PLATFORM_TARGETS.join(', ')}`,
153
150
  );
154
151
  }
155
152
  return normalized;
@@ -213,11 +210,19 @@ function getWorkerRuntimeTarget(target = getPlatformTarget()) {
213
210
  if (platform === 'darwin') return `darwin-${arch}`;
214
211
  if (platform === 'linux') return `linux-${arch}`;
215
212
  if (platform === 'windows') return `win32-${arch}`;
213
+ if (platform === 'openharmony') return `openharmony-${arch}`;
216
214
  throw new Error(
217
- `Worker runtime platform artifacts do not support ${target}. Supported: darwin-arm64, darwin-x64, linux-arm64, linux-x64, windows-arm64, windows-x64`,
215
+ `Worker runtime platform artifacts do not support ${target}. Supported: darwin-arm64, darwin-x64, linux-arm64, linux-x64, openharmony-arm64, windows-arm64, windows-x64`,
218
216
  );
219
217
  }
220
218
 
219
+ function getWorkerRuntimeArtifactTargets() {
220
+ return [
221
+ 'universal',
222
+ ...new Set(SUPPORTED_CLI_PLATFORM_TARGETS.map((target) => getWorkerRuntimeTarget(target))),
223
+ ];
224
+ }
225
+
221
226
  function getWorkerRuntimeArtifactName(site = getCliSite(), target = getPlatformTarget()) {
222
227
  const workerTarget = getWorkerRuntimeTarget(target);
223
228
  return `${getCliArtifactPrefix(site)}-worker-runtime-${workerTarget}.tgz`;
@@ -230,6 +235,10 @@ function getWorkerRuntimeArtifactNameTemplate(site = getCliSite()) {
230
235
  function getWorkerRuntimeRequiredAssets(target = getPlatformTarget()) {
231
236
  const workerTarget = getWorkerRuntimeTarget(target);
232
237
  const [platform, arch] = workerTarget.split('-');
238
+ // ripgrep has no HarmonyOS build, so its worker archive ships without one and
239
+ // search falls back to a system rg. Demanding the asset would make every
240
+ // HarmonyOS install fail the completeness check.
241
+ if (platform === 'openharmony') return [];
233
242
  return [
234
243
  path.join(
235
244
  'vendor',
@@ -240,10 +249,6 @@ function getWorkerRuntimeRequiredAssets(target = getPlatformTarget()) {
240
249
  ];
241
250
  }
242
251
 
243
- function hasWorkerRuntimeDownloadSiteOverride() {
244
- return Boolean(process.env.QODER_CLI_SITE || process.env.QODER_CLI_MIRROR);
245
- }
246
-
247
252
  function getManifestWorkerRuntime(manifest = readRuntimeManifest()) {
248
253
  const workerRuntime = manifest?.workerRuntime;
249
254
  return workerRuntime && typeof workerRuntime === 'object' ? workerRuntime : null;
@@ -281,14 +286,17 @@ function getManifestWorkerRuntimeSha256(manifest = readRuntimeManifest()) {
281
286
  return checksum ? checksum.trim().toLowerCase() : null;
282
287
  }
283
288
 
284
- function getWorkerRuntimeDownloadUrlTemplate() {
289
+ function getWorkerRuntimeDownloadUrlTemplate(site = getCliSite()) {
285
290
  const version = getCliVersion();
286
- const baseUrl = normalizeDownloadBaseUrl(getWorkerRuntimeDownloadBaseUrl(version), version);
287
- return `${baseUrl}/${getWorkerRuntimeArtifactNameTemplate()}`;
291
+ const baseUrl = normalizeDownloadBaseUrl(
292
+ getWorkerRuntimeDownloadBaseUrl(version, site),
293
+ version,
294
+ );
295
+ return `${baseUrl}/${getWorkerRuntimeArtifactNameTemplate(site)}`;
288
296
  }
289
297
 
290
- function getWorkerRuntimeDefaultDownloadUrl() {
291
- return getWorkerRuntimeDownloadUrlTemplate().replace(
298
+ function getWorkerRuntimeDefaultDownloadUrl(site = getCliSite()) {
299
+ return getWorkerRuntimeDownloadUrlTemplate(site).replace(
292
300
  '{target}',
293
301
  getWorkerRuntimeTarget(),
294
302
  );
@@ -299,7 +307,7 @@ function getWorkerRuntimeDownloadUrl() {
299
307
  return process.env.QODER_WORKER_RUNTIME_URL;
300
308
  }
301
309
 
302
- if (!hasWorkerRuntimeDownloadSiteOverride()) {
310
+ if (!process.env.QODER_CLI_MIRROR) {
303
311
  const template = getManifestWorkerRuntimeDownloadUrlTemplate();
304
312
  if (template) {
305
313
  return template.replace('{target}', getWorkerRuntimeTarget());
@@ -781,7 +789,7 @@ function assertWorkerRuntimeComplete(runtimeDir, sourceDescription) {
781
789
  return runtimePath;
782
790
  }
783
791
 
784
- function assertWorkerRuntimeIdentity(runtimeDir) {
792
+ function assertWorkerRuntimeIdentity(runtimeDir, brand = getCliSite()) {
785
793
  const runtimeInfoPath = path.join(runtimeDir, 'runtime-info.json');
786
794
  if (!fs.existsSync(runtimeInfoPath)) {
787
795
  throw new Error(`Downloaded Worker runtime is missing identity metadata: ${runtimeInfoPath}`);
@@ -795,8 +803,7 @@ function assertWorkerRuntimeIdentity(runtimeDir) {
795
803
  }
796
804
 
797
805
  const expectedTarget = getWorkerRuntimeTarget();
798
- const expectedSite = getCliSite();
799
- const expectedProductName = getCliArtifactPrefix(expectedSite);
806
+ const expectedProductName = getCliArtifactPrefix(brand);
800
807
  const expectedVersion = getCliVersion();
801
808
  const mismatches = [];
802
809
 
@@ -809,8 +816,8 @@ function assertWorkerRuntimeIdentity(runtimeDir) {
809
816
  if (runtimeInfo.version !== expectedVersion) {
810
817
  mismatches.push(`version=${String(runtimeInfo.version)} (expected ${expectedVersion})`);
811
818
  }
812
- if (runtimeInfo.build?.site !== expectedSite) {
813
- mismatches.push(`site=${String(runtimeInfo.build?.site)} (expected ${expectedSite})`);
819
+ if (runtimeInfo.build?.site !== brand) {
820
+ mismatches.push(`site=${String(runtimeInfo.build?.site)} (expected ${brand})`);
814
821
  }
815
822
  if (runtimeInfo.build?.productName !== expectedProductName) {
816
823
  mismatches.push(
@@ -832,7 +839,7 @@ function installExtractedWorkerRuntime(extractDir, workerDir, options = {}) {
832
839
  }
833
840
  assertWorkerRuntimeComplete(contentRoot, contentRoot);
834
841
  if (options.validateIdentity === true) {
835
- assertWorkerRuntimeIdentity(contentRoot);
842
+ assertWorkerRuntimeIdentity(contentRoot, options.brand);
836
843
  }
837
844
 
838
845
  fs.rmSync(workerDir, { recursive: true, force: true });
@@ -846,8 +853,12 @@ function installExtractedWorkerRuntime(extractDir, workerDir, options = {}) {
846
853
  }
847
854
 
848
855
  async function downloadAndInstallWorkerRuntime(workerDir, options = {}) {
849
- const url = options.urlOverride ?? getWorkerRuntimeDownloadUrl();
850
- const expectedSha256 = options.urlOverride ? null : getWorkerRuntimeExpectedSha256(url);
856
+ const brand = options.brand ?? getCliSite();
857
+ const url =
858
+ options.urlOverride ??
859
+ (options.brand ? getWorkerRuntimeDefaultDownloadUrl(brand) : getWorkerRuntimeDownloadUrl());
860
+ const expectedSha256 =
861
+ options.urlOverride || options.brand ? null : getWorkerRuntimeExpectedSha256(url);
851
862
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'qoder-worker-runtime-'));
852
863
  const archivePath = path.join(tempDir, path.basename(new URL(url).pathname));
853
864
  const extractDir = path.join(tempDir, 'extract');
@@ -860,6 +871,7 @@ async function downloadAndInstallWorkerRuntime(workerDir, options = {}) {
860
871
  return {
861
872
  runtimePath: installExtractedWorkerRuntime(extractDir, workerDir, {
862
873
  validateIdentity: true,
874
+ brand,
863
875
  }),
864
876
  url,
865
877
  };
@@ -986,6 +998,7 @@ module.exports = {
986
998
  getWorkerRuntimeExpectedSha256,
987
999
  getWorkerRuntimeArtifactName,
988
1000
  getWorkerRuntimeArtifactNameTemplate,
1001
+ getWorkerRuntimeArtifactTargets,
989
1002
  getWorkerRuntimeDownloadBaseUrl,
990
1003
  getWorkerRuntimeDownloadUrl,
991
1004
  getWorkerRuntimeDownloadUrlTemplate,