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.
- package/CHANGELOG.md +38 -0
- package/README.md +47 -60
- package/dist/bin/gd-cli.js +2 -2
- package/dist/src/api/app-runner.d.ts +1 -4
- package/dist/src/api/app-runner.js +11 -44
- package/dist/src/api/creative-client.d.ts +34 -0
- package/dist/src/api/creative-client.js +184 -0
- package/dist/src/api/dam-client.d.ts +0 -7
- package/dist/src/api/dam-client.js +0 -17
- package/dist/src/api/direct-client.d.ts +9 -2
- package/dist/src/api/direct-client.js +45 -18
- package/dist/src/api/hub-client.d.ts +11 -8
- package/dist/src/api/hub-client.js +17 -18
- package/dist/src/api/usage-client.d.ts +4 -4
- package/dist/src/api/usage-client.js +9 -49
- package/dist/src/auth/credential-store-v2.d.ts +0 -1
- package/dist/src/auth/credential-store-v2.js +0 -6
- package/dist/src/commands/auth.js +17 -53
- package/dist/src/commands/creative.d.ts +8 -0
- package/dist/src/commands/creative.js +178 -0
- package/dist/src/commands/dam.js +270 -127
- package/dist/src/commands/org.js +31 -47
- package/dist/src/commands/skills.js +33 -118
- package/dist/src/commands/update.js +11 -82
- package/dist/src/commands/usage.d.ts +1 -1
- package/dist/src/commands/usage.js +10 -21
- package/dist/src/commands/workflows.d.ts +1 -1
- package/dist/src/commands/workflows.js +299 -197
- package/dist/src/core/auth/auth-guard.d.ts +21 -0
- package/dist/src/core/auth/auth-guard.js +121 -0
- package/dist/src/core/auth/org-manager.d.ts +2 -1
- package/dist/src/core/auth/org-manager.js +61 -6
- package/dist/src/core/output/envelope.d.ts +1 -0
- package/dist/src/core/output/envelope.js +76 -6
- package/dist/src/creative/contract.d.ts +1060 -0
- package/dist/src/creative/contract.js +722 -0
- package/dist/src/creative/media.d.ts +19 -0
- package/dist/src/creative/media.js +83 -0
- package/dist/src/creative/protocol.d.ts +60 -0
- package/dist/src/creative/protocol.js +337 -0
- package/dist/src/io.d.ts +0 -2
- package/dist/src/io.js +0 -8
- package/dist/src/middleware/auth.d.ts +6 -2
- package/dist/src/middleware/auth.js +25 -63
- package/dist/src/middleware/error-handler.d.ts +8 -0
- package/dist/src/middleware/error-handler.js +61 -57
- package/dist/src/program.d.ts +1 -0
- package/dist/src/program.js +137 -122
- package/dist/src/registry/agent-contracts.d.ts +14 -34
- package/dist/src/registry/agent-contracts.js +209 -297
- package/dist/src/registry/direct-registry.d.ts +0 -5
- package/dist/src/registry/direct-registry.js +0 -19
- package/dist/src/registry/registry-client.d.ts +0 -21
- package/dist/src/registry/registry-client.js +1 -40
- package/dist/src/utils/capability-presenter.js +6 -29
- package/dist/src/utils/config.d.ts +2 -2
- package/dist/src/utils/config.js +1 -1
- package/dist/src/utils/help-verify.d.ts +1 -3
- package/dist/src/utils/help-verify.js +1 -25
- package/dist/src/utils/sensitive-path.js +1 -4
- package/package.json +4 -3
- package/skills/gd-cli/SKILL.md +13 -12
- package/skills/gd-cli/references/auth.md +9 -10
- package/skills/gd-cli/references/creative.md +47 -0
- package/skills/gd-cli/references/entitlement.md +3 -3
- package/skills/gd-cli/references/errors.md +10 -3
- package/skills/gd-cli/references/org.md +3 -3
- package/skills/gd-cli/references/sop.md +8 -8
- package/skills/gd-cli/references/update.md +4 -3
- package/skills/gd-cli/references/workflows.md +7 -6
- package/dist/src/commands/models.d.ts +0 -2
- package/dist/src/commands/models.js +0 -78
- package/dist/src/commands/tools.d.ts +0 -4
- package/dist/src/commands/tools.js +0 -383
- package/dist/src/utils/list-output-format.d.ts +0 -10
- package/dist/src/utils/list-output-format.js +0 -34
- 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
|
|
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
|
-
|
|
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.
|
|
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
|
|
64
|
-
if (
|
|
65
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
|
5
|
-
constructor(baseUrl?: string, storeV2?: CredentialStoreV2);
|
|
6
|
-
get<T>(path: string, options?:
|
|
7
|
-
|
|
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 {
|
|
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
|
-
|
|
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.
|
|
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
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
32
|
-
private
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
this.
|
|
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.
|
|
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) ?? {};
|
|
@@ -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 {
|
|
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
|
-
|
|
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("
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
63
|
+
setAuthRequirement(login, "none");
|
|
64
|
+
const logout = auth
|
|
90
65
|
.command("logout")
|
|
91
66
|
.description("取消授权")
|
|
92
67
|
.action(logoutAction);
|
|
93
|
-
|
|
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
|
+
}
|