@my-life-buddies/cli 0.8.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 +11 -11
- package/dist/bin/buddy.js +8 -0
- package/dist/bin/buddy.js.map +2 -2
- package/dist/bin/core.js +510 -291
- 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,11 @@ 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.12.
|
|
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
|
|
1470
1685
|
|
|
1471
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
|
|
1472
1687
|
|
|
@@ -1544,16 +1759,16 @@ var REQUIRED = [
|
|
|
1544
1759
|
var CreatorPreparationError = class extends Error {
|
|
1545
1760
|
};
|
|
1546
1761
|
async function state(path) {
|
|
1547
|
-
return
|
|
1762
|
+
return lstat7(path).catch((cause) => {
|
|
1548
1763
|
if (cause.code === "ENOENT") return void 0;
|
|
1549
1764
|
throw cause;
|
|
1550
1765
|
});
|
|
1551
1766
|
}
|
|
1552
1767
|
async function safeDirectory(path) {
|
|
1553
|
-
await
|
|
1768
|
+
await mkdir5(path, { mode: 448 }).catch((cause) => {
|
|
1554
1769
|
if (cause.code !== "EEXIST") throw cause;
|
|
1555
1770
|
});
|
|
1556
|
-
if (!(await
|
|
1771
|
+
if (!(await lstat7(path)).isDirectory()) {
|
|
1557
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}`);
|
|
1558
1773
|
}
|
|
1559
1774
|
}
|
|
@@ -1563,11 +1778,11 @@ async function safeRead(path) {
|
|
|
1563
1778
|
if (!entry.isFile() || entry.size > MAX_LOCAL_FILE_BYTES) {
|
|
1564
1779
|
throw new CreatorPreparationError(`\u521B\u4F5C\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6\u6216\u8D85\u51FA\u5927\u5C0F\u9650\u5236\uFF1A${path}`);
|
|
1565
1780
|
}
|
|
1566
|
-
return
|
|
1781
|
+
return readFile7(path);
|
|
1567
1782
|
}
|
|
1568
1783
|
async function writeOnce(path, bytes) {
|
|
1569
1784
|
const temporary = `${path}.${randomUUID2()}.tmp`;
|
|
1570
|
-
await
|
|
1785
|
+
await writeFile4(temporary, bytes, { flag: "wx", mode: 384 });
|
|
1571
1786
|
try {
|
|
1572
1787
|
await link2(temporary, path).catch((cause) => {
|
|
1573
1788
|
if (cause.code !== "EEXIST") throw cause;
|
|
@@ -1578,17 +1793,17 @@ async function writeOnce(path, bytes) {
|
|
|
1578
1793
|
}
|
|
1579
1794
|
async function bundledSkill(root) {
|
|
1580
1795
|
for (const path of REQUIRED) {
|
|
1581
|
-
if (!(await state(
|
|
1796
|
+
if (!(await state(join7(root, path)))?.isFile()) {
|
|
1582
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`);
|
|
1583
1798
|
}
|
|
1584
1799
|
}
|
|
1585
1800
|
return root;
|
|
1586
1801
|
}
|
|
1587
1802
|
function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
1588
|
-
const contextPath =
|
|
1803
|
+
const contextPath = join7(context.projectRoot, ".buddy", "creator", "initial-context.json");
|
|
1589
1804
|
const openArgs = [
|
|
1590
1805
|
"-B",
|
|
1591
|
-
|
|
1806
|
+
join7(skillRoot, "scripts/buddy.py"),
|
|
1592
1807
|
"open",
|
|
1593
1808
|
"--workspace",
|
|
1594
1809
|
workspacePath,
|
|
@@ -1618,7 +1833,7 @@ function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
|
1618
1833
|
|
|
1619
1834
|
## \u73B0\u5728\u7531 Coding Agent \u6267\u884C
|
|
1620
1835
|
|
|
1621
|
-
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
|
|
1622
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
|
|
1623
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
|
|
1624
1839
|
|
|
@@ -1655,11 +1870,11 @@ ${DEVELOPMENT_HANDOFF}
|
|
|
1655
1870
|
`;
|
|
1656
1871
|
}
|
|
1657
1872
|
async function prepareCreatorSkill(context, io, options = {}) {
|
|
1658
|
-
const root =
|
|
1873
|
+
const root = join7(context.projectRoot, ".buddy", "creator");
|
|
1659
1874
|
try {
|
|
1660
|
-
await safeDirectory(
|
|
1875
|
+
await safeDirectory(join7(context.projectRoot, ".buddy"));
|
|
1661
1876
|
await safeDirectory(root);
|
|
1662
|
-
const sessionPath =
|
|
1877
|
+
const sessionPath = join7(root, "session.json");
|
|
1663
1878
|
await writeOnce(sessionPath, JSON.stringify({
|
|
1664
1879
|
schemaVersion: 1,
|
|
1665
1880
|
platformBuddyId: context.buddyId,
|
|
@@ -1671,11 +1886,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1671
1886
|
}
|
|
1672
1887
|
options.signal?.throwIfAborted();
|
|
1673
1888
|
const skillRoot = await bundledSkill(options.bundledSkillRoot ?? BUNDLED_CREATOR_SKILL_ROOT);
|
|
1674
|
-
await safeDirectory(
|
|
1675
|
-
const workspacePath =
|
|
1889
|
+
await safeDirectory(join7(root, "workspaces"));
|
|
1890
|
+
const workspacePath = join7(root, "workspaces", session.creationKey);
|
|
1676
1891
|
const workspace = await state(workspacePath);
|
|
1677
1892
|
if (workspace && !workspace.isDirectory()) throw new CreatorPreparationError("\u5DF2\u6709\u521B\u4F5C\u5DE5\u4F5C\u533A\u4E0D\u662F\u666E\u901A\u76EE\u5F55\u3002");
|
|
1678
|
-
const contextPath =
|
|
1893
|
+
const contextPath = join7(root, "initial-context.json");
|
|
1679
1894
|
await writeOnce(contextPath, JSON.stringify({
|
|
1680
1895
|
source: "platform_meta",
|
|
1681
1896
|
name: context.appName,
|
|
@@ -1691,7 +1906,7 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1691
1906
|
} catch {
|
|
1692
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}`);
|
|
1693
1908
|
}
|
|
1694
|
-
const handoffPath =
|
|
1909
|
+
const handoffPath = join7(root, "HANDOFF.md");
|
|
1695
1910
|
const handoff = handoffMarkdown({
|
|
1696
1911
|
...context,
|
|
1697
1912
|
appName: initial.name,
|
|
@@ -1702,11 +1917,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1702
1917
|
if ((await safeRead(handoffPath))?.toString("utf8") !== handoff) {
|
|
1703
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}`);
|
|
1704
1919
|
}
|
|
1705
|
-
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")}`);
|
|
1706
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`);
|
|
1707
1922
|
io.write(DEVELOPMENT_HANDOFF_SUMMARY);
|
|
1708
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");
|
|
1709
|
-
return { exitCode: 0, creator: { skillPath:
|
|
1924
|
+
return { exitCode: 0, creator: { skillPath: join7(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
|
|
1710
1925
|
} catch (cause) {
|
|
1711
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";
|
|
1712
1927
|
io.writeError(`Creator Skill \u51C6\u5907\u5931\u8D25\uFF1A${message}`);
|
|
@@ -1766,7 +1981,7 @@ function validateBuddyMetaUpdate(meta2) {
|
|
|
1766
1981
|
}
|
|
1767
1982
|
|
|
1768
1983
|
// packages/cli-core/src/commands/dev.ts
|
|
1769
|
-
import { resolve as
|
|
1984
|
+
import { resolve as resolve12 } from "node:path";
|
|
1770
1985
|
|
|
1771
1986
|
// packages/cli-core/src/platform-url.ts
|
|
1772
1987
|
var DEFAULT_MLB_TEST_URL = "http://47.116.168.81:8788";
|
|
@@ -2064,15 +2279,15 @@ function buildAgentEnvironment(options) {
|
|
|
2064
2279
|
|
|
2065
2280
|
// packages/cli-core/src/dev/orchestrator.ts
|
|
2066
2281
|
import { createHash as createHash3 } from "node:crypto";
|
|
2067
|
-
import { readFile as
|
|
2068
|
-
import { join as
|
|
2282
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
2283
|
+
import { join as join9, resolve as resolve10 } from "node:path";
|
|
2069
2284
|
|
|
2070
2285
|
// packages/cli-core/src/dev/launch.ts
|
|
2071
2286
|
import { lstatSync, readFileSync } from "node:fs";
|
|
2072
2287
|
import { createRequire as createRequire2 } from "node:module";
|
|
2073
|
-
import { dirname as dirname2, resolve as
|
|
2288
|
+
import { dirname as dirname2, resolve as resolve9 } from "node:path";
|
|
2074
2289
|
async function officialAgentLaunchPlan(input) {
|
|
2075
|
-
const entry =
|
|
2290
|
+
const entry = resolve9(input.projectRoot, BUDDY_PLATFORM_START_ENTRY);
|
|
2076
2291
|
for (const [filename, kind] of [
|
|
2077
2292
|
[dirname2(BUDDY_PLATFORM_FACTORY_ENTRY), "directory"],
|
|
2078
2293
|
[BUDDY_PLATFORM_START_ENTRY, "file"],
|
|
@@ -2080,7 +2295,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2080
2295
|
[BUDDY_PLATFORM_PACKAGE_JSON, "file"]
|
|
2081
2296
|
]) {
|
|
2082
2297
|
try {
|
|
2083
|
-
const entry2 = lstatSync(
|
|
2298
|
+
const entry2 = lstatSync(resolve9(input.projectRoot, filename));
|
|
2084
2299
|
if (!(kind === "directory" ? entry2.isDirectory() : entry2.isFile())) {
|
|
2085
2300
|
throw new Error(`${filename} must be a regular ${kind}`);
|
|
2086
2301
|
}
|
|
@@ -2093,7 +2308,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2093
2308
|
});
|
|
2094
2309
|
}
|
|
2095
2310
|
}
|
|
2096
|
-
const packagePath =
|
|
2311
|
+
const packagePath = resolve9(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
|
|
2097
2312
|
try {
|
|
2098
2313
|
const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
|
|
2099
2314
|
if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
@@ -2101,6 +2316,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2101
2316
|
const installed = JSON.parse(readFileSync(runtimePath, "utf8")).version;
|
|
2102
2317
|
if (installed !== expected) throw new Error(`Runtime \u5DF2\u5B89\u88C5 ${installed}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
|
|
2103
2318
|
await validateRuntimeDatasets(input.projectRoot, input.datasets);
|
|
2319
|
+
await validateRuntimeModel(input.projectRoot, input.model);
|
|
2104
2320
|
} catch (cause) {
|
|
2105
2321
|
throw new BuddyDevError({
|
|
2106
2322
|
code: "DEV_CONFIG_INVALID",
|
|
@@ -2143,16 +2359,16 @@ var NodeAgentProcessLauncher = class {
|
|
|
2143
2359
|
}
|
|
2144
2360
|
let hasExited = false;
|
|
2145
2361
|
let spawned = false;
|
|
2146
|
-
const exit = new Promise((
|
|
2362
|
+
const exit = new Promise((resolve17) => {
|
|
2147
2363
|
child.once("error", (error2) => {
|
|
2148
2364
|
if (!spawned) {
|
|
2149
2365
|
hasExited = true;
|
|
2150
|
-
|
|
2366
|
+
resolve17({ code: null, signal: null, error: error2 });
|
|
2151
2367
|
}
|
|
2152
2368
|
});
|
|
2153
2369
|
child.once("exit", (code2, signal) => {
|
|
2154
2370
|
hasExited = true;
|
|
2155
|
-
|
|
2371
|
+
resolve17({ code: code2, signal });
|
|
2156
2372
|
});
|
|
2157
2373
|
});
|
|
2158
2374
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -2168,11 +2384,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
2168
2384
|
}
|
|
2169
2385
|
});
|
|
2170
2386
|
try {
|
|
2171
|
-
await new Promise((
|
|
2387
|
+
await new Promise((resolve17, reject) => {
|
|
2172
2388
|
const onSpawn = () => {
|
|
2173
2389
|
spawned = true;
|
|
2174
2390
|
child.off("error", onError);
|
|
2175
|
-
|
|
2391
|
+
resolve17();
|
|
2176
2392
|
};
|
|
2177
2393
|
const onError = (error2) => {
|
|
2178
2394
|
child.off("spawn", onSpawn);
|
|
@@ -2211,11 +2427,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
2211
2427
|
};
|
|
2212
2428
|
function waitForExit(process2, timeoutMs) {
|
|
2213
2429
|
if (process2.hasExited()) return Promise.resolve(true);
|
|
2214
|
-
return new Promise((
|
|
2215
|
-
const timeout = setTimeout(() =>
|
|
2430
|
+
return new Promise((resolve17) => {
|
|
2431
|
+
const timeout = setTimeout(() => resolve17(false), timeoutMs);
|
|
2216
2432
|
process2.exit.then(() => {
|
|
2217
2433
|
clearTimeout(timeout);
|
|
2218
|
-
|
|
2434
|
+
resolve17(true);
|
|
2219
2435
|
});
|
|
2220
2436
|
});
|
|
2221
2437
|
}
|
|
@@ -2298,7 +2514,7 @@ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
|
|
|
2298
2514
|
while (isGroupAlive(pid)) {
|
|
2299
2515
|
const remaining = deadline - Date.now();
|
|
2300
2516
|
if (remaining <= 0) return false;
|
|
2301
|
-
await new Promise((
|
|
2517
|
+
await new Promise((resolve17) => setTimeout(resolve17, Math.min(25, remaining)));
|
|
2302
2518
|
}
|
|
2303
2519
|
return true;
|
|
2304
2520
|
}
|
|
@@ -2313,9 +2529,9 @@ function processTreeControllerFor(platform) {
|
|
|
2313
2529
|
|
|
2314
2530
|
// packages/cli-core/src/dev/instance-lock.ts
|
|
2315
2531
|
import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
|
|
2316
|
-
import { mkdir as
|
|
2317
|
-
import { homedir } from "node:os";
|
|
2318
|
-
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";
|
|
2319
2535
|
function alreadyRunningError() {
|
|
2320
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";
|
|
2321
2537
|
return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
|
|
@@ -2335,20 +2551,20 @@ function code(error2) {
|
|
|
2335
2551
|
return error2?.code;
|
|
2336
2552
|
}
|
|
2337
2553
|
async function acquireDevInstanceLock(options) {
|
|
2338
|
-
const root = options.directory ??
|
|
2554
|
+
const root = options.directory ?? join8(homedir2(), ".buddy", "dev-locks");
|
|
2339
2555
|
const key = createHash2("sha256").update(`${new URL(options.gatewayUrl).origin}
|
|
2340
2556
|
${options.devAgentId}`).digest("hex");
|
|
2341
|
-
const path =
|
|
2557
|
+
const path = join8(root, key);
|
|
2342
2558
|
const ownerName = `owner-${randomUUID3()}.json`;
|
|
2343
|
-
const candidate =
|
|
2559
|
+
const candidate = join8(root, `.pending-${randomUUID3()}`);
|
|
2344
2560
|
const owner = { pid: process.pid };
|
|
2345
|
-
await
|
|
2346
|
-
await
|
|
2347
|
-
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" });
|
|
2348
2564
|
try {
|
|
2349
2565
|
for (let attempt = 0; ; attempt++) {
|
|
2350
2566
|
try {
|
|
2351
|
-
await
|
|
2567
|
+
await rename4(candidate, path);
|
|
2352
2568
|
break;
|
|
2353
2569
|
} catch (error2) {
|
|
2354
2570
|
if (!["EEXIST", "ENOTEMPTY"].includes(code(error2) ?? "")) throw error2;
|
|
@@ -2363,10 +2579,10 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2363
2579
|
}
|
|
2364
2580
|
if (names.length === 0) continue;
|
|
2365
2581
|
if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw alreadyRunningError();
|
|
2366
|
-
const previousPath =
|
|
2582
|
+
const previousPath = join8(path, names[0]);
|
|
2367
2583
|
let previous;
|
|
2368
2584
|
try {
|
|
2369
|
-
previous = JSON.parse(await
|
|
2585
|
+
previous = JSON.parse(await readFile8(previousPath, "utf8"));
|
|
2370
2586
|
} catch (error2) {
|
|
2371
2587
|
if (code(error2) === "ENOENT") continue;
|
|
2372
2588
|
throw alreadyRunningError();
|
|
@@ -2383,17 +2599,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2383
2599
|
});
|
|
2384
2600
|
}
|
|
2385
2601
|
} finally {
|
|
2386
|
-
await unlink4(
|
|
2602
|
+
await unlink4(join8(candidate, ownerName)).catch(() => void 0);
|
|
2387
2603
|
await rmdir2(candidate).catch(() => void 0);
|
|
2388
2604
|
}
|
|
2389
2605
|
let released = false;
|
|
2390
2606
|
return {
|
|
2391
2607
|
async attachRuntime(pid) {
|
|
2392
2608
|
if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
|
|
2393
|
-
const temporary =
|
|
2609
|
+
const temporary = join8(root, `.update-${randomUUID3()}`);
|
|
2394
2610
|
try {
|
|
2395
|
-
await
|
|
2396
|
-
await
|
|
2611
|
+
await writeFile5(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
|
|
2612
|
+
await rename4(temporary, join8(path, ownerName));
|
|
2397
2613
|
} finally {
|
|
2398
2614
|
await unlink4(temporary).catch(() => void 0);
|
|
2399
2615
|
}
|
|
@@ -2401,7 +2617,7 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2401
2617
|
async release() {
|
|
2402
2618
|
if (released) return;
|
|
2403
2619
|
released = true;
|
|
2404
|
-
await unlink4(
|
|
2620
|
+
await unlink4(join8(path, ownerName)).catch((error2) => {
|
|
2405
2621
|
if (code(error2) !== "ENOENT") throw error2;
|
|
2406
2622
|
});
|
|
2407
2623
|
await rmdir2(path).catch((error2) => {
|
|
@@ -2454,6 +2670,7 @@ function validateLaunchPlan(value) {
|
|
|
2454
2670
|
async function launchPlanFor(config, options, projectRoot, signal) {
|
|
2455
2671
|
const provider = options.platformLaunchPlan ?? ((context) => officialAgentLaunchPlan({
|
|
2456
2672
|
datasets: config.datasets,
|
|
2673
|
+
model: config.model,
|
|
2457
2674
|
projectRoot: context.projectRoot
|
|
2458
2675
|
}));
|
|
2459
2676
|
try {
|
|
@@ -2644,7 +2861,7 @@ async function startBuddyDev(options) {
|
|
|
2644
2861
|
const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS, "pollIntervalMs");
|
|
2645
2862
|
const stopGracePeriodMs = positiveDuration(options.stopGracePeriodMs ?? DEFAULT_STOP_GRACE_PERIOD_MS, "stopGracePeriodMs");
|
|
2646
2863
|
const devAgentId = safeDevAgentId(options.devAgentId);
|
|
2647
|
-
const projectRoot =
|
|
2864
|
+
const projectRoot = resolve10(options.projectRoot);
|
|
2648
2865
|
const lifecycle = new AbortController();
|
|
2649
2866
|
const signal = options.signal === void 0 ? lifecycle.signal : AbortSignal.any([lifecycle.signal, options.signal]);
|
|
2650
2867
|
let agentProcess;
|
|
@@ -2662,7 +2879,7 @@ async function startBuddyDev(options) {
|
|
|
2662
2879
|
let config;
|
|
2663
2880
|
let configSource;
|
|
2664
2881
|
try {
|
|
2665
|
-
configSource = await
|
|
2882
|
+
configSource = await readFile9(join9(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
|
|
2666
2883
|
config = combineBuddyProject(parseBuddyAgentConfig(configSource));
|
|
2667
2884
|
} catch (cause) {
|
|
2668
2885
|
throw new BuddyDevError({
|
|
@@ -2766,7 +2983,7 @@ async function startBuddyDev(options) {
|
|
|
2766
2983
|
}
|
|
2767
2984
|
|
|
2768
2985
|
// packages/cli-core/src/dev/starter.ts
|
|
2769
|
-
import { resolve as
|
|
2986
|
+
import { resolve as resolve11 } from "node:path";
|
|
2770
2987
|
function createBuddyDevStarter(options = {}) {
|
|
2771
2988
|
const readManifest = options.readManifest ?? (async (projectRoot) => {
|
|
2772
2989
|
const manifest = await readBuddyProjectManifest(projectRoot);
|
|
@@ -2775,7 +2992,7 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2775
2992
|
const startDev = options.startDev ?? startBuddyDev;
|
|
2776
2993
|
return Object.freeze({
|
|
2777
2994
|
async start(input) {
|
|
2778
|
-
const projectRoot =
|
|
2995
|
+
const projectRoot = resolve11(input.projectRoot);
|
|
2779
2996
|
const manifest = await readManifest(projectRoot);
|
|
2780
2997
|
return startDev({
|
|
2781
2998
|
...input,
|
|
@@ -2866,7 +3083,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2866
3083
|
return { exitCode: 0 };
|
|
2867
3084
|
}
|
|
2868
3085
|
try {
|
|
2869
|
-
const requestedRoot =
|
|
3086
|
+
const requestedRoot = resolve12(options.cwd ?? process.cwd(), parsed.directory);
|
|
2870
3087
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
2871
3088
|
const environment = options.env ?? process.env;
|
|
2872
3089
|
const url = appServerUrl(parsed.appServer, environment);
|
|
@@ -2919,7 +3136,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
2919
3136
|
}
|
|
2920
3137
|
|
|
2921
3138
|
// packages/cli-core/src/commands/cloud.ts
|
|
2922
|
-
import { resolve as
|
|
3139
|
+
import { resolve as resolve13 } from "node:path";
|
|
2923
3140
|
|
|
2924
3141
|
// packages/cli-core/src/cloud/avatar.ts
|
|
2925
3142
|
import { open as open3 } from "node:fs/promises";
|
|
@@ -3448,10 +3665,10 @@ function browserCommand(url) {
|
|
|
3448
3665
|
}
|
|
3449
3666
|
async function openExternalUrl(url) {
|
|
3450
3667
|
const launch = browserCommand(url);
|
|
3451
|
-
await new Promise((
|
|
3668
|
+
await new Promise((resolve17, reject) => {
|
|
3452
3669
|
const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
3453
3670
|
child.once("error", reject);
|
|
3454
|
-
child.once("exit", (code2) => code2 === 0 ?
|
|
3671
|
+
child.once("exit", (code2) => code2 === 0 ? resolve17() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
3455
3672
|
});
|
|
3456
3673
|
}
|
|
3457
3674
|
async function write(res, status, title, message, webUrl, tone = "error") {
|
|
@@ -3463,10 +3680,10 @@ async function write(res, status, title, message, webUrl, tone = "error") {
|
|
|
3463
3680
|
"referrer-policy": "no-referrer",
|
|
3464
3681
|
"x-content-type-options": "nosniff"
|
|
3465
3682
|
});
|
|
3466
|
-
await new Promise((
|
|
3683
|
+
await new Promise((resolve17, reject) => {
|
|
3467
3684
|
res.once("error", reject);
|
|
3468
|
-
res.once("close",
|
|
3469
|
-
res.end(renderLoginResult(title, message, webUrl, tone),
|
|
3685
|
+
res.once("close", resolve17);
|
|
3686
|
+
res.end(renderLoginResult(title, message, webUrl, tone), resolve17);
|
|
3470
3687
|
});
|
|
3471
3688
|
}
|
|
3472
3689
|
async function readJson(response) {
|
|
@@ -3501,11 +3718,11 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3501
3718
|
let settle;
|
|
3502
3719
|
let reject;
|
|
3503
3720
|
let settled = false;
|
|
3504
|
-
const result = new Promise((
|
|
3721
|
+
const result = new Promise((resolve17, rejectResult) => {
|
|
3505
3722
|
settle = (value) => {
|
|
3506
3723
|
if (!settled) {
|
|
3507
3724
|
settled = true;
|
|
3508
|
-
|
|
3725
|
+
resolve17(value);
|
|
3509
3726
|
}
|
|
3510
3727
|
};
|
|
3511
3728
|
reject = (reason) => {
|
|
@@ -3524,9 +3741,9 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3524
3741
|
state: request.state
|
|
3525
3742
|
}).then(settle, reject);
|
|
3526
3743
|
});
|
|
3527
|
-
await new Promise((
|
|
3744
|
+
await new Promise((resolve17, rejectListen) => {
|
|
3528
3745
|
server.once("error", rejectListen);
|
|
3529
|
-
server.listen(0, "127.0.0.1", () =>
|
|
3746
|
+
server.listen(0, "127.0.0.1", () => resolve17());
|
|
3530
3747
|
});
|
|
3531
3748
|
const address = server.address();
|
|
3532
3749
|
if (!address || typeof address === "string") {
|
|
@@ -3559,8 +3776,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3559
3776
|
} finally {
|
|
3560
3777
|
clearTimeout(timer);
|
|
3561
3778
|
request.signal?.removeEventListener("abort", abort);
|
|
3562
|
-
await new Promise((
|
|
3563
|
-
server.close(() =>
|
|
3779
|
+
await new Promise((resolve17) => {
|
|
3780
|
+
server.close(() => resolve17());
|
|
3564
3781
|
server.closeIdleConnections();
|
|
3565
3782
|
server.closeAllConnections();
|
|
3566
3783
|
});
|
|
@@ -3675,7 +3892,7 @@ function parse(source) {
|
|
|
3675
3892
|
});
|
|
3676
3893
|
}
|
|
3677
3894
|
async function runCredentialCommand(input) {
|
|
3678
|
-
return await new Promise((
|
|
3895
|
+
return await new Promise((resolve17, reject) => {
|
|
3679
3896
|
const child = spawn2(input.command, [...input.args], {
|
|
3680
3897
|
shell: false,
|
|
3681
3898
|
windowsHide: true,
|
|
@@ -3691,7 +3908,7 @@ async function runCredentialCommand(input) {
|
|
|
3691
3908
|
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3692
3909
|
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3693
3910
|
child.once("error", reject);
|
|
3694
|
-
child.once("close", (code2) =>
|
|
3911
|
+
child.once("close", (code2) => resolve17({
|
|
3695
3912
|
code: code2 ?? 1,
|
|
3696
3913
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3697
3914
|
stderr: Buffer.concat(stderr).toString("utf8")
|
|
@@ -3923,7 +4140,7 @@ function dependencies(options) {
|
|
|
3923
4140
|
return { session: options.session, client: options.client };
|
|
3924
4141
|
}
|
|
3925
4142
|
function requestedDirectory(options, parsed) {
|
|
3926
|
-
return
|
|
4143
|
+
return resolve13(options.cwd ?? process.cwd(), parsed.values.get("--directory") ?? ".");
|
|
3927
4144
|
}
|
|
3928
4145
|
function auditLabel(status) {
|
|
3929
4146
|
if (status === "auditing") return "\u5BA1\u6838\u4E2D";
|
|
@@ -4007,7 +4224,7 @@ async function runAvatar(parsed, io, options) {
|
|
|
4007
4224
|
const file2 = parsed.values.get("--file");
|
|
4008
4225
|
let upload;
|
|
4009
4226
|
try {
|
|
4010
|
-
upload = file2 ? await readBuddyAvatarFile(
|
|
4227
|
+
upload = file2 ? await readBuddyAvatarFile(resolve13(options.cwd ?? process.cwd(), file2)) : void 0;
|
|
4011
4228
|
} catch (error2) {
|
|
4012
4229
|
throw new CloudCommandUsageError(error2 instanceof Error ? error2.message : "\u65E0\u6CD5\u8BFB\u53D6\u5934\u50CF\u56FE\u7247");
|
|
4013
4230
|
}
|
|
@@ -4076,8 +4293,8 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
4076
4293
|
var CLOUD_COMMAND_HELP = HELP;
|
|
4077
4294
|
|
|
4078
4295
|
// packages/cli-core/src/migration/index.ts
|
|
4079
|
-
import { lstat as
|
|
4080
|
-
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";
|
|
4081
4298
|
var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
|
|
4082
4299
|
maxFiles: 128,
|
|
4083
4300
|
maxFileBytes: 256 * 1024,
|
|
@@ -4230,7 +4447,7 @@ async function existingProjectRoot(value) {
|
|
|
4230
4447
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
4231
4448
|
}
|
|
4232
4449
|
try {
|
|
4233
|
-
const root = await realpath4(
|
|
4450
|
+
const root = await realpath4(resolve14(value));
|
|
4234
4451
|
if (!(await stat(root)).isDirectory()) {
|
|
4235
4452
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
4236
4453
|
}
|
|
@@ -4246,9 +4463,9 @@ async function existingProjectRoot(value) {
|
|
|
4246
4463
|
}
|
|
4247
4464
|
}
|
|
4248
4465
|
async function existingSkillRoot(projectRoot, skillPath) {
|
|
4249
|
-
const candidate = skillPath === "." ? projectRoot :
|
|
4466
|
+
const candidate = skillPath === "." ? projectRoot : join10(projectRoot, ...skillPath.split("/"));
|
|
4250
4467
|
try {
|
|
4251
|
-
await
|
|
4468
|
+
await lstat8(candidate);
|
|
4252
4469
|
} catch (cause) {
|
|
4253
4470
|
if (filesystemCode(cause) === "ENOENT") {
|
|
4254
4471
|
return rejectScan("SKILL_ROOT_NOT_FOUND", skillPath, "\u627E\u4E0D\u5230\u6307\u5B9A\u7684 Skill \u76EE\u5F55\u3002");
|
|
@@ -4283,9 +4500,9 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
4283
4500
|
}
|
|
4284
4501
|
async function assertEntrypoint(context) {
|
|
4285
4502
|
const entryPath = reportChild(context.skillPath, "SKILL.md");
|
|
4286
|
-
const candidate =
|
|
4503
|
+
const candidate = join10(context.skillRoot, "SKILL.md");
|
|
4287
4504
|
try {
|
|
4288
|
-
await
|
|
4505
|
+
await lstat8(candidate);
|
|
4289
4506
|
const target = await realpath4(candidate);
|
|
4290
4507
|
if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
|
|
4291
4508
|
rejectScan(
|
|
@@ -4387,7 +4604,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
4387
4604
|
}
|
|
4388
4605
|
for (const entry of entries) {
|
|
4389
4606
|
const childReportPath = reportChild(reportDirectory, entry.name);
|
|
4390
|
-
const childPath =
|
|
4607
|
+
const childPath = join10(directory, entry.name);
|
|
4391
4608
|
let target;
|
|
4392
4609
|
try {
|
|
4393
4610
|
target = await realpath4(childPath);
|
|
@@ -4842,8 +5059,8 @@ async function scanExternalSkillPortability(options) {
|
|
|
4842
5059
|
|
|
4843
5060
|
// packages/cli-core/src/push/preflight.ts
|
|
4844
5061
|
import { createHash as createHash6 } from "node:crypto";
|
|
4845
|
-
import { lstat as
|
|
4846
|
-
import { isAbsolute as isAbsolute3, join as
|
|
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";
|
|
4847
5064
|
|
|
4848
5065
|
// packages/cli-core/src/push/errors.ts
|
|
4849
5066
|
var PushPreflightError = class extends Error {
|
|
@@ -5031,10 +5248,10 @@ function normalizeBookletPath(value) {
|
|
|
5031
5248
|
return normalized;
|
|
5032
5249
|
}
|
|
5033
5250
|
async function canonicalProjectRoot(projectRoot) {
|
|
5034
|
-
const absolute =
|
|
5251
|
+
const absolute = resolve15(projectRoot);
|
|
5035
5252
|
let entry;
|
|
5036
5253
|
try {
|
|
5037
|
-
entry = await
|
|
5254
|
+
entry = await lstat9(absolute);
|
|
5038
5255
|
} catch (cause) {
|
|
5039
5256
|
error({
|
|
5040
5257
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -5062,10 +5279,10 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
5062
5279
|
}
|
|
5063
5280
|
}
|
|
5064
5281
|
async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
5065
|
-
const path =
|
|
5282
|
+
const path = join11(projectRoot, filename);
|
|
5066
5283
|
let entry;
|
|
5067
5284
|
try {
|
|
5068
|
-
entry = await
|
|
5285
|
+
entry = await lstat9(path);
|
|
5069
5286
|
} catch (cause) {
|
|
5070
5287
|
error({
|
|
5071
5288
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -5265,10 +5482,10 @@ async function createManifest(options) {
|
|
|
5265
5482
|
sensitiveEntriesExcluded += 1;
|
|
5266
5483
|
continue;
|
|
5267
5484
|
}
|
|
5268
|
-
const absolutePath =
|
|
5485
|
+
const absolutePath = join11(directory, name);
|
|
5269
5486
|
let entry;
|
|
5270
5487
|
try {
|
|
5271
|
-
entry = await
|
|
5488
|
+
entry = await lstat9(absolutePath);
|
|
5272
5489
|
} catch (cause) {
|
|
5273
5490
|
error({
|
|
5274
5491
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5517,8 +5734,8 @@ async function assertCapturedConfigStable(options) {
|
|
|
5517
5734
|
}
|
|
5518
5735
|
let current;
|
|
5519
5736
|
try {
|
|
5520
|
-
const path =
|
|
5521
|
-
const entry = await
|
|
5737
|
+
const path = join11(options.projectRoot, options.filename);
|
|
5738
|
+
const entry = await lstat9(path);
|
|
5522
5739
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
5523
5740
|
error({
|
|
5524
5741
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5601,14 +5818,15 @@ async function createPushPreflight(options) {
|
|
|
5601
5818
|
maxFileBytes: limits.maxFileBytes
|
|
5602
5819
|
});
|
|
5603
5820
|
const config = parseCapturedProject(agentEntry);
|
|
5604
|
-
if (config.datasets?.length) {
|
|
5821
|
+
if (config.datasets?.length || config.model !== void 0) {
|
|
5605
5822
|
try {
|
|
5606
|
-
await validateRuntimeDatasets(projectRoot, config.datasets);
|
|
5823
|
+
if (config.datasets?.length) await validateRuntimeDatasets(projectRoot, config.datasets);
|
|
5824
|
+
await validateRuntimeModel(projectRoot, config.model);
|
|
5607
5825
|
} catch (cause) {
|
|
5608
5826
|
error({
|
|
5609
5827
|
code: "PUSH_CONFIG_INVALID",
|
|
5610
5828
|
path: BUDDY_AGENT_FILENAME,
|
|
5611
|
-
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)}`,
|
|
5612
5830
|
cause
|
|
5613
5831
|
});
|
|
5614
5832
|
}
|
|
@@ -5653,8 +5871,8 @@ async function createPushPreflight(options) {
|
|
|
5653
5871
|
|
|
5654
5872
|
// packages/cli-core/src/push/bundle.ts
|
|
5655
5873
|
import { createHash as createHash7 } from "node:crypto";
|
|
5656
|
-
import { lstat as
|
|
5657
|
-
import { join as
|
|
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";
|
|
5658
5876
|
import { Writable } from "node:stream";
|
|
5659
5877
|
import { pipeline } from "node:stream/promises";
|
|
5660
5878
|
import { createGzip } from "node:zlib";
|
|
@@ -5676,15 +5894,15 @@ function inside(root, target) {
|
|
|
5676
5894
|
return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
|
|
5677
5895
|
}
|
|
5678
5896
|
async function capture(root, file2) {
|
|
5679
|
-
const absolute =
|
|
5897
|
+
const absolute = join12(root, ...file2.path.split("/"));
|
|
5680
5898
|
try {
|
|
5681
|
-
const entry = await
|
|
5899
|
+
const entry = await lstat10(absolute);
|
|
5682
5900
|
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !== file2.bytes) {
|
|
5683
5901
|
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5684
5902
|
}
|
|
5685
5903
|
const canonical = await realpath6(absolute);
|
|
5686
5904
|
if (!inside(root, canonical)) changed(file2.path, `\u63D0\u4EA4\u6587\u4EF6\u8D8A\u8FC7\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${file2.path}`);
|
|
5687
|
-
const contents = await
|
|
5905
|
+
const contents = await readFile10(absolute);
|
|
5688
5906
|
if (contents.byteLength !== file2.bytes || digest(contents) !== file2.digest) {
|
|
5689
5907
|
changed(file2.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file2.path}`);
|
|
5690
5908
|
}
|
|
@@ -5773,7 +5991,7 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5773
5991
|
buddy-cli dev [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5774
5992
|
buddy-cli login | logout | push | status
|
|
5775
5993
|
buddy-cli avatar (--file <\u56FE\u7247\u6587\u4EF6> | --clear) [--directory <\u9879\u76EE\u76EE\u5F55> | --buddy-id <\u642D\u5B50 ID>]
|
|
5776
|
-
buddy-cli models [--json]
|
|
5994
|
+
buddy-cli models [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5777
5995
|
buddy-cli widgets sync | check | verify
|
|
5778
5996
|
buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5779
5997
|
buddy-cli preview [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
@@ -5786,8 +6004,8 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5786
6004
|
dev \u542F\u52A8\u5B98\u65B9 Runtime \u5E76\u8FDE\u63A5 DEV App Server
|
|
5787
6005
|
login \u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u642D\u642D\u8D26\u53F7
|
|
5788
6006
|
logout \u9000\u51FA\u642D\u642D\u5F00\u53D1\u8005\u8D26\u53F7\u5E76\u6E05\u9664\u672C\u673A\u767B\u5F55\u72B6\u6001
|
|
5789
|
-
models \u67E5\u770B
|
|
5790
|
-
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
|
|
5791
6009
|
push \u4E0A\u4F20 Agent \u5DE5\u7A0B\u548C commit\uFF0C\u63D0\u4EA4\u5E73\u53F0\u5BA1\u6838
|
|
5792
6010
|
status \u67E5\u8BE2\u642D\u5B50\u7684\u8D44\u6599\u3001\u4EE3\u7801\u3001\u8FD0\u884C\u4E0E\u53D1\u5E03\u72B6\u6001
|
|
5793
6011
|
avatar \u8BBE\u7F6E\u6216\u6E05\u9664\u642D\u5B50\u5934\u50CF\uFF0C\u4FDD\u7559\u5176\u4ED6\u4E91\u7AEF\u8D44\u6599
|
|
@@ -5996,7 +6214,7 @@ function defaultProjectDirectory(name) {
|
|
|
5996
6214
|
return `./${folder}`;
|
|
5997
6215
|
}
|
|
5998
6216
|
async function initializeProjectRoute(route, args, io, options, application) {
|
|
5999
|
-
const cwd =
|
|
6217
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
6000
6218
|
const { name, tagline, intro } = application;
|
|
6001
6219
|
const directoryInput = await textAnswer(
|
|
6002
6220
|
io,
|
|
@@ -6005,7 +6223,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
6005
6223
|
"\u9879\u76EE\u76EE\u5F55\uFF08\u7A7A\u76EE\u5F55\u6216\u5DF2\u6709\u642D\u642D\u5DE5\u7A0B\uFF09",
|
|
6006
6224
|
defaultProjectDirectory(name)
|
|
6007
6225
|
);
|
|
6008
|
-
const rootDirectory =
|
|
6226
|
+
const rootDirectory = resolve16(cwd, directoryInput);
|
|
6009
6227
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
6010
6228
|
const initializationTarget = { directory: rootDirectory, kind: "new" };
|
|
6011
6229
|
const initializationPlan = options.initializeProject ? void 0 : await planBuddyProjectInitialization(initializationTarget);
|
|
@@ -6049,7 +6267,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
6049
6267
|
}
|
|
6050
6268
|
async function runRoute(route, args, io, options) {
|
|
6051
6269
|
if (!io.canPrompt) assertNonInteractiveProjectArguments(route, args);
|
|
6052
|
-
const cwd =
|
|
6270
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
6053
6271
|
const name = await textAnswer(io, args.name, "--name", "\u642D\u5B50\u540D\u79F0", void 0, BUDDY_PROFILE_TEXT_LIMITS.name);
|
|
6054
6272
|
const tagline = await textAnswer(
|
|
6055
6273
|
io,
|
|
@@ -6077,7 +6295,7 @@ async function runRoute(route, args, io, options) {
|
|
|
6077
6295
|
"\u9879\u76EE\u76EE\u5F55",
|
|
6078
6296
|
defaultProjectDirectory(name)
|
|
6079
6297
|
);
|
|
6080
|
-
const rootDirectory =
|
|
6298
|
+
const rootDirectory = resolve16(cwd, directoryInput);
|
|
6081
6299
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
6082
6300
|
const plan = await planBuddyDraftInitialization(rootDirectory);
|
|
6083
6301
|
const existingBuddyId = plan.existingManifest?.buddyId;
|
|
@@ -6111,11 +6329,11 @@ async function runRoute(route, args, io, options) {
|
|
|
6111
6329
|
return { ...result, route: "create", draft, buddyId };
|
|
6112
6330
|
}
|
|
6113
6331
|
async function resumeProjectArguments(args, cwd, options) {
|
|
6114
|
-
const root =
|
|
6332
|
+
const root = resolve16(cwd, args.directory?.trim() || ".");
|
|
6115
6333
|
await assertInitializableBuddyTarget(root);
|
|
6116
6334
|
let entry;
|
|
6117
6335
|
try {
|
|
6118
|
-
entry = await
|
|
6336
|
+
entry = await lstat11(join13(root, "buddy.agent.json"));
|
|
6119
6337
|
} catch (cause) {
|
|
6120
6338
|
if (cause.code === "ENOENT") return;
|
|
6121
6339
|
throw cause;
|
|
@@ -6144,7 +6362,7 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6144
6362
|
try {
|
|
6145
6363
|
const [command, ...rest] = args;
|
|
6146
6364
|
if (!command) {
|
|
6147
|
-
const cwd =
|
|
6365
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
6148
6366
|
let projectRoot;
|
|
6149
6367
|
try {
|
|
6150
6368
|
projectRoot = await findBuddyProjectRoot(cwd);
|
|
@@ -6172,13 +6390,13 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6172
6390
|
io.write(HELP2);
|
|
6173
6391
|
return { exitCode: 0 };
|
|
6174
6392
|
}
|
|
6175
|
-
if (command === "models") return runModelsCommand(rest, io);
|
|
6393
|
+
if (command === "models") return await runModelsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
|
|
6176
6394
|
if (command === "widgets") return await runWidgetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6177
6395
|
if (command === "preview") {
|
|
6178
6396
|
return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
|
|
6179
6397
|
}
|
|
6180
6398
|
if (command === "datasets") {
|
|
6181
|
-
return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6399
|
+
return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
|
|
6182
6400
|
}
|
|
6183
6401
|
if (command === "dev") {
|
|
6184
6402
|
return await runDevCommand(rest, io, {
|
|
@@ -6200,11 +6418,11 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6200
6418
|
}
|
|
6201
6419
|
let route = routeFromMode(parsed.mode);
|
|
6202
6420
|
if (route === void 0 && !io.canPrompt) requireModeForNonInteractive();
|
|
6203
|
-
const cwd =
|
|
6421
|
+
const cwd = resolve16(options.cwd ?? process.cwd());
|
|
6204
6422
|
await assertInitializableBuddyTarget(cwd);
|
|
6205
6423
|
if (parsed.directory !== void 0) {
|
|
6206
6424
|
if (!parsed.directory.trim()) throw new CliUsageError("--directory \u4E0D\u80FD\u4E3A\u7A7A");
|
|
6207
|
-
await assertInitializableBuddyTarget(
|
|
6425
|
+
await assertInitializableBuddyTarget(resolve16(cwd, parsed.directory));
|
|
6208
6426
|
}
|
|
6209
6427
|
await authenticate(options);
|
|
6210
6428
|
await resumeProjectArguments(parsed, cwd, options);
|
|
@@ -6357,6 +6575,7 @@ export {
|
|
|
6357
6575
|
isAbortLike,
|
|
6358
6576
|
isSafeProjectRelativePath,
|
|
6359
6577
|
isWindowsReservedPathSegment,
|
|
6578
|
+
latestRuntimeTemplate,
|
|
6360
6579
|
normalizePlatformUrl,
|
|
6361
6580
|
openExternalUrl,
|
|
6362
6581
|
parseBuddyAgentConfig,
|