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,146 @@
1
+ export const ENTITLEMENT_PRICING_URL = "https://www.gaoding.com/pricing";
2
+ const ENTITLEMENT_CODE_PATTERNS = [
3
+ /QUOTA/i,
4
+ /CREDIT/i,
5
+ /BALANCE/i,
6
+ /ENTITLEMENT/i,
7
+ /POINT/i,
8
+ /BEAN/i,
9
+ /COIN/i,
10
+ /INSUFFICIENT/i,
11
+ /NO_ENOUGH/i,
12
+ /NOT_ENOUGH/i,
13
+ ];
14
+ const ENTITLEMENT_TEXT_PATTERNS = [
15
+ /(点数|稿豆).*(不足|不够|耗尽|用完)/,
16
+ /稿豆.*(不足|不够|耗尽|用完)/,
17
+ /权益.*(不足|不够|耗尽|用完|无法|不能)/,
18
+ /额度.*(不足|不够|耗尽|用完)/,
19
+ /余额.*(不足|不够)/,
20
+ /次数.*(不足|不够|耗尽|用完)/,
21
+ /配额.*(不足|不够|耗尽|用完)/,
22
+ /套餐.*(不足|不够|过期|升级)/,
23
+ /insufficient/i,
24
+ /not enough/i,
25
+ /quota/i,
26
+ /credit/i,
27
+ /balance/i,
28
+ /entitlement/i,
29
+ ];
30
+ export function detectEntitlementIssue(input) {
31
+ const haystack = [
32
+ input.code,
33
+ input.errorName,
34
+ input.message,
35
+ collectStrings(input.detail).join(" "),
36
+ ].filter(Boolean).join(" ");
37
+ if (!haystack)
38
+ return undefined;
39
+ const hasCodeSignal = [input.code, input.errorName]
40
+ .filter(Boolean)
41
+ .some((value) => ENTITLEMENT_CODE_PATTERNS.some((pattern) => pattern.test(String(value))));
42
+ const hasTextSignal = ENTITLEMENT_TEXT_PATTERNS.some((pattern) => pattern.test(haystack));
43
+ if (!hasCodeSignal && !hasTextSignal)
44
+ return undefined;
45
+ const pricingUrl = resolveEntitlementPricingUrl();
46
+ return {
47
+ code: "ENTITLEMENT_INSUFFICIENT",
48
+ message: normalizeEntitlementMessage(input.message),
49
+ hint: `当前账号或组织权益不足,无法继续执行。请到权益页充值/升级后重试:${pricingUrl}`,
50
+ pricing_url: pricingUrl,
51
+ diagnostic_context: collectDiagnosticContext(input.detail),
52
+ };
53
+ }
54
+ export function resolveEntitlementPricingUrl() {
55
+ const override = process.env.GD_ENTITLEMENT_PRICING_URL ??
56
+ process.env.GAODING_ENTITLEMENT_PRICING_URL;
57
+ if (override?.trim())
58
+ return override.trim();
59
+ return ENTITLEMENT_PRICING_URL;
60
+ }
61
+ export function entitlementCliError(issue, detail) {
62
+ return {
63
+ code: issue.code,
64
+ type: "business",
65
+ message: issue.message,
66
+ hint: issue.hint,
67
+ retryable: false,
68
+ pricing_url: issue.pricing_url,
69
+ diagnostic_context: issue.diagnostic_context,
70
+ user_action_required: "purchase_or_switch_entitled_org",
71
+ allow_org_check: true,
72
+ forbidden_actions: [
73
+ "org.switch_without_user_choice",
74
+ "model.switch_for_entitlement",
75
+ "env.switch_for_entitlement",
76
+ "blind_retry",
77
+ ],
78
+ next_steps: [
79
+ `到权益页充值/升级:${issue.pricing_url}`,
80
+ "或切到一个有权益的团队后,重试原命令",
81
+ "如用户明确要求查看团队,再执行 gd-cli org list -f json",
82
+ ],
83
+ agent_message_template: `当前组织权益不足,生成没有继续执行。\n你可以到权益页充值/升级:<${issue.pricing_url}>\n` +
84
+ "也可以切到一个有权益的团队后让我继续处理。",
85
+ detail,
86
+ };
87
+ }
88
+ function normalizeEntitlementMessage(message) {
89
+ const trimmed = message?.trim();
90
+ if (!trimmed)
91
+ return "权益不足,无法继续执行";
92
+ if (/权益不足|点数不足|稿豆不足|额度不足|余额不足/.test(trimmed)) {
93
+ return trimmed;
94
+ }
95
+ return `权益不足,无法继续执行:${trimmed}`;
96
+ }
97
+ function collectStrings(value, out = [], depth = 0) {
98
+ if (value === null || value === undefined || depth > 5)
99
+ return out;
100
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
101
+ out.push(String(value));
102
+ return out;
103
+ }
104
+ if (Array.isArray(value)) {
105
+ for (const item of value)
106
+ collectStrings(item, out, depth + 1);
107
+ return out;
108
+ }
109
+ if (typeof value === "object") {
110
+ for (const item of Object.values(value)) {
111
+ collectStrings(item, out, depth + 1);
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+ function collectDiagnosticContext(value, depth = 0) {
117
+ if (!value || typeof value !== "object" || depth > 5)
118
+ return undefined;
119
+ const record = value;
120
+ const direct = record.diagnostic_context ?? record.diagnosticContext;
121
+ const normalized = normalizeDiagnosticContext(direct);
122
+ if (normalized)
123
+ return normalized;
124
+ for (const item of Object.values(record)) {
125
+ const nested = collectDiagnosticContext(item, depth + 1);
126
+ if (nested)
127
+ return nested;
128
+ }
129
+ return undefined;
130
+ }
131
+ function normalizeDiagnosticContext(value) {
132
+ if (!value || typeof value !== "object" || Array.isArray(value))
133
+ return undefined;
134
+ const out = {};
135
+ for (const [key, item] of Object.entries(value)) {
136
+ if (item === undefined || item === null || item === "")
137
+ continue;
138
+ if (typeof item === "string" ||
139
+ typeof item === "number" ||
140
+ typeof item === "boolean" ||
141
+ Array.isArray(item)) {
142
+ out[key] = item;
143
+ }
144
+ }
145
+ return Object.keys(out).length > 0 ? out : undefined;
146
+ }
@@ -0,0 +1,7 @@
1
+ export declare const DEFAULT_GD_DIR: string;
2
+ export declare function resolveGdHome(): string;
3
+ export declare function hasCustomGdHome(): boolean;
4
+ export declare const GD_DIR: string;
5
+ export declare const CREDENTIALS_FILE: string;
6
+ export declare const REGISTRY_CACHE_FILE: string;
7
+ export declare const SETTINGS_FILE: string;
@@ -0,0 +1,26 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ export const DEFAULT_GD_DIR = path.join(os.homedir(), ".gd");
4
+ export function resolveGdHome() {
5
+ const configured = readGdHomeEnv();
6
+ if (!configured?.trim())
7
+ return DEFAULT_GD_DIR;
8
+ return path.resolve(expandHome(configured.trim()));
9
+ }
10
+ export function hasCustomGdHome() {
11
+ return Boolean(readGdHomeEnv()?.trim());
12
+ }
13
+ export const GD_DIR = resolveGdHome();
14
+ export const CREDENTIALS_FILE = path.join(GD_DIR, "credentials.json");
15
+ export const REGISTRY_CACHE_FILE = path.join(GD_DIR, "registry-cache.json");
16
+ export const SETTINGS_FILE = path.join(GD_DIR, "settings.json");
17
+ function readGdHomeEnv() {
18
+ return process.env.GD_CLI_HOME ?? process.env.GAODING_CLI_HOME;
19
+ }
20
+ function expandHome(value) {
21
+ if (value === "~")
22
+ return os.homedir();
23
+ if (value.startsWith("~/"))
24
+ return path.join(os.homedir(), value.slice(2));
25
+ return value;
26
+ }
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * 在命令 `-h` 底部追加一段「快速验证」单行示例(用于具体 Tool / Workflow 子命令)。
4
+ */
5
+ export declare function appendVerifySnippet(cmd: Command, line: string): void;
6
+ /** 按工具 id 附加对应示例;无映射则不加(避免误导) */
7
+ export declare function appendToolQuickExample(cmd: Command, toolName: string): void;
8
+ /** `gd-cli workflows run -h` 专用 */
9
+ export declare function appendAppRunQuickExample(cmd: Command, _legacyPrefix?: string): void;
@@ -0,0 +1,41 @@
1
+ /** 各工具一条可复制命令(与 registry 中 tool id 一致) */
2
+ const TOOL_QUICK_LINES = {
3
+ "asset.upload": "gd-cli tools call upload --file ./demo.jpg -f json",
4
+ upload: "gd-cli tools call upload --file ./demo.jpg -f json",
5
+ chat: 'gd-cli tools call chat --prompt "运动品牌主图要点" -f json',
6
+ cutout: "gd-cli tools call cutout --image https://www.gaoding.art/favicon.ico -f json",
7
+ title: 'gd-cli tools call title --prompt "北欧实木餐桌" -f json',
8
+ prompt: 'gd-cli tools call prompt --prompt "一只猫" -f json',
9
+ generate: 'gd-cli tools call generate --prompt "产品白底图,极简" --ratio 1:1 -f json',
10
+ edit: "gd-cli tools call edit --image https://www.gaoding.art/favicon.ico --style <风格> -f json",
11
+ expand: "gd-cli tools call expand --image https://www.gaoding.art/favicon.ico --width 1920 --height 1080 -f json",
12
+ erase: "gd-cli tools call erase --image https://www.gaoding.art/favicon.ico --mask https://example.com/mask.png -f json",
13
+ upscale: "gd-cli tools call upscale --image https://www.gaoding.art/favicon.ico -f json",
14
+ vectorize: "gd-cli tools call vectorize --image https://www.gaoding.art/favicon.ico -f json",
15
+ video: 'gd-cli tools call video --prompt "运动品牌短片" -f json',
16
+ "video-hd": "gd-cli tools call video-hd --file https://example.com/video.mp4 -f json",
17
+ };
18
+ const APP_RUN_QUICK_LINE = 'gd-cli workflows run listing-gallery --image ./product.jpg --brief "运动鞋" -f json';
19
+ /**
20
+ * 在命令 `-h` 底部追加一段「快速验证」单行示例(用于具体 Tool / Workflow 子命令)。
21
+ */
22
+ export function appendVerifySnippet(cmd, line) {
23
+ const text = `
24
+ Cases:
25
+
26
+ ${line.trim()}
27
+ `;
28
+ cmd.addHelpText("after", text);
29
+ }
30
+ /** 按工具 id 附加对应示例;无映射则不加(避免误导) */
31
+ export function appendToolQuickExample(cmd, toolName) {
32
+ const line = TOOL_QUICK_LINES[toolName];
33
+ if (!line)
34
+ return;
35
+ appendVerifySnippet(cmd, line);
36
+ }
37
+ /** `gd-cli workflows run -h` 专用 */
38
+ export function appendAppRunQuickExample(cmd, _legacyPrefix) {
39
+ void _legacyPrefix;
40
+ appendVerifySnippet(cmd, APP_RUN_QUICK_LINE);
41
+ }
@@ -0,0 +1,3 @@
1
+ export declare function readJsonInput(ref: string): Promise<Record<string, unknown>>;
2
+ export declare function applySetEntries(target: Record<string, unknown>, entries: unknown): void;
3
+ export declare function collectArg(value: string, previous?: string[]): string[];
@@ -0,0 +1,61 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ export async function readJsonInput(ref) {
4
+ const raw = ref === "-" ? await readStdin() : await readFile(resolve(process.cwd(), ref), "utf8");
5
+ const trimmed = raw.trim();
6
+ if (!trimmed)
7
+ return {};
8
+ const parsed = JSON.parse(trimmed);
9
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
10
+ throw new Error("JSON 输入必须是 object");
11
+ }
12
+ return parsed;
13
+ }
14
+ export function applySetEntries(target, entries) {
15
+ if (!Array.isArray(entries))
16
+ return;
17
+ for (const entry of entries) {
18
+ const raw = String(entry);
19
+ const index = raw.indexOf("=");
20
+ if (index <= 0) {
21
+ throw new Error(`--set 必须是 key=value: ${raw}`);
22
+ }
23
+ const key = raw.slice(0, index).trim();
24
+ const value = raw.slice(index + 1);
25
+ if (!key)
26
+ throw new Error(`--set 缺少 key: ${raw}`);
27
+ target[key] = parseScalar(value);
28
+ }
29
+ }
30
+ export function collectArg(value, previous = []) {
31
+ previous.push(value);
32
+ return previous;
33
+ }
34
+ async function readStdin() {
35
+ const chunks = [];
36
+ for await (const chunk of process.stdin) {
37
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
38
+ }
39
+ return Buffer.concat(chunks).toString("utf8");
40
+ }
41
+ function parseScalar(value) {
42
+ if (value === "true")
43
+ return true;
44
+ if (value === "false")
45
+ return false;
46
+ if (value === "null")
47
+ return null;
48
+ if (/^-?\d+(\.\d+)?$/.test(value))
49
+ return Number(value);
50
+ const trimmed = value.trim();
51
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
52
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
53
+ try {
54
+ return JSON.parse(trimmed);
55
+ }
56
+ catch {
57
+ return value;
58
+ }
59
+ }
60
+ return value;
61
+ }
@@ -0,0 +1,10 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * 列表类命令:未显式指定 -f/--format 时,TTY 用 table,管道/非 TTY 用 json。
4
+ * 显式传入 -f json|text|table 时始终尊重用户选择。
5
+ */
6
+ export declare function resolveListOutputFormat(subcommand: Command): string;
7
+ /**
8
+ * 工具调用 / 应用运行 / 任务状态:未显式 -f 时,TTY 用 human(交付物可读),非 TTY 用 json。
9
+ */
10
+ export declare function resolveInvokeOutputFormat(subcommand: Command): string;
@@ -0,0 +1,34 @@
1
+ function getRoot(subcommand) {
2
+ return subcommand.parent?.parent ?? subcommand.parent;
3
+ }
4
+ function isImplicitFormat(root) {
5
+ const source = root?.getOptionValueSource("format");
6
+ return (source === undefined || source === "default" || source === "implied");
7
+ }
8
+ /**
9
+ * 列表类命令:未显式指定 -f/--format 时,TTY 用 table,管道/非 TTY 用 json。
10
+ * 显式传入 -f json|text|table 时始终尊重用户选择。
11
+ */
12
+ export function resolveListOutputFormat(subcommand) {
13
+ const root = getRoot(subcommand);
14
+ const opts = (root?.opts() ?? {});
15
+ if (opts.json)
16
+ return "json";
17
+ const format = opts.format ?? "json";
18
+ if (!isImplicitFormat(root))
19
+ return format;
20
+ return process.stdout.isTTY ? "table" : "json";
21
+ }
22
+ /**
23
+ * 工具调用 / 应用运行 / 任务状态:未显式 -f 时,TTY 用 human(交付物可读),非 TTY 用 json。
24
+ */
25
+ export function resolveInvokeOutputFormat(subcommand) {
26
+ const root = getRoot(subcommand);
27
+ const opts = (root?.opts() ?? {});
28
+ if (opts.json)
29
+ return "json";
30
+ const format = opts.format ?? "json";
31
+ if (!isImplicitFormat(root))
32
+ return format;
33
+ return process.stdout.isTTY ? "human" : "json";
34
+ }
@@ -0,0 +1,6 @@
1
+ export declare const logger: {
2
+ info(msg: string): void;
3
+ debug(msg: string): void;
4
+ warn(msg: string): void;
5
+ error(msg: string): void;
6
+ };
@@ -0,0 +1,19 @@
1
+ import { isVerbose, isDebug } from "./config.js";
2
+ export const logger = {
3
+ info(msg) {
4
+ if (isVerbose()) {
5
+ process.stderr.write(`[info] ${msg}\n`);
6
+ }
7
+ },
8
+ debug(msg) {
9
+ if (isDebug()) {
10
+ process.stderr.write(`[debug] ${msg}\n`);
11
+ }
12
+ },
13
+ warn(msg) {
14
+ process.stderr.write(`[warn] ${msg}\n`);
15
+ },
16
+ error(msg) {
17
+ process.stderr.write(`[error] ${msg}\n`);
18
+ },
19
+ };
@@ -0,0 +1,10 @@
1
+ export interface ComposeMattingOptions {
2
+ /** 裁掉透明边,对齐前端 trim */
3
+ trim?: boolean;
4
+ }
5
+ /**
6
+ * 原图 + mask → 透明 PNG。
7
+ * mask 亮度作为 alpha(对齐前端 canvas destination-in)。
8
+ */
9
+ export declare function composeMattingImage(sourceBuffer: Buffer, maskBuffer: Buffer, options?: ComposeMattingOptions): Promise<Buffer>;
10
+ export declare function fetchImageBuffer(url: string): Promise<Buffer>;
@@ -0,0 +1,45 @@
1
+ import sharp from "sharp";
2
+ import { redactSensitiveText } from "./redact.js";
3
+ import { assertSafeHttpUrl } from "./url-safety.js";
4
+ /**
5
+ * 原图 + mask → 透明 PNG。
6
+ * mask 亮度作为 alpha(对齐前端 canvas destination-in)。
7
+ */
8
+ export async function composeMattingImage(sourceBuffer, maskBuffer, options = {}) {
9
+ const { trim = true } = options;
10
+ const sourceMeta = await sharp(sourceBuffer).metadata();
11
+ const width = sourceMeta.width ?? 0;
12
+ const height = sourceMeta.height ?? 0;
13
+ if (!width || !height) {
14
+ throw new Error("composeMattingImage: invalid source dimensions");
15
+ }
16
+ const { data: rgba, info } = await sharp(sourceBuffer)
17
+ .resize(width, height, { fit: "fill" })
18
+ .ensureAlpha()
19
+ .raw()
20
+ .toBuffer({ resolveWithObject: true });
21
+ const alpha = await sharp(maskBuffer)
22
+ .resize(width, height, { fit: "fill" })
23
+ .grayscale()
24
+ .raw()
25
+ .toBuffer();
26
+ const pixels = width * height;
27
+ for (let i = 0; i < pixels; i += 1) {
28
+ rgba[i * 4 + 3] = alpha[i] ?? 0;
29
+ }
30
+ let pipeline = sharp(rgba, {
31
+ raw: { width: info.width, height: info.height, channels: 4 },
32
+ });
33
+ if (trim) {
34
+ pipeline = pipeline.trim();
35
+ }
36
+ return pipeline.png().toBuffer();
37
+ }
38
+ export async function fetchImageBuffer(url) {
39
+ const safeUrl = assertSafeHttpUrl(url, { context: "图片 URL" });
40
+ const response = await fetch(safeUrl);
41
+ if (!response.ok) {
42
+ throw new Error(redactSensitiveText(`下载图片失败 [${response.status}]: ${safeUrl}`));
43
+ }
44
+ return Buffer.from(await response.arrayBuffer());
45
+ }
@@ -0,0 +1 @@
1
+ export declare function openBrowser(url: string): Promise<void>;
@@ -0,0 +1,4 @@
1
+ import open from "open";
2
+ export async function openBrowser(url) {
3
+ await open(url);
4
+ }
@@ -0,0 +1,5 @@
1
+ export declare function resolveSafeOutputDir(outputDir: string): string;
2
+ export declare function createUniqueOutputDir(root: string, preferredName: string): Promise<{
3
+ dir: string;
4
+ name: string;
5
+ }>;
@@ -0,0 +1,70 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const PROTECTED_SEGMENTS = new Set([
5
+ ".aws",
6
+ ".azure",
7
+ ".docker",
8
+ ".gd",
9
+ ".gd-cli-dev",
10
+ ".git",
11
+ ".gnupg",
12
+ ".kube",
13
+ ".ssh",
14
+ ]);
15
+ export function resolveSafeOutputDir(outputDir) {
16
+ const resolved = path.resolve(process.cwd(), outputDir);
17
+ const reason = unsafeOutputDirReason(resolved);
18
+ if (reason) {
19
+ throw new Error(`输出目录不安全:${resolved}(${reason})`);
20
+ }
21
+ return resolved;
22
+ }
23
+ export async function createUniqueOutputDir(root, preferredName) {
24
+ for (let i = 0; i < 100; i += 1) {
25
+ const name = i === 0 ? preferredName : `${preferredName}-${i + 1}`;
26
+ const dir = path.join(root, name);
27
+ try {
28
+ await mkdir(dir, { recursive: false });
29
+ return { dir, name };
30
+ }
31
+ catch (error) {
32
+ if (!isAlreadyExists(error))
33
+ throw error;
34
+ }
35
+ }
36
+ throw new Error(`无法创建唯一输出目录:${path.join(root, preferredName)}`);
37
+ }
38
+ function unsafeOutputDirReason(resolved) {
39
+ const root = path.parse(resolved).root;
40
+ if (resolved === root)
41
+ return "不能写入文件系统根目录";
42
+ if (resolved === os.homedir())
43
+ return "不能直接写入家目录根";
44
+ const lowerSegments = resolved.split(path.sep).filter(Boolean).map((segment) => segment.toLowerCase());
45
+ const protectedSegment = lowerSegments.find((segment) => PROTECTED_SEGMENTS.has(segment));
46
+ if (protectedSegment)
47
+ return `包含受保护目录 ${protectedSegment}`;
48
+ const protectedRoots = [
49
+ "/bin",
50
+ "/etc",
51
+ "/sbin",
52
+ "/usr/bin",
53
+ "/usr/sbin",
54
+ path.join(os.homedir(), ".gd"),
55
+ path.join(os.homedir(), ".ssh"),
56
+ ];
57
+ for (const protectedRoot of protectedRoots) {
58
+ if (isSameOrChild(resolved, protectedRoot)) {
59
+ return `位于受保护目录 ${protectedRoot}`;
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ function isSameOrChild(target, parent) {
65
+ const relative = path.relative(parent, target);
66
+ return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative));
67
+ }
68
+ function isAlreadyExists(error) {
69
+ return !!error && typeof error === "object" && error.code === "EEXIST";
70
+ }
@@ -0,0 +1,3 @@
1
+ export declare function isSensitiveKey(key: string): boolean;
2
+ export declare function redactSensitiveText(value: string): string;
3
+ export declare function redactSensitiveValue<T>(value: T): T;
@@ -0,0 +1,96 @@
1
+ const REDACTED = "[REDACTED]";
2
+ const REDACTED_URL = "[REDACTED_INTERNAL_URL]";
3
+ const SENSITIVE_KEYS = new Set([
4
+ "ak",
5
+ "sk",
6
+ "token",
7
+ "access_token",
8
+ "refresh_token",
9
+ "upload_token",
10
+ "security_token",
11
+ "cookie",
12
+ "authorization",
13
+ "auth_key",
14
+ "signature",
15
+ "x-accesskey",
16
+ "x-signature",
17
+ "authorize_code",
18
+ "authorization_code",
19
+ "device_code",
20
+ "access_key_id",
21
+ "access_key_secret",
22
+ "secret",
23
+ "password",
24
+ ]);
25
+ export function isSensitiveKey(key) {
26
+ return SENSITIVE_KEYS.has(normalizeKey(key));
27
+ }
28
+ export function redactSensitiveText(value) {
29
+ let out = value;
30
+ out = out.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, `Bearer ${REDACTED}`);
31
+ out = out.replace(/\b(Authorization|Cookie|X-AccessKey|X-Signature)\s*[:=]\s*([^\r\n,;]+)/gi, (_m, key) => `${key}: ${REDACTED}`);
32
+ out = out.replace(/([?&](?:ak|sk|auth_key|token|access_token|refresh_token|upload_token|security_token|signature|authorize_code|authorization_code|device_code|access_key_id|access_key_secret)=)[^&\s"'<>]+/gi, `$1${REDACTED}`);
33
+ out = out.replace(/(["']?)(ak|sk|auth_key|token|access_token|refresh_token|upload_token|security_token|cookie|authorization|signature|x-accesskey|x-signature|authorize_code|authorization_code|device_code|access_key_id|access_key_secret|secret|password)\1\s*[:=]\s*(["']?)[^"',\s}]+(\3)/gi, (_m, quote, key, valueQuote, endQuote) => `${quote}${key}${quote}: ${valueQuote}${REDACTED}${endQuote}`);
34
+ out = out.replace(/\b(AKIA|LTAI)[A-Z0-9]{12,}\b/g, REDACTED);
35
+ out = out.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, (url) => {
36
+ if (isInternalUrl(url))
37
+ return REDACTED_URL;
38
+ return url;
39
+ });
40
+ return out;
41
+ }
42
+ export function redactSensitiveValue(value) {
43
+ return redactValue(value, new WeakSet());
44
+ }
45
+ function redactValue(value, seen) {
46
+ if (typeof value === "string")
47
+ return redactSensitiveText(value);
48
+ if (!value || typeof value !== "object")
49
+ return value;
50
+ if (seen.has(value))
51
+ return "[Circular]";
52
+ seen.add(value);
53
+ if (Array.isArray(value))
54
+ return value.map((item) => redactValue(item, seen));
55
+ const out = {};
56
+ for (const [key, item] of Object.entries(value)) {
57
+ out[key] = isSensitiveKey(key) ? REDACTED : redactValue(item, seen);
58
+ }
59
+ return out;
60
+ }
61
+ function normalizeKey(key) {
62
+ return key.trim().toLowerCase().replace(/-/g, "_");
63
+ }
64
+ function isInternalUrl(raw) {
65
+ try {
66
+ const parsed = new URL(raw);
67
+ const host = parsed.hostname.toLowerCase().replace(/^\[|\]$/g, "");
68
+ return isInternalHost(host);
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ function isInternalHost(host) {
75
+ if (host === "localhost" ||
76
+ host.endsWith(".localhost") ||
77
+ host === "metadata.google.internal" ||
78
+ host === "metadata" ||
79
+ host.endsWith(".local")) {
80
+ return true;
81
+ }
82
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
83
+ const [a = 0, b = 0] = host.split(".").map((part) => Number(part));
84
+ return (a === 0 ||
85
+ a === 10 ||
86
+ a === 127 ||
87
+ (a === 169 && b === 254) ||
88
+ (a === 172 && b >= 16 && b <= 31) ||
89
+ (a === 192 && b === 168));
90
+ }
91
+ return (host === "::1" ||
92
+ host === "::" ||
93
+ host.startsWith("fc") ||
94
+ host.startsWith("fd") ||
95
+ host.startsWith("fe80:"));
96
+ }
@@ -0,0 +1,8 @@
1
+ export interface ReferenceImageCheckOptions {
2
+ context: string;
3
+ imageRequired?: boolean;
4
+ }
5
+ export declare function isRemoteImageReference(value: string): boolean;
6
+ export declare function firstReferenceImage(value: unknown): string | undefined;
7
+ export declare function assertReferenceImageAvailable(value: unknown, options: ReferenceImageCheckOptions): string;
8
+ export declare function referenceImageRequiredMessage(context: string): string;