gaoding-cli 1.0.0-alpha.5

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 (177) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/LICENSE +21 -0
  3. package/README.md +136 -0
  4. package/SECURITY.md +34 -0
  5. package/dist/bin/gd-cli.d.ts +2 -0
  6. package/dist/bin/gd-cli.js +10 -0
  7. package/dist/src/api/app-runner.d.ts +33 -0
  8. package/dist/src/api/app-runner.js +1177 -0
  9. package/dist/src/api/dam-client.d.ts +111 -0
  10. package/dist/src/api/dam-client.js +897 -0
  11. package/dist/src/api/direct-auth.d.ts +3 -0
  12. package/dist/src/api/direct-auth.js +11 -0
  13. package/dist/src/api/direct-client.d.ts +27 -0
  14. package/dist/src/api/direct-client.js +151 -0
  15. package/dist/src/api/direct-tools.d.ts +43 -0
  16. package/dist/src/api/direct-tools.js +1155 -0
  17. package/dist/src/api/hub-client.d.ts +20 -0
  18. package/dist/src/api/hub-client.js +99 -0
  19. package/dist/src/api/sso-client.d.ts +60 -0
  20. package/dist/src/api/sso-client.js +140 -0
  21. package/dist/src/api/task-poller.d.ts +38 -0
  22. package/dist/src/api/task-poller.js +72 -0
  23. package/dist/src/api/tool-invoker.d.ts +12 -0
  24. package/dist/src/api/tool-invoker.js +11 -0
  25. package/dist/src/api/usage-client.d.ts +39 -0
  26. package/dist/src/api/usage-client.js +179 -0
  27. package/dist/src/auth/credential-store-v2.d.ts +12 -0
  28. package/dist/src/auth/credential-store-v2.js +59 -0
  29. package/dist/src/commands/auth.d.ts +2 -0
  30. package/dist/src/commands/auth.js +191 -0
  31. package/dist/src/commands/dam.d.ts +2 -0
  32. package/dist/src/commands/dam.js +293 -0
  33. package/dist/src/commands/models.d.ts +2 -0
  34. package/dist/src/commands/models.js +78 -0
  35. package/dist/src/commands/org.d.ts +2 -0
  36. package/dist/src/commands/org.js +137 -0
  37. package/dist/src/commands/skills.d.ts +2 -0
  38. package/dist/src/commands/skills.js +213 -0
  39. package/dist/src/commands/tools.d.ts +4 -0
  40. package/dist/src/commands/tools.js +383 -0
  41. package/dist/src/commands/update.d.ts +2 -0
  42. package/dist/src/commands/update.js +167 -0
  43. package/dist/src/commands/usage.d.ts +7 -0
  44. package/dist/src/commands/usage.js +43 -0
  45. package/dist/src/commands/workflows.d.ts +3 -0
  46. package/dist/src/commands/workflows.js +398 -0
  47. package/dist/src/core/auth/auth-provider.d.ts +8 -0
  48. package/dist/src/core/auth/auth-provider.js +12 -0
  49. package/dist/src/core/auth/device-oauth-provider.d.ts +12 -0
  50. package/dist/src/core/auth/device-oauth-provider.js +124 -0
  51. package/dist/src/core/auth/org-manager.d.ts +26 -0
  52. package/dist/src/core/auth/org-manager.js +224 -0
  53. package/dist/src/core/auth/signature.d.ts +19 -0
  54. package/dist/src/core/auth/signature.js +33 -0
  55. package/dist/src/core/auth/types.d.ts +52 -0
  56. package/dist/src/core/auth/types.js +3 -0
  57. package/dist/src/core/output/cli-error.d.ts +5 -0
  58. package/dist/src/core/output/cli-error.js +8 -0
  59. package/dist/src/core/output/detector.d.ts +5 -0
  60. package/dist/src/core/output/detector.js +14 -0
  61. package/dist/src/core/output/engine.d.ts +32 -0
  62. package/dist/src/core/output/engine.js +77 -0
  63. package/dist/src/core/output/envelope.d.ts +40 -0
  64. package/dist/src/core/output/envelope.js +119 -0
  65. package/dist/src/core/output/index.d.ts +4 -0
  66. package/dist/src/core/output/index.js +4 -0
  67. package/dist/src/core/output/live-display.d.ts +15 -0
  68. package/dist/src/core/output/live-display.js +111 -0
  69. package/dist/src/core/output/result-card.d.ts +18 -0
  70. package/dist/src/core/output/result-card.js +39 -0
  71. package/dist/src/core/prompt/index.d.ts +31 -0
  72. package/dist/src/core/prompt/index.js +36 -0
  73. package/dist/src/io.d.ts +14 -0
  74. package/dist/src/io.js +31 -0
  75. package/dist/src/middleware/auth.d.ts +3 -0
  76. package/dist/src/middleware/auth.js +74 -0
  77. package/dist/src/middleware/error-handler.d.ts +3 -0
  78. package/dist/src/middleware/error-handler.js +431 -0
  79. package/dist/src/middleware/interactive-params.d.ts +14 -0
  80. package/dist/src/middleware/interactive-params.js +53 -0
  81. package/dist/src/middleware/output.d.ts +14 -0
  82. package/dist/src/middleware/output.js +546 -0
  83. package/dist/src/models/model-catalog.d.ts +26 -0
  84. package/dist/src/models/model-catalog.js +216 -0
  85. package/dist/src/program.d.ts +2 -0
  86. package/dist/src/program.js +152 -0
  87. package/dist/src/registry/agent-contracts.d.ts +43 -0
  88. package/dist/src/registry/agent-contracts.js +483 -0
  89. package/dist/src/registry/app-loader.d.ts +2 -0
  90. package/dist/src/registry/app-loader.js +70 -0
  91. package/dist/src/registry/direct-registry.d.ts +11 -0
  92. package/dist/src/registry/direct-registry.js +234 -0
  93. package/dist/src/registry/registry-cache.d.ts +10 -0
  94. package/dist/src/registry/registry-cache.js +52 -0
  95. package/dist/src/registry/registry-client.d.ts +28 -0
  96. package/dist/src/registry/registry-client.js +50 -0
  97. package/dist/src/registry/types.d.ts +69 -0
  98. package/dist/src/registry/types.js +1 -0
  99. package/dist/src/tty/auth-presenter.d.ts +3 -0
  100. package/dist/src/tty/auth-presenter.js +21 -0
  101. package/dist/src/update/install-source.d.ts +19 -0
  102. package/dist/src/update/install-source.js +139 -0
  103. package/dist/src/update/notifier.d.ts +2 -0
  104. package/dist/src/update/notifier.js +59 -0
  105. package/dist/src/update/update-checker.d.ts +22 -0
  106. package/dist/src/update/update-checker.js +110 -0
  107. package/dist/src/update/version.d.ts +9 -0
  108. package/dist/src/update/version.js +52 -0
  109. package/dist/src/utils/agent-skill-installer.d.ts +57 -0
  110. package/dist/src/utils/agent-skill-installer.js +389 -0
  111. package/dist/src/utils/capability-metadata.d.ts +18 -0
  112. package/dist/src/utils/capability-metadata.js +88 -0
  113. package/dist/src/utils/capability-presenter.d.ts +4 -0
  114. package/dist/src/utils/capability-presenter.js +190 -0
  115. package/dist/src/utils/config.d.ts +16 -0
  116. package/dist/src/utils/config.js +54 -0
  117. package/dist/src/utils/credential-permissions.d.ts +2 -0
  118. package/dist/src/utils/credential-permissions.js +22 -0
  119. package/dist/src/utils/ecommerce-input-guidance.d.ts +15 -0
  120. package/dist/src/utils/ecommerce-input-guidance.js +85 -0
  121. package/dist/src/utils/endpoints.d.ts +52 -0
  122. package/dist/src/utils/endpoints.js +258 -0
  123. package/dist/src/utils/enrich-upload-params.d.ts +6 -0
  124. package/dist/src/utils/enrich-upload-params.js +11 -0
  125. package/dist/src/utils/entitlement.d.ts +17 -0
  126. package/dist/src/utils/entitlement.js +146 -0
  127. package/dist/src/utils/gd-home.d.ts +7 -0
  128. package/dist/src/utils/gd-home.js +26 -0
  129. package/dist/src/utils/help-verify.d.ts +9 -0
  130. package/dist/src/utils/help-verify.js +41 -0
  131. package/dist/src/utils/input-json.d.ts +3 -0
  132. package/dist/src/utils/input-json.js +61 -0
  133. package/dist/src/utils/list-output-format.d.ts +10 -0
  134. package/dist/src/utils/list-output-format.js +34 -0
  135. package/dist/src/utils/logger.d.ts +6 -0
  136. package/dist/src/utils/logger.js +19 -0
  137. package/dist/src/utils/matting-compose.d.ts +10 -0
  138. package/dist/src/utils/matting-compose.js +45 -0
  139. package/dist/src/utils/open-browser.d.ts +1 -0
  140. package/dist/src/utils/open-browser.js +4 -0
  141. package/dist/src/utils/output-path.d.ts +5 -0
  142. package/dist/src/utils/output-path.js +70 -0
  143. package/dist/src/utils/redact.d.ts +3 -0
  144. package/dist/src/utils/redact.js +96 -0
  145. package/dist/src/utils/reference-image.d.ts +8 -0
  146. package/dist/src/utils/reference-image.js +43 -0
  147. package/dist/src/utils/resolve-app-local-images.d.ts +8 -0
  148. package/dist/src/utils/resolve-app-local-images.js +84 -0
  149. package/dist/src/utils/sensitive-path.d.ts +6 -0
  150. package/dist/src/utils/sensitive-path.js +69 -0
  151. package/dist/src/utils/settings-store.d.ts +42 -0
  152. package/dist/src/utils/settings-store.js +50 -0
  153. package/dist/src/utils/sleep.d.ts +1 -0
  154. package/dist/src/utils/sleep.js +3 -0
  155. package/dist/src/utils/spinner.d.ts +2 -0
  156. package/dist/src/utils/spinner.js +4 -0
  157. package/dist/src/utils/static-help.d.ts +2 -0
  158. package/dist/src/utils/static-help.js +3 -0
  159. package/dist/src/utils/tool-display.d.ts +10 -0
  160. package/dist/src/utils/tool-display.js +55 -0
  161. package/dist/src/utils/transient-network-error.d.ts +4 -0
  162. package/dist/src/utils/transient-network-error.js +40 -0
  163. package/dist/src/utils/url-safety.d.ts +6 -0
  164. package/dist/src/utils/url-safety.js +67 -0
  165. package/dist/src/utils/write-app-deliverables.d.ts +18 -0
  166. package/dist/src/utils/write-app-deliverables.js +386 -0
  167. package/package.json +91 -0
  168. package/skills/gd-cli/SKILL.md +57 -0
  169. package/skills/gd-cli/references/auth.md +31 -0
  170. package/skills/gd-cli/references/deliverables.md +22 -0
  171. package/skills/gd-cli/references/entitlement.md +21 -0
  172. package/skills/gd-cli/references/errors.md +19 -0
  173. package/skills/gd-cli/references/org.md +18 -0
  174. package/skills/gd-cli/references/sop.md +25 -0
  175. package/skills/gd-cli/references/tools.md +29 -0
  176. package/skills/gd-cli/references/update.md +16 -0
  177. package/skills/gd-cli/references/workflows.md +22 -0
@@ -0,0 +1,43 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { assertSafeHttpUrl } from "./url-safety.js";
4
+ export function isRemoteImageReference(value) {
5
+ return /^https?:\/\//i.test(value.trim());
6
+ }
7
+ export function firstReferenceImage(value) {
8
+ if (Array.isArray(value)) {
9
+ return value.map(String).map((item) => item.trim()).find(Boolean);
10
+ }
11
+ if (typeof value !== "string")
12
+ return undefined;
13
+ const trimmed = value.trim();
14
+ if (!trimmed)
15
+ return undefined;
16
+ return trimmed.split(/[,\n]+/g).map((item) => item.trim()).find(Boolean);
17
+ }
18
+ export function assertReferenceImageAvailable(value, options) {
19
+ const image = firstReferenceImage(value);
20
+ if (!image) {
21
+ throw new Error(referenceImageRequiredMessage(options.context));
22
+ }
23
+ if (isRemoteImageReference(image)) {
24
+ return assertSafeHttpUrl(image, { context: "参考图 URL" });
25
+ }
26
+ const filePath = resolve(process.cwd(), image);
27
+ if (!existsSync(filePath)) {
28
+ throw new Error([
29
+ `REFERENCE_IMAGE_REQUIRED: ${options.context} 需要原始参考图,但当前图片路径不存在:${image}`,
30
+ "请提供本地图片文件路径或可访问的 http(s) 图片 URL。",
31
+ "不要根据截图描述或识图结果重新生成替代参考图;只有用户明确要求“重新生成一张相似参考图”时才允许这么做。",
32
+ ].join("\n"));
33
+ }
34
+ return image;
35
+ }
36
+ export function referenceImageRequiredMessage(context) {
37
+ return [
38
+ `REFERENCE_IMAGE_REQUIRED: ${context} 需要原始参考图。`,
39
+ "请提供本地图片文件路径或可访问的 http(s) 图片 URL。",
40
+ "如果图片来自 Codex 对话附件,请先确认附件已落到工作区可读路径,再把该路径传给 --image。",
41
+ "不要通过解析附件内容后重新生成替代商品图。",
42
+ ].join("\n");
43
+ }
@@ -0,0 +1,8 @@
1
+ import type { ToolInvoker } from "../api/tool-invoker.js";
2
+ /**
3
+ * App 编排侧工具需要可访问 URL;本地路径在网关会变成 file: 导致 Dify 失败。
4
+ * 在 `gd-cli workflows run` 提交前对常见图片字段做上传替换,并写入 `image_url` 供 chat/cutout 等读取。
5
+ */
6
+ export declare function resolveAppLocalMediaToUrls(params: Record<string, unknown>, invoker: ToolInvoker, options?: {
7
+ allowSensitivePath?: boolean;
8
+ }): Promise<Record<string, unknown>>;
@@ -0,0 +1,84 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { basename, resolve } from "node:path";
3
+ import { assertSafeUploadPath } from "./sensitive-path.js";
4
+ import { assertSafeHttpUrl } from "./url-safety.js";
5
+ const MAX_BYTES = 25 * 1024 * 1024;
6
+ function isHttpUrl(s) {
7
+ return /^https?:\/\//i.test(s.trim());
8
+ }
9
+ function extractUrlFromOutputs(outputs) {
10
+ if (outputs && typeof outputs === "object") {
11
+ const o = outputs;
12
+ const u = o.url ?? o.uri;
13
+ if (typeof u === "string" && isHttpUrl(u)) {
14
+ return assertSafeHttpUrl(u.trim(), { context: "上传响应 URL" });
15
+ }
16
+ }
17
+ const raw = JSON.stringify(outputs ?? "");
18
+ const m = raw.match(/https?:\/\/[^"'\s\\]+/);
19
+ if (m)
20
+ return assertSafeHttpUrl(m[0], { context: "上传响应 URL" });
21
+ throw new Error("上传响应中未找到可用的 http(s) URL");
22
+ }
23
+ async function uploadLocalFile(invoker, absolutePath, allowSensitivePath) {
24
+ const safePath = assertSafeUploadPath(absolutePath, {
25
+ allowSensitivePath,
26
+ context: "App 图片",
27
+ });
28
+ const fileStat = statSync(safePath);
29
+ if (fileStat.size > MAX_BYTES) {
30
+ throw new Error(`文件过大(>${MAX_BYTES} 字节): ${absolutePath}`);
31
+ }
32
+ const result = await invoker.invoke("upload", {
33
+ file: safePath,
34
+ file_name: basename(safePath),
35
+ allow_sensitive_path: allowSensitivePath,
36
+ }, { timeout: 120 });
37
+ if (result.status !== "completed") {
38
+ throw new Error(`上传失败: ${result.status} ${JSON.stringify(result.error ?? result)}`);
39
+ }
40
+ return extractUrlFromOutputs(result.outputs);
41
+ }
42
+ /**
43
+ * 若值为本机可读文件路径,则先 `upload` 再替换为 CDN URL。
44
+ * 已是 http(s) 则原样保留。
45
+ */
46
+ async function resolveMaybeLocalPath(invoker, raw, allowSensitivePath) {
47
+ const t = raw.trim();
48
+ if (!t)
49
+ return t;
50
+ if (isHttpUrl(t))
51
+ return assertSafeHttpUrl(t, { context: "App 图片 URL" });
52
+ const p = resolve(process.cwd(), t);
53
+ if (!existsSync(p) || !statSync(p).isFile()) {
54
+ return raw;
55
+ }
56
+ return uploadLocalFile(invoker, p, allowSensitivePath);
57
+ }
58
+ /**
59
+ * App 编排侧工具需要可访问 URL;本地路径在网关会变成 file: 导致 Dify 失败。
60
+ * 在 `gd-cli workflows run` 提交前对常见图片字段做上传替换,并写入 `image_url` 供 chat/cutout 等读取。
61
+ */
62
+ export async function resolveAppLocalMediaToUrls(params, invoker, options = {}) {
63
+ const out = { ...params };
64
+ const allowSensitivePath = options.allowSensitivePath === true;
65
+ const singleKeys = ["image", "product_image", "cover_image"];
66
+ for (const key of singleKeys) {
67
+ const v = out[key];
68
+ if (typeof v !== "string" || !v.trim())
69
+ continue;
70
+ out[key] = await resolveMaybeLocalPath(invoker, v, allowSensitivePath);
71
+ }
72
+ const primary = out.image ?? out.product_image ?? out.cover_image;
73
+ if (typeof primary === "string" && isHttpUrl(primary)) {
74
+ out.image_url = primary.trim();
75
+ }
76
+ if (typeof out.images === "string" && out.images.trim()) {
77
+ const parts = out.images
78
+ .split(",")
79
+ .map((s) => s.trim())
80
+ .filter(Boolean);
81
+ out.images = (await Promise.all(parts.map((p) => resolveMaybeLocalPath(invoker, p, allowSensitivePath)))).join(",");
82
+ }
83
+ return out;
84
+ }
@@ -0,0 +1,6 @@
1
+ export interface SensitivePathOptions {
2
+ allowSensitivePath?: boolean;
3
+ context?: string;
4
+ }
5
+ export declare function assertSafeUploadPath(file: string, options?: SensitivePathOptions): string;
6
+ export declare function sensitivePathReason(file: string): string | null;
@@ -0,0 +1,69 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ const SENSITIVE_SEGMENTS = new Set([
4
+ ".aws",
5
+ ".azure",
6
+ ".config",
7
+ ".docker",
8
+ ".gd",
9
+ ".gd-cli-dev",
10
+ ".git",
11
+ ".gnupg",
12
+ ".kube",
13
+ ".npm",
14
+ ".ssh",
15
+ ]);
16
+ const SENSITIVE_FILENAMES = new Set([
17
+ ".env",
18
+ ".env.local",
19
+ ".git-credentials",
20
+ ".gitconfig",
21
+ ".npmrc",
22
+ ".netrc",
23
+ ".pypirc",
24
+ "credentials.json",
25
+ "credential.json",
26
+ "id_ed25519",
27
+ "id_rsa",
28
+ "known_hosts",
29
+ ]);
30
+ export function assertSafeUploadPath(file, options = {}) {
31
+ const absolutePath = path.resolve(process.cwd(), file);
32
+ if (allowSensitive(options))
33
+ return absolutePath;
34
+ const reason = sensitivePathReason(absolutePath);
35
+ if (reason) {
36
+ const context = options.context ?? "本地文件";
37
+ throw new Error(`${context} 位于受保护路径,默认不上传:${absolutePath}(${reason})。确认需要上传时请显式传 --allow-sensitive-path。`);
38
+ }
39
+ return absolutePath;
40
+ }
41
+ export function sensitivePathReason(file) {
42
+ const absolutePath = path.resolve(process.cwd(), file);
43
+ const segments = absolutePath.split(path.sep).filter(Boolean);
44
+ const lowerSegments = segments.map((segment) => segment.toLowerCase());
45
+ const filename = lowerSegments[lowerSegments.length - 1] ?? "";
46
+ if (SENSITIVE_FILENAMES.has(filename) || /^\.env(\.|$)/.test(filename)) {
47
+ return `敏感文件名 ${filename}`;
48
+ }
49
+ const hiddenSegment = lowerSegments.find((segment) => segment.startsWith("."));
50
+ if (hiddenSegment) {
51
+ return `隐藏路径片段 ${hiddenSegment}`;
52
+ }
53
+ const sensitiveSegment = lowerSegments.find((segment) => SENSITIVE_SEGMENTS.has(segment));
54
+ if (sensitiveSegment) {
55
+ return `敏感目录 ${sensitiveSegment}`;
56
+ }
57
+ const home = os.homedir();
58
+ const relativeToHome = path.relative(home, absolutePath);
59
+ if (relativeToHome && !relativeToHome.startsWith("..") && !path.isAbsolute(relativeToHome)) {
60
+ const firstHomeSegment = relativeToHome.split(path.sep)[0]?.toLowerCase();
61
+ if (firstHomeSegment && SENSITIVE_SEGMENTS.has(firstHomeSegment)) {
62
+ return `家目录敏感路径 ${firstHomeSegment}`;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ function allowSensitive(options) {
68
+ return options.allowSensitivePath === true || process.env.GD_CLI_ALLOW_SENSITIVE_PATHS === "1";
69
+ }
@@ -0,0 +1,42 @@
1
+ export { SETTINGS_FILE } from "./gd-home.js";
2
+ export type GdEnvName = "prod" | "fat" | "stage" | "local";
3
+ export interface GdSettingsOverrides {
4
+ hub_api_base?: string;
5
+ sso_api_base?: string;
6
+ auth_page_origin?: string;
7
+ web_origin?: string;
8
+ channel_id?: string;
9
+ business_id?: string;
10
+ }
11
+ /** 持久化偏好(环境变量仍优先于本文件) */
12
+ export interface GdSettings {
13
+ /** Active environment. Only prod has built-in endpoints. */
14
+ active_env?: GdEnvName;
15
+ /** 图像/视频模型偏好;auto 表示使用平台返回的默认顺序 */
16
+ models?: {
17
+ image?: string;
18
+ video?: string;
19
+ };
20
+ /** 平台模型顺序缓存;由 CLI 内部每日最多刷新一次,Agent 不需要显式查询模型 */
21
+ model_cache?: {
22
+ image?: ModelCacheEntry;
23
+ video?: ModelCacheEntry;
24
+ };
25
+ /** 单项覆盖,不改变 active_env */
26
+ overrides?: GdSettingsOverrides;
27
+ /** @deprecated 请用 active_env=local */
28
+ use_local_server?: boolean;
29
+ /** @deprecated 请用 overrides.hub_api_base */
30
+ api_base?: string;
31
+ }
32
+ export interface ModelCacheEntry {
33
+ date: string;
34
+ codes: string[];
35
+ source: "remote" | "fallback";
36
+ fetched_at: string;
37
+ }
38
+ export declare function readSettings(): GdSettings;
39
+ export declare function writeSettings(next: GdSettings): void;
40
+ export declare function patchSettings(patch: Partial<GdSettings>): GdSettings;
41
+ /** 清除端点相关字段,恢复为默认 prod(在无环境变量覆盖时) */
42
+ export declare function clearEndpointSettings(): void;
@@ -0,0 +1,50 @@
1
+ import fs from "node:fs";
2
+ import { ensureCredentialDir, ensureCredentialFile } from "./credential-permissions.js";
3
+ import { GD_DIR, SETTINGS_FILE } from "./gd-home.js";
4
+ export { SETTINGS_FILE } from "./gd-home.js";
5
+ export function readSettings() {
6
+ try {
7
+ if (!fs.existsSync(SETTINGS_FILE))
8
+ return {};
9
+ ensureCredentialDir(GD_DIR);
10
+ ensureCredentialFile(SETTINGS_FILE);
11
+ const raw = fs.readFileSync(SETTINGS_FILE, "utf-8");
12
+ const data = JSON.parse(raw);
13
+ if (!data || typeof data !== "object")
14
+ return {};
15
+ return data;
16
+ }
17
+ catch {
18
+ return {};
19
+ }
20
+ }
21
+ export function writeSettings(next) {
22
+ ensureCredentialDir(GD_DIR);
23
+ fs.writeFileSync(SETTINGS_FILE, `${JSON.stringify(next, null, 2)}\n`, {
24
+ encoding: "utf-8",
25
+ mode: 0o600,
26
+ });
27
+ ensureCredentialFile(SETTINGS_FILE);
28
+ }
29
+ export function patchSettings(patch) {
30
+ const cur = readSettings();
31
+ const merged = { ...cur, ...patch };
32
+ writeSettings(merged);
33
+ return merged;
34
+ }
35
+ /** 清除端点相关字段,恢复为默认 prod(在无环境变量覆盖时) */
36
+ export function clearEndpointSettings() {
37
+ const cur = readSettings();
38
+ delete cur.use_local_server;
39
+ delete cur.api_base;
40
+ delete cur.active_env;
41
+ delete cur.overrides;
42
+ delete cur.direct_auth;
43
+ const keys = Object.keys(cur);
44
+ if (keys.length === 0) {
45
+ if (fs.existsSync(SETTINGS_FILE))
46
+ fs.unlinkSync(SETTINGS_FILE);
47
+ return;
48
+ }
49
+ writeSettings(cur);
50
+ }
@@ -0,0 +1 @@
1
+ export declare function sleep(ms: number): Promise<void>;
@@ -0,0 +1,3 @@
1
+ export function sleep(ms) {
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
+ }
@@ -0,0 +1,2 @@
1
+ import { type Ora } from "ora";
2
+ export declare function createSpinner(text: string): Ora;
@@ -0,0 +1,4 @@
1
+ import ora from "ora";
2
+ export function createSpinner(text) {
3
+ return ora({ text, stream: process.stderr });
4
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function setStaticHelp(command: Command, text: string): void;
@@ -0,0 +1,3 @@
1
+ export function setStaticHelp(command, text) {
2
+ command.helpInformation = () => `${text.trimEnd()}\n`;
3
+ }
@@ -0,0 +1,10 @@
1
+ import type { ToolSpec } from "../registry/types.js";
2
+ /**
3
+ * 对用户可见的工具名称(中文说明),用于 spinner / 结果区,避免只显示 generate 等内部 id。
4
+ */
5
+ export declare function toolActivityLabel(toolId: string, spec?: Pick<ToolSpec, "display_name"> | null): string;
6
+ export declare function appActivityLabel(appId: string, displayName?: string | null): string;
7
+ /** 从 Registry 解析工具展示名(失败时用本地兜底) */
8
+ export declare function resolveToolActivityLabel(toolId: string, _client?: unknown): Promise<string>;
9
+ /** 从 Registry 解析应用展示名 */
10
+ export declare function resolveAppActivityLabel(appId: string, _client?: unknown): Promise<string>;
@@ -0,0 +1,55 @@
1
+ import { resolveAppSpec, resolveToolSpec } from "../registry/direct-registry.js";
2
+ /** 无 Registry 时的兜底(与 Server 种子 display_name 对齐) */
3
+ const TOOL_DISPLAY_FALLBACK = {
4
+ generate: "AI 生图",
5
+ cutout: "智能抠图",
6
+ edit: "AI 编辑",
7
+ expand: "AI 扩图",
8
+ erase: "AI 消除",
9
+ upscale: "高清放大",
10
+ vectorize: "转矢量图",
11
+ chat: "AI 对话",
12
+ upload: "上传素材",
13
+ prompt: "提示词优化",
14
+ title: "智能标题",
15
+ video: "AI 视频",
16
+ "video-hd": "视频超清",
17
+ };
18
+ /**
19
+ * 对用户可见的工具名称(中文说明),用于 spinner / 结果区,避免只显示 generate 等内部 id。
20
+ */
21
+ export function toolActivityLabel(toolId, spec) {
22
+ const fromSpec = spec?.display_name?.trim();
23
+ if (fromSpec)
24
+ return fromSpec;
25
+ const fb = TOOL_DISPLAY_FALLBACK[toolId];
26
+ if (fb)
27
+ return fb;
28
+ return `工具「${toolId}」`;
29
+ }
30
+ /** App 对用户展示的名称(与 registry / 种子对齐) */
31
+ const APP_DISPLAY_FALLBACK = {
32
+ "listing-gallery": "商品图库",
33
+ "pdp-a-plus": "详情页模块",
34
+ "social-seeding-pack": "小红书种草",
35
+ "campaign-kv-pack": "Campaign KV",
36
+ };
37
+ export function appActivityLabel(appId, displayName) {
38
+ const d = displayName?.trim();
39
+ if (d)
40
+ return d;
41
+ const fb = APP_DISPLAY_FALLBACK[appId];
42
+ if (fb)
43
+ return fb;
44
+ return `应用「${appId}」`;
45
+ }
46
+ /** 从 Registry 解析工具展示名(失败时用本地兜底) */
47
+ export async function resolveToolActivityLabel(toolId, _client) {
48
+ const spec = resolveToolSpec(toolId);
49
+ return toolActivityLabel(toolId, spec ?? undefined);
50
+ }
51
+ /** 从 Registry 解析应用展示名 */
52
+ export async function resolveAppActivityLabel(appId, _client) {
53
+ const app = resolveAppSpec(appId);
54
+ return appActivityLabel(appId, app?.name);
55
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 判断是否为可重试的瞬时网络错误(对端重置、拒绝连接、超时等)。
3
+ */
4
+ export declare function isTransientNetworkError(err: unknown): boolean;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 判断是否为可重试的瞬时网络错误(对端重置、拒绝连接、超时等)。
3
+ */
4
+ export function isTransientNetworkError(err) {
5
+ const codes = new Set();
6
+ const walk = (e) => {
7
+ if (!e || typeof e !== "object")
8
+ return;
9
+ const x = e;
10
+ if (typeof x.code === "string")
11
+ codes.add(x.code);
12
+ if (x.cause)
13
+ walk(x.cause);
14
+ if (typeof AggregateError !== "undefined" && e instanceof AggregateError) {
15
+ for (const sub of e.errors)
16
+ walk(sub);
17
+ }
18
+ };
19
+ walk(err);
20
+ const transient = new Set([
21
+ "ECONNRESET",
22
+ "ECONNREFUSED",
23
+ "ETIMEDOUT",
24
+ "EPIPE",
25
+ "ENOTFOUND",
26
+ "EAI_AGAIN",
27
+ "UND_ERR_SOCKET",
28
+ "UND_ERR_CONNECT_TIMEOUT",
29
+ ]);
30
+ for (const c of codes) {
31
+ if (transient.has(c))
32
+ return true;
33
+ }
34
+ const msg = err instanceof Error ? err.message : String(err);
35
+ if (/ECONNRESET|ECONNREFUSED|ETIMEDOUT|fetch failed|socket|HTTP status code 503|grpc-status=14|UNAVAILABLE|Service Unavailable/i
36
+ .test(msg)) {
37
+ return true;
38
+ }
39
+ return false;
40
+ }
@@ -0,0 +1,6 @@
1
+ export interface SafeUrlOptions {
2
+ context?: string;
3
+ allowPrivate?: boolean;
4
+ }
5
+ export declare function assertSafeHttpUrl(raw: string, options?: SafeUrlOptions): string;
6
+ export declare function isBlockedHost(hostname: string): boolean;
@@ -0,0 +1,67 @@
1
+ import net from "node:net";
2
+ const METADATA_IPV4 = "169.254.169.254";
3
+ export function assertSafeHttpUrl(raw, options = {}) {
4
+ const context = options.context ?? "URL";
5
+ let parsed;
6
+ try {
7
+ parsed = new URL(raw);
8
+ }
9
+ catch {
10
+ throw new Error(`${context} 必须是合法 URL`);
11
+ }
12
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
13
+ throw new Error(`${context} 仅支持 http/https URL`);
14
+ }
15
+ if (parsed.username || parsed.password) {
16
+ throw new Error(`${context} 不能包含用户名或密码`);
17
+ }
18
+ if (!allowPrivate(options) && isBlockedHost(parsed.hostname)) {
19
+ throw new Error(`${context} 不能指向 localhost、内网、link-local 或云元数据地址`);
20
+ }
21
+ return parsed.toString();
22
+ }
23
+ export function isBlockedHost(hostname) {
24
+ const host = hostname.toLowerCase().replace(/^\[|\]$/g, "");
25
+ if (host === "localhost" ||
26
+ host.endsWith(".localhost") ||
27
+ host.endsWith(".local") ||
28
+ host === "metadata" ||
29
+ host === "metadata.google.internal") {
30
+ return true;
31
+ }
32
+ if (host === METADATA_IPV4)
33
+ return true;
34
+ const ipType = net.isIP(host);
35
+ if (ipType === 4)
36
+ return isPrivateIpv4(host);
37
+ if (ipType === 6)
38
+ return isPrivateIpv6(host);
39
+ return false;
40
+ }
41
+ function allowPrivate(options) {
42
+ return options.allowPrivate === true || process.env.GD_CLI_ALLOW_PRIVATE_URLS === "1";
43
+ }
44
+ function isPrivateIpv4(host) {
45
+ const parts = host.split(".").map((part) => Number(part));
46
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
47
+ return true;
48
+ }
49
+ const [a, b] = parts;
50
+ return (a === 0 ||
51
+ a === 10 ||
52
+ a === 127 ||
53
+ (a === 169 && b === 254) ||
54
+ (a === 172 && b >= 16 && b <= 31) ||
55
+ (a === 192 && b === 168));
56
+ }
57
+ function isPrivateIpv6(host) {
58
+ return (host === "::" ||
59
+ host === "::1" ||
60
+ host.startsWith("fc") ||
61
+ host.startsWith("fd") ||
62
+ host.startsWith("fe80:") ||
63
+ host.startsWith("::ffff:127.") ||
64
+ host.startsWith("::ffff:10.") ||
65
+ host.startsWith("::ffff:192.168.") ||
66
+ host.startsWith("::ffff:169.254."));
67
+ }
@@ -0,0 +1,18 @@
1
+ export interface WriteAppDeliverablesOptions {
2
+ appId: string;
3
+ taskId: string;
4
+ outputs: unknown;
5
+ /** 根目录,其下会建子目录并生成 zip */
6
+ outputDir: string;
7
+ /** 人类可读应用名,用于 README */
8
+ activityLabel?: string;
9
+ }
10
+ export interface WriteAppDeliverablesResult {
11
+ dir: string;
12
+ zipPath: string;
13
+ readmePath: string;
14
+ }
15
+ /**
16
+ * 将 App 完成后的 outputs 落盘:每步一个 Markdown、图片下载到 images/、打 zip。
17
+ */
18
+ export declare function writeAppDeliverables(opts: WriteAppDeliverablesOptions): Promise<WriteAppDeliverablesResult>;