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