@my-life-buddies/cli 0.1.2 → 0.2.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/dist/bin/core.js CHANGED
@@ -1,27 +1,28 @@
1
1
  // packages/cli-core/src/index.ts
2
- import { lstat as lstat8 } from "node:fs/promises";
3
- import { join as join8, resolve as resolve11 } from "node:path";
2
+ import { lstat as lstat9 } from "node:fs/promises";
3
+ import { join as join10, resolve as resolve11 } from "node:path";
4
4
 
5
5
  // packages/cli-core/src/config/index.ts
6
6
  import {
7
7
  link,
8
- lstat,
9
- mkdir,
10
- open,
11
- readFile,
8
+ lstat as lstat3,
9
+ mkdir as mkdir2,
10
+ open as open2,
11
+ readFile as readFile2,
12
12
  readdir,
13
13
  rename,
14
- unlink
14
+ unlink as unlink2
15
15
  } from "node:fs/promises";
16
16
  import { randomUUID } from "node:crypto";
17
- import { isAbsolute, join, resolve, win32 } from "node:path";
17
+ import { isAbsolute, join as join2, resolve as resolve2, win32 } from "node:path";
18
+ import { isDeepStrictEqual } from "node:util";
18
19
  import { Ajv2020 } from "ajv/dist/2020.js";
19
20
 
20
21
  // packages/cli-core/src/config/buddy.agent.schema.json
21
22
  var buddy_agent_schema_default = {
22
23
  $schema: "https://json-schema.org/draft/2020-12/schema",
23
24
  title: "buddy.agent.json",
24
- description: "\u642D\u642D\u9879\u76EE\u8EAB\u4EFD\u4E0E Agent \u53EF\u8FD0\u884C\u5DE5\u7A0B\u5B9A\u4E49\uFF1B\u5F00\u53D1\u51C6\u5907\u9636\u6BB5\u53EF\u53EA\u5305\u542B buddyId",
25
+ description: "\u642D\u642D\u9879\u76EE\u8EAB\u4EFD\u3001\u6A21\u578B\u9009\u62E9\u4E0E\u6570\u636E\u8BBF\u95EE\u9700\u6C42\uFF1B\u5DE5\u7A0B\u662F\u5426\u53EF\u8FD0\u884C\u7531\u5B9E\u9645\u6587\u4EF6\u5224\u65AD",
25
26
  type: "object",
26
27
  additionalProperties: false,
27
28
  required: [
@@ -46,26 +47,211 @@ var buddy_agent_schema_default = {
46
47
  pattern: "^[^\\s\\u0000-\\u001f\\u007f]+$",
47
48
  description: "\u4ECE buddy-cli models \u67E5\u8BE2\u7684\u6A21\u578B ID"
48
49
  },
49
- runtime: {
50
- type: "object",
51
- additionalProperties: false,
52
- required: [
53
- "language"
54
- ],
55
- properties: {
56
- language: {
57
- const: "node"
50
+ datasets: {
51
+ type: "array",
52
+ description: "\u672C\u642D\u5B50\u7533\u8BF7\u7684\u6570\u636E\u96C6\u53CA\u7528\u9014\uFF1B\u7701\u7565\u6216\u7A7A\u6570\u7EC4\u8868\u793A\u4E0D\u7533\u8BF7\u6570\u636E\uFF0C\u58F0\u660E\u4E0D\u4EE3\u8868\u7528\u6237\u5DF2\u6388\u6743",
53
+ default: [],
54
+ maxItems: 8,
55
+ items: {
56
+ type: "object",
57
+ additionalProperties: false,
58
+ required: ["id", "purpose"],
59
+ properties: {
60
+ id: {
61
+ type: "string",
62
+ enum: [
63
+ "health.sleep",
64
+ "health.workouts",
65
+ "health.distance",
66
+ "health.heartRate",
67
+ "health.hrv",
68
+ "health.respiratoryRate",
69
+ "health.oxygenSaturation",
70
+ "health.environmentalAudioExposure"
71
+ ]
72
+ },
73
+ purpose: {
74
+ type: "string",
75
+ minLength: 1,
76
+ maxLength: 200,
77
+ pattern: "\\S",
78
+ description: "\u5411\u7528\u6237\u8BF4\u660E\u8FD9\u53EA\u642D\u5B50\u4E3A\u4F55\u9700\u8981\u8BE5\u6570\u636E"
79
+ }
58
80
  }
59
81
  }
60
82
  }
61
- },
62
- dependentRequired: {
63
- model: [
64
- "runtime"
65
- ]
66
83
  }
67
84
  };
68
85
 
86
+ // packages/cli-core/src/project-files.ts
87
+ import { lstat as lstat2 } from "node:fs/promises";
88
+ import { join } from "node:path";
89
+
90
+ // packages/cli-core/src/init/scaffold.ts
91
+ import { lstat, mkdir, open, readFile, rmdir, unlink } from "node:fs/promises";
92
+ import { resolve } from "node:path";
93
+ var BUDDY_PLATFORM_AGENT_ENTRY = "agent.md";
94
+ var BUDDY_PLATFORM_FACTORY_ENTRY = "src/buddy.mjs";
95
+ var BUDDY_PLATFORM_PACKAGE_JSON = "package.json";
96
+ var BUDDY_PLATFORM_PACKAGE_LOCK = "package-lock.json";
97
+ var BUDDY_PLATFORM_START_ENTRY = "start.mjs";
98
+ var PLATFORM_DIRECTORIES = ["src", "src/tools", "src/hooks", "src/services", "resources", "widgets"];
99
+ function promptFor(name) {
100
+ const safeName = name.trim() || "\u642D\u642D";
101
+ return `\u4F60\u662F${safeName}\u3002\u8BF7\u7406\u89E3\u7528\u6237\u7684\u771F\u5B9E\u9700\u6C42\uFF0C\u7ED9\u51FA\u81EA\u7136\u3001\u51C6\u786E\u4E14\u6709\u5E2E\u52A9\u7684\u5B8C\u6574\u56DE\u590D\u3002`;
102
+ }
103
+ function platformAgentTemplate(name) {
104
+ return `${promptFor(name)}
105
+ `;
106
+ }
107
+ function platformFactoryTemplate() {
108
+ return `import { readFileSync } from "node:fs";
109
+
110
+ const systemPrompt = readFileSync(new URL("../agent.md", import.meta.url), "utf8").trim();
111
+ if (!systemPrompt) throw new Error("agent.md must contain the Buddy system prompt");
112
+
113
+ /**
114
+ * Called when the Runtime creates an agent for a conversation.
115
+ * Import tools from ./tools/ and hooks from ./hooks/, then register them below.
116
+ * Shared business code can live in ./services/; directories do not enable capabilities.
117
+ * Use conversation.memory / dataAccess / widgets / proactive for platform capabilities.
118
+ * Place widget assets in ../widgets/<type-id>/index.html and schema.json.
119
+ * Runtime uploads these files before connecting to MLB; upload failures stop startup.
120
+ * Select widget_* in platformTools to expose widget tools to the model.
121
+ * Use platform APIs only; do not call third-party services directly.
122
+ */
123
+ export default function createBuddy(_conversation) {
124
+ return {
125
+ initialState: {
126
+ systemPrompt,
127
+ thinkingLevel: "off",
128
+ tools: [],
129
+ },
130
+ };
131
+ }
132
+ `;
133
+ }
134
+ function platformStartTemplate() {
135
+ return `// Generated startup: local DEV and hosted execution use the same Buddy Runtime.
136
+ import { readFileSync } from "node:fs";
137
+ import { BuddyServer } from "@my-life-buddies/buddy-runtime";
138
+ import createBuddy from "./src/buddy.mjs";
139
+
140
+ const config = JSON.parse(readFileSync(new URL("./buddy.agent.json", import.meta.url), "utf8"));
141
+ if (!config || config.schemaVersion !== 1 || typeof config.buddyId !== "string" || !config.buddyId) {
142
+ throw new Error("buddy.agent.json is not a complete Buddy project");
143
+ }
144
+ if (process.env.MLB_BUDDY_ID && process.env.MLB_BUDDY_ID !== config.buddyId) {
145
+ throw new Error("MLB_BUDDY_ID does not match buddy.agent.json");
146
+ }
147
+ const model = typeof config.model === "string" && config.model ? config.model : "kimi-k2.6";
148
+
149
+ const lifecycle = new AbortController();
150
+ const stop = () => lifecycle.abort();
151
+ process.on("SIGINT", stop);
152
+ process.on("SIGTERM", stop);
153
+ let runtime;
154
+ try {
155
+ runtime = await BuddyServer.start({ model, buddy: createBuddy });
156
+ if (!lifecycle.signal.aborted) {
157
+ await new Promise((resolve) => lifecycle.signal.addEventListener("abort", resolve, { once: true }));
158
+ }
159
+ } catch (error) {
160
+ if (!lifecycle.signal.aborted) {
161
+ console.error("buddy-agent:", error instanceof Error ? error.message : "Startup failed");
162
+ process.exitCode = 1;
163
+ }
164
+ } finally {
165
+ await runtime?.stop();
166
+ process.off("SIGINT", stop);
167
+ process.off("SIGTERM", stop);
168
+ }
169
+ `;
170
+ }
171
+ async function scaffoldPlatformAgent(projectRoot, name) {
172
+ const path = resolve(projectRoot, BUDDY_PLATFORM_AGENT_ENTRY);
173
+ const hasAlternateLockfile = (await Promise.all(["pnpm-lock.yaml", "yarn.lock"].map(async (name2) => {
174
+ try {
175
+ if (!(await lstat(resolve(projectRoot, name2))).isFile()) {
176
+ throw new TypeError(`\u4F9D\u8D56\u9501\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF1A${name2}`);
177
+ }
178
+ return true;
179
+ } catch (cause) {
180
+ if (cause.code === "ENOENT") return false;
181
+ throw cause;
182
+ }
183
+ }))).some(Boolean);
184
+ const files = [
185
+ { path, content: platformAgentTemplate(name) },
186
+ { path: resolve(projectRoot, BUDDY_PLATFORM_FACTORY_ENTRY), content: platformFactoryTemplate() },
187
+ { path: resolve(projectRoot, BUDDY_PLATFORM_PACKAGE_JSON), content: await readFile(new URL("../../resources/agent-template/package.json", import.meta.url), "utf8") },
188
+ ...hasAlternateLockfile ? [] : [{ path: resolve(projectRoot, BUDDY_PLATFORM_PACKAGE_LOCK), content: await readFile(new URL("../../resources/agent-template/package-lock.json", import.meta.url), "utf8") }],
189
+ { path: resolve(projectRoot, BUDDY_PLATFORM_START_ENTRY), content: platformStartTemplate() }
190
+ ];
191
+ const handles = [];
192
+ const createdDirectories = [];
193
+ try {
194
+ for (const directory of PLATFORM_DIRECTORIES) {
195
+ const directoryPath = resolve(projectRoot, directory);
196
+ try {
197
+ await mkdir(directoryPath, { mode: 448 });
198
+ createdDirectories.push(directoryPath);
199
+ } catch (cause) {
200
+ if (cause.code !== "EEXIST") throw cause;
201
+ }
202
+ if (!(await lstat(directoryPath)).isDirectory()) {
203
+ throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u76EE\u5F55\u5FC5\u987B\u662F\u666E\u901A\u76EE\u5F55\uFF0C\u4E0D\u80FD\u4F7F\u7528\u6587\u4EF6\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${directoryPath}`);
204
+ }
205
+ }
206
+ for (const file of files) {
207
+ try {
208
+ handles.push({ path: file.path, handle: await open(file.path, "wx", 384) });
209
+ } catch (cause) {
210
+ if (cause.code !== "EEXIST") throw cause;
211
+ if (!(await lstat(file.path)).isFile()) {
212
+ throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u4F7F\u7528\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${file.path}`);
213
+ }
214
+ }
215
+ }
216
+ for (const { path: filePath, handle } of handles) {
217
+ const file = files.find((candidate) => candidate.path === filePath);
218
+ await handle.writeFile(file.content, "utf8");
219
+ await handle.sync();
220
+ }
221
+ return Object.freeze({ path, created: handles.length > 0 || createdDirectories.length > 0 });
222
+ } catch (cause) {
223
+ await Promise.all(handles.map(({ handle }) => handle.close().catch(() => void 0)));
224
+ await Promise.all(handles.map(({ path: createdPath }) => unlink(createdPath).catch(() => void 0)));
225
+ for (const directory of createdDirectories.reverse()) {
226
+ await rmdir(directory).catch(() => void 0);
227
+ }
228
+ throw cause;
229
+ } finally {
230
+ await Promise.all(handles.map(({ handle }) => handle.close().catch(() => void 0)));
231
+ }
232
+ }
233
+
234
+ // packages/cli-core/src/project-files.ts
235
+ async function missingBuddyAgentFiles(projectRoot) {
236
+ const entries = [
237
+ ["src", "directory"],
238
+ [BUDDY_PLATFORM_AGENT_ENTRY, "file"],
239
+ [BUDDY_PLATFORM_FACTORY_ENTRY, "file"],
240
+ [BUDDY_PLATFORM_PACKAGE_JSON, "file"],
241
+ [BUDDY_PLATFORM_START_ENTRY, "file"]
242
+ ];
243
+ const missing = await Promise.all(entries.map(async ([path, kind]) => {
244
+ try {
245
+ const entry = await lstat2(join(projectRoot, path));
246
+ return (kind === "directory" ? entry.isDirectory() : entry.isFile()) ? void 0 : path;
247
+ } catch (cause) {
248
+ if (["ENOENT", "ENOTDIR"].includes(cause.code ?? "")) return path;
249
+ throw cause;
250
+ }
251
+ }));
252
+ return missing.filter((path) => path !== void 0);
253
+ }
254
+
69
255
  // packages/cli-core/src/config/index.ts
70
256
  var BUDDY_AGENT_FILENAME = "buddy.agent.json";
71
257
  var BUDDY_AGENT_SCHEMA = buddy_agent_schema_default;
@@ -124,15 +310,18 @@ function validateBuddyProjectManifest(value) {
124
310
  if (!validateAgentSchema(value)) {
125
311
  return { ok: false, issues: schemaIssues(validateAgentSchema.errors) };
126
312
  }
127
- return { ok: true, value };
313
+ const manifest = value;
314
+ const seen = /* @__PURE__ */ new Set();
315
+ for (const [index, dataset] of (manifest.datasets ?? []).entries()) {
316
+ if (seen.has(dataset.id)) {
317
+ return { ok: false, issues: [{ path: `/datasets/${index}/id`, message: `\u6570\u636E\u96C6\u4E0D\u80FD\u91CD\u590D\u58F0\u660E\uFF1A${dataset.id}` }] };
318
+ }
319
+ seen.add(dataset.id);
320
+ }
321
+ return { ok: true, value: manifest };
128
322
  }
129
323
  function validateBuddyAgentConfig(value) {
130
- const result = validateBuddyProjectManifest(value);
131
- if (!result.ok) return result;
132
- if (!("runtime" in result.value)) {
133
- return { ok: false, issues: [{ path: "/runtime", message: "Agent \u5DE5\u7A0B\u5C1A\u672A\u5B8C\u6210\uFF0C\u8BF7\u8FD0\u884C init --mode direct \u8865\u9F50 Runtime \u5DE5\u7A0B" }] };
134
- }
135
- return { ok: true, value: result.value };
324
+ return validateBuddyProjectManifest(value);
136
325
  }
137
326
  function assertBuddyAgentConfig(value) {
138
327
  const result = validateBuddyAgentConfig(value);
@@ -148,12 +337,7 @@ function parseBuddyProjectManifest(source) {
148
337
  return result.value;
149
338
  }
150
339
  function serializeBuddyDraftConfig(config) {
151
- const result = validateBuddyProjectManifest(config);
152
- if (!result.ok || "runtime" in result.value) {
153
- throw new BuddyConfigValidationError(result.ok ? [{ path: "/runtime", message: "\u5FC5\u987B\u662F\u672A\u5B8C\u6210\u7684\u521B\u4F5C\u9879\u76EE" }] : result.issues);
154
- }
155
- return `${JSON.stringify(result.value, null, 2)}
156
- `;
340
+ return serializeBuddyAgentConfig(config);
157
341
  }
158
342
  function serializeBuddyAgentConfig(config) {
159
343
  return `${JSON.stringify(assertBuddyAgentConfig(config), null, 2)}
@@ -169,14 +353,14 @@ function combineBuddyProject(config) {
169
353
  function createBuddyConfig(options) {
170
354
  return assertBuddyConfig({
171
355
  buddyId: options.buddyId,
172
- runtime: { language: "node" },
173
- ...options.model === void 0 ? {} : { model: options.model }
356
+ ...options.model === void 0 ? {} : { model: options.model },
357
+ datasets: options.datasets ?? []
174
358
  });
175
359
  }
176
360
  var ProjectInitializationError = class extends Error {
177
- constructor(code, message, targetPath, options) {
361
+ constructor(code2, message, targetPath, options) {
178
362
  super(message, options);
179
- this.code = code;
363
+ this.code = code2;
180
364
  this.targetPath = targetPath;
181
365
  this.name = "ProjectInitializationError";
182
366
  }
@@ -185,7 +369,7 @@ var ProjectInitializationError = class extends Error {
185
369
  };
186
370
  async function pathState(targetPath) {
187
371
  try {
188
- const entry = await lstat(targetPath);
372
+ const entry = await lstat3(targetPath);
189
373
  if (!entry.isDirectory() || entry.isSymbolicLink()) return { kind: "other" };
190
374
  return { kind: "directory", identity: `${entry.dev}:${entry.ino}` };
191
375
  } catch (cause) {
@@ -205,7 +389,7 @@ function entryIdentity(entry) {
205
389
  }
206
390
  async function fileIdentity(targetPath) {
207
391
  try {
208
- const entry = await lstat(targetPath);
392
+ const entry = await lstat3(targetPath);
209
393
  return entry.isFile() && !entry.isSymbolicLink() ? entryIdentity(entry) : void 0;
210
394
  } catch (cause) {
211
395
  if (cause.code === "ENOENT") return void 0;
@@ -214,11 +398,11 @@ async function fileIdentity(targetPath) {
214
398
  }
215
399
  async function unlinkMatchingFile(targetPath, expectedIdentity) {
216
400
  try {
217
- const entry = await lstat(targetPath);
401
+ const entry = await lstat3(targetPath);
218
402
  if (!entry.isFile() || entry.isSymbolicLink() || entryIdentity(entry) !== expectedIdentity) {
219
403
  return false;
220
404
  }
221
- await unlink(targetPath);
405
+ await unlink2(targetPath);
222
406
  return true;
223
407
  } catch (cause) {
224
408
  return cause.code === "ENOENT";
@@ -252,7 +436,7 @@ async function readExistingManifest(agentConfigPath) {
252
436
  agentConfigPath
253
437
  );
254
438
  }
255
- const entry = await lstat(agentConfigPath);
439
+ const entry = await lstat3(agentConfigPath);
256
440
  if (!entry.isFile() || entry.isSymbolicLink()) {
257
441
  throw new ProjectInitializationError(
258
442
  "TARGET_CHANGED",
@@ -261,7 +445,7 @@ async function readExistingManifest(agentConfigPath) {
261
445
  );
262
446
  }
263
447
  try {
264
- return parseBuddyProjectManifest(await readFile(agentConfigPath, "utf8"));
448
+ return parseBuddyProjectManifest(await readFile2(agentConfigPath, "utf8"));
265
449
  } catch (cause) {
266
450
  throw new ProjectInitializationError(
267
451
  "TARGET_CHANGED",
@@ -272,8 +456,8 @@ async function readExistingManifest(agentConfigPath) {
272
456
  }
273
457
  }
274
458
  async function planBuddyDraftInitialization(directory) {
275
- const rootDirectory = resolve(directory);
276
- const agentConfigPath = join(rootDirectory, BUDDY_AGENT_FILENAME);
459
+ const rootDirectory = resolve2(directory);
460
+ const agentConfigPath = join2(rootDirectory, BUDDY_AGENT_FILENAME);
277
461
  const state2 = await pathState(rootDirectory);
278
462
  if (state2.kind === "other") {
279
463
  throw new ProjectInitializationError(
@@ -283,7 +467,7 @@ async function planBuddyDraftInitialization(directory) {
283
467
  );
284
468
  }
285
469
  const existingManifest = state2.kind === "directory" ? await readExistingManifest(agentConfigPath) : void 0;
286
- if (existingManifest !== void 0 && "runtime" in existingManifest) {
470
+ if (existingManifest !== void 0 && (await missingBuddyAgentFiles(rootDirectory)).length === 0) {
287
471
  throw new ProjectInitializationError(
288
472
  "ALREADY_INITIALIZED",
289
473
  `\u9879\u76EE\u5DF2\u7ECF\u662F\u5B8C\u6574 Agent \u5DE5\u7A0B\uFF0C\u4E0D\u9700\u8981\u91CD\u65B0\u8FDB\u5165\u521B\u4F5C\u521D\u59CB\u5316\u3002`,
@@ -300,8 +484,8 @@ async function planBuddyDraftInitialization(directory) {
300
484
  };
301
485
  }
302
486
  async function planBuddyProjectInitialization(options) {
303
- const rootDirectory = resolve(options.directory);
304
- const agentConfigPath = resolve(rootDirectory, BUDDY_AGENT_FILENAME);
487
+ const rootDirectory = resolve2(options.directory);
488
+ const agentConfigPath = resolve2(rootDirectory, BUDDY_AGENT_FILENAME);
305
489
  const state2 = await pathState(rootDirectory);
306
490
  if (state2.kind === "other") {
307
491
  throw new ProjectInitializationError(
@@ -330,7 +514,7 @@ async function planBuddyProjectInitialization(options) {
330
514
  async function prepareInitializationDirectory(plan) {
331
515
  if (plan.createRootDirectory) {
332
516
  if ((await pathState(plan.rootDirectory)).kind !== "missing") throw targetChanged(plan);
333
- const firstCreatedDirectory = await mkdir(plan.rootDirectory, { recursive: true });
517
+ const firstCreatedDirectory = await mkdir2(plan.rootDirectory, { recursive: true });
334
518
  if (firstCreatedDirectory === void 0) throw targetChanged(plan);
335
519
  const created = await pathState(plan.rootDirectory);
336
520
  if (created.kind !== "directory") throw targetChanged(plan);
@@ -345,14 +529,14 @@ async function prepareInitializationDirectory(plan) {
345
529
  async function publishNewFile(options) {
346
530
  let temporaryHandle;
347
531
  let publishedHandle;
348
- const temporaryPath = join(
532
+ const temporaryPath = join2(
349
533
  options.rootDirectory,
350
534
  `.${options.temporaryLabel}.${process.pid}.${randomUUID()}.tmp`
351
535
  );
352
536
  let temporaryIdentity;
353
537
  let publishedIdentity;
354
538
  try {
355
- temporaryHandle = await open(temporaryPath, "wx");
539
+ temporaryHandle = await open2(temporaryPath, "wx");
356
540
  temporaryIdentity = entryIdentity(await temporaryHandle.stat());
357
541
  await temporaryHandle.writeFile(options.contents, "utf8");
358
542
  await temporaryHandle.sync();
@@ -369,13 +553,13 @@ async function publishNewFile(options) {
369
553
  await link(temporaryPath, options.targetPath);
370
554
  publishedIdentity = temporaryIdentity;
371
555
  } catch (cause) {
372
- const code = cause.code;
373
- if (!code || !HARD_LINK_UNSUPPORTED_CODES.has(code)) throw cause;
556
+ const code2 = cause.code;
557
+ if (!code2 || !HARD_LINK_UNSUPPORTED_CODES.has(code2)) throw cause;
374
558
  const fallbackBeforeOpen = await pathState(options.rootDirectory);
375
559
  if (fallbackBeforeOpen.kind !== "directory" || fallbackBeforeOpen.identity !== options.directoryIdentity) {
376
560
  throw targetChanged({ rootDirectory: options.rootDirectory });
377
561
  }
378
- publishedHandle = await open(options.targetPath, "wx");
562
+ publishedHandle = await open2(options.targetPath, "wx");
379
563
  publishedIdentity = entryIdentity(await publishedHandle.stat());
380
564
  const fallbackRoot = await pathState(options.rootDirectory);
381
565
  if (fallbackRoot.kind !== "directory" || fallbackRoot.identity !== options.directoryIdentity) {
@@ -389,7 +573,7 @@ async function publishNewFile(options) {
389
573
  const afterPublish = await pathState(options.rootDirectory);
390
574
  const publishedFileIdentity = await fileIdentity(options.targetPath);
391
575
  const temporaryFileIdentity = await fileIdentity(temporaryPath);
392
- const publishedContents = publishedFileIdentity ? await readFile(options.targetPath, "utf8") : void 0;
576
+ const publishedContents = publishedFileIdentity ? await readFile2(options.targetPath, "utf8") : void 0;
393
577
  if (afterPublish.kind !== "directory" || afterPublish.identity !== options.directoryIdentity || !publishedIdentity || publishedFileIdentity !== publishedIdentity || temporaryFileIdentity !== temporaryIdentity || publishedContents !== options.contents) {
394
578
  if (afterPublish.kind === "directory" && afterPublish.identity === options.directoryIdentity && publishedIdentity) {
395
579
  await unlinkMatchingFile(options.targetPath, publishedIdentity);
@@ -416,11 +600,11 @@ async function publishNewFile(options) {
416
600
  }
417
601
  }
418
602
  async function replaceExistingFile(options) {
419
- const temporaryPath = join(
603
+ const temporaryPath = join2(
420
604
  options.rootDirectory,
421
605
  `.${options.temporaryLabel}.${process.pid}.${randomUUID()}.tmp`
422
606
  );
423
- const handle = await open(temporaryPath, "wx", 384);
607
+ const handle = await open2(temporaryPath, "wx", 384);
424
608
  try {
425
609
  await handle.writeFile(options.contents, "utf8");
426
610
  await handle.sync();
@@ -431,11 +615,11 @@ async function replaceExistingFile(options) {
431
615
  const root = await pathState(options.rootDirectory);
432
616
  if (root.kind !== "directory" || root.identity !== options.directoryIdentity || await fileIdentity(options.targetPath) !== options.expectedFileIdentity) throw targetChanged({ rootDirectory: options.rootDirectory });
433
617
  await rename(temporaryPath, options.targetPath);
434
- if (await readFile(options.targetPath, "utf8") !== options.contents) {
618
+ if (await readFile2(options.targetPath, "utf8") !== options.contents) {
435
619
  throw targetChanged({ rootDirectory: options.rootDirectory });
436
620
  }
437
621
  } finally {
438
- await unlink(temporaryPath).catch((cause) => {
622
+ await unlink2(temporaryPath).catch((cause) => {
439
623
  if (cause.code !== "ENOENT") throw cause;
440
624
  });
441
625
  }
@@ -445,14 +629,14 @@ async function applyBuddyDraftInitialization(plan, draft) {
445
629
  const directoryIdentity = await prepareInitializationDirectory(plan);
446
630
  if (plan.existingManifest !== void 0) {
447
631
  const current = await readExistingManifest(plan.agentConfigPath);
448
- if (current === void 0 || "runtime" in current || current.buddyId !== draft.buddyId) {
632
+ if (current === void 0 || current.buddyId !== draft.buddyId || (await missingBuddyAgentFiles(plan.rootDirectory)).length === 0) {
449
633
  throw new ProjectInitializationError(
450
634
  "ALREADY_INITIALIZED",
451
635
  `\u5DF2\u6709 ${BUDDY_AGENT_FILENAME} \u7ED1\u5B9A\u7684 buddyId \u4E0E\u672C\u6B21\u4E91\u7AEF\u642D\u5B50\u4E0D\u4E00\u81F4\u3002`,
452
636
  plan.agentConfigPath
453
637
  );
454
638
  }
455
- return { rootDirectory: plan.rootDirectory, agentConfigPath: plan.agentConfigPath, manifest: draft };
639
+ return { rootDirectory: plan.rootDirectory, agentConfigPath: plan.agentConfigPath, manifest: current };
456
640
  }
457
641
  try {
458
642
  const published = await publishNewFile({
@@ -487,15 +671,16 @@ async function applyBuddyDraftInitialization(plan, draft) {
487
671
  }
488
672
  }
489
673
  async function applyBuddyProjectInitialization(plan, config) {
490
- const agentConfig = buddyAgentConfig(config);
491
- const contents = serializeBuddyAgentConfig(agentConfig);
492
- if (plan.existingManifest !== void 0 && plan.existingManifest.buddyId !== agentConfig.buddyId) {
674
+ const requestedConfig = buddyAgentConfig(config);
675
+ if (plan.existingManifest !== void 0 && plan.existingManifest.buddyId !== requestedConfig.buddyId) {
493
676
  throw new ProjectInitializationError(
494
677
  "ALREADY_INITIALIZED",
495
- `\u5DF2\u6709 ${BUDDY_AGENT_FILENAME} \u7ED1\u5B9A\u7684\u662F ${plan.existingManifest.buddyId}\uFF0C\u4E0D\u80FD\u6539\u6210 ${agentConfig.buddyId}\u3002`,
678
+ `\u5DF2\u6709 ${BUDDY_AGENT_FILENAME} \u7ED1\u5B9A\u7684\u662F ${plan.existingManifest.buddyId}\uFF0C\u4E0D\u80FD\u6539\u6210 ${requestedConfig.buddyId}\u3002`,
496
679
  plan.agentConfigPath
497
680
  );
498
681
  }
682
+ const agentConfig = { ...requestedConfig, ...plan.existingManifest };
683
+ const contents = serializeBuddyAgentConfig(agentConfig);
499
684
  const directoryIdentity = await prepareInitializationDirectory(plan);
500
685
  if (plan.existingManifest !== void 0) {
501
686
  const currentManifest = await readExistingManifest(plan.agentConfigPath);
@@ -504,7 +689,7 @@ async function applyBuddyProjectInitialization(plan, config) {
504
689
  }
505
690
  }
506
691
  try {
507
- if (plan.existingManifest !== void 0 && "runtime" in plan.existingManifest) {
692
+ if (plan.existingManifest !== void 0 && isDeepStrictEqual(plan.existingManifest, agentConfig)) {
508
693
  return { rootDirectory: plan.rootDirectory, agentConfigPath: plan.agentConfigPath };
509
694
  }
510
695
  let warning2;
@@ -555,167 +740,23 @@ async function initializeBuddyProject(options) {
555
740
  return applyBuddyProjectInitialization(plan, options.config);
556
741
  }
557
742
  async function readBuddyProjectManifest(projectRoot) {
558
- const rootDirectory = resolve(projectRoot);
559
- return parseBuddyProjectManifest(await readFile(resolve(rootDirectory, BUDDY_AGENT_FILENAME), "utf8"));
743
+ const rootDirectory = resolve2(projectRoot);
744
+ return parseBuddyProjectManifest(await readFile2(resolve2(rootDirectory, BUDDY_AGENT_FILENAME), "utf8"));
560
745
  }
561
746
  async function readBuddyAgentConfig(projectRoot) {
562
- const rootDirectory = resolve(projectRoot);
563
- const source = await readFile(resolve(rootDirectory, BUDDY_AGENT_FILENAME), "utf8");
747
+ const rootDirectory = resolve2(projectRoot);
748
+ const source = await readFile2(resolve2(rootDirectory, BUDDY_AGENT_FILENAME), "utf8");
564
749
  return parseBuddyAgentConfig(source);
565
750
  }
566
751
  async function readBuddyConfig(projectRoot) {
567
- const rootDirectory = resolve(projectRoot);
752
+ const rootDirectory = resolve2(projectRoot);
568
753
  return combineBuddyProject(await readBuddyAgentConfig(rootDirectory));
569
754
  }
570
755
 
571
- // packages/cli-core/src/init/scaffold.ts
572
- import { lstat as lstat2, mkdir as mkdir2, open as open2, readFile as readFile2, rmdir, unlink as unlink2 } from "node:fs/promises";
573
- import { resolve as resolve2 } from "node:path";
574
- var BUDDY_PLATFORM_AGENT_ENTRY = "agent.md";
575
- var BUDDY_PLATFORM_FACTORY_ENTRY = "src/buddy.mjs";
576
- var BUDDY_PLATFORM_PACKAGE_JSON = "package.json";
577
- var BUDDY_PLATFORM_PACKAGE_LOCK = "package-lock.json";
578
- var BUDDY_PLATFORM_START_ENTRY = "start.mjs";
579
- var PLATFORM_DIRECTORIES = ["src", "src/tools", "src/hooks", "src/services", "resources", "widgets"];
580
- function promptFor(name) {
581
- const safeName = name.trim() || "\u642D\u642D";
582
- return `\u4F60\u662F${safeName}\u3002\u8BF7\u7406\u89E3\u7528\u6237\u7684\u771F\u5B9E\u9700\u6C42\uFF0C\u7ED9\u51FA\u81EA\u7136\u3001\u51C6\u786E\u4E14\u6709\u5E2E\u52A9\u7684\u5B8C\u6574\u56DE\u590D\u3002`;
583
- }
584
- function platformAgentTemplate(name) {
585
- return `${promptFor(name)}
586
- `;
587
- }
588
- function platformFactoryTemplate() {
589
- return `import { readFileSync } from "node:fs";
590
-
591
- const systemPrompt = readFileSync(new URL("../agent.md", import.meta.url), "utf8").trim();
592
- if (!systemPrompt) throw new Error("agent.md must contain the Buddy system prompt");
593
-
594
- /**
595
- * Called when the Runtime creates an agent for a conversation.
596
- * Import tools from ./tools/ and hooks from ./hooks/, then register them below.
597
- * Shared business code can live in ./services/; directories do not enable capabilities.
598
- * Use conversation.memory / dataAccess / widgets / proactive for platform capabilities.
599
- * Place widget assets in ../widgets/<type-id>/index.html and schema.json.
600
- * Runtime uploads these files before connecting to MLB; upload failures stop startup.
601
- * Select widget_* in platformTools to expose widget tools to the model.
602
- * Use platform APIs only; do not call third-party services directly.
603
- */
604
- export default function createBuddy(_conversation) {
605
- return {
606
- initialState: {
607
- systemPrompt,
608
- thinkingLevel: "off",
609
- tools: [],
610
- },
611
- };
612
- }
613
- `;
614
- }
615
- function platformStartTemplate() {
616
- return `// Generated startup: local DEV and hosted execution use the same Buddy Runtime.
617
- import { readFileSync } from "node:fs";
618
- import { BuddyServer } from "@my-life-buddies/buddy-runtime";
619
- import createBuddy from "./src/buddy.mjs";
620
-
621
- const config = JSON.parse(readFileSync(new URL("./buddy.agent.json", import.meta.url), "utf8"));
622
- if (!config || config.schemaVersion !== 1 || typeof config.buddyId !== "string" || !config.buddyId || config.runtime?.language !== "node") {
623
- throw new Error("buddy.agent.json is not a complete Buddy project");
624
- }
625
- if (process.env.MLB_BUDDY_ID && process.env.MLB_BUDDY_ID !== config.buddyId) {
626
- throw new Error("MLB_BUDDY_ID does not match buddy.agent.json");
627
- }
628
- const model = typeof config.model === "string" && config.model ? config.model : "kimi-k2.6";
629
-
630
- const lifecycle = new AbortController();
631
- const stop = () => lifecycle.abort();
632
- process.on("SIGINT", stop);
633
- process.on("SIGTERM", stop);
634
- let runtime;
635
- try {
636
- runtime = await BuddyServer.start({ model, buddy: createBuddy });
637
- if (!lifecycle.signal.aborted) {
638
- await new Promise((resolve) => lifecycle.signal.addEventListener("abort", resolve, { once: true }));
639
- }
640
- } catch (error) {
641
- if (!lifecycle.signal.aborted) {
642
- console.error("buddy-agent:", error instanceof Error ? error.message : "Startup failed");
643
- process.exitCode = 1;
644
- }
645
- } finally {
646
- await runtime?.stop();
647
- process.off("SIGINT", stop);
648
- process.off("SIGTERM", stop);
649
- }
650
- `;
651
- }
652
- async function scaffoldPlatformAgent(projectRoot, name) {
653
- const path = resolve2(projectRoot, BUDDY_PLATFORM_AGENT_ENTRY);
654
- const hasAlternateLockfile = (await Promise.all(["pnpm-lock.yaml", "yarn.lock"].map(async (name2) => {
655
- try {
656
- if (!(await lstat2(resolve2(projectRoot, name2))).isFile()) {
657
- throw new TypeError(`\u4F9D\u8D56\u9501\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF1A${name2}`);
658
- }
659
- return true;
660
- } catch (cause) {
661
- if (cause.code === "ENOENT") return false;
662
- throw cause;
663
- }
664
- }))).some(Boolean);
665
- const files = [
666
- { path, content: platformAgentTemplate(name) },
667
- { path: resolve2(projectRoot, BUDDY_PLATFORM_FACTORY_ENTRY), content: platformFactoryTemplate() },
668
- { path: resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_JSON), content: await readFile2(new URL("../../resources/agent-template/package.json", import.meta.url), "utf8") },
669
- ...hasAlternateLockfile ? [] : [{ path: resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_LOCK), content: await readFile2(new URL("../../resources/agent-template/package-lock.json", import.meta.url), "utf8") }],
670
- { path: resolve2(projectRoot, BUDDY_PLATFORM_START_ENTRY), content: platformStartTemplate() }
671
- ];
672
- const handles = [];
673
- const createdDirectories = [];
674
- try {
675
- for (const directory of PLATFORM_DIRECTORIES) {
676
- const directoryPath = resolve2(projectRoot, directory);
677
- try {
678
- await mkdir2(directoryPath, { mode: 448 });
679
- createdDirectories.push(directoryPath);
680
- } catch (cause) {
681
- if (cause.code !== "EEXIST") throw cause;
682
- }
683
- if (!(await lstat2(directoryPath)).isDirectory()) {
684
- 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}`);
685
- }
686
- }
687
- for (const file of files) {
688
- try {
689
- handles.push({ path: file.path, handle: await open2(file.path, "wx", 384) });
690
- } catch (cause) {
691
- if (cause.code !== "EEXIST") throw cause;
692
- if (!(await lstat2(file.path)).isFile()) {
693
- 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${file.path}`);
694
- }
695
- }
696
- }
697
- for (const { path: filePath, handle } of handles) {
698
- const file = files.find((candidate) => candidate.path === filePath);
699
- await handle.writeFile(file.content, "utf8");
700
- await handle.sync();
701
- }
702
- return Object.freeze({ path, created: handles.length > 0 || createdDirectories.length > 0 });
703
- } catch (cause) {
704
- await Promise.all(handles.map(({ handle }) => handle.close().catch(() => void 0)));
705
- await Promise.all(handles.map(({ path: createdPath }) => unlink2(createdPath).catch(() => void 0)));
706
- for (const directory of createdDirectories.reverse()) {
707
- await rmdir(directory).catch(() => void 0);
708
- }
709
- throw cause;
710
- } finally {
711
- await Promise.all(handles.map(({ handle }) => handle.close().catch(() => void 0)));
712
- }
713
- }
714
-
715
756
  // packages/cli-core/src/init/creator.ts
716
757
  import { randomUUID as randomUUID2 } from "node:crypto";
717
- import { link as link2, lstat as lstat3, mkdir as mkdir3, readFile as readFile3, unlink as unlink3, writeFile } from "node:fs/promises";
718
- import { join as join2 } from "node:path";
758
+ import { link as link2, lstat as lstat4, mkdir as mkdir3, readFile as readFile3, unlink as unlink3, writeFile } from "node:fs/promises";
759
+ import { join as join3 } from "node:path";
719
760
  import { fileURLToPath } from "node:url";
720
761
 
721
762
  // packages/cli-core/src/init/development-handoff.ts
@@ -769,7 +810,7 @@ Creator \u8FD4\u56DE\u6709\u6548\u7684 \`completion.manualPath\` \u540E\uFF0C\u8
769
810
 
770
811
  | \u4F4D\u7F6E | \u804C\u8D23\u4E0E\u751F\u6210\u65F6\u673A |
771
812
  | --- | --- |
772
- | \`buddy.agent.json\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u4FDD\u5B58\u5E73\u53F0\u8EAB\u4EFD\u3001\u8FD0\u884C\u8BED\u8A00\u548C\u53EF\u9009\u6A21\u578B\uFF0C\u4E0D\u653E\u4E1A\u52A1\u51FD\u6570 |
813
+ | \`buddy.agent.json\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u4FDD\u5B58\u5E73\u53F0\u8EAB\u4EFD\u3001\u53EF\u9009\u6A21\u578B\u548C\u6570\u636E\u9700\u6C42 datasets\uFF0C\u4E0D\u653E\u4E1A\u52A1\u51FD\u6570 |
773
814
  | \`agent.md\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u4FDD\u5B58\u7CFB\u7EDF Prompt |
774
815
  | \`start.mjs\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u8BFB\u53D6\u542F\u52A8\u914D\u7F6E\u3001\u542F\u52A8 Runtime \u5E76\u5904\u7406\u9000\u51FA |
775
816
  | \`package.json\`\u3001\`package-lock.json\` | \u9ED8\u8BA4\u751F\u6210\uFF0C\u58F0\u660E\u542F\u52A8\u547D\u4EE4\u3001\u51C6\u786E Runtime \u7248\u672C\u5E76\u9501\u5B9A\u4F9D\u8D56 |
@@ -790,6 +831,8 @@ Widget \u5DE5\u7A0B\u7EA6\u5B9A\uFF1A\u4E00\u7EA7\u76EE\u5F55\u540D\u5C31\u662F\
790
831
 
791
832
  \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
792
833
 
834
+ \u9700\u8981\u7528\u6237\u6570\u636E\u65F6\uFF0C\u5728 \`buddy.agent.json.datasets\` \u5199\u660E\u6240\u9700\u7684 \`id\` \u548C \`purpose\`\uFF0C\u540C\u4E00 ID \u53EA\u5199\u4E00\u6B21\uFF0C\u7528\u9014\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u6700\u591A 200 \u5B57\uFF1B\u65E0\u6570\u636E\u9700\u6C42\u65F6\u4FDD\u7559\u7A7A\u6570\u7EC4\u3002\u5F53\u524D\u63A5\u53D7 \`health.sleep\`\u3001\`health.workouts\`\u3001\`health.distance\`\u3001\`health.heartRate\`\u3001\`health.hrv\`\u3001\`health.respiratoryRate\`\u3001\`health.oxygenSaturation\` \u548C \`health.environmentalAudioExposure\`\u3002CLI \u6821\u9A8C\u5E76\u968F\u5305\u4E0A\u4F20\u58F0\u660E\uFF0C\u670D\u52A1\u7AEF\u89E3\u6790\u5305\u4E0E App \u6388\u6743\u94FE\u8DEF\u4ECD\u5F85\u63A5\u5165\uFF1B\u4E0D\u80FD\u628A\u586B\u5199\u914D\u7F6E\u8BF4\u6210\u7528\u6237\u5DF2\u7ECF\u6388\u6743\u3002Runtime \u4ECD\u8BFB\u53D6\u5E73\u53F0\u5DF2\u751F\u6548\u7684\u58F0\u660E\uFF1B\u6A21\u578B\u76F4\u63A5\u67E5\u6570\u636E\u9700\u5728 \`src/buddy.mjs\` \u663E\u5F0F\u542F\u7528 \`data_access_query\`\uFF0C\u4E1A\u52A1 Tool \u4E5F\u53EF\u8C03\u7528 \`conversation.dataAccess.query\`\u3002
835
+
793
836
  ### \u53EA\u6709\u5173\u952E\u7F3A\u53E3\u624D\u8865\u95EE
794
837
 
795
838
  \u80FD\u4ECE\u624B\u518C\u548C\u73B0\u6709\u4EE3\u7801\u786E\u5B9A\u7684\u76F4\u63A5\u5B9E\u73B0\uFF0C\u5305\u62EC\u9009\u62E9\u54EA\u4E2A\u51FD\u6570\u3001\u6587\u4EF6\u3001Tool \u6216\u5DF2\u6709\u9ED8\u8BA4\u80FD\u529B\uFF1B\u4E0D\u8981\u628A\u6280\u672F\u6620\u5C04\u518D\u4EA4\u7ED9\u5F00\u53D1\u8005\u51B3\u5B9A\uFF0C\u4E0D\u8981\u6C42\u5BF9\u6574\u4EFD\u5B9E\u73B0\u65B9\u6848\u91CD\u65B0\u7B7E\u5B57\u3002
@@ -832,7 +875,7 @@ var REQUIRED = [
832
875
  var CreatorPreparationError = class extends Error {
833
876
  };
834
877
  async function state(path) {
835
- return lstat3(path).catch((cause) => {
878
+ return lstat4(path).catch((cause) => {
836
879
  if (cause.code === "ENOENT") return void 0;
837
880
  throw cause;
838
881
  });
@@ -841,7 +884,7 @@ async function safeDirectory(path) {
841
884
  await mkdir3(path, { mode: 448 }).catch((cause) => {
842
885
  if (cause.code !== "EEXIST") throw cause;
843
886
  });
844
- if (!(await lstat3(path)).isDirectory()) {
887
+ if (!(await lstat4(path)).isDirectory()) {
845
888
  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}`);
846
889
  }
847
890
  }
@@ -866,17 +909,17 @@ async function writeOnce(path, bytes) {
866
909
  }
867
910
  async function bundledSkill(root) {
868
911
  for (const path of REQUIRED) {
869
- if (!(await state(join2(root, path)))?.isFile()) {
912
+ if (!(await state(join3(root, path)))?.isFile()) {
870
913
  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`);
871
914
  }
872
915
  }
873
916
  return root;
874
917
  }
875
918
  function handoffMarkdown(context, skillRoot, session, workspacePath) {
876
- const contextPath = join2(context.projectRoot, ".buddy", "creator", "initial-context.json");
919
+ const contextPath = join3(context.projectRoot, ".buddy", "creator", "initial-context.json");
877
920
  const openArgs = [
878
921
  "-B",
879
- join2(skillRoot, "scripts/buddy.py"),
922
+ join3(skillRoot, "scripts/buddy.py"),
880
923
  "open",
881
924
  "--workspace",
882
925
  workspacePath,
@@ -906,7 +949,7 @@ function handoffMarkdown(context, skillRoot, session, workspacePath) {
906
949
 
907
950
  ## \u73B0\u5728\u7531 Coding Agent \u6267\u884C
908
951
 
909
- 1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(join2(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
952
+ 1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(join3(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
910
953
  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
911
954
  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
912
955
 
@@ -943,11 +986,11 @@ ${DEVELOPMENT_HANDOFF}
943
986
  `;
944
987
  }
945
988
  async function prepareCreatorSkill(context, io, options = {}) {
946
- const root = join2(context.projectRoot, ".buddy", "creator");
989
+ const root = join3(context.projectRoot, ".buddy", "creator");
947
990
  try {
948
- await safeDirectory(join2(context.projectRoot, ".buddy"));
991
+ await safeDirectory(join3(context.projectRoot, ".buddy"));
949
992
  await safeDirectory(root);
950
- const sessionPath = join2(root, "session.json");
993
+ const sessionPath = join3(root, "session.json");
951
994
  await writeOnce(sessionPath, JSON.stringify({
952
995
  schemaVersion: 1,
953
996
  platformBuddyId: context.buddyId,
@@ -959,11 +1002,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
959
1002
  }
960
1003
  options.signal?.throwIfAborted();
961
1004
  const skillRoot = await bundledSkill(options.bundledSkillRoot ?? BUNDLED_CREATOR_SKILL_ROOT);
962
- await safeDirectory(join2(root, "workspaces"));
963
- const workspacePath = join2(root, "workspaces", session.creationKey);
1005
+ await safeDirectory(join3(root, "workspaces"));
1006
+ const workspacePath = join3(root, "workspaces", session.creationKey);
964
1007
  const workspace = await state(workspacePath);
965
1008
  if (workspace && !workspace.isDirectory()) throw new CreatorPreparationError("\u5DF2\u6709\u521B\u4F5C\u5DE5\u4F5C\u533A\u4E0D\u662F\u666E\u901A\u76EE\u5F55\u3002");
966
- const contextPath = join2(root, "initial-context.json");
1009
+ const contextPath = join3(root, "initial-context.json");
967
1010
  await writeOnce(contextPath, JSON.stringify({
968
1011
  source: "platform_meta",
969
1012
  name: context.appName,
@@ -979,7 +1022,7 @@ async function prepareCreatorSkill(context, io, options = {}) {
979
1022
  } catch {
980
1023
  throw new CreatorPreparationError(`\u5DF2\u6709\u521D\u59CB\u80CC\u666F\u65E0\u6548\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\uFF1A${contextPath}`);
981
1024
  }
982
- const handoffPath = join2(root, "HANDOFF.md");
1025
+ const handoffPath = join3(root, "HANDOFF.md");
983
1026
  const handoff = handoffMarkdown({
984
1027
  ...context,
985
1028
  appName: initial.name,
@@ -990,11 +1033,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
990
1033
  if ((await safeRead(handoffPath))?.toString("utf8") !== handoff) {
991
1034
  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}`);
992
1035
  }
993
- io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${join2(skillRoot, "SKILL.md")}`);
1036
+ io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${join3(skillRoot, "SKILL.md")}`);
994
1037
  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`);
995
1038
  io.write(DEVELOPMENT_HANDOFF_SUMMARY);
996
1039
  io.write("\u672C\u547D\u4EE4\u53EA\u8D1F\u8D23\u8EAB\u4EFD\u767B\u8BB0\u548C Skill \u51C6\u5907\uFF0C\u4E0D\u4EE3\u8868\u91C7\u8BBF\u3001Booklet \u6216 Agent \u5DF2\u5B8C\u6210\u3002\u8BF7\u6309\u5DF2\u6709\u4F5C\u54C1\u8FDB\u5EA6\u63A5\u7EED\uFF0C\u624B\u518C\u786E\u8BA4\u540E\u518D\u8865\u9F50\u5B98\u65B9 Runtime \u5DE5\u7A0B\uFF1B\u91CD\u590D create \u4F1A\u590D\u7528\u540C\u4E00\u4F5C\u54C1\u3002");
997
- return { exitCode: 0, creator: { skillPath: join2(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
1040
+ return { exitCode: 0, creator: { skillPath: join3(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
998
1041
  } catch (cause) {
999
1042
  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";
1000
1043
  io.writeError(`Creator Skill \u51C6\u5907\u5931\u8D25\uFF1A${message}`);
@@ -1005,9 +1048,9 @@ async function prepareCreatorSkill(context, io, options = {}) {
1005
1048
 
1006
1049
  // packages/cli-core/src/cloud/errors.ts
1007
1050
  var CloudLifecycleError = class extends Error {
1008
- constructor(code, message, details = {}) {
1051
+ constructor(code2, message, details = {}) {
1009
1052
  super(message);
1010
- this.code = code;
1053
+ this.code = code2;
1011
1054
  this.name = "CloudLifecycleError";
1012
1055
  this.details = Object.freeze({ ...details });
1013
1056
  }
@@ -1345,9 +1388,9 @@ function buildAgentEnvironment(options) {
1345
1388
  }
1346
1389
 
1347
1390
  // packages/cli-core/src/dev/orchestrator.ts
1348
- import { createHash } from "node:crypto";
1349
- import { readFile as readFile4 } from "node:fs/promises";
1350
- import { join as join3, resolve as resolve4 } from "node:path";
1391
+ import { createHash as createHash2 } from "node:crypto";
1392
+ import { readFile as readFile5 } from "node:fs/promises";
1393
+ import { join as join5, resolve as resolve4 } from "node:path";
1351
1394
 
1352
1395
  // packages/cli-core/src/dev/launch.ts
1353
1396
  import { lstatSync, readFileSync } from "node:fs";
@@ -1443,9 +1486,9 @@ var NodeAgentProcessLauncher = class {
1443
1486
  resolve12({ code: null, signal: null, error: error2 });
1444
1487
  }
1445
1488
  });
1446
- child.once("exit", (code, signal) => {
1489
+ child.once("exit", (code2, signal) => {
1447
1490
  hasExited = true;
1448
- resolve12({ code, signal });
1491
+ resolve12({ code: code2, signal });
1449
1492
  });
1450
1493
  });
1451
1494
  child.stdout?.on("data", (chunk) => {
@@ -1604,6 +1647,106 @@ function processTreeControllerFor(platform) {
1604
1647
  });
1605
1648
  }
1606
1649
 
1650
+ // packages/cli-core/src/dev/instance-lock.ts
1651
+ import { createHash, randomUUID as randomUUID3 } from "node:crypto";
1652
+ import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, rename as rename2, rmdir as rmdir2, unlink as unlink4, writeFile as writeFile2 } from "node:fs/promises";
1653
+ import { homedir } from "node:os";
1654
+ import { join as join4 } from "node:path";
1655
+ function alreadyRunningError() {
1656
+ const message = "\u8FD9\u4E2A\u642D\u5B50\u5DF2\u6709 DEV \u5B9E\u4F8B\u5728\u8FD0\u884C\u3002\u8BF7\u5148\u5728\u539F\u9884\u89C8\u9875\u70B9\u51FB\u300C\u505C\u6B62 DEV\u300D\uFF0C\u6216\u5728\u8FD0\u884C dev \u7684\u7EC8\u7AEF\u6309 Ctrl+C\uFF0C\u518D\u542F\u52A8\u3002";
1657
+ return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
1658
+ }
1659
+ function alive(pid) {
1660
+ try {
1661
+ process.kill(pid, 0);
1662
+ return true;
1663
+ } catch (error2) {
1664
+ return error2.code !== "ESRCH";
1665
+ }
1666
+ }
1667
+ function validPid(pid) {
1668
+ return Number.isSafeInteger(pid) && pid > 0;
1669
+ }
1670
+ function code(error2) {
1671
+ return error2?.code;
1672
+ }
1673
+ async function acquireDevInstanceLock(options) {
1674
+ const root = options.directory ?? join4(homedir(), ".buddy", "dev-locks");
1675
+ const key = createHash("sha256").update(`${new URL(options.gatewayUrl).origin}
1676
+ ${options.devAgentId}`).digest("hex");
1677
+ const path = join4(root, key);
1678
+ const ownerName = `owner-${randomUUID3()}.json`;
1679
+ const candidate = join4(root, `.pending-${randomUUID3()}`);
1680
+ const owner = { pid: process.pid };
1681
+ await mkdir4(root, { recursive: true, mode: 448 });
1682
+ await mkdir4(candidate, { mode: 448 });
1683
+ await writeFile2(join4(candidate, ownerName), JSON.stringify(owner), { mode: 384, flag: "wx" });
1684
+ try {
1685
+ for (let attempt = 0; ; attempt++) {
1686
+ try {
1687
+ await rename2(candidate, path);
1688
+ break;
1689
+ } catch (error2) {
1690
+ if (!["EEXIST", "ENOTEMPTY"].includes(code(error2) ?? "")) throw error2;
1691
+ if (attempt >= 8) throw alreadyRunningError();
1692
+ }
1693
+ let names;
1694
+ try {
1695
+ names = await readdir2(path);
1696
+ } catch (error2) {
1697
+ if (code(error2) === "ENOENT") continue;
1698
+ throw error2;
1699
+ }
1700
+ if (names.length === 0) continue;
1701
+ if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw alreadyRunningError();
1702
+ const previousPath = join4(path, names[0]);
1703
+ let previous;
1704
+ try {
1705
+ previous = JSON.parse(await readFile4(previousPath, "utf8"));
1706
+ } catch (error2) {
1707
+ if (code(error2) === "ENOENT") continue;
1708
+ throw alreadyRunningError();
1709
+ }
1710
+ if (!validPid(previous.pid) || previous.runtimePid !== void 0 && !validPid(previous.runtimePid) || alive(previous.pid) || previous.runtimePid !== void 0 && alive(previous.runtimePid)) throw alreadyRunningError();
1711
+ try {
1712
+ await unlink4(previousPath);
1713
+ } catch (error2) {
1714
+ if (code(error2) === "ENOENT") continue;
1715
+ throw error2;
1716
+ }
1717
+ await rmdir2(path).catch((error2) => {
1718
+ if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(code(error2) ?? "")) throw error2;
1719
+ });
1720
+ }
1721
+ } finally {
1722
+ await unlink4(join4(candidate, ownerName)).catch(() => void 0);
1723
+ await rmdir2(candidate).catch(() => void 0);
1724
+ }
1725
+ let released = false;
1726
+ return {
1727
+ async attachRuntime(pid) {
1728
+ if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
1729
+ const temporary = join4(root, `.update-${randomUUID3()}`);
1730
+ try {
1731
+ await writeFile2(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
1732
+ await rename2(temporary, join4(path, ownerName));
1733
+ } finally {
1734
+ await unlink4(temporary).catch(() => void 0);
1735
+ }
1736
+ },
1737
+ async release() {
1738
+ if (released) return;
1739
+ released = true;
1740
+ await unlink4(join4(path, ownerName)).catch((error2) => {
1741
+ if (code(error2) !== "ENOENT") throw error2;
1742
+ });
1743
+ await rmdir2(path).catch((error2) => {
1744
+ if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(code(error2) ?? "")) throw error2;
1745
+ });
1746
+ }
1747
+ };
1748
+ }
1749
+
1607
1750
  // packages/cli-core/src/dev/orchestrator.ts
1608
1751
  var DEFAULT_ONLINE_TIMEOUT_MS = 3e4;
1609
1752
  var DEFAULT_POLL_INTERVAL_MS = 200;
@@ -1740,6 +1883,7 @@ var ReadyBuddyDevHandle = class {
1740
1883
  completion;
1741
1884
  #process;
1742
1885
  #processTree;
1886
+ #instanceLock;
1743
1887
  #lifecycle;
1744
1888
  #stopGracePeriodMs;
1745
1889
  #onStateChange;
@@ -1755,6 +1899,7 @@ var ReadyBuddyDevHandle = class {
1755
1899
  this.gatewayUrl = new URL(options.gatewayUrl);
1756
1900
  this.#process = options.process;
1757
1901
  this.#processTree = options.processTree;
1902
+ this.#instanceLock = options.instanceLock;
1758
1903
  this.#lifecycle = options.lifecycle;
1759
1904
  this.#stopGracePeriodMs = options.stopGracePeriodMs;
1760
1905
  this.#signal = options.signal;
@@ -1785,6 +1930,7 @@ var ReadyBuddyDevHandle = class {
1785
1930
  this.#lifecycle.abort(new Error("DEV session stopped"));
1786
1931
  try {
1787
1932
  await this.#processTree.terminate(this.#process, { gracePeriodMs: this.#stopGracePeriodMs });
1933
+ await this.#instanceLock.release();
1788
1934
  this.#emit("stopped");
1789
1935
  this.#finish({ state: "stopped" });
1790
1936
  } catch (cause) {
@@ -1805,7 +1951,7 @@ var ReadyBuddyDevHandle = class {
1805
1951
  this.#lifecycle.abort(error2);
1806
1952
  await this.#processTree.terminate(this.#process, {
1807
1953
  gracePeriodMs: Math.min(this.#stopGracePeriodMs, 1e3)
1808
- }).catch(() => void 0);
1954
+ }).then(() => this.#instanceLock.release()).catch(() => void 0);
1809
1955
  this.#finish({ state: "failed", error: error2 });
1810
1956
  }
1811
1957
  #emit(state2) {
@@ -1838,6 +1984,7 @@ async function startBuddyDev(options) {
1838
1984
  const signal = options.signal === void 0 ? lifecycle.signal : AbortSignal.any([lifecycle.signal, options.signal]);
1839
1985
  let agentProcess;
1840
1986
  let processTree;
1987
+ let instanceLock;
1841
1988
  const emit = (state2) => {
1842
1989
  try {
1843
1990
  options.onStateChange?.(state2);
@@ -1850,7 +1997,7 @@ async function startBuddyDev(options) {
1850
1997
  let config;
1851
1998
  let configSource;
1852
1999
  try {
1853
- configSource = await readFile4(join3(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
2000
+ configSource = await readFile5(join5(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
1854
2001
  config = combineBuddyProject(parseBuddyAgentConfig(configSource));
1855
2002
  } catch (cause) {
1856
2003
  throw new BuddyDevError({
@@ -1865,10 +2012,20 @@ async function startBuddyDev(options) {
1865
2012
  client: options.registrationClient,
1866
2013
  devAgentId,
1867
2014
  configSource,
1868
- configDigest: `sha256:${createHash("sha256").update(configSource).digest("hex")}`,
2015
+ configDigest: `sha256:${createHash2("sha256").update(configSource).digest("hex")}`,
1869
2016
  signal
1870
2017
  });
1871
2018
  throwIfDevAborted(signal);
2019
+ instanceLock = await acquireDevInstanceLock({
2020
+ gatewayUrl: registration.appServerUrl,
2021
+ devAgentId,
2022
+ directory: options.instanceLockDirectory
2023
+ });
2024
+ const online = await options.registrationClient.listOnline({
2025
+ signal: AbortSignal.any([signal, AbortSignal.timeout(onlineTimeoutMs)])
2026
+ });
2027
+ if (online.some((item) => item.devAgentId === devAgentId && item.online)) throw alreadyRunningError();
2028
+ throwIfDevAborted(signal);
1872
2029
  const environment = buildAgentEnvironment({
1873
2030
  parentEnv: options.parentEnv ?? process.env,
1874
2031
  buddyId: devAgentId,
@@ -1889,6 +2046,7 @@ async function startBuddyDev(options) {
1889
2046
  onStderr: (text) => options.onLog?.({ source: "agent.stderr", text })
1890
2047
  }
1891
2048
  });
2049
+ await instanceLock.attachRuntime(agentProcess.pid);
1892
2050
  const exited = agentProcess.exit.then((exit) => {
1893
2051
  throw agentExitedError(exit);
1894
2052
  });
@@ -1910,6 +2068,7 @@ async function startBuddyDev(options) {
1910
2068
  gatewayUrl: new URL(environment.MLB_GATEWAY_URL),
1911
2069
  process: agentProcess,
1912
2070
  processTree,
2071
+ instanceLock,
1913
2072
  lifecycle,
1914
2073
  stopGracePeriodMs,
1915
2074
  ...options.signal === void 0 ? {} : { signal: options.signal },
@@ -1920,7 +2079,9 @@ async function startBuddyDev(options) {
1920
2079
  const aborted = options.signal?.aborted || isAbortLike(cause) || cause instanceof BuddyDevError && cause.code === "DEV_ABORTED";
1921
2080
  if (agentProcess !== void 0 || aborted) emit("stopping");
1922
2081
  if (agentProcess !== void 0 && processTree !== void 0) {
1923
- await processTree.terminate(agentProcess, { gracePeriodMs: stopGracePeriodMs }).catch(() => void 0);
2082
+ await processTree.terminate(agentProcess, { gracePeriodMs: stopGracePeriodMs }).then(() => instanceLock?.release()).catch(() => void 0);
2083
+ } else {
2084
+ await instanceLock?.release();
1924
2085
  }
1925
2086
  if (aborted) {
1926
2087
  emit("stopped");
@@ -1959,12 +2120,12 @@ function createBuddyDevStarter(options = {}) {
1959
2120
  }
1960
2121
 
1961
2122
  // packages/cli-core/src/project-context.ts
1962
- import { lstat as lstat4, realpath } from "node:fs/promises";
1963
- import { dirname as dirname2, join as join4, resolve as resolve6 } from "node:path";
2123
+ import { lstat as lstat5, realpath } from "node:fs/promises";
2124
+ import { dirname as dirname2, join as join6, resolve as resolve6 } from "node:path";
1964
2125
  var BuddyProjectContextError = class extends Error {
1965
- constructor(code, message, targetPath, options) {
2126
+ constructor(code2, message, targetPath, options) {
1966
2127
  super(message, options);
1967
- this.code = code;
2128
+ this.code = code2;
1968
2129
  this.targetPath = targetPath;
1969
2130
  this.name = "BuddyProjectContextError";
1970
2131
  }
@@ -1975,7 +2136,7 @@ async function existingDirectory(target) {
1975
2136
  const requested = resolve6(target);
1976
2137
  let entry;
1977
2138
  try {
1978
- entry = await lstat4(requested);
2139
+ entry = await lstat5(requested);
1979
2140
  } catch (cause) {
1980
2141
  if (cause.code === "ENOENT") {
1981
2142
  throw new BuddyProjectContextError(
@@ -1997,9 +2158,9 @@ async function existingDirectory(target) {
1997
2158
  return realpath(requested);
1998
2159
  }
1999
2160
  async function projectFileState(directory, filename) {
2000
- const configPath = join4(directory, filename);
2161
+ const configPath = join6(directory, filename);
2001
2162
  try {
2002
- const entry = await lstat4(configPath);
2163
+ const entry = await lstat5(configPath);
2003
2164
  if (!entry.isFile() || entry.isSymbolicLink()) {
2004
2165
  throw new BuddyProjectContextError(
2005
2166
  "PROJECT_CONFIG_INVALID",
@@ -2040,15 +2201,17 @@ async function requireBuddyProjectRoot(startDirectory) {
2040
2201
  }
2041
2202
  async function requireBuddyAgentProjectRoot(startDirectory) {
2042
2203
  const root = await requireBuddyProjectRoot(startDirectory);
2043
- const agentPath = join4(root, BUDDY_AGENT_FILENAME);
2204
+ const agentPath = join6(root, BUDDY_AGENT_FILENAME);
2044
2205
  try {
2045
2206
  await readBuddyAgentConfig(root);
2207
+ const missing = await missingBuddyAgentFiles(root);
2208
+ if (missing.length > 0) throw new Error(`\u5DE5\u7A0B\u7F3A\u5C11\u6709\u6548\u6587\u4EF6\uFF1A${missing.join("\u3001")}`);
2046
2209
  return root;
2047
2210
  } catch (cause) {
2048
2211
  throw new BuddyProjectContextError(
2049
2212
  "PROJECT_NOT_FOUND",
2050
2213
  [
2051
- `\u5F53\u524D ${BUDDY_AGENT_FILENAME} \u5C1A\u672A\u5F62\u6210\u53EF\u8FD0\u884C\u7684 Agent \u5DE5\u7A0B\u5B9A\u4E49\u3002`,
2214
+ `\u5F53\u524D buddy.agent.json \u6240\u5728\u5DE5\u7A0B\u5C1A\u4E0D\u53EF\u8FD0\u884C\uFF1A${cause instanceof Error ? cause.message : "\u8BF7\u68C0\u67E5 buddy.agent.json \u4E0E\u5DE5\u7A0B\u6587\u4EF6"}`,
2052
2215
  "\u8BF7\u5148\u8FD0\u884C buddy-cli init --mode direct \u8865\u9F50\u5B98\u65B9 Runtime \u5DE5\u7A0B\uFF0C\u518D\u8FD0\u884C dev\u3001preview \u6216 push\u3002"
2053
2216
  ].join("\n"),
2054
2217
  agentPath,
@@ -2060,7 +2223,7 @@ async function nearestExistingDirectory(target) {
2060
2223
  let candidate = resolve6(target);
2061
2224
  while (true) {
2062
2225
  try {
2063
- const entry = await lstat4(candidate);
2226
+ const entry = await lstat5(candidate);
2064
2227
  if (!entry.isDirectory()) {
2065
2228
  throw new BuddyProjectContextError(
2066
2229
  "PROJECT_DIRECTORY_INVALID",
@@ -2109,7 +2272,7 @@ async function assertInitializableBuddyTarget(target) {
2109
2272
  } catch (cause) {
2110
2273
  throw new BuddyProjectContextError(
2111
2274
  "PROJECT_CONFIG_INVALID",
2112
- `${join4(projectRoot, BUDDY_AGENT_FILENAME)} \u4E0D\u662F\u6709\u6548\u7684\u9879\u76EE\u6E05\u5355\u3002`,
2275
+ `${join6(projectRoot, BUDDY_AGENT_FILENAME)} \u4E0D\u662F\u6709\u6548\u7684\u9879\u76EE\u6E05\u5355\u3002`,
2113
2276
  requested,
2114
2277
  { cause }
2115
2278
  );
@@ -2261,7 +2424,7 @@ async function runDevCommand(args, io, options = {}) {
2261
2424
  import { resolve as resolve8 } from "node:path";
2262
2425
 
2263
2426
  // packages/cli-core/src/cloud/auth.ts
2264
- import { randomUUID as randomUUID3 } from "node:crypto";
2427
+ import { randomUUID as randomUUID4 } from "node:crypto";
2265
2428
 
2266
2429
  // packages/cli-core/src/cloud/validation.ts
2267
2430
  function record(value, path = "request") {
@@ -2329,7 +2492,7 @@ var CloudAuthenticator = class {
2329
2492
  this.#adapter = adapter;
2330
2493
  this.#store = store;
2331
2494
  this.#now = options.now ?? Date.now;
2332
- this.#createState = options.createState ?? randomUUID3;
2495
+ this.#createState = options.createState ?? randomUUID4;
2333
2496
  this.#minimumValidityMs = options.minimumValidityMs ?? 3e4;
2334
2497
  if (!Number.isSafeInteger(this.#minimumValidityMs) || this.#minimumValidityMs < 0) {
2335
2498
  throw new TypeError("minimumValidityMs must be a non-negative safe integer");
@@ -2548,8 +2711,8 @@ async function boundedJson(response) {
2548
2711
  function serverError(response, body) {
2549
2712
  const source = typeof body === "object" && body !== null && !Array.isArray(body) ? body : void 0;
2550
2713
  const message = typeof source?.error === "string" && source.error.trim() ? source.error.slice(0, 8 * 1024) : `\u5E73\u53F0\u8BF7\u6C42\u5931\u8D25\uFF08HTTP ${response.status}\uFF09`;
2551
- const code = response.status === 401 ? "AUTH_TOKEN_EXPIRED" : response.status === 404 ? "APP_NOT_FOUND" : response.status >= 500 ? "PLATFORM_UNAVAILABLE" : "INVALID_REQUEST";
2552
- return new CloudLifecycleError(code, message, {
2714
+ const code2 = response.status === 401 ? "AUTH_TOKEN_EXPIRED" : response.status === 404 ? "APP_NOT_FOUND" : response.status >= 500 ? "PLATFORM_UNAVAILABLE" : "INVALID_REQUEST";
2715
+ return new CloudLifecycleError(code2, message, {
2553
2716
  httpStatus: response.status,
2554
2717
  retryable: response.status === 408 || response.status === 429 || response.status >= 500
2555
2718
  });
@@ -2703,7 +2866,7 @@ var HttpDeveloperPlatformClient = class {
2703
2866
  };
2704
2867
 
2705
2868
  // packages/cli-core/src/cloud/browser-login.ts
2706
- import { createHash as createHash2, randomBytes } from "node:crypto";
2869
+ import { createHash as createHash3, randomBytes } from "node:crypto";
2707
2870
  import { spawn } from "node:child_process";
2708
2871
  import { createServer } from "node:http";
2709
2872
 
@@ -2766,7 +2929,7 @@ async function openExternalUrl(url) {
2766
2929
  await new Promise((resolve12, reject) => {
2767
2930
  const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
2768
2931
  child.once("error", reject);
2769
- child.once("exit", (code) => code === 0 ? resolve12() : reject(new Error(`browser launcher exited with ${code ?? "unknown"}`)));
2932
+ child.once("exit", (code2) => code2 === 0 ? resolve12() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
2770
2933
  });
2771
2934
  }
2772
2935
  async function write(res, status, title, message, webUrl, tone = "error") {
@@ -2811,7 +2974,7 @@ var LoopbackBrowserLoginAdapter = class {
2811
2974
  if (request.signal?.aborted) throw request.signal.reason;
2812
2975
  const callbackSecret = randomBytes(24).toString("hex");
2813
2976
  const codeVerifier = randomBytes(32).toString("base64url");
2814
- const codeChallenge = createHash2("sha256").update(codeVerifier).digest("base64url");
2977
+ const codeChallenge = createHash3("sha256").update(codeVerifier).digest("base64url");
2815
2978
  let requestId = "";
2816
2979
  let settle;
2817
2980
  let reject;
@@ -2895,15 +3058,15 @@ var LoopbackBrowserLoginAdapter = class {
2895
3058
  await write(res, 200, "\u5DF2\u53D6\u6D88\u6388\u6743", "\u6CA1\u6709\u5411 buddy-cli \u63D0\u4F9B\u8D26\u53F7\u6743\u9650\uFF0C\u53EF\u4EE5\u5173\u95ED\u8FD9\u4E2A\u9875\u9762\u3002", this.#developerWebUrl, "neutral");
2896
3059
  throw new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u7528\u6237\u53D6\u6D88\u4E86 CLI \u6388\u6743");
2897
3060
  }
2898
- const code = url.searchParams.get("code");
2899
- if (!code || !AUTH_CODE.test(code) || !expected.requestId) {
3061
+ const code2 = url.searchParams.get("code");
3062
+ if (!code2 || !AUTH_CODE.test(code2) || !expected.requestId) {
2900
3063
  await write(res, 400, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u7ED3\u679C\u65E0\u6548\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
2901
3064
  throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6388\u6743\u7801");
2902
3065
  }
2903
3066
  try {
2904
3067
  const exchanged = await this.#post("api/cli/exchange", {
2905
3068
  requestId: expected.requestId,
2906
- code,
3069
+ code: code2,
2907
3070
  codeVerifier: expected.codeVerifier
2908
3071
  });
2909
3072
  if (exchanged.platformOrigin !== this.#platformUrl.origin || typeof exchanged.token !== "string" || typeof exchanged.userId !== "string" || !Number.isSafeInteger(exchanged.expiresAt)) {
@@ -3006,8 +3169,8 @@ async function runCredentialCommand(input) {
3006
3169
  child.stdout.on("data", (chunk) => collect(stdout, chunk));
3007
3170
  child.stderr.on("data", (chunk) => collect(stderr, chunk));
3008
3171
  child.once("error", reject);
3009
- child.once("close", (code) => resolve12({
3010
- code: code ?? 1,
3172
+ child.once("close", (code2) => resolve12({
3173
+ code: code2 ?? 1,
3011
3174
  stdout: Buffer.concat(stdout).toString("utf8"),
3012
3175
  stderr: Buffer.concat(stderr).toString("utf8")
3013
3176
  }));
@@ -3360,8 +3523,8 @@ async function runCloudCommand(command, args, io, options = {}) {
3360
3523
  var CLOUD_COMMAND_HELP = HELP;
3361
3524
 
3362
3525
  // packages/cli-core/src/migration/index.ts
3363
- import { lstat as lstat5, open as open3, readdir as readdir2, realpath as realpath2, stat } from "node:fs/promises";
3364
- import { extname, isAbsolute as isAbsolute2, join as join5, relative, resolve as resolve9, sep, win32 as win322 } from "node:path";
3526
+ import { lstat as lstat6, open as open3, readdir as readdir3, realpath as realpath2, stat } from "node:fs/promises";
3527
+ import { extname, isAbsolute as isAbsolute2, join as join7, relative, resolve as resolve9, sep, win32 as win322 } from "node:path";
3365
3528
  var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
3366
3529
  maxFiles: 128,
3367
3530
  maxFileBytes: 256 * 1024,
@@ -3404,20 +3567,20 @@ var KNOWN_HOST_TOOLS = [
3404
3567
  "WebSearch",
3405
3568
  "Write"
3406
3569
  ];
3407
- function issue(code, path, line, severity, message) {
3408
- return Object.freeze({ code, path, line, severity, message });
3570
+ function issue(code2, path, line, severity, message) {
3571
+ return Object.freeze({ code: code2, path, line, severity, message });
3409
3572
  }
3410
- function rejectScan(code, path, message, cause) {
3573
+ function rejectScan(code2, path, message, cause) {
3411
3574
  throw new SkillPortabilityScanError(
3412
- issue(code, path, 1, "error", message),
3575
+ issue(code2, path, 1, "error", message),
3413
3576
  cause === void 0 ? void 0 : { cause }
3414
3577
  );
3415
3578
  }
3416
- function addFinding(state2, code, path, line, severity, message) {
3417
- const key = `${code}\0${path}\0${line}\0${message}`;
3579
+ function addFinding(state2, code2, path, line, severity, message) {
3580
+ const key = `${code2}\0${path}\0${line}\0${message}`;
3418
3581
  if (state2.issueKeys.has(key)) return;
3419
3582
  state2.issueKeys.add(key);
3420
- state2.issues.push(issue(code, path, line, severity, message));
3583
+ state2.issues.push(issue(code2, path, line, severity, message));
3421
3584
  }
3422
3585
  function compareText(left, right) {
3423
3586
  return left < right ? -1 : left > right ? 1 : 0;
@@ -3530,9 +3693,9 @@ async function existingProjectRoot(value) {
3530
3693
  }
3531
3694
  }
3532
3695
  async function existingSkillRoot(projectRoot, skillPath) {
3533
- const candidate = skillPath === "." ? projectRoot : join5(projectRoot, ...skillPath.split("/"));
3696
+ const candidate = skillPath === "." ? projectRoot : join7(projectRoot, ...skillPath.split("/"));
3534
3697
  try {
3535
- await lstat5(candidate);
3698
+ await lstat6(candidate);
3536
3699
  } catch (cause) {
3537
3700
  if (filesystemCode(cause) === "ENOENT") {
3538
3701
  return rejectScan("SKILL_ROOT_NOT_FOUND", skillPath, "\u627E\u4E0D\u5230\u6307\u5B9A\u7684 Skill \u76EE\u5F55\u3002");
@@ -3567,9 +3730,9 @@ async function existingSkillRoot(projectRoot, skillPath) {
3567
3730
  }
3568
3731
  async function assertEntrypoint(context) {
3569
3732
  const entryPath = reportChild(context.skillPath, "SKILL.md");
3570
- const candidate = join5(context.skillRoot, "SKILL.md");
3733
+ const candidate = join7(context.skillRoot, "SKILL.md");
3571
3734
  try {
3572
- await lstat5(candidate);
3735
+ await lstat6(candidate);
3573
3736
  const target = await realpath2(candidate);
3574
3737
  if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
3575
3738
  rejectScan(
@@ -3634,7 +3797,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
3634
3797
  nextAncestors.add(directoryKey);
3635
3798
  let entries;
3636
3799
  try {
3637
- entries = await readdir2(directory, { withFileTypes: true, encoding: "utf8" });
3800
+ entries = await readdir3(directory, { withFileTypes: true, encoding: "utf8" });
3638
3801
  } catch (cause) {
3639
3802
  return rejectScan("SKILL_SCAN_IO_ERROR", reportDirectory, "\u65E0\u6CD5\u5217\u51FA Skill \u5B50\u76EE\u5F55\u3002", cause);
3640
3803
  }
@@ -3671,7 +3834,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
3671
3834
  }
3672
3835
  for (const entry of entries) {
3673
3836
  const childReportPath = reportChild(reportDirectory, entry.name);
3674
- const childPath = join5(directory, entry.name);
3837
+ const childPath = join7(directory, entry.name);
3675
3838
  let target;
3676
3839
  try {
3677
3840
  target = await realpath2(childPath);
@@ -4125,9 +4288,9 @@ async function scanExternalSkillPortability(options) {
4125
4288
  }
4126
4289
 
4127
4290
  // packages/cli-core/src/push/preflight.ts
4128
- import { createHash as createHash3 } from "node:crypto";
4129
- import { lstat as lstat6, open as open4, readdir as readdir3, realpath as realpath3 } from "node:fs/promises";
4130
- import { isAbsolute as isAbsolute3, join as join6, relative as relative2, resolve as resolve10, sep as sep2, win32 as win323 } from "node:path";
4291
+ import { createHash as createHash4 } from "node:crypto";
4292
+ import { lstat as lstat7, open as open4, readdir as readdir4, realpath as realpath3 } from "node:fs/promises";
4293
+ import { isAbsolute as isAbsolute3, join as join8, relative as relative2, resolve as resolve10, sep as sep2, win32 as win323 } from "node:path";
4131
4294
 
4132
4295
  // packages/cli-core/src/push/errors.ts
4133
4296
  var PushPreflightError = class extends Error {
@@ -4225,7 +4388,7 @@ function error(options) {
4225
4388
  throw new PushPreflightError(options);
4226
4389
  }
4227
4390
  function sha256(value) {
4228
- return `sha256:${createHash3("sha256").update(value).digest("hex")}`;
4391
+ return `sha256:${createHash4("sha256").update(value).digest("hex")}`;
4229
4392
  }
4230
4393
  function positiveLimit(value, name, hardMaximum) {
4231
4394
  if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
@@ -4318,7 +4481,7 @@ async function canonicalProjectRoot(projectRoot) {
4318
4481
  const absolute = resolve10(projectRoot);
4319
4482
  let entry;
4320
4483
  try {
4321
- entry = await lstat6(absolute);
4484
+ entry = await lstat7(absolute);
4322
4485
  } catch (cause) {
4323
4486
  error({
4324
4487
  code: "PUSH_PROJECT_INVALID",
@@ -4346,10 +4509,10 @@ async function canonicalProjectRoot(projectRoot) {
4346
4509
  }
4347
4510
  }
4348
4511
  async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
4349
- const path = join6(projectRoot, filename);
4512
+ const path = join8(projectRoot, filename);
4350
4513
  let entry;
4351
4514
  try {
4352
- entry = await lstat6(path);
4515
+ entry = await lstat7(path);
4353
4516
  } catch (cause) {
4354
4517
  error({
4355
4518
  code: "PUSH_CONFIG_INVALID",
@@ -4416,7 +4579,7 @@ function parseCapturedProject(agentFile) {
4416
4579
  async function selectLockfile(projectRoot) {
4417
4580
  let names;
4418
4581
  try {
4419
- names = await readdir3(projectRoot);
4582
+ names = await readdir4(projectRoot);
4420
4583
  } catch (cause) {
4421
4584
  error({
4422
4585
  code: "PUSH_PROJECT_INVALID",
@@ -4471,7 +4634,7 @@ async function hashRegularFile(options) {
4471
4634
  message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
4472
4635
  });
4473
4636
  }
4474
- const hash = createHash3("sha256");
4637
+ const hash = createHash4("sha256");
4475
4638
  const captured = [];
4476
4639
  const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
4477
4640
  let bytes = 0;
@@ -4525,7 +4688,7 @@ async function createManifest(options) {
4525
4688
  }
4526
4689
  let names;
4527
4690
  try {
4528
- names = await readdir3(directory);
4691
+ names = await readdir4(directory);
4529
4692
  } catch (cause) {
4530
4693
  error({
4531
4694
  code: "PUSH_PROJECT_INVALID",
@@ -4549,10 +4712,10 @@ async function createManifest(options) {
4549
4712
  sensitiveEntriesExcluded += 1;
4550
4713
  continue;
4551
4714
  }
4552
- const absolutePath = join6(directory, name);
4715
+ const absolutePath = join8(directory, name);
4553
4716
  let entry;
4554
4717
  try {
4555
- entry = await lstat6(absolutePath);
4718
+ entry = await lstat7(absolutePath);
4556
4719
  } catch (cause) {
4557
4720
  error({
4558
4721
  code: "PUSH_FILE_CHANGED",
@@ -4739,8 +4902,8 @@ function bundleDigest(files) {
4739
4902
  });
4740
4903
  return sha256(`buddy-push-bundle-v1\0${descriptor}`);
4741
4904
  }
4742
- function warning(code, message) {
4743
- return Object.freeze({ code, message });
4905
+ function warning(code2, message) {
4906
+ return Object.freeze({ code: code2, message });
4744
4907
  }
4745
4908
  function assertOfficialProjectIncluded(files) {
4746
4909
  for (const path of OFFICIAL_RUNTIME_INPUTS) {
@@ -4801,8 +4964,8 @@ async function assertCapturedConfigStable(options) {
4801
4964
  }
4802
4965
  let current;
4803
4966
  try {
4804
- const path = join6(options.projectRoot, options.filename);
4805
- const entry = await lstat6(path);
4967
+ const path = join8(options.projectRoot, options.filename);
4968
+ const entry = await lstat7(path);
4806
4969
  if (!entry.isFile() || entry.isSymbolicLink()) {
4807
4970
  error({
4808
4971
  code: "PUSH_FILE_CHANGED",
@@ -4844,7 +5007,7 @@ async function createPushPreflight(options) {
4844
5007
  limits.maxFileBytes,
4845
5008
  BUDDY_AGENT_FILENAME
4846
5009
  );
4847
- const initialConfig = parseCapturedProject(initialAgentEntry);
5010
+ parseCapturedProject(initialAgentEntry);
4848
5011
  const lockfile = await selectLockfile(projectRoot);
4849
5012
  assertBookletIsNotRequiredInput(bookletPath, lockfile);
4850
5013
  const manifest = await createManifest({
@@ -4902,7 +5065,7 @@ async function createPushPreflight(options) {
4902
5065
  schemaVersion: "1",
4903
5066
  assurance: "preflight-only",
4904
5067
  projectRoot,
4905
- runtimeLanguage: initialConfig.runtime.language,
5068
+ runtimeLanguage: "node",
4906
5069
  ...bookletPath === void 0 ? {} : { bookletPath },
4907
5070
  lockfile,
4908
5071
  limits,
@@ -4916,16 +5079,16 @@ async function createPushPreflight(options) {
4916
5079
  }
4917
5080
 
4918
5081
  // packages/cli-core/src/push/bundle.ts
4919
- import { createHash as createHash4 } from "node:crypto";
4920
- import { lstat as lstat7, readFile as readFile5, realpath as realpath4 } from "node:fs/promises";
4921
- import { join as join7, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
5082
+ import { createHash as createHash5 } from "node:crypto";
5083
+ import { lstat as lstat8, readFile as readFile6, realpath as realpath4 } from "node:fs/promises";
5084
+ import { join as join9, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
4922
5085
  import { Writable } from "node:stream";
4923
5086
  import { pipeline } from "node:stream/promises";
4924
5087
  import { createGzip } from "node:zlib";
4925
5088
  import { pack } from "tar-stream";
4926
5089
  var MEDIA_TYPE = "application/gzip";
4927
5090
  function digest(bytes) {
4928
- return `sha256:${createHash4("sha256").update(bytes).digest("hex")}`;
5091
+ return `sha256:${createHash5("sha256").update(bytes).digest("hex")}`;
4929
5092
  }
4930
5093
  function changed(path, message, cause) {
4931
5094
  throw new PushPreflightError({
@@ -4940,15 +5103,15 @@ function inside(root, target) {
4940
5103
  return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
4941
5104
  }
4942
5105
  async function capture(root, file) {
4943
- const absolute = join7(root, ...file.path.split("/"));
5106
+ const absolute = join9(root, ...file.path.split("/"));
4944
5107
  try {
4945
- const entry = await lstat7(absolute);
5108
+ const entry = await lstat8(absolute);
4946
5109
  if (!entry.isFile() || entry.isSymbolicLink() || entry.size !== file.bytes) {
4947
5110
  changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
4948
5111
  }
4949
5112
  const canonical = await realpath4(absolute);
4950
5113
  if (!inside(root, canonical)) changed(file.path, `\u63D0\u4EA4\u6587\u4EF6\u8D8A\u8FC7\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${file.path}`);
4951
- const contents = await readFile5(absolute);
5114
+ const contents = await readFile6(absolute);
4952
5115
  if (contents.byteLength !== file.bytes || digest(contents) !== file.digest) {
4953
5116
  changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
4954
5117
  }
@@ -4974,7 +5137,7 @@ async function createPushSourceBundle(plan) {
4974
5137
  schemaVersion: "1",
4975
5138
  files: files.map((file) => ({ path: file.path, bytes: file.bytes, digest: file.digest }))
4976
5139
  });
4977
- const calculatedBundleDigest = `sha256:${createHash4("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
5140
+ const calculatedBundleDigest = `sha256:${createHash5("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
4978
5141
  if (calculatedBundleDigest !== plan.bundleDigest) {
4979
5142
  changed(plan.projectRoot, "\u63D0\u4EA4\u6E05\u5355\u8EAB\u4EFD\u5728\u6253\u5305\u524D\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C push");
4980
5143
  }
@@ -5370,7 +5533,7 @@ async function resumeProjectArguments(args, cwd, options) {
5370
5533
  await assertInitializableBuddyTarget(root);
5371
5534
  let entry;
5372
5535
  try {
5373
- entry = await lstat8(join8(root, "buddy.agent.json"));
5536
+ entry = await lstat9(join10(root, "buddy.agent.json"));
5374
5537
  } catch (cause) {
5375
5538
  if (cause.code === "ENOENT") return;
5376
5539
  throw cause;