@bakers-build/addon-grok 0.5.1-next.557 → 0.5.1-next.746
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/dist/src/index.d.ts +8 -2
- package/dist/src/index.js +161 -4
- package/package.json +3 -3
package/dist/src/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type AgentFootprint, type AgentPlugin, type AgentRunIdentity, type BbAgent } from '@bakers-build/conductor/contract';
|
|
1
|
+
import { type AgentFootprint, type AgentPlugin, type AgentRunIdentity, type BbAgent, type RunEnvPolicy, type SandboxHost, type SandboxPreflight, type SecurityOptions } from '@bakers-build/conductor/contract';
|
|
2
2
|
export { parseGrokTranscript } from './footprint.js';
|
|
3
3
|
export type GrokReasoningEffort = 'high' | 'medium' | 'low';
|
|
4
|
-
export interface GrokAgentOptions {
|
|
4
|
+
export interface GrokAgentOptions extends SecurityOptions {
|
|
5
5
|
model?: string;
|
|
6
6
|
reasoningEffort?: GrokReasoningEffort | string;
|
|
7
7
|
effort?: string;
|
|
@@ -15,8 +15,14 @@ export declare class GrokAgent implements BbAgent {
|
|
|
15
15
|
private readonly model;
|
|
16
16
|
private readonly effort;
|
|
17
17
|
private readonly overrides;
|
|
18
|
+
private readonly security;
|
|
19
|
+
readonly runEnvPolicy: RunEnvPolicy;
|
|
18
20
|
constructor(options?: GrokAgentOptions, home?: string);
|
|
19
21
|
launchCommand(prompt: string, run?: AgentRunIdentity): string;
|
|
22
|
+
/** The launch flags a security mode owns (`docs/addons/grok.md`). */
|
|
23
|
+
private securityFlags;
|
|
24
|
+
/** Grok's sandbox is Landlock on Linux and Seatbelt on macOS. */
|
|
25
|
+
sandboxPreflight(host?: SandboxHost): Promise<SandboxPreflight>;
|
|
20
26
|
/**
|
|
21
27
|
* `grok --session-id <uuid>` documents "new conversation… must not already
|
|
22
28
|
* exist" (BB-473 review round 2), so the conductor checks this before
|
package/dist/src/index.js
CHANGED
|
@@ -1,11 +1,112 @@
|
|
|
1
1
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { resolveAgentCliOptions, validateOptionalString, } from '@bakers-build/conductor/contract';
|
|
4
|
+
import { resolveAgentCliOptions, resolveSecurityOptions, systemSandboxHost, validateOptionalString, } from '@bakers-build/conductor/contract';
|
|
5
5
|
import { shellQuote } from '@bakers-build/core';
|
|
6
6
|
import { parseGrokTranscript } from './footprint.js';
|
|
7
7
|
export { parseGrokTranscript } from './footprint.js';
|
|
8
|
-
const GROK_RESERVED = ['--permission-mode', '--session-id'];
|
|
8
|
+
const GROK_RESERVED = ['--permission-mode', '--session-id', '-s'];
|
|
9
|
+
/** Flags sandbox mode owns on top of `GROK_RESERVED` (`docs/addons/grok.md`). */
|
|
10
|
+
const GROK_SANDBOX_RESERVED = [
|
|
11
|
+
...GROK_RESERVED,
|
|
12
|
+
'--sandbox',
|
|
13
|
+
'--always-approve',
|
|
14
|
+
'--allow',
|
|
15
|
+
'--allowedTools',
|
|
16
|
+
'--allowed-tools',
|
|
17
|
+
'--add-dir',
|
|
18
|
+
// the workspace profile makes the cwd writable; agent definitions may carry their own permissions
|
|
19
|
+
'--cwd',
|
|
20
|
+
'-w',
|
|
21
|
+
'--worktree',
|
|
22
|
+
'--agents',
|
|
23
|
+
'--agent',
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Grok splits short-flag clusters (`-cw` is `-c -w`), which the shared matcher
|
|
27
|
+
* only checks at the token's start. Only `-m`/`-p` end the scan: their value
|
|
28
|
+
* follows. Fail-closed: an optional-value flag such as `-r` does not end it.
|
|
29
|
+
*/
|
|
30
|
+
function rejectClusteredReserved(args, reserved) {
|
|
31
|
+
const letters = reserved.filter((f) => /^-[^-]$/.test(f)).map((f) => f[1]);
|
|
32
|
+
for (const token of args) {
|
|
33
|
+
if (!/^-[^-]/.test(token))
|
|
34
|
+
continue;
|
|
35
|
+
for (const letter of token.slice(1)) {
|
|
36
|
+
if (letters.includes(letter)) {
|
|
37
|
+
throw new Error(`args contains reserved flag -${letter}`);
|
|
38
|
+
}
|
|
39
|
+
if (letter === 'm' || letter === 'p')
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Grok flags whose value is the next token (`grok --help`, grok 1.0.70). */
|
|
45
|
+
const GROK_VALUE_FLAGS = new Set([
|
|
46
|
+
'--model',
|
|
47
|
+
'--single',
|
|
48
|
+
'--resume',
|
|
49
|
+
'--debug-file',
|
|
50
|
+
'--deny',
|
|
51
|
+
'--disallowedTools',
|
|
52
|
+
'--disallowed-tools',
|
|
53
|
+
'--json-schema',
|
|
54
|
+
'--leader-socket',
|
|
55
|
+
'--max-turns',
|
|
56
|
+
'--output-format',
|
|
57
|
+
'--prompt-file',
|
|
58
|
+
'--prompt-json',
|
|
59
|
+
'--reasoning-effort',
|
|
60
|
+
'--effort',
|
|
61
|
+
'--rules',
|
|
62
|
+
'--system-prompt-override',
|
|
63
|
+
'--system-prompt',
|
|
64
|
+
'--tools',
|
|
65
|
+
'--worktree-ref',
|
|
66
|
+
'--ref',
|
|
67
|
+
]);
|
|
68
|
+
/**
|
|
69
|
+
* After the prompt, Grok parses a bare token as a subcommand (`wrap`, `agent`),
|
|
70
|
+
* which replaces the sandboxed session. Allowlist: only a value of a known
|
|
71
|
+
* value flag may be bare. A short cluster qualifies only when its last letter
|
|
72
|
+
* is the first that takes a value (`-cm`): in `-pm` the `m` is `-p`'s value.
|
|
73
|
+
* `-c`, `-h`, `-v` are Grok's only short flags without a value.
|
|
74
|
+
*/
|
|
75
|
+
function rejectBareArgs(args) {
|
|
76
|
+
args.forEach((token, i) => {
|
|
77
|
+
if (token.startsWith('-'))
|
|
78
|
+
return;
|
|
79
|
+
const prev = args[i - 1] ?? '';
|
|
80
|
+
if (GROK_VALUE_FLAGS.has(prev) || /^-[chv]*[mpr]$/.test(prev))
|
|
81
|
+
return;
|
|
82
|
+
throw new Error(`args contains bare token ${token}, which Grok would run as a subcommand outside the sandboxed session`);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** Only what Grok's built-in `workspace` profile enforces. */
|
|
86
|
+
const SANDBOX_DEFAULTS = {
|
|
87
|
+
trusted_commands: [],
|
|
88
|
+
deny_read: [],
|
|
89
|
+
allow_write: [],
|
|
90
|
+
network: ['*'],
|
|
91
|
+
mcp_servers: {},
|
|
92
|
+
};
|
|
93
|
+
/** Throws on a sandbox key Grok cannot enforce from the command line. */
|
|
94
|
+
function rejectUnsupportedSandbox(sandbox) {
|
|
95
|
+
if (sandbox.network.length !== 1 || sandbox.network[0] !== '*') {
|
|
96
|
+
throw new Error("Grok cannot restrict sandbox.network: its workspace profile allows all hosts; leave network at ['*']");
|
|
97
|
+
}
|
|
98
|
+
if (Object.keys(sandbox.mcp_servers).length > 0) {
|
|
99
|
+
throw new Error('Grok cannot allow only named sandbox.mcp_servers: sandbox mode passes a deny rule for every MCP tool; leave mcp_servers empty');
|
|
100
|
+
}
|
|
101
|
+
if (sandbox.trusted_commands.length > 0) {
|
|
102
|
+
throw new Error("Grok has no command exception narrowed to executable, arguments and directory for sandbox.trusted_commands; use security: 'system' with your own sandbox.toml profile");
|
|
103
|
+
}
|
|
104
|
+
for (const key of ['deny_read', 'allow_write']) {
|
|
105
|
+
if (sandbox[key].length > 0) {
|
|
106
|
+
throw new Error(`Grok needs a sandbox.toml profile for sandbox.${key}, which bakers.build does not write; use security: 'system' with args: ['--sandbox', '<profile>']`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
9
110
|
const GROK_KNOWN = [
|
|
10
111
|
{ kind: 'model', flags: ['--model', '-m'] },
|
|
11
112
|
{ kind: 'effort', flags: ['--reasoning-effort', '--effort'] },
|
|
@@ -39,9 +140,21 @@ export class GrokAgent {
|
|
|
39
140
|
model;
|
|
40
141
|
effort;
|
|
41
142
|
overrides;
|
|
143
|
+
security;
|
|
144
|
+
runEnvPolicy;
|
|
42
145
|
constructor(options = {}, home = homedir()) {
|
|
43
146
|
this.home = home;
|
|
44
|
-
|
|
147
|
+
this.security = resolveSecurityOptions(options, SANDBOX_DEFAULTS);
|
|
148
|
+
this.runEnvPolicy = this.security.runEnvPolicy;
|
|
149
|
+
if (this.security.mode === 'custom') {
|
|
150
|
+
throw new Error("Grok has no config flags for security: 'custom'; use security: 'system' with args: ['--sandbox', '<profile>'] for your own sandbox.toml profile");
|
|
151
|
+
}
|
|
152
|
+
rejectUnsupportedSandbox(this.security.sandbox);
|
|
153
|
+
const reserved = this.security.mode === 'sandbox' ? GROK_SANDBOX_RESERVED : GROK_RESERVED;
|
|
154
|
+
const cli = resolveAgentCliOptions(options, reserved, GROK_KNOWN);
|
|
155
|
+
rejectClusteredReserved(cli.args, reserved);
|
|
156
|
+
if (this.security.mode === 'sandbox')
|
|
157
|
+
rejectBareArgs(cli.args);
|
|
45
158
|
this.args = cli.args;
|
|
46
159
|
this.model = cli.model;
|
|
47
160
|
this.effort =
|
|
@@ -58,7 +171,7 @@ export class GrokAgent {
|
|
|
58
171
|
// dispatched into instead of holding a session there.
|
|
59
172
|
parts.push(shellQuote(prompt));
|
|
60
173
|
}
|
|
61
|
-
parts.push(
|
|
174
|
+
parts.push(...this.securityFlags());
|
|
62
175
|
if (this.model !== undefined && !this.overrides.model) {
|
|
63
176
|
parts.push('-m', shellQuote(this.model));
|
|
64
177
|
}
|
|
@@ -77,6 +190,50 @@ export class GrokAgent {
|
|
|
77
190
|
}
|
|
78
191
|
return parts.join(' ');
|
|
79
192
|
}
|
|
193
|
+
/** The launch flags a security mode owns (`docs/addons/grok.md`). */
|
|
194
|
+
securityFlags() {
|
|
195
|
+
if (this.security.mode === 'system')
|
|
196
|
+
return [];
|
|
197
|
+
if (this.security.mode === 'yolo') {
|
|
198
|
+
return ['--permission-mode', 'bypassPermissions'];
|
|
199
|
+
}
|
|
200
|
+
return [
|
|
201
|
+
'--sandbox',
|
|
202
|
+
'workspace',
|
|
203
|
+
'--permission-mode',
|
|
204
|
+
'acceptEdits',
|
|
205
|
+
'--allow',
|
|
206
|
+
shellQuote('Bash(*)'),
|
|
207
|
+
'--deny',
|
|
208
|
+
shellQuote('MCPTool(*)'),
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
/** Grok's sandbox is Landlock on Linux and Seatbelt on macOS. */
|
|
212
|
+
async sandboxPreflight(host = systemSandboxHost()) {
|
|
213
|
+
if (this.security.mode !== 'sandbox' || host.platform === 'darwin') {
|
|
214
|
+
return { ok: true };
|
|
215
|
+
}
|
|
216
|
+
const fallback = "set security: 'system' or 'yolo' for Grok";
|
|
217
|
+
if (host.platform !== 'linux') {
|
|
218
|
+
return {
|
|
219
|
+
ok: false,
|
|
220
|
+
cause: `Grok's sandbox does not support ${host.platform}`,
|
|
221
|
+
fix: fallback,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const probe = await host.run('grep', [
|
|
225
|
+
'-qw',
|
|
226
|
+
'landlock',
|
|
227
|
+
'/sys/kernel/security/lsm',
|
|
228
|
+
]);
|
|
229
|
+
return probe.ok
|
|
230
|
+
? { ok: true }
|
|
231
|
+
: {
|
|
232
|
+
ok: false,
|
|
233
|
+
cause: 'Landlock is not active in this kernel',
|
|
234
|
+
fix: `boot a kernel with landlock in its lsm= list, or ${fallback}`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
80
237
|
/**
|
|
81
238
|
* `grok --session-id <uuid>` documents "new conversation… must not already
|
|
82
239
|
* exist" (BB-473 review round 2), so the conductor checks this before
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakers-build/addon-grok",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.746",
|
|
4
4
|
"description": "bakers.build agent plugin for xAI Grok Build, with a real session footprint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"directory": "bakers-cli/addons/grok"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@bakers-build/conductor": "^0.5.1-next.
|
|
24
|
-
"@bakers-build/core": "^0.5.1-next.
|
|
23
|
+
"@bakers-build/conductor": "^0.5.1-next.746",
|
|
24
|
+
"@bakers-build/core": "^0.5.1-next.746"
|
|
25
25
|
}
|
|
26
26
|
}
|