@my-life-buddies/cli 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/bin/core.js +210 -549
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +0 -5
- package/dist/bin/preview.js.map +2 -2
- package/dist/web/app.css +0 -8
- package/dist/web/index.html +0 -7
- package/package.json +3 -3
- package/dist/web/widget-delivery.js +0 -28
package/dist/bin/core.js
CHANGED
|
@@ -459,26 +459,26 @@ async function scaffoldPlatformAgent(projectRoot, name, options = {}) {
|
|
|
459
459
|
throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u76EE\u5F55\u5FC5\u987B\u662F\u666E\u901A\u76EE\u5F55\uFF0C\u4E0D\u80FD\u4F7F\u7528\u6587\u4EF6\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${directoryPath}`);
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
|
-
for (const
|
|
462
|
+
for (const file of files) {
|
|
463
463
|
try {
|
|
464
|
-
handles.push({ path:
|
|
464
|
+
handles.push({ path: file.path, handle: await open(file.path, "wx", 384) });
|
|
465
465
|
} catch (cause) {
|
|
466
466
|
if (cause.code !== "EEXIST") throw cause;
|
|
467
|
-
if (!(await lstat2(
|
|
468
|
-
throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u4F7F\u7528\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${
|
|
467
|
+
if (!(await lstat2(file.path)).isFile()) {
|
|
468
|
+
throw new TypeError(`\u5B98\u65B9\u5DE5\u7A0B\u6587\u4EF6\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\uFF0C\u4E0D\u80FD\u4F7F\u7528\u76EE\u5F55\u6216\u7B26\u53F7\u94FE\u63A5\uFF1A${file.path}`);
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
|
-
const packageFile = files.find((
|
|
473
|
-
const lockFile = files.find((
|
|
474
|
-
if (options.resolveDependencies && lockFile && handles.some((
|
|
472
|
+
const packageFile = files.find((file) => file.path === resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_JSON));
|
|
473
|
+
const lockFile = files.find((file) => file.path === resolve2(projectRoot, BUDDY_PLATFORM_PACKAGE_LOCK));
|
|
474
|
+
if (options.resolveDependencies && lockFile && handles.some((file) => file.path === packageFile.path) && handles.some((file) => file.path === lockFile.path)) {
|
|
475
475
|
const template = await options.resolveDependencies({ packageJson: packageFile.content, packageLock: lockFile.content });
|
|
476
476
|
packageFile.content = template.packageJson;
|
|
477
477
|
lockFile.content = template.packageLock;
|
|
478
478
|
}
|
|
479
|
-
if (lockFile && (handles.some((
|
|
480
|
-
const packageContents = handles.some((
|
|
481
|
-
const lockContents = handles.some((
|
|
479
|
+
if (lockFile && (handles.some((file) => file.path === packageFile.path) || handles.some((file) => file.path === lockFile.path))) {
|
|
480
|
+
const packageContents = handles.some((file) => file.path === packageFile.path) ? packageFile.content : await readFile4(packageFile.path, "utf8");
|
|
481
|
+
const lockContents = handles.some((file) => file.path === lockFile.path) ? lockFile.content : await readFile4(lockFile.path, "utf8");
|
|
482
482
|
const declared = JSON.parse(packageContents).dependencies ?? {};
|
|
483
483
|
const locked = JSON.parse(lockContents).packages?.[""]?.dependencies ?? {};
|
|
484
484
|
if (JSON.stringify(Object.entries(declared).sort()) !== JSON.stringify(Object.entries(locked).sort())) {
|
|
@@ -486,8 +486,8 @@ async function scaffoldPlatformAgent(projectRoot, name, options = {}) {
|
|
|
486
486
|
}
|
|
487
487
|
}
|
|
488
488
|
for (const { path: filePath, handle } of handles) {
|
|
489
|
-
const
|
|
490
|
-
await handle.writeFile(
|
|
489
|
+
const file = files.find((candidate) => candidate.path === filePath);
|
|
490
|
+
await handle.writeFile(file.content, "utf8");
|
|
491
491
|
await handle.sync();
|
|
492
492
|
}
|
|
493
493
|
return Object.freeze({ path, created: handles.length > 0 || createdDirectories.length > 0 });
|
|
@@ -1233,333 +1233,11 @@ ${MODELS_COMMAND_HELP}`);
|
|
|
1233
1233
|
}
|
|
1234
1234
|
}
|
|
1235
1235
|
|
|
1236
|
-
// packages/cli-core/src/commands/widgets.ts
|
|
1237
|
-
import { resolve as resolve7 } from "node:path";
|
|
1238
|
-
|
|
1239
|
-
// packages/cli-core/src/widget-delivery.ts
|
|
1240
|
-
import { createHash } from "node:crypto";
|
|
1241
|
-
import { lstat as lstat6, readFile as readFile6, realpath as realpath3, readdir as readdir2, mkdir as mkdir4, writeFile as writeFile3, rename as rename3 } from "node:fs/promises";
|
|
1242
|
-
import { join as join6, resolve as resolve6 } from "node:path";
|
|
1243
|
-
var LOCK = "widget-design.lock.json";
|
|
1244
|
-
var IMPLEMENTATION = "widget-implementation.json";
|
|
1245
|
-
var RECEIPTS = ".buddy/widget-verification.json";
|
|
1246
|
-
var idPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
|
|
1247
|
-
var hashPattern = /^[a-f0-9]{64}$/u;
|
|
1248
|
-
var sha = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
1249
|
-
var object = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
1250
|
-
var text = (value) => typeof value === "string" && value.trim().length > 0;
|
|
1251
|
-
var WIDGET_REVIEW_CHECKS = ["visual", "interactions", "data-update", "trigger", "read-only"];
|
|
1252
|
-
var WIDGET_REVIEW_STATES = ["initial", "partial", "updated"];
|
|
1253
|
-
var NOTICE = "\u7ED3\u6784\u68C0\u67E5\u4E0E\u9884\u89C8\u6838\u5BF9\u8BB0\u5F55\u5206\u522B\u5C55\u793A\uFF1B\u6838\u5BF9\u8BB0\u5F55\u7531\u6267\u884C\u6838\u5BF9\u8005\u63D0\u4F9B\uFF0C\u5DE5\u5177\u4E0D\u81EA\u52A8\u5224\u65AD\u89C6\u89C9\u4E00\u81F4\uFF0C\u4E5F\u4E0D\u4EE3\u8868\u5F00\u53D1\u8005\u4F53\u9A8C\u9A8C\u6536\u6216\u53D1\u5E03\u6210\u529F\u3002";
|
|
1254
|
-
function portable(path) {
|
|
1255
|
-
if (!text(path) || path.startsWith("/") || path.includes("\\") || /[\x00-\x1f:]/u.test(path) || path.split("/").some((p) => !p || p === "." || p === "..")) throw new Error("\u5C0F\u6302\u4EF6\u8DEF\u5F84\u5FC5\u987B\u662F\u9879\u76EE\u5185\u76F8\u5BF9\u8DEF\u5F84");
|
|
1256
|
-
return path;
|
|
1257
|
-
}
|
|
1258
|
-
async function file(root, path, optional = false) {
|
|
1259
|
-
portable(path);
|
|
1260
|
-
const base = await realpath3(root);
|
|
1261
|
-
let cursor = base;
|
|
1262
|
-
const parts = path.split("/");
|
|
1263
|
-
for (const [index, part] of parts.entries()) {
|
|
1264
|
-
cursor = join6(cursor, part);
|
|
1265
|
-
const stat2 = await lstat6(cursor).catch((e) => {
|
|
1266
|
-
if (e.code === "ENOENT") return void 0;
|
|
1267
|
-
throw e;
|
|
1268
|
-
});
|
|
1269
|
-
if (!stat2) {
|
|
1270
|
-
if (optional) return void 0;
|
|
1271
|
-
throw new Error(`\u7F3A\u5C11\u6587\u4EF6\uFF1A${path}`);
|
|
1272
|
-
}
|
|
1273
|
-
if (stat2.isSymbolicLink() || (index < parts.length - 1 ? !stat2.isDirectory() : !stat2.isFile())) throw new Error(`\u4E0D\u63A5\u53D7\u7B26\u53F7\u94FE\u63A5\u6216\u7279\u6B8A\u6587\u4EF6\uFF1A${path}`);
|
|
1274
|
-
if (index === parts.length - 1 && stat2.size > 8 * 1024 * 1024) throw new Error(`\u6587\u4EF6\u8D85\u8FC7 8 MB\uFF1A${path}`);
|
|
1275
|
-
}
|
|
1276
|
-
return readFile6(cursor);
|
|
1277
|
-
}
|
|
1278
|
-
async function jsonFile(root, path, optional = false) {
|
|
1279
|
-
const bytes = await file(root, path, optional);
|
|
1280
|
-
if (!bytes) return void 0;
|
|
1281
|
-
try {
|
|
1282
|
-
return JSON.parse(bytes.toString("utf8"));
|
|
1283
|
-
} catch {
|
|
1284
|
-
throw new Error(`JSON \u65E0\u6548\uFF1A${path}`);
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
async function save(root, path, value) {
|
|
1288
|
-
portable(path);
|
|
1289
|
-
let cursor = resolve6(root);
|
|
1290
|
-
for (const segment of path.split("/").slice(0, -1)) {
|
|
1291
|
-
cursor = join6(cursor, segment);
|
|
1292
|
-
await mkdir4(cursor).catch((e) => {
|
|
1293
|
-
if (e.code !== "EEXIST") throw e;
|
|
1294
|
-
});
|
|
1295
|
-
const stat2 = await lstat6(cursor);
|
|
1296
|
-
if (!stat2.isDirectory() || stat2.isSymbolicLink()) throw new Error("\u5199\u5165\u76EE\u5F55\u4E0D\u5B89\u5168");
|
|
1297
|
-
}
|
|
1298
|
-
await file(root, path, true);
|
|
1299
|
-
const target = join6(root, path), temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
1300
|
-
await writeFile3(temp, JSON.stringify(value, null, 2) + "\n", { flag: "wx", mode: 384 });
|
|
1301
|
-
await rename3(temp, target);
|
|
1302
|
-
}
|
|
1303
|
-
function validateLock(value) {
|
|
1304
|
-
if (!object(value) || value.schemaVersion !== 1 || !text(value.buddyId) || !text(value.revision) || !hashPattern.test(value.catalogHash) || !Array.isArray(value.items) || value.items.length > 100) throw new Error("\u8BBE\u8BA1\u7ED1\u5B9A\u6587\u4EF6\u65E0\u6548\uFF0C\u8BF7\u8FD0\u884C buddy-cli widgets sync");
|
|
1305
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1306
|
-
for (const item of value.items) {
|
|
1307
|
-
if (!object(item) || typeof item.id !== "string" || item.id.length > 64 || !idPattern.test(item.id) || seen.has(item.id) || !text(item.name) || !hashPattern.test(item.prototypeHash) || !Array.isArray(item.fields) || !item.fields.every(text) || new Set(item.fields).size !== item.fields.length) throw new Error("\u8BBE\u8BA1\u6E05\u5355\u5305\u542B\u65E0\u6548\u6216\u91CD\u590D\u7684\u5C0F\u6302\u4EF6");
|
|
1308
|
-
seen.add(item.id);
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
async function creatorDesign(root) {
|
|
1312
|
-
const session = await jsonFile(root, ".buddy/creator/session.json", true);
|
|
1313
|
-
if (!session) return void 0;
|
|
1314
|
-
if (session.schemaVersion !== 1 || !text(session.platformBuddyId) || !/^[a-f0-9-]{36}$/u.test(session.creationKey)) throw new Error("Creator \u9879\u76EE\u8EAB\u4EFD\u65E0\u6548");
|
|
1315
|
-
const workspace = `.buddy/creator/workspaces/${session.creationKey}`;
|
|
1316
|
-
const state2 = await jsonFile(root, `${workspace}/state.json`);
|
|
1317
|
-
if (!state2.artifacts?.["widget.catalog"]) return void 0;
|
|
1318
|
-
const completion = await jsonFile(root, `${workspace}/completion.json`);
|
|
1319
|
-
if (completion.status !== "ready" || completion.revision !== state2.revision || state2.paused || state2.pendingTurnId || !/^[a-zA-Z0-9_-]+$/u.test(state2.revision)) throw new Error("\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u4EA4\u4ED8\u672A\u5B8C\u6210\u6216\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u5148\u6062\u590D Creator \u5F53\u524D\u8BBE\u8BA1\u9A8C\u6536");
|
|
1320
|
-
const directory = `${workspace}/deliverables/${state2.revision}`;
|
|
1321
|
-
const manifest = await jsonFile(root, `${directory}/MANIFEST.json`);
|
|
1322
|
-
if (state2.buddyId !== session.creationKey || manifest.buddyId !== state2.buddyId) throw new Error("Creator \u4F5C\u54C1\u4E0E\u4EA4\u4ED8\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1323
|
-
if (manifest.complete !== true || manifest.revision !== state2.revision || !Array.isArray(manifest.files)) throw new Error("\u8BBE\u8BA1\u4EA4\u4ED8\u6E05\u5355\u65E0\u6548");
|
|
1324
|
-
const artifact = async (path) => {
|
|
1325
|
-
const bytes = await file(root, `${directory}/${path}`);
|
|
1326
|
-
const entry = manifest.files.find((e) => e.path === path);
|
|
1327
|
-
if (!entry || entry.sha256 !== sha(bytes) || entry.bytes !== bytes.length) throw new Error(`\u8BBE\u8BA1\u6210\u679C\u88AB\u4FEE\u6539\uFF1A${path}\uFF1B\u5148\u6062\u590D\u4EA4\u4ED8`);
|
|
1328
|
-
return bytes;
|
|
1329
|
-
};
|
|
1330
|
-
const confirmed = (id, hash) => state2.artifacts?.[id]?.hash === hash && state2.confirmations?.some((c) => c.objectId === id && c.hash === hash && c.decision === "confirmed" && !c.invalidatedBy);
|
|
1331
|
-
const catalog = JSON.parse((await artifact("widgets/catalog.json")).toString());
|
|
1332
|
-
if (!confirmed("widget.catalog", catalog.hash) || !Array.isArray(catalog.data?.widgets)) throw new Error("\u5C0F\u6302\u4EF6\u6E05\u5355\u4E0D\u662F\u5F53\u524D\u5DF2\u786E\u8BA4\u7248\u672C");
|
|
1333
|
-
const items = [];
|
|
1334
|
-
for (const item of catalog.data.widgets) {
|
|
1335
|
-
if (!text(item.id) || item.id.length > 64 || !idPattern.test(item.id)) throw new Error("\u8BBE\u8BA1\u7C7B\u578B ID \u65E0\u6548");
|
|
1336
|
-
const prefix = `widgets/${item.id}`;
|
|
1337
|
-
const prototype = JSON.parse((await artifact(`${prefix}/prototype.json`)).toString());
|
|
1338
|
-
const acceptance = JSON.parse((await artifact(`${prefix}/acceptance.json`)).toString()).artifact;
|
|
1339
|
-
if (!confirmed(`widget.${item.id}.prototype`, prototype.hash) || !confirmed(`widget.${item.id}.acceptance`, acceptance.hash) || acceptance.data?.prototypeHash !== prototype.hash) throw new Error(`\u8BBE\u8BA1\u5C1A\u672A\u6709\u6548\u9A8C\u6536\uFF1A${item.id}`);
|
|
1340
|
-
for (const asset of ["index.html", "style.css", "app.js"]) await artifact(`${prefix}/${asset}`);
|
|
1341
|
-
items.push({ id: item.id, name: item.name, prototypeHash: prototype.hash, fields: item.fields.map((f) => f.key) });
|
|
1342
|
-
}
|
|
1343
|
-
const result = { schemaVersion: 1, buddyId: session.platformBuddyId, revision: state2.revision, catalogHash: catalog.hash, items };
|
|
1344
|
-
validateLock(result);
|
|
1345
|
-
return result;
|
|
1346
|
-
}
|
|
1347
|
-
async function syncWidgetDesign(root) {
|
|
1348
|
-
const design = await creatorDesign(root);
|
|
1349
|
-
if (!design) throw new Error("\u5F53\u524D\u9879\u76EE\u6CA1\u6709\u5DF2\u9A8C\u6536\u7684\u5C0F\u6302\u4EF6\u8BBE\u8BA1\uFF1B\u76F4\u63A5\u5F00\u53D1\u9879\u76EE\u53EF\u7EE7\u7EED\u4F7F\u7528\u539F\u6D41\u7A0B");
|
|
1350
|
-
const config = await jsonFile(root, "buddy.agent.json");
|
|
1351
|
-
if (config.buddyId !== design.buddyId) throw new Error("\u8BBE\u8BA1\u4E0E\u642D\u5B50\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1352
|
-
const old = await jsonFile(root, IMPLEMENTATION, true);
|
|
1353
|
-
if (old && (!object(old) || old.schemaVersion !== 1 || !Array.isArray(old.widgets))) throw new Error("\u5DF2\u6709\u5B9E\u73B0\u6620\u5C04\u635F\u574F\uFF0C\u4FDD\u7559\u539F\u6587\u4EF6\uFF0C\u8BF7\u4FEE\u590D\u540E\u518D\u540C\u6B65");
|
|
1354
|
-
const mappings = design.items.map((item) => old?.widgets.find((w) => w.designId === item.id) ?? {
|
|
1355
|
-
designId: item.id,
|
|
1356
|
-
typeId: item.id,
|
|
1357
|
-
prototypeHash: item.prototypeHash,
|
|
1358
|
-
fields: Object.fromEntries(item.fields.map((key) => [key, ""])),
|
|
1359
|
-
lifecycle: { create: [], update: [], present: [] }
|
|
1360
|
-
});
|
|
1361
|
-
const next = { schemaVersion: 1, widgets: mappings };
|
|
1362
|
-
if (JSON.stringify(old) !== JSON.stringify(next)) await save(root, IMPLEMENTATION, next);
|
|
1363
|
-
await save(root, LOCK, design);
|
|
1364
|
-
}
|
|
1365
|
-
async function sourceSnapshot(root) {
|
|
1366
|
-
const result = [];
|
|
1367
|
-
const visit = async (path, depth = 0) => {
|
|
1368
|
-
if (depth > 32 || result.length > 1e4) throw new Error("\u5B9E\u73B0\u6E90\u7801\u8D85\u51FA\u68C0\u67E5\u8303\u56F4");
|
|
1369
|
-
const stat2 = await lstat6(join6(root, path)).catch((e) => {
|
|
1370
|
-
if (e.code === "ENOENT") return void 0;
|
|
1371
|
-
throw e;
|
|
1372
|
-
});
|
|
1373
|
-
if (!stat2) return;
|
|
1374
|
-
if (stat2.isSymbolicLink()) throw new Error(`\u5B9E\u73B0\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5\uFF1A${path}`);
|
|
1375
|
-
if (stat2.isDirectory()) {
|
|
1376
|
-
for (const name of (await readdir2(join6(root, path))).sort()) await visit(`${path}/${name}`, depth + 1);
|
|
1377
|
-
} else result.push([path, sha(await file(root, path))]);
|
|
1378
|
-
};
|
|
1379
|
-
for (const name of ["src", "resources", "widgets", "start.mjs", "agent.md", "buddy.agent.json", "package.json", "package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) await visit(name);
|
|
1380
|
-
return result;
|
|
1381
|
-
}
|
|
1382
|
-
async function checkWidgetDelivery(root) {
|
|
1383
|
-
const report = { status: "not-configured", items: [], issues: [], notice: NOTICE };
|
|
1384
|
-
try {
|
|
1385
|
-
const current = await creatorDesign(root);
|
|
1386
|
-
const lockBytes = await file(root, LOCK, true);
|
|
1387
|
-
let lock = lockBytes ? JSON.parse(lockBytes.toString()) : void 0;
|
|
1388
|
-
if (!current && !lock) {
|
|
1389
|
-
if (await file(root, IMPLEMENTATION, true)) throw new Error("\u5B9E\u73B0\u6620\u5C04\u5B58\u5728\uFF0C\u4F46\u8BBE\u8BA1\u7ED1\u5B9A\u7F3A\u5931\uFF0C\u8BF7\u6062\u590D widget-design.lock.json");
|
|
1390
|
-
return report;
|
|
1391
|
-
}
|
|
1392
|
-
report.status = "incomplete";
|
|
1393
|
-
if (!lock) {
|
|
1394
|
-
report.issues.push("\u5DF2\u9A8C\u6536\u8BBE\u8BA1\u5C1A\u672A\u7ED1\u5B9A\u5230\u5DE5\u7A0B\uFF0C\u8BF7\u8FD0\u884C buddy-cli widgets sync");
|
|
1395
|
-
lock = current;
|
|
1396
|
-
}
|
|
1397
|
-
validateLock(lock);
|
|
1398
|
-
report.designRevision = lock.revision;
|
|
1399
|
-
const config = await jsonFile(root, "buddy.agent.json");
|
|
1400
|
-
if (config.buddyId !== lock.buddyId) throw new Error("\u8BBE\u8BA1\u4E0E\u642D\u5B50\u8EAB\u4EFD\u4E0D\u4E00\u81F4");
|
|
1401
|
-
if (current && JSON.stringify(current) !== JSON.stringify(lock)) report.issues.push("\u8BBE\u8BA1\u5DF2\u53D8\u5316\uFF0C\u8BF7\u8FD0\u884C buddy-cli widgets sync\uFF1B\u65E7\u5B9E\u73B0\u548C\u6838\u5BF9\u8BB0\u5F55\u4E0D\u80FD\u6CBF\u7528\u4E3A\u65B0\u7248\u9A8C\u6536");
|
|
1402
|
-
const implementationBytes = await file(root, IMPLEMENTATION, true);
|
|
1403
|
-
const implementation = implementationBytes ? JSON.parse(implementationBytes.toString()) : void 0;
|
|
1404
|
-
const mappings = implementation?.schemaVersion === 1 && Array.isArray(implementation.widgets) ? implementation.widgets : [];
|
|
1405
|
-
if (!implementation || mappings.length !== lock.items.length) report.issues.push("\u5B9E\u73B0\u6620\u5C04\u4E0E\u5DF2\u9A8C\u6536\u6E05\u5355\u6570\u91CF\u4E0D\u7B26");
|
|
1406
|
-
const receipts = await jsonFile(root, RECEIPTS, true);
|
|
1407
|
-
const snapshot = await sourceSnapshot(root);
|
|
1408
|
-
report.files = snapshot.map(([path, digest2]) => ({ path, digest: `sha256:${digest2}` }));
|
|
1409
|
-
if (lockBytes) report.files.push({ path: LOCK, digest: `sha256:${sha(lockBytes)}` });
|
|
1410
|
-
if (implementationBytes) report.files.push({ path: IMPLEMENTATION, digest: `sha256:${sha(implementationBytes)}` });
|
|
1411
|
-
const usedTypes = /* @__PURE__ */ new Set();
|
|
1412
|
-
for (const mapping of mappings) if (!lock.items.some((item) => item.id === mapping.designId)) report.issues.push("\u5B58\u5728\u4E0D\u5C5E\u4E8E\u5F53\u524D\u8BBE\u8BA1\u7684\u5B9E\u73B0\u6620\u5C04\uFF0C\u8BF7\u8FD0\u884C widgets sync");
|
|
1413
|
-
for (const item of lock.items) {
|
|
1414
|
-
const row = { id: item.id, name: item.name, status: "missing", issues: [] };
|
|
1415
|
-
report.items.push(row);
|
|
1416
|
-
try {
|
|
1417
|
-
const matches = mappings.filter((w) => w.designId === item.id);
|
|
1418
|
-
if (matches.length !== 1) throw new Error("\u7F3A\u5C11\u6216\u91CD\u590D\u7684\u5B9E\u73B0\u6620\u5C04");
|
|
1419
|
-
const m = matches[0];
|
|
1420
|
-
if (!text(m.typeId) || m.typeId.length > 64 || !/^[\p{L}\p{N}][\p{L}\p{N}_-]*$/u.test(m.typeId) || usedTypes.has(m.typeId)) throw new Error("\u6BCF\u4E2A\u5DF2\u9A8C\u6536\u8BBE\u8BA1\u5FC5\u987B\u5BF9\u5E94\u4E00\u4E2A\u72EC\u7ACB\u3001\u6709\u6548\u7684\u7C7B\u578B ID\uFF0C\u4E0D\u80FD\u5408\u5E76\u6210\u540C\u4E00\u4E2A\u5DE5\u4F5C\u53F0");
|
|
1421
|
-
usedTypes.add(m.typeId);
|
|
1422
|
-
row.typeId = m.typeId;
|
|
1423
|
-
if (m.prototypeHash !== item.prototypeHash) row.issues.push("\u5B9E\u73B0\u7ED1\u5B9A\u7684\u539F\u578B\u7248\u672C\u5DF2\u8FC7\u671F");
|
|
1424
|
-
const html = (await file(root, `widgets/${m.typeId}/index.html`)).toString();
|
|
1425
|
-
if (!html.trim()) throw new Error("\u9875\u9762\u4E3A\u7A7A");
|
|
1426
|
-
const schema = await jsonFile(root, `widgets/${m.typeId}/schema.json`);
|
|
1427
|
-
if (!object(schema.modules) || !Object.keys(schema.modules).length) throw new Error("\u7F3A\u5C11\u6709\u6548\u7684\u6570\u636E\u6A21\u5757");
|
|
1428
|
-
for (const key of item.fields) {
|
|
1429
|
-
const binding = m.fields?.[key];
|
|
1430
|
-
if (!text(binding) || !object(schema.modules[binding.split(".")[0]]?.record)) row.issues.push(`\u5B57\u6BB5 ${key} \u672A\u6620\u5C04\u5230\u5B9E\u9645\u6570\u636E\u6A21\u5757`);
|
|
1431
|
-
}
|
|
1432
|
-
for (const phase of ["create", "update", "present"]) {
|
|
1433
|
-
const paths = m.lifecycle?.[phase];
|
|
1434
|
-
if (!Array.isArray(paths) || !paths.length || paths.length > 50 || !paths.every(text)) {
|
|
1435
|
-
row.issues.push(`\u7F3A\u5C11${phase}\u4EE3\u7801\u8DEF\u5F84`);
|
|
1436
|
-
continue;
|
|
1437
|
-
}
|
|
1438
|
-
for (const path of paths) {
|
|
1439
|
-
if (!path.startsWith("src/") || !/\.(?:mjs|js|ts|cjs)$/u.test(path)) throw new Error("\u521B\u5EFA\u3001\u66F4\u65B0\u548C\u5C55\u793A\u5FC5\u987B\u6620\u5C04\u5230 src/ \u4E0B\u7684\u4EE3\u7801");
|
|
1440
|
-
if (!(await file(root, path)).toString().trim()) throw new Error(`\u4EE3\u7801\u4E3A\u7A7A\uFF1A${path}`);
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
if (row.issues.length) continue;
|
|
1444
|
-
row.status = "implemented";
|
|
1445
|
-
row.implementationDigest = sha(JSON.stringify({ design: lock, mapping: m, snapshot }));
|
|
1446
|
-
const receipt = receipts?.items?.[item.id];
|
|
1447
|
-
if (receipt?.implementationDigest === row.implementationDigest) {
|
|
1448
|
-
validateEvidence(receipt.evidence);
|
|
1449
|
-
if (!Array.isArray(receipt.artifacts) || receipt.artifacts.length !== 3) throw new Error("\u6838\u5BF9\u8BC1\u636E\u8BB0\u5F55\u65E0\u6548");
|
|
1450
|
-
for (const [index, artifact] of receipt.artifacts.entries()) {
|
|
1451
|
-
if (artifact.path !== receipt.evidence.states[WIDGET_REVIEW_STATES[index]].artifact) throw new Error("\u6838\u5BF9\u8BC1\u636E\u8DEF\u5F84\u4E0D\u5339\u914D");
|
|
1452
|
-
if (sha(await file(root, artifact.path)) !== artifact.digest) throw new Error("\u9884\u89C8\u6838\u5BF9\u8BC1\u636E\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6838\u5BF9");
|
|
1453
|
-
}
|
|
1454
|
-
if (!receipt.artifacts.length) throw new Error("\u7F3A\u5C11\u9884\u89C8\u6838\u5BF9\u8BC1\u636E");
|
|
1455
|
-
row.status = "verified";
|
|
1456
|
-
} else row.issues.push("\u5C1A\u672A\u8BB0\u5F55\u5F53\u524D\u5B9E\u73B0\u7248\u672C\u7684\u771F\u5B9E\u9884\u89C8\u6838\u5BF9\uFF1B\u6587\u4EF6\u5B58\u5728\u4E0D\u7B49\u4E8E\u6837\u5F0F\u4E0E\u884C\u4E3A\u5DF2\u9A8C\u8BC1");
|
|
1457
|
-
} catch (cause) {
|
|
1458
|
-
row.issues.push(cause instanceof Error ? cause.message : String(cause));
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
if (!report.issues.length && report.items.every((item) => item.status === "verified" && !item.issues.length)) report.status = "ready";
|
|
1462
|
-
} catch (cause) {
|
|
1463
|
-
report.status = "incomplete";
|
|
1464
|
-
report.issues.push(cause instanceof Error ? cause.message : String(cause));
|
|
1465
|
-
}
|
|
1466
|
-
return report;
|
|
1467
|
-
}
|
|
1468
|
-
function validateEvidence(value) {
|
|
1469
|
-
if (!object(value) || !text(value.previewUrl) || !text(value.observedAt) || !Number.isFinite(Date.parse(value.observedAt)) || !text(value.reviewer) || !object(value.states) || !object(value.checks)) throw new Error("\u6838\u5BF9\u8BB0\u5F55\u9700\u5305\u542B previewUrl\u3001observedAt\u3001reviewer\u3001states \u548C checks");
|
|
1470
|
-
const url = new URL(value.previewUrl);
|
|
1471
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname.includes("bootstrap") || url.search || url.hash) throw new Error("\u9884\u89C8\u5730\u5740\u9700\u4F7F\u7528\u4E0D\u542B\u767B\u5F55\u51ED\u636E\u7684\u9875\u9762\u5730\u5740");
|
|
1472
|
-
for (const state2 of WIDGET_REVIEW_STATES) {
|
|
1473
|
-
if (!text(value.states[state2]?.notes) || !text(value.states[state2]?.artifact)) throw new Error(`\u7F3A\u5C11 ${state2} \u7684\u89C2\u5BDF\u8BF4\u660E\u4E0E\u8BC1\u636E\u6587\u4EF6`);
|
|
1474
|
-
portable(value.states[state2].artifact);
|
|
1475
|
-
}
|
|
1476
|
-
for (const check of WIDGET_REVIEW_CHECKS) if (!text(value.checks[check])) throw new Error(`\u7F3A\u5C11 ${check} \u7684\u5B9E\u9645\u89C2\u5BDF\u8BF4\u660E`);
|
|
1477
|
-
}
|
|
1478
|
-
async function recordWidgetVerification(root, designId, evidencePath) {
|
|
1479
|
-
const report = await checkWidgetDelivery(root);
|
|
1480
|
-
const row = report.items.find((item) => item.id === designId);
|
|
1481
|
-
if (report.issues.length || !row?.implementationDigest) throw new Error("\u5148\u5B8C\u6210\u5F53\u524D\u8BBE\u8BA1\u4E0E\u5B9E\u73B0\u7684\u7ED3\u6784\u68C0\u67E5\uFF0C\u518D\u8BB0\u5F55\u9884\u89C8\u6838\u5BF9");
|
|
1482
|
-
const evidence = await jsonFile(root, evidencePath);
|
|
1483
|
-
validateEvidence(evidence);
|
|
1484
|
-
if (evidence.implementationDigest !== row.implementationDigest) throw new Error("\u6838\u5BF9\u8BB0\u5F55\u7684 implementationDigest \u4E0E\u5F53\u524D\u4EE3\u7801\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u5148\u6838\u5BF9\u5F53\u524D\u5B9E\u73B0");
|
|
1485
|
-
const artifacts = [];
|
|
1486
|
-
for (const state2 of WIDGET_REVIEW_STATES) {
|
|
1487
|
-
const path = evidence.states[state2].artifact;
|
|
1488
|
-
artifacts.push({ path, digest: sha(await file(root, path)) });
|
|
1489
|
-
}
|
|
1490
|
-
const old = await jsonFile(root, RECEIPTS, true);
|
|
1491
|
-
if (old && (old.schemaVersion !== 1 || !object(old.items))) throw new Error("\u5DF2\u6709\u6838\u5BF9\u8BB0\u5F55\u635F\u574F\uFF0C\u4FDD\u7559\u6587\u4EF6\u8BF7\u5148\u4FEE\u590D");
|
|
1492
|
-
await save(root, RECEIPTS, { schemaVersion: 1, items: { ...old?.items, [designId]: {
|
|
1493
|
-
implementationDigest: row.implementationDigest,
|
|
1494
|
-
evidence,
|
|
1495
|
-
artifacts
|
|
1496
|
-
} } });
|
|
1497
|
-
}
|
|
1498
|
-
function widgetDeliverySummary(report) {
|
|
1499
|
-
if (report.status === "not-configured") return "\u5F53\u524D\u5DE5\u7A0B\u6CA1\u6709\u7ED1\u5B9A\u7684 Creator \u5C0F\u6302\u4EF6\u8BBE\u8BA1\uFF1B\u672A\u6267\u884C\u8BBE\u8BA1\u4E00\u81F4\u6027\u68C0\u67E5\u3002";
|
|
1500
|
-
return [
|
|
1501
|
-
report.status === "ready" ? "\u5C0F\u6302\u4EF6\u7ED3\u6784\u68C0\u67E5\u4E0E\u5F53\u524D\u7248\u672C\u9884\u89C8\u6838\u5BF9\u8BB0\u5F55\u9F50\u5168\u3002" : "\u5C0F\u6302\u4EF6\u4EA4\u4ED8\u672A\u5B8C\u6210\uFF1A",
|
|
1502
|
-
...report.issues,
|
|
1503
|
-
...report.items.map((item) => `${item.name}\uFF08${item.id}\uFF09\uFF1A${item.status === "verified" ? "\u6838\u5BF9\u5DF2\u8BB0\u5F55" : item.status === "implemented" ? "\u5DF2\u5B9E\u73B0\uFF0C\u5F85\u6838\u5BF9" : "\u5F85\u8865\u9F50\u5B9E\u73B0"}${item.issues.length ? "\uFF1B" + item.issues.join("\uFF1B") : ""}`),
|
|
1504
|
-
report.notice
|
|
1505
|
-
].join("\n");
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
// packages/cli-core/src/commands/widgets.ts
|
|
1509
|
-
var WIDGET_COMMAND_HELP = `\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u843D\u5730\u68C0\u67E5\uFF08\u672C\u5730\uFF0C\u4E0D\u4E0A\u4F20\u3001\u4E0D\u53D1\u5E03\uFF09
|
|
1510
|
-
buddy-cli widgets sync [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
1511
|
-
buddy-cli widgets check [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1512
|
-
buddy-cli widgets verify --id <\u8BBE\u8BA1ID> --evidence <\u9879\u76EE\u5185\u8BB0\u5F55.json> [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
1513
|
-
sync \u4ECE\u5F53\u524D\u5DF2\u9A8C\u6536 Creator \u4EA4\u4ED8\u7ED1\u5B9A\u8BBE\u8BA1\uFF0C\u751F\u6210\u5B9E\u73B0\u6620\u5C04\uFF1B\u4FDD\u7559\u5DF2\u6709\u6620\u5C04\uFF0C\u8BBE\u8BA1\u53D8\u5316\u540E\u91CD\u65B0\u6838\u5BF9\u3002
|
|
1514
|
-
check \u9010\u9879\u5217\u51FA\u9057\u6F0F\u3001\u8FC7\u671F\u7248\u672C\u548C\u5F85\u6838\u5BF9\u9879\uFF1B\u9000\u51FA\u7801 1 \u8868\u793A\u672A\u5B8C\u6210\u3002DEV \u53EF\u7EE7\u7EED\u8C03\u8BD5\uFF0Cpush \u4F1A\u62E6\u622A\u3002
|
|
1515
|
-
verify \u8BB0\u5F55\u771F\u5B9E\u9884\u89C8\u7684\u4E09\u6001\u8BC1\u636E\u4E0E\u4E94\u9879\u89C2\u5BDF\uFF0C\u5E76\u7ED1\u5B9A\u5F53\u524D\u4EE3\u7801\u6458\u8981\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u5224\u65AD\u89C6\u89C9\u4E00\u81F4\u3002`;
|
|
1516
|
-
async function runWidgetsCommand(args, io, cwd) {
|
|
1517
|
-
try {
|
|
1518
|
-
const [action, ...rest] = args;
|
|
1519
|
-
if (!action || action === "--help" || action === "help") {
|
|
1520
|
-
io.write(WIDGET_COMMAND_HELP);
|
|
1521
|
-
return { exitCode: 0 };
|
|
1522
|
-
}
|
|
1523
|
-
if (!["sync", "check", "verify"].includes(action)) throw new Error(WIDGET_COMMAND_HELP);
|
|
1524
|
-
const flags = /* @__PURE__ */ new Map();
|
|
1525
|
-
for (let i = 0; i < rest.length; i++) {
|
|
1526
|
-
const key = rest[i];
|
|
1527
|
-
if (!["--directory", "--id", "--evidence", "--json"].includes(key) || flags.has(key)) throw new Error(`\u4E0D\u652F\u6301\u6216\u91CD\u590D\u7684\u53C2\u6570\uFF1A${key}`);
|
|
1528
|
-
if (key === "--json") flags.set(key, "true");
|
|
1529
|
-
else {
|
|
1530
|
-
const value = rest[++i];
|
|
1531
|
-
if (!value || value.startsWith("--")) throw new Error(`\u7F3A\u5C11\u53C2\u6570\uFF1A${key}`);
|
|
1532
|
-
flags.set(key, value);
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
if (action !== "verify" && (flags.has("--id") || flags.has("--evidence"))) throw new Error("--id / --evidence \u4EC5\u7528\u4E8E verify");
|
|
1536
|
-
if (action !== "check" && flags.has("--json")) throw new Error("--json \u4EC5\u7528\u4E8E check");
|
|
1537
|
-
const root = await findBuddyProjectRoot(resolve7(cwd, flags.get("--directory") ?? "."));
|
|
1538
|
-
if (!root) throw new Error("\u8BF7\u5728\u642D\u5B50\u5DE5\u7A0B\u4E2D\u8FD0\u884C\uFF0C\u6216\u6307\u5B9A --directory");
|
|
1539
|
-
if (action === "sync") {
|
|
1540
|
-
await syncWidgetDesign(root);
|
|
1541
|
-
io.write("\u5DF2\u540C\u6B65\u5F53\u524D\u9A8C\u6536\u8BBE\u8BA1\u5230 widget-design.lock.json\uFF0C\u5E76\u751F\u6210 widget-implementation.json\uFF1B\u8BF7\u9010\u9879\u586B\u5199\u5B57\u6BB5\u4E0E\u5B9E\u73B0\u8DEF\u5F84\uFF0C\u968F\u540E\u8FD0\u884C widgets check\u3002");
|
|
1542
|
-
return { exitCode: 0 };
|
|
1543
|
-
}
|
|
1544
|
-
if (action === "verify") {
|
|
1545
|
-
if (!flags.get("--id") || !flags.get("--evidence")) throw new Error("verify \u9700\u8981 --id \u4E0E --evidence");
|
|
1546
|
-
await recordWidgetVerification(root, flags.get("--id"), flags.get("--evidence"));
|
|
1547
|
-
io.write("\u5DF2\u4FDD\u5B58\u4E0E\u5F53\u524D\u4EE3\u7801\u7ED1\u5B9A\u7684\u9884\u89C8\u6838\u5BF9\u8BB0\u5F55\uFF1B\u8FD9\u4E0D\u4EE3\u8868\u5F00\u53D1\u8005\u4F53\u9A8C\u9A8C\u6536\u6216\u53D1\u5E03\u6210\u529F\u3002");
|
|
1548
|
-
}
|
|
1549
|
-
const report = await checkWidgetDelivery(root);
|
|
1550
|
-
io.write(flags.has("--json") ? JSON.stringify(report, null, 2) : widgetDeliverySummary(report));
|
|
1551
|
-
return { exitCode: report.status === "incomplete" ? 1 : 0 };
|
|
1552
|
-
} catch (cause) {
|
|
1553
|
-
io.writeError(cause instanceof Error ? cause.message : String(cause));
|
|
1554
|
-
return { exitCode: 1 };
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
1236
|
// packages/cli-core/src/index.ts
|
|
1559
|
-
import { lstat as
|
|
1237
|
+
import { lstat as lstat10 } from "node:fs/promises";
|
|
1560
1238
|
|
|
1561
1239
|
// packages/cli-core/src/commands/datasets.ts
|
|
1562
|
-
import { resolve as
|
|
1240
|
+
import { resolve as resolve6 } from "node:path";
|
|
1563
1241
|
var DATASETS_COMMAND_HELP = `\u7528\u6CD5: buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
1564
1242
|
\u5DE5\u7A0B\u5185\u8BFB\u53D6\u5DF2\u5B89\u88C5 Runtime \u7684 DATA_ACCESS_CAPABILITIES\uFF1B\u5DE5\u7A0B\u5916\u8054\u7F51\u8BFB\u53D6\u5E76\u7F13\u5B58\u516C\u5171 npm \u6700\u65B0 Runtime\u3002
|
|
1565
1243
|
\u65E0\u9700\u767B\u5F55\u642D\u642D\u8D26\u53F7\u3002\u5C06\u6240\u9009 id \u4E0E\u7528\u9014 purpose \u5199\u5165 buddy.agent.json.datasets\uFF1B\u76EE\u5F55\u4E0D\u4EE3\u8868\u7528\u6237\u5DF2\u6388\u6743\u3002`;
|
|
@@ -1577,7 +1255,7 @@ async function runDatasetsCommand(args, io, cwd, options = {}) {
|
|
|
1577
1255
|
else throw new Error(`datasets \u53C2\u6570\u65E0\u6548\uFF1A${args[i]}
|
|
1578
1256
|
${DATASETS_COMMAND_HELP}`);
|
|
1579
1257
|
}
|
|
1580
|
-
const { root, source } = await runtimeCatalogSource(
|
|
1258
|
+
const { root, source } = await runtimeCatalogSource(resolve6(cwd, directory ?? "."), { report: (message) => io.writeError(message), ...options });
|
|
1581
1259
|
const catalog = await readRuntimeDataCatalog(root);
|
|
1582
1260
|
if (json) io.write(JSON.stringify({ source, ...catalog }, null, 2));
|
|
1583
1261
|
else {
|
|
@@ -1594,12 +1272,12 @@ ${DATASETS_COMMAND_HELP}`);
|
|
|
1594
1272
|
}
|
|
1595
1273
|
|
|
1596
1274
|
// packages/cli-core/src/index.ts
|
|
1597
|
-
import { join as
|
|
1275
|
+
import { join as join12, resolve as resolve14 } from "node:path";
|
|
1598
1276
|
|
|
1599
1277
|
// packages/cli-core/src/init/creator.ts
|
|
1600
1278
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1601
|
-
import { link as link2, lstat as
|
|
1602
|
-
import { join as
|
|
1279
|
+
import { link as link2, lstat as lstat6, mkdir as mkdir4, readFile as readFile6, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
|
|
1280
|
+
import { join as join6 } from "node:path";
|
|
1603
1281
|
import { fileURLToPath } from "node:url";
|
|
1604
1282
|
|
|
1605
1283
|
// packages/cli-core/src/init/development-handoff.ts
|
|
@@ -1609,7 +1287,7 @@ var DEVELOPMENT_HANDOFF = `## \u5F00\u53D1\u5B8C\u6210\u540E\u4EA4\u7ED9\u5F00\u
|
|
|
1609
1287
|
${DEVELOPMENT_HANDOFF_SUMMARY}
|
|
1610
1288
|
|
|
1611
1289
|
1. \u6839\u636E\u5DF2\u786E\u8BA4\u7684\u9700\u6C42\u5B8C\u6210 Agent / Prompt / Tool \u4E0E\u56DE\u5408\u94A9\u5B50\uFF1B\u6709 Booklet \u65F6\u4EE5\u5DF2\u786E\u8BA4\u624B\u518C\u4E3A\u51C6\u3002\u6A21\u677F\u751F\u6210\u4E0D\u7B49\u4E8E\u4E1A\u52A1\u5DF2\u5B9E\u73B0\u3002
|
|
1612
|
-
2. \u6709\
|
|
1290
|
+
2. \u6709 Creator \u5C0F\u6302\u4EF6\u8BBE\u8BA1\u65F6\uFF0C\u6309\u5DF2\u786E\u8BA4\u7684\u9700\u6C42\u3001\u539F\u578B\u548C\u8BBE\u8BA1\u8BF4\u660E\u5B9E\u73B0\u9875\u9762\u3001\u6570\u636E\u7ED3\u6784\u4E0E\u4E1A\u52A1\u4EA4\u4E92\uFF0C\u5E76\u8BF4\u660E\u672A\u5B8C\u6210\u9879\u3002\u505A\u57FA\u7840\u6280\u672F\u81EA\u6D4B\uFF1A\u68C0\u67E5\u914D\u7F6E\u3001\u4F9D\u8D56\u3001\u6784\u5EFA\u548C\u542F\u52A8\u5165\u53E3\uFF0C\u6267\u884C\u5DF2\u6709\u81EA\u52A8\u5316\u6D4B\u8BD5\u53CA\u660E\u786E\u4E1A\u52A1\u89C4\u5219\u3001\u5B89\u5168\u8FB9\u754C\u7684\u57FA\u7840\u6D4B\u8BD5\u3002\u4E0D\u8981\u628A\u6280\u672F\u68C0\u67E5\u901A\u8FC7\u8BF4\u6210\u4EA7\u54C1\u4F53\u9A8C\u5DF2\u9A8C\u6536\u3002
|
|
1613
1291
|
3. \u4F7F\u7528 \`buddy-cli preview\` \u6253\u5F00\u5F53\u524D\u5DE5\u7A0B\uFF0C\u5728\u9875\u9762\u542F\u52A8 DEV\uFF1B\u5DF2\u6709\u53EF\u7528\u9884\u89C8\u5219\u590D\u7528\uFF0C\u4E0D\u540C\u65F6\u518D\u542F\u52A8\u4E00\u5957 \`dev\` \u8FDB\u7A0B\u3002\u786E\u8BA4\u540D\u79F0\u3001DEV \u72B6\u6001\u548C\u79C1\u804A\u6D88\u606F\u5F80\u8FD4\uFF1B\u82E5\u7F3A\u5C11\u5F80\u8FD4\u8BC1\u636E\uFF0C\u53EA\u53D1\u9001\u4E00\u6761\u4E0D\u542B\u4E2A\u4EBA\u654F\u611F\u4FE1\u606F\u7684\u8054\u8C03\u6D88\u606F\uFF0C\u5DF2\u6709\u5F80\u8FD4\u8BC1\u636E\u65F6\u4E0D\u91CD\u590D\u53D1\u9001\u3002\u5931\u8D25\u5148\u5B9A\u4F4D\u6280\u672F\u95EE\u9898\uFF1B\u4ECD\u672A\u89E3\u51B3\u65F6\u8BF4\u660E\u963B\u585E\uFF0C\u4E0D\u628A Mock\u3001\u9875\u9762\u6253\u5F00\u6216\u8FDB\u7A0B\u542F\u52A8\u5F53\u6210\u94FE\u8DEF\u5DF2\u901A\u3002
|
|
1614
1292
|
4. \u53EF\u4F53\u9A8C\u540E\u4FDD\u7559\u9884\u89C8\u548C DEV\uFF0C\u63D0\u4F9B\u5B9E\u9645\u6253\u5F00\u7684\u9875\u9762\u5730\u5740\uFF0C\u544A\u8BC9\u5F00\u53D1\u8005\u201C\u73B0\u5728\u53EF\u4EE5\u5F00\u59CB\u8BD5\u804A\u4E86\u201D\uFF0C\u8BF4\u660E\u5DF2\u9A8C\u8BC1\u8303\u56F4\u548C\u5DF2\u77E5\u9650\u5236\uFF0C\u7136\u540E\u7B49\u5F85\u53CD\u9988\u3002\u4E0B\u4E00\u8F6E\u7531\u5F00\u53D1\u8005\u4F53\u9A8C\u540E\u53CD\u9988\uFF0C\u518D\u6309\u53CD\u9988\u4FEE\u6539\u548C\u9A8C\u8BC1\u3002
|
|
1615
1293
|
|
|
@@ -1697,17 +1375,13 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
|
|
|
1697
1375
|
|
|
1698
1376
|
\u9047\u5230\u771F\u6B63\u7684\u963B\u585E\u5C31\u8BF4\u660E\u5177\u4F53\u7F3A\u53E3\u5E76\u7B49\u5F85\u7B54\u590D\uFF1B\u4E0D\u53D7\u5F71\u54CD\u7684\u5DF2\u6388\u6743\u5DE5\u4F5C\u53EF\u4EE5\u7EE7\u7EED\u3002\u65E0\u6CD5\u5B9E\u73B0\u7684\u8981\u6C42\u5982\u5B9E\u8BF4\u660E\uFF0C\u4E0D\u6697\u4E2D\u5220\u53BB\uFF0C\u4E5F\u4E0D\u4F2A\u9020\u5F00\u53D1\u8005\u540C\u610F\u964D\u7EA7\u3002\u7528\u6237\u53EA\u8981\u8BBE\u8BA1\u3001\u521D\u59CB\u5316\u6216\u5C40\u90E8\u4FEE\u6539\u65F6\u9075\u5B88\u8BE5\u8303\u56F4\uFF0C\u4E0D\u56E0\u624B\u518C\u5B58\u5728\u5C31\u81EA\u52A8\u6269\u5C55\u4EFB\u52A1\u3002
|
|
1699
1377
|
|
|
1700
|
-
### \
|
|
1701
|
-
|
|
1702
|
-
\u6709 Creator \u5C0F\u6302\u4EF6\u8BBE\u8BA1\u65F6\uFF0C\u5728\u5F00\u53D1\u5F00\u59CB\u524D\u6267\u884C \`buddy-cli widgets sync\`\u3002\u5B83\u4ECE\u5F53\u524D\u6709\u6548\u9A8C\u6536\u4EA4\u4ED8\u751F\u6210 \`widget-design.lock.json\` \u548C \`widget-implementation.json\`\uFF1B\u9010\u9879\u4FDD\u7559\u72EC\u7ACB\u7C7B\u578B ID\uFF0C\u4E0D\u5F97\u628A\u591A\u7C7B\u8BBE\u8BA1\u9759\u9ED8\u5408\u5E76\u6210\u4E00\u4E2A\u901A\u7528\u5DE5\u4F5C\u53F0\u3002\u540C\u6B65\u4E0D\u662F\u5B9E\u73B0\u5B8C\u6210\u3002
|
|
1378
|
+
### \u6309\u8BBE\u8BA1\u5F00\u53D1\u5C0F\u6302\u4EF6
|
|
1703
1379
|
|
|
1704
|
-
\
|
|
1380
|
+
\u6709 Creator \u8BBE\u8BA1\u4EA7\u7269\u65F6\uFF0C\u8BFB\u53D6\u5DF2\u786E\u8BA4\u7684\u9700\u6C42\u624B\u518C\u3001\u5C0F\u6302\u4EF6\u6E05\u5355\u3001\u539F\u578B\u548C\u8BBE\u8BA1\u8BF4\u660E\uFF0C\u6309\u7528\u9014\u5B9E\u73B0\u5404\u7C7B\u5C0F\u6302\u4EF6\u3002\u4FDD\u7559\u8BBE\u8BA1\u8981\u6C42\u7684\u5C55\u793A\u5185\u5BB9\u3001\u5E03\u5C40\u3001\u4EA4\u4E92\u548C\u51FA\u73B0\u65F6\u673A\uFF1B\u65E0\u6CD5\u5B8C\u6210\u65F6\u660E\u786E\u8BF4\u660E\u7F3A\u53E3\uFF0C\u4E0D\u9759\u9ED8\u7701\u7565\u6216\u5408\u5E76\u8BBE\u8BA1\u3002
|
|
1705
1381
|
|
|
1706
|
-
\
|
|
1382
|
+
\u5728\u5DE5\u7A0B\u7684 \`widgets/<type-id>/\` \u4E2D\u7F16\u5199 \`index.html\` \u548C \`schema.json\`\uFF0C\u5728\u4E1A\u52A1\u4EE3\u7801\u4E2D\u63A5\u5165\u521B\u5EFA\u3001\u8BFB\u53D6\u3001\u66F4\u65B0\u548C\u53D1\u9001\u80FD\u529B\u3002\u5F00\u53D1\u8005\u5728 \`buddy-cli preview\` \u4E2D\u67E5\u770B\u771F\u5B9E\u9875\u9762\u548C\u4EA4\u4E92\u6548\u679C\uFF0C\u4FEE\u6539\u540E\u91CD\u542F DEV\u3002\u51C6\u5907\u4E0A\u4F20\u65F6\u4F7F\u7528 \`buddy-cli push\`\uFF0C\u9875\u9762\u548C\u6570\u636E\u5B9A\u4E49\u968F\u5DE5\u7A0B\u6253\u5305\u3002
|
|
1707
1383
|
|
|
1708
|
-
\
|
|
1709
|
-
|
|
1710
|
-
\u6240\u6709\u7C7B\u578B\u7ED3\u6784\u548C\u6838\u5BF9\u8BB0\u5F55\u9F50\u5168\u540E\u624D\u79F0\u201C\u5C0F\u6302\u4EF6\u5F00\u53D1\u6838\u5BF9\u5B8C\u6210\u201D\u3002\u4EE3\u7801\u3001\u9875\u9762\u3001\u4F9D\u8D56\u3001\u6570\u636E\u7ED1\u5B9A\u6216\u8BBE\u8BA1\u7248\u672C\u53D8\u5316\u4F1A\u8BA9\u65E7\u8BB0\u5F55\u5931\u6548\uFF0C\u5E94\u9488\u5BF9\u53D8\u5316\u91CD\u65B0\u6838\u5BF9\u3002\u6CA1\u6709\u5B8C\u6210\u5C31\u660E\u786E\u5217\u51FA\u7F3A\u9879\u3002push \u4F1A\u5728\u4E0A\u4F20\u524D\u62E6\u622A\u4E0D\u5B8C\u6574\u4EA4\u4ED8\uFF0C\u4F46\u4E0D\u4F1A\u66FF\u5F00\u53D1\u8005\u53D1\u8D77\u63D0\u5BA1\u3002\u76F4\u63A5\u5F00\u53D1\u4E14\u65E0 Creator \u8BBE\u8BA1\u7684\u9879\u76EE\u6CBF\u7528\u539F\u6D41\u7A0B\uFF0C\u68C0\u67E5\u7ED3\u679C\u660E\u786E\u4E3A\u201C\u672A\u914D\u7F6E\u8BBE\u8BA1\u6838\u5BF9\u201D\uFF0C\u4E0D\u5192\u5145\u901A\u8FC7\u3002
|
|
1384
|
+
\u6309\u9700\u6C42\u5B8C\u6210\u57FA\u7840\u6280\u672F\u81EA\u6D4B\uFF0C\u5411\u5F00\u53D1\u8005\u8BF4\u660E\u5DF2\u5B9E\u73B0\u5185\u5BB9\u548C\u4ECD\u5B58\u5728\u7684\u95EE\u9898\uFF0C\u518D\u4EA4\u7ED9\u5F00\u53D1\u8005\u4F53\u9A8C\u3002\u8BBE\u8BA1\u8BF4\u660E\u4F5C\u4E3A\u5F00\u53D1\u53C2\u8003\uFF0CCLI \u4E0D\u8981\u6C42\u989D\u5916\u586B\u5199\u8BBE\u8BA1\u6620\u5C04\u6216\u63D0\u4EA4\u9884\u89C8\u6838\u5BF9\u8BB0\u5F55\u3002
|
|
1711
1385
|
|
|
1712
1386
|
### \u6838\u5BF9\u5B9E\u73B0\uFF0C\u518D\u4EA4\u7ED9\u5F00\u53D1\u8005\u4F53\u9A8C
|
|
1713
1387
|
|
|
@@ -1759,16 +1433,16 @@ var REQUIRED = [
|
|
|
1759
1433
|
var CreatorPreparationError = class extends Error {
|
|
1760
1434
|
};
|
|
1761
1435
|
async function state(path) {
|
|
1762
|
-
return
|
|
1436
|
+
return lstat6(path).catch((cause) => {
|
|
1763
1437
|
if (cause.code === "ENOENT") return void 0;
|
|
1764
1438
|
throw cause;
|
|
1765
1439
|
});
|
|
1766
1440
|
}
|
|
1767
1441
|
async function safeDirectory(path) {
|
|
1768
|
-
await
|
|
1442
|
+
await mkdir4(path, { mode: 448 }).catch((cause) => {
|
|
1769
1443
|
if (cause.code !== "EEXIST") throw cause;
|
|
1770
1444
|
});
|
|
1771
|
-
if (!(await
|
|
1445
|
+
if (!(await lstat6(path)).isDirectory()) {
|
|
1772
1446
|
throw new CreatorPreparationError(`\u521B\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u666E\u901A\u76EE\u5F55\uFF0C\u4E0D\u80FD\u4F7F\u7528\u7B26\u53F7\u94FE\u63A5\uFF1A${path}`);
|
|
1773
1447
|
}
|
|
1774
1448
|
}
|
|
@@ -1778,11 +1452,11 @@ async function safeRead(path) {
|
|
|
1778
1452
|
if (!entry.isFile() || entry.size > MAX_LOCAL_FILE_BYTES) {
|
|
1779
1453
|
throw new CreatorPreparationError(`\u521B\u4F5C\u6587\u4EF6\u4E0D\u662F\u666E\u901A\u6587\u4EF6\u6216\u8D85\u51FA\u5927\u5C0F\u9650\u5236\uFF1A${path}`);
|
|
1780
1454
|
}
|
|
1781
|
-
return
|
|
1455
|
+
return readFile6(path);
|
|
1782
1456
|
}
|
|
1783
1457
|
async function writeOnce(path, bytes) {
|
|
1784
1458
|
const temporary = `${path}.${randomUUID2()}.tmp`;
|
|
1785
|
-
await
|
|
1459
|
+
await writeFile3(temporary, bytes, { flag: "wx", mode: 384 });
|
|
1786
1460
|
try {
|
|
1787
1461
|
await link2(temporary, path).catch((cause) => {
|
|
1788
1462
|
if (cause.code !== "EEXIST") throw cause;
|
|
@@ -1793,17 +1467,17 @@ async function writeOnce(path, bytes) {
|
|
|
1793
1467
|
}
|
|
1794
1468
|
async function bundledSkill(root) {
|
|
1795
1469
|
for (const path of REQUIRED) {
|
|
1796
|
-
if (!(await state(
|
|
1470
|
+
if (!(await state(join6(root, path)))?.isFile()) {
|
|
1797
1471
|
throw new CreatorPreparationError(`CLI \u5185\u7F6E Creator Skill \u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5 CLI\uFF08\u7F3A\u5C11 ${path}\uFF09\u3002\u4E0D\u4F1A\u5C1D\u8BD5\u5728\u7EBF\u4E0B\u8F7D\u6216\u8986\u76D6\u9879\u76EE\u8D44\u6599\u3002`);
|
|
1798
1472
|
}
|
|
1799
1473
|
}
|
|
1800
1474
|
return root;
|
|
1801
1475
|
}
|
|
1802
1476
|
function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
1803
|
-
const contextPath =
|
|
1477
|
+
const contextPath = join6(context.projectRoot, ".buddy", "creator", "initial-context.json");
|
|
1804
1478
|
const openArgs = [
|
|
1805
1479
|
"-B",
|
|
1806
|
-
|
|
1480
|
+
join6(skillRoot, "scripts/buddy.py"),
|
|
1807
1481
|
"open",
|
|
1808
1482
|
"--workspace",
|
|
1809
1483
|
workspacePath,
|
|
@@ -1833,7 +1507,7 @@ function handoffMarkdown(context, skillRoot, session, workspacePath) {
|
|
|
1833
1507
|
|
|
1834
1508
|
## \u73B0\u5728\u7531 Coding Agent \u6267\u884C
|
|
1835
1509
|
|
|
1836
|
-
1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(
|
|
1510
|
+
1. \u5B8C\u6574\u8BFB\u53D6 ${JSON.stringify(join6(skillRoot, "SKILL.md"))} \u53CA\u5176\u6307\u5B9A\u7684 references\u3002\u4FDD\u7559\u539F\u6709\u56DB\u9636\u6BB5\u89C4\u5219\uFF0C\u7531\u5F53\u524D\u4E3B\u5BF9\u8BDD\u5B8C\u6210\u91C7\u8BBF\uFF1B\u65E0\u9700\u53E6\u8D77 Sub-agent \u6216\u8FD0\u884C\u91C7\u8BBF\u6A21\u578B\u670D\u52A1\u3002
|
|
1837
1511
|
2. \u67E5\u627E\u5BBF\u4E3B\u6216\u7CFB\u7EDF\u5B9E\u9645\u53EF\u7528\u7684 Python 3.9+\uFF0C\u68C0\u67E5\u7248\u672C\uFF0C\u4FDD\u5B58\u5176\u7EDD\u5BF9\u8DEF\u5F84\u3002\u4E0D\u8981\u5199\u6B7B\u672C\u673A\u8DEF\u5F84\uFF0C\u4E0D\u5B89\u88C5 pip/npm \u4F9D\u8D56\u3002\u6CA1\u6709 Python \u65F6\u62A5\u544A\u7F3A\u53E3\uFF0C\u4FDD\u7559\u672C\u5730\u9879\u76EE\u3002\u6240\u6709 Python \u8C03\u7528\u5747\u52A0 -B\uFF0C\u5E76\u8BBE\u7F6E\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF PYTHONDONTWRITEBYTECODE=1\uFF08\u7531\u9884\u89C8\u5B50\u8FDB\u7A0B\u7EE7\u627F\uFF09\uFF0C\u907F\u514D\u5411 CLI \u5B89\u88C5\u76EE\u5F55\u5199\u5165\u5B57\u8282\u7801\u7F13\u5B58\u3002
|
|
1838
1512
|
3. \u4F7F\u7528\u5DE5\u5177\u4F20\u9012\u72EC\u7ACB\u53C2\u6570\uFF0C\u4E0D\u62FC\u63A5 shell \u5B57\u7B26\u4E32\u3002\u5DE5\u4F5C\u76EE\u5F55\u4E3A ${JSON.stringify(context.projectRoot)}\uFF1B\u4EE5\u5B9E\u9645 Python \u8DEF\u5F84\u4E3A executable\uFF0C\u9996\u6B21\u6253\u5F00\u7684 argv \u5982\u4E0B\uFF1A
|
|
1839
1513
|
|
|
@@ -1870,11 +1544,11 @@ ${DEVELOPMENT_HANDOFF}
|
|
|
1870
1544
|
`;
|
|
1871
1545
|
}
|
|
1872
1546
|
async function prepareCreatorSkill(context, io, options = {}) {
|
|
1873
|
-
const root =
|
|
1547
|
+
const root = join6(context.projectRoot, ".buddy", "creator");
|
|
1874
1548
|
try {
|
|
1875
|
-
await safeDirectory(
|
|
1549
|
+
await safeDirectory(join6(context.projectRoot, ".buddy"));
|
|
1876
1550
|
await safeDirectory(root);
|
|
1877
|
-
const sessionPath =
|
|
1551
|
+
const sessionPath = join6(root, "session.json");
|
|
1878
1552
|
await writeOnce(sessionPath, JSON.stringify({
|
|
1879
1553
|
schemaVersion: 1,
|
|
1880
1554
|
platformBuddyId: context.buddyId,
|
|
@@ -1886,11 +1560,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1886
1560
|
}
|
|
1887
1561
|
options.signal?.throwIfAborted();
|
|
1888
1562
|
const skillRoot = await bundledSkill(options.bundledSkillRoot ?? BUNDLED_CREATOR_SKILL_ROOT);
|
|
1889
|
-
await safeDirectory(
|
|
1890
|
-
const workspacePath =
|
|
1563
|
+
await safeDirectory(join6(root, "workspaces"));
|
|
1564
|
+
const workspacePath = join6(root, "workspaces", session.creationKey);
|
|
1891
1565
|
const workspace = await state(workspacePath);
|
|
1892
1566
|
if (workspace && !workspace.isDirectory()) throw new CreatorPreparationError("\u5DF2\u6709\u521B\u4F5C\u5DE5\u4F5C\u533A\u4E0D\u662F\u666E\u901A\u76EE\u5F55\u3002");
|
|
1893
|
-
const contextPath =
|
|
1567
|
+
const contextPath = join6(root, "initial-context.json");
|
|
1894
1568
|
await writeOnce(contextPath, JSON.stringify({
|
|
1895
1569
|
source: "platform_meta",
|
|
1896
1570
|
name: context.appName,
|
|
@@ -1906,7 +1580,7 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1906
1580
|
} catch {
|
|
1907
1581
|
throw new CreatorPreparationError(`\u5DF2\u6709\u521D\u59CB\u80CC\u666F\u65E0\u6548\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\uFF1A${contextPath}`);
|
|
1908
1582
|
}
|
|
1909
|
-
const handoffPath =
|
|
1583
|
+
const handoffPath = join6(root, "HANDOFF.md");
|
|
1910
1584
|
const handoff = handoffMarkdown({
|
|
1911
1585
|
...context,
|
|
1912
1586
|
appName: initial.name,
|
|
@@ -1917,11 +1591,11 @@ async function prepareCreatorSkill(context, io, options = {}) {
|
|
|
1917
1591
|
if ((await safeRead(handoffPath))?.toString("utf8") !== handoff) {
|
|
1918
1592
|
throw new CreatorPreparationError(`\u5DF2\u6709\u63A5\u7EED\u8BF4\u660E\u88AB\u4FEE\u6539\u6216\u9879\u76EE\u8DEF\u5F84\u5DF2\u53D8\u5316\uFF0C\u5DF2\u4FDD\u7559\u539F\u6587\u4EF6\u3002\u8BF7\u5907\u4EFD\u79FB\u8D70\u8BE5\u8BF4\u660E\u540E\u91CD\u8BD5\uFF1A${handoffPath}`);
|
|
1919
1593
|
}
|
|
1920
|
-
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${
|
|
1594
|
+
io.write(`\u4F7F\u7528 CLI \u5185\u7F6E Creator Skill\uFF0C\u65E0\u9700\u989D\u5916\u4E0B\u8F7D\uFF1A${join6(skillRoot, "SKILL.md")}`);
|
|
1921
1595
|
io.write(`\u4E0B\u4E00\u6B65\u4EA4\u7ED9 Codex / Claude Code \u7B49 Coding Agent\uFF1A\u8BF7\u8BFB\u53D6 ${JSON.stringify(handoffPath)}\uFF0C\u6309\u5F53\u524D\u9636\u6BB5\u63A5\u7EED\uFF1BCreator \u5B8C\u6210\u540E\u8FD4\u56DE CLI \u5F00\u53D1\u6D41\u7A0B\u3002`);
|
|
1922
1596
|
io.write(DEVELOPMENT_HANDOFF_SUMMARY);
|
|
1923
1597
|
io.write("\u672C\u547D\u4EE4\u53EA\u8D1F\u8D23\u8EAB\u4EFD\u767B\u8BB0\u548C Skill \u51C6\u5907\uFF0C\u4E0D\u4EE3\u8868\u91C7\u8BBF\u3001Booklet \u6216 Agent \u5DF2\u5B8C\u6210\u3002\u8BF7\u6309\u5DF2\u6709\u4F5C\u54C1\u8FDB\u5EA6\u63A5\u7EED\uFF0C\u56DB\u518C\u53CA\u5C0F\u6302\u4EF6\u8BBE\u8BA1\u9A8C\u6536\u3001\u4EA4\u4ED8\u6821\u9A8C\u5B8C\u6210\u540E\u81EA\u52A8\u8FDB\u5165\u5B98\u65B9 Runtime \u5F00\u53D1\uFF1B\u91CD\u590D create \u4F1A\u590D\u7528\u540C\u4E00\u4F5C\u54C1\u3002");
|
|
1924
|
-
return { exitCode: 0, creator: { skillPath:
|
|
1598
|
+
return { exitCode: 0, creator: { skillPath: join6(skillRoot, "SKILL.md"), handoffPath, workspacePath } };
|
|
1925
1599
|
} catch (cause) {
|
|
1926
1600
|
const message = cause instanceof CreatorPreparationError ? cause.message : "\u5185\u7F6E\u8D44\u6E90\u8BFB\u53D6\u6216\u672C\u5730\u5199\u5165\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 CLI \u5B89\u88C5\u548C\u76EE\u5F55\u6743\u9650\u3002";
|
|
1927
1601
|
io.writeError(`Creator Skill \u51C6\u5907\u5931\u8D25\uFF1A${message}`);
|
|
@@ -1981,7 +1655,7 @@ function validateBuddyMetaUpdate(meta2) {
|
|
|
1981
1655
|
}
|
|
1982
1656
|
|
|
1983
1657
|
// packages/cli-core/src/commands/dev.ts
|
|
1984
|
-
import { resolve as
|
|
1658
|
+
import { resolve as resolve10 } from "node:path";
|
|
1985
1659
|
|
|
1986
1660
|
// packages/cli-core/src/platform-url.ts
|
|
1987
1661
|
var DEFAULT_MLB_TEST_URL = "http://47.116.168.81:8788";
|
|
@@ -2042,7 +1716,7 @@ function safeCredential(value, name) {
|
|
|
2042
1716
|
function endpoint(baseUrl, path) {
|
|
2043
1717
|
return new URL(path.replace(/^\//u, ""), baseUrl);
|
|
2044
1718
|
}
|
|
2045
|
-
function
|
|
1719
|
+
function object(value) {
|
|
2046
1720
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
2047
1721
|
}
|
|
2048
1722
|
async function responseJson(response) {
|
|
@@ -2084,7 +1758,7 @@ async function responseJson(response) {
|
|
|
2084
1758
|
}
|
|
2085
1759
|
}
|
|
2086
1760
|
function safeServerError(value, fallback) {
|
|
2087
|
-
const candidate =
|
|
1761
|
+
const candidate = object(value)?.error;
|
|
2088
1762
|
if (typeof candidate !== "string" || !candidate.trim()) return fallback;
|
|
2089
1763
|
return candidate.slice(0, 1024).replace(/[\u0000-\u001f\u007f]/gu, " ");
|
|
2090
1764
|
}
|
|
@@ -2165,7 +1839,7 @@ var MlbDevRegistrationClient = class {
|
|
|
2165
1839
|
message: "buddy.agent.json \u4E2D\u7684 buddyId \u65E0\u6548\u3002"
|
|
2166
1840
|
});
|
|
2167
1841
|
}
|
|
2168
|
-
const body =
|
|
1842
|
+
const body = object(await this.#request(
|
|
2169
1843
|
`/cli/buddies/${encodeURIComponent(request.devAgentId)}`,
|
|
2170
1844
|
{
|
|
2171
1845
|
method: "GET",
|
|
@@ -2186,7 +1860,7 @@ var MlbDevRegistrationClient = class {
|
|
|
2186
1860
|
});
|
|
2187
1861
|
}
|
|
2188
1862
|
async listOnline(options) {
|
|
2189
|
-
const body =
|
|
1863
|
+
const body = object(await this.#request("/cli/fleet", {
|
|
2190
1864
|
method: "GET",
|
|
2191
1865
|
signal: options.signal
|
|
2192
1866
|
}));
|
|
@@ -2278,16 +1952,16 @@ function buildAgentEnvironment(options) {
|
|
|
2278
1952
|
}
|
|
2279
1953
|
|
|
2280
1954
|
// packages/cli-core/src/dev/orchestrator.ts
|
|
2281
|
-
import { createHash as
|
|
2282
|
-
import { readFile as
|
|
2283
|
-
import { join as
|
|
1955
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1956
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
1957
|
+
import { join as join8, resolve as resolve8 } from "node:path";
|
|
2284
1958
|
|
|
2285
1959
|
// packages/cli-core/src/dev/launch.ts
|
|
2286
1960
|
import { lstatSync, readFileSync } from "node:fs";
|
|
2287
1961
|
import { createRequire as createRequire2 } from "node:module";
|
|
2288
|
-
import { dirname as dirname2, resolve as
|
|
1962
|
+
import { dirname as dirname2, resolve as resolve7 } from "node:path";
|
|
2289
1963
|
async function officialAgentLaunchPlan(input) {
|
|
2290
|
-
const entry =
|
|
1964
|
+
const entry = resolve7(input.projectRoot, BUDDY_PLATFORM_START_ENTRY);
|
|
2291
1965
|
for (const [filename, kind] of [
|
|
2292
1966
|
[dirname2(BUDDY_PLATFORM_FACTORY_ENTRY), "directory"],
|
|
2293
1967
|
[BUDDY_PLATFORM_START_ENTRY, "file"],
|
|
@@ -2295,7 +1969,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2295
1969
|
[BUDDY_PLATFORM_PACKAGE_JSON, "file"]
|
|
2296
1970
|
]) {
|
|
2297
1971
|
try {
|
|
2298
|
-
const entry2 = lstatSync(
|
|
1972
|
+
const entry2 = lstatSync(resolve7(input.projectRoot, filename));
|
|
2299
1973
|
if (!(kind === "directory" ? entry2.isDirectory() : entry2.isFile())) {
|
|
2300
1974
|
throw new Error(`${filename} must be a regular ${kind}`);
|
|
2301
1975
|
}
|
|
@@ -2308,7 +1982,7 @@ async function officialAgentLaunchPlan(input) {
|
|
|
2308
1982
|
});
|
|
2309
1983
|
}
|
|
2310
1984
|
}
|
|
2311
|
-
const packagePath =
|
|
1985
|
+
const packagePath = resolve7(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
|
|
2312
1986
|
try {
|
|
2313
1987
|
const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
|
|
2314
1988
|
if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
|
|
@@ -2359,16 +2033,16 @@ var NodeAgentProcessLauncher = class {
|
|
|
2359
2033
|
}
|
|
2360
2034
|
let hasExited = false;
|
|
2361
2035
|
let spawned = false;
|
|
2362
|
-
const exit = new Promise((
|
|
2036
|
+
const exit = new Promise((resolve15) => {
|
|
2363
2037
|
child.once("error", (error2) => {
|
|
2364
2038
|
if (!spawned) {
|
|
2365
2039
|
hasExited = true;
|
|
2366
|
-
|
|
2040
|
+
resolve15({ code: null, signal: null, error: error2 });
|
|
2367
2041
|
}
|
|
2368
2042
|
});
|
|
2369
2043
|
child.once("exit", (code2, signal) => {
|
|
2370
2044
|
hasExited = true;
|
|
2371
|
-
|
|
2045
|
+
resolve15({ code: code2, signal });
|
|
2372
2046
|
});
|
|
2373
2047
|
});
|
|
2374
2048
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -2384,11 +2058,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
2384
2058
|
}
|
|
2385
2059
|
});
|
|
2386
2060
|
try {
|
|
2387
|
-
await new Promise((
|
|
2061
|
+
await new Promise((resolve15, reject) => {
|
|
2388
2062
|
const onSpawn = () => {
|
|
2389
2063
|
spawned = true;
|
|
2390
2064
|
child.off("error", onError);
|
|
2391
|
-
|
|
2065
|
+
resolve15();
|
|
2392
2066
|
};
|
|
2393
2067
|
const onError = (error2) => {
|
|
2394
2068
|
child.off("spawn", onSpawn);
|
|
@@ -2427,11 +2101,11 @@ var NodeAgentProcessLauncher = class {
|
|
|
2427
2101
|
};
|
|
2428
2102
|
function waitForExit(process2, timeoutMs) {
|
|
2429
2103
|
if (process2.hasExited()) return Promise.resolve(true);
|
|
2430
|
-
return new Promise((
|
|
2431
|
-
const timeout = setTimeout(() =>
|
|
2104
|
+
return new Promise((resolve15) => {
|
|
2105
|
+
const timeout = setTimeout(() => resolve15(false), timeoutMs);
|
|
2432
2106
|
process2.exit.then(() => {
|
|
2433
2107
|
clearTimeout(timeout);
|
|
2434
|
-
|
|
2108
|
+
resolve15(true);
|
|
2435
2109
|
});
|
|
2436
2110
|
});
|
|
2437
2111
|
}
|
|
@@ -2514,7 +2188,7 @@ async function waitForGroupExit(pid, isGroupAlive, timeoutMs) {
|
|
|
2514
2188
|
while (isGroupAlive(pid)) {
|
|
2515
2189
|
const remaining = deadline - Date.now();
|
|
2516
2190
|
if (remaining <= 0) return false;
|
|
2517
|
-
await new Promise((
|
|
2191
|
+
await new Promise((resolve15) => setTimeout(resolve15, Math.min(25, remaining)));
|
|
2518
2192
|
}
|
|
2519
2193
|
return true;
|
|
2520
2194
|
}
|
|
@@ -2528,10 +2202,10 @@ function processTreeControllerFor(platform) {
|
|
|
2528
2202
|
}
|
|
2529
2203
|
|
|
2530
2204
|
// packages/cli-core/src/dev/instance-lock.ts
|
|
2531
|
-
import { createHash
|
|
2532
|
-
import { mkdir as
|
|
2205
|
+
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
2206
|
+
import { mkdir as mkdir5, readFile as readFile7, readdir as readdir2, rename as rename3, rmdir as rmdir2, unlink as unlink4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2533
2207
|
import { homedir as homedir2 } from "node:os";
|
|
2534
|
-
import { join as
|
|
2208
|
+
import { join as join7 } from "node:path";
|
|
2535
2209
|
function alreadyRunningError() {
|
|
2536
2210
|
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";
|
|
2537
2211
|
return Object.assign(new BuddyDevError({ code: "DEV_ALREADY_RUNNING", phase: "launch", message }), { publicMessage: message });
|
|
@@ -2551,20 +2225,20 @@ function code(error2) {
|
|
|
2551
2225
|
return error2?.code;
|
|
2552
2226
|
}
|
|
2553
2227
|
async function acquireDevInstanceLock(options) {
|
|
2554
|
-
const root = options.directory ??
|
|
2555
|
-
const key =
|
|
2228
|
+
const root = options.directory ?? join7(homedir2(), ".buddy", "dev-locks");
|
|
2229
|
+
const key = createHash("sha256").update(`${new URL(options.gatewayUrl).origin}
|
|
2556
2230
|
${options.devAgentId}`).digest("hex");
|
|
2557
|
-
const path =
|
|
2231
|
+
const path = join7(root, key);
|
|
2558
2232
|
const ownerName = `owner-${randomUUID3()}.json`;
|
|
2559
|
-
const candidate =
|
|
2233
|
+
const candidate = join7(root, `.pending-${randomUUID3()}`);
|
|
2560
2234
|
const owner = { pid: process.pid };
|
|
2561
|
-
await
|
|
2562
|
-
await
|
|
2563
|
-
await
|
|
2235
|
+
await mkdir5(root, { recursive: true, mode: 448 });
|
|
2236
|
+
await mkdir5(candidate, { mode: 448 });
|
|
2237
|
+
await writeFile4(join7(candidate, ownerName), JSON.stringify(owner), { mode: 384, flag: "wx" });
|
|
2564
2238
|
try {
|
|
2565
2239
|
for (let attempt = 0; ; attempt++) {
|
|
2566
2240
|
try {
|
|
2567
|
-
await
|
|
2241
|
+
await rename3(candidate, path);
|
|
2568
2242
|
break;
|
|
2569
2243
|
} catch (error2) {
|
|
2570
2244
|
if (!["EEXIST", "ENOTEMPTY"].includes(code(error2) ?? "")) throw error2;
|
|
@@ -2572,17 +2246,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2572
2246
|
}
|
|
2573
2247
|
let names;
|
|
2574
2248
|
try {
|
|
2575
|
-
names = await
|
|
2249
|
+
names = await readdir2(path);
|
|
2576
2250
|
} catch (error2) {
|
|
2577
2251
|
if (code(error2) === "ENOENT") continue;
|
|
2578
2252
|
throw error2;
|
|
2579
2253
|
}
|
|
2580
2254
|
if (names.length === 0) continue;
|
|
2581
2255
|
if (names.length !== 1 || !/^owner-[a-f0-9-]+\.json$/u.test(names[0])) throw alreadyRunningError();
|
|
2582
|
-
const previousPath =
|
|
2256
|
+
const previousPath = join7(path, names[0]);
|
|
2583
2257
|
let previous;
|
|
2584
2258
|
try {
|
|
2585
|
-
previous = JSON.parse(await
|
|
2259
|
+
previous = JSON.parse(await readFile7(previousPath, "utf8"));
|
|
2586
2260
|
} catch (error2) {
|
|
2587
2261
|
if (code(error2) === "ENOENT") continue;
|
|
2588
2262
|
throw alreadyRunningError();
|
|
@@ -2599,17 +2273,17 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2599
2273
|
});
|
|
2600
2274
|
}
|
|
2601
2275
|
} finally {
|
|
2602
|
-
await unlink4(
|
|
2276
|
+
await unlink4(join7(candidate, ownerName)).catch(() => void 0);
|
|
2603
2277
|
await rmdir2(candidate).catch(() => void 0);
|
|
2604
2278
|
}
|
|
2605
2279
|
let released = false;
|
|
2606
2280
|
return {
|
|
2607
2281
|
async attachRuntime(pid) {
|
|
2608
2282
|
if (!validPid(pid)) throw new TypeError("Runtime PID is invalid");
|
|
2609
|
-
const temporary =
|
|
2283
|
+
const temporary = join7(root, `.update-${randomUUID3()}`);
|
|
2610
2284
|
try {
|
|
2611
|
-
await
|
|
2612
|
-
await
|
|
2285
|
+
await writeFile4(temporary, JSON.stringify({ ...owner, runtimePid: pid }), { mode: 384, flag: "wx" });
|
|
2286
|
+
await rename3(temporary, join7(path, ownerName));
|
|
2613
2287
|
} finally {
|
|
2614
2288
|
await unlink4(temporary).catch(() => void 0);
|
|
2615
2289
|
}
|
|
@@ -2617,7 +2291,7 @@ ${options.devAgentId}`).digest("hex");
|
|
|
2617
2291
|
async release() {
|
|
2618
2292
|
if (released) return;
|
|
2619
2293
|
released = true;
|
|
2620
|
-
await unlink4(
|
|
2294
|
+
await unlink4(join7(path, ownerName)).catch((error2) => {
|
|
2621
2295
|
if (code(error2) !== "ENOENT") throw error2;
|
|
2622
2296
|
});
|
|
2623
2297
|
await rmdir2(path).catch((error2) => {
|
|
@@ -2861,7 +2535,7 @@ async function startBuddyDev(options) {
|
|
|
2861
2535
|
const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS, "pollIntervalMs");
|
|
2862
2536
|
const stopGracePeriodMs = positiveDuration(options.stopGracePeriodMs ?? DEFAULT_STOP_GRACE_PERIOD_MS, "stopGracePeriodMs");
|
|
2863
2537
|
const devAgentId = safeDevAgentId(options.devAgentId);
|
|
2864
|
-
const projectRoot =
|
|
2538
|
+
const projectRoot = resolve8(options.projectRoot);
|
|
2865
2539
|
const lifecycle = new AbortController();
|
|
2866
2540
|
const signal = options.signal === void 0 ? lifecycle.signal : AbortSignal.any([lifecycle.signal, options.signal]);
|
|
2867
2541
|
let agentProcess;
|
|
@@ -2879,7 +2553,7 @@ async function startBuddyDev(options) {
|
|
|
2879
2553
|
let config;
|
|
2880
2554
|
let configSource;
|
|
2881
2555
|
try {
|
|
2882
|
-
configSource = await
|
|
2556
|
+
configSource = await readFile8(join8(projectRoot, BUDDY_AGENT_FILENAME), "utf8");
|
|
2883
2557
|
config = combineBuddyProject(parseBuddyAgentConfig(configSource));
|
|
2884
2558
|
} catch (cause) {
|
|
2885
2559
|
throw new BuddyDevError({
|
|
@@ -2889,14 +2563,12 @@ async function startBuddyDev(options) {
|
|
|
2889
2563
|
cause
|
|
2890
2564
|
});
|
|
2891
2565
|
}
|
|
2892
|
-
const widgetDelivery = await checkWidgetDelivery(projectRoot);
|
|
2893
|
-
if (widgetDelivery.status === "incomplete") options.onLog?.({ source: "agent.stderr", text: widgetDeliverySummary(widgetDelivery) + "\nDEV \u5141\u8BB8\u7EE7\u7EED\u8C03\u8BD5\uFF0C\u63D0\u5BA1\u524D\u987B\u8865\u9F50\u3002\n" });
|
|
2894
2566
|
const launchPlan = await launchPlanFor(config, options, projectRoot, signal);
|
|
2895
2567
|
const registration = await registerDevAgent({
|
|
2896
2568
|
client: options.registrationClient,
|
|
2897
2569
|
devAgentId,
|
|
2898
2570
|
configSource,
|
|
2899
|
-
configDigest: `sha256:${
|
|
2571
|
+
configDigest: `sha256:${createHash2("sha256").update(configSource).digest("hex")}`,
|
|
2900
2572
|
signal
|
|
2901
2573
|
});
|
|
2902
2574
|
throwIfDevAborted(signal);
|
|
@@ -2926,8 +2598,8 @@ async function startBuddyDev(options) {
|
|
|
2926
2598
|
shell: false,
|
|
2927
2599
|
detached: platform === "darwin",
|
|
2928
2600
|
...options.onLog === void 0 ? {} : {
|
|
2929
|
-
onStdout: (
|
|
2930
|
-
onStderr: (
|
|
2601
|
+
onStdout: (text) => options.onLog?.({ source: "agent.stdout", text }),
|
|
2602
|
+
onStderr: (text) => options.onLog?.({ source: "agent.stderr", text })
|
|
2931
2603
|
}
|
|
2932
2604
|
});
|
|
2933
2605
|
await instanceLock.attachRuntime(agentProcess.pid);
|
|
@@ -2983,7 +2655,7 @@ async function startBuddyDev(options) {
|
|
|
2983
2655
|
}
|
|
2984
2656
|
|
|
2985
2657
|
// packages/cli-core/src/dev/starter.ts
|
|
2986
|
-
import { resolve as
|
|
2658
|
+
import { resolve as resolve9 } from "node:path";
|
|
2987
2659
|
function createBuddyDevStarter(options = {}) {
|
|
2988
2660
|
const readManifest = options.readManifest ?? (async (projectRoot) => {
|
|
2989
2661
|
const manifest = await readBuddyProjectManifest(projectRoot);
|
|
@@ -2992,7 +2664,7 @@ function createBuddyDevStarter(options = {}) {
|
|
|
2992
2664
|
const startDev = options.startDev ?? startBuddyDev;
|
|
2993
2665
|
return Object.freeze({
|
|
2994
2666
|
async start(input) {
|
|
2995
|
-
const projectRoot =
|
|
2667
|
+
const projectRoot = resolve9(input.projectRoot);
|
|
2996
2668
|
const manifest = await readManifest(projectRoot);
|
|
2997
2669
|
return startDev({
|
|
2998
2670
|
...input,
|
|
@@ -3083,7 +2755,7 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
3083
2755
|
return { exitCode: 0 };
|
|
3084
2756
|
}
|
|
3085
2757
|
try {
|
|
3086
|
-
const requestedRoot =
|
|
2758
|
+
const requestedRoot = resolve10(options.cwd ?? process.cwd(), parsed.directory);
|
|
3087
2759
|
const root = await (options.resolveProjectRoot ?? requireBuddyAgentProjectRoot)(requestedRoot);
|
|
3088
2760
|
const environment = options.env ?? process.env;
|
|
3089
2761
|
const url = appServerUrl(parsed.appServer, environment);
|
|
@@ -3136,22 +2808,22 @@ async function runDevCommand(args, io, options = {}) {
|
|
|
3136
2808
|
}
|
|
3137
2809
|
|
|
3138
2810
|
// packages/cli-core/src/commands/cloud.ts
|
|
3139
|
-
import { resolve as
|
|
2811
|
+
import { resolve as resolve11 } from "node:path";
|
|
3140
2812
|
|
|
3141
2813
|
// packages/cli-core/src/cloud/avatar.ts
|
|
3142
2814
|
import { open as open3 } from "node:fs/promises";
|
|
3143
2815
|
import { basename } from "node:path";
|
|
3144
2816
|
var MAX_BUDDY_AVATAR_BYTES = 5 * 1024 * 1024;
|
|
3145
2817
|
async function readBuddyAvatarFile(path) {
|
|
3146
|
-
const
|
|
2818
|
+
const file = await open3(path, "r");
|
|
3147
2819
|
try {
|
|
3148
|
-
const stat2 = await
|
|
2820
|
+
const stat2 = await file.stat();
|
|
3149
2821
|
if (!stat2.isFile()) throw new Error("\u5934\u50CF\u5FC5\u987B\u662F\u666E\u901A\u56FE\u7247\u6587\u4EF6");
|
|
3150
2822
|
if (!stat2.size || stat2.size > MAX_BUDDY_AVATAR_BYTES) throw new Error("\u5934\u50CF\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u8D85\u8FC7 5 MiB");
|
|
3151
2823
|
const buffer = Buffer.alloc(MAX_BUDDY_AVATAR_BYTES + 1);
|
|
3152
2824
|
let size = 0;
|
|
3153
2825
|
while (size < buffer.length) {
|
|
3154
|
-
const { bytesRead } = await
|
|
2826
|
+
const { bytesRead } = await file.read(buffer, size, buffer.length - size, null);
|
|
3155
2827
|
if (!bytesRead) break;
|
|
3156
2828
|
size += bytesRead;
|
|
3157
2829
|
}
|
|
@@ -3161,7 +2833,7 @@ async function readBuddyAvatarFile(path) {
|
|
|
3161
2833
|
if (!mediaType) throw new Error("\u4EC5\u652F\u6301 PNG\u3001JPEG\u3001WebP \u56FE\u7247\u6587\u4EF6");
|
|
3162
2834
|
return { bytes, fileName: basename(path), mediaType };
|
|
3163
2835
|
} finally {
|
|
3164
|
-
await
|
|
2836
|
+
await file.close();
|
|
3165
2837
|
}
|
|
3166
2838
|
}
|
|
3167
2839
|
|
|
@@ -3334,7 +3006,7 @@ function positiveTimeout(value, fallback, name) {
|
|
|
3334
3006
|
if (!Number.isSafeInteger(result) || result <= 0) throw new TypeError(`${name} must be a positive safe integer`);
|
|
3335
3007
|
return result;
|
|
3336
3008
|
}
|
|
3337
|
-
function
|
|
3009
|
+
function object2(value, path) {
|
|
3338
3010
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3339
3011
|
throw invalid(`${path} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
3340
3012
|
}
|
|
@@ -3354,7 +3026,7 @@ function optionalText(value, path, allowNewlines = false) {
|
|
|
3354
3026
|
}
|
|
3355
3027
|
function meta(value, path) {
|
|
3356
3028
|
if (value === null) return null;
|
|
3357
|
-
const source =
|
|
3029
|
+
const source = object2(value, path);
|
|
3358
3030
|
const name = source.name === void 0 ? void 0 : safeText(source.name, `${path}.name`);
|
|
3359
3031
|
const avatar = optionalText(source.avatar, `${path}.avatar`);
|
|
3360
3032
|
const tagline = optionalText(source.tagline, `${path}.tagline`);
|
|
@@ -3389,7 +3061,7 @@ function auditStatus(value, path) {
|
|
|
3389
3061
|
return value;
|
|
3390
3062
|
}
|
|
3391
3063
|
function packageVersion(value, path) {
|
|
3392
|
-
const source =
|
|
3064
|
+
const source = object2(value, path);
|
|
3393
3065
|
const auditNote = source.auditNote === null ? null : safeText(source.auditNote, `${path}.auditNote`, true);
|
|
3394
3066
|
return Object.freeze({
|
|
3395
3067
|
version: safeText(source.version, `${path}.version`),
|
|
@@ -3407,7 +3079,7 @@ function positivePage(value, name) {
|
|
|
3407
3079
|
return value;
|
|
3408
3080
|
}
|
|
3409
3081
|
function buddy(value, includeToken) {
|
|
3410
|
-
const source =
|
|
3082
|
+
const source = object2(value, "response");
|
|
3411
3083
|
const token = includeToken ? safeText(source.token, "response.token") : void 0;
|
|
3412
3084
|
return Object.freeze({
|
|
3413
3085
|
id: safeText(source.id, "response.id"),
|
|
@@ -3543,7 +3215,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3543
3215
|
}), true);
|
|
3544
3216
|
}
|
|
3545
3217
|
async listBuddies() {
|
|
3546
|
-
const source =
|
|
3218
|
+
const source = object2(await this.#request("/cli/buddies", "GET"), "response");
|
|
3547
3219
|
if (!Array.isArray(source.buddies)) throw invalid("response.buddies \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
3548
3220
|
return Object.freeze({
|
|
3549
3221
|
buddies: Object.freeze(source.buddies.map((item) => buddy(item, false)))
|
|
@@ -3560,7 +3232,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3560
3232
|
...pageSize === void 0 ? {} : { pageSize: String(pageSize) }
|
|
3561
3233
|
});
|
|
3562
3234
|
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
3563
|
-
const source =
|
|
3235
|
+
const source = object2(await this.#request(
|
|
3564
3236
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages${suffix}`,
|
|
3565
3237
|
"GET"
|
|
3566
3238
|
), "response");
|
|
@@ -3582,7 +3254,7 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3582
3254
|
throw new CloudLifecycleError("INVALID_REQUEST", "\u63D0\u4EA4\u5305\u4E0D\u80FD\u4E3A\u7A7A");
|
|
3583
3255
|
}
|
|
3584
3256
|
const query = new URLSearchParams({ commit });
|
|
3585
|
-
const source =
|
|
3257
|
+
const source = object2(await this.#request(
|
|
3586
3258
|
`/cli/buddies/${encodeURIComponent(request.id)}/packages/push?${query.toString()}`,
|
|
3587
3259
|
"POST",
|
|
3588
3260
|
{
|
|
@@ -3599,13 +3271,13 @@ var HttpDeveloperPlatformClient = class {
|
|
|
3599
3271
|
});
|
|
3600
3272
|
}
|
|
3601
3273
|
async logout(accessToken) {
|
|
3602
|
-
const source =
|
|
3274
|
+
const source = object2(await this.#request("/cli/auth/logout", "POST", { accessToken }), "response");
|
|
3603
3275
|
if (source.ok !== true) throw invalid("response.ok \u5FC5\u987B\u662F true");
|
|
3604
3276
|
}
|
|
3605
3277
|
};
|
|
3606
3278
|
|
|
3607
3279
|
// packages/cli-core/src/cloud/browser-login.ts
|
|
3608
|
-
import { createHash as
|
|
3280
|
+
import { createHash as createHash3, randomBytes } from "node:crypto";
|
|
3609
3281
|
import { spawn } from "node:child_process";
|
|
3610
3282
|
import { createServer } from "node:http";
|
|
3611
3283
|
|
|
@@ -3665,10 +3337,10 @@ function browserCommand(url) {
|
|
|
3665
3337
|
}
|
|
3666
3338
|
async function openExternalUrl(url) {
|
|
3667
3339
|
const launch = browserCommand(url);
|
|
3668
|
-
await new Promise((
|
|
3340
|
+
await new Promise((resolve15, reject) => {
|
|
3669
3341
|
const child = spawn(launch.command, launch.args, { shell: false, stdio: "ignore", windowsHide: true });
|
|
3670
3342
|
child.once("error", reject);
|
|
3671
|
-
child.once("exit", (code2) => code2 === 0 ?
|
|
3343
|
+
child.once("exit", (code2) => code2 === 0 ? resolve15() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
|
|
3672
3344
|
});
|
|
3673
3345
|
}
|
|
3674
3346
|
async function write(res, status, title, message, webUrl, tone = "error") {
|
|
@@ -3680,10 +3352,10 @@ async function write(res, status, title, message, webUrl, tone = "error") {
|
|
|
3680
3352
|
"referrer-policy": "no-referrer",
|
|
3681
3353
|
"x-content-type-options": "nosniff"
|
|
3682
3354
|
});
|
|
3683
|
-
await new Promise((
|
|
3355
|
+
await new Promise((resolve15, reject) => {
|
|
3684
3356
|
res.once("error", reject);
|
|
3685
|
-
res.once("close",
|
|
3686
|
-
res.end(renderLoginResult(title, message, webUrl, tone),
|
|
3357
|
+
res.once("close", resolve15);
|
|
3358
|
+
res.end(renderLoginResult(title, message, webUrl, tone), resolve15);
|
|
3687
3359
|
});
|
|
3688
3360
|
}
|
|
3689
3361
|
async function readJson(response) {
|
|
@@ -3713,16 +3385,16 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3713
3385
|
if (request.signal?.aborted) throw request.signal.reason;
|
|
3714
3386
|
const callbackSecret = randomBytes(24).toString("hex");
|
|
3715
3387
|
const codeVerifier = randomBytes(32).toString("base64url");
|
|
3716
|
-
const codeChallenge =
|
|
3388
|
+
const codeChallenge = createHash3("sha256").update(codeVerifier).digest("base64url");
|
|
3717
3389
|
let requestId = "";
|
|
3718
3390
|
let settle;
|
|
3719
3391
|
let reject;
|
|
3720
3392
|
let settled = false;
|
|
3721
|
-
const result = new Promise((
|
|
3393
|
+
const result = new Promise((resolve15, rejectResult) => {
|
|
3722
3394
|
settle = (value) => {
|
|
3723
3395
|
if (!settled) {
|
|
3724
3396
|
settled = true;
|
|
3725
|
-
|
|
3397
|
+
resolve15(value);
|
|
3726
3398
|
}
|
|
3727
3399
|
};
|
|
3728
3400
|
reject = (reason) => {
|
|
@@ -3741,9 +3413,9 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3741
3413
|
state: request.state
|
|
3742
3414
|
}).then(settle, reject);
|
|
3743
3415
|
});
|
|
3744
|
-
await new Promise((
|
|
3416
|
+
await new Promise((resolve15, rejectListen) => {
|
|
3745
3417
|
server.once("error", rejectListen);
|
|
3746
|
-
server.listen(0, "127.0.0.1", () =>
|
|
3418
|
+
server.listen(0, "127.0.0.1", () => resolve15());
|
|
3747
3419
|
});
|
|
3748
3420
|
const address = server.address();
|
|
3749
3421
|
if (!address || typeof address === "string") {
|
|
@@ -3776,8 +3448,8 @@ var LoopbackBrowserLoginAdapter = class {
|
|
|
3776
3448
|
} finally {
|
|
3777
3449
|
clearTimeout(timer);
|
|
3778
3450
|
request.signal?.removeEventListener("abort", abort);
|
|
3779
|
-
await new Promise((
|
|
3780
|
-
server.close(() =>
|
|
3451
|
+
await new Promise((resolve15) => {
|
|
3452
|
+
server.close(() => resolve15());
|
|
3781
3453
|
server.closeIdleConnections();
|
|
3782
3454
|
server.closeAllConnections();
|
|
3783
3455
|
});
|
|
@@ -3892,7 +3564,7 @@ function parse(source) {
|
|
|
3892
3564
|
});
|
|
3893
3565
|
}
|
|
3894
3566
|
async function runCredentialCommand(input) {
|
|
3895
|
-
return await new Promise((
|
|
3567
|
+
return await new Promise((resolve15, reject) => {
|
|
3896
3568
|
const child = spawn2(input.command, [...input.args], {
|
|
3897
3569
|
shell: false,
|
|
3898
3570
|
windowsHide: true,
|
|
@@ -3908,7 +3580,7 @@ async function runCredentialCommand(input) {
|
|
|
3908
3580
|
child.stdout.on("data", (chunk) => collect(stdout, chunk));
|
|
3909
3581
|
child.stderr.on("data", (chunk) => collect(stderr, chunk));
|
|
3910
3582
|
child.once("error", reject);
|
|
3911
|
-
child.once("close", (code2) =>
|
|
3583
|
+
child.once("close", (code2) => resolve15({
|
|
3912
3584
|
code: code2 ?? 1,
|
|
3913
3585
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
3914
3586
|
stderr: Buffer.concat(stderr).toString("utf8")
|
|
@@ -3968,7 +3640,7 @@ var SystemCloudTokenStore = class {
|
|
|
3968
3640
|
};
|
|
3969
3641
|
|
|
3970
3642
|
// packages/cli-core/src/cloud/mock-client.ts
|
|
3971
|
-
import { createHash as
|
|
3643
|
+
import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
|
|
3972
3644
|
var MockDeveloperPlatformClient = class {
|
|
3973
3645
|
#now;
|
|
3974
3646
|
#buddies = /* @__PURE__ */ new Map();
|
|
@@ -3996,7 +3668,7 @@ var MockDeveloperPlatformClient = class {
|
|
|
3996
3668
|
const current = this.#require(request.id);
|
|
3997
3669
|
validateBuddyMetaUpdate(request.meta);
|
|
3998
3670
|
const onlyAvatar = Object.keys(request.meta).every((key) => key === "avatar");
|
|
3999
|
-
const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${
|
|
3671
|
+
const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${createHash4("sha256").update(request.avatar.bytes).digest("hex").slice(0, 16)}` : request.meta.avatar === null ? null : current.meta?.avatar ?? null;
|
|
4000
3672
|
const next = this.#value({ ...current, meta: { ...onlyAvatar ? current.meta : request.meta, avatar }, updatedAt: this.#now() });
|
|
4001
3673
|
this.#buddies.set(request.id, next);
|
|
4002
3674
|
return structuredClone(next);
|
|
@@ -4140,7 +3812,7 @@ function dependencies(options) {
|
|
|
4140
3812
|
return { session: options.session, client: options.client };
|
|
4141
3813
|
}
|
|
4142
3814
|
function requestedDirectory(options, parsed) {
|
|
4143
|
-
return
|
|
3815
|
+
return resolve11(options.cwd ?? process.cwd(), parsed.values.get("--directory") ?? ".");
|
|
4144
3816
|
}
|
|
4145
3817
|
function auditLabel(status) {
|
|
4146
3818
|
if (status === "auditing") return "\u5BA1\u6838\u4E2D";
|
|
@@ -4221,10 +3893,10 @@ async function runAvatar(parsed, io, options) {
|
|
|
4221
3893
|
if (parsed.values.has("--buddy-id") && parsed.values.has("--directory")) {
|
|
4222
3894
|
throw new CloudCommandUsageError("--buddy-id \u548C --directory \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
|
|
4223
3895
|
}
|
|
4224
|
-
const
|
|
3896
|
+
const file = parsed.values.get("--file");
|
|
4225
3897
|
let upload;
|
|
4226
3898
|
try {
|
|
4227
|
-
upload =
|
|
3899
|
+
upload = file ? await readBuddyAvatarFile(resolve11(options.cwd ?? process.cwd(), file)) : void 0;
|
|
4228
3900
|
} catch (error2) {
|
|
4229
3901
|
throw new CloudCommandUsageError(error2 instanceof Error ? error2.message : "\u65E0\u6CD5\u8BFB\u53D6\u5934\u50CF\u56FE\u7247");
|
|
4230
3902
|
}
|
|
@@ -4293,8 +3965,8 @@ async function runCloudCommand(command, args, io, options = {}) {
|
|
|
4293
3965
|
var CLOUD_COMMAND_HELP = HELP;
|
|
4294
3966
|
|
|
4295
3967
|
// packages/cli-core/src/migration/index.ts
|
|
4296
|
-
import { lstat as
|
|
4297
|
-
import { extname, isAbsolute as isAbsolute2, join as
|
|
3968
|
+
import { lstat as lstat7, open as open4, readdir as readdir3, realpath as realpath3, stat } from "node:fs/promises";
|
|
3969
|
+
import { extname, isAbsolute as isAbsolute2, join as join9, relative, resolve as resolve12, sep, win32 as win322 } from "node:path";
|
|
4298
3970
|
var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
|
|
4299
3971
|
maxFiles: 128,
|
|
4300
3972
|
maxFileBytes: 256 * 1024,
|
|
@@ -4447,7 +4119,7 @@ async function existingProjectRoot(value) {
|
|
|
4447
4119
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
4448
4120
|
}
|
|
4449
4121
|
try {
|
|
4450
|
-
const root = await
|
|
4122
|
+
const root = await realpath3(resolve12(value));
|
|
4451
4123
|
if (!(await stat(root)).isDirectory()) {
|
|
4452
4124
|
return rejectScan("SKILL_PROJECT_ROOT_INVALID", ".", "projectRoot \u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u5DF2\u6709\u9879\u76EE\u76EE\u5F55\u3002");
|
|
4453
4125
|
}
|
|
@@ -4463,9 +4135,9 @@ async function existingProjectRoot(value) {
|
|
|
4463
4135
|
}
|
|
4464
4136
|
}
|
|
4465
4137
|
async function existingSkillRoot(projectRoot, skillPath) {
|
|
4466
|
-
const candidate = skillPath === "." ? projectRoot :
|
|
4138
|
+
const candidate = skillPath === "." ? projectRoot : join9(projectRoot, ...skillPath.split("/"));
|
|
4467
4139
|
try {
|
|
4468
|
-
await
|
|
4140
|
+
await lstat7(candidate);
|
|
4469
4141
|
} catch (cause) {
|
|
4470
4142
|
if (filesystemCode(cause) === "ENOENT") {
|
|
4471
4143
|
return rejectScan("SKILL_ROOT_NOT_FOUND", skillPath, "\u627E\u4E0D\u5230\u6307\u5B9A\u7684 Skill \u76EE\u5F55\u3002");
|
|
@@ -4474,7 +4146,7 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
4474
4146
|
}
|
|
4475
4147
|
let root;
|
|
4476
4148
|
try {
|
|
4477
|
-
root = await
|
|
4149
|
+
root = await realpath3(candidate);
|
|
4478
4150
|
} catch (cause) {
|
|
4479
4151
|
if (filesystemCode(cause) === "ELOOP") {
|
|
4480
4152
|
return rejectScan("SKILL_SYMLINK_CYCLE", skillPath, "Skill \u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -4500,10 +4172,10 @@ async function existingSkillRoot(projectRoot, skillPath) {
|
|
|
4500
4172
|
}
|
|
4501
4173
|
async function assertEntrypoint(context) {
|
|
4502
4174
|
const entryPath = reportChild(context.skillPath, "SKILL.md");
|
|
4503
|
-
const candidate =
|
|
4175
|
+
const candidate = join9(context.skillRoot, "SKILL.md");
|
|
4504
4176
|
try {
|
|
4505
|
-
await
|
|
4506
|
-
const target = await
|
|
4177
|
+
await lstat7(candidate);
|
|
4178
|
+
const target = await realpath3(candidate);
|
|
4507
4179
|
if (!isWithin(context.projectRoot, target) || !isWithin(context.skillRoot, target)) {
|
|
4508
4180
|
rejectScan(
|
|
4509
4181
|
"SKILL_SYMLINK_ESCAPE",
|
|
@@ -4547,7 +4219,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
4547
4219
|
}
|
|
4548
4220
|
let directory;
|
|
4549
4221
|
try {
|
|
4550
|
-
directory = await
|
|
4222
|
+
directory = await realpath3(actualDirectory);
|
|
4551
4223
|
} catch (cause) {
|
|
4552
4224
|
if (filesystemCode(cause) === "ELOOP") {
|
|
4553
4225
|
return rejectScan("SKILL_SYMLINK_CYCLE", reportDirectory, "Skill \u76EE\u5F55\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -4567,7 +4239,7 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
4567
4239
|
nextAncestors.add(directoryKey);
|
|
4568
4240
|
let entries;
|
|
4569
4241
|
try {
|
|
4570
|
-
entries = await
|
|
4242
|
+
entries = await readdir3(directory, { withFileTypes: true, encoding: "utf8" });
|
|
4571
4243
|
} catch (cause) {
|
|
4572
4244
|
return rejectScan("SKILL_SCAN_IO_ERROR", reportDirectory, "\u65E0\u6CD5\u5217\u51FA Skill \u5B50\u76EE\u5F55\u3002", cause);
|
|
4573
4245
|
}
|
|
@@ -4604,10 +4276,10 @@ async function walkDirectory(actualDirectory, reportDirectory, ancestors, contex
|
|
|
4604
4276
|
}
|
|
4605
4277
|
for (const entry of entries) {
|
|
4606
4278
|
const childReportPath = reportChild(reportDirectory, entry.name);
|
|
4607
|
-
const childPath =
|
|
4279
|
+
const childPath = join9(directory, entry.name);
|
|
4608
4280
|
let target;
|
|
4609
4281
|
try {
|
|
4610
|
-
target = await
|
|
4282
|
+
target = await realpath3(childPath);
|
|
4611
4283
|
} catch (cause) {
|
|
4612
4284
|
if (filesystemCode(cause) === "ELOOP") {
|
|
4613
4285
|
return rejectScan("SKILL_SYMLINK_CYCLE", childReportPath, "Skill \u5185\u5BB9\u5305\u542B\u7B26\u53F7\u94FE\u63A5\u5FAA\u73AF\u3002", cause);
|
|
@@ -5058,9 +4730,9 @@ async function scanExternalSkillPortability(options) {
|
|
|
5058
4730
|
}
|
|
5059
4731
|
|
|
5060
4732
|
// packages/cli-core/src/push/preflight.ts
|
|
5061
|
-
import { createHash as
|
|
5062
|
-
import { lstat as
|
|
5063
|
-
import { isAbsolute as isAbsolute3, join as
|
|
4733
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
4734
|
+
import { lstat as lstat8, open as open5, readdir as readdir4, realpath as realpath4 } from "node:fs/promises";
|
|
4735
|
+
import { isAbsolute as isAbsolute3, join as join10, relative as relative2, resolve as resolve13, sep as sep2, win32 as win323 } from "node:path";
|
|
5064
4736
|
|
|
5065
4737
|
// packages/cli-core/src/push/errors.ts
|
|
5066
4738
|
var PushPreflightError = class extends Error {
|
|
@@ -5158,7 +4830,7 @@ function error(options) {
|
|
|
5158
4830
|
throw new PushPreflightError(options);
|
|
5159
4831
|
}
|
|
5160
4832
|
function sha256(value) {
|
|
5161
|
-
return `sha256:${
|
|
4833
|
+
return `sha256:${createHash5("sha256").update(value).digest("hex")}`;
|
|
5162
4834
|
}
|
|
5163
4835
|
function positiveLimit(value, name, hardMaximum) {
|
|
5164
4836
|
if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
|
|
@@ -5248,10 +4920,10 @@ function normalizeBookletPath(value) {
|
|
|
5248
4920
|
return normalized;
|
|
5249
4921
|
}
|
|
5250
4922
|
async function canonicalProjectRoot(projectRoot) {
|
|
5251
|
-
const absolute =
|
|
4923
|
+
const absolute = resolve13(projectRoot);
|
|
5252
4924
|
let entry;
|
|
5253
4925
|
try {
|
|
5254
|
-
entry = await
|
|
4926
|
+
entry = await lstat8(absolute);
|
|
5255
4927
|
} catch (cause) {
|
|
5256
4928
|
error({
|
|
5257
4929
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -5268,7 +4940,7 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
5268
4940
|
});
|
|
5269
4941
|
}
|
|
5270
4942
|
try {
|
|
5271
|
-
return await
|
|
4943
|
+
return await realpath4(absolute);
|
|
5272
4944
|
} catch (cause) {
|
|
5273
4945
|
error({
|
|
5274
4946
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -5279,10 +4951,10 @@ async function canonicalProjectRoot(projectRoot) {
|
|
|
5279
4951
|
}
|
|
5280
4952
|
}
|
|
5281
4953
|
async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
5282
|
-
const path =
|
|
4954
|
+
const path = join10(projectRoot, filename);
|
|
5283
4955
|
let entry;
|
|
5284
4956
|
try {
|
|
5285
|
-
entry = await
|
|
4957
|
+
entry = await lstat8(path);
|
|
5286
4958
|
} catch (cause) {
|
|
5287
4959
|
error({
|
|
5288
4960
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -5313,22 +4985,22 @@ async function captureConfigWithinLimit(projectRoot, maxFileBytes, filename) {
|
|
|
5313
4985
|
maxFileBytes
|
|
5314
4986
|
});
|
|
5315
4987
|
}
|
|
5316
|
-
function decodeCapturedConfig(
|
|
5317
|
-
if (
|
|
4988
|
+
function decodeCapturedConfig(file) {
|
|
4989
|
+
if (file.contents === void 0) {
|
|
5318
4990
|
error({
|
|
5319
4991
|
code: "PUSH_CONFIG_INVALID",
|
|
5320
|
-
path:
|
|
5321
|
-
message: `\u65E0\u6CD5\u8BFB\u53D6 ${
|
|
4992
|
+
path: file.path,
|
|
4993
|
+
message: `\u65E0\u6CD5\u8BFB\u53D6 ${file.path} \u5185\u5BB9\u3002`
|
|
5322
4994
|
});
|
|
5323
4995
|
}
|
|
5324
4996
|
let source;
|
|
5325
4997
|
try {
|
|
5326
|
-
source = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
4998
|
+
source = new TextDecoder("utf-8", { fatal: true }).decode(file.contents);
|
|
5327
4999
|
} catch (cause) {
|
|
5328
5000
|
error({
|
|
5329
5001
|
code: "PUSH_CONFIG_INVALID",
|
|
5330
|
-
path:
|
|
5331
|
-
message: `${
|
|
5002
|
+
path: file.path,
|
|
5003
|
+
message: `${file.path} \u4E0D\u662F\u6709\u6548 UTF-8\u3002`,
|
|
5332
5004
|
cause
|
|
5333
5005
|
});
|
|
5334
5006
|
}
|
|
@@ -5349,7 +5021,7 @@ function parseCapturedProject(agentFile) {
|
|
|
5349
5021
|
async function selectLockfile(projectRoot) {
|
|
5350
5022
|
let names;
|
|
5351
5023
|
try {
|
|
5352
|
-
names = await
|
|
5024
|
+
names = await readdir4(projectRoot);
|
|
5353
5025
|
} catch (cause) {
|
|
5354
5026
|
error({
|
|
5355
5027
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -5404,7 +5076,7 @@ async function hashRegularFile(options) {
|
|
|
5404
5076
|
message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
|
|
5405
5077
|
});
|
|
5406
5078
|
}
|
|
5407
|
-
const hash =
|
|
5079
|
+
const hash = createHash5("sha256");
|
|
5408
5080
|
const captured = [];
|
|
5409
5081
|
const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
|
|
5410
5082
|
let bytes = 0;
|
|
@@ -5458,7 +5130,7 @@ async function createManifest(options) {
|
|
|
5458
5130
|
}
|
|
5459
5131
|
let names;
|
|
5460
5132
|
try {
|
|
5461
|
-
names = await
|
|
5133
|
+
names = await readdir4(directory);
|
|
5462
5134
|
} catch (cause) {
|
|
5463
5135
|
error({
|
|
5464
5136
|
code: "PUSH_PROJECT_INVALID",
|
|
@@ -5482,10 +5154,10 @@ async function createManifest(options) {
|
|
|
5482
5154
|
sensitiveEntriesExcluded += 1;
|
|
5483
5155
|
continue;
|
|
5484
5156
|
}
|
|
5485
|
-
const absolutePath =
|
|
5157
|
+
const absolutePath = join10(directory, name);
|
|
5486
5158
|
let entry;
|
|
5487
5159
|
try {
|
|
5488
|
-
entry = await
|
|
5160
|
+
entry = await lstat8(absolutePath);
|
|
5489
5161
|
} catch (cause) {
|
|
5490
5162
|
error({
|
|
5491
5163
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5516,7 +5188,7 @@ async function createManifest(options) {
|
|
|
5516
5188
|
}
|
|
5517
5189
|
let canonicalPath;
|
|
5518
5190
|
try {
|
|
5519
|
-
canonicalPath = await
|
|
5191
|
+
canonicalPath = await realpath4(absolutePath);
|
|
5520
5192
|
} catch (cause) {
|
|
5521
5193
|
error({
|
|
5522
5194
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5575,15 +5247,15 @@ async function createManifest(options) {
|
|
|
5575
5247
|
message: `\u63D0\u4EA4\u6587\u4EF6\u603B\u91CF\u8D85\u8FC7\u4E0A\u9650 ${options.limits.maxTotalBytes} bytes\u3002`
|
|
5576
5248
|
});
|
|
5577
5249
|
}
|
|
5578
|
-
const
|
|
5250
|
+
const file = await hashRegularFile({
|
|
5579
5251
|
absolutePath,
|
|
5580
5252
|
portablePath: path,
|
|
5581
5253
|
expected: entry,
|
|
5582
5254
|
captureContents: options.capturePaths.has(path),
|
|
5583
5255
|
maxFileBytes: options.limits.maxFileBytes
|
|
5584
5256
|
});
|
|
5585
|
-
files.push(
|
|
5586
|
-
totalBytes +=
|
|
5257
|
+
files.push(file);
|
|
5258
|
+
totalBytes += file.bytes;
|
|
5587
5259
|
}
|
|
5588
5260
|
};
|
|
5589
5261
|
await walk(options.projectRoot, []);
|
|
@@ -5664,10 +5336,10 @@ function validateLockfile(lockfile, contents) {
|
|
|
5664
5336
|
function bundleDigest(files) {
|
|
5665
5337
|
const descriptor = JSON.stringify({
|
|
5666
5338
|
schemaVersion: "1",
|
|
5667
|
-
files: files.map((
|
|
5668
|
-
path:
|
|
5669
|
-
bytes:
|
|
5670
|
-
digest:
|
|
5339
|
+
files: files.map((file) => ({
|
|
5340
|
+
path: file.path,
|
|
5341
|
+
bytes: file.bytes,
|
|
5342
|
+
digest: file.digest
|
|
5671
5343
|
}))
|
|
5672
5344
|
});
|
|
5673
5345
|
return sha256(`buddy-push-bundle-v1\0${descriptor}`);
|
|
@@ -5677,8 +5349,8 @@ function warning(code2, message) {
|
|
|
5677
5349
|
}
|
|
5678
5350
|
function assertOfficialProjectIncluded(files) {
|
|
5679
5351
|
for (const path of OFFICIAL_RUNTIME_INPUTS) {
|
|
5680
|
-
const
|
|
5681
|
-
if (
|
|
5352
|
+
const file = files.find((entry) => entry.path === path);
|
|
5353
|
+
if (file === void 0 || file.bytes === 0) {
|
|
5682
5354
|
error({
|
|
5683
5355
|
code: "PUSH_PROJECT_INVALID",
|
|
5684
5356
|
path,
|
|
@@ -5717,7 +5389,7 @@ function assertOfficialProjectIncluded(files) {
|
|
|
5717
5389
|
}
|
|
5718
5390
|
}
|
|
5719
5391
|
async function assertCapturedConfigStable(options) {
|
|
5720
|
-
const manifestEntry = options.manifestFiles.find((
|
|
5392
|
+
const manifestEntry = options.manifestFiles.find((file) => file.path === options.filename);
|
|
5721
5393
|
if (manifestEntry === void 0) {
|
|
5722
5394
|
error({
|
|
5723
5395
|
code: "PUSH_CONFIG_INVALID",
|
|
@@ -5734,8 +5406,8 @@ async function assertCapturedConfigStable(options) {
|
|
|
5734
5406
|
}
|
|
5735
5407
|
let current;
|
|
5736
5408
|
try {
|
|
5737
|
-
const path =
|
|
5738
|
-
const entry = await
|
|
5409
|
+
const path = join10(options.projectRoot, options.filename);
|
|
5410
|
+
const entry = await lstat8(path);
|
|
5739
5411
|
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
5740
5412
|
error({
|
|
5741
5413
|
code: "PUSH_FILE_CHANGED",
|
|
@@ -5778,8 +5450,6 @@ async function createPushPreflight(options) {
|
|
|
5778
5450
|
BUDDY_AGENT_FILENAME
|
|
5779
5451
|
);
|
|
5780
5452
|
parseCapturedProject(initialAgentEntry);
|
|
5781
|
-
const widgetDelivery = await checkWidgetDelivery(projectRoot);
|
|
5782
|
-
if (widgetDelivery.status === "incomplete") error({ code: "PUSH_WIDGET_DELIVERY_INCOMPLETE", message: widgetDeliverySummary(widgetDelivery) });
|
|
5783
5453
|
const lockfile = await selectLockfile(projectRoot);
|
|
5784
5454
|
assertBookletIsNotRequiredInput(bookletPath, lockfile);
|
|
5785
5455
|
const manifest = await createManifest({
|
|
@@ -5788,12 +5458,7 @@ async function createPushPreflight(options) {
|
|
|
5788
5458
|
capturePaths: /* @__PURE__ */ new Set([BUDDY_AGENT_FILENAME, lockfile.path, BUDDY_PLATFORM_PACKAGE_JSON, BUDDY_PLATFORM_AGENT_ENTRY, BUDDY_PLATFORM_FACTORY_ENTRY]),
|
|
5789
5459
|
...bookletPath === void 0 ? {} : { bookletPath }
|
|
5790
5460
|
});
|
|
5791
|
-
|
|
5792
|
-
if (manifest.files.find((file2) => file2.path === checked.path)?.digest !== checked.digest) {
|
|
5793
|
-
error({ code: "PUSH_WIDGET_DELIVERY_INCOMPLETE", message: `\u5C0F\u6302\u4EF6\u6838\u5BF9\u540E\u6587\u4EF6\u53D8\u5316\u6216\u672A\u8FDB\u5165\u4E0A\u4F20\u5305\uFF1A${checked.path}\uFF1B\u8BF7\u91CD\u65B0\u68C0\u67E5\u3002` });
|
|
5794
|
-
}
|
|
5795
|
-
}
|
|
5796
|
-
const lockEntry = manifest.files.find((file2) => file2.path === lockfile.path);
|
|
5461
|
+
const lockEntry = manifest.files.find((file) => file.path === lockfile.path);
|
|
5797
5462
|
if (lockEntry?.contents === void 0) {
|
|
5798
5463
|
error({
|
|
5799
5464
|
code: "PUSH_LOCKFILE_INVALID",
|
|
@@ -5831,10 +5496,10 @@ async function createPushPreflight(options) {
|
|
|
5831
5496
|
});
|
|
5832
5497
|
}
|
|
5833
5498
|
}
|
|
5834
|
-
const files = Object.freeze(manifest.files.map((
|
|
5835
|
-
path:
|
|
5836
|
-
bytes:
|
|
5837
|
-
digest:
|
|
5499
|
+
const files = Object.freeze(manifest.files.map((file) => Object.freeze({
|
|
5500
|
+
path: file.path,
|
|
5501
|
+
bytes: file.bytes,
|
|
5502
|
+
digest: file.digest
|
|
5838
5503
|
})));
|
|
5839
5504
|
const warnings = [warning(
|
|
5840
5505
|
"PREFLIGHT_ONLY",
|
|
@@ -5870,16 +5535,16 @@ async function createPushPreflight(options) {
|
|
|
5870
5535
|
}
|
|
5871
5536
|
|
|
5872
5537
|
// packages/cli-core/src/push/bundle.ts
|
|
5873
|
-
import { createHash as
|
|
5874
|
-
import { lstat as
|
|
5875
|
-
import { join as
|
|
5538
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5539
|
+
import { lstat as lstat9, readFile as readFile9, realpath as realpath5 } from "node:fs/promises";
|
|
5540
|
+
import { join as join11, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
|
|
5876
5541
|
import { Writable } from "node:stream";
|
|
5877
5542
|
import { pipeline } from "node:stream/promises";
|
|
5878
5543
|
import { createGzip } from "node:zlib";
|
|
5879
5544
|
import { pack } from "tar-stream";
|
|
5880
5545
|
var MEDIA_TYPE = "application/gzip";
|
|
5881
5546
|
function digest(bytes) {
|
|
5882
|
-
return `sha256:${
|
|
5547
|
+
return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
|
|
5883
5548
|
}
|
|
5884
5549
|
function changed(path, message, cause) {
|
|
5885
5550
|
throw new PushPreflightError({
|
|
@@ -5893,42 +5558,42 @@ function inside(root, target) {
|
|
|
5893
5558
|
const path = relative3(root, target);
|
|
5894
5559
|
return path === "" || path !== ".." && !path.startsWith(`..${sep3}`) && !path.startsWith("/") && !win324.isAbsolute(path);
|
|
5895
5560
|
}
|
|
5896
|
-
async function capture(root,
|
|
5897
|
-
const absolute =
|
|
5561
|
+
async function capture(root, file) {
|
|
5562
|
+
const absolute = join11(root, ...file.path.split("/"));
|
|
5898
5563
|
try {
|
|
5899
|
-
const entry = await
|
|
5900
|
-
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !==
|
|
5901
|
-
changed(
|
|
5564
|
+
const entry = await lstat9(absolute);
|
|
5565
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !== file.bytes) {
|
|
5566
|
+
changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
|
|
5902
5567
|
}
|
|
5903
|
-
const canonical = await
|
|
5904
|
-
if (!inside(root, canonical)) changed(
|
|
5905
|
-
const contents = await
|
|
5906
|
-
if (contents.byteLength !==
|
|
5907
|
-
changed(
|
|
5568
|
+
const canonical = await realpath5(absolute);
|
|
5569
|
+
if (!inside(root, canonical)) changed(file.path, `\u63D0\u4EA4\u6587\u4EF6\u8D8A\u8FC7\u9879\u76EE\u6839\u76EE\u5F55\uFF1A${file.path}`);
|
|
5570
|
+
const contents = await readFile9(absolute);
|
|
5571
|
+
if (contents.byteLength !== file.bytes || digest(contents) !== file.digest) {
|
|
5572
|
+
changed(file.path, `\u63D0\u4EA4\u524D\u6587\u4EF6\u5185\u5BB9\u53D1\u751F\u53D8\u5316\uFF1A${file.path}`);
|
|
5908
5573
|
}
|
|
5909
5574
|
return Object.freeze({
|
|
5910
|
-
path:
|
|
5911
|
-
bytes:
|
|
5912
|
-
digest:
|
|
5575
|
+
path: file.path,
|
|
5576
|
+
bytes: file.bytes,
|
|
5577
|
+
digest: file.digest,
|
|
5913
5578
|
content: contents
|
|
5914
5579
|
});
|
|
5915
5580
|
} catch (cause) {
|
|
5916
5581
|
if (cause instanceof PushPreflightError) throw cause;
|
|
5917
|
-
changed(
|
|
5582
|
+
changed(file.path, `\u63D0\u4EA4\u524D\u65E0\u6CD5\u518D\u6B21\u8BFB\u53D6\u6587\u4EF6\uFF1A${file.path}`, cause);
|
|
5918
5583
|
}
|
|
5919
5584
|
}
|
|
5920
5585
|
async function createPushSourceBundle(plan) {
|
|
5921
|
-
const canonicalRoot = await
|
|
5586
|
+
const canonicalRoot = await realpath5(plan.projectRoot).catch((cause) => changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u76EE\u5F55\u5728\u6253\u5305\u524D\u5DF2\u4E0D\u53EF\u7528", cause));
|
|
5922
5587
|
if (canonicalRoot !== plan.projectRoot) {
|
|
5923
5588
|
changed(plan.projectRoot, "\u63D0\u4EA4\u9879\u76EE\u6839\u76EE\u5F55\u5728\u9884\u68C0\u540E\u53D1\u751F\u53D8\u5316");
|
|
5924
5589
|
}
|
|
5925
5590
|
const files = [];
|
|
5926
|
-
for (const
|
|
5591
|
+
for (const file of plan.files) files.push(await capture(canonicalRoot, file));
|
|
5927
5592
|
const descriptor = JSON.stringify({
|
|
5928
5593
|
schemaVersion: "1",
|
|
5929
|
-
files: files.map((
|
|
5594
|
+
files: files.map((file) => ({ path: file.path, bytes: file.bytes, digest: file.digest }))
|
|
5930
5595
|
});
|
|
5931
|
-
const calculatedBundleDigest = `sha256:${
|
|
5596
|
+
const calculatedBundleDigest = `sha256:${createHash6("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
|
|
5932
5597
|
if (calculatedBundleDigest !== plan.bundleDigest) {
|
|
5933
5598
|
changed(plan.projectRoot, "\u63D0\u4EA4\u6E05\u5355\u8EAB\u4EFD\u5728\u6253\u5305\u524D\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C push");
|
|
5934
5599
|
}
|
|
@@ -5941,16 +5606,16 @@ async function createPushSourceBundle(plan) {
|
|
|
5941
5606
|
}
|
|
5942
5607
|
});
|
|
5943
5608
|
const completed = pipeline(archive, createGzip({ level: 9 }), output);
|
|
5944
|
-
for (const
|
|
5609
|
+
for (const file of files) {
|
|
5945
5610
|
await new Promise((resolveEntry, rejectEntry) => {
|
|
5946
5611
|
archive.entry({
|
|
5947
|
-
name:
|
|
5948
|
-
size:
|
|
5612
|
+
name: file.path,
|
|
5613
|
+
size: file.content.byteLength,
|
|
5949
5614
|
mode: 420,
|
|
5950
5615
|
uid: 0,
|
|
5951
5616
|
gid: 0,
|
|
5952
5617
|
mtime: /* @__PURE__ */ new Date(0)
|
|
5953
|
-
},
|
|
5618
|
+
}, file.content, (cause) => cause ? rejectEntry(cause) : resolveEntry());
|
|
5954
5619
|
});
|
|
5955
5620
|
}
|
|
5956
5621
|
archive.finalize();
|
|
@@ -5992,7 +5657,6 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
|
|
|
5992
5657
|
buddy-cli login | logout | push | status
|
|
5993
5658
|
buddy-cli avatar (--file <\u56FE\u7247\u6587\u4EF6> | --clear) [--directory <\u9879\u76EE\u76EE\u5F55> | --buddy-id <\u642D\u5B50 ID>]
|
|
5994
5659
|
buddy-cli models [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5995
|
-
buddy-cli widgets sync | check | verify
|
|
5996
5660
|
buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
|
|
5997
5661
|
buddy-cli preview [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
|
|
5998
5662
|
buddy-cli help
|
|
@@ -6214,7 +5878,7 @@ function defaultProjectDirectory(name) {
|
|
|
6214
5878
|
return `./${folder}`;
|
|
6215
5879
|
}
|
|
6216
5880
|
async function initializeProjectRoute(route, args, io, options, application) {
|
|
6217
|
-
const cwd =
|
|
5881
|
+
const cwd = resolve14(options.cwd ?? process.cwd());
|
|
6218
5882
|
const { name, tagline, intro } = application;
|
|
6219
5883
|
const directoryInput = await textAnswer(
|
|
6220
5884
|
io,
|
|
@@ -6223,7 +5887,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
6223
5887
|
"\u9879\u76EE\u76EE\u5F55\uFF08\u7A7A\u76EE\u5F55\u6216\u5DF2\u6709\u642D\u642D\u5DE5\u7A0B\uFF09",
|
|
6224
5888
|
defaultProjectDirectory(name)
|
|
6225
5889
|
);
|
|
6226
|
-
const rootDirectory =
|
|
5890
|
+
const rootDirectory = resolve14(cwd, directoryInput);
|
|
6227
5891
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
6228
5892
|
const initializationTarget = { directory: rootDirectory, kind: "new" };
|
|
6229
5893
|
const initializationPlan = options.initializeProject ? void 0 : await planBuddyProjectInitialization(initializationTarget);
|
|
@@ -6267,7 +5931,7 @@ async function initializeProjectRoute(route, args, io, options, application) {
|
|
|
6267
5931
|
}
|
|
6268
5932
|
async function runRoute(route, args, io, options) {
|
|
6269
5933
|
if (!io.canPrompt) assertNonInteractiveProjectArguments(route, args);
|
|
6270
|
-
const cwd =
|
|
5934
|
+
const cwd = resolve14(options.cwd ?? process.cwd());
|
|
6271
5935
|
const name = await textAnswer(io, args.name, "--name", "\u642D\u5B50\u540D\u79F0", void 0, BUDDY_PROFILE_TEXT_LIMITS.name);
|
|
6272
5936
|
const tagline = await textAnswer(
|
|
6273
5937
|
io,
|
|
@@ -6295,7 +5959,7 @@ async function runRoute(route, args, io, options) {
|
|
|
6295
5959
|
"\u9879\u76EE\u76EE\u5F55",
|
|
6296
5960
|
defaultProjectDirectory(name)
|
|
6297
5961
|
);
|
|
6298
|
-
const rootDirectory =
|
|
5962
|
+
const rootDirectory = resolve14(cwd, directoryInput);
|
|
6299
5963
|
await assertInitializableBuddyTarget(rootDirectory);
|
|
6300
5964
|
const plan = await planBuddyDraftInitialization(rootDirectory);
|
|
6301
5965
|
const existingBuddyId = plan.existingManifest?.buddyId;
|
|
@@ -6329,11 +5993,11 @@ async function runRoute(route, args, io, options) {
|
|
|
6329
5993
|
return { ...result, route: "create", draft, buddyId };
|
|
6330
5994
|
}
|
|
6331
5995
|
async function resumeProjectArguments(args, cwd, options) {
|
|
6332
|
-
const root =
|
|
5996
|
+
const root = resolve14(cwd, args.directory?.trim() || ".");
|
|
6333
5997
|
await assertInitializableBuddyTarget(root);
|
|
6334
5998
|
let entry;
|
|
6335
5999
|
try {
|
|
6336
|
-
entry = await
|
|
6000
|
+
entry = await lstat10(join12(root, "buddy.agent.json"));
|
|
6337
6001
|
} catch (cause) {
|
|
6338
6002
|
if (cause.code === "ENOENT") return;
|
|
6339
6003
|
throw cause;
|
|
@@ -6362,7 +6026,7 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6362
6026
|
try {
|
|
6363
6027
|
const [command, ...rest] = args;
|
|
6364
6028
|
if (!command) {
|
|
6365
|
-
const cwd =
|
|
6029
|
+
const cwd = resolve14(options.cwd ?? process.cwd());
|
|
6366
6030
|
let projectRoot;
|
|
6367
6031
|
try {
|
|
6368
6032
|
projectRoot = await findBuddyProjectRoot(cwd);
|
|
@@ -6391,7 +6055,6 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6391
6055
|
return { exitCode: 0 };
|
|
6392
6056
|
}
|
|
6393
6057
|
if (command === "models") return await runModelsCommand(rest, io, options.cwd ?? process.cwd(), options.runtimeCatalog);
|
|
6394
|
-
if (command === "widgets") return await runWidgetsCommand(rest, io, options.cwd ?? process.cwd());
|
|
6395
6058
|
if (command === "preview") {
|
|
6396
6059
|
return options.preview?.(rest, io) ?? failure("preview \u5BBF\u4E3B\u672A\u914D\u7F6E", io, true);
|
|
6397
6060
|
}
|
|
@@ -6418,11 +6081,11 @@ async function runBuddyCli(args, io, options = {}) {
|
|
|
6418
6081
|
}
|
|
6419
6082
|
let route = routeFromMode(parsed.mode);
|
|
6420
6083
|
if (route === void 0 && !io.canPrompt) requireModeForNonInteractive();
|
|
6421
|
-
const cwd =
|
|
6084
|
+
const cwd = resolve14(options.cwd ?? process.cwd());
|
|
6422
6085
|
await assertInitializableBuddyTarget(cwd);
|
|
6423
6086
|
if (parsed.directory !== void 0) {
|
|
6424
6087
|
if (!parsed.directory.trim()) throw new CliUsageError("--directory \u4E0D\u80FD\u4E3A\u7A7A");
|
|
6425
|
-
await assertInitializableBuddyTarget(
|
|
6088
|
+
await assertInitializableBuddyTarget(resolve14(cwd, parsed.directory));
|
|
6426
6089
|
}
|
|
6427
6090
|
await authenticate(options);
|
|
6428
6091
|
await resumeProjectArguments(parsed, cwd, options);
|
|
@@ -6564,7 +6227,6 @@ export {
|
|
|
6564
6227
|
assertOutsideBuddyProject,
|
|
6565
6228
|
buddyAgentConfig,
|
|
6566
6229
|
buildAgentEnvironment,
|
|
6567
|
-
checkWidgetDelivery,
|
|
6568
6230
|
combineBuddyProject,
|
|
6569
6231
|
createBuddyConfig,
|
|
6570
6232
|
createBuddyDevStarter,
|
|
@@ -6603,7 +6265,6 @@ export {
|
|
|
6603
6265
|
throwIfDevAborted,
|
|
6604
6266
|
validateBuddyAgentConfig,
|
|
6605
6267
|
validateBuddyConfig,
|
|
6606
|
-
validateBuddyProjectManifest
|
|
6607
|
-
widgetDeliverySummary
|
|
6268
|
+
validateBuddyProjectManifest
|
|
6608
6269
|
};
|
|
6609
6270
|
//# sourceMappingURL=core.js.map
|