gaoding-cli 1.0.0-alpha.7 → 1.0.0-alpha.9

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 (77) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +47 -60
  3. package/dist/bin/gd-cli.js +2 -2
  4. package/dist/src/api/app-runner.d.ts +1 -4
  5. package/dist/src/api/app-runner.js +11 -44
  6. package/dist/src/api/creative-client.d.ts +34 -0
  7. package/dist/src/api/creative-client.js +184 -0
  8. package/dist/src/api/dam-client.d.ts +0 -7
  9. package/dist/src/api/dam-client.js +0 -17
  10. package/dist/src/api/direct-client.d.ts +9 -2
  11. package/dist/src/api/direct-client.js +45 -18
  12. package/dist/src/api/hub-client.d.ts +11 -8
  13. package/dist/src/api/hub-client.js +17 -18
  14. package/dist/src/api/usage-client.d.ts +4 -4
  15. package/dist/src/api/usage-client.js +9 -49
  16. package/dist/src/auth/credential-store-v2.d.ts +0 -1
  17. package/dist/src/auth/credential-store-v2.js +0 -6
  18. package/dist/src/commands/auth.js +17 -53
  19. package/dist/src/commands/creative.d.ts +8 -0
  20. package/dist/src/commands/creative.js +178 -0
  21. package/dist/src/commands/dam.js +270 -127
  22. package/dist/src/commands/org.js +31 -47
  23. package/dist/src/commands/skills.js +33 -118
  24. package/dist/src/commands/update.js +11 -82
  25. package/dist/src/commands/usage.d.ts +1 -1
  26. package/dist/src/commands/usage.js +10 -21
  27. package/dist/src/commands/workflows.d.ts +1 -1
  28. package/dist/src/commands/workflows.js +299 -197
  29. package/dist/src/core/auth/auth-guard.d.ts +21 -0
  30. package/dist/src/core/auth/auth-guard.js +121 -0
  31. package/dist/src/core/auth/org-manager.d.ts +2 -1
  32. package/dist/src/core/auth/org-manager.js +61 -6
  33. package/dist/src/core/output/envelope.d.ts +1 -0
  34. package/dist/src/core/output/envelope.js +76 -6
  35. package/dist/src/creative/contract.d.ts +1060 -0
  36. package/dist/src/creative/contract.js +722 -0
  37. package/dist/src/creative/media.d.ts +19 -0
  38. package/dist/src/creative/media.js +83 -0
  39. package/dist/src/creative/protocol.d.ts +60 -0
  40. package/dist/src/creative/protocol.js +337 -0
  41. package/dist/src/io.d.ts +0 -2
  42. package/dist/src/io.js +0 -8
  43. package/dist/src/middleware/auth.d.ts +6 -2
  44. package/dist/src/middleware/auth.js +25 -63
  45. package/dist/src/middleware/error-handler.d.ts +8 -0
  46. package/dist/src/middleware/error-handler.js +61 -57
  47. package/dist/src/program.d.ts +1 -0
  48. package/dist/src/program.js +137 -122
  49. package/dist/src/registry/agent-contracts.d.ts +14 -34
  50. package/dist/src/registry/agent-contracts.js +209 -297
  51. package/dist/src/registry/direct-registry.d.ts +0 -5
  52. package/dist/src/registry/direct-registry.js +0 -19
  53. package/dist/src/registry/registry-client.d.ts +0 -21
  54. package/dist/src/registry/registry-client.js +1 -40
  55. package/dist/src/utils/capability-presenter.js +6 -29
  56. package/dist/src/utils/config.d.ts +2 -2
  57. package/dist/src/utils/config.js +1 -1
  58. package/dist/src/utils/help-verify.d.ts +1 -3
  59. package/dist/src/utils/help-verify.js +1 -25
  60. package/dist/src/utils/sensitive-path.js +1 -4
  61. package/package.json +4 -3
  62. package/skills/gd-cli/SKILL.md +13 -12
  63. package/skills/gd-cli/references/auth.md +9 -10
  64. package/skills/gd-cli/references/creative.md +47 -0
  65. package/skills/gd-cli/references/entitlement.md +3 -3
  66. package/skills/gd-cli/references/errors.md +10 -3
  67. package/skills/gd-cli/references/org.md +3 -3
  68. package/skills/gd-cli/references/sop.md +8 -8
  69. package/skills/gd-cli/references/update.md +4 -3
  70. package/skills/gd-cli/references/workflows.md +7 -6
  71. package/dist/src/commands/models.d.ts +0 -2
  72. package/dist/src/commands/models.js +0 -78
  73. package/dist/src/commands/tools.d.ts +0 -4
  74. package/dist/src/commands/tools.js +0 -383
  75. package/dist/src/utils/list-output-format.d.ts +0 -10
  76. package/dist/src/utils/list-output-format.js +0 -34
  77. package/skills/gd-cli/references/tools.md +0 -29
@@ -1,4 +1,5 @@
1
1
  import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ import { type AuthGuardLike } from "../core/auth/auth-guard.js";
2
3
  type QueryValue = string | number | boolean | readonly (string | number | boolean)[] | undefined;
3
4
  export interface DirectRequestOptions {
4
5
  query?: Record<string, QueryValue>;
@@ -13,10 +14,16 @@ export declare class DirectApiError extends Error {
13
14
  }
14
15
  export declare class DirectApiClient {
15
16
  private baseUrl;
16
- private storeV2;
17
- constructor(baseUrl?: string, storeV2?: CredentialStoreV2);
17
+ private authGuard;
18
+ constructor(baseUrl?: string, storeV2?: CredentialStoreV2, authGuard?: AuthGuardLike);
18
19
  get<T>(path: string, options?: DirectRequestOptions): Promise<T>;
19
20
  post<T>(path: string, body?: unknown, options?: DirectRequestOptions): Promise<T>;
21
+ /**
22
+ * Start a streaming POST and leave the response body untouched for the
23
+ * caller. JSON requests use `post`; Creative uses this method for the
24
+ * Editor-compatible completion stream.
25
+ */
26
+ postStream(path: string, body?: unknown, options?: DirectRequestOptions): Promise<Response>;
20
27
  delete<T>(path: string, options?: DirectRequestOptions): Promise<T>;
21
28
  private request;
22
29
  private attachAuthHeaders;
@@ -1,7 +1,6 @@
1
- import chalk from "chalk";
2
1
  import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ import { AuthGuard, } from "../core/auth/auth-guard.js";
3
3
  import { signApiRequest } from "../core/auth/signature.js";
4
- import { isCredentialsV2 } from "../core/auth/types.js";
5
4
  import { CLI_USER_AGENT, resolveApiBase, resolveEndpoints } from "../utils/config.js";
6
5
  import { readEnv } from "./direct-auth.js";
7
6
  import { redactSensitiveText } from "../utils/redact.js";
@@ -20,10 +19,10 @@ export class DirectApiError extends Error {
20
19
  }
21
20
  export class DirectApiClient {
22
21
  baseUrl;
23
- storeV2;
24
- constructor(baseUrl = resolveApiBase(), storeV2 = new CredentialStoreV2()) {
22
+ authGuard;
23
+ constructor(baseUrl = resolveApiBase(), storeV2 = new CredentialStoreV2(), authGuard = new AuthGuard({ store: storeV2 })) {
25
24
  this.baseUrl = normalizeBaseUrl(baseUrl);
26
- this.storeV2 = storeV2;
25
+ this.authGuard = authGuard;
27
26
  }
28
27
  async get(path, options) {
29
28
  return this.request("GET", path, undefined, options);
@@ -31,6 +30,39 @@ export class DirectApiClient {
31
30
  async post(path, body, options) {
32
31
  return this.request("POST", path, body, options);
33
32
  }
33
+ /**
34
+ * Start a streaming POST and leave the response body untouched for the
35
+ * caller. JSON requests use `post`; Creative uses this method for the
36
+ * Editor-compatible completion stream.
37
+ */
38
+ async postStream(path, body, options = {}) {
39
+ const url = buildUrl(this.baseUrl, path, options.query);
40
+ const headers = new Headers({
41
+ Accept: "text/event-stream",
42
+ "User-Agent": CLI_USER_AGENT,
43
+ ...defaultBusinessHeaders(this.baseUrl),
44
+ ...(options.headers ?? {}),
45
+ });
46
+ if (body !== undefined) {
47
+ headers.set("Content-Type", "application/json");
48
+ }
49
+ if (options.auth !== false) {
50
+ await this.attachAuthHeaders(headers, "POST", url);
51
+ }
52
+ const response = await fetch(url.toString(), {
53
+ method: "POST",
54
+ headers,
55
+ body: body === undefined ? undefined : JSON.stringify(body),
56
+ });
57
+ if (!response.ok) {
58
+ const parsed = await parseResponse(response);
59
+ throw new DirectApiError(response.status, parsed, url.toString());
60
+ }
61
+ if (!response.body) {
62
+ throw new DirectApiError(response.status, { message: "流式接口没有返回响应 body" }, url.toString());
63
+ }
64
+ return response;
65
+ }
34
66
  async delete(path, options) {
35
67
  return this.request("DELETE", path, undefined, options);
36
68
  }
@@ -60,20 +92,15 @@ export class DirectApiClient {
60
92
  return parsed;
61
93
  }
62
94
  async attachAuthHeaders(headers, method, url) {
63
- const v2 = this.storeV2.read();
64
- if (v2 && isCredentialsV2(v2)) {
65
- if (!v2.current_org?.id) {
66
- throw new Error(chalk.yellow("当前未绑定组织:请先执行 gd-cli org switch\n"));
67
- }
68
- const { signature, timestamp } = signApiRequest(method, url.pathname, url.searchParams.toString(), v2.sk);
69
- headers.set("X-AccessKey", v2.ak);
70
- headers.set("X-Signature", signature);
71
- headers.set("X-Timestamp", timestamp);
72
- headers.set("X-Org-Id", v2.current_org.id);
73
- return;
95
+ const credentials = await this.authGuard.ensure("current-org");
96
+ if (!credentials?.current_org?.id) {
97
+ throw new Error("AuthGuard 未返回当前组织凭证。");
74
98
  }
75
- console.log("");
76
- throw new Error(chalk.yellow("先登录并绑定组织:gd-cli auth login\n"));
99
+ const { signature, timestamp } = signApiRequest(method, url.pathname, url.searchParams.toString(), credentials.sk);
100
+ headers.set("X-AccessKey", credentials.ak);
101
+ headers.set("X-Signature", signature);
102
+ headers.set("X-Timestamp", timestamp);
103
+ headers.set("X-Org-Id", credentials.current_org.id);
77
104
  }
78
105
  }
79
106
  export function unwrapEnvelope(value) {
@@ -1,20 +1,23 @@
1
1
  import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ import { type AuthGuardLike } from "../core/auth/auth-guard.js";
2
3
  export declare class HubClient {
3
4
  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>;
5
+ private authGuard;
6
+ constructor(baseUrl?: string, storeV2?: CredentialStoreV2, authGuard?: AuthGuardLike);
7
+ get<T>(path: string, options?: HubRequestOptions): Promise<T>;
8
+ post<T>(path: string, body?: unknown, options?: HubRequestOptions): Promise<T>;
12
9
  private request;
13
10
  private attachAuthHeaders;
14
11
  }
12
+ export interface HubRequestOptions {
13
+ auth?: boolean;
14
+ authRequirement?: AuthenticatedRequirement;
15
+ }
16
+ type AuthenticatedRequirement = "credential" | "current-org";
15
17
  export declare class ApiError extends Error {
16
18
  statusCode: number;
17
19
  body: string;
18
20
  path: string;
19
21
  constructor(statusCode: number, body: string, path: string);
20
22
  }
23
+ export {};
@@ -1,6 +1,6 @@
1
1
  import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
2
  import { resolveApiBase, CLI_USER_AGENT } from "../utils/config.js";
3
- import { isCredentialsV2 } from "../core/auth/types.js";
3
+ import { AuthGuard, } from "../core/auth/auth-guard.js";
4
4
  import { signApiRequest } from "../core/auth/signature.js";
5
5
  import { redactSensitiveText } from "../utils/redact.js";
6
6
  function collectErrorCodes(err, out) {
@@ -27,10 +27,10 @@ function fetchFailureHint(baseUrl, requestUrl) {
27
27
  }
28
28
  export class HubClient {
29
29
  baseUrl;
30
- storeV2;
31
- constructor(baseUrl, storeV2) {
30
+ authGuard;
31
+ constructor(baseUrl, storeV2 = new CredentialStoreV2(), authGuard = new AuthGuard({ store: storeV2 })) {
32
32
  this.baseUrl = baseUrl ?? resolveApiBase();
33
- this.storeV2 = storeV2 ?? new CredentialStoreV2();
33
+ this.authGuard = authGuard;
34
34
  }
35
35
  async get(path, options) {
36
36
  return this.request("GET", path, undefined, options);
@@ -46,7 +46,7 @@ export class HubClient {
46
46
  };
47
47
  const needsAuth = options?.auth !== false;
48
48
  if (needsAuth) {
49
- await this.attachAuthHeaders(headers, method, path);
49
+ await this.attachAuthHeaders(headers, method, path, options?.authRequirement ?? "current-org");
50
50
  }
51
51
  let response;
52
52
  try {
@@ -69,20 +69,19 @@ export class HubClient {
69
69
  }
70
70
  return response.json();
71
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;
72
+ async attachAuthHeaders(headers, method, path, requirement) {
73
+ const credentials = await this.authGuard.ensure(requirement);
74
+ if (!credentials) {
75
+ throw new Error("AuthGuard 未返回签名凭证。");
76
+ }
77
+ const url = new URL(path.replace(/^\//, ""), `${this.baseUrl.replace(/\/$/, "")}/`);
78
+ const { signature, timestamp } = signApiRequest(method, url.pathname, url.searchParams.toString(), credentials.sk);
79
+ headers["X-AccessKey"] = credentials.ak;
80
+ headers["X-Signature"] = signature;
81
+ headers["X-Timestamp"] = timestamp;
82
+ if (requirement === "current-org" && credentials.current_org?.id) {
83
+ headers["X-Org-Id"] = credentials.current_org.id;
84
84
  }
85
- throw new Error("AK/SK 模式需要先登录并绑定组织:\n gd-cli auth login\n gd-cli org switch --org <org-id>");
86
85
  }
87
86
  }
88
87
  export class ApiError extends Error {
@@ -1,4 +1,5 @@
1
1
  import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ import { type AuthGuardLike } from "../core/auth/auth-guard.js";
2
3
  declare const RIGHTS_OVERVIEW_ENDPOINT = "/trade/rights-overview";
3
4
  declare const GAODOU = "GAODOU";
4
5
  export interface UsageCredits {
@@ -28,12 +29,11 @@ interface DirectGetClient {
28
29
  }): Promise<T>;
29
30
  }
30
31
  export declare class UsageClient {
31
- private store;
32
- private client;
33
- constructor(store?: CredentialStoreV2, client?: DirectGetClient);
32
+ private readonly client;
33
+ private readonly authGuard;
34
+ constructor(store?: CredentialStoreV2, client?: DirectGetClient, authGuard?: AuthGuardLike);
34
35
  getCreditsOverview(): Promise<UsageOverview>;
35
36
  private fetchRightsOverview;
36
- private readUsableCredentials;
37
37
  }
38
38
  export declare function normalizeUsageCredits(response: unknown): UsageCredits;
39
39
  export {};
@@ -1,17 +1,21 @@
1
1
  import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
2
+ import { AuthGuard, } from "../core/auth/auth-guard.js";
2
3
  import { CliUsageError } from "../core/output/cli-error.js";
3
4
  import { DirectApiClient, DirectApiError } from "./direct-client.js";
4
5
  const RIGHTS_OVERVIEW_ENDPOINT = "/trade/rights-overview";
5
6
  const GAODOU = "GAODOU";
6
7
  export class UsageClient {
7
- store;
8
8
  client;
9
- constructor(store = new CredentialStoreV2(), client = new DirectApiClient(undefined, store)) {
10
- this.store = store;
11
- this.client = client;
9
+ authGuard;
10
+ constructor(store = new CredentialStoreV2(), client, authGuard = new AuthGuard({ store })) {
11
+ this.authGuard = authGuard;
12
+ this.client = client ?? new DirectApiClient(undefined, store, authGuard);
12
13
  }
13
14
  async getCreditsOverview() {
14
- const credentials = this.readUsableCredentials();
15
+ const credentials = await this.authGuard.ensure("current-org");
16
+ if (!credentials?.current_org?.id) {
17
+ throw new Error("AuthGuard 未返回当前组织凭证。");
18
+ }
15
19
  const response = await this.fetchRightsOverview();
16
20
  return {
17
21
  status: "completed",
@@ -53,50 +57,6 @@ export class UsageClient {
53
57
  throw error;
54
58
  }
55
59
  }
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
60
  }
101
61
  export function normalizeUsageCredits(response) {
102
62
  const record = findGaodouRecord(response) ?? {};
@@ -8,5 +8,4 @@ export declare class CredentialStoreV2 {
8
8
  write(credentials: CredentialsV2): void;
9
9
  delete(): void;
10
10
  isAkExpired(): boolean;
11
- isBindExpired(): boolean;
12
11
  }
@@ -50,10 +50,4 @@ export class CredentialStoreV2 {
50
50
  return true;
51
51
  return new Date(creds.expires_at) <= new Date();
52
52
  }
53
- isBindExpired() {
54
- const creds = this.read();
55
- if (!creds?.current_org?.bind_expires_at)
56
- return false;
57
- return new Date(creds.current_org.bind_expires_at) <= new Date();
58
- }
59
53
  }
@@ -3,10 +3,8 @@ import { withErrorHandler } from "../middleware/error-handler.js";
3
3
  import { createAuthProvider } from "../core/auth/auth-provider.js";
4
4
  import { CredentialStoreV2 } from "../auth/credential-store-v2.js";
5
5
  import { resolveEndpoints, isVerbose, isDebug } from "../utils/config.js";
6
- import { getIO } from "../io.js";
7
6
  import { emitJson, wrapEnvelope } from "../core/output/envelope.js";
8
- import { setStaticHelp } from "../utils/static-help.js";
9
- import { resolveInvokeOutputFormat } from "../utils/list-output-format.js";
7
+ import { setAuthRequirement } from "../middleware/auth.js";
10
8
  const loginAction = withErrorHandler(async (options) => {
11
9
  if (isVerbose() || isDebug()) {
12
10
  const ep = resolveEndpoints();
@@ -14,36 +12,21 @@ const loginAction = withErrorHandler(async (options) => {
14
12
  }
15
13
  const provider = await createAuthProvider();
16
14
  const result = await provider.login({
17
- orgId: options.org,
18
- noBrowser: !!options.noBrowser,
19
- scope: options.scope,
20
- env: options.env,
15
+ noBrowser: options.browser === false,
21
16
  });
22
- const io = getIO();
23
- if (io.prefersJsonOutput()) {
24
- emitJson(wrapEnvelope({
25
- mode: provider.mode,
26
- logged_in: true,
27
- user: result.credentials.user,
28
- org: result.org,
29
- next_steps: result.org ? [] : ["gd-cli org switch"],
30
- }, { command: "auth login" }));
31
- return;
32
- }
33
17
  console.log(chalk.green("\n✓ 授权成功,可以开始使用稿定能力进行创意生产\n"));
34
18
  if (!result.org) {
35
- console.log(chalk.yellow(" 未获取到授权团队,请执行 gd-cli org switch 后再调用 Workflows / Tools / DAM。"));
19
+ console.log(chalk.yellow(" 尚未选择当前组织,请执行 gd-cli org switch 后再调用 Creative / Workflows / DAM。"));
36
20
  }
37
21
  });
38
22
  const statusAction = withErrorHandler(async (_opts, command) => {
39
23
  const cmd = command;
40
- const format = resolveInvokeOutputFormat(cmd);
41
- const io = getIO();
24
+ const json = cmd.opts().json === true;
42
25
  const store = new CredentialStoreV2();
43
26
  const creds = store.read();
44
27
  const ep = resolveEndpoints();
45
28
  if (!creds) {
46
- if (io.prefersJsonOutput(format)) {
29
+ if (json) {
47
30
  emitJson(wrapEnvelope({
48
31
  logged_in: false,
49
32
  environment: { active: ep.active_env },
@@ -56,7 +39,7 @@ const statusAction = withErrorHandler(async (_opts, command) => {
56
39
  return;
57
40
  }
58
41
  const payload = buildStatusPayload(creds, store);
59
- if (io.prefersJsonOutput(format)) {
42
+ if (json) {
60
43
  emitJson(wrapEnvelope(payload, { command: "auth status" }));
61
44
  return;
62
45
  }
@@ -66,54 +49,39 @@ const logoutAction = withErrorHandler(async () => {
66
49
  const provider = await createAuthProvider();
67
50
  await provider.logout();
68
51
  new CredentialStoreV2().delete();
69
- const io = getIO();
70
- if (io.prefersJsonOutput()) {
71
- emitJson(wrapEnvelope({ logged_out: true }, { command: "auth logout" }));
72
- return;
73
- }
74
52
  console.log(chalk.green("✓ 已取消授权,可以重新执行 gd-cli auth login 进行授权。\n"));
75
53
  });
76
54
  export function registerAuthCommand(program) {
77
55
  const auth = program
78
56
  .command("auth")
79
57
  .description("登录、退出、查看登录状态");
80
- setStaticHelp(auth, AUTH_HELP);
81
- auth
58
+ const login = auth
82
59
  .command("login")
83
60
  .description("浏览器/扫码授权登录")
84
- .option("--org <org-id>", "登录后直接绑定该组织")
85
- .option("--env <env>", "环境标识 cursor / claude / ci")
86
- .option("--scope <scope>", "授权范围,逗号分隔")
87
61
  .option("--no-browser", "不自动打开浏览器")
88
62
  .action(loginAction);
89
- auth
63
+ setAuthRequirement(login, "none");
64
+ const logout = auth
90
65
  .command("logout")
91
66
  .description("取消授权")
92
67
  .action(logoutAction);
93
- auth.command("status").description("查看授权状态").action(statusAction);
68
+ setAuthRequirement(logout, "none");
69
+ const status = auth
70
+ .command("status")
71
+ .description("查看授权状态")
72
+ .option("--json", "输出机器可读 JSON")
73
+ .action(statusAction);
74
+ setAuthRequirement(status, "none");
94
75
  }
95
- const AUTH_HELP = `
96
- Usage: gd-cli auth [options] [command]
97
-
98
- 登录、退出、查看登录状态
99
-
100
- Commands:
101
- login 浏览器/扫码授权登录
102
- logout 取消授权
103
- status 查看授权状态
104
- `;
105
76
  function buildStatusPayload(creds, store) {
106
77
  const ep = resolveEndpoints();
107
78
  const credentialExpired = store.isAkExpired();
108
- const orgBindExpired = store.isBindExpired();
109
79
  const envMatches = !creds.issued_for_env || creds.issued_for_env === ep.active_env;
110
80
  const nextSteps = [];
111
81
  if (credentialExpired)
112
82
  nextSteps.push("gd-cli auth login");
113
83
  if (!creds.current_org?.id)
114
84
  nextSteps.push("gd-cli org list", "gd-cli org switch");
115
- if (orgBindExpired)
116
- nextSteps.push("gd-cli org switch");
117
85
  if (!envMatches)
118
86
  nextSteps.push("gd-cli auth login");
119
87
  const payload = {
@@ -131,8 +99,6 @@ function buildStatusPayload(creds, store) {
131
99
  ? {
132
100
  id: creds.current_org.id,
133
101
  name: creds.current_org.name,
134
- bind_expires_at: creds.current_org.bind_expires_at ?? null,
135
- bind_valid: !orgBindExpired,
136
102
  }
137
103
  : null,
138
104
  credential: {
@@ -170,12 +136,10 @@ function printStatusHuman(payload) {
170
136
  console.log(` 用户: ${account.name ?? account.user_id}`);
171
137
  }
172
138
  if (organization) {
173
- const valid = organization.bind_valid ? "有效" : "已过期";
174
139
  console.log(` 当前组织: ${organization.name} (${organization.id})`);
175
- console.log(` 组织绑定: ${valid}${organization.bind_expires_at ? `,至 ${organization.bind_expires_at}` : ""}`);
176
140
  }
177
141
  else {
178
- console.log(chalk.yellow(" 当前组织: 未绑定"));
142
+ console.log(chalk.yellow(" 当前组织: 未选择"));
179
143
  }
180
144
  console.log(` 凭证: AK/SK ${credential.ak_preview},${credential.valid ? "有效" : "已过期"}`);
181
145
  console.log(` 过期时间: ${credential.expires_at}`);
@@ -0,0 +1,8 @@
1
+ import type { Command } from "commander";
2
+ import { CreativeClient } from "../api/creative-client.js";
3
+ export interface CreativeCommandDeps {
4
+ createClient?: () => Pick<CreativeClient, "run">;
5
+ readInput?: (ref: string) => Promise<Record<string, unknown>>;
6
+ }
7
+ export declare function registerCreativeCommand(program: Command, deps?: CreativeCommandDeps): void;
8
+ export declare function runCreative(options: Record<string, unknown>, command: Command, deps?: CreativeCommandDeps): Promise<void>;
@@ -0,0 +1,178 @@
1
+ import chalk from "chalk";
2
+ import { CreativeClient } from "../api/creative-client.js";
3
+ import { CliUsageError } from "../core/output/cli-error.js";
4
+ import { formatOutput } from "../middleware/output.js";
5
+ import { jsonFailureEnvelopeFor, setJsonFailureProjector, } from "../middleware/error-handler.js";
6
+ import { getCreativeExamples, getCreativeSchema, validateCreativeInput, } from "../creative/contract.js";
7
+ import { readJsonInput } from "../utils/input-json.js";
8
+ import { setAuthRequirement } from "../middleware/auth.js";
9
+ const CREATIVE_FAILURE_META = {
10
+ command: "creative run",
11
+ schema_version: "creative/v1",
12
+ };
13
+ export function registerCreativeCommand(program, deps = {}) {
14
+ const creative = program
15
+ .command("creative")
16
+ .usage("<command> [options]")
17
+ .description("通过稿定 Creative 完成图片、视频和文本创作");
18
+ const run = creative
19
+ .command("run")
20
+ .usage("[options]")
21
+ .description("发起创作、继续已有创作或回答追问")
22
+ .option("--prompt <text>", "详细描述期望的最终结果")
23
+ .option("--input <path>", "从 JSON 文件读取完整输入;传 - 表示读取 stdin")
24
+ .option("--json", "输出机器可读 JSON")
25
+ .action(async (options, command) => {
26
+ await runCreative(options, command, deps);
27
+ });
28
+ setJsonFailureProjector(run, creativeFailureEnvelope);
29
+ setAuthRequirement(run, "current-org");
30
+ const schema = creative
31
+ .command("schema")
32
+ .description("查看 Creative 输入与输出 JSON Schema")
33
+ .option("--json", "输出完整 JSON Schema")
34
+ .action((_options, command) => {
35
+ const payload = getCreativeSchema();
36
+ if (command.opts().json === true) {
37
+ formatOutput(payload, "json");
38
+ return;
39
+ }
40
+ console.log("Creative 输入: prompt、media、parameters、conversation_id、question_message_id、answers");
41
+ console.log("Creative 输出: message、outputs,以及连续创作或追问所需的会话信息");
42
+ console.log("完整 JSON Schema: gd-cli creative schema --json");
43
+ });
44
+ setAuthRequirement(schema, "none");
45
+ const examples = creative
46
+ .command("examples")
47
+ .description("查看 Creative 调用示例")
48
+ .action(() => {
49
+ const payload = getCreativeExamples();
50
+ for (const [index, example] of payload.examples.entries()) {
51
+ console.log(`${index + 1}. ${example.description} (${example.name})`);
52
+ console.log(` ${example.command}`);
53
+ console.log(" 请求 JSON:");
54
+ console.log(indentJson(JSON.stringify(example.input, null, 2), " "));
55
+ console.log("");
56
+ }
57
+ });
58
+ setAuthRequirement(examples, "none");
59
+ }
60
+ function indentJson(value, prefix) {
61
+ return value
62
+ .split("\n")
63
+ .map((line) => `${prefix}${line}`)
64
+ .join("\n");
65
+ }
66
+ export async function runCreative(options, command, deps = {}) {
67
+ const format = command.opts().json === true ? "json" : "human";
68
+ try {
69
+ const input = await resolveCreativeInput(options, deps.readInput ?? readJsonInput);
70
+ const client = deps.createClient?.() ?? new CreativeClient();
71
+ const result = await client.run(validateCreativeInput(input));
72
+ const envelope = {
73
+ status: "completed",
74
+ data: result,
75
+ error: null,
76
+ meta: CREATIVE_FAILURE_META,
77
+ };
78
+ renderCreativeEnvelope(envelope, format);
79
+ }
80
+ catch (error) {
81
+ const envelope = jsonFailureEnvelopeFor(command, error);
82
+ if (format === "json") {
83
+ formatOutput(envelope, "json");
84
+ }
85
+ else {
86
+ printCreativeError(envelope.error);
87
+ }
88
+ process.exitCode = 1;
89
+ }
90
+ }
91
+ async function resolveCreativeInput(options, readInput) {
92
+ const prompt = typeof options.prompt === "string" ? options.prompt : undefined;
93
+ const inputRef = typeof options.input === "string" ? options.input : undefined;
94
+ if (prompt !== undefined && inputRef !== undefined) {
95
+ throw new CliUsageError({
96
+ code: "PARAM_INVALID",
97
+ type: "validation",
98
+ message: "--prompt 与 --input 不能同时使用",
99
+ hint: "纯文本请求使用 --prompt;包含媒体、parameters 或连续会话信息时使用 --input",
100
+ retryable: false,
101
+ });
102
+ }
103
+ if (prompt !== undefined)
104
+ return { prompt };
105
+ if (inputRef !== undefined)
106
+ return readInput(inputRef);
107
+ throw new CliUsageError({
108
+ code: "PARAM_MISSING",
109
+ type: "validation",
110
+ message: "Creative 请求必须提供 --prompt 或 --input",
111
+ hint: "执行 gd-cli creative --help 查看输入方式",
112
+ retryable: false,
113
+ });
114
+ }
115
+ function renderCreativeEnvelope(envelope, format) {
116
+ if (format === "json") {
117
+ formatOutput(envelope, "json");
118
+ return;
119
+ }
120
+ if (!envelope.data)
121
+ return;
122
+ printCreativeResult(envelope.data, format);
123
+ }
124
+ function printCreativeResult(result, format) {
125
+ if (result.message)
126
+ console.log(result.message);
127
+ for (const output of result.outputs)
128
+ printCreativeOutput(output, format);
129
+ if (result.finish_reason === "requires_input" && result.questions) {
130
+ console.log(chalk.yellow(`\n${result.questions.title || "需要补充信息"}`));
131
+ for (const question of result.questions.questions) {
132
+ console.log(` ${question.question}`);
133
+ if (question.options.length > 0) {
134
+ console.log(` 可选: ${question.options.map((option) => option.label).join("、")}`);
135
+ }
136
+ }
137
+ console.log(`\n conversation_id: ${result.conversation_id}`);
138
+ if (result.question_message_id) {
139
+ console.log(` question_message_id: ${result.question_message_id}`);
140
+ }
141
+ }
142
+ }
143
+ function printCreativeOutput(output, _format) {
144
+ if (output.type === "text") {
145
+ if (output.text)
146
+ console.log(output.text);
147
+ return;
148
+ }
149
+ if (output.type === "json") {
150
+ console.log(JSON.stringify(output.value, null, 2));
151
+ return;
152
+ }
153
+ console.log(`${output.type}: ${output.url}`);
154
+ }
155
+ function printCreativeError(error) {
156
+ console.error(chalk.red(`Error: ${error.message}`));
157
+ if (error.code)
158
+ console.error(` 错误码: ${error.code}`);
159
+ if (error.hint)
160
+ console.error(` 说明: ${error.hint}`);
161
+ }
162
+ function creativeFailureEnvelope(error) {
163
+ return {
164
+ status: "failed",
165
+ data: null,
166
+ error: toCreativeError(error),
167
+ meta: CREATIVE_FAILURE_META,
168
+ };
169
+ }
170
+ function toCreativeError(body) {
171
+ return {
172
+ code: body.code,
173
+ type: body.type,
174
+ message: body.message,
175
+ ...(body.hint ? { hint: body.hint } : {}),
176
+ ...(body.retryable !== undefined ? { retryable: body.retryable } : {}),
177
+ };
178
+ }