@my-life-buddies/cli 0.6.0 → 0.9.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 +60 -19
- package/dist/bin/buddy.js +182 -1
- package/dist/bin/buddy.js.map +4 -4
- package/dist/bin/core.js +583 -315
- package/dist/bin/core.js.map +4 -4
- 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,53 +1,106 @@
|
|
|
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
1
|
// packages/cli-core/src/commands/models.ts
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
return { exitCode: 0 };
|
|
2
|
+
import { resolve as resolve5 } from "node:path";
|
|
3
|
+
|
|
4
|
+
// packages/cli-core/src/config/runtime-catalog.ts
|
|
5
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { pathToFileURL } from "node:url";
|
|
9
|
+
|
|
10
|
+
// packages/cli-core/src/config/runtime-dependency.ts
|
|
11
|
+
var BUDDY_RUNTIME_PACKAGE = "@my-life-buddies/buddy-runtime";
|
|
12
|
+
function runtimeDependencyVersion(pkg) {
|
|
13
|
+
if (typeof pkg !== "object" || pkg === null || !("dependencies" in pkg)) return void 0;
|
|
14
|
+
const dependencies2 = pkg.dependencies;
|
|
15
|
+
if (typeof dependencies2 !== "object" || dependencies2 === null || !(BUDDY_RUNTIME_PACKAGE in dependencies2)) return void 0;
|
|
16
|
+
const version = dependencies2[BUDDY_RUNTIME_PACKAGE];
|
|
17
|
+
return typeof version === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version) ? version : void 0;
|
|
29
18
|
}
|
|
30
19
|
|
|
31
|
-
// packages/cli-core/src/
|
|
32
|
-
|
|
20
|
+
// packages/cli-core/src/config/runtime-catalog.ts
|
|
21
|
+
async function loadRuntime(projectRoot) {
|
|
22
|
+
const packagePath = resolve(projectRoot, "package.json");
|
|
23
|
+
const expected = runtimeDependencyVersion(JSON.parse(await readFile(packagePath, "utf8")));
|
|
24
|
+
if (!expected) throw new Error(`package.json \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
25
|
+
const projectRequire = createRequire(packagePath);
|
|
26
|
+
let runtimePath;
|
|
27
|
+
try {
|
|
28
|
+
const installedPath = await realpath(resolve(projectRoot, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"));
|
|
29
|
+
if (await realpath(projectRequire.resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`)) !== installedPath) {
|
|
30
|
+
throw new Error("Runtime \u5FC5\u987B\u5B89\u88C5\u5728\u5F53\u524D\u5DE5\u7A0B\u4E2D");
|
|
31
|
+
}
|
|
32
|
+
const installed = JSON.parse(await readFile(installedPath, "utf8"));
|
|
33
|
+
if (installed.version !== expected) throw new Error(`\u5DF2\u5B89\u88C5 ${installed.version}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
|
|
34
|
+
runtimePath = projectRequire.resolve(BUDDY_RUNTIME_PACKAGE);
|
|
35
|
+
} catch (cause) {
|
|
36
|
+
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)}`);
|
|
37
|
+
}
|
|
38
|
+
const runtime = await import(pathToFileURL(runtimePath).href);
|
|
39
|
+
return { runtime, runtimeVersion: expected, runtimePath };
|
|
40
|
+
}
|
|
41
|
+
async function readRuntimeDataCatalog(projectRoot) {
|
|
42
|
+
const { runtime, runtimeVersion: expected } = await loadRuntime(projectRoot);
|
|
43
|
+
const catalog = runtime.DATA_ACCESS_CAPABILITIES;
|
|
44
|
+
if (!Array.isArray(catalog)) {
|
|
45
|
+
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`);
|
|
46
|
+
}
|
|
47
|
+
const seen = /* @__PURE__ */ new Set();
|
|
48
|
+
const datasets = catalog.map((value) => {
|
|
49
|
+
if (!value || typeof value !== "object") throw new Error("Runtime \u6570\u636E\u76EE\u5F55\u683C\u5F0F\u65E0\u6548");
|
|
50
|
+
const item = value;
|
|
51
|
+
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) {
|
|
52
|
+
throw new Error("Runtime \u6570\u636E\u76EE\u5F55\u683C\u5F0F\u65E0\u6548\u6216\u5305\u542B\u91CD\u590D ID");
|
|
53
|
+
}
|
|
54
|
+
seen.add(item.id);
|
|
55
|
+
return Object.freeze({ id: item.id, title: item.title, source: item.source, schemaVersion: item.schemaVersion });
|
|
56
|
+
});
|
|
57
|
+
return Object.freeze({ runtimeVersion: expected, datasets: Object.freeze(datasets) });
|
|
58
|
+
}
|
|
59
|
+
async function readRuntimeModelCatalog(projectRoot) {
|
|
60
|
+
const { runtime, runtimeVersion, runtimePath } = await loadRuntime(projectRoot);
|
|
61
|
+
let models = runtime.MODEL_IDS;
|
|
62
|
+
if (models === void 0) {
|
|
63
|
+
try {
|
|
64
|
+
models = (await import(new URL("./model.js", pathToFileURL(runtimePath)).href)).MODEL_IDS;
|
|
65
|
+
} catch (cause) {
|
|
66
|
+
throw new Error(`Runtime ${runtimeVersion} \u7684\u6A21\u578B\u76EE\u5F55\u4E0D\u53EF\u8BFB\u53D6\uFF1A${cause instanceof Error ? cause.message : String(cause)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!Array.isArray(models) || models.length === 0 || models.some((id) => typeof id !== "string" || !id.trim()) || new Set(models).size !== models.length) {
|
|
70
|
+
throw new Error(`Runtime ${runtimeVersion} \u7684 MODEL_IDS \u7F3A\u5931\u6216\u683C\u5F0F\u65E0\u6548`);
|
|
71
|
+
}
|
|
72
|
+
return Object.freeze({ runtimeVersion, models: Object.freeze([...models]) });
|
|
73
|
+
}
|
|
74
|
+
async function validateRuntimeModel(projectRoot, model) {
|
|
75
|
+
if (model === void 0) return;
|
|
76
|
+
const catalog = await readRuntimeModelCatalog(projectRoot);
|
|
77
|
+
if (!catalog.models.includes(model)) throw new Error(`/model\uFF1A\u5DE5\u7A0B Runtime ${catalog.runtimeVersion} \u4E0D\u652F\u6301 ${model}\uFF1B\u53EF\u9009 ${catalog.models.join("\u3001")}`);
|
|
78
|
+
}
|
|
79
|
+
async function validateRuntimeDatasets(projectRoot, requirements = []) {
|
|
80
|
+
const catalog = await readRuntimeDataCatalog(projectRoot);
|
|
81
|
+
const ids = new Set(catalog.datasets.map((item) => item.id));
|
|
82
|
+
for (const [index, item] of requirements.entries()) {
|
|
83
|
+
if (!ids.has(item.id)) throw new Error(`/datasets/${index}/id\uFF1A\u5DE5\u7A0B Runtime ${catalog.runtimeVersion} \u4E0D\u652F\u6301 ${item.id}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
33
86
|
|
|
34
87
|
// packages/cli-core/src/project-context.ts
|
|
35
|
-
import { lstat as
|
|
36
|
-
import { dirname, join as
|
|
88
|
+
import { lstat as lstat5, realpath as realpath2 } from "node:fs/promises";
|
|
89
|
+
import { dirname, join as join5, resolve as resolve4 } from "node:path";
|
|
37
90
|
|
|
38
91
|
// packages/cli-core/src/config/index.ts
|
|
39
92
|
import {
|
|
40
93
|
link,
|
|
41
|
-
lstat as
|
|
42
|
-
mkdir as
|
|
94
|
+
lstat as lstat4,
|
|
95
|
+
mkdir as mkdir3,
|
|
43
96
|
open as open2,
|
|
44
|
-
readFile as
|
|
97
|
+
readFile as readFile5,
|
|
45
98
|
readdir,
|
|
46
|
-
rename,
|
|
99
|
+
rename as rename2,
|
|
47
100
|
unlink as unlink2
|
|
48
101
|
} from "node:fs/promises";
|
|
49
102
|
import { randomUUID } from "node:crypto";
|
|
50
|
-
import { isAbsolute, join as
|
|
103
|
+
import { isAbsolute, join as join4, resolve as resolve3, win32 } from "node:path";
|
|
51
104
|
import { isDeepStrictEqual } from "node:util";
|
|
52
105
|
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
53
106
|
|
|
@@ -78,7 +131,7 @@ var buddy_agent_schema_default = {
|
|
|
78
131
|
minLength: 1,
|
|
79
132
|
maxLength: 128,
|
|
80
133
|
pattern: "^[^\\s\\u0000-\\u001f\\u007f]+$",
|
|
81
|
-
description: "\u4ECE buddy-cli models \
|
|
134
|
+
description: "\u4ECE buddy-cli models \u67E5\u770B Runtime \u6A21\u578B\u76EE\u5F55\uFF1B\u5DE5\u7A0B\u5185\u8BFB\u53D6\u5B89\u88C5\u7248\u672C\uFF0C\u5DE5\u7A0B\u5916\u8BFB\u53D6 npm \u6700\u65B0\u7248\u3002\u8FD0\u884C\u4E0E\u4E0A\u4F20\u9884\u68C0\u6309\u5DE5\u7A0B Runtime \u6821\u9A8C\u3002"
|
|
82
135
|
},
|
|
83
136
|
datasets: {
|
|
84
137
|
type: "array",
|
|
@@ -110,12 +163,177 @@ var buddy_agent_schema_default = {
|
|
|
110
163
|
};
|
|
111
164
|
|
|
112
165
|
// packages/cli-core/src/project-files.ts
|
|
113
|
-
import { lstat as
|
|
166
|
+
import { lstat as lstat3 } from "node:fs/promises";
|
|
167
|
+
import { join as join3 } from "node:path";
|
|
168
|
+
|
|
169
|
+
// packages/cli-core/src/init/scaffold.ts
|
|
170
|
+
import { lstat as lstat2, mkdir as mkdir2, open, readFile as readFile4, rmdir, unlink } from "node:fs/promises";
|
|
171
|
+
import { resolve as resolve2 } from "node:path";
|
|
172
|
+
|
|
173
|
+
// packages/cli-core/src/config/models.ts
|
|
174
|
+
var MODEL_IDS = Object.freeze([
|
|
175
|
+
"kimi-k2.6",
|
|
176
|
+
"kimi-k3",
|
|
177
|
+
"deepseek-v4-flash",
|
|
178
|
+
"deepseek-v4-pro"
|
|
179
|
+
]);
|
|
180
|
+
var DEFAULT_MODEL_ID = "kimi-k2.6";
|
|
181
|
+
|
|
182
|
+
// packages/cli-core/src/init/runtime-template.ts
|
|
183
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
184
|
+
import { mkdtemp as mkdtemp2, readFile as readFile3, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
185
|
+
import { tmpdir } from "node:os";
|
|
186
|
+
import { join as join2 } from "node:path";
|
|
187
|
+
import { promisify as promisify2 } from "node:util";
|
|
188
|
+
|
|
189
|
+
// packages/cli-core/src/config/runtime-package.ts
|
|
190
|
+
import { execFile } from "node:child_process";
|
|
191
|
+
import { lstat, mkdir, mkdtemp, readFile as readFile2, rename, rm, writeFile } from "node:fs/promises";
|
|
192
|
+
import { homedir } from "node:os";
|
|
114
193
|
import { join } from "node:path";
|
|
194
|
+
import { promisify } from "node:util";
|
|
195
|
+
var RUNTIME_REGISTRY = "https://registry.npmjs.org/";
|
|
196
|
+
var execute = promisify(execFile);
|
|
197
|
+
async function latestRuntimeVersion(options = {}) {
|
|
198
|
+
options.signal?.throwIfAborted();
|
|
199
|
+
const timeout = AbortSignal.timeout(1e4);
|
|
200
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
201
|
+
const response = await (options.fetch ?? globalThis.fetch)(`${RUNTIME_REGISTRY}${encodeURIComponent(BUDDY_RUNTIME_PACKAGE)}/latest`, {
|
|
202
|
+
signal,
|
|
203
|
+
headers: { Accept: "application/json" },
|
|
204
|
+
redirect: "error"
|
|
205
|
+
});
|
|
206
|
+
if (!response.ok) throw new Error(`npm \u8FD4\u56DE HTTP ${response.status}`);
|
|
207
|
+
const metadata = await response.json();
|
|
208
|
+
if (metadata?.name !== BUDDY_RUNTIME_PACKAGE || typeof metadata.version !== "string" || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u.test(metadata.version)) {
|
|
209
|
+
throw new Error("npm latest \u672A\u8FD4\u56DE\u6B64 Runtime \u7684\u51C6\u786E\u7A33\u5B9A\u7248\u672C");
|
|
210
|
+
}
|
|
211
|
+
return metadata.version;
|
|
212
|
+
}
|
|
213
|
+
async function cachedLatestRuntimeRoot(options = {}) {
|
|
214
|
+
options.report?.("\u6B63\u5728\u67E5\u8BE2\u516C\u5171 npm \u7684\u6700\u65B0 Runtime\u2026");
|
|
215
|
+
const version = await latestRuntimeVersion(options);
|
|
216
|
+
const cache = options.cacheDirectory ?? join(homedir(), ".cache", "buddy-cli", "runtimes");
|
|
217
|
+
const destination = join(cache, version);
|
|
218
|
+
let cacheExists = false;
|
|
219
|
+
try {
|
|
220
|
+
await lstat(destination);
|
|
221
|
+
cacheExists = true;
|
|
222
|
+
} catch (cause) {
|
|
223
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
224
|
+
}
|
|
225
|
+
if (cacheExists) {
|
|
226
|
+
try {
|
|
227
|
+
const pkg = JSON.parse(await readFile2(join(destination, "package.json"), "utf8"));
|
|
228
|
+
const installed = JSON.parse(await readFile2(join(destination, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"), "utf8"));
|
|
229
|
+
if (runtimeDependencyVersion(pkg) === version && installed.version === version) return destination;
|
|
230
|
+
throw new Error("\u7F13\u5B58\u7248\u672C\u4E0D\u5339\u914D");
|
|
231
|
+
} catch (cause) {
|
|
232
|
+
throw new Error(`Runtime ${version} \u7684\u7F13\u5B58\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u5220\u9664 ${destination} \u540E\u91CD\u8BD5`, { cause });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
await mkdir(cache, { recursive: true });
|
|
236
|
+
const temporaryRoot = await mkdtemp(join(cache, `.install-${version}-`));
|
|
237
|
+
try {
|
|
238
|
+
await writeFile(join(temporaryRoot, "package.json"), JSON.stringify({
|
|
239
|
+
name: "buddy-runtime-catalog",
|
|
240
|
+
private: true,
|
|
241
|
+
type: "module",
|
|
242
|
+
dependencies: { [BUDDY_RUNTIME_PACKAGE]: version }
|
|
243
|
+
}));
|
|
244
|
+
const userConfig = join(temporaryRoot, ".npmrc");
|
|
245
|
+
await writeFile(userConfig, `registry=${RUNTIME_REGISTRY}
|
|
246
|
+
`);
|
|
247
|
+
options.report?.(`\u6B63\u5728\u7F13\u5B58 Runtime ${version} \u7684\u80FD\u529B\u76EE\u5F55\uFF0C\u9996\u6B21\u4F7F\u7528\u9700\u8981\u4E0B\u8F7D\u4F9D\u8D56\u2026`);
|
|
248
|
+
const runNpm = options.runNpm ?? ((args, settings) => execute("npm", args, { ...settings, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 }));
|
|
249
|
+
await runNpm([
|
|
250
|
+
"install",
|
|
251
|
+
"--ignore-scripts",
|
|
252
|
+
"--no-audit",
|
|
253
|
+
"--no-fund",
|
|
254
|
+
"--engine-strict",
|
|
255
|
+
`--registry=${RUNTIME_REGISTRY}`,
|
|
256
|
+
`--@my-life-buddies:registry=${RUNTIME_REGISTRY}`,
|
|
257
|
+
`--userconfig=${userConfig}`,
|
|
258
|
+
"--prefer-online",
|
|
259
|
+
"--fetch-retries=0",
|
|
260
|
+
"--fetch-timeout=20000"
|
|
261
|
+
], { cwd: temporaryRoot, signal: options.signal, timeout: 18e4 });
|
|
262
|
+
const installed = JSON.parse(await readFile2(join(temporaryRoot, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"), "utf8"));
|
|
263
|
+
if (installed.version !== version) throw new Error(`\u7F13\u5B58\u7684 Runtime \u7248\u672C\u4E0E npm latest ${version} \u4E0D\u4E00\u81F4`);
|
|
264
|
+
options.signal?.throwIfAborted();
|
|
265
|
+
try {
|
|
266
|
+
await rename(temporaryRoot, destination);
|
|
267
|
+
} catch (cause) {
|
|
268
|
+
if (!["EEXIST", "ENOTEMPTY"].includes(cause.code ?? "")) throw cause;
|
|
269
|
+
}
|
|
270
|
+
return destination;
|
|
271
|
+
} finally {
|
|
272
|
+
await rm(temporaryRoot, { recursive: true, force: true });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// packages/cli-core/src/init/runtime-template.ts
|
|
277
|
+
var execute2 = promisify2(execFile2);
|
|
278
|
+
async function latestRuntimeTemplate(template, options) {
|
|
279
|
+
const bundledVersion = runtimeDependencyVersion(JSON.parse(template.packageJson));
|
|
280
|
+
if (!bundledVersion) throw new Error("CLI \u5DE5\u7A0B\u6A21\u677F\u7F3A\u5C11 Runtime \u7684\u51C6\u786E\u7248\u672C");
|
|
281
|
+
let temporaryRoot;
|
|
282
|
+
try {
|
|
283
|
+
options.signal?.throwIfAborted();
|
|
284
|
+
options.report("\u6B63\u5728\u67E5\u8BE2\u516C\u5171 npm \u7684\u6700\u65B0 Runtime\u2026");
|
|
285
|
+
const version = await latestRuntimeVersion(options);
|
|
286
|
+
let result = template;
|
|
287
|
+
if (version !== bundledVersion) {
|
|
288
|
+
temporaryRoot = await mkdtemp2(join2(tmpdir(), "buddy-runtime-template-"));
|
|
289
|
+
await writeFile2(join2(temporaryRoot, "package.json"), template.packageJson);
|
|
290
|
+
await writeFile2(join2(temporaryRoot, "package-lock.json"), template.packageLock);
|
|
291
|
+
const userConfig = join2(temporaryRoot, ".npmrc");
|
|
292
|
+
await writeFile2(userConfig, `registry=${RUNTIME_REGISTRY}
|
|
293
|
+
`);
|
|
294
|
+
options.report(`\u6B63\u5728\u4E3A Runtime ${version} \u751F\u6210\u4F9D\u8D56\u9501\u6587\u4EF6\u2026`);
|
|
295
|
+
const runNpm = options.runNpm ?? ((args, settings) => execute2("npm", args, { ...settings, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 }));
|
|
296
|
+
await runNpm([
|
|
297
|
+
"install",
|
|
298
|
+
"--package-lock-only",
|
|
299
|
+
"--save-exact",
|
|
300
|
+
`${BUDDY_RUNTIME_PACKAGE}@${version}`,
|
|
301
|
+
"--ignore-scripts",
|
|
302
|
+
"--no-audit",
|
|
303
|
+
"--no-fund",
|
|
304
|
+
"--engine-strict",
|
|
305
|
+
`--registry=${RUNTIME_REGISTRY}`,
|
|
306
|
+
`--@my-life-buddies:registry=${RUNTIME_REGISTRY}`,
|
|
307
|
+
`--userconfig=${userConfig}`,
|
|
308
|
+
"--prefer-online",
|
|
309
|
+
"--fetch-retries=0",
|
|
310
|
+
"--fetch-timeout=20000"
|
|
311
|
+
], { cwd: temporaryRoot, signal: options.signal, timeout: 6e4 });
|
|
312
|
+
result = {
|
|
313
|
+
packageJson: await readFile3(join2(temporaryRoot, "package.json"), "utf8"),
|
|
314
|
+
packageLock: await readFile3(join2(temporaryRoot, "package-lock.json"), "utf8")
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
const pkg = JSON.parse(result.packageJson);
|
|
318
|
+
const lock = JSON.parse(result.packageLock);
|
|
319
|
+
const locked = lock.packages?.[`node_modules/${BUDDY_RUNTIME_PACKAGE}`];
|
|
320
|
+
if (runtimeDependencyVersion(pkg) !== version || runtimeDependencyVersion(lock.packages?.[""]) !== version || locked?.version !== version || typeof locked.integrity !== "string" || !locked.integrity || typeof locked.resolved !== "string" || !locked.resolved.startsWith(RUNTIME_REGISTRY)) {
|
|
321
|
+
throw new Error("\u751F\u6210\u7684 Runtime \u7248\u672C\u4E0E\u4F9D\u8D56\u9501\u6587\u4EF6\u4E0D\u4E00\u81F4");
|
|
322
|
+
}
|
|
323
|
+
options.signal?.throwIfAborted();
|
|
324
|
+
options.report(`\u5DF2\u4ECE\u516C\u5171 npm latest \u9501\u5B9A Runtime ${version}\uFF08package.json \u4E0E package-lock.json\uFF09\u3002`);
|
|
325
|
+
return result;
|
|
326
|
+
} catch (cause) {
|
|
327
|
+
if (options.signal?.aborted) throw new Error("Runtime \u7248\u672C\u9009\u62E9\u5DF2\u53D6\u6D88");
|
|
328
|
+
const reason = cause instanceof Error && !cause.message.includes("Command failed") ? cause.message : "npm \u672A\u5B8C\u6210\u9501\u6587\u4EF6\u751F\u6210";
|
|
329
|
+
options.report(`\u672A\u80FD\u9501\u5B9A\u6700\u65B0 Runtime\uFF1A${reason}\u3002\u672C\u6B21\u4F7F\u7528 CLI \u968F\u9644\u7684 Runtime ${bundledVersion} \u53CA\u5176\u9501\u6587\u4EF6\uFF1B\u5B89\u88C5\u540E\u53EF\u663E\u5F0F\u5347\u7EA7\u5DE5\u7A0B\u4F9D\u8D56\u3002`);
|
|
330
|
+
return template;
|
|
331
|
+
} finally {
|
|
332
|
+
if (temporaryRoot) await rm2(temporaryRoot, { recursive: true, force: true });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
115
335
|
|
|
116
336
|
// packages/cli-core/src/init/scaffold.ts
|
|
117
|
-
import { lstat, mkdir, open, readFile, rmdir, unlink } from "node:fs/promises";
|
|
118
|
-
import { resolve } from "node:path";
|
|
119
337
|
var BUDDY_PLATFORM_AGENT_ENTRY = "agent.md";
|
|
120
338
|
var BUDDY_PLATFORM_FACTORY_ENTRY = "src/buddy-options.mjs";
|
|
121
339
|
var BUDDY_PLATFORM_PACKAGE_JSON = "package.json";
|
|
@@ -206,11 +424,11 @@ try {
|
|
|
206
424
|
}
|
|
207
425
|
`;
|
|
208
426
|
}
|
|
209
|
-
async function scaffoldPlatformAgent(projectRoot, name) {
|
|
210
|
-
const path =
|
|
427
|
+
async function scaffoldPlatformAgent(projectRoot, name, options = {}) {
|
|
428
|
+
const path = resolve2(projectRoot, BUDDY_PLATFORM_AGENT_ENTRY);
|
|
211
429
|
const hasAlternateLockfile = (await Promise.all(["pnpm-lock.yaml", "yarn.lock"].map(async (name2) => {
|
|
212
430
|
try {
|
|
213
|
-
if (!(await
|
|
431
|
+
if (!(await lstat2(resolve2(projectRoot, name2))).isFile()) {
|
|
214
432
|
throw new TypeError(`\u4F9D\u8D56\u9501\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF1A${name2}`);
|
|
215
433
|
}
|
|
216
434
|
return true;
|
|
@@ -221,23 +439,23 @@ async function scaffoldPlatformAgent(projectRoot, name) {
|
|
|
221
439
|
}))).some(Boolean);
|
|
222
440
|
const files = [
|
|
223
441
|
{ path, content: platformAgentTemplate(name) },
|
|
224
|
-
{ path:
|
|
225
|
-
{ path:
|
|
226
|
-
...hasAlternateLockfile ? [] : [{ path:
|
|
227
|
-
{ path:
|
|
442
|
+
{ path: resolve2(projectRoot, BUDDY_PLATFORM_FACTORY_ENTRY), content: platformFactoryTemplate() },
|
|
443
|
+
{ path: resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_JSON), content: await readFile4(new URL("../../resources/agent-template/package.json", import.meta.url), "utf8") },
|
|
444
|
+
...hasAlternateLockfile ? [] : [{ path: resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_LOCK), content: await readFile4(new URL("../../resources/agent-template/package-lock.json", import.meta.url), "utf8") }],
|
|
445
|
+
{ path: resolve2(projectRoot, BUDDY_PLATFORM_START_ENTRY), content: platformStartTemplate() }
|
|
228
446
|
];
|
|
229
447
|
const handles = [];
|
|
230
448
|
const createdDirectories = [];
|
|
231
449
|
try {
|
|
232
450
|
for (const directory of PLATFORM_DIRECTORIES) {
|
|
233
|
-
const directoryPath =
|
|
451
|
+
const directoryPath = resolve2(projectRoot, directory);
|
|
234
452
|
try {
|
|
235
|
-
await
|
|
453
|
+
await mkdir2(directoryPath, { mode: 448 });
|
|
236
454
|
createdDirectories.push(directoryPath);
|
|
237
455
|
} catch (cause) {
|
|
238
456
|
if (cause.code !== "EEXIST") throw cause;
|
|
239
457
|
}
|
|
240
|
-
if (!(await
|
|
458
|
+
if (!(await lstat2(directoryPath)).isDirectory()) {
|
|
241
459
|
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}`);
|
|
242
460
|
}
|
|
243
461
|
}
|
|
@@ -246,11 +464,27 @@ async function scaffoldPlatformAgent(projectRoot, name) {
|
|
|
246
464
|
handles.push({ path: file2.path, handle: await open(file2.path, "wx", 384) });
|
|
247
465
|
} catch (cause) {
|
|
248
466
|
if (cause.code !== "EEXIST") throw cause;
|
|
249
|
-
if (!(await
|
|
467
|
+
if (!(await lstat2(file2.path)).isFile()) {
|
|
250
468
|
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}`);
|
|
251
469
|
}
|
|
252
470
|
}
|
|
253
471
|
}
|
|
472
|
+
const packageFile = files.find((file2) => file2.path === resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_JSON));
|
|
473
|
+
const lockFile = files.find((file2) => file2.path === resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_LOCK));
|
|
474
|
+
if (options.resolveDependencies && lockFile && handles.some((file2) => file2.path === packageFile.path) && handles.some((file2) => file2.path === lockFile.path)) {
|
|
475
|
+
const template = await options.resolveDependencies({ packageJson: packageFile.content, packageLock: lockFile.content });
|
|
476
|
+
packageFile.content = template.packageJson;
|
|
477
|
+
lockFile.content = template.packageLock;
|
|
478
|
+
}
|
|
479
|
+
if (lockFile && (handles.some((file2) => file2.path === packageFile.path) || handles.some((file2) => file2.path === lockFile.path))) {
|
|
480
|
+
const packageContents = handles.some((file2) => file2.path === packageFile.path) ? packageFile.content : await readFile4(packageFile.path, "utf8");
|
|
481
|
+
const lockContents = handles.some((file2) => file2.path === lockFile.path) ? lockFile.content : await readFile4(lockFile.path, "utf8");
|
|
482
|
+
const declared = JSON.parse(packageContents).dependencies ?? {};
|
|
483
|
+
const locked = JSON.parse(lockContents).packages?.[""]?.dependencies ?? {};
|
|
484
|
+
if (JSON.stringify(Object.entries(declared).sort()) !== JSON.stringify(Object.entries(locked).sort())) {
|
|
485
|
+
throw new Error("\u5DF2\u6709 package.json \u4E0E\u5F85\u8865\u9F50\u7684\u9501\u6587\u4EF6\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u7528\u5DE5\u7A0B\u7684\u5305\u7BA1\u7406\u5668\u6062\u590D\u5339\u914D\u7684\u4F9D\u8D56\u6587\u4EF6\u540E\u91CD\u8BD5");
|
|
486
|
+
}
|
|
487
|
+
}
|
|
254
488
|
for (const { path: filePath, handle } of handles) {
|
|
255
489
|
const file2 = files.find((candidate) => candidate.path === filePath);
|
|
256
490
|
await handle.writeFile(file2.content, "utf8");
|
|
@@ -280,7 +514,7 @@ async function missingBuddyAgentFiles(projectRoot) {
|
|
|
280
514
|
];
|
|
281
515
|
const missing = await Promise.all(entries.map(async ([path, kind]) => {
|
|
282
516
|
try {
|
|
283
|
-
const entry = await
|
|
517
|
+
const entry = await lstat3(join3(projectRoot, path));
|
|
284
518
|
return (kind === "directory" ? entry.isDirectory() : entry.isFile()) ? void 0 : path;
|
|
285
519
|
} catch (cause) {
|
|
286
520
|
if (["ENOENT", "ENOTDIR"].includes(cause.code ?? "")) return path;
|
|
@@ -349,9 +583,6 @@ function validateBuddyProjectManifest(value) {
|
|
|
349
583
|
return { ok: false, issues: schemaIssues(validateAgentSchema.errors) };
|
|
350
584
|
}
|
|
351
585
|
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
|
-
}
|
|
355
586
|
const seen = /* @__PURE__ */ new Set();
|
|
356
587
|
for (const [index, dataset] of (manifest.datasets ?? []).entries()) {
|
|
357
588
|
if (seen.has(dataset.id)) {
|
|
@@ -410,7 +641,7 @@ var ProjectInitializationError = class extends Error {
|
|
|
410
641
|
};
|
|
411
642
|
async function pathState(targetPath) {
|
|
412
643
|
try {
|
|
413
|
-
const entry = await
|
|
644
|
+
const entry = await lstat4(targetPath);
|
|
414
645
|
if (!entry.isDirectory() || entry.isSymbolicLink()) return { kind: "other" };
|
|
415
646
|
return { kind: "directory", identity: `${entry.dev}:${entry.ino}` };
|
|
416
647
|
} catch (cause) {
|
|
@@ -430,7 +661,7 @@ function entryIdentity(entry) {
|
|
|
430
661
|
}
|
|
431
662
|
async function fileIdentity(targetPath) {
|
|
432
663
|
try {
|
|
433
|
-
const entry = await
|
|
664
|
+
const entry = await lstat4(targetPath);
|
|
434
665
|
return entry.isFile() && !entry.isSymbolicLink() ? entryIdentity(entry) : void 0;
|
|
435
666
|
} catch (cause) {
|
|
436
667
|
if (cause.code === "ENOENT") return void 0;
|
|
@@ -439,7 +670,7 @@ async function fileIdentity(targetPath) {
|
|
|
439
670
|
}
|
|
440
671
|
async function unlinkMatchingFile(targetPath, expectedIdentity) {
|
|
441
672
|
try {
|
|
442
|
-
const entry = await
|
|
673
|
+
const entry = await lstat4(targetPath);
|
|
443
674
|
if (!entry.isFile() || entry.isSymbolicLink() || entryIdentity(entry) !== expectedIdentity) {
|
|
444
675
|
return false;
|
|
445
676
|
}
|
|
@@ -477,7 +708,7 @@ async function readExistingManifest(agentConfigPath) {
|
|
|
477
708
|
agentConfigPath
|
|
478
709
|
);
|
|
479
710
|
}
|
|
480
|
-
const entry = await
|
|
711
|
+
const entry = await lstat4(agentConfigPath);
|
|
481
712
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
482
713
|
throw new ProjectInitializationError(
|
|
483
714
|
"TARGET_CHANGED",
|
|
@@ -486,7 +717,7 @@ async function readExistingManifest(agentConfigPath) {
|
|
|
486
717
|
);
|
|
487
718
|
}
|
|
488
719
|
try {
|
|
489
|
-
return parseBuddyProjectManifest(await
|
|
720
|
+
return parseBuddyProjectManifest(await readFile5(agentConfigPath, "utf8"));
|
|
490
721
|
} catch (cause) {
|
|
491
722
|
throw new ProjectInitializationError(
|
|
492
723
|
"TARGET_CHANGED",
|
|
@@ -497,8 +728,8 @@ async function readExistingManifest(agentConfigPath) {
|
|
|
497
728
|
}
|
|
498
729
|
}
|
|
499
730
|
async function planBuddyDraftInitialization(directory) {
|
|
500
|
-
const rootDirectory =
|
|
501
|
-
const agentConfigPath =
|
|
731
|
+
const rootDirectory = resolve3(directory);
|
|
732
|
+
const agentConfigPath = join4(rootDirectory, BUDDY_AGENT_FILENAME);
|
|
502
733
|
const state2 = await pathState(rootDirectory);
|
|
503
734
|
if (state2.kind === "other") {
|
|
504
735
|
throw new ProjectInitializationError(
|
|
@@ -525,8 +756,8 @@ async function planBuddyDraftInitialization(directory) {
|
|
|
525
756
|
};
|
|
526
757
|
}
|
|
527
758
|
async function planBuddyProjectInitialization(options) {
|
|
528
|
-
const rootDirectory =
|
|
529
|
-
const agentConfigPath =
|
|
759
|
+
const rootDirectory = resolve3(options.directory);
|
|
760
|
+
const agentConfigPath = resolve3(rootDirectory, BUDDY_AGENT_FILENAME);
|
|
530
761
|
const state2 = await pathState(rootDirectory);
|
|
531
762
|
if (state2.kind === "other") {
|
|
532
763
|
throw new ProjectInitializationError(
|
|
@@ -555,7 +786,7 @@ async function planBuddyProjectInitialization(options) {
|
|
|
555
786
|
async function prepareInitializationDirectory(plan) {
|
|
556
787
|
if (plan.createRootDirectory) {
|
|
557
788
|
if ((await pathState(plan.rootDirectory)).kind !== "missing") throw targetChanged(plan);
|
|
558
|
-
const firstCreatedDirectory = await
|
|
789
|
+
const firstCreatedDirectory = await mkdir3(plan.rootDirectory, { recursive: true });
|
|
559
790
|
if (firstCreatedDirectory === void 0) throw targetChanged(plan);
|
|
560
791
|
const created = await pathState(plan.rootDirectory);
|
|
561
792
|
if (created.kind !== "directory") throw targetChanged(plan);
|
|
@@ -570,7 +801,7 @@ async function prepareInitializationDirectory(plan) {
|
|
|
570
801
|
async function publishNewFile(options) {
|
|
571
802
|
let temporaryHandle;
|
|
572
803
|
let publishedHandle;
|
|
573
|
-
const temporaryPath =
|
|
804
|
+
const temporaryPath = join4(
|
|
574
805
|
options.rootDirectory,
|
|
575
806
|
`.${options.temporaryLabel}.${process.pid}.${randomUUID()}.tmp`
|
|
576
807
|
);
|
|
@@ -614,7 +845,7 @@ async function publishNewFile(options) {
|
|
|
614
845
|
const afterPublish = await pathState(options.rootDirectory);
|
|
615
846
|
const publishedFileIdentity = await fileIdentity(options.targetPath);
|
|
616
847
|
const temporaryFileIdentity = await fileIdentity(temporaryPath);
|
|
617
|
-
const publishedContents = publishedFileIdentity ? await
|
|
848
|
+
const publishedContents = publishedFileIdentity ? await readFile5(options.targetPath, "utf8") : void 0;
|
|
618
849
|
if (afterPublish.kind !== "directory" || afterPublish.identity !== options.directoryIdentity || !publishedIdentity || publishedFileIdentity !== publishedIdentity || temporaryFileIdentity !== temporaryIdentity || publishedContents !== options.contents) {
|
|
619
850
|
if (afterPublish.kind === "directory" && afterPublish.identity === options.directoryIdentity && publishedIdentity) {
|
|
620
851
|
await unlinkMatchingFile(options.targetPath, publishedIdentity);
|
|
@@ -641,7 +872,7 @@ async function publishNewFile(options) {
|
|
|
641
872
|
}
|
|
642
873
|
}
|
|
643
874
|
async function replaceExistingFile(options) {
|
|
644
|
-
const temporaryPath =
|
|
875
|
+
const temporaryPath = join4(
|
|
645
876
|
options.rootDirectory,
|
|
646
877
|
`.${options.temporaryLabel}.${process.pid}.${randomUUID()}.tmp`
|
|
647
878
|
);
|
|
@@ -655,8 +886,8 @@ async function replaceExistingFile(options) {
|
|
|
655
886
|
try {
|
|
656
887
|
const root = await pathState(options.rootDirectory);
|
|
657
888
|
if (root.kind !== "directory" || root.identity !== options.directoryIdentity || await fileIdentity(options.targetPath) !== options.expectedFileIdentity) throw targetChanged({ rootDirectory: options.rootDirectory });
|
|
658
|
-
await
|
|
659
|
-
if (await
|
|
889
|
+
await rename2(temporaryPath, options.targetPath);
|
|
890
|
+
if (await readFile5(options.targetPath, "utf8") !== options.contents) {
|
|
660
891
|
throw targetChanged({ rootDirectory: options.rootDirectory });
|
|
661
892
|
}
|
|
662
893
|
} finally {
|
|
@@ -781,16 +1012,16 @@ async function initializeBuddyProject(options) {
|
|
|
781
1012
|
return applyBuddyProjectInitialization(plan, options.config);
|
|
782
1013
|
}
|
|
783
1014
|
async function readBuddyProjectManifest(projectRoot) {
|
|
784
|
-
const rootDirectory =
|
|
785
|
-
return parseBuddyProjectManifest(await
|
|
1015
|
+
const rootDirectory = resolve3(projectRoot);
|
|
1016
|
+
return parseBuddyProjectManifest(await readFile5(resolve3(rootDirectory, BUDDY_AGENT_FILENAME), "utf8"));
|
|
786
1017
|
}
|
|
787
1018
|
async function readBuddyAgentConfig(projectRoot) {
|
|
788
|
-
const rootDirectory =
|
|
789
|
-
const source = await
|
|
1019
|
+
const rootDirectory = resolve3(projectRoot);
|
|
1020
|
+
const source = await readFile5(resolve3(rootDirectory, BUDDY_AGENT_FILENAME), "utf8");
|
|
790
1021
|
return parseBuddyAgentConfig(source);
|
|
791
1022
|
}
|
|
792
1023
|
async function readBuddyConfig(projectRoot) {
|
|
793
|
-
const rootDirectory =
|
|
1024
|
+
const rootDirectory = resolve3(projectRoot);
|
|
794
1025
|
return combineBuddyProject(await readBuddyAgentConfig(rootDirectory));
|
|
795
1026
|
}
|
|
796
1027
|
|
|
@@ -806,10 +1037,10 @@ var BuddyProjectContextError = class extends Error {
|
|
|
806
1037
|
targetPath;
|
|
807
1038
|
};
|
|
808
1039
|
async function existingDirectory(target) {
|
|
809
|
-
const requested =
|
|
1040
|
+
const requested = resolve4(target);
|
|
810
1041
|
let entry;
|
|
811
1042
|
try {
|
|
812
|
-
entry = await
|
|
1043
|
+
entry = await lstat5(requested);
|
|
813
1044
|
} catch (cause) {
|
|
814
1045
|
if (cause.code === "ENOENT") {
|
|
815
1046
|
throw new BuddyProjectContextError(
|
|
@@ -828,12 +1059,12 @@ async function existingDirectory(target) {
|
|
|
828
1059
|
requested
|
|
829
1060
|
);
|
|
830
1061
|
}
|
|
831
|
-
return
|
|
1062
|
+
return realpath2(requested);
|
|
832
1063
|
}
|
|
833
1064
|
async function projectFileState(directory, filename) {
|
|
834
|
-
const configPath =
|
|
1065
|
+
const configPath = join5(directory, filename);
|
|
835
1066
|
try {
|
|
836
|
-
const entry = await
|
|
1067
|
+
const entry = await lstat5(configPath);
|
|
837
1068
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
838
1069
|
throw new BuddyProjectContextError(
|
|
839
1070
|
"PROJECT_CONFIG_INVALID",
|
|
@@ -860,7 +1091,7 @@ async function findBuddyProjectRoot(startDirectory) {
|
|
|
860
1091
|
return searchUp(await existingDirectory(startDirectory));
|
|
861
1092
|
}
|
|
862
1093
|
async function requireBuddyProjectRoot(startDirectory) {
|
|
863
|
-
const requested =
|
|
1094
|
+
const requested = resolve4(startDirectory);
|
|
864
1095
|
const root = await findBuddyProjectRoot(requested);
|
|
865
1096
|
if (root !== void 0) return root;
|
|
866
1097
|
throw new BuddyProjectContextError(
|
|
@@ -874,7 +1105,7 @@ async function requireBuddyProjectRoot(startDirectory) {
|
|
|
874
1105
|
}
|
|
875
1106
|
async function requireBuddyAgentProjectRoot(startDirectory) {
|
|
876
1107
|
const root = await requireBuddyProjectRoot(startDirectory);
|
|
877
|
-
const agentPath =
|
|
1108
|
+
const agentPath = join5(root, BUDDY_AGENT_FILENAME);
|
|
878
1109
|
try {
|
|
879
1110
|
await readBuddyAgentConfig(root);
|
|
880
1111
|
const missing = await missingBuddyAgentFiles(root);
|
|
@@ -893,10 +1124,10 @@ async function requireBuddyAgentProjectRoot(startDirectory) {
|
|
|
893
1124
|
}
|
|
894
1125
|
}
|
|
895
1126
|
async function nearestExistingDirectory(target) {
|
|
896
|
-
let candidate =
|
|
1127
|
+
let candidate = resolve4(target);
|
|
897
1128
|
while (true) {
|
|
898
1129
|
try {
|
|
899
|
-
const entry = await
|
|
1130
|
+
const entry = await lstat5(candidate);
|
|
900
1131
|
if (!entry.isDirectory()) {
|
|
901
1132
|
throw new BuddyProjectContextError(
|
|
902
1133
|
"PROJECT_DIRECTORY_INVALID",
|
|
@@ -904,7 +1135,7 @@ async function nearestExistingDirectory(target) {
|
|
|
904
1135
|
candidate
|
|
905
1136
|
);
|
|
906
1137
|
}
|
|
907
|
-
return
|
|
1138
|
+
return realpath2(candidate);
|
|
908
1139
|
} catch (cause) {
|
|
909
1140
|
if (cause.code !== "ENOENT") throw cause;
|
|
910
1141
|
const parent = dirname(candidate);
|
|
@@ -914,7 +1145,7 @@ async function nearestExistingDirectory(target) {
|
|
|
914
1145
|
}
|
|
915
1146
|
}
|
|
916
1147
|
async function assertOutsideBuddyProject(target) {
|
|
917
|
-
const requested =
|
|
1148
|
+
const requested = resolve4(target);
|
|
918
1149
|
const container = await nearestExistingDirectory(requested);
|
|
919
1150
|
const projectRoot = await searchUp(container);
|
|
920
1151
|
if (projectRoot === void 0) return;
|
|
@@ -928,13 +1159,13 @@ async function assertOutsideBuddyProject(target) {
|
|
|
928
1159
|
);
|
|
929
1160
|
}
|
|
930
1161
|
async function assertInitializableBuddyTarget(target) {
|
|
931
|
-
const requested =
|
|
1162
|
+
const requested = resolve4(target);
|
|
932
1163
|
const container = await nearestExistingDirectory(requested);
|
|
933
1164
|
const projectRoot = await searchUp(container);
|
|
934
1165
|
if (projectRoot === void 0) return;
|
|
935
1166
|
let requestedRoot = requested;
|
|
936
1167
|
try {
|
|
937
|
-
requestedRoot = await
|
|
1168
|
+
requestedRoot = await realpath2(requested);
|
|
938
1169
|
} catch (cause) {
|
|
939
1170
|
if (cause.code !== "ENOENT") throw cause;
|
|
940
1171
|
}
|
|
@@ -945,7 +1176,7 @@ async function assertInitializableBuddyTarget(target) {
|
|
|
945
1176
|
} catch (cause) {
|
|
946
1177
|
throw new BuddyProjectContextError(
|
|
947
1178
|
"PROJECT_CONFIG_INVALID",
|
|
948
|
-
`${
|
|
1179
|
+
`${join5(projectRoot, BUDDY_AGENT_FILENAME)} \u4E0D\u662F\u6709\u6548\u7684\u9879\u76EE\u6E05\u5355\u3002`,
|
|
949
1180
|
requested,
|
|
950
1181
|
{ cause }
|
|
951
1182
|
);
|
|
@@ -961,10 +1192,54 @@ async function assertInitializableBuddyTarget(target) {
|
|
|
961
1192
|
);
|
|
962
1193
|
}
|
|
963
1194
|
|
|
1195
|
+
// packages/cli-core/src/commands/runtime-source.ts
|
|
1196
|
+
async function runtimeCatalogSource(directory, options = {}) {
|
|
1197
|
+
const project = await findBuddyProjectRoot(directory);
|
|
1198
|
+
if (project) return { root: project, source: "project" };
|
|
1199
|
+
return { root: await (options.latestRuntimeRoot?.() ?? cachedLatestRuntimeRoot(options)), source: "npm" };
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// packages/cli-core/src/commands/models.ts
|
|
1203
|
+
var MODELS_COMMAND_HELP = `\u7528\u6CD5: buddy-cli models [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1204
|
+
\u5DE5\u7A0B\u5185\u8BFB\u53D6\u5DF2\u5B89\u88C5 Runtime \u7684\u6A21\u578B\u76EE\u5F55\uFF1B\u5DE5\u7A0B\u5916\u8054\u7F51\u8BFB\u53D6\u5E76\u7F13\u5B58\u516C\u5171 npm \u6700\u65B0 Runtime\u3002
|
|
1205
|
+
\u65E0\u9700\u767B\u5F55\u642D\u642D\u8D26\u53F7\u3002\u8F93\u51FA\u5305\u542B\u5B9E\u9645 Runtime \u7248\u672C\u548C\u6765\u6E90\u3002
|
|
1206
|
+
\u5C06\u6240\u9009 ID \u5199\u5165 buddy.agent.json.model\uFF1B\u6A21\u677F\u5728 initialState.model \u4E2D\u4F7F\u7528\u5B83\u3002`;
|
|
1207
|
+
async function runModelsCommand(args, io, cwd, options = {}) {
|
|
1208
|
+
try {
|
|
1209
|
+
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
1210
|
+
io.write(MODELS_COMMAND_HELP);
|
|
1211
|
+
return { exitCode: 0 };
|
|
1212
|
+
}
|
|
1213
|
+
let directory;
|
|
1214
|
+
let json = false;
|
|
1215
|
+
for (let i = 0; i < args.length; i++) {
|
|
1216
|
+
if (args[i] === "--json" && !json) json = true;
|
|
1217
|
+
else if (args[i] === "--directory" && directory === void 0 && args[i + 1]?.trim() && !args[i + 1].startsWith("--")) directory = args[++i];
|
|
1218
|
+
else throw new Error(`models \u53C2\u6570\u65E0\u6548\uFF1A${args[i]}
|
|
1219
|
+
${MODELS_COMMAND_HELP}`);
|
|
1220
|
+
}
|
|
1221
|
+
const { root, source } = await runtimeCatalogSource(resolve5(cwd, directory ?? "."), { report: (message) => io.writeError(message), ...options });
|
|
1222
|
+
const catalog = await readRuntimeModelCatalog(root);
|
|
1223
|
+
if (json) io.write(JSON.stringify({ source, ...catalog }, null, 2));
|
|
1224
|
+
else {
|
|
1225
|
+
io.write(`${source === "project" ? "\u5DE5\u7A0B" : "npm \u6700\u65B0"} Runtime ${catalog.runtimeVersion} \u63D0\u4F9B ${catalog.models.length} \u4E2A\u6A21\u578B\uFF1A`);
|
|
1226
|
+
for (const id of catalog.models) io.write(`- ${id}`);
|
|
1227
|
+
io.write("\u5C06\u6240\u9009 ID \u5199\u5165 buddy.agent.json.model\uFF1B\u81EA\u5B9A\u4E49\u4F1A\u8BDD\u5DE5\u5382\u4E5F\u53EF\u6309\u4F1A\u8BDD\u8BBE\u7F6E initialState.model\u3002");
|
|
1228
|
+
}
|
|
1229
|
+
return { exitCode: 0 };
|
|
1230
|
+
} catch (cause) {
|
|
1231
|
+
io.writeError(cause instanceof Error ? cause.message : String(cause));
|
|
1232
|
+
return { exitCode: 2 };
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// packages/cli-core/src/commands/widgets.ts
|
|
1237
|
+
import { resolve as resolve7 } from "node:path";
|
|
1238
|
+
|
|
964
1239
|
// packages/cli-core/src/widget-delivery.ts
|
|
965
1240
|
import { createHash } from "node:crypto";
|
|
966
|
-
import { lstat as
|
|
967
|
-
import { join as
|
|
1241
|
+
import { lstat as lstat6, readFile as readFile6, realpath as realpath3, readdir as readdir2, mkdir as mkdir4, writeFile as writeFile3, rename as rename3 } from "node:fs/promises";
|
|
1242
|
+
import { join as join6, resolve as resolve6 } from "node:path";
|
|
968
1243
|
var LOCK = "widget-design.lock.json";
|
|
969
1244
|
var IMPLEMENTATION = "widget-implementation.json";
|
|
970
1245
|
var RECEIPTS = ".buddy/widget-verification.json";
|
|
@@ -982,12 +1257,12 @@ function portable(path) {
|
|
|
982
1257
|
}
|
|
983
1258
|
async function file(root, path, optional = false) {
|
|
984
1259
|
portable(path);
|
|
985
|
-
const base = await
|
|
1260
|
+
const base = await realpath3(root);
|
|
986
1261
|
let cursor = base;
|
|
987
1262
|
const parts = path.split("/");
|
|
988
1263
|
for (const [index, part] of parts.entries()) {
|
|
989
|
-
cursor =
|
|
990
|
-
const stat2 = await
|
|
1264
|
+
cursor = join6(cursor, part);
|
|
1265
|
+
const stat2 = await lstat6(cursor).catch((e) => {
|
|
991
1266
|
if (e.code === "ENOENT") return void 0;
|
|
992
1267
|
throw e;
|
|
993
1268
|
});
|
|
@@ -998,7 +1273,7 @@ async function file(root, path, optional = false) {
|
|
|
998
1273
|
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
1274
|
if (index === parts.length - 1 && stat2.size > 8 * 1024 * 1024) throw new Error(`\u6587\u4EF6\u8D85\u8FC7 8 MB\uFF1A${path}`);
|
|
1000
1275
|
}
|
|
1001
|
-
return
|
|
1276
|
+
return readFile6(cursor);
|
|
1002
1277
|
}
|
|
1003
1278
|
async function jsonFile(root, path, optional = false) {
|
|
1004
1279
|
const bytes = await file(root, path, optional);
|
|
@@ -1011,19 +1286,19 @@ async function jsonFile(root, path, optional = false) {
|
|
|
1011
1286
|
}
|
|
1012
1287
|
async function save(root, path, value) {
|
|
1013
1288
|
portable(path);
|
|
1014
|
-
let cursor =
|
|
1289
|
+
let cursor = resolve6(root);
|
|
1015
1290
|
for (const segment of path.split("/").slice(0, -1)) {
|
|
1016
|
-
cursor =
|
|
1017
|
-
await
|
|
1291
|
+
cursor = join6(cursor, segment);
|
|
1292
|
+
await mkdir4(cursor).catch((e) => {
|
|
1018
1293
|
if (e.code !== "EEXIST") throw e;
|
|
1019
1294
|
});
|
|
1020
|
-
const stat2 = await
|
|
1295
|
+
const stat2 = await lstat6(cursor);
|
|
1021
1296
|
if (!stat2.isDirectory() || stat2.isSymbolicLink()) throw new Error("\u5199\u5165\u76EE\u5F55\u4E0D\u5B89\u5168");
|
|
1022
1297
|
}
|
|
1023
1298
|
await file(root, path, true);
|
|
1024
|
-
const target =
|
|
1025
|
-
await
|
|
1026
|
-
await
|
|
1299
|
+
const target = join6(root, path), temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
1300
|
+
await writeFile3(temp, JSON.stringify(value, null, 2) + "\n", { flag: "wx", mode: 384 });
|
|
1301
|
+
await rename3(temp, target);
|
|
1027
1302
|
}
|
|
1028
1303
|
function validateLock(value) {
|
|
1029
1304
|
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");
|
|
@@ -1091,14 +1366,14 @@ async function sourceSnapshot(root) {
|
|
|
1091
1366
|
const result = [];
|
|
1092
1367
|
const visit = async (path, depth = 0) => {
|
|
1093
1368
|
if (depth > 32 || result.length > 1e4) throw new Error("\u5B9E\u73B0\u6E90\u7801\u8D85\u51FA\u68C0\u67E5\u8303\u56F4");
|
|
1094
|
-
const stat2 = await
|
|
1369
|
+
const stat2 = await lstat6(join6(root, path)).catch((e) => {
|
|
1095
1370
|
if (e.code === "ENOENT") return void 0;
|
|
1096
1371
|
throw e;
|
|
1097
1372
|
});
|
|
1098
1373
|
if (!stat2) return;
|
|
1099
1374
|
if (stat2.isSymbolicLink()) throw new Error(`\u5B9E\u73B0\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5\uFF1A${path}`);
|
|
1100
1375
|
if (stat2.isDirectory()) {
|
|
1101
|
-
for (const name of (await readdir2(
|
|
1376
|
+
for (const name of (await readdir2(join6(root, path))).sort()) await visit(`${path}/${name}`, depth + 1);
|
|
1102
1377
|
} else result.push([path, sha(await file(root, path))]);
|
|
1103
1378
|
};
|
|
1104
1379
|
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);
|
|
@@ -1259,7 +1534,7 @@ async function runWidgetsCommand(args, io, cwd) {
|
|
|
1259
1534
|
}
|
|
1260
1535
|
if (action !== "verify" && (flags.has("--id") || flags.has("--evidence"))) throw new Error("--id / --evidence \u4EC5\u7528\u4E8E verify");
|
|
1261
1536
|
if (action !== "check" && flags.has("--json")) throw new Error("--json \u4EC5\u7528\u4E8E check");
|
|
1262
|
-
const root = await findBuddyProjectRoot(
|
|
1537
|
+
const root = await findBuddyProjectRoot(resolve7(cwd, flags.get("--directory") ?? "."));
|
|
1263
1538
|
if (!root) throw new Error("\u8BF7\u5728\u642D\u5B50\u5DE5\u7A0B\u4E2D\u8FD0\u884C\uFF0C\u6216\u6307\u5B9A --directory");
|
|
1264
1539
|
if (action === "sync") {
|
|
1265
1540
|
await syncWidgetDesign(root);
|
|
@@ -1281,75 +1556,14 @@ async function runWidgetsCommand(args, io, cwd) {
|
|
|
1281
1556
|
}
|
|
1282
1557
|
|
|
1283
1558
|
// packages/cli-core/src/index.ts
|
|
1284
|
-
import { lstat as
|
|
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
|
-
}
|
|
1559
|
+
import { lstat as lstat11 } from "node:fs/promises";
|
|
1347
1560
|
|
|
1348
1561
|
// packages/cli-core/src/commands/datasets.ts
|
|
1562
|
+
import { resolve as resolve8 } from "node:path";
|
|
1349
1563
|
var DATASETS_COMMAND_HELP = `\u7528\u6CD5: buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1350
|
-
\u8BFB\u53D6\
|
|
1351
|
-
\
|
|
1352
|
-
async function runDatasetsCommand(args, io, cwd) {
|
|
1564
|
+
\u5DE5\u7A0B\u5185\u8BFB\u53D6\u5DF2\u5B89\u88C5 Runtime \u7684 DATA_ACCESS_CAPABILITIES\uFF1B\u5DE5\u7A0B\u5916\u8054\u7F51\u8BFB\u53D6\u5E76\u7F13\u5B58\u516C\u5171 npm \u6700\u65B0 Runtime\u3002
|
|
1565
|
+
\u65E0\u9700\u767B\u5F55\u642D\u642D\u8D26\u53F7\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`;
|
|
1566
|
+
async function runDatasetsCommand(args, io, cwd, options = {}) {
|
|
1353
1567
|
try {
|
|
1354
1568
|
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
|
|
1355
1569
|
io.write(DATASETS_COMMAND_HELP);
|
|
@@ -1363,13 +1577,14 @@ async function runDatasetsCommand(args, io, cwd) {
|
|
|
1363
1577
|
else throw new Error(`datasets \u53C2\u6570\u65E0\u6548\uFF1A${args[i]}
|
|
1364
1578
|
${DATASETS_COMMAND_HELP}`);
|
|
1365
1579
|
}
|
|
1366
|
-
const root = await
|
|
1580
|
+
const { root, source } = await runtimeCatalogSource(resolve8(cwd, directory ?? "."), { report: (message) => io.writeError(message), ...options });
|
|
1367
1581
|
const catalog = await readRuntimeDataCatalog(root);
|
|
1368
|
-
if (json) io.write(JSON.stringify(catalog, null, 2));
|
|
1582
|
+
if (json) io.write(JSON.stringify({ source, ...catalog }, null, 2));
|
|
1369
1583
|
else {
|
|
1370
|
-
io.write(
|
|
1584
|
+
io.write(`${source === "project" ? "\u5DE5\u7A0B" : "npm \u6700\u65B0"} Runtime ${catalog.runtimeVersion} \u63D0\u4F9B ${catalog.datasets.length} \u9879\u6570\u636E\u80FD\u529B\uFF1A`);
|
|
1371
1585
|
for (const item of catalog.datasets) io.write(`- ${item.id} \u2014 ${item.title}\uFF08${item.source}\uFF0CSchema ${item.schemaVersion}\uFF09`);
|
|
1372
1586
|
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");
|
|
1587
|
+
if (source === "project") io.write(`\u66F4\u65B0 CLI \u4E0D\u4F1A\u66F4\u65B0\u5DE5\u7A0B Runtime\u3002\u5982\u9700\u5347\u7EA7\u5230\u516C\u5171 npm \u6700\u65B0\u7248\uFF0C\u5728\u5DE5\u7A0B\u6839\u76EE\u5F55\u8FD0\u884C\uFF1Anpm install --save-exact ${BUDDY_RUNTIME_PACKAGE}@latest --registry=https://registry.npmjs.org/ --@my-life-buddies:registry=https://registry.npmjs.org/`);
|
|
1373
1588
|
}
|
|
1374
1589
|
return { exitCode: 0 };
|
|
1375
1590
|
} catch (cause) {
|
|
@@ -1379,12 +1594,12 @@ ${DATASETS_COMMAND_HELP}`);
|
|
|
1379
1594
|
}
|
|
1380
1595
|
|
|
1381
1596
|
// packages/cli-core/src/index.ts
|
|
1382
|
-
import { join as
|
|
1597
|
+
import { join as join13, resolve as resolve16 } from "node:path";
|
|
1383
1598
|
|
|
1384
1599
|
// packages/cli-core/src/init/creator.ts
|
|
1385
1600
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1386
|
-
import { link as link2, lstat as
|
|
1387
|
-
import { join as
|
|
1601
|
+
import { link as link2, lstat as lstat7, mkdir as mkdir5, readFile as readFile7, unlink as unlink3, writeFile as writeFile4 } from "node:fs/promises";
|
|
1602
|
+
import { join as join7 } from "node:path";
|
|
1388
1603
|
import { fileURLToPath } from "node:url";
|
|
1389
1604
|
|
|
1390
1605
|
// packages/cli-core/src/init/development-handoff.ts
|
|
@@ -1462,11 +1677,13 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
|
|
|
1462
1677
|
|
|
1463
1678
|
\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
|
|
1464
1679
|
|
|
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.
|
|
1680
|
+
\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.12.2 \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
1681
|
|
|
1467
1682
|
\`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
1683
|
|
|
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\
|
|
1684
|
+
\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\u65B0\u5EFA\u5DE5\u7A0B\u52A8\u6001\u9009\u62E9\u516C\u5171 npm latest \u5E76\u9501\u5B9A\u51C6\u786E\u7248\u672C\uFF1B\u8054\u7F51\u5931\u8D25\u65F6\u660E\u786E\u63D0\u793A\u4F7F\u7528\u968F CLI \u9644\u5E26\u7684 Runtime 0.12.2\u3002\u5DF2\u6709\u5DE5\u7A0B\u901A\u8FC7\u5305\u7BA1\u7406\u5668\u663E\u5F0F\u5347\u7EA7\u4F9D\u8D56\u3002
|
|
1685
|
+
|
|
1686
|
+
\u5C0F\u6302\u4EF6\u7684\u6A21\u578B\u5DE5\u5177\u4E3A \`widget_create\`\u3001\`widget_read\`\uFF08\u6574\u4E2A\u5C0F\u6302\u4EF6\uFF09\u3001\`widget_read_module\`\uFF08\u4E00\u4E2A\u6A21\u5757\uFF09\u3001\`widget_rename\`\u3001\`widget_write_record\`\u3001\`widget_delete_record\`\u3001\`widget_delete\`\uFF08\u6574\u4E2A\u5C0F\u6302\u4EF6\u8FDE\u8BB0\u5F55\uFF09\u548C \`widget_send\`\uFF0C\u6309\u4E1A\u52A1\u9700\u8981\u6CE8\u518C\u5230 \`initialState.tools\`\u3002\u4FEE\u6539\u8BB0\u5F55\u7528 \`conversation.widget.writeRecord(id, input)\`\uFF1B\u5220\u4E00\u6761\u8BB0\u5F55\u7528 \`conversation.widget.deleteRecord(id, module, recordId)\`\uFF0C\u4E0D\u80FD\u7528\u5220\u9664\u6574\u4E2A\u5C0F\u6302\u4EF6\u7684 \`conversation.widget.delete(id)\` \u4EE3\u66FF\u3002\u4E24\u4E2A\u8BFB\u53D6\u5DE5\u5177\u53EF\u7528\u4E8E\u4E3B\u52A8\u56DE\u5408\uFF0C\u5176\u4F59\u516D\u4E2A\u5199\u5165\u5DE5\u5177\u7531 Runtime \u963B\u6B62\u5728\u4E3B\u52A8\u56DE\u5408\u6267\u884C\u3002
|
|
1470
1687
|
|
|
1471
1688
|
\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
1689
|
|
|
@@ -1542,16 +1759,16 @@ var REQUIRED = [
|
|
|
1542
1759
|
var CreatorPreparationError = class extends Error {
|
|
1543
1760
|
};
|
|
1544
1761
|
async function state(path) {
|
|
1545
|
-
return
|
|
1762
|
+
return lstat7(path).catch((cause) => {
|
|
1546
1763
|
if (cause.code === "ENOENT") return void 0;
|
|
1547
1764
|
throw cause;
|
|
1548
1765
|
});
|
|
1549
1766
|
}
|
|
1550
1767
|
async function safeDirectory(path) {
|
|
1551
|
-
await
|
|
1768
|
+
await mkdir5(path, { mode: 448 }).catch((cause) => {
|
|
1552
1769
|
if (cause.code !== "EEXIST") throw cause;
|
|
1553
1770
|
});
|
|
1554
|
-
if (!(await
|
|
1771
|
+
if (!(await lstat7(path)).isDirectory()) {
|
|
1555
1772
|
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}`);
|
|
1556
1773
|
}
|
|
1557
1774
|
}
|
|
@@ -1561,11 +1778,11 @@ async function safeRead(path) {
|
|
|
1561
1778
|
if (!entry.isFile() || entry.size > MAX_LOCAL_FILE_BYTES) {
|
|
1562
1779
|
throw new CreatorPreparationError(`\u521B\u4F5C\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6\u6216\u8D85\u51FA\u5927\u5C0F\u9650\u5236\uFF1A${path}`);
|
|
1563
1780
|
}
|
|
1564
|
-
return
|
|
1781
|
+
return readFile7(path);
|
|
1565
1782
|
}
|
|
1566
1783
|
async function writeOnce(path, bytes) {
|
|
1567
1784
|
const temporary = `${path}.${randomUUID2()}.tmp`;
|
|
1568
|
-
await
|
|
1785
|
+
await writeFile4(temporary, bytes, { flag: "wx", mode: 384 });
|
|
1569
1786
|
try {
|
|
1570
1787
|
await link2(temporary, path).catch((cause) => {
|
|
1571
1788
|
if (cause.code !== "EEXIST") throw cause;
|
|
@@ -1576,17 +1793,17 @@ async function writeOnce(path, bytes) {
|
|
|
1576
1793
|
}
|
|
1577
1794
|
async function bundledSkill(root) {
|
|
1578
1795
|
for (const path of REQUIRED) {
|
|
1579
|
-
if (!(await state(
|
|
1796
|
+
if (!(await state(join7(root, path)))?.isFile()) {
|
|
1580
1797
|
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`);
|
|
1581
1798
|
}
|
|
1582
1799
|
}
|
|
1583
1800
|
return root;
|
|
1584
1801
|
}
|
|
1585
1802
|
function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
1586
|
-
const contextPath =
|
|
1803
|
+
const contextPath = join7(context.projectRoot, ".buddy", "creator", "initial-context.json");
|
|
1587
1804
|
const openArgs = [
|
|
1588
1805
|
"-B",
|
|
1589
|
-
|
|
1806
|
+
join7(skillRoot, "scripts/buddy.py"),
|
|
1590
1807
|
"open",
|
|
1591
1808
|
"--workspace",
|
|
1592
1809
|
workspacePath,
|
|
@@ -1616,7 +1833,7 @@ function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
|
1616
1833
|
|
|
1617
1834
|
## \u73B0\u5728\u7531 Coding Agent \u6267\u884C
|
|
1618
1835
|
|
|
1619
|
-
1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(
|
|
1836
|
+
1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(join7(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
|
|
1620
1837
|
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
|
|
1621
1838
|
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
|
|
1622
1839
|
|
|
@@ -1653,11 +1870,11 @@ ${DEVELOPMENT_HANDOFF}
|
|
|
1653
1870
|
`;
|
|
1654
1871
|
}
|
|
1655
1872
|
async function prepareCreatorSkill(context, io, options = {}) {
|
|
1656
|
-
const root =
|
|
1873
|
+
const root = join7(context.projectRoot, ".buddy", "creator");
|
|
1657
1874
|
try {
|
|
1658
|
-
await safeDirectory(
|
|
1875
|
+
await safeDirectory(join7(context.projectRoot, ".buddy"));
|
|
1659
1876
|
await safeDirectory(root);
|
|
1660
|
-
const sessionPath =
|
|
1877
|
+
const sessionPath = join7(root, "session.json");
|
|
1661
1878
|
await writeOnce(sessionPath, JSON.stringify({
|
|
1662
1879
|
schemaVersion: 1,
|
|
1663
1880
|
platformBuddyId: context.buddyId,
|
|
@@ -1669,11 +1886,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1669
1886
|
}
|
|
1670
1887
|
options.signal?.throwIfAborted();
|
|
1671
1888
|
const skillRoot = await bundledSkill(options.bundledSkillRoot ?? BUNDLED_CREATOR_SKILL_ROOT);
|
|
1672
|
-
await safeDirectory(
|
|
1673
|
-
const workspacePath =
|
|
1889
|
+
await safeDirectory(join7(root, "workspaces"));
|
|
1890
|
+
const workspacePath = join7(root, "workspaces", session.creationKey);
|
|
1674
1891
|
const workspace = await state(workspacePath);
|
|
1675
1892
|
if (workspace && !workspace.isDirectory()) throw new CreatorPreparationError("\u5DF2\u6709\u521B\u4F5C\u5DE5\u4F5C\u533A\u4E0D\u662F\u666E\u901A\u76EE\u5F55\u3002");
|
|
1676
|
-
const contextPath =
|
|
1893
|
+
const contextPath = join7(root, "initial-context.json");
|
|
1677
1894
|
await writeOnce(contextPath, JSON.stringify({
|
|
1678
1895
|
source: "platform_meta",
|
|
1679
1896
|
name: context.appName,
|
|
@@ -1689,7 +1906,7 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1689
1906
|
} catch {
|
|
1690
1907
|
throw new CreatorPreparationError(`\u5DF2\u6709\u521D\u59CB\u80CC\u666F\u65E0\u6548\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\uFF1A${contextPath}`);
|
|
1691
1908
|
}
|
|
1692
|
-
const handoffPath =
|
|
1909
|
+
const handoffPath = join7(root, "HANDOFF.md");
|
|
1693
1910
|
const handoff = handoffMarkdown({
|
|
1694
1911
|
...context,
|
|
1695
1912
|
appName: initial.name,
|
|
@@ -1700,11 +1917,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1700
1917
|
if ((await safeRead(handoffPath))?.toString("utf8") !== handoff) {
|
|
1701
1918
|
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}`);
|
|
1702
1919
|
}
|
|
1703
|
-
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${
|
|
1920
|
+
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${join7(skillRoot, "SKILL.md")}`);
|
|
1704
1921
|
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`);
|
|
1705
1922
|
io.write(DEVELOPMENT_HANDOFF_SUMMARY);
|
|
1706
1923
|
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");
|
|
1707
|
-
return { exitCode: 0, creator: { skillPath:
|
|
1924
|
+
return { exitCode: 0, creator: { skillPath: join7(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
|
|
1708
1925
|
} catch (cause) {
|
|
1709
1926
|
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";
|
|
1710
1927
|
io.writeError(`Creator Skill \u51C6\u5907\u5931\u8D25\uFF1A${message}`);
|
|
@@ -1746,7 +1963,7 @@ function validateBuddyAvatar(value) {
|
|
|
1746
1963
|
if (value !== null) {
|
|
1747
1964
|
let valid = false;
|
|
1748
1965
|
if (typeof value === "string" && value.length > 0 && value.length <= 2048 && !/[\s\u0000-\u001f]/u.test(value)) {
|
|
1749
|
-
valid = /^\/media\/[A-Za-z0-9_.-]
|
|
1966
|
+
valid = /^\/media\/[A-Za-z0-9_.-]+(?:\?v=[a-f0-9]{16})?$/u.test(value);
|
|
1750
1967
|
if (!valid) {
|
|
1751
1968
|
try {
|
|
1752
1969
|
const url = new URL(value);
|
|
@@ -1758,9 +1975,13 @@ function validateBuddyAvatar(value) {
|
|
|
1758
1975
|
if (!valid) invalidRequest("avatar \u5FC5\u987B\u4E3A\u5408\u6CD5\u56FE\u7247\u5730\u5740\u6216 null");
|
|
1759
1976
|
}
|
|
1760
1977
|
}
|
|
1978
|
+
function validateBuddyMetaUpdate(meta2) {
|
|
1979
|
+
if (meta2.avatar !== void 0 && meta2.avatar !== null) invalidRequest("\u5934\u50CF\u5730\u5740\u7531\u5E73\u53F0\u751F\u6210\uFF0C\u8BF7\u4E0A\u4F20\u56FE\u7247\u6587\u4EF6\u6216\u4F20 null \u6E05\u9664");
|
|
1980
|
+
if (Object.keys(meta2).some((key) => key !== "avatar")) validateBuddyMeta(meta2);
|
|
1981
|
+
}
|
|
1761
1982
|
|
|
1762
1983
|
// packages/cli-core/src/commands/dev.ts
|
|
1763
|
-
import { resolve as
|
|
1984
|
+
import { resolve as resolve12 } from "node:path";
|
|
1764
1985
|
|
|
1765
1986
|
// packages/cli-core/src/platform-url.ts
|
|
1766
1987
|
var DEFAULT_MLB_TEST_URL = "http://47.116.168.81:8788";
|
|
@@ -2058,15 +2279,15 @@ function buildAgentEnvironment(options) {
|
|
|
2058
2279
|
|
|
2059
2280
|
// packages/cli-core/src/dev/orchestrator.ts
|
|
2060
2281
|
import { createHash as createHash3 } from "node:crypto";
|
|
2061
|
-
import { readFile as
|
|
2062
|
-
import { join as
|
|
2282
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
2283
|
+
import { join as join9, resolve as resolve10 } from "node:path";
|
|
2063
2284
|
|
|
2064
2285
|
// packages/cli-core/src/dev/launch.ts
|
|
2065
2286
|
import { lstatSync, readFileSync } from "node:fs";
|
|
2066
2287
|
import { createRequire as createRequire2 } from "node:module";
|
|
2067
|
-
import { dirname as dirname2, resolve as
|
|
2288
|
+
import { dirname as dirname2, resolve as resolve9 } from "node:path";
|
|
2068
2289
|
async function officialAgentLaunchPlan(input) {
|
|
2069
|
-
const entry =
|
|
2290
|
+
const entry = resolve9(input.projectRoot, BUDDY_PLATFORM_START_ENTRY);
|
|
2070
2291
|
for (const [filename, kind] of [
|
|
2071
2292
|
[dirname2(BUDDY_PLATFORM_FACTORY_ENTRY), "directory"],
|
|
2072
2293
|
[BUDDY_PLATFORM_START_ENTRY, "file"],
|
|
@@ -2074,7 +2295,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2074
2295
|
[BUDDY_PLATFORM_PACKAGE_JSON, "file"]
|
|
2075
2296
|
]) {
|
|
2076
2297
|
try {
|
|
2077
|
-
const entry2 = lstatSync(
|
|
2298
|
+
const entry2 = lstatSync(resolve9(input.projectRoot, filename));
|
|
2078
2299
|
if (!(kind === "directory" ? entry2.isDirectory() : entry2.isFile())) {
|
|
2079
2300
|
throw new Error(`${filename} must be a regular ${kind}`);
|
|
2080
2301
|
}
|
|
@@ -2087,7 +2308,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2087
2308
|
});
|
|
2088
2309
|
}
|
|
2089
2310
|
}
|
|
2090
|
-
const packagePath =
|
|
2311
|
+
const packagePath = resolve9(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
|
|
2091
2312
|
try {
|
|
2092
2313
|
const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
|
|
2093
2314
|
if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
@@ -2095,6 +2316,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2095
2316
|
const installed = JSON.parse(readFileSync(runtimePath, "utf8")).version;
|
|
2096
2317
|
if (installed !== expected) throw new Error(`Runtime \u5DF2\u5B89\u88C5 ${installed}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
|
|
2097
2318
|
await validateRuntimeDatasets(input.projectRoot, input.datasets);
|
|
2319
|
+
await validateRuntimeModel(input.projectRoot, input.model);
|
|
2098
2320
|
} catch (cause) {
|
|
2099
2321
|
throw new BuddyDevError({
|
|
2100
2322
|
code: "DEV_CONFIG_INVALID",
|
|
@@ -2137,16 +2359,16 @@ var NodeAgentProcessLauncher = class {
|
|
|
2137
2359
|
}
|
|
2138
2360
|
let hasExited = false;
|
|
2139
2361
|
let spawned = false;
|
|
2140
|
-
const exit = new Promise((
|
|
2362
|
+
const exit = new Promise((resolve17) => {
|
|
2141
2363
|
child.once("error", (error2) => {
|
|
2142
2364
|
if (!spawned) {
|
|
2143
2365
|
hasExited = true;
|
|
2144
|
-
|
|
2366
|
+
resolve17({ code: null, signal: null, error: error2 });
|
|
2145
2367
|
}
|
|
2146
2368
|
});
|
|
2147
2369
|
child.once("exit", (code2, signal) => {
|
|
2148
2370
|
hasExited = true;
|
|
2149
|
-
|
|
2371
|
+
resolve17({ code: code2, signal });
|
|
2150
2372
|
});
|
|
2151
2373
|
});
|
|
2152
2374
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -2162,11 +2384,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
2162
2384
|
}
|
|
2163
2385
|
});
|
|
2164
2386
|
try {
|
|
2165
|
-
await new Promise((
|
|
2387
|
+
await new Promise((resolve17, reject) => {
|
|
2166
2388
|
const onSpawn = () => {
|
|
2167
2389
|
spawned = true;
|
|
2168
2390
|
child.off("error", onError);
|
|
2169
|
-
|
|
2391
|
+
resolve17();
|
|
2170
2392
|
};
|
|
2171
2393
|
const onError = (error2) => {
|
|
2172
2394
|
child.off("spawn", onSpawn);
|
|
@@ -2205,11 +2427,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
2205
2427
|
};
|
|
2206
2428
|
function waitForExit(process2, timeoutMs) {
|
|
2207
2429
|
if (process2.hasExited()) return Promise.resolve(true);
|
|
2208
|
-
return new Promise((
|
|
2209
|
-
const timeout = setTimeout(() =>
|
|
2430
|
+
return new Promise((resolve17) => {
|
|
2431
|
+
const timeout = setTimeout(() => resolve17(false), timeoutMs);
|
|
2210
2432
|
process2.exit.then(() => {
|
|
2211
2433
|
clearTimeout(timeout);
|
|
2212
|
-
|
|
2434
|
+
resolve17(true);
|
|
2213
2435
|
});
|
|
2214
2436
|
});
|
|
2215
2437
|
}
|
|
@@ -2292,7 +2514,7 @@ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
|
|
|
2292
2514
|
while (isGroupAlive(pid)) {
|
|
2293
2515
|
const remaining = deadline - Date.now();
|
|
2294
2516
|
if (remaining <= 0) return false;
|
|
2295
|
-
await new Promise((
|
|
2517
|
+
await new Promise((resolve17) => setTimeout(resolve17, Math.min(25, remaining)));
|
|
2296
2518
|
}
|
|
2297
2519
|
return true;
|
|
2298
2520
|
}
|
|
@@ -2307,9 +2529,9 @@ function processTreeControllerFor(platform) {
|
|
|
2307
2529
|
|
|
2308
2530
|
// packages/cli-core/src/dev/instance-lock.ts
|
|
2309
2531
|
import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
|
|
2310
|
-
import { mkdir as
|
|
2311
|
-
import { homedir } from "node:os";
|
|
2312
|
-
import { join as
|
|
2532
|
+
import { mkdir as mkdir6, readFile as readFile8, readdir as readdir3, rename as rename4, rmdir as rmdir2, unlink as unlink4, writeFile as writeFile5 } from "node:fs/promises";
|
|
2533
|
+
import { homedir as homedir2 } from "node:os";
|
|
2534
|
+
import { join as join8 } from "node:path";
|
|
2313
2535
|
function alreadyRunningError() {
|
|
2314
2536
|
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";
|
|
2315
2537
|
return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
|
|
@@ -2329,20 +2551,20 @@ function code(error2) {
|
|
|
2329
2551
|
return error2?.code;
|
|
2330
2552
|
}
|
|
2331
2553
|
async function acquireDevInstanceLock(options) {
|
|
2332
|
-
const root = options.directory ??
|
|
2554
|
+
const root = options.directory ?? join8(homedir2(), ".buddy", "dev-locks");
|
|
2333
2555
|
const key = createHash2("sha256").update(`${new URL(options.gatewayUrl).origin}
|
|
2334
2556
|
${options.devAgentId}`).digest("hex");
|
|
2335
|
-
const path =
|
|
2557
|
+
const path = join8(root, key);
|
|
2336
2558
|
const ownerName = `owner-${randomUUID3()}.json`;
|
|
2337
|
-
const candidate =
|
|
2559
|
+
const candidate = join8(root, `.pending-${randomUUID3()}`);
|
|
2338
2560
|
const owner = { pid: process.pid };
|
|
2339
|
-
await
|
|
2340
|
-
await
|
|
2341
|
-
await
|
|
2561
|
+
await mkdir6(root, { recursive: true, mode: 448 });
|
|
2562
|
+
await mkdir6(candidate, { mode: 448 });
|
|
2563
|
+
await writeFile5(join8(candidate, ownerName), JSON.stringify(owner), { mode: 384, flag: "wx" });
|
|
2342
2564
|
try {
|
|
2343
2565
|
for (let attempt = 0; ; attempt++) {
|
|
2344
2566
|
try {
|
|
2345
|
-
await
|
|
2567
|
+
await rename4(candidate, path);
|
|
2346
2568
|
break;
|
|
2347
2569
|
} catch (error2) {
|
|
2348
2570
|
if (!["EEXIST", "ENOTEMPTY"].includes(code(error2) ?? "")) throw error2;
|
|
@@ -2357,10 +2579,10 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2357
2579
|
}
|
|
2358
2580
|
if (names.length === 0) continue;
|
|
2359
2581
|
if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw alreadyRunningError();
|
|
2360
|
-
const previousPath =
|
|
2582
|
+
const previousPath = join8(path, names[0]);
|
|
2361
2583
|
let previous;
|
|
2362
2584
|
try {
|
|
2363
|
-
previous = JSON.parse(await
|
|
2585
|
+
previous = JSON.parse(await readFile8(previousPath, "utf8"));
|
|
2364
2586
|
} catch (error2) {
|
|
2365
2587
|
if (code(error2) === "ENOENT") continue;
|
|
2366
2588
|
throw alreadyRunningError();
|
|
@@ -2377,17 +2599,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2377
2599
|
});
|
|
2378
2600
|
}
|
|
2379
2601
|
} finally {
|
|
2380
|
-
await unlink4(
|
|
2602
|
+
await unlink4(join8(candidate, ownerName)).catch(() => void 0);
|
|
2381
2603
|
await rmdir2(candidate).catch(() => void 0);
|
|
2382
2604
|
}
|
|
2383
2605
|
let released = false;
|
|
2384
2606
|
return {
|
|
2385
2607
|
async attachRuntime(pid) {
|
|
2386
2608
|
if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
|
|
2387
|
-
const temporary =
|
|
2609
|
+
const temporary = join8(root, `.update-${randomUUID3()}`);
|
|
2388
2610
|
try {
|
|
2389
|
-
await
|
|
2390
|
-
await
|
|
2611
|
+
await writeFile5(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
|
|
2612
|
+
await rename4(temporary, join8(path, ownerName));
|
|
2391
2613
|
} finally {
|
|
2392
2614
|
await unlink4(temporary).catch(() => void 0);
|
|
2393
2615
|
}
|
|
@@ -2395,7 +2617,7 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2395
2617
|
async release() {
|
|
2396
2618
|
if (released) return;
|
|
2397
2619
|
released = true;
|
|
2398
|
-
await unlink4(
|
|
2620
|
+
await unlink4(join8(path, ownerName)).catch((error2) => {
|
|
2399
2621
|
if (code(error2) !== "ENOENT") throw error2;
|
|
2400
2622
|
});
|
|
2401
2623
|
await rmdir2(path).catch((error2) => {
|
|
@@ -2448,6 +2670,7 @@ function validateLaunchPlan(value) {
|
|
|
2448
2670
|
async function launchPlanFor(config, options, projectRoot, signal) {
|
|
2449
2671
|
const provider = options.platformLaunchPlan ?? ((context) => officialAgentLaunchPlan({
|
|
2450
2672
|
datasets: config.datasets,
|
|
2673
|
+
model: config.model,
|
|
2451
2674
|
projectRoot: context.projectRoot
|
|
2452
2675
|
}));
|
|
2453
2676
|
try {
|
|
@@ -2638,7 +2861,7 @@ async function startBuddyDev(options) {
|
|
|
2638
2861
|
const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS, "pollIntervalMs");
|
|
2639
2862
|
const stopGracePeriodMs = positiveDuration(options.stopGracePeriodMs ?? DEFAULT_STOP_GRACE_PERIOD_MS, "stopGracePeriodMs");
|
|
2640
2863
|
const devAgentId = safeDevAgentId(options.devAgentId);
|
|
2641
|
-
const projectRoot =
|
|
2864
|
+
const projectRoot = resolve10(options.projectRoot);
|
|
2642
2865
|
const lifecycle = new AbortController();
|
|
2643
2866
|
const signal = options.signal === void 0 ? lifecycle.signal : AbortSignal.any([lifecycle.signal, options.signal]);
|
|
2644
2867
|
let agentProcess;
|
|
@@ -2656,7 +2879,7 @@ async function startBuddyDev(options) {
|
|
|
2656
2879
|
let config;
|
|
2657
2880
|
let configSource;
|
|
2658
2881
|
try {
|
|
2659
|
-
configSource = await
|
|
2882
|
+
configSource = await readFile9(join9(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
|
|
2660
2883
|
config = combineBuddyProject(parseBuddyAgentConfig(configSource));
|
|
2661
2884
|
} catch (cause) {
|
|
2662
2885
|
throw new BuddyDevError({
|
|
@@ -2760,7 +2983,7 @@ async function startBuddyDev(options) {
|
|
|
2760
2983
|
}
|
|
2761
2984
|
|
|
2762
2985
|
// packages/cli-core/src/dev/starter.ts
|
|
2763
|
-
import { resolve as
|
|
2986
|
+
import { resolve as resolve11 } from "node:path";
|
|
2764
2987
|
function createBuddyDevStarter(options = {}) {
|
|
2765
2988
|
const readManifest = options.readManifest ?? (async (projectRoot) => {
|
|
2766
2989
|
const manifest = await readBuddyProjectManifest(projectRoot);
|
|
@@ -2769,7 +2992,7 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2769
2992
|
const startDev = options.startDev ?? startBuddyDev;
|
|
2770
2993
|
return Object.freeze({
|
|
2771
2994
|
async start(input) {
|
|
2772
|
-
const projectRoot =
|
|
2995
|
+
const projectRoot = resolve11(input.projectRoot);
|
|
2773
2996
|
const manifest = await readManifest(projectRoot);
|
|
2774
2997
|
return startDev({
|
|
2775
2998
|
...input,
|
|
@@ -2860,7 +3083,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2860
3083
|
return { exitCode: 0 };
|
|
2861
3084
|
}
|
|
2862
3085
|
try {
|
|
2863
|
-
const requestedRoot =
|
|
3086
|
+
const requestedRoot = resolve12(options.cwd ?? process.cwd(), parsed.directory);
|
|
2864
3087
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
2865
3088
|
const environment = options.env ?? process.env;
|
|
2866
3089
|
const url = appServerUrl(parsed.appServer, environment);
|
|
@@ -2913,7 +3136,34 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2913
3136
|
}
|
|
2914
3137
|
|
|
2915
3138
|
// packages/cli-core/src/commands/cloud.ts
|
|
2916
|
-
import { resolve as
|
|
3139
|
+
import { resolve as resolve13 } from "node:path";
|
|
3140
|
+
|
|
3141
|
+
// packages/cli-core/src/cloud/avatar.ts
|
|
3142
|
+
import { open as open3 } from "node:fs/promises";
|
|
3143
|
+
import { basename } from "node:path";
|
|
3144
|
+
var MAX_BUDDY_AVATAR_BYTES = 5 * 1024 * 1024;
|
|
3145
|
+
async function readBuddyAvatarFile(path) {
|
|
3146
|
+
const file2 = await open3(path, "r");
|
|
3147
|
+
try {
|
|
3148
|
+
const stat2 = await file2.stat();
|
|
3149
|
+
if (!stat2.isFile()) throw new Error("\u5934\u50CF\u5FC5\u987B\u662F\u666E\u901A\u56FE\u7247\u6587\u4EF6");
|
|
3150
|
+
if (!stat2.size || stat2.size > MAX_BUDDY_AVATAR_BYTES) throw new Error("\u5934\u50CF\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u8D85\u8FC7 5 MiB");
|
|
3151
|
+
const buffer = Buffer.alloc(MAX_BUDDY_AVATAR_BYTES + 1);
|
|
3152
|
+
let size = 0;
|
|
3153
|
+
while (size < buffer.length) {
|
|
3154
|
+
const { bytesRead } = await file2.read(buffer, size, buffer.length - size, null);
|
|
3155
|
+
if (!bytesRead) break;
|
|
3156
|
+
size += bytesRead;
|
|
3157
|
+
}
|
|
3158
|
+
if (!size || size > MAX_BUDDY_AVATAR_BYTES) throw new Error("\u5934\u50CF\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u8D85\u8FC7 5 MiB");
|
|
3159
|
+
const bytes = buffer.subarray(0, size);
|
|
3160
|
+
const mediaType = bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) ? "image/png" : bytes.subarray(0, 3).equals(Buffer.from([255, 216, 255])) ? "image/jpeg" : bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP" ? "image/webp" : void 0;
|
|
3161
|
+
if (!mediaType) throw new Error("\u4EC5\u652F\u6301 PNG\u3001JPEG\u3001WebP \u56FE\u7247\u6587\u4EF6");
|
|
3162
|
+
return { bytes, fileName: basename(path), mediaType };
|
|
3163
|
+
} finally {
|
|
3164
|
+
await file2.close();
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
2917
3167
|
|
|
2918
3168
|
// packages/cli-core/src/cloud/auth.ts
|
|
2919
3169
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -3253,7 +3503,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3253
3503
|
"Content-Length": String(binaryBody.byteLength)
|
|
3254
3504
|
}
|
|
3255
3505
|
},
|
|
3256
|
-
...options.json !== void 0 ? { body: JSON.stringify(options.json) } : binaryBody === void 0 ? {} : { body: binaryBody },
|
|
3506
|
+
...options.form !== void 0 ? { body: options.form } : options.json !== void 0 ? { body: JSON.stringify(options.json) } : binaryBody === void 0 ? {} : { body: binaryBody },
|
|
3257
3507
|
redirect: "error",
|
|
3258
3508
|
signal: controller.signal
|
|
3259
3509
|
});
|
|
@@ -3280,7 +3530,14 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3280
3530
|
return buddy(await this.#request("/cli/buddies", "POST", { json: { meta: request.meta } }), true);
|
|
3281
3531
|
}
|
|
3282
3532
|
async updateBuddy(request) {
|
|
3283
|
-
|
|
3533
|
+
validateBuddyMetaUpdate(request.meta);
|
|
3534
|
+
if (request.avatar) {
|
|
3535
|
+
if (request.meta.avatar === null) throw new CloudLifecycleError("INVALID_REQUEST", "\u4E0D\u80FD\u540C\u65F6\u4E0A\u4F20\u548C\u6E05\u9664\u5934\u50CF");
|
|
3536
|
+
const form = new FormData();
|
|
3537
|
+
form.set("meta", JSON.stringify(request.meta));
|
|
3538
|
+
form.set("avatar", new Blob([new Uint8Array(request.avatar.bytes)], { type: request.avatar.mediaType }), request.avatar.fileName);
|
|
3539
|
+
return buddy(await this.#request(`/cli/buddies/${encodeURIComponent(request.id)}/meta`, "PUT", { form }), true);
|
|
3540
|
+
}
|
|
3284
3541
|
return buddy(await this.#request(`/cli/buddies/${encodeURIComponent(request.id)}/meta`, "PUT", {
|
|
3285
3542
|
json: request.meta
|
|
3286
3543
|
}), true);
|
|
@@ -3408,10 +3665,10 @@ function browserCommand(url) {
|
|
|
3408
3665
|
}
|
|
3409
3666
|
async function openExternalUrl(url) {
|
|
3410
3667
|
const launch = browserCommand(url);
|
|
3411
|
-
await new Promise((
|
|
3668
|
+
await new Promise((resolve17, reject) => {
|
|
3412
3669
|
const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
3413
3670
|
child.once("error", reject);
|
|
3414
|
-
child.once("exit", (code2) => code2 === 0 ?
|
|
3671
|
+
child.once("exit", (code2) => code2 === 0 ? resolve17() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
3415
3672
|
});
|
|
3416
3673
|
}
|
|
3417
3674
|
async function write(res, status, title, message, webUrl, tone = "error") {
|
|
@@ -3423,10 +3680,10 @@ async function write(res, status, title, message, webUrl, tone = "error") {
|
|
|
3423
3680
|
"referrer-policy": "no-referrer",
|
|
3424
3681
|
"x-content-type-options": "nosniff"
|
|
3425
3682
|
});
|
|
3426
|
-
await new Promise((
|
|
3683
|
+
await new Promise((resolve17, reject) => {
|
|
3427
3684
|
res.once("error", reject);
|
|
3428
|
-
res.once("close",
|
|
3429
|
-
res.end(renderLoginResult(title, message, webUrl, tone),
|
|
3685
|
+
res.once("close", resolve17);
|
|
3686
|
+
res.end(renderLoginResult(title, message, webUrl, tone), resolve17);
|
|
3430
3687
|
});
|
|
3431
3688
|
}
|
|
3432
3689
|
async function readJson(response) {
|
|
@@ -3461,11 +3718,11 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3461
3718
|
let settle;
|
|
3462
3719
|
let reject;
|
|
3463
3720
|
let settled = false;
|
|
3464
|
-
const result = new Promise((
|
|
3721
|
+
const result = new Promise((resolve17, rejectResult) => {
|
|
3465
3722
|
settle = (value) => {
|
|
3466
3723
|
if (!settled) {
|
|
3467
3724
|
settled = true;
|
|
3468
|
-
|
|
3725
|
+
resolve17(value);
|
|
3469
3726
|
}
|
|
3470
3727
|
};
|
|
3471
3728
|
reject = (reason) => {
|
|
@@ -3484,9 +3741,9 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3484
3741
|
state: request.state
|
|
3485
3742
|
}).then(settle, reject);
|
|
3486
3743
|
});
|
|
3487
|
-
await new Promise((
|
|
3744
|
+
await new Promise((resolve17, rejectListen) => {
|
|
3488
3745
|
server.once("error", rejectListen);
|
|
3489
|
-
server.listen(0, "127.0.0.1", () =>
|
|
3746
|
+
server.listen(0, "127.0.0.1", () => resolve17());
|
|
3490
3747
|
});
|
|
3491
3748
|
const address = server.address();
|
|
3492
3749
|
if (!address || typeof address === "string") {
|
|
@@ -3519,8 +3776,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3519
3776
|
} finally {
|
|
3520
3777
|
clearTimeout(timer);
|
|
3521
3778
|
request.signal?.removeEventListener("abort", abort);
|
|
3522
|
-
await new Promise((
|
|
3523
|
-
server.close(() =>
|
|
3779
|
+
await new Promise((resolve17) => {
|
|
3780
|
+
server.close(() => resolve17());
|
|
3524
3781
|
server.closeIdleConnections();
|
|
3525
3782
|
server.closeAllConnections();
|
|
3526
3783
|
});
|
|
@@ -3635,7 +3892,7 @@ function parse(source) {
|
|
|
3635
3892
|
});
|
|
3636
3893
|
}
|
|
3637
3894
|
async function runCredentialCommand(input) {
|
|
3638
|
-
return await new Promise((
|
|
3895
|
+
return await new Promise((resolve17, reject) => {
|
|
3639
3896
|
const child = spawn2(input.command, [...input.args], {
|
|
3640
3897
|
shell: false,
|
|
3641
3898
|
windowsHide: true,
|
|
@@ -3651,7 +3908,7 @@ async function runCredentialCommand(input) {
|
|
|
3651
3908
|
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3652
3909
|
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3653
3910
|
child.once("error", reject);
|
|
3654
|
-
child.once("close", (code2) =>
|
|
3911
|
+
child.once("close", (code2) => resolve17({
|
|
3655
3912
|
code: code2 ?? 1,
|
|
3656
3913
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3657
3914
|
stderr: Buffer.concat(stderr).toString("utf8")
|
|
@@ -3711,7 +3968,7 @@ var SystemCloudTokenStore = class {
|
|
|
3711
3968
|
};
|
|
3712
3969
|
|
|
3713
3970
|
// packages/cli-core/src/cloud/mock-client.ts
|
|
3714
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3971
|
+
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
3715
3972
|
var MockDeveloperPlatformClient = class {
|
|
3716
3973
|
#now;
|
|
3717
3974
|
#buddies = /* @__PURE__ */ new Map();
|
|
@@ -3737,7 +3994,10 @@ var MockDeveloperPlatformClient = class {
|
|
|
3737
3994
|
}
|
|
3738
3995
|
async updateBuddy(request) {
|
|
3739
3996
|
const current = this.#require(request.id);
|
|
3740
|
-
|
|
3997
|
+
validateBuddyMetaUpdate(request.meta);
|
|
3998
|
+
const onlyAvatar = Object.keys(request.meta).every((key) => key === "avatar");
|
|
3999
|
+
const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${createHash5("sha256").update(request.avatar.bytes).digest("hex").slice(0, 16)}` : request.meta.avatar === null ? null : current.meta?.avatar ?? null;
|
|
4000
|
+
const next = this.#value({ ...current, meta: { ...onlyAvatar ? current.meta : request.meta, avatar }, updatedAt: this.#now() });
|
|
3741
4001
|
this.#buddies.set(request.id, next);
|
|
3742
4002
|
return structuredClone(next);
|
|
3743
4003
|
}
|
|
@@ -3823,9 +4083,9 @@ var HELP = Object.freeze({
|
|
|
3823
4083
|
logout: "\u7528\u6CD5: buddy-cli logout",
|
|
3824
4084
|
push: "\u7528\u6CD5: buddy-cli push [--directory <\u9879\u76EE\u76EE\u5F55>] [--commit <\u672C\u6B21\u6539\u52A8\u8BF4\u660E>]",
|
|
3825
4085
|
status: "\u7528\u6CD5: buddy-cli status [--directory <\u9879\u76EE\u76EE\u5F55>]",
|
|
3826
|
-
avatar: `\u7528\u6CD5: buddy-cli avatar (--
|
|
4086
|
+
avatar: `\u7528\u6CD5: buddy-cli avatar (--file <\u56FE\u7247\u6587\u4EF6> | --clear) [--directory <\u9879\u76EE\u76EE\u5F55> | --buddy-id <\u642D\u5B50 ID>]
|
|
3827
4087
|
\u4FEE\u6539\u5F53\u524D\u9879\u76EE\u6216\u6307\u5B9A\u642D\u5B50\u7684\u4E91\u7AEF\u5934\u50CF\uFF0C\u4FDD\u7559\u5176\u4ED6\u8D44\u6599\uFF1B\u4E0D\u9700\u8981\u5B89\u88C5 Runtime \u6216\u4E0A\u4F20\u4EE3\u7801\u5305\u3002
|
|
3828
|
-
--
|
|
4088
|
+
--file \u63A5\u53D7\u672C\u5730\u9759\u6001 PNG\u3001JPEG\u3001WebP \u56FE\u7247\uFF0C\u6700\u5927 5 MiB\uFF1BServer \u81EA\u52A8\u5904\u7406\u5E76\u751F\u6210\u5730\u5740\u3002
|
|
3829
4089
|
\u9700\u8981\u5F53\u524D\u8D26\u53F7\u62E5\u6709\u8BE5\u642D\u5B50\uFF0C\u547D\u4EE4\u6267\u884C\u540E\u7ACB\u5373\u66F4\u65B0\u4E91\u7AEF\u8D44\u6599\u3002`
|
|
3830
4090
|
});
|
|
3831
4091
|
function parse2(args, command) {
|
|
@@ -3843,7 +4103,7 @@ function parse2(args, command) {
|
|
|
3843
4103
|
values.set(flag2, "");
|
|
3844
4104
|
continue;
|
|
3845
4105
|
}
|
|
3846
|
-
const allowed = command === "push" ? flag2 === "--directory" || flag2 === "--commit" : command === "avatar" ? ["--directory", "--buddy-id", "--
|
|
4106
|
+
const allowed = command === "push" ? flag2 === "--directory" || flag2 === "--commit" : command === "avatar" ? ["--directory", "--buddy-id", "--file"].includes(flag2) : command === "status" && flag2 === "--directory";
|
|
3847
4107
|
if (!allowed) throw new CloudCommandUsageError(`\u672A\u77E5\u53C2\u6570 ${flag2}`);
|
|
3848
4108
|
const value = args[index + 1];
|
|
3849
4109
|
if (value === void 0 || !value.trim() || command === "avatar" && value.startsWith("--")) throw new CloudCommandUsageError(`${flag2} \u7F3A\u5C11\u503C`);
|
|
@@ -3880,7 +4140,7 @@ function dependencies(options) {
|
|
|
3880
4140
|
return { session: options.session, client: options.client };
|
|
3881
4141
|
}
|
|
3882
4142
|
function requestedDirectory(options, parsed) {
|
|
3883
|
-
return
|
|
4143
|
+
return resolve13(options.cwd ?? process.cwd(), parsed.values.get("--directory") ?? ".");
|
|
3884
4144
|
}
|
|
3885
4145
|
function auditLabel(status) {
|
|
3886
4146
|
if (status === "auditing") return "\u5BA1\u6838\u4E2D";
|
|
@@ -3955,17 +4215,18 @@ async function runPush(parsed, io, options) {
|
|
|
3955
4215
|
return { exitCode: 0, buddyId: config.buddyId, version: receipt.version };
|
|
3956
4216
|
}
|
|
3957
4217
|
async function runAvatar(parsed, io, options) {
|
|
3958
|
-
if (parsed.values.has("--
|
|
3959
|
-
throw new CloudCommandUsageError("\u5FC5\u987B\u9009\u62E9 --
|
|
4218
|
+
if (parsed.values.has("--file") === parsed.values.has("--clear")) {
|
|
4219
|
+
throw new CloudCommandUsageError("\u5FC5\u987B\u9009\u62E9 --file <\u56FE\u7247\u6587\u4EF6> \u6216 --clear\uFF0C\u4E14\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
|
|
3960
4220
|
}
|
|
3961
4221
|
if (parsed.values.has("--buddy-id") && parsed.values.has("--directory")) {
|
|
3962
4222
|
throw new CloudCommandUsageError("--buddy-id \u548C --directory \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
|
|
3963
4223
|
}
|
|
3964
|
-
const
|
|
4224
|
+
const file2 = parsed.values.get("--file");
|
|
4225
|
+
let upload;
|
|
3965
4226
|
try {
|
|
3966
|
-
|
|
4227
|
+
upload = file2 ? await readBuddyAvatarFile(resolve13(options.cwd ?? process.cwd(), file2)) : void 0;
|
|
3967
4228
|
} catch (error2) {
|
|
3968
|
-
throw new CloudCommandUsageError(error2 instanceof Error ? error2.message : "\u5934\u50CF\
|
|
4229
|
+
throw new CloudCommandUsageError(error2 instanceof Error ? error2.message : "\u65E0\u6CD5\u8BFB\u53D6\u5934\u50CF\u56FE\u7247");
|
|
3969
4230
|
}
|
|
3970
4231
|
let id = parsed.values.get("--buddy-id");
|
|
3971
4232
|
if (id !== void 0 && !/^[A-Za-z0-9_-]{1,128}$/u.test(id)) throw new CloudCommandUsageError("--buddy-id \u5FC5\u987B\u662F\u6709\u6548\u7684\u642D\u5B50 ID");
|
|
@@ -3978,12 +4239,13 @@ async function runAvatar(parsed, io, options) {
|
|
|
3978
4239
|
await session.token({ signal: options.signal });
|
|
3979
4240
|
const current = await client.getBuddy({ id });
|
|
3980
4241
|
if (current.id !== id) throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u5E73\u53F0\u8FD4\u56DE\u7684\u642D\u5B50\u8EAB\u4EFD\u4E0D\u5339\u914D");
|
|
3981
|
-
const updated = await client.updateBuddy({ id, meta: {
|
|
3982
|
-
|
|
4242
|
+
const updated = await client.updateBuddy({ id, meta: upload ? {} : { avatar: null }, ...upload ? { avatar: upload } : {} });
|
|
4243
|
+
const avatar = updated.meta?.avatar;
|
|
4244
|
+
if (updated.id !== id || (upload ? typeof avatar !== "string" || !avatar : avatar !== null)) {
|
|
3983
4245
|
throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u5E73\u53F0\u672A\u786E\u8BA4\u5934\u50CF\u66F4\u65B0\uFF0C\u8BF7\u67E5\u8BE2\u642D\u5B50\u8D44\u6599\u786E\u8BA4\u7ED3\u679C");
|
|
3984
4246
|
}
|
|
3985
4247
|
io.write(`${avatar === null ? "\u5DF2\u6E05\u9664" : "\u5DF2\u66F4\u65B0"}\u642D\u5B50\u5934\u50CF\uFF1A${updated.meta?.name ?? id}\uFF08${id}\uFF09`);
|
|
3986
|
-
if (avatar
|
|
4248
|
+
if (typeof avatar === "string") io.write(`\u5934\u50CF\uFF1A${avatar}`);
|
|
3987
4249
|
io.write("\u4E91\u7AEF\u8D44\u6599\u5DF2\u66F4\u65B0\uFF0C\u65E0\u9700 push \u6216\u91CD\u65B0\u53D1\u5E03\u7A0B\u5E8F\u5305\u3002");
|
|
3988
4250
|
return { exitCode: 0, buddyId: id };
|
|
3989
4251
|
}
|
|
@@ -4031,8 +4293,8 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
4031
4293
|
var CLOUD_COMMAND_HELP = HELP;
|
|
4032
4294
|
|
|
4033
4295
|
// packages/cli-core/src/migration/index.ts
|
|
4034
|
-
import { lstat as
|
|
4035
|
-
import { extname, isAbsolute as isAbsolute2, join as
|
|
4296
|
+
import { lstat as lstat8, open as open4, readdir as readdir4, realpath as realpath4, stat } from "node:fs/promises";
|
|
4297
|
+
import { extname, isAbsolute as isAbsolute2, join as join10, relative, resolve as resolve14, sep, win32 as win322 } from "node:path";
|
|
4036
4298
|
var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
|
|
4037
4299
|
maxFiles: 128,
|
|
4038
4300
|
maxFileBytes: 256 * 1024,
|
|
@@ -4185,7 +4447,7 @@ async function existingProjectRoot(value) {
|
|
|
4185
4447
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
4186
4448
|
}
|
|
4187
4449
|
try {
|
|
4188
|
-
const root = await realpath4(
|
|
4450
|
+
const root = await realpath4(resolve14(value));
|
|
4189
4451
|
if (!(await stat(root)).isDirectory()) {
|
|
4190
4452
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
4191
4453
|
}
|
|
@@ -4201,9 +4463,9 @@ async function existingProjectRoot(value) {
|
|
|
4201
4463
|
}
|
|
4202
4464
|
}
|
|
4203
4465
|
async function existingSkillRoot(projectRoot, skillPath) {
|
|
4204
|
-
const candidate = skillPath === "." ? projectRoot :
|
|
4466
|
+
const candidate = skillPath === "." ? projectRoot : join10(projectRoot, ...skillPath.split("/"));
|
|
4205
4467
|
try {
|
|
4206
|
-
await
|
|
4468
|
+
await lstat8(candidate);
|
|
4207
4469
|
} catch (cause) {
|
|
4208
4470
|
if (filesystemCode(cause) === "ENOENT") {
|
|
4209
4471
|
return rejectScan("SKILL_ROOT_NOT_FOUND", skillPath, "\u627E\u4E0D\u5230\u6307\u5B9A\u7684 Skill \u76EE\u5F55\u3002");
|
|
@@ -4238,9 +4500,9 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
4238
4500
|
}
|
|
4239
4501
|
async function assertEntrypoint(context) {
|
|
4240
4502
|
const entryPath = reportChild(context.skillPath, "SKILL.md");
|
|
4241
|
-
const candidate =
|
|
4503
|
+
const candidate = join10(context.skillRoot, "SKILL.md");
|
|
4242
4504
|
try {
|
|
4243
|
-
await
|
|
4505
|
+
await lstat8(candidate);
|
|
4244
4506
|
const target = await realpath4(candidate);
|
|
4245
4507
|
if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
|
|
4246
4508
|
rejectScan(
|
|
@@ -4342,7 +4604,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
4342
4604
|
}
|
|
4343
4605
|
for (const entry of entries) {
|
|
4344
4606
|
const childReportPath = reportChild(reportDirectory, entry.name);
|
|
4345
|
-
const childPath =
|
|
4607
|
+
const childPath = join10(directory, entry.name);
|
|
4346
4608
|
let target;
|
|
4347
4609
|
try {
|
|
4348
4610
|
target = await realpath4(childPath);
|
|
@@ -4384,7 +4646,7 @@ async function scanFile(actualPath, reportPath, context) {
|
|
|
4384
4646
|
}
|
|
4385
4647
|
let handle;
|
|
4386
4648
|
try {
|
|
4387
|
-
handle = await
|
|
4649
|
+
handle = await open4(actualPath, "r");
|
|
4388
4650
|
} catch (cause) {
|
|
4389
4651
|
return rejectScan("SKILL_SCAN_IO_ERROR", reportPath, "\u65E0\u6CD5\u6253\u5F00 Skill \u6587\u4EF6\u3002", cause);
|
|
4390
4652
|
}
|
|
@@ -4796,9 +5058,9 @@ async function scanExternalSkillPortability(options) {
|
|
|
4796
5058
|
}
|
|
4797
5059
|
|
|
4798
5060
|
// packages/cli-core/src/push/preflight.ts
|
|
4799
|
-
import { createHash as
|
|
4800
|
-
import { lstat as
|
|
4801
|
-
import { isAbsolute as isAbsolute3, join as
|
|
5061
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5062
|
+
import { lstat as lstat9, open as open5, readdir as readdir5, realpath as realpath5 } from "node:fs/promises";
|
|
5063
|
+
import { isAbsolute as isAbsolute3, join as join11, relative as relative2, resolve as resolve15, sep as sep2, win32 as win323 } from "node:path";
|
|
4802
5064
|
|
|
4803
5065
|
// packages/cli-core/src/push/errors.ts
|
|
4804
5066
|
var PushPreflightError = class extends Error {
|
|
@@ -4896,7 +5158,7 @@ function error(options) {
|
|
|
4896
5158
|
throw new PushPreflightError(options);
|
|
4897
5159
|
}
|
|
4898
5160
|
function sha256(value) {
|
|
4899
|
-
return `sha256:${
|
|
5161
|
+
return `sha256:${createHash6("sha256").update(value).digest("hex")}`;
|
|
4900
5162
|
}
|
|
4901
5163
|
function positiveLimit(value, name, hardMaximum) {
|
|
4902
5164
|
if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
|
|
@@ -4986,10 +5248,10 @@ function normalizeBookletPath(value) {
|
|
|
4986
5248
|
return normalized;
|
|
4987
5249
|
}
|
|
4988
5250
|
async function canonicalProjectRoot(projectRoot) {
|
|
4989
|
-
const absolute =
|
|
5251
|
+
const absolute = resolve15(projectRoot);
|
|
4990
5252
|
let entry;
|
|
4991
5253
|
try {
|
|
4992
|
-
entry = await
|
|
5254
|
+
entry = await lstat9(absolute);
|
|
4993
5255
|
} catch (cause) {
|
|
4994
5256
|
error({
|
|
4995
5257
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -5017,10 +5279,10 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
5017
5279
|
}
|
|
5018
5280
|
}
|
|
5019
5281
|
async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
5020
|
-
const path =
|
|
5282
|
+
const path = join11(projectRoot, filename);
|
|
5021
5283
|
let entry;
|
|
5022
5284
|
try {
|
|
5023
|
-
entry = await
|
|
5285
|
+
entry = await lstat9(path);
|
|
5024
5286
|
} catch (cause) {
|
|
5025
5287
|
error({
|
|
5026
5288
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -5127,7 +5389,7 @@ function sameFileIdentity(left, right) {
|
|
|
5127
5389
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs;
|
|
5128
5390
|
}
|
|
5129
5391
|
async function hashRegularFile(options) {
|
|
5130
|
-
const handle = await
|
|
5392
|
+
const handle = await open5(options.absolutePath, "r").catch((cause) => error({
|
|
5131
5393
|
code: "PUSH_PROJECT_INVALID",
|
|
5132
5394
|
path: options.portablePath,
|
|
5133
5395
|
message: `\u65E0\u6CD5\u8BFB\u53D6\u63D0\u4EA4\u6587\u4EF6\uFF1A${options.portablePath}`,
|
|
@@ -5142,7 +5404,7 @@ async function hashRegularFile(options) {
|
|
|
5142
5404
|
message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
|
|
5143
5405
|
});
|
|
5144
5406
|
}
|
|
5145
|
-
const hash =
|
|
5407
|
+
const hash = createHash6("sha256");
|
|
5146
5408
|
const captured = [];
|
|
5147
5409
|
const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
|
|
5148
5410
|
let bytes = 0;
|
|
@@ -5220,10 +5482,10 @@ async function createManifest(options) {
|
|
|
5220
5482
|
sensitiveEntriesExcluded += 1;
|
|
5221
5483
|
continue;
|
|
5222
5484
|
}
|
|
5223
|
-
const absolutePath =
|
|
5485
|
+
const absolutePath = join11(directory, name);
|
|
5224
5486
|
let entry;
|
|
5225
5487
|
try {
|
|
5226
|
-
entry = await
|
|
5488
|
+
entry = await lstat9(absolutePath);
|
|
5227
5489
|
} catch (cause) {
|
|
5228
5490
|
error({
|
|
5229
5491
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5472,8 +5734,8 @@ async function assertCapturedConfigStable(options) {
|
|
|
5472
5734
|
}
|
|
5473
5735
|
let current;
|
|
5474
5736
|
try {
|
|
5475
|
-
const path =
|
|
5476
|
-
const entry = await
|
|
5737
|
+
const path = join11(options.projectRoot, options.filename);
|
|
5738
|
+
const entry = await lstat9(path);
|
|
5477
5739
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
5478
5740
|
error({
|
|
5479
5741
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5556,14 +5818,15 @@ async function createPushPreflight(options) {
|
|
|
5556
5818
|
maxFileBytes: limits.maxFileBytes
|
|
5557
5819
|
});
|
|
5558
5820
|
const config = parseCapturedProject(agentEntry);
|
|
5559
|
-
if (config.datasets?.length) {
|
|
5821
|
+
if (config.datasets?.length || config.model !== void 0) {
|
|
5560
5822
|
try {
|
|
5561
|
-
await validateRuntimeDatasets(projectRoot, config.datasets);
|
|
5823
|
+
if (config.datasets?.length) await validateRuntimeDatasets(projectRoot, config.datasets);
|
|
5824
|
+
await validateRuntimeModel(projectRoot, config.model);
|
|
5562
5825
|
} catch (cause) {
|
|
5563
5826
|
error({
|
|
5564
5827
|
code: "PUSH_CONFIG_INVALID",
|
|
5565
5828
|
path: BUDDY_AGENT_FILENAME,
|
|
5566
|
-
message: `\u65E0\u6CD5\u6309\u5DE5\u7A0B Runtime \u6821\u9A8C\u6570\u636E\u58F0\u660E\uFF1A${cause instanceof Error ? cause.message : String(cause)}`,
|
|
5829
|
+
message: `\u65E0\u6CD5\u6309\u5DE5\u7A0B Runtime \u6821\u9A8C\u6A21\u578B\u6216\u6570\u636E\u58F0\u660E\uFF1A${cause instanceof Error ? cause.message : String(cause)}`,
|
|
5567
5830
|
cause
|
|
5568
5831
|
});
|
|
5569
5832
|
}
|
|
@@ -5607,16 +5870,16 @@ async function createPushPreflight(options) {
|
|
|
5607
5870
|
}
|
|
5608
5871
|
|
|
5609
5872
|
// packages/cli-core/src/push/bundle.ts
|
|
5610
|
-
import { createHash as
|
|
5611
|
-
import { lstat as
|
|
5612
|
-
import { join as
|
|
5873
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
5874
|
+
import { lstat as lstat10, readFile as readFile10, realpath as realpath6 } from "node:fs/promises";
|
|
5875
|
+
import { join as join12, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
|
|
5613
5876
|
import { Writable } from "node:stream";
|
|
5614
5877
|
import { pipeline } from "node:stream/promises";
|
|
5615
5878
|
import { createGzip } from "node:zlib";
|
|
5616
5879
|
import { pack } from "tar-stream";
|
|
5617
5880
|
var MEDIA_TYPE = "application/gzip";
|
|
5618
5881
|
function digest(bytes) {
|
|
5619
|
-
return `sha256:${
|
|
5882
|
+
return `sha256:${createHash7("sha256").update(bytes).digest("hex")}`;
|
|
5620
5883
|
}
|
|
5621
5884
|
function changed(path, message, cause) {
|
|
5622
5885
|
throw new PushPreflightError({
|
|
@@ -5631,15 +5894,15 @@ function inside(root, target) {
|
|
|
5631
5894
|
return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
|
|
5632
5895
|
}
|
|
5633
5896
|
async function capture(root, file2) {
|
|
5634
|
-
const absolute =
|
|
5897
|
+
const absolute = join12(root, ...file2.path.split("/"));
|
|
5635
5898
|
try {
|
|
5636
|
-
const entry = await
|
|
5899
|
+
const entry = await lstat10(absolute);
|
|
5637
5900
|
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !== file2.bytes) {
|
|
5638
5901
|
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5639
5902
|
}
|
|
5640
5903
|
const canonical = await realpath6(absolute);
|
|
5641
5904
|
if (!inside(root, canonical)) changed(file2.path, `\u63D0\u4EA4\u6587\u4EF6\u8D8A\u8FC7\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${file2.path}`);
|
|
5642
|
-
const contents = await
|
|
5905
|
+
const contents = await readFile10(absolute);
|
|
5643
5906
|
if (contents.byteLength !== file2.bytes || digest(contents) !== file2.digest) {
|
|
5644
5907
|
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5645
5908
|
}
|
|
@@ -5665,7 +5928,7 @@ async function createPushSourceBundle(plan) {
|
|
|
5665
5928
|
schemaVersion: "1",
|
|
5666
5929
|
files: files.map((file2) => ({ path: file2.path, bytes: file2.bytes, digest: file2.digest }))
|
|
5667
5930
|
});
|
|
5668
|
-
const calculatedBundleDigest = `sha256:${
|
|
5931
|
+
const calculatedBundleDigest = `sha256:${createHash7("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
|
|
5669
5932
|
if (calculatedBundleDigest !== plan.bundleDigest) {
|
|
5670
5933
|
changed(plan.projectRoot, "\u63D0\u4EA4\u6E05\u5355\u8EAB\u4EFD\u5728\u6253\u5305\u524D\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C push");
|
|
5671
5934
|
}
|
|
@@ -5720,25 +5983,29 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5720
5983
|
|
|
5721
5984
|
\u7528\u6CD5:
|
|
5722
5985
|
buddy-cli
|
|
5986
|
+
buddy-cli --version [--json]
|
|
5987
|
+
buddy-cli update [--check] [--json]
|
|
5723
5988
|
buddy-cli init
|
|
5724
5989
|
buddy-cli init --mode create --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>]
|
|
5725
5990
|
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>]
|
|
5726
5991
|
buddy-cli dev [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5727
5992
|
buddy-cli login | logout | push | status
|
|
5728
|
-
buddy-cli avatar (--
|
|
5729
|
-
buddy-cli models [--json]
|
|
5993
|
+
buddy-cli avatar (--file <\u56FE\u7247\u6587\u4EF6> | --clear) [--directory <\u9879\u76EE\u76EE\u5F55> | --buddy-id <\u642D\u5B50 ID>]
|
|
5994
|
+
buddy-cli models [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5730
5995
|
buddy-cli widgets sync | check | verify
|
|
5731
5996
|
buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5732
5997
|
buddy-cli preview [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5733
5998
|
buddy-cli help
|
|
5734
5999
|
|
|
5735
6000
|
\u547D\u4EE4:
|
|
6001
|
+
--version \u67E5\u770B\u5F53\u524D\u8FD0\u884C\u7684 CLI \u7248\u672C\uFF08\u65E0\u9700\u8054\u7F51\u6216\u767B\u5F55\uFF09
|
|
6002
|
+
update \u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\u6216\u66F4\u65B0\u5168\u5C40 CLI\uFF08\u65E0\u9700\u767B\u5F55\u6216 Agent \u5DE5\u7A0B\uFF09
|
|
5736
6003
|
init \u521B\u5EFA\u6216\u6062\u590D\u642D\u5B50\uFF0C\u51C6\u5907\u5B98\u65B9 Runtime \u5DE5\u7A0B\u6216 Creator \u624B\u518C
|
|
5737
6004
|
dev \u542F\u52A8\u5B98\u65B9 Runtime \u5E76\u8FDE\u63A5 DEV App Server
|
|
5738
6005
|
login \u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u642D\u642D\u8D26\u53F7
|
|
5739
6006
|
logout \u9000\u51FA\u642D\u642D\u5F00\u53D1\u8005\u8D26\u53F7\u5E76\u6E05\u9664\u672C\u673A\u767B\u5F55\u72B6\u6001
|
|
5740
|
-
models \u67E5\u770B
|
|
5741
|
-
datasets \
|
|
6007
|
+
models \u67E5\u770B Runtime \u6A21\u578B ID\uFF08\u5DE5\u7A0B\u5916\u8054\u7F51\u8BFB\u53D6\u6700\u65B0\u7248\uFF0C\u65E0\u9700\u767B\u5F55\uFF09
|
|
6008
|
+
datasets \u67E5\u770B Runtime \u6570\u636E\u96C6\u76EE\u5F55\uFF08\u5DE5\u7A0B\u5916\u8054\u7F51\u8BFB\u53D6\u6700\u65B0\u7248\uFF0C\u65E0\u9700\u767B\u5F55\uFF09
|
|
5742
6009
|
push \u4E0A\u4F20 Agent \u5DE5\u7A0B\u548C commit\uFF0C\u63D0\u4EA4\u5E73\u53F0\u5BA1\u6838
|
|
5743
6010
|
status \u67E5\u8BE2\u642D\u5B50\u7684\u8D44\u6599\u3001\u4EE3\u7801\u3001\u8FD0\u884C\u4E0E\u53D1\u5E03\u72B6\u6001
|
|
5744
6011
|
avatar \u8BBE\u7F6E\u6216\u6E05\u9664\u642D\u5B50\u5934\u50CF\uFF0C\u4FDD\u7559\u5176\u4ED6\u4E91\u7AEF\u8D44\u6599
|
|
@@ -5947,7 +6214,7 @@ function defaultProjectDirectory(name) {
|
|
|
5947
6214
|
return `./${folder}`;
|
|
5948
6215
|
}
|
|
5949
6216
|
async function initializeProjectRoute(route, args, io, options, application) {
|
|
5950
|
-
const cwd =
|
|
6217
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
5951
6218
|
const { name, tagline, intro } = application;
|
|
5952
6219
|
const directoryInput = await textAnswer(
|
|
5953
6220
|
io,
|
|
@@ -5956,7 +6223,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
5956
6223
|
"\u9879\u76EE\u76EE\u5F55\uFF08\u7A7A\u76EE\u5F55\u6216\u5DF2\u6709\u642D\u642D\u5DE5\u7A0B\uFF09",
|
|
5957
6224
|
defaultProjectDirectory(name)
|
|
5958
6225
|
);
|
|
5959
|
-
const rootDirectory =
|
|
6226
|
+
const rootDirectory = resolve16(cwd, directoryInput);
|
|
5960
6227
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
5961
6228
|
const initializationTarget = { directory: rootDirectory, kind: "new" };
|
|
5962
6229
|
const initializationPlan = options.initializeProject ? void 0 : await planBuddyProjectInitialization(initializationTarget);
|
|
@@ -6000,7 +6267,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
6000
6267
|
}
|
|
6001
6268
|
async function runRoute(route, args, io, options) {
|
|
6002
6269
|
if (!io.canPrompt) assertNonInteractiveProjectArguments(route, args);
|
|
6003
|
-
const cwd =
|
|
6270
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
6004
6271
|
const name = await textAnswer(io, args.name, "--name", "\u642D\u5B50\u540D\u79F0", void 0, BUDDY_PROFILE_TEXT_LIMITS.name);
|
|
6005
6272
|
const tagline = await textAnswer(
|
|
6006
6273
|
io,
|
|
@@ -6028,7 +6295,7 @@ async function runRoute(route, args, io, options) {
|
|
|
6028
6295
|
"\u9879\u76EE\u76EE\u5F55",
|
|
6029
6296
|
defaultProjectDirectory(name)
|
|
6030
6297
|
);
|
|
6031
|
-
const rootDirectory =
|
|
6298
|
+
const rootDirectory = resolve16(cwd, directoryInput);
|
|
6032
6299
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
6033
6300
|
const plan = await planBuddyDraftInitialization(rootDirectory);
|
|
6034
6301
|
const existingBuddyId = plan.existingManifest?.buddyId;
|
|
@@ -6062,11 +6329,11 @@ async function runRoute(route, args, io, options) {
|
|
|
6062
6329
|
return { ...result, route: "create", draft, buddyId };
|
|
6063
6330
|
}
|
|
6064
6331
|
async function resumeProjectArguments(args, cwd, options) {
|
|
6065
|
-
const root =
|
|
6332
|
+
const root = resolve16(cwd, args.directory?.trim() || ".");
|
|
6066
6333
|
await assertInitializableBuddyTarget(root);
|
|
6067
6334
|
let entry;
|
|
6068
6335
|
try {
|
|
6069
|
-
entry = await
|
|
6336
|
+
entry = await lstat11(join13(root, "buddy.agent.json"));
|
|
6070
6337
|
} catch (cause) {
|
|
6071
6338
|
if (cause.code === "ENOENT") return;
|
|
6072
6339
|
throw cause;
|
|
@@ -6095,7 +6362,7 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6095
6362
|
try {
|
|
6096
6363
|
const [command, ...rest] = args;
|
|
6097
6364
|
if (!command) {
|
|
6098
|
-
const cwd =
|
|
6365
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
6099
6366
|
let projectRoot;
|
|
6100
6367
|
try {
|
|
6101
6368
|
projectRoot = await findBuddyProjectRoot(cwd);
|
|
@@ -6123,13 +6390,13 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6123
6390
|
io.write(HELP2);
|
|
6124
6391
|
return { exitCode: 0 };
|
|
6125
6392
|
}
|
|
6126
|
-
if (command === "models") return runModelsCommand(rest, io);
|
|
6393
|
+
if (command === "models") return await runModelsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
|
|
6127
6394
|
if (command === "widgets") return await runWidgetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6128
6395
|
if (command === "preview") {
|
|
6129
6396
|
return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
|
|
6130
6397
|
}
|
|
6131
6398
|
if (command === "datasets") {
|
|
6132
|
-
return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6399
|
+
return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
|
|
6133
6400
|
}
|
|
6134
6401
|
if (command === "dev") {
|
|
6135
6402
|
return await runDevCommand(rest, io, {
|
|
@@ -6151,11 +6418,11 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6151
6418
|
}
|
|
6152
6419
|
let route = routeFromMode(parsed.mode);
|
|
6153
6420
|
if (route === void 0 && !io.canPrompt) requireModeForNonInteractive();
|
|
6154
|
-
const cwd =
|
|
6421
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
6155
6422
|
await assertInitializableBuddyTarget(cwd);
|
|
6156
6423
|
if (parsed.directory !== void 0) {
|
|
6157
6424
|
if (!parsed.directory.trim()) throw new CliUsageError("--directory \u4E0D\u80FD\u4E3A\u7A7A");
|
|
6158
|
-
await assertInitializableBuddyTarget(
|
|
6425
|
+
await assertInitializableBuddyTarget(resolve16(cwd, parsed.directory));
|
|
6159
6426
|
}
|
|
6160
6427
|
await authenticate(options);
|
|
6161
6428
|
await resumeProjectArguments(parsed, cwd, options);
|
|
@@ -6308,6 +6575,7 @@ export {
|
|
|
6308
6575
|
isAbortLike,
|
|
6309
6576
|
isSafeProjectRelativePath,
|
|
6310
6577
|
isWindowsReservedPathSegment,
|
|
6578
|
+
latestRuntimeTemplate,
|
|
6311
6579
|
normalizePlatformUrl,
|
|
6312
6580
|
openExternalUrl,
|
|
6313
6581
|
parseBuddyAgentConfig,
|