grok-bot-sdk 0.1.0 → 0.75.8
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/CHANGELOG.md +20 -0
- package/README.md +9 -7
- package/README_ZH.md +9 -7
- package/avatar.mjs +6 -3
- package/bin/grok-bot-sdk.mjs +110 -3
- package/core.mjs +3 -1
- package/package.json +4 -3
- package/sdk.d.mts +211 -3
- package/sdk.mjs +164 -9
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.75.8
|
|
4
|
+
|
|
5
|
+
Protocol surface matched to the Grok Bot 0.75.8 desktop client. The package stays ESM, with no runtime dependencies.
|
|
6
|
+
|
|
7
|
+
- Default `x-cursor-client-version` is `0.75.8`.
|
|
8
|
+
- Login polling stops on HTTP 403 `{ "error": "sign_in_policy_violation" }` and throws `SIGN_IN_POLICY_VIOLATION`.
|
|
9
|
+
- Roster reads optional `role` and `addedTeamAgentsOnly`, and returns Slack and Teams connection records plus the additional `GrokBotAgent` scalars present in 0.75.8.
|
|
10
|
+
- `sendMessage` accepts `replyToId`, `replyQuote`, `taskId`, `language`, `richText`, and `machineId`. Delivery `4` (`REFUSED`) throws `SEND_REFUSED`.
|
|
11
|
+
- New read methods: `listSessions`, `listTranscript`, `listAgentStatuses`, `getSendStatus`, `listAgentTasks`, `listTasks`, `listSubagents`, `listAgentTodos`, `listAgentSkills`, `listDeletedAgentIds`, `getRuntimeCapabilities`, `getSandBoxRunState`.
|
|
12
|
+
- Watch events add `agent.state`, `computer.actions`, `box.state`, and `page.title.changed`. `bot.state.changed` includes `changedAtMs`.
|
|
13
|
+
- CLI read-only subcommands: `bots`, `sessions`, `transcript`, `tasks`, `capabilities`, `box`.
|
|
14
|
+
- Avatar derivation is unchanged (`grok-bot-0.70.2`). `pumpkin` is accepted as an explicit shape.
|
|
15
|
+
|
|
16
|
+
The checksum, ghost-mode default, and desktop chat enums `source=1`, `initiator=1`, `sendSource=1` are the same values the 0.75.8 client still sends for desktop chat.
|
|
17
|
+
|
|
18
|
+
## 0.1.0
|
|
19
|
+
|
|
20
|
+
First release, matched to the Grok Bot 0.70.2 desktop client. From 0.75.8 on, the package version follows the Grok Bot version it targets.
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<h1 align="center">grok-bot-sdk</h1>
|
|
9
9
|
|
|
10
10
|
<p align="center"><strong>Unofficial Node.js SDK for the Grok Bot desktop client protocol</strong></p>
|
|
11
|
-
<p align="center"><em>Browser login · Account and bot roster · Normalized avatars · Send · Connect replies forwarded as SSE</em></p>
|
|
11
|
+
<p align="center"><em>Browser login · Account and bot roster · Sessions, transcript, and tasks · Normalized avatars · Send · Connect replies forwarded as SSE</em></p>
|
|
12
12
|
<p align="center"><a href="https://ronglecat.github.io/grok-bot-sdk/">https://ronglecat.github.io/grok-bot-sdk/</a></p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
|
|
22
22
|
<a href="https://github.com/RongleCat/grok-bot-sdk/stargazers"><img src="https://img.shields.io/github/stars/RongleCat/grok-bot-sdk?style=social" alt="GitHub stars" /></a>
|
|
23
23
|
<img src="https://img.shields.io/badge/node-%3E%3D22.13-339933?logo=node.js&logoColor=white" alt="Node.js" />
|
|
24
|
-
<img src="https://img.shields.io/badge/client-Grok%20Bot%200.
|
|
24
|
+
<img src="https://img.shields.io/badge/client-Grok%20Bot%200.75.8-lightgrey" alt="Grok Bot 0.75.8" />
|
|
25
25
|
<img src="https://img.shields.io/badge/note-unofficial-yellow" alt="Unofficial" />
|
|
26
26
|
</p>
|
|
27
27
|
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
44
|
> [!NOTE]
|
|
45
|
-
> **About this SDK.** grok-bot-sdk reproduces the browser login and the read, send, and reply-stream calls used by the Grok Bot **0.
|
|
45
|
+
> **About this SDK.** grok-bot-sdk reproduces the browser login and the read, send, and reply-stream calls used by the Grok Bot **0.75.8** desktop client. It is an independent open-source library by [铁柱AGI](https://x.com/cgnot996). It is **not an xAI product, not a Cursor product, and not the official Grok Bot SDK**.
|
|
46
46
|
>
|
|
47
47
|
> The same author also builds [Grok App](https://github.com/RongleCat/grok-app), a desktop workbench for the local Grok Build CLI. Grok App and this SDK are different programs.
|
|
48
48
|
|
|
@@ -155,15 +155,17 @@ return client.botEventsSSE({
|
|
|
155
155
|
|
|
156
156
|
## What this release includes
|
|
157
157
|
|
|
158
|
-
Verified against Grok Bot 0.
|
|
158
|
+
Verified against Grok Bot 0.75.8:
|
|
159
159
|
|
|
160
160
|
- One browser login continued into account read and the cloud bot roster.
|
|
161
161
|
- The same read path works with the Mac client's existing session.
|
|
162
|
-
- Roster records come back with one avatar shape, whether the server sent the fields or the SDK derived them.
|
|
162
|
+
- Roster records come back with one avatar shape, whether the server sent the fields or the SDK derived them. The avatar hash is still `grok-bot-0.70.2`. `pumpkin` is explicit only.
|
|
163
163
|
- `WatchGrokBotTranscripts` connects with Connect JSON, and an existing reply was forwarded through `botEventsSSE`.
|
|
164
|
-
-
|
|
164
|
+
- Read calls: `listSessions`, `listTranscript`, `listAgentStatuses`, `getSendStatus`, `listAgentTasks`, `listTasks`, `listSubagents`, `listAgentTodos`, `listAgentSkills`, `listDeletedAgentIds`, `getRuntimeCapabilities`, and `getSandBoxRunState`.
|
|
165
|
+
- Watch events also include `agent.state`, `computer.actions`, `box.state`, and `page.title.changed`.
|
|
166
|
+
- Offline tests cover login URL shape, avatar vectors, frame parsing, cursors, cancellation, SSE framing, send results, and the new read records.
|
|
165
167
|
|
|
166
|
-
The default client version string is `0.
|
|
168
|
+
The default client version string is `0.75.8`. Pass `version` when you know the client you are matching. The CLI also has read-only subcommands: `bots`, `sessions`, `transcript`, `tasks`, `capabilities`, and `box`.
|
|
167
169
|
|
|
168
170
|
## Notes you should read
|
|
169
171
|
|
package/README_ZH.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<h1 align="center">grok-bot-sdk</h1>
|
|
9
9
|
|
|
10
10
|
<p align="center"><strong>Grok Bot 桌面客户端协议的非官方 Node.js SDK</strong></p>
|
|
11
|
-
<p align="center"><em>浏览器登录 · 账号与 Bot 名单 · 统一头像 · 发送 · Connect 回复转发为 SSE</em></p>
|
|
11
|
+
<p align="center"><em>浏览器登录 · 账号与 Bot 名单 · 会话、转录与任务 · 统一头像 · 发送 · Connect 回复转发为 SSE</em></p>
|
|
12
12
|
<p align="center"><a href="https://ronglecat.github.io/grok-bot-sdk/zh/">https://ronglecat.github.io/grok-bot-sdk/zh/</a></p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
|
|
22
22
|
<a href="https://github.com/RongleCat/grok-bot-sdk/stargazers"><img src="https://img.shields.io/github/stars/RongleCat/grok-bot-sdk?style=social" alt="GitHub stars" /></a>
|
|
23
23
|
<img src="https://img.shields.io/badge/node-%3E%3D22.13-339933?logo=node.js&logoColor=white" alt="Node.js" />
|
|
24
|
-
<img src="https://img.shields.io/badge/client-Grok%20Bot%200.
|
|
24
|
+
<img src="https://img.shields.io/badge/client-Grok%20Bot%200.75.8-lightgrey" alt="Grok Bot 0.75.8" />
|
|
25
25
|
<img src="https://img.shields.io/badge/note-unofficial-yellow" alt="非官方" />
|
|
26
26
|
</p>
|
|
27
27
|
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
44
|
> [!NOTE]
|
|
45
|
-
> **关于这个 SDK。** grok-bot-sdk 复现 Grok Bot **0.
|
|
45
|
+
> **关于这个 SDK。** grok-bot-sdk 复现 Grok Bot **0.75.8** 桌面客户端使用的浏览器登录,以及读取账号、读取名单、发送消息和订阅回复。作者是 [铁柱AGI](https://x.com/cgnot996)。它**不是 xAI 官方产品,不是 Cursor 官方产品,也不是 Grok Bot 官方 SDK**。
|
|
46
46
|
>
|
|
47
47
|
> 同一作者的 [Grok App](https://github.com/RongleCat/grok-app) 是本机 Grok Build CLI 的桌面工作台。Grok App 和这个 SDK 是两个程序。
|
|
48
48
|
|
|
@@ -155,15 +155,17 @@ return client.botEventsSSE({
|
|
|
155
155
|
|
|
156
156
|
## 这一版包含什么
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
对照 Grok Bot 0.75.8 核对过:
|
|
159
159
|
|
|
160
160
|
- 一次浏览器登录可以接着读账号和云端 Bot 名单。
|
|
161
161
|
- 用 Mac 客户端里已有的会话,可以走同一条读取路径。
|
|
162
|
-
- 名单里的每条记录都带回同一种头像结构。服务端带了字段就用字段,缺了就按 UUID
|
|
162
|
+
- 名单里的每条记录都带回同一种头像结构。服务端带了字段就用字段,缺了就按 UUID 推算。头像哈希仍是 `grok-bot-0.70.2`。`pumpkin` 只在显式指定时出现。
|
|
163
163
|
- `WatchGrokBotTranscripts` 以 Connect JSON 接通,既有回复可以经 `botEventsSSE` 转发。
|
|
164
|
-
-
|
|
164
|
+
- 只读调用:`listSessions`、`listTranscript`、`listAgentStatuses`、`getSendStatus`、`listAgentTasks`、`listTasks`、`listSubagents`、`listAgentTodos`、`listAgentSkills`、`listDeletedAgentIds`、`getRuntimeCapabilities`、`getSandBoxRunState`。
|
|
165
|
+
- 订阅事件还包含 `agent.state`、`computer.actions`、`box.state` 和 `page.title.changed`。
|
|
166
|
+
- 离线测试覆盖登录 URL、头像向量、帧解析、游标、取消、SSE 帧、发送结果,以及新增的只读记录。
|
|
165
167
|
|
|
166
|
-
默认客户端版本字符串是 `0.
|
|
168
|
+
默认客户端版本字符串是 `0.75.8`。你知道自己要匹配的客户端版本时,传入 `version`。CLI 还有只读子命令:`bots`、`sessions`、`transcript`、`tasks`、`capabilities`、`box`。
|
|
167
169
|
|
|
168
170
|
## 使用前要知道的事
|
|
169
171
|
|
package/avatar.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
// Parameter derivation verified against Grok Bot 0.70.2
|
|
2
|
-
//
|
|
1
|
+
// Parameter derivation verified against Grok Bot 0.70.2 and again against 0.75.8.
|
|
2
|
+
// 0.75.8 keeps the same hash, salts, default shapes, and palette. It adds one
|
|
3
|
+
// explicit shape, pumpkin, behind the sand_pumpkin_2026 picker gate. Pumpkin is
|
|
4
|
+
// not part of the default hash set. This module does not load or execute the
|
|
5
|
+
// official renderer or its assets.
|
|
3
6
|
export const AVATAR_ALGORITHM_VERSION='grok-bot-0.70.2';
|
|
4
7
|
export const DEFAULT_SHAPES=Object.freeze(['blob','pebble','squircle','tablet','wedge','hex','cloud','teardrop']);
|
|
5
|
-
export const VALID_SHAPES=Object.freeze(['blob','pebble','bean','egg','squircle','tablet','capsule','cylinder','hex','gem','crystal','wedge','shield','dome','arch','cloud','teardrop','leaf','square','sparkle','clover','flower','heart','house','star']);
|
|
8
|
+
export const VALID_SHAPES=Object.freeze(['blob','pebble','bean','egg','squircle','tablet','capsule','cylinder','hex','gem','crystal','wedge','shield','dome','arch','cloud','teardrop','leaf','square','sparkle','clover','flower','heart','house','star','pumpkin']);
|
|
6
9
|
export const DEFAULT_COLORS=Object.freeze(['brown','red','orange','yellow','green','cyan','blue','violet','magenta','gray']);
|
|
7
10
|
const PALETTE=Object.freeze(Object.fromEntries([
|
|
8
11
|
['black','#000000','#000000','#FFFFFF'],
|
package/bin/grok-bot-sdk.mjs
CHANGED
|
@@ -19,10 +19,17 @@ const failed = code => {
|
|
|
19
19
|
function usage() {
|
|
20
20
|
console.log(`grok-bot-sdk login [--no-open] --out <file>
|
|
21
21
|
grok-bot-sdk whoami [--token-file <file>]
|
|
22
|
+
grok-bot-sdk bots [--token-file <file>]
|
|
23
|
+
grok-bot-sdk sessions --bot <id> [--token-file <file>]
|
|
24
|
+
grok-bot-sdk transcript --bot <id> [--session <id>] [--limit <n>] [--token-file <file>]
|
|
25
|
+
grok-bot-sdk tasks --bot <id> [--session <id>] [--token-file <file>]
|
|
26
|
+
grok-bot-sdk capabilities [--token-file <file>]
|
|
27
|
+
grok-bot-sdk box [--token-file <file>]
|
|
22
28
|
|
|
23
29
|
login opens the browser, waits for approval, and writes the session to <file> (mode 0600).
|
|
24
30
|
The command prints the login page URL and a redacted summary. It does not print tokens.
|
|
25
|
-
whoami reads GROK_BOT_ACCESS_TOKEN, or the accessToken field inside --token-file, and prints only whether account fields exist
|
|
31
|
+
whoami reads GROK_BOT_ACCESS_TOKEN, or the accessToken field inside --token-file, and prints only whether account fields exist.
|
|
32
|
+
The other commands are read-only. They print roster, session, transcript, task, capability, or box fields for the saved token.`);
|
|
26
33
|
process.exitCode = 2;
|
|
27
34
|
}
|
|
28
35
|
|
|
@@ -73,13 +80,17 @@ async function loadToken() {
|
|
|
73
80
|
return { accessToken, version: process.env.GROK_BOT_CLIENT_VERSION };
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
async function
|
|
83
|
+
async function clientFromToken() {
|
|
77
84
|
const loaded = await loadToken();
|
|
78
|
-
|
|
85
|
+
return new GrokBotClient({
|
|
79
86
|
accessToken: loaded.accessToken,
|
|
80
87
|
...loaded.version ? { version: loaded.version } : {},
|
|
81
88
|
...loaded.teamId !== undefined ? { teamId: loaded.teamId } : {},
|
|
82
89
|
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function whoami() {
|
|
93
|
+
const client = await clientFromToken();
|
|
83
94
|
const me = await client.getMe();
|
|
84
95
|
console.log(JSON.stringify({
|
|
85
96
|
status: 'ok',
|
|
@@ -89,9 +100,105 @@ async function whoami() {
|
|
|
89
100
|
}));
|
|
90
101
|
}
|
|
91
102
|
|
|
103
|
+
function requireBot() {
|
|
104
|
+
const bot = option('--bot');
|
|
105
|
+
if (!bot || bot.startsWith('-')) {
|
|
106
|
+
usage();
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return bot;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function bots() {
|
|
113
|
+
const roster = await (await clientFromToken()).listBots();
|
|
114
|
+
console.log(JSON.stringify({
|
|
115
|
+
status: 'ok',
|
|
116
|
+
count: roster.bots.length,
|
|
117
|
+
truncated: roster.truncated,
|
|
118
|
+
slackConnectionCount: roster.slackConnections.length,
|
|
119
|
+
teamsConnectionCount: roster.teamsConnections.length,
|
|
120
|
+
bots: roster.bots.map(bot => ({
|
|
121
|
+
id: bot.id,
|
|
122
|
+
name: bot.name,
|
|
123
|
+
kind: bot.kind,
|
|
124
|
+
role: bot.role,
|
|
125
|
+
sessionId: bot.sessionId,
|
|
126
|
+
})),
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function sessions() {
|
|
131
|
+
const bot = requireBot();
|
|
132
|
+
if (!bot) return;
|
|
133
|
+
const result = await (await clientFromToken()).listSessions(bot);
|
|
134
|
+
console.log(JSON.stringify({ status: 'ok', count: result.sessions.length, sessions: result.sessions }));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function transcript() {
|
|
138
|
+
const bot = requireBot();
|
|
139
|
+
if (!bot) return;
|
|
140
|
+
const limitText = option('--limit');
|
|
141
|
+
const limit = limitText === undefined ? 20 : Number(limitText);
|
|
142
|
+
const result = await (await clientFromToken()).listTranscript({
|
|
143
|
+
botId: bot,
|
|
144
|
+
sessionId: option('--session') ?? '',
|
|
145
|
+
limit,
|
|
146
|
+
});
|
|
147
|
+
console.log(JSON.stringify({
|
|
148
|
+
status: 'ok',
|
|
149
|
+
generation: result.generation,
|
|
150
|
+
count: result.entries.length,
|
|
151
|
+
cloudAgentPeerIdsKnown: result.cloudAgentPeerIdsKnown,
|
|
152
|
+
peerCount: result.cloudAgentPeerIds.length,
|
|
153
|
+
unreadAnchor: result.unreadAnchor,
|
|
154
|
+
entries: result.entries.map(entry => ({
|
|
155
|
+
seq: entry.seq,
|
|
156
|
+
updatedSeq: entry.updatedSeq,
|
|
157
|
+
entryId: entry.entryId,
|
|
158
|
+
entryKind: entry.entryKind,
|
|
159
|
+
bodyOmitted: entry.bodyOmitted,
|
|
160
|
+
bodyKind: typeof entry.body?.kind === 'string' ? entry.body.kind : null,
|
|
161
|
+
})),
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function tasks() {
|
|
166
|
+
const bot = requireBot();
|
|
167
|
+
if (!bot) return;
|
|
168
|
+
const client = await clientFromToken();
|
|
169
|
+
const sessionId = option('--session') ?? '';
|
|
170
|
+
const [agentTasks, taskList] = await Promise.all([
|
|
171
|
+
client.listAgentTasks({ botId: bot }),
|
|
172
|
+
client.listTasks({ botId: bot, sessionId }),
|
|
173
|
+
]);
|
|
174
|
+
console.log(JSON.stringify({
|
|
175
|
+
status: 'ok',
|
|
176
|
+
agentTaskCount: agentTasks.tasks.length,
|
|
177
|
+
taskCount: taskList.tasks.length,
|
|
178
|
+
agentTasks: agentTasks.tasks,
|
|
179
|
+
tasks: taskList.tasks,
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function capabilities() {
|
|
184
|
+
const result = await (await clientFromToken()).getRuntimeCapabilities();
|
|
185
|
+
console.log(JSON.stringify({ status: 'ok', ...result }));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function box() {
|
|
189
|
+
const result = await (await clientFromToken()).getSandBoxRunState();
|
|
190
|
+
console.log(JSON.stringify({ status: 'ok', ...result }));
|
|
191
|
+
}
|
|
192
|
+
|
|
92
193
|
try {
|
|
93
194
|
if (command === 'login') await login();
|
|
94
195
|
else if (command === 'whoami') await whoami();
|
|
196
|
+
else if (command === 'bots') await bots();
|
|
197
|
+
else if (command === 'sessions') await sessions();
|
|
198
|
+
else if (command === 'transcript') await transcript();
|
|
199
|
+
else if (command === 'tasks') await tasks();
|
|
200
|
+
else if (command === 'capabilities') await capabilities();
|
|
201
|
+
else if (command === 'box') await box();
|
|
95
202
|
else usage();
|
|
96
203
|
} catch (error) {
|
|
97
204
|
const code = error instanceof Error && /^[A-Z_]{1,80}$/.test(error.message) ? error.message : 'COMMAND_FAILED';
|
package/core.mjs
CHANGED
|
@@ -73,7 +73,8 @@ export async function requestJSON(url,{method='POST',headers={},body,fetchImpl=f
|
|
|
73
73
|
if(Buffer.byteLength(text)>8*1024*1024) return {ok:false,httpStatus:response.status,code:'RESPONSE_TOO_LARGE'};
|
|
74
74
|
let data;
|
|
75
75
|
try {data=JSON.parse(text);} catch {return {ok:false,httpStatus:response.status,code:'NON_JSON_RESPONSE'};}
|
|
76
|
-
|
|
76
|
+
const rawCode=typeof data?.code==='string'?data.code:typeof data?.error==='string'?data.error:'';
|
|
77
|
+
if(!response.ok) return {ok:false,httpStatus:response.status,code:/^[a-z_]{1,48}$/.test(rawCode)?rawCode:'HTTP_ERROR'};
|
|
77
78
|
return {ok:true,httpStatus:response.status,data};
|
|
78
79
|
} catch {return {ok:false,code:'NETWORK_OR_TLS_ERROR'};}
|
|
79
80
|
}
|
|
@@ -125,6 +126,7 @@ export async function pollLogin(flow,{fetchImpl=fetch,sleep=delay,maxAttempts=15
|
|
|
125
126
|
for(let attempt=0;attempt<maxAttempts;attempt++){
|
|
126
127
|
const result=await requestJSON(url,{method:'GET',headers:{'content-type':'application/json'},fetchImpl});
|
|
127
128
|
if(result.ok && typeof result.data.accessToken==='string' && typeof result.data.refreshToken==='string')return result.data;
|
|
129
|
+
if(result.httpStatus===403 && result.code==='sign_in_policy_violation')throw fixedError('SIGN_IN_POLICY_VIOLATION');
|
|
128
130
|
if(result.httpStatus===404){errors=0;onPending(attempt+1);}
|
|
129
131
|
else if(++errors>=3)throw fixedError('LOGIN_POLL_FAILED');
|
|
130
132
|
await sleep(Math.min(1000*1.2**attempt,10000));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grok-bot-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Unofficial Node.js SDK for the Grok Bot 0.
|
|
3
|
+
"version": "0.75.8",
|
|
4
|
+
"description": "Unofficial Node.js SDK for the Grok Bot 0.75.8 desktop client protocol: login, roster, sessions, transcript, tasks, send, and Connect-to-SSE replies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "RongleCat (铁柱AGI)",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node": ">=22.13"
|
|
23
23
|
},
|
|
24
24
|
"bin": {
|
|
25
|
-
"grok-bot-sdk": "bin/grok-bot-sdk.mjs"
|
|
25
|
+
"grok-bot-sdk": "./bin/grok-bot-sdk.mjs"
|
|
26
26
|
},
|
|
27
27
|
"exports": {
|
|
28
28
|
".": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"bin",
|
|
46
46
|
"README.md",
|
|
47
47
|
"README_ZH.md",
|
|
48
|
+
"CHANGELOG.md",
|
|
48
49
|
"LICENSE"
|
|
49
50
|
],
|
|
50
51
|
"scripts": {
|
package/sdk.d.mts
CHANGED
|
@@ -12,6 +12,7 @@ export interface ClientOptions {
|
|
|
12
12
|
export interface Bot {
|
|
13
13
|
id: string;
|
|
14
14
|
serverId: string | null;
|
|
15
|
+
legacyAgentId: string;
|
|
15
16
|
name: string;
|
|
16
17
|
description: string;
|
|
17
18
|
title: string;
|
|
@@ -19,11 +20,188 @@ export interface Bot {
|
|
|
19
20
|
avatarColor: string;
|
|
20
21
|
avatar: ResolvedAvatar;
|
|
21
22
|
harness: string;
|
|
23
|
+
role: string;
|
|
22
24
|
kind: string;
|
|
25
|
+
visibility: string;
|
|
23
26
|
sessionId: string;
|
|
24
27
|
teamId: number | null;
|
|
25
28
|
viewerIsOwner: boolean;
|
|
26
29
|
viewerCanManage: boolean;
|
|
30
|
+
teamDefaultForViewer: boolean;
|
|
31
|
+
ownerDisplayName: string;
|
|
32
|
+
draftingDisabled: boolean;
|
|
33
|
+
accessGrantsOnly: boolean;
|
|
34
|
+
memberAgentIds: string[];
|
|
35
|
+
viewerAllowedActions: string[];
|
|
36
|
+
createdAtMs: string | null;
|
|
37
|
+
updatedAtMs: string | null;
|
|
38
|
+
voiceId: string;
|
|
39
|
+
voiceSpeed: number | null;
|
|
40
|
+
voiceLanguage: string;
|
|
41
|
+
}
|
|
42
|
+
export interface SlackConnection {
|
|
43
|
+
agentId: string;
|
|
44
|
+
connected: boolean;
|
|
45
|
+
workspaceName: string | null;
|
|
46
|
+
installUrl: string | null;
|
|
47
|
+
unavailableReason: string | null;
|
|
48
|
+
pendingAdminApproval: boolean;
|
|
49
|
+
needsManifestUpdate: boolean;
|
|
50
|
+
reinstallUrl: string | null;
|
|
51
|
+
approvalDenied: boolean;
|
|
52
|
+
appUnreachable: boolean;
|
|
53
|
+
removedInSlack: boolean;
|
|
54
|
+
approvalRequestUnsent: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface TeamsInstalledTeam {
|
|
57
|
+
teamId: string;
|
|
58
|
+
displayName: string;
|
|
59
|
+
rscGranted: boolean;
|
|
60
|
+
installedAtMs: string | null;
|
|
61
|
+
}
|
|
62
|
+
export interface TeamsConnection {
|
|
63
|
+
agentId: string;
|
|
64
|
+
installState: string;
|
|
65
|
+
tenantId: string | null;
|
|
66
|
+
tenantDisplayName: string | null;
|
|
67
|
+
installedTeamCount: number;
|
|
68
|
+
installedTeams: TeamsInstalledTeam[];
|
|
69
|
+
}
|
|
70
|
+
export interface BotSession {
|
|
71
|
+
agentId: string;
|
|
72
|
+
sessionId: string;
|
|
73
|
+
kind: string;
|
|
74
|
+
createdAtMs: string | null;
|
|
75
|
+
updatedAtMs: string | null;
|
|
76
|
+
lastActivityAtMs: string | null;
|
|
77
|
+
}
|
|
78
|
+
export interface TranscriptEntry {
|
|
79
|
+
seq: string;
|
|
80
|
+
updatedSeq: string;
|
|
81
|
+
entryId: string | null;
|
|
82
|
+
entryKind: string;
|
|
83
|
+
bodyOmitted: boolean;
|
|
84
|
+
blobHash: string | null;
|
|
85
|
+
body: Record<string, unknown> | null;
|
|
86
|
+
}
|
|
87
|
+
export interface UnreadAnchor {
|
|
88
|
+
hasNewerRows: boolean;
|
|
89
|
+
entryId: string;
|
|
90
|
+
seq: string | null;
|
|
91
|
+
newerRows: number;
|
|
92
|
+
}
|
|
93
|
+
export interface AgentLiveState {
|
|
94
|
+
agentId: string;
|
|
95
|
+
sessionId: string;
|
|
96
|
+
isRunning: boolean;
|
|
97
|
+
isComposingMessage: boolean;
|
|
98
|
+
isRetrying: boolean;
|
|
99
|
+
updatedAtMs: string | null;
|
|
100
|
+
staleAfterMs: string | null;
|
|
101
|
+
hasRunningSubagents: boolean;
|
|
102
|
+
surfaceTaskId: string;
|
|
103
|
+
workingTaskIds: string[];
|
|
104
|
+
activityKind: string;
|
|
105
|
+
}
|
|
106
|
+
export interface AgentClientState {
|
|
107
|
+
agentId: string;
|
|
108
|
+
lastViewedAtMs: string | null;
|
|
109
|
+
unreadCount: number;
|
|
110
|
+
lastActivityAtMs: string | null;
|
|
111
|
+
lastEntryId: string;
|
|
112
|
+
lastEntryKind: string;
|
|
113
|
+
lastMessageId: string;
|
|
114
|
+
lastMessagePreview: string;
|
|
115
|
+
notificationsEnabled: boolean;
|
|
116
|
+
notifyOnUpdatesEnabled: boolean;
|
|
117
|
+
hiddenFromSidebar: boolean;
|
|
118
|
+
updatedAtMs: string | null;
|
|
119
|
+
lastMessageTaskId: string;
|
|
120
|
+
removedFromSidebar: boolean;
|
|
121
|
+
}
|
|
122
|
+
export interface AgentStatus {
|
|
123
|
+
agentId: string;
|
|
124
|
+
isRunning: boolean;
|
|
125
|
+
live: AgentLiveState | null;
|
|
126
|
+
client: AgentClientState | null;
|
|
127
|
+
}
|
|
128
|
+
export interface AgentTask {
|
|
129
|
+
id: string;
|
|
130
|
+
name: string;
|
|
131
|
+
description: string;
|
|
132
|
+
status: string;
|
|
133
|
+
updatedAtMs: string | null;
|
|
134
|
+
unreadEntryId: string | null;
|
|
135
|
+
unreadPreview: string | null;
|
|
136
|
+
unreadAtMs: string | null;
|
|
137
|
+
pinnedAtMs: string | null;
|
|
138
|
+
subagentIds: string[];
|
|
139
|
+
}
|
|
140
|
+
export interface BotTask {
|
|
141
|
+
id: string;
|
|
142
|
+
title: string;
|
|
143
|
+
state: string;
|
|
144
|
+
note: string;
|
|
145
|
+
createdAtMs: string | null;
|
|
146
|
+
doneAtMs: string | null;
|
|
147
|
+
seenDoneAtMs: string | null;
|
|
148
|
+
}
|
|
149
|
+
export interface RunningSubagent {
|
|
150
|
+
subagentId: string;
|
|
151
|
+
subagentType: string;
|
|
152
|
+
title: string;
|
|
153
|
+
startedAtMs: string | null;
|
|
154
|
+
freshUntilMs: string | null;
|
|
155
|
+
}
|
|
156
|
+
export interface AgentTodo {
|
|
157
|
+
id: string;
|
|
158
|
+
content: string;
|
|
159
|
+
status: string;
|
|
160
|
+
createdAtMs: string | null;
|
|
161
|
+
updatedAtMs: string | null;
|
|
162
|
+
dependencies: string[];
|
|
163
|
+
}
|
|
164
|
+
export interface AgentSkill {
|
|
165
|
+
id: string;
|
|
166
|
+
name: string;
|
|
167
|
+
description: string;
|
|
168
|
+
body: string;
|
|
169
|
+
source: string;
|
|
170
|
+
}
|
|
171
|
+
export interface RuntimeCapabilities {
|
|
172
|
+
durableIdentityEnabled: boolean;
|
|
173
|
+
durableIdentityWritesEnabled: boolean;
|
|
174
|
+
temporalCreationEnabled: boolean;
|
|
175
|
+
agentMessagingEnabled: boolean;
|
|
176
|
+
serverRoomsEnabled: boolean;
|
|
177
|
+
}
|
|
178
|
+
export interface SandBoxImageInfo {
|
|
179
|
+
registryTag: string | null;
|
|
180
|
+
staticSnapshotPath: string | null;
|
|
181
|
+
}
|
|
182
|
+
export interface SandBoxRunState {
|
|
183
|
+
runState: string;
|
|
184
|
+
imageUpdateAvailable: boolean;
|
|
185
|
+
egressTunnelOwnership: boolean;
|
|
186
|
+
image: SandBoxImageInfo | null;
|
|
187
|
+
}
|
|
188
|
+
export interface BoxState {
|
|
189
|
+
runState: string;
|
|
190
|
+
recreateInFlight: boolean;
|
|
191
|
+
imageUpdateAvailable: boolean;
|
|
192
|
+
hostVersion: string | null;
|
|
193
|
+
hostUpdateAvailable: boolean;
|
|
194
|
+
diskPressure: string;
|
|
195
|
+
updatedAtMs: string | null;
|
|
196
|
+
}
|
|
197
|
+
export interface ComputerAction {
|
|
198
|
+
agentId: string;
|
|
199
|
+
type: string;
|
|
200
|
+
x: number | null;
|
|
201
|
+
y: number | null;
|
|
202
|
+
button: string;
|
|
203
|
+
count: number;
|
|
204
|
+
atMs: string | null;
|
|
27
205
|
}
|
|
28
206
|
export interface BotCursor { generation: number; afterUpdatedSeq: string; }
|
|
29
207
|
export interface WatchOptions {
|
|
@@ -53,8 +231,12 @@ export type BotEvent =
|
|
|
53
231
|
| { type: 'reset'; id: string; data: { botId: string; generation: number } }
|
|
54
232
|
| { type: 'resync-required'; data: { botId: string; generation: number } }
|
|
55
233
|
| { type: 'turn.failed'; data: { botId: string; turnId: string; code: number | string; summary: string } }
|
|
56
|
-
| { type: 'bot.state.changed'; data: { botId: string; families: string[] } }
|
|
57
|
-
| { type: 'bot.updated'; data: { botId: string; changedFields: Array<string | number>; bot: Bot | null } }
|
|
234
|
+
| { type: 'bot.state.changed'; data: { botId: string; families: string[]; changedAtMs: string | null } }
|
|
235
|
+
| { type: 'bot.updated'; data: { botId: string; changedFields: Array<string | number>; bot: Bot | null } }
|
|
236
|
+
| { type: 'agent.state'; data: { botId: string; sessionId: string; snapshot: boolean; live: AgentLiveState[]; client: AgentClientState[] } }
|
|
237
|
+
| { type: 'computer.actions'; data: { botId: string; actions: ComputerAction[] } }
|
|
238
|
+
| { type: 'box.state'; data: { botId: string; snapshot: boolean; state: BoxState | null } }
|
|
239
|
+
| { type: 'page.title.changed'; data: { botId: string; storeId: string; canvasId: string | null; documentId: string | null; title: string; changedAtMs: string | null } };
|
|
58
240
|
export interface SendOptions {
|
|
59
241
|
botId: string;
|
|
60
242
|
text: string;
|
|
@@ -62,6 +244,20 @@ export interface SendOptions {
|
|
|
62
244
|
messageId?: string;
|
|
63
245
|
attachmentPaths?: string[];
|
|
64
246
|
attachmentNames?: string[];
|
|
247
|
+
replyToId?: string;
|
|
248
|
+
replyQuote?: string;
|
|
249
|
+
taskId?: string;
|
|
250
|
+
language?: string;
|
|
251
|
+
richText?: string;
|
|
252
|
+
machineId?: string;
|
|
253
|
+
}
|
|
254
|
+
export interface TranscriptQuery {
|
|
255
|
+
botId: string;
|
|
256
|
+
sessionId?: string;
|
|
257
|
+
limit?: number;
|
|
258
|
+
generation?: number;
|
|
259
|
+
beforeSeq?: string;
|
|
260
|
+
unreadBoundaryMs?: string;
|
|
65
261
|
}
|
|
66
262
|
export class SDKError extends Error {
|
|
67
263
|
code: string;
|
|
@@ -72,8 +268,20 @@ export function normalizeBot(agent: CloudAvatarRecord): Bot;
|
|
|
72
268
|
export class GrokBotClient {
|
|
73
269
|
constructor(options: ClientOptions);
|
|
74
270
|
getMe(): Promise<Record<string, unknown>>;
|
|
75
|
-
listBots(options?: { includeTeamAgents?: boolean }): Promise<{ bots: Bot[]; truncated: boolean }>;
|
|
271
|
+
listBots(options?: { includeTeamAgents?: boolean; role?: string; addedTeamAgentsOnly?: boolean }): Promise<{ bots: Bot[]; truncated: boolean; slackConnections: SlackConnection[]; teamsConnections: TeamsConnection[] }>;
|
|
76
272
|
sendMessage(options: SendOptions): Promise<{ botId: string; messageId: string; delivery: 'accepted-box' | 'accepted-temporal' | 'duplicate'; dispatched: boolean }>;
|
|
273
|
+
listSessions(botId: string): Promise<{ sessions: BotSession[] }>;
|
|
274
|
+
listTranscript(options: TranscriptQuery): Promise<{ generation: number; entries: TranscriptEntry[]; cloudAgentPeerIds: string[]; cloudAgentPeerIdsKnown: boolean; unreadAnchor: UnreadAnchor | null }>;
|
|
275
|
+
listAgentStatuses(agentIds?: string[]): Promise<{ statuses: AgentStatus[] }>;
|
|
276
|
+
getSendStatus(options: { botId: string; messageId: string; sessionId?: string; firstSentAgeMs?: string }): Promise<{ status: string; echoEntryId: string | null; rejectionCode: string | null; acceptedAtMs: string | null }>;
|
|
277
|
+
listAgentTasks(options: { botId: string; includeRecentlyDone?: boolean }): Promise<{ tasks: AgentTask[] }>;
|
|
278
|
+
listTasks(options: { botId: string; sessionId?: string }): Promise<{ tasks: BotTask[] }>;
|
|
279
|
+
listSubagents(botId: string): Promise<{ subagents: RunningSubagent[] }>;
|
|
280
|
+
listAgentTodos(botId: string): Promise<{ todos: AgentTodo[] }>;
|
|
281
|
+
listAgentSkills(botId: string): Promise<{ skills: AgentSkill[] }>;
|
|
282
|
+
listDeletedAgentIds(agentIds?: string[]): Promise<{ agentIds: string[] }>;
|
|
283
|
+
getRuntimeCapabilities(): Promise<RuntimeCapabilities>;
|
|
284
|
+
getSandBoxRunState(): Promise<SandBoxRunState>;
|
|
77
285
|
watchBot(options: WatchOptions): AsyncGenerator<BotEvent, void, unknown>;
|
|
78
286
|
botEventsSSE(options: WatchOptions & { keepAliveMs?: number }): Response;
|
|
79
287
|
}
|
package/sdk.mjs
CHANGED
|
@@ -9,7 +9,19 @@ export {createLogin,pollLogin} from './core.mjs';
|
|
|
9
9
|
export {deriveDefaultAvatar,resolveAgentAvatar} from './avatar.mjs';
|
|
10
10
|
export {SDKError} from './errors.mjs';
|
|
11
11
|
|
|
12
|
+
const CLIENT_VERSION='0.75.8';
|
|
13
|
+
const SEND_STATUS={0:'unspecified',1:'not-found',2:'accepted',3:'rejected',4:'pending',5:'unknown-durability',GROK_BOT_SEND_STATUS_UNSPECIFIED:'unspecified',GROK_BOT_SEND_STATUS_NOT_FOUND:'not-found',GROK_BOT_SEND_STATUS_ACCEPTED:'accepted',GROK_BOT_SEND_STATUS_REJECTED:'rejected',GROK_BOT_SEND_STATUS_PENDING:'pending',GROK_BOT_SEND_STATUS_UNKNOWN_DURABILITY:'unknown-durability'};
|
|
14
|
+
const RUN_STATE={0:'unspecified',1:'absent',2:'hibernated',3:'running',4:'starting',SAND_BOX_RUN_STATE_UNSPECIFIED:'unspecified',SAND_BOX_RUN_STATE_ABSENT:'absent',SAND_BOX_RUN_STATE_HIBERNATED:'hibernated',SAND_BOX_RUN_STATE_RUNNING:'running',SAND_BOX_RUN_STATE_STARTING:'starting'};
|
|
15
|
+
|
|
12
16
|
function botKey(value){if(typeof value!=='string'||!value.length||value.length>128||!/^[A-Za-z0-9_-]+$/.test(value))throw new SDKError('BOT_ID_REQUIRED');return value;}
|
|
17
|
+
function botIds(values,{allowEmpty=false}={}){if(!Array.isArray(values)||(!allowEmpty&&!values.length))throw new SDKError('BOT_ID_REQUIRED');return values.map(botKey);}
|
|
18
|
+
function asText(value){return typeof value==='string'?value:'';}
|
|
19
|
+
function wireText(value){if(typeof value==='string')return value;if(typeof value==='number'&&Number.isFinite(value))return String(value);return '';}
|
|
20
|
+
function msText(value){if(typeof value==='string'&&/^-?\d{1,20}$/.test(value))return value;if(typeof value==='number'&&Number.isSafeInteger(value))return String(value);return null;}
|
|
21
|
+
function stringList(value){return Array.isArray(value)?value.filter(item=>typeof item==='string'):[];}
|
|
22
|
+
function wireList(value){return Array.isArray(value)?value.map(wireText).filter(item=>item.length>0):[];}
|
|
23
|
+
function mappedEnum(value,table){if(Object.hasOwn(table,value))return table[value];return wireText(value)||'unspecified';}
|
|
24
|
+
function objectRows(value){return Array.isArray(value)?value.filter(row=>row&&typeof row==='object'&&!Array.isArray(row)):[];}
|
|
13
25
|
function uint64(value){
|
|
14
26
|
if(typeof value==='number'&&!Number.isSafeInteger(value))throw new SDKError('CURSOR_INVALID');
|
|
15
27
|
const text=String(value??0);if(!/^\d{1,20}$/.test(text)||BigInt(text)>18446744073709551615n)throw new SDKError('CURSOR_INVALID');return String(BigInt(text));
|
|
@@ -27,38 +39,84 @@ function decodeEntry(entry){
|
|
|
27
39
|
try{const body=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(Buffer.from(entry.body,'base64')));return body&&typeof body==='object'&&!Array.isArray(body)?body:null;}catch{return null;}
|
|
28
40
|
}
|
|
29
41
|
|
|
42
|
+
function normalizeSlackConnection(row){
|
|
43
|
+
return {agentId:asText(row.agentId),connected:row.connected===true,workspaceName:typeof row.workspaceName==='string'?row.workspaceName:null,installUrl:typeof row.installUrl==='string'?row.installUrl:null,unavailableReason:typeof row.unavailableReason==='string'?row.unavailableReason:null,pendingAdminApproval:row.pendingAdminApproval===true,needsManifestUpdate:row.needsManifestUpdate===true,reinstallUrl:typeof row.reinstallUrl==='string'?row.reinstallUrl:null,approvalDenied:row.approvalDenied===true,appUnreachable:row.appUnreachable===true,removedInSlack:row.removedInSlack===true,approvalRequestUnsent:row.approvalRequestUnsent===true};
|
|
44
|
+
}
|
|
45
|
+
function normalizeInstalledTeam(row){
|
|
46
|
+
if(!row||typeof row!=='object'||Array.isArray(row))return null;
|
|
47
|
+
return {teamId:asText(row.teamId),displayName:asText(row.displayName),rscGranted:row.rscGranted===true,installedAtMs:msText(row.installedAtMs)};
|
|
48
|
+
}
|
|
49
|
+
function normalizeTeamsConnection(row){
|
|
50
|
+
return {agentId:asText(row.agentId),installState:wireText(row.installState),tenantId:typeof row.tenantId==='string'?row.tenantId:null,tenantDisplayName:typeof row.tenantDisplayName==='string'?row.tenantDisplayName:null,installedTeamCount:Number.isInteger(row.installedTeamCount)?row.installedTeamCount:0,installedTeams:objectRows(row.installedTeams).map(normalizeInstalledTeam).filter(Boolean)};
|
|
51
|
+
}
|
|
52
|
+
function normalizeSession(row){
|
|
53
|
+
return {agentId:asText(row.agentId),sessionId:asText(row.sessionId),kind:wireText(row.kind),createdAtMs:msText(row.createdAtMs),updatedAtMs:msText(row.updatedAtMs),lastActivityAtMs:msText(row.lastActivityAtMs)};
|
|
54
|
+
}
|
|
55
|
+
function normalizeTranscriptEntry(entry){
|
|
56
|
+
return {seq:uint64(entry?.seq),updatedSeq:uint64(entry?.updatedSeq??entry?.seq),entryId:typeof entry?.entryId==='string'?entry.entryId:null,entryKind:asText(entry?.entryKind),bodyOmitted:entry?.bodyOmitted===true,blobHash:typeof entry?.blobHash==='string'?entry.blobHash:null,body:decodeEntry(entry??{})};
|
|
57
|
+
}
|
|
58
|
+
function normalizeUnreadAnchor(anchor){
|
|
59
|
+
if(!anchor||typeof anchor!=='object'||Array.isArray(anchor))return null;
|
|
60
|
+
return {hasNewerRows:anchor.hasNewerRows===true,entryId:asText(anchor.entryId),seq:msText(anchor.seq),newerRows:Number.isInteger(anchor.newerRows)?anchor.newerRows:0};
|
|
61
|
+
}
|
|
62
|
+
function normalizeLiveState(row){
|
|
63
|
+
return {agentId:asText(row.agentId),sessionId:asText(row.sessionId),isRunning:row.isRunning===true,isComposingMessage:row.isComposingMessage===true,isRetrying:row.isRetrying===true,updatedAtMs:msText(row.updatedAtMs),staleAfterMs:msText(row.staleAfterMs),hasRunningSubagents:row.hasRunningSubagents===true,surfaceTaskId:asText(row.surfaceTaskId),workingTaskIds:stringList(row.workingTaskIds),activityKind:asText(row.activity?.kind)};
|
|
64
|
+
}
|
|
65
|
+
function normalizeClientState(row){
|
|
66
|
+
return {agentId:asText(row.agentId),lastViewedAtMs:msText(row.lastViewedAtMs),unreadCount:Number.isInteger(row.unreadCount)?row.unreadCount:0,lastActivityAtMs:msText(row.lastActivityAtMs),lastEntryId:asText(row.lastEntryId),lastEntryKind:asText(row.lastEntryKind),lastMessageId:asText(row.lastMessageId),lastMessagePreview:asText(row.lastMessagePreview),notificationsEnabled:row.notificationsEnabled===true,notifyOnUpdatesEnabled:row.notifyOnUpdatesEnabled===true,hiddenFromSidebar:row.hiddenFromSidebar===true,updatedAtMs:msText(row.updatedAtMs),lastMessageTaskId:asText(row.lastMessageTaskId),removedFromSidebar:row.removedFromSidebar===true};
|
|
67
|
+
}
|
|
68
|
+
function normalizeBoxState(state){
|
|
69
|
+
if(!state||typeof state!=='object'||Array.isArray(state))return null;
|
|
70
|
+
return {runState:mappedEnum(state.runState,RUN_STATE),recreateInFlight:state.recreateInFlight===true,imageUpdateAvailable:state.imageUpdateAvailable===true,hostVersion:typeof state.hostVersion==='string'?state.hostVersion:null,hostUpdateAvailable:state.hostUpdateAvailable===true,diskPressure:wireText(state.diskPressure),updatedAtMs:msText(state.updatedAtMs)};
|
|
71
|
+
}
|
|
72
|
+
function normalizeComputerAction(row){
|
|
73
|
+
const number=value=>typeof value==='number'&&Number.isFinite(value)?value:null;
|
|
74
|
+
return {agentId:asText(row.agentId),type:asText(row.type),x:number(row.x),y:number(row.y),button:asText(row.button),count:Number.isInteger(row.count)?row.count:0,atMs:msText(row.atMs)};
|
|
75
|
+
}
|
|
76
|
+
|
|
30
77
|
export function normalizeBot(agent){
|
|
31
78
|
if(!agent||typeof agent.agentId!=='string'||!agent.agentId)throw new SDKError('INVALID_BOT_RECORD');
|
|
32
79
|
const avatar=resolveAgentAvatar(agent);
|
|
33
|
-
|
|
34
|
-
return {id:agent.agentId,serverId:typeof agent.id==='string'?agent.id:null,name:text(agent.name),description:text(agent.description),title:text(agent.title),avatarShape:avatar.character.shape,avatarColor:avatar.character.color,avatar,harness:text(agent.harness),kind:text(agent.kind),sessionId:text(agent.viewerSessionId),teamId:Number.isInteger(agent.teamId)?agent.teamId:null,viewerIsOwner:agent.viewerIsOwner===true,viewerCanManage:agent.viewerCanManage===true};
|
|
80
|
+
return {id:agent.agentId,serverId:typeof agent.id==='string'?agent.id:null,legacyAgentId:asText(agent.legacyAgentId),name:asText(agent.name),description:asText(agent.description),title:asText(agent.title),avatarShape:avatar.character.shape,avatarColor:avatar.character.color,avatar,harness:asText(agent.harness),role:asText(agent.role),kind:wireText(agent.kind),visibility:wireText(agent.visibility),sessionId:asText(agent.viewerSessionId),teamId:Number.isInteger(agent.teamId)?agent.teamId:null,viewerIsOwner:agent.viewerIsOwner===true,viewerCanManage:agent.viewerCanManage===true,teamDefaultForViewer:agent.teamDefaultForViewer===true,ownerDisplayName:asText(agent.ownerDisplayName),draftingDisabled:agent.draftingDisabled===true,accessGrantsOnly:agent.accessGrantsOnly===true,memberAgentIds:stringList(agent.memberAgentIds),viewerAllowedActions:wireList(agent.viewerAllowedActions),createdAtMs:msText(agent.createdAtMs),updatedAtMs:msText(agent.updatedAtMs),voiceId:asText(agent.voiceId),voiceSpeed:typeof agent.voiceSpeed==='number'&&Number.isFinite(agent.voiceSpeed)?agent.voiceSpeed:null,voiceLanguage:asText(agent.voiceLanguage)};
|
|
35
81
|
}
|
|
36
82
|
|
|
37
83
|
export class GrokBotClient{
|
|
38
84
|
#options;
|
|
39
85
|
constructor(options){
|
|
40
86
|
if(typeof options?.accessToken!=='string'||!options.accessToken)throw new SDKError('MISSING_ACCESS_TOKEN');
|
|
41
|
-
this.#options={...options,version:options.version??
|
|
87
|
+
this.#options={...options,version:options.version??CLIENT_VERSION};
|
|
42
88
|
}
|
|
43
89
|
async #rpc(service,method,body,signal){
|
|
44
90
|
return requestJSON(`${API_ORIGIN}/aiserver.v1.${service}/${method}`,{headers:clientHeaders(this.#options),body,fetchImpl:this.#options.fetchImpl,signal});
|
|
45
91
|
}
|
|
92
|
+
async #data(service,method,body){
|
|
93
|
+
const response=await this.#rpc(service,method,body);
|
|
94
|
+
if(!response.ok)throw new SDKError('RPC_FAILED',response.httpStatus);
|
|
95
|
+
return response.data??{};
|
|
96
|
+
}
|
|
46
97
|
async getMe(){
|
|
47
98
|
const response=await this.#rpc('DashboardService','GetMe',{});
|
|
48
99
|
if(!response.ok||!(response.data?.authId||response.data?.userId))throw new SDKError('ACCOUNT_AUTH_FAILED',response.httpStatus);
|
|
49
100
|
return response.data;
|
|
50
101
|
}
|
|
51
|
-
async listBots({includeTeamAgents=true}={}){
|
|
52
|
-
const
|
|
102
|
+
async listBots({includeTeamAgents=true,role,addedTeamAgentsOnly}={}){
|
|
103
|
+
const body={includeTeamAgents};
|
|
104
|
+
if(role!==undefined){if(typeof role!=='string')throw new SDKError('MESSAGE_INVALID');body.role=role;}
|
|
105
|
+
if(addedTeamAgentsOnly!==undefined)body.addedTeamAgentsOnly=addedTeamAgentsOnly===true;
|
|
106
|
+
const response=await this.#rpc('GrokBotService','ListGrokBotAgents',body);
|
|
53
107
|
if(!response.ok)throw new SDKError('RPC_FAILED',response.httpStatus);
|
|
54
108
|
if(!Array.isArray(response.data?.agents))throw new SDKError('INVALID_ROSTER_RESPONSE');
|
|
55
|
-
return {bots:response.data.agents.map(normalizeBot),truncated:response.data.truncated===true};
|
|
109
|
+
return {bots:response.data.agents.map(normalizeBot),truncated:response.data.truncated===true,slackConnections:objectRows(response.data.slackConnections).map(normalizeSlackConnection),teamsConnections:objectRows(response.data.teamsConnections).map(normalizeTeamsConnection)};
|
|
56
110
|
}
|
|
57
111
|
|
|
58
|
-
async sendMessage({botId,text,sessionId='',messageId=randomUUID(),attachmentPaths=[],attachmentNames=[]}={}){
|
|
112
|
+
async sendMessage({botId,text,sessionId='',messageId=randomUUID(),attachmentPaths=[],attachmentNames=[],replyToId,replyQuote,taskId,language,richText,machineId}={}){
|
|
59
113
|
botKey(botId);
|
|
60
114
|
if(typeof text!=='string'||!text.trim()||typeof sessionId!=='string'||typeof messageId!=='string'||!messageId||!Array.isArray(attachmentPaths)||!Array.isArray(attachmentNames)||attachmentPaths.length!==attachmentNames.length||[...attachmentPaths,...attachmentNames].some(value=>typeof value!=='string'||!value))throw new SDKError('MESSAGE_INVALID');
|
|
61
|
-
const
|
|
115
|
+
const extra={};
|
|
116
|
+
const put=(key,value)=>{if(value===undefined)return;if(typeof value!=='string'||!value)throw new SDKError('MESSAGE_INVALID');extra[key]=value;};
|
|
117
|
+
put('replyToId',replyToId);put('replyQuote',replyQuote);put('taskId',taskId);put('language',language);put('machineId',machineId);
|
|
118
|
+
if(richText!==undefined){if(typeof richText!=='string')throw new SDKError('MESSAGE_INVALID');extra.richText=richText;}
|
|
119
|
+
const response=await this.#rpc('GrokBotService','SendGrokBotUserMessage',{agentId:botId,text,sessionId,messageId,attachmentPaths,attachmentNames,sentAtMs:String(Date.now()),isFork:false,source:1,initiator:1,sendSource:1,...extra});
|
|
62
120
|
if(!response.ok)throw new SDKError('SEND_UNCONFIRMED',response.httpStatus);
|
|
63
121
|
const deliveries={1:'accepted-box',2:'accepted-temporal',3:'duplicate',GROK_BOT_USER_MESSAGE_DELIVERY_ACCEPTED_BOX:'accepted-box',GROK_BOT_USER_MESSAGE_DELIVERY_ACCEPTED_TEMPORAL:'accepted-temporal',GROK_BOT_USER_MESSAGE_DELIVERY_DUPLICATE:'duplicate'};
|
|
64
122
|
const delivery=deliveries[response.data?.delivery];
|
|
@@ -66,6 +124,99 @@ export class GrokBotClient{
|
|
|
66
124
|
return {botId,messageId,delivery,dispatched:response.data.dispatched===true};
|
|
67
125
|
}
|
|
68
126
|
|
|
127
|
+
async listSessions(botId){
|
|
128
|
+
botKey(botId);
|
|
129
|
+
const data=await this.#data('GrokBotService','ListGrokBotAgentSessions',{agentId:botId});
|
|
130
|
+
if(!Array.isArray(data.sessions))throw new SDKError('RPC_FAILED');
|
|
131
|
+
return {sessions:objectRows(data.sessions).map(normalizeSession)};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async listTranscript({botId,sessionId='',limit=50,generation:generationValue,beforeSeq,unreadBoundaryMs}={}){
|
|
135
|
+
botKey(botId);
|
|
136
|
+
if(typeof sessionId!=='string'||sessionId.length>256||!Number.isInteger(limit)||limit<1||limit>4294967295)throw new SDKError('CURSOR_INVALID');
|
|
137
|
+
const body={agentId:botId,sessionId,limit};
|
|
138
|
+
if(generationValue!==undefined)body.generation=generation(generationValue);
|
|
139
|
+
if(beforeSeq!==undefined)body.beforeSeq=uint64(beforeSeq);
|
|
140
|
+
if(unreadBoundaryMs!==undefined)body.unreadBoundaryMs=uint64(unreadBoundaryMs);
|
|
141
|
+
const data=await this.#data('GrokBotService','ListGrokBotTranscriptEntries',body);
|
|
142
|
+
if(!Array.isArray(data.entries))throw new SDKError('RPC_FAILED');
|
|
143
|
+
return {generation:generation(data.generation),entries:data.entries.map(normalizeTranscriptEntry),cloudAgentPeerIds:stringList(data.cloudAgentPeerIds),cloudAgentPeerIdsKnown:data.cloudAgentPeerIdsKnown===true,unreadAnchor:normalizeUnreadAnchor(data.unreadAnchor)};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async listAgentStatuses(agentIds=[]){
|
|
147
|
+
const ids=botIds(agentIds,{allowEmpty:true});
|
|
148
|
+
const data=await this.#data('GrokBotService','ListGrokBotAgentStatuses',{agentIds:ids});
|
|
149
|
+
if(!Array.isArray(data.statuses))throw new SDKError('RPC_FAILED');
|
|
150
|
+
return {statuses:objectRows(data.statuses).map(row=>({agentId:asText(row.agentId),isRunning:row.isRunning===true,live:row.live&&typeof row.live==='object'&&!Array.isArray(row.live)?normalizeLiveState(row.live):null,client:row.client&&typeof row.client==='object'&&!Array.isArray(row.client)?normalizeClientState(row.client):null}))};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async getSendStatus({botId,messageId,sessionId='',firstSentAgeMs}={}){
|
|
154
|
+
botKey(botId);
|
|
155
|
+
if(typeof messageId!=='string'||!messageId||typeof sessionId!=='string')throw new SDKError('MESSAGE_INVALID');
|
|
156
|
+
const body={agentId:botId,messageId,sessionId};
|
|
157
|
+
if(firstSentAgeMs!==undefined)body.firstSentAgeMs=uint64(firstSentAgeMs);
|
|
158
|
+
const data=await this.#data('GrokBotService','GetGrokBotSendStatus',body);
|
|
159
|
+
return {status:mappedEnum(data.status,SEND_STATUS),echoEntryId:typeof data.echoEntryId==='string'?data.echoEntryId:null,rejectionCode:typeof data.rejectionCode==='string'?data.rejectionCode:null,acceptedAtMs:msText(data.acceptedAtMs)};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async listAgentTasks({botId,includeRecentlyDone}={}){
|
|
163
|
+
botKey(botId);
|
|
164
|
+
const body={agentId:botId};
|
|
165
|
+
if(includeRecentlyDone!==undefined)body.includeRecentlyDone=includeRecentlyDone===true;
|
|
166
|
+
const data=await this.#data('GrokBotService','ListGrokBotAgentTasks',body);
|
|
167
|
+
if(!Array.isArray(data.tasks))throw new SDKError('RPC_FAILED');
|
|
168
|
+
return {tasks:objectRows(data.tasks).map(row=>({id:asText(row.id),name:asText(row.name),description:asText(row.description),status:asText(row.status),updatedAtMs:msText(row.updatedAtMs),unreadEntryId:typeof row.unreadEntryId==='string'?row.unreadEntryId:null,unreadPreview:typeof row.unreadPreview==='string'?row.unreadPreview:null,unreadAtMs:msText(row.unreadAtMs),pinnedAtMs:msText(row.pinnedAtMs),subagentIds:stringList(row.subagentIds)}))};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async listTasks({botId,sessionId=''}={}){
|
|
172
|
+
botKey(botId);
|
|
173
|
+
if(typeof sessionId!=='string'||sessionId.length>256)throw new SDKError('CURSOR_INVALID');
|
|
174
|
+
const data=await this.#data('GrokBotService','ListGrokBotTasks',{agentId:botId,sessionId});
|
|
175
|
+
if(!Array.isArray(data.tasks))throw new SDKError('RPC_FAILED');
|
|
176
|
+
return {tasks:objectRows(data.tasks).map(row=>({id:asText(row.id),title:asText(row.title),state:wireText(row.state),note:asText(row.note),createdAtMs:msText(row.createdAtMs),doneAtMs:msText(row.doneAtMs),seenDoneAtMs:msText(row.seenDoneAtMs)}))};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async listSubagents(botId){
|
|
180
|
+
botKey(botId);
|
|
181
|
+
const data=await this.#data('GrokBotService','ListGrokBotSubagents',{agentId:botId});
|
|
182
|
+
if(!Array.isArray(data.subagents))throw new SDKError('RPC_FAILED');
|
|
183
|
+
return {subagents:objectRows(data.subagents).map(row=>({subagentId:asText(row.subagentId),subagentType:asText(row.subagentType),title:asText(row.title),startedAtMs:msText(row.startedAtMs),freshUntilMs:msText(row.freshUntilMs)}))};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async listAgentTodos(botId){
|
|
187
|
+
botKey(botId);
|
|
188
|
+
const data=await this.#data('GrokBotService','ListGrokBotAgentTodos',{agentId:botId});
|
|
189
|
+
if(!Array.isArray(data.todos))throw new SDKError('RPC_FAILED');
|
|
190
|
+
return {todos:objectRows(data.todos).map(row=>({id:asText(row.id),content:asText(row.content),status:wireText(row.status),createdAtMs:msText(row.createdAt),updatedAtMs:msText(row.updatedAt),dependencies:stringList(row.dependencies)}))};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async listAgentSkills(botId){
|
|
194
|
+
botKey(botId);
|
|
195
|
+
const data=await this.#data('GrokBotService','ListGrokBotAgentSkills',{agentId:botId});
|
|
196
|
+
if(!Array.isArray(data.skills))throw new SDKError('RPC_FAILED');
|
|
197
|
+
return {skills:objectRows(data.skills).map(row=>({id:asText(row.id),name:asText(row.name),description:asText(row.description),body:asText(row.body),source:asText(row.source)}))};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async listDeletedAgentIds(agentIds=[]){
|
|
201
|
+
const ids=botIds(agentIds,{allowEmpty:true});
|
|
202
|
+
const data=await this.#data('GrokBotService','ListDeletedGrokBotAgentIds',{agentIds:ids});
|
|
203
|
+
if(!Array.isArray(data.agentIds))throw new SDKError('RPC_FAILED');
|
|
204
|
+
return {agentIds:stringList(data.agentIds)};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async getRuntimeCapabilities(){
|
|
208
|
+
const data=await this.#data('GrokBotService','GetGrokBotRuntimeCapabilities',{});
|
|
209
|
+
const caps=data.capabilities;
|
|
210
|
+
if(!caps||typeof caps!=='object'||Array.isArray(caps))throw new SDKError('RPC_FAILED');
|
|
211
|
+
return {durableIdentityEnabled:caps.durableIdentityEnabled===true,durableIdentityWritesEnabled:caps.durableIdentityWritesEnabled===true,temporalCreationEnabled:caps.temporalCreationEnabled===true,agentMessagingEnabled:caps.agentMessagingEnabled===true,serverRoomsEnabled:caps.serverRoomsEnabled===true};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async getSandBoxRunState(){
|
|
215
|
+
const data=await this.#data('GrokBotService','GetSandBoxRunState',{});
|
|
216
|
+
const image=data.image&&typeof data.image==='object'&&!Array.isArray(data.image)?{registryTag:typeof data.image.registryTag==='string'?data.image.registryTag:null,staticSnapshotPath:typeof data.image.staticSnapshotPath==='string'?data.image.staticSnapshotPath:null}:null;
|
|
217
|
+
return {runState:mappedEnum(data.state,RUN_STATE),imageUpdateAvailable:data.imageUpdateAvailable===true,egressTunnelOwnership:data.egressTunnelOwnership===true,image};
|
|
218
|
+
}
|
|
219
|
+
|
|
69
220
|
async *watchBot({botId,sessionId='',cursor,resumeFrom,signal,inlineBodyMaxBytes=262144}={}){
|
|
70
221
|
botKey(botId);
|
|
71
222
|
if(typeof sessionId!=='string'||sessionId.length>256||cursor&&resumeFrom)throw new SDKError('CURSOR_INVALID');
|
|
@@ -113,8 +264,12 @@ export class GrokBotClient{
|
|
|
113
264
|
if(frame.cleared?.agentId===botId&&(frame.cleared.sessionId??'')===sessionId){state={generation:generation(frame.cleared.newGeneration),afterUpdatedSeq:'0'};yield {type:'reset',id:cursorID(botId,sessionId,state),data:{botId,generation:state.generation}};}
|
|
114
265
|
if(frame.cursorTooOld?.agentId===botId&&(frame.cursorTooOld.sessionId??'')===sessionId){yield {type:'resync-required',data:{botId,generation:generation(frame.cursorTooOld.generation)}};return;}
|
|
115
266
|
if(frame.turnFailed?.agentId===botId&&(frame.turnFailed.sessionId??'')===sessionId)yield {type:'turn.failed',data:{botId,turnId:frame.turnFailed.turnId??'',code:frame.turnFailed.code??0,summary:frame.turnFailed.summary??''}};
|
|
116
|
-
if(frame.agentStateChanged?.agentId===botId)yield {type:'bot.state.changed',data:{botId,families:frame.agentStateChanged.families
|
|
267
|
+
if(frame.agentStateChanged?.agentId===botId)yield {type:'bot.state.changed',data:{botId,families:stringList(frame.agentStateChanged.families),changedAtMs:msText(frame.agentStateChanged.changedAtMs)}};
|
|
117
268
|
if(frame.rosterChanged?.agentId===botId){const changed=frame.rosterChanged;if(!changed.agent||changed.agent.agentId===botId)yield {type:'bot.updated',data:{botId,changedFields:changed.changedFields??[],bot:changed.agent?normalizeBot(changed.agent):null}};}
|
|
269
|
+
if(frame.agentState){const live=objectRows(frame.agentState.live).filter(row=>row.agentId===botId&&asText(row.sessionId)===sessionId).map(normalizeLiveState);const client=objectRows(frame.agentState.client).filter(row=>row.agentId===botId).map(normalizeClientState);if(frame.agentState.snapshot===true||live.length||client.length)yield {type:'agent.state',data:{botId,sessionId,snapshot:frame.agentState.snapshot===true,live,client}};}
|
|
270
|
+
if(frame.computerActions){const actions=objectRows(frame.computerActions.actions).filter(row=>row.agentId===botId).map(normalizeComputerAction);if(actions.length)yield {type:'computer.actions',data:{botId,actions}};}
|
|
271
|
+
if(frame.boxState)yield {type:'box.state',data:{botId,snapshot:frame.boxState.snapshot===true,state:normalizeBoxState(frame.boxState.state)}};
|
|
272
|
+
if(frame.pageTitleChanged){const page=frame.pageTitleChanged;const wrapped=page.page&&typeof page.page==='object'?page.page:null;yield {type:'page.title.changed',data:{botId,storeId:asText(page.storeId),canvasId:wrapped?.case==='canvasId'?asText(wrapped.value):typeof page.canvasId==='string'?page.canvasId:null,documentId:wrapped?.case==='documentId'?asText(wrapped.value):typeof page.documentId==='string'?page.documentId:null,title:asText(page.title),changedAtMs:msText(page.changedAtMs)}};}
|
|
118
273
|
}
|
|
119
274
|
}catch(error){
|
|
120
275
|
if(signal?.aborted)return;
|