mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-19 02:42:29 +08:00
feat(browser-skills): \$B skill list/show/run/test/rm subcommands
handleSkillCommand dispatches to per-subcommand handlers; spawnSkill is
the load-bearing function that:
1. Mints a per-spawn scoped token (read+write only) bound to the
skill name + spawn-id.
2. Builds the spawn env:
- trusted: passes process.env minus GSTACK_TOKEN (defense in depth).
- untrusted: minimal allowlist (LANG, LC_ALL, TERM, TZ) + locked
PATH; explicitly drops anything matching TOKEN/KEY/SECRET/etc.
Also drops AWS_/AZURE_/GCP_/GOOGLE_APPLICATION_/ANTHROPIC_/OPENAI_/
GITHUB_/GH_/SSH_/GPG_/NPM_TOKEN/PYPI_ patterns.
3. Always injects GSTACK_PORT + GSTACK_SKILL_TOKEN last (cannot be
overridden by parent env).
4. Spawns bun run script.ts -- <args> with cwd=skillDir, captures
stdout (1MB cap), stderr, and timeout-kills past the deadline.
5. Revokes the token in finally{}, always.
list output prints the resolved tier inline so "why did it run that
one?" never becomes a debugging mystery (Codex finding #4 mitigation).
server.ts threads the listen port to meta-commands via MetaCommandOpts.daemonPort.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,6 +71,14 @@ const AUTH_TOKEN = crypto.randomUUID();
|
||||
initRegistry(AUTH_TOKEN);
|
||||
const BROWSE_PORT = parseInt(process.env.BROWSE_PORT || '0', 10);
|
||||
const IDLE_TIMEOUT_MS = parseInt(process.env.BROWSE_IDLE_TIMEOUT || '1800000', 10); // 30 min
|
||||
|
||||
/**
|
||||
* Port the local listener bound to. Set once the daemon picks a port.
|
||||
* Used by `$B skill run` to point spawned skill scripts at the daemon over
|
||||
* loopback. Module-level so handleCommandInternal can read it without threading
|
||||
* the port through every dispatch.
|
||||
*/
|
||||
let LOCAL_LISTEN_PORT: number = 0;
|
||||
// Sidebar chat is always enabled in headed mode (ungated in v0.12.0)
|
||||
|
||||
// ─── Tunnel State ───────────────────────────────────────────────
|
||||
@@ -1286,6 +1294,7 @@ async function handleCommandInternal(
|
||||
const chainDepth = (opts?.chainDepth ?? 0);
|
||||
result = await handleMetaCommand(command, args, browserManager, shutdown, tokenInfo, {
|
||||
chainDepth,
|
||||
daemonPort: LOCAL_LISTEN_PORT,
|
||||
executeCommand: (body, ti) => handleCommandInternal(body, ti, {
|
||||
skipRateCheck: true, // chain counts as 1 request
|
||||
skipActivity: true, // chain emits 1 event for all subcommands
|
||||
@@ -1571,6 +1580,7 @@ async function start() {
|
||||
safeUnlink(DIALOG_LOG_PATH);
|
||||
|
||||
const port = await findPort();
|
||||
LOCAL_LISTEN_PORT = port;
|
||||
|
||||
// Launch browser (headless or headed with extension)
|
||||
// BROWSE_HEADLESS_SKIP=1 skips browser launch entirely (for HTTP-only testing)
|
||||
|
||||
Reference in New Issue
Block a user