grok-bot-sdk 0.0.0-stage → 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.
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { dirname, resolve } from 'node:path';
5
+ import { createLogin, pollLogin } from '../core.mjs';
6
+ import { GrokBotClient } from '../sdk.mjs';
7
+
8
+ const args = process.argv.slice(2);
9
+ const command = args[0];
10
+ const option = name => {
11
+ const index = args.indexOf(name);
12
+ return index >= 0 ? args[index + 1] : undefined;
13
+ };
14
+ const failed = code => {
15
+ console.log(JSON.stringify({ status: 'failed', code }));
16
+ process.exitCode = 1;
17
+ };
18
+
19
+ function usage() {
20
+ console.log(`grok-bot-sdk login [--no-open] --out <file>
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>]
28
+
29
+ login opens the browser, waits for approval, and writes the session to <file> (mode 0600).
30
+ The command prints the login page URL and a redacted summary. It does not print tokens.
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.`);
33
+ process.exitCode = 2;
34
+ }
35
+
36
+ async function login() {
37
+ const out = option('--out');
38
+ if (!out || out.startsWith('-')) {
39
+ usage();
40
+ return;
41
+ }
42
+ const flow = createLogin();
43
+ console.log(JSON.stringify({ phase: 'browser-login', loginUrl: flow.loginUrl }));
44
+ if (!args.includes('--no-open') && process.platform === 'darwin') {
45
+ execFileSync('/usr/bin/open', [flow.loginUrl], { stdio: 'ignore' });
46
+ }
47
+ const session = await pollLogin(flow, {
48
+ onPending: attempt => {
49
+ if (attempt === 1 || attempt % 6 === 0) console.log(JSON.stringify({ phase: 'awaiting-browser-authorization', attempt }));
50
+ },
51
+ });
52
+ const path = resolve(out);
53
+ await mkdir(dirname(path), { recursive: true });
54
+ const body = {
55
+ accessToken: session.accessToken,
56
+ refreshToken: session.refreshToken,
57
+ ...session.selectedTeamId !== undefined ? { selectedTeamId: session.selectedTeamId } : {},
58
+ };
59
+ await writeFile(path, JSON.stringify(body, null, 2) + '\n', { mode: 0o600 });
60
+ console.log(JSON.stringify({
61
+ phase: 'saved',
62
+ path,
63
+ accessTokenPresent: true,
64
+ refreshTokenPresent: true,
65
+ selectedTeamIdPresent: session.selectedTeamId !== undefined,
66
+ persistedBySdk: false,
67
+ refreshImplemented: false,
68
+ }));
69
+ }
70
+
71
+ async function loadToken() {
72
+ const file = option('--token-file');
73
+ if (file) {
74
+ const parsed = JSON.parse(await readFile(resolve(file), 'utf8'));
75
+ if (typeof parsed?.accessToken !== 'string' || !parsed.accessToken) throw new Error('TOKEN_FILE_INVALID');
76
+ return { accessToken: parsed.accessToken, teamId: parsed.selectedTeamId, version: parsed.version };
77
+ }
78
+ const accessToken = process.env.GROK_BOT_ACCESS_TOKEN;
79
+ if (!accessToken) throw new Error('MISSING_ACCESS_TOKEN');
80
+ return { accessToken, version: process.env.GROK_BOT_CLIENT_VERSION };
81
+ }
82
+
83
+ async function clientFromToken() {
84
+ const loaded = await loadToken();
85
+ return new GrokBotClient({
86
+ accessToken: loaded.accessToken,
87
+ ...loaded.version ? { version: loaded.version } : {},
88
+ ...loaded.teamId !== undefined ? { teamId: loaded.teamId } : {},
89
+ });
90
+ }
91
+
92
+ async function whoami() {
93
+ const client = await clientFromToken();
94
+ const me = await client.getMe();
95
+ console.log(JSON.stringify({
96
+ status: 'ok',
97
+ identityPresent: Boolean(me.authId || me.userId),
98
+ emailPresent: Boolean(me.email),
99
+ teamPresent: me.teamId !== undefined && me.teamId !== null,
100
+ }));
101
+ }
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
+
193
+ try {
194
+ if (command === 'login') await login();
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();
202
+ else usage();
203
+ } catch (error) {
204
+ const code = error instanceof Error && /^[A-Z_]{1,80}$/.test(error.message) ? error.message : 'COMMAND_FAILED';
205
+ failed(code);
206
+ }
@@ -0,0 +1,36 @@
1
+ import {SDKError} from './errors.mjs';
2
+ export function encodeConnectFrame(data){
3
+ const payload=Buffer.from(JSON.stringify(data));
4
+ const header=Buffer.alloc(5);header.writeUInt32BE(payload.length,1);
5
+ return Buffer.concat([header,payload]);
6
+ }
7
+
8
+ export async function* readConnectFrames(body,{maxFrameBytes=1024*1024,onClose=()=>{}}={}){
9
+ if(!body?.getReader)throw new SDKError('STREAM_INVALID_RESPONSE');
10
+ const reader=body.getReader(),decoder=new TextDecoder('utf-8',{fatal:true});
11
+ let pending=Buffer.alloc(0);
12
+ try{
13
+ for(;;){
14
+ const {value,done}=await reader.read();
15
+ if(done)throw new SDKError('STREAM_UNEXPECTED_EOF');
16
+ pending=Buffer.concat([pending,Buffer.from(value)]);
17
+ while(pending.length>=5){
18
+ const flags=pending[0],length=pending.readUInt32BE(1);
19
+ if(length>maxFrameBytes)throw new SDKError('STREAM_FRAME_TOO_LARGE');
20
+ if(flags&1)throw new SDKError('STREAM_COMPRESSION_UNSUPPORTED');
21
+ if(flags!==0&&flags!==2)throw new SDKError('STREAM_FLAGS_INVALID');
22
+ if(pending.length<5+length)break;
23
+ let data;
24
+ try{data=JSON.parse(decoder.decode(pending.subarray(5,5+length)));}catch{throw new SDKError('STREAM_INVALID_JSON');}
25
+ pending=pending.subarray(5+length);
26
+ if(!data||typeof data!=='object'||Array.isArray(data))throw new SDKError('STREAM_INVALID_JSON');
27
+ if(flags===2){
28
+ if(Object.hasOwn(data,'error'))throw new SDKError('STREAM_RPC_ERROR');
29
+ if(pending.length)throw new SDKError('STREAM_END_INVALID');
30
+ return;
31
+ }
32
+ yield data;
33
+ }
34
+ }
35
+ }finally{onClose();await reader.cancel().catch(()=>{});reader.releaseLock();}
36
+ }
package/core.mjs ADDED
@@ -0,0 +1,135 @@
1
+ import { randomBytes, randomUUID, createHash, pbkdf2Sync, createDecipheriv } from 'node:crypto';
2
+ import { setTimeout as delay } from 'node:timers/promises';
3
+
4
+ export const API_ORIGIN = 'https://api2.cursor.sh';
5
+ const fixedError = code => new Error(code);
6
+
7
+ export function createLogin() {
8
+ const verifier = randomBytes(32).toString('base64url');
9
+ const uuid = randomUUID();
10
+ const url = new URL('/loginDeepControl', 'https://cursor.com');
11
+ url.search = new URLSearchParams({challenge:createHash('sha256').update(verifier).digest('base64url'),uuid,mode:'login',redirectTarget:'sand',supportsSelectedTeamLogin:'true'}).toString();
12
+ return {uuid,verifier,loginUrl:url.toString()};
13
+ }
14
+
15
+ export function decryptMacSecret(value, password) {
16
+ try {
17
+ const bytes = Buffer.from(value,'base64');
18
+ if (bytes.subarray(0,3).toString() !== 'v10' || bytes.length < 19) throw fixedError('FORMAT');
19
+ const key=pbkdf2Sync(password,'saltysalt',1003,16,'sha1');
20
+ try {
21
+ const decipher=createDecipheriv('aes-128-cbc',key,Buffer.alloc(16,32));
22
+ return Buffer.concat([decipher.update(bytes.subarray(3)),decipher.final()]).toString('utf8');
23
+ } finally {key.fill(0);}
24
+ } catch {throw fixedError('CREDENTIAL_DECRYPT_FAILED');}
25
+ }
26
+
27
+ export function selectDesktopAccount(doc) {
28
+ let record;
29
+ try {record=JSON.parse(doc['cursor-accounts']);} catch {throw fixedError('ACCOUNT_STORE_INVALID');}
30
+ if (typeof record.active !== 'string' || !record.accounts?.[record.active]) throw fixedError('NO_ACTIVE_ACCOUNT');
31
+ const active=record.accounts[record.active];
32
+ if (typeof active['cursor-access-token'] !== 'string') throw fixedError('ACCESS_TOKEN_MISSING');
33
+ return {encryptedAccessToken:active['cursor-access-token'],encryptedTeamId:active['cursor-selected-team-id'],storedMachineId:doc['cursor-machine-id']};
34
+ }
35
+
36
+ export function validateOrigin(value, kind) {
37
+ try {
38
+ const u=new URL(value);
39
+ const allowed=kind==='cursor' ? u.origin===API_ORIGIN : u.hostname.endsWith('.cursorvm.com');
40
+ if (!allowed || u.protocol!=='https:' || u.username || u.password || u.search || u.hash || (u.port && u.port!=='443')) throw fixedError('ORIGIN');
41
+ if (kind==='cursor' && u.pathname!=='/') throw fixedError('PATH');
42
+ return u.toString().replace(/\/$/,'');
43
+ } catch {throw fixedError('UNTRUSTED_ORIGIN');}
44
+ }
45
+
46
+ function checksum(machineId) {
47
+ const t=Math.floor(Date.now()/1e6);
48
+ const bytes=Uint8Array.from([t>>40&255,t>>32&255,t>>24&255,t>>16&255,t>>8&255,t&255]);
49
+ let previous=165;
50
+ for(let i=0;i<bytes.length;i++){bytes[i]=(bytes[i]^previous)+i%256;previous=bytes[i];}
51
+ return Buffer.from(bytes).toString('base64url')+machineId;
52
+ }
53
+
54
+ export function clientHeaders({accessToken,version,machineId,teamId}) {
55
+ const headers={
56
+ 'content-type':'application/json','connect-protocol-version':'1',
57
+ 'authorization':`Bearer ${accessToken}`,'x-cursor-client-type':'sand',
58
+ 'x-cursor-client-version':version,'x-cursor-client-source':'sand-desktop',
59
+ 'x-cursor-client-os':'darwin','x-sand-box-namespace':'prod','x-ghost-mode':'true',
60
+ 'x-request-id':randomUUID(),
61
+ 'user-agent':`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Sand/${version} Safari/537.36`,
62
+ };
63
+ if(machineId)headers['x-cursor-checksum']=checksum(machineId);
64
+ if(teamId!==undefined)headers['x-cursor-team-id']=String(teamId);
65
+ return headers;
66
+ }
67
+
68
+ export async function requestJSON(url,{method='POST',headers={},body,fetchImpl=fetch,timeoutMs=20000,signal}={}) {
69
+ try {
70
+ const timeout=AbortSignal.timeout(timeoutMs);
71
+ const response=await fetchImpl(url,{method,headers,body:body===undefined?undefined:JSON.stringify(body),redirect:'error',signal:signal?AbortSignal.any([signal,timeout]):timeout});
72
+ const text=await response.text();
73
+ if(Buffer.byteLength(text)>8*1024*1024) return {ok:false,httpStatus:response.status,code:'RESPONSE_TOO_LARGE'};
74
+ let data;
75
+ try {data=JSON.parse(text);} catch {return {ok:false,httpStatus:response.status,code:'NON_JSON_RESPONSE'};}
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'};
78
+ return {ok:true,httpStatus:response.status,data};
79
+ } catch {return {ok:false,code:'NETWORK_OR_TLS_ERROR'};}
80
+ }
81
+
82
+ export async function verify(options) {
83
+ const report={mode:options.mode??'desktop-existing-session',clientVersion:options.version,steps:[]};
84
+ const headers=clientHeaders(options);
85
+ const rpc=async(method,body)=>requestJSON(API_ORIGIN+'/'+method,{headers,body,fetchImpl:options.fetchImpl});
86
+ const checked=(result,predicate)=>result.ok&&!predicate(result.data)?{ok:false,httpStatus:result.httpStatus,code:'INVALID_RESPONSE'}:result;
87
+ const record=(name,result,extra={})=>report.steps.push({name,status:result.ok?'passed':'failed',httpStatus:result.httpStatus,...result.code?{code:result.code}:{},...extra});
88
+ const me=checked(await rpc('aiserver.v1.DashboardService/GetMe',{}),data=>!!(data?.authId||data?.userId));
89
+ record('account.GetMe',me,{...me.ok?{identityPresent:!!(me.data.authId||me.data.userId),emailPresent:!!me.data.email}:{}});
90
+ if(!me.ok)return report;
91
+ if(options.cloudRoster){
92
+ const roster=checked(await rpc('aiserver.v1.GrokBotService/ListGrokBotAgents',{includeTeamAgents:true}),data=>Array.isArray(data?.agents)&&data.agents.every(x=>x&&typeof x==='object'));
93
+ record('cloud.ListGrokBotAgents',roster,{...roster.ok?{agentsArrayPresent:Array.isArray(roster.data.agents)}:{}});
94
+ if(roster.ok&&Array.isArray(roster.data.agents)){
95
+ report.cloudBotCount=roster.data.agents.length;
96
+ report.cloudRoster={truncated:roster.data.truncated===true,avatarMetadataCount:roster.data.agents.filter(x=>!!(x.avatarShape||x.avatarColor||x.avatarDataUrl)).length,harnessCounts:roster.data.agents.reduce((counts,x)=>{const harness=typeof x.harness==='string'&&/^[a-z_]{1,30}$/.test(x.harness)?x.harness:'other';counts[harness]=(counts[harness]??0)+1;return counts;},{})};
97
+ }
98
+ if(options.cloudOnly)return report;
99
+ }
100
+ const box=checked(await rpc('aiserver.v1.GrokBotService/EnsureSandBox',{wake:false,supportsAsleepBox:true}),data=>data&&typeof data==='object'&&!Array.isArray(data)&&data.runState!==undefined);
101
+ record('broker.EnsureSandBox(no-wake)',box);
102
+ if(!box.ok)return report;
103
+ const {gatewayUrl,gatewayToken,networkToken,runState}=box.data;
104
+ report.connection={gatewayURLPresent:!!gatewayUrl,gatewayTokenPresent:!!gatewayToken,networkTokenPresent:!!networkToken,runState:typeof runState==='string'&&/^[A-Z_]{1,80}$/.test(runState)?runState:typeof runState==='number'?runState:'unknown'};
105
+ if(!gatewayUrl || !gatewayToken){report.steps.push({name:'gateway',status:'host_not_running'});return report;}
106
+ let base;
107
+ try{base=validateOrigin(gatewayUrl,'gateway');}catch{report.steps.push({name:'gateway',status:'failed',code:'UNTRUSTED_ORIGIN'});return report;}
108
+ const gatewayHeaders={'authorization':`Bearer ${gatewayToken}`,'content-type':'application/json'};
109
+ if(networkToken)gatewayHeaders['x-anyrun-network-token']=networkToken;
110
+ const health=checked(await requestJSON(base+'/health',{method:'GET',headers:gatewayHeaders,fetchImpl:options.fetchImpl}),data=>data?.ok===true);
111
+ record('gateway.health',health,{...health.ok?{reportedOK:health.data.ok===true}:{}});
112
+ if(!health.ok)return report;
113
+ const roster=checked(await requestJSON(base+'/api/listAgents',{headers:gatewayHeaders,body:{},fetchImpl:options.fetchImpl}),data=>Array.isArray(data)&&data.every(x=>x&&typeof x==='object'));
114
+ record('gateway.listAgents',roster,{...roster.ok?{arrayResponse:Array.isArray(roster.data)}:{}});
115
+ if(roster.ok&&Array.isArray(roster.data)){
116
+ report.botCount=roster.data.length;
117
+ report.rosterMetadata={hiddenCount:roster.data.filter(x=>x.isHiddenFromSidebar===true).length,groupCount:roster.data.filter(x=>x.isGroup===true).length,avatarCount:roster.data.filter(x=>!!(x.avatarDataUrl||x.avatarVersion||x.avatarShape)).length};
118
+ }
119
+ return report;
120
+ }
121
+
122
+ export async function pollLogin(flow,{fetchImpl=fetch,sleep=delay,maxAttempts=150,onPending=()=>{}}={}) {
123
+ const url=new URL('/auth/poll',API_ORIGIN);
124
+ url.search=new URLSearchParams({uuid:flow.uuid,verifier:flow.verifier}).toString();
125
+ let errors=0;
126
+ for(let attempt=0;attempt<maxAttempts;attempt++){
127
+ const result=await requestJSON(url,{method:'GET',headers:{'content-type':'application/json'},fetchImpl});
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');
130
+ if(result.httpStatus===404){errors=0;onPending(attempt+1);}
131
+ else if(++errors>=3)throw fixedError('LOGIN_POLL_FAILED');
132
+ await sleep(Math.min(1000*1.2**attempt,10000));
133
+ }
134
+ throw fixedError('LOGIN_TIMED_OUT');
135
+ }
package/desktop.d.mts ADDED
@@ -0,0 +1,3 @@
1
+ import type { ClientOptions } from './sdk.mjs';
2
+ export function version(): string;
3
+ export function desktopCredentials(): Promise<ClientOptions & {version: string}>;
package/desktop.mjs ADDED
@@ -0,0 +1,28 @@
1
+ import {readFile} from 'node:fs/promises';
2
+ import {execFileSync} from 'node:child_process';
3
+ import {homedir} from 'node:os';
4
+ import {join} from 'node:path';
5
+ import {selectDesktopAccount,decryptMacSecret} from './core.mjs';
6
+
7
+ const app='/Applications/Grok Bot.app';
8
+ export function version(){return execFileSync('/usr/libexec/PlistBuddy',['-c','Print :CFBundleShortVersionString',app+'/Contents/Info.plist'],{encoding:'utf8',stdio:['ignore','pipe','pipe']}).trim();}
9
+ export async function desktopCredentials(){
10
+ const doc=JSON.parse(await readFile(join(homedir(),'Library/Application Support/Grok Bot/sand-secrets.json'),'utf8'));
11
+ const selected=selectDesktopAccount(doc);
12
+ let password;
13
+ try{password=execFileSync('/usr/bin/security',['find-generic-password','-s','Grok Bot Safe Storage','-w'],{encoding:'utf8',timeout:15000,stdio:['ignore','pipe','pipe']}).trimEnd();}
14
+ catch{throw new Error('KEYCHAIN_ACCESS_UNAVAILABLE');}
15
+ const accessToken=decryptMacSecret(selected.encryptedAccessToken,password);
16
+ let machineId,teamId;
17
+ if(selected.storedMachineId){
18
+ const wrapped=selected.storedMachineId.replace(/^scoped:v1:[a-f0-9]{64}:/,'');
19
+ if(wrapped.startsWith('plaintext:v1:'))machineId=wrapped.slice('plaintext:v1:'.length);
20
+ else machineId=decryptMacSecret(wrapped,password);
21
+ }
22
+ if(selected.encryptedTeamId){
23
+ const value=decryptMacSecret(selected.encryptedTeamId,password);
24
+ if(/^\d+$/.test(value))teamId=Number(value);
25
+ }
26
+ password=undefined;
27
+ return {accessToken,machineId,teamId,version:version()};
28
+ }
package/errors.mjs ADDED
@@ -0,0 +1,4 @@
1
+ const CODES=new Set(['SDK_ERROR','MISSING_ACCESS_TOKEN','RPC_FAILED','ACCOUNT_AUTH_FAILED','INVALID_ROSTER_RESPONSE','INVALID_BOT_RECORD','BOT_ID_REQUIRED','MESSAGE_INVALID','SEND_UNCONFIRMED','SEND_REFUSED','CURSOR_INVALID','CURSOR_SCOPE_MISMATCH','STREAM_CONNECTION_FAILED','STREAM_RPC_ERROR','STREAM_FRAME_TOO_LARGE','STREAM_UNEXPECTED_EOF','STREAM_COMPRESSION_UNSUPPORTED','STREAM_FLAGS_INVALID','STREAM_INVALID_JSON','STREAM_END_INVALID','STREAM_INVALID_RESPONSE','STREAM_PROTOCOL_ERROR','STREAM_CANCELLED']);
2
+ export class SDKError extends Error{
3
+ constructor(code,httpStatus){super(CODES.has(code)?code:'SDK_ERROR');this.name='SDKError';this.code=this.message;if(Number.isInteger(httpStatus))this.httpStatus=httpStatus;}
4
+ }
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "grok-bot-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
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
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": "RongleCat (铁柱AGI)",
8
+ "homepage": "https://ronglecat.github.io/grok-bot-sdk/",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/RongleCat/grok-bot-sdk.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/RongleCat/grok-bot-sdk/issues"
15
+ },
16
+ "keywords": [
17
+ "grok",
18
+ "grok-bot",
19
+ "sdk"
20
+ ],
21
+ "engines": {
22
+ "node": ">=22.13"
23
+ },
24
+ "bin": {
25
+ "grok-bot-sdk": "./bin/grok-bot-sdk.mjs"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./sdk.d.mts",
30
+ "import": "./sdk.mjs"
31
+ },
32
+ "./avatar": {
33
+ "types": "./avatar.d.mts",
34
+ "import": "./avatar.mjs"
35
+ },
36
+ "./desktop": {
37
+ "types": "./desktop.d.mts",
38
+ "import": "./desktop.mjs"
39
+ }
40
+ },
41
+ "types": "./sdk.d.mts",
42
+ "files": [
43
+ "*.mjs",
44
+ "*.d.mts",
45
+ "bin",
46
+ "README.md",
47
+ "README_ZH.md",
48
+ "CHANGELOG.md",
49
+ "LICENSE"
50
+ ],
51
+ "scripts": {
52
+ "test": "node --test test/*.test.mjs",
53
+ "typecheck": "tsc -p tsconfig.json --noEmit",
54
+ "docs:dev": "vitepress dev docs",
55
+ "docs:build": "vitepress build docs",
56
+ "docs:preview": "vitepress preview docs"
57
+ },
58
+ "devDependencies": {
59
+ "typescript": "^5.9.3",
60
+ "vitepress": "^1.6.4"
61
+ }
62
+ }