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

@ -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 });