@my-life-buddies/cli 0.17.6 → 0.18.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
@@ -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);
@@ -2119,7 +2122,7 @@ ${KNOWLEDGE_HELP}`);
2119
2122
  }
2120
2123
 
2121
2124
  // packages/cli-core/src/index.ts
2122
- import { join as join21, resolve as resolve18 } from "node:path";
2125
+ import { join as join22, resolve as resolve18 } from "node:path";
2123
2126
 
2124
2127
  // packages/cli-core/src/init/creator.ts
2125
2128
  import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypto";
@@ -2158,7 +2161,7 @@ ${DEVELOPMENT_HANDOFF_SUMMARY}
2158
2161
 
2159
2162
  \u5F00\u53D1\u8005\u660E\u786E\u8981\u6C42\u81EA\u52A8\u4F53\u9A8C\u6216\u591A\u573A\u666F\u56DE\u5F52\u65F6\uFF0C\u6309\u6307\u5B9A\u8303\u56F4\u6267\u884C\uFF0C\u62A5\u544A\u7ED3\u679C\uFF0C\u4E0D\u628A\u6D4B\u8BD5\u6388\u6743\u6269\u5927\u4E3A\u6539\u9700\u6C42\u6216\u63D0\u5BA1\u6388\u6743\u3002push\u3001\u53D1\u5E03\u548C\u6536\u8D39\u64CD\u4F5C\u4E0D\u5C5E\u4E8E\u9ED8\u8BA4\u4EA4\u63A5\uFF0C\u9700\u8981\u53E6\u884C\u660E\u786E\u6388\u6743\u3002
2160
2163
 
2161
- \u9700\u8981\u91CD\u65B0\u6253\u5F00\u65F6\uFF0C\u4F7F\u7528 \`buddy-cli preview\`\uFF0C\u6216\u4ECE\u540E\u53F0\u300C\u83B7\u53D6\u8FDE\u63A5\u6307\u4EE4\u300D\u53D6\u5F97\u5E26\u642D\u5B50 ID \u548C\u73AF\u5883\u7684\u5B8C\u6574\u547D\u4EE4\uFF0C\u5728\u6B63\u786E\u5DE5\u7A0B\u76EE\u5F55\u6267\u884C\u3002\u540E\u53F0\u663E\u793A\u300C\u672A\u8FDE\u63A5\u672C\u5730\u5DE5\u7A0B\u300D\u300C\u5DF2\u8FDE\u63A5\u4F46 DEV \u672A\u542F\u52A8\u300D\u300CDEV \u8FD0\u884C\u4E2D\u300D\u3002\u5237\u65B0\u4E0D\u4F1A\u542F\u52A8 DEV\uFF1BCLI \u9000\u51FA\u4F1A\u505C\u6B62 DEV\u3002\u53EA\u6709\u540E\u53F0\u4E0D\u53EF\u7528\u65F6\u4F7F\u7528 \`buddy-cli preview --local\` \u5907\u7528\u5165\u53E3\uFF0C\u5E76\u660E\u786E\u8BF4\u660E\u5907\u7528\u539F\u56E0\u3002
2164
+ \u9700\u8981\u91CD\u65B0\u6253\u5F00\u65F6\uFF0C\u4F7F\u7528 \`buddy-cli preview\`\uFF0C\u6216\u4ECE\u540E\u53F0\u300C\u83B7\u53D6\u8FDE\u63A5\u6307\u4EE4\u300D\u53D6\u5F97\u5E26\u642D\u5B50 ID \u548C\u73AF\u5883\u7684\u5B8C\u6574\u547D\u4EE4\uFF0C\u5728\u6B63\u786E\u5DE5\u7A0B\u76EE\u5F55\u6267\u884C\u3002\u540E\u53F0\u590D\u5236\u7684\u547D\u4EE4\u5E26 \`--no-open\`\uFF0C\u6267\u884C\u540E\u8FD4\u56DE\u539F\u8C03\u8BD5\u9875\uFF0C\u4E0D\u91CD\u590D\u6253\u5F00\u7F51\u9875\uFF1B\u9875\u9762\u5DF2\u5173\u95ED\u65F6\u6253\u5F00\u7EC8\u7AEF\u6253\u5370\u7684\u5730\u5740\u3002\u540E\u53F0\u663E\u793A\u300C\u672A\u8FDE\u63A5\u672C\u5730\u5DE5\u7A0B\u300D\u300C\u5DF2\u8FDE\u63A5\u4F46 DEV \u672A\u542F\u52A8\u300D\u300CDEV \u8FD0\u884C\u4E2D\u300D\u3002\u5237\u65B0\u4E0D\u4F1A\u542F\u52A8 DEV\uFF1BCLI \u9000\u51FA\u4F1A\u505C\u6B62 DEV\u3002\u53EA\u6709\u540E\u53F0\u4E0D\u53EF\u7528\u65F6\u4F7F\u7528 \`buddy-cli preview --local\` \u5907\u7528\u5165\u53E3\uFF0C\u5E76\u660E\u786E\u8BF4\u660E\u5907\u7528\u539F\u56E0\u3002
2162
2165
 
2163
2166
  \u7528\u6237\u660E\u786E\u53EA\u505A\u8BBE\u8BA1\u3001\u521D\u59CB\u5316\u3001\u4EE3\u7801\u4FEE\u6539\u6216\u8981\u6C42\u6682\u505C\u65F6\uFF0C\u4EE5\u8BE5\u8303\u56F4\u4E3A\u51C6\uFF0C\u4E0D\u5F3A\u5236\u542F\u52A8\u9884\u89C8\u3002`;
2164
2167
 
@@ -2600,6 +2603,10 @@ Creator \u8DEF\u7EBF\u987B\u7B49\u56DB\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355
2600
2603
 
2601
2604
  ${KNOWLEDGE_GUIDE}
2602
2605
 
2606
+ ### \u672C\u5730\u8C03\u8BD5\u7684\u9000\u51FA\u548C\u6E05\u7406
2607
+
2608
+ \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
2609
+
2603
2610
  ### \u6309\u9700\u6C42\u68C0\u67E5\u9700\u8981\u54EA\u4E9B\u80FD\u529B
2604
2611
 
2605
2612
  \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
@@ -2956,7 +2963,7 @@ var CLI_HELP = `\u642D\u642D Developer Platform CLI
2956
2963
  models \u67E5\u770B\u6700\u65B0 Runtime \u7684\u6A21\u578B\u76EE\u5F55
2957
2964
  datasets \u67E5\u770B\u6700\u65B0 Runtime \u7684\u6570\u636E\u96C6\u76EE\u5F55
2958
2965
  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
2966
+ dev \u5728\u7EC8\u7AEF\u542F\u52A8\u5F00\u53D1\u7248\u642D\u5B50\uFF1Bdev stop \u505C\u6B62\u672C\u673A DEV
2960
2967
  preview \u8FDE\u63A5\u5DE5\u7A0B\u5E76\u6253\u5F00\u540E\u53F0\u5F00\u53D1\u8C03\u8BD5
2961
2968
  push \u4E0A\u4F20\u7A0B\u5E8F\u5305\u548C\u6539\u52A8\u8BF4\u660E
2962
2969
  status \u67E5\u770B\u4E91\u7AEF\u7248\u672C\u3001\u8FD0\u884C\u548C\u5BA1\u6838\u72B6\u6001
@@ -2971,7 +2978,7 @@ var CLI_HELP = `\u642D\u642D Developer Platform CLI
2971
2978
  \u76F4\u63A5\u8FD0\u884C buddy-cli \u53EF\u8BC6\u522B\u5F53\u524D\u5DE5\u7A0B\u6216\u5F15\u5BFC\u521B\u5EFA\u3002
2972
2979
  \u9ED8\u8BA4\u5E73\u53F0\uFF1A${DEFAULT_MLB_TEST_URL}\uFF1B\u5F00\u53D1\u8005\u540E\u53F0\uFF1A${DEFAULT_DEVELOPER_WEB_URL}\u3002
2973
2980
  \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
2981
+ \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
2982
  \u53C2\u6570\u4E0E\u793A\u4F8B: buddy-cli <\u547D\u4EE4> --help\uFF0C\u4F8B\u5982 buddy-cli init --help\u3002
2976
2983
  Coding Agent \u5F00\u59CB\u5DE5\u4F5C\u524D\u8BF7\u8BFB\u53D6 buddy-cli guide\u3002`;
2977
2984
  var INIT_HELP = `\u7528\u6CD5: buddy-cli init [\u53C2\u6570]
@@ -3019,7 +3026,7 @@ Skill \u7684\u5177\u4F53\u6D41\u7A0B\u8BFB\u53D6 HANDOFF.md \u4E2D\u7684\u6587\u
3019
3026
 
3020
3027
  \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
3028
  \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
3029
+ \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
3030
  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
3031
  \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
3032
 
@@ -3046,15 +3053,12 @@ ${IMPLEMENTATION_GUIDE}
3046
3053
 
3047
3054
  ${DEVELOPMENT_HANDOFF}`;
3048
3055
 
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";
3056
+ // packages/cli-core/src/dev/local-stop.ts
3057
+ import { execFile as execFile5 } from "node:child_process";
3058
+ import { promisify as promisify5 } from "node:util";
3059
+ import { readFile as readFile13, readdir as readdir4, realpath as realpath5, rmdir as rmdir3, unlink as unlink7 } from "node:fs/promises";
3060
+ import { join as join14 } from "node:path";
3061
+ import { setTimeout as delay } from "node:timers/promises";
3058
3062
 
3059
3063
  // packages/cli-core/src/dev/errors.ts
3060
3064
  var BuddyDevError = class extends Error {
@@ -3084,7 +3088,377 @@ function throwIfDevAborted(signal) {
3084
3088
  if (signal?.aborted) throw abortedDevError(signal);
3085
3089
  }
3086
3090
 
3091
+ // packages/cli-core/src/dev/process.ts
3092
+ import { promisify as promisify4 } from "node:util";
3093
+ import { spawn as spawnChild, execFile as execFile4 } from "node:child_process";
3094
+ var NodeAgentProcessLauncher = class {
3095
+ #spawn;
3096
+ constructor(options = {}) {
3097
+ this.#spawn = options.spawn ?? spawnChild;
3098
+ }
3099
+ async spawn(request) {
3100
+ let child;
3101
+ try {
3102
+ child = this.#spawn(request.command, [...request.args], {
3103
+ cwd: request.cwd,
3104
+ env: { ...request.env },
3105
+ shell: false,
3106
+ detached: request.detached,
3107
+ windowsHide: false,
3108
+ stdio: ["ignore", "pipe", "pipe"]
3109
+ });
3110
+ } catch (cause) {
3111
+ throw new BuddyDevError({
3112
+ code: "DEV_AGENT_SPAWN_FAILED",
3113
+ phase: "launch",
3114
+ message: `\u65E0\u6CD5\u542F\u52A8 Agent \u7A0B\u5E8F ${request.command}\u3002`,
3115
+ cause
3116
+ });
3117
+ }
3118
+ let hasExited = false;
3119
+ let spawned = false;
3120
+ const exit = new Promise((resolve19) => {
3121
+ child.once("error", (error2) => {
3122
+ if (!spawned) {
3123
+ hasExited = true;
3124
+ resolve19({ code: null, signal: null, error: error2 });
3125
+ }
3126
+ });
3127
+ child.once("exit", (code2, signal) => {
3128
+ hasExited = true;
3129
+ resolve19({ code: code2, signal });
3130
+ });
3131
+ });
3132
+ child.stdout?.on("data", (chunk) => {
3133
+ try {
3134
+ request.onStdout?.(String(chunk));
3135
+ } catch {
3136
+ }
3137
+ });
3138
+ child.stderr?.on("data", (chunk) => {
3139
+ try {
3140
+ request.onStderr?.(String(chunk));
3141
+ } catch {
3142
+ }
3143
+ });
3144
+ try {
3145
+ await new Promise((resolve19, reject) => {
3146
+ const onSpawn = () => {
3147
+ spawned = true;
3148
+ child.off("error", onError);
3149
+ resolve19();
3150
+ };
3151
+ const onError = (error2) => {
3152
+ child.off("spawn", onSpawn);
3153
+ reject(error2);
3154
+ };
3155
+ child.once("spawn", onSpawn);
3156
+ child.once("error", onError);
3157
+ });
3158
+ } catch (cause) {
3159
+ throw new BuddyDevError({
3160
+ code: "DEV_AGENT_SPAWN_FAILED",
3161
+ phase: "launch",
3162
+ message: `\u65E0\u6CD5\u542F\u52A8 Agent \u7A0B\u5E8F ${request.command}\u3002`,
3163
+ cause
3164
+ });
3165
+ }
3166
+ const pid = child.pid;
3167
+ if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 0) {
3168
+ try {
3169
+ child.kill("SIGKILL");
3170
+ } catch {
3171
+ }
3172
+ throw new BuddyDevError({
3173
+ code: "DEV_AGENT_SPAWN_FAILED",
3174
+ phase: "launch",
3175
+ message: "Agent \u8FDB\u7A0B\u6CA1\u6709\u6709\u6548 PID\u3002"
3176
+ });
3177
+ }
3178
+ return {
3179
+ pid,
3180
+ exit,
3181
+ hasExited: () => hasExited,
3182
+ kill: (signal) => child.kill(signal)
3183
+ };
3184
+ }
3185
+ };
3186
+ function waitForExit(process2, timeoutMs) {
3187
+ if (process2.hasExited()) return Promise.resolve(true);
3188
+ return new Promise((resolve19) => {
3189
+ const timeout = setTimeout(() => resolve19(false), timeoutMs);
3190
+ process2.exit.then(() => {
3191
+ clearTimeout(timeout);
3192
+ resolve19(true);
3193
+ });
3194
+ });
3195
+ }
3196
+ var PosixProcessTreeController = class {
3197
+ #killGroup;
3198
+ #isGroupAlive;
3199
+ constructor(options = {}) {
3200
+ this.#killGroup = options.killGroup ?? ((pid, signal) => process.kill(-pid, signal));
3201
+ this.#isGroupAlive = options.isGroupAlive ?? ((pid) => {
3202
+ try {
3203
+ process.kill(-pid, 0);
3204
+ return true;
3205
+ } catch (cause) {
3206
+ return typeof cause === "object" && cause !== null && "code" in cause && cause.code !== "ESRCH";
3207
+ }
3208
+ });
3209
+ }
3210
+ async terminate(child, options = {}) {
3211
+ if (!Number.isSafeInteger(child.pid) || child.pid <= 0) {
3212
+ throw new BuddyDevError({
3213
+ code: "DEV_STOP_FAILED",
3214
+ phase: "stop",
3215
+ message: "\u65E0\u6CD5\u505C\u6B62\u6CA1\u6709\u6709\u6548 PID \u7684 Agent \u8FDB\u7A0B\u6811\u3002"
3216
+ });
3217
+ }
3218
+ const gracePeriodMs = options.gracePeriodMs ?? 5e3;
3219
+ let gracefulSent = false;
3220
+ let gracefulWasGroup = false;
3221
+ try {
3222
+ this.#killGroup(child.pid, "SIGTERM");
3223
+ gracefulSent = true;
3224
+ gracefulWasGroup = true;
3225
+ } catch {
3226
+ try {
3227
+ gracefulSent = child.kill("SIGTERM");
3228
+ } catch {
3229
+ gracefulSent = false;
3230
+ }
3231
+ }
3232
+ if (gracefulSent && await (gracefulWasGroup ? waitForGroupExit(child.pid, this.#isGroupAlive, gracePeriodMs) : waitForExit(child, gracePeriodMs))) return;
3233
+ let forceSent = false;
3234
+ let forceWasGroup = false;
3235
+ try {
3236
+ this.#killGroup(child.pid, "SIGKILL");
3237
+ forceSent = true;
3238
+ forceWasGroup = true;
3239
+ } catch {
3240
+ try {
3241
+ forceSent = child.kill("SIGKILL");
3242
+ } catch (cause) {
3243
+ if (!child.hasExited()) {
3244
+ throw new BuddyDevError({
3245
+ code: "DEV_STOP_FAILED",
3246
+ phase: "stop",
3247
+ message: `\u65E0\u6CD5\u505C\u6B62 Agent \u8FDB\u7A0B\u6811\uFF08PID ${child.pid}\uFF09\u3002`,
3248
+ cause
3249
+ });
3250
+ }
3251
+ }
3252
+ }
3253
+ if (!forceSent && !child.hasExited()) {
3254
+ throw new BuddyDevError({
3255
+ code: "DEV_STOP_FAILED",
3256
+ phase: "stop",
3257
+ message: `\u65E0\u6CD5\u5411 Agent \u8FDB\u7A0B\u6811\u53D1\u9001\u5F3A\u5236\u505C\u6B62\u4FE1\u53F7\uFF08PID ${child.pid}\uFF09\u3002`
3258
+ });
3259
+ }
3260
+ const forceStopped = forceWasGroup ? await waitForGroupExit(child.pid, this.#isGroupAlive, Math.min(gracePeriodMs, 1e3)) : await waitForExit(child, Math.min(gracePeriodMs, 1e3));
3261
+ if (!forceStopped) {
3262
+ throw new BuddyDevError({
3263
+ code: "DEV_STOP_FAILED",
3264
+ phase: "stop",
3265
+ message: `Agent \u8FDB\u7A0B\u6811\u6CA1\u6709\u5728\u5F3A\u5236\u505C\u6B62\u540E\u9000\u51FA\uFF08PID ${child.pid}\uFF09\u3002`
3266
+ });
3267
+ }
3268
+ }
3269
+ };
3270
+ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
3271
+ const deadline = Date.now() + timeoutMs;
3272
+ while (isGroupAlive(pid)) {
3273
+ const remaining = deadline - Date.now();
3274
+ if (remaining <= 0) return false;
3275
+ await new Promise((resolve19) => setTimeout(resolve19, Math.min(25, remaining)));
3276
+ }
3277
+ return true;
3278
+ }
3279
+ function processTreeControllerFor(platform) {
3280
+ if (platform === "darwin") return new PosixProcessTreeController();
3281
+ throw new BuddyDevError({
3282
+ code: "DEV_OS_UNSUPPORTED",
3283
+ phase: "launch",
3284
+ message: "buddy-cli dev V1 \u76EE\u524D\u53EA\u652F\u6301 macOS \u5F00\u53D1\u73AF\u5883\u3002"
3285
+ });
3286
+ }
3287
+ async function processIdentity(pid) {
3288
+ try {
3289
+ 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;
3290
+ } catch {
3291
+ return void 0;
3292
+ }
3293
+ }
3294
+
3295
+ // packages/cli-core/src/dev/instance-lock.ts
3296
+ import { createServer as createServer2 } from "node:http";
3297
+ import { realpath as realpath4 } from "node:fs/promises";
3298
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
3299
+ import { homedir as homedir3 } from "node:os";
3300
+ import { join as join13 } from "node:path";
3301
+ function alreadyRunningError() {
3302
+ 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";
3303
+ return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
3304
+ }
3305
+ function devLockPath(options) {
3306
+ return join13(
3307
+ options.directory ?? join13(homedir3(), ".buddy", "dev-locks"),
3308
+ createHash5("sha256").update(`${new URL(options.gatewayUrl).origin}
3309
+ ${options.devAgentId}`).digest("hex")
3310
+ );
3311
+ }
3312
+ async function acquireDevInstanceLock(options) {
3313
+ const token = randomBytes2(32).toString("hex");
3314
+ const server = options.onStop ? createServer2(async (request, response) => {
3315
+ if (request.method !== "POST" || request.url !== "/stop" || request.headers.origin || request.headers.cookie || request.headers.authorization !== `Bearer ${token}`) {
3316
+ response.writeHead(403);
3317
+ response.end();
3318
+ return;
3319
+ }
3320
+ try {
3321
+ await options.onStop();
3322
+ response.writeHead(200);
3323
+ response.end();
3324
+ } catch {
3325
+ response.writeHead(500);
3326
+ response.end();
3327
+ }
3328
+ }) : void 0;
3329
+ try {
3330
+ if (server) await new Promise((resolve19, reject) => {
3331
+ server.once("error", reject);
3332
+ server.listen(0, "127.0.0.1", () => {
3333
+ server.off("error", reject);
3334
+ resolve19();
3335
+ });
3336
+ });
3337
+ const address = server?.address();
3338
+ const path = devLockPath(options);
3339
+ const lock = await acquireProcessLock({
3340
+ directory: options.directory ?? join13(homedir3(), ".buddy", "dev-locks"),
3341
+ key: path.split("/").at(-1),
3342
+ busyError: alreadyRunningError,
3343
+ ownerData: {
3344
+ ...options.projectRoot ? { projectRoot: await realpath4(options.projectRoot) } : {},
3345
+ ...address && typeof address !== "string" ? { control: { port: address.port, token } } : {}
3346
+ },
3347
+ runtimeData: async (pid) => ({ runtimeIdentity: await processIdentity(pid) })
3348
+ });
3349
+ return {
3350
+ attachRuntime: (pid) => lock.attachRuntime(pid),
3351
+ closeControl: () => {
3352
+ server?.close();
3353
+ },
3354
+ async release() {
3355
+ await lock.release();
3356
+ server?.close();
3357
+ }
3358
+ };
3359
+ } catch (error2) {
3360
+ server?.close();
3361
+ throw error2;
3362
+ }
3363
+ }
3364
+
3365
+ // packages/cli-core/src/dev/local-stop.ts
3366
+ var exec = promisify5(execFile5);
3367
+ var alive2 = (pid) => {
3368
+ try {
3369
+ process.kill(pid, 0);
3370
+ return true;
3371
+ } catch (e) {
3372
+ return e.code !== "ESRCH";
3373
+ }
3374
+ };
3375
+ var validPid2 = (pid) => Number.isSafeInteger(pid) && pid > 1;
3376
+ var failure = (message) => new BuddyDevError({ code: "DEV_STOP_FAILED", phase: "stop", message });
3377
+ async function stopLocalDev(options) {
3378
+ const path = devLockPath(options);
3379
+ let names;
3380
+ try {
3381
+ names = await readdir4(path);
3382
+ } catch (error2) {
3383
+ if (error2.code === "ENOENT") return { stopped: false };
3384
+ throw error2;
3385
+ }
3386
+ 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");
3387
+ const ownerPath = join14(path, names[0]);
3388
+ const source = await readFile13(ownerPath, "utf8");
3389
+ const owner = JSON.parse(source);
3390
+ if (!validPid2(owner.pid) || owner.runtimePid !== void 0 && !validPid2(owner.runtimePid)) throw failure("DEV \u9501\u4E2D\u7684 PID \u65E0\u6548\u3002");
3391
+ const root = await realpath5(options.projectRoot);
3392
+ 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`);
3393
+ if (alive2(owner.pid)) {
3394
+ const control = owner.control;
3395
+ if (!control || !Number.isInteger(control.port) || control.port < 1 || control.port > 65535 || !/^[a-f0-9]{64}$/u.test(control.token)) {
3396
+ 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`);
3397
+ }
3398
+ try {
3399
+ 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) });
3400
+ if (!response.ok) throw new Error("stop failed");
3401
+ return { stopped: true };
3402
+ } catch {
3403
+ 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`);
3404
+ }
3405
+ }
3406
+ const pid = owner.runtimePid;
3407
+ if (pid && alive2(pid)) {
3408
+ const identity = await processIdentity(pid);
3409
+ 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`);
3410
+ const [uid, group] = identity.split(/\s+/u);
3411
+ 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`);
3412
+ if (!owner.runtimeIdentity) {
3413
+ const cwd = (await exec("/usr/sbin/lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"])).stdout.split("\n").find((line) => line.startsWith("n"))?.slice(1);
3414
+ 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`);
3415
+ }
3416
+ 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");
3417
+ try {
3418
+ process.kill(-pid, "SIGTERM");
3419
+ } catch (error2) {
3420
+ if (error2.code !== "ESRCH") throw error2;
3421
+ }
3422
+ const groupAlive = () => {
3423
+ try {
3424
+ process.kill(-pid, 0);
3425
+ return true;
3426
+ } catch (e) {
3427
+ return e.code !== "ESRCH";
3428
+ }
3429
+ };
3430
+ const deadline = Date.now() + 5e3;
3431
+ while (groupAlive() && Date.now() < deadline) await delay(25);
3432
+ if (groupAlive()) {
3433
+ const current = await processIdentity(pid);
3434
+ 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");
3435
+ process.kill(-pid, "SIGKILL");
3436
+ const forceDeadline = Date.now() + 1e3;
3437
+ while (groupAlive() && Date.now() < forceDeadline) await delay(25);
3438
+ if (groupAlive()) throw failure(`Runtime \u8FDB\u7A0B\u7EC4 ${pid} \u5C1A\u672A\u786E\u8BA4\u9000\u51FA\uFF0C\u4FDD\u7559\u8FDB\u7A0B\u9501\u3002`);
3439
+ }
3440
+ }
3441
+ 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`);
3442
+ try {
3443
+ if (await readFile13(ownerPath, "utf8") === source) await unlink7(ownerPath);
3444
+ } catch (error2) {
3445
+ if (error2.code !== "ENOENT") throw error2;
3446
+ }
3447
+ await rmdir3(path).catch((error2) => {
3448
+ if (!["ENOENT", "ENOTEMPTY", "EEXIST"].includes(error2.code)) throw error2;
3449
+ });
3450
+ return { stopped: true };
3451
+ }
3452
+
3453
+ // packages/cli-core/src/commands/dev.ts
3454
+ import { resolve as resolve14 } from "node:path";
3455
+
3087
3456
  // packages/cli-core/src/dev/project-env.ts
3457
+ import { constants } from "node:fs";
3458
+ import { open as open4, rename as rename9, unlink as unlink8 } from "node:fs/promises";
3459
+ import { randomUUID as randomUUID7 } from "node:crypto";
3460
+ import { join as join15 } from "node:path";
3461
+ import { parseEnv } from "node:util";
3088
3462
  function invalid(message) {
3089
3463
  return new BuddyDevError({ code: "DEV_CONFIG_INVALID", phase: "config", message });
3090
3464
  }
@@ -3104,7 +3478,7 @@ async function readLocalFile(path) {
3104
3478
  }
3105
3479
  }
3106
3480
  async function readProjectEnvironment(projectRoot) {
3107
- const source = await readLocalFile(join13(projectRoot, ".env"));
3481
+ const source = await readLocalFile(join15(projectRoot, ".env"));
3108
3482
  let values;
3109
3483
  try {
3110
3484
  values = parseEnv(source);
@@ -3183,13 +3557,13 @@ async function replaceFile2(path, contents, mode) {
3183
3557
  await rename9(temporary, path);
3184
3558
  } finally {
3185
3559
  await file?.close();
3186
- await unlink7(temporary).catch((cause) => {
3560
+ await unlink8(temporary).catch((cause) => {
3187
3561
  if (cause.code !== "ENOENT") throw cause;
3188
3562
  });
3189
3563
  }
3190
3564
  }
3191
3565
  async function writeProjectEnvironment(projectRoot, values) {
3192
- const envPath = join13(projectRoot, ".env"), ignorePath = join13(projectRoot, ".gitignore");
3566
+ const envPath = join15(projectRoot, ".env"), ignorePath = join15(projectRoot, ".gitignore");
3193
3567
  const contents = updateValues(await readLocalFile(envPath), { ...values, BUDDY_ID: void 0 });
3194
3568
  const ignored = await readLocalFile(ignorePath);
3195
3569
  if (ignored.trimEnd().split(/\r?\n/u).at(-1) !== "/.env") {
@@ -3468,42 +3842,23 @@ function buildAgentEnvironment(options) {
3468
3842
 
3469
3843
  // packages/cli-core/src/dev/orchestrator.ts
3470
3844
  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";
3845
+ import { readFile as readFile17 } from "node:fs/promises";
3846
+ import { join as join18, resolve as resolve12 } from "node:path";
3473
3847
 
3474
3848
  // packages/cli-core/src/dev/reload.ts
3475
3849
  import { watch } from "node:fs";
3476
3850
  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";
3851
+ import { readFile as readFile14, readdir as readdir5 } from "node:fs/promises";
3852
+ import { join as join16, resolve as resolve9 } from "node:path";
3479
3853
  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
3854
  var digest2 = (contents) => createHash6("sha256").update(contents).digest("hex");
3500
3855
  async function snapshot(root, prefix = "", result = /* @__PURE__ */ new Map()) {
3501
- for (const entry2 of await readdir4(join15(root, prefix), { withFileTypes: true })) {
3856
+ for (const entry2 of await readdir5(join16(root, prefix), { withFileTypes: true })) {
3502
3857
  const path = prefix ? `${prefix}/${entry2.name}` : entry2.name;
3503
3858
  if (!relevant(path)) continue;
3504
3859
  try {
3505
3860
  if (entry2.isDirectory()) await snapshot(root, path, result);
3506
- else if (entry2.isFile()) result.set(path, digest2(await readFile13(join15(root, path))));
3861
+ else if (entry2.isFile()) result.set(path, digest2(await readFile14(join16(root, path))));
3507
3862
  } catch (cause) {
3508
3863
  if (cause.code !== "ENOENT") throw cause;
3509
3864
  }
@@ -3593,7 +3948,7 @@ var ReloadingDevSession = class {
3593
3948
  const path = String(name);
3594
3949
  if (!relevant(path)) return;
3595
3950
  if (path === ".env") {
3596
- void readFile13(join15(this.projectRoot, path), "utf8").then((contents) => {
3951
+ void readFile14(join16(this.projectRoot, path), "utf8").then((contents) => {
3597
3952
  if (contents !== this.#environment) {
3598
3953
  this.#environment = contents;
3599
3954
  this.#changed();
@@ -3615,8 +3970,10 @@ var ReloadingDevSession = class {
3615
3970
  this.#initializing = false;
3616
3971
  this.#watcher?.close();
3617
3972
  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();
3973
+ if (cause instanceof BuddyDevError && cause.code === "DEV_STOP_FAILED") {
3974
+ this.#unsafeToRelease = true;
3975
+ this.#lock?.closeControl?.();
3976
+ } else await this.#lock?.release();
3620
3977
  throw cause;
3621
3978
  }
3622
3979
  }
@@ -3648,7 +4005,7 @@ var ReloadingDevSession = class {
3648
4005
  acquireLock: async (input) => {
3649
4006
  const origin = new URL(input.gatewayUrl).origin;
3650
4007
  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);
4008
+ this.#lock ??= await acquireDevInstanceLock({ ...input, projectRoot: this.projectRoot, onStop: () => this.stop() });
3652
4009
  this.#lockOrigin = origin;
3653
4010
  return { attachRuntime: (pid) => this.#lock.attachRuntime(pid), async release() {
3654
4011
  } };
@@ -3673,6 +4030,7 @@ var ReloadingDevSession = class {
3673
4030
  this.#unsafeToRelease = true;
3674
4031
  this.#ended = true;
3675
4032
  this.#watcher?.close();
4033
+ this.#lock?.closeControl?.();
3676
4034
  this.#resolve(completion);
3677
4035
  }
3678
4036
  }
@@ -3726,6 +4084,7 @@ var ReloadingDevSession = class {
3726
4084
  this.#unsafeToRelease = true;
3727
4085
  this.#ended = true;
3728
4086
  this.#watcher?.close();
4087
+ this.#lock?.closeControl?.();
3729
4088
  this.#resolve({ state: "failed", error: error2 });
3730
4089
  return;
3731
4090
  }
@@ -3759,6 +4118,7 @@ var ReloadingDevSession = class {
3759
4118
  this.#emit("stopped");
3760
4119
  this.#resolve({ state: "stopped" });
3761
4120
  } catch (cause) {
4121
+ this.#lock?.closeControl?.();
3762
4122
  const error2 = this.#error(cause);
3763
4123
  this.#emit("failed");
3764
4124
  this.#resolve({ state: "failed", error: error2 });
@@ -3771,7 +4131,7 @@ function startReloadingDev(options, startProcess) {
3771
4131
  }
3772
4132
 
3773
4133
  // packages/cli-core/src/config/runtime-policy.ts
3774
- import { readFile as readFile14, realpath as realpath4 } from "node:fs/promises";
4134
+ import { readFile as readFile15, realpath as realpath6 } from "node:fs/promises";
3775
4135
  import { createRequire as createRequire2 } from "node:module";
3776
4136
  import { resolve as resolve10 } from "node:path";
3777
4137
  var RuntimeRequirementError = class extends Error {
@@ -3808,14 +4168,14 @@ async function requireLatestRuntime(root, options = {}) {
3808
4168
  }
3809
4169
  try {
3810
4170
  assertRequiredRuntime(await projectRuntimeVersion(root), latest);
3811
- const installedPath = await realpath4(resolve10(root, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"));
4171
+ const installedPath = await realpath6(resolve10(root, "node_modules", BUDDY_RUNTIME_PACKAGE, "package.json"));
3812
4172
  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"));
4173
+ if (await realpath6(fromProject) !== installedPath) throw new Error("Runtime \u5FC5\u987B\u5B89\u88C5\u5728\u5F53\u524D\u5DE5\u7A0B\u4E2D");
4174
+ const installed2 = JSON.parse(await readFile15(installedPath, "utf8"));
3815
4175
  if (installed2.version !== latest) throw new Error(`Runtime \u5B9E\u9645\u5B89\u88C5 ${installed2.version}\uFF0C\u8981\u6C42 ${latest}`);
3816
4176
  let lock;
3817
4177
  try {
3818
- lock = await readFile14(resolve10(root, "package-lock.json"), "utf8");
4178
+ lock = await readFile15(resolve10(root, "package-lock.json"), "utf8");
3819
4179
  } catch (cause) {
3820
4180
  if (cause.code !== "ENOENT") throw cause;
3821
4181
  }
@@ -3835,8 +4195,8 @@ import { createRequire as createRequire3 } from "node:module";
3835
4195
  import { dirname as dirname4, resolve as resolve11 } from "node:path";
3836
4196
 
3837
4197
  // 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";
4198
+ import { lstat as lstat10, readFile as readFile16 } from "node:fs/promises";
4199
+ import { join as join17 } from "node:path";
3840
4200
  var DATA_ACCESS_FILENAME = "data-access.json";
3841
4201
  function parseDataAccessRequirements(source) {
3842
4202
  if (source === void 0) return [];
@@ -3864,10 +4224,10 @@ function parseDataAccessRequirements(source) {
3864
4224
  });
3865
4225
  }
3866
4226
  async function readDataAccessRequirements(projectRoot) {
3867
- const path = join16(projectRoot, DATA_ACCESS_FILENAME);
4227
+ const path = join17(projectRoot, DATA_ACCESS_FILENAME);
3868
4228
  try {
3869
4229
  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"));
4230
+ return parseDataAccessRequirements(await readFile16(path, "utf8"));
3871
4231
  } catch (cause) {
3872
4232
  if (cause.code === "ENOENT") return [];
3873
4233
  throw cause;
@@ -3920,202 +4280,6 @@ async function officialAgentLaunchPlan(input) {
3920
4280
  });
3921
4281
  }
3922
4282
 
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
4283
  // packages/cli-core/src/dev/orchestrator.ts
4120
4284
  var DEFAULT_ONLINE_TIMEOUT_MS = 3e4;
4121
4285
  var DEFAULT_POLL_INTERVAL_MS = 200;
@@ -4194,7 +4358,7 @@ async function registerDevAgent(options) {
4194
4358
  });
4195
4359
  }
4196
4360
  }
4197
- function delay(milliseconds, signal) {
4361
+ function delay2(milliseconds, signal) {
4198
4362
  if (signal.aborted) return Promise.reject(abortedDevError(signal));
4199
4363
  return new Promise((resolveDelay, reject) => {
4200
4364
  const onAbort = () => {
@@ -4234,7 +4398,7 @@ async function waitForAgentOnline(options) {
4234
4398
  if (cause instanceof BuddyDevError && !cause.retryable) throw cause;
4235
4399
  lastError = cause;
4236
4400
  }
4237
- await delay(Math.min(options.pollIntervalMs, Math.max(1, deadline - Date.now())), options.signal);
4401
+ await delay2(Math.min(options.pollIntervalMs, Math.max(1, deadline - Date.now())), options.signal);
4238
4402
  }
4239
4403
  }
4240
4404
  function agentExitedError(exit, stderr = "") {
@@ -4386,7 +4550,7 @@ async function startBuddyDevProcess(options, hooks) {
4386
4550
  let config;
4387
4551
  let configSource;
4388
4552
  try {
4389
- configSource = await readFile16(join17(projectRoot, BUDDY_PACKAGE_FILENAME), "utf8");
4553
+ configSource = await readFile17(join18(projectRoot, BUDDY_PACKAGE_FILENAME), "utf8");
4390
4554
  config = combineBuddyProject(parseBuddyAgentConfig(configSource));
4391
4555
  } catch (cause) {
4392
4556
  throw new BuddyDevError({
@@ -4709,8 +4873,11 @@ var DevCommandUsageError = class extends Error {
4709
4873
  };
4710
4874
  var DEV_COMMAND_HELP = `\u7528\u6CD5:
4711
4875
  buddy-cli dev [--app-server <App Server URL>] [--directory <\u9879\u76EE\u76EE\u5F55>] [--refresh-token]
4876
+ buddy-cli dev stop [--directory <\u9879\u76EE\u76EE\u5F55>] [--app-server <App Server URL>] [--buddy-id <\u642D\u5B50 ID>]
4712
4877
 
4713
4878
  \u8BF4\u660E:
4879
+ 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
4880
+ \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
4881
  \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
4882
  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
4883
  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 +4893,9 @@ function setOnce(current, value, flag2) {
4726
4893
  return value;
4727
4894
  }
4728
4895
  function parseDevArguments(args) {
4896
+ const stop = args[0] === "stop";
4897
+ if (stop) args = args.slice(1);
4898
+ let buddyId;
4729
4899
  let directory = ".";
4730
4900
  let directorySet = false;
4731
4901
  let appServer;
@@ -4746,6 +4916,10 @@ function parseDevArguments(args) {
4746
4916
  const value = valueAfter(args, index, flag2 ?? "\u53C2\u6570");
4747
4917
  index += 1;
4748
4918
  switch (flag2) {
4919
+ case "--buddy-id":
4920
+ if (!stop) throw new DevCommandUsageError("--buddy-id \u4EC5\u7528\u4E8E dev stop \u8EAB\u4EFD\u6821\u9A8C");
4921
+ buddyId = setOnce(buddyId, value, flag2);
4922
+ break;
4749
4923
  case "--directory":
4750
4924
  if (directorySet) throw new DevCommandUsageError("--directory \u4E0D\u80FD\u91CD\u590D");
4751
4925
  directory = value;
@@ -4758,7 +4932,8 @@ function parseDevArguments(args) {
4758
4932
  throw new DevCommandUsageError(`\u672A\u77E5 dev \u53C2\u6570 ${flag2}`);
4759
4933
  }
4760
4934
  }
4761
- return { directory, appServer, help, refreshToken };
4935
+ if (stop && refreshToken) throw new DevCommandUsageError("dev stop \u4E0D\u652F\u6301 --refresh-token");
4936
+ return { stop, buddyId, directory, appServer, help, refreshToken };
4762
4937
  }
4763
4938
  function appServerUrl(override, env) {
4764
4939
  try {
@@ -4792,6 +4967,13 @@ async function runDevCommand(args, io, options = {}) {
4792
4967
  const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
4793
4968
  const environment = options.env ?? process.env;
4794
4969
  const url = appServerUrl(parsed.appServer, environment);
4970
+ if (parsed.stop) {
4971
+ const manifest = await (options.readManifest ?? readBuddyProjectManifest)(root);
4972
+ 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`);
4973
+ const result = await stopLocalDev({ gatewayUrl: url, devAgentId: manifest.buddyId, projectRoot: root });
4974
+ 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");
4975
+ return { exitCode: 0 };
4976
+ }
4795
4977
  const authorizationInput = { appServerUrl: url, ...parsed.refreshToken ? { refreshToken: true } : {} };
4796
4978
  if (!options.authorize) {
4797
4979
  throw new DevCommandUsageError("\u5F53\u524D CLI Host \u5C1A\u672A\u914D\u7F6E\u642D\u642D\u8D26\u53F7\u767B\u5F55 Adapter");
@@ -5308,7 +5490,7 @@ var HttpDeveloperPlatformClient = class {
5308
5490
  };
5309
5491
 
5310
5492
  // packages/cli-core/src/cloud/mock-client.ts
5311
- import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
5493
+ import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
5312
5494
  var MockDeveloperPlatformClient = class {
5313
5495
  #now;
5314
5496
  #buddies = /* @__PURE__ */ new Map();
@@ -5317,7 +5499,7 @@ var MockDeveloperPlatformClient = class {
5317
5499
  this.#now = options.now ?? Date.now;
5318
5500
  }
5319
5501
  async createBuddy(request) {
5320
- const id = `b_mock_${randomBytes2(8).toString("hex")}`;
5502
+ const id = `b_mock_${randomBytes3(8).toString("hex")}`;
5321
5503
  const value = this.#value({
5322
5504
  id,
5323
5505
  meta: request.meta,
@@ -5326,7 +5508,7 @@ var MockDeveloperPlatformClient = class {
5326
5508
  released: false,
5327
5509
  online: false,
5328
5510
  updatedAt: this.#now(),
5329
- token: `bt_mock_${randomBytes2(16).toString("hex")}`
5511
+ token: `bt_mock_${randomBytes3(16).toString("hex")}`
5330
5512
  });
5331
5513
  this.#buddies.set(id, value);
5332
5514
  this.#packages.set(id, []);
@@ -5638,8 +5820,8 @@ async function runCloudCommand(command, args, io, options = {}) {
5638
5820
  var CLOUD_COMMAND_HELP = HELP;
5639
5821
 
5640
5822
  // 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";
5823
+ import { lstat as lstat11, open as open5, readdir as readdir6, realpath as realpath7, stat } from "node:fs/promises";
5824
+ import { extname, isAbsolute as isAbsolute3, join as join19, relative, resolve as resolve16, sep, win32 as win322 } from "node:path";
5643
5825
  var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
5644
5826
  maxFiles: 128,
5645
5827
  maxFileBytes: 256 * 1024,
@@ -5792,7 +5974,7 @@ async function existingProjectRoot(value) {
5792
5974
  return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
5793
5975
  }
5794
5976
  try {
5795
- const root = await realpath5(resolve16(value));
5977
+ const root = await realpath7(resolve16(value));
5796
5978
  if (!(await stat(root)).isDirectory()) {
5797
5979
  return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
5798
5980
  }
@@ -5808,7 +5990,7 @@ async function existingProjectRoot(value) {
5808
5990
  }
5809
5991
  }
5810
5992
  async function existingSkillRoot(projectRoot, skillPath) {
5811
- const candidate = skillPath === "." ? projectRoot : join18(projectRoot, ...skillPath.split("/"));
5993
+ const candidate = skillPath === "." ? projectRoot : join19(projectRoot, ...skillPath.split("/"));
5812
5994
  try {
5813
5995
  await lstat11(candidate);
5814
5996
  } catch (cause) {
@@ -5819,7 +6001,7 @@ async function existingSkillRoot(projectRoot, skillPath) {
5819
6001
  }
5820
6002
  let root;
5821
6003
  try {
5822
- root = await realpath5(candidate);
6004
+ root = await realpath7(candidate);
5823
6005
  } catch (cause) {
5824
6006
  if (filesystemCode(cause) === "ELOOP") {
5825
6007
  return rejectScan("SKILL_SYMLINK_CYCLE", skillPath, "Skill \u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
@@ -5845,10 +6027,10 @@ async function existingSkillRoot(projectRoot, skillPath) {
5845
6027
  }
5846
6028
  async function assertEntrypoint(context) {
5847
6029
  const entryPath = reportChild(context.skillPath, "SKILL.md");
5848
- const candidate = join18(context.skillRoot, "SKILL.md");
6030
+ const candidate = join19(context.skillRoot, "SKILL.md");
5849
6031
  try {
5850
6032
  await lstat11(candidate);
5851
- const target = await realpath5(candidate);
6033
+ const target = await realpath7(candidate);
5852
6034
  if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
5853
6035
  rejectScan(
5854
6036
  "SKILL_SYMLINK_ESCAPE",
@@ -5892,7 +6074,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
5892
6074
  }
5893
6075
  let directory;
5894
6076
  try {
5895
- directory = await realpath5(actualDirectory);
6077
+ directory = await realpath7(actualDirectory);
5896
6078
  } catch (cause) {
5897
6079
  if (filesystemCode(cause) === "ELOOP") {
5898
6080
  return rejectScan("SKILL_SYMLINK_CYCLE", reportDirectory, "Skill \u76EE\u5F55\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
@@ -5912,7 +6094,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
5912
6094
  nextAncestors.add(directoryKey);
5913
6095
  let entries;
5914
6096
  try {
5915
- entries = await readdir5(directory, { withFileTypes: true, encoding: "utf8" });
6097
+ entries = await readdir6(directory, { withFileTypes: true, encoding: "utf8" });
5916
6098
  } catch (cause) {
5917
6099
  return rejectScan("SKILL_SCAN_IO_ERROR", reportDirectory, "\u65E0\u6CD5\u5217\u51FA Skill \u5B50\u76EE\u5F55\u3002", cause);
5918
6100
  }
@@ -5949,10 +6131,10 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
5949
6131
  }
5950
6132
  for (const entry2 of entries) {
5951
6133
  const childReportPath = reportChild(reportDirectory, entry2.name);
5952
- const childPath = join18(directory, entry2.name);
6134
+ const childPath = join19(directory, entry2.name);
5953
6135
  let target;
5954
6136
  try {
5955
- target = await realpath5(childPath);
6137
+ target = await realpath7(childPath);
5956
6138
  } catch (cause) {
5957
6139
  if (filesystemCode(cause) === "ELOOP") {
5958
6140
  return rejectScan("SKILL_SYMLINK_CYCLE", childReportPath, "Skill \u5185\u5BB9\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
@@ -6404,8 +6586,8 @@ async function scanExternalSkillPortability(options) {
6404
6586
 
6405
6587
  // packages/cli-core/src/push/preflight.ts
6406
6588
  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";
6589
+ import { lstat as lstat12, open as open6, readdir as readdir7, realpath as realpath8 } from "node:fs/promises";
6590
+ import { isAbsolute as isAbsolute4, join as join20, relative as relative2, resolve as resolve17, sep as sep2, win32 as win323 } from "node:path";
6409
6591
  var MEBIBYTE = 1024 * 1024;
6410
6592
  var DEFAULT_PUSH_MANIFEST_LIMITS = Object.freeze({
6411
6593
  maxFiles: 1e4,
@@ -6599,7 +6781,7 @@ async function canonicalProjectRoot(projectRoot) {
6599
6781
  });
6600
6782
  }
6601
6783
  try {
6602
- return await realpath6(absolute);
6784
+ return await realpath8(absolute);
6603
6785
  } catch (cause) {
6604
6786
  error({
6605
6787
  code: "PUSH_PROJECT_INVALID",
@@ -6610,7 +6792,7 @@ async function canonicalProjectRoot(projectRoot) {
6610
6792
  }
6611
6793
  }
6612
6794
  async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
6613
- const path = join19(projectRoot, filename);
6795
+ const path = join20(projectRoot, filename);
6614
6796
  let entry2;
6615
6797
  try {
6616
6798
  entry2 = await lstat12(path);
@@ -6680,7 +6862,7 @@ function parseCapturedProject(agentFile) {
6680
6862
  async function selectLockfile(projectRoot) {
6681
6863
  let names;
6682
6864
  try {
6683
- names = await readdir6(projectRoot);
6865
+ names = await readdir7(projectRoot);
6684
6866
  } catch (cause) {
6685
6867
  error({
6686
6868
  code: "PUSH_PROJECT_INVALID",
@@ -6789,7 +6971,7 @@ async function createManifest(options) {
6789
6971
  }
6790
6972
  let names;
6791
6973
  try {
6792
- names = await readdir6(directory);
6974
+ names = await readdir7(directory);
6793
6975
  } catch (cause) {
6794
6976
  error({
6795
6977
  code: "PUSH_PROJECT_INVALID",
@@ -6813,7 +6995,7 @@ async function createManifest(options) {
6813
6995
  sensitiveEntriesExcluded += 1;
6814
6996
  continue;
6815
6997
  }
6816
- const absolutePath = join19(directory, name);
6998
+ const absolutePath = join20(directory, name);
6817
6999
  let entry2;
6818
7000
  try {
6819
7001
  entry2 = await lstat12(absolutePath);
@@ -6847,7 +7029,7 @@ async function createManifest(options) {
6847
7029
  }
6848
7030
  let canonicalPath;
6849
7031
  try {
6850
- canonicalPath = await realpath6(absolutePath);
7032
+ canonicalPath = await realpath8(absolutePath);
6851
7033
  } catch (cause) {
6852
7034
  error({
6853
7035
  code: "PUSH_FILE_CHANGED",
@@ -7067,7 +7249,7 @@ async function assertCapturedConfigStable(options) {
7067
7249
  }
7068
7250
  let current;
7069
7251
  try {
7070
- const path = join19(options.projectRoot, options.filename);
7252
+ const path = join20(options.projectRoot, options.filename);
7071
7253
  const entry2 = await lstat12(path);
7072
7254
  if (!entry2.isFile() || entry2.isSymbolicLink()) {
7073
7255
  error({
@@ -7113,7 +7295,7 @@ async function createPushPreflight(options) {
7113
7295
  parseCapturedProject(initialAgentEntry);
7114
7296
  let initialDataEntry;
7115
7297
  try {
7116
- await lstat12(join19(projectRoot, DATA_ACCESS_FILENAME));
7298
+ await lstat12(join20(projectRoot, DATA_ACCESS_FILENAME));
7117
7299
  initialDataEntry = await captureConfigWithinLimit(projectRoot, limits.maxFileBytes, DATA_ACCESS_FILENAME);
7118
7300
  } catch (cause) {
7119
7301
  if (cause.code !== "ENOENT") throw cause;
@@ -7215,8 +7397,8 @@ async function createPushPreflight(options) {
7215
7397
 
7216
7398
  // packages/cli-core/src/push/bundle.ts
7217
7399
  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";
7400
+ import { lstat as lstat13, readFile as readFile18, realpath as realpath9 } from "node:fs/promises";
7401
+ import { join as join21, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
7220
7402
  import { Writable } from "node:stream";
7221
7403
  import { pipeline } from "node:stream/promises";
7222
7404
  import { createGzip } from "node:zlib";
@@ -7238,15 +7420,15 @@ function inside(root, target) {
7238
7420
  return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
7239
7421
  }
7240
7422
  async function capture(root, file) {
7241
- const absolute = join20(root, ...file.path.split("/"));
7423
+ const absolute = join21(root, ...file.path.split("/"));
7242
7424
  try {
7243
7425
  const entry2 = await lstat13(absolute);
7244
7426
  if (!entry2.isFile() || entry2.isSymbolicLink() || entry2.size !== file.bytes) {
7245
7427
  changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
7246
7428
  }
7247
- const canonical = await realpath7(absolute);
7429
+ const canonical = await realpath9(absolute);
7248
7430
  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);
7431
+ const contents = await readFile18(absolute);
7250
7432
  if (contents.byteLength !== file.bytes || digest3(contents) !== file.digest) {
7251
7433
  changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
7252
7434
  }
@@ -7262,7 +7444,7 @@ async function capture(root, file) {
7262
7444
  }
7263
7445
  }
7264
7446
  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));
7447
+ 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
7448
  if (canonicalRoot !== plan.projectRoot) {
7267
7449
  changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u6839\u76EE\u5F55\u5728\u9884\u68C0\u540E\u53D1\u751F\u53D8\u5316");
7268
7450
  }
@@ -7339,7 +7521,7 @@ var MODE_ROUTES = [
7339
7521
  ["direct", NEW_AGENT_CHOICES[0]],
7340
7522
  ["create", NEW_AGENT_CHOICES[1]]
7341
7523
  ];
7342
- function failure(message, io, includeHelp = false) {
7524
+ function failure2(message, io, includeHelp = false) {
7343
7525
  io.writeError(`buddy-cli: ${message}`);
7344
7526
  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
7527
  return { exitCode: 2 };
@@ -7653,7 +7835,7 @@ async function resumeProjectArguments(args, cwd, options, io) {
7653
7835
  await assertInitializableBuddyTarget(root);
7654
7836
  let entry2;
7655
7837
  try {
7656
- entry2 = await lstat14(join21(root, "package.json"));
7838
+ entry2 = await lstat14(join22(root, "package.json"));
7657
7839
  } catch (cause) {
7658
7840
  if (cause.code === "ENOENT") {
7659
7841
  const draft = await readMetaDraft(root);
@@ -7725,13 +7907,13 @@ async function runBuddyCli(args, io, options = {}) {
7725
7907
  if (command === "guide") {
7726
7908
  if (rest.length === 0) io.write(AGENT_GUIDE);
7727
7909
  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);
7910
+ else return failure2("guide \u4E0D\u63A5\u53D7\u5176\u4ED6\u53C2\u6570\uFF1B\u7528 buddy-cli guide --help \u67E5\u770B\u7528\u6CD5", io);
7729
7911
  return { exitCode: 0 };
7730
7912
  }
7731
7913
  if (command === "models") return await runModelsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
7732
7914
  if (command === "knowledge") return await runKnowledgeCommand(rest, io, options.cwd ?? process.cwd());
7733
7915
  if (command === "preview") {
7734
- return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
7916
+ return options.preview?.(rest, io) ?? failure2("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
7735
7917
  }
7736
7918
  if (command === "datasets") {
7737
7919
  return await runDatasetsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
@@ -7773,14 +7955,14 @@ async function runBuddyCli(args, io, options = {}) {
7773
7955
  route ??= await chooseEntryRoute(io);
7774
7956
  return await runRoute(route, parsed, io, options);
7775
7957
  }
7776
- return failure(`\u672A\u77E5\u547D\u4EE4 ${command}`, io, true);
7958
+ return failure2(`\u672A\u77E5\u547D\u4EE4 ${command}`, io, true);
7777
7959
  } catch (cause) {
7778
7960
  if (cause instanceof PromptCancelledError) {
7779
7961
  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
7962
  return { exitCode: 130 };
7781
7963
  }
7782
7964
  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);
7965
+ return failure2(cause.message, io, cause instanceof CliUsageError);
7784
7966
  }
7785
7967
  throw cause;
7786
7968
  }
@@ -7943,6 +8125,7 @@ export {
7943
8125
  serializeBuddyAgentConfig,
7944
8126
  serializeBuddyDraftConfig,
7945
8127
  startBuddyDev,
8128
+ stopLocalDev,
7946
8129
  throwIfDevAborted,
7947
8130
  validateBuddyAgentConfig,
7948
8131
  validateBuddyConfig,