@code-yeongyu/senpi 2026.10.24-2 → 2026.10.24-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/bundle/chunks/{args-6T5EQVY3.js → args-JXULI4EH.js} +1 -1
- package/dist/bundle/chunks/{chunk-VUKNGMDT.js → chunk-3LNSLJ7N.js} +1 -1
- package/dist/bundle/chunks/chunk-3VOG6WFU.js +27 -0
- package/dist/bundle/chunks/{chunk-HHYSXDYP.js → chunk-42OY5SZZ.js} +2 -2
- package/dist/bundle/chunks/chunk-7IRLFYNM.js +23 -0
- package/dist/bundle/chunks/{chunk-FIBIVBQK.js → chunk-7UUOGZKR.js} +1 -1
- package/dist/bundle/chunks/chunk-BMYNMUVZ.js +7 -0
- package/dist/bundle/chunks/{chunk-JAXZOEJE.js → chunk-BWA4FVFW.js} +1 -1
- package/dist/bundle/chunks/{chunk-AGQSRWNN.js → chunk-EHQXLHRB.js} +1 -1
- package/dist/bundle/chunks/chunk-EWOEVVQT.js +4 -0
- package/dist/bundle/chunks/{chunk-YJA2B7FQ.js → chunk-FDL4CF6F.js} +1 -1
- package/dist/bundle/chunks/{chunk-RQHK4QZ5.js → chunk-FWXMMMCV.js} +1 -1
- package/dist/bundle/chunks/{chunk-OA5INWHI.js → chunk-GVJ76VQI.js} +102 -42
- package/dist/bundle/chunks/{chunk-HCF5M6NA.js → chunk-GXY5JEI5.js} +1 -1
- package/dist/bundle/chunks/{chunk-VZAYN5SB.js → chunk-HKUXKTFW.js} +1 -1
- package/dist/bundle/chunks/chunk-II22E3CV.js +3 -0
- package/dist/bundle/chunks/{chunk-ONLNXGO3.js → chunk-IUHU5PEC.js} +1 -1
- package/dist/bundle/chunks/{chunk-LR6Z6V54.js → chunk-NQC7MI2G.js} +1 -1
- package/dist/bundle/chunks/{chunk-IELM5U6I.js → chunk-PXKF2RHD.js} +2 -2
- package/dist/bundle/chunks/{chunk-BRXN5R2F.js → chunk-QH2HSCMX.js} +1 -1
- package/dist/bundle/chunks/{chunk-QQSCUEYJ.js → chunk-RHHBXIRN.js} +2 -5
- package/dist/bundle/chunks/chunk-RVYSJBZ7.js +7 -0
- package/dist/bundle/chunks/chunk-SGDOZT6B.js +8 -0
- package/dist/bundle/chunks/{chunk-BZ75DRZB.js → chunk-U67ML6CQ.js} +4 -2
- package/dist/bundle/chunks/{chunk-3N2PQEQV.js → chunk-UT6AJHPK.js} +1 -1
- package/dist/bundle/chunks/{chunk-RAIZG6HA.js → chunk-V3CK2AVC.js} +1 -1
- package/dist/bundle/chunks/{chunk-CZR7C6CY.js → chunk-VOBD6FQG.js} +1 -1
- package/dist/bundle/chunks/{chunk-HRPMLI7O.js → chunk-VRDU425E.js} +2 -2
- package/dist/bundle/chunks/{chunk-OUPD5LWY.js → chunk-XUFUKDKJ.js} +3 -3
- package/dist/bundle/chunks/{chunk-SGATKHO4.js → chunk-Y5OTHMXO.js} +1 -1
- package/dist/bundle/chunks/{chunk-QEQG42HL.js → chunk-ZGAB7WQA.js} +1 -1
- package/dist/bundle/chunks/cli-main-UH3MAREK.js +3 -0
- package/dist/bundle/chunks/{dispatch-KQEGIXQW.js → dispatch-DEP4PEDZ.js} +3 -2
- package/dist/bundle/chunks/{help-fast-path-6PVN7WI5.js → help-fast-path-ZHZGXAUG.js} +1 -1
- package/dist/bundle/chunks/host-command-OQR4RDTM.js +2 -0
- package/dist/bundle/chunks/host-lifecycle-U36U7JTE.js +3 -0
- package/dist/bundle/chunks/host-lifecycle.js +26 -21
- package/dist/bundle/chunks/host-session-command-IGB27ZPJ.js +20 -0
- package/dist/bundle/chunks/{host-socket-request-KKGH3JHV.js → host-socket-request-ZDJGSSZO.js} +1 -1
- package/dist/bundle/chunks/interactive-mode-R5RCSJ2F.js +2 -0
- package/dist/bundle/chunks/{list-tips-BO5RP34W.js → list-tips-CREX4IXZ.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-O5GZQSRL.js → multi-session-host-YZEMYUFY.js} +12 -12
- package/dist/bundle/chunks/{package-manager-cli-LC5GAGTW.js → package-manager-cli-5FOXDKUA.js} +1 -1
- package/dist/bundle/chunks/{role-4G43UDOC.js → role-M2MTVACJ.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-RXMCJJUL.js → rotation-stream-U7YQMC7H.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-6K62CZBW.js +2 -0
- package/dist/bundle/chunks/rpc-monitor-source-OL5KU6NO.js +3 -0
- package/dist/bundle/chunks/{schedule-command-QNCVE4HM.js → schedule-command-XBW4EOG5.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-54TMCS7Q.js +3 -0
- package/dist/bundle/chunks/{session-picker-HIC5ZTE6.js → session-picker-P2VUU2MQ.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +437 -368
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +26 -21
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +27 -0
- package/dist/cli/host-command-args.d.ts +33 -0
- package/dist/cli/host-command-args.js +145 -0
- package/dist/cli/host-command.d.ts +1 -30
- package/dist/cli/host-command.js +5 -137
- package/dist/cli/host-session-command.d.ts +1 -0
- package/dist/cli/host-session-command.js +5 -3
- package/dist/core/agent-session.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/index.js +4 -2
- package/dist/core/extensions/builtin/anthropic-subscription/options.js +2 -1
- package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.js +9 -2
- package/dist/core/extensions/builtin/anthropic-subscription/session-cwd.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-cwd.js +9 -0
- package/dist/core/extensions/builtin/anthropic-subscription/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/settings.js +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +4 -1
- package/dist/core/extensions/builtin/btw/index.js +4 -1
- package/dist/core/extensions/builtin/compaction/lane-policy.js +3 -1
- package/dist/core/extensions/builtin/look-at/runner.js +1 -0
- package/dist/core/extensions/builtin/mcp/connection-types.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/connection.js +3 -0
- package/dist/core/extensions/builtin/mcp/elicitation.d.ts +5 -2
- package/dist/core/extensions/builtin/mcp/elicitation.js +57 -11
- package/dist/core/extensions/builtin/mcp/index.js +1 -1
- package/dist/core/extensions/builtin/mcp/modern/dispatch.js +6 -9
- package/dist/core/extensions/builtin/mcp/service-connection.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/service-connection.js +1 -0
- package/dist/core/extensions/builtin/mcp/service.js +1 -0
- package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/transport-sdk.js +5 -5
- package/dist/core/extensions/builtin/mcp/transport.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/transport.js +1 -0
- package/dist/core/extensions/builtin/prompt-preset/contract.js +6 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.js +93 -0
- package/dist/core/extensions/builtin/prompt-preset/matchers.d.ts +2 -0
- package/dist/core/extensions/builtin/prompt-preset/matchers.js +9 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.js +3 -0
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
- package/dist/core/extensions/builtin/terminal/rpc-monitor-observer.d.ts +2 -1
- package/dist/core/extensions/builtin/terminal/rpc-monitor-observer.js +12 -4
- package/dist/core/external-admission.d.ts +4 -1
- package/dist/core/external-admission.js +18 -2
- package/dist/core/lifecycle-session-binding.js +20 -0
- package/dist/core/sdk.js +1 -0
- package/dist/core/session-spawned-by.d.ts +4 -0
- package/dist/core/session-spawned-by.js +16 -0
- package/dist/main.js +7 -2
- package/dist/modes/rpc/host-admission-proxy.d.ts +2 -1
- package/dist/modes/rpc/host-admission-proxy.js +10 -9
- package/dist/modes/rpc/host-cli-entry.d.ts +2 -0
- package/dist/modes/rpc/host-cli-entry.js +13 -1
- package/dist/modes/rpc/host-ensure-client.d.ts +5 -1
- package/dist/modes/rpc/host-ensure-client.js +8 -0
- package/dist/modes/rpc/host-ensure-types.d.ts +1 -0
- package/dist/modes/rpc/host-ensure.js +6 -11
- package/dist/modes/rpc/host-gc-pass.js +3 -0
- package/dist/modes/rpc/host-gc.js +3 -0
- package/dist/modes/rpc/host-internal-transport.d.ts +16 -5
- package/dist/modes/rpc/host-internal-transport.js +40 -9
- package/dist/modes/rpc/host-lifecycle-proxy.d.ts +4 -8
- package/dist/modes/rpc/host-lifecycle-proxy.js +9 -21
- package/dist/modes/rpc/host-lifecycle-shutdown.d.ts +2 -0
- package/dist/modes/rpc/host-lifecycle-shutdown.js +3 -0
- package/dist/modes/rpc/host-lifecycle.js +9 -16
- package/dist/modes/rpc/host-observe-request.js +1 -1
- package/dist/modes/rpc/host-probe.d.ts +8 -6
- package/dist/modes/rpc/host-probe.js +13 -13
- package/dist/modes/rpc/host-request-key-root.d.ts +8 -0
- package/dist/modes/rpc/host-request-key-root.js +14 -0
- package/dist/modes/rpc/host-runner.d.ts +3 -1
- package/dist/modes/rpc/host-runner.js +6 -1
- package/dist/modes/rpc/host-socket-bind.d.ts +6 -0
- package/dist/modes/rpc/host-socket-bind.js +42 -0
- package/dist/modes/rpc/host-socket-request.d.ts +3 -2
- package/dist/modes/rpc/host-socket-request.js +55 -10
- package/dist/modes/rpc/host-status.d.ts +3 -2
- package/dist/modes/rpc/host-status.js +5 -0
- package/dist/modes/rpc/host-supervisor-log.d.ts +1 -0
- package/dist/modes/rpc/host-supervisor-log.js +3 -0
- package/dist/modes/rpc/host-watchdog.d.ts +8 -1
- package/dist/modes/rpc/host-watchdog.js +37 -12
- package/dist/modes/rpc/internal-channel.js +14 -14
- package/dist/modes/rpc/multi-session-host.js +18 -46
- package/dist/modes/rpc/rpc-auth-client.d.ts +13 -0
- package/dist/modes/rpc/rpc-auth-client.js +142 -0
- package/dist/modes/rpc/rpc-auth-proof.d.ts +12 -0
- package/dist/modes/rpc/rpc-auth-proof.js +27 -0
- package/dist/modes/rpc/rpc-client-socket.d.ts +5 -0
- package/dist/modes/rpc/rpc-client-socket.js +30 -0
- package/dist/modes/rpc/rpc-client.d.ts +2 -0
- package/dist/modes/rpc/rpc-client.js +2 -24
- package/dist/modes/rpc/rpc-host-command-types.d.ts +4 -0
- package/dist/modes/rpc/rpc-protocol-info-types.d.ts +6 -0
- package/dist/modes/rpc/rpc-public-auth.d.ts +41 -0
- package/dist/modes/rpc/rpc-public-auth.js +237 -0
- package/dist/modes/rpc/rpc-public-key.d.ts +28 -0
- package/dist/modes/rpc/rpc-public-key.js +193 -0
- package/dist/modes/rpc/session-router-open-profile.js +2 -10
- package/dist/modes/rpc/socket-ownership.d.ts +7 -0
- package/dist/modes/rpc/socket-ownership.js +17 -0
- package/docs/rpc.md +108 -2
- package/docs/settings.md +1 -1
- package/docs/usage.md +14 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-3474DPZK.js +0 -2
- package/dist/bundle/chunks/chunk-64CFJHKT.js +0 -7
- package/dist/bundle/chunks/chunk-B6NJHBLH.js +0 -21
- package/dist/bundle/chunks/chunk-HZTQHE5O.js +0 -26
- package/dist/bundle/chunks/chunk-IBDADH5Z.js +0 -4
- package/dist/bundle/chunks/chunk-OJZD7CO7.js +0 -8
- package/dist/bundle/chunks/cli-main-4JNPG6LB.js +0 -3
- package/dist/bundle/chunks/host-command-I3ZMWQQ3.js +0 -2
- package/dist/bundle/chunks/host-lifecycle-QEO422VW.js +0 -3
- package/dist/bundle/chunks/host-session-command-INULKX6E.js +0 -20
- package/dist/bundle/chunks/interactive-mode-SYWJVPKK.js +0 -2
- package/dist/bundle/chunks/rpc-mode-JMCYSP4P.js +0 -2
- package/dist/bundle/chunks/rpc-monitor-source-34AQDTZ6.js +0 -3
- package/dist/bundle/chunks/session-control-endpoint-IW5X56WN.js +0 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{TOOL_CHILD_ROLE_ENV,channelEnvPresent}from"./chunk-
|
|
2
|
+
import{TOOL_CHILD_ROLE_ENV,channelEnvPresent}from"./chunk-V3CK2AVC.js";var role=process.env[TOOL_CHILD_ROLE_ENV];if(role!==void 0&&role!==""){if(role==="launch-shim"){let{runLaunchShimRole}=await import("./launch-shim-role-Q6NG2YCV.js");runLaunchShimRole()}role!=="tool-child"&&(process.stderr.write(`senpi: unknown internal role ${JSON.stringify(role)}
|
|
3
3
|
`),process.exit(70)),channelEnvPresent(process.env)||(process.stderr.write(`tool-child: channel unavailable
|
|
4
|
-
`),process.exit(70));let{runToolChildRole}=await import("./role-
|
|
4
|
+
`),process.exit(70));let{runToolChildRole}=await import("./role-M2MTVACJ.js");await runToolChildRole()}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{Text}from"./chunk-YJA2B7FQ.js";import{getShellConfig}from"./chunk-JAXZOEJE.js";import{HOST_CALL_KINDS,TOOL_CHILD_LIMITS,connectChannel,isRefId}from"./chunk-RAIZG6HA.js";import{typebox_exports}from"./chunk-MIENF2YK.js";import{TOOL_CHILD_LEDGER_ENV,TOOL_CHILD_REENTRY_ENV,TOOL_CHILD_REFS_ROOT_ENV,rememberToolChildReentry,scrubToolChildEnv,toolChildReentry}from"./chunk-OXNWERO4.js";import{BACKGROUND_START_GRACE_MS,DEFAULT_COLS,DEFAULT_MAX_SESSIONS,DEFAULT_ROWS,FOREGROUND_ENV_OVERRIDES,KILLED_SESSION_EXIT_GRACE_MS,TERMINAL_BASH_TOOL,TERMINAL_INPUT_TOOL,TERMINAL_KILL_TOOL,TERMINAL_OUTPUT_TOOL,TERMINAL_RESIZE_TOOL,encodeKeys,loadPty,safeRegExp}from"./chunk-NTK2MHJ5.js";import{processIsLive,processStartTimeMs,readProcessStartTime,sameProcessStartMs}from"./chunk-7ASLQ243.js";var ToolChildJobError=class extends Error{constructor(code,message){super(message),this.code=code,this.name="ToolChildJobError"}};var WakeAdmissionError=class extends Error{constructor(bytes){super(`wake_too_large: ${bytes} bytes exceeds the ${TOOL_CHILD_LIMITS.wakePendingBytes}-byte pending budget`),this.code="wake_too_large",this.name="WakeAdmissionError"}},WakeAdmission=class{#pending=new Map;#blocked=[];#pendingBytes=0;get pending(){return this.#pending.size}get blocked(){return this.#blocked.length}send(wakeId,bytes){return bytes>TOOL_CHILD_LIMITS.wakePendingBytes?Promise.reject(new WakeAdmissionError(bytes)):this.#blocked.length===0&&this.#fits(bytes)?(this.#admit(wakeId,bytes),Promise.resolve()):new Promise(release=>{this.#blocked.push({wakeId,bytes,release})})}ack(wakeId){let bytes=this.#pending.get(wakeId);if(bytes!==void 0){this.#pending.delete(wakeId),this.#pendingBytes-=bytes;for(let next=this.#blocked[0];next!==void 0&&this.#fits(next.bytes);next=this.#blocked[0])this.#blocked.shift(),this.#admit(next.wakeId,next.bytes),next.release()}}#fits(bytes){return this.#pending.size<TOOL_CHILD_LIMITS.wakePending&&this.#pendingBytes+bytes<=TOOL_CHILD_LIMITS.wakePendingBytes}#admit(wakeId,bytes){this.#pending.set(wakeId,bytes),this.#pendingBytes+=bytes}};function hostCallCapacity(){return TOOL_CHILD_LIMITS.HOST_CALL_ACTIVE+TOOL_CHILD_LIMITS.HOST_CALL_WAITING}var HostCallAdmission=class{#active=new Set;#waiting=[];get active(){return this.#active.size}get waiting(){return this.#waiting.length}submit(callId){return this.#active.has(callId)||this.#waiting.includes(callId)?refuse(callId,"host_call_duplicate"):this.#active.size<TOOL_CHILD_LIMITS.HOST_CALL_ACTIVE&&this.#waiting.length===0?(this.#active.add(callId),{state:"active"}):this.#active.size+this.#waiting.length>=hostCallCapacity()?refuse(callId,"host_call_capacity"):(this.#waiting.push(callId),{state:"waiting"})}settle(callId){if(!this.#active.delete(callId)){let index=this.#waiting.indexOf(callId);index>=0&&this.#waiting.splice(index,1);return}let next=this.#waiting.shift();return next!==void 0&&this.#active.add(next),next}};function refuse(callId,code){return{state:"refused",frame:{type:"host_call_refused",callId,code}}}import{createHash,randomBytes}from"node:crypto";import{createReadStream}from"node:fs";import{mkdir,readFile,rm as rm2,stat,writeFile}from"node:fs/promises";import{dirname}from"node:path";import{readdir,rm}from"node:fs/promises";import{join}from"node:path";function refGenerationDir(root,generation){return join(root,`gen-${generation}`)}function refFilePath(root,ref){if(!isRefId(ref.refId))throw new Error(`invalid ref id: ${JSON.stringify(ref.refId)}`);return join(refGenerationDir(root,ref.generation),"refs",ref.refId)}async function teardownRefGeneration(root,generation){await rm(refGenerationDir(root,generation),{recursive:!0,force:!0})}var PayloadRefError=class extends Error{constructor(code,message,reason){super(`${code}: ${message}`),this.name="PayloadRefError",this.code=code,this.reason=reason}},PayloadRefWriter=class{#root;#now;#pending=new Map;#ownedBytes=0;constructor(options){this.generation=options.generation,this.budget=options.budget??TOOL_CHILD_LIMITS.refsBudget,this.#root=options.root,this.#now=options.now??Date.now}get ownedBytes(){return this.#ownedBytes}async create(payload){let size=payload.byteLength;if(this.#ownedBytes+size>this.budget)throw new PayloadRefError("ref_budget_exceeded",`${size} bytes on top of ${this.#ownedBytes} owned exceeds ${this.budget}`);this.#ownedBytes+=size;let ref={refId:randomBytes(12).toString("hex"),generation:this.generation,bytes:size,sha256:createHash("sha256").update(payload).digest("hex")},path=refFilePath(this.#root,ref);try{await mkdir(dirname(path),{recursive:!0,mode:448}),await writeFile(path,payload,{flag:"wx",mode:384})}catch(error){throw this.#ownedBytes-=size,await rm2(path,{force:!0}),error}return this.#pending.set(ref.refId,{bytes:size,createdAtMs:this.#now()}),ref}async handle(control){let pending=this.#pending.get(control.refId);pending!==void 0&&(this.#pending.delete(control.refId),this.#ownedBytes-=pending.bytes,control.type==="ref_nack"&&await rm2(refFilePath(this.#root,{refId:control.refId,generation:this.generation}),{force:!0}))}pendingIds(){return[...this.#pending.keys()]}overdue(){let deadline=this.#now()-TOOL_CHILD_LIMITS.refAckMs;return[...this.#pending].filter(([,ref])=>ref.createdAtMs<=deadline).map(([refId])=>refId)}async teardown(){this.#pending.clear(),this.#ownedBytes=0,await teardownRefGeneration(this.#root,this.generation)}},isMissingFile=error=>error instanceof Error&&"code"in error&&(error.code==="ENOENT"||error.code==="ENOTDIR");async function hashFile(path,signal){let hash=createHash("sha256");for await(let chunk of createReadStream(path)){if(signal?.aborted)return"cancelled";hash.update(chunk)}return hash.digest("hex")}var PayloadRefReader=class{#root;#generation;#budget;#send;#owned=new Map;#ownedBytes=0;constructor(options){this.#root=options.root,this.#generation=options.generation,this.#budget=options.budget??TOOL_CHILD_LIMITS.refsBudget,this.#send=options.send}async accept(ref,signal){let reason=await this.#verify(ref,signal);return reason!==void 0?(this.refuse(ref,reason),{ok:!1,reason}):(this.#owned.set(ref.refId,ref),this.#ownedBytes+=ref.bytes,this.#send({type:"ref_ack",refId:ref.refId}),{ok:!0})}refuse(ref,reason){this.#owned.has(ref.refId)||this.#send({type:"ref_nack",refId:ref.refId,reason})}async consume(refId){let ref=this.#owned.get(refId);if(ref===void 0)throw new PayloadRefError("ref_not_owned",`ref ${refId} is not owned by this side`);let path=refFilePath(this.#root,ref),bytes=await readFile(path);return await rm2(path,{force:!0}),this.#owned.delete(refId),this.#ownedBytes-=ref.bytes,bytes}ownedIds(){return[...this.#owned.keys()]}async#verify(ref,signal){if(signal?.aborted)return"cancelled";if(ref.generation!==this.#generation||!isRefId(ref.refId)||this.#owned.has(ref.refId))return"foreign";if(this.#ownedBytes+ref.bytes>this.#budget)return"budget";let path=refFilePath(this.#root,ref);try{if((await stat(path)).size!==ref.bytes)return"corrupt";let digest=await hashFile(path,signal);return digest==="cancelled"?"cancelled":digest===ref.sha256?void 0:"corrupt"}catch(error){if(isMissingFile(error))return"missing";throw error}}};async function packPayload(value,writer){let normalized=value===void 0?null:value,json=JSON.stringify(normalized);return json===void 0||Buffer.byteLength(json,"utf8")<=TOOL_CHILD_LIMITS.REF_THRESHOLD?{inline:!0,value:normalized}:{inline:!1,ref:await writer.create(Buffer.from(json,"utf8"))}}async function unpackPayload(ref,reader,signal){let acceptance=await reader.accept(ref,signal);if(!acceptance.ok)throw new PayloadRefError("ref_rejected",`ref ${ref.refId} was refused (${acceptance.reason})`,acceptance.reason);let bytes=await reader.consume(ref.refId);return JSON.parse(Buffer.from(bytes.buffer,bytes.byteOffset,bytes.byteLength).toString("utf8"))}var ChildHostCalls=class{#channel;#admissions=new Map;#pending=new Map;#refs;#next=0;constructor(channel,refs){this.#channel=channel,this.#refs=refs}load(jobId){let admission=this.#admissions.get(jobId);return{active:admission?.active??0,waiting:admission?.waiting??0}}async call(jobId,target,signal){let label=target.name??target.kind;if(signal.aborted)throw new ToolChildJobError("tool_child_cancelled",`${label}: job cancelled`);let callId=`h${++this.#next}`,packed=target.args===void 0||this.#refs===void 0?{inline:!0,value:target.args}:await packPayload(target.args,this.#refs.writer),frame={type:"host_call",callId,jobId,kind:target.kind,...target.name===void 0?{}:{name:target.name},...packed.inline?packed.value===void 0?{}:{args:packed.value}:{argsRef:packed.ref}};if(signal.aborted)throw this.#releaseUnsent(frame),new ToolChildJobError("tool_child_cancelled",`${label}: job cancelled`);return new Promise((resolve,reject)=>{let decision=this.#admission(jobId).submit(callId);if(decision.state==="refused"){this.#forgetIdle(jobId),this.#releaseUnsent(frame),reject(new ToolChildJobError(decision.frame.code,`${label}: host call ${callId} refused`));return}let pending={frame,resolve,reject,sent:!1};this.#pending.set(callId,pending),signal.addEventListener("abort",()=>this.#settle(callId,call=>{call.sent||this.#releaseUnsent(call.frame),call.reject(new ToolChildJobError("tool_child_cancelled",`${label}: job cancelled`))}),{once:!0}),decision.state==="active"&&this.#send(pending)})}handle(frame){if(frame.type==="host_reply"){let{valueRef}=frame;return this.#settle(frame.callId,call=>{frame.error!==void 0?call.reject(new ToolChildJobError("host_call_failed",frame.error)):valueRef===void 0?call.resolve(frame.value??null):this.#refs===void 0?call.reject(new ToolChildJobError("host_call_failed","a valueRef reply on a side that reads no refs")):unpackPayload(valueRef,this.#refs.reader).then(call.resolve,error=>call.reject(new ToolChildJobError("host_call_failed",error instanceof Error?error.message:String(error))))}),!0}return frame.type==="host_call_refused"?(this.#settle(frame.callId,call=>call.reject(new ToolChildJobError(frame.code,`host call ${frame.callId} refused by the server`))),!0):!1}close(){for(let callId of[...this.#pending.keys()])this.#settle(callId,call=>call.reject(new ToolChildJobError("tool_child_exited","the channel closed")))}#admission(jobId){let admission=this.#admissions.get(jobId);return admission===void 0&&(admission=new HostCallAdmission,this.#admissions.set(jobId,admission)),admission}#forgetIdle(jobId){let admission=this.#admissions.get(jobId);admission!==void 0&&admission.active===0&&admission.waiting===0&&this.#admissions.delete(jobId)}#send(call){call.sent=!0,this.#channel.send(call.frame)}#releaseUnsent(frame){frame.argsRef!==void 0&&this.#refs?.writer.handle({type:"ref_nack",refId:frame.argsRef.refId,reason:"cancelled"})}#settle(callId,apply){let call=this.#pending.get(callId);if(call===void 0)return;this.#pending.delete(callId);let{jobId}=call.frame,promoted=this.#admissions.get(jobId)?.settle(callId);if(this.#forgetIdle(jobId),apply(call),promoted!==void 0){let next=this.#pending.get(promoted);next!==void 0&&this.#send(next)}}};var JobUpdates=class{#jobId;#channel;#writer;#window;#nextSeq=0;#acked=0;#waiters=[];#ended=!1;#tail=Promise.resolve();constructor(options){this.#jobId=options.jobId,this.#channel=options.channel,this.#writer=options.writer,this.#window=options.window??TOOL_CHILD_LIMITS.updatesInFlight}update(value){let sent=this.#tail.then(()=>this.#send(value));return this.#tail=sent.catch(()=>{}),sent}async#send(value){for(;!this.#ended&&this.#nextSeq-this.#acked>=this.#window;)await new Promise(resolve=>this.#waiters.push(resolve));if(this.#ended)throw new Error(`update after job ${this.#jobId} ended`);let packed=this.#writer===void 0?{inline:!0,value}:await packPayload(value,this.#writer);if(this.#ended)throw new Error(`update after job ${this.#jobId} ended`);let seq=++this.#nextSeq;this.#channel.send(packed.inline?{type:"update",jobId:this.#jobId,seq,...packed.value===void 0?{}:{value:packed.value}}:{type:"update",jobId:this.#jobId,seq,valueRef:packed.ref})}acked(seq){seq<=this.#acked||(this.#acked=Math.min(seq,this.#nextSeq),this.#wake())}end(){this.#ended=!0,this.#wake()}#wake(){for(let resolve of this.#waiters.splice(0))resolve()}};var BarrierClock=class _BarrierClock{constructor(buffer){if(buffer.byteLength%Int32Array.BYTES_PER_ELEMENT!==0)throw new Error("barrier clock buffer is misaligned");let length=buffer.byteLength/Int32Array.BYTES_PER_ELEMENT;if(length<5||(length-1)%4!==0)throw new Error("barrier clock buffer has no slots");this.buffer=buffer,this.cells=new Int32Array(buffer),this.slots=(length-1)/4}static create(slots=256){if(!Number.isInteger(slots)||slots<1)throw new Error(`barrier clock needs at least one slot, got ${slots}`);return new _BarrierClock(new SharedArrayBuffer((1+slots*4)*Int32Array.BYTES_PER_ELEMENT))}tick(){return Atomics.add(this.cells,0,1)+1}claimSlot(){for(let slot=0;slot<this.slots;slot++)if(Atomics.compareExchange(this.cells,this.index(slot,3),0,1)===0)return Atomics.store(this.cells,this.index(slot,0),0),Atomics.store(this.cells,this.index(slot,1),0),Atomics.store(this.cells,this.index(slot,2),0),slot}releaseSlot(slot){Atomics.store(this.cells,this.index(slot,3),0)}recordAck(slot){let tick=this.tick();return Atomics.store(this.cells,this.index(slot,0),tick),Atomics.notify(this.cells,this.index(slot,0)),tick}recordInvoke(slot){let ackTick=Atomics.load(this.cells,this.index(slot,0)),invokeTick=this.tick();return Atomics.store(this.cells,this.index(slot,1),invokeTick),Atomics.add(this.cells,this.index(slot,2),1),{ackTick:ackTick===0?null:ackTick,invokeTick}}waitForAck(slot,timeoutMs=Number.POSITIVE_INFINITY){let at=this.index(slot,0);for(;Atomics.load(this.cells,at)===0;)if(Atomics.wait(this.cells,at,0,timeoutMs)==="timed-out")return"timed-out";return"acked"}read(slot){let ackTick=Atomics.load(this.cells,this.index(slot,0)),invokeTick=Atomics.load(this.cells,this.index(slot,1));return{ackTick:ackTick===0?null:ackTick,invokeTick:invokeTick===0?null:invokeTick,invocations:Atomics.load(this.cells,this.index(slot,2))}}index(slot,field){if(!Number.isInteger(slot)||slot<0||slot>=this.slots)throw new Error(`no barrier slot ${slot}`);return 1+slot*4+field}};function proofIsOrdered(proof){return proof.ackTick!==null&&proof.ackTick<proof.invokeTick}import{randomUUID}from"node:crypto";import{existsSync,readdirSync,rmSync,watch}from"node:fs";import{writeFile as writeFile2}from"node:fs/promises";import{join as join2}from"node:path";var PREFIX="SENPI_TOOL_CHILD_TEST_",TEST_CONTROL_DIR_ENV=`${PREFIX}CONTROL_DIR`;function readTestControls(env){let dir=env[TEST_CONTROL_DIR_ENV],value=name=>{let raw=env[`${PREFIX}${name}`];return raw===void 0||raw===""||raw==="0"?void 0:raw};return{enabled:name=>value(name)!==void 0,value,released:file=>dir===void 0?Promise.reject(new Error(`${TEST_CONTROL_DIR_ENV} is not set`)):waitForFile(dir,file),watchPrefix:(prefix,onFile)=>dir===void 0?{ready:Promise.reject(new Error(`${TEST_CONTROL_DIR_ENV} is not set`)),close:()=>{}}:watchPrefix(dir,prefix,onFile)}}function watchPrefix(dir,prefix,onFile){let seen=new Set,report=name=>{!name.startsWith(prefix)||seen.has(name)||(seen.add(name),onFile(name))},sentinel=`.watch-${process.pid}-${randomUUID()}`,resend,live=!1,markReady=()=>{},failReady=()=>{},ready=new Promise((resolve,reject)=>{markReady=resolve,failReady=reject}),watcher=watch(dir,(_event,name)=>{if(name!==null){if(name===sentinel){live=!0,clearTimeout(resend),rmSync(join2(dir,sentinel),{force:!0});for(let existing of readdirSync(dir))report(existing);markReady();return}existsSync(join2(dir,name))&&report(name)}}),touch=()=>{live||writeFile2(join2(dir,sentinel),String(Date.now())).then(()=>{live||(resend=setTimeout(touch,50))},error=>failReady(error))};return touch(),{ready,close:()=>{live=!0,clearTimeout(resend),watcher.close(),rmSync(join2(dir,sentinel),{force:!0})}}}function waitForFile(dir,file){let path=join2(dir,file),sentinel=join2(dir,`.watch-${process.pid}-${randomUUID()}`);return new Promise((resolve,reject)=>{let done=!1,resend,finish=()=>{done||(done=!0,clearTimeout(resend),watcher.close(),rmSync(sentinel,{force:!0}),resolve())},watcher=watch(dir,(_event,name)=>{if(existsSync(path)){finish();return}name!==null&&join2(dir,name)===sentinel&&clearTimeout(resend)}),touch=()=>{done||writeFile2(sentinel,String(Date.now())).then(()=>{done||(existsSync(path)?finish():resend=setTimeout(touch,50))},error=>{done||(done=!0,watcher.close(),reject(error))})};touch()})}var WindowsJobError=class extends Error{constructor(step,win32Error){super(`tool_child_job_assign_failed: ${step} failed (win32 error ${win32Error})`),this.code="tool_child_job_assign_failed",this.name="WindowsJobError",this.win32Error=win32Error}};function extendedLimitInformation(){let info=new Uint8Array(144);return new DataView(info.buffer).setUint32(16,10240,!0),info}var WindowsJob=class _WindowsJob{constructor(kernel,handle){this.closed=!1,this.kernel=kernel,this.handle=handle}static create(kernel){let handle=kernel.CreateJobObjectW(null,null);if(!handle)throw new WindowsJobError("CreateJobObjectW",kernel.GetLastError());let info=extendedLimitInformation();if(kernel.SetInformationJobObject(handle,9,info,info.byteLength)===0){let error=kernel.GetLastError();throw kernel.CloseHandle(handle),new WindowsJobError("SetInformationJobObject",error)}return new _WindowsJob(kernel,handle)}assign(pid){let process2=this.kernel.OpenProcess(4353,0,pid);if(!process2)throw new WindowsJobError("OpenProcess",this.kernel.GetLastError());try{if(this.kernel.AssignProcessToJobObject(this.handle,process2)===0)throw new WindowsJobError("AssignProcessToJobObject",this.kernel.GetLastError());let inJob=new Uint8Array(4);if(this.kernel.IsProcessInJob(process2,this.handle,inJob)===0||new DataView(inJob.buffer).getInt32(0,!0)===0)throw new WindowsJobError("IsProcessInJob",this.kernel.GetLastError())}finally{this.kernel.CloseHandle(process2)}}memberPids(){let list=new Uint8Array(8200);if(this.kernel.QueryInformationJobObject(this.handle,3,list,list.byteLength,null)===0)return[];let view=new DataView(list.buffer),count=Math.min(view.getUint32(4,!0),1024);return Array.from({length:count},(_,index)=>Number(view.getBigUint64(8+index*8,!0)))}terminate(exitCode=1){this.closed||this.kernel.TerminateJobObject(this.handle,exitCode)}close(){this.closed||(this.closed=!0,this.kernel.CloseHandle(this.handle))}};function loadKernel32Job(){if(process.platform!=="win32")return;let ffi=process.getBuiltinModule("bun:ffi");if(ffi===void 0)return;let lib=ffi.dlopen("kernel32.dll",{CreateJobObjectW:{args:["ptr","ptr"],returns:"ptr"},SetInformationJobObject:{args:["ptr","i32","ptr","u32"],returns:"i32"},QueryInformationJobObject:{args:["ptr","i32","ptr","u32","ptr"],returns:"i32"},OpenProcess:{args:["u32","i32","u32"],returns:"ptr"},AssignProcessToJobObject:{args:["ptr","ptr"],returns:"i32"},IsProcessInJob:{args:["ptr","ptr","ptr"],returns:"i32"},TerminateJobObject:{args:["ptr","u32"],returns:"i32"},CloseHandle:{args:["ptr"],returns:"i32"},GetLastError:{args:[],returns:"u32"},GetCurrentProcess:{args:[],returns:"ptr"}}),call=(name,...args)=>{let fn=lib.symbols[name];if(fn===void 0)throw new Error(`kernel32 has no ${name}`);return fn(...args.map(arg=>arg instanceof Uint8Array?ffi.ptr(arg):arg))};return{CreateJobObjectW:()=>call("CreateJobObjectW",null,null),SetInformationJobObject:(job,infoClass,info,length)=>Number(call("SetInformationJobObject",job,infoClass,info,length)),QueryInformationJobObject:(job,infoClass,info,length)=>Number(call("QueryInformationJobObject",job,infoClass,info,length,null)),OpenProcess:(access,inherit,pid)=>call("OpenProcess",access,inherit,pid),AssignProcessToJobObject:(job,process2)=>Number(call("AssignProcessToJobObject",job,process2)),IsProcessInJob:(process2,job,result)=>Number(call("IsProcessInJob",process2,job,result)),TerminateJobObject:(job,exitCode)=>Number(call("TerminateJobObject",job,exitCode)),CloseHandle:handle=>Number(call("CloseHandle",handle)),GetLastError:()=>Number(call("GetLastError")),GetCurrentProcess:()=>call("GetCurrentProcess")}}var defaultInJob;function currentProcessInJob(kernel){if(kernel!==void 0)return queryInJob(kernel);if(defaultInJob===void 0){let loaded=loadKernel32Job();defaultInJob=loaded===void 0?!1:queryInJob(loaded)}return defaultInJob}function queryInJob(kernel){let result=new Uint8Array(4);return kernel.IsProcessInJob(kernel.GetCurrentProcess(),null,result)!==0&&new DataView(result.buffer).getInt32(0,!0)!==0}var FINISHED_SLOT_STATES=256,LaunchGateError=class extends Error{constructor(code,message){super(`${code}: ${message}`),this.name="LaunchGateError",this.code=code}},LaunchGate=class{#channel;#slots=new Map;#finished=new Map;#controls;#next=0;constructor(channel,clock=BarrierClock.create(),env=process.env,creatorInJob=currentProcessInJob){this.#channel=channel,this.clock=clock,this.#controls=readTestControls(env),this.#creatorInJob=creatorInJob}#creatorInJob;started(launchId,pid,breakaway=!1){this.#slots.has(launchId)&&this.#channel.send({type:"command_started",launchId,pid,startedAtMs:Date.now(),creatorInJob:this.#creatorInJob(),breakaway})}get skipsAckWait(){return this.#controls.enabled("SKIP_ACK_WAIT")}request(request){let slot=this.clock.claimSlot();if(slot===void 0)throw new LaunchGateError("tool_child_launch_capacity",`${this.clock.slots} launches in flight`);let launchId=`L${process.pid}-${++this.#next}`,ack,acked=new Promise(resolve=>{ack=resolve});return this.#slots.set(launchId,{slot,ack,lifetime:request.lifetime,request}),this.#channel.send({type:"launch",launchId,...request}),this.#controls.enabled("STOP_AFTER_SPAWN")&&process.kill(process.pid,"SIGSTOP"),{launchId,slot,acked}}adopt(launchId,slot,request){this.#slots.set(launchId,{slot,ack:()=>{},lifetime:request.lifetime,request}),this.#channel.send({type:"launch",launchId,...request})}handleWorker(message){message.kind==="launch"?this.adopt(message.launchId,message.slot,message.request):message.kind==="invoking"?this.invoking(message.launchId):message.kind==="started"?this.started(message.launchId,message.pid,message.breakaway):this.done(message.launchId,{...message.exitCode===void 0?{}:{exitCode:message.exitCode},...message.signal===void 0?{}:{signal:message.signal}})}handle(message){if(message.type==="launch_ack"){let entry=this.#slots.get(message.launchId);return entry!==void 0&&(this.clock.recordAck(entry.slot),entry.ack()),!0}if(message.type==="slot_query"){let entry=this.#slots.get(message.launchId),state=entry===void 0?this.#finished.get(message.launchId)??{ackTick:null,invokeTick:null,invocations:0}:this.clock.read(entry.slot);return this.#channel.send({type:"slot_state",launchId:message.launchId,...state}),!0}return!1}invokeNow(launchId){let entry=this.#slots.get(launchId);if(entry===void 0)throw new LaunchGateError("tool_child_launch_unknown",launchId);this.clock.recordInvoke(entry.slot),this.invoking(launchId)}invoking(launchId){let entry=this.#slots.get(launchId);if(entry===void 0)return;let state=this.clock.read(entry.slot),invokeTick=state.invokeTick;if(invokeTick===null)return;let send=()=>this.#channel.send({type:"launch_proof",launchId,ackTick:state.ackTick,invokeTick}),hold=this.#controls.value("HOLD_LAUNCH_PROOF");if(hold!==void 0&&(hold!=="detached"||entry.lifetime==="detached")){this.#controls.released(`release-proof-${launchId}`).then(send);return}send()}endJobGroups(jobId){let ended=[];for(let[launchId,{request}]of this.#slots)if(!(request.jobId!==jobId||request.lifetime!=="owned"||request.containment!=="own-group")&&!(request.pgid===void 0||request.pgid<=1))try{process.kill(-request.pgid,"SIGKILL"),ended.push({launchId,pgid:request.pgid})}catch{}for(let{launchId}of ended)this.done(launchId,{signal:"SIGKILL"});return ended.map(({pgid})=>pgid)}done(launchId,outcome){let entry=this.#slots.get(launchId);entry!==void 0&&(this.#slots.delete(launchId),this.#finished.set(launchId,this.clock.read(entry.slot)),this.#finished.size>FINISHED_SLOT_STATES&&this.#finished.delete(this.#finished.keys().next().value??""),this.clock.releaseSlot(entry.slot),this.#channel.send({type:"launch_done",launchId,...outcome.exitCode===void 0?{}:{exitCode:outcome.exitCode},...outcome.signal===void 0?{}:{signal:outcome.signal}}))}};import{mkdir as mkdir3,readdir as readdir2,readFile as readFile3,rm as rm4,writeFile as writeFile3}from"node:fs/promises";import{dirname as dirname3,join as join4}from"node:path";import{appendFile,mkdir as mkdir2,open,readFile as readFile2,rm as rm3}from"node:fs/promises";import{dirname as dirname2,join as join3}from"node:path";function generationLedgerPath(dir,generation){return join3(dir,`g${generation}`,"descendants.ledger")}var DescendantsLedger=class{#path;#durableDir=!1;#entries=new Map;constructor(path){this.#path=path}get path(){return this.#path}entries(){return[...this.#entries.values()]}async append(entry){if(!this.#durableDir){let directory=dirname2(this.#path);await mkdir2(directory,{recursive:!0,mode:448}),await(await open(this.#path,"a",384)).close(),await syncDirectory(directory),await syncDirectory(dirname2(directory)),this.#durableDir=!0}let handle=await open(this.#path,"a",384);try{await handle.write(`${JSON.stringify(entry)}
|
|
2
|
+
import{Text}from"./chunk-FDL4CF6F.js";import{getShellConfig}from"./chunk-BWA4FVFW.js";import{HOST_CALL_KINDS,TOOL_CHILD_LIMITS,connectChannel,isRefId}from"./chunk-V3CK2AVC.js";import{typebox_exports}from"./chunk-MIENF2YK.js";import{TOOL_CHILD_LEDGER_ENV,TOOL_CHILD_REENTRY_ENV,TOOL_CHILD_REFS_ROOT_ENV,rememberToolChildReentry,scrubToolChildEnv,toolChildReentry}from"./chunk-OXNWERO4.js";import{BACKGROUND_START_GRACE_MS,DEFAULT_COLS,DEFAULT_MAX_SESSIONS,DEFAULT_ROWS,FOREGROUND_ENV_OVERRIDES,KILLED_SESSION_EXIT_GRACE_MS,TERMINAL_BASH_TOOL,TERMINAL_INPUT_TOOL,TERMINAL_KILL_TOOL,TERMINAL_OUTPUT_TOOL,TERMINAL_RESIZE_TOOL,encodeKeys,loadPty,safeRegExp}from"./chunk-NTK2MHJ5.js";import{processIsLive,processStartTimeMs,readProcessStartTime,sameProcessStartMs}from"./chunk-7ASLQ243.js";var ToolChildJobError=class extends Error{constructor(code,message){super(message),this.code=code,this.name="ToolChildJobError"}};var WakeAdmissionError=class extends Error{constructor(bytes){super(`wake_too_large: ${bytes} bytes exceeds the ${TOOL_CHILD_LIMITS.wakePendingBytes}-byte pending budget`),this.code="wake_too_large",this.name="WakeAdmissionError"}},WakeAdmission=class{#pending=new Map;#blocked=[];#pendingBytes=0;get pending(){return this.#pending.size}get blocked(){return this.#blocked.length}send(wakeId,bytes){return bytes>TOOL_CHILD_LIMITS.wakePendingBytes?Promise.reject(new WakeAdmissionError(bytes)):this.#blocked.length===0&&this.#fits(bytes)?(this.#admit(wakeId,bytes),Promise.resolve()):new Promise(release=>{this.#blocked.push({wakeId,bytes,release})})}ack(wakeId){let bytes=this.#pending.get(wakeId);if(bytes!==void 0){this.#pending.delete(wakeId),this.#pendingBytes-=bytes;for(let next=this.#blocked[0];next!==void 0&&this.#fits(next.bytes);next=this.#blocked[0])this.#blocked.shift(),this.#admit(next.wakeId,next.bytes),next.release()}}#fits(bytes){return this.#pending.size<TOOL_CHILD_LIMITS.wakePending&&this.#pendingBytes+bytes<=TOOL_CHILD_LIMITS.wakePendingBytes}#admit(wakeId,bytes){this.#pending.set(wakeId,bytes),this.#pendingBytes+=bytes}};function hostCallCapacity(){return TOOL_CHILD_LIMITS.HOST_CALL_ACTIVE+TOOL_CHILD_LIMITS.HOST_CALL_WAITING}var HostCallAdmission=class{#active=new Set;#waiting=[];get active(){return this.#active.size}get waiting(){return this.#waiting.length}submit(callId){return this.#active.has(callId)||this.#waiting.includes(callId)?refuse(callId,"host_call_duplicate"):this.#active.size<TOOL_CHILD_LIMITS.HOST_CALL_ACTIVE&&this.#waiting.length===0?(this.#active.add(callId),{state:"active"}):this.#active.size+this.#waiting.length>=hostCallCapacity()?refuse(callId,"host_call_capacity"):(this.#waiting.push(callId),{state:"waiting"})}settle(callId){if(!this.#active.delete(callId)){let index=this.#waiting.indexOf(callId);index>=0&&this.#waiting.splice(index,1);return}let next=this.#waiting.shift();return next!==void 0&&this.#active.add(next),next}};function refuse(callId,code){return{state:"refused",frame:{type:"host_call_refused",callId,code}}}import{createHash,randomBytes}from"node:crypto";import{createReadStream}from"node:fs";import{mkdir,readFile,rm as rm2,stat,writeFile}from"node:fs/promises";import{dirname}from"node:path";import{readdir,rm}from"node:fs/promises";import{join}from"node:path";function refGenerationDir(root,generation){return join(root,`gen-${generation}`)}function refFilePath(root,ref){if(!isRefId(ref.refId))throw new Error(`invalid ref id: ${JSON.stringify(ref.refId)}`);return join(refGenerationDir(root,ref.generation),"refs",ref.refId)}async function teardownRefGeneration(root,generation){await rm(refGenerationDir(root,generation),{recursive:!0,force:!0})}var PayloadRefError=class extends Error{constructor(code,message,reason){super(`${code}: ${message}`),this.name="PayloadRefError",this.code=code,this.reason=reason}},PayloadRefWriter=class{#root;#now;#pending=new Map;#ownedBytes=0;constructor(options){this.generation=options.generation,this.budget=options.budget??TOOL_CHILD_LIMITS.refsBudget,this.#root=options.root,this.#now=options.now??Date.now}get ownedBytes(){return this.#ownedBytes}async create(payload){let size=payload.byteLength;if(this.#ownedBytes+size>this.budget)throw new PayloadRefError("ref_budget_exceeded",`${size} bytes on top of ${this.#ownedBytes} owned exceeds ${this.budget}`);this.#ownedBytes+=size;let ref={refId:randomBytes(12).toString("hex"),generation:this.generation,bytes:size,sha256:createHash("sha256").update(payload).digest("hex")},path=refFilePath(this.#root,ref);try{await mkdir(dirname(path),{recursive:!0,mode:448}),await writeFile(path,payload,{flag:"wx",mode:384})}catch(error){throw this.#ownedBytes-=size,await rm2(path,{force:!0}),error}return this.#pending.set(ref.refId,{bytes:size,createdAtMs:this.#now()}),ref}async handle(control){let pending=this.#pending.get(control.refId);pending!==void 0&&(this.#pending.delete(control.refId),this.#ownedBytes-=pending.bytes,control.type==="ref_nack"&&await rm2(refFilePath(this.#root,{refId:control.refId,generation:this.generation}),{force:!0}))}pendingIds(){return[...this.#pending.keys()]}overdue(){let deadline=this.#now()-TOOL_CHILD_LIMITS.refAckMs;return[...this.#pending].filter(([,ref])=>ref.createdAtMs<=deadline).map(([refId])=>refId)}async teardown(){this.#pending.clear(),this.#ownedBytes=0,await teardownRefGeneration(this.#root,this.generation)}},isMissingFile=error=>error instanceof Error&&"code"in error&&(error.code==="ENOENT"||error.code==="ENOTDIR");async function hashFile(path,signal){let hash=createHash("sha256");for await(let chunk of createReadStream(path)){if(signal?.aborted)return"cancelled";hash.update(chunk)}return hash.digest("hex")}var PayloadRefReader=class{#root;#generation;#budget;#send;#owned=new Map;#ownedBytes=0;constructor(options){this.#root=options.root,this.#generation=options.generation,this.#budget=options.budget??TOOL_CHILD_LIMITS.refsBudget,this.#send=options.send}async accept(ref,signal){let reason=await this.#verify(ref,signal);return reason!==void 0?(this.refuse(ref,reason),{ok:!1,reason}):(this.#owned.set(ref.refId,ref),this.#ownedBytes+=ref.bytes,this.#send({type:"ref_ack",refId:ref.refId}),{ok:!0})}refuse(ref,reason){this.#owned.has(ref.refId)||this.#send({type:"ref_nack",refId:ref.refId,reason})}async consume(refId){let ref=this.#owned.get(refId);if(ref===void 0)throw new PayloadRefError("ref_not_owned",`ref ${refId} is not owned by this side`);let path=refFilePath(this.#root,ref),bytes=await readFile(path);return await rm2(path,{force:!0}),this.#owned.delete(refId),this.#ownedBytes-=ref.bytes,bytes}ownedIds(){return[...this.#owned.keys()]}async#verify(ref,signal){if(signal?.aborted)return"cancelled";if(ref.generation!==this.#generation||!isRefId(ref.refId)||this.#owned.has(ref.refId))return"foreign";if(this.#ownedBytes+ref.bytes>this.#budget)return"budget";let path=refFilePath(this.#root,ref);try{if((await stat(path)).size!==ref.bytes)return"corrupt";let digest=await hashFile(path,signal);return digest==="cancelled"?"cancelled":digest===ref.sha256?void 0:"corrupt"}catch(error){if(isMissingFile(error))return"missing";throw error}}};async function packPayload(value,writer){let normalized=value===void 0?null:value,json=JSON.stringify(normalized);return json===void 0||Buffer.byteLength(json,"utf8")<=TOOL_CHILD_LIMITS.REF_THRESHOLD?{inline:!0,value:normalized}:{inline:!1,ref:await writer.create(Buffer.from(json,"utf8"))}}async function unpackPayload(ref,reader,signal){let acceptance=await reader.accept(ref,signal);if(!acceptance.ok)throw new PayloadRefError("ref_rejected",`ref ${ref.refId} was refused (${acceptance.reason})`,acceptance.reason);let bytes=await reader.consume(ref.refId);return JSON.parse(Buffer.from(bytes.buffer,bytes.byteOffset,bytes.byteLength).toString("utf8"))}var ChildHostCalls=class{#channel;#admissions=new Map;#pending=new Map;#refs;#next=0;constructor(channel,refs){this.#channel=channel,this.#refs=refs}load(jobId){let admission=this.#admissions.get(jobId);return{active:admission?.active??0,waiting:admission?.waiting??0}}async call(jobId,target,signal){let label=target.name??target.kind;if(signal.aborted)throw new ToolChildJobError("tool_child_cancelled",`${label}: job cancelled`);let callId=`h${++this.#next}`,packed=target.args===void 0||this.#refs===void 0?{inline:!0,value:target.args}:await packPayload(target.args,this.#refs.writer),frame={type:"host_call",callId,jobId,kind:target.kind,...target.name===void 0?{}:{name:target.name},...packed.inline?packed.value===void 0?{}:{args:packed.value}:{argsRef:packed.ref}};if(signal.aborted)throw this.#releaseUnsent(frame),new ToolChildJobError("tool_child_cancelled",`${label}: job cancelled`);return new Promise((resolve,reject)=>{let decision=this.#admission(jobId).submit(callId);if(decision.state==="refused"){this.#forgetIdle(jobId),this.#releaseUnsent(frame),reject(new ToolChildJobError(decision.frame.code,`${label}: host call ${callId} refused`));return}let pending={frame,resolve,reject,sent:!1};this.#pending.set(callId,pending),signal.addEventListener("abort",()=>this.#settle(callId,call=>{call.sent||this.#releaseUnsent(call.frame),call.reject(new ToolChildJobError("tool_child_cancelled",`${label}: job cancelled`))}),{once:!0}),decision.state==="active"&&this.#send(pending)})}handle(frame){if(frame.type==="host_reply"){let{valueRef}=frame;return this.#settle(frame.callId,call=>{frame.error!==void 0?call.reject(new ToolChildJobError("host_call_failed",frame.error)):valueRef===void 0?call.resolve(frame.value??null):this.#refs===void 0?call.reject(new ToolChildJobError("host_call_failed","a valueRef reply on a side that reads no refs")):unpackPayload(valueRef,this.#refs.reader).then(call.resolve,error=>call.reject(new ToolChildJobError("host_call_failed",error instanceof Error?error.message:String(error))))}),!0}return frame.type==="host_call_refused"?(this.#settle(frame.callId,call=>call.reject(new ToolChildJobError(frame.code,`host call ${frame.callId} refused by the server`))),!0):!1}close(){for(let callId of[...this.#pending.keys()])this.#settle(callId,call=>call.reject(new ToolChildJobError("tool_child_exited","the channel closed")))}#admission(jobId){let admission=this.#admissions.get(jobId);return admission===void 0&&(admission=new HostCallAdmission,this.#admissions.set(jobId,admission)),admission}#forgetIdle(jobId){let admission=this.#admissions.get(jobId);admission!==void 0&&admission.active===0&&admission.waiting===0&&this.#admissions.delete(jobId)}#send(call){call.sent=!0,this.#channel.send(call.frame)}#releaseUnsent(frame){frame.argsRef!==void 0&&this.#refs?.writer.handle({type:"ref_nack",refId:frame.argsRef.refId,reason:"cancelled"})}#settle(callId,apply){let call=this.#pending.get(callId);if(call===void 0)return;this.#pending.delete(callId);let{jobId}=call.frame,promoted=this.#admissions.get(jobId)?.settle(callId);if(this.#forgetIdle(jobId),apply(call),promoted!==void 0){let next=this.#pending.get(promoted);next!==void 0&&this.#send(next)}}};var JobUpdates=class{#jobId;#channel;#writer;#window;#nextSeq=0;#acked=0;#waiters=[];#ended=!1;#tail=Promise.resolve();constructor(options){this.#jobId=options.jobId,this.#channel=options.channel,this.#writer=options.writer,this.#window=options.window??TOOL_CHILD_LIMITS.updatesInFlight}update(value){let sent=this.#tail.then(()=>this.#send(value));return this.#tail=sent.catch(()=>{}),sent}async#send(value){for(;!this.#ended&&this.#nextSeq-this.#acked>=this.#window;)await new Promise(resolve=>this.#waiters.push(resolve));if(this.#ended)throw new Error(`update after job ${this.#jobId} ended`);let packed=this.#writer===void 0?{inline:!0,value}:await packPayload(value,this.#writer);if(this.#ended)throw new Error(`update after job ${this.#jobId} ended`);let seq=++this.#nextSeq;this.#channel.send(packed.inline?{type:"update",jobId:this.#jobId,seq,...packed.value===void 0?{}:{value:packed.value}}:{type:"update",jobId:this.#jobId,seq,valueRef:packed.ref})}acked(seq){seq<=this.#acked||(this.#acked=Math.min(seq,this.#nextSeq),this.#wake())}end(){this.#ended=!0,this.#wake()}#wake(){for(let resolve of this.#waiters.splice(0))resolve()}};var BarrierClock=class _BarrierClock{constructor(buffer){if(buffer.byteLength%Int32Array.BYTES_PER_ELEMENT!==0)throw new Error("barrier clock buffer is misaligned");let length=buffer.byteLength/Int32Array.BYTES_PER_ELEMENT;if(length<5||(length-1)%4!==0)throw new Error("barrier clock buffer has no slots");this.buffer=buffer,this.cells=new Int32Array(buffer),this.slots=(length-1)/4}static create(slots=256){if(!Number.isInteger(slots)||slots<1)throw new Error(`barrier clock needs at least one slot, got ${slots}`);return new _BarrierClock(new SharedArrayBuffer((1+slots*4)*Int32Array.BYTES_PER_ELEMENT))}tick(){return Atomics.add(this.cells,0,1)+1}claimSlot(){for(let slot=0;slot<this.slots;slot++)if(Atomics.compareExchange(this.cells,this.index(slot,3),0,1)===0)return Atomics.store(this.cells,this.index(slot,0),0),Atomics.store(this.cells,this.index(slot,1),0),Atomics.store(this.cells,this.index(slot,2),0),slot}releaseSlot(slot){Atomics.store(this.cells,this.index(slot,3),0)}recordAck(slot){let tick=this.tick();return Atomics.store(this.cells,this.index(slot,0),tick),Atomics.notify(this.cells,this.index(slot,0)),tick}recordInvoke(slot){let ackTick=Atomics.load(this.cells,this.index(slot,0)),invokeTick=this.tick();return Atomics.store(this.cells,this.index(slot,1),invokeTick),Atomics.add(this.cells,this.index(slot,2),1),{ackTick:ackTick===0?null:ackTick,invokeTick}}waitForAck(slot,timeoutMs=Number.POSITIVE_INFINITY){let at=this.index(slot,0);for(;Atomics.load(this.cells,at)===0;)if(Atomics.wait(this.cells,at,0,timeoutMs)==="timed-out")return"timed-out";return"acked"}read(slot){let ackTick=Atomics.load(this.cells,this.index(slot,0)),invokeTick=Atomics.load(this.cells,this.index(slot,1));return{ackTick:ackTick===0?null:ackTick,invokeTick:invokeTick===0?null:invokeTick,invocations:Atomics.load(this.cells,this.index(slot,2))}}index(slot,field){if(!Number.isInteger(slot)||slot<0||slot>=this.slots)throw new Error(`no barrier slot ${slot}`);return 1+slot*4+field}};function proofIsOrdered(proof){return proof.ackTick!==null&&proof.ackTick<proof.invokeTick}import{randomUUID}from"node:crypto";import{existsSync,readdirSync,rmSync,watch}from"node:fs";import{writeFile as writeFile2}from"node:fs/promises";import{join as join2}from"node:path";var PREFIX="SENPI_TOOL_CHILD_TEST_",TEST_CONTROL_DIR_ENV=`${PREFIX}CONTROL_DIR`;function readTestControls(env){let dir=env[TEST_CONTROL_DIR_ENV],value=name=>{let raw=env[`${PREFIX}${name}`];return raw===void 0||raw===""||raw==="0"?void 0:raw};return{enabled:name=>value(name)!==void 0,value,released:file=>dir===void 0?Promise.reject(new Error(`${TEST_CONTROL_DIR_ENV} is not set`)):waitForFile(dir,file),watchPrefix:(prefix,onFile)=>dir===void 0?{ready:Promise.reject(new Error(`${TEST_CONTROL_DIR_ENV} is not set`)),close:()=>{}}:watchPrefix(dir,prefix,onFile)}}function watchPrefix(dir,prefix,onFile){let seen=new Set,report=name=>{!name.startsWith(prefix)||seen.has(name)||(seen.add(name),onFile(name))},sentinel=`.watch-${process.pid}-${randomUUID()}`,resend,live=!1,markReady=()=>{},failReady=()=>{},ready=new Promise((resolve,reject)=>{markReady=resolve,failReady=reject}),watcher=watch(dir,(_event,name)=>{if(name!==null){if(name===sentinel){live=!0,clearTimeout(resend),rmSync(join2(dir,sentinel),{force:!0});for(let existing of readdirSync(dir))report(existing);markReady();return}existsSync(join2(dir,name))&&report(name)}}),touch=()=>{live||writeFile2(join2(dir,sentinel),String(Date.now())).then(()=>{live||(resend=setTimeout(touch,50))},error=>failReady(error))};return touch(),{ready,close:()=>{live=!0,clearTimeout(resend),watcher.close(),rmSync(join2(dir,sentinel),{force:!0})}}}function waitForFile(dir,file){let path=join2(dir,file),sentinel=join2(dir,`.watch-${process.pid}-${randomUUID()}`);return new Promise((resolve,reject)=>{let done=!1,resend,finish=()=>{done||(done=!0,clearTimeout(resend),watcher.close(),rmSync(sentinel,{force:!0}),resolve())},watcher=watch(dir,(_event,name)=>{if(existsSync(path)){finish();return}name!==null&&join2(dir,name)===sentinel&&clearTimeout(resend)}),touch=()=>{done||writeFile2(sentinel,String(Date.now())).then(()=>{done||(existsSync(path)?finish():resend=setTimeout(touch,50))},error=>{done||(done=!0,watcher.close(),reject(error))})};touch()})}var WindowsJobError=class extends Error{constructor(step,win32Error){super(`tool_child_job_assign_failed: ${step} failed (win32 error ${win32Error})`),this.code="tool_child_job_assign_failed",this.name="WindowsJobError",this.win32Error=win32Error}};function extendedLimitInformation(){let info=new Uint8Array(144);return new DataView(info.buffer).setUint32(16,10240,!0),info}var WindowsJob=class _WindowsJob{constructor(kernel,handle){this.closed=!1,this.kernel=kernel,this.handle=handle}static create(kernel){let handle=kernel.CreateJobObjectW(null,null);if(!handle)throw new WindowsJobError("CreateJobObjectW",kernel.GetLastError());let info=extendedLimitInformation();if(kernel.SetInformationJobObject(handle,9,info,info.byteLength)===0){let error=kernel.GetLastError();throw kernel.CloseHandle(handle),new WindowsJobError("SetInformationJobObject",error)}return new _WindowsJob(kernel,handle)}assign(pid){let process2=this.kernel.OpenProcess(4353,0,pid);if(!process2)throw new WindowsJobError("OpenProcess",this.kernel.GetLastError());try{if(this.kernel.AssignProcessToJobObject(this.handle,process2)===0)throw new WindowsJobError("AssignProcessToJobObject",this.kernel.GetLastError());let inJob=new Uint8Array(4);if(this.kernel.IsProcessInJob(process2,this.handle,inJob)===0||new DataView(inJob.buffer).getInt32(0,!0)===0)throw new WindowsJobError("IsProcessInJob",this.kernel.GetLastError())}finally{this.kernel.CloseHandle(process2)}}memberPids(){let list=new Uint8Array(8200);if(this.kernel.QueryInformationJobObject(this.handle,3,list,list.byteLength,null)===0)return[];let view=new DataView(list.buffer),count=Math.min(view.getUint32(4,!0),1024);return Array.from({length:count},(_,index)=>Number(view.getBigUint64(8+index*8,!0)))}terminate(exitCode=1){this.closed||this.kernel.TerminateJobObject(this.handle,exitCode)}close(){this.closed||(this.closed=!0,this.kernel.CloseHandle(this.handle))}};function loadKernel32Job(){if(process.platform!=="win32")return;let ffi=process.getBuiltinModule("bun:ffi");if(ffi===void 0)return;let lib=ffi.dlopen("kernel32.dll",{CreateJobObjectW:{args:["ptr","ptr"],returns:"ptr"},SetInformationJobObject:{args:["ptr","i32","ptr","u32"],returns:"i32"},QueryInformationJobObject:{args:["ptr","i32","ptr","u32","ptr"],returns:"i32"},OpenProcess:{args:["u32","i32","u32"],returns:"ptr"},AssignProcessToJobObject:{args:["ptr","ptr"],returns:"i32"},IsProcessInJob:{args:["ptr","ptr","ptr"],returns:"i32"},TerminateJobObject:{args:["ptr","u32"],returns:"i32"},CloseHandle:{args:["ptr"],returns:"i32"},GetLastError:{args:[],returns:"u32"},GetCurrentProcess:{args:[],returns:"ptr"}}),call=(name,...args)=>{let fn=lib.symbols[name];if(fn===void 0)throw new Error(`kernel32 has no ${name}`);return fn(...args.map(arg=>arg instanceof Uint8Array?ffi.ptr(arg):arg))};return{CreateJobObjectW:()=>call("CreateJobObjectW",null,null),SetInformationJobObject:(job,infoClass,info,length)=>Number(call("SetInformationJobObject",job,infoClass,info,length)),QueryInformationJobObject:(job,infoClass,info,length)=>Number(call("QueryInformationJobObject",job,infoClass,info,length,null)),OpenProcess:(access,inherit,pid)=>call("OpenProcess",access,inherit,pid),AssignProcessToJobObject:(job,process2)=>Number(call("AssignProcessToJobObject",job,process2)),IsProcessInJob:(process2,job,result)=>Number(call("IsProcessInJob",process2,job,result)),TerminateJobObject:(job,exitCode)=>Number(call("TerminateJobObject",job,exitCode)),CloseHandle:handle=>Number(call("CloseHandle",handle)),GetLastError:()=>Number(call("GetLastError")),GetCurrentProcess:()=>call("GetCurrentProcess")}}var defaultInJob;function currentProcessInJob(kernel){if(kernel!==void 0)return queryInJob(kernel);if(defaultInJob===void 0){let loaded=loadKernel32Job();defaultInJob=loaded===void 0?!1:queryInJob(loaded)}return defaultInJob}function queryInJob(kernel){let result=new Uint8Array(4);return kernel.IsProcessInJob(kernel.GetCurrentProcess(),null,result)!==0&&new DataView(result.buffer).getInt32(0,!0)!==0}var FINISHED_SLOT_STATES=256,LaunchGateError=class extends Error{constructor(code,message){super(`${code}: ${message}`),this.name="LaunchGateError",this.code=code}},LaunchGate=class{#channel;#slots=new Map;#finished=new Map;#controls;#next=0;constructor(channel,clock=BarrierClock.create(),env=process.env,creatorInJob=currentProcessInJob){this.#channel=channel,this.clock=clock,this.#controls=readTestControls(env),this.#creatorInJob=creatorInJob}#creatorInJob;started(launchId,pid,breakaway=!1){this.#slots.has(launchId)&&this.#channel.send({type:"command_started",launchId,pid,startedAtMs:Date.now(),creatorInJob:this.#creatorInJob(),breakaway})}get skipsAckWait(){return this.#controls.enabled("SKIP_ACK_WAIT")}request(request){let slot=this.clock.claimSlot();if(slot===void 0)throw new LaunchGateError("tool_child_launch_capacity",`${this.clock.slots} launches in flight`);let launchId=`L${process.pid}-${++this.#next}`,ack,acked=new Promise(resolve=>{ack=resolve});return this.#slots.set(launchId,{slot,ack,lifetime:request.lifetime,request}),this.#channel.send({type:"launch",launchId,...request}),this.#controls.enabled("STOP_AFTER_SPAWN")&&process.kill(process.pid,"SIGSTOP"),{launchId,slot,acked}}adopt(launchId,slot,request){this.#slots.set(launchId,{slot,ack:()=>{},lifetime:request.lifetime,request}),this.#channel.send({type:"launch",launchId,...request})}handleWorker(message){message.kind==="launch"?this.adopt(message.launchId,message.slot,message.request):message.kind==="invoking"?this.invoking(message.launchId):message.kind==="started"?this.started(message.launchId,message.pid,message.breakaway):this.done(message.launchId,{...message.exitCode===void 0?{}:{exitCode:message.exitCode},...message.signal===void 0?{}:{signal:message.signal}})}handle(message){if(message.type==="launch_ack"){let entry=this.#slots.get(message.launchId);return entry!==void 0&&(this.clock.recordAck(entry.slot),entry.ack()),!0}if(message.type==="slot_query"){let entry=this.#slots.get(message.launchId),state=entry===void 0?this.#finished.get(message.launchId)??{ackTick:null,invokeTick:null,invocations:0}:this.clock.read(entry.slot);return this.#channel.send({type:"slot_state",launchId:message.launchId,...state}),!0}return!1}invokeNow(launchId){let entry=this.#slots.get(launchId);if(entry===void 0)throw new LaunchGateError("tool_child_launch_unknown",launchId);this.clock.recordInvoke(entry.slot),this.invoking(launchId)}invoking(launchId){let entry=this.#slots.get(launchId);if(entry===void 0)return;let state=this.clock.read(entry.slot),invokeTick=state.invokeTick;if(invokeTick===null)return;let send=()=>this.#channel.send({type:"launch_proof",launchId,ackTick:state.ackTick,invokeTick}),hold=this.#controls.value("HOLD_LAUNCH_PROOF");if(hold!==void 0&&(hold!=="detached"||entry.lifetime==="detached")){this.#controls.released(`release-proof-${launchId}`).then(send);return}send()}endJobGroups(jobId){let ended=[];for(let[launchId,{request}]of this.#slots)if(!(request.jobId!==jobId||request.lifetime!=="owned"||request.containment!=="own-group")&&!(request.pgid===void 0||request.pgid<=1))try{process.kill(-request.pgid,"SIGKILL"),ended.push({launchId,pgid:request.pgid})}catch{}for(let{launchId}of ended)this.done(launchId,{signal:"SIGKILL"});return ended.map(({pgid})=>pgid)}done(launchId,outcome){let entry=this.#slots.get(launchId);entry!==void 0&&(this.#slots.delete(launchId),this.#finished.set(launchId,this.clock.read(entry.slot)),this.#finished.size>FINISHED_SLOT_STATES&&this.#finished.delete(this.#finished.keys().next().value??""),this.clock.releaseSlot(entry.slot),this.#channel.send({type:"launch_done",launchId,...outcome.exitCode===void 0?{}:{exitCode:outcome.exitCode},...outcome.signal===void 0?{}:{signal:outcome.signal}}))}};import{mkdir as mkdir3,readdir as readdir2,readFile as readFile3,rm as rm4,writeFile as writeFile3}from"node:fs/promises";import{dirname as dirname3,join as join4}from"node:path";import{appendFile,mkdir as mkdir2,open,readFile as readFile2,rm as rm3}from"node:fs/promises";import{dirname as dirname2,join as join3}from"node:path";function generationLedgerPath(dir,generation){return join3(dir,`g${generation}`,"descendants.ledger")}var DescendantsLedger=class{#path;#durableDir=!1;#entries=new Map;constructor(path){this.#path=path}get path(){return this.#path}entries(){return[...this.#entries.values()]}async append(entry){if(!this.#durableDir){let directory=dirname2(this.#path);await mkdir2(directory,{recursive:!0,mode:448}),await(await open(this.#path,"a",384)).close(),await syncDirectory(directory),await syncDirectory(dirname2(directory)),this.#durableDir=!0}let handle=await open(this.#path,"a",384);try{await handle.write(`${JSON.stringify(entry)}
|
|
3
3
|
`),await handle.datasync()}finally{await handle.close()}this.#entries.set(entry.launchId,entry)}static async readPersisted(path){let text;try{text=await readFile2(path,"utf8")}catch{return[]}let entries=[];for(let line of text.split(`
|
|
4
4
|
`))if(line.trim()!=="")try{entries.push(JSON.parse(line))}catch{}return entries}async remove(){this.#entries.clear(),await rm3(this.#path,{force:!0})}};async function syncDirectory(directory){if(process.platform==="win32")return;let handle=await open(directory,"r");try{await handle.sync()}finally{await handle.close()}}var LaunchLog=class{#path;#chain=Promise.resolve();#failedWrites=0;constructor(path){this.#path=path}get path(){return this.#path}get failedWrites(){return this.#failedWrites}settled(){return this.#chain}record(record){let line=`${JSON.stringify({atMs:Date.now(),...record})}
|
|
5
5
|
`;return this.#chain=this.#chain.then(async()=>{try{await mkdir2(dirname2(this.#path),{recursive:!0,mode:448}),await appendFile(this.#path,line,{mode:384})}catch{this.#failedWrites+=1}}),this.#chain}};import{execFile}from"node:child_process";async function classifyGroup(group,ownGroup,probes){if(group.lifetime==="detached")return"detached";if(ownGroup===void 0||group.pgid===ownGroup||group.pgid<=1)return"own-group";if(!probes.groupHasMembers(group.pgid))return"empty";if(!probes.isAlive(group.pgid))return"sweep";let observed=await probes.startOf(group.pgid).catch(()=>{}),recorded=group.leaderStart===void 0?void 0:processStartTimeMs(group.leaderStart);return sameProcessStartMs(recorded,observed===void 0?void 0:processStartTimeMs(observed))?"sweep":"leader-reused"}async function sweepGroups(groups,options,probes=defaultGroupProbes){let ownGroup=await probes.ownGroup(),verdicts=await Promise.all(groups.map(async group=>({group,verdict:await classifyGroup(group,ownGroup,probes)}))),targets=verdicts.filter(entry=>entry.verdict==="sweep").map(entry=>entry.group.pgid);for(let pgid of targets)probes.signalGroup(pgid,"SIGTERM");let remaining=await drain(targets,options.graceMs,options.pollMs??25,probes);for(let pgid of remaining)probes.signalGroup(pgid,"SIGKILL");return remaining=await drain(remaining,options.killWaitMs,options.pollMs??25,probes),verdicts.map(({group,verdict})=>({launchId:group.launchId,pgid:group.pgid,verdict,...verdict==="sweep"?{survived:remaining.includes(group.pgid)}:{}}))}async function drain(pgids,budgetMs,pollMs,probes){let remaining=pgids.filter(pgid=>probes.groupHasMembers(pgid));for(let waited=0;remaining.length>0&&waited<budgetMs;waited+=pollMs)await probes.wait(pollMs),remaining=remaining.filter(pgid=>probes.groupHasMembers(pgid));return remaining}function processGroupOf(pid){return process.platform==="win32"?Promise.resolve(void 0):new Promise(resolve=>{execFile("ps",["-o","pgid=","-p",String(pid)],{timeout:2e3},(error,stdout)=>{let text=String(stdout).trim();resolve(error===null&&/^\d+$/u.test(text)?Number(text):void 0)})})}function groupHasMembers(pgid){try{return process.kill(-pgid,0),!0}catch(error){return error instanceof Error&&"code"in error&&error.code==="EPERM"}}var defaultGroupProbes={groupHasMembers,isAlive:processIsLive,startOf:pid=>readProcessStartTime(pid),ownGroup:()=>processGroupOf(process.pid),signalGroup:(pgid,signal)=>{try{process.kill(-pgid,signal)}catch{}},wait:ms=>new Promise(resolve=>setTimeout(resolve,ms))};var SWEEP_TIMING={graceMs:500,killWaitMs:1e3};async function sweepLedger(path){let groups=(await DescendantsLedger.readPersisted(path)).flatMap(entry=>entry.pgid===void 0?[]:[{launchId:entry.launchId,pgid:entry.pgid,leaderStart:entry.leaderStart,lifetime:entry.lifetime}]);return groups.length===0?[]:sweepGroups(groups,SWEEP_TIMING)}var GENERATION_CHILD_FILE="child.pid";async function recordGenerationChild(dir,generation,pid){let generationDir=dirname3(generationLedgerPath(dir,generation));await mkdir3(generationDir,{recursive:!0,mode:448}),await writeFile3(join4(generationDir,GENERATION_CHILD_FILE),String(pid),{mode:384})}async function childAlive(file){let pid=Number(await readFile3(file,"utf8").catch(()=>""));if(!Number.isSafeInteger(pid)||pid<=1)return!1;try{return process.kill(pid,0),!0}catch(error){return Reflect.get(Object(error),"code")==="EPERM"}}async function sweepStaleGenerations(dir){let names=await readdir2(dir).catch(()=>[]),outcomes=[];for(let name of names){let generation=/^g(\d+)$/.exec(name)?.[1];generation!==void 0&&(await childAlive(join4(dir,name,GENERATION_CHILD_FILE))||(outcomes.push(...await sweepLedger(generationLedgerPath(dir,Number(generation)))),await rm4(join4(dir,name),{recursive:!0,force:!0})))}return outcomes}var TOOL_CHILD_GENERATION_ENV="SENPI_TOOL_CHILD_GENERATION",TOOL_CHILD_PROTOCOL_VERSION=2,NEVER_CANCELLED=new AbortController().signal;async function runToolChild(options){let env={...options.env??process.env},exit=options.exit??(code=>process.exit(code)),generation=Number(env[TOOL_CHILD_GENERATION_ENV]);(!Number.isSafeInteger(generation)||generation<0)&&(process.stderr.write(`tool-child: invalid generation
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
2
|
import{getAgentDir}from"./chunk-7YO7VQZW.js";import{processStartTimeMs,readProcessIdentity,sameProcessStartMs}from"./chunk-7ASLQ243.js";import{existsSync,realpathSync}from"node:fs";import{basename,dirname,join,resolve}from"node:path";function canonicalSessionPath(path){let absolutePath=resolve(path),missing=[],existing=absolutePath;for(;!existsSync(existing);){let parent=dirname(existing);if(parent===existing)return absolutePath;missing.unshift(basename(existing)),existing=parent}try{return join(realpathSync(existing),...missing)}catch{return absolutePath}}function sessionDirectoryRemoved(sessionFile){return!existsSync(dirname(sessionFile))}import{createHash}from"node:crypto";import{basename as basename2,dirname as dirname2,join as join2,win32}from"node:path";function canonicalEndpointPath(socket,platform=process.platform){return platform==="win32"?win32.normalize(socket).toLowerCase():socket.startsWith("\0")?socket:join2(canonicalSessionPath(dirname2(socket)),basename2(socket))}function daemonDirectoryName(socket,platform=process.platform){return directoryNameOf(canonicalEndpointPath(socket,platform))}function sameEndpoint(socket,other){return socket===other||canonicalEndpointPath(socket)===canonicalEndpointPath(other)}function socketNamesDirectory(socket,name){return daemonDirectoryName(socket)===name||process.platform!=="win32"&&directoryNameOf(socket)===name}function directoryNameOf(endpoint){return createHash("sha256").update(endpoint,"utf8").digest("hex").slice(0,16)}function shardKey(kind,ownerId){return createHash("sha256").update(`${kind}:${ownerId}`,"utf8").digest("hex").slice(0,16)}function shardSocketPathForKey(root,kind,key){return join2(root,`${kind}-${key}.sock`)}function shardSocketPath(root,kind,ownerId){return shardSocketPathForKey(root,kind,shardKey(kind,ownerId))}var SHARD_SOCKET_NAME=/^(p|i)-([0-9a-f]{16})\.sock$/;function parseShardSocket(socket){let match=SHARD_SOCKET_NAME.exec(basename2(socket)),key=match?.[2];return key===void 0?null:{kind:match?.[1]==="i"?"i":"p",key}}import{chmod,mkdir}from"node:fs/promises";import{join as join3}from"node:path";var DIRECTORY_MODE=448;function generationPaths(paths,instanceId){let dir=join3(paths.generationsDir,instanceId);return{dir,relativeDir:`generations/${instanceId}`,pidFile:join3(dir,"host.pid"),settingsFile:join3(dir,"settings.json"),scratchDir:join3(dir,"scratch"),childPidFile:join3(dir,"host-child.pid"),stopIntentFile:join3(dir,"stop-intent.json"),drainRequestFile:join3(dir,"drain-request.json"),childDrainRequestFile:join3(dir,"host-drain-request.json"),drainWatchFile:join3(dir,"drain-watch.json"),stalledFile:join3(dir,"host-stalled.json"),aliveFile:join3(dir,"host-alive.json"),stopProgressFile:join3(dir,"stop-progress.json")}}var HostDaemonStateError=class extends Error{constructor(path,cause){super(`RPC daemon state directory ${path} is not usable: ${cause instanceof Error?cause.message:cause}`,{cause}),this.name="HostDaemonStateError",this.path=path}};async function createGenerationDirectory(generation){try{await mkdir(generation.scratchDir,{recursive:!0,mode:DIRECTORY_MODE}),await chmod(generation.dir,DIRECTORY_MODE)}catch(cause){throw new HostDaemonStateError(generation.dir,cause)}}import{randomUUID}from"node:crypto";import{chmod as chmod2,link,mkdir as mkdir2,readFile,rename,rm,writeFile}from"node:fs/promises";import{basename as basename3,join as join4}from"node:path";var HOST_DAEMON_LAYOUT=2,HOST_DAEMON_DIR_ENV="SENPI_RPC_HOST_DAEMON_DIR",ENDPOINT_REGISTRY_VERSION=1,DIRECTORY_MODE2=448,HOST_STATE_FILE_MODE=384;function createHostDaemonPaths(target){let flatDir=join4(target.agentDir??getAgentDir(),"rpc-host-daemon");return{socket:target.socket,flatDir,layoutMarker:join4(flatDir,"layout.json"),legacyPidFile:join4(flatDir,"host.pid"),...hostDaemonDirectoryPaths(join4(flatDir,daemonDirectoryName(target.socket)))}}function hostDaemonDirectoryPaths(dir){return{dir,endpointFile:join4(dir,"endpoint.json"),pointerFile:join4(dir,"host.pid"),lockFile:join4(dir,"daemon.lock"),settingsFile:join4(dir,"settings.json"),stderrLog:join4(dir,"stderr.log"),generationsDir:join4(dir,"generations"),reservationsDir:join4(dir,"reservations")}}async function createDaemonDirectories(paths,identity={}){try{await mkdir2(paths.flatDir,{recursive:!0,mode:DIRECTORY_MODE2}),await makePrivateDirectory(paths.dir),await Promise.all([makePrivateDirectory(paths.generationsDir),makePrivateDirectory(paths.reservationsDir),writeFile(paths.layoutMarker,`${JSON.stringify({layout:HOST_DAEMON_LAYOUT,dir:basename3(paths.dir)})}
|
|
3
3
|
`,{mode:HOST_STATE_FILE_MODE})])}catch(cause){throw new HostDaemonStateError(paths.dir,cause)}await ensureEndpointIdentity(paths,paths.socket,identity)}async function makePrivateDirectory(directory){await mkdir2(directory,{recursive:!0,mode:DIRECTORY_MODE2})===void 0&&await chmod2(directory,DIRECTORY_MODE2)}async function ensureEndpointIdentity(paths,socket,options={}){let temporary=`${paths.endpointFile}.${process.pid}-${randomUUID()}.tmp`;try{await mkdir2(paths.dir,{recursive:!0,mode:DIRECTORY_MODE2});let record=`${JSON.stringify({layout:HOST_DAEMON_LAYOUT,registry_version:ENDPOINT_REGISTRY_VERSION,endpoint_kind:options.kind??"rpc_host",socket,created_at:new Date().toISOString()})}
|
|
4
|
-
`;await writeFile(temporary,record,{mode:HOST_STATE_FILE_MODE,flag:"wx"}),!await link(temporary,paths.endpointFile).then(()=>!0,cause=>isErrorCode(cause,"EEXIST")?!1:createExclusively(paths.endpointFile,record))&&options.repair===!0&&!await namesThisDirectory(paths)&&await rename(temporary,paths.endpointFile)}catch(cause){throw new HostDaemonStateError(paths.endpointFile,cause)}finally{await rm(temporary,{force:!0})}}async function createExclusively(path,content){try{return await writeFile(path,content,{mode:HOST_STATE_FILE_MODE,flag:"wx"}),!0}catch(cause){if(isErrorCode(cause,"EEXIST"))return!1;throw cause}}function isErrorCode(cause,code){return cause instanceof Error&&"code"in cause&&cause.code===code}async function namesThisDirectory(paths){let record;try{record=JSON.parse(await readFile(paths.endpointFile,"utf8"))}catch{return!1}return typeof record!="object"||record===null||!("socket"in record)?!1:typeof record.socket=="string"&&record.socket!==""&&socketNamesDirectory(record.socket,basename3(paths.dir))}
|
|
5
|
-
`}var MAX_RPC_LINE_CHARACTERS=16*1024*1024;function attachJsonlLineReader(stream,onLine,options={}){let decoder=new StringDecoder("utf8"),maxLineLength=options.maxLineLength??Number.POSITIVE_INFINITY;if(!(maxLineLength>0))throw new Error("maxLineLength must be greater than zero.");let buffer="",discardingOversizedLine=!1,consume=text=>{let offset=0;for(;offset<text.length;){if(discardingOversizedLine){let newlineIndex2=text.indexOf(`
|
|
6
|
-
`,offset);if(newlineIndex2===-1)return;discardingOversizedLine=!1,offset=newlineIndex2+1;continue}let newlineIndex=text.indexOf(`
|
|
7
|
-
`,offset);if(newlineIndex===-1){let remainder=text.slice(offset);buffer.length+remainder.length>maxLineLength?(buffer="",discardingOversizedLine=!0,options.onOversizedLine?.()):buffer+=remainder;return}let segment=text.slice(offset,newlineIndex);if(buffer.length+segment.length>maxLineLength)options.onOversizedLine?.();else{let line=buffer+segment;onLine(line.endsWith("\r")?line.slice(0,-1):line)}buffer="",offset=newlineIndex+1}},onData=chunk=>{consume(typeof chunk=="string"?chunk:decoder.write(chunk))},onEnd=()=>{consume(decoder.end()),!discardingOversizedLine&&buffer.length>0&&onLine(buffer.endsWith("\r")?buffer.slice(0,-1):buffer),buffer="",discardingOversizedLine=!1};return stream.on("data",onData),stream.on("end",onEnd),()=>{stream.off("data",onData),stream.off("end",onEnd)}}import{randomUUID as randomUUID2}from"node:crypto";import{readFile as readFile2,rename as rename2,rm as rm2,writeFile as writeFile2}from"node:fs/promises";async function writeJsonAtomic(path,content){let temporary=`${path}.${process.pid}.${randomUUID2().slice(0,8)}.tmp`;try{await writeFile2(temporary,`${JSON.stringify(content)}
|
|
4
|
+
`;await writeFile(temporary,record,{mode:HOST_STATE_FILE_MODE,flag:"wx"}),!await link(temporary,paths.endpointFile).then(()=>!0,cause=>isErrorCode(cause,"EEXIST")?!1:createExclusively(paths.endpointFile,record))&&options.repair===!0&&!await namesThisDirectory(paths)&&await rename(temporary,paths.endpointFile)}catch(cause){throw new HostDaemonStateError(paths.endpointFile,cause)}finally{await rm(temporary,{force:!0})}}async function createExclusively(path,content){try{return await writeFile(path,content,{mode:HOST_STATE_FILE_MODE,flag:"wx"}),!0}catch(cause){if(isErrorCode(cause,"EEXIST"))return!1;throw cause}}function isErrorCode(cause,code){return cause instanceof Error&&"code"in cause&&cause.code===code}async function namesThisDirectory(paths){let record;try{record=JSON.parse(await readFile(paths.endpointFile,"utf8"))}catch{return!1}return typeof record!="object"||record===null||!("socket"in record)?!1:typeof record.socket=="string"&&record.socket!==""&&socketNamesDirectory(record.socket,basename3(paths.dir))}import{randomUUID as randomUUID2}from"node:crypto";import{readFile as readFile2,rename as rename2,rm as rm2,writeFile as writeFile2}from"node:fs/promises";async function writeJsonAtomic(path,content){let temporary=`${path}.${process.pid}.${randomUUID2().slice(0,8)}.tmp`;try{await writeFile2(temporary,`${JSON.stringify(content)}
|
|
8
5
|
`,{mode:384}),await rename2(temporary,path)}catch(cause){throw await rm2(temporary,{force:!0}).catch(()=>{}),cause}}async function readJsonObject(path){try{let value=JSON.parse(await readFile2(path,"utf8"));return typeof value=="object"&&value!==null&&!Array.isArray(value)?{...value}:void 0}catch{return}}function ageOf(at,now){if(typeof at!="string")return;let parsed=Date.parse(at);return Number.isNaN(parsed)?void 0:now-parsed}import{readFile as readFile3,writeFile as writeFile3}from"node:fs/promises";import{join as join5}from"node:path";async function readHostOwner(generationDir){let record=await readJsonObject(join5(generationDir,"owner.json"));if(record?.owner===null)return null;if(!isRecord(record?.owner))return;let{pid,startTime}=record.owner;return typeof pid=="number"&&Number.isSafeInteger(pid)&&pid>0&&typeof startTime=="string"&&startTime.length>0?{pid,startTime}:void 0}function writeHostOwner(generationDir,owner){return writeJsonAtomic(join5(generationDir,"owner.json"),{owner})}function sameHostOwner(a,b){if(a==null||b==null||a.pid!==b.pid)return!1;let started=processStartTimeMs(a.startTime);return started!==void 0&&started===processStartTimeMs(b.startTime)}async function callerHostOwner(){let observed=await readProcessIdentity(process.pid,process.platform,void 0,void 0,"UTC");if(observed.kind!=="present"||processStartTimeMs(observed.identity)===void 0)throw new Error("cannot establish RPC host owner OS start identity");return{pid:process.pid,startTime:observed.identity}}async function hostOwnerGone(owner){let observed=await readProcessIdentity(owner.pid,process.platform,1e3,void 0,"UTC");return observed.kind==="absent"||observed.kind==="present"&&processStartTimeMs(observed.identity)!==void 0&&processStartTimeMs(owner.startTime)!==void 0&&!sameProcessStartMs(processStartTimeMs(observed.identity),processStartTimeMs(owner.startTime))}async function claimHostOwner(generationDir){let previous=await readHostOwner(generationDir);if(previous===void 0)throw new Error("RPC host does not support owner lifetime registration");let next=await callerHostOwner();if(!sameHostOwner(previous,next)){if(previous!==null&&!await hostOwnerGone(previous))throw new Error("RPC host lifetime owner is still alive or its identity is unknown");await writeHostOwner(generationDir,next)}}async function writeHostSettings(paths,settings){let generation=generationPaths(paths,settings.instanceId);await createGenerationDirectory(generation),await writeStateFile(paths.settingsFile,settings),await writeStateFile(generation.settingsFile,settings)}async function readHostSettings(paths){let parsed=parseJson(await readFileOrUndefined(paths.settingsFile));if(parsed)return{...typeof parsed.coldStart=="string"&&{coldStart:parsed.coldStart},...typeof parsed.idleExitMs=="number"&&{idleExitMs:parsed.idleExitMs}}}async function writeStateFile(path,content){try{await writeFile3(path,`${JSON.stringify(content)}
|
|
9
|
-
`,{mode:HOST_STATE_FILE_MODE})}catch(cause){throw new HostDaemonStateError(path,cause)}}async function readFileOrUndefined(path){try{return await readFile3(path,"utf8")}catch(error){if(isNodeErrorCode(error,"ENOENT")||isNodeErrorCode(error,"ENOTDIR"))return;throw error}}function parseJson(text){if(text!==void 0)try{let parsed=JSON.parse(text);return isRecord(parsed)?parsed:void 0}catch{return}}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}
|
|
6
|
+
`,{mode:HOST_STATE_FILE_MODE})}catch(cause){throw new HostDaemonStateError(path,cause)}}async function readFileOrUndefined(path){try{return await readFile3(path,"utf8")}catch(error){if(isNodeErrorCode(error,"ENOENT")||isNodeErrorCode(error,"ENOTDIR"))return;throw error}}function parseJson(text){if(text!==void 0)try{let parsed=JSON.parse(text);return isRecord(parsed)?parsed:void 0}catch{return}}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}export{canonicalSessionPath,sessionDirectoryRemoved,canonicalEndpointPath,daemonDirectoryName,sameEndpoint,socketNamesDirectory,shardKey,shardSocketPathForKey,shardSocketPath,parseShardSocket,generationPaths,createGenerationDirectory,HOST_DAEMON_LAYOUT,HOST_DAEMON_DIR_ENV,ENDPOINT_REGISTRY_VERSION,HOST_STATE_FILE_MODE,createHostDaemonPaths,hostDaemonDirectoryPaths,createDaemonDirectories,ensureEndpointIdentity,writeJsonAtomic,readJsonObject,ageOf,readHostOwner,writeHostOwner,sameHostOwner,callerHostOwner,hostOwnerGone,claimHostOwner,writeHostSettings,readHostSettings,writeStateFile,readFileOrUndefined,parseJson,isRecord};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
+
import{ProcessIdentityUnreadableError,processMatchesPidFile,readProcessStartTime}from"./chunk-7ASLQ243.js";var HOST_GENERATION_ENV="SENPI_RPC_HOST_GENERATION",HOST_INSTANCE_ID_ENV="SENPI_RPC_HOST_INSTANCE_ID";import{StringDecoder}from"node:string_decoder";function serializeJsonLine(value){return`${JSON.stringify(value)}
|
|
3
|
+
`}var MAX_RPC_LINE_CHARACTERS=16*1024*1024;function attachJsonlLineReader(stream,onLine,options={}){let decoder=new StringDecoder("utf8"),maxLineLength=options.maxLineLength??Number.POSITIVE_INFINITY;if(!(maxLineLength>0))throw new Error("maxLineLength must be greater than zero.");let buffer="",discardingOversizedLine=!1,consume=text=>{let offset=0;for(;offset<text.length;){if(discardingOversizedLine){let newlineIndex2=text.indexOf(`
|
|
4
|
+
`,offset);if(newlineIndex2===-1)return;discardingOversizedLine=!1,offset=newlineIndex2+1;continue}let newlineIndex=text.indexOf(`
|
|
5
|
+
`,offset);if(newlineIndex===-1){let remainder=text.slice(offset);buffer.length+remainder.length>maxLineLength?(buffer="",discardingOversizedLine=!0,options.onOversizedLine?.()):buffer+=remainder;return}let segment=text.slice(offset,newlineIndex);if(buffer.length+segment.length>maxLineLength)options.onOversizedLine?.();else{let line=buffer+segment;onLine(line.endsWith("\r")?line.slice(0,-1):line)}buffer="",offset=newlineIndex+1}},onData=chunk=>{consume(typeof chunk=="string"?chunk:decoder.write(chunk))},onEnd=()=>{consume(decoder.end()),!discardingOversizedLine&&buffer.length>0&&onLine(buffer.endsWith("\r")?buffer.slice(0,-1):buffer),buffer="",discardingOversizedLine=!1};return stream.on("data",onData),stream.on("end",onEnd),()=>{stream.off("data",onData),stream.off("end",onEnd)}}import{createHmac,timingSafeEqual}from"node:crypto";var RPC_AUTH_CAPABILITY="socket_auth",RPC_AUTH_HEX=/^[a-f0-9]{64}$/,RPC_LAUNCH_ID=/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/;function rpcAuthProof(key,label,...parts){return createHmac("sha256",key).update([`senpi-rpc-${label}`,...parts].join(`
|
|
6
|
+
`),"utf8").digest("hex")}function rpcProofMatches(expected,received){return typeof received=="string"&&RPC_AUTH_HEX.test(received)&&timingSafeEqual(Buffer.from(expected,"hex"),Buffer.from(received,"hex"))}var RpcEndpointAuthError=class extends Error{constructor(code,detail,options){super(`${code}: ${detail}`,options),this.code=code,this.name="RpcEndpointAuthError"}};function authRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)?value:void 0}import{randomBytes,randomUUID}from"node:crypto";import{constants,unlinkSync}from"node:fs";import*as fs from"node:fs/promises";import{join}from"node:path";var MAX_KEY_RECORD_BYTES=4096;async function checkRpcKeyDirectory(path,privateKeys=!1){let checked=privateKeys?path:await fs.realpath(path),problem=keyDirectoryProblem(await fs.lstat(checked),checked,privateKeys);if(problem!==void 0)throw new RpcEndpointAuthError("unsafe_key_record",`${checked} ${problem}`)}function keyDirectoryProblem(info,path,privateKeys){if(!info.isDirectory())return"is not a directory";if(info.uid!==process.getuid?.())return"is not owned by this user";let mode=info.mode&4095;return privateKeys?mode===448?void 0:`must be mode 0700 (is 0${mode.toString(8)}); run: chmod 700 ${path}`:(mode&18)===0?void 0:`is writable by group or others; run: chmod go-w ${path}`}async function checkRpcKeyParents(root){await checkRpcKeyDirectory(root),await checkRpcKeyDirectory(join(root,"rpc"))}async function readRpcKeyRecord(root,launchId){if(!RPC_LAUNCH_ID.test(launchId))throw new RpcEndpointAuthError("unsafe_key_record",`invalid launch id under ${root}`);let path=join(root,"rpc","keys",launchId);try{await checkRpcKeyParents(root),await checkRpcKeyDirectory(join(root,"rpc","keys"),!0);let handle=await fs.open(path,constants.O_RDONLY|constants.O_NOFOLLOW|constants.O_NONBLOCK);try{let info=await handle.stat();if(!info.isFile()||info.uid!==process.getuid?.()||(info.mode&4095)!==384||info.size>MAX_KEY_RECORD_BYTES)throw new RpcEndpointAuthError("unsafe_key_record",path);let record=authRecord(JSON.parse(await handle.readFile("utf8")));if(record?.version!==1||record.launch_id!==launchId||typeof record.key!="string"||!RPC_AUTH_HEX.test(record.key))throw new RpcEndpointAuthError("unsafe_key_record",path);return record}finally{await handle.close()}}catch(cause){if(cause instanceof Error&&"code"in cause&&cause.code==="ENOENT")return;throw cause instanceof RpcEndpointAuthError?cause:new RpcEndpointAuthError("unsafe_key_record",path,{cause})}}var RpcPublicEndpoint=class{constructor(agentDir,endpoint){this.key=randomBytes(32),this.launchId=randomUUID(),this.publication=Promise.withResolvers(),this.created=!1,this.exitCleanup=()=>{if(this.created)try{unlinkSync(this.recordPath)}catch(cause){cause instanceof Error&&"code"in cause&&cause.code==="ENOENT"||process.stderr.write(`senpi rpc host: could not remove own endpoint key record
|
|
7
|
+
`)}},this.agentDir=agentDir,this.endpoint=endpoint,this.recordPath=join(agentDir,"rpc","keys",this.launchId),this.ready=this.publication.promise,this.ready.catch(()=>{})}async publish(){try{await fs.mkdir(join(this.agentDir,"rpc"),{recursive:!0,mode:448}),await checkRpcKeyParents(this.agentDir);let keys=join(this.agentDir,"rpc","keys");await fs.mkdir(keys,{recursive:!0,mode:448});let directory=await fs.lstat(keys);if(!directory.isDirectory()||directory.uid!==process.getuid?.())throw new RpcEndpointAuthError("unsafe_key_record",keys);(directory.mode&4095)!==448&&await fs.chmod(keys,448),await checkRpcKeyDirectory(keys,!0);let handle=await fs.open(this.recordPath,constants.O_WRONLY|constants.O_CREAT|constants.O_EXCL|constants.O_NOFOLLOW,384);this.created=!0,process.once("exit",this.exitCleanup);try{await handle.writeFile(JSON.stringify({version:1,launch_id:this.launchId,key:this.key.toString("hex"),pid:process.pid,process_start:await readProcessStartTime(process.pid)??null,endpoint:this.endpoint,created_at:new Date().toISOString()})),await handle.sync();let info=await handle.stat();if(!info.isFile()||info.uid!==process.getuid?.()||(info.mode&4095)!==384)throw new RpcEndpointAuthError("unsafe_key_record",this.recordPath)}finally{await handle.close()}this.publication.resolve()}catch(cause){let error=new RpcEndpointAuthError("key_publication_failed",cause instanceof RpcEndpointAuthError?cause.message:this.recordPath,{cause});throw this.publication.reject(error),error}}async dispose(){process.off("exit",this.exitCleanup),this.created&&(this.created=!1,await fs.unlink(this.recordPath).catch(cause=>{if(!(cause instanceof Error&&"code"in cause&&cause.code==="ENOENT"))throw cause}))}};async function gcRpcPublicKeys(agentDir,budget={}){let keys=join(agentDir,"rpc","keys");try{await checkRpcKeyParents(agentDir),await checkRpcKeyDirectory(keys,!0)}catch(cause){if(cause instanceof RpcEndpointAuthError||cause instanceof Error&&"code"in cause&&cause.code==="ENOENT")return;throw cause}let names=await fs.readdir(keys).catch(cause=>{if(cause instanceof Error&&"code"in cause&&cause.code==="ENOENT")return[];throw cause}),examined=0;for(let name of names){if(Date.now()>=(budget.deadline??1/0)||examined>=(budget.maxRecords??1/0))break;if(RPC_LAUNCH_ID.test(name)){examined++;try{let record=await readRpcKeyRecord(agentDir,name);if(record===void 0||typeof record.pid!="number"||!Number.isSafeInteger(record.pid)||record.pid<=0||!(record.process_start===null||typeof record.process_start=="string")||await processMatchesPidFile({pid:record.pid,processStartTime:record.process_start}))continue;await fs.unlink(join(keys,name))}catch(cause){if(cause instanceof ProcessIdentityUnreadableError||cause instanceof RpcEndpointAuthError||cause instanceof Error&&"code"in cause&&cause.code==="ENOENT")continue;throw cause}}}}var OBSERVE_REQUEST_FIELD="observe",ORIGIN_HOST_INSTANCE_FIELD="origin_host_instance",OBSERVING_COMMANDS=new Set(["get_protocol_info","list_sessions","subscribe_sessions","unsubscribe_sessions","get_effect_status","get_pending_inputs","get_subscription_limits"]);function isObservingRequest(line){let request;try{request=JSON.parse(line)}catch{return!1}if(typeof request!="object"||request===null||Array.isArray(request))return!1;let record=request;return record.type==="warm"||record.type==="authenticate"?!0:record[OBSERVE_REQUEST_FIELD]===!0&&OBSERVING_COMMANDS.has(record.type)}function originHostInstance(env=process.env){let instance=env[HOST_INSTANCE_ID_ENV]?.trim();return instance||void 0}function requestOriginHostInstance(line){let request;try{request=JSON.parse(line)}catch{return}if(typeof request!="object"||request===null||Array.isArray(request))return;let origin=Reflect.get(request,ORIGIN_HOST_INSTANCE_FIELD);return typeof origin=="string"&&origin.length>0?origin:void 0}export{HOST_GENERATION_ENV,HOST_INSTANCE_ID_ENV,OBSERVE_REQUEST_FIELD,ORIGIN_HOST_INSTANCE_FIELD,isObservingRequest,originHostInstance,requestOriginHostInstance,serializeJsonLine,MAX_RPC_LINE_CHARACTERS,attachJsonlLineReader,RPC_AUTH_CAPABILITY,RPC_AUTH_HEX,RPC_LAUNCH_ID,rpcAuthProof,rpcProofMatches,RpcEndpointAuthError,authRecord,readRpcKeyRecord,RpcPublicEndpoint,gcRpcPublicKeys};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
+
import{RPC_ERROR_SESSION_NOT_ATTACHED,RPC_ERROR_UNKNOWN_COMMAND,SUMMARIZATION_MAX_DURATION_CAP_MS,SUMMARIZATION_TOTAL_BUDGET_MS,clientMessageIdentity,isAbortGraceInRange}from"./chunk-GY5MR5KR.js";import{signalSpawnedChild}from"./chunk-KLAX467B.js";import{DETACH_CONNECTION_CAPABILITY}from"./chunk-BMYNMUVZ.js";import{connectAuthenticatedRpcSocket,socketNeedsHandshake}from"./chunk-II22E3CV.js";import{ORIGIN_HOST_INSTANCE_FIELD,attachJsonlLineReader,originHostInstance,serializeJsonLine}from"./chunk-RVYSJBZ7.js";import{fuzzyFilter}from"./chunk-FDL4CF6F.js";import{readSocketSecret,resolveSocketTransportAddress,sendSocketHandshake,socketSecretPath}from"./chunk-ZGAB7WQA.js";var COMMAND_TOKEN=/^\/[A-Za-z][A-Za-z0-9:_-]*$/,MAX_SUGGESTIONS=3,UnknownCommandError=class extends Error{constructor(command,suggestions,reason){super(formatUnknownCommandMessage(command,suggestions,reason)),this.name="UnknownCommandError",this.command=command,this.suggestions=suggestions,this.reason=reason}};function formatUnknownCommandMessage(command,suggestions,reason){if(reason==="interactive_only")return`/${command} is an interactive command and cannot be sent as a prompt.`;let hint=suggestions.length===0?"":` Did you mean ${suggestions.map(name=>`/${name}`).join(", ")}?`;return`Unknown command /${command}.${hint}`}var UNKNOWN_COMMAND_CONFIRM_HINT='Resend with "unknownCommandAsText": true to send it as text.';function unknownCommandErrorFromWire(data){if(typeof data!="object"||data===null)return;let command=Reflect.get(data,"command"),suggestions=Reflect.get(data,"suggestions"),reason=Reflect.get(data,"reason");if(typeof command!="string"||!Array.isArray(suggestions)||reason!=="unknown"&&reason!=="interactive_only")return;let names=suggestions.filter(name=>typeof name=="string");return new UnknownCommandError(command,names,reason)}function commandShapedName(text){let token=text.trim().split(/\s/,1)[0]??"";return COMMAND_TOKEN.test(token)?token.slice(1):void 0}function findUnknownCommand(text,catalog){let command=commandShapedName(text);if(command===void 0||catalog.promptCommands.has(command))return;if(catalog.interactiveCommands.has(command))return new UnknownCommandError(command,[],"interactive_only");let known=[...new Set([...catalog.promptCommands,...catalog.interactiveCommands])],suggestions=fuzzyFilter(known,command,name=>name).slice(0,MAX_SUGGESTIONS);return new UnknownCommandError(command,suggestions,"unknown")}import{existsSync}from"node:fs";function getMissingSessionCwdIssue(sessionManager,fallbackCwd){let sessionFile=sessionManager.getSessionFile();if(!sessionFile)return;let sessionCwd=sessionManager.getCwd();if(!(!sessionCwd||existsSync(sessionCwd)))return{sessionFile,sessionCwd,fallbackCwd}}function formatMissingSessionCwdError(issue){let sessionFile=issue.sessionFile?`
|
|
3
|
+
Session file: ${issue.sessionFile}`:"";return`Stored session working directory does not exist: ${issue.sessionCwd}${sessionFile}
|
|
4
|
+
Current working directory: ${issue.fallbackCwd}`}function formatMissingSessionCwdPrompt(issue){return`cwd from session file does not exist
|
|
5
|
+
${issue.sessionCwd}
|
|
6
|
+
|
|
7
|
+
continue in current cwd
|
|
8
|
+
${issue.fallbackCwd}`}var MissingSessionCwdError=class extends Error{constructor(issue){super(formatMissingSessionCwdError(issue)),this.name="MissingSessionCwdError",this.issue=issue}};function assertSessionCwdExists(sessionManager,fallbackCwd){let issue=getMissingSessionCwdIssue(sessionManager,fallbackCwd);if(issue)throw new MissingSessionCwdError(issue)}import{spawn}from"node:child_process";import{createConnection}from"node:net";async function connectRpcClientSocket(path,options){if(!socketNeedsHandshake(path))return(await connectAuthenticatedRpcSocket(path,void 0,options)).socket;let secret=await readSocketSecret(socketSecretPath(path)),socket=createConnection(resolveSocketTransportAddress(path,process.platform,secret));return await new Promise((resolve,reject)=>{let cleanup=()=>{socket.off("connect",onConnect),socket.off("error",onError)},onConnect=()=>{cleanup(),resolve()},onError=error=>{cleanup(),reject(error)};socket.once("connect",onConnect),socket.once("error",onError)}),sendSocketHandshake(socket,secret),socket}var REQUEST_DEADLINE_MS=3e4;function requestDeadlineMs(command){return command.type!=="abort"||command.awaitIdle!==!0?REQUEST_DEADLINE_MS:command.graceMs===void 0?1e4+REQUEST_DEADLINE_MS:isAbortGraceInRange(command.graceMs)?command.graceMs+REQUEST_DEADLINE_MS:REQUEST_DEADLINE_MS}var PROMPT_COMPACTION_DEADLINE_MS=SUMMARIZATION_TOTAL_BUDGET_MS+SUMMARIZATION_MAX_DURATION_CAP_MS+REQUEST_DEADLINE_MS,PROMPT_ACK_MAX_WAIT_MS=PROMPT_COMPACTION_DEADLINE_MS+REQUEST_DEADLINE_MS,OPEN_AFTER_QUEUED_DEADLINE_MS=10*6e4,PROMPT_AFTER_QUEUED_DEADLINE_MS=5*6e4;function promptStalledMessage(){return`prompt was received by the host but not accepted within ${PROMPT_AFTER_QUEUED_DEADLINE_MS/6e4} minutes`}function openStalledMessage(position){let queued=typeof position=="number"?` at queue position ${position}`:"",minutes=OPEN_AFTER_QUEUED_DEADLINE_MS/6e4;return`open_session was accepted by the host${queued} but not answered within ${minutes} minutes`}var RpcRequestTimeoutError=class extends Error{constructor(message){super(message),this.code="rpc_request_timeout",this.name="RpcRequestTimeoutError"}};function armRequestDeadline(ms,message,expire){let timer=setTimeout(()=>expire(new RpcRequestTimeoutError(message())),ms);return{extend(nextMs,nextMessage){clearTimeout(timer),timer=setTimeout(()=>expire(new RpcRequestTimeoutError(nextMessage())),nextMs)},clear(){clearTimeout(timer)}}}var RpcCommandError=class extends Error{constructor(message,errorCode,errorData){super(message),this.name="RpcCommandError",this.errorCode=errorCode,this.errorData=errorData}};function isProviderAccountEvent(event){return event.type==="auth_accounts_changed"||event.type==="account_failover"}var RpcTransportGoneError=class extends Error{constructor(message="Shared RPC host is unavailable"){super(message),this.code="rpc_transport_gone",this.name="RpcTransportGoneError"}};function isTransportGoneError(error){return error instanceof RpcTransportGoneError||error instanceof Error&&(error.message==="Client not started"||error.message.startsWith("RPC transport is not writable.")||error.message==="RPC socket closed")}var RpcClientOpenInFlightError=class extends Error{constructor(){super("An open_session request is already in flight"),this.code="open_session_in_flight",this.name="RpcClientOpenInFlightError"}},MAX_PENDING_SESSION_EVENTS=512,MAX_PENDING_SESSION_EVENT_BYTES=1024*1024,RpcClient=class{constructor(options={}){this.process=null,this.socket=null,this.stopReadingStdout=null,this.eventListeners=[],this.pendingRequests=new Map,this.requestId=0,this.compactions=new Map,this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.pendingSessionEventBytes=0,this.stderr="",this.exitError=null,this.disconnectNotified=!1,this.stopping=!1,this.originHost=originHostInstance(),this.options=options,this.onEvent(event=>{switch(event.type){case"compaction_start":{let key=event.requestId??"";if(this.compactions.has(key))return;this.compactions.set(key,Date.now());break}case"compaction_end":if(!this.compactions.delete(event.requestId??""))return;break;case"session_replaced":this.compactions.clear();break;default:return}for(let pending of this.pendingRequests.values())pending.sessionId===this.sessionId&&pending.onCompaction?.(this.isCompacting())})}async start(){if(this.process||this.socket)throw new Error("Client already started");if(this.exitError=null,this.compactions.clear(),this.stopping=!1,this.disconnectNotified=!1,this.options.socketPath){await this.startSocket(this.options.socketPath);return}let cliPath=this.options.cliPath??"dist/cli.js",args=["--mode","rpc"];this.options.provider&&this.options.model&&args.push("--provider",this.options.provider),this.options.model&&args.push("--model",this.options.model),this.options.args&&args.push(...this.options.args);let childProcess=spawn("node",[cliPath,...args],{cwd:this.options.cwd,env:{...process.env,SENPI_RUNTIME:"node",...this.options.env},stdio:["pipe","pipe","pipe"],windowsHide:!0});if(this.process=childProcess,childProcess.stderr?.on("data",data=>{this.stderr+=data.toString(),process.stderr.write(data)}),childProcess.once("exit",(code,signal)=>{if(this.process!==childProcess)return;let error=this.createProcessExitError(code,signal);this.exitError=error,this.rejectPendingRequests(error)}),childProcess.once("error",error=>{if(this.process!==childProcess)return;let processError=new Error(`Agent process error: ${error.message}. Stderr: ${this.stderr}`);this.exitError=processError,this.rejectPendingRequests(processError)}),childProcess.stdin?.on("error",error=>{if(this.process!==childProcess)return;let stdinError=this.exitError??new Error(`Agent process stdin error: ${error.message}. Stderr: ${this.stderr}`);this.exitError=stdinError,this.rejectPendingRequests(stdinError)}),this.stopReadingStdout=attachJsonlLineReader(childProcess.stdout,line=>{this.handleLine(line)}),await new Promise(resolve=>setTimeout(resolve,100)),this.process.exitCode!==null){let error=this.exitError??this.createProcessExitError(this.process.exitCode,this.process.signalCode);throw this.exitError=error,error}}isCompacting(){let now=Date.now();for(let[key,startedAt]of this.compactions)now-startedAt>=PROMPT_COMPACTION_DEADLINE_MS&&this.compactions.delete(key);return this.compactions.size>0}async stop(){if(this.compactions.clear(),this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.pendingSessionEventBytes=0,this.socket){this.stopping=!0,this.stopReadingStdout?.(),this.stopReadingStdout=null,this.socket.destroy(),this.socket=null,this.sessionId=void 0,this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.notifyDisconnect(new RpcTransportGoneError);return}this.process&&(this.stopReadingStdout?.(),this.stopReadingStdout=null,signalSpawnedChild(this.process,"SIGTERM"),await new Promise(resolve=>{let timeout=setTimeout(()=>{this.process&&signalSpawnedChild(this.process,"SIGKILL"),resolve()},1e3);this.process?.on("exit",()=>{clearTimeout(timeout),resolve()})}),this.process=null,this.pendingRequests.clear())}async startSocket(path){let socket=await connectRpcClientSocket(path,this.options);this.socket=socket,this.stopReadingStdout=attachJsonlLineReader(socket,line=>this.handleLine(line)),socket.once("close",()=>{this.socket===socket&&(this.socket=null,this.notifyDisconnect(new RpcTransportGoneError))}),socket.once("error",error=>{this.socket===socket&&this.notifyDisconnect(new RpcTransportGoneError)})}onEvent(listener){return this.eventListeners.push(listener),()=>{let index=this.eventListeners.indexOf(listener);index!==-1&&this.eventListeners.splice(index,1)}}getStderr(){return this.stderr}async setClientInfo(width,capabilities){await this.send({type:"set_client_info",width,capabilities},this.sessionId!==void 0)}async openSession(options){if(this.pendingOpenSession)throw new RpcClientOpenInFlightError;this.pendingOpenSession=!0;try{let response=await this.send({type:"open_session",...options},!1),opened=this.getData(response);return this.sessionId=opened.sessionId,this.compactions.clear(),this.pendingOpenSession=!1,this.flushPendingSessionEvents(),opened}catch(error){throw this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.pendingSessionEventBytes=0,error}}async sendExtensionUIResponse(response){await this.send(response,!0,void 0,!1)}async resumeSession(){await this.send({type:"resume_session"})}async pauseForRun(command){return this.getData(await this.send({...command,type:"pause_for_run"}))}async resumeForRun(command){return this.getData(await this.send({...command,type:"resume_for_run"}))}async holdInput(command){return this.getData(await this.send({...command,type:"hold_input"}))}async sendExtensionUIProgress(progress){await this.send(progress,!0,void 0,!1)}async unloadSession(options){let sessionId=options.sessionId??this.sessionId;if(!sessionId)throw new Error("unload_session needs a session");return this.getData(await this.send({...options,type:"unload_session",sessionId},!1))}async setSessionContext(patch,sessionId=this.sessionId){if(!sessionId)throw new RpcCommandError(`${RPC_ERROR_SESSION_NOT_ATTACHED}: set_session_context needs a session`,RPC_ERROR_SESSION_NOT_ATTACHED,{reason:"this client has no attached session",action:"attach with openSession first, or pass a sessionId"});this.getData(await this.send({type:"set_session_context",sessionId,patch},!1))}async closeSession(sessionId=this.sessionId){if(sessionId){try{this.getData(await this.send({type:"close_session",sessionId},!1))}catch(error){if(!isTransportGoneError(error))throw error}this.sessionId===sessionId&&(this.sessionId=void 0,this.compactions.clear())}}async detachConnection(){let info=this.getData(await this.send({type:"get_protocol_info"},!1));if(!Array.isArray(info.capabilities)||!info.capabilities.includes(DETACH_CONNECTION_CAPABILITY))return"unsupported";let data=this.getData(await this.send({type:"detach_connection"},!1));return{deferred:Array.isArray(data?.deferred)?data.deferred.filter(id=>typeof id=="string"):[]}}async listSessions(options){let response=await this.send({type:"list_sessions",...options},!1);return this.getData(response).sessions}async prompt(message,optionsOrImages){let options=Array.isArray(optionsOrImages)?{images:optionsOrImages}:optionsOrImages??{},response=await this.send({type:"prompt",message,...clientMessageIdentity(options),...options.images?{images:options.images}:{},...options.streamingBehavior?{streamingBehavior:options.streamingBehavior}:{},...options.thinkingLevel?{thinkingLevel:options.thinkingLevel}:{},...options.sessionTitlePrompt!==void 0?{sessionTitlePrompt:options.sessionTitlePrompt}:{},...options.expandPromptTemplates!==void 0?{expandPromptTemplates:options.expandPromptTemplates}:{},...options.unknownCommandAsText?{unknownCommandAsText:!0}:{}},!0,{onResponse:wireResponse=>{if(!wireResponse.success){options?.preflightResult?.(!1);return}let data=wireResponse.data;options?.promptDisposition?.(data?.disposition??"handled"),options?.preflightResult?.(!0)},onReject:()=>{options?.preflightResult?.(!1)}});if(!response.success){let failure=response;throw(failure.errorCode===RPC_ERROR_UNKNOWN_COMMAND?unknownCommandErrorFromWire(failure.errorData):void 0)??new RpcCommandError(failure.error,failure.errorCode,failure.errorData)}return response.data?.disposition??"handled"}async appendUserMessage(content){await this.send({type:"append_user_message",content})}async appendSessionEntry(entry){await this.send({type:"append_session_entry",entry})}async sendCustomMessage(message,options){await this.send({type:"send_custom_message",...message,...options})}async continueFromLeaf(){let response=await this.send({type:"continue_from_leaf"});this.getData(response)}async steer(message,images,recovery){let response=await this.send({type:"steer",message,images,enqueueOrder:recovery?.enqueueOrder,...clientMessageIdentity(recovery)});return this.getData(response)?.disposition??"handled"}async followUp(message,images,recovery){let response=await this.send({type:"follow_up",message,images,enqueueOrder:recovery?.enqueueOrder,...clientMessageIdentity(recovery)});return this.getData(response)?.disposition??"handled"}async abort(options){if(options===void 0){try{await this.send({type:"abort"})}catch(error){if(!isTransportGoneError(error))throw error}return}return this.getData(await this.send({...options,type:"abort"}))}async getEffectStatus(effectId,options={}){let command={type:"get_effect_status",effectId,...options.sessionPath===void 0?{}:{sessionPath:options.sessionPath}};return this.getData(await this.send(command,options.sessionPath===void 0))}async getPendingInputs(knownClientMessageIds){let command={type:"get_pending_inputs",...knownClientMessageIds===void 0?{}:{knownClientMessageIds:[...knownClientMessageIds]}};return this.getData(await this.send(command))}async abortStrict(){this.getData(await this.send({type:"abort"}))}async abortCompaction(){await this.send({type:"abort_compaction"})}async reload(){let response=await this.send({type:"reload"});return this.getData(response)}async checkReloadVeto(){let response=await this.send({type:"check_reload_veto"});return this.getData(response)}async clearQueue(options){let response=await this.send({type:"clear_queue",abortWillFollow:options?.abortWillFollow});return this.getData(response)}async getSteeringMessages(){let response=await this.send({type:"get_steering_messages"});return this.getData(response).messages}async getFollowUpMessages(){let response=await this.send({type:"get_follow_up_messages"});return this.getData(response).messages}async abortBranchSummary(){await this.send({type:"abort_branch_summary"})}async recordBashResult(command,result,excludeFromContext){await this.send({type:"record_bash_result",command,result,excludeFromContext})}async setLabel(entryId,label){await this.send({type:"set_label",entryId,label})}async newSession(parentSession){let response=await this.send({type:"new_session",parentSession});return this.getData(response)}async getState(){let response=await this.send({type:"get_state"});return this.getData(response)}async setModel(provider,modelId){let response=await this.send({type:"set_model",provider,modelId});return this.getData(response)}async setFavoriteModels(models){await this.send({type:"set_favorite_models",models})}async setScopedModels(models){await this.send({type:"set_scoped_models",models})}async cycleModel(direction="forward"){let response=await this.send({type:"cycle_model",direction});return this.getData(response)}async getAvailableModels(){let response=await this.send({type:"get_available_models"});return this.getData(response).models}async setThinkingLevel(level,options){let response=await this.send({type:"set_thinking_level",level,scope:options?.scope});if(!response.success)throw new Error(response.error)}async cycleThinkingLevel(){let response=await this.send({type:"cycle_thinking_level"});return this.getData(response)}async getAvailableThinkingLevels(){let response=await this.send({type:"get_available_thinking_levels"});return this.getData(response).levels}async getModelThinkingLevel(provider,modelId){let response=await this.send({type:"get_model_thinking_level",provider,modelId});return this.getData(response)}async setFastMode(enabled){let response=await this.send({type:"set_fast_mode",enabled});return this.getData(response)}async getFastMode(){let response=await this.send({type:"get_fast_mode"});return this.getData(response)}async setSteeringMode(mode){await this.send({type:"set_steering_mode",mode})}async setFollowUpMode(mode){await this.send({type:"set_follow_up_mode",mode})}async compact(customInstructions){let response=await this.send({type:"compact",customInstructions});return this.getData(response)}async setAutoCompaction(enabled){await this.send({type:"set_auto_compaction",enabled})}async setAutoRetry(enabled){await this.send({type:"set_auto_retry",enabled})}async abortRetry(){await this.send({type:"abort_retry"})}async setRetryFallback(retryFallback){await this.send({type:"set_retry_fallback",retryFallback})}async bash(command,options){let response=await this.send({type:"bash",command,excludeFromContext:options?.excludeFromContext,executionId:options?.executionId,operations:options?.operations});return this.getData(response)}async navigateTree(targetId,options){let response=await this.send({type:"navigate_tree",targetId,...options});return this.getData(response)}async abortBash(){await this.send({type:"abort_bash"})}async cleanupBashOutput(path){await this.send({type:"cleanup_bash_output",path})}async getSessionStats(){let response=await this.send({type:"get_session_stats"});return this.getData(response)}async resolvePath(path){let response=await this.send({type:"resolve_path",path});return this.getData(response)}async exportHtml(outputPath,themeName){let response=await this.send({type:"export_html",outputPath,themeName});return this.getData(response)}async exportJsonl(outputPath){let response=await this.send({type:"export_jsonl",outputPath});return this.getData(response)}async switchSession(sessionPath,options){let response=await this.send({type:"switch_session",sessionPath,cwdOverride:options?.cwdOverride});return this.getData(response)}async fork(entryId,options){let response=await this.send({type:"fork",entryId,position:options?.position});return this.getData(response)}async clone(){let response=await this.send({type:"clone"});return this.getData(response)}async getForkMessages(){let response=await this.send({type:"get_fork_messages"});return this.getData(response).messages}async getEntries(since,page){let response=await this.send({type:"get_entries",since,...page});return this.getData(response)}async getTree(){let response=await this.send({type:"get_tree"});return this.getData(response)}async editAssistantMessage(entryId,text,options={}){let response=await this.send({type:"edit_assistant_message",entryId,text,expectedLeafId:options.expectedLeafId,summarize:options.summarize,customInstructions:options.customInstructions});return this.getData(response)}async editUserMessage(entryId,text,options={}){let response=await this.send({type:"edit_user_message",entryId,text,expectedLeafId:options.expectedLeafId,summarize:options.summarize,customInstructions:options.customInstructions});return this.getData(response)}async getLastAssistantText(){let response=await this.send({type:"get_last_assistant_text"});return this.getData(response).text}async setSessionName(name){await this.send({type:"set_session_name",name})}async recordDraftActivity(){await this.send({type:"record_user_activity",kind:"draft"})}async importJsonl(inputPath,cwdOverride){let response=await this.send({type:"import_jsonl",inputPath,cwdOverride});return this.getData(response)}async getMessages(){let response=await this.send({type:"get_messages"});return this.getData(response).messages}async getMedia(toolCallId,contentIndex){let response=await this.send({type:"get_media",toolCallId,contentIndex});return this.getData(response).content}async getCommands(){let response=await this.send({type:"get_commands"});return this.getData(response).commands}async requestExtension(name,data){let response=await this.send({type:"extension_request",name,...data===void 0?{}:{data}});return this.getData(response)}async listEvalCells(){return this.getData(await this.send({type:"eval_cells.list"}))}async peekEvalCell(cellId,tailBytes){return this.getData(await this.send({type:"eval_cells.peek",cellId,...tailBytes===void 0?{}:{tailBytes}}))}async stopEvalCell(cellId){return this.getData(await this.send({type:"eval_cells.stop",cellId}))}async getProviderAccounts(provider){let response=await this.send({type:"get_provider_accounts",provider});return this.getData(response).accounts}async pinProviderAccount(provider,name){await this.send({type:"account_pin",provider,name})}async orderProviderAccounts(provider,names){await this.send({type:"account_order",provider,names})}async removeProviderAccount(provider,name){await this.send({type:"account_remove",provider,name})}waitForIdle(timeout=6e4){return new Promise((resolve,reject)=>{let timer=setTimeout(()=>{unsubscribe(),reject(new Error(`Timeout waiting for agent to become idle. Stderr: ${this.stderr}`))},timeout),unsubscribe=this.onEvent(event=>{event.type==="agent_settled"&&(clearTimeout(timer),unsubscribe(),resolve())})})}collectEvents(timeout=6e4){return new Promise((resolve,reject)=>{let events=[],timer=setTimeout(()=>{unsubscribe(),reject(new Error(`Timeout collecting events. Stderr: ${this.stderr}`))},timeout),unsubscribe=this.onEvent(event=>{isProviderAccountEvent(event)||event.type==="extension_event"||event.type==="bash_start"||event.type==="bash_end"||event.type==="extension_ui_request"||event.type==="session_replaced"||event.type==="session_parked"||event.type==="queued"||(events.push(event),event.type==="agent_settled"&&(clearTimeout(timer),unsubscribe(),resolve(events)))})})}async promptAndWait(message,images,timeout=6e4){let eventsPromise=this.collectEvents(timeout);return await this.prompt(message,images?{images}:void 0),eventsPromise}handleLine(line){try{let data=JSON.parse(line);if(data.type==="response"&&data.id&&this.pendingRequests.has(data.id)){let pending=this.pendingRequests.get(data.id);this.pendingRequests.delete(data.id),pending.onResponse?.(data),pending.resolve(data);return}if(data.type==="queued"&&typeof data.for_request=="string"&&this.pendingRequests.get(data.for_request)?.onQueued?.(data.position),typeof data.sessionId=="string"&&data.sessionId!==this.sessionId){if(this.pendingOpenSession){let bytes=Buffer.byteLength(line);for(this.pendingSessionEvents.push({sessionId:data.sessionId,event:data,bytes}),this.pendingSessionEventBytes+=bytes;this.pendingSessionEvents.length>MAX_PENDING_SESSION_EVENTS||this.pendingSessionEventBytes>MAX_PENDING_SESSION_EVENT_BYTES;){let oldest=this.pendingSessionEvents.shift();if(!oldest)break;this.pendingSessionEventBytes-=oldest.bytes}}return}for(let listener of[...this.eventListeners])listener(data)}catch{}}flushPendingSessionEvents(){let pending=this.pendingSessionEvents;this.pendingSessionEvents=[],this.pendingSessionEventBytes=0;for(let{sessionId,event}of pending)if(sessionId===this.sessionId)for(let listener of[...this.eventListeners])listener(event)}createProcessExitError(code,signal){return new Error(`Agent process exited (code=${code} signal=${signal}). Stderr: ${this.stderr}`)}notifyDisconnect(error){this.rejectPendingRequests(error),!this.disconnectNotified&&!this.stopping&&(this.disconnectNotified=!0,this.options.onDisconnect?.(error))}rejectPendingRequests(error){for(let pending of this.pendingRequests.values())pending.onReject?.(error),pending.reject(error);this.pendingRequests.clear()}async send(command,route=!0,hooks,expectResponse=!0){let childProcess=this.process,stream=this.socket??childProcess?.stdin;if(!stream)throw new RpcTransportGoneError;if(this.exitError)throw this.exitError;if(childProcess?.exitCode!==null&&childProcess?.exitCode!==void 0){let error=this.createProcessExitError(childProcess.exitCode,childProcess.signalCode);throw this.exitError=error,error}if(stream.destroyed||!stream.writable){let error=new RpcTransportGoneError;throw this.notifyDisconnect(error),error}let ownId=command.type==="extension_ui_response"||command.type==="extension_ui_progress"?command.id:void 0,id=ownId??`req_${++this.requestId}`,fullCommand={...command,...route&&this.sessionId&&!("sessionId"in command)?{sessionId:this.sessionId}:{},...ownId===void 0?{id}:{},...this.socket&&this.originHost?{[ORIGIN_HOST_INSTANCE_FIELD]:this.originHost}:{}};if(!expectResponse)return stream.write(serializeJsonLine(fullCommand)),Promise.resolve({type:"response",command:command.type,success:!0});let sessionId="sessionId"in fullCommand?fullCommand.sessionId:void 0;return new Promise((resolve,reject)=>{let timeoutMessage=()=>`Timeout waiting for response to ${command.type}. Stderr: ${this.stderr}`,promptCap,promptReceived=!1,expire=timeoutError=>{clearTimeout(promptCap);let pending=this.pendingRequests.get(id);this.pendingRequests.delete(id),pending?.onReject?.(timeoutError),reject(timeoutError)},deadline=armRequestDeadline(command.type==="prompt"&&sessionId===this.sessionId&&this.isCompacting()?PROMPT_COMPACTION_DEADLINE_MS:requestDeadlineMs(command),timeoutMessage,expire);command.type==="prompt"&&(promptCap=setTimeout(()=>{deadline.clear(),expire(new RpcRequestTimeoutError(timeoutMessage()))},PROMPT_ACK_MAX_WAIT_MS)),this.pendingRequests.set(id,{sessionId,resolve:response=>{deadline.clear(),clearTimeout(promptCap),resolve(response)},reject:error=>{deadline.clear(),clearTimeout(promptCap),reject(error)},...command.type==="prompt"?{onCompaction:compacting=>compacting?deadline.extend(PROMPT_COMPACTION_DEADLINE_MS,timeoutMessage):promptReceived?deadline.extend(PROMPT_AFTER_QUEUED_DEADLINE_MS,promptStalledMessage):deadline.extend(REQUEST_DEADLINE_MS,timeoutMessage),onQueued:()=>{promptReceived=!0,this.isCompacting()||deadline.extend(PROMPT_AFTER_QUEUED_DEADLINE_MS,promptStalledMessage)}}:{},...command.type==="open_session"?{onQueued:position=>deadline.extend(OPEN_AFTER_QUEUED_DEADLINE_MS,()=>openStalledMessage(position))}:{},...hooks?.onResponse?{onResponse:hooks.onResponse}:{},...hooks?.onReject?{onReject:hooks.onReject}:{}});try{stream.write(serializeJsonLine(fullCommand))}catch(error){let writeError=error instanceof Error?error:new Error(String(error)),pending=this.pendingRequests.get(id);this.pendingRequests.delete(id),pending?.onReject?.(writeError),pending?.reject(writeError)}})}getData(response){if(!response.success){let errorResponse=response;throw errorResponse.errorCode==="missing_session_cwd"&&errorResponse.errorData?new MissingSessionCwdError(errorResponse.errorData):new RpcCommandError(errorResponse.error,errorResponse.errorCode,errorResponse.errorData)}return response.data}};export{UnknownCommandError,UNKNOWN_COMMAND_CONFIRM_HINT,commandShapedName,findUnknownCommand,getMissingSessionCwdIssue,formatMissingSessionCwdPrompt,MissingSessionCwdError,assertSessionCwdExists,RpcRequestTimeoutError,RpcCommandError,RpcTransportGoneError,isTransportGoneError,RpcClientOpenInFlightError,RpcClient};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{source_default}from"./chunk-26UBFF6N.js";import{legacyProviderIdRejection}from"./chunk-6AOVUTUR.js";import{APP_NAME,CONFIG_DIR_NAME,ENV_AGENT_DIR,ENV_SESSION_DIR,brandEnvNames,envValue}from"./chunk-7YO7VQZW.js";function isGrokNeoEnabled(env=process.env){let value=envValue("ENABLE_GROK_NEO",env);if(value===void 0)return!1;let normalized=value.trim().toLowerCase();return normalized==="1"||normalized==="true"||normalized==="yes"}var TOOL_RUNTIMES=["in-process","child"];function isToolRuntimeKind(value){return TOOL_RUNTIMES.some(kind=>kind===value)}var TOOL_RUNTIME_ENV_SUFFIX="TOOL_RUNTIME",TOOL_CHILD_PLACEMENTS=["launch-barrier","job-policy","hostcall","offload","terminal"],OFFLOAD_API_CAPABILITY="offload_api";function toolRuntimeCapabilities(runtime){return[toolRuntimeCapability(runtime),...runtime==="child"?TOOL_CHILD_PLACEMENTS.map(name=>`placement:${name}`):[],OFFLOAD_API_CAPABILITY]}function isHostOwnedToolRuntimeCapability(capability){return capability.startsWith("tool_runtime:")||capability.startsWith("placement:")||capability===OFFLOAD_API_CAPABILITY}function toolRuntimeCapability(runtime){return`tool_runtime:${runtime}`}var TOOL_RUNTIME_UNSUPPORTED_HOST="tool_runtime_unsupported_host";function toolRuntimeHostRefusal(runtime,platform=process.platform,hasFfi=()=>process.getBuiltinModule("bun:ffi")!==void 0){if(!(runtime!=="child"||platform!=="win32"||hasFfi()))return`${TOOL_RUNTIME_UNSUPPORTED_HOST}: --tool-runtime child on Windows needs the Bun runtime (bun:ffi holds the tool child in a Job Object); run senpi under Bun or use in-process`}var ToolRuntimeUnsupportedHostError=class extends Error{constructor(message){super(message),this.code=TOOL_RUNTIME_UNSUPPORTED_HOST,this.name="ToolRuntimeUnsupportedHostError"}};function resolveToolRuntime(parsed,env=process.env,platform=process.platform,hasFfi){let resolved=resolveRequestedToolRuntime(parsed,env);if(!resolved.ok)return resolved;let refusal=toolRuntimeHostRefusal(resolved.value,platform,hasFfi);return refusal===void 0?resolved:{ok:!1,message:refusal}}function resolveRequestedToolRuntime(parsed,env){if(parsed.toolRuntime!==void 0)return{ok:!0,value:parsed.toolRuntime};let fromEnv=envValue(TOOL_RUNTIME_ENV_SUFFIX,env);return fromEnv===void 0||fromEnv===""?{ok:!0,value:"in-process"}:isToolRuntimeKind(fromEnv)?{ok:!0,value:fromEnv}:{ok:!1,message:`${brandEnvNames(TOOL_RUNTIME_ENV_SUFFIX).find(candidate=>env[candidate]!==void 0)??TOOL_RUNTIME_ENV_SUFFIX} (--tool-runtime) must be in-process or child, got ${JSON.stringify(fromEnv)}`}}var SESSION_RUNTIMES=["in-process","worker"];function isSessionRuntimeKind(value){return SESSION_RUNTIMES.includes(value)}function resolveSessionRuntime(parsed){return parsed.sessionRuntime?parsed.sessionRuntime:parsed.listen!==void 0&&parsed.listen!=="stdio://"?"in-process":"worker"}var VALID_THINKING_LEVELS=["off","minimal","low","medium","high","xhigh","max"];function isValidThinkingLevel(level){return VALID_THINKING_LEVELS.includes(level)}function normalizeSessionName(value){let name=value.trim();return name.length>0?name:void 0}function parseArgs(args,options={}){let grokNeoEnabled=options.grokNeoEnabled??isGrokNeoEnabled(),result={messages:[],fileArgs:[],unknownFlags:new Map,diagnostics:[]};if(args[0]==="app-server")return result.diagnostics.push({type:"error",message:`The app-server command has been removed. Use "${APP_NAME} --mode rpc" for process integration.`}),result;let hasRetiredCommand=!1;for(let i=0;i<args.length;i++){let arg=args[i];if(arg==="--"){for(let positionalArg of args.slice(i+1))positionalArg.startsWith("@")?result.fileArgs.push(positionalArg.slice(1)):result.messages.push(positionalArg);break}else if(arg==="--help"||arg==="-h")result.help=!0;else if(arg==="--version"||arg==="-v")result.version=!0;else if(arg==="--mode"){let mode=args[i+1];if(mode===void 0||mode.startsWith("-")){result.diagnostics.push({type:"error",message:"--mode requires text, json, or rpc"});continue}if(i++,mode!=="text"&&mode!=="json"&&mode!=="rpc"){result.diagnostics.push({type:"error",message:`Invalid mode "${mode}". Valid values: text, json, rpc`});continue}result.mode=mode}else if(arg==="--continue"||arg==="-c")result.continue=!0;else if(arg==="--resume"||arg==="-r")result.resume=!0;else if(arg==="--provider"&&i+1<args.length){let typedProvider=args[++i],rejection=typedProvider===void 0?void 0:legacyProviderIdRejection(typedProvider);if(rejection)throw new Error(rejection);result.provider=typedProvider}else if(arg==="--model"&&i+1<args.length)result.model=args[++i];else if(arg==="--api-key"&&i+1<args.length)result.apiKey=args[++i];else if(arg==="--system-prompt"&&i+1<args.length)result.systemPrompt=args[++i];else if(arg==="--append-system-prompt"&&i+1<args.length)result.appendSystemPrompt=result.appendSystemPrompt??[],result.appendSystemPrompt.push(args[++i]);else if(arg==="--name"||arg==="-n")i+1<args.length?result.name=args[++i]:result.diagnostics.push({type:"error",message:"--name requires a value"});else if(arg==="--no-session")result.noSession=!0;else if(arg==="--session"&&i+1<args.length)result.session=args[++i];else if(arg==="--session-id"&&i+1<args.length)result.sessionId=args[++i];else if(arg==="--fork"&&i+1<args.length)result.fork=args[++i];else if(arg==="--rebind"&&i+1<args.length)result.rebind=args[++i];else if(arg==="--session-dir"&&i+1<args.length)result.sessionDir=args[++i];else if(arg==="--models"&&i+1<args.length)result.models=args[++i].split(",").map(s=>s.trim());else if(arg==="--no-tools"||arg==="-nt")result.noTools=!0;else if(arg==="--no-builtin-tools"||arg==="-nbt")result.noBuiltinTools=!0;else if((arg==="--tools"||arg==="-t")&&i+1<args.length)result.tools=args[++i].split(",").map(s=>s.trim()).filter(name=>name.length>0);else if((arg==="--exclude-tools"||arg==="-xt")&&i+1<args.length)result.excludeTools=args[++i].split(",").map(s=>s.trim()).filter(name=>name.length>0);else if(arg==="--thinking"&&i+1<args.length){let level=args[++i];isValidThinkingLevel(level)?result.thinking=level:result.diagnostics.push({type:"warning",message:`Invalid thinking level "${level}". Valid values: ${VALID_THINKING_LEVELS.join(", ")}`})}else if(arg==="--print"||arg==="-p"){result.print=!0;let next=args[i+1];next!==void 0&&!next.startsWith("@")&&(!next.startsWith("-")||next.startsWith("---"))&&(result.messages.push(next),i++)}else if(arg==="--export"&&i+1<args.length)result.export=args[++i];else if((arg==="--extension"||arg==="-e")&&i+1<args.length)result.extensions=result.extensions??[],result.extensions.push(args[++i]);else if(arg==="--no-extensions"||arg==="-ne")result.noExtensions=!0;else if(arg==="--skill"&&i+1<args.length)result.skills=result.skills??[],result.skills.push(args[++i]);else if(arg==="--prompt-template"&&i+1<args.length)result.promptTemplates=result.promptTemplates??[],result.promptTemplates.push(args[++i]);else if(arg==="--theme"&&i+1<args.length)result.themes=result.themes??[],result.themes.push(args[++i]);else if(arg==="--use-theme"){let themeName=args[i+1];themeName===void 0||themeName.startsWith("-")?result.diagnostics.push({type:"error",message:"--use-theme requires a theme name"}):(result.useTheme=themeName,i++)}else if(arg==="--no-skills"||arg==="-ns")result.noSkills=!0;else if(arg==="--no-prompt-templates"||arg==="-np")result.noPromptTemplates=!0;else if(arg==="--no-themes")result.noThemes=!0;else if(arg==="--no-context-files"||arg==="-nc")result.noContextFiles=!0;else if(arg==="--print-prompt-sizes")result.printPromptSizes=!0;else if(arg==="--list-models")i+1<args.length&&!args[i+1].startsWith("-")&&!args[i+1].startsWith("@")?result.listModels=args[++i]:result.listModels=!0;else if(arg==="--list-tips")result.listTips=!0;else if(arg==="--tui-mode"){let mode=args[i+1];mode==="regular"||mode==="fullscreen"?(result.tuiMode=mode,i++):mode===void 0||mode.startsWith("-")?result.diagnostics.push({type:"error",message:"--tui-mode requires regular or fullscreen"}):(i++,result.diagnostics.push({type:"error",message:`Invalid TUI mode "${mode}". Valid values: regular, fullscreen`}))}else if(arg==="--verbose")result.verbose=!0;else if(arg==="--approve"||arg==="-a")result.projectTrustOverride=!0;else if(arg==="--no-approve"||arg==="-na")result.projectTrustOverride=!1;else if(arg==="--offline")result.offline=!0;else if(grokNeoEnabled&&arg==="--grok-neo")result.grokNeo=!0;else if(arg==="--multi-session")result.multiSession=!0;else if(arg==="--auto-title-sessions")result.autoTitleSessions=!0;else if(arg==="--session-runtime"){let value=args[i+1];value!==void 0&&!value.startsWith("--")&&i++,value!==void 0&&isSessionRuntimeKind(value)?result.sessionRuntime=value:result.diagnostics.push({type:"error",message:"--session-runtime must be in-process or worker"})}else if(arg==="--tool-runtime"){let value=args[i+1];value!==void 0&&!value.startsWith("--")&&i++,value!==void 0&&isToolRuntimeKind(value)?result.toolRuntime=value:result.diagnostics.push({type:"error",message:"--tool-runtime must be in-process or child"})}else if(arg==="--listen"&&result.mode==="rpc"){let value=args[i+1];value===void 0||value.startsWith("--")?result.diagnostics.push({type:"error",message:"--listen requires a value"}):(result.listen=value,result.multiSession=!0,i++)}else if(arg.startsWith("@"))result.fileArgs.push(arg.slice(1));else if(arg.startsWith("--")){let eqIndex=arg.indexOf("=");if(eqIndex!==-1)result.unknownFlags.set(arg.slice(2,eqIndex),arg.slice(eqIndex+1));else{let flagName=arg.slice(2),next=args[i+1];next!==void 0&&!next.startsWith("-")&&!next.startsWith("@")?(result.unknownFlags.set(flagName,next),i++):result.unknownFlags.set(flagName,!0)}}else arg==="-"?result.readStdin=!0:arg.startsWith("-")&&!arg.startsWith("--")?result.diagnostics.push({type:"error",message:`Unknown option: ${arg}`}):arg.startsWith("-")||(result.messages.length===0&&arg==="app-server"&&(hasRetiredCommand=!0),result.messages.push(arg))}return hasRetiredCommand&&result.mode===void 0&&!result.print&&(result.messages=[],result.diagnostics.push({type:"error",message:`The app-server command has been removed. Use "${APP_NAME} --mode rpc" for process integration.`})),result}function printHelp(extensionFlags,grokNeoEnabled=isGrokNeoEnabled()){let extensionFlagsText=extensionFlags&&extensionFlags.length>0?`
|
|
2
|
+
import{source_default}from"./chunk-26UBFF6N.js";import{legacyProviderIdRejection}from"./chunk-6AOVUTUR.js";import{APP_NAME,CONFIG_DIR_NAME,ENV_AGENT_DIR,ENV_SESSION_DIR,brandEnvNames,envValue}from"./chunk-7YO7VQZW.js";function isSpawnedBy(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)&&"parentSessionId"in value&&typeof value.parentSessionId=="string"&&value.parentSessionId.length>0&&"rootSessionId"in value&&typeof value.rootSessionId=="string"&&value.rootSessionId.length>0&&"taskId"in value&&typeof value.taskId=="string"&&value.taskId.length>0}function isGrokNeoEnabled(env=process.env){let value=envValue("ENABLE_GROK_NEO",env);if(value===void 0)return!1;let normalized=value.trim().toLowerCase();return normalized==="1"||normalized==="true"||normalized==="yes"}var TOOL_RUNTIMES=["in-process","child"];function isToolRuntimeKind(value){return TOOL_RUNTIMES.some(kind=>kind===value)}var TOOL_RUNTIME_ENV_SUFFIX="TOOL_RUNTIME",TOOL_CHILD_PLACEMENTS=["launch-barrier","job-policy","hostcall","offload","terminal"],OFFLOAD_API_CAPABILITY="offload_api";function toolRuntimeCapabilities(runtime){return[toolRuntimeCapability(runtime),...runtime==="child"?TOOL_CHILD_PLACEMENTS.map(name=>`placement:${name}`):[],OFFLOAD_API_CAPABILITY]}function isHostOwnedToolRuntimeCapability(capability){return capability.startsWith("tool_runtime:")||capability.startsWith("placement:")||capability===OFFLOAD_API_CAPABILITY}function toolRuntimeCapability(runtime){return`tool_runtime:${runtime}`}var TOOL_RUNTIME_UNSUPPORTED_HOST="tool_runtime_unsupported_host";function toolRuntimeHostRefusal(runtime,platform=process.platform,hasFfi=()=>process.getBuiltinModule("bun:ffi")!==void 0){if(!(runtime!=="child"||platform!=="win32"||hasFfi()))return`${TOOL_RUNTIME_UNSUPPORTED_HOST}: --tool-runtime child on Windows needs the Bun runtime (bun:ffi holds the tool child in a Job Object); run senpi under Bun or use in-process`}var ToolRuntimeUnsupportedHostError=class extends Error{constructor(message){super(message),this.code=TOOL_RUNTIME_UNSUPPORTED_HOST,this.name="ToolRuntimeUnsupportedHostError"}};function resolveToolRuntime(parsed,env=process.env,platform=process.platform,hasFfi){let resolved=resolveRequestedToolRuntime(parsed,env);if(!resolved.ok)return resolved;let refusal=toolRuntimeHostRefusal(resolved.value,platform,hasFfi);return refusal===void 0?resolved:{ok:!1,message:refusal}}function resolveRequestedToolRuntime(parsed,env){if(parsed.toolRuntime!==void 0)return{ok:!0,value:parsed.toolRuntime};let fromEnv=envValue(TOOL_RUNTIME_ENV_SUFFIX,env);return fromEnv===void 0||fromEnv===""?{ok:!0,value:"in-process"}:isToolRuntimeKind(fromEnv)?{ok:!0,value:fromEnv}:{ok:!1,message:`${brandEnvNames(TOOL_RUNTIME_ENV_SUFFIX).find(candidate=>env[candidate]!==void 0)??TOOL_RUNTIME_ENV_SUFFIX} (--tool-runtime) must be in-process or child, got ${JSON.stringify(fromEnv)}`}}var SESSION_RUNTIMES=["in-process","worker"];function isSessionRuntimeKind(value){return SESSION_RUNTIMES.includes(value)}function resolveSessionRuntime(parsed){return parsed.sessionRuntime?parsed.sessionRuntime:parsed.listen!==void 0&&parsed.listen!=="stdio://"?"in-process":"worker"}var VALID_THINKING_LEVELS=["off","minimal","low","medium","high","xhigh","max"];function isValidThinkingLevel(level){return VALID_THINKING_LEVELS.includes(level)}function normalizeSessionName(value){let name=value.trim();return name.length>0?name:void 0}function parseArgs(args,options={}){let grokNeoEnabled=options.grokNeoEnabled??isGrokNeoEnabled(),result={messages:[],fileArgs:[],unknownFlags:new Map,diagnostics:[]};if(args[0]==="app-server")return result.diagnostics.push({type:"error",message:`The app-server command has been removed. Use "${APP_NAME} --mode rpc" for process integration.`}),result;let hasRetiredCommand=!1;for(let i=0;i<args.length;i++){let arg=args[i];if(arg==="--"){for(let positionalArg of args.slice(i+1))positionalArg.startsWith("@")?result.fileArgs.push(positionalArg.slice(1)):result.messages.push(positionalArg);break}else if(arg==="--help"||arg==="-h")result.help=!0;else if(arg==="--version"||arg==="-v")result.version=!0;else if(arg==="--mode"){let mode=args[i+1];if(mode===void 0||mode.startsWith("-")){result.diagnostics.push({type:"error",message:"--mode requires text, json, or rpc"});continue}if(i++,mode!=="text"&&mode!=="json"&&mode!=="rpc"){result.diagnostics.push({type:"error",message:`Invalid mode "${mode}". Valid values: text, json, rpc`});continue}result.mode=mode}else if(arg==="--continue"||arg==="-c")result.continue=!0;else if(arg==="--resume"||arg==="-r")result.resume=!0;else if(arg==="--provider"&&i+1<args.length){let typedProvider=args[++i],rejection=typedProvider===void 0?void 0:legacyProviderIdRejection(typedProvider);if(rejection)throw new Error(rejection);result.provider=typedProvider}else if(arg==="--model"&&i+1<args.length)result.model=args[++i];else if(arg==="--api-key"&&i+1<args.length)result.apiKey=args[++i];else if(arg==="--system-prompt"&&i+1<args.length)result.systemPrompt=args[++i];else if(arg==="--append-system-prompt"&&i+1<args.length)result.appendSystemPrompt=result.appendSystemPrompt??[],result.appendSystemPrompt.push(args[++i]);else if(arg==="--name"||arg==="-n")i+1<args.length?result.name=args[++i]:result.diagnostics.push({type:"error",message:"--name requires a value"});else if(arg==="--no-session")result.noSession=!0;else if(arg==="--session"&&i+1<args.length)result.session=args[++i];else if(arg==="--session-id"&&i+1<args.length)result.sessionId=args[++i];else if(arg==="--spawned-by"){let value=args[i+1];if(value===void 0||value.startsWith("-"))result.diagnostics.push({type:"error",message:"--spawned-by requires a JSON object"});else{i++;try{let ancestry=JSON.parse(value);isSpawnedBy(ancestry)?result.spawnedBy=ancestry:result.diagnostics.push({type:"error",message:"--spawned-by requires non-empty parentSessionId, rootSessionId, and taskId strings"})}catch(error){if(!(error instanceof SyntaxError))throw error;result.diagnostics.push({type:"error",message:"--spawned-by must be valid JSON"})}}}else if(arg==="--fork"&&i+1<args.length)result.fork=args[++i];else if(arg==="--rebind"&&i+1<args.length)result.rebind=args[++i];else if(arg==="--session-dir"&&i+1<args.length)result.sessionDir=args[++i];else if(arg==="--models"&&i+1<args.length)result.models=args[++i].split(",").map(s=>s.trim());else if(arg==="--no-tools"||arg==="-nt")result.noTools=!0;else if(arg==="--no-builtin-tools"||arg==="-nbt")result.noBuiltinTools=!0;else if((arg==="--tools"||arg==="-t")&&i+1<args.length)result.tools=args[++i].split(",").map(s=>s.trim()).filter(name=>name.length>0);else if((arg==="--exclude-tools"||arg==="-xt")&&i+1<args.length)result.excludeTools=args[++i].split(",").map(s=>s.trim()).filter(name=>name.length>0);else if(arg==="--thinking"&&i+1<args.length){let level=args[++i];isValidThinkingLevel(level)?result.thinking=level:result.diagnostics.push({type:"warning",message:`Invalid thinking level "${level}". Valid values: ${VALID_THINKING_LEVELS.join(", ")}`})}else if(arg==="--print"||arg==="-p"){result.print=!0;let next=args[i+1];next!==void 0&&!next.startsWith("@")&&(!next.startsWith("-")||next.startsWith("---"))&&(result.messages.push(next),i++)}else if(arg==="--export"&&i+1<args.length)result.export=args[++i];else if((arg==="--extension"||arg==="-e")&&i+1<args.length)result.extensions=result.extensions??[],result.extensions.push(args[++i]);else if(arg==="--no-extensions"||arg==="-ne")result.noExtensions=!0;else if(arg==="--skill"&&i+1<args.length)result.skills=result.skills??[],result.skills.push(args[++i]);else if(arg==="--prompt-template"&&i+1<args.length)result.promptTemplates=result.promptTemplates??[],result.promptTemplates.push(args[++i]);else if(arg==="--theme"&&i+1<args.length)result.themes=result.themes??[],result.themes.push(args[++i]);else if(arg==="--use-theme"){let themeName=args[i+1];themeName===void 0||themeName.startsWith("-")?result.diagnostics.push({type:"error",message:"--use-theme requires a theme name"}):(result.useTheme=themeName,i++)}else if(arg==="--no-skills"||arg==="-ns")result.noSkills=!0;else if(arg==="--no-prompt-templates"||arg==="-np")result.noPromptTemplates=!0;else if(arg==="--no-themes")result.noThemes=!0;else if(arg==="--no-context-files"||arg==="-nc")result.noContextFiles=!0;else if(arg==="--print-prompt-sizes")result.printPromptSizes=!0;else if(arg==="--list-models")i+1<args.length&&!args[i+1].startsWith("-")&&!args[i+1].startsWith("@")?result.listModels=args[++i]:result.listModels=!0;else if(arg==="--list-tips")result.listTips=!0;else if(arg==="--tui-mode"){let mode=args[i+1];mode==="regular"||mode==="fullscreen"?(result.tuiMode=mode,i++):mode===void 0||mode.startsWith("-")?result.diagnostics.push({type:"error",message:"--tui-mode requires regular or fullscreen"}):(i++,result.diagnostics.push({type:"error",message:`Invalid TUI mode "${mode}". Valid values: regular, fullscreen`}))}else if(arg==="--verbose")result.verbose=!0;else if(arg==="--approve"||arg==="-a")result.projectTrustOverride=!0;else if(arg==="--no-approve"||arg==="-na")result.projectTrustOverride=!1;else if(arg==="--offline")result.offline=!0;else if(grokNeoEnabled&&arg==="--grok-neo")result.grokNeo=!0;else if(arg==="--multi-session")result.multiSession=!0;else if(arg==="--auto-title-sessions")result.autoTitleSessions=!0;else if(arg==="--session-runtime"){let value=args[i+1];value!==void 0&&!value.startsWith("--")&&i++,value!==void 0&&isSessionRuntimeKind(value)?result.sessionRuntime=value:result.diagnostics.push({type:"error",message:"--session-runtime must be in-process or worker"})}else if(arg==="--tool-runtime"){let value=args[i+1];value!==void 0&&!value.startsWith("--")&&i++,value!==void 0&&isToolRuntimeKind(value)?result.toolRuntime=value:result.diagnostics.push({type:"error",message:"--tool-runtime must be in-process or child"})}else if(arg==="--listen"&&result.mode==="rpc"){let value=args[i+1];value===void 0||value.startsWith("--")?result.diagnostics.push({type:"error",message:"--listen requires a value"}):(result.listen=value,result.multiSession=!0,i++)}else if(arg.startsWith("@"))result.fileArgs.push(arg.slice(1));else if(arg.startsWith("--")){let eqIndex=arg.indexOf("=");if(eqIndex!==-1)result.unknownFlags.set(arg.slice(2,eqIndex),arg.slice(eqIndex+1));else{let flagName=arg.slice(2),next=args[i+1];next!==void 0&&!next.startsWith("-")&&!next.startsWith("@")?(result.unknownFlags.set(flagName,next),i++):result.unknownFlags.set(flagName,!0)}}else arg==="-"?result.readStdin=!0:arg.startsWith("-")&&!arg.startsWith("--")?result.diagnostics.push({type:"error",message:`Unknown option: ${arg}`}):arg.startsWith("-")||(result.messages.length===0&&arg==="app-server"&&(hasRetiredCommand=!0),result.messages.push(arg))}return hasRetiredCommand&&result.mode===void 0&&!result.print&&(result.messages=[],result.diagnostics.push({type:"error",message:`The app-server command has been removed. Use "${APP_NAME} --mode rpc" for process integration.`})),result}function printHelp(extensionFlags,grokNeoEnabled=isGrokNeoEnabled()){let extensionFlagsText=extensionFlags&&extensionFlags.length>0?`
|
|
3
3
|
${source_default.bold("Extension CLI Flags:")}
|
|
4
4
|
${extensionFlags.map(flag=>{let value=flag.type==="string"?" <value>":"",description=flag.description??`Registered by ${flag.extensionPath}`;return` --${flag.name}${value}`.padEnd(30)+description}).join(`
|
|
5
5
|
`)}
|
|
@@ -40,6 +40,8 @@ ${source_default.bold("Options:")}
|
|
|
40
40
|
--resume, -r Select a session to resume
|
|
41
41
|
--session <path|id> Use specific session file or partial UUID
|
|
42
42
|
--session-id <id> Use exact project session ID, creating it if missing
|
|
43
|
+
--spawned-by <json> Fresh session ancestry: parentSessionId, rootSessionId, taskId
|
|
44
|
+
Ignored with a warning when resuming or forking
|
|
43
45
|
--fork <path|id> Fork specific session file or partial UUID into a new session
|
|
44
46
|
--rebind <path|id> Move a session from a moved or re-cloned repository into this directory and continue it
|
|
45
47
|
--session-dir <dir> Directory for session storage and lookup
|
|
@@ -214,4 +216,4 @@ ${source_default.bold("Built-in Tool Names:")}
|
|
|
214
216
|
grep - Search file contents (read-only, off by default)
|
|
215
217
|
find - Find files by glob pattern (read-only, off by default)
|
|
216
218
|
ls - List directory contents (read-only, off by default)
|
|
217
|
-
`)}export{isToolRuntimeKind,toolRuntimeCapabilities,isHostOwnedToolRuntimeCapability,toolRuntimeCapability,TOOL_RUNTIME_UNSUPPORTED_HOST,toolRuntimeHostRefusal,ToolRuntimeUnsupportedHostError,resolveToolRuntime,isSessionRuntimeKind,resolveSessionRuntime,isValidThinkingLevel,normalizeSessionName,parseArgs,printHelp};
|
|
219
|
+
`)}export{isSpawnedBy,isToolRuntimeKind,toolRuntimeCapabilities,isHostOwnedToolRuntimeCapability,toolRuntimeCapability,TOOL_RUNTIME_UNSUPPORTED_HOST,toolRuntimeHostRefusal,ToolRuntimeUnsupportedHostError,resolveToolRuntime,isSessionRuntimeKind,resolveSessionRuntime,isValidThinkingLevel,normalizeSessionName,parseArgs,printHelp};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{CONFIG_COMMAND_ARGV,HOST_COMMAND_ARGV,SCHEDULE_COMMAND_ARGV,isPackageCommandArgv}from"./chunk-
|
|
2
|
+
import{CONFIG_COMMAND_ARGV,HOST_COMMAND_ARGV,SCHEDULE_COMMAND_ARGV,isPackageCommandArgv}from"./chunk-IUHU5PEC.js";import{homedir}from"node:os";try{process.cwd()}catch{let fallback=homedir();process.chdir(fallback),console.error(`the current working directory no longer exists; continuing from ${fallback}`)}import*as nodeModule from"node:module";async function enableStartupCompileCache(){let enable=nodeModule.enableCompileCache;if(typeof enable!="function")return;let args=process.argv.slice(2);if(!isPackageCommandArgv(args)&&args[0]!==CONFIG_COMMAND_ARGV&&(args.includes("--version")||args.includes("-v")))return;let scheduleWatch=!1;if(args[0]===SCHEDULE_COMMAND_ARGV&&args[1]==="run")for(let index=2;index<args.length;index++){let arg=args[index];arg==="--watch"?scheduleWatch=!0:["--exec","--poll-seconds","--timeout-seconds","--concurrency"].includes(arg??"")&&index++}if(!(isPackageCommandArgv(args)||args[0]===HOST_COMMAND_ARGV||args[0]===SCHEDULE_COMMAND_ARGV&&!scheduleWatch)){if(!scheduleWatch&&args.some(arg=>["--help","-h","--export","--list-models","--list-tips"].includes(arg)))try{let{parseArgs}=await import("./args-JXULI4EH.js"),parsed=parseArgs(args);if(parsed.version||parsed.export||parsed.listModels!==void 0||parsed.listTips||parsed.help&&!(parsed.mode==="rpc"&&parsed.multiSession))return}catch{}try{let result=enable();result.directory!==void 0&&process.env.NODE_COMPILE_CACHE===void 0&&(process.env.NODE_COMPILE_CACHE=result.directory)}catch{}}}export{enableStartupCompileCache};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{MAX_SOCKET_PATH_BYTES}from"./chunk-HRPMLI7O.js";import{SOCKET_SECRET_BYTES,authenticateSocket,readSocketSecret,resolveSocketTransportAddress,sendSocketHandshake,socketSecretPath}from"./chunk-QEQG42HL.js";var TOOL_CHILD_LIMITS={frameBytes:16777216,jobs:32,wakePending:64,updatesInFlight:64,wakePendingBytes:1048576,HOST_CALL_ACTIVE:8,HOST_CALL_WAITING:256,REF_THRESHOLD:4194304,refAckMs:3e4,refsBudget:1073741824,tracePerSecond:1024},LAUNCH_CONTAINMENTS=["own-group","inherited","job","breakaway"],LAUNCH_LIFETIMES=["owned","detached"],LAUNCH_ORIGINS=["tool","terminal","user_bash","interpreter","monitor","kernel"],LAUNCH_APIS=["bun.spawn","bun.spawnSync","bun.shell","cp.spawn","cp.spawnSync","cp.execFileSync","pty.spawn"],REF_NACK_REASONS=["cancelled","corrupt","missing","budget","foreign"],HOST_CALL_KINDS=["tool","completion","reserved","list_tools","authorize","lock_file","unlock_file"],TRACE_RESERVED_KEYS=["value","valueRef","result","ref","args","argsRef","ackTick","invokeTick"];import{chmod as chmod2,rm as rm2,rmdir}from"node:fs/promises";import{createConnection,createServer}from"node:net";import{dirname as dirname2}from"node:path";var req=kind=>({kind}),opt=kind=>({kind,optional:!0}),SHAPES={hello_ack:{generation:req("number")},job:{jobId:req("string"),kind:req("string"),parentJobId:opt("string"),args:opt("json"),argsRef:opt("ref")},cancel:{jobId:req("string")},host_reply:{callId:req("string"),value:opt("json"),valueRef:opt("ref"),error:opt("string")},host_call_refused:{callId:req("string"),code:req("string")},launch_ack:{launchId:req("string")},wake_ack:{wakeId:req("string")},slot_query:{launchId:req("string")},ping:{seq:req("number")},update_ack:{jobId:req("string"),seq:req("number")},shutdown:{},hello:{generation:req("number"),pid:req("number"),protocol:req("number"),jobKinds:req("strings")},heartbeat:{seq:req("number")},pong:{seq:req("number")},cancel_ack:{jobId:req("string")},result:{jobId:req("string"),value:req("json")},result_ref:{jobId:req("string"),ref:req("ref")},job_error:{jobId:req("string"),code:req("string"),message:req("string")},job_refused:{jobId:req("string"),code:req("string")},host_call:{callId:req("string"),jobId:req("string"),kind:req(HOST_CALL_KINDS),name:opt("string"),args:opt("json"),argsRef:opt("ref")},launch:{launchId:req("string"),jobId:req("string"),origin:req(LAUNCH_ORIGINS),api:req(LAUNCH_APIS),containment:req(LAUNCH_CONTAINMENTS),lifetime:req(LAUNCH_LIFETIMES),pid:opt("number"),pgid:opt("number")},launch_proof:{launchId:req("string"),ackTick:req("number|null"),invokeTick:req("number")},launch_done:{launchId:req("string"),exitCode:opt("number"),signal:opt("string")},command_started:{launchId:req("string"),pid:req("number"),startedAtMs:req("number"),creatorInJob:req("boolean"),breakaway:req("boolean")},slot_state:{launchId:req("string"),ackTick:req("number|null"),invokeTick:req("number|null"),invocations:req("number")},wake:{wakeId:req("string"),source:req("string"),bytes:req("number"),value:opt("json")},batch:{jobId:req("string"),entries:req("entries"),done:req("boolean")},update:{jobId:req("string"),seq:req("number"),value:opt("json"),valueRef:opt("ref")},trace:{atMs:req("number"),event:req("string"),detail:opt("detail")},ref_ack:{refId:req("string")},ref_nack:{refId:req("string"),reason:req(REF_NACK_REASONS)}},TOOL_CHILD_MESSAGE_TYPES=Object.keys(SHAPES),REF_ID=/^[A-Za-z0-9_-]{1,64}$/,SHA256=/^[0-9a-f]{64}$/,RESERVED=new Set(TRACE_RESERVED_KEYS),isRecord=value=>typeof value=="object"&&value!==null&&!Array.isArray(value),isCount=value=>Number.isSafeInteger(value)&&Number(value)>=0,isRefId=value=>typeof value=="string"&&REF_ID.test(value);function isPayloadRef(value){return isRecord(value)&&isRefId(value.refId)&&isCount(value.generation)&&isCount(value.bytes)&&typeof value.sha256=="string"&&SHA256.test(value.sha256)}function isEntryMeta(value){return isRecord(value)&&typeof value.id=="string"&&(value.parentId===null||typeof value.parentId=="string")&&isCount(value.lineOffset)&&isCount(value.lineBytes)&&Number.isSafeInteger(value.lineDelta)}function isTraceDetail(value){return isRecord(value)?Object.entries(value).every(([key,item])=>!RESERVED.has(key)&&(typeof item=="string"||typeof item=="number"||typeof item=="boolean")):!1}function matches(kind,value){if(typeof kind!="string")return typeof value=="string"&&kind.includes(value);switch(kind){case"string":return typeof value=="string";case"number":return typeof value=="number"&&Number.isFinite(value);case"boolean":return typeof value=="boolean";case"json":return value!==void 0;case"ref":return isPayloadRef(value);case"entries":return Array.isArray(value)&&value.every(isEntryMeta);case"detail":return isTraceDetail(value);case"number|null":return value===null||typeof value=="number"&&Number.isFinite(value);case"strings":return Array.isArray(value)&&value.every(item=>typeof item=="string")}}var isMessageType=value=>typeof value=="string"&&Object.hasOwn(SHAPES,value);function isToolChildMessage(value){if(!isRecord(value)||!isMessageType(value.type))return!1;let rules=SHAPES[value.type];for(let key of Object.keys(value))if(key!=="type"&&!Object.hasOwn(rules,key))return!1;return Object.entries(rules).every(([key,rule])=>value[key]===void 0?rule.optional===!0:matches(rule.kind,value[key]))}var ToolChildCodecError=class extends Error{constructor(code,message){super(`${code}: ${message}`),this.name="ToolChildCodecError",this.code=code}},FRAME_HEADER_BYTES=4,tooLarge=bytes=>new ToolChildCodecError("tool_child_frame_too_large",`${bytes} bytes exceeds ${TOOL_CHILD_LIMITS.frameBytes}`);function encodeFrame(message){let normalized=message.type==="result"&&message.value===void 0?{...message,value:null}:message,json=JSON.stringify(normalized),bodyBytes=Buffer.byteLength(json,"utf8");if(bodyBytes>TOOL_CHILD_LIMITS.frameBytes)throw tooLarge(bodyBytes);if(!isToolChildMessage(JSON.parse(json)))throw new ToolChildCodecError("tool_child_encode_invalid","the message serializes to a frame the peer's guard rejects");return frameFromBody(json,bodyBytes)}function frameFromBody(json,bodyBytes){let frame=Buffer.allocUnsafe(FRAME_HEADER_BYTES+bodyBytes);return frame.writeUInt32BE(bodyBytes,0),frame.write(json,FRAME_HEADER_BYTES,"utf8"),frame}function parseBody(body){let value;try{value=JSON.parse(body.toString("utf8"))}catch{throw new ToolChildCodecError("tool_child_malformed","frame body is not JSON")}if(!isToolChildMessage(value))throw new ToolChildCodecError("tool_child_malformed","frame is not a protocol message");return value}var FrameDecoder=class{#chunks=[];#buffered=0;#bodyBytes;#failure;get bufferedBytes(){return this.#buffered}push(chunk){if(this.#failure)throw this.#failure;chunk.length>0&&(this.#chunks.push(Buffer.from(chunk.buffer,chunk.byteOffset,chunk.byteLength)),this.#buffered+=chunk.length);let messages=[];try{for(;;){if(this.#bodyBytes===void 0){if(this.#buffered<FRAME_HEADER_BYTES)break;let length=this.#take(FRAME_HEADER_BYTES).readUInt32BE(0);if(length>TOOL_CHILD_LIMITS.frameBytes)throw tooLarge(length);this.#bodyBytes=length}if(this.#buffered<this.#bodyBytes)break;let body=this.#take(this.#bodyBytes);this.#bodyBytes=void 0,messages.push(parseBody(body))}}catch(error){throw error instanceof ToolChildCodecError&&this.#fail(error),error}return messages}end(){if(this.#failure)throw this.#failure;if(this.#buffered===0&&this.#bodyBytes===void 0)return;let truncated=new ToolChildCodecError("tool_child_truncated",`stream ended inside a frame (${this.#buffered} of ${this.#bodyBytes??FRAME_HEADER_BYTES} bytes)`);throw this.#fail(truncated),truncated}#fail(error){this.#failure=error,this.#chunks=[],this.#buffered=0,this.#bodyBytes=void 0}#take(bytes){let first=this.#chunks[0];if(first!==void 0&&first.length>=bytes){let taken=first.subarray(0,bytes);return first.length===bytes?this.#chunks.shift():this.#chunks[0]=first.subarray(bytes),this.#buffered-=bytes,taken}let out=Buffer.allocUnsafe(bytes),filled=0;for(;filled<bytes;){let chunk=this.#chunks[0];if(chunk===void 0)break;let count=Math.min(chunk.length,bytes-filled);chunk.copy(out,filled,0,count),filled+=count,count===chunk.length?this.#chunks.shift():this.#chunks[0]=chunk.subarray(count)}return this.#buffered-=bytes,out}};import{createHash}from"node:crypto";import{constants as constants2}from"node:fs";import{chmod,lstat,mkdir,mkdtemp,open as open2,rm}from"node:fs/promises";import{dirname,join}from"node:path";import{randomBytes}from"node:crypto";import{constants}from"node:fs";import{open}from"node:fs/promises";async function writeExclusiveSecret(path,onCreated){let secret=randomBytes(SOCKET_SECRET_BYTES),flags=constants.O_WRONLY|constants.O_CREAT|constants.O_EXCL|(constants.O_NOFOLLOW??0),handle=await open(path,flags,384);try{let stat=await handle.stat();return onCreated({dev:stat.dev,ino:stat.ino}),await handle.writeFile(secret),await handle.chmod(384),secret}finally{await handle.close()}}async function channelSocketPath(dir,generation){let name=`c-${generation}.sock`,primary=join(dir,name);if(process.platform==="win32"||Buffer.byteLength(primary)<=MAX_SOCKET_PATH_BYTES)return await makePrivateDirectory(dir),primary;let root=fallbackSocketRoot(dir),claimed=await claimPrivateDirectory(root).then(()=>!0,error=>{if(error instanceof SocketDirUnsafeError)return!1;throw error});return join(claimed?root:await mkdtemp(`${root}-`),name)}function fallbackSocketRoot(dir){return join("/tmp",`senpi-tc-${createHash("sha256").update(dir).digest("hex").slice(0,12)}`)}var SocketDirUnsafeError=class extends Error{constructor(root){super(`socket_dir_unsafe: ${root} is not a private directory of this user`)}};async function claimPrivateDirectory(root,create=!0){create&&await mkdir(root,{mode:448}).catch(error=>{if(Reflect.get(Object(error),"code")!=="EEXIST")throw error});let unsafe=()=>new SocketDirUnsafeError(root),handle=await open2(root,constants2.O_RDONLY|constants2.O_DIRECTORY|constants2.O_NOFOLLOW).catch(()=>{throw unsafe()});try{let stat=await handle.stat();if(!stat.isDirectory()||process.getuid!==void 0&&stat.uid!==process.getuid())throw unsafe();(stat.mode&63)!==0&&await handle.chmod(448)}finally{await handle.close()}}async function makePrivateDirectory(directory){await mkdir(directory,{recursive:!0,mode:448}),await chmod(directory,448)}async function claimListenerFiles(dir,generation){for(let attempt=1;;attempt++){let logicalPath=await channelSocketPath(dir,generation),root=dirname(logicalPath),secretPath=socketSecretPath(logicalPath),created;try{let secret=await writeSecret(secretPath,root,dir,identity=>{created=identity});return await proveRoot(root,dir),await proveOwnFile(secretPath,root),await rm(logicalPath,{force:!0}),{logicalPath,secretPath,secret}}catch(error){if(created!==void 0&&await withdraw({secretPath,root,dir,created}),attempt>=2||!(root!==dir&&foreignRootError(error)))throw error}}}async function writeSecret(secretPath,root,dir,onCreated){try{return await writeExclusiveSecret(secretPath,onCreated)}catch(error){if(Reflect.get(Object(error),"code")!=="EEXIST")throw error;return await proveRoot(root,dir),await proveOwnFile(secretPath,root),await rm(secretPath,{force:!0}),writeExclusiveSecret(secretPath,onCreated)}}function proveRoot(root,dir,create=!0){return root===dir?makePrivateDirectory(root):claimPrivateDirectory(root,create)}async function withdraw(target){try{if(!await emptyCreatedFile(target))return warnWithdrawal(target,"foreign","the entry is not the file this claim created; it was left untouched");if(await proveRoot(target.root,target.dir,!1),!sameFile(await lstat(target.secretPath),target.created))return warnWithdrawal(target,"foreign","our file was emptied, then replaced by another entry, left untouched");await rm(target.secretPath,{force:!0})}catch(cleanup){warnWithdrawal(target,"unknown",String(Reflect.get(Object(cleanup),"code")??cleanup))}}function warnWithdrawal(target,state,reason){process.emitWarning("tool-child socket secret withdrawal stopped short",{code:"SENPI_TOOL_CHILD_SECRET_WITHDRAWAL",detail:JSON.stringify({path:target.secretPath,state,reason})})}async function emptyCreatedFile(target){let flags=constants2.O_WRONLY|constants2.O_NONBLOCK|(constants2.O_NOFOLLOW??0),handle=await open2(target.secretPath,flags);try{return sameFile(await handle.stat(),target.created)?(await handle.truncate(0),!0):!1}finally{await handle.close()}}function sameFile(entry,created){return entry.dev===created.dev&&entry.ino===created.ino}var FOREIGN_ROOT_CODES=new Set(["EACCES","EPERM","ENOENT","ENOTDIR","ELOOP"]);function foreignRootError(error){return error instanceof SocketDirUnsafeError||FOREIGN_ROOT_CODES.has(String(Reflect.get(Object(error),"code")))}async function proveOwnFile(path,root){let stat=await lstat(path);if(!stat.isFile()||process.getuid!==void 0&&stat.uid!==process.getuid())throw new SocketDirUnsafeError(root)}var TOOL_CHILD_ROLE_ENV="SENPI_INTERNAL_ROLE",TOOL_CHILD_CHANNEL_ENV="SENPI_TOOL_CHILD_CHANNEL",TOOL_CHILD_SECRET_ENV="SENPI_TOOL_CHILD_CHANNEL_SECRET";function wrapChannelSocket(socket){let decoder=new FrameDecoder,listeners=[],early=[],settle,closed=new Promise(resolve=>{settle=resolve}),reason,finish=next=>{reason===void 0&&(reason=next,socket.destroy(),settle(next))};return socket.on("data",chunk=>{if(reason===void 0)try{for(let message of decoder.push(chunk))if(listeners.length===0)early.push(message);else for(let listener of listeners)listener(message)}catch(error){finish(error instanceof ToolChildCodecError?{kind:"codec",code:error.code}:{kind:"error",message:String(error)})}}),socket.on("end",()=>{try{decoder.end(),finish({kind:"eof"})}catch(error){finish(error instanceof ToolChildCodecError?{kind:"codec",code:error.code}:{kind:"error",message:String(error)})}}),socket.on("error",error=>finish({kind:"error",message:error.message})),socket.on("close",()=>finish({kind:"eof"})),{send:message=>reason===void 0&&socket.write(encodeFrame(message)),onMessage:listener=>{listeners.push(listener);for(let message of early.splice(0))listener(message);return()=>{let index=listeners.indexOf(listener);index>=0&&listeners.splice(index,1)}},closed,close:()=>finish({kind:"eof"})}}async function openChannelListener(options){let{logicalPath,secretPath,secret}=await claimListenerFiles(options.dir,options.generation),resolveAccepted,accepted=new Promise(resolve=>{resolveAccepted=resolve}),taken=!1,server=createServer(socket=>{if(taken){socket.destroy();return}authenticateSocket(socket,secret,()=>{if(taken){socket.destroy();return}taken=!0,server.close(),resolveAccepted(wrapChannelSocket(socket))})}),address=resolveSocketTransportAddress(logicalPath,process.platform,secret);return await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(address,()=>{server.off("error",reject),resolve()})}),process.platform!=="win32"&&await chmod2(logicalPath,384),{env:{[TOOL_CHILD_CHANNEL_ENV]:logicalPath,[TOOL_CHILD_SECRET_ENV]:secretPath},accepted,close:async()=>{taken=!0,server.listening&&server.close(),process.platform!=="win32"&&await rm2(logicalPath,{force:!0}),await rm2(secretPath,{force:!0}),await rmdir(dirname2(logicalPath)).catch(()=>{})}}}function channelEnvPresent(env){return!!env[TOOL_CHILD_CHANNEL_ENV]&&!!env[TOOL_CHILD_SECRET_ENV]}async function connectChannel(env){let logicalPath=env[TOOL_CHILD_CHANNEL_ENV],secretPath=env[TOOL_CHILD_SECRET_ENV];if(!logicalPath||!secretPath)throw new Error("tool-child: channel unavailable");let secret=await readSocketSecret(secretPath),socket=createConnection(resolveSocketTransportAddress(logicalPath,process.platform,secret));return await new Promise((resolve,reject)=>{socket.once("connect",resolve),socket.once("error",reject)}),sendSocketHandshake(socket,secret),wrapChannelSocket(socket)}export{TOOL_CHILD_LIMITS,HOST_CALL_KINDS,isRefId,TOOL_CHILD_ROLE_ENV,openChannelListener,channelEnvPresent,connectChannel};
|
|
2
|
+
import{MAX_SOCKET_PATH_BYTES}from"./chunk-VRDU425E.js";import{SOCKET_SECRET_BYTES,authenticateSocket,readSocketSecret,resolveSocketTransportAddress,sendSocketHandshake,socketSecretPath}from"./chunk-ZGAB7WQA.js";var TOOL_CHILD_LIMITS={frameBytes:16777216,jobs:32,wakePending:64,updatesInFlight:64,wakePendingBytes:1048576,HOST_CALL_ACTIVE:8,HOST_CALL_WAITING:256,REF_THRESHOLD:4194304,refAckMs:3e4,refsBudget:1073741824,tracePerSecond:1024},LAUNCH_CONTAINMENTS=["own-group","inherited","job","breakaway"],LAUNCH_LIFETIMES=["owned","detached"],LAUNCH_ORIGINS=["tool","terminal","user_bash","interpreter","monitor","kernel"],LAUNCH_APIS=["bun.spawn","bun.spawnSync","bun.shell","cp.spawn","cp.spawnSync","cp.execFileSync","pty.spawn"],REF_NACK_REASONS=["cancelled","corrupt","missing","budget","foreign"],HOST_CALL_KINDS=["tool","completion","reserved","list_tools","authorize","lock_file","unlock_file"],TRACE_RESERVED_KEYS=["value","valueRef","result","ref","args","argsRef","ackTick","invokeTick"];import{chmod as chmod2,rm as rm2,rmdir}from"node:fs/promises";import{createConnection,createServer}from"node:net";import{dirname as dirname2}from"node:path";var req=kind=>({kind}),opt=kind=>({kind,optional:!0}),SHAPES={hello_ack:{generation:req("number")},job:{jobId:req("string"),kind:req("string"),parentJobId:opt("string"),args:opt("json"),argsRef:opt("ref")},cancel:{jobId:req("string")},host_reply:{callId:req("string"),value:opt("json"),valueRef:opt("ref"),error:opt("string")},host_call_refused:{callId:req("string"),code:req("string")},launch_ack:{launchId:req("string")},wake_ack:{wakeId:req("string")},slot_query:{launchId:req("string")},ping:{seq:req("number")},update_ack:{jobId:req("string"),seq:req("number")},shutdown:{},hello:{generation:req("number"),pid:req("number"),protocol:req("number"),jobKinds:req("strings")},heartbeat:{seq:req("number")},pong:{seq:req("number")},cancel_ack:{jobId:req("string")},result:{jobId:req("string"),value:req("json")},result_ref:{jobId:req("string"),ref:req("ref")},job_error:{jobId:req("string"),code:req("string"),message:req("string")},job_refused:{jobId:req("string"),code:req("string")},host_call:{callId:req("string"),jobId:req("string"),kind:req(HOST_CALL_KINDS),name:opt("string"),args:opt("json"),argsRef:opt("ref")},launch:{launchId:req("string"),jobId:req("string"),origin:req(LAUNCH_ORIGINS),api:req(LAUNCH_APIS),containment:req(LAUNCH_CONTAINMENTS),lifetime:req(LAUNCH_LIFETIMES),pid:opt("number"),pgid:opt("number")},launch_proof:{launchId:req("string"),ackTick:req("number|null"),invokeTick:req("number")},launch_done:{launchId:req("string"),exitCode:opt("number"),signal:opt("string")},command_started:{launchId:req("string"),pid:req("number"),startedAtMs:req("number"),creatorInJob:req("boolean"),breakaway:req("boolean")},slot_state:{launchId:req("string"),ackTick:req("number|null"),invokeTick:req("number|null"),invocations:req("number")},wake:{wakeId:req("string"),source:req("string"),bytes:req("number"),value:opt("json")},batch:{jobId:req("string"),entries:req("entries"),done:req("boolean")},update:{jobId:req("string"),seq:req("number"),value:opt("json"),valueRef:opt("ref")},trace:{atMs:req("number"),event:req("string"),detail:opt("detail")},ref_ack:{refId:req("string")},ref_nack:{refId:req("string"),reason:req(REF_NACK_REASONS)}},TOOL_CHILD_MESSAGE_TYPES=Object.keys(SHAPES),REF_ID=/^[A-Za-z0-9_-]{1,64}$/,SHA256=/^[0-9a-f]{64}$/,RESERVED=new Set(TRACE_RESERVED_KEYS),isRecord=value=>typeof value=="object"&&value!==null&&!Array.isArray(value),isCount=value=>Number.isSafeInteger(value)&&Number(value)>=0,isRefId=value=>typeof value=="string"&&REF_ID.test(value);function isPayloadRef(value){return isRecord(value)&&isRefId(value.refId)&&isCount(value.generation)&&isCount(value.bytes)&&typeof value.sha256=="string"&&SHA256.test(value.sha256)}function isEntryMeta(value){return isRecord(value)&&typeof value.id=="string"&&(value.parentId===null||typeof value.parentId=="string")&&isCount(value.lineOffset)&&isCount(value.lineBytes)&&Number.isSafeInteger(value.lineDelta)}function isTraceDetail(value){return isRecord(value)?Object.entries(value).every(([key,item])=>!RESERVED.has(key)&&(typeof item=="string"||typeof item=="number"||typeof item=="boolean")):!1}function matches(kind,value){if(typeof kind!="string")return typeof value=="string"&&kind.includes(value);switch(kind){case"string":return typeof value=="string";case"number":return typeof value=="number"&&Number.isFinite(value);case"boolean":return typeof value=="boolean";case"json":return value!==void 0;case"ref":return isPayloadRef(value);case"entries":return Array.isArray(value)&&value.every(isEntryMeta);case"detail":return isTraceDetail(value);case"number|null":return value===null||typeof value=="number"&&Number.isFinite(value);case"strings":return Array.isArray(value)&&value.every(item=>typeof item=="string")}}var isMessageType=value=>typeof value=="string"&&Object.hasOwn(SHAPES,value);function isToolChildMessage(value){if(!isRecord(value)||!isMessageType(value.type))return!1;let rules=SHAPES[value.type];for(let key of Object.keys(value))if(key!=="type"&&!Object.hasOwn(rules,key))return!1;return Object.entries(rules).every(([key,rule])=>value[key]===void 0?rule.optional===!0:matches(rule.kind,value[key]))}var ToolChildCodecError=class extends Error{constructor(code,message){super(`${code}: ${message}`),this.name="ToolChildCodecError",this.code=code}},FRAME_HEADER_BYTES=4,tooLarge=bytes=>new ToolChildCodecError("tool_child_frame_too_large",`${bytes} bytes exceeds ${TOOL_CHILD_LIMITS.frameBytes}`);function encodeFrame(message){let normalized=message.type==="result"&&message.value===void 0?{...message,value:null}:message,json=JSON.stringify(normalized),bodyBytes=Buffer.byteLength(json,"utf8");if(bodyBytes>TOOL_CHILD_LIMITS.frameBytes)throw tooLarge(bodyBytes);if(!isToolChildMessage(JSON.parse(json)))throw new ToolChildCodecError("tool_child_encode_invalid","the message serializes to a frame the peer's guard rejects");return frameFromBody(json,bodyBytes)}function frameFromBody(json,bodyBytes){let frame=Buffer.allocUnsafe(FRAME_HEADER_BYTES+bodyBytes);return frame.writeUInt32BE(bodyBytes,0),frame.write(json,FRAME_HEADER_BYTES,"utf8"),frame}function parseBody(body){let value;try{value=JSON.parse(body.toString("utf8"))}catch{throw new ToolChildCodecError("tool_child_malformed","frame body is not JSON")}if(!isToolChildMessage(value))throw new ToolChildCodecError("tool_child_malformed","frame is not a protocol message");return value}var FrameDecoder=class{#chunks=[];#buffered=0;#bodyBytes;#failure;get bufferedBytes(){return this.#buffered}push(chunk){if(this.#failure)throw this.#failure;chunk.length>0&&(this.#chunks.push(Buffer.from(chunk.buffer,chunk.byteOffset,chunk.byteLength)),this.#buffered+=chunk.length);let messages=[];try{for(;;){if(this.#bodyBytes===void 0){if(this.#buffered<FRAME_HEADER_BYTES)break;let length=this.#take(FRAME_HEADER_BYTES).readUInt32BE(0);if(length>TOOL_CHILD_LIMITS.frameBytes)throw tooLarge(length);this.#bodyBytes=length}if(this.#buffered<this.#bodyBytes)break;let body=this.#take(this.#bodyBytes);this.#bodyBytes=void 0,messages.push(parseBody(body))}}catch(error){throw error instanceof ToolChildCodecError&&this.#fail(error),error}return messages}end(){if(this.#failure)throw this.#failure;if(this.#buffered===0&&this.#bodyBytes===void 0)return;let truncated=new ToolChildCodecError("tool_child_truncated",`stream ended inside a frame (${this.#buffered} of ${this.#bodyBytes??FRAME_HEADER_BYTES} bytes)`);throw this.#fail(truncated),truncated}#fail(error){this.#failure=error,this.#chunks=[],this.#buffered=0,this.#bodyBytes=void 0}#take(bytes){let first=this.#chunks[0];if(first!==void 0&&first.length>=bytes){let taken=first.subarray(0,bytes);return first.length===bytes?this.#chunks.shift():this.#chunks[0]=first.subarray(bytes),this.#buffered-=bytes,taken}let out=Buffer.allocUnsafe(bytes),filled=0;for(;filled<bytes;){let chunk=this.#chunks[0];if(chunk===void 0)break;let count=Math.min(chunk.length,bytes-filled);chunk.copy(out,filled,0,count),filled+=count,count===chunk.length?this.#chunks.shift():this.#chunks[0]=chunk.subarray(count)}return this.#buffered-=bytes,out}};import{createHash}from"node:crypto";import{constants as constants2}from"node:fs";import{chmod,lstat,mkdir,mkdtemp,open as open2,rm}from"node:fs/promises";import{dirname,join}from"node:path";import{randomBytes}from"node:crypto";import{constants}from"node:fs";import{open}from"node:fs/promises";async function writeExclusiveSecret(path,onCreated){let secret=randomBytes(SOCKET_SECRET_BYTES),flags=constants.O_WRONLY|constants.O_CREAT|constants.O_EXCL|(constants.O_NOFOLLOW??0),handle=await open(path,flags,384);try{let stat=await handle.stat();return onCreated({dev:stat.dev,ino:stat.ino}),await handle.writeFile(secret),await handle.chmod(384),secret}finally{await handle.close()}}async function channelSocketPath(dir,generation){let name=`c-${generation}.sock`,primary=join(dir,name);if(process.platform==="win32"||Buffer.byteLength(primary)<=MAX_SOCKET_PATH_BYTES)return await makePrivateDirectory(dir),primary;let root=fallbackSocketRoot(dir),claimed=await claimPrivateDirectory(root).then(()=>!0,error=>{if(error instanceof SocketDirUnsafeError)return!1;throw error});return join(claimed?root:await mkdtemp(`${root}-`),name)}function fallbackSocketRoot(dir){return join("/tmp",`senpi-tc-${createHash("sha256").update(dir).digest("hex").slice(0,12)}`)}var SocketDirUnsafeError=class extends Error{constructor(root){super(`socket_dir_unsafe: ${root} is not a private directory of this user`)}};async function claimPrivateDirectory(root,create=!0){create&&await mkdir(root,{mode:448}).catch(error=>{if(Reflect.get(Object(error),"code")!=="EEXIST")throw error});let unsafe=()=>new SocketDirUnsafeError(root),handle=await open2(root,constants2.O_RDONLY|constants2.O_DIRECTORY|constants2.O_NOFOLLOW).catch(()=>{throw unsafe()});try{let stat=await handle.stat();if(!stat.isDirectory()||process.getuid!==void 0&&stat.uid!==process.getuid())throw unsafe();(stat.mode&63)!==0&&await handle.chmod(448)}finally{await handle.close()}}async function makePrivateDirectory(directory){await mkdir(directory,{recursive:!0,mode:448}),await chmod(directory,448)}async function claimListenerFiles(dir,generation){for(let attempt=1;;attempt++){let logicalPath=await channelSocketPath(dir,generation),root=dirname(logicalPath),secretPath=socketSecretPath(logicalPath),created;try{let secret=await writeSecret(secretPath,root,dir,identity=>{created=identity});return await proveRoot(root,dir),await proveOwnFile(secretPath,root),await rm(logicalPath,{force:!0}),{logicalPath,secretPath,secret}}catch(error){if(created!==void 0&&await withdraw({secretPath,root,dir,created}),attempt>=2||!(root!==dir&&foreignRootError(error)))throw error}}}async function writeSecret(secretPath,root,dir,onCreated){try{return await writeExclusiveSecret(secretPath,onCreated)}catch(error){if(Reflect.get(Object(error),"code")!=="EEXIST")throw error;return await proveRoot(root,dir),await proveOwnFile(secretPath,root),await rm(secretPath,{force:!0}),writeExclusiveSecret(secretPath,onCreated)}}function proveRoot(root,dir,create=!0){return root===dir?makePrivateDirectory(root):claimPrivateDirectory(root,create)}async function withdraw(target){try{if(!await emptyCreatedFile(target))return warnWithdrawal(target,"foreign","the entry is not the file this claim created; it was left untouched");if(await proveRoot(target.root,target.dir,!1),!sameFile(await lstat(target.secretPath),target.created))return warnWithdrawal(target,"foreign","our file was emptied, then replaced by another entry, left untouched");await rm(target.secretPath,{force:!0})}catch(cleanup){warnWithdrawal(target,"unknown",String(Reflect.get(Object(cleanup),"code")??cleanup))}}function warnWithdrawal(target,state,reason){process.emitWarning("tool-child socket secret withdrawal stopped short",{code:"SENPI_TOOL_CHILD_SECRET_WITHDRAWAL",detail:JSON.stringify({path:target.secretPath,state,reason})})}async function emptyCreatedFile(target){let flags=constants2.O_WRONLY|constants2.O_NONBLOCK|(constants2.O_NOFOLLOW??0),handle=await open2(target.secretPath,flags);try{return sameFile(await handle.stat(),target.created)?(await handle.truncate(0),!0):!1}finally{await handle.close()}}function sameFile(entry,created){return entry.dev===created.dev&&entry.ino===created.ino}var FOREIGN_ROOT_CODES=new Set(["EACCES","EPERM","ENOENT","ENOTDIR","ELOOP"]);function foreignRootError(error){return error instanceof SocketDirUnsafeError||FOREIGN_ROOT_CODES.has(String(Reflect.get(Object(error),"code")))}async function proveOwnFile(path,root){let stat=await lstat(path);if(!stat.isFile()||process.getuid!==void 0&&stat.uid!==process.getuid())throw new SocketDirUnsafeError(root)}var TOOL_CHILD_ROLE_ENV="SENPI_INTERNAL_ROLE",TOOL_CHILD_CHANNEL_ENV="SENPI_TOOL_CHILD_CHANNEL",TOOL_CHILD_SECRET_ENV="SENPI_TOOL_CHILD_CHANNEL_SECRET";function wrapChannelSocket(socket){let decoder=new FrameDecoder,listeners=[],early=[],settle,closed=new Promise(resolve=>{settle=resolve}),reason,finish=next=>{reason===void 0&&(reason=next,socket.destroy(),settle(next))};return socket.on("data",chunk=>{if(reason===void 0)try{for(let message of decoder.push(chunk))if(listeners.length===0)early.push(message);else for(let listener of listeners)listener(message)}catch(error){finish(error instanceof ToolChildCodecError?{kind:"codec",code:error.code}:{kind:"error",message:String(error)})}}),socket.on("end",()=>{try{decoder.end(),finish({kind:"eof"})}catch(error){finish(error instanceof ToolChildCodecError?{kind:"codec",code:error.code}:{kind:"error",message:String(error)})}}),socket.on("error",error=>finish({kind:"error",message:error.message})),socket.on("close",()=>finish({kind:"eof"})),{send:message=>reason===void 0&&socket.write(encodeFrame(message)),onMessage:listener=>{listeners.push(listener);for(let message of early.splice(0))listener(message);return()=>{let index=listeners.indexOf(listener);index>=0&&listeners.splice(index,1)}},closed,close:()=>finish({kind:"eof"})}}async function openChannelListener(options){let{logicalPath,secretPath,secret}=await claimListenerFiles(options.dir,options.generation),resolveAccepted,accepted=new Promise(resolve=>{resolveAccepted=resolve}),taken=!1,server=createServer(socket=>{if(taken){socket.destroy();return}authenticateSocket(socket,secret,()=>{if(taken){socket.destroy();return}taken=!0,server.close(),resolveAccepted(wrapChannelSocket(socket))})}),address=resolveSocketTransportAddress(logicalPath,process.platform,secret);return await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(address,()=>{server.off("error",reject),resolve()})}),process.platform!=="win32"&&await chmod2(logicalPath,384),{env:{[TOOL_CHILD_CHANNEL_ENV]:logicalPath,[TOOL_CHILD_SECRET_ENV]:secretPath},accepted,close:async()=>{taken=!0,server.listening&&server.close(),process.platform!=="win32"&&await rm2(logicalPath,{force:!0}),await rm2(secretPath,{force:!0}),await rmdir(dirname2(logicalPath)).catch(()=>{})}}}function channelEnvPresent(env){return!!env[TOOL_CHILD_CHANNEL_ENV]&&!!env[TOOL_CHILD_SECRET_ENV]}async function connectChannel(env){let logicalPath=env[TOOL_CHILD_CHANNEL_ENV],secretPath=env[TOOL_CHILD_SECRET_ENV];if(!logicalPath||!secretPath)throw new Error("tool-child: channel unavailable");let secret=await readSocketSecret(secretPath),socket=createConnection(resolveSocketTransportAddress(logicalPath,process.platform,secret));return await new Promise((resolve,reject)=>{socket.once("connect",resolve),socket.once("error",reject)}),sendSocketHandshake(socket,secret),wrapChannelSocket(socket)}export{TOOL_CHILD_LIMITS,HOST_CALL_KINDS,isRefId,TOOL_CHILD_ROLE_ENV,openChannelListener,channelEnvPresent,connectChannel};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{resolveMovedPath}from"./chunk-CNPDUC4D.js";import{advanceSessionFence}from"./chunk-SOMFLLPD.js";import{killWindowsProcessTree}from"./chunk-
|
|
2
|
+
import{resolveMovedPath}from"./chunk-CNPDUC4D.js";import{advanceSessionFence}from"./chunk-SOMFLLPD.js";import{killWindowsProcessTree}from"./chunk-BWA4FVFW.js";import{APP_NAME}from"./chunk-7YO7VQZW.js";import{ownProcessStartedAtMs,processBootAtMs,sameBoot}from"./chunk-QN2BHP3M.js";import{processStartTimeMs,sameProcessStartMs}from"./chunk-7ASLQ243.js";import{randomUUID}from"node:crypto";import{mkdir,readdir,readFile,rename,rm,stat,writeFile}from"node:fs/promises";import{dirname,join}from"node:path";var InvalidScheduledJobError=class extends Error{constructor(message){super(message),this.name="InvalidScheduledJobError"}};function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function requireString(raw,key){let value=raw[key];if(typeof value!="string"||value.length===0)throw new InvalidScheduledJobError(`scheduled job field "${key}" must be a non-empty string`);return value}function requireCount(raw,key){let value=raw[key];if(typeof value!="number"||!Number.isSafeInteger(value)||value<0)throw new InvalidScheduledJobError(`scheduled job field "${key}" must be a non-negative integer`);return value}function nullableCount(raw,key){return raw[key]===null||raw[key]===void 0?null:requireCount(raw,key)}function nullableString(raw,key){let value=raw[key];if(value==null)return null;if(typeof value!="string")throw new InvalidScheduledJobError(`scheduled job field "${key}" must be a string`);return value}function parseScheduledJob(raw){if(!isRecord(raw))throw new InvalidScheduledJobError("scheduled job must be a JSON object");if(raw.version!==1)throw new InvalidScheduledJobError(`unsupported scheduled job version: ${JSON.stringify(raw.version)}`);let id=requireString(raw,"id");if(!/^sch_[a-z0-9]{12}$/.test(id))throw new InvalidScheduledJobError(`invalid scheduled job id: ${id}`);let everyMs=nullableCount(raw,"everyMs");if(everyMs!==null&&everyMs<60*1e3)throw new InvalidScheduledJobError("scheduled job recurrence must be at least 60s");if(everyMs!==null&&everyMs>316224e5)throw new InvalidScheduledJobError("scheduled job recurrence must be at most 366 days");let prompt=requireString(raw,"prompt");if(Buffer.byteLength(prompt,"utf8")>16384)throw new InvalidScheduledJobError("scheduled job prompt exceeds 16384 bytes");let createdAt=requireCount(raw,"createdAt"),dueAt=requireCount(raw,"dueAt");return{version:1,id,sessionId:requireString(raw,"sessionId"),sessionFile:nullableString(raw,"sessionFile"),cwd:requireString(raw,"cwd"),prompt,createdAt,dueAt,everyMs,fireCount:requireCount(raw,"fireCount"),lastFiredAt:nullableCount(raw,"lastFiredAt"),lastError:nullableString(raw,"lastError")}}function nextRecurringDueAt(dueAt,everyMs,now){if(dueAt>now)return dueAt;let missed=Math.floor((now-dueAt)/everyMs)+1;return dueAt+missed*everyMs}function formatScheduledMessage(job,firedAt){let recurrence=job.everyMs===null?"":`, repeats every ${Math.round(job.everyMs/1e3)}s`;return`[Scheduled prompt ${job.id}: created ${new Date(job.createdAt).toISOString()}, due ${new Date(job.dueAt).toISOString()}, fired ${new Date(firedAt).toISOString()}${recurrence}]
|
|
3
3
|
${job.prompt}`}var STATE_DIRS=["pending","firing","failed"],JOB_ID=/^sch_[a-z0-9]{12}$/,RECORD_FILE=/^(sch_[a-z0-9]{12})(?:@(\d+)(?:~(\d+)-(\d+))?)?\.json$/;function scheduleDir(agentDir){return join(agentDir,"schedule")}function isScheduledJobId(id){return JOB_ID.test(id)}function isFileSystemError(error,code){return error instanceof Error&&"code"in error&&error.code===code}async function ensureDir(dir){await mkdir(dir,{recursive:!0,mode:448})}async function writeAtomic(filePath,contents){await ensureDir(dirname(filePath));let tempPath=join(dirname(filePath),`.schedule-${randomUUID()}.tmp`);try{await writeFile(tempPath,contents,{encoding:"utf8",mode:384}),await rename(tempPath,filePath)}catch(error){throw await rm(tempPath,{force:!0}),error}}function serialize(job){return`${JSON.stringify(job,null,2)}
|
|
4
4
|
`}async function readJobFile(path,expectedId){if((await stat(path)).size>65536)throw new InvalidScheduledJobError(`scheduled job file exceeds ${65536} bytes`);let parsed;try{parsed=JSON.parse(await readFile(path,"utf8"))}catch(error){throw isFileSystemError(error,"ENOENT")?error:new InvalidScheduledJobError("scheduled job file is not valid JSON")}let job=parseScheduledJob(parsed);if(job.id!==expectedId)throw new InvalidScheduledJobError(`scheduled job id ${job.id} does not match its file`);return job}async function listScheduledJobs(dir){let jobs=[],invalid=[];for(let state of STATE_DIRS){let names;try{names=await readdir(join(dir,state))}catch(error){if(isFileSystemError(error,"ENOENT"))continue;throw error}for(let name of names.sort()){let match=RECORD_FILE.exec(name);if(match===null)continue;let[,id,occurrence,pid,startedAt]=match,file=join(state,name);if(!(state==="pending"?occurrence===void 0:state==="firing"?pid!==void 0:occurrence!==void 0&&pid===void 0)||id===void 0){invalid.push({state,file,error:`unexpected record name for ${state}/`});continue}try{let job=await readJobFile(join(dir,file),id);jobs.push({state,job,file,...occurrence===void 0?{}:{occurrence:Number(occurrence)},...pid===void 0||startedAt===void 0?{}:{owner:{pid:Number(pid),processStartedAtMs:Number(startedAt)}}})}catch(error){if(isFileSystemError(error,"ENOENT"))continue;invalid.push({state,file,error:error instanceof Error?error.message:String(error)})}}}return jobs.sort((a,b)=>a.job.dueAt-b.job.dueAt),{jobs,invalid}}async function createScheduledJob(dir,input,now){let job=parseScheduledJob({version:1,id:`sch_${randomUUID().replace(/-/g,"").slice(0,12)}`,...input,createdAt:now,fireCount:0,lastFiredAt:null});return await writeAtomic(join(dir,"pending",`${job.id}.json`),serialize(job)),job}function tombstonePath(dir,id){return join(dir,"cancelled",id)}async function isCancelled(dir,id){try{return await stat(tombstonePath(dir,id)),!0}catch(error){if(isFileSystemError(error,"ENOENT"))return!1;throw error}}function runnersDir(dir){return join(dir,"runners")}import{randomUUID as randomUUID2}from"node:crypto";import{link,readdir as readdir2,rename as rename2,rm as rm2,stat as stat2,writeFile as writeFile2}from"node:fs/promises";import{join as join2}from"node:path";var TOMBSTONE_RETENTION_MS=1440*60*1e3;async function cancelScheduledJob(dir,id,options={}){if(!isScheduledJobId(id))return;let records=(await listScheduledJobs(dir)).jobs.filter(record=>record.job.id===id),representative=records.find(record=>record.state==="pending")??records[0];if(representative===void 0||options.sessionId!==void 0&&representative.job.sessionId!==options.sessionId)return;await writeAtomic(tombstonePath(dir,id),`${new Date().toISOString()}
|
|
5
5
|
`);let inFlight=(await occurrenceNumbers(dir,"firing",id)).length>0;await rm2(join2(dir,"pending",`${id}.json`),{force:!0});let failed=records.filter(record=>record.state==="failed");for(let record of failed)await rm2(join2(dir,record.file),{force:!0});return{job:representative.job,inFlight,removedFailed:failed.length}}function occurrenceFile(id,occurrence,owner){return join2("firing",`${id}@${occurrence}~${owner.pid}-${owner.processStartedAtMs}.json`)}async function restorePending(dir,record,id){let pending=join2(dir,"pending",`${id}.json`);try{await link(join2(dir,record),pending)}catch(error){if(!isFileSystemError(error,"EEXIST"))throw error}await rm2(join2(dir,record),{force:!0}),await isCancelled(dir,id)&&await rm2(pending,{force:!0})}async function claimOccurrence(dir,job,owner){let record=occurrenceFile(job.id,job.fireCount+1,owner);await ensureDir(join2(dir,"firing"));try{await rename2(join2(dir,"pending",`${job.id}.json`),join2(dir,record))}catch(error){if(isFileSystemError(error,"ENOENT"))return;throw error}if(await isCancelled(dir,job.id)){await rm2(join2(dir,record),{force:!0});return}if((await readJobFile(join2(dir,record),job.id)).fireCount!==job.fireCount){await restorePending(dir,record,job.id);return}return record}async function rearmRecurringJob(dir,next){if(await isCancelled(dir,next.id))return;let path=join2(dir,"pending",`${next.id}.json`);await writeAtomic(path,serialize(next)),await isCancelled(dir,next.id)&&await rm2(path,{force:!0})}async function occurrenceNumbers(dir,state,id){let names;try{names=await readdir2(join2(dir,state))}catch(error){if(isFileSystemError(error,"ENOENT"))return[];throw error}let prefix=`${id}@`;return names.filter(name=>name.startsWith(prefix)).map(name=>Number.parseInt(name.slice(prefix.length),10)).filter(occurrence=>Number.isSafeInteger(occurrence))}async function restoreAbandonedSchedule(dir,job,occurrence,now){if(job.everyMs===null||await isCancelled(dir,job.id))return;let pending=join2(dir,"pending",`${job.id}.json`);if(await stat2(pending).then(()=>!0,()=>!1)||[...await occurrenceNumbers(dir,"firing",job.id),...await occurrenceNumbers(dir,"failed",job.id)].some(n=>n>occurrence))return;let next={...job,fireCount:occurrence,dueAt:nextRecurringDueAt(job.dueAt,job.everyMs,now),lastError:null};await ensureDir(join2(dir,"pending"));let temp=join2(dir,"pending",`.schedule-${randomUUID2()}.tmp`);try{await writeFile2(temp,serialize(next),{encoding:"utf8",mode:384}),await link(temp,pending)}catch(error){if(!isFileSystemError(error,"EEXIST"))throw error;return}finally{await rm2(temp,{force:!0})}if(await isCancelled(dir,job.id)){await rm2(pending,{force:!0});return}return next}async function settleOccurrence(dir,record,job,occurrence,outcome){if(!outcome.ok&&!await isCancelled(dir,job.id)){let failed=join2(dir,"failed",`${job.id}@${occurrence}.json`);await writeAtomic(failed,serialize({...job,lastError:outcome.error})),await isCancelled(dir,job.id)&&await rm2(failed,{force:!0});let older=(await occurrenceNumbers(dir,"failed",job.id)).sort((a,b)=>b-a);for(let stale of older.slice(10))await rm2(join2(dir,"failed",`${job.id}@${stale}.json`),{force:!0})}await rm2(join2(dir,record),{force:!0})}async function pruneTombstones(dir,listing,now){let names;try{names=await readdir2(join2(dir,"cancelled"))}catch(error){if(isFileSystemError(error,"ENOENT"))return;throw error}let live=new Set(listing.jobs.filter(record=>record.state!=="failed").map(record=>record.job.id));for(let id of names){if(!isScheduledJobId(id)||live.has(id))continue;let info=await stat2(tombstonePath(dir,id)).catch(()=>{});info!==void 0&&now-info.mtimeMs>TOMBSTONE_RETENTION_MS&&await rm2(tombstonePath(dir,id),{force:!0})}}import{mkdir as mkdir2,readdir as readdir3,stat as stat4}from"node:fs/promises";import{join as join3}from"node:path";import{randomUUID as randomUUID3}from"node:crypto";import{link as link2,open,readFile as readFile3,rename as rename3,stat as stat3,unlink}from"node:fs/promises";import{execFile}from"node:child_process";import{readFile as readFile2}from"node:fs/promises";var LINUX_CLOCK_TICKS_PER_SECOND=100;async function linuxProcessStartMs(pid){let[stat5,systemStat]=await Promise.all([readFile2(`/proc/${pid}/stat`,"utf8"),readFile2("/proc/stat","utf8")]),afterComm=stat5.slice(stat5.lastIndexOf(")")+2).split(" "),startTicks=Number(afterComm[19]),bootSeconds=Number(/^btime (\d+)/m.exec(systemStat)?.[1]);if(!(!Number.isFinite(startTicks)||!Number.isFinite(bootSeconds)))return Math.round((bootSeconds+startTicks/LINUX_CLOCK_TICKS_PER_SECOND)*1e3)}function execText(command,args,env){return new Promise((resolve,reject)=>{execFile(command,[...args],{encoding:"utf8",timeout:5e3,windowsHide:!0,...env===void 0?{}:{env}},(error,stdout)=>{error?reject(error):resolve(stdout)})})}async function darwinProcessStartMs(pid){let text=(await execText("ps",["-o","lstart=","-p",String(pid)],{...process.env,LC_ALL:"C",LANG:"C"})).trim();if(text.length!==0)return processStartTimeMs(text)}async function windowsProcessStartMs(pid){let script=`(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().ToString('o')`,text=(await execText("powershell",["-NoProfile","-NonInteractive","-Command",script])).trim();return processStartTimeMs(text)}async function readProcessStartMs(pid){try{switch(process.platform){case"linux":return await linuxProcessStartMs(pid);case"darwin":return await darwinProcessStartMs(pid);case"win32":return await windowsProcessStartMs(pid);default:return}}catch{return}}function errorCode(error){if(typeof error=="object"&&error!==null&&"code"in error&&typeof error.code=="string")return error.code}async function unlinkIfPresent(path){try{await unlink(path)}catch(error){if(errorCode(error)!=="ENOENT")throw error}}async function readLeaseText(path){try{return await readFile3(path,"utf8")}catch(error){if(errorCode(error)==="ENOENT")return;throw error}}async function writeTemp(path,content,mode){let temp=`${path}.${process.pid}.${randomUUID3().slice(0,8)}.tmp`,file=await open(temp,"wx",mode);try{await file.writeFile(content,"utf8")}finally{await file.close()}return temp}async function publishExclusive(path,content,options={}){let temp=await writeTemp(path,content,options.mode);try{await link2(temp,path)}catch(error){let code=errorCode(error);if(code!=="ENOTSUP"&&code!=="EOPNOTSUPP"&&code!=="EPERM")throw error;let file=await open(path,"wx",options.mode);try{await file.writeFile(content,"utf8")}finally{await file.close()}}finally{await unlinkIfPresent(temp)}}async function publishReplace(path,content,options={}){let temp=await writeTemp(path,content,options.mode);try{await rename3(temp,path)}catch(error){throw await unlinkIfPresent(temp),error}}function pidAlive(pid){try{return process.kill(pid,0),!0}catch(error){return errorCode(error)==="EPERM"}}var UNREADABLE_LOCK_STALE_MS=3e4;function readLockHolder(raw){try{let parsed=JSON.parse(raw);if(typeof parsed!="object"||parsed===null||!("pid"in parsed)||!("bootAtMs"in parsed)||!("processStartedAtMs"in parsed))return;let{pid,bootAtMs,processStartedAtMs}=parsed;return typeof pid!="number"||typeof bootAtMs!="number"||typeof processStartedAtMs!="number"?void 0:{pid,bootAtMs,processStartedAtMs}}catch{return}}async function reclaimLockState(lock,probes={}){let raw=await readLeaseText(lock);if(raw===void 0)return{state:"free"};let holder=readLockHolder(raw);if(holder===void 0)return await stat3(lock).then(info=>Date.now()-info.mtimeMs,()=>0)>UNREADABLE_LOCK_STALE_MS?{state:"stale",raw}:{state:"held"};if(!sameBoot(holder.bootAtMs,processBootAtMs()))return{state:"stale",raw};if(holder.pid===process.pid)return sameProcessStartMs(holder.processStartedAtMs,ownProcessStartedAtMs())?{state:"held",holder}:{state:"stale",raw};if(!(probes.isProcessAlive??pidAlive)(holder.pid))return{state:"stale",raw};let observed=await(probes.readProcessStartMs??readProcessStartMs)(holder.pid).catch(()=>{});return observed===void 0?{state:"held",holder,unproven:!0}:sameProcessStartMs(holder.processStartedAtMs,observed)?{state:"held",holder}:{state:"stale",raw}}async function breakStaleLock(lock,raw){let aside=`${lock}.${process.pid}.${randomUUID3().slice(0,8)}.broken`;try{await rename3(lock,aside)}catch(error){if(errorCode(error)==="ENOENT")return;throw error}let moved=await readFile3(aside,"utf8");if(moved!==raw)try{await publishExclusive(lock,moved)}catch(error){if(errorCode(error)!=="EEXIST")throw error}await unlinkIfPresent(aside)}async function takeReclaimLock(lock,probes){let mine=JSON.stringify({pid:process.pid,bootAtMs:processBootAtMs(),processStartedAtMs:ownProcessStartedAtMs()});for(let attempt=0;attempt<3;attempt+=1){try{return await publishExclusive(lock,mine),mine}catch(error){if(errorCode(error)!=="EEXIST")throw error}let seen=await reclaimLockState(lock,probes);if(seen.state==="held")return{holder:seen.holder};seen.state==="stale"&&await breakStaleLock(lock,seen.raw)}return{}}async function reclaimInspected(path,inspected,probes={}){let lock=`${path}.lock`,taken=await takeReclaimLock(lock,probes);if(typeof taken!="string")return{outcome:"busy",...taken.holder?{holder:taken.holder}:{}};try{return await readLeaseText(path)!==inspected?{outcome:"changed"}:(await unlinkIfPresent(path),{outcome:"removed"})}finally{await readLeaseText(lock)===taken&&await unlinkIfPresent(lock)}}var RUNNER_HEARTBEAT_STALE_MS=12e4,RUNNER_HEARTBEAT_INTERVAL_MS=3e4,DEFAULT_MAX_DELIVERY_MS=9e5,PRIVATE={mode:384};function ownRunnerIdentity(){return{pid:process.pid,processStartedAtMs:ownProcessStartedAtMs()}}function leasePath(dir,pid){return join3(runnersDir(dir),`${pid}.json`)}async function writeRunnerLease(dir,lease,now){await mkdir2(runnersDir(dir),{recursive:!0,mode:448});let identity=ownRunnerIdentity();await publishReplace(leasePath(dir,identity.pid),JSON.stringify({...identity,bootAtMs:processBootAtMs(),beatAt:now,...lease}),PRIVATE)}async function removeRunnerLease(dir){await unlinkIfPresent(leasePath(dir,process.pid))}function parseLease(raw){try{let parsed=JSON.parse(raw);if(typeof parsed!="object"||parsed===null)return;let beatAt="beatAt"in parsed?parsed.beatAt:void 0;if(typeof beatAt!="number")return;let watch="watch"in parsed&&parsed.watch===!0,exec="exec"in parsed&&typeof parsed.exec=="string"?parsed.exec:null;return{beatAt,watch,exec}}catch{return}}async function liveRunners(dir){let names;try{names=await readdir3(runnersDir(dir))}catch(error){if(error instanceof Error&&"code"in error&&error.code==="ENOENT")return[];throw error}let runners=[];for(let name of names){if(!/^\d+\.json$/.test(name))continue;let path=join3(runnersDir(dir),name),state=await reclaimLockState(path);if(state.state==="stale"&&await breakStaleLock(path,state.raw),state.state!=="held"||state.holder===void 0)continue;let raw=await readLeaseText(path),lease=raw===void 0?void 0:parseLease(raw);lease!==void 0&&runners.push({pid:state.holder.pid,processStartedAtMs:state.holder.processStartedAtMs,...lease})}return runners}async function isWatchRunnerAvailable(dir,now){return(await liveRunners(dir)).some(runner=>runner.watch&&now-runner.beatAt<=RUNNER_HEARTBEAT_STALE_MS)}function isOwnerAmong(owner,runners){return runners.some(runner=>runner.pid===owner.pid&&sameProcessStartMs(runner.processStartedAtMs,owner.processStartedAtMs))}function sessionLockPath(dir,sessionId){return join3(dir,"sessions",`${encodeURIComponent(sessionId)}.lock`)}function isDeliveryGroupAlive(pid){try{return process.kill(process.platform==="win32"?pid:-pid,0),!0}catch(error){return errorCode(error)==="EPERM"}}function isPidAlive(pid){try{return process.kill(pid,0),!0}catch(error){return errorCode(error)==="EPERM"}}async function orphanedDelivery(raw,lockAgeMs,probes){let parsed;try{parsed=JSON.parse(raw)}catch{return}if(typeof parsed!="object"||parsed===null)return;let field=key=>key in parsed?Reflect.get(parsed,key):void 0,boot=field("bootAtMs");if(typeof boot!="number"||!sameBoot(boot,processBootAtMs()))return;let pid=field("deliveryPid"),startedAt=field("deliveryStartedAtMs");if(typeof pid!="number"||typeof startedAt!="number"){let maxDeliveryMs=field("maxDeliveryMs"),runner=field("pid");return field("gated")===!1&&typeof maxDeliveryMs=="number"&&lockAgeMs<=maxDeliveryMs&&typeof runner=="number"?runner:void 0}if(!isDeliveryGroupAlive(pid))return;if(!isPidAlive(pid))return pid;let leaderStart=await(probes.readProcessStartMs??readProcessStartMs)(pid).catch(()=>{});return leaderStart!==void 0&&sameProcessStartMs(startedAt,leaderStart)?pid:void 0}async function acquireSessionDeliveryLock(dir,sessionId,options={}){let probes=options,path=sessionLockPath(dir,sessionId),identity={pid:process.pid,bootAtMs:processBootAtMs(),processStartedAtMs:ownProcessStartedAtMs(),gated:options.gated??process.platform!=="win32",maxDeliveryMs:options.maxDeliveryMs??DEFAULT_MAX_DELIVERY_MS},mine=JSON.stringify(identity);for(let attempt=0;attempt<3;attempt+=1){try{await publishExclusive(path,mine,PRIVATE);let tail=Promise.resolve(),serialized=operation=>{let run=tail.then(operation);return tail=run.catch(()=>{}),run};return{acquired:!0,attachDelivery:pid=>serialized(async()=>{let startedAt=Math.floor(Date.now()/1e3)*1e3;if(await readLeaseText(path)!==mine)throw new Error("session delivery lock was lost");let next=JSON.stringify({...identity,deliveryPid:pid,deliveryStartedAtMs:startedAt});await publishReplace(path,next,PRIVATE),mine=next}),release:()=>serialized(async()=>{await readLeaseText(path)===mine&&await unlinkIfPresent(path)})}}catch(error){let code=errorCode(error);if(code==="ENOENT"){await mkdir2(join3(dir,"sessions"),{recursive:!0,mode:448});continue}if(code!=="EEXIST")throw error}let state=await reclaimLockState(path);if(state.state==="held")return{acquired:!1,heldByPid:state.holder?.pid};if(state.state==="stale"){let lockAgeMs=await stat4(path).then(info=>Date.now()-info.mtimeMs,()=>0),delivering=await orphanedDelivery(state.raw,lockAgeMs,probes);if(delivering!==void 0)return{acquired:!1,heldByPid:delivering};await breakStaleLock(path,state.raw)}}return{acquired:!1,heldByPid:void 0}}import{randomUUID as randomUUID5}from"node:crypto";import{existsSync,mkdirSync,readFileSync,renameSync,rmdirSync,statSync,unlinkSync,writeFileSync}from"node:fs";import{mkdir as mkdir4}from"node:fs/promises";import{dirname as dirname3,join as join6}from"node:path";var SessionHeldError=class extends Error{constructor(sessionFile,holders){let who=holders.map(h=>h.cwd?`pid ${h.pid} in ${h.cwd}`:`pid ${h.pid}`).join("; ");super(`This session is open in another ${APP_NAME} process (${who}). Quit it there, then try again.`),this.code="ESESSIONHELD",this.name="SessionHeldError",this.sessionFile=sessionFile,this.holders=holders}},SessionMovingError=class extends Error{constructor(sessionFile,moverPid){let who=moverPid===void 0?"another process":`another process (pid ${moverPid})`;super(`Session ${sessionFile} is being moved by ${who}. Try again in a moment.`),this.code="ESESSIONMOVING",this.name="SessionMovingError",this.moverPid=moverPid}},SessionMovedError=class extends Error{constructor(sessionFile){super(`Session ${sessionFile} no longer exists: another process moved it. Resume it from its new location.`),this.code="ESESSIONMOVED",this.name="SessionMovedError"}};import{readdir as readdir4}from"node:fs/promises";import{join as join4}from"node:path";var HOLDER_RECORD=/^\d+\.json$/;function parseCwd(raw){let parsed=JSON.parse(raw);if(!(typeof parsed!="object"||parsed===null||!("cwd"in parsed)))return typeof parsed.cwd=="string"?parsed.cwd:void 0}async function fenced(fenceSession){return fenceSession===void 0?!1:fenceSession().then(()=>!0,()=>!1)}function holderGeneration(parsed){return typeof parsed!="object"||parsed===null?{}:{..."host_instance"in parsed&&typeof parsed.host_instance=="string"?{host_instance:parsed.host_instance}:{},..."host_daemon_dir"in parsed&&typeof parsed.host_daemon_dir=="string"?{host_daemon_dir:parsed.host_daemon_dir}:{}}}async function liveHolders(dir,probes={},fenceSession){let holders=[];for(let name of await readdir4(dir)){if(!HOLDER_RECORD.test(name))continue;let path=join4(dir,name),before=await readLeaseText(path),state=await reclaimLockState(path,probes);if(state.state==="stale"&&await breakStaleLock(path,state.raw),state.state!=="held"||state.holder===void 0)continue;let raw=await readLeaseText(path),stable=raw!==void 0&&raw===before,parsed=stable?JSON.parse(raw):void 0;if(stable&&state.unproven===!0&&await probes.holderEnded?.(holderGeneration(parsed))===!0&&await fenced(fenceSession)){await breakStaleLock(path,raw);continue}let endpoint=typeof parsed=="object"&&parsed!==null&&"host_instance"in parsed&&typeof parsed.host_instance=="string"?{host_instance:parsed.host_instance,..."host_socket"in parsed&&typeof parsed.host_socket=="string"?{host_socket:parsed.host_socket}:{}}:{};holders.push({pid:state.holder.pid,cwd:stable?parseCwd(raw):void 0,...endpoint})}return holders}import{randomUUID as randomUUID4}from"node:crypto";import{mkdir as mkdir3,readFile as readFile4,rename as rename4,rm as rm3,writeFile as writeFile3}from"node:fs/promises";import{dirname as dirname2,join as join5}from"node:path";var InvalidSidecarStoreError=class extends Error{constructor(message,options){super(message,options),this.name="InvalidSidecarStoreError"}},UnsupportedSidecarStoreVersionError=class extends Error{constructor(message){super(message),this.name="UnsupportedSidecarStoreVersionError"}},mutationTails=new Map,snapshots=new Map;function encodedSessionId(sessionId){return encodeURIComponent(sessionId)}function sidecarFilePath(ref){return join5(ref.baseDir,`${encodedSessionId(ref.sessionId)}.json`)}function createSidecarStore(options){let ref={baseDir:options.baseDir,sessionId:options.sessionId},filePath=sidecarFilePath(ref);async function read(){let raw;try{raw=await readFile4(filePath,"utf8")}catch(error){if(isFileSystemError2(error,"ENOENT"))return null;throw error}let state=parsePayload(raw,options,ref);return snapshots.set(filePath,state),state}async function write(state){await mkdir3(dirname2(filePath),{recursive:!0}),await writeAtomic2(filePath,`${JSON.stringify(state,null,2)}
|