grok-bot-cli 0.3.0 → 0.4.0

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +54 -22
  3. package/dist/.agents/plugins/marketplace.json +1 -0
  4. package/dist/.claude-plugin/marketplace.json +1 -0
  5. package/dist/.claude-plugin/plugin.json +1 -0
  6. package/dist/.codex-plugin/mcp.json +1 -0
  7. package/dist/.codex-plugin/plugin.json +1 -0
  8. package/dist/.cursor-plugin/marketplace.json +1 -0
  9. package/dist/.cursor-plugin/mcp.json +1 -0
  10. package/dist/.cursor-plugin/plugin.json +1 -0
  11. package/dist/.mcp.json +1 -0
  12. package/dist/INSTALL.md +269 -0
  13. package/dist/LICENSE +21 -0
  14. package/dist/README.md +173 -0
  15. package/dist/agent-bundle.compile-evidence.json +1 -0
  16. package/dist/agent-bundle.manifest.json +1 -0
  17. package/dist/agent-bundle.package-compile-evidence.json +1 -0
  18. package/dist/bin/gbot-flight.mjs +29162 -0
  19. package/dist/bin/gbot-install.js +135699 -0
  20. package/dist/bin/gbot.mjs +87242 -0
  21. package/dist/install.mjs +1322 -0
  22. package/dist/mcp/mcp-grok-bot-b8c2461e-flight.mjs +26413 -0
  23. package/dist/mcp/mcp-grok-bot-b8c2461e.mjs +107760 -0
  24. package/dist/mcp.json +1 -0
  25. package/dist/package.json +60 -0
  26. package/dist/plugin.json +1 -0
  27. package/dist/skills/talk-to-grok-bot/SKILL.md +37 -0
  28. package/package.json +30 -10
  29. package/src/app-session.js +0 -249
  30. package/src/cli.js +0 -548
  31. package/src/codex-bridge.js +0 -480
  32. package/src/commands.js +0 -52
  33. package/src/gateway.js +0 -361
  34. package/src/headers.js +0 -53
  35. package/src/history.js +0 -83
  36. package/src/store.js +0 -358
  37. package/src/transcript.js +0 -50
  38. package/src/url-policy.js +0 -161
package/dist/mcp.json ADDED
@@ -0,0 +1 @@
1
+ {"$schema":"https://agent-plugins.org/schemas/1.0.0/mcp.schema.json","mcpServers":{"grok-bot":{"args":["mcp/mcp-grok-bot-b8c2461e.mjs"],"command":"node","cwd":"${PLUGIN_ROOT}","env":{"AGENT_BUNDLE_PLUGIN_ROOT":"${PLUGIN_ROOT}"},"type":"stdio"}}}
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "grok-bot-cli",
3
+ "version": "0.4.0",
4
+ "description": "CLI and Agent Bundle plugin for Grok Bot agents and groups: create, update, message, inspect threads, and automate cleanup",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22.19.0"
8
+ },
9
+ "scripts": {
10
+ "build": "agent-bundle build",
11
+ "changeset": "changeset",
12
+ "check": "npm run validate && npm run build && npm run validate:artifact && npm run typecheck && npm test",
13
+ "gbot": "node dist/bin/gbot.mjs",
14
+ "release": "changeset publish",
15
+ "test": "npm run test:unit && npm run test:routes",
16
+ "test:routes": "rstest --config rstest.route-unit.config.ts",
17
+ "test:unit": "node scripts/run-unit-tests.mjs",
18
+ "typecheck": "tsc -p tsconfig.json --noEmit",
19
+ "validate": "agent-bundle validate",
20
+ "validate:artifact": "agent-bundle validate --artifact artifact"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/ScriptedAlchemy/grok-bot-cli.git"
25
+ },
26
+ "homepage": "https://github.com/ScriptedAlchemy/grok-bot-cli#readme",
27
+ "bugs": {
28
+ "url": "https://github.com/ScriptedAlchemy/grok-bot-cli/issues"
29
+ },
30
+ "author": "Zack Jackson (https://github.com/ScriptedAlchemy)",
31
+ "license": "MIT",
32
+ "keywords": [
33
+ "grok",
34
+ "grok-bot",
35
+ "grokbot",
36
+ "gbot",
37
+ "cursor",
38
+ "ai-agents",
39
+ "automation",
40
+ "terminal",
41
+ "cli",
42
+ "agent-bundle"
43
+ ],
44
+ "devDependencies": {
45
+ "@agent-bundle/runtime": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@8e55ab832d",
46
+ "@changesets/cli": "3.0.3",
47
+ "@rstest/core": "0.11.12",
48
+ "@types/node": "^22.18.0",
49
+ "@types/react": "^19.2.18",
50
+ "agent-bundle": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@8e55ab832d",
51
+ "react": "19.3.0",
52
+ "react-dom": "19.3.0",
53
+ "typescript": "7.0.2",
54
+ "zod": "4.6.5"
55
+ },
56
+ "bin": {
57
+ "gbot": "./bin/gbot.mjs",
58
+ "gbot-install": "./bin/gbot-install.js"
59
+ }
60
+ }
@@ -0,0 +1 @@
1
+ {"$schema":"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json","author":{"name":"Zack Jackson","url":"https://github.com/ScriptedAlchemy"},"description":"Message Grok Bot bots and groups and read their threads from Codex, Claude Code, and Cursor.","homepage":"https://github.com/ScriptedAlchemy/grok-bot-cli#readme","keywords":["grok","grok-bot","grokbot","gbot","cursor","ai-agents","automation","terminal","cli","agent-bundle"],"license":"MIT","name":"gbot","repository":"https://github.com/ScriptedAlchemy/grok-bot-cli","version":"0.4.0"}
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: talk-to-grok-bot
3
+ description: Send or read a Grok Bot thread via gbot_send/gbot_thread. Use when handing off to a named bot/group or posting a status note agents watch — not for work you can finish yourself.
4
+ ---
5
+ # Talk to Grok Bot
6
+
7
+ MCP tools on the `grok-bot` server use the same gateway as the `gbot` CLI.
8
+
9
+ ## When to load this
10
+
11
+ - The repo or task names a bot as owner, or you need a decision only that thread holds.
12
+ - You want a short status note in a shared group other agents watch.
13
+
14
+ Do not ping a bot for work you can finish yourself. Replies are asynchronous — never block a turn waiting.
15
+
16
+ ## How
17
+
18
+ 1. `gbot_send` with `target` (name or id) and `message` (first line: who you are + what you need).
19
+ 2. Later, call `gbot_thread` with the same `target` (`limit` defaults to 40). The default receipt has only `summary`, `cursor`, `entryCount`, and `gapReset`; it never includes entries.
20
+ 3. Poll with the previous `cursor` as `after`. This is exclusive and client-side: `entryCount: 0` means no change.
21
+ 4. Pass `full: true` only when entry bodies are needed inline; it adds bounded `entries` to structured content, not to `Agent.Text`. If `gapReset` is true, repeat the same call with `full: true` to inspect the bounded reset snapshot.
22
+
23
+ Bot replies are `send-message` entries; yours are `message` with `role: user`.
24
+
25
+ List targets with `gbot bots list` / `gbot groups list` when the name is ambiguous.
26
+
27
+ ## CLI automation
28
+
29
+ The bundled `gbot` and `gbot-install` executables require Node.js 22.19.0 or newer.
30
+ For `gbot send`, `gbot codex status`, and `gbot codex send` with `--json`, read the result
31
+ document from stdout and branch on its `exitCode`, `mode`, `reason`, and `delivery`.
32
+ Framework argument/schema errors use stderr and exit 2. `--json` is reserved before
33
+ `--`; put `--` before flag-like message text.
34
+
35
+ ## Auth
36
+
37
+ Same order as `gbot`: explicit `GROK_BOT_GATEWAY_*`, else Grok Bot app session, else `CURSOR_ACCESS_TOKEN`. `gbot doctor` shows which source is present.
package/package.json CHANGED
@@ -1,20 +1,29 @@
1
1
  {
2
2
  "name": "grok-bot-cli",
3
- "version": "0.3.0",
4
- "description": "CLI for Grok Bot agents and groups: create, update, message, inspect threads, and automate cleanup",
3
+ "version": "0.4.0",
4
+ "description": "CLI and Agent Bundle plugin for Grok Bot agents and groups: create, update, message, inspect threads, and automate cleanup",
5
5
  "type": "module",
6
6
  "bin": {
7
- "gbot": "src/cli.js",
8
- "grok-bot": "src/cli.js"
7
+ "gbot": "./dist/bin/gbot.mjs",
8
+ "gbot-install": "./dist/bin/gbot-install.js",
9
+ "grok-bot": "./dist/bin/gbot.mjs"
9
10
  },
10
11
  "engines": {
11
- "node": ">=18"
12
+ "node": ">=22.19.0"
12
13
  },
13
14
  "scripts": {
15
+ "build": "agent-bundle build",
14
16
  "changeset": "changeset",
15
- "gbot": "node src/cli.js",
17
+ "check": "npm run validate && npm run build && npm run validate:artifact && npm run typecheck && npm test",
18
+ "gbot": "node dist/bin/gbot.mjs",
19
+ "prepack": "agent-bundle prepack",
16
20
  "release": "changeset publish",
17
- "test": "node scripts/run-unit-tests.mjs"
21
+ "test": "npm run test:unit && npm run test:routes",
22
+ "test:routes": "rstest --config rstest.route-unit.config.ts",
23
+ "test:unit": "node scripts/run-unit-tests.mjs",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "validate": "agent-bundle validate",
26
+ "validate:artifact": "agent-bundle validate --artifact artifact"
18
27
  },
19
28
  "repository": {
20
29
  "type": "git",
@@ -35,14 +44,25 @@
35
44
  "ai-agents",
36
45
  "automation",
37
46
  "terminal",
38
- "cli"
47
+ "cli",
48
+ "agent-bundle"
39
49
  ],
40
50
  "files": [
41
- "src",
51
+ "dist",
42
52
  "README.md",
53
+ "CHANGELOG.md",
43
54
  "LICENSE"
44
55
  ],
45
56
  "devDependencies": {
46
- "@changesets/cli": "3.0.3"
57
+ "@agent-bundle/runtime": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@8e55ab832d",
58
+ "@changesets/cli": "3.0.3",
59
+ "@rstest/core": "0.11.12",
60
+ "@types/node": "^22.18.0",
61
+ "@types/react": "^19.2.18",
62
+ "agent-bundle": "https://pkg.pr.new/ScriptedAlchemy/agent-bundle/agent-bundle@8e55ab832d",
63
+ "react": "19.3.0",
64
+ "react-dom": "19.3.0",
65
+ "typescript": "7.0.2",
66
+ "zod": "4.6.5"
47
67
  }
48
68
  }
@@ -1,249 +0,0 @@
1
- import crypto from "node:crypto";
2
- import { execFileSync } from "node:child_process";
3
- import { existsSync, readFileSync } from "node:fs";
4
- import { homedir } from "node:os";
5
- import { join } from "node:path";
6
-
7
- // Chromium OSCrypt: "v10" = fixed password (macOS: the Keychain password; Linux: "peanuts"),
8
- // "v11" = Linux Secret Service password. macOS stretches with 1003 PBKDF2 rounds, Linux with 1.
9
- // Windows v10 uses AES-256-GCM with a DPAPI-wrapped key from Local State (not PBKDF2).
10
- const SAFE_STORAGE_PREFIX_V10 = "v10";
11
- const SAFE_STORAGE_PREFIX_V11 = "v11";
12
- const SAFE_STORAGE_PREFIX_V10_BUF = Buffer.from(SAFE_STORAGE_PREFIX_V10);
13
- const LINUX_BASIC_TEXT_PASSWORD = "peanuts";
14
- const SUPPORTED_PLATFORMS = new Set(["darwin", "linux", "win32"]);
15
-
16
- export class GrokBotGatewaySessionError extends Error {
17
- constructor(code, message) {
18
- super(message);
19
- this.name = "GrokBotGatewaySessionError";
20
- this.code = code;
21
- }
22
- }
23
-
24
- function encryptedPayload(wrapped) {
25
- if (wrapped.version != null && wrapped.version !== 1 && wrapped.version !== 2) {
26
- throw new GrokBotGatewaySessionError(
27
- "UNSUPPORTED_VERSION",
28
- `Unsupported Grok Bot gateway descriptor version ${wrapped.version}.`,
29
- );
30
- }
31
- let encrypted;
32
- if (wrapped.version === 2) {
33
- const entries = Object.values(wrapped.entries ?? {});
34
- if (entries.length === 0) {
35
- throw new GrokBotGatewaySessionError(
36
- "EMPTY_ENTRIES",
37
- "Grok Bot gateway descriptor has no saved gateway entries.",
38
- );
39
- }
40
- if (entries.length > 1) {
41
- throw new GrokBotGatewaySessionError(
42
- "AMBIGUOUS_ENTRIES",
43
- "Grok Bot gateway descriptor has multiple saved gateway entries and no active entry selection.",
44
- );
45
- }
46
- encrypted = entries[0]?.encrypted;
47
- } else {
48
- encrypted = wrapped.encrypted;
49
- }
50
- if (typeof encrypted !== "string" || !encrypted) {
51
- throw new GrokBotGatewaySessionError(
52
- "MISSING_ENCRYPTED_PAYLOAD",
53
- "Grok Bot gateway descriptor is missing an encrypted payload.",
54
- );
55
- }
56
- return encrypted;
57
- }
58
-
59
- function safeStorageKey(password, platform) {
60
- const iterations = platform === "linux" ? 1 : 1003;
61
- return crypto.pbkdf2Sync(password, "saltysalt", iterations, 16, "sha1");
62
- }
63
-
64
- export function decryptSafeStorageString(encryptedBase64, password, platform = "darwin") {
65
- const encrypted = Buffer.from(encryptedBase64, "base64");
66
- const prefix = encrypted.subarray(0, 3).toString("latin1");
67
- const linuxBasicText = platform === "linux" && prefix === SAFE_STORAGE_PREFIX_V10;
68
- const keyring = prefix === SAFE_STORAGE_PREFIX_V10 || (platform === "linux" && prefix === SAFE_STORAGE_PREFIX_V11);
69
- if (!keyring) {
70
- throw new Error("Unsupported Grok Bot Safe Storage format.");
71
- }
72
-
73
- const key = safeStorageKey(linuxBasicText ? LINUX_BASIC_TEXT_PASSWORD : password, platform);
74
- const decipher = crypto.createDecipheriv(
75
- "aes-128-cbc",
76
- key,
77
- Buffer.alloc(16, 32),
78
- );
79
- return Buffer.concat([
80
- decipher.update(encrypted.subarray(3)),
81
- decipher.final(),
82
- ]).toString("utf8");
83
- }
84
-
85
- // Windows Chromium Safe Storage: "v10" + 12-byte nonce + AES-256-GCM ciphertext + 16-byte tag.
86
- export function decryptWindowsSafeStorageString(encryptedBase64, key) {
87
- const encrypted = Buffer.from(encryptedBase64, "base64");
88
- if (!encrypted.subarray(0, 3).equals(SAFE_STORAGE_PREFIX_V10_BUF)) {
89
- throw new Error("Unsupported Grok Bot Safe Storage format.");
90
- }
91
-
92
- const decipher = crypto.createDecipheriv(
93
- "aes-256-gcm",
94
- key,
95
- encrypted.subarray(3, 15),
96
- );
97
- decipher.setAuthTag(encrypted.subarray(-16));
98
- return Buffer.concat([
99
- decipher.update(encrypted.subarray(15, -16)),
100
- decipher.final(),
101
- ]).toString("utf8");
102
- }
103
-
104
- function grokBotAppDataPath(home, platform, env = {}) {
105
- if (platform === "win32") {
106
- const appData = env.APPDATA || join(home, "AppData/Roaming");
107
- return join(appData, "Grok Bot");
108
- }
109
- if (platform === "linux") {
110
- const configHome = env.XDG_CONFIG_HOME || join(home, ".config");
111
- return join(configHome, "Grok Bot");
112
- }
113
- return join(home, "Library/Application Support/Grok Bot");
114
- }
115
-
116
- export function grokBotGatewayDescriptorPath(home = homedir(), platform = process.platform, env = process.env) {
117
- return join(grokBotAppDataPath(home, platform, env), "gateway-descriptor.json");
118
- }
119
-
120
- function sessionEnv({ env = process.env, appData } = {}) {
121
- // Windows tests pass appData directly; empty string clears APPDATA to exercise the home fallback.
122
- if (appData !== undefined) return { ...env, APPDATA: appData };
123
- return env;
124
- }
125
-
126
- export function hasGrokBotGatewaySession({
127
- platform = process.platform,
128
- home = homedir(),
129
- env = process.env,
130
- appData,
131
- } = {}) {
132
- return (
133
- SUPPORTED_PLATFORMS.has(platform) &&
134
- existsSync(grokBotGatewayDescriptorPath(home, platform, sessionEnv({ env, appData })))
135
- );
136
- }
137
-
138
- function readKeychainPassword(platform = process.platform) {
139
- if (platform === "linux") {
140
- return execFileSync(
141
- "secret-tool",
142
- ["lookup", "xdg:schema", "chrome_libsecret_os_crypt_password_v2", "application", "Grok Bot"],
143
- { encoding: "utf8" },
144
- ).trimEnd();
145
- }
146
- return execFileSync(
147
- "/usr/bin/security",
148
- ["find-generic-password", "-w", "-s", "Grok Bot Safe Storage"],
149
- { encoding: "utf8" },
150
- ).trimEnd();
151
- }
152
-
153
- function unprotectWithDpapi(blob) {
154
- const script =
155
- "Add-Type -AssemblyName System.Security; " +
156
- "$blob = [Convert]::FromBase64String([Console]::In.ReadToEnd().Trim()); " +
157
- "[Convert]::ToBase64String([Security.Cryptography.ProtectedData]::Unprotect($blob, $null, 'CurrentUser'))";
158
- const out = execFileSync(
159
- join(
160
- process.env.SystemRoot ?? "C:\\Windows",
161
- "System32/WindowsPowerShell/v1.0/powershell.exe",
162
- ),
163
- ["-NoProfile", "-NonInteractive", "-Command", script],
164
- { input: blob.toString("base64"), encoding: "utf8" },
165
- );
166
- return Buffer.from(out.trim(), "base64");
167
- }
168
-
169
- function readWindowsSafeStorageKey(home, env, unprotectData) {
170
- const path = join(grokBotAppDataPath(home, "win32", env), "Local State");
171
- const encryptedKey = existsSync(path)
172
- ? JSON.parse(readFileSync(path, "utf8")).os_crypt?.encrypted_key
173
- : null;
174
- const blob = Buffer.from(
175
- typeof encryptedKey === "string" ? encryptedKey : "",
176
- "base64",
177
- );
178
- if (blob.subarray(0, 5).toString("latin1") !== "DPAPI") {
179
- throw new GrokBotGatewaySessionError(
180
- "MISSING_SAFE_STORAGE_KEY",
181
- "Grok Bot Local State has no Safe Storage key.",
182
- );
183
- }
184
- return unprotectData(blob.subarray(5));
185
- }
186
-
187
- export function loadGrokBotGatewaySession({
188
- platform = process.platform,
189
- home = homedir(),
190
- env = process.env,
191
- appData,
192
- getKeychainPassword = readKeychainPassword,
193
- unprotectData = unprotectWithDpapi,
194
- } = {}) {
195
- if (!SUPPORTED_PLATFORMS.has(platform)) return null;
196
-
197
- const effectiveEnv = sessionEnv({ env, appData });
198
- const path = grokBotGatewayDescriptorPath(home, platform, effectiveEnv);
199
- if (!existsSync(path)) return null;
200
-
201
- const wrapped = JSON.parse(readFileSync(path, "utf8"));
202
- const encrypted = encryptedPayload(wrapped);
203
- let clear;
204
- if (platform === "win32") {
205
- clear = decryptWindowsSafeStorageString(
206
- encrypted,
207
- readWindowsSafeStorageKey(home, effectiveEnv, unprotectData),
208
- );
209
- } else {
210
- const prefix = Buffer.from(encrypted, "base64").subarray(0, 3).toString("latin1");
211
- // Linux v10 is the keyring-less basic_text backend; no secret store to ask.
212
- const needsKeychain = !(platform === "linux" && prefix === SAFE_STORAGE_PREFIX_V10);
213
- clear = decryptSafeStorageString(
214
- encrypted,
215
- needsKeychain ? getKeychainPassword(platform) : LINUX_BASIC_TEXT_PASSWORD,
216
- platform,
217
- );
218
- }
219
- const descriptor = JSON.parse(clear);
220
- if (!descriptor.baseUrl || !descriptor.token) {
221
- throw new GrokBotGatewaySessionError(
222
- "INCOMPLETE_DESCRIPTOR",
223
- "Decrypted Grok Bot gateway descriptor is incomplete.",
224
- );
225
- }
226
-
227
- return {
228
- gatewayUrl: String(descriptor.baseUrl).replace(/\/$/, ""),
229
- gatewayToken: String(descriptor.token),
230
- headers: descriptor.headers ?? {},
231
- };
232
- }
233
-
234
- export function inspectGrokBotGatewaySession(options = {}) {
235
- if (!hasGrokBotGatewaySession(options)) {
236
- return { present: false, usable: false };
237
- }
238
- try {
239
- loadGrokBotGatewaySession(options);
240
- return { present: true, usable: true };
241
- } catch (error) {
242
- return {
243
- present: true,
244
- usable: false,
245
- code: error instanceof GrokBotGatewaySessionError ? error.code : "UNUSABLE_SESSION",
246
- error: error instanceof Error ? error.message : String(error),
247
- };
248
- }
249
- }