@my-life-buddies/cli 0.17.7 → 0.18.1

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
@@ -450,6 +450,9 @@ function alive(pid) {
450
450
  return error2.code !== "ESRCH";
451
451
  }
452
452
  }
453
+ function runtimeAlive(pid) {
454
+ return alive(pid) || alive(-pid);
455
+ }
453
456
  function validPid(pid) {
454
457
  return Number.isSafeInteger(pid) && pid > 0;
455
458
  }
@@ -462,7 +465,7 @@ async function acquireProcessLock(options) {
462
465
  const path = join4(root, key);
463
466
  const ownerName = `owner-${randomUUID()}.json`;
464
467
  const candidate = join4(root, `.pending-${randomUUID()}`);
465
- const owner = { pid: process.pid };
468
+ const owner = { ...options.ownerData, pid: process.pid };
466
469
  await mkdir3(root, { recursive: true, mode: 448 });
467
470
  await mkdir3(candidate, { mode: 448 });
468
471
  await writeFile3(join4(candidate, ownerName), JSON.stringify(owner), { mode: 384, flag: "wx" });
@@ -492,7 +495,7 @@ async function acquireProcessLock(options) {
492
495
  if (code(error2) === "ENOENT") continue;
493
496
  throw options.busyError();
494
497
  }
495
- if (!validPid(previous.pid) || previous.runtimePid !== void 0 && !validPid(previous.runtimePid) || alive(previous.pid) || previous.runtimePid !== void 0 && alive(previous.runtimePid)) throw options.busyError();
498
+ if (!validPid(previous.pid) || previous.runtimePid !== void 0 && !validPid(previous.runtimePid) || alive(previous.pid) || previous.runtimePid !== void 0 && runtimeAlive(previous.runtimePid)) throw options.busyError();
496
499
  try {
497
500
  await unlink(previousPath);
498
501
  } catch (error2) {
@@ -513,7 +516,7 @@ async function acquireProcessLock(options) {
513
516
  if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
514
517
  const temporary = join4(root, `.update-${randomUUID()}`);
515
518
  try {
516
- await writeFile3(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
519
+ await writeFile3(temporary, JSON.stringify({ ...owner, ...await options.runtimeData?.(pid), runtimePid: pid }), { mode: 384, flag: "wx" });
517
520
  await rename3(temporary, join4(path, ownerName));
518
521
  } finally {
519
522
  await unlink(temporary).catch(() => void 0);
@@ -879,8 +882,9 @@ const model = ${JSON.stringify(model)};
879
882
  * Import tools from ./tools/ and hooks from ./hooks/, then register them below.
880
883
  * Shared business code can live in ./services/; directories do not enable capabilities.
881
884
  * Use conversation.memory / dataAccess / widget / resource / proactive for platform capabilities.
882
- * Add conversation.tools.data_access_query / data_access_request / data_access_read_result
883
- * to initialState.tools for model-driven data access; declarations come from data-access.json.requirements.
885
+ * Add conversation.tools.data_access_query / data_access_request to initialState.tools.
886
+ * Declare every required dataset, including device.location / device.calendar, in data-access.json.requirements.
887
+ * Query first; if notGranted, request({ datasets, reason }), wait for the user, then query again.
884
888
  * Add conversation.tools.ask_question for multiple-choice questions.
885
889
  * Add conversation.tools.resource_list / resource_read to discover and read reference material.
886
890
  * Place widget assets in ../widgets/<type-id>/index.html and schema.json.
@@ -1775,7 +1779,8 @@ import { lstat as lstat14 } from "node:fs/promises";
1775
1779
  import { resolve as resolve7 } from "node:path";
1776
1780
  var DATASETS_COMMAND_HELP = `\u7528\u6CD5: buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
1777
1781
  \u59CB\u7EC8\u8054\u7F51\u67E5\u8BE2\u516C\u5171 npm \u6700\u65B0 Runtime\uFF0C\u8BFB\u53D6\u5E76\u7F13\u5B58\u5176 DATA_ACCESS_CAPABILITIES\u3002
1778
- \u65E0\u9700\u767B\u5F55\u642D\u642D\u8D26\u53F7\u3002\u5C06\u6240\u9009 id \u4E0E\u7528\u9014 purpose \u5199\u5165 data-access.json.requirements\uFF1B\u76EE\u5F55\u4E0D\u4EE3\u8868\u7528\u6237\u5DF2\u6388\u6743\u3002`;
1782
+ \u65E0\u9700\u767B\u5F55\u642D\u642D\u8D26\u53F7\u3002\u5C06\u6240\u9009 id \u4E0E\u7528\u9014 purpose \u5199\u5165 data-access.json.requirements\uFF1B\u4F4D\u7F6E device.location \u548C\u65E5\u5386 device.calendar \u4E5F\u9700\u8981\u58F0\u660E\u3002
1783
+ \u76EE\u5F55\u4E0D\u4EE3\u8868\u7528\u6237\u5DF2\u6388\u6743\u3002\u4F7F\u7528 data_access_query \u67E5\u8BE2\uFF0C\u672A\u6388\u6743\u65F6\u4EE5 datasets\u3001reason \u8BF7\u6C42\u6388\u6743\uFF0C\u7528\u6237\u5904\u7406\u540E\u518D\u6B21\u67E5\u8BE2\u3002`;
1779
1784
  async function runDatasetsCommand(args, io, cwd, options = {}) {
1780
1785
  try {
1781
1786
  if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
@@ -2119,7 +2124,7 @@ ${KNOWLEDGE_HELP}`);
2119
2124
  }
2120
2125
 
2121
2126
  // packages/cli-core/src/index.ts
2122
- import { join as join21, resolve as resolve18 } from "node:path";
2127
+ import { join as join22, resolve as resolve18 } from "node:path";
2123
2128
 
2124
2129
  // packages/cli-core/src/init/creator.ts
2125
2130
  import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypto";
@@ -2600,6 +2605,10 @@ Creator \u8DEF\u7EBF\u987B\u7B49\u56DB\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355
2600
2605
 
2601
2606
  ${KNOWLEDGE_GUIDE}
2602
2607
 
2608
+ ### \u672C\u5730\u8C03\u8BD5\u7684\u9000\u51FA\u548C\u6E05\u7406
2609
+
2610
+ \u901A\u8FC7 \`buddy-cli preview\` \u8FDE\u63A5\u540E\u53F0\uFF0C\u9875\u9762\u542F\u52A8 DEV \u540E\u624D\u8FD0\u884C Runtime\u3002\u540E\u53F0\u590D\u5236\u7684\u6307\u4EE4\u5E26 \`--no-open\`\uFF0C\u6267\u884C\u540E\u8FD4\u56DE\u539F\u9875\u9762\uFF1B\u4E0D\u8981\u518D\u5F00\u4E00\u4E2A\u9884\u89C8\u8FDB\u7A0B\u3002\u6B63\u5E38\u505C\u6B62\u7528\u9875\u9762\u300C\u505C\u6B62 DEV\u300D\u6216 \`buddy-cli dev stop\`\uFF0C\u540E\u8005\u65E0\u9700\u8054\u7F51\u66F4\u65B0\u6216\u767B\u5F55\uFF0C\u53EA\u505C\u6B62\u5F53\u524D\u5E73\u53F0\u3001\u5F53\u524D\u642D\u5B50\u7684\u672C\u5730 Runtime \u548C\u76D1\u542C\uFF0C\u4E0D\u81EA\u52A8\u91CD\u8FDE\u6216\u91CD\u542F\u3002\u5173\u95ED\u7EC8\u7AEF\u3001Ctrl+C \u6216 SIGTERM \u4F1A\u505C\u6B62 CLI \u7BA1\u7406\u7684 Runtime \u5E76\u65AD\u5F00\u3002\u82E5\u6B8B\u7559\u5360\u7528\uFF0C\u540E\u53F0\u53EF\u300C\u5F3A\u5236\u65AD\u8FDE\u300D\uFF1B\u9875\u9762\u672A\u786E\u8BA4\u672C\u673A\u505C\u6B62\u65F6\uFF0C\u5728\u539F\u7535\u8111\u3001\u539F\u5DE5\u7A0B\u76EE\u5F55\u6267\u884C\u5176\u505C\u6B62\u6307\u4EE4\u3002\u4E0D\u8981\u624B\u5DE5\u5220\u9501\u6587\u4EF6\uFF0C\u4E0D\u8981\u6279\u91CF\u6740\u6B7B node\uFF1B\u8EAB\u4EFD\u6821\u9A8C\u5931\u8D25\u65F6\u5148\u6838\u5BF9\u5DE5\u7A0B\u548C\u539F\u8FDB\u7A0B\u3002\u8FD9\u4E9B\u64CD\u4F5C\u4E0D\u4F1A\u63D0\u5BA1\u6216\u53D1\u5E03\u3002
2611
+
2603
2612
  ### \u6309\u9700\u6C42\u68C0\u67E5\u9700\u8981\u54EA\u4E9B\u80FD\u529B
2604
2613
 
2605
2614
  \u4E0B\u9762\u662F Coding Agent \u7684\u5B9E\u73B0\u68C0\u67E5\u8868\uFF0C\u4E0D\u662F\u4EA4\u7ED9\u5F00\u53D1\u8005\u9010\u9879\u9009\u62E9\u7684\u95EE\u5377\u3002\u53EA\u5B9E\u73B0\u9700\u6C42\u7528\u5F97\u4E0A\u7684\u80FD\u529B\uFF1B\u5DF2\u6709\u5B9E\u73B0\u6216\u9ED8\u8BA4\u80FD\u529B\u6EE1\u8DB3\u65F6\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u4E3A\u4E86\u51D1\u9F50\u7C7B\u522B\u751F\u6210 Tool\u3002\u4E1A\u52A1\u80FD\u529B\u901A\u8FC7 \`agent.md\`\u3001\`src/buddy-options.mjs\` \u548C\u5B98\u65B9 Runtime \u7684\u6269\u5C55\u70B9\u5B9E\u73B0\u3002
@@ -2649,9 +2658,11 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
2649
2658
 
2650
2659
  \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 npm \u6700\u65B0 Runtime \u5BFC\u51FA\u8BFB\u53D6\u5168\u90E8\u6570\u636E\u96C6\uFF1B\u4E0D\u8981\u6C42\u767B\u5F55\uFF0C\u4E0D\u4ECE Server \u62C9\u76EE\u5F55\u3002CLI \u6BCF\u6B21\u67E5\u8BE2 latest\uFF0C\u540C\u7248\u672C\u590D\u7528\u7F13\u5B58\uFF0C\u4E0D\u590D\u5236\u679A\u4E3E\u3002\u76EE\u5F55\u547D\u4EE4\u4F1A\u63D0\u793A\u5DE5\u7A0B\u7248\u672C\u5DEE\u5F02\uFF1B\u4F7F\u7528\u65B0\u7248\u80FD\u529B\u524D\uFF0C\u7531\u4F60\u6216\u5F00\u53D1 Agent \u6309\u63D0\u793A\u5347\u7EA7\u4F9D\u8D56\uFF0C\u68C0\u67E5\u542F\u52A8\u53C2\u6570\u3001\u6A21\u578B\u3001datasets\u3001\u5DE5\u5177\u53CA Hook \u7684\u9002\u914D\u5E76\u9A8C\u8BC1 DEV\u3002CLI \u4E0D\u81EA\u52A8\u4FEE\u6539\u5DE5\u7A0B\u4F9D\u8D56\u6216\u914D\u7F6E\u3002\u628A\u5B9E\u9645\u9700\u8981\u7684 \`id\` \u548C\u7528\u9014 \`purpose\` \u5199\u5165 \`data-access.json.requirements\`\uFF0C\u6700\u591A 32 \u9879\uFF0C\u540C\u4E00 ID \u53EA\u5199\u4E00\u6B21\uFF0CID \u548C\u7528\u9014\u53BB\u9664\u9996\u5C3E\u7A7A\u767D\uFF0C\u7528\u9014\u4E3A 1\uFF5E200 \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
2651
2660
 
2652
- Runtime \u542F\u52A8\u65F6\u8BFB\u53D6\u6839\u76EE\u5F55 \`data-access.json\`\uFF0C\u901A\u8FC7 \`PUT /internal/v1/data-access/requirements\` \u767B\u8BB0\uFF0C\u6210\u529F\u540E\u518D\u8FDE\u63A5\u4E8B\u4EF6\u6D41\uFF1B\u767B\u8BB0\u5931\u8D25\u963B\u6B62\u542F\u52A8\u3002\u6587\u4EF6\u7F3A\u5931\u6216\u7A7A\u6570\u7EC4\u90FD\u4F1A\u4E0A\u4F20\u7A7A\u58F0\u660E\u5E76\u6E05\u9664\u65E7\u58F0\u660E\u3002DEV \u4FDD\u5B58\u8BE5\u6587\u4EF6\u540E\u81EA\u52A8\u91CD\u542F\u5E76\u91CD\u65B0\u767B\u8BB0\u3002\u58F0\u660E\u4E0D\u8868\u793A\u7528\u6237\u5DF2\u6388\u6743\uFF1B\u4F4D\u7F6E\u548C\u65E5\u5386\u4E0D\u653E\u5165\u6B64\u6587\u4EF6\u3002
2661
+ Runtime \u542F\u52A8\u65F6\u8BFB\u53D6\u6839\u76EE\u5F55 \`data-access.json\`\uFF0C\u901A\u8FC7 \`PUT /internal/v1/data-access/requirements\` \u767B\u8BB0\uFF0C\u6210\u529F\u540E\u518D\u8FDE\u63A5\u4E8B\u4EF6\u6D41\uFF1B\u767B\u8BB0\u5931\u8D25\u963B\u6B62\u542F\u52A8\u3002\u6587\u4EF6\u7F3A\u5931\u6216\u7A7A\u6570\u7EC4\u90FD\u4F1A\u4E0A\u4F20\u7A7A\u58F0\u660E\u5E76\u6E05\u9664\u65E7\u58F0\u660E\u3002DEV \u4FDD\u5B58\u8BE5\u6587\u4EF6\u540E\u81EA\u52A8\u91CD\u542F\u5E76\u91CD\u65B0\u767B\u8BB0\u3002\u58F0\u660E\u4E0D\u8868\u793A\u7528\u6237\u5DF2\u6388\u6743\uFF1B\u6240\u6709\u9700\u8981\u7684\u6570\u636E\u90FD\u8981\u58F0\u660E\uFF0C\u5305\u62EC\u4F4D\u7F6E \`device.location\` \u548C\u65E5\u5386 \`device.calendar\`\u3002
2653
2662
 
2654
- \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\u67E5\u8BE2\u6216\u9501\u6587\u4EF6\u751F\u6210\u5931\u8D25\u65F6\u505C\u6B62\uFF0C\u4E0D\u56DE\u9000\u5185\u7F6E\u65E7\u7248\u672C\u3002\u5DF2\u6709\u5DE5\u7A0B\u901A\u8FC7\u5305\u7BA1\u7406\u5668\u663E\u5F0F\u5347\u7EA7\u4F9D\u8D56\u3002CLI \u7684 DEV \u542F\u52A8\u3001\u81EA\u52A8\u91CD\u8F7D\u548C push \u5FC5\u987B\u4F7F\u7528\u672C\u6B21 npm \u6700\u65B0\u7A33\u5B9A\u7248\uFF0C\u58F0\u660E\u3001\u5B89\u88C5\u548C npm \u9501\u6587\u4EF6\u7248\u672C\u5FC5\u987B\u4E00\u81F4\uFF0C\u67E5\u8BE2\u5931\u8D25\u4F1A\u963B\u6B62\u64CD\u4F5C\u3002
2663
+ \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\`\u3002\u5065\u5EB7\u3001\u4F4D\u7F6E\u3001\u65E5\u5386\u7EDF\u4E00\u5148 query\uFF1B\u8FD4\u56DE \`notGranted\` \u65F6\uFF0C\u7528 \`data_access_request({ datasets, reason })\` \u7533\u8BF7\u5DF2\u58F0\u660E\u7684 1\uFF5E8 \u9879\u6570\u636E\uFF0C\u7B49\u5F85\u7528\u6237\u5904\u7406\u540E\u518D\u6B21 query\u3002\u4E1A\u52A1\u4EE3\u7801\u5BF9\u5E94\u4F7F\u7528 \`conversation.dataAccess.query({ datasets, memberId? })\` \u4E0E \`conversation.dataAccess.request({ datasets, reason, idempotencyKey })\`\u3002\u67E5\u8BE2\u53EA\u6709 \`available\` \u643A\u5E26\u6570\u636E\uFF1B\`notDeclared\` \u8981\u4FEE\u6B63\u5DE5\u7A0B\u58F0\u660E\uFF0C\`systemPermissionDenied\` \u8868\u793A\u7CFB\u7EDF\u6743\u9650\u5173\u95ED\uFF0C\u5E94\u5F15\u5BFC\u7528\u6237\u5728 App \u6216\u7CFB\u7EDF\u8BBE\u7F6E\u4E2D\u68C0\u67E5\uFF1B\`unavailable\` \u8868\u793A\u6682\u65E0\u53EF\u7528\u7ED3\u679C\uFF0C\u4E0D\u7B49\u4E8E\u7528\u6237\u62D2\u7EDD\uFF0C\u4E5F\u4E0D\u8981\u53CD\u590D\u53D1\u6388\u6743\u5361\u3002\u4F4D\u7F6E\u3001\u65E5\u5386\u5FEB\u7167\u8FC7\u671F\u540E\u7531\u5E73\u53F0\u5C1D\u8BD5\u8BF7\u6C42\u5728\u7EBF App \u5237\u65B0\uFF0CCLI \u4E0D\u91C7\u96C6\u8BBE\u5907\u6570\u636E\u3002
2664
+
2665
+ \u65B0\u5EFA\u5DE5\u7A0B\u52A8\u6001\u9009\u62E9\u516C\u5171 npm latest \u5E76\u9501\u5B9A\u51C6\u786E\u7248\u672C\uFF1B\u67E5\u8BE2\u6216\u9501\u6587\u4EF6\u751F\u6210\u5931\u8D25\u65F6\u505C\u6B62\uFF0C\u4E0D\u56DE\u9000\u5185\u7F6E\u65E7\u7248\u672C\u3002\u5DF2\u6709\u5DE5\u7A0B\u901A\u8FC7\u5305\u7BA1\u7406\u5668\u663E\u5F0F\u5347\u7EA7\u4F9D\u8D56\u3002CLI \u7684 DEV \u542F\u52A8\u3001\u81EA\u52A8\u91CD\u8F7D\u548C push \u5FC5\u987B\u4F7F\u7528\u672C\u6B21 npm \u6700\u65B0\u7A33\u5B9A\u7248\uFF0C\u58F0\u660E\u3001\u5B89\u88C5\u548C npm \u9501\u6587\u4EF6\u7248\u672C\u5FC5\u987B\u4E00\u81F4\uFF0C\u67E5\u8BE2\u5931\u8D25\u4F1A\u963B\u6B62\u64CD\u4F5C\u3002
2655
2666
 
2656
2667
  \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
2657
2668
 
@@ -2956,7 +2967,7 @@ var CLI_HELP = `\u642D\u642D Developer Platform CLI
2956
2967
  models \u67E5\u770B\u6700\u65B0 Runtime \u7684\u6A21\u578B\u76EE\u5F55
2957
2968
  datasets \u67E5\u770B\u6700\u65B0 Runtime \u7684\u6570\u636E\u96C6\u76EE\u5F55
2958
2969
  knowledge \u5C06\u521B\u4F5C\u8D44\u6599\u540C\u6B65\u4E3A\u672C\u5730\u8FD0\u884C\u77E5\u8BC6
2959
- dev \u5728\u7EC8\u7AEF\u542F\u52A8\u5F00\u53D1\u7248\u642D\u5B50
2970
+ dev \u5728\u7EC8\u7AEF\u542F\u52A8\u5F00\u53D1\u7248\u642D\u5B50\uFF1Bdev stop \u505C\u6B62\u672C\u673A DEV
2960
2971
  preview \u8FDE\u63A5\u5DE5\u7A0B\u5E76\u6253\u5F00\u540E\u53F0\u5F00\u53D1\u8C03\u8BD5
2961
2972
  push \u4E0A\u4F20\u7A0B\u5E8F\u5305\u548C\u6539\u52A8\u8BF4\u660E
2962
2973
  status \u67E5\u770B\u4E91\u7AEF\u7248\u672C\u3001\u8FD0\u884C\u548C\u5BA1\u6838\u72B6\u6001
@@ -2971,7 +2982,7 @@ var CLI_HELP = `\u642D\u642D Developer Platform CLI
2971
2982
  \u76F4\u63A5\u8FD0\u884C buddy-cli \u53EF\u8BC6\u522B\u5F53\u524D\u5DE5\u7A0B\u6216\u5F15\u5BFC\u521B\u5EFA\u3002
2972
2983
  \u9ED8\u8BA4\u5E73\u53F0\uFF1A${DEFAULT_MLB_TEST_URL}\uFF1B\u5F00\u53D1\u8005\u540E\u53F0\uFF1A${DEFAULT_DEVELOPER_WEB_URL}\u3002
2973
2984
  \u4ECE\u6D77\u5916\u56DE\u8FC1\u963F\u91CC\u4E91\u540E\u91CD\u65B0 login\uFF1B\u65E7\u5DE5\u7A0B\u7528 dev --refresh-token \u6216 preview --refresh-token \u66F4\u65B0\u5F00\u53D1\u51ED\u636E\u548C .env \u5730\u5740\u3002
2974
- \u6B63\u5E38\u547D\u4EE4\u5148\u8054\u7F51\u81EA\u68C0\uFF0C\u65E7\u7248\u81EA\u52A8\u66F4\u65B0\u540E\u7EE7\u7EED\u6267\u884C\uFF1B\u5931\u8D25\u65F6\u505C\u6B62\u3002\u5E2E\u52A9\u548C --version \u79BB\u7EBF\u53EF\u7528\u3002
2985
+ \u6B63\u5E38\u547D\u4EE4\u5148\u8054\u7F51\u81EA\u68C0\uFF0C\u65E7\u7248\u81EA\u52A8\u66F4\u65B0\u540E\u7EE7\u7EED\u6267\u884C\uFF1B\u5931\u8D25\u65F6\u505C\u6B62\u3002\u5E2E\u52A9\u3001--version \u548C dev stop \u79BB\u7EBF\u53EF\u7528\u3002
2975
2986
  \u53C2\u6570\u4E0E\u793A\u4F8B: buddy-cli <\u547D\u4EE4> --help\uFF0C\u4F8B\u5982 buddy-cli init --help\u3002
2976
2987
  Coding Agent \u5F00\u59CB\u5DE5\u4F5C\u524D\u8BF7\u8BFB\u53D6 buddy-cli guide\u3002`;
2977
2988
  var INIT_HELP = `\u7528\u6CD5: buddy-cli init [\u53C2\u6570]
@@ -3019,7 +3030,7 @@ Skill \u7684\u5177\u4F53\u6D41\u7A0B\u8BFB\u53D6 HANDOFF.md \u4E2D\u7684\u6587\u
3019
3030
 
3020
3031
  \u4F7F\u7528\u5B98\u65B9 buddy-cli \u5165\u53E3\u3002\u6BCF\u6761\u6B63\u5E38\u547D\u4EE4\uFF08\u5305\u62EC guide\uFF09\u6267\u884C\u524D\u90FD\u4F1A\u67E5\u8BE2\u516C\u5171 npm latest\uFF1B\u7248\u672C\u8FC7\u65E7\u65F6\u81EA\u52A8\u66F4\u65B0\uFF0C\u518D\u7531\u65B0\u7248\u6267\u884C\u540C\u4E00\u547D\u4EE4\u3002\u7B49\u5F85\u5F53\u524D\u547D\u4EE4\u5B8C\u6210\uFF0C\u4E0D\u91CD\u590D\u53D1\u8D77\uFF0C\u4E0D\u53E6\u5F00\u8FDB\u7A0B\u540C\u65F6\u91CD\u8DD1\u3002
3021
3032
  \u5168\u5C40\u5B89\u88C5\u66F4\u65B0\u5BF9\u5E94 npm \u73AF\u5883\u7684\u5B89\u88C5\uFF1B\u6E90\u7801\u3001npm link\u3001\u9879\u76EE\u5185\u5B89\u88C5\u548C npx \u901A\u8FC7\u7F13\u5B58\u7684\u65B0\u7248\u7EE7\u7EED\u6267\u884C\uFF0C\u4E0D\u6539\u5199\u6E90\u7801\u6216\u9879\u76EE\u4F9D\u8D56\u3002\u53C2\u6570\u3001\u5DE5\u4F5C\u76EE\u5F55\u3001\u7EC8\u7AEF\u8F93\u5165\u8F93\u51FA\u548C\u9000\u51FA\u72B6\u6001\u4FDD\u7559\u3002\u4E00\u6B21\u8C03\u7528\u6700\u591A\u81EA\u52A8\u5207\u6362\u4E00\u6B21\uFF1B\u5F53\u524D\u7248\u672C\u9AD8\u4E8E latest \u65F6\u4E0D\u964D\u7EA7\uFF0C\u5DF2\u8FD0\u884C\u7684 DEV \u4E0D\u4F1A\u56E0\u53D1\u5E03\u65B0 CLI \u81EA\u52A8\u91CD\u542F\u3002
3022
- \u5E2E\u52A9\u3001\u5404\u547D\u4EE4 --help \u548C --version \u79BB\u7EBF\u53EF\u7528\uFF1Bupdate \u662F\u5355\u72EC\u68C0\u67E5\u6216\u66F4\u65B0\u5165\u53E3\u3002\u67E5\u8BE2\u6216\u66F4\u65B0\u5931\u8D25\u65F6\u539F\u4E1A\u52A1\u64CD\u4F5C\u4E0D\u4F1A\u5F00\u59CB\uFF0C\u6839\u636E\u9519\u8BEF\u5904\u7406\u540E\u91CD\u8BD5\uFF0C\u4E0D\u8C03\u7528\u65E7\u7248\u6E90\u7801\u7ED5\u8FC7\u68C0\u67E5\u3002\u65E7 CLI \u9700\u5148\u901A\u8FC7\u5B98\u65B9 npm \u5B89\u88C5\u547D\u4EE4\u5347\u7EA7\u4E00\u6B21\uFF0C\u624D\u80FD\u83B7\u5F97\u6B64\u673A\u5236\u3002
3033
+ \u5E2E\u52A9\u3001\u5404\u547D\u4EE4 --help\u3001--version \u548C\u5E94\u6025\u6E05\u7406 dev stop \u79BB\u7EBF\u53EF\u7528\uFF1Bupdate \u662F\u5355\u72EC\u68C0\u67E5\u6216\u66F4\u65B0\u5165\u53E3\u3002\u67E5\u8BE2\u6216\u66F4\u65B0\u5931\u8D25\u65F6\u539F\u4E1A\u52A1\u64CD\u4F5C\u4E0D\u4F1A\u5F00\u59CB\uFF0C\u6839\u636E\u9519\u8BEF\u5904\u7406\u540E\u91CD\u8BD5\uFF0C\u4E0D\u8C03\u7528\u65E7\u7248\u6E90\u7801\u7ED5\u8FC7\u68C0\u67E5\u3002\u65E7 CLI \u9700\u5148\u901A\u8FC7\u5B98\u65B9 npm \u5B89\u88C5\u547D\u4EE4\u5347\u7EA7\u4E00\u6B21\uFF0C\u624D\u80FD\u83B7\u5F97\u6B64\u673A\u5236\u3002
3023
3034
  CLI \u81EA\u52A8\u66F4\u65B0\u4E0D\u7B49\u4E8E\u5DE5\u7A0B Runtime \u5DF2\u9002\u914D\u3002\u4ECD\u987B\u6839\u636E Runtime \u68C0\u67E5\u7ED3\u679C\u66F4\u65B0\u5DE5\u7A0B\u4F9D\u8D56\u3001\u9501\u6587\u4EF6\u548C\u5FC5\u8981\u7684\u4E1A\u52A1\u4EE3\u7801\uFF0C\u5B8C\u6210\u9A8C\u8BC1\u540E\u518D\u542F\u52A8\u6216\u4E0A\u4F20\uFF1B\u4E0D\u8981\u53EA\u4FEE\u6539\u7248\u672C\u53F7\u5C31\u5BA3\u79F0\u9002\u914D\u5B8C\u6210\u3002
3024
3035
  \u901A\u7528\u89C4\u5219\u4EE5\u672C\u547D\u4EE4\u7684\u5F53\u524D\u8F93\u51FA\u4E3A\u51C6\u3002HANDOFF.md \u53EA\u63D0\u4F9B\u5F53\u524D\u5DE5\u7A0B\u3001Skill \u8DEF\u5F84\u53CA\u5177\u4F53\u63A5\u7EED\u65B9\u5F0F\uFF0C\u4E0D\u5305\u542B\u6574\u4EFD\u5F00\u53D1\u6307\u5357\u3002
3025
3036
 
@@ -3046,15 +3057,12 @@ ${IMPLEMENTATION_GUIDE}
3046
3057
 
3047
3058
  ${DEVELOPMENT_HANDOFF}`;
3048
3059
 
3049
- // packages/cli-core/src/commands/dev.ts
3050
- import { resolve as resolve14 } from "node:path";
3051
-
3052
- // packages/cli-core/src/dev/project-env.ts
3053
- import { constants } from "node:fs";
3054
- import { open as open4, rename as rename9, unlink as unlink7 } from "node:fs/promises";
3055
- import { randomUUID as randomUUID7 } from "node:crypto";
3056
- import { join as join13 } from "node:path";
3057
- import { parseEnv } from "node:util";
3060
+ // packages/cli-core/src/dev/local-stop.ts
3061
+ import { execFile as execFile5 } from "node:child_process";
3062
+ import { promisify as promisify5 } from "node:util";
3063
+ import { readFile as readFile13, readdir as readdir4, realpath as realpath5, rmdir as rmdir3, unlink as unlink7 } from "node:fs/promises";
3064
+ import { join as join14 } from "node:path";
3065
+ import { setTimeout as delay } from "node:timers/promises";
3058
3066
 
3059
3067
  // packages/cli-core/src/dev/errors.ts
3060
3068
  var BuddyDevError = class extends Error {
@@ -3084,7 +3092,377 @@ function throwIfDevAborted(signal) {
3084
3092
  if (signal?.aborted) throw abortedDevError(signal);
3085
3093
  }
3086
3094
 
3095
+ // packages/cli-core/src/dev/process.ts
3096
+ import { promisify as promisify4 } from "node:util";
3097
+ import { spawn as spawnChild, execFile as execFile4 } from "node:child_process";
3098
+ var NodeAgentProcessLauncher = class {
3099
+ #spawn;
3100
+ constructor(options = {}) {
3101
+ this.#spawn = options.spawn ?? spawnChild;
3102
+ }
3103
+ async spawn(request) {
3104
+ let child;
3105
+ try {
3106
+ child = this.#spawn(request.command, [...request.args], {
3107
+ cwd: request.cwd,
3108
+ env: { ...request.env },
3109
+ shell: false,
3110
+ detached: request.detached,
3111
+ windowsHide: false,
3112
+ stdio: ["ignore", "pipe", "pipe"]
3113
+ });
3114
+ } catch (cause) {
3115
+ throw new BuddyDevError({
3116
+ code: "DEV_AGENT_SPAWN_FAILED",
3117
+ phase: "launch",
3118
+ message: `\u65E0\u6CD5\u542F\u52A8 Agent \u7A0B\u5E8F ${request.command}\u3002`,
3119
+ cause
3120
+ });
3121
+ }
3122
+ let hasExited = false;
3123
+ let spawned = false;
3124
+ const exit = new Promise((resolve19) => {
3125
+ child.once("error", (error2) => {
3126
+ if (!spawned) {
3127
+ hasExited = true;
3128
+ resolve19({ code: null, signal: null, error: error2 });
3129
+ }
3130
+ });
3131
+ child.once("exit", (code2, signal) => {
3132
+ hasExited = true;
3133
+ resolve19({ code: code2, signal });
3134
+ });
3135
+ });
3136
+ child.stdout?.on("data", (chunk) => {
3137
+ try {
3138
+ request.onStdout?.(String(chunk));
3139
+ } catch {
3140
+ }
3141
+ });
3142
+ child.stderr?.on("data", (chunk) => {
3143
+ try {
3144
+ request.onStderr?.(String(chunk));
3145
+ } catch {
3146
+ }
3147
+ });
3148
+ try {
3149
+ await new Promise((resolve19, reject) => {
3150
+ const onSpawn = () => {
3151
+ spawned = true;
3152
+ child.off("error", onError);
3153
+ resolve19();
3154
+ };
3155
+ const onError = (error2) => {
3156
+ child.off("spawn", onSpawn);
3157
+ reject(error2);
3158
+ };
3159
+ child.once("spawn", onSpawn);
3160
+ child.once("error", onError);
3161
+ });
3162
+ } catch (cause) {
3163
+ throw new BuddyDevError({
3164
+ code: "DEV_AGENT_SPAWN_FAILED",
3165
+ phase: "launch",
3166
+ message: `\u65E0\u6CD5\u542F\u52A8 Agent \u7A0B\u5E8F ${request.command}\u3002`,
3167
+ cause
3168
+ });
3169
+ }
3170
+ const pid = child.pid;
3171
+ if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 0) {
3172
+ try {
3173
+ child.kill("SIGKILL");
3174
+ } catch {
3175
+ }
3176
+ throw new BuddyDevError({
3177
+ code: "DEV_AGENT_SPAWN_FAILED",
3178
+ phase: "launch",
3179
+ message: "Agent \u8FDB\u7A0B\u6CA1\u6709\u6709\u6548 PID\u3002"
3180
+ });
3181
+ }
3182
+ return {
3183
+ pid,
3184
+ exit,
3185
+ hasExited: () => hasExited,
3186
+ kill: (signal) => child.kill(signal)
3187
+ };
3188
+ }
3189
+ };
3190
+ function waitForExit(process2, timeoutMs) {
3191
+ if (process2.hasExited()) return Promise.resolve(true);
3192
+ return new Promise((resolve19) => {
3193
+ const timeout = setTimeout(() => resolve19(false), timeoutMs);
3194
+ process2.exit.then(() => {
3195
+ clearTimeout(timeout);
3196
+ resolve19(true);
3197
+ });
3198
+ });
3199
+ }
3200
+ var PosixProcessTreeController = class {
3201
+ #killGroup;
3202
+ #isGroupAlive;
3203
+ constructor(options = {}) {
3204
+ this.#killGroup = options.killGroup ?? ((pid, signal) => process.kill(-pid, signal));
3205
+ this.#isGroupAlive = options.isGroupAlive ?? ((pid) => {
3206
+ try {
3207
+ process.kill(-pid, 0);
3208
+ return true;
3209
+ } catch (cause) {
3210
+ return typeof cause === "object" && cause !== null && "code" in cause && cause.code !== "ESRCH";
3211
+ }
3212
+ });
3213
+ }
3214
+ async terminate(child, options = {}) {
3215
+ if (!Number.isSafeInteger(child.pid) || child.pid <= 0) {
3216
+ throw new BuddyDevError({
3217
+ code: "DEV_STOP_FAILED",
3218
+ phase: "stop",
3219
+ message: "\u65E0\u6CD5\u505C\u6B62\u6CA1\u6709\u6709\u6548 PID \u7684 Agent \u8FDB\u7A0B\u6811\u3002"
3220
+ });
3221
+ }
3222
+ const gracePeriodMs = options.gracePeriodMs ?? 5e3;
3223
+ let gracefulSent = false;
3224
+ let gracefulWasGroup = false;
3225
+ try {
3226
+ this.#killGroup(child.pid, "SIGTERM");
3227
+ gracefulSent = true;
3228
+ gracefulWasGroup = true;
3229
+ } catch {
3230
+ try {
3231
+ gracefulSent = child.kill("SIGTERM");
3232
+ } catch {
3233
+ gracefulSent = false;
3234
+ }
3235
+ }
3236
+ if (gracefulSent && await (gracefulWasGroup ? waitForGroupExit(child.pid, this.#isGroupAlive, gracePeriodMs) : waitForExit(child, gracePeriodMs))) return;
3237
+ let forceSent = false;
3238
+ let forceWasGroup = false;
3239
+ try {
3240
+ this.#killGroup(child.pid, "SIGKILL");
3241
+ forceSent = true;
3242
+ forceWasGroup = true;
3243
+ } catch {
3244
+ try {
3245
+ forceSent = child.kill("SIGKILL");
3246
+ } catch (cause) {
3247
+ if (!child.hasExited()) {
3248
+ throw new BuddyDevError({
3249
+ code: "DEV_STOP_FAILED",
3250
+ phase: "stop",
3251
+ message: `\u65E0\u6CD5\u505C\u6B62 Agent \u8FDB\u7A0B\u6811\uFF08PID ${child.pid}\uFF09\u3002`,
3252
+ cause
3253
+ });
3254
+ }
3255
+ }
3256
+ }
3257
+ if (!forceSent && !child.hasExited()) {
3258
+ throw new BuddyDevError({
3259
+ code: "DEV_STOP_FAILED",
3260
+ phase: "stop",
3261
+ message: `\u65E0\u6CD5\u5411 Agent \u8FDB\u7A0B\u6811\u53D1\u9001\u5F3A\u5236\u505C\u6B62\u4FE1\u53F7\uFF08PID ${child.pid}\uFF09\u3002`
3262
+ });
3263
+ }
3264
+ const forceStopped = forceWasGroup ? await waitForGroupExit(child.pid, this.#isGroupAlive, Math.min(gracePeriodMs, 1e3)) : await waitForExit(child, Math.min(gracePeriodMs, 1e3));
3265
+ if (!forceStopped) {
3266
+ throw new BuddyDevError({
3267
+ code: "DEV_STOP_FAILED",
3268
+ phase: "stop",
3269
+ message: `Agent \u8FDB\u7A0B\u6811\u6CA1\u6709\u5728\u5F3A\u5236\u505C\u6B62\u540E\u9000\u51FA\uFF08PID ${child.pid}\uFF09\u3002`
3270
+ });
3271
+ }
3272
+ }
3273
+ };
3274
+ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
3275
+ const deadline = Date.now() + timeoutMs;
3276
+ while (isGroupAlive(pid)) {
3277
+ const remaining = deadline - Date.now();
3278
+ if (remaining <= 0) return false;
3279
+ await new Promise((resolve19) => setTimeout(resolve19, Math.min(25, remaining)));
3280
+ }
3281
+ return true;
3282
+ }
3283
+ function processTreeControllerFor(platform) {
3284
+ if (platform === "darwin") return new PosixProcessTreeController();
3285
+ throw new BuddyDevError({
3286
+ code: "DEV_OS_UNSUPPORTED",
3287
+ phase: "launch",
3288
+ message: "buddy-cli dev V1 \u76EE\u524D\u53EA\u652F\u6301 macOS \u5F00\u53D1\u73AF\u5883\u3002"
3289
+ });
3290
+ }
3291
+ async function processIdentity(pid) {
3292
+ try {
3293
+ return (await promisify4(execFile4)("/bin/ps", ["-ww", "-p", String(pid), "-o", "uid=,pgid=,lstart=,command="], { env: { ...process.env, LC_ALL: "en_US.UTF-8" } })).stdout.trim() || void 0;
3294
+ } catch {
3295
+ return void 0;
3296
+ }
3297
+ }
3298
+
3299
+ // packages/cli-core/src/dev/instance-lock.ts
3300
+ import { createServer as createServer2 } from "node:http";
3301
+ import { realpath as realpath4 } from "node:fs/promises";
3302
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
3303
+ import { homedir as homedir3 } from "node:os";
3304
+ import { join as join13 } from "node:path";
3305
+ function alreadyRunningError() {
3306
+ 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\u5DE5\u7A0B\u76EE\u5F55\u6267\u884C buddy-cli dev stop\uFF0C\u518D\u542F\u52A8\u3002";
3307
+ return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
3308
+ }
3309
+ function devLockPath(options) {
3310
+ return join13(
3311
+ options.directory ?? join13(homedir3(), ".buddy", "dev-locks"),
3312
+ createHash5("sha256").update(`${new URL(options.gatewayUrl).origin}
3313
+ ${options.devAgentId}`).digest("hex")
3314
+ );
3315
+ }
3316
+ async function acquireDevInstanceLock(options) {
3317
+ const token = randomBytes2(32).toString("hex");
3318
+ const server = options.onStop ? createServer2(async (request, response) => {
3319
+ if (request.method !== "POST" || request.url !== "/stop" || request.headers.origin || request.headers.cookie || request.headers.authorization !== `Bearer ${token}`) {
3320
+ response.writeHead(403);
3321
+ response.end();
3322
+ return;
3323
+ }
3324
+ try {
3325
+ await options.onStop();
3326
+ response.writeHead(200);
3327
+ response.end();
3328
+ } catch {
3329
+ response.writeHead(500);
3330
+ response.end();
3331
+ }
3332
+ }) : void 0;
3333
+ try {
3334
+ if (server) await new Promise((resolve19, reject) => {
3335
+ server.once("error", reject);
3336
+ server.listen(0, "127.0.0.1", () => {
3337
+ server.off("error", reject);
3338
+ resolve19();
3339
+ });
3340
+ });
3341
+ const address = server?.address();
3342
+ const path = devLockPath(options);
3343
+ const lock = await acquireProcessLock({
3344
+ directory: options.directory ?? join13(homedir3(), ".buddy", "dev-locks"),
3345
+ key: path.split("/").at(-1),
3346
+ busyError: alreadyRunningError,
3347
+ ownerData: {
3348
+ ...options.projectRoot ? { projectRoot: await realpath4(options.projectRoot) } : {},
3349
+ ...address && typeof address !== "string" ? { control: { port: address.port, token } } : {}
3350
+ },
3351
+ runtimeData: async (pid) => ({ runtimeIdentity: await processIdentity(pid) })
3352
+ });
3353
+ return {
3354
+ attachRuntime: (pid) => lock.attachRuntime(pid),
3355
+ closeControl: () => {
3356
+ server?.close();
3357
+ },
3358
+ async release() {
3359
+ await lock.release();
3360
+ server?.close();
3361
+ }
3362
+ };
3363
+ } catch (error2) {
3364
+ server?.close();
3365
+ throw error2;
3366
+ }
3367
+ }
3368
+
3369
+ // packages/cli-core/src/dev/local-stop.ts
3370
+ var exec = promisify5(execFile5);
3371
+ var alive2 = (pid) => {
3372
+ try {
3373
+ process.kill(pid, 0);
3374
+ return true;
3375
+ } catch (e) {
3376
+ return e.code !== "ESRCH";
3377
+ }
3378
+ };
3379
+ var validPid2 = (pid) => Number.isSafeInteger(pid) && pid > 1;
3380
+ var failure = (message) => new BuddyDevError({ code: "DEV_STOP_FAILED", phase: "stop", message });
3381
+ async function stopLocalDev(options) {
3382
+ const path = devLockPath(options);
3383
+ let names;
3384
+ try {
3385
+ names = await readdir4(path);
3386
+ } catch (error2) {
3387
+ if (error2.code === "ENOENT") return { stopped: false };
3388
+ throw error2;
3389
+ }
3390
+ if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw failure("DEV \u9501\u4FE1\u606F\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u68C0\u67E5\u539F\u7EC8\u7AEF\uFF1B\u672A\u7EC8\u6B62\u4EFB\u4F55\u8FDB\u7A0B\u3002");
3391
+ const ownerPath = join14(path, names[0]);
3392
+ const source = await readFile13(ownerPath, "utf8");
3393
+ const owner = JSON.parse(source);
3394
+ if (!validPid2(owner.pid) || owner.runtimePid !== void 0 && !validPid2(owner.runtimePid)) throw failure("DEV \u9501\u4E2D\u7684 PID \u65E0\u6548\u3002");
3395
+ const root = await realpath5(options.projectRoot);
3396
+ if (owner.projectRoot && await realpath5(owner.projectRoot) !== root) throw failure(`\u6B64\u642D\u5B50\u7684 DEV \u5C5E\u4E8E\u53E6\u4E00\u4E2A\u5DE5\u7A0B\u76EE\u5F55\uFF1A${owner.projectRoot}\u3002\u8BF7\u5728\u8BE5\u76EE\u5F55\u6267\u884C dev stop\u3002`);
3397
+ if (alive2(owner.pid)) {
3398
+ const control = owner.control;
3399
+ if (!control || !Number.isInteger(control.port) || control.port < 1 || control.port > 65535 || !/^[a-f0-9]{64}$/u.test(control.token)) {
3400
+ throw failure(`\u539F CLI\uFF08PID ${owner.pid}\uFF09\u4ECD\u5728\u8FD0\u884C\u4E14\u4E0D\u652F\u6301\u8FDC\u7A0B\u505C\u6B62\uFF0C\u8BF7\u5728\u539F\u7EC8\u7AEF\u6309 Ctrl+C\u3002`);
3401
+ }
3402
+ try {
3403
+ const response = await fetch(`http://127.0.0.1:${control.port}/stop`, { method: "POST", redirect: "error", headers: { Authorization: `Bearer ${control.token}` }, signal: AbortSignal.timeout(12e3) });
3404
+ if (!response.ok) throw new Error("stop failed");
3405
+ return { stopped: true };
3406
+ } catch {
3407
+ throw failure(`\u672A\u80FD\u786E\u8BA4\u539F CLI\uFF08PID ${owner.pid}\uFF09\u505C\u6B62 DEV\uFF0C\u8BF7\u5728\u539F\u7EC8\u7AEF\u6309 Ctrl+C\uFF1B\u4FDD\u7559\u8FDB\u7A0B\u9501\u3002`);
3408
+ }
3409
+ }
3410
+ const pid = owner.runtimePid;
3411
+ if (pid && alive2(pid)) {
3412
+ const identity = await processIdentity(pid);
3413
+ if (!identity || owner.runtimeIdentity && identity !== owner.runtimeIdentity) throw failure(`PID ${pid} \u7684\u8EAB\u4EFD\u5DF2\u6539\u53D8\u6216\u65E0\u6CD5\u786E\u8BA4\uFF0C\u672A\u7EC8\u6B62\u8BE5\u8FDB\u7A0B\u3002`);
3414
+ const [uid, group] = identity.split(/\s+/u);
3415
+ if (Number(uid) !== process.getuid?.() || Number(group) !== pid) throw failure(`PID ${pid} \u4E0D\u662F\u5F53\u524D\u7528\u6237\u7684\u72EC\u7ACB Runtime \u8FDB\u7A0B\u7EC4\uFF0C\u672A\u7EC8\u6B62\u8BE5\u8FDB\u7A0B\u3002`);
3416
+ if (!owner.runtimeIdentity) {
3417
+ const cwd = (await exec("/usr/sbin/lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"])).stdout.split("\n").find((line) => line.startsWith("n"))?.slice(1);
3418
+ if (!cwd || await realpath5(cwd) !== root || !identity.endsWith(` ${join14(root, "start.mjs")}`) || !/\bnode\s/u.test(identity)) throw failure(`\u65E0\u6CD5\u786E\u8BA4\u65E7 Runtime\uFF08PID ${pid}\uFF09\u5C5E\u4E8E\u5F53\u524D\u5DE5\u7A0B\uFF0C\u672A\u7EC8\u6B62\u8BE5\u8FDB\u7A0B\u3002`);
3419
+ }
3420
+ if (await readFile13(ownerPath, "utf8") !== source || await processIdentity(pid) !== identity || alive2(owner.pid)) throw failure("DEV \u8FDB\u7A0B\u72B6\u6001\u5DF2\u6539\u53D8\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C dev stop\u3002");
3421
+ try {
3422
+ process.kill(-pid, "SIGTERM");
3423
+ } catch (error2) {
3424
+ if (error2.code !== "ESRCH") throw error2;
3425
+ }
3426
+ const groupAlive = () => {
3427
+ try {
3428
+ process.kill(-pid, 0);
3429
+ return true;
3430
+ } catch (e) {
3431
+ return e.code !== "ESRCH";
3432
+ }
3433
+ };
3434
+ const deadline = Date.now() + 5e3;
3435
+ while (groupAlive() && Date.now() < deadline) await delay(25);
3436
+ if (groupAlive()) {
3437
+ const current = await processIdentity(pid);
3438
+ if (current && current !== identity) throw failure("Runtime \u8FDB\u7A0B\u8EAB\u4EFD\u5DF2\u6539\u53D8\uFF0C\u672A\u53D1\u9001\u5F3A\u5236\u505C\u6B62\u4FE1\u53F7\u3002");
3439
+ process.kill(-pid, "SIGKILL");
3440
+ const forceDeadline = Date.now() + 1e3;
3441
+ while (groupAlive() && Date.now() < forceDeadline) await delay(25);
3442
+ if (groupAlive()) throw failure(`Runtime \u8FDB\u7A0B\u7EC4 ${pid} \u5C1A\u672A\u786E\u8BA4\u9000\u51FA\uFF0C\u4FDD\u7559\u8FDB\u7A0B\u9501\u3002`);
3443
+ }
3444
+ }
3445
+ if (pid && (alive2(pid) || alive2(-pid))) throw failure(`Runtime \u8FDB\u7A0B\u7EC4 ${pid} \u4ECD\u6709\u6B8B\u7559\u4E14\u65E0\u6CD5\u5B8C\u6574\u786E\u8BA4\u8EAB\u4EFD\uFF0C\u8BF7\u68C0\u67E5\u539F\u7EC8\u7AEF\uFF1B\u4FDD\u7559\u8FDB\u7A0B\u9501\u3002`);
3446
+ try {
3447
+ if (await readFile13(ownerPath, "utf8") === source) await unlink7(ownerPath);
3448
+ } catch (error2) {
3449
+ if (error2.code !== "ENOENT") throw error2;
3450
+ }
3451
+ await rmdir3(path).catch((error2) => {
3452
+ if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error2.code)) throw error2;
3453
+ });
3454
+ return { stopped: true };
3455
+ }
3456
+
3457
+ // packages/cli-core/src/commands/dev.ts
3458
+ import { resolve as resolve14 } from "node:path";
3459
+
3087
3460
  // packages/cli-core/src/dev/project-env.ts
3461
+ import { constants } from "node:fs";
3462
+ import { open as open4, rename as rename9, unlink as unlink8 } from "node:fs/promises";
3463
+ import { randomUUID as randomUUID7 } from "node:crypto";
3464
+ import { join as join15 } from "node:path";
3465
+ import { parseEnv } from "node:util";
3088
3466
  function invalid(message) {
3089
3467
  return new BuddyDevError({ code: "DEV_CONFIG_INVALID", phase: "config", message });
3090
3468
  }
@@ -3104,7 +3482,7 @@ async function readLocalFile(path) {
3104
3482
  }
3105
3483
  }
3106
3484
  async function readProjectEnvironment(projectRoot) {
3107
- const source = await readLocalFile(join13(projectRoot, ".env"));
3485
+ const source = await readLocalFile(join15(projectRoot, ".env"));
3108
3486
  let values;
3109
3487
  try {
3110
3488
  values = parseEnv(source);
@@ -3183,13 +3561,13 @@ async function replaceFile2(path, contents, mode) {
3183
3561
  await rename9(temporary, path);
3184
3562
  } finally {
3185
3563
  await file?.close();
3186
- await unlink7(temporary).catch((cause) => {
3564
+ await unlink8(temporary).catch((cause) => {
3187
3565
  if (cause.code !== "ENOENT") throw cause;
3188
3566
  });
3189
3567
  }
3190
3568
  }
3191
3569
  async function writeProjectEnvironment(projectRoot, values) {
3192
- const envPath = join13(projectRoot, ".env"), ignorePath = join13(projectRoot, ".gitignore");
3570
+ const envPath = join15(projectRoot, ".env"), ignorePath = join15(projectRoot, ".gitignore");
3193
3571
  const contents = updateValues(await readLocalFile(envPath), { ...values, BUDDY_ID: void 0 });
3194
3572
  const ignored = await readLocalFile(ignorePath);
3195
3573
  if (ignored.trimEnd().split(/\r?\n/u).at(-1) !== "/.env") {
@@ -3468,42 +3846,23 @@ function buildAgentEnvironment(options) {
3468
3846
 
3469
3847
  // packages/cli-core/src/dev/orchestrator.ts
3470
3848
  import { createHash as createHash7 } from "node:crypto";
3471
- import { readFile as readFile16 } from "node:fs/promises";
3472
- import { join as join17, resolve as resolve12 } from "node:path";
3849
+ import { readFile as readFile17 } from "node:fs/promises";
3850
+ import { join as join18, resolve as resolve12 } from "node:path";
3473
3851
 
3474
3852
  // packages/cli-core/src/dev/reload.ts
3475
3853
  import { watch } from "node:fs";
3476
3854
  import { createHash as createHash6 } from "node:crypto";
3477
- import { readFile as readFile13, readdir as readdir4 } from "node:fs/promises";
3478
- import { join as join15, resolve as resolve9 } from "node:path";
3855
+ import { readFile as readFile14, readdir as readdir5 } from "node:fs/promises";
3856
+ import { join as join16, resolve as resolve9 } from "node:path";
3479
3857
  import { setTimeout as sleep } from "node:timers/promises";
3480
-
3481
- // packages/cli-core/src/dev/instance-lock.ts
3482
- import { createHash as createHash5 } from "node:crypto";
3483
- import { homedir as homedir3 } from "node:os";
3484
- import { join as join14 } from "node:path";
3485
- function alreadyRunningError() {
3486
- 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";
3487
- return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
3488
- }
3489
- async function acquireDevInstanceLock(options) {
3490
- return acquireProcessLock({
3491
- directory: options.directory ?? join14(homedir3(), ".buddy", "dev-locks"),
3492
- key: createHash5("sha256").update(`${new URL(options.gatewayUrl).origin}
3493
- ${options.devAgentId}`).digest("hex"),
3494
- busyError: alreadyRunningError
3495
- });
3496
- }
3497
-
3498
- // packages/cli-core/src/dev/reload.ts
3499
3858
  var digest2 = (contents) => createHash6("sha256").update(contents).digest("hex");
3500
3859
  async function snapshot(root, prefix = "", result = /* @__PURE__ */ new Map()) {
3501
- for (const entry2 of await readdir4(join15(root, prefix), { withFileTypes: true })) {
3860
+ for (const entry2 of await readdir5(join16(root, prefix), { withFileTypes: true })) {
3502
3861
  const path = prefix ? `${prefix}/${entry2.name}` : entry2.name;
3503
3862
  if (!relevant(path)) continue;
3504
3863
  try {
3505
3864
  if (entry2.isDirectory()) await snapshot(root, path, result);
3506
- else if (entry2.isFile()) result.set(path, digest2(await readFile13(join15(root, path))));
3865
+ else if (entry2.isFile()) result.set(path, digest2(await readFile14(join16(root, path))));
3507
3866
  } catch (cause) {
3508
3867
  if (cause.code !== "ENOENT") throw cause;
3509
3868
  }
@@ -3593,7 +3952,7 @@ var ReloadingDevSession = class {
3593
3952
  const path = String(name);
3594
3953
  if (!relevant(path)) return;
3595
3954
  if (path === ".env") {
3596
- void readFile13(join15(this.projectRoot, path), "utf8").then((contents) => {
3955
+ void readFile14(join16(this.projectRoot, path), "utf8").then((contents) => {
3597
3956
  if (contents !== this.#environment) {
3598
3957
  this.#environment = contents;
3599
3958
  this.#changed();
@@ -3615,8 +3974,10 @@ var ReloadingDevSession = class {
3615
3974
  this.#initializing = false;
3616
3975
  this.#watcher?.close();
3617
3976
  this.#options.signal?.removeEventListener("abort", this.#onAbort);
3618
- if (cause instanceof BuddyDevError && cause.code === "DEV_STOP_FAILED") this.#unsafeToRelease = true;
3619
- else await this.#lock?.release();
3977
+ if (cause instanceof BuddyDevError && cause.code === "DEV_STOP_FAILED") {
3978
+ this.#unsafeToRelease = true;
3979
+ this.#lock?.closeControl?.();
3980
+ } else await this.#lock?.release();
3620
3981
  throw cause;
3621
3982
  }
3622
3983
  }
@@ -3648,7 +4009,7 @@ var ReloadingDevSession = class {
3648
4009
  acquireLock: async (input) => {
3649
4010
  const origin = new URL(input.gatewayUrl).origin;
3650
4011
  if (this.#lockOrigin && this.#lockOrigin !== origin) throw new BuddyDevError({ code: "DEV_CONFIG_INVALID", phase: "config", message: "\u5DE5\u7A0B\u5E73\u53F0\u5730\u5740\u5DF2\u6539\u53D8\uFF0C\u8BF7\u505C\u6B62 DEV \u540E\u91CD\u65B0\u8FDE\u63A5\u3002" });
3651
- this.#lock ??= await acquireDevInstanceLock(input);
4012
+ this.#lock ??= await acquireDevInstanceLock({ ...input, projectRoot: this.projectRoot, onStop: () => this.stop() });
3652
4013
  this.#lockOrigin = origin;
3653
4014
  return { attachRuntime: (pid) => this.#lock.attachRuntime(pid), async release() {
3654
4015
  } };
@@ -3673,6 +4034,7 @@ var ReloadingDevSession = class {
3673
4034
  this.#unsafeToRelease = true;
3674
4035
  this.#ended = true;
3675
4036
  this.#watcher?.close();
4037
+ this.#lock?.closeControl?.();
3676
4038
  this.#resolve(completion);
3677
4039
  }
3678
4040
  }
@@ -3726,6 +4088,7 @@ var ReloadingDevSession = class {
3726
4088
  this.#unsafeToRelease = true;
3727
4089
  this.#ended = true;
3728
4090
  this.#watcher?.close();
4091
+ this.#lock?.closeControl?.();
3729
4092
  this.#resolve({ state: "failed", error: error2 });
3730
4093
  return;
3731
4094
  }
@@ -3759,6 +4122,7 @@ var ReloadingDevSession = class {
3759
4122
  this.#emit("stopped");
3760
4123
  this.#resolve({ state: "stopped" });
3761
4124
  } catch (cause) {
4125
+ this.#lock?.closeControl?.();
3762
4126
  const error2 = this.#error(cause);
3763
4127
  this.#emit("failed");
3764
4128
  this.#resolve({ state: "failed", error: error2 });
@@ -3771,7 +4135,7 @@ function startReloadingDev(options, startProcess) {
3771
4135
  }
3772
4136
 
3773
4137
  // packages/cli-core/src/config/runtime-policy.ts
3774
- import { readFile as readFile14, realpath as realpath4 } from "node:fs/promises";
4138
+ import { readFile as readFile15, realpath as realpath6 } from "node:fs/promises";
3775
4139
  import { createRequire as createRequire2 } from "node:module";
3776
4140
  import { resolve as resolve10 } from "node:path";
3777
4141
  var RuntimeRequirementError = class extends Error {
@@ -3808,14 +4172,14 @@ async function requireLatestRuntime(root, options = {}) {
3808
4172
  }
3809
4173
  try {
3810
4174
  assertRequiredRuntime(await projectRuntimeVersion(root), latest);
3811
- const installedPath = await realpath4(resolve10(root, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"));
4175
+ const installedPath = await realpath6(resolve10(root, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"));
3812
4176
  const fromProject = createRequire2(resolve10(root, "package.json")).resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`);
3813
- if (await realpath4(fromProject) !== installedPath) throw new Error("Runtime \u5FC5\u987B\u5B89\u88C5\u5728\u5F53\u524D\u5DE5\u7A0B\u4E2D");
3814
- const installed2 = JSON.parse(await readFile14(installedPath, "utf8"));
4177
+ if (await realpath6(fromProject) !== installedPath) throw new Error("Runtime \u5FC5\u987B\u5B89\u88C5\u5728\u5F53\u524D\u5DE5\u7A0B\u4E2D");
4178
+ const installed2 = JSON.parse(await readFile15(installedPath, "utf8"));
3815
4179
  if (installed2.version !== latest) throw new Error(`Runtime \u5B9E\u9645\u5B89\u88C5 ${installed2.version}\uFF0C\u8981\u6C42 ${latest}`);
3816
4180
  let lock;
3817
4181
  try {
3818
- lock = await readFile14(resolve10(root, "package-lock.json"), "utf8");
4182
+ lock = await readFile15(resolve10(root, "package-lock.json"), "utf8");
3819
4183
  } catch (cause) {
3820
4184
  if (cause.code !== "ENOENT") throw cause;
3821
4185
  }
@@ -3835,8 +4199,8 @@ import { createRequire as createRequire3 } from "node:module";
3835
4199
  import { dirname as dirname4, resolve as resolve11 } from "node:path";
3836
4200
 
3837
4201
  // packages/cli-core/src/config/data-access.ts
3838
- import { lstat as lstat10, readFile as readFile15 } from "node:fs/promises";
3839
- import { join as join16 } from "node:path";
4202
+ import { lstat as lstat10, readFile as readFile16 } from "node:fs/promises";
4203
+ import { join as join17 } from "node:path";
3840
4204
  var DATA_ACCESS_FILENAME = "data-access.json";
3841
4205
  function parseDataAccessRequirements(source) {
3842
4206
  if (source === void 0) return [];
@@ -3864,10 +4228,10 @@ function parseDataAccessRequirements(source) {
3864
4228
  });
3865
4229
  }
3866
4230
  async function readDataAccessRequirements(projectRoot) {
3867
- const path = join16(projectRoot, DATA_ACCESS_FILENAME);
4231
+ const path = join17(projectRoot, DATA_ACCESS_FILENAME);
3868
4232
  try {
3869
4233
  if (!(await lstat10(path)).isFile()) throw new Error(`${DATA_ACCESS_FILENAME} \u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6`);
3870
- return parseDataAccessRequirements(await readFile15(path, "utf8"));
4234
+ return parseDataAccessRequirements(await readFile16(path, "utf8"));
3871
4235
  } catch (cause) {
3872
4236
  if (cause.code === "ENOENT") return [];
3873
4237
  throw cause;
@@ -3920,202 +4284,6 @@ async function officialAgentLaunchPlan(input) {
3920
4284
  });
3921
4285
  }
3922
4286
 
3923
- // packages/cli-core/src/dev/process.ts
3924
- import { spawn as spawnChild } from "node:child_process";
3925
- var NodeAgentProcessLauncher = class {
3926
- #spawn;
3927
- constructor(options = {}) {
3928
- this.#spawn = options.spawn ?? spawnChild;
3929
- }
3930
- async spawn(request) {
3931
- let child;
3932
- try {
3933
- child = this.#spawn(request.command, [...request.args], {
3934
- cwd: request.cwd,
3935
- env: { ...request.env },
3936
- shell: false,
3937
- detached: request.detached,
3938
- windowsHide: false,
3939
- stdio: ["ignore", "pipe", "pipe"]
3940
- });
3941
- } catch (cause) {
3942
- throw new BuddyDevError({
3943
- code: "DEV_AGENT_SPAWN_FAILED",
3944
- phase: "launch",
3945
- message: `\u65E0\u6CD5\u542F\u52A8 Agent \u7A0B\u5E8F ${request.command}\u3002`,
3946
- cause
3947
- });
3948
- }
3949
- let hasExited = false;
3950
- let spawned = false;
3951
- const exit = new Promise((resolve19) => {
3952
- child.once("error", (error2) => {
3953
- if (!spawned) {
3954
- hasExited = true;
3955
- resolve19({ code: null, signal: null, error: error2 });
3956
- }
3957
- });
3958
- child.once("exit", (code2, signal) => {
3959
- hasExited = true;
3960
- resolve19({ code: code2, signal });
3961
- });
3962
- });
3963
- child.stdout?.on("data", (chunk) => {
3964
- try {
3965
- request.onStdout?.(String(chunk));
3966
- } catch {
3967
- }
3968
- });
3969
- child.stderr?.on("data", (chunk) => {
3970
- try {
3971
- request.onStderr?.(String(chunk));
3972
- } catch {
3973
- }
3974
- });
3975
- try {
3976
- await new Promise((resolve19, reject) => {
3977
- const onSpawn = () => {
3978
- spawned = true;
3979
- child.off("error", onError);
3980
- resolve19();
3981
- };
3982
- const onError = (error2) => {
3983
- child.off("spawn", onSpawn);
3984
- reject(error2);
3985
- };
3986
- child.once("spawn", onSpawn);
3987
- child.once("error", onError);
3988
- });
3989
- } catch (cause) {
3990
- throw new BuddyDevError({
3991
- code: "DEV_AGENT_SPAWN_FAILED",
3992
- phase: "launch",
3993
- message: `\u65E0\u6CD5\u542F\u52A8 Agent \u7A0B\u5E8F ${request.command}\u3002`,
3994
- cause
3995
- });
3996
- }
3997
- const pid = child.pid;
3998
- if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 0) {
3999
- try {
4000
- child.kill("SIGKILL");
4001
- } catch {
4002
- }
4003
- throw new BuddyDevError({
4004
- code: "DEV_AGENT_SPAWN_FAILED",
4005
- phase: "launch",
4006
- message: "Agent \u8FDB\u7A0B\u6CA1\u6709\u6709\u6548 PID\u3002"
4007
- });
4008
- }
4009
- return {
4010
- pid,
4011
- exit,
4012
- hasExited: () => hasExited,
4013
- kill: (signal) => child.kill(signal)
4014
- };
4015
- }
4016
- };
4017
- function waitForExit(process2, timeoutMs) {
4018
- if (process2.hasExited()) return Promise.resolve(true);
4019
- return new Promise((resolve19) => {
4020
- const timeout = setTimeout(() => resolve19(false), timeoutMs);
4021
- process2.exit.then(() => {
4022
- clearTimeout(timeout);
4023
- resolve19(true);
4024
- });
4025
- });
4026
- }
4027
- var PosixProcessTreeController = class {
4028
- #killGroup;
4029
- #isGroupAlive;
4030
- constructor(options = {}) {
4031
- this.#killGroup = options.killGroup ?? ((pid, signal) => process.kill(-pid, signal));
4032
- this.#isGroupAlive = options.isGroupAlive ?? ((pid) => {
4033
- try {
4034
- process.kill(-pid, 0);
4035
- return true;
4036
- } catch (cause) {
4037
- return typeof cause === "object" && cause !== null && "code" in cause && cause.code !== "ESRCH";
4038
- }
4039
- });
4040
- }
4041
- async terminate(child, options = {}) {
4042
- if (!Number.isSafeInteger(child.pid) || child.pid <= 0) {
4043
- throw new BuddyDevError({
4044
- code: "DEV_STOP_FAILED",
4045
- phase: "stop",
4046
- message: "\u65E0\u6CD5\u505C\u6B62\u6CA1\u6709\u6709\u6548 PID \u7684 Agent \u8FDB\u7A0B\u6811\u3002"
4047
- });
4048
- }
4049
- const gracePeriodMs = options.gracePeriodMs ?? 5e3;
4050
- let gracefulSent = false;
4051
- let gracefulWasGroup = false;
4052
- try {
4053
- this.#killGroup(child.pid, "SIGTERM");
4054
- gracefulSent = true;
4055
- gracefulWasGroup = true;
4056
- } catch {
4057
- try {
4058
- gracefulSent = child.kill("SIGTERM");
4059
- } catch {
4060
- gracefulSent = false;
4061
- }
4062
- }
4063
- if (gracefulSent && await (gracefulWasGroup ? waitForGroupExit(child.pid, this.#isGroupAlive, gracePeriodMs) : waitForExit(child, gracePeriodMs))) return;
4064
- let forceSent = false;
4065
- let forceWasGroup = false;
4066
- try {
4067
- this.#killGroup(child.pid, "SIGKILL");
4068
- forceSent = true;
4069
- forceWasGroup = true;
4070
- } catch {
4071
- try {
4072
- forceSent = child.kill("SIGKILL");
4073
- } catch (cause) {
4074
- if (!child.hasExited()) {
4075
- throw new BuddyDevError({
4076
- code: "DEV_STOP_FAILED",
4077
- phase: "stop",
4078
- message: `\u65E0\u6CD5\u505C\u6B62 Agent \u8FDB\u7A0B\u6811\uFF08PID ${child.pid}\uFF09\u3002`,
4079
- cause
4080
- });
4081
- }
4082
- }
4083
- }
4084
- if (!forceSent && !child.hasExited()) {
4085
- throw new BuddyDevError({
4086
- code: "DEV_STOP_FAILED",
4087
- phase: "stop",
4088
- message: `\u65E0\u6CD5\u5411 Agent \u8FDB\u7A0B\u6811\u53D1\u9001\u5F3A\u5236\u505C\u6B62\u4FE1\u53F7\uFF08PID ${child.pid}\uFF09\u3002`
4089
- });
4090
- }
4091
- const forceStopped = forceWasGroup ? await waitForGroupExit(child.pid, this.#isGroupAlive, Math.min(gracePeriodMs, 1e3)) : await waitForExit(child, Math.min(gracePeriodMs, 1e3));
4092
- if (!forceStopped) {
4093
- throw new BuddyDevError({
4094
- code: "DEV_STOP_FAILED",
4095
- phase: "stop",
4096
- message: `Agent \u8FDB\u7A0B\u6811\u6CA1\u6709\u5728\u5F3A\u5236\u505C\u6B62\u540E\u9000\u51FA\uFF08PID ${child.pid}\uFF09\u3002`
4097
- });
4098
- }
4099
- }
4100
- };
4101
- async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
4102
- const deadline = Date.now() + timeoutMs;
4103
- while (isGroupAlive(pid)) {
4104
- const remaining = deadline - Date.now();
4105
- if (remaining <= 0) return false;
4106
- await new Promise((resolve19) => setTimeout(resolve19, Math.min(25, remaining)));
4107
- }
4108
- return true;
4109
- }
4110
- function processTreeControllerFor(platform) {
4111
- if (platform === "darwin") return new PosixProcessTreeController();
4112
- throw new BuddyDevError({
4113
- code: "DEV_OS_UNSUPPORTED",
4114
- phase: "launch",
4115
- message: "buddy-cli dev V1 \u76EE\u524D\u53EA\u652F\u6301 macOS \u5F00\u53D1\u73AF\u5883\u3002"
4116
- });
4117
- }
4118
-
4119
4287
  // packages/cli-core/src/dev/orchestrator.ts
4120
4288
  var DEFAULT_ONLINE_TIMEOUT_MS = 3e4;
4121
4289
  var DEFAULT_POLL_INTERVAL_MS = 200;
@@ -4194,7 +4362,7 @@ async function registerDevAgent(options) {
4194
4362
  });
4195
4363
  }
4196
4364
  }
4197
- function delay(milliseconds, signal) {
4365
+ function delay2(milliseconds, signal) {
4198
4366
  if (signal.aborted) return Promise.reject(abortedDevError(signal));
4199
4367
  return new Promise((resolveDelay, reject) => {
4200
4368
  const onAbort = () => {
@@ -4234,7 +4402,7 @@ async function waitForAgentOnline(options) {
4234
4402
  if (cause instanceof BuddyDevError && !cause.retryable) throw cause;
4235
4403
  lastError = cause;
4236
4404
  }
4237
- await delay(Math.min(options.pollIntervalMs, Math.max(1, deadline - Date.now())), options.signal);
4405
+ await delay2(Math.min(options.pollIntervalMs, Math.max(1, deadline - Date.now())), options.signal);
4238
4406
  }
4239
4407
  }
4240
4408
  function agentExitedError(exit, stderr = "") {
@@ -4386,7 +4554,7 @@ async function startBuddyDevProcess(options, hooks) {
4386
4554
  let config;
4387
4555
  let configSource;
4388
4556
  try {
4389
- configSource = await readFile16(join17(projectRoot, BUDDY_PACKAGE_FILENAME), "utf8");
4557
+ configSource = await readFile17(join18(projectRoot, BUDDY_PACKAGE_FILENAME), "utf8");
4390
4558
  config = combineBuddyProject(parseBuddyAgentConfig(configSource));
4391
4559
  } catch (cause) {
4392
4560
  throw new BuddyDevError({
@@ -4709,8 +4877,11 @@ var DevCommandUsageError = class extends Error {
4709
4877
  };
4710
4878
  var DEV_COMMAND_HELP = `\u7528\u6CD5:
4711
4879
  buddy-cli dev [--app-server <App Server URL>] [--directory <\u9879\u76EE\u76EE\u5F55>] [--refresh-token]
4880
+ buddy-cli dev stop [--directory <\u9879\u76EE\u76EE\u5F55>] [--app-server <App Server URL>] [--buddy-id <\u642D\u5B50 ID>]
4712
4881
 
4713
4882
  \u8BF4\u660E:
4883
+ dev stop \u505C\u6B62\u5F53\u524D\u5DE5\u7A0B\u7684 DEV \u53CA\u6587\u4EF6\u76D1\u542C\uFF0C\u6E05\u7406\u5DF2\u786E\u8BA4\u9000\u51FA\u7684\u6B8B\u7559\u8FDB\u7A0B\uFF1B\u4E0D\u91CD\u8FDE\u3001\u4E0D\u91CD\u542F\uFF0C\u65E0\u9700\u767B\u5F55\u6216\u8054\u7F51\u66F4\u65B0\u3002
4884
+ \u5173\u95ED\u7EC8\u7AEF\u3001Ctrl+C \u6216 SIGTERM \u4F1A\u505C\u6B62 CLI \u6258\u7BA1\u7684 Runtime\uFF1B\u5F3A\u5236\u6740\u6B7B CLI \u540E\u53EF\u7528 dev stop \u6E05\u7406\u3002
4714
4885
  \u672A\u914D\u7F6E\u5730\u5740\u65F6\u8FDE\u63A5 MLB \u6D4B\u8BD5\u73AF\u5883 ${DEFAULT_MLB_TEST_URL}\uFF08HTTP \u4EC5\u9650\u6D4B\u8BD5\uFF09\u3002
4715
4886
  CLI \u7EDF\u4E00\u542F\u52A8\u5E76\u7BA1\u7406\u5DE5\u7A0B\u4E2D\u7684\u5B98\u65B9 Runtime\uFF1B\u6BCF\u6B21\u542F\u52A8\u6216\u91CD\u8F7D\u5FC5\u987B\u901A\u8FC7 npm \u6700\u65B0\u7A33\u5B9A\u7248\u68C0\u67E5\u3002
4716
4887
  DEV \u4F1A\u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u83B7\u53D6\u5F00\u53D1\u8005\u4F1A\u8BDD\uFF0C\u5C06\u5F53\u524D\u642D\u5B50\u7684\u5F00\u53D1 Token \u5199\u5165\u5DE5\u7A0B .env\uFF0C\u590D\u7528\u524D\u5411 Server \u6838\u5BF9\u6240\u5C5E\u642D\u5B50\uFF1B\u767B\u5F55\u51ED\u636E\u4ECD\u4F7F\u7528\u7CFB\u7EDF\u51ED\u636E\u5B58\u50A8\u3002
@@ -4726,6 +4897,9 @@ function setOnce(current, value, flag2) {
4726
4897
  return value;
4727
4898
  }
4728
4899
  function parseDevArguments(args) {
4900
+ const stop = args[0] === "stop";
4901
+ if (stop) args = args.slice(1);
4902
+ let buddyId;
4729
4903
  let directory = ".";
4730
4904
  let directorySet = false;
4731
4905
  let appServer;
@@ -4746,6 +4920,10 @@ function parseDevArguments(args) {
4746
4920
  const value = valueAfter(args, index, flag2 ?? "\u53C2\u6570");
4747
4921
  index += 1;
4748
4922
  switch (flag2) {
4923
+ case "--buddy-id":
4924
+ if (!stop) throw new DevCommandUsageError("--buddy-id \u4EC5\u7528\u4E8E dev stop \u8EAB\u4EFD\u6821\u9A8C");
4925
+ buddyId = setOnce(buddyId, value, flag2);
4926
+ break;
4749
4927
  case "--directory":
4750
4928
  if (directorySet) throw new DevCommandUsageError("--directory \u4E0D\u80FD\u91CD\u590D");
4751
4929
  directory = value;
@@ -4758,7 +4936,8 @@ function parseDevArguments(args) {
4758
4936
  throw new DevCommandUsageError(`\u672A\u77E5 dev \u53C2\u6570 ${flag2}`);
4759
4937
  }
4760
4938
  }
4761
- return { directory, appServer, help, refreshToken };
4939
+ if (stop && refreshToken) throw new DevCommandUsageError("dev stop \u4E0D\u652F\u6301 --refresh-token");
4940
+ return { stop, buddyId, directory, appServer, help, refreshToken };
4762
4941
  }
4763
4942
  function appServerUrl(override, env) {
4764
4943
  try {
@@ -4792,6 +4971,13 @@ async function runDevCommand(args, io, options = {}) {
4792
4971
  const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
4793
4972
  const environment = options.env ?? process.env;
4794
4973
  const url = appServerUrl(parsed.appServer, environment);
4974
+ if (parsed.stop) {
4975
+ const manifest = await (options.readManifest ?? readBuddyProjectManifest)(root);
4976
+ if (parsed.buddyId && parsed.buddyId !== manifest.buddyId) throw new DevCommandUsageError(`\u5F53\u524D\u5DE5\u7A0B\u5C5E\u4E8E ${manifest.buddyId}\uFF0C\u505C\u6B62\u6307\u4EE4\u8981\u6C42 ${parsed.buddyId}\uFF1B\u672A\u505C\u6B62\u4EFB\u4F55\u8FDB\u7A0B\u3002`);
4977
+ const result = await stopLocalDev({ gatewayUrl: url, devAgentId: manifest.buddyId, projectRoot: root });
4978
+ io.write(result.stopped ? "DEV \u5DF2\u505C\u6B62\uFF0C\u6B8B\u7559\u8FDB\u7A0B\u53CA\u8FDB\u7A0B\u9501\u5DF2\u6E05\u7406\u3002\u9700\u8981\u8C03\u8BD5\u65F6\u91CD\u65B0\u8FDE\u63A5\u5E76\u542F\u52A8 DEV\u3002" : "\u5F53\u524D\u5DE5\u7A0B\u6CA1\u6709 CLI \u6258\u7BA1\u7684\u672C\u5730 DEV \u8FDB\u7A0B\u3002\u5176\u4ED6\u7535\u8111\u4E0A\u7684\u670D\u52A1\u8BF7\u5728\u5BF9\u5E94\u7535\u8111\u505C\u6B62\u3002");
4979
+ return { exitCode: 0 };
4980
+ }
4795
4981
  const authorizationInput = { appServerUrl: url, ...parsed.refreshToken ? { refreshToken: true } : {} };
4796
4982
  if (!options.authorize) {
4797
4983
  throw new DevCommandUsageError("\u5F53\u524D CLI Host \u5C1A\u672A\u914D\u7F6E\u642D\u642D\u8D26\u53F7\u767B\u5F55 Adapter");
@@ -5308,7 +5494,7 @@ var HttpDeveloperPlatformClient = class {
5308
5494
  };
5309
5495
 
5310
5496
  // packages/cli-core/src/cloud/mock-client.ts
5311
- import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
5497
+ import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
5312
5498
  var MockDeveloperPlatformClient = class {
5313
5499
  #now;
5314
5500
  #buddies = /* @__PURE__ */ new Map();
@@ -5317,7 +5503,7 @@ var MockDeveloperPlatformClient = class {
5317
5503
  this.#now = options.now ?? Date.now;
5318
5504
  }
5319
5505
  async createBuddy(request) {
5320
- const id = `b_mock_${randomBytes2(8).toString("hex")}`;
5506
+ const id = `b_mock_${randomBytes3(8).toString("hex")}`;
5321
5507
  const value = this.#value({
5322
5508
  id,
5323
5509
  meta: request.meta,
@@ -5326,7 +5512,7 @@ var MockDeveloperPlatformClient = class {
5326
5512
  released: false,
5327
5513
  online: false,
5328
5514
  updatedAt: this.#now(),
5329
- token: `bt_mock_${randomBytes2(16).toString("hex")}`
5515
+ token: `bt_mock_${randomBytes3(16).toString("hex")}`
5330
5516
  });
5331
5517
  this.#buddies.set(id, value);
5332
5518
  this.#packages.set(id, []);
@@ -5638,8 +5824,8 @@ async function runCloudCommand(command, args, io, options = {}) {
5638
5824
  var CLOUD_COMMAND_HELP = HELP;
5639
5825
 
5640
5826
  // packages/cli-core/src/migration/index.ts
5641
- import { lstat as lstat11, open as open5, readdir as readdir5, realpath as realpath5, stat } from "node:fs/promises";
5642
- import { extname, isAbsolute as isAbsolute3, join as join18, relative, resolve as resolve16, sep, win32 as win322 } from "node:path";
5827
+ import { lstat as lstat11, open as open5, readdir as readdir6, realpath as realpath7, stat } from "node:fs/promises";
5828
+ import { extname, isAbsolute as isAbsolute3, join as join19, relative, resolve as resolve16, sep, win32 as win322 } from "node:path";
5643
5829
  var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
5644
5830
  maxFiles: 128,
5645
5831
  maxFileBytes: 256 * 1024,
@@ -5792,7 +5978,7 @@ async function existingProjectRoot(value) {
5792
5978
  return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
5793
5979
  }
5794
5980
  try {
5795
- const root = await realpath5(resolve16(value));
5981
+ const root = await realpath7(resolve16(value));
5796
5982
  if (!(await stat(root)).isDirectory()) {
5797
5983
  return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
5798
5984
  }
@@ -5808,7 +5994,7 @@ async function existingProjectRoot(value) {
5808
5994
  }
5809
5995
  }
5810
5996
  async function existingSkillRoot(projectRoot, skillPath) {
5811
- const candidate = skillPath === "." ? projectRoot : join18(projectRoot, ...skillPath.split("/"));
5997
+ const candidate = skillPath === "." ? projectRoot : join19(projectRoot, ...skillPath.split("/"));
5812
5998
  try {
5813
5999
  await lstat11(candidate);
5814
6000
  } catch (cause) {
@@ -5819,7 +6005,7 @@ async function existingSkillRoot(projectRoot, skillPath) {
5819
6005
  }
5820
6006
  let root;
5821
6007
  try {
5822
- root = await realpath5(candidate);
6008
+ root = await realpath7(candidate);
5823
6009
  } catch (cause) {
5824
6010
  if (filesystemCode(cause) === "ELOOP") {
5825
6011
  return rejectScan("SKILL_SYMLINK_CYCLE", skillPath, "Skill \u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
@@ -5845,10 +6031,10 @@ async function existingSkillRoot(projectRoot, skillPath) {
5845
6031
  }
5846
6032
  async function assertEntrypoint(context) {
5847
6033
  const entryPath = reportChild(context.skillPath, "SKILL.md");
5848
- const candidate = join18(context.skillRoot, "SKILL.md");
6034
+ const candidate = join19(context.skillRoot, "SKILL.md");
5849
6035
  try {
5850
6036
  await lstat11(candidate);
5851
- const target = await realpath5(candidate);
6037
+ const target = await realpath7(candidate);
5852
6038
  if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
5853
6039
  rejectScan(
5854
6040
  "SKILL_SYMLINK_ESCAPE",
@@ -5892,7 +6078,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
5892
6078
  }
5893
6079
  let directory;
5894
6080
  try {
5895
- directory = await realpath5(actualDirectory);
6081
+ directory = await realpath7(actualDirectory);
5896
6082
  } catch (cause) {
5897
6083
  if (filesystemCode(cause) === "ELOOP") {
5898
6084
  return rejectScan("SKILL_SYMLINK_CYCLE", reportDirectory, "Skill \u76EE\u5F55\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
@@ -5912,7 +6098,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
5912
6098
  nextAncestors.add(directoryKey);
5913
6099
  let entries;
5914
6100
  try {
5915
- entries = await readdir5(directory, { withFileTypes: true, encoding: "utf8" });
6101
+ entries = await readdir6(directory, { withFileTypes: true, encoding: "utf8" });
5916
6102
  } catch (cause) {
5917
6103
  return rejectScan("SKILL_SCAN_IO_ERROR", reportDirectory, "\u65E0\u6CD5\u5217\u51FA Skill \u5B50\u76EE\u5F55\u3002", cause);
5918
6104
  }
@@ -5949,10 +6135,10 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
5949
6135
  }
5950
6136
  for (const entry2 of entries) {
5951
6137
  const childReportPath = reportChild(reportDirectory, entry2.name);
5952
- const childPath = join18(directory, entry2.name);
6138
+ const childPath = join19(directory, entry2.name);
5953
6139
  let target;
5954
6140
  try {
5955
- target = await realpath5(childPath);
6141
+ target = await realpath7(childPath);
5956
6142
  } catch (cause) {
5957
6143
  if (filesystemCode(cause) === "ELOOP") {
5958
6144
  return rejectScan("SKILL_SYMLINK_CYCLE", childReportPath, "Skill \u5185\u5BB9\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
@@ -6404,8 +6590,8 @@ async function scanExternalSkillPortability(options) {
6404
6590
 
6405
6591
  // packages/cli-core/src/push/preflight.ts
6406
6592
  import { createHash as createHash10 } from "node:crypto";
6407
- import { lstat as lstat12, open as open6, readdir as readdir6, realpath as realpath6 } from "node:fs/promises";
6408
- import { isAbsolute as isAbsolute4, join as join19, relative as relative2, resolve as resolve17, sep as sep2, win32 as win323 } from "node:path";
6593
+ import { lstat as lstat12, open as open6, readdir as readdir7, realpath as realpath8 } from "node:fs/promises";
6594
+ import { isAbsolute as isAbsolute4, join as join20, relative as relative2, resolve as resolve17, sep as sep2, win32 as win323 } from "node:path";
6409
6595
  var MEBIBYTE = 1024 * 1024;
6410
6596
  var DEFAULT_PUSH_MANIFEST_LIMITS = Object.freeze({
6411
6597
  maxFiles: 1e4,
@@ -6599,7 +6785,7 @@ async function canonicalProjectRoot(projectRoot) {
6599
6785
  });
6600
6786
  }
6601
6787
  try {
6602
- return await realpath6(absolute);
6788
+ return await realpath8(absolute);
6603
6789
  } catch (cause) {
6604
6790
  error({
6605
6791
  code: "PUSH_PROJECT_INVALID",
@@ -6610,7 +6796,7 @@ async function canonicalProjectRoot(projectRoot) {
6610
6796
  }
6611
6797
  }
6612
6798
  async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
6613
- const path = join19(projectRoot, filename);
6799
+ const path = join20(projectRoot, filename);
6614
6800
  let entry2;
6615
6801
  try {
6616
6802
  entry2 = await lstat12(path);
@@ -6680,7 +6866,7 @@ function parseCapturedProject(agentFile) {
6680
6866
  async function selectLockfile(projectRoot) {
6681
6867
  let names;
6682
6868
  try {
6683
- names = await readdir6(projectRoot);
6869
+ names = await readdir7(projectRoot);
6684
6870
  } catch (cause) {
6685
6871
  error({
6686
6872
  code: "PUSH_PROJECT_INVALID",
@@ -6789,7 +6975,7 @@ async function createManifest(options) {
6789
6975
  }
6790
6976
  let names;
6791
6977
  try {
6792
- names = await readdir6(directory);
6978
+ names = await readdir7(directory);
6793
6979
  } catch (cause) {
6794
6980
  error({
6795
6981
  code: "PUSH_PROJECT_INVALID",
@@ -6813,7 +6999,7 @@ async function createManifest(options) {
6813
6999
  sensitiveEntriesExcluded += 1;
6814
7000
  continue;
6815
7001
  }
6816
- const absolutePath = join19(directory, name);
7002
+ const absolutePath = join20(directory, name);
6817
7003
  let entry2;
6818
7004
  try {
6819
7005
  entry2 = await lstat12(absolutePath);
@@ -6847,7 +7033,7 @@ async function createManifest(options) {
6847
7033
  }
6848
7034
  let canonicalPath;
6849
7035
  try {
6850
- canonicalPath = await realpath6(absolutePath);
7036
+ canonicalPath = await realpath8(absolutePath);
6851
7037
  } catch (cause) {
6852
7038
  error({
6853
7039
  code: "PUSH_FILE_CHANGED",
@@ -7067,7 +7253,7 @@ async function assertCapturedConfigStable(options) {
7067
7253
  }
7068
7254
  let current;
7069
7255
  try {
7070
- const path = join19(options.projectRoot, options.filename);
7256
+ const path = join20(options.projectRoot, options.filename);
7071
7257
  const entry2 = await lstat12(path);
7072
7258
  if (!entry2.isFile() || entry2.isSymbolicLink()) {
7073
7259
  error({
@@ -7113,7 +7299,7 @@ async function createPushPreflight(options) {
7113
7299
  parseCapturedProject(initialAgentEntry);
7114
7300
  let initialDataEntry;
7115
7301
  try {
7116
- await lstat12(join19(projectRoot, DATA_ACCESS_FILENAME));
7302
+ await lstat12(join20(projectRoot, DATA_ACCESS_FILENAME));
7117
7303
  initialDataEntry = await captureConfigWithinLimit(projectRoot, limits.maxFileBytes, DATA_ACCESS_FILENAME);
7118
7304
  } catch (cause) {
7119
7305
  if (cause.code !== "ENOENT") throw cause;
@@ -7215,8 +7401,8 @@ async function createPushPreflight(options) {
7215
7401
 
7216
7402
  // packages/cli-core/src/push/bundle.ts
7217
7403
  import { createHash as createHash11 } from "node:crypto";
7218
- import { lstat as lstat13, readFile as readFile17, realpath as realpath7 } from "node:fs/promises";
7219
- import { join as join20, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
7404
+ import { lstat as lstat13, readFile as readFile18, realpath as realpath9 } from "node:fs/promises";
7405
+ import { join as join21, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
7220
7406
  import { Writable } from "node:stream";
7221
7407
  import { pipeline } from "node:stream/promises";
7222
7408
  import { createGzip } from "node:zlib";
@@ -7238,15 +7424,15 @@ function inside(root, target) {
7238
7424
  return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
7239
7425
  }
7240
7426
  async function capture(root, file) {
7241
- const absolute = join20(root, ...file.path.split("/"));
7427
+ const absolute = join21(root, ...file.path.split("/"));
7242
7428
  try {
7243
7429
  const entry2 = await lstat13(absolute);
7244
7430
  if (!entry2.isFile() || entry2.isSymbolicLink() || entry2.size !== file.bytes) {
7245
7431
  changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
7246
7432
  }
7247
- const canonical = await realpath7(absolute);
7433
+ const canonical = await realpath9(absolute);
7248
7434
  if (!inside(root, canonical)) changed(file.path, `\u63D0\u4EA4\u6587\u4EF6\u8D8A\u8FC7\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${file.path}`);
7249
- const contents = await readFile17(absolute);
7435
+ const contents = await readFile18(absolute);
7250
7436
  if (contents.byteLength !== file.bytes || digest3(contents) !== file.digest) {
7251
7437
  changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
7252
7438
  }
@@ -7262,7 +7448,7 @@ async function capture(root, file) {
7262
7448
  }
7263
7449
  }
7264
7450
  async function createPushSourceBundle(plan) {
7265
- const canonicalRoot = await realpath7(plan.projectRoot).catch((cause) => changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u76EE\u5F55\u5728\u6253\u5305\u524D\u5DF2\u4E0D\u53EF\u7528", cause));
7451
+ const canonicalRoot = await realpath9(plan.projectRoot).catch((cause) => changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u76EE\u5F55\u5728\u6253\u5305\u524D\u5DF2\u4E0D\u53EF\u7528", cause));
7266
7452
  if (canonicalRoot !== plan.projectRoot) {
7267
7453
  changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u6839\u76EE\u5F55\u5728\u9884\u68C0\u540E\u53D1\u751F\u53D8\u5316");
7268
7454
  }
@@ -7339,7 +7525,7 @@ var MODE_ROUTES = [
7339
7525
  ["direct", NEW_AGENT_CHOICES[0]],
7340
7526
  ["create", NEW_AGENT_CHOICES[1]]
7341
7527
  ];
7342
- function failure(message, io, includeHelp = false) {
7528
+ function failure2(message, io, includeHelp = false) {
7343
7529
  io.writeError(`buddy-cli: ${message}`);
7344
7530
  if (includeHelp) io.write("\u67E5\u770B\u7528\u6CD5\uFF1Abuddy-cli --help\uFF1B\u53C2\u6570\u8BF4\u660E\uFF1Abuddy-cli <\u547D\u4EE4> --help\uFF1B\u5F00\u53D1\u6307\u5F15\uFF1Abuddy-cli guide\u3002");
7345
7531
  return { exitCode: 2 };
@@ -7653,7 +7839,7 @@ async function resumeProjectArguments(args, cwd, options, io) {
7653
7839
  await assertInitializableBuddyTarget(root);
7654
7840
  let entry2;
7655
7841
  try {
7656
- entry2 = await lstat14(join21(root, "package.json"));
7842
+ entry2 = await lstat14(join22(root, "package.json"));
7657
7843
  } catch (cause) {
7658
7844
  if (cause.code === "ENOENT") {
7659
7845
  const draft = await readMetaDraft(root);
@@ -7725,13 +7911,13 @@ async function runBuddyCli(args, io, options = {}) {
7725
7911
  if (command === "guide") {
7726
7912
  if (rest.length === 0) io.write(AGENT_GUIDE);
7727
7913
  else if (rest.length === 1 && (rest[0] === "--help" || rest[0] === "-h")) io.write(GUIDE_HELP);
7728
- else return failure("guide \u4E0D\u63A5\u53D7\u5176\u4ED6\u53C2\u6570\uFF1B\u7528 buddy-cli guide --help \u67E5\u770B\u7528\u6CD5", io);
7914
+ else return failure2("guide \u4E0D\u63A5\u53D7\u5176\u4ED6\u53C2\u6570\uFF1B\u7528 buddy-cli guide --help \u67E5\u770B\u7528\u6CD5", io);
7729
7915
  return { exitCode: 0 };
7730
7916
  }
7731
7917
  if (command === "models") return await runModelsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
7732
7918
  if (command === "knowledge") return await runKnowledgeCommand(rest, io, options.cwd ?? process.cwd());
7733
7919
  if (command === "preview") {
7734
- return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
7920
+ return options.preview?.(rest, io) ?? failure2("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
7735
7921
  }
7736
7922
  if (command === "datasets") {
7737
7923
  return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
@@ -7773,14 +7959,14 @@ async function runBuddyCli(args, io, options = {}) {
7773
7959
  route ??= await chooseEntryRoute(io);
7774
7960
  return await runRoute(route, parsed, io, options);
7775
7961
  }
7776
- return failure(`\u672A\u77E5\u547D\u4EE4 ${command}`, io, true);
7962
+ return failure2(`\u672A\u77E5\u547D\u4EE4 ${command}`, io, true);
7777
7963
  } catch (cause) {
7778
7964
  if (cause instanceof PromptCancelledError) {
7779
7965
  io.writeError("\u5DF2\u53D6\u6D88\uFF1B\u5DF2\u4FDD\u5B58\u7684\u672C\u5730\u8349\u7A3F\u548C\u5DF2\u6709\u642D\u5B50\u8D44\u6599\u4F1A\u4FDD\u7559\uFF0C\u672A\u786E\u8BA4\u7684\u8D44\u6599\u4E0D\u4F1A\u767B\u8BB0\u3002");
7780
7966
  return { exitCode: 130 };
7781
7967
  }
7782
7968
  if (cause instanceof MetaPreparationError || cause instanceof CliUsageError || cause instanceof BuddyConfigValidationError || cause instanceof ProjectInitializationError || cause instanceof BuddyProjectContextError || cause instanceof CloudLifecycleError) {
7783
- return failure(cause.message, io, cause instanceof CliUsageError);
7969
+ return failure2(cause.message, io, cause instanceof CliUsageError);
7784
7970
  }
7785
7971
  throw cause;
7786
7972
  }
@@ -7943,6 +8129,7 @@ export {
7943
8129
  serializeBuddyAgentConfig,
7944
8130
  serializeBuddyDraftConfig,
7945
8131
  startBuddyDev,
8132
+ stopLocalDev,
7946
8133
  throwIfDevAborted,
7947
8134
  validateBuddyAgentConfig,
7948
8135
  validateBuddyConfig,