@my-life-buddies/cli 0.13.0 → 0.14.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 +19 -5
- package/dist/bin/buddy.js +21 -6
- package/dist/bin/buddy.js.map +2 -2
- package/dist/bin/core.js +452 -192
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +281 -19
- package/dist/bin/preview.js.map +4 -4
- package/dist/web/app.css +199 -31
- package/dist/web/app.js +258 -69
- package/dist/web/diagnostics.js +184 -0
- package/dist/web/index.html +38 -40
- package/package.json +3 -3
- package/resources/agent-template/package-lock.json +4 -4
- package/resources/agent-template/package.json +2 -2
package/dist/bin/core.js
CHANGED
|
@@ -16,6 +16,12 @@ function runtimeDependencyVersion(pkg) {
|
|
|
16
16
|
const version = dependencies2[BUDDY_RUNTIME_PACKAGE];
|
|
17
17
|
return typeof version === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version) ? version : void 0;
|
|
18
18
|
}
|
|
19
|
+
function runtimeUsesEnvironmentIdentity(pkg) {
|
|
20
|
+
const version = runtimeDependencyVersion(pkg);
|
|
21
|
+
if (!version) return false;
|
|
22
|
+
const [major, minor] = version.split(".").map(Number);
|
|
23
|
+
return major > 0 || minor >= 16;
|
|
24
|
+
}
|
|
19
25
|
|
|
20
26
|
// packages/cli-core/src/config/runtime-catalog.ts
|
|
21
27
|
async function loadRuntime(projectRoot) {
|
|
@@ -529,7 +535,7 @@ import { lstat as lstat4 } from "node:fs/promises";
|
|
|
529
535
|
import { join as join5 } from "node:path";
|
|
530
536
|
|
|
531
537
|
// packages/cli-core/src/init/scaffold.ts
|
|
532
|
-
import { lstat as lstat3, mkdir as mkdir4, open as open2, readFile as readFile6, rmdir as rmdir2, unlink as unlink3 } from "node:fs/promises";
|
|
538
|
+
import { lstat as lstat3, mkdir as mkdir4, open as open2, readFile as readFile6, rmdir as rmdir2, unlink as unlink3, writeFile as writeFile5 } from "node:fs/promises";
|
|
533
539
|
import { resolve as resolve3 } from "node:path";
|
|
534
540
|
|
|
535
541
|
// packages/cli-core/src/config/models.ts
|
|
@@ -766,8 +772,6 @@ process.on("SIGTERM", stop);
|
|
|
766
772
|
let runtime;
|
|
767
773
|
try {
|
|
768
774
|
runtime = await BuddyServer.start({
|
|
769
|
-
buddyId: process.env.BUDDY_ID,
|
|
770
|
-
token: process.env.BUDDY_TOKEN,
|
|
771
775
|
dataRequirements: config.datasets ?? [],
|
|
772
776
|
buddy: createBuddyOptions,
|
|
773
777
|
});
|
|
@@ -786,6 +790,32 @@ try {
|
|
|
786
790
|
}
|
|
787
791
|
`;
|
|
788
792
|
}
|
|
793
|
+
function legacyPlatformStartTemplate() {
|
|
794
|
+
return platformStartTemplate().replace(" dataRequirements: config.datasets ?? [],", " buddyId: process.env.BUDDY_ID,\n token: process.env.BUDDY_TOKEN,\n dataRequirements: config.datasets ?? [],");
|
|
795
|
+
}
|
|
796
|
+
async function migrateRuntimeStartup(projectRoot) {
|
|
797
|
+
const packagePath = resolve3(projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
|
|
798
|
+
const entryPath = resolve3(projectRoot, BUDDY_PLATFORM_START_ENTRY);
|
|
799
|
+
for (const path of [packagePath, entryPath]) {
|
|
800
|
+
if (!(await lstat3(path)).isFile()) throw new Error("\u542F\u52A8\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5");
|
|
801
|
+
}
|
|
802
|
+
const pkg = JSON.parse(await readFile6(packagePath, "utf8"));
|
|
803
|
+
if (!runtimeUsesEnvironmentIdentity(pkg)) return false;
|
|
804
|
+
const current = await readFile6(entryPath, "utf8");
|
|
805
|
+
const latest = platformStartTemplate();
|
|
806
|
+
const legacy = legacyPlatformStartTemplate();
|
|
807
|
+
let changed2 = false;
|
|
808
|
+
if (current.replace(/\r\n/gu, "\n") === legacy) {
|
|
809
|
+
await writeFile5(entryPath, latest, "utf8");
|
|
810
|
+
changed2 = true;
|
|
811
|
+
}
|
|
812
|
+
if (pkg.scripts?.start === "node start.mjs") {
|
|
813
|
+
pkg.scripts.start = "node --env-file-if-exists=.env start.mjs";
|
|
814
|
+
await writeFile5(packagePath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
815
|
+
changed2 = true;
|
|
816
|
+
}
|
|
817
|
+
return changed2;
|
|
818
|
+
}
|
|
789
819
|
async function scaffoldPlatformAgent(projectRoot, name, options = {}) {
|
|
790
820
|
const path = resolve3(projectRoot, BUDDY_PLATFORM_AGENT_ENTRY);
|
|
791
821
|
const hasAlternateLockfile = (await Promise.all(["pnpm-lock.yaml", "yarn.lock"].map(async (name2) => {
|
|
@@ -804,7 +834,9 @@ async function scaffoldPlatformAgent(projectRoot, name, options = {}) {
|
|
|
804
834
|
{ path: resolve3(projectRoot, BUDDY_PLATFORM_FACTORY_ENTRY), content: platformFactoryTemplate() },
|
|
805
835
|
{ path: resolve3(projectRoot, BUDDY_PLATFORM_PACKAGE_JSON), content: await readFile6(new URL("../../resources/agent-template/package.json", import.meta.url), "utf8") },
|
|
806
836
|
...hasAlternateLockfile ? [] : [{ path: resolve3(projectRoot, BUDDY_PLATFORM_PACKAGE_LOCK), content: await readFile6(new URL("../../resources/agent-template/package-lock.json", import.meta.url), "utf8") }],
|
|
807
|
-
{ path: resolve3(projectRoot, BUDDY_PLATFORM_START_ENTRY), content: platformStartTemplate() }
|
|
837
|
+
{ path: resolve3(projectRoot, BUDDY_PLATFORM_START_ENTRY), content: platformStartTemplate() },
|
|
838
|
+
{ path: resolve3(projectRoot, ".gitignore"), content: "node_modules/\n/.env\n" },
|
|
839
|
+
{ path: resolve3(projectRoot, ".env.example"), content: "# \u9996\u6B21\u8FD0\u884C buddy-cli dev \u6216\u5728\u540E\u53F0\u542F\u52A8 DEV \u65F6\uFF0CCLI \u4F1A\u51C6\u5907 .env\u3002\nBUDDY_ID=\nBUDDY_TOKEN=\n# \u53EF\u9009\uFF1A\u4F7F\u7528\u5176\u4ED6\u5E73\u53F0\u65F6\u8BBE\u7F6E MLB_GATEWAY_URL\u3002\n" }
|
|
808
840
|
];
|
|
809
841
|
const handles = [];
|
|
810
842
|
const createdDirectories = [];
|
|
@@ -847,12 +879,17 @@ async function scaffoldPlatformAgent(projectRoot, name, options = {}) {
|
|
|
847
879
|
throw new Error("\u5DF2\u6709 package.json \u4E0E\u5F85\u8865\u9F50\u7684\u9501\u6587\u4EF6\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u7528\u5DE5\u7A0B\u7684\u5305\u7BA1\u7406\u5668\u6062\u590D\u5339\u914D\u7684\u4F9D\u8D56\u6587\u4EF6\u540E\u91CD\u8BD5");
|
|
848
880
|
}
|
|
849
881
|
}
|
|
882
|
+
const effectivePackage = handles.some((file) => file.path === packageFile.path) ? packageFile.content : await readFile6(packageFile.path, "utf8");
|
|
883
|
+
if (!runtimeUsesEnvironmentIdentity(JSON.parse(effectivePackage))) {
|
|
884
|
+
files.find((file) => file.path === resolve3(projectRoot, BUDDY_PLATFORM_START_ENTRY)).content = legacyPlatformStartTemplate();
|
|
885
|
+
}
|
|
850
886
|
for (const { path: filePath, handle } of handles) {
|
|
851
887
|
const file = files.find((candidate) => candidate.path === filePath);
|
|
852
888
|
await handle.writeFile(file.content, "utf8");
|
|
853
889
|
await handle.sync();
|
|
854
890
|
}
|
|
855
|
-
|
|
891
|
+
const migrated = await migrateRuntimeStartup(projectRoot);
|
|
892
|
+
return Object.freeze({ path, created: migrated || handles.length > 0 || createdDirectories.length > 0 });
|
|
856
893
|
} catch (cause) {
|
|
857
894
|
await Promise.all(handles.map(({ handle }) => handle.close().catch(() => void 0)));
|
|
858
895
|
await Promise.all(handles.map(({ path: createdPath }) => unlink3(createdPath).catch(() => void 0)));
|
|
@@ -1635,7 +1672,7 @@ ${DATASETS_COMMAND_HELP}`);
|
|
|
1635
1672
|
|
|
1636
1673
|
// packages/cli-core/src/commands/knowledge.ts
|
|
1637
1674
|
import { createHash as createHash2, randomUUID as randomUUID4 } from "node:crypto";
|
|
1638
|
-
import { lstat as lstat7, mkdir as mkdir6, readFile as readFile8, rename as rename5, unlink as unlink5, writeFile as
|
|
1675
|
+
import { lstat as lstat7, mkdir as mkdir6, readFile as readFile8, rename as rename5, unlink as unlink5, writeFile as writeFile6 } from "node:fs/promises";
|
|
1639
1676
|
import { dirname as dirname2, join as join8, resolve as resolve8 } from "node:path";
|
|
1640
1677
|
var MANIFEST = "knowledge-manifest.json";
|
|
1641
1678
|
var INDEX = "resources/knowledge/index.md";
|
|
@@ -1705,7 +1742,7 @@ async function atomicWrite(root, path, value) {
|
|
|
1705
1742
|
const full = await safePath(root, path);
|
|
1706
1743
|
await mkdir6(dirname2(full), { recursive: true });
|
|
1707
1744
|
const temporary = `${full}.${randomUUID4()}.tmp`;
|
|
1708
|
-
await
|
|
1745
|
+
await writeFile6(temporary, value, { flag: "wx", mode: 384 });
|
|
1709
1746
|
try {
|
|
1710
1747
|
await rename5(temporary, full);
|
|
1711
1748
|
} finally {
|
|
@@ -1947,11 +1984,11 @@ ${KNOWLEDGE_HELP}`);
|
|
|
1947
1984
|
}
|
|
1948
1985
|
|
|
1949
1986
|
// packages/cli-core/src/index.ts
|
|
1950
|
-
import { join as
|
|
1987
|
+
import { join as join16, resolve as resolve16 } from "node:path";
|
|
1951
1988
|
|
|
1952
1989
|
// packages/cli-core/src/init/creator.ts
|
|
1953
1990
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
1954
|
-
import { link as link2, lstat as lstat8, mkdir as mkdir7, readFile as readFile9, unlink as unlink6, writeFile as
|
|
1991
|
+
import { link as link2, lstat as lstat8, mkdir as mkdir7, readFile as readFile9, unlink as unlink6, writeFile as writeFile7 } from "node:fs/promises";
|
|
1955
1992
|
import { join as join9 } from "node:path";
|
|
1956
1993
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1957
1994
|
|
|
@@ -2022,7 +2059,7 @@ ${KNOWLEDGE_GUIDE}
|
|
|
2022
2059
|
| \u56DE\u7B54\u65F6\u9700\u8981\u53C2\u8003\u7684\u5386\u53F2\u3001\u8BA1\u5212\u548C\u72B6\u6001 | Runtime \u4F1A\u4ECE MLB \u91CD\u5EFA\u4F1A\u8BDD\u5386\u53F2\uFF1B\u957F\u671F\u8BA1\u5212\u548C\u7ED3\u6784\u5316\u72B6\u6001\u4E0D\u80FD\u53EA\u4F9D\u8D56\u5B83\u4E00\u76F4\u7559\u5728\u6700\u8FD1\u804A\u5929\u91CC\u3002\u6309\u9700\u6C42\u4F7F\u7528 Memory\u3001\u6388\u6743\u6570\u636E\u6216\u4E1A\u52A1 Tool\uFF0C\u4E0D\u8981\u627F\u8BFA\u9ED8\u8BA4\u914D\u7F6E\u5DF2\u7ECF\u8986\u76D6\u3002 |
|
|
2023
2060
|
| \u6A21\u578B\u548C\u5E73\u53F0\u8D44\u6E90 | \u5DF2\u786E\u5B9A\u7684\u6A21\u578B\u76F4\u63A5\u6CBF\u7528\uFF1B\u672A\u6307\u5B9A\u65F6\u67E5\u8BE2 \`buddy-cli models\`\uFF0C\u6838\u5BF9\u53EF\u7528\u6027\u5E76\u91C7\u7528\u6EE1\u8DB3\u9700\u6C42\u7684\u9009\u62E9\uFF0C\u4E0D\u9ED8\u8BA4\u518D\u95EE\u4E00\u8F6E\u9009\u578B\u95EE\u9898\uFF0C\u4E5F\u4E0D\u7F16\u9020\u4EF7\u683C\u6216\u80FD\u529B\u3002\u6A21\u677F\u628A\u914D\u7F6E\u4E2D\u7684\u6A21\u578B\u4EA4\u7ED9 \`initialState.model\`\uFF1B\u81EA\u5B9A\u4E49\u5DE5\u5382\u4E5F\u53EF\u6309\u4F1A\u8BDD\u9009\u62E9\u53D7\u652F\u6301\u6A21\u578B\uFF1B\u4F1A\u4EA7\u751F\u65B0\u7684\u8D39\u7528\u3001\u6570\u636E\u4F7F\u7528\u6216\u6743\u9650\u8FB9\u754C\u65F6\u5148\u786E\u8BA4\u3002 |
|
|
2024
2061
|
|
|
2025
|
-
\`src/buddy-options.mjs\` \u662F\u6BCF\u573A\u4F1A\u8BDD\u7684\u4E1A\u52A1\u88C5\u914D\u5165\u53E3\uFF0C\u5BFC\u51FA \`createBuddyOptions(conversation)\`\uFF0C\u8FD4\u56DE \`initialState\`\u3001\u5728 \`initialState.tools\` \u6CE8\u518C\u7684\u4E1A\u52A1\u53CA\u5E73\u53F0\u5DE5\u5177\u548C\u53EF\u9009\u56DE\u5408\u94A9\u5B50\u3002\`start.mjs\` \u4ECE \`./src/buddy-options.mjs\` \u5BFC\u5165\u5DE5\u5382\u5E76\u4F20\u7ED9 \`BuddyServer.start({
|
|
2062
|
+
\`src/buddy-options.mjs\` \u662F\u6BCF\u573A\u4F1A\u8BDD\u7684\u4E1A\u52A1\u88C5\u914D\u5165\u53E3\uFF0C\u5BFC\u51FA \`createBuddyOptions(conversation)\`\uFF0C\u8FD4\u56DE \`initialState\`\u3001\u5728 \`initialState.tools\` \u6CE8\u518C\u7684\u4E1A\u52A1\u53CA\u5E73\u53F0\u5DE5\u5177\u548C\u53EF\u9009\u56DE\u5408\u94A9\u5B50\u3002\`start.mjs\` \u4ECE \`./src/buddy-options.mjs\` \u5BFC\u5165\u5DE5\u5382\u5E76\u4F20\u7ED9 \`BuddyServer.start({ dataRequirements: config.datasets ?? [], buddy: createBuddyOptions })\`\uFF1B\u7EC4\u88C5\u5668\u8BFB\u53D6 \`buddy.agent.json.model\`\uFF0C\u7701\u7565\u65F6\u7528 \`kimi-k2.6\`\uFF0C\u653E\u5165 \`initialState.model\`\uFF1B\u7528 \`new URL("../agent.md", import.meta.url)\` \u8BFB\u53D6\u6839\u76EE\u5F55 Prompt\u3002\u9ED8\u8BA4\u6A21\u677F\u53EA\u662F\u8D77\u70B9\uFF0C\u53EA\u6539\u8BF4\u660E\u4E0D\u7B49\u4E8E\u6539\u53D8\u4E86\u4FDD\u5B58\u3001\u67E5\u8BE2\u6216\u5904\u7406\u903B\u8F91\u3002 Runtime 0.16.0 \u4ECE\u73AF\u5883\u53D8\u91CF\u8BFB\u53D6\u8EAB\u4EFD\u548C\u4EE4\u724C\uFF1B\u9996\u6B21\u542F\u52A8 DEV \u65F6 CLI \u51C6\u5907\u5DE5\u7A0B \`.env\`\uFF0CNode \u7528 \`--env-file-if-exists=.env\` \u52A0\u8F7D\u3002\u4E0D\u8981\u5C06 token \u5199\u5165\u4E1A\u52A1\u4EE3\u7801\u3002
|
|
2026
2063
|
|
|
2027
2064
|
### \u6309\u76EE\u5F55\u804C\u8D23\u7EC4\u7EC7\u5B9E\u73B0
|
|
2028
2065
|
|
|
@@ -2052,11 +2089,11 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
|
|
|
2052
2089
|
|
|
2053
2090
|
\u5E73\u53F0\u80FD\u529B\u8981\u4EE5\u5B9E\u9645\u63A5\u5165\u4E3A\u51C6\u3002\u5F53\u524D Runtime \u63D0\u4F9B\u4F1A\u8BDD\u6D88\u606F\u3001Memory\u3001\u6388\u6743\u6570\u636E\u3001\u5C0F\u6302\u4EF6\u548C\u672C\u5730 Markdown \u8D44\u6599\u80FD\u529B\uFF1B\u6CA1\u6709\u63A5\u901A\u7684 Files\u3001Knowledge \u4E0D\u80FD\u56E0\u624B\u518C\u63D0\u5230\u5C31\u5BA3\u79F0\u53EF\u7528\u3002\u666E\u901A Tool \u4E5F\u4E0D\u4EE3\u8868\u5DF2\u7ECF\u63D0\u4F9B\u5B9A\u65F6\u3001\u957F\u671F\u4EFB\u52A1\u3001\u540E\u53F0\u8C03\u5EA6\u6216\u4E3B\u52A8\u89E6\u8FBE\u3002\u624B\u518C\u63CF\u8FF0\u7684\u4ED8\u8D39\u3001\u53D1\u5E03\u7B49\u670D\u52A1\u8BBE\u8BA1\u4E5F\u4E0D\u6784\u6210\u6267\u884C\u6536\u8D39\u6216\u53D1\u5E03\u52A8\u4F5C\u7684\u6388\u6743\u3002
|
|
2054
2091
|
|
|
2055
|
-
\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\u5F53\u524D\u5B89\u88C5\u7684 Runtime \u5BFC\u51FA\u8BFB\u53D6\u5168\u90E8\u6570\u636E\u96C6\uFF1B\u4E0D\u8981\u6C42\u767B\u5F55\uFF0C\u4E0D\u4ECE Server \u62C9\u76EE\u5F55\
|
|
2092
|
+
\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\u5F53\u524D\u5B89\u88C5\u7684 Runtime \u5BFC\u51FA\u8BFB\u53D6\u5168\u90E8\u6570\u636E\u96C6\uFF1B\u4E0D\u8981\u6C42\u767B\u5F55\uFF0C\u4E0D\u4ECE Server \u62C9\u76EE\u5F55\u3002\u4EE5\u5DE5\u7A0B\u5B89\u88C5\u7248\u672C\u5BFC\u51FA\u7684\u76EE\u5F55\u4E3A\u51C6\uFF0CCLI \u4E0D\u590D\u5236\u679A\u4E3E\u3002\u628A\u5B9E\u9645\u9700\u8981\u7684 \`id\` \u548C\u7528\u9014 \`purpose\` \u5199\u5165 \`buddy.agent.json.datasets\`\uFF0C\u540C\u4E00 ID \u53EA\u5199\u4E00\u6B21\uFF0C\u7528\u9014\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u6700\u591A 200 \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
|
|
2056
2093
|
|
|
2057
2094
|
\`start.mjs\` \u5C06\u58F0\u660E\u4EA4\u7ED9 \`StartOptions.dataRequirements\`\u3002Runtime \u5728\u5B9E\u9645\u6570\u636E\u67E5\u8BE2\u3001HealthKit \u6388\u6743\u548C\u4E3B\u52A8\u670D\u52A1\u8BF7\u6C42\u4E2D\u643A\u5E26\u5B8C\u6574\u58F0\u660E\uFF0C\u7531 Server \u6821\u9A8C\u8303\u56F4\u4E0E\u7528\u6237\u540C\u610F\uFF1B\u586B\u5199\u914D\u7F6E\u4E0D\u8868\u793A\u5DF2\u7ECF\u6388\u6743\u3002\u670D\u52A1\u7AEF\u4ECE\u4E0A\u4F20\u5305\u89E3\u6790\u5E76\u7ED1\u5B9A\u53D1\u5E03\u7248\u672C\u4ECD\u5F85\u5B9E\u73B0\u3002
|
|
2058
2095
|
|
|
2059
|
-
\u6A21\u578B\u9700\u8981\u67E5\u6570\u636E\u65F6\uFF0C\u5728 \`src/buddy-options.mjs\` \u7684 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.data_access_query\`\uFF1B\u9700\u8981\u5411\u7528\u6237\u7533\u8BF7\u65F6\u52A0\u5165 \`conversation.tools.data_access_request\`\uFF0C\u4F4D\u7F6E\u3001\u65E5\u5386\u7684\u4E00\u6B21\u6027\u7ED3\u679C\u901A\u8FC7 \`data_access_read_result\` \u8BFB\u53D6\uFF0CHealthKit \u5219\u91CD\u65B0\u67E5\u8BE2 Dataset\u3002\u4E1A\u52A1\u4EE3\u7801\u5BF9\u5E94\u4F7F\u7528 \`conversation.dataAccess.query/request/readResult\`\u3002\u65B0\u5EFA\u5DE5\u7A0B\u52A8\u6001\u9009\u62E9\u516C\u5171 npm latest \u5E76\u9501\u5B9A\u51C6\u786E\u7248\u672C\uFF1B\u8054\u7F51\u5931\u8D25\u65F6\u660E\u786E\u63D0\u793A\u4F7F\u7528\u968F CLI \u9644\u5E26\u7684 Runtime 0.
|
|
2096
|
+
\u6A21\u578B\u9700\u8981\u67E5\u6570\u636E\u65F6\uFF0C\u5728 \`src/buddy-options.mjs\` \u7684 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.data_access_query\`\uFF1B\u9700\u8981\u5411\u7528\u6237\u7533\u8BF7\u65F6\u52A0\u5165 \`conversation.tools.data_access_request\`\uFF0C\u4F4D\u7F6E\u3001\u65E5\u5386\u7684\u4E00\u6B21\u6027\u7ED3\u679C\u901A\u8FC7 \`data_access_read_result\` \u8BFB\u53D6\uFF0CHealthKit \u5219\u91CD\u65B0\u67E5\u8BE2 Dataset\u3002\u4E1A\u52A1\u4EE3\u7801\u5BF9\u5E94\u4F7F\u7528 \`conversation.dataAccess.query/request/readResult\`\u3002\u65B0\u5EFA\u5DE5\u7A0B\u52A8\u6001\u9009\u62E9\u516C\u5171 npm latest \u5E76\u9501\u5B9A\u51C6\u786E\u7248\u672C\uFF1B\u8054\u7F51\u5931\u8D25\u65F6\u660E\u786E\u63D0\u793A\u4F7F\u7528\u968F CLI \u9644\u5E26\u7684 Runtime 0.16.0\u3002\u5DF2\u6709\u5DE5\u7A0B\u901A\u8FC7\u5305\u7BA1\u7406\u5668\u663E\u5F0F\u5347\u7EA7\u4F9D\u8D56\u3002
|
|
2060
2097
|
|
|
2061
2098
|
\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
|
|
2062
2099
|
|
|
@@ -2153,7 +2190,7 @@ async function safeRead(path) {
|
|
|
2153
2190
|
}
|
|
2154
2191
|
async function writeOnce(path, bytes) {
|
|
2155
2192
|
const temporary = `${path}.${randomUUID5()}.tmp`;
|
|
2156
|
-
await
|
|
2193
|
+
await writeFile7(temporary, bytes, { flag: "wx", mode: 384 });
|
|
2157
2194
|
try {
|
|
2158
2195
|
await link2(temporary, path).catch((cause) => {
|
|
2159
2196
|
if (cause.code !== "EEXIST") throw cause;
|
|
@@ -2376,6 +2413,13 @@ function resolvePlatformUrl(options = {}) {
|
|
|
2376
2413
|
return normalizePlatformUrl(options.override ?? configured ?? DEFAULT_MLB_TEST_URL);
|
|
2377
2414
|
}
|
|
2378
2415
|
|
|
2416
|
+
// packages/cli-core/src/dev/project-env.ts
|
|
2417
|
+
import { constants } from "node:fs";
|
|
2418
|
+
import { open as open4, rename as rename6, unlink as unlink7 } from "node:fs/promises";
|
|
2419
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
2420
|
+
import { join as join10 } from "node:path";
|
|
2421
|
+
import { parseEnv } from "node:util";
|
|
2422
|
+
|
|
2379
2423
|
// packages/cli-core/src/dev/errors.ts
|
|
2380
2424
|
var BuddyDevError = class extends Error {
|
|
2381
2425
|
code;
|
|
@@ -2404,6 +2448,124 @@ function throwIfDevAborted(signal) {
|
|
|
2404
2448
|
if (signal?.aborted) throw abortedDevError(signal);
|
|
2405
2449
|
}
|
|
2406
2450
|
|
|
2451
|
+
// packages/cli-core/src/dev/project-env.ts
|
|
2452
|
+
function invalid(message) {
|
|
2453
|
+
return new BuddyDevError({ code: "DEV_CONFIG_INVALID", phase: "config", message });
|
|
2454
|
+
}
|
|
2455
|
+
async function readLocalFile(path) {
|
|
2456
|
+
let file;
|
|
2457
|
+
try {
|
|
2458
|
+
file = await open4(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
2459
|
+
const stat2 = await file.stat();
|
|
2460
|
+
if (!stat2.isFile() || stat2.nlink !== 1 || stat2.size > 256 * 1024) throw invalid("\u672C\u5730\u914D\u7F6E\u5FC5\u987B\u662F\u5C0F\u4E8E 256 KB \u7684\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u4F7F\u7528\u94FE\u63A5\u3002");
|
|
2461
|
+
return await file.readFile("utf8");
|
|
2462
|
+
} catch (cause) {
|
|
2463
|
+
if (cause.code === "ENOENT") return "";
|
|
2464
|
+
if (cause instanceof BuddyDevError) throw cause;
|
|
2465
|
+
throw invalid("\u65E0\u6CD5\u8BFB\u53D6\u5DE5\u7A0B\u672C\u5730\u914D\u7F6E\uFF0C\u8BF7\u68C0\u67E5 .env \u548C .gitignore \u7684\u6587\u4EF6\u7C7B\u578B\u4E0E\u6743\u9650\u3002");
|
|
2466
|
+
} finally {
|
|
2467
|
+
await file?.close();
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
async function readProjectEnvironment(projectRoot, buddyId) {
|
|
2471
|
+
const source = await readLocalFile(join10(projectRoot, ".env"));
|
|
2472
|
+
let values;
|
|
2473
|
+
try {
|
|
2474
|
+
values = parseEnv(source);
|
|
2475
|
+
} catch {
|
|
2476
|
+
throw invalid("\u5DE5\u7A0B .env \u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002");
|
|
2477
|
+
}
|
|
2478
|
+
if (values.BUDDY_ID && values.BUDDY_ID !== buddyId) throw invalid(".env \u7684 BUDDY_ID \u4E0E buddy.agent.json \u4E0D\u4E00\u81F4\uFF0C\u8BF7\u5148\u4FEE\u6B63\u5DE5\u7A0B\u8EAB\u4EFD\u3002");
|
|
2479
|
+
return values;
|
|
2480
|
+
}
|
|
2481
|
+
async function projectBuddyToken(projectRoot, buddyId, gatewayUrl) {
|
|
2482
|
+
const values = await readProjectEnvironment(projectRoot, buddyId);
|
|
2483
|
+
if (!values.BUDDY_TOKEN) return void 0;
|
|
2484
|
+
if (!values.BUDDY_ID) throw invalid(".env \u4E2D\u6709 BUDDY_TOKEN\uFF0C\u4F46\u7F3A\u5C11 BUDDY_ID\uFF0C\u8BF7\u8865\u9F50\u540E\u91CD\u8BD5\u3002");
|
|
2485
|
+
if (values.MLB_GATEWAY_URL) {
|
|
2486
|
+
let gateway;
|
|
2487
|
+
try {
|
|
2488
|
+
gateway = normalizePlatformUrl(values.MLB_GATEWAY_URL);
|
|
2489
|
+
} catch {
|
|
2490
|
+
throw invalid(".env \u4E2D\u7684 MLB_GATEWAY_URL \u65E0\u6548\u3002");
|
|
2491
|
+
}
|
|
2492
|
+
if (gateway.href !== gatewayUrl.href) throw invalid(".env \u7684\u5E73\u53F0\u5730\u5740\u4E0E\u672C\u6B21\u542F\u52A8\u4E0D\u540C\uFF1B\u8BF7\u786E\u8BA4\u5E73\u53F0\u540E\u4F7F\u7528 --refresh-token \u91CD\u65B0\u9886\u53D6\u5F00\u53D1\u4EE4\u724C\u3002");
|
|
2493
|
+
}
|
|
2494
|
+
if (!values.BUDDY_TOKEN.trim() || /[\u0000-\u001f\u007f]/u.test(values.BUDDY_TOKEN)) throw invalid(".env \u4E2D\u7684\u5F00\u53D1\u4EE4\u724C\u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 --refresh-token \u91CD\u65B0\u9886\u53D6\u3002");
|
|
2495
|
+
return values.BUDDY_TOKEN;
|
|
2496
|
+
}
|
|
2497
|
+
function quote(value) {
|
|
2498
|
+
if (/[\u0000-\u001f\u007f]/u.test(value)) throw invalid("\u672C\u5730\u8FD0\u884C\u914D\u7F6E\u4E0D\u80FD\u5305\u542B\u63A7\u5236\u5B57\u7B26\u3002");
|
|
2499
|
+
const delimiter = ["'", '"', "`"].find((character) => !value.includes(character));
|
|
2500
|
+
if (!delimiter) throw invalid("\u672C\u5730\u8FD0\u884C\u914D\u7F6E\u5305\u542B\u65E0\u6CD5\u5199\u5165 .env \u7684\u5F15\u53F7\u7EC4\u5408\u3002");
|
|
2501
|
+
return `${delimiter}${value}${delimiter}`;
|
|
2502
|
+
}
|
|
2503
|
+
function updateValues(source, values) {
|
|
2504
|
+
const assignment = /^[\t ]*(?:export[\t ]+)?([A-Za-z_][A-Za-z0-9_]*)[\t ]*=[\t ]*/gm;
|
|
2505
|
+
let output = "", cursor = 0, match;
|
|
2506
|
+
const written = /* @__PURE__ */ new Set();
|
|
2507
|
+
while (match = assignment.exec(source)) {
|
|
2508
|
+
const valueStart = assignment.lastIndex;
|
|
2509
|
+
let end = source.indexOf("\n", valueStart);
|
|
2510
|
+
if (end < 0) end = source.length;
|
|
2511
|
+
let commentStart = valueStart;
|
|
2512
|
+
const delimiter = source[valueStart];
|
|
2513
|
+
if (delimiter && ["'", '"', "`"].includes(delimiter)) {
|
|
2514
|
+
const close = source.indexOf(delimiter, valueStart + 1);
|
|
2515
|
+
if (close < 0) throw invalid(".env \u4E2D\u6709\u672A\u95ED\u5408\u7684\u5F15\u53F7\uFF0C\u8BF7\u4FEE\u6B63\u540E\u91CD\u8BD5\u3002");
|
|
2516
|
+
commentStart = close + 1;
|
|
2517
|
+
end = source.indexOf("\n", close + 1);
|
|
2518
|
+
if (end < 0) end = source.length;
|
|
2519
|
+
}
|
|
2520
|
+
assignment.lastIndex = end;
|
|
2521
|
+
const name = match[1];
|
|
2522
|
+
if (!(name in values)) continue;
|
|
2523
|
+
const comment = source.slice(commentStart, end).match(/[\t ]*#[^\r\n]*/u)?.[0] ?? "";
|
|
2524
|
+
output += source.slice(cursor, match.index);
|
|
2525
|
+
output += written.has(name) ? comment : `${name}=${quote(values[name])}${comment ? ` ${comment.trimStart()}` : ""}`;
|
|
2526
|
+
if (source[end - 1] === "\r") output += "\r";
|
|
2527
|
+
cursor = end;
|
|
2528
|
+
written.add(name);
|
|
2529
|
+
}
|
|
2530
|
+
output += source.slice(cursor);
|
|
2531
|
+
const newline = source.includes("\r\n") ? "\r\n" : "\n";
|
|
2532
|
+
for (const [name, value] of Object.entries(values)) if (!written.has(name)) {
|
|
2533
|
+
if (output && !output.endsWith("\n")) output += newline;
|
|
2534
|
+
output += `${name}=${quote(value)}${newline}`;
|
|
2535
|
+
}
|
|
2536
|
+
const parsed = parseEnv(output);
|
|
2537
|
+
if (Object.entries(values).some(([name, value]) => parsed[name] !== value)) throw invalid("\u65E0\u6CD5\u5B8C\u6574\u66F4\u65B0 .env\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u683C\u5F0F\u3002");
|
|
2538
|
+
return output;
|
|
2539
|
+
}
|
|
2540
|
+
async function replaceFile(path, contents, mode) {
|
|
2541
|
+
const temporary = `${path}.${randomUUID6()}.tmp`;
|
|
2542
|
+
let file;
|
|
2543
|
+
try {
|
|
2544
|
+
file = await open4(temporary, "wx", mode);
|
|
2545
|
+
await file.writeFile(contents, "utf8");
|
|
2546
|
+
await file.sync();
|
|
2547
|
+
await file.close();
|
|
2548
|
+
file = void 0;
|
|
2549
|
+
await rename6(temporary, path);
|
|
2550
|
+
} finally {
|
|
2551
|
+
await file?.close();
|
|
2552
|
+
await unlink7(temporary).catch((cause) => {
|
|
2553
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
async function writeProjectEnvironment(projectRoot, values) {
|
|
2558
|
+
await readProjectEnvironment(projectRoot, values.BUDDY_ID);
|
|
2559
|
+
const envPath = join10(projectRoot, ".env"), ignorePath = join10(projectRoot, ".gitignore");
|
|
2560
|
+
const contents = updateValues(await readLocalFile(envPath), values);
|
|
2561
|
+
const ignored = await readLocalFile(ignorePath);
|
|
2562
|
+
if (ignored.trimEnd().split(/\r?\n/u).at(-1) !== "/.env") {
|
|
2563
|
+
await replaceFile(ignorePath, `${ignored}${ignored && !ignored.endsWith("\n") ? "\n" : ""}/.env
|
|
2564
|
+
`, 420);
|
|
2565
|
+
}
|
|
2566
|
+
await replaceFile(envPath, contents, 384);
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2407
2569
|
// packages/cli-core/src/dev/app-server.ts
|
|
2408
2570
|
var MAX_JSON_BODY_LENGTH = 1048576;
|
|
2409
2571
|
function safeCredential(value, name) {
|
|
@@ -2465,10 +2627,14 @@ var MlbDevRegistrationClient = class {
|
|
|
2465
2627
|
#baseUrl;
|
|
2466
2628
|
#developerToken;
|
|
2467
2629
|
#fetch;
|
|
2630
|
+
#tokenStoreFor;
|
|
2631
|
+
#refreshToken;
|
|
2468
2632
|
constructor(options) {
|
|
2469
2633
|
this.#baseUrl = normalizePlatformUrl(options.baseUrl);
|
|
2470
2634
|
this.#developerToken = safeCredential(options.developerToken, "developerToken");
|
|
2471
2635
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
2636
|
+
this.#tokenStoreFor = options.tokenStoreFor;
|
|
2637
|
+
this.#refreshToken = options.refreshToken ?? false;
|
|
2472
2638
|
if (typeof this.#fetch !== "function") {
|
|
2473
2639
|
throw new TypeError("A Fetch API implementation is required");
|
|
2474
2640
|
}
|
|
@@ -2538,6 +2704,18 @@ var MlbDevRegistrationClient = class {
|
|
|
2538
2704
|
message: "buddy.agent.json \u4E2D\u7684 buddyId \u65E0\u6548\u3002"
|
|
2539
2705
|
});
|
|
2540
2706
|
}
|
|
2707
|
+
options.signal.throwIfAborted();
|
|
2708
|
+
if (request.projectRoot) {
|
|
2709
|
+
await readProjectEnvironment(request.projectRoot, request.devAgentId);
|
|
2710
|
+
const localToken = this.#refreshToken ? void 0 : await projectBuddyToken(request.projectRoot, request.devAgentId, this.#baseUrl);
|
|
2711
|
+
if (localToken !== void 0) return Object.freeze({ devAgentId: request.devAgentId, appServerUrl: new URL(this.#baseUrl), agentToken: localToken });
|
|
2712
|
+
}
|
|
2713
|
+
const store = this.#tokenStoreFor?.(request.devAgentId);
|
|
2714
|
+
const cached = this.#refreshToken ? void 0 : await store?.load();
|
|
2715
|
+
if (cached !== void 0) {
|
|
2716
|
+
options.signal.throwIfAborted();
|
|
2717
|
+
return Object.freeze({ devAgentId: request.devAgentId, appServerUrl: new URL(this.#baseUrl), agentToken: safeCredential(cached, "agentToken") });
|
|
2718
|
+
}
|
|
2541
2719
|
const body = object2(await this.#request(
|
|
2542
2720
|
`/cli/buddies/${encodeURIComponent(request.devAgentId)}`,
|
|
2543
2721
|
{
|
|
@@ -2552,10 +2730,13 @@ var MlbDevRegistrationClient = class {
|
|
|
2552
2730
|
message: "\u642D\u5B50\u8BE6\u60C5\u54CD\u5E94\u7F3A\u5C11\u5339\u914D\u7684 id \u6216 Buddy token\u3002"
|
|
2553
2731
|
});
|
|
2554
2732
|
}
|
|
2733
|
+
const token = safeCredential(body.token, "agentToken");
|
|
2734
|
+
options.signal.throwIfAborted();
|
|
2735
|
+
if (!request.projectRoot) await store?.save(token);
|
|
2555
2736
|
return Object.freeze({
|
|
2556
2737
|
devAgentId: request.devAgentId,
|
|
2557
2738
|
appServerUrl: new URL(this.#baseUrl),
|
|
2558
|
-
agentToken:
|
|
2739
|
+
agentToken: token
|
|
2559
2740
|
});
|
|
2560
2741
|
}
|
|
2561
2742
|
async listOnline(options) {
|
|
@@ -2653,7 +2834,7 @@ function buildAgentEnvironment(options) {
|
|
|
2653
2834
|
// packages/cli-core/src/dev/orchestrator.ts
|
|
2654
2835
|
import { createHash as createHash4 } from "node:crypto";
|
|
2655
2836
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
2656
|
-
import { join as
|
|
2837
|
+
import { join as join12, resolve as resolve10 } from "node:path";
|
|
2657
2838
|
|
|
2658
2839
|
// packages/cli-core/src/dev/launch.ts
|
|
2659
2840
|
import { lstatSync, readFileSync } from "node:fs";
|
|
@@ -2698,9 +2879,10 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2698
2879
|
cause
|
|
2699
2880
|
});
|
|
2700
2881
|
}
|
|
2882
|
+
await migrateRuntimeStartup(input.projectRoot);
|
|
2701
2883
|
return Object.freeze({
|
|
2702
2884
|
command: process.execPath,
|
|
2703
|
-
args: Object.freeze([entry2])
|
|
2885
|
+
args: Object.freeze(["--env-file-if-exists=.env", entry2])
|
|
2704
2886
|
});
|
|
2705
2887
|
}
|
|
2706
2888
|
|
|
@@ -2903,14 +3085,14 @@ function processTreeControllerFor(platform) {
|
|
|
2903
3085
|
// packages/cli-core/src/dev/instance-lock.ts
|
|
2904
3086
|
import { createHash as createHash3 } from "node:crypto";
|
|
2905
3087
|
import { homedir as homedir2 } from "node:os";
|
|
2906
|
-
import { join as
|
|
3088
|
+
import { join as join11 } from "node:path";
|
|
2907
3089
|
function alreadyRunningError() {
|
|
2908
3090
|
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";
|
|
2909
3091
|
return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
|
|
2910
3092
|
}
|
|
2911
3093
|
async function acquireDevInstanceLock(options) {
|
|
2912
3094
|
return acquireProcessLock({
|
|
2913
|
-
directory: options.directory ??
|
|
3095
|
+
directory: options.directory ?? join11(homedir2(), ".buddy", "dev-locks"),
|
|
2914
3096
|
key: createHash3("sha256").update(`${new URL(options.gatewayUrl).origin}
|
|
2915
3097
|
${options.devAgentId}`).digest("hex"),
|
|
2916
3098
|
busyError: alreadyRunningError
|
|
@@ -2942,7 +3124,7 @@ function validateRegistration(value, expectedAgentId) {
|
|
|
2942
3124
|
throw new BuddyDevError({
|
|
2943
3125
|
code: "DEV_APP_SERVER_RESPONSE_INVALID",
|
|
2944
3126
|
phase: "registration",
|
|
2945
|
-
message: "DEV \u6302\u53F7\u6CA1\u6709\u8FD4\u56DE\u5339\u914D\u7684 Buddy \u8EAB\u4EFD\u4E0E\
|
|
3127
|
+
message: "DEV \u6302\u53F7\u6CA1\u6709\u8FD4\u56DE\u5339\u914D\u7684 Buddy \u8EAB\u4EFD\u4E0E\u5F00\u53D1 Token\u3002"
|
|
2946
3128
|
});
|
|
2947
3129
|
}
|
|
2948
3130
|
return value;
|
|
@@ -2979,6 +3161,7 @@ async function launchPlanFor(config, options, projectRoot, signal) {
|
|
|
2979
3161
|
async function registerDevAgent(options) {
|
|
2980
3162
|
try {
|
|
2981
3163
|
return validateRegistration(await options.client.register({
|
|
3164
|
+
projectRoot: options.projectRoot,
|
|
2982
3165
|
devAgentId: options.devAgentId,
|
|
2983
3166
|
configSource: options.configSource,
|
|
2984
3167
|
configDigest: options.configDigest
|
|
@@ -3169,7 +3352,7 @@ async function startBuddyDev(options) {
|
|
|
3169
3352
|
let config;
|
|
3170
3353
|
let configSource;
|
|
3171
3354
|
try {
|
|
3172
|
-
configSource = await readFile10(
|
|
3355
|
+
configSource = await readFile10(join12(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
|
|
3173
3356
|
config = combineBuddyProject(parseBuddyAgentConfig(configSource));
|
|
3174
3357
|
} catch (cause) {
|
|
3175
3358
|
throw new BuddyDevError({
|
|
@@ -3179,8 +3362,10 @@ async function startBuddyDev(options) {
|
|
|
3179
3362
|
cause
|
|
3180
3363
|
});
|
|
3181
3364
|
}
|
|
3365
|
+
await readProjectEnvironment(projectRoot, devAgentId);
|
|
3182
3366
|
const launchPlan = await launchPlanFor(config, options, projectRoot, signal);
|
|
3183
3367
|
const registration = await registerDevAgent({
|
|
3368
|
+
projectRoot,
|
|
3184
3369
|
client: options.registrationClient,
|
|
3185
3370
|
devAgentId,
|
|
3186
3371
|
configSource,
|
|
@@ -3204,13 +3389,19 @@ async function startBuddyDev(options) {
|
|
|
3204
3389
|
buddyToken: registration.agentToken,
|
|
3205
3390
|
gatewayUrl: registration.appServerUrl
|
|
3206
3391
|
});
|
|
3392
|
+
await writeProjectEnvironment(projectRoot, {
|
|
3393
|
+
BUDDY_ID: environment.BUDDY_ID,
|
|
3394
|
+
BUDDY_TOKEN: environment.BUDDY_TOKEN,
|
|
3395
|
+
MLB_GATEWAY_URL: environment.MLB_GATEWAY_URL
|
|
3396
|
+
});
|
|
3397
|
+
const { BUDDY_ID: _id, BUDDY_TOKEN: _token, MLB_GATEWAY_URL: _gateway, ...childEnvironment } = environment;
|
|
3207
3398
|
const launcher = options.processLauncher ?? new NodeAgentProcessLauncher();
|
|
3208
3399
|
processTree = options.processTree ?? processTreeControllerFor(platform);
|
|
3209
3400
|
agentProcess = await launcher.spawn({
|
|
3210
3401
|
command: launchPlan.command,
|
|
3211
3402
|
args: launchPlan.args,
|
|
3212
3403
|
cwd: projectRoot,
|
|
3213
|
-
env:
|
|
3404
|
+
env: childEnvironment,
|
|
3214
3405
|
shell: false,
|
|
3215
3406
|
detached: platform === "darwin",
|
|
3216
3407
|
...options.onLog === void 0 ? {} : {
|
|
@@ -3291,6 +3482,183 @@ function createBuddyDevStarter(options = {}) {
|
|
|
3291
3482
|
});
|
|
3292
3483
|
}
|
|
3293
3484
|
|
|
3485
|
+
// packages/cli-core/src/dev/token-store.ts
|
|
3486
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
3487
|
+
|
|
3488
|
+
// packages/cli-core/src/cloud/credential-store.ts
|
|
3489
|
+
import { spawn } from "node:child_process";
|
|
3490
|
+
var DEFAULT_SERVICE = "com.xiaohongshu.buddy.developer-cli";
|
|
3491
|
+
var DEFAULT_ACCOUNT = "developer-session";
|
|
3492
|
+
var MAX_CREDENTIAL_BYTES = 32 * 1024;
|
|
3493
|
+
function safeLabel(value, fallback, name) {
|
|
3494
|
+
const result = value ?? fallback;
|
|
3495
|
+
if (!result || result !== result.trim() || /[\u0000-\u001f\u007f]/u.test(result) || result.length > 256) {
|
|
3496
|
+
throw new TypeError(`${name} is invalid`);
|
|
3497
|
+
}
|
|
3498
|
+
return result;
|
|
3499
|
+
}
|
|
3500
|
+
function serialize(token) {
|
|
3501
|
+
const value = JSON.stringify(token);
|
|
3502
|
+
if (Buffer.byteLength(value, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
3503
|
+
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u4E91\u7AEF\u4F1A\u8BDD\u51ED\u636E\u8FC7\u5927\uFF0C\u672A\u4FDD\u5B58");
|
|
3504
|
+
}
|
|
3505
|
+
return value;
|
|
3506
|
+
}
|
|
3507
|
+
function parse(source) {
|
|
3508
|
+
if (Buffer.byteLength(source, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
3509
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3510
|
+
}
|
|
3511
|
+
let value;
|
|
3512
|
+
try {
|
|
3513
|
+
value = JSON.parse(source);
|
|
3514
|
+
} catch {
|
|
3515
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3516
|
+
}
|
|
3517
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3518
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3519
|
+
}
|
|
3520
|
+
const record2 = value;
|
|
3521
|
+
if (Object.keys(record2).length !== 3 || typeof record2.accessToken !== "string" || typeof record2.subject !== "string" || !Number.isSafeInteger(record2.expiresAt)) {
|
|
3522
|
+
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3523
|
+
}
|
|
3524
|
+
return Object.freeze({
|
|
3525
|
+
accessToken: record2.accessToken,
|
|
3526
|
+
subject: record2.subject,
|
|
3527
|
+
expiresAt: record2.expiresAt
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3530
|
+
async function runCredentialCommand(input) {
|
|
3531
|
+
return await new Promise((resolve17, reject) => {
|
|
3532
|
+
const child = spawn(input.command, [...input.args], {
|
|
3533
|
+
shell: false,
|
|
3534
|
+
windowsHide: true,
|
|
3535
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3536
|
+
});
|
|
3537
|
+
const stdout = [];
|
|
3538
|
+
const stderr = [];
|
|
3539
|
+
let outputBytes = 0;
|
|
3540
|
+
const collect = (target, chunk) => {
|
|
3541
|
+
outputBytes += chunk.byteLength;
|
|
3542
|
+
if (outputBytes <= MAX_CREDENTIAL_BYTES) target.push(chunk);
|
|
3543
|
+
};
|
|
3544
|
+
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3545
|
+
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3546
|
+
child.once("error", reject);
|
|
3547
|
+
child.once("close", (code2) => resolve17({
|
|
3548
|
+
code: code2 ?? 1,
|
|
3549
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3550
|
+
stderr: Buffer.concat(stderr).toString("utf8")
|
|
3551
|
+
}));
|
|
3552
|
+
child.stdin.on("error", () => void 0);
|
|
3553
|
+
child.stdin.end(input.input ?? "");
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
var SystemCredentialStore = class {
|
|
3557
|
+
#platform;
|
|
3558
|
+
#service;
|
|
3559
|
+
#account;
|
|
3560
|
+
#run;
|
|
3561
|
+
constructor(options = {}) {
|
|
3562
|
+
this.#platform = options.platform ?? process.platform;
|
|
3563
|
+
this.#service = safeLabel(options.service, DEFAULT_SERVICE, "service");
|
|
3564
|
+
this.#account = safeLabel(options.account, DEFAULT_ACCOUNT, "account");
|
|
3565
|
+
this.#run = options.commandRunner ?? runCredentialCommand;
|
|
3566
|
+
if (this.#platform !== "darwin") {
|
|
3567
|
+
throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D CLI V1 \u7684\u5B89\u5168\u51ED\u636E\u5B58\u50A8\u76EE\u524D\u53EA\u652F\u6301 macOS Keychain");
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
async load() {
|
|
3571
|
+
const result = await this.#run({
|
|
3572
|
+
command: "security",
|
|
3573
|
+
args: ["find-generic-password", "-s", this.#service, "-a", this.#account, "-w"]
|
|
3574
|
+
});
|
|
3575
|
+
if (result.code === 44) return void 0;
|
|
3576
|
+
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u8BFB\u53D6 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u51ED\u636E");
|
|
3577
|
+
const raw = result.stdout.trimEnd();
|
|
3578
|
+
return /^(?:[a-fA-F0-9]{2})+$/u.test(raw) ? Buffer.from(raw, "hex").toString("utf8") : raw;
|
|
3579
|
+
}
|
|
3580
|
+
async save(source) {
|
|
3581
|
+
if (!source || /[\u0000-\u001f\u007f]/u.test(source)) throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
3582
|
+
const line = ["add-generic-password", "-U", "-s", this.#service, "-a", this.#account, "-w", source].map((part) => `"${part.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"')}"`).join(" ") + "\n";
|
|
3583
|
+
if (Buffer.byteLength(line, "utf8") >= 4096) {
|
|
3584
|
+
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u51ED\u636E\u8D85\u8FC7 macOS \u5199\u5165\u957F\u5EA6\u9650\u5236\uFF0C\u672A\u4FDD\u5B58\uFF1B\u8BF7\u8054\u7CFB\u5E73\u53F0\u5904\u7406");
|
|
3585
|
+
}
|
|
3586
|
+
const result = await this.#run({
|
|
3587
|
+
command: "security",
|
|
3588
|
+
args: ["-i", "-q"],
|
|
3589
|
+
input: line
|
|
3590
|
+
});
|
|
3591
|
+
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u628A\u642D\u642D\u51ED\u636E\u5199\u5165 macOS \u94A5\u5319\u4E32");
|
|
3592
|
+
const saved = await this.load();
|
|
3593
|
+
if (!saved || saved !== source) throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u51ED\u636E\u5199\u5165\u540E\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
3594
|
+
}
|
|
3595
|
+
async clear() {
|
|
3596
|
+
const result = await this.#run({
|
|
3597
|
+
command: "security",
|
|
3598
|
+
args: ["delete-generic-password", "-s", this.#service, "-a", this.#account]
|
|
3599
|
+
});
|
|
3600
|
+
if (result.code !== 0 && result.code !== 44) {
|
|
3601
|
+
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u6E05\u9664 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u51ED\u636E");
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
};
|
|
3605
|
+
var SystemCloudTokenStore = class {
|
|
3606
|
+
#store;
|
|
3607
|
+
constructor(options = {}) {
|
|
3608
|
+
this.#store = new SystemCredentialStore(options);
|
|
3609
|
+
}
|
|
3610
|
+
async load() {
|
|
3611
|
+
const raw = await this.#store.load();
|
|
3612
|
+
return raw === void 0 ? void 0 : parse(raw);
|
|
3613
|
+
}
|
|
3614
|
+
async save(token) {
|
|
3615
|
+
await this.#store.save(serialize(token));
|
|
3616
|
+
}
|
|
3617
|
+
async clear() {
|
|
3618
|
+
await this.#store.clear();
|
|
3619
|
+
}
|
|
3620
|
+
};
|
|
3621
|
+
|
|
3622
|
+
// packages/cli-core/src/dev/token-store.ts
|
|
3623
|
+
var SystemBuddyTokenStore = class {
|
|
3624
|
+
#store;
|
|
3625
|
+
constructor(options) {
|
|
3626
|
+
if (![options.subject, options.buddyId].every((value) => value.trim() && !/[\u0000-\u001f\u007f]/u.test(value))) throw new TypeError("\u5F00\u53D1 Token \u7684\u8D26\u53F7\u548C\u642D\u5B50 ID \u65E0\u6548");
|
|
3627
|
+
const scope = JSON.stringify([normalizePlatformUrl(options.platformUrl).origin, options.subject, options.buddyId]);
|
|
3628
|
+
this.#store = new SystemCredentialStore({
|
|
3629
|
+
service: "com.xiaohongshu.buddy.developer-cli.dev-token",
|
|
3630
|
+
account: createHash5("sha256").update(scope).digest("hex"),
|
|
3631
|
+
platform: options.platform,
|
|
3632
|
+
commandRunner: options.commandRunner
|
|
3633
|
+
});
|
|
3634
|
+
}
|
|
3635
|
+
async load() {
|
|
3636
|
+
const raw = await this.#store.load();
|
|
3637
|
+
if (raw === void 0) return void 0;
|
|
3638
|
+
let value;
|
|
3639
|
+
try {
|
|
3640
|
+
value = JSON.parse(raw);
|
|
3641
|
+
} catch {
|
|
3642
|
+
throw corrupt();
|
|
3643
|
+
}
|
|
3644
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || Object.keys(value).length !== 1 || !("token" in value)) throw corrupt();
|
|
3645
|
+
return validateToken(value.token);
|
|
3646
|
+
}
|
|
3647
|
+
async save(token) {
|
|
3648
|
+
await this.#store.save(JSON.stringify({ token: validateToken(token) }));
|
|
3649
|
+
}
|
|
3650
|
+
async clear() {
|
|
3651
|
+
await this.#store.clear();
|
|
3652
|
+
}
|
|
3653
|
+
};
|
|
3654
|
+
function corrupt() {
|
|
3655
|
+
return new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u672C\u673A\u5F00\u53D1\u51ED\u636E\u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 --refresh-token \u91CD\u65B0\u9886\u53D6");
|
|
3656
|
+
}
|
|
3657
|
+
function validateToken(value) {
|
|
3658
|
+
if (typeof value !== "string" || !value.trim() || /[\u0000-\u001f\u007f]/u.test(value) || value.length > 1024) throw corrupt();
|
|
3659
|
+
return value;
|
|
3660
|
+
}
|
|
3661
|
+
|
|
3294
3662
|
// packages/cli-core/src/commands/dev.ts
|
|
3295
3663
|
var DevCommandUsageError = class extends Error {
|
|
3296
3664
|
constructor(message) {
|
|
@@ -3299,12 +3667,13 @@ var DevCommandUsageError = class extends Error {
|
|
|
3299
3667
|
}
|
|
3300
3668
|
};
|
|
3301
3669
|
var DEV_COMMAND_HELP = `\u7528\u6CD5:
|
|
3302
|
-
buddy-cli dev [--app-server <App Server URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
3670
|
+
buddy-cli dev [--app-server <App Server URL>] [--directory <\u9879\u76EE\u76EE\u5F55>] [--refresh-token]
|
|
3303
3671
|
|
|
3304
3672
|
\u8BF4\u660E:
|
|
3305
3673
|
\u672A\u914D\u7F6E\u5730\u5740\u65F6\u8FDE\u63A5 MLB \u6D4B\u8BD5\u73AF\u5883 ${DEFAULT_MLB_TEST_URL}\uFF08HTTP \u4EC5\u9650\u6D4B\u8BD5\uFF09\u3002
|
|
3306
3674
|
CLI \u7EDF\u4E00\u542F\u52A8\u5E76\u7BA1\u7406\u5DE5\u7A0B\u4E2D\u7684\u5B98\u65B9 Runtime\u3002
|
|
3307
|
-
DEV \u4F1A\u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u83B7\u53D6\u5F00\u53D1\u8005\u4F1A\u8BDD\uFF0C\
|
|
3675
|
+
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\u540E\u7EED\u4F18\u5148\u590D\u7528\uFF1B\u767B\u5F55\u51ED\u636E\u4ECD\u4F7F\u7528\u7CFB\u7EDF\u51ED\u636E\u5B58\u50A8\u3002
|
|
3676
|
+
--refresh-token \u91CD\u65B0\u9886\u53D6\u5DF2\u6709\u5F00\u53D1 Token\uFF0C\u7528\u4E8E\u670D\u52A1\u7AEF\u91CD\u7F6E\u540E\u7684\u672C\u673A\u66F4\u65B0\uFF1B\u4E0D\u4F1A\u751F\u6210\u65B0\u5BC6\u94A5\u3002`;
|
|
3308
3677
|
function valueAfter(args, index, flag2) {
|
|
3309
3678
|
const value = args[index + 1];
|
|
3310
3679
|
if (value === void 0 || !value.trim()) throw new DevCommandUsageError(`${flag2} \u7F3A\u5C11\u503C`);
|
|
@@ -3319,6 +3688,7 @@ function parseDevArguments(args) {
|
|
|
3319
3688
|
let directorySet = false;
|
|
3320
3689
|
let appServer;
|
|
3321
3690
|
let help = false;
|
|
3691
|
+
let refreshToken = false;
|
|
3322
3692
|
for (let index = 0; index < args.length; index += 1) {
|
|
3323
3693
|
const flag2 = args[index];
|
|
3324
3694
|
if (flag2 === "--help" || flag2 === "-h") {
|
|
@@ -3326,6 +3696,11 @@ function parseDevArguments(args) {
|
|
|
3326
3696
|
help = true;
|
|
3327
3697
|
continue;
|
|
3328
3698
|
}
|
|
3699
|
+
if (flag2 === "--refresh-token") {
|
|
3700
|
+
if (refreshToken) throw new DevCommandUsageError("--refresh-token \u4E0D\u80FD\u91CD\u590D");
|
|
3701
|
+
refreshToken = true;
|
|
3702
|
+
continue;
|
|
3703
|
+
}
|
|
3329
3704
|
const value = valueAfter(args, index, flag2 ?? "\u53C2\u6570");
|
|
3330
3705
|
index += 1;
|
|
3331
3706
|
switch (flag2) {
|
|
@@ -3341,7 +3716,7 @@ function parseDevArguments(args) {
|
|
|
3341
3716
|
throw new DevCommandUsageError(`\u672A\u77E5 dev \u53C2\u6570 ${flag2}`);
|
|
3342
3717
|
}
|
|
3343
3718
|
}
|
|
3344
|
-
return { directory, appServer, help };
|
|
3719
|
+
return { directory, appServer, help, refreshToken };
|
|
3345
3720
|
}
|
|
3346
3721
|
function appServerUrl(override, env) {
|
|
3347
3722
|
try {
|
|
@@ -3375,7 +3750,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
3375
3750
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
3376
3751
|
const environment = options.env ?? process.env;
|
|
3377
3752
|
const url = appServerUrl(parsed.appServer, environment);
|
|
3378
|
-
const authorizationInput = { appServerUrl: url };
|
|
3753
|
+
const authorizationInput = { appServerUrl: url, ...parsed.refreshToken ? { refreshToken: true } : {} };
|
|
3379
3754
|
if (!options.authorize) {
|
|
3380
3755
|
throw new DevCommandUsageError("\u5F53\u524D CLI Host \u5C1A\u672A\u914D\u7F6E\u642D\u642D\u8D26\u53F7\u767B\u5F55 Adapter");
|
|
3381
3756
|
}
|
|
@@ -3427,7 +3802,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
3427
3802
|
import { resolve as resolve13 } from "node:path";
|
|
3428
3803
|
|
|
3429
3804
|
// packages/cli-core/src/cloud/auth.ts
|
|
3430
|
-
import { randomUUID as
|
|
3805
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
3431
3806
|
|
|
3432
3807
|
// packages/cli-core/src/cloud/validation.ts
|
|
3433
3808
|
function record(value, path = "request") {
|
|
@@ -3478,7 +3853,7 @@ var MemoryCloudTokenStore = class {
|
|
|
3478
3853
|
return this.#token === void 0 ? void 0 : frozenClone(this.#token);
|
|
3479
3854
|
}
|
|
3480
3855
|
async save(token) {
|
|
3481
|
-
this.#token =
|
|
3856
|
+
this.#token = validateToken2(token);
|
|
3482
3857
|
}
|
|
3483
3858
|
async clear() {
|
|
3484
3859
|
this.#token = void 0;
|
|
@@ -3495,7 +3870,7 @@ var CloudAuthenticator = class {
|
|
|
3495
3870
|
this.#adapter = adapter;
|
|
3496
3871
|
this.#store = store;
|
|
3497
3872
|
this.#now = options.now ?? Date.now;
|
|
3498
|
-
this.#createState = options.createState ??
|
|
3873
|
+
this.#createState = options.createState ?? randomUUID7;
|
|
3499
3874
|
this.#minimumValidityMs = options.minimumValidityMs ?? 3e4;
|
|
3500
3875
|
if (!Number.isSafeInteger(this.#minimumValidityMs) || this.#minimumValidityMs < 0) {
|
|
3501
3876
|
throw new TypeError("minimumValidityMs must be a non-negative safe integer");
|
|
@@ -3534,7 +3909,7 @@ var CloudAuthenticator = class {
|
|
|
3534
3909
|
}
|
|
3535
3910
|
if (stored === void 0) return void 0;
|
|
3536
3911
|
try {
|
|
3537
|
-
const token =
|
|
3912
|
+
const token = validateToken2(stored);
|
|
3538
3913
|
if (token.expiresAt > this.#now() + this.#minimumValidityMs) return token;
|
|
3539
3914
|
} catch {
|
|
3540
3915
|
}
|
|
@@ -3555,7 +3930,7 @@ var CloudAuthenticator = class {
|
|
|
3555
3930
|
"\u6D4F\u89C8\u5668\u767B\u5F55\u8FD4\u56DE\u7684 state \u4E0D\u5339\u914D\uFF1B\u51ED\u636E\u672A\u4FDD\u5B58"
|
|
3556
3931
|
);
|
|
3557
3932
|
}
|
|
3558
|
-
const token =
|
|
3933
|
+
const token = validateToken2(resultRecord.token);
|
|
3559
3934
|
if (token.expiresAt <= this.#now() + this.#minimumValidityMs) {
|
|
3560
3935
|
throw new CloudLifecycleError("AUTH_TOKEN_EXPIRED", "\u6D4F\u89C8\u5668\u767B\u5F55\u8FD4\u56DE\u4E86\u5DF2\u8FC7\u671F\u6216\u5373\u5C06\u8FC7\u671F\u7684\u4F1A\u8BDD");
|
|
3561
3936
|
}
|
|
@@ -3563,7 +3938,7 @@ var CloudAuthenticator = class {
|
|
|
3563
3938
|
return token;
|
|
3564
3939
|
}
|
|
3565
3940
|
};
|
|
3566
|
-
function
|
|
3941
|
+
function validateToken2(value) {
|
|
3567
3942
|
try {
|
|
3568
3943
|
const input = record(value, "cloud token");
|
|
3569
3944
|
exactKeys(input, ["accessToken", "subject", "expiresAt"], [], "cloud token");
|
|
@@ -3597,17 +3972,17 @@ function positiveTimeout(value, fallback, name) {
|
|
|
3597
3972
|
}
|
|
3598
3973
|
function object3(value, path) {
|
|
3599
3974
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3600
|
-
throw
|
|
3975
|
+
throw invalid2(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
3601
3976
|
}
|
|
3602
3977
|
return value;
|
|
3603
3978
|
}
|
|
3604
|
-
function
|
|
3979
|
+
function invalid2(message) {
|
|
3605
3980
|
return new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", message);
|
|
3606
3981
|
}
|
|
3607
3982
|
function safeText(value, path, allowNewlines = false, allowEmpty = false) {
|
|
3608
|
-
if (typeof value !== "string" || !allowEmpty && !value.trim()) throw
|
|
3983
|
+
if (typeof value !== "string" || !allowEmpty && !value.trim()) throw invalid2(`${path} \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
3609
3984
|
const invalidControl = allowNewlines ? /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/u : /[\u0000-\u001f\u007f]/u;
|
|
3610
|
-
if (invalidControl.test(value)) throw
|
|
3985
|
+
if (invalidControl.test(value)) throw invalid2(`${path} \u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26`);
|
|
3611
3986
|
return value;
|
|
3612
3987
|
}
|
|
3613
3988
|
function optionalText(value, path, allowNewlines = false) {
|
|
@@ -3629,23 +4004,23 @@ function meta(value, path) {
|
|
|
3629
4004
|
});
|
|
3630
4005
|
}
|
|
3631
4006
|
function flag(value, path) {
|
|
3632
|
-
if (typeof value !== "boolean") throw
|
|
4007
|
+
if (typeof value !== "boolean") throw invalid2(`${path} \u5FC5\u987B\u662F boolean`);
|
|
3633
4008
|
return value;
|
|
3634
4009
|
}
|
|
3635
4010
|
function timestamp(value, path) {
|
|
3636
|
-
if (!Number.isSafeInteger(value) || value < 0) throw
|
|
4011
|
+
if (!Number.isSafeInteger(value) || value < 0) throw invalid2(`${path} \u5FC5\u987B\u662F\u975E\u8D1F\u6BEB\u79D2\u65F6\u95F4\u6233`);
|
|
3637
4012
|
return value;
|
|
3638
4013
|
}
|
|
3639
4014
|
function nullableText(value, path) {
|
|
3640
4015
|
return value === null ? null : safeText(value, path);
|
|
3641
4016
|
}
|
|
3642
4017
|
function nonNegativeInteger(value, path) {
|
|
3643
|
-
if (!Number.isSafeInteger(value) || value < 0) throw
|
|
4018
|
+
if (!Number.isSafeInteger(value) || value < 0) throw invalid2(`${path} \u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570`);
|
|
3644
4019
|
return value;
|
|
3645
4020
|
}
|
|
3646
4021
|
function auditStatus(value, path) {
|
|
3647
4022
|
if (value !== "auditing" && value !== "approved" && value !== "rejected") {
|
|
3648
|
-
throw
|
|
4023
|
+
throw invalid2(`${path} \u5FC5\u987B\u662F auditing\u3001approved \u6216 rejected`);
|
|
3649
4024
|
}
|
|
3650
4025
|
return value;
|
|
3651
4026
|
}
|
|
@@ -3693,7 +4068,7 @@ async function boundedJson(response) {
|
|
|
3693
4068
|
total += item.value.byteLength;
|
|
3694
4069
|
if (total > MAX_RESPONSE_BYTES) {
|
|
3695
4070
|
void reader.cancel().catch(() => void 0);
|
|
3696
|
-
throw
|
|
4071
|
+
throw invalid2("\u5E73\u53F0\u54CD\u5E94\u8D85\u8FC7 1 MiB");
|
|
3697
4072
|
}
|
|
3698
4073
|
chunks.push(item.value);
|
|
3699
4074
|
}
|
|
@@ -3709,7 +4084,7 @@ async function boundedJson(response) {
|
|
|
3709
4084
|
try {
|
|
3710
4085
|
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
3711
4086
|
} catch {
|
|
3712
|
-
throw
|
|
4087
|
+
throw invalid2("\u5E73\u53F0\u54CD\u5E94\u4E0D\u662F\u6709\u6548 JSON");
|
|
3713
4088
|
}
|
|
3714
4089
|
}
|
|
3715
4090
|
function serverError(response, body) {
|
|
@@ -3780,7 +4155,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3780
4155
|
const mediaType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
3781
4156
|
if (mediaType !== "application/json") {
|
|
3782
4157
|
await response.body?.cancel().catch(() => void 0);
|
|
3783
|
-
throw
|
|
4158
|
+
throw invalid2(`\u5E73\u53F0\u54CD\u5E94 Content-Type \u65E0\u6548\uFF08HTTP ${response.status}\uFF09`);
|
|
3784
4159
|
}
|
|
3785
4160
|
const payload = await boundedJson(response);
|
|
3786
4161
|
if (!response.ok) throw serverError(response, payload);
|
|
@@ -3805,7 +4180,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3805
4180
|
}
|
|
3806
4181
|
async listBuddies() {
|
|
3807
4182
|
const source = object3(await this.#request("/cli/buddies", "GET"), "response");
|
|
3808
|
-
if (!Array.isArray(source.buddies)) throw
|
|
4183
|
+
if (!Array.isArray(source.buddies)) throw invalid2("response.buddies \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
3809
4184
|
return Object.freeze({
|
|
3810
4185
|
buddies: Object.freeze(source.buddies.map((item) => buddy(item, false)))
|
|
3811
4186
|
});
|
|
@@ -3825,7 +4200,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3825
4200
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages${suffix}`,
|
|
3826
4201
|
"GET"
|
|
3827
4202
|
), "response");
|
|
3828
|
-
if (!Array.isArray(source.items)) throw
|
|
4203
|
+
if (!Array.isArray(source.items)) throw invalid2("response.items \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
3829
4204
|
return Object.freeze({
|
|
3830
4205
|
total: nonNegativeInteger(source.total, "response.total"),
|
|
3831
4206
|
page: nonNegativeInteger(source.page, "response.page"),
|
|
@@ -3861,13 +4236,13 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3861
4236
|
}
|
|
3862
4237
|
async logout(accessToken) {
|
|
3863
4238
|
const source = object3(await this.#request("/cli/auth/logout", "POST", { accessToken }), "response");
|
|
3864
|
-
if (source.ok !== true) throw
|
|
4239
|
+
if (source.ok !== true) throw invalid2("response.ok \u5FC5\u987B\u662F true");
|
|
3865
4240
|
}
|
|
3866
4241
|
};
|
|
3867
4242
|
|
|
3868
4243
|
// packages/cli-core/src/cloud/browser-login.ts
|
|
3869
|
-
import { createHash as
|
|
3870
|
-
import { spawn } from "node:child_process";
|
|
4244
|
+
import { createHash as createHash6, randomBytes } from "node:crypto";
|
|
4245
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3871
4246
|
import { createServer } from "node:http";
|
|
3872
4247
|
|
|
3873
4248
|
// packages/cli-core/src/cloud/login-result.ts
|
|
@@ -3927,7 +4302,7 @@ function browserCommand(url) {
|
|
|
3927
4302
|
async function openExternalUrl(url) {
|
|
3928
4303
|
const launch = browserCommand(url);
|
|
3929
4304
|
await new Promise((resolve17, reject) => {
|
|
3930
|
-
const child =
|
|
4305
|
+
const child = spawn2(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
3931
4306
|
child.once("error", reject);
|
|
3932
4307
|
child.once("exit", (code2) => code2 === 0 ? resolve17() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
3933
4308
|
});
|
|
@@ -3974,7 +4349,7 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3974
4349
|
if (request.signal?.aborted) throw request.signal.reason;
|
|
3975
4350
|
const callbackSecret = randomBytes(24).toString("hex");
|
|
3976
4351
|
const codeVerifier = randomBytes(32).toString("base64url");
|
|
3977
|
-
const codeChallenge =
|
|
4352
|
+
const codeChallenge = createHash6("sha256").update(codeVerifier).digest("base64url");
|
|
3978
4353
|
let requestId = "";
|
|
3979
4354
|
let settle;
|
|
3980
4355
|
let reject;
|
|
@@ -4110,126 +4485,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
4110
4485
|
}
|
|
4111
4486
|
};
|
|
4112
4487
|
|
|
4113
|
-
// packages/cli-core/src/cloud/credential-store.ts
|
|
4114
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
4115
|
-
var DEFAULT_SERVICE = "com.xiaohongshu.buddy.developer-cli";
|
|
4116
|
-
var DEFAULT_ACCOUNT = "developer-session";
|
|
4117
|
-
var MAX_CREDENTIAL_BYTES = 32 * 1024;
|
|
4118
|
-
function safeLabel(value, fallback, name) {
|
|
4119
|
-
const result = value ?? fallback;
|
|
4120
|
-
if (!result || result !== result.trim() || /[\u0000-\u001f\u007f]/u.test(result) || result.length > 256) {
|
|
4121
|
-
throw new TypeError(`${name} is invalid`);
|
|
4122
|
-
}
|
|
4123
|
-
return result;
|
|
4124
|
-
}
|
|
4125
|
-
function serialize(token) {
|
|
4126
|
-
const value = JSON.stringify(token);
|
|
4127
|
-
if (Buffer.byteLength(value, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
4128
|
-
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u4E91\u7AEF\u4F1A\u8BDD\u51ED\u636E\u8FC7\u5927\uFF0C\u672A\u4FDD\u5B58");
|
|
4129
|
-
}
|
|
4130
|
-
return value;
|
|
4131
|
-
}
|
|
4132
|
-
function parse(source) {
|
|
4133
|
-
if (Buffer.byteLength(source, "utf8") > MAX_CREDENTIAL_BYTES) {
|
|
4134
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4135
|
-
}
|
|
4136
|
-
let value;
|
|
4137
|
-
try {
|
|
4138
|
-
value = JSON.parse(source);
|
|
4139
|
-
} catch {
|
|
4140
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4141
|
-
}
|
|
4142
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4143
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4144
|
-
}
|
|
4145
|
-
const record2 = value;
|
|
4146
|
-
if (Object.keys(record2).length !== 3 || typeof record2.accessToken !== "string" || typeof record2.subject !== "string" || !Number.isSafeInteger(record2.expiresAt)) {
|
|
4147
|
-
throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u7CFB\u7EDF\u51ED\u636E\u683C\u5F0F\u65E0\u6548");
|
|
4148
|
-
}
|
|
4149
|
-
return Object.freeze({
|
|
4150
|
-
accessToken: record2.accessToken,
|
|
4151
|
-
subject: record2.subject,
|
|
4152
|
-
expiresAt: record2.expiresAt
|
|
4153
|
-
});
|
|
4154
|
-
}
|
|
4155
|
-
async function runCredentialCommand(input) {
|
|
4156
|
-
return await new Promise((resolve17, reject) => {
|
|
4157
|
-
const child = spawn2(input.command, [...input.args], {
|
|
4158
|
-
shell: false,
|
|
4159
|
-
windowsHide: true,
|
|
4160
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4161
|
-
});
|
|
4162
|
-
const stdout = [];
|
|
4163
|
-
const stderr = [];
|
|
4164
|
-
let outputBytes = 0;
|
|
4165
|
-
const collect = (target, chunk) => {
|
|
4166
|
-
outputBytes += chunk.byteLength;
|
|
4167
|
-
if (outputBytes <= MAX_CREDENTIAL_BYTES) target.push(chunk);
|
|
4168
|
-
};
|
|
4169
|
-
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
4170
|
-
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
4171
|
-
child.once("error", reject);
|
|
4172
|
-
child.once("close", (code2) => resolve17({
|
|
4173
|
-
code: code2 ?? 1,
|
|
4174
|
-
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
4175
|
-
stderr: Buffer.concat(stderr).toString("utf8")
|
|
4176
|
-
}));
|
|
4177
|
-
child.stdin.on("error", () => void 0);
|
|
4178
|
-
child.stdin.end(input.input ?? "");
|
|
4179
|
-
});
|
|
4180
|
-
}
|
|
4181
|
-
var SystemCloudTokenStore = class {
|
|
4182
|
-
#platform;
|
|
4183
|
-
#service;
|
|
4184
|
-
#account;
|
|
4185
|
-
#run;
|
|
4186
|
-
constructor(options = {}) {
|
|
4187
|
-
this.#platform = options.platform ?? process.platform;
|
|
4188
|
-
this.#service = safeLabel(options.service, DEFAULT_SERVICE, "service");
|
|
4189
|
-
this.#account = safeLabel(options.account, DEFAULT_ACCOUNT, "account");
|
|
4190
|
-
this.#run = options.commandRunner ?? runCredentialCommand;
|
|
4191
|
-
if (this.#platform !== "darwin") {
|
|
4192
|
-
throw new CloudLifecycleError("INVALID_REQUEST", "\u642D\u642D CLI V1 \u7684\u5B89\u5168\u767B\u5F55\u5B58\u50A8\u76EE\u524D\u53EA\u652F\u6301 macOS Keychain");
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
async load() {
|
|
4196
|
-
const result = await this.#run({
|
|
4197
|
-
command: "security",
|
|
4198
|
-
args: ["find-generic-password", "-s", this.#service, "-a", this.#account, "-w"]
|
|
4199
|
-
});
|
|
4200
|
-
if (result.code === 44) return void 0;
|
|
4201
|
-
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u8BFB\u53D6 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u767B\u5F55\u72B6\u6001");
|
|
4202
|
-
const raw = result.stdout.trimEnd();
|
|
4203
|
-
return parse(/^(?:[a-fA-F0-9]{2})+$/u.test(raw) ? Buffer.from(raw, "hex").toString("utf8") : raw);
|
|
4204
|
-
}
|
|
4205
|
-
async save(token) {
|
|
4206
|
-
const source = serialize(token);
|
|
4207
|
-
const line = ["add-generic-password", "-U", "-s", this.#service, "-a", this.#account, "-w", source].map((part) => `"${part.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"')}"`).join(" ") + "\n";
|
|
4208
|
-
if (Buffer.byteLength(line, "utf8") >= 4096) {
|
|
4209
|
-
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u767B\u5F55\u51ED\u636E\u8D85\u8FC7 macOS \u5199\u5165\u957F\u5EA6\u9650\u5236\uFF0C\u672A\u4FDD\u5B58\uFF1B\u8BF7\u8054\u7CFB\u5E73\u53F0\u5904\u7406");
|
|
4210
|
-
}
|
|
4211
|
-
const result = await this.#run({
|
|
4212
|
-
command: "security",
|
|
4213
|
-
args: ["-i", "-q"],
|
|
4214
|
-
input: line
|
|
4215
|
-
});
|
|
4216
|
-
if (result.code !== 0) throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u628A\u642D\u642D\u767B\u5F55\u72B6\u6001\u5199\u5165 macOS \u94A5\u5319\u4E32");
|
|
4217
|
-
const saved = await this.load();
|
|
4218
|
-
if (!saved || serialize(saved) !== source) throw new CloudLifecycleError("AUTH_CREDENTIAL_CORRUPT", "\u767B\u5F55\u51ED\u636E\u5199\u5165\u540E\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
|
|
4219
|
-
}
|
|
4220
|
-
async clear() {
|
|
4221
|
-
const result = await this.#run({
|
|
4222
|
-
command: "security",
|
|
4223
|
-
args: ["delete-generic-password", "-s", this.#service, "-a", this.#account]
|
|
4224
|
-
});
|
|
4225
|
-
if (result.code !== 0 && result.code !== 44) {
|
|
4226
|
-
throw new CloudLifecycleError("AUTH_TOKEN_INVALID", "\u65E0\u6CD5\u6E05\u9664 macOS \u94A5\u5319\u4E32\u4E2D\u7684\u642D\u642D\u767B\u5F55\u72B6\u6001");
|
|
4227
|
-
}
|
|
4228
|
-
}
|
|
4229
|
-
};
|
|
4230
|
-
|
|
4231
4488
|
// packages/cli-core/src/cloud/mock-client.ts
|
|
4232
|
-
import { createHash as
|
|
4489
|
+
import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
|
|
4233
4490
|
var MockDeveloperPlatformClient = class {
|
|
4234
4491
|
#now;
|
|
4235
4492
|
#buddies = /* @__PURE__ */ new Map();
|
|
@@ -4257,7 +4514,7 @@ var MockDeveloperPlatformClient = class {
|
|
|
4257
4514
|
const current = this.#require(request.id);
|
|
4258
4515
|
validateBuddyMetaUpdate(request.meta);
|
|
4259
4516
|
const onlyAvatar = Object.keys(request.meta).every((key) => key === "avatar");
|
|
4260
|
-
const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${
|
|
4517
|
+
const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${createHash7("sha256").update(request.avatar.bytes).digest("hex").slice(0, 16)}` : request.meta.avatar === null ? null : current.meta?.avatar ?? null;
|
|
4261
4518
|
const next = this.#value({ ...current, meta: { ...onlyAvatar ? current.meta : request.meta, avatar }, updatedAt: this.#now() });
|
|
4262
4519
|
this.#buddies.set(request.id, next);
|
|
4263
4520
|
return structuredClone(next);
|
|
@@ -4554,8 +4811,8 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
4554
4811
|
var CLOUD_COMMAND_HELP = HELP;
|
|
4555
4812
|
|
|
4556
4813
|
// packages/cli-core/src/migration/index.ts
|
|
4557
|
-
import { lstat as lstat9, open as
|
|
4558
|
-
import { extname, isAbsolute as isAbsolute2, join as
|
|
4814
|
+
import { lstat as lstat9, open as open5, readdir as readdir4, realpath as realpath3, stat } from "node:fs/promises";
|
|
4815
|
+
import { extname, isAbsolute as isAbsolute2, join as join13, relative, resolve as resolve14, sep, win32 as win322 } from "node:path";
|
|
4559
4816
|
var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
|
|
4560
4817
|
maxFiles: 128,
|
|
4561
4818
|
maxFileBytes: 256 * 1024,
|
|
@@ -4724,7 +4981,7 @@ async function existingProjectRoot(value) {
|
|
|
4724
4981
|
}
|
|
4725
4982
|
}
|
|
4726
4983
|
async function existingSkillRoot(projectRoot, skillPath) {
|
|
4727
|
-
const candidate = skillPath === "." ? projectRoot :
|
|
4984
|
+
const candidate = skillPath === "." ? projectRoot : join13(projectRoot, ...skillPath.split("/"));
|
|
4728
4985
|
try {
|
|
4729
4986
|
await lstat9(candidate);
|
|
4730
4987
|
} catch (cause) {
|
|
@@ -4761,7 +5018,7 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
4761
5018
|
}
|
|
4762
5019
|
async function assertEntrypoint(context) {
|
|
4763
5020
|
const entryPath = reportChild(context.skillPath, "SKILL.md");
|
|
4764
|
-
const candidate =
|
|
5021
|
+
const candidate = join13(context.skillRoot, "SKILL.md");
|
|
4765
5022
|
try {
|
|
4766
5023
|
await lstat9(candidate);
|
|
4767
5024
|
const target = await realpath3(candidate);
|
|
@@ -4865,7 +5122,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
4865
5122
|
}
|
|
4866
5123
|
for (const entry2 of entries) {
|
|
4867
5124
|
const childReportPath = reportChild(reportDirectory, entry2.name);
|
|
4868
|
-
const childPath =
|
|
5125
|
+
const childPath = join13(directory, entry2.name);
|
|
4869
5126
|
let target;
|
|
4870
5127
|
try {
|
|
4871
5128
|
target = await realpath3(childPath);
|
|
@@ -4907,7 +5164,7 @@ async function scanFile(actualPath, reportPath, context) {
|
|
|
4907
5164
|
}
|
|
4908
5165
|
let handle;
|
|
4909
5166
|
try {
|
|
4910
|
-
handle = await
|
|
5167
|
+
handle = await open5(actualPath, "r");
|
|
4911
5168
|
} catch (cause) {
|
|
4912
5169
|
return rejectScan("SKILL_SCAN_IO_ERROR", reportPath, "\u65E0\u6CD5\u6253\u5F00 Skill \u6587\u4EF6\u3002", cause);
|
|
4913
5170
|
}
|
|
@@ -5319,9 +5576,9 @@ async function scanExternalSkillPortability(options) {
|
|
|
5319
5576
|
}
|
|
5320
5577
|
|
|
5321
5578
|
// packages/cli-core/src/push/preflight.ts
|
|
5322
|
-
import { createHash as
|
|
5323
|
-
import { lstat as lstat10, open as
|
|
5324
|
-
import { isAbsolute as isAbsolute3, join as
|
|
5579
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
5580
|
+
import { lstat as lstat10, open as open6, readdir as readdir5, realpath as realpath4 } from "node:fs/promises";
|
|
5581
|
+
import { isAbsolute as isAbsolute3, join as join14, relative as relative2, resolve as resolve15, sep as sep2, win32 as win323 } from "node:path";
|
|
5325
5582
|
|
|
5326
5583
|
// packages/cli-core/src/push/errors.ts
|
|
5327
5584
|
var PushPreflightError = class extends Error {
|
|
@@ -5419,7 +5676,7 @@ function error(options) {
|
|
|
5419
5676
|
throw new PushPreflightError(options);
|
|
5420
5677
|
}
|
|
5421
5678
|
function sha256(value) {
|
|
5422
|
-
return `sha256:${
|
|
5679
|
+
return `sha256:${createHash8("sha256").update(value).digest("hex")}`;
|
|
5423
5680
|
}
|
|
5424
5681
|
function positiveLimit(value, name, hardMaximum) {
|
|
5425
5682
|
if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
|
|
@@ -5540,7 +5797,7 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
5540
5797
|
}
|
|
5541
5798
|
}
|
|
5542
5799
|
async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
5543
|
-
const path =
|
|
5800
|
+
const path = join14(projectRoot, filename);
|
|
5544
5801
|
let entry2;
|
|
5545
5802
|
try {
|
|
5546
5803
|
entry2 = await lstat10(path);
|
|
@@ -5650,7 +5907,7 @@ function sameFileIdentity(left, right) {
|
|
|
5650
5907
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs;
|
|
5651
5908
|
}
|
|
5652
5909
|
async function hashRegularFile(options) {
|
|
5653
|
-
const handle = await
|
|
5910
|
+
const handle = await open6(options.absolutePath, "r").catch((cause) => error({
|
|
5654
5911
|
code: "PUSH_PROJECT_INVALID",
|
|
5655
5912
|
path: options.portablePath,
|
|
5656
5913
|
message: `\u65E0\u6CD5\u8BFB\u53D6\u63D0\u4EA4\u6587\u4EF6\uFF1A${options.portablePath}`,
|
|
@@ -5665,7 +5922,7 @@ async function hashRegularFile(options) {
|
|
|
5665
5922
|
message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
|
|
5666
5923
|
});
|
|
5667
5924
|
}
|
|
5668
|
-
const hash3 =
|
|
5925
|
+
const hash3 = createHash8("sha256");
|
|
5669
5926
|
const captured = [];
|
|
5670
5927
|
const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
|
|
5671
5928
|
let bytes = 0;
|
|
@@ -5743,7 +6000,7 @@ async function createManifest(options) {
|
|
|
5743
6000
|
sensitiveEntriesExcluded += 1;
|
|
5744
6001
|
continue;
|
|
5745
6002
|
}
|
|
5746
|
-
const absolutePath =
|
|
6003
|
+
const absolutePath = join14(directory, name);
|
|
5747
6004
|
let entry2;
|
|
5748
6005
|
try {
|
|
5749
6006
|
entry2 = await lstat10(absolutePath);
|
|
@@ -5969,11 +6226,12 @@ function assertOfficialProjectIncluded(files) {
|
|
|
5969
6226
|
message: `package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C\uFF0C\u5E76\u63D0\u4EA4\u76F8\u5E94\u9501\u6587\u4EF6\u3002`
|
|
5970
6227
|
});
|
|
5971
6228
|
}
|
|
5972
|
-
|
|
6229
|
+
const expectedStart = `node ${runtimeUsesEnvironmentIdentity(pkg) ? "--env-file-if-exists=.env " : ""}${BUDDY_PLATFORM_START_ENTRY}`;
|
|
6230
|
+
if (typeof pkg !== "object" || pkg === null || Array.isArray(pkg) || !("scripts" in pkg) || typeof pkg.scripts !== "object" || pkg.scripts === null || !("start" in pkg.scripts) || pkg.scripts.start !== expectedStart) {
|
|
5973
6231
|
error({
|
|
5974
6232
|
code: "PUSH_PROJECT_INVALID",
|
|
5975
6233
|
path: BUDDY_PLATFORM_PACKAGE_JSON,
|
|
5976
|
-
message: `\u5B98\u65B9 Runtime \u5DE5\u7A0B\u7684 package.json \u5FC5\u987B\u8BBE\u7F6E scripts.start \u4E3A "
|
|
6234
|
+
message: `\u5B98\u65B9 Runtime \u5DE5\u7A0B\u7684 package.json \u5FC5\u987B\u8BBE\u7F6E scripts.start \u4E3A "${expectedStart}"\uFF0C\u4F7F\u4E91\u7AEF\u4E0E\u672C\u5730\u4F7F\u7528\u540C\u4E00\u542F\u52A8\u5165\u53E3\u3002`
|
|
5977
6235
|
});
|
|
5978
6236
|
}
|
|
5979
6237
|
}
|
|
@@ -5995,7 +6253,7 @@ async function assertCapturedConfigStable(options) {
|
|
|
5995
6253
|
}
|
|
5996
6254
|
let current;
|
|
5997
6255
|
try {
|
|
5998
|
-
const path =
|
|
6256
|
+
const path = join14(options.projectRoot, options.filename);
|
|
5999
6257
|
const entry2 = await lstat10(path);
|
|
6000
6258
|
if (!entry2.isFile() || entry2.isSymbolicLink()) {
|
|
6001
6259
|
error({
|
|
@@ -6124,16 +6382,16 @@ async function createPushPreflight(options) {
|
|
|
6124
6382
|
}
|
|
6125
6383
|
|
|
6126
6384
|
// packages/cli-core/src/push/bundle.ts
|
|
6127
|
-
import { createHash as
|
|
6385
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
6128
6386
|
import { lstat as lstat11, readFile as readFile11, realpath as realpath5 } from "node:fs/promises";
|
|
6129
|
-
import { join as
|
|
6387
|
+
import { join as join15, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
|
|
6130
6388
|
import { Writable } from "node:stream";
|
|
6131
6389
|
import { pipeline } from "node:stream/promises";
|
|
6132
6390
|
import { createGzip } from "node:zlib";
|
|
6133
6391
|
import { pack } from "tar-stream";
|
|
6134
6392
|
var MEDIA_TYPE = "application/gzip";
|
|
6135
6393
|
function digest2(bytes) {
|
|
6136
|
-
return `sha256:${
|
|
6394
|
+
return `sha256:${createHash9("sha256").update(bytes).digest("hex")}`;
|
|
6137
6395
|
}
|
|
6138
6396
|
function changed(path, message, cause) {
|
|
6139
6397
|
throw new PushPreflightError({
|
|
@@ -6148,7 +6406,7 @@ function inside(root, target) {
|
|
|
6148
6406
|
return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
|
|
6149
6407
|
}
|
|
6150
6408
|
async function capture(root, file) {
|
|
6151
|
-
const absolute =
|
|
6409
|
+
const absolute = join15(root, ...file.path.split("/"));
|
|
6152
6410
|
try {
|
|
6153
6411
|
const entry2 = await lstat11(absolute);
|
|
6154
6412
|
if (!entry2.isFile() || entry2.isSymbolicLink() || entry2.size !== file.bytes) {
|
|
@@ -6182,7 +6440,7 @@ async function createPushSourceBundle(plan) {
|
|
|
6182
6440
|
schemaVersion: "1",
|
|
6183
6441
|
files: files.map((file) => ({ path: file.path, bytes: file.bytes, digest: file.digest }))
|
|
6184
6442
|
});
|
|
6185
|
-
const calculatedBundleDigest = `sha256:${
|
|
6443
|
+
const calculatedBundleDigest = `sha256:${createHash9("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
|
|
6186
6444
|
if (calculatedBundleDigest !== plan.bundleDigest) {
|
|
6187
6445
|
changed(plan.projectRoot, "\u63D0\u4EA4\u6E05\u5355\u8EAB\u4EFD\u5728\u6253\u5305\u524D\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C push");
|
|
6188
6446
|
}
|
|
@@ -6607,7 +6865,7 @@ async function resumeProjectArguments(args, cwd, options) {
|
|
|
6607
6865
|
await assertInitializableBuddyTarget(root);
|
|
6608
6866
|
let entry2;
|
|
6609
6867
|
try {
|
|
6610
|
-
entry2 = await lstat12(
|
|
6868
|
+
entry2 = await lstat12(join16(root, "buddy.agent.json"));
|
|
6611
6869
|
} catch (cause) {
|
|
6612
6870
|
if (cause.code === "ENOENT") {
|
|
6613
6871
|
const draft = await readMetaDraft(root);
|
|
@@ -6840,6 +7098,7 @@ export {
|
|
|
6840
7098
|
PromptCancelledError,
|
|
6841
7099
|
PushPreflightError,
|
|
6842
7100
|
SkillPortabilityScanError,
|
|
7101
|
+
SystemBuddyTokenStore,
|
|
6843
7102
|
SystemCloudTokenStore,
|
|
6844
7103
|
abortedDevError,
|
|
6845
7104
|
applyBuddyDraftInitialization,
|
|
@@ -6861,6 +7120,7 @@ export {
|
|
|
6861
7120
|
isSafeProjectRelativePath,
|
|
6862
7121
|
isWindowsReservedPathSegment,
|
|
6863
7122
|
latestRuntimeTemplate,
|
|
7123
|
+
migrateRuntimeStartup,
|
|
6864
7124
|
normalizePlatformUrl,
|
|
6865
7125
|
openExternalUrl,
|
|
6866
7126
|
parseBuddyAgentConfig,
|