@my-life-buddies/cli 0.2.0 → 0.4.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.
- package/README.md +70 -53
- package/dist/bin/core.js +936 -447
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +147 -12
- package/dist/bin/preview.js.map +3 -3
- package/dist/web/app.css +8 -0
- package/dist/web/app.js +7 -2
- package/dist/web/index.html +11 -3
- package/dist/web/widget-delivery.js +28 -0
- package/dist/web/widgets.css +31 -0
- package/dist/web/widgets.js +127 -0
- package/package.json +3 -3
- package/resources/README.md +3 -1
- package/resources/agent-template/package-lock.json +4 -4
- package/resources/agent-template/package.json +1 -1
- package/resources/buddy-creator/INSTALL.md +2 -2
- package/resources/buddy-creator/PATCHES.md +21 -1
- package/resources/buddy-creator/SKILL.md +16 -6
- package/resources/buddy-creator/agents/openai.yaml +1 -1
- package/resources/buddy-creator/assets/preview/index.html +2 -0
- package/resources/buddy-creator/assets/preview/widgets.css +1 -0
- package/resources/buddy-creator/assets/preview/widgets.js +168 -0
- package/resources/buddy-creator/assets/widget-reference/conversation.png +0 -0
- package/resources/buddy-creator/assets/widget-reference/expanded.png +0 -0
- package/resources/buddy-creator/assets/widget-v2/app.js +62 -0
- package/resources/buddy-creator/assets/widget-v2/style.css +1 -0
- package/resources/buddy-creator/assets/widget-v3/app.js +65 -0
- package/resources/buddy-creator/assets/widget-v3/style.css +103 -0
- package/resources/buddy-creator/assets/widget-v4/app.js +66 -0
- package/resources/buddy-creator/assets/widget-v4/style.css +187 -0
- package/resources/buddy-creator/references/artifact-schema.md +2 -0
- package/resources/buddy-creator/references/host-guide.md +12 -6
- package/resources/buddy-creator/references/recovery.md +6 -3
- package/resources/buddy-creator/references/service.md +2 -0
- package/resources/buddy-creator/references/widgets-protocol.md +83 -0
- package/resources/buddy-creator/references/widgets.md +99 -0
- package/resources/buddy-creator/scripts/buddy_core.py +75 -13
- package/resources/buddy-creator/scripts/completion.py +64 -8
- package/resources/buddy-creator/scripts/preview.py +46 -6
- package/resources/buddy-creator/scripts/widget_render.py +66 -0
- package/resources/buddy-creator/scripts/widget_render_v1.py +176 -0
- package/resources/buddy-creator/scripts/widget_render_v2.py +98 -0
- package/resources/buddy-creator/scripts/widget_render_v3.py +86 -0
- package/resources/buddy-creator/scripts/widget_render_v4.py +121 -0
- package/resources/buddy-creator/scripts/widgets.py +227 -0
- package/resources/buddy-creator/version.json +1 -1
- package/resources/buddy-creator.manifest.json +121 -26
package/dist/bin/core.js
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
|
-
// packages/cli-core/src/
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// packages/cli-core/src/config/models.ts
|
|
2
|
+
var MODEL_IDS = Object.freeze([
|
|
3
|
+
"kimi-k2.6",
|
|
4
|
+
"kimi-k3",
|
|
5
|
+
"deepseek-v4-flash",
|
|
6
|
+
"deepseek-v4-pro"
|
|
7
|
+
]);
|
|
8
|
+
var DEFAULT_MODEL_ID = MODEL_IDS[0];
|
|
9
|
+
|
|
10
|
+
// packages/cli-core/src/commands/models.ts
|
|
11
|
+
var MODELS_COMMAND_HELP = `\u7528\u6CD5: buddy-cli models [--json]
|
|
12
|
+
\u67E5\u770B CLI \u5185\u7F6E\u7684\u6A21\u578B ID\uFF0C\u65E0\u9700\u767B\u5F55\u3001\u8054\u7F51\u6216 Agent \u5DE5\u7A0B\u3002
|
|
13
|
+
\u5C06\u6240\u9009 ID \u5199\u5165 buddy.agent.json.model\uFF1B\u6A21\u677F\u5728 initialState.model \u4E2D\u4F7F\u7528\u5B83\u3002`;
|
|
14
|
+
function runModelsCommand(args, io) {
|
|
15
|
+
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
16
|
+
io.write(MODELS_COMMAND_HELP);
|
|
17
|
+
} else if (args.length === 1 && args[0] === "--json") {
|
|
18
|
+
io.write(JSON.stringify({ models: MODEL_IDS }, null, 2));
|
|
19
|
+
} else if (args.length === 0) {
|
|
20
|
+
io.write("CLI \u5185\u7F6E\u6A21\u578B\u76EE\u5F55\uFF08Runtime 0.10.0\uFF09\uFF1A");
|
|
21
|
+
for (const id of MODEL_IDS) io.write(`- ${id}`);
|
|
22
|
+
io.write("\u5C06\u6240\u9009 ID \u5199\u5165 buddy.agent.json.model\uFF1B\u672A\u8BBE\u7F6E\u65F6\u6A21\u677F\u4F7F\u7528 kimi-k2.6\u3002\u81EA\u5B9A\u4E49\u4F1A\u8BDD\u5DE5\u5382\u4E5F\u53EF\u6309\u4F1A\u8BDD\u8BBE\u7F6E initialState.model\u3002");
|
|
23
|
+
} else {
|
|
24
|
+
io.writeError(`models \u53C2\u6570\u65E0\u6548\u3002
|
|
25
|
+
${MODELS_COMMAND_HELP}`);
|
|
26
|
+
return { exitCode: 2 };
|
|
27
|
+
}
|
|
28
|
+
return { exitCode: 0 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// packages/cli-core/src/commands/widgets.ts
|
|
32
|
+
import { resolve as resolve5 } from "node:path";
|
|
33
|
+
|
|
34
|
+
// packages/cli-core/src/project-context.ts
|
|
35
|
+
import { lstat as lstat4, realpath } from "node:fs/promises";
|
|
36
|
+
import { dirname, join as join3, resolve as resolve3 } from "node:path";
|
|
4
37
|
|
|
5
38
|
// packages/cli-core/src/config/index.ts
|
|
6
39
|
import {
|
|
@@ -45,13 +78,12 @@ var buddy_agent_schema_default = {
|
|
|
45
78
|
minLength: 1,
|
|
46
79
|
maxLength: 128,
|
|
47
80
|
pattern: "^[^\\s\\u0000-\\u001f\\u007f]+$",
|
|
48
|
-
description: "\u4ECE buddy-cli models \u67E5\
|
|
81
|
+
description: "\u4ECE buddy-cli models \u79BB\u7EBF\u67E5\u770B CLI \u5185\u7F6E\u6A21\u578B ID\uFF1B\u6A21\u677F\u5C06\u9009\u62E9\u4F20\u7ED9 initialState.model"
|
|
49
82
|
},
|
|
50
83
|
datasets: {
|
|
51
84
|
type: "array",
|
|
52
85
|
description: "\u672C\u642D\u5B50\u7533\u8BF7\u7684\u6570\u636E\u96C6\u53CA\u7528\u9014\uFF1B\u7701\u7565\u6216\u7A7A\u6570\u7EC4\u8868\u793A\u4E0D\u7533\u8BF7\u6570\u636E\uFF0C\u58F0\u660E\u4E0D\u4EE3\u8868\u7528\u6237\u5DF2\u6388\u6743",
|
|
53
86
|
default: [],
|
|
54
|
-
maxItems: 8,
|
|
55
87
|
items: {
|
|
56
88
|
type: "object",
|
|
57
89
|
additionalProperties: false,
|
|
@@ -59,16 +91,10 @@ var buddy_agent_schema_default = {
|
|
|
59
91
|
properties: {
|
|
60
92
|
id: {
|
|
61
93
|
type: "string",
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"health.heartRate",
|
|
67
|
-
"health.hrv",
|
|
68
|
-
"health.respiratoryRate",
|
|
69
|
-
"health.oxygenSaturation",
|
|
70
|
-
"health.environmentalAudioExposure"
|
|
71
|
-
]
|
|
94
|
+
minLength: 1,
|
|
95
|
+
maxLength: 128,
|
|
96
|
+
pattern: "^[^\\s\\u0000-\\u001f\\u007f]+$",
|
|
97
|
+
description: "\u53D6\u503C\u6765\u81EA\u5DE5\u7A0B Runtime \u5BFC\u51FA\u7684 DATA_ACCESS_CAPABILITIES\uFF0C\u7531\u8FD0\u884C\u4E0E\u4E0A\u4F20\u9884\u68C0\u6821\u9A8C\u3002\u58F0\u660E\u6570\u91CF\u4E0D\u7B49\u4E8E\u5355\u6B21\u67E5\u8BE2\u4E0A\u9650\u3002"
|
|
72
98
|
},
|
|
73
99
|
purpose: {
|
|
74
100
|
type: "string",
|
|
@@ -91,7 +117,7 @@ import { join } from "node:path";
|
|
|
91
117
|
import { lstat, mkdir, open, readFile, rmdir, unlink } from "node:fs/promises";
|
|
92
118
|
import { resolve } from "node:path";
|
|
93
119
|
var BUDDY_PLATFORM_AGENT_ENTRY = "agent.md";
|
|
94
|
-
var BUDDY_PLATFORM_FACTORY_ENTRY = "src/buddy.mjs";
|
|
120
|
+
var BUDDY_PLATFORM_FACTORY_ENTRY = "src/buddy-options.mjs";
|
|
95
121
|
var BUDDY_PLATFORM_PACKAGE_JSON = "package.json";
|
|
96
122
|
var BUDDY_PLATFORM_PACKAGE_LOCK = "package-lock.json";
|
|
97
123
|
var BUDDY_PLATFORM_START_ENTRY = "start.mjs";
|
|
@@ -109,20 +135,29 @@ function platformFactoryTemplate() {
|
|
|
109
135
|
|
|
110
136
|
const systemPrompt = readFileSync(new URL("../agent.md", import.meta.url), "utf8").trim();
|
|
111
137
|
if (!systemPrompt) throw new Error("agent.md must contain the Buddy system prompt");
|
|
138
|
+
const config = JSON.parse(readFileSync(new URL("../buddy.agent.json", import.meta.url), "utf8"));
|
|
139
|
+
/** @type {import("@my-life-buddies/buddy-runtime").ModelId} */
|
|
140
|
+
const model = config.model ?? "${DEFAULT_MODEL_ID}";
|
|
112
141
|
|
|
113
142
|
/**
|
|
114
|
-
*
|
|
143
|
+
* Builds BuddyOptions when the Runtime creates an agent for a conversation.
|
|
115
144
|
* Import tools from ./tools/ and hooks from ./hooks/, then register them below.
|
|
116
145
|
* Shared business code can live in ./services/; directories do not enable capabilities.
|
|
117
|
-
* Use conversation.memory / dataAccess /
|
|
146
|
+
* Use conversation.memory / dataAccess / widget / resource / proactive for platform capabilities.
|
|
147
|
+
* Add conversation.tools.data_access_query / data_access_request / data_access_read_result
|
|
148
|
+
* to initialState.tools for model-driven data access; declarations come from buddy.agent.json.datasets.
|
|
149
|
+
* Add conversation.tools.ask_question for multiple-choice questions.
|
|
150
|
+
* Add conversation.tools.resource_list / resource_read to discover and read reference material.
|
|
118
151
|
* Place widget assets in ../widgets/<type-id>/index.html and schema.json.
|
|
119
152
|
* Runtime uploads these files before connecting to MLB; upload failures stop startup.
|
|
120
|
-
*
|
|
153
|
+
* Add the required conversation.tools.widget_* tools to initialState.tools.
|
|
121
154
|
* Use platform APIs only; do not call third-party services directly.
|
|
155
|
+
* @type {import("@my-life-buddies/buddy-runtime").BuddyFactory}
|
|
122
156
|
*/
|
|
123
|
-
export default function
|
|
157
|
+
export default function createBuddyOptions(_conversation) {
|
|
124
158
|
return {
|
|
125
159
|
initialState: {
|
|
160
|
+
model,
|
|
126
161
|
systemPrompt,
|
|
127
162
|
thinkingLevel: "off",
|
|
128
163
|
tools: [],
|
|
@@ -135,7 +170,7 @@ function platformStartTemplate() {
|
|
|
135
170
|
return `// Generated startup: local DEV and hosted execution use the same Buddy Runtime.
|
|
136
171
|
import { readFileSync } from "node:fs";
|
|
137
172
|
import { BuddyServer } from "@my-life-buddies/buddy-runtime";
|
|
138
|
-
import
|
|
173
|
+
import createBuddyOptions from "./src/buddy-options.mjs";
|
|
139
174
|
|
|
140
175
|
const config = JSON.parse(readFileSync(new URL("./buddy.agent.json", import.meta.url), "utf8"));
|
|
141
176
|
if (!config || config.schemaVersion !== 1 || typeof config.buddyId !== "string" || !config.buddyId) {
|
|
@@ -144,15 +179,16 @@ if (!config || config.schemaVersion !== 1 || typeof config.buddyId !== "string"
|
|
|
144
179
|
if (process.env.MLB_BUDDY_ID && process.env.MLB_BUDDY_ID !== config.buddyId) {
|
|
145
180
|
throw new Error("MLB_BUDDY_ID does not match buddy.agent.json");
|
|
146
181
|
}
|
|
147
|
-
const model = typeof config.model === "string" && config.model ? config.model : "kimi-k2.6";
|
|
148
|
-
|
|
149
182
|
const lifecycle = new AbortController();
|
|
150
183
|
const stop = () => lifecycle.abort();
|
|
151
184
|
process.on("SIGINT", stop);
|
|
152
185
|
process.on("SIGTERM", stop);
|
|
153
186
|
let runtime;
|
|
154
187
|
try {
|
|
155
|
-
runtime = await BuddyServer.start({
|
|
188
|
+
runtime = await BuddyServer.start({
|
|
189
|
+
dataRequirements: config.datasets ?? [],
|
|
190
|
+
buddy: createBuddyOptions,
|
|
191
|
+
});
|
|
156
192
|
if (!lifecycle.signal.aborted) {
|
|
157
193
|
await new Promise((resolve) => lifecycle.signal.addEventListener("abort", resolve, { once: true }));
|
|
158
194
|
}
|
|
@@ -203,19 +239,19 @@ async function scaffoldPlatformAgent(projectRoot, name) {
|
|
|
203
239
|
throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u76EE\u5F55\u5FC5\u987B\u662F\u666E\u901A\u76EE\u5F55\uFF0C\u4E0D\u80FD\u4F7F\u7528\u6587\u4EF6\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${directoryPath}`);
|
|
204
240
|
}
|
|
205
241
|
}
|
|
206
|
-
for (const
|
|
242
|
+
for (const file2 of files) {
|
|
207
243
|
try {
|
|
208
|
-
handles.push({ path:
|
|
244
|
+
handles.push({ path: file2.path, handle: await open(file2.path, "wx", 384) });
|
|
209
245
|
} catch (cause) {
|
|
210
246
|
if (cause.code !== "EEXIST") throw cause;
|
|
211
|
-
if (!(await lstat(
|
|
212
|
-
throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u4F7F\u7528\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${
|
|
247
|
+
if (!(await lstat(file2.path)).isFile()) {
|
|
248
|
+
throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u4F7F\u7528\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${file2.path}`);
|
|
213
249
|
}
|
|
214
250
|
}
|
|
215
251
|
}
|
|
216
252
|
for (const { path: filePath, handle } of handles) {
|
|
217
|
-
const
|
|
218
|
-
await handle.writeFile(
|
|
253
|
+
const file2 = files.find((candidate) => candidate.path === filePath);
|
|
254
|
+
await handle.writeFile(file2.content, "utf8");
|
|
219
255
|
await handle.sync();
|
|
220
256
|
}
|
|
221
257
|
return Object.freeze({ path, created: handles.length > 0 || createdDirectories.length > 0 });
|
|
@@ -311,6 +347,9 @@ function validateBuddyProjectManifest(value) {
|
|
|
311
347
|
return { ok: false, issues: schemaIssues(validateAgentSchema.errors) };
|
|
312
348
|
}
|
|
313
349
|
const manifest = value;
|
|
350
|
+
if (manifest.model !== void 0 && !MODEL_IDS.includes(manifest.model)) {
|
|
351
|
+
return { ok: false, issues: [{ path: "/model", message: `\u4E0D\u652F\u6301\u7684\u6A21\u578B ID\uFF1B\u53EF\u9009 ${MODEL_IDS.join("\u3001")}\uFF08buddy-cli models\uFF09` }] };
|
|
352
|
+
}
|
|
314
353
|
const seen = /* @__PURE__ */ new Set();
|
|
315
354
|
for (const [index, dataset] of (manifest.datasets ?? []).entries()) {
|
|
316
355
|
if (seen.has(dataset.id)) {
|
|
@@ -753,10 +792,597 @@ async function readBuddyConfig(projectRoot) {
|
|
|
753
792
|
return combineBuddyProject(await readBuddyAgentConfig(rootDirectory));
|
|
754
793
|
}
|
|
755
794
|
|
|
795
|
+
// packages/cli-core/src/project-context.ts
|
|
796
|
+
var BuddyProjectContextError = class extends Error {
|
|
797
|
+
constructor(code2, message, targetPath, options) {
|
|
798
|
+
super(message, options);
|
|
799
|
+
this.code = code2;
|
|
800
|
+
this.targetPath = targetPath;
|
|
801
|
+
this.name = "BuddyProjectContextError";
|
|
802
|
+
}
|
|
803
|
+
code;
|
|
804
|
+
targetPath;
|
|
805
|
+
};
|
|
806
|
+
async function existingDirectory(target) {
|
|
807
|
+
const requested = resolve3(target);
|
|
808
|
+
let entry;
|
|
809
|
+
try {
|
|
810
|
+
entry = await lstat4(requested);
|
|
811
|
+
} catch (cause) {
|
|
812
|
+
if (cause.code === "ENOENT") {
|
|
813
|
+
throw new BuddyProjectContextError(
|
|
814
|
+
"PROJECT_DIRECTORY_NOT_FOUND",
|
|
815
|
+
`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${requested}`,
|
|
816
|
+
requested,
|
|
817
|
+
{ cause }
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
throw cause;
|
|
821
|
+
}
|
|
822
|
+
if (!entry.isDirectory()) {
|
|
823
|
+
throw new BuddyProjectContextError(
|
|
824
|
+
"PROJECT_DIRECTORY_INVALID",
|
|
825
|
+
`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${requested}`,
|
|
826
|
+
requested
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
return realpath(requested);
|
|
830
|
+
}
|
|
831
|
+
async function projectFileState(directory, filename) {
|
|
832
|
+
const configPath = join3(directory, filename);
|
|
833
|
+
try {
|
|
834
|
+
const entry = await lstat4(configPath);
|
|
835
|
+
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
836
|
+
throw new BuddyProjectContextError(
|
|
837
|
+
"PROJECT_CONFIG_INVALID",
|
|
838
|
+
`${configPath} \u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u662F\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\u3002`,
|
|
839
|
+
configPath
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
return "valid";
|
|
843
|
+
} catch (cause) {
|
|
844
|
+
if (cause.code === "ENOENT") return "missing";
|
|
845
|
+
throw cause;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
async function searchUp(startDirectory) {
|
|
849
|
+
let directory = startDirectory;
|
|
850
|
+
while (true) {
|
|
851
|
+
if (await projectFileState(directory, BUDDY_AGENT_FILENAME) === "valid") return directory;
|
|
852
|
+
const parent = dirname(directory);
|
|
853
|
+
if (parent === directory) return void 0;
|
|
854
|
+
directory = parent;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
async function findBuddyProjectRoot(startDirectory) {
|
|
858
|
+
return searchUp(await existingDirectory(startDirectory));
|
|
859
|
+
}
|
|
860
|
+
async function requireBuddyProjectRoot(startDirectory) {
|
|
861
|
+
const requested = resolve3(startDirectory);
|
|
862
|
+
const root = await findBuddyProjectRoot(requested);
|
|
863
|
+
if (root !== void 0) return root;
|
|
864
|
+
throw new BuddyProjectContextError(
|
|
865
|
+
"PROJECT_NOT_FOUND",
|
|
866
|
+
[
|
|
867
|
+
"\u5F53\u524D\u76EE\u5F55\u4E0D\u5C5E\u4E8E\u642D\u642D\u9879\u76EE\u3002",
|
|
868
|
+
`\u8BF7\u8FDB\u5165\u5305\u542B ${BUDDY_AGENT_FILENAME} \u7684\u9879\u76EE\u76EE\u5F55\u540E\u91CD\u8BD5\uFF0C\u6216\u8005\u8FD0\u884C buddy-cli init \u521B\u5EFA\u65B0\u9879\u76EE\u3002`
|
|
869
|
+
].join("\n"),
|
|
870
|
+
requested
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
async function requireBuddyAgentProjectRoot(startDirectory) {
|
|
874
|
+
const root = await requireBuddyProjectRoot(startDirectory);
|
|
875
|
+
const agentPath = join3(root, BUDDY_AGENT_FILENAME);
|
|
876
|
+
try {
|
|
877
|
+
await readBuddyAgentConfig(root);
|
|
878
|
+
const missing = await missingBuddyAgentFiles(root);
|
|
879
|
+
if (missing.length > 0) throw new Error(`\u5DE5\u7A0B\u7F3A\u5C11\u6709\u6548\u6587\u4EF6\uFF1A${missing.join("\u3001")}`);
|
|
880
|
+
return root;
|
|
881
|
+
} catch (cause) {
|
|
882
|
+
throw new BuddyProjectContextError(
|
|
883
|
+
"PROJECT_NOT_FOUND",
|
|
884
|
+
[
|
|
885
|
+
`\u5F53\u524D buddy.agent.json \u6240\u5728\u5DE5\u7A0B\u5C1A\u4E0D\u53EF\u8FD0\u884C\uFF1A${cause instanceof Error ? cause.message : "\u8BF7\u68C0\u67E5 buddy.agent.json \u4E0E\u5DE5\u7A0B\u6587\u4EF6"}`,
|
|
886
|
+
"\u8BF7\u5148\u8FD0\u884C buddy-cli init --mode direct \u8865\u9F50\u5B98\u65B9 Runtime \u5DE5\u7A0B\uFF0C\u518D\u8FD0\u884C dev\u3001preview \u6216 push\u3002"
|
|
887
|
+
].join("\n"),
|
|
888
|
+
agentPath,
|
|
889
|
+
{ cause }
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
async function nearestExistingDirectory(target) {
|
|
894
|
+
let candidate = resolve3(target);
|
|
895
|
+
while (true) {
|
|
896
|
+
try {
|
|
897
|
+
const entry = await lstat4(candidate);
|
|
898
|
+
if (!entry.isDirectory()) {
|
|
899
|
+
throw new BuddyProjectContextError(
|
|
900
|
+
"PROJECT_DIRECTORY_INVALID",
|
|
901
|
+
`\u521D\u59CB\u5316\u76EE\u6807\u7684\u5DF2\u6709\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${candidate}`,
|
|
902
|
+
candidate
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
return realpath(candidate);
|
|
906
|
+
} catch (cause) {
|
|
907
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
908
|
+
const parent = dirname(candidate);
|
|
909
|
+
if (parent === candidate) throw cause;
|
|
910
|
+
candidate = parent;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async function assertOutsideBuddyProject(target) {
|
|
915
|
+
const requested = resolve3(target);
|
|
916
|
+
const container = await nearestExistingDirectory(requested);
|
|
917
|
+
const projectRoot = await searchUp(container);
|
|
918
|
+
if (projectRoot === void 0) return;
|
|
919
|
+
throw new BuddyProjectContextError(
|
|
920
|
+
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
921
|
+
[
|
|
922
|
+
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
923
|
+
`\u8BF7\u76F4\u63A5\u4F7F\u7528\u73B0\u6709\u9879\u76EE\uFF0C\u4E0D\u8981\u5728\u5176\u4E2D\u5D4C\u5957\u6216\u91CD\u590D\u6267\u884C buddy-cli init\u3002`
|
|
924
|
+
].join("\n"),
|
|
925
|
+
requested
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
async function assertInitializableBuddyTarget(target) {
|
|
929
|
+
const requested = resolve3(target);
|
|
930
|
+
const container = await nearestExistingDirectory(requested);
|
|
931
|
+
const projectRoot = await searchUp(container);
|
|
932
|
+
if (projectRoot === void 0) return;
|
|
933
|
+
let requestedRoot = requested;
|
|
934
|
+
try {
|
|
935
|
+
requestedRoot = await realpath(requested);
|
|
936
|
+
} catch (cause) {
|
|
937
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
938
|
+
}
|
|
939
|
+
if (projectRoot === requestedRoot) {
|
|
940
|
+
try {
|
|
941
|
+
await readBuddyProjectManifest(projectRoot);
|
|
942
|
+
return;
|
|
943
|
+
} catch (cause) {
|
|
944
|
+
throw new BuddyProjectContextError(
|
|
945
|
+
"PROJECT_CONFIG_INVALID",
|
|
946
|
+
`${join3(projectRoot, BUDDY_AGENT_FILENAME)} \u4E0D\u662F\u6709\u6548\u7684\u9879\u76EE\u6E05\u5355\u3002`,
|
|
947
|
+
requested,
|
|
948
|
+
{ cause }
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
throw new BuddyProjectContextError(
|
|
953
|
+
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
954
|
+
[
|
|
955
|
+
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u5B8C\u6574\u7684\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
956
|
+
"\u8BF7\u76F4\u63A5\u4F7F\u7528\u73B0\u6709\u9879\u76EE\uFF0C\u4E0D\u8981\u5728\u5176\u4E2D\u5D4C\u5957\u6216\u91CD\u590D\u6267\u884C buddy-cli init\u3002"
|
|
957
|
+
].join("\n"),
|
|
958
|
+
requested
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// packages/cli-core/src/widget-delivery.ts
|
|
963
|
+
import { createHash } from "node:crypto";
|
|
964
|
+
import { lstat as lstat5, readFile as readFile3, realpath as realpath2, readdir as readdir2, mkdir as mkdir3, writeFile, rename as rename2 } from "node:fs/promises";
|
|
965
|
+
import { join as join4, resolve as resolve4 } from "node:path";
|
|
966
|
+
var LOCK = "widget-design.lock.json";
|
|
967
|
+
var IMPLEMENTATION = "widget-implementation.json";
|
|
968
|
+
var RECEIPTS = ".buddy/widget-verification.json";
|
|
969
|
+
var idPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
|
|
970
|
+
var hashPattern = /^[a-f0-9]{64}$/u;
|
|
971
|
+
var sha = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
972
|
+
var object = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
973
|
+
var text = (value) => typeof value === "string" && value.trim().length > 0;
|
|
974
|
+
var WIDGET_REVIEW_CHECKS = ["visual", "interactions", "data-update", "trigger", "read-only"];
|
|
975
|
+
var WIDGET_REVIEW_STATES = ["initial", "partial", "updated"];
|
|
976
|
+
var NOTICE = "\u7ED3\u6784\u68C0\u67E5\u4E0E\u9884\u89C8\u6838\u5BF9\u8BB0\u5F55\u5206\u522B\u5C55\u793A\uFF1B\u6838\u5BF9\u8BB0\u5F55\u7531\u6267\u884C\u6838\u5BF9\u8005\u63D0\u4F9B\uFF0C\u5DE5\u5177\u4E0D\u81EA\u52A8\u5224\u65AD\u89C6\u89C9\u4E00\u81F4\uFF0C\u4E5F\u4E0D\u4EE3\u8868\u5F00\u53D1\u8005\u4F53\u9A8C\u9A8C\u6536\u6216\u53D1\u5E03\u6210\u529F\u3002";
|
|
977
|
+
function portable(path) {
|
|
978
|
+
if (!text(path) || path.startsWith("/") || path.includes("\\") || /[\x00-\x1f:]/u.test(path) || path.split("/").some((p) => !p || p === "." || p === "..")) throw new Error("\u5C0F\u6302\u4EF6\u8DEF\u5F84\u5FC5\u987B\u662F\u9879\u76EE\u5185\u76F8\u5BF9\u8DEF\u5F84");
|
|
979
|
+
return path;
|
|
980
|
+
}
|
|
981
|
+
async function file(root, path, optional = false) {
|
|
982
|
+
portable(path);
|
|
983
|
+
const base = await realpath2(root);
|
|
984
|
+
let cursor = base;
|
|
985
|
+
const parts = path.split("/");
|
|
986
|
+
for (const [index, part] of parts.entries()) {
|
|
987
|
+
cursor = join4(cursor, part);
|
|
988
|
+
const stat2 = await lstat5(cursor).catch((e) => {
|
|
989
|
+
if (e.code === "ENOENT") return void 0;
|
|
990
|
+
throw e;
|
|
991
|
+
});
|
|
992
|
+
if (!stat2) {
|
|
993
|
+
if (optional) return void 0;
|
|
994
|
+
throw new Error(`\u7F3A\u5C11\u6587\u4EF6\uFF1A${path}`);
|
|
995
|
+
}
|
|
996
|
+
if (stat2.isSymbolicLink() || (index < parts.length - 1 ? !stat2.isDirectory() : !stat2.isFile())) throw new Error(`\u4E0D\u63A5\u53D7\u7B26\u53F7\u94FE\u63A5\u6216\u7279\u6B8A\u6587\u4EF6\uFF1A${path}`);
|
|
997
|
+
if (index === parts.length - 1 && stat2.size > 8 * 1024 * 1024) throw new Error(`\u6587\u4EF6\u8D85\u8FC7 8 MB\uFF1A${path}`);
|
|
998
|
+
}
|
|
999
|
+
return readFile3(cursor);
|
|
1000
|
+
}
|
|
1001
|
+
async function jsonFile(root, path, optional = false) {
|
|
1002
|
+
const bytes = await file(root, path, optional);
|
|
1003
|
+
if (!bytes) return void 0;
|
|
1004
|
+
try {
|
|
1005
|
+
return JSON.parse(bytes.toString("utf8"));
|
|
1006
|
+
} catch {
|
|
1007
|
+
throw new Error(`JSON \u65E0\u6548\uFF1A${path}`);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
async function save(root, path, value) {
|
|
1011
|
+
portable(path);
|
|
1012
|
+
let cursor = resolve4(root);
|
|
1013
|
+
for (const segment of path.split("/").slice(0, -1)) {
|
|
1014
|
+
cursor = join4(cursor, segment);
|
|
1015
|
+
await mkdir3(cursor).catch((e) => {
|
|
1016
|
+
if (e.code !== "EEXIST") throw e;
|
|
1017
|
+
});
|
|
1018
|
+
const stat2 = await lstat5(cursor);
|
|
1019
|
+
if (!stat2.isDirectory() || stat2.isSymbolicLink()) throw new Error("\u5199\u5165\u76EE\u5F55\u4E0D\u5B89\u5168");
|
|
1020
|
+
}
|
|
1021
|
+
await file(root, path, true);
|
|
1022
|
+
const target = join4(root, path), temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
1023
|
+
await writeFile(temp, JSON.stringify(value, null, 2) + "\n", { flag: "wx", mode: 384 });
|
|
1024
|
+
await rename2(temp, target);
|
|
1025
|
+
}
|
|
1026
|
+
function validateLock(value) {
|
|
1027
|
+
if (!object(value) || value.schemaVersion !== 1 || !text(value.buddyId) || !text(value.revision) || !hashPattern.test(value.catalogHash) || !Array.isArray(value.items) || value.items.length > 100) throw new Error("\u8BBE\u8BA1\u7ED1\u5B9A\u6587\u4EF6\u65E0\u6548\uFF0C\u8BF7\u8FD0\u884C buddy-cli widgets sync");
|
|
1028
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1029
|
+
for (const item of value.items) {
|
|
1030
|
+
if (!object(item) || typeof item.id !== "string" || item.id.length > 64 || !idPattern.test(item.id) || seen.has(item.id) || !text(item.name) || !hashPattern.test(item.prototypeHash) || !Array.isArray(item.fields) || !item.fields.every(text) || new Set(item.fields).size !== item.fields.length) throw new Error("\u8BBE\u8BA1\u6E05\u5355\u5305\u542B\u65E0\u6548\u6216\u91CD\u590D\u7684\u5C0F\u6302\u4EF6");
|
|
1031
|
+
seen.add(item.id);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
async function creatorDesign(root) {
|
|
1035
|
+
const session = await jsonFile(root, ".buddy/creator/session.json", true);
|
|
1036
|
+
if (!session) return void 0;
|
|
1037
|
+
if (session.schemaVersion !== 1 || !text(session.platformBuddyId) || !/^[a-f0-9-]{36}$/u.test(session.creationKey)) throw new Error("Creator \u9879\u76EE\u8EAB\u4EFD\u65E0\u6548");
|
|
1038
|
+
const workspace = `.buddy/creator/workspaces/${session.creationKey}`;
|
|
1039
|
+
const state2 = await jsonFile(root, `${workspace}/state.json`);
|
|
1040
|
+
if (!state2.artifacts?.["widget.catalog"]) return void 0;
|
|
1041
|
+
const completion = await jsonFile(root, `${workspace}/completion.json`);
|
|
1042
|
+
if (completion.status !== "ready" || completion.revision !== state2.revision || state2.paused || state2.pendingTurnId || !/^[a-zA-Z0-9_-]+$/u.test(state2.revision)) throw new Error("\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u4EA4\u4ED8\u672A\u5B8C\u6210\u6216\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u5148\u6062\u590D Creator \u5F53\u524D\u8BBE\u8BA1\u9A8C\u6536");
|
|
1043
|
+
const directory = `${workspace}/deliverables/${state2.revision}`;
|
|
1044
|
+
const manifest = await jsonFile(root, `${directory}/MANIFEST.json`);
|
|
1045
|
+
if (state2.buddyId !== session.creationKey || manifest.buddyId !== state2.buddyId) throw new Error("Creator \u4F5C\u54C1\u4E0E\u4EA4\u4ED8\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1046
|
+
if (manifest.complete !== true || manifest.revision !== state2.revision || !Array.isArray(manifest.files)) throw new Error("\u8BBE\u8BA1\u4EA4\u4ED8\u6E05\u5355\u65E0\u6548");
|
|
1047
|
+
const artifact = async (path) => {
|
|
1048
|
+
const bytes = await file(root, `${directory}/${path}`);
|
|
1049
|
+
const entry = manifest.files.find((e) => e.path === path);
|
|
1050
|
+
if (!entry || entry.sha256 !== sha(bytes) || entry.bytes !== bytes.length) throw new Error(`\u8BBE\u8BA1\u6210\u679C\u88AB\u4FEE\u6539\uFF1A${path}\uFF1B\u5148\u6062\u590D\u4EA4\u4ED8`);
|
|
1051
|
+
return bytes;
|
|
1052
|
+
};
|
|
1053
|
+
const confirmed = (id, hash) => state2.artifacts?.[id]?.hash === hash && state2.confirmations?.some((c) => c.objectId === id && c.hash === hash && c.decision === "confirmed" && !c.invalidatedBy);
|
|
1054
|
+
const catalog = JSON.parse((await artifact("widgets/catalog.json")).toString());
|
|
1055
|
+
if (!confirmed("widget.catalog", catalog.hash) || !Array.isArray(catalog.data?.widgets)) throw new Error("\u5C0F\u6302\u4EF6\u6E05\u5355\u4E0D\u662F\u5F53\u524D\u5DF2\u786E\u8BA4\u7248\u672C");
|
|
1056
|
+
const items = [];
|
|
1057
|
+
for (const item of catalog.data.widgets) {
|
|
1058
|
+
if (!text(item.id) || item.id.length > 64 || !idPattern.test(item.id)) throw new Error("\u8BBE\u8BA1\u7C7B\u578B ID \u65E0\u6548");
|
|
1059
|
+
const prefix = `widgets/${item.id}`;
|
|
1060
|
+
const prototype = JSON.parse((await artifact(`${prefix}/prototype.json`)).toString());
|
|
1061
|
+
const acceptance = JSON.parse((await artifact(`${prefix}/acceptance.json`)).toString()).artifact;
|
|
1062
|
+
if (!confirmed(`widget.${item.id}.prototype`, prototype.hash) || !confirmed(`widget.${item.id}.acceptance`, acceptance.hash) || acceptance.data?.prototypeHash !== prototype.hash) throw new Error(`\u8BBE\u8BA1\u5C1A\u672A\u6709\u6548\u9A8C\u6536\uFF1A${item.id}`);
|
|
1063
|
+
for (const asset of ["index.html", "style.css", "app.js"]) await artifact(`${prefix}/${asset}`);
|
|
1064
|
+
items.push({ id: item.id, name: item.name, prototypeHash: prototype.hash, fields: item.fields.map((f) => f.key) });
|
|
1065
|
+
}
|
|
1066
|
+
const result = { schemaVersion: 1, buddyId: session.platformBuddyId, revision: state2.revision, catalogHash: catalog.hash, items };
|
|
1067
|
+
validateLock(result);
|
|
1068
|
+
return result;
|
|
1069
|
+
}
|
|
1070
|
+
async function syncWidgetDesign(root) {
|
|
1071
|
+
const design = await creatorDesign(root);
|
|
1072
|
+
if (!design) throw new Error("\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u5DF2\u9A8C\u6536\u7684\u5C0F\u6302\u4EF6\u8BBE\u8BA1\uFF1B\u76F4\u63A5\u5F00\u53D1\u9879\u76EE\u53EF\u7EE7\u7EED\u4F7F\u7528\u539F\u6D41\u7A0B");
|
|
1073
|
+
const config = await jsonFile(root, "buddy.agent.json");
|
|
1074
|
+
if (config.buddyId !== design.buddyId) throw new Error("\u8BBE\u8BA1\u4E0E\u642D\u5B50\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1075
|
+
const old = await jsonFile(root, IMPLEMENTATION, true);
|
|
1076
|
+
if (old && (!object(old) || old.schemaVersion !== 1 || !Array.isArray(old.widgets))) throw new Error("\u5DF2\u6709\u5B9E\u73B0\u6620\u5C04\u635F\u574F\uFF0C\u4FDD\u7559\u539F\u6587\u4EF6\uFF0C\u8BF7\u4FEE\u590D\u540E\u518D\u540C\u6B65");
|
|
1077
|
+
const mappings = design.items.map((item) => old?.widgets.find((w) => w.designId === item.id) ?? {
|
|
1078
|
+
designId: item.id,
|
|
1079
|
+
typeId: item.id,
|
|
1080
|
+
prototypeHash: item.prototypeHash,
|
|
1081
|
+
fields: Object.fromEntries(item.fields.map((key) => [key, ""])),
|
|
1082
|
+
lifecycle: { create: [], update: [], present: [] }
|
|
1083
|
+
});
|
|
1084
|
+
const next = { schemaVersion: 1, widgets: mappings };
|
|
1085
|
+
if (JSON.stringify(old) !== JSON.stringify(next)) await save(root, IMPLEMENTATION, next);
|
|
1086
|
+
await save(root, LOCK, design);
|
|
1087
|
+
}
|
|
1088
|
+
async function sourceSnapshot(root) {
|
|
1089
|
+
const result = [];
|
|
1090
|
+
const visit = async (path, depth = 0) => {
|
|
1091
|
+
if (depth > 32 || result.length > 1e4) throw new Error("\u5B9E\u73B0\u6E90\u7801\u8D85\u51FA\u68C0\u67E5\u8303\u56F4");
|
|
1092
|
+
const stat2 = await lstat5(join4(root, path)).catch((e) => {
|
|
1093
|
+
if (e.code === "ENOENT") return void 0;
|
|
1094
|
+
throw e;
|
|
1095
|
+
});
|
|
1096
|
+
if (!stat2) return;
|
|
1097
|
+
if (stat2.isSymbolicLink()) throw new Error(`\u5B9E\u73B0\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5\uFF1A${path}`);
|
|
1098
|
+
if (stat2.isDirectory()) {
|
|
1099
|
+
for (const name of (await readdir2(join4(root, path))).sort()) await visit(`${path}/${name}`, depth + 1);
|
|
1100
|
+
} else result.push([path, sha(await file(root, path))]);
|
|
1101
|
+
};
|
|
1102
|
+
for (const name of ["src", "resources", "widgets", "start.mjs", "agent.md", "buddy.agent.json", "package.json", "package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) await visit(name);
|
|
1103
|
+
return result;
|
|
1104
|
+
}
|
|
1105
|
+
async function checkWidgetDelivery(root) {
|
|
1106
|
+
const report = { status: "not-configured", items: [], issues: [], notice: NOTICE };
|
|
1107
|
+
try {
|
|
1108
|
+
const current = await creatorDesign(root);
|
|
1109
|
+
const lockBytes = await file(root, LOCK, true);
|
|
1110
|
+
let lock = lockBytes ? JSON.parse(lockBytes.toString()) : void 0;
|
|
1111
|
+
if (!current && !lock) {
|
|
1112
|
+
if (await file(root, IMPLEMENTATION, true)) throw new Error("\u5B9E\u73B0\u6620\u5C04\u5B58\u5728\uFF0C\u4F46\u8BBE\u8BA1\u7ED1\u5B9A\u7F3A\u5931\uFF0C\u8BF7\u6062\u590D widget-design.lock.json");
|
|
1113
|
+
return report;
|
|
1114
|
+
}
|
|
1115
|
+
report.status = "incomplete";
|
|
1116
|
+
if (!lock) {
|
|
1117
|
+
report.issues.push("\u5DF2\u9A8C\u6536\u8BBE\u8BA1\u5C1A\u672A\u7ED1\u5B9A\u5230\u5DE5\u7A0B\uFF0C\u8BF7\u8FD0\u884C buddy-cli widgets sync");
|
|
1118
|
+
lock = current;
|
|
1119
|
+
}
|
|
1120
|
+
validateLock(lock);
|
|
1121
|
+
report.designRevision = lock.revision;
|
|
1122
|
+
const config = await jsonFile(root, "buddy.agent.json");
|
|
1123
|
+
if (config.buddyId !== lock.buddyId) throw new Error("\u8BBE\u8BA1\u4E0E\u642D\u5B50\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1124
|
+
if (current && JSON.stringify(current) !== JSON.stringify(lock)) report.issues.push("\u8BBE\u8BA1\u5DF2\u53D8\u5316\uFF0C\u8BF7\u8FD0\u884C buddy-cli widgets sync\uFF1B\u65E7\u5B9E\u73B0\u548C\u6838\u5BF9\u8BB0\u5F55\u4E0D\u80FD\u6CBF\u7528\u4E3A\u65B0\u7248\u9A8C\u6536");
|
|
1125
|
+
const implementationBytes = await file(root, IMPLEMENTATION, true);
|
|
1126
|
+
const implementation = implementationBytes ? JSON.parse(implementationBytes.toString()) : void 0;
|
|
1127
|
+
const mappings = implementation?.schemaVersion === 1 && Array.isArray(implementation.widgets) ? implementation.widgets : [];
|
|
1128
|
+
if (!implementation || mappings.length !== lock.items.length) report.issues.push("\u5B9E\u73B0\u6620\u5C04\u4E0E\u5DF2\u9A8C\u6536\u6E05\u5355\u6570\u91CF\u4E0D\u7B26");
|
|
1129
|
+
const receipts = await jsonFile(root, RECEIPTS, true);
|
|
1130
|
+
const snapshot = await sourceSnapshot(root);
|
|
1131
|
+
report.files = snapshot.map(([path, digest2]) => ({ path, digest: `sha256:${digest2}` }));
|
|
1132
|
+
if (lockBytes) report.files.push({ path: LOCK, digest: `sha256:${sha(lockBytes)}` });
|
|
1133
|
+
if (implementationBytes) report.files.push({ path: IMPLEMENTATION, digest: `sha256:${sha(implementationBytes)}` });
|
|
1134
|
+
const usedTypes = /* @__PURE__ */ new Set();
|
|
1135
|
+
for (const mapping of mappings) if (!lock.items.some((item) => item.id === mapping.designId)) report.issues.push("\u5B58\u5728\u4E0D\u5C5E\u4E8E\u5F53\u524D\u8BBE\u8BA1\u7684\u5B9E\u73B0\u6620\u5C04\uFF0C\u8BF7\u8FD0\u884C widgets sync");
|
|
1136
|
+
for (const item of lock.items) {
|
|
1137
|
+
const row = { id: item.id, name: item.name, status: "missing", issues: [] };
|
|
1138
|
+
report.items.push(row);
|
|
1139
|
+
try {
|
|
1140
|
+
const matches = mappings.filter((w) => w.designId === item.id);
|
|
1141
|
+
if (matches.length !== 1) throw new Error("\u7F3A\u5C11\u6216\u91CD\u590D\u7684\u5B9E\u73B0\u6620\u5C04");
|
|
1142
|
+
const m = matches[0];
|
|
1143
|
+
if (!text(m.typeId) || m.typeId.length > 64 || !/^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u.test(m.typeId) || usedTypes.has(m.typeId)) throw new Error("\u6BCF\u4E2A\u5DF2\u9A8C\u6536\u8BBE\u8BA1\u5FC5\u987B\u5BF9\u5E94\u4E00\u4E2A\u72EC\u7ACB\u3001\u6709\u6548\u7684\u7C7B\u578B ID\uFF0C\u4E0D\u80FD\u5408\u5E76\u6210\u540C\u4E00\u4E2A\u5DE5\u4F5C\u53F0");
|
|
1144
|
+
usedTypes.add(m.typeId);
|
|
1145
|
+
row.typeId = m.typeId;
|
|
1146
|
+
if (m.prototypeHash !== item.prototypeHash) row.issues.push("\u5B9E\u73B0\u7ED1\u5B9A\u7684\u539F\u578B\u7248\u672C\u5DF2\u8FC7\u671F");
|
|
1147
|
+
const html = (await file(root, `widgets/${m.typeId}/index.html`)).toString();
|
|
1148
|
+
if (!html.trim()) throw new Error("\u9875\u9762\u4E3A\u7A7A");
|
|
1149
|
+
const schema = await jsonFile(root, `widgets/${m.typeId}/schema.json`);
|
|
1150
|
+
if (!object(schema.modules) || !Object.keys(schema.modules).length) throw new Error("\u7F3A\u5C11\u6709\u6548\u7684\u6570\u636E\u6A21\u5757");
|
|
1151
|
+
for (const key of item.fields) {
|
|
1152
|
+
const binding = m.fields?.[key];
|
|
1153
|
+
if (!text(binding) || !object(schema.modules[binding.split(".")[0]]?.record)) row.issues.push(`\u5B57\u6BB5 ${key} \u672A\u6620\u5C04\u5230\u5B9E\u9645\u6570\u636E\u6A21\u5757`);
|
|
1154
|
+
}
|
|
1155
|
+
for (const phase of ["create", "update", "present"]) {
|
|
1156
|
+
const paths = m.lifecycle?.[phase];
|
|
1157
|
+
if (!Array.isArray(paths) || !paths.length || paths.length > 50 || !paths.every(text)) {
|
|
1158
|
+
row.issues.push(`\u7F3A\u5C11${phase}\u4EE3\u7801\u8DEF\u5F84`);
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
for (const path of paths) {
|
|
1162
|
+
if (!path.startsWith("src/") || !/\.(?:mjs|js|ts|cjs)$/u.test(path)) throw new Error("\u521B\u5EFA\u3001\u66F4\u65B0\u548C\u5C55\u793A\u5FC5\u987B\u6620\u5C04\u5230 src/ \u4E0B\u7684\u4EE3\u7801");
|
|
1163
|
+
if (!(await file(root, path)).toString().trim()) throw new Error(`\u4EE3\u7801\u4E3A\u7A7A\uFF1A${path}`);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
if (row.issues.length) continue;
|
|
1167
|
+
row.status = "implemented";
|
|
1168
|
+
row.implementationDigest = sha(JSON.stringify({ design: lock, mapping: m, snapshot }));
|
|
1169
|
+
const receipt = receipts?.items?.[item.id];
|
|
1170
|
+
if (receipt?.implementationDigest === row.implementationDigest) {
|
|
1171
|
+
validateEvidence(receipt.evidence);
|
|
1172
|
+
if (!Array.isArray(receipt.artifacts) || receipt.artifacts.length !== 3) throw new Error("\u6838\u5BF9\u8BC1\u636E\u8BB0\u5F55\u65E0\u6548");
|
|
1173
|
+
for (const [index, artifact] of receipt.artifacts.entries()) {
|
|
1174
|
+
if (artifact.path !== receipt.evidence.states[WIDGET_REVIEW_STATES[index]].artifact) throw new Error("\u6838\u5BF9\u8BC1\u636E\u8DEF\u5F84\u4E0D\u5339\u914D");
|
|
1175
|
+
if (sha(await file(root, artifact.path)) !== artifact.digest) throw new Error("\u9884\u89C8\u6838\u5BF9\u8BC1\u636E\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6838\u5BF9");
|
|
1176
|
+
}
|
|
1177
|
+
if (!receipt.artifacts.length) throw new Error("\u7F3A\u5C11\u9884\u89C8\u6838\u5BF9\u8BC1\u636E");
|
|
1178
|
+
row.status = "verified";
|
|
1179
|
+
} else row.issues.push("\u5C1A\u672A\u8BB0\u5F55\u5F53\u524D\u5B9E\u73B0\u7248\u672C\u7684\u771F\u5B9E\u9884\u89C8\u6838\u5BF9\uFF1B\u6587\u4EF6\u5B58\u5728\u4E0D\u7B49\u4E8E\u6837\u5F0F\u4E0E\u884C\u4E3A\u5DF2\u9A8C\u8BC1");
|
|
1180
|
+
} catch (cause) {
|
|
1181
|
+
row.issues.push(cause instanceof Error ? cause.message : String(cause));
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
if (!report.issues.length && report.items.every((item) => item.status === "verified" && !item.issues.length)) report.status = "ready";
|
|
1185
|
+
} catch (cause) {
|
|
1186
|
+
report.status = "incomplete";
|
|
1187
|
+
report.issues.push(cause instanceof Error ? cause.message : String(cause));
|
|
1188
|
+
}
|
|
1189
|
+
return report;
|
|
1190
|
+
}
|
|
1191
|
+
function validateEvidence(value) {
|
|
1192
|
+
if (!object(value) || !text(value.previewUrl) || !text(value.observedAt) || !Number.isFinite(Date.parse(value.observedAt)) || !text(value.reviewer) || !object(value.states) || !object(value.checks)) throw new Error("\u6838\u5BF9\u8BB0\u5F55\u9700\u5305\u542B previewUrl\u3001observedAt\u3001reviewer\u3001states \u548C checks");
|
|
1193
|
+
const url = new URL(value.previewUrl);
|
|
1194
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname.includes("bootstrap") || url.search || url.hash) throw new Error("\u9884\u89C8\u5730\u5740\u9700\u4F7F\u7528\u4E0D\u542B\u767B\u5F55\u51ED\u636E\u7684\u9875\u9762\u5730\u5740");
|
|
1195
|
+
for (const state2 of WIDGET_REVIEW_STATES) {
|
|
1196
|
+
if (!text(value.states[state2]?.notes) || !text(value.states[state2]?.artifact)) throw new Error(`\u7F3A\u5C11 ${state2} \u7684\u89C2\u5BDF\u8BF4\u660E\u4E0E\u8BC1\u636E\u6587\u4EF6`);
|
|
1197
|
+
portable(value.states[state2].artifact);
|
|
1198
|
+
}
|
|
1199
|
+
for (const check of WIDGET_REVIEW_CHECKS) if (!text(value.checks[check])) throw new Error(`\u7F3A\u5C11 ${check} \u7684\u5B9E\u9645\u89C2\u5BDF\u8BF4\u660E`);
|
|
1200
|
+
}
|
|
1201
|
+
async function recordWidgetVerification(root, designId, evidencePath) {
|
|
1202
|
+
const report = await checkWidgetDelivery(root);
|
|
1203
|
+
const row = report.items.find((item) => item.id === designId);
|
|
1204
|
+
if (report.issues.length || !row?.implementationDigest) throw new Error("\u5148\u5B8C\u6210\u5F53\u524D\u8BBE\u8BA1\u4E0E\u5B9E\u73B0\u7684\u7ED3\u6784\u68C0\u67E5\uFF0C\u518D\u8BB0\u5F55\u9884\u89C8\u6838\u5BF9");
|
|
1205
|
+
const evidence = await jsonFile(root, evidencePath);
|
|
1206
|
+
validateEvidence(evidence);
|
|
1207
|
+
if (evidence.implementationDigest !== row.implementationDigest) throw new Error("\u6838\u5BF9\u8BB0\u5F55\u7684 implementationDigest \u4E0E\u5F53\u524D\u4EE3\u7801\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u5148\u6838\u5BF9\u5F53\u524D\u5B9E\u73B0");
|
|
1208
|
+
const artifacts = [];
|
|
1209
|
+
for (const state2 of WIDGET_REVIEW_STATES) {
|
|
1210
|
+
const path = evidence.states[state2].artifact;
|
|
1211
|
+
artifacts.push({ path, digest: sha(await file(root, path)) });
|
|
1212
|
+
}
|
|
1213
|
+
const old = await jsonFile(root, RECEIPTS, true);
|
|
1214
|
+
if (old && (old.schemaVersion !== 1 || !object(old.items))) throw new Error("\u5DF2\u6709\u6838\u5BF9\u8BB0\u5F55\u635F\u574F\uFF0C\u4FDD\u7559\u6587\u4EF6\u8BF7\u5148\u4FEE\u590D");
|
|
1215
|
+
await save(root, RECEIPTS, { schemaVersion: 1, items: { ...old?.items, [designId]: {
|
|
1216
|
+
implementationDigest: row.implementationDigest,
|
|
1217
|
+
evidence,
|
|
1218
|
+
artifacts
|
|
1219
|
+
} } });
|
|
1220
|
+
}
|
|
1221
|
+
function widgetDeliverySummary(report) {
|
|
1222
|
+
if (report.status === "not-configured") return "\u5F53\u524D\u5DE5\u7A0B\u6CA1\u6709\u7ED1\u5B9A\u7684 Creator \u5C0F\u6302\u4EF6\u8BBE\u8BA1\uFF1B\u672A\u6267\u884C\u8BBE\u8BA1\u4E00\u81F4\u6027\u68C0\u67E5\u3002";
|
|
1223
|
+
return [
|
|
1224
|
+
report.status === "ready" ? "\u5C0F\u6302\u4EF6\u7ED3\u6784\u68C0\u67E5\u4E0E\u5F53\u524D\u7248\u672C\u9884\u89C8\u6838\u5BF9\u8BB0\u5F55\u9F50\u5168\u3002" : "\u5C0F\u6302\u4EF6\u4EA4\u4ED8\u672A\u5B8C\u6210\uFF1A",
|
|
1225
|
+
...report.issues,
|
|
1226
|
+
...report.items.map((item) => `${item.name}\uFF08${item.id}\uFF09\uFF1A${item.status === "verified" ? "\u6838\u5BF9\u5DF2\u8BB0\u5F55" : item.status === "implemented" ? "\u5DF2\u5B9E\u73B0\uFF0C\u5F85\u6838\u5BF9" : "\u5F85\u8865\u9F50\u5B9E\u73B0"}${item.issues.length ? "\uFF1B" + item.issues.join("\uFF1B") : ""}`),
|
|
1227
|
+
report.notice
|
|
1228
|
+
].join("\n");
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// packages/cli-core/src/commands/widgets.ts
|
|
1232
|
+
var WIDGET_COMMAND_HELP = `\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u843D\u5730\u68C0\u67E5\uFF08\u672C\u5730\uFF0C\u4E0D\u4E0A\u4F20\u3001\u4E0D\u53D1\u5E03\uFF09
|
|
1233
|
+
buddy-cli widgets sync [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
1234
|
+
buddy-cli widgets check [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1235
|
+
buddy-cli widgets verify --id <\u8BBE\u8BA1ID> --evidence <\u9879\u76EE\u5185\u8BB0\u5F55.json> [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
1236
|
+
sync \u4ECE\u5F53\u524D\u5DF2\u9A8C\u6536 Creator \u4EA4\u4ED8\u7ED1\u5B9A\u8BBE\u8BA1\uFF0C\u751F\u6210\u5B9E\u73B0\u6620\u5C04\uFF1B\u4FDD\u7559\u5DF2\u6709\u6620\u5C04\uFF0C\u8BBE\u8BA1\u53D8\u5316\u540E\u91CD\u65B0\u6838\u5BF9\u3002
|
|
1237
|
+
check \u9010\u9879\u5217\u51FA\u9057\u6F0F\u3001\u8FC7\u671F\u7248\u672C\u548C\u5F85\u6838\u5BF9\u9879\uFF1B\u9000\u51FA\u7801 1 \u8868\u793A\u672A\u5B8C\u6210\u3002DEV \u53EF\u7EE7\u7EED\u8C03\u8BD5\uFF0Cpush \u4F1A\u62E6\u622A\u3002
|
|
1238
|
+
verify \u8BB0\u5F55\u771F\u5B9E\u9884\u89C8\u7684\u4E09\u6001\u8BC1\u636E\u4E0E\u4E94\u9879\u89C2\u5BDF\uFF0C\u5E76\u7ED1\u5B9A\u5F53\u524D\u4EE3\u7801\u6458\u8981\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u5224\u65AD\u89C6\u89C9\u4E00\u81F4\u3002`;
|
|
1239
|
+
async function runWidgetsCommand(args, io, cwd) {
|
|
1240
|
+
try {
|
|
1241
|
+
const [action, ...rest] = args;
|
|
1242
|
+
if (!action || action === "--help" || action === "help") {
|
|
1243
|
+
io.write(WIDGET_COMMAND_HELP);
|
|
1244
|
+
return { exitCode: 0 };
|
|
1245
|
+
}
|
|
1246
|
+
if (!["sync", "check", "verify"].includes(action)) throw new Error(WIDGET_COMMAND_HELP);
|
|
1247
|
+
const flags = /* @__PURE__ */ new Map();
|
|
1248
|
+
for (let i = 0; i < rest.length; i++) {
|
|
1249
|
+
const key = rest[i];
|
|
1250
|
+
if (!["--directory", "--id", "--evidence", "--json"].includes(key) || flags.has(key)) throw new Error(`\u4E0D\u652F\u6301\u6216\u91CD\u590D\u7684\u53C2\u6570\uFF1A${key}`);
|
|
1251
|
+
if (key === "--json") flags.set(key, "true");
|
|
1252
|
+
else {
|
|
1253
|
+
const value = rest[++i];
|
|
1254
|
+
if (!value || value.startsWith("--")) throw new Error(`\u7F3A\u5C11\u53C2\u6570\uFF1A${key}`);
|
|
1255
|
+
flags.set(key, value);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
if (action !== "verify" && (flags.has("--id") || flags.has("--evidence"))) throw new Error("--id / --evidence \u4EC5\u7528\u4E8E verify");
|
|
1259
|
+
if (action !== "check" && flags.has("--json")) throw new Error("--json \u4EC5\u7528\u4E8E check");
|
|
1260
|
+
const root = await findBuddyProjectRoot(resolve5(cwd, flags.get("--directory") ?? "."));
|
|
1261
|
+
if (!root) throw new Error("\u8BF7\u5728\u642D\u5B50\u5DE5\u7A0B\u4E2D\u8FD0\u884C\uFF0C\u6216\u6307\u5B9A --directory");
|
|
1262
|
+
if (action === "sync") {
|
|
1263
|
+
await syncWidgetDesign(root);
|
|
1264
|
+
io.write("\u5DF2\u540C\u6B65\u5F53\u524D\u9A8C\u6536\u8BBE\u8BA1\u5230 widget-design.lock.json\uFF0C\u5E76\u751F\u6210 widget-implementation.json\uFF1B\u8BF7\u9010\u9879\u586B\u5199\u5B57\u6BB5\u4E0E\u5B9E\u73B0\u8DEF\u5F84\uFF0C\u968F\u540E\u8FD0\u884C widgets check\u3002");
|
|
1265
|
+
return { exitCode: 0 };
|
|
1266
|
+
}
|
|
1267
|
+
if (action === "verify") {
|
|
1268
|
+
if (!flags.get("--id") || !flags.get("--evidence")) throw new Error("verify \u9700\u8981 --id \u4E0E --evidence");
|
|
1269
|
+
await recordWidgetVerification(root, flags.get("--id"), flags.get("--evidence"));
|
|
1270
|
+
io.write("\u5DF2\u4FDD\u5B58\u4E0E\u5F53\u524D\u4EE3\u7801\u7ED1\u5B9A\u7684\u9884\u89C8\u6838\u5BF9\u8BB0\u5F55\uFF1B\u8FD9\u4E0D\u4EE3\u8868\u5F00\u53D1\u8005\u4F53\u9A8C\u9A8C\u6536\u6216\u53D1\u5E03\u6210\u529F\u3002");
|
|
1271
|
+
}
|
|
1272
|
+
const report = await checkWidgetDelivery(root);
|
|
1273
|
+
io.write(flags.has("--json") ? JSON.stringify(report, null, 2) : widgetDeliverySummary(report));
|
|
1274
|
+
return { exitCode: report.status === "incomplete" ? 1 : 0 };
|
|
1275
|
+
} catch (cause) {
|
|
1276
|
+
io.writeError(cause instanceof Error ? cause.message : String(cause));
|
|
1277
|
+
return { exitCode: 1 };
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// packages/cli-core/src/index.ts
|
|
1282
|
+
import { lstat as lstat10 } from "node:fs/promises";
|
|
1283
|
+
|
|
1284
|
+
// packages/cli-core/src/commands/datasets.ts
|
|
1285
|
+
import { resolve as resolve7 } from "node:path";
|
|
1286
|
+
|
|
1287
|
+
// packages/cli-core/src/config/runtime-catalog.ts
|
|
1288
|
+
import { readFile as readFile4, realpath as realpath3 } from "node:fs/promises";
|
|
1289
|
+
import { createRequire } from "node:module";
|
|
1290
|
+
import { resolve as resolve6 } from "node:path";
|
|
1291
|
+
import { pathToFileURL } from "node:url";
|
|
1292
|
+
|
|
1293
|
+
// packages/cli-core/src/config/runtime-dependency.ts
|
|
1294
|
+
var BUDDY_RUNTIME_PACKAGE = "@my-life-buddies/buddy-runtime";
|
|
1295
|
+
function runtimeDependencyVersion(pkg) {
|
|
1296
|
+
if (typeof pkg !== "object" || pkg === null || !("dependencies" in pkg)) return void 0;
|
|
1297
|
+
const dependencies2 = pkg.dependencies;
|
|
1298
|
+
if (typeof dependencies2 !== "object" || dependencies2 === null || !(BUDDY_RUNTIME_PACKAGE in dependencies2)) return void 0;
|
|
1299
|
+
const version = dependencies2[BUDDY_RUNTIME_PACKAGE];
|
|
1300
|
+
return typeof version === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version) ? version : void 0;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// packages/cli-core/src/config/runtime-catalog.ts
|
|
1304
|
+
async function readRuntimeDataCatalog(projectRoot) {
|
|
1305
|
+
const packagePath = resolve6(projectRoot, "package.json");
|
|
1306
|
+
const expected = runtimeDependencyVersion(JSON.parse(await readFile4(packagePath, "utf8")));
|
|
1307
|
+
if (!expected) throw new Error(`package.json \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
1308
|
+
const projectRequire = createRequire(packagePath);
|
|
1309
|
+
let runtimePath;
|
|
1310
|
+
try {
|
|
1311
|
+
const installedPath = await realpath3(resolve6(projectRoot, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"));
|
|
1312
|
+
if (await realpath3(projectRequire.resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`)) !== installedPath) {
|
|
1313
|
+
throw new Error("Runtime \u5FC5\u987B\u5B89\u88C5\u5728\u5F53\u524D\u5DE5\u7A0B\u4E2D");
|
|
1314
|
+
}
|
|
1315
|
+
const installed = JSON.parse(await readFile4(installedPath, "utf8"));
|
|
1316
|
+
if (installed.version !== expected) throw new Error(`\u5DF2\u5B89\u88C5 ${installed.version}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
|
|
1317
|
+
runtimePath = projectRequire.resolve(BUDDY_RUNTIME_PACKAGE);
|
|
1318
|
+
} catch (cause) {
|
|
1319
|
+
throw new Error(`\u5DE5\u7A0B\u7684 Runtime \u4F9D\u8D56\u672A\u6B63\u786E\u5B89\u88C5\uFF0C\u8BF7\u6309\u9501\u6587\u4EF6\u5B89\u88C5\u4F9D\u8D56\uFF1A${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1320
|
+
}
|
|
1321
|
+
const runtime = await import(pathToFileURL(runtimePath).href);
|
|
1322
|
+
const catalog = runtime.DATA_ACCESS_CAPABILITIES;
|
|
1323
|
+
if (!Array.isArray(catalog)) {
|
|
1324
|
+
throw new Error(`\u5DE5\u7A0B\u7684 Runtime ${expected} \u672A\u5BFC\u51FA DATA_ACCESS_CAPABILITIES\uFF0C\u8BF7\u4F7F\u7528\u5305\u542B\u6570\u636E\u76EE\u5F55\u7684\u65B0 Runtime \u5305\u3002`);
|
|
1325
|
+
}
|
|
1326
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1327
|
+
const datasets = catalog.map((value) => {
|
|
1328
|
+
if (!value || typeof value !== "object") throw new Error("Runtime \u6570\u636E\u76EE\u5F55\u683C\u5F0F\u65E0\u6548");
|
|
1329
|
+
const item = value;
|
|
1330
|
+
if (typeof item.id !== "string" || !item.id.trim() || seen.has(item.id) || typeof item.title !== "string" || !item.title.trim() || typeof item.source !== "string" || !item.source.trim() || !Number.isSafeInteger(item.schemaVersion) || item.schemaVersion < 1) {
|
|
1331
|
+
throw new Error("Runtime \u6570\u636E\u76EE\u5F55\u683C\u5F0F\u65E0\u6548\u6216\u5305\u542B\u91CD\u590D ID");
|
|
1332
|
+
}
|
|
1333
|
+
seen.add(item.id);
|
|
1334
|
+
return Object.freeze({ id: item.id, title: item.title, source: item.source, schemaVersion: item.schemaVersion });
|
|
1335
|
+
});
|
|
1336
|
+
return Object.freeze({ runtimeVersion: expected, datasets: Object.freeze(datasets) });
|
|
1337
|
+
}
|
|
1338
|
+
async function validateRuntimeDatasets(projectRoot, requirements = []) {
|
|
1339
|
+
const catalog = await readRuntimeDataCatalog(projectRoot);
|
|
1340
|
+
const ids = new Set(catalog.datasets.map((item) => item.id));
|
|
1341
|
+
for (const [index, item] of requirements.entries()) {
|
|
1342
|
+
if (!ids.has(item.id)) throw new Error(`/datasets/${index}/id\uFF1A\u5DE5\u7A0B Runtime ${catalog.runtimeVersion} \u4E0D\u652F\u6301 ${item.id}`);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// packages/cli-core/src/commands/datasets.ts
|
|
1347
|
+
var DATASETS_COMMAND_HELP = `\u7528\u6CD5: buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1348
|
+
\u8BFB\u53D6\u5DE5\u7A0B\u6240\u5B89\u88C5 Runtime \u7684 DATA_ACCESS_CAPABILITIES\uFF0C\u65E0\u9700\u767B\u5F55\u6216\u8BF7\u6C42\u5E73\u53F0\u76EE\u5F55\u3002
|
|
1349
|
+
\u5148\u6309\u5DE5\u7A0B\u9501\u6587\u4EF6\u5B89\u88C5\u4F9D\u8D56\u3002\u5C06\u6240\u9009 id \u4E0E\u7528\u9014 purpose \u5199\u5165 buddy.agent.json.datasets\uFF1B\u76EE\u5F55\u4E0D\u4EE3\u8868\u7528\u6237\u5DF2\u6388\u6743\u3002`;
|
|
1350
|
+
async function runDatasetsCommand(args, io, cwd) {
|
|
1351
|
+
try {
|
|
1352
|
+
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
1353
|
+
io.write(DATASETS_COMMAND_HELP);
|
|
1354
|
+
return { exitCode: 0 };
|
|
1355
|
+
}
|
|
1356
|
+
let directory;
|
|
1357
|
+
let json = false;
|
|
1358
|
+
for (let i = 0; i < args.length; i++) {
|
|
1359
|
+
if (args[i] === "--json" && !json) json = true;
|
|
1360
|
+
else if (args[i] === "--directory" && directory === void 0 && args[i + 1]?.trim() && !args[i + 1].startsWith("--")) directory = args[++i];
|
|
1361
|
+
else throw new Error(`datasets \u53C2\u6570\u65E0\u6548\uFF1A${args[i]}
|
|
1362
|
+
${DATASETS_COMMAND_HELP}`);
|
|
1363
|
+
}
|
|
1364
|
+
const root = await requireBuddyProjectRoot(resolve7(cwd, directory ?? "."));
|
|
1365
|
+
const catalog = await readRuntimeDataCatalog(root);
|
|
1366
|
+
if (json) io.write(JSON.stringify(catalog, null, 2));
|
|
1367
|
+
else {
|
|
1368
|
+
io.write(`\u5DE5\u7A0B Runtime ${catalog.runtimeVersion} \u63D0\u4F9B ${catalog.datasets.length} \u9879\u6570\u636E\u80FD\u529B\uFF1A`);
|
|
1369
|
+
for (const item of catalog.datasets) io.write(`- ${item.id} \u2014 ${item.title}\uFF08${item.source}\uFF0CSchema ${item.schemaVersion}\uFF09`);
|
|
1370
|
+
io.write("\u5C06\u6240\u9009 id \u548C purpose \u5199\u5165 buddy.agent.json.datasets\uFF1B\u7528\u6237\u6388\u6743\u7531\u5E73\u53F0\u5904\u7406\u3002");
|
|
1371
|
+
}
|
|
1372
|
+
return { exitCode: 0 };
|
|
1373
|
+
} catch (cause) {
|
|
1374
|
+
io.writeError(cause instanceof Error ? cause.message : String(cause));
|
|
1375
|
+
return { exitCode: 2 };
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// packages/cli-core/src/index.ts
|
|
1380
|
+
import { join as join11, resolve as resolve15 } from "node:path";
|
|
1381
|
+
|
|
756
1382
|
// packages/cli-core/src/init/creator.ts
|
|
757
1383
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
758
|
-
import { link as link2, lstat as
|
|
759
|
-
import { join as
|
|
1384
|
+
import { link as link2, lstat as lstat6, mkdir as mkdir4, readFile as readFile5, unlink as unlink3, writeFile as writeFile2 } from "node:fs/promises";
|
|
1385
|
+
import { join as join5 } from "node:path";
|
|
760
1386
|
import { fileURLToPath } from "node:url";
|
|
761
1387
|
|
|
762
1388
|
// packages/cli-core/src/init/development-handoff.ts
|
|
@@ -766,7 +1392,7 @@ var DEVELOPMENT_HANDOFF = `## \u5F00\u53D1\u5B8C\u6210\u540E\u4EA4\u7ED9\u5F00\u
|
|
|
766
1392
|
${DEVELOPMENT_HANDOFF_SUMMARY}
|
|
767
1393
|
|
|
768
1394
|
1. \u6839\u636E\u5DF2\u786E\u8BA4\u7684\u9700\u6C42\u5B8C\u6210 Agent / Prompt / Tool \u4E0E\u56DE\u5408\u94A9\u5B50\uFF1B\u6709 Booklet \u65F6\u4EE5\u5DF2\u786E\u8BA4\u624B\u518C\u4E3A\u51C6\u3002\u6A21\u677F\u751F\u6210\u4E0D\u7B49\u4E8E\u4E1A\u52A1\u5DF2\u5B9E\u73B0\u3002
|
|
769
|
-
2. \u505A\u57FA\u7840\u6280\u672F\u81EA\u6D4B\uFF1A\u68C0\u67E5\u914D\u7F6E\u3001\u4F9D\u8D56\u3001\u6784\u5EFA\u548C\u542F\u52A8\u5165\u53E3\uFF0C\u6267\u884C\u5DF2\u6709\u81EA\u52A8\u5316\u6D4B\u8BD5\u53CA\u660E\u786E\u4E1A\u52A1\u89C4\u5219\u3001\u5B89\u5168\u8FB9\u754C\u7684\u57FA\u7840\u6D4B\u8BD5\u3002\u4E0D\u8981\u628A\u6280\u672F\u68C0\u67E5\u901A\u8FC7\u8BF4\u6210\u4EA7\u54C1\u4F53\u9A8C\u5DF2\u9A8C\u6536\u3002
|
|
1395
|
+
2. \u6709\u5DF2\u9A8C\u6536\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u65F6\u6309\u5B9E\u73B0\u6307\u5F15\u6267\u884C \`widgets sync \u2192 widgets check \u2192 \u771F\u5B9E\u9884\u89C8\u5BF9\u7167 \u2192 widgets verify\`\uFF0C\u9010\u9879\u62A5\u544A\u72B6\u6001\uFF1B\u68C0\u67E5\u672A\u5B8C\u6210\u65F6\u4E0D\u80FD\u5C06\u7B80\u5316\u7248\u79F0\u4E3A\u5DF2\u6309\u8BBE\u8BA1\u4EA4\u4ED8\u3002\u505A\u57FA\u7840\u6280\u672F\u81EA\u6D4B\uFF1A\u68C0\u67E5\u914D\u7F6E\u3001\u4F9D\u8D56\u3001\u6784\u5EFA\u548C\u542F\u52A8\u5165\u53E3\uFF0C\u6267\u884C\u5DF2\u6709\u81EA\u52A8\u5316\u6D4B\u8BD5\u53CA\u660E\u786E\u4E1A\u52A1\u89C4\u5219\u3001\u5B89\u5168\u8FB9\u754C\u7684\u57FA\u7840\u6D4B\u8BD5\u3002\u4E0D\u8981\u628A\u6280\u672F\u68C0\u67E5\u901A\u8FC7\u8BF4\u6210\u4EA7\u54C1\u4F53\u9A8C\u5DF2\u9A8C\u6536\u3002
|
|
770
1396
|
3. \u4F7F\u7528 \`buddy-cli preview\` \u6253\u5F00\u5F53\u524D\u5DE5\u7A0B\uFF0C\u5728\u9875\u9762\u542F\u52A8 DEV\uFF1B\u5DF2\u6709\u53EF\u7528\u9884\u89C8\u5219\u590D\u7528\uFF0C\u4E0D\u540C\u65F6\u518D\u542F\u52A8\u4E00\u5957 \`dev\` \u8FDB\u7A0B\u3002\u786E\u8BA4\u540D\u79F0\u3001DEV \u72B6\u6001\u548C\u79C1\u804A\u6D88\u606F\u5F80\u8FD4\uFF1B\u82E5\u7F3A\u5C11\u5F80\u8FD4\u8BC1\u636E\uFF0C\u53EA\u53D1\u9001\u4E00\u6761\u4E0D\u542B\u4E2A\u4EBA\u654F\u611F\u4FE1\u606F\u7684\u8054\u8C03\u6D88\u606F\uFF0C\u5DF2\u6709\u5F80\u8FD4\u8BC1\u636E\u65F6\u4E0D\u91CD\u590D\u53D1\u9001\u3002\u5931\u8D25\u5148\u5B9A\u4F4D\u6280\u672F\u95EE\u9898\uFF1B\u4ECD\u672A\u89E3\u51B3\u65F6\u8BF4\u660E\u963B\u585E\uFF0C\u4E0D\u628A Mock\u3001\u9875\u9762\u6253\u5F00\u6216\u8FDB\u7A0B\u542F\u52A8\u5F53\u6210\u94FE\u8DEF\u5DF2\u901A\u3002
|
|
771
1397
|
4. \u53EF\u4F53\u9A8C\u540E\u4FDD\u7559\u9884\u89C8\u548C DEV\uFF0C\u63D0\u4F9B\u5B9E\u9645\u6253\u5F00\u7684\u9875\u9762\u5730\u5740\uFF0C\u544A\u8BC9\u5F00\u53D1\u8005\u201C\u73B0\u5728\u53EF\u4EE5\u5F00\u59CB\u8BD5\u804A\u4E86\u201D\uFF0C\u8BF4\u660E\u5DF2\u9A8C\u8BC1\u8303\u56F4\u548C\u5DF2\u77E5\u9650\u5236\uFF0C\u7136\u540E\u7B49\u5F85\u53CD\u9988\u3002\u4E0B\u4E00\u8F6E\u7531\u5F00\u53D1\u8005\u4F53\u9A8C\u540E\u53CD\u9988\uFF0C\u518D\u6309\u53CD\u9988\u4FEE\u6539\u548C\u9A8C\u8BC1\u3002
|
|
772
1398
|
|
|
@@ -784,7 +1410,7 @@ ${IMPLEMENTATION_GUIDE_SUMMARY}
|
|
|
784
1410
|
|
|
785
1411
|
### \u5148\u770B\u9700\u6C42\uFF0C\u4E0D\u91CD\u65B0\u91C7\u8BBF
|
|
786
1412
|
|
|
787
|
-
Creator \u8FD4\u56DE\
|
|
1413
|
+
Creator \u8DEF\u7EBF\u987B\u7B49\u56DB\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355\u3001\u5F53\u524D\u539F\u578B\u53CA\u8BBE\u8BA1\u9A8C\u6536\u5168\u90E8\u786E\u8BA4\uFF0C\u4E14\u4EA4\u4ED8\u6821\u9A8C\u8FD4\u56DE \`directive=creator_complete\`\u3001\`completion.status=ready\` \u540E\uFF0C\u518D\u8FDB\u5165\u5F00\u53D1\uFF1B\u65E7\u9879\u76EE\u6309 Skill \u7684\u7248\u672C\u63A5\u7EED\u89C4\u5219\u5224\u65AD\u3002\u56DB\u518C\u786E\u8BA4\u6216\u5B58\u5728 \`completion.manualPath\` \u5747\u4E0D\u8DB3\u4EE5\u89E6\u53D1\u5F00\u53D1\u3002\u5728\u5DF2\u6388\u6743\u7684\u5B8C\u6574\u521B\u5EFA\u4EFB\u52A1\u4E2D\uFF0C\u5F53\u524D Coding Agent \u9000\u51FA Skill \u540E\u4E8E\u540C\u4E00\u8F6E\u8BFB\u53D6\u5B8C\u6574\u624B\u518C\uFF0C\u5305\u62EC\u5B9A\u4E49\u3001\u77E5\u8BC6\u3001\u65B9\u6CD5\u3001\u670D\u52A1\u3001\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u4E0E\u9A8C\u6536\u8BB0\u5F55\u4EE5\u53CA\u5F85\u8865\u5145\u9879\uFF0C\u68C0\u67E5\u73B0\u6709\u5DE5\u7A0B\u5E76\u76F4\u63A5\u5F00\u59CB\u5B9E\u73B0\uFF0C\u4E0D\u518D\u7B49\u5F85\u4E00\u6B21\u5F00\u53D1\u786E\u8BA4\u3002\u624B\u518C\u51B3\u5B9A\u642D\u5B50\u5E94\u8BE5\u600E\u4E48\u505A\uFF0CCoding Agent \u8D1F\u8D23\u9009\u62E9\u5408\u9002\u7684\u5B9E\u73B0\uFF1B\u624B\u518C\u4E0D\u9700\u8981\u5217\u51FA Tool\u3001Skill\u3001Hook \u7B49\u6280\u672F\u540D\u8BCD\u3002
|
|
788
1414
|
|
|
789
1415
|
\u76F4\u63A5\u5F00\u53D1\u65F6\uFF0C\u4F7F\u7528\u5F00\u53D1\u8005\u5DF2\u63D0\u4F9B\u5E76\u786E\u8BA4\u7684\u9700\u6C42\uFF0C\u4E0D\u5F3A\u5236\u8865\u505A Creator \u6216\u5236\u9020\u4E00\u4EFD\u201C\u5DF2\u786E\u8BA4\u624B\u518C\u201D\u3002\u6240\u6709\u5DE5\u7A0B\u7EDF\u4E00\u4F7F\u7528\u5B98\u65B9 Runtime\u3002\u5DF2\u6709\u5B98\u65B9\u5DE5\u7A0B\u5148\u68C0\u67E5\u5F53\u524D\u4EE3\u7801\u548C\u914D\u7F6E\uFF0C\u590D\u7528\u6EE1\u8DB3\u8981\u6C42\u7684\u5B9E\u73B0\uFF1B\u5DE5\u7A0B\u5B8C\u6574\u65F6\u4E0D\u91CD\u590D init\uFF0C\u7F3A\u5C11\u6A21\u677F\u6587\u4EF6\u65F6\u7528 init --mode direct \u8865\u9F50\u3002
|
|
790
1416
|
|
|
@@ -792,19 +1418,19 @@ Creator \u8FD4\u56DE\u6709\u6548\u7684 \`completion.manualPath\` \u540E\uFF0C\u8
|
|
|
792
1418
|
|
|
793
1419
|
### \u6309\u9700\u6C42\u68C0\u67E5\u9700\u8981\u54EA\u4E9B\u80FD\u529B
|
|
794
1420
|
|
|
795
|
-
\u4E0B\u9762\u662F Coding Agent \u7684\u5B9E\u73B0\u68C0\u67E5\u8868\uFF0C\u4E0D\u662F\u4EA4\u7ED9\u5F00\u53D1\u8005\u9010\u9879\u9009\u62E9\u7684\u95EE\u5377\u3002\u53EA\u5B9E\u73B0\u9700\u6C42\u7528\u5F97\u4E0A\u7684\u80FD\u529B\uFF1B\u5DF2\u6709\u5B9E\u73B0\u6216\u9ED8\u8BA4\u80FD\u529B\u6EE1\u8DB3\u65F6\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u4E3A\u4E86\u51D1\u9F50\u7C7B\u522B\u751F\u6210 Tool\u3002\u4E1A\u52A1\u80FD\u529B\u901A\u8FC7 \`agent.md\`\u3001\`src/buddy.mjs\` \u548C\u5B98\u65B9 Runtime \u7684\u6269\u5C55\u70B9\u5B9E\u73B0\u3002
|
|
1421
|
+
\u4E0B\u9762\u662F Coding Agent \u7684\u5B9E\u73B0\u68C0\u67E5\u8868\uFF0C\u4E0D\u662F\u4EA4\u7ED9\u5F00\u53D1\u8005\u9010\u9879\u9009\u62E9\u7684\u95EE\u5377\u3002\u53EA\u5B9E\u73B0\u9700\u6C42\u7528\u5F97\u4E0A\u7684\u80FD\u529B\uFF1B\u5DF2\u6709\u5B9E\u73B0\u6216\u9ED8\u8BA4\u80FD\u529B\u6EE1\u8DB3\u65F6\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u4E3A\u4E86\u51D1\u9F50\u7C7B\u522B\u751F\u6210 Tool\u3002\u4E1A\u52A1\u80FD\u529B\u901A\u8FC7 \`agent.md\`\u3001\`src/buddy-options.mjs\` \u548C\u5B98\u65B9 Runtime \u7684\u6269\u5C55\u70B9\u5B9E\u73B0\u3002
|
|
796
1422
|
|
|
797
1423
|
| \u9700\u6C42\u91CC\u8981\u6C42\u4EC0\u4E48 | \u68C0\u67E5\u4E0E\u5B9E\u73B0\u65B9\u5F0F |
|
|
798
1424
|
| --- | --- |
|
|
799
1425
|
| \u89D2\u8272\u3001\u8868\u8FBE\u65B9\u5F0F\u3001\u804C\u8D23\u548C\u8FB9\u754C | \u843D\u5230 \`agent.md\` \u7684 Prompt \u4E0E\u5FC5\u8981\u7684\u6267\u884C\u6821\u9A8C\uFF1B\u4E0D\u91CD\u65B0\u8BE2\u95EE\u624B\u518C\u5DF2\u660E\u786E\u7684\u8BED\u6C14\u548C\u76EE\u6807\u3002 |
|
|
800
1426
|
| \u8BA1\u7B97\u3001\u67E5\u627E\u6216\u6267\u884C\u5177\u4F53\u64CD\u4F5C | \u5224\u65AD\u662F\u5426\u786E\u5B9E\u9700\u8981 Tool\uFF1B\u5DF2\u6709\u5DE5\u5177\u76F4\u63A5\u590D\u7528\uFF0C\u9700\u8981\u65B0\u5DE5\u5177\u624D\u5B9E\u73B0\u5E76\u6CE8\u518C\u3002\u4F7F\u7528\u5E73\u53F0\u63D0\u4F9B\u7684\u63A5\u53E3\uFF0C\u4E0D\u80FD\u76F4\u63A5\u8C03\u7528\u7B2C\u4E09\u65B9\u670D\u52A1\uFF1B\u6743\u9650\u548C\u6570\u636E\u6765\u6E90\u5FC5\u987B\u5B9E\u9645\u53EF\u7528\uFF0C\u4E0D\u80FD\u7528\u5047\u6570\u636E\u5192\u5145\u5DF2\u63A5\u901A\u3002 |
|
|
801
|
-
| \u4E13\u4E1A\u65B9\u6CD5\u3001\u6B65\u9AA4\u4E0E\u6848\u4F8B | \u7B80\u5355\u89C4\u5219\u53EF\u76F4\u63A5\u5199\u5165 Prompt\uFF1B\u56FA\u5B9A\u53C2\u8003\u8D44\u6599\u53EF\u653E\u5728 \`resources/**/*.md\`\uFF0C\u5E76\u5728 \`src/buddy.mjs\` \u7684 \`
|
|
802
|
-
| \u5FC5\u987B\u9075\u5FAA\u7684\u5904\u7406\u987A\u5E8F\u6216\u9644\u52A0\u5904\u7406 | \u5148\u770B Runtime \u9ED8\u8BA4\u56DE\u5408\u662F\u5426\u6EE1\u8DB3\uFF1B\u5FC5\u987B\u7531\u7A0B\u5E8F\u4FDD\u8BC1\u7684\u987A\u5E8F\u53EF\u7528 Tool\uFF0C\u6216\u5728 \`src/buddy.mjs\` \u4F7F\u7528 \`beforeToolCall\`\u3001\`afterToolCall\`\u3001\`shouldStopAfterTurn\` \u7B49\u56DE\u5408\u94A9\u5B50\u3002 |
|
|
1427
|
+
| \u4E13\u4E1A\u65B9\u6CD5\u3001\u6B65\u9AA4\u4E0E\u6848\u4F8B | \u7B80\u5355\u89C4\u5219\u53EF\u76F4\u63A5\u5199\u5165 Prompt\uFF1B\u56FA\u5B9A\u53C2\u8003\u8D44\u6599\u53EF\u653E\u5728 \`resources/**/*.md\`\uFF0C\u5E76\u5728 \`src/buddy-options.mjs\` \u7684 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.resource_list\` \u4E0E \`conversation.tools.resource_read\`\u3002Creator Skill \u662F\u521B\u4F5C\u5DE5\u5177\uFF0C\u4E0D\u4F1A\u88AB Runtime \u5F53\u4F5C\u4E1A\u52A1\u811A\u672C\u6267\u884C\u3002 |
|
|
1428
|
+
| \u5FC5\u987B\u9075\u5FAA\u7684\u5904\u7406\u987A\u5E8F\u6216\u9644\u52A0\u5904\u7406 | \u5148\u770B Runtime \u9ED8\u8BA4\u56DE\u5408\u662F\u5426\u6EE1\u8DB3\uFF1B\u5FC5\u987B\u7531\u7A0B\u5E8F\u4FDD\u8BC1\u7684\u987A\u5E8F\u53EF\u7528 Tool\uFF0C\u6216\u5728 \`src/buddy-options.mjs\` \u4F7F\u7528 \`beforeToolCall\`\u3001\`afterToolCall\`\u3001\`shouldStopAfterTurn\` \u7B49\u56DE\u5408\u94A9\u5B50\u3002 |
|
|
803
1429
|
| \u4EE5\u540E\u8FD8\u9700\u8981\u8BB0\u4F4F\u7684\u4FE1\u606F | \u901A\u8FC7 \`conversation.memory\` \u843D\u5B9E\u4FDD\u5B58\u3001\u8BFB\u53D6\u3001\u66F4\u65B0\u548C\u9057\u5FD8\uFF0C\u5E76\u6309\u9700\u8981\u5C01\u88C5\u4E3A\u6A21\u578B Tool\u3002\u65B0 Runtime \u4E0D\u4F1A\u81EA\u52A8\u67E5\u8BE2\u6216\u63D0\u70BC\u8BB0\u5FC6\uFF1B\u662F\u5426\u4FDD\u5B58\u3001\u4FDD\u5B58\u4EC0\u4E48\u5FC5\u987B\u7531\u4E1A\u52A1\u4EE3\u7801\u548C\u5DF2\u786E\u8BA4\u7684\u6570\u636E\u8FB9\u754C\u51B3\u5B9A\u3002 |
|
|
804
1430
|
| \u56DE\u7B54\u65F6\u9700\u8981\u53C2\u8003\u7684\u5386\u53F2\u3001\u8BA1\u5212\u548C\u72B6\u6001 | Runtime \u4F1A\u4ECE MLB \u91CD\u5EFA\u4F1A\u8BDD\u5386\u53F2\uFF1B\u957F\u671F\u8BA1\u5212\u548C\u7ED3\u6784\u5316\u72B6\u6001\u4E0D\u80FD\u53EA\u4F9D\u8D56\u5B83\u4E00\u76F4\u7559\u5728\u6700\u8FD1\u804A\u5929\u91CC\u3002\u6309\u9700\u6C42\u4F7F\u7528 Memory\u3001\u6388\u6743\u6570\u636E\u6216\u4E1A\u52A1 Tool\uFF0C\u4E0D\u8981\u627F\u8BFA\u9ED8\u8BA4\u914D\u7F6E\u5DF2\u7ECF\u8986\u76D6\u3002 |
|
|
805
|
-
| \u6A21\u578B\u548C\u5E73\u53F0\u8D44\u6E90 | \u5DF2\u786E\u5B9A\u7684\u6A21\u578B\u76F4\u63A5\u6CBF\u7528\uFF1B\u672A\u6307\u5B9A\u65F6\u67E5\u8BE2 \`buddy-cli models\`\uFF0C\u6838\u5BF9\u53EF\u7528\u6027\u5E76\u91C7\u7528\u6EE1\u8DB3\u9700\u6C42\u7684\u9009\u62E9\uFF0C\u4E0D\u9ED8\u8BA4\u518D\u95EE\u4E00\u8F6E\u9009\u578B\u95EE\u9898\uFF0C\u4E5F\u4E0D\u7F16\u9020\u4EF7\u683C\u6216\u80FD\u529B\u3002\
|
|
1431
|
+
| \u6A21\u578B\u548C\u5E73\u53F0\u8D44\u6E90 | \u5DF2\u786E\u5B9A\u7684\u6A21\u578B\u76F4\u63A5\u6CBF\u7528\uFF1B\u672A\u6307\u5B9A\u65F6\u67E5\u8BE2 \`buddy-cli models\`\uFF0C\u6838\u5BF9\u53EF\u7528\u6027\u5E76\u91C7\u7528\u6EE1\u8DB3\u9700\u6C42\u7684\u9009\u62E9\uFF0C\u4E0D\u9ED8\u8BA4\u518D\u95EE\u4E00\u8F6E\u9009\u578B\u95EE\u9898\uFF0C\u4E5F\u4E0D\u7F16\u9020\u4EF7\u683C\u6216\u80FD\u529B\u3002\u6A21\u677F\u628A\u914D\u7F6E\u4E2D\u7684\u6A21\u578B\u4EA4\u7ED9 \`initialState.model\`\uFF1B\u81EA\u5B9A\u4E49\u5DE5\u5382\u4E5F\u53EF\u6309\u4F1A\u8BDD\u9009\u62E9\u53D7\u652F\u6301\u6A21\u578B\uFF1B\u4F1A\u4EA7\u751F\u65B0\u7684\u8D39\u7528\u3001\u6570\u636E\u4F7F\u7528\u6216\u6743\u9650\u8FB9\u754C\u65F6\u5148\u786E\u8BA4\u3002 |
|
|
806
1432
|
|
|
807
|
-
\`src/buddy.mjs\` \u662F\u6BCF\u573A\u4F1A\u8BDD\u7684\u4E1A\u52A1\u88C5\u914D\u5165\u53E3\uFF0C\u5BFC\u51FA \`
|
|
1433
|
+
\`src/buddy-options.mjs\` \u662F\u6BCF\u573A\u4F1A\u8BDD\u7684\u4E1A\u52A1\u88C5\u914D\u5165\u53E3\uFF0C\u5BFC\u51FA \`createBuddyOptions(conversation)\`\uFF0C\u8FD4\u56DE \`initialState\`\u3001\u5728 \`initialState.tools\` \u6CE8\u518C\u7684\u4E1A\u52A1\u53CA\u5E73\u53F0\u5DE5\u5177\u548C\u53EF\u9009\u56DE\u5408\u94A9\u5B50\u3002\`start.mjs\` \u4ECE \`./src/buddy-options.mjs\` \u5BFC\u5165\u5DE5\u5382\u5E76\u4F20\u7ED9 \`BuddyServer.start({ dataRequirements: config.datasets ?? [], buddy: createBuddyOptions })\`\uFF1B\u7EC4\u88C5\u5668\u8BFB\u53D6 \`buddy.agent.json.model\`\uFF0C\u7701\u7565\u65F6\u7528 \`kimi-k2.6\`\uFF0C\u653E\u5165 \`initialState.model\`\uFF1B\u7528 \`new URL("../agent.md", import.meta.url)\` \u8BFB\u53D6\u6839\u76EE\u5F55 Prompt\u3002\u9ED8\u8BA4\u6A21\u677F\u53EA\u662F\u8D77\u70B9\uFF0C\u53EA\u6539\u8BF4\u660E\u4E0D\u7B49\u4E8E\u6539\u53D8\u4E86\u4FDD\u5B58\u3001\u67E5\u8BE2\u6216\u5904\u7406\u903B\u8F91\u3002
|
|
808
1434
|
|
|
809
1435
|
### \u6309\u76EE\u5F55\u804C\u8D23\u7EC4\u7EC7\u5B9E\u73B0
|
|
810
1436
|
|
|
@@ -814,24 +1440,35 @@ Creator \u8FD4\u56DE\u6709\u6548\u7684 \`completion.manualPath\` \u540E\uFF0C\u8
|
|
|
814
1440
|
| \`agent.md\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u4FDD\u5B58\u7CFB\u7EDF Prompt |
|
|
815
1441
|
| \`start.mjs\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u8BFB\u53D6\u542F\u52A8\u914D\u7F6E\u3001\u542F\u52A8 Runtime \u5E76\u5904\u7406\u9000\u51FA |
|
|
816
1442
|
| \`package.json\`\u3001\`package-lock.json\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u58F0\u660E\u542F\u52A8\u547D\u4EE4\u3001\u51C6\u786E Runtime \u7248\u672C\u5E76\u9501\u5B9A\u4F9D\u8D56 |
|
|
817
|
-
| \`src/buddy.mjs\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u7EC4\u88C5\u5E76\u6CE8\u518C\u4F1A\u8BDD\u4E2D\u7684\u80FD\u529B |
|
|
1443
|
+
| \`src/buddy-options.mjs\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u7EC4\u88C5\u5E76\u6CE8\u518C\u4F1A\u8BDD\u4E2D\u7684\u80FD\u529B |
|
|
818
1444
|
| \`src/tools/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E\u6A21\u578B\u53EF\u8C03\u7528\u7684\u5DE5\u5177\u540D\u79F0\u3001\u8BF4\u660E\u3001\u53C2\u6570 Schema \u548C\u6267\u884C\u51FD\u6570 |
|
|
819
|
-
| \`src/hooks/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E\u5DE5\u5177\u8C03\u7528\u524D\u540E\u3001\
|
|
1445
|
+
| \`src/hooks/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E\u5DE5\u5177\u8C03\u7528\u524D\u540E\u3001\u505C\u6B62\u7B49\u94A9\u5B50 |
|
|
820
1446
|
| \`src/services/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E\u4F9B Tool\u3001Hook \u4F7F\u7528\u7684\u4E1A\u52A1\u903B\u8F91\u6216\u5E73\u53F0\u63A5\u53E3\u5C01\u88C5 |
|
|
821
1447
|
| \`resources/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E Runtime \u542F\u52A8\u65F6\u8BFB\u53D6\u7684 Markdown \u53C2\u8003\u8D44\u6599 |
|
|
822
1448
|
| \`widgets/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u6BCF\u4E2A\u7C7B\u578B\u653E\u5728 \`widgets/<type-id>/\`\uFF0C\u5176\u4E2D \`index.html\` \u662F\u9875\u9762\uFF0C\`schema.json\` \u662F\u7C7B\u578B\u7684\u6570\u636E\u5B9A\u4E49 |
|
|
823
1449
|
| \`tests/\` | \u7F16\u5199\u4E1A\u52A1\u6D4B\u8BD5\u65F6\u6309\u9700\u521B\u5EFA |
|
|
824
1450
|
| \`.buddy/\`\u3001\`node_modules/\` | \u7531\u521B\u4F5C\u6D41\u7A0B\u6216\u4F9D\u8D56\u5B89\u88C5\u751F\u6210\uFF0C\u4E0D\u653E\u4E1A\u52A1\u6E90\u7801 |
|
|
825
1451
|
|
|
826
|
-
Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u662F\u666E\u901A\u4E1A\u52A1\u4EE3\u7801\uFF0CRuntime \u6CA1\u6709 Service \u6CE8\u518C\u63A5\u53E3\u3002\u7B80\u5355 Tool \u53EF\u4EE5\u76F4\u63A5\u5B9E\u73B0\u4E1A\u52A1\u64CD\u4F5C\uFF0C\u590D\u6742\u6216\u590D\u7528\u7684\u903B\u8F91\u518D\u62BD\u5230 Service\u3002\u4EE3\u7801\u6309\u4E0A\u8FF0\u804C\u8D23\u653E\u7F6E\uFF0C\u5728 \`src/buddy.mjs\` \u663E\u5F0F\u5BFC\u5165\u5E76\u6CE8\u518C\uFF0C\u4E0D\u628A\u6240\u6709\u5B9E\u73B0\u5806\u8FDB\u5165\u53E3\uFF0C\u4E5F\u4E0D\u4E3A\u76EE\u5F55\u9F50\u5168\u800C\u7F16\u9020\u80FD\u529B\u3002
|
|
1452
|
+
Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u662F\u666E\u901A\u4E1A\u52A1\u4EE3\u7801\uFF0CRuntime \u6CA1\u6709 Service \u6CE8\u518C\u63A5\u53E3\u3002\u7B80\u5355 Tool \u53EF\u4EE5\u76F4\u63A5\u5B9E\u73B0\u4E1A\u52A1\u64CD\u4F5C\uFF0C\u590D\u6742\u6216\u590D\u7528\u7684\u903B\u8F91\u518D\u62BD\u5230 Service\u3002\u4EE3\u7801\u6309\u4E0A\u8FF0\u804C\u8D23\u653E\u7F6E\uFF0C\u5728 \`src/buddy-options.mjs\` \u663E\u5F0F\u5BFC\u5165\u5E76\u6CE8\u518C\uFF0C\u4E0D\u628A\u6240\u6709\u5B9E\u73B0\u5806\u8FDB\u5165\u53E3\uFF0C\u4E5F\u4E0D\u4E3A\u76EE\u5F55\u9F50\u5168\u800C\u7F16\u9020\u80FD\u529B\u3002
|
|
827
1453
|
|
|
828
1454
|
\u7A7A\u76EE\u5F55\u7528\u4E8E\u5C55\u793A\u6269\u5C55\u4F4D\u7F6E\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u6CE8\u518C\u5DE5\u5177\u6216\u542F\u7528\u94A9\u5B50\u3002\u4E0D\u8981\u6DFB\u52A0\u5360\u4F4D\u6587\u4EF6\u3001README \u6216 \`.gitignore\`\uFF1B\u6A21\u677F\u4E0D\u627F\u62C5 Git \u4ED3\u5E93\u521D\u59CB\u5316\u3002\u6E90\u7801\u5305\u53EA\u6536\u5F55\u6587\u4EF6\uFF0C\u7A7A\u76EE\u5F55\u4E0D\u4E0A\u4F20\uFF0C\u5B9E\u9645\u4E1A\u52A1\u6587\u4EF6\u548C\u8D44\u6599\u4F1A\u6B63\u5E38\u6536\u5F55\u3002
|
|
829
1455
|
|
|
830
|
-
|
|
1456
|
+
\u5C0F\u6302\u4EF6 \u5DE5\u7A0B\u7EA6\u5B9A\uFF1A\u4E00\u7EA7\u76EE\u5F55\u540D\u5C31\u662F\u7A33\u5B9A\u7684\u7C7B\u578B ID\uFF0C\u63A8\u8350\u4F7F\u7528\u5B57\u6BCD\u5F00\u5934\u7684\u5C0F\u5199\u82F1\u6587\u3001\u6570\u5B57\u548C\u5355\u4E2A\u8FDE\u5B57\u7B26\u5206\u9694\uFF0C\u603B\u957F\u4E0D\u8D85\u8FC7 64 \u5B57\u7B26\uFF0C\u5E73\u53F0\u4E5F\u652F\u6301\u4E2D\u6587\u7C7B\u578B\u540D\uFF0C\u4F8B\u5982 \`sleep-record\`\u3002\u540C\u4E00\u4E2A Buddy \u5185\u4E0D\u80FD\u91CD\u540D\uFF0C\u4E0D\u53E6\u8BBE\u4E00\u4EFD ID \u914D\u7F6E\uFF1B\u6539\u76EE\u5F55\u540D\u7B49\u4E8E\u65B0\u5EFA\u7C7B\u578B\uFF0C\u5DF2\u6709\u5B9E\u4F8B\u4E0D\u4F1A\u81EA\u52A8\u6539\u540D\u3002\`schema.json\` \u9876\u5C42\u91C7\u7528 MLB \u7684 \`description\` \u548C \`modules\` \u7ED3\u6784\uFF0C\u5404\u6A21\u5757\u7684 \`record\` \u624D\u662F JSON Schema\uFF1B\`index.html\` \u662F\u5355\u6587\u4EF6\u9875\u9762\uFF0C\u6837\u5F0F\u4E0E\u811A\u672C\u5185\u8054\uFF0C\u4E1A\u52A1\u4E0D\u76F4\u8FDE\u7B2C\u4E09\u65B9\u670D\u52A1\u3002CLI \u628A\u5B9E\u9645\u6587\u4EF6\u968F\u5DE5\u7A0B\u6253\u5305\uFF1B\u5DE5\u7A0B\u5B89\u88C5\u7684 Runtime \u5728 BuddyServer \u542F\u52A8\u65F6\u4E00\u8D77\u4E0A\u4F20 Schema \u4E0E HTML\uFF0C\u5168\u90E8\u6210\u529F\u540E\u624D\u8FDE\u63A5\u4E8B\u4EF6\u6D41\u3002\u7F3A\u6587\u4EF6\u3001\u65E0\u6548 JSON \u6216\u4E0A\u4F20\u5931\u8D25\u4F1A\u4F7F\u542F\u52A8\u5931\u8D25\uFF0C\u5148\u4FEE\u590D\u65E5\u5FD7\u4E2D\u6307\u660E\u7684\u6587\u4EF6\u6216\u5E73\u53F0\u9519\u8BEF\u3002\u6A21\u578B\u4F7F\u7528\u5C0F\u6302\u4EF6\u5DE5\u5177\u9700\u628A\u6240\u9700 \`conversation.tools.widget_*\` \u653E\u8FDB \`initialState.tools\`\uFF1B\u4E1A\u52A1\u4E5F\u53EF\u76F4\u63A5\u8C03\u7528 \`conversation.widget\`\uFF0C\u4E3B\u52A8\u670D\u52A1\u56DE\u5408\u7981\u6B62\u5199\u64CD\u4F5C\u3002DEV \u5206\u8EAB\u4E0E\u6B63\u5F0F\u7248\u7684\u7C7B\u578B\u6309\u8FDB\u7A0B\u8EAB\u4EFD\u9694\u79BB\uFF1B\u4FEE\u6539\u6587\u4EF6\u540E\u91CD\u542F Runtime\u3002
|
|
1457
|
+
|
|
1458
|
+
\u5C0F\u6302\u4EF6\u5B9E\u73B0\u987B\u8BFB\u53D6 WIDGET_DESIGN.md\u3001\u6E05\u5355\u3001\u539F\u578B\u548C\u9A8C\u6536\u8BB0\u5F55\uFF0C\u6CBF\u7528\u5DF2\u786E\u8BA4\u7684 H5 \u6837\u5F0F\u4E0E\u6846\u67B6\uFF0C\u4E0D\u628A\u793A\u4F8B\u6570\u636E\u5F53\u4F5C\u771F\u5B9E\u4F1A\u8BDD\u6570\u636E\u3002\u9875\u9762\u53EA\u5141\u8BB8\u9884\u8BBE\u7B5B\u9009\u3001\u5207\u6362\u3001\u6298\u53E0\u3001\u6EDA\u52A8\u548C\u53EA\u8BFB\u6D6E\u5C42\uFF0C\u7981\u6B62\u8F93\u5165\u3001\u7F16\u8F91\u3001\u6253\u5361\u548C\u63D0\u4EA4\u3002\u6240\u6709\u4E1A\u52A1\u4FEE\u6539\u7531\u5BF9\u8BDD\u5904\u7406\u5E76\u66F4\u65B0\u5B9E\u4F8B\uFF0C\u6458\u8981\u4E0E\u5C55\u5F00\u9875\u540C\u6B65\u3002Creator \u7684\u8BBE\u8BA1\u9A8C\u6536\u4E0D\u8BC1\u660E\u751F\u4EA7 H5 \u6CE8\u518C\u6216\u4F1A\u8BDD\u63A5\u5165\u5B8C\u6210\uFF0C\u5B9E\u9645\u80FD\u529B\u6309\u5E73\u53F0\u63A5\u53E3\u6838\u5BF9\u5E76\u9A8C\u8BC1\u3002
|
|
1459
|
+
|
|
831
1460
|
|
|
832
1461
|
\u5E73\u53F0\u80FD\u529B\u8981\u4EE5\u5B9E\u9645\u63A5\u5165\u4E3A\u51C6\u3002\u5F53\u524D Runtime \u63D0\u4F9B\u4F1A\u8BDD\u6D88\u606F\u3001Memory\u3001\u6388\u6743\u6570\u636E\u3001\u5C0F\u6302\u4EF6\u548C\u672C\u5730 Markdown \u8D44\u6599\u80FD\u529B\uFF1B\u6CA1\u6709\u63A5\u901A\u7684 Files\u3001Knowledge \u4E0D\u80FD\u56E0\u624B\u518C\u63D0\u5230\u5C31\u5BA3\u79F0\u53EF\u7528\u3002\u666E\u901A Tool \u4E5F\u4E0D\u4EE3\u8868\u5DF2\u7ECF\u63D0\u4F9B\u5B9A\u65F6\u3001\u957F\u671F\u4EFB\u52A1\u3001\u540E\u53F0\u8C03\u5EA6\u6216\u4E3B\u52A8\u89E6\u8FBE\u3002\u624B\u518C\u63CF\u8FF0\u7684\u4ED8\u8D39\u3001\u53D1\u5E03\u7B49\u670D\u52A1\u8BBE\u8BA1\u4E5F\u4E0D\u6784\u6210\u6267\u884C\u6536\u8D39\u6216\u53D1\u5E03\u52A8\u4F5C\u7684\u6388\u6743\u3002
|
|
833
1462
|
|
|
834
|
-
\u9700\u8981\u7528\u6237\u6570\u636E\u65F6\uFF0C\u5728 \`buddy.
|
|
1463
|
+
\u9700\u8981\u7528\u6237\u6570\u636E\u65F6\uFF0C\u5148\u5728\u5DE5\u7A0B\u76EE\u5F55\u6267\u884C \`buddy-cli datasets\`\uFF08\u811A\u672C\u53EF\u7528 \`--json\`\uFF09\uFF0C\u4ECE\u5F53\u524D\u5B89\u88C5\u7684 Runtime \u5BFC\u51FA\u8BFB\u53D6\u5168\u90E8\u6570\u636E\u96C6\uFF1B\u4E0D\u8981\u6C42\u767B\u5F55\uFF0C\u4E0D\u4ECE Server \u62C9\u76EE\u5F55\u3002Runtime 0.10.0 \u542B 10 \u9879\uFF0CCLI \u4E0D\u590D\u5236\u679A\u4E3E\u3002\u628A\u5B9E\u9645\u9700\u8981\u7684 \`id\` \u548C\u7528\u9014 \`purpose\` \u5199\u5165 \`buddy.agent.json.datasets\`\uFF0C\u540C\u4E00 ID \u53EA\u5199\u4E00\u6B21\uFF0C\u7528\u9014\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u6700\u591A 200 \u5B57\uFF1B\u65E0\u9700\u6C42\u65F6\u4FDD\u7559\u7A7A\u6570\u7EC4\u3002DEV \u548C\u58F0\u660E\u975E\u7A7A\u65F6\u7684 push \u9884\u68C0\u7528\u8FD9\u4EFD\u5DE5\u7A0B\u4F9D\u8D56\u6821\u9A8C ID\uFF1B\u9700\u8981\u5148\u5B89\u88C5\u4E0E package.json \u51C6\u786E\u7248\u672C\u4E00\u81F4\u7684 Runtime\u3002\u6E05\u5355\u58F0\u660E\u6570\u91CF\u4E0D\u7B49\u4E8E\u5355\u6B21\u67E5\u8BE2\u4E0A\u9650\uFF0C\u5F53\u524D\u4E00\u6B21\u67E5\u8BE2\u6700\u591A 8 \u9879\u3002
|
|
1464
|
+
|
|
1465
|
+
\`start.mjs\` \u5C06\u58F0\u660E\u4EA4\u7ED9 \`StartOptions.dataRequirements\`\u3002Runtime \u5728\u5B9E\u9645\u6570\u636E\u67E5\u8BE2\u3001HealthKit \u6388\u6743\u548C\u4E3B\u52A8\u670D\u52A1\u8BF7\u6C42\u4E2D\u643A\u5E26\u5B8C\u6574\u58F0\u660E\uFF0C\u7531 Server \u6821\u9A8C\u8303\u56F4\u4E0E\u7528\u6237\u540C\u610F\uFF1B\u586B\u5199\u914D\u7F6E\u4E0D\u8868\u793A\u5DF2\u7ECF\u6388\u6743\u3002\u670D\u52A1\u7AEF\u4ECE\u4E0A\u4F20\u5305\u89E3\u6790\u5E76\u7ED1\u5B9A\u53D1\u5E03\u7248\u672C\u4ECD\u5F85\u5B9E\u73B0\u3002
|
|
1466
|
+
|
|
1467
|
+
\u6A21\u578B\u9700\u8981\u67E5\u6570\u636E\u65F6\uFF0C\u5728 \`src/buddy-options.mjs\` \u7684 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.data_access_query\`\uFF1B\u9700\u8981\u5411\u7528\u6237\u7533\u8BF7\u65F6\u52A0\u5165 \`conversation.tools.data_access_request\`\uFF0C\u4F4D\u7F6E\u3001\u65E5\u5386\u7684\u4E00\u6B21\u6027\u7ED3\u679C\u901A\u8FC7 \`data_access_read_result\` \u8BFB\u53D6\uFF0CHealthKit \u5219\u91CD\u65B0\u67E5\u8BE2 Dataset\u3002\u4E1A\u52A1\u4EE3\u7801\u5BF9\u5E94\u4F7F\u7528 \`conversation.dataAccess.query/request/readResult\`\u3002\u5DE5\u7A0B\u6A21\u677F\u5DF2\u9501\u5B9A npm Runtime 0.10.0\u3002
|
|
1468
|
+
|
|
1469
|
+
\u9700\u8981\u7528\u6237\u4ECE\u51E0\u4E2A\u9009\u9879\u4E2D\u9009\u62E9\u65F6\uFF0C\u5728 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.ask_question\` \u5E76\u5728\u4EBA\u8BBE\u4E2D\u8BF4\u660E\u9002\u7528\u573A\u666F\u3002\u53D1\u9898\u5361\u6216\u6388\u6743\u5361\u540E\uFF0C\u672C\u8F6E\u7ED3\u675F\u5E76\u7B49\u5F85\u7528\u6237\uFF1BPreview \u80FD\u663E\u793A\u9009\u9879\u6587\u5B57\u5E76\u53D1\u9001\u7528\u6237\u7684\u6587\u5B57\u56DE\u7B54\uFF0C\u7CFB\u7EDF\u6570\u636E\u6388\u6743\u9700\u8981\u5728 App \u4E2D\u5B8C\u6210\u3002\u6A21\u677F\u7684 \`initialState.tools\` \u9ED8\u8BA4\u4E3A\u7A7A\u6570\u7EC4\uFF0C\u53EA\u52A0\u5165\u4E1A\u52A1\u5B9E\u9645\u9700\u8981\u7684\u5DE5\u5177\uFF1B\u8D44\u6599\u76EE\u5F55\u901A\u8FC7 \`resource_list\` \u53D1\u73B0\uFF0C\u4E0D\u81EA\u52A8\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u3002\u65B0\u4F1A\u8BDD\u7684\u95EE\u5019\u7531\u6A21\u578B\u6309\u4EBA\u8BBE\u751F\u6210\uFF0C\u4E0D\u53E6\u8BBE\u9759\u6001\u6B22\u8FCE\u8BED\u914D\u7F6E\u3002
|
|
1470
|
+
|
|
1471
|
+
\u666E\u901A\u56DE\u5408\u7684\u9519\u8BEF\u515C\u5E95\u53EF\u7528 \`BuddyOptions.errorReply\`\u3002\u4E3B\u52A8\u56DE\u5408\u7528 \`conversation.proactive.isRunning()\` \u5224\u65AD\uFF0C\u5728 \`beforeToolCall\` \u4E2D\u9650\u5236\u4E1A\u52A1\u526F\u4F5C\u7528\uFF1B\u94A9\u5B50\u629B\u9519\u65F6\u5DE5\u5177\u4E0D\u4F1A\u6267\u884C\u3002\u5E73\u53F0\u80FD\u529B\u7684\u5199\u5165\u9650\u5236\u7531 Runtime \u6267\u884C\u3002\u7C7B\u578B\u53EA\u4ECE \`@my-life-buddies/buddy-runtime\` \u5BFC\u5165\uFF0C\u53C2\u6570 Schema \u7684 Type \u4ECE typebox \u5BFC\u5165\u3002
|
|
835
1472
|
|
|
836
1473
|
### \u53EA\u6709\u5173\u952E\u7F3A\u53E3\u624D\u8865\u95EE
|
|
837
1474
|
|
|
@@ -841,6 +1478,18 @@ Widget \u5DE5\u7A0B\u7EA6\u5B9A\uFF1A\u4E00\u7EA7\u76EE\u5F55\u540D\u5C31\u662F\
|
|
|
841
1478
|
|
|
842
1479
|
\u9047\u5230\u771F\u6B63\u7684\u963B\u585E\u5C31\u8BF4\u660E\u5177\u4F53\u7F3A\u53E3\u5E76\u7B49\u5F85\u7B54\u590D\uFF1B\u4E0D\u53D7\u5F71\u54CD\u7684\u5DF2\u6388\u6743\u5DE5\u4F5C\u53EF\u4EE5\u7EE7\u7EED\u3002\u65E0\u6CD5\u5B9E\u73B0\u7684\u8981\u6C42\u5982\u5B9E\u8BF4\u660E\uFF0C\u4E0D\u6697\u4E2D\u5220\u53BB\uFF0C\u4E5F\u4E0D\u4F2A\u9020\u5F00\u53D1\u8005\u540C\u610F\u964D\u7EA7\u3002\u7528\u6237\u53EA\u8981\u8BBE\u8BA1\u3001\u521D\u59CB\u5316\u6216\u5C40\u90E8\u4FEE\u6539\u65F6\u9075\u5B88\u8BE5\u8303\u56F4\uFF0C\u4E0D\u56E0\u624B\u518C\u5B58\u5728\u5C31\u81EA\u52A8\u6269\u5C55\u4EFB\u52A1\u3002
|
|
843
1480
|
|
|
1481
|
+
### \u5C0F\u6302\u4EF6\u8BBE\u8BA1\u843D\u5730\u68C0\u67E5
|
|
1482
|
+
|
|
1483
|
+
\u6709 Creator \u5C0F\u6302\u4EF6\u8BBE\u8BA1\u65F6\uFF0C\u5728\u5F00\u53D1\u5F00\u59CB\u524D\u6267\u884C \`buddy-cli widgets sync\`\u3002\u5B83\u4ECE\u5F53\u524D\u6709\u6548\u9A8C\u6536\u4EA4\u4ED8\u751F\u6210 \`widget-design.lock.json\` \u548C \`widget-implementation.json\`\uFF1B\u9010\u9879\u4FDD\u7559\u72EC\u7ACB\u7C7B\u578B ID\uFF0C\u4E0D\u5F97\u628A\u591A\u7C7B\u8BBE\u8BA1\u9759\u9ED8\u5408\u5E76\u6210\u4E00\u4E2A\u901A\u7528\u5DE5\u4F5C\u53F0\u3002\u540C\u6B65\u4E0D\u662F\u5B9E\u73B0\u5B8C\u6210\u3002
|
|
1484
|
+
|
|
1485
|
+
\u6309\u6620\u5C04\u5B9E\u73B0\u6BCF\u7C7B\u7684\u9875\u9762\u548C Schema\uFF0C\u586B\u5199\u5404\u8BBE\u8BA1\u5B57\u6BB5\u7684\u6570\u636E\u6A21\u5757\u7ED1\u5B9A\uFF0C\u4EE5\u53CA create/update/present \u5BF9\u5E94\u7684\u771F\u5B9E src/ \u4EE3\u7801\u8DEF\u5F84\u3002\u6CBF\u7528\u9A8C\u6536\u539F\u578B\u5E03\u5C40\u548C\u9605\u8BFB\u4EA4\u4E92\uFF0C\u793A\u4F8B\u6570\u636E\u53EA\u7528\u4E8E\u9694\u79BB\u7684\u9884\u89C8\u6838\u5BF9\uFF0C\u4E0D\u5199\u5165\u771F\u5B9E\u7528\u6237\u4F1A\u8BDD\u3002
|
|
1486
|
+
|
|
1487
|
+
\u6267\u884C \`buddy-cli widgets check --json\`\uFF0C\u5148\u4FEE\u590D\u7F3A\u5931\u7C7B\u578B\u3001\u5B57\u6BB5\u3001\u4EE3\u7801\u8DEF\u5F84\u6216\u8FC7\u671F\u539F\u578B\u3002DEV \u5141\u8BB8\u7EE7\u7EED\u8C03\u8BD5\uFF0C\u9884\u89C8\u9875\u9762\u4F1A\u5355\u72EC\u5C55\u793A\u672A\u5B8C\u6210\u9879\uFF1B\u4E0A\u4F20\u6210\u529F\u3001DEV \u5728\u7EBF\u5747\u4E0D\u80FD\u4EE3\u66FF\u8FD9\u4EFD\u68C0\u67E5\u3002
|
|
1488
|
+
|
|
1489
|
+
\u5B8C\u6210\u5B9E\u9645\u9875\u9762\u4E0E\u9A8C\u6536\u539F\u578B\u7684\u9996\u6B21/\u8865\u5145/\u66F4\u65B0\u4E09\u6001\u5BF9\u7167\uFF0C\u8BB0\u5F55 visual\u3001interactions\u3001data-update\u3001trigger\u3001read-only \u4E94\u9879\u5B9E\u9645\u89C2\u5BDF\uFF1B\u4F7F\u7528\u9694\u79BB\u6D4B\u8BD5\u6570\u636E\u9A8C\u8BC1\u672A\u89E6\u53D1\u65F6\u65E0\u5165\u53E3\u3001\u521B\u5EFA\u540E\u663E\u793A\u3001\u66F4\u65B0\u6CBF\u7528\u5B9E\u4F8B\u4E0E\u6458\u8981\u4E00\u81F4\u3002\u4FDD\u7559\u622A\u56FE\u6216\u53EF\u590D\u6838\u6D4B\u8BD5\u8F93\u51FA\u5230\u9879\u76EE .buddy/ \u4E0B\u3002\u8BB0\u5F55\u91CC\u586B\u5199 check \u8F93\u51FA\u7684\u5F53\u524D implementationDigest\u3001\u65E0\u51ED\u636E previewUrl\u3001observedAt\u3001reviewer\u3001states\uFF08\u5404\u6001 notes/artifact\uFF09\u548C checks\uFF08\u4E94\u9879\u8BF4\u660E\uFF09\uFF0C\u7136\u540E\u6267\u884C \`buddy-cli widgets verify --id <\u8BBE\u8BA1ID> --evidence <\u9879\u76EE\u5185\u8BB0\u5F55.json>\`\u3002\u547D\u4EE4\u8BA1\u7B97\u8BC1\u636E\u6458\u8981\u5E76\u7ED1\u5B9A\u5F53\u524D\u4EE3\u7801\uFF1B\u5B83\u4FDD\u5B58\u6838\u5BF9\u8005\u7684\u89C2\u5BDF\uFF0C\u4E0D\u81EA\u52A8\u8BC1\u660E\u89C6\u89C9\u76F8\u540C\uFF0C\u4E5F\u4E0D\u65B0\u589E\u4E00\u8F6E\u7528\u6237\u5BA1\u6279\u3002
|
|
1490
|
+
|
|
1491
|
+
\u6240\u6709\u7C7B\u578B\u7ED3\u6784\u548C\u6838\u5BF9\u8BB0\u5F55\u9F50\u5168\u540E\u624D\u79F0\u201C\u5C0F\u6302\u4EF6\u5F00\u53D1\u6838\u5BF9\u5B8C\u6210\u201D\u3002\u4EE3\u7801\u3001\u9875\u9762\u3001\u4F9D\u8D56\u3001\u6570\u636E\u7ED1\u5B9A\u6216\u8BBE\u8BA1\u7248\u672C\u53D8\u5316\u4F1A\u8BA9\u65E7\u8BB0\u5F55\u5931\u6548\uFF0C\u5E94\u9488\u5BF9\u53D8\u5316\u91CD\u65B0\u6838\u5BF9\u3002\u6CA1\u6709\u5B8C\u6210\u5C31\u660E\u786E\u5217\u51FA\u7F3A\u9879\u3002push \u4F1A\u5728\u4E0A\u4F20\u524D\u62E6\u622A\u4E0D\u5B8C\u6574\u4EA4\u4ED8\uFF0C\u4F46\u4E0D\u4F1A\u66FF\u5F00\u53D1\u8005\u53D1\u8D77\u63D0\u5BA1\u3002\u76F4\u63A5\u5F00\u53D1\u4E14\u65E0 Creator \u8BBE\u8BA1\u7684\u9879\u76EE\u6CBF\u7528\u539F\u6D41\u7A0B\uFF0C\u68C0\u67E5\u7ED3\u679C\u660E\u786E\u4E3A\u201C\u672A\u914D\u7F6E\u8BBE\u8BA1\u6838\u5BF9\u201D\uFF0C\u4E0D\u5192\u5145\u901A\u8FC7\u3002
|
|
1492
|
+
|
|
844
1493
|
### \u6838\u5BF9\u5B9E\u73B0\uFF0C\u518D\u4EA4\u7ED9\u5F00\u53D1\u8005\u4F53\u9A8C
|
|
845
1494
|
|
|
846
1495
|
\u5B8C\u6210\u5F00\u53D1\u540E\u5411\u5F00\u53D1\u8005\u7B80\u8981\u8BF4\u660E\uFF1A\u9700\u6C42\u6240\u5728\u7AE0\u8282\u6216\u6765\u6E90\u3001\u5BF9\u5E94\u5B9E\u73B0\u548C\u68C0\u67E5\u7ED3\u679C\u3001\u4ECD\u5B58\u5728\u7684\u7F3A\u53E3\uFF1B\u5185\u5BB9\u7B80\u5355\u65F6\u7528\u77ED\u6E05\u5355\u5373\u53EF\uFF0C\u4E0D\u8981\u6C42\u989D\u5916\u751F\u6210 README\u3001\u786E\u8BA4\u8868\u6216\u72B6\u6001\u6587\u4EF6\u3002\u533A\u5206\u5DF2\u5B9E\u73B0\u3001\u5F85\u5B9E\u73B0\u548C\u7F3A\u5C11\u6761\u4EF6\uFF0C\u4E0D\u628A\u8BA1\u5212\u5199\u6210\u5B8C\u6210\uFF1B\u4E0D\u8981\u590D\u5236\u79C1\u4EBA\u804A\u5929\u3001\u5065\u5EB7\u539F\u59CB\u8BB0\u5F55\u6216\u51ED\u636E\u3002\u6062\u590D\u5F00\u53D1\u65F6\u6309\u624B\u518C\u4E0E\u4EE3\u7801\u6838\u5BF9\uFF0C\u4E0D\u628A\u65E7\u8BF4\u660E\u5F53\u4F5C\u5F53\u524D\u5B9E\u73B0\u7684\u8BC1\u636E\u3002
|
|
@@ -858,6 +1507,8 @@ var REQUIRED = [
|
|
|
858
1507
|
"THIRD_PARTY_NOTICES.md",
|
|
859
1508
|
"version.json",
|
|
860
1509
|
"references/host-guide.md",
|
|
1510
|
+
"references/widgets.md",
|
|
1511
|
+
"references/widgets-protocol.md",
|
|
861
1512
|
"references/preview-panel.md",
|
|
862
1513
|
"references/catalog.json",
|
|
863
1514
|
"references/interview.md",
|
|
@@ -866,6 +1517,20 @@ var REQUIRED = [
|
|
|
866
1517
|
"scripts/buddy_client.py",
|
|
867
1518
|
"scripts/buddy_core.py",
|
|
868
1519
|
"scripts/interview.py",
|
|
1520
|
+
"scripts/widgets.py",
|
|
1521
|
+
"scripts/widget_render.py",
|
|
1522
|
+
"scripts/widget_render_v1.py",
|
|
1523
|
+
"scripts/widget_render_v2.py",
|
|
1524
|
+
"scripts/widget_render_v3.py",
|
|
1525
|
+
"scripts/widget_render_v4.py",
|
|
1526
|
+
"assets/widget-v2/style.css",
|
|
1527
|
+
"assets/widget-v2/app.js",
|
|
1528
|
+
"assets/widget-v3/style.css",
|
|
1529
|
+
"assets/widget-v3/app.js",
|
|
1530
|
+
"assets/widget-v4/style.css",
|
|
1531
|
+
"assets/widget-v4/app.js",
|
|
1532
|
+
"assets/preview/widgets.js",
|
|
1533
|
+
"assets/preview/widgets.css",
|
|
869
1534
|
"scripts/sources.py",
|
|
870
1535
|
"scripts/preview.py",
|
|
871
1536
|
"scripts/preview_panel.py",
|
|
@@ -875,16 +1540,16 @@ var REQUIRED = [
|
|
|
875
1540
|
var CreatorPreparationError = class extends Error {
|
|
876
1541
|
};
|
|
877
1542
|
async function state(path) {
|
|
878
|
-
return
|
|
1543
|
+
return lstat6(path).catch((cause) => {
|
|
879
1544
|
if (cause.code === "ENOENT") return void 0;
|
|
880
1545
|
throw cause;
|
|
881
1546
|
});
|
|
882
1547
|
}
|
|
883
1548
|
async function safeDirectory(path) {
|
|
884
|
-
await
|
|
1549
|
+
await mkdir4(path, { mode: 448 }).catch((cause) => {
|
|
885
1550
|
if (cause.code !== "EEXIST") throw cause;
|
|
886
1551
|
});
|
|
887
|
-
if (!(await
|
|
1552
|
+
if (!(await lstat6(path)).isDirectory()) {
|
|
888
1553
|
throw new CreatorPreparationError(`\u521B\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u666E\u901A\u76EE\u5F55\uFF0C\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5\uFF1A${path}`);
|
|
889
1554
|
}
|
|
890
1555
|
}
|
|
@@ -894,11 +1559,11 @@ async function safeRead(path) {
|
|
|
894
1559
|
if (!entry.isFile() || entry.size > MAX_LOCAL_FILE_BYTES) {
|
|
895
1560
|
throw new CreatorPreparationError(`\u521B\u4F5C\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6\u6216\u8D85\u51FA\u5927\u5C0F\u9650\u5236\uFF1A${path}`);
|
|
896
1561
|
}
|
|
897
|
-
return
|
|
1562
|
+
return readFile5(path);
|
|
898
1563
|
}
|
|
899
1564
|
async function writeOnce(path, bytes) {
|
|
900
1565
|
const temporary = `${path}.${randomUUID2()}.tmp`;
|
|
901
|
-
await
|
|
1566
|
+
await writeFile2(temporary, bytes, { flag: "wx", mode: 384 });
|
|
902
1567
|
try {
|
|
903
1568
|
await link2(temporary, path).catch((cause) => {
|
|
904
1569
|
if (cause.code !== "EEXIST") throw cause;
|
|
@@ -909,17 +1574,17 @@ async function writeOnce(path, bytes) {
|
|
|
909
1574
|
}
|
|
910
1575
|
async function bundledSkill(root) {
|
|
911
1576
|
for (const path of REQUIRED) {
|
|
912
|
-
if (!(await state(
|
|
1577
|
+
if (!(await state(join5(root, path)))?.isFile()) {
|
|
913
1578
|
throw new CreatorPreparationError(`CLI \u5185\u7F6E Creator Skill \u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5 CLI\uFF08\u7F3A\u5C11 ${path}\uFF09\u3002\u4E0D\u4F1A\u5C1D\u8BD5\u5728\u7EBF\u4E0B\u8F7D\u6216\u8986\u76D6\u9879\u76EE\u8D44\u6599\u3002`);
|
|
914
1579
|
}
|
|
915
1580
|
}
|
|
916
1581
|
return root;
|
|
917
1582
|
}
|
|
918
1583
|
function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
919
|
-
const contextPath =
|
|
1584
|
+
const contextPath = join5(context.projectRoot, ".buddy", "creator", "initial-context.json");
|
|
920
1585
|
const openArgs = [
|
|
921
1586
|
"-B",
|
|
922
|
-
|
|
1587
|
+
join5(skillRoot, "scripts/buddy.py"),
|
|
923
1588
|
"open",
|
|
924
1589
|
"--workspace",
|
|
925
1590
|
workspacePath,
|
|
@@ -949,7 +1614,7 @@ function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
|
949
1614
|
|
|
950
1615
|
## \u73B0\u5728\u7531 Coding Agent \u6267\u884C
|
|
951
1616
|
|
|
952
|
-
1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(
|
|
1617
|
+
1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(join5(skillRoot, "SKILL.md"))} \u53CA\u5176\u6307\u5B9A\u7684 references\u3002\u4FDD\u7559\u539F\u6709\u56DB\u9636\u6BB5\u89C4\u5219\uFF0C\u7531\u5F53\u524D\u4E3B\u5BF9\u8BDD\u5B8C\u6210\u91C7\u8BBF\uFF1B\u65E0\u9700\u53E6\u8D77 Sub-agent \u6216\u8FD0\u884C\u91C7\u8BBF\u6A21\u578B\u670D\u52A1\u3002
|
|
953
1618
|
2. \u67E5\u627E\u5BBF\u4E3B\u6216\u7CFB\u7EDF\u5B9E\u9645\u53EF\u7528\u7684 Python 3.9+\uFF0C\u68C0\u67E5\u7248\u672C\uFF0C\u4FDD\u5B58\u5176\u7EDD\u5BF9\u8DEF\u5F84\u3002\u4E0D\u8981\u5199\u6B7B\u672C\u673A\u8DEF\u5F84\uFF0C\u4E0D\u5B89\u88C5 pip/npm \u4F9D\u8D56\u3002\u6CA1\u6709 Python \u65F6\u62A5\u544A\u7F3A\u53E3\uFF0C\u4FDD\u7559\u672C\u5730\u9879\u76EE\u3002\u6240\u6709 Python \u8C03\u7528\u5747\u52A0 -B\uFF0C\u5E76\u8BBE\u7F6E\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF PYTHONDONTWRITEBYTECODE=1\uFF08\u7531\u9884\u89C8\u5B50\u8FDB\u7A0B\u7EE7\u627F\uFF09\uFF0C\u907F\u514D\u5411 CLI \u5B89\u88C5\u76EE\u5F55\u5199\u5165\u5B57\u8282\u7801\u7F13\u5B58\u3002
|
|
954
1619
|
3. \u4F7F\u7528\u5DE5\u5177\u4F20\u9012\u72EC\u7ACB\u53C2\u6570\uFF0C\u4E0D\u62FC\u63A5 shell \u5B57\u7B26\u4E32\u3002\u5DE5\u4F5C\u76EE\u5F55\u4E3A ${JSON.stringify(context.projectRoot)}\uFF1B\u4EE5\u5B9E\u9645 Python \u8DEF\u5F84\u4E3A executable\uFF0C\u9996\u6B21\u6253\u5F00\u7684 argv \u5982\u4E0B\uFF1A
|
|
955
1620
|
|
|
@@ -959,17 +1624,17 @@ ${JSON.stringify(openArgs, null, 2)}
|
|
|
959
1624
|
|
|
960
1625
|
\u91CD\u8BD5\u6CBF\u7528 creation-key \u548C workspace\u3002\u5DF2\u521B\u5EFA\u4F5C\u54C1\u540E\u6309 Skill \u534F\u8BAE open --workspace \u63A5\u7EED\uFF1B\u52FF\u53E6\u5F00\u9ED8\u8BA4\u76EE\u5F55\u3001\u52FF\u6539\u5199 state.json\u3002\u9996\u6B21\u521B\u4F5C\u6210\u529F\u540E\u7531 Coding Agent \u6253\u5F00\u771F\u5B9E preview.url\uFF1B\u63A5\u7EED\u5DF2\u6709\u4F5C\u54C1\u65F6\u6309 Skill \u7684\u9762\u677F\u68C0\u67E5\u89C4\u5219\u5904\u7406\uFF0C\u786E\u8BA4\u5173\u95ED\u6700\u591A\u8BE2\u95EE\u4E00\u6B21\uFF0C\u672A\u56DE\u5E94\u6216\u62D2\u7EDD\u540E\u4E0D\u518D\u95EE\uFF0C\u7528\u6237\u660E\u786E\u8981\u6C42\u91CD\u5F00\u65F6\u76F4\u63A5\u6062\u590D\u539F\u9762\u677F\u3002\u6309\u8FD4\u56DE\u7684 directive \u7EE7\u7EED\u3002prepare_opening \u8868\u793A\u9700\u8981\u627F\u63A5\u5DF2\u77E5\u4E3B\u9898\uFF0C\u6309\u5BBF\u4E3B\u534F\u8BAE\u4FDD\u5B58\u7B80\u77ED\u5F00\u573A\u540E\u518D\u5C55\u793A\uFF1B\u5DF2\u6709\u771F\u5B9E\u56DE\u7B54\u5219\u5148\u4FDD\u5B58\u539F\u8BDD\u3001\u8865\u95EE\u5C1A\u672A\u660E\u786E\u7684\u90E8\u5206\u3002\u4E0D\u8981\u5957\u7528\u65E0\u80CC\u666F\u7684\u901A\u7528\u4ECB\u7ECD\u3002\u51C6\u5907\u597D\u672C\u8BF4\u660E\u4E0D\u7B49\u4E8E\u9884\u89C8\u5DF2\u7ECF\u542F\u52A8\u6216\u91C7\u8BBF\u5B8C\u6210\u3002
|
|
961
1626
|
4. \u6BCF\u8F6E\u4F9D\u7167 Skill \u4FDD\u5B58\u771F\u5B9E\u7528\u6237\u8F93\u5165\u3001\u4EA7\u7269\u7248\u672C\u4E0E\u660E\u786E\u786E\u8BA4\uFF1B\u4E0D\u628A\u5E73\u53F0\u57FA\u7840\u8D44\u6599\u5F53\u4F5C\u56DB\u518C\u5747\u5DF2\u786E\u8BA4\u3002\u7528\u6237\u539F\u59CB\u8D44\u6599\u3001\u4F5C\u54C1\u548C\u624B\u518C\u5168\u90E8\u4FDD\u7559\u5728\u672C\u9879\u76EE .buddy/creator/ \u5185\uFF0Cpush \u4E0D\u4E0A\u4F20\u8BE5\u76EE\u5F55\u3002
|
|
962
|
-
5. \u56DB\u518C\u786E\u8BA4\u540E\u8BFB\u53D6\u5B9E\u9645
|
|
1627
|
+
5. \u65B0\u9879\u76EE\u5B8C\u6210\u56DB\u518C\u540E\u7EE7\u7EED\u5C0F\u6302\u4EF6\u6E05\u5355\u3001\u539F\u578B\u548C\u8BBE\u8BA1\u9A8C\u6536\uFF0C\u5728 Booklet \u9875\u9762\u540C\u6B65\u5C55\u793A\uFF1B\u65E7\u9879\u76EE\u6309 Skill \u7248\u672C\u89C4\u5219\u63A5\u7EED\u3002\u56DB\u518C\u5B8C\u6210\u53EA\u89E6\u53D1\u5C0F\u6302\u4EF6\u8BBE\u8BA1\uFF0C\u4E0D\u80FD\u63D0\u524D\u5F00\u53D1\u3002\u5168\u90E8\u5FC5\u9700\u8BBE\u8BA1\u786E\u8BA4\u540E\u8BFB\u53D6\u5B9E\u9645\u8FD4\u56DE\u503C\uFF1B\u53EA\u6709 directive=creator_complete\u3001completion.status=ready\u3001\u7248\u672C\u4ECD\u6709\u6548\u4E14 manualPath \u6587\u4EF6\u786E\u5B9E\u5B58\u5728\u65F6\uFF0C\u624D\u8FDB\u5165\u4E0B\u65B9\u5F00\u53D1\u6D41\u7A0B\u3002\u4E0D\u8981\u4F2A\u9020\u624B\u518C\u6216\u81EA\u52A8\u4E0A\u4F20\u3001\u63D0\u5BA1\u3002
|
|
963
1628
|
|
|
964
1629
|
## Creator \u5B8C\u6210\u65F6\u4EA4\u8FD8\u7ED9 CLI \u6D41\u7A0B
|
|
965
1630
|
|
|
966
|
-
\u672C\u8BF4\u660E\u662F\u5B8C\u6574\u521B\u5EFA\u6D41\u7A0B\u7684\u5165\u53E3\
|
|
1631
|
+
\u672C\u8BF4\u660E\u662F\u5B8C\u6574\u521B\u5EFA\u6D41\u7A0B\u7684\u5165\u53E3\uFF1A\u56DB\u518C\u786E\u8BA4 \u2192 \u5C0F\u6302\u4EF6\u6E05\u5355\u786E\u8BA4 \u2192 \u539F\u578B\u786E\u8BA4 \u2192 \u8BBE\u8BA1\u9A8C\u6536 \u2192 \u4EA4\u4ED8\u6821\u9A8C \u2192 \u81EA\u52A8\u8FDB\u5165\u5F00\u53D1\u3002\u6536\u5230 directive=creator_complete \u4E14 completion.status=ready \u540E\uFF0C\u7ED3\u675F Creator Skill\uFF1B\u5F53\u524D Coding Agent \u5728\u540C\u4E00\u8F6E\u91CD\u65B0\u8BFB\u53D6\u672C\u8BF4\u660E\u3001\u5B8C\u6574\u624B\u518C\u3001\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u4E0E\u9A8C\u6536\u4EA7\u7269\u53CA\u73B0\u6709\u5DE5\u7A0B\uFF0C\u6267\u884C\u4E0B\u9762\u7684\u5F00\u53D1\u6D41\u7A0B\u3002\u5C55\u793A\u6210\u679C\u53EA\u5B8C\u6210\u4E86\u4E00\u4E2A\u9636\u6BB5\uFF0C\u4E0D\u80FD\u4EC5\u53D1\u6210\u679C\u94FE\u63A5\u5C31\u7ED3\u675F\u6574\u6761\u521B\u5EFA\u4EFB\u52A1\uFF0C\u4E0D\u518D\u8BE2\u95EE\u201C\u662F\u5426\u5F00\u59CB\u5F00\u53D1\u201D\uFF0C\u4E5F\u4E0D\u8981\u7B49\u5F85\u5F00\u53D1\u8005\u518D\u95EE\u201C\u4E0B\u4E00\u6B65\u662F\u4EC0\u4E48\u201D\u3002
|
|
967
1632
|
|
|
968
1633
|
\u4E0B\u4E00\u6B65\u7684\u6280\u672F\u9009\u62E9\u3001\u5DE5\u7A0B\u7F16\u5199\u548C\u8C03\u8BD5\u7531\u5F53\u524D Coding Agent \u6309 CLI \u89C4\u5219\u5904\u7406\uFF0C\u4E0D\u518D\u4F5C\u4E3A Creator \u7684 explanation/turn_begin/turn_finish \u5199\u56DE\u91C7\u8BBF\u8BB0\u5F55\uFF1B\u201C\u4E0D\u5728 delivery \u540E\u52A0\u91C7\u8BBF\u95EE\u9898\u201D\u7684\u9650\u5236\u4E0D\u963B\u6B62\u9000\u51FA Skill \u540E\u8FDB\u5165\u5DE5\u7A0B\u8BA8\u8BBA\u3002\u53EA\u6709\u7528\u6237\u8981\u4FEE\u8BA2 Booklet \u65F6\u624D\u91CD\u65B0\u8FDB\u5165 Creator\u3002
|
|
969
1634
|
|
|
970
1635
|
directive=finalize_required \u8868\u793A\u6210\u679C\u4E0D\u53EF\u7528\uFF0C\u5148\u6309 Skill \u534F\u8BAE\u4FEE\u590D\u4EA4\u4ED8\uFF1B\u672A\u5B8C\u6210\u3001\u5DF2\u6682\u505C\u6216\u4ECD\u6709\u672A\u5904\u7406\u56DE\u5408\u65F6\u7EE7\u7EED\u539F\u9636\u6BB5\uFF0C\u4E0D\u80FD\u8DF3\u5230\u5F00\u53D1\u3002\u65E7\u9879\u76EE\u6062\u590D\u5148 open/status \u8BFB\u53D6\u5B9E\u9645\u72B6\u6001\uFF1B\u5DF2\u5B8C\u6210\u7684\u9879\u76EE\u76F4\u63A5\u63A5\u56DE\u5F00\u53D1\uFF0C\u4E0D\u91CD\u95EE\u91C7\u8BBF\u3001\u4E0D\u91CD\u65B0\u521B\u5EFA\u642D\u5B50\u3002\u7528\u6237\u660E\u786E\u53EA\u505A\u8BBE\u8BA1\u6216\u8981\u6C42\u6682\u505C\u65F6\uFF0C\u9075\u5B88\u8BE5\u8303\u56F4\uFF0C\u4E0D\u81EA\u52A8\u5F00\u59CB\u4EE3\u7801\u5F00\u53D1\u3002
|
|
971
1636
|
|
|
972
|
-
## \
|
|
1637
|
+
## \u5168\u90E8\u8BBE\u8BA1\u9A8C\u6536\u4E0E\u4EA4\u4ED8\u6821\u9A8C\u901A\u8FC7\u540E\u81EA\u52A8\u8FDB\u5165\u5F00\u53D1
|
|
973
1638
|
|
|
974
1639
|
${IMPLEMENTATION_GUIDANCE}
|
|
975
1640
|
${IMPLEMENTATION_GUIDE}
|
|
@@ -980,17 +1645,17 @@ ${IMPLEMENTATION_GUIDE}
|
|
|
980
1645
|
${JSON.stringify(directArgs, null, 2)}
|
|
981
1646
|
\`\`\`
|
|
982
1647
|
|
|
983
|
-
\u8BE5\u547D\u4EE4\u590D\u7528 buddy.agent.json \u4E2D\u7684 buddyId\uFF0C\u5E76\u4ECE\u5E73\u53F0\u8BFB\u53D6\u6700\u65B0 Meta\u3001\u91CD\u65B0\u9A8C\u8BC1\u5F52\u5C5E\uFF0C\u4E0D\u4F1A\u53E6\u5EFA\u642D\u5B50\u3002\u5DE5\u7A0B\u7EDF\u4E00\u4F7F\u7528\u5B98\u65B9 Runtime\uFF1B\
|
|
1648
|
+
\u8BE5\u547D\u4EE4\u590D\u7528 buddy.agent.json \u4E2D\u7684 buddyId\uFF0C\u5E76\u4ECE\u5E73\u53F0\u8BFB\u53D6\u6700\u65B0 Meta\u3001\u91CD\u65B0\u9A8C\u8BC1\u5F52\u5C5E\uFF0C\u4E0D\u4F1A\u53E6\u5EFA\u642D\u5B50\u3002\u5DE5\u7A0B\u7EDF\u4E00\u4F7F\u7528\u5B98\u65B9 Runtime\uFF1B\u5168\u90E8\u8BBE\u8BA1\u9A8C\u6536\u53CA\u4EA4\u4ED8\u6821\u9A8C\u5B8C\u6210\u524D\u4E0D\u751F\u6210\u4E1A\u52A1\u4EE3\u7801\uFF0C\u4E5F\u4E0D\u8981\u628A\u9700\u6C42\u624B\u518C\u8BF4\u6210\u5DF2\u5B9E\u73B0\u4EE3\u7801\u3002
|
|
984
1649
|
\u5DF2\u6709\u5B8C\u6574 Agent \u5DE5\u7A0B\u65F6\u4E0D\u91CD\u590D init\uFF0C\u76F4\u63A5\u6309\u624B\u518C\u7EE7\u7EED\u5B9E\u73B0\u3002\u6821\u9A8C\u9879\u76EE\u8EAB\u4EFD\u4E0E Booklet \u6765\u6E90\uFF0C\u4E0D\u7528\u5176\u4ED6\u642D\u5B50\u7684\u624B\u518C\u6216\u76EE\u5F55\u3002
|
|
985
1650
|
${DEVELOPMENT_HANDOFF}
|
|
986
1651
|
`;
|
|
987
1652
|
}
|
|
988
1653
|
async function prepareCreatorSkill(context, io, options = {}) {
|
|
989
|
-
const root =
|
|
1654
|
+
const root = join5(context.projectRoot, ".buddy", "creator");
|
|
990
1655
|
try {
|
|
991
|
-
await safeDirectory(
|
|
1656
|
+
await safeDirectory(join5(context.projectRoot, ".buddy"));
|
|
992
1657
|
await safeDirectory(root);
|
|
993
|
-
const sessionPath =
|
|
1658
|
+
const sessionPath = join5(root, "session.json");
|
|
994
1659
|
await writeOnce(sessionPath, JSON.stringify({
|
|
995
1660
|
schemaVersion: 1,
|
|
996
1661
|
platformBuddyId: context.buddyId,
|
|
@@ -1002,11 +1667,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1002
1667
|
}
|
|
1003
1668
|
options.signal?.throwIfAborted();
|
|
1004
1669
|
const skillRoot = await bundledSkill(options.bundledSkillRoot ?? BUNDLED_CREATOR_SKILL_ROOT);
|
|
1005
|
-
await safeDirectory(
|
|
1006
|
-
const workspacePath =
|
|
1670
|
+
await safeDirectory(join5(root, "workspaces"));
|
|
1671
|
+
const workspacePath = join5(root, "workspaces", session.creationKey);
|
|
1007
1672
|
const workspace = await state(workspacePath);
|
|
1008
1673
|
if (workspace && !workspace.isDirectory()) throw new CreatorPreparationError("\u5DF2\u6709\u521B\u4F5C\u5DE5\u4F5C\u533A\u4E0D\u662F\u666E\u901A\u76EE\u5F55\u3002");
|
|
1009
|
-
const contextPath =
|
|
1674
|
+
const contextPath = join5(root, "initial-context.json");
|
|
1010
1675
|
await writeOnce(contextPath, JSON.stringify({
|
|
1011
1676
|
source: "platform_meta",
|
|
1012
1677
|
name: context.appName,
|
|
@@ -1022,7 +1687,7 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1022
1687
|
} catch {
|
|
1023
1688
|
throw new CreatorPreparationError(`\u5DF2\u6709\u521D\u59CB\u80CC\u666F\u65E0\u6548\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\uFF1A${contextPath}`);
|
|
1024
1689
|
}
|
|
1025
|
-
const handoffPath =
|
|
1690
|
+
const handoffPath = join5(root, "HANDOFF.md");
|
|
1026
1691
|
const handoff = handoffMarkdown({
|
|
1027
1692
|
...context,
|
|
1028
1693
|
appName: initial.name,
|
|
@@ -1033,11 +1698,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1033
1698
|
if ((await safeRead(handoffPath))?.toString("utf8") !== handoff) {
|
|
1034
1699
|
throw new CreatorPreparationError(`\u5DF2\u6709\u63A5\u7EED\u8BF4\u660E\u88AB\u4FEE\u6539\u6216\u9879\u76EE\u8DEF\u5F84\u5DF2\u53D8\u5316\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\u3002\u8BF7\u5907\u4EFD\u79FB\u8D70\u8BE5\u8BF4\u660E\u540E\u91CD\u8BD5\uFF1A${handoffPath}`);
|
|
1035
1700
|
}
|
|
1036
|
-
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${
|
|
1701
|
+
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${join5(skillRoot, "SKILL.md")}`);
|
|
1037
1702
|
io.write(`\u4E0B\u4E00\u6B65\u4EA4\u7ED9 Codex / Claude Code \u7B49 Coding Agent\uFF1A\u8BF7\u8BFB\u53D6 ${JSON.stringify(handoffPath)}\uFF0C\u6309\u5F53\u524D\u9636\u6BB5\u63A5\u7EED\uFF1BCreator \u5B8C\u6210\u540E\u8FD4\u56DE CLI \u5F00\u53D1\u6D41\u7A0B\u3002`);
|
|
1038
1703
|
io.write(DEVELOPMENT_HANDOFF_SUMMARY);
|
|
1039
|
-
io.write("\u672C\u547D\u4EE4\u53EA\u8D1F\u8D23\u8EAB\u4EFD\u767B\u8BB0\u548C Skill \u51C6\u5907\uFF0C\u4E0D\u4EE3\u8868\u91C7\u8BBF\u3001Booklet \u6216 Agent \u5DF2\u5B8C\u6210\u3002\u8BF7\u6309\u5DF2\u6709\u4F5C\u54C1\u8FDB\u5EA6\u63A5\u7EED\uFF0C\
|
|
1040
|
-
return { exitCode: 0, creator: { skillPath:
|
|
1704
|
+
io.write("\u672C\u547D\u4EE4\u53EA\u8D1F\u8D23\u8EAB\u4EFD\u767B\u8BB0\u548C Skill \u51C6\u5907\uFF0C\u4E0D\u4EE3\u8868\u91C7\u8BBF\u3001Booklet \u6216 Agent \u5DF2\u5B8C\u6210\u3002\u8BF7\u6309\u5DF2\u6709\u4F5C\u54C1\u8FDB\u5EA6\u63A5\u7EED\uFF0C\u56DB\u518C\u53CA\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u9A8C\u6536\u3001\u4EA4\u4ED8\u6821\u9A8C\u5B8C\u6210\u540E\u81EA\u52A8\u8FDB\u5165\u5B98\u65B9 Runtime \u5F00\u53D1\uFF1B\u91CD\u590D create \u4F1A\u590D\u7528\u540C\u4E00\u4F5C\u54C1\u3002");
|
|
1705
|
+
return { exitCode: 0, creator: { skillPath: join5(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
|
|
1041
1706
|
} catch (cause) {
|
|
1042
1707
|
const message = cause instanceof CreatorPreparationError ? cause.message : "\u5185\u7F6E\u8D44\u6E90\u8BFB\u53D6\u6216\u672C\u5730\u5199\u5165\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 CLI \u5B89\u88C5\u548C\u76EE\u5F55\u6743\u9650\u3002";
|
|
1043
1708
|
io.writeError(`Creator Skill \u51C6\u5907\u5931\u8D25\uFF1A${message}`);
|
|
@@ -1091,7 +1756,7 @@ function validateBuddyMeta(meta2) {
|
|
|
1091
1756
|
}
|
|
1092
1757
|
|
|
1093
1758
|
// packages/cli-core/src/commands/dev.ts
|
|
1094
|
-
import { resolve as
|
|
1759
|
+
import { resolve as resolve11 } from "node:path";
|
|
1095
1760
|
|
|
1096
1761
|
// packages/cli-core/src/platform-url.ts
|
|
1097
1762
|
var DEFAULT_MLB_TEST_URL = "http://47.116.168.81:8788";
|
|
@@ -1152,7 +1817,7 @@ function safeCredential(value, name) {
|
|
|
1152
1817
|
function endpoint(baseUrl, path) {
|
|
1153
1818
|
return new URL(path.replace(/^\//u, ""), baseUrl);
|
|
1154
1819
|
}
|
|
1155
|
-
function
|
|
1820
|
+
function object2(value) {
|
|
1156
1821
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1157
1822
|
}
|
|
1158
1823
|
async function responseJson(response) {
|
|
@@ -1194,7 +1859,7 @@ async function responseJson(response) {
|
|
|
1194
1859
|
}
|
|
1195
1860
|
}
|
|
1196
1861
|
function safeServerError(value, fallback) {
|
|
1197
|
-
const candidate =
|
|
1862
|
+
const candidate = object2(value)?.error;
|
|
1198
1863
|
if (typeof candidate !== "string" || !candidate.trim()) return fallback;
|
|
1199
1864
|
return candidate.slice(0, 1024).replace(/[\u0000-\u001f\u007f]/gu, " ");
|
|
1200
1865
|
}
|
|
@@ -1275,7 +1940,7 @@ var MlbDevRegistrationClient = class {
|
|
|
1275
1940
|
message: "buddy.agent.json \u4E2D\u7684 buddyId \u65E0\u6548\u3002"
|
|
1276
1941
|
});
|
|
1277
1942
|
}
|
|
1278
|
-
const body =
|
|
1943
|
+
const body = object2(await this.#request(
|
|
1279
1944
|
`/cli/buddies/${encodeURIComponent(request.devAgentId)}`,
|
|
1280
1945
|
{
|
|
1281
1946
|
method: "GET",
|
|
@@ -1296,7 +1961,7 @@ var MlbDevRegistrationClient = class {
|
|
|
1296
1961
|
});
|
|
1297
1962
|
}
|
|
1298
1963
|
async listOnline(options) {
|
|
1299
|
-
const body =
|
|
1964
|
+
const body = object2(await this.#request("/cli/fleet", {
|
|
1300
1965
|
method: "GET",
|
|
1301
1966
|
signal: options.signal
|
|
1302
1967
|
}));
|
|
@@ -1388,36 +2053,24 @@ function buildAgentEnvironment(options) {
|
|
|
1388
2053
|
}
|
|
1389
2054
|
|
|
1390
2055
|
// packages/cli-core/src/dev/orchestrator.ts
|
|
1391
|
-
import { createHash as
|
|
1392
|
-
import { readFile as
|
|
1393
|
-
import { join as
|
|
2056
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2057
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
2058
|
+
import { join as join7, resolve as resolve9 } from "node:path";
|
|
1394
2059
|
|
|
1395
2060
|
// packages/cli-core/src/dev/launch.ts
|
|
1396
2061
|
import { lstatSync, readFileSync } from "node:fs";
|
|
1397
|
-
import { createRequire } from "node:module";
|
|
1398
|
-
import { dirname, resolve as
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
var BUDDY_RUNTIME_PACKAGE = "@my-life-buddies/buddy-runtime";
|
|
1402
|
-
function runtimeDependencyVersion(pkg) {
|
|
1403
|
-
if (typeof pkg !== "object" || pkg === null || !("dependencies" in pkg)) return void 0;
|
|
1404
|
-
const dependencies2 = pkg.dependencies;
|
|
1405
|
-
if (typeof dependencies2 !== "object" || dependencies2 === null || !(BUDDY_RUNTIME_PACKAGE in dependencies2)) return void 0;
|
|
1406
|
-
const version = dependencies2[BUDDY_RUNTIME_PACKAGE];
|
|
1407
|
-
return typeof version === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version) ? version : void 0;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
// packages/cli-core/src/dev/launch.ts
|
|
1411
|
-
function officialAgentLaunchPlan(input) {
|
|
1412
|
-
const entry = resolve3(input.projectRoot, BUDDY_PLATFORM_START_ENTRY);
|
|
2062
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2063
|
+
import { dirname as dirname2, resolve as resolve8 } from "node:path";
|
|
2064
|
+
async function officialAgentLaunchPlan(input) {
|
|
2065
|
+
const entry = resolve8(input.projectRoot, BUDDY_PLATFORM_START_ENTRY);
|
|
1413
2066
|
for (const [filename, kind] of [
|
|
1414
|
-
[
|
|
2067
|
+
[dirname2(BUDDY_PLATFORM_FACTORY_ENTRY), "directory"],
|
|
1415
2068
|
[BUDDY_PLATFORM_START_ENTRY, "file"],
|
|
1416
2069
|
[BUDDY_PLATFORM_FACTORY_ENTRY, "file"],
|
|
1417
2070
|
[BUDDY_PLATFORM_PACKAGE_JSON, "file"]
|
|
1418
2071
|
]) {
|
|
1419
2072
|
try {
|
|
1420
|
-
const entry2 = lstatSync(
|
|
2073
|
+
const entry2 = lstatSync(resolve8(input.projectRoot, filename));
|
|
1421
2074
|
if (!(kind === "directory" ? entry2.isDirectory() : entry2.isFile())) {
|
|
1422
2075
|
throw new Error(`${filename} must be a regular ${kind}`);
|
|
1423
2076
|
}
|
|
@@ -1430,18 +2083,19 @@ function officialAgentLaunchPlan(input) {
|
|
|
1430
2083
|
});
|
|
1431
2084
|
}
|
|
1432
2085
|
}
|
|
1433
|
-
const packagePath =
|
|
2086
|
+
const packagePath = resolve8(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
|
|
1434
2087
|
try {
|
|
1435
2088
|
const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
|
|
1436
2089
|
if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
1437
|
-
const runtimePath =
|
|
2090
|
+
const runtimePath = createRequire2(packagePath).resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`);
|
|
1438
2091
|
const installed = JSON.parse(readFileSync(runtimePath, "utf8")).version;
|
|
1439
2092
|
if (installed !== expected) throw new Error(`Runtime \u5DF2\u5B89\u88C5 ${installed}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
|
|
2093
|
+
await validateRuntimeDatasets(input.projectRoot, input.datasets);
|
|
1440
2094
|
} catch (cause) {
|
|
1441
2095
|
throw new BuddyDevError({
|
|
1442
2096
|
code: "DEV_CONFIG_INVALID",
|
|
1443
2097
|
phase: "config",
|
|
1444
|
-
message:
|
|
2098
|
+
message: `\u5DE5\u7A0B\u7684 Runtime \u4F9D\u8D56\u6216\u6570\u636E\u58F0\u660E\u65E0\u6548\uFF0C\u8BF7\u6309\u9501\u6587\u4EF6\u5B89\u88C5\u652F\u6301\u5F53\u524D\u5951\u7EA6\u7684 Runtime\uFF08npm \u5DE5\u7A0B\u8FD0\u884C npm ci\uFF09\uFF1A${cause instanceof Error ? cause.message : String(cause)}`,
|
|
1445
2099
|
cause
|
|
1446
2100
|
});
|
|
1447
2101
|
}
|
|
@@ -1479,16 +2133,16 @@ var NodeAgentProcessLauncher = class {
|
|
|
1479
2133
|
}
|
|
1480
2134
|
let hasExited = false;
|
|
1481
2135
|
let spawned = false;
|
|
1482
|
-
const exit = new Promise((
|
|
2136
|
+
const exit = new Promise((resolve16) => {
|
|
1483
2137
|
child.once("error", (error2) => {
|
|
1484
2138
|
if (!spawned) {
|
|
1485
2139
|
hasExited = true;
|
|
1486
|
-
|
|
2140
|
+
resolve16({ code: null, signal: null, error: error2 });
|
|
1487
2141
|
}
|
|
1488
2142
|
});
|
|
1489
2143
|
child.once("exit", (code2, signal) => {
|
|
1490
2144
|
hasExited = true;
|
|
1491
|
-
|
|
2145
|
+
resolve16({ code: code2, signal });
|
|
1492
2146
|
});
|
|
1493
2147
|
});
|
|
1494
2148
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -1504,11 +2158,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
1504
2158
|
}
|
|
1505
2159
|
});
|
|
1506
2160
|
try {
|
|
1507
|
-
await new Promise((
|
|
2161
|
+
await new Promise((resolve16, reject) => {
|
|
1508
2162
|
const onSpawn = () => {
|
|
1509
2163
|
spawned = true;
|
|
1510
2164
|
child.off("error", onError);
|
|
1511
|
-
|
|
2165
|
+
resolve16();
|
|
1512
2166
|
};
|
|
1513
2167
|
const onError = (error2) => {
|
|
1514
2168
|
child.off("spawn", onSpawn);
|
|
@@ -1547,11 +2201,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
1547
2201
|
};
|
|
1548
2202
|
function waitForExit(process2, timeoutMs) {
|
|
1549
2203
|
if (process2.hasExited()) return Promise.resolve(true);
|
|
1550
|
-
return new Promise((
|
|
1551
|
-
const timeout = setTimeout(() =>
|
|
2204
|
+
return new Promise((resolve16) => {
|
|
2205
|
+
const timeout = setTimeout(() => resolve16(false), timeoutMs);
|
|
1552
2206
|
process2.exit.then(() => {
|
|
1553
2207
|
clearTimeout(timeout);
|
|
1554
|
-
|
|
2208
|
+
resolve16(true);
|
|
1555
2209
|
});
|
|
1556
2210
|
});
|
|
1557
2211
|
}
|
|
@@ -1634,7 +2288,7 @@ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
|
|
|
1634
2288
|
while (isGroupAlive(pid)) {
|
|
1635
2289
|
const remaining = deadline - Date.now();
|
|
1636
2290
|
if (remaining <= 0) return false;
|
|
1637
|
-
await new Promise((
|
|
2291
|
+
await new Promise((resolve16) => setTimeout(resolve16, Math.min(25, remaining)));
|
|
1638
2292
|
}
|
|
1639
2293
|
return true;
|
|
1640
2294
|
}
|
|
@@ -1648,10 +2302,10 @@ function processTreeControllerFor(platform) {
|
|
|
1648
2302
|
}
|
|
1649
2303
|
|
|
1650
2304
|
// packages/cli-core/src/dev/instance-lock.ts
|
|
1651
|
-
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
1652
|
-
import { mkdir as
|
|
2305
|
+
import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
|
|
2306
|
+
import { mkdir as mkdir5, readFile as readFile6, readdir as readdir3, rename as rename3, rmdir as rmdir2, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
|
|
1653
2307
|
import { homedir } from "node:os";
|
|
1654
|
-
import { join as
|
|
2308
|
+
import { join as join6 } from "node:path";
|
|
1655
2309
|
function alreadyRunningError() {
|
|
1656
2310
|
const message = "\u8FD9\u4E2A\u642D\u5B50\u5DF2\u6709 DEV \u5B9E\u4F8B\u5728\u8FD0\u884C\u3002\u8BF7\u5148\u5728\u539F\u9884\u89C8\u9875\u70B9\u51FB\u300C\u505C\u6B62 DEV\u300D\uFF0C\u6216\u5728\u8FD0\u884C dev \u7684\u7EC8\u7AEF\u6309 Ctrl+C\uFF0C\u518D\u542F\u52A8\u3002";
|
|
1657
2311
|
return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
|
|
@@ -1671,20 +2325,20 @@ function code(error2) {
|
|
|
1671
2325
|
return error2?.code;
|
|
1672
2326
|
}
|
|
1673
2327
|
async function acquireDevInstanceLock(options) {
|
|
1674
|
-
const root = options.directory ??
|
|
1675
|
-
const key =
|
|
2328
|
+
const root = options.directory ?? join6(homedir(), ".buddy", "dev-locks");
|
|
2329
|
+
const key = createHash2("sha256").update(`${new URL(options.gatewayUrl).origin}
|
|
1676
2330
|
${options.devAgentId}`).digest("hex");
|
|
1677
|
-
const path =
|
|
2331
|
+
const path = join6(root, key);
|
|
1678
2332
|
const ownerName = `owner-${randomUUID3()}.json`;
|
|
1679
|
-
const candidate =
|
|
2333
|
+
const candidate = join6(root, `.pending-${randomUUID3()}`);
|
|
1680
2334
|
const owner = { pid: process.pid };
|
|
1681
|
-
await
|
|
1682
|
-
await
|
|
1683
|
-
await
|
|
2335
|
+
await mkdir5(root, { recursive: true, mode: 448 });
|
|
2336
|
+
await mkdir5(candidate, { mode: 448 });
|
|
2337
|
+
await writeFile3(join6(candidate, ownerName), JSON.stringify(owner), { mode: 384, flag: "wx" });
|
|
1684
2338
|
try {
|
|
1685
2339
|
for (let attempt = 0; ; attempt++) {
|
|
1686
2340
|
try {
|
|
1687
|
-
await
|
|
2341
|
+
await rename3(candidate, path);
|
|
1688
2342
|
break;
|
|
1689
2343
|
} catch (error2) {
|
|
1690
2344
|
if (!["EEXIST", "ENOTEMPTY"].includes(code(error2) ?? "")) throw error2;
|
|
@@ -1692,17 +2346,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1692
2346
|
}
|
|
1693
2347
|
let names;
|
|
1694
2348
|
try {
|
|
1695
|
-
names = await
|
|
2349
|
+
names = await readdir3(path);
|
|
1696
2350
|
} catch (error2) {
|
|
1697
2351
|
if (code(error2) === "ENOENT") continue;
|
|
1698
2352
|
throw error2;
|
|
1699
2353
|
}
|
|
1700
2354
|
if (names.length === 0) continue;
|
|
1701
2355
|
if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw alreadyRunningError();
|
|
1702
|
-
const previousPath =
|
|
2356
|
+
const previousPath = join6(path, names[0]);
|
|
1703
2357
|
let previous;
|
|
1704
2358
|
try {
|
|
1705
|
-
previous = JSON.parse(await
|
|
2359
|
+
previous = JSON.parse(await readFile6(previousPath, "utf8"));
|
|
1706
2360
|
} catch (error2) {
|
|
1707
2361
|
if (code(error2) === "ENOENT") continue;
|
|
1708
2362
|
throw alreadyRunningError();
|
|
@@ -1719,17 +2373,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1719
2373
|
});
|
|
1720
2374
|
}
|
|
1721
2375
|
} finally {
|
|
1722
|
-
await unlink4(
|
|
2376
|
+
await unlink4(join6(candidate, ownerName)).catch(() => void 0);
|
|
1723
2377
|
await rmdir2(candidate).catch(() => void 0);
|
|
1724
2378
|
}
|
|
1725
2379
|
let released = false;
|
|
1726
2380
|
return {
|
|
1727
2381
|
async attachRuntime(pid) {
|
|
1728
2382
|
if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
|
|
1729
|
-
const temporary =
|
|
2383
|
+
const temporary = join6(root, `.update-${randomUUID3()}`);
|
|
1730
2384
|
try {
|
|
1731
|
-
await
|
|
1732
|
-
await
|
|
2385
|
+
await writeFile3(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
|
|
2386
|
+
await rename3(temporary, join6(path, ownerName));
|
|
1733
2387
|
} finally {
|
|
1734
2388
|
await unlink4(temporary).catch(() => void 0);
|
|
1735
2389
|
}
|
|
@@ -1737,7 +2391,7 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1737
2391
|
async release() {
|
|
1738
2392
|
if (released) return;
|
|
1739
2393
|
released = true;
|
|
1740
|
-
await unlink4(
|
|
2394
|
+
await unlink4(join6(path, ownerName)).catch((error2) => {
|
|
1741
2395
|
if (code(error2) !== "ENOENT") throw error2;
|
|
1742
2396
|
});
|
|
1743
2397
|
await rmdir2(path).catch((error2) => {
|
|
@@ -1789,6 +2443,7 @@ function validateLaunchPlan(value) {
|
|
|
1789
2443
|
}
|
|
1790
2444
|
async function launchPlanFor(config, options, projectRoot, signal) {
|
|
1791
2445
|
const provider = options.platformLaunchPlan ?? ((context) => officialAgentLaunchPlan({
|
|
2446
|
+
datasets: config.datasets,
|
|
1792
2447
|
projectRoot: context.projectRoot
|
|
1793
2448
|
}));
|
|
1794
2449
|
try {
|
|
@@ -1979,7 +2634,7 @@ async function startBuddyDev(options) {
|
|
|
1979
2634
|
const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS, "pollIntervalMs");
|
|
1980
2635
|
const stopGracePeriodMs = positiveDuration(options.stopGracePeriodMs ?? DEFAULT_STOP_GRACE_PERIOD_MS, "stopGracePeriodMs");
|
|
1981
2636
|
const devAgentId = safeDevAgentId(options.devAgentId);
|
|
1982
|
-
const projectRoot =
|
|
2637
|
+
const projectRoot = resolve9(options.projectRoot);
|
|
1983
2638
|
const lifecycle = new AbortController();
|
|
1984
2639
|
const signal = options.signal === void 0 ? lifecycle.signal : AbortSignal.any([lifecycle.signal, options.signal]);
|
|
1985
2640
|
let agentProcess;
|
|
@@ -1997,7 +2652,7 @@ async function startBuddyDev(options) {
|
|
|
1997
2652
|
let config;
|
|
1998
2653
|
let configSource;
|
|
1999
2654
|
try {
|
|
2000
|
-
configSource = await
|
|
2655
|
+
configSource = await readFile7(join7(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
|
|
2001
2656
|
config = combineBuddyProject(parseBuddyAgentConfig(configSource));
|
|
2002
2657
|
} catch (cause) {
|
|
2003
2658
|
throw new BuddyDevError({
|
|
@@ -2007,12 +2662,14 @@ async function startBuddyDev(options) {
|
|
|
2007
2662
|
cause
|
|
2008
2663
|
});
|
|
2009
2664
|
}
|
|
2665
|
+
const widgetDelivery = await checkWidgetDelivery(projectRoot);
|
|
2666
|
+
if (widgetDelivery.status === "incomplete") options.onLog?.({ source: "agent.stderr", text: widgetDeliverySummary(widgetDelivery) + "\nDEV \u5141\u8BB8\u7EE7\u7EED\u8C03\u8BD5\uFF0C\u63D0\u5BA1\u524D\u987B\u8865\u9F50\u3002\n" });
|
|
2010
2667
|
const launchPlan = await launchPlanFor(config, options, projectRoot, signal);
|
|
2011
2668
|
const registration = await registerDevAgent({
|
|
2012
2669
|
client: options.registrationClient,
|
|
2013
2670
|
devAgentId,
|
|
2014
2671
|
configSource,
|
|
2015
|
-
configDigest: `sha256:${
|
|
2672
|
+
configDigest: `sha256:${createHash3("sha256").update(configSource).digest("hex")}`,
|
|
2016
2673
|
signal
|
|
2017
2674
|
});
|
|
2018
2675
|
throwIfDevAborted(signal);
|
|
@@ -2042,8 +2699,8 @@ async function startBuddyDev(options) {
|
|
|
2042
2699
|
shell: false,
|
|
2043
2700
|
detached: platform === "darwin",
|
|
2044
2701
|
...options.onLog === void 0 ? {} : {
|
|
2045
|
-
onStdout: (
|
|
2046
|
-
onStderr: (
|
|
2702
|
+
onStdout: (text2) => options.onLog?.({ source: "agent.stdout", text: text2 }),
|
|
2703
|
+
onStderr: (text2) => options.onLog?.({ source: "agent.stderr", text: text2 })
|
|
2047
2704
|
}
|
|
2048
2705
|
});
|
|
2049
2706
|
await instanceLock.attachRuntime(agentProcess.pid);
|
|
@@ -2099,7 +2756,7 @@ async function startBuddyDev(options) {
|
|
|
2099
2756
|
}
|
|
2100
2757
|
|
|
2101
2758
|
// packages/cli-core/src/dev/starter.ts
|
|
2102
|
-
import { resolve as
|
|
2759
|
+
import { resolve as resolve10 } from "node:path";
|
|
2103
2760
|
function createBuddyDevStarter(options = {}) {
|
|
2104
2761
|
const readManifest = options.readManifest ?? (async (projectRoot) => {
|
|
2105
2762
|
const manifest = await readBuddyProjectManifest(projectRoot);
|
|
@@ -2108,7 +2765,7 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2108
2765
|
const startDev = options.startDev ?? startBuddyDev;
|
|
2109
2766
|
return Object.freeze({
|
|
2110
2767
|
async start(input) {
|
|
2111
|
-
const projectRoot =
|
|
2768
|
+
const projectRoot = resolve10(input.projectRoot);
|
|
2112
2769
|
const manifest = await readManifest(projectRoot);
|
|
2113
2770
|
return startDev({
|
|
2114
2771
|
...input,
|
|
@@ -2119,175 +2776,6 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2119
2776
|
});
|
|
2120
2777
|
}
|
|
2121
2778
|
|
|
2122
|
-
// packages/cli-core/src/project-context.ts
|
|
2123
|
-
import { lstat as lstat5, realpath } from "node:fs/promises";
|
|
2124
|
-
import { dirname as dirname2, join as join6, resolve as resolve6 } from "node:path";
|
|
2125
|
-
var BuddyProjectContextError = class extends Error {
|
|
2126
|
-
constructor(code2, message, targetPath, options) {
|
|
2127
|
-
super(message, options);
|
|
2128
|
-
this.code = code2;
|
|
2129
|
-
this.targetPath = targetPath;
|
|
2130
|
-
this.name = "BuddyProjectContextError";
|
|
2131
|
-
}
|
|
2132
|
-
code;
|
|
2133
|
-
targetPath;
|
|
2134
|
-
};
|
|
2135
|
-
async function existingDirectory(target) {
|
|
2136
|
-
const requested = resolve6(target);
|
|
2137
|
-
let entry;
|
|
2138
|
-
try {
|
|
2139
|
-
entry = await lstat5(requested);
|
|
2140
|
-
} catch (cause) {
|
|
2141
|
-
if (cause.code === "ENOENT") {
|
|
2142
|
-
throw new BuddyProjectContextError(
|
|
2143
|
-
"PROJECT_DIRECTORY_NOT_FOUND",
|
|
2144
|
-
`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${requested}`,
|
|
2145
|
-
requested,
|
|
2146
|
-
{ cause }
|
|
2147
|
-
);
|
|
2148
|
-
}
|
|
2149
|
-
throw cause;
|
|
2150
|
-
}
|
|
2151
|
-
if (!entry.isDirectory()) {
|
|
2152
|
-
throw new BuddyProjectContextError(
|
|
2153
|
-
"PROJECT_DIRECTORY_INVALID",
|
|
2154
|
-
`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${requested}`,
|
|
2155
|
-
requested
|
|
2156
|
-
);
|
|
2157
|
-
}
|
|
2158
|
-
return realpath(requested);
|
|
2159
|
-
}
|
|
2160
|
-
async function projectFileState(directory, filename) {
|
|
2161
|
-
const configPath = join6(directory, filename);
|
|
2162
|
-
try {
|
|
2163
|
-
const entry = await lstat5(configPath);
|
|
2164
|
-
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
2165
|
-
throw new BuddyProjectContextError(
|
|
2166
|
-
"PROJECT_CONFIG_INVALID",
|
|
2167
|
-
`${configPath} \u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u662F\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\u3002`,
|
|
2168
|
-
configPath
|
|
2169
|
-
);
|
|
2170
|
-
}
|
|
2171
|
-
return "valid";
|
|
2172
|
-
} catch (cause) {
|
|
2173
|
-
if (cause.code === "ENOENT") return "missing";
|
|
2174
|
-
throw cause;
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
async function searchUp(startDirectory) {
|
|
2178
|
-
let directory = startDirectory;
|
|
2179
|
-
while (true) {
|
|
2180
|
-
if (await projectFileState(directory, BUDDY_AGENT_FILENAME) === "valid") return directory;
|
|
2181
|
-
const parent = dirname2(directory);
|
|
2182
|
-
if (parent === directory) return void 0;
|
|
2183
|
-
directory = parent;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
async function findBuddyProjectRoot(startDirectory) {
|
|
2187
|
-
return searchUp(await existingDirectory(startDirectory));
|
|
2188
|
-
}
|
|
2189
|
-
async function requireBuddyProjectRoot(startDirectory) {
|
|
2190
|
-
const requested = resolve6(startDirectory);
|
|
2191
|
-
const root = await findBuddyProjectRoot(requested);
|
|
2192
|
-
if (root !== void 0) return root;
|
|
2193
|
-
throw new BuddyProjectContextError(
|
|
2194
|
-
"PROJECT_NOT_FOUND",
|
|
2195
|
-
[
|
|
2196
|
-
"\u5F53\u524D\u76EE\u5F55\u4E0D\u5C5E\u4E8E\u642D\u642D\u9879\u76EE\u3002",
|
|
2197
|
-
`\u8BF7\u8FDB\u5165\u5305\u542B ${BUDDY_AGENT_FILENAME} \u7684\u9879\u76EE\u76EE\u5F55\u540E\u91CD\u8BD5\uFF0C\u6216\u8005\u8FD0\u884C buddy-cli init \u521B\u5EFA\u65B0\u9879\u76EE\u3002`
|
|
2198
|
-
].join("\n"),
|
|
2199
|
-
requested
|
|
2200
|
-
);
|
|
2201
|
-
}
|
|
2202
|
-
async function requireBuddyAgentProjectRoot(startDirectory) {
|
|
2203
|
-
const root = await requireBuddyProjectRoot(startDirectory);
|
|
2204
|
-
const agentPath = join6(root, BUDDY_AGENT_FILENAME);
|
|
2205
|
-
try {
|
|
2206
|
-
await readBuddyAgentConfig(root);
|
|
2207
|
-
const missing = await missingBuddyAgentFiles(root);
|
|
2208
|
-
if (missing.length > 0) throw new Error(`\u5DE5\u7A0B\u7F3A\u5C11\u6709\u6548\u6587\u4EF6\uFF1A${missing.join("\u3001")}`);
|
|
2209
|
-
return root;
|
|
2210
|
-
} catch (cause) {
|
|
2211
|
-
throw new BuddyProjectContextError(
|
|
2212
|
-
"PROJECT_NOT_FOUND",
|
|
2213
|
-
[
|
|
2214
|
-
`\u5F53\u524D buddy.agent.json \u6240\u5728\u5DE5\u7A0B\u5C1A\u4E0D\u53EF\u8FD0\u884C\uFF1A${cause instanceof Error ? cause.message : "\u8BF7\u68C0\u67E5 buddy.agent.json \u4E0E\u5DE5\u7A0B\u6587\u4EF6"}`,
|
|
2215
|
-
"\u8BF7\u5148\u8FD0\u884C buddy-cli init --mode direct \u8865\u9F50\u5B98\u65B9 Runtime \u5DE5\u7A0B\uFF0C\u518D\u8FD0\u884C dev\u3001preview \u6216 push\u3002"
|
|
2216
|
-
].join("\n"),
|
|
2217
|
-
agentPath,
|
|
2218
|
-
{ cause }
|
|
2219
|
-
);
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
async function nearestExistingDirectory(target) {
|
|
2223
|
-
let candidate = resolve6(target);
|
|
2224
|
-
while (true) {
|
|
2225
|
-
try {
|
|
2226
|
-
const entry = await lstat5(candidate);
|
|
2227
|
-
if (!entry.isDirectory()) {
|
|
2228
|
-
throw new BuddyProjectContextError(
|
|
2229
|
-
"PROJECT_DIRECTORY_INVALID",
|
|
2230
|
-
`\u521D\u59CB\u5316\u76EE\u6807\u7684\u5DF2\u6709\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${candidate}`,
|
|
2231
|
-
candidate
|
|
2232
|
-
);
|
|
2233
|
-
}
|
|
2234
|
-
return realpath(candidate);
|
|
2235
|
-
} catch (cause) {
|
|
2236
|
-
if (cause.code !== "ENOENT") throw cause;
|
|
2237
|
-
const parent = dirname2(candidate);
|
|
2238
|
-
if (parent === candidate) throw cause;
|
|
2239
|
-
candidate = parent;
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
async function assertOutsideBuddyProject(target) {
|
|
2244
|
-
const requested = resolve6(target);
|
|
2245
|
-
const container = await nearestExistingDirectory(requested);
|
|
2246
|
-
const projectRoot = await searchUp(container);
|
|
2247
|
-
if (projectRoot === void 0) return;
|
|
2248
|
-
throw new BuddyProjectContextError(
|
|
2249
|
-
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
2250
|
-
[
|
|
2251
|
-
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
2252
|
-
`\u8BF7\u76F4\u63A5\u4F7F\u7528\u73B0\u6709\u9879\u76EE\uFF0C\u4E0D\u8981\u5728\u5176\u4E2D\u5D4C\u5957\u6216\u91CD\u590D\u6267\u884C buddy-cli init\u3002`
|
|
2253
|
-
].join("\n"),
|
|
2254
|
-
requested
|
|
2255
|
-
);
|
|
2256
|
-
}
|
|
2257
|
-
async function assertInitializableBuddyTarget(target) {
|
|
2258
|
-
const requested = resolve6(target);
|
|
2259
|
-
const container = await nearestExistingDirectory(requested);
|
|
2260
|
-
const projectRoot = await searchUp(container);
|
|
2261
|
-
if (projectRoot === void 0) return;
|
|
2262
|
-
let requestedRoot = requested;
|
|
2263
|
-
try {
|
|
2264
|
-
requestedRoot = await realpath(requested);
|
|
2265
|
-
} catch (cause) {
|
|
2266
|
-
if (cause.code !== "ENOENT") throw cause;
|
|
2267
|
-
}
|
|
2268
|
-
if (projectRoot === requestedRoot) {
|
|
2269
|
-
try {
|
|
2270
|
-
await readBuddyProjectManifest(projectRoot);
|
|
2271
|
-
return;
|
|
2272
|
-
} catch (cause) {
|
|
2273
|
-
throw new BuddyProjectContextError(
|
|
2274
|
-
"PROJECT_CONFIG_INVALID",
|
|
2275
|
-
`${join6(projectRoot, BUDDY_AGENT_FILENAME)} \u4E0D\u662F\u6709\u6548\u7684\u9879\u76EE\u6E05\u5355\u3002`,
|
|
2276
|
-
requested,
|
|
2277
|
-
{ cause }
|
|
2278
|
-
);
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
throw new BuddyProjectContextError(
|
|
2282
|
-
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
2283
|
-
[
|
|
2284
|
-
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u5B8C\u6574\u7684\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
2285
|
-
"\u8BF7\u76F4\u63A5\u4F7F\u7528\u73B0\u6709\u9879\u76EE\uFF0C\u4E0D\u8981\u5728\u5176\u4E2D\u5D4C\u5957\u6216\u91CD\u590D\u6267\u884C buddy-cli init\u3002"
|
|
2286
|
-
].join("\n"),
|
|
2287
|
-
requested
|
|
2288
|
-
);
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
2779
|
// packages/cli-core/src/commands/dev.ts
|
|
2292
2780
|
var DevCommandUsageError = class extends Error {
|
|
2293
2781
|
constructor(message) {
|
|
@@ -2368,7 +2856,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2368
2856
|
return { exitCode: 0 };
|
|
2369
2857
|
}
|
|
2370
2858
|
try {
|
|
2371
|
-
const requestedRoot =
|
|
2859
|
+
const requestedRoot = resolve11(options.cwd ?? process.cwd(), parsed.directory);
|
|
2372
2860
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
2373
2861
|
const environment = options.env ?? process.env;
|
|
2374
2862
|
const url = appServerUrl(parsed.appServer, environment);
|
|
@@ -2421,7 +2909,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2421
2909
|
}
|
|
2422
2910
|
|
|
2423
2911
|
// packages/cli-core/src/commands/cloud.ts
|
|
2424
|
-
import { resolve as
|
|
2912
|
+
import { resolve as resolve12 } from "node:path";
|
|
2425
2913
|
|
|
2426
2914
|
// packages/cli-core/src/cloud/auth.ts
|
|
2427
2915
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -2592,7 +3080,7 @@ function positiveTimeout(value, fallback, name) {
|
|
|
2592
3080
|
if (!Number.isSafeInteger(result) || result <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
2593
3081
|
return result;
|
|
2594
3082
|
}
|
|
2595
|
-
function
|
|
3083
|
+
function object3(value, path) {
|
|
2596
3084
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2597
3085
|
throw invalid(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
2598
3086
|
}
|
|
@@ -2612,7 +3100,7 @@ function optionalText(value, path, allowNewlines = false) {
|
|
|
2612
3100
|
}
|
|
2613
3101
|
function meta(value, path) {
|
|
2614
3102
|
if (value === null) return null;
|
|
2615
|
-
const source =
|
|
3103
|
+
const source = object3(value, path);
|
|
2616
3104
|
const name = source.name === void 0 ? void 0 : safeText(source.name, `${path}.name`);
|
|
2617
3105
|
const avatar = optionalText(source.avatar, `${path}.avatar`);
|
|
2618
3106
|
const tagline = optionalText(source.tagline, `${path}.tagline`);
|
|
@@ -2646,7 +3134,7 @@ function auditStatus(value, path) {
|
|
|
2646
3134
|
return value;
|
|
2647
3135
|
}
|
|
2648
3136
|
function packageVersion(value, path) {
|
|
2649
|
-
const source =
|
|
3137
|
+
const source = object3(value, path);
|
|
2650
3138
|
const auditNote = source.auditNote === null ? null : safeText(source.auditNote, `${path}.auditNote`, true);
|
|
2651
3139
|
return Object.freeze({
|
|
2652
3140
|
version: safeText(source.version, `${path}.version`),
|
|
@@ -2664,7 +3152,7 @@ function positivePage(value, name) {
|
|
|
2664
3152
|
return value;
|
|
2665
3153
|
}
|
|
2666
3154
|
function buddy(value, includeToken) {
|
|
2667
|
-
const source =
|
|
3155
|
+
const source = object3(value, "response");
|
|
2668
3156
|
const token = includeToken ? safeText(source.token, "response.token") : void 0;
|
|
2669
3157
|
return Object.freeze({
|
|
2670
3158
|
id: safeText(source.id, "response.id"),
|
|
@@ -2782,17 +3270,6 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2782
3270
|
if (!response.ok) throw serverError(response, payload);
|
|
2783
3271
|
return payload;
|
|
2784
3272
|
}
|
|
2785
|
-
async listModels() {
|
|
2786
|
-
const source = object2(await this.#request("/cli/models", "GET"), "response");
|
|
2787
|
-
if (!Array.isArray(source.models)) throw invalid("response.models \u5FC5\u987B\u662F\u6A21\u578B ID \u6570\u7EC4");
|
|
2788
|
-
const models = source.models.map((value, index) => {
|
|
2789
|
-
const id = safeText(value, `response.models[${index}]`);
|
|
2790
|
-
if (id.length > 128 || /\s/u.test(id)) throw invalid("\u6A21\u578B ID \u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u6216\u8D85\u8FC7 128 \u4E2A\u5B57\u7B26");
|
|
2791
|
-
return id;
|
|
2792
|
-
});
|
|
2793
|
-
if (new Set(models).size !== models.length) throw invalid("response.models \u4E2D\u6709\u91CD\u590D\u6A21\u578B ID");
|
|
2794
|
-
return Object.freeze({ models: Object.freeze(models) });
|
|
2795
|
-
}
|
|
2796
3273
|
async createBuddy(request) {
|
|
2797
3274
|
validateBuddyMeta(request.meta);
|
|
2798
3275
|
return buddy(await this.#request("/cli/buddies", "POST", { json: { meta: request.meta } }), true);
|
|
@@ -2804,7 +3281,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2804
3281
|
}), true);
|
|
2805
3282
|
}
|
|
2806
3283
|
async listBuddies() {
|
|
2807
|
-
const source =
|
|
3284
|
+
const source = object3(await this.#request("/cli/buddies", "GET"), "response");
|
|
2808
3285
|
if (!Array.isArray(source.buddies)) throw invalid("response.buddies \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
2809
3286
|
return Object.freeze({
|
|
2810
3287
|
buddies: Object.freeze(source.buddies.map((item) => buddy(item, false)))
|
|
@@ -2821,7 +3298,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2821
3298
|
...pageSize === void 0 ? {} : { pageSize: String(pageSize) }
|
|
2822
3299
|
});
|
|
2823
3300
|
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
2824
|
-
const source =
|
|
3301
|
+
const source = object3(await this.#request(
|
|
2825
3302
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages${suffix}`,
|
|
2826
3303
|
"GET"
|
|
2827
3304
|
), "response");
|
|
@@ -2843,7 +3320,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2843
3320
|
throw new CloudLifecycleError("INVALID_REQUEST", "\u63D0\u4EA4\u5305\u4E0D\u80FD\u4E3A\u7A7A");
|
|
2844
3321
|
}
|
|
2845
3322
|
const query = new URLSearchParams({ commit });
|
|
2846
|
-
const source =
|
|
3323
|
+
const source = object3(await this.#request(
|
|
2847
3324
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages/push?${query.toString()}`,
|
|
2848
3325
|
"POST",
|
|
2849
3326
|
{
|
|
@@ -2860,13 +3337,13 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2860
3337
|
});
|
|
2861
3338
|
}
|
|
2862
3339
|
async logout(accessToken) {
|
|
2863
|
-
const source =
|
|
3340
|
+
const source = object3(await this.#request("/cli/auth/logout", "POST", { accessToken }), "response");
|
|
2864
3341
|
if (source.ok !== true) throw invalid("response.ok \u5FC5\u987B\u662F true");
|
|
2865
3342
|
}
|
|
2866
3343
|
};
|
|
2867
3344
|
|
|
2868
3345
|
// packages/cli-core/src/cloud/browser-login.ts
|
|
2869
|
-
import { createHash as
|
|
3346
|
+
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
2870
3347
|
import { spawn } from "node:child_process";
|
|
2871
3348
|
import { createServer } from "node:http";
|
|
2872
3349
|
|
|
@@ -2926,10 +3403,10 @@ function browserCommand(url) {
|
|
|
2926
3403
|
}
|
|
2927
3404
|
async function openExternalUrl(url) {
|
|
2928
3405
|
const launch = browserCommand(url);
|
|
2929
|
-
await new Promise((
|
|
3406
|
+
await new Promise((resolve16, reject) => {
|
|
2930
3407
|
const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
2931
3408
|
child.once("error", reject);
|
|
2932
|
-
child.once("exit", (code2) => code2 === 0 ?
|
|
3409
|
+
child.once("exit", (code2) => code2 === 0 ? resolve16() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
2933
3410
|
});
|
|
2934
3411
|
}
|
|
2935
3412
|
async function write(res, status, title, message, webUrl, tone = "error") {
|
|
@@ -2941,10 +3418,10 @@ async function write(res, status, title, message, webUrl, tone = "error") {
|
|
|
2941
3418
|
"referrer-policy": "no-referrer",
|
|
2942
3419
|
"x-content-type-options": "nosniff"
|
|
2943
3420
|
});
|
|
2944
|
-
await new Promise((
|
|
3421
|
+
await new Promise((resolve16, reject) => {
|
|
2945
3422
|
res.once("error", reject);
|
|
2946
|
-
res.once("close",
|
|
2947
|
-
res.end(renderLoginResult(title, message, webUrl, tone),
|
|
3423
|
+
res.once("close", resolve16);
|
|
3424
|
+
res.end(renderLoginResult(title, message, webUrl, tone), resolve16);
|
|
2948
3425
|
});
|
|
2949
3426
|
}
|
|
2950
3427
|
async function readJson(response) {
|
|
@@ -2974,16 +3451,16 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
2974
3451
|
if (request.signal?.aborted) throw request.signal.reason;
|
|
2975
3452
|
const callbackSecret = randomBytes(24).toString("hex");
|
|
2976
3453
|
const codeVerifier = randomBytes(32).toString("base64url");
|
|
2977
|
-
const codeChallenge =
|
|
3454
|
+
const codeChallenge = createHash4("sha256").update(codeVerifier).digest("base64url");
|
|
2978
3455
|
let requestId = "";
|
|
2979
3456
|
let settle;
|
|
2980
3457
|
let reject;
|
|
2981
3458
|
let settled = false;
|
|
2982
|
-
const result = new Promise((
|
|
3459
|
+
const result = new Promise((resolve16, rejectResult) => {
|
|
2983
3460
|
settle = (value) => {
|
|
2984
3461
|
if (!settled) {
|
|
2985
3462
|
settled = true;
|
|
2986
|
-
|
|
3463
|
+
resolve16(value);
|
|
2987
3464
|
}
|
|
2988
3465
|
};
|
|
2989
3466
|
reject = (reason) => {
|
|
@@ -3002,9 +3479,9 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3002
3479
|
state: request.state
|
|
3003
3480
|
}).then(settle, reject);
|
|
3004
3481
|
});
|
|
3005
|
-
await new Promise((
|
|
3482
|
+
await new Promise((resolve16, rejectListen) => {
|
|
3006
3483
|
server.once("error", rejectListen);
|
|
3007
|
-
server.listen(0, "127.0.0.1", () =>
|
|
3484
|
+
server.listen(0, "127.0.0.1", () => resolve16());
|
|
3008
3485
|
});
|
|
3009
3486
|
const address = server.address();
|
|
3010
3487
|
if (!address || typeof address === "string") {
|
|
@@ -3037,8 +3514,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3037
3514
|
} finally {
|
|
3038
3515
|
clearTimeout(timer);
|
|
3039
3516
|
request.signal?.removeEventListener("abort", abort);
|
|
3040
|
-
await new Promise((
|
|
3041
|
-
server.close(() =>
|
|
3517
|
+
await new Promise((resolve16) => {
|
|
3518
|
+
server.close(() => resolve16());
|
|
3042
3519
|
server.closeIdleConnections();
|
|
3043
3520
|
server.closeAllConnections();
|
|
3044
3521
|
});
|
|
@@ -3153,7 +3630,7 @@ function parse(source) {
|
|
|
3153
3630
|
});
|
|
3154
3631
|
}
|
|
3155
3632
|
async function runCredentialCommand(input) {
|
|
3156
|
-
return await new Promise((
|
|
3633
|
+
return await new Promise((resolve16, reject) => {
|
|
3157
3634
|
const child = spawn2(input.command, [...input.args], {
|
|
3158
3635
|
shell: false,
|
|
3159
3636
|
windowsHide: true,
|
|
@@ -3169,7 +3646,7 @@ async function runCredentialCommand(input) {
|
|
|
3169
3646
|
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3170
3647
|
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3171
3648
|
child.once("error", reject);
|
|
3172
|
-
child.once("close", (code2) =>
|
|
3649
|
+
child.once("close", (code2) => resolve16({
|
|
3173
3650
|
code: code2 ?? 1,
|
|
3174
3651
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3175
3652
|
stderr: Buffer.concat(stderr).toString("utf8")
|
|
@@ -3237,9 +3714,6 @@ var MockDeveloperPlatformClient = class {
|
|
|
3237
3714
|
constructor(options = {}) {
|
|
3238
3715
|
this.#now = options.now ?? Date.now;
|
|
3239
3716
|
}
|
|
3240
|
-
async listModels() {
|
|
3241
|
-
throw new CloudLifecycleError("INVALID_REQUEST", "\u672C\u5730\u6A21\u62DF\u6A21\u5F0F\u4E0D\u63D0\u4F9B\u865A\u5047\u7684\u5E73\u53F0\u6A21\u578B\u76EE\u5F55\uFF0C\u8BF7\u8FDE\u63A5\u771F\u5B9E MLB Server");
|
|
3242
|
-
}
|
|
3243
3717
|
async createBuddy(request) {
|
|
3244
3718
|
const id = `b_mock_${randomBytes2(8).toString("hex")}`;
|
|
3245
3719
|
const value = this.#value({
|
|
@@ -3342,7 +3816,6 @@ var CloudCommandUsageError = class extends Error {
|
|
|
3342
3816
|
var HELP = Object.freeze({
|
|
3343
3817
|
login: "\u7528\u6CD5: buddy-cli login",
|
|
3344
3818
|
logout: "\u7528\u6CD5: buddy-cli logout",
|
|
3345
|
-
models: "\u7528\u6CD5: buddy-cli models\uFF08\u9700\u8981\u767B\u5F55\uFF0C\u4E0D\u8981\u6C42\u5F53\u524D\u76EE\u5F55\u662F Agent \u5DE5\u7A0B\uFF09",
|
|
3346
3819
|
push: "\u7528\u6CD5: buddy-cli push [--directory <\u9879\u76EE\u76EE\u5F55>] [--commit <\u672C\u6B21\u6539\u52A8\u8BF4\u660E>]",
|
|
3347
3820
|
status: "\u7528\u6CD5: buddy-cli status [--directory <\u9879\u76EE\u76EE\u5F55>]"
|
|
3348
3821
|
});
|
|
@@ -3393,7 +3866,7 @@ function dependencies(options) {
|
|
|
3393
3866
|
return { session: options.session, client: options.client };
|
|
3394
3867
|
}
|
|
3395
3868
|
function requestedDirectory(options, parsed) {
|
|
3396
|
-
return
|
|
3869
|
+
return resolve12(options.cwd ?? process.cwd(), parsed.values.get("--directory") ?? ".");
|
|
3397
3870
|
}
|
|
3398
3871
|
function auditLabel(status) {
|
|
3399
3872
|
if (status === "auditing") return "\u5BA1\u6838\u4E2D";
|
|
@@ -3482,20 +3955,6 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
3482
3955
|
}
|
|
3483
3956
|
if (command === "logout") return await runLogout(io, options);
|
|
3484
3957
|
if (command === "push") return await runPush(parsed, io, options);
|
|
3485
|
-
if (command === "models") {
|
|
3486
|
-
if (options.mode === "local-mock") throw new CloudCommandUsageError("\u8BF7\u8FDE\u63A5\u771F\u5B9E MLB Server \u67E5\u8BE2\u6A21\u578B\uFF0C\u672C\u5730\u6A21\u62DF\u6A21\u5F0F\u4E0D\u63D0\u4F9B\u6A21\u578B\u76EE\u5F55");
|
|
3487
|
-
const { session: session2, client: client2 } = dependencies(options);
|
|
3488
|
-
await session2.token({ signal: options.signal });
|
|
3489
|
-
const { models } = await client2.listModels();
|
|
3490
|
-
if (models.length === 0) io.write("\u5E73\u53F0\u5F53\u524D\u6CA1\u6709\u53EF\u9009\u6A21\u578B\u3002");
|
|
3491
|
-
else {
|
|
3492
|
-
io.write("\u5E73\u53F0\u53EF\u9009\u6A21\u578B\uFF1A");
|
|
3493
|
-
for (const id of models) io.write(`- ${id}`);
|
|
3494
|
-
io.write('\u5728 buddy.agent.json \u9876\u5C42\u8BBE\u7F6E "model": "\u6240\u9009\u6A21\u578B ID"\uFF0C\u91CD\u542F dev / preview \u540E\u751F\u6548\u3002');
|
|
3495
|
-
io.write("\u5E73\u53F0\u6682\u672A\u63D0\u4F9B\u9ED8\u8BA4\u9879\u548C\u6A21\u578B\u80FD\u529B\u63CF\u8FF0\uFF1B\u6B64\u5217\u8868\u4E0D\u4F1A\u81EA\u52A8\u4FEE\u6539\u5DE5\u7A0B\u914D\u7F6E\u3002");
|
|
3496
|
-
}
|
|
3497
|
-
return { exitCode: 0 };
|
|
3498
|
-
}
|
|
3499
3958
|
const root = await (options.resolveProjectRoot ?? requireBuddyProjectRoot)(requestedDirectory(options, parsed));
|
|
3500
3959
|
const manifest = await (options.readManifest ?? readBuddyProjectManifest)(root);
|
|
3501
3960
|
if (options.mode === "local-mock") {
|
|
@@ -3523,8 +3982,8 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
3523
3982
|
var CLOUD_COMMAND_HELP = HELP;
|
|
3524
3983
|
|
|
3525
3984
|
// packages/cli-core/src/migration/index.ts
|
|
3526
|
-
import { lstat as
|
|
3527
|
-
import { extname, isAbsolute as isAbsolute2, join as
|
|
3985
|
+
import { lstat as lstat7, open as open3, readdir as readdir4, realpath as realpath4, stat } from "node:fs/promises";
|
|
3986
|
+
import { extname, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve13, sep, win32 as win322 } from "node:path";
|
|
3528
3987
|
var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
|
|
3529
3988
|
maxFiles: 128,
|
|
3530
3989
|
maxFileBytes: 256 * 1024,
|
|
@@ -3677,7 +4136,7 @@ async function existingProjectRoot(value) {
|
|
|
3677
4136
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
3678
4137
|
}
|
|
3679
4138
|
try {
|
|
3680
|
-
const root = await
|
|
4139
|
+
const root = await realpath4(resolve13(value));
|
|
3681
4140
|
if (!(await stat(root)).isDirectory()) {
|
|
3682
4141
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
3683
4142
|
}
|
|
@@ -3693,9 +4152,9 @@ async function existingProjectRoot(value) {
|
|
|
3693
4152
|
}
|
|
3694
4153
|
}
|
|
3695
4154
|
async function existingSkillRoot(projectRoot, skillPath) {
|
|
3696
|
-
const candidate = skillPath === "." ? projectRoot :
|
|
4155
|
+
const candidate = skillPath === "." ? projectRoot : join8(projectRoot, ...skillPath.split("/"));
|
|
3697
4156
|
try {
|
|
3698
|
-
await
|
|
4157
|
+
await lstat7(candidate);
|
|
3699
4158
|
} catch (cause) {
|
|
3700
4159
|
if (filesystemCode(cause) === "ENOENT") {
|
|
3701
4160
|
return rejectScan("SKILL_ROOT_NOT_FOUND", skillPath, "\u627E\u4E0D\u5230\u6307\u5B9A\u7684 Skill \u76EE\u5F55\u3002");
|
|
@@ -3704,7 +4163,7 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
3704
4163
|
}
|
|
3705
4164
|
let root;
|
|
3706
4165
|
try {
|
|
3707
|
-
root = await
|
|
4166
|
+
root = await realpath4(candidate);
|
|
3708
4167
|
} catch (cause) {
|
|
3709
4168
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3710
4169
|
return rejectScan("SKILL_SYMLINK_CYCLE", skillPath, "Skill \u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -3730,10 +4189,10 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
3730
4189
|
}
|
|
3731
4190
|
async function assertEntrypoint(context) {
|
|
3732
4191
|
const entryPath = reportChild(context.skillPath, "SKILL.md");
|
|
3733
|
-
const candidate =
|
|
4192
|
+
const candidate = join8(context.skillRoot, "SKILL.md");
|
|
3734
4193
|
try {
|
|
3735
|
-
await
|
|
3736
|
-
const target = await
|
|
4194
|
+
await lstat7(candidate);
|
|
4195
|
+
const target = await realpath4(candidate);
|
|
3737
4196
|
if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
|
|
3738
4197
|
rejectScan(
|
|
3739
4198
|
"SKILL_SYMLINK_ESCAPE",
|
|
@@ -3777,7 +4236,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3777
4236
|
}
|
|
3778
4237
|
let directory;
|
|
3779
4238
|
try {
|
|
3780
|
-
directory = await
|
|
4239
|
+
directory = await realpath4(actualDirectory);
|
|
3781
4240
|
} catch (cause) {
|
|
3782
4241
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3783
4242
|
return rejectScan("SKILL_SYMLINK_CYCLE", reportDirectory, "Skill \u76EE\u5F55\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -3797,7 +4256,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3797
4256
|
nextAncestors.add(directoryKey);
|
|
3798
4257
|
let entries;
|
|
3799
4258
|
try {
|
|
3800
|
-
entries = await
|
|
4259
|
+
entries = await readdir4(directory, { withFileTypes: true, encoding: "utf8" });
|
|
3801
4260
|
} catch (cause) {
|
|
3802
4261
|
return rejectScan("SKILL_SCAN_IO_ERROR", reportDirectory, "\u65E0\u6CD5\u5217\u51FA Skill \u5B50\u76EE\u5F55\u3002", cause);
|
|
3803
4262
|
}
|
|
@@ -3834,10 +4293,10 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3834
4293
|
}
|
|
3835
4294
|
for (const entry of entries) {
|
|
3836
4295
|
const childReportPath = reportChild(reportDirectory, entry.name);
|
|
3837
|
-
const childPath =
|
|
4296
|
+
const childPath = join8(directory, entry.name);
|
|
3838
4297
|
let target;
|
|
3839
4298
|
try {
|
|
3840
|
-
target = await
|
|
4299
|
+
target = await realpath4(childPath);
|
|
3841
4300
|
} catch (cause) {
|
|
3842
4301
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3843
4302
|
return rejectScan("SKILL_SYMLINK_CYCLE", childReportPath, "Skill \u5185\u5BB9\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -4288,9 +4747,9 @@ async function scanExternalSkillPortability(options) {
|
|
|
4288
4747
|
}
|
|
4289
4748
|
|
|
4290
4749
|
// packages/cli-core/src/push/preflight.ts
|
|
4291
|
-
import { createHash as
|
|
4292
|
-
import { lstat as
|
|
4293
|
-
import { isAbsolute as isAbsolute3, join as
|
|
4750
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
4751
|
+
import { lstat as lstat8, open as open4, readdir as readdir5, realpath as realpath5 } from "node:fs/promises";
|
|
4752
|
+
import { isAbsolute as isAbsolute3, join as join9, relative as relative2, resolve as resolve14, sep as sep2, win32 as win323 } from "node:path";
|
|
4294
4753
|
|
|
4295
4754
|
// packages/cli-core/src/push/errors.ts
|
|
4296
4755
|
var PushPreflightError = class extends Error {
|
|
@@ -4388,7 +4847,7 @@ function error(options) {
|
|
|
4388
4847
|
throw new PushPreflightError(options);
|
|
4389
4848
|
}
|
|
4390
4849
|
function sha256(value) {
|
|
4391
|
-
return `sha256:${
|
|
4850
|
+
return `sha256:${createHash5("sha256").update(value).digest("hex")}`;
|
|
4392
4851
|
}
|
|
4393
4852
|
function positiveLimit(value, name, hardMaximum) {
|
|
4394
4853
|
if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
|
|
@@ -4478,10 +4937,10 @@ function normalizeBookletPath(value) {
|
|
|
4478
4937
|
return normalized;
|
|
4479
4938
|
}
|
|
4480
4939
|
async function canonicalProjectRoot(projectRoot) {
|
|
4481
|
-
const absolute =
|
|
4940
|
+
const absolute = resolve14(projectRoot);
|
|
4482
4941
|
let entry;
|
|
4483
4942
|
try {
|
|
4484
|
-
entry = await
|
|
4943
|
+
entry = await lstat8(absolute);
|
|
4485
4944
|
} catch (cause) {
|
|
4486
4945
|
error({
|
|
4487
4946
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4498,7 +4957,7 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
4498
4957
|
});
|
|
4499
4958
|
}
|
|
4500
4959
|
try {
|
|
4501
|
-
return await
|
|
4960
|
+
return await realpath5(absolute);
|
|
4502
4961
|
} catch (cause) {
|
|
4503
4962
|
error({
|
|
4504
4963
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4509,10 +4968,10 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
4509
4968
|
}
|
|
4510
4969
|
}
|
|
4511
4970
|
async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
4512
|
-
const path =
|
|
4971
|
+
const path = join9(projectRoot, filename);
|
|
4513
4972
|
let entry;
|
|
4514
4973
|
try {
|
|
4515
|
-
entry = await
|
|
4974
|
+
entry = await lstat8(path);
|
|
4516
4975
|
} catch (cause) {
|
|
4517
4976
|
error({
|
|
4518
4977
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -4543,22 +5002,22 @@ async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
|
4543
5002
|
maxFileBytes
|
|
4544
5003
|
});
|
|
4545
5004
|
}
|
|
4546
|
-
function decodeCapturedConfig(
|
|
4547
|
-
if (
|
|
5005
|
+
function decodeCapturedConfig(file2) {
|
|
5006
|
+
if (file2.contents === void 0) {
|
|
4548
5007
|
error({
|
|
4549
5008
|
code: "PUSH_CONFIG_INVALID",
|
|
4550
|
-
path:
|
|
4551
|
-
message: `\u65E0\u6CD5\u8BFB\u53D6 ${
|
|
5009
|
+
path: file2.path,
|
|
5010
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6 ${file2.path} \u5185\u5BB9\u3002`
|
|
4552
5011
|
});
|
|
4553
5012
|
}
|
|
4554
5013
|
let source;
|
|
4555
5014
|
try {
|
|
4556
|
-
source = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
5015
|
+
source = new TextDecoder("utf-8", { fatal: true }).decode(file2.contents);
|
|
4557
5016
|
} catch (cause) {
|
|
4558
5017
|
error({
|
|
4559
5018
|
code: "PUSH_CONFIG_INVALID",
|
|
4560
|
-
path:
|
|
4561
|
-
message: `${
|
|
5019
|
+
path: file2.path,
|
|
5020
|
+
message: `${file2.path} \u4E0D\u662F\u6709\u6548 UTF-8\u3002`,
|
|
4562
5021
|
cause
|
|
4563
5022
|
});
|
|
4564
5023
|
}
|
|
@@ -4579,7 +5038,7 @@ function parseCapturedProject(agentFile) {
|
|
|
4579
5038
|
async function selectLockfile(projectRoot) {
|
|
4580
5039
|
let names;
|
|
4581
5040
|
try {
|
|
4582
|
-
names = await
|
|
5041
|
+
names = await readdir5(projectRoot);
|
|
4583
5042
|
} catch (cause) {
|
|
4584
5043
|
error({
|
|
4585
5044
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4634,7 +5093,7 @@ async function hashRegularFile(options) {
|
|
|
4634
5093
|
message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
|
|
4635
5094
|
});
|
|
4636
5095
|
}
|
|
4637
|
-
const hash =
|
|
5096
|
+
const hash = createHash5("sha256");
|
|
4638
5097
|
const captured = [];
|
|
4639
5098
|
const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
|
|
4640
5099
|
let bytes = 0;
|
|
@@ -4688,7 +5147,7 @@ async function createManifest(options) {
|
|
|
4688
5147
|
}
|
|
4689
5148
|
let names;
|
|
4690
5149
|
try {
|
|
4691
|
-
names = await
|
|
5150
|
+
names = await readdir5(directory);
|
|
4692
5151
|
} catch (cause) {
|
|
4693
5152
|
error({
|
|
4694
5153
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4712,10 +5171,10 @@ async function createManifest(options) {
|
|
|
4712
5171
|
sensitiveEntriesExcluded += 1;
|
|
4713
5172
|
continue;
|
|
4714
5173
|
}
|
|
4715
|
-
const absolutePath =
|
|
5174
|
+
const absolutePath = join9(directory, name);
|
|
4716
5175
|
let entry;
|
|
4717
5176
|
try {
|
|
4718
|
-
entry = await
|
|
5177
|
+
entry = await lstat8(absolutePath);
|
|
4719
5178
|
} catch (cause) {
|
|
4720
5179
|
error({
|
|
4721
5180
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -4746,7 +5205,7 @@ async function createManifest(options) {
|
|
|
4746
5205
|
}
|
|
4747
5206
|
let canonicalPath;
|
|
4748
5207
|
try {
|
|
4749
|
-
canonicalPath = await
|
|
5208
|
+
canonicalPath = await realpath5(absolutePath);
|
|
4750
5209
|
} catch (cause) {
|
|
4751
5210
|
error({
|
|
4752
5211
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -4805,15 +5264,15 @@ async function createManifest(options) {
|
|
|
4805
5264
|
message: `\u63D0\u4EA4\u6587\u4EF6\u603B\u91CF\u8D85\u8FC7\u4E0A\u9650 ${options.limits.maxTotalBytes} bytes\u3002`
|
|
4806
5265
|
});
|
|
4807
5266
|
}
|
|
4808
|
-
const
|
|
5267
|
+
const file2 = await hashRegularFile({
|
|
4809
5268
|
absolutePath,
|
|
4810
5269
|
portablePath: path,
|
|
4811
5270
|
expected: entry,
|
|
4812
5271
|
captureContents: options.capturePaths.has(path),
|
|
4813
5272
|
maxFileBytes: options.limits.maxFileBytes
|
|
4814
5273
|
});
|
|
4815
|
-
files.push(
|
|
4816
|
-
totalBytes +=
|
|
5274
|
+
files.push(file2);
|
|
5275
|
+
totalBytes += file2.bytes;
|
|
4817
5276
|
}
|
|
4818
5277
|
};
|
|
4819
5278
|
await walk(options.projectRoot, []);
|
|
@@ -4894,10 +5353,10 @@ function validateLockfile(lockfile, contents) {
|
|
|
4894
5353
|
function bundleDigest(files) {
|
|
4895
5354
|
const descriptor = JSON.stringify({
|
|
4896
5355
|
schemaVersion: "1",
|
|
4897
|
-
files: files.map((
|
|
4898
|
-
path:
|
|
4899
|
-
bytes:
|
|
4900
|
-
digest:
|
|
5356
|
+
files: files.map((file2) => ({
|
|
5357
|
+
path: file2.path,
|
|
5358
|
+
bytes: file2.bytes,
|
|
5359
|
+
digest: file2.digest
|
|
4901
5360
|
}))
|
|
4902
5361
|
});
|
|
4903
5362
|
return sha256(`buddy-push-bundle-v1\0${descriptor}`);
|
|
@@ -4907,8 +5366,8 @@ function warning(code2, message) {
|
|
|
4907
5366
|
}
|
|
4908
5367
|
function assertOfficialProjectIncluded(files) {
|
|
4909
5368
|
for (const path of OFFICIAL_RUNTIME_INPUTS) {
|
|
4910
|
-
const
|
|
4911
|
-
if (
|
|
5369
|
+
const file2 = files.find((entry) => entry.path === path);
|
|
5370
|
+
if (file2 === void 0 || file2.bytes === 0) {
|
|
4912
5371
|
error({
|
|
4913
5372
|
code: "PUSH_PROJECT_INVALID",
|
|
4914
5373
|
path,
|
|
@@ -4947,7 +5406,7 @@ function assertOfficialProjectIncluded(files) {
|
|
|
4947
5406
|
}
|
|
4948
5407
|
}
|
|
4949
5408
|
async function assertCapturedConfigStable(options) {
|
|
4950
|
-
const manifestEntry = options.manifestFiles.find((
|
|
5409
|
+
const manifestEntry = options.manifestFiles.find((file2) => file2.path === options.filename);
|
|
4951
5410
|
if (manifestEntry === void 0) {
|
|
4952
5411
|
error({
|
|
4953
5412
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -4964,8 +5423,8 @@ async function assertCapturedConfigStable(options) {
|
|
|
4964
5423
|
}
|
|
4965
5424
|
let current;
|
|
4966
5425
|
try {
|
|
4967
|
-
const path =
|
|
4968
|
-
const entry = await
|
|
5426
|
+
const path = join9(options.projectRoot, options.filename);
|
|
5427
|
+
const entry = await lstat8(path);
|
|
4969
5428
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
4970
5429
|
error({
|
|
4971
5430
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5008,6 +5467,8 @@ async function createPushPreflight(options) {
|
|
|
5008
5467
|
BUDDY_AGENT_FILENAME
|
|
5009
5468
|
);
|
|
5010
5469
|
parseCapturedProject(initialAgentEntry);
|
|
5470
|
+
const widgetDelivery = await checkWidgetDelivery(projectRoot);
|
|
5471
|
+
if (widgetDelivery.status === "incomplete") error({ code: "PUSH_WIDGET_DELIVERY_INCOMPLETE", message: widgetDeliverySummary(widgetDelivery) });
|
|
5011
5472
|
const lockfile = await selectLockfile(projectRoot);
|
|
5012
5473
|
assertBookletIsNotRequiredInput(bookletPath, lockfile);
|
|
5013
5474
|
const manifest = await createManifest({
|
|
@@ -5016,7 +5477,12 @@ async function createPushPreflight(options) {
|
|
|
5016
5477
|
capturePaths: /* @__PURE__ */ new Set([BUDDY_AGENT_FILENAME, lockfile.path, BUDDY_PLATFORM_PACKAGE_JSON, BUDDY_PLATFORM_AGENT_ENTRY, BUDDY_PLATFORM_FACTORY_ENTRY]),
|
|
5017
5478
|
...bookletPath === void 0 ? {} : { bookletPath }
|
|
5018
5479
|
});
|
|
5019
|
-
const
|
|
5480
|
+
for (const checked of widgetDelivery.files ?? []) {
|
|
5481
|
+
if (manifest.files.find((file2) => file2.path === checked.path)?.digest !== checked.digest) {
|
|
5482
|
+
error({ code: "PUSH_WIDGET_DELIVERY_INCOMPLETE", message: `\u5C0F\u6302\u4EF6\u6838\u5BF9\u540E\u6587\u4EF6\u53D8\u5316\u6216\u672A\u8FDB\u5165\u4E0A\u4F20\u5305\uFF1A${checked.path}\uFF1B\u8BF7\u91CD\u65B0\u68C0\u67E5\u3002` });
|
|
5483
|
+
}
|
|
5484
|
+
}
|
|
5485
|
+
const lockEntry = manifest.files.find((file2) => file2.path === lockfile.path);
|
|
5020
5486
|
if (lockEntry?.contents === void 0) {
|
|
5021
5487
|
error({
|
|
5022
5488
|
code: "PUSH_LOCKFILE_INVALID",
|
|
@@ -5040,10 +5506,23 @@ async function createPushPreflight(options) {
|
|
|
5040
5506
|
manifestFiles: manifest.files,
|
|
5041
5507
|
maxFileBytes: limits.maxFileBytes
|
|
5042
5508
|
});
|
|
5043
|
-
const
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5509
|
+
const config = parseCapturedProject(agentEntry);
|
|
5510
|
+
if (config.datasets?.length) {
|
|
5511
|
+
try {
|
|
5512
|
+
await validateRuntimeDatasets(projectRoot, config.datasets);
|
|
5513
|
+
} catch (cause) {
|
|
5514
|
+
error({
|
|
5515
|
+
code: "PUSH_CONFIG_INVALID",
|
|
5516
|
+
path: BUDDY_AGENT_FILENAME,
|
|
5517
|
+
message: `\u65E0\u6CD5\u6309\u5DE5\u7A0B Runtime \u6821\u9A8C\u6570\u636E\u58F0\u660E\uFF1A${cause instanceof Error ? cause.message : String(cause)}`,
|
|
5518
|
+
cause
|
|
5519
|
+
});
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
const files = Object.freeze(manifest.files.map((file2) => Object.freeze({
|
|
5523
|
+
path: file2.path,
|
|
5524
|
+
bytes: file2.bytes,
|
|
5525
|
+
digest: file2.digest
|
|
5047
5526
|
})));
|
|
5048
5527
|
const warnings = [warning(
|
|
5049
5528
|
"PREFLIGHT_ONLY",
|
|
@@ -5079,16 +5558,16 @@ async function createPushPreflight(options) {
|
|
|
5079
5558
|
}
|
|
5080
5559
|
|
|
5081
5560
|
// packages/cli-core/src/push/bundle.ts
|
|
5082
|
-
import { createHash as
|
|
5083
|
-
import { lstat as
|
|
5084
|
-
import { join as
|
|
5561
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5562
|
+
import { lstat as lstat9, readFile as readFile8, realpath as realpath6 } from "node:fs/promises";
|
|
5563
|
+
import { join as join10, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
|
|
5085
5564
|
import { Writable } from "node:stream";
|
|
5086
5565
|
import { pipeline } from "node:stream/promises";
|
|
5087
5566
|
import { createGzip } from "node:zlib";
|
|
5088
5567
|
import { pack } from "tar-stream";
|
|
5089
5568
|
var MEDIA_TYPE = "application/gzip";
|
|
5090
5569
|
function digest(bytes) {
|
|
5091
|
-
return `sha256:${
|
|
5570
|
+
return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
|
|
5092
5571
|
}
|
|
5093
5572
|
function changed(path, message, cause) {
|
|
5094
5573
|
throw new PushPreflightError({
|
|
@@ -5102,42 +5581,42 @@ function inside(root, target) {
|
|
|
5102
5581
|
const path = relative3(root, target);
|
|
5103
5582
|
return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
|
|
5104
5583
|
}
|
|
5105
|
-
async function capture(root,
|
|
5106
|
-
const absolute =
|
|
5584
|
+
async function capture(root, file2) {
|
|
5585
|
+
const absolute = join10(root, ...file2.path.split("/"));
|
|
5107
5586
|
try {
|
|
5108
|
-
const entry = await
|
|
5109
|
-
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !==
|
|
5110
|
-
changed(
|
|
5587
|
+
const entry = await lstat9(absolute);
|
|
5588
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !== file2.bytes) {
|
|
5589
|
+
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5111
5590
|
}
|
|
5112
|
-
const canonical = await
|
|
5113
|
-
if (!inside(root, canonical)) changed(
|
|
5114
|
-
const contents = await
|
|
5115
|
-
if (contents.byteLength !==
|
|
5116
|
-
changed(
|
|
5591
|
+
const canonical = await realpath6(absolute);
|
|
5592
|
+
if (!inside(root, canonical)) changed(file2.path, `\u63D0\u4EA4\u6587\u4EF6\u8D8A\u8FC7\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${file2.path}`);
|
|
5593
|
+
const contents = await readFile8(absolute);
|
|
5594
|
+
if (contents.byteLength !== file2.bytes || digest(contents) !== file2.digest) {
|
|
5595
|
+
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5117
5596
|
}
|
|
5118
5597
|
return Object.freeze({
|
|
5119
|
-
path:
|
|
5120
|
-
bytes:
|
|
5121
|
-
digest:
|
|
5598
|
+
path: file2.path,
|
|
5599
|
+
bytes: file2.bytes,
|
|
5600
|
+
digest: file2.digest,
|
|
5122
5601
|
content: contents
|
|
5123
5602
|
});
|
|
5124
5603
|
} catch (cause) {
|
|
5125
5604
|
if (cause instanceof PushPreflightError) throw cause;
|
|
5126
|
-
changed(
|
|
5605
|
+
changed(file2.path, `\u63D0\u4EA4\u524D\u65E0\u6CD5\u518D\u6B21\u8BFB\u53D6\u6587\u4EF6\uFF1A${file2.path}`, cause);
|
|
5127
5606
|
}
|
|
5128
5607
|
}
|
|
5129
5608
|
async function createPushSourceBundle(plan) {
|
|
5130
|
-
const canonicalRoot = await
|
|
5609
|
+
const canonicalRoot = await realpath6(plan.projectRoot).catch((cause) => changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u76EE\u5F55\u5728\u6253\u5305\u524D\u5DF2\u4E0D\u53EF\u7528", cause));
|
|
5131
5610
|
if (canonicalRoot !== plan.projectRoot) {
|
|
5132
5611
|
changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u6839\u76EE\u5F55\u5728\u9884\u68C0\u540E\u53D1\u751F\u53D8\u5316");
|
|
5133
5612
|
}
|
|
5134
5613
|
const files = [];
|
|
5135
|
-
for (const
|
|
5614
|
+
for (const file2 of plan.files) files.push(await capture(canonicalRoot, file2));
|
|
5136
5615
|
const descriptor = JSON.stringify({
|
|
5137
5616
|
schemaVersion: "1",
|
|
5138
|
-
files: files.map((
|
|
5617
|
+
files: files.map((file2) => ({ path: file2.path, bytes: file2.bytes, digest: file2.digest }))
|
|
5139
5618
|
});
|
|
5140
|
-
const calculatedBundleDigest = `sha256:${
|
|
5619
|
+
const calculatedBundleDigest = `sha256:${createHash6("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
|
|
5141
5620
|
if (calculatedBundleDigest !== plan.bundleDigest) {
|
|
5142
5621
|
changed(plan.projectRoot, "\u63D0\u4EA4\u6E05\u5355\u8EAB\u4EFD\u5728\u6253\u5305\u524D\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C push");
|
|
5143
5622
|
}
|
|
@@ -5150,16 +5629,16 @@ async function createPushSourceBundle(plan) {
|
|
|
5150
5629
|
}
|
|
5151
5630
|
});
|
|
5152
5631
|
const completed = pipeline(archive, createGzip({ level: 9 }), output);
|
|
5153
|
-
for (const
|
|
5632
|
+
for (const file2 of files) {
|
|
5154
5633
|
await new Promise((resolveEntry, rejectEntry) => {
|
|
5155
5634
|
archive.entry({
|
|
5156
|
-
name:
|
|
5157
|
-
size:
|
|
5635
|
+
name: file2.path,
|
|
5636
|
+
size: file2.content.byteLength,
|
|
5158
5637
|
mode: 420,
|
|
5159
5638
|
uid: 0,
|
|
5160
5639
|
gid: 0,
|
|
5161
5640
|
mtime: /* @__PURE__ */ new Date(0)
|
|
5162
|
-
},
|
|
5641
|
+
}, file2.content, (cause) => cause ? rejectEntry(cause) : resolveEntry());
|
|
5163
5642
|
});
|
|
5164
5643
|
}
|
|
5165
5644
|
archive.finalize();
|
|
@@ -5197,7 +5676,9 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5197
5676
|
buddy-cli init --mode direct --directory <\u9879\u76EE\u76EE\u5F55> --name <\u540D\u79F0> --tagline <\u4E00\u53E5\u8BDD\u4ECB\u7ECD> --intro <\u5B8C\u6574\u4ECB\u7ECD> [--buddy-id <\u5DF2\u6709\u642D\u5B50 ID>]
|
|
5198
5677
|
buddy-cli dev [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5199
5678
|
buddy-cli login | logout | push | status
|
|
5200
|
-
buddy-cli models
|
|
5679
|
+
buddy-cli models [--json]
|
|
5680
|
+
buddy-cli widgets sync | check | verify
|
|
5681
|
+
buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5201
5682
|
buddy-cli preview [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5202
5683
|
buddy-cli help
|
|
5203
5684
|
|
|
@@ -5206,7 +5687,8 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5206
5687
|
dev \u542F\u52A8\u5B98\u65B9 Runtime \u5E76\u8FDE\u63A5 DEV App Server
|
|
5207
5688
|
login \u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u642D\u642D\u8D26\u53F7
|
|
5208
5689
|
logout \u9000\u51FA\u642D\u642D\u5F00\u53D1\u8005\u8D26\u53F7\u5E76\u6E05\u9664\u672C\u673A\u767B\u5F55\u72B6\u6001
|
|
5209
|
-
models \u67E5\
|
|
5690
|
+
models \u67E5\u770B CLI \u5185\u7F6E\u6A21\u578B ID\uFF08\u65E0\u9700\u767B\u5F55\u3001\u8054\u7F51\u6216 Agent \u5DE5\u7A0B\uFF09
|
|
5691
|
+
datasets \u8BFB\u53D6\u5DE5\u7A0B Runtime \u63D0\u4F9B\u7684\u6570\u636E\u96C6\u76EE\u5F55\uFF08\u65E0\u9700\u767B\u5F55\uFF09
|
|
5210
5692
|
push \u4E0A\u4F20 Agent \u5DE5\u7A0B\u548C commit\uFF0C\u63D0\u4EA4\u5E73\u53F0\u5BA1\u6838
|
|
5211
5693
|
status \u67E5\u8BE2\u642D\u5B50\u7684\u8D44\u6599\u3001\u4EE3\u7801\u3001\u8FD0\u884C\u4E0E\u53D1\u5E03\u72B6\u6001
|
|
5212
5694
|
preview \u6253\u5F00\u642D\u642D App \u79C1\u804A\u6A21\u62DF\u5668\u4E0E\u8C03\u8BD5\u9875\u9762
|
|
@@ -5220,7 +5702,7 @@ Coding Agent \u5E94\u76F4\u63A5\u6CBF\u7528\u7528\u6237\u660E\u786E\u63D0\u4F9B\
|
|
|
5220
5702
|
--buddy-id \u53EA\u7528\u4E8E\u6062\u590D\u5E73\u53F0\u5DF2\u7ECF\u751F\u6210\u4E14\u5C5E\u4E8E\u5F53\u524D\u8D26\u53F7\u7684\u642D\u5B50\u8EAB\u4EFD\uFF0C\u4E0D\u80FD\u81EA\u884C\u6307\u5B9A\u8EAB\u4EFD\u3002
|
|
5221
5703
|
--mode create \u5148\u767B\u8BB0\u642D\u5B50\u8EAB\u4EFD\uFF0C\u518D\u4E3A CLI \u5185\u7F6E\u7684 Creator Skill \u751F\u6210\u63A5\u7EED\u8BF4\u660E\uFF0C\u9879\u76EE .buddy/creator/ \u53EA\u4FDD\u5B58\u521B\u4F5C\u8D44\u6599\u3002
|
|
5222
5704
|
\u7531\u5F53\u524D Coding Agent \u8BFB\u53D6\u63A5\u7EED\u8BF4\u660E\u5E76\u6267\u884C Skill\uFF1B\u9700\u8981 Python 3.9+\uFF0C\u4E0D\u542F\u52A8\u65E7\u91C7\u8BBF Web \u6216\u72EC\u7ACB\u6A21\u578B\u3002
|
|
5223
|
-
\
|
|
5705
|
+
\u56DB\u518C\u786E\u8BA4\u540E\u7EE7\u7EED\u5C0F\u6302\u4EF6\u6E05\u5355\u3001\u539F\u578B\u548C\u8BBE\u8BA1\u9A8C\u6536\uFF1B\u5168\u90E8\u786E\u8BA4\u5E76\u901A\u8FC7\u4EA4\u4ED8\u6821\u9A8C\u540E\uFF0C\u7531 Coding Agent \u81EA\u52A8\u8854\u63A5\u5F00\u53D1\uFF0C\u9700\u8981\u521D\u59CB\u5316\u65F6\u6267\u884C init --mode direct \u751F\u6210\u5B98\u65B9 Runtime \u5DE5\u7A0B\u3002\u540C\u76EE\u5F55\u6062\u590D\u65F6\u4ECE buddy.agent.json \u8BFB\u53D6 buddyId\uFF0C\u5E76\u4F7F\u7528\u5E73\u53F0\u6700\u65B0 Meta\u3002
|
|
5224
5706
|
Coding Agent \u5E94\u4F20\u5165\u6309\u4E0A\u8FF0\u89C4\u5219\u786E\u5B9A\u7684\u8D44\u6599\uFF0C\u811A\u672C\u4F7F\u7528\u5F00\u53D1\u8005\u63D0\u4F9B\u7684\u53C2\u6570\uFF0C\u907F\u514D\u7B49\u5F85\u7EC8\u7AEF\u8F93\u5165\u3002CLI \u6821\u9A8C\u53C2\u6570\uFF0C\u4E0D\u5224\u65AD\u5BF9\u8BDD\u4E2D\u662F\u5426\u5DF2\u7ECF\u786E\u8BA4\uFF1B\u53C2\u6570\u9F50\u5168\u4E0D\u7B49\u4E8E\u7528\u6237\u786E\u8BA4\u3002
|
|
5225
5707
|
Coding Agent \u6839\u636E\u5DF2\u786E\u8BA4\u624B\u518C\u6216\u9700\u6C42\u63A8\u8FDB\u5B9E\u73B0\uFF1B\u53EA\u4E3A\u5173\u952E\u7F3A\u53E3\u8BE2\u95EE\uFF0C\u4E0D\u56FA\u5B9A\u8FFD\u52A0\u7B56\u7565\u786E\u8BA4\u3002
|
|
5226
5708
|
\u6240\u6709\u642D\u5B50\u7EDF\u4E00\u4F7F\u7528\u5B98\u65B9 Runtime\uFF1B\u9700\u6C42\u660E\u786E\u65F6\u4F7F\u7528 --mode direct\uFF0C\u9700\u8981\u68B3\u7406\u9700\u6C42\u65F6\u4F7F\u7528 --mode create\u3002
|
|
@@ -5414,7 +5896,7 @@ function defaultProjectDirectory(name) {
|
|
|
5414
5896
|
return `./${folder}`;
|
|
5415
5897
|
}
|
|
5416
5898
|
async function initializeProjectRoute(route, args, io, options, application) {
|
|
5417
|
-
const cwd =
|
|
5899
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5418
5900
|
const { name, tagline, intro } = application;
|
|
5419
5901
|
const directoryInput = await textAnswer(
|
|
5420
5902
|
io,
|
|
@@ -5423,7 +5905,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
5423
5905
|
"\u9879\u76EE\u76EE\u5F55\uFF08\u7A7A\u76EE\u5F55\u6216\u5DF2\u6709\u642D\u642D\u5DE5\u7A0B\uFF09",
|
|
5424
5906
|
defaultProjectDirectory(name)
|
|
5425
5907
|
);
|
|
5426
|
-
const rootDirectory =
|
|
5908
|
+
const rootDirectory = resolve15(cwd, directoryInput);
|
|
5427
5909
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
5428
5910
|
const initializationTarget = { directory: rootDirectory, kind: "new" };
|
|
5429
5911
|
const initializationPlan = options.initializeProject ? void 0 : await planBuddyProjectInitialization(initializationTarget);
|
|
@@ -5467,7 +5949,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
5467
5949
|
}
|
|
5468
5950
|
async function runRoute(route, args, io, options) {
|
|
5469
5951
|
if (!io.canPrompt) assertNonInteractiveProjectArguments(route, args);
|
|
5470
|
-
const cwd =
|
|
5952
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5471
5953
|
const name = await textAnswer(io, args.name, "--name", "\u642D\u5B50\u540D\u79F0", void 0, BUDDY_PROFILE_TEXT_LIMITS.name);
|
|
5472
5954
|
const tagline = await textAnswer(
|
|
5473
5955
|
io,
|
|
@@ -5495,7 +5977,7 @@ async function runRoute(route, args, io, options) {
|
|
|
5495
5977
|
"\u9879\u76EE\u76EE\u5F55",
|
|
5496
5978
|
defaultProjectDirectory(name)
|
|
5497
5979
|
);
|
|
5498
|
-
const rootDirectory =
|
|
5980
|
+
const rootDirectory = resolve15(cwd, directoryInput);
|
|
5499
5981
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
5500
5982
|
const plan = await planBuddyDraftInitialization(rootDirectory);
|
|
5501
5983
|
const existingBuddyId = plan.existingManifest?.buddyId;
|
|
@@ -5529,11 +6011,11 @@ async function runRoute(route, args, io, options) {
|
|
|
5529
6011
|
return { ...result, route: "create", draft, buddyId };
|
|
5530
6012
|
}
|
|
5531
6013
|
async function resumeProjectArguments(args, cwd, options) {
|
|
5532
|
-
const root =
|
|
6014
|
+
const root = resolve15(cwd, args.directory?.trim() || ".");
|
|
5533
6015
|
await assertInitializableBuddyTarget(root);
|
|
5534
6016
|
let entry;
|
|
5535
6017
|
try {
|
|
5536
|
-
entry = await
|
|
6018
|
+
entry = await lstat10(join11(root, "buddy.agent.json"));
|
|
5537
6019
|
} catch (cause) {
|
|
5538
6020
|
if (cause.code === "ENOENT") return;
|
|
5539
6021
|
throw cause;
|
|
@@ -5562,7 +6044,7 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5562
6044
|
try {
|
|
5563
6045
|
const [command, ...rest] = args;
|
|
5564
6046
|
if (!command) {
|
|
5565
|
-
const cwd =
|
|
6047
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5566
6048
|
let projectRoot;
|
|
5567
6049
|
try {
|
|
5568
6050
|
projectRoot = await findBuddyProjectRoot(cwd);
|
|
@@ -5590,16 +6072,21 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5590
6072
|
io.write(HELP2);
|
|
5591
6073
|
return { exitCode: 0 };
|
|
5592
6074
|
}
|
|
6075
|
+
if (command === "models") return runModelsCommand(rest, io);
|
|
6076
|
+
if (command === "widgets") return await runWidgetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
5593
6077
|
if (command === "preview") {
|
|
5594
6078
|
return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
|
|
5595
6079
|
}
|
|
6080
|
+
if (command === "datasets") {
|
|
6081
|
+
return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6082
|
+
}
|
|
5596
6083
|
if (command === "dev") {
|
|
5597
6084
|
return await runDevCommand(rest, io, {
|
|
5598
6085
|
cwd: options.cwd,
|
|
5599
6086
|
...options.dev
|
|
5600
6087
|
});
|
|
5601
6088
|
}
|
|
5602
|
-
if (command === "login" || command === "logout" || command === "
|
|
6089
|
+
if (command === "login" || command === "logout" || command === "push" || command === "status") {
|
|
5603
6090
|
return await runCloudCommand(command, rest, io, {
|
|
5604
6091
|
cwd: options.cwd,
|
|
5605
6092
|
...options.cloud
|
|
@@ -5613,11 +6100,11 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5613
6100
|
}
|
|
5614
6101
|
let route = routeFromMode(parsed.mode);
|
|
5615
6102
|
if (route === void 0 && !io.canPrompt) requireModeForNonInteractive();
|
|
5616
|
-
const cwd =
|
|
6103
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5617
6104
|
await assertInitializableBuddyTarget(cwd);
|
|
5618
6105
|
if (parsed.directory !== void 0) {
|
|
5619
6106
|
if (!parsed.directory.trim()) throw new CliUsageError("--directory \u4E0D\u80FD\u4E3A\u7A7A");
|
|
5620
|
-
await assertInitializableBuddyTarget(
|
|
6107
|
+
await assertInitializableBuddyTarget(resolve15(cwd, parsed.directory));
|
|
5621
6108
|
}
|
|
5622
6109
|
await authenticate(options);
|
|
5623
6110
|
await resumeProjectArguments(parsed, cwd, options);
|
|
@@ -5759,6 +6246,7 @@ export {
|
|
|
5759
6246
|
assertOutsideBuddyProject,
|
|
5760
6247
|
buddyAgentConfig,
|
|
5761
6248
|
buildAgentEnvironment,
|
|
6249
|
+
checkWidgetDelivery,
|
|
5762
6250
|
combineBuddyProject,
|
|
5763
6251
|
createBuddyConfig,
|
|
5764
6252
|
createBuddyDevStarter,
|
|
@@ -5796,6 +6284,7 @@ export {
|
|
|
5796
6284
|
throwIfDevAborted,
|
|
5797
6285
|
validateBuddyAgentConfig,
|
|
5798
6286
|
validateBuddyConfig,
|
|
5799
|
-
validateBuddyProjectManifest
|
|
6287
|
+
validateBuddyProjectManifest,
|
|
6288
|
+
widgetDeliverySummary
|
|
5800
6289
|
};
|
|
5801
6290
|
//# sourceMappingURL=core.js.map
|