@neta-art/cohub-cli 1.0.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 ADDED
@@ -0,0 +1,15 @@
1
+ # @neta-art/cohub-cli
2
+
3
+ CLI for [Cohub](https://cohub.neta.art) — spaces, sessions, and agent collaboration.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @neta-art/cohub-cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ cohub --help
15
+ ```
package/bin/cohub.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/index.js";
package/dist/auth.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Resolve auth token with priority:
3
+ * 1. COHUB_EXECUTION_TOKEN environment variable
4
+ * 2. ~/.config/cohub/token file
5
+ */
6
+ export declare function resolveToken(): string | null;
7
+ export declare function saveToken(token: string): void;
8
+ export declare function clearToken(): void;
9
+ export declare function tokenSource(): "env" | "file" | null;
package/dist/auth.js ADDED
@@ -0,0 +1,35 @@
1
+ import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const TOKEN_DIR = join(homedir(), ".config", "cohub");
5
+ const TOKEN_PATH = join(TOKEN_DIR, "token");
6
+ /**
7
+ * Resolve auth token with priority:
8
+ * 1. COHUB_EXECUTION_TOKEN environment variable
9
+ * 2. ~/.config/cohub/token file
10
+ */
11
+ export function resolveToken() {
12
+ if (process.env.COHUB_EXECUTION_TOKEN) {
13
+ return process.env.COHUB_EXECUTION_TOKEN.trim();
14
+ }
15
+ if (existsSync(TOKEN_PATH)) {
16
+ return readFileSync(TOKEN_PATH, "utf-8").trim();
17
+ }
18
+ return null;
19
+ }
20
+ export function saveToken(token) {
21
+ mkdirSync(TOKEN_DIR, { recursive: true });
22
+ writeFileSync(TOKEN_PATH, token.trim());
23
+ }
24
+ export function clearToken() {
25
+ if (existsSync(TOKEN_PATH)) {
26
+ rmSync(TOKEN_PATH);
27
+ }
28
+ }
29
+ export function tokenSource() {
30
+ if (process.env.COHUB_EXECUTION_TOKEN)
31
+ return "env";
32
+ if (existsSync(TOKEN_PATH))
33
+ return "file";
34
+ return null;
35
+ }
@@ -0,0 +1,2 @@
1
+ import { CohubHttpClient } from "@neta-art/cohub/http";
2
+ export declare function createClient(token: string, baseUrl?: string): CohubHttpClient;
package/dist/client.js ADDED
@@ -0,0 +1,7 @@
1
+ import { CohubHttpClient } from "@neta-art/cohub/http";
2
+ export function createClient(token, baseUrl) {
3
+ return new CohubHttpClient({
4
+ baseUrl,
5
+ getAccessToken: () => token,
6
+ });
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerAuth(program: Command): void;
@@ -0,0 +1,51 @@
1
+ import { resolveToken, saveToken, clearToken, tokenSource } from "../auth.js";
2
+ import { createClient } from "../client.js";
3
+ import { table, json as outJson, ok, error, spinner, handleHttp } from "../output.js";
4
+ export function registerAuth(program) {
5
+ const auth = program.command("auth").description("Authentication management");
6
+ auth
7
+ .command("login <token>")
8
+ .description("Set auth token")
9
+ .action((token) => {
10
+ saveToken(token);
11
+ ok("Token saved to ~/.config/cohub/token");
12
+ });
13
+ auth
14
+ .command("whoami")
15
+ .description("Show current user info")
16
+ .option("--json", "Output as JSON")
17
+ .action(async (opts) => {
18
+ const token = resolveToken();
19
+ if (!token)
20
+ return error("Not authenticated", "Run 'cohub auth login <token>'");
21
+ const client = createClient(token);
22
+ const sp = spinner();
23
+ sp.start("Fetching user info");
24
+ try {
25
+ const user = await client.user.getMe();
26
+ sp.stop("Done");
27
+ if (opts.json)
28
+ return outJson(user);
29
+ const src = tokenSource();
30
+ const u = user;
31
+ console.log(` Auth source: ${src}\n`);
32
+ table([u], [
33
+ { key: "id", label: "ID" },
34
+ { key: "name", label: "Name" },
35
+ { key: "email", label: "Email" },
36
+ { key: "created_at", label: "Created" },
37
+ ]);
38
+ }
39
+ catch (e) {
40
+ sp.stop("Failed");
41
+ handleHttp(e);
42
+ }
43
+ });
44
+ auth
45
+ .command("logout")
46
+ .description("Clear stored token")
47
+ .action(() => {
48
+ clearToken();
49
+ ok("Token cleared");
50
+ });
51
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerChannels(program: Command): void;
@@ -0,0 +1,84 @@
1
+ import { resolveToken } from "../auth.js";
2
+ import { createClient } from "../client.js";
3
+ import { table, json as outJson, ok, error, handleHttp } from "../output.js";
4
+ export function registerChannels(program) {
5
+ const cmd = program.command("channels").description("Channel management");
6
+ cmd
7
+ .command("ls")
8
+ .alias("list")
9
+ .description("List channels")
10
+ .option("--json", "Output as JSON")
11
+ .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);
16
+ try {
17
+ const items = await client.channels.list();
18
+ if (opts.json)
19
+ return outJson(items);
20
+ if (items.length === 0)
21
+ return console.log(" (empty)");
22
+ table(items, [
23
+ { key: "id", label: "ID" },
24
+ { key: "provider", label: "Provider" },
25
+ { key: "name", label: "Name" },
26
+ { key: "status", label: "Status" },
27
+ ]);
28
+ }
29
+ catch (e) {
30
+ handleHttp(e);
31
+ }
32
+ });
33
+ cmd
34
+ .command("create")
35
+ .description("Create a channel")
36
+ .requiredOption("-p, --provider <provider>", "Channel provider")
37
+ .requiredOption("-n, --name <name>", "Channel name")
38
+ .option("--credentials <json>", "Credentials as JSON string")
39
+ .option("--json", "Output as JSON")
40
+ .action(async (opts) => {
41
+ const token = resolveToken();
42
+ if (!token)
43
+ return error("Not authenticated");
44
+ let credentials = {};
45
+ if (opts.credentials) {
46
+ try {
47
+ credentials = JSON.parse(opts.credentials);
48
+ }
49
+ catch {
50
+ return error("Invalid JSON", "--credentials must be valid JSON");
51
+ }
52
+ }
53
+ const client = createClient(token);
54
+ try {
55
+ const result = await client.channels.create({
56
+ provider: opts.provider,
57
+ name: opts.name,
58
+ credentials,
59
+ });
60
+ if (opts.json)
61
+ return outJson(result);
62
+ ok("Channel created");
63
+ }
64
+ catch (e) {
65
+ handleHttp(e);
66
+ }
67
+ });
68
+ cmd
69
+ .command("delete <id>")
70
+ .description("Delete a channel")
71
+ .action(async (id) => {
72
+ const token = resolveToken();
73
+ if (!token)
74
+ return error("Not authenticated");
75
+ const client = createClient(token);
76
+ try {
77
+ await client.channels.delete(id);
78
+ ok(`Channel deleted: ${id}`);
79
+ }
80
+ catch (e) {
81
+ handleHttp(e);
82
+ }
83
+ });
84
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerCronJobs(program: Command): void;
@@ -0,0 +1,136 @@
1
+ import { resolveToken } from "../auth.js";
2
+ import { createClient } from "../client.js";
3
+ import { table, json as outJson, ok, error, handleHttp } from "../output.js";
4
+ export function registerCronJobs(program) {
5
+ const cmd = program.command("cron-jobs").description("Cron job management");
6
+ cmd
7
+ .command("ls [spaceId]")
8
+ .alias("list")
9
+ .description("List cron jobs")
10
+ .option("--json", "Output as JSON")
11
+ .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);
16
+ try {
17
+ const result = await client.cronJobs.list(spaceId);
18
+ if (opts.json)
19
+ return outJson(result);
20
+ if (result.jobs.length === 0)
21
+ return console.log(" (empty)");
22
+ table(result.jobs, [
23
+ { key: "id", label: "ID" },
24
+ { key: "title", label: "Title" },
25
+ { key: "cronExpression", label: "Schedule" },
26
+ { key: "enabled", label: "Enabled" },
27
+ { key: "spaceId", label: "Space" },
28
+ ]);
29
+ }
30
+ catch (e) {
31
+ handleHttp(e);
32
+ }
33
+ });
34
+ cmd
35
+ .command("create")
36
+ .description("Create a cron job")
37
+ .requiredOption("-t, --title <title>", "Job title")
38
+ .requiredOption("--task-type <type>", "Task type")
39
+ .requiredOption("--cron <expression>", "Cron expression")
40
+ .option("--payload <json>", "Payload as JSON")
41
+ .option("--timezone <tz>", "Timezone", "UTC")
42
+ .option("--space <id>", "Space ID")
43
+ .option("--session <id>", "Session ID")
44
+ .option("--json", "Output as JSON")
45
+ .action(async (opts) => {
46
+ const token = resolveToken();
47
+ if (!token)
48
+ return error("Not authenticated");
49
+ let payload = {};
50
+ if (opts.payload) {
51
+ try {
52
+ payload = JSON.parse(opts.payload);
53
+ }
54
+ catch {
55
+ return error("Invalid JSON", "--payload must be valid JSON");
56
+ }
57
+ }
58
+ const client = createClient(token);
59
+ try {
60
+ const result = await client.cronJobs.create({
61
+ title: opts.title,
62
+ taskType: opts.taskType,
63
+ cronExpression: opts.cron,
64
+ payload,
65
+ timezone: opts.timezone,
66
+ spaceId: opts.space,
67
+ sessionId: opts.session,
68
+ });
69
+ if (opts.json)
70
+ return outJson(result);
71
+ ok(`Cron job created: ${result.id}`);
72
+ }
73
+ catch (e) {
74
+ handleHttp(e);
75
+ }
76
+ });
77
+ cmd
78
+ .command("delete <id>")
79
+ .description("Delete a cron job")
80
+ .action(async (id) => {
81
+ const token = resolveToken();
82
+ if (!token)
83
+ return error("Not authenticated");
84
+ const client = createClient(token);
85
+ try {
86
+ await client.cronJobs.delete(id);
87
+ ok(`Cron job deleted: ${id}`);
88
+ }
89
+ catch (e) {
90
+ handleHttp(e);
91
+ }
92
+ });
93
+ cmd
94
+ .command("toggle <id> <on|off>")
95
+ .description("Enable or disable a cron job")
96
+ .action(async (id, state) => {
97
+ const token = resolveToken();
98
+ if (!token)
99
+ return error("Not authenticated");
100
+ const enabled = state === "on";
101
+ const client = createClient(token);
102
+ try {
103
+ await client.cronJobs.toggle(id, enabled);
104
+ ok(`Cron job ${enabled ? "enabled" : "disabled"}: ${id}`);
105
+ }
106
+ catch (e) {
107
+ handleHttp(e);
108
+ }
109
+ });
110
+ cmd
111
+ .command("runs <id>")
112
+ .description("List cron job runs")
113
+ .option("--json", "Output as JSON")
114
+ .action(async (id, opts) => {
115
+ const token = resolveToken();
116
+ if (!token)
117
+ return error("Not authenticated");
118
+ const client = createClient(token);
119
+ try {
120
+ const result = await client.cronJobs.runs(id);
121
+ if (opts.json)
122
+ return outJson(result);
123
+ if (result.runs.length === 0)
124
+ return console.log(" (empty)");
125
+ table(result.runs, [
126
+ { key: "id", label: "ID" },
127
+ { key: "status", label: "Status" },
128
+ { key: "startedAt", label: "Started" },
129
+ { key: "finishedAt", label: "Finished" },
130
+ ]);
131
+ }
132
+ catch (e) {
133
+ handleHttp(e);
134
+ }
135
+ });
136
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerModels(program: Command): void;
@@ -0,0 +1,34 @@
1
+ import { resolveToken } from "../auth.js";
2
+ import { createClient } from "../client.js";
3
+ import { table, json as outJson, error, handleHttp } from "../output.js";
4
+ export function registerModels(program) {
5
+ const cmd = program.command("models").description("Model management");
6
+ cmd
7
+ .command("ls")
8
+ .alias("list")
9
+ .description("List available models")
10
+ .option("--json", "Output as JSON")
11
+ .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);
16
+ try {
17
+ const catalog = await client.models.list();
18
+ if (opts.json)
19
+ return outJson(catalog);
20
+ // catalog is Record<provider, ModelCatalogEntry[]>
21
+ for (const [provider, entries] of Object.entries(catalog)) {
22
+ console.log(`\n ${provider}`);
23
+ console.log(" " + "─".repeat(provider.length));
24
+ table(entries, [
25
+ { key: "id", label: "ID" },
26
+ { key: "provider", label: "Provider" },
27
+ ]);
28
+ }
29
+ }
30
+ catch (e) {
31
+ handleHttp(e);
32
+ }
33
+ });
34
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerPrompts(program: Command): void;
@@ -0,0 +1,33 @@
1
+ import { resolveToken } from "../auth.js";
2
+ import { createClient } from "../client.js";
3
+ import { table, json as outJson, error, handleHttp } from "../output.js";
4
+ export function registerPrompts(program) {
5
+ const cmd = program.command("prompts").description("Prompt template management");
6
+ cmd
7
+ .command("ls")
8
+ .alias("list")
9
+ .description("List prompt templates")
10
+ .option("--space <id>", "Filter by space")
11
+ .option("--json", "Output as JSON")
12
+ .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);
17
+ try {
18
+ const result = await client.prompts.list({ spaceId: opts.space });
19
+ if (opts.json)
20
+ return outJson(result);
21
+ if (result.prompts.length === 0)
22
+ return console.log(" (empty)");
23
+ table(result.prompts, [
24
+ { key: "name", label: "Name" },
25
+ { key: "category", label: "Category" },
26
+ { key: "description", label: "Description" },
27
+ ]);
28
+ }
29
+ catch (e) {
30
+ handleHttp(e);
31
+ }
32
+ });
33
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerSessionAccess(program: Command): void;
@@ -0,0 +1,72 @@
1
+ import { resolveToken } from "../auth.js";
2
+ import { createClient } from "../client.js";
3
+ import { table, json as outJson, ok, error, handleHttp } from "../output.js";
4
+ export function registerSessionAccess(program) {
5
+ const cmd = program
6
+ .command("session-access")
7
+ .description("Session-level access control");
8
+ cmd
9
+ .command("get <id>")
10
+ .description("Get session access policy")
11
+ .option("--json", "Output as JSON")
12
+ .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);
17
+ try {
18
+ const policy = await client.sessionAccess.get(id);
19
+ if (opts.json)
20
+ return outJson(policy);
21
+ table([policy], [
22
+ { key: "signed_in_user", label: "Signed-in" },
23
+ { key: "anonymous_user", label: "Anonymous" },
24
+ ]);
25
+ }
26
+ catch (e) {
27
+ handleHttp(e);
28
+ }
29
+ });
30
+ cmd
31
+ .command("set <id>")
32
+ .description("Set session anonymous access")
33
+ .option("--anonymous <role>", "Anonymous role (host|builder|guest|null)")
34
+ .option("--json", "Output as JSON")
35
+ .action(async (id, opts) => {
36
+ const token = resolveToken();
37
+ if (!token)
38
+ return error("Not authenticated");
39
+ const client = createClient(token);
40
+ try {
41
+ const policy = await client.sessionAccess.set(id, {
42
+ anonymous_user: (opts.anonymous ?? null),
43
+ });
44
+ if (opts.json)
45
+ return outJson(policy);
46
+ ok("Session access updated");
47
+ table([policy], [
48
+ { key: "signed_in_user", label: "Signed-in" },
49
+ { key: "anonymous_user", label: "Anonymous" },
50
+ ]);
51
+ }
52
+ catch (e) {
53
+ handleHttp(e);
54
+ }
55
+ });
56
+ cmd
57
+ .command("remove <id>")
58
+ .description("Remove session access override")
59
+ .action(async (id) => {
60
+ const token = resolveToken();
61
+ if (!token)
62
+ return error("Not authenticated");
63
+ const client = createClient(token);
64
+ try {
65
+ await client.sessionAccess.remove(id);
66
+ ok(`Session access override removed: ${id}`);
67
+ }
68
+ catch (e) {
69
+ handleHttp(e);
70
+ }
71
+ });
72
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerSpaces(program: Command): void;