feat: add Pi provider managed resources
This commit is contained in:
@@ -18,6 +18,27 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 10_000;
|
||||
const DEFAULT_SHUTDOWN_GRACE_MS = 3_000;
|
||||
const DEFAULT_DIAGNOSTIC_BYTES = 16_000;
|
||||
const ANSI_COLOR_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g');
|
||||
const PI_INHERITED_ENV_KEYS = [
|
||||
'APPDATA',
|
||||
'COMSPEC',
|
||||
'HOME',
|
||||
'LANG',
|
||||
'LC_ALL',
|
||||
'LD_LIBRARY_PATH',
|
||||
'LOCALAPPDATA',
|
||||
'NODE_EXTRA_CA_CERTS',
|
||||
'PATH',
|
||||
'PATHEXT',
|
||||
'SSL_CERT_DIR',
|
||||
'SSL_CERT_FILE',
|
||||
'SYSTEMROOT',
|
||||
'TEMP',
|
||||
'TMP',
|
||||
'TMPDIR',
|
||||
'TZ',
|
||||
'USERPROFILE',
|
||||
'WINDIR',
|
||||
] as const;
|
||||
|
||||
export type PiWorkerStopResult = {
|
||||
mode: 'not-started' | 'stdin-close' | 'forced-tree-kill';
|
||||
@@ -67,13 +88,50 @@ export function sanitizePiDiagnostic(
|
||||
.replace(ANSI_COLOR_PATTERN, '')
|
||||
.replace(/(authorization\s*[:=]\s*(?:bearer\s+)?)[^\s,;]+/gi, '$1[REDACTED]')
|
||||
.replace(/((?:x-api-key|api[_-]?key|token|secret)\s*[:=]\s*)[^\s,;]+/gi, '$1[REDACTED]');
|
||||
for (const value of sensitiveValues) {
|
||||
if (value.length < 4) continue;
|
||||
const uniqueSensitiveValues = [...new Set(sensitiveValues.filter(Boolean))]
|
||||
.sort((left, right) => right.length - left.length);
|
||||
for (const value of uniqueSensitiveValues) {
|
||||
sanitized = sanitized.split(value).join('[REDACTED]');
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
export function buildPiWorkerEnvironment(
|
||||
configDir: string,
|
||||
overlay: NodeJS.ProcessEnv = {},
|
||||
inherited: NodeJS.ProcessEnv = process.env,
|
||||
): NodeJS.ProcessEnv {
|
||||
const env: NodeJS.ProcessEnv = {};
|
||||
for (const key of PI_INHERITED_ENV_KEYS) {
|
||||
const exact = inherited[key];
|
||||
if (exact !== undefined) {
|
||||
env[key] = exact;
|
||||
continue;
|
||||
}
|
||||
const matchingKey = Object.keys(inherited).find((candidate) => candidate.toUpperCase() === key);
|
||||
if (matchingKey && inherited[matchingKey] !== undefined) env[matchingKey] = inherited[matchingKey];
|
||||
}
|
||||
return {
|
||||
...env,
|
||||
...overlay,
|
||||
ELECTRON_RUN_AS_NODE: '1',
|
||||
PI_CODING_AGENT_DIR: configDir,
|
||||
PI_OFFLINE: '1',
|
||||
PI_TELEMETRY: '0',
|
||||
};
|
||||
}
|
||||
|
||||
function assertSensitiveValuesAbsentFromArgs(
|
||||
args: readonly string[],
|
||||
sensitiveValues: readonly string[] = [],
|
||||
): void {
|
||||
for (const value of sensitiveValues) {
|
||||
if (value && args.some((argument) => argument.includes(value))) {
|
||||
throw new Error('Pi worker arguments contain a sensitive value');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function boundedUtf8Tail(source: string, maxBytes: number): string {
|
||||
const bytes = Buffer.from(source, 'utf8');
|
||||
if (bytes.length <= maxBytes) return source;
|
||||
@@ -158,19 +216,14 @@ export class PiWorkerProcess {
|
||||
async start(): Promise<this> {
|
||||
if (this.child) throw new Error('Pi worker process already started');
|
||||
const generation = this.generationValue;
|
||||
const args = buildPiRpcArgs(this.options.sessionDir, this.options.additionalArgs);
|
||||
assertSensitiveValuesAbsentFromArgs(args, this.options.sensitiveValues);
|
||||
const child = spawn(
|
||||
this.options.executablePath,
|
||||
[this.options.cliPath, ...buildPiRpcArgs(this.options.sessionDir, this.options.additionalArgs)],
|
||||
[this.options.cliPath, ...args],
|
||||
{
|
||||
cwd: this.options.cwd,
|
||||
env: {
|
||||
...process.env,
|
||||
...this.options.env,
|
||||
ELECTRON_RUN_AS_NODE: '1',
|
||||
PI_CODING_AGENT_DIR: this.options.configDir,
|
||||
PI_OFFLINE: '1',
|
||||
PI_TELEMETRY: '0',
|
||||
},
|
||||
env: buildPiWorkerEnvironment(this.options.configDir, this.options.env),
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
windowsHide: true,
|
||||
detached: platform() !== 'win32',
|
||||
|
||||
Reference in New Issue
Block a user