grok-bot-cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +54 -22
  3. package/dist/.agents/plugins/marketplace.json +1 -0
  4. package/dist/.claude-plugin/marketplace.json +1 -0
  5. package/dist/.claude-plugin/plugin.json +1 -0
  6. package/dist/.codex-plugin/mcp.json +1 -0
  7. package/dist/.codex-plugin/plugin.json +1 -0
  8. package/dist/.cursor-plugin/marketplace.json +1 -0
  9. package/dist/.cursor-plugin/mcp.json +1 -0
  10. package/dist/.cursor-plugin/plugin.json +1 -0
  11. package/dist/.mcp.json +1 -0
  12. package/dist/INSTALL.md +269 -0
  13. package/dist/LICENSE +21 -0
  14. package/dist/README.md +173 -0
  15. package/dist/agent-bundle.compile-evidence.json +1 -0
  16. package/dist/agent-bundle.manifest.json +1 -0
  17. package/dist/agent-bundle.package-compile-evidence.json +1 -0
  18. package/dist/bin/gbot-flight.mjs +29162 -0
  19. package/dist/bin/gbot-install.js +135699 -0
  20. package/dist/bin/gbot.mjs +87242 -0
  21. package/dist/install.mjs +1322 -0
  22. package/dist/mcp/mcp-grok-bot-b8c2461e-flight.mjs +26413 -0
  23. package/dist/mcp/mcp-grok-bot-b8c2461e.mjs +107760 -0
  24. package/dist/mcp.json +1 -0
  25. package/dist/package.json +60 -0
  26. package/dist/plugin.json +1 -0
  27. package/dist/skills/talk-to-grok-bot/SKILL.md +37 -0
  28. package/package.json +30 -10
  29. package/src/app-session.js +0 -249
  30. package/src/cli.js +0 -548
  31. package/src/codex-bridge.js +0 -480
  32. package/src/commands.js +0 -52
  33. package/src/gateway.js +0 -361
  34. package/src/headers.js +0 -53
  35. package/src/history.js +0 -83
  36. package/src/store.js +0 -358
  37. package/src/transcript.js +0 -50
  38. package/src/url-policy.js +0 -161
package/src/store.js DELETED
@@ -1,358 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import {
3
- existsSync,
4
- mkdirSync,
5
- readdirSync,
6
- readFileSync,
7
- rmSync,
8
- statSync,
9
- writeFileSync,
10
- } from "node:fs";
11
- import { homedir } from "node:os";
12
- import { join } from "node:path";
13
-
14
- export const GROUP_JSON_VERSION = 1;
15
- export const MAX_GROUP_MEMBERS = 6;
16
- export const PROFILE_FILE = "profile.json";
17
- export const GROUP_FILE = "group.json";
18
- export const SETTINGS_FILE = "settings.json";
19
-
20
- export const AVATAR_SHAPES = [
21
- "blob",
22
- "pebble",
23
- "bean",
24
- "egg",
25
- "squircle",
26
- "tablet",
27
- "capsule",
28
- "cylinder",
29
- "hex",
30
- "gem",
31
- "crystal",
32
- "wedge",
33
- "shield",
34
- "dome",
35
- "arch",
36
- "cloud",
37
- "teardrop",
38
- "leaf",
39
- ];
40
-
41
- export const AVATAR_COLORS = [
42
- "black",
43
- "brown",
44
- "red",
45
- "orange",
46
- "yellow",
47
- "green",
48
- "cyan",
49
- "blue",
50
- "violet",
51
- "magenta",
52
- "gray",
53
- ];
54
-
55
- const UUID_RE =
56
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
57
-
58
- export class StoreError extends Error {
59
- constructor(message) {
60
- super(message);
61
- this.name = "StoreError";
62
- }
63
- }
64
-
65
- function readJson(path) {
66
- return JSON.parse(readFileSync(path, "utf8"));
67
- }
68
-
69
- function writeJson(path, value) {
70
- writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
71
- }
72
-
73
- function isUuid(value) {
74
- return UUID_RE.test(value);
75
- }
76
-
77
- export function defaultCandidateRoots() {
78
- const home = homedir();
79
- const env = [
80
- process.env.GROK_BOT_AGENTS_DIR,
81
- process.env.SAND_AGENTS_DIR,
82
- process.env.SAND_DATA_ROOT && join(process.env.SAND_DATA_ROOT, "agents"),
83
- process.env.SAND_DATA_ROOT && join(process.env.SAND_DATA_ROOT, "agent-data", "agents"),
84
- ].filter(Boolean);
85
-
86
- return [
87
- ...env,
88
- "/home/box/agent-data/agents",
89
- "/home/box/sand-data/agents",
90
- join(home, ".grokbot", "agent-data", "agents"),
91
- join(home, ".grokbot", "sand-data", "agents"),
92
- join(home, "Library", "Application Support", "Grok Bot", "agent-data", "agents"),
93
- join(home, "Library", "Application Support", "Grok Bot", "sand-data", "agents"),
94
- ];
95
- }
96
-
97
- export function looksLikeAgentsRoot(dir) {
98
- if (!dir || !existsSync(dir) || !statSync(dir).isDirectory()) return false;
99
- const entries = readdirSync(dir, { withFileTypes: true });
100
- return entries.some(
101
- (e) => e.isDirectory() && isUuid(e.name) && existsSync(join(dir, e.name, PROFILE_FILE)),
102
- );
103
- }
104
-
105
- export function resolveAgentsRoot(explicit) {
106
- if (explicit) {
107
- mkdirSync(explicit, { recursive: true });
108
- return explicit;
109
- }
110
- for (const candidate of defaultCandidateRoots()) {
111
- if (looksLikeAgentsRoot(candidate)) return candidate;
112
- }
113
- throw new StoreError(
114
- "Could not find a Grok Bot agents directory. Pass --dir or set GROK_BOT_AGENTS_DIR.\nThe macOS app keeps the live roster on its cloud computer (agent-data/agents), not in ~/Library/Application Support/Grok Bot.",
115
- );
116
- }
117
-
118
- function readProfile(dir) {
119
- const path = join(dir, PROFILE_FILE);
120
- if (!existsSync(path)) return null;
121
- const raw = readJson(path);
122
- return {
123
- name: String(raw.name ?? ""),
124
- description: String(raw.description ?? ""),
125
- title: String(raw.title ?? ""),
126
- avatarShape: String(raw.avatarShape ?? ""),
127
- avatarColor: String(raw.avatarColor ?? ""),
128
- };
129
- }
130
-
131
- function readGroup(dir) {
132
- const path = join(dir, GROUP_FILE);
133
- if (!existsSync(path)) return null;
134
- const raw = readJson(path);
135
- const memberIds = Array.isArray(raw.memberIds) ? raw.memberIds.map(String) : [];
136
- return {
137
- version: Number(raw.version ?? GROUP_JSON_VERSION),
138
- memberIds,
139
- };
140
- }
141
-
142
- function readRawSettings(dir) {
143
- const path = join(dir, SETTINGS_FILE);
144
- if (!existsSync(path)) return {};
145
- try {
146
- const raw = readJson(path);
147
- return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
148
- } catch {
149
- return {};
150
- }
151
- }
152
-
153
- function readSettings(dir) {
154
- const raw = readRawSettings(dir);
155
- return {
156
- notifyOnAgentUpdates: typeof raw.notifyOnAgentUpdates === "boolean" ? raw.notifyOnAgentUpdates : true,
157
- hiddenFromSidebar: typeof raw.hiddenFromSidebar === "boolean" ? raw.hiddenFromSidebar : false,
158
- };
159
- }
160
-
161
- export function listRecords(root) {
162
- if (!existsSync(root)) return [];
163
- const out = [];
164
- for (const entry of readdirSync(root, { withFileTypes: true })) {
165
- if (!entry.isDirectory() || !isUuid(entry.name)) continue;
166
- const dir = join(root, entry.name);
167
- const profile = readProfile(dir);
168
- if (!profile) continue;
169
- const group = readGroup(dir);
170
- out.push({
171
- id: entry.name,
172
- path: dir,
173
- ...profile,
174
- ...readSettings(dir),
175
- isGroup: group != null,
176
- memberIds: group?.memberIds ?? [],
177
- });
178
- }
179
- return out.sort((a, b) => a.name.localeCompare(b.name) || a.id.localeCompare(b.id));
180
- }
181
-
182
- export function resolveRef(root, ref) {
183
- const records = listRecords(root);
184
- if (isUuid(ref)) {
185
- const exact = records.find((r) => r.id.toLowerCase() === ref.toLowerCase());
186
- if (!exact) throw new StoreError(`No bot or group with id ${ref}`);
187
- return exact;
188
- }
189
- const needle = ref.trim().toLowerCase();
190
- const matches = records.filter(
191
- (r) => r.name.toLowerCase() === needle || r.title.toLowerCase() === needle,
192
- );
193
- if (matches.length === 1) return matches[0];
194
- if (matches.length === 0) throw new StoreError(`No bot or group named "${ref}"`);
195
- throw new StoreError(
196
- `Ambiguous name "${ref}": ${matches.map((m) => `${m.name} (${m.id})`).join(", ")}`,
197
- );
198
- }
199
-
200
- function assertAvatar(kind, value, allowed) {
201
- const trimmed = (value ?? "").trim();
202
- if (!trimmed) return "";
203
- if (!allowed.includes(trimmed)) {
204
- throw new StoreError(`Unknown avatar ${kind} "${value}". Use: ${allowed.join(" ")}`);
205
- }
206
- return trimmed;
207
- }
208
-
209
- function writeProfile(dir, profile) {
210
- writeJson(join(dir, PROFILE_FILE), {
211
- name: profile.name.trim(),
212
- description: (profile.description ?? "").trim(),
213
- title: (profile.title ?? "").trim(),
214
- avatarShape: assertAvatar("shape", profile.avatarShape, AVATAR_SHAPES),
215
- avatarColor: assertAvatar("color", profile.avatarColor, AVATAR_COLORS),
216
- });
217
- }
218
-
219
- function writeSettings(dir) {
220
- writeJson(join(dir, SETTINGS_FILE), { notifyOnAgentUpdates: true });
221
- }
222
-
223
- function mergeSettings(dir, patch) {
224
- const update = {};
225
- if (patch.notifyOnAgentUpdates !== undefined) update.notifyOnAgentUpdates = Boolean(patch.notifyOnAgentUpdates);
226
- if (patch.hiddenFromSidebar !== undefined) update.hiddenFromSidebar = Boolean(patch.hiddenFromSidebar);
227
- if (Object.keys(update).length === 0) return;
228
- writeJson(join(dir, SETTINGS_FILE), { ...readRawSettings(dir), ...update });
229
- }
230
-
231
- function writeGroup(dir, memberIds) {
232
- writeJson(join(dir, GROUP_FILE), {
233
- version: GROUP_JSON_VERSION,
234
- memberIds,
235
- });
236
- }
237
-
238
- function normalizeMemberIds(root, memberRefs) {
239
- const records = listRecords(root);
240
- const groups = new Set(records.filter((r) => r.isGroup).map((r) => r.id));
241
- const resolved = [];
242
- const seen = new Set();
243
- for (const ref of memberRefs) {
244
- const rec = resolveRef(root, ref);
245
- if (groups.has(rec.id)) {
246
- throw new StoreError(`Cannot add group "${rec.name}" as a member. Nested groups are not allowed.`);
247
- }
248
- if (seen.has(rec.id)) continue;
249
- seen.add(rec.id);
250
- resolved.push(rec.id);
251
- }
252
- if (resolved.length > MAX_GROUP_MEMBERS) {
253
- throw new StoreError(`A group can have at most ${MAX_GROUP_MEMBERS} members.`);
254
- }
255
- return resolved;
256
- }
257
-
258
- export function createAgent(root, input) {
259
- const name = (input.name ?? "").trim();
260
- if (!name) throw new StoreError("Name is required.");
261
- const id = randomUUID();
262
- const dir = join(root, id);
263
- mkdirSync(dir, { recursive: true });
264
- mkdirSync(join(dir, "memory"), { recursive: true });
265
- mkdirSync(join(dir, "automations"), { recursive: true });
266
- writeProfile(dir, {
267
- name,
268
- description: input.description ?? "",
269
- title: input.title ?? "",
270
- avatarShape: input.avatarShape ?? "",
271
- avatarColor: input.avatarColor ?? "",
272
- });
273
- writeSettings(dir);
274
- return resolveRef(root, id);
275
- }
276
-
277
- export function updateAgent(root, ref, patch = {}) {
278
- const rec = resolveRef(root, ref);
279
- if (patch.name !== undefined) {
280
- const name = String(patch.name).trim();
281
- if (!name) throw new StoreError("Name cannot be blank.");
282
- }
283
- const hasProfile =
284
- patch.name !== undefined ||
285
- patch.description !== undefined ||
286
- patch.title !== undefined ||
287
- patch.avatarShape !== undefined ||
288
- patch.avatarColor !== undefined;
289
- if (hasProfile) {
290
- writeProfile(rec.path, {
291
- name: patch.name !== undefined ? String(patch.name) : rec.name,
292
- description: patch.description !== undefined ? String(patch.description) : rec.description,
293
- title: patch.title !== undefined ? String(patch.title) : rec.title,
294
- avatarShape: patch.avatarShape !== undefined ? String(patch.avatarShape) : rec.avatarShape,
295
- avatarColor: patch.avatarColor !== undefined ? String(patch.avatarColor) : rec.avatarColor,
296
- });
297
- }
298
- mergeSettings(rec.path, patch);
299
- return resolveRef(root, rec.id);
300
- }
301
-
302
- export function deleteAgent(root, ref) {
303
- const rec = resolveRef(root, ref);
304
- rmSync(rec.path, { recursive: true, force: true });
305
- for (const other of listRecords(root).filter((r) => r.isGroup)) {
306
- if (!other.memberIds.includes(rec.id)) continue;
307
- writeGroup(
308
- other.path,
309
- other.memberIds.filter((id) => id !== rec.id),
310
- );
311
- }
312
- return rec;
313
- }
314
-
315
- export function createGroup(root, input) {
316
- const memberIds = normalizeMemberIds(root, input.memberIds ?? []);
317
- if (memberIds.length === 0) {
318
- throw new StoreError("A group needs at least one existing member agent.");
319
- }
320
- const existing = listRecords(root).find(
321
- (r) =>
322
- r.isGroup &&
323
- r.memberIds.length === memberIds.length &&
324
- r.memberIds.every((id) => memberIds.includes(id)),
325
- );
326
- if (existing) return existing;
327
- const group = createAgent(root, {
328
- name: input.name,
329
- description: input.description ?? "",
330
- title: input.title ?? "",
331
- avatarShape: input.avatarShape ?? "",
332
- avatarColor: input.avatarColor ?? "",
333
- });
334
- writeGroup(join(root, group.id), memberIds);
335
- return resolveRef(root, group.id);
336
- }
337
-
338
- export function setGroupMembers(root, groupRef, memberRefs) {
339
- const group = resolveRef(root, groupRef);
340
- if (!group.isGroup) throw new StoreError(`"${group.name}" is a bot, not a group.`);
341
- const memberIds = normalizeMemberIds(root, memberRefs);
342
- if (memberIds.length === 0) {
343
- throw new StoreError("A group needs at least one existing member agent.");
344
- }
345
- writeGroup(group.path, memberIds);
346
- return resolveRef(root, group.id);
347
- }
348
-
349
- export function addGroupMember(root, groupRef, memberRef) {
350
- const group = resolveRef(root, groupRef);
351
- return setGroupMembers(root, group.id, [...group.memberIds, memberRef]);
352
- }
353
-
354
- export function removeGroupMember(root, groupRef, memberRef) {
355
- const group = resolveRef(root, groupRef);
356
- const member = resolveRef(root, memberRef);
357
- return setGroupMembers(root, group.id, group.memberIds.filter((id) => id !== member.id));
358
- }
package/src/transcript.js DELETED
@@ -1,50 +0,0 @@
1
- // Shared by `gbot thread` and the grok-bot plugin's gbot_thread tool.
2
-
3
- /** Coerce anything to a string without throwing (numbers, BigInt, unserializable objects). */
4
- export function toSafeText(value) {
5
- if (typeof value === "string") return value;
6
- if (value == null) return "";
7
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
8
- try {
9
- const out = JSON.stringify(value);
10
- return typeof out === "string" ? out : "";
11
- } catch {
12
- return "[unserializable]";
13
- }
14
- }
15
-
16
- /** Coerce to string and replace lone surrogates so downstream slicing/JSON never breaks. */
17
- export function normalizeText(value) {
18
- return toSafeText(value).replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
19
- }
20
-
21
- export function entryText(e) {
22
- return normalizeText(entryTextRaw(e));
23
- }
24
-
25
- function entryTextRaw(e) {
26
- if (!e || typeof e !== "object") return "";
27
- // Prefer full body fields over `preview` (often truncated for list UIs).
28
- const direct = e.text || e.prompt || e.message;
29
- if (typeof direct === "string" && direct) return direct;
30
- const content = e.content;
31
- if (typeof content === "string") return content;
32
- if (Array.isArray(content)) {
33
- return content.map((part) => {
34
- if (typeof part === "string") return part;
35
- if (part && typeof part === "object") return part.text || part.content || "";
36
- return "";
37
- }).filter(Boolean).join("\n");
38
- }
39
- if (content && typeof content === "object") return content.text || toSafeText(content);
40
- // Bot replies arrive as `{ kind: "send-message", message: { type, content } }`.
41
- if (e.message && typeof e.message === "object" && typeof e.message.content === "string") return e.message.content;
42
- return typeof e.preview === "string" ? e.preview : "";
43
- }
44
-
45
- export function transcriptEntries(payload) {
46
- if (Array.isArray(payload)) return payload;
47
- if (!payload || typeof payload !== "object") return [];
48
- const entries = payload.entries || payload.messages || payload.items;
49
- return Array.isArray(entries) ? entries : [];
50
- }
package/src/url-policy.js DELETED
@@ -1,161 +0,0 @@
1
- /**
2
- * Gateway / backend URL policy: only send credentials to expected hosts.
3
- *
4
- * Gateway (box + API): https on *.cursor.sh / *.cursor.com (and apex) /
5
- * *.cursorvm.com (EnsureSandBox box hosts, e.g. <id>-pod-<id>.us12.cursorvm.com).
6
- * Backend (EnsureSandBox / Cursor API): https on *.cursor.sh / *.cursor.com only —
7
- * never *.cursorvm.com, so a CURSOR_ACCESS_TOKEN cannot be pointed at a box host.
8
- * Local/dev gateways: http(s)://127.0.0.1|localhost|::1 when GROK_BOT_ALLOW_LOCAL_GATEWAY=1.
9
- * Escape hatch: GROK_BOT_ALLOW_ANY_GATEWAY=1 (unsafe; disables host checks; warns once).
10
- */
11
-
12
- function truthyEnv(name) {
13
- const v = (process.env[name] || "").trim().toLowerCase();
14
- return v === "1" || v === "true" || v === "yes";
15
- }
16
-
17
- export function allowAnyGateway() {
18
- return truthyEnv("GROK_BOT_ALLOW_ANY_GATEWAY");
19
- }
20
-
21
- export function allowLocalGateway() {
22
- return truthyEnv("GROK_BOT_ALLOW_LOCAL_GATEWAY");
23
- }
24
-
25
- const warned = new Set();
26
-
27
- function warnOnce(key, message) {
28
- if (warned.has(key)) return;
29
- warned.add(key);
30
- process.stderr.write(message + "\n");
31
- }
32
-
33
- /** Test hook: clear the one-shot warn set. */
34
- export function resetPolicyWarnings() {
35
- warned.clear();
36
- }
37
-
38
- function isLocalHostname(hostname) {
39
- const h = String(hostname || "").toLowerCase().replace(/^\[|\]$/g, "");
40
- return h === "localhost" || h === "127.0.0.1" || h === "::1";
41
- }
42
-
43
- function isCursorApiHostname(hostname) {
44
- const h = String(hostname || "").toLowerCase();
45
- if (!h) return false;
46
- if (h === "cursor.sh" || h === "cursor.com") return true;
47
- return h.endsWith(".cursor.sh") || h.endsWith(".cursor.com");
48
- }
49
-
50
- function isCursorGatewayHostname(hostname) {
51
- const h = String(hostname || "").toLowerCase();
52
- return isCursorApiHostname(h) || h.endsWith(".cursorvm.com");
53
- }
54
-
55
- /**
56
- * @param {string} rawUrl
57
- * @param {{ kind?: "gateway" | "backend" }} [opts]
58
- * @returns {string} normalized URL without trailing slash
59
- */
60
- export function assertAllowedCredentialUrl(rawUrl, opts = {}) {
61
- const kind = opts.kind || "gateway";
62
- const label = kind === "backend" ? "backend URL" : "gateway URL";
63
- let parsed;
64
- try {
65
- parsed = new URL(String(rawUrl));
66
- } catch {
67
- throw new Error("Invalid " + label + ".");
68
- }
69
-
70
- if (parsed.username || parsed.password) {
71
- throw new Error("Rejected " + label + ": userinfo is not allowed.");
72
- }
73
-
74
- if (allowAnyGateway()) {
75
- warnOnce(
76
- "ALLOW_ANY",
77
- "warning: GROK_BOT_ALLOW_ANY_GATEWAY is set; credential host checks are disabled.",
78
- );
79
- return String(rawUrl).replace(/\/$/, "");
80
- }
81
-
82
- const host = parsed.hostname;
83
- const local = isLocalHostname(host);
84
-
85
- if (local) {
86
- if (kind === "backend") {
87
- throw new Error(
88
- "Rejected backend URL host \"" +
89
- host +
90
- "\". EnsureSandBox backends must be https on *.cursor.sh / *.cursor.com.",
91
- );
92
- }
93
- if (!allowLocalGateway()) {
94
- throw new Error(
95
- "Rejected " +
96
- label +
97
- " host \"" +
98
- host +
99
- "\". Set GROK_BOT_ALLOW_LOCAL_GATEWAY=1 to permit localhost/127.0.0.1 gateways.",
100
- );
101
- }
102
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
103
- throw new Error("Rejected " + label + ": local gateways must use http or https.");
104
- }
105
- warnOnce(
106
- "ALLOW_LOCAL",
107
- "warning: GROK_BOT_ALLOW_LOCAL_GATEWAY is set; credentials may be sent to a loopback gateway.",
108
- );
109
- return String(rawUrl).replace(/\/$/, "");
110
- }
111
-
112
- if (parsed.protocol !== "https:") {
113
- throw new Error("Rejected " + label + ": only https is allowed (got " + parsed.protocol + ").");
114
- }
115
-
116
- const allowed =
117
- kind === "backend" ? isCursorApiHostname(host) : isCursorGatewayHostname(host);
118
- if (!allowed) {
119
- const expected =
120
- kind === "backend"
121
- ? "*.cursor.sh / *.cursor.com"
122
- : "*.cursor.sh / *.cursor.com / *.cursorvm.com";
123
- throw new Error(
124
- "Rejected " +
125
- label +
126
- " host \"" +
127
- host +
128
- "\". Expected " +
129
- expected +
130
- ", or set GROK_BOT_ALLOW_LOCAL_GATEWAY=1 / GROK_BOT_ALLOW_ANY_GATEWAY=1.",
131
- );
132
- }
133
-
134
- return String(rawUrl).replace(/\/$/, "");
135
- }
136
-
137
- /**
138
- * Redact common credential shapes from error / log strings.
139
- * Covers Bearer/Basic/scheme Authorization values, cookie headers, and named token fields.
140
- */
141
- export function redactSecrets(text) {
142
- let s = String(text);
143
- // Cookie headers first so a later Authorization pass cannot swallow them.
144
- // Stop the value before the next header-shaped token on the same line.
145
- s = s.replace(
146
- /(^|[\s,{;])((?:set-cookie|cookie)\s*[:=]\s*)([^\n;]+?)(?=\s+(?:set-cookie|cookie|authorization|proxy-authorization)\b|\s*$)/gi,
147
- "$1$2<redacted>",
148
- );
149
- // Scheme + credential only (e.g. "Basic abc", "Bearer xyz") — not the rest of the line.
150
- s = s.replace(
151
- /(^|[\s,{;])((?:authorization|proxy-authorization)\s*[:=]\s*)(\S+(?:\s+\S+)?)/gi,
152
- "$1$2<redacted>",
153
- );
154
- s = s.replace(/Bearer\s+[A-Za-z0-9._+\/=-]+/gi, "Bearer <redacted>");
155
- s = s.replace(
156
- /(["']?(?:authorization|gatewayToken|gateway_token|access_token|accessToken|refresh_token|refreshToken|token|x-anyrun-network-token|cookie|set-cookie)["']?\s*[:=]\s*["']?)([^"',\s}]+)/gi,
157
- "$1<redacted>",
158
- );
159
- s = s.replace(/(x-anyrun-network-token\s*[=:]\s*)(\S+)/gi, "$1<redacted>");
160
- return s;
161
- }