grokbot-control 0.0.0-stage → 0.1.3
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/README.md +160 -2
- package/dist/auth/lock.d.ts +14 -0
- package/dist/auth/lock.js +71 -0
- package/dist/auth/provider.d.ts +89 -0
- package/dist/auth/provider.js +884 -0
- package/dist/auth/store.d.ts +25 -0
- package/dist/auth/store.js +106 -0
- package/dist/auth/types.d.ts +138 -0
- package/dist/auth/types.js +1 -0
- package/dist/cli/config.d.ts +26 -0
- package/dist/cli/config.js +98 -0
- package/dist/cli/help.d.ts +1 -0
- package/dist/cli/help.js +83 -0
- package/dist/cli/io.d.ts +6 -0
- package/dist/cli/io.js +61 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +275 -0
- package/dist/cli/sync.d.ts +4 -0
- package/dist/cli/sync.js +105 -0
- package/dist/connect/client.d.ts +32 -0
- package/dist/connect/client.js +257 -0
- package/dist/dto/result.d.ts +33 -0
- package/dist/dto/result.js +29 -0
- package/dist/dto/types.d.ts +63 -0
- package/dist/dto/types.js +2 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +64 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +11 -0
- package/dist/mcp/main.d.ts +2 -0
- package/dist/mcp/main.js +82 -0
- package/dist/mcp/tools.d.ts +24 -0
- package/dist/mcp/tools.js +408 -0
- package/dist/mcp/zod-json.d.ts +2 -0
- package/dist/mcp/zod-json.js +4 -0
- package/dist/ops/auth.d.ts +48 -0
- package/dist/ops/auth.js +89 -0
- package/dist/ops/bot.d.ts +52 -0
- package/dist/ops/bot.js +211 -0
- package/dist/ops/limits.d.ts +10 -0
- package/dist/ops/limits.js +31 -0
- package/dist/ops/map.d.ts +7 -0
- package/dist/ops/map.js +57 -0
- package/dist/ops/message.d.ts +33 -0
- package/dist/ops/message.js +118 -0
- package/dist/ops/plane.d.ts +26 -0
- package/dist/ops/plane.js +33 -0
- package/dist/ops/progress.d.ts +34 -0
- package/dist/ops/progress.js +92 -0
- package/dist/ops/session.d.ts +13 -0
- package/dist/ops/session.js +34 -0
- package/dist/ops/sidebar.d.ts +70 -0
- package/dist/ops/sidebar.js +205 -0
- package/dist/sync/attachments.d.ts +76 -0
- package/dist/sync/attachments.js +274 -0
- package/dist/sync/core.d.ts +92 -0
- package/dist/sync/core.js +1027 -0
- package/dist/sync/decode.d.ts +83 -0
- package/dist/sync/decode.js +229 -0
- package/dist/sync/download-policy.d.ts +3 -0
- package/dist/sync/download-policy.js +103 -0
- package/dist/sync/fs.d.ts +24 -0
- package/dist/sync/fs.js +252 -0
- package/dist/sync/hydrate.d.ts +43 -0
- package/dist/sync/hydrate.js +472 -0
- package/dist/sync/render.d.ts +5 -0
- package/dist/sync/render.js +100 -0
- package/dist/testing/mock-persist.d.ts +3 -0
- package/dist/testing/mock-persist.js +45 -0
- package/dist/testing/mock-transport.d.ts +18 -0
- package/dist/testing/mock-transport.js +202 -0
- package/docs/acceptance.md +19 -0
- package/docs/architecture.md +128 -0
- package/docs/auth-lock.md +87 -0
- package/docs/implementation-plan.md +14 -0
- package/docs/implementation-status.md +92 -0
- package/docs/proto-drift.md +9 -0
- package/docs/protocol/README.md +34 -0
- package/docs/protocol/attachment-pagination-0.79.3.md +32 -0
- package/docs/protocol/auth-flow.md +51 -0
- package/docs/protocol/baseline-manifest.md +72 -0
- package/docs/protocol/baselines/full-schema-diff-0.78.4-to-0.79.2.txt +239 -0
- package/docs/protocol/baselines/full-schema-diff-0.78.4-to-0.79.3.txt +240 -0
- package/docs/protocol/baselines/full-schema-diff-0.79.2-to-0.79.3.txt +23 -0
- package/docs/protocol/baselines/schema-0.79.3/README.md +7 -0
- package/docs/protocol/baselines/schema-0.79.3/agent.v1.proto +5184 -0
- package/docs/protocol/baselines/schema-0.79.3/aiserver.v1.proto +18182 -0
- package/docs/protocol/baselines/schema-0.79.3/aiserver.v1_2.proto +54 -0
- package/docs/protocol/baselines/schema-0.79.3/anyrun.v1.proto +37 -0
- package/docs/protocol/baselines/schema-0.79.3/internapi.v1.proto +35 -0
- package/docs/protocol/baselines/schema-0.79.3/snapshot.json.gz +0 -0
- package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.AgentStoreService.proto +34 -0
- package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.AiService.proto +17 -0
- package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.AnalyticsService.proto +17 -0
- package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.BackgroundComposerService.proto +19 -0
- package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.DashboardService.proto +101 -0
- package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.GrokBotService.proto +473 -0
- package/docs/protocol/baselines/schema-0.79.3/svc_aiserver.v1.SandBoxService.proto +64 -0
- package/docs/protocol/baselines/used-surface-0.78.4.json +3694 -0
- package/docs/protocol/baselines/used-surface-0.79.2.json +3835 -0
- package/docs/protocol/baselines/used-surface-0.79.3.json +3835 -0
- package/docs/protocol/baselines/used-surface-diff-0.78.4-to-0.79.2.md +34 -0
- package/docs/protocol/baselines/used-surface-diff-0.78.4-to-0.79.3.md +33 -0
- package/docs/protocol/compat-matrix.md +20 -0
- package/docs/protocol/static-restore.md +64 -0
- package/docs/protocol/surface-map.md +39 -0
- package/docs/protocol/transcript-body-0.79.3.md +98 -0
- package/docs/protocol/update-runbook.md +30 -0
- package/docs/protocol-map.md +106 -0
- package/docs/references.md +55 -0
- package/docs/requirements.md +26 -0
- package/docs/review.md +36 -0
- package/docs/sync-format.md +98 -0
- package/package.json +60 -4
- package/vendor/proto-gen/NOTICE.md +23 -0
- package/vendor/proto-gen/agent.v1_pb.js +9503 -0
- package/vendor/proto-gen/aiserver.v1_2_pb.js +105 -0
- package/vendor/proto-gen/aiserver.v1_pb.js +33163 -0
- package/vendor/proto-gen/anyrun.v1_pb.js +64 -0
- package/vendor/proto-gen/internapi.v1_pb.js +60 -0
- package/vendor/proto-gen/package.json +10 -0
- package/vendor/proto-gen/svc_aiserver.v1.GrokBotService_connect.js +4193 -0
package/dist/ops/bot.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { GrokBotAgentHarnessKind } from "#proto-gen/aiserver";
|
|
2
|
+
import { mapConnectError } from "../connect/client.js";
|
|
3
|
+
import { failResult, newRequestId, okResult, } from "../dto/result.js";
|
|
4
|
+
import { GbcError } from "../errors.js";
|
|
5
|
+
import { mapBot } from "./map.js";
|
|
6
|
+
import { validateLimit } from "./limits.js";
|
|
7
|
+
function requireWrite(opts, action) {
|
|
8
|
+
if (opts?.dryRun)
|
|
9
|
+
return;
|
|
10
|
+
if (!opts?.allowWrite && process.env.GBT_ALLOW_WRITES !== "1") {
|
|
11
|
+
throw new GbcError("denied", `refusing ${action}: pass allowWrite / --allow-writes or GBT_ALLOW_WRITES=1`, {
|
|
12
|
+
exitCode: 7,
|
|
13
|
+
hint: "Writes are explicit; dry-run is pure and does not call RPC.",
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class BotOps {
|
|
18
|
+
client;
|
|
19
|
+
flags;
|
|
20
|
+
constructor(client, flags = {}) {
|
|
21
|
+
this.client = client;
|
|
22
|
+
this.flags = flags;
|
|
23
|
+
}
|
|
24
|
+
wrapSim(r) {
|
|
25
|
+
if (this.flags.simulated)
|
|
26
|
+
r.simulated = true;
|
|
27
|
+
return r;
|
|
28
|
+
}
|
|
29
|
+
/** Update/Delete RPC `id` is the numeric server id (App: serverId); accept agent_id UUID by resolving via list. */
|
|
30
|
+
async serverId(idOrAgentId) {
|
|
31
|
+
if (/^\d+$/.test(idOrAgentId))
|
|
32
|
+
return idOrAgentId;
|
|
33
|
+
const res = await this.client.listGrokBotAgents({});
|
|
34
|
+
const bot = (res.agents ?? [])
|
|
35
|
+
.map(mapBot)
|
|
36
|
+
.find((b) => b.agent_id === idOrAgentId);
|
|
37
|
+
if (bot?.id)
|
|
38
|
+
return bot.id;
|
|
39
|
+
throw new GbcError(res.truncated ? "lookup_incomplete" : "not_found", res.truncated
|
|
40
|
+
? "agent list truncated; cannot resolve server id"
|
|
41
|
+
: `bot ${idOrAgentId} not found; no write sent`, { exitCode: 5 });
|
|
42
|
+
}
|
|
43
|
+
async list(opts) {
|
|
44
|
+
const request_id = newRequestId();
|
|
45
|
+
try {
|
|
46
|
+
const limit = validateLimit(opts?.limit);
|
|
47
|
+
const res = await this.client.listGrokBotAgents({});
|
|
48
|
+
const all = (res.agents ?? []).map(mapBot);
|
|
49
|
+
const truncated = Boolean(res.truncated) || all.length > limit;
|
|
50
|
+
return this.wrapSim(okResult({
|
|
51
|
+
bots: all.slice(0, limit),
|
|
52
|
+
truncated,
|
|
53
|
+
}, { request_id }));
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
return this.fromErr(e, request_id);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** No GetBot RPC — filter ListGrokBotAgents client-side (full server page, not the user-facing cap). */
|
|
60
|
+
async get(agentId) {
|
|
61
|
+
const request_id = newRequestId();
|
|
62
|
+
try {
|
|
63
|
+
const res = await this.client.listGrokBotAgents({});
|
|
64
|
+
const all = (res.agents ?? []).map(mapBot);
|
|
65
|
+
const bot = all.find((b) => b.agent_id === agentId || b.id === agentId);
|
|
66
|
+
if (bot)
|
|
67
|
+
return this.wrapSim(okResult({ bot }, { request_id }));
|
|
68
|
+
if (res.truncated) {
|
|
69
|
+
return this.wrapSim(failResult("lookup_incomplete", `bot ${agentId} not in a truncated ListGrokBotAgents page; existence unknown`, {
|
|
70
|
+
request_id,
|
|
71
|
+
outcome: "unknown",
|
|
72
|
+
hint: "Server list was truncated; absence is not proof of non-existence.",
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
return this.wrapSim(failResult("not_found", `bot ${agentId} not found in ListGrokBotAgents`, {
|
|
76
|
+
request_id,
|
|
77
|
+
hint: "There is no GetGrokBotAgent RPC; get = list + filter.",
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
return this.fromErr(e, request_id);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async create(input, write) {
|
|
85
|
+
const request_id = newRequestId();
|
|
86
|
+
if (write?.dryRun) {
|
|
87
|
+
return this.wrapSim(okResult({
|
|
88
|
+
bot: {
|
|
89
|
+
agent_id: input.agentId || "(dry-run-nonce)",
|
|
90
|
+
id: "",
|
|
91
|
+
name: input.name,
|
|
92
|
+
description: input.description || "",
|
|
93
|
+
title: input.title || input.name,
|
|
94
|
+
harness: "TEMPORAL",
|
|
95
|
+
role: "",
|
|
96
|
+
kind: "",
|
|
97
|
+
viewer_session_id: "",
|
|
98
|
+
},
|
|
99
|
+
agent_id: input.agentId || "(dry-run-nonce)",
|
|
100
|
+
}, { request_id, code: "dry_run" }));
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
requireWrite(write, "bot.create");
|
|
104
|
+
const agentId = input.agentId || crypto.randomUUID();
|
|
105
|
+
// App Temporal path: CreateGrokBotTemporalAgent with CreateGrokBotAgentRequest fields.
|
|
106
|
+
// Drift (visibility/teamId/botBuilderRequested) intentionally NOT sent — not in 0.78.4 Request.
|
|
107
|
+
const res = await this.client.createGrokBotTemporalAgent({
|
|
108
|
+
agentId,
|
|
109
|
+
name: input.name,
|
|
110
|
+
description: input.description ?? "",
|
|
111
|
+
title: input.title ?? input.name,
|
|
112
|
+
avatarShape: "circle",
|
|
113
|
+
avatarColor: "#888888",
|
|
114
|
+
harness: GrokBotAgentHarnessKind.TEMPORAL,
|
|
115
|
+
kickstartRequested: input.kickstart ?? false,
|
|
116
|
+
introductionSuppressed: !(input.kickstart ?? false),
|
|
117
|
+
language: input.language,
|
|
118
|
+
});
|
|
119
|
+
const bot = mapBot(res.agent);
|
|
120
|
+
return this.wrapSim(okResult({ bot, agent_id: bot.agent_id || agentId }, { request_id }));
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
return this.fromErr(e, request_id, true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async update(agentId, patch, write) {
|
|
127
|
+
const request_id = newRequestId();
|
|
128
|
+
if (write?.dryRun) {
|
|
129
|
+
return this.wrapSim(okResult({
|
|
130
|
+
bot: {
|
|
131
|
+
agent_id: agentId,
|
|
132
|
+
id: agentId,
|
|
133
|
+
name: patch.name || "",
|
|
134
|
+
description: patch.description || "",
|
|
135
|
+
title: patch.title || "",
|
|
136
|
+
harness: "",
|
|
137
|
+
role: "",
|
|
138
|
+
kind: "",
|
|
139
|
+
viewer_session_id: "",
|
|
140
|
+
},
|
|
141
|
+
}, { request_id, code: "dry_run" }));
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
requireWrite(write, "bot.update");
|
|
145
|
+
const res = await this.client.updateGrokBotAgent({
|
|
146
|
+
id: await this.serverId(agentId),
|
|
147
|
+
name: patch.name,
|
|
148
|
+
description: patch.description,
|
|
149
|
+
title: patch.title,
|
|
150
|
+
partialUpdate: true,
|
|
151
|
+
});
|
|
152
|
+
return this.wrapSim(okResult({ bot: mapBot(res.agent) }, { request_id }));
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
return this.fromErr(e, request_id, true);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async delete(agentId, write) {
|
|
159
|
+
const request_id = newRequestId();
|
|
160
|
+
if (write?.dryRun) {
|
|
161
|
+
return this.wrapSim(okResult({
|
|
162
|
+
deleted: false,
|
|
163
|
+
note: "dry-run: would call DeleteGrokBotAgent; sidebar cleanup unverified",
|
|
164
|
+
}, { request_id, code: "dry_run" }));
|
|
165
|
+
}
|
|
166
|
+
if (write?.confirm !== agentId) {
|
|
167
|
+
return this.wrapSim(failResult("denied", "delete requires --confirm=<agent_id> matching the resource", {
|
|
168
|
+
request_id,
|
|
169
|
+
hint: `gbc bot delete --agent-id ${agentId} --confirm=${agentId} --allow-writes`,
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
requireWrite(write, "bot.delete");
|
|
174
|
+
await this.client.deleteGrokBotAgent({
|
|
175
|
+
id: await this.serverId(agentId),
|
|
176
|
+
});
|
|
177
|
+
return this.wrapSim(okResult({
|
|
178
|
+
deleted: true,
|
|
179
|
+
note: "DeleteGrokBotAgent succeeded. Sidebar section cleanup after delete is unverified.",
|
|
180
|
+
}, { request_id }));
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
if (e instanceof GbcError && e.code === "not_found")
|
|
184
|
+
return this.wrapSim(okResult({
|
|
185
|
+
deleted: false,
|
|
186
|
+
note: "agent_id not present in complete ListGrokBotAgents; no delete RPC sent (already absent).",
|
|
187
|
+
}, { request_id }));
|
|
188
|
+
if (e instanceof GbcError)
|
|
189
|
+
return this.fromErr(e, request_id, true);
|
|
190
|
+
const mapped = mapConnectError(e, request_id, { write: true });
|
|
191
|
+
// App treats NotFound as ok on delete.
|
|
192
|
+
if (mapped.code === "not_found") {
|
|
193
|
+
return this.wrapSim(okResult({
|
|
194
|
+
deleted: true,
|
|
195
|
+
note: "Delete NotFound normalized to ok (App behavior). Sidebar residual unverified.",
|
|
196
|
+
}, { request_id }));
|
|
197
|
+
}
|
|
198
|
+
return this.fromErr(mapped, request_id, true);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
fromErr(e, request_id, write = false) {
|
|
202
|
+
const err = e instanceof GbcError ? e : mapConnectError(e, request_id, { write });
|
|
203
|
+
return this.wrapSim(failResult(err.code, err.message, {
|
|
204
|
+
outcome: err.outcome,
|
|
205
|
+
retryable: err.retryable,
|
|
206
|
+
hint: err.hint,
|
|
207
|
+
request_id,
|
|
208
|
+
details: err.details,
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Documented list caps (CLI/MCP/SDK share these). */
|
|
2
|
+
export declare const LIMIT_DEFAULT = 50;
|
|
3
|
+
export declare const LIMIT_MAX = 1000;
|
|
4
|
+
/**
|
|
5
|
+
* Validate a list limit. undefined -> default. Anything not a positive integer
|
|
6
|
+
* <= LIMIT_MAX throws invalid_argument (exit 2) — never silently empty.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateLimit(v: unknown, max?: number): number;
|
|
9
|
+
/** Parse a CLI string limit strictly (digits only). */
|
|
10
|
+
export declare function parseCliLimit(raw: string | undefined, max?: number): number;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { GbcError } from "../errors.js";
|
|
2
|
+
/** Documented list caps (CLI/MCP/SDK share these). */
|
|
3
|
+
export const LIMIT_DEFAULT = 50;
|
|
4
|
+
export const LIMIT_MAX = 1000;
|
|
5
|
+
/**
|
|
6
|
+
* Validate a list limit. undefined -> default. Anything not a positive integer
|
|
7
|
+
* <= LIMIT_MAX throws invalid_argument (exit 2) — never silently empty.
|
|
8
|
+
*/
|
|
9
|
+
export function validateLimit(v, max = LIMIT_MAX) {
|
|
10
|
+
if (v === undefined || v === null)
|
|
11
|
+
return LIMIT_DEFAULT;
|
|
12
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 1 || v > max) {
|
|
13
|
+
throw new GbcError("invalid_argument", `limit must be an integer in 1..${max} (got: ${String(v)})`, {
|
|
14
|
+
exitCode: 2,
|
|
15
|
+
hint: `pass --limit=<1..${max}>`,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return v;
|
|
19
|
+
}
|
|
20
|
+
/** Parse a CLI string limit strictly (digits only). */
|
|
21
|
+
export function parseCliLimit(raw, max = LIMIT_MAX) {
|
|
22
|
+
if (raw === undefined)
|
|
23
|
+
return LIMIT_DEFAULT;
|
|
24
|
+
if (!/^\d+$/.test(raw)) {
|
|
25
|
+
throw new GbcError("usage", `--limit must be an integer in 1..${max} (got: "${raw}")`, {
|
|
26
|
+
exitCode: 2,
|
|
27
|
+
hint: `gbc ... --limit=50`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return validateLimit(Number(raw), max);
|
|
31
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BotSummary, ProgressStatus, SessionSummary, SidebarSection } from "../dto/types.js";
|
|
2
|
+
/** int64 from protobuf JSON/TS may be bigint | string | number — normalize to string. */
|
|
3
|
+
export declare function int64ToString(v: unknown): string | undefined;
|
|
4
|
+
export declare function mapBot(a: any): BotSummary;
|
|
5
|
+
export declare function mapSection(s: any): SidebarSection;
|
|
6
|
+
export declare function mapSession(s: any): SessionSummary;
|
|
7
|
+
export declare function mapStatus(s: any): ProgressStatus;
|
package/dist/ops/map.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** int64 from protobuf JSON/TS may be bigint | string | number — normalize to string. */
|
|
2
|
+
export function int64ToString(v) {
|
|
3
|
+
if (v === undefined || v === null)
|
|
4
|
+
return undefined;
|
|
5
|
+
return String(v);
|
|
6
|
+
}
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
export function mapBot(a) {
|
|
9
|
+
return {
|
|
10
|
+
agent_id: a.agentId || a.agent_id || a.id || "",
|
|
11
|
+
id: a.id || "",
|
|
12
|
+
name: a.name || "",
|
|
13
|
+
description: a.description || "",
|
|
14
|
+
title: a.title || "",
|
|
15
|
+
harness: String(a.harness ?? ""),
|
|
16
|
+
role: a.role || "",
|
|
17
|
+
kind: String(a.kind ?? ""),
|
|
18
|
+
viewer_session_id: a.viewerSessionId || a.viewer_session_id || "",
|
|
19
|
+
created_at_ms: int64ToString(a.createdAtMs ?? a.created_at_ms),
|
|
20
|
+
updated_at_ms: int64ToString(a.updatedAtMs ?? a.updated_at_ms),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
export function mapSection(s) {
|
|
25
|
+
return {
|
|
26
|
+
id: s.id || "",
|
|
27
|
+
name: s.name || "",
|
|
28
|
+
agent_ids: [...(s.agentIds || s.agent_ids || [])],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
export function mapSession(s) {
|
|
33
|
+
return {
|
|
34
|
+
agent_id: s.agentId || s.agent_id || "",
|
|
35
|
+
session_id: s.sessionId || s.session_id || "",
|
|
36
|
+
kind: String(s.kind ?? ""),
|
|
37
|
+
created_at_ms: int64ToString(s.createdAtMs ?? s.created_at_ms),
|
|
38
|
+
updated_at_ms: int64ToString(s.updatedAtMs ?? s.updated_at_ms),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
export function mapStatus(s) {
|
|
43
|
+
const live = s.live;
|
|
44
|
+
return {
|
|
45
|
+
agent_id: s.agentId || s.agent_id || "",
|
|
46
|
+
is_running: Boolean(s.isRunning ?? s.is_running ?? live?.isRunning),
|
|
47
|
+
session_id: live?.sessionId || live?.session_id,
|
|
48
|
+
live: live
|
|
49
|
+
? {
|
|
50
|
+
is_composing_message: live.isComposingMessage ?? live.is_composing_message,
|
|
51
|
+
is_retrying: live.isRetrying ?? live.is_retrying,
|
|
52
|
+
awaiting: live.awaiting,
|
|
53
|
+
working_task_ids: live.workingTaskIds || live.working_task_ids,
|
|
54
|
+
}
|
|
55
|
+
: undefined,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { GrokBotClient } from "../connect/client.js";
|
|
2
|
+
import { type ResultEnvelope } from "../dto/result.js";
|
|
3
|
+
import type { SendDelivery, SendStatusView, TranscriptEntryView } from "../dto/types.js";
|
|
4
|
+
export declare class MessageOps {
|
|
5
|
+
private client;
|
|
6
|
+
private flags;
|
|
7
|
+
constructor(client: GrokBotClient, flags?: {
|
|
8
|
+
simulated?: boolean;
|
|
9
|
+
});
|
|
10
|
+
private sim;
|
|
11
|
+
send(input: {
|
|
12
|
+
agentId: string;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
text: string;
|
|
15
|
+
messageId?: string;
|
|
16
|
+
dryRun?: boolean;
|
|
17
|
+
allowWrite?: boolean;
|
|
18
|
+
}): Promise<ResultEnvelope<SendDelivery>>;
|
|
19
|
+
status(input: {
|
|
20
|
+
agentId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
messageId: string;
|
|
23
|
+
}): Promise<ResultEnvelope<SendStatusView>>;
|
|
24
|
+
history(input: {
|
|
25
|
+
agentId: string;
|
|
26
|
+
limit?: number;
|
|
27
|
+
}): Promise<ResultEnvelope<{
|
|
28
|
+
entries: TranscriptEntryView[];
|
|
29
|
+
generation?: number;
|
|
30
|
+
note: string;
|
|
31
|
+
}>>;
|
|
32
|
+
private fromErr;
|
|
33
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { mapConnectError } from "../connect/client.js";
|
|
2
|
+
import { failResult, newRequestId, okResult } from "../dto/result.js";
|
|
3
|
+
import { GbcError } from "../errors.js";
|
|
4
|
+
import { validateLimit } from "./limits.js";
|
|
5
|
+
function requireWrite(allow, action) {
|
|
6
|
+
if (!allow && process.env.GBT_ALLOW_WRITES !== "1") {
|
|
7
|
+
throw new GbcError("denied", `refusing ${action}: need --allow-writes / GBT_ALLOW_WRITES=1`, { exitCode: 7 });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class MessageOps {
|
|
11
|
+
client;
|
|
12
|
+
flags;
|
|
13
|
+
constructor(client, flags = {}) {
|
|
14
|
+
this.client = client;
|
|
15
|
+
this.flags = flags;
|
|
16
|
+
}
|
|
17
|
+
sim(r) {
|
|
18
|
+
if (this.flags.simulated)
|
|
19
|
+
r.simulated = true;
|
|
20
|
+
return r;
|
|
21
|
+
}
|
|
22
|
+
async send(input) {
|
|
23
|
+
const request_id = newRequestId();
|
|
24
|
+
const message_id = input.messageId || crypto.randomUUID();
|
|
25
|
+
if (input.dryRun) {
|
|
26
|
+
return this.sim(okResult({
|
|
27
|
+
message_id,
|
|
28
|
+
agent_id: input.agentId,
|
|
29
|
+
session_id: input.sessionId,
|
|
30
|
+
note: "dry-run: would SendGrokBotUserMessage (no task_id required)",
|
|
31
|
+
}, { request_id, code: "dry_run" }));
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
requireWrite(input.allowWrite, "message.send");
|
|
35
|
+
const res = await this.client.sendGrokBotUserMessage({
|
|
36
|
+
agentId: input.agentId,
|
|
37
|
+
sessionId: input.sessionId,
|
|
38
|
+
messageId: message_id,
|
|
39
|
+
text: input.text,
|
|
40
|
+
sentAtMs: BigInt(Date.now()),
|
|
41
|
+
});
|
|
42
|
+
return this.sim(okResult({
|
|
43
|
+
message_id,
|
|
44
|
+
agent_id: input.agentId,
|
|
45
|
+
session_id: input.sessionId,
|
|
46
|
+
dispatched: res.dispatched,
|
|
47
|
+
delivery: String(res.delivery ?? ""),
|
|
48
|
+
note: "delivery/acceptance only — NOT task or business success",
|
|
49
|
+
}, { request_id }));
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return this.fromErr(e, request_id, true);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async status(input) {
|
|
56
|
+
const request_id = newRequestId();
|
|
57
|
+
try {
|
|
58
|
+
const res = await this.client.getGrokBotSendStatus({
|
|
59
|
+
agentId: input.agentId,
|
|
60
|
+
sessionId: input.sessionId,
|
|
61
|
+
messageId: input.messageId,
|
|
62
|
+
});
|
|
63
|
+
return this.sim(okResult({
|
|
64
|
+
agent_id: input.agentId,
|
|
65
|
+
session_id: input.sessionId,
|
|
66
|
+
message_id: input.messageId,
|
|
67
|
+
status: String(res.status ?? ""),
|
|
68
|
+
rejection_code: res.rejectionCode,
|
|
69
|
+
accepted_at_ms: res.acceptedAtMs != null ? String(res.acceptedAtMs) : undefined,
|
|
70
|
+
note: "send_status ≠ final user-visible success",
|
|
71
|
+
}, { request_id }));
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
return this.fromErr(e, request_id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async history(input) {
|
|
78
|
+
const request_id = newRequestId();
|
|
79
|
+
try {
|
|
80
|
+
const limit = validateLimit(input.limit);
|
|
81
|
+
// NOTE: server-side `limit`/`session_id` are still not sent (semantics of 0 unverified); client caps.
|
|
82
|
+
const res = await this.client.listGrokBotTranscriptEntries({
|
|
83
|
+
agentId: input.agentId,
|
|
84
|
+
});
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
const entries = (res.entries ?? []).slice(0, limit).map((e) => {
|
|
87
|
+
const id = e.id || e.entryId || e.entry_id;
|
|
88
|
+
const preview = e.plaintext || e.text || e.preview ||
|
|
89
|
+
(e.ciphertext || e.omitted ? undefined : undefined);
|
|
90
|
+
return {
|
|
91
|
+
entry_id: id,
|
|
92
|
+
kind: String(e.entryKind ?? e.kind ?? e.type ?? ""),
|
|
93
|
+
omitted: Boolean(e.bodyOmitted || e.omitted || e.ciphertext),
|
|
94
|
+
preview: preview ? String(preview).slice(0, 200) : undefined,
|
|
95
|
+
untrusted: true,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
return this.sim(okResult({
|
|
99
|
+
entries,
|
|
100
|
+
generation: res.generation,
|
|
101
|
+
note: "Ciphertext/omitted entries are not decrypted. Bodies marked untrusted.",
|
|
102
|
+
}, { request_id }));
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
return this.fromErr(e, request_id);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
fromErr(e, request_id, write = false) {
|
|
109
|
+
const err = e instanceof GbcError ? e : mapConnectError(e, request_id, { write });
|
|
110
|
+
return this.sim(failResult(err.code, err.message, {
|
|
111
|
+
outcome: err.outcome,
|
|
112
|
+
retryable: err.retryable,
|
|
113
|
+
hint: err.hint,
|
|
114
|
+
request_id,
|
|
115
|
+
details: err.details,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SyncOps } from "../sync/core.js";
|
|
2
|
+
import type { AuthProvider } from "../auth/types.js";
|
|
3
|
+
import { type ConnectConfig, type GrokBotClient } from "../connect/client.js";
|
|
4
|
+
import { AuthOps } from "./auth.js";
|
|
5
|
+
import { BotOps } from "./bot.js";
|
|
6
|
+
import { MessageOps } from "./message.js";
|
|
7
|
+
import { ProgressOps } from "./progress.js";
|
|
8
|
+
import { SessionOps } from "./session.js";
|
|
9
|
+
import { SidebarOps } from "./sidebar.js";
|
|
10
|
+
export type ControlPlaneOptions = ConnectConfig & {
|
|
11
|
+
/** Marks all results as simulated (mock/in-memory). */
|
|
12
|
+
simulated?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare class ControlPlane {
|
|
15
|
+
readonly client: GrokBotClient;
|
|
16
|
+
readonly sync: SyncOps;
|
|
17
|
+
readonly bot: BotOps;
|
|
18
|
+
readonly message: MessageOps;
|
|
19
|
+
readonly session: SessionOps;
|
|
20
|
+
readonly progress: ProgressOps;
|
|
21
|
+
readonly sidebar: SidebarOps;
|
|
22
|
+
readonly auth?: AuthProvider;
|
|
23
|
+
readonly authOps: AuthOps;
|
|
24
|
+
readonly simulated: boolean;
|
|
25
|
+
constructor(opts: ControlPlaneOptions);
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SyncOps } from "../sync/core.js";
|
|
2
|
+
import { createGrokBotClient } from "../connect/client.js";
|
|
3
|
+
import { AuthOps } from "./auth.js";
|
|
4
|
+
import { BotOps } from "./bot.js";
|
|
5
|
+
import { MessageOps } from "./message.js";
|
|
6
|
+
import { ProgressOps } from "./progress.js";
|
|
7
|
+
import { SessionOps } from "./session.js";
|
|
8
|
+
import { SidebarOps } from "./sidebar.js";
|
|
9
|
+
export class ControlPlane {
|
|
10
|
+
client;
|
|
11
|
+
sync;
|
|
12
|
+
bot;
|
|
13
|
+
message;
|
|
14
|
+
session;
|
|
15
|
+
progress;
|
|
16
|
+
sidebar;
|
|
17
|
+
auth;
|
|
18
|
+
authOps;
|
|
19
|
+
simulated;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
this.auth = opts.auth;
|
|
22
|
+
this.simulated = Boolean(opts.simulated);
|
|
23
|
+
this.client = createGrokBotClient(opts);
|
|
24
|
+
this.sync = new SyncOps(createGrokBotClient({ ...opts, readMaxBytes: 16 * 1024 * 1024 }), opts.baseUrl, this.simulated);
|
|
25
|
+
const f = { simulated: this.simulated };
|
|
26
|
+
this.authOps = new AuthOps(this.auth, f);
|
|
27
|
+
this.bot = new BotOps(this.client, f);
|
|
28
|
+
this.message = new MessageOps(this.client, f);
|
|
29
|
+
this.session = new SessionOps(this.client, f);
|
|
30
|
+
this.progress = new ProgressOps(this.client, f);
|
|
31
|
+
this.sidebar = new SidebarOps(this.client, f);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { GrokBotClient } from "../connect/client.js";
|
|
2
|
+
import { type ResultEnvelope } from "../dto/result.js";
|
|
3
|
+
import type { ProgressStatus } from "../dto/types.js";
|
|
4
|
+
export declare class ProgressOps {
|
|
5
|
+
private client;
|
|
6
|
+
private flags;
|
|
7
|
+
constructor(client: GrokBotClient, flags?: {
|
|
8
|
+
simulated?: boolean;
|
|
9
|
+
});
|
|
10
|
+
private sim;
|
|
11
|
+
status(agentIds: string[]): Promise<ResultEnvelope<{
|
|
12
|
+
statuses: ProgressStatus[];
|
|
13
|
+
}>>;
|
|
14
|
+
todos(agentId: string): Promise<ResultEnvelope<{
|
|
15
|
+
todos: unknown[];
|
|
16
|
+
}>>;
|
|
17
|
+
tasks(agentId: string): Promise<ResultEnvelope<{
|
|
18
|
+
tasks: unknown[];
|
|
19
|
+
}>>;
|
|
20
|
+
subagents(agentId: string): Promise<ResultEnvelope<{
|
|
21
|
+
subagents: unknown[];
|
|
22
|
+
}>>;
|
|
23
|
+
interrupt(agentId: string, opts: {
|
|
24
|
+
confirm?: string;
|
|
25
|
+
reason?: string;
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
dryRun?: boolean;
|
|
28
|
+
allowWrite?: boolean;
|
|
29
|
+
}): Promise<ResultEnvelope<{
|
|
30
|
+
had_active_run?: boolean;
|
|
31
|
+
note: string;
|
|
32
|
+
}>>;
|
|
33
|
+
private fromErr;
|
|
34
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { mapConnectError } from "../connect/client.js";
|
|
2
|
+
import { failResult, newRequestId, okResult } from "../dto/result.js";
|
|
3
|
+
import { GbcError } from "../errors.js";
|
|
4
|
+
import { mapStatus } from "./map.js";
|
|
5
|
+
export class ProgressOps {
|
|
6
|
+
client;
|
|
7
|
+
flags;
|
|
8
|
+
constructor(client, flags = {}) {
|
|
9
|
+
this.client = client;
|
|
10
|
+
this.flags = flags;
|
|
11
|
+
}
|
|
12
|
+
sim(r) {
|
|
13
|
+
if (this.flags.simulated)
|
|
14
|
+
r.simulated = true;
|
|
15
|
+
return r;
|
|
16
|
+
}
|
|
17
|
+
async status(agentIds) {
|
|
18
|
+
const request_id = newRequestId();
|
|
19
|
+
try {
|
|
20
|
+
const res = await this.client.listGrokBotAgentStatuses({ agentIds });
|
|
21
|
+
return this.sim(okResult({ statuses: (res.statuses ?? []).map(mapStatus) }, { request_id }));
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
return this.fromErr(e, request_id);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async todos(agentId) {
|
|
28
|
+
const request_id = newRequestId();
|
|
29
|
+
try {
|
|
30
|
+
const res = await this.client.listGrokBotAgentTodos({ agentId });
|
|
31
|
+
return this.sim(okResult({ todos: res.todos ?? [] }, { request_id }));
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
return this.fromErr(e, request_id);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async tasks(agentId) {
|
|
38
|
+
const request_id = newRequestId();
|
|
39
|
+
try {
|
|
40
|
+
const res = await this.client.listGrokBotAgentTasks({ agentId });
|
|
41
|
+
return this.sim(okResult({ tasks: res.tasks ?? [] }, { request_id }));
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
return this.fromErr(e, request_id);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async subagents(agentId) {
|
|
48
|
+
const request_id = newRequestId();
|
|
49
|
+
try {
|
|
50
|
+
const res = await this.client.listGrokBotSubagents({ agentId });
|
|
51
|
+
return this.sim(okResult({ subagents: res.subagents ?? [] }, { request_id }));
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
return this.fromErr(e, request_id);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async interrupt(agentId, opts) {
|
|
58
|
+
const request_id = newRequestId();
|
|
59
|
+
if (opts.dryRun) {
|
|
60
|
+
return this.sim(okResult({ note: "dry-run: would InterruptGrokBotAgentRun (MCP cancel ≠ this)" }, { request_id, code: "dry_run" }));
|
|
61
|
+
}
|
|
62
|
+
if (opts.confirm !== agentId) {
|
|
63
|
+
return this.sim(failResult("denied", "interrupt requires --confirm=<agent_id>", { request_id }));
|
|
64
|
+
}
|
|
65
|
+
if (!opts.allowWrite && process.env.GBT_ALLOW_WRITES !== "1") {
|
|
66
|
+
return this.sim(failResult("denied", "need --allow-writes", { request_id }));
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const res = await this.client.interruptGrokBotAgentRun({
|
|
70
|
+
agentId,
|
|
71
|
+
reason: opts.reason ?? "gbc interrupt",
|
|
72
|
+
sessionId: opts.sessionId,
|
|
73
|
+
});
|
|
74
|
+
return this.sim(okResult({
|
|
75
|
+
had_active_run: res.hadActiveRun,
|
|
76
|
+
note: "InterruptGrokBotAgentRun invoked. Canceling an MCP/CLI HTTP request does NOT call this.",
|
|
77
|
+
}, { request_id }));
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return this.fromErr(e, request_id, true);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
fromErr(e, request_id, write = false) {
|
|
84
|
+
const err = e instanceof GbcError ? e : mapConnectError(e, request_id, { write });
|
|
85
|
+
return this.sim(failResult(err.code, err.message, {
|
|
86
|
+
outcome: err.outcome,
|
|
87
|
+
retryable: err.retryable,
|
|
88
|
+
hint: err.hint,
|
|
89
|
+
request_id,
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GrokBotClient } from "../connect/client.js";
|
|
2
|
+
import { type ResultEnvelope } from "../dto/result.js";
|
|
3
|
+
import type { SessionSummary } from "../dto/types.js";
|
|
4
|
+
export declare class SessionOps {
|
|
5
|
+
private client;
|
|
6
|
+
private flags;
|
|
7
|
+
constructor(client: GrokBotClient, flags?: {
|
|
8
|
+
simulated?: boolean;
|
|
9
|
+
});
|
|
10
|
+
list(agentId: string): Promise<ResultEnvelope<{
|
|
11
|
+
sessions: SessionSummary[];
|
|
12
|
+
}>>;
|
|
13
|
+
}
|