@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.
- package/dist/attachments.js +45 -0
- package/dist/commands/brain.js +7 -0
- package/dist/commands/sense.js +77 -0
- package/dist/commands/space.js +13 -2
- package/dist/main.js +2 -0
- package/package.json +1 -1
- package/skills/gobi/SKILL.md +2 -2
- package/skills/gobi/references/brain.md +1 -0
- package/skills/gobi/references/space.md +5 -3
|
@@ -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
|
+
}
|
package/dist/commands/brain.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/commands/space.js
CHANGED
|
@@ -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
|
|
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
package/skills/gobi/SKILL.md
CHANGED
|
@@ -10,12 +10,12 @@ description: >-
|
|
|
10
10
|
allowed-tools: Bash(gobi:*)
|
|
11
11
|
metadata:
|
|
12
12
|
author: gobi-ai
|
|
13
|
-
version: "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.
|
|
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>
|
|
83
|
-
--content <content>
|
|
84
|
-
-
|
|
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
|