@gaoding/cli 0.0.0 → 1.0.0

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 (136) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -3
  3. package/contracts/operations/agent.send/input.schema.json +353 -0
  4. package/contracts/operations/agent.send/output.schema.json +271 -0
  5. package/contracts/operations/auth.status/input.schema.json +7 -0
  6. package/contracts/operations/auth.status/output.schema.json +132 -0
  7. package/contracts/operations/dam.copy/input.schema.json +19 -0
  8. package/contracts/operations/dam.copy/output.schema.json +19 -0
  9. package/contracts/operations/dam.delete/input.schema.json +23 -0
  10. package/contracts/operations/dam.delete/output.schema.json +6 -0
  11. package/contracts/operations/dam.download/input.schema.json +14 -0
  12. package/contracts/operations/dam.download/output.schema.json +25 -0
  13. package/contracts/operations/dam.folder.list/input.schema.json +15 -0
  14. package/contracts/operations/dam.folder.list/output.schema.json +37 -0
  15. package/contracts/operations/dam.get/input.schema.json +16 -0
  16. package/contracts/operations/dam.get/output.schema.json +46 -0
  17. package/contracts/operations/dam.list/input.schema.json +44 -0
  18. package/contracts/operations/dam.list/output.schema.json +71 -0
  19. package/contracts/operations/dam.move/input.schema.json +19 -0
  20. package/contracts/operations/dam.move/output.schema.json +18 -0
  21. package/contracts/operations/dam.recycle.list/input.schema.json +13 -0
  22. package/contracts/operations/dam.recycle.list/output.schema.json +34 -0
  23. package/contracts/operations/dam.recycle.restore/input.schema.json +14 -0
  24. package/contracts/operations/dam.recycle.restore/output.schema.json +6 -0
  25. package/contracts/operations/dam.rename/input.schema.json +18 -0
  26. package/contracts/operations/dam.rename/output.schema.json +18 -0
  27. package/contracts/operations/dam.repository.list/input.schema.json +8 -0
  28. package/contracts/operations/dam.repository.list/output.schema.json +52 -0
  29. package/contracts/operations/dam.search/input.schema.json +46 -0
  30. package/contracts/operations/dam.search/output.schema.json +71 -0
  31. package/contracts/operations/dam.tag.list/input.schema.json +12 -0
  32. package/contracts/operations/dam.tag.list/output.schema.json +26 -0
  33. package/contracts/operations/dam.upload/input.schema.json +31 -0
  34. package/contracts/operations/dam.upload/output.schema.json +46 -0
  35. package/contracts/operations/editor.apply/input.schema.json +134 -0
  36. package/contracts/operations/editor.apply/output.schema.json +15 -0
  37. package/contracts/operations/editor.connect/input.schema.json +15 -0
  38. package/contracts/operations/editor.connect/output.schema.json +17 -0
  39. package/contracts/operations/editor.disconnect/input.schema.json +8 -0
  40. package/contracts/operations/editor.disconnect/output.schema.json +14 -0
  41. package/contracts/operations/editor.save/input.schema.json +8 -0
  42. package/contracts/operations/editor.save/output.schema.json +23 -0
  43. package/contracts/operations/editor.screenshot/input.schema.json +8 -0
  44. package/contracts/operations/editor.screenshot/output.schema.json +16 -0
  45. package/contracts/operations/editor.snapshot/input.schema.json +8 -0
  46. package/contracts/operations/editor.snapshot/output.schema.json +113 -0
  47. package/contracts/operations/model.get/input.schema.json +18 -0
  48. package/contracts/operations/model.get/output.schema.json +191 -0
  49. package/contracts/operations/model.list/input.schema.json +15 -0
  50. package/contracts/operations/model.list/output.schema.json +111 -0
  51. package/contracts/operations/org.current/input.schema.json +7 -0
  52. package/contracts/operations/org.current/output.schema.json +49 -0
  53. package/contracts/operations/org.list/input.schema.json +7 -0
  54. package/contracts/operations/org.list/output.schema.json +71 -0
  55. package/contracts/operations/tool.call/input.schema.json +35 -0
  56. package/contracts/operations/tool.call/output.schema.json +129 -0
  57. package/contracts/operations/tool.list/input.schema.json +7 -0
  58. package/contracts/operations/tool.list/output.schema.json +45 -0
  59. package/dist/bin/gd-cli.js +60 -0
  60. package/dist/bin/postinstall.js +42 -0
  61. package/dist/src/bootstrap/create-cli.js +112 -0
  62. package/dist/src/bootstrap/create-runtime.js +290 -0
  63. package/dist/src/bootstrap/validators.js +157 -0
  64. package/dist/src/cli/action-binding.js +109 -0
  65. package/dist/src/cli/agent-commands.js +49 -0
  66. package/dist/src/cli/auth-commands.js +55 -0
  67. package/dist/src/cli/dam-commands.js +469 -0
  68. package/dist/src/cli/editor-commands.js +73 -0
  69. package/dist/src/cli/errors.js +157 -0
  70. package/dist/src/cli/model-commands.js +61 -0
  71. package/dist/src/cli/org-commands.js +58 -0
  72. package/dist/src/cli/presenter.js +105 -0
  73. package/dist/src/cli/prompt.js +30 -0
  74. package/dist/src/cli/tool-commands.js +77 -0
  75. package/dist/src/cli/update-command.js +14 -0
  76. package/dist/src/contracts/schema.js +4 -0
  77. package/dist/src/features/agent/creative-agent-adapter.js +36 -0
  78. package/dist/src/features/agent/creative-protocol.js +432 -0
  79. package/dist/src/features/agent/creative-stream.js +127 -0
  80. package/dist/src/features/agent/use-cases.js +150 -0
  81. package/dist/src/features/auth/access-policy.js +101 -0
  82. package/dist/src/features/auth/credential-store.js +62 -0
  83. package/dist/src/features/auth/sso-service.js +194 -0
  84. package/dist/src/features/auth/state.js +129 -0
  85. package/dist/src/features/auth/use-cases.js +136 -0
  86. package/dist/src/features/dam/asset-projection.js +278 -0
  87. package/dist/src/features/dam/dam-api-adapter.js +562 -0
  88. package/dist/src/features/dam/file-downloader.js +242 -0
  89. package/dist/src/features/dam/object-storage.js +193 -0
  90. package/dist/src/features/dam/operation-executor.js +72 -0
  91. package/dist/src/features/dam/registered-uploader.js +247 -0
  92. package/dist/src/features/dam/repository-catalog.js +127 -0
  93. package/dist/src/features/dam/storage-upload.js +141 -0
  94. package/dist/src/features/dam/transient-uploader.js +17 -0
  95. package/dist/src/features/dam/use-cases.js +470 -0
  96. package/dist/src/features/editor/bridge-client.js +109 -0
  97. package/dist/src/features/editor/bridge-process.js +222 -0
  98. package/dist/src/features/editor/bridge-server.js +337 -0
  99. package/dist/src/features/editor/protocol.js +1 -0
  100. package/dist/src/features/editor/public-url.js +36 -0
  101. package/dist/src/features/editor/session-state.js +78 -0
  102. package/dist/src/features/editor/session.js +207 -0
  103. package/dist/src/features/editor/use-cases.js +88 -0
  104. package/dist/src/features/org/org-service.js +76 -0
  105. package/dist/src/features/org/use-cases.js +156 -0
  106. package/dist/src/features/skill/bundled-skills.js +55 -0
  107. package/dist/src/features/skill/installer.js +271 -0
  108. package/dist/src/features/tool/catalog.js +83 -0
  109. package/dist/src/features/tool/dynamic-schema.js +128 -0
  110. package/dist/src/features/tool/mns-catalog-adapter.js +265 -0
  111. package/dist/src/features/tool/tool-api-adapter.js +352 -0
  112. package/dist/src/features/tool/use-cases.js +177 -0
  113. package/dist/src/features/update/latest-version.js +37 -0
  114. package/dist/src/features/update/update-notification.js +262 -0
  115. package/dist/src/features/update/update-service.js +183 -0
  116. package/dist/src/platform/json-input.js +67 -0
  117. package/dist/src/platform/local-json-file.js +59 -0
  118. package/dist/src/platform/open-browser.js +8 -0
  119. package/dist/src/platform/redact.js +80 -0
  120. package/dist/src/platform/remote-error-evidence.js +80 -0
  121. package/dist/src/platform/remote-protocol.js +10 -0
  122. package/dist/src/platform/safe-upload-file.js +146 -0
  123. package/dist/src/platform/signature.js +20 -0
  124. package/dist/src/platform/signed-http-transport.js +143 -0
  125. package/dist/src/platform/url-safety.js +100 -0
  126. package/dist/src/telemetry/invocation.js +156 -0
  127. package/dist/src/telemetry/sls-sink.js +31 -0
  128. package/package.json +54 -8
  129. package/skills/gd-cli/SKILL.md +15 -0
  130. package/skills/gd-cli/references/auth-org.md +9 -0
  131. package/skills/gd-cli/references/creation.md +115 -0
  132. package/skills/gd-cli/references/dam.md +52 -0
  133. package/skills/gd-cli/references/editor.md +254 -0
  134. package/skills/gd-cli/references/errors.md +13 -0
  135. package/skills/gd-cli/references/update.md +13 -0
  136. package/index.js +0 -3
@@ -0,0 +1,157 @@
1
+ import { CommanderError } from "commander";
2
+ import { AuthExpiredError, AuthRequiredError } from "../features/auth/access-policy.js";
3
+ import { AuthStateInvalidError } from "../features/auth/state.js";
4
+ import { SsoRequestError } from "../features/auth/sso-service.js";
5
+ import { DeviceAuthorizationExpiredError } from "../features/auth/use-cases.js";
6
+ import { AgentInputError } from "../features/agent/use-cases.js";
7
+ import { ToolInputError } from "../features/tool/catalog.js";
8
+ import { ToolTaskFailedError } from "../features/tool/tool-api-adapter.js";
9
+ import { ObjectStorageUploadError } from "../features/dam/object-storage.js";
10
+ import { DamAssetNotDownloadableError, DamAssetNotFoundError, DamFolderNotFoundError } from "../features/dam/dam-api-adapter.js";
11
+ import { DamDownloadDestinationExistsError } from "../features/dam/file-downloader.js";
12
+ import { DamOperationFailedError } from "../features/dam/operation-executor.js";
13
+ import { DamInputError, DamOperationUnconfirmedError, DamRepositoryNotFoundError } from "../features/dam/use-cases.js";
14
+ import { OrganizationResponseError } from "../features/org/org-service.js";
15
+ import { OrganizationBindFailedError, OrganizationInvalidError, OrgUsageError } from "../features/org/use-cases.js";
16
+ import { RemoteRequestError } from "../platform/signed-http-transport.js";
17
+ import { RemoteProtocolError } from "../platform/remote-protocol.js";
18
+ import { InputContractError, OutputContractError } from "../bootstrap/validators.js";
19
+ import { JsonInputError } from "../platform/json-input.js";
20
+ import { LocalFileInputError } from "../platform/safe-upload-file.js";
21
+ import { EditorBridgeError } from "../features/editor/bridge-client.js";
22
+ import { EditorNotReadyError, EditorSessionNotFoundError, EditorTargetError } from "../features/editor/session.js";
23
+ import { UpdateError } from "../features/update/update-service.js";
24
+ export class CliUsageError extends Error {
25
+ constructor() {
26
+ super("命令或参数无效。");
27
+ this.name = "CliUsageError";
28
+ }
29
+ }
30
+ const INSUFFICIENT_CREDITS_REMOTE_CODE = "12020770";
31
+ const INSUFFICIENT_CREDITS_NEXT_STEPS = [
32
+ "前往 https://www.gaoding.art/pricing 购买稿豆",
33
+ "也可以执行 gd-cli org switch 切换到有可用稿豆的组织",
34
+ "处理后重新发起本次创作"
35
+ ];
36
+ export function isHelpOutcome(error) {
37
+ return error instanceof CommanderError
38
+ && error.exitCode === 0
39
+ && (error.code === "commander.helpDisplayed" || error.code === "commander.version");
40
+ }
41
+ export function mapCliError(error, options) {
42
+ if (options.aborted)
43
+ return { kind: "interrupted", exitCode: 130 };
44
+ if (error instanceof AuthRequiredError) {
45
+ return failure("AUTH_REQUIRED", "请先执行 gd-cli auth login", 1, ["gd-cli auth login"]);
46
+ }
47
+ if (error instanceof AuthExpiredError) {
48
+ return failure("AUTH_EXPIRED", "登录已过期,请退出后重新登录。", 1, ["gd-cli auth logout", "gd-cli auth login"]);
49
+ }
50
+ if (error instanceof AuthStateInvalidError) {
51
+ return failure("AUTH_STATE_INVALID", "本地登录状态已失效,请退出后重新登录。", 1, ["gd-cli auth logout", "gd-cli auth login"]);
52
+ }
53
+ if (error instanceof OrganizationInvalidError) {
54
+ return failure("ORG_INVALID", "所选组织不可用,请重新选择组织。", 1, ["gd-cli org switch"]);
55
+ }
56
+ if (error instanceof UpdateError) {
57
+ return failure(error.code, error.message, 1, error.nextSteps);
58
+ }
59
+ if (error instanceof EditorTargetError) {
60
+ return failure(error.code, error.message, 2);
61
+ }
62
+ if (error instanceof OrgUsageError
63
+ || error instanceof CliUsageError
64
+ || error instanceof JsonInputError
65
+ || error instanceof InputContractError
66
+ || error instanceof AgentInputError
67
+ || error instanceof ToolInputError
68
+ || error instanceof DamInputError
69
+ || error instanceof LocalFileInputError
70
+ || error instanceof CommanderError) {
71
+ return failure("USAGE_ERROR", "命令或参数无效,请查看 --help。", 2);
72
+ }
73
+ if (isInsufficientCreditsError(error)) {
74
+ return failure("INSUFFICIENT_CREDITS", "当前组织稿豆不足,无法完成本次创作。", 1, INSUFFICIENT_CREDITS_NEXT_STEPS);
75
+ }
76
+ if (error instanceof RemoteProtocolError) {
77
+ return failure("REMOTE_PROTOCOL_INCOMPATIBLE", "稿定服务返回了当前 CLI 无法安全处理的结果;为避免重复创作,已停止处理。", 1);
78
+ }
79
+ if (error instanceof ToolTaskFailedError) {
80
+ return failure("TOOL_TASK_FAILED", "稿定工具任务执行失败。", 1);
81
+ }
82
+ if (error instanceof DamAssetNotFoundError) {
83
+ return failure("DAM_ASSET_NOT_FOUND", "未找到指定的 DAM 素材。", 1);
84
+ }
85
+ if (error instanceof DamFolderNotFoundError) {
86
+ return failure("DAM_FOLDER_NOT_FOUND", "未找到指定的 DAM 文件夹。", 1);
87
+ }
88
+ if (error instanceof DamAssetNotDownloadableError) {
89
+ return failure("DAM_ASSET_NOT_DOWNLOADABLE", "该素材当前没有可下载的源文件;请先查看素材状态。", 1, ["gd-cli dam get <asset-id>"]);
90
+ }
91
+ if (error instanceof DamDownloadDestinationExistsError) {
92
+ return failure("DAM_DOWNLOAD_DESTINATION_EXISTS", "目标文件已存在,未覆盖任何文件;请更换输出目录或移走同名文件。", 1);
93
+ }
94
+ if (error instanceof DamOperationUnconfirmedError) {
95
+ return failure("DAM_OPERATION_UNCONFIRMED", "操作已提交,但未能确认最终结果;请先查询目标位置,不要立即重复提交。", 1);
96
+ }
97
+ if (error instanceof DamOperationFailedError) {
98
+ return failure("DAM_OPERATION_FAILED", "DAM 操作执行失败;请先按诊断 ID 查询原因,不要立即重复提交。", 1);
99
+ }
100
+ if (error instanceof DamRepositoryNotFoundError) {
101
+ return failure("DAM_REPOSITORY_NOT_FOUND", "当前组织没有可用的 DAM 资源库。", 1, ["gd-cli org list", "gd-cli org switch --org <org-id>"]);
102
+ }
103
+ if (error instanceof RemoteRequestError
104
+ || error instanceof SsoRequestError
105
+ || error instanceof DeviceAuthorizationExpiredError
106
+ || error instanceof OrganizationResponseError
107
+ || error instanceof OrganizationBindFailedError
108
+ || error instanceof ObjectStorageUploadError) {
109
+ return failure("REMOTE_REQUEST_FAILED", "稿定服务请求失败。", 1);
110
+ }
111
+ if (error instanceof OutputContractError) {
112
+ return failure("INTERNAL_ERROR", "CLI 内部结果校验失败。", 1);
113
+ }
114
+ if (error instanceof EditorBridgeError
115
+ || error instanceof EditorSessionNotFoundError
116
+ || error instanceof EditorNotReadyError) {
117
+ return failure(error.code, error.message, 1);
118
+ }
119
+ return failure("INTERNAL_ERROR", "CLI 发生内部错误。", 1);
120
+ }
121
+ function isInsufficientCreditsError(error) {
122
+ return (error instanceof RemoteRequestError
123
+ || error instanceof RemoteProtocolError
124
+ || error instanceof ToolTaskFailedError) && error.remoteCode === INSUFFICIENT_CREDITS_REMOTE_CODE;
125
+ }
126
+ export function classifyTelemetryError(error) {
127
+ const outcome = mapCliError(error, { aborted: false });
128
+ const remoteError = error instanceof RemoteRequestError
129
+ || error instanceof RemoteProtocolError
130
+ || error instanceof ToolTaskFailedError
131
+ ? error
132
+ : undefined;
133
+ return {
134
+ outcome: outcome.kind === "interrupted" ? "interrupted" : "failure",
135
+ ...(outcome.kind === "failure" ? { errorCode: outcome.failure.code } : {}),
136
+ ...(error instanceof RemoteRequestError && error.status !== undefined
137
+ ? { httpStatus: error.status }
138
+ : {}),
139
+ ...(remoteError?.remoteCode === undefined
140
+ ? {}
141
+ : { remoteErrorCode: remoteError.remoteCode }),
142
+ ...(remoteError?.remoteBody === undefined
143
+ ? {}
144
+ : { remoteErrorBody: remoteError.remoteBody })
145
+ };
146
+ }
147
+ function failure(code, message, exitCode, nextSteps) {
148
+ return {
149
+ kind: "failure",
150
+ failure: {
151
+ code,
152
+ message,
153
+ ...(nextSteps ? { details: { next_steps: nextSteps } } : {})
154
+ },
155
+ exitCode
156
+ };
157
+ }
@@ -0,0 +1,61 @@
1
+ import { bindAction } from "./action-binding.js";
2
+ import { CliUsageError } from "./errors.js";
3
+ export function registerModelCommands(program, runtime) {
4
+ const model = program.command("model")
5
+ .description("创作模型")
6
+ .helpCommand(false);
7
+ const list = model.command("list")
8
+ .description("列出可用模型")
9
+ .option("--tool <tool>", "按工具筛选")
10
+ .allowExcessArguments(false);
11
+ bindAction(list, "organization", async (context, signal) => {
12
+ const selectedTool = list.opts().tool;
13
+ const input = selectedTool === undefined ? {} : { tool: selectedTool };
14
+ await runtime.telemetry.stage("input", async () => {
15
+ runtime.validators.modelListInput(input);
16
+ });
17
+ const result = await runtime.tool.listModels({
18
+ input,
19
+ access: {
20
+ credential: context.state.credential,
21
+ organizationId: context.state.organization.id
22
+ },
23
+ signal
24
+ });
25
+ if (selectedTool !== undefined)
26
+ runtime.telemetry.annotate({ tool_name: selectedTool });
27
+ await runtime.telemetry.stage("output", async () => {
28
+ runtime.validators.modelListOutput(result);
29
+ runtime.io.writeOut(`${JSON.stringify(result)}\n`);
30
+ });
31
+ }, { operation: "model.list" });
32
+ const get = model.command("get")
33
+ .description("查看模型完整信息")
34
+ .argument("<model>", "模型机器标识")
35
+ .allowExcessArguments(false);
36
+ bindAction(get, "organization", async (context, signal) => {
37
+ const modelName = get.processedArgs[0];
38
+ if (typeof modelName !== "string" || modelName.trim() === "")
39
+ throw new CliUsageError();
40
+ const input = { model: modelName };
41
+ await runtime.telemetry.stage("input", async () => {
42
+ runtime.validators.modelGetInput(input);
43
+ });
44
+ const access = {
45
+ credential: context.state.credential,
46
+ organizationId: context.state.organization.id
47
+ };
48
+ const result = await runtime.tool.getModel({
49
+ input,
50
+ access,
51
+ signal
52
+ });
53
+ runtime.telemetry.annotate({ model_id: modelName });
54
+ await runtime.telemetry.stage("output", async () => {
55
+ runtime.validators.modelGetOutput(result);
56
+ runtime.io.writeOut(`${JSON.stringify(result)}\n`);
57
+ });
58
+ }, {
59
+ operation: "model.get"
60
+ });
61
+ }
@@ -0,0 +1,58 @@
1
+ import { bindAction } from "./action-binding.js";
2
+ import { OrgUsageError } from "../features/org/use-cases.js";
3
+ import { orgCurrentView, orgListView } from "./presenter.js";
4
+ export function registerOrgCommands(program, runtime) {
5
+ const org = program.command("org")
6
+ .description("组织选择")
7
+ .helpCommand(false);
8
+ const list = leaf(org.command("list"))
9
+ .description("列出可用组织")
10
+ .option("--json", "输出 JSON");
11
+ bindAction(list, "credential", async (context, signal) => {
12
+ const result = await runtime.org.list({ state: context.state, signal });
13
+ await runtime.telemetry.stage("output", async () => {
14
+ runtime.validators.orgList(result);
15
+ runtime.presenter.result(result, {
16
+ json: list.opts().json === true,
17
+ view: orgListView
18
+ });
19
+ });
20
+ }, { operation: "org.list" });
21
+ const current = leaf(org.command("current"))
22
+ .description("查看当前组织")
23
+ .option("--json", "输出 JSON");
24
+ bindAction(current, "none", async () => {
25
+ const result = await runtime.org.current();
26
+ await runtime.telemetry.stage("output", async () => {
27
+ runtime.validators.orgCurrent(result);
28
+ runtime.presenter.result(result, {
29
+ json: current.opts().json === true,
30
+ view: orgCurrentView
31
+ });
32
+ });
33
+ }, { operation: "org.current" });
34
+ const switchCommand = leaf(org.command("switch"))
35
+ .description("切换当前组织")
36
+ .option("--org <org-id>", "指定组织 ID");
37
+ bindAction(switchCommand, "credential", async (context, signal) => {
38
+ const organizationId = switchCommand.opts().org;
39
+ if (organizationId === undefined && !isInteractive(runtime))
40
+ throw new OrgUsageError();
41
+ const record = await runtime.org.switchOrganization({
42
+ state: context.state,
43
+ ...(organizationId === undefined
44
+ ? { select: (records) => runtime.prompt.choose(records, signal) }
45
+ : { organizationId }),
46
+ signal
47
+ });
48
+ await runtime.telemetry.stage("output", async () => {
49
+ runtime.presenter.success(`已切换到${record.publicName} (${record.id})。`);
50
+ });
51
+ }, { operation: "org.switch" });
52
+ }
53
+ function leaf(command) {
54
+ return command.allowExcessArguments(false);
55
+ }
56
+ function isInteractive(runtime) {
57
+ return runtime.io.inputTerminal && runtime.io.stderrTerminal;
58
+ }
@@ -0,0 +1,105 @@
1
+ import * as QRCode from "qrcode";
2
+ export function createPresenter(options) {
3
+ const qrCode = options.qrCode ?? ((text) => QRCode.toString(text, {
4
+ type: "terminal",
5
+ small: true
6
+ }));
7
+ return {
8
+ result(value, presentation) {
9
+ options.writeOut(presentation.json
10
+ ? `${JSON.stringify(value)}\n`
11
+ : `${presentation.view(value)}\n`);
12
+ },
13
+ success(message) {
14
+ options.writeOut(`${message}\n`);
15
+ },
16
+ async authorization(input) {
17
+ options.writeError(`授权地址: ${input.url.href}\n`);
18
+ if (input.terminal)
19
+ options.writeError(await qrCode(input.url.href));
20
+ },
21
+ notice(message) {
22
+ options.writeError(`提示: ${message}\n`);
23
+ },
24
+ warning(message) {
25
+ options.writeError(`警告: ${message}\n`);
26
+ },
27
+ error(outcome, presentation) {
28
+ if (outcome.kind === "interrupted") {
29
+ options.writeError("已取消。\n");
30
+ return;
31
+ }
32
+ if (presentation.json) {
33
+ options.writeError(`${JSON.stringify({
34
+ error: {
35
+ ...outcome.failure,
36
+ ...(presentation.traceId
37
+ ? { trace_id: presentation.traceId }
38
+ : {})
39
+ }
40
+ })}\n`);
41
+ return;
42
+ }
43
+ options.writeError(`${outcome.failure.code}: ${outcome.failure.message}\n`);
44
+ const nextSteps = nextStepsFrom(outcome.failure.details);
45
+ if (nextSteps.length > 0) {
46
+ options.writeError(`下一步: ${nextSteps.join(",")}\n`);
47
+ }
48
+ if (presentation.traceId) {
49
+ options.writeError(`诊断 ID: ${presentation.traceId}\n`);
50
+ }
51
+ }
52
+ };
53
+ }
54
+ export const authStatusView = (value) => {
55
+ const status = value;
56
+ if (!status.logged_in)
57
+ return lines("未登录", nextStepsLine(status.next_steps));
58
+ return lines("已登录", `账号: ${status.account.name ?? "未记录"}${status.account.user_id ? ` (${status.account.user_id})` : ""}`, status.organization
59
+ ? `组织: ${status.organization.name} (${status.organization.id})`
60
+ : "组织: 未选择", `凭证: ${status.credential.valid ? "有效" : "已过期"},到期 ${status.credential.expires_at}`, status.credential.scope.length > 0
61
+ ? `权限: ${status.credential.scope.join(", ")}`
62
+ : "权限: 未声明", nextStepsLine(status.next_steps));
63
+ };
64
+ export const orgListView = (value) => {
65
+ const result = value;
66
+ if (result.orgs.length === 0)
67
+ return "没有可用组织。";
68
+ return result.orgs.map((organization) => {
69
+ const product = organization.product_name ? ` · ${organization.product_name}` : "";
70
+ const current = organization.id === result.current_org_id ? " [当前]" : "";
71
+ return `${organization.name} (${organization.id})${product}${current}`;
72
+ }).join("\n");
73
+ };
74
+ export const orgCurrentView = (value) => {
75
+ const result = value;
76
+ return result.organization
77
+ ? `当前组织: ${result.organization.name} (${result.organization.id})`
78
+ : lines("尚未选择组织。", nextStepsLine(result.next_steps));
79
+ };
80
+ export const repositoryListView = (value) => {
81
+ const result = value;
82
+ if (result.repositories.length === 0)
83
+ return "没有可用资源库。";
84
+ return result.repositories.map((repository) => {
85
+ const permission = repository.permission_code === undefined
86
+ ? ""
87
+ : ` · ${repository.permission_code}`;
88
+ const defaultMarker = repository.is_default ? " [默认]" : "";
89
+ return `${repository.name} (${repository.repository_id}) · ${repository.type}${permission}${defaultMarker}`;
90
+ }).join("\n");
91
+ };
92
+ function lines(...values) {
93
+ return values.filter((value) => Boolean(value)).join("\n");
94
+ }
95
+ function nextStepsLine(nextSteps) {
96
+ return nextSteps.length > 0 ? `下一步: ${nextSteps.join(",")}` : undefined;
97
+ }
98
+ function nextStepsFrom(details) {
99
+ if (typeof details !== "object" || details === null || Array.isArray(details))
100
+ return [];
101
+ const nextSteps = details.next_steps;
102
+ return Array.isArray(nextSteps) && nextSteps.every((item) => typeof item === "string")
103
+ ? nextSteps
104
+ : [];
105
+ }
@@ -0,0 +1,30 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { OrganizationInvalidError } from "../features/org/use-cases.js";
3
+ export function createOrganizationPrompt(options) {
4
+ return {
5
+ async choose(records, signal) {
6
+ signal.throwIfAborted();
7
+ if (records.length === 0)
8
+ throw new OrganizationInvalidError();
9
+ const readline = createInterface({ input: options.input, output: options.output });
10
+ try {
11
+ options.output.write("请选择组织:\n");
12
+ records.forEach((record, index) => {
13
+ const product = record.productName ? ` · ${record.productName}` : "";
14
+ options.output.write(`${index + 1}. ${record.publicName} (${record.id})${product}\n`);
15
+ });
16
+ while (true) {
17
+ const answer = (await readline.question("输入序号: ", { signal })).trim();
18
+ const index = Number(answer) - 1;
19
+ if (Number.isInteger(index) && index >= 0 && index < records.length) {
20
+ return records[index].id;
21
+ }
22
+ options.output.write("请输入列表中的有效序号。\n");
23
+ }
24
+ }
25
+ finally {
26
+ readline.close();
27
+ }
28
+ }
29
+ };
30
+ }
@@ -0,0 +1,77 @@
1
+ import { bindAction, bindPreparedAction } from "./action-binding.js";
2
+ import { CliUsageError } from "./errors.js";
3
+ export function registerToolCommands(program, runtime) {
4
+ const tool = program.command("tool")
5
+ .description("创作工具")
6
+ .helpCommand(false);
7
+ const list = tool.command("list")
8
+ .description("列出可用工具")
9
+ .allowExcessArguments(false);
10
+ bindAction(list, "organization", async (context, signal) => {
11
+ const result = await runtime.tool.listTools({
12
+ access: {
13
+ credential: context.state.credential,
14
+ organizationId: context.state.organization.id
15
+ },
16
+ signal
17
+ });
18
+ await runtime.telemetry.stage("output", async () => {
19
+ runtime.validators.toolListOutput(result);
20
+ runtime.io.writeOut(`${JSON.stringify(result)}\n`);
21
+ });
22
+ }, { operation: "tool.list" });
23
+ const call = tool.command("call")
24
+ .description("调用创作工具")
25
+ .argument("<tool>", "工具机器标识")
26
+ .option("--schema", "输出当前工具所有模型参数并集的输入 Schema")
27
+ .option("--input <file|->", "从文件或标准输入读取参数 JSON")
28
+ .allowExcessArguments(false);
29
+ bindPreparedAction(call, "organization", async (signal) => {
30
+ signal.throwIfAborted();
31
+ const name = call.processedArgs[0];
32
+ if (typeof name !== "string" || name.trim() === "")
33
+ throw new CliUsageError();
34
+ const options = call.opts();
35
+ const modes = Number(options.schema === true) + Number(options.input !== undefined);
36
+ if (modes !== 1)
37
+ throw new CliUsageError();
38
+ if (options.schema === true) {
39
+ return { handled: false, input: { mode: "schema", name } };
40
+ }
41
+ const argumentsInput = await runtime.jsonInput.read(options.input, signal);
42
+ const input = { name, arguments: argumentsInput };
43
+ runtime.validators.toolCallInput(input);
44
+ return { handled: false, input: { mode: "call", input } };
45
+ }, async (context, prepared, signal) => {
46
+ const access = {
47
+ credential: context.state.credential,
48
+ organizationId: context.state.organization.id
49
+ };
50
+ if (prepared.mode === "schema") {
51
+ const schema = await runtime.tool.getArgumentsSchema({
52
+ name: prepared.name,
53
+ access,
54
+ signal
55
+ });
56
+ runtime.telemetry.annotate({ tool_name: prepared.name });
57
+ await runtime.telemetry.stage("output", async () => {
58
+ runtime.io.writeOut(`${JSON.stringify(schema)}\n`);
59
+ });
60
+ return;
61
+ }
62
+ const result = await runtime.tool.call({
63
+ input: prepared.input,
64
+ access,
65
+ signal
66
+ });
67
+ await runtime.telemetry.stage("output", async () => {
68
+ runtime.validators.toolCallOutput(result);
69
+ runtime.io.writeOut(`${JSON.stringify(result)}\n`);
70
+ });
71
+ }, {
72
+ operation: "tool.call",
73
+ mode: () => call.opts().schema === true
74
+ ? "schema"
75
+ : "execute"
76
+ });
77
+ }
@@ -0,0 +1,14 @@
1
+ import { bindAction } from "./action-binding.js";
2
+ export function registerUpdateCommand(program, runtime) {
3
+ const update = program.command("update")
4
+ .description("更新 GD CLI 与内置 Agent Skill")
5
+ .allowExcessArguments(false);
6
+ bindAction(update, "none", async (_context, signal) => {
7
+ const result = await runtime.update.run(signal);
8
+ await runtime.telemetry.stage("output", async () => {
9
+ runtime.presenter.success(result.updated
10
+ ? `gd-cli 已更新到 ${result.version},Agent Skill 已同步。`
11
+ : `gd-cli ${result.version} 已是最新版本,Agent Skill 已同步。`);
12
+ });
13
+ }, { operation: "update" });
14
+ }
@@ -0,0 +1,4 @@
1
+ import { readFileSync } from "node:fs";
2
+ export function contractSchema(operation, direction) {
3
+ return JSON.parse(readFileSync(new URL(`../../../contracts/operations/${operation}/${direction}.schema.json`, import.meta.url), "utf8"));
4
+ }
@@ -0,0 +1,36 @@
1
+ import { buildCreativeRequest, creativeContextId, creativeTurnMessages, normalizeCreativeMessages, restoreCreativeResourceUrls } from "./creative-protocol.js";
2
+ import { parseCreativeStream } from "./creative-stream.js";
3
+ export function createCreativeAgentAdapter(dependencies) {
4
+ return {
5
+ async complete(message, access, signal) {
6
+ signal.throwIfAborted();
7
+ const ids = message.contextId === undefined
8
+ ? {
9
+ localThreadId: dependencies.idFactory(),
10
+ localMessageId: dependencies.idFactory()
11
+ }
12
+ : { localMessageId: dependencies.idFactory() };
13
+ const streamed = await dependencies.telemetry.stage("execute", async () => {
14
+ const stream = await dependencies.transport.postStream({
15
+ path: "/ai-agent/v1/thread/completion",
16
+ body: buildCreativeRequest(message, ids),
17
+ accept: "text/event-stream",
18
+ credential: access.credential,
19
+ organizationId: access.organizationId,
20
+ signal
21
+ });
22
+ return parseCreativeStream(stream, signal);
23
+ });
24
+ const contextId = creativeContextId(streamed);
25
+ return dependencies.telemetry.stage("result", async () => {
26
+ const messages = await dependencies.transport.getJson({
27
+ path: `/ai-agent/v1/thread/${encodeURIComponent(contextId)}/messages`,
28
+ credential: access.credential,
29
+ organizationId: access.organizationId,
30
+ signal
31
+ });
32
+ return normalizeCreativeMessages(restoreCreativeResourceUrls(creativeTurnMessages(messages, ids.localMessageId), streamed));
33
+ });
34
+ }
35
+ };
36
+ }