@gobi-ai/cli 0.6.0 → 0.6.2

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.
@@ -0,0 +1,45 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join, extname } from "path";
3
+ import { WEBDRIVE_BASE_URL } from "./constants.js";
4
+ export function extractWikiLinks(content) {
5
+ const seen = new Set();
6
+ const results = [];
7
+ for (const match of content.matchAll(/\[\[([^\]]+)\]\]/g)) {
8
+ const link = match[1].trim();
9
+ if (!seen.has(link)) {
10
+ seen.add(link);
11
+ results.push(link);
12
+ }
13
+ }
14
+ return results;
15
+ }
16
+ export async function uploadAttachments(vaultSlug, links, token) {
17
+ for (const link of links) {
18
+ let localPath = join(process.cwd(), link);
19
+ if (!existsSync(localPath)) {
20
+ if (!extname(link)) {
21
+ localPath = join(process.cwd(), link + ".md");
22
+ }
23
+ if (!existsSync(localPath)) {
24
+ console.warn(`Warning: Skipping [[${link}]]: not found locally`);
25
+ continue;
26
+ }
27
+ }
28
+ const filePath = extname(link) ? link : link + ".md";
29
+ console.log(`Uploading [[${link}]]...`);
30
+ const content = readFileSync(localPath);
31
+ const url = `${WEBDRIVE_BASE_URL}/api/v1/vaults/${vaultSlug}/files/${filePath}`;
32
+ const res = await fetch(url, {
33
+ method: "PUT",
34
+ headers: {
35
+ Authorization: `Bearer ${token}`,
36
+ "Content-Type": "application/octet-stream",
37
+ },
38
+ body: content,
39
+ });
40
+ if (!res.ok) {
41
+ throw new Error(`Failed to upload [[${link}]]: HTTP ${res.status}: ${(await res.text()) || "(no body)"}`);
42
+ }
43
+ console.log(`Uploaded [[${link}]]`);
44
+ }
45
+ }
@@ -5,6 +5,7 @@ import { WEBDRIVE_BASE_URL } from "../constants.js";
5
5
  import { getValidToken } from "../auth/manager.js";
6
6
  import { getVaultSlug } from "./init.js";
7
7
  import { isJsonMode, jsonOut, resolveVaultSlug, unwrapResp } from "./utils.js";
8
+ import { extractWikiLinks, uploadAttachments } from "../attachments.js";
8
9
  export function registerBrainCommand(program) {
9
10
  const brain = program
10
11
  .command("brain")
@@ -191,8 +192,14 @@ export function registerBrainCommand(program) {
191
192
  .option("--vault-slug <vaultSlug>", "Vault slug (overrides .gobi/settings.yaml)")
192
193
  .requiredOption("--title <title>", "Title of the update")
193
194
  .requiredOption("--content <content>", "Update content (markdown supported)")
195
+ .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting")
194
196
  .action(async (opts) => {
195
197
  const vaultSlug = resolveVaultSlug(opts);
198
+ if (opts.autoAttachments) {
199
+ const token = await getValidToken();
200
+ const links = extractWikiLinks(opts.content);
201
+ await uploadAttachments(vaultSlug, links, token);
202
+ }
196
203
  const resp = (await apiPost(`/brain-updates/vault/${vaultSlug}`, {
197
204
  title: opts.title,
198
205
  content: opts.content,
@@ -0,0 +1,77 @@
1
+ import { apiGet } from "../client.js";
2
+ import { isJsonMode, jsonOut } from "./utils.js";
3
+ export function registerSenseCommand(program) {
4
+ const sense = program
5
+ .command("sense")
6
+ .description("Sense commands (activities, transcriptions).");
7
+ // ── Activities ──
8
+ sense
9
+ .command("activities")
10
+ .description("Fetch activity records within a time range.")
11
+ .requiredOption("--start-time <iso>", "Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)")
12
+ .requiredOption("--end-time <iso>", "End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)")
13
+ .action(async (opts) => {
14
+ const params = {
15
+ startTime: opts.startTime,
16
+ endTime: opts.endTime,
17
+ };
18
+ const resp = (await apiGet("/app/activities", params));
19
+ const activities = (resp.activities || []);
20
+ const pagination = (resp.pagination || {});
21
+ const latestTimestamp = resp.latestTimestamp;
22
+ if (isJsonMode(sense)) {
23
+ jsonOut({ activities, pagination, latestTimestamp });
24
+ return;
25
+ }
26
+ if (!activities.length) {
27
+ console.log("No activities found.");
28
+ if (latestTimestamp)
29
+ console.log(`Latest data available: ${latestTimestamp}`);
30
+ return;
31
+ }
32
+ const lines = activities.map((a) => {
33
+ const endStr = a.end_time ? ` → ${a.end_time}` : "";
34
+ return `- [${a.device_id}] ${a.category}: ${a.details} (${a.start_time}${endStr})`;
35
+ });
36
+ console.log(`Activities (${activities.length} items):\n` + lines.join("\n"));
37
+ if (latestTimestamp)
38
+ console.log(`Latest data available: ${latestTimestamp}`);
39
+ });
40
+ // ── Transcriptions ──
41
+ sense
42
+ .command("transcriptions")
43
+ .description("Fetch transcription records within a time range.")
44
+ .requiredOption("--start-time <iso>", "Start of time range (ISO 8601 UTC, e.g. 2026-03-20T00:00:00Z)")
45
+ .requiredOption("--end-time <iso>", "End of time range (ISO 8601 UTC, e.g. 2026-03-20T23:59:59Z)")
46
+ .action(async (opts) => {
47
+ const params = {
48
+ startTime: opts.startTime,
49
+ endTime: opts.endTime,
50
+ };
51
+ const resp = (await apiGet("/app/transcriptions", params));
52
+ const transcriptions = (resp.transcriptions || []);
53
+ const pagination = (resp.pagination || {});
54
+ const latestTimestamp = resp.latestTimestamp;
55
+ if (isJsonMode(sense)) {
56
+ jsonOut({ transcriptions, pagination, latestTimestamp });
57
+ return;
58
+ }
59
+ if (!transcriptions.length) {
60
+ console.log("No transcriptions found.");
61
+ if (latestTimestamp)
62
+ console.log(`Latest data available: ${latestTimestamp}`);
63
+ return;
64
+ }
65
+ const lines = [];
66
+ for (const t of transcriptions) {
67
+ lines.push(`- [${t.device_id}] ${t.created_at}`);
68
+ const turns = (t.turns || []);
69
+ for (const turn of turns) {
70
+ lines.push(` Speaker ${turn.speaker} (${turn.timestamp}): ${turn.text}`);
71
+ }
72
+ }
73
+ console.log(`Transcriptions (${transcriptions.length} items):\n` + lines.join("\n"));
74
+ if (latestTimestamp)
75
+ console.log(`Latest data available: ${latestTimestamp}`);
76
+ });
77
+ }
@@ -1,7 +1,9 @@
1
1
  import { readFileSync } from "fs";
2
2
  import { apiGet, apiPost, apiPatch, apiDelete } from "../client.js";
3
3
  import { selectSpace, writeSpaceSetting } from "./init.js";
4
- import { isJsonMode, jsonOut, resolveSpaceSlug, unwrapResp } from "./utils.js";
4
+ import { isJsonMode, jsonOut, resolveSpaceSlug, resolveVaultSlug, unwrapResp } from "./utils.js";
5
+ import { extractWikiLinks, uploadAttachments } from "../attachments.js";
6
+ import { getValidToken } from "../auth/manager.js";
5
7
  function readContent(value) {
6
8
  if (value === "-")
7
9
  return readFileSync("/dev/stdin", "utf8");
@@ -144,11 +146,20 @@ export function registerSpaceCommand(program) {
144
146
  .description("Create a thread in a space.")
145
147
  .requiredOption("--title <title>", "Title of the thread")
146
148
  .requiredOption("--content <content>", "Thread content (markdown supported)")
149
+ .option("--auto-attachments", "Upload wiki-linked [[files]] to webdrive before posting")
150
+ .option("--vault-slug <vaultSlug>", "Vault slug for attachment uploads (overrides .gobi/settings.yaml)")
147
151
  .action(async (opts) => {
152
+ const content = readContent(opts.content);
153
+ if (opts.autoAttachments) {
154
+ const vaultSlug = resolveVaultSlug(opts);
155
+ const token = await getValidToken();
156
+ const links = extractWikiLinks(content);
157
+ await uploadAttachments(vaultSlug, links, token);
158
+ }
148
159
  const spaceSlug = resolveSpaceSlug(space);
149
160
  const resp = (await apiPost(`/spaces/${spaceSlug}/threads`, {
150
161
  title: opts.title,
151
- content: readContent(opts.content),
162
+ content,
152
163
  }));
153
164
  const thread = unwrapResp(resp);
154
165
  if (isJsonMode(space)) {
package/dist/main.js CHANGED
@@ -7,6 +7,7 @@ import { registerInitCommand, printContext } from "./commands/init.js";
7
7
  import { registerSpaceCommand } from "./commands/space.js";
8
8
  import { registerBrainCommand } from "./commands/brain.js";
9
9
  import { registerSessionsCommand } from "./commands/sessions.js";
10
+ import { registerSenseCommand } from "./commands/sense.js";
10
11
  const require = createRequire(import.meta.url);
11
12
  const { version } = require("../package.json");
12
13
  const SKIP_BANNER_COMMANDS = new Set(["auth", "init"]);
@@ -30,6 +31,7 @@ export async function cli() {
30
31
  registerSpaceCommand(program);
31
32
  registerBrainCommand(program);
32
33
  registerSessionsCommand(program);
34
+ registerSenseCommand(program);
33
35
  // Propagate helpWidth to all subcommands
34
36
  const helpWidth = process.stdout.columns || 200;
35
37
  for (const cmd of program.commands) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobi-ai/cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "CLI client for the Gobi collaborative knowledge platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,12 +10,12 @@ description: >-
10
10
  allowed-tools: Bash(gobi:*)
11
11
  metadata:
12
12
  author: gobi-ai
13
- version: "0.5.0"
13
+ version: "0.6.1"
14
14
  ---
15
15
 
16
16
  # gobi-cli
17
17
 
18
- A CLI client for the Gobi collaborative knowledge platform (v0.5.0).
18
+ A CLI client for the Gobi collaborative knowledge platform (v0.6.1).
19
19
 
20
20
  ## Prerequisites
21
21
 
@@ -97,6 +97,7 @@ Options:
97
97
  --vault-slug <vaultSlug> Vault slug (overrides .gobi/settings.yaml)
98
98
  --title <title> Title of the update
99
99
  --content <content> Update content (markdown supported)
100
+ --auto-attachments Upload wiki-linked [[files]] to webdrive before posting
100
101
  -h, --help display help for command
101
102
  ```
102
103
 
@@ -79,9 +79,11 @@ Usage: gobi space create-thread [options]
79
79
  Create a thread in a space.
80
80
 
81
81
  Options:
82
- --title <title> Title of the thread
83
- --content <content> Thread content (markdown supported)
84
- -h, --help display help for command
82
+ --title <title> Title of the thread
83
+ --content <content> Thread content (markdown supported)
84
+ --auto-attachments Upload wiki-linked [[files]] to webdrive before posting
85
+ --vault-slug <vaultSlug> Vault slug for attachment uploads (overrides .gobi/settings.yaml)
86
+ -h, --help display help for command
85
87
  ```
86
88
 
87
89
  ## edit-thread