gaoding-cli 1.0.0-alpha.7 → 1.0.0-alpha.8
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 +20 -0
- package/README.md +18 -14
- package/dist/src/api/app-runner.js +1 -2
- package/dist/src/api/creative-client.d.ts +34 -0
- package/dist/src/api/creative-client.js +184 -0
- 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 +13 -13
- package/dist/src/commands/creative.d.ts +8 -0
- package/dist/src/commands/creative.js +186 -0
- package/dist/src/commands/dam.js +27 -22
- package/dist/src/commands/org.js +20 -14
- package/dist/src/commands/skills.js +9 -4
- package/dist/src/commands/update.js +6 -2
- package/dist/src/commands/usage.d.ts +1 -1
- package/dist/src/commands/usage.js +6 -4
- package/dist/src/commands/workflows.js +31 -13
- 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/creative/contract.d.ts +1112 -0
- package/dist/src/creative/contract.js +743 -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/middleware/auth.d.ts +6 -2
- package/dist/src/middleware/auth.js +25 -63
- package/dist/src/middleware/error-handler.d.ts +2 -0
- package/dist/src/middleware/error-handler.js +3 -4
- package/dist/src/program.js +7 -89
- package/dist/src/registry/agent-contracts.d.ts +3 -11
- package/dist/src/registry/agent-contracts.js +5 -243
- 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/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/package.json +3 -3
- package/skills/gd-cli/SKILL.md +10 -9
- package/skills/gd-cli/references/auth.md +6 -0
- package/skills/gd-cli/references/creative.md +47 -0
- package/skills/gd-cli/references/entitlement.md +2 -2
- package/skills/gd-cli/references/errors.md +7 -0
- package/skills/gd-cli/references/sop.md +4 -4
- package/skills/gd-cli/references/workflows.md +1 -1
- 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/skills/gd-cli/references/tools.md +0 -29
|
@@ -8,7 +8,7 @@ import { appendAppRunQuickExample } from "../utils/help-verify.js";
|
|
|
8
8
|
import { writeAppDeliverables } from "../utils/write-app-deliverables.js";
|
|
9
9
|
import { emitJson, wrapEnvelope, shouldEmitEnvelope } from "../core/output/envelope.js";
|
|
10
10
|
import { DIRECT_APPS, resolveAppSpec, } from "../registry/direct-registry.js";
|
|
11
|
-
import { buildWorkflowExamples, buildWorkflowSchema,
|
|
11
|
+
import { buildWorkflowExamples, buildWorkflowSchema, buildWorkflowManifest, rowsForWorkflowManifest, } from "../registry/agent-contracts.js";
|
|
12
12
|
import { applySetEntries, collectArg, readJsonInput } from "../utils/input-json.js";
|
|
13
13
|
import { setStaticHelp } from "../utils/static-help.js";
|
|
14
14
|
import { printCapabilityExamples, printCapabilitySchema, } from "../utils/capability-presenter.js";
|
|
@@ -16,6 +16,7 @@ import { capabilityMetadata } from "../utils/capability-metadata.js";
|
|
|
16
16
|
import { assertReferenceImageAvailable, firstReferenceImage, } from "../utils/reference-image.js";
|
|
17
17
|
import { buildEcommerceInputGuidance } from "../utils/ecommerce-input-guidance.js";
|
|
18
18
|
import { resolveSafeOutputDir } from "../utils/output-path.js";
|
|
19
|
+
import { setAuthRequirement } from "../middleware/auth.js";
|
|
19
20
|
const INTERNAL_OPTS = new Set([
|
|
20
21
|
"outputDir",
|
|
21
22
|
"save",
|
|
@@ -26,13 +27,13 @@ const INTERNAL_OPTS = new Set([
|
|
|
26
27
|
"asset",
|
|
27
28
|
]);
|
|
28
29
|
export function registerWorkflowsCommand(program) {
|
|
29
|
-
const
|
|
30
|
+
const workflows = program
|
|
30
31
|
.command("workflows")
|
|
31
32
|
.usage("<command> [options]")
|
|
32
33
|
.description("完整创意场景 Workflows")
|
|
33
34
|
.configureHelp({ showGlobalOptions: true });
|
|
34
|
-
setStaticHelp(
|
|
35
|
-
|
|
35
|
+
setStaticHelp(workflows, WORKFLOWS_HELP);
|
|
36
|
+
const list = workflows
|
|
36
37
|
.command("list")
|
|
37
38
|
.alias("ls")
|
|
38
39
|
.description("列出内置 Workflows")
|
|
@@ -50,7 +51,8 @@ export function registerWorkflowsCommand(program) {
|
|
|
50
51
|
formatOutput(rowsForAppList(publicItems), format);
|
|
51
52
|
}
|
|
52
53
|
}));
|
|
53
|
-
|
|
54
|
+
setAuthRequirement(list, "none");
|
|
55
|
+
const info = workflows
|
|
54
56
|
.command("info <workflow-id>")
|
|
55
57
|
.description("查看 Workflow 详情")
|
|
56
58
|
.action(withErrorHandler(async (appId, _opts, command) => {
|
|
@@ -67,7 +69,8 @@ export function registerWorkflowsCommand(program) {
|
|
|
67
69
|
formatOutput(payload, format);
|
|
68
70
|
}
|
|
69
71
|
}));
|
|
70
|
-
|
|
72
|
+
setAuthRequirement(info, "none");
|
|
73
|
+
const schema = workflows
|
|
71
74
|
.command("schema <workflow-id>")
|
|
72
75
|
.description("查看 Workflow 输入/输出 JSON Schema")
|
|
73
76
|
.action(withErrorHandler(async (appId, _opts, command) => {
|
|
@@ -81,7 +84,8 @@ export function registerWorkflowsCommand(program) {
|
|
|
81
84
|
printCapabilitySchema(payload);
|
|
82
85
|
}
|
|
83
86
|
}));
|
|
84
|
-
|
|
87
|
+
setAuthRequirement(schema, "none");
|
|
88
|
+
const examples = workflows
|
|
85
89
|
.command("examples <workflow-id>")
|
|
86
90
|
.description("查看 Workflow 调用示例")
|
|
87
91
|
.action(withErrorHandler(async (appId, _opts, command) => {
|
|
@@ -95,15 +99,17 @@ export function registerWorkflowsCommand(program) {
|
|
|
95
99
|
printCapabilityExamples(payload);
|
|
96
100
|
}
|
|
97
101
|
}));
|
|
98
|
-
|
|
102
|
+
setAuthRequirement(examples, "none");
|
|
103
|
+
const manifest = workflows
|
|
99
104
|
.command("manifest")
|
|
100
105
|
.description("输出 Workflows 机器可读能力清单")
|
|
101
106
|
.action((_opts, command) => {
|
|
102
|
-
const manifest =
|
|
107
|
+
const manifest = buildWorkflowManifest();
|
|
103
108
|
const format = resolveListOutputFormat(command);
|
|
104
|
-
formatOutput(format === "json" ? manifest :
|
|
109
|
+
formatOutput(format === "json" ? manifest : rowsForWorkflowManifest(manifest), format);
|
|
105
110
|
});
|
|
106
|
-
|
|
111
|
+
setAuthRequirement(manifest, "none");
|
|
112
|
+
const callCmd = workflows
|
|
107
113
|
.command("run <workflow-id> [input]")
|
|
108
114
|
.usage("<workflow-id> [input] [options]")
|
|
109
115
|
.description("运行 Workflow(场景编排)")
|
|
@@ -135,6 +141,7 @@ export function registerWorkflowsCommand(program) {
|
|
|
135
141
|
callCmd.action(withErrorHandler(async (appId, input, options, command) => {
|
|
136
142
|
await runAppCall(appId, input, options, command);
|
|
137
143
|
}));
|
|
144
|
+
setAuthRequirement(callCmd, (command) => command.opts().dryRun === true ? "none" : "current-org");
|
|
138
145
|
appendAppRunQuickExample(callCmd, "gd-cli workflows run");
|
|
139
146
|
callCmd.addHelpText("afterAll", WORKFLOW_RUN_HELP);
|
|
140
147
|
}
|
|
@@ -358,7 +365,18 @@ function applyAssetEntries(target, entries) {
|
|
|
358
365
|
}
|
|
359
366
|
function toPublicAppPayload(app) {
|
|
360
367
|
return {
|
|
361
|
-
|
|
368
|
+
id: app.id,
|
|
369
|
+
aliases: app.aliases ?? [],
|
|
370
|
+
name: app.name,
|
|
371
|
+
description: app.description,
|
|
372
|
+
category: app.category,
|
|
373
|
+
estimated_duration: app.estimated_duration,
|
|
374
|
+
input: app.input,
|
|
375
|
+
output: app.output,
|
|
376
|
+
outputs: app.outputs ?? [],
|
|
377
|
+
examples: app.examples ?? [],
|
|
378
|
+
supports_batch: app.supports_batch ?? false,
|
|
379
|
+
consumes_quota: app.consumes_quota ?? false,
|
|
362
380
|
...capabilityMetadata("app", app.id),
|
|
363
381
|
canonical_command: `gd-cli workflows run ${app.id}`,
|
|
364
382
|
schema_command: `gd-cli workflows schema ${app.id} --format json`,
|
|
@@ -369,7 +387,7 @@ const WORKFLOWS_HELP = `
|
|
|
369
387
|
gd-cli workflows <command> [options]
|
|
370
388
|
|
|
371
389
|
用于运行完整业务场景,例如商品图库素材包、详情页/A+模块素材包、小红书种草图文包。
|
|
372
|
-
一个 Workflow
|
|
390
|
+
一个 Workflow 通常会编排多个内部能力,并输出一组可交付物料。
|
|
373
391
|
|
|
374
392
|
常用命令:
|
|
375
393
|
list/ls 列出内置 Workflows
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CredentialStoreV2 } from "../../auth/credential-store-v2.js";
|
|
2
|
+
import type { CredentialsV2, OrgBinding } from "./types.js";
|
|
3
|
+
export type AuthRequirement = "none" | "credential" | "current-org";
|
|
4
|
+
export interface AuthGuardOptions {
|
|
5
|
+
store?: CredentialStoreV2;
|
|
6
|
+
now?: () => Date;
|
|
7
|
+
refreshCurrentOrg?: (organization: OrgBinding) => Promise<void>;
|
|
8
|
+
onRefreshWarning?: (error: unknown) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface AuthGuardLike {
|
|
11
|
+
ensure(requirement: AuthRequirement): Promise<CredentialsV2 | null>;
|
|
12
|
+
}
|
|
13
|
+
export declare class AuthGuard {
|
|
14
|
+
private readonly store;
|
|
15
|
+
private readonly now;
|
|
16
|
+
private readonly refreshCurrentOrg;
|
|
17
|
+
private readonly onRefreshWarning;
|
|
18
|
+
constructor(options?: AuthGuardOptions);
|
|
19
|
+
ensure(requirement: AuthRequirement): Promise<CredentialsV2 | null>;
|
|
20
|
+
private clearCurrentOrganization;
|
|
21
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { CredentialStoreV2 } from "../../auth/credential-store-v2.js";
|
|
2
|
+
import { isVerbose } from "../../utils/config.js";
|
|
3
|
+
import { redactSensitiveText } from "../../utils/redact.js";
|
|
4
|
+
import { CliUsageError } from "../output/cli-error.js";
|
|
5
|
+
export class AuthGuard {
|
|
6
|
+
store;
|
|
7
|
+
now;
|
|
8
|
+
refreshCurrentOrg;
|
|
9
|
+
onRefreshWarning;
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.store = options.store ?? new CredentialStoreV2();
|
|
12
|
+
this.now = options.now ?? (() => new Date());
|
|
13
|
+
this.refreshCurrentOrg = options.refreshCurrentOrg ?? (async (organization) => {
|
|
14
|
+
const { OrgManager } = await import("./org-manager.js");
|
|
15
|
+
await new OrgManager(this.store).refreshCurrentOrgBinding(organization);
|
|
16
|
+
});
|
|
17
|
+
this.onRefreshWarning = options.onRefreshWarning ?? warnRefreshFailure;
|
|
18
|
+
}
|
|
19
|
+
async ensure(requirement) {
|
|
20
|
+
if (requirement === "none")
|
|
21
|
+
return null;
|
|
22
|
+
const credentials = this.store.read();
|
|
23
|
+
if (!credentials) {
|
|
24
|
+
throw new CliUsageError({
|
|
25
|
+
code: "AUTH_MISSING",
|
|
26
|
+
type: "auth",
|
|
27
|
+
message: "未找到 AK/SK 凭证,请先登录。",
|
|
28
|
+
hint: "请先登录:gd-cli auth login",
|
|
29
|
+
retryable: false,
|
|
30
|
+
next_steps: ["gd-cli auth login"],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (isExpired(credentials.expires_at, this.now())) {
|
|
34
|
+
throw new CliUsageError({
|
|
35
|
+
code: "AUTH_EXPIRED",
|
|
36
|
+
type: "auth",
|
|
37
|
+
message: "AK/SK 凭证已过期,请重新登录。",
|
|
38
|
+
hint: "请重新登录:gd-cli auth login",
|
|
39
|
+
retryable: false,
|
|
40
|
+
next_steps: ["gd-cli auth login"],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (requirement === "current-org" && !credentials.current_org?.id) {
|
|
44
|
+
throw new CliUsageError({
|
|
45
|
+
code: "ORG_REQUIRED",
|
|
46
|
+
type: "auth",
|
|
47
|
+
message: "当前未选择组织。",
|
|
48
|
+
hint: "请先选择组织:gd-cli org switch",
|
|
49
|
+
retryable: false,
|
|
50
|
+
next_steps: ["gd-cli org list", "gd-cli org switch"],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (requirement === "current-org" &&
|
|
54
|
+
credentials.current_org &&
|
|
55
|
+
isRefreshDue(credentials.current_org.bind_expires_at, this.now())) {
|
|
56
|
+
try {
|
|
57
|
+
await refreshOnce(`${credentials.ak}:${credentials.current_org.id}`, () => this.refreshCurrentOrg(credentials.current_org));
|
|
58
|
+
return this.store.read();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (isOrgInvalid(error)) {
|
|
62
|
+
const organization = {
|
|
63
|
+
id: credentials.current_org.id,
|
|
64
|
+
name: credentials.current_org.name,
|
|
65
|
+
};
|
|
66
|
+
this.clearCurrentOrganization(credentials.current_org.id);
|
|
67
|
+
throw new CliUsageError({
|
|
68
|
+
code: "ORG_INVALID",
|
|
69
|
+
type: "auth",
|
|
70
|
+
message: `当前组织 ${organization.name} (${organization.id}) 不可用或已无访问权限。`,
|
|
71
|
+
hint: "请重新选择组织:gd-cli org switch",
|
|
72
|
+
retryable: false,
|
|
73
|
+
next_steps: ["gd-cli org list", "gd-cli org switch"],
|
|
74
|
+
detail: { organization },
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
this.onRefreshWarning(error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return credentials;
|
|
81
|
+
}
|
|
82
|
+
clearCurrentOrganization(expectedOrgId) {
|
|
83
|
+
const latest = this.store.read();
|
|
84
|
+
if (!latest || latest.current_org?.id !== expectedOrgId)
|
|
85
|
+
return;
|
|
86
|
+
delete latest.current_org;
|
|
87
|
+
this.store.write(latest);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const REFRESH_WINDOW_MS = 5 * 60 * 1000;
|
|
91
|
+
const pendingRefreshes = new Map();
|
|
92
|
+
function refreshOnce(key, refresh) {
|
|
93
|
+
const pending = pendingRefreshes.get(key);
|
|
94
|
+
if (pending)
|
|
95
|
+
return pending;
|
|
96
|
+
const started = refresh().finally(() => {
|
|
97
|
+
if (pendingRefreshes.get(key) === started)
|
|
98
|
+
pendingRefreshes.delete(key);
|
|
99
|
+
});
|
|
100
|
+
pendingRefreshes.set(key, started);
|
|
101
|
+
return started;
|
|
102
|
+
}
|
|
103
|
+
function isRefreshDue(expiresAt, now) {
|
|
104
|
+
if (!expiresAt)
|
|
105
|
+
return true;
|
|
106
|
+
const expires = Date.parse(expiresAt);
|
|
107
|
+
return !Number.isFinite(expires) || expires <= now.getTime() + REFRESH_WINDOW_MS;
|
|
108
|
+
}
|
|
109
|
+
function isExpired(expiresAt, now) {
|
|
110
|
+
const expires = Date.parse(expiresAt);
|
|
111
|
+
return !Number.isFinite(expires) || expires <= now.getTime();
|
|
112
|
+
}
|
|
113
|
+
function isOrgInvalid(error) {
|
|
114
|
+
return error instanceof CliUsageError && error.body.code === "ORG_INVALID";
|
|
115
|
+
}
|
|
116
|
+
function warnRefreshFailure(error) {
|
|
117
|
+
if (!isVerbose())
|
|
118
|
+
return;
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
console.error(`[gd-cli] 当前组织访问刷新失败,本次继续使用现有组织:${redactSensitiveText(message)}`);
|
|
121
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SsoClient } from "../../api/sso-client.js";
|
|
2
2
|
import { HubClient } from "../../api/hub-client.js";
|
|
3
3
|
import { CredentialStoreV2 } from "../../auth/credential-store-v2.js";
|
|
4
|
-
import type { OrgSummary } from "./types.js";
|
|
4
|
+
import type { OrgBinding, OrgSummary } from "./types.js";
|
|
5
5
|
/**
|
|
6
6
|
* 组织列表与 bind-org。
|
|
7
7
|
* Device 模式:组织列表走 Hub 网关代理的 /structure/user/orgs;
|
|
@@ -17,6 +17,7 @@ export declare class OrgManager {
|
|
|
17
17
|
switchOrgInteractive(): Promise<OrgSummary | undefined>;
|
|
18
18
|
switchOrg(orgId: string): Promise<OrgSummary>;
|
|
19
19
|
bindAuthorizedOrg(orgId: string): Promise<OrgSummary>;
|
|
20
|
+
refreshCurrentOrgBinding(organization: OrgBinding): Promise<void>;
|
|
20
21
|
getCurrentOrgId(): string | undefined;
|
|
21
22
|
getCurrentOrg(): OrgSummary | undefined;
|
|
22
23
|
private listOrgsWithSource;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { SsoClient } from "../../api/sso-client.js";
|
|
1
|
+
import { SsoApiError, SsoClient } from "../../api/sso-client.js";
|
|
2
2
|
import { HubClient } from "../../api/hub-client.js";
|
|
3
3
|
import { CredentialStoreV2 } from "../../auth/credential-store-v2.js";
|
|
4
4
|
import { GDHUB_CLI_CLIENT_ID } from "../../utils/config.js";
|
|
5
|
+
import { CliUsageError } from "../output/cli-error.js";
|
|
5
6
|
import chalk from "chalk";
|
|
6
7
|
const BIND_TTL_MS = 24 * 60 * 60 * 1000;
|
|
7
8
|
const STRUCTURE_USER_ORGS_PATH = "/structure/user/orgs";
|
|
@@ -46,7 +47,7 @@ export class OrgManager {
|
|
|
46
47
|
console.log(chalk.yellow("\nSSO 未返回可切换的组织列表。请从稿定工作台获取 orgId,或使用:\n gd-cli org switch --org <org-id>\n"));
|
|
47
48
|
const { Prompt } = await import("../prompt/index.js");
|
|
48
49
|
const orgId = await Prompt.text({
|
|
49
|
-
message: "
|
|
50
|
+
message: "输入要选择的组织 ID (orgId)",
|
|
50
51
|
validate: (v) => (v?.trim() ? true : "orgId 不能为空"),
|
|
51
52
|
});
|
|
52
53
|
return this.switchOrg(orgId.trim());
|
|
@@ -61,7 +62,7 @@ export class OrgManager {
|
|
|
61
62
|
const listed = await this.listOrgsWithSource(v2);
|
|
62
63
|
const selected = listed.orgs.find((o) => o.id === normalizedOrgId);
|
|
63
64
|
if (listed.source === "structure" && !selected) {
|
|
64
|
-
throw new Error(`组织 ${normalizedOrgId}
|
|
65
|
+
throw new Error(`组织 ${normalizedOrgId} 不在当前用户的可用组织列表中,请先执行 gd-cli org list 确认 orgId`);
|
|
65
66
|
}
|
|
66
67
|
return this.bindOrgDevice(v2, normalizedOrgId, selected);
|
|
67
68
|
}
|
|
@@ -82,6 +83,34 @@ export class OrgManager {
|
|
|
82
83
|
}
|
|
83
84
|
return this.bindOrgDevice(v2, normalizedOrgId, selected);
|
|
84
85
|
}
|
|
86
|
+
async refreshCurrentOrgBinding(organization) {
|
|
87
|
+
const v2 = this.v2Store.read();
|
|
88
|
+
if (!v2)
|
|
89
|
+
throw new Error("未找到 AK/SK 凭证,请先执行 gd-cli auth login");
|
|
90
|
+
if (v2.current_org?.id !== organization.id)
|
|
91
|
+
return;
|
|
92
|
+
let result;
|
|
93
|
+
try {
|
|
94
|
+
result = await this.sso.bindOrg(v2.ak, v2.sk, organization.id);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const message = explicitInvalidOrganizationMessage(error);
|
|
98
|
+
if (message)
|
|
99
|
+
throw orgInvalidError(organization.id, message);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
assertBindOrgSuccess(result, organization.id);
|
|
103
|
+
const latest = this.v2Store.read();
|
|
104
|
+
if (!latest || latest.current_org?.id !== organization.id)
|
|
105
|
+
return;
|
|
106
|
+
latest.current_org = {
|
|
107
|
+
id: organization.id,
|
|
108
|
+
name: latest.current_org.name,
|
|
109
|
+
bind_expires_at: result.bind_expires_at ?? new Date(Date.now() + BIND_TTL_MS).toISOString(),
|
|
110
|
+
};
|
|
111
|
+
latest.client_id = GDHUB_CLI_CLIENT_ID;
|
|
112
|
+
this.v2Store.write(latest);
|
|
113
|
+
}
|
|
85
114
|
getCurrentOrgId() {
|
|
86
115
|
const v2 = this.v2Store.read();
|
|
87
116
|
if (v2?.current_org?.id)
|
|
@@ -112,7 +141,7 @@ export class OrgManager {
|
|
|
112
141
|
};
|
|
113
142
|
}
|
|
114
143
|
async listStructureOrgs() {
|
|
115
|
-
const raw = await this.hub.get(STRUCTURE_USER_ORGS_PATH);
|
|
144
|
+
const raw = await this.hub.get(STRUCTURE_USER_ORGS_PATH, { authRequirement: "credential" });
|
|
116
145
|
if (!Array.isArray(raw))
|
|
117
146
|
return [];
|
|
118
147
|
const seen = new Set();
|
|
@@ -180,13 +209,39 @@ function assertBindOrgSuccess(result, orgId) {
|
|
|
180
209
|
/成功|success|ok/i.test(text);
|
|
181
210
|
const hasFailureSignal = /(失败|错误|无效|不允许|未授权|无权限|没有权限|不存在|过期|fail|error|invalid|denied|forbidden|unauthorized|not found)/i
|
|
182
211
|
.test(text);
|
|
212
|
+
if (code?.toUpperCase() === "ORG_INVALID") {
|
|
213
|
+
throw orgInvalidError(orgId, message ?? code);
|
|
214
|
+
}
|
|
183
215
|
if (hasFailureSignal && !hasSuccessSignal) {
|
|
184
|
-
throw new Error(
|
|
216
|
+
throw new Error(`选择组织失败:${message ?? code ?? "服务端返回失败"}(orgId=${orgId})`);
|
|
185
217
|
}
|
|
186
218
|
if (code && !hasSuccessSignal) {
|
|
187
|
-
throw new Error(
|
|
219
|
+
throw new Error(`选择组织失败:${message ?? `服务端返回 code=${code}`}(orgId=${orgId})`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function explicitInvalidOrganizationMessage(error) {
|
|
223
|
+
if (!(error instanceof SsoApiError))
|
|
224
|
+
return undefined;
|
|
225
|
+
try {
|
|
226
|
+
const body = JSON.parse(error.body);
|
|
227
|
+
if (cleanString(body.code)?.toUpperCase() !== "ORG_INVALID")
|
|
228
|
+
return undefined;
|
|
229
|
+
return cleanString(body.message) ?? "当前组织不可用";
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return undefined;
|
|
188
233
|
}
|
|
189
234
|
}
|
|
235
|
+
function orgInvalidError(orgId, message) {
|
|
236
|
+
return new CliUsageError({
|
|
237
|
+
code: "ORG_INVALID",
|
|
238
|
+
type: "auth",
|
|
239
|
+
message: `选择组织失败:${message}(orgId=${orgId})`,
|
|
240
|
+
hint: "请通过 gd-cli org list 确认可用组织。",
|
|
241
|
+
retryable: false,
|
|
242
|
+
next_steps: ["gd-cli org list", "gd-cli org switch"],
|
|
243
|
+
});
|
|
244
|
+
}
|
|
190
245
|
function formatOrgChoice(org, currentOrgId) {
|
|
191
246
|
const product = org.product_name ? ` · ${org.product_name}` : "";
|
|
192
247
|
const current = org.id === currentOrgId ? " ← 当前" : "";
|