@my-life-buddies/cli 0.3.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 +47 -31
- package/dist/bin/core.js +901 -432
- 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/agent-template/package-lock.json +4 -4
- package/resources/agent-template/package.json +1 -1
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",
|
|
@@ -109,21 +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.
|
|
122
155
|
* @type {import("@my-life-buddies/buddy-runtime").BuddyFactory}
|
|
123
156
|
*/
|
|
124
157
|
export default function createBuddyOptions(_conversation) {
|
|
125
158
|
return {
|
|
126
159
|
initialState: {
|
|
160
|
+
model,
|
|
127
161
|
systemPrompt,
|
|
128
162
|
thinkingLevel: "off",
|
|
129
163
|
tools: [],
|
|
@@ -145,15 +179,16 @@ if (!config || config.schemaVersion !== 1 || typeof config.buddyId !== "string"
|
|
|
145
179
|
if (process.env.MLB_BUDDY_ID && process.env.MLB_BUDDY_ID !== config.buddyId) {
|
|
146
180
|
throw new Error("MLB_BUDDY_ID does not match buddy.agent.json");
|
|
147
181
|
}
|
|
148
|
-
const model = typeof config.model === "string" && config.model ? config.model : "kimi-k2.6";
|
|
149
|
-
|
|
150
182
|
const lifecycle = new AbortController();
|
|
151
183
|
const stop = () => lifecycle.abort();
|
|
152
184
|
process.on("SIGINT", stop);
|
|
153
185
|
process.on("SIGTERM", stop);
|
|
154
186
|
let runtime;
|
|
155
187
|
try {
|
|
156
|
-
runtime = await BuddyServer.start({
|
|
188
|
+
runtime = await BuddyServer.start({
|
|
189
|
+
dataRequirements: config.datasets ?? [],
|
|
190
|
+
buddy: createBuddyOptions,
|
|
191
|
+
});
|
|
157
192
|
if (!lifecycle.signal.aborted) {
|
|
158
193
|
await new Promise((resolve) => lifecycle.signal.addEventListener("abort", resolve, { once: true }));
|
|
159
194
|
}
|
|
@@ -204,19 +239,19 @@ async function scaffoldPlatformAgent(projectRoot, name) {
|
|
|
204
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}`);
|
|
205
240
|
}
|
|
206
241
|
}
|
|
207
|
-
for (const
|
|
242
|
+
for (const file2 of files) {
|
|
208
243
|
try {
|
|
209
|
-
handles.push({ path:
|
|
244
|
+
handles.push({ path: file2.path, handle: await open(file2.path, "wx", 384) });
|
|
210
245
|
} catch (cause) {
|
|
211
246
|
if (cause.code !== "EEXIST") throw cause;
|
|
212
|
-
if (!(await lstat(
|
|
213
|
-
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}`);
|
|
214
249
|
}
|
|
215
250
|
}
|
|
216
251
|
}
|
|
217
252
|
for (const { path: filePath, handle } of handles) {
|
|
218
|
-
const
|
|
219
|
-
await handle.writeFile(
|
|
253
|
+
const file2 = files.find((candidate) => candidate.path === filePath);
|
|
254
|
+
await handle.writeFile(file2.content, "utf8");
|
|
220
255
|
await handle.sync();
|
|
221
256
|
}
|
|
222
257
|
return Object.freeze({ path, created: handles.length > 0 || createdDirectories.length > 0 });
|
|
@@ -312,6 +347,9 @@ function validateBuddyProjectManifest(value) {
|
|
|
312
347
|
return { ok: false, issues: schemaIssues(validateAgentSchema.errors) };
|
|
313
348
|
}
|
|
314
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
|
+
}
|
|
315
353
|
const seen = /* @__PURE__ */ new Set();
|
|
316
354
|
for (const [index, dataset] of (manifest.datasets ?? []).entries()) {
|
|
317
355
|
if (seen.has(dataset.id)) {
|
|
@@ -754,10 +792,597 @@ async function readBuddyConfig(projectRoot) {
|
|
|
754
792
|
return combineBuddyProject(await readBuddyAgentConfig(rootDirectory));
|
|
755
793
|
}
|
|
756
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
|
+
|
|
757
1382
|
// packages/cli-core/src/init/creator.ts
|
|
758
1383
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
759
|
-
import { link as link2, lstat as
|
|
760
|
-
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";
|
|
761
1386
|
import { fileURLToPath } from "node:url";
|
|
762
1387
|
|
|
763
1388
|
// packages/cli-core/src/init/development-handoff.ts
|
|
@@ -767,7 +1392,7 @@ var DEVELOPMENT_HANDOFF = `## \u5F00\u53D1\u5B8C\u6210\u540E\u4EA4\u7ED9\u5F00\u
|
|
|
767
1392
|
${DEVELOPMENT_HANDOFF_SUMMARY}
|
|
768
1393
|
|
|
769
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
|
|
770
|
-
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
|
|
771
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
|
|
772
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
|
|
773
1398
|
|
|
@@ -799,13 +1424,13 @@ Creator \u8DEF\u7EBF\u987B\u7B49\u56DB\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355
|
|
|
799
1424
|
| --- | --- |
|
|
800
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 |
|
|
801
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 |
|
|
802
|
-
| \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 \`
|
|
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 |
|
|
803
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 |
|
|
804
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 |
|
|
805
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 |
|
|
806
|
-
| \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 |
|
|
807
1432
|
|
|
808
|
-
\`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\
|
|
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
|
|
809
1434
|
|
|
810
1435
|
### \u6309\u76EE\u5F55\u804C\u8D23\u7EC4\u7EC7\u5B9E\u73B0
|
|
811
1436
|
|
|
@@ -817,7 +1442,7 @@ Creator \u8DEF\u7EBF\u987B\u7B49\u56DB\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355
|
|
|
817
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 |
|
|
818
1443
|
| \`src/buddy-options.mjs\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u7EC4\u88C5\u5E76\u6CE8\u518C\u4F1A\u8BDD\u4E2D\u7684\u80FD\u529B |
|
|
819
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 |
|
|
820
|
-
| \`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 |
|
|
821
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 |
|
|
822
1447
|
| \`resources/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E Runtime \u542F\u52A8\u65F6\u8BFB\u53D6\u7684 Markdown \u53C2\u8003\u8D44\u6599 |
|
|
823
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 |
|
|
@@ -828,14 +1453,22 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
|
|
|
828
1453
|
|
|
829
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
|
|
830
1455
|
|
|
831
|
-
\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\
|
|
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
|
|
832
1457
|
|
|
833
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
|
|
834
1459
|
|
|
835
1460
|
|
|
836
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
|
|
837
1462
|
|
|
838
|
-
\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
|
|
839
1472
|
|
|
840
1473
|
### \u53EA\u6709\u5173\u952E\u7F3A\u53E3\u624D\u8865\u95EE
|
|
841
1474
|
|
|
@@ -845,6 +1478,18 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
|
|
|
845
1478
|
|
|
846
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
|
|
847
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
|
+
|
|
848
1493
|
### \u6838\u5BF9\u5B9E\u73B0\uFF0C\u518D\u4EA4\u7ED9\u5F00\u53D1\u8005\u4F53\u9A8C
|
|
849
1494
|
|
|
850
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
|
|
@@ -895,16 +1540,16 @@ var REQUIRED = [
|
|
|
895
1540
|
var CreatorPreparationError = class extends Error {
|
|
896
1541
|
};
|
|
897
1542
|
async function state(path) {
|
|
898
|
-
return
|
|
1543
|
+
return lstat6(path).catch((cause) => {
|
|
899
1544
|
if (cause.code === "ENOENT") return void 0;
|
|
900
1545
|
throw cause;
|
|
901
1546
|
});
|
|
902
1547
|
}
|
|
903
1548
|
async function safeDirectory(path) {
|
|
904
|
-
await
|
|
1549
|
+
await mkdir4(path, { mode: 448 }).catch((cause) => {
|
|
905
1550
|
if (cause.code !== "EEXIST") throw cause;
|
|
906
1551
|
});
|
|
907
|
-
if (!(await
|
|
1552
|
+
if (!(await lstat6(path)).isDirectory()) {
|
|
908
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}`);
|
|
909
1554
|
}
|
|
910
1555
|
}
|
|
@@ -914,11 +1559,11 @@ async function safeRead(path) {
|
|
|
914
1559
|
if (!entry.isFile() || entry.size > MAX_LOCAL_FILE_BYTES) {
|
|
915
1560
|
throw new CreatorPreparationError(`\u521B\u4F5C\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6\u6216\u8D85\u51FA\u5927\u5C0F\u9650\u5236\uFF1A${path}`);
|
|
916
1561
|
}
|
|
917
|
-
return
|
|
1562
|
+
return readFile5(path);
|
|
918
1563
|
}
|
|
919
1564
|
async function writeOnce(path, bytes) {
|
|
920
1565
|
const temporary = `${path}.${randomUUID2()}.tmp`;
|
|
921
|
-
await
|
|
1566
|
+
await writeFile2(temporary, bytes, { flag: "wx", mode: 384 });
|
|
922
1567
|
try {
|
|
923
1568
|
await link2(temporary, path).catch((cause) => {
|
|
924
1569
|
if (cause.code !== "EEXIST") throw cause;
|
|
@@ -929,17 +1574,17 @@ async function writeOnce(path, bytes) {
|
|
|
929
1574
|
}
|
|
930
1575
|
async function bundledSkill(root) {
|
|
931
1576
|
for (const path of REQUIRED) {
|
|
932
|
-
if (!(await state(
|
|
1577
|
+
if (!(await state(join5(root, path)))?.isFile()) {
|
|
933
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`);
|
|
934
1579
|
}
|
|
935
1580
|
}
|
|
936
1581
|
return root;
|
|
937
1582
|
}
|
|
938
1583
|
function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
939
|
-
const contextPath =
|
|
1584
|
+
const contextPath = join5(context.projectRoot, ".buddy", "creator", "initial-context.json");
|
|
940
1585
|
const openArgs = [
|
|
941
1586
|
"-B",
|
|
942
|
-
|
|
1587
|
+
join5(skillRoot, "scripts/buddy.py"),
|
|
943
1588
|
"open",
|
|
944
1589
|
"--workspace",
|
|
945
1590
|
workspacePath,
|
|
@@ -969,7 +1614,7 @@ function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
|
969
1614
|
|
|
970
1615
|
## \u73B0\u5728\u7531 Coding Agent \u6267\u884C
|
|
971
1616
|
|
|
972
|
-
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
|
|
973
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
|
|
974
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
|
|
975
1620
|
|
|
@@ -1006,11 +1651,11 @@ ${DEVELOPMENT_HANDOFF}
|
|
|
1006
1651
|
`;
|
|
1007
1652
|
}
|
|
1008
1653
|
async function prepareCreatorSkill(context, io, options = {}) {
|
|
1009
|
-
const root =
|
|
1654
|
+
const root = join5(context.projectRoot, ".buddy", "creator");
|
|
1010
1655
|
try {
|
|
1011
|
-
await safeDirectory(
|
|
1656
|
+
await safeDirectory(join5(context.projectRoot, ".buddy"));
|
|
1012
1657
|
await safeDirectory(root);
|
|
1013
|
-
const sessionPath =
|
|
1658
|
+
const sessionPath = join5(root, "session.json");
|
|
1014
1659
|
await writeOnce(sessionPath, JSON.stringify({
|
|
1015
1660
|
schemaVersion: 1,
|
|
1016
1661
|
platformBuddyId: context.buddyId,
|
|
@@ -1022,11 +1667,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1022
1667
|
}
|
|
1023
1668
|
options.signal?.throwIfAborted();
|
|
1024
1669
|
const skillRoot = await bundledSkill(options.bundledSkillRoot ?? BUNDLED_CREATOR_SKILL_ROOT);
|
|
1025
|
-
await safeDirectory(
|
|
1026
|
-
const workspacePath =
|
|
1670
|
+
await safeDirectory(join5(root, "workspaces"));
|
|
1671
|
+
const workspacePath = join5(root, "workspaces", session.creationKey);
|
|
1027
1672
|
const workspace = await state(workspacePath);
|
|
1028
1673
|
if (workspace && !workspace.isDirectory()) throw new CreatorPreparationError("\u5DF2\u6709\u521B\u4F5C\u5DE5\u4F5C\u533A\u4E0D\u662F\u666E\u901A\u76EE\u5F55\u3002");
|
|
1029
|
-
const contextPath =
|
|
1674
|
+
const contextPath = join5(root, "initial-context.json");
|
|
1030
1675
|
await writeOnce(contextPath, JSON.stringify({
|
|
1031
1676
|
source: "platform_meta",
|
|
1032
1677
|
name: context.appName,
|
|
@@ -1042,7 +1687,7 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1042
1687
|
} catch {
|
|
1043
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}`);
|
|
1044
1689
|
}
|
|
1045
|
-
const handoffPath =
|
|
1690
|
+
const handoffPath = join5(root, "HANDOFF.md");
|
|
1046
1691
|
const handoff = handoffMarkdown({
|
|
1047
1692
|
...context,
|
|
1048
1693
|
appName: initial.name,
|
|
@@ -1053,11 +1698,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1053
1698
|
if ((await safeRead(handoffPath))?.toString("utf8") !== handoff) {
|
|
1054
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}`);
|
|
1055
1700
|
}
|
|
1056
|
-
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")}`);
|
|
1057
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`);
|
|
1058
1703
|
io.write(DEVELOPMENT_HANDOFF_SUMMARY);
|
|
1059
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");
|
|
1060
|
-
return { exitCode: 0, creator: { skillPath:
|
|
1705
|
+
return { exitCode: 0, creator: { skillPath: join5(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
|
|
1061
1706
|
} catch (cause) {
|
|
1062
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";
|
|
1063
1708
|
io.writeError(`Creator Skill \u51C6\u5907\u5931\u8D25\uFF1A${message}`);
|
|
@@ -1111,7 +1756,7 @@ function validateBuddyMeta(meta2) {
|
|
|
1111
1756
|
}
|
|
1112
1757
|
|
|
1113
1758
|
// packages/cli-core/src/commands/dev.ts
|
|
1114
|
-
import { resolve as
|
|
1759
|
+
import { resolve as resolve11 } from "node:path";
|
|
1115
1760
|
|
|
1116
1761
|
// packages/cli-core/src/platform-url.ts
|
|
1117
1762
|
var DEFAULT_MLB_TEST_URL = "http://47.116.168.81:8788";
|
|
@@ -1172,7 +1817,7 @@ function safeCredential(value, name) {
|
|
|
1172
1817
|
function endpoint(baseUrl, path) {
|
|
1173
1818
|
return new URL(path.replace(/^\//u, ""), baseUrl);
|
|
1174
1819
|
}
|
|
1175
|
-
function
|
|
1820
|
+
function object2(value) {
|
|
1176
1821
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1177
1822
|
}
|
|
1178
1823
|
async function responseJson(response) {
|
|
@@ -1214,7 +1859,7 @@ async function responseJson(response) {
|
|
|
1214
1859
|
}
|
|
1215
1860
|
}
|
|
1216
1861
|
function safeServerError(value, fallback) {
|
|
1217
|
-
const candidate =
|
|
1862
|
+
const candidate = object2(value)?.error;
|
|
1218
1863
|
if (typeof candidate !== "string" || !candidate.trim()) return fallback;
|
|
1219
1864
|
return candidate.slice(0, 1024).replace(/[\u0000-\u001f\u007f]/gu, " ");
|
|
1220
1865
|
}
|
|
@@ -1295,7 +1940,7 @@ var MlbDevRegistrationClient = class {
|
|
|
1295
1940
|
message: "buddy.agent.json \u4E2D\u7684 buddyId \u65E0\u6548\u3002"
|
|
1296
1941
|
});
|
|
1297
1942
|
}
|
|
1298
|
-
const body =
|
|
1943
|
+
const body = object2(await this.#request(
|
|
1299
1944
|
`/cli/buddies/${encodeURIComponent(request.devAgentId)}`,
|
|
1300
1945
|
{
|
|
1301
1946
|
method: "GET",
|
|
@@ -1316,7 +1961,7 @@ var MlbDevRegistrationClient = class {
|
|
|
1316
1961
|
});
|
|
1317
1962
|
}
|
|
1318
1963
|
async listOnline(options) {
|
|
1319
|
-
const body =
|
|
1964
|
+
const body = object2(await this.#request("/cli/fleet", {
|
|
1320
1965
|
method: "GET",
|
|
1321
1966
|
signal: options.signal
|
|
1322
1967
|
}));
|
|
@@ -1408,36 +2053,24 @@ function buildAgentEnvironment(options) {
|
|
|
1408
2053
|
}
|
|
1409
2054
|
|
|
1410
2055
|
// packages/cli-core/src/dev/orchestrator.ts
|
|
1411
|
-
import { createHash as
|
|
1412
|
-
import { readFile as
|
|
1413
|
-
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";
|
|
1414
2059
|
|
|
1415
2060
|
// packages/cli-core/src/dev/launch.ts
|
|
1416
2061
|
import { lstatSync, readFileSync } from "node:fs";
|
|
1417
|
-
import { createRequire } from "node:module";
|
|
1418
|
-
import { dirname, resolve as
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
var BUDDY_RUNTIME_PACKAGE = "@my-life-buddies/buddy-runtime";
|
|
1422
|
-
function runtimeDependencyVersion(pkg) {
|
|
1423
|
-
if (typeof pkg !== "object" || pkg === null || !("dependencies" in pkg)) return void 0;
|
|
1424
|
-
const dependencies2 = pkg.dependencies;
|
|
1425
|
-
if (typeof dependencies2 !== "object" || dependencies2 === null || !(BUDDY_RUNTIME_PACKAGE in dependencies2)) return void 0;
|
|
1426
|
-
const version = dependencies2[BUDDY_RUNTIME_PACKAGE];
|
|
1427
|
-
return typeof version === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version) ? version : void 0;
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
// packages/cli-core/src/dev/launch.ts
|
|
1431
|
-
function officialAgentLaunchPlan(input) {
|
|
1432
|
-
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);
|
|
1433
2066
|
for (const [filename, kind] of [
|
|
1434
|
-
[
|
|
2067
|
+
[dirname2(BUDDY_PLATFORM_FACTORY_ENTRY), "directory"],
|
|
1435
2068
|
[BUDDY_PLATFORM_START_ENTRY, "file"],
|
|
1436
2069
|
[BUDDY_PLATFORM_FACTORY_ENTRY, "file"],
|
|
1437
2070
|
[BUDDY_PLATFORM_PACKAGE_JSON, "file"]
|
|
1438
2071
|
]) {
|
|
1439
2072
|
try {
|
|
1440
|
-
const entry2 = lstatSync(
|
|
2073
|
+
const entry2 = lstatSync(resolve8(input.projectRoot, filename));
|
|
1441
2074
|
if (!(kind === "directory" ? entry2.isDirectory() : entry2.isFile())) {
|
|
1442
2075
|
throw new Error(`${filename} must be a regular ${kind}`);
|
|
1443
2076
|
}
|
|
@@ -1450,18 +2083,19 @@ function officialAgentLaunchPlan(input) {
|
|
|
1450
2083
|
});
|
|
1451
2084
|
}
|
|
1452
2085
|
}
|
|
1453
|
-
const packagePath =
|
|
2086
|
+
const packagePath = resolve8(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
|
|
1454
2087
|
try {
|
|
1455
2088
|
const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
|
|
1456
2089
|
if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
1457
|
-
const runtimePath =
|
|
2090
|
+
const runtimePath = createRequire2(packagePath).resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`);
|
|
1458
2091
|
const installed = JSON.parse(readFileSync(runtimePath, "utf8")).version;
|
|
1459
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);
|
|
1460
2094
|
} catch (cause) {
|
|
1461
2095
|
throw new BuddyDevError({
|
|
1462
2096
|
code: "DEV_CONFIG_INVALID",
|
|
1463
2097
|
phase: "config",
|
|
1464
|
-
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)}`,
|
|
1465
2099
|
cause
|
|
1466
2100
|
});
|
|
1467
2101
|
}
|
|
@@ -1499,16 +2133,16 @@ var NodeAgentProcessLauncher = class {
|
|
|
1499
2133
|
}
|
|
1500
2134
|
let hasExited = false;
|
|
1501
2135
|
let spawned = false;
|
|
1502
|
-
const exit = new Promise((
|
|
2136
|
+
const exit = new Promise((resolve16) => {
|
|
1503
2137
|
child.once("error", (error2) => {
|
|
1504
2138
|
if (!spawned) {
|
|
1505
2139
|
hasExited = true;
|
|
1506
|
-
|
|
2140
|
+
resolve16({ code: null, signal: null, error: error2 });
|
|
1507
2141
|
}
|
|
1508
2142
|
});
|
|
1509
2143
|
child.once("exit", (code2, signal) => {
|
|
1510
2144
|
hasExited = true;
|
|
1511
|
-
|
|
2145
|
+
resolve16({ code: code2, signal });
|
|
1512
2146
|
});
|
|
1513
2147
|
});
|
|
1514
2148
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -1524,11 +2158,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
1524
2158
|
}
|
|
1525
2159
|
});
|
|
1526
2160
|
try {
|
|
1527
|
-
await new Promise((
|
|
2161
|
+
await new Promise((resolve16, reject) => {
|
|
1528
2162
|
const onSpawn = () => {
|
|
1529
2163
|
spawned = true;
|
|
1530
2164
|
child.off("error", onError);
|
|
1531
|
-
|
|
2165
|
+
resolve16();
|
|
1532
2166
|
};
|
|
1533
2167
|
const onError = (error2) => {
|
|
1534
2168
|
child.off("spawn", onSpawn);
|
|
@@ -1567,11 +2201,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
1567
2201
|
};
|
|
1568
2202
|
function waitForExit(process2, timeoutMs) {
|
|
1569
2203
|
if (process2.hasExited()) return Promise.resolve(true);
|
|
1570
|
-
return new Promise((
|
|
1571
|
-
const timeout = setTimeout(() =>
|
|
2204
|
+
return new Promise((resolve16) => {
|
|
2205
|
+
const timeout = setTimeout(() => resolve16(false), timeoutMs);
|
|
1572
2206
|
process2.exit.then(() => {
|
|
1573
2207
|
clearTimeout(timeout);
|
|
1574
|
-
|
|
2208
|
+
resolve16(true);
|
|
1575
2209
|
});
|
|
1576
2210
|
});
|
|
1577
2211
|
}
|
|
@@ -1654,7 +2288,7 @@ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
|
|
|
1654
2288
|
while (isGroupAlive(pid)) {
|
|
1655
2289
|
const remaining = deadline - Date.now();
|
|
1656
2290
|
if (remaining <= 0) return false;
|
|
1657
|
-
await new Promise((
|
|
2291
|
+
await new Promise((resolve16) => setTimeout(resolve16, Math.min(25, remaining)));
|
|
1658
2292
|
}
|
|
1659
2293
|
return true;
|
|
1660
2294
|
}
|
|
@@ -1668,10 +2302,10 @@ function processTreeControllerFor(platform) {
|
|
|
1668
2302
|
}
|
|
1669
2303
|
|
|
1670
2304
|
// packages/cli-core/src/dev/instance-lock.ts
|
|
1671
|
-
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
1672
|
-
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";
|
|
1673
2307
|
import { homedir } from "node:os";
|
|
1674
|
-
import { join as
|
|
2308
|
+
import { join as join6 } from "node:path";
|
|
1675
2309
|
function alreadyRunningError() {
|
|
1676
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";
|
|
1677
2311
|
return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
|
|
@@ -1691,20 +2325,20 @@ function code(error2) {
|
|
|
1691
2325
|
return error2?.code;
|
|
1692
2326
|
}
|
|
1693
2327
|
async function acquireDevInstanceLock(options) {
|
|
1694
|
-
const root = options.directory ??
|
|
1695
|
-
const key =
|
|
2328
|
+
const root = options.directory ?? join6(homedir(), ".buddy", "dev-locks");
|
|
2329
|
+
const key = createHash2("sha256").update(`${new URL(options.gatewayUrl).origin}
|
|
1696
2330
|
${options.devAgentId}`).digest("hex");
|
|
1697
|
-
const path =
|
|
2331
|
+
const path = join6(root, key);
|
|
1698
2332
|
const ownerName = `owner-${randomUUID3()}.json`;
|
|
1699
|
-
const candidate =
|
|
2333
|
+
const candidate = join6(root, `.pending-${randomUUID3()}`);
|
|
1700
2334
|
const owner = { pid: process.pid };
|
|
1701
|
-
await
|
|
1702
|
-
await
|
|
1703
|
-
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" });
|
|
1704
2338
|
try {
|
|
1705
2339
|
for (let attempt = 0; ; attempt++) {
|
|
1706
2340
|
try {
|
|
1707
|
-
await
|
|
2341
|
+
await rename3(candidate, path);
|
|
1708
2342
|
break;
|
|
1709
2343
|
} catch (error2) {
|
|
1710
2344
|
if (!["EEXIST", "ENOTEMPTY"].includes(code(error2) ?? "")) throw error2;
|
|
@@ -1712,17 +2346,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1712
2346
|
}
|
|
1713
2347
|
let names;
|
|
1714
2348
|
try {
|
|
1715
|
-
names = await
|
|
2349
|
+
names = await readdir3(path);
|
|
1716
2350
|
} catch (error2) {
|
|
1717
2351
|
if (code(error2) === "ENOENT") continue;
|
|
1718
2352
|
throw error2;
|
|
1719
2353
|
}
|
|
1720
2354
|
if (names.length === 0) continue;
|
|
1721
2355
|
if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw alreadyRunningError();
|
|
1722
|
-
const previousPath =
|
|
2356
|
+
const previousPath = join6(path, names[0]);
|
|
1723
2357
|
let previous;
|
|
1724
2358
|
try {
|
|
1725
|
-
previous = JSON.parse(await
|
|
2359
|
+
previous = JSON.parse(await readFile6(previousPath, "utf8"));
|
|
1726
2360
|
} catch (error2) {
|
|
1727
2361
|
if (code(error2) === "ENOENT") continue;
|
|
1728
2362
|
throw alreadyRunningError();
|
|
@@ -1739,17 +2373,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1739
2373
|
});
|
|
1740
2374
|
}
|
|
1741
2375
|
} finally {
|
|
1742
|
-
await unlink4(
|
|
2376
|
+
await unlink4(join6(candidate, ownerName)).catch(() => void 0);
|
|
1743
2377
|
await rmdir2(candidate).catch(() => void 0);
|
|
1744
2378
|
}
|
|
1745
2379
|
let released = false;
|
|
1746
2380
|
return {
|
|
1747
2381
|
async attachRuntime(pid) {
|
|
1748
2382
|
if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
|
|
1749
|
-
const temporary =
|
|
2383
|
+
const temporary = join6(root, `.update-${randomUUID3()}`);
|
|
1750
2384
|
try {
|
|
1751
|
-
await
|
|
1752
|
-
await
|
|
2385
|
+
await writeFile3(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
|
|
2386
|
+
await rename3(temporary, join6(path, ownerName));
|
|
1753
2387
|
} finally {
|
|
1754
2388
|
await unlink4(temporary).catch(() => void 0);
|
|
1755
2389
|
}
|
|
@@ -1757,7 +2391,7 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1757
2391
|
async release() {
|
|
1758
2392
|
if (released) return;
|
|
1759
2393
|
released = true;
|
|
1760
|
-
await unlink4(
|
|
2394
|
+
await unlink4(join6(path, ownerName)).catch((error2) => {
|
|
1761
2395
|
if (code(error2) !== "ENOENT") throw error2;
|
|
1762
2396
|
});
|
|
1763
2397
|
await rmdir2(path).catch((error2) => {
|
|
@@ -1809,6 +2443,7 @@ function validateLaunchPlan(value) {
|
|
|
1809
2443
|
}
|
|
1810
2444
|
async function launchPlanFor(config, options, projectRoot, signal) {
|
|
1811
2445
|
const provider = options.platformLaunchPlan ?? ((context) => officialAgentLaunchPlan({
|
|
2446
|
+
datasets: config.datasets,
|
|
1812
2447
|
projectRoot: context.projectRoot
|
|
1813
2448
|
}));
|
|
1814
2449
|
try {
|
|
@@ -1999,7 +2634,7 @@ async function startBuddyDev(options) {
|
|
|
1999
2634
|
const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS, "pollIntervalMs");
|
|
2000
2635
|
const stopGracePeriodMs = positiveDuration(options.stopGracePeriodMs ?? DEFAULT_STOP_GRACE_PERIOD_MS, "stopGracePeriodMs");
|
|
2001
2636
|
const devAgentId = safeDevAgentId(options.devAgentId);
|
|
2002
|
-
const projectRoot =
|
|
2637
|
+
const projectRoot = resolve9(options.projectRoot);
|
|
2003
2638
|
const lifecycle = new AbortController();
|
|
2004
2639
|
const signal = options.signal === void 0 ? lifecycle.signal : AbortSignal.any([lifecycle.signal, options.signal]);
|
|
2005
2640
|
let agentProcess;
|
|
@@ -2017,7 +2652,7 @@ async function startBuddyDev(options) {
|
|
|
2017
2652
|
let config;
|
|
2018
2653
|
let configSource;
|
|
2019
2654
|
try {
|
|
2020
|
-
configSource = await
|
|
2655
|
+
configSource = await readFile7(join7(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
|
|
2021
2656
|
config = combineBuddyProject(parseBuddyAgentConfig(configSource));
|
|
2022
2657
|
} catch (cause) {
|
|
2023
2658
|
throw new BuddyDevError({
|
|
@@ -2027,12 +2662,14 @@ async function startBuddyDev(options) {
|
|
|
2027
2662
|
cause
|
|
2028
2663
|
});
|
|
2029
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" });
|
|
2030
2667
|
const launchPlan = await launchPlanFor(config, options, projectRoot, signal);
|
|
2031
2668
|
const registration = await registerDevAgent({
|
|
2032
2669
|
client: options.registrationClient,
|
|
2033
2670
|
devAgentId,
|
|
2034
2671
|
configSource,
|
|
2035
|
-
configDigest: `sha256:${
|
|
2672
|
+
configDigest: `sha256:${createHash3("sha256").update(configSource).digest("hex")}`,
|
|
2036
2673
|
signal
|
|
2037
2674
|
});
|
|
2038
2675
|
throwIfDevAborted(signal);
|
|
@@ -2062,8 +2699,8 @@ async function startBuddyDev(options) {
|
|
|
2062
2699
|
shell: false,
|
|
2063
2700
|
detached: platform === "darwin",
|
|
2064
2701
|
...options.onLog === void 0 ? {} : {
|
|
2065
|
-
onStdout: (
|
|
2066
|
-
onStderr: (
|
|
2702
|
+
onStdout: (text2) => options.onLog?.({ source: "agent.stdout", text: text2 }),
|
|
2703
|
+
onStderr: (text2) => options.onLog?.({ source: "agent.stderr", text: text2 })
|
|
2067
2704
|
}
|
|
2068
2705
|
});
|
|
2069
2706
|
await instanceLock.attachRuntime(agentProcess.pid);
|
|
@@ -2119,7 +2756,7 @@ async function startBuddyDev(options) {
|
|
|
2119
2756
|
}
|
|
2120
2757
|
|
|
2121
2758
|
// packages/cli-core/src/dev/starter.ts
|
|
2122
|
-
import { resolve as
|
|
2759
|
+
import { resolve as resolve10 } from "node:path";
|
|
2123
2760
|
function createBuddyDevStarter(options = {}) {
|
|
2124
2761
|
const readManifest = options.readManifest ?? (async (projectRoot) => {
|
|
2125
2762
|
const manifest = await readBuddyProjectManifest(projectRoot);
|
|
@@ -2128,7 +2765,7 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2128
2765
|
const startDev = options.startDev ?? startBuddyDev;
|
|
2129
2766
|
return Object.freeze({
|
|
2130
2767
|
async start(input) {
|
|
2131
|
-
const projectRoot =
|
|
2768
|
+
const projectRoot = resolve10(input.projectRoot);
|
|
2132
2769
|
const manifest = await readManifest(projectRoot);
|
|
2133
2770
|
return startDev({
|
|
2134
2771
|
...input,
|
|
@@ -2139,175 +2776,6 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2139
2776
|
});
|
|
2140
2777
|
}
|
|
2141
2778
|
|
|
2142
|
-
// packages/cli-core/src/project-context.ts
|
|
2143
|
-
import { lstat as lstat5, realpath } from "node:fs/promises";
|
|
2144
|
-
import { dirname as dirname2, join as join6, resolve as resolve6 } from "node:path";
|
|
2145
|
-
var BuddyProjectContextError = class extends Error {
|
|
2146
|
-
constructor(code2, message, targetPath, options) {
|
|
2147
|
-
super(message, options);
|
|
2148
|
-
this.code = code2;
|
|
2149
|
-
this.targetPath = targetPath;
|
|
2150
|
-
this.name = "BuddyProjectContextError";
|
|
2151
|
-
}
|
|
2152
|
-
code;
|
|
2153
|
-
targetPath;
|
|
2154
|
-
};
|
|
2155
|
-
async function existingDirectory(target) {
|
|
2156
|
-
const requested = resolve6(target);
|
|
2157
|
-
let entry;
|
|
2158
|
-
try {
|
|
2159
|
-
entry = await lstat5(requested);
|
|
2160
|
-
} catch (cause) {
|
|
2161
|
-
if (cause.code === "ENOENT") {
|
|
2162
|
-
throw new BuddyProjectContextError(
|
|
2163
|
-
"PROJECT_DIRECTORY_NOT_FOUND",
|
|
2164
|
-
`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${requested}`,
|
|
2165
|
-
requested,
|
|
2166
|
-
{ cause }
|
|
2167
|
-
);
|
|
2168
|
-
}
|
|
2169
|
-
throw cause;
|
|
2170
|
-
}
|
|
2171
|
-
if (!entry.isDirectory()) {
|
|
2172
|
-
throw new BuddyProjectContextError(
|
|
2173
|
-
"PROJECT_DIRECTORY_INVALID",
|
|
2174
|
-
`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${requested}`,
|
|
2175
|
-
requested
|
|
2176
|
-
);
|
|
2177
|
-
}
|
|
2178
|
-
return realpath(requested);
|
|
2179
|
-
}
|
|
2180
|
-
async function projectFileState(directory, filename) {
|
|
2181
|
-
const configPath = join6(directory, filename);
|
|
2182
|
-
try {
|
|
2183
|
-
const entry = await lstat5(configPath);
|
|
2184
|
-
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
2185
|
-
throw new BuddyProjectContextError(
|
|
2186
|
-
"PROJECT_CONFIG_INVALID",
|
|
2187
|
-
`${configPath} \u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u662F\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\u3002`,
|
|
2188
|
-
configPath
|
|
2189
|
-
);
|
|
2190
|
-
}
|
|
2191
|
-
return "valid";
|
|
2192
|
-
} catch (cause) {
|
|
2193
|
-
if (cause.code === "ENOENT") return "missing";
|
|
2194
|
-
throw cause;
|
|
2195
|
-
}
|
|
2196
|
-
}
|
|
2197
|
-
async function searchUp(startDirectory) {
|
|
2198
|
-
let directory = startDirectory;
|
|
2199
|
-
while (true) {
|
|
2200
|
-
if (await projectFileState(directory, BUDDY_AGENT_FILENAME) === "valid") return directory;
|
|
2201
|
-
const parent = dirname2(directory);
|
|
2202
|
-
if (parent === directory) return void 0;
|
|
2203
|
-
directory = parent;
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
async function findBuddyProjectRoot(startDirectory) {
|
|
2207
|
-
return searchUp(await existingDirectory(startDirectory));
|
|
2208
|
-
}
|
|
2209
|
-
async function requireBuddyProjectRoot(startDirectory) {
|
|
2210
|
-
const requested = resolve6(startDirectory);
|
|
2211
|
-
const root = await findBuddyProjectRoot(requested);
|
|
2212
|
-
if (root !== void 0) return root;
|
|
2213
|
-
throw new BuddyProjectContextError(
|
|
2214
|
-
"PROJECT_NOT_FOUND",
|
|
2215
|
-
[
|
|
2216
|
-
"\u5F53\u524D\u76EE\u5F55\u4E0D\u5C5E\u4E8E\u642D\u642D\u9879\u76EE\u3002",
|
|
2217
|
-
`\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`
|
|
2218
|
-
].join("\n"),
|
|
2219
|
-
requested
|
|
2220
|
-
);
|
|
2221
|
-
}
|
|
2222
|
-
async function requireBuddyAgentProjectRoot(startDirectory) {
|
|
2223
|
-
const root = await requireBuddyProjectRoot(startDirectory);
|
|
2224
|
-
const agentPath = join6(root, BUDDY_AGENT_FILENAME);
|
|
2225
|
-
try {
|
|
2226
|
-
await readBuddyAgentConfig(root);
|
|
2227
|
-
const missing = await missingBuddyAgentFiles(root);
|
|
2228
|
-
if (missing.length > 0) throw new Error(`\u5DE5\u7A0B\u7F3A\u5C11\u6709\u6548\u6587\u4EF6\uFF1A${missing.join("\u3001")}`);
|
|
2229
|
-
return root;
|
|
2230
|
-
} catch (cause) {
|
|
2231
|
-
throw new BuddyProjectContextError(
|
|
2232
|
-
"PROJECT_NOT_FOUND",
|
|
2233
|
-
[
|
|
2234
|
-
`\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"}`,
|
|
2235
|
-
"\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"
|
|
2236
|
-
].join("\n"),
|
|
2237
|
-
agentPath,
|
|
2238
|
-
{ cause }
|
|
2239
|
-
);
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
async function nearestExistingDirectory(target) {
|
|
2243
|
-
let candidate = resolve6(target);
|
|
2244
|
-
while (true) {
|
|
2245
|
-
try {
|
|
2246
|
-
const entry = await lstat5(candidate);
|
|
2247
|
-
if (!entry.isDirectory()) {
|
|
2248
|
-
throw new BuddyProjectContextError(
|
|
2249
|
-
"PROJECT_DIRECTORY_INVALID",
|
|
2250
|
-
`\u521D\u59CB\u5316\u76EE\u6807\u7684\u5DF2\u6709\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${candidate}`,
|
|
2251
|
-
candidate
|
|
2252
|
-
);
|
|
2253
|
-
}
|
|
2254
|
-
return realpath(candidate);
|
|
2255
|
-
} catch (cause) {
|
|
2256
|
-
if (cause.code !== "ENOENT") throw cause;
|
|
2257
|
-
const parent = dirname2(candidate);
|
|
2258
|
-
if (parent === candidate) throw cause;
|
|
2259
|
-
candidate = parent;
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
async function assertOutsideBuddyProject(target) {
|
|
2264
|
-
const requested = resolve6(target);
|
|
2265
|
-
const container = await nearestExistingDirectory(requested);
|
|
2266
|
-
const projectRoot = await searchUp(container);
|
|
2267
|
-
if (projectRoot === void 0) return;
|
|
2268
|
-
throw new BuddyProjectContextError(
|
|
2269
|
-
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
2270
|
-
[
|
|
2271
|
-
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
2272
|
-
`\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`
|
|
2273
|
-
].join("\n"),
|
|
2274
|
-
requested
|
|
2275
|
-
);
|
|
2276
|
-
}
|
|
2277
|
-
async function assertInitializableBuddyTarget(target) {
|
|
2278
|
-
const requested = resolve6(target);
|
|
2279
|
-
const container = await nearestExistingDirectory(requested);
|
|
2280
|
-
const projectRoot = await searchUp(container);
|
|
2281
|
-
if (projectRoot === void 0) return;
|
|
2282
|
-
let requestedRoot = requested;
|
|
2283
|
-
try {
|
|
2284
|
-
requestedRoot = await realpath(requested);
|
|
2285
|
-
} catch (cause) {
|
|
2286
|
-
if (cause.code !== "ENOENT") throw cause;
|
|
2287
|
-
}
|
|
2288
|
-
if (projectRoot === requestedRoot) {
|
|
2289
|
-
try {
|
|
2290
|
-
await readBuddyProjectManifest(projectRoot);
|
|
2291
|
-
return;
|
|
2292
|
-
} catch (cause) {
|
|
2293
|
-
throw new BuddyProjectContextError(
|
|
2294
|
-
"PROJECT_CONFIG_INVALID",
|
|
2295
|
-
`${join6(projectRoot, BUDDY_AGENT_FILENAME)} \u4E0D\u662F\u6709\u6548\u7684\u9879\u76EE\u6E05\u5355\u3002`,
|
|
2296
|
-
requested,
|
|
2297
|
-
{ cause }
|
|
2298
|
-
);
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
throw new BuddyProjectContextError(
|
|
2302
|
-
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
2303
|
-
[
|
|
2304
|
-
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u5B8C\u6574\u7684\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
2305
|
-
"\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"
|
|
2306
|
-
].join("\n"),
|
|
2307
|
-
requested
|
|
2308
|
-
);
|
|
2309
|
-
}
|
|
2310
|
-
|
|
2311
2779
|
// packages/cli-core/src/commands/dev.ts
|
|
2312
2780
|
var DevCommandUsageError = class extends Error {
|
|
2313
2781
|
constructor(message) {
|
|
@@ -2388,7 +2856,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2388
2856
|
return { exitCode: 0 };
|
|
2389
2857
|
}
|
|
2390
2858
|
try {
|
|
2391
|
-
const requestedRoot =
|
|
2859
|
+
const requestedRoot = resolve11(options.cwd ?? process.cwd(), parsed.directory);
|
|
2392
2860
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
2393
2861
|
const environment = options.env ?? process.env;
|
|
2394
2862
|
const url = appServerUrl(parsed.appServer, environment);
|
|
@@ -2441,7 +2909,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2441
2909
|
}
|
|
2442
2910
|
|
|
2443
2911
|
// packages/cli-core/src/commands/cloud.ts
|
|
2444
|
-
import { resolve as
|
|
2912
|
+
import { resolve as resolve12 } from "node:path";
|
|
2445
2913
|
|
|
2446
2914
|
// packages/cli-core/src/cloud/auth.ts
|
|
2447
2915
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -2612,7 +3080,7 @@ function positiveTimeout(value, fallback, name) {
|
|
|
2612
3080
|
if (!Number.isSafeInteger(result) || result <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
2613
3081
|
return result;
|
|
2614
3082
|
}
|
|
2615
|
-
function
|
|
3083
|
+
function object3(value, path) {
|
|
2616
3084
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2617
3085
|
throw invalid(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
2618
3086
|
}
|
|
@@ -2632,7 +3100,7 @@ function optionalText(value, path, allowNewlines = false) {
|
|
|
2632
3100
|
}
|
|
2633
3101
|
function meta(value, path) {
|
|
2634
3102
|
if (value === null) return null;
|
|
2635
|
-
const source =
|
|
3103
|
+
const source = object3(value, path);
|
|
2636
3104
|
const name = source.name === void 0 ? void 0 : safeText(source.name, `${path}.name`);
|
|
2637
3105
|
const avatar = optionalText(source.avatar, `${path}.avatar`);
|
|
2638
3106
|
const tagline = optionalText(source.tagline, `${path}.tagline`);
|
|
@@ -2666,7 +3134,7 @@ function auditStatus(value, path) {
|
|
|
2666
3134
|
return value;
|
|
2667
3135
|
}
|
|
2668
3136
|
function packageVersion(value, path) {
|
|
2669
|
-
const source =
|
|
3137
|
+
const source = object3(value, path);
|
|
2670
3138
|
const auditNote = source.auditNote === null ? null : safeText(source.auditNote, `${path}.auditNote`, true);
|
|
2671
3139
|
return Object.freeze({
|
|
2672
3140
|
version: safeText(source.version, `${path}.version`),
|
|
@@ -2684,7 +3152,7 @@ function positivePage(value, name) {
|
|
|
2684
3152
|
return value;
|
|
2685
3153
|
}
|
|
2686
3154
|
function buddy(value, includeToken) {
|
|
2687
|
-
const source =
|
|
3155
|
+
const source = object3(value, "response");
|
|
2688
3156
|
const token = includeToken ? safeText(source.token, "response.token") : void 0;
|
|
2689
3157
|
return Object.freeze({
|
|
2690
3158
|
id: safeText(source.id, "response.id"),
|
|
@@ -2802,17 +3270,6 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2802
3270
|
if (!response.ok) throw serverError(response, payload);
|
|
2803
3271
|
return payload;
|
|
2804
3272
|
}
|
|
2805
|
-
async listModels() {
|
|
2806
|
-
const source = object2(await this.#request("/cli/models", "GET"), "response");
|
|
2807
|
-
if (!Array.isArray(source.models)) throw invalid("response.models \u5FC5\u987B\u662F\u6A21\u578B ID \u6570\u7EC4");
|
|
2808
|
-
const models = source.models.map((value, index) => {
|
|
2809
|
-
const id = safeText(value, `response.models[${index}]`);
|
|
2810
|
-
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");
|
|
2811
|
-
return id;
|
|
2812
|
-
});
|
|
2813
|
-
if (new Set(models).size !== models.length) throw invalid("response.models \u4E2D\u6709\u91CD\u590D\u6A21\u578B ID");
|
|
2814
|
-
return Object.freeze({ models: Object.freeze(models) });
|
|
2815
|
-
}
|
|
2816
3273
|
async createBuddy(request) {
|
|
2817
3274
|
validateBuddyMeta(request.meta);
|
|
2818
3275
|
return buddy(await this.#request("/cli/buddies", "POST", { json: { meta: request.meta } }), true);
|
|
@@ -2824,7 +3281,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2824
3281
|
}), true);
|
|
2825
3282
|
}
|
|
2826
3283
|
async listBuddies() {
|
|
2827
|
-
const source =
|
|
3284
|
+
const source = object3(await this.#request("/cli/buddies", "GET"), "response");
|
|
2828
3285
|
if (!Array.isArray(source.buddies)) throw invalid("response.buddies \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
2829
3286
|
return Object.freeze({
|
|
2830
3287
|
buddies: Object.freeze(source.buddies.map((item) => buddy(item, false)))
|
|
@@ -2841,7 +3298,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2841
3298
|
...pageSize === void 0 ? {} : { pageSize: String(pageSize) }
|
|
2842
3299
|
});
|
|
2843
3300
|
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
2844
|
-
const source =
|
|
3301
|
+
const source = object3(await this.#request(
|
|
2845
3302
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages${suffix}`,
|
|
2846
3303
|
"GET"
|
|
2847
3304
|
), "response");
|
|
@@ -2863,7 +3320,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2863
3320
|
throw new CloudLifecycleError("INVALID_REQUEST", "\u63D0\u4EA4\u5305\u4E0D\u80FD\u4E3A\u7A7A");
|
|
2864
3321
|
}
|
|
2865
3322
|
const query = new URLSearchParams({ commit });
|
|
2866
|
-
const source =
|
|
3323
|
+
const source = object3(await this.#request(
|
|
2867
3324
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages/push?${query.toString()}`,
|
|
2868
3325
|
"POST",
|
|
2869
3326
|
{
|
|
@@ -2880,13 +3337,13 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2880
3337
|
});
|
|
2881
3338
|
}
|
|
2882
3339
|
async logout(accessToken) {
|
|
2883
|
-
const source =
|
|
3340
|
+
const source = object3(await this.#request("/cli/auth/logout", "POST", { accessToken }), "response");
|
|
2884
3341
|
if (source.ok !== true) throw invalid("response.ok \u5FC5\u987B\u662F true");
|
|
2885
3342
|
}
|
|
2886
3343
|
};
|
|
2887
3344
|
|
|
2888
3345
|
// packages/cli-core/src/cloud/browser-login.ts
|
|
2889
|
-
import { createHash as
|
|
3346
|
+
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
2890
3347
|
import { spawn } from "node:child_process";
|
|
2891
3348
|
import { createServer } from "node:http";
|
|
2892
3349
|
|
|
@@ -2946,10 +3403,10 @@ function browserCommand(url) {
|
|
|
2946
3403
|
}
|
|
2947
3404
|
async function openExternalUrl(url) {
|
|
2948
3405
|
const launch = browserCommand(url);
|
|
2949
|
-
await new Promise((
|
|
3406
|
+
await new Promise((resolve16, reject) => {
|
|
2950
3407
|
const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
2951
3408
|
child.once("error", reject);
|
|
2952
|
-
child.once("exit", (code2) => code2 === 0 ?
|
|
3409
|
+
child.once("exit", (code2) => code2 === 0 ? resolve16() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
2953
3410
|
});
|
|
2954
3411
|
}
|
|
2955
3412
|
async function write(res, status, title, message, webUrl, tone = "error") {
|
|
@@ -2961,10 +3418,10 @@ async function write(res, status, title, message, webUrl, tone = "error") {
|
|
|
2961
3418
|
"referrer-policy": "no-referrer",
|
|
2962
3419
|
"x-content-type-options": "nosniff"
|
|
2963
3420
|
});
|
|
2964
|
-
await new Promise((
|
|
3421
|
+
await new Promise((resolve16, reject) => {
|
|
2965
3422
|
res.once("error", reject);
|
|
2966
|
-
res.once("close",
|
|
2967
|
-
res.end(renderLoginResult(title, message, webUrl, tone),
|
|
3423
|
+
res.once("close", resolve16);
|
|
3424
|
+
res.end(renderLoginResult(title, message, webUrl, tone), resolve16);
|
|
2968
3425
|
});
|
|
2969
3426
|
}
|
|
2970
3427
|
async function readJson(response) {
|
|
@@ -2994,16 +3451,16 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
2994
3451
|
if (request.signal?.aborted) throw request.signal.reason;
|
|
2995
3452
|
const callbackSecret = randomBytes(24).toString("hex");
|
|
2996
3453
|
const codeVerifier = randomBytes(32).toString("base64url");
|
|
2997
|
-
const codeChallenge =
|
|
3454
|
+
const codeChallenge = createHash4("sha256").update(codeVerifier).digest("base64url");
|
|
2998
3455
|
let requestId = "";
|
|
2999
3456
|
let settle;
|
|
3000
3457
|
let reject;
|
|
3001
3458
|
let settled = false;
|
|
3002
|
-
const result = new Promise((
|
|
3459
|
+
const result = new Promise((resolve16, rejectResult) => {
|
|
3003
3460
|
settle = (value) => {
|
|
3004
3461
|
if (!settled) {
|
|
3005
3462
|
settled = true;
|
|
3006
|
-
|
|
3463
|
+
resolve16(value);
|
|
3007
3464
|
}
|
|
3008
3465
|
};
|
|
3009
3466
|
reject = (reason) => {
|
|
@@ -3022,9 +3479,9 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3022
3479
|
state: request.state
|
|
3023
3480
|
}).then(settle, reject);
|
|
3024
3481
|
});
|
|
3025
|
-
await new Promise((
|
|
3482
|
+
await new Promise((resolve16, rejectListen) => {
|
|
3026
3483
|
server.once("error", rejectListen);
|
|
3027
|
-
server.listen(0, "127.0.0.1", () =>
|
|
3484
|
+
server.listen(0, "127.0.0.1", () => resolve16());
|
|
3028
3485
|
});
|
|
3029
3486
|
const address = server.address();
|
|
3030
3487
|
if (!address || typeof address === "string") {
|
|
@@ -3057,8 +3514,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3057
3514
|
} finally {
|
|
3058
3515
|
clearTimeout(timer);
|
|
3059
3516
|
request.signal?.removeEventListener("abort", abort);
|
|
3060
|
-
await new Promise((
|
|
3061
|
-
server.close(() =>
|
|
3517
|
+
await new Promise((resolve16) => {
|
|
3518
|
+
server.close(() => resolve16());
|
|
3062
3519
|
server.closeIdleConnections();
|
|
3063
3520
|
server.closeAllConnections();
|
|
3064
3521
|
});
|
|
@@ -3173,7 +3630,7 @@ function parse(source) {
|
|
|
3173
3630
|
});
|
|
3174
3631
|
}
|
|
3175
3632
|
async function runCredentialCommand(input) {
|
|
3176
|
-
return await new Promise((
|
|
3633
|
+
return await new Promise((resolve16, reject) => {
|
|
3177
3634
|
const child = spawn2(input.command, [...input.args], {
|
|
3178
3635
|
shell: false,
|
|
3179
3636
|
windowsHide: true,
|
|
@@ -3189,7 +3646,7 @@ async function runCredentialCommand(input) {
|
|
|
3189
3646
|
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3190
3647
|
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3191
3648
|
child.once("error", reject);
|
|
3192
|
-
child.once("close", (code2) =>
|
|
3649
|
+
child.once("close", (code2) => resolve16({
|
|
3193
3650
|
code: code2 ?? 1,
|
|
3194
3651
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3195
3652
|
stderr: Buffer.concat(stderr).toString("utf8")
|
|
@@ -3257,9 +3714,6 @@ var MockDeveloperPlatformClient = class {
|
|
|
3257
3714
|
constructor(options = {}) {
|
|
3258
3715
|
this.#now = options.now ?? Date.now;
|
|
3259
3716
|
}
|
|
3260
|
-
async listModels() {
|
|
3261
|
-
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");
|
|
3262
|
-
}
|
|
3263
3717
|
async createBuddy(request) {
|
|
3264
3718
|
const id = `b_mock_${randomBytes2(8).toString("hex")}`;
|
|
3265
3719
|
const value = this.#value({
|
|
@@ -3362,7 +3816,6 @@ var CloudCommandUsageError = class extends Error {
|
|
|
3362
3816
|
var HELP = Object.freeze({
|
|
3363
3817
|
login: "\u7528\u6CD5: buddy-cli login",
|
|
3364
3818
|
logout: "\u7528\u6CD5: buddy-cli logout",
|
|
3365
|
-
models: "\u7528\u6CD5: buddy-cli models\uFF08\u9700\u8981\u767B\u5F55\uFF0C\u4E0D\u8981\u6C42\u5F53\u524D\u76EE\u5F55\u662F Agent \u5DE5\u7A0B\uFF09",
|
|
3366
3819
|
push: "\u7528\u6CD5: buddy-cli push [--directory <\u9879\u76EE\u76EE\u5F55>] [--commit <\u672C\u6B21\u6539\u52A8\u8BF4\u660E>]",
|
|
3367
3820
|
status: "\u7528\u6CD5: buddy-cli status [--directory <\u9879\u76EE\u76EE\u5F55>]"
|
|
3368
3821
|
});
|
|
@@ -3413,7 +3866,7 @@ function dependencies(options) {
|
|
|
3413
3866
|
return { session: options.session, client: options.client };
|
|
3414
3867
|
}
|
|
3415
3868
|
function requestedDirectory(options, parsed) {
|
|
3416
|
-
return
|
|
3869
|
+
return resolve12(options.cwd ?? process.cwd(), parsed.values.get("--directory") ?? ".");
|
|
3417
3870
|
}
|
|
3418
3871
|
function auditLabel(status) {
|
|
3419
3872
|
if (status === "auditing") return "\u5BA1\u6838\u4E2D";
|
|
@@ -3502,20 +3955,6 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
3502
3955
|
}
|
|
3503
3956
|
if (command === "logout") return await runLogout(io, options);
|
|
3504
3957
|
if (command === "push") return await runPush(parsed, io, options);
|
|
3505
|
-
if (command === "models") {
|
|
3506
|
-
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");
|
|
3507
|
-
const { session: session2, client: client2 } = dependencies(options);
|
|
3508
|
-
await session2.token({ signal: options.signal });
|
|
3509
|
-
const { models } = await client2.listModels();
|
|
3510
|
-
if (models.length === 0) io.write("\u5E73\u53F0\u5F53\u524D\u6CA1\u6709\u53EF\u9009\u6A21\u578B\u3002");
|
|
3511
|
-
else {
|
|
3512
|
-
io.write("\u5E73\u53F0\u53EF\u9009\u6A21\u578B\uFF1A");
|
|
3513
|
-
for (const id of models) io.write(`- ${id}`);
|
|
3514
|
-
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');
|
|
3515
|
-
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");
|
|
3516
|
-
}
|
|
3517
|
-
return { exitCode: 0 };
|
|
3518
|
-
}
|
|
3519
3958
|
const root = await (options.resolveProjectRoot ?? requireBuddyProjectRoot)(requestedDirectory(options, parsed));
|
|
3520
3959
|
const manifest = await (options.readManifest ?? readBuddyProjectManifest)(root);
|
|
3521
3960
|
if (options.mode === "local-mock") {
|
|
@@ -3543,8 +3982,8 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
3543
3982
|
var CLOUD_COMMAND_HELP = HELP;
|
|
3544
3983
|
|
|
3545
3984
|
// packages/cli-core/src/migration/index.ts
|
|
3546
|
-
import { lstat as
|
|
3547
|
-
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";
|
|
3548
3987
|
var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
|
|
3549
3988
|
maxFiles: 128,
|
|
3550
3989
|
maxFileBytes: 256 * 1024,
|
|
@@ -3697,7 +4136,7 @@ async function existingProjectRoot(value) {
|
|
|
3697
4136
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
3698
4137
|
}
|
|
3699
4138
|
try {
|
|
3700
|
-
const root = await
|
|
4139
|
+
const root = await realpath4(resolve13(value));
|
|
3701
4140
|
if (!(await stat(root)).isDirectory()) {
|
|
3702
4141
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
3703
4142
|
}
|
|
@@ -3713,9 +4152,9 @@ async function existingProjectRoot(value) {
|
|
|
3713
4152
|
}
|
|
3714
4153
|
}
|
|
3715
4154
|
async function existingSkillRoot(projectRoot, skillPath) {
|
|
3716
|
-
const candidate = skillPath === "." ? projectRoot :
|
|
4155
|
+
const candidate = skillPath === "." ? projectRoot : join8(projectRoot, ...skillPath.split("/"));
|
|
3717
4156
|
try {
|
|
3718
|
-
await
|
|
4157
|
+
await lstat7(candidate);
|
|
3719
4158
|
} catch (cause) {
|
|
3720
4159
|
if (filesystemCode(cause) === "ENOENT") {
|
|
3721
4160
|
return rejectScan("SKILL_ROOT_NOT_FOUND", skillPath, "\u627E\u4E0D\u5230\u6307\u5B9A\u7684 Skill \u76EE\u5F55\u3002");
|
|
@@ -3724,7 +4163,7 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
3724
4163
|
}
|
|
3725
4164
|
let root;
|
|
3726
4165
|
try {
|
|
3727
|
-
root = await
|
|
4166
|
+
root = await realpath4(candidate);
|
|
3728
4167
|
} catch (cause) {
|
|
3729
4168
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3730
4169
|
return rejectScan("SKILL_SYMLINK_CYCLE", skillPath, "Skill \u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -3750,10 +4189,10 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
3750
4189
|
}
|
|
3751
4190
|
async function assertEntrypoint(context) {
|
|
3752
4191
|
const entryPath = reportChild(context.skillPath, "SKILL.md");
|
|
3753
|
-
const candidate =
|
|
4192
|
+
const candidate = join8(context.skillRoot, "SKILL.md");
|
|
3754
4193
|
try {
|
|
3755
|
-
await
|
|
3756
|
-
const target = await
|
|
4194
|
+
await lstat7(candidate);
|
|
4195
|
+
const target = await realpath4(candidate);
|
|
3757
4196
|
if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
|
|
3758
4197
|
rejectScan(
|
|
3759
4198
|
"SKILL_SYMLINK_ESCAPE",
|
|
@@ -3797,7 +4236,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3797
4236
|
}
|
|
3798
4237
|
let directory;
|
|
3799
4238
|
try {
|
|
3800
|
-
directory = await
|
|
4239
|
+
directory = await realpath4(actualDirectory);
|
|
3801
4240
|
} catch (cause) {
|
|
3802
4241
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3803
4242
|
return rejectScan("SKILL_SYMLINK_CYCLE", reportDirectory, "Skill \u76EE\u5F55\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -3817,7 +4256,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3817
4256
|
nextAncestors.add(directoryKey);
|
|
3818
4257
|
let entries;
|
|
3819
4258
|
try {
|
|
3820
|
-
entries = await
|
|
4259
|
+
entries = await readdir4(directory, { withFileTypes: true, encoding: "utf8" });
|
|
3821
4260
|
} catch (cause) {
|
|
3822
4261
|
return rejectScan("SKILL_SCAN_IO_ERROR", reportDirectory, "\u65E0\u6CD5\u5217\u51FA Skill \u5B50\u76EE\u5F55\u3002", cause);
|
|
3823
4262
|
}
|
|
@@ -3854,10 +4293,10 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3854
4293
|
}
|
|
3855
4294
|
for (const entry of entries) {
|
|
3856
4295
|
const childReportPath = reportChild(reportDirectory, entry.name);
|
|
3857
|
-
const childPath =
|
|
4296
|
+
const childPath = join8(directory, entry.name);
|
|
3858
4297
|
let target;
|
|
3859
4298
|
try {
|
|
3860
|
-
target = await
|
|
4299
|
+
target = await realpath4(childPath);
|
|
3861
4300
|
} catch (cause) {
|
|
3862
4301
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3863
4302
|
return rejectScan("SKILL_SYMLINK_CYCLE", childReportPath, "Skill \u5185\u5BB9\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -4308,9 +4747,9 @@ async function scanExternalSkillPortability(options) {
|
|
|
4308
4747
|
}
|
|
4309
4748
|
|
|
4310
4749
|
// packages/cli-core/src/push/preflight.ts
|
|
4311
|
-
import { createHash as
|
|
4312
|
-
import { lstat as
|
|
4313
|
-
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";
|
|
4314
4753
|
|
|
4315
4754
|
// packages/cli-core/src/push/errors.ts
|
|
4316
4755
|
var PushPreflightError = class extends Error {
|
|
@@ -4408,7 +4847,7 @@ function error(options) {
|
|
|
4408
4847
|
throw new PushPreflightError(options);
|
|
4409
4848
|
}
|
|
4410
4849
|
function sha256(value) {
|
|
4411
|
-
return `sha256:${
|
|
4850
|
+
return `sha256:${createHash5("sha256").update(value).digest("hex")}`;
|
|
4412
4851
|
}
|
|
4413
4852
|
function positiveLimit(value, name, hardMaximum) {
|
|
4414
4853
|
if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
|
|
@@ -4498,10 +4937,10 @@ function normalizeBookletPath(value) {
|
|
|
4498
4937
|
return normalized;
|
|
4499
4938
|
}
|
|
4500
4939
|
async function canonicalProjectRoot(projectRoot) {
|
|
4501
|
-
const absolute =
|
|
4940
|
+
const absolute = resolve14(projectRoot);
|
|
4502
4941
|
let entry;
|
|
4503
4942
|
try {
|
|
4504
|
-
entry = await
|
|
4943
|
+
entry = await lstat8(absolute);
|
|
4505
4944
|
} catch (cause) {
|
|
4506
4945
|
error({
|
|
4507
4946
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4518,7 +4957,7 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
4518
4957
|
});
|
|
4519
4958
|
}
|
|
4520
4959
|
try {
|
|
4521
|
-
return await
|
|
4960
|
+
return await realpath5(absolute);
|
|
4522
4961
|
} catch (cause) {
|
|
4523
4962
|
error({
|
|
4524
4963
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4529,10 +4968,10 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
4529
4968
|
}
|
|
4530
4969
|
}
|
|
4531
4970
|
async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
4532
|
-
const path =
|
|
4971
|
+
const path = join9(projectRoot, filename);
|
|
4533
4972
|
let entry;
|
|
4534
4973
|
try {
|
|
4535
|
-
entry = await
|
|
4974
|
+
entry = await lstat8(path);
|
|
4536
4975
|
} catch (cause) {
|
|
4537
4976
|
error({
|
|
4538
4977
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -4563,22 +5002,22 @@ async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
|
4563
5002
|
maxFileBytes
|
|
4564
5003
|
});
|
|
4565
5004
|
}
|
|
4566
|
-
function decodeCapturedConfig(
|
|
4567
|
-
if (
|
|
5005
|
+
function decodeCapturedConfig(file2) {
|
|
5006
|
+
if (file2.contents === void 0) {
|
|
4568
5007
|
error({
|
|
4569
5008
|
code: "PUSH_CONFIG_INVALID",
|
|
4570
|
-
path:
|
|
4571
|
-
message: `\u65E0\u6CD5\u8BFB\u53D6 ${
|
|
5009
|
+
path: file2.path,
|
|
5010
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6 ${file2.path} \u5185\u5BB9\u3002`
|
|
4572
5011
|
});
|
|
4573
5012
|
}
|
|
4574
5013
|
let source;
|
|
4575
5014
|
try {
|
|
4576
|
-
source = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
5015
|
+
source = new TextDecoder("utf-8", { fatal: true }).decode(file2.contents);
|
|
4577
5016
|
} catch (cause) {
|
|
4578
5017
|
error({
|
|
4579
5018
|
code: "PUSH_CONFIG_INVALID",
|
|
4580
|
-
path:
|
|
4581
|
-
message: `${
|
|
5019
|
+
path: file2.path,
|
|
5020
|
+
message: `${file2.path} \u4E0D\u662F\u6709\u6548 UTF-8\u3002`,
|
|
4582
5021
|
cause
|
|
4583
5022
|
});
|
|
4584
5023
|
}
|
|
@@ -4599,7 +5038,7 @@ function parseCapturedProject(agentFile) {
|
|
|
4599
5038
|
async function selectLockfile(projectRoot) {
|
|
4600
5039
|
let names;
|
|
4601
5040
|
try {
|
|
4602
|
-
names = await
|
|
5041
|
+
names = await readdir5(projectRoot);
|
|
4603
5042
|
} catch (cause) {
|
|
4604
5043
|
error({
|
|
4605
5044
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4654,7 +5093,7 @@ async function hashRegularFile(options) {
|
|
|
4654
5093
|
message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
|
|
4655
5094
|
});
|
|
4656
5095
|
}
|
|
4657
|
-
const hash =
|
|
5096
|
+
const hash = createHash5("sha256");
|
|
4658
5097
|
const captured = [];
|
|
4659
5098
|
const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
|
|
4660
5099
|
let bytes = 0;
|
|
@@ -4708,7 +5147,7 @@ async function createManifest(options) {
|
|
|
4708
5147
|
}
|
|
4709
5148
|
let names;
|
|
4710
5149
|
try {
|
|
4711
|
-
names = await
|
|
5150
|
+
names = await readdir5(directory);
|
|
4712
5151
|
} catch (cause) {
|
|
4713
5152
|
error({
|
|
4714
5153
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4732,10 +5171,10 @@ async function createManifest(options) {
|
|
|
4732
5171
|
sensitiveEntriesExcluded += 1;
|
|
4733
5172
|
continue;
|
|
4734
5173
|
}
|
|
4735
|
-
const absolutePath =
|
|
5174
|
+
const absolutePath = join9(directory, name);
|
|
4736
5175
|
let entry;
|
|
4737
5176
|
try {
|
|
4738
|
-
entry = await
|
|
5177
|
+
entry = await lstat8(absolutePath);
|
|
4739
5178
|
} catch (cause) {
|
|
4740
5179
|
error({
|
|
4741
5180
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -4766,7 +5205,7 @@ async function createManifest(options) {
|
|
|
4766
5205
|
}
|
|
4767
5206
|
let canonicalPath;
|
|
4768
5207
|
try {
|
|
4769
|
-
canonicalPath = await
|
|
5208
|
+
canonicalPath = await realpath5(absolutePath);
|
|
4770
5209
|
} catch (cause) {
|
|
4771
5210
|
error({
|
|
4772
5211
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -4825,15 +5264,15 @@ async function createManifest(options) {
|
|
|
4825
5264
|
message: `\u63D0\u4EA4\u6587\u4EF6\u603B\u91CF\u8D85\u8FC7\u4E0A\u9650 ${options.limits.maxTotalBytes} bytes\u3002`
|
|
4826
5265
|
});
|
|
4827
5266
|
}
|
|
4828
|
-
const
|
|
5267
|
+
const file2 = await hashRegularFile({
|
|
4829
5268
|
absolutePath,
|
|
4830
5269
|
portablePath: path,
|
|
4831
5270
|
expected: entry,
|
|
4832
5271
|
captureContents: options.capturePaths.has(path),
|
|
4833
5272
|
maxFileBytes: options.limits.maxFileBytes
|
|
4834
5273
|
});
|
|
4835
|
-
files.push(
|
|
4836
|
-
totalBytes +=
|
|
5274
|
+
files.push(file2);
|
|
5275
|
+
totalBytes += file2.bytes;
|
|
4837
5276
|
}
|
|
4838
5277
|
};
|
|
4839
5278
|
await walk(options.projectRoot, []);
|
|
@@ -4914,10 +5353,10 @@ function validateLockfile(lockfile, contents) {
|
|
|
4914
5353
|
function bundleDigest(files) {
|
|
4915
5354
|
const descriptor = JSON.stringify({
|
|
4916
5355
|
schemaVersion: "1",
|
|
4917
|
-
files: files.map((
|
|
4918
|
-
path:
|
|
4919
|
-
bytes:
|
|
4920
|
-
digest:
|
|
5356
|
+
files: files.map((file2) => ({
|
|
5357
|
+
path: file2.path,
|
|
5358
|
+
bytes: file2.bytes,
|
|
5359
|
+
digest: file2.digest
|
|
4921
5360
|
}))
|
|
4922
5361
|
});
|
|
4923
5362
|
return sha256(`buddy-push-bundle-v1\0${descriptor}`);
|
|
@@ -4927,8 +5366,8 @@ function warning(code2, message) {
|
|
|
4927
5366
|
}
|
|
4928
5367
|
function assertOfficialProjectIncluded(files) {
|
|
4929
5368
|
for (const path of OFFICIAL_RUNTIME_INPUTS) {
|
|
4930
|
-
const
|
|
4931
|
-
if (
|
|
5369
|
+
const file2 = files.find((entry) => entry.path === path);
|
|
5370
|
+
if (file2 === void 0 || file2.bytes === 0) {
|
|
4932
5371
|
error({
|
|
4933
5372
|
code: "PUSH_PROJECT_INVALID",
|
|
4934
5373
|
path,
|
|
@@ -4967,7 +5406,7 @@ function assertOfficialProjectIncluded(files) {
|
|
|
4967
5406
|
}
|
|
4968
5407
|
}
|
|
4969
5408
|
async function assertCapturedConfigStable(options) {
|
|
4970
|
-
const manifestEntry = options.manifestFiles.find((
|
|
5409
|
+
const manifestEntry = options.manifestFiles.find((file2) => file2.path === options.filename);
|
|
4971
5410
|
if (manifestEntry === void 0) {
|
|
4972
5411
|
error({
|
|
4973
5412
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -4984,8 +5423,8 @@ async function assertCapturedConfigStable(options) {
|
|
|
4984
5423
|
}
|
|
4985
5424
|
let current;
|
|
4986
5425
|
try {
|
|
4987
|
-
const path =
|
|
4988
|
-
const entry = await
|
|
5426
|
+
const path = join9(options.projectRoot, options.filename);
|
|
5427
|
+
const entry = await lstat8(path);
|
|
4989
5428
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
4990
5429
|
error({
|
|
4991
5430
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5028,6 +5467,8 @@ async function createPushPreflight(options) {
|
|
|
5028
5467
|
BUDDY_AGENT_FILENAME
|
|
5029
5468
|
);
|
|
5030
5469
|
parseCapturedProject(initialAgentEntry);
|
|
5470
|
+
const widgetDelivery = await checkWidgetDelivery(projectRoot);
|
|
5471
|
+
if (widgetDelivery.status === "incomplete") error({ code: "PUSH_WIDGET_DELIVERY_INCOMPLETE", message: widgetDeliverySummary(widgetDelivery) });
|
|
5031
5472
|
const lockfile = await selectLockfile(projectRoot);
|
|
5032
5473
|
assertBookletIsNotRequiredInput(bookletPath, lockfile);
|
|
5033
5474
|
const manifest = await createManifest({
|
|
@@ -5036,7 +5477,12 @@ async function createPushPreflight(options) {
|
|
|
5036
5477
|
capturePaths: /* @__PURE__ */ new Set([BUDDY_AGENT_FILENAME, lockfile.path, BUDDY_PLATFORM_PACKAGE_JSON, BUDDY_PLATFORM_AGENT_ENTRY, BUDDY_PLATFORM_FACTORY_ENTRY]),
|
|
5037
5478
|
...bookletPath === void 0 ? {} : { bookletPath }
|
|
5038
5479
|
});
|
|
5039
|
-
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);
|
|
5040
5486
|
if (lockEntry?.contents === void 0) {
|
|
5041
5487
|
error({
|
|
5042
5488
|
code: "PUSH_LOCKFILE_INVALID",
|
|
@@ -5060,10 +5506,23 @@ async function createPushPreflight(options) {
|
|
|
5060
5506
|
manifestFiles: manifest.files,
|
|
5061
5507
|
maxFileBytes: limits.maxFileBytes
|
|
5062
5508
|
});
|
|
5063
|
-
const
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
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
|
|
5067
5526
|
})));
|
|
5068
5527
|
const warnings = [warning(
|
|
5069
5528
|
"PREFLIGHT_ONLY",
|
|
@@ -5099,16 +5558,16 @@ async function createPushPreflight(options) {
|
|
|
5099
5558
|
}
|
|
5100
5559
|
|
|
5101
5560
|
// packages/cli-core/src/push/bundle.ts
|
|
5102
|
-
import { createHash as
|
|
5103
|
-
import { lstat as
|
|
5104
|
-
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";
|
|
5105
5564
|
import { Writable } from "node:stream";
|
|
5106
5565
|
import { pipeline } from "node:stream/promises";
|
|
5107
5566
|
import { createGzip } from "node:zlib";
|
|
5108
5567
|
import { pack } from "tar-stream";
|
|
5109
5568
|
var MEDIA_TYPE = "application/gzip";
|
|
5110
5569
|
function digest(bytes) {
|
|
5111
|
-
return `sha256:${
|
|
5570
|
+
return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
|
|
5112
5571
|
}
|
|
5113
5572
|
function changed(path, message, cause) {
|
|
5114
5573
|
throw new PushPreflightError({
|
|
@@ -5122,42 +5581,42 @@ function inside(root, target) {
|
|
|
5122
5581
|
const path = relative3(root, target);
|
|
5123
5582
|
return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
|
|
5124
5583
|
}
|
|
5125
|
-
async function capture(root,
|
|
5126
|
-
const absolute =
|
|
5584
|
+
async function capture(root, file2) {
|
|
5585
|
+
const absolute = join10(root, ...file2.path.split("/"));
|
|
5127
5586
|
try {
|
|
5128
|
-
const entry = await
|
|
5129
|
-
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !==
|
|
5130
|
-
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}`);
|
|
5131
5590
|
}
|
|
5132
|
-
const canonical = await
|
|
5133
|
-
if (!inside(root, canonical)) changed(
|
|
5134
|
-
const contents = await
|
|
5135
|
-
if (contents.byteLength !==
|
|
5136
|
-
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}`);
|
|
5137
5596
|
}
|
|
5138
5597
|
return Object.freeze({
|
|
5139
|
-
path:
|
|
5140
|
-
bytes:
|
|
5141
|
-
digest:
|
|
5598
|
+
path: file2.path,
|
|
5599
|
+
bytes: file2.bytes,
|
|
5600
|
+
digest: file2.digest,
|
|
5142
5601
|
content: contents
|
|
5143
5602
|
});
|
|
5144
5603
|
} catch (cause) {
|
|
5145
5604
|
if (cause instanceof PushPreflightError) throw cause;
|
|
5146
|
-
changed(
|
|
5605
|
+
changed(file2.path, `\u63D0\u4EA4\u524D\u65E0\u6CD5\u518D\u6B21\u8BFB\u53D6\u6587\u4EF6\uFF1A${file2.path}`, cause);
|
|
5147
5606
|
}
|
|
5148
5607
|
}
|
|
5149
5608
|
async function createPushSourceBundle(plan) {
|
|
5150
|
-
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));
|
|
5151
5610
|
if (canonicalRoot !== plan.projectRoot) {
|
|
5152
5611
|
changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u6839\u76EE\u5F55\u5728\u9884\u68C0\u540E\u53D1\u751F\u53D8\u5316");
|
|
5153
5612
|
}
|
|
5154
5613
|
const files = [];
|
|
5155
|
-
for (const
|
|
5614
|
+
for (const file2 of plan.files) files.push(await capture(canonicalRoot, file2));
|
|
5156
5615
|
const descriptor = JSON.stringify({
|
|
5157
5616
|
schemaVersion: "1",
|
|
5158
|
-
files: files.map((
|
|
5617
|
+
files: files.map((file2) => ({ path: file2.path, bytes: file2.bytes, digest: file2.digest }))
|
|
5159
5618
|
});
|
|
5160
|
-
const calculatedBundleDigest = `sha256:${
|
|
5619
|
+
const calculatedBundleDigest = `sha256:${createHash6("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
|
|
5161
5620
|
if (calculatedBundleDigest !== plan.bundleDigest) {
|
|
5162
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");
|
|
5163
5622
|
}
|
|
@@ -5170,16 +5629,16 @@ async function createPushSourceBundle(plan) {
|
|
|
5170
5629
|
}
|
|
5171
5630
|
});
|
|
5172
5631
|
const completed = pipeline(archive, createGzip({ level: 9 }), output);
|
|
5173
|
-
for (const
|
|
5632
|
+
for (const file2 of files) {
|
|
5174
5633
|
await new Promise((resolveEntry, rejectEntry) => {
|
|
5175
5634
|
archive.entry({
|
|
5176
|
-
name:
|
|
5177
|
-
size:
|
|
5635
|
+
name: file2.path,
|
|
5636
|
+
size: file2.content.byteLength,
|
|
5178
5637
|
mode: 420,
|
|
5179
5638
|
uid: 0,
|
|
5180
5639
|
gid: 0,
|
|
5181
5640
|
mtime: /* @__PURE__ */ new Date(0)
|
|
5182
|
-
},
|
|
5641
|
+
}, file2.content, (cause) => cause ? rejectEntry(cause) : resolveEntry());
|
|
5183
5642
|
});
|
|
5184
5643
|
}
|
|
5185
5644
|
archive.finalize();
|
|
@@ -5217,7 +5676,9 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5217
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>]
|
|
5218
5677
|
buddy-cli dev [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5219
5678
|
buddy-cli login | logout | push | status
|
|
5220
|
-
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]
|
|
5221
5682
|
buddy-cli preview [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5222
5683
|
buddy-cli help
|
|
5223
5684
|
|
|
@@ -5226,7 +5687,8 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5226
5687
|
dev \u542F\u52A8\u5B98\u65B9 Runtime \u5E76\u8FDE\u63A5 DEV App Server
|
|
5227
5688
|
login \u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u642D\u642D\u8D26\u53F7
|
|
5228
5689
|
logout \u9000\u51FA\u642D\u642D\u5F00\u53D1\u8005\u8D26\u53F7\u5E76\u6E05\u9664\u672C\u673A\u767B\u5F55\u72B6\u6001
|
|
5229
|
-
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
|
|
5230
5692
|
push \u4E0A\u4F20 Agent \u5DE5\u7A0B\u548C commit\uFF0C\u63D0\u4EA4\u5E73\u53F0\u5BA1\u6838
|
|
5231
5693
|
status \u67E5\u8BE2\u642D\u5B50\u7684\u8D44\u6599\u3001\u4EE3\u7801\u3001\u8FD0\u884C\u4E0E\u53D1\u5E03\u72B6\u6001
|
|
5232
5694
|
preview \u6253\u5F00\u642D\u642D App \u79C1\u804A\u6A21\u62DF\u5668\u4E0E\u8C03\u8BD5\u9875\u9762
|
|
@@ -5434,7 +5896,7 @@ function defaultProjectDirectory(name) {
|
|
|
5434
5896
|
return `./${folder}`;
|
|
5435
5897
|
}
|
|
5436
5898
|
async function initializeProjectRoute(route, args, io, options, application) {
|
|
5437
|
-
const cwd =
|
|
5899
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5438
5900
|
const { name, tagline, intro } = application;
|
|
5439
5901
|
const directoryInput = await textAnswer(
|
|
5440
5902
|
io,
|
|
@@ -5443,7 +5905,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
5443
5905
|
"\u9879\u76EE\u76EE\u5F55\uFF08\u7A7A\u76EE\u5F55\u6216\u5DF2\u6709\u642D\u642D\u5DE5\u7A0B\uFF09",
|
|
5444
5906
|
defaultProjectDirectory(name)
|
|
5445
5907
|
);
|
|
5446
|
-
const rootDirectory =
|
|
5908
|
+
const rootDirectory = resolve15(cwd, directoryInput);
|
|
5447
5909
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
5448
5910
|
const initializationTarget = { directory: rootDirectory, kind: "new" };
|
|
5449
5911
|
const initializationPlan = options.initializeProject ? void 0 : await planBuddyProjectInitialization(initializationTarget);
|
|
@@ -5487,7 +5949,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
5487
5949
|
}
|
|
5488
5950
|
async function runRoute(route, args, io, options) {
|
|
5489
5951
|
if (!io.canPrompt) assertNonInteractiveProjectArguments(route, args);
|
|
5490
|
-
const cwd =
|
|
5952
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5491
5953
|
const name = await textAnswer(io, args.name, "--name", "\u642D\u5B50\u540D\u79F0", void 0, BUDDY_PROFILE_TEXT_LIMITS.name);
|
|
5492
5954
|
const tagline = await textAnswer(
|
|
5493
5955
|
io,
|
|
@@ -5515,7 +5977,7 @@ async function runRoute(route, args, io, options) {
|
|
|
5515
5977
|
"\u9879\u76EE\u76EE\u5F55",
|
|
5516
5978
|
defaultProjectDirectory(name)
|
|
5517
5979
|
);
|
|
5518
|
-
const rootDirectory =
|
|
5980
|
+
const rootDirectory = resolve15(cwd, directoryInput);
|
|
5519
5981
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
5520
5982
|
const plan = await planBuddyDraftInitialization(rootDirectory);
|
|
5521
5983
|
const existingBuddyId = plan.existingManifest?.buddyId;
|
|
@@ -5549,11 +6011,11 @@ async function runRoute(route, args, io, options) {
|
|
|
5549
6011
|
return { ...result, route: "create", draft, buddyId };
|
|
5550
6012
|
}
|
|
5551
6013
|
async function resumeProjectArguments(args, cwd, options) {
|
|
5552
|
-
const root =
|
|
6014
|
+
const root = resolve15(cwd, args.directory?.trim() || ".");
|
|
5553
6015
|
await assertInitializableBuddyTarget(root);
|
|
5554
6016
|
let entry;
|
|
5555
6017
|
try {
|
|
5556
|
-
entry = await
|
|
6018
|
+
entry = await lstat10(join11(root, "buddy.agent.json"));
|
|
5557
6019
|
} catch (cause) {
|
|
5558
6020
|
if (cause.code === "ENOENT") return;
|
|
5559
6021
|
throw cause;
|
|
@@ -5582,7 +6044,7 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5582
6044
|
try {
|
|
5583
6045
|
const [command, ...rest] = args;
|
|
5584
6046
|
if (!command) {
|
|
5585
|
-
const cwd =
|
|
6047
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5586
6048
|
let projectRoot;
|
|
5587
6049
|
try {
|
|
5588
6050
|
projectRoot = await findBuddyProjectRoot(cwd);
|
|
@@ -5610,16 +6072,21 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5610
6072
|
io.write(HELP2);
|
|
5611
6073
|
return { exitCode: 0 };
|
|
5612
6074
|
}
|
|
6075
|
+
if (command === "models") return runModelsCommand(rest, io);
|
|
6076
|
+
if (command === "widgets") return await runWidgetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
5613
6077
|
if (command === "preview") {
|
|
5614
6078
|
return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
|
|
5615
6079
|
}
|
|
6080
|
+
if (command === "datasets") {
|
|
6081
|
+
return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6082
|
+
}
|
|
5616
6083
|
if (command === "dev") {
|
|
5617
6084
|
return await runDevCommand(rest, io, {
|
|
5618
6085
|
cwd: options.cwd,
|
|
5619
6086
|
...options.dev
|
|
5620
6087
|
});
|
|
5621
6088
|
}
|
|
5622
|
-
if (command === "login" || command === "logout" || command === "
|
|
6089
|
+
if (command === "login" || command === "logout" || command === "push" || command === "status") {
|
|
5623
6090
|
return await runCloudCommand(command, rest, io, {
|
|
5624
6091
|
cwd: options.cwd,
|
|
5625
6092
|
...options.cloud
|
|
@@ -5633,11 +6100,11 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5633
6100
|
}
|
|
5634
6101
|
let route = routeFromMode(parsed.mode);
|
|
5635
6102
|
if (route === void 0 && !io.canPrompt) requireModeForNonInteractive();
|
|
5636
|
-
const cwd =
|
|
6103
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5637
6104
|
await assertInitializableBuddyTarget(cwd);
|
|
5638
6105
|
if (parsed.directory !== void 0) {
|
|
5639
6106
|
if (!parsed.directory.trim()) throw new CliUsageError("--directory \u4E0D\u80FD\u4E3A\u7A7A");
|
|
5640
|
-
await assertInitializableBuddyTarget(
|
|
6107
|
+
await assertInitializableBuddyTarget(resolve15(cwd, parsed.directory));
|
|
5641
6108
|
}
|
|
5642
6109
|
await authenticate(options);
|
|
5643
6110
|
await resumeProjectArguments(parsed, cwd, options);
|
|
@@ -5779,6 +6246,7 @@ export {
|
|
|
5779
6246
|
assertOutsideBuddyProject,
|
|
5780
6247
|
buddyAgentConfig,
|
|
5781
6248
|
buildAgentEnvironment,
|
|
6249
|
+
checkWidgetDelivery,
|
|
5782
6250
|
combineBuddyProject,
|
|
5783
6251
|
createBuddyConfig,
|
|
5784
6252
|
createBuddyDevStarter,
|
|
@@ -5816,6 +6284,7 @@ export {
|
|
|
5816
6284
|
throwIfDevAborted,
|
|
5817
6285
|
validateBuddyAgentConfig,
|
|
5818
6286
|
validateBuddyConfig,
|
|
5819
|
-
validateBuddyProjectManifest
|
|
6287
|
+
validateBuddyProjectManifest,
|
|
6288
|
+
widgetDeliverySummary
|
|
5820
6289
|
};
|
|
5821
6290
|
//# sourceMappingURL=core.js.map
|