@neta-art/cohub-cli 1.7.0 → 1.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/README.md +2 -0
- package/dist/auth.d.ts +3 -1
- package/dist/auth.js +2 -2
- package/dist/commands/auth.js +68 -13
- package/dist/commands/channels.js +3 -3
- package/dist/commands/cron-jobs.js +5 -3
- package/dist/commands/generations.js +40 -10
- package/dist/commands/models.js +93 -5
- package/dist/commands/profile.js +2 -2
- package/dist/commands/prompts.js +2 -2
- package/dist/commands/search.js +2 -2
- package/dist/commands/session-access.js +12 -4
- package/dist/commands/spaces.js +144 -107
- package/dist/commands/tasks.js +3 -3
- package/dist/output.d.ts +3 -0
- package/dist/output.js +3 -0
- package/dist/space.d.ts +2 -0
- package/dist/space.js +14 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -129,6 +129,8 @@ cohub search "query" --limit 20 --json
|
|
|
129
129
|
```bash
|
|
130
130
|
cohub models ls --json
|
|
131
131
|
cohub models ls --model-type multimodal --json
|
|
132
|
+
cohub models show <model>
|
|
133
|
+
cohub models show <model> --json
|
|
132
134
|
|
|
133
135
|
cohub generate "a calm lake at sunrise" \
|
|
134
136
|
--model <model> \
|
package/dist/auth.d.ts
CHANGED
|
@@ -30,7 +30,9 @@ export declare class AuthRequiredError extends Error {
|
|
|
30
30
|
export declare const readAuthSession: () => AuthSession | null;
|
|
31
31
|
export declare const clearAuthSession: () => void;
|
|
32
32
|
export declare const authSource: () => AuthSource;
|
|
33
|
-
export declare function resolveAccessToken(
|
|
33
|
+
export declare function resolveAccessToken(options?: {
|
|
34
|
+
forceRefresh?: boolean;
|
|
35
|
+
}): Promise<string | null>;
|
|
34
36
|
export declare function requireAccessToken(): Promise<string>;
|
|
35
37
|
export declare function refreshAccessToken(session?: AuthSession | null): Promise<string | null>;
|
|
36
38
|
export declare function requestDeviceCode(): Promise<DeviceCode>;
|
package/dist/auth.js
CHANGED
|
@@ -125,14 +125,14 @@ export const authSource = () => {
|
|
|
125
125
|
return "logto";
|
|
126
126
|
return null;
|
|
127
127
|
};
|
|
128
|
-
export async function resolveAccessToken() {
|
|
128
|
+
export async function resolveAccessToken(options) {
|
|
129
129
|
const executionToken = process.env.COHUB_EXECUTION_TOKEN?.trim();
|
|
130
130
|
if (executionToken)
|
|
131
131
|
return executionToken;
|
|
132
132
|
const session = readAuthSession();
|
|
133
133
|
if (!session)
|
|
134
134
|
throw new AuthRequiredError();
|
|
135
|
-
if (session.accessTokenExpiresAt - Date.now() > EXPIRY_SKEW_MS)
|
|
135
|
+
if (!options?.forceRefresh && session.accessTokenExpiresAt - Date.now() > EXPIRY_SKEW_MS)
|
|
136
136
|
return session.accessToken;
|
|
137
137
|
return refreshAccessToken(session);
|
|
138
138
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { authSource, loginWithDeviceFlow, readAuthSession, refreshAccessToken, requestDeviceCode, revokeAndClearAuthSession, verifyDeviceCode } from "../auth.js";
|
|
2
2
|
import { createClient } from "../client.js";
|
|
3
|
-
import { table, json as outJson, ok, error, spinner, handleHttp } from "../output.js";
|
|
3
|
+
import { table, json as outJson, jsonRequested, ok, error, spinner, handleHttp } from "../output.js";
|
|
4
4
|
export function registerAuth(program) {
|
|
5
5
|
const auth = program.command("auth").description("Authentication management");
|
|
6
6
|
auth
|
|
@@ -9,8 +9,8 @@ export function registerAuth(program) {
|
|
|
9
9
|
.option("--request-code", "Request a device code without polling")
|
|
10
10
|
.option("--verify-code", "Exchange a previously requested device code")
|
|
11
11
|
.option("--json", "Output as JSON")
|
|
12
|
-
.action(async (opts
|
|
13
|
-
const asJson =
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
const asJson = jsonRequested(opts);
|
|
14
14
|
if (opts.requestCode && opts.verifyCode) {
|
|
15
15
|
return error("Conflicting options", "Use only one of --request-code or --verify-code");
|
|
16
16
|
}
|
|
@@ -43,8 +43,8 @@ export function registerAuth(program) {
|
|
|
43
43
|
.command("whoami")
|
|
44
44
|
.description("Show current user info")
|
|
45
45
|
.option("--json", "Output as JSON")
|
|
46
|
-
.action(async (opts
|
|
47
|
-
const asJson =
|
|
46
|
+
.action(async (opts) => {
|
|
47
|
+
const asJson = jsonRequested(opts);
|
|
48
48
|
return showSignedIn(asJson);
|
|
49
49
|
});
|
|
50
50
|
auth
|
|
@@ -80,11 +80,11 @@ async function showSignedIn(asJson) {
|
|
|
80
80
|
if (!source)
|
|
81
81
|
return error("Not authenticated", "Run `cohub auth login`.");
|
|
82
82
|
const client = createClient();
|
|
83
|
-
const sp = spinner();
|
|
84
|
-
sp
|
|
83
|
+
const sp = asJson ? null : spinner();
|
|
84
|
+
sp?.start("Fetching user info");
|
|
85
85
|
try {
|
|
86
86
|
const user = await client.user.getMe();
|
|
87
|
-
sp
|
|
87
|
+
sp?.stop("Done");
|
|
88
88
|
const session = readAuthSession();
|
|
89
89
|
const payload = {
|
|
90
90
|
source,
|
|
@@ -93,22 +93,77 @@ async function showSignedIn(asJson) {
|
|
|
93
93
|
};
|
|
94
94
|
if (asJson)
|
|
95
95
|
return outJson(payload);
|
|
96
|
-
const u = user;
|
|
96
|
+
const u = flattenMeForTable(user);
|
|
97
97
|
console.log(` Auth source: ${source}`);
|
|
98
98
|
console.log(` Token: ${payload.refreshable ? "refreshable" : "ephemeral"}\n`);
|
|
99
99
|
table([u], [
|
|
100
|
-
{ key: "
|
|
100
|
+
{ key: "uuid", label: "UUID" },
|
|
101
101
|
{ key: "username", label: "Username" },
|
|
102
|
-
{ key: "
|
|
102
|
+
{ key: "displayName", label: "Name" },
|
|
103
103
|
{ key: "email", label: "Email" },
|
|
104
|
-
{ key: "created_at", label: "Created" },
|
|
105
104
|
]);
|
|
106
105
|
}
|
|
107
106
|
catch (e) {
|
|
108
|
-
|
|
107
|
+
if (source === "execution-token") {
|
|
108
|
+
sp?.stop("Using local execution token");
|
|
109
|
+
return showExecutionTokenFallback(asJson);
|
|
110
|
+
}
|
|
111
|
+
sp?.stop("Failed");
|
|
109
112
|
handleHttp(e);
|
|
110
113
|
}
|
|
111
114
|
}
|
|
115
|
+
function flattenMeForTable(user) {
|
|
116
|
+
const profile = user.profile && typeof user.profile === "object" ? user.profile : {};
|
|
117
|
+
return {
|
|
118
|
+
uuid: user.uuid,
|
|
119
|
+
username: profile.username,
|
|
120
|
+
displayName: profile.displayName,
|
|
121
|
+
email: user.email,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function decodeExecutionTokenPayload() {
|
|
125
|
+
const token = process.env.COHUB_EXECUTION_TOKEN?.trim();
|
|
126
|
+
const payload = token?.split(".")[1];
|
|
127
|
+
if (!payload)
|
|
128
|
+
return null;
|
|
129
|
+
try {
|
|
130
|
+
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
|
|
131
|
+
const parsed = JSON.parse(decoded);
|
|
132
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function showExecutionTokenFallback(asJson) {
|
|
139
|
+
const payload = decodeExecutionTokenPayload();
|
|
140
|
+
const execution = {
|
|
141
|
+
actorUserId: typeof payload?.actorUserId === "string" ? payload.actorUserId : null,
|
|
142
|
+
spaceId: typeof payload?.spaceId === "string" ? payload.spaceId : null,
|
|
143
|
+
sessionId: typeof payload?.sessionId === "string" ? payload.sessionId : null,
|
|
144
|
+
source: typeof payload?.source === "string" ? payload.source : null,
|
|
145
|
+
expiresAt: typeof payload?.exp === "number" ? new Date(payload.exp * 1000).toISOString() : null,
|
|
146
|
+
};
|
|
147
|
+
const result = {
|
|
148
|
+
source: "execution-token",
|
|
149
|
+
refreshable: false,
|
|
150
|
+
user: execution.actorUserId ? { uuid: execution.actorUserId } : null,
|
|
151
|
+
execution,
|
|
152
|
+
};
|
|
153
|
+
if (asJson) {
|
|
154
|
+
outJson(result);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log(" Auth source: execution-token");
|
|
158
|
+
console.log(" Token: ephemeral\n");
|
|
159
|
+
table([execution], [
|
|
160
|
+
{ key: "actorUserId", label: "Actor" },
|
|
161
|
+
{ key: "spaceId", label: "Space" },
|
|
162
|
+
{ key: "sessionId", label: "Session" },
|
|
163
|
+
{ key: "source", label: "Source" },
|
|
164
|
+
{ key: "expiresAt", label: "Expires" },
|
|
165
|
+
]);
|
|
166
|
+
}
|
|
112
167
|
function printDeviceCode(code) {
|
|
113
168
|
console.log("\nOpen this URL to sign in:\n");
|
|
114
169
|
console.log(` ${code.verificationUriComplete}\n`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createClient } from "../client.js";
|
|
2
|
-
import { table, json as outJson, ok, error, handleHttp } from "../output.js";
|
|
2
|
+
import { table, json as outJson, jsonRequested, ok, error, handleHttp } from "../output.js";
|
|
3
3
|
export function registerChannels(program) {
|
|
4
4
|
const cmd = program.command("channels", { hidden: true }).description("Channel integrations");
|
|
5
5
|
cmd
|
|
@@ -11,7 +11,7 @@ export function registerChannels(program) {
|
|
|
11
11
|
const client = createClient();
|
|
12
12
|
try {
|
|
13
13
|
const items = await client.channels.list();
|
|
14
|
-
if (opts
|
|
14
|
+
if (jsonRequested(opts))
|
|
15
15
|
return outJson(items);
|
|
16
16
|
if (items.length === 0)
|
|
17
17
|
return console.log(" (empty)");
|
|
@@ -50,7 +50,7 @@ export function registerChannels(program) {
|
|
|
50
50
|
name: opts.name,
|
|
51
51
|
credentials,
|
|
52
52
|
});
|
|
53
|
-
if (opts
|
|
53
|
+
if (jsonRequested(opts))
|
|
54
54
|
return outJson(result);
|
|
55
55
|
ok("Channel created");
|
|
56
56
|
}
|
|
@@ -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, jsonRequested, ok, error, handleHttp } from "../output.js";
|
|
3
3
|
export function registerCronJobs(program) {
|
|
4
4
|
const cmd = program.command("cron-jobs", { hidden: true }).description("Scheduled prompt jobs");
|
|
5
5
|
cmd
|
|
@@ -11,7 +11,7 @@ export function registerCronJobs(program) {
|
|
|
11
11
|
const client = createClient();
|
|
12
12
|
try {
|
|
13
13
|
const result = await client.cronJobs.list(spaceId);
|
|
14
|
-
if (opts
|
|
14
|
+
if (jsonRequested(opts))
|
|
15
15
|
return outJson(result);
|
|
16
16
|
if (result.jobs.length === 0)
|
|
17
17
|
return console.log(" (empty)");
|
|
@@ -44,6 +44,8 @@ export function registerCronJobs(program) {
|
|
|
44
44
|
.command("toggle <id> <on|off>")
|
|
45
45
|
.description("Enable or disable a cron job")
|
|
46
46
|
.action(async (id, state) => {
|
|
47
|
+
if (state !== "on" && state !== "off")
|
|
48
|
+
return error("Invalid state", "Use on or off");
|
|
47
49
|
const enabled = state === "on";
|
|
48
50
|
const client = createClient();
|
|
49
51
|
try {
|
|
@@ -62,7 +64,7 @@ export function registerCronJobs(program) {
|
|
|
62
64
|
const client = createClient();
|
|
63
65
|
try {
|
|
64
66
|
const result = await client.cronJobs.runs(id);
|
|
65
|
-
if (opts
|
|
67
|
+
if (jsonRequested(opts))
|
|
66
68
|
return outJson(result);
|
|
67
69
|
if (result.runs.length === 0)
|
|
68
70
|
return console.log(" (empty)");
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, extname, join } from "node:path";
|
|
3
3
|
import { createClient } from "../client.js";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveSpace } from "../space.js";
|
|
5
|
+
import { json as outJson, jsonRequested, ok, error, handleHttp, spinner } from "../output.js";
|
|
5
6
|
const mimeByExt = {
|
|
6
7
|
".png": "image/png",
|
|
7
8
|
".jpg": "image/jpeg",
|
|
@@ -108,43 +109,72 @@ function printGeneration(output) {
|
|
|
108
109
|
console.log(`${block.type}: ${block.source.space_id}:${block.source.path}`);
|
|
109
110
|
}
|
|
110
111
|
}
|
|
112
|
+
function parseTimeoutMs(value) {
|
|
113
|
+
if (!value)
|
|
114
|
+
return undefined;
|
|
115
|
+
if (!/^\d+$/.test(value.trim()))
|
|
116
|
+
return error("Invalid timeout", "--timeout-ms must be a positive integer");
|
|
117
|
+
const timeoutMs = Number.parseInt(value, 10);
|
|
118
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0)
|
|
119
|
+
return error("Invalid timeout", "--timeout-ms must be a positive integer");
|
|
120
|
+
return timeoutMs;
|
|
121
|
+
}
|
|
111
122
|
export function registerGenerations(program) {
|
|
112
123
|
program
|
|
113
124
|
.command("generate")
|
|
114
125
|
.description("Generate multimodal outputs")
|
|
115
126
|
.argument("<prompt>", "Prompt text")
|
|
116
|
-
.requiredOption("--model <model>", "Multimodal model ID from `cohub models ls --model-type multimodal`")
|
|
127
|
+
.requiredOption("-m, --model <model>", "Multimodal model ID from `cohub models ls --model-type multimodal`")
|
|
117
128
|
.option("--image <path-or-url>", "Image input file path or URL; repeatable", collect, [])
|
|
118
129
|
.option("--video <path-or-url>", "Video input file path or URL; repeatable", collect, [])
|
|
119
130
|
.option("--audio <path-or-url>", "Audio input file path or URL; repeatable", collect, [])
|
|
120
131
|
.option("--param <key=value>", "Generation parameter; repeatable, values may be JSON/number/boolean", collect, [])
|
|
121
132
|
.option("--parameters <json>", "Generation parameters as a JSON object")
|
|
122
133
|
.option("--metadata <json>", "Metadata as a JSON object")
|
|
123
|
-
.option("--output <path>", "Save generated output to a file or directory")
|
|
134
|
+
.option("-o, --output <path>", "Save generated output to a file or directory")
|
|
135
|
+
.option("--async", "Queue the generation task and return immediately")
|
|
136
|
+
.option("--timeout-ms <ms>", "Maximum time to wait in synchronous mode")
|
|
124
137
|
.option("--json", "Output as JSON")
|
|
125
138
|
.addHelpText("after", `
|
|
126
139
|
|
|
127
140
|
Examples:
|
|
128
141
|
cohub models ls --model-type multimodal
|
|
129
|
-
cohub generate "A calm lake at sunrise"
|
|
130
|
-
cohub generate "Restyle this image"
|
|
142
|
+
cohub -s <space-id> generate "A calm lake at sunrise" -m <model> -o lake.png
|
|
143
|
+
COHUB_SPACE_ID=<space-id> cohub generate "Restyle this image" -m <model> --image input.png
|
|
144
|
+
cohub -s <space-id> generate "A calm lake" -m <model> --async
|
|
131
145
|
`)
|
|
132
146
|
.action(async (prompt, opts) => {
|
|
133
147
|
try {
|
|
148
|
+
const spaceId = resolveSpace(program);
|
|
134
149
|
const content = [{ type: "text", text: prompt }];
|
|
135
150
|
content.push(...await Promise.all(opts.image.map((value) => contentFromPathOrUrl("image", value))));
|
|
136
151
|
content.push(...await Promise.all(opts.video.map((value) => contentFromPathOrUrl("video", value))));
|
|
137
152
|
content.push(...await Promise.all(opts.audio.map((value) => contentFromPathOrUrl("audio", value))));
|
|
138
|
-
const
|
|
153
|
+
const client = createClient();
|
|
154
|
+
const created = await client.generations.create({
|
|
155
|
+
spaceId,
|
|
139
156
|
model: opts.model,
|
|
140
157
|
content,
|
|
141
158
|
parameters: parseParams(opts.param, opts.parameters),
|
|
142
159
|
metadata: opts.metadata ? JSON.parse(opts.metadata) : undefined,
|
|
143
160
|
});
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
161
|
+
if (opts.async) {
|
|
162
|
+
if (jsonRequested(opts))
|
|
163
|
+
return outJson(created);
|
|
164
|
+
return ok(`Generation queued — taskRunId: ${created.taskRunId}`);
|
|
165
|
+
}
|
|
166
|
+
const spin = spinner();
|
|
167
|
+
if (!jsonRequested(opts))
|
|
168
|
+
spin.start("Generating");
|
|
169
|
+
const result = await client.generations.wait(created.taskRunId, {
|
|
170
|
+
timeoutMs: parseTimeoutMs(opts.timeoutMs),
|
|
171
|
+
});
|
|
172
|
+
if (!jsonRequested(opts))
|
|
173
|
+
spin.stop("Generation completed");
|
|
174
|
+
const savedPaths = opts.output ? await saveOutputs(result.output, opts.output) : [];
|
|
175
|
+
if (jsonRequested(opts))
|
|
176
|
+
return outJson(savedPaths.length > 0 ? { ...result, taskRunId: created.taskRunId, savedPaths } : { ...result, taskRunId: created.taskRunId });
|
|
177
|
+
printGeneration(result.output);
|
|
148
178
|
if (savedPaths.length > 0)
|
|
149
179
|
ok(`Saved to ${savedPaths.join(", ")}`);
|
|
150
180
|
}
|
package/dist/commands/models.js
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
import { createClient } from "../client.js";
|
|
2
|
-
import { table, json as outJson, error, handleHttp } from "../output.js";
|
|
2
|
+
import { table, json as outJson, jsonRequested, error, handleHttp } from "../output.js";
|
|
3
|
+
function toMultimodalModelSummary(model) {
|
|
4
|
+
return {
|
|
5
|
+
model: model.model,
|
|
6
|
+
...(model.title ? { title: model.title } : {}),
|
|
7
|
+
...(model.description ? { description: model.description } : {}),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function printSection(title, lines) {
|
|
11
|
+
if (lines.length === 0)
|
|
12
|
+
return;
|
|
13
|
+
console.log(`\n${title}`);
|
|
14
|
+
for (const line of lines)
|
|
15
|
+
console.log(` ${line}`);
|
|
16
|
+
}
|
|
17
|
+
function formatContentSpec(spec) {
|
|
18
|
+
const details = [];
|
|
19
|
+
details.push(spec.required === false ? "optional" : "required");
|
|
20
|
+
if (typeof spec.min === "number")
|
|
21
|
+
details.push(`min ${spec.min}`);
|
|
22
|
+
if (typeof spec.max === "number")
|
|
23
|
+
details.push(`max ${spec.max}`);
|
|
24
|
+
if (spec.sources?.length)
|
|
25
|
+
details.push(`sources: ${spec.sources.join(", ")}`);
|
|
26
|
+
if (spec.merge)
|
|
27
|
+
details.push(`merge: ${spec.merge}`);
|
|
28
|
+
if (spec.description)
|
|
29
|
+
details.push(spec.description);
|
|
30
|
+
return `${spec.type}${details.length > 0 ? ` — ${details.join("; ")}` : ""}`;
|
|
31
|
+
}
|
|
32
|
+
function formatParameter(name, spec) {
|
|
33
|
+
const lines = [`${name}`];
|
|
34
|
+
const details = [`type: ${spec.type}`];
|
|
35
|
+
if (spec.optional)
|
|
36
|
+
details.push("optional");
|
|
37
|
+
if ("default" in spec && spec.default !== undefined)
|
|
38
|
+
details.push(`default: ${String(spec.default)}`);
|
|
39
|
+
if ("min" in spec && typeof spec.min === "number")
|
|
40
|
+
details.push(`min: ${spec.min}`);
|
|
41
|
+
if ("max" in spec && typeof spec.max === "number")
|
|
42
|
+
details.push(`max: ${spec.max}`);
|
|
43
|
+
if ("enum" in spec && spec.enum?.length)
|
|
44
|
+
details.push(`values: ${spec.enum.join(", ")}`);
|
|
45
|
+
lines.push(` ${details.join("; ")}`);
|
|
46
|
+
if (spec.description)
|
|
47
|
+
lines.push(` ${spec.description}`);
|
|
48
|
+
if ("examples" in spec && spec.examples?.length)
|
|
49
|
+
lines.push(` examples: ${spec.examples.map(String).join(", ")}`);
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
function printMultimodalModel(model) {
|
|
53
|
+
console.log(model.title ?? model.model);
|
|
54
|
+
printSection("Model", [model.model]);
|
|
55
|
+
if (model.description)
|
|
56
|
+
printSection("Description", [model.description]);
|
|
57
|
+
printSection("Input", model.content.input.map(formatContentSpec));
|
|
58
|
+
const parameterLines = Object.entries(model.parameters ?? {}).flatMap(([name, spec]) => formatParameter(name, spec));
|
|
59
|
+
printSection("Parameters", parameterLines);
|
|
60
|
+
const examples = model.examples ?? [];
|
|
61
|
+
printSection("Examples", examples.map((example, index) => {
|
|
62
|
+
const title = example.title ? `${example.title}: ` : "";
|
|
63
|
+
const prompt = example.request.content.find((block) => block.type === "text")?.text;
|
|
64
|
+
return `${index + 1}. ${title}${prompt ? `"${prompt}"` : example.request.model}`;
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
3
67
|
export function registerModels(program) {
|
|
4
68
|
const cmd = program
|
|
5
69
|
.command("models")
|
|
@@ -10,6 +74,8 @@ Examples:
|
|
|
10
74
|
cohub models ls
|
|
11
75
|
cohub models ls --model-type multimodal
|
|
12
76
|
cohub models ls --model-type multimodal --json
|
|
77
|
+
cohub models show <model>
|
|
78
|
+
cohub models show <model> --json
|
|
13
79
|
`);
|
|
14
80
|
cmd
|
|
15
81
|
.command("ls")
|
|
@@ -22,9 +88,10 @@ Examples:
|
|
|
22
88
|
try {
|
|
23
89
|
if (opts.modelType === "multimodal") {
|
|
24
90
|
const response = await client.models.listMultimodal();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
91
|
+
const models = response.models.map(toMultimodalModelSummary);
|
|
92
|
+
if (jsonRequested(opts))
|
|
93
|
+
return outJson({ models });
|
|
94
|
+
table(models, [
|
|
28
95
|
{ key: "model", label: "Model" },
|
|
29
96
|
{ key: "title", label: "Title" },
|
|
30
97
|
{ key: "description", label: "Description" },
|
|
@@ -35,7 +102,7 @@ Examples:
|
|
|
35
102
|
return error("Invalid model type", "Use --model-type llm or --model-type multimodal");
|
|
36
103
|
}
|
|
37
104
|
const catalog = await client.models.list();
|
|
38
|
-
if (opts
|
|
105
|
+
if (jsonRequested(opts))
|
|
39
106
|
return outJson(catalog);
|
|
40
107
|
// catalog is Record<provider, ModelCatalogEntry[]>
|
|
41
108
|
for (const [provider, entries] of Object.entries(catalog)) {
|
|
@@ -51,4 +118,25 @@ Examples:
|
|
|
51
118
|
handleHttp(e);
|
|
52
119
|
}
|
|
53
120
|
});
|
|
121
|
+
cmd
|
|
122
|
+
.command("show")
|
|
123
|
+
.description("Show full multimodal model details")
|
|
124
|
+
.argument("<model>", "Multimodal model ID")
|
|
125
|
+
.option("--json", "Output as JSON")
|
|
126
|
+
.action(async (modelId, opts) => {
|
|
127
|
+
const client = createClient();
|
|
128
|
+
try {
|
|
129
|
+
const response = await client.models.listMultimodal();
|
|
130
|
+
const model = response.models.find((item) => item.model === modelId);
|
|
131
|
+
if (!model) {
|
|
132
|
+
return error("Model not found", `No multimodal model named ${modelId}`);
|
|
133
|
+
}
|
|
134
|
+
if (jsonRequested(opts))
|
|
135
|
+
return outJson(model);
|
|
136
|
+
printMultimodalModel(model);
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
handleHttp(e);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
54
142
|
}
|
package/dist/commands/profile.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { uploadAvatarAsset } from "../avatar.js";
|
|
2
2
|
import { createClient } from "../client.js";
|
|
3
|
-
import { json as outJson, ok, handleHttp } from "../output.js";
|
|
3
|
+
import { json as outJson, jsonRequested, ok, handleHttp } from "../output.js";
|
|
4
4
|
export function registerProfile(program) {
|
|
5
5
|
const profileCmd = program.command("profile").description("Manage your profile");
|
|
6
6
|
profileCmd
|
|
@@ -12,7 +12,7 @@ export function registerProfile(program) {
|
|
|
12
12
|
try {
|
|
13
13
|
const asset = await uploadAvatarAsset({ client, purpose: "user_avatar", path });
|
|
14
14
|
const result = await client.user.updateProfile({ avatarUrl: asset.publicUrl });
|
|
15
|
-
if (opts
|
|
15
|
+
if (jsonRequested(opts))
|
|
16
16
|
return outJson({ ...result, asset });
|
|
17
17
|
ok("Avatar updated");
|
|
18
18
|
}
|
package/dist/commands/prompts.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createClient } from "../client.js";
|
|
2
|
-
import { table, json as outJson, handleHttp } from "../output.js";
|
|
2
|
+
import { table, json as outJson, jsonRequested, handleHttp } from "../output.js";
|
|
3
3
|
export function registerPrompts(program) {
|
|
4
4
|
const cmd = program.command("prompts").description("Prompt template management");
|
|
5
5
|
cmd
|
|
@@ -12,7 +12,7 @@ export function registerPrompts(program) {
|
|
|
12
12
|
const client = createClient();
|
|
13
13
|
try {
|
|
14
14
|
const result = await client.prompts.list({ spaceId: opts.space });
|
|
15
|
-
if (opts
|
|
15
|
+
if (jsonRequested(opts))
|
|
16
16
|
return outJson(result);
|
|
17
17
|
if (result.prompts.length === 0)
|
|
18
18
|
return console.log(" (empty)");
|
package/dist/commands/search.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createClient } from "../client.js";
|
|
2
|
-
import { table, json as outJson, error, handleHttp } from "../output.js";
|
|
2
|
+
import { table, json as outJson, jsonRequested, error, handleHttp } from "../output.js";
|
|
3
3
|
const DEFAULT_LIMIT = 20;
|
|
4
4
|
const MAX_TITLE_LENGTH = 72;
|
|
5
5
|
const MAX_CONTEXT_LENGTH = 42;
|
|
@@ -81,7 +81,7 @@ Examples:
|
|
|
81
81
|
types: input.types,
|
|
82
82
|
spaceId: input.spaceId,
|
|
83
83
|
});
|
|
84
|
-
if (opts
|
|
84
|
+
if (jsonRequested(opts))
|
|
85
85
|
return outJson(result);
|
|
86
86
|
if (result.degraded) {
|
|
87
87
|
process.stderr.write(" Search is temporarily degraded; results may be incomplete.\n");
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { createClient } from "../client.js";
|
|
2
|
-
import { table, json as outJson, handleHttp } from "../output.js";
|
|
2
|
+
import { table, json as outJson, jsonRequested, error, handleHttp } from "../output.js";
|
|
3
|
+
const SPACE_ROLES = ["host", "builder", "guest"];
|
|
4
|
+
function parseAnonymousRole(value) {
|
|
5
|
+
if (value === undefined || value === "null")
|
|
6
|
+
return null;
|
|
7
|
+
if (SPACE_ROLES.includes(value))
|
|
8
|
+
return value;
|
|
9
|
+
return error("Invalid anonymous role", "Use one of: host, builder, guest, null");
|
|
10
|
+
}
|
|
3
11
|
export function registerSessionAccess(program) {
|
|
4
12
|
const cmd = program
|
|
5
13
|
.command("session-access")
|
|
@@ -12,7 +20,7 @@ export function registerSessionAccess(program) {
|
|
|
12
20
|
const client = createClient();
|
|
13
21
|
try {
|
|
14
22
|
const policy = await client.sessionAccess.get(id);
|
|
15
|
-
if (opts
|
|
23
|
+
if (jsonRequested(opts))
|
|
16
24
|
return outJson(policy);
|
|
17
25
|
table([policy], [
|
|
18
26
|
{ key: "signed_in_user", label: "Signed-in" },
|
|
@@ -32,9 +40,9 @@ export function registerSessionAccess(program) {
|
|
|
32
40
|
const client = createClient();
|
|
33
41
|
try {
|
|
34
42
|
const policy = await client.sessionAccess.set(id, {
|
|
35
|
-
anonymous_user: (opts.anonymous
|
|
43
|
+
anonymous_user: parseAnonymousRole(opts.anonymous),
|
|
36
44
|
});
|
|
37
|
-
if (opts
|
|
45
|
+
if (jsonRequested(opts))
|
|
38
46
|
return outJson(policy);
|
|
39
47
|
console.log("Session access updated");
|
|
40
48
|
table([policy], [
|