grok-bot-cli 0.2.0 → 0.2.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/package.json +2 -2
- package/src/app-session.js +66 -2
- package/src/cli.js +8 -2
- package/src/store.js +2 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grok-bot-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "CLI for Grok Bot agents and groups: create, update, message, inspect threads, and automate cleanup",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,6 +43,6 @@
|
|
|
43
43
|
"LICENSE"
|
|
44
44
|
],
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@changesets/cli": "
|
|
46
|
+
"@changesets/cli": "3.0.1"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/src/app-session.js
CHANGED
|
@@ -6,6 +6,49 @@ import { join } from "node:path";
|
|
|
6
6
|
|
|
7
7
|
const SAFE_STORAGE_PREFIX = Buffer.from("v10");
|
|
8
8
|
|
|
9
|
+
export class GrokBotGatewaySessionError extends Error {
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "GrokBotGatewaySessionError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function encryptedPayload(wrapped) {
|
|
18
|
+
if (wrapped.version != null && wrapped.version !== 1 && wrapped.version !== 2) {
|
|
19
|
+
throw new GrokBotGatewaySessionError(
|
|
20
|
+
"UNSUPPORTED_VERSION",
|
|
21
|
+
`Unsupported Grok Bot gateway descriptor version ${wrapped.version}.`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
let encrypted;
|
|
25
|
+
if (wrapped.version === 2) {
|
|
26
|
+
const entries = Object.values(wrapped.entries ?? {});
|
|
27
|
+
if (entries.length === 0) {
|
|
28
|
+
throw new GrokBotGatewaySessionError(
|
|
29
|
+
"EMPTY_ENTRIES",
|
|
30
|
+
"Grok Bot gateway descriptor has no saved gateway entries.",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (entries.length > 1) {
|
|
34
|
+
throw new GrokBotGatewaySessionError(
|
|
35
|
+
"AMBIGUOUS_ENTRIES",
|
|
36
|
+
"Grok Bot gateway descriptor has multiple saved gateway entries and no active entry selection.",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
encrypted = entries[0]?.encrypted;
|
|
40
|
+
} else {
|
|
41
|
+
encrypted = wrapped.encrypted;
|
|
42
|
+
}
|
|
43
|
+
if (typeof encrypted !== "string" || !encrypted) {
|
|
44
|
+
throw new GrokBotGatewaySessionError(
|
|
45
|
+
"MISSING_ENCRYPTED_PAYLOAD",
|
|
46
|
+
"Grok Bot gateway descriptor is missing an encrypted payload.",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return encrypted;
|
|
50
|
+
}
|
|
51
|
+
|
|
9
52
|
export function decryptSafeStorageString(encryptedBase64, password) {
|
|
10
53
|
const encrypted = Buffer.from(encryptedBase64, "base64");
|
|
11
54
|
if (!encrypted.subarray(0, 3).equals(SAFE_STORAGE_PREFIX)) {
|
|
@@ -57,13 +100,17 @@ export function loadGrokBotGatewaySession({
|
|
|
57
100
|
if (!existsSync(path)) return null;
|
|
58
101
|
|
|
59
102
|
const wrapped = JSON.parse(readFileSync(path, "utf8"));
|
|
103
|
+
const encrypted = encryptedPayload(wrapped);
|
|
60
104
|
const clear = decryptSafeStorageString(
|
|
61
|
-
|
|
105
|
+
encrypted,
|
|
62
106
|
getKeychainPassword(),
|
|
63
107
|
);
|
|
64
108
|
const descriptor = JSON.parse(clear);
|
|
65
109
|
if (!descriptor.baseUrl || !descriptor.token) {
|
|
66
|
-
throw new
|
|
110
|
+
throw new GrokBotGatewaySessionError(
|
|
111
|
+
"INCOMPLETE_DESCRIPTOR",
|
|
112
|
+
"Decrypted Grok Bot gateway descriptor is incomplete.",
|
|
113
|
+
);
|
|
67
114
|
}
|
|
68
115
|
|
|
69
116
|
return {
|
|
@@ -72,3 +119,20 @@ export function loadGrokBotGatewaySession({
|
|
|
72
119
|
headers: descriptor.headers ?? {},
|
|
73
120
|
};
|
|
74
121
|
}
|
|
122
|
+
|
|
123
|
+
export function inspectGrokBotGatewaySession(options = {}) {
|
|
124
|
+
if (!hasGrokBotGatewaySession(options)) {
|
|
125
|
+
return { present: false, usable: false };
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
loadGrokBotGatewaySession(options);
|
|
129
|
+
return { present: true, usable: true };
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
present: true,
|
|
133
|
+
usable: false,
|
|
134
|
+
code: error instanceof GrokBotGatewaySessionError ? error.code : "UNUSABLE_SESSION",
|
|
135
|
+
error: error instanceof Error ? error.message : String(error),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { AVATAR_COLORS, AVATAR_SHAPES, MAX_GROUP_MEMBERS, StoreError, defaultCandidateRoots, looksLikeAgentsRoot, resolveAgentsRoot } from "./store.js";
|
|
3
3
|
import { hasGatewayAuth } from "./gateway.js";
|
|
4
4
|
import { openBackend } from "./commands.js";
|
|
5
|
+
import { inspectGrokBotGatewaySession } from "./app-session.js";
|
|
5
6
|
|
|
6
7
|
function print(value) {
|
|
7
8
|
if (typeof value === "string") process.stdout.write(value + "\n");
|
|
@@ -247,11 +248,16 @@ async function main(argv) {
|
|
|
247
248
|
if (!(err instanceof StoreError)) throw err;
|
|
248
249
|
}
|
|
249
250
|
const note = "Live roster is on the box. Prefer CURSOR_ACCESS_TOKEN then EnsureSandBox then POST gateway /api/*.";
|
|
250
|
-
const
|
|
251
|
+
const gatewayAuthPresent = hasGatewayAuth();
|
|
252
|
+
const grokBotAppSession = inspectGrokBotGatewaySession();
|
|
253
|
+
const payload = { resolved, found, candidates, gatewayAuthPresent, grokBotAppSession, note };
|
|
251
254
|
if (json) print(payload);
|
|
252
255
|
else {
|
|
253
256
|
print("resolved: " + (resolved ?? "(none)"));
|
|
254
|
-
print("gateway auth: " + (
|
|
257
|
+
print("gateway auth: " + (gatewayAuthPresent ? "present" : "no"));
|
|
258
|
+
if (grokBotAppSession.usable) print("Grok Bot app session: usable");
|
|
259
|
+
else if (grokBotAppSession.present) print("Grok Bot app session: present but unusable: " + grokBotAppSession.error);
|
|
260
|
+
else print("Grok Bot app session: not found");
|
|
255
261
|
print("found:");
|
|
256
262
|
print(found.length ? found.map((p) => " " + p).join("\n") : " (none)");
|
|
257
263
|
print("candidates:");
|
package/src/store.js
CHANGED
|
@@ -235,9 +235,8 @@ function writeGroup(dir, memberIds) {
|
|
|
235
235
|
});
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
function normalizeMemberIds(root, memberRefs
|
|
238
|
+
function normalizeMemberIds(root, memberRefs) {
|
|
239
239
|
const records = listRecords(root);
|
|
240
|
-
const byId = new Map(records.map((r) => [r.id, r]));
|
|
241
240
|
const groups = new Set(records.filter((r) => r.isGroup).map((r) => r.id));
|
|
242
241
|
const resolved = [];
|
|
243
242
|
const seen = new Set();
|
|
@@ -246,8 +245,6 @@ function normalizeMemberIds(root, memberRefs, { groupId } = {}) {
|
|
|
246
245
|
if (groups.has(rec.id)) {
|
|
247
246
|
throw new StoreError(`Cannot add group "${rec.name}" as a member. Nested groups are not allowed.`);
|
|
248
247
|
}
|
|
249
|
-
if (groupId && rec.id === groupId) continue;
|
|
250
|
-
if (!byId.has(rec.id)) continue;
|
|
251
248
|
if (seen.has(rec.id)) continue;
|
|
252
249
|
seen.add(rec.id);
|
|
253
250
|
resolved.push(rec.id);
|
|
@@ -341,7 +338,7 @@ export function createGroup(root, input) {
|
|
|
341
338
|
export function setGroupMembers(root, groupRef, memberRefs) {
|
|
342
339
|
const group = resolveRef(root, groupRef);
|
|
343
340
|
if (!group.isGroup) throw new StoreError(`"${group.name}" is a bot, not a group.`);
|
|
344
|
-
const memberIds = normalizeMemberIds(root, memberRefs
|
|
341
|
+
const memberIds = normalizeMemberIds(root, memberRefs);
|
|
345
342
|
if (memberIds.length === 0) {
|
|
346
343
|
throw new StoreError("A group needs at least one existing member agent.");
|
|
347
344
|
}
|