@my-life-buddies/cli 0.28.0-dev.1 → 0.28.0-dev.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/core.js CHANGED
@@ -386,7 +386,8 @@ var resolveAvatarSkill = (previous, options = {}) => resolveSkillPackage(definit
386
386
 
387
387
  // packages/cli-core/src/cloud/avatar.ts
388
388
  import { open } from "node:fs/promises";
389
- import { basename } from "node:path";
389
+ import { basename, extname } from "node:path";
390
+ import sharp from "sharp";
390
391
  var MAX_BUDDY_AVATAR_BYTES = 5 * 1024 * 1024;
391
392
  async function readBuddyAvatarFile(path) {
392
393
  const file = await open(path, "r");
@@ -410,6 +411,33 @@ async function readBuddyAvatarFile(path) {
410
411
  await file.close();
411
412
  }
412
413
  }
414
+ async function prepareBuddyAvatarUpload(source) {
415
+ const input = Buffer.from(source.bytes);
416
+ if (!input.length || input.length > MAX_BUDDY_AVATAR_BYTES) throw new Error("\u5934\u50CF\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u8D85\u8FC7 5 MiB");
417
+ if (source.mediaType === "image/png") {
418
+ for (let at = 8; at + 12 <= input.length; ) {
419
+ const length = input.readUInt32BE(at);
420
+ if (input.toString("ascii", at + 4, at + 8) === "acTL") throw new Error("\u4EC5\u652F\u6301\u9759\u6001\u5934\u50CF\uFF0C\u4E0D\u652F\u6301\u52A8\u56FE");
421
+ at += length + 12;
422
+ }
423
+ }
424
+ const image = sharp(input, { failOn: "warning", limitInputPixels: 4096 * 4096 });
425
+ let meta2;
426
+ try {
427
+ meta2 = await image.metadata();
428
+ } catch {
429
+ throw new Error("\u56FE\u7247\u65E0\u6CD5\u89E3\u7801\u6216\u50CF\u7D20\u8FC7\u5927\uFF0C\u8BF7\u9009\u62E9\u5BBD\u9AD8\u4E0D\u8D85\u8FC7 4096 px \u7684\u9759\u6001 PNG\u3001JPEG\u3001WebP");
430
+ }
431
+ if (!["png", "jpeg", "webp"].includes(meta2.format ?? "")) throw new Error("\u4EC5\u652F\u6301 PNG\u3001JPEG\u3001WebP \u56FE\u7247\u6587\u4EF6");
432
+ if ((meta2.pages ?? 1) !== 1) throw new Error("\u4EC5\u652F\u6301\u9759\u6001\u5934\u50CF\uFF0C\u4E0D\u652F\u6301\u52A8\u56FE");
433
+ if (!meta2.width || !meta2.height || meta2.width > 4096 || meta2.height > 4096) throw new Error("\u5934\u50CF\u5BBD\u9AD8\u4E0D\u80FD\u8D85\u8FC7 4096 px");
434
+ try {
435
+ const bytes = await image.rotate().resize(512, 512, { fit: "cover", position: "centre" }).webp({ lossless: true }).timeout({ seconds: 10 }).toBuffer();
436
+ return { bytes, mediaType: "image/webp", fileName: `${basename(source.fileName, extname(source.fileName))}.webp` };
437
+ } catch {
438
+ throw new Error("\u5934\u50CF\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u56FE\u7247\u662F\u5426\u5B8C\u6574\uFF0C\u6216\u6362\u4E00\u5F20\u56FE\u7247\u91CD\u8BD5");
439
+ }
440
+ }
413
441
 
414
442
  // packages/cli-core/src/process-lock.ts
415
443
  import { randomUUID } from "node:crypto";
@@ -547,9 +575,10 @@ async function save(path, content) {
547
575
  });
548
576
  }
549
577
  }
550
- async function readImage(path) {
578
+ async function readImage(path, { forUpload = false } = {}) {
551
579
  try {
552
- return await readBuddyAvatarFile(path);
580
+ const source = await readBuddyAvatarFile(path);
581
+ return forUpload ? await prepareBuddyAvatarUpload(source) : source;
553
582
  } catch (error2) {
554
583
  throw new MetaPreparationError(`\u5934\u50CF\u672A\u5B8C\u6210\uFF1A${error2 instanceof Error ? error2.message : "\u65E0\u6CD5\u8BFB\u53D6\u56FE\u7247"}\u3002\u672C\u5730\u8D44\u6599\u5DF2\u4FDD\u7559\uFF0C\u5C1A\u672A\u63D0\u4EA4\u8FD9\u5F20\u5934\u50CF\u3002`);
555
584
  }
@@ -603,10 +632,9 @@ ${JSON.stringify(argv, null, 2)}
603
632
 
604
633
  \u4E0A\u4F20\u5931\u8D25\u6CBF\u7528\u5DF2\u4FDD\u5B58\u7684 buddyId \u548C\u5019\u9009\u56FE\u7247\uFF0C\u5728\u539F\u76EE\u5F55\u91CD\u8BD5\uFF0C\u4E0D\u53E6\u5EFA\u642D\u5B50\u3002\u5DF2\u6709\u4E91\u7AEF\u5934\u50CF\u4FDD\u7559\uFF1B\u53EA\u6709\u7528\u6237\u660E\u786E\u8981\u6C42\u66FF\u6362\u65F6\u624D\u4F7F\u7528 buddy-cli-sit meta update --avatar\u3002\u5B8C\u6210\u540E\u8BFB\u53D6 CLI \u8FD4\u56DE\u7684 Creator HANDOFF \u6216 Runtime \u5F00\u53D1\u6307\u5F15\u7EE7\u7EED\u3002
605
634
 
606
- \u666E\u901A\u7EC8\u7AEF\u53EF\u901A\u8FC7 --avatar \u6307\u5B9A\u5DF2\u6709\u56FE\u7247\uFF0C\u9884\u89C8\u5E76\u786E\u8BA4\uFF0C\u65E0\u9700\u4F7F\u7528\u751F\u6210 Skill\u3002
635
+ \u5DF2\u6709\u56FE\u7247\u53EF\u901A\u8FC7 --avatar \u6307\u5B9A\uFF0C\u65E0\u9700\u4F7F\u7528\u751F\u6210 Skill\u3002\u8BF7\u5728 Agent \u5BF9\u8BDD\u4E2D\u5C55\u793A\u8D44\u6599\u548C\u56FE\u7247\u5E76\u53D6\u5F97\u786E\u8BA4\uFF1BCLI \u4E0D\u751F\u6210\u6216\u6253\u5F00\u672C\u5730\u9884\u89C8\u9875\u3002
607
636
  `;
608
637
  }
609
- var escape = (text2) => text2.replace(/[&<>"']/gu, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[character]);
610
638
  async function prepareAndRegisterMeta(options) {
611
639
  const draft = await readMetaDraft(options.root);
612
640
  if (options.requestedBuddyId && (!draft || draft.status === "complete")) return options.requestedBuddyId;
@@ -679,10 +707,8 @@ async function prepareLockedMeta(options) {
679
707
  io.write(`\u5934\u50CF\u672A\u5B8C\u6210\uFF0C\u5DF2\u4FDD\u5B58\u672C\u5730\u8D44\u6599\uFF0C\u5C1A\u672A\u521B\u5EFA\u4E91\u7AEF\u642D\u5B50\u3002\u8BF7\u8BA9 Coding Agent \u8BFB\u53D6 ${join4(folder(root), "HANDOFF.md")}\uFF1B\u6216\u4F7F\u7528 --avatar <\u56FE\u7247\u8DEF\u5F84> \u7EE7\u7EED\u3002`);
680
708
  return void 0;
681
709
  }
682
- const upload = await readImage(join4(folder(root), draft.avatarPath));
710
+ const upload = await readImage(join4(folder(root), draft.avatarPath), { forUpload: true });
683
711
  const confirmation = hash(JSON.stringify([profile.name, profile.tagline, profile.intro, hash(upload.bytes)]));
684
- const preview = join4(folder(root), "preview.html");
685
- await save(preview, `<!doctype html><html lang="zh-CN"><meta charset="utf-8"><title>\u786E\u8BA4\u642D\u5B50\u8D44\u6599\u4E0E\u5934\u50CF</title><style>body{font:18px system-ui;max-width:800px;margin:48px auto;padding:24px;color:#163342}p{white-space:pre-wrap}section{display:flex;gap:24px}figure{margin:0;padding:24px;background:#eef4f8;border-radius:12px}figure.dark{background:#183040}img{width:220px;height:220px;object-fit:contain;border-radius:50%}</style><h1>${escape(profile.name)}</h1><p>${escape(profile.tagline)}</p><p>${escape(profile.intro)}</p><section><figure><img alt="\u6D45\u8272\u80CC\u666F\u5934\u50CF" src="data:${upload.mediaType};base64,${Buffer.from(upload.bytes).toString("base64")}"></figure><figure class="dark"><img alt="\u6DF1\u8272\u80CC\u666F\u5934\u50CF" src="data:${upload.mediaType};base64,${Buffer.from(upload.bytes).toString("base64")}"></figure></section><p>\u67E5\u770B\u5934\u50CF\u548C\u8D44\u6599\u540E\uFF0C\u56DE\u5230\u7EC8\u7AEF\u786E\u8BA4\u3002\u6B64\u9875\u9762\u4E0D\u4F1A\u4E0A\u4F20\u4EFB\u4F55\u5185\u5BB9\u3002</p></html>`);
686
712
  if (draft.confirmed !== confirmation) {
687
713
  draft.status = "awaiting_confirmation";
688
714
  delete draft.confirmed;
@@ -690,16 +716,8 @@ async function prepareLockedMeta(options) {
690
716
  io.write(`\u5F85\u786E\u8BA4\u8D44\u6599\uFF1A${profile.name}
691
717
  ${profile.tagline}
692
718
  ${profile.intro}
693
- \u5934\u50CF\u9884\u89C8\uFF1A${preview}`);
694
- let confirmed = options.confirmMeta === true;
695
- if (!confirmed && io.canPrompt) {
696
- await io.openMetaPreview?.(preview);
697
- confirmed = await io.select("\u5DF2\u67E5\u770B\u5934\u50CF\uFF0C\u786E\u8BA4\u8FD9\u4EFD\u8D44\u6599\u548C\u5934\u50CF\u5E76\u767B\u8BB0\u5230\u5E73\u53F0\uFF1F", [
698
- { value: "later", label: "\u6682\u4E0D\u767B\u8BB0\uFF0C\u4FDD\u7559\u8349\u7A3F" },
699
- { value: "confirm", label: "\u786E\u8BA4\u5E76\u767B\u8BB0" }
700
- ], { defaultValue: "later" }) === "confirm";
701
- }
702
- if (!confirmed) {
719
+ \u5934\u50CF\u6587\u4EF6\uFF1A${join4(folder(root), draft.avatarPath)}`);
720
+ if (options.confirmMeta !== true) {
703
721
  io.write("\u5F85\u5F00\u53D1\u8005\u786E\u8BA4\uFF0C\u5C1A\u672A\u8C03\u7528 Meta \u5199\u63A5\u53E3\u3002Coding Agent \u5C55\u793A\u5934\u50CF\u5E76\u53D6\u5F97\u660E\u786E\u786E\u8BA4\u540E\uFF0C\u4F7F\u7528 --avatar <\u5DF2\u786E\u8BA4\u56FE\u7247> --confirm-meta \u7EE7\u7EED\u3002");
704
722
  return void 0;
705
723
  }
@@ -3138,38 +3156,6 @@ var IMPLEMENTATION_GUIDE = implementationGuide();
3138
3156
  import { createHash as createHash4, randomBytes } from "node:crypto";
3139
3157
  import { spawn } from "node:child_process";
3140
3158
  import { createServer } from "node:http";
3141
-
3142
- // packages/cli-core/src/cloud/login-result.ts
3143
- import { readFileSync } from "node:fs";
3144
- var brand = new URL("../../resources/brand/", import.meta.url);
3145
- var tokens = readFileSync(new URL("tokens.css", brand), "utf8");
3146
- var styles = readFileSync(new URL("login-result.css", brand), "utf8");
3147
- var logo = readFileSync(new URL("logo.png", brand)).toString("base64");
3148
- function escapeHtml(value) {
3149
- return value.replace(/[&<>"']/gu, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;" })[character]);
3150
- }
3151
- function renderLoginResult(title, message, webUrl, tone) {
3152
- const label = { success: "\u5DF2\u6388\u6743", neutral: "\u5DF2\u53D6\u6D88", error: "\u672A\u5B8C\u6210" }[tone];
3153
- return `<!doctype html>
3154
- <html lang="zh-CN"><head>
3155
- <meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
3156
- <meta name="color-scheme" content="light dark"><title>${escapeHtml(title)} \xB7 \u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</title>
3157
- <style>${tokens}
3158
- ${styles}</style></head>
3159
- <body><div class="result-shell">
3160
- <header class="result-brand"><img src="data:image/png;base64,${logo}" alt="" width="44" height="44"><span>\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</span></header>
3161
- <main class="result-card" aria-labelledby="result-title">
3162
- <div class="result-heading"><p class="result-eyebrow">buddy-cli-sit \u767B\u5F55</p><span class="result-status" data-tone="${tone}">${label}</span></div>
3163
- <h1 id="result-title">${escapeHtml(title)}</h1>
3164
- <p class="result-message">${escapeHtml(message)}</p>
3165
- <div class="result-next"><strong>${tone === "success" ? "\u56DE\u5230\u7EC8\u7AEF\uFF0C\u7EE7\u7EED\u521B\u4F5C" : "\u56DE\u5230\u7EC8\u7AEF\uFF0C\u91CD\u65B0\u5F00\u59CB"}</strong>
3166
- <p>${tone === "success" ? "\u67E5\u770B CLI \u7684\u767B\u5F55\u7ED3\u679C\uFF0C\u7EE7\u7EED\u521B\u5EFA\u3001\u5F00\u53D1\u548C\u9884\u89C8\u4F60\u7684\u642D\u5B50\u3002" : "\u9700\u8981\u7EE7\u7EED\u65F6\uFF0C\u5728\u7EC8\u7AEF\u91CD\u65B0\u8FD0\u884C\u767B\u5F55\u547D\u4EE4\u3002"}</p>
3167
- ${tone === "success" ? "" : "<code>buddy-cli-sit login</code>"}</div>
3168
- <a class="result-link" href="${escapeHtml(webUrl.href)}">\u8FD4\u56DE\u5F00\u653E\u5E73\u53F0</a>
3169
- </main><footer>\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0</footer></div></body></html>`;
3170
- }
3171
-
3172
- // packages/cli-core/src/cloud/browser-login.ts
3173
3159
  var DEFAULT_LOGIN_TIMEOUT_MS = 5 * 6e4;
3174
3160
  var DEFAULT_EXCHANGE_TIMEOUT_MS = 15e3;
3175
3161
  var DEFAULT_DEVELOPER_WEB_URL = new URL("/developer/", DEFAULT_MLB_TEST_URL).href;
@@ -3192,19 +3178,23 @@ async function openExternalUrl(url) {
3192
3178
  child.once("exit", (code2) => code2 === 0 ? resolve20() : reject(new Error(`browser launcher exited with ${code2 ?? "unknown"}`)));
3193
3179
  });
3194
3180
  }
3195
- async function write(res, status, title, message, webUrl, tone = "error") {
3196
- res.writeHead(status, {
3181
+ async function redirectResult(res, webUrl, status) {
3182
+ const destination = new URL(webUrl);
3183
+ destination.search = "";
3184
+ destination.hash = `/cli-result?status=${status}`;
3185
+ res.writeHead(303, {
3186
+ location: destination.href,
3197
3187
  "cache-control": "no-store",
3198
3188
  "connection": "close",
3199
- "content-security-policy": "default-src 'none'; img-src data:; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'",
3200
- "content-type": "text/html; charset=utf-8",
3189
+ "content-security-policy": "default-src 'none'; frame-ancestors 'none'; base-uri 'none'",
3190
+ "content-length": "0",
3201
3191
  "referrer-policy": "no-referrer",
3202
3192
  "x-content-type-options": "nosniff"
3203
3193
  });
3204
3194
  await new Promise((resolve20, reject) => {
3205
3195
  res.once("error", reject);
3206
3196
  res.once("close", resolve20);
3207
- res.end(renderLoginResult(title, message, webUrl, tone), resolve20);
3197
+ res.end(resolve20);
3208
3198
  });
3209
3199
  }
3210
3200
  async function readJson(response) {
@@ -3308,20 +3298,20 @@ var LoopbackBrowserLoginAdapter = class {
3308
3298
  async #receiveCallback(req, res, expected) {
3309
3299
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
3310
3300
  if (req.method !== "GET" || url.pathname !== `/callback/${expected.callbackSecret}`) {
3311
- await write(res, 404, "\u9875\u9762\u4E0D\u5B58\u5728", "\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u65B0\u53D1\u8D77\u767B\u5F55\u3002", this.#developerWebUrl);
3301
+ await redirectResult(res, this.#developerWebUrl, "invalid");
3312
3302
  throw new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u6D4F\u89C8\u5668\u56DE\u8C03\u65E0\u6548\uFF0C\u8BF7\u91CD\u8BD5");
3313
3303
  }
3314
3304
  if (url.searchParams.get("state") !== expected.state) {
3315
- await write(res, 400, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
3305
+ await redirectResult(res, this.#developerWebUrl, "invalid");
3316
3306
  throw new CloudLifecycleError("AUTH_STATE_MISMATCH", "\u6D4F\u89C8\u5668\u767B\u5F55\u8FD4\u56DE\u7684 state \u4E0D\u5339\u914D\uFF1B\u51ED\u636E\u672A\u4FDD\u5B58");
3317
3307
  }
3318
3308
  if (url.searchParams.get("error") === "access_denied") {
3319
- await write(res, 200, "\u5DF2\u53D6\u6D88\u6388\u6743", "\u6CA1\u6709\u5411 buddy-cli-sit \u63D0\u4F9B\u8D26\u53F7\u6743\u9650\uFF0C\u53EF\u4EE5\u5173\u95ED\u8FD9\u4E2A\u9875\u9762\u3002", this.#developerWebUrl, "neutral");
3309
+ await redirectResult(res, this.#developerWebUrl, "cancelled");
3320
3310
  throw new CloudLifecycleError("AUTH_LOGIN_CANCELLED", "\u7528\u6237\u53D6\u6D88\u4E86 CLI \u6388\u6743");
3321
3311
  }
3322
3312
  const code2 = url.searchParams.get("code");
3323
3313
  if (!code2 || !AUTH_CODE.test(code2) || !expected.requestId) {
3324
- await write(res, 400, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u7ED3\u679C\u65E0\u6548\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
3314
+ await redirectResult(res, this.#developerWebUrl, "invalid");
3325
3315
  throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6388\u6743\u7801");
3326
3316
  }
3327
3317
  try {
@@ -3333,7 +3323,7 @@ var LoopbackBrowserLoginAdapter = class {
3333
3323
  if (exchanged.platformOrigin !== this.#platformUrl.origin || typeof exchanged.token !== "string" || typeof exchanged.userId !== "string" || !Number.isSafeInteger(exchanged.expiresAt)) {
3334
3324
  throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u642D\u642D\u521B\u4F5C\u8005\u5E73\u53F0\u8FD4\u56DE\u7684\u8D26\u53F7\u51ED\u636E\u4E0D\u5B8C\u6574");
3335
3325
  }
3336
- await write(res, 200, "\u767B\u5F55\u6210\u529F", "buddy-cli-sit \u5DF2\u83B7\u5F97\u6388\u6743\uFF0C\u53EF\u4EE5\u5173\u95ED\u8FD9\u4E2A\u9875\u9762\u5E76\u8FD4\u56DE\u7EC8\u7AEF\u3002", this.#developerWebUrl, "success");
3326
+ await redirectResult(res, this.#developerWebUrl, "authorized");
3337
3327
  return {
3338
3328
  state: expected.state,
3339
3329
  token: {
@@ -3343,7 +3333,7 @@ var LoopbackBrowserLoginAdapter = class {
3343
3333
  }
3344
3334
  };
3345
3335
  } catch (error2) {
3346
- await write(res, 502, "\u767B\u5F55\u6CA1\u6709\u5B8C\u6210", "\u6388\u6743\u4EA4\u6362\u5931\u8D25\uFF0C\u8BF7\u56DE\u5230\u7EC8\u7AEF\u91CD\u8BD5\u3002", this.#developerWebUrl);
3336
+ if (!res.headersSent) await redirectResult(res, this.#developerWebUrl, "failed");
3347
3337
  throw error2;
3348
3338
  }
3349
3339
  }
@@ -3425,6 +3415,7 @@ direct \u751F\u6210\u65B0\u4E1A\u52A1\u5DE5\u5382\u524D\uFF0C\u6309\u6700\u65B0
3425
3415
  \u540C\u76EE\u5F55\u6062\u590D\u4FDD\u7559\u5DF2\u6709\u4E1A\u52A1\u6587\u4EF6\u3002create \u540E\u6309 .buddy/creator/HANDOFF.md \u7EE7\u7EED\uFF0Cdirect \u540E\u5B89\u88C5\u4F9D\u8D56\u5E76\u5B9E\u73B0\u4E1A\u52A1\u3002
3426
3416
  Creator \u6BCF\u6B21\u8FDB\u5165\u6216\u6062\u590D\u90FD\u67E5\u8BE2\u516C\u5171 npm latest\uFF1B\u6709 Creator \u8BB0\u5F55\u7684\u5B8C\u6574\u5DE5\u7A0B\u4E5F\u53EF\u6267\u884C create \u63A5\u7EED\u521B\u4F5C\uFF0C\u4E0D\u91CD\u5EFA\u4E1A\u52A1\u6587\u4EF6\u3002
3427
3417
  \u5DF2\u767B\u8BB0\u642D\u5B50\u7684\u540D\u79F0\u3001\u4ECB\u7ECD\u6216\u5934\u50CF\u7528 buddy-cli-sit meta update \u4FEE\u6539\uFF1Binit \u4E0D\u8986\u76D6\u4E91\u7AEF\u8D44\u6599\u3002
3418
+ \u5934\u50CF\u4E0A\u4F20\u8BF7\u6C42\u6700\u591A\u7B49\u5F85 5 \u5206\u949F\uFF0CCLI \u4E0A\u4F20\u524D\u5C45\u4E2D\u88C1\u5207\u81F3 512\xD7512 \u65E0\u635F WebP\uFF0C\u4FDD\u7559\u989C\u8272\u3001\u900F\u660E\u5EA6\u4E0E\u539F\u59CB\u6587\u4EF6\uFF1B\u666E\u901A\u8BF7\u6C42\u4ECD\u4E3A 30 \u79D2\u3002
3428
3419
 
3429
3420
  \u793A\u4F8B:
3430
3421
  buddy-cli-sit init --mode direct --directory ./my-buddy --name "\u505A\u996D\u642D\u5B50" --tagline "\u5E2E\u4F60\u5B89\u6392\u665A\u996D" --intro "\u6839\u636E\u73B0\u6709\u98DF\u6750\u63A8\u8350\u83DC\u8C31"
@@ -3476,8 +3467,8 @@ Coding Agent \u5E94\u76F4\u63A5\u6CBF\u7528\u7528\u6237\u660E\u786E\u63D0\u4F9B\
3476
3467
  --buddy-id \u53EA\u7528\u4E8E\u6062\u590D\u5E73\u53F0\u5DF2\u7ECF\u751F\u6210\u4E14\u5C5E\u4E8E\u5F53\u524D\u8D26\u53F7\u7684\u642D\u5B50\u8EAB\u4EFD\uFF0C\u4E0D\u80FD\u81EA\u884C\u6307\u5B9A\u8EAB\u4EFD\u3002
3477
3468
  \u65B0\u5EFA\u65F6\u9700\u8981\u751F\u6210\u5934\u50CF\u624D\u6309\u9700\u52A0\u8F7D\u72EC\u7ACB @my-life-buddies/avatar-creator-skill\uFF0C\u7531 Coding Agent \u8BFB\u53D6 .buddy/meta/HANDOFF.md \u4E0E\u5176\u4E2D\u6307\u5411\u7684 Skill\uFF0C\u8C03\u7528\u5BBF\u4E3B\u56FE\u7247\u751F\u6210\u80FD\u529B\u5E76\u5C55\u793A\u5B9E\u9645\u5934\u50CF\u3002\u5DF2\u6709\u56FE\u7247\u786E\u8BA4\u548C\u4E0A\u4F20\u4E0D\u4F9D\u8D56\u751F\u6210 Skill\u3002
3478
3469
  \u5934\u50CF Skill \u7248\u672C\u8BB0\u5F55\u5728 .buddy/meta/skill-lock.json\uFF1B--avatar-skill-version <\u51C6\u786E\u7248\u672C> \u663E\u5F0F\u9009\u62E9\u7248\u672C\uFF0C\u672C\u5730\u8054\u8C03\u8BBE\u7F6E BUDDY_AVATAR_SKILL_PATH \u7EDD\u5BF9\u8DEF\u5F84\u3002
3479
- \u666E\u901A\u7EC8\u7AEF\u53EF\u9009\u62E9\u5DF2\u6709\u56FE\u7247\uFF1BCLI \u4E0D\u6267\u884C\u56FE\u7247\u751F\u6210\uFF0C\u4E5F\u4E0D\u8981\u6C42\u5B89\u88C5 Coding Agent\u3002\u4F7F\u7528 --avatar <\u56FE\u7247\u6587\u4EF6> \u6307\u5B9A\u5019\u9009\u5934\u50CF\uFF0C\u7EC8\u7AEF\u9884\u89C8\u5E76\u786E\u8BA4\u540E\u767B\u8BB0\u3002
3480
- \u975E\u4EA4\u4E92\u8C03\u7528\u987B\u5728\u5F00\u53D1\u8005\u660E\u786E\u786E\u8BA4\u5B9E\u9645\u5934\u50CF\u548C\u8D44\u6599\u540E\u4F20 --avatar <\u5DF2\u786E\u8BA4\u56FE\u7247> --confirm-meta\u3002\u672A\u786E\u8BA4\u4E0D\u5F97\u4F20\u8BE5\u6807\u5FD7\uFF1B\u7F3A\u5C11\u56FE\u7247\u6216\u786E\u8BA4\u65F6\u9000\u51FA\u7801\u4E3A 2\uFF0C\u8349\u7A3F\u4FDD\u7559\uFF0C\u53EF\u4EE5\u5728\u539F\u76EE\u5F55\u7EE7\u7EED\u3002
3470
+ \u4F7F\u7528 --avatar <\u56FE\u7247\u6587\u4EF6> \u6307\u5B9A\u5DF2\u6709\u5019\u9009\u5934\u50CF\uFF1BCLI \u4E0D\u6267\u884C\u56FE\u7247\u751F\u6210\uFF0C\u4E5F\u4E0D\u751F\u6210\u6216\u6253\u5F00\u672C\u5730\u9884\u89C8\u7F51\u9875\u3002\u8BF7\u8BA9 Coding Agent \u5728\u5BF9\u8BDD\u4E2D\u5C55\u793A\u5B9E\u9645\u56FE\u7247\u548C\u5B8C\u6574\u8D44\u6599\u3002
3471
+ \u53D6\u5F97\u5F00\u53D1\u8005\u5BF9\u5B9E\u9645\u5934\u50CF\u548C\u8D44\u6599\u7684\u660E\u786E\u786E\u8BA4\u540E\uFF0C\u624D\u53EF\u4F20 --avatar <\u5DF2\u786E\u8BA4\u56FE\u7247> --confirm-meta\u3002\u672A\u786E\u8BA4\u4E0D\u5F97\u4F20\u8BE5\u6807\u5FD7\uFF1B\u7F3A\u5C11\u56FE\u7247\u6216\u786E\u8BA4\u65F6\u9000\u51FA\u7801\u4E3A 2\uFF0C\u8349\u7A3F\u4FDD\u7559\uFF0C\u53EF\u4EE5\u5728\u539F\u76EE\u5F55\u7EE7\u7EED\u3002
3481
3472
  --mode create \u5B8C\u6210\u8D44\u6599\u548C\u5934\u50CF\u767B\u8BB0\u540E\uFF0C\u52A8\u6001\u83B7\u53D6\u72EC\u7ACB Creator Skill \u5E76\u751F\u6210\u63A5\u7EED\u8BF4\u660E\uFF0C\u652F\u6301 Creator \u63A5\u5165\u534F\u8BAE 1 \u548C 2\uFF1B\u9879\u76EE .buddy/creator/ \u4FDD\u5B58\u521B\u4F5C\u8D44\u6599\u4E0E skill-lock.json \u7248\u672C\u8BB0\u5F55\u3002\u6BCF\u6B21\u8FDB\u5165\u6216\u6062\u590D\u90FD\u67E5\u8BE2\u516C\u5171 npm latest\uFF0C\u786E\u8BA4\u662F\u6700\u65B0\u7248\u540E\u624D\u590D\u7528\u7F13\u5B58\uFF1Bskill-lock.json \u53EA\u8BB0\u5F55\u672C\u6B21\u7248\u672C\uFF0C\u4E0D\u56FA\u5B9A\u65E7\u7248\u672C\u3002\u67E5\u8BE2\u3001\u4E0B\u8F7D\u6216\u534F\u8BAE\u6821\u9A8C\u5931\u8D25\u65F6\u660E\u786E\u62A5\u9519\uFF0C\u4E0D\u9000\u56DE\u65E7\u7248\u3002\u672C\u5730\u8054\u8C03\u53EF\u663E\u5F0F\u8BBE\u7F6E BUDDY_CREATOR_SKILL_PATH \u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u6E05\u9664\u540E\u6062\u590D\u67E5\u8BE2 latest\u3002
3482
3473
  \u7531\u5F53\u524D Coding Agent \u8BFB\u53D6 .buddy/creator/HANDOFF.md \u5E76\u6267\u884C\u5176\u4E2D\u6307\u5411\u7684 Skill\uFF1B\u9700\u8981 Python 3.9+\uFF0C\u4E0D\u542F\u52A8\u65E7\u91C7\u8BBF Web \u6216\u72EC\u7ACB\u6A21\u578B\u3002
3483
3474
  \u6309\u5F53\u524D\u4F5C\u54C1\u5DE5\u4F5C\u6D41\u5B8C\u6210\u9700\u6C42\u3001\u5C0F\u6302\u4EF6\u6E05\u5355\u3001\u539F\u578B\u548C\u8BBE\u8BA1\u9A8C\u6536\uFF1B\u53EA\u6709\u4EA4\u4ED8\u6821\u9A8C\u8FD4\u56DE creator_complete\u3001completion.status=ready \u4E14\u4EA4\u4ED8\u6709\u6548\u540E\uFF0C\u624D\u7531 Coding Agent \u81EA\u52A8\u8854\u63A5\u5F00\u53D1\u3002\u8BFB\u53D6\u5B9E\u9645 manifest\uFF0C\u4E0D\u786C\u7F16\u7801\u624B\u518C\u6570\u91CF\u6216\u6587\u4EF6\u540D\uFF1B\u65B0\u7248\u4E09\u518C\u8FD8\u6309\u80FD\u529B\u5951\u7EA6\u5B8C\u6210\u5B9E\u65BD\u62A5\u544A\u548C handoff-check\uFF0C\u7ED3\u6784\u6821\u9A8C\u4E0D\u7B49\u4E8E\u771F\u5B9E Runtime \u9A8C\u8BC1\u3002\u65E7\u56DB\u518C\u7531\u6700\u65B0\u7248 Creator \u4FDD\u5B58\u5386\u53F2\u540E\u8FC1\u79FB\u4E3A\u4E09\u518C\uFF0C\u590D\u7528\u8D44\u6599\u548C\u8FDB\u5EA6\uFF0C\u8865\u9F50\u7F3A\u53E3\u5E76\u91CD\u65B0\u786E\u8BA4\u53D8\u5316\u5185\u5BB9\uFF1B\u4E0D\u76F4\u63A5\u6CBF\u7528\u65E7\u4EA4\u4ED8\u7684\u5B8C\u6210\u6807\u8BB0\u3002\u9700\u8981\u521D\u59CB\u5316\u65F6\u6267\u884C init --mode direct \u751F\u6210\u5B98\u65B9 Runtime \u5DE5\u7A0B\u3002\u751F\u6210\u65B0\u5DE5\u5382\u524D\u5217\u51FA\u6700\u65B0 dev Runtime \u652F\u6301\u7684\u6A21\u578B\uFF0C\u5148\u8BF7\u5F00\u53D1\u8005\u660E\u786E\u9009\u62E9\uFF0C\u518D\u4F20 --model\uFF1B\u5DF2\u6709\u660E\u786E\u9009\u62E9\u4E0D\u91CD\u590D\u8BE2\u95EE\uFF0C\u4E0D\u80FD\u7531 Coding Agent \u81EA\u884C\u9ED8\u8BA4\u3002\u5DF2\u6709\u4E1A\u52A1\u5DE5\u5382\u6CBF\u7528\u539F\u6A21\u578B\uFF0C\u4E0D\u4F20 --model\u3002\u540C\u76EE\u5F55\u6062\u590D\u65F6\u4ECE package.json \u8BFB\u53D6 buddyId\uFF0C\u5E76\u4F7F\u7528\u5E73\u53F0\u6700\u65B0 Meta\u3002
@@ -4470,7 +4461,7 @@ function startReloadingDev(options, startProcess) {
4470
4461
  }
4471
4462
 
4472
4463
  // packages/cli-core/src/dev/launch.ts
4473
- import { lstatSync, readFileSync as readFileSync2 } from "node:fs";
4464
+ import { lstatSync, readFileSync } from "node:fs";
4474
4465
  import { createRequire as createRequire3 } from "node:module";
4475
4466
  import { dirname as dirname4, resolve as resolve11 } from "node:path";
4476
4467
 
@@ -4540,10 +4531,10 @@ async function officialAgentLaunchPlan(input) {
4540
4531
  const packagePath = resolve11(input.projectRoot, BUDDY_PLATFORM_PACKAGE_JSON);
4541
4532
  try {
4542
4533
  await requireProjectRuntime(input.projectRoot, input.runtimeUpdate);
4543
- const expected = runtimeDependencyVersion(JSON.parse(readFileSync2(packagePath, "utf8")));
4534
+ const expected = runtimeDependencyVersion(JSON.parse(readFileSync(packagePath, "utf8")));
4544
4535
  if (!expected) throw new Error(`package.json \u7684 dependencies \u5FC5\u987B\u58F0\u660E ${BUDDY_RUNTIME_PACKAGE} \u7684\u51C6\u786E\u7248\u672C`);
4545
4536
  const runtimePath = createRequire3(packagePath).resolve(`${BUDDY_RUNTIME_PACKAGE}/package.json`);
4546
- const installed2 = JSON.parse(readFileSync2(runtimePath, "utf8")).version;
4537
+ const installed2 = JSON.parse(readFileSync(runtimePath, "utf8")).version;
4547
4538
  if (installed2 !== expected) throw new Error(`Runtime \u5DF2\u5B89\u88C5 ${installed2}\uFF0C\u5DE5\u7A0B\u8981\u6C42 ${expected}`);
4548
4539
  await validateRuntimeDatasets(input.projectRoot, await readDataAccessRequirements(input.projectRoot));
4549
4540
  } catch (cause) {
@@ -6060,7 +6051,10 @@ var HttpDeveloperPlatformClient = class {
6060
6051
  const form = new FormData();
6061
6052
  form.set("meta", JSON.stringify(request.meta));
6062
6053
  form.set("avatar", new Blob([new Uint8Array(request.avatar.bytes)], { type: request.avatar.mediaType }), request.avatar.fileName);
6063
- return buddy(await this.#request(`/cli/buddies/${encodeURIComponent(request.id)}/meta`, "PUT", { form }), true);
6054
+ return buddy(await this.#request(`/cli/buddies/${encodeURIComponent(request.id)}/meta`, "PUT", {
6055
+ form,
6056
+ timeoutMs: this.#uploadTimeoutMs
6057
+ }), true);
6064
6058
  }
6065
6059
  return buddy(await this.#request(`/cli/buddies/${encodeURIComponent(request.id)}/meta`, "PUT", {
6066
6060
  json: request.meta
@@ -6285,6 +6279,7 @@ var HELP = Object.freeze({
6285
6279
  meta \u53EA\u67E5\u770B\u4E91\u7AEF\u8D44\u6599\uFF1Bmeta update \u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u4FEE\u6539\u5B57\u6BB5\uFF0C\u672A\u4F20\u5B57\u6BB5\u4FDD\u6301\u539F\u6837\u3002
6286
6280
  \u6587\u5B57\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A\uFF1B\u540D\u79F0\u6700\u591A ${BUDDY_PROFILE_TEXT_LIMITS.name} \u5B57\uFF0C\u4E00\u53E5\u8BDD\u4ECB\u7ECD\u6700\u591A ${BUDDY_PROFILE_TEXT_LIMITS.tagline} \u5B57\uFF0C\u5B8C\u6574\u4ECB\u7ECD\u6700\u591A ${BUDDY_PROFILE_TEXT_LIMITS.intro} \u5B57\u3002
6287
6281
  --avatar \u4E0A\u4F20\u66FF\u6362\u5934\u50CF\uFF1A\u672C\u5730\u9759\u6001 PNG\u3001JPEG\u3001WebP\uFF0C\u6700\u5927 5 MiB\uFF1B\u4E0D\u63D0\u4F9B\u6E05\u9664\u5934\u50CF\u9009\u9879\u3002
6282
+ \u5934\u50CF\u4E0A\u4F20\u8BF7\u6C42\u6700\u591A\u7B49\u5F85 5 \u5206\u949F\uFF0CCLI \u4E0A\u4F20\u524D\u5C45\u4E2D\u88C1\u5207\u81F3 512\xD7512 \u65E0\u635F WebP\uFF0C\u4FDD\u7559\u989C\u8272\u3001\u900F\u660E\u5EA6\u4E0E\u539F\u59CB\u6587\u4EF6\uFF1B\u67E5\u8BE2\u548C\u4EC5\u6587\u5B57\u66F4\u65B0\u4ECD\u4E3A 30 \u79D2\u3002
6288
6283
  \u9ED8\u8BA4\u8BFB\u53D6\u5F53\u524D\u5DE5\u7A0B\u7684 package.json.buddyId\uFF1B--buddy-id \u4E0E --directory \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528\u3002\u56FE\u7247\u8DEF\u5F84\u76F8\u5BF9\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3002
6289
6284
  \u9700\u8981\u5F53\u524D\u8D26\u53F7\u62E5\u6709\u8BE5\u642D\u5B50\uFF1B\u66F4\u65B0\u7ACB\u5373\u5F71\u54CD\u4E91\u7AEF\u5C55\u793A\u8D44\u6599\uFF0C\u65E0\u9700 Runtime\u3001push \u6216\u91CD\u65B0\u53D1\u5E03\u7A0B\u5E8F\u5305\u3002
6290
6285
  \u793A\u4F8B: buddy-cli-sit meta update --name "\u5DDD\u5927\u53A8" --tagline "\u5E2E\u4F60\u5B89\u6392\u6BCF\u5929\u5403\u4EC0\u4E48" --avatar ./avatar.png`,
@@ -6292,6 +6287,7 @@ meta \u53EA\u67E5\u770B\u4E91\u7AEF\u8D44\u6599\uFF1Bmeta update \u81F3\u5C11\u6
6292
6287
  \u65E7\u547D\u4EE4\u517C\u5BB9\u5165\u53E3\uFF1B\u66F4\u6362\u5934\u50CF\u63A8\u8350\u4F7F\u7528 buddy-cli-sit meta update --avatar <\u56FE\u7247\u6587\u4EF6>\u3002
6293
6288
  \u4FEE\u6539\u5F53\u524D\u9879\u76EE\u6216\u6307\u5B9A\u642D\u5B50\u7684\u4E91\u7AEF\u5934\u50CF\uFF0C\u4FDD\u7559\u5176\u4ED6\u8D44\u6599\uFF1B\u4E0D\u9700\u8981\u5B89\u88C5 Runtime \u6216\u4E0A\u4F20\u4EE3\u7801\u5305\u3002
6294
6289
  --file \u63A5\u53D7\u672C\u5730\u9759\u6001 PNG\u3001JPEG\u3001WebP \u56FE\u7247\uFF0C\u6700\u5927 5 MiB\uFF1BServer \u81EA\u52A8\u5904\u7406\u5E76\u751F\u6210\u5730\u5740\u3002
6290
+ \u5934\u50CF\u4E0A\u4F20\u8BF7\u6C42\u6700\u591A\u7B49\u5F85 5 \u5206\u949F\uFF0CCLI \u4E0A\u4F20\u524D\u5C45\u4E2D\u88C1\u5207\u81F3 512\xD7512 \u65E0\u635F WebP\uFF0C\u4FDD\u7559\u989C\u8272\u3001\u900F\u660E\u5EA6\u4E0E\u539F\u59CB\u6587\u4EF6\uFF1B\u6E05\u9664\u5934\u50CF\u4ECD\u4E3A 30 \u79D2\u3002
6295
6291
  \u9700\u8981\u5F53\u524D\u8D26\u53F7\u62E5\u6709\u8BE5\u642D\u5B50\uFF0C\u547D\u4EE4\u6267\u884C\u540E\u7ACB\u5373\u66F4\u65B0\u4E91\u7AEF\u8D44\u6599\u3002`
6296
6292
  });
6297
6293
  function parse3(args, command) {
@@ -6420,7 +6416,7 @@ async function runMeta(parsed, io, options, legacyAvatar = false) {
6420
6416
  }
6421
6417
  let upload;
6422
6418
  try {
6423
- upload = file ? await readBuddyAvatarFile(resolve16(options.cwd ?? process.cwd(), file)) : void 0;
6419
+ upload = file ? await prepareBuddyAvatarUpload(await readBuddyAvatarFile(resolve16(options.cwd ?? process.cwd(), file))) : void 0;
6424
6420
  } catch (error2) {
6425
6421
  throw new CloudCommandUsageError(error2 instanceof Error ? error2.message : "\u65E0\u6CD5\u8BFB\u53D6\u5934\u50CF\u56FE\u7247");
6426
6422
  }
@@ -6518,7 +6514,7 @@ var CLOUD_COMMAND_HELP = HELP;
6518
6514
 
6519
6515
  // packages/cli-core/src/migration/index.ts
6520
6516
  import { lstat as lstat12, open as open5, readdir as readdir6, realpath as realpath7, stat } from "node:fs/promises";
6521
- import { extname, isAbsolute as isAbsolute3, join as join22, relative, resolve as resolve17, sep, win32 as win322 } from "node:path";
6517
+ import { extname as extname2, isAbsolute as isAbsolute3, join as join22, relative, resolve as resolve17, sep, win32 as win322 } from "node:path";
6522
6518
  var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
6523
6519
  maxFiles: 128,
6524
6520
  maxFileBytes: 256 * 1024,
@@ -6950,7 +6946,7 @@ function frontmatterEnd(lines) {
6950
6946
  return -1;
6951
6947
  }
6952
6948
  function shellLines(lines, path) {
6953
- const extension = extname(path).toLowerCase();
6949
+ const extension = extname2(path).toLowerCase();
6954
6950
  const shebang = lines[0] ?? "";
6955
6951
  if (SHELL_EXTENSIONS.has(extension) || /^#!.*(?:[/\s])(?:bash|fish|powershell|pwsh|sh|zsh)(?:\s|$)/u.test(shebang)) return lines.map(() => true);
6956
6952
  const result = lines.map(() => false);
@@ -7078,7 +7074,7 @@ function analyzeText(source, path, context) {
7078
7074
  const lines = source.split(/\r\n|\n|\r/u);
7079
7075
  const metadataEnd = frontmatterEnd(lines);
7080
7076
  const shellContext = shellLines(lines, path);
7081
- const extension = extname(path).toLowerCase();
7077
+ const extension = extname2(path).toLowerCase();
7082
7078
  const localShellVariables = /* @__PURE__ */ new Set();
7083
7079
  const localBatchVariables = /* @__PURE__ */ new Set();
7084
7080
  const localPowerShellEnvironment = /* @__PURE__ */ new Set();