@h-ai/cli 1.0.0-beta.5 → 1.0.0-beta.6

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 (54) hide show
  1. package/README.en.md +21 -7
  2. package/README.ja.md +21 -7
  3. package/README.md +21 -7
  4. package/dist/{chunk-VUWM43KR.js → chunk-4XVCGFBA.js} +6123 -714
  5. package/dist/chunk-4XVCGFBA.js.map +1 -0
  6. package/dist/{chunk-YHSX7AQX.js → chunk-7DNIMG2H.js} +89 -14
  7. package/dist/chunk-7DNIMG2H.js.map +1 -0
  8. package/dist/{chunk-YVNNMC4H.js → chunk-STWZQ3IP.js} +59 -3
  9. package/dist/chunk-STWZQ3IP.js.map +1 -0
  10. package/dist/cli-config-templates-YGPVNRX5.js +5 -0
  11. package/dist/{cli-config-templates-LUJGDQNW.js.map → cli-config-templates-YGPVNRX5.js.map} +1 -1
  12. package/dist/{cli-skill-templates-COTP767H.js → cli-skill-templates-S2YH642Z.js} +3 -3
  13. package/dist/{cli-skill-templates-COTP767H.js.map → cli-skill-templates-S2YH642Z.js.map} +1 -1
  14. package/dist/index.js +362 -359
  15. package/dist/index.js.map +1 -1
  16. package/dist/metafile-esm.json +1 -1
  17. package/dist/src-NXCCU6GO.js +5 -0
  18. package/dist/src-NXCCU6GO.js.map +1 -0
  19. package/package.json +11 -11
  20. package/templates/README.md +4 -0
  21. package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +1 -1
  22. package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +4 -4
  23. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +41 -2
  24. package/templates/apps/api/images/serv.Dockerfile.hbs +11 -6
  25. package/templates/apps/fullstack/docs/security-configuration.md.hbs +1 -1
  26. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +11 -6
  27. package/templates/apps/fullstack/images/web.Dockerfile.hbs +11 -5
  28. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +4 -4
  29. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +4 -4
  30. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +1 -2
  31. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +26 -2
  32. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +0 -1
  33. package/templates/base/images/web.Dockerfile.hbs +20 -10
  34. package/templates/base/package.json.hbs +3 -3
  35. package/templates/base/src/lib/server/init.ts.hbs +40 -2
  36. package/templates/shared/run-node.mjs +45 -0
  37. package/templates/skills/hai-ai/guide.md +1 -1
  38. package/templates/skills/hai-app-create/development.md +8 -7
  39. package/templates/skills/hai-build/SKILL.md +4 -1
  40. package/templates/skills/hai-build/reference.md +15 -1
  41. package/templates/skills/hai-core/SKILL.md +2 -2
  42. package/templates/skills/hai-core/reference.md +3 -3
  43. package/templates/skills/hai-core/request-access.md +1 -1
  44. package/templates/skills/hai-crypto/reference.md +1 -1
  45. package/templates/skills/hai-deploy/SKILL.md +8 -4
  46. package/templates/skills/hai-deploy/middleware.md +9 -9
  47. package/templates/skills/hai-iam/reference.md +2 -2
  48. package/templates/skills/hai-serv/reference.md +1 -1
  49. package/dist/chunk-VUWM43KR.js.map +0 -1
  50. package/dist/chunk-YHSX7AQX.js.map +0 -1
  51. package/dist/chunk-YVNNMC4H.js.map +0 -1
  52. package/dist/cli-config-templates-LUJGDQNW.js +0 -5
  53. package/dist/src-24PXSKE6.js +0 -5
  54. package/dist/src-24PXSKE6.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, generateProductionEnvironment, moduleEnvironmentVariables, documentConfig, parameterizeConfig } from './chunk-YVNNMC4H.js';
3
- import { cliM } from './chunk-YHSX7AQX.js';
4
- import { external_exports, normalizeProjectName } from './chunk-VUWM43KR.js';
2
+ import { generateConfigFile, runtimeEnvironmentVariables, renderEnvironment, generateProductionEnvironment, moduleEnvironmentVariables, documentConfig, parameterizeConfig } from './chunk-STWZQ3IP.js';
3
+ import { cliM } from './chunk-7DNIMG2H.js';
4
+ import { normalizeProjectName, resolveImageReference, resolveDownloadProxy } from './chunk-4XVCGFBA.js';
5
5
  import './chunk-ZSDEVXZ7.js';
6
- import process4 from 'process';
6
+ import process5 from 'process';
7
7
  import { cac } from 'cac';
8
8
  import chalk from 'chalk';
9
9
  import path2, { resolve, dirname, join, relative, isAbsolute } from 'path';
@@ -14,11 +14,15 @@ import Handlebars2 from 'handlebars';
14
14
  import { parseEnv } from 'util';
15
15
  import ora from 'ora';
16
16
  import prompts from 'prompts';
17
- import { parse, stringify, parseAllDocuments, parseDocument } from 'yaml';
17
+ import { parse, stringify, parseAllDocuments } from 'yaml';
18
18
  import { Buffer } from 'buffer';
19
19
  import { execSync, execFileSync, spawn } from 'child_process';
20
20
  import { randomBytes, createECDH, randomUUID, createHash } from 'crypto';
21
21
  import { readFileSync, existsSync, mkdirSync, mkdtempSync, appendFileSync, createReadStream, writeFileSync, renameSync, rmSync, realpathSync, readdirSync, statSync, copyFileSync } from 'fs';
22
+ import { connect } from 'net';
23
+ import { Writable, Transform } from 'stream';
24
+ import { pipeline } from 'stream/promises';
25
+ import { StringDecoder } from 'string_decoder';
22
26
  import { createRequire, findPackageJSON } from 'module';
23
27
  import { core, ok, err as err$1, HaiCommonError } from '@h-ai/core';
24
28
  import { stat, readFile, glob } from 'fs/promises';
@@ -28,20 +32,20 @@ import { gunzipSync } from 'zlib';
28
32
  var CAMEL_CASE_SEPARATOR_REGEX = /[-_\s]+(.)?/g;
29
33
  var LEADING_CHAR_REGEX = /^./;
30
34
  function writeCliMessage(message) {
31
- process4.stdout.write(`${message}
35
+ process5.stdout.write(`${message}
32
36
  `);
33
37
  }
34
38
  function writeCliError(message, error) {
35
- process4.stderr.write(`${message}
39
+ process5.stderr.write(`${message}
36
40
  `);
37
41
  if (error && typeof error === "object" && "error" in error)
38
42
  error = error.error;
39
43
  if (error !== void 0)
40
- process4.stderr.write(`${error instanceof Error ? error.message : String(error)}
44
+ process5.stderr.write(`${error instanceof Error ? error.message : String(error)}
41
45
  `);
42
46
  }
43
47
  function formatPromptDescription(description) {
44
- return wrapAnsi(description, Math.max(1, (process4.stdout.columns || 80) - 32), { hard: true });
48
+ return wrapAnsi(description, Math.max(1, (process5.stdout.columns || 80) - 32), { hard: true });
45
49
  }
46
50
  function toCamelCase(str) {
47
51
  return str.replace(CAMEL_CASE_SEPARATOR_REGEX, (_, c) => c ? c.toUpperCase() : "").replace(LEADING_CHAR_REGEX, (c) => c.toLowerCase());
@@ -828,6 +832,7 @@ async function generateFromTemplates(projectPath, context) {
828
832
  }
829
833
  await renderDynamicFiles(root, projectPath, context);
830
834
  await fse.copy(path2.join(root, "shared", "run-e2e.mjs"), path2.join(projectPath, "scripts", "run-e2e.mjs"));
835
+ await fse.copy(path2.join(root, "shared", "run-node.mjs"), path2.join(projectPath, "scripts", "run-node.mjs"));
831
836
  await fse.copy(path2.join(root, "shared", "e2e-env.ts"), path2.join(projectPath, "scripts", "e2e-env.ts"));
832
837
  await fse.copy(path2.join(root, "shared", "build-metadata.ts"), path2.join(projectPath, "scripts", "build-metadata.ts"));
833
838
  await fse.copy(path2.join(root, "shared", "public-base-path.mjs"), path2.join(projectPath, "scripts", "public-base-path.mjs"));
@@ -1266,7 +1271,7 @@ async function createProject(options) {
1266
1271
  });
1267
1272
  await generateFromTemplates(projectPath, context);
1268
1273
  {
1269
- const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-LUJGDQNW.js');
1274
+ const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-YGPVNRX5.js');
1270
1275
  const configs = resolvedOptions.moduleConfigs;
1271
1276
  const isApiWorkspace = resolvedOptions.appType === "api";
1272
1277
  const isFullstack = resolvedOptions.appType === "fullstack";
@@ -1333,13 +1338,14 @@ ${runtimeExample.split("\n").map((line) => line && !line.startsWith("#") ? `# ${
1333
1338
  ${sharedVariables.join("\n")}
1334
1339
  `);
1335
1340
  for (const [mode, environment] of [["dev", "development"], ["test", "test"], ["prod", "production"]]) {
1336
- let values = mode === "prod" ? generateProductionEnvironment2(envFeatures, configs) : `# \u516C\u5171 ${mode} \u73AF\u5883\u914D\u7F6E\uFF1B\u672C\u673A\u79C1\u6709\u503C\u653E .env.local\uFF0C\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF\u4F18\u5148\u3002
1341
+ let privateValues = "# \u6A21\u5F0F\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4EA4 Git\uFF1B\u5404\u8282\u70B9\u901A\u8FC7\u53D7\u7BA1\u914D\u7F6E\u5171\u4EAB\u5BF9\u5E94\u79D8\u5BC6\u3002\n";
1342
+ const values = mode === "prod" ? generateProductionEnvironment2(envFeatures, configs) : `# \u516C\u5171 ${mode} \u73AF\u5883\u914D\u7F6E\uFF1B\u672C\u673A\u79C1\u6709\u503C\u653E .env.local\uFF0C\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF\u4F18\u5148\u3002
1337
1343
  HAI_CORE_ENV=${environment}
1338
1344
  HAI_CORE_LOGGING_LEVEL=${mode === "test" ? "warn" : "debug"}
1339
1345
  `;
1340
1346
  if (isFullstack) {
1341
1347
  for (const name of ["HAI_ADMIN_DEFAULT_PASSWORD", "HAI_AUDIT_ADMIN_PASSWORD", "HAI_CONFIG_ADMIN_PASSWORD", "HAI_BUSINESS_OPERATOR_PASSWORD"])
1342
- values += `
1348
+ privateValues += `
1343
1349
  ${name}=Aa1!${randomBytes(24).toString("base64url")}
1344
1350
  `;
1345
1351
  }
@@ -1348,15 +1354,16 @@ ${name}=Aa1!${randomBytes(24).toString("base64url")}
1348
1354
  key.generateKeys();
1349
1355
  const id = `${mode}-${randomBytes(6).toString("hex")}`;
1350
1356
  const ring = { activeKeyId: id, keys: { [id]: { publicKey: key.getPublicKey("hex"), privateKey: key.getPrivateKey("hex").padStart(64, "0") } } };
1351
- values += `
1357
+ privateValues += `
1352
1358
  HAI_CRYPTO_TRANSPORT_SERVERKEYRING='${JSON.stringify(ring)}'
1353
1359
  `;
1354
1360
  }
1355
1361
  await writeFile(path2.join(envDirectory, `.env.${mode}`), values);
1362
+ await fse.writeFile(path2.join(envDirectory, `.env.${mode}.local`), privateValues, { mode: 384 });
1356
1363
  }
1357
- await fse.writeFile(path2.join(envDirectory, ".env.local"), "# \u672C\u673A\u79C1\u6709\u8986\u76D6\uFF0C\u4E0D\u63D0\u4EA4 Git\uFF1B\u521D\u59CB\u7BA1\u7406\u5458\u5BC6\u7801\u548C\u4F20\u8F93\u5BC6\u94A5\u73AF\u89C1 .env.dev / .env.test / .env.prod\u3002\n", { mode: 384 });
1364
+ await fse.writeFile(path2.join(envDirectory, ".env.local"), "# \u672C\u673A\u901A\u7528\u79C1\u6709\u8986\u76D6\uFF0C\u4E0D\u63D0\u4EA4 Git\uFF1B\u521D\u59CB\u5BC6\u7801\u548C\u4F20\u8F93\u5BC6\u94A5\u73AF\u5206\u522B\u4F4D\u4E8E .env.dev.local / .env.test.local / .env.prod.local\u3002\n", { mode: 384 });
1358
1365
  }
1359
- const { generateSkillFiles } = await import('./cli-skill-templates-COTP767H.js');
1366
+ const { generateSkillFiles } = await import('./cli-skill-templates-S2YH642Z.js');
1360
1367
  await generateSkillFiles(
1361
1368
  projectPath,
1362
1369
  resolvedOptions.features,
@@ -1403,7 +1410,7 @@ async function resolveOptions(options) {
1403
1410
  writeCliMessage("");
1404
1411
  const onCancel = () => {
1405
1412
  writeCliMessage(chalk.red(cliM("cli_cancelledLine")));
1406
- process4.exit(1);
1413
+ process5.exit(1);
1407
1414
  };
1408
1415
  let projectName = options.name;
1409
1416
  if (!projectName && useDefaults) {
@@ -1607,7 +1614,7 @@ async function promptModuleConfigs(features, projectName) {
1607
1614
  const configs = {};
1608
1615
  const onCancel = () => {
1609
1616
  writeCliMessage(chalk.red(cliM("cli_cancelledLine")));
1610
- process4.exit(1);
1617
+ process5.exit(1);
1611
1618
  };
1612
1619
  writeCliMessage("");
1613
1620
  writeCliMessage(chalk.bold(cliM("cli_createModuleConfig")));
@@ -1848,7 +1855,7 @@ function generateAiDevelopmentReadme() {
1848
1855
  function generateEnvironmentReadme() {
1849
1856
  return `### \u5206\u73AF\u5883\u914D\u7F6E
1850
1857
 
1851
- \u8FD0\u884C\u53C2\u6570\u5728 \`.env.example\` \u4E2D\u9010\u9879\u8BF4\u660E\u7528\u9014\u3001\u679A\u4E3E\u548C\u8303\u56F4\u3002\`.env\` \u4E3A\u516C\u5171\u57FA\u7840\uFF0C\`.env.dev\` / \`.env.test\` / \`.env.prod\` \u4E3A\u73AF\u5883\u8986\u76D6\uFF0C\u5747\u63D0\u4EA4 Git\uFF1B\`.env.local\` \u4E3A\u672C\u673A\u79C1\u6709\u8986\u76D6\uFF0C\u4E0D\u63D0\u4EA4 Git\u3002\u4F18\u5148\u7EA7\u4E3A\u8FDB\u7A0B\u73AF\u5883 > \`.env.local\` > \u73AF\u5883\u6587\u4EF6 > \`.env\`\u3002YAML \u663E\u5F0F\u5F15\u7528 \`HAI_<\u914D\u7F6E\u540D>_<\u8DEF\u5F84>\` \u5E76\u4FDD\u7559\u672C\u5730\u56DE\u9000\uFF0CcamelCase \u4E0D\u62C6\u8BCD\u3002\u5B8C\u6574\u547D\u4EE4\u3001\u52A0\u8F7D\u4F18\u5148\u7EA7\u548C Provider \u5207\u6362\u89C1 [\u73AF\u5883\u914D\u7F6E](.agents/skills/hai-app-create/development.md#\u73AF\u5883\u914D\u7F6E)\u3002
1858
+ \u8FD0\u884C\u53C2\u6570\u5728 \`.env.example\` \u4E2D\u9010\u9879\u8BF4\u660E\u7528\u9014\u3001\u679A\u4E3E\u548C\u8303\u56F4\u3002\`.env\` \u4E3A\u516C\u5171\u57FA\u7840\uFF0C\`.env.dev\` / \`.env.test\` / \`.env.prod\` \u4E3A\u73AF\u5883\u8986\u76D6\uFF0C\u5747\u63D0\u4EA4 Git\uFF1B\`.env.local\` \u4E3A\u672C\u673A\u79C1\u6709\u8986\u76D6\uFF0C\u4E0D\u63D0\u4EA4 Git\u3002\u4F18\u5148\u7EA7\u4E3A\u8FDB\u7A0B > \`.env.<mode>.local\` > \`.env.local\` > \`.env.<mode>\` > \`.env\` > YAML\u3002\u540C\u6765\u6E90\u5148\u7236 JSON \u540E\u7EC6\u9879\uFF0C\u9AD8\u6765\u6E90\u6574\u6BB5\u66FF\u6362\u5148\u6E05\u9664\u4F4E\u6765\u6E90\u7EC6\u9879\u3002YAML \u663E\u5F0F\u5F15\u7528 \`HAI_<\u914D\u7F6E\u540D>_<\u8DEF\u5F84>\` \u5E76\u4FDD\u7559\u672C\u5730\u56DE\u9000\uFF0CcamelCase \u4E0D\u62C6\u8BCD\u3002\u5B8C\u6574\u547D\u4EE4\u3001\u52A0\u8F7D\u4F18\u5148\u7EA7\u548C Provider \u5207\u6362\u89C1 [\u73AF\u5883\u914D\u7F6E](.agents/skills/hai-app-create/development.md#\u73AF\u5883\u914D\u7F6E)\u3002
1852
1859
 
1853
1860
  \u751F\u4EA7 .env.prod \u6309\u5DF2\u542F\u7528\u6A21\u5757\u9009\u62E9\u5171\u4EAB PostgreSQL\uFF08\u660E\u786E\u9009\u62E9 MySQL \u65F6\u4FDD\u7559\uFF09\u3001Redis\u3001S3\u3001Qdrant\uFF0C\u9ED8\u8BA4\u4F7F\u7528 Compose \u5185\u90E8\u670D\u52A1\u540D db/cache/storage/vecdb\u3002\u5BC6\u7801\u3001S3 \u51ED\u636E\u3001\u6A21\u578B Key \u4E0E\u53D7\u4FDD\u62A4 Qdrant \u7684 Key \u53EA\u7531\u79C1\u6709\u73AF\u5883\u6CE8\u5165\uFF1B\u5BB9\u5668\u5916\u8FD0\u884C\u8986\u76D6\u5185\u90E8\u8FDE\u63A5\u5730\u5740\u3002\u751F\u4EA7 Reach \u5FC5\u987B\u6CE8\u5165\u771F\u5B9E HAI_REACH_PROVIDERS JSON\uFF0C\u516C\u5171\u7A7A\u6570\u7EC4\u4F1A\u62D2\u7EDD\u542F\u52A8\u3002AI \u81EA\u52A8\u8865\u5168 db+vecdb \u4F9D\u8D56\uFF0C\u5E76\u5728\u4E8C\u8005\u5C31\u7EEA\u540E\u521D\u59CB\u5316\uFF0C\u5931\u8D25\u9006\u5E8F\u6E05\u7406\u3002
1854
1861
 
@@ -1873,6 +1880,7 @@ function generateBuildDeploymentReadme(pm, appDir) {
1873
1880
 
1874
1881
  \u81EA\u5EFAS3\u7684\u5185\u90E8endpoint\u4E0E\u6D4F\u89C8\u5668publicEndpoint\u5206\u5F00\u3002publicEndpoint\u53EF\u8BBE\u7F6E\u4E3Ahttps://example.com/delivery/files\u7B49\u5E26\u76EE\u5F55\u5730\u5740\uFF0C\u4E0D\u542B\u6876\u540D\u3001\u51ED\u636E\u6216\u67E5\u8BE2\uFF1B\u8FD4\u56DEURL\u4FDD\u7559\u6876\u540D\uFF0CWeb Nginx\u6216SSR/API gateway\u53BB\u6389\u5165\u53E3\u524D\u7F00\uFF0C\u4FDD\u7559\u539F\u59CBS3\u8DEF\u5F84\u3001Host\u4E0E\u67E5\u8BE2\u540E\u76F4\u63A5\u8F6C\u53D1storage\uFF0C\u5927\u6587\u4EF6\u4E0D\u7ECF\u8FC7Serv\u3002\u5916\u5C42\u4EE3\u7406\u4FDD\u7559\u5B8C\u6574\u5165\u53E3\u8DEF\u5F84\uFF0C\u4E0D\u91CD\u590D\u53BB\u524D\u7F00\uFF1B\u5916\u90E8S3/CORS\u6309\u5B9E\u9645\u670D\u52A1\u914D\u7F6E\uFF0CLocal\u4E0D\u516C\u5F00\u79C1\u6709\u76EE\u5F55\u3002\u589E\u52A0\u7F51\u5173\u3001\u6539\u53D8\u6876\u6216\u516C\u5F00\u5730\u5740\u65F6\u5168\u91CF\u90E8\u7F72\uFF0C\u5E94\u7528\u589E\u91CF\u4E0E\u56DE\u6EDA\u4FDD\u6301\u539F\u914D\u7F6E\uFF1B\u8BE6\u89C1[\u5B58\u50A8\u4F7F\u7528\u89C4\u8303](.agents/skills/hai-storage/SKILL.md)\u3002
1875
1882
  \u6BCF\u4E2A\u955C\u50CF\u5BF9\u5E94 images/ \u4E2D\u4E00\u4E2A Dockerfile\uFF0C\u6839 hai.json \u7684 build \u5206\u533A\u6307\u5B9A\u6587\u4EF6\uFF0Cdeploy \u5206\u533A\u4FDD\u5B58\u6574\u4E2A\u9879\u76EE\u7684\u90E8\u7F72\u914D\u7F6E\u3002
1883
+ \u955C\u50CF\u6784\u5EFA\u53EF\u52A0 --image-source cn --npm-source cn\uFF0C\u5206\u522B\u9009\u62E9\u4E2D\u56FD\u5927\u9646\u955C\u50CF\u6E90\u4E0E npm \u6E90\uFF1B\u8FD8\u652F\u6301 official/custom\u3002\u9996\u6B21\u4EA4\u4E92\u9009\u62E9\u4FDD\u5B58\u5230 hai.json.build.sources\uFF0C--yes \u590D\u7528\u914D\u7F6E\uFF0C\u672A\u914D\u7F6E\u65F6\u4F7F\u7528\u5B98\u65B9\u6E90\u3002\u4EE3\u7406\u7528 --proxy-mode inherit|system|custom|none\uFF0C\u81EA\u5B9A\u4E49\u914D\u5408 --proxy-url\uFF1BDocker/Podman \u5F15\u64CE\u62C9\u53D6\u4EE3\u7406\u9700\u5728\u5F15\u64CE\u4FA7\u8BBE\u7F6E\uFF0C\u6784\u5EFA\u5BB9\u5668\u4F1A\u5355\u72EC\u68C0\u67E5\u4EE3\u7406\u53EF\u8FBE\u6027\u3002\u81EA\u5B9A\u4E49\u6E90\u548C\u65E7\u5DE5\u7A0B Dockerfile \u66F4\u65B0\u89C1 .agents/skills/hai-build/reference.md \u7684\u4E0B\u8F7D\u6E90\u4E0E\u4EE3\u7406\u8BF4\u660E\u3002
1876
1884
  \u4E0B\u8F7D\u3001\u5B89\u88C5\u548C\u6574\u7406\u751F\u4EA7\u4F9D\u8D56\u590D\u7528\u540C\u4E00\u4EFD\u5305\u7F13\u5B58\u4E0E SQLite \u9A71\u52A8\u7F13\u5B58\uFF0C\u4F9D\u8D56\u5B89\u88C5\u5148\u4E8E\u6E90\u7801\u590D\u5236\u3002\u524D\u540E\u7AEF\u9ED8\u8BA4\u6700\u591A\u5E76\u884C\u6784\u5EFA\u4E24\u9879\uFF0C--concurrency 1\u53EF\u964D\u4F4E\u8D44\u6E90\u5360\u7528\u3002\u8FD0\u884C\u955C\u50CF\u53BB\u9664\u5E94\u7528\u81EA\u8EAB\u7684\u58F0\u660E\u6587\u4EF6\u3001\u6E90\u7801\u6620\u5C04\u548C\u8BF4\u660E\u6587\u4EF6\u3002
1877
1885
  \u9ED8\u8BA4\u5BB9\u5668\u7528\u4E8E CPU \u8FD0\u884C\u6216\u8FDE\u63A5\u8FDC\u7A0B AI \u670D\u52A1\uFF0C\u4E0D\u989D\u5916\u4E0B\u8F7D GPU \u8FD0\u884C\u5E93\uFF1B\u9700\u8981\u4F7F\u7528\u90E8\u7F72\u673A\u5668\u4E0A\u7684 GPU \u65F6\uFF0C\u81EA\u884C\u5B9A\u5236\u955C\u50CF\u548C\u5BB9\u5668\u8FD0\u884C\u53C2\u6570\u3002
1878
1886
 
@@ -1896,7 +1904,7 @@ ${pm} exec hai deploy ${appDir} --env prod --services web
1896
1904
  \u6BCF\u6B21\u79BB\u7EBF\u4EA4\u4ED8\u751F\u6210\u72EC\u7ACB\u76EE\u5F55\uFF0C\u9ED8\u8BA4tar.gz\uFF08\u663E\u5F0F--output release.tar\u53EF\u4FDD\u7559\u672A\u538B\u7F29\uFF09\uFF0C\u5916\u5C42\u4EC5\u542B\u5F52\u6863\u3001deploy.ps1\uFF08Windows\uFF09\u3001deploy.sh\uFF08Linux/macOS\uFF09\u3001deploy-target.json\u3001read-target.awk\u3001deploy-local.sh \u548C README.txt\uFF1BCompose\u3001\u8FD0\u884C\u73AF\u5883\u548C\u6E05\u5355\u4EC5\u5728\u5F52\u6863\u5185\uFF0CREADME\u4E0D\u91CD\u590D\u3002\u4E2D\u95F4\u4EF6\u5F52\u6863\u6309\u955C\u50CF\u5185\u5BB9ID\u7F13\u5B58\u5E76\u5728\u590D\u7528\u524D\u9A8C\u8BC1\u6458\u8981\u3002\u6210\u529F\u540E\u6E05\u7406\u672C\u6B21incoming\u4E0A\u4F20\u4E0E\u89E3\u5305\u76EE\u5F55\uFF0Crelease\u4FDD\u7559\u6062\u590D\u914D\u7F6E\u548C\u72B6\u6001\uFF0C\u955C\u50CF\u7531\u5F15\u64CE\u4FDD\u5B58\u3002\u4EA4\u4E92\u5F0F\u6253\u5305\u5728\u6784\u5EFA\u548C\u4E2D\u95F4\u4EF6\u914D\u7F6E\u540E\u6700\u540E\u8BE2\u95EE\u662F\u5426\u4FDD\u5B58\u6216\u4FEE\u6539 SSH \u4FE1\u606F\uFF0C\u5199\u5165 .env.<\u73AF\u5883>.local \u5E76\u7528\u4E8E\u672C\u6B21\u548C\u540E\u7EED target\uFF1B--yes \u590D\u7528\u5DF2\u6709\u914D\u7F6E\uFF0C\u590D\u5236\u6574\u4E2A\u76EE\u5F55\u5373\u53EF\u3002\u811A\u672C\u6309\u5DF2\u4FDD\u5B58\u7684 SSH \u53C2\u6570\u4E0A\u4F20\u3001\u5BFC\u5165\u3001\u542F\u52A8\u5E76\u9A8C\u8BC1 HTTP\u6216HTTPS\u3002
1897
1905
  \u90E8\u7F72\u5411\u5BFC\u53EF\u9009\u5DF2\u6709PEM\u8BC1\u4E66\u4E0E\u79C1\u94A5\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u7A0B\u6839\uFF0C\u6821\u9A8C\u540E\u590D\u5236\u5230\u79C1\u6709\u5305\uFF09\u6216Let\u2019s Encrypt\u3002\u5B98\u65B9Certbot\u7533\u8BF7\u5E76\u6BCF12\u5C0F\u65F6\u68C0\u67E5\u7EED\u671F\uFF0CNginx\u81EA\u52A8\u91CD\u8F7D\uFF1B\u9700\u8981\u6761\u6B3E\u786E\u8BA4\u3001\u771F\u5B9E\u90AE\u7BB1\u3001\u516C\u7F51DNS\u300180\u7AEF\u53E3\u53CACA\u7F51\u7EDC\u3002HTTPS\u9ED8\u8BA4443\u3001HTTP\u8DF3\u8F6C\u5F00\u542F\uFF0C\u672A\u914D\u7F6E\u4ECD\u4E3AHTTP\uFF1B\u542F\u505C\u6216\u6539\u6A21\u5F0F/\u57DF\u540D/\u7AEF\u53E3/\u672C\u5730\u8BC1\u4E66\u987B\u5168\u91CF\u3002\u8BC1\u4E66\u72B6\u6001\u4FDD\u7559\u5728\u670D\u52A1\u5668\u5E76\u5907\u4EFD\uFF0C\u9694\u79BB\u76EE\u6807\u4F7F\u7528\u5DF2\u6709\u8BC1\u4E66\u3002--plan\u4E2D\u7684root\u8868\u793A\u5DE5\u7A0B\u6839\uFF0CserviceDir\u8868\u793A\u90E8\u7F72\u670D\u52A1\u6E90\u7801\u4E0E\u914D\u7F6E\u76EE\u5F55\u3002
1898
1906
  \u5B8C\u5168\u9694\u79BB\u65F6\u590D\u5236\u6574\u4E2A\u4EA4\u4ED8\u76EE\u5F55\uFF0C\u8FD0\u884C sh deploy-local.sh \u81EA\u52A8\u89E3\u5305\u5E76\u90E8\u7F72\uFF1B\u53EF\u4F20\u5165\u90E8\u7F72\u76EE\u5F55\u8986\u76D6\u9ED8\u8BA4\u4F4D\u7F6E\u3002
1899
- Node\u540E\u7AEF\u9ED8\u8BA4\u5728\u542F\u52A8\u65F6\u91CD\u8BFB\u5B9E\u9645\u6302\u8F7D\u7684releases/<\u540E\u7AEF\u53D1\u5E03ID>/.env.runtime\uFF0C\u4FEE\u6539\u540Erestart\u5BB9\u5668\u5373\u53EF\uFF1B\u65E0\u9700\u65B0\u5F00\u5173\u6216override\uFF0C\u65E7\u90E8\u7F72\u5148\u7528\u65B0\u7248CLI\u53D1\u5E03\u4E00\u6B21\u3002\u4EC5\u66F4\u65B0Web/\u4E2D\u95F4\u4EF6\u540E\uFF0C\u6309\u540E\u7AEF\u5BB9\u5668Mounts\u786E\u8BA4\u6E90\u6587\u4EF6\uFF0C\u4E0D\u80FD\u6839\u636Ecurrent\u731C\u8DEF\u5F84\u3002\u6258\u7BA1\u8FDE\u63A5middleware.env\u4ECD\u4F18\u5148\uFF1B\u7AEF\u53E3/\u6302\u8F7D/Node\u542F\u52A8\u53C2\u6570\u3001\u9759\u6001Web\u548C\u4E2D\u95F4\u4EF6\u6309\u539F\u6D41\u7A0B\u66F4\u65B0\u3002\u624B\u5DE5\u4FEE\u6539\u5148\u5907\u4EFD\uFF0C\u957F\u671F\u914D\u7F6E\u540C\u6B65\u6E90\u73AF\u5883\uFF0C\u4E0B\u6B21\u540E\u7AEF\u53D1\u5E03\u4F1A\u91CD\u65B0\u751F\u6210\u3002
1907
+ Node \u540E\u7AEF\u542F\u52A8\u65F6\u8BFB\u53D6\u670D\u52A1\u5668 config/.env.runtime\uFF0C\u5E94\u7528\u914D\u7F6E\u548C\u6258\u7BA1\u8FDE\u63A5\u5408\u5E76\u5728\u6B64\uFF1B\u4FEE\u6539\u7EC6\u9879\u540E\u91CD\u542F\u540E\u7AEF\u5373\u53EF\u3002\u540C\u6E90\u7EC6\u9879\u8986\u76D6 JSON\uFF0C\u914D\u7F6E\u76EE\u5F55\u53EA\u8BFB\u6302\u8F7D\u5230 /run/hai/config\uFF0C\u539F\u5B50\u66FF\u6362\u6587\u4EF6\u4E5F\u80FD\u8BFB\u53D6\u3002\u5F52\u6863\u4EC5\u6709 apply.sh\u3001manifest.json\u3001config/\u3001images/\u3001.hai/ \u4E94\u4E2A\u9876\u5C42\u5165\u53E3\uFF1B\u5B8C\u6574 Compose\u3001\u52A0\u8F7D\u5668\u3001\u539F\u751F\u73AF\u5883\u3001\u4EE3\u7406/TLS\u3001\u7B56\u7565\u548C\u6821\u9A8C\u548C\u4F4D\u4E8E .hai/\uFF0C\u8F85\u52A9\u811A\u672C\u5185\u5D4C apply.sh\u3002Web/\u4E2D\u95F4\u4EF6\u5C40\u90E8\u66F4\u65B0\u4FDD\u7559\u5E94\u7528\u6587\u4EF6\uFF0C\u5931\u8D25\u5148\u6062\u590D\u5E94\u7528\u914D\u7F6E\u518D\u6062\u590D\u670D\u52A1\uFF1B\u957F\u671F\u4FEE\u6539\u540C\u6B65\u6E90\u73AF\u5883\u3002\u7AEF\u53E3\u3001\u6302\u8F7D\u3001Node \u542F\u52A8\u53C2\u6570\u548C\u4EE3\u7406\u53D8\u66F4\u9700\u90E8\u7F72\u3002
1900
1908
  \u90E8\u5206\u66F4\u65B0\u8981\u6C42\u5DF2\u6709\u6210\u529F\u7684\u5B8C\u6574\u90E8\u7F72\uFF0C\u4FDD\u7559\u672A\u9009\u670D\u52A1\u53CA\u6570\u636E\u5377\uFF1B\u5931\u8D25\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u7801\uFF0C\u5DF2\u6709\u90E8\u7F72\u5C1D\u8BD5\u6062\u590D\u9009\u4E2D\u670D\u52A1\u3002
1901
1909
  \u4E2D\u95F4\u4EF6\u8FDE\u63A5\u9F50\u5168\u65F6\u4F7F\u7528 --skip-middleware \u8DF3\u8FC7\u914D\u7F6E\u5411\u5BFC\u3002\u5305\u4E2D\u5305\u542B\u79C1\u6709\u914D\u7F6E\uFF0C\u4E0D\u63D0\u4EA4 Git\u3002
1902
1910
  `;
@@ -2079,7 +2087,7 @@ Web/App/Desktop \u5171\u4EAB AppShell \u7684 header/footer\u3002\u65B0\u589E\u98
2079
2087
 
2080
2088
  ${hasFeedback ? "\u666E\u901A\u6210\u5458\u548C\u4E1A\u52A1\u64CD\u4F5C\u5458\u53EF\u4ECE\u9876\u90E8\u201C\u95EE\u9898\u53CD\u9988\u201D\u63D0\u4EA4\u8BF4\u660E\u3001\u622A\u56FE\u4E0E\u65E5\u5FD7\uFF1B\u53EA\u6709 business_operator \u663E\u793A\u53CD\u9988\u7BA1\u7406\u83DC\u5355\u5E76\u80FD\u67E5\u770B\u3001\u5904\u7406\u3001\u64A4\u9500\u548C\u5BFC\u51FA\u95EE\u9898\u5305\u3002\u670D\u52A1\u7AEF\u7EE7\u7EED\u6309\u53EF\u4FE1\u8EAB\u4EFD\u4E0E\u5E94\u7528\u8303\u56F4\u6388\u6743\u3002\u5F00\u53D1\u901A\u77E5\u4F7F\u7528 Console\uFF0C\u751F\u4EA7\u987B\u5728 _feedback.yml \u4E0E _reach.yml \u914D\u7F6E\u6536\u4EF6\u4EBA\u548C\u771F\u5B9E\u90AE\u4EF6\u3002" : ""}
2081
2089
 
2082
- CLI \u5728\u6839\u76EE\u5F55 .env.dev\u3001.env.test\u3001.env.prod \u4E2D\u4E3A admin\u3001audit_admin\u3001config_admin\u3001business_operator \u5206\u522B\u751F\u6210\u72EC\u7ACB\u968F\u673A\u5F3A\u5BC6\u7801\uFF0C\u5BF9\u5E94 HAI_ADMIN_DEFAULT_PASSWORD\u3001HAI_AUDIT_ADMIN_PASSWORD\u3001HAI_CONFIG_ADMIN_PASSWORD\u3001HAI_BUSINESS_OPERATOR_PASSWORD\u3002\u540C\u4E00\u73AF\u5883\u7684\u914D\u7F6E\u968F\u9879\u76EE\u63D0\u4EA4\u4F9B\u514B\u9686\u540E\u5171\u4EAB\uFF1B\u88AB\u5FFD\u7565\u7684 .env.local \u6216\u540C\u540D\u8FDB\u7A0B\u73AF\u5883\u53EF\u8986\u76D6\u3002\u5DF2\u6709\u8D26\u53F7\u4E0D\u6539\u5BC6\u3001\u4E0D\u91CD\u65B0\u5206\u914D\u804C\u8D23\uFF1B\u672A\u914D\u7F6E\u5BC6\u7801\u7684\u72EC\u7ACB\u804C\u8D23\u8D26\u53F7\u4E0D\u521B\u5EFA\u3002\u5BC6\u7801\u987B\u7B26\u5408 _iam.yml \u7B56\u7565\uFF1B\u542F\u52A8\u5931\u8D25\u4FDD\u7559\u6A21\u5757\u3001\u914D\u7F6E\u5B57\u6BB5\u3001\u9519\u8BEF\u7801\u548C\u7AEF\u53E3\u5360\u7528\u8BCA\u65AD\u3002
2090
+ CLI \u5728\u6839\u76EE\u5F55 .env.dev.local\u3001.env.test.local\u3001.env.prod.local \u4E2D\u4E3A admin\u3001audit_admin\u3001config_admin\u3001business_operator \u5206\u522B\u751F\u6210\u72EC\u7ACB\u968F\u673A\u5F3A\u5BC6\u7801\uFF0C\u5BF9\u5E94 HAI_ADMIN_DEFAULT_PASSWORD\u3001HAI_AUDIT_ADMIN_PASSWORD\u3001HAI_CONFIG_ADMIN_PASSWORD\u3001HAI_BUSINESS_OPERATOR_PASSWORD\u3002\u8FD9\u4E9B\u79C1\u6709\u6587\u4EF6\u4E0D\u63D0\u4EA4 Git\uFF1B\u514B\u9686\u540E\u901A\u8FC7\u53D7\u4FDD\u62A4\u914D\u7F6E\u6216 Secret Manager \u5206\u53D1\u5BF9\u5E94\u73AF\u5883\u79D8\u5BC6\uFF0C\u540C\u4E00\u73AF\u5883\u5404\u8282\u70B9\u5171\u4EAB\u3002\u5171\u4EAB .env.local \u6216\u8FDB\u7A0B\u53D8\u91CF\u53EF\u8986\u76D6\u516C\u5171\u503C\uFF0C\u6A21\u5F0F\u79C1\u6709\u6587\u4EF6\u4F18\u5148\u4E8E\u5171\u4EAB\u79C1\u6709\u6587\u4EF6\u3002\u5DF2\u6709\u8D26\u53F7\u4E0D\u6539\u5BC6\u3001\u4E0D\u91CD\u65B0\u5206\u914D\u804C\u8D23\uFF1B\u672A\u914D\u7F6E\u5BC6\u7801\u7684\u72EC\u7ACB\u804C\u8D23\u8D26\u53F7\u4E0D\u521B\u5EFA\u3002\u5BC6\u7801\u987B\u7B26\u5408 _iam.yml \u7B56\u7565\uFF1B\u542F\u52A8\u5931\u8D25\u4FDD\u7559\u6A21\u5757\u3001\u914D\u7F6E\u5B57\u6BB5\u3001\u9519\u8BEF\u7801\u548C\u7AEF\u53E3\u5360\u7528\u8BCA\u65AD\u3002
2083
2091
 
2084
2092
  \`_iam.yml\` \u53EF\u6309\u4E1A\u52A1\u542F\u7528\u8D26\u53F7\u95F2\u7F6E\u626B\u63CF\u3001\u5BC6\u7801\u590D\u6742\u5EA6/\u671F\u9650\u3001ALTCHA \u6311\u6218\u3001\u5931\u8D25\u7A97\u53E3\u9501\u5B9A\u3001\u4E00\u6B21\u6027\u9080\u8BF7\u3001\u9996\u6B21\u6539\u5BC6\u3001\u654F\u611F\u64CD\u4F5C\u518D\u8BA4\u8BC1\u3001\u767B\u5F55\u65F6\u95F4/IP \u548C\u4F1A\u8BDD\u671F\u9650/\u5E76\u53D1\u7B56\u7565\u3002\u654F\u611F\u64CD\u4F5C\u6E05\u5355\u4F7F\u7528\u670D\u52A1\u7AEF\u64CD\u4F5C\u6807\u8BC6\uFF1B\u754C\u9762\u901A\u8FC7\u77ED\u671F\u5355\u6B21\u7968\u636E\u5B8C\u6210\u518D\u8BA4\u8BC1\u3002\`session.endOnPageHide\` \u9ED8\u8BA4\u5173\u95ED\uFF0C\u5F00\u542F\u65F6\u9875\u9762\u79BB\u5F00\u5C1D\u8BD5\u64A4\u9500\uFF0C\u4F46\u6D4F\u89C8\u5668\u901A\u77E5\u4EC5\u6700\u4F73\u52AA\u529B\uFF0C\u9700\u914D\u5408\u670D\u52A1\u7AEF\u95F2\u7F6E/\u7EDD\u5BF9\u671F\u9650\u3002\`_audit.yml\` \u914D\u7F6E\u4FDD\u7559\u4FDD\u62A4\u3001\u79C1\u6709\u5907\u4EFD\u53CA Reach \u544A\u8B66\uFF1B\u624B\u52A8\u5907\u4EFD\u5931\u8D25\u590D\u7528\u540C\u4E00\u7F16\u53F7\u91CD\u8BD5\uFF0C\u56DE\u6267\u542B key/\u8BB0\u5F55\u6570/SHA-256\uFF0C\u4E0D\u516C\u5F00\u4E0B\u8F7D\u3002\u5F52\u6863\u4E0D\u662F\u539F\u5B50\u6570\u636E\u5E93\u5FEB\u7167\uFF0C\u4E25\u683C\u6062\u590D\u987B\u6682\u505C\u5199\u5165/\u6E05\u7406\u6216\u4F7F\u7528\u539F\u751F\u5907\u4EFD\uFF0C\u5728\u72EC\u7ACB\u73AF\u5883\u6838\u9A8C\u6062\u590D\u3002
2085
2093
 
@@ -2087,7 +2095,7 @@ CLI \u5728\u6839\u76EE\u5F55 .env.dev\u3001.env.test\u3001.env.prod \u4E2D\u4E3A
2087
2095
 
2088
2096
  \u672C\u5730\u5F00\u53D1\u9ED8\u8BA4 \`PUBLIC_API_BASE=http://127.0.0.1:3000\`\uFF0CVite \u4F7F\u7528\u540C\u6E90\u4EE3\u7406\uFF1B\u90E8\u7F72\u65F6\u6539\u4E3A\u5B9E\u9645\u516C\u7F51 API Origin\uFF0CAndroid \u6A21\u62DF\u5668\u8BBF\u95EE\u5BBF\u4E3B\u673A\u65F6\u4F7F\u7528 \`http://10.0.2.2:3000\`\u3002\u4E0D\u8981\u5C06\u5BC6\u94A5\u653E\u5165\u4EFB\u4F55 \`PUBLIC_\` \u6216 \`VITE_\` \u53D8\u91CF\u3002
2089
2097
 
2090
- \u670D\u52A1\u7AEF\u7248\u672C\u5316\u4F20\u8F93\u5BC6\u94A5\u73AF\u5728\u521B\u5EFA\u65F6\u5206\u522B\u5199\u5165 \`.env.dev\`\u3001\`.env.test\`\u3001\`.env.prod\`\uFF0C\u53D8\u91CF\u4E3A \`HAI_CRYPTO_TRANSPORT_SERVERKEYRING\`\uFF0C\u5404\u73AF\u5883\u5BC6\u94A5\u4E0D\u540C\uFF1B\u8FD0\u884C\u65F6\u4E0D\u751F\u6210\u5BC6\u94A5\u3002\u540C\u4E00\u73AF\u5883\u7684\u6240\u6709\u8282\u70B9\u4F7F\u7528\u540C\u4E00 ring \u548C\u5171\u4EAB registry\uFF0C\u4E5F\u53EF\u901A\u8FC7\u8FDB\u7A0B\u73AF\u5883\u6216 Secret Manager \u8986\u76D6\u3002\u79C1\u94A5\u4E0D\u8FDB\u5165 PUBLIC_/VITE_\u3001\u65E5\u5FD7\u6216\u6D4F\u89C8\u5668\u3002
2098
+ \u670D\u52A1\u7AEF\u7248\u672C\u5316\u4F20\u8F93\u5BC6\u94A5\u73AF\u5728\u521B\u5EFA\u65F6\u5206\u522B\u5199\u5165\u4E0D\u63D0\u4EA4 Git \u7684 \`.env.dev.local\`\u3001\`.env.test.local\`\u3001\`.env.prod.local\`\uFF0C\u53D8\u91CF\u4E3A \`HAI_CRYPTO_TRANSPORT_SERVERKEYRING\`\uFF0C\u5404\u73AF\u5883\u5BC6\u94A5\u4E0D\u540C\uFF1B\u8FD0\u884C\u65F6\u4E0D\u751F\u6210\u5BC6\u94A5\u3002\u540C\u4E00\u73AF\u5883\u7684\u6240\u6709\u8282\u70B9\u4F7F\u7528\u540C\u4E00 ring \u548C\u5171\u4EAB registry\uFF0C\u4E5F\u53EF\u901A\u8FC7\u8FDB\u7A0B\u73AF\u5883\u6216 Secret Manager \u8986\u76D6\u3002\u79C1\u94A5\u4E0D\u8FDB\u5165 PUBLIC_/VITE_\u3001\u65E5\u5FD7\u6216\u6D4F\u89C8\u5668\u3002
2091
2099
 
2092
2100
  \u6240\u6709\u4E1A\u52A1 JSON \u8BF7\u6C42\u548C\u54CD\u5E94\u9ED8\u8BA4\u901A\u8FC7 \`@h-ai/crypto\` transport \u53CC\u5411\u52A0\u5BC6\uFF08\u542B\u767B\u5F55\u3001\u5237\u65B0\u3001\u5934\u50CF\u4E0A\u4F20\uFF09\uFF1B\u524D\u7AEF\u7EDF\u4E00\u4F7F\u7528\u5171\u4EAB typed API client\u3002Web \u8BBF\u95EE\u4EE4\u724C\u4EC5\u5B58\u5185\u5B58\u3001\u5237\u65B0\u4EE4\u724C\u7531 HttpOnly Cookie \u4FDD\u5B58\uFF1B\u53D7\u4FE1\u539F\u751F\u7AEF\u4F7F\u7528\u5E73\u53F0\u5B89\u5168\u5B58\u50A8\u6216\u5185\u5B58\u3002\u5934\u50CF\u4E3A\u968F\u673A\u8DEF\u5F84\u53EA\u8BFB\u56FE\u7247\u8D44\u6E90\uFF0C\u652F\u6301 PNG/JPEG/WebP\uFF0C\u6700\u5927 512 KiB\u3002\u5065\u5EB7\u68C0\u67E5\u548C\u53EA\u8BFB\u5934\u50CF\u56FE\u7247\u53EF\u76F4\u63A5\u8BBF\u95EE\u3002
2093
2101
 
@@ -2332,7 +2340,7 @@ async function addModule(options) {
2332
2340
  writeCliMessage(cliM("cli_addAvailableModules", { params: { modules: Object.keys(MODULE_MAP).join(", ") } }));
2333
2341
  return;
2334
2342
  }
2335
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-COTP767H.js');
2343
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-S2YH642Z.js');
2336
2344
  if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
2337
2345
  if (moduleName === "deploy")
2338
2346
  await ensureDeployConfig(cwd);
@@ -2454,7 +2462,7 @@ ${production.join("\n")}`);
2454
2462
  }
2455
2463
  }
2456
2464
  async function buildMobileApp(root, directories, platform, run2) {
2457
- if (!directories.length || platform === "ios" && process4.platform !== "darwin")
2465
+ if (!directories.length || platform === "ios" && process5.platform !== "darwin")
2458
2466
  throw new Error(cliM("cli_build_app_platform"));
2459
2467
  const outputs = [];
2460
2468
  for (const item of directories) {
@@ -2492,31 +2500,240 @@ async function buildMobileApp(root, directories, platform, run2) {
2492
2500
  }
2493
2501
  return outputs;
2494
2502
  }
2503
+ function object(value) {
2504
+ if (value === void 0)
2505
+ return {};
2506
+ if (!value || typeof value !== "object" || Array.isArray(value))
2507
+ throw new Error(cliM("cli_download_invalid"));
2508
+ return value;
2509
+ }
2510
+ function registryUrl(value) {
2511
+ try {
2512
+ if (typeof value !== "string")
2513
+ throw new Error("Missing registry");
2514
+ const url = new URL(value);
2515
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash)
2516
+ throw new Error("Invalid registry");
2517
+ return url.toString().replace(/\/$/, "");
2518
+ } catch {
2519
+ throw new Error(cliM("cli_download_invalid"));
2520
+ }
2521
+ }
2522
+ async function resolveBuildDownloads(directory, options) {
2523
+ const path10 = projectConfigPath(directory);
2524
+ const project = readProjectConfig(path10);
2525
+ const build = object(project.build);
2526
+ const sources = object(build.sources);
2527
+ const savedImage = object(sources.image);
2528
+ const savedNpm = object(sources.npm);
2529
+ const savedProxy = object(build.proxy);
2530
+ const interactive = !!process5.stdin.isTTY && !options.yes;
2531
+ const choose = async (message, values) => {
2532
+ const answer = await prompts({ type: "select", name: "value", message, choices: values.map((value) => ({ title: cliM(`cli_download_${value}`), value })) });
2533
+ if (typeof answer.value !== "string")
2534
+ throw new Error(cliM("cli_deploy_cancelled"));
2535
+ return answer.value;
2536
+ };
2537
+ const mirrorEntries = options.imageMirrors?.split(",").map((entry) => {
2538
+ const separator = entry.indexOf("=");
2539
+ if (separator <= 0 || separator === entry.length - 1)
2540
+ throw new Error(cliM("cli_download_invalid"));
2541
+ return [entry.slice(0, separator).trim(), entry.slice(separator + 1).trim()];
2542
+ });
2543
+ if (mirrorEntries && new Set(mirrorEntries.map(([key]) => key)).size !== mirrorEntries.length)
2544
+ throw new Error(cliM("cli_download_invalid"));
2545
+ let imageType = options.imageSource ?? (mirrorEntries ? "custom" : savedImage.type);
2546
+ let npmType = options.npmSource ?? (options.npmRegistry ? "custom" : savedNpm.type);
2547
+ const saveImage = imageType === void 0 && interactive;
2548
+ const saveNpm = npmType === void 0 && interactive;
2549
+ if (saveImage)
2550
+ imageType = await choose(cliM("cli_download_choose_image"), ["official", "cn", "custom"]);
2551
+ if (saveNpm)
2552
+ npmType = await choose(cliM("cli_download_choose_npm"), ["official", "cn", "custom"]);
2553
+ imageType ??= "official";
2554
+ npmType ??= "official";
2555
+ if (typeof imageType !== "string" || !["official", "cn", "custom"].includes(imageType) || typeof npmType !== "string" || !["official", "cn", "custom"].includes(npmType))
2556
+ throw new Error(cliM("cli_download_invalid"));
2557
+ let registries = mirrorEntries ? Object.fromEntries(mirrorEntries) : savedImage.registries;
2558
+ if (imageType === "custom" && !registries && interactive) {
2559
+ const answer = await prompts({ type: "text", name: "value", message: cliM("cli_download_image_mirrors") });
2560
+ if (typeof answer.value !== "string" || !answer.value.trim())
2561
+ throw new Error(cliM("cli_deploy_cancelled"));
2562
+ registries = Object.fromEntries(answer.value.split(",").map((entry) => {
2563
+ const index = entry.indexOf("=");
2564
+ if (index <= 0)
2565
+ throw new Error(cliM("cli_download_invalid"));
2566
+ return [entry.slice(0, index).trim(), entry.slice(index + 1).trim()];
2567
+ }));
2568
+ }
2569
+ const mappings = registries === void 0 ? void 0 : object(registries);
2570
+ if (mappings && Object.entries(mappings).some(([key, value]) => !/^[\w.-]+(?::\d+)?$/.test(key) || typeof value !== "string"))
2571
+ throw new Error(cliM("cli_download_invalid"));
2572
+ const imageSource = { type: imageType, ...imageType === "custom" ? { registries: mappings } : {} };
2573
+ if (mirrorEntries && imageType !== "custom")
2574
+ throw new Error(cliM("cli_download_invalid"));
2575
+ if (imageType === "custom") {
2576
+ if (!mappings || !Object.keys(mappings).length)
2577
+ throw new Error(cliM("cli_download_invalid"));
2578
+ for (const key of Object.keys(mappings)) resolveImageReference(`${key}/hai-source-check:1`, imageSource);
2579
+ }
2580
+ let customRegistry = options.npmRegistry ?? savedNpm.registry;
2581
+ if (npmType === "custom" && !customRegistry && interactive) {
2582
+ const answer = await prompts({ type: "text", name: "value", message: cliM("cli_download_npm_registry") });
2583
+ if (typeof answer.value !== "string" || !answer.value.trim())
2584
+ throw new Error(cliM("cli_deploy_cancelled"));
2585
+ customRegistry = answer.value;
2586
+ }
2587
+ if (options.npmRegistry && npmType !== "custom")
2588
+ throw new Error(cliM("cli_download_invalid"));
2589
+ const npmRegistry = npmType === "cn" ? "https://registry.npmmirror.com" : npmType === "official" ? "https://registry.npmjs.org" : registryUrl(customRegistry);
2590
+ const npmSource = { type: npmType, ...npmType === "custom" ? { registry: npmRegistry } : {} };
2591
+ let proxyMode = options.proxyMode ?? (options.proxyUrl ? "custom" : savedProxy.mode);
2592
+ const saveProxy = proxyMode === void 0 && interactive && options.httpProxy !== false;
2593
+ if (saveProxy)
2594
+ proxyMode = await choose(cliM("cli_download_choose_proxy"), ["inherit", "system", "custom", "none"]);
2595
+ proxyMode ??= "inherit";
2596
+ if (options.proxyUrl !== void 0 && proxyMode !== "custom")
2597
+ throw new Error(cliM("cli_download_invalid"));
2598
+ if (typeof proxyMode !== "string" || !["inherit", "system", "custom", "none"].includes(proxyMode))
2599
+ throw new Error(cliM("cli_download_invalid"));
2600
+ if (options.httpProxy === false && (options.proxyUrl !== void 0 || options.proxyMode !== void 0 && options.proxyMode !== "none"))
2601
+ throw new Error(cliM("cli_download_invalid"));
2602
+ let proxyUrl = options.proxyUrl ?? savedProxy.url;
2603
+ if (proxyMode === "custom" && !proxyUrl && interactive) {
2604
+ const answer = await prompts({ type: "text", name: "value", message: cliM("cli_download_proxy_url") });
2605
+ if (typeof answer.value !== "string" || !answer.value.trim())
2606
+ throw new Error(cliM("cli_deploy_cancelled"));
2607
+ proxyUrl = answer.value;
2608
+ }
2609
+ const bypass = options.proxyBypass ?? savedProxy.bypass;
2610
+ if (proxyUrl !== void 0 && typeof proxyUrl !== "string" || bypass !== void 0 && typeof bypass !== "string")
2611
+ throw new Error(cliM("cli_download_invalid"));
2612
+ const proxy = { mode: options.httpProxy === false ? "none" : proxyMode, ...proxyMode === "custom" && options.httpProxy !== false ? { url: proxyUrl } : {}, ...bypass !== void 0 ? { bypass } : {} };
2613
+ resolveDownloadProxy(proxy);
2614
+ if (saveImage || saveNpm || saveProxy) {
2615
+ project.build = { ...build, sources: { ...sources, ...saveImage ? { image: imageSource } : {}, ...saveNpm ? { npm: npmSource } : {} }, ...saveProxy ? { proxy } : {} };
2616
+ writeFileSync(path10, `${JSON.stringify(project, null, 2)}
2617
+ `);
2618
+ }
2619
+ return { imageSource, npmRegistry, npmSource, proxy };
2620
+ }
2621
+ function buildProxyArguments(proxy, runtime, network) {
2622
+ const environment = resolveDownloadProxy(proxy);
2623
+ for (const key of ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "ALL_PROXY", "all_proxy"]) {
2624
+ if (!environment[key])
2625
+ continue;
2626
+ const url = new URL(environment[key]);
2627
+ if (network !== "host" && (runtime === "docker" || network !== void 0) && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)) {
2628
+ url.hostname = runtime === "podman" ? "host.containers.internal" : "host.docker.internal";
2629
+ environment[key] = url.toString().replace(/\/$/, "");
2630
+ }
2631
+ }
2632
+ return Object.entries(environment).flatMap(([key, value]) => ["--build-arg", `${key}=${value}`]);
2633
+ }
2634
+ async function preflightDownloadProxy(runtime, proxy) {
2635
+ const environment = resolveDownloadProxy(proxy);
2636
+ for (const value of new Set([environment.HTTP_PROXY, environment.HTTPS_PROXY].filter(Boolean))) {
2637
+ const url = new URL(value);
2638
+ await new Promise((resolve7, reject) => {
2639
+ const socket = connect({ host: url.hostname.replace(/^\[|\]$/g, ""), port: Number(url.port || (url.protocol === "https:" ? 443 : 80)) });
2640
+ socket.setTimeout(3e3);
2641
+ socket.once("connect", () => {
2642
+ socket.destroy();
2643
+ resolve7();
2644
+ });
2645
+ const fail = () => {
2646
+ socket.destroy();
2647
+ reject(new Error(cliM("cli_download_proxy_unreachable")));
2648
+ };
2649
+ socket.once("error", fail);
2650
+ socket.once("timeout", fail);
2651
+ });
2652
+ }
2653
+ if (!environment.HTTP_PROXY && !environment.HTTPS_PROXY)
2654
+ return;
2655
+ for (const candidate of runtime === "auto" ? ["podman", "docker"] : [runtime]) {
2656
+ try {
2657
+ const data = JSON.parse(execFileSync(candidate, ["info", "--format", "{{json .}}"], { encoding: "utf8", windowsHide: true, timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }));
2658
+ writeCliMessage(cliM(candidate === "docker" && (data.HTTPProxy || data.HTTPSProxy) ? "cli_download_engine_proxy_configured" : candidate === "podman" && !data.host?.serviceIsRemote ? "cli_download_engine_proxy_local" : "cli_download_engine_proxy_hint", { params: { runtime: candidate } }));
2659
+ return;
2660
+ } catch {
2661
+ }
2662
+ }
2663
+ writeCliMessage(cliM("cli_download_engine_proxy_hint", { params: { runtime } }));
2664
+ }
2665
+ function validateDownloadDockerfile(file, downloads) {
2666
+ const content = readFileSync(file, "utf8");
2667
+ if (downloads.imageSource.type !== "official" && !/^ARG HAI_NODE_IMAGE=/m.test(content) || downloads.npmSource.type !== "official" && !/^ARG HAI_NPM_REGISTRY[=\s]/m.test(content)) {
2668
+ throw new Error(cliM("cli_download_template_required", { params: { path: file } }));
2669
+ }
2670
+ }
2671
+ function createWindowsBuildOutput() {
2672
+ const decoder = new StringDecoder("utf8");
2673
+ let previousCarriageReturn = false;
2674
+ const normalize = (value) => {
2675
+ const result = value.replace(/\n/g, (newline, offset) => (offset === 0 ? previousCarriageReturn : value[offset - 1] === "\r") ? newline : "\r\n");
2676
+ if (value.length)
2677
+ previousCarriageReturn = value.endsWith("\r");
2678
+ return result;
2679
+ };
2680
+ return new Transform({
2681
+ transform(chunk, _encoding, callback) {
2682
+ callback(null, normalize(decoder.write(chunk)));
2683
+ },
2684
+ flush(callback) {
2685
+ callback(null, normalize(decoder.end()));
2686
+ }
2687
+ });
2688
+ }
2689
+ async function forwardWindowsBuildOutput(source, destination) {
2690
+ const forwarding = new Writable({
2691
+ write(chunk, encoding, callback) {
2692
+ destination.write(chunk, encoding, callback);
2693
+ }
2694
+ });
2695
+ await pipeline(source, createWindowsBuildOutput(), forwarding);
2696
+ }
2495
2697
 
2496
2698
  // src/commands/cli-build.ts
2497
2699
  async function run(command, args, cwd, quiet = false, identity, compileEnvironment) {
2498
2700
  await new Promise((resolve7, reject) => {
2499
- const env = Object.fromEntries(Object.entries(process4.env).filter(([key]) => !/^(?:HAI_|PUBLIC_|VITE_)/.test(key)));
2701
+ const env = Object.fromEntries(Object.entries(process5.env).filter(([key]) => !/^(?:HAI_|PUBLIC_|VITE_)/.test(key)));
2500
2702
  Object.assign(env, compileEnvironment);
2501
2703
  if (identity) {
2502
2704
  env.HAI_BUILD_ID = identity.buildId;
2503
2705
  env.HAI_BUILD_COMMIT = identity.commit;
2504
2706
  }
2505
2707
  env.NODE_ENV = "production";
2708
+ if (process5.platform === "win32") {
2709
+ env.npm_config_reporter = "append-only";
2710
+ env.pnpm_config_reporter = "append-only";
2711
+ }
2506
2712
  delete env.CAPACITOR_SERVER_URL;
2507
2713
  if (env.CI === "1" || env.CI === "0")
2508
2714
  env.CI = env.CI === "1" ? "true" : "false";
2509
- const child = spawn(command, args, { cwd, env, windowsHide: true, stdio: quiet ? "ignore" : "inherit", shell: false });
2715
+ const windowsOutput = !quiet && process5.platform === "win32";
2716
+ const child = spawn(command, args, { cwd, env, windowsHide: true, stdio: quiet ? "ignore" : windowsOutput ? ["inherit", "pipe", "pipe"] : "inherit", shell: false });
2717
+ let output = Promise.resolve();
2718
+ if (windowsOutput && child.stdout && child.stderr) {
2719
+ output = Promise.all([
2720
+ forwardWindowsBuildOutput(child.stdout, process5.stdout),
2721
+ forwardWindowsBuildOutput(child.stderr, process5.stderr)
2722
+ ]).then(() => void 0);
2723
+ void output.catch(() => child.kill());
2724
+ }
2510
2725
  child.once("error", reject);
2511
- child.once("exit", (code) => code === 0 ? resolve7() : reject(new Error(cliM("cli_build_failed", { params: { command, code: String(code) } }))));
2726
+ child.once(windowsOutput && child.stdout && child.stderr ? "close" : "exit", (code) => {
2727
+ void output.then(() => code === 0 ? resolve7() : reject(new Error(cliM("cli_build_failed", { params: { command, code: String(code) } }))), reject);
2728
+ });
2512
2729
  });
2513
2730
  }
2514
2731
  async function pnpm(args, cwd, identity, compileEnvironment) {
2515
- const entry = process4.env.npm_execpath;
2732
+ const entry = process5.env.npm_execpath;
2516
2733
  if (entry && /(?:^|[/\\])pnpm(?:-native)?(?:\.exe|\.[cm]?js)?$/i.test(entry))
2517
- return /\.[cm]?js$/i.test(entry) ? run(process4.execPath, [entry, ...args], cwd, false, identity, compileEnvironment) : run(entry, args, cwd, false, identity, compileEnvironment);
2518
- if (process4.platform === "win32") {
2519
- const directories = (process4.env.PATH ?? "").split(";");
2734
+ return /\.[cm]?js$/i.test(entry) ? run(process5.execPath, [entry, ...args], cwd, false, identity, compileEnvironment) : run(entry, args, cwd, false, identity, compileEnvironment);
2735
+ if (process5.platform === "win32") {
2736
+ const directories = (process5.env.PATH ?? "").split(";");
2520
2737
  for (const directory of directories) {
2521
2738
  const executable = join(directory, "pnpm.exe");
2522
2739
  if (existsSync(executable))
@@ -2524,7 +2741,7 @@ async function pnpm(args, cwd, identity, compileEnvironment) {
2524
2741
  for (const relative4 of ["node_modules/pnpm/bin/pnpm.cjs", "pnpm.cjs"]) {
2525
2742
  const candidate = join(directory, relative4);
2526
2743
  if (existsSync(candidate))
2527
- return run(process4.execPath, [candidate, ...args], cwd, false, identity, compileEnvironment);
2744
+ return run(process5.execPath, [candidate, ...args], cwd, false, identity, compileEnvironment);
2528
2745
  }
2529
2746
  }
2530
2747
  throw new Error(cliM("cli_build_use_pnpm"));
@@ -2532,8 +2749,8 @@ async function pnpm(args, cwd, identity, compileEnvironment) {
2532
2749
  return run("pnpm", args, cwd, false, identity, compileEnvironment);
2533
2750
  }
2534
2751
  function staticCompileEnvironment(target) {
2535
- const flag = process4.env.HAI_BUILD_STATIC;
2536
- const base = process4.env.PUBLIC_BASE_PATH;
2752
+ const flag = process5.env.HAI_BUILD_STATIC;
2753
+ const base = process5.env.PUBLIC_BASE_PATH;
2537
2754
  if (flag !== void 0 && flag !== "" && flag !== "0" && flag !== "1")
2538
2755
  throw new Error(cliM("cli_build_static_flag"));
2539
2756
  if (base && (!/^\/[\w.-]+(?:\/[\w.-]+)*$/.test(base) || base.split("/").some((segment) => segment === "." || segment === "..")))
@@ -2560,7 +2777,7 @@ function sourceCommit(cwd) {
2560
2777
  }
2561
2778
  }
2562
2779
  async function buildCommand(options) {
2563
- const directory = resolve(options.cwd ?? process4.cwd());
2780
+ const directory = resolve(options.cwd ?? process5.cwd());
2564
2781
  const target = options.target ?? "compile";
2565
2782
  const concurrency = Number(options.concurrency ?? 2);
2566
2783
  if (![1, 2].includes(concurrency))
@@ -2569,7 +2786,7 @@ async function buildCommand(options) {
2569
2786
  if (!["compile", "image", "desktop", "app"].includes(target))
2570
2787
  throw new Error(cliM("cli_build_target_invalid"));
2571
2788
  const compileEnvironment = staticCompileEnvironment(target);
2572
- if (target !== "image" && (options.images !== void 0 || options.concurrency !== void 0 || options.runtime !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false) || !["desktop", "app"].includes(target) && (options.platform !== void 0 || options.arch !== void 0) || target === "app" && options.arch !== void 0) {
2789
+ if (target !== "image" && (options.images !== void 0 || options.concurrency !== void 0 || options.runtime !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false || options.imageSource !== void 0 || options.imageMirrors !== void 0 || options.npmSource !== void 0 || options.npmRegistry !== void 0 || options.proxyMode !== void 0 || options.proxyUrl !== void 0 || options.proxyBypass !== void 0) || !["desktop", "app"].includes(target) && (options.platform !== void 0 || options.arch !== void 0) || target === "app" && options.arch !== void 0) {
2573
2790
  throw new Error(cliM("cli_build_options_invalid"));
2574
2791
  }
2575
2792
  if (target === "compile") {
@@ -2592,9 +2809,9 @@ async function buildCommand(options) {
2592
2809
  }
2593
2810
  if (target === "desktop") {
2594
2811
  const platforms = { win32: "windows", linux: "linux", darwin: "macos" };
2595
- const host = platforms[process4.platform];
2812
+ const host = platforms[process5.platform];
2596
2813
  const platform = options.platform ?? host;
2597
- const arch = options.arch ?? process4.arch;
2814
+ const arch = options.arch ?? process5.arch;
2598
2815
  if (!platform || platform !== host || !["x64", "arm64"].includes(arch))
2599
2816
  throw new Error(cliM("cli_build_native_host"));
2600
2817
  if (!config.desktop?.length)
@@ -2624,6 +2841,8 @@ async function buildCommand(options) {
2624
2841
  if (!existsSync(resolve(cwd, image.dockerfile)))
2625
2842
  throw new Error(cliM("cli_build_dockerfile_missing", { params: { path: image.dockerfile } }));
2626
2843
  }
2844
+ const downloads = await resolveBuildDownloads(cwd, options);
2845
+ for (const image of images) validateDownloadDockerfile(resolve(cwd, image.dockerfile), downloads);
2627
2846
  const candidates = options.runtime && options.runtime !== "auto" ? [options.runtime] : ["podman", "docker"];
2628
2847
  if (candidates.some((runtime2) => !["podman", "docker"].includes(runtime2)))
2629
2848
  throw new Error(cliM("cli_build_runtime_missing"));
@@ -2638,6 +2857,7 @@ async function buildCommand(options) {
2638
2857
  }
2639
2858
  if (!runtime)
2640
2859
  throw new Error(cliM("cli_build_runtime_missing"));
2860
+ await preflightDownloadProxy(runtime, downloads.proxy);
2641
2861
  if (options.sqlitePrebuildMirror) {
2642
2862
  const url = new URL(options.sqlitePrebuildMirror);
2643
2863
  if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash)
@@ -2667,8 +2887,8 @@ async function buildCommand(options) {
2667
2887
  const context = resolve(cwd, image.context ?? ".");
2668
2888
  const imageRef = `localhost/hai-${projectName}-${image.name}:${buildId.toLowerCase().replaceAll("+", "_")}`;
2669
2889
  const args = ["build", "-f", resolve(cwd, image.dockerfile), "-t", imageRef];
2670
- const testRun = process4.env.HAI_TEST_IMAGE_RUN;
2671
- const testLedger = process4.env.HAI_TEST_IMAGE_LEDGER;
2890
+ const testRun = process5.env.HAI_TEST_IMAGE_RUN;
2891
+ const testLedger = process5.env.HAI_TEST_IMAGE_LEDGER;
2672
2892
  if (testLedger && /^[a-f0-9-]{36}$/.test(testRun ?? "")) {
2673
2893
  const label = `org.h-ai.test.image-run=${testRun}`;
2674
2894
  args.push("--label", label);
@@ -2678,19 +2898,19 @@ async function buildCommand(options) {
2678
2898
  `);
2679
2899
  }
2680
2900
  args.push("--build-arg", `HAI_BUILD_ID=${buildId}`, "--build-arg", `HAI_BUILD_COMMIT=${commit}`);
2901
+ const dockerfile = readFileSync(resolve(cwd, image.dockerfile), "utf8");
2902
+ for (const match of dockerfile.matchAll(/^ARG (HAI_(?:NODE|NGINX)_IMAGE)=(\S+)\s*$/gm))
2903
+ args.push("--build-arg", `${match[1]}=${resolveImageReference(match[2], downloads.imageSource)}`);
2904
+ args.push("--build-arg", `HAI_NPM_REGISTRY=${downloads.npmRegistry}`, ...buildProxyArguments(downloads.proxy, runtime, options.network));
2905
+ if (runtime === "docker" && process5.platform === "linux" && options.network !== "host")
2906
+ args.push("--add-host", "host.docker.internal:host-gateway");
2681
2907
  if (options.network !== void 0)
2682
2908
  args.push("--network", options.network);
2683
2909
  const ignore = `${resolve(cwd, image.dockerfile)}.dockerignore`;
2684
2910
  if (runtime === "podman" && existsSync(ignore))
2685
2911
  args.push("--ignorefile", ignore);
2686
2912
  if (runtime === "podman") {
2687
- args.push("--format", "docker", "--jobs", "1", ...options.httpProxy === false ? ["--http-proxy=false"] : []);
2688
- if (options.httpProxy !== false) {
2689
- for (const key of ["HTTP_PROXY", "HTTPS_PROXY", "FTP_PROXY", "NO_PROXY", "http_proxy", "https_proxy", "ftp_proxy", "no_proxy"]) {
2690
- if (process4.env[key])
2691
- args.push("--build-arg", `${key}=${process4.env[key]}`);
2692
- }
2693
- }
2913
+ args.push("--format", "docker", "--jobs", "1", ...downloads.proxy.mode === "none" ? ["--http-proxy=false"] : []);
2694
2914
  }
2695
2915
  if (options.sqlitePrebuildMirror)
2696
2916
  args.push("--build-arg", `SQLITE_PREBUILD_MIRROR=${options.sqlitePrebuildMirror}`);
@@ -2772,7 +2992,7 @@ function prepareDependencyInputs(cwd) {
2772
2992
  async function deploySecretsCommand(directory, options) {
2773
2993
  if (!options.import || !["docker", "podman"].includes(options.runtime ?? "docker"))
2774
2994
  throw new Error(cliM("cli_deploy_secrets_options"));
2775
- const cwd = resolve(options.cwd ?? process4.cwd());
2995
+ const cwd = resolve(options.cwd ?? process5.cwd());
2776
2996
  const local = createRequire(resolve(cwd, "package.json"));
2777
2997
  let module;
2778
2998
  try {
@@ -2788,237 +3008,12 @@ ${JSON.stringify({ phase: context?.phase, state: context?.state })}`);
2788
3008
  }
2789
3009
  writeCliMessage(JSON.stringify(result.data, null, 2));
2790
3010
  }
2791
-
2792
- // ../reldb/dist/chunk-SDS2ZT3V.js
2793
- external_exports.enum(["sqlite", "postgresql", "mysql"]);
2794
- var ReldbOperationLogConfigSchema = external_exports.object({
2795
- read: external_exports.boolean().default(false),
2796
- write: external_exports.boolean().default(false),
2797
- /** 安全数量摘要的最大字符数;0 表示省略摘要,永不记录 SQL 原文或参数值。 */
2798
- maxLength: external_exports.number().int().min(0).default(1e3),
2799
- level: external_exports.enum(["info", "debug", "trace"]).default("debug")
2800
- });
2801
- var OperationLogConfigFieldSchema = {
2802
- operationLog: ReldbOperationLogConfigSchema.optional()
2803
- };
2804
- var PoolConfigSchema = external_exports.object({
2805
- /** PostgreSQL 已建立连接的最小保留数(默认 1,不主动预建) */
2806
- min: external_exports.number().int().min(0).default(1),
2807
- /** 最大连接数(默认 10) */
2808
- max: external_exports.number().int().min(1).default(10),
2809
- /** PostgreSQL 空闲连接超时,毫秒(默认 30000;0 禁用) */
2810
- idleTimeout: external_exports.number().int().min(0).default(3e4),
2811
- /** 获取连接超时,毫秒(默认10000);PostgreSQL >=0,0禁用;MySQL排队+建连总期限为1–2147483647 */
2812
- acquireTimeout: external_exports.number().int().min(0).default(1e4)
2813
- });
2814
- var MysqlPoolConfigSchema = external_exports.object({
2815
- /** 最大连接数(默认 10) */
2816
- max: external_exports.number().int().min(1).default(10),
2817
- /** 获取连接超时,毫秒(默认10000);PostgreSQL >=0,0禁用;MySQL排队+建连总期限为1–2147483647 */
2818
- acquireTimeout: external_exports.number().int().min(1).max(2147483647).default(1e4),
2819
- /** MySQL 等待队列最大请求数(默认 1000;正整数,不支持无限队列) */
2820
- queueLimit: external_exports.number().int().min(1).default(1e3)
2821
- }).strict();
2822
- var SslConfigSchema = external_exports.union([
2823
- external_exports.boolean(),
2824
- external_exports.enum(["require", "prefer", "allow", "disable"]),
2825
- external_exports.record(external_exports.string(), external_exports.unknown())
2826
- ]);
2827
- var MysqlSslConfigSchema = external_exports.union([
2828
- external_exports.literal(false),
2829
- external_exports.object({
2830
- /** MySQL 是否校验证书链(默认 true);false 明确放弃服务端认证 */
2831
- rejectUnauthorized: external_exports.boolean().default(true),
2832
- /** MySQL 是否验证 DNS 主机身份(默认 true);仅证书链验证开启时有效 */
2833
- verifyIdentity: external_exports.boolean().default(true),
2834
- /** MySQL 信任的 CA,PEM 内容;省略时使用 Node 信任库 */
2835
- ca: external_exports.string().min(1).optional(),
2836
- /** MySQL 客户端证书,PEM 内容;必须同时提供 key */
2837
- cert: external_exports.string().min(1).optional(),
2838
- /** MySQL 客户端私钥,PEM 内容;必须同时提供 cert */
2839
- key: external_exports.string().min(1).optional()
2840
- }).strict().refine((ssl) => Boolean(ssl.cert) === Boolean(ssl.key), { path: ["key"] })
2841
- ]);
2842
- var SqliteOptionsSchema = external_exports.object({
2843
- /** 是否启用 WAL 模式(默认 true),可提高并发读写性能 */
2844
- walMode: external_exports.boolean().default(true),
2845
- /** 是否只读模式(默认 false) */
2846
- readonly: external_exports.boolean().default(false),
2847
- /**
2848
- * 执行方式(默认 sync)。
2849
- *
2850
- * - `sync`:主线程直接执行 better-sqlite3(同步阻塞主线程事件循环)。
2851
- * - `worker`:在专用 worker 线程持有唯一连接执行,把阻塞移出主线程。
2852
- * 注意 WAL 只改善数据库锁并发,无法避免 better-sqlite3 同步调用占用调用线程。
2853
- */
2854
- executor: external_exports.enum(["sync", "worker"]).default("sync"),
2855
- /** worker 执行方式下等待连接打开的超时(毫秒,默认 10000) */
2856
- workerConnectTimeoutMs: external_exports.number().int().positive().default(1e4)
2857
- });
2858
- var MysqlOptionsSchema = external_exports.object({
2859
- /** 字符集(默认 utf8mb4),推荐使用 utf8mb4 支持完整 Unicode */
2860
- charset: external_exports.string().default("utf8mb4")
2861
- }).strict();
2862
- var NetworkConfigSchema = external_exports.object({
2863
- /**
2864
- * 连接字符串(可选)
2865
- * 如果提供 url,将优先使用,忽略 host/port 等字段
2866
- */
2867
- url: external_exports.string().optional(),
2868
- /** 数据库主机地址(默认 localhost) */
2869
- host: external_exports.string().default("localhost"),
2870
- /**
2871
- * 数据库端口
2872
- * - PostgreSQL 默认 5432
2873
- * - MySQL 默认 3306
2874
- */
2875
- port: external_exports.number().int().min(1).max(65535).optional(),
2876
- /** 数据库名称 */
2877
- database: external_exports.string(),
2878
- /** 数据库用户名 */
2879
- user: external_exports.string().optional(),
2880
- /** 数据库密码 */
2881
- password: external_exports.string().optional(),
2882
- /** SSL/TLS 连接配置 */
2883
- ssl: SslConfigSchema.optional(),
2884
- /** 连接池配置 */
2885
- pool: PoolConfigSchema.optional()
2886
- });
2887
- var SqliteConfigSchema = external_exports.object({
2888
- type: external_exports.literal("sqlite"),
2889
- /** 数据库文件路径(如 './data.db')或 ':memory:'(内存数据库) */
2890
- database: external_exports.string(),
2891
- /** SQLite 特定选项 */
2892
- sqlite: SqliteOptionsSchema.optional(),
2893
- ...OperationLogConfigFieldSchema
2894
- });
2895
- var PostgresqlConfigSchema = NetworkConfigSchema.extend({
2896
- type: external_exports.literal("postgresql"),
2897
- ...OperationLogConfigFieldSchema
2898
- });
2899
- var MysqlConfigSchema = NetworkConfigSchema.extend({
2900
- type: external_exports.literal("mysql"),
2901
- /** 网络数据库URL;MySQL空串表示使用分字段,非空仅mysql://且拒绝query/fragment,优先于host/port/database/user/password */
2902
- url: external_exports.union([external_exports.literal("").transform(() => void 0), external_exports.url({ protocol: /^mysql$/ }).refine((value) => !value.includes("?") && !value.includes("#"))]).optional(),
2903
- /** 数据库:SQLite文件路径或:memory:;PostgreSQL必填名称;MySQL无URL时必填,URL模式从URL读取 */
2904
- database: external_exports.string().min(1).optional(),
2905
- /** TLS:PostgreSQL使用其模式;MySQL仅false或严格对象,空对象默认校验证书链和DNS身份 */
2906
- ssl: MysqlSslConfigSchema.optional(),
2907
- /** 连接池配置:PostgreSQL支持min/idleTimeout;MySQL仅max/acquireTimeout/queueLimit,拒绝其他字段 */
2908
- pool: MysqlPoolConfigSchema.optional(),
2909
- /** MySQL 特定选项 */
2910
- mysql: MysqlOptionsSchema.optional(),
2911
- ...OperationLogConfigFieldSchema
2912
- }).refine((config) => Boolean(config.url) || Boolean(config.database), { path: ["database"] });
2913
- var ReldbConfigSchema = external_exports.discriminatedUnion("type", [
2914
- SqliteConfigSchema,
2915
- PostgresqlConfigSchema,
2916
- MysqlConfigSchema
2917
- ]);
2918
- var CLOUD_FIELDS = {
2919
- "neon": ["apiKey"],
2920
- "upstash": ["email", "apiKey"],
2921
- "cloudflare-r2": ["accountId", "apiToken", "accessKeyId", "secretAccessKey"],
2922
- "resend": ["apiKey", "from"],
2923
- "aliyun": ["accessKeyId", "accessKeySecret", "signName"]
2924
- };
2925
- function targetConfig(requirement, environment) {
2926
- const result = {};
2927
- const parent = environment.targetValues[requirement.envPrefix];
2928
- if (parent) {
2929
- try {
2930
- Object.assign(result, JSON.parse(parent));
2931
- } catch {
2932
- return {};
2933
- }
2934
- }
2935
- for (const [key, value] of Object.entries(environment.targetValues)) {
2936
- if (key.startsWith(`${requirement.envPrefix}_`))
2937
- result[key.slice(requirement.envPrefix.length + 1).toLowerCase()] = value;
2938
- }
2939
- return result;
2940
- }
2941
- function canKeepMiddleware(requirement, backend, environment) {
2942
- const target = targetConfig(requirement, environment);
2943
- if (requirement.service === "ldap" && backend.backend === "disabled")
2944
- return environment.targetValues.HAI_IAM_LOGIN_LDAP === "false";
2945
- if (requirement.service === "ldap") {
2946
- if (!Object.keys(target).length)
2947
- return false;
2948
- } else if (target.type !== backend.backend) {
2949
- return false;
2950
- }
2951
- return backend.fields.filter((field) => field.required && !field.secret).every((field) => {
2952
- if ((target.url || requirement.config.url && Object.keys(target).length) && ["host", "port"].includes(field.name))
2953
- return true;
2954
- return target[field.name] !== void 0 && target[field.name] !== "" || target[field.name.toLowerCase()] !== void 0 && target[field.name.toLowerCase()] !== "";
2955
- }) && backend.fields.filter((field) => field.required && field.secret).every((field) => Boolean(requirement.config[field.name] || environment.values[`${requirement.envPrefix}_${field.name.toUpperCase()}`]));
2956
- }
2957
3011
  async function ask(question) {
2958
3012
  const answer = await prompts({ ...question, name: "value" });
2959
3013
  if (answer.value === void 0)
2960
3014
  throw new Error(cliM("cli_deploy_cancelled"));
2961
3015
  return answer.value;
2962
3016
  }
2963
- function validateField(value, field, backend) {
2964
- if (value === "")
2965
- return field.required ? cliM("cli_deploy_required") : true;
2966
- if (field.kind === "port")
2967
- return /^\d+$/.test(value) && Number(value) >= 1 && Number(value) <= 65535 || cliM("cli_deploy_invalid_value");
2968
- if (field.kind === "number")
2969
- return /^\d+$/.test(value) && Number(value) >= (field.minimum ?? 0) && Number(value) <= (field.maximum ?? Number.MAX_SAFE_INTEGER) || cliM("cli_deploy_invalid_value");
2970
- if (field.kind === "boolean")
2971
- return ["true", "false"].includes(value) || cliM("cli_deploy_invalid_value");
2972
- if (field.kind === "url") {
2973
- try {
2974
- return Boolean(new URL(value).protocol);
2975
- } catch {
2976
- return cliM("cli_deploy_invalid_value");
2977
- }
2978
- }
2979
- if (field.name === "ssl") {
2980
- const parsed2 = parseDocument(value);
2981
- const schema = backend === "mysql" ? MysqlSslConfigSchema : SslConfigSchema;
2982
- return parsed2.errors.length === 0 && schema.safeParse(parsed2.toJS()).success || cliM("cli_deploy_invalid_value");
2983
- }
2984
- if (field.name === "headers") {
2985
- try {
2986
- const headers = JSON.parse(value);
2987
- return headers !== null && typeof headers === "object" && !Array.isArray(headers) && Object.values(headers).every((item) => typeof item === "string") || cliM("cli_deploy_invalid_value");
2988
- } catch {
2989
- return cliM("cli_deploy_invalid_value");
2990
- }
2991
- }
2992
- if (field.name === "method")
2993
- return ["POST", "PUT"].includes(value) || cliM("cli_deploy_invalid_value");
2994
- return true;
2995
- }
2996
- function ensureConfigNode(appDir, requirement, backend) {
2997
- if (requirement.service === "ldap" && backend === "disabled")
2998
- return;
2999
- const directory = join(appDir, "config");
3000
- mkdirSync(directory, { recursive: true });
3001
- const path10 = join(directory, `_${requirement.configName}.yml`);
3002
- const document = parseDocument(existsSync(path10) ? readFileSync(path10, "utf8") : "{}\n");
3003
- if (document.errors.length)
3004
- throw new Error(cliM("cli_deploy_invalid_value"));
3005
- if (requirement.configPath.length && !document.hasIn(requirement.configPath))
3006
- document.setIn(requirement.configPath, document.createNode(requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service, type: backend } : {}));
3007
- if (requirement.service === "ldap") {
3008
- if (!document.hasIn(["login"]))
3009
- document.set("login", document.createNode({}));
3010
- if (!document.hasIn(["login", "ldap"]))
3011
- document.setIn(["login", "ldap"], `\${HAI_IAM_LOGIN_LDAP:false}`);
3012
- }
3013
- const content = document.toString();
3014
- if (!existsSync(path10) || readFileSync(path10, "utf8") !== content)
3015
- writeFileSync(path10, content);
3016
- }
3017
- function connectionEnv(requirement, backend, config) {
3018
- if (requirement.service === "ldap")
3019
- return backend === "disabled" ? { HAI_IAM_LOGIN_LDAP: "false" } : { HAI_IAM_LOGIN_LDAP: "true", [requirement.envPrefix]: JSON.stringify(config) };
3020
- return { [requirement.envPrefix]: JSON.stringify({ ...config, type: backend }) };
3021
- }
3022
3017
  async function configureDeployMiddleware(module, scan, environment, config, yes = false) {
3023
3018
  const modes = {};
3024
3019
  const services = {};
@@ -3030,7 +3025,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3030
3025
  if (!current)
3031
3026
  throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: requirement.backend } }));
3032
3027
  writeCliMessage(cliM("cli_deploy_middleware", { params: { service: requirement.service, backend: current.label, purpose: module.describeMiddlewareBackend(current), source: requirement.source } }));
3033
- const canKeep = canKeepMiddleware(requirement, current, environment);
3028
+ const canKeep = module.canKeepMiddleware(requirement, current, environment);
3034
3029
  const mode = yes ? canKeep ? "keep" : "default" : await ask({
3035
3030
  type: "select",
3036
3031
  name: "value",
@@ -3055,22 +3050,23 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3055
3050
  throw new Error(cliM("cli_deploy_invalid_value"));
3056
3051
  requirement.backend = backendName;
3057
3052
  const updates = {};
3058
- if (mode === "default" && backendName === "s3" && provider?.type === "docker-ssh") {
3053
+ let configuration;
3054
+ for (const option of mode === "default" && provider?.type === "docker-ssh" ? backend.containerOptions ?? [] : []) {
3059
3055
  const container = config.container && typeof config.container === "object" ? config.container : {};
3060
- const currentEngine = container.s3Engine === "minio" ? "minio" : "rustfs";
3061
- const engine = yes ? currentEngine : String(await ask({ type: "select", name: "value", message: cliM("cli_deploy_s3_engine"), initial: currentEngine === "rustfs" ? 0 : 1, choices: [{ title: "RustFS", value: "rustfs" }, { title: "MinIO", value: "minio" }] }));
3062
- if (!["rustfs", "minio"].includes(engine))
3056
+ const currentValue = String(container[option.field] ?? option.defaultValue);
3057
+ const selected = yes ? currentValue : String(await ask({ type: "select", name: "value", message: module.describeMiddlewareBackend(option), initial: Math.max(0, option.choices.findIndex((item) => item.value === currentValue)), choices: option.choices.map((item) => ({ title: item.label, value: item.value })) }));
3058
+ if (!option.choices.some((item) => item.value === selected))
3063
3059
  throw new Error(cliM("cli_deploy_invalid_value"));
3064
- config.container = { ...container, s3Engine: engine };
3065
- updates.HAI_DEPLOY_CONTAINER_S3ENGINE = engine;
3060
+ config.container = { ...container, [option.field]: selected };
3061
+ updates[option.variable] = selected;
3066
3062
  const deploymentFile = projectConfigPath(scan.appDir ?? environment.envDir);
3067
3063
  const project = readProjectConfig(deploymentFile);
3068
3064
  project.deploy ??= {};
3069
3065
  if (project.deploy.container != null && (typeof project.deploy.container !== "object" || Array.isArray(project.deploy.container)))
3070
3066
  throw new Error(cliM("cli_deploy_config_node", { params: { path: deploymentFile, field: "deploy.container" } }));
3071
3067
  project.deploy.container ??= {};
3072
- if (!("s3Engine" in project.deploy.container)) {
3073
- project.deploy.container.s3Engine = `\${HAI_DEPLOY_CONTAINER_S3ENGINE:rustfs}`;
3068
+ if (!(option.field in project.deploy.container)) {
3069
+ project.deploy.container[option.field] = `\${${option.variable}:${option.defaultValue}}`;
3074
3070
  writeFileSync(deploymentFile, `${JSON.stringify(project, null, 2)}
3075
3071
  `);
3076
3072
  }
@@ -3081,7 +3077,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3081
3077
  const prior = existingServices[requirement.service];
3082
3078
  const priorConfig = prior && typeof prior === "object" ? prior : {};
3083
3079
  const cloud = { provisioner: backend.provisioner };
3084
- for (const name of CLOUD_FIELDS[backend.provisioner]) {
3080
+ for (const name of backend.cloudFields ?? []) {
3085
3081
  const key = `HAI_DEPLOY_SERVICES_${requirement.service.toUpperCase()}_${name.toUpperCase()}`;
3086
3082
  const initial = String(priorConfig[name] ?? environment.values[key] ?? "");
3087
3083
  const label = `${backend.label} \u2014 ${cliM(`cli_deploy_cloud_input_${name}`)}`;
@@ -3093,32 +3089,25 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3093
3089
  }
3094
3090
  services[requirement.service] = cloud;
3095
3091
  } else {
3096
- if (mode === "default" && ["aliyun-sms", "api"].includes(backendName))
3092
+ if (mode === "default" && backend.containerSupported === false)
3097
3093
  throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: backend.label } }));
3098
- const connection = backendName === current.backend ? { ...requirement.config } : requirement.configName === "reach" ? { name: requirement.config.name ?? requirement.service } : {};
3094
+ const connection = backendName === current.backend ? { ...requirement.config } : requirement.config.name === void 0 ? {} : { name: requirement.config.name };
3099
3095
  for (const field of backend.fields) {
3100
3096
  const previous2 = backendName === current.backend ? requirement.config[field.name] : void 0;
3101
3097
  const initial = previous2 === void 0 ? field.defaultValue ?? "" : typeof previous2 === "object" ? JSON.stringify(previous2) : String(previous2);
3102
3098
  const variable = `${requirement.envPrefix}_${field.name.toUpperCase()}`;
3103
3099
  const info = moduleEnvironmentVariables(requirement.configName).find((item) => item.name === variable);
3104
- const value = mode === "custom" && !yes ? String(await ask({ type: field.secret ? "password" : "text", name: "value", message: cliM("cli_deploy_field", { params: { description: info?.description ?? field.name, constraint: info?.constraint ?? field.kind ?? "string" } }), initial, validate: (input) => validateField(String(input), field, backendName) })) : initial;
3105
- if (field.name === "ssl" && validateField(value, field, backendName) !== true)
3106
- throw new Error(cliM("cli_deploy_invalid_value"));
3107
- if (value !== "") {
3108
- connection[field.name] = field.kind === "number" || field.kind === "port" ? Number(value) : field.kind === "boolean" ? value === "true" : field.name === "headers" || field.name === "ssl" ? parseDocument(value).toJS() : value;
3109
- } else {
3100
+ const value = mode === "custom" && !yes ? String(await ask({ type: field.secret ? "password" : "text", name: "value", message: cliM("cli_deploy_field", { params: { description: info?.description ?? field.name, constraint: info?.constraint ?? field.kind ?? "string" } }), initial, validate: (input) => module.validateMiddlewareField({ value: String(input), field, backend: backendName }) || cliM("cli_deploy_invalid_value") })) : initial;
3101
+ const parsed2 = module.parseMiddlewareField({ value, field, backend: backendName, managed: mode === "default" });
3102
+ if (!parsed2.success)
3103
+ throw new Error(parsed2.error.message);
3104
+ if (parsed2.data === void 0)
3110
3105
  delete connection[field.name];
3111
- }
3106
+ else connection[field.name] = parsed2.data;
3112
3107
  }
3113
- if (requirement.service === "db") {
3114
- const validated = ReldbConfigSchema.safeParse({ ...connection, type: backendName });
3115
- if (!validated.success)
3116
- throw new Error(cliM("cli_deploy_config_invalid", { params: { path: join(scan.appDir ?? ".", "config", "_db.yml"), fields: validated.error.issues.map((issue) => issue.path.join(".")).join(", ") } }));
3117
- }
3118
- Object.assign(updates, connectionEnv(requirement, backendName, connection));
3108
+ configuration = connection;
3119
3109
  }
3120
- ensureConfigNode(scan.appDir ?? ".", requirement, backendName);
3121
- const saved = module.saveDeploymentEnvironment(environment, updates);
3110
+ const saved = module.saveMiddlewareEnvironment(environment, { appDir: scan.appDir ?? ".", requirement, backend: backendName, configuration, updates });
3122
3111
  if (!saved.success)
3123
3112
  throw new Error(saved.error.message);
3124
3113
  writeCliMessage(cliM("cli_deploy_saved", { params: { service: requirement.service } }));
@@ -3222,7 +3211,7 @@ async function configureDeployTarget(module, config, environment, nonInteractive
3222
3211
  const validated = module.ProviderConfigSchema.safeParse(provider);
3223
3212
  if (!validated.success)
3224
3213
  throw new Error(cliM("cli_deploy_invalid_value"));
3225
- const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: JSON.stringify(validated.data) }, { private: true });
3214
+ const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: validated.data }, { private: true });
3226
3215
  if (!saved.success)
3227
3216
  throw new Error(saved.error.message);
3228
3217
  config.provider = validated.data;
@@ -3273,7 +3262,7 @@ async function configureDeployHttps(module, config, environment, nonInteractive,
3273
3262
  const parsed2 = module.ProviderConfigSchema.safeParse({ ...provider, expose: { ...expose, ...https?.type === "letsencrypt" ? { hostPort: 80 } : {}, https } });
3274
3263
  if (!parsed2.success)
3275
3264
  throw new Error(cliM("cli_deploy_invalid_value"));
3276
- const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: JSON.stringify(parsed2.data) }, { private: true });
3265
+ const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: parsed2.data }, { private: true });
3277
3266
  if (!saved.success)
3278
3267
  throw new Error(saved.error.message);
3279
3268
  config.provider = parsed2.data;
@@ -3281,11 +3270,11 @@ async function configureDeployHttps(module, config, environment, nonInteractive,
3281
3270
  }
3282
3271
  async function deployCommand(options) {
3283
3272
  const spinner = ora();
3284
- const cwd = options.cwd ?? process4.cwd();
3273
+ const cwd = options.cwd ?? process5.cwd();
3285
3274
  let appDir = resolve(cwd, options.appDir ?? ".");
3286
3275
  let closeDeploy = null;
3287
3276
  let restoreProjectLogger;
3288
- const originalEnv = { ...process4.env };
3277
+ const originalEnv = { ...process5.env };
3289
3278
  const previousLogLevel = core.logger.getLevel();
3290
3279
  core.logger.setLevel("fatal");
3291
3280
  try {
@@ -3295,21 +3284,21 @@ async function deployCommand(options) {
3295
3284
  throw new Error(cliM("cli_build_runtime_missing"));
3296
3285
  if (options.artifactDir && options.build)
3297
3286
  throw new Error(cliM("cli_deploy_build_artifact_conflict"));
3298
- if (!options.build && (options.concurrency !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.httpProxy === false))
3287
+ if (!options.build && (options.concurrency !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.npmSource !== void 0 || options.npmRegistry !== void 0))
3299
3288
  throw new Error(cliM("cli_deploy_build_options_required"));
3300
3289
  let selectedServices = options.services?.split(",").map((name) => name.trim());
3301
3290
  if (selectedServices && (!selectedServices.length || selectedServices.some((name) => !/^[a-z][a-z0-9-]*$/.test(name)) || new Set(selectedServices).size !== selectedServices.length))
3302
3291
  throw new Error(cliM("cli_build_selection_invalid"));
3303
3292
  let environmentName = options.env;
3304
3293
  if (!environmentName) {
3305
- if (!process4.stdin.isTTY)
3294
+ if (!process5.stdin.isTTY)
3306
3295
  throw new Error(cliM("cli_deploy_environment_hint"));
3307
3296
  const answer = await prompts({ type: "text", name: "environment", message: cliM("cli_deploy_environment"), initial: "prod" });
3308
3297
  environmentName = answer.environment;
3309
3298
  if (!environmentName)
3310
3299
  throw new Error(cliM("cli_deploy_cancelled"));
3311
3300
  }
3312
- if (!options.plan && !options.yes && !process4.stdin.isTTY)
3301
+ if (!options.plan && !options.yes && !process5.stdin.isTTY)
3313
3302
  throw new Error(cliM("cli_deploy_environment_hint"));
3314
3303
  let deployModule;
3315
3304
  try {
@@ -3328,7 +3317,7 @@ async function deployCommand(options) {
3328
3317
  localModule = localRequire.resolve("@h-ai/deploy");
3329
3318
  } catch {
3330
3319
  }
3331
- deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-24PXSKE6.js');
3320
+ deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-NXCCU6GO.js');
3332
3321
  } catch {
3333
3322
  writeCliError(chalk.red(cliM("cli_deploy_module_missing")));
3334
3323
  writeCliMessage(chalk.cyan(" pnpm add -D @h-ai/deploy"));
@@ -3356,9 +3345,9 @@ async function deployCommand(options) {
3356
3345
  if (!environment.success)
3357
3346
  throw new Error(environment.error.message);
3358
3347
  if (options.plan) {
3359
- if (options.build || options.bundleOnly || options.output)
3348
+ if (options.build || options.bundleOnly || options.output || options.imageSource !== void 0 || options.imageMirrors !== void 0 || options.npmSource !== void 0 || options.npmRegistry !== void 0 || options.proxyMode !== void 0 || options.proxyUrl !== void 0 || options.proxyBypass !== void 0 || options.httpProxy === false)
3360
3349
  throw new Error(cliM("cli_deploy_plan_options"));
3361
- const loaded = deployModule.loadDeploymentConfig("deploy", buildFile, { ...process4.env, ...environment.data.values });
3350
+ const loaded = deployModule.loadDeploymentConfig("deploy", buildFile, { ...process5.env, ...environment.data.values });
3362
3351
  if (!loaded.success)
3363
3352
  throw new Error(loaded.error.message);
3364
3353
  const validated = deployModule.DeployConfigSchema.safeParse(loaded.data);
@@ -3370,7 +3359,7 @@ async function deployCommand(options) {
3370
3359
  writeCliMessage(JSON.stringify(plan.data, null, 2));
3371
3360
  return;
3372
3361
  }
3373
- Object.assign(process4.env, environment.data.values);
3362
+ Object.assign(process5.env, environment.data.values);
3374
3363
  spinner.start(cliM("cli_deploy_load_credentials"));
3375
3364
  const credResult = deploy.credentials.load();
3376
3365
  if (!credResult.success) {
@@ -3384,7 +3373,7 @@ async function deployCommand(options) {
3384
3373
  writeCliMessage(chalk.cyan(cliM("cli_deploy_config_hint")));
3385
3374
  throw new Error(cliM("cli_deploy_config_missing", { params: { path: configPath } }));
3386
3375
  }
3387
- const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process4.env);
3376
+ const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process5.env);
3388
3377
  if (!loadedConfig.success)
3389
3378
  throw new Error(loadedConfig.error.message);
3390
3379
  const deployConfig = loadedConfig.data;
@@ -3429,13 +3418,16 @@ async function deployCommand(options) {
3429
3418
  }
3430
3419
  }
3431
3420
  spinner.succeed(cliM("cli_deploy_scanned", { params: { name: scan.appName, sveltekit: String(scan.isSvelteKit), services: scan.requiredServices.join(", ") || cliM("cli_deploy_none") } }));
3421
+ const downloads = configObject(deployConfig.provider).type === "docker-ssh" ? await resolveBuildDownloads(buildRoot, options) : void 0;
3422
+ let builtImages = false;
3432
3423
  if (options.build) {
3433
3424
  if (!existsSync(buildFile))
3434
3425
  throw new Error(cliM("cli_build_images_missing"));
3435
3426
  const imageNames2 = selectedServices?.filter((name) => ["serv", "web"].includes(name));
3436
3427
  if (!selectedServices || imageNames2?.length) {
3437
3428
  writeCliMessage(cliM("cli_deploy_build_started"));
3438
- await buildCommand({ cwd: buildRoot, target: "image", images: imageNames2?.join(","), concurrency: options.concurrency, runtime: options.runtime, network: options.network, sqlitePrebuildMirror: options.sqlitePrebuildMirror, httpProxy: options.httpProxy });
3429
+ builtImages = true;
3430
+ await buildCommand({ ...options, cwd: buildRoot, target: "image", images: imageNames2?.join(","), yes: true, imageSource: downloads?.imageSource.type, imageMirrors: downloads?.imageSource.registries ? Object.entries(downloads.imageSource.registries).map(([key, value]) => `${key}=${value}`).join(",") : void 0, npmSource: downloads?.npmSource.type, npmRegistry: downloads?.npmSource.registry, proxyMode: downloads?.proxy.mode, proxyUrl: downloads?.proxy.url, proxyBypass: downloads?.proxy.bypass });
3439
3431
  }
3440
3432
  }
3441
3433
  if (selectedServices)
@@ -3460,15 +3452,15 @@ async function deployCommand(options) {
3460
3452
  if (options.bundleOnly)
3461
3453
  await configureDeployTarget(deployModule, deployConfig, environment.data, options.yes, true);
3462
3454
  await configureDeployHttps(deployModule, deployConfig, environment.data, options.yes, selectedServices !== void 0);
3463
- for (const key of Object.keys(process4.env)) {
3455
+ for (const key of Object.keys(process5.env)) {
3464
3456
  if (originalEnv[key] === void 0)
3465
- delete process4.env[key];
3457
+ delete process5.env[key];
3466
3458
  }
3467
- Object.assign(process4.env, originalEnv);
3459
+ Object.assign(process5.env, originalEnv);
3468
3460
  const refreshed = deployModule.loadDeploymentEnvironment({ environment: environment.data.environment, envDir: environment.data.envDir });
3469
3461
  if (!refreshed.success)
3470
3462
  throw new Error(refreshed.error.message);
3471
- Object.assign(process4.env, refreshed.data.values);
3463
+ Object.assign(process5.env, refreshed.data.values);
3472
3464
  spinner.start(cliM("cli_deploy_initializing"));
3473
3465
  const validatedConfig = deployModule.DeployConfigSchema.safeParse(deployConfig);
3474
3466
  if (!validatedConfig.success)
@@ -3480,7 +3472,11 @@ async function deployCommand(options) {
3480
3472
  closeDeploy = () => deploy.close();
3481
3473
  spinner.succeed(cliM("cli_deploy_initialized"));
3482
3474
  spinner.start(cliM("cli_deploy_deploying"));
3475
+ if (downloads && !builtImages && (!selectedServices || selectedServices.some((name) => !["serv", "web"].includes(name))))
3476
+ await preflightDownloadProxy(options.runtime ?? "auto", downloads.proxy);
3483
3477
  const deployResult = await deploy.deployApp(appDir, {
3478
+ imageSource: downloads?.imageSource,
3479
+ proxy: downloads?.proxy,
3484
3480
  selectedServices,
3485
3481
  runtime: options.runtime,
3486
3482
  onProgress: (stage) => writeCliMessage(cliM(`cli_deploy_stage_${stage}`)),
@@ -3542,14 +3538,14 @@ async function deployCommand(options) {
3542
3538
  await closeDeploy();
3543
3539
  } catch (error) {
3544
3540
  writeCliError("Failed to close deploy module", error);
3545
- process4.exitCode = 1;
3541
+ process5.exitCode = 1;
3546
3542
  }
3547
3543
  }
3548
- for (const key of Object.keys(process4.env)) {
3544
+ for (const key of Object.keys(process5.env)) {
3549
3545
  if (originalEnv[key] === void 0)
3550
- delete process4.env[key];
3546
+ delete process5.env[key];
3551
3547
  }
3552
- Object.assign(process4.env, originalEnv);
3548
+ Object.assign(process5.env, originalEnv);
3553
3549
  core.logger.setLevel(previousLogLevel);
3554
3550
  restoreProjectLogger?.();
3555
3551
  }
@@ -4383,7 +4379,7 @@ async function startFeedbackViewer(bundle, options = {}) {
4383
4379
  async function feedbackCommand(action, bundlePath, options = {}) {
4384
4380
  if (!["inspect", "summary", "evidence", "view"].includes(action))
4385
4381
  throw new Error(cliM("cli_feedback_invalidAction"));
4386
- const filePath = path2.resolve(options.cwd ?? process4.cwd(), bundlePath);
4382
+ const filePath = path2.resolve(options.cwd ?? process5.cwd(), bundlePath);
4387
4383
  const metadata = await stat(filePath);
4388
4384
  if (!metadata.isFile() || metadata.size > 64 * 1024 * 1024)
4389
4385
  throw new Error(cliM("cli_feedback_invalidBundle"));
@@ -4417,12 +4413,12 @@ async function feedbackCommand(action, bundlePath, options = {}) {
4417
4413
  writeCliMessage(cliM("cli_feedback_viewerStarted", { params: { url: viewer.url } }));
4418
4414
  await new Promise((resolve7, reject) => {
4419
4415
  const stop = () => {
4420
- process4.off("SIGINT", stop);
4421
- process4.off("SIGTERM", stop);
4416
+ process5.off("SIGINT", stop);
4417
+ process5.off("SIGTERM", stop);
4422
4418
  viewer.close().then(resolve7, reject);
4423
4419
  };
4424
- process4.once("SIGINT", stop);
4425
- process4.once("SIGTERM", stop);
4420
+ process5.once("SIGINT", stop);
4421
+ process5.once("SIGTERM", stop);
4426
4422
  });
4427
4423
  }
4428
4424
  function appendAt(source, marker, entry) {
@@ -5042,7 +5038,7 @@ async function initProject(options) {
5042
5038
  const { project, existing, missing } = result;
5043
5039
  spinner.succeed(cliM("cli_initDetected", { params: { name: chalk.cyan(project.name) } }));
5044
5040
  if (project.isHaiProject) {
5045
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-COTP767H.js');
5041
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-S2YH642Z.js');
5046
5042
  const generatedFiles = await generateProjectAiSupport(cwd, resolveSkillNamesForPackages(project.haiPackages), {
5047
5043
  overwriteSkills: false,
5048
5044
  overwriteBridgeFiles: false
@@ -5120,7 +5116,7 @@ async function initProject(options) {
5120
5116
  throw error;
5121
5117
  }
5122
5118
  }
5123
- function object(value) {
5119
+ function object2(value) {
5124
5120
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
5125
5121
  }
5126
5122
  function declaresValueExport(source, name) {
@@ -5153,9 +5149,9 @@ async function inspectFrameworkUpgrade(directory, version) {
5153
5149
  throw new Error(cliM("cli_upgrade_exact_version"));
5154
5150
  const root = realpathSync(resolve(directory));
5155
5151
  const workspaceFile = join(root, "pnpm-workspace.yaml");
5156
- const workspace = existsSync(workspaceFile) ? object(parse(readFileSync(workspaceFile, "utf8"))) : {};
5157
- const rootManifest = object(JSON.parse(readFileSync(join(root, "package.json"), "utf8")));
5158
- const patterns = Array.isArray(workspace.packages) ? workspace.packages : Array.isArray(rootManifest.workspaces) ? rootManifest.workspaces : Array.isArray(object(rootManifest.workspaces).packages) ? object(rootManifest.workspaces).packages : [];
5152
+ const workspace = existsSync(workspaceFile) ? object2(parse(readFileSync(workspaceFile, "utf8"))) : {};
5153
+ const rootManifest = object2(JSON.parse(readFileSync(join(root, "package.json"), "utf8")));
5154
+ const patterns = Array.isArray(workspace.packages) ? workspace.packages : Array.isArray(rootManifest.workspaces) ? rootManifest.workspaces : Array.isArray(object2(rootManifest.workspaces).packages) ? object2(rootManifest.workspaces).packages : [];
5159
5155
  const directories = /* @__PURE__ */ new Set([root]);
5160
5156
  const safePatterns = patterns.filter((pattern) => typeof pattern === "string" && !isAbsolute(pattern.replace(/^!/, "")) && !pattern.split(/[\\/]/).includes(".."));
5161
5157
  const exclude = ["**/node_modules/**", ...safePatterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => `${pattern.slice(1)}/package.json`)];
@@ -5163,29 +5159,29 @@ async function inspectFrameworkUpgrade(directory, version) {
5163
5159
  for await (const manifest of glob(`${pattern}/package.json`, { cwd: root, exclude })) {
5164
5160
  const location = realpathSync(dirname(join(root, manifest)));
5165
5161
  const fromRoot = relative(root, location);
5166
- if (!isAbsolute(fromRoot) && fromRoot !== ".." && !fromRoot.startsWith(`..${process4.platform === "win32" ? "\\" : "/"}`))
5162
+ if (!isAbsolute(fromRoot) && fromRoot !== ".." && !fromRoot.startsWith(`..${process5.platform === "win32" ? "\\" : "/"}`))
5167
5163
  directories.add(location);
5168
5164
  }
5169
5165
  }
5170
5166
  const packages = /* @__PURE__ */ new Set(["@h-ai/cli", "@h-ai/deploy"]);
5171
5167
  const drift = [];
5172
- const catalog = object(workspace.catalog);
5168
+ const catalog = object2(workspace.catalog);
5173
5169
  const lockPath = join(root, "pnpm-lock.yaml");
5174
5170
  const lock = existsSync(lockPath) ? parseAllDocuments(readFileSync(lockPath, "utf8")).reduce((all, document) => {
5175
5171
  if (document.errors.length)
5176
5172
  throw new Error(cliM("cli_upgrade_lock_invalid"));
5177
- const part = object(document.toJS());
5178
- return { ...all, ...part, importers: { ...object(all.importers), ...object(part.importers) } };
5173
+ const part = object2(document.toJS());
5174
+ return { ...all, ...part, importers: { ...object2(all.importers), ...object2(part.importers) } };
5179
5175
  }, {}) : {};
5180
5176
  for (const location of directories) {
5181
- const manifest = object(JSON.parse(readFileSync(join(location, "package.json"), "utf8")));
5182
- const dependencies = { ...object(manifest.dependencies), ...object(manifest.devDependencies), ...object(manifest.optionalDependencies) };
5183
- const importer = object(object(lock.importers)[relative(root, location).replaceAll("\\", "/") || "."]);
5177
+ const manifest = object2(JSON.parse(readFileSync(join(location, "package.json"), "utf8")));
5178
+ const dependencies = { ...object2(manifest.dependencies), ...object2(manifest.devDependencies), ...object2(manifest.optionalDependencies) };
5179
+ const importer = object2(object2(lock.importers)[relative(root, location).replaceAll("\\", "/") || "."]);
5184
5180
  for (const [name, specifier] of Object.entries(dependencies)) {
5185
5181
  if (!/^@h-ai\/[\w-]+$/.test(name) || typeof specifier !== "string")
5186
5182
  continue;
5187
5183
  packages.add(name);
5188
- const declared = specifier.startsWith("catalog:") ? String(object(specifier === "catalog:" ? catalog : object(workspace.catalogs)[specifier.slice(8)])[name] ?? "") : specifier;
5184
+ const declared = specifier.startsWith("catalog:") ? String(object2(specifier === "catalog:" ? catalog : object2(workspace.catalogs)[specifier.slice(8)])[name] ?? "") : specifier;
5189
5185
  const issues = [];
5190
5186
  if (declared !== version && !declared.startsWith("workspace:"))
5191
5187
  issues.push("declared-version-drift");
@@ -5193,7 +5189,7 @@ async function inspectFrameworkUpgrade(directory, version) {
5193
5189
  try {
5194
5190
  const path10 = findPackageJSON(name, pathToFileURL(join(location, "package.json")));
5195
5191
  if (path10) {
5196
- const candidate = object(JSON.parse(readFileSync(path10, "utf8")));
5192
+ const candidate = object2(JSON.parse(readFileSync(path10, "utf8")));
5197
5193
  if (candidate.name === name && typeof candidate.version === "string")
5198
5194
  installed = candidate.version;
5199
5195
  }
@@ -5201,13 +5197,13 @@ async function inspectFrameworkUpgrade(directory, version) {
5201
5197
  }
5202
5198
  if (installed !== version)
5203
5199
  issues.push(installed ? "installed-version-drift" : "not-installed");
5204
- const locked = object(object(importer.dependencies)[name] ?? object(importer.devDependencies)[name] ?? object(importer.optionalDependencies)[name]);
5200
+ const locked = object2(object2(importer.dependencies)[name] ?? object2(importer.devDependencies)[name] ?? object2(importer.optionalDependencies)[name]);
5205
5201
  if (typeof locked.version !== "string" || !locked.version.startsWith("link:") && locked.version.split("(")[0] !== version)
5206
5202
  issues.push("lock-version-drift");
5207
5203
  drift.push({ package: name, source: relative(root, join(location, "package.json")).replaceAll("\\", "/"), declared, installed, issues });
5208
5204
  }
5209
5205
  }
5210
- const registry = new URL(process4.env.npm_config_registry ?? "https://registry.npmjs.org/");
5206
+ const registry = new URL(process5.env.npm_config_registry ?? "https://registry.npmjs.org/");
5211
5207
  if (registry.username || registry.password || registry.protocol !== "https:")
5212
5208
  throw new Error(cliM("cli_upgrade_registry"));
5213
5209
  const releases = [];
@@ -5222,11 +5218,11 @@ async function inspectFrameworkUpgrade(directory, version) {
5222
5218
  const response = await fetch(new URL(`${encodeURIComponent(name)}/${encodeURIComponent(version)}`, registry), { signal: AbortSignal.timeout(2e4) });
5223
5219
  if (!response.ok)
5224
5220
  throw new Error("Release missing");
5225
- const metadata = object(await response.json());
5226
- const dist = object(metadata.dist);
5221
+ const metadata = object2(await response.json());
5222
+ const dist = object2(metadata.dist);
5227
5223
  if (metadata.version !== version || typeof dist.tarball !== "string" || typeof dist.integrity !== "string")
5228
5224
  throw new Error("Invalid metadata");
5229
- for (const dependency of Object.keys(object(metadata.dependencies))) {
5225
+ for (const dependency of Object.keys(object2(metadata.dependencies))) {
5230
5226
  if (/^@h-ai\/[\w-]+$/.test(dependency) && !packages.has(dependency)) {
5231
5227
  packages.add(dependency);
5232
5228
  pending.push(dependency);
@@ -5250,7 +5246,7 @@ async function inspectFrameworkUpgrade(directory, version) {
5250
5246
  throw new Error("Integrity mismatch");
5251
5247
  const files = tarFiles(bytes);
5252
5248
  const manifest = files.get("package/package.json");
5253
- if (!manifest || object(JSON.parse(new TextDecoder().decode(manifest))).version !== version || !files.has("package/dist/index.js"))
5249
+ if (!manifest || object2(JSON.parse(new TextDecoder().decode(manifest))).version !== version || !files.has("package/dist/index.js"))
5254
5250
  throw new Error("Incomplete artifact");
5255
5251
  const migration = { "@h-ai/iam": "migrateIam", "@h-ai/reach": "migrateReach", "@h-ai/scheduler": "migrateScheduler", "@h-ai/audit": "migrateAudit" }[name];
5256
5252
  if (migration && declaresValueExport(new TextDecoder().decode(files.get("package/dist/index.d.ts")), migration) && declaresValueExport(new TextDecoder().decode(files.get("package/dist/index.js")), migration))
@@ -5277,13 +5273,13 @@ async function inspectFrameworkUpgrade(directory, version) {
5277
5273
  dirtyFiles = execFileSync("git", ["status", "--porcelain", "--", "."], { cwd: root, encoding: "utf8", windowsHide: true, stdio: "pipe" }).trim().split("\n").filter(Boolean);
5278
5274
  } catch {
5279
5275
  }
5280
- return { version, releaseComplete: releases.every((item) => item.available), modified: false, releases, drift, skills, dirtyFiles, customizations: { overrides: [.../* @__PURE__ */ new Set([...Object.keys(object(workspace.overrides)), ...Object.keys(object(rootManifest.overrides)), ...Object.keys(object(object(rootManifest.pnpm).overrides))])], patches: [.../* @__PURE__ */ new Set([...Object.keys(object(workspace.patchedDependencies)), ...Object.keys(object(object(rootManifest.pnpm).patchedDependencies))])] }, migration: { preflight: migrationPreflight, inspectModuleReadmes: true, exclusiveAccess: true, backup: ["database", "shared-session-cache"], rollback: "restore-compatible-database-and-binaries", sessions: "legacy-credential-sessions-require-login" } };
5276
+ return { version, releaseComplete: releases.every((item) => item.available), modified: false, releases, drift, skills, dirtyFiles, customizations: { overrides: [.../* @__PURE__ */ new Set([...Object.keys(object2(workspace.overrides)), ...Object.keys(object2(rootManifest.overrides)), ...Object.keys(object2(object2(rootManifest.pnpm).overrides))])], patches: [.../* @__PURE__ */ new Set([...Object.keys(object2(workspace.patchedDependencies)), ...Object.keys(object2(object2(rootManifest.pnpm).patchedDependencies))])] }, migration: { preflight: migrationPreflight, inspectModuleReadmes: true, exclusiveAccess: true, backup: ["database", "shared-session-cache"], rollback: "restore-compatible-database-and-binaries", sessions: "legacy-credential-sessions-require-login" } };
5281
5277
  }
5282
5278
  async function upgradeCommand(version, options = {}) {
5283
- const report = await inspectFrameworkUpgrade(options.cwd ?? process4.cwd(), version);
5279
+ const report = await inspectFrameworkUpgrade(options.cwd ?? process5.cwd(), version);
5284
5280
  writeCliMessage(JSON.stringify(report, null, 2));
5285
5281
  if (!report.releaseComplete)
5286
- process4.exitCode = 1;
5282
+ process5.exitCode = 1;
5287
5283
  }
5288
5284
 
5289
5285
  // src/cli-main.ts
@@ -5308,7 +5304,7 @@ cli.command("create [name]", cliM("cli_help_create")).option("--type <type>", cl
5308
5304
  cwd: options.cwd
5309
5305
  });
5310
5306
  } catch {
5311
- process4.exitCode = 1;
5307
+ process5.exitCode = 1;
5312
5308
  }
5313
5309
  });
5314
5310
  cli.command("add [module]", cliM("cli_help_add")).option("--no-install", cliM("cli_help_noInstall")).action(async (module, options) => {
@@ -5320,7 +5316,7 @@ cli.command("add [module]", cliM("cli_help_add")).option("--no-install", cliM("c
5320
5316
  cwd: options.cwd
5321
5317
  });
5322
5318
  } catch {
5323
- process4.exit(1);
5319
+ process5.exit(1);
5324
5320
  }
5325
5321
  });
5326
5322
  cli.command("init", cliM("cli_help_init")).option("-f, --force", cliM("cli_help_forceConfig")).action(async (options) => {
@@ -5331,7 +5327,7 @@ cli.command("init", cliM("cli_help_init")).option("-f, --force", cliM("cli_help_
5331
5327
  cwd: options.cwd
5332
5328
  });
5333
5329
  } catch {
5334
- process4.exit(1);
5330
+ process5.exit(1);
5335
5331
  }
5336
5332
  });
5337
5333
  cli.command("generate [type] [name]", cliM("cli_help_generate")).option("-w, --workspace <name>", cliM("cli_help_workspace")).alias("g").option("-o, --output <path>", cliM("cli_help_output")).option("-f, --force", cliM("cli_help_force")).action(async (type, name, options) => {
@@ -5346,15 +5342,15 @@ cli.command("generate [type] [name]", cliM("cli_help_generate")).option("-w, --w
5346
5342
  cwd: options.cwd
5347
5343
  });
5348
5344
  } catch {
5349
- process4.exit(1);
5345
+ process5.exit(1);
5350
5346
  }
5351
5347
  });
5352
- cli.command("build", cliM("cli_help_build")).option("--target <target>", cliM("cli_help_target")).option("--images <names>", cliM("cli_help_images")).option("--concurrency <count>", cliM("cli_help_buildConcurrency")).option("--runtime <runtime>", cliM("cli_help_buildRuntime")).option("--network <network>", cliM("cli_help_buildNetwork")).option("--platform <platform>", cliM("cli_help_platform")).option("--arch <arch>", cliM("cli_help_arch")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_sqliteMirror")).option("--no-http-proxy", cliM("cli_help_noHttpProxy")).action(async (options) => {
5348
+ cli.command("build", cliM("cli_help_build")).option("--target <target>", cliM("cli_help_target")).option("--images <names>", cliM("cli_help_images")).option("--concurrency <count>", cliM("cli_help_buildConcurrency")).option("--runtime <runtime>", cliM("cli_help_buildRuntime")).option("--network <network>", cliM("cli_help_buildNetwork")).option("--platform <platform>", cliM("cli_help_platform")).option("--arch <arch>", cliM("cli_help_arch")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_sqliteMirror")).option("--no-http-proxy", cliM("cli_help_noHttpProxy")).option("-y, --yes", cliM("cli_help_deployYes")).option("--image-source <source>", cliM("cli_help_imageSource")).option("--image-mirrors <mappings>", cliM("cli_help_imageMirrors")).option("--npm-source <source>", cliM("cli_help_npmSource")).option("--npm-registry <url>", cliM("cli_help_npmRegistry")).option("--proxy-mode <mode>", cliM("cli_help_proxyMode")).option("--proxy-url <url>", cliM("cli_help_proxyUrl")).option("--proxy-bypass <hosts>", cliM("cli_help_proxyBypass")).action(async (options) => {
5353
5349
  try {
5354
5350
  await buildCommand(options);
5355
5351
  } catch (error) {
5356
5352
  writeCliMessage(error instanceof Error ? error.message : String(error));
5357
- process4.exitCode = 1;
5353
+ process5.exitCode = 1;
5358
5354
  }
5359
5355
  });
5360
5356
  cli.command("upgrade <version>", cliM("cli_help_upgrade")).action(async (version, options) => {
@@ -5362,10 +5358,10 @@ cli.command("upgrade <version>", cliM("cli_help_upgrade")).action(async (version
5362
5358
  await upgradeCommand(version, options);
5363
5359
  } catch (error) {
5364
5360
  writeCliMessage(error instanceof Error ? error.message : String(error));
5365
- process4.exitCode = 1;
5361
+ process5.exitCode = 1;
5366
5362
  }
5367
5363
  });
5368
- cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("cli_help_deployPlan")).option("--inspect-remote", cliM("cli_help_inspectRemote")).option("--env <environment>", cliM("cli_help_environment")).option("--env-dir <path>", cliM("cli_help_envDir")).option("-y, --yes", cliM("cli_help_deployYes")).option("--skip-middleware", cliM("cli_help_skipMiddleware")).option("--artifact-dir <path>", cliM("cli_help_artifactDir")).option("--services <names>", cliM("cli_help_services")).option("--build", cliM("cli_help_deployBuild")).option("--concurrency <count>", cliM("cli_help_buildConcurrency")).option("--network <network>", cliM("cli_help_deployNetwork")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_deploySqliteMirror")).option("--no-http-proxy", cliM("cli_help_deployNoProxy")).option("--runtime <runtime>", cliM("cli_help_deployRuntime")).option("--bundle-only", cliM("cli_help_bundleOnly")).option("--output <path>", cliM("cli_help_bundleOutput")).action(async (appDir, options) => {
5364
+ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("cli_help_deployPlan")).option("--inspect-remote", cliM("cli_help_inspectRemote")).option("--env <environment>", cliM("cli_help_environment")).option("--env-dir <path>", cliM("cli_help_envDir")).option("-y, --yes", cliM("cli_help_deployYes")).option("--skip-middleware", cliM("cli_help_skipMiddleware")).option("--artifact-dir <path>", cliM("cli_help_artifactDir")).option("--services <names>", cliM("cli_help_services")).option("--build", cliM("cli_help_deployBuild")).option("--concurrency <count>", cliM("cli_help_buildConcurrency")).option("--network <network>", cliM("cli_help_deployNetwork")).option("--sqlite-prebuild-mirror <url>", cliM("cli_help_deploySqliteMirror")).option("--no-http-proxy", cliM("cli_help_deployNoProxy")).option("--image-source <source>", cliM("cli_help_imageSource")).option("--image-mirrors <mappings>", cliM("cli_help_imageMirrors")).option("--npm-source <source>", cliM("cli_help_npmSource")).option("--npm-registry <url>", cliM("cli_help_npmRegistry")).option("--proxy-mode <mode>", cliM("cli_help_proxyMode")).option("--proxy-url <url>", cliM("cli_help_proxyUrl")).option("--proxy-bypass <hosts>", cliM("cli_help_proxyBypass")).option("--runtime <runtime>", cliM("cli_help_deployRuntime")).option("--bundle-only", cliM("cli_help_bundleOnly")).option("--output <path>", cliM("cli_help_bundleOutput")).action(async (appDir, options) => {
5369
5365
  try {
5370
5366
  await deployCommand({
5371
5367
  appDir,
@@ -5382,6 +5378,13 @@ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("c
5382
5378
  network: options.network,
5383
5379
  sqlitePrebuildMirror: options.sqlitePrebuildMirror,
5384
5380
  httpProxy: options.httpProxy,
5381
+ imageSource: options.imageSource,
5382
+ imageMirrors: options.imageMirrors,
5383
+ npmSource: options.npmSource,
5384
+ npmRegistry: options.npmRegistry,
5385
+ proxyMode: options.proxyMode,
5386
+ proxyUrl: options.proxyUrl,
5387
+ proxyBypass: options.proxyBypass,
5385
5388
  runtime: options.runtime,
5386
5389
  bundleOnly: options.bundleOnly,
5387
5390
  output: options.output,
@@ -5389,7 +5392,7 @@ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("c
5389
5392
  cwd: options.cwd
5390
5393
  });
5391
5394
  } catch {
5392
- process4.exitCode = 1;
5395
+ process5.exitCode = 1;
5393
5396
  }
5394
5397
  });
5395
5398
  cli.command("deploy-secrets <directory>", cliM("cli_help_deploySecrets")).option("--import <file>", cliM("cli_help_secretImport")).option("--apply", cliM("cli_help_secretApply")).option("--runtime <runtime>", cliM("cli_help_deployRuntime")).action(async (directory, options) => {
@@ -5397,7 +5400,7 @@ cli.command("deploy-secrets <directory>", cliM("cli_help_deploySecrets")).option
5397
5400
  await deploySecretsCommand(directory, options);
5398
5401
  } catch (error) {
5399
5402
  writeCliMessage(error instanceof Error ? error.message : String(error));
5400
- process4.exitCode = 1;
5403
+ process5.exitCode = 1;
5401
5404
  }
5402
5405
  });
5403
5406
  cli.command("feedback <action> <bundle>", cliM("cli_help_feedback")).option("--page <number>", cliM("cli_feedback_page")).option("--page-size <number>", cliM("cli_feedback_pageSize")).option("--kind <kind>", cliM("cli_feedback_kind")).option("--request-id <id>", cliM("cli_feedback_requestId")).option("--error-id <id>", cliM("cli_feedback_errorId")).option("--from <timestamp>", cliM("cli_feedback_from")).option("--to <timestamp>", cliM("cli_feedback_to")).option("--port <number>", cliM("cli_feedback_port")).action(async (action, bundle, options) => {
@@ -5416,7 +5419,7 @@ cli.command("feedback <action> <bundle>", cliM("cli_help_feedback")).option("--p
5416
5419
  });
5417
5420
  } catch (error) {
5418
5421
  writeCliMessage(error instanceof Error ? error.message : String(error));
5419
- process4.exitCode = 1;
5422
+ process5.exitCode = 1;
5420
5423
  }
5421
5424
  });
5422
5425
  cli.command("g:page <name>", cliM("cli_help_generatePage")).option("-w, --workspace <name>", cliM("cli_help_workspace")).action(async (name, options) => {