Synced from monorepo

Changes:
- Non-blocking coding-data sharing upsell banner
- Consolidate remediation in Doctor
- Auto mode defers fail-closed gate asks to the classifier
- Coalesce marketplace list fetches
- Allow removing a marketplace source by name
- Contain hung git marketplace sources (timeouts, non-blocking refresh, unbrick modal)
- Label failed workspace RPCs with error_kind
- Drop redundant explicit tonic/prost deps from xai-grok-shell
- Report real exit codes for completed background shells
- Narrow the date-rollover reminder to date-bearing templates
- Wire toolOverrides through the session and agent
- Security: Bash(git:*) allowlist matches whole command chain by prefix
- Split prompt-trigger telemetry and record classifier provenance
- Raise connectors-manager timeout to 60s
- Auto classifier honors recorded approvals for repeat actions
- Apply doctor fixes in the TUI
- Auto-mode classifier timeouts prompt instead of silently denying
- Scope subagent completion drains to the owning session
- Add the toolOverrides wire types
- Set client_identifier=grok-agent-sdk
- Accept both spellings of the workspace-teleport kill switch
- Persist one-shot occurrence journal
- Stop turns that poll the exact same tool call 16x in a row
- Copy compaction checkpoint files when forking sessions
- Auto-focus permission prompt from scrollback
- Esc cancels the running turn in non-vim and minimal modes
- List Ctrl+Z undo and redo in keyboard shortcuts
- Out-of-process macOS mic capture
- Show active auth mode on session-info
- Install the npm binary under $GROK_HOME
- Remove hover/click dead zones between dashboard items
- Route startup warnings to doctor
- Document [feedback.user] author identity config
- Extend bang command timeout
- Close combine-queued edit-hold race
- Integrate relocation recovery
- Expose privacy notice rollout flag
- Break harness discovery ref cycle so connections can idle-evict
- Shift/Alt+Enter inserts newline when editing a queued prompt
- Gate project Claude permissions on folder trust
- Echo response.create.event_id on response.created
- Toast when session creation fails from disk full
- Add shared test process lifecycle
- Enable dynamic workflows by default
- Add relocation transaction state machine
- Add shared test sandbox
- Surface auth failures on model-switch compact
- Persist durable scheduler expiry
- Confirm before removing extensions-modal items
- Re-run compact and prompt after login when compact hit expired auth
- Recap sends hosted tools under backend search
This commit is contained in:
grokkybara[bot] 2026-07-22 19:18:53 +01:00
commit a5727c5960
482 changed files with 37627 additions and 13402 deletions

View file

@ -1,17 +1,15 @@
#!/usr/bin/env node
// Thin trampoline: resolves the grok binary from the matching per-platform
// optional dependency package and execs it.
// Thin trampoline: resolves the grok binary and execs it.
//
// Falls back to bootstrapping the canonical ~/.grok/bin/grok-<version> symlink
// layout if postinstall hasn't run (e.g. npx, or postinstall failure).
// Resolution order:
// 1. $GROK_HOME/bin/grok — canonical versioned symlink (installed by postinstall.js)
// 2. bootstrap it from the per-platform @xai-official/grok-<platform> package,
// decompressing the brotli payload straight into $GROK_HOME/bin
// 3. last resort (no resolvable version or an unwritable home): decompress the
// payload in place under node_modules and exec that
//
// Binary location strategy (in priority order):
// 1. ~/.grok/bin/grok — canonical versioned symlink (postinstall.js)
// 2. @xai-official/grok-<platform>/bin/grok[.exe] — decompressed sibling
// 3. @xai-official/grok-<platform>/bin/grok[.exe].br — brotli-compressed
//
// Per-platform binaries are shipped brotli-compressed to stay well under
// npm's ~200 MB tarball ceiling. See sibling packages @xai-official/grok-*.
// Per-platform binaries ship brotli-compressed to stay under npm's ~200 MB
// tarball ceiling. See sibling packages @xai-official/grok-*.
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
@ -22,7 +20,14 @@ const pkgName = '@xai-official/grok';
const IS_WINDOWS = process.platform === 'win32';
const EXE = IS_WINDOWS ? '.exe' : '';
const BIN_NAME = `grok${EXE}`;
const CANONICAL_DIR = path.join(os.homedir(), '.grok', 'bin');
// $GROK_HOME/bin (else ~/.grok/bin), matching the Rust grok_home(), including
// its canonicalized-home default (so a symlinked $HOME resolves the same way).
function defaultGrokHome() {
const home = os.homedir();
try { return path.join(fs.realpathSync(home), '.grok'); } catch { return path.join(home, '.grok'); }
}
const GROK_HOME = process.env.GROK_HOME ?? defaultGrokHome();
const CANONICAL_DIR = path.join(GROK_HOME, 'bin');
const CANONICAL_PATH = path.join(CANONICAL_DIR, BIN_NAME);
function readLocalVersion() {
@ -41,53 +46,63 @@ function resolvePlatformPackageDir() {
}
}
// Decompress a brotli-compressed binary to a sibling path. Atomic via tmp+rename.
function decompressBrotli(brPath, outPath) {
const compressed = fs.readFileSync(brPath);
const decompressed = zlib.brotliDecompressSync(compressed);
const tmp = outPath + `.tmp.${process.pid}`;
fs.writeFileSync(tmp, decompressed);
if (!IS_WINDOWS) fs.chmodSync(tmp, 0o755);
try { fs.renameSync(tmp, outPath); } catch {}
function writeVendorBinary(brPath, rawPath, destPath) {
const tmp = destPath + `.tmp.${process.pid}`;
try {
if (fs.existsSync(brPath)) {
fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brPath)));
} else if (fs.existsSync(rawPath)) {
fs.copyFileSync(rawPath, tmp);
} else {
return false;
}
if (!IS_WINDOWS) fs.chmodSync(tmp, 0o755);
fs.renameSync(tmp, destPath);
return true;
} catch {
return false;
} finally {
try { fs.unlinkSync(tmp); } catch {}
}
}
// Bootstrap the canonical versioned-symlink layout from a source binary.
// Returns the canonical path on success, or the source path on failure.
function bootstrapCanonical(sourceBinPath, version) {
function swapCanonical(versionedName, versionedPath) {
if (!IS_WINDOWS) {
const tmpLink = CANONICAL_PATH + `.link.${process.pid}`;
try { fs.unlinkSync(tmpLink); } catch {}
fs.symlinkSync(versionedName, tmpLink);
fs.renameSync(tmpLink, CANONICAL_PATH);
return;
}
const oldPath = CANONICAL_PATH + '.old';
try { fs.unlinkSync(oldPath); } catch {}
try {
try { fs.unlinkSync(CANONICAL_PATH); } catch {}
fs.copyFileSync(versionedPath, CANONICAL_PATH);
} catch {
fs.renameSync(CANONICAL_PATH, oldPath);
try {
fs.copyFileSync(versionedPath, CANONICAL_PATH);
} catch {
try { fs.renameSync(oldPath, CANONICAL_PATH); } catch {}
throw new Error('locked');
}
}
}
function bootstrapCanonical(brPath, rawPath, version) {
try {
fs.mkdirSync(CANONICAL_DIR, { recursive: true });
const versionedName = `grok-${version}${EXE}`;
const versionedPath = path.join(CANONICAL_DIR, versionedName);
if (!fs.existsSync(versionedPath)) {
const tmpPath = versionedPath + `.tmp.${process.pid}`;
fs.copyFileSync(sourceBinPath, tmpPath);
if (!IS_WINDOWS) fs.chmodSync(tmpPath, 0o755);
fs.renameSync(tmpPath, versionedPath);
if (!fs.existsSync(versionedPath) && !writeVendorBinary(brPath, rawPath, versionedPath)) {
return null;
}
if (IS_WINDOWS) {
const oldPath = CANONICAL_PATH + '.old';
try { fs.unlinkSync(oldPath); } catch {}
try {
try { fs.unlinkSync(CANONICAL_PATH); } catch {}
fs.copyFileSync(versionedPath, CANONICAL_PATH);
} catch {
fs.renameSync(CANONICAL_PATH, oldPath);
try {
fs.copyFileSync(versionedPath, CANONICAL_PATH);
} catch {
try { fs.renameSync(oldPath, CANONICAL_PATH); } catch {}
throw new Error('locked');
}
}
} else {
const tmpLink = CANONICAL_PATH + `.link.${process.pid}`;
try { fs.unlinkSync(tmpLink); } catch {}
fs.symlinkSync(versionedName, tmpLink);
fs.renameSync(tmpLink, CANONICAL_PATH);
}
return CANONICAL_PATH;
swapCanonical(versionedName, versionedPath);
// null on a broken wire-up so the caller falls back to in-place launch.
return fs.existsSync(CANONICAL_PATH) ? CANONICAL_PATH : null;
} catch {
return sourceBinPath;
return null;
}
}
@ -105,22 +120,20 @@ function resolveBinary() {
const rawPath = path.join(platformDir, 'bin', BIN_NAME);
const brPath = rawPath + '.br';
const version = readLocalVersion();
// Decompress on first use if needed (atomic via tmp+rename).
if (!fs.existsSync(rawPath)) {
if (fs.existsSync(brPath)) {
decompressBrotli(brPath, rawPath);
}
// Prefer the canonical layout, decompressing straight into CANONICAL_DIR so
// no second uncompressed copy lands under node_modules.
if (version) {
const bootstrapped = bootstrapCanonical(brPath, rawPath, version);
if (bootstrapped) return bootstrapped;
}
if (!fs.existsSync(rawPath)) {
// Fallback (unresolved version or unwritable home): materialize in place.
if (!fs.existsSync(rawPath) && !writeVendorBinary(brPath, rawPath, rawPath)) {
console.error(`${pkgName}: missing binary at ${rawPath}`);
process.exit(1);
}
const version = readLocalVersion();
if (version) {
return bootstrapCanonical(rawPath, version);
}
return rawPath;
}

View file

@ -16,7 +16,15 @@ const zlib = require('zlib');
const { execSync } = require('child_process');
const TOML = require('@iarna/toml');
const CANONICAL_DIR = path.join(os.homedir(), '.grok', 'bin');
// $GROK_HOME (else ~/.grok), matching the Rust grok_home() including its
// canonicalized-home default. Lets fleets relocate the binary off a slow $HOME
// (NFS); old code hardcoded os.homedir().
function defaultGrokHome() {
const home = os.homedir();
try { return path.join(fs.realpathSync(home), '.grok'); } catch { return path.join(home, '.grok'); }
}
const GROK_HOME = process.env.GROK_HOME ?? defaultGrokHome();
const CANONICAL_DIR = path.join(GROK_HOME, 'bin');
const key = `${process.platform}-${process.arch}`;
const SUPPORTED = new Set([
@ -57,43 +65,39 @@ const EXE = IS_WINDOWS ? '.exe' : '';
fs.mkdirSync(CANONICAL_DIR, { recursive: true });
// Install a vendored binary: versioned filename + symlink (Unix) or copy (Windows).
// Binaries are shipped brotli-compressed in the per-platform npm tarball to keep
// each sub-package well under npm's ~200 MB tarball limit. This function
// decompresses them before installing into the canonical layout.
function writeVendorBinary(brPath, rawPath, destPath) {
const tmp = destPath + `.tmp.${process.pid}`;
try {
if (fs.existsSync(brPath)) {
fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brPath)));
} else if (fs.existsSync(rawPath)) {
fs.copyFileSync(rawPath, tmp);
} else {
return false;
}
if (!IS_WINDOWS) fs.chmodSync(tmp, 0o755);
fs.renameSync(tmp, destPath);
return true;
} catch {
return false;
} finally {
try { fs.unlinkSync(tmp); } catch {}
}
}
function installBinary(binName, sourceDir, vendorSubpath) {
const brPath = path.join(sourceDir, 'bin', vendorSubpath + '.br');
const rawPath = path.join(sourceDir, 'bin', vendorSubpath);
let vendoredBinPath;
if (fs.existsSync(brPath)) {
const compressed = fs.readFileSync(brPath);
const decompressed = zlib.brotliDecompressSync(compressed);
vendoredBinPath = rawPath;
fs.writeFileSync(vendoredBinPath, decompressed);
if (!IS_WINDOWS) fs.chmodSync(vendoredBinPath, 0o755);
try { fs.unlinkSync(brPath); } catch {}
} else if (fs.existsSync(rawPath)) {
vendoredBinPath = rawPath;
} else {
console.error(`@xai-official/grok: missing binary at ${brPath}`);
return false;
}
const versionedName = `${binName}-${version}${EXE}`;
const versionedPath = path.join(CANONICAL_DIR, versionedName);
const canonicalName = `${binName}${EXE}`;
const canonicalPath = path.join(CANONICAL_DIR, canonicalName);
// Only copy if this exact version isn't already installed.
if (!fs.existsSync(versionedPath)) {
const tmpPath = versionedPath + `.tmp.${process.pid}`;
try {
fs.copyFileSync(vendoredBinPath, tmpPath);
if (!IS_WINDOWS) fs.chmodSync(tmpPath, 0o755);
fs.renameSync(tmpPath, versionedPath);
} finally {
try { fs.unlinkSync(tmpPath); } catch {}
}
// Skip if this exact version is already installed.
if (!fs.existsSync(versionedPath) && !writeVendorBinary(brPath, rawPath, versionedPath)) {
console.error(`@xai-official/grok: missing binary at ${brPath}`);
return false;
}
if (IS_WINDOWS) {
@ -128,10 +132,28 @@ function installBinary(binName, sourceDir, vendorSubpath) {
fs.renameSync(tmpLink, canonicalPath);
}
// Don't report a broken wire-up as success.
if (!fs.existsSync(canonicalPath)) {
console.error(`@xai-official/grok: ${canonicalName} did not resolve after install`);
return false;
}
console.log(`${binName} ${version} installed to ${canonicalPath} -> ${versionedName}`);
return true;
}
// Comparator: sort "<prefix>X.Y.Z" filenames by version, newest first.
function byVersionDescending(prefix) {
return (a, b) => {
const pa = a.slice(prefix.length).split('.').map(Number);
const pb = b.slice(prefix.length).split('.').map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] || 0) !== (pb[i] || 0)) return (pb[i] || 0) - (pa[i] || 0);
}
return 0;
};
}
// Best-effort cleanup of old versioned binaries for a given binary name.
// Keeps the current version and the previous one (in case a process is still
// running the old binary and hasn't fully loaded all pages yet).
@ -149,14 +171,7 @@ function cleanupOldVersions(binName) {
const suffix = e.slice(prefix.length);
return /^\d/.test(suffix);
})
.sort((a, b) => {
const pa = a.slice(prefix.length).split('.').map(Number);
const pb = b.slice(prefix.length).split('.').map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] || 0) !== (pb[i] || 0)) return (pb[i] || 0) - (pa[i] || 0);
}
return 0;
});
.sort(byVersionDescending(prefix));
for (const old of versionedBinaries.slice(1)) {
try { fs.unlinkSync(path.join(CANONICAL_DIR, old)); } catch {}
}
@ -176,7 +191,7 @@ cleanupOldVersions('grok');
cleanupOldVersions('grok-pager');
// Write installer config
const configDir = path.join(os.homedir(), '.grok');
const configDir = GROK_HOME;
const configPath = path.join(configDir, 'config.toml');
let obj = {};
try { obj = TOML.parse(fs.readFileSync(configPath, 'utf8')); } catch { }
@ -208,7 +223,7 @@ const GROK_PATH = path.join(CANONICAL_DIR, `grok${EXE}`);
if (process.env.GROK_INSTALL_COMPLETIONS === '1' && !IS_WINDOWS) {
try {
const { spawnSync } = require('child_process');
const completionsDir = path.join(os.homedir(), '.grok', 'completions');
const completionsDir = path.join(GROK_HOME, 'completions');
const bashPath = path.join(completionsDir, 'bash', 'grok.bash');
const zshPath = path.join(completionsDir, 'zsh', '_grok');
fs.mkdirSync(path.dirname(bashPath), { recursive: true });

View file

@ -9,6 +9,7 @@
const fs = require('fs');
const path = require('path');
const os = require('os');
const zlib = require('zlib');
const assert = require('assert');
let passed = 0;
@ -36,14 +37,16 @@ function cleanup(dir) {
// ─── Extracted logic (mirrors postinstall.js and bin/grok exactly) ─────
/** Semver-aware descending sort for "grok-X.Y.Z" filenames. */
function semverSortDescending(a, b) {
const pa = a.slice(5).split('.').map(Number);
const pb = b.slice(5).split('.').map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] || 0) !== (pb[i] || 0)) return (pb[i] || 0) - (pa[i] || 0);
}
return 0;
/** Comparator: sort "<prefix>X.Y.Z" filenames by version, newest first. */
function byVersionDescending(prefix) {
return (a, b) => {
const pa = a.slice(prefix.length).split('.').map(Number);
const pb = b.slice(prefix.length).split('.').map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] || 0) !== (pb[i] || 0)) return (pb[i] || 0) - (pa[i] || 0);
}
return 0;
};
}
/** Install a versioned binary + atomic symlink (same as postinstall.js). */
@ -78,7 +81,7 @@ function cleanupOldVersions(canonicalDir, currentVersionedName) {
const entries = fs.readdirSync(canonicalDir);
const versionedBinaries = entries
.filter(e => e.startsWith('grok-') && !e.includes('.tmp.') && !e.includes('.link.') && e !== currentVersionedName)
.sort(semverSortDescending);
.sort(byVersionDescending('grok-'));
// Keep the most recent old version, remove anything older.
for (const old of versionedBinaries.slice(1)) {
try { fs.unlinkSync(path.join(canonicalDir, old)); } catch {}
@ -86,6 +89,60 @@ function cleanupOldVersions(canonicalDir, currentVersionedName) {
return versionedBinaries;
}
/** Grok bin dir resolution (mirrors postinstall.js and bin/grok). */
function resolveGrokBinDir(env, homedir) {
const grokHome = env.GROK_HOME ?? path.join(homedir, '.grok');
return path.join(grokHome, 'bin');
}
/** Materialize the vendored binary at destPath (mirrors writeVendorBinary). */
function writeVendorBinary(brPath, rawPath, destPath) {
const tmp = destPath + `.tmp.${process.pid}`;
try {
if (fs.existsSync(brPath)) {
fs.writeFileSync(tmp, zlib.brotliDecompressSync(fs.readFileSync(brPath)));
} else if (fs.existsSync(rawPath)) {
fs.copyFileSync(rawPath, tmp);
} else {
return false;
}
fs.chmodSync(tmp, 0o755);
fs.renameSync(tmp, destPath);
return true;
} catch {
return false;
} finally {
try { fs.unlinkSync(tmp); } catch {}
}
}
/** Decompress a brotli payload into the canonical dir (mirrors installBinary). */
function installBinaryFromBrotli(brPath, version, canonicalDir) {
fs.mkdirSync(canonicalDir, { recursive: true });
const versionedName = `grok-${version}`;
const versionedPath = path.join(canonicalDir, versionedName);
const canonicalPath = path.join(canonicalDir, 'grok');
if (!fs.existsSync(versionedPath)) {
const tmpPath = versionedPath + `.tmp.${process.pid}`;
try {
const decompressed = zlib.brotliDecompressSync(fs.readFileSync(brPath));
fs.writeFileSync(tmpPath, decompressed);
fs.chmodSync(tmpPath, 0o755);
fs.renameSync(tmpPath, versionedPath);
} finally {
try { fs.unlinkSync(tmpPath); } catch {}
}
}
const tmpLink = canonicalPath + `.link.${process.pid}`;
try { fs.unlinkSync(tmpLink); } catch {}
fs.symlinkSync(versionedName, tmpLink);
fs.renameSync(tmpLink, canonicalPath);
return { canonicalPath, versionedPath, versionedName };
}
/** Bootstrap canonical from vendored (same as bin/grok trampoline). */
function bootstrapCanonical(vendoredBinPath, version, canonicalDir) {
const canonicalPath = path.join(canonicalDir, 'grok');
@ -458,9 +515,9 @@ test('semver sort: minor version boundary (0.1.x vs 0.2.x)', () => {
}
});
test('semverSortDescending: unit test comparator directly', () => {
test('byVersionDescending: unit test comparator directly', () => {
const input = ['grok-0.1.9', 'grok-0.1.10', 'grok-0.1.2', 'grok-1.0.0', 'grok-0.2.0'];
const sorted = [...input].sort(semverSortDescending);
const sorted = [...input].sort(byVersionDescending('grok-'));
assert.deepStrictEqual(sorted, [
'grok-1.0.0',
'grok-0.2.0',
@ -674,14 +731,7 @@ function cleanupOldVersionsNamed(canonicalDir, binName, version) {
const suffix = e.slice(prefix.length);
return /^\d/.test(suffix);
})
.sort((a, b) => {
const pa = a.slice(prefix.length).split('.').map(Number);
const pb = b.slice(prefix.length).split('.').map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] || 0) !== (pb[i] || 0)) return (pb[i] || 0) - (pa[i] || 0);
}
return 0;
});
.sort(byVersionDescending(prefix));
for (const old of versionedBinaries.slice(1)) {
try { fs.unlinkSync(path.join(canonicalDir, old)); } catch {}
}
@ -977,6 +1027,58 @@ test('canonical pager from non-npm install is preserved on Linux', () => {
}
});
console.log('\ngrok home + brotli install tests\n');
test('resolveGrokBinDir honors $GROK_HOME, else falls back to <home>/.grok/bin', () => {
assert.strictEqual(
resolveGrokBinDir({ GROK_HOME: '/fast/local/.grok' }, '/home/alice'),
path.join('/fast/local/.grok', 'bin'),
);
assert.strictEqual(
resolveGrokBinDir({}, '/home/alice'),
path.join('/home/alice', '.grok', 'bin'),
);
assert.strictEqual(resolveGrokBinDir({ GROK_HOME: '' }, '/home/alice'), path.join('', 'bin'));
});
test('writeVendorBinary returns false (not true) when the destination cannot be written', () => {
const dir = makeTmpDir();
try {
const brPath = path.join(dir, 'grok.br');
fs.writeFileSync(brPath, zlib.brotliCompressSync(Buffer.from('binary')));
// A non-empty directory at destPath makes the final rename fail.
const dest = path.join(dir, 'dest');
fs.mkdirSync(dest);
fs.writeFileSync(path.join(dest, 'child'), 'x');
assert.strictEqual(writeVendorBinary(brPath, path.join(dir, 'raw'), dest), false);
assert.ok(!fs.existsSync(`${dest}.tmp.${process.pid}`), 'temp file is cleaned up on failure');
} finally {
cleanup(dir);
}
});
test('decompresses brotli into the canonical dir without duplicating into node_modules', () => {
const dir = makeTmpDir();
try {
const vendorBin = path.join(dir, 'node_modules', 'bin');
fs.mkdirSync(vendorBin, { recursive: true });
const brPath = path.join(vendorBin, 'grok.br');
fs.writeFileSync(brPath, zlib.brotliCompressSync(Buffer.from('native-binary-bytes')));
const binDir = path.join(dir, '.grok', 'bin');
const result = installBinaryFromBrotli(brPath, '0.1.220', binDir);
assert.ok(fs.lstatSync(result.canonicalPath).isSymbolicLink());
assert.strictEqual(fs.readFileSync(result.canonicalPath, 'utf8'), 'native-binary-bytes');
assert.ok(!fs.existsSync(path.join(vendorBin, 'grok')), 'no uncompressed binary in node_modules');
assert.ok(fs.existsSync(brPath), 'compressed .br payload is preserved');
} finally {
cleanup(dir);
}
});
// ─── Summary ───────────────────────────────────────────────────────────
console.log(`\n${passed} passed, ${failed} failed`);