@qoder-ai/qoder-agent-sdk 1.0.20 → 1.0.22

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.
@@ -30,7 +30,6 @@ const CN_DEV_DOWNLOAD_BASE_URL = 'https://static.qoder.com.cn/qoder-cli-cn/dev/r
30
30
  const PACKAGE_JSON = require('../package.json');
31
31
  const LOG_PREFIX = `[${PACKAGE_JSON.name ?? '@qoder-ai/qoder-agent-sdk'}]`;
32
32
  const WORKER_RUNTIME_ENTRY_NAMES = ['qoder-worker-runtime.obf.mjs', 'qoder-worker-runtime.mjs'];
33
- const REQUIRED_WORKER_RUNTIME_ASSETS = [path.join('vendor', 'ripgrep', 'x64-win32', 'rg.exe')];
34
33
 
35
34
  function isTruthy(value) {
36
35
  return value === '1' || value === 'true';
@@ -143,6 +142,7 @@ function normalizePlatformOverride(value) {
143
142
  'linux-x64',
144
143
  'linux-x64-baseline',
145
144
  'linux-x64-musl',
145
+ 'openharmony-arm64',
146
146
  'windows-arm64',
147
147
  'windows-x64',
148
148
  ]);
@@ -182,6 +182,15 @@ function getPlatformTarget() {
182
182
  }
183
183
  }
184
184
 
185
+ // HarmonyOS reports process.platform === 'openharmony'. Only aarch64 is
186
+ // produced; the artifact is qodercli-openharmony-arm64.tar.gz.
187
+ if (process.platform === 'openharmony') {
188
+ if (process.arch !== 'arm64') {
189
+ throw new Error(`Unsupported OpenHarmony architecture: ${process.arch}`);
190
+ }
191
+ return 'openharmony-arm64';
192
+ }
193
+
185
194
  throw new Error(`Unsupported platform: ${process.platform}/${process.arch}`);
186
195
  }
187
196
 
@@ -199,8 +208,36 @@ function getDownloadUrl() {
199
208
  return `${baseUrl}/${getArtifactName()}`;
200
209
  }
201
210
 
202
- function getWorkerRuntimeArtifactName(site = getCliSite()) {
203
- return `${getCliArtifactPrefix(site)}-worker-runtime-universal.tgz`;
211
+ function getWorkerRuntimeTarget(target = getPlatformTarget()) {
212
+ const [platform, arch] = target.split('-');
213
+ if (platform === 'darwin') return `darwin-${arch}`;
214
+ if (platform === 'linux') return `linux-${arch}`;
215
+ if (platform === 'windows') return `win32-${arch}`;
216
+ 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`,
218
+ );
219
+ }
220
+
221
+ function getWorkerRuntimeArtifactName(site = getCliSite(), target = getPlatformTarget()) {
222
+ const workerTarget = getWorkerRuntimeTarget(target);
223
+ return `${getCliArtifactPrefix(site)}-worker-runtime-${workerTarget}.tgz`;
224
+ }
225
+
226
+ function getWorkerRuntimeArtifactNameTemplate(site = getCliSite()) {
227
+ return `${getCliArtifactPrefix(site)}-worker-runtime-{target}.tgz`;
228
+ }
229
+
230
+ function getWorkerRuntimeRequiredAssets(target = getPlatformTarget()) {
231
+ const workerTarget = getWorkerRuntimeTarget(target);
232
+ const [platform, arch] = workerTarget.split('-');
233
+ return [
234
+ path.join(
235
+ 'vendor',
236
+ 'ripgrep',
237
+ `${arch}-${platform}`,
238
+ platform === 'win32' ? 'rg.exe' : 'rg',
239
+ ),
240
+ ];
204
241
  }
205
242
 
206
243
  function hasWorkerRuntimeDownloadSiteOverride() {
@@ -221,6 +258,14 @@ function getManifestWorkerRuntimeDownloadUrl(manifest = readRuntimeManifest()) {
221
258
  return url ?? null;
222
259
  }
223
260
 
261
+ function getManifestWorkerRuntimeDownloadUrlTemplate(manifest = readRuntimeManifest()) {
262
+ const workerRuntime = getManifestWorkerRuntime(manifest);
263
+ if (!workerRuntime || workerRuntime.packaged === true) return null;
264
+ return typeof workerRuntime.urlTemplate === 'string' && workerRuntime.urlTemplate.length > 0
265
+ ? workerRuntime.urlTemplate
266
+ : null;
267
+ }
268
+
224
269
  function getManifestWorkerRuntimeSha256(manifest = readRuntimeManifest()) {
225
270
  const workerRuntime = getManifestWorkerRuntime(manifest);
226
271
  if (!workerRuntime || workerRuntime.packaged === true) return null;
@@ -236,30 +281,39 @@ function getManifestWorkerRuntimeSha256(manifest = readRuntimeManifest()) {
236
281
  return checksum ? checksum.trim().toLowerCase() : null;
237
282
  }
238
283
 
284
+ function getWorkerRuntimeDownloadUrlTemplate() {
285
+ const version = getCliVersion();
286
+ const baseUrl = normalizeDownloadBaseUrl(getWorkerRuntimeDownloadBaseUrl(version), version);
287
+ return `${baseUrl}/${getWorkerRuntimeArtifactNameTemplate()}`;
288
+ }
289
+
290
+ function getWorkerRuntimeDefaultDownloadUrl() {
291
+ return getWorkerRuntimeDownloadUrlTemplate().replace(
292
+ '{target}',
293
+ getWorkerRuntimeTarget(),
294
+ );
295
+ }
296
+
239
297
  function getWorkerRuntimeDownloadUrl() {
240
298
  if (process.env.QODER_WORKER_RUNTIME_URL) {
241
299
  return process.env.QODER_WORKER_RUNTIME_URL;
242
300
  }
243
301
 
244
- const manifestUrl = hasWorkerRuntimeDownloadSiteOverride()
245
- ? null
246
- : getManifestWorkerRuntimeDownloadUrl();
247
- if (manifestUrl) {
248
- return manifestUrl;
302
+ if (!hasWorkerRuntimeDownloadSiteOverride()) {
303
+ const template = getManifestWorkerRuntimeDownloadUrlTemplate();
304
+ if (template) {
305
+ return template.replace('{target}', getWorkerRuntimeTarget());
306
+ }
307
+ const manifestUrl = getManifestWorkerRuntimeDownloadUrl();
308
+ if (manifestUrl) return manifestUrl;
249
309
  }
250
310
 
251
- const version = getCliVersion();
252
- const baseUrl = normalizeDownloadBaseUrl(getWorkerRuntimeDownloadBaseUrl(version), version);
253
- return `${baseUrl}/${getWorkerRuntimeArtifactName()}`;
311
+ return getWorkerRuntimeDefaultDownloadUrl();
254
312
  }
255
313
 
256
314
  function getWorkerRuntimeExpectedSha256(url = getWorkerRuntimeDownloadUrl()) {
257
315
  const manifestUrl = getManifestWorkerRuntimeDownloadUrl();
258
- if (manifestUrl && manifestUrl === url) {
259
- return getManifestWorkerRuntimeSha256();
260
- }
261
-
262
- return null;
316
+ return manifestUrl === url ? getManifestWorkerRuntimeSha256() : null;
263
317
  }
264
318
 
265
319
  function getBundledDir() {
@@ -365,6 +419,15 @@ function isPackagedWorkerRuntimeCompatibleWithSite(site = getCliSite()) {
365
419
  runtimeInfo.build && typeof runtimeInfo.build === 'object' ? runtimeInfo.build : {};
366
420
  const actualProductName = buildInfo.productName;
367
421
  const actualSite = buildInfo.site;
422
+ const expectedVersion = getCliVersion();
423
+
424
+ if (runtimeInfo.version !== expectedVersion) {
425
+ return false;
426
+ }
427
+
428
+ if (runtimeInfo.profile !== 'platform' || runtimeInfo.target !== getWorkerRuntimeTarget()) {
429
+ return false;
430
+ }
368
431
 
369
432
  if (
370
433
  typeof actualProductName === 'string' &&
@@ -452,8 +515,10 @@ async function downloadFile(url, destPath) {
452
515
  console.log(`${LOG_PREFIX} Downloading: ${url}`);
453
516
  const response = await requestUrl(url);
454
517
  if (response.statusCode !== 200) {
518
+ const error = new Error(`Download failed: HTTP ${response.statusCode}`);
519
+ error.statusCode = response.statusCode;
455
520
  response.resume();
456
- throw new Error(`Download failed: HTTP ${response.statusCode}`);
521
+ throw error;
457
522
  }
458
523
 
459
524
  await new Promise((resolve, reject) => {
@@ -496,6 +561,7 @@ async function downloadWithRetry(url, destPath, maxRetries = 2) {
496
561
  lastError = error;
497
562
  console.error(`${LOG_PREFIX} Download failed: ${error.message}`);
498
563
  fs.rmSync(destPath, { force: true });
564
+ if (error.statusCode === 404) break;
499
565
  }
500
566
  }
501
567
  throw lastError;
@@ -699,7 +765,7 @@ function assertWorkerRuntimeComplete(runtimeDir, sourceDescription) {
699
765
  throw new Error(`Worker runtime entry missing in ${sourceDescription}: ${runtimePath}`);
700
766
  }
701
767
 
702
- const missingAssets = REQUIRED_WORKER_RUNTIME_ASSETS.filter(
768
+ const missingAssets = getWorkerRuntimeRequiredAssets().filter(
703
769
  (assetPath) => !fs.existsSync(path.join(runtimeDir, assetPath)),
704
770
  );
705
771
  if (missingAssets.length > 0) {
@@ -715,7 +781,49 @@ function assertWorkerRuntimeComplete(runtimeDir, sourceDescription) {
715
781
  return runtimePath;
716
782
  }
717
783
 
718
- function installExtractedWorkerRuntime(extractDir, workerDir) {
784
+ function assertWorkerRuntimeIdentity(runtimeDir) {
785
+ const runtimeInfoPath = path.join(runtimeDir, 'runtime-info.json');
786
+ if (!fs.existsSync(runtimeInfoPath)) {
787
+ throw new Error(`Downloaded Worker runtime is missing identity metadata: ${runtimeInfoPath}`);
788
+ }
789
+
790
+ let runtimeInfo;
791
+ try {
792
+ runtimeInfo = JSON.parse(fs.readFileSync(runtimeInfoPath, 'utf8'));
793
+ } catch (error) {
794
+ throw new Error(`Downloaded Worker runtime has invalid identity metadata: ${error.message}`);
795
+ }
796
+
797
+ const expectedTarget = getWorkerRuntimeTarget();
798
+ const expectedSite = getCliSite();
799
+ const expectedProductName = getCliArtifactPrefix(expectedSite);
800
+ const expectedVersion = getCliVersion();
801
+ const mismatches = [];
802
+
803
+ if (runtimeInfo.profile !== 'platform') {
804
+ mismatches.push(`profile=${String(runtimeInfo.profile)} (expected platform)`);
805
+ }
806
+ if (runtimeInfo.target !== expectedTarget) {
807
+ mismatches.push(`target=${String(runtimeInfo.target)} (expected ${expectedTarget})`);
808
+ }
809
+ if (runtimeInfo.version !== expectedVersion) {
810
+ mismatches.push(`version=${String(runtimeInfo.version)} (expected ${expectedVersion})`);
811
+ }
812
+ if (runtimeInfo.build?.site !== expectedSite) {
813
+ mismatches.push(`site=${String(runtimeInfo.build?.site)} (expected ${expectedSite})`);
814
+ }
815
+ if (runtimeInfo.build?.productName !== expectedProductName) {
816
+ mismatches.push(
817
+ `productName=${String(runtimeInfo.build?.productName)} (expected ${expectedProductName})`,
818
+ );
819
+ }
820
+
821
+ if (mismatches.length > 0) {
822
+ throw new Error(`Downloaded Worker runtime identity mismatch: ${mismatches.join(', ')}`);
823
+ }
824
+ }
825
+
826
+ function installExtractedWorkerRuntime(extractDir, workerDir, options = {}) {
719
827
  const contentRoot = findWorkerRuntimeRoot(extractDir);
720
828
  if (!contentRoot) {
721
829
  throw new Error(
@@ -723,6 +831,9 @@ function installExtractedWorkerRuntime(extractDir, workerDir) {
723
831
  );
724
832
  }
725
833
  assertWorkerRuntimeComplete(contentRoot, contentRoot);
834
+ if (options.validateIdentity === true) {
835
+ assertWorkerRuntimeIdentity(contentRoot);
836
+ }
726
837
 
727
838
  fs.rmSync(workerDir, { recursive: true, force: true });
728
839
  copyDirContents(contentRoot, workerDir);
@@ -734,24 +845,36 @@ function installExtractedWorkerRuntime(extractDir, workerDir) {
734
845
  return runtimePath;
735
846
  }
736
847
 
737
- async function installWorkerRuntimeFromDownload() {
738
- const url = getWorkerRuntimeDownloadUrl();
739
- const expectedSha256 = getWorkerRuntimeExpectedSha256(url);
848
+ async function downloadAndInstallWorkerRuntime(workerDir, options = {}) {
849
+ const url = options.urlOverride ?? getWorkerRuntimeDownloadUrl();
850
+ const expectedSha256 = options.urlOverride ? null : getWorkerRuntimeExpectedSha256(url);
740
851
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'qoder-worker-runtime-'));
741
852
  const archivePath = path.join(tempDir, path.basename(new URL(url).pathname));
742
853
  const extractDir = path.join(tempDir, 'extract');
743
854
 
744
855
  try {
856
+ fs.mkdirSync(extractDir, { recursive: true });
745
857
  await downloadWithRetry(url, archivePath);
746
858
  await verifyChecksum(url, archivePath, expectedSha256);
747
- fs.mkdirSync(extractDir, { recursive: true });
748
859
  await extractArchive(archivePath, extractDir);
749
- return installExtractedWorkerRuntime(extractDir, getPackagedWorkerRuntimeDir());
860
+ return {
861
+ runtimePath: installExtractedWorkerRuntime(extractDir, workerDir, {
862
+ validateIdentity: true,
863
+ }),
864
+ url,
865
+ };
750
866
  } finally {
751
867
  fs.rmSync(tempDir, { recursive: true, force: true });
752
868
  }
753
869
  }
754
870
 
871
+ async function installWorkerRuntimeFromDownload() {
872
+ const installed = await downloadAndInstallWorkerRuntime(
873
+ getPackagedWorkerRuntimeDir(),
874
+ );
875
+ return installed.runtimePath;
876
+ }
877
+
755
878
  async function main() {
756
879
  if (isTruthy(process.env.QODER_SKIP_DOWNLOAD)) {
757
880
  console.log(`${LOG_PREFIX} QODER_SKIP_DOWNLOAD is set, skipping runtime downloads.`);
@@ -839,6 +962,8 @@ if (require.main === module) {
839
962
  }
840
963
 
841
964
  module.exports = {
965
+ assertWorkerRuntimeIdentity,
966
+ downloadAndInstallWorkerRuntime,
842
967
  downloadWithRetry,
843
968
  extractArchive,
844
969
  getTarExtractInvocation,
@@ -860,8 +985,13 @@ module.exports = {
860
985
  getDefaultRuntimeTransport,
861
986
  getWorkerRuntimeExpectedSha256,
862
987
  getWorkerRuntimeArtifactName,
988
+ getWorkerRuntimeArtifactNameTemplate,
863
989
  getWorkerRuntimeDownloadBaseUrl,
864
990
  getWorkerRuntimeDownloadUrl,
991
+ getWorkerRuntimeDownloadUrlTemplate,
992
+ getWorkerRuntimeRequiredAssets,
993
+ getWorkerRuntimeTarget,
994
+ getManifestWorkerRuntimeDownloadUrlTemplate,
865
995
  hasPackagedWorkerRuntime,
866
996
  installExtractedContents,
867
997
  installExtractedWorkerRuntime,