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
|
@@ -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 ? " ← 当前" : "";
|
|
@@ -36,5 +36,6 @@ export interface ResultEnvelope<T = unknown> {
|
|
|
36
36
|
}
|
|
37
37
|
export declare function wrapEnvelope<T>(payload: T, meta?: Record<string, unknown>): ResultEnvelope<T>;
|
|
38
38
|
export declare function failedEnvelope(error: CliErrorBody, meta?: Record<string, unknown>): ResultEnvelope<null>;
|
|
39
|
+
export declare function publicCliErrorBody(error: CliErrorBody): CliErrorBody;
|
|
39
40
|
export declare function emitJson(data: unknown): void;
|
|
40
41
|
export declare function shouldEmitEnvelope(format: string, rootFormat?: string): boolean;
|
|
@@ -24,7 +24,7 @@ function normalizeEnvelopeStatus(value) {
|
|
|
24
24
|
return "completed";
|
|
25
25
|
}
|
|
26
26
|
export function failedEnvelope(error, meta) {
|
|
27
|
-
return { status: "failed", data: null, error, meta };
|
|
27
|
+
return { status: "failed", data: null, error: publicCliErrorBody(error), meta };
|
|
28
28
|
}
|
|
29
29
|
function normalizePayloadError(value) {
|
|
30
30
|
if (!value)
|
|
@@ -45,7 +45,7 @@ function normalizePayloadError(value) {
|
|
|
45
45
|
}
|
|
46
46
|
const error = value;
|
|
47
47
|
const type = error.type;
|
|
48
|
-
return {
|
|
48
|
+
return publicCliErrorBody({
|
|
49
49
|
code: typeof error.code === "string" ? error.code : "ERROR",
|
|
50
50
|
type: type === "business" ||
|
|
51
51
|
type === "auth" ||
|
|
@@ -54,11 +54,9 @@ function normalizePayloadError(value) {
|
|
|
54
54
|
type === "unknown"
|
|
55
55
|
? type
|
|
56
56
|
: "unknown",
|
|
57
|
-
message: typeof error.message === "string" ? error.message :
|
|
57
|
+
message: typeof error.message === "string" ? error.message : "请求失败",
|
|
58
58
|
reason: typeof error.reason === "string" ? error.reason : undefined,
|
|
59
59
|
hint: typeof error.hint === "string" ? error.hint : undefined,
|
|
60
|
-
app_id: typeof error.app_id === "string" ? error.app_id : undefined,
|
|
61
|
-
tool_id: typeof error.tool_id === "string" ? error.tool_id : undefined,
|
|
62
60
|
retryable: typeof error.retryable === "boolean" ? error.retryable : undefined,
|
|
63
61
|
next_command: typeof error.next_command === "string" ? error.next_command : undefined,
|
|
64
62
|
next_steps: Array.isArray(error.next_steps) ? error.next_steps.map(String) : undefined,
|
|
@@ -67,8 +65,80 @@ function normalizePayloadError(value) {
|
|
|
67
65
|
forbidden_actions: Array.isArray(error.forbidden_actions) ? error.forbidden_actions.map(String) : undefined,
|
|
68
66
|
allow_org_check: typeof error.allow_org_check === "boolean" ? error.allow_org_check : undefined,
|
|
69
67
|
agent_message_template: typeof error.agent_message_template === "string" ? error.agent_message_template : undefined,
|
|
70
|
-
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export function publicCliErrorBody(error) {
|
|
71
|
+
const body = {
|
|
72
|
+
code: publicString(error.code) ?? "ERROR",
|
|
73
|
+
type: publicErrorType(error.type),
|
|
74
|
+
message: publicString(error.message) ?? "请求失败",
|
|
75
|
+
reason: publicString(error.reason),
|
|
76
|
+
hint: publicString(error.hint),
|
|
77
|
+
retryable: typeof error.retryable === "boolean" ? error.retryable : undefined,
|
|
78
|
+
diagnostic_context: publicDiagnosticContext(error.diagnostic_context),
|
|
79
|
+
next_command: publicString(error.next_command),
|
|
80
|
+
next_steps: publicStringArray(error.next_steps),
|
|
81
|
+
pricing_url: publicString(error.pricing_url),
|
|
82
|
+
user_action_required: publicString(error.user_action_required),
|
|
83
|
+
forbidden_actions: publicStringArray(error.forbidden_actions),
|
|
84
|
+
allow_org_check: typeof error.allow_org_check === "boolean" ? error.allow_org_check : undefined,
|
|
85
|
+
agent_message_template: publicString(error.agent_message_template),
|
|
71
86
|
};
|
|
87
|
+
return Object.fromEntries(Object.entries(body).filter(([, value]) => value !== undefined));
|
|
88
|
+
}
|
|
89
|
+
const PUBLIC_DIAGNOSTIC_KEYS = new Set([
|
|
90
|
+
"environment",
|
|
91
|
+
"business_id",
|
|
92
|
+
"channel_id",
|
|
93
|
+
"org_id",
|
|
94
|
+
"org_name",
|
|
95
|
+
"user_id",
|
|
96
|
+
"api_path",
|
|
97
|
+
"selected_model",
|
|
98
|
+
"attempted_models",
|
|
99
|
+
]);
|
|
100
|
+
function publicDiagnosticContext(value) {
|
|
101
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
102
|
+
return undefined;
|
|
103
|
+
const entries = Object.entries(value)
|
|
104
|
+
.filter(([key]) => PUBLIC_DIAGNOSTIC_KEYS.has(key))
|
|
105
|
+
.flatMap(([key, item]) => {
|
|
106
|
+
const projected = publicDiagnosticValue(item);
|
|
107
|
+
return projected === undefined ? [] : [[key, projected]];
|
|
108
|
+
});
|
|
109
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
110
|
+
}
|
|
111
|
+
function publicDiagnosticValue(value) {
|
|
112
|
+
if (typeof value === "string" ||
|
|
113
|
+
typeof value === "number" ||
|
|
114
|
+
typeof value === "boolean") {
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(value)) {
|
|
118
|
+
const items = value.filter((item) => typeof item === "string" ||
|
|
119
|
+
typeof item === "number" ||
|
|
120
|
+
typeof item === "boolean");
|
|
121
|
+
return items.length > 0 ? items : undefined;
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
function publicString(value) {
|
|
126
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
127
|
+
}
|
|
128
|
+
function publicStringArray(value) {
|
|
129
|
+
if (!Array.isArray(value))
|
|
130
|
+
return undefined;
|
|
131
|
+
const items = value.filter((item) => typeof item === "string" && item.length > 0);
|
|
132
|
+
return items.length > 0 ? items : undefined;
|
|
133
|
+
}
|
|
134
|
+
function publicErrorType(value) {
|
|
135
|
+
return value === "business" ||
|
|
136
|
+
value === "auth" ||
|
|
137
|
+
value === "network" ||
|
|
138
|
+
value === "validation" ||
|
|
139
|
+
value === "unknown"
|
|
140
|
+
? value
|
|
141
|
+
: "unknown";
|
|
72
142
|
}
|
|
73
143
|
function extractAssets(obj) {
|
|
74
144
|
const out = [];
|