@neta-art/cohub-cli 1.2.0 → 1.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.
@@ -1,56 +1,117 @@
1
- import { resolveToken, saveToken, clearToken, tokenSource, getTokenPath } from "../auth.js";
1
+ import { authSource, loginWithDeviceFlow, readAuthSession, refreshAccessToken, requestDeviceCode, revokeAndClearAuthSession, verifyDeviceCode } from "../auth.js";
2
2
  import { createClient } from "../client.js";
3
3
  import { table, json as outJson, 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
7
- .command("login <token>")
8
- .description("Set auth token for the current environment")
9
- .action((token) => {
7
+ .command("login")
8
+ .description("Sign in with Logto device authorization")
9
+ .option("--request-code", "Request a device code without polling")
10
+ .option("--verify-code", "Exchange a previously requested device code")
11
+ .option("--json", "Output as JSON")
12
+ .action(async (opts, command) => {
13
+ const asJson = Boolean(opts.json || command.parent?.optsWithGlobals().json);
14
+ if (opts.requestCode && opts.verifyCode) {
15
+ return error("Conflicting options", "Use only one of --request-code or --verify-code");
16
+ }
10
17
  try {
11
- saveToken(token);
12
- ok(`Token saved to ${getTokenPath()}`);
18
+ if (opts.requestCode) {
19
+ const code = await requestDeviceCode();
20
+ if (asJson)
21
+ return outJson(code);
22
+ printDeviceCode(code);
23
+ return;
24
+ }
25
+ if (opts.verifyCode) {
26
+ await verifyDeviceCode();
27
+ return showSignedIn(asJson);
28
+ }
29
+ const sp = spinner();
30
+ sp.start("Starting login");
31
+ await loginWithDeviceFlow((code) => {
32
+ sp.stop("Login started");
33
+ printDeviceCode(code);
34
+ process.stderr.write(" Waiting for authorization...\n");
35
+ });
36
+ return showSignedIn(asJson);
13
37
  }
14
38
  catch (e) {
15
- return error(e instanceof Error ? e.message : String(e));
39
+ handleHttp(e);
16
40
  }
17
41
  });
18
42
  auth
19
43
  .command("whoami")
20
44
  .description("Show current user info")
21
45
  .option("--json", "Output as JSON")
22
- .action(async (opts) => {
23
- const token = resolveToken();
24
- if (!token)
25
- return error("Not authenticated", "Run 'cohub auth login <token>'");
26
- const client = createClient(token);
27
- const sp = spinner();
28
- sp.start("Fetching user info");
46
+ .action(async (opts, command) => {
47
+ const asJson = Boolean(opts.json || command.parent?.optsWithGlobals().json);
48
+ return showSignedIn(asJson);
49
+ });
50
+ auth
51
+ .command("refresh")
52
+ .description("Refresh the stored Logto access token")
53
+ .action(async () => {
29
54
  try {
30
- const user = await client.user.getMe();
31
- sp.stop("Done");
32
- if (opts.json)
33
- return outJson(user);
34
- const src = tokenSource();
35
- const u = user;
36
- console.log(` Auth source: ${src}\n`);
37
- table([u], [
38
- { key: "id", label: "ID" },
39
- { key: "name", label: "Name" },
40
- { key: "email", label: "Email" },
41
- { key: "created_at", label: "Created" },
42
- ]);
55
+ if (authSource() === "execution-token") {
56
+ return error("Cannot refresh COHUB_EXECUTION_TOKEN", "Run `cohub auth login` for long-lived Logto auth.");
57
+ }
58
+ await refreshAccessToken();
59
+ ok("Token refreshed");
43
60
  }
44
61
  catch (e) {
45
- sp.stop("Failed");
46
62
  handleHttp(e);
47
63
  }
48
64
  });
49
65
  auth
50
66
  .command("logout")
51
- .description("Clear stored token")
52
- .action(() => {
53
- clearToken();
54
- ok("Token cleared");
67
+ .description("Clear stored Logto session")
68
+ .action(async () => {
69
+ await revokeAndClearAuthSession();
70
+ if (process.env.COHUB_EXECUTION_TOKEN?.trim()) {
71
+ ok("Local session cleared. COHUB_EXECUTION_TOKEN is still set.");
72
+ }
73
+ else {
74
+ ok("Signed out");
75
+ }
55
76
  });
56
77
  }
78
+ async function showSignedIn(asJson) {
79
+ const source = authSource();
80
+ if (!source)
81
+ return error("Not authenticated", "Run `cohub auth login`.");
82
+ const client = createClient();
83
+ const sp = spinner();
84
+ sp.start("Fetching user info");
85
+ try {
86
+ const user = await client.user.getMe();
87
+ sp.stop("Done");
88
+ const session = readAuthSession();
89
+ const payload = {
90
+ source,
91
+ refreshable: source === "logto" && Boolean(session?.refreshToken),
92
+ user,
93
+ };
94
+ if (asJson)
95
+ return outJson(payload);
96
+ const u = user;
97
+ console.log(` Auth source: ${source}`);
98
+ console.log(` Token: ${payload.refreshable ? "refreshable" : "ephemeral"}\n`);
99
+ table([u], [
100
+ { key: "id", label: "ID" },
101
+ { key: "name", label: "Name" },
102
+ { key: "email", label: "Email" },
103
+ { key: "created_at", label: "Created" },
104
+ ]);
105
+ }
106
+ catch (e) {
107
+ sp.stop("Failed");
108
+ handleHttp(e);
109
+ }
110
+ }
111
+ function printDeviceCode(code) {
112
+ console.log("\nOpen this URL to sign in:\n");
113
+ console.log(` ${code.verificationUriComplete}\n`);
114
+ console.log("Or enter this code manually:\n");
115
+ console.log(` ${code.userCode}\n`);
116
+ console.log(`Code expires at ${new Date(code.expiresAt).toLocaleString()}.`);
117
+ }
@@ -1,18 +1,14 @@
1
- import { resolveToken } from "../auth.js";
2
1
  import { createClient } from "../client.js";
3
2
  import { table, json as outJson, ok, error, handleHttp } from "../output.js";
4
3
  export function registerChannels(program) {
5
- const cmd = program.command("channels").description("Channel management");
4
+ const cmd = program.command("channels", { hidden: true }).description("Channel integrations");
6
5
  cmd
7
6
  .command("ls")
8
7
  .alias("list")
9
8
  .description("List channels")
10
9
  .option("--json", "Output as JSON")
11
10
  .action(async (opts) => {
12
- const token = resolveToken();
13
- if (!token)
14
- return error("Not authenticated", "Run 'cohub auth login <token>'");
15
- const client = createClient(token);
11
+ const client = createClient();
16
12
  try {
17
13
  const items = await client.channels.list();
18
14
  if (opts.json)
@@ -38,9 +34,6 @@ export function registerChannels(program) {
38
34
  .option("--credentials <json>", "Credentials as JSON string")
39
35
  .option("--json", "Output as JSON")
40
36
  .action(async (opts) => {
41
- const token = resolveToken();
42
- if (!token)
43
- return error("Not authenticated");
44
37
  let credentials = {};
45
38
  if (opts.credentials) {
46
39
  try {
@@ -50,7 +43,7 @@ export function registerChannels(program) {
50
43
  return error("Invalid JSON", "--credentials must be valid JSON");
51
44
  }
52
45
  }
53
- const client = createClient(token);
46
+ const client = createClient();
54
47
  try {
55
48
  const result = await client.channels.create({
56
49
  provider: opts.provider,
@@ -69,10 +62,7 @@ export function registerChannels(program) {
69
62
  .command("delete <id>")
70
63
  .description("Delete a channel")
71
64
  .action(async (id) => {
72
- const token = resolveToken();
73
- if (!token)
74
- return error("Not authenticated");
75
- const client = createClient(token);
65
+ const client = createClient();
76
66
  try {
77
67
  await client.channels.delete(id);
78
68
  ok(`Channel deleted: ${id}`);
@@ -1,18 +1,14 @@
1
- import { resolveToken } from "../auth.js";
2
1
  import { createClient } from "../client.js";
3
- import { table, json as outJson, ok, error, handleHttp } from "../output.js";
2
+ import { table, json as outJson, ok, handleHttp } from "../output.js";
4
3
  export function registerCronJobs(program) {
5
- const cmd = program.command("cron-jobs").description("Cron job management");
4
+ const cmd = program.command("cron-jobs", { hidden: true }).description("Scheduled prompt jobs");
6
5
  cmd
7
6
  .command("ls [spaceId]")
8
7
  .alias("list")
9
8
  .description("List cron jobs")
10
9
  .option("--json", "Output as JSON")
11
10
  .action(async (spaceId, opts) => {
12
- const token = resolveToken();
13
- if (!token)
14
- return error("Not authenticated", "Run 'cohub auth login <token>'");
15
- const client = createClient(token);
11
+ const client = createClient();
16
12
  try {
17
13
  const result = await client.cronJobs.list(spaceId);
18
14
  if (opts.json)
@@ -35,10 +31,7 @@ export function registerCronJobs(program) {
35
31
  .command("delete <id>")
36
32
  .description("Delete a cron job")
37
33
  .action(async (id) => {
38
- const token = resolveToken();
39
- if (!token)
40
- return error("Not authenticated");
41
- const client = createClient(token);
34
+ const client = createClient();
42
35
  try {
43
36
  await client.cronJobs.delete(id);
44
37
  ok(`Cron job deleted: ${id}`);
@@ -51,11 +44,8 @@ export function registerCronJobs(program) {
51
44
  .command("toggle <id> <on|off>")
52
45
  .description("Enable or disable a cron job")
53
46
  .action(async (id, state) => {
54
- const token = resolveToken();
55
- if (!token)
56
- return error("Not authenticated");
57
47
  const enabled = state === "on";
58
- const client = createClient(token);
48
+ const client = createClient();
59
49
  try {
60
50
  await client.cronJobs.toggle(id, enabled);
61
51
  ok(`Cron job ${enabled ? "enabled" : "disabled"}: ${id}`);
@@ -69,10 +59,7 @@ export function registerCronJobs(program) {
69
59
  .description("List cron job runs")
70
60
  .option("--json", "Output as JSON")
71
61
  .action(async (id, opts) => {
72
- const token = resolveToken();
73
- if (!token)
74
- return error("Not authenticated");
75
- const client = createClient(token);
62
+ const client = createClient();
76
63
  try {
77
64
  const result = await client.cronJobs.runs(id);
78
65
  if (opts.json)
@@ -1,8 +1,7 @@
1
1
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
- import { basename, extname, join } from "node:path";
3
- import { resolveToken } from "../auth.js";
2
+ import { basename, dirname, extname, join } from "node:path";
4
3
  import { createClient } from "../client.js";
5
- import { table, json as outJson, error, handleHttp } from "../output.js";
4
+ import { json as outJson, ok, handleHttp } from "../output.js";
6
5
  const mimeByExt = {
7
6
  ".png": "image/png",
8
7
  ".jpg": "image/jpeg",
@@ -52,16 +51,25 @@ async function contentFromPathOrUrl(type, value) {
52
51
  return { type, source: { type: "base64", media_type, data: data.toString("base64") } };
53
52
  }
54
53
  async function saveOutputs(output, outputPath) {
55
- const files = output.filter((block) => block.type !== "text");
56
- if (files.length === 0)
57
- return;
54
+ const outputs = output.filter((block) => block.type === "text" || block.type === "image" || block.type === "video" || block.type === "audio");
55
+ if (outputs.length === 0)
56
+ return [];
58
57
  const info = await stat(outputPath).catch(() => null);
59
- const isDir = info?.isDirectory() ?? (!extname(outputPath) && files.length > 1);
60
- if (files.length > 1 && !isDir)
61
- throw new Error("--output must be a directory when generation returns multiple files");
58
+ const isDir = info?.isDirectory() ?? (!extname(outputPath) && outputs.length > 1);
59
+ if (outputs.length > 1 && !isDir)
60
+ throw new Error("--output must be a directory when generation returns multiple outputs");
62
61
  if (isDir)
63
62
  await mkdir(outputPath, { recursive: true });
64
- for (const [i, block] of files.entries()) {
63
+ else
64
+ await mkdir(dirname(outputPath), { recursive: true });
65
+ const savedPaths = [];
66
+ for (const [i, block] of outputs.entries()) {
67
+ if (block.type === "text") {
68
+ const target = isDir ? join(outputPath, `generation-${i + 1}.txt`) : outputPath;
69
+ await writeFile(target, block.text, "utf-8");
70
+ savedPaths.push(target);
71
+ continue;
72
+ }
65
73
  const source = block.source;
66
74
  const target = isDir ? join(outputPath, outputName(block.type, source.type === "url" ? source.url : undefined, i)) : outputPath;
67
75
  if (source.type === "url") {
@@ -69,11 +77,17 @@ async function saveOutputs(output, outputPath) {
69
77
  if (!response.ok)
70
78
  throw new Error(`Failed to download ${source.url}: HTTP ${response.status}`);
71
79
  await writeFile(target, Buffer.from(await response.arrayBuffer()));
80
+ savedPaths.push(target);
72
81
  }
73
82
  else if (source.type === "base64") {
74
83
  await writeFile(target, Buffer.from(source.data, "base64"));
84
+ savedPaths.push(target);
85
+ }
86
+ else {
87
+ throw new Error(`Cannot save space file output locally: ${source.space_id}:${source.path}`);
75
88
  }
76
89
  }
90
+ return savedPaths;
77
91
  }
78
92
  function outputName(type, url, index) {
79
93
  const fromUrl = url ? basename(new URL(url).pathname) : "";
@@ -97,61 +111,42 @@ function printGeneration(output) {
97
111
  export function registerGenerations(program) {
98
112
  program
99
113
  .command("generate")
100
- .description("Generate multimodal content")
114
+ .description("Generate multimodal outputs")
101
115
  .argument("<prompt>", "Prompt text")
102
- .requiredOption("--model <model>", "Generation model")
103
- .option("--image <path-or-url>", "Image input", collect, [])
104
- .option("--video <path-or-url>", "Video input", collect, [])
105
- .option("--audio <path-or-url>", "Audio input", collect, [])
106
- .option("--param <key=value>", "Generation parameter", collect, [])
107
- .option("--parameters <json>", "Generation parameters JSON")
108
- .option("--metadata <json>", "Metadata JSON")
109
- .option("--output <path>", "Save generated file output")
116
+ .requiredOption("--model <model>", "Multimodal model ID from `cohub models ls --model-type multimodal`")
117
+ .option("--image <path-or-url>", "Image input file path or URL; repeatable", collect, [])
118
+ .option("--video <path-or-url>", "Video input file path or URL; repeatable", collect, [])
119
+ .option("--audio <path-or-url>", "Audio input file path or URL; repeatable", collect, [])
120
+ .option("--param <key=value>", "Generation parameter; repeatable, values may be JSON/number/boolean", collect, [])
121
+ .option("--parameters <json>", "Generation parameters as a JSON object")
122
+ .option("--metadata <json>", "Metadata as a JSON object")
123
+ .option("--output <path>", "Save generated output to a file or directory")
110
124
  .option("--json", "Output as JSON")
125
+ .addHelpText("after", `
126
+
127
+ Examples:
128
+ cohub models ls --model-type multimodal
129
+ cohub generate "A calm lake at sunrise" --model <model> --output lake.png
130
+ cohub generate "Restyle this image" --model <model> --image input.png --param size=1024x1024
131
+ `)
111
132
  .action(async (prompt, opts) => {
112
- const token = resolveToken();
113
- if (!token)
114
- return error("Not authenticated", "Run 'cohub auth login <token>'");
115
133
  try {
116
134
  const content = [{ type: "text", text: prompt }];
117
135
  content.push(...await Promise.all(opts.image.map((value) => contentFromPathOrUrl("image", value))));
118
136
  content.push(...await Promise.all(opts.video.map((value) => contentFromPathOrUrl("video", value))));
119
137
  content.push(...await Promise.all(opts.audio.map((value) => contentFromPathOrUrl("audio", value))));
120
- const generation = await createClient(token).generations.create({
138
+ const generation = await createClient().generations.create({
121
139
  model: opts.model,
122
140
  content,
123
141
  parameters: parseParams(opts.param, opts.parameters),
124
142
  metadata: opts.metadata ? JSON.parse(opts.metadata) : undefined,
125
143
  });
126
- if (opts.output && generation.output)
127
- await saveOutputs(generation.output, opts.output);
144
+ const savedPaths = opts.output && generation.output ? await saveOutputs(generation.output, opts.output) : [];
128
145
  if (opts.json)
129
- return outJson(generation);
146
+ return outJson(savedPaths.length > 0 ? { ...generation, savedPaths } : generation);
130
147
  printGeneration(generation.output ?? []);
131
- }
132
- catch (e) {
133
- handleHttp(e);
134
- }
135
- });
136
- const cmd = program.command("generations").description("Generation model declarations");
137
- cmd
138
- .command("ls")
139
- .alias("list")
140
- .description("List generation declarations")
141
- .option("--json", "Output as JSON")
142
- .action(async (opts) => {
143
- const token = resolveToken();
144
- if (!token)
145
- return error("Not authenticated", "Run 'cohub auth login <token>'");
146
- try {
147
- const response = await createClient(token).generations.listDeclarations();
148
- if (opts.json)
149
- return outJson(response);
150
- table(response.declarations, [
151
- { key: "model", label: "Model" },
152
- { key: "title", label: "Title" },
153
- { key: "description", label: "Description" },
154
- ]);
148
+ if (savedPaths.length > 0)
149
+ ok(`Saved to ${savedPaths.join(", ")}`);
155
150
  }
156
151
  catch (e) {
157
152
  handleHttp(e);
@@ -1,19 +1,39 @@
1
- import { resolveToken } from "../auth.js";
2
1
  import { createClient } from "../client.js";
3
2
  import { table, json as outJson, error, handleHttp } from "../output.js";
4
3
  export function registerModels(program) {
5
- const cmd = program.command("models").description("Model management");
4
+ const cmd = program
5
+ .command("models")
6
+ .description("List available LLM and multimodal models")
7
+ .addHelpText("after", `
8
+
9
+ Examples:
10
+ cohub models ls
11
+ cohub models ls --model-type multimodal
12
+ cohub models ls --model-type multimodal --json
13
+ `);
6
14
  cmd
7
15
  .command("ls")
8
16
  .alias("list")
9
17
  .description("List available models")
18
+ .option("--model-type <type>", "Model type: llm | multimodal", "llm")
10
19
  .option("--json", "Output as JSON")
11
20
  .action(async (opts) => {
12
- const token = resolveToken();
13
- if (!token)
14
- return error("Not authenticated", "Run 'cohub auth login <token>'");
15
- const client = createClient(token);
21
+ const client = createClient();
16
22
  try {
23
+ if (opts.modelType === "multimodal") {
24
+ const response = await client.models.listMultimodal();
25
+ if (opts.json)
26
+ return outJson(response);
27
+ table(response.models, [
28
+ { key: "model", label: "Model" },
29
+ { key: "title", label: "Title" },
30
+ { key: "description", label: "Description" },
31
+ ]);
32
+ return;
33
+ }
34
+ if (opts.modelType && opts.modelType !== "llm") {
35
+ return error("Invalid model type", "Use --model-type llm or --model-type multimodal");
36
+ }
17
37
  const catalog = await client.models.list();
18
38
  if (opts.json)
19
39
  return outJson(catalog);
@@ -1,6 +1,5 @@
1
- import { resolveToken } from "../auth.js";
2
1
  import { createClient } from "../client.js";
3
- import { table, json as outJson, error, handleHttp } from "../output.js";
2
+ import { table, json as outJson, handleHttp } from "../output.js";
4
3
  export function registerPrompts(program) {
5
4
  const cmd = program.command("prompts").description("Prompt template management");
6
5
  cmd
@@ -10,10 +9,7 @@ export function registerPrompts(program) {
10
9
  .option("--space <id>", "Filter by space")
11
10
  .option("--json", "Output as JSON")
12
11
  .action(async (opts) => {
13
- const token = resolveToken();
14
- if (!token)
15
- return error("Not authenticated", "Run 'cohub auth login <token>'");
16
- const client = createClient(token);
12
+ const client = createClient();
17
13
  try {
18
14
  const result = await client.prompts.list({ spaceId: opts.space });
19
15
  if (opts.json)
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerSearch(program: Command): void;
@@ -0,0 +1,104 @@
1
+ import { createClient } from "../client.js";
2
+ import { table, json as outJson, error, handleHttp } from "../output.js";
3
+ const DEFAULT_LIMIT = 20;
4
+ const MAX_TITLE_LENGTH = 72;
5
+ const MAX_CONTEXT_LENGTH = 42;
6
+ const SEARCH_TYPES = new Set(["turn", "session", "space"]);
7
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
8
+ function clampLimit(value) {
9
+ const parsed = Number(value ?? DEFAULT_LIMIT);
10
+ if (!Number.isFinite(parsed))
11
+ return DEFAULT_LIMIT;
12
+ return Math.min(Math.max(Math.floor(parsed), 1), 50);
13
+ }
14
+ function truncate(value, maxLength) {
15
+ const text = (value ?? "").replace(/\s+/g, " ").trim();
16
+ if (text.length <= maxLength)
17
+ return text;
18
+ return `${text.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;
19
+ }
20
+ function contextFor(item) {
21
+ if (item.type === "space")
22
+ return item.spaceName ?? "";
23
+ if (item.type === "session")
24
+ return item.spaceName ?? "";
25
+ return item.sessionTitle || item.spaceName || "";
26
+ }
27
+ function parseTypes(value) {
28
+ const types = value
29
+ ?.split(",")
30
+ .map((type) => type.trim())
31
+ .filter(Boolean);
32
+ if (!types?.length)
33
+ return undefined;
34
+ const invalidType = types.find((type) => !SEARCH_TYPES.has(type));
35
+ if (invalidType)
36
+ throw new Error(`Invalid search type: ${invalidType}`);
37
+ return [...new Set(types)];
38
+ }
39
+ function parseSearchInput(opts) {
40
+ const types = parseTypes(opts.types);
41
+ const spaceId = opts.spaceId?.trim();
42
+ if (spaceId && !UUID_PATTERN.test(spaceId))
43
+ throw new Error("Invalid space id");
44
+ return { types, spaceId: spaceId || undefined };
45
+ }
46
+ function rowsFor(items) {
47
+ return items.map((item) => ({
48
+ type: item.type,
49
+ title: truncate(item.title || item.excerpt, MAX_TITLE_LENGTH),
50
+ context: truncate(contextFor(item), MAX_CONTEXT_LENGTH),
51
+ match: item.matchedField,
52
+ updated: item.updatedAt ? item.updatedAt.slice(0, 10) : "",
53
+ href: item.href,
54
+ }));
55
+ }
56
+ export function registerSearch(program) {
57
+ program
58
+ .command("search")
59
+ .description("Search spaces, chats, and turns")
60
+ .argument("<query>", "Search query")
61
+ .option("--limit <n>", "Maximum results, 1-50", String(DEFAULT_LIMIT))
62
+ .option("--types <types>", "Comma-separated result types: turn,session,space")
63
+ .option("--space-id <id>", "Limit search to a space")
64
+ .option("--json", "Output as JSON")
65
+ .addHelpText("after", `
66
+
67
+ Examples:
68
+ cohub search "release notes"
69
+ cohub search "failing tests" --limit 10
70
+ cohub search "bug" --types turn,session --space-id <spaceId>
71
+ cohub search "design review" --json
72
+ `)
73
+ .action(async (query, opts) => {
74
+ const client = createClient();
75
+ try {
76
+ const input = parseSearchInput(opts);
77
+ const result = await client.search.query({
78
+ q: query,
79
+ limit: clampLimit(opts.limit),
80
+ types: input.types,
81
+ spaceId: input.spaceId,
82
+ });
83
+ if (opts.json)
84
+ return outJson(result);
85
+ if (result.degraded) {
86
+ process.stderr.write(" Search is temporarily degraded; results may be incomplete.\n");
87
+ }
88
+ table(rowsFor(result.items), [
89
+ { key: "type", label: "Type" },
90
+ { key: "title", label: "Title" },
91
+ { key: "context", label: "Context" },
92
+ { key: "match", label: "Match" },
93
+ { key: "updated", label: "Updated" },
94
+ { key: "href", label: "Href" },
95
+ ]);
96
+ }
97
+ catch (e) {
98
+ if (e instanceof Error && (e.message === "Invalid space id" || e.message.startsWith("Invalid search type:"))) {
99
+ return error(e.message);
100
+ }
101
+ handleHttp(e);
102
+ }
103
+ });
104
+ }
@@ -1,6 +1,5 @@
1
- import { resolveToken } from "../auth.js";
2
1
  import { createClient } from "../client.js";
3
- import { table, json as outJson, ok, error, handleHttp } from "../output.js";
2
+ import { table, json as outJson, ok, handleHttp } from "../output.js";
4
3
  export function registerSessionAccess(program) {
5
4
  const cmd = program
6
5
  .command("session-access")
@@ -10,10 +9,7 @@ export function registerSessionAccess(program) {
10
9
  .description("Get session access policy")
11
10
  .option("--json", "Output as JSON")
12
11
  .action(async (id, opts) => {
13
- const token = resolveToken();
14
- if (!token)
15
- return error("Not authenticated", "Run 'cohub auth login <token>'");
16
- const client = createClient(token);
12
+ const client = createClient();
17
13
  try {
18
14
  const policy = await client.sessionAccess.get(id);
19
15
  if (opts.json)
@@ -33,10 +29,7 @@ export function registerSessionAccess(program) {
33
29
  .option("--anonymous <role>", "Anonymous role (host|builder|guest|null)")
34
30
  .option("--json", "Output as JSON")
35
31
  .action(async (id, opts) => {
36
- const token = resolveToken();
37
- if (!token)
38
- return error("Not authenticated");
39
- const client = createClient(token);
32
+ const client = createClient();
40
33
  try {
41
34
  const policy = await client.sessionAccess.set(id, {
42
35
  anonymous_user: (opts.anonymous ?? null),
@@ -57,10 +50,7 @@ export function registerSessionAccess(program) {
57
50
  .command("remove <id>")
58
51
  .description("Remove session access override")
59
52
  .action(async (id) => {
60
- const token = resolveToken();
61
- if (!token)
62
- return error("Not authenticated");
63
- const client = createClient(token);
53
+ const client = createClient();
64
54
  try {
65
55
  await client.sessionAccess.remove(id);
66
56
  ok(`Session access override removed: ${id}`);
@@ -1,2 +1,3 @@
1
1
  import type { Command } from "commander";
2
+ export declare function registerPrompt(program: Command): void;
2
3
  export declare function registerSpaces(program: Command): void;