grok-bot-cli 0.10.2 → 0.11.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.
- package/CHANGELOG.md +15 -0
- package/README.md +21 -2
- package/dist/.claude-plugin/marketplace.json +1 -1
- package/dist/.claude-plugin/plugin.json +1 -1
- package/dist/.codex-plugin/plugin.json +1 -1
- package/dist/.cursor-plugin/plugin.json +1 -1
- package/dist/INSTALL.md +1 -1
- package/dist/README.md +21 -2
- package/dist/agent-bundle.compile-evidence.json +1 -1
- package/dist/agent-bundle.manifest.json +1 -1
- package/dist/bin/gbot-flight.mjs +6273 -1609
- package/dist/bin/gbot.mjs +5181 -216
- package/dist/install.mjs +1 -1
- package/dist/mcp/mcp-grok-bot-b8c2461e-flight.mjs +5643 -636
- package/dist/mcp/mcp-grok-bot-b8c2461e.mjs +5107 -109
- package/dist/package.json +1 -1
- package/dist/plugin.json +1 -1
- package/dist/scripts/gbot-relay.mjs +397 -24
- package/dist/skills/chatgpt-desktop/SKILL.md +75 -0
- package/package.json +1 -1
package/dist/package.json
CHANGED
package/dist/plugin.json
CHANGED
|
@@ -1 +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 from Codex, Claude Code, and Cursor. Codex/Claude tools use local sockets on the user's registered machines only (no remote transport); from the Grok Bot box, run gbot via Grok Bot Shell with a machineId.","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.
|
|
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 from Codex, Claude Code, and Cursor. Codex/Claude tools use local sockets on the user's registered machines only (no remote transport); from the Grok Bot box, run gbot via Grok Bot Shell with a machineId.","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.11.0"}
|
|
@@ -22133,8 +22133,10 @@ var __webpack_modules__ = {
|
|
|
22133
22133
|
var node_child_process__rspack_import_5 = __webpack_require__("node:child_process");
|
|
22134
22134
|
var _package_json__rspack_import_6 = __webpack_require__("./package.json");
|
|
22135
22135
|
var _codex_contract_js__rspack_import_7 = __webpack_require__("./src/core/codex/contract.js");
|
|
22136
|
-
var
|
|
22137
|
-
var
|
|
22136
|
+
var _codex_remote_control_js__rspack_import_8 = __webpack_require__("./src/core/codex/remote-control.js");
|
|
22137
|
+
var _codex_thread_id_js__rspack_import_11 = __webpack_require__("./src/core/codex/thread-id.js");
|
|
22138
|
+
var _desktop_shim_js__rspack_import_9 = __webpack_require__("./src/core/desktop-shim.js");
|
|
22139
|
+
var _user_machine_guidance_js__rspack_import_10 = __webpack_require__("./src/core/user-machine-guidance.js");
|
|
22138
22140
|
const PINNED_CODEX_VERSION = "0.158.0";
|
|
22139
22141
|
const UPSTREAM_DESKTOP_ISSUES = [
|
|
22140
22142
|
"https://github.com/openai/codex/issues/41014",
|
|
@@ -22142,8 +22144,9 @@ var __webpack_modules__ = {
|
|
|
22142
22144
|
];
|
|
22143
22145
|
const WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
22144
22146
|
const WS_MAX_HEADER_BYTES = 16 * 1024;
|
|
22145
|
-
const WS_MAX_MESSAGE_BYTES =
|
|
22146
|
-
const WS_MAX_BUFFER_BYTES =
|
|
22147
|
+
const WS_MAX_MESSAGE_BYTES = 16 * 1024 * 1024;
|
|
22148
|
+
const WS_MAX_BUFFER_BYTES = 32 * 1024 * 1024;
|
|
22149
|
+
const CODEX_SEND_MAX_BYTES = 4 * 1024 * 1024;
|
|
22147
22150
|
const WS_MAX_WRITE_BYTES = 8 * 1024 * 1024;
|
|
22148
22151
|
const CODEX_MAX_REQUESTS = 128;
|
|
22149
22152
|
const CODEX_MAX_LISTENERS = 128;
|
|
@@ -22171,11 +22174,11 @@ var __webpack_modules__ = {
|
|
|
22171
22174
|
function boxUnreachableMessage(path) {
|
|
22172
22175
|
return [
|
|
22173
22176
|
"No Codex app-server control socket at " + path + ".",
|
|
22174
|
-
|
|
22177
|
+
_user_machine_guidance_js__rspack_import_10.M
|
|
22175
22178
|
].join("\n");
|
|
22176
22179
|
}
|
|
22177
22180
|
function unreachableMessage(path, desktopAttached = "unknown", env = process.env) {
|
|
22178
|
-
if ((0,
|
|
22181
|
+
if ((0, _user_machine_guidance_js__rspack_import_10.R)({
|
|
22179
22182
|
path,
|
|
22180
22183
|
env,
|
|
22181
22184
|
home: env.HOME || (0, node_os__rspack_import_3.homedir)()
|
|
@@ -23014,6 +23017,13 @@ var __webpack_modules__ = {
|
|
|
23014
23017
|
if (value == null) return null;
|
|
23015
23018
|
return typeof value === "string" ? singleLine(value) : value;
|
|
23016
23019
|
}
|
|
23020
|
+
function summarizeSection(section) {
|
|
23021
|
+
if (!isObject(section) || typeof section.id !== "string" || !section.id) return null;
|
|
23022
|
+
return {
|
|
23023
|
+
id: singleLine(section.id),
|
|
23024
|
+
name: lineField(section.name)
|
|
23025
|
+
};
|
|
23026
|
+
}
|
|
23017
23027
|
function summarizeThread(t) {
|
|
23018
23028
|
if (!isObject(t) || typeof t.id !== "string" || !t.id) throw new CodexProtocolError("thread/list", "entry without a string `id`");
|
|
23019
23029
|
const type = isObject(t.status) && typeof t.status.type === "string" ? t.status.type : "unknown";
|
|
@@ -23025,43 +23035,72 @@ var __webpack_modules__ = {
|
|
|
23025
23035
|
preview: typeof t.preview === "string" ? stripTerminalControls(t.preview) : "",
|
|
23026
23036
|
cwd: lineField(t.cwd),
|
|
23027
23037
|
source: sourceField(t.source),
|
|
23028
|
-
|
|
23038
|
+
createdAt: typeof t.createdAt === "number" ? t.createdAt : null,
|
|
23039
|
+
updatedAt: typeof t.updatedAt === "number" ? t.updatedAt : null,
|
|
23040
|
+
section: summarizeSection(t.section),
|
|
23041
|
+
projectId: lineField(t.projectId),
|
|
23042
|
+
modelProvider: lineField(t.modelProvider),
|
|
23043
|
+
model: lineField(t.model),
|
|
23044
|
+
originator: lineField(t.originator)
|
|
23029
23045
|
};
|
|
23030
23046
|
}
|
|
23031
23047
|
const THREAD_LIST_MAX_LIMIT = 200;
|
|
23032
|
-
async function listCodexThreads({ limit = 20, cursor, env = process.env } = {}) {
|
|
23048
|
+
async function listCodexThreads({ limit = 20, cursor, modelProviders = [], env = process.env } = {}) {
|
|
23033
23049
|
if (!Number.isInteger(limit) || limit < 1 || limit > THREAD_LIST_MAX_LIMIT) {
|
|
23034
23050
|
throw new RangeError("--limit must be an integer 1-" + THREAD_LIST_MAX_LIMIT);
|
|
23035
23051
|
}
|
|
23036
23052
|
if (cursor !== undefined && (typeof cursor !== "string" || !cursor)) throw new RangeError("--cursor must be a non-empty string");
|
|
23053
|
+
if (!Array.isArray(modelProviders) || modelProviders.some((p)=>typeof p !== "string")) {
|
|
23054
|
+
throw new RangeError("modelProviders must be an array of strings (empty = all providers)");
|
|
23055
|
+
}
|
|
23037
23056
|
const { client } = await openSession(env);
|
|
23038
23057
|
try {
|
|
23039
|
-
const
|
|
23058
|
+
const baseParams = {
|
|
23040
23059
|
limit,
|
|
23041
|
-
|
|
23060
|
+
modelProviders,
|
|
23042
23061
|
...cursor !== undefined ? {
|
|
23043
23062
|
cursor
|
|
23044
23063
|
} : {}
|
|
23045
23064
|
};
|
|
23046
23065
|
let out;
|
|
23066
|
+
let usedStateDbOnly = true;
|
|
23047
23067
|
try {
|
|
23048
|
-
out = await client.request("thread/list",
|
|
23068
|
+
out = await client.request("thread/list", {
|
|
23069
|
+
...baseParams,
|
|
23070
|
+
useStateDbOnly: true
|
|
23071
|
+
});
|
|
23049
23072
|
} catch (err) {
|
|
23050
23073
|
throw transportError(err);
|
|
23051
23074
|
}
|
|
23052
23075
|
if (!isObject(out) || !Array.isArray(out.data)) throw new CodexProtocolError("thread/list", "missing `data` array");
|
|
23076
|
+
if (out.data.length === 0 && cursor === undefined && (out.nextCursor == null || out.nextCursor === "")) {
|
|
23077
|
+
try {
|
|
23078
|
+
out = await client.request("thread/list", {
|
|
23079
|
+
...baseParams,
|
|
23080
|
+
useStateDbOnly: false
|
|
23081
|
+
});
|
|
23082
|
+
usedStateDbOnly = false;
|
|
23083
|
+
} catch (err) {
|
|
23084
|
+
throw transportError(err);
|
|
23085
|
+
}
|
|
23086
|
+
if (!isObject(out) || !Array.isArray(out.data)) throw new CodexProtocolError("thread/list", "missing `data` array");
|
|
23087
|
+
}
|
|
23053
23088
|
if (out.nextCursor != null && typeof out.nextCursor !== "string") throw new CodexProtocolError("thread/list", "`nextCursor` is not a string");
|
|
23054
23089
|
const threads = out.data.map(summarizeThread);
|
|
23055
23090
|
return {
|
|
23056
23091
|
threads,
|
|
23057
23092
|
nextCursor: out.nextCursor ?? null,
|
|
23058
|
-
limit
|
|
23093
|
+
limit,
|
|
23094
|
+
useStateDbOnly: usedStateDbOnly
|
|
23059
23095
|
};
|
|
23060
23096
|
} finally{
|
|
23061
23097
|
client.close();
|
|
23062
23098
|
}
|
|
23063
23099
|
}
|
|
23064
|
-
function explainSendError(err, threadId) {
|
|
23100
|
+
function explainSendError(err, threadId, env = process.env) {
|
|
23101
|
+
if (err instanceof _codex_remote_control_js__rspack_import_8.Rg) return err;
|
|
23102
|
+
const mapped = (0, _codex_remote_control_js__rspack_import_8.R3)(err, threadId, env);
|
|
23103
|
+
if (mapped instanceof _codex_remote_control_js__rspack_import_8.Rg) return mapped;
|
|
23065
23104
|
if (!(err instanceof CodexRpcError)) return err;
|
|
23066
23105
|
const msg = String(err.rpc && err.rpc.message || "");
|
|
23067
23106
|
if (/no rollout found|thread not found/i.test(msg)) {
|
|
@@ -23136,12 +23175,19 @@ var __webpack_modules__ = {
|
|
|
23136
23175
|
function assertThreadAllowed(threadId, env = process.env) {
|
|
23137
23176
|
const raw = env.GROK_BOT_CODEX_THREADS;
|
|
23138
23177
|
if (raw == null || raw.trim() === "") return;
|
|
23139
|
-
const
|
|
23140
|
-
|
|
23141
|
-
|
|
23178
|
+
const bare = (0, _codex_thread_id_js__rspack_import_11.SX)(threadId);
|
|
23179
|
+
const allowed = raw.split(",").map((s)=>{
|
|
23180
|
+
try {
|
|
23181
|
+
return (0, _codex_thread_id_js__rspack_import_11.SX)(s.trim());
|
|
23182
|
+
} catch {
|
|
23183
|
+
return s.trim();
|
|
23184
|
+
}
|
|
23185
|
+
}).filter(Boolean);
|
|
23186
|
+
if (!allowed.includes(bare)) {
|
|
23187
|
+
throw new CodexSendError("Codex thread " + bare + " is not in GROK_BOT_CODEX_THREADS; the operator allows only: " + allowed.join(", "), {
|
|
23142
23188
|
delivery: "rejected",
|
|
23143
23189
|
reason: "route-not-allowed",
|
|
23144
|
-
threadId
|
|
23190
|
+
threadId: bare
|
|
23145
23191
|
});
|
|
23146
23192
|
}
|
|
23147
23193
|
}
|
|
@@ -23211,6 +23257,7 @@ var __webpack_modules__ = {
|
|
|
23211
23257
|
}
|
|
23212
23258
|
async function listCodexQueue(threadId, { env = process.env, limit = 50, cursor } = {}) {
|
|
23213
23259
|
requireExperimental(env, "gbot codex queue");
|
|
23260
|
+
threadId = normalizeCodexThreadId(threadId);
|
|
23214
23261
|
const { client } = await openSession(env, {
|
|
23215
23262
|
experimental: true
|
|
23216
23263
|
});
|
|
@@ -23241,6 +23288,64 @@ var __webpack_modules__ = {
|
|
|
23241
23288
|
client.close();
|
|
23242
23289
|
}
|
|
23243
23290
|
}
|
|
23291
|
+
async function readCodexThread(threadId, { limit = 100, env = process.env } = {}) {
|
|
23292
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 2000) {
|
|
23293
|
+
throw new RangeError("limit must be an integer 1-2000");
|
|
23294
|
+
}
|
|
23295
|
+
const bare = normalizeCodexThreadId(threadId);
|
|
23296
|
+
const { client } = await openSession(env);
|
|
23297
|
+
try {
|
|
23298
|
+
let meta;
|
|
23299
|
+
try {
|
|
23300
|
+
meta = await client.request("thread/read", {
|
|
23301
|
+
threadId: bare
|
|
23302
|
+
});
|
|
23303
|
+
} catch (err) {
|
|
23304
|
+
const mapped = explainSendError(err, bare, env);
|
|
23305
|
+
if (mapped instanceof RemoteThreadNotLoadedError) throw mapped;
|
|
23306
|
+
throw transportError(err);
|
|
23307
|
+
}
|
|
23308
|
+
const turns = [];
|
|
23309
|
+
let cursor;
|
|
23310
|
+
const seen = new Set();
|
|
23311
|
+
for(let page = 0; page < 40 && turns.length < limit; page++){
|
|
23312
|
+
let out;
|
|
23313
|
+
try {
|
|
23314
|
+
out = await client.request("thread/turns/list", {
|
|
23315
|
+
threadId: bare,
|
|
23316
|
+
limit: Math.min(100, limit - turns.length),
|
|
23317
|
+
itemsView: "full",
|
|
23318
|
+
sortDirection: "desc",
|
|
23319
|
+
...cursor !== undefined ? {
|
|
23320
|
+
cursor
|
|
23321
|
+
} : {}
|
|
23322
|
+
});
|
|
23323
|
+
} catch (err) {
|
|
23324
|
+
const mapped = explainSendError(err, bare, env);
|
|
23325
|
+
if (mapped instanceof RemoteThreadNotLoadedError) throw mapped;
|
|
23326
|
+
throw transportError(err);
|
|
23327
|
+
}
|
|
23328
|
+
if (!isObject(out) || !Array.isArray(out.data)) {
|
|
23329
|
+
throw new CodexProtocolError("thread/turns/list", "missing `data` array");
|
|
23330
|
+
}
|
|
23331
|
+
for (const row of out.data)turns.push(row);
|
|
23332
|
+
cursor = out.nextCursor ?? null;
|
|
23333
|
+
if (cursor === null) break;
|
|
23334
|
+
if (typeof cursor !== "string" || !cursor || seen.has(cursor)) {
|
|
23335
|
+
throw new CodexProtocolError("thread/turns/list", "invalid or repeated cursor");
|
|
23336
|
+
}
|
|
23337
|
+
seen.add(cursor);
|
|
23338
|
+
}
|
|
23339
|
+
return {
|
|
23340
|
+
threadId: bare,
|
|
23341
|
+
thread: isObject(meta) && isObject(meta.thread) ? meta.thread : null,
|
|
23342
|
+
turns: turns.slice(0, limit),
|
|
23343
|
+
nextCursor: cursor ?? null
|
|
23344
|
+
};
|
|
23345
|
+
} finally{
|
|
23346
|
+
client.close();
|
|
23347
|
+
}
|
|
23348
|
+
}
|
|
23244
23349
|
async function sendToCodexThread(threadId, text, { env = process.env, envelope = buildEnvelope({
|
|
23245
23350
|
env
|
|
23246
23351
|
}), whenBusy = "reject", session, expectedTurnId, expectedCwd, model, effort, signal } = {}) {
|
|
@@ -23286,8 +23391,8 @@ var __webpack_modules__ = {
|
|
|
23286
23391
|
] : []
|
|
23287
23392
|
].includes(whenBusy)) throw new RangeError("--when-busy must be reject, queue, or persistent steer");
|
|
23288
23393
|
if (whenBusy === "steer" && (typeof expectedTurnId !== "string" || !ID_PATTERN.test(expectedTurnId))) throw new RangeError("steer requires expectedTurnId");
|
|
23289
|
-
|
|
23290
|
-
if (typeof text !== "string" || !text.trim() || Buffer.byteLength(text) >
|
|
23394
|
+
threadId = (0, _codex_thread_id_js__rspack_import_11.SX)(threadId);
|
|
23395
|
+
if (typeof text !== "string" || !text.trim() || Buffer.byteLength(text) > CODEX_SEND_MAX_BYTES) throw new RangeError("Message must contain text within 4 MiB");
|
|
23291
23396
|
if (!envelope || typeof envelope.messageId !== "string" || !ID_PATTERN.test(envelope.messageId)) throw new RangeError("Invalid envelope messageId");
|
|
23292
23397
|
const validated = buildEnvelope({
|
|
23293
23398
|
correlationId: envelope.correlationId,
|
|
@@ -23335,8 +23440,10 @@ var __webpack_modules__ = {
|
|
|
23335
23440
|
});
|
|
23336
23441
|
} catch (err) {
|
|
23337
23442
|
assertNotCancelled();
|
|
23338
|
-
if (err instanceof CodexSendError) throw err;
|
|
23339
|
-
|
|
23443
|
+
if (err instanceof CodexSendError || err instanceof _codex_remote_control_js__rspack_import_8.Rg) throw err;
|
|
23444
|
+
const explained = explainSendError(err, threadId, env);
|
|
23445
|
+
if (explained instanceof _codex_remote_control_js__rspack_import_8.Rg) throw explained;
|
|
23446
|
+
throw new CodexSendError(explained.message, {
|
|
23340
23447
|
delivery: err instanceof CodexRpcError ? "rejected" : err && err.delivery || "unknown",
|
|
23341
23448
|
reason: err instanceof CodexRpcError ? /no rollout found|thread not found/i.test(String(err.rpc && err.rpc.message)) ? "unknown-thread" : /active writer/i.test(String(err.rpc && err.rpc.message)) ? "external-owner" : "rejected" : "transport",
|
|
23342
23449
|
threadId,
|
|
@@ -23567,7 +23674,11 @@ var __webpack_modules__ = {
|
|
|
23567
23674
|
"messageId",
|
|
23568
23675
|
"correlationId",
|
|
23569
23676
|
"refused",
|
|
23570
|
-
"hop"
|
|
23677
|
+
"hop",
|
|
23678
|
+
"hostId",
|
|
23679
|
+
"hostName",
|
|
23680
|
+
"hint",
|
|
23681
|
+
"code"
|
|
23571
23682
|
]){
|
|
23572
23683
|
if (error[key] !== undefined) out[key] = error[key];
|
|
23573
23684
|
}
|
|
@@ -23602,9 +23713,12 @@ var __webpack_modules__ = {
|
|
|
23602
23713
|
var _contract_js__rspack_import_0 = __webpack_require__("./src/core/codex/contract.js");
|
|
23603
23714
|
var node_fs__rspack_import_1 = __webpack_require__("node:fs");
|
|
23604
23715
|
var _codex_bridge_js__rspack_import_2 = __webpack_require__("./src/core/codex-bridge.js");
|
|
23716
|
+
var _remote_control_js__rspack_import_3 = __webpack_require__("./src/core/codex/remote-control.js");
|
|
23717
|
+
var _thread_id_js__rspack_import_4 = __webpack_require__("./src/core/codex/thread-id.js");
|
|
23605
23718
|
const ID = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
23606
23719
|
const BUDGET = 4 * 1024 * 1024;
|
|
23607
23720
|
function conversationId(value, name = 'threadId') {
|
|
23721
|
+
if (name === 'threadId') return (0, _thread_id_js__rspack_import_4.SX)(value);
|
|
23608
23722
|
if (typeof value !== 'string' || !ID.test(value)) throw new RangeError(`${name} must be 1-128 identifier characters`);
|
|
23609
23723
|
return value;
|
|
23610
23724
|
}
|
|
@@ -23729,7 +23843,8 @@ var __webpack_modules__ = {
|
|
|
23729
23843
|
if (expected !== undefined && (typeof cwd !== 'string' || (0, node_fs__rspack_import_1.realpathSync)(cwd) !== expected)) throw new Error('Codex thread cwd does not match expectedCwd');
|
|
23730
23844
|
} catch (error) {
|
|
23731
23845
|
await close();
|
|
23732
|
-
throw error;
|
|
23846
|
+
if (error instanceof _remote_control_js__rspack_import_3.Rg) throw error;
|
|
23847
|
+
throw (0, _remote_control_js__rspack_import_3.R3)(error, threadId, env);
|
|
23733
23848
|
}
|
|
23734
23849
|
async function wait({ turnId, messageId, afterMessageId, timeoutMs = 120000, signal: waitSignal, maxOutputBytes = 1048576 }) {
|
|
23735
23850
|
conversationId(turnId, 'turnId');
|
|
@@ -24080,6 +24195,264 @@ var __webpack_modules__ = {
|
|
|
24080
24195
|
j: ()=>openCodexConversation
|
|
24081
24196
|
});
|
|
24082
24197
|
},
|
|
24198
|
+
"./src/core/codex/remote-control.js" (__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
24199
|
+
var node_fs__rspack_import_0 = __webpack_require__("node:fs");
|
|
24200
|
+
var node_os__rspack_import_1 = __webpack_require__("node:os");
|
|
24201
|
+
var node_path__rspack_import_2 = __webpack_require__("node:path");
|
|
24202
|
+
var _thread_id_js__rspack_import_3 = __webpack_require__("./src/core/codex/thread-id.js");
|
|
24203
|
+
const REMOTE_SUMMARY_KEY = /^remote-thread-summaries-v3:(.+)$/;
|
|
24204
|
+
class RemoteThreadNotLoadedError1 extends Error {
|
|
24205
|
+
constructor(threadId, hostId = null, hostName = null){
|
|
24206
|
+
const hostLabel = hostName ? `${JSON.stringify(hostName)} (${JSON.stringify(hostId)})` : hostId ? JSON.stringify(hostId) : null;
|
|
24207
|
+
const owner = hostLabel ? `owned by remote-control host ${hostLabel}` : 'owned by a remote-control host (hostId unknown; check ~/.codex/.codex-global-state.json)';
|
|
24208
|
+
const hint = hostId ? `Read this thread via host ${JSON.stringify(hostId)}'s app-server` + (hostName ? ` (${hostName})` : '') + '. SSH remoting is not implemented in this adapter.' : "Read this thread via the owning host's app-server. SSH remoting is not implemented in this adapter.";
|
|
24209
|
+
super(`Codex thread ${JSON.stringify(threadId)} is not loaded on the local app-server; ${owner}. ${hint}`);
|
|
24210
|
+
this.name = 'RemoteThreadNotLoadedError';
|
|
24211
|
+
this.code = 'REMOTE_THREAD_NOT_LOADED';
|
|
24212
|
+
this.delivery = 'rejected';
|
|
24213
|
+
this.reason = 'remote-thread-not-loaded';
|
|
24214
|
+
this.threadId = threadId;
|
|
24215
|
+
this.hostId = hostId;
|
|
24216
|
+
this.hostName = hostName;
|
|
24217
|
+
this.hint = hint;
|
|
24218
|
+
}
|
|
24219
|
+
}
|
|
24220
|
+
function codexGlobalStatePath(env = process.env) {
|
|
24221
|
+
const home = env.CODEX_HOME || (0, node_path__rspack_import_2.join)((0, node_os__rspack_import_1.homedir)(), '.codex');
|
|
24222
|
+
return (0, node_path__rspack_import_2.join)(home, '.codex-global-state.json');
|
|
24223
|
+
}
|
|
24224
|
+
function loadCodexGlobalState(env = process.env) {
|
|
24225
|
+
let raw;
|
|
24226
|
+
try {
|
|
24227
|
+
raw = (0, node_fs__rspack_import_0.readFileSync)(codexGlobalStatePath(env), 'utf8');
|
|
24228
|
+
} catch {
|
|
24229
|
+
return null;
|
|
24230
|
+
}
|
|
24231
|
+
try {
|
|
24232
|
+
const data = JSON.parse(raw);
|
|
24233
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
24234
|
+
return data;
|
|
24235
|
+
} catch {
|
|
24236
|
+
return null;
|
|
24237
|
+
}
|
|
24238
|
+
}
|
|
24239
|
+
function codexStateEntries(data) {
|
|
24240
|
+
const nested = data?.['electron-persisted-atom-state'];
|
|
24241
|
+
return [
|
|
24242
|
+
nested,
|
|
24243
|
+
data
|
|
24244
|
+
].filter((value)=>value && typeof value === 'object' && !Array.isArray(value));
|
|
24245
|
+
}
|
|
24246
|
+
function findRemoteThreadHost(threadId, env = process.env) {
|
|
24247
|
+
let bare;
|
|
24248
|
+
try {
|
|
24249
|
+
bare = (0, _thread_id_js__rspack_import_3.SX)(threadId);
|
|
24250
|
+
} catch {
|
|
24251
|
+
bare = typeof threadId === 'string' && threadId.startsWith("local:") ? threadId.slice("local:".length) : threadId;
|
|
24252
|
+
}
|
|
24253
|
+
if (!bare) return null;
|
|
24254
|
+
const data = loadCodexGlobalState(env);
|
|
24255
|
+
if (!data) return null;
|
|
24256
|
+
const hostNames = collectHostNames(data);
|
|
24257
|
+
for (const [key, value] of codexStateEntries(data).flatMap(Object.entries)){
|
|
24258
|
+
const match = REMOTE_SUMMARY_KEY.exec(key);
|
|
24259
|
+
if (!match?.[1]) continue;
|
|
24260
|
+
const hostId = match[1];
|
|
24261
|
+
const hostName = hostNames.get(hostId) ?? extractHostName(value) ?? null;
|
|
24262
|
+
for (const entry of extractThreadEntries(value)){
|
|
24263
|
+
const id = threadIdFromEntry(entry);
|
|
24264
|
+
if (!id) continue;
|
|
24265
|
+
const entryBare = id.startsWith("local:") ? id.slice("local:".length) : id;
|
|
24266
|
+
if (entryBare === bare) return {
|
|
24267
|
+
hostId,
|
|
24268
|
+
hostName
|
|
24269
|
+
};
|
|
24270
|
+
}
|
|
24271
|
+
}
|
|
24272
|
+
return null;
|
|
24273
|
+
}
|
|
24274
|
+
function mapRemoteThreadError(error, threadId, env = process.env) {
|
|
24275
|
+
const message = errorMessage(error);
|
|
24276
|
+
if (!/thread not loaded|not loaded|no rollout found|thread not found|invalid thread id/i.test(message)) {
|
|
24277
|
+
return error instanceof Error ? error : new Error(message || String(error));
|
|
24278
|
+
}
|
|
24279
|
+
const remote = findRemoteThreadHost(threadId, env);
|
|
24280
|
+
if (remote) {
|
|
24281
|
+
return new RemoteThreadNotLoadedError1(threadId, remote.hostId, remote.hostName);
|
|
24282
|
+
}
|
|
24283
|
+
if (/thread not loaded|not loaded/i.test(message)) {
|
|
24284
|
+
return new RemoteThreadNotLoadedError1(threadId, null, null);
|
|
24285
|
+
}
|
|
24286
|
+
return error instanceof Error ? error : new Error(message || String(error));
|
|
24287
|
+
}
|
|
24288
|
+
function collectHostNames(data) {
|
|
24289
|
+
const names = new Map();
|
|
24290
|
+
for (const [key, value] of codexStateEntries(data).flatMap(Object.entries)){
|
|
24291
|
+
if (!/remote-host|hosts|host-meta|host_directory|environments/i.test(key)) continue;
|
|
24292
|
+
if (!value || typeof value !== 'object') continue;
|
|
24293
|
+
if (Array.isArray(value)) {
|
|
24294
|
+
for (const entry of value){
|
|
24295
|
+
const pair = hostNameFromEntry(entry);
|
|
24296
|
+
if (pair && !names.has(pair.hostId)) names.set(pair.hostId, pair.hostName);
|
|
24297
|
+
}
|
|
24298
|
+
continue;
|
|
24299
|
+
}
|
|
24300
|
+
for (const [hostId, entry] of Object.entries(value)){
|
|
24301
|
+
const fromEntry = hostNameFromEntry(entry);
|
|
24302
|
+
if (fromEntry) {
|
|
24303
|
+
if (!names.has(fromEntry.hostId)) names.set(fromEntry.hostId, fromEntry.hostName);
|
|
24304
|
+
continue;
|
|
24305
|
+
}
|
|
24306
|
+
const label = stringField(entry, [
|
|
24307
|
+
'name',
|
|
24308
|
+
'hostName',
|
|
24309
|
+
'displayName',
|
|
24310
|
+
'envName',
|
|
24311
|
+
'label',
|
|
24312
|
+
'title'
|
|
24313
|
+
]);
|
|
24314
|
+
if (label && !names.has(hostId)) names.set(hostId, label);
|
|
24315
|
+
}
|
|
24316
|
+
}
|
|
24317
|
+
return names;
|
|
24318
|
+
}
|
|
24319
|
+
function hostNameFromEntry(entry) {
|
|
24320
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return null;
|
|
24321
|
+
const hostId = stringField(entry, [
|
|
24322
|
+
'hostId',
|
|
24323
|
+
'id',
|
|
24324
|
+
'host_id',
|
|
24325
|
+
'uuid'
|
|
24326
|
+
]);
|
|
24327
|
+
const hostName = stringField(entry, [
|
|
24328
|
+
'name',
|
|
24329
|
+
'hostName',
|
|
24330
|
+
'displayName',
|
|
24331
|
+
'envName',
|
|
24332
|
+
'label',
|
|
24333
|
+
'title'
|
|
24334
|
+
]);
|
|
24335
|
+
if (!hostId || !hostName) return null;
|
|
24336
|
+
return {
|
|
24337
|
+
hostId,
|
|
24338
|
+
hostName
|
|
24339
|
+
};
|
|
24340
|
+
}
|
|
24341
|
+
function extractHostName(value) {
|
|
24342
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
24343
|
+
return stringField(value, [
|
|
24344
|
+
'hostName',
|
|
24345
|
+
'displayName',
|
|
24346
|
+
'name',
|
|
24347
|
+
'envName',
|
|
24348
|
+
'label',
|
|
24349
|
+
'title',
|
|
24350
|
+
'friendlyName'
|
|
24351
|
+
]);
|
|
24352
|
+
}
|
|
24353
|
+
function extractThreadEntries(value) {
|
|
24354
|
+
if (Array.isArray(value)) return value;
|
|
24355
|
+
if (!value || typeof value !== 'object') return [];
|
|
24356
|
+
const record = value;
|
|
24357
|
+
for (const key of [
|
|
24358
|
+
'threads',
|
|
24359
|
+
'summaries',
|
|
24360
|
+
'items',
|
|
24361
|
+
'data',
|
|
24362
|
+
'conversations'
|
|
24363
|
+
]){
|
|
24364
|
+
if (Array.isArray(record[key])) return record[key];
|
|
24365
|
+
}
|
|
24366
|
+
const entries = [];
|
|
24367
|
+
for (const [key, entry] of Object.entries(record)){
|
|
24368
|
+
if ([
|
|
24369
|
+
'pinnedIds',
|
|
24370
|
+
'pinned',
|
|
24371
|
+
'pinnedThreadIds',
|
|
24372
|
+
'hostName',
|
|
24373
|
+
'displayName',
|
|
24374
|
+
'name',
|
|
24375
|
+
'envName',
|
|
24376
|
+
'label',
|
|
24377
|
+
'title',
|
|
24378
|
+
'friendlyName',
|
|
24379
|
+
'hostId',
|
|
24380
|
+
'version'
|
|
24381
|
+
].includes(key)) continue;
|
|
24382
|
+
if (typeof entry === 'string') continue;
|
|
24383
|
+
if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
|
|
24384
|
+
const id = stringField(entry, [
|
|
24385
|
+
'id',
|
|
24386
|
+
'threadId',
|
|
24387
|
+
'thread_id',
|
|
24388
|
+
'conversationId'
|
|
24389
|
+
]);
|
|
24390
|
+
entries.push(id ? entry : {
|
|
24391
|
+
...entry,
|
|
24392
|
+
id: key
|
|
24393
|
+
});
|
|
24394
|
+
}
|
|
24395
|
+
}
|
|
24396
|
+
return entries;
|
|
24397
|
+
}
|
|
24398
|
+
function threadIdFromEntry(entry) {
|
|
24399
|
+
if (typeof entry === 'string' && entry) return entry;
|
|
24400
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return null;
|
|
24401
|
+
return stringField(entry, [
|
|
24402
|
+
'id',
|
|
24403
|
+
'threadId',
|
|
24404
|
+
'thread_id',
|
|
24405
|
+
'conversationId'
|
|
24406
|
+
]);
|
|
24407
|
+
}
|
|
24408
|
+
function stringField(row, keys) {
|
|
24409
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) return null;
|
|
24410
|
+
for (const key of keys){
|
|
24411
|
+
const value = row[key];
|
|
24412
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
24413
|
+
}
|
|
24414
|
+
return null;
|
|
24415
|
+
}
|
|
24416
|
+
function errorMessage(error) {
|
|
24417
|
+
if (!error) return '';
|
|
24418
|
+
if (typeof error === 'string') return error;
|
|
24419
|
+
if (error instanceof Error) return error.message;
|
|
24420
|
+
if (typeof error === 'object' && error && 'message' in error) return String(error.message);
|
|
24421
|
+
if (typeof error === 'object' && error && 'rpc' in error) {
|
|
24422
|
+
const rpc = error.rpc;
|
|
24423
|
+
if (rpc && typeof rpc.message === 'string') return rpc.message;
|
|
24424
|
+
}
|
|
24425
|
+
return String(error);
|
|
24426
|
+
}
|
|
24427
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
24428
|
+
R3: ()=>mapRemoteThreadError,
|
|
24429
|
+
Rg: ()=>RemoteThreadNotLoadedError1
|
|
24430
|
+
});
|
|
24431
|
+
},
|
|
24432
|
+
"./src/core/codex/thread-id.js" (__unused_rspack___webpack_module__, __webpack_exports__, __webpack_require__) {
|
|
24433
|
+
const LOCAL_THREAD_ID_PREFIX = 'local:';
|
|
24434
|
+
const TEMP_THREAD_ID_PREFIX = 'local:client-new-thread:';
|
|
24435
|
+
const ID_PATTERN = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
24436
|
+
function isTemporaryDesktopThreadId(threadId) {
|
|
24437
|
+
return typeof threadId === 'string' && (threadId.startsWith(TEMP_THREAD_ID_PREFIX) || threadId.startsWith('client-new-thread:'));
|
|
24438
|
+
}
|
|
24439
|
+
function normalizeCodexThreadId1(threadId) {
|
|
24440
|
+
if (typeof threadId !== 'string' || !threadId) {
|
|
24441
|
+
throw new RangeError('Invalid threadId');
|
|
24442
|
+
}
|
|
24443
|
+
if (isTemporaryDesktopThreadId(threadId)) {
|
|
24444
|
+
throw new RangeError('Temporary Desktop thread id ' + JSON.stringify(threadId) + ' has no app-server mapping; wait for the real conversation id');
|
|
24445
|
+
}
|
|
24446
|
+
const bare = threadId.startsWith(LOCAL_THREAD_ID_PREFIX) ? threadId.slice(LOCAL_THREAD_ID_PREFIX.length) : threadId;
|
|
24447
|
+
if (!bare || !ID_PATTERN.test(bare)) {
|
|
24448
|
+
throw new RangeError('Invalid threadId');
|
|
24449
|
+
}
|
|
24450
|
+
return bare;
|
|
24451
|
+
}
|
|
24452
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
24453
|
+
SX: ()=>normalizeCodexThreadId1
|
|
24454
|
+
});
|
|
24455
|
+
},
|
|
24083
24456
|
"./src/core/desktop-shim.js" (__unused_rspack___webpack_module__, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
24084
24457
|
var node_child_process__rspack_import_0 = __webpack_require__("node:child_process");
|
|
24085
24458
|
var node_fs__rspack_import_1 = __webpack_require__("node:fs");
|
|
@@ -27537,7 +27910,7 @@ ts() { date -u +%Y-%m-%dT%H:%M:%SZ; }
|
|
|
27537
27910
|
},
|
|
27538
27911
|
"./package.json" (module) {
|
|
27539
27912
|
module.exports = {
|
|
27540
|
-
"rE": "0.
|
|
27913
|
+
"rE": "0.11.0"
|
|
27541
27914
|
};
|
|
27542
27915
|
}
|
|
27543
27916
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: chatgpt-desktop
|
|
3
|
+
description: Use the gbot ChatGPT Desktop MCP tools to discover hosts and threads, page/search/read conversations, and send or wait for replies on the user's Mac. Use for local Desktop CDP automation and remote-control thread routing; do not use for ordinary Grok Bot messaging.
|
|
4
|
+
---
|
|
5
|
+
# ChatGPT Desktop through gbot
|
|
6
|
+
|
|
7
|
+
Use the `chatgpt_desktop_*` MCP tools on the user's Mac. The CLI is a secondary
|
|
8
|
+
surface. CDP listens on `127.0.0.1` only; these tools do not transport CDP to
|
|
9
|
+
another machine.
|
|
10
|
+
|
|
11
|
+
## Find the right conversation
|
|
12
|
+
|
|
13
|
+
1. Call `chatgpt_desktop_status`. `reachable: false` or `exitCode: 1` means
|
|
14
|
+
CDP is down: open/send/wait cannot work. `appServerFallback.reachable` is a
|
|
15
|
+
separate list/search/read probe. Do not retry a send because CDP disappeared.
|
|
16
|
+
2. Call `chatgpt_desktop_list_hosts` for dynamic host IDs, labels, and
|
|
17
|
+
`modelProvider` values. `local` is this Mac. A remote-control host ID looks
|
|
18
|
+
like `remote-control:env_e_...`; its `hostName` may be `null`. Do not infer a
|
|
19
|
+
host name from a project label. Managed SSH hosts may have no threads.
|
|
20
|
+
3. Use `chatgpt_desktop_list_threads` with `host`, `modelProvider`, or `project`
|
|
21
|
+
as needed. Pass `nextCursor` back as `cursor` with the same filters until it
|
|
22
|
+
is `null`. The cursor refers to a sorted merged inventory; pages normally
|
|
23
|
+
fill `limit` and can stop earlier at the MCP document cap. If the inventory
|
|
24
|
+
changed after a cursor expired, restart without one. Project labels and IDs
|
|
25
|
+
come from Desktop project assignments and workspace roots; `projectRootPath`
|
|
26
|
+
is the resolved root when available.
|
|
27
|
+
`chatgpt_desktop_search_threads` also accepts `cursor`; keep its query and
|
|
28
|
+
filters unchanged. Search matches the full title even when the display row
|
|
29
|
+
has a 200-character `title` and `titleTruncated: true`.
|
|
30
|
+
4. Read with `chatgpt_desktop_read_thread`. `full: false` gets the recent tail;
|
|
31
|
+
`full: true` pages chronologically from the start. Pass `nextCursor` back
|
|
32
|
+
as `cursor` with the same thread ID and `full` value until `complete: true`.
|
|
33
|
+
The byte budget can stop before `limit`. An oversized turn is returned as
|
|
34
|
+
ordered fragments with one stable `turnKey`; use `continuation.field`,
|
|
35
|
+
`offsetChars`, and `totalChars` to reassemble `text`, `userText`, and
|
|
36
|
+
`assistantText`. `textTruncated: true` signals a fragment, not missing text.
|
|
37
|
+
Check `complete` and `warnings` before treating a page as full history.
|
|
38
|
+
|
|
39
|
+
Durable local IDs are `local:<conversationId>` (a bare conversation ID also
|
|
40
|
+
works). `local:client-new-thread:*` is temporary and works only while that
|
|
41
|
+
conversation remains selected. `REMOTE_THREAD_NOT_LOADED` includes the
|
|
42
|
+
owning `hostId`: read or send through gbot running on that host's own Desktop
|
|
43
|
+
or app-server. The local Mac cannot fetch that remote history through CDP.
|
|
44
|
+
|
|
45
|
+
## Open, send, and wait
|
|
46
|
+
|
|
47
|
+
- `chatgpt_desktop_open_thread` navigates to a durable conversation even if it
|
|
48
|
+
is absent from the sidebar. An archived route returns `archived: true`; the
|
|
49
|
+
thread remains readable through `chatgpt_desktop_read_thread`.
|
|
50
|
+
- `chatgpt_desktop_send` with `threadId` sends to that conversation. Omit
|
|
51
|
+
`threadId` and `project` to start outside a project. Omit `threadId` and set
|
|
52
|
+
`project` to start inside that project. The tool verifies the empty new-chat
|
|
53
|
+
view and its project selection before typing. Send once. A new-thread receipt may
|
|
54
|
+
include `temporaryThreadId` and resolves `threadId` to the durable
|
|
55
|
+
`local:<conversationId>` before returning. Keep both until the durable ID is
|
|
56
|
+
confirmed; use the durable ID for later work.
|
|
57
|
+
- Then call `chatgpt_desktop_wait_reply` with the returned `threadId` (or the
|
|
58
|
+
selected temporary ID). It returns the reply and durable `conversationId`.
|
|
59
|
+
A timeout is an observation, not permission to resend.
|
|
60
|
+
|
|
61
|
+
`ARCHIVED_THREAD` means send was rejected before submission. Read remains
|
|
62
|
+
available. Pass `unarchive: true` with an existing `threadId` on send only if
|
|
63
|
+
the user wants the archive state changed; this explicitly calls app-server
|
|
64
|
+
`thread/unarchive` and retries once. Never unarchive merely to read or open.
|
|
65
|
+
|
|
66
|
+
`COMPOSER_HAS_DRAFT` means the user's draft would be disturbed; leave it for
|
|
67
|
+
the operator. `PROJECT_UNAVAILABLE` means Desktop disabled a project's new-chat
|
|
68
|
+
action, often because its configured workspace root no longer exists; correct
|
|
69
|
+
the project root in Desktop before retrying. `NEW_CHAT_NAVIGATION_FAILED` means
|
|
70
|
+
the button did not open the requested empty view within three seconds; inspect
|
|
71
|
+
Desktop before retrying. `CDP_UNREACHABLE` requires restoring the local Desktop CDP
|
|
72
|
+
endpoint. App-server failures, including oversized frames, appear as errors or
|
|
73
|
+
read `warnings` with `complete: false`; inspect the partial result and source
|
|
74
|
+
status before continuing. A send with unknown delivery must not be repeated
|
|
75
|
+
without checking the selected thread and reply state.
|
package/package.json
CHANGED