/** Standalone source is staged unchanged on local, SSH, and sandbox runtimes. No secrets in files. */ export function githubLauncherSource(): string { return String.raw`#!/usr/bin/env node const fs = require('node:fs'); const path = require('node:path'); const os = require('node:os'); const { spawn } = require('node:child_process'); const directory = path.dirname(fs.realpathSync(process.argv[1])); const program = path.basename(process.argv[1]); const originalPath = (process.env.PATH || '').split(path.delimiter).filter(p => { try { return fs.realpathSync(p) !== directory; } catch { return true; } }); const executable = originalPath.map(p => path.join(p, program)).find(p => { try { fs.accessSync(p, fs.constants.X_OK); return fs.statSync(p).isFile(); } catch { return false; } }); if (!['git', 'gh'].includes(program) || !executable) { process.stderr.write('Paperclip: requested GitHub command is not installed.\n'); process.exit(127); } async function main() { let env = { ...process.env }; const diagnostic = (code) => process.stderr.write('Paperclip: GitHub ' + code + '; continuing without managed credentials.\n'); const configRoot = env.GH_CONFIG_DIR || os.tmpdir(); // A missing/unwritable scratch directory must not break local Git. The // fallback deliberately cannot load the host's gh authentication files. let configDirectory = path.join(directory, 'unavailable-gh-config'); let configReady = false; try { fs.mkdirSync(configRoot, { recursive: true, mode: 0o700 }); configDirectory = fs.mkdtempSync(path.join(configRoot, 'paperclip-github-operation-')); fs.chmodSync(configDirectory, 0o700); configReady = true; process.once('exit', () => { try { fs.rmSync(configDirectory, { recursive: true, force: true }); } catch {} }); } catch { diagnostic('configuration_directory_unavailable'); } { for (const key of Object.keys(env)) { if (/^(GH_TOKEN|GITHUB_TOKEN|GH_ENTERPRISE_TOKEN|GITHUB_ENTERPRISE_TOKEN|PAPERCLIP_GIT_TOKEN|GIT_AUTHOR_.*|GIT_COMMITTER_.*|GIT_CONFIG_.*|GIT_ASKPASS|SSH_ASKPASS|SSH_AUTH_SOCK|GIT_SSH.*)$/.test(key)) delete env[key]; } Object.assign(env, { GH_CONFIG_DIR: configDirectory, SSH_AUTH_SOCK: '', GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null', GIT_TERMINAL_PROMPT: '0', GIT_AUTHOR_NAME: '', GIT_AUTHOR_EMAIL: '', GIT_COMMITTER_NAME: '', GIT_COMMITTER_EMAIL: '', GIT_CONFIG_COUNT: '4', GIT_CONFIG_KEY_0: 'credential.helper', GIT_CONFIG_VALUE_0: '', GIT_CONFIG_KEY_1: 'url.https://github.com/.insteadOf', GIT_CONFIG_VALUE_1: 'git@github.com:', GIT_CONFIG_KEY_2: 'url.https://github.com/.insteadOf', GIT_CONFIG_VALUE_2: 'ssh://git@github.com/', GIT_CONFIG_KEY_3: 'core.askPass', GIT_CONFIG_VALUE_3: '', }); const base = env.PAPERCLIP_GITHUB_BROKER_URL || env.PAPERCLIP_API_URL; try { let response; if (base && env.PAPERCLIP_GITHUB_BROKER_TOKEN) { const url = base.replace(/\/+$/, '').replace(/\/api$/, '') + '/runtime-tools/github/credentials'; for (let attempt = 0; attempt < 30; attempt++) { response = await fetch(url, { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(10000), headers: { authorization: 'Bearer ' + (env.PAPERCLIP_GITHUB_BRIDGE_TOKEN || env.PAPERCLIP_API_KEY || env.PAPERCLIP_GITHUB_BROKER_TOKEN), 'x-paperclip-github-capability': env.PAPERCLIP_GITHUB_BROKER_TOKEN, 'content-type': 'application/json' }, body: '{}', }); if (response.status !== 409) break; await response.arrayBuffer(); await new Promise(resolve => setTimeout(resolve, 1000)); } if (!response.ok) { diagnostic(response.status === 401 || response.status === 403 ? 'capability_rejected' : 'broker_response_unavailable'); } else { const result = await response.json(); if (result.status === 'unavailable') { const reason = typeof result.reason === 'string' ? result.reason.replace(/[\x00-\x1f\x7f]/g, ' ').slice(0, 500) : 'Check the GitHub connection in Paperclip'; process.stderr.write('Paperclip: GitHub access unavailable: ' + reason + '. Continuing without GitHub credentials.\n'); } if (result.status === 'available' && configReady) { for (const [key, value] of Object.entries(result.env || {})) { if (/^(GH_TOKEN|GITHUB_TOKEN|PAPERCLIP_GIT_TOKEN|GIT_TERMINAL_PROMPT|GIT_AUTHOR_(NAME|EMAIL)|GIT_COMMITTER_(NAME|EMAIL)|GIT_CONFIG_COUNT|GIT_CONFIG_(KEY|VALUE)_\d+)$/.test(key) && typeof value === 'string') env[key] = value; } } } } else { diagnostic('capability_missing'); } } catch { diagnostic('broker_transport_unavailable'); } } // Only this invocation and its children inherit the captured credential. // Its Git children use the real binary, so steering cannot split a gh operation. env.PATH = originalPath.join(path.delimiter); // Nested shell aliases must not reload the parent launcher profile and // recapture a newer identity. All ordinary descendants stay in this operation. env.ZDOTDIR = configDirectory; env.BASH_ENV = '/dev/null'; env.GIT_SSH_COMMAND = 'ssh -F /dev/null -o IdentityAgent=none -o IdentitiesOnly=yes -o IdentityFile=none -o BatchMode=yes'; const child = spawn(executable, process.argv.slice(2), { env, stdio: 'inherit' }); for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) process.on(signal, () => child.kill(signal)); child.once('error', () => { process.stderr.write('Paperclip: GitHub command could not start.\n'); process.exitCode = 1; }); child.once('exit', (code, signal) => { process.exitCode = code === null ? 128 : code; }); } main().catch(() => { process.stderr.write('Paperclip: GitHub launcher_setup_failed.\n'); process.exitCode = 1; }); `; } /** Override inherited credentials even when adapters merge the host environment later. */ export function githubBrokerEnvironment(input: Record, broker: { url: string; token: string }): Record { const env: Record = {}; for (const [key, value] of Object.entries(input)) if (typeof value === "string") env[key] = value; for (const key of ["GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "PAPERCLIP_GIT_TOKEN", "GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL", "GIT_CONFIG_COUNT", "PAPERCLIP_GITHUB_OPERATION_ACTIVE"]) env[key] = ""; for (const key of Object.keys(env)) { if (/^GIT_CONFIG_(KEY|VALUE)_\d+$/.test(key)) env[key] = ""; } env.GIT_CONFIG_GLOBAL = "/dev/null"; env.GIT_CONFIG_SYSTEM = "/dev/null"; env.GIT_CONFIG_NOSYSTEM = "1"; env.GIT_TERMINAL_PROMPT = "0"; env.GIT_ASKPASS = ""; env.SSH_ASKPASS = ""; env.GIT_SSH_COMMAND = "ssh -F /dev/null -o IdentityAgent=none -o IdentitiesOnly=yes -o IdentityFile=none -o BatchMode=yes"; env.SSH_AUTH_SOCK = ""; env.PAPERCLIP_GITHUB_BROKER_URL = broker.url; env.PAPERCLIP_GITHUB_BROKER_TOKEN = broker.token; return env; }