@my-life-buddies/cli 0.5.0 → 0.8.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/dist/bin/core.js CHANGED
@@ -17,7 +17,7 @@ function runModelsCommand(args, io) {
17
17
  } else if (args.length === 1 && args[0] === "--json") {
18
18
  io.write(JSON.stringify({ models: MODEL_IDS }, null, 2));
19
19
  } else if (args.length === 0) {
20
- io.write("CLI \u5185\u7F6E\u6A21\u578B\u76EE\u5F55\uFF08Runtime 0.11.0\uFF09\uFF1A");
20
+ io.write("CLI \u5185\u7F6E\u6A21\u578B\u76EE\u5F55\uFF08Runtime 0.12.0\uFF09\uFF1A");
21
21
  for (const id of MODEL_IDS) io.write(`- ${id}`);
22
22
  io.write("\u5C06\u6240\u9009 ID \u5199\u5165 buddy.agent.json.model\uFF1B\u672A\u8BBE\u7F6E\u65F6\u6A21\u677F\u4F7F\u7528 kimi-k2.6\u3002\u81EA\u5B9A\u4E49\u4F1A\u8BDD\u5DE5\u5382\u4E5F\u53EF\u6309\u4F1A\u8BDD\u8BBE\u7F6E initialState.model\u3002");
23
23
  } else {
@@ -1462,11 +1462,13 @@ Tool \u548C Hook \u662F Runtime \u7684\u6269\u5C55\u70B9\uFF1BService \u53EA\u66
1462
1462
 
1463
1463
  \u5E73\u53F0\u80FD\u529B\u8981\u4EE5\u5B9E\u9645\u63A5\u5165\u4E3A\u51C6\u3002\u5F53\u524D Runtime \u63D0\u4F9B\u4F1A\u8BDD\u6D88\u606F\u3001Memory\u3001\u6388\u6743\u6570\u636E\u3001\u5C0F\u6302\u4EF6\u548C\u672C\u5730 Markdown \u8D44\u6599\u80FD\u529B\uFF1B\u6CA1\u6709\u63A5\u901A\u7684 Files\u3001Knowledge \u4E0D\u80FD\u56E0\u624B\u518C\u63D0\u5230\u5C31\u5BA3\u79F0\u53EF\u7528\u3002\u666E\u901A Tool \u4E5F\u4E0D\u4EE3\u8868\u5DF2\u7ECF\u63D0\u4F9B\u5B9A\u65F6\u3001\u957F\u671F\u4EFB\u52A1\u3001\u540E\u53F0\u8C03\u5EA6\u6216\u4E3B\u52A8\u89E6\u8FBE\u3002\u624B\u518C\u63CF\u8FF0\u7684\u4ED8\u8D39\u3001\u53D1\u5E03\u7B49\u670D\u52A1\u8BBE\u8BA1\u4E5F\u4E0D\u6784\u6210\u6267\u884C\u6536\u8D39\u6216\u53D1\u5E03\u52A8\u4F5C\u7684\u6388\u6743\u3002
1464
1464
 
1465
- \u9700\u8981\u7528\u6237\u6570\u636E\u65F6\uFF0C\u5148\u5728\u5DE5\u7A0B\u76EE\u5F55\u6267\u884C \`buddy-cli datasets\`\uFF08\u811A\u672C\u53EF\u7528 \`--json\`\uFF09\uFF0C\u4ECE\u5F53\u524D\u5B89\u88C5\u7684 Runtime \u5BFC\u51FA\u8BFB\u53D6\u5168\u90E8\u6570\u636E\u96C6\uFF1B\u4E0D\u8981\u6C42\u767B\u5F55\uFF0C\u4E0D\u4ECE Server \u62C9\u76EE\u5F55\u3002Runtime 0.11.0 \u542B 10 \u9879\uFF0CCLI \u4E0D\u590D\u5236\u679A\u4E3E\u3002\u628A\u5B9E\u9645\u9700\u8981\u7684 \`id\` \u548C\u7528\u9014 \`purpose\` \u5199\u5165 \`buddy.agent.json.datasets\`\uFF0C\u540C\u4E00 ID \u53EA\u5199\u4E00\u6B21\uFF0C\u7528\u9014\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u6700\u591A 200 \u5B57\uFF1B\u65E0\u9700\u6C42\u65F6\u4FDD\u7559\u7A7A\u6570\u7EC4\u3002DEV \u548C\u58F0\u660E\u975E\u7A7A\u65F6\u7684 push \u9884\u68C0\u7528\u8FD9\u4EFD\u5DE5\u7A0B\u4F9D\u8D56\u6821\u9A8C ID\uFF1B\u9700\u8981\u5148\u5B89\u88C5\u4E0E package.json \u51C6\u786E\u7248\u672C\u4E00\u81F4\u7684 Runtime\u3002\u6E05\u5355\u58F0\u660E\u6570\u91CF\u4E0D\u7B49\u4E8E\u5355\u6B21\u67E5\u8BE2\u4E0A\u9650\uFF0C\u5F53\u524D\u4E00\u6B21\u67E5\u8BE2\u6700\u591A 8 \u9879\u3002
1465
+ \u9700\u8981\u7528\u6237\u6570\u636E\u65F6\uFF0C\u5148\u5728\u5DE5\u7A0B\u76EE\u5F55\u6267\u884C \`buddy-cli datasets\`\uFF08\u811A\u672C\u53EF\u7528 \`--json\`\uFF09\uFF0C\u4ECE\u5F53\u524D\u5B89\u88C5\u7684 Runtime \u5BFC\u51FA\u8BFB\u53D6\u5168\u90E8\u6570\u636E\u96C6\uFF1B\u4E0D\u8981\u6C42\u767B\u5F55\uFF0C\u4E0D\u4ECE Server \u62C9\u76EE\u5F55\u3002Runtime 0.12.0 \u542B 10 \u9879\uFF0CCLI \u4E0D\u590D\u5236\u679A\u4E3E\u3002\u628A\u5B9E\u9645\u9700\u8981\u7684 \`id\` \u548C\u7528\u9014 \`purpose\` \u5199\u5165 \`buddy.agent.json.datasets\`\uFF0C\u540C\u4E00 ID \u53EA\u5199\u4E00\u6B21\uFF0C\u7528\u9014\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u6700\u591A 200 \u5B57\uFF1B\u65E0\u9700\u6C42\u65F6\u4FDD\u7559\u7A7A\u6570\u7EC4\u3002DEV \u548C\u58F0\u660E\u975E\u7A7A\u65F6\u7684 push \u9884\u68C0\u7528\u8FD9\u4EFD\u5DE5\u7A0B\u4F9D\u8D56\u6821\u9A8C ID\uFF1B\u9700\u8981\u5148\u5B89\u88C5\u4E0E package.json \u51C6\u786E\u7248\u672C\u4E00\u81F4\u7684 Runtime\u3002\u6E05\u5355\u58F0\u660E\u6570\u91CF\u4E0D\u7B49\u4E8E\u5355\u6B21\u67E5\u8BE2\u4E0A\u9650\uFF0C\u5F53\u524D\u4E00\u6B21\u67E5\u8BE2\u6700\u591A 8 \u9879\u3002
1466
1466
 
1467
1467
  \`start.mjs\` \u5C06\u58F0\u660E\u4EA4\u7ED9 \`StartOptions.dataRequirements\`\u3002Runtime \u5728\u5B9E\u9645\u6570\u636E\u67E5\u8BE2\u3001HealthKit \u6388\u6743\u548C\u4E3B\u52A8\u670D\u52A1\u8BF7\u6C42\u4E2D\u643A\u5E26\u5B8C\u6574\u58F0\u660E\uFF0C\u7531 Server \u6821\u9A8C\u8303\u56F4\u4E0E\u7528\u6237\u540C\u610F\uFF1B\u586B\u5199\u914D\u7F6E\u4E0D\u8868\u793A\u5DF2\u7ECF\u6388\u6743\u3002\u670D\u52A1\u7AEF\u4ECE\u4E0A\u4F20\u5305\u89E3\u6790\u5E76\u7ED1\u5B9A\u53D1\u5E03\u7248\u672C\u4ECD\u5F85\u5B9E\u73B0\u3002
1468
1468
 
1469
- \u6A21\u578B\u9700\u8981\u67E5\u6570\u636E\u65F6\uFF0C\u5728 \`src/buddy-options.mjs\` \u7684 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.data_access_query\`\uFF1B\u9700\u8981\u5411\u7528\u6237\u7533\u8BF7\u65F6\u52A0\u5165 \`conversation.tools.data_access_request\`\uFF0C\u4F4D\u7F6E\u3001\u65E5\u5386\u7684\u4E00\u6B21\u6027\u7ED3\u679C\u901A\u8FC7 \`data_access_read_result\` \u8BFB\u53D6\uFF0CHealthKit \u5219\u91CD\u65B0\u67E5\u8BE2 Dataset\u3002\u4E1A\u52A1\u4EE3\u7801\u5BF9\u5E94\u4F7F\u7528 \`conversation.dataAccess.query/request/readResult\`\u3002\u5DE5\u7A0B\u6A21\u677F\u5DF2\u9501\u5B9A npm Runtime 0.11.0\u3002
1469
+ \u6A21\u578B\u9700\u8981\u67E5\u6570\u636E\u65F6\uFF0C\u5728 \`src/buddy-options.mjs\` \u7684 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.data_access_query\`\uFF1B\u9700\u8981\u5411\u7528\u6237\u7533\u8BF7\u65F6\u52A0\u5165 \`conversation.tools.data_access_request\`\uFF0C\u4F4D\u7F6E\u3001\u65E5\u5386\u7684\u4E00\u6B21\u6027\u7ED3\u679C\u901A\u8FC7 \`data_access_read_result\` \u8BFB\u53D6\uFF0CHealthKit \u5219\u91CD\u65B0\u67E5\u8BE2 Dataset\u3002\u4E1A\u52A1\u4EE3\u7801\u5BF9\u5E94\u4F7F\u7528 \`conversation.dataAccess.query/request/readResult\`\u3002\u5DE5\u7A0B\u6A21\u677F\u5DF2\u9501\u5B9A npm Runtime 0.12.0\u3002
1470
+
1471
+ \u5C0F\u6302\u4EF6\u7684\u6A21\u578B\u5DE5\u5177\u4E3A \`widget_create\`\u3001\`widget_read\`\uFF08\u6574\u4E2A\u5C0F\u6302\u4EF6\uFF09\u3001\`widget_read_module\`\uFF08\u4E00\u4E2A\u6A21\u5757\uFF09\u3001\`widget_rename\`\u3001\`widget_write_record\`\u3001\`widget_delete_record\`\u3001\`widget_delete\`\uFF08\u6574\u4E2A\u5C0F\u6302\u4EF6\u8FDE\u8BB0\u5F55\uFF09\u548C \`widget_send\`\uFF0C\u6309\u4E1A\u52A1\u9700\u8981\u6CE8\u518C\u5230 \`initialState.tools\`\u3002\u4FEE\u6539\u8BB0\u5F55\u7528 \`conversation.widget.writeRecord(id, input)\`\uFF1B\u5220\u4E00\u6761\u8BB0\u5F55\u7528 \`conversation.widget.deleteRecord(id, module, recordId)\`\uFF0C\u4E0D\u80FD\u7528\u5220\u9664\u6574\u4E2A\u5C0F\u6302\u4EF6\u7684 \`conversation.widget.delete(id)\` \u4EE3\u66FF\u3002\u4E24\u4E2A\u8BFB\u53D6\u5DE5\u5177\u53EF\u7528\u4E8E\u4E3B\u52A8\u56DE\u5408\uFF0C\u5176\u4F59\u516D\u4E2A\u5199\u5165\u5DE5\u5177\u7531 Runtime \u963B\u6B62\u5728\u4E3B\u52A8\u56DE\u5408\u6267\u884C\u3002
1470
1472
 
1471
1473
  \u9700\u8981\u7528\u6237\u4ECE\u51E0\u4E2A\u9009\u9879\u4E2D\u9009\u62E9\u65F6\uFF0C\u5728 \`initialState.tools\` \u4E2D\u52A0\u5165 \`conversation.tools.ask_question\` \u5E76\u5728\u4EBA\u8BBE\u4E2D\u8BF4\u660E\u9002\u7528\u573A\u666F\u3002\u53D1\u9898\u5361\u6216\u6388\u6743\u5361\u540E\uFF0C\u672C\u8F6E\u7ED3\u675F\u5E76\u7B49\u5F85\u7528\u6237\uFF1BPreview \u80FD\u663E\u793A\u9009\u9879\u6587\u5B57\u5E76\u53D1\u9001\u7528\u6237\u7684\u6587\u5B57\u56DE\u7B54\uFF0C\u7CFB\u7EDF\u6570\u636E\u6388\u6743\u9700\u8981\u5728 App \u4E2D\u5B8C\u6210\u3002\u6A21\u677F\u7684 \`initialState.tools\` \u9ED8\u8BA4\u4E3A\u7A7A\u6570\u7EC4\uFF0C\u53EA\u52A0\u5165\u4E1A\u52A1\u5B9E\u9645\u9700\u8981\u7684\u5DE5\u5177\uFF1B\u8D44\u6599\u76EE\u5F55\u901A\u8FC7 \`resource_list\` \u53D1\u73B0\uFF0C\u4E0D\u81EA\u52A8\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u3002\u65B0\u4F1A\u8BDD\u7684\u95EE\u5019\u7531\u6A21\u578B\u6309\u4EBA\u8BBE\u751F\u6210\uFF0C\u4E0D\u53E6\u8BBE\u9759\u6001\u6B22\u8FCE\u8BED\u914D\u7F6E\u3002
1472
1474
 
@@ -1740,11 +1742,13 @@ function validateBuddyMeta(meta2) {
1740
1742
  invalidRequest(`meta.${key} \u5FC5\u987B\u4E3A\u4E0D\u8D85\u8FC7 ${limit} \u5B57\u7684\u5B57\u7B26\u4E32\u6216 null`);
1741
1743
  }
1742
1744
  }
1743
- if (meta2.avatar != null) {
1744
- const value = meta2.avatar;
1745
+ if (meta2.avatar !== void 0) validateBuddyAvatar(meta2.avatar);
1746
+ }
1747
+ function validateBuddyAvatar(value) {
1748
+ if (value !== null) {
1745
1749
  let valid = false;
1746
1750
  if (typeof value === "string" && value.length > 0 && value.length <= 2048 && !/[\s\u0000-\u001f]/u.test(value)) {
1747
- valid = /^\/media\/[A-Za-z0-9_.-]+$/u.test(value);
1751
+ valid = /^\/media\/[A-Za-z0-9_.-]+(?:\?v=[a-f0-9]{16})?$/u.test(value);
1748
1752
  if (!valid) {
1749
1753
  try {
1750
1754
  const url = new URL(value);
@@ -1753,9 +1757,13 @@ function validateBuddyMeta(meta2) {
1753
1757
  }
1754
1758
  }
1755
1759
  }
1756
- if (!valid) invalidRequest("meta.avatar \u5FC5\u987B\u4E3A\u5408\u6CD5\u56FE\u7247\u5730\u5740\u6216 null");
1760
+ if (!valid) invalidRequest("avatar \u5FC5\u987B\u4E3A\u5408\u6CD5\u56FE\u7247\u5730\u5740\u6216 null");
1757
1761
  }
1758
1762
  }
1763
+ function validateBuddyMetaUpdate(meta2) {
1764
+ if (meta2.avatar !== void 0 && meta2.avatar !== null) invalidRequest("\u5934\u50CF\u5730\u5740\u7531\u5E73\u53F0\u751F\u6210\uFF0C\u8BF7\u4E0A\u4F20\u56FE\u7247\u6587\u4EF6\u6216\u4F20 null \u6E05\u9664");
1765
+ if (Object.keys(meta2).some((key) => key !== "avatar")) validateBuddyMeta(meta2);
1766
+ }
1759
1767
 
1760
1768
  // packages/cli-core/src/commands/dev.ts
1761
1769
  import { resolve as resolve11 } from "node:path";
@@ -2913,6 +2921,33 @@ async function runDevCommand(args, io, options = {}) {
2913
2921
  // packages/cli-core/src/commands/cloud.ts
2914
2922
  import { resolve as resolve12 } from "node:path";
2915
2923
 
2924
+ // packages/cli-core/src/cloud/avatar.ts
2925
+ import { open as open3 } from "node:fs/promises";
2926
+ import { basename } from "node:path";
2927
+ var MAX_BUDDY_AVATAR_BYTES = 5 * 1024 * 1024;
2928
+ async function readBuddyAvatarFile(path) {
2929
+ const file2 = await open3(path, "r");
2930
+ try {
2931
+ const stat2 = await file2.stat();
2932
+ if (!stat2.isFile()) throw new Error("\u5934\u50CF\u5FC5\u987B\u662F\u666E\u901A\u56FE\u7247\u6587\u4EF6");
2933
+ 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");
2934
+ const buffer = Buffer.alloc(MAX_BUDDY_AVATAR_BYTES + 1);
2935
+ let size = 0;
2936
+ while (size < buffer.length) {
2937
+ const { bytesRead } = await file2.read(buffer, size, buffer.length - size, null);
2938
+ if (!bytesRead) break;
2939
+ size += bytesRead;
2940
+ }
2941
+ if (!size || size > MAX_BUDDY_AVATAR_BYTES) throw new Error("\u5934\u50CF\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u8D85\u8FC7 5 MiB");
2942
+ const bytes = buffer.subarray(0, size);
2943
+ const mediaType = bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) ? "image/png" : bytes.subarray(0, 3).equals(Buffer.from([255, 216, 255])) ? "image/jpeg" : bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP" ? "image/webp" : void 0;
2944
+ if (!mediaType) throw new Error("\u4EC5\u652F\u6301 PNG\u3001JPEG\u3001WebP \u56FE\u7247\u6587\u4EF6");
2945
+ return { bytes, fileName: basename(path), mediaType };
2946
+ } finally {
2947
+ await file2.close();
2948
+ }
2949
+ }
2950
+
2916
2951
  // packages/cli-core/src/cloud/auth.ts
2917
2952
  import { randomUUID as randomUUID4 } from "node:crypto";
2918
2953
 
@@ -3108,6 +3143,7 @@ function meta(value, path) {
3108
3143
  const tagline = optionalText(source.tagline, `${path}.tagline`);
3109
3144
  const intro = optionalText(source.intro, `${path}.intro`, true);
3110
3145
  return Object.freeze({
3146
+ ...source,
3111
3147
  ...name === void 0 ? {} : { name },
3112
3148
  ...avatar === void 0 ? {} : { avatar },
3113
3149
  ...tagline === void 0 ? {} : { tagline },
@@ -3250,7 +3286,7 @@ var HttpDeveloperPlatformClient = class {
3250
3286
  "Content-Length": String(binaryBody.byteLength)
3251
3287
  }
3252
3288
  },
3253
- ...options.json !== void 0 ? { body: JSON.stringify(options.json) } : binaryBody === void 0 ? {} : { body: binaryBody },
3289
+ ...options.form !== void 0 ? { body: options.form } : options.json !== void 0 ? { body: JSON.stringify(options.json) } : binaryBody === void 0 ? {} : { body: binaryBody },
3254
3290
  redirect: "error",
3255
3291
  signal: controller.signal
3256
3292
  });
@@ -3277,7 +3313,14 @@ var HttpDeveloperPlatformClient = class {
3277
3313
  return buddy(await this.#request("/cli/buddies", "POST", { json: { meta: request.meta } }), true);
3278
3314
  }
3279
3315
  async updateBuddy(request) {
3280
- validateBuddyMeta(request.meta);
3316
+ validateBuddyMetaUpdate(request.meta);
3317
+ if (request.avatar) {
3318
+ if (request.meta.avatar === null) throw new CloudLifecycleError("INVALID_REQUEST", "\u4E0D\u80FD\u540C\u65F6\u4E0A\u4F20\u548C\u6E05\u9664\u5934\u50CF");
3319
+ const form = new FormData();
3320
+ form.set("meta", JSON.stringify(request.meta));
3321
+ form.set("avatar", new Blob([new Uint8Array(request.avatar.bytes)], { type: request.avatar.mediaType }), request.avatar.fileName);
3322
+ return buddy(await this.#request(`/cli/buddies/${encodeURIComponent(request.id)}/meta`, "PUT", { form }), true);
3323
+ }
3281
3324
  return buddy(await this.#request(`/cli/buddies/${encodeURIComponent(request.id)}/meta`, "PUT", {
3282
3325
  json: request.meta
3283
3326
  }), true);
@@ -3708,7 +3751,7 @@ var SystemCloudTokenStore = class {
3708
3751
  };
3709
3752
 
3710
3753
  // packages/cli-core/src/cloud/mock-client.ts
3711
- import { randomBytes as randomBytes2 } from "node:crypto";
3754
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
3712
3755
  var MockDeveloperPlatformClient = class {
3713
3756
  #now;
3714
3757
  #buddies = /* @__PURE__ */ new Map();
@@ -3734,7 +3777,10 @@ var MockDeveloperPlatformClient = class {
3734
3777
  }
3735
3778
  async updateBuddy(request) {
3736
3779
  const current = this.#require(request.id);
3737
- const next = this.#value({ ...current, meta: request.meta, updatedAt: this.#now() });
3780
+ validateBuddyMetaUpdate(request.meta);
3781
+ const onlyAvatar = Object.keys(request.meta).every((key) => key === "avatar");
3782
+ const avatar = request.avatar ? `/media/avatar-${request.id}.webp?v=${createHash5("sha256").update(request.avatar.bytes).digest("hex").slice(0, 16)}` : request.meta.avatar === null ? null : current.meta?.avatar ?? null;
3783
+ const next = this.#value({ ...current, meta: { ...onlyAvatar ? current.meta : request.meta, avatar }, updatedAt: this.#now() });
3738
3784
  this.#buddies.set(request.id, next);
3739
3785
  return structuredClone(next);
3740
3786
  }
@@ -3819,7 +3865,11 @@ var HELP = Object.freeze({
3819
3865
  login: "\u7528\u6CD5: buddy-cli login",
3820
3866
  logout: "\u7528\u6CD5: buddy-cli logout",
3821
3867
  push: "\u7528\u6CD5: buddy-cli push [--directory <\u9879\u76EE\u76EE\u5F55>] [--commit <\u672C\u6B21\u6539\u52A8\u8BF4\u660E>]",
3822
- status: "\u7528\u6CD5: buddy-cli status [--directory <\u9879\u76EE\u76EE\u5F55>]"
3868
+ status: "\u7528\u6CD5: buddy-cli status [--directory <\u9879\u76EE\u76EE\u5F55>]",
3869
+ avatar: `\u7528\u6CD5: buddy-cli avatar (--file <\u56FE\u7247\u6587\u4EF6> | --clear) [--directory <\u9879\u76EE\u76EE\u5F55> | --buddy-id <\u642D\u5B50 ID>]
3870
+ \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
3871
+ --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
3872
+ \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`
3823
3873
  });
3824
3874
  function parse2(args, command) {
3825
3875
  const values = /* @__PURE__ */ new Map();
@@ -3831,10 +3881,15 @@ function parse2(args, command) {
3831
3881
  help = true;
3832
3882
  continue;
3833
3883
  }
3834
- const allowed = command === "push" ? flag2 === "--directory" || flag2 === "--commit" : command === "status" && flag2 === "--directory";
3884
+ if (command === "avatar" && flag2 === "--clear") {
3885
+ if (values.has(flag2)) throw new CloudCommandUsageError(`${flag2} \u4E0D\u80FD\u91CD\u590D`);
3886
+ values.set(flag2, "");
3887
+ continue;
3888
+ }
3889
+ const allowed = command === "push" ? flag2 === "--directory" || flag2 === "--commit" : command === "avatar" ? ["--directory", "--buddy-id", "--file"].includes(flag2) : command === "status" && flag2 === "--directory";
3835
3890
  if (!allowed) throw new CloudCommandUsageError(`\u672A\u77E5\u53C2\u6570 ${flag2}`);
3836
3891
  const value = args[index + 1];
3837
- if (value === void 0 || !value.trim()) throw new CloudCommandUsageError(`${flag2} \u7F3A\u5C11\u503C`);
3892
+ if (value === void 0 || !value.trim() || command === "avatar" && value.startsWith("--")) throw new CloudCommandUsageError(`${flag2} \u7F3A\u5C11\u503C`);
3838
3893
  if (values.has(flag2)) throw new CloudCommandUsageError(`${flag2} \u4E0D\u80FD\u91CD\u590D`);
3839
3894
  values.set(flag2, value);
3840
3895
  index += 1;
@@ -3881,6 +3936,7 @@ function printStatus(io, buddy2, packages) {
3881
3936
  io.write(`\u642D\u5B50\uFF1A${displayName}\uFF08${buddy2.id}\uFF09`);
3882
3937
  if (buddy2.meta?.tagline) io.write(`\u4E00\u53E5\u8BDD\u4ECB\u7ECD\uFF1A${buddy2.meta.tagline}`);
3883
3938
  if (buddy2.meta?.intro) io.write(`\u5B8C\u6574\u4ECB\u7ECD\uFF1A${buddy2.meta.intro}`);
3939
+ if (buddy2.meta?.avatar) io.write(`\u5934\u50CF\uFF1A${buddy2.meta.avatar}`);
3884
3940
  io.write(`\u4EE3\u7801\uFF1A${buddy2.pushed ? "\u5DF2\u4E0A\u4F20" : "\u672A\u4E0A\u4F20"}`);
3885
3941
  if (buddy2.version) io.write(`\u7EBF\u4E0A\u7248\u672C\uFF1A${buddy2.version}`);
3886
3942
  io.write(`\u8FD0\u884C\uFF1A${buddy2.online ? "\u5728\u7EBF" : "\u79BB\u7EBF"}`);
@@ -3941,6 +3997,41 @@ async function runPush(parsed, io, options) {
3941
3997
  io.write("\u672C\u6B21 push \u53EA\u63D0\u4EA4\u5BA1\u6838\uFF0C\u4E0D\u4F1A\u90E8\u7F72\u6216\u53D1\u5E03 Agent\u3002");
3942
3998
  return { exitCode: 0, buddyId: config.buddyId, version: receipt.version };
3943
3999
  }
4000
+ async function runAvatar(parsed, io, options) {
4001
+ if (parsed.values.has("--file") === parsed.values.has("--clear")) {
4002
+ throw new CloudCommandUsageError("\u5FC5\u987B\u9009\u62E9 --file <\u56FE\u7247\u6587\u4EF6> \u6216 --clear\uFF0C\u4E14\u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
4003
+ }
4004
+ if (parsed.values.has("--buddy-id") && parsed.values.has("--directory")) {
4005
+ throw new CloudCommandUsageError("--buddy-id \u548C --directory \u4E0D\u80FD\u540C\u65F6\u4F7F\u7528");
4006
+ }
4007
+ const file2 = parsed.values.get("--file");
4008
+ let upload;
4009
+ try {
4010
+ upload = file2 ? await readBuddyAvatarFile(resolve12(options.cwd ?? process.cwd(), file2)) : void 0;
4011
+ } catch (error2) {
4012
+ throw new CloudCommandUsageError(error2 instanceof Error ? error2.message : "\u65E0\u6CD5\u8BFB\u53D6\u5934\u50CF\u56FE\u7247");
4013
+ }
4014
+ let id = parsed.values.get("--buddy-id");
4015
+ if (id !== void 0 && !/^[A-Za-z0-9_-]{1,128}$/u.test(id)) throw new CloudCommandUsageError("--buddy-id \u5FC5\u987B\u662F\u6709\u6548\u7684\u642D\u5B50 ID");
4016
+ if (id === void 0) {
4017
+ const root = await (options.resolveProjectRoot ?? requireBuddyProjectRoot)(requestedDirectory(options, parsed));
4018
+ id = (await (options.readManifest ?? readBuddyProjectManifest)(root)).buddyId;
4019
+ }
4020
+ if (options.mode === "local-mock") throw new CloudCommandUsageError("\u672C\u5730\u6A21\u62DF\u6A21\u5F0F\u4E0D\u4FEE\u6539\u4E91\u7AEF\u5934\u50CF\uFF0C\u8BF7\u8FDE\u63A5\u771F\u5B9E MLB Server");
4021
+ const { session, client } = dependencies(options);
4022
+ await session.token({ signal: options.signal });
4023
+ const current = await client.getBuddy({ id });
4024
+ if (current.id !== id) throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u5E73\u53F0\u8FD4\u56DE\u7684\u642D\u5B50\u8EAB\u4EFD\u4E0D\u5339\u914D");
4025
+ const updated = await client.updateBuddy({ id, meta: upload ? {} : { avatar: null }, ...upload ? { avatar: upload } : {} });
4026
+ const avatar = updated.meta?.avatar;
4027
+ if (updated.id !== id || (upload ? typeof avatar !== "string" || !avatar : avatar !== null)) {
4028
+ throw new CloudLifecycleError("PLATFORM_RESPONSE_INVALID", "\u5E73\u53F0\u672A\u786E\u8BA4\u5934\u50CF\u66F4\u65B0\uFF0C\u8BF7\u67E5\u8BE2\u642D\u5B50\u8D44\u6599\u786E\u8BA4\u7ED3\u679C");
4029
+ }
4030
+ io.write(`${avatar === null ? "\u5DF2\u6E05\u9664" : "\u5DF2\u66F4\u65B0"}\u642D\u5B50\u5934\u50CF\uFF1A${updated.meta?.name ?? id}\uFF08${id}\uFF09`);
4031
+ if (typeof avatar === "string") io.write(`\u5934\u50CF\uFF1A${avatar}`);
4032
+ io.write("\u4E91\u7AEF\u8D44\u6599\u5DF2\u66F4\u65B0\uFF0C\u65E0\u9700 push \u6216\u91CD\u65B0\u53D1\u5E03\u7A0B\u5E8F\u5305\u3002");
4033
+ return { exitCode: 0, buddyId: id };
4034
+ }
3944
4035
  async function runCloudCommand(command, args, io, options = {}) {
3945
4036
  try {
3946
4037
  const parsed = parse2(args, command);
@@ -3957,6 +4048,7 @@ async function runCloudCommand(command, args, io, options = {}) {
3957
4048
  }
3958
4049
  if (command === "logout") return await runLogout(io, options);
3959
4050
  if (command === "push") return await runPush(parsed, io, options);
4051
+ if (command === "avatar") return await runAvatar(parsed, io, options);
3960
4052
  const root = await (options.resolveProjectRoot ?? requireBuddyProjectRoot)(requestedDirectory(options, parsed));
3961
4053
  const manifest = await (options.readManifest ?? readBuddyProjectManifest)(root);
3962
4054
  if (options.mode === "local-mock") {
@@ -3984,7 +4076,7 @@ async function runCloudCommand(command, args, io, options = {}) {
3984
4076
  var CLOUD_COMMAND_HELP = HELP;
3985
4077
 
3986
4078
  // packages/cli-core/src/migration/index.ts
3987
- import { lstat as lstat7, open as open3, readdir as readdir4, realpath as realpath4, stat } from "node:fs/promises";
4079
+ import { lstat as lstat7, open as open4, readdir as readdir4, realpath as realpath4, stat } from "node:fs/promises";
3988
4080
  import { extname, isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve13, sep, win32 as win322 } from "node:path";
3989
4081
  var DEFAULT_SKILL_SCAN_LIMITS = Object.freeze({
3990
4082
  maxFiles: 128,
@@ -4337,7 +4429,7 @@ async function scanFile(actualPath, reportPath, context) {
4337
4429
  }
4338
4430
  let handle;
4339
4431
  try {
4340
- handle = await open3(actualPath, "r");
4432
+ handle = await open4(actualPath, "r");
4341
4433
  } catch (cause) {
4342
4434
  return rejectScan("SKILL_SCAN_IO_ERROR", reportPath, "\u65E0\u6CD5\u6253\u5F00 Skill \u6587\u4EF6\u3002", cause);
4343
4435
  }
@@ -4749,8 +4841,8 @@ async function scanExternalSkillPortability(options) {
4749
4841
  }
4750
4842
 
4751
4843
  // packages/cli-core/src/push/preflight.ts
4752
- import { createHash as createHash5 } from "node:crypto";
4753
- import { lstat as lstat8, open as open4, readdir as readdir5, realpath as realpath5 } from "node:fs/promises";
4844
+ import { createHash as createHash6 } from "node:crypto";
4845
+ import { lstat as lstat8, open as open5, readdir as readdir5, realpath as realpath5 } from "node:fs/promises";
4754
4846
  import { isAbsolute as isAbsolute3, join as join9, relative as relative2, resolve as resolve14, sep as sep2, win32 as win323 } from "node:path";
4755
4847
 
4756
4848
  // packages/cli-core/src/push/errors.ts
@@ -4849,7 +4941,7 @@ function error(options) {
4849
4941
  throw new PushPreflightError(options);
4850
4942
  }
4851
4943
  function sha256(value) {
4852
- return `sha256:${createHash5("sha256").update(value).digest("hex")}`;
4944
+ return `sha256:${createHash6("sha256").update(value).digest("hex")}`;
4853
4945
  }
4854
4946
  function positiveLimit(value, name, hardMaximum) {
4855
4947
  if (!Number.isSafeInteger(value) || value < 1 || value > hardMaximum) {
@@ -5080,7 +5172,7 @@ function sameFileIdentity(left, right) {
5080
5172
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs;
5081
5173
  }
5082
5174
  async function hashRegularFile(options) {
5083
- const handle = await open4(options.absolutePath, "r").catch((cause) => error({
5175
+ const handle = await open5(options.absolutePath, "r").catch((cause) => error({
5084
5176
  code: "PUSH_PROJECT_INVALID",
5085
5177
  path: options.portablePath,
5086
5178
  message: `\u65E0\u6CD5\u8BFB\u53D6\u63D0\u4EA4\u6587\u4EF6\uFF1A${options.portablePath}`,
@@ -5095,7 +5187,7 @@ async function hashRegularFile(options) {
5095
5187
  message: `\u751F\u6210\u6E05\u5355\u65F6\u6587\u4EF6\u53D1\u751F\u53D8\u5316\uFF1A${options.portablePath}`
5096
5188
  });
5097
5189
  }
5098
- const hash = createHash5("sha256");
5190
+ const hash = createHash6("sha256");
5099
5191
  const captured = [];
5100
5192
  const buffer = Buffer.allocUnsafe(READ_BUFFER_BYTES);
5101
5193
  let bytes = 0;
@@ -5560,7 +5652,7 @@ async function createPushPreflight(options) {
5560
5652
  }
5561
5653
 
5562
5654
  // packages/cli-core/src/push/bundle.ts
5563
- import { createHash as createHash6 } from "node:crypto";
5655
+ import { createHash as createHash7 } from "node:crypto";
5564
5656
  import { lstat as lstat9, readFile as readFile8, realpath as realpath6 } from "node:fs/promises";
5565
5657
  import { join as join10, relative as relative3, sep as sep3, win32 as win324 } from "node:path";
5566
5658
  import { Writable } from "node:stream";
@@ -5569,7 +5661,7 @@ import { createGzip } from "node:zlib";
5569
5661
  import { pack } from "tar-stream";
5570
5662
  var MEDIA_TYPE = "application/gzip";
5571
5663
  function digest(bytes) {
5572
- return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
5664
+ return `sha256:${createHash7("sha256").update(bytes).digest("hex")}`;
5573
5665
  }
5574
5666
  function changed(path, message, cause) {
5575
5667
  throw new PushPreflightError({
@@ -5618,7 +5710,7 @@ async function createPushSourceBundle(plan) {
5618
5710
  schemaVersion: "1",
5619
5711
  files: files.map((file2) => ({ path: file2.path, bytes: file2.bytes, digest: file2.digest }))
5620
5712
  });
5621
- const calculatedBundleDigest = `sha256:${createHash6("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
5713
+ const calculatedBundleDigest = `sha256:${createHash7("sha256").update(`buddy-push-bundle-v1\0${descriptor}`).digest("hex")}`;
5622
5714
  if (calculatedBundleDigest !== plan.bundleDigest) {
5623
5715
  changed(plan.projectRoot, "\u63D0\u4EA4\u6E05\u5355\u8EAB\u4EFD\u5728\u6253\u5305\u524D\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C push");
5624
5716
  }
@@ -5673,11 +5765,14 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
5673
5765
 
5674
5766
  \u7528\u6CD5:
5675
5767
  buddy-cli
5768
+ buddy-cli --version [--json]
5769
+ buddy-cli update [--check] [--json]
5676
5770
  buddy-cli init
5677
5771
  buddy-cli init --mode create --directory <\u9879\u76EE\u76EE\u5F55> --name <\u540D\u79F0> --tagline <\u4E00\u53E5\u8BDD\u4ECB\u7ECD> --intro <\u5B8C\u6574\u4ECB\u7ECD> [--buddy-id <\u5DF2\u6709\u642D\u5B50 ID>]
5678
5772
  buddy-cli init --mode direct --directory <\u9879\u76EE\u76EE\u5F55> --name <\u540D\u79F0> --tagline <\u4E00\u53E5\u8BDD\u4ECB\u7ECD> --intro <\u5B8C\u6574\u4ECB\u7ECD> [--buddy-id <\u5DF2\u6709\u642D\u5B50 ID>]
5679
5773
  buddy-cli dev [--app-server <URL>] [--directory <\u9879\u76EE\u76EE\u5F55>]
5680
5774
  buddy-cli login | logout | push | status
5775
+ buddy-cli avatar (--file <\u56FE\u7247\u6587\u4EF6> | --clear) [--directory <\u9879\u76EE\u76EE\u5F55> | --buddy-id <\u642D\u5B50 ID>]
5681
5776
  buddy-cli models [--json]
5682
5777
  buddy-cli widgets sync | check | verify
5683
5778
  buddy-cli datasets [--directory <\u9879\u76EE\u76EE\u5F55>] [--json]
@@ -5685,6 +5780,8 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
5685
5780
  buddy-cli help
5686
5781
 
5687
5782
  \u547D\u4EE4:
5783
+ --version \u67E5\u770B\u5F53\u524D\u8FD0\u884C\u7684 CLI \u7248\u672C\uFF08\u65E0\u9700\u8054\u7F51\u6216\u767B\u5F55\uFF09
5784
+ update \u68C0\u67E5 npm \u6700\u65B0\u7248\u672C\u6216\u66F4\u65B0\u5168\u5C40 CLI\uFF08\u65E0\u9700\u767B\u5F55\u6216 Agent \u5DE5\u7A0B\uFF09
5688
5785
  init \u521B\u5EFA\u6216\u6062\u590D\u642D\u5B50\uFF0C\u51C6\u5907\u5B98\u65B9 Runtime \u5DE5\u7A0B\u6216 Creator \u624B\u518C
5689
5786
  dev \u542F\u52A8\u5B98\u65B9 Runtime \u5E76\u8FDE\u63A5 DEV App Server
5690
5787
  login \u901A\u8FC7\u6D4F\u89C8\u5668\u767B\u5F55\u642D\u642D\u8D26\u53F7
@@ -5693,6 +5790,7 @@ var HELP2 = `\u642D\u642D Developer Platform CLI
5693
5790
  datasets \u8BFB\u53D6\u5DE5\u7A0B Runtime \u63D0\u4F9B\u7684\u6570\u636E\u96C6\u76EE\u5F55\uFF08\u65E0\u9700\u767B\u5F55\uFF09
5694
5791
  push \u4E0A\u4F20 Agent \u5DE5\u7A0B\u548C commit\uFF0C\u63D0\u4EA4\u5E73\u53F0\u5BA1\u6838
5695
5792
  status \u67E5\u8BE2\u642D\u5B50\u7684\u8D44\u6599\u3001\u4EE3\u7801\u3001\u8FD0\u884C\u4E0E\u53D1\u5E03\u72B6\u6001
5793
+ avatar \u8BBE\u7F6E\u6216\u6E05\u9664\u642D\u5B50\u5934\u50CF\uFF0C\u4FDD\u7559\u5176\u4ED6\u4E91\u7AEF\u8D44\u6599
5696
5794
  preview \u6253\u5F00\u642D\u642D App \u79C1\u804A\u6A21\u62DF\u5668\u4E0E\u8C03\u8BD5\u9875\u9762
5697
5795
  help \u67E5\u770B\u5E2E\u52A9
5698
5796
 
@@ -6088,7 +6186,7 @@ async function runBuddyCli(args, io, options = {}) {
6088
6186
  ...options.dev
6089
6187
  });
6090
6188
  }
6091
- if (command === "login" || command === "logout" || command === "push" || command === "status") {
6189
+ if (command === "login" || command === "logout" || command === "push" || command === "status" || command === "avatar") {
6092
6190
  return await runCloudCommand(command, rest, io, {
6093
6191
  cwd: options.cwd,
6094
6192
  ...options.cloud