@gobi-ai/cli 0.3.2 → 0.3.4

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.
@@ -28,13 +28,15 @@ export function registerAstraCommand(program) {
28
28
  .command("get-thread <threadId>")
29
29
  .description("Get a thread and its replies (paginated).")
30
30
  .option("--limit <number>", "Replies per page", "20")
31
- .option("--offset <number>", "Offset for reply pagination", "0")
31
+ .option("--cursor <string>", "Pagination cursor from previous response")
32
32
  .action(async (threadId, opts) => {
33
33
  const spaceSlug = resolveSpaceSlug(space);
34
- const resp = (await apiGet(`/spaces/${spaceSlug}/threads/${threadId}`, {
34
+ const params = {
35
35
  limit: parseInt(opts.limit, 10),
36
- offset: parseInt(opts.offset, 10),
37
- }));
36
+ };
37
+ if (opts.cursor)
38
+ params.cursor = opts.cursor;
39
+ const resp = (await apiGet(`/spaces/${spaceSlug}/threads/${threadId}`, params));
38
40
  const data = unwrapResp(resp);
39
41
  const pagination = (resp.pagination || {});
40
42
  if (isJsonMode(space)) {
@@ -43,9 +45,6 @@ export function registerAstraCommand(program) {
43
45
  }
44
46
  const thread = (data.thread || data);
45
47
  const replies = (data.items || []);
46
- const totalReplies = pagination.total ||
47
- thread.replyCount ||
48
- 0;
49
48
  const author = thread.author?.name ||
50
49
  `User ${thread.authorId}`;
51
50
  const replyLines = [];
@@ -62,8 +61,9 @@ export function registerAstraCommand(program) {
62
61
  "",
63
62
  thread.content,
64
63
  "",
65
- `Replies (${replies.length} of ${totalReplies}):`,
64
+ `Replies (${replies.length} items):`,
66
65
  ...replyLines,
66
+ ...(pagination.hasMore ? [` Next cursor: ${pagination.nextCursor}`] : []),
67
67
  ].join("\n");
68
68
  console.log(output);
69
69
  });
@@ -71,13 +71,15 @@ export function registerAstraCommand(program) {
71
71
  .command("list-threads")
72
72
  .description("List threads in a space (paginated).")
73
73
  .option("--limit <number>", "Items per page", "20")
74
- .option("--offset <number>", "Offset for pagination", "0")
74
+ .option("--cursor <string>", "Pagination cursor from previous response")
75
75
  .action(async (opts) => {
76
76
  const spaceSlug = resolveSpaceSlug(space);
77
- const resp = (await apiGet(`/spaces/${spaceSlug}/threads`, {
77
+ const params = {
78
78
  limit: parseInt(opts.limit, 10),
79
- offset: parseInt(opts.offset, 10),
80
- }));
79
+ };
80
+ if (opts.cursor)
81
+ params.cursor = opts.cursor;
82
+ const resp = (await apiGet(`/spaces/${spaceSlug}/threads`, params));
81
83
  if (isJsonMode(space)) {
82
84
  jsonOut({
83
85
  items: resp.data || [],
@@ -97,8 +99,8 @@ export function registerAstraCommand(program) {
97
99
  `User ${t.authorId}`;
98
100
  lines.push(`- [${t.id}] "${t.title}" by ${author} (${t.replyCount} replies, ${t.createdAt})`);
99
101
  }
100
- const total = pagination.total || items.length;
101
- console.log(`Threads (${items.length} of ${total}):\n` + lines.join("\n"));
102
+ const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
103
+ console.log(`Threads (${items.length} items):\n` + lines.join("\n") + footer);
102
104
  });
103
105
  space
104
106
  .command("create-thread")
@@ -47,15 +47,33 @@ export function registerBrainCommand(program) {
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
49
  .requiredOption("--space-slug <spaceSlug>", "Space slug where the brain belongs")
50
- .requiredOption("--question <question>", "The question to ask (markdown supported)")
50
+ .option("--question <question>", "The question to ask (markdown supported)")
51
+ .option("--rich-text <richText>", "Rich-text JSON array (e.g. [{\"type\":\"text\",\"text\":\"hello\"}])")
51
52
  .option("--mode <mode>", 'Session mode: "auto" or "manual"')
52
53
  .action(async (opts) => {
54
+ if (!opts.question && !opts.richText) {
55
+ throw new Error("Provide either --question or --rich-text.");
56
+ }
57
+ if (opts.question && opts.richText) {
58
+ throw new Error("--question and --rich-text are mutually exclusive.");
59
+ }
53
60
  const spaceSlug = opts.spaceSlug;
54
61
  const body = {
55
62
  vaultSlug: opts.vaultSlug,
56
63
  spaceSlug,
57
- question: opts.question,
58
64
  };
65
+ if (opts.question != null)
66
+ body.question = opts.question;
67
+ if (opts.richText != null) {
68
+ let parsed;
69
+ try {
70
+ parsed = JSON.parse(opts.richText);
71
+ }
72
+ catch {
73
+ throw new Error("Invalid --rich-text JSON.");
74
+ }
75
+ body.richText = parsed;
76
+ }
59
77
  if (opts.mode != null)
60
78
  body.mode = opts.mode;
61
79
  const resp = (await apiPost(`/session/targeted`, body));
@@ -126,21 +144,26 @@ export function registerBrainCommand(program) {
126
144
  // ── Updates (list, post, edit, delete) ──
127
145
  brain
128
146
  .command("list-updates")
129
- .description("List recent brain updates for a vault (paginated).")
147
+ .description("List recent brain updates. Without --space-slug, lists all updates for you. With --space-slug, lists updates for that space. Use --mine to show only updates by you.")
130
148
  .option("--vault-slug <vaultSlug>", "Vault slug (overrides .gobi/settings.yaml)")
149
+ .option("--space-slug <spaceSlug>", "List updates for a space")
131
150
  .option("--mine", "List only my own brain updates")
132
151
  .option("--limit <number>", "Items per page", "20")
133
- .option("--offset <number>", "Offset for pagination", "0")
152
+ .option("--cursor <string>", "Pagination cursor from previous response")
134
153
  .action(async (opts) => {
135
154
  const params = {
136
155
  limit: parseInt(opts.limit, 10),
137
- offset: parseInt(opts.offset, 10),
138
156
  };
157
+ if (opts.cursor)
158
+ params.cursor = opts.cursor;
139
159
  if (opts.mine)
140
160
  params.mine = true;
141
161
  if (opts.vaultSlug)
142
162
  params.vaultSlug = opts.vaultSlug;
143
- const resp = (await apiGet(`/brain-updates`, params));
163
+ const path = opts.spaceSlug
164
+ ? `/spaces/${opts.spaceSlug}/brain-updates`
165
+ : `/brain-updates`;
166
+ const resp = (await apiGet(path, params));
144
167
  if (isJsonMode(brain)) {
145
168
  jsonOut({
146
169
  items: resp.data || [],
@@ -162,8 +185,8 @@ export function registerBrainCommand(program) {
162
185
  "?";
163
186
  lines.push(`- [${u.id}] "${u.title}" by ${author} (vault: ${vaultSlug}, ${u.createdAt})`);
164
187
  }
165
- const total = pagination.total || items.length;
166
- console.log(`Brain updates (${items.length} of ${total}):\n` + lines.join("\n"));
188
+ const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
189
+ console.log(`Brain updates (${items.length} items):\n` + lines.join("\n") + footer);
167
190
  });
168
191
  brain
169
192
  .command("post-update")
@@ -1,20 +1,22 @@
1
- import { apiGet, apiPost, apiPatch } from "../client.js";
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, update).");
6
+ .description("Session commands (get, list, reply).");
7
7
  // ── Get ──
8
8
  sessions
9
9
  .command("get <sessionId>")
10
10
  .description("Get a session and its messages (paginated).")
11
11
  .option("--limit <number>", "Messages per page", "20")
12
- .option("--offset <number>", "Offset for message pagination", "0")
12
+ .option("--cursor <string>", "Pagination cursor from previous response")
13
13
  .action(async (sessionId, opts) => {
14
- const resp = (await apiGet(`/session/${sessionId}`, {
14
+ const params = {
15
15
  limit: parseInt(opts.limit, 10),
16
- offset: parseInt(opts.offset, 10),
17
- }));
16
+ };
17
+ if (opts.cursor)
18
+ params.cursor = opts.cursor;
19
+ const resp = (await apiGet(`/session/${sessionId}`, params));
18
20
  const data = unwrapResp(resp);
19
21
  if (isJsonMode(sessions)) {
20
22
  jsonOut(data);
@@ -23,7 +25,6 @@ export function registerSessionsCommand(program) {
23
25
  const session = (data.session || data);
24
26
  const messages = (data.messages || []);
25
27
  const pagination = (data.pagination || {});
26
- const totalMessages = pagination.total || messages.length;
27
28
  const msgLines = [];
28
29
  for (const m of messages) {
29
30
  const author = m.author?.name ||
@@ -39,8 +40,9 @@ export function registerSessionsCommand(program) {
39
40
  ` Mode: ${session.mode}`,
40
41
  ` Last activity: ${session.lastMessageAt}`,
41
42
  "",
42
- `Messages (${messages.length} of ${totalMessages}):`,
43
+ `Messages (${messages.length} items):`,
43
44
  ...msgLines,
45
+ ...(pagination.hasMore ? [` Next cursor: ${pagination.nextCursor}`] : []),
44
46
  ].join("\n");
45
47
  console.log(output);
46
48
  });
@@ -50,17 +52,16 @@ export function registerSessionsCommand(program) {
50
52
  .description("List all sessions you are part of, sorted by most recent activity.")
51
53
  .option("--space-slug <spaceSlug>", "Filter by space slug")
52
54
  .option("--limit <number>", "Items per page", "20")
53
- .option("--offset <number>", "Offset for pagination", "0")
55
+ .option("--cursor <string>", "Pagination cursor from previous response")
54
56
  .action(async (opts) => {
55
- const limit = parseInt(opts.limit, 10);
56
- const offset = parseInt(opts.offset, 10);
57
- const query = { limit, offset };
57
+ const query = { limit: parseInt(opts.limit, 10) };
58
+ if (opts.cursor)
59
+ query.cursor = opts.cursor;
58
60
  if (opts.spaceSlug)
59
61
  query.spaceSlug = opts.spaceSlug;
60
62
  const resp = (await apiGet(`/session/my-sessions`, query));
61
63
  const items = (resp.data || []);
62
64
  const pagination = (resp.pagination || {});
63
- const total = pagination.total ?? items.length;
64
65
  if (isJsonMode(sessions)) {
65
66
  jsonOut({
66
67
  items,
@@ -87,17 +88,37 @@ export function registerSessionsCommand(program) {
87
88
  }
88
89
  lines.push(`- [${s.id}] "${title}" (mode: ${s.mode}, last activity: ${s.lastMessageAt})${memberInfo}`);
89
90
  }
90
- console.log(`Sessions (${items.length} of ${total}):\n` + lines.join("\n"));
91
+ const footer = pagination.hasMore ? `\n Next cursor: ${pagination.nextCursor}` : "";
92
+ console.log(`Sessions (${items.length} items):\n` + lines.join("\n") + footer);
91
93
  });
92
94
  // ── Reply ──
93
95
  sessions
94
96
  .command("reply <sessionId>")
95
97
  .description("Send a human reply to a session you are a member of.")
96
- .requiredOption("--content <content>", "Reply content (markdown supported)")
98
+ .option("--content <content>", "Reply content (markdown supported)")
99
+ .option("--rich-text <richText>", "Rich-text JSON array (e.g. [{\"type\":\"text\",\"text\":\"hello\"}])")
97
100
  .action(async (sessionId, opts) => {
98
- const resp = (await apiPost(`/session/${sessionId}/reply`, {
99
- content: opts.content,
100
- }));
101
+ if (!opts.content && !opts.richText) {
102
+ throw new Error("Provide either --content or --rich-text.");
103
+ }
104
+ if (opts.content && opts.richText) {
105
+ throw new Error("--content and --rich-text are mutually exclusive.");
106
+ }
107
+ const body = {};
108
+ if (opts.richText != null) {
109
+ let parsed;
110
+ try {
111
+ parsed = JSON.parse(opts.richText);
112
+ }
113
+ catch {
114
+ throw new Error("Invalid --rich-text JSON.");
115
+ }
116
+ body.richText = parsed;
117
+ }
118
+ else {
119
+ body.content = opts.content;
120
+ }
121
+ const resp = (await apiPost(`/session/${sessionId}/reply`, body));
101
122
  const msg = unwrapResp(resp);
102
123
  if (isJsonMode(sessions)) {
103
124
  jsonOut(msg);
@@ -108,31 +129,4 @@ export function registerSessionsCommand(program) {
108
129
  ` Source: ${msg.source}\n` +
109
130
  ` Created: ${msg.createdAt}`);
110
131
  });
111
- // ── Update ──
112
- sessions
113
- .command("update <sessionId>")
114
- .description('Update a session. "auto" lets the AI respond automatically; "manual" requires human replies.')
115
- .option("--mode <mode>", 'Session mode: "auto" or "manual"')
116
- .action(async (sessionId, opts) => {
117
- if (!opts.mode) {
118
- throw new Error("Provide at least one option to update (e.g. --mode).");
119
- }
120
- const body = {};
121
- if (opts.mode != null) {
122
- if (opts.mode !== "auto" && opts.mode !== "manual") {
123
- throw new Error('Invalid mode. Must be "auto" or "manual".');
124
- }
125
- body.mode = opts.mode;
126
- }
127
- const resp = (await apiPatch(`/session/${sessionId}`, body));
128
- const data = unwrapResp(resp);
129
- if (isJsonMode(sessions)) {
130
- jsonOut(data);
131
- return;
132
- }
133
- const session = (data.session || data);
134
- console.log(`Session updated!\n` +
135
- ` ID: ${session.id}\n` +
136
- ` Mode: ${session.mode}`);
137
- });
138
132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",