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,20 @@
1
+ import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ export declare class HubClient {
3
+ private baseUrl;
4
+ private storeV2;
5
+ constructor(baseUrl?: string, storeV2?: CredentialStoreV2);
6
+ get<T>(path: string, options?: {
7
+ auth?: boolean;
8
+ }): Promise<T>;
9
+ post<T>(path: string, body?: unknown, options?: {
10
+ auth?: boolean;
11
+ }): Promise<T>;
12
+ private request;
13
+ private attachAuthHeaders;
14
+ }
15
+ export declare class ApiError extends Error {
16
+ statusCode: number;
17
+ body: string;
18
+ path: string;
19
+ constructor(statusCode: number, body: string, path: string);
20
+ }
@@ -0,0 +1,99 @@
1
+ import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ import { resolveApiBase, CLI_USER_AGENT } from "../utils/config.js";
3
+ import { isCredentialsV2 } from "../core/auth/types.js";
4
+ import { signApiRequest } from "../core/auth/signature.js";
5
+ import { redactSensitiveText } from "../utils/redact.js";
6
+ function collectErrorCodes(err, out) {
7
+ if (!err || typeof err !== "object")
8
+ return;
9
+ const e = err;
10
+ if (typeof e.code === "string")
11
+ out.add(e.code);
12
+ if (e.cause)
13
+ collectErrorCodes(e.cause, out);
14
+ if (typeof AggregateError !== "undefined" && err instanceof AggregateError) {
15
+ for (const sub of err.errors)
16
+ collectErrorCodes(sub, out);
17
+ }
18
+ }
19
+ function fetchFailureHint(baseUrl, requestUrl) {
20
+ return [
21
+ `请求: ${redactSensitiveText(requestUrl)}`,
22
+ `当前 API 根: ${redactSensitiveText(baseUrl)}`,
23
+ "请检查网络连接和 GD_API_BASE 配置。",
24
+ "请先执行 gd-cli auth login 获取 AK/SK 凭证。",
25
+ "排查: GD_VERBOSE=1 gd-cli <command>",
26
+ ].join("\n");
27
+ }
28
+ export class HubClient {
29
+ baseUrl;
30
+ storeV2;
31
+ constructor(baseUrl, storeV2) {
32
+ this.baseUrl = baseUrl ?? resolveApiBase();
33
+ this.storeV2 = storeV2 ?? new CredentialStoreV2();
34
+ }
35
+ async get(path, options) {
36
+ return this.request("GET", path, undefined, options);
37
+ }
38
+ async post(path, body, options) {
39
+ return this.request("POST", path, body, options);
40
+ }
41
+ async request(method, path, body, options) {
42
+ const url = new URL(path.replace(/^\//, ""), `${this.baseUrl.replace(/\/$/, "")}/`);
43
+ const headers = {
44
+ "Content-Type": "application/json",
45
+ "User-Agent": CLI_USER_AGENT,
46
+ };
47
+ const needsAuth = options?.auth !== false;
48
+ if (needsAuth) {
49
+ await this.attachAuthHeaders(headers, method, path);
50
+ }
51
+ let response;
52
+ try {
53
+ response = await fetch(url, {
54
+ method,
55
+ headers,
56
+ body: body ? JSON.stringify(body) : undefined,
57
+ });
58
+ }
59
+ catch (err) {
60
+ const codes = new Set();
61
+ collectErrorCodes(err, codes);
62
+ const codeSuffix = codes.size > 0 ? ` [${[...codes].join(", ")}]` : "";
63
+ const base = err instanceof Error ? err.message : String(err);
64
+ throw new Error(`${redactSensitiveText(base)}${codeSuffix}\n\n${fetchFailureHint(this.baseUrl, url.toString())}`);
65
+ }
66
+ if (!response.ok) {
67
+ const errorBody = await response.text();
68
+ throw new ApiError(response.status, errorBody, path);
69
+ }
70
+ return response.json();
71
+ }
72
+ async attachAuthHeaders(headers, method, path) {
73
+ const v2 = this.storeV2.read();
74
+ if (v2 && isCredentialsV2(v2)) {
75
+ const url = new URL(path.replace(/^\//, ""), `${this.baseUrl.replace(/\/$/, "")}/`);
76
+ const { signature, timestamp } = signApiRequest(method, url.pathname, url.searchParams.toString(), v2.sk);
77
+ headers["X-AccessKey"] = v2.ak;
78
+ headers["X-Signature"] = signature;
79
+ headers["X-Timestamp"] = timestamp;
80
+ if (v2.current_org?.id) {
81
+ headers["X-Org-Id"] = v2.current_org.id;
82
+ }
83
+ return;
84
+ }
85
+ throw new Error("AK/SK 模式需要先登录并绑定组织:\n gd-cli auth login\n gd-cli org switch --org <org-id>");
86
+ }
87
+ }
88
+ export class ApiError extends Error {
89
+ statusCode;
90
+ body;
91
+ path;
92
+ constructor(statusCode, body, path) {
93
+ super(redactSensitiveText(`API 请求失败 [${statusCode}] ${path}: ${body}`));
94
+ this.statusCode = statusCode;
95
+ this.body = body;
96
+ this.path = path;
97
+ this.name = "ApiError";
98
+ }
99
+ }
@@ -0,0 +1,60 @@
1
+ export interface DeviceAuthorizationResult {
2
+ device_code: string;
3
+ authorize_code?: string;
4
+ verification_uri: string;
5
+ verification_uri_complete: string;
6
+ expires_in: number;
7
+ interval: number;
8
+ }
9
+ export interface DeviceTokenResult {
10
+ ak: string;
11
+ sk: string;
12
+ scope: string | string[];
13
+ expires_at: string;
14
+ device_code?: string;
15
+ agent_name?: string | null;
16
+ orgId?: string;
17
+ user?: {
18
+ id: string;
19
+ name: string;
20
+ };
21
+ }
22
+ export interface DeviceIdentityResult {
23
+ ak: string;
24
+ userId: string | number;
25
+ umsId?: string | number;
26
+ orgId?: string | number;
27
+ scope?: string;
28
+ provider?: string;
29
+ }
30
+ export type DeviceTokenPollState = {
31
+ status: "success";
32
+ token: DeviceTokenResult;
33
+ } | {
34
+ status: "pending";
35
+ } | {
36
+ status: "slow_down";
37
+ } | {
38
+ status: "failed";
39
+ code: string;
40
+ message: string;
41
+ };
42
+ export declare class SsoClient {
43
+ private readonly baseUrl;
44
+ constructor(baseUrl?: string);
45
+ deviceAuthorization(clientId?: string): Promise<DeviceAuthorizationResult>;
46
+ pollDeviceTokenOnce(deviceCode: string): Promise<DeviceTokenPollState>;
47
+ pollDeviceToken(deviceCode: string, intervalSec: number, expiresInSec: number, onPending?: () => void): Promise<DeviceTokenResult>;
48
+ getIdentity(ak: string): Promise<DeviceIdentityResult>;
49
+ bindOrg(ak: string, sk: string, orgId: string): Promise<{
50
+ code?: string;
51
+ message?: string;
52
+ bind_expires_at?: string;
53
+ }>;
54
+ }
55
+ export declare class SsoApiError extends Error {
56
+ statusCode: number;
57
+ body: string;
58
+ url: string;
59
+ constructor(statusCode: number, body: string, url: string);
60
+ }
@@ -0,0 +1,140 @@
1
+ import { resolveSsoBase, buildDeviceAuthPageUrls, GDHUB_CLI_CLIENT_ID, CLI_USER_AGENT, } from "../utils/config.js";
2
+ import { signBindOrg } from "../core/auth/signature.js";
3
+ import { sleep } from "../utils/sleep.js";
4
+ import { redactSensitiveText } from "../utils/redact.js";
5
+ export class SsoClient {
6
+ baseUrl;
7
+ constructor(baseUrl = resolveSsoBase()) {
8
+ this.baseUrl = baseUrl;
9
+ }
10
+ async deviceAuthorization(clientId = GDHUB_CLI_CLIENT_ID) {
11
+ const url = `${this.baseUrl}/oauth/device-authorization?clientId=${encodeURIComponent(clientId)}`;
12
+ const res = await fetch(url, {
13
+ method: "POST",
14
+ headers: { "User-Agent": CLI_USER_AGENT },
15
+ });
16
+ const text = await res.text();
17
+ if (!res.ok) {
18
+ throw new SsoApiError(res.status, text, url);
19
+ }
20
+ const raw = JSON.parse(text);
21
+ const code = raw.authorize_code?.trim();
22
+ if (!code) {
23
+ throw new SsoApiError(502, "device-authorization 响应缺少 authorize_code", url);
24
+ }
25
+ const pages = buildDeviceAuthPageUrls(code);
26
+ return {
27
+ ...raw,
28
+ authorize_code: code,
29
+ verification_uri: pages.verification_uri,
30
+ verification_uri_complete: pages.verification_uri_complete,
31
+ };
32
+ }
33
+ async pollDeviceTokenOnce(deviceCode) {
34
+ const url = `${this.baseUrl}/oauth/device/token?grantType=device_code&deviceCode=${encodeURIComponent(deviceCode)}`;
35
+ const res = await fetch(url, {
36
+ method: "POST",
37
+ headers: { "User-Agent": CLI_USER_AGENT },
38
+ });
39
+ const body = (await res.json());
40
+ const ak = body.ak;
41
+ const sk = body.sk;
42
+ if (ak && sk) {
43
+ return {
44
+ status: "success",
45
+ token: {
46
+ ak,
47
+ sk,
48
+ scope: body.scope ?? "scene-executor",
49
+ expires_at: String(body.expires_at),
50
+ device_code: body.device_code,
51
+ agent_name: body.agent_name,
52
+ orgId: cleanString(body.orgId ?? body.org_id ?? body.orgID),
53
+ },
54
+ };
55
+ }
56
+ const err = String(body.error ?? "");
57
+ const msg = String(body.error_description ?? body.message ?? err ?? "授权失败");
58
+ if (err === "authorization_pending")
59
+ return { status: "pending" };
60
+ if (err === "slow_down")
61
+ return { status: "slow_down" };
62
+ return { status: "failed", code: err || String(res.status), message: msg };
63
+ }
64
+ async pollDeviceToken(deviceCode, intervalSec, expiresInSec, onPending) {
65
+ const deadline = Date.now() + expiresInSec * 1000;
66
+ let intervalMs = Math.max(intervalSec, 2) * 1000;
67
+ while (Date.now() < deadline) {
68
+ const state = await this.pollDeviceTokenOnce(deviceCode);
69
+ if (state.status === "success")
70
+ return state.token;
71
+ if (state.status === "pending") {
72
+ onPending?.();
73
+ await sleep(intervalMs);
74
+ continue;
75
+ }
76
+ if (state.status === "slow_down") {
77
+ intervalMs = Math.min(intervalMs * 2, 30_000);
78
+ await sleep(intervalMs);
79
+ continue;
80
+ }
81
+ throw new Error(mapDeviceTokenError(state.code, state.message));
82
+ }
83
+ throw new Error("授权超时。请重新执行: gd-cli auth login(或检查是否已在浏览器完成授权)");
84
+ }
85
+ async getIdentity(ak) {
86
+ const url = `${this.baseUrl}/oauth/device/identity?ak=${encodeURIComponent(ak)}`;
87
+ const res = await fetch(url, { headers: { "User-Agent": CLI_USER_AGENT } });
88
+ const text = await res.text();
89
+ if (!res.ok) {
90
+ throw new SsoApiError(res.status, text, url);
91
+ }
92
+ return JSON.parse(text);
93
+ }
94
+ async bindOrg(ak, sk, orgId) {
95
+ const { signature, timestamp } = signBindOrg(ak, sk, orgId);
96
+ const q = new URLSearchParams({ ak, orgId, signature, timestamp });
97
+ const url = `${this.baseUrl}/oauth/device/bind-org?${q}`;
98
+ const res = await fetch(url, {
99
+ method: "POST",
100
+ headers: { "User-Agent": CLI_USER_AGENT },
101
+ });
102
+ const text = await res.text();
103
+ if (!res.ok) {
104
+ throw new SsoApiError(res.status, text, url);
105
+ }
106
+ try {
107
+ return JSON.parse(text);
108
+ }
109
+ catch {
110
+ return { message: text };
111
+ }
112
+ }
113
+ }
114
+ function cleanString(value) {
115
+ if (value === null || value === undefined)
116
+ return undefined;
117
+ const s = String(value).trim();
118
+ return s ? s : undefined;
119
+ }
120
+ export class SsoApiError extends Error {
121
+ statusCode;
122
+ body;
123
+ url;
124
+ constructor(statusCode, body, url) {
125
+ super(redactSensitiveText(`SSO 请求失败 [${statusCode}] ${url}: ${body}`));
126
+ this.statusCode = statusCode;
127
+ this.body = body;
128
+ this.url = url;
129
+ this.name = "SsoApiError";
130
+ }
131
+ }
132
+ function mapDeviceTokenError(code, message) {
133
+ const hints = {
134
+ expired: "授权码已过期,请重新执行 gd-cli auth login",
135
+ authorization_declined: "用户已拒绝授权",
136
+ invalid_token: "OAuth token 无效,请重新登录",
137
+ };
138
+ const hint = hints[code];
139
+ return hint ? `${hint}(${message})` : message || `授权失败: ${code}`;
140
+ }
@@ -0,0 +1,38 @@
1
+ import type { HubClient } from "./hub-client.js";
2
+ export interface TaskResult {
3
+ task_id: string;
4
+ status: string;
5
+ progress?: number;
6
+ outputs?: unknown;
7
+ message?: string;
8
+ error?: unknown;
9
+ /** `gd-cli workflows run` 成功落盘后:本地交付目录 */
10
+ deliverable_dir?: string;
11
+ /** 与 deliverable_dir 同级的 zip 路径 */
12
+ deliverable_zip?: string;
13
+ deliverable_readme?: string;
14
+ }
15
+ interface ProgressInfo {
16
+ current: number;
17
+ total: number;
18
+ step_name: string;
19
+ status: string;
20
+ progress: number;
21
+ }
22
+ interface PollOptions {
23
+ timeout?: number;
24
+ outputDir?: string;
25
+ onProgress?: (progress: ProgressInfo) => void;
26
+ }
27
+ export declare class TaskPoller {
28
+ private client;
29
+ constructor(client: HubClient);
30
+ waitForCompletion(taskId: string, options?: PollOptions): Promise<TaskResult>;
31
+ /** 轮询时偶发 ECONNRESET(Server 热重载、瞬时崩溃)时重试若干次 */
32
+ private getTaskStatusWithRetry;
33
+ /**
34
+ * 轮询间隔策略:前 1 次 2s,2-5 次 3s,之后 5s
35
+ */
36
+ private getInterval;
37
+ }
38
+ export {};
@@ -0,0 +1,72 @@
1
+ import { sleep } from "../utils/sleep.js";
2
+ import { isTransientNetworkError } from "../utils/transient-network-error.js";
3
+ export class TaskPoller {
4
+ client;
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ async waitForCompletion(taskId, options = {}) {
9
+ const timeout = (options.timeout ?? 600) * 1000;
10
+ const startTime = Date.now();
11
+ let pollCount = 0;
12
+ while (Date.now() - startTime < timeout) {
13
+ const result = await this.getTaskStatusWithRetry(taskId);
14
+ if (result.status === "completed") {
15
+ return {
16
+ task_id: result.task_id,
17
+ status: "completed",
18
+ outputs: result.outputs,
19
+ };
20
+ }
21
+ if (result.status === "failed") {
22
+ return {
23
+ task_id: result.task_id,
24
+ status: "failed",
25
+ error: result.error,
26
+ };
27
+ }
28
+ if (options.onProgress && result.current_step !== undefined) {
29
+ options.onProgress({
30
+ current: result.current_step,
31
+ total: result.total_steps ?? 0,
32
+ step_name: result.step_name ?? "",
33
+ status: result.status,
34
+ progress: result.progress ?? 0,
35
+ });
36
+ }
37
+ pollCount++;
38
+ const interval = this.getInterval(pollCount);
39
+ await sleep(interval);
40
+ }
41
+ throw new Error(`任务超时 (${options.timeout}s): ${taskId}`);
42
+ }
43
+ /** 轮询时偶发 ECONNRESET(Server 热重载、瞬时崩溃)时重试若干次 */
44
+ async getTaskStatusWithRetry(taskId) {
45
+ const maxAttempts = 8;
46
+ let lastErr;
47
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
48
+ try {
49
+ return await this.client.get(`/tasks/${taskId}`);
50
+ }
51
+ catch (err) {
52
+ lastErr = err;
53
+ if (!isTransientNetworkError(err) || attempt === maxAttempts - 1) {
54
+ throw err;
55
+ }
56
+ const backoff = Math.min(500 * (attempt + 1), 4000);
57
+ await sleep(backoff);
58
+ }
59
+ }
60
+ throw lastErr;
61
+ }
62
+ /**
63
+ * 轮询间隔策略:前 1 次 2s,2-5 次 3s,之后 5s
64
+ */
65
+ getInterval(pollCount) {
66
+ if (pollCount <= 1)
67
+ return 2000;
68
+ if (pollCount <= 5)
69
+ return 3000;
70
+ return 5000;
71
+ }
72
+ }
@@ -0,0 +1,12 @@
1
+ import { HubClient } from "./hub-client.js";
2
+ import type { TaskResult } from "./task-poller.js";
3
+ interface InvokeOptions {
4
+ async?: boolean;
5
+ timeout?: number;
6
+ }
7
+ export declare class ToolInvoker {
8
+ private direct;
9
+ constructor(client?: HubClient);
10
+ invoke(toolName: string, params: Record<string, unknown>, options?: InvokeOptions): Promise<TaskResult>;
11
+ }
12
+ export {};
@@ -0,0 +1,11 @@
1
+ import { DirectToolInvoker } from "./direct-tools.js";
2
+ export class ToolInvoker {
3
+ direct;
4
+ constructor(client) {
5
+ void client;
6
+ this.direct = new DirectToolInvoker();
7
+ }
8
+ async invoke(toolName, params, options = {}) {
9
+ return this.direct.invoke(toolName, params, options);
10
+ }
11
+ }
@@ -0,0 +1,39 @@
1
+ import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ declare const RIGHTS_OVERVIEW_ENDPOINT = "/trade/rights-overview";
3
+ declare const GAODOU = "GAODOU";
4
+ export interface UsageCredits {
5
+ unit: "gaodou";
6
+ goods_type_enum: string;
7
+ identifier: string;
8
+ total: number;
9
+ used: number;
10
+ wait: number;
11
+ surplus: number;
12
+ }
13
+ export interface UsageOverview {
14
+ status: "completed";
15
+ organization: {
16
+ id: string;
17
+ name: string | null;
18
+ };
19
+ credits: UsageCredits;
20
+ source: {
21
+ endpoint: typeof RIGHTS_OVERVIEW_ENDPOINT;
22
+ goods_type_enums: [typeof GAODOU];
23
+ };
24
+ }
25
+ interface DirectGetClient {
26
+ get<T>(path: string, options?: {
27
+ query?: Record<string, string | number | boolean | readonly (string | number | boolean)[]>;
28
+ }): Promise<T>;
29
+ }
30
+ export declare class UsageClient {
31
+ private store;
32
+ private client;
33
+ constructor(store?: CredentialStoreV2, client?: DirectGetClient);
34
+ getCreditsOverview(): Promise<UsageOverview>;
35
+ private fetchRightsOverview;
36
+ private readUsableCredentials;
37
+ }
38
+ export declare function normalizeUsageCredits(response: unknown): UsageCredits;
39
+ export {};
@@ -0,0 +1,179 @@
1
+ import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ import { CliUsageError } from "../core/output/cli-error.js";
3
+ import { DirectApiClient, DirectApiError } from "./direct-client.js";
4
+ const RIGHTS_OVERVIEW_ENDPOINT = "/trade/rights-overview";
5
+ const GAODOU = "GAODOU";
6
+ export class UsageClient {
7
+ store;
8
+ client;
9
+ constructor(store = new CredentialStoreV2(), client = new DirectApiClient(undefined, store)) {
10
+ this.store = store;
11
+ this.client = client;
12
+ }
13
+ async getCreditsOverview() {
14
+ const credentials = this.readUsableCredentials();
15
+ const response = await this.fetchRightsOverview();
16
+ return {
17
+ status: "completed",
18
+ organization: {
19
+ id: credentials.current_org.id,
20
+ name: credentials.current_org.name ?? null,
21
+ },
22
+ credits: normalizeUsageCredits(response),
23
+ source: {
24
+ endpoint: RIGHTS_OVERVIEW_ENDPOINT,
25
+ goods_type_enums: [GAODOU],
26
+ },
27
+ };
28
+ }
29
+ async fetchRightsOverview() {
30
+ try {
31
+ return await this.client.get(RIGHTS_OVERVIEW_ENDPOINT, {
32
+ query: { goods_type_enums: GAODOU },
33
+ });
34
+ }
35
+ catch (error) {
36
+ if (isUnsupportedAuthorizationType(error)) {
37
+ throw new CliUsageError({
38
+ code: "USAGE_ENDPOINT_AUTH_UNSUPPORTED",
39
+ type: "business",
40
+ message: "稿豆余额接口暂不支持当前 AK/SK 网关鉴权。",
41
+ hint: "需要在 gdcli BFF 增加代理接口 GET /api/cli/entitlements/credits 后重试。",
42
+ retryable: false,
43
+ next_steps: [
44
+ "联系管理员确认 /trade/rights-overview 是否允许 AK/SK 签名访问",
45
+ "或在 gdcli BFF 增加 GET /api/cli/entitlements/credits 代理接口",
46
+ ],
47
+ detail: {
48
+ endpoint: RIGHTS_OVERVIEW_ENDPOINT,
49
+ status_code: error.statusCode,
50
+ },
51
+ });
52
+ }
53
+ throw error;
54
+ }
55
+ }
56
+ readUsableCredentials() {
57
+ const credentials = this.store.read();
58
+ if (!credentials) {
59
+ throw new CliUsageError({
60
+ code: "AUTH_MISSING",
61
+ type: "auth",
62
+ message: "未找到 AK/SK 凭证,请先登录。",
63
+ hint: "请先登录:gd-cli auth login",
64
+ retryable: false,
65
+ next_steps: ["gd-cli auth login"],
66
+ });
67
+ }
68
+ if (this.store.isAkExpired()) {
69
+ throw new CliUsageError({
70
+ code: "AUTH_EXPIRED",
71
+ type: "auth",
72
+ message: "AK/SK 凭证已过期,请重新登录。",
73
+ hint: "请重新登录:gd-cli auth login",
74
+ retryable: false,
75
+ next_steps: ["gd-cli auth login"],
76
+ });
77
+ }
78
+ if (!credentials.current_org?.id) {
79
+ throw new CliUsageError({
80
+ code: "ORG_REQUIRED",
81
+ type: "auth",
82
+ message: "当前未绑定组织,无法查询稿豆余额。",
83
+ hint: "请先切换组织:gd-cli org switch",
84
+ retryable: false,
85
+ next_steps: ["gd-cli org list", "gd-cli org switch"],
86
+ });
87
+ }
88
+ if (this.store.isBindExpired()) {
89
+ throw new CliUsageError({
90
+ code: "ORG_BIND_EXPIRED",
91
+ type: "auth",
92
+ message: "当前组织绑定已过期,无法查询稿豆余额。",
93
+ hint: "请重新切换组织:gd-cli org switch",
94
+ retryable: false,
95
+ next_steps: ["gd-cli org switch"],
96
+ });
97
+ }
98
+ return credentials;
99
+ }
100
+ }
101
+ export function normalizeUsageCredits(response) {
102
+ const record = findGaodouRecord(response) ?? {};
103
+ const total = numberField(record, "total");
104
+ const used = numberField(record, "used");
105
+ const wait = numberField(record, "wait");
106
+ return {
107
+ unit: "gaodou",
108
+ goods_type_enum: stringField(record, "goods_type_enum") ?? GAODOU,
109
+ identifier: stringField(record, "identifier") ?? GAODOU,
110
+ total,
111
+ used,
112
+ wait,
113
+ surplus: total - used,
114
+ };
115
+ }
116
+ function findGaodouRecord(value) {
117
+ const records = collectRecords(value);
118
+ return (records.find((record) => {
119
+ const goodsType = stringField(record, "goods_type_enum");
120
+ const identifier = stringField(record, "identifier");
121
+ return goodsType === GAODOU || identifier === GAODOU;
122
+ }) ??
123
+ records[0] ??
124
+ null);
125
+ }
126
+ function collectRecords(value) {
127
+ if (Array.isArray(value)) {
128
+ return value.flatMap((item) => collectRecords(item));
129
+ }
130
+ const record = objectRecord(value);
131
+ if (!record)
132
+ return [];
133
+ const nestedKeys = ["data", "list", "items", "records", "rights", "overview"];
134
+ const nestedRecords = nestedKeys.flatMap((key) => collectRecords(record[key]));
135
+ if (nestedRecords.length > 0)
136
+ return nestedRecords;
137
+ if ("total" in record ||
138
+ "used" in record ||
139
+ "wait" in record ||
140
+ "goods_type_enum" in record ||
141
+ "identifier" in record) {
142
+ return [record];
143
+ }
144
+ return [];
145
+ }
146
+ function objectRecord(value) {
147
+ return value && typeof value === "object" && !Array.isArray(value)
148
+ ? value
149
+ : null;
150
+ }
151
+ function stringField(record, key) {
152
+ const value = record[key];
153
+ if (typeof value !== "string")
154
+ return undefined;
155
+ const trimmed = value.trim();
156
+ return trimmed ? trimmed : undefined;
157
+ }
158
+ function numberField(record, key) {
159
+ const value = record[key];
160
+ if (value === undefined || value === null || value === "")
161
+ return 0;
162
+ const numeric = Number(value);
163
+ if (!Number.isFinite(numeric)) {
164
+ throw new Error(`权益接口字段 ${key} 不是有效数字`);
165
+ }
166
+ return numeric;
167
+ }
168
+ function isUnsupportedAuthorizationType(error) {
169
+ if (!(error instanceof DirectApiError))
170
+ return false;
171
+ if (error.statusCode !== 401)
172
+ return false;
173
+ const text = typeof error.body === "string"
174
+ ? error.body
175
+ : error.body
176
+ ? JSON.stringify(error.body)
177
+ : error.message;
178
+ return /Unsupported Authorization Type/i.test(text);
179
+ }
@@ -0,0 +1,12 @@
1
+ import type { CredentialsV2, StoredCredentials } from "../core/auth/types.js";
2
+ export declare class CredentialStoreV2 {
3
+ private dir;
4
+ private file;
5
+ constructor(customDir?: string);
6
+ read(): CredentialsV2 | null;
7
+ readAny(): StoredCredentials | null;
8
+ write(credentials: CredentialsV2): void;
9
+ delete(): void;
10
+ isAkExpired(): boolean;
11
+ isBindExpired(): boolean;
12
+ }