@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/README.md +19 -3
- package/dist/bin/buddy.js +52 -15
- package/dist/bin/buddy.js.map +3 -3
- package/dist/bin/core.js +477 -294
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +17 -3
- package/dist/bin/preview.js.map +2 -2
- package/dist/web/app.css +6 -3
- package/dist/web/app.js +47 -9
- package/package.json +3 -3
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 &&
|
|
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
|
|
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\
|
|
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
|
|
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/
|
|
3050
|
-
import {
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
import {
|
|
3054
|
-
import {
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
3472
|
-
import { join as
|
|
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
|
|
3478
|
-
import { join as
|
|
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
|
|
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
|
|
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
|
|
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")
|
|
3619
|
-
|
|
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
|
|
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
|
|
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
|
|
3814
|
-
const installed2 = JSON.parse(await
|
|
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
|
|
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
|
|
3839
|
-
import { join as
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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_${
|
|
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_${
|
|
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
|
|
5642
|
-
import { extname, isAbsolute as isAbsolute3, join as
|
|
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
|
|
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 :
|
|
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
|
|
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 =
|
|
6030
|
+
const candidate = join19(context.skillRoot, "SKILL.md");
|
|
5849
6031
|
try {
|
|
5850
6032
|
await lstat11(candidate);
|
|
5851
|
-
const target = await
|
|
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
|
|
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
|
|
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 =
|
|
6134
|
+
const childPath = join19(directory, entry2.name);
|
|
5953
6135
|
let target;
|
|
5954
6136
|
try {
|
|
5955
|
-
target = await
|
|
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
|
|
6408
|
-
import { isAbsolute as isAbsolute4, join as
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
7219
|
-
import { join as
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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) ??
|
|
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
|
|
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
|
|
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,
|