@neta-art/cohub-cli 1.6.3 → 1.7.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/avatar.d.ts +15 -0
- package/dist/avatar.js +35 -0
- package/dist/commands/generations.js +1 -1
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/profile.js +23 -0
- package/dist/commands/session-access.js +3 -3
- package/dist/commands/spaces.js +26 -31
- package/dist/index.js +3 -0
- package/package.json +3 -2
package/dist/avatar.d.ts
ADDED
|
@@ -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
|
|
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,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,
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
console.log(`Session access override removed: ${id}`);
|
|
57
57
|
}
|
|
58
58
|
catch (e) {
|
|
59
59
|
handleHttp(e);
|
package/dist/commands/spaces.js
CHANGED
|
@@ -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;
|
|
@@ -124,21 +125,6 @@ async function uploadFiles(command, paths, opts) {
|
|
|
124
125
|
handleHttp(e);
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
|
-
async function confirmRestart(opts) {
|
|
128
|
-
if (opts.yes)
|
|
129
|
-
return;
|
|
130
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
131
|
-
return error("Confirmation required", "Pass --yes to restart the sandbox automatically.");
|
|
132
|
-
process.stdout.write("Changing mods restarts the sandbox and may interrupt running work. Continue? [y/N] ");
|
|
133
|
-
const chunks = [];
|
|
134
|
-
for await (const chunk of process.stdin) {
|
|
135
|
-
chunks.push(chunk);
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
const answer = Buffer.concat(chunks).toString().trim().toLowerCase();
|
|
139
|
-
if (answer !== "y" && answer !== "yes")
|
|
140
|
-
return error("Cancelled");
|
|
141
|
-
}
|
|
142
128
|
async function readPromptContent(words) {
|
|
143
129
|
let content = words.join(" ");
|
|
144
130
|
if (!content && !process.stdin.isTTY) {
|
|
@@ -295,6 +281,25 @@ export function registerSpaces(program) {
|
|
|
295
281
|
handleHttp(e);
|
|
296
282
|
}
|
|
297
283
|
});
|
|
284
|
+
// ── spaces avatar ──
|
|
285
|
+
spacesCmd
|
|
286
|
+
.command("avatar <path>")
|
|
287
|
+
.description("Upload the space avatar")
|
|
288
|
+
.option("--json", "Output as JSON")
|
|
289
|
+
.action(async (path, opts) => {
|
|
290
|
+
const spaceId = requireSpace(spacesCmd);
|
|
291
|
+
const client = createClient();
|
|
292
|
+
try {
|
|
293
|
+
const asset = await uploadAvatarAsset({ client, purpose: "space_avatar", spaceId, path });
|
|
294
|
+
const result = await client.space(spaceId).profile({ avatarUrl: asset.publicUrl });
|
|
295
|
+
if (opts.json)
|
|
296
|
+
return outJson({ ...result, asset });
|
|
297
|
+
ok("Space avatar updated");
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
handleHttp(e);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
298
303
|
// ── spaces config ──
|
|
299
304
|
spacesCmd
|
|
300
305
|
.command("config <id>")
|
|
@@ -398,7 +403,7 @@ function registerMods(spacesCmd) {
|
|
|
398
403
|
table(result.items, [
|
|
399
404
|
{ key: "id", label: "ID" },
|
|
400
405
|
{ key: "modSpaceName", label: "Name" },
|
|
401
|
-
{ key: "
|
|
406
|
+
{ key: "modSpaceId", label: "Space" },
|
|
402
407
|
{ key: "enabled", label: "On" },
|
|
403
408
|
]);
|
|
404
409
|
}
|
|
@@ -409,19 +414,15 @@ function registerMods(spacesCmd) {
|
|
|
409
414
|
modsCmd
|
|
410
415
|
.command("add <modSpaceId>")
|
|
411
416
|
.description("Add a mod")
|
|
412
|
-
.option("--name <name>", "Display name")
|
|
413
|
-
.option("--slug <slug>", "Mount slug")
|
|
414
|
-
.option("-y, --yes", "Confirm sandbox restart")
|
|
415
417
|
.option("--json", "Output as JSON")
|
|
416
418
|
.action(async (modSpaceId, opts) => {
|
|
417
|
-
await confirmRestart(opts);
|
|
418
419
|
const spaceId = requireSpace(spacesCmd);
|
|
419
420
|
const client = createClient();
|
|
420
421
|
try {
|
|
421
|
-
const result = await client.space(spaceId).mods.create({ modSpaceId
|
|
422
|
+
const result = await client.space(spaceId).mods.create({ modSpaceId });
|
|
422
423
|
if (opts.json)
|
|
423
424
|
return outJson(result);
|
|
424
|
-
ok(
|
|
425
|
+
ok("Mod added");
|
|
425
426
|
}
|
|
426
427
|
catch (e) {
|
|
427
428
|
handleHttp(e);
|
|
@@ -430,17 +431,15 @@ function registerMods(spacesCmd) {
|
|
|
430
431
|
modsCmd
|
|
431
432
|
.command("enable <modId>")
|
|
432
433
|
.description("Enable a mod")
|
|
433
|
-
.option("-y, --yes", "Confirm sandbox restart")
|
|
434
434
|
.option("--json", "Output as JSON")
|
|
435
435
|
.action(async (modId, opts) => {
|
|
436
|
-
await confirmRestart(opts);
|
|
437
436
|
const spaceId = requireSpace(spacesCmd);
|
|
438
437
|
const client = createClient();
|
|
439
438
|
try {
|
|
440
439
|
const result = await client.space(spaceId).mods.update(modId, { enabled: true });
|
|
441
440
|
if (opts.json)
|
|
442
441
|
return outJson(result);
|
|
443
|
-
ok("Mod enabled
|
|
442
|
+
ok("Mod enabled");
|
|
444
443
|
}
|
|
445
444
|
catch (e) {
|
|
446
445
|
handleHttp(e);
|
|
@@ -449,17 +448,15 @@ function registerMods(spacesCmd) {
|
|
|
449
448
|
modsCmd
|
|
450
449
|
.command("disable <modId>")
|
|
451
450
|
.description("Disable a mod")
|
|
452
|
-
.option("-y, --yes", "Confirm sandbox restart")
|
|
453
451
|
.option("--json", "Output as JSON")
|
|
454
452
|
.action(async (modId, opts) => {
|
|
455
|
-
await confirmRestart(opts);
|
|
456
453
|
const spaceId = requireSpace(spacesCmd);
|
|
457
454
|
const client = createClient();
|
|
458
455
|
try {
|
|
459
456
|
const result = await client.space(spaceId).mods.update(modId, { enabled: false });
|
|
460
457
|
if (opts.json)
|
|
461
458
|
return outJson(result);
|
|
462
|
-
ok("Mod disabled
|
|
459
|
+
ok("Mod disabled");
|
|
463
460
|
}
|
|
464
461
|
catch (e) {
|
|
465
462
|
handleHttp(e);
|
|
@@ -469,17 +466,15 @@ function registerMods(spacesCmd) {
|
|
|
469
466
|
.command("rm <modId>")
|
|
470
467
|
.alias("remove")
|
|
471
468
|
.description("Remove a mod")
|
|
472
|
-
.option("-y, --yes", "Confirm sandbox restart")
|
|
473
469
|
.option("--json", "Output as JSON")
|
|
474
470
|
.action(async (modId, opts) => {
|
|
475
|
-
await confirmRestart(opts);
|
|
476
471
|
const spaceId = requireSpace(spacesCmd);
|
|
477
472
|
const client = createClient();
|
|
478
473
|
try {
|
|
479
474
|
const result = await client.space(spaceId).mods.remove(modId);
|
|
480
475
|
if (opts.json)
|
|
481
476
|
return outJson(result);
|
|
482
|
-
ok("Mod removed
|
|
477
|
+
ok("Mod removed");
|
|
483
478
|
}
|
|
484
479
|
catch (e) {
|
|
485
480
|
handleHttp(e);
|
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.
|
|
3
|
+
"version": "1.7.0",
|
|
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
|
-
"
|
|
17
|
+
"sharp": "^0.34.5",
|
|
18
|
+
"@neta-art/cohub": "1.15.0"
|
|
18
19
|
},
|
|
19
20
|
"publishConfig": {
|
|
20
21
|
"access": "public"
|