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

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 (59) hide show
  1. package/README.en.md +24 -10
  2. package/README.ja.md +24 -10
  3. package/README.md +24 -10
  4. package/dist/{chunk-YHSX7AQX.js → chunk-7DNIMG2H.js} +89 -14
  5. package/dist/chunk-7DNIMG2H.js.map +1 -0
  6. package/dist/{chunk-YVNNMC4H.js → chunk-RT5OZJKM.js} +644 -11
  7. package/dist/chunk-RT5OZJKM.js.map +1 -0
  8. package/dist/{chunk-VUWM43KR.js → chunk-XI5SKOOR.js} +6673 -723
  9. package/dist/chunk-XI5SKOOR.js.map +1 -0
  10. package/dist/cli-config-templates-5KS3AE4H.js +5 -0
  11. package/dist/{cli-config-templates-LUJGDQNW.js.map → cli-config-templates-5KS3AE4H.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 +395 -387
  15. package/dist/index.js.map +1 -1
  16. package/dist/metafile-esm.json +1 -1
  17. package/dist/src-CX5IJGYM.js +5 -0
  18. package/dist/src-CX5IJGYM.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 +6 -6
  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 +8 -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/SKILL.md +6 -2
  38. package/templates/skills/hai-ai/acp.md +143 -0
  39. package/templates/skills/hai-ai/guide.md +2 -2
  40. package/templates/skills/hai-ai/resources.md +69 -0
  41. package/templates/skills/hai-app-create/development.md +9 -8
  42. package/templates/skills/hai-app-create/reference.md +1 -1
  43. package/templates/skills/hai-build/SKILL.md +5 -2
  44. package/templates/skills/hai-build/reference.md +16 -2
  45. package/templates/skills/hai-ci/SKILL.md +2 -0
  46. package/templates/skills/hai-core/SKILL.md +2 -2
  47. package/templates/skills/hai-core/reference.md +3 -3
  48. package/templates/skills/hai-core/request-access.md +1 -1
  49. package/templates/skills/hai-crypto/reference.md +1 -1
  50. package/templates/skills/hai-deploy/SKILL.md +8 -4
  51. package/templates/skills/hai-deploy/middleware.md +9 -9
  52. package/templates/skills/hai-iam/reference.md +2 -2
  53. package/templates/skills/hai-serv/reference.md +1 -1
  54. package/dist/chunk-VUWM43KR.js.map +0 -1
  55. package/dist/chunk-YHSX7AQX.js.map +0 -1
  56. package/dist/chunk-YVNNMC4H.js.map +0 -1
  57. package/dist/cli-config-templates-LUJGDQNW.js +0 -5
  58. package/dist/src-24PXSKE6.js +0 -5
  59. 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-RT5OZJKM.js';
3
+ import { cliM } from './chunk-7DNIMG2H.js';
4
+ import { normalizeProjectName, resolveImageReference, resolveDownloadProxy } from './chunk-XI5SKOOR.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());
@@ -129,11 +133,10 @@ Handlebars2.registerHelper("html", (value) => Handlebars2.escapeExpression(value
129
133
  Handlebars2.registerHelper("yaml", (key, value) => stringify({ [key]: value }).trim());
130
134
  Handlebars2.registerHelper("jsString", (value) => `'${JSON.stringify(value).slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'`);
131
135
  Handlebars2.registerHelper("moduleConfig", function(moduleKey, enabled) {
132
- const generated = generateConfigFile(moduleKey, this.moduleConfigs).trimEnd();
136
+ const generated = generateConfigFile(moduleKey, this.moduleConfigs, Boolean(enabled)).trimEnd();
133
137
  const content = moduleKey === "feedback" ? generated.replace("HAI_FEEDBACK_APPID:my-app", `HAI_FEEDBACK_APPID:${this.projectName}`) : generated;
134
138
  const configured = this.appType === "fullstack" && moduleKey === "iam" ? content.replace("HAI_IAM_SEEDDEFAULTDATA:true", "HAI_IAM_SEEDDEFAULTDATA:false").replace("variant: card", "variant: split") : content;
135
- return enabled ? configured : `# # \u672A\u542F\u7528\uFF1A\u5B89\u88C5\u5BF9\u5E94\u6A21\u5757\u5E76\u5728\u670D\u52A1\u7AEF runtime \u4E2D\u521D\u59CB\u5316\uFF0C\u518D\u53D6\u6D88\u6240\u9700\u914D\u7F6E\u7684\u6CE8\u91CA\u3002
136
- ${configured.split("\n").map((line) => line ? `# ${line}` : "").join("\n")}`;
139
+ return configured;
137
140
  });
138
141
  var TEMPLATE_SKIP_IF_EMPTY_MARKER = "@skipIfEmpty";
139
142
  var NORMALIZED_RENDER_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".js", ".svelte", ".json", ".css"]);
@@ -456,7 +459,11 @@ function buildApiWorkspaceCatalogPackages(featureMap) {
456
459
  function buildAppHaiCatalogPackages(options) {
457
460
  const packageNames = ["@h-ai/cli"];
458
461
  if (options.isSvelteOnlyApp) {
459
- packageNames.push("@h-ai/api-client", "@h-ai/api-contract", "@h-ai/capacitor", "@h-ai/ui");
462
+ packageNames.push("@h-ai/api-client", "@h-ai/capacitor", "@h-ai/core", "@h-ai/ui");
463
+ if (options.featureMap.iam)
464
+ packageNames.push("@h-ai/api-contract");
465
+ if (options.featureMap.deploy)
466
+ packageNames.push("@h-ai/deploy");
460
467
  if (options.featureMap.crypto)
461
468
  packageNames.push("@h-ai/crypto");
462
469
  return pickCatalogPackages(packageNames, HAI_CATALOG_PACKAGES);
@@ -828,6 +835,7 @@ async function generateFromTemplates(projectPath, context) {
828
835
  }
829
836
  await renderDynamicFiles(root, projectPath, context);
830
837
  await fse.copy(path2.join(root, "shared", "run-e2e.mjs"), path2.join(projectPath, "scripts", "run-e2e.mjs"));
838
+ await fse.copy(path2.join(root, "shared", "run-node.mjs"), path2.join(projectPath, "scripts", "run-node.mjs"));
831
839
  await fse.copy(path2.join(root, "shared", "e2e-env.ts"), path2.join(projectPath, "scripts", "e2e-env.ts"));
832
840
  await fse.copy(path2.join(root, "shared", "build-metadata.ts"), path2.join(projectPath, "scripts", "build-metadata.ts"));
833
841
  await fse.copy(path2.join(root, "shared", "public-base-path.mjs"), path2.join(projectPath, "scripts", "public-base-path.mjs"));
@@ -1223,7 +1231,14 @@ var FEATURES = {
1223
1231
  packages: ["@h-ai/capacitor"]
1224
1232
  }
1225
1233
  };
1226
- var FULL_FEATURES = ["iam", "db", "cache", "crypto", "ai", "storage", "audit", "reach", "feedback", "payment", "vecdb", "datapipe", "scheduler", "deploy", "api-client", "capacitor"];
1234
+ var TEMPLATE_FEATURES = {
1235
+ "admin": ["iam", "reach"],
1236
+ "website": [],
1237
+ "h5": ["iam", "reach"],
1238
+ "api": ["iam", "reach", "storage", "feedback"],
1239
+ "mobile-app": ["iam", "crypto", "api-client", "capacitor"],
1240
+ "fullstack": ["iam", "storage", "audit", "reach", "scheduler", "feedback"]
1241
+ };
1227
1242
  var PROJECT_NAME_REGEX = /^[a-z0-9-]+$/;
1228
1243
  async function createProject(options) {
1229
1244
  const spinner = ora();
@@ -1266,30 +1281,23 @@ async function createProject(options) {
1266
1281
  });
1267
1282
  await generateFromTemplates(projectPath, context);
1268
1283
  {
1269
- const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-LUJGDQNW.js');
1284
+ const { generateConfigFile: generateConfigFile2, generateEnvExample, generateProductionEnvironment: generateProductionEnvironment2 } = await import('./cli-config-templates-5KS3AE4H.js');
1270
1285
  const configs = resolvedOptions.moduleConfigs;
1271
1286
  const isApiWorkspace = resolvedOptions.appType === "api";
1272
1287
  const isFullstack = resolvedOptions.appType === "fullstack";
1273
1288
  const configDir = isApiWorkspace ? path2.join(projectPath, "apps", `${resolvedOptions.name}-serv`, "config") : isFullstack ? path2.join(projectPath, "packages", `${resolvedOptions.name}-serv`, "config") : path2.join(projectPath, "config");
1274
1289
  const envExamplePath = isApiWorkspace ? path2.join(projectPath, "apps", `${resolvedOptions.name}-serv`, ".env.example") : path2.join(projectPath, ".env.example");
1275
- for (const featureId of resolvedOptions.features) {
1276
- const configKey = getFeatureConfigKey(featureId);
1277
- if (!context.isSvelteOnlyApp && configKey && configKey !== "deploy") {
1278
- const configPath = path2.join(configDir, `_${configKey}.yml`);
1279
- if (isFullstack && await fse.pathExists(configPath))
1280
- continue;
1281
- const baseConfig = generateConfigFile2(configKey, configs);
1282
- const renderedConfig = baseConfig;
1283
- await writeFile(configPath, configKey === "feedback" ? renderedConfig.replace("HAI_FEEDBACK_APPID:my-app", `HAI_FEEDBACK_APPID:${resolvedOptions.name}`) : renderedConfig);
1284
- }
1285
- }
1286
- if (!isApiWorkspace && !isFullstack) {
1287
- await writeFile(
1288
- path2.join(configDir, "_core.yml"),
1289
- generateConfigFile2("core", configs)
1290
- );
1291
- if (!context.isSvelteOnlyApp)
1292
- await writeFile(path2.join(configDir, "_kit.yml"), generateConfigFile2("kit", configs));
1290
+ const enabledConfigs = /* @__PURE__ */ new Set(["core", ...context.isSvelteOnlyApp ? [] : resolvedOptions.features.map(getFeatureConfigKey).filter(Boolean)]);
1291
+ if (isApiWorkspace || isFullstack)
1292
+ enabledConfigs.add("serv");
1293
+ else if (!context.isSvelteOnlyApp)
1294
+ enabledConfigs.add("kit");
1295
+ for (const configKey of ["core", "serv", "kit", "db", "cache", "iam", "storage", "audit", "reach", "ai", "vecdb", "payment", "scheduler", "feedback"]) {
1296
+ const configPath = path2.join(configDir, `_${configKey}.yml`);
1297
+ if ((isApiWorkspace || isFullstack) && await fse.pathExists(configPath))
1298
+ continue;
1299
+ const content = generateConfigFile2(configKey, configs, enabledConfigs.has(configKey));
1300
+ await writeFile(configPath, configKey === "feedback" ? content.replace("HAI_FEEDBACK_APPID:my-app", `HAI_FEEDBACK_APPID:${resolvedOptions.name}`) : content);
1293
1301
  }
1294
1302
  const configContents = {};
1295
1303
  for (const file of await fse.readdir(configDir)) {
@@ -1297,7 +1305,7 @@ async function createProject(options) {
1297
1305
  configContents[file.slice(1, -4)] = await fse.readFile(path2.join(configDir, file), "utf8");
1298
1306
  }
1299
1307
  const envFeatures = context.isSvelteOnlyApp ? [] : isFullstack ? [.../* @__PURE__ */ new Set([...resolvedOptions.features, "reach"])] : resolvedOptions.features;
1300
- const envContents = generateEnvExample(envFeatures, configs, isFullstack, configContents);
1308
+ const envContents = generateEnvExample(envFeatures, configs, true, configContents);
1301
1309
  let envExample = isApiWorkspace ? envContents : isFullstack ? `${envContents}
1302
1310
  # First-start administrator password; must satisfy the IAM password policy.
1303
1311
  HAI_ADMIN_DEFAULT_PASSWORD=
@@ -1333,13 +1341,14 @@ ${runtimeExample.split("\n").map((line) => line && !line.startsWith("#") ? `# ${
1333
1341
  ${sharedVariables.join("\n")}
1334
1342
  `);
1335
1343
  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
1344
+ 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";
1345
+ 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
1346
  HAI_CORE_ENV=${environment}
1338
1347
  HAI_CORE_LOGGING_LEVEL=${mode === "test" ? "warn" : "debug"}
1339
1348
  `;
1340
1349
  if (isFullstack) {
1341
1350
  for (const name of ["HAI_ADMIN_DEFAULT_PASSWORD", "HAI_AUDIT_ADMIN_PASSWORD", "HAI_CONFIG_ADMIN_PASSWORD", "HAI_BUSINESS_OPERATOR_PASSWORD"])
1342
- values += `
1351
+ privateValues += `
1343
1352
  ${name}=Aa1!${randomBytes(24).toString("base64url")}
1344
1353
  `;
1345
1354
  }
@@ -1348,15 +1357,16 @@ ${name}=Aa1!${randomBytes(24).toString("base64url")}
1348
1357
  key.generateKeys();
1349
1358
  const id = `${mode}-${randomBytes(6).toString("hex")}`;
1350
1359
  const ring = { activeKeyId: id, keys: { [id]: { publicKey: key.getPublicKey("hex"), privateKey: key.getPrivateKey("hex").padStart(64, "0") } } };
1351
- values += `
1360
+ privateValues += `
1352
1361
  HAI_CRYPTO_TRANSPORT_SERVERKEYRING='${JSON.stringify(ring)}'
1353
1362
  `;
1354
1363
  }
1355
1364
  await writeFile(path2.join(envDirectory, `.env.${mode}`), values);
1365
+ await fse.writeFile(path2.join(envDirectory, `.env.${mode}.local`), privateValues, { mode: 384 });
1356
1366
  }
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 });
1367
+ 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
1368
  }
1359
- const { generateSkillFiles } = await import('./cli-skill-templates-COTP767H.js');
1369
+ const { generateSkillFiles } = await import('./cli-skill-templates-S2YH642Z.js');
1360
1370
  await generateSkillFiles(
1361
1371
  projectPath,
1362
1372
  resolvedOptions.features,
@@ -1403,7 +1413,7 @@ async function resolveOptions(options) {
1403
1413
  writeCliMessage("");
1404
1414
  const onCancel = () => {
1405
1415
  writeCliMessage(chalk.red(cliM("cli_cancelledLine")));
1406
- process4.exit(1);
1416
+ process5.exit(1);
1407
1417
  };
1408
1418
  let projectName = options.name;
1409
1419
  if (!projectName && useDefaults) {
@@ -1445,7 +1455,7 @@ ${formatPromptDescription(cliM("cli_selectHint"))}`,
1445
1455
  }
1446
1456
  const selectedAppType = resolvedAppType || "admin";
1447
1457
  const selectedFrontends = selectedAppType === "fullstack" ? await resolveFrontendTargets(options.frontends, useDefaults, onCancel) : [];
1448
- let selectedFeatures = selectedAppType === "mobile-app" ? ["iam", "crypto", "api-client", "capacitor"] : resolveFeatureDependencies(FULL_FEATURES);
1458
+ let selectedFeatures = selectedAppType === "mobile-app" ? [...TEMPLATE_FEATURES[selectedAppType]] : resolveFeatureDependencies(TEMPLATE_FEATURES[selectedAppType]);
1449
1459
  selectedFeatures = selectedFeatures.filter((feature) => feature !== "deploy");
1450
1460
  if (options.deploy !== false)
1451
1461
  selectedFeatures.push("deploy");
@@ -1607,7 +1617,7 @@ async function promptModuleConfigs(features, projectName) {
1607
1617
  const configs = {};
1608
1618
  const onCancel = () => {
1609
1619
  writeCliMessage(chalk.red(cliM("cli_cancelledLine")));
1610
- process4.exit(1);
1620
+ process5.exit(1);
1611
1621
  };
1612
1622
  writeCliMessage("");
1613
1623
  writeCliMessage(chalk.bold(cliM("cli_createModuleConfig")));
@@ -1848,9 +1858,11 @@ function generateAiDevelopmentReadme() {
1848
1858
  function generateEnvironmentReadme() {
1849
1859
  return `### \u5206\u73AF\u5883\u914D\u7F6E
1850
1860
 
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
1861
+ \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
1862
+
1863
+ \u4F9D\u8D56\u53EA\u5305\u542B\u6A21\u677F\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u5757\uFF1Bconfig \u4E0E \`.env.example\` \u4FDD\u7559\u6240\u6709\u6A21\u5757\u7684\u5B8C\u6574\u53C2\u8003\uFF0C\u672A\u542F\u7528\u9879\u4EE5\u6CE8\u91CA\u63D0\u4F9B\uFF0C\u4E0D\u8FDB\u5165\u516C\u5171\u73AF\u5883\u9ED8\u8BA4\u503C\u3002\u65B0\u589E\u529F\u80FD\u65F6\u6DFB\u52A0\u4F9D\u8D56\u3001\u542F\u7528\u6240\u9700\u914D\u7F6E\uFF0C\u518D\u63A5\u5165\u521D\u59CB\u5316\u3001\u53CD\u5E8F\u5173\u95ED\u548C\u4E1A\u52A1\u4EE3\u7801\uFF1B\u5B89\u88C5\u4F9D\u8D56\u4E0D\u4F1A\u81EA\u52A8\u63A5\u5165\u6A21\u5757\u3002\u7EAF\u5BA2\u6237\u7AEF\u7684\u670D\u52A1\u7AEF\u914D\u7F6E\u4EC5\u4F9B\u540E\u7EED\u540E\u7AEF\u96C6\u6210\u53C2\u8003\uFF0C\u79D8\u5BC6\u4E0D\u5F97\u6CE8\u5165\u6D4F\u89C8\u5668\u3002
1852
1864
 
1853
- \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
1865
+ \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\u3002\u540E\u7EED\u901A\u8FC7 hai add ai \u8865\u9F50 db+vecdb \u4F9D\u8D56\u65F6\uFF0C\u8FD8\u987B\u5728\u5E94\u7528\u5165\u53E3\u63A5\u5165\u521D\u59CB\u5316\u548C\u53CD\u5E8F\u5173\u95ED\uFF0C\u4E0D\u80FD\u4EC5\u5B89\u88C5\u4F9D\u8D56\u3002
1854
1866
 
1855
1867
  | \u7528\u9014 | \u547D\u4EE4 |
1856
1868
  | --- | --- |
@@ -1873,6 +1885,7 @@ function generateBuildDeploymentReadme(pm, appDir) {
1873
1885
 
1874
1886
  \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
1887
  \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
1888
+ \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
1889
  \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
1890
  \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
1891
 
@@ -1896,7 +1909,7 @@ ${pm} exec hai deploy ${appDir} --env prod --services web
1896
1909
  \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
1910
  \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
1911
  \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
1912
+ 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
1913
  \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
1914
  \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
1915
  `;
@@ -1905,7 +1918,7 @@ function generateReadme(name, appTypeLabel, pm, appType) {
1905
1918
  const install = getInstallCommand(pm);
1906
1919
  const staticWebsite = appType === "website" ? `### \u9759\u6001\u7F51\u7AD9\u4E0E GitHub Pages
1907
1920
 
1908
- \u9ED8\u8BA4\u4F7F\u7528Node SSR\uFF0C\u5305\u542B\u5B8C\u6574\u6A21\u5757\u548C\u793A\u4F8B\u3002\u9759\u6001\u53D1\u5E03\u524D\u5148\u79FB\u9664\u670D\u52A1\u7AEF\u4E1A\u52A1\u4F9D\u8D56\u3001\u521D\u59CB\u5316\u53CAAPI/actions\uFF0C\u4FDD\u7559Core/UI\uFF1B\u5DF2\u6709\u52A8\u6001\u6A21\u5757\u4E0D\u80FD\u4EC5\u6253\u5F00\u5F00\u5173\u9759\u6001\u53D1\u5E03\u3002
1921
+ \u9ED8\u8BA4\u4F7F\u7528Node SSR\uFF0C\u53EA\u5B89\u88C5\u5C55\u793A\u529F\u80FD\u6240\u9700\u7684Core/Kit/UI\uFF0C\u53EF\u76F4\u63A5\u5207\u6362\u9759\u6001\u53D1\u5E03\uFF1B\u5B8C\u6574\u6CE8\u91CA\u914D\u7F6E\u4E0D\u542F\u7528\u6A21\u5757\u3002\u540E\u7EED\u65B0\u589E\u7684\u670D\u52A1\u7AEF\u4E1A\u52A1\u4F9D\u8D56\u3001\u521D\u59CB\u5316\u53CAAPI/actions\u987B\u5148\u79FB\u9664\uFF0C\u5DF2\u6709\u52A8\u6001\u6A21\u5757\u4E0D\u80FD\u4EC5\u6253\u5F00\u5F00\u5173\u9759\u6001\u53D1\u5E03\u3002
1909
1922
 
1910
1923
  \`HAI_BUILD_STATIC=1\`\u663E\u5F0F\u9884\u6E32\u67D3\uFF0C\`PUBLIC_BASE_PATH=/${name}\`\u6307\u5B9A\u9879\u76EE\u7AD9\u76EE\u5F55\uFF08\u65E0\u672B\u5C3E/\uFF0C\u6839\u7AD9\u7559\u7A7A\uFF09\uFF1B\u5728PowerShell\u8BBE\u7F6E\u672C\u6B21\u6784\u5EFA\u73AF\u5883\uFF1A
1911
1924
 
@@ -2079,7 +2092,7 @@ Web/App/Desktop \u5171\u4EAB AppShell \u7684 header/footer\u3002\u65B0\u589E\u98
2079
2092
 
2080
2093
  ${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
2094
 
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
2095
+ 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
2096
 
2084
2097
  \`_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
2098
 
@@ -2087,7 +2100,7 @@ CLI \u5728\u6839\u76EE\u5F55 .env.dev\u3001.env.test\u3001.env.prod \u4E2D\u4E3A
2087
2100
 
2088
2101
  \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
2102
 
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
2103
+ \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
2104
 
2092
2105
  \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
2106
 
@@ -2095,7 +2108,7 @@ CLI \u5728\u6839\u76EE\u5F55 .env.dev\u3001.env.test\u3001.env.prod \u4E2D\u4E3A
2095
2108
 
2096
2109
  ### \u914D\u7F6E\u6269\u5C55
2097
2110
 
2098
- \u670D\u52A1\u7AEF \`packages/${name}-serv/config/\` \u59CB\u7EC8\u4FDD\u7559 core\u3001serv\u3001db\u3001cache\u3001iam\u3001storage\u3001audit\u3001reach\u3001ai\u3001vecdb\u3001payment\u3001scheduler\u3001feedback \u5171 13 \u4EFD YAML\uFF1B\u6784\u5EFA\u90E8\u7F72\u914D\u7F6E\u7EDF\u4E00\u4F4D\u4E8E\u9879\u76EE\u6839 hai.json\u3002\u5DF2\u7528\u6A21\u5757\u4FDD\u7559\u5DE5\u4F5C\u9ED8\u8BA4\u503C\uFF0C\u672A\u542F\u7528\u6A21\u5757\u6574\u4EFD\u6CE8\u91CA\uFF0C\u53EF\u9009\u5B57\u6BB5\u548C\u66FF\u4EE3 Provider \u6309\u5757\u6CE8\u91CA\uFF1B\u6839 \`.env.example\` \u4E5F\u4FDD\u7559\u672A\u542F\u7528\u6A21\u5757\u7684\u51ED\u636E\u793A\u4F8B\u3002Reach \u9ED8\u8BA4\u4E3A\u5F00\u53D1\u63A7\u5236\u53F0\u901A\u77E5\uFF0Cfeedback \u4F7F\u7528 email \u522B\u540D\uFF1B\u751F\u4EA7\u6309 \`_reach.yml\` \u6CE8\u91CA\u66FF\u6362\u4E3A SMTP\u3001\u77ED\u4FE1\u6216 API Provider\uFF0C\u5E76\u4FDD\u6301\u540D\u79F0\u552F\u4E00\u3002
2111
+ \u670D\u52A1\u7AEF \`packages/${name}-serv/config/\` \u59CB\u7EC8\u4FDD\u7559 core\u3001serv\u3001kit\u3001db\u3001cache\u3001iam\u3001storage\u3001audit\u3001reach\u3001ai\u3001vecdb\u3001payment\u3001scheduler\u3001feedback \u5171 14 \u4EFD YAML\uFF1B\u6784\u5EFA\u90E8\u7F72\u914D\u7F6E\u7EDF\u4E00\u4F4D\u4E8E\u9879\u76EE\u6839 hai.json\u3002\u53EA\u5B89\u88C5\u7BA1\u7406\u540E\u53F0\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u5757\uFF1B\u5DF2\u7528\u6A21\u5757\u4FDD\u7559\u5DE5\u4F5C\u9ED8\u8BA4\u503C\uFF0C\u672A\u542F\u7528\u6A21\u5757\u6574\u4EFD\u6CE8\u91CA\uFF0C\u53EF\u9009\u5B57\u6BB5\u548C\u66FF\u4EE3 Provider \u6309\u5757\u6CE8\u91CA\uFF1B\u6839 \`.env.example\` \u4E5F\u4FDD\u7559\u672A\u542F\u7528\u6A21\u5757\u7684\u51ED\u636E\u793A\u4F8B\u3002\u65B0\u589E\u529F\u80FD\u65F6\u5B89\u88C5\u4F9D\u8D56\u5E76\u63A5\u5165\u521D\u59CB\u5316\u3001\u5173\u95ED\u53CA\u4E1A\u52A1\u4EE3\u7801\u3002Reach \u9ED8\u8BA4\u4E3A\u5F00\u53D1\u63A7\u5236\u53F0\u901A\u77E5\uFF0Cfeedback \u4F7F\u7528 email \u522B\u540D\uFF1B\u751F\u4EA7\u6309 \`_reach.yml\` \u6CE8\u91CA\u66FF\u6362\u4E3A SMTP\u3001\u77ED\u4FE1\u6216 API Provider\uFF0C\u5E76\u4FDD\u6301\u540D\u79F0\u552F\u4E00\u3002
2099
2112
 
2100
2113
  \u81EA\u52A8\u751F\u6210\u7684 \`.env\` \u5305\u542B\u5DF2\u542F\u7528\u6A21\u5757\u7684\u73AF\u5883\u53C2\u6570\uFF1BYAML \u663E\u5F0F\u5F15\u7528\u76F8\u540C\u53D8\u91CF\u5E76\u4FDD\u7559\u672C\u5730\u56DE\u9000\u3002\u7F16\u8F91\u73AF\u5883\u53C2\u6570\u4F18\u5148\u4FEE\u6539\u5BF9\u5E94 \`.env\`\uFF1B\u672A\u542F\u7528\u6A21\u5757\u4ECD\u4FDD\u7559\u6CE8\u91CA\u3002
2101
2114
 
@@ -2332,7 +2345,7 @@ async function addModule(options) {
2332
2345
  writeCliMessage(cliM("cli_addAvailableModules", { params: { modules: Object.keys(MODULE_MAP).join(", ") } }));
2333
2346
  return;
2334
2347
  }
2335
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-COTP767H.js');
2348
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-S2YH642Z.js');
2336
2349
  if (moduleDef.packages.every((p) => project.haiPackages.includes(p))) {
2337
2350
  if (moduleName === "deploy")
2338
2351
  await ensureDeployConfig(cwd);
@@ -2454,7 +2467,7 @@ ${production.join("\n")}`);
2454
2467
  }
2455
2468
  }
2456
2469
  async function buildMobileApp(root, directories, platform, run2) {
2457
- if (!directories.length || platform === "ios" && process4.platform !== "darwin")
2470
+ if (!directories.length || platform === "ios" && process5.platform !== "darwin")
2458
2471
  throw new Error(cliM("cli_build_app_platform"));
2459
2472
  const outputs = [];
2460
2473
  for (const item of directories) {
@@ -2492,31 +2505,240 @@ async function buildMobileApp(root, directories, platform, run2) {
2492
2505
  }
2493
2506
  return outputs;
2494
2507
  }
2508
+ function object(value) {
2509
+ if (value === void 0)
2510
+ return {};
2511
+ if (!value || typeof value !== "object" || Array.isArray(value))
2512
+ throw new Error(cliM("cli_download_invalid"));
2513
+ return value;
2514
+ }
2515
+ function registryUrl(value) {
2516
+ try {
2517
+ if (typeof value !== "string")
2518
+ throw new Error("Missing registry");
2519
+ const url = new URL(value);
2520
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash)
2521
+ throw new Error("Invalid registry");
2522
+ return url.toString().replace(/\/$/, "");
2523
+ } catch {
2524
+ throw new Error(cliM("cli_download_invalid"));
2525
+ }
2526
+ }
2527
+ async function resolveBuildDownloads(directory, options) {
2528
+ const path10 = projectConfigPath(directory);
2529
+ const project = readProjectConfig(path10);
2530
+ const build = object(project.build);
2531
+ const sources = object(build.sources);
2532
+ const savedImage = object(sources.image);
2533
+ const savedNpm = object(sources.npm);
2534
+ const savedProxy = object(build.proxy);
2535
+ const interactive = !!process5.stdin.isTTY && !options.yes;
2536
+ const choose = async (message, values) => {
2537
+ const answer = await prompts({ type: "select", name: "value", message, choices: values.map((value) => ({ title: cliM(`cli_download_${value}`), value })) });
2538
+ if (typeof answer.value !== "string")
2539
+ throw new Error(cliM("cli_deploy_cancelled"));
2540
+ return answer.value;
2541
+ };
2542
+ const mirrorEntries = options.imageMirrors?.split(",").map((entry) => {
2543
+ const separator = entry.indexOf("=");
2544
+ if (separator <= 0 || separator === entry.length - 1)
2545
+ throw new Error(cliM("cli_download_invalid"));
2546
+ return [entry.slice(0, separator).trim(), entry.slice(separator + 1).trim()];
2547
+ });
2548
+ if (mirrorEntries && new Set(mirrorEntries.map(([key]) => key)).size !== mirrorEntries.length)
2549
+ throw new Error(cliM("cli_download_invalid"));
2550
+ let imageType = options.imageSource ?? (mirrorEntries ? "custom" : savedImage.type);
2551
+ let npmType = options.npmSource ?? (options.npmRegistry ? "custom" : savedNpm.type);
2552
+ const saveImage = imageType === void 0 && interactive;
2553
+ const saveNpm = npmType === void 0 && interactive;
2554
+ if (saveImage)
2555
+ imageType = await choose(cliM("cli_download_choose_image"), ["official", "cn", "custom"]);
2556
+ if (saveNpm)
2557
+ npmType = await choose(cliM("cli_download_choose_npm"), ["official", "cn", "custom"]);
2558
+ imageType ??= "official";
2559
+ npmType ??= "official";
2560
+ if (typeof imageType !== "string" || !["official", "cn", "custom"].includes(imageType) || typeof npmType !== "string" || !["official", "cn", "custom"].includes(npmType))
2561
+ throw new Error(cliM("cli_download_invalid"));
2562
+ let registries = mirrorEntries ? Object.fromEntries(mirrorEntries) : savedImage.registries;
2563
+ if (imageType === "custom" && !registries && interactive) {
2564
+ const answer = await prompts({ type: "text", name: "value", message: cliM("cli_download_image_mirrors") });
2565
+ if (typeof answer.value !== "string" || !answer.value.trim())
2566
+ throw new Error(cliM("cli_deploy_cancelled"));
2567
+ registries = Object.fromEntries(answer.value.split(",").map((entry) => {
2568
+ const index = entry.indexOf("=");
2569
+ if (index <= 0)
2570
+ throw new Error(cliM("cli_download_invalid"));
2571
+ return [entry.slice(0, index).trim(), entry.slice(index + 1).trim()];
2572
+ }));
2573
+ }
2574
+ const mappings = registries === void 0 ? void 0 : object(registries);
2575
+ if (mappings && Object.entries(mappings).some(([key, value]) => !/^[\w.-]+(?::\d+)?$/.test(key) || typeof value !== "string"))
2576
+ throw new Error(cliM("cli_download_invalid"));
2577
+ const imageSource = { type: imageType, ...imageType === "custom" ? { registries: mappings } : {} };
2578
+ if (mirrorEntries && imageType !== "custom")
2579
+ throw new Error(cliM("cli_download_invalid"));
2580
+ if (imageType === "custom") {
2581
+ if (!mappings || !Object.keys(mappings).length)
2582
+ throw new Error(cliM("cli_download_invalid"));
2583
+ for (const key of Object.keys(mappings)) resolveImageReference(`${key}/hai-source-check:1`, imageSource);
2584
+ }
2585
+ let customRegistry = options.npmRegistry ?? savedNpm.registry;
2586
+ if (npmType === "custom" && !customRegistry && interactive) {
2587
+ const answer = await prompts({ type: "text", name: "value", message: cliM("cli_download_npm_registry") });
2588
+ if (typeof answer.value !== "string" || !answer.value.trim())
2589
+ throw new Error(cliM("cli_deploy_cancelled"));
2590
+ customRegistry = answer.value;
2591
+ }
2592
+ if (options.npmRegistry && npmType !== "custom")
2593
+ throw new Error(cliM("cli_download_invalid"));
2594
+ const npmRegistry = npmType === "cn" ? "https://registry.npmmirror.com" : npmType === "official" ? "https://registry.npmjs.org" : registryUrl(customRegistry);
2595
+ const npmSource = { type: npmType, ...npmType === "custom" ? { registry: npmRegistry } : {} };
2596
+ let proxyMode = options.proxyMode ?? (options.proxyUrl ? "custom" : savedProxy.mode);
2597
+ const saveProxy = proxyMode === void 0 && interactive && options.httpProxy !== false;
2598
+ if (saveProxy)
2599
+ proxyMode = await choose(cliM("cli_download_choose_proxy"), ["inherit", "system", "custom", "none"]);
2600
+ proxyMode ??= "inherit";
2601
+ if (options.proxyUrl !== void 0 && proxyMode !== "custom")
2602
+ throw new Error(cliM("cli_download_invalid"));
2603
+ if (typeof proxyMode !== "string" || !["inherit", "system", "custom", "none"].includes(proxyMode))
2604
+ throw new Error(cliM("cli_download_invalid"));
2605
+ if (options.httpProxy === false && (options.proxyUrl !== void 0 || options.proxyMode !== void 0 && options.proxyMode !== "none"))
2606
+ throw new Error(cliM("cli_download_invalid"));
2607
+ let proxyUrl = options.proxyUrl ?? savedProxy.url;
2608
+ if (proxyMode === "custom" && !proxyUrl && interactive) {
2609
+ const answer = await prompts({ type: "text", name: "value", message: cliM("cli_download_proxy_url") });
2610
+ if (typeof answer.value !== "string" || !answer.value.trim())
2611
+ throw new Error(cliM("cli_deploy_cancelled"));
2612
+ proxyUrl = answer.value;
2613
+ }
2614
+ const bypass = options.proxyBypass ?? savedProxy.bypass;
2615
+ if (proxyUrl !== void 0 && typeof proxyUrl !== "string" || bypass !== void 0 && typeof bypass !== "string")
2616
+ throw new Error(cliM("cli_download_invalid"));
2617
+ const proxy = { mode: options.httpProxy === false ? "none" : proxyMode, ...proxyMode === "custom" && options.httpProxy !== false ? { url: proxyUrl } : {}, ...bypass !== void 0 ? { bypass } : {} };
2618
+ resolveDownloadProxy(proxy);
2619
+ if (saveImage || saveNpm || saveProxy) {
2620
+ project.build = { ...build, sources: { ...sources, ...saveImage ? { image: imageSource } : {}, ...saveNpm ? { npm: npmSource } : {} }, ...saveProxy ? { proxy } : {} };
2621
+ writeFileSync(path10, `${JSON.stringify(project, null, 2)}
2622
+ `);
2623
+ }
2624
+ return { imageSource, npmRegistry, npmSource, proxy };
2625
+ }
2626
+ function buildProxyArguments(proxy, runtime, network) {
2627
+ const environment = resolveDownloadProxy(proxy);
2628
+ for (const key of ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "ALL_PROXY", "all_proxy"]) {
2629
+ if (!environment[key])
2630
+ continue;
2631
+ const url = new URL(environment[key]);
2632
+ if (network !== "host" && (runtime === "docker" || network !== void 0) && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)) {
2633
+ url.hostname = runtime === "podman" ? "host.containers.internal" : "host.docker.internal";
2634
+ environment[key] = url.toString().replace(/\/$/, "");
2635
+ }
2636
+ }
2637
+ return Object.entries(environment).flatMap(([key, value]) => ["--build-arg", `${key}=${value}`]);
2638
+ }
2639
+ async function preflightDownloadProxy(runtime, proxy) {
2640
+ const environment = resolveDownloadProxy(proxy);
2641
+ for (const value of new Set([environment.HTTP_PROXY, environment.HTTPS_PROXY].filter(Boolean))) {
2642
+ const url = new URL(value);
2643
+ await new Promise((resolve7, reject) => {
2644
+ const socket = connect({ host: url.hostname.replace(/^\[|\]$/g, ""), port: Number(url.port || (url.protocol === "https:" ? 443 : 80)) });
2645
+ socket.setTimeout(3e3);
2646
+ socket.once("connect", () => {
2647
+ socket.destroy();
2648
+ resolve7();
2649
+ });
2650
+ const fail = () => {
2651
+ socket.destroy();
2652
+ reject(new Error(cliM("cli_download_proxy_unreachable")));
2653
+ };
2654
+ socket.once("error", fail);
2655
+ socket.once("timeout", fail);
2656
+ });
2657
+ }
2658
+ if (!environment.HTTP_PROXY && !environment.HTTPS_PROXY)
2659
+ return;
2660
+ for (const candidate of runtime === "auto" ? ["podman", "docker"] : [runtime]) {
2661
+ try {
2662
+ const data = JSON.parse(execFileSync(candidate, ["info", "--format", "{{json .}}"], { encoding: "utf8", windowsHide: true, timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }));
2663
+ 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 } }));
2664
+ return;
2665
+ } catch {
2666
+ }
2667
+ }
2668
+ writeCliMessage(cliM("cli_download_engine_proxy_hint", { params: { runtime } }));
2669
+ }
2670
+ function validateDownloadDockerfile(file, downloads) {
2671
+ const content = readFileSync(file, "utf8");
2672
+ if (downloads.imageSource.type !== "official" && !/^ARG HAI_NODE_IMAGE=/m.test(content) || downloads.npmSource.type !== "official" && !/^ARG HAI_NPM_REGISTRY[=\s]/m.test(content)) {
2673
+ throw new Error(cliM("cli_download_template_required", { params: { path: file } }));
2674
+ }
2675
+ }
2676
+ function createWindowsBuildOutput() {
2677
+ const decoder = new StringDecoder("utf8");
2678
+ let previousCarriageReturn = false;
2679
+ const normalize = (value) => {
2680
+ const result = value.replace(/\n/g, (newline, offset) => (offset === 0 ? previousCarriageReturn : value[offset - 1] === "\r") ? newline : "\r\n");
2681
+ if (value.length)
2682
+ previousCarriageReturn = value.endsWith("\r");
2683
+ return result;
2684
+ };
2685
+ return new Transform({
2686
+ transform(chunk, _encoding, callback) {
2687
+ callback(null, normalize(decoder.write(chunk)));
2688
+ },
2689
+ flush(callback) {
2690
+ callback(null, normalize(decoder.end()));
2691
+ }
2692
+ });
2693
+ }
2694
+ async function forwardWindowsBuildOutput(source, destination) {
2695
+ const forwarding = new Writable({
2696
+ write(chunk, encoding, callback) {
2697
+ destination.write(chunk, encoding, callback);
2698
+ }
2699
+ });
2700
+ await pipeline(source, createWindowsBuildOutput(), forwarding);
2701
+ }
2495
2702
 
2496
2703
  // src/commands/cli-build.ts
2497
2704
  async function run(command, args, cwd, quiet = false, identity, compileEnvironment) {
2498
2705
  await new Promise((resolve7, reject) => {
2499
- const env = Object.fromEntries(Object.entries(process4.env).filter(([key]) => !/^(?:HAI_|PUBLIC_|VITE_)/.test(key)));
2706
+ const env = Object.fromEntries(Object.entries(process5.env).filter(([key]) => !/^(?:HAI_|PUBLIC_|VITE_)/.test(key)));
2500
2707
  Object.assign(env, compileEnvironment);
2501
2708
  if (identity) {
2502
2709
  env.HAI_BUILD_ID = identity.buildId;
2503
2710
  env.HAI_BUILD_COMMIT = identity.commit;
2504
2711
  }
2505
2712
  env.NODE_ENV = "production";
2713
+ if (process5.platform === "win32") {
2714
+ env.npm_config_reporter = "append-only";
2715
+ env.pnpm_config_reporter = "append-only";
2716
+ }
2506
2717
  delete env.CAPACITOR_SERVER_URL;
2507
2718
  if (env.CI === "1" || env.CI === "0")
2508
2719
  env.CI = env.CI === "1" ? "true" : "false";
2509
- const child = spawn(command, args, { cwd, env, windowsHide: true, stdio: quiet ? "ignore" : "inherit", shell: false });
2720
+ const windowsOutput = !quiet && process5.platform === "win32";
2721
+ const child = spawn(command, args, { cwd, env, windowsHide: true, stdio: quiet ? "ignore" : windowsOutput ? ["inherit", "pipe", "pipe"] : "inherit", shell: false });
2722
+ let output = Promise.resolve();
2723
+ if (windowsOutput && child.stdout && child.stderr) {
2724
+ output = Promise.all([
2725
+ forwardWindowsBuildOutput(child.stdout, process5.stdout),
2726
+ forwardWindowsBuildOutput(child.stderr, process5.stderr)
2727
+ ]).then(() => void 0);
2728
+ void output.catch(() => child.kill());
2729
+ }
2510
2730
  child.once("error", reject);
2511
- child.once("exit", (code) => code === 0 ? resolve7() : reject(new Error(cliM("cli_build_failed", { params: { command, code: String(code) } }))));
2731
+ child.once(windowsOutput && child.stdout && child.stderr ? "close" : "exit", (code) => {
2732
+ void output.then(() => code === 0 ? resolve7() : reject(new Error(cliM("cli_build_failed", { params: { command, code: String(code) } }))), reject);
2733
+ });
2512
2734
  });
2513
2735
  }
2514
2736
  async function pnpm(args, cwd, identity, compileEnvironment) {
2515
- const entry = process4.env.npm_execpath;
2737
+ const entry = process5.env.npm_execpath;
2516
2738
  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(";");
2739
+ return /\.[cm]?js$/i.test(entry) ? run(process5.execPath, [entry, ...args], cwd, false, identity, compileEnvironment) : run(entry, args, cwd, false, identity, compileEnvironment);
2740
+ if (process5.platform === "win32") {
2741
+ const directories = (process5.env.PATH ?? "").split(";");
2520
2742
  for (const directory of directories) {
2521
2743
  const executable = join(directory, "pnpm.exe");
2522
2744
  if (existsSync(executable))
@@ -2524,7 +2746,7 @@ async function pnpm(args, cwd, identity, compileEnvironment) {
2524
2746
  for (const relative4 of ["node_modules/pnpm/bin/pnpm.cjs", "pnpm.cjs"]) {
2525
2747
  const candidate = join(directory, relative4);
2526
2748
  if (existsSync(candidate))
2527
- return run(process4.execPath, [candidate, ...args], cwd, false, identity, compileEnvironment);
2749
+ return run(process5.execPath, [candidate, ...args], cwd, false, identity, compileEnvironment);
2528
2750
  }
2529
2751
  }
2530
2752
  throw new Error(cliM("cli_build_use_pnpm"));
@@ -2532,8 +2754,8 @@ async function pnpm(args, cwd, identity, compileEnvironment) {
2532
2754
  return run("pnpm", args, cwd, false, identity, compileEnvironment);
2533
2755
  }
2534
2756
  function staticCompileEnvironment(target) {
2535
- const flag = process4.env.HAI_BUILD_STATIC;
2536
- const base = process4.env.PUBLIC_BASE_PATH;
2757
+ const flag = process5.env.HAI_BUILD_STATIC;
2758
+ const base = process5.env.PUBLIC_BASE_PATH;
2537
2759
  if (flag !== void 0 && flag !== "" && flag !== "0" && flag !== "1")
2538
2760
  throw new Error(cliM("cli_build_static_flag"));
2539
2761
  if (base && (!/^\/[\w.-]+(?:\/[\w.-]+)*$/.test(base) || base.split("/").some((segment) => segment === "." || segment === "..")))
@@ -2560,7 +2782,7 @@ function sourceCommit(cwd) {
2560
2782
  }
2561
2783
  }
2562
2784
  async function buildCommand(options) {
2563
- const directory = resolve(options.cwd ?? process4.cwd());
2785
+ const directory = resolve(options.cwd ?? process5.cwd());
2564
2786
  const target = options.target ?? "compile";
2565
2787
  const concurrency = Number(options.concurrency ?? 2);
2566
2788
  if (![1, 2].includes(concurrency))
@@ -2569,7 +2791,7 @@ async function buildCommand(options) {
2569
2791
  if (!["compile", "image", "desktop", "app"].includes(target))
2570
2792
  throw new Error(cliM("cli_build_target_invalid"));
2571
2793
  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) {
2794
+ 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
2795
  throw new Error(cliM("cli_build_options_invalid"));
2574
2796
  }
2575
2797
  if (target === "compile") {
@@ -2592,9 +2814,9 @@ async function buildCommand(options) {
2592
2814
  }
2593
2815
  if (target === "desktop") {
2594
2816
  const platforms = { win32: "windows", linux: "linux", darwin: "macos" };
2595
- const host = platforms[process4.platform];
2817
+ const host = platforms[process5.platform];
2596
2818
  const platform = options.platform ?? host;
2597
- const arch = options.arch ?? process4.arch;
2819
+ const arch = options.arch ?? process5.arch;
2598
2820
  if (!platform || platform !== host || !["x64", "arm64"].includes(arch))
2599
2821
  throw new Error(cliM("cli_build_native_host"));
2600
2822
  if (!config.desktop?.length)
@@ -2624,6 +2846,8 @@ async function buildCommand(options) {
2624
2846
  if (!existsSync(resolve(cwd, image.dockerfile)))
2625
2847
  throw new Error(cliM("cli_build_dockerfile_missing", { params: { path: image.dockerfile } }));
2626
2848
  }
2849
+ const downloads = await resolveBuildDownloads(cwd, options);
2850
+ for (const image of images) validateDownloadDockerfile(resolve(cwd, image.dockerfile), downloads);
2627
2851
  const candidates = options.runtime && options.runtime !== "auto" ? [options.runtime] : ["podman", "docker"];
2628
2852
  if (candidates.some((runtime2) => !["podman", "docker"].includes(runtime2)))
2629
2853
  throw new Error(cliM("cli_build_runtime_missing"));
@@ -2638,6 +2862,7 @@ async function buildCommand(options) {
2638
2862
  }
2639
2863
  if (!runtime)
2640
2864
  throw new Error(cliM("cli_build_runtime_missing"));
2865
+ await preflightDownloadProxy(runtime, downloads.proxy);
2641
2866
  if (options.sqlitePrebuildMirror) {
2642
2867
  const url = new URL(options.sqlitePrebuildMirror);
2643
2868
  if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash)
@@ -2667,8 +2892,8 @@ async function buildCommand(options) {
2667
2892
  const context = resolve(cwd, image.context ?? ".");
2668
2893
  const imageRef = `localhost/hai-${projectName}-${image.name}:${buildId.toLowerCase().replaceAll("+", "_")}`;
2669
2894
  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;
2895
+ const testRun = process5.env.HAI_TEST_IMAGE_RUN;
2896
+ const testLedger = process5.env.HAI_TEST_IMAGE_LEDGER;
2672
2897
  if (testLedger && /^[a-f0-9-]{36}$/.test(testRun ?? "")) {
2673
2898
  const label = `org.h-ai.test.image-run=${testRun}`;
2674
2899
  args.push("--label", label);
@@ -2678,19 +2903,19 @@ async function buildCommand(options) {
2678
2903
  `);
2679
2904
  }
2680
2905
  args.push("--build-arg", `HAI_BUILD_ID=${buildId}`, "--build-arg", `HAI_BUILD_COMMIT=${commit}`);
2906
+ const dockerfile = readFileSync(resolve(cwd, image.dockerfile), "utf8");
2907
+ for (const match of dockerfile.matchAll(/^ARG (HAI_(?:NODE|NGINX)_IMAGE)=(\S+)\s*$/gm))
2908
+ args.push("--build-arg", `${match[1]}=${resolveImageReference(match[2], downloads.imageSource)}`);
2909
+ args.push("--build-arg", `HAI_NPM_REGISTRY=${downloads.npmRegistry}`, ...buildProxyArguments(downloads.proxy, runtime, options.network));
2910
+ if (runtime === "docker" && process5.platform === "linux" && options.network !== "host")
2911
+ args.push("--add-host", "host.docker.internal:host-gateway");
2681
2912
  if (options.network !== void 0)
2682
2913
  args.push("--network", options.network);
2683
2914
  const ignore = `${resolve(cwd, image.dockerfile)}.dockerignore`;
2684
2915
  if (runtime === "podman" && existsSync(ignore))
2685
2916
  args.push("--ignorefile", ignore);
2686
2917
  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
- }
2918
+ args.push("--format", "docker", "--jobs", "1", ...downloads.proxy.mode === "none" ? ["--http-proxy=false"] : []);
2694
2919
  }
2695
2920
  if (options.sqlitePrebuildMirror)
2696
2921
  args.push("--build-arg", `SQLITE_PREBUILD_MIRROR=${options.sqlitePrebuildMirror}`);
@@ -2772,7 +2997,7 @@ function prepareDependencyInputs(cwd) {
2772
2997
  async function deploySecretsCommand(directory, options) {
2773
2998
  if (!options.import || !["docker", "podman"].includes(options.runtime ?? "docker"))
2774
2999
  throw new Error(cliM("cli_deploy_secrets_options"));
2775
- const cwd = resolve(options.cwd ?? process4.cwd());
3000
+ const cwd = resolve(options.cwd ?? process5.cwd());
2776
3001
  const local = createRequire(resolve(cwd, "package.json"));
2777
3002
  let module;
2778
3003
  try {
@@ -2788,237 +3013,12 @@ ${JSON.stringify({ phase: context?.phase, state: context?.state })}`);
2788
3013
  }
2789
3014
  writeCliMessage(JSON.stringify(result.data, null, 2));
2790
3015
  }
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
3016
  async function ask(question) {
2958
3017
  const answer = await prompts({ ...question, name: "value" });
2959
3018
  if (answer.value === void 0)
2960
3019
  throw new Error(cliM("cli_deploy_cancelled"));
2961
3020
  return answer.value;
2962
3021
  }
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
3022
  async function configureDeployMiddleware(module, scan, environment, config, yes = false) {
3023
3023
  const modes = {};
3024
3024
  const services = {};
@@ -3030,7 +3030,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3030
3030
  if (!current)
3031
3031
  throw new Error(cliM("cli_deploy_cloud_unsupported", { params: { backend: requirement.backend } }));
3032
3032
  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);
3033
+ const canKeep = module.canKeepMiddleware(requirement, current, environment);
3034
3034
  const mode = yes ? canKeep ? "keep" : "default" : await ask({
3035
3035
  type: "select",
3036
3036
  name: "value",
@@ -3055,22 +3055,23 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3055
3055
  throw new Error(cliM("cli_deploy_invalid_value"));
3056
3056
  requirement.backend = backendName;
3057
3057
  const updates = {};
3058
- if (mode === "default" && backendName === "s3" && provider?.type === "docker-ssh") {
3058
+ let configuration;
3059
+ for (const option of mode === "default" && provider?.type === "docker-ssh" ? backend.containerOptions ?? [] : []) {
3059
3060
  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))
3061
+ const currentValue = String(container[option.field] ?? option.defaultValue);
3062
+ 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 })) }));
3063
+ if (!option.choices.some((item) => item.value === selected))
3063
3064
  throw new Error(cliM("cli_deploy_invalid_value"));
3064
- config.container = { ...container, s3Engine: engine };
3065
- updates.HAI_DEPLOY_CONTAINER_S3ENGINE = engine;
3065
+ config.container = { ...container, [option.field]: selected };
3066
+ updates[option.variable] = selected;
3066
3067
  const deploymentFile = projectConfigPath(scan.appDir ?? environment.envDir);
3067
3068
  const project = readProjectConfig(deploymentFile);
3068
3069
  project.deploy ??= {};
3069
3070
  if (project.deploy.container != null && (typeof project.deploy.container !== "object" || Array.isArray(project.deploy.container)))
3070
3071
  throw new Error(cliM("cli_deploy_config_node", { params: { path: deploymentFile, field: "deploy.container" } }));
3071
3072
  project.deploy.container ??= {};
3072
- if (!("s3Engine" in project.deploy.container)) {
3073
- project.deploy.container.s3Engine = `\${HAI_DEPLOY_CONTAINER_S3ENGINE:rustfs}`;
3073
+ if (!(option.field in project.deploy.container)) {
3074
+ project.deploy.container[option.field] = `\${${option.variable}:${option.defaultValue}}`;
3074
3075
  writeFileSync(deploymentFile, `${JSON.stringify(project, null, 2)}
3075
3076
  `);
3076
3077
  }
@@ -3081,7 +3082,7 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3081
3082
  const prior = existingServices[requirement.service];
3082
3083
  const priorConfig = prior && typeof prior === "object" ? prior : {};
3083
3084
  const cloud = { provisioner: backend.provisioner };
3084
- for (const name of CLOUD_FIELDS[backend.provisioner]) {
3085
+ for (const name of backend.cloudFields ?? []) {
3085
3086
  const key = `HAI_DEPLOY_SERVICES_${requirement.service.toUpperCase()}_${name.toUpperCase()}`;
3086
3087
  const initial = String(priorConfig[name] ?? environment.values[key] ?? "");
3087
3088
  const label = `${backend.label} \u2014 ${cliM(`cli_deploy_cloud_input_${name}`)}`;
@@ -3093,32 +3094,25 @@ async function configureDeployMiddleware(module, scan, environment, config, yes
3093
3094
  }
3094
3095
  services[requirement.service] = cloud;
3095
3096
  } else {
3096
- if (mode === "default" && ["aliyun-sms", "api"].includes(backendName))
3097
+ if (mode === "default" && backend.containerSupported === false)
3097
3098
  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 } : {};
3099
+ const connection = backendName === current.backend ? { ...requirement.config } : requirement.config.name === void 0 ? {} : { name: requirement.config.name };
3099
3100
  for (const field of backend.fields) {
3100
3101
  const previous2 = backendName === current.backend ? requirement.config[field.name] : void 0;
3101
3102
  const initial = previous2 === void 0 ? field.defaultValue ?? "" : typeof previous2 === "object" ? JSON.stringify(previous2) : String(previous2);
3102
3103
  const variable = `${requirement.envPrefix}_${field.name.toUpperCase()}`;
3103
3104
  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 {
3105
+ 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;
3106
+ const parsed2 = module.parseMiddlewareField({ value, field, backend: backendName, managed: mode === "default" });
3107
+ if (!parsed2.success)
3108
+ throw new Error(parsed2.error.message);
3109
+ if (parsed2.data === void 0)
3110
3110
  delete connection[field.name];
3111
- }
3111
+ else connection[field.name] = parsed2.data;
3112
3112
  }
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));
3113
+ configuration = connection;
3119
3114
  }
3120
- ensureConfigNode(scan.appDir ?? ".", requirement, backendName);
3121
- const saved = module.saveDeploymentEnvironment(environment, updates);
3115
+ const saved = module.saveMiddlewareEnvironment(environment, { appDir: scan.appDir ?? ".", requirement, backend: backendName, configuration, updates });
3122
3116
  if (!saved.success)
3123
3117
  throw new Error(saved.error.message);
3124
3118
  writeCliMessage(cliM("cli_deploy_saved", { params: { service: requirement.service } }));
@@ -3222,7 +3216,7 @@ async function configureDeployTarget(module, config, environment, nonInteractive
3222
3216
  const validated = module.ProviderConfigSchema.safeParse(provider);
3223
3217
  if (!validated.success)
3224
3218
  throw new Error(cliM("cli_deploy_invalid_value"));
3225
- const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: JSON.stringify(validated.data) }, { private: true });
3219
+ const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: validated.data }, { private: true });
3226
3220
  if (!saved.success)
3227
3221
  throw new Error(saved.error.message);
3228
3222
  config.provider = validated.data;
@@ -3273,7 +3267,7 @@ async function configureDeployHttps(module, config, environment, nonInteractive,
3273
3267
  const parsed2 = module.ProviderConfigSchema.safeParse({ ...provider, expose: { ...expose, ...https?.type === "letsencrypt" ? { hostPort: 80 } : {}, https } });
3274
3268
  if (!parsed2.success)
3275
3269
  throw new Error(cliM("cli_deploy_invalid_value"));
3276
- const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: JSON.stringify(parsed2.data) }, { private: true });
3270
+ const saved = module.saveDeploymentEnvironment(environment, { HAI_DEPLOY_PROVIDER: parsed2.data }, { private: true });
3277
3271
  if (!saved.success)
3278
3272
  throw new Error(saved.error.message);
3279
3273
  config.provider = parsed2.data;
@@ -3281,11 +3275,11 @@ async function configureDeployHttps(module, config, environment, nonInteractive,
3281
3275
  }
3282
3276
  async function deployCommand(options) {
3283
3277
  const spinner = ora();
3284
- const cwd = options.cwd ?? process4.cwd();
3278
+ const cwd = options.cwd ?? process5.cwd();
3285
3279
  let appDir = resolve(cwd, options.appDir ?? ".");
3286
3280
  let closeDeploy = null;
3287
3281
  let restoreProjectLogger;
3288
- const originalEnv = { ...process4.env };
3282
+ const originalEnv = { ...process5.env };
3289
3283
  const previousLogLevel = core.logger.getLevel();
3290
3284
  core.logger.setLevel("fatal");
3291
3285
  try {
@@ -3295,21 +3289,21 @@ async function deployCommand(options) {
3295
3289
  throw new Error(cliM("cli_build_runtime_missing"));
3296
3290
  if (options.artifactDir && options.build)
3297
3291
  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))
3292
+ if (!options.build && (options.concurrency !== void 0 || options.network !== void 0 || options.sqlitePrebuildMirror !== void 0 || options.npmSource !== void 0 || options.npmRegistry !== void 0))
3299
3293
  throw new Error(cliM("cli_deploy_build_options_required"));
3300
3294
  let selectedServices = options.services?.split(",").map((name) => name.trim());
3301
3295
  if (selectedServices && (!selectedServices.length || selectedServices.some((name) => !/^[a-z][a-z0-9-]*$/.test(name)) || new Set(selectedServices).size !== selectedServices.length))
3302
3296
  throw new Error(cliM("cli_build_selection_invalid"));
3303
3297
  let environmentName = options.env;
3304
3298
  if (!environmentName) {
3305
- if (!process4.stdin.isTTY)
3299
+ if (!process5.stdin.isTTY)
3306
3300
  throw new Error(cliM("cli_deploy_environment_hint"));
3307
3301
  const answer = await prompts({ type: "text", name: "environment", message: cliM("cli_deploy_environment"), initial: "prod" });
3308
3302
  environmentName = answer.environment;
3309
3303
  if (!environmentName)
3310
3304
  throw new Error(cliM("cli_deploy_cancelled"));
3311
3305
  }
3312
- if (!options.plan && !options.yes && !process4.stdin.isTTY)
3306
+ if (!options.plan && !options.yes && !process5.stdin.isTTY)
3313
3307
  throw new Error(cliM("cli_deploy_environment_hint"));
3314
3308
  let deployModule;
3315
3309
  try {
@@ -3328,7 +3322,7 @@ async function deployCommand(options) {
3328
3322
  localModule = localRequire.resolve("@h-ai/deploy");
3329
3323
  } catch {
3330
3324
  }
3331
- deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-24PXSKE6.js');
3325
+ deployModule = localModule ? await import(pathToFileURL(localModule).href) : await import('./src-CX5IJGYM.js');
3332
3326
  } catch {
3333
3327
  writeCliError(chalk.red(cliM("cli_deploy_module_missing")));
3334
3328
  writeCliMessage(chalk.cyan(" pnpm add -D @h-ai/deploy"));
@@ -3356,9 +3350,9 @@ async function deployCommand(options) {
3356
3350
  if (!environment.success)
3357
3351
  throw new Error(environment.error.message);
3358
3352
  if (options.plan) {
3359
- if (options.build || options.bundleOnly || options.output)
3353
+ 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
3354
  throw new Error(cliM("cli_deploy_plan_options"));
3361
- const loaded = deployModule.loadDeploymentConfig("deploy", buildFile, { ...process4.env, ...environment.data.values });
3355
+ const loaded = deployModule.loadDeploymentConfig("deploy", buildFile, { ...process5.env, ...environment.data.values });
3362
3356
  if (!loaded.success)
3363
3357
  throw new Error(loaded.error.message);
3364
3358
  const validated = deployModule.DeployConfigSchema.safeParse(loaded.data);
@@ -3370,7 +3364,7 @@ async function deployCommand(options) {
3370
3364
  writeCliMessage(JSON.stringify(plan.data, null, 2));
3371
3365
  return;
3372
3366
  }
3373
- Object.assign(process4.env, environment.data.values);
3367
+ Object.assign(process5.env, environment.data.values);
3374
3368
  spinner.start(cliM("cli_deploy_load_credentials"));
3375
3369
  const credResult = deploy.credentials.load();
3376
3370
  if (!credResult.success) {
@@ -3384,7 +3378,7 @@ async function deployCommand(options) {
3384
3378
  writeCliMessage(chalk.cyan(cliM("cli_deploy_config_hint")));
3385
3379
  throw new Error(cliM("cli_deploy_config_missing", { params: { path: configPath } }));
3386
3380
  }
3387
- const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process4.env);
3381
+ const loadedConfig = deployModule.loadDeploymentConfig("deploy", configPath, process5.env);
3388
3382
  if (!loadedConfig.success)
3389
3383
  throw new Error(loadedConfig.error.message);
3390
3384
  const deployConfig = loadedConfig.data;
@@ -3429,13 +3423,16 @@ async function deployCommand(options) {
3429
3423
  }
3430
3424
  }
3431
3425
  spinner.succeed(cliM("cli_deploy_scanned", { params: { name: scan.appName, sveltekit: String(scan.isSvelteKit), services: scan.requiredServices.join(", ") || cliM("cli_deploy_none") } }));
3426
+ const downloads = configObject(deployConfig.provider).type === "docker-ssh" ? await resolveBuildDownloads(buildRoot, options) : void 0;
3427
+ let builtImages = false;
3432
3428
  if (options.build) {
3433
3429
  if (!existsSync(buildFile))
3434
3430
  throw new Error(cliM("cli_build_images_missing"));
3435
3431
  const imageNames2 = selectedServices?.filter((name) => ["serv", "web"].includes(name));
3436
3432
  if (!selectedServices || imageNames2?.length) {
3437
3433
  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 });
3434
+ builtImages = true;
3435
+ 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
3436
  }
3440
3437
  }
3441
3438
  if (selectedServices)
@@ -3460,15 +3457,15 @@ async function deployCommand(options) {
3460
3457
  if (options.bundleOnly)
3461
3458
  await configureDeployTarget(deployModule, deployConfig, environment.data, options.yes, true);
3462
3459
  await configureDeployHttps(deployModule, deployConfig, environment.data, options.yes, selectedServices !== void 0);
3463
- for (const key of Object.keys(process4.env)) {
3460
+ for (const key of Object.keys(process5.env)) {
3464
3461
  if (originalEnv[key] === void 0)
3465
- delete process4.env[key];
3462
+ delete process5.env[key];
3466
3463
  }
3467
- Object.assign(process4.env, originalEnv);
3464
+ Object.assign(process5.env, originalEnv);
3468
3465
  const refreshed = deployModule.loadDeploymentEnvironment({ environment: environment.data.environment, envDir: environment.data.envDir });
3469
3466
  if (!refreshed.success)
3470
3467
  throw new Error(refreshed.error.message);
3471
- Object.assign(process4.env, refreshed.data.values);
3468
+ Object.assign(process5.env, refreshed.data.values);
3472
3469
  spinner.start(cliM("cli_deploy_initializing"));
3473
3470
  const validatedConfig = deployModule.DeployConfigSchema.safeParse(deployConfig);
3474
3471
  if (!validatedConfig.success)
@@ -3480,7 +3477,11 @@ async function deployCommand(options) {
3480
3477
  closeDeploy = () => deploy.close();
3481
3478
  spinner.succeed(cliM("cli_deploy_initialized"));
3482
3479
  spinner.start(cliM("cli_deploy_deploying"));
3480
+ if (downloads && !builtImages && (!selectedServices || selectedServices.some((name) => !["serv", "web"].includes(name))))
3481
+ await preflightDownloadProxy(options.runtime ?? "auto", downloads.proxy);
3483
3482
  const deployResult = await deploy.deployApp(appDir, {
3483
+ imageSource: downloads?.imageSource,
3484
+ proxy: downloads?.proxy,
3484
3485
  selectedServices,
3485
3486
  runtime: options.runtime,
3486
3487
  onProgress: (stage) => writeCliMessage(cliM(`cli_deploy_stage_${stage}`)),
@@ -3542,14 +3543,14 @@ async function deployCommand(options) {
3542
3543
  await closeDeploy();
3543
3544
  } catch (error) {
3544
3545
  writeCliError("Failed to close deploy module", error);
3545
- process4.exitCode = 1;
3546
+ process5.exitCode = 1;
3546
3547
  }
3547
3548
  }
3548
- for (const key of Object.keys(process4.env)) {
3549
+ for (const key of Object.keys(process5.env)) {
3549
3550
  if (originalEnv[key] === void 0)
3550
- delete process4.env[key];
3551
+ delete process5.env[key];
3551
3552
  }
3552
- Object.assign(process4.env, originalEnv);
3553
+ Object.assign(process5.env, originalEnv);
3553
3554
  core.logger.setLevel(previousLogLevel);
3554
3555
  restoreProjectLogger?.();
3555
3556
  }
@@ -4383,7 +4384,7 @@ async function startFeedbackViewer(bundle, options = {}) {
4383
4384
  async function feedbackCommand(action, bundlePath, options = {}) {
4384
4385
  if (!["inspect", "summary", "evidence", "view"].includes(action))
4385
4386
  throw new Error(cliM("cli_feedback_invalidAction"));
4386
- const filePath = path2.resolve(options.cwd ?? process4.cwd(), bundlePath);
4387
+ const filePath = path2.resolve(options.cwd ?? process5.cwd(), bundlePath);
4387
4388
  const metadata = await stat(filePath);
4388
4389
  if (!metadata.isFile() || metadata.size > 64 * 1024 * 1024)
4389
4390
  throw new Error(cliM("cli_feedback_invalidBundle"));
@@ -4417,12 +4418,12 @@ async function feedbackCommand(action, bundlePath, options = {}) {
4417
4418
  writeCliMessage(cliM("cli_feedback_viewerStarted", { params: { url: viewer.url } }));
4418
4419
  await new Promise((resolve7, reject) => {
4419
4420
  const stop = () => {
4420
- process4.off("SIGINT", stop);
4421
- process4.off("SIGTERM", stop);
4421
+ process5.off("SIGINT", stop);
4422
+ process5.off("SIGTERM", stop);
4422
4423
  viewer.close().then(resolve7, reject);
4423
4424
  };
4424
- process4.once("SIGINT", stop);
4425
- process4.once("SIGTERM", stop);
4425
+ process5.once("SIGINT", stop);
4426
+ process5.once("SIGTERM", stop);
4426
4427
  });
4427
4428
  }
4428
4429
  function appendAt(source, marker, entry) {
@@ -5042,7 +5043,7 @@ async function initProject(options) {
5042
5043
  const { project, existing, missing } = result;
5043
5044
  spinner.succeed(cliM("cli_initDetected", { params: { name: chalk.cyan(project.name) } }));
5044
5045
  if (project.isHaiProject) {
5045
- const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-COTP767H.js');
5046
+ const { generateProjectAiSupport, resolveSkillNamesForPackages } = await import('./cli-skill-templates-S2YH642Z.js');
5046
5047
  const generatedFiles = await generateProjectAiSupport(cwd, resolveSkillNamesForPackages(project.haiPackages), {
5047
5048
  overwriteSkills: false,
5048
5049
  overwriteBridgeFiles: false
@@ -5120,7 +5121,7 @@ async function initProject(options) {
5120
5121
  throw error;
5121
5122
  }
5122
5123
  }
5123
- function object(value) {
5124
+ function object2(value) {
5124
5125
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
5125
5126
  }
5126
5127
  function declaresValueExport(source, name) {
@@ -5153,9 +5154,9 @@ async function inspectFrameworkUpgrade(directory, version) {
5153
5154
  throw new Error(cliM("cli_upgrade_exact_version"));
5154
5155
  const root = realpathSync(resolve(directory));
5155
5156
  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 : [];
5157
+ const workspace = existsSync(workspaceFile) ? object2(parse(readFileSync(workspaceFile, "utf8"))) : {};
5158
+ const rootManifest = object2(JSON.parse(readFileSync(join(root, "package.json"), "utf8")));
5159
+ 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
5160
  const directories = /* @__PURE__ */ new Set([root]);
5160
5161
  const safePatterns = patterns.filter((pattern) => typeof pattern === "string" && !isAbsolute(pattern.replace(/^!/, "")) && !pattern.split(/[\\/]/).includes(".."));
5161
5162
  const exclude = ["**/node_modules/**", ...safePatterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => `${pattern.slice(1)}/package.json`)];
@@ -5163,29 +5164,29 @@ async function inspectFrameworkUpgrade(directory, version) {
5163
5164
  for await (const manifest of glob(`${pattern}/package.json`, { cwd: root, exclude })) {
5164
5165
  const location = realpathSync(dirname(join(root, manifest)));
5165
5166
  const fromRoot = relative(root, location);
5166
- if (!isAbsolute(fromRoot) && fromRoot !== ".." && !fromRoot.startsWith(`..${process4.platform === "win32" ? "\\" : "/"}`))
5167
+ if (!isAbsolute(fromRoot) && fromRoot !== ".." && !fromRoot.startsWith(`..${process5.platform === "win32" ? "\\" : "/"}`))
5167
5168
  directories.add(location);
5168
5169
  }
5169
5170
  }
5170
5171
  const packages = /* @__PURE__ */ new Set(["@h-ai/cli", "@h-ai/deploy"]);
5171
5172
  const drift = [];
5172
- const catalog = object(workspace.catalog);
5173
+ const catalog = object2(workspace.catalog);
5173
5174
  const lockPath = join(root, "pnpm-lock.yaml");
5174
5175
  const lock = existsSync(lockPath) ? parseAllDocuments(readFileSync(lockPath, "utf8")).reduce((all, document) => {
5175
5176
  if (document.errors.length)
5176
5177
  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) } };
5178
+ const part = object2(document.toJS());
5179
+ return { ...all, ...part, importers: { ...object2(all.importers), ...object2(part.importers) } };
5179
5180
  }, {}) : {};
5180
5181
  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("\\", "/") || "."]);
5182
+ const manifest = object2(JSON.parse(readFileSync(join(location, "package.json"), "utf8")));
5183
+ const dependencies = { ...object2(manifest.dependencies), ...object2(manifest.devDependencies), ...object2(manifest.optionalDependencies) };
5184
+ const importer = object2(object2(lock.importers)[relative(root, location).replaceAll("\\", "/") || "."]);
5184
5185
  for (const [name, specifier] of Object.entries(dependencies)) {
5185
5186
  if (!/^@h-ai\/[\w-]+$/.test(name) || typeof specifier !== "string")
5186
5187
  continue;
5187
5188
  packages.add(name);
5188
- const declared = specifier.startsWith("catalog:") ? String(object(specifier === "catalog:" ? catalog : object(workspace.catalogs)[specifier.slice(8)])[name] ?? "") : specifier;
5189
+ const declared = specifier.startsWith("catalog:") ? String(object2(specifier === "catalog:" ? catalog : object2(workspace.catalogs)[specifier.slice(8)])[name] ?? "") : specifier;
5189
5190
  const issues = [];
5190
5191
  if (declared !== version && !declared.startsWith("workspace:"))
5191
5192
  issues.push("declared-version-drift");
@@ -5193,7 +5194,7 @@ async function inspectFrameworkUpgrade(directory, version) {
5193
5194
  try {
5194
5195
  const path10 = findPackageJSON(name, pathToFileURL(join(location, "package.json")));
5195
5196
  if (path10) {
5196
- const candidate = object(JSON.parse(readFileSync(path10, "utf8")));
5197
+ const candidate = object2(JSON.parse(readFileSync(path10, "utf8")));
5197
5198
  if (candidate.name === name && typeof candidate.version === "string")
5198
5199
  installed = candidate.version;
5199
5200
  }
@@ -5201,13 +5202,13 @@ async function inspectFrameworkUpgrade(directory, version) {
5201
5202
  }
5202
5203
  if (installed !== version)
5203
5204
  issues.push(installed ? "installed-version-drift" : "not-installed");
5204
- const locked = object(object(importer.dependencies)[name] ?? object(importer.devDependencies)[name] ?? object(importer.optionalDependencies)[name]);
5205
+ const locked = object2(object2(importer.dependencies)[name] ?? object2(importer.devDependencies)[name] ?? object2(importer.optionalDependencies)[name]);
5205
5206
  if (typeof locked.version !== "string" || !locked.version.startsWith("link:") && locked.version.split("(")[0] !== version)
5206
5207
  issues.push("lock-version-drift");
5207
5208
  drift.push({ package: name, source: relative(root, join(location, "package.json")).replaceAll("\\", "/"), declared, installed, issues });
5208
5209
  }
5209
5210
  }
5210
- const registry = new URL(process4.env.npm_config_registry ?? "https://registry.npmjs.org/");
5211
+ const registry = new URL(process5.env.npm_config_registry ?? "https://registry.npmjs.org/");
5211
5212
  if (registry.username || registry.password || registry.protocol !== "https:")
5212
5213
  throw new Error(cliM("cli_upgrade_registry"));
5213
5214
  const releases = [];
@@ -5222,11 +5223,11 @@ async function inspectFrameworkUpgrade(directory, version) {
5222
5223
  const response = await fetch(new URL(`${encodeURIComponent(name)}/${encodeURIComponent(version)}`, registry), { signal: AbortSignal.timeout(2e4) });
5223
5224
  if (!response.ok)
5224
5225
  throw new Error("Release missing");
5225
- const metadata = object(await response.json());
5226
- const dist = object(metadata.dist);
5226
+ const metadata = object2(await response.json());
5227
+ const dist = object2(metadata.dist);
5227
5228
  if (metadata.version !== version || typeof dist.tarball !== "string" || typeof dist.integrity !== "string")
5228
5229
  throw new Error("Invalid metadata");
5229
- for (const dependency of Object.keys(object(metadata.dependencies))) {
5230
+ for (const dependency of Object.keys(object2(metadata.dependencies))) {
5230
5231
  if (/^@h-ai\/[\w-]+$/.test(dependency) && !packages.has(dependency)) {
5231
5232
  packages.add(dependency);
5232
5233
  pending.push(dependency);
@@ -5250,7 +5251,7 @@ async function inspectFrameworkUpgrade(directory, version) {
5250
5251
  throw new Error("Integrity mismatch");
5251
5252
  const files = tarFiles(bytes);
5252
5253
  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"))
5254
+ if (!manifest || object2(JSON.parse(new TextDecoder().decode(manifest))).version !== version || !files.has("package/dist/index.js"))
5254
5255
  throw new Error("Incomplete artifact");
5255
5256
  const migration = { "@h-ai/iam": "migrateIam", "@h-ai/reach": "migrateReach", "@h-ai/scheduler": "migrateScheduler", "@h-ai/audit": "migrateAudit" }[name];
5256
5257
  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 +5278,13 @@ async function inspectFrameworkUpgrade(directory, version) {
5277
5278
  dirtyFiles = execFileSync("git", ["status", "--porcelain", "--", "."], { cwd: root, encoding: "utf8", windowsHide: true, stdio: "pipe" }).trim().split("\n").filter(Boolean);
5278
5279
  } catch {
5279
5280
  }
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" } };
5281
+ 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
5282
  }
5282
5283
  async function upgradeCommand(version, options = {}) {
5283
- const report = await inspectFrameworkUpgrade(options.cwd ?? process4.cwd(), version);
5284
+ const report = await inspectFrameworkUpgrade(options.cwd ?? process5.cwd(), version);
5284
5285
  writeCliMessage(JSON.stringify(report, null, 2));
5285
5286
  if (!report.releaseComplete)
5286
- process4.exitCode = 1;
5287
+ process5.exitCode = 1;
5287
5288
  }
5288
5289
 
5289
5290
  // src/cli-main.ts
@@ -5308,7 +5309,7 @@ cli.command("create [name]", cliM("cli_help_create")).option("--type <type>", cl
5308
5309
  cwd: options.cwd
5309
5310
  });
5310
5311
  } catch {
5311
- process4.exitCode = 1;
5312
+ process5.exitCode = 1;
5312
5313
  }
5313
5314
  });
5314
5315
  cli.command("add [module]", cliM("cli_help_add")).option("--no-install", cliM("cli_help_noInstall")).action(async (module, options) => {
@@ -5320,7 +5321,7 @@ cli.command("add [module]", cliM("cli_help_add")).option("--no-install", cliM("c
5320
5321
  cwd: options.cwd
5321
5322
  });
5322
5323
  } catch {
5323
- process4.exit(1);
5324
+ process5.exit(1);
5324
5325
  }
5325
5326
  });
5326
5327
  cli.command("init", cliM("cli_help_init")).option("-f, --force", cliM("cli_help_forceConfig")).action(async (options) => {
@@ -5331,7 +5332,7 @@ cli.command("init", cliM("cli_help_init")).option("-f, --force", cliM("cli_help_
5331
5332
  cwd: options.cwd
5332
5333
  });
5333
5334
  } catch {
5334
- process4.exit(1);
5335
+ process5.exit(1);
5335
5336
  }
5336
5337
  });
5337
5338
  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 +5347,15 @@ cli.command("generate [type] [name]", cliM("cli_help_generate")).option("-w, --w
5346
5347
  cwd: options.cwd
5347
5348
  });
5348
5349
  } catch {
5349
- process4.exit(1);
5350
+ process5.exit(1);
5350
5351
  }
5351
5352
  });
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) => {
5353
+ 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
5354
  try {
5354
5355
  await buildCommand(options);
5355
5356
  } catch (error) {
5356
5357
  writeCliMessage(error instanceof Error ? error.message : String(error));
5357
- process4.exitCode = 1;
5358
+ process5.exitCode = 1;
5358
5359
  }
5359
5360
  });
5360
5361
  cli.command("upgrade <version>", cliM("cli_help_upgrade")).action(async (version, options) => {
@@ -5362,10 +5363,10 @@ cli.command("upgrade <version>", cliM("cli_help_upgrade")).action(async (version
5362
5363
  await upgradeCommand(version, options);
5363
5364
  } catch (error) {
5364
5365
  writeCliMessage(error instanceof Error ? error.message : String(error));
5365
- process4.exitCode = 1;
5366
+ process5.exitCode = 1;
5366
5367
  }
5367
5368
  });
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) => {
5369
+ 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
5370
  try {
5370
5371
  await deployCommand({
5371
5372
  appDir,
@@ -5382,6 +5383,13 @@ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("c
5382
5383
  network: options.network,
5383
5384
  sqlitePrebuildMirror: options.sqlitePrebuildMirror,
5384
5385
  httpProxy: options.httpProxy,
5386
+ imageSource: options.imageSource,
5387
+ imageMirrors: options.imageMirrors,
5388
+ npmSource: options.npmSource,
5389
+ npmRegistry: options.npmRegistry,
5390
+ proxyMode: options.proxyMode,
5391
+ proxyUrl: options.proxyUrl,
5392
+ proxyBypass: options.proxyBypass,
5385
5393
  runtime: options.runtime,
5386
5394
  bundleOnly: options.bundleOnly,
5387
5395
  output: options.output,
@@ -5389,7 +5397,7 @@ cli.command("deploy [appDir]", cliM("cli_help_deploy")).option("--plan", cliM("c
5389
5397
  cwd: options.cwd
5390
5398
  });
5391
5399
  } catch {
5392
- process4.exitCode = 1;
5400
+ process5.exitCode = 1;
5393
5401
  }
5394
5402
  });
5395
5403
  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 +5405,7 @@ cli.command("deploy-secrets <directory>", cliM("cli_help_deploySecrets")).option
5397
5405
  await deploySecretsCommand(directory, options);
5398
5406
  } catch (error) {
5399
5407
  writeCliMessage(error instanceof Error ? error.message : String(error));
5400
- process4.exitCode = 1;
5408
+ process5.exitCode = 1;
5401
5409
  }
5402
5410
  });
5403
5411
  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 +5424,7 @@ cli.command("feedback <action> <bundle>", cliM("cli_help_feedback")).option("--p
5416
5424
  });
5417
5425
  } catch (error) {
5418
5426
  writeCliMessage(error instanceof Error ? error.message : String(error));
5419
- process4.exitCode = 1;
5427
+ process5.exitCode = 1;
5420
5428
  }
5421
5429
  });
5422
5430
  cli.command("g:page <name>", cliM("cli_help_generatePage")).option("-w, --workspace <name>", cliM("cli_help_workspace")).action(async (name, options) => {