@qoder-ai/qoder-agent-sdk 1.0.24 → 1.0.26

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.
@@ -9,7 +9,7 @@ import type { SDKMessage as ProtocolSDKMessage, SDKStatusMessage as ProtocolSDKS
9
9
  import type { MessageParam, UUID } from './common.js';
10
10
  import type { PermissionMode } from './permissions.js';
11
11
  import type { SessionKey } from '../session/session-store.js';
12
- export type { SDKAssistantMessage, SDKResultSuccess, SDKResultError, SDKResultMessage, SDKPartialAssistantMessage, SDKCompactBoundaryMessage, SDKAPIRetryMessage, SDKModelQueueStatusMessage, SDKControlRequestProgressMessage, SDKHookStartedMessage, SDKHookProgressMessage, SDKHookResponseMessage, SDKTaskNotificationMessage, SDKTaskStartedMessage, SDKTaskProgressMessage, SDKTaskUpdatedMessage, SDKBackgroundTasksChangedMessage, SDKSessionStateChangedMessage, SDKSessionTitleChangedMessage, SDKFilesPersistedEvent, SDKElicitationCompleteMessage, SDKPromptSuggestionMessage, SDKCloudAgentEventMessage, SDKMemoryGenerationMessage, SDKMemoryConsumptionMessage, SDKPermissionDenial, SDKToolNonExecutionKind, SDKToolResultMeta, } from '../protocol/index.js';
12
+ export type { SDKAssistantMessage, SDKResultSuccess, SDKResultError, SDKResultMessage, SDKPartialAssistantMessage, SDKCompactBoundaryMessage, SDKAPIRetryMessage, SDKModelQueueStatusMessage, SDKControlRequestProgressMessage, SDKHookStartedMessage, SDKHookProgressMessage, SDKHookResponseMessage, SDKTaskNotificationMessage, SDKTaskStartedMessage, SDKTaskProgressMessage, SDKTaskUpdatedMessage, SDKBackgroundTasksChangedMessage, SDKSessionStateChangedMessage, SDKPlanModeChangedMessage, SDKSessionTitleChangedMessage, SDKFilesPersistedEvent, SDKElicitationCompleteMessage, SDKPromptSuggestionMessage, SDKCloudAgentEventMessage, SDKMemoryGenerationMessage, SDKMemoryConsumptionMessage, SDKSkillEvolutionMessage, SDKPermissionDenial, SDKToolNonExecutionKind, SDKToolResultMeta, } from '../protocol/index.js';
13
13
  export type SDKUserMessage = {
14
14
  type: 'user';
15
15
  message: MessageParam;
@@ -28,6 +28,12 @@ export interface ModelPolicyContext {
28
28
  sessionId: string;
29
29
  /** Current conversation turn index (0-based) */
30
30
  turnIndex: number;
31
+ /** Sub-agent runtime ID (undefined for main session or older CLI versions). */
32
+ agentId?: string;
33
+ /** Stable sub-agent type, for example `bash-agent`. */
34
+ agentType?: string;
35
+ /** Model already selected by the CLI before the host policy runs. */
36
+ resolvedModel?: string;
31
37
  /** Available models provided by CLI on each `get_model_policy` request (real-time) */
32
38
  availableModels: ModelInfo[];
33
39
  }
@@ -43,7 +49,7 @@ export interface ModelPolicyResult {
43
49
  * The SDK extracts `model` as the identifier and forwards the rest
44
50
  * as `custom_model` on the wire.
45
51
  */
46
- model: string | (CustomModel & {
52
+ model?: string | (CustomModel & {
47
53
  model: string;
48
54
  });
49
55
  /** Optional per-request model policy parameters (contextWindow, reasoningEffort). */
@@ -66,4 +72,4 @@ export interface ModelPolicyResult {
66
72
  * If the callback throws or times out, the error is propagated to the SDK
67
73
  * caller — no silent fallback occurs.
68
74
  */
69
- export type ModelPolicyProvider = (context: ModelPolicyContext) => ModelPolicyResult | Promise<ModelPolicyResult>;
75
+ export type ModelPolicyProvider = (context: ModelPolicyContext) => ModelPolicyResult | undefined | Promise<ModelPolicyResult | undefined>;
@@ -18,6 +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 { EvolutionOptions } from './skill-evolution.js';
22
+ import type { SDKGoalSnapshot, SDKGoalStatus, SDKPlanModeSnapshot } from '../protocol/control.js';
21
23
  export type { CanUseTool };
22
24
  export type { AuthOptions };
23
25
  export type ElicitationRequest = {
@@ -146,19 +148,39 @@ export type Options = {
146
148
  skills?: string[] | 'all';
147
149
  extensions?: string[];
148
150
  /**
149
- * Proxy URL used by the qodercli child process for outbound network traffic.
151
+ * Proxy URL used by qodercli for its own outbound network traffic.
150
152
  *
151
153
  * Supports the same schemes as qodercli: `http://`, `https://`,
152
- * `socks5://`, and `socks://`. When set, the SDK writes this value to
153
- * `HTTPS_PROXY`, `https_proxy`, `HTTP_PROXY`, and `http_proxy` in the child
154
- * 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.
155
160
  */
156
161
  proxy?: string;
162
+ /**
163
+ * VPC private-deployment endpoint for the qodercli child process (CN
164
+ * deployments only).
165
+ *
166
+ * Accepts either a bare instance name (`acme`) or a full VPC domain
167
+ * (`acme.vpc.qoder.com.cn`, `acme-gateway.vpc.qoder.com.cn`,
168
+ * `acme-openapi.vpc.qoder.com.cn`). When set, the SDK writes this value to
169
+ * the `QODERCN_VPC_ENDPOINT` (CN) / `QODER_VPC_ENDPOINT` (Global)
170
+ * environment variable, which the CLI reads at highest priority — above any
171
+ * `vpcInstanceName` in `settings.json` — to derive its inference, OpenAPI,
172
+ * and base endpoints locally without remote endpoint election.
173
+ *
174
+ * VPC mode only activates in CN builds of the CLI; the value is ignored by
175
+ * Global builds.
176
+ */
177
+ vpcEndpoint?: string;
157
178
  env?: {
158
179
  [envVar: string]: string | undefined;
159
180
  };
160
181
  executable?: 'bun' | 'deno' | 'node';
161
182
  executableArgs?: string[];
183
+ /** Additional qodercli flags. `proxy` is reserved; use `proxy` instead. */
162
184
  extraArgs?: Record<string, string | null>;
163
185
  enableFileCheckpointing?: boolean;
164
186
  toolConfig?: ToolConfig;
@@ -188,6 +210,8 @@ export type Options = {
188
210
  * Functions stay in the SDK process and are bridged through control requests.
189
211
  */
190
212
  memory?: MemoryOptions;
213
+ /** Configure component evolution capabilities for this query. */
214
+ evolution?: EvolutionOptions;
191
215
  onElicitation?: OnElicitation;
192
216
  /**
193
217
  * Called when the SDK detects that authentication has expired or
@@ -228,6 +252,13 @@ export type Options = {
228
252
  */
229
253
  pathToQoderCLIExecutable?: string;
230
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;
231
262
  allowDangerouslySkipPermissions?: boolean;
232
263
  /**
233
264
  * MCP tool name to use for permission prompts. Mutually exclusive with
@@ -370,6 +401,30 @@ export interface Query extends AsyncGenerator<SDKMessage, void> {
370
401
  * eligible foreground executions are targeted.
371
402
  */
372
403
  backgroundTasks(toolUseId?: string): Promise<boolean>;
404
+ /**
405
+ * Create, redirect, transition, or re-budget the session goal. Omitted
406
+ * fields are left unchanged; `creditsBudget: null` explicitly clears the
407
+ * budget. Setting `status: 'active'` while the session is idle starts the
408
+ * goal loop. Requires the CLI `goal_v1` capability. Only present on the
409
+ * local CLI transport; absent on cloud-agent queries.
410
+ */
411
+ setGoal?(params: {
412
+ objective?: string;
413
+ status?: SDKGoalStatus;
414
+ creditsBudget?: number | null;
415
+ }): Promise<SDKGoalSnapshot | null>;
416
+ /** Read the current goal, or null when the session has none. */
417
+ getGoal?(): Promise<SDKGoalSnapshot | null>;
418
+ /** Clear the session goal; returns whether a goal was cleared. */
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>;
373
428
  setPermissionMode(mode: PermissionMode): Promise<void>;
374
429
  setModel(model?: string): Promise<void>;
375
430
  /**
@@ -456,6 +511,8 @@ export interface Query extends AsyncGenerator<SDKMessage, void> {
456
511
  seedReadState(path: string, mtime: number): Promise<void>;
457
512
  /** Wait until pending TurnComplete memory generation has finished. */
458
513
  flushMemory(): Promise<void>;
514
+ /** Wait until pending TurnComplete skill evolution review has finished. */
515
+ flushSkillEvolution(): Promise<void>;
459
516
  /** Reload configured memory consumption files without changing config. */
460
517
  refreshMemory(): Promise<void>;
461
518
  /**
@@ -1,4 +1,15 @@
1
1
  import type { PluginInfo } from '../protocol/common.js';
2
+ /** Plugin installation scopes that can be changed through the CLI. */
3
+ export type PluginScope = 'user' | 'project' | 'local';
4
+ export type PluginToggleOptions = {
5
+ /** Directory used to resolve project and local plugin state. */
6
+ cwd?: string;
7
+ /** Omit to let qodercli infer the installed plugin's scope. */
8
+ scope?: PluginScope;
9
+ /** Optional qodercli executable or qoder Worker runtime entry override. */
10
+ cliPath?: string;
11
+ env?: Record<string, string>;
12
+ };
2
13
  export type PluginResources = {
3
14
  skills: {
4
15
  name: string;
@@ -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.24",
3
+ "version": "1.0.26",
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.25",
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,