gaoding-cli 1.0.0-alpha.5
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 +88 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/SECURITY.md +34 -0
- package/dist/bin/gd-cli.d.ts +2 -0
- package/dist/bin/gd-cli.js +10 -0
- package/dist/src/api/app-runner.d.ts +33 -0
- package/dist/src/api/app-runner.js +1177 -0
- package/dist/src/api/dam-client.d.ts +111 -0
- package/dist/src/api/dam-client.js +897 -0
- package/dist/src/api/direct-auth.d.ts +3 -0
- package/dist/src/api/direct-auth.js +11 -0
- package/dist/src/api/direct-client.d.ts +27 -0
- package/dist/src/api/direct-client.js +151 -0
- package/dist/src/api/direct-tools.d.ts +43 -0
- package/dist/src/api/direct-tools.js +1155 -0
- package/dist/src/api/hub-client.d.ts +20 -0
- package/dist/src/api/hub-client.js +99 -0
- package/dist/src/api/sso-client.d.ts +60 -0
- package/dist/src/api/sso-client.js +140 -0
- package/dist/src/api/task-poller.d.ts +38 -0
- package/dist/src/api/task-poller.js +72 -0
- package/dist/src/api/tool-invoker.d.ts +12 -0
- package/dist/src/api/tool-invoker.js +11 -0
- package/dist/src/api/usage-client.d.ts +39 -0
- package/dist/src/api/usage-client.js +179 -0
- package/dist/src/auth/credential-store-v2.d.ts +12 -0
- package/dist/src/auth/credential-store-v2.js +59 -0
- package/dist/src/commands/auth.d.ts +2 -0
- package/dist/src/commands/auth.js +191 -0
- package/dist/src/commands/dam.d.ts +2 -0
- package/dist/src/commands/dam.js +293 -0
- package/dist/src/commands/models.d.ts +2 -0
- package/dist/src/commands/models.js +78 -0
- package/dist/src/commands/org.d.ts +2 -0
- package/dist/src/commands/org.js +137 -0
- package/dist/src/commands/skills.d.ts +2 -0
- package/dist/src/commands/skills.js +213 -0
- package/dist/src/commands/tools.d.ts +4 -0
- package/dist/src/commands/tools.js +383 -0
- package/dist/src/commands/update.d.ts +2 -0
- package/dist/src/commands/update.js +167 -0
- package/dist/src/commands/usage.d.ts +7 -0
- package/dist/src/commands/usage.js +43 -0
- package/dist/src/commands/workflows.d.ts +3 -0
- package/dist/src/commands/workflows.js +398 -0
- package/dist/src/core/auth/auth-provider.d.ts +8 -0
- package/dist/src/core/auth/auth-provider.js +12 -0
- package/dist/src/core/auth/device-oauth-provider.d.ts +12 -0
- package/dist/src/core/auth/device-oauth-provider.js +124 -0
- package/dist/src/core/auth/org-manager.d.ts +26 -0
- package/dist/src/core/auth/org-manager.js +224 -0
- package/dist/src/core/auth/signature.d.ts +19 -0
- package/dist/src/core/auth/signature.js +33 -0
- package/dist/src/core/auth/types.d.ts +52 -0
- package/dist/src/core/auth/types.js +3 -0
- package/dist/src/core/output/cli-error.d.ts +5 -0
- package/dist/src/core/output/cli-error.js +8 -0
- package/dist/src/core/output/detector.d.ts +5 -0
- package/dist/src/core/output/detector.js +14 -0
- package/dist/src/core/output/engine.d.ts +32 -0
- package/dist/src/core/output/engine.js +77 -0
- package/dist/src/core/output/envelope.d.ts +40 -0
- package/dist/src/core/output/envelope.js +119 -0
- package/dist/src/core/output/index.d.ts +4 -0
- package/dist/src/core/output/index.js +4 -0
- package/dist/src/core/output/live-display.d.ts +15 -0
- package/dist/src/core/output/live-display.js +111 -0
- package/dist/src/core/output/result-card.d.ts +18 -0
- package/dist/src/core/output/result-card.js +39 -0
- package/dist/src/core/prompt/index.d.ts +31 -0
- package/dist/src/core/prompt/index.js +36 -0
- package/dist/src/io.d.ts +14 -0
- package/dist/src/io.js +31 -0
- package/dist/src/middleware/auth.d.ts +3 -0
- package/dist/src/middleware/auth.js +74 -0
- package/dist/src/middleware/error-handler.d.ts +3 -0
- package/dist/src/middleware/error-handler.js +431 -0
- package/dist/src/middleware/interactive-params.d.ts +14 -0
- package/dist/src/middleware/interactive-params.js +53 -0
- package/dist/src/middleware/output.d.ts +14 -0
- package/dist/src/middleware/output.js +546 -0
- package/dist/src/models/model-catalog.d.ts +26 -0
- package/dist/src/models/model-catalog.js +216 -0
- package/dist/src/program.d.ts +2 -0
- package/dist/src/program.js +152 -0
- package/dist/src/registry/agent-contracts.d.ts +43 -0
- package/dist/src/registry/agent-contracts.js +483 -0
- package/dist/src/registry/app-loader.d.ts +2 -0
- package/dist/src/registry/app-loader.js +70 -0
- package/dist/src/registry/direct-registry.d.ts +11 -0
- package/dist/src/registry/direct-registry.js +234 -0
- package/dist/src/registry/registry-cache.d.ts +10 -0
- package/dist/src/registry/registry-cache.js +52 -0
- package/dist/src/registry/registry-client.d.ts +28 -0
- package/dist/src/registry/registry-client.js +50 -0
- package/dist/src/registry/types.d.ts +69 -0
- package/dist/src/registry/types.js +1 -0
- package/dist/src/tty/auth-presenter.d.ts +3 -0
- package/dist/src/tty/auth-presenter.js +21 -0
- package/dist/src/update/install-source.d.ts +19 -0
- package/dist/src/update/install-source.js +139 -0
- package/dist/src/update/notifier.d.ts +2 -0
- package/dist/src/update/notifier.js +59 -0
- package/dist/src/update/update-checker.d.ts +22 -0
- package/dist/src/update/update-checker.js +110 -0
- package/dist/src/update/version.d.ts +9 -0
- package/dist/src/update/version.js +52 -0
- package/dist/src/utils/agent-skill-installer.d.ts +57 -0
- package/dist/src/utils/agent-skill-installer.js +389 -0
- package/dist/src/utils/capability-metadata.d.ts +18 -0
- package/dist/src/utils/capability-metadata.js +88 -0
- package/dist/src/utils/capability-presenter.d.ts +4 -0
- package/dist/src/utils/capability-presenter.js +190 -0
- package/dist/src/utils/config.d.ts +16 -0
- package/dist/src/utils/config.js +54 -0
- package/dist/src/utils/credential-permissions.d.ts +2 -0
- package/dist/src/utils/credential-permissions.js +22 -0
- package/dist/src/utils/ecommerce-input-guidance.d.ts +15 -0
- package/dist/src/utils/ecommerce-input-guidance.js +85 -0
- package/dist/src/utils/endpoints.d.ts +52 -0
- package/dist/src/utils/endpoints.js +258 -0
- package/dist/src/utils/enrich-upload-params.d.ts +6 -0
- package/dist/src/utils/enrich-upload-params.js +11 -0
- package/dist/src/utils/entitlement.d.ts +17 -0
- package/dist/src/utils/entitlement.js +146 -0
- package/dist/src/utils/gd-home.d.ts +7 -0
- package/dist/src/utils/gd-home.js +26 -0
- package/dist/src/utils/help-verify.d.ts +9 -0
- package/dist/src/utils/help-verify.js +41 -0
- package/dist/src/utils/input-json.d.ts +3 -0
- package/dist/src/utils/input-json.js +61 -0
- package/dist/src/utils/list-output-format.d.ts +10 -0
- package/dist/src/utils/list-output-format.js +34 -0
- package/dist/src/utils/logger.d.ts +6 -0
- package/dist/src/utils/logger.js +19 -0
- package/dist/src/utils/matting-compose.d.ts +10 -0
- package/dist/src/utils/matting-compose.js +45 -0
- package/dist/src/utils/open-browser.d.ts +1 -0
- package/dist/src/utils/open-browser.js +4 -0
- package/dist/src/utils/output-path.d.ts +5 -0
- package/dist/src/utils/output-path.js +70 -0
- package/dist/src/utils/redact.d.ts +3 -0
- package/dist/src/utils/redact.js +96 -0
- package/dist/src/utils/reference-image.d.ts +8 -0
- package/dist/src/utils/reference-image.js +43 -0
- package/dist/src/utils/resolve-app-local-images.d.ts +8 -0
- package/dist/src/utils/resolve-app-local-images.js +84 -0
- package/dist/src/utils/sensitive-path.d.ts +6 -0
- package/dist/src/utils/sensitive-path.js +69 -0
- package/dist/src/utils/settings-store.d.ts +42 -0
- package/dist/src/utils/settings-store.js +50 -0
- package/dist/src/utils/sleep.d.ts +1 -0
- package/dist/src/utils/sleep.js +3 -0
- package/dist/src/utils/spinner.d.ts +2 -0
- package/dist/src/utils/spinner.js +4 -0
- package/dist/src/utils/static-help.d.ts +2 -0
- package/dist/src/utils/static-help.js +3 -0
- package/dist/src/utils/tool-display.d.ts +10 -0
- package/dist/src/utils/tool-display.js +55 -0
- package/dist/src/utils/transient-network-error.d.ts +4 -0
- package/dist/src/utils/transient-network-error.js +40 -0
- package/dist/src/utils/url-safety.d.ts +6 -0
- package/dist/src/utils/url-safety.js +67 -0
- package/dist/src/utils/write-app-deliverables.d.ts +18 -0
- package/dist/src/utils/write-app-deliverables.js +386 -0
- package/package.json +91 -0
- package/skills/gd-cli/SKILL.md +57 -0
- package/skills/gd-cli/references/auth.md +31 -0
- package/skills/gd-cli/references/deliverables.md +22 -0
- package/skills/gd-cli/references/entitlement.md +21 -0
- package/skills/gd-cli/references/errors.md +19 -0
- package/skills/gd-cli/references/org.md +18 -0
- package/skills/gd-cli/references/sop.md +25 -0
- package/skills/gd-cli/references/tools.md +29 -0
- package/skills/gd-cli/references/update.md +16 -0
- package/skills/gd-cli/references/workflows.md +22 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { findPackageRoot } from "../update/install-source.js";
|
|
6
|
+
import { CLI_VERSION } from "./config.js";
|
|
7
|
+
export class AgentSkillOperationError extends Error {
|
|
8
|
+
result;
|
|
9
|
+
constructor(message, result) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.result = result;
|
|
12
|
+
this.name = "AgentSkillOperationError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const SKILL_NAME = "gd-cli";
|
|
16
|
+
const MARKER_FILE = ".gd-cli.json";
|
|
17
|
+
const TARGET_IDS = [
|
|
18
|
+
"universal",
|
|
19
|
+
"claude-code",
|
|
20
|
+
"codex",
|
|
21
|
+
"cursor",
|
|
22
|
+
"opencode",
|
|
23
|
+
"openclaw",
|
|
24
|
+
"workbuddy",
|
|
25
|
+
];
|
|
26
|
+
export class AgentSkillInstaller {
|
|
27
|
+
homeDir;
|
|
28
|
+
env;
|
|
29
|
+
platform;
|
|
30
|
+
sourceDir;
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
this.homeDir = options.homeDir ?? os.homedir();
|
|
33
|
+
this.env = options.env ?? process.env;
|
|
34
|
+
this.platform = options.platform ?? process.platform;
|
|
35
|
+
const packageRoot = options.packageRoot
|
|
36
|
+
?? findPackageRoot(fileURLToPath(import.meta.url));
|
|
37
|
+
if (!packageRoot) {
|
|
38
|
+
throw new Error("无法定位 gaoding-cli package root");
|
|
39
|
+
}
|
|
40
|
+
this.sourceDir = path.join(packageRoot, "skills", SKILL_NAME);
|
|
41
|
+
const sourceSkill = path.join(this.sourceDir, "SKILL.md");
|
|
42
|
+
const sourceStat = lstat(sourceSkill);
|
|
43
|
+
if (!sourceStat?.isFile()) {
|
|
44
|
+
throw new Error(`Agent Skill source 不存在: ${sourceSkill}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
listTargets() {
|
|
48
|
+
const home = this.homeDir;
|
|
49
|
+
return [
|
|
50
|
+
this.target("universal", "通用 Agent Skills", path.join(home, ".agents"), undefined),
|
|
51
|
+
this.target("claude-code", "Claude Code", this.env.CLAUDE_HOME || path.join(home, ".claude"), "claude"),
|
|
52
|
+
this.target("codex", "Codex", this.env.CODEX_HOME || path.join(home, ".codex"), "codex"),
|
|
53
|
+
this.target("cursor", "Cursor", this.env.CURSOR_HOME || path.join(home, ".cursor"), "cursor"),
|
|
54
|
+
this.target("opencode", "OpenCode", this.env.OPENCODE_HOME || path.join(home, ".config", "opencode"), "opencode"),
|
|
55
|
+
this.target("openclaw", "OpenClaw", this.env.OPENCLAW_HOME || path.join(home, ".openclaw"), "openclaw"),
|
|
56
|
+
this.target("workbuddy", "WorkBuddy", path.join(home, ".workbuddy"), "workbuddy"),
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
detectedTargets() {
|
|
60
|
+
return this.listTargets()
|
|
61
|
+
.filter((target) => target.id !== "universal" && target.detected)
|
|
62
|
+
.map((target) => target.id);
|
|
63
|
+
}
|
|
64
|
+
status() {
|
|
65
|
+
return this.result(TARGET_IDS, new Set());
|
|
66
|
+
}
|
|
67
|
+
install(agents) {
|
|
68
|
+
const ids = uniqueTargets(["universal", ...agents]);
|
|
69
|
+
const targets = this.targets(ids);
|
|
70
|
+
const preflight = targets.map((target) => this.inspect(target));
|
|
71
|
+
const conflicts = preflight.filter((target) => target.status === "conflict");
|
|
72
|
+
if (conflicts.length > 0) {
|
|
73
|
+
throw new AgentSkillOperationError(conflictMessage("安装", conflicts), this.result(ids, new Set()));
|
|
74
|
+
}
|
|
75
|
+
const changed = new Set();
|
|
76
|
+
let active;
|
|
77
|
+
try {
|
|
78
|
+
const canonical = targets[0];
|
|
79
|
+
active = canonical;
|
|
80
|
+
const canonicalStatus = this.inspect(canonical);
|
|
81
|
+
if (canonicalStatus.status === "missing"
|
|
82
|
+
|| canonicalStatus.status === "outdated") {
|
|
83
|
+
this.replaceManagedDirectory(canonical.path);
|
|
84
|
+
changed.add(canonical.id);
|
|
85
|
+
}
|
|
86
|
+
for (const target of targets.slice(1)) {
|
|
87
|
+
active = target;
|
|
88
|
+
const current = this.inspect(target);
|
|
89
|
+
if (current.status === "missing") {
|
|
90
|
+
this.createLinkOrCopy(target);
|
|
91
|
+
changed.add(target.id);
|
|
92
|
+
}
|
|
93
|
+
else if (current.status === "outdated" && current.mode === "copy") {
|
|
94
|
+
this.replaceManagedDirectory(target.path);
|
|
95
|
+
changed.add(target.id);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return this.result(ids, changed);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error instanceof AgentSkillOperationError)
|
|
102
|
+
throw error;
|
|
103
|
+
throw this.ioError("安装 Agent Skill 失败", ids, changed, active, error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
update() {
|
|
107
|
+
const canonical = this.targetById("universal");
|
|
108
|
+
const canonicalStatus = this.inspect(canonical);
|
|
109
|
+
if (canonicalStatus.status !== "installed"
|
|
110
|
+
&& canonicalStatus.status !== "outdated") {
|
|
111
|
+
throw new AgentSkillOperationError("canonical Skill 未安装或不是 gd-cli 托管,请先执行 gd-cli skills install", this.status());
|
|
112
|
+
}
|
|
113
|
+
const changed = new Set();
|
|
114
|
+
let active;
|
|
115
|
+
try {
|
|
116
|
+
active = canonical;
|
|
117
|
+
if (canonicalStatus.status === "outdated") {
|
|
118
|
+
this.replaceManagedDirectory(canonical.path);
|
|
119
|
+
changed.add(canonical.id);
|
|
120
|
+
}
|
|
121
|
+
for (const target of this.listTargets().slice(1)) {
|
|
122
|
+
active = target;
|
|
123
|
+
const current = this.inspect(target);
|
|
124
|
+
if (current.status === "outdated" && current.mode === "copy") {
|
|
125
|
+
this.replaceManagedDirectory(target.path);
|
|
126
|
+
changed.add(target.id);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return this.result(TARGET_IDS, changed);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (error instanceof AgentSkillOperationError)
|
|
133
|
+
throw error;
|
|
134
|
+
throw this.ioError("更新 Agent Skill 失败", TARGET_IDS, changed, active, error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
uninstall(agents) {
|
|
138
|
+
const ids = uniqueTargets(agents);
|
|
139
|
+
const targets = this.targets(ids);
|
|
140
|
+
const preflight = targets.map((target) => this.inspect(target));
|
|
141
|
+
const conflicts = preflight.filter((target) => target.status === "conflict");
|
|
142
|
+
if (conflicts.length > 0) {
|
|
143
|
+
throw new AgentSkillOperationError(conflictMessage("卸载", conflicts), this.result(ids, new Set()));
|
|
144
|
+
}
|
|
145
|
+
if (ids.includes("universal")) {
|
|
146
|
+
const selected = new Set(ids);
|
|
147
|
+
const blockers = this.listTargets()
|
|
148
|
+
.slice(1)
|
|
149
|
+
.map((target) => this.inspect(target))
|
|
150
|
+
.filter((target) => (target.status === "installed" || target.status === "outdated")
|
|
151
|
+
&& !selected.has(target.agent));
|
|
152
|
+
if (blockers.length > 0) {
|
|
153
|
+
const resultIds = uniqueTargets([
|
|
154
|
+
...ids,
|
|
155
|
+
...blockers.map((target) => target.agent),
|
|
156
|
+
]);
|
|
157
|
+
throw new AgentSkillOperationError(`不能卸载 universal:这些 Agent 仍依赖 canonical Skill:${blockers.map((item) => item.agent).join(", ")}`, this.result(resultIds, new Set()));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const changed = new Set();
|
|
161
|
+
const ordered = [
|
|
162
|
+
...targets.filter((target) => target.id !== "universal"),
|
|
163
|
+
...targets.filter((target) => target.id === "universal"),
|
|
164
|
+
];
|
|
165
|
+
let active;
|
|
166
|
+
try {
|
|
167
|
+
for (const target of ordered) {
|
|
168
|
+
active = target;
|
|
169
|
+
const current = this.inspect(target);
|
|
170
|
+
if (current.status === "installed" || current.status === "outdated") {
|
|
171
|
+
fs.rmSync(target.path, { recursive: true, force: false });
|
|
172
|
+
changed.add(target.id);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return this.result(ids, changed);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (error instanceof AgentSkillOperationError)
|
|
179
|
+
throw error;
|
|
180
|
+
throw this.ioError("卸载 Agent Skill 失败", ids, changed, active, error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
target(id, name, root, executable) {
|
|
184
|
+
return {
|
|
185
|
+
id,
|
|
186
|
+
name,
|
|
187
|
+
path: path.join(root, "skills", SKILL_NAME),
|
|
188
|
+
detected: id === "universal"
|
|
189
|
+
|| Boolean(lstat(root))
|
|
190
|
+
|| Boolean(executable && hasExecutable(this.env.PATH, executable, this.platform)),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
targetById(id) {
|
|
194
|
+
const target = this.listTargets().find((item) => item.id === id);
|
|
195
|
+
if (!target)
|
|
196
|
+
throw new Error(`不支持的 Agent target: ${id}`);
|
|
197
|
+
return target;
|
|
198
|
+
}
|
|
199
|
+
targets(ids) {
|
|
200
|
+
return ids.map((id) => this.targetById(id));
|
|
201
|
+
}
|
|
202
|
+
inspect(target) {
|
|
203
|
+
const stat = lstat(target.path);
|
|
204
|
+
if (!stat) {
|
|
205
|
+
return targetResult(target, "missing");
|
|
206
|
+
}
|
|
207
|
+
if (target.id === "universal") {
|
|
208
|
+
if (stat.isSymbolicLink()) {
|
|
209
|
+
return targetResult(target, "conflict", undefined, "canonical 不能是链接");
|
|
210
|
+
}
|
|
211
|
+
if (!stat.isDirectory()) {
|
|
212
|
+
return targetResult(target, "conflict", undefined, "canonical 路径不是目录");
|
|
213
|
+
}
|
|
214
|
+
const version = readMarkerVersion(target.path);
|
|
215
|
+
if (!version) {
|
|
216
|
+
return targetResult(target, "conflict", undefined, "canonical 缺少合法 .gd-cli.json");
|
|
217
|
+
}
|
|
218
|
+
return targetResult(target, version === CLI_VERSION ? "installed" : "outdated", "canonical");
|
|
219
|
+
}
|
|
220
|
+
if (stat.isSymbolicLink()) {
|
|
221
|
+
let resolved;
|
|
222
|
+
try {
|
|
223
|
+
const link = fs.readlinkSync(target.path);
|
|
224
|
+
resolved = path.resolve(path.dirname(target.path), link);
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return targetResult(target, "conflict", this.linkMode(), `无法读取链接: ${errorMessage(error)}`);
|
|
228
|
+
}
|
|
229
|
+
const canonical = this.targetById("universal").path;
|
|
230
|
+
if (!samePath(resolved, canonical, this.platform)) {
|
|
231
|
+
return targetResult(target, "conflict", this.linkMode(), "链接未指向 canonical Skill");
|
|
232
|
+
}
|
|
233
|
+
if (!lstat(canonical)) {
|
|
234
|
+
return targetResult(target, "broken", this.linkMode(), "链接指向 canonical Skill,但 canonical 不存在");
|
|
235
|
+
}
|
|
236
|
+
return targetResult(target, "installed", this.linkMode());
|
|
237
|
+
}
|
|
238
|
+
if (!stat.isDirectory()) {
|
|
239
|
+
return targetResult(target, "conflict", undefined, "target 路径不是目录或链接");
|
|
240
|
+
}
|
|
241
|
+
const version = readMarkerVersion(target.path);
|
|
242
|
+
if (!version) {
|
|
243
|
+
return targetResult(target, "conflict", undefined, "目录缺少合法 .gd-cli.json");
|
|
244
|
+
}
|
|
245
|
+
return targetResult(target, version === CLI_VERSION ? "installed" : "outdated", "copy");
|
|
246
|
+
}
|
|
247
|
+
result(ids, changedTargets, reasonOverrides = new Map()) {
|
|
248
|
+
const targets = this.targets(ids).map((target) => {
|
|
249
|
+
const inspected = this.inspect(target);
|
|
250
|
+
const reason = reasonOverrides.get(target.id) ?? inspected.reason;
|
|
251
|
+
return {
|
|
252
|
+
...inspected,
|
|
253
|
+
changed: changedTargets.has(target.id),
|
|
254
|
+
...(reason ? { reason } : {}),
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
const changed = targets.some((target) => target.changed);
|
|
258
|
+
return {
|
|
259
|
+
skill: "gd-cli",
|
|
260
|
+
version: CLI_VERSION,
|
|
261
|
+
canonical_path: this.targetById("universal").path,
|
|
262
|
+
changed,
|
|
263
|
+
restart_required: changed,
|
|
264
|
+
targets,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
replaceManagedDirectory(destination) {
|
|
268
|
+
const parent = path.dirname(destination);
|
|
269
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
270
|
+
const stagingRoot = fs.mkdtempSync(path.join(parent, ".gd-cli-stage-"));
|
|
271
|
+
const staged = path.join(stagingRoot, SKILL_NAME);
|
|
272
|
+
try {
|
|
273
|
+
fs.cpSync(this.sourceDir, staged, { recursive: true });
|
|
274
|
+
fs.writeFileSync(path.join(staged, MARKER_FILE), JSON.stringify({ version: CLI_VERSION }), "utf8");
|
|
275
|
+
if (lstat(destination)) {
|
|
276
|
+
fs.rmSync(destination, { recursive: true, force: false });
|
|
277
|
+
}
|
|
278
|
+
fs.renameSync(staged, destination);
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
fs.rmSync(stagingRoot, { recursive: true, force: true });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
createLinkOrCopy(target) {
|
|
285
|
+
const canonical = this.targetById("universal").path;
|
|
286
|
+
fs.mkdirSync(path.dirname(target.path), { recursive: true });
|
|
287
|
+
try {
|
|
288
|
+
if (this.platform === "win32") {
|
|
289
|
+
fs.symlinkSync(canonical, target.path, "junction");
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
const relative = path.relative(path.dirname(target.path), canonical);
|
|
293
|
+
fs.symlinkSync(relative, target.path);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
if (lstat(target.path))
|
|
298
|
+
throw error;
|
|
299
|
+
this.replaceManagedDirectory(target.path);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
linkMode() {
|
|
303
|
+
return this.platform === "win32" ? "junction" : "symlink";
|
|
304
|
+
}
|
|
305
|
+
ioError(message, ids, changed, active, cause) {
|
|
306
|
+
const detail = errorMessage(cause);
|
|
307
|
+
const reasons = active
|
|
308
|
+
? new Map([[active.id, detail]])
|
|
309
|
+
: new Map();
|
|
310
|
+
return new AgentSkillOperationError(`${message}${active ? ` (${active.path})` : ""}: ${detail}`, this.result(ids, changed, reasons));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function targetResult(target, status, mode, reason) {
|
|
314
|
+
return {
|
|
315
|
+
agent: target.id,
|
|
316
|
+
path: target.path,
|
|
317
|
+
status,
|
|
318
|
+
...(mode ? { mode } : {}),
|
|
319
|
+
changed: false,
|
|
320
|
+
...(reason ? { reason } : {}),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function uniqueTargets(ids) {
|
|
324
|
+
const unique = [];
|
|
325
|
+
for (const id of ids) {
|
|
326
|
+
if (!TARGET_IDS.includes(id)) {
|
|
327
|
+
throw new Error(`不支持的 Agent target: ${String(id)}`);
|
|
328
|
+
}
|
|
329
|
+
if (!unique.includes(id))
|
|
330
|
+
unique.push(id);
|
|
331
|
+
}
|
|
332
|
+
return unique;
|
|
333
|
+
}
|
|
334
|
+
function readMarkerVersion(directory) {
|
|
335
|
+
try {
|
|
336
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(directory, MARKER_FILE), "utf8"));
|
|
337
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
const marker = parsed;
|
|
341
|
+
if (Object.keys(marker).length !== 1)
|
|
342
|
+
return undefined;
|
|
343
|
+
if (typeof marker.version !== "string" || !marker.version.trim()) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
return marker.version;
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function lstat(file) {
|
|
353
|
+
try {
|
|
354
|
+
return fs.lstatSync(file);
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
const code = error.code;
|
|
358
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
359
|
+
return undefined;
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function samePath(left, right, platform) {
|
|
364
|
+
const normalizedLeft = path.resolve(left);
|
|
365
|
+
const normalizedRight = path.resolve(right);
|
|
366
|
+
return platform === "win32"
|
|
367
|
+
? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase()
|
|
368
|
+
: normalizedLeft === normalizedRight;
|
|
369
|
+
}
|
|
370
|
+
function hasExecutable(pathValue, executable, platform) {
|
|
371
|
+
if (!pathValue)
|
|
372
|
+
return false;
|
|
373
|
+
const suffixes = platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
374
|
+
return pathValue.split(path.delimiter).some((directory) => suffixes.some((suffix) => {
|
|
375
|
+
try {
|
|
376
|
+
fs.accessSync(path.join(directory, `${executable}${suffix}`), fs.constants.X_OK);
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
}));
|
|
383
|
+
}
|
|
384
|
+
function conflictMessage(operation, conflicts) {
|
|
385
|
+
return `无法${operation} Agent Skill,以下路径存在冲突:${conflicts.map((item) => item.path).join(", ")}`;
|
|
386
|
+
}
|
|
387
|
+
function errorMessage(error) {
|
|
388
|
+
return error instanceof Error ? error.message : String(error);
|
|
389
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type CapabilitySource = "built-in" | "remote" | "custom" | "local";
|
|
2
|
+
export type CapabilityStatus = "ready" | "login-required" | "org-required" | "unauthorized" | "quota-limited" | "error" | "deprecated" | "updating";
|
|
3
|
+
export type CapabilityPolicy = "safe" | "standard" | "costly" | "batch" | "long-running" | "restricted" | "experimental";
|
|
4
|
+
export interface CapabilityMetadata {
|
|
5
|
+
source: CapabilitySource;
|
|
6
|
+
status: CapabilityStatus;
|
|
7
|
+
policy: CapabilityPolicy;
|
|
8
|
+
}
|
|
9
|
+
export declare const SOURCE_LABELS: Record<CapabilitySource, string>;
|
|
10
|
+
export declare const STATUS_LABELS: Record<CapabilityStatus, string>;
|
|
11
|
+
export declare const POLICY_LABELS: Record<CapabilityPolicy, string>;
|
|
12
|
+
export declare function capabilityMetadata(kind: "tool" | "app" | "skill" | "dam" | "org", id: string): CapabilityMetadata;
|
|
13
|
+
export declare function policyFor(kind: "tool" | "app" | "skill" | "dam" | "org", id: string): CapabilityPolicy;
|
|
14
|
+
export declare function toolPolicy(id: string): CapabilityPolicy;
|
|
15
|
+
export declare function appPolicy(id: string): CapabilityPolicy;
|
|
16
|
+
export declare function sourceLabel(value: unknown): string;
|
|
17
|
+
export declare function statusLabel(value: unknown): string;
|
|
18
|
+
export declare function policyLabel(value: unknown): string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const SOURCE_LABELS = {
|
|
2
|
+
"built-in": "内置",
|
|
3
|
+
remote: "云端",
|
|
4
|
+
custom: "自定义",
|
|
5
|
+
local: "本地",
|
|
6
|
+
};
|
|
7
|
+
export const STATUS_LABELS = {
|
|
8
|
+
ready: "启用",
|
|
9
|
+
"login-required": "需登录",
|
|
10
|
+
"org-required": "需选择团队",
|
|
11
|
+
unauthorized: "无权限",
|
|
12
|
+
"quota-limited": "额度不足",
|
|
13
|
+
error: "异常",
|
|
14
|
+
deprecated: "已弃用",
|
|
15
|
+
updating: "更新中",
|
|
16
|
+
};
|
|
17
|
+
export const POLICY_LABELS = {
|
|
18
|
+
safe: "安全",
|
|
19
|
+
standard: "标准",
|
|
20
|
+
costly: "高成本",
|
|
21
|
+
batch: "批量",
|
|
22
|
+
"long-running": "长任务",
|
|
23
|
+
restricted: "受限",
|
|
24
|
+
experimental: "实验",
|
|
25
|
+
};
|
|
26
|
+
export function capabilityMetadata(kind, id) {
|
|
27
|
+
return {
|
|
28
|
+
source: "built-in",
|
|
29
|
+
status: "ready",
|
|
30
|
+
policy: policyFor(kind, id),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function policyFor(kind, id) {
|
|
34
|
+
if (kind === "app")
|
|
35
|
+
return appPolicy(id);
|
|
36
|
+
if (kind === "tool")
|
|
37
|
+
return toolPolicy(id);
|
|
38
|
+
return "safe";
|
|
39
|
+
}
|
|
40
|
+
export function toolPolicy(id) {
|
|
41
|
+
if (id === "asset.upload" || id === "text.title" || id === "text.prompt") {
|
|
42
|
+
return "safe";
|
|
43
|
+
}
|
|
44
|
+
if (id === "video.generate")
|
|
45
|
+
return "costly";
|
|
46
|
+
return "standard";
|
|
47
|
+
}
|
|
48
|
+
export function appPolicy(id) {
|
|
49
|
+
void id;
|
|
50
|
+
return "batch";
|
|
51
|
+
}
|
|
52
|
+
export function sourceLabel(value) {
|
|
53
|
+
return SOURCE_LABELS[asSource(value)];
|
|
54
|
+
}
|
|
55
|
+
export function statusLabel(value) {
|
|
56
|
+
return STATUS_LABELS[asStatus(value)];
|
|
57
|
+
}
|
|
58
|
+
export function policyLabel(value) {
|
|
59
|
+
return POLICY_LABELS[asPolicy(value)];
|
|
60
|
+
}
|
|
61
|
+
function asSource(value) {
|
|
62
|
+
return value === "remote" || value === "custom" || value === "local"
|
|
63
|
+
? value
|
|
64
|
+
: "built-in";
|
|
65
|
+
}
|
|
66
|
+
function asStatus(value) {
|
|
67
|
+
if (value === "login-required" ||
|
|
68
|
+
value === "org-required" ||
|
|
69
|
+
value === "unauthorized" ||
|
|
70
|
+
value === "quota-limited" ||
|
|
71
|
+
value === "error" ||
|
|
72
|
+
value === "deprecated" ||
|
|
73
|
+
value === "updating") {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
return "ready";
|
|
77
|
+
}
|
|
78
|
+
function asPolicy(value) {
|
|
79
|
+
if (value === "safe" ||
|
|
80
|
+
value === "costly" ||
|
|
81
|
+
value === "batch" ||
|
|
82
|
+
value === "long-running" ||
|
|
83
|
+
value === "restricted" ||
|
|
84
|
+
value === "experimental") {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
return "standard";
|
|
88
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export function printCapabilitySchema(payload) {
|
|
3
|
+
const id = stringValue(payload.id);
|
|
4
|
+
const type = stringValue(payload.type);
|
|
5
|
+
const namespace = namespaceForType(type);
|
|
6
|
+
const title = stringValue(payload.title) || id;
|
|
7
|
+
const category = stringValue(payload.category);
|
|
8
|
+
const description = stringValue(payload.description);
|
|
9
|
+
console.log(chalk.bold(`${title} (${id})`));
|
|
10
|
+
console.log(`类型: ${labelForType(type)}`);
|
|
11
|
+
if (category)
|
|
12
|
+
console.log(`分类: ${category}`);
|
|
13
|
+
if (description)
|
|
14
|
+
console.log(`说明: ${description}`);
|
|
15
|
+
const inputSchema = recordValue(payload.input_schema);
|
|
16
|
+
const outputSchema = recordValue(payload.output_schema);
|
|
17
|
+
console.log("");
|
|
18
|
+
printSchemaFields("输入参数", inputSchema, "input");
|
|
19
|
+
console.log("");
|
|
20
|
+
printSchemaFields("输出字段", outputSchema, "output");
|
|
21
|
+
const sampleInput = recordValue(payload.sample_input) ?? buildSampleInput(inputSchema);
|
|
22
|
+
if (sampleInput && Object.keys(sampleInput).length > 0) {
|
|
23
|
+
console.log("");
|
|
24
|
+
console.log(chalk.bold("最小 input.json:"));
|
|
25
|
+
console.log(indentBlock(JSON.stringify(sampleInput, null, 2), " "));
|
|
26
|
+
}
|
|
27
|
+
const stdin = recordValue(payload.stdin);
|
|
28
|
+
const stdinCommand = stringValue(stdin?.command);
|
|
29
|
+
const canonicalCommand = stringValue(payload.canonical_command);
|
|
30
|
+
const fallbackCommand = type === "workflow"
|
|
31
|
+
? `gd-cli workflows run ${id} --input input.json --format json`
|
|
32
|
+
: `gd-cli ${namespace} call ${id} --input input.json --format json`;
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log(chalk.bold("调用方式:"));
|
|
35
|
+
console.log(` ${canonicalCommand ?? fallbackCommand}`);
|
|
36
|
+
if (stdinCommand)
|
|
37
|
+
console.log(` ${stdinCommand}`);
|
|
38
|
+
console.log("");
|
|
39
|
+
console.log(chalk.bold("Agent:"));
|
|
40
|
+
console.log(` gd-cli ${namespace} schema ${id} --format json`);
|
|
41
|
+
console.log(` gd-cli ${namespace} examples ${id} --format json`);
|
|
42
|
+
}
|
|
43
|
+
export function printCapabilityExamples(payload) {
|
|
44
|
+
const id = stringValue(payload.id);
|
|
45
|
+
const type = stringValue(payload.type);
|
|
46
|
+
const namespace = namespaceForType(type);
|
|
47
|
+
const title = stringValue(payload.title) || id;
|
|
48
|
+
const category = stringValue(payload.category);
|
|
49
|
+
const description = stringValue(payload.description);
|
|
50
|
+
const examples = Array.isArray(payload.examples)
|
|
51
|
+
? payload.examples.filter(isRecord)
|
|
52
|
+
: [];
|
|
53
|
+
console.log(chalk.bold(`${title} (${id})`));
|
|
54
|
+
console.log(`类型: ${labelForType(type)}`);
|
|
55
|
+
if (category)
|
|
56
|
+
console.log(`分类: ${category}`);
|
|
57
|
+
if (description)
|
|
58
|
+
console.log(`说明: ${description}`);
|
|
59
|
+
if (examples.length === 0) {
|
|
60
|
+
console.log("\n暂无示例。");
|
|
61
|
+
}
|
|
62
|
+
examples.forEach((example, index) => {
|
|
63
|
+
const description = stringValue(example.description) || `示例 ${index + 1}`;
|
|
64
|
+
const command = stringValue(example.command);
|
|
65
|
+
const inputJson = recordValue(example.input_json);
|
|
66
|
+
console.log("");
|
|
67
|
+
console.log(chalk.bold(`示例 ${index + 1}: ${description}`));
|
|
68
|
+
if (command)
|
|
69
|
+
console.log(` ${command}`);
|
|
70
|
+
if (inputJson && Object.keys(inputJson).length > 0) {
|
|
71
|
+
console.log("");
|
|
72
|
+
console.log(" input.json:");
|
|
73
|
+
console.log(indentBlock(JSON.stringify(inputJson, null, 2), " "));
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
console.log("");
|
|
77
|
+
console.log(chalk.bold("Agent:"));
|
|
78
|
+
console.log(` gd-cli ${namespace} examples ${id} --format json`);
|
|
79
|
+
console.log(` gd-cli ${namespace} schema ${id} --format json`);
|
|
80
|
+
}
|
|
81
|
+
function namespaceForType(type) {
|
|
82
|
+
if (type === "workflow")
|
|
83
|
+
return "workflows";
|
|
84
|
+
if (type === "skill")
|
|
85
|
+
return "skills";
|
|
86
|
+
return "tools";
|
|
87
|
+
}
|
|
88
|
+
function labelForType(type) {
|
|
89
|
+
if (type === "workflow")
|
|
90
|
+
return "Workflow 场景";
|
|
91
|
+
if (type === "skill")
|
|
92
|
+
return "Skill 说明";
|
|
93
|
+
return "Tool 能力";
|
|
94
|
+
}
|
|
95
|
+
function printSchemaFields(title, schema, kind) {
|
|
96
|
+
console.log(chalk.bold(`${title}:`));
|
|
97
|
+
const properties = recordValue(schema?.properties);
|
|
98
|
+
const required = new Set(Array.isArray(schema?.required)
|
|
99
|
+
? schema.required.map((item) => String(item))
|
|
100
|
+
: []);
|
|
101
|
+
if (!properties || Object.keys(properties).length === 0) {
|
|
102
|
+
console.log(" 无");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const names = Object.keys(properties);
|
|
106
|
+
const width = Math.max(...names.map((name) => name.length), 4);
|
|
107
|
+
for (const name of names) {
|
|
108
|
+
const prop = recordValue(properties[name]) ?? {};
|
|
109
|
+
const type = typeName(prop);
|
|
110
|
+
const requirement = kind === "input" ? (required.has(name) ? "必填" : "可选") : "";
|
|
111
|
+
const flag = stringValue(prop["x-cli-flag"]);
|
|
112
|
+
const parts = [type, requirement, flag].filter(Boolean);
|
|
113
|
+
console.log(` ${name.padEnd(width)} ${parts.join(" ")}`);
|
|
114
|
+
const description = stringValue(prop.description);
|
|
115
|
+
if (description)
|
|
116
|
+
console.log(` ${description}`);
|
|
117
|
+
const details = [];
|
|
118
|
+
const format = stringValue(prop.format);
|
|
119
|
+
if (format)
|
|
120
|
+
details.push(`format: ${format}`);
|
|
121
|
+
if (Array.isArray(prop.enum) && prop.enum.length > 0) {
|
|
122
|
+
details.push(`enum: ${prop.enum.map((item) => String(item)).join(" | ")}`);
|
|
123
|
+
}
|
|
124
|
+
if (prop.default !== undefined)
|
|
125
|
+
details.push(`default: ${String(prop.default)}`);
|
|
126
|
+
if (details.length > 0)
|
|
127
|
+
console.log(` ${details.join("; ")}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function buildSampleInput(schema) {
|
|
131
|
+
const properties = recordValue(schema?.properties);
|
|
132
|
+
const required = Array.isArray(schema?.required)
|
|
133
|
+
? schema.required.map((item) => String(item))
|
|
134
|
+
: [];
|
|
135
|
+
if (!properties || required.length === 0)
|
|
136
|
+
return undefined;
|
|
137
|
+
const sample = {};
|
|
138
|
+
for (const name of required) {
|
|
139
|
+
const prop = recordValue(properties[name]) ?? {};
|
|
140
|
+
sample[name] = sampleValue(name, prop);
|
|
141
|
+
}
|
|
142
|
+
return sample;
|
|
143
|
+
}
|
|
144
|
+
function sampleValue(name, prop) {
|
|
145
|
+
if (prop.default !== undefined)
|
|
146
|
+
return prop.default;
|
|
147
|
+
if (Array.isArray(prop.enum) && prop.enum.length > 0)
|
|
148
|
+
return prop.enum[0];
|
|
149
|
+
const n = name.toLowerCase();
|
|
150
|
+
const type = stringValue(prop.type);
|
|
151
|
+
if (type === "boolean")
|
|
152
|
+
return true;
|
|
153
|
+
if (type === "number" || type === "integer")
|
|
154
|
+
return 1;
|
|
155
|
+
if (n.includes("image"))
|
|
156
|
+
return "./sku.png";
|
|
157
|
+
if (n.includes("assets_dir"))
|
|
158
|
+
return "./images";
|
|
159
|
+
if (n === "input")
|
|
160
|
+
return "./products.csv";
|
|
161
|
+
if (n.includes("duration"))
|
|
162
|
+
return "5";
|
|
163
|
+
if (n.includes("ratio"))
|
|
164
|
+
return "1:1";
|
|
165
|
+
if (n.includes("brief"))
|
|
166
|
+
return "夏季防晒衣,轻薄透气,适合户外通勤";
|
|
167
|
+
if (n.includes("prompt"))
|
|
168
|
+
return "生成一张干净高级的电商商品主图";
|
|
169
|
+
if (n.includes("file"))
|
|
170
|
+
return "./image.png";
|
|
171
|
+
return "value";
|
|
172
|
+
}
|
|
173
|
+
function indentBlock(value, prefix) {
|
|
174
|
+
return value.split("\n").map((line) => `${prefix}${line}`).join("\n");
|
|
175
|
+
}
|
|
176
|
+
function typeName(prop) {
|
|
177
|
+
const type = prop.type;
|
|
178
|
+
if (Array.isArray(type))
|
|
179
|
+
return type.map((item) => String(item)).join("|");
|
|
180
|
+
return stringValue(type) || "unknown";
|
|
181
|
+
}
|
|
182
|
+
function recordValue(value) {
|
|
183
|
+
return isRecord(value) ? value : undefined;
|
|
184
|
+
}
|
|
185
|
+
function isRecord(value) {
|
|
186
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
187
|
+
}
|
|
188
|
+
function stringValue(value) {
|
|
189
|
+
return typeof value === "string" ? value : "";
|
|
190
|
+
}
|