@neta-art/cohub-cli 1.6.3 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ import type { CohubHttpClient, PublicAssetPurpose } from "@neta-art/cohub";
2
+ export declare function normalizeAvatarFile(path: string): Promise<Buffer>;
3
+ export declare function uploadAvatarAsset(input: {
4
+ client: CohubHttpClient;
5
+ purpose: PublicAssetPurpose;
6
+ path: string;
7
+ spaceId?: string;
8
+ }): Promise<{
9
+ purpose: PublicAssetPurpose;
10
+ objectKey: string;
11
+ publicUrl: string;
12
+ uploadMethod: "POST";
13
+ uploadUrl: string;
14
+ uploadFields: Record<string, string>;
15
+ }>;
package/dist/avatar.js ADDED
@@ -0,0 +1,35 @@
1
+ import sharp from "sharp";
2
+ const AVATAR_SIZE = 1024;
3
+ const AVATAR_QUALITY = 86;
4
+ export async function normalizeAvatarFile(path) {
5
+ return sharp(path)
6
+ .rotate()
7
+ .resize(AVATAR_SIZE, AVATAR_SIZE, { fit: "cover", position: "centre" })
8
+ .webp({ quality: AVATAR_QUALITY })
9
+ .toBuffer();
10
+ }
11
+ export async function uploadAvatarAsset(input) {
12
+ const body = await normalizeAvatarFile(input.path);
13
+ const plan = await input.client.publicAssets.createUpload({
14
+ purpose: input.purpose,
15
+ spaceId: input.spaceId,
16
+ file: {
17
+ size: body.byteLength,
18
+ mimeType: "image/webp",
19
+ },
20
+ });
21
+ const formData = new FormData();
22
+ for (const [key, value] of Object.entries(plan.asset.uploadFields)) {
23
+ formData.append(key, value);
24
+ }
25
+ formData.append("file", new Blob([new Uint8Array(body)], { type: "image/webp" }), "avatar.webp");
26
+ const response = await fetch(plan.asset.uploadUrl, {
27
+ method: plan.asset.uploadMethod,
28
+ body: formData,
29
+ });
30
+ if (!response.ok) {
31
+ const detail = await response.text().catch(() => "");
32
+ throw new Error(`Avatar upload failed: HTTP ${response.status}${detail ? ` — ${detail}` : ""}`);
33
+ }
34
+ return plan.asset;
35
+ }
@@ -91,7 +91,7 @@ async function saveOutputs(output, outputPath) {
91
91
  }
92
92
  function outputName(type, url, index) {
93
93
  const fromUrl = url ? basename(new URL(url).pathname) : "";
94
- if (fromUrl && fromUrl.includes("."))
94
+ if (fromUrl?.includes("."))
95
95
  return `generation-${index + 1}-${fromUrl}`;
96
96
  const ext = type === "video" ? "mp4" : type === "audio" ? "bin" : "png";
97
97
  return `generation-${index + 1}.${ext}`;
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerProfile(program: Command): void;
@@ -0,0 +1,23 @@
1
+ import { uploadAvatarAsset } from "../avatar.js";
2
+ import { createClient } from "../client.js";
3
+ import { json as outJson, ok, handleHttp } from "../output.js";
4
+ export function registerProfile(program) {
5
+ const profileCmd = program.command("profile").description("Manage your profile");
6
+ profileCmd
7
+ .command("avatar <path>")
8
+ .description("Upload your avatar")
9
+ .option("--json", "Output as JSON")
10
+ .action(async (path, opts) => {
11
+ const client = createClient();
12
+ try {
13
+ const asset = await uploadAvatarAsset({ client, purpose: "user_avatar", path });
14
+ const result = await client.user.updateProfile({ avatarUrl: asset.publicUrl });
15
+ if (opts.json)
16
+ return outJson({ ...result, asset });
17
+ ok("Avatar updated");
18
+ }
19
+ catch (e) {
20
+ handleHttp(e);
21
+ }
22
+ });
23
+ }
@@ -1,5 +1,5 @@
1
1
  import { createClient } from "../client.js";
2
- import { table, json as outJson, ok, handleHttp } from "../output.js";
2
+ import { table, json as outJson, handleHttp } from "../output.js";
3
3
  export function registerSessionAccess(program) {
4
4
  const cmd = program
5
5
  .command("session-access")
@@ -36,7 +36,7 @@ export function registerSessionAccess(program) {
36
36
  });
37
37
  if (opts.json)
38
38
  return outJson(policy);
39
- ok("Session access updated");
39
+ console.log("Session access updated");
40
40
  table([policy], [
41
41
  { key: "signed_in_user", label: "Signed-in" },
42
42
  { key: "anonymous_user", label: "Anonymous" },
@@ -53,7 +53,7 @@ export function registerSessionAccess(program) {
53
53
  const client = createClient();
54
54
  try {
55
55
  await client.sessionAccess.remove(id);
56
- ok(`Session access override removed: ${id}`);
56
+ console.log(`Session access override removed: ${id}`);
57
57
  }
58
58
  catch (e) {
59
59
  handleHttp(e);
@@ -3,6 +3,7 @@ import { createReadStream } from "node:fs";
3
3
  import { readdir, stat } from "node:fs/promises";
4
4
  import { basename, dirname, relative, resolve, sep } from "node:path";
5
5
  import { createClient } from "../client.js";
6
+ import { uploadAvatarAsset } from "../avatar.js";
6
7
  import { table, json as outJson, ok, error, handleHttp } from "../output.js";
7
8
  function requireSpace(program) {
8
9
  let current = program;
@@ -295,6 +296,25 @@ export function registerSpaces(program) {
295
296
  handleHttp(e);
296
297
  }
297
298
  });
299
+ // ── spaces avatar ──
300
+ spacesCmd
301
+ .command("avatar <path>")
302
+ .description("Upload the space avatar")
303
+ .option("--json", "Output as JSON")
304
+ .action(async (path, opts) => {
305
+ const spaceId = requireSpace(spacesCmd);
306
+ const client = createClient();
307
+ try {
308
+ const asset = await uploadAvatarAsset({ client, purpose: "space_avatar", spaceId, path });
309
+ const result = await client.space(spaceId).profile({ avatarUrl: asset.publicUrl });
310
+ if (opts.json)
311
+ return outJson({ ...result, asset });
312
+ ok("Space avatar updated");
313
+ }
314
+ catch (e) {
315
+ handleHttp(e);
316
+ }
317
+ });
298
318
  // ── spaces config ──
299
319
  spacesCmd
300
320
  .command("config <id>")
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { registerChannels } from "./commands/channels.js";
6
6
  import { registerCronJobs } from "./commands/cron-jobs.js";
7
7
  import { registerGenerations } from "./commands/generations.js";
8
8
  import { registerModels } from "./commands/models.js";
9
+ import { registerProfile } from "./commands/profile.js";
9
10
  import { registerSearch } from "./commands/search.js";
10
11
  import { registerPrompt, registerSpaces } from "./commands/spaces.js";
11
12
  import { registerTasks } from "./commands/tasks.js";
@@ -32,6 +33,7 @@ program
32
33
 
33
34
  Common commands:
34
35
  cohub auth login
36
+ cohub profile avatar ./avatar.png
35
37
  cohub spaces ls
36
38
  cohub -s <space-id> prompt "Fix the failing tests"
37
39
  cohub search "release notes"
@@ -46,6 +48,7 @@ Environment:
46
48
  ENV=dev Use the development Cohub environment
47
49
  `);
48
50
  registerAuth(program);
51
+ registerProfile(program);
49
52
  registerPrompt(program);
50
53
  registerSpaces(program);
51
54
  registerChannels(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "1.6.3",
3
+ "version": "1.7.1",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -14,7 +14,8 @@
14
14
  ],
15
15
  "dependencies": {
16
16
  "commander": "^13.1.0",
17
- "@neta-art/cohub": "1.14.0"
17
+ "sharp": "^0.34.5",
18
+ "@neta-art/cohub": "1.15.1"
18
19
  },
19
20
  "publishConfig": {
20
21
  "access": "public"