@gobi-ai/cli 0.3.3 → 0.3.6
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/commands/brain.js +20 -5
- package/dist/commands/sessions.js +25 -36
- package/dist/commands/{astra.js → space.js} +23 -1
- package/dist/errors.js +6 -6
- package/dist/main.js +4 -4
- package/package.json +2 -1
package/dist/commands/brain.js
CHANGED
|
@@ -46,16 +46,31 @@ export function registerBrainCommand(program) {
|
|
|
46
46
|
.command("ask")
|
|
47
47
|
.description("Ask a brain a question. Creates a targeted session (1:1 conversation).")
|
|
48
48
|
.requiredOption("--vault-slug <vaultSlug>", "Slug of the brain/vault to ask")
|
|
49
|
-
.
|
|
50
|
-
.
|
|
49
|
+
.option("--question <question>", "The question to ask (markdown supported)")
|
|
50
|
+
.option("--rich-text <richText>", "Rich-text JSON array (e.g. [{\"type\":\"text\",\"text\":\"hello\"}])")
|
|
51
51
|
.option("--mode <mode>", 'Session mode: "auto" or "manual"')
|
|
52
52
|
.action(async (opts) => {
|
|
53
|
-
|
|
53
|
+
if (!opts.question && !opts.richText) {
|
|
54
|
+
throw new Error("Provide either --question or --rich-text.");
|
|
55
|
+
}
|
|
56
|
+
if (opts.question && opts.richText) {
|
|
57
|
+
throw new Error("--question and --rich-text are mutually exclusive.");
|
|
58
|
+
}
|
|
54
59
|
const body = {
|
|
55
60
|
vaultSlug: opts.vaultSlug,
|
|
56
|
-
spaceSlug,
|
|
57
|
-
question: opts.question,
|
|
58
61
|
};
|
|
62
|
+
if (opts.question != null)
|
|
63
|
+
body.question = opts.question;
|
|
64
|
+
if (opts.richText != null) {
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = JSON.parse(opts.richText);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
throw new Error("Invalid --rich-text JSON.");
|
|
71
|
+
}
|
|
72
|
+
body.richText = parsed;
|
|
73
|
+
}
|
|
59
74
|
if (opts.mode != null)
|
|
60
75
|
body.mode = opts.mode;
|
|
61
76
|
const resp = (await apiPost(`/session/targeted`, body));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { apiGet, apiPost
|
|
1
|
+
import { apiGet, apiPost } from "../client.js";
|
|
2
2
|
import { isJsonMode, jsonOut, unwrapResp } from "./utils.js";
|
|
3
3
|
export function registerSessionsCommand(program) {
|
|
4
4
|
const sessions = program
|
|
5
5
|
.command("session")
|
|
6
|
-
.description("Session commands (get, list, reply
|
|
6
|
+
.description("Session commands (get, list, reply).");
|
|
7
7
|
// ── Get ──
|
|
8
8
|
sessions
|
|
9
9
|
.command("get <sessionId>")
|
|
@@ -50,15 +50,12 @@ export function registerSessionsCommand(program) {
|
|
|
50
50
|
sessions
|
|
51
51
|
.command("list")
|
|
52
52
|
.description("List all sessions you are part of, sorted by most recent activity.")
|
|
53
|
-
.option("--space-slug <spaceSlug>", "Filter by space slug")
|
|
54
53
|
.option("--limit <number>", "Items per page", "20")
|
|
55
54
|
.option("--cursor <string>", "Pagination cursor from previous response")
|
|
56
55
|
.action(async (opts) => {
|
|
57
56
|
const query = { limit: parseInt(opts.limit, 10) };
|
|
58
57
|
if (opts.cursor)
|
|
59
58
|
query.cursor = opts.cursor;
|
|
60
|
-
if (opts.spaceSlug)
|
|
61
|
-
query.spaceSlug = opts.spaceSlug;
|
|
62
59
|
const resp = (await apiGet(`/session/my-sessions`, query));
|
|
63
60
|
const items = (resp.data || []);
|
|
64
61
|
const pagination = (resp.pagination || {});
|
|
@@ -95,11 +92,30 @@ export function registerSessionsCommand(program) {
|
|
|
95
92
|
sessions
|
|
96
93
|
.command("reply <sessionId>")
|
|
97
94
|
.description("Send a human reply to a session you are a member of.")
|
|
98
|
-
.
|
|
95
|
+
.option("--content <content>", "Reply content (markdown supported)")
|
|
96
|
+
.option("--rich-text <richText>", "Rich-text JSON array (e.g. [{\"type\":\"text\",\"text\":\"hello\"}])")
|
|
99
97
|
.action(async (sessionId, opts) => {
|
|
100
|
-
|
|
101
|
-
content
|
|
102
|
-
}
|
|
98
|
+
if (!opts.content && !opts.richText) {
|
|
99
|
+
throw new Error("Provide either --content or --rich-text.");
|
|
100
|
+
}
|
|
101
|
+
if (opts.content && opts.richText) {
|
|
102
|
+
throw new Error("--content and --rich-text are mutually exclusive.");
|
|
103
|
+
}
|
|
104
|
+
const body = {};
|
|
105
|
+
if (opts.richText != null) {
|
|
106
|
+
let parsed;
|
|
107
|
+
try {
|
|
108
|
+
parsed = JSON.parse(opts.richText);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new Error("Invalid --rich-text JSON.");
|
|
112
|
+
}
|
|
113
|
+
body.richText = parsed;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
body.content = opts.content;
|
|
117
|
+
}
|
|
118
|
+
const resp = (await apiPost(`/session/${sessionId}/reply`, body));
|
|
103
119
|
const msg = unwrapResp(resp);
|
|
104
120
|
if (isJsonMode(sessions)) {
|
|
105
121
|
jsonOut(msg);
|
|
@@ -110,31 +126,4 @@ export function registerSessionsCommand(program) {
|
|
|
110
126
|
` Source: ${msg.source}\n` +
|
|
111
127
|
` Created: ${msg.createdAt}`);
|
|
112
128
|
});
|
|
113
|
-
// ── Update ──
|
|
114
|
-
sessions
|
|
115
|
-
.command("update <sessionId>")
|
|
116
|
-
.description('Update a session. "auto" lets the AI respond automatically; "manual" requires human replies.')
|
|
117
|
-
.option("--mode <mode>", 'Session mode: "auto" or "manual"')
|
|
118
|
-
.action(async (sessionId, opts) => {
|
|
119
|
-
if (!opts.mode) {
|
|
120
|
-
throw new Error("Provide at least one option to update (e.g. --mode).");
|
|
121
|
-
}
|
|
122
|
-
const body = {};
|
|
123
|
-
if (opts.mode != null) {
|
|
124
|
-
if (opts.mode !== "auto" && opts.mode !== "manual") {
|
|
125
|
-
throw new Error('Invalid mode. Must be "auto" or "manual".');
|
|
126
|
-
}
|
|
127
|
-
body.mode = opts.mode;
|
|
128
|
-
}
|
|
129
|
-
const resp = (await apiPatch(`/session/${sessionId}`, body));
|
|
130
|
-
const data = unwrapResp(resp);
|
|
131
|
-
if (isJsonMode(sessions)) {
|
|
132
|
-
jsonOut(data);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
const session = (data.session || data);
|
|
136
|
-
console.log(`Session updated!\n` +
|
|
137
|
-
` ID: ${session.id}\n` +
|
|
138
|
-
` Mode: ${session.mode}`);
|
|
139
|
-
});
|
|
140
129
|
}
|
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
|
|
2
2
|
import { selectSpace, writeSpaceSetting } from "./init.js";
|
|
3
3
|
import { isJsonMode, jsonOut, resolveSpaceSlug, unwrapResp } from "./utils.js";
|
|
4
|
-
export function
|
|
4
|
+
export function registerSpaceCommand(program) {
|
|
5
5
|
const space = program
|
|
6
6
|
.command("space")
|
|
7
7
|
.description("Space commands (threads, replies).")
|
|
8
8
|
.option("--space-slug <slug>", "Space slug (overrides .gobi/settings.yaml)");
|
|
9
|
+
// ── List spaces ──
|
|
10
|
+
space
|
|
11
|
+
.command("list")
|
|
12
|
+
.description("List spaces you are a member of.")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
const resp = (await apiGet("/spaces"));
|
|
15
|
+
const items = (resp.data || []);
|
|
16
|
+
if (isJsonMode(space)) {
|
|
17
|
+
jsonOut(items);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!items.length) {
|
|
21
|
+
console.log("No spaces found.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const lines = [];
|
|
25
|
+
for (const s of items) {
|
|
26
|
+
const desc = s.description ? ` - ${s.description}` : "";
|
|
27
|
+
lines.push(`- [${s.slug}] ${s.name}${desc}`);
|
|
28
|
+
}
|
|
29
|
+
console.log(`Spaces (${items.length}):\n` + lines.join("\n"));
|
|
30
|
+
});
|
|
9
31
|
// ── Warp (space selection) ──
|
|
10
32
|
space
|
|
11
33
|
.command("warp")
|
package/dist/errors.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
export class
|
|
1
|
+
export class GobiError extends Error {
|
|
2
2
|
code;
|
|
3
3
|
constructor(message, code) {
|
|
4
4
|
super(message);
|
|
5
5
|
this.code = code;
|
|
6
|
-
this.name = "
|
|
6
|
+
this.name = "GobiError";
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
export class NotAuthenticatedError extends
|
|
9
|
+
export class NotAuthenticatedError extends GobiError {
|
|
10
10
|
constructor() {
|
|
11
11
|
super("Not authenticated. Use 'gobi auth login' to begin the login flow.", "NOT_AUTHENTICATED");
|
|
12
12
|
this.name = "NotAuthenticatedError";
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
export class TokenRefreshError extends
|
|
15
|
+
export class TokenRefreshError extends GobiError {
|
|
16
16
|
constructor(detail) {
|
|
17
17
|
super(`Failed to refresh access token: ${detail}. Please run 'gobi auth login' to re-authenticate.`, "TOKEN_REFRESH_FAILED");
|
|
18
18
|
this.name = "TokenRefreshError";
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
export class ApiError extends
|
|
21
|
+
export class ApiError extends GobiError {
|
|
22
22
|
status;
|
|
23
23
|
endpoint;
|
|
24
24
|
constructor(status, endpoint, body) {
|
|
@@ -37,7 +37,7 @@ export class ApiError extends AstraError {
|
|
|
37
37
|
this.name = "ApiError";
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
export class DeviceCodeError extends
|
|
40
|
+
export class DeviceCodeError extends GobiError {
|
|
41
41
|
constructor(detail) {
|
|
42
42
|
super(`Device code flow error: ${detail}`, "DEVICE_CODE_ERROR");
|
|
43
43
|
this.name = "DeviceCodeError";
|
package/dist/main.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire } from "module";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { initCredentials } from "./auth/manager.js";
|
|
4
|
-
import { ApiError,
|
|
4
|
+
import { ApiError, GobiError } from "./errors.js";
|
|
5
5
|
import { registerAuthCommand } from "./commands/auth.js";
|
|
6
6
|
import { registerInitCommand, printContext } from "./commands/init.js";
|
|
7
|
-
import {
|
|
7
|
+
import { registerSpaceCommand } from "./commands/space.js";
|
|
8
8
|
import { registerBrainCommand } from "./commands/brain.js";
|
|
9
9
|
import { registerSessionsCommand } from "./commands/sessions.js";
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
@@ -27,7 +27,7 @@ export async function cli() {
|
|
|
27
27
|
// Register all command groups
|
|
28
28
|
registerAuthCommand(program);
|
|
29
29
|
registerInitCommand(program);
|
|
30
|
-
|
|
30
|
+
registerSpaceCommand(program);
|
|
31
31
|
registerBrainCommand(program);
|
|
32
32
|
registerSessionsCommand(program);
|
|
33
33
|
// Propagate helpWidth to all subcommands
|
|
@@ -66,7 +66,7 @@ export async function cli() {
|
|
|
66
66
|
console.error(`Error: API error (HTTP ${err.status}): ${err.message}${hint}`);
|
|
67
67
|
process.exit(1);
|
|
68
68
|
}
|
|
69
|
-
else if (err instanceof
|
|
69
|
+
else if (err instanceof GobiError) {
|
|
70
70
|
if (isJson) {
|
|
71
71
|
console.log(JSON.stringify({ success: false, error: err.message }));
|
|
72
72
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobi-ai/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "CLI client for the Gobi collaborative knowledge platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"start": "node dist/index.js",
|
|
39
39
|
"test": "node --test dist/*.test.js",
|
|
40
40
|
"generate-skill-docs": "npm run build && npx tsx skills/gobi-cli/scripts/generate-docs.ts",
|
|
41
|
+
"prepare": "npm run build",
|
|
41
42
|
"prepublishOnly": "npm run build"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|