@my-life-buddies/cli 0.3.0 → 0.5.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 +54 -33
- package/dist/bin/core.js +907 -436
- 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.11.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: [],
|
|
@@ -142,18 +176,21 @@ const config = JSON.parse(readFileSync(new URL("./buddy.agent.json", import.meta
|
|
|
142
176
|
if (!config || config.schemaVersion !== 1 || typeof config.buddyId !== "string" || !config.buddyId) {
|
|
143
177
|
throw new Error("buddy.agent.json is not a complete Buddy project");
|
|
144
178
|
}
|
|
145
|
-
if (process.env.
|
|
146
|
-
throw new Error("
|
|
179
|
+
if (process.env.BUDDY_ID && process.env.BUDDY_ID !== config.buddyId) {
|
|
180
|
+
throw new Error("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
|
+
buddyId: process.env.BUDDY_ID,
|
|
190
|
+
token: process.env.BUDDY_TOKEN,
|
|
191
|
+
dataRequirements: config.datasets ?? [],
|
|
192
|
+
buddy: createBuddyOptions,
|
|
193
|
+
});
|
|
157
194
|
if (!lifecycle.signal.aborted) {
|
|
158
195
|
await new Promise((resolve) => lifecycle.signal.addEventListener("abort", resolve, { once: true }));
|
|
159
196
|
}
|
|
@@ -204,19 +241,19 @@ async function scaffoldPlatformAgent(projectRoot, name) {
|
|
|
204
241
|
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
242
|
}
|
|
206
243
|
}
|
|
207
|
-
for (const
|
|
244
|
+
for (const file2 of files) {
|
|
208
245
|
try {
|
|
209
|
-
handles.push({ path:
|
|
246
|
+
handles.push({ path: file2.path, handle: await open(file2.path, "wx", 384) });
|
|
210
247
|
} catch (cause) {
|
|
211
248
|
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${
|
|
249
|
+
if (!(await lstat(file2.path)).isFile()) {
|
|
250
|
+
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
251
|
}
|
|
215
252
|
}
|
|
216
253
|
}
|
|
217
254
|
for (const { path: filePath, handle } of handles) {
|
|
218
|
-
const
|
|
219
|
-
await handle.writeFile(
|
|
255
|
+
const file2 = files.find((candidate) => candidate.path === filePath);
|
|
256
|
+
await handle.writeFile(file2.content, "utf8");
|
|
220
257
|
await handle.sync();
|
|
221
258
|
}
|
|
222
259
|
return Object.freeze({ path, created: handles.length > 0 || createdDirectories.length > 0 });
|
|
@@ -312,6 +349,9 @@ function validateBuddyProjectManifest(value) {
|
|
|
312
349
|
return { ok: false, issues: schemaIssues(validateAgentSchema.errors) };
|
|
313
350
|
}
|
|
314
351
|
const manifest = value;
|
|
352
|
+
if (manifest.model !== void 0 && !MODEL_IDS.includes(manifest.model)) {
|
|
353
|
+
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` }] };
|
|
354
|
+
}
|
|
315
355
|
const seen = /* @__PURE__ */ new Set();
|
|
316
356
|
for (const [index, dataset] of (manifest.datasets ?? []).entries()) {
|
|
317
357
|
if (seen.has(dataset.id)) {
|
|
@@ -754,10 +794,597 @@ async function readBuddyConfig(projectRoot) {
|
|
|
754
794
|
return combineBuddyProject(await readBuddyAgentConfig(rootDirectory));
|
|
755
795
|
}
|
|
756
796
|
|
|
797
|
+
// packages/cli-core/src/project-context.ts
|
|
798
|
+
var BuddyProjectContextError = class extends Error {
|
|
799
|
+
constructor(code2, message, targetPath, options) {
|
|
800
|
+
super(message, options);
|
|
801
|
+
this.code = code2;
|
|
802
|
+
this.targetPath = targetPath;
|
|
803
|
+
this.name = "BuddyProjectContextError";
|
|
804
|
+
}
|
|
805
|
+
code;
|
|
806
|
+
targetPath;
|
|
807
|
+
};
|
|
808
|
+
async function existingDirectory(target) {
|
|
809
|
+
const requested = resolve3(target);
|
|
810
|
+
let entry;
|
|
811
|
+
try {
|
|
812
|
+
entry = await lstat4(requested);
|
|
813
|
+
} catch (cause) {
|
|
814
|
+
if (cause.code === "ENOENT") {
|
|
815
|
+
throw new BuddyProjectContextError(
|
|
816
|
+
"PROJECT_DIRECTORY_NOT_FOUND",
|
|
817
|
+
`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${requested}`,
|
|
818
|
+
requested,
|
|
819
|
+
{ cause }
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
throw cause;
|
|
823
|
+
}
|
|
824
|
+
if (!entry.isDirectory()) {
|
|
825
|
+
throw new BuddyProjectContextError(
|
|
826
|
+
"PROJECT_DIRECTORY_INVALID",
|
|
827
|
+
`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${requested}`,
|
|
828
|
+
requested
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
return realpath(requested);
|
|
832
|
+
}
|
|
833
|
+
async function projectFileState(directory, filename) {
|
|
834
|
+
const configPath = join3(directory, filename);
|
|
835
|
+
try {
|
|
836
|
+
const entry = await lstat4(configPath);
|
|
837
|
+
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
838
|
+
throw new BuddyProjectContextError(
|
|
839
|
+
"PROJECT_CONFIG_INVALID",
|
|
840
|
+
`${configPath} \u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u662F\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\u3002`,
|
|
841
|
+
configPath
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
return "valid";
|
|
845
|
+
} catch (cause) {
|
|
846
|
+
if (cause.code === "ENOENT") return "missing";
|
|
847
|
+
throw cause;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
async function searchUp(startDirectory) {
|
|
851
|
+
let directory = startDirectory;
|
|
852
|
+
while (true) {
|
|
853
|
+
if (await projectFileState(directory, BUDDY_AGENT_FILENAME) === "valid") return directory;
|
|
854
|
+
const parent = dirname(directory);
|
|
855
|
+
if (parent === directory) return void 0;
|
|
856
|
+
directory = parent;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
async function findBuddyProjectRoot(startDirectory) {
|
|
860
|
+
return searchUp(await existingDirectory(startDirectory));
|
|
861
|
+
}
|
|
862
|
+
async function requireBuddyProjectRoot(startDirectory) {
|
|
863
|
+
const requested = resolve3(startDirectory);
|
|
864
|
+
const root = await findBuddyProjectRoot(requested);
|
|
865
|
+
if (root !== void 0) return root;
|
|
866
|
+
throw new BuddyProjectContextError(
|
|
867
|
+
"PROJECT_NOT_FOUND",
|
|
868
|
+
[
|
|
869
|
+
"\u5F53\u524D\u76EE\u5F55\u4E0D\u5C5E\u4E8E\u642D\u642D\u9879\u76EE\u3002",
|
|
870
|
+
`\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`
|
|
871
|
+
].join("\n"),
|
|
872
|
+
requested
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
async function requireBuddyAgentProjectRoot(startDirectory) {
|
|
876
|
+
const root = await requireBuddyProjectRoot(startDirectory);
|
|
877
|
+
const agentPath = join3(root, BUDDY_AGENT_FILENAME);
|
|
878
|
+
try {
|
|
879
|
+
await readBuddyAgentConfig(root);
|
|
880
|
+
const missing = await missingBuddyAgentFiles(root);
|
|
881
|
+
if (missing.length > 0) throw new Error(`\u5DE5\u7A0B\u7F3A\u5C11\u6709\u6548\u6587\u4EF6\uFF1A${missing.join("\u3001")}`);
|
|
882
|
+
return root;
|
|
883
|
+
} catch (cause) {
|
|
884
|
+
throw new BuddyProjectContextError(
|
|
885
|
+
"PROJECT_NOT_FOUND",
|
|
886
|
+
[
|
|
887
|
+
`\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"}`,
|
|
888
|
+
"\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"
|
|
889
|
+
].join("\n"),
|
|
890
|
+
agentPath,
|
|
891
|
+
{ cause }
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
async function nearestExistingDirectory(target) {
|
|
896
|
+
let candidate = resolve3(target);
|
|
897
|
+
while (true) {
|
|
898
|
+
try {
|
|
899
|
+
const entry = await lstat4(candidate);
|
|
900
|
+
if (!entry.isDirectory()) {
|
|
901
|
+
throw new BuddyProjectContextError(
|
|
902
|
+
"PROJECT_DIRECTORY_INVALID",
|
|
903
|
+
`\u521D\u59CB\u5316\u76EE\u6807\u7684\u5DF2\u6709\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${candidate}`,
|
|
904
|
+
candidate
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
return realpath(candidate);
|
|
908
|
+
} catch (cause) {
|
|
909
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
910
|
+
const parent = dirname(candidate);
|
|
911
|
+
if (parent === candidate) throw cause;
|
|
912
|
+
candidate = parent;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
async function assertOutsideBuddyProject(target) {
|
|
917
|
+
const requested = resolve3(target);
|
|
918
|
+
const container = await nearestExistingDirectory(requested);
|
|
919
|
+
const projectRoot = await searchUp(container);
|
|
920
|
+
if (projectRoot === void 0) return;
|
|
921
|
+
throw new BuddyProjectContextError(
|
|
922
|
+
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
923
|
+
[
|
|
924
|
+
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
925
|
+
`\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`
|
|
926
|
+
].join("\n"),
|
|
927
|
+
requested
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
async function assertInitializableBuddyTarget(target) {
|
|
931
|
+
const requested = resolve3(target);
|
|
932
|
+
const container = await nearestExistingDirectory(requested);
|
|
933
|
+
const projectRoot = await searchUp(container);
|
|
934
|
+
if (projectRoot === void 0) return;
|
|
935
|
+
let requestedRoot = requested;
|
|
936
|
+
try {
|
|
937
|
+
requestedRoot = await realpath(requested);
|
|
938
|
+
} catch (cause) {
|
|
939
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
940
|
+
}
|
|
941
|
+
if (projectRoot === requestedRoot) {
|
|
942
|
+
try {
|
|
943
|
+
await readBuddyProjectManifest(projectRoot);
|
|
944
|
+
return;
|
|
945
|
+
} catch (cause) {
|
|
946
|
+
throw new BuddyProjectContextError(
|
|
947
|
+
"PROJECT_CONFIG_INVALID",
|
|
948
|
+
`${join3(projectRoot, BUDDY_AGENT_FILENAME)} \u4E0D\u662F\u6709\u6548\u7684\u9879\u76EE\u6E05\u5355\u3002`,
|
|
949
|
+
requested,
|
|
950
|
+
{ cause }
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
throw new BuddyProjectContextError(
|
|
955
|
+
"PROJECT_ALREADY_CONTAINS_TARGET",
|
|
956
|
+
[
|
|
957
|
+
`\u521D\u59CB\u5316\u76EE\u6807\u5DF2\u7ECF\u4F4D\u4E8E\u5B8C\u6574\u7684\u642D\u642D\u9879\u76EE\u4E2D\uFF1A${projectRoot}`,
|
|
958
|
+
"\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"
|
|
959
|
+
].join("\n"),
|
|
960
|
+
requested
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// packages/cli-core/src/widget-delivery.ts
|
|
965
|
+
import { createHash } from "node:crypto";
|
|
966
|
+
import { lstat as lstat5, readFile as readFile3, realpath as realpath2, readdir as readdir2, mkdir as mkdir3, writeFile, rename as rename2 } from "node:fs/promises";
|
|
967
|
+
import { join as join4, resolve as resolve4 } from "node:path";
|
|
968
|
+
var LOCK = "widget-design.lock.json";
|
|
969
|
+
var IMPLEMENTATION = "widget-implementation.json";
|
|
970
|
+
var RECEIPTS = ".buddy/widget-verification.json";
|
|
971
|
+
var idPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
|
|
972
|
+
var hashPattern = /^[a-f0-9]{64}$/u;
|
|
973
|
+
var sha = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
974
|
+
var object = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
975
|
+
var text = (value) => typeof value === "string" && value.trim().length > 0;
|
|
976
|
+
var WIDGET_REVIEW_CHECKS = ["visual", "interactions", "data-update", "trigger", "read-only"];
|
|
977
|
+
var WIDGET_REVIEW_STATES = ["initial", "partial", "updated"];
|
|
978
|
+
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";
|
|
979
|
+
function portable(path) {
|
|
980
|
+
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");
|
|
981
|
+
return path;
|
|
982
|
+
}
|
|
983
|
+
async function file(root, path, optional = false) {
|
|
984
|
+
portable(path);
|
|
985
|
+
const base = await realpath2(root);
|
|
986
|
+
let cursor = base;
|
|
987
|
+
const parts = path.split("/");
|
|
988
|
+
for (const [index, part] of parts.entries()) {
|
|
989
|
+
cursor = join4(cursor, part);
|
|
990
|
+
const stat2 = await lstat5(cursor).catch((e) => {
|
|
991
|
+
if (e.code === "ENOENT") return void 0;
|
|
992
|
+
throw e;
|
|
993
|
+
});
|
|
994
|
+
if (!stat2) {
|
|
995
|
+
if (optional) return void 0;
|
|
996
|
+
throw new Error(`\u7F3A\u5C11\u6587\u4EF6\uFF1A${path}`);
|
|
997
|
+
}
|
|
998
|
+
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}`);
|
|
999
|
+
if (index === parts.length - 1 && stat2.size > 8 * 1024 * 1024) throw new Error(`\u6587\u4EF6\u8D85\u8FC7 8 MB\uFF1A${path}`);
|
|
1000
|
+
}
|
|
1001
|
+
return readFile3(cursor);
|
|
1002
|
+
}
|
|
1003
|
+
async function jsonFile(root, path, optional = false) {
|
|
1004
|
+
const bytes = await file(root, path, optional);
|
|
1005
|
+
if (!bytes) return void 0;
|
|
1006
|
+
try {
|
|
1007
|
+
return JSON.parse(bytes.toString("utf8"));
|
|
1008
|
+
} catch {
|
|
1009
|
+
throw new Error(`JSON \u65E0\u6548\uFF1A${path}`);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
async function save(root, path, value) {
|
|
1013
|
+
portable(path);
|
|
1014
|
+
let cursor = resolve4(root);
|
|
1015
|
+
for (const segment of path.split("/").slice(0, -1)) {
|
|
1016
|
+
cursor = join4(cursor, segment);
|
|
1017
|
+
await mkdir3(cursor).catch((e) => {
|
|
1018
|
+
if (e.code !== "EEXIST") throw e;
|
|
1019
|
+
});
|
|
1020
|
+
const stat2 = await lstat5(cursor);
|
|
1021
|
+
if (!stat2.isDirectory() || stat2.isSymbolicLink()) throw new Error("\u5199\u5165\u76EE\u5F55\u4E0D\u5B89\u5168");
|
|
1022
|
+
}
|
|
1023
|
+
await file(root, path, true);
|
|
1024
|
+
const target = join4(root, path), temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
1025
|
+
await writeFile(temp, JSON.stringify(value, null, 2) + "\n", { flag: "wx", mode: 384 });
|
|
1026
|
+
await rename2(temp, target);
|
|
1027
|
+
}
|
|
1028
|
+
function validateLock(value) {
|
|
1029
|
+
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");
|
|
1030
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1031
|
+
for (const item of value.items) {
|
|
1032
|
+
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");
|
|
1033
|
+
seen.add(item.id);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
async function creatorDesign(root) {
|
|
1037
|
+
const session = await jsonFile(root, ".buddy/creator/session.json", true);
|
|
1038
|
+
if (!session) return void 0;
|
|
1039
|
+
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");
|
|
1040
|
+
const workspace = `.buddy/creator/workspaces/${session.creationKey}`;
|
|
1041
|
+
const state2 = await jsonFile(root, `${workspace}/state.json`);
|
|
1042
|
+
if (!state2.artifacts?.["widget.catalog"]) return void 0;
|
|
1043
|
+
const completion = await jsonFile(root, `${workspace}/completion.json`);
|
|
1044
|
+
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");
|
|
1045
|
+
const directory = `${workspace}/deliverables/${state2.revision}`;
|
|
1046
|
+
const manifest = await jsonFile(root, `${directory}/MANIFEST.json`);
|
|
1047
|
+
if (state2.buddyId !== session.creationKey || manifest.buddyId !== state2.buddyId) throw new Error("Creator \u4F5C\u54C1\u4E0E\u4EA4\u4ED8\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1048
|
+
if (manifest.complete !== true || manifest.revision !== state2.revision || !Array.isArray(manifest.files)) throw new Error("\u8BBE\u8BA1\u4EA4\u4ED8\u6E05\u5355\u65E0\u6548");
|
|
1049
|
+
const artifact = async (path) => {
|
|
1050
|
+
const bytes = await file(root, `${directory}/${path}`);
|
|
1051
|
+
const entry = manifest.files.find((e) => e.path === path);
|
|
1052
|
+
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`);
|
|
1053
|
+
return bytes;
|
|
1054
|
+
};
|
|
1055
|
+
const confirmed = (id, hash) => state2.artifacts?.[id]?.hash === hash && state2.confirmations?.some((c) => c.objectId === id && c.hash === hash && c.decision === "confirmed" && !c.invalidatedBy);
|
|
1056
|
+
const catalog = JSON.parse((await artifact("widgets/catalog.json")).toString());
|
|
1057
|
+
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");
|
|
1058
|
+
const items = [];
|
|
1059
|
+
for (const item of catalog.data.widgets) {
|
|
1060
|
+
if (!text(item.id) || item.id.length > 64 || !idPattern.test(item.id)) throw new Error("\u8BBE\u8BA1\u7C7B\u578B ID \u65E0\u6548");
|
|
1061
|
+
const prefix = `widgets/${item.id}`;
|
|
1062
|
+
const prototype = JSON.parse((await artifact(`${prefix}/prototype.json`)).toString());
|
|
1063
|
+
const acceptance = JSON.parse((await artifact(`${prefix}/acceptance.json`)).toString()).artifact;
|
|
1064
|
+
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}`);
|
|
1065
|
+
for (const asset of ["index.html", "style.css", "app.js"]) await artifact(`${prefix}/${asset}`);
|
|
1066
|
+
items.push({ id: item.id, name: item.name, prototypeHash: prototype.hash, fields: item.fields.map((f) => f.key) });
|
|
1067
|
+
}
|
|
1068
|
+
const result = { schemaVersion: 1, buddyId: session.platformBuddyId, revision: state2.revision, catalogHash: catalog.hash, items };
|
|
1069
|
+
validateLock(result);
|
|
1070
|
+
return result;
|
|
1071
|
+
}
|
|
1072
|
+
async function syncWidgetDesign(root) {
|
|
1073
|
+
const design = await creatorDesign(root);
|
|
1074
|
+
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");
|
|
1075
|
+
const config = await jsonFile(root, "buddy.agent.json");
|
|
1076
|
+
if (config.buddyId !== design.buddyId) throw new Error("\u8BBE\u8BA1\u4E0E\u642D\u5B50\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1077
|
+
const old = await jsonFile(root, IMPLEMENTATION, true);
|
|
1078
|
+
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");
|
|
1079
|
+
const mappings = design.items.map((item) => old?.widgets.find((w) => w.designId === item.id) ?? {
|
|
1080
|
+
designId: item.id,
|
|
1081
|
+
typeId: item.id,
|
|
1082
|
+
prototypeHash: item.prototypeHash,
|
|
1083
|
+
fields: Object.fromEntries(item.fields.map((key) => [key, ""])),
|
|
1084
|
+
lifecycle: { create: [], update: [], present: [] }
|
|
1085
|
+
});
|
|
1086
|
+
const next = { schemaVersion: 1, widgets: mappings };
|
|
1087
|
+
if (JSON.stringify(old) !== JSON.stringify(next)) await save(root, IMPLEMENTATION, next);
|
|
1088
|
+
await save(root, LOCK, design);
|
|
1089
|
+
}
|
|
1090
|
+
async function sourceSnapshot(root) {
|
|
1091
|
+
const result = [];
|
|
1092
|
+
const visit = async (path, depth = 0) => {
|
|
1093
|
+
if (depth > 32 || result.length > 1e4) throw new Error("\u5B9E\u73B0\u6E90\u7801\u8D85\u51FA\u68C0\u67E5\u8303\u56F4");
|
|
1094
|
+
const stat2 = await lstat5(join4(root, path)).catch((e) => {
|
|
1095
|
+
if (e.code === "ENOENT") return void 0;
|
|
1096
|
+
throw e;
|
|
1097
|
+
});
|
|
1098
|
+
if (!stat2) return;
|
|
1099
|
+
if (stat2.isSymbolicLink()) throw new Error(`\u5B9E\u73B0\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5\uFF1A${path}`);
|
|
1100
|
+
if (stat2.isDirectory()) {
|
|
1101
|
+
for (const name of (await readdir2(join4(root, path))).sort()) await visit(`${path}/${name}`, depth + 1);
|
|
1102
|
+
} else result.push([path, sha(await file(root, path))]);
|
|
1103
|
+
};
|
|
1104
|
+
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);
|
|
1105
|
+
return result;
|
|
1106
|
+
}
|
|
1107
|
+
async function checkWidgetDelivery(root) {
|
|
1108
|
+
const report = { status: "not-configured", items: [], issues: [], notice: NOTICE };
|
|
1109
|
+
try {
|
|
1110
|
+
const current = await creatorDesign(root);
|
|
1111
|
+
const lockBytes = await file(root, LOCK, true);
|
|
1112
|
+
let lock = lockBytes ? JSON.parse(lockBytes.toString()) : void 0;
|
|
1113
|
+
if (!current && !lock) {
|
|
1114
|
+
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");
|
|
1115
|
+
return report;
|
|
1116
|
+
}
|
|
1117
|
+
report.status = "incomplete";
|
|
1118
|
+
if (!lock) {
|
|
1119
|
+
report.issues.push("\u5DF2\u9A8C\u6536\u8BBE\u8BA1\u5C1A\u672A\u7ED1\u5B9A\u5230\u5DE5\u7A0B\uFF0C\u8BF7\u8FD0\u884C buddy-cli widgets sync");
|
|
1120
|
+
lock = current;
|
|
1121
|
+
}
|
|
1122
|
+
validateLock(lock);
|
|
1123
|
+
report.designRevision = lock.revision;
|
|
1124
|
+
const config = await jsonFile(root, "buddy.agent.json");
|
|
1125
|
+
if (config.buddyId !== lock.buddyId) throw new Error("\u8BBE\u8BA1\u4E0E\u642D\u5B50\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1126
|
+
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");
|
|
1127
|
+
const implementationBytes = await file(root, IMPLEMENTATION, true);
|
|
1128
|
+
const implementation = implementationBytes ? JSON.parse(implementationBytes.toString()) : void 0;
|
|
1129
|
+
const mappings = implementation?.schemaVersion === 1 && Array.isArray(implementation.widgets) ? implementation.widgets : [];
|
|
1130
|
+
if (!implementation || mappings.length !== lock.items.length) report.issues.push("\u5B9E\u73B0\u6620\u5C04\u4E0E\u5DF2\u9A8C\u6536\u6E05\u5355\u6570\u91CF\u4E0D\u7B26");
|
|
1131
|
+
const receipts = await jsonFile(root, RECEIPTS, true);
|
|
1132
|
+
const snapshot = await sourceSnapshot(root);
|
|
1133
|
+
report.files = snapshot.map(([path, digest2]) => ({ path, digest: `sha256:${digest2}` }));
|
|
1134
|
+
if (lockBytes) report.files.push({ path: LOCK, digest: `sha256:${sha(lockBytes)}` });
|
|
1135
|
+
if (implementationBytes) report.files.push({ path: IMPLEMENTATION, digest: `sha256:${sha(implementationBytes)}` });
|
|
1136
|
+
const usedTypes = /* @__PURE__ */ new Set();
|
|
1137
|
+
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");
|
|
1138
|
+
for (const item of lock.items) {
|
|
1139
|
+
const row = { id: item.id, name: item.name, status: "missing", issues: [] };
|
|
1140
|
+
report.items.push(row);
|
|
1141
|
+
try {
|
|
1142
|
+
const matches = mappings.filter((w) => w.designId === item.id);
|
|
1143
|
+
if (matches.length !== 1) throw new Error("\u7F3A\u5C11\u6216\u91CD\u590D\u7684\u5B9E\u73B0\u6620\u5C04");
|
|
1144
|
+
const m = matches[0];
|
|
1145
|
+
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");
|
|
1146
|
+
usedTypes.add(m.typeId);
|
|
1147
|
+
row.typeId = m.typeId;
|
|
1148
|
+
if (m.prototypeHash !== item.prototypeHash) row.issues.push("\u5B9E\u73B0\u7ED1\u5B9A\u7684\u539F\u578B\u7248\u672C\u5DF2\u8FC7\u671F");
|
|
1149
|
+
const html = (await file(root, `widgets/${m.typeId}/index.html`)).toString();
|
|
1150
|
+
if (!html.trim()) throw new Error("\u9875\u9762\u4E3A\u7A7A");
|
|
1151
|
+
const schema = await jsonFile(root, `widgets/${m.typeId}/schema.json`);
|
|
1152
|
+
if (!object(schema.modules) || !Object.keys(schema.modules).length) throw new Error("\u7F3A\u5C11\u6709\u6548\u7684\u6570\u636E\u6A21\u5757");
|
|
1153
|
+
for (const key of item.fields) {
|
|
1154
|
+
const binding = m.fields?.[key];
|
|
1155
|
+
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`);
|
|
1156
|
+
}
|
|
1157
|
+
for (const phase of ["create", "update", "present"]) {
|
|
1158
|
+
const paths = m.lifecycle?.[phase];
|
|
1159
|
+
if (!Array.isArray(paths) || !paths.length || paths.length > 50 || !paths.every(text)) {
|
|
1160
|
+
row.issues.push(`\u7F3A\u5C11${phase}\u4EE3\u7801\u8DEF\u5F84`);
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
for (const path of paths) {
|
|
1164
|
+
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");
|
|
1165
|
+
if (!(await file(root, path)).toString().trim()) throw new Error(`\u4EE3\u7801\u4E3A\u7A7A\uFF1A${path}`);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
if (row.issues.length) continue;
|
|
1169
|
+
row.status = "implemented";
|
|
1170
|
+
row.implementationDigest = sha(JSON.stringify({ design: lock, mapping: m, snapshot }));
|
|
1171
|
+
const receipt = receipts?.items?.[item.id];
|
|
1172
|
+
if (receipt?.implementationDigest === row.implementationDigest) {
|
|
1173
|
+
validateEvidence(receipt.evidence);
|
|
1174
|
+
if (!Array.isArray(receipt.artifacts) || receipt.artifacts.length !== 3) throw new Error("\u6838\u5BF9\u8BC1\u636E\u8BB0\u5F55\u65E0\u6548");
|
|
1175
|
+
for (const [index, artifact] of receipt.artifacts.entries()) {
|
|
1176
|
+
if (artifact.path !== receipt.evidence.states[WIDGET_REVIEW_STATES[index]].artifact) throw new Error("\u6838\u5BF9\u8BC1\u636E\u8DEF\u5F84\u4E0D\u5339\u914D");
|
|
1177
|
+
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");
|
|
1178
|
+
}
|
|
1179
|
+
if (!receipt.artifacts.length) throw new Error("\u7F3A\u5C11\u9884\u89C8\u6838\u5BF9\u8BC1\u636E");
|
|
1180
|
+
row.status = "verified";
|
|
1181
|
+
} 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");
|
|
1182
|
+
} catch (cause) {
|
|
1183
|
+
row.issues.push(cause instanceof Error ? cause.message : String(cause));
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
if (!report.issues.length && report.items.every((item) => item.status === "verified" && !item.issues.length)) report.status = "ready";
|
|
1187
|
+
} catch (cause) {
|
|
1188
|
+
report.status = "incomplete";
|
|
1189
|
+
report.issues.push(cause instanceof Error ? cause.message : String(cause));
|
|
1190
|
+
}
|
|
1191
|
+
return report;
|
|
1192
|
+
}
|
|
1193
|
+
function validateEvidence(value) {
|
|
1194
|
+
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");
|
|
1195
|
+
const url = new URL(value.previewUrl);
|
|
1196
|
+
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");
|
|
1197
|
+
for (const state2 of WIDGET_REVIEW_STATES) {
|
|
1198
|
+
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`);
|
|
1199
|
+
portable(value.states[state2].artifact);
|
|
1200
|
+
}
|
|
1201
|
+
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`);
|
|
1202
|
+
}
|
|
1203
|
+
async function recordWidgetVerification(root, designId, evidencePath) {
|
|
1204
|
+
const report = await checkWidgetDelivery(root);
|
|
1205
|
+
const row = report.items.find((item) => item.id === designId);
|
|
1206
|
+
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");
|
|
1207
|
+
const evidence = await jsonFile(root, evidencePath);
|
|
1208
|
+
validateEvidence(evidence);
|
|
1209
|
+
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");
|
|
1210
|
+
const artifacts = [];
|
|
1211
|
+
for (const state2 of WIDGET_REVIEW_STATES) {
|
|
1212
|
+
const path = evidence.states[state2].artifact;
|
|
1213
|
+
artifacts.push({ path, digest: sha(await file(root, path)) });
|
|
1214
|
+
}
|
|
1215
|
+
const old = await jsonFile(root, RECEIPTS, true);
|
|
1216
|
+
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");
|
|
1217
|
+
await save(root, RECEIPTS, { schemaVersion: 1, items: { ...old?.items, [designId]: {
|
|
1218
|
+
implementationDigest: row.implementationDigest,
|
|
1219
|
+
evidence,
|
|
1220
|
+
artifacts
|
|
1221
|
+
} } });
|
|
1222
|
+
}
|
|
1223
|
+
function widgetDeliverySummary(report) {
|
|
1224
|
+
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";
|
|
1225
|
+
return [
|
|
1226
|
+
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",
|
|
1227
|
+
...report.issues,
|
|
1228
|
+
...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") : ""}`),
|
|
1229
|
+
report.notice
|
|
1230
|
+
].join("\n");
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// packages/cli-core/src/commands/widgets.ts
|
|
1234
|
+
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
|
|
1235
|
+
buddy-cli widgets sync [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
1236
|
+
buddy-cli widgets check [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1237
|
+
buddy-cli widgets verify --id <\u8BBE\u8BA1ID> --evidence <\u9879\u76EE\u5185\u8BB0\u5F55.json> [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
1238
|
+
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
|
|
1239
|
+
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
|
|
1240
|
+
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`;
|
|
1241
|
+
async function runWidgetsCommand(args, io, cwd) {
|
|
1242
|
+
try {
|
|
1243
|
+
const [action, ...rest] = args;
|
|
1244
|
+
if (!action || action === "--help" || action === "help") {
|
|
1245
|
+
io.write(WIDGET_COMMAND_HELP);
|
|
1246
|
+
return { exitCode: 0 };
|
|
1247
|
+
}
|
|
1248
|
+
if (!["sync", "check", "verify"].includes(action)) throw new Error(WIDGET_COMMAND_HELP);
|
|
1249
|
+
const flags = /* @__PURE__ */ new Map();
|
|
1250
|
+
for (let i = 0; i < rest.length; i++) {
|
|
1251
|
+
const key = rest[i];
|
|
1252
|
+
if (!["--directory", "--id", "--evidence", "--json"].includes(key) || flags.has(key)) throw new Error(`\u4E0D\u652F\u6301\u6216\u91CD\u590D\u7684\u53C2\u6570\uFF1A${key}`);
|
|
1253
|
+
if (key === "--json") flags.set(key, "true");
|
|
1254
|
+
else {
|
|
1255
|
+
const value = rest[++i];
|
|
1256
|
+
if (!value || value.startsWith("--")) throw new Error(`\u7F3A\u5C11\u53C2\u6570\uFF1A${key}`);
|
|
1257
|
+
flags.set(key, value);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
if (action !== "verify" && (flags.has("--id") || flags.has("--evidence"))) throw new Error("--id / --evidence \u4EC5\u7528\u4E8E verify");
|
|
1261
|
+
if (action !== "check" && flags.has("--json")) throw new Error("--json \u4EC5\u7528\u4E8E check");
|
|
1262
|
+
const root = await findBuddyProjectRoot(resolve5(cwd, flags.get("--directory") ?? "."));
|
|
1263
|
+
if (!root) throw new Error("\u8BF7\u5728\u642D\u5B50\u5DE5\u7A0B\u4E2D\u8FD0\u884C\uFF0C\u6216\u6307\u5B9A --directory");
|
|
1264
|
+
if (action === "sync") {
|
|
1265
|
+
await syncWidgetDesign(root);
|
|
1266
|
+
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");
|
|
1267
|
+
return { exitCode: 0 };
|
|
1268
|
+
}
|
|
1269
|
+
if (action === "verify") {
|
|
1270
|
+
if (!flags.get("--id") || !flags.get("--evidence")) throw new Error("verify \u9700\u8981 --id \u4E0E --evidence");
|
|
1271
|
+
await recordWidgetVerification(root, flags.get("--id"), flags.get("--evidence"));
|
|
1272
|
+
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");
|
|
1273
|
+
}
|
|
1274
|
+
const report = await checkWidgetDelivery(root);
|
|
1275
|
+
io.write(flags.has("--json") ? JSON.stringify(report, null, 2) : widgetDeliverySummary(report));
|
|
1276
|
+
return { exitCode: report.status === "incomplete" ? 1 : 0 };
|
|
1277
|
+
} catch (cause) {
|
|
1278
|
+
io.writeError(cause instanceof Error ? cause.message : String(cause));
|
|
1279
|
+
return { exitCode: 1 };
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// packages/cli-core/src/index.ts
|
|
1284
|
+
import { lstat as lstat10 } from "node:fs/promises";
|
|
1285
|
+
|
|
1286
|
+
// packages/cli-core/src/commands/datasets.ts
|
|
1287
|
+
import { resolve as resolve7 } from "node:path";
|
|
1288
|
+
|
|
1289
|
+
// packages/cli-core/src/config/runtime-catalog.ts
|
|
1290
|
+
import { readFile as readFile4, realpath as realpath3 } from "node:fs/promises";
|
|
1291
|
+
import { createRequire } from "node:module";
|
|
1292
|
+
import { resolve as resolve6 } from "node:path";
|
|
1293
|
+
import { pathToFileURL } from "node:url";
|
|
1294
|
+
|
|
1295
|
+
// packages/cli-core/src/config/runtime-dependency.ts
|
|
1296
|
+
var BUDDY_RUNTIME_PACKAGE = "@my-life-buddies/buddy-runtime";
|
|
1297
|
+
function runtimeDependencyVersion(pkg) {
|
|
1298
|
+
if (typeof pkg !== "object" || pkg === null || !("dependencies" in pkg)) return void 0;
|
|
1299
|
+
const dependencies2 = pkg.dependencies;
|
|
1300
|
+
if (typeof dependencies2 !== "object" || dependencies2 === null || !(BUDDY_RUNTIME_PACKAGE in dependencies2)) return void 0;
|
|
1301
|
+
const version = dependencies2[BUDDY_RUNTIME_PACKAGE];
|
|
1302
|
+
return typeof version === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version) ? version : void 0;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// packages/cli-core/src/config/runtime-catalog.ts
|
|
1306
|
+
async function readRuntimeDataCatalog(projectRoot) {
|
|
1307
|
+
const packagePath = resolve6(projectRoot, "package.json");
|
|
1308
|
+
const expected = runtimeDependencyVersion(JSON.parse(await readFile4(packagePath, "utf8")));
|
|
1309
|
+
if (!expected) throw new Error(`package.json \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
1310
|
+
const projectRequire = createRequire(packagePath);
|
|
1311
|
+
let runtimePath;
|
|
1312
|
+
try {
|
|
1313
|
+
const installedPath = await realpath3(resolve6(projectRoot, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"));
|
|
1314
|
+
if (await realpath3(projectRequire.resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`)) !== installedPath) {
|
|
1315
|
+
throw new Error("Runtime \u5FC5\u987B\u5B89\u88C5\u5728\u5F53\u524D\u5DE5\u7A0B\u4E2D");
|
|
1316
|
+
}
|
|
1317
|
+
const installed = JSON.parse(await readFile4(installedPath, "utf8"));
|
|
1318
|
+
if (installed.version !== expected) throw new Error(`\u5DF2\u5B89\u88C5 ${installed.version}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
|
|
1319
|
+
runtimePath = projectRequire.resolve(BUDDY_RUNTIME_PACKAGE);
|
|
1320
|
+
} catch (cause) {
|
|
1321
|
+
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)}`);
|
|
1322
|
+
}
|
|
1323
|
+
const runtime = await import(pathToFileURL(runtimePath).href);
|
|
1324
|
+
const catalog = runtime.DATA_ACCESS_CAPABILITIES;
|
|
1325
|
+
if (!Array.isArray(catalog)) {
|
|
1326
|
+
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`);
|
|
1327
|
+
}
|
|
1328
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1329
|
+
const datasets = catalog.map((value) => {
|
|
1330
|
+
if (!value || typeof value !== "object") throw new Error("Runtime \u6570\u636E\u76EE\u5F55\u683C\u5F0F\u65E0\u6548");
|
|
1331
|
+
const item = value;
|
|
1332
|
+
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) {
|
|
1333
|
+
throw new Error("Runtime \u6570\u636E\u76EE\u5F55\u683C\u5F0F\u65E0\u6548\u6216\u5305\u542B\u91CD\u590D ID");
|
|
1334
|
+
}
|
|
1335
|
+
seen.add(item.id);
|
|
1336
|
+
return Object.freeze({ id: item.id, title: item.title, source: item.source, schemaVersion: item.schemaVersion });
|
|
1337
|
+
});
|
|
1338
|
+
return Object.freeze({ runtimeVersion: expected, datasets: Object.freeze(datasets) });
|
|
1339
|
+
}
|
|
1340
|
+
async function validateRuntimeDatasets(projectRoot, requirements = []) {
|
|
1341
|
+
const catalog = await readRuntimeDataCatalog(projectRoot);
|
|
1342
|
+
const ids = new Set(catalog.datasets.map((item) => item.id));
|
|
1343
|
+
for (const [index, item] of requirements.entries()) {
|
|
1344
|
+
if (!ids.has(item.id)) throw new Error(`/datasets/${index}/id\uFF1A\u5DE5\u7A0B Runtime ${catalog.runtimeVersion} \u4E0D\u652F\u6301 ${item.id}`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// packages/cli-core/src/commands/datasets.ts
|
|
1349
|
+
var DATASETS_COMMAND_HELP = `\u7528\u6CD5: buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1350
|
+
\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
|
|
1351
|
+
\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`;
|
|
1352
|
+
async function runDatasetsCommand(args, io, cwd) {
|
|
1353
|
+
try {
|
|
1354
|
+
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
1355
|
+
io.write(DATASETS_COMMAND_HELP);
|
|
1356
|
+
return { exitCode: 0 };
|
|
1357
|
+
}
|
|
1358
|
+
let directory;
|
|
1359
|
+
let json = false;
|
|
1360
|
+
for (let i = 0; i < args.length; i++) {
|
|
1361
|
+
if (args[i] === "--json" && !json) json = true;
|
|
1362
|
+
else if (args[i] === "--directory" && directory === void 0 && args[i + 1]?.trim() && !args[i + 1].startsWith("--")) directory = args[++i];
|
|
1363
|
+
else throw new Error(`datasets \u53C2\u6570\u65E0\u6548\uFF1A${args[i]}
|
|
1364
|
+
${DATASETS_COMMAND_HELP}`);
|
|
1365
|
+
}
|
|
1366
|
+
const root = await requireBuddyProjectRoot(resolve7(cwd, directory ?? "."));
|
|
1367
|
+
const catalog = await readRuntimeDataCatalog(root);
|
|
1368
|
+
if (json) io.write(JSON.stringify(catalog, null, 2));
|
|
1369
|
+
else {
|
|
1370
|
+
io.write(`\u5DE5\u7A0B Runtime ${catalog.runtimeVersion} \u63D0\u4F9B ${catalog.datasets.length} \u9879\u6570\u636E\u80FD\u529B\uFF1A`);
|
|
1371
|
+
for (const item of catalog.datasets) io.write(`- ${item.id} \u2014 ${item.title}\uFF08${item.source}\uFF0CSchema ${item.schemaVersion}\uFF09`);
|
|
1372
|
+
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");
|
|
1373
|
+
}
|
|
1374
|
+
return { exitCode: 0 };
|
|
1375
|
+
} catch (cause) {
|
|
1376
|
+
io.writeError(cause instanceof Error ? cause.message : String(cause));
|
|
1377
|
+
return { exitCode: 2 };
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// packages/cli-core/src/index.ts
|
|
1382
|
+
import { join as join11, resolve as resolve15 } from "node:path";
|
|
1383
|
+
|
|
757
1384
|
// packages/cli-core/src/init/creator.ts
|
|
758
1385
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
759
|
-
import { link as link2, lstat as
|
|
760
|
-
import { join as
|
|
1386
|
+
import { link as link2, lstat as lstat6, mkdir as mkdir4, readFile as readFile5, unlink as unlink3, writeFile as writeFile2 } from "node:fs/promises";
|
|
1387
|
+
import { join as join5 } from "node:path";
|
|
761
1388
|
import { fileURLToPath } from "node:url";
|
|
762
1389
|
|
|
763
1390
|
// packages/cli-core/src/init/development-handoff.ts
|
|
@@ -767,7 +1394,7 @@ var DEVELOPMENT_HANDOFF = `## \u5F00\u53D1\u5B8C\u6210\u540E\u4EA4\u7ED9\u5F00\u
|
|
|
767
1394
|
${DEVELOPMENT_HANDOFF_SUMMARY}
|
|
768
1395
|
|
|
769
1396
|
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
|
|
1397
|
+
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
1398
|
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
1399
|
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
1400
|
|
|
@@ -799,13 +1426,13 @@ Creator \u8DEF\u7EBF\u987B\u7B49\u56DB\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355
|
|
|
799
1426
|
| --- | --- |
|
|
800
1427
|
| \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
1428
|
| \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 \`
|
|
1429
|
+
| \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
1430
|
| \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
1431
|
| \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
1432
|
| \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\
|
|
1433
|
+
| \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
1434
|
|
|
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\
|
|
1435
|
+
\`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({ buddyId: process.env.BUDDY_ID, token: process.env.BUDDY_TOKEN, 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
1436
|
|
|
810
1437
|
### \u6309\u76EE\u5F55\u804C\u8D23\u7EC4\u7EC7\u5B9E\u73B0
|
|
811
1438
|
|
|
@@ -817,7 +1444,7 @@ Creator \u8DEF\u7EBF\u987B\u7B49\u56DB\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355
|
|
|
817
1444
|
| \`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
1445
|
| \`src/buddy-options.mjs\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u7EC4\u88C5\u5E76\u6CE8\u518C\u4F1A\u8BDD\u4E2D\u7684\u80FD\u529B |
|
|
819
1446
|
| \`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\
|
|
1447
|
+
| \`src/hooks/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E\u5DE5\u5177\u8C03\u7528\u524D\u540E\u3001\u505C\u6B62\u7B49\u94A9\u5B50 |
|
|
821
1448
|
| \`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
1449
|
| \`resources/\` | \u9ED8\u8BA4\u7A7A\u76EE\u5F55\uFF1B\u653E Runtime \u542F\u52A8\u65F6\u8BFB\u53D6\u7684 Markdown \u53C2\u8003\u8D44\u6599 |
|
|
823
1450
|
| \`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 +1455,22 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
|
|
|
828
1455
|
|
|
829
1456
|
\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
1457
|
|
|
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\
|
|
1458
|
+
\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
1459
|
|
|
833
1460
|
\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
1461
|
|
|
835
1462
|
|
|
836
1463
|
\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
1464
|
|
|
838
|
-
\u9700\u8981\u7528\u6237\u6570\u636E\u65F6\uFF0C\u5728 \`buddy.
|
|
1465
|
+
\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.11.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
|
|
1466
|
+
|
|
1467
|
+
\`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
|
|
1468
|
+
|
|
1469
|
+
\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.11.0\u3002
|
|
1470
|
+
|
|
1471
|
+
\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
|
|
1472
|
+
|
|
1473
|
+
\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
1474
|
|
|
840
1475
|
### \u53EA\u6709\u5173\u952E\u7F3A\u53E3\u624D\u8865\u95EE
|
|
841
1476
|
|
|
@@ -845,6 +1480,18 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
|
|
|
845
1480
|
|
|
846
1481
|
\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
1482
|
|
|
1483
|
+
### \u5C0F\u6302\u4EF6\u8BBE\u8BA1\u843D\u5730\u68C0\u67E5
|
|
1484
|
+
|
|
1485
|
+
\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
|
|
1486
|
+
|
|
1487
|
+
\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
|
|
1488
|
+
|
|
1489
|
+
\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
|
|
1490
|
+
|
|
1491
|
+
\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
|
|
1492
|
+
|
|
1493
|
+
\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
|
|
1494
|
+
|
|
848
1495
|
### \u6838\u5BF9\u5B9E\u73B0\uFF0C\u518D\u4EA4\u7ED9\u5F00\u53D1\u8005\u4F53\u9A8C
|
|
849
1496
|
|
|
850
1497
|
\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 +1542,16 @@ var REQUIRED = [
|
|
|
895
1542
|
var CreatorPreparationError = class extends Error {
|
|
896
1543
|
};
|
|
897
1544
|
async function state(path) {
|
|
898
|
-
return
|
|
1545
|
+
return lstat6(path).catch((cause) => {
|
|
899
1546
|
if (cause.code === "ENOENT") return void 0;
|
|
900
1547
|
throw cause;
|
|
901
1548
|
});
|
|
902
1549
|
}
|
|
903
1550
|
async function safeDirectory(path) {
|
|
904
|
-
await
|
|
1551
|
+
await mkdir4(path, { mode: 448 }).catch((cause) => {
|
|
905
1552
|
if (cause.code !== "EEXIST") throw cause;
|
|
906
1553
|
});
|
|
907
|
-
if (!(await
|
|
1554
|
+
if (!(await lstat6(path)).isDirectory()) {
|
|
908
1555
|
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
1556
|
}
|
|
910
1557
|
}
|
|
@@ -914,11 +1561,11 @@ async function safeRead(path) {
|
|
|
914
1561
|
if (!entry.isFile() || entry.size > MAX_LOCAL_FILE_BYTES) {
|
|
915
1562
|
throw new CreatorPreparationError(`\u521B\u4F5C\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6\u6216\u8D85\u51FA\u5927\u5C0F\u9650\u5236\uFF1A${path}`);
|
|
916
1563
|
}
|
|
917
|
-
return
|
|
1564
|
+
return readFile5(path);
|
|
918
1565
|
}
|
|
919
1566
|
async function writeOnce(path, bytes) {
|
|
920
1567
|
const temporary = `${path}.${randomUUID2()}.tmp`;
|
|
921
|
-
await
|
|
1568
|
+
await writeFile2(temporary, bytes, { flag: "wx", mode: 384 });
|
|
922
1569
|
try {
|
|
923
1570
|
await link2(temporary, path).catch((cause) => {
|
|
924
1571
|
if (cause.code !== "EEXIST") throw cause;
|
|
@@ -929,17 +1576,17 @@ async function writeOnce(path, bytes) {
|
|
|
929
1576
|
}
|
|
930
1577
|
async function bundledSkill(root) {
|
|
931
1578
|
for (const path of REQUIRED) {
|
|
932
|
-
if (!(await state(
|
|
1579
|
+
if (!(await state(join5(root, path)))?.isFile()) {
|
|
933
1580
|
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
1581
|
}
|
|
935
1582
|
}
|
|
936
1583
|
return root;
|
|
937
1584
|
}
|
|
938
1585
|
function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
939
|
-
const contextPath =
|
|
1586
|
+
const contextPath = join5(context.projectRoot, ".buddy", "creator", "initial-context.json");
|
|
940
1587
|
const openArgs = [
|
|
941
1588
|
"-B",
|
|
942
|
-
|
|
1589
|
+
join5(skillRoot, "scripts/buddy.py"),
|
|
943
1590
|
"open",
|
|
944
1591
|
"--workspace",
|
|
945
1592
|
workspacePath,
|
|
@@ -969,7 +1616,7 @@ function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
|
969
1616
|
|
|
970
1617
|
## \u73B0\u5728\u7531 Coding Agent \u6267\u884C
|
|
971
1618
|
|
|
972
|
-
1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(
|
|
1619
|
+
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
1620
|
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
1621
|
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
1622
|
|
|
@@ -1006,11 +1653,11 @@ ${DEVELOPMENT_HANDOFF}
|
|
|
1006
1653
|
`;
|
|
1007
1654
|
}
|
|
1008
1655
|
async function prepareCreatorSkill(context, io, options = {}) {
|
|
1009
|
-
const root =
|
|
1656
|
+
const root = join5(context.projectRoot, ".buddy", "creator");
|
|
1010
1657
|
try {
|
|
1011
|
-
await safeDirectory(
|
|
1658
|
+
await safeDirectory(join5(context.projectRoot, ".buddy"));
|
|
1012
1659
|
await safeDirectory(root);
|
|
1013
|
-
const sessionPath =
|
|
1660
|
+
const sessionPath = join5(root, "session.json");
|
|
1014
1661
|
await writeOnce(sessionPath, JSON.stringify({
|
|
1015
1662
|
schemaVersion: 1,
|
|
1016
1663
|
platformBuddyId: context.buddyId,
|
|
@@ -1022,11 +1669,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1022
1669
|
}
|
|
1023
1670
|
options.signal?.throwIfAborted();
|
|
1024
1671
|
const skillRoot = await bundledSkill(options.bundledSkillRoot ?? BUNDLED_CREATOR_SKILL_ROOT);
|
|
1025
|
-
await safeDirectory(
|
|
1026
|
-
const workspacePath =
|
|
1672
|
+
await safeDirectory(join5(root, "workspaces"));
|
|
1673
|
+
const workspacePath = join5(root, "workspaces", session.creationKey);
|
|
1027
1674
|
const workspace = await state(workspacePath);
|
|
1028
1675
|
if (workspace && !workspace.isDirectory()) throw new CreatorPreparationError("\u5DF2\u6709\u521B\u4F5C\u5DE5\u4F5C\u533A\u4E0D\u662F\u666E\u901A\u76EE\u5F55\u3002");
|
|
1029
|
-
const contextPath =
|
|
1676
|
+
const contextPath = join5(root, "initial-context.json");
|
|
1030
1677
|
await writeOnce(contextPath, JSON.stringify({
|
|
1031
1678
|
source: "platform_meta",
|
|
1032
1679
|
name: context.appName,
|
|
@@ -1042,7 +1689,7 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1042
1689
|
} catch {
|
|
1043
1690
|
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
1691
|
}
|
|
1045
|
-
const handoffPath =
|
|
1692
|
+
const handoffPath = join5(root, "HANDOFF.md");
|
|
1046
1693
|
const handoff = handoffMarkdown({
|
|
1047
1694
|
...context,
|
|
1048
1695
|
appName: initial.name,
|
|
@@ -1053,11 +1700,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1053
1700
|
if ((await safeRead(handoffPath))?.toString("utf8") !== handoff) {
|
|
1054
1701
|
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
1702
|
}
|
|
1056
|
-
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${
|
|
1703
|
+
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${join5(skillRoot, "SKILL.md")}`);
|
|
1057
1704
|
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
1705
|
io.write(DEVELOPMENT_HANDOFF_SUMMARY);
|
|
1059
1706
|
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:
|
|
1707
|
+
return { exitCode: 0, creator: { skillPath: join5(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
|
|
1061
1708
|
} catch (cause) {
|
|
1062
1709
|
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
1710
|
io.writeError(`Creator Skill \u51C6\u5907\u5931\u8D25\uFF1A${message}`);
|
|
@@ -1111,7 +1758,7 @@ function validateBuddyMeta(meta2) {
|
|
|
1111
1758
|
}
|
|
1112
1759
|
|
|
1113
1760
|
// packages/cli-core/src/commands/dev.ts
|
|
1114
|
-
import { resolve as
|
|
1761
|
+
import { resolve as resolve11 } from "node:path";
|
|
1115
1762
|
|
|
1116
1763
|
// packages/cli-core/src/platform-url.ts
|
|
1117
1764
|
var DEFAULT_MLB_TEST_URL = "http://47.116.168.81:8788";
|
|
@@ -1172,7 +1819,7 @@ function safeCredential(value, name) {
|
|
|
1172
1819
|
function endpoint(baseUrl, path) {
|
|
1173
1820
|
return new URL(path.replace(/^\//u, ""), baseUrl);
|
|
1174
1821
|
}
|
|
1175
|
-
function
|
|
1822
|
+
function object2(value) {
|
|
1176
1823
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1177
1824
|
}
|
|
1178
1825
|
async function responseJson(response) {
|
|
@@ -1214,7 +1861,7 @@ async function responseJson(response) {
|
|
|
1214
1861
|
}
|
|
1215
1862
|
}
|
|
1216
1863
|
function safeServerError(value, fallback) {
|
|
1217
|
-
const candidate =
|
|
1864
|
+
const candidate = object2(value)?.error;
|
|
1218
1865
|
if (typeof candidate !== "string" || !candidate.trim()) return fallback;
|
|
1219
1866
|
return candidate.slice(0, 1024).replace(/[\u0000-\u001f\u007f]/gu, " ");
|
|
1220
1867
|
}
|
|
@@ -1295,7 +1942,7 @@ var MlbDevRegistrationClient = class {
|
|
|
1295
1942
|
message: "buddy.agent.json \u4E2D\u7684 buddyId \u65E0\u6548\u3002"
|
|
1296
1943
|
});
|
|
1297
1944
|
}
|
|
1298
|
-
const body =
|
|
1945
|
+
const body = object2(await this.#request(
|
|
1299
1946
|
`/cli/buddies/${encodeURIComponent(request.devAgentId)}`,
|
|
1300
1947
|
{
|
|
1301
1948
|
method: "GET",
|
|
@@ -1316,7 +1963,7 @@ var MlbDevRegistrationClient = class {
|
|
|
1316
1963
|
});
|
|
1317
1964
|
}
|
|
1318
1965
|
async listOnline(options) {
|
|
1319
|
-
const body =
|
|
1966
|
+
const body = object2(await this.#request("/cli/fleet", {
|
|
1320
1967
|
method: "GET",
|
|
1321
1968
|
signal: options.signal
|
|
1322
1969
|
}));
|
|
@@ -1401,43 +2048,31 @@ function buildAgentEnvironment(options) {
|
|
|
1401
2048
|
});
|
|
1402
2049
|
}
|
|
1403
2050
|
environment.BUDDY_ENV = "dev";
|
|
1404
|
-
environment.
|
|
1405
|
-
environment.
|
|
2051
|
+
environment.BUDDY_ID = safeOpaqueValue(options.buddyId, "Buddy ID");
|
|
2052
|
+
environment.BUDDY_TOKEN = safeOpaqueValue(options.buddyToken, "Buddy token");
|
|
1406
2053
|
environment.MLB_GATEWAY_URL = gateway.href;
|
|
1407
2054
|
return environment;
|
|
1408
2055
|
}
|
|
1409
2056
|
|
|
1410
2057
|
// packages/cli-core/src/dev/orchestrator.ts
|
|
1411
|
-
import { createHash as
|
|
1412
|
-
import { readFile as
|
|
1413
|
-
import { join as
|
|
2058
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2059
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
2060
|
+
import { join as join7, resolve as resolve9 } from "node:path";
|
|
1414
2061
|
|
|
1415
2062
|
// packages/cli-core/src/dev/launch.ts
|
|
1416
2063
|
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);
|
|
2064
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2065
|
+
import { dirname as dirname2, resolve as resolve8 } from "node:path";
|
|
2066
|
+
async function officialAgentLaunchPlan(input) {
|
|
2067
|
+
const entry = resolve8(input.projectRoot, BUDDY_PLATFORM_START_ENTRY);
|
|
1433
2068
|
for (const [filename, kind] of [
|
|
1434
|
-
[
|
|
2069
|
+
[dirname2(BUDDY_PLATFORM_FACTORY_ENTRY), "directory"],
|
|
1435
2070
|
[BUDDY_PLATFORM_START_ENTRY, "file"],
|
|
1436
2071
|
[BUDDY_PLATFORM_FACTORY_ENTRY, "file"],
|
|
1437
2072
|
[BUDDY_PLATFORM_PACKAGE_JSON, "file"]
|
|
1438
2073
|
]) {
|
|
1439
2074
|
try {
|
|
1440
|
-
const entry2 = lstatSync(
|
|
2075
|
+
const entry2 = lstatSync(resolve8(input.projectRoot, filename));
|
|
1441
2076
|
if (!(kind === "directory" ? entry2.isDirectory() : entry2.isFile())) {
|
|
1442
2077
|
throw new Error(`${filename} must be a regular ${kind}`);
|
|
1443
2078
|
}
|
|
@@ -1450,18 +2085,19 @@ function officialAgentLaunchPlan(input) {
|
|
|
1450
2085
|
});
|
|
1451
2086
|
}
|
|
1452
2087
|
}
|
|
1453
|
-
const packagePath =
|
|
2088
|
+
const packagePath = resolve8(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
|
|
1454
2089
|
try {
|
|
1455
2090
|
const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
|
|
1456
2091
|
if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
1457
|
-
const runtimePath =
|
|
2092
|
+
const runtimePath = createRequire2(packagePath).resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`);
|
|
1458
2093
|
const installed = JSON.parse(readFileSync(runtimePath, "utf8")).version;
|
|
1459
2094
|
if (installed !== expected) throw new Error(`Runtime \u5DF2\u5B89\u88C5 ${installed}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
|
|
2095
|
+
await validateRuntimeDatasets(input.projectRoot, input.datasets);
|
|
1460
2096
|
} catch (cause) {
|
|
1461
2097
|
throw new BuddyDevError({
|
|
1462
2098
|
code: "DEV_CONFIG_INVALID",
|
|
1463
2099
|
phase: "config",
|
|
1464
|
-
message:
|
|
2100
|
+
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
2101
|
cause
|
|
1466
2102
|
});
|
|
1467
2103
|
}
|
|
@@ -1499,16 +2135,16 @@ var NodeAgentProcessLauncher = class {
|
|
|
1499
2135
|
}
|
|
1500
2136
|
let hasExited = false;
|
|
1501
2137
|
let spawned = false;
|
|
1502
|
-
const exit = new Promise((
|
|
2138
|
+
const exit = new Promise((resolve16) => {
|
|
1503
2139
|
child.once("error", (error2) => {
|
|
1504
2140
|
if (!spawned) {
|
|
1505
2141
|
hasExited = true;
|
|
1506
|
-
|
|
2142
|
+
resolve16({ code: null, signal: null, error: error2 });
|
|
1507
2143
|
}
|
|
1508
2144
|
});
|
|
1509
2145
|
child.once("exit", (code2, signal) => {
|
|
1510
2146
|
hasExited = true;
|
|
1511
|
-
|
|
2147
|
+
resolve16({ code: code2, signal });
|
|
1512
2148
|
});
|
|
1513
2149
|
});
|
|
1514
2150
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -1524,11 +2160,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
1524
2160
|
}
|
|
1525
2161
|
});
|
|
1526
2162
|
try {
|
|
1527
|
-
await new Promise((
|
|
2163
|
+
await new Promise((resolve16, reject) => {
|
|
1528
2164
|
const onSpawn = () => {
|
|
1529
2165
|
spawned = true;
|
|
1530
2166
|
child.off("error", onError);
|
|
1531
|
-
|
|
2167
|
+
resolve16();
|
|
1532
2168
|
};
|
|
1533
2169
|
const onError = (error2) => {
|
|
1534
2170
|
child.off("spawn", onSpawn);
|
|
@@ -1567,11 +2203,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
1567
2203
|
};
|
|
1568
2204
|
function waitForExit(process2, timeoutMs) {
|
|
1569
2205
|
if (process2.hasExited()) return Promise.resolve(true);
|
|
1570
|
-
return new Promise((
|
|
1571
|
-
const timeout = setTimeout(() =>
|
|
2206
|
+
return new Promise((resolve16) => {
|
|
2207
|
+
const timeout = setTimeout(() => resolve16(false), timeoutMs);
|
|
1572
2208
|
process2.exit.then(() => {
|
|
1573
2209
|
clearTimeout(timeout);
|
|
1574
|
-
|
|
2210
|
+
resolve16(true);
|
|
1575
2211
|
});
|
|
1576
2212
|
});
|
|
1577
2213
|
}
|
|
@@ -1654,7 +2290,7 @@ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
|
|
|
1654
2290
|
while (isGroupAlive(pid)) {
|
|
1655
2291
|
const remaining = deadline - Date.now();
|
|
1656
2292
|
if (remaining <= 0) return false;
|
|
1657
|
-
await new Promise((
|
|
2293
|
+
await new Promise((resolve16) => setTimeout(resolve16, Math.min(25, remaining)));
|
|
1658
2294
|
}
|
|
1659
2295
|
return true;
|
|
1660
2296
|
}
|
|
@@ -1668,10 +2304,10 @@ function processTreeControllerFor(platform) {
|
|
|
1668
2304
|
}
|
|
1669
2305
|
|
|
1670
2306
|
// packages/cli-core/src/dev/instance-lock.ts
|
|
1671
|
-
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
1672
|
-
import { mkdir as
|
|
2307
|
+
import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
|
|
2308
|
+
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
2309
|
import { homedir } from "node:os";
|
|
1674
|
-
import { join as
|
|
2310
|
+
import { join as join6 } from "node:path";
|
|
1675
2311
|
function alreadyRunningError() {
|
|
1676
2312
|
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
2313
|
return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
|
|
@@ -1691,20 +2327,20 @@ function code(error2) {
|
|
|
1691
2327
|
return error2?.code;
|
|
1692
2328
|
}
|
|
1693
2329
|
async function acquireDevInstanceLock(options) {
|
|
1694
|
-
const root = options.directory ??
|
|
1695
|
-
const key =
|
|
2330
|
+
const root = options.directory ?? join6(homedir(), ".buddy", "dev-locks");
|
|
2331
|
+
const key = createHash2("sha256").update(`${new URL(options.gatewayUrl).origin}
|
|
1696
2332
|
${options.devAgentId}`).digest("hex");
|
|
1697
|
-
const path =
|
|
2333
|
+
const path = join6(root, key);
|
|
1698
2334
|
const ownerName = `owner-${randomUUID3()}.json`;
|
|
1699
|
-
const candidate =
|
|
2335
|
+
const candidate = join6(root, `.pending-${randomUUID3()}`);
|
|
1700
2336
|
const owner = { pid: process.pid };
|
|
1701
|
-
await
|
|
1702
|
-
await
|
|
1703
|
-
await
|
|
2337
|
+
await mkdir5(root, { recursive: true, mode: 448 });
|
|
2338
|
+
await mkdir5(candidate, { mode: 448 });
|
|
2339
|
+
await writeFile3(join6(candidate, ownerName), JSON.stringify(owner), { mode: 384, flag: "wx" });
|
|
1704
2340
|
try {
|
|
1705
2341
|
for (let attempt = 0; ; attempt++) {
|
|
1706
2342
|
try {
|
|
1707
|
-
await
|
|
2343
|
+
await rename3(candidate, path);
|
|
1708
2344
|
break;
|
|
1709
2345
|
} catch (error2) {
|
|
1710
2346
|
if (!["EEXIST", "ENOTEMPTY"].includes(code(error2) ?? "")) throw error2;
|
|
@@ -1712,17 +2348,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1712
2348
|
}
|
|
1713
2349
|
let names;
|
|
1714
2350
|
try {
|
|
1715
|
-
names = await
|
|
2351
|
+
names = await readdir3(path);
|
|
1716
2352
|
} catch (error2) {
|
|
1717
2353
|
if (code(error2) === "ENOENT") continue;
|
|
1718
2354
|
throw error2;
|
|
1719
2355
|
}
|
|
1720
2356
|
if (names.length === 0) continue;
|
|
1721
2357
|
if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw alreadyRunningError();
|
|
1722
|
-
const previousPath =
|
|
2358
|
+
const previousPath = join6(path, names[0]);
|
|
1723
2359
|
let previous;
|
|
1724
2360
|
try {
|
|
1725
|
-
previous = JSON.parse(await
|
|
2361
|
+
previous = JSON.parse(await readFile6(previousPath, "utf8"));
|
|
1726
2362
|
} catch (error2) {
|
|
1727
2363
|
if (code(error2) === "ENOENT") continue;
|
|
1728
2364
|
throw alreadyRunningError();
|
|
@@ -1739,17 +2375,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1739
2375
|
});
|
|
1740
2376
|
}
|
|
1741
2377
|
} finally {
|
|
1742
|
-
await unlink4(
|
|
2378
|
+
await unlink4(join6(candidate, ownerName)).catch(() => void 0);
|
|
1743
2379
|
await rmdir2(candidate).catch(() => void 0);
|
|
1744
2380
|
}
|
|
1745
2381
|
let released = false;
|
|
1746
2382
|
return {
|
|
1747
2383
|
async attachRuntime(pid) {
|
|
1748
2384
|
if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
|
|
1749
|
-
const temporary =
|
|
2385
|
+
const temporary = join6(root, `.update-${randomUUID3()}`);
|
|
1750
2386
|
try {
|
|
1751
|
-
await
|
|
1752
|
-
await
|
|
2387
|
+
await writeFile3(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
|
|
2388
|
+
await rename3(temporary, join6(path, ownerName));
|
|
1753
2389
|
} finally {
|
|
1754
2390
|
await unlink4(temporary).catch(() => void 0);
|
|
1755
2391
|
}
|
|
@@ -1757,7 +2393,7 @@ ${options.devAgentId}`).digest("hex");
|
|
|
1757
2393
|
async release() {
|
|
1758
2394
|
if (released) return;
|
|
1759
2395
|
released = true;
|
|
1760
|
-
await unlink4(
|
|
2396
|
+
await unlink4(join6(path, ownerName)).catch((error2) => {
|
|
1761
2397
|
if (code(error2) !== "ENOENT") throw error2;
|
|
1762
2398
|
});
|
|
1763
2399
|
await rmdir2(path).catch((error2) => {
|
|
@@ -1809,6 +2445,7 @@ function validateLaunchPlan(value) {
|
|
|
1809
2445
|
}
|
|
1810
2446
|
async function launchPlanFor(config, options, projectRoot, signal) {
|
|
1811
2447
|
const provider = options.platformLaunchPlan ?? ((context) => officialAgentLaunchPlan({
|
|
2448
|
+
datasets: config.datasets,
|
|
1812
2449
|
projectRoot: context.projectRoot
|
|
1813
2450
|
}));
|
|
1814
2451
|
try {
|
|
@@ -1999,7 +2636,7 @@ async function startBuddyDev(options) {
|
|
|
1999
2636
|
const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS, "pollIntervalMs");
|
|
2000
2637
|
const stopGracePeriodMs = positiveDuration(options.stopGracePeriodMs ?? DEFAULT_STOP_GRACE_PERIOD_MS, "stopGracePeriodMs");
|
|
2001
2638
|
const devAgentId = safeDevAgentId(options.devAgentId);
|
|
2002
|
-
const projectRoot =
|
|
2639
|
+
const projectRoot = resolve9(options.projectRoot);
|
|
2003
2640
|
const lifecycle = new AbortController();
|
|
2004
2641
|
const signal = options.signal === void 0 ? lifecycle.signal : AbortSignal.any([lifecycle.signal, options.signal]);
|
|
2005
2642
|
let agentProcess;
|
|
@@ -2017,7 +2654,7 @@ async function startBuddyDev(options) {
|
|
|
2017
2654
|
let config;
|
|
2018
2655
|
let configSource;
|
|
2019
2656
|
try {
|
|
2020
|
-
configSource = await
|
|
2657
|
+
configSource = await readFile7(join7(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
|
|
2021
2658
|
config = combineBuddyProject(parseBuddyAgentConfig(configSource));
|
|
2022
2659
|
} catch (cause) {
|
|
2023
2660
|
throw new BuddyDevError({
|
|
@@ -2027,12 +2664,14 @@ async function startBuddyDev(options) {
|
|
|
2027
2664
|
cause
|
|
2028
2665
|
});
|
|
2029
2666
|
}
|
|
2667
|
+
const widgetDelivery = await checkWidgetDelivery(projectRoot);
|
|
2668
|
+
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
2669
|
const launchPlan = await launchPlanFor(config, options, projectRoot, signal);
|
|
2031
2670
|
const registration = await registerDevAgent({
|
|
2032
2671
|
client: options.registrationClient,
|
|
2033
2672
|
devAgentId,
|
|
2034
2673
|
configSource,
|
|
2035
|
-
configDigest: `sha256:${
|
|
2674
|
+
configDigest: `sha256:${createHash3("sha256").update(configSource).digest("hex")}`,
|
|
2036
2675
|
signal
|
|
2037
2676
|
});
|
|
2038
2677
|
throwIfDevAborted(signal);
|
|
@@ -2062,8 +2701,8 @@ async function startBuddyDev(options) {
|
|
|
2062
2701
|
shell: false,
|
|
2063
2702
|
detached: platform === "darwin",
|
|
2064
2703
|
...options.onLog === void 0 ? {} : {
|
|
2065
|
-
onStdout: (
|
|
2066
|
-
onStderr: (
|
|
2704
|
+
onStdout: (text2) => options.onLog?.({ source: "agent.stdout", text: text2 }),
|
|
2705
|
+
onStderr: (text2) => options.onLog?.({ source: "agent.stderr", text: text2 })
|
|
2067
2706
|
}
|
|
2068
2707
|
});
|
|
2069
2708
|
await instanceLock.attachRuntime(agentProcess.pid);
|
|
@@ -2119,7 +2758,7 @@ async function startBuddyDev(options) {
|
|
|
2119
2758
|
}
|
|
2120
2759
|
|
|
2121
2760
|
// packages/cli-core/src/dev/starter.ts
|
|
2122
|
-
import { resolve as
|
|
2761
|
+
import { resolve as resolve10 } from "node:path";
|
|
2123
2762
|
function createBuddyDevStarter(options = {}) {
|
|
2124
2763
|
const readManifest = options.readManifest ?? (async (projectRoot) => {
|
|
2125
2764
|
const manifest = await readBuddyProjectManifest(projectRoot);
|
|
@@ -2128,7 +2767,7 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2128
2767
|
const startDev = options.startDev ?? startBuddyDev;
|
|
2129
2768
|
return Object.freeze({
|
|
2130
2769
|
async start(input) {
|
|
2131
|
-
const projectRoot =
|
|
2770
|
+
const projectRoot = resolve10(input.projectRoot);
|
|
2132
2771
|
const manifest = await readManifest(projectRoot);
|
|
2133
2772
|
return startDev({
|
|
2134
2773
|
...input,
|
|
@@ -2139,175 +2778,6 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2139
2778
|
});
|
|
2140
2779
|
}
|
|
2141
2780
|
|
|
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
2781
|
// packages/cli-core/src/commands/dev.ts
|
|
2312
2782
|
var DevCommandUsageError = class extends Error {
|
|
2313
2783
|
constructor(message) {
|
|
@@ -2388,7 +2858,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2388
2858
|
return { exitCode: 0 };
|
|
2389
2859
|
}
|
|
2390
2860
|
try {
|
|
2391
|
-
const requestedRoot =
|
|
2861
|
+
const requestedRoot = resolve11(options.cwd ?? process.cwd(), parsed.directory);
|
|
2392
2862
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
2393
2863
|
const environment = options.env ?? process.env;
|
|
2394
2864
|
const url = appServerUrl(parsed.appServer, environment);
|
|
@@ -2441,7 +2911,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2441
2911
|
}
|
|
2442
2912
|
|
|
2443
2913
|
// packages/cli-core/src/commands/cloud.ts
|
|
2444
|
-
import { resolve as
|
|
2914
|
+
import { resolve as resolve12 } from "node:path";
|
|
2445
2915
|
|
|
2446
2916
|
// packages/cli-core/src/cloud/auth.ts
|
|
2447
2917
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -2612,7 +3082,7 @@ function positiveTimeout(value, fallback, name) {
|
|
|
2612
3082
|
if (!Number.isSafeInteger(result) || result <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
2613
3083
|
return result;
|
|
2614
3084
|
}
|
|
2615
|
-
function
|
|
3085
|
+
function object3(value, path) {
|
|
2616
3086
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2617
3087
|
throw invalid(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
2618
3088
|
}
|
|
@@ -2632,7 +3102,7 @@ function optionalText(value, path, allowNewlines = false) {
|
|
|
2632
3102
|
}
|
|
2633
3103
|
function meta(value, path) {
|
|
2634
3104
|
if (value === null) return null;
|
|
2635
|
-
const source =
|
|
3105
|
+
const source = object3(value, path);
|
|
2636
3106
|
const name = source.name === void 0 ? void 0 : safeText(source.name, `${path}.name`);
|
|
2637
3107
|
const avatar = optionalText(source.avatar, `${path}.avatar`);
|
|
2638
3108
|
const tagline = optionalText(source.tagline, `${path}.tagline`);
|
|
@@ -2666,7 +3136,7 @@ function auditStatus(value, path) {
|
|
|
2666
3136
|
return value;
|
|
2667
3137
|
}
|
|
2668
3138
|
function packageVersion(value, path) {
|
|
2669
|
-
const source =
|
|
3139
|
+
const source = object3(value, path);
|
|
2670
3140
|
const auditNote = source.auditNote === null ? null : safeText(source.auditNote, `${path}.auditNote`, true);
|
|
2671
3141
|
return Object.freeze({
|
|
2672
3142
|
version: safeText(source.version, `${path}.version`),
|
|
@@ -2684,7 +3154,7 @@ function positivePage(value, name) {
|
|
|
2684
3154
|
return value;
|
|
2685
3155
|
}
|
|
2686
3156
|
function buddy(value, includeToken) {
|
|
2687
|
-
const source =
|
|
3157
|
+
const source = object3(value, "response");
|
|
2688
3158
|
const token = includeToken ? safeText(source.token, "response.token") : void 0;
|
|
2689
3159
|
return Object.freeze({
|
|
2690
3160
|
id: safeText(source.id, "response.id"),
|
|
@@ -2802,17 +3272,6 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2802
3272
|
if (!response.ok) throw serverError(response, payload);
|
|
2803
3273
|
return payload;
|
|
2804
3274
|
}
|
|
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
3275
|
async createBuddy(request) {
|
|
2817
3276
|
validateBuddyMeta(request.meta);
|
|
2818
3277
|
return buddy(await this.#request("/cli/buddies", "POST", { json: { meta: request.meta } }), true);
|
|
@@ -2824,7 +3283,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2824
3283
|
}), true);
|
|
2825
3284
|
}
|
|
2826
3285
|
async listBuddies() {
|
|
2827
|
-
const source =
|
|
3286
|
+
const source = object3(await this.#request("/cli/buddies", "GET"), "response");
|
|
2828
3287
|
if (!Array.isArray(source.buddies)) throw invalid("response.buddies \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
2829
3288
|
return Object.freeze({
|
|
2830
3289
|
buddies: Object.freeze(source.buddies.map((item) => buddy(item, false)))
|
|
@@ -2841,7 +3300,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2841
3300
|
...pageSize === void 0 ? {} : { pageSize: String(pageSize) }
|
|
2842
3301
|
});
|
|
2843
3302
|
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
2844
|
-
const source =
|
|
3303
|
+
const source = object3(await this.#request(
|
|
2845
3304
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages${suffix}`,
|
|
2846
3305
|
"GET"
|
|
2847
3306
|
), "response");
|
|
@@ -2863,7 +3322,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2863
3322
|
throw new CloudLifecycleError("INVALID_REQUEST", "\u63D0\u4EA4\u5305\u4E0D\u80FD\u4E3A\u7A7A");
|
|
2864
3323
|
}
|
|
2865
3324
|
const query = new URLSearchParams({ commit });
|
|
2866
|
-
const source =
|
|
3325
|
+
const source = object3(await this.#request(
|
|
2867
3326
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages/push?${query.toString()}`,
|
|
2868
3327
|
"POST",
|
|
2869
3328
|
{
|
|
@@ -2880,13 +3339,13 @@ var HttpDeveloperPlatformClient = class {
|
|
|
2880
3339
|
});
|
|
2881
3340
|
}
|
|
2882
3341
|
async logout(accessToken) {
|
|
2883
|
-
const source =
|
|
3342
|
+
const source = object3(await this.#request("/cli/auth/logout", "POST", { accessToken }), "response");
|
|
2884
3343
|
if (source.ok !== true) throw invalid("response.ok \u5FC5\u987B\u662F true");
|
|
2885
3344
|
}
|
|
2886
3345
|
};
|
|
2887
3346
|
|
|
2888
3347
|
// packages/cli-core/src/cloud/browser-login.ts
|
|
2889
|
-
import { createHash as
|
|
3348
|
+
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
2890
3349
|
import { spawn } from "node:child_process";
|
|
2891
3350
|
import { createServer } from "node:http";
|
|
2892
3351
|
|
|
@@ -2946,10 +3405,10 @@ function browserCommand(url) {
|
|
|
2946
3405
|
}
|
|
2947
3406
|
async function openExternalUrl(url) {
|
|
2948
3407
|
const launch = browserCommand(url);
|
|
2949
|
-
await new Promise((
|
|
3408
|
+
await new Promise((resolve16, reject) => {
|
|
2950
3409
|
const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
2951
3410
|
child.once("error", reject);
|
|
2952
|
-
child.once("exit", (code2) => code2 === 0 ?
|
|
3411
|
+
child.once("exit", (code2) => code2 === 0 ? resolve16() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
2953
3412
|
});
|
|
2954
3413
|
}
|
|
2955
3414
|
async function write(res, status, title, message, webUrl, tone = "error") {
|
|
@@ -2961,10 +3420,10 @@ async function write(res, status, title, message, webUrl, tone = "error") {
|
|
|
2961
3420
|
"referrer-policy": "no-referrer",
|
|
2962
3421
|
"x-content-type-options": "nosniff"
|
|
2963
3422
|
});
|
|
2964
|
-
await new Promise((
|
|
3423
|
+
await new Promise((resolve16, reject) => {
|
|
2965
3424
|
res.once("error", reject);
|
|
2966
|
-
res.once("close",
|
|
2967
|
-
res.end(renderLoginResult(title, message, webUrl, tone),
|
|
3425
|
+
res.once("close", resolve16);
|
|
3426
|
+
res.end(renderLoginResult(title, message, webUrl, tone), resolve16);
|
|
2968
3427
|
});
|
|
2969
3428
|
}
|
|
2970
3429
|
async function readJson(response) {
|
|
@@ -2994,16 +3453,16 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
2994
3453
|
if (request.signal?.aborted) throw request.signal.reason;
|
|
2995
3454
|
const callbackSecret = randomBytes(24).toString("hex");
|
|
2996
3455
|
const codeVerifier = randomBytes(32).toString("base64url");
|
|
2997
|
-
const codeChallenge =
|
|
3456
|
+
const codeChallenge = createHash4("sha256").update(codeVerifier).digest("base64url");
|
|
2998
3457
|
let requestId = "";
|
|
2999
3458
|
let settle;
|
|
3000
3459
|
let reject;
|
|
3001
3460
|
let settled = false;
|
|
3002
|
-
const result = new Promise((
|
|
3461
|
+
const result = new Promise((resolve16, rejectResult) => {
|
|
3003
3462
|
settle = (value) => {
|
|
3004
3463
|
if (!settled) {
|
|
3005
3464
|
settled = true;
|
|
3006
|
-
|
|
3465
|
+
resolve16(value);
|
|
3007
3466
|
}
|
|
3008
3467
|
};
|
|
3009
3468
|
reject = (reason) => {
|
|
@@ -3022,9 +3481,9 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3022
3481
|
state: request.state
|
|
3023
3482
|
}).then(settle, reject);
|
|
3024
3483
|
});
|
|
3025
|
-
await new Promise((
|
|
3484
|
+
await new Promise((resolve16, rejectListen) => {
|
|
3026
3485
|
server.once("error", rejectListen);
|
|
3027
|
-
server.listen(0, "127.0.0.1", () =>
|
|
3486
|
+
server.listen(0, "127.0.0.1", () => resolve16());
|
|
3028
3487
|
});
|
|
3029
3488
|
const address = server.address();
|
|
3030
3489
|
if (!address || typeof address === "string") {
|
|
@@ -3057,8 +3516,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3057
3516
|
} finally {
|
|
3058
3517
|
clearTimeout(timer);
|
|
3059
3518
|
request.signal?.removeEventListener("abort", abort);
|
|
3060
|
-
await new Promise((
|
|
3061
|
-
server.close(() =>
|
|
3519
|
+
await new Promise((resolve16) => {
|
|
3520
|
+
server.close(() => resolve16());
|
|
3062
3521
|
server.closeIdleConnections();
|
|
3063
3522
|
server.closeAllConnections();
|
|
3064
3523
|
});
|
|
@@ -3173,7 +3632,7 @@ function parse(source) {
|
|
|
3173
3632
|
});
|
|
3174
3633
|
}
|
|
3175
3634
|
async function runCredentialCommand(input) {
|
|
3176
|
-
return await new Promise((
|
|
3635
|
+
return await new Promise((resolve16, reject) => {
|
|
3177
3636
|
const child = spawn2(input.command, [...input.args], {
|
|
3178
3637
|
shell: false,
|
|
3179
3638
|
windowsHide: true,
|
|
@@ -3189,7 +3648,7 @@ async function runCredentialCommand(input) {
|
|
|
3189
3648
|
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3190
3649
|
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3191
3650
|
child.once("error", reject);
|
|
3192
|
-
child.once("close", (code2) =>
|
|
3651
|
+
child.once("close", (code2) => resolve16({
|
|
3193
3652
|
code: code2 ?? 1,
|
|
3194
3653
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3195
3654
|
stderr: Buffer.concat(stderr).toString("utf8")
|
|
@@ -3257,9 +3716,6 @@ var MockDeveloperPlatformClient = class {
|
|
|
3257
3716
|
constructor(options = {}) {
|
|
3258
3717
|
this.#now = options.now ?? Date.now;
|
|
3259
3718
|
}
|
|
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
3719
|
async createBuddy(request) {
|
|
3264
3720
|
const id = `b_mock_${randomBytes2(8).toString("hex")}`;
|
|
3265
3721
|
const value = this.#value({
|
|
@@ -3362,7 +3818,6 @@ var CloudCommandUsageError = class extends Error {
|
|
|
3362
3818
|
var HELP = Object.freeze({
|
|
3363
3819
|
login: "\u7528\u6CD5: buddy-cli login",
|
|
3364
3820
|
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
3821
|
push: "\u7528\u6CD5: buddy-cli push [--directory <\u9879\u76EE\u76EE\u5F55>] [--commit <\u672C\u6B21\u6539\u52A8\u8BF4\u660E>]",
|
|
3367
3822
|
status: "\u7528\u6CD5: buddy-cli status [--directory <\u9879\u76EE\u76EE\u5F55>]"
|
|
3368
3823
|
});
|
|
@@ -3413,7 +3868,7 @@ function dependencies(options) {
|
|
|
3413
3868
|
return { session: options.session, client: options.client };
|
|
3414
3869
|
}
|
|
3415
3870
|
function requestedDirectory(options, parsed) {
|
|
3416
|
-
return
|
|
3871
|
+
return resolve12(options.cwd ?? process.cwd(), parsed.values.get("--directory") ?? ".");
|
|
3417
3872
|
}
|
|
3418
3873
|
function auditLabel(status) {
|
|
3419
3874
|
if (status === "auditing") return "\u5BA1\u6838\u4E2D";
|
|
@@ -3502,20 +3957,6 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
3502
3957
|
}
|
|
3503
3958
|
if (command === "logout") return await runLogout(io, options);
|
|
3504
3959
|
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
3960
|
const root = await (options.resolveProjectRoot ?? requireBuddyProjectRoot)(requestedDirectory(options, parsed));
|
|
3520
3961
|
const manifest = await (options.readManifest ?? readBuddyProjectManifest)(root);
|
|
3521
3962
|
if (options.mode === "local-mock") {
|
|
@@ -3543,8 +3984,8 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
3543
3984
|
var CLOUD_COMMAND_HELP = HELP;
|
|
3544
3985
|
|
|
3545
3986
|
// packages/cli-core/src/migration/index.ts
|
|
3546
|
-
import { lstat as
|
|
3547
|
-
import { extname, isAbsolute as isAbsolute2, join as
|
|
3987
|
+
import { lstat as lstat7, open as open3, readdir as readdir4, realpath as realpath4, stat } from "node:fs/promises";
|
|
3988
|
+
import { extname, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve13, sep, win32 as win322 } from "node:path";
|
|
3548
3989
|
var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
|
|
3549
3990
|
maxFiles: 128,
|
|
3550
3991
|
maxFileBytes: 256 * 1024,
|
|
@@ -3697,7 +4138,7 @@ async function existingProjectRoot(value) {
|
|
|
3697
4138
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
3698
4139
|
}
|
|
3699
4140
|
try {
|
|
3700
|
-
const root = await
|
|
4141
|
+
const root = await realpath4(resolve13(value));
|
|
3701
4142
|
if (!(await stat(root)).isDirectory()) {
|
|
3702
4143
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
3703
4144
|
}
|
|
@@ -3713,9 +4154,9 @@ async function existingProjectRoot(value) {
|
|
|
3713
4154
|
}
|
|
3714
4155
|
}
|
|
3715
4156
|
async function existingSkillRoot(projectRoot, skillPath) {
|
|
3716
|
-
const candidate = skillPath === "." ? projectRoot :
|
|
4157
|
+
const candidate = skillPath === "." ? projectRoot : join8(projectRoot, ...skillPath.split("/"));
|
|
3717
4158
|
try {
|
|
3718
|
-
await
|
|
4159
|
+
await lstat7(candidate);
|
|
3719
4160
|
} catch (cause) {
|
|
3720
4161
|
if (filesystemCode(cause) === "ENOENT") {
|
|
3721
4162
|
return rejectScan("SKILL_ROOT_NOT_FOUND", skillPath, "\u627E\u4E0D\u5230\u6307\u5B9A\u7684 Skill \u76EE\u5F55\u3002");
|
|
@@ -3724,7 +4165,7 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
3724
4165
|
}
|
|
3725
4166
|
let root;
|
|
3726
4167
|
try {
|
|
3727
|
-
root = await
|
|
4168
|
+
root = await realpath4(candidate);
|
|
3728
4169
|
} catch (cause) {
|
|
3729
4170
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3730
4171
|
return rejectScan("SKILL_SYMLINK_CYCLE", skillPath, "Skill \u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -3750,10 +4191,10 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
3750
4191
|
}
|
|
3751
4192
|
async function assertEntrypoint(context) {
|
|
3752
4193
|
const entryPath = reportChild(context.skillPath, "SKILL.md");
|
|
3753
|
-
const candidate =
|
|
4194
|
+
const candidate = join8(context.skillRoot, "SKILL.md");
|
|
3754
4195
|
try {
|
|
3755
|
-
await
|
|
3756
|
-
const target = await
|
|
4196
|
+
await lstat7(candidate);
|
|
4197
|
+
const target = await realpath4(candidate);
|
|
3757
4198
|
if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
|
|
3758
4199
|
rejectScan(
|
|
3759
4200
|
"SKILL_SYMLINK_ESCAPE",
|
|
@@ -3797,7 +4238,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3797
4238
|
}
|
|
3798
4239
|
let directory;
|
|
3799
4240
|
try {
|
|
3800
|
-
directory = await
|
|
4241
|
+
directory = await realpath4(actualDirectory);
|
|
3801
4242
|
} catch (cause) {
|
|
3802
4243
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3803
4244
|
return rejectScan("SKILL_SYMLINK_CYCLE", reportDirectory, "Skill \u76EE\u5F55\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -3817,7 +4258,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3817
4258
|
nextAncestors.add(directoryKey);
|
|
3818
4259
|
let entries;
|
|
3819
4260
|
try {
|
|
3820
|
-
entries = await
|
|
4261
|
+
entries = await readdir4(directory, { withFileTypes: true, encoding: "utf8" });
|
|
3821
4262
|
} catch (cause) {
|
|
3822
4263
|
return rejectScan("SKILL_SCAN_IO_ERROR", reportDirectory, "\u65E0\u6CD5\u5217\u51FA Skill \u5B50\u76EE\u5F55\u3002", cause);
|
|
3823
4264
|
}
|
|
@@ -3854,10 +4295,10 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
3854
4295
|
}
|
|
3855
4296
|
for (const entry of entries) {
|
|
3856
4297
|
const childReportPath = reportChild(reportDirectory, entry.name);
|
|
3857
|
-
const childPath =
|
|
4298
|
+
const childPath = join8(directory, entry.name);
|
|
3858
4299
|
let target;
|
|
3859
4300
|
try {
|
|
3860
|
-
target = await
|
|
4301
|
+
target = await realpath4(childPath);
|
|
3861
4302
|
} catch (cause) {
|
|
3862
4303
|
if (filesystemCode(cause) === "ELOOP") {
|
|
3863
4304
|
return rejectScan("SKILL_SYMLINK_CYCLE", childReportPath, "Skill \u5185\u5BB9\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -4308,9 +4749,9 @@ async function scanExternalSkillPortability(options) {
|
|
|
4308
4749
|
}
|
|
4309
4750
|
|
|
4310
4751
|
// packages/cli-core/src/push/preflight.ts
|
|
4311
|
-
import { createHash as
|
|
4312
|
-
import { lstat as
|
|
4313
|
-
import { isAbsolute as isAbsolute3, join as
|
|
4752
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
4753
|
+
import { lstat as lstat8, open as open4, readdir as readdir5, realpath as realpath5 } from "node:fs/promises";
|
|
4754
|
+
import { isAbsolute as isAbsolute3, join as join9, relative as relative2, resolve as resolve14, sep as sep2, win32 as win323 } from "node:path";
|
|
4314
4755
|
|
|
4315
4756
|
// packages/cli-core/src/push/errors.ts
|
|
4316
4757
|
var PushPreflightError = class extends Error {
|
|
@@ -4408,7 +4849,7 @@ function error(options) {
|
|
|
4408
4849
|
throw new PushPreflightError(options);
|
|
4409
4850
|
}
|
|
4410
4851
|
function sha256(value) {
|
|
4411
|
-
return `sha256:${
|
|
4852
|
+
return `sha256:${createHash5("sha256").update(value).digest("hex")}`;
|
|
4412
4853
|
}
|
|
4413
4854
|
function positiveLimit(value, name, hardMaximum) {
|
|
4414
4855
|
if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
|
|
@@ -4498,10 +4939,10 @@ function normalizeBookletPath(value) {
|
|
|
4498
4939
|
return normalized;
|
|
4499
4940
|
}
|
|
4500
4941
|
async function canonicalProjectRoot(projectRoot) {
|
|
4501
|
-
const absolute =
|
|
4942
|
+
const absolute = resolve14(projectRoot);
|
|
4502
4943
|
let entry;
|
|
4503
4944
|
try {
|
|
4504
|
-
entry = await
|
|
4945
|
+
entry = await lstat8(absolute);
|
|
4505
4946
|
} catch (cause) {
|
|
4506
4947
|
error({
|
|
4507
4948
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4518,7 +4959,7 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
4518
4959
|
});
|
|
4519
4960
|
}
|
|
4520
4961
|
try {
|
|
4521
|
-
return await
|
|
4962
|
+
return await realpath5(absolute);
|
|
4522
4963
|
} catch (cause) {
|
|
4523
4964
|
error({
|
|
4524
4965
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4529,10 +4970,10 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
4529
4970
|
}
|
|
4530
4971
|
}
|
|
4531
4972
|
async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
4532
|
-
const path =
|
|
4973
|
+
const path = join9(projectRoot, filename);
|
|
4533
4974
|
let entry;
|
|
4534
4975
|
try {
|
|
4535
|
-
entry = await
|
|
4976
|
+
entry = await lstat8(path);
|
|
4536
4977
|
} catch (cause) {
|
|
4537
4978
|
error({
|
|
4538
4979
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -4563,22 +5004,22 @@ async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
|
4563
5004
|
maxFileBytes
|
|
4564
5005
|
});
|
|
4565
5006
|
}
|
|
4566
|
-
function decodeCapturedConfig(
|
|
4567
|
-
if (
|
|
5007
|
+
function decodeCapturedConfig(file2) {
|
|
5008
|
+
if (file2.contents === void 0) {
|
|
4568
5009
|
error({
|
|
4569
5010
|
code: "PUSH_CONFIG_INVALID",
|
|
4570
|
-
path:
|
|
4571
|
-
message: `\u65E0\u6CD5\u8BFB\u53D6 ${
|
|
5011
|
+
path: file2.path,
|
|
5012
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6 ${file2.path} \u5185\u5BB9\u3002`
|
|
4572
5013
|
});
|
|
4573
5014
|
}
|
|
4574
5015
|
let source;
|
|
4575
5016
|
try {
|
|
4576
|
-
source = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
5017
|
+
source = new TextDecoder("utf-8", { fatal: true }).decode(file2.contents);
|
|
4577
5018
|
} catch (cause) {
|
|
4578
5019
|
error({
|
|
4579
5020
|
code: "PUSH_CONFIG_INVALID",
|
|
4580
|
-
path:
|
|
4581
|
-
message: `${
|
|
5021
|
+
path: file2.path,
|
|
5022
|
+
message: `${file2.path} \u4E0D\u662F\u6709\u6548 UTF-8\u3002`,
|
|
4582
5023
|
cause
|
|
4583
5024
|
});
|
|
4584
5025
|
}
|
|
@@ -4599,7 +5040,7 @@ function parseCapturedProject(agentFile) {
|
|
|
4599
5040
|
async function selectLockfile(projectRoot) {
|
|
4600
5041
|
let names;
|
|
4601
5042
|
try {
|
|
4602
|
-
names = await
|
|
5043
|
+
names = await readdir5(projectRoot);
|
|
4603
5044
|
} catch (cause) {
|
|
4604
5045
|
error({
|
|
4605
5046
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4654,7 +5095,7 @@ async function hashRegularFile(options) {
|
|
|
4654
5095
|
message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
|
|
4655
5096
|
});
|
|
4656
5097
|
}
|
|
4657
|
-
const hash =
|
|
5098
|
+
const hash = createHash5("sha256");
|
|
4658
5099
|
const captured = [];
|
|
4659
5100
|
const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
|
|
4660
5101
|
let bytes = 0;
|
|
@@ -4708,7 +5149,7 @@ async function createManifest(options) {
|
|
|
4708
5149
|
}
|
|
4709
5150
|
let names;
|
|
4710
5151
|
try {
|
|
4711
|
-
names = await
|
|
5152
|
+
names = await readdir5(directory);
|
|
4712
5153
|
} catch (cause) {
|
|
4713
5154
|
error({
|
|
4714
5155
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -4732,10 +5173,10 @@ async function createManifest(options) {
|
|
|
4732
5173
|
sensitiveEntriesExcluded += 1;
|
|
4733
5174
|
continue;
|
|
4734
5175
|
}
|
|
4735
|
-
const absolutePath =
|
|
5176
|
+
const absolutePath = join9(directory, name);
|
|
4736
5177
|
let entry;
|
|
4737
5178
|
try {
|
|
4738
|
-
entry = await
|
|
5179
|
+
entry = await lstat8(absolutePath);
|
|
4739
5180
|
} catch (cause) {
|
|
4740
5181
|
error({
|
|
4741
5182
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -4766,7 +5207,7 @@ async function createManifest(options) {
|
|
|
4766
5207
|
}
|
|
4767
5208
|
let canonicalPath;
|
|
4768
5209
|
try {
|
|
4769
|
-
canonicalPath = await
|
|
5210
|
+
canonicalPath = await realpath5(absolutePath);
|
|
4770
5211
|
} catch (cause) {
|
|
4771
5212
|
error({
|
|
4772
5213
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -4825,15 +5266,15 @@ async function createManifest(options) {
|
|
|
4825
5266
|
message: `\u63D0\u4EA4\u6587\u4EF6\u603B\u91CF\u8D85\u8FC7\u4E0A\u9650 ${options.limits.maxTotalBytes} bytes\u3002`
|
|
4826
5267
|
});
|
|
4827
5268
|
}
|
|
4828
|
-
const
|
|
5269
|
+
const file2 = await hashRegularFile({
|
|
4829
5270
|
absolutePath,
|
|
4830
5271
|
portablePath: path,
|
|
4831
5272
|
expected: entry,
|
|
4832
5273
|
captureContents: options.capturePaths.has(path),
|
|
4833
5274
|
maxFileBytes: options.limits.maxFileBytes
|
|
4834
5275
|
});
|
|
4835
|
-
files.push(
|
|
4836
|
-
totalBytes +=
|
|
5276
|
+
files.push(file2);
|
|
5277
|
+
totalBytes += file2.bytes;
|
|
4837
5278
|
}
|
|
4838
5279
|
};
|
|
4839
5280
|
await walk(options.projectRoot, []);
|
|
@@ -4914,10 +5355,10 @@ function validateLockfile(lockfile, contents) {
|
|
|
4914
5355
|
function bundleDigest(files) {
|
|
4915
5356
|
const descriptor = JSON.stringify({
|
|
4916
5357
|
schemaVersion: "1",
|
|
4917
|
-
files: files.map((
|
|
4918
|
-
path:
|
|
4919
|
-
bytes:
|
|
4920
|
-
digest:
|
|
5358
|
+
files: files.map((file2) => ({
|
|
5359
|
+
path: file2.path,
|
|
5360
|
+
bytes: file2.bytes,
|
|
5361
|
+
digest: file2.digest
|
|
4921
5362
|
}))
|
|
4922
5363
|
});
|
|
4923
5364
|
return sha256(`buddy-push-bundle-v1\0${descriptor}`);
|
|
@@ -4927,8 +5368,8 @@ function warning(code2, message) {
|
|
|
4927
5368
|
}
|
|
4928
5369
|
function assertOfficialProjectIncluded(files) {
|
|
4929
5370
|
for (const path of OFFICIAL_RUNTIME_INPUTS) {
|
|
4930
|
-
const
|
|
4931
|
-
if (
|
|
5371
|
+
const file2 = files.find((entry) => entry.path === path);
|
|
5372
|
+
if (file2 === void 0 || file2.bytes === 0) {
|
|
4932
5373
|
error({
|
|
4933
5374
|
code: "PUSH_PROJECT_INVALID",
|
|
4934
5375
|
path,
|
|
@@ -4967,7 +5408,7 @@ function assertOfficialProjectIncluded(files) {
|
|
|
4967
5408
|
}
|
|
4968
5409
|
}
|
|
4969
5410
|
async function assertCapturedConfigStable(options) {
|
|
4970
|
-
const manifestEntry = options.manifestFiles.find((
|
|
5411
|
+
const manifestEntry = options.manifestFiles.find((file2) => file2.path === options.filename);
|
|
4971
5412
|
if (manifestEntry === void 0) {
|
|
4972
5413
|
error({
|
|
4973
5414
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -4984,8 +5425,8 @@ async function assertCapturedConfigStable(options) {
|
|
|
4984
5425
|
}
|
|
4985
5426
|
let current;
|
|
4986
5427
|
try {
|
|
4987
|
-
const path =
|
|
4988
|
-
const entry = await
|
|
5428
|
+
const path = join9(options.projectRoot, options.filename);
|
|
5429
|
+
const entry = await lstat8(path);
|
|
4989
5430
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
4990
5431
|
error({
|
|
4991
5432
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5028,6 +5469,8 @@ async function createPushPreflight(options) {
|
|
|
5028
5469
|
BUDDY_AGENT_FILENAME
|
|
5029
5470
|
);
|
|
5030
5471
|
parseCapturedProject(initialAgentEntry);
|
|
5472
|
+
const widgetDelivery = await checkWidgetDelivery(projectRoot);
|
|
5473
|
+
if (widgetDelivery.status === "incomplete") error({ code: "PUSH_WIDGET_DELIVERY_INCOMPLETE", message: widgetDeliverySummary(widgetDelivery) });
|
|
5031
5474
|
const lockfile = await selectLockfile(projectRoot);
|
|
5032
5475
|
assertBookletIsNotRequiredInput(bookletPath, lockfile);
|
|
5033
5476
|
const manifest = await createManifest({
|
|
@@ -5036,7 +5479,12 @@ async function createPushPreflight(options) {
|
|
|
5036
5479
|
capturePaths: /* @__PURE__ */ new Set([BUDDY_AGENT_FILENAME, lockfile.path, BUDDY_PLATFORM_PACKAGE_JSON, BUDDY_PLATFORM_AGENT_ENTRY, BUDDY_PLATFORM_FACTORY_ENTRY]),
|
|
5037
5480
|
...bookletPath === void 0 ? {} : { bookletPath }
|
|
5038
5481
|
});
|
|
5039
|
-
const
|
|
5482
|
+
for (const checked of widgetDelivery.files ?? []) {
|
|
5483
|
+
if (manifest.files.find((file2) => file2.path === checked.path)?.digest !== checked.digest) {
|
|
5484
|
+
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` });
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
const lockEntry = manifest.files.find((file2) => file2.path === lockfile.path);
|
|
5040
5488
|
if (lockEntry?.contents === void 0) {
|
|
5041
5489
|
error({
|
|
5042
5490
|
code: "PUSH_LOCKFILE_INVALID",
|
|
@@ -5060,10 +5508,23 @@ async function createPushPreflight(options) {
|
|
|
5060
5508
|
manifestFiles: manifest.files,
|
|
5061
5509
|
maxFileBytes: limits.maxFileBytes
|
|
5062
5510
|
});
|
|
5063
|
-
const
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5511
|
+
const config = parseCapturedProject(agentEntry);
|
|
5512
|
+
if (config.datasets?.length) {
|
|
5513
|
+
try {
|
|
5514
|
+
await validateRuntimeDatasets(projectRoot, config.datasets);
|
|
5515
|
+
} catch (cause) {
|
|
5516
|
+
error({
|
|
5517
|
+
code: "PUSH_CONFIG_INVALID",
|
|
5518
|
+
path: BUDDY_AGENT_FILENAME,
|
|
5519
|
+
message: `\u65E0\u6CD5\u6309\u5DE5\u7A0B Runtime \u6821\u9A8C\u6570\u636E\u58F0\u660E\uFF1A${cause instanceof Error ? cause.message : String(cause)}`,
|
|
5520
|
+
cause
|
|
5521
|
+
});
|
|
5522
|
+
}
|
|
5523
|
+
}
|
|
5524
|
+
const files = Object.freeze(manifest.files.map((file2) => Object.freeze({
|
|
5525
|
+
path: file2.path,
|
|
5526
|
+
bytes: file2.bytes,
|
|
5527
|
+
digest: file2.digest
|
|
5067
5528
|
})));
|
|
5068
5529
|
const warnings = [warning(
|
|
5069
5530
|
"PREFLIGHT_ONLY",
|
|
@@ -5099,16 +5560,16 @@ async function createPushPreflight(options) {
|
|
|
5099
5560
|
}
|
|
5100
5561
|
|
|
5101
5562
|
// packages/cli-core/src/push/bundle.ts
|
|
5102
|
-
import { createHash as
|
|
5103
|
-
import { lstat as
|
|
5104
|
-
import { join as
|
|
5563
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5564
|
+
import { lstat as lstat9, readFile as readFile8, realpath as realpath6 } from "node:fs/promises";
|
|
5565
|
+
import { join as join10, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
|
|
5105
5566
|
import { Writable } from "node:stream";
|
|
5106
5567
|
import { pipeline } from "node:stream/promises";
|
|
5107
5568
|
import { createGzip } from "node:zlib";
|
|
5108
5569
|
import { pack } from "tar-stream";
|
|
5109
5570
|
var MEDIA_TYPE = "application/gzip";
|
|
5110
5571
|
function digest(bytes) {
|
|
5111
|
-
return `sha256:${
|
|
5572
|
+
return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
|
|
5112
5573
|
}
|
|
5113
5574
|
function changed(path, message, cause) {
|
|
5114
5575
|
throw new PushPreflightError({
|
|
@@ -5122,42 +5583,42 @@ function inside(root, target) {
|
|
|
5122
5583
|
const path = relative3(root, target);
|
|
5123
5584
|
return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
|
|
5124
5585
|
}
|
|
5125
|
-
async function capture(root,
|
|
5126
|
-
const absolute =
|
|
5586
|
+
async function capture(root, file2) {
|
|
5587
|
+
const absolute = join10(root, ...file2.path.split("/"));
|
|
5127
5588
|
try {
|
|
5128
|
-
const entry = await
|
|
5129
|
-
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !==
|
|
5130
|
-
changed(
|
|
5589
|
+
const entry = await lstat9(absolute);
|
|
5590
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !== file2.bytes) {
|
|
5591
|
+
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5131
5592
|
}
|
|
5132
|
-
const canonical = await
|
|
5133
|
-
if (!inside(root, canonical)) changed(
|
|
5134
|
-
const contents = await
|
|
5135
|
-
if (contents.byteLength !==
|
|
5136
|
-
changed(
|
|
5593
|
+
const canonical = await realpath6(absolute);
|
|
5594
|
+
if (!inside(root, canonical)) changed(file2.path, `\u63D0\u4EA4\u6587\u4EF6\u8D8A\u8FC7\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${file2.path}`);
|
|
5595
|
+
const contents = await readFile8(absolute);
|
|
5596
|
+
if (contents.byteLength !== file2.bytes || digest(contents) !== file2.digest) {
|
|
5597
|
+
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5137
5598
|
}
|
|
5138
5599
|
return Object.freeze({
|
|
5139
|
-
path:
|
|
5140
|
-
bytes:
|
|
5141
|
-
digest:
|
|
5600
|
+
path: file2.path,
|
|
5601
|
+
bytes: file2.bytes,
|
|
5602
|
+
digest: file2.digest,
|
|
5142
5603
|
content: contents
|
|
5143
5604
|
});
|
|
5144
5605
|
} catch (cause) {
|
|
5145
5606
|
if (cause instanceof PushPreflightError) throw cause;
|
|
5146
|
-
changed(
|
|
5607
|
+
changed(file2.path, `\u63D0\u4EA4\u524D\u65E0\u6CD5\u518D\u6B21\u8BFB\u53D6\u6587\u4EF6\uFF1A${file2.path}`, cause);
|
|
5147
5608
|
}
|
|
5148
5609
|
}
|
|
5149
5610
|
async function createPushSourceBundle(plan) {
|
|
5150
|
-
const canonicalRoot = await
|
|
5611
|
+
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
5612
|
if (canonicalRoot !== plan.projectRoot) {
|
|
5152
5613
|
changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u6839\u76EE\u5F55\u5728\u9884\u68C0\u540E\u53D1\u751F\u53D8\u5316");
|
|
5153
5614
|
}
|
|
5154
5615
|
const files = [];
|
|
5155
|
-
for (const
|
|
5616
|
+
for (const file2 of plan.files) files.push(await capture(canonicalRoot, file2));
|
|
5156
5617
|
const descriptor = JSON.stringify({
|
|
5157
5618
|
schemaVersion: "1",
|
|
5158
|
-
files: files.map((
|
|
5619
|
+
files: files.map((file2) => ({ path: file2.path, bytes: file2.bytes, digest: file2.digest }))
|
|
5159
5620
|
});
|
|
5160
|
-
const calculatedBundleDigest = `sha256:${
|
|
5621
|
+
const calculatedBundleDigest = `sha256:${createHash6("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
|
|
5161
5622
|
if (calculatedBundleDigest !== plan.bundleDigest) {
|
|
5162
5623
|
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
5624
|
}
|
|
@@ -5170,16 +5631,16 @@ async function createPushSourceBundle(plan) {
|
|
|
5170
5631
|
}
|
|
5171
5632
|
});
|
|
5172
5633
|
const completed = pipeline(archive, createGzip({ level: 9 }), output);
|
|
5173
|
-
for (const
|
|
5634
|
+
for (const file2 of files) {
|
|
5174
5635
|
await new Promise((resolveEntry, rejectEntry) => {
|
|
5175
5636
|
archive.entry({
|
|
5176
|
-
name:
|
|
5177
|
-
size:
|
|
5637
|
+
name: file2.path,
|
|
5638
|
+
size: file2.content.byteLength,
|
|
5178
5639
|
mode: 420,
|
|
5179
5640
|
uid: 0,
|
|
5180
5641
|
gid: 0,
|
|
5181
5642
|
mtime: /* @__PURE__ */ new Date(0)
|
|
5182
|
-
},
|
|
5643
|
+
}, file2.content, (cause) => cause ? rejectEntry(cause) : resolveEntry());
|
|
5183
5644
|
});
|
|
5184
5645
|
}
|
|
5185
5646
|
archive.finalize();
|
|
@@ -5217,7 +5678,9 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5217
5678
|
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
5679
|
buddy-cli dev [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5219
5680
|
buddy-cli login | logout | push | status
|
|
5220
|
-
buddy-cli models
|
|
5681
|
+
buddy-cli models [--json]
|
|
5682
|
+
buddy-cli widgets sync | check | verify
|
|
5683
|
+
buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5221
5684
|
buddy-cli preview [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5222
5685
|
buddy-cli help
|
|
5223
5686
|
|
|
@@ -5226,7 +5689,8 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5226
5689
|
dev \u542F\u52A8\u5B98\u65B9 Runtime \u5E76\u8FDE\u63A5 DEV App Server
|
|
5227
5690
|
login \u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u642D\u642D\u8D26\u53F7
|
|
5228
5691
|
logout \u9000\u51FA\u642D\u642D\u5F00\u53D1\u8005\u8D26\u53F7\u5E76\u6E05\u9664\u672C\u673A\u767B\u5F55\u72B6\u6001
|
|
5229
|
-
models \u67E5\
|
|
5692
|
+
models \u67E5\u770B CLI \u5185\u7F6E\u6A21\u578B ID\uFF08\u65E0\u9700\u767B\u5F55\u3001\u8054\u7F51\u6216 Agent \u5DE5\u7A0B\uFF09
|
|
5693
|
+
datasets \u8BFB\u53D6\u5DE5\u7A0B Runtime \u63D0\u4F9B\u7684\u6570\u636E\u96C6\u76EE\u5F55\uFF08\u65E0\u9700\u767B\u5F55\uFF09
|
|
5230
5694
|
push \u4E0A\u4F20 Agent \u5DE5\u7A0B\u548C commit\uFF0C\u63D0\u4EA4\u5E73\u53F0\u5BA1\u6838
|
|
5231
5695
|
status \u67E5\u8BE2\u642D\u5B50\u7684\u8D44\u6599\u3001\u4EE3\u7801\u3001\u8FD0\u884C\u4E0E\u53D1\u5E03\u72B6\u6001
|
|
5232
5696
|
preview \u6253\u5F00\u642D\u642D App \u79C1\u804A\u6A21\u62DF\u5668\u4E0E\u8C03\u8BD5\u9875\u9762
|
|
@@ -5434,7 +5898,7 @@ function defaultProjectDirectory(name) {
|
|
|
5434
5898
|
return `./${folder}`;
|
|
5435
5899
|
}
|
|
5436
5900
|
async function initializeProjectRoute(route, args, io, options, application) {
|
|
5437
|
-
const cwd =
|
|
5901
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5438
5902
|
const { name, tagline, intro } = application;
|
|
5439
5903
|
const directoryInput = await textAnswer(
|
|
5440
5904
|
io,
|
|
@@ -5443,7 +5907,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
5443
5907
|
"\u9879\u76EE\u76EE\u5F55\uFF08\u7A7A\u76EE\u5F55\u6216\u5DF2\u6709\u642D\u642D\u5DE5\u7A0B\uFF09",
|
|
5444
5908
|
defaultProjectDirectory(name)
|
|
5445
5909
|
);
|
|
5446
|
-
const rootDirectory =
|
|
5910
|
+
const rootDirectory = resolve15(cwd, directoryInput);
|
|
5447
5911
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
5448
5912
|
const initializationTarget = { directory: rootDirectory, kind: "new" };
|
|
5449
5913
|
const initializationPlan = options.initializeProject ? void 0 : await planBuddyProjectInitialization(initializationTarget);
|
|
@@ -5487,7 +5951,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
5487
5951
|
}
|
|
5488
5952
|
async function runRoute(route, args, io, options) {
|
|
5489
5953
|
if (!io.canPrompt) assertNonInteractiveProjectArguments(route, args);
|
|
5490
|
-
const cwd =
|
|
5954
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5491
5955
|
const name = await textAnswer(io, args.name, "--name", "\u642D\u5B50\u540D\u79F0", void 0, BUDDY_PROFILE_TEXT_LIMITS.name);
|
|
5492
5956
|
const tagline = await textAnswer(
|
|
5493
5957
|
io,
|
|
@@ -5515,7 +5979,7 @@ async function runRoute(route, args, io, options) {
|
|
|
5515
5979
|
"\u9879\u76EE\u76EE\u5F55",
|
|
5516
5980
|
defaultProjectDirectory(name)
|
|
5517
5981
|
);
|
|
5518
|
-
const rootDirectory =
|
|
5982
|
+
const rootDirectory = resolve15(cwd, directoryInput);
|
|
5519
5983
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
5520
5984
|
const plan = await planBuddyDraftInitialization(rootDirectory);
|
|
5521
5985
|
const existingBuddyId = plan.existingManifest?.buddyId;
|
|
@@ -5549,11 +6013,11 @@ async function runRoute(route, args, io, options) {
|
|
|
5549
6013
|
return { ...result, route: "create", draft, buddyId };
|
|
5550
6014
|
}
|
|
5551
6015
|
async function resumeProjectArguments(args, cwd, options) {
|
|
5552
|
-
const root =
|
|
6016
|
+
const root = resolve15(cwd, args.directory?.trim() || ".");
|
|
5553
6017
|
await assertInitializableBuddyTarget(root);
|
|
5554
6018
|
let entry;
|
|
5555
6019
|
try {
|
|
5556
|
-
entry = await
|
|
6020
|
+
entry = await lstat10(join11(root, "buddy.agent.json"));
|
|
5557
6021
|
} catch (cause) {
|
|
5558
6022
|
if (cause.code === "ENOENT") return;
|
|
5559
6023
|
throw cause;
|
|
@@ -5582,7 +6046,7 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5582
6046
|
try {
|
|
5583
6047
|
const [command, ...rest] = args;
|
|
5584
6048
|
if (!command) {
|
|
5585
|
-
const cwd =
|
|
6049
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5586
6050
|
let projectRoot;
|
|
5587
6051
|
try {
|
|
5588
6052
|
projectRoot = await findBuddyProjectRoot(cwd);
|
|
@@ -5610,16 +6074,21 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5610
6074
|
io.write(HELP2);
|
|
5611
6075
|
return { exitCode: 0 };
|
|
5612
6076
|
}
|
|
6077
|
+
if (command === "models") return runModelsCommand(rest, io);
|
|
6078
|
+
if (command === "widgets") return await runWidgetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
5613
6079
|
if (command === "preview") {
|
|
5614
6080
|
return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
|
|
5615
6081
|
}
|
|
6082
|
+
if (command === "datasets") {
|
|
6083
|
+
return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6084
|
+
}
|
|
5616
6085
|
if (command === "dev") {
|
|
5617
6086
|
return await runDevCommand(rest, io, {
|
|
5618
6087
|
cwd: options.cwd,
|
|
5619
6088
|
...options.dev
|
|
5620
6089
|
});
|
|
5621
6090
|
}
|
|
5622
|
-
if (command === "login" || command === "logout" || command === "
|
|
6091
|
+
if (command === "login" || command === "logout" || command === "push" || command === "status") {
|
|
5623
6092
|
return await runCloudCommand(command, rest, io, {
|
|
5624
6093
|
cwd: options.cwd,
|
|
5625
6094
|
...options.cloud
|
|
@@ -5633,11 +6102,11 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
5633
6102
|
}
|
|
5634
6103
|
let route = routeFromMode(parsed.mode);
|
|
5635
6104
|
if (route === void 0 && !io.canPrompt) requireModeForNonInteractive();
|
|
5636
|
-
const cwd =
|
|
6105
|
+
const cwd = resolve15(options.cwd ?? process.cwd());
|
|
5637
6106
|
await assertInitializableBuddyTarget(cwd);
|
|
5638
6107
|
if (parsed.directory !== void 0) {
|
|
5639
6108
|
if (!parsed.directory.trim()) throw new CliUsageError("--directory \u4E0D\u80FD\u4E3A\u7A7A");
|
|
5640
|
-
await assertInitializableBuddyTarget(
|
|
6109
|
+
await assertInitializableBuddyTarget(resolve15(cwd, parsed.directory));
|
|
5641
6110
|
}
|
|
5642
6111
|
await authenticate(options);
|
|
5643
6112
|
await resumeProjectArguments(parsed, cwd, options);
|
|
@@ -5779,6 +6248,7 @@ export {
|
|
|
5779
6248
|
assertOutsideBuddyProject,
|
|
5780
6249
|
buddyAgentConfig,
|
|
5781
6250
|
buildAgentEnvironment,
|
|
6251
|
+
checkWidgetDelivery,
|
|
5782
6252
|
combineBuddyProject,
|
|
5783
6253
|
createBuddyConfig,
|
|
5784
6254
|
createBuddyDevStarter,
|
|
@@ -5816,6 +6286,7 @@ export {
|
|
|
5816
6286
|
throwIfDevAborted,
|
|
5817
6287
|
validateBuddyAgentConfig,
|
|
5818
6288
|
validateBuddyConfig,
|
|
5819
|
-
validateBuddyProjectManifest
|
|
6289
|
+
validateBuddyProjectManifest,
|
|
6290
|
+
widgetDeliverySummary
|
|
5820
6291
|
};
|
|
5821
6292
|
//# sourceMappingURL=core.js.map
|