@alfe.ai/openclaw 0.3.2 → 0.4.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/plugin2.cjs CHANGED
@@ -3106,6 +3106,23 @@ function avatarMimeFromPath(path) {
3106
3106
  if (!mimeType) throw new Error(`unsupported image type "${ext || path}" — set_avatar accepts .png, .jpg/.jpeg, or .webp`);
3107
3107
  return mimeType;
3108
3108
  }
3109
+ /**
3110
+ * Infer an avatar mime type for a remote image — prefer the response's
3111
+ * Content-Type, else the URL path extension. Lets `set_avatar` accept a URL
3112
+ * (e.g. the one `generate_image` returns), which is how an agent naturally
3113
+ * wants to set the picture it just generated.
3114
+ */
3115
+ function avatarMimeFromUrl(url, contentType) {
3116
+ const ct = (contentType ?? "").split(";")[0].trim().toLowerCase();
3117
+ if (ct === "image/png" || ct === "image/jpeg" || ct === "image/webp") return ct;
3118
+ let pathname = url;
3119
+ try {
3120
+ pathname = new URL(url).pathname;
3121
+ } catch {}
3122
+ const byExt = AVATAR_MIME_BY_EXT[(0, node_path.extname)(pathname).toLowerCase()];
3123
+ if (byExt) return byExt;
3124
+ throw new Error(`could not determine the image type for "${url}" — set_avatar accepts png, jpeg, or webp`);
3125
+ }
3109
3126
  function ok(result) {
3110
3127
  return { content: [{
3111
3128
  type: "text",
@@ -3257,12 +3274,25 @@ function buildVoiceTools(client) {
3257
3274
  }),
3258
3275
  defineTool({
3259
3276
  name: "set_avatar",
3260
- description: "Set THIS agent's own profile picture from a local image file you created or downloaded (png/jpeg/webp, <=5MB). Updates the real profile picture shown in the dashboard, chat, and apps. Use this — do NOT use `config set ui.assistant.avatar`, which has no effect on the real profile.",
3261
- parameters: Type.Object({ path: Type.String({ description: "Absolute path to a local image file (.png, .jpg/.jpeg, or .webp, <=5MB) to use as the avatar." }) }),
3277
+ description: "Set THIS agent's OWN profile picture from an image — pass a `url` (e.g. the URL returned by `generate_image`) OR a local file `path` you created/downloaded (png/jpeg/webp, <=5MB). Updates the real profile picture shown in the dashboard, chat, and apps. IMPORTANT: this is how you set YOUR OWN avatar — do NOT use `update_identity` (that edits a CONTACT/person you know, not your own profile) and do NOT use `config set ui.assistant.avatar` (no effect).",
3278
+ parameters: Type.Object({
3279
+ url: Type.Optional(Type.String({ description: "URL of an image to use as the avatar — e.g. the `url` returned by `generate_image`. png/jpeg/webp, <=5MB." })),
3280
+ path: Type.Optional(Type.String({ description: "Absolute path to a LOCAL image file (.png, .jpg/.jpeg, or .webp, <=5MB). Use `url` instead if you have a URL." }))
3281
+ }),
3262
3282
  handler: async (params) => {
3283
+ const url = params.url;
3263
3284
  const path = params.path;
3264
- const mimeType = avatarMimeFromPath(path);
3265
- const bytes = await (0, node_fs_promises.readFile)(path);
3285
+ let bytes;
3286
+ let mimeType;
3287
+ if (url) {
3288
+ const dl = await fetch(url);
3289
+ if (!dl.ok) throw new Error(`failed to download the image from the url (${String(dl.status)})`);
3290
+ bytes = Buffer.from(await dl.arrayBuffer());
3291
+ mimeType = avatarMimeFromUrl(url, dl.headers.get("content-type"));
3292
+ } else if (path) {
3293
+ mimeType = avatarMimeFromPath(path);
3294
+ bytes = await (0, node_fs_promises.readFile)(path);
3295
+ } else throw new Error("provide either `url` (an image URL, e.g. from generate_image) or `path` (a local image file)");
3266
3296
  if (bytes.length > MAX_AVATAR_BYTES) throw new Error(`image is ${String(bytes.length)} bytes — the avatar limit is ${String(MAX_AVATAR_BYTES)} bytes (5MB)`);
3267
3297
  const { uploadUrl, s3Key } = await client.presignAvatar({
3268
3298
  mimeType,
@@ -3271,7 +3301,7 @@ function buildVoiceTools(client) {
3271
3301
  const putRes = await fetch(uploadUrl, {
3272
3302
  method: "PUT",
3273
3303
  headers: { "Content-Type": mimeType },
3274
- body: bytes
3304
+ body: new Uint8Array(bytes)
3275
3305
  });
3276
3306
  if (!putRes.ok) throw new Error(`failed to upload the avatar image (${String(putRes.status)})`);
3277
3307
  return {
@@ -3283,7 +3313,7 @@ function buildVoiceTools(client) {
3283
3313
  }),
3284
3314
  defineTool({
3285
3315
  name: "generate_image",
3286
- description: "Generate an image from a text prompt. Returns a URL to the generated PNG. To show the image to the user, embed it in your reply as markdown: ![description](imageUrl). Optional `model` picks the image model (default gpt-image-1). Generation is synchronous and may take up to ~30s; if it times out, retry or use a standard size.",
3316
+ description: "Generate an image from a text prompt. Returns a URL to the generated PNG. To show the image to the user, embed it in your reply as markdown: ![description](imageUrl). To set the generated image as YOUR OWN profile picture, pass its url to `set_avatar` (NOT update_identity, which edits other people you know). Or skip this and use `generate_avatar` to generate + set your avatar in one step. Optional `model` picks the image model (default gpt-image-1). Generation is synchronous and may take up to ~30s; if it times out, retry or use a standard size.",
3287
3317
  parameters: Type.Object({
3288
3318
  prompt: Type.String({ description: "Text description of the image to generate." }),
3289
3319
  model: Type.Optional(Type.String({ description: "Image model id (default \"gpt-image-1\"). Others may be available, e.g. dall-e-3." })),
package/dist/plugin2.js CHANGED
@@ -3107,6 +3107,23 @@ function avatarMimeFromPath(path) {
3107
3107
  if (!mimeType) throw new Error(`unsupported image type "${ext || path}" — set_avatar accepts .png, .jpg/.jpeg, or .webp`);
3108
3108
  return mimeType;
3109
3109
  }
3110
+ /**
3111
+ * Infer an avatar mime type for a remote image — prefer the response's
3112
+ * Content-Type, else the URL path extension. Lets `set_avatar` accept a URL
3113
+ * (e.g. the one `generate_image` returns), which is how an agent naturally
3114
+ * wants to set the picture it just generated.
3115
+ */
3116
+ function avatarMimeFromUrl(url, contentType) {
3117
+ const ct = (contentType ?? "").split(";")[0].trim().toLowerCase();
3118
+ if (ct === "image/png" || ct === "image/jpeg" || ct === "image/webp") return ct;
3119
+ let pathname = url;
3120
+ try {
3121
+ pathname = new URL(url).pathname;
3122
+ } catch {}
3123
+ const byExt = AVATAR_MIME_BY_EXT[extname(pathname).toLowerCase()];
3124
+ if (byExt) return byExt;
3125
+ throw new Error(`could not determine the image type for "${url}" — set_avatar accepts png, jpeg, or webp`);
3126
+ }
3110
3127
  function ok(result) {
3111
3128
  return { content: [{
3112
3129
  type: "text",
@@ -3258,12 +3275,25 @@ function buildVoiceTools(client) {
3258
3275
  }),
3259
3276
  defineTool({
3260
3277
  name: "set_avatar",
3261
- description: "Set THIS agent's own profile picture from a local image file you created or downloaded (png/jpeg/webp, <=5MB). Updates the real profile picture shown in the dashboard, chat, and apps. Use this — do NOT use `config set ui.assistant.avatar`, which has no effect on the real profile.",
3262
- parameters: Type.Object({ path: Type.String({ description: "Absolute path to a local image file (.png, .jpg/.jpeg, or .webp, <=5MB) to use as the avatar." }) }),
3278
+ description: "Set THIS agent's OWN profile picture from an image — pass a `url` (e.g. the URL returned by `generate_image`) OR a local file `path` you created/downloaded (png/jpeg/webp, <=5MB). Updates the real profile picture shown in the dashboard, chat, and apps. IMPORTANT: this is how you set YOUR OWN avatar — do NOT use `update_identity` (that edits a CONTACT/person you know, not your own profile) and do NOT use `config set ui.assistant.avatar` (no effect).",
3279
+ parameters: Type.Object({
3280
+ url: Type.Optional(Type.String({ description: "URL of an image to use as the avatar — e.g. the `url` returned by `generate_image`. png/jpeg/webp, <=5MB." })),
3281
+ path: Type.Optional(Type.String({ description: "Absolute path to a LOCAL image file (.png, .jpg/.jpeg, or .webp, <=5MB). Use `url` instead if you have a URL." }))
3282
+ }),
3263
3283
  handler: async (params) => {
3284
+ const url = params.url;
3264
3285
  const path = params.path;
3265
- const mimeType = avatarMimeFromPath(path);
3266
- const bytes = await readFile(path);
3286
+ let bytes;
3287
+ let mimeType;
3288
+ if (url) {
3289
+ const dl = await fetch(url);
3290
+ if (!dl.ok) throw new Error(`failed to download the image from the url (${String(dl.status)})`);
3291
+ bytes = Buffer.from(await dl.arrayBuffer());
3292
+ mimeType = avatarMimeFromUrl(url, dl.headers.get("content-type"));
3293
+ } else if (path) {
3294
+ mimeType = avatarMimeFromPath(path);
3295
+ bytes = await readFile(path);
3296
+ } else throw new Error("provide either `url` (an image URL, e.g. from generate_image) or `path` (a local image file)");
3267
3297
  if (bytes.length > MAX_AVATAR_BYTES) throw new Error(`image is ${String(bytes.length)} bytes — the avatar limit is ${String(MAX_AVATAR_BYTES)} bytes (5MB)`);
3268
3298
  const { uploadUrl, s3Key } = await client.presignAvatar({
3269
3299
  mimeType,
@@ -3272,7 +3302,7 @@ function buildVoiceTools(client) {
3272
3302
  const putRes = await fetch(uploadUrl, {
3273
3303
  method: "PUT",
3274
3304
  headers: { "Content-Type": mimeType },
3275
- body: bytes
3305
+ body: new Uint8Array(bytes)
3276
3306
  });
3277
3307
  if (!putRes.ok) throw new Error(`failed to upload the avatar image (${String(putRes.status)})`);
3278
3308
  return {
@@ -3284,7 +3314,7 @@ function buildVoiceTools(client) {
3284
3314
  }),
3285
3315
  defineTool({
3286
3316
  name: "generate_image",
3287
- description: "Generate an image from a text prompt. Returns a URL to the generated PNG. To show the image to the user, embed it in your reply as markdown: ![description](imageUrl). Optional `model` picks the image model (default gpt-image-1). Generation is synchronous and may take up to ~30s; if it times out, retry or use a standard size.",
3317
+ description: "Generate an image from a text prompt. Returns a URL to the generated PNG. To show the image to the user, embed it in your reply as markdown: ![description](imageUrl). To set the generated image as YOUR OWN profile picture, pass its url to `set_avatar` (NOT update_identity, which edits other people you know). Or skip this and use `generate_avatar` to generate + set your avatar in one step. Optional `model` picks the image model (default gpt-image-1). Generation is synchronous and may take up to ~30s; if it times out, retry or use a standard size.",
3288
3318
  parameters: Type.Object({
3289
3319
  prompt: Type.String({ description: "Text description of the image to generate." }),
3290
3320
  model: Type.Optional(Type.String({ description: "Image model id (default \"gpt-image-1\"). Others may be available, e.g. dall-e-3." })),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "OpenClaw plugin for Alfe — connects to local gateway daemon via IPC for integration management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",