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

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