@task-handoff/node-agent 0.0.5 → 0.0.6
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/dist/cli.js +1 -1
- package/package.json +1 -4
package/dist/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("commander"),t=require("node:fs"),n=require("node:path"),r=require("node:http"),o=require("node:net"),i=require("node:url"),s=require("node:child_process"),a=require("fastify"),c=require("@fastify/websocket"),d=require("ws"),l=require("zod"),u=require("node:crypto"),p=require("node:os"),m=require("write-file-atomic"),g=require("semver"),h="undefined"!=typeof document?document.currentScript:null;const f=41943040,y="ai.sessions",I=l.z.string().trim().min(1).max(80),b=l.z.enum(["running","waiting","idle","failed"]),A=l.z.enum(["thinking","tool","editing","approval","responding","unknown"]),z=l.z.object({name:l.z.string().trim().min(1).max(120),inputPreview:l.z.string().trim().max(500).optional(),startedAt:l.z.string().datetime().optional()}).strict(),w=l.z.object({toolCalls:l.z.number().int().min(0).default(0),edits:l.z.number().int().min(0).default(0),approvals:l.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),S=l.z.object({send:l.z.boolean().optional(),interrupt:l.z.boolean().optional(),approval:l.z.boolean().optional()}).strict(),k=l.z.object({id:l.z.string().trim().min(1).max(120),kind:l.z.enum(["image"]),name:l.z.string().trim().min(1).max(240),mime:l.z.string().trim().min(1).max(120),size:l.z.number().int().min(0).max(20971520),data:l.z.string().min(1).max(31457280)}).strict(),N=k.omit({data:!0}),E=l.z.object({id:l.z.string().trim().min(1).max(120),kind:l.z.enum(["image"]).optional()}).strict(),_=l.z.enum(["auto","queue","steer","immediate"]),v=l.z.object({message:l.z.string().trim().min(1).max(2e4),mode:_.optional()});v.extend({attachments:l.z.array(k).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+t.size,0)>f&&t.addIssue({code:l.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),v.extend({attachments:l.z.array(E).max(6).default([])}).strict(),l.z.object({decision:l.z.enum(["allow","deny","skip"])}).strict(),l.z.object({queueIds:l.z.array(l.z.string().trim().min(1).max(120)).max(100)}).strict(),l.z.object({code:l.z.string().trim().min(1).max(160),message:l.z.string(),instanceId:l.z.string().trim().min(1).max(160).optional(),nodeId:l.z.string().trim().min(1).max(160).optional(),route:l.z.string().trim().min(1).max(4096).optional()}).strict();const O=l.z.object({id:l.z.string().trim().min(1).max(120),message:l.z.string().trim().min(1).max(2e4),attachments:l.z.array(N).max(6).default([]),status:l.z.enum(["queued","sending","failed"]).default("queued"),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime(),error:l.z.string().trim().max(4e3).optional()}).strict(),x=l.z.object({pendingCount:l.z.number().int().min(0).default(0),items:l.z.array(O).max(100).default([])}).strict().default({pendingCount:0,items:[]}),T=l.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),C=l.z.object({id:l.z.string().trim().min(1).max(240),providerTurnId:l.z.string().trim().max(240).optional(),source:T.optional(),userPrompt:l.z.string().trim().optional(),status:l.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:A.optional(),summary:l.z.string().trim().max(1e3).optional(),lastMessage:l.z.string().trim().optional(),revision:l.z.number().int().min(0).default(0),sourcePriority:l.z.number().int().min(0).max(100).optional(),snapshotVersion:l.z.number().int().min(0).optional(),observedAt:l.z.string().datetime().optional(),startedAt:l.z.string().datetime().optional(),updatedAt:l.z.string().datetime().optional(),completedAt:l.z.string().datetime().optional()}).strict(),D=l.z.object({id:l.z.string().trim().min(1).max(120),agent:I,appSessionId:l.z.string().trim().max(120).optional(),appId:l.z.string().trim().max(120).optional(),providerSessionId:l.z.string().trim().max(240).optional(),providerMeta:l.z.record(l.z.string(),l.z.unknown()).optional(),appBindingKeys:l.z.array(l.z.string().trim().min(1).max(240)).max(20).optional(),actions:S.optional(),activeTurnId:l.z.string().trim().max(240).optional(),conversationId:l.z.number().int().positive().optional(),title:l.z.string().trim().max(240).optional(),cwd:l.z.string().trim().max(4096).optional(),userPrompt:l.z.string().trim().optional(),turns:l.z.array(C).max(50).optional(),status:b.default("running"),phase:A.default("unknown"),summary:l.z.string().trim().max(1e3).optional(),lastMessage:l.z.string().trim().optional(),currentTool:z.optional(),transcriptPath:l.z.string().trim().max(4096).optional(),transcriptSize:l.z.number().int().min(0).optional(),startedAt:l.z.string().datetime(),updatedAt:l.z.string().datetime(),completedAt:l.z.string().datetime().optional(),error:l.z.string().trim().max(4e3).optional(),counters:w,queue:x}).strict();l.z.object({session:D,provider:l.z.string().trim().min(1).max(80),action:l.z.enum(["send","queue","steer","interrupt","approval"]),decision:l.z.enum(["allow","deny","skip"]).optional(),turnId:l.z.string().trim().min(1).max(240).optional(),providerTurnId:l.z.string().trim().min(1).max(240).optional(),queueId:l.z.string().trim().min(1).max(120).optional()}).strict();const j=D.pick({id:!0,agent:!0,appSessionId:!0,appId:!0,providerSessionId:!0,providerMeta:!0,appBindingKeys:!0,actions:!0,activeTurnId:!0,conversationId:!0,title:!0,cwd:!0,userPrompt:!0,turns:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,currentTool:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),R=l.z.object({runningCount:l.z.number().int().min(0).default(0),waitingCount:l.z.number().int().min(0).default(0),staleCount:l.z.number().int().min(0).default(0),sessions:l.z.array(j).default([]),updatedAt:l.z.string().datetime()}).strict();l.z.object({streamId:l.z.string().trim().min(1).max(240),revision:l.z.number().int().min(0),lastEventAt:l.z.string().datetime(),snapshot:R}).strict(),R.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:l.z.number().int().min(0).default(0),problemCount:l.z.number().int().min(0).default(0),revision:l.z.number().int().min(0).optional()});const P=l.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),M=l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),nodeId:l.z.string().trim().min(1).max(160).optional(),revision:l.z.number().int().min(0),previousRevision:l.z.number().int().min(0).optional(),traceId:l.z.string().trim().min(1).max(160),generatedAt:l.z.string().datetime(),reason:P}).strict(),F=l.z.object({meta:M,snapshot:R}).strict(),$=l.z.object({meta:M,upserted:l.z.array(j).default([]),removed:l.z.array(l.z.string().trim().min(1).max(120)).default([])}).strict(),L=l.z.object({meta:M,sessionIds:l.z.array(l.z.string().trim().min(1).max(120)).default([]),expiresAt:l.z.string().datetime()}).strict();l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),sinceRevision:l.z.number().int().min(0),latestRevision:l.z.number().int().min(0),earliestRetainedRevision:l.z.number().int().min(0),syncRequired:l.z.boolean().default(!1),events:l.z.array(l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("ai-session.snapshot"),payload:F}).strict(),l.z.object({type:l.z.literal("ai-session.patch"),payload:$}).strict(),l.z.object({type:l.z.literal("ai-session.removed"),payload:L}).strict()])).default([])}).strict();const U=l.z.object({source:T,sourcePriority:l.z.number().int().min(0).max(100).optional(),observedAt:l.z.string().datetime().optional(),snapshotVersion:l.z.number().int().min(0).optional()}).strict(),H=U.extend({type:l.z.literal("snapshot"),agent:l.z.string().trim().min(1).max(80),appSessionId:l.z.string().trim().max(120).optional(),appId:l.z.string().trim().max(120).optional(),providerSessionId:l.z.string().trim().max(240).optional(),providerMeta:l.z.record(l.z.string(),l.z.unknown()).optional(),appBindingKeys:l.z.array(l.z.string().trim().min(1).max(240)).max(20).optional(),actions:S.optional(),conversationId:l.z.number().int().positive().optional(),title:l.z.string().trim().max(240).optional(),cwd:l.z.string().trim().max(4096).optional(),activeTurnId:l.z.string().trim().max(240).optional(),userPrompt:l.z.string().trim().optional(),turns:l.z.array(C).max(50).optional(),status:b.optional(),phase:A.optional(),summary:l.z.string().trim().max(1e3).optional(),lastMessage:l.z.string().trim().optional(),transcriptPath:l.z.string().trim().max(4096).optional(),transcriptSize:l.z.number().int().min(0).optional(),replaceActivity:l.z.boolean().optional()}).strict(),K=U.extend({type:l.z.literal("event"),sessionId:l.z.string().trim().min(1).max(120),kind:l.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed"]),activeTurnId:l.z.string().trim().max(240).optional(),providerTurnId:l.z.string().trim().max(240).optional(),userPrompt:l.z.string().trim().optional(),text:l.z.string().optional(),status:b.optional(),phase:A.optional(),summary:l.z.string().trim().max(1e3).optional(),error:l.z.string().trim().max(4e3).optional(),counters:l.z.object({toolCalls:l.z.number().int().min(0).optional(),edits:l.z.number().int().min(0).optional(),approvals:l.z.number().int().min(0).optional()}).strict().optional()}).strict();l.z.discriminatedUnion("type",[H,K]);const q=l.z.union([l.z.object({type:l.z.literal("schedule"),scheduleKind:l.z.literal("interval").default("interval"),intervalMs:l.z.number().int().min(1e3)}).strict(),l.z.object({type:l.z.literal("schedule"),scheduleKind:l.z.literal("daily"),timeOfDay:l.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:l.z.string().trim().min(1).max(120)}).strict(),l.z.object({type:l.z.literal("schedule"),scheduleKind:l.z.literal("weekly"),weekdays:l.z.array(l.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:l.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:l.z.string().trim().min(1).max(120)}).strict()]),B=l.z.union([q,l.z.object({type:l.z.literal("file-change"),roots:l.z.array(l.z.string().trim().min(1).max(4096)).min(1).max(50),globs:l.z.array(l.z.string().trim().min(1).max(500)).min(1).max(100),ignore:l.z.array(l.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:l.z.number().int().min(100).max(6e4).default(1500)}).strict(),l.z.object({type:l.z.literal("ai-session"),agent:l.z.string().trim().min(1).max(80).optional(),statuses:l.z.array(l.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:l.z.array(l.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),V=l.z.object({promptTemplate:l.z.string().trim().min(1).max(2e4)}).strict(),G=l.z.object({cooldownMs:l.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:l.z.number().int().min(1).max(20).default(1),whenBusy:l.z.enum(["skip","queue"]).default("skip")}).strict(),J=l.z.object({configHash:l.z.string().trim().min(8).max(80),name:l.z.string().trim().min(1).max(160),description:l.z.string().trim().max(1e3).optional(),source:B,action:V,policy:G.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}).strict(),W=l.z.object({type:l.z.literal("ai-session"),aiSessionId:l.z.string().trim().min(1).max(160)}).strict(),Y=l.z.object({configHash:l.z.string().trim().min(8).max(80),deploymentId:l.z.string().trim().min(1).max(240).optional(),instanceId:l.z.string().trim().min(1).max(160),origin:l.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:l.z.boolean().default(!0),target:W,localName:l.z.string().trim().min(1).max(160).optional(),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}).strict(),X=l.z.object({configHash:l.z.string().trim().min(8).max(80),deploymentId:l.z.string().trim().min(1).max(240).optional(),instanceId:l.z.string().trim().min(1).max(160),status:l.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:l.z.string().datetime().optional(),lastCompletedAt:l.z.string().datetime().optional(),lastSkippedAt:l.z.string().datetime().optional(),lastError:l.z.string().trim().max(4e3).optional(),runCount:l.z.number().int().min(0).default(0),skippedCount:l.z.number().int().min(0).default(0)}).strict(),Q=l.z.object({id:l.z.string().trim().min(1).max(160),configHash:l.z.string().trim().min(8).max(80),deploymentId:l.z.string().trim().min(1).max(240).optional(),instanceId:l.z.string().trim().min(1).max(160),eventType:l.z.enum(["manual","schedule","file-change","ai-session"]),status:l.z.enum(["started","completed","failed","skipped"]),target:W,promptPreview:l.z.string().trim().max(500),eventSummary:l.z.string().trim().max(1e3).optional(),error:l.z.string().trim().max(4e3).optional(),startedAt:l.z.string().datetime(),completedAt:l.z.string().datetime().optional()}).strict();l.z.object({schemaVersion:l.z.literal(1).default(1),configs:l.z.array(J).default([]),deployments:l.z.array(Y).default([]),runtime:l.z.array(X).default([]),recentRuns:l.z.array(Q).default([])}).strict();const Z="2026-07-15",ee=l.z.string(),te=l.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),ne=l.z.string().datetime(),re=l.z.record(l.z.string(),l.z.string()).default({}),oe=l.z.record(l.z.string(),l.z.string()).default({}),ie=l.z.object({codexModelHash:te.optional(),claudeModelHash:te.optional()}).strict().default({}),se=l.z.object({id:te,name:l.z.string().trim().min(1).max(120),kind:l.z.enum(["tty","gui","web"]),source:l.z.enum(["builtin","custom"]),availability:l.z.enum(["available","missing-dependency"]),capabilities:l.z.object({automation:l.z.enum(["cdp"]).optional(),supportsCwdSelection:l.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:l.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),ae=l.z.object({code:l.z.enum(["APP_CATALOG_INVALID"]),message:l.z.string().trim().min(1).max(500)}).strict(),ce=l.z.object({items:l.z.array(se).max(256),observedAt:ne,issues:l.z.array(ae).max(32).default([])}).strict(),de=l.z.object({component:l.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:l.z.string().trim().max(160).optional(),packageVersion:l.z.string().trim().max(80).optional(),protocolVersion:ee.optional(),buildId:l.z.string().trim().max(160).optional(),builtAt:l.z.string().trim().max(120).optional(),gitCommit:l.z.string().trim().max(120).optional(),imageRef:l.z.string().trim().max(512).optional(),imageDigest:l.z.string().trim().max(240).optional()}).strict(),le=l.z.enum(["stable","beta","alpha"]),ue=l.z.discriminatedUnion("component",[l.z.object({component:l.z.literal("node-agent")}).strict(),l.z.object({component:l.z.literal("controlled-instance"),instanceId:te}).strict()]),pe=l.z.object({target:ue,channel:le.default("stable")}).strict(),me=l.z.object({target:ue,source:l.z.enum(["npm","docker-registry"]),channel:le,currentVersion:l.z.string().trim().max(240).optional(),availableVersion:l.z.string().trim().min(1).max(240),artifactRef:l.z.string().trim().min(1).max(512).optional(),updateAvailable:l.z.boolean(),supported:l.z.boolean().default(!0),reason:l.z.string().trim().max(2048).optional(),checkedAt:ne}).strict(),ge=l.z.object({id:te,nodeId:te,target:ue,source:l.z.enum(["npm","docker-registry"]),channel:le,fromVersion:l.z.string().trim().max(240).optional(),toVersion:l.z.string().trim().min(1).max(240),artifactRef:l.z.string().trim().min(1).max(512).optional(),status:l.z.enum(["queued","updating","restarting","succeeded","failed"]),error:l.z.string().trim().max(4096).optional(),startedAt:ne.optional(),completedAt:ne.optional(),createdAt:ne,updatedAt:ne}).strict(),he=pe,fe=l.z.object({type:l.z.enum(["branch","tag","commit"]),name:l.z.string().trim().min(1).max(240).optional(),commit:l.z.string().trim().max(80).optional()}).strict(),ye=l.z.object({type:l.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:l.z.string().trim().min(1).max(120).optional()}).strict(),Ie=l.z.object({depth:l.z.number().int().positive().max(1e5).optional(),submodules:l.z.boolean().default(!1),lfs:l.z.boolean().default(!1),subdirectory:l.z.string().trim().max(240).default("")}).strict();l.z.object({id:te,name:l.z.string().trim().min(1).max(160),url:l.z.string().trim().min(1).max(2048),provider:l.z.string().trim().max(80).optional(),ref:fe.default({type:"branch",name:"main"}),auth:ye.default({type:"none"}),clone:Ie.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:te.optional(),labels:re,createdAt:ne,updatedAt:ne}).strict();const be=l.z.object({id:te,nodeId:te,name:l.z.string().trim().min(1).max(160),path:l.z.string().trim().min(1).max(4096),defaultImageId:te.optional(),labels:re,createdAt:ne,updatedAt:ne}).strict(),Ae=l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("git-repository"),repositoryId:te.optional(),url:l.z.string().trim().min(1).max(2048),provider:l.z.string().trim().max(80).optional(),ref:fe.default({type:"branch",name:"main"}),auth:ye.default({type:"none"}),clone:Ie.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),l.z.object({type:l.z.literal("git-template"),url:l.z.string().trim().min(1).max(2048),templateId:l.z.string().trim().min(1).max(120).optional(),ref:fe.default({type:"branch",name:"main"}),auth:ye.default({type:"none"}),clone:Ie.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),l.z.object({type:l.z.literal("local-folder"),localFolderId:te.optional(),path:l.z.string().trim().min(1).max(4096),ownerNodeId:te.optional()}).strict()]),ze=l.z.object({mode:l.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:l.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:l.z.string().trim().max(240).optional(),readOnly:l.z.boolean().default(!1)}).strict(),we=l.z.enum(["codex","claude"]),Se=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),source:Ae,defaultImageId:te.optional(),defaultNodeId:te.optional(),defaultRuntimeId:te.optional(),workspacePolicy:ze,labels:re,createdAt:ne,updatedAt:ne}).strict(),ke=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),endpoint:l.z.string().trim().min(1).max(2048),key:l.z.string().trim().min(1).max(4096),model:l.z.string().trim().min(1).max(240),app:we,enabled:l.z.boolean().default(!0),order:l.z.number().int().min(0).max(1e6).default(0),labels:re,createdAt:ne,updatedAt:ne}).strict(),Ne=ke,Ee=ke.omit({key:!0}).extend({keyPreview:l.z.string().trim().min(1).max(160),keySet:l.z.boolean()}).strict(),_e=Ee.extend({referenceCount:l.z.number().int().min(0)}).strict(),ve=l.z.object({instanceId:te,codexModelHash:te.optional(),claudeModelHash:te.optional(),updatedAt:ne}).strict(),Oe=l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("control-plane"),name:ke.shape.name,enabled:ke.shape.enabled,order:ke.shape.order}).strict(),l.z.object({type:l.z.literal("node"),nodeId:te,name:ke.shape.name,enabled:ke.shape.enabled,order:ke.shape.order,referenceCount:l.z.number().int().min(0)}).strict()]),xe=l.z.object({id:te,model:Ee,locations:l.z.array(Oe).min(1),referenceCount:l.z.number().int().min(0).default(0)}).strict(),Te=l.z.object({nodeId:te,code:l.z.string().trim().min(1).max(120),message:l.z.string().trim().min(1).max(2e3)}).strict();l.z.object({models:l.z.array(xe),nodeDiagnostics:l.z.array(Te).default([]),updatedAt:ne}).strict();const Ce=ke.omit({id:!0,createdAt:!0,updatedAt:!0}).strict(),De=Ce.partial().strict(),je=ke,Re=l.z.object({modelSelection:ie,codexModelHash:te.optional(),claudeModelHash:te.optional()}).strict();function Pe(e){const t={app:we.parse(e.app),endpoint:ke.shape.endpoint.parse(e.endpoint),key:ke.shape.key.parse(e.key),model:ke.shape.model.parse(e.model)};return`mdl_${u.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}const Me=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),image:l.z.string().trim().min(1).max(512),registry:l.z.string().trim().min(1).max(160).default("local"),capabilities:l.z.array(l.z.string().trim().min(1).max(80)).default([]),optionalApps:l.z.array(l.z.string().trim().min(1).max(120)).default([]),defaultEnv:oe,labels:re,createdAt:ne,updatedAt:ne}).strict(),Fe=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),connectionMode:l.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:l.z.object({mode:l.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:te.optional(),secret:l.z.string().trim().max(4096).optional(),pairedAt:ne.optional(),pairing:l.z.object({status:l.z.enum(["pending","paired","expired"]).default("pending"),joinToken:l.z.string().trim().max(4096).optional(),expiresAt:ne.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:l.z.string().trim().max(2048).optional(),controlEndpoint:l.z.string().trim().max(2048).optional(),containerEndpoint:l.z.string().trim().max(2048).optional(),publicWebBase:l.z.string().trim().max(2048).optional(),status:l.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:l.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),appInventory:ce.optional(),labels:re,lastSeenAt:ne.optional(),createdAt:ne,updatedAt:ne}).strict(),$e=l.z.object({id:te,nodeId:te,name:l.z.string().trim().min(1).max(160),type:l.z.enum(["docker","kubernetes","local"]),status:l.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:l.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),labels:re,createdAt:ne,updatedAt:ne}).strict();l.z.object({ok:l.z.boolean().optional(),role:l.z.string().optional(),nodeId:te.optional()}).passthrough();const Le=l.z.object({bindScope:l.z.enum(["loopback","all-ipv4"]),port:l.z.number().int().min(1).max(65535)}).strict(),Ue=Le.extend({host:l.z.enum(["127.0.0.1","0.0.0.0"]),status:l.z.enum(["listening","error"]),source:l.z.enum(["bootstrap","persisted"]),error:l.z.string().trim().max(2048).optional()}).strict(),He=Le;l.z.object({nodeId:te,joinToken:l.z.string().trim().min(1),expiresAt:ne}).passthrough(),l.z.object({id:te,keyId:te,url:l.z.string().trim().min(1).max(2048).optional(),name:l.z.string().trim().min(1).max(160).optional(),pairedAt:ne,updatedAt:ne.optional(),active:l.z.boolean().optional(),current:l.z.boolean().optional()}).passthrough(),l.z.object({remote:l.z.object({id:te,url:l.z.string().trim().min(1).max(2048).optional(),keyId:te,pairedAt:ne,active:l.z.boolean()}).passthrough(),tunnel:l.z.object({status:l.z.enum(["disabled","saved","connecting","connected","failed"]),error:l.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),l.z.object({deleted:l.z.boolean().optional()}).passthrough(),l.z.object({repository:l.z.string().trim().optional(),tag:l.z.string().trim().optional(),id:l.z.string().trim(),createdSince:l.z.string().trim().optional(),size:l.z.string().trim().optional(),reference:l.z.string().trim().min(1)}).passthrough();const Ke=l.z.lazy(()=>l.z.object({name:l.z.string().trim().min(1),path:l.z.string().trim().min(1).max(4096),children:l.z.array(Ke)}).passthrough());l.z.object({status:l.z.number().int().min(100).max(599).optional(),headers:l.z.record(l.z.string(),l.z.string()).optional(),bodyBase64:l.z.string().optional()}).passthrough();const qe=l.z.object({strategy:l.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:l.z.string().trim().max(2048).optional(),api:l.z.string().trim().max(2048).optional(),vnc:l.z.string().trim().max(2048).optional(),tty:l.z.string().trim().max(2048).optional(),logs:l.z.string().trim().max(2048).optional(),status:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),Be=l.z.object({strategy:l.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:l.z.string().trim().max(2048).optional(),api:l.z.string().trim().max(2048).optional(),ws:l.z.string().trim().max(2048).optional(),status:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),Ve=l.z.object({mode:l.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:l.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:l.z.string().trim().max(4096).optional(),resolvedCommit:l.z.string().trim().max(120).optional(),error:l.z.string().trim().max(2048).optional()}).passthrough(),Ge=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),projectId:te.optional(),source:Ae,sourceSnapshot:l.z.record(l.z.string(),l.z.unknown()).default({}),modelSelection:ie,nodeId:te,runtimeId:te,imageId:te.optional(),imageSnapshot:Me.optional(),status:l.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:l.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:l.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:l.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:l.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:ee.optional(),instanceVersion:l.z.string().trim().max(80).optional(),build:de.optional(),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),appInventory:ce.optional(),config:l.z.object({autoImportAgentConfigs:l.z.boolean().default(!0)}).strict().default({autoImportAgentConfigs:!0}),workspace:Ve.default({status:"unknown"}),target:qe.default({strategy:"direct-port",status:"unknown"}),access:Be,apps:l.z.object({runningCount:l.z.number().int().min(0).default(0),problemCount:l.z.number().int().min(0).default(0),updatedAt:ne.optional(),revision:l.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:R.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:l.z.object({enabledCount:l.z.number().int().min(0).default(0),runningCount:l.z.number().int().min(0).default(0),errorCount:l.z.number().int().min(0).default(0),configs:l.z.array(l.z.object({configHash:l.z.string().trim().min(1),config:J,deployments:l.z.array(Y).default([]),runtime:l.z.array(X).default([])}).strict()).default([]),recentRuns:l.z.array(Q).default([])}).strict().optional(),runtime:l.z.object({kind:l.z.string().trim().max(80).optional(),containerName:l.z.string().trim().max(240).optional(),containerId:l.z.string().trim().max(240).optional(),workspacePath:l.z.string().trim().max(4096).optional(),pid:l.z.number().int().positive().optional(),port:l.z.number().int().positive().max(65535).optional(),labels:l.z.record(l.z.string(),l.z.string()).default({})}).strict().default({labels:{}}),registrationToken:l.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:ne.optional(),createdAt:ne,updatedAt:ne}).strict();function Je(e,t){return Ge.safeParse(We(e,t))}function We(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e;Object.prototype.hasOwnProperty.call(n,"receiver")&&t?.({instanceId:"string"==typeof n.id?n.id:void 0,field:"receiver"});const r=new Set(Object.keys(Ge.shape)),o={};for(const[e,t]of Object.entries(n))r.has(e)&&(o[e]=t);if(n.capabilities&&"object"==typeof n.capabilities&&!Array.isArray(n.capabilities)){const e={...n.capabilities};Object.prototype.hasOwnProperty.call(e,"apps")&&(t?.({instanceId:"string"==typeof n.id?n.id:void 0,field:"capabilities.apps"}),delete e.apps),o.capabilities=e}return o.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{const t=Ye(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Ye(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Ye(e,["code","message"])):t.issues},r=ce.safeParse(n);return r.success?r.data:void 0}(n.appInventory),o.apps=Ye(n.apps,["runningCount","problemCount","updatedAt","revision"]),o.config=Ye(n.config,["autoImportAgentConfigs"]),o.modelSelection=Ye(n.modelSelection,["codexModelHash","claudeModelHash"]),o.runtime=Ye(n.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),o.target=Ye(n.target??n.endpoints,["strategy","web","api","status"]),o.access=Ye(n.access,["strategy","web","api","ws","status"]),o}function Ye(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e;return Object.fromEntries(t.filter(e=>Object.prototype.hasOwnProperty.call(n,e)).map(e=>[e,n[e]]))}const Xe=l.z.object({instanceId:te.optional(),name:l.z.string().trim().min(1).max(160),projectId:te.optional(),source:Ae.optional(),nodeId:te.optional(),runtimeId:te.optional(),imageId:te.optional(),instanceVersion:l.z.string().trim().max(80).optional(),protocolVersion:ee,build:de.optional(),controlMode:l.z.enum(["standalone","controlled"]).default("controlled"),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),appInventory:ce.optional(),target:qe.default({strategy:"direct-port",status:"unknown"}),workspace:Ve.default({status:"unknown"}),registrationToken:l.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Z||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),Qe=l.z.object({status:Ge.shape.status.optional(),health:Ge.shape.health.optional(),protocolVersion:ee,build:de.optional(),capabilities:Ge.shape.capabilities.optional(),appInventory:ce.optional(),apps:Ge.shape.apps.optional(),aiSessions:Ge.shape.aiSessions.optional(),triggers:Ge.shape.triggers.optional(),workspace:Ve.optional(),target:qe.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Z||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})}),Ze=l.z.enum(["web","telegram","wechat","dingding"]);function et(e){return e.readyState===(e.OPEN??1)}function tt(e,t,n){e.close(function(e){if("number"==typeof e&&Number.isInteger(e))return e>=3e3&&e<=4999||e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e?e:void 0}(t),function(e){const t=Buffer.isBuffer(e)?e.toString("utf8"):"string"==typeof e?e:"";if(Buffer.byteLength(t,"utf8")<=123)return t;let n="";for(const e of t){const t=`${n}${e}`;if(Buffer.byteLength(t,"utf8")>123)break;n=t}return n}(n)||void 0)}function nt(e,t){e.send(t.data,{binary:t.isBinary})}function rt(e,t){if(et(e))for(const n of t.splice(0))nt(e,n)}function ot(e,t){return new Promise((n,r)=>{const o=s.spawn(e,t,{stdio:["ignore","pipe","pipe"]}),i=[],a=[];o.stdout.on("data",e=>i.push(Buffer.from(e))),o.stderr.on("data",e=>a.push(Buffer.from(e))),o.on("error",r),o.on("close",t=>{const o={stdout:Buffer.concat(i).toString("utf8").trim(),stderr:Buffer.concat(a).toString("utf8").trim()};if(0===t)return void n(o);const s=new Error(o.stderr||`${e} exited with code ${t}`);Object.assign(s,{statusCode:502,code:"RUNTIME_EXECUTOR_FAILED",details:o}),r(s)})})}l.z.object({id:te,channel:Ze,name:l.z.string().trim().min(1).max(160),enabled:l.z.boolean().default(!1),token:l.z.string().trim().max(4096).optional(),defaultChatId:l.z.string().trim().max(240).optional(),allowedUserIds:l.z.array(l.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:l.z.number().int().positive().max(6e4).default(3e3),settings:l.z.record(l.z.string(),l.z.unknown()).default({}),createdAt:ne,updatedAt:ne}).strict(),l.z.object({id:te,channel:Ze,bridgeId:te.optional(),chatSessionId:l.z.string().trim().min(1).max(240),userId:l.z.string().trim().max(240).optional(),activeProjectId:te.optional(),activeInstanceId:te.optional(),activeAiSessionId:l.z.string().trim().min(1).max(120).optional(),lastUsedAt:ne,createdAt:ne,updatedAt:ne}).strict(),l.z.object({source:l.z.object({type:l.z.literal("chat-gateway").default("chat-gateway"),channel:Ze,bridgeId:te.optional(),chatSessionId:l.z.string().trim().min(1).max(240),userId:l.z.string().trim().max(240).optional()}).strict(),message:l.z.object({text:l.z.string().trim().max(2e4).default(""),attachments:l.z.array(k).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+t.size,0)>f&&t.addIssue({code:l.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),target:l.z.object({instanceId:te,aiSessionId:l.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),l.z.object({id:te,instanceId:te,projectId:te.optional(),aiSessionId:l.z.string().trim().min(1).max(120),providerSessionId:l.z.string().trim().max(240).optional(),result:l.z.string().default(""),timeoutMs:l.z.number().positive().optional(),source:l.z.string().trim().max(120).optional(),kind:l.z.enum(["task","approval"]).default("task"),status:l.z.enum(["pending","unreachable"]).default("pending"),error:l.z.string().trim().max(2048).optional(),lastSeenAt:ne}).strict();class it{runCommand;publishHost;constructor(e=ot,t={}){this.runCommand=e,this.publishHost=t.publishHost||"127.0.0.1"}async start(e){ct(e);const t=e.instance.runtime.containerName||at(e.instance.id);if(await this.runCommand("docker",["start",t]).catch(()=>{}))return this.runningResult(e,t,e.instance.runtime.containerId);await this.ensureImageAvailable(e.image);const n=await this.runCommand("docker",function(e,t,n={}){const r=n.publishHost||"127.0.0.1",o=e.node?.id||"node_unset",i=e.runtime?.id||"runtime_local_docker",s=["run","-d","--name",t,"--label",`task-handoff.instance-id=${e.instance.id}`,"--label",`task-handoff.project-id=${e.project.id}`,"--label",`task-handoff.node-id=${o}`,"--label",`task-handoff.runtime-id=${i}`,"--label",`task-handoff.image-id=${e.image.id}`,"--shm-size","1gb","--security-opt","seccomp=unconfined","--network","bridge","--add-host","host.docker.internal:host-gateway","--tmpfs","/tmp:rw,mode=1777","-v",`${t}-data:/data`,"-v",`${t}-agent-home:/home/agent`,"-p",`${r}::8080`,"-e","TASK_HANDOFF_CONTROL_MODE=controlled","-e",`TASK_HANDOFF_NODE_AGENT_URL=${e.nodeAgentUrl||e.node?.endpoint||""}`,"-e",`TASK_HANDOFF_INSTANCE_ID=${e.instance.id}`,"-e",`TASK_HANDOFF_INSTANCE_NAME=${e.instance.name}`,"-e",`TASK_HANDOFF_REGISTRATION_TOKEN=${e.instance.registrationToken||""}`,"-e",`TASK_HANDOFF_PROJECT_ID=${e.project.id}`,"-e",`TASK_HANDOFF_NODE_ID=${o}`,"-e",`TASK_HANDOFF_RUNTIME_ID=${i}`,"-e",`TASK_HANDOFF_IMAGE_ID=${e.image.id}`,"-e","TASK_HANDOFF_CHAT_BRIDGES=none","-e",`TASK_HANDOFF_WORKSPACE=${e.project.workspacePolicy.path||"/workspace"}`,"-e",`TASK_HANDOFF_WORKSPACE_MODE=${e.project.workspacePolicy.mode}`];"local-folder"===e.project.source.type?s.push("-v",`${e.project.source.path}:${e.project.workspacePolicy.path||"/workspace"}:${e.project.workspacePolicy.readOnly?"ro":"rw"}`):(s.push("-e",`TASK_HANDOFF_PROJECT_SOURCE=${JSON.stringify(e.project.source)}`),s.push("-e",`TASK_HANDOFF_WORKSPACE_POLICY=${JSON.stringify(e.project.workspacePolicy)}`),s.push("-e",`TASK_HANDOFF_GIT_URL=${e.project.source.url}`),e.project.source.ref?.commit?s.push("-e",`TASK_HANDOFF_GIT_COMMIT=${e.project.source.ref.commit}`):e.project.source.ref?.name&&s.push("-e",`TASK_HANDOFF_GIT_REF=${e.project.source.ref.name}`),e.project.source.clone?.depth&&s.push("-e",`TASK_HANDOFF_GIT_DEPTH=${e.project.source.clone.depth}`),s.push("-e","TASK_HANDOFF_GIT_SUBMODULES="+(e.project.source.clone?.submodules?"true":"false")),s.push("-e","TASK_HANDOFF_GIT_LFS="+(e.project.source.clone?.lfs?"true":"false")),e.project.source.clone?.subdirectory&&s.push("-e",`TASK_HANDOFF_WORKSPACE_SUBDIRECTORY=${e.project.source.clone.subdirectory}`));for(const[t,n]of Object.entries(e.image.defaultEnv))s.push("-e",`${t}=${n}`);for(const[t,n]of Object.entries(e.modelEnv||{}))s.push("-e",`${t}=${n}`);return s.push(e.image.image),s}(e,t,{publishHost:this.publishHost}));return this.runningResult(e,t,n.stdout||void 0)}async runningResult(e,t,n){const r=await this.containerPort(t,"8080/tcp"),o=r?`http://127.0.0.1:${r}`:void 0,i=e.node?.id||"node_unset",s=e.runtime?.id||"runtime_local_docker";return{status:"registering",health:"unknown",connectionStatus:o?"online":"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:o?"reachable":"unknown",...o?{web:o,api:`${o}/api`}:{}},workspace:{mode:e.project.workspacePolicy.mode,status:"pending",path:e.project.workspacePolicy.path},runtime:{...e.instance.runtime,kind:"docker",containerName:t,...n?{containerId:n}:{},labels:{...e.instance.runtime.labels,"task-handoff.instance-id":e.instance.id,"task-handoff.project-id":e.project.id,"task-handoff.node-id":i,"task-handoff.runtime-id":s,"task-handoff.image-id":e.image.id}}}}async stop(e){return await this.runCommand("docker",["stop",e.instance.runtime.containerName||at(e.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async restart(e){ct(e);const t=e.instance.runtime.containerName||at(e.instance.id);return await this.runCommand("docker",["restart",t]),this.runningResult(e,t,e.instance.runtime.containerId)}async delete(e){return await this.runCommand("docker",["rm","-f",e.instance.runtime.containerName||at(e.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async containerPort(e,t){const n=(await this.runCommand("docker",["port",e,t]).catch(()=>({stdout:"",stderr:""}))).stdout.split(/\r?\n/).find(Boolean),r=n?.match(/:(\d+)$/);return r?.[1]}async ensureImageAvailable(e){if(!await this.runCommand("docker",["image","inspect",e.image]).then(()=>!0,()=>!1)){if("local"===e.registry){const t=new Error(`Local Docker image ${e.image} was not found. Build it or add an existing local image before starting an instance.`);throw Object.assign(t,{statusCode:400,code:"LOCAL_IMAGE_NOT_FOUND"}),t}await this.runCommand("docker",["pull",e.image])}}}async function st(e=ot){return(await e("docker",["images","--format","{{json .}}"])).stdout.split(/\r?\n/).map(e=>e.trim()).filter(Boolean).map(e=>{const t=JSON.parse(e),n=t.Repository||"<none>",r=t.Tag||"<none>",o=t.ID||t.IDShort||"",i="<none>"!==n&&"<none>"!==r?`${n}:${r}`:o;return{repository:n,tag:r,id:o,createdSince:t.CreatedSince,size:t.Size,reference:i}}).filter(e=>Boolean(e.reference))}function at(e){return`task-handoff-${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`}function ct(e){if("local-folder"===e.project.source.type&&e.project.source.ownerNodeId&&e.project.source.ownerNodeId!==e.node.id){const e=new Error("Local folder projects can only start on the node that owns the folder.");throw Object.assign(e,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_OWNER_NODE"}),e}if(!e.image.image.trim()){const e=new Error("Image profile must reference a registered image.");throw Object.assign(e,{statusCode:400,code:"IMAGE_PROFILE_INVALID"}),e}}const dt=l.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),lt=l.z.object({type:l.z.enum(["app-session","provider-session","adapter-key"]),id:l.z.string().trim().min(1).max(240),agent:l.z.string().trim().min(1).max(80).optional(),adapter:l.z.string().trim().min(1).max(80).optional(),key:l.z.string().trim().min(1).max(240).optional()}).strict(),ut=l.z.record(l.z.string(),l.z.unknown()).and(l.z.object({id:l.z.string().trim().min(1).max(160),appId:l.z.string().trim().min(1).max(160).optional(),title:l.z.string().trim().max(240).optional(),kind:l.z.string().trim().max(80).optional(),status:dt.default("unknown"),bindings:l.z.array(lt).max(40).default([]),workspace:l.z.object({cwd:l.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:l.z.string().datetime().optional(),updatedAt:l.z.string().datetime().optional()})),pt=l.z.object({runningCount:l.z.number().int().min(0).default(0),problemCount:l.z.number().int().min(0).default(0),sessions:l.z.array(ut).default([]),updatedAt:l.z.string().datetime()}).strict();l.z.object({streamId:l.z.string().trim().min(1).max(240),revision:l.z.number().int().min(0),lastEventAt:l.z.string().datetime(),snapshot:pt}).strict(),pt.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:l.z.number().int().min(0).optional()});const mt=l.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),gt=l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),nodeId:l.z.string().trim().min(1).max(160).optional(),revision:l.z.number().int().min(0),previousRevision:l.z.number().int().min(0).optional(),traceId:l.z.string().trim().min(1).max(160),generatedAt:l.z.string().datetime(),reason:mt}).strict(),ht=l.z.object({meta:gt,snapshot:pt}).strict(),ft=l.z.object({meta:gt,session:ut}).strict(),yt=l.z.object({meta:gt,sessionId:l.z.string().trim().min(1).max(160),tombstone:ut.optional(),expiresAt:l.z.string().datetime().optional()}).strict();function It(e){return e.startsWith("ai-session.")||e.startsWith("ai.session.")?"ai.sessions":e.startsWith("app-session.")?"app.sessions":e.startsWith("receiver.")?"receiver":e.startsWith("conversation.")?"conversations":e.startsWith("trigger.")?"triggers":e.startsWith("channel.")?"channels":e.startsWith("instance.")?"instances":e.startsWith("node.")?"nodes":e.startsWith("project.")?"projects":e.startsWith("model.")?"models":e.startsWith("image.")?"images":"system"}l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),sinceRevision:l.z.number().int().min(0),latestRevision:l.z.number().int().min(0),earliestRetainedRevision:l.z.number().int().min(0),syncRequired:l.z.boolean().default(!1),events:l.z.array(l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("app-session.snapshot"),payload:ht}).strict(),l.z.object({type:l.z.literal("app-session.patch"),payload:ft}).strict(),l.z.object({type:l.z.literal("app-session.removed"),payload:yt}).strict()])).default([])}).strict();const bt=l.z.enum(["ai.sessions","app.sessions"]),At=l.z.object({topic:bt,instanceId:l.z.string().trim().min(1).max(160),streamId:l.z.string().trim().min(1).max(240),latestRevision:l.z.number().int().min(0),earliestRetainedRevision:l.z.number().int().min(0)}).strict(),zt=l.z.object({protocolVersion:l.z.literal(1),streams:l.z.array(At)}).strict();class wt{attempt=0;timer;get pending(){return Boolean(this.timer)}get attempts(){return this.attempt}schedule(e,t={}){if(this.timer)return;const n=function(e,t=Math.random){const n=Math.min(3e4,1e3*2**Math.max(0,e));return Math.min(3e4,Math.round(n*(.75+.5*t())))}(this.attempt,t.random);this.attempt+=1;const r=t.setTimeoutFn||setTimeout;return this.timer=r(()=>{this.timer=void 0,e()},n),{attempt:this.attempt,delay:n}}reset(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0,this.attempt=0}cancel(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0}}class St{sockets=new Map;socketUrls=new Map;retries=new Map;outputs=new Set;state;token;logger;safetyIntervalMs;lastAiSessionEventByInstance=new Map;lastAppSessionEventByInstance=new Map;timer;reconnectAttempts=0;safetyReconciliations=0;createSocket;setIntervalFn;clearIntervalFn;constructor(e,t,n={}){var r;this.state=e,this.token=t,this.logger=n.logger,this.safetyIntervalMs=(r=n.safetyIntervalMs,Math.min(6e4,Math.max(3e4,r??45e3))),this.createSocket=n.createSocket||((e,t)=>new d(e,t)),this.setIntervalFn=n.setIntervalFn||setInterval,this.clearIntervalFn=n.clearIntervalFn||clearInterval}start(){this.sync(),this.timer=this.setIntervalFn(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(this.clearIntervalFn(this.timer),this.timer=void 0);for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();this.retries.clear(),this.outputs.clear()}addOutput(e){return this.outputs.add(e),e.on("close",()=>this.outputs.delete(e)),e.on("error",()=>this.outputs.delete(e)),this.syncNow(),()=>{this.outputs.delete(e)}}syncNow(){this.sync()}diagnostics(){return{reconnectAttempts:this.reconnectAttempts,safetyReconciliations:this.safetyReconciliations,activeConnections:this.sockets.size,pendingRetries:[...this.retries.values()].filter(e=>e.timer.pending).length,safetyIntervalMs:this.safetyIntervalMs}}sync(){if(!this.outputs.size){for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();return void this.retries.clear()}const e=new Set;for(const t of this.state.listInstances()){const n=Nt(t);if(!n)continue;e.add(t.id);const r=this.retries.get(t.id);r?.timer.pending&&r.url!==n&&(r.timer.cancel(),this.retries.delete(t.id)),this.socketUrls.get(t.id)!==n&&this.sockets.has(t.id)&&(this.sockets.get(t.id)?.close(),this.sockets.delete(t.id)),this.sockets.has(t.id)||this.retries.get(t.id)?.timer.pending||this.connect(t.id,n)}for(const[t,n]of this.sockets)if(!e.has(t)){n.close(),this.sockets.delete(t),this.socketUrls.delete(t);const e=this.retries.get(t);e?.timer.cancel(),this.retries.delete(t)}for(const[t,n]of this.retries)e.has(t)||(n.timer.cancel(),this.retries.delete(t),this.socketUrls.delete(t))}connect(e,t){const n=this.token?{authorization:`Bearer ${this.token}`}:void 0,r=this.createSocket(t,{headers:n});this.sockets.set(e,r),this.socketUrls.set(e,t),r.on("open",()=>{const n=this.retries.get(e);n?.timer.reset(),this.retries.set(e,{timer:n?.timer||new wt,url:t}),r.readyState===d.OPEN&&(r.send(JSON.stringify({v:1,type:"subscribe",topics:[y,"app.sessions","instances"]})),this.logger?.info?.({instanceId:e,url:t},"ai-session.event.forward.connect"),this.logger?.info?.({instanceId:e,url:t},"app-session.event.forward.connect"))}),r.on("message",t=>{const n=function(e,t){try{const n=JSON.parse(String(e));if(!n||"object"!=typeof n||Array.isArray(n))return;const r=n,o="string"==typeof r.type?r.type:"";if(!o||"events.connected"===o)return;const i=r.scope&&"object"==typeof r.scope&&!Array.isArray(r.scope)?r.scope:{};return{v:1,id:"string"==typeof r.id?r.id:`evt_${Date.now().toString(36)}`,seq:Number(r.seq)||0,type:o,topic:"string"==typeof r.topic?r.topic:It(o),createdAt:"string"==typeof r.createdAt?r.createdAt:(new Date).toISOString(),payload:"payload"in r?r.payload:{},scope:{...i,instanceId:t}}}catch{return}}(t,e);if(!n)return;if("streams.hello"===n.type){const t=zt.safeParse(n.payload);if(!t.success)return this.logger?.warn?.({instanceId:e,issues:t.error.issues},"session-stream.handshake.invalid"),void r.close(1002,"Incompatible session stream handshake.");const o=JSON.stringify({type:"node-agent.streams.hello",instanceId:e,payload:t.data});for(const e of this.outputs)e.readyState===d.OPEN&&e.send(o);return}this.recordAiSessionEvent(e,n),this.recordAppSessionEvent(e,n);const o=JSON.stringify({type:"node-agent.event.forwarded",event:n});for(const e of this.outputs)e.readyState===d.OPEN&&e.send(o)}),r.on("close",()=>{this.sockets.get(e)===r&&(this.sockets.delete(e),this.socketUrls.delete(e),this.scheduleReconnect(e))}),r.on("error",()=>{this.logger?.warn?.({instanceId:e,url:t},"ai-session.event.forward.error"),this.logger?.warn?.({instanceId:e,url:t},"app-session.event.forward.error"),r.close()})}scheduleReconnect(e){const t=this.state.listInstances().find(t=>t.id===e),n=t&&Nt(t);if(!n||!this.outputs.size)return;const r=this.retries.get(e)??{timer:new wt};if(r.timer.pending)return;r.url=n;const o=r.timer.schedule(()=>{this.sockets.has(e)||this.connect(e,n)});o&&(this.reconnectAttempts+=1,this.logger?.info?.({instanceId:e,url:n,attempt:o.attempt,delay:o.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.connection.reconnect-scheduled")),this.retries.set(e,r)}recordAiSessionEvent(e,t){if(t.topic!==y)return;const n=kt(t.payload),r=this.lastAiSessionEventByInstance.get(e);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:e,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"ai-session.event.forward.gap"),this.lastAiSessionEventByInstance.set(e,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:e,traceId:n.traceId,eventType:t.type,streamId:n.streamId,revision:n.revision},"ai-session.event.forward.message")}recordAppSessionEvent(e,t){if("app.sessions"!==t.topic)return;const n=kt(t.payload),r=this.lastAppSessionEventByInstance.get(e);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:e,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"app-session.event.forward.gap"),this.lastAppSessionEventByInstance.set(e,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:e,traceId:n.traceId,eventType:t.type,streamId:n.streamId,revision:n.revision},"app-session.event.forward.message")}}function kt(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{},n=t.meta&&"object"==typeof t.meta&&!Array.isArray(t.meta)?t.meta:{};return{streamId:"string"==typeof n.streamId?n.streamId:void 0,revision:"number"==typeof n.revision?n.revision:void 0,traceId:"string"==typeof n.traceId?n.traceId:void 0}}function Nt(e){const t=e.target?.api||e.target?.web;if(t)try{const e=new URL("/api/events",t);return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}catch{return}}function Et(e){try{return t.statSync(e).isDirectory()}catch{return!1}}function _t(e,r){let o;try{o=t.statSync(e)}catch{return}if(!o.isDirectory())return;const i=n.resolve(e),s=n.basename(i)||i;let a=[];try{a=r>0?t.readdirSync(i,{withFileTypes:!0}):[]}catch{a=[]}const c=r>0?a.filter(e=>e.isDirectory()||e.isSymbolicLink()&&Et(n.join(i,e.name))).sort((e,t)=>e.name.localeCompare(t.name)).slice(0,80).flatMap(e=>{const t=_t(n.join(i,e.name),r-1);return t?[t]:[]}):[];return{name:s,path:i,children:c}}function vt(e){return(e.path?[e.path]:function(){if("win32"!==process.platform)return[n.parse(n.resolve(process.cwd())).root];const e=n.parse(n.resolve(process.cwd())).root,t=Array.from({length:26},(e,t)=>`${String.fromCharCode(65+t)}:\\`);return[...new Set([e,...t])].filter(Et)}()).flatMap(t=>{const n=_t(t,e.depth);return n?[n]:[]})}const Ot=l.z.object({path:l.z.string().trim().min(1).max(2048),method:l.z.string().trim().min(1).max(20).default("GET"),headers:l.z.record(l.z.string(),l.z.string()).default({}),body:l.z.string().optional(),bodyBase64:l.z.string().optional()}).strict(),xt=l.z.object({id:l.z.string().trim().min(1).max(120).optional(),name:l.z.string().trim().min(1).max(160),path:l.z.string().trim().min(1).max(4096),defaultImageId:l.z.string().trim().min(1).max(120).optional(),labels:l.z.record(l.z.string(),l.z.string()).default({})}).strict(),Tt=l.z.object({path:l.z.string().trim().min(1).max(4096).optional(),depth:l.z.coerce.number().int().min(0).max(1).default(0)}).strict(),Ct=l.z.object({id:l.z.string().trim().min(1).max(120).optional(),name:l.z.string().trim().min(1).max(160).optional(),runtimeId:l.z.string().trim().min(1).max(120),imageId:l.z.string().trim().min(1).max(120).optional(),projectId:l.z.string().trim().min(1).max(120).optional(),image:Me.optional(),source:Ae,sourceSnapshot:l.z.record(l.z.string(),l.z.unknown()).default({}),config:Ge.shape.config.optional(),modelSelection:Ge.shape.modelSelection}).strict(),Dt=l.z.object({name:l.z.string().trim().min(1).max(160).optional(),config:Ge.shape.config.optional(),modelSelection:Ge.shape.modelSelection.optional()}).strict(),jt=l.z.object({id:l.z.string().trim().min(1).max(120).optional(),name:l.z.string().trim().min(1).max(160),type:l.z.enum(["docker","kubernetes","local"]),status:l.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:l.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).optional(),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),labels:l.z.record(l.z.string(),l.z.string()).default({})}).strict(),Rt=jt.omit({id:!0}).partial().strict();function Pt(e=function(){return process.env.TASK_HANDOFF_NODE_AGENT_DATA_DIR||n.join(p.homedir(),".config","task-handoff","node-agent")}()){const t=n.resolve(e);return{dataDir:t,identityPath:n.join(t,"identity.json"),settingsPath:n.join(t,"runtime-settings.json"),localFoldersDir:n.join(t,"local-folders"),nodeRuntimesDir:n.join(t,"node-runtimes"),controlledInstancesDir:n.join(t,"controlled-instances"),nodeModelsDir:n.join(t,"models"),modelAssignmentsDir:n.join(t,"model-assignments"),modelEnvironmentsDir:n.join(t,"model-environments"),updatesDir:n.join(t,"updates"),logsDir:n.join(t,"logs")}}function Mt(e){t.mkdirSync(e,{recursive:!0})}function Ft(e,t){Mt(n.dirname(e)),m.sync(e,`${JSON.stringify(t,null,2)}\n`,{encoding:"utf8"})}function $t(e,t){console.warn(JSON.stringify({message:e,...t}))}function Lt(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function Ut(e,n){const r=n.logger||$t;let o;try{o=JSON.parse(t.readFileSync(e,"utf8"))}catch(t){return void r("stored JSON could not be read",{filePath:e,...Lt(t)})}if(!n.schema)return o;const i=n.sanitize?n.sanitize(o):o,s=n.schema.safeParse(i);if(s.success)return s.data;const a="strip"in n.schema&&"function"==typeof n.schema.strip?n.schema.strip().safeParse(i):void 0;if(a?.success)return r("unknown stored fields were ignored",{filePath:e,issues:s.error.issues}),a.data;r("stored record failed validation and was isolated",{filePath:e,issues:s.error.issues})}function Ht(e){return`${e}_${u.randomUUID().replace(/-/g,"").slice(0,20)}`}function Kt(){return u.randomBytes(24).toString("base64url")}class qt{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){Mt(this.directory)}filePath(e){return n.join(this.directory,`${e}.json`)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=Ut(n.join(this.directory,e),this.options);return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?Ut(n,this.options):void 0}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,n=this.options.schema?this.options.schema.parse(t):t;return Ft(this.filePath(n.id),n),n}patch(e,t){const n=this.get(e);if(!n)return;const r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:(new Date).toISOString()};return this.put(r)}delete(e){const n=this.filePath(e);return!!t.existsSync(n)&&(t.unlinkSync(n),!0)}}class Bt{filePathValue;defaults;options;constructor(e,t,n={}){this.filePathValue=e,this.defaults=t,this.options=n}init(){Mt(n.dirname(this.filePathValue)),t.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),t.existsSync(this.filePathValue)?Ut(this.filePathValue,this.options)??this.defaults():this.defaults()}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,n=this.options.schema?this.options.schema.parse(t):t;return Ft(this.filePathValue,n),n}}class Vt extends Error{code;lockPath;owner;constructor(e,t,n={}){const r=t?.pid?` pid ${t.pid}`:"";super(`${n.label||"Process"} is already running${r}. Lock: ${e}`),this.name="ProcessSingletonError",this.code=n.code||"PROCESS_ALREADY_RUNNING",this.lockPath=e,this.owner=t}}function Gt(e){return n.join(e,"owner.json")}function Jt(e){return n.join(e,"initializing.json")}function Wt(e){return n.join(e,"recovering.json")}function Yt(e){try{const n=JSON.parse(t.readFileSync(Gt(e),"utf8"));if(!Number.isInteger(n.pid)||Number(n.pid)<=0||"string"!=typeof n.hostname||!n.hostname||"string"!=typeof n.command||"string"!=typeof n.acquiredAt||!Number.isFinite(Date.parse(n.acquiredAt))||"string"!=typeof n.token||!n.token)return;return n}catch{return}}function Xt(e){if(!Number.isInteger(e)||Number(e)<=0)return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function Qt(e,r){const o=n.join(e,`.owner-${r.token}.tmp`);t.writeFileSync(o,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const i=Zt(e);if(i?.token!==r.token)throw en(o),new Error(`Process lock initialization was superseded: ${e}`);t.renameSync(o,Gt(e)),en(Jt(e))}function Zt(e){try{return JSON.parse(t.readFileSync(Jt(e),"utf8"))}catch{return}}function en(e){try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function tn(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function nn(e){try{const n=Zt(e),r=Date.parse(n?.startedAt||"");return Number.isFinite(r)?Math.max(0,Date.now()-r):Math.max(0,Date.now()-t.statSync(e).mtimeMs)}catch{return 0}}function rn(e,r){try{return t.writeFileSync(Wt(e),`${JSON.stringify({token:r,pid:process.pid,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(o){if("EEXIST"===o.code)return!!function(e){const r=Wt(e),o=on(r),i=Date.parse(o?.startedAt||"");if(Xt(o?.pid)||Number.isFinite(i)&&Date.now()-i<2e3)return!1;const s=n.join(e,`.recovering-${u.randomUUID()}.stale`);try{t.renameSync(r,s)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const a=on(s);if(a?.token===o?.token)return en(s),!0;try{t.linkSync(s,r)}catch(e){if("EEXIST"!==e.code)throw e}finally{en(s)}return!1}(e)&&rn(e,r);if("ENOENT"===o.code)return!1;throw o}}function on(e){try{return JSON.parse(t.readFileSync(e,"utf8"))}catch{return}}function sn(e,t){return on(Wt(e))?.token===t}function an(e,t=process.platform){const r=n.resolve(e),o=u.createHash("sha256").update(r).digest("hex").slice(0,16);return"win32"===t?`\\\\.\\pipe\\task-handoff-node-agent-${o}`:n.join("/tmp",`task-handoff-node-agent-${process.getuid?.()??"user"}`,`${o}.sock`)}const cn="TASK_HANDOFF_NODE_AGENT_V1";function dn(e=""){return u.createHash("sha256").update(e).digest("hex")}function ln(e,t){return u.createHmac("sha256",e).update(function(e){return[cn,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function un(e){const t=e.timestamp||(new Date).toISOString(),n=e.nonce||u.randomBytes(18).toString("base64url"),r=dn(e.body||""),o=ln(e.secret,{keyId:e.keyId,method:e.method,pathWithQuery:e.pathWithQuery,timestamp:t,nonce:n,bodySha256:r});return{"x-taskhandoff-node-id":e.nodeId,"x-taskhandoff-key-id":e.keyId,"x-taskhandoff-timestamp":t,"x-taskhandoff-nonce":n,"x-taskhandoff-body-sha256":r,"x-taskhandoff-signature":o}}const pn=new Set(["DELETE","GET","HEAD","PATCH","POST","PUT","OPTIONS"]);function mn(){return(new Date).toISOString()}function gn(e,t){if(!e)return!0;const n=g.valid(e),r=g.valid(t);return n&&r?g.gt(r,n):e!==t}async function hn(e,t,n){const r=In(n),o=await e(fn(),["view",`${t}@${r}`,"version","--json"]).catch(e=>{if(!function(e){if(!e||"object"!=typeof e)return!1;const t=e,n=[t.message,t.details?.stdout,t.details?.stderr].filter(e=>"string"==typeof e).join("\n");return/\bE404\b/i.test(n)}(e))throw e});if(!o)return;const i=JSON.parse(o.stdout);if("string"!=typeof i)throw new Error(`npm did not resolve ${t}@${r} to one version.`);return i}function fn(){return process.env.TASK_HANDOFF_NPM_COMMAND||"npm"}function yn(e){return me.parse({target:e.target,source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:e.currentVersion||"unavailable",updateAvailable:!1,reason:`No ${e.channel} release is currently published.`,checkedAt:mn()})}function In(e){return"stable"===e?"latest":e}function bn(e){if(Array.isArray(e))return e.map(bn).find(Boolean);if(!e||"object"!=typeof e)return;const t=e;return"string"==typeof t.digest&&t.digest.startsWith("sha256:")?t.digest:"string"==typeof t.Digest&&t.Digest.startsWith("sha256:")?t.Digest:Object.values(t).map(bn).find(Boolean)}class An{records;constructor(e){this.records=new qt(e.updatesDir,{schema:ge})}init(){this.records.init()}list(){return this.records.list().sort((e,t)=>t.createdAt.localeCompare(e.createdAt))}create(e,t){const n=mn();return this.records.put(ge.parse({id:Ht("update"),nodeId:e,target:t.target,source:t.source,channel:t.channel,fromVersion:t.currentVersion,toVersion:t.availableVersion,artifactRef:t.artifactRef,status:"queued",createdAt:n,updatedAt:n}))}patch(e,t){const n=this.records.get(e);if(!n)throw Object.assign(new Error(`Update job ${e} was not found.`),{statusCode:404,code:"UPDATE_JOB_NOT_FOUND"});return this.records.put(ge.parse({...n,...t,id:e,createdAt:n.createdAt,updatedAt:mn()}))}run(e,t){(async()=>{this.patch(e.id,{status:"updating",startedAt:mn(),error:void 0});try{await t(e),this.patch(e.id,{status:"succeeded",completedAt:mn()})}catch(t){this.patch(e.id,{status:"failed",error:t instanceof Error?t.message:String(t),completedAt:mn()})}})()}}const zn=l.z.record(l.z.string(),l.z.string()),wn=new Set(["OPENAI_API_KEY","OPENAI_BASE_URL","CODEX_MODEL","TASK_HANDOFF_CODEX_BASE_URL","TASK_HANDOFF_CODEX_MODEL","TASK_HANDOFF_CODEX_MODEL_ID","ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","CLAUDE_MODEL","TASK_HANDOFF_CLAUDE_MODEL","TASK_HANDOFF_CLAUDE_MODEL_ID"]);class Sn{directory;constructor(e){this.directory=e}get(e){try{return zn.parse(JSON.parse(t.readFileSync(this.filePath(e),"utf8")))}catch(t){if(t&&"object"==typeof t&&"code"in t&&"ENOENT"===t.code)return{};throw new Error(`Stored model environment for instance ${e} is invalid: ${t instanceof Error?t.message:String(t)}`)}}has(e){return t.existsSync(this.filePath(e))}listInstanceIds(){return t.existsSync(this.directory)?t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).map(e=>e.slice(0,-5)).sort():[]}put(e,n){const r=zn.parse(n);t.mkdirSync(this.directory,{recursive:!0,mode:448});const o=this.filePath(e);return m.sync(o,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(o,384),r}delete(e){t.rmSync(this.filePath(e),{force:!0})}filePath(e){return n.join(this.directory,`${e}.json`)}}class kn{directory;nodeId;constructor(e,t){this.directory=e,this.nodeId=t}init(){t.mkdirSync(this.directory,{recursive:!0,mode:448}),t.chmodSync(this.directory,448)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=this.read(n.join(this.directory,e));return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?this.read(n):void 0}put(e){const n=Ne.parse(e);this.init();const r=this.filePath(n.id);return m.sync(r,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(r,384),n}delete(e){const n=this.filePath(e);return!!t.existsSync(n)&&(t.unlinkSync(n),!0)}read(e){try{const r=JSON.parse(t.readFileSync(e,"utf8")),o=n.basename(e,".json"),i=Ne.safeParse(r);if(i.success&&i.data.id===o&&Pe(i.data)===i.data.id)return i.data;const s=Ne.strip().safeParse(r);return s.success&&s.data.id===o&&Pe(s.data)===s.data.id?(console.warn(JSON.stringify({message:"unknown stored node model fields were ignored",nodeId:this.nodeId,filePath:e})),s.data):void console.warn(JSON.stringify({message:"stored node model failed validation and was isolated",nodeId:this.nodeId,filePath:e}))}catch(t){return void console.warn(JSON.stringify({message:"stored node model could not be read",nodeId:this.nodeId,filePath:e,error:t instanceof Error?t.message:String(t)}))}}filePath(e){return n.join(this.directory,`${e}.json`)}}class Nn{directory;constructor(e){this.directory=e}init(){t.mkdirSync(this.directory,{recursive:!0,mode:448}),t.chmodSync(this.directory,448)}get(e){const n=this.filePath(e);if(t.existsSync(n))try{const r=JSON.parse(t.readFileSync(n,"utf8")),o=ve.safeParse(r);if(o.success)return o.data;const i=ve.strip().safeParse(r);if(i.success)return console.warn(JSON.stringify({message:"unknown stored model assignment fields were ignored",instanceId:e,filePath:n})),i.data;throw o.error}catch(t){throw Object.assign(new Error(`Stored model assignment for instance ${e} is invalid.`),{statusCode:409,code:"NODE_MODEL_ASSIGNMENT_INVALID",cause:t})}}put(e){const n=ve.parse(e);this.init();const r=this.filePath(n.instanceId);return m.sync(r,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(r,384),n}delete(e){t.rmSync(this.filePath(e),{force:!0})}filePath(e){return n.join(this.directory,`${e}.json`)}}class En{usedNonces=new Map;identity;nodeId;overrideSecret;overrideKeyId;nowMs;constructor(e,t,n,r,o=Date.now){this.identity=e,this.nodeId=t,this.overrideSecret=n,this.overrideKeyId=r,this.nowMs=o}hasRemoteSecrets(){return this.identity.remoteSecrets(this.overrideSecret,this.overrideKeyId).length>0}verify(e){const t=function(e){const t=new Map;for(const[n,r]of Object.entries(e)){const e=Array.isArray(r)?r.join(","):"string"==typeof r?r:void 0===r?"":String(r);t.set(n.toLowerCase(),e)}return{nodeId:t.get("x-taskhandoff-node-id")||"",keyId:t.get("x-taskhandoff-key-id")||"",timestamp:t.get("x-taskhandoff-timestamp")||"",nonce:t.get("x-taskhandoff-nonce")||"",bodySha256:t.get("x-taskhandoff-body-sha256")||"",signature:t.get("x-taskhandoff-signature")||""}}(e.headers);if(!t.nodeId&&!t.signature)return!1;const n=this.identity.remoteSecrets(this.overrideSecret,this.overrideKeyId);if(!n.length)return!1;if(t.nodeId!==this.nodeId)throw Object.assign(new Error("Invalid node agent HMAC node id."),{statusCode:401,code:"NODE_AGENT_HMAC_NODE_MISMATCH"});if(!t.keyId)throw Object.assign(new Error("Node agent HMAC key id is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_MISSING"});const r=n.filter(e=>t.keyId===e.keyId);if(!r.length)throw Object.assign(new Error("Invalid node agent HMAC key id."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_INVALID"});const o=Date.parse(t.timestamp);if(!Number.isFinite(o)||Math.abs(this.nowMs()-o)>6e4)throw Object.assign(new Error("Node agent HMAC timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_HMAC_TIMESTAMP_INVALID"});if(!t.nonce)throw Object.assign(new Error("Node agent HMAC nonce is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_MISSING"});this.pruneNonces();const i=`${t.nodeId}:${t.keyId}:${t.nonce}`;if(this.usedNonces.has(i))throw Object.assign(new Error("Node agent HMAC nonce was already used."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_REPLAY"});const s=dn(null==(a=e.body)?"":"string"==typeof a||Buffer.isBuffer(a)?a:JSON.stringify(a));var a;if(t.bodySha256!==s)throw Object.assign(new Error("Node agent HMAC body hash mismatch."),{statusCode:401,code:"NODE_AGENT_HMAC_BODY_HASH_INVALID"});if(!r.some(n=>function(e,t){const n=Buffer.from(e,"hex"),r=Buffer.from(t,"hex");return n.length===r.length&&u.timingSafeEqual(n,r)}(t.signature,ln(n.secret,{keyId:t.keyId,method:e.method,pathWithQuery:e.url,timestamp:t.timestamp,nonce:t.nonce,bodySha256:s}))))throw Object.assign(new Error("Invalid node agent HMAC signature."),{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_INVALID"});return this.usedNonces.set(i,o+6e4+1),t.keyId}pruneNonces(){const e=this.nowMs();for(const[t,n]of this.usedNonces)n<=e&&this.usedNonces.delete(t)}}const _n=l.z.string().trim().min(1).max(160),vn=l.z.string().datetime(),On=l.z.string().min(1).max(4096),xn=l.z.string().trim().url().max(2048),Tn=l.z.object({tokenHash:l.z.string().trim().min(1).max(256),expiresAt:vn,createdAt:vn,controlPlaneName:l.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:xn.optional()}).strip(),Cn=l.z.object({id:_n,keyId:_n,name:l.z.string().trim().min(1).max(160).optional(),url:xn.optional(),secret:On,pairedAt:vn,updatedAt:vn,active:l.z.boolean().default(!0)}).strip(),Dn=l.z.object({controlPlaneName:l.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:l.z.string().trim().max(2048).optional(),expiresInMs:l.z.number().int().positive().max(36e5).optional()}).strict(),jn=l.z.object({joinToken:l.z.string().trim().min(1).max(4096),controlPlaneId:l.z.string().trim().min(1).max(160).optional(),controlPlaneName:l.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:l.z.string().trim().max(2048).optional()}).strict(),Rn=l.z.object({controlPlaneUrl:l.z.string().trim().url().max(2048),joinToken:l.z.string().trim().min(1).max(4096),controlPlaneName:l.z.string().trim().min(1).max(160).optional(),activate:l.z.boolean().optional()}).strict();function Pn(){return(new Date).toISOString()}const Mn=new Set(["nodeId","createdAt","updatedAt","pairingInvites","remoteControlPlanes"]),Fn=new Set(["tokenHash","expiresAt","createdAt","controlPlaneName","controlPlaneUrl"]),$n=new Set(["id","keyId","name","url","secret","pairedAt","updatedAt","active"]);function Ln(e,t){console.warn(JSON.stringify({message:e,...t}))}function Un(e,t){return Object.assign(new Error(e,void 0===t?void 0:{cause:t}),{code:"NODE_AGENT_IDENTITY_INVALID"})}function Hn(e,t){return Object.keys(e).filter(e=>!t.has(e))}function Kn(e,t,n){const r=vn.safeParse(e);return r.success?r.data:(n("invalid node agent identity timestamp was replaced",{field:t}),Pn())}function qn(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw Un("Node agent identity must be a JSON object.");const n=e,r=_n.safeParse(n.nodeId);if(!r.success)throw Un("Node agent identity has an invalid nodeId.",r.error);const o=r.data,i=Hn(n,Mn);return i.length&&t("unknown node agent identity fields were ignored",{fields:i}),{nodeId:o,createdAt:Kn(n.createdAt,"createdAt",t),updatedAt:Kn(n.updatedAt,"updatedAt",t),pairingInvites:Array.isArray(n.pairingInvites)?n.pairingInvites.flatMap(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return t("invalid node agent pairing invite was ignored",{}),[];const n=Hn(e,Fn);n.length&&t("unknown node agent pairing invite fields were ignored",{fields:n});const r=Tn.safeParse(e);return r.success?[r.data]:(t("invalid node agent pairing invite was ignored",{issues:r.error.issues}),[])}):[],remoteControlPlanes:Array.isArray(n.remoteControlPlanes)?n.remoteControlPlanes.flatMap(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return t("invalid node agent remote control-plane was ignored",{}),[];const n=Hn(e,$n);n.length&&t("unknown node agent remote control-plane fields were ignored",{fields:n});const r=Cn.safeParse(e);return r.success?[r.data]:(t("invalid node agent remote control-plane was ignored",{issues:r.error.issues}),[])}):[]}}class Bn{paths;logger;constructor(e,t={}){this.paths=e,this.logger=t.logger||Ln}read(){let e,n;try{e=t.readFileSync(this.paths.identityPath,"utf8")}catch(e){if("ENOENT"===e.code)return;throw Object.assign(new Error(`Node agent identity could not be read: ${this.paths.identityPath}`,{cause:e}),{code:"NODE_AGENT_IDENTITY_READ_FAILED"})}try{n=JSON.parse(e)}catch(e){throw Un(`Node agent identity contains invalid JSON: ${this.paths.identityPath}`,e)}return qn(n,this.logger)}write(e){t.mkdirSync(n.dirname(this.paths.identityPath),{recursive:!0});const r=qn({...e,updatedAt:Pn()},this.logger);m.sync(this.paths.identityPath,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384})}}function Vn(){return(new Date).toISOString()}class Gn{store;constructor(e){this.store=new Bn(e)}resolveNodeId(e){if(e?.trim())return this.writeNodeId(e.trim()),e.trim();const t=this.store.read();if(t?.nodeId)return t.nodeId;const n=Ht("node");return this.writeNodeId(n),n}createPairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=Vn(),r=new Date(Date.now()+(e.expiresInMs||6e5)).toISOString(),o=Kt(),i={tokenHash:dn(o),createdAt:n,expiresAt:r,...e.controlPlaneName?{controlPlaneName:e.controlPlaneName}:{},...e.controlPlaneUrl?{controlPlaneUrl:e.controlPlaneUrl}:{}};return this.store.write({...t,pairingInvites:[...t.pairingInvites||[],i]}),{...i,token:o}}completePairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=dn(e.joinToken),r=(t.pairingInvites||[]).find(e=>e.tokenHash===n);if(!r){const e=new Error("Node agent pairing invite is invalid or expired.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_PAIRING_INVITE_INVALID"}),e}const o=Vn(),i={id:e.controlPlaneId||Ht("cp"),keyId:Ht("key"),...e.controlPlaneName||r.controlPlaneName?{name:e.controlPlaneName||r.controlPlaneName}:{},...e.controlPlaneUrl||r.controlPlaneUrl?{url:e.controlPlaneUrl||r.controlPlaneUrl}:{},secret:Kt(),pairedAt:o,updatedAt:o,active:!0};return this.store.write({...t,pairingInvites:(t.pairingInvites||[]).filter(e=>e.tokenHash!==n),remoteControlPlanes:[...(t.remoteControlPlanes||[]).filter(e=>e.id!==i.id),i]}),i}upsertRemoteControlPlane(e){const t=this.store.read()||this.newIdentity(),n=e.url?.replace(/\/$/,"");return this.store.write({...t,remoteControlPlanes:[...(t.remoteControlPlanes||[]).filter(t=>t.id!==e.id&&(!n||t.url?.replace(/\/$/,"")!==n)),e]}),e}createRemoteControlPlane(e){const t=Vn();return{id:Ht("cp"),keyId:Ht("key"),...e.name?{name:e.name}:{},url:e.url,secret:Kt(),pairedAt:t,updatedAt:t,active:!1!==e.active}}configuredRemoteControlPlanes(){return this.store.read()?.remoteControlPlanes||[]}listRemoteControlPlanes(e=""){return(this.store.read()?.remoteControlPlanes||[]).map(({secret:t,...n})=>({...n,current:Boolean(e&&n.keyId===e)})).sort((e,t)=>Number(t.current)-Number(e.current)||t.updatedAt.localeCompare(e.updatedAt))}deleteRemoteControlPlane(e,t=""){if(t&&e===t){const e=new Error("Cannot delete the key currently used by this control-plane connection.");throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_REMOTE_KEY_IN_USE"}),e}const n=this.store.read();if(!n)return!1;const r=(n.remoteControlPlanes||[]).filter(t=>t.keyId!==e);return r.length!==(n.remoteControlPlanes||[]).length&&(this.store.write({...n,remoteControlPlanes:r}),!0)}remoteSecrets(e,t){return[...(this.store.read()?.remoteControlPlanes||[]).map(e=>({keyId:e.keyId,secret:e.secret})),...e&&t?[{keyId:t,secret:e}]:[]]}reverseTunnelSecret(e,t,n){if(t&&n)return{keyId:n,secret:t};const r=this.store.read()?.remoteControlPlanes||[];if(!r.length)return;if(e){const t=e.replace(/\/$/,""),n=r.find(e=>e.url?.replace(/\/$/,"")===t);if(n)return{keyId:n.keyId,secret:n.secret}}const o=[...r].sort((e,t)=>t.updatedAt.localeCompare(e.updatedAt))[0];return o?{keyId:o.keyId,secret:o.secret}:void 0}newIdentity(){return{nodeId:this.resolveNodeId(),createdAt:Vn(),updatedAt:Vn(),pairingInvites:[],remoteControlPlanes:[]}}writeNodeId(e){const t=this.store.read();this.store.write({...t||{createdAt:Vn()},nodeId:e,updatedAt:Vn(),pairingInvites:t?.pairingInvites||[],remoteControlPlanes:t?.remoteControlPlanes||[]})}prunePairingInvites(e){const t=Date.now();return{...e,pairingInvites:(e.pairingInvites||[]).filter(e=>Date.parse(e.expiresAt)>t)}}}const Jn=new Set(["content-encoding","content-length","transfer-encoding"]),Wn=["codex","claude"],Yn=5e3,Xn=l.z.object({version:l.z.literal(1),externalListener:Le}).strict(),Qn=l.z.object({}).strict().default({}),Zn=he.strict();function er(e){const t=process.env[e]?.trim();return t||void 0}function tr(){try{const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)):__dirname,r=[n.resolve(e,"..","package.json"),n.resolve(e,"..","..","package.json")].find(e=>t.existsSync(e));return r&&JSON.parse(t.readFileSync(r,"utf8")).version||"unknown"}catch{return"unknown"}}function nr(e){return e.bodyBase64?Buffer.from(e.bodyBase64,"base64"):e.body}function rr(e){const t=e.authorization;return"string"==typeof t&&t.startsWith("Bearer ")?t.slice(7).trim():void 0}function or(e){if("string"!=typeof e)return!1;const t=e.trim().toLowerCase();return"localhost"===t||"127.0.0.1"===t||"::1"===t||"::ffff:127.0.0.1"===t}function ir(e){return!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}function sr(){return(new Date).toISOString()}function ar(e){return"all-ipv4"===e?"0.0.0.0":"127.0.0.1"}function cr(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=t.externalListener&&"object"==typeof t.externalListener&&!Array.isArray(t.externalListener)?t.externalListener:{},r=Object.keys(t).filter(e=>"version"!==e&&"externalListener"!==e),o=Object.keys(n).filter(e=>"bindScope"!==e&&"port"!==e);return(r.length||o.length)&&console.warn(JSON.stringify({message:"unknown stored node agent runtime setting fields were ignored",filePath:"runtime-settings.json",fields:[...r,...o.map(e=>`externalListener.${e}`)]})),{version:t.version,externalListener:{bindScope:n.bindScope,port:n.port}}}function dr(e){return"local-folder"===e.type?ze.parse({mode:"local-bind",path:"/workspace",readOnly:!1}):ze.parse({mode:"git-clone",path:"/workspace",readOnly:!1})}function lr(e){const t=Ae.parse(e.source),n=e.projectId||("local-folder"===t.type?t.localFolderId:void 0)||("git-repository"===t.type?t.repositoryId:void 0)||("git-template"===t.type?t.templateId:void 0)||`project_${e.id}`;return Se.parse({id:n,name:"string"==typeof e.sourceSnapshot.name?e.sourceSnapshot.name:e.name,source:t,defaultImageId:e.imageId,defaultNodeId:e.nodeId,defaultRuntimeId:e.runtimeId,workspacePolicy:dr(t),labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt})}async function ur(e,t){if(!t.target.web)return"unknown";const n=new AbortController,r=setTimeout(()=>n.abort(),2e3);try{return(await e(`${hr(t)}/api/health`,{signal:n.signal})).ok?"reachable":"endpoint-unreachable"}catch{return"endpoint-unreachable"}finally{clearTimeout(r)}}async function pr(e,t,n,r){const o=new AbortController,i=setTimeout(()=>o.abort(),r);try{return await e(t,{...n,signal:o.signal})}finally{clearTimeout(i)}}async function mr(e,t,n,r){if(!t.config.autoImportAgentConfigs)return void r.diagnostic({instanceId:t.id,action:n},"node instance config auto-import skipped");if("reachable"!==t.targetStatus)return;const o=hr(t),i=Number(process.env.TASK_HANDOFF_CONFIG_AUTO_IMPORT_TIMEOUT_MS||Yn);for(const s of Wn)try{const a=await pr(e,`${o}/api/config-sync/import/${encodeURIComponent(s)}`,{method:"POST"},Number.isFinite(i)&&i>0?i:Yn);if(!a.ok){r.warn({instanceId:t.id,action:n,preset:s,statusCode:a.status},"node instance config auto-import failed");continue}r.diagnostic({instanceId:t.id,action:n,preset:s},"node instance config auto-import completed")}catch(e){r.warn({instanceId:t.id,action:n,preset:s,error:e instanceof Error?e.message:String(e)},"node instance config auto-import failed")}}async function gr(e,t,n){const r=t.requireInstance(n);if("reachable"!==r.targetStatus||!r.target.web)return!1;const o=await pr(e,`${hr(r)}/api/internal/model-environment`,{method:"PUT",headers:{"content-type":"application/json",authorization:`Bearer ${r.registrationToken}`},body:JSON.stringify(t.resolvedAssignedModelEnvironment(n))},Yn);if(!o.ok)throw Object.assign(new Error(`Instance ${n} rejected its managed model environment with HTTP ${o.status}.`),{statusCode:502,code:"INSTANCE_MODEL_ENVIRONMENT_APPLY_FAILED"});return!0}function hr(e){const t=e.target.web;if(!t){const t=new Error(`Instance ${e.id} does not have a web endpoint.`);throw Object.assign(t,{statusCode:409,code:"NODE_INSTANCE_WEB_ENDPOINT_MISSING"}),t}if("direct-port"!==e.target.strategy)return t.replace(/\/$/,"");try{const e=new URL(t);return e.port?(e.hostname=process.env.TASK_HANDOFF_NODE_AGENT_PROXY_HOST||"127.0.0.1",e.toString().replace(/\/$/,"")):t.replace(/\/$/,"")}catch{return t.replace(/\/$/,"")}}function fr(e,t){const n=new Error(t);throw Object.assign(n,{statusCode:403,code:e}),n}function yr(e,t){if(e===Z)return;const n=new Error(`${t} protocol version ${e||"missing"} is not compatible with ${Z}.`);throw Object.assign(n,{statusCode:409,code:"PROTOCOL_VERSION_MISMATCH"}),n}function Ir(e={}){return{requiresImage:!1,supportsControlledInstanceApi:!0,supportsContainerLifecycle:!1,supportsAppSessions:!0,supportsHostSessions:!0,artifactKind:"none",isolation:"none",...e}}function br(){const e=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND?.trim();if(e)return e.split(/\s+/);const r=n.resolve(process.cwd(),"bin","task-handoff.js");return t.existsSync(r)?[process.execPath,r,"web"]:["task-handoff-controlled-instance","web"]}function Ar(e){return new Promise(t=>{const n=o.createServer();n.once("error",()=>t(!1)),n.listen({host:"127.0.0.1",port:e},()=>{n.close(()=>t(!0))})})}function zr(e,t=3e3){return null!==e.exitCode||null!==e.signalCode?Promise.resolve():new Promise(n=>{const r=setTimeout(()=>{null===e.exitCode&&null===e.signalCode&&e.kill("SIGKILL"),n()},t);r.unref(),e.once("exit",()=>{clearTimeout(r),n()})})}async function wr(e,t){const n=t.get(e.id);if(n&&!n.killed)return n.kill("SIGTERM"),t.delete(e.id),void await zr(n);if(e.runtime.pid){try{process.kill(e.runtime.pid,"SIGTERM")}catch{return}const t=Date.now()+3e3;for(;Date.now()<t;){try{process.kill(e.runtime.pid,0)}catch{return}await new Promise(e=>setTimeout(e,50))}try{process.kill(e.runtime.pid,"SIGKILL")}catch{}}}const Sr=new Set(["provisioning","starting","registering","registered","running"]);async function kr(e,t){try{const n=await e(t,["--version"]);return{available:!0,command:t,version:(n.stdout||n.stderr).split(/\r?\n/)[0]?.trim()||void 0}}catch{return{available:!1}}}class Nr{executor;constructor(e){this.executor=e}start(e){return this.executor.start(e)}stop(e){return this.executor.stop(e)}restart(e){return this.executor.restart(e)}delete(e){return this.executor.delete(e)}}class Er{runCommand;paths;nodeAgentUrl;processByInstanceId=new Map;constructor(e,t,n){this.runCommand=e,this.paths=t,this.nodeAgentUrl=n}async start(e){const r=function(e){if("local-folder"!==e.source.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}return n.resolve(e.source.path)}(e.instance),o=e.instance.runtime.port||await async function(){const e=Number(process.env.TASK_HANDOFF_LOCAL_INSTANCE_PORT_START||19e3),t=Number.isInteger(e)&&e>0?e:19e3;for(let e=t;e<t+1e3&&e<=65535;e+=1)if(await Ar(e))return e;const n=new Error(`No free localhost port found in range ${t}-${Math.min(t+999,65535)}.`);throw Object.assign(n,{statusCode:503,code:"LOCAL_INSTANCE_PORT_UNAVAILABLE"}),n}(),i=n.join(this.paths.dataDir,"local-instances",e.instance.id),a=n.join(i,"logs");t.mkdirSync(a,{recursive:!0});const[c,...d]=br(),l=[...d,"--host","127.0.0.1","--port",String(o)],u=t.openSync(n.join(a,"controlled-instance.out.log"),"a"),p=t.openSync(n.join(a,"controlled-instance.err.log"),"a"),m=s.spawn(c,l,{cwd:r,detached:!1,stdio:["ignore",u,p],env:{...process.env,TASK_HANDOFF_CONTROL_MODE:"controlled",TASK_HANDOFF_NODE_AGENT_URL:this.nodeAgentUrl(),TASK_HANDOFF_INSTANCE_ID:e.instance.id,TASK_HANDOFF_INSTANCE_NAME:e.instance.name,TASK_HANDOFF_REGISTRATION_TOKEN:e.instance.registrationToken||"",TASK_HANDOFF_PROJECT_ID:e.project.id,TASK_HANDOFF_NODE_ID:e.node.id,TASK_HANDOFF_RUNTIME_ID:e.runtime.id,TASK_HANDOFF_WORKSPACE:r,TASK_HANDOFF_WORKSPACE_MODE:"local-bind",TASK_HANDOFF_DATA_DIR:i,TASK_HANDOFF_LOG_DIR:a,TASK_HANDOFF_APP_SESSION_PERSIST:"1",TASK_HANDOFF_CODEX_APP_SERVER:process.env.TASK_HANDOFF_CODEX_APP_SERVER||"1",TASK_HANDOFF_WEB_PORT:String(o),TASK_HANDOFF_WEB_HOST:"127.0.0.1",...e.modelEnv||{}}});t.closeSync(u),t.closeSync(p);try{await function(e){return new Promise((t,n)=>{const r=()=>{e.off("error",o),t()},o=t=>{e.off("spawn",r),n(t)};e.once("spawn",r),e.once("error",o)})}(m)}catch(e){const o=e instanceof Error?e.message:String(e);try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] spawn failed command=${c} cwd=${r} error=${o}\n`)}catch{}throw Object.assign(new Error(`Controlled instance process could not start with command ${c} in ${r}: ${o}`),{statusCode:500,code:"LOCAL_INSTANCE_PROCESS_SPAWN_FAILED"})}this.processByInstanceId.set(e.instance.id,m),m.on("error",e=>{try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] process error pid=${m.pid??""} error=${e.message}\n`)}catch{}}),m.once("exit",(r,o)=>{this.processByInstanceId.delete(e.instance.id);try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] exited pid=${m.pid??""} code=${r??""} signal=${o??""}\n`)}catch{}});const g=`http://127.0.0.1:${o}`;return await async function(e,t=3e3){const n=Date.now()+t;for(;Date.now()<n;){try{if((await fetch(`${e}/api/health`)).ok)return!0}catch{}await new Promise(e=>setTimeout(e,100))}return!1}(g),{status:"registering",health:"unknown",connectionStatus:"online",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:"unknown",web:g,api:`${g}/api`},workspace:{mode:"local-bind",status:"pending",path:r},runtime:{kind:"local",workspacePath:r,pid:m.pid,port:o,labels:{...e.instance.runtime.labels,"task-handoff.runtime-kind":"local"}}}}async stop(e){return await wr(e.instance,this.processByInstanceId),{status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async restart(e){return await wr(e.instance,this.processByInstanceId),this.start(e)}async delete(e){return this.stop(e)}async check(e){const[t,n]=await Promise.all([kr(this.runCommand,"codex"),kr(this.runCommand,"claude")]);return{status:"online",capabilities:Ir({...e.capabilities,apps:{terminal:!0,codex:t,claude:n}})}}async stopAll(){const e=Array.from(this.processByInstanceId.values());for(const e of this.processByInstanceId.values())e.killed||e.kill("SIGTERM");this.processByInstanceId.clear(),await Promise.all(e.map(e=>zr(e)))}}class _r{docker;local;constructor(e,t){this.docker=e,this.local=t}forRuntime(e){if("docker"===e.type)return this.docker;if("local"===e.type)return this.local;const t=new Error(`Runtime type ${e.type} is not supported by this node agent.`);throw Object.assign(t,{statusCode:400,code:"NODE_RUNTIME_TYPE_UNSUPPORTED"}),t}async stopAll(){this.local instanceof Er&&await this.local.stopAll()}}class vr{nodeId;paths;localFolders;nodeRuntimes;controlledInstances;models;modelAssignments;modelEnvironments;updateJobs;node;listenerPort;containerUrlOverride;constructor(e,t,n,r,o){this.paths=e,this.nodeId=t,this.localFolders=new qt(e.localFoldersDir,{schema:be}),this.nodeRuntimes=new qt(e.nodeRuntimesDir,{schema:$e}),this.controlledInstances=new qt(e.controlledInstancesDir,{schema:Ge,sanitize:e=>We(e,e=>{console.warn(JSON.stringify({message:"legacy controlled instance field was ignored",...e}))})}),this.models=new kn(e.nodeModelsDir,t),this.modelAssignments=new Nn(e.modelAssignmentsDir),this.modelEnvironments=new Sn(e.modelEnvironmentsDir),this.updateJobs=new An(e),this.listenerPort=o,this.containerUrlOverride=r;const i=sr();this.node=Fe.parse({id:t,name:t,connectionMode:"direct-http",endpoint:n,controlEndpoint:n,containerEndpoint:this.containerUrl,publicWebBase:n?n.replace(/:\d+$/,""):void 0,status:"online",health:"ok",capabilities:{},labels:{},createdAt:i,updatedAt:i})}init(){if(this.localFolders.init(),this.nodeRuntimes.init(),this.controlledInstances.init(),this.models.init(),this.modelAssignments.init(),this.migrateLegacyModelEnvironments(),this.updateJobs.init(),!this.nodeRuntimes.get("runtime_local_docker")){const e=sr();this.nodeRuntimes.put($e.parse({id:"runtime_local_docker",nodeId:this.nodeId,name:"Local Docker",type:"docker",status:"unknown",accessStrategy:"direct-port",capabilities:{},labels:{},createdAt:e,updatedAt:e}))}for(const e of this.nodeRuntimes.list())e.nodeId!==this.nodeId&&this.nodeRuntimes.put($e.parse({...e,nodeId:this.nodeId,updatedAt:sr()}));for(const e of this.localFolders.list())e.nodeId!==this.nodeId&&this.localFolders.put(be.parse({...e,nodeId:this.nodeId,updatedAt:sr()}))}get localNodeAgentUrl(){return`http://127.0.0.1:${this.listenerPort}`}get currentListenerPort(){return this.listenerPort}get containerUrl(){return this.containerUrlOverride||`http://host.docker.internal:${this.listenerPort}`}setListenerPort(e){this.listenerPort=e,this.node.containerEndpoint=this.containerUrl,this.node.updatedAt=sr()}runningInstanceCount(){const e=new Set(["created","stopped","failed"]);return this.listInstances().filter(t=>!e.has(t.status)).length}createLocalFolder(e){const t=sr(),n=be.parse({...e,id:e.id||Ht("folder"),nodeId:this.nodeId,labels:e.labels||{},createdAt:t,updatedAt:t});return this.localFolders.put(n)}createRuntime(e){const t=sr(),n=$e.parse({...e,id:e.id||Ht("runtime"),nodeId:this.nodeId,accessStrategy:e.accessStrategy||(r=e.type,"docker"===r?"direct-port":"node-proxy"),capabilities:"local"===e.type?Ir(e.capabilities):e.capabilities||{},labels:e.labels||{},createdAt:t,updatedAt:t});var r;return this.nodeRuntimes.put(n)}updateRuntime(e,t){const n=this.requireRuntime(e),r=$e.parse({...n,...t,id:n.id,nodeId:this.nodeId,accessStrategy:t.accessStrategy||n.accessStrategy,capabilities:"local"===(t.type||n.type)?Ir(t.capabilities||n.capabilities):t.capabilities||n.capabilities,createdAt:n.createdAt,updatedAt:sr()});return this.nodeRuntimes.put(r)}deleteRuntime(e){this.requireRuntime(e);const t=this.listInstances().filter(t=>t.runtimeId===e);if(t.length){const n=new Error(`Runtime ${e} is used by ${t.length} instance${1===t.length?"":"s"}.`);throw Object.assign(n,{statusCode:409,code:"NODE_RUNTIME_IN_USE"}),n}return this.nodeRuntimes.delete(e)}checkRuntime(e,t){const n=this.requireRuntime(e);return t.check?t.check(n).then(e=>{const t=$e.parse({...n,...e,id:n.id,nodeId:this.nodeId,createdAt:n.createdAt,updatedAt:sr()});return this.nodeRuntimes.put(t)}):this.nodeRuntimes.put($e.parse({...n,status:"unknown",updatedAt:sr()}))}requireRuntime(e){const t=this.nodeRuntimes.get(e);if(!t){const t=new Error(`Node runtime ${e} was not found.`);throw Object.assign(t,{statusCode:404,code:"NODE_RUNTIME_NOT_FOUND"}),t}return t}requireInstance(e){const t=this.controlledInstances.get(e);if(!t){const t=new Error(`Instance ${e} was not found on node ${this.nodeId}.`);throw Object.assign(t,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),t}const n=Je(t);if(!n.success)throw function(e,t){const n=t.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; "),r=new Error(`Stored instance ${e} is not compatible with protocol ${Z}: ${n}`);return Object.assign(r,{statusCode:409,code:"NODE_INSTANCE_PAYLOAD_INVALID"}),r}(e,n.error.issues);return n.data}listInstances(){return this.controlledInstances.list().flatMap(e=>{const t=Je(e);return t.success?[t.data]:[]})}listModels(){const e=new Map;for(const t of this.listInstances()){const n=this.modelAssignments.get(t.id);for(const t of[n?.codexModelHash,n?.claudeModelHash])t&&e.set(t,(e.get(t)||0)+1)}return this.models.list().map(t=>this.publicModel(t,e.get(t.id)||0))}createModel(e){const t=sr(),n=Pe(e),r=this.models.get(n),o=Ne.parse({...e,id:n,enabled:e.enabled??!0,order:e.order??this.nextModelOrder(),labels:e.labels||{},createdAt:r?.createdAt||t,updatedAt:t});return this.publicModel(this.models.put(o),this.modelReferenceIds(n).length)}deployModel(e){const t=Pe(e);if(e.id!==t)throw Object.assign(new Error(`Model content hash ${t} does not match ${e.id}.`),{statusCode:400,code:"NODE_MODEL_HASH_MISMATCH"});const n=this.models.get(e.id)||this.models.put(Ne.parse(e));return this.publicModel(n,this.modelReferenceIds(n.id).length)}updateModel(e,t){const n=this.requireModel(e),r=Ne.parse({...n,...t,key:t.key?.trim()?t.key:n.key,createdAt:n.createdAt,updatedAt:sr()}),o=Pe(r),i=this.models.put(Ne.parse({...r,id:o}));return this.publicModel(i,this.modelReferenceIds(o).length)}deleteModel(e){this.requireModel(e);const t=this.modelReferenceIds(e);if(t.length)throw Object.assign(new Error(`Model ${e} is assigned to ${t.length} instance${1===t.length?"":"s"}.`),{statusCode:409,code:"NODE_MODEL_IN_USE",instanceIds:t});return this.models.delete(e)}assignModels(e,t){const n=this.requireInstance(e);if(this.validateAssignmentRef("codex",t.codexModelHash),this.validateAssignmentRef("claude",t.claudeModelHash),t.modelSelection.codexModelHash&&t.modelSelection.codexModelHash!==t.codexModelHash)throw Object.assign(new Error("Codex model selection does not match its node assignment."),{statusCode:400,code:"NODE_MODEL_SELECTION_MISMATCH"});if(t.modelSelection.claudeModelHash&&t.modelSelection.claudeModelHash!==t.claudeModelHash)throw Object.assign(new Error("Claude model selection does not match its node assignment."),{statusCode:400,code:"NODE_MODEL_SELECTION_MISMATCH"});const r=this.modelAssignments.get(e),o=ve.parse({instanceId:e,codexModelHash:t.codexModelHash,claudeModelHash:t.claudeModelHash,updatedAt:sr()});this.modelAssignments.put(o);try{return{assignment:o,instance:this.controlledInstances.put(Ge.parse({...n,modelSelection:t.modelSelection,updatedAt:sr()}))}}catch(t){throw r?this.modelAssignments.put(r):this.modelAssignments.delete(e),t}}resolvedAssignedModelEnvironment(e){const t=this.modelAssignments.get(e);if(!t){if(this.modelEnvironments.has(e))throw Object.assign(new Error(`Legacy model environment for instance ${e} requires manual migration.`),{statusCode:409,code:"NODE_MODEL_MIGRATION_REQUIRED"});return{}}return{...this.modelEnvironmentForRef("codex",t.codexModelHash),...this.modelEnvironmentForRef("claude",t.claudeModelHash)}}validateAssignmentRef(e,t){if(!t)return;const n=this.requireModel(t);if(n.app!==e)throw Object.assign(new Error(`Model ${n.id} belongs to ${n.app}, not ${e}.`),{statusCode:400,code:"NODE_MODEL_APP_MISMATCH"});if(!n.enabled)throw Object.assign(new Error(`Model ${n.id} is disabled.`),{statusCode:409,code:"NODE_MODEL_DISABLED"})}modelEnvironmentForRef(e,t){if(!t)return{};this.validateAssignmentRef(e,t);const n=this.requireModel(t);return"codex"===e?{OPENAI_API_KEY:n.key,OPENAI_BASE_URL:n.endpoint,TASK_HANDOFF_CODEX_BASE_URL:n.endpoint,TASK_HANDOFF_CODEX_MODEL:n.model}:{ANTHROPIC_API_KEY:n.key,ANTHROPIC_BASE_URL:n.endpoint,TASK_HANDOFF_CLAUDE_MODEL:n.model}}requireModel(e){const t=this.models.get(e);if(!t)throw Object.assign(new Error(`Model ${e} was not found on node ${this.nodeId}.`),{statusCode:404,code:"NODE_MODEL_NOT_FOUND"});if(Pe(t)!==t.id)throw Object.assign(new Error(`Stored model ${e} does not match its content hash.`),{statusCode:409,code:"NODE_MODEL_HASH_INVALID"});return t}modelReferenceIds(e){return this.listInstances().filter(t=>{const n=this.modelAssignments.get(t.id);return n?.codexModelHash===e||n?.claudeModelHash===e}).map(e=>e.id)}publicModel(e,t){const{key:n,...r}=e;return _e.parse({...r,keyPreview:n.length<=8?"set":`${n.slice(0,4)}...${n.slice(-4)}`,keySet:!0,referenceCount:t})}nextModelOrder(){return this.models.list().reduce((e,t)=>Math.max(e,t.order),0)+100}migrateLegacyModelEnvironments(){for(const e of this.modelEnvironments.listInstanceIds()){if(!this.controlledInstances.get(e)){this.warnLegacyModelMigration(e,"instance-not-found");continue}if(this.modelAssignments.get(e)){try{this.resolvedAssignedModelEnvironment(e),this.modelEnvironments.delete(e)}catch{this.warnLegacyModelMigration(e,"existing-assignment-invalid")}continue}let t;try{t=this.modelEnvironments.get(e)}catch{this.warnLegacyModelMigration(e,"sidecar-invalid");continue}if(!Object.keys(t).length){this.modelEnvironments.delete(e);continue}if(Object.keys(t).some(e=>!wn.has(e))){this.warnLegacyModelMigration(e,"unknown-fields");continue}const n=[];try{const r=this.migrateLegacyModelForApp(t,"codex",n),o=this.migrateLegacyModelForApp(t,"claude",n);if(!r&&!o)throw new Error("no complete model configuration");const i={...r?{codexModelHash:r}:{},...o?{claudeModelHash:o}:{}};this.assignModels(e,{modelSelection:i,codexModelHash:r,claudeModelHash:o}),this.resolvedAssignedModelEnvironment(e),this.modelEnvironments.delete(e)}catch{this.modelAssignments.delete(e);for(const e of n)this.models.delete(e);this.warnLegacyModelMigration(e,"mapping-failed")}}}migrateLegacyModelForApp(e,t,n){const r=e["codex"===t?"OPENAI_API_KEY":"ANTHROPIC_API_KEY"],o="codex"===t?e.TASK_HANDOFF_CODEX_BASE_URL||e.OPENAI_BASE_URL:e.ANTHROPIC_BASE_URL,i="codex"===t?e.TASK_HANDOFF_CODEX_MODEL||e.CODEX_MODEL:e.TASK_HANDOFF_CLAUDE_MODEL||e.CLAUDE_MODEL;if(!Boolean(r||o||i))return;if(!r||!o||!i)throw new Error("model configuration incomplete");const s=Pe({app:t,endpoint:o,key:r,model:i}),a=this.models.get(s);if(a){if(a.app!==t||a.key!==r||a.endpoint!==o||a.model!==i||!a.enabled)throw new Error("existing model conflicts with legacy environment");return s}const c=sr();return this.models.put(Ne.parse({id:s,name:`Migrated ${"codex"===t?"Codex":"Claude"} model`,endpoint:o,key:r,model:i,app:t,enabled:!0,order:this.nextModelOrder(),labels:{migratedFrom:"instance-model-environment"},createdAt:c,updatedAt:c})),n.push(s),s}warnLegacyModelMigration(e,t){console.warn(JSON.stringify({message:"legacy model environment was preserved because it could not be migrated",nodeId:this.nodeId,instanceId:e,reason:t}))}createInstance(e){const t=this.requireRuntime(e.runtimeId),r=sr(),o=e.id||Ht("inst"),i=Ae.parse(e.source);if(function(e){return"boolean"==typeof e.capabilities.requiresImage?e.capabilities.requiresImage:"local"!==e.type}(t)&&(!e.imageId||!e.image)){const e=new Error(`Runtime ${t.name} requires an image.`);throw Object.assign(e,{statusCode:400,code:"NODE_RUNTIME_IMAGE_REQUIRED"}),e}if("local"===t.type&&"local-folder"!==i.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}if("local"===t.type&&this.listInstances().some(e=>"local"===this.requireRuntime(e.runtimeId).type)){const e=new Error("A localhost instance already exists on this node.");throw Object.assign(e,{statusCode:409,code:"LOCAL_RUNTIME_INSTANCE_EXISTS"}),e}const s=e.image?Me.parse(e.image):void 0,a="local"===t.type&&"local-folder"===i.type?n.resolve(i.path):void 0,c=Ge.parse({id:o,name:e.name||`instance-${r.replace(/[-:.TZ]/g,"").slice(0,14)}-${o.replace(/^inst_?/,"").slice(0,4)}`,source:i,sourceSnapshot:e.sourceSnapshot||{},modelSelection:e.modelSelection,projectId:e.projectId,nodeId:this.nodeId,runtimeId:t.id,imageId:s?.id,imageSnapshot:s,status:"created",health:"unknown",connectionStatus:"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",controlMode:"controlled",capabilities:{},config:e.config,workspace:"local"===t.type?{mode:"local-bind",status:"unknown",path:a}:{status:"unknown"},target:{strategy:"node-proxy",status:"unknown"},runtime:"local"===t.type?{kind:"local",workspacePath:a,labels:{"task-handoff.runtime-kind":"local"}}:{labels:{}},registrationToken:Kt(),createdAt:r,updatedAt:r});return this.controlledInstances.put(c)}registerInstance(e,t,n){const r=Xe.parse(t),o=sr(),i=this.controlledInstances.get(e);if(!i){const t=new Error(`Instance ${e} was not found on node ${this.nodeId}.`);throw Object.assign(t,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),t}this.validateInstanceReport(i,r,n),yr(r.protocolVersion,`Instance ${e}`);const s=Ge.parse({...i,name:r.name,status:"registered",health:"ok",connectionStatus:"online",agentStatus:"online",targetStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":"reachable"===r.target.status?"reachable":i.targetStatus,uiAccessStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":i.uiAccessStatus,controlMode:r.controlMode,protocolVersion:r.protocolVersion,instanceVersion:r.instanceVersion,build:r.build,capabilities:r.capabilities,appInventory:r.appInventory,workspace:r.workspace,target:{...i.target,...r.target},registrationToken:i.registrationToken||r.registrationToken,lastHeartbeatAt:o,updatedAt:o});return this.controlledInstances.put(s)}heartbeatInstance(e,t,n){const r=this.requireInstance(e);this.validateInstanceToken(r,n);const o=Qe.parse(t);yr(o.protocolVersion,`Instance ${e}`);const i=sr(),s=o.target?{...r.target,...o.target}:r.target,a={...s,status:"unknown"===s.status&&(s.web||s.api)?"reachable":s.status},c="endpoint-unreachable"===a.status?"endpoint-unreachable":"reachable"===a.status?"reachable":r.targetStatus,d=Ge.parse({...r,...o,target:a,agentStatus:"online",targetStatus:c,uiAccessStatus:c,connectionStatus:"online",build:o.build||r.build,lastHeartbeatAt:i,updatedAt:i});return this.controlledInstances.put(d)}validateInstanceReport(e,t,n){this.validateInstanceToken(e,n||t.registrationToken),t.instanceId&&t.instanceId!==e.id&&fr("INSTANCE_ID_MISMATCH",`Instance ${t.instanceId} cannot register as ${e.id}.`),t.nodeId&&t.nodeId!==this.nodeId&&fr("INSTANCE_NODE_MISMATCH",`Instance ${e.id} belongs to node ${this.nodeId}.`),t.runtimeId&&t.runtimeId!==e.runtimeId&&fr("INSTANCE_RUNTIME_MISMATCH",`Instance ${e.id} belongs to runtime ${e.runtimeId}.`),t.imageId&&t.imageId!==e.imageId&&fr("INSTANCE_IMAGE_MISMATCH",`Instance ${e.id} belongs to image ${e.imageId}.`)}validateInstanceToken(e,t){e.registrationToken&&t===e.registrationToken||fr("INSTANCE_REGISTRATION_TOKEN_INVALID",`Invalid registration token for instance ${e.id}.`)}context(e,t=this.resolvedAssignedModelEnvironment(e.id)){const n=e.imageSnapshot||Me.parse({id:e.imageId||"img_localhost",name:e.imageId||"Localhost",image:e.imageId||"localhost",registry:"local",capabilities:[],optionalApps:[],defaultEnv:{},labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt});return{project:lr(e),image:n,node:this.node,runtime:this.requireRuntime(e.runtimeId),instance:e,nodeAgentUrl:this.containerUrl,modelEnv:t}}}class Or{app;state;settings;sockets=new Set;config;source;status="error";error;constructor(e){this.app=e.app,this.state=e.state,this.settings=e.settings,this.config=e.config,this.source=e.source,this.app.server.on("connection",e=>{this.sockets.add(e),e.once("close",()=>this.sockets.delete(e))})}current(){return Ue.parse({...this.config,host:ar(this.config.bindScope),status:this.status,source:this.source,...this.error?{error:this.error}:{}})}async start(){try{await this.listen(this.config),this.status="listening",this.error=void 0,this.state.setListenerPort(this.config.port)}catch(e){this.status="error",this.error=e instanceof Error?e.message:String(e),this.app.log.error({host:ar(this.config.bindScope),port:this.config.port,error:this.error},"node agent TCP listener failed to start; Unix IPC remains available")}return this.current()}async update(e){const t=He.parse(e);if(t.bindScope===this.config.bindScope&&t.port===this.config.port)return this.current();if(t.port!==this.config.port){const e=this.state.runningInstanceCount();if(e>0){const t=new Error(`Cannot change the node agent port while ${e} controlled instance(s) are running.`);throw Object.assign(t,{statusCode:409,code:"NODE_AGENT_LISTENER_PORT_IN_USE_BY_INSTANCES",blockingInstanceCount:e}),t}}const n={config:this.config,source:this.source,status:this.status,error:this.error};await this.stop();try{await this.listen(t)}catch(e){await this.restore(n);const r=new Error(`Failed to bind node agent TCP listener at ${ar(t.bindScope)}:${t.port}: ${e instanceof Error?e.message:String(e)}`);throw Object.assign(r,{statusCode:409,code:"NODE_AGENT_LISTENER_BIND_FAILED"}),r}try{this.settings.put({version:1,externalListener:t})}catch(e){await this.stop(),await this.restore(n);const t=new Error(`Failed to persist node agent TCP listener: ${e instanceof Error?e.message:String(e)}`);throw Object.assign(t,{statusCode:500,code:"NODE_AGENT_LISTENER_PERSIST_FAILED"}),t}return this.config=t,this.source="persisted",this.status="listening",this.error=void 0,this.state.setListenerPort(t.port),this.current()}async shutdown(){await this.stop()}async restore(e){if(this.config=e.config,this.source=e.source,this.status=e.status,this.error=e.error,"listening"===e.status)try{await this.listen(e.config)}catch(e){this.status="error",this.error=`Failed to restore previous listener: ${e instanceof Error?e.message:String(e)}`,this.app.log.error({error:this.error},"node agent TCP listener rollback failed")}this.state.setListenerPort(e.config.port)}async listen(e){await new Promise((t,n)=>{const r=e=>{this.app.server.off("listening",o),n(e)},o=()=>{this.app.server.off("error",r),t()};this.app.server.once("error",r),this.app.server.once("listening",o),this.app.server.listen({host:ar(e.bindScope),port:e.port})})}async stop(){this.app.server.listening&&await new Promise((e,t)=>{this.app.server.close(n=>n?t(n):e());for(const e of this.sockets)e.destroy()})}}async function xr(e,t,n,r,o,i="request"){const s=e.requireInstance(r);o.diagnostic({instanceId:r,action:"start",reason:i,runtimeId:s.runtimeId,imageId:s.imageId},"node instance start requested");const a=e.controlledInstances.put(Ge.parse({...s,status:"provisioning",updatedAt:sr()})),c=t.forRuntime(e.requireRuntime(a.runtimeId)),d=await c.start(e.context(a)),l=await ur(n,Ge.parse({...a,...d,target:d.target?{...a.target,...d.target}:a.target,workspace:d.workspace?{...a.workspace,error:void 0,...d.workspace}:a.workspace,runtime:d.runtime?{...a.runtime,...d.runtime}:a.runtime,updatedAt:sr()})),u=Ge.parse({...a,...d,target:d.target?{...a.target,...d.target,status:l}:a.target,workspace:d.workspace?{...a.workspace,error:void 0,...d.workspace}:a.workspace,runtime:d.runtime?{...a.runtime,...d.runtime}:a.runtime,targetStatus:l,uiAccessStatus:l,connectionStatus:d.connectionStatus,updatedAt:sr()}),p=e.controlledInstances.put(u);return await mr(n,p,"start",o),o.diagnostic({instanceId:r,action:"start",reason:i,status:p.status,connectionStatus:p.connectionStatus,targetStatus:p.targetStatus,targetWeb:p.target.web,containerName:p.runtime.containerName},"node instance start completed"),p}function Tr(e,t){const n=t instanceof Error?t.message:String(t);return Ge.parse({...e,status:"failed",health:"failed",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",workspace:{...e.workspace,error:n},updatedAt:sr()})}function Cr(e){return Ge.parse({...e,health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:sr()})}async function Dr(e={}){const r=e.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,o=Number(e.port||process.env.TASK_HANDOFF_NODE_AGENT_PORT||"8091"),s=`http://127.0.0.1:${o}`,u=e.containerUrl||process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,p=Pt(e.dataDir),m=e.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,g=e.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID,f=new Gn(p),y=f.resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID),I=e.connectionMode||("local-ipc"===process.env.TASK_HANDOFF_NODE_AGENT_CONNECTION_MODE?"local-ipc":"local-loopback"),b=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||an(p.dataDir),A="local-ipc"===I?`ipc://${encodeURIComponent(b)}`:s,z=new vr(p,y,s,u,o);z.node.connectionMode=I,z.node.controlEndpoint=A,z.node.endpoint=A,z.init();const w=new it(e.dockerCommandRunner,{publishHost:"127.0.0.1"}),S=new _r(new Nr(w),new Er(e.dockerCommandRunner||ot,p,()=>z.localNodeAgentUrl)),k=e.updateCommandRunner||e.dockerCommandRunner||ot,N=e.fetchImpl||fetch,E=a({logger:e.logger??!0});E.decorate("nodeAgentState",z),await E.register(c);const _=new St(z,r,{logger:E.log,safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0});_.start(),E.decorate("nodeAgentEventForwarder",_);const v=(j=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(j||"").trim().toLowerCase())),O=new En(f,y,m,g),x=(e,t)=>{v&&E.log.info(e,t)},T={diagnostic:x,warn:(e,t)=>E.log.warn(e,t)},C=async e=>{if("node-agent"===e.target.component)return async function(e){const t=await hn(e.runCommand,"@task-handoff/node-agent",e.channel);return t?me.parse({target:{component:"node-agent"},source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:t,updateAvailable:gn(e.currentVersion,t),checkedAt:mn()}):yn({target:{component:"node-agent"},channel:e.channel,currentVersion:e.currentVersion})}({channel:e.channel,currentVersion:tr(),runCommand:k});const t=z.requireInstance(e.target.instanceId);return async function(e){const t={component:"controlled-instance",instanceId:e.instance.id};if("local"===e.runtime.type){const n=await hn(e.runCommand,"@task-handoff/controlled-instance",e.channel),r=e.instance.build?.packageVersion||e.instance.instanceVersion;return n?me.parse({target:t,source:"npm",channel:e.channel,currentVersion:r,availableVersion:n,updateAvailable:gn(r,n),checkedAt:mn()}):yn({target:t,channel:e.channel,currentVersion:r})}if("docker"===e.runtime.type){const n=e.instance.imageSnapshot?.image;if(!n||"local"===e.instance.imageSnapshot?.registry)return me.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:e.instance.build?.imageDigest,availableVersion:e.instance.build?.imageDigest||"unavailable",updateAvailable:!1,supported:!1,reason:"The instance does not use a remote Docker image.",checkedAt:mn()});const r=function(e,t){const n=e.split("@",1)[0],r=n.lastIndexOf("/"),o=n.lastIndexOf(":");return`${o>r?n.slice(0,o):n}:${In(t)}`}(n,e.channel),o=await e.runCommand("docker",["manifest","inspect","--verbose",r]),i=bn(JSON.parse(o.stdout));if(!i)throw new Error(`Docker registry did not return a digest for ${r}.`);const s=e.instance.build?.imageDigest;return me.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:s,availableVersion:i,artifactRef:r,updateAvailable:s!==i,checkedAt:mn()})}return me.parse({target:t,source:"npm",channel:e.channel,availableVersion:"unsupported",updateAvailable:!1,supported:!1,reason:`Runtime type ${e.runtime.type} does not support managed updates.`,checkedAt:mn()})}({channel:e.channel,instance:t,runtime:z.requireRuntime(t.runtimeId),runCommand:k})},D=async e=>{if("controlled-instance"!==e.target.component)return;const r=z.requireInstance(e.target.instanceId),o=z.requireRuntime(r.runtimeId),i=S.forRuntime(o);if("local"===o.type){const r=fn(),o=(await k(r,["prefix","--global"])).stdout.trim(),[i]=br(),s=function(e){const r=function(e,r){if(!n.isAbsolute(e)||!t.existsSync(e))return;const o=t.realpathSync(e);let i=t.statSync(o).isDirectory()?o:n.dirname(o);for(;i!==n.dirname(i);){try{if(JSON.parse(t.readFileSync(n.join(i,"package.json"),"utf8")).name===r)return i}catch{}i=n.dirname(i)}}(e.executable,e.packageName),o=r&&function(e){let r=n.dirname(e);for(;r!==n.dirname(r);){try{if("@task-handoff/server"===JSON.parse(t.readFileSync(n.join(r,"package.json"),"utf8")).name)return r}catch{}r=n.dirname(r)}}(r);return o?{args:["install","--prefix",o,"--no-save",`${e.packageName}@${e.targetVersion}`],manifestPath:n.join(r,"package.json")}:{args:["install","--global","--prefix",e.globalPrefix,`${e.packageName}@${e.targetVersion}`],manifestPath:r?n.join(r,"package.json"):n.join(e.globalPrefix,"lib","node_modules",...e.packageName.split("/"),"package.json")}}({executable:i,globalPrefix:o,packageName:"@task-handoff/controlled-instance",targetVersion:e.toVersion});await k(r,s.args),function(e,n){let r;try{r=JSON.parse(t.readFileSync(e,"utf8")).version}catch(t){throw new Error(`Could not verify the updated package at ${e}: ${t instanceof Error?t.message:String(t)}`)}if(r!==n)throw new Error(`Updated package verification failed: expected ${n}, found ${String(r||"unknown")}.`)}(s.manifestPath,e.toVersion)}else{if("docker"!==o.type)throw Object.assign(new Error(`Runtime type ${o.type} does not support managed updates.`),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});{const t=e.artifactRef;if(!t||"local"===r.imageSnapshot?.registry)throw Object.assign(new Error("The instance does not use an updateable remote Docker image."),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});await k("docker",["pull",t])}}await i.delete(z.context(r,{})),z.controlledInstances.put(function(e){return Ge.parse({...e,status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:sr()})}(Ge.parse({...r,..."docker"===o.type&&e.artifactRef&&r.imageSnapshot?{imageSnapshot:{...r.imageSnapshot,image:e.artifactRef,updatedAt:sr()}}:{}}))),await xr(z,S,N,r.id,T,"update")};var j;E.setErrorHandler((e,t,n)=>{const r=function(e){if(e instanceof l.z.ZodError)return{statusCode:400,code:"VALIDATION_ERROR",message:e.issues.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; ")};const t=e&&"object"==typeof e?e:{};return{statusCode:"number"==typeof t.statusCode?t.statusCode:500,code:"string"==typeof t.code?t.code:"NODE_AGENT_ERROR",message:e instanceof Error?e.message:String(e)}}(e);(v||r.statusCode>=500)&&(r.statusCode>=500?E.log.error.bind(E.log):E.log.warn.bind(E.log))({method:t.method,url:t.url,statusCode:r.statusCode,errorCode:r.code,error:r.message},"node agent request failed"),n.code(r.statusCode).send({error:{code:r.code,message:r.message,...e&&"object"==typeof e&&"number"==typeof e.blockingInstanceCount?{blockingInstanceCount:e.blockingInstanceCount}:{}}})}),E.addHook("preHandler",async e=>{const t=O.verify(e);if(t)e.nodeAgentAuthKeyId=t;else if(ir(e)){if(r&&rr(e.headers)!==r){const e=new Error("Invalid node agent token.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),e}}else if("/api/node-agent/pairing/complete"!==e.url.split("?")[0]&&!function(e){const t=e.split("?")[0];return/^\/api\/node-agent\/instances\/[^/]+\/(register|heartbeat)$/.test(t)}(e.url)){if(O.hasRemoteSecrets()){const e=new Error("Node agent HMAC signature is required.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_REQUIRED"}),e}if(!function(e){return or(e.ip)||or(e.socket.remoteAddress)||!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}(e)){const e=new Error("Local node agent access is only accepted from loopback or local IPC connections.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_LOCAL_TOKEN_REQUIRES_LOOPBACK"}),e}if(r&&rr(e.headers)!==r){const e=new Error("Invalid node agent token.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),e}}}),E.addHook("onClose",async()=>{_.stop(),await async function(e,t){await t.stopAll();for(const t of e.listInstances())"local"===e.requireRuntime(t.runtimeId).type&&e.controlledInstances.put(Cr(t))}(z,S)}),E.decorate("nodeAgentRestoreLocalInstances",async()=>{const e=z.listInstances().filter(e=>"local"===z.requireRuntime(e.runtimeId).type&&Sr.has(e.status));for(const t of e)try{await xr(z,S,N,t.id,T,"restore")}catch(e){z.controlledInstances.put(Tr(z.requireInstance(t.id),e)),x({instanceId:t.id,action:"restore",error:e instanceof Error?e.message:String(e)},"node local instance restore failed")}}),E.get("/api/node-agent/health",async()=>{return{data:{ok:!0,role:"node-agent",nodeId:y,protocolVersion:Z,build:(e="node-agent",{component:e,packageName:"@task-handoff/node-agent",packageVersion:tr(),protocolVersion:Z,buildId:er("TASK_HANDOFF_BUILD_ID"),builtAt:er("TASK_HANDOFF_BUILT_AT"),gitCommit:er("TASK_HANDOFF_GIT_COMMIT"),imageRef:er("TASK_HANDOFF_IMAGE_REF"),imageDigest:er("TASK_HANDOFF_IMAGE_DIGEST")}),serverTime:(new Date).toISOString()}};var e});const R=e=>{if(!ir(e)){const e=new Error("Node agent TCP listener settings are available only over local Unix IPC.");throw Object.assign(e,{statusCode:403,code:"NODE_AGENT_LISTENER_LOCAL_IPC_ONLY"}),e}if(!E.nodeAgentListenerManager){const e=new Error("Node agent TCP listener manager is not initialized.");throw Object.assign(e,{statusCode:503,code:"NODE_AGENT_LISTENER_UNAVAILABLE"}),e}return E.nodeAgentListenerManager};return E.get("/api/node-agent/settings/external-listener",async e=>({data:R(e).current()})),E.patch("/api/node-agent/settings/external-listener",async e=>({data:await R(e).update(e.body)})),E.get("/api/node-agent/updates/jobs",async()=>({data:z.updateJobs.list()})),E.post("/api/node-agent/updates/check",async e=>({data:await C(pe.parse(e.body))})),E.post("/api/node-agent/updates/apply",async(e,r)=>{const o=Zn.parse(e.body),s=await C(o);if(!s.supported){const e=new Error(s.reason||"The requested update is not supported.");throw Object.assign(e,{statusCode:400,code:"UPDATE_UNSUPPORTED"}),e}if(!s.updateAvailable){const e=new Error(s.reason||"No update is available for the selected channel.");throw Object.assign(e,{statusCode:409,code:"UPDATE_NOT_AVAILABLE"}),e}const a=z.updateJobs.create(y,s);if("node-agent"===o.target.component){const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)):__dirname,{worker:r,packaged:o,expectedWorker:s}=function(e,r=t.existsSync){const o=[n.resolve(e,"..","bin","task-handoff-node-update-worker"),n.resolve(e,"..","..","bin","task-handoff-node-update-worker")],i=o.find(r),s=n.resolve(e,"..","..","..","..","scripts","node-update-worker.cjs");return i?{worker:i,packaged:!0,expectedWorker:o[0]}:r(s)?{worker:s,packaged:!1,expectedWorker:o[0]}:{worker:void 0,packaged:!1,expectedWorker:o[0]}}(e);if(!r){z.updateJobs.patch(a.id,{status:"failed",error:`Update worker was not found: ${s}`,completedAt:sr()});const e=new Error(`Node agent update worker was not found: ${s}`);throw Object.assign(e,{statusCode:500,code:"UPDATE_WORKER_NOT_FOUND"}),e}await k("systemd-run",["--unit",`task-handoff-update-${a.id}`,"--collect","--property=Type=exec",...o?[r]:[process.execPath,r],"--job-file",z.updateJobs.records.filePath(a.id),"--target-version",a.toVersion,"--npm-command",fn()])}else z.updateJobs.run(a,e=>D(e));return r.code(202).send({data:a})}),E.post("/api/node-agent/pairing/invites",async(e,t)=>{const n=f.createPairingInvite(Dn.parse(e.body||{}));return t.code(201).send({data:{nodeId:y,joinToken:n.token,expiresAt:n.expiresAt}})}),E.post("/api/node-agent/pairing/complete",async(e,t)=>{const n=f.completePairingInvite(jn.parse(e.body));return t.code(201).send({data:{nodeId:y,keyId:n.keyId,secret:n.secret,pairedAt:n.pairedAt}})}),E.get("/api/node-agent/remotes",async e=>({data:f.listRemoteControlPlanes(e.nodeAgentAuthKeyId)})),E.delete("/api/node-agent/remotes/:keyId",async e=>{const t=l.z.string().trim().min(1).max(160).parse(e.params.keyId);return{data:{deleted:f.deleteRemoteControlPlane(t,e.nodeAgentAuthKeyId)}}}),E.post("/api/node-agent/remotes/connect",async(e,t)=>{const n=Rn.parse(e.body),r=function(e){const t=new URL(e);if("http:"!==t.protocol&&"https:"!==t.protocol){const e=new Error("Control-plane URL must use http or https.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_CONTROL_PLANE_URL_INVALID"}),e}return t.toString().replace(/\/$/,"")}(n.controlPlaneUrl),o=f.createRemoteControlPlane({url:r,...n.controlPlaneName?{name:n.controlPlaneName}:{},active:!1!==n.activate}),i=await async function(e,t,n){const r=await e(new URL("/api/node-join/complete",t).toString(),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),o=await r.json().catch(()=>({}));if(!r.ok){const e=new Error(o.error?.message||`Control-plane node join failed with HTTP ${r.status}.`);throw Object.assign(e,{statusCode:r.status,code:o.error?.code||"NODE_AGENT_REMOTE_JOIN_FAILED"}),e}return o.data||{}}(N,r,{joinToken:n.joinToken,nodeId:y,nodeName:z.node.name,keyId:o.keyId,secret:o.secret,pairedAt:o.pairedAt}),s=f.upsertRemoteControlPlane({...o,..."string"==typeof i.name&&i.name?{name:n.controlPlaneName||i.name}:{}});let a,c=!1!==s.active?"saved":"disabled";if(!1!==s.active&&E.nodeAgentReverseTunnels)try{E.nodeAgentReverseTunnels.connect({url:r,keyId:s.keyId,secret:s.secret}),c="connecting"}catch(e){c="failed",a=e instanceof Error?e.message:String(e)}return t.code(201).send({data:{remote:{id:s.id,url:s.url,keyId:s.keyId,pairedAt:s.pairedAt,active:!1!==s.active},tunnel:{status:c,...a?{error:a}:{}}}})}),E.get("/api/node-agent/runtimes",async()=>({data:z.nodeRuntimes.list()})),E.post("/api/node-agent/runtimes",async(e,t)=>t.code(201).send({data:z.createRuntime(jt.parse(e.body))})),E.patch("/api/node-agent/runtimes/:id",async e=>({data:z.updateRuntime(e.params.id,Rt.parse(e.body))})),E.delete("/api/node-agent/runtimes/:id",async e=>({data:{deleted:z.deleteRuntime(e.params.id)}})),E.post("/api/node-agent/runtimes/:id/check",async e=>{const t=z.requireRuntime(e.params.id);return{data:await z.checkRuntime(t.id,S.forRuntime(t))}}),E.get("/api/node-agent/local-folders",async()=>({data:z.localFolders.list()})),E.get("/api/node-agent/folders/tree",async e=>({data:vt(Tt.parse(e.query))})),E.post("/api/node-agent/local-folders",async(e,t)=>t.code(201).send({data:z.createLocalFolder(xt.parse(e.body))})),E.delete("/api/node-agent/local-folders/:id",async e=>({data:{deleted:z.localFolders.delete(e.params.id)}})),E.get("/api/node-agent/models",async()=>({data:z.listModels()})),E.post("/api/node-agent/models",async(e,t)=>t.code(201).send({data:z.createModel(Ce.parse(e.body))})),E.put("/api/node-agent/models/:id/deploy",async e=>{const t=e.params.id,n=je.parse(e.body);if(n.id!==t)throw Object.assign(new Error(`Model payload id ${n.id} does not match route id ${t}.`),{statusCode:400,code:"NODE_MODEL_ID_MISMATCH"});return{data:z.deployModel(n)}}),E.patch("/api/node-agent/models/:id",async e=>({data:z.updateModel(e.params.id,De.parse(e.body))})),E.delete("/api/node-agent/models/:id",async e=>({data:{deleted:z.deleteModel(e.params.id)}})),E.get("/api/node-agent/instances",async()=>({data:z.listInstances()})),E.post("/api/node-agent/instances",async(e,t)=>{const n=z.createInstance(Ct.parse(e.body));return _.syncNow(),t.code(201).send({data:n})}),E.patch("/api/node-agent/instances/:id",async e=>{const t=e.params.id,n=z.requireInstance(t),r=Dt.parse(e.body),o=Ge.parse({...n,...r,updatedAt:sr()}),i=z.controlledInstances.put(o);return _.syncNow(),{data:i}}),E.put("/api/node-agent/instances/:id/model-assignment",async e=>{const t=e.params.id,n=z.assignModels(t,Re.parse(e.body));return await gr(N,z,t),{data:n}}),E.post("/api/node-agent/instances/:id/register",async(e,t)=>{const n=e.params.id,r=z.registerInstance(n,Xe.parse(e.body),rr(e.headers));return _.syncNow(),x({instanceId:n,action:"register",protocolVersion:r.protocolVersion,build:r.build,targetStatus:r.targetStatus,targetStrategy:r.target.strategy},"node instance registered"),t.code(201).send({data:r})}),E.post("/api/node-agent/instances/:id/heartbeat",async e=>{const t=e.params.id,n=z.heartbeatInstance(t,Qe.parse(e.body),rr(e.headers));return _.syncNow(),x({instanceId:t,action:"heartbeat",status:n.status,health:n.health,protocolVersion:n.protocolVersion,build:n.build,targetStatus:n.targetStatus,apps:n.apps.runningCount},"node instance heartbeat accepted"),{data:n}}),E.get("/api/node-agent/events",{websocket:!0},e=>{const t=e;t.send(JSON.stringify({type:"node-agent.events.connected",nodeId:y,serverTime:(new Date).toISOString()}));const n=_.addOutput(t);t.on("close",n),t.on("error",n)}),E.get("/api/node-agent/docker/images",async()=>({data:await st(e.dockerCommandRunner)})),E.post("/api/node-agent/instances/:id/start",async e=>{const t=e.params.id;Qn.parse(e.body);const n=await xr(z,S,N,t,T);return _.syncNow(),{data:n}}),E.post("/api/node-agent/instances/:id/stop",async e=>{const t=z.requireInstance(e.params.id);x({instanceId:t.id,action:"stop",runtimeId:t.runtimeId,containerName:t.runtime.containerName},"node instance stop requested");const n=S.forRuntime(z.requireRuntime(t.runtimeId)),r=await n.stop(z.context(t)),o=Ge.parse({...t,...r,agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",updatedAt:sr()}),i=z.controlledInstances.put(o);return _.syncNow(),x({instanceId:t.id,action:"stop",status:i.status,connectionStatus:i.connectionStatus,containerName:i.runtime.containerName},"node instance stop completed"),{data:i}}),E.post("/api/node-agent/instances/:id/restart",async e=>{const t=e.params.id,n=z.requireInstance(t);Qn.parse(e.body),x({instanceId:t,action:"restart",runtimeId:n.runtimeId,imageId:n.imageId,containerName:n.runtime.containerName},"node instance restart requested");const r=S.forRuntime(z.requireRuntime(n.runtimeId)),o=await r.restart(z.context(n)),i=Ge.parse({...n,...o,target:o.target?{...n.target,...o.target}:n.target,updatedAt:sr()}),s=await ur(N,i),a=Ge.parse({...n,...o,target:o.target?{...n.target,...o.target,status:s}:n.target,targetStatus:s,uiAccessStatus:s,connectionStatus:o.connectionStatus,updatedAt:sr()}),c=z.controlledInstances.put(a);return await mr(N,c,"restart",T),_.syncNow(),x({instanceId:t,action:"restart",status:c.status,connectionStatus:c.connectionStatus,targetStatus:c.targetStatus,targetWeb:c.target.web,containerName:c.runtime.containerName},"node instance restart completed"),{data:c}}),E.post("/api/node-agent/instances/:id/delete",async e=>{const t=e.params.id,n=z.requireInstance(t);x({instanceId:t,action:"delete",runtimeId:n.runtimeId,containerName:n.runtime.containerName},"node instance delete requested");const r=S.forRuntime(z.requireRuntime(n.runtimeId));await r.delete(z.context(n,{})),x({instanceId:t,action:"delete"},"node instance delete completed");const o=z.controlledInstances.delete(t);return z.modelAssignments.delete(t),z.modelEnvironments.delete(t),_.syncNow(),{data:{deleted:o}}}),E.post("/api/node-agent/instances/:id/proxy",async(e,t)=>{const n=e.params.id,r=Ot.parse(e.body),o=hr(z.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;"POST"===r.method&&"/api/apps/sessions"===i&&await gr(N,z,n),x({instanceId:n,action:"proxy",method:r.method,path:i,instanceBase:o},"node instance proxy requested");const s=await N(`${o}${i}`,{method:r.method,headers:{...r.headers},body:nr(r)}),a=s.headers.get("content-type")||"application/octet-stream",c=await s.text();x({instanceId:n,action:"proxy",method:r.method,path:i,statusCode:s.status,contentType:a},"node instance proxy completed"),t.code(s.status).type(a).send(c)}),E.post("/api/node-agent/instances/:id/proxy/raw",async e=>{const t=e.params.id,n=Ot.parse(e.body),r=hr(z.requireInstance(t)),o=n.path.startsWith("/")?n.path:`/${n.path}`;x({instanceId:t,action:"proxy.raw",method:n.method,path:o,instanceBase:r},"node instance raw proxy requested");const i=await N(`${r}${o}`,{method:n.method,headers:{...n.headers},body:nr(n)}),s=Buffer.from(await i.arrayBuffer());return x({instanceId:t,action:"proxy.raw",method:n.method,path:o,statusCode:i.status,byteLength:s.length},"node instance raw proxy completed"),{data:{status:i.status,headers:(a=i.headers,Object.fromEntries([...a.entries()].filter(([e])=>!Jn.has(e.toLowerCase())))),bodyBase64:s.toString("base64")}};var a}),E.get("/api/node-agent/instances/:id/proxy/ws/*",{websocket:!0},(e,t)=>{const n=t.params.id,r=t.params["*"]||"",o=t.url.indexOf("?"),i=o>=0?t.url.slice(o):"";let s;try{const o=hr(z.requireInstance(n)),a=new URL(`/${r}${i}`,`${o}/`);a.protocol="https:"===a.protocol?"wss:":"ws:";const c=function(e){const t=e["sec-websocket-protocol"],n=(Array.isArray(t)?t.join(","):"string"==typeof t?t:"").split(",").map(e=>e.trim()).filter(Boolean);return n.length?n:void 0}(t.headers);x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,upstreamUrl:a.toString(),protocols:c||[]},"node instance websocket proxy opening"),s=c?new d(a,c):new d(a),s.on("open",()=>{x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy opened"),e.on("message",(e,t)=>s?.readyState===d.OPEN&&s.send(function(e,t){return t?e:e.toString()}(e,t))),e.on("close",()=>s?.close()),e.on("error",()=>s?.close())}),s.on("message",(t,n)=>{e.readyState===d.OPEN&&e.send(n?t:t.toString())}),s.on("close",()=>{x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy closed"),e.close()}),s.on("error",t=>{x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:t instanceof Error?t.message:String(t)},"node instance websocket proxy failed"),e.close(1011,"Instance websocket proxy failed.")})}catch(t){x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:t instanceof Error?t.message:String(t)},"node instance websocket endpoint unavailable"),s?.close(),e.close(1011,"Instance websocket endpoint is not reachable.")}}),E}function jr(e){const t=new URL("/api/node-agent/tunnel",e);return t.protocol="https:"===t.protocol?"wss:":"ws:",t.toString()}function Rr(e,t){const n=new URL(t.tunnelUrl);n.searchParams.set("nodeId",t.nodeId);const r=t.secret?un({nodeId:t.nodeId,keyId:t.keyId,secret:t.secret,method:"GET",pathWithQuery:`${n.pathname}${n.search}`}):{},o=new d(n,{headers:r}),i=new Map;let s;const a=e=>{const n=e.startsWith("/")?e:`/${e}`,r="function"==typeof t.port?t.port():t.port,o=new URL(`/api/node-agent${n}`,`http://127.0.0.1:${r}`);return o.protocol="ws:",o},c=e=>t.secret?un({nodeId:t.nodeId,keyId:t.keyId,secret:t.secret,method:"GET",pathWithQuery:`${e.pathname}${e.search}`}):{},l=(e,t=1e3,n="")=>{const r=i.get(e);i.delete(e),r?.upstream?.close(t,n),r?.tunnel?.close(t,n)};return o.on("open",()=>{o.send(JSON.stringify({type:"node-agent.identify",nodeId:t.nodeId,serverTime:(new Date).toISOString()})),s=e.nodeAgentEventForwarder?.addOutput(o)}),o.on("close",()=>{for(const e of i.keys())l(e,1001,"Reverse tunnel disconnected.");s?.(),s=void 0}),o.on("message",async r=>{let s;try{s=JSON.parse(String(r))}catch{return void o.send(JSON.stringify({type:"node-agent.error",code:"INVALID_JSON"}))}const u=s&&"object"==typeof s?s:{};if("control-plane.websocket.open"===u.type){const e="string"==typeof u.streamId?u.streamId:"",r="string"==typeof u.route?u.route:"",s=Array.isArray(u.protocols)?u.protocols.filter(e=>"string"==typeof e):void 0;try{const l=t.token?{authorization:`Bearer ${t.token}`}:void 0,u=s?.length?new d(a(r),s,{headers:l}):new d(a(r),{headers:l}),p=(e=>{const t=new URL(n);return t.pathname=`${t.pathname.replace(/\/$/,"")}/streams/${encodeURIComponent(e)}`,t})(e),m=new d(p,{headers:c(p)});i.set(e,{upstream:u,tunnel:m,close:(e=1e3,t="")=>{u.close(e,t),m.close(e,t)}}),u.on("open",()=>{o.send(JSON.stringify({type:"node-agent.websocket.open",streamId:e,protocol:u.protocol}))}),function(e,t,n={}){const r=n.pendingFrameLimit??256;let o=!1,i=et(t);const s=[],a=[];let c;const d=()=>{c&&(clearTimeout(c),c=void 0)},l=(n,o)=>{if(n.length>=r)return tt(e,1011,"WebSocket bridge pending frame limit exceeded."),void tt(t,1011,"WebSocket bridge pending frame limit exceeded.");n.push(o)};n.upstreamOpenTimeoutMs&&!i&&(c=setTimeout(()=>{i||o||(tt(e,1011,"Upstream websocket did not open."),tt(t))},n.upstreamOpenTimeoutMs)),e.on("open",()=>rt(e,a)),t.on("open",()=>{d(),i=!0,n.onUpstreamOpen?.(),rt(t,s)}),e.on("message",(e,n=!1)=>{const r={data:e,isBinary:Boolean(n)};et(t)?nt(t,r):l(s,r)}),t.on("message",(t,n=!1)=>{const r={data:t,isBinary:Boolean(n)};et(e)?nt(e,r):l(a,r)}),t.on("close",(t,r)=>{d(),n.onUpstreamClose?.(t,r),(i||o||!n.onUpstreamCloseBeforeOpen?.())&&tt(e,t,r)}),t.on("error",t=>{n.onUpstreamError?.(t),(i||o||!n.onUpstreamErrorBeforeOpen?.(t))&&tt(e)}),e.on("close",(e,r)=>{d(),o=!0,n.onClientClose?.(e,r),tt(t,e,r)}),e.on("error",e=>{d(),o=!0,n.onClientError?.(e),tt(t,1011,"WebSocket bridge client failed.")})}(m,u,{pendingFrameLimit:256,upstreamOpenTimeoutMs:1e4,onClientClose:()=>{i.delete(e)},onClientError:t=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))},onUpstreamClose:(t,n)=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.close",streamId:e,code:"number"==typeof t?t:1e3,reason:Buffer.isBuffer(n)?n.toString("utf8"):String(n||"")}))},onUpstreamError:t=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))}})}catch(t){o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))}return}if("control-plane.websocket.close"===u.type){const e="string"==typeof u.streamId?u.streamId:"";return void l(e,"number"==typeof u.code?u.code:1e3,"string"==typeof u.reason?u.reason:"")}if("control-plane.request"===u.type){const n="string"==typeof u.requestId?u.requestId:"",r=u.init&&"object"==typeof u.init?u.init:{},i="string"==typeof u.route&&u.route.startsWith("/")?u.route:"/health",s=r.headers&&"object"==typeof r.headers?r.headers:{};try{const a=await e.inject({method:(p=r.method,"string"==typeof p&&pn.has(p.toUpperCase())?p.toUpperCase():"GET"),url:`/api/node-agent${i}`,headers:{...s,...t.token?{authorization:`Bearer ${t.token}`}:{}},payload:"string"==typeof r.body?r.body:void 0});o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:a.statusCode,headers:a.headers,body:a.body}))}catch(e){o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:502,error:{code:"NODE_AGENT_REVERSE_INJECT_FAILED",message:e instanceof Error?e.message:String(e)}}))}}var p}),o}async function Pr(e){const o=Pt(e.dataDir),i=(s=e.host,a=e.port,Le.parse({bindScope:"0.0.0.0"===s.trim()?"all-ipv4":"loopback",port:a}));var s,a;const c=t.existsSync(o.settingsPath),d=function(e,t){return new Bt(e.settingsPath,()=>({version:1,externalListener:t}),{schema:Xn,sanitize:cr})}(o,i),l=d.get().externalListener,m=function(e,r={}){return function(e,r={}){t.mkdirSync(n.dirname(e),{recursive:!0});const o={pid:process.pid,hostname:p.hostname(),component:r.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:u.randomUUID(),dataDir:r.dataDir,host:r.host,port:r.port};for(let n=0;n<5;n+=1)try{return t.mkdirSync(e),t.writeFileSync(Jt(e),`${JSON.stringify({token:o.token,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Qt(e,o),{lockPath:e,owner:o,release(){const n=Yt(e);n?.token===o.token&&t.rmSync(e,{recursive:!0,force:!0})}}}catch(o){if("EEXIST"!==o.code)throw o;const i=Yt(e);if(Xt(i?.pid))throw new Vt(e,i,r.error);if(!i&&nn(e)<2e3){if(n+1<5){tn();continue}throw new Vt(e,void 0,r.error)}const s=u.randomUUID();if(!rn(e,s)){if(n+1<5){tn();continue}throw new Vt(e,Yt(e),r.error)}const a=Yt(e);if(Xt(a?.pid))throw en(Wt(e)),new Vt(e,a,r.error);if(i?.token&&a?.token!==i.token){en(Wt(e));continue}if(!sn(e,s))continue;t.rmSync(e,{recursive:!0,force:!0})}const i=Yt(e);throw new Vt(e,i,r.error)}(e,{...r,component:"node-agent",error:{label:"Node agent",code:"NODE_AGENT_ALREADY_RUNNING"}})}(n.join(p.tmpdir(),`task-handoff-node-agent-${process.getuid?.()??"user"}.lock`),{dataDir:o.dataDir,host:ar(l.bindScope),port:l.port});try{const i={...e,port:l.port},s=await Dr(i),a=function(e,t,n,r){const o=t.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,i=new Gn(n),s=new Map,a=t=>{const n=t.url.replace(/\/$/,"");s.get(n)?.close(1e3,"Reverse tunnel reconnecting.");const i=Rr(e,{tunnelUrl:jr(t.url),nodeId:r,port:()=>e.nodeAgentState.currentListenerPort,token:o,keyId:t.keyId,secret:t.secret});return s.set(n,i),i.on("close",()=>{s.get(n)===i&&s.delete(n)}),i};return{connect:a,connectConfigured:()=>{const n=function(e){const t=e.controlPlaneTunnelUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_TUNNEL_URL;if(t)return t;const n=process.env.TASK_HANDOFF_CONTROL_PLANE_URL;if(!n)return;const r=new URL("/api/node-agent/tunnel",n);return r.protocol="https:"===r.protocol?"wss:":"ws:",r.toString()}(t);if(n){const a=i.reverseTunnelSecret(process.env.TASK_HANDOFF_CONTROL_PLANE_URL,t.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,t.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID),c=(process.env.TASK_HANDOFF_CONTROL_PLANE_URL||n).replace(/\/$/,"");s.get(c)?.close(1e3,"Reverse tunnel reconnecting.");const d=Rr(e,{tunnelUrl:n,nodeId:r,port:()=>e.nodeAgentState.currentListenerPort,token:o,keyId:a?.keyId,secret:a?.secret});s.set(c,d)}for(const e of i.configuredRemoteControlPlanes())e.url&&!1!==e.active&&a({url:e.url,keyId:e.keyId,secret:e.secret})},closeAll:()=>{for(const e of s.values())e.close(1001,"Node agent shutting down.");s.clear()}}}(s,i,o,new Gn(o).resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID));s.decorate("nodeAgentReverseTunnels",a);const u=new Or({app:s,state:s.nodeAgentState,settings:d,config:l,source:c?"persisted":"bootstrap"});let p;s.decorate("nodeAgentListenerManager",u),function(e){let t=!1;const n=async n=>{if(!t){t=!0,a.closeAll(),p?.close();try{await e.close()}finally{process.exitCode=n?0:process.exitCode}}},r=()=>{n("SIGINT")},o=()=>{n("SIGTERM")};process.once("SIGINT",r),process.once("SIGTERM",o),e.addHook("onClose",async()=>{process.off("SIGINT",r),process.off("SIGTERM",o)})}(s),s.addHook("onClose",async()=>{p?.close(),m.release()});try{await s.ready();const i=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||an(o.dataDir);p=await async function(e,o){!function(e,r=process.platform){if("win32"!==r){t.mkdirSync(n.dirname(e),{recursive:!0,mode:448});try{t.chmodSync(n.dirname(e),448)}catch{}try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}}(o);const i=r.createServer((t,n)=>{e.server.emit("request",t,n)});return i.on("upgrade",(t,n,r)=>{e.server.emit("upgrade",t,n,r)}),await new Promise((e,t)=>{i.once("error",t),i.listen(o,()=>{i.off("error",t),e()})}),i}(s,i),await u.start(),a.connectConfigured(),await(s.nodeAgentRestoreLocalInstances?.())}catch(e){throw await s.close(),e}}catch(e){throw m.release(),e}}function Mr(e){const t=Number(e);if(!Number.isInteger(t)||t<=0||t>65535)throw new Error("Port must be a valid TCP port.");return t}function Fr(e){if("local-ipc"!==e&&"local-loopback"!==e)throw new Error("Connection mode must be local-ipc or local-loopback.");return e}(async function(){const r=new e.Command;r.name("task-handoff-node-agent").description("Run a TaskHandoff node agent.").version(function(){try{return JSON.parse(t.readFileSync(n.resolve(__dirname,"..","package.json"),"utf8")).version||"unknown"}catch{return"unknown"}}()).option("--host <host>","Node agent host",process.env.TASK_HANDOFF_NODE_AGENT_HOST||"127.0.0.1").option("-p, --port <port>","Node agent port",Mr,Number(process.env.TASK_HANDOFF_NODE_AGENT_PORT)||8091).option("--data-dir <path>","Node agent data directory").option("--token <token>","Bearer token required by the control plane").option("--connection-mode <mode>","Local control connection mode",Fr).option("--ipc-path <path>","Unix socket path used by local-ipc mode").option("--control-plane-tunnel-url <url>","Control plane reverse WebSocket tunnel URL").action(async e=>{await Pr({host:e.host,port:e.port,dataDir:e.dataDir,token:e.token,connectionMode:e.connectionMode,ipcPath:e.ipcPath,controlPlaneTunnelUrl:e.controlPlaneTunnelUrl})}),await r.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});
|
|
1
|
+
"use strict";var e=require("commander"),t=require("node:fs"),n=require("node:path"),r=require("node:http"),o=require("node:net"),i=require("node:url"),s=require("node:child_process"),a=require("node:stream"),c=require("fastify"),d=require("@fastify/websocket"),l=require("ws"),u=require("zod"),p=require("node:crypto"),m=require("node:os"),g=require("write-file-atomic"),h=require("semver"),f="undefined"!=typeof document?document.currentScript:null;const y=41943040,I="ai.sessions",b=u.z.string().trim().min(1).max(80),A=u.z.enum(["running","waiting","idle","failed"]),z=u.z.enum(["thinking","tool","editing","approval","responding","unknown"]),w=u.z.object({id:u.z.string().trim().min(1).max(240).optional(),kind:u.z.string().trim().min(1).max(80).optional(),name:u.z.string().trim().min(1).max(120),inputPreview:u.z.string().trim().max(500).optional(),startedAt:u.z.string().datetime().optional()}).strict(),S=u.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),v=u.z.enum(["started","interacted","interrupted"]),N=u.z.object({threadId:u.z.string().trim().min(1).max(240),path:u.z.string().trim().max(4096).optional(),status:S,activity:v.optional(),message:u.z.string().trim().max(1e3).optional(),updatedAt:u.z.string().datetime()}).strict(),E=u.z.object({toolCalls:u.z.number().int().min(0).default(0),edits:u.z.number().int().min(0).default(0),approvals:u.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),k=u.z.object({send:u.z.boolean().optional(),interrupt:u.z.boolean().optional(),approval:u.z.boolean().optional()}).strict(),_=u.z.object({id:u.z.string().trim().min(1).max(120),kind:u.z.enum(["image"]),name:u.z.string().trim().min(1).max(240),mime:u.z.string().trim().min(1).max(120),size:u.z.number().int().min(0).max(20971520),data:u.z.string().min(1).max(31457280)}).strict(),O=_.omit({data:!0}),x=u.z.object({id:u.z.string().trim().min(1).max(120),kind:u.z.enum(["image"]).optional()}).strict(),T=u.z.enum(["auto","queue","steer","immediate"]),C=u.z.object({message:u.z.string().trim().min(1).max(2e4),mode:T.optional()});C.extend({attachments:u.z.array(_).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+t.size,0)>y&&t.addIssue({code:u.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),C.extend({attachments:u.z.array(x).max(6).default([])}).strict(),u.z.object({decision:u.z.enum(["allow","deny","skip"])}).strict(),u.z.object({queueIds:u.z.array(u.z.string().trim().min(1).max(120)).max(100)}).strict(),u.z.object({code:u.z.string().trim().min(1).max(160),message:u.z.string(),instanceId:u.z.string().trim().min(1).max(160).optional(),nodeId:u.z.string().trim().min(1).max(160).optional(),route:u.z.string().trim().min(1).max(4096).optional()}).strict();const R=u.z.object({id:u.z.string().trim().min(1).max(120),message:u.z.string().trim().min(1).max(2e4),attachments:u.z.array(O).max(6).default([]),status:u.z.enum(["queued","sending","failed"]).default("queued"),createdAt:u.z.string().datetime(),updatedAt:u.z.string().datetime(),error:u.z.string().trim().max(4e3).optional()}).strict(),D=u.z.object({pendingCount:u.z.number().int().min(0).default(0),items:u.z.array(R).max(100).default([])}).strict().default({pendingCount:0,items:[]}),j=u.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),P=u.z.object({id:u.z.string().trim().min(1).max(240),providerTurnId:u.z.string().trim().max(240).optional(),source:j.optional(),userPrompt:u.z.string().trim().optional(),status:u.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:z.optional(),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),revision:u.z.number().int().min(0).default(0),sourcePriority:u.z.number().int().min(0).max(100).optional(),snapshotVersion:u.z.number().int().min(0).optional(),observedAt:u.z.string().datetime().optional(),startedAt:u.z.string().datetime().optional(),updatedAt:u.z.string().datetime().optional(),completedAt:u.z.string().datetime().optional()}).strict(),M=u.z.object({id:u.z.string().trim().min(1).max(120),agent:b,appSessionId:u.z.string().trim().max(120).optional(),appId:u.z.string().trim().max(120).optional(),providerSessionId:u.z.string().trim().max(240).optional(),providerMeta:u.z.record(u.z.string(),u.z.unknown()).optional(),appBindingKeys:u.z.array(u.z.string().trim().min(1).max(240)).max(20).optional(),actions:k.optional(),activeTurnId:u.z.string().trim().max(240).optional(),title:u.z.string().trim().max(240).optional(),cwd:u.z.string().trim().max(4096).optional(),userPrompt:u.z.string().trim().optional(),turns:u.z.array(P).max(50).optional(),status:A.default("running"),phase:z.default("unknown"),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),currentTool:w.optional(),toolCallsSinceLastMessage:u.z.number().int().min(0).default(0),subAgents:u.z.array(N).max(50).default([]),transcriptPath:u.z.string().trim().max(4096).optional(),transcriptSize:u.z.number().int().min(0).optional(),startedAt:u.z.string().datetime(),updatedAt:u.z.string().datetime(),completedAt:u.z.string().datetime().optional(),error:u.z.string().trim().max(4e3).optional(),counters:E,queue:D}).strict();u.z.object({session:M,provider:u.z.string().trim().min(1).max(80),action:u.z.enum(["send","queue","steer","interrupt","approval"]),decision:u.z.enum(["allow","deny","skip"]).optional(),turnId:u.z.string().trim().min(1).max(240).optional(),providerTurnId:u.z.string().trim().min(1).max(240).optional(),queueId:u.z.string().trim().min(1).max(120).optional()}).strict();const $=M.pick({id:!0,agent:!0,appSessionId:!0,appId:!0,providerSessionId:!0,providerMeta:!0,appBindingKeys:!0,actions:!0,activeTurnId:!0,title:!0,cwd:!0,userPrompt:!0,turns:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,currentTool:!0,toolCallsSinceLastMessage:!0,subAgents:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),F=u.z.object({runningCount:u.z.number().int().min(0).default(0),waitingCount:u.z.number().int().min(0).default(0),staleCount:u.z.number().int().min(0).default(0),sessions:u.z.array($).default([]),updatedAt:u.z.string().datetime()}).strict();u.z.object({streamId:u.z.string().trim().min(1).max(240),revision:u.z.number().int().min(0),lastEventAt:u.z.string().datetime(),snapshot:F}).strict(),F.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:u.z.number().int().min(0).default(0),problemCount:u.z.number().int().min(0).default(0),revision:u.z.number().int().min(0).optional()});const L=u.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),U=u.z.object({streamId:u.z.string().trim().min(1).max(240),instanceId:u.z.string().trim().min(1).max(160),nodeId:u.z.string().trim().min(1).max(160).optional(),revision:u.z.number().int().min(0),previousRevision:u.z.number().int().min(0).optional(),traceId:u.z.string().trim().min(1).max(160),generatedAt:u.z.string().datetime(),reason:L}).strict(),H=u.z.object({meta:U,snapshot:F}).strict(),q=u.z.object({meta:U,upserted:u.z.array($).default([]),removed:u.z.array(u.z.string().trim().min(1).max(120)).default([])}).strict(),K=u.z.object({meta:U,sessionIds:u.z.array(u.z.string().trim().min(1).max(120)).default([]),expiresAt:u.z.string().datetime()}).strict();u.z.object({instanceId:u.z.string().trim().min(1).max(160),nodeId:u.z.string().trim().min(1).max(160).optional(),sessionId:u.z.string().trim().min(1).max(120),providerSessionId:u.z.string().trim().min(1).max(240),turnId:u.z.string().trim().min(1).max(240).optional(),itemId:u.z.string().trim().min(1).max(240).optional(),delta:u.z.string().min(1),generatedAt:u.z.string().datetime()}).strict(),u.z.object({streamId:u.z.string().trim().min(1).max(240),instanceId:u.z.string().trim().min(1).max(160),sinceRevision:u.z.number().int().min(0),latestRevision:u.z.number().int().min(0),earliestRetainedRevision:u.z.number().int().min(0),syncRequired:u.z.boolean().default(!1),events:u.z.array(u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("ai-session.snapshot"),payload:H}).strict(),u.z.object({type:u.z.literal("ai-session.patch"),payload:q}).strict(),u.z.object({type:u.z.literal("ai-session.removed"),payload:K}).strict()])).default([])}).strict();const B=u.z.object({source:j,sourcePriority:u.z.number().int().min(0).max(100).optional(),observedAt:u.z.string().datetime().optional(),snapshotVersion:u.z.number().int().min(0).optional()}).strict(),G=B.extend({type:u.z.literal("snapshot"),agent:u.z.string().trim().min(1).max(80),appSessionId:u.z.string().trim().max(120).optional(),appId:u.z.string().trim().max(120).optional(),providerSessionId:u.z.string().trim().max(240).optional(),providerMeta:u.z.record(u.z.string(),u.z.unknown()).optional(),appBindingKeys:u.z.array(u.z.string().trim().min(1).max(240)).max(20).optional(),actions:k.optional(),title:u.z.string().trim().max(240).optional(),cwd:u.z.string().trim().max(4096).optional(),activeTurnId:u.z.string().trim().max(240).optional(),userPrompt:u.z.string().trim().optional(),turns:u.z.array(P).max(50).optional(),status:A.optional(),phase:z.optional(),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),currentTool:w.optional(),toolCallsSinceLastMessage:u.z.number().int().min(0).optional(),subAgents:u.z.array(N).max(50).optional(),transcriptPath:u.z.string().trim().max(4096).optional(),transcriptSize:u.z.number().int().min(0).optional(),replaceActivity:u.z.boolean().optional()}).strict(),V=B.extend({type:u.z.literal("event"),sessionId:u.z.string().trim().min(1).max(120),kind:u.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed","tool-activity","sub-agent-activity"]),activeTurnId:u.z.string().trim().max(240).optional(),providerTurnId:u.z.string().trim().max(240).optional(),userPrompt:u.z.string().trim().optional(),text:u.z.string().optional(),status:A.optional(),phase:z.optional(),summary:u.z.string().trim().max(1e3).optional(),error:u.z.string().trim().max(4e3).optional(),currentTool:w.nullable().optional(),toolCallsSinceLastMessage:u.z.number().int().min(0).optional(),subAgents:u.z.array(N).max(50).optional(),counters:u.z.object({toolCalls:u.z.number().int().min(0).optional(),edits:u.z.number().int().min(0).optional(),approvals:u.z.number().int().min(0).optional()}).strict().optional()}).strict().superRefine((e,t)=>{if("sub-agent-activity"===e.kind?e.subAgents||t.addIssue({code:"custom",path:["subAgents"],message:"sub-agent-activity events require subAgents"}):void 0!==e.subAgents&&t.addIssue({code:"custom",path:["subAgents"],message:"subAgents is only valid for sub-agent-activity events"}),"tool-activity"!==e.kind)return void 0!==e.currentTool&&t.addIssue({code:"custom",path:["currentTool"],message:"currentTool is only valid for tool-activity events"}),void(void 0!==e.toolCallsSinceLastMessage&&t.addIssue({code:"custom",path:["toolCallsSinceLastMessage"],message:"toolCallsSinceLastMessage is only valid for tool-activity events"}));"currentTool"in e||t.addIssue({code:"custom",path:["currentTool"],message:"tool-activity events require currentTool"}),void 0===e.toolCallsSinceLastMessage&&t.addIssue({code:"custom",path:["toolCallsSinceLastMessage"],message:"tool-activity events require toolCallsSinceLastMessage"})});u.z.discriminatedUnion("type",[G,V]);const J=u.z.union([u.z.object({type:u.z.literal("schedule"),scheduleKind:u.z.literal("interval").default("interval"),intervalMs:u.z.number().int().min(1e3)}).strict(),u.z.object({type:u.z.literal("schedule"),scheduleKind:u.z.literal("daily"),timeOfDay:u.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:u.z.string().trim().min(1).max(120)}).strict(),u.z.object({type:u.z.literal("schedule"),scheduleKind:u.z.literal("weekly"),weekdays:u.z.array(u.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:u.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:u.z.string().trim().min(1).max(120)}).strict()]),W=u.z.union([J,u.z.object({type:u.z.literal("file-change"),roots:u.z.array(u.z.string().trim().min(1).max(4096)).min(1).max(50),globs:u.z.array(u.z.string().trim().min(1).max(500)).min(1).max(100),ignore:u.z.array(u.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:u.z.number().int().min(100).max(6e4).default(1500)}).strict(),u.z.object({type:u.z.literal("ai-session"),agent:u.z.string().trim().min(1).max(80).optional(),statuses:u.z.array(u.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:u.z.array(u.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),Y=u.z.object({promptTemplate:u.z.string().trim().min(1).max(2e4)}).strict(),X=u.z.object({cooldownMs:u.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:u.z.number().int().min(1).max(20).default(1),whenBusy:u.z.enum(["skip","queue"]).default("skip")}).strict(),Q=u.z.object({configHash:u.z.string().trim().min(8).max(80),name:u.z.string().trim().min(1).max(160),description:u.z.string().trim().max(1e3).optional(),source:W,action:Y,policy:X.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:u.z.string().datetime(),updatedAt:u.z.string().datetime()}).strict(),Z=u.z.object({type:u.z.literal("ai-session"),aiSessionId:u.z.string().trim().min(1).max(160)}).strict(),ee=u.z.object({configHash:u.z.string().trim().min(8).max(80),deploymentId:u.z.string().trim().min(1).max(240).optional(),instanceId:u.z.string().trim().min(1).max(160),origin:u.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:u.z.boolean().default(!0),target:Z,localName:u.z.string().trim().min(1).max(160).optional(),createdAt:u.z.string().datetime(),updatedAt:u.z.string().datetime()}).strict(),te=u.z.object({configHash:u.z.string().trim().min(8).max(80),deploymentId:u.z.string().trim().min(1).max(240).optional(),instanceId:u.z.string().trim().min(1).max(160),status:u.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:u.z.string().datetime().optional(),lastCompletedAt:u.z.string().datetime().optional(),lastSkippedAt:u.z.string().datetime().optional(),lastError:u.z.string().trim().max(4e3).optional(),runCount:u.z.number().int().min(0).default(0),skippedCount:u.z.number().int().min(0).default(0)}).strict(),ne=u.z.object({id:u.z.string().trim().min(1).max(160),configHash:u.z.string().trim().min(8).max(80),deploymentId:u.z.string().trim().min(1).max(240).optional(),instanceId:u.z.string().trim().min(1).max(160),eventType:u.z.enum(["manual","schedule","file-change","ai-session"]),status:u.z.enum(["started","completed","failed","skipped"]),target:Z,promptPreview:u.z.string().trim().max(500),eventSummary:u.z.string().trim().max(1e3).optional(),error:u.z.string().trim().max(4e3).optional(),startedAt:u.z.string().datetime(),completedAt:u.z.string().datetime().optional()}).strict();u.z.object({schemaVersion:u.z.literal(1).default(1),configs:u.z.array(Q).default([]),deployments:u.z.array(ee).default([]),runtime:u.z.array(te).default([]),recentRuns:u.z.array(ne).default([])}).strict();const re="2026-07-18",oe=u.z.string(),ie=u.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),se=u.z.string().datetime(),ae=u.z.record(u.z.string(),u.z.string()).default({}),ce=u.z.record(u.z.string(),u.z.string()).default({}),de=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,le=/^sha256:([a-fA-F0-9]{64})$/,ue=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,pe=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function me(e){const t=e.trim();if(!t||t.length>512||t.includes("://")||/\s/.test(t))throw new Error("Docker image reference must not contain a URL scheme or whitespace.");const n=t.indexOf("@");if(-1!==n&&n!==t.lastIndexOf("@"))throw new Error("Docker image reference contains more than one digest separator.");const r=-1===n?void 0:t.slice(n+1),o=-1===n?t:t.slice(0,n),i=r?le.exec(r):void 0;if(r&&!i)throw new Error("Docker image digest must use sha256 followed by 64 hexadecimal characters.");const s=o.lastIndexOf("/"),a=o.lastIndexOf(":"),c=a>s,d=c?o.slice(a+1):void 0,l=c?o.slice(0,a):o;if(d&&!de.test(d))throw new Error("Docker image tag is invalid.");if(!d&&!i)throw new Error("Docker image reference must include an explicit tag or sha256 digest.");const u=l.split("/");if(!u.length||u.some(e=>!e))throw new Error("Docker image repository path is invalid.");const p=u.length>1&&(u[0].includes(".")||u[0].includes(":")||"localhost"===u[0]),m=p?u.slice(1):u;if(p&&!ue.test(u[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!m.length||m.some(e=>!pe.test(e)))throw new Error("Docker image repository names must be lowercase and use valid separators.");const g=u.map((e,t)=>p&&0===t?e.toLowerCase():e).join("/"),h=i?`sha256:${i[1].toLowerCase()}`:void 0;return`${g}${d?`:${d}`:""}${h?`@${h}`:""}`}const ge=u.z.string().trim().min(1).max(512).transform((e,t)=>{try{return me(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),u.z.NEVER}}),he=u.z.string().trim().transform((e,t)=>{const n=le.exec(e);return n?`sha256:${n[1].toLowerCase()}`:(t.addIssue({code:"custom",message:"Docker image digest must use sha256 followed by 64 hexadecimal characters."}),u.z.NEVER)}),fe=u.z.object({codexModelHash:ie.nullable().optional(),claudeModelHash:ie.nullable().optional()}).strict().default({}),ye=u.z.object({id:ie,name:u.z.string().trim().min(1).max(120),kind:u.z.enum(["tty","gui","web"]),source:u.z.enum(["builtin","custom"]),availability:u.z.enum(["available","missing-dependency"]),capabilities:u.z.object({automation:u.z.enum(["cdp"]).optional(),supportsCwdSelection:u.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:u.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),Ie=u.z.object({code:u.z.enum(["APP_CATALOG_INVALID"]),message:u.z.string().trim().min(1).max(500)}).strict(),be=u.z.object({items:u.z.array(ye).max(256),observedAt:se,issues:u.z.array(Ie).max(32).default([])}).strict(),Ae=u.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),ze=u.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),we=u.z.enum(["apt","dnf","brew","npm"]),Se=u.z.enum(["user","passwordless-sudo","root"]),ve=u.z.object({platform:Ae,arch:ze,installers:u.z.array(we).max(8),privilege:Se,installerAccess:u.z.object({npmGlobalWritable:u.z.boolean().optional()}).strict().optional()}).strict(),Ne=u.z.enum(["installed","not-installed","broken","unsupported"]),Ee=u.z.enum(["recipe","bundled","external","none"]),ke=u.z.object({code:u.z.enum(["BUNDLED","EXTERNALLY_MANAGED","ALREADY_INSTALLED","NOT_INSTALLED","UNSUPPORTED_PLATFORM","INSTALLER_UNAVAILABLE","INSTALLER_NOT_WRITABLE","INSUFFICIENT_PRIVILEGE","OPERATION_IN_PROGRESS"]),message:u.z.string().trim().min(1).max(500)}).strict(),_e=u.z.object({id:ie,name:u.z.string().trim().min(1).max(120),kind:u.z.enum(["tty","gui","web"]),description:u.z.string().trim().max(500).optional(),state:Ne,managementSource:Ee,version:u.z.string().trim().max(120).optional(),canInstall:u.z.boolean(),canUninstall:u.z.boolean(),installReason:ke.optional(),uninstallReason:ke.optional(),activeJobId:ie.optional()}).strict(),Oe=u.z.enum(["install","uninstall"]),xe=u.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),Te=u.z.object({current:u.z.number().finite().nonnegative().optional(),total:u.z.number().finite().positive().optional(),unit:u.z.string().trim().min(1).max(40).optional()}).strict().refine(e=>void 0===e.total||void 0===e.current||e.current<=e.total,{message:"Progress current must not exceed total.",path:["current"]}),Ce=u.z.object({executable:u.z.string().trim().min(1).max(4096),args:u.z.array(u.z.string().max(4096)).max(128)}).strict(),Re=u.z.object({code:u.z.string().trim().min(1).max(120).regex(/^[a-z0-9][a-z0-9._-]*$/),message:u.z.string().trim().min(1).max(4096),retryable:u.z.boolean(),sessionIds:u.z.array(ie).max(64).optional()}).strict(),De=u.z.object({id:ie,requestId:ie.optional(),appId:ie,operation:Oe,state:xe,phase:u.z.string().trim().min(1).max(120).optional(),progress:Te.optional(),command:Ce.optional(),logTail:u.z.string().max(32768).optional(),logTruncated:u.z.boolean().optional(),error:Re.optional(),requestedAt:se,startedAt:se.optional(),finishedAt:se.optional(),updatedAt:se}).strict(),je=u.z.object({streamId:ie,sequence:u.z.number().int().nonnegative(),capabilities:ve,apps:u.z.array(_e).max(256),activeJobs:u.z.array(De).max(256),recentJobs:u.z.array(De).max(256),observedAt:se}).strict();u.z.object({requestId:ie.optional()}).strict(),u.z.object({job:De}).strict(),u.z.object({type:u.z.literal("app-management"),streamId:ie,sequence:u.z.number().int().nonnegative(),observedAt:se,job:De.optional(),snapshot:je.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const Pe=u.z.object({component:u.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:u.z.string().trim().max(160).optional(),packageVersion:u.z.string().trim().max(80).optional(),protocolVersion:oe.optional(),buildId:u.z.string().trim().max(160).optional(),builtAt:u.z.string().trim().max(120).optional(),gitCommit:u.z.string().trim().max(120).optional(),imageRef:u.z.string().trim().max(512).optional(),imageDigest:u.z.string().trim().max(240).optional()}).strict(),Me=u.z.enum(["stable","beta","alpha"]),$e=u.z.discriminatedUnion("component",[u.z.object({component:u.z.literal("node-agent")}).strict(),u.z.object({component:u.z.literal("controlled-instance"),instanceId:ie}).strict()]),Fe=u.z.object({target:$e,channel:Me.default("stable")}).strict(),Le=u.z.object({target:$e,source:u.z.enum(["npm","docker-registry"]),channel:Me,currentVersion:u.z.string().trim().max(240).optional(),availableVersion:u.z.string().trim().min(1).max(240),artifactRef:u.z.string().trim().min(1).max(512).optional(),updateAvailable:u.z.boolean(),supported:u.z.boolean().default(!0),reason:u.z.string().trim().max(2048).optional(),checkedAt:se}).strict(),Ue=u.z.object({id:ie,nodeId:ie,target:$e,source:u.z.enum(["npm","docker-registry"]),channel:Me,fromVersion:u.z.string().trim().max(240).optional(),toVersion:u.z.string().trim().min(1).max(240),artifactRef:u.z.string().trim().min(1).max(512).optional(),status:u.z.enum(["queued","updating","restarting","succeeded","failed"]),error:u.z.string().trim().max(4096).optional(),startedAt:se.optional(),completedAt:se.optional(),createdAt:se,updatedAt:se}).strict(),He=Fe,qe=u.z.object({type:u.z.enum(["branch","tag","commit"]),name:u.z.string().trim().min(1).max(240).optional(),commit:u.z.string().trim().max(80).optional()}).strict(),Ke=u.z.object({type:u.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:u.z.string().trim().min(1).max(120).optional()}).strict(),Be=u.z.object({depth:u.z.number().int().positive().max(1e5).optional(),submodules:u.z.boolean().default(!1),lfs:u.z.boolean().default(!1),subdirectory:u.z.string().trim().max(240).default("")}).strict();u.z.object({id:ie,name:u.z.string().trim().min(1).max(160),url:u.z.string().trim().min(1).max(2048),provider:u.z.string().trim().max(80).optional(),ref:qe.default({type:"branch",name:"main"}),auth:Ke.default({type:"none"}),clone:Be.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:ie.optional(),labels:ae,createdAt:se,updatedAt:se}).strict();const Ge=u.z.object({id:ie,nodeId:ie,name:u.z.string().trim().min(1).max(160),path:u.z.string().trim().min(1).max(4096),defaultImageId:ie.optional(),labels:ae,createdAt:se,updatedAt:se}).strict(),Ve=u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("git-repository"),repositoryId:ie.optional(),url:u.z.string().trim().min(1).max(2048),provider:u.z.string().trim().max(80).optional(),ref:qe.default({type:"branch",name:"main"}),auth:Ke.default({type:"none"}),clone:Be.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),u.z.object({type:u.z.literal("git-template"),url:u.z.string().trim().min(1).max(2048),templateId:u.z.string().trim().min(1).max(120).optional(),ref:qe.default({type:"branch",name:"main"}),auth:Ke.default({type:"none"}),clone:Be.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),u.z.object({type:u.z.literal("local-folder"),localFolderId:ie.optional(),path:u.z.string().trim().min(1).max(4096),ownerNodeId:ie.optional()}).strict()]),Je=u.z.object({mode:u.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:u.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:u.z.string().trim().max(240).optional(),readOnly:u.z.boolean().default(!1)}).strict(),We=u.z.enum(["codex","claude"]),Ye=u.z.object({id:ie,name:u.z.string().trim().min(1).max(160),source:Ve,defaultImageId:ie.optional(),defaultNodeId:ie.optional(),defaultRuntimeId:ie.optional(),workspacePolicy:Je,labels:ae,createdAt:se,updatedAt:se}).strict(),Xe=u.z.object({id:ie,name:u.z.string().trim().min(1).max(160),endpoint:u.z.string().trim().min(1).max(2048),key:u.z.string().trim().min(1).max(4096),model:u.z.string().trim().min(1).max(240),app:We,enabled:u.z.boolean().default(!0),order:u.z.number().int().min(0).max(1e6).default(0),labels:ae,createdAt:se,updatedAt:se}).strict(),Qe=Xe,Ze=Xe.omit({key:!0}).extend({keyPreview:u.z.string().trim().min(1).max(160),keySet:u.z.boolean()}).strict(),et=Ze.extend({referenceCount:u.z.number().int().min(0)}).strict(),tt=u.z.object({instanceId:ie,codexModelHash:ie.optional(),claudeModelHash:ie.optional(),updatedAt:se}).strict(),nt=u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("control-plane"),name:Xe.shape.name,enabled:Xe.shape.enabled,order:Xe.shape.order}).strict(),u.z.object({type:u.z.literal("node"),nodeId:ie,name:Xe.shape.name,enabled:Xe.shape.enabled,order:Xe.shape.order,referenceCount:u.z.number().int().min(0)}).strict()]),rt=u.z.object({id:ie,model:Ze,locations:u.z.array(nt).min(1),referenceCount:u.z.number().int().min(0).default(0)}).strict(),ot=u.z.object({nodeId:ie,code:u.z.string().trim().min(1).max(120),message:u.z.string().trim().min(1).max(2e3)}).strict();u.z.object({models:u.z.array(rt),nodeDiagnostics:u.z.array(ot).default([]),updatedAt:se}).strict();const it=Xe.omit({id:!0,createdAt:!0,updatedAt:!0}).strict(),st=it.partial().strict(),at=Xe,ct=u.z.object({modelSelection:fe,codexModelHash:ie.optional(),claudeModelHash:ie.optional()}).strict();function dt(e){const t={app:We.parse(e.app),endpoint:Xe.shape.endpoint.parse(e.endpoint),key:Xe.shape.key.parse(e.key),model:Xe.shape.model.parse(e.model)};return`mdl_${p.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}const lt=u.z.object({id:ie,name:u.z.string().trim().min(1).max(160),reference:ge,pullPolicy:u.z.literal("if-not-present").default("if-not-present"),capabilities:u.z.array(u.z.string().trim().min(1).max(80)).default([]),optionalApps:u.z.array(u.z.string().trim().min(1).max(120)).default([]),defaultEnv:ce,labels:ae,createdAt:se,updatedAt:se}).strict(),ut=lt.omit({reference:!0}).extend({requestedReference:ge,resolvedDigest:he.optional(),resolvedReference:ge.optional()}).strict(),pt=u.z.object({phase:u.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:ge,generation:u.z.number().int().nonnegative().default(0),error:u.z.string().trim().max(4096).optional(),startedAt:se,updatedAt:se}).strict();const mt=u.z.object({id:ie,name:u.z.string().trim().min(1).max(160),connectionMode:u.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:u.z.object({mode:u.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:ie.optional(),secret:u.z.string().trim().max(4096).optional(),pairedAt:se.optional(),pairing:u.z.object({status:u.z.enum(["pending","paired","expired"]).default("pending"),joinToken:u.z.string().trim().max(4096).optional(),expiresAt:se.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:u.z.string().trim().max(2048).optional(),controlEndpoint:u.z.string().trim().max(2048).optional(),containerEndpoint:u.z.string().trim().max(2048).optional(),publicWebBase:u.z.string().trim().max(2048).optional(),status:u.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:u.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),appInventory:be.optional(),labels:ae,lastSeenAt:se.optional(),createdAt:se,updatedAt:se}).strict(),gt=u.z.object({id:ie,nodeId:ie,name:u.z.string().trim().min(1).max(160),type:u.z.enum(["docker","kubernetes","local"]),status:u.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:u.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),labels:ae,createdAt:se,updatedAt:se}).strict(),ht=u.z.object({instanceId:ie,runtimeKind:u.z.literal("docker"),state:u.z.enum(["pending","available","stopped","unavailable"]),sampledAt:se,cpu:u.z.object({usagePercent:u.z.number().nonnegative()}).strict().optional(),memory:u.z.object({usageBytes:u.z.number().nonnegative(),limitBytes:u.z.number().nonnegative().optional(),usagePercent:u.z.number().nonnegative().optional()}).strict().optional(),network:u.z.object({rxBytes:u.z.number().nonnegative(),txBytes:u.z.number().nonnegative()}).strict().optional(),blockIo:u.z.object({readBytes:u.z.number().nonnegative(),writeBytes:u.z.number().nonnegative()}).strict().optional(),pids:u.z.number().int().nonnegative().optional(),error:u.z.string().trim().max(2048).optional()}).strict();u.z.object({ok:u.z.boolean().optional(),role:u.z.string().optional(),nodeId:ie.optional()}).passthrough();const ft=u.z.object({bindScope:u.z.enum(["loopback","all-ipv4"]),port:u.z.number().int().min(1).max(65535)}).strict(),yt=ft.extend({host:u.z.enum(["127.0.0.1","0.0.0.0"]),status:u.z.enum(["listening","error"]),source:u.z.enum(["bootstrap","persisted"]),error:u.z.string().trim().max(2048).optional()}).strict(),It=ft;u.z.object({nodeId:ie,joinToken:u.z.string().trim().min(1),expiresAt:se}).passthrough(),u.z.object({id:ie,keyId:ie,url:u.z.string().trim().min(1).max(2048).optional(),name:u.z.string().trim().min(1).max(160).optional(),pairedAt:se,updatedAt:se.optional(),active:u.z.boolean().optional(),current:u.z.boolean().optional()}).passthrough(),u.z.object({remote:u.z.object({id:ie,url:u.z.string().trim().min(1).max(2048).optional(),keyId:ie,pairedAt:se,active:u.z.boolean()}).passthrough(),tunnel:u.z.object({status:u.z.enum(["disabled","saved","connecting","connected","failed"]),error:u.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),u.z.object({deleted:u.z.boolean().optional()}).passthrough();const bt=u.z.object({repository:u.z.string().trim().optional(),tag:u.z.string().trim().optional(),id:u.z.string().trim(),createdSince:u.z.string().trim().optional(),size:u.z.string().trim().optional(),reference:u.z.string().trim().min(1),repoDigests:u.z.array(u.z.string().trim().min(1).max(512)).default([])}).passthrough();u.z.object({image:lt,status:u.z.enum(["available","pull-required","unknown"]),localImage:bt.optional(),error:u.z.string().trim().max(2048).optional()}).strict();const At=u.z.lazy(()=>u.z.object({name:u.z.string().trim().min(1),path:u.z.string().trim().min(1).max(4096),children:u.z.array(At)}).passthrough());u.z.object({status:u.z.number().int().min(100).max(599).optional(),headers:u.z.record(u.z.string(),u.z.string()).optional(),bodyBase64:u.z.string().optional()}).passthrough();const zt=u.z.object({strategy:u.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:u.z.string().trim().max(2048).optional(),api:u.z.string().trim().max(2048).optional(),vnc:u.z.string().trim().max(2048).optional(),tty:u.z.string().trim().max(2048).optional(),logs:u.z.string().trim().max(2048).optional(),status:u.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),wt=u.z.object({strategy:u.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:u.z.string().trim().max(2048).optional(),api:u.z.string().trim().max(2048).optional(),ws:u.z.string().trim().max(2048).optional(),status:u.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),St=u.z.object({mode:u.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:u.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:u.z.string().trim().max(4096).optional(),resolvedCommit:u.z.string().trim().max(120).optional(),error:u.z.string().trim().max(2048).optional()}).passthrough(),vt=u.z.object({id:ie,name:u.z.string().trim().min(1).max(160),projectId:ie.optional(),source:Ve,sourceSnapshot:u.z.record(u.z.string(),u.z.unknown()).default({}),modelSelection:fe,nodeId:ie,runtimeId:ie,imageId:ie.optional(),imageSnapshot:ut.optional(),imageProvisioning:pt.optional(),status:u.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:u.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:u.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:u.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:u.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:u.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:u.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:oe.optional(),instanceVersion:u.z.string().trim().max(80).optional(),build:Pe.optional(),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),appInventory:be.optional(),config:u.z.object({autoImportAgentConfigs:u.z.boolean().default(!0)}).strict().default({autoImportAgentConfigs:!0}),workspace:St.default({status:"unknown"}),target:zt.default({strategy:"direct-port",status:"unknown"}),access:wt,apps:u.z.object({runningCount:u.z.number().int().min(0).default(0),problemCount:u.z.number().int().min(0).default(0),updatedAt:se.optional(),revision:u.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:F.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:u.z.object({enabledCount:u.z.number().int().min(0).default(0),runningCount:u.z.number().int().min(0).default(0),errorCount:u.z.number().int().min(0).default(0),configs:u.z.array(u.z.object({configHash:u.z.string().trim().min(1),config:Q,deployments:u.z.array(ee).default([]),runtime:u.z.array(te).default([])}).strict()).default([]),recentRuns:u.z.array(ne).default([])}).strict().optional(),runtime:u.z.object({kind:u.z.string().trim().max(80).optional(),containerName:u.z.string().trim().max(240).optional(),containerId:u.z.string().trim().max(240).optional(),workspacePath:u.z.string().trim().max(4096).optional(),pid:u.z.number().int().positive().optional(),port:u.z.number().int().positive().max(65535).optional(),labels:u.z.record(u.z.string(),u.z.string()).default({})}).strict().default({labels:{}}),registrationToken:u.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:se.optional(),createdAt:se,updatedAt:se}).strict();function Nt(e,t){return vt.safeParse(Et(e,t))}function Et(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,r=new Set(Object.keys(vt.shape)),o={};for(const[e,i]of Object.entries(n))r.has(e)?o[e]=i:t?.({instanceId:"string"==typeof n.id?n.id:void 0,field:e});if(n.capabilities&&"object"==typeof n.capabilities&&!Array.isArray(n.capabilities)){const e={...n.capabilities};Object.prototype.hasOwnProperty.call(e,"apps")&&(t?.({instanceId:"string"==typeof n.id?n.id:void 0,field:"capabilities.apps"}),delete e.apps),o.capabilities=e}return o.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{const t=kt(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:kt(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>kt(e,["code","message"])):t.issues},r=be.safeParse(n);return r.success?r.data:void 0}(n.appInventory),o.apps=kt(n.apps,["runningCount","problemCount","updatedAt","revision"]),o.config=kt(n.config,["autoImportAgentConfigs"]),o.modelSelection=kt(n.modelSelection,["codexModelHash","claudeModelHash"]),o.imageSnapshot=function(e,t,n,r){if(!e||"object"!=typeof e||Array.isArray(e))return e;const o=e,i=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=("string"==typeof t.id&&t.id,"string"==typeof t.image?t.image:void 0);Object.prototype.hasOwnProperty.call(t,"registry");let r="string"==typeof t.reference?t.reference:n;if(r)try{r=me(r)}catch{if(!r.includes("@")&&r.lastIndexOf(":")<=r.lastIndexOf("/"))try{r=me(`${r}:latest`)}catch{}}return{id:t.id,name:t.name,reference:r,pullPolicy:t.pullPolicy??"if-not-present",capabilities:t.capabilities,optionalApps:t.optionalApps,defaultEnv:t.defaultEnv,labels:t.labels,createdAt:t.createdAt,updatedAt:t.updatedAt}}(o),{reference:s,...a}=i,c=o.requestedReference??s;return void 0===o.image&&void 0===o.reference||n?.({instanceId:r,field:"imageSnapshot.reference"}),{...a,id:o.id??t,requestedReference:c,resolvedDigest:o.resolvedDigest,resolvedReference:o.resolvedReference}}(n.imageSnapshot,n.imageId,t,"string"==typeof n.id?n.id:void 0),o.imageProvisioning=kt(n.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),o.runtime=kt(n.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),o.target=kt(n.target??n.endpoints,["strategy","web","api","status"]),o.access=kt(n.access,["strategy","web","api","ws","status"]),o}function kt(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e;return Object.fromEntries(t.filter(e=>Object.prototype.hasOwnProperty.call(n,e)).map(e=>[e,n[e]]))}const _t=u.z.object({instanceId:ie.optional(),name:u.z.string().trim().min(1).max(160),projectId:ie.optional(),source:Ve.optional(),nodeId:ie.optional(),runtimeId:ie.optional(),imageId:ie.optional(),instanceVersion:u.z.string().trim().max(80).optional(),protocolVersion:oe,build:Pe.optional(),controlMode:u.z.enum(["standalone","controlled"]).default("controlled"),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),appInventory:be.optional(),target:zt.default({strategy:"direct-port",status:"unknown"}),workspace:St.default({status:"unknown"}),registrationToken:u.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==re||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),Ot=u.z.object({status:vt.shape.status.optional(),health:vt.shape.health.optional(),protocolVersion:oe,build:Pe.optional(),capabilities:vt.shape.capabilities.optional(),appInventory:be.optional(),apps:vt.shape.apps.optional(),aiSessions:vt.shape.aiSessions.optional(),triggers:vt.shape.triggers.optional(),workspace:St.optional(),target:zt.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==re||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})}),xt=u.z.enum(["web","telegram","wechat","dingding"]);function Tt(e){return e.readyState===(e.OPEN??1)}function Ct(e,t,n){e.close(function(e){if("number"==typeof e&&Number.isInteger(e))return e>=3e3&&e<=4999||e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e?e:void 0}(t),function(e){const t=Buffer.isBuffer(e)?e.toString("utf8"):"string"==typeof e?e:"";if(Buffer.byteLength(t,"utf8")<=123)return t;let n="";for(const e of t){const t=`${n}${e}`;if(Buffer.byteLength(t,"utf8")>123)break;n=t}return n}(n)||void 0)}function Rt(e,t){e.send(t.data,{binary:t.isBinary})}function Dt(e,t){if(Tt(e))for(const n of t.splice(0))Rt(e,n)}function jt(e,t,n={}){return new Promise((r,o)=>{const i=s.spawn(e,t,{stdio:["ignore","pipe","pipe"]}),a=[],c=[];let d=!1;const l=n.timeoutMs?setTimeout(()=>{d=!0,i.kill("SIGKILL")},n.timeoutMs):void 0;l?.unref?.(),i.stdout.on("data",e=>a.push(Buffer.from(e))),i.stderr.on("data",e=>c.push(Buffer.from(e))),i.on("error",e=>{l&&clearTimeout(l),o(e)}),i.on("close",t=>{l&&clearTimeout(l);const i={stdout:Buffer.concat(a).toString("utf8").trim(),stderr:Buffer.concat(c).toString("utf8").trim()};if(d){const t=new Error(`${e} timed out after ${n.timeoutMs}ms`);return Object.assign(t,{statusCode:504,code:"RUNTIME_COMMAND_TIMEOUT",details:i}),void o(t)}if(0===t)return void r(i);const s=new Error(i.stderr||`${e} exited with code ${t}`);Object.assign(s,{statusCode:502,code:"RUNTIME_EXECUTOR_FAILED",details:i}),o(s)})})}u.z.object({id:ie,channel:xt,name:u.z.string().trim().min(1).max(160),enabled:u.z.boolean().default(!1),token:u.z.string().trim().max(4096).optional(),defaultChatId:u.z.string().trim().max(240).optional(),allowedUserIds:u.z.array(u.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:u.z.number().int().positive().max(6e4).default(3e3),settings:u.z.record(u.z.string(),u.z.unknown()).default({}),createdAt:se,updatedAt:se}).strict(),u.z.object({id:ie,channel:xt,bridgeId:ie.optional(),chatSessionId:u.z.string().trim().min(1).max(240),userId:u.z.string().trim().max(240).optional(),activeProjectId:ie.optional(),activeInstanceId:ie.optional(),activeAiSessionId:u.z.string().trim().min(1).max(120).optional(),lastUsedAt:se,createdAt:se,updatedAt:se}).strict(),u.z.object({source:u.z.object({type:u.z.literal("chat-gateway").default("chat-gateway"),channel:xt,bridgeId:ie.optional(),chatSessionId:u.z.string().trim().min(1).max(240),userId:u.z.string().trim().max(240).optional()}).strict(),message:u.z.object({text:u.z.string().trim().max(2e4).default(""),attachments:u.z.array(_).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+t.size,0)>y&&t.addIssue({code:u.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),target:u.z.object({instanceId:ie,aiSessionId:u.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),u.z.object({id:ie,instanceId:ie,projectId:ie.optional(),aiSessionId:u.z.string().trim().min(1).max(120),providerSessionId:u.z.string().trim().max(240).optional(),result:u.z.string().default(""),timeoutMs:u.z.number().positive().optional(),source:u.z.string().trim().max(120).optional(),kind:u.z.enum(["task","approval"]).default("task"),status:u.z.enum(["pending","unreachable"]).default("pending"),error:u.z.string().trim().max(2048).optional(),lastSeenAt:se}).strict();class Pt{inFlight=new Map;runCommand;constructor(e=jt){this.runCommand=e}ensure(e,t){const n=me(e),r=this.inFlight.get(n);if(r)return t&&(r.listeners.add(t),r.phase&&t(r.phase)),r.promise;const o=new Set;t&&o.add(t);const i={promise:Promise.resolve(void 0),listeners:o};return i.promise=this.ensureOnce(n,e=>{i.phase=e,o.forEach(t=>t(e))}).finally(()=>this.inFlight.delete(n)),this.inFlight.set(n,i),i.promise}async ensureOnce(e,t){t("checking-image");let n=await this.inspect(e).catch(()=>{}),r=!1;n||(t("pulling-image"),await this.runCommand("docker",["pull",e]),r=!0,n=await this.inspect(e)),t("resolving-image");const o=(i=n,Array.isArray(i.RepoDigests)?i.RepoDigests.filter(e=>"string"==typeof e):[]).find(e=>/@sha256:[a-fA-F0-9]{64}$/.test(e));var i;const s=o?.split("@").at(-1)?.toLowerCase();return{requestedReference:e,...s?{resolvedDigest:s}:{},resolvedReference:o||e,pulled:r}}async inspect(e){const t=await this.runCommand("docker",["image","inspect",e,"--format","{{json .}}"]);return JSON.parse(t.stdout.trim()||"{}")}}class Mt{runCommand;publishHost;images;constructor(e=jt,t={}){this.runCommand=e,this.images=t.imageService||new Pt(e),this.publishHost=t.publishHost||"127.0.0.1"}async start(e){Lt(e);const t=e.instance.runtime.containerName||Ft(e.instance.id);if(await this.runCommand("docker",["start",t]).catch(()=>{}))return this.runningResult(e,t,e.instance.runtime.containerId);await this.images.ensure(e.image.resolvedReference||e.image.requestedReference);const n=await this.runCommand("docker",function(e,t,n={}){const r=n.publishHost||"127.0.0.1",o=e.node?.id||"node_unset",i=e.runtime?.id||"runtime_local_docker",s=["run","-d","--name",t,"--label",`task-handoff.instance-id=${e.instance.id}`,"--label",`task-handoff.project-id=${e.project.id}`,"--label",`task-handoff.node-id=${o}`,"--label",`task-handoff.runtime-id=${i}`,"--label",`task-handoff.image-id=${e.image.id}`,"--shm-size","1gb","--security-opt","seccomp=unconfined","--network","bridge","--add-host","host.docker.internal:host-gateway","--tmpfs","/tmp:rw,mode=1777","-v",`${t}-data:/data`,"-v",`${t}-agent-home:/home/agent`,"-p",`${r}::8080`,"-e","TASK_HANDOFF_CONTROL_MODE=controlled","-e",`TASK_HANDOFF_NODE_AGENT_URL=${e.nodeAgentUrl||e.node?.endpoint||""}`,"-e",`TASK_HANDOFF_INSTANCE_ID=${e.instance.id}`,"-e",`TASK_HANDOFF_INSTANCE_NAME=${e.instance.name}`,"-e",`TASK_HANDOFF_REGISTRATION_TOKEN=${e.instance.registrationToken||""}`,"-e",`TASK_HANDOFF_PROJECT_ID=${e.project.id}`,"-e",`TASK_HANDOFF_NODE_ID=${o}`,"-e",`TASK_HANDOFF_RUNTIME_ID=${i}`,"-e",`TASK_HANDOFF_IMAGE_ID=${e.image.id}`,"-e","TASK_HANDOFF_CHAT_BRIDGES=none","-e",`TASK_HANDOFF_WORKSPACE=${e.project.workspacePolicy.path||"/workspace"}`,"-e",`TASK_HANDOFF_WORKSPACE_MODE=${e.project.workspacePolicy.mode}`];"local-folder"===e.project.source.type?s.push("-v",`${e.project.source.path}:${e.project.workspacePolicy.path||"/workspace"}:${e.project.workspacePolicy.readOnly?"ro":"rw"}`):(s.push("-e",`TASK_HANDOFF_PROJECT_SOURCE=${JSON.stringify(e.project.source)}`),s.push("-e",`TASK_HANDOFF_WORKSPACE_POLICY=${JSON.stringify(e.project.workspacePolicy)}`),s.push("-e",`TASK_HANDOFF_GIT_URL=${e.project.source.url}`),e.project.source.ref?.commit?s.push("-e",`TASK_HANDOFF_GIT_COMMIT=${e.project.source.ref.commit}`):e.project.source.ref?.name&&s.push("-e",`TASK_HANDOFF_GIT_REF=${e.project.source.ref.name}`),e.project.source.clone?.depth&&s.push("-e",`TASK_HANDOFF_GIT_DEPTH=${e.project.source.clone.depth}`),s.push("-e","TASK_HANDOFF_GIT_SUBMODULES="+(e.project.source.clone?.submodules?"true":"false")),s.push("-e","TASK_HANDOFF_GIT_LFS="+(e.project.source.clone?.lfs?"true":"false")),e.project.source.clone?.subdirectory&&s.push("-e",`TASK_HANDOFF_WORKSPACE_SUBDIRECTORY=${e.project.source.clone.subdirectory}`));for(const[t,n]of Object.entries(e.image.defaultEnv))s.push("-e",`${t}=${n}`);for(const[t,n]of Object.entries(e.modelEnv||{}))s.push("-e",`${t}=${n}`);return s.push(e.image.resolvedReference||e.image.requestedReference),s}(e,t,{publishHost:this.publishHost}));return this.runningResult(e,t,n.stdout||void 0)}async runningResult(e,t,n){const r=await this.containerPort(t,"8080/tcp"),o=r?`http://127.0.0.1:${r}`:void 0,i=e.node?.id||"node_unset",s=e.runtime?.id||"runtime_local_docker";return{status:"registering",health:"unknown",connectionStatus:o?"online":"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:o?"reachable":"unknown",...o?{web:o,api:`${o}/api`}:{}},workspace:{mode:e.project.workspacePolicy.mode,status:"pending",path:e.project.workspacePolicy.path},runtime:{...e.instance.runtime,kind:"docker",containerName:t,...n?{containerId:n}:{},labels:{...e.instance.runtime.labels,"task-handoff.instance-id":e.instance.id,"task-handoff.project-id":e.project.id,"task-handoff.node-id":i,"task-handoff.runtime-id":s,"task-handoff.image-id":e.image.id}}}}async stop(e){return await this.runCommand("docker",["stop",e.instance.runtime.containerName||Ft(e.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async restart(e){Lt(e);const t=e.instance.runtime.containerName||Ft(e.instance.id);return await this.runCommand("docker",["restart",t]),this.runningResult(e,t,e.instance.runtime.containerId)}async delete(e){return await this.runCommand("docker",["rm","-f",e.instance.runtime.containerName||Ft(e.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async containerPort(e,t){const n=(await this.runCommand("docker",["port",e,t]).catch(()=>({stdout:"",stderr:""}))).stdout.split(/\r?\n/).find(Boolean),r=n?.match(/:(\d+)$/);return r?.[1]}}async function $t(e=jt){return async function(e=jt){return(await e("docker",["images","--digests","--format","{{json .}}"])).stdout.split(/\r?\n/).map(e=>e.trim()).filter(Boolean).map(e=>{const t=JSON.parse(e),n=t.Repository||"<none>",r=t.Tag||"<none>",o=t.ID||t.IDShort||"",i="<none>"!==n&&"<none>"!==r?`${n}:${r}`:o,s="<none>"!==n&&t.Digest&&"<none>"!==t.Digest?[`${n}@${t.Digest}`]:[];return{repository:n,tag:r,id:o,createdSince:t.CreatedSince,size:t.Size,reference:i,repoDigests:s}}).filter(e=>Boolean(e.reference))}(e)}function Ft(e){return`task-handoff-${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`}function Lt(e){if("local-folder"===e.project.source.type&&e.project.source.ownerNodeId&&e.project.source.ownerNodeId!==e.node.id){const e=new Error("Local folder projects can only start on the node that owns the folder.");throw Object.assign(e,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_OWNER_NODE"}),e}if(!e.image.requestedReference?.trim()){const e=new Error("Image profile must reference a registered image.");throw Object.assign(e,{statusCode:400,code:"IMAGE_PROFILE_INVALID"}),e}}const Ut=u.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),Ht=u.z.object({type:u.z.enum(["app-session","provider-session","adapter-key"]),id:u.z.string().trim().min(1).max(240),agent:u.z.string().trim().min(1).max(80).optional(),adapter:u.z.string().trim().min(1).max(80).optional(),key:u.z.string().trim().min(1).max(240).optional()}).strict(),qt=u.z.record(u.z.string(),u.z.unknown()).and(u.z.object({id:u.z.string().trim().min(1).max(160),appId:u.z.string().trim().min(1).max(160).optional(),title:u.z.string().trim().max(240).optional(),kind:u.z.string().trim().max(80).optional(),status:Ut.default("unknown"),bindings:u.z.array(Ht).max(40).default([]),workspace:u.z.object({cwd:u.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:u.z.string().datetime().optional(),updatedAt:u.z.string().datetime().optional()})),Kt=u.z.object({runningCount:u.z.number().int().min(0).default(0),problemCount:u.z.number().int().min(0).default(0),sessions:u.z.array(qt).default([]),updatedAt:u.z.string().datetime()}).strict();u.z.object({streamId:u.z.string().trim().min(1).max(240),revision:u.z.number().int().min(0),lastEventAt:u.z.string().datetime(),snapshot:Kt}).strict(),Kt.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:u.z.number().int().min(0).optional()});const Bt=u.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),Gt=u.z.object({streamId:u.z.string().trim().min(1).max(240),instanceId:u.z.string().trim().min(1).max(160),nodeId:u.z.string().trim().min(1).max(160).optional(),revision:u.z.number().int().min(0),previousRevision:u.z.number().int().min(0).optional(),traceId:u.z.string().trim().min(1).max(160),generatedAt:u.z.string().datetime(),reason:Bt}).strict(),Vt=u.z.object({meta:Gt,snapshot:Kt}).strict(),Jt=u.z.object({meta:Gt,session:qt}).strict(),Wt=u.z.object({meta:Gt,sessionId:u.z.string().trim().min(1).max(160),tombstone:qt.optional(),expiresAt:u.z.string().datetime().optional()}).strict();function Yt(e){return e.startsWith("ai-session.")||e.startsWith("ai.session.")?"ai.sessions":e.startsWith("app-session.")?"app.sessions":e.startsWith("app.management")?"apps":e.startsWith("trigger.")?"triggers":e.startsWith("instance.")?"instances":e.startsWith("node.")?"nodes":e.startsWith("project.")?"projects":e.startsWith("model.")?"models":e.startsWith("image.")?"images":"system"}u.z.object({streamId:u.z.string().trim().min(1).max(240),instanceId:u.z.string().trim().min(1).max(160),sinceRevision:u.z.number().int().min(0),latestRevision:u.z.number().int().min(0),earliestRetainedRevision:u.z.number().int().min(0),syncRequired:u.z.boolean().default(!1),events:u.z.array(u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("app-session.snapshot"),payload:Vt}).strict(),u.z.object({type:u.z.literal("app-session.patch"),payload:Jt}).strict(),u.z.object({type:u.z.literal("app-session.removed"),payload:Wt}).strict()])).default([])}).strict();const Xt=u.z.enum(["ai.sessions","app.sessions"]),Qt=u.z.object({topic:Xt,instanceId:u.z.string().trim().min(1).max(160),streamId:u.z.string().trim().min(1).max(240),latestRevision:u.z.number().int().min(0),earliestRetainedRevision:u.z.number().int().min(0)}).strict(),Zt=u.z.object({protocolVersion:u.z.literal(1),streams:u.z.array(Qt)}).strict();class en{attempt=0;timer;get pending(){return Boolean(this.timer)}get attempts(){return this.attempt}schedule(e,t={}){if(this.timer)return;const n=function(e,t=Math.random){const n=Math.min(3e4,1e3*2**Math.max(0,e));return Math.min(3e4,Math.round(n*(.75+.5*t())))}(this.attempt,t.random);this.attempt+=1;const r=t.setTimeoutFn||setTimeout;return this.timer=r(()=>{this.timer=void 0,e()},n),{attempt:this.attempt,delay:n}}reset(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0,this.attempt=0}cancel(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0}}class tn{sockets=new Map;socketUrls=new Map;retries=new Map;outputs=new Set;state;token;logger;safetyIntervalMs;lastAiSessionEventByInstance=new Map;lastAppSessionEventByInstance=new Map;timer;reconnectAttempts=0;safetyReconciliations=0;localSequence=0;createSocket;setIntervalFn;clearIntervalFn;constructor(e,t,n={}){var r;this.state=e,this.token=t,this.logger=n.logger,this.safetyIntervalMs=(r=n.safetyIntervalMs,Math.min(6e4,Math.max(3e4,r??45e3))),this.createSocket=n.createSocket||((e,t)=>new l(e,t)),this.setIntervalFn=n.setIntervalFn||setInterval,this.clearIntervalFn=n.clearIntervalFn||clearInterval}start(){this.sync(),this.timer=this.setIntervalFn(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(this.clearIntervalFn(this.timer),this.timer=void 0);for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();this.retries.clear(),this.outputs.clear()}addOutput(e){return this.outputs.add(e),e.on("close",()=>this.outputs.delete(e)),e.on("error",()=>this.outputs.delete(e)),this.syncNow(),()=>{this.outputs.delete(e)}}publish(e,t,n={}){const r=++this.localSequence,o={v:1,id:`node_evt_${Date.now().toString(36)}_${r.toString(36)}`,seq:r,type:e,topic:Yt(e),createdAt:(new Date).toISOString(),payload:t,scope:n},i=JSON.stringify({type:"node-agent.event.forwarded",event:o});for(const e of this.outputs)e.readyState===l.OPEN&&e.send(i)}syncNow(){this.sync()}diagnostics(){return{reconnectAttempts:this.reconnectAttempts,safetyReconciliations:this.safetyReconciliations,activeConnections:this.sockets.size,pendingRetries:[...this.retries.values()].filter(e=>e.timer.pending).length,safetyIntervalMs:this.safetyIntervalMs}}sync(){if(!this.outputs.size){for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();return void this.retries.clear()}const e=new Set;for(const t of this.state.listInstances()){const n=rn(t);if(!n)continue;e.add(t.id);const r=this.retries.get(t.id);r?.timer.pending&&r.url!==n&&(r.timer.cancel(),this.retries.delete(t.id)),this.socketUrls.get(t.id)!==n&&this.sockets.has(t.id)&&(this.sockets.get(t.id)?.close(),this.sockets.delete(t.id)),this.sockets.has(t.id)||this.retries.get(t.id)?.timer.pending||this.connect(t.id,n)}for(const[t,n]of this.sockets)if(!e.has(t)){n.close(),this.sockets.delete(t),this.socketUrls.delete(t);const e=this.retries.get(t);e?.timer.cancel(),this.retries.delete(t)}for(const[t,n]of this.retries)e.has(t)||(n.timer.cancel(),this.retries.delete(t),this.socketUrls.delete(t))}connect(e,t){const n=this.token?{authorization:`Bearer ${this.token}`}:void 0,r=this.createSocket(t,{headers:n});this.sockets.set(e,r),this.socketUrls.set(e,t),r.on("open",()=>{const n=this.retries.get(e);n?.timer.reset(),this.retries.set(e,{timer:n?.timer||new en,url:t}),r.readyState===l.OPEN&&(r.send(JSON.stringify({v:1,type:"subscribe",topics:[I,"app.sessions","apps","instances"]})),this.logger?.info?.({instanceId:e,url:t},"ai-session.event.forward.connect"),this.logger?.info?.({instanceId:e,url:t},"app-session.event.forward.connect"))}),r.on("message",t=>{const n=function(e,t){try{const n=JSON.parse(String(e));if(!n||"object"!=typeof n||Array.isArray(n))return;const r=n,o="string"==typeof r.type?r.type:"";if(!o||"events.connected"===o)return;const i=r.scope&&"object"==typeof r.scope&&!Array.isArray(r.scope)?r.scope:{};return{v:1,id:"string"==typeof r.id?r.id:`evt_${Date.now().toString(36)}`,seq:Number(r.seq)||0,type:o,topic:"string"==typeof r.topic?r.topic:Yt(o),createdAt:"string"==typeof r.createdAt?r.createdAt:(new Date).toISOString(),payload:"payload"in r?r.payload:{},scope:{...i,instanceId:t}}}catch{return}}(t,e);if(!n)return;if("streams.hello"===n.type){const t=Zt.safeParse(n.payload);if(!t.success)return this.logger?.warn?.({instanceId:e,issues:t.error.issues},"session-stream.handshake.invalid"),void r.close(1002,"Incompatible session stream handshake.");const o=JSON.stringify({type:"node-agent.streams.hello",instanceId:e,payload:t.data});for(const e of this.outputs)e.readyState===l.OPEN&&e.send(o);return}this.recordAiSessionEvent(e,n),this.recordAppSessionEvent(e,n);const o=JSON.stringify({type:"node-agent.event.forwarded",event:n});for(const e of this.outputs)e.readyState===l.OPEN&&e.send(o)}),r.on("close",()=>{this.sockets.get(e)===r&&(this.sockets.delete(e),this.socketUrls.delete(e),this.scheduleReconnect(e))}),r.on("error",()=>{this.logger?.warn?.({instanceId:e,url:t},"ai-session.event.forward.error"),this.logger?.warn?.({instanceId:e,url:t},"app-session.event.forward.error"),r.close()})}scheduleReconnect(e){const t=this.state.listInstances().find(t=>t.id===e),n=t&&rn(t);if(!n||!this.outputs.size)return;const r=this.retries.get(e)??{timer:new en};if(r.timer.pending)return;r.url=n;const o=r.timer.schedule(()=>{this.sockets.has(e)||this.connect(e,n)});o&&(this.reconnectAttempts+=1,this.logger?.info?.({instanceId:e,url:n,attempt:o.attempt,delay:o.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.connection.reconnect-scheduled")),this.retries.set(e,r)}recordAiSessionEvent(e,t){if(t.topic!==I)return;if("ai-session.message-delta"===t.type)return;const n=nn(t.payload),r=this.lastAiSessionEventByInstance.get(e);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:e,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"ai-session.event.forward.gap"),this.lastAiSessionEventByInstance.set(e,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:e,traceId:n.traceId,eventType:t.type,streamId:n.streamId,revision:n.revision},"ai-session.event.forward.message")}recordAppSessionEvent(e,t){if("app.sessions"!==t.topic)return;const n=nn(t.payload),r=this.lastAppSessionEventByInstance.get(e);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:e,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"app-session.event.forward.gap"),this.lastAppSessionEventByInstance.set(e,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:e,traceId:n.traceId,eventType:t.type,streamId:n.streamId,revision:n.revision},"app-session.event.forward.message")}}function nn(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{},n=t.meta&&"object"==typeof t.meta&&!Array.isArray(t.meta)?t.meta:{};return{streamId:"string"==typeof n.streamId?n.streamId:void 0,revision:"number"==typeof n.revision?n.revision:void 0,traceId:"string"==typeof n.traceId?n.traceId:void 0}}function rn(e){const t=e.target?.api||e.target?.web;if(t)try{const e=new URL("/api/events",t);return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}catch{return}}const on=new Set(["provisioning","starting","registering","registered","running","unhealthy"]),sn=JSON.stringify({id:"{{.ID}}",name:"{{.Name}}",cpu:"{{.CPUPerc}}",memory:"{{.MemUsage}}",memoryPercent:"{{.MemPerc}}",network:"{{.NetIO}}",blockIo:"{{.BlockIO}}",pids:"{{.PIDs}}"});class an{snapshots=new Map;runCommand;targets;publish;intervalMs;timer;inFlight;pendingTargets=new Map;constructor(e,t,n,r=3e3){this.runCommand=e,this.targets=t,this.publish=n,this.intervalMs=r}start(){this.collect().catch(()=>{}),this.timer=setInterval(()=>{this.collect().catch(()=>{})},this.intervalMs)}stop(){this.timer&&clearInterval(this.timer),this.timer=void 0}async snapshot(e){const t=this.targets().find(t=>t.id===e);if(t)return on.has(t.status)?(await this.collect([t]),this.snapshots.get(e)):pn(t.id)}async collect(e){const t=e||this.targets();if(!e){const e=new Set(t.map(e=>e.id));for(const t of this.snapshots.keys())e.has(t)||this.snapshots.delete(t)}for(const e of t)this.pendingTargets.set(e.id,e);this.inFlight||(this.inFlight=this.drain().finally(()=>{this.inFlight=void 0})),await this.inFlight,this.pendingTargets.size&&await this.collect([])}async drain(){for(;this.pendingTargets.size;){const e=[...this.pendingTargets.values()];this.pendingTargets.clear(),await this.collectOnce(e)}}async collectOnce(e){const t=e.filter(e=>on.has(e.status)&&(e.runtime.containerId||e.runtime.containerName));for(const t of e.filter(e=>on.has(e.status)&&!e.runtime.containerId&&!e.runtime.containerName))this.update(mn(t.id));for(const t of e.filter(e=>!on.has(e.status)))this.update(pn(t.id));if(!t.length)return;const n=t.map(e=>e.runtime.containerId||e.runtime.containerName),r=(new Date).toISOString();try{const e=await this.runCommand("docker",["stats","--no-stream","--format",sn,...n],{timeoutMs:5e3});this.applyDockerOutput(t,e.stdout,r)}catch(e){if(1===t.length)return void this.update(gn(t[0].id,r,e instanceof Error?e.message:String(e)));await Promise.all(t.map(async e=>{const t=e.runtime.containerId||e.runtime.containerName;try{const n=await this.runCommand("docker",["stats","--no-stream","--format",sn,t],{timeoutMs:5e3});this.applyDockerOutput([e],n.stdout,r)}catch(t){this.update(gn(e.id,r,t instanceof Error?t.message:String(t)))}}))}}applyDockerOutput(e,t,n){const r=function(e,t=(new Date).toISOString()){const n=new Map;for(const r of e.split(/\r?\n/).filter(Boolean)){let e;try{e=JSON.parse(r)}catch{continue}const o=un(e.id),i=un(e.name),s=o||i;if(!s)continue;const a=cn(un(e.memory)),c=cn(un(e.network)),d=cn(un(e.blockIo)),l=ht.parse({instanceId:s,runtimeKind:"docker",state:"available",sampledAt:t,cpu:dn(e.cpu,"usagePercent"),memory:a?{usageBytes:a[0],...void 0!==a[1]?{limitBytes:a[1]}:{},...dn(e.memoryPercent,"usagePercent")}:void 0,network:c?{rxBytes:c[0],txBytes:c[1]||0}:void 0,blockIo:d?{readBytes:d[0],writeBytes:d[1]||0}:void 0,pids:ln(e.pids)});o&&n.set(o,l),i&&n.set(i,l)}return n}(t,n);for(const t of e){const e=[t.runtime.containerId,t.runtime.containerName].filter(Boolean),o=[...r.entries()].find(([t])=>e.some(e=>e===t||e.startsWith(t)||t.startsWith(e)))?.[1];this.update(o?{...o,instanceId:t.id}:gn(t.id,n,"Container metrics were not returned by Docker."))}}update(e){const t=ht.parse(e);this.snapshots.set(t.instanceId,t),this.publish?.(t)}}function cn(e){if(!e)return;const[t,n]=e.split("/").map(e=>function(e){const t=e.trim().match(/^([0-9]+(?:\.[0-9]+)?)\s*([kmgtpe]?i?b)$/i);if(!t)return;const n=Number(t[1]),r=t[2].toLowerCase(),o=r.includes("i"),i="b"===r[0]?"":r[0],s=i?"kmgtpe".indexOf(i)+1:0;return Math.round(n*(o?1024:1e3)**s)}(e||""));return void 0===t?void 0:[t,n]}function dn(e,t){const n=Number(un(e).replace(/%$/,""));return Number.isFinite(n)&&n>=0?{[t]:n}:void 0}function ln(e){const t=Number(un(e));return Number.isFinite(t)&&t>=0?t:void 0}function un(e){return"string"==typeof e||"number"==typeof e?String(e).trim():""}function pn(e){return{instanceId:e,runtimeKind:"docker",state:"stopped",sampledAt:(new Date).toISOString()}}function mn(e){return{instanceId:e,runtimeKind:"docker",state:"pending",sampledAt:(new Date).toISOString()}}function gn(e,t,n){return{instanceId:e,runtimeKind:"docker",state:"unavailable",sampledAt:t,error:n.slice(0,2048)}}function hn(e){try{return t.statSync(e).isDirectory()}catch{return!1}}function fn(e,r){let o;try{o=t.statSync(e)}catch{return}if(!o.isDirectory())return;const i=n.resolve(e),s=n.basename(i)||i;let a=[];try{a=r>0?t.readdirSync(i,{withFileTypes:!0}):[]}catch{a=[]}const c=r>0?a.filter(e=>e.isDirectory()||e.isSymbolicLink()&&hn(n.join(i,e.name))).sort((e,t)=>e.name.localeCompare(t.name)).slice(0,80).flatMap(e=>{const t=fn(n.join(i,e.name),r-1);return t?[t]:[]}):[];return{name:s,path:i,children:c}}function yn(e){return(e.path?[e.path]:function(){if("win32"!==process.platform)return[n.parse(n.resolve(process.cwd())).root];const e=n.parse(n.resolve(process.cwd())).root,t=Array.from({length:26},(e,t)=>`${String.fromCharCode(65+t)}:\\`);return[...new Set([e,...t])].filter(hn)}()).flatMap(t=>{const n=fn(t,e.depth);return n?[n]:[]})}const In=u.z.object({path:u.z.string().trim().min(1).max(2048),method:u.z.string().trim().min(1).max(20).default("GET"),headers:u.z.record(u.z.string(),u.z.string()).default({}),body:u.z.string().optional(),bodyBase64:u.z.string().optional()}).strict(),bn=u.z.object({id:u.z.string().trim().min(1).max(120).optional(),name:u.z.string().trim().min(1).max(160),path:u.z.string().trim().min(1).max(4096),defaultImageId:u.z.string().trim().min(1).max(120).optional(),labels:u.z.record(u.z.string(),u.z.string()).default({})}).strict(),An=u.z.object({path:u.z.string().trim().min(1).max(4096).optional(),depth:u.z.coerce.number().int().min(0).max(1).default(0)}).strict(),zn=u.z.object({id:u.z.string().trim().min(1).max(120).optional(),name:u.z.string().trim().min(1).max(160).optional(),runtimeId:u.z.string().trim().min(1).max(120),imageId:u.z.string().trim().min(1).max(120).optional(),projectId:u.z.string().trim().min(1).max(120).optional(),image:lt.optional(),source:Ve,sourceSnapshot:u.z.record(u.z.string(),u.z.unknown()).default({}),config:vt.shape.config.optional(),modelSelection:vt.shape.modelSelection}).strict(),wn=u.z.object({name:u.z.string().trim().min(1).max(160).optional(),config:vt.shape.config.optional(),modelSelection:vt.shape.modelSelection.optional()}).strict(),Sn=u.z.object({id:u.z.string().trim().min(1).max(120).optional(),name:u.z.string().trim().min(1).max(160),type:u.z.enum(["docker","kubernetes","local"]),status:u.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:u.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).optional(),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),labels:u.z.record(u.z.string(),u.z.string()).default({})}).strict(),vn=Sn.omit({id:!0}).partial().strict();function Nn(e=function(){return process.env.TASK_HANDOFF_NODE_AGENT_DATA_DIR||n.join(m.homedir(),".config","task-handoff","node-agent")}()){const t=n.resolve(e);return{dataDir:t,identityPath:n.join(t,"identity.json"),settingsPath:n.join(t,"runtime-settings.json"),localFoldersDir:n.join(t,"local-folders"),nodeRuntimesDir:n.join(t,"node-runtimes"),controlledInstancesDir:n.join(t,"controlled-instances"),nodeModelsDir:n.join(t,"models"),modelAssignmentsDir:n.join(t,"model-assignments"),modelEnvironmentsDir:n.join(t,"model-environments"),updatesDir:n.join(t,"updates"),logsDir:n.join(t,"logs")}}function En(e){t.mkdirSync(e,{recursive:!0})}function kn(e,t){En(n.dirname(e)),g.sync(e,`${JSON.stringify(t,null,2)}\n`,{encoding:"utf8"})}function _n(e,t){console.warn(JSON.stringify({message:e,...t}))}function On(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function xn(e,n){const r=n.logger||_n;let o;try{o=JSON.parse(t.readFileSync(e,"utf8"))}catch(t){return void r("stored JSON could not be read",{filePath:e,...On(t)})}if(!n.schema)return o;const i=n.sanitize?n.sanitize(o):o,s=n.schema.safeParse(i);if(s.success)return s.data;const a="strip"in n.schema&&"function"==typeof n.schema.strip?n.schema.strip().safeParse(i):void 0;if(a?.success)return r("unknown stored fields were ignored",{filePath:e,issues:s.error.issues}),a.data;r("stored record failed validation and was isolated",{filePath:e,issues:s.error.issues})}function Tn(e){return`${e}_${p.randomUUID().replace(/-/g,"").slice(0,20)}`}function Cn(){return p.randomBytes(24).toString("base64url")}class Rn{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){En(this.directory)}filePath(e){return n.join(this.directory,`${e}.json`)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=xn(n.join(this.directory,e),this.options);return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?xn(n,this.options):void 0}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,n=this.options.schema?this.options.schema.parse(t):t;return kn(this.filePath(n.id),n),n}patch(e,t){const n=this.get(e);if(!n)return;const r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:(new Date).toISOString()};return this.put(r)}delete(e){const n=this.filePath(e);return!!t.existsSync(n)&&(t.unlinkSync(n),!0)}}class Dn{filePathValue;defaults;options;constructor(e,t,n={}){this.filePathValue=e,this.defaults=t,this.options=n}init(){En(n.dirname(this.filePathValue)),t.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),t.existsSync(this.filePathValue)?xn(this.filePathValue,this.options)??this.defaults():this.defaults()}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,n=this.options.schema?this.options.schema.parse(t):t;return kn(this.filePathValue,n),n}}class jn extends Error{code;lockPath;owner;constructor(e,t,n={}){const r=t?.pid?` pid ${t.pid}`:"";super(`${n.label||"Process"} is already running${r}. Lock: ${e}`),this.name="ProcessSingletonError",this.code=n.code||"PROCESS_ALREADY_RUNNING",this.lockPath=e,this.owner=t}}function Pn(e){return n.join(e,"owner.json")}function Mn(e){return n.join(e,"initializing.json")}function $n(e){return n.join(e,"recovering.json")}function Fn(e){try{const n=JSON.parse(t.readFileSync(Pn(e),"utf8"));if(!Number.isInteger(n.pid)||Number(n.pid)<=0||"string"!=typeof n.hostname||!n.hostname||"string"!=typeof n.command||"string"!=typeof n.acquiredAt||!Number.isFinite(Date.parse(n.acquiredAt))||"string"!=typeof n.token||!n.token)return;return n}catch{return}}function Ln(e){if(!Number.isInteger(e)||Number(e)<=0)return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function Un(e,r){const o=n.join(e,`.owner-${r.token}.tmp`);t.writeFileSync(o,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const i=Hn(e);if(i?.token!==r.token)throw qn(o),new Error(`Process lock initialization was superseded: ${e}`);t.renameSync(o,Pn(e)),qn(Mn(e))}function Hn(e){try{return JSON.parse(t.readFileSync(Mn(e),"utf8"))}catch{return}}function qn(e){try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function Kn(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function Bn(e){try{const n=Hn(e),r=Date.parse(n?.startedAt||"");return Number.isFinite(r)?Math.max(0,Date.now()-r):Math.max(0,Date.now()-t.statSync(e).mtimeMs)}catch{return 0}}function Gn(e,r){try{return t.writeFileSync($n(e),`${JSON.stringify({token:r,pid:process.pid,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(o){if("EEXIST"===o.code)return!!function(e){const r=$n(e),o=Vn(r),i=Date.parse(o?.startedAt||"");if(Ln(o?.pid)||Number.isFinite(i)&&Date.now()-i<2e3)return!1;const s=n.join(e,`.recovering-${p.randomUUID()}.stale`);try{t.renameSync(r,s)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const a=Vn(s);if(a?.token===o?.token)return qn(s),!0;try{t.linkSync(s,r)}catch(e){if("EEXIST"!==e.code)throw e}finally{qn(s)}return!1}(e)&&Gn(e,r);if("ENOENT"===o.code)return!1;throw o}}function Vn(e){try{return JSON.parse(t.readFileSync(e,"utf8"))}catch{return}}function Jn(e,t){return Vn($n(e))?.token===t}function Wn(e,t=process.platform){const r=n.resolve(e),o=p.createHash("sha256").update(r).digest("hex").slice(0,16);return"win32"===t?`\\\\.\\pipe\\task-handoff-node-agent-${o}`:n.join("/tmp",`task-handoff-node-agent-${process.getuid?.()??"user"}`,`${o}.sock`)}const Yn="TASK_HANDOFF_NODE_AGENT_V1";function Xn(e=""){return p.createHash("sha256").update(e).digest("hex")}function Qn(e,t){return p.createHmac("sha256",e).update(function(e){return[Yn,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function Zn(e){const t=e.timestamp||(new Date).toISOString(),n=e.nonce||p.randomBytes(18).toString("base64url"),r=Xn(e.body||""),o=Qn(e.secret,{keyId:e.keyId,method:e.method,pathWithQuery:e.pathWithQuery,timestamp:t,nonce:n,bodySha256:r});return{"x-taskhandoff-node-id":e.nodeId,"x-taskhandoff-key-id":e.keyId,"x-taskhandoff-timestamp":t,"x-taskhandoff-nonce":n,"x-taskhandoff-body-sha256":r,"x-taskhandoff-signature":o}}const er=new Set(["DELETE","GET","HEAD","PATCH","POST","PUT","OPTIONS"]);function tr(e){return"string"==typeof e&&er.has(e.toUpperCase())?e.toUpperCase():"GET"}function nr(){return(new Date).toISOString()}function rr(e,t){if(!e)return!0;const n=h.valid(e),r=h.valid(t);return n&&r?h.gt(r,n):e!==t}async function or(e,t,n){const r=ar(n),o=await e(ir(),["view",`${t}@${r}`,"version","--json"]).catch(e=>{if(!function(e){if(!e||"object"!=typeof e)return!1;const t=e,n=[t.message,t.details?.stdout,t.details?.stderr].filter(e=>"string"==typeof e).join("\n");return/\bE404\b/i.test(n)}(e))throw e});if(!o)return;const i=JSON.parse(o.stdout);if("string"!=typeof i)throw new Error(`npm did not resolve ${t}@${r} to one version.`);return i}function ir(){return process.env.TASK_HANDOFF_NPM_COMMAND||"npm"}function sr(e){return Le.parse({target:e.target,source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:e.currentVersion||"unavailable",updateAvailable:!1,reason:`No ${e.channel} release is currently published.`,checkedAt:nr()})}function ar(e){return"stable"===e?"latest":e}function cr(e){if(Array.isArray(e))return e.map(cr).find(Boolean);if(!e||"object"!=typeof e)return;const t=e;return"string"==typeof t.digest&&t.digest.startsWith("sha256:")?t.digest:"string"==typeof t.Digest&&t.Digest.startsWith("sha256:")?t.Digest:Object.values(t).map(cr).find(Boolean)}function dr(e){return{x64:"amd64",ia32:"386",arm:"arm",arm64:"arm64",ppc64:"ppc64le",s390x:"s390x",riscv64:"riscv64"}[e]||e}function lr(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n=t.Descriptor||t.descriptor||t,r=n.platform||n.Platform;return"string"==typeof r?.architecture?r.architecture:"string"==typeof r?.Architecture?r.Architecture:void 0}class ur{records;constructor(e){this.records=new Rn(e.updatesDir,{schema:Ue})}init(){this.records.init()}list(){return this.records.list().sort((e,t)=>t.createdAt.localeCompare(e.createdAt))}create(e,t){const n=nr();return this.records.put(Ue.parse({id:Tn("update"),nodeId:e,target:t.target,source:t.source,channel:t.channel,fromVersion:t.currentVersion,toVersion:t.availableVersion,artifactRef:t.artifactRef,status:"queued",createdAt:n,updatedAt:n}))}patch(e,t){const n=this.records.get(e);if(!n)throw Object.assign(new Error(`Update job ${e} was not found.`),{statusCode:404,code:"UPDATE_JOB_NOT_FOUND"});return this.records.put(Ue.parse({...n,...t,id:e,createdAt:n.createdAt,updatedAt:nr()}))}run(e,t){(async()=>{this.patch(e.id,{status:"updating",startedAt:nr(),error:void 0});try{await t(e),this.patch(e.id,{status:"succeeded",completedAt:nr()})}catch(t){this.patch(e.id,{status:"failed",error:t instanceof Error?t.message:String(t),completedAt:nr()})}})()}}const pr=u.z.record(u.z.string(),u.z.string()),mr=new Set(["OPENAI_API_KEY","OPENAI_BASE_URL","CODEX_MODEL","TASK_HANDOFF_CODEX_BASE_URL","TASK_HANDOFF_CODEX_MODEL","TASK_HANDOFF_CODEX_MODEL_ID","ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","CLAUDE_MODEL","TASK_HANDOFF_CLAUDE_MODEL","TASK_HANDOFF_CLAUDE_MODEL_ID"]);class gr{directory;constructor(e){this.directory=e}get(e){try{return pr.parse(JSON.parse(t.readFileSync(this.filePath(e),"utf8")))}catch(t){if(t&&"object"==typeof t&&"code"in t&&"ENOENT"===t.code)return{};throw new Error(`Stored model environment for instance ${e} is invalid: ${t instanceof Error?t.message:String(t)}`)}}has(e){return t.existsSync(this.filePath(e))}listInstanceIds(){return t.existsSync(this.directory)?t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).map(e=>e.slice(0,-5)).sort():[]}put(e,n){const r=pr.parse(n);t.mkdirSync(this.directory,{recursive:!0,mode:448});const o=this.filePath(e);return g.sync(o,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(o,384),r}delete(e){t.rmSync(this.filePath(e),{force:!0})}filePath(e){return n.join(this.directory,`${e}.json`)}}class hr{directory;nodeId;constructor(e,t){this.directory=e,this.nodeId=t}init(){t.mkdirSync(this.directory,{recursive:!0,mode:448}),t.chmodSync(this.directory,448)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=this.read(n.join(this.directory,e));return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?this.read(n):void 0}put(e){const n=Qe.parse(e);this.init();const r=this.filePath(n.id);return g.sync(r,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(r,384),n}delete(e){const n=this.filePath(e);return!!t.existsSync(n)&&(t.unlinkSync(n),!0)}read(e){try{const r=JSON.parse(t.readFileSync(e,"utf8")),o=n.basename(e,".json"),i=Qe.safeParse(r);if(i.success&&i.data.id===o&&dt(i.data)===i.data.id)return i.data;const s=Qe.strip().safeParse(r);return s.success&&s.data.id===o&&dt(s.data)===s.data.id?(console.warn(JSON.stringify({message:"unknown stored node model fields were ignored",nodeId:this.nodeId,filePath:e})),s.data):void console.warn(JSON.stringify({message:"stored node model failed validation and was isolated",nodeId:this.nodeId,filePath:e}))}catch(t){return void console.warn(JSON.stringify({message:"stored node model could not be read",nodeId:this.nodeId,filePath:e,error:t instanceof Error?t.message:String(t)}))}}filePath(e){return n.join(this.directory,`${e}.json`)}}class fr{directory;constructor(e){this.directory=e}init(){t.mkdirSync(this.directory,{recursive:!0,mode:448}),t.chmodSync(this.directory,448)}get(e){const n=this.filePath(e);if(t.existsSync(n))try{const r=JSON.parse(t.readFileSync(n,"utf8")),o=tt.safeParse(r);if(o.success)return o.data;const i=tt.strip().safeParse(r);if(i.success)return console.warn(JSON.stringify({message:"unknown stored model assignment fields were ignored",instanceId:e,filePath:n})),i.data;throw o.error}catch(t){throw Object.assign(new Error(`Stored model assignment for instance ${e} is invalid.`),{statusCode:409,code:"NODE_MODEL_ASSIGNMENT_INVALID",cause:t})}}put(e){const n=tt.parse(e);this.init();const r=this.filePath(n.instanceId);return g.sync(r,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(r,384),n}delete(e){t.rmSync(this.filePath(e),{force:!0})}filePath(e){return n.join(this.directory,`${e}.json`)}}class yr{usedNonces=new Map;identity;nodeId;overrideSecret;overrideKeyId;nowMs;constructor(e,t,n,r,o=Date.now){this.identity=e,this.nodeId=t,this.overrideSecret=n,this.overrideKeyId=r,this.nowMs=o}hasRemoteSecrets(){return this.identity.remoteSecrets(this.overrideSecret,this.overrideKeyId).length>0}verify(e){const t=function(e){const t=new Map;for(const[n,r]of Object.entries(e)){const e=Array.isArray(r)?r.join(","):"string"==typeof r?r:void 0===r?"":String(r);t.set(n.toLowerCase(),e)}return{nodeId:t.get("x-taskhandoff-node-id")||"",keyId:t.get("x-taskhandoff-key-id")||"",timestamp:t.get("x-taskhandoff-timestamp")||"",nonce:t.get("x-taskhandoff-nonce")||"",bodySha256:t.get("x-taskhandoff-body-sha256")||"",signature:t.get("x-taskhandoff-signature")||""}}(e.headers);if(!t.nodeId&&!t.signature)return!1;const n=this.identity.remoteSecrets(this.overrideSecret,this.overrideKeyId);if(!n.length)return!1;if(t.nodeId!==this.nodeId)throw Object.assign(new Error("Invalid node agent HMAC node id."),{statusCode:401,code:"NODE_AGENT_HMAC_NODE_MISMATCH"});if(!t.keyId)throw Object.assign(new Error("Node agent HMAC key id is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_MISSING"});const r=n.filter(e=>t.keyId===e.keyId);if(!r.length)throw Object.assign(new Error("Invalid node agent HMAC key id."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_INVALID"});const o=Date.parse(t.timestamp);if(!Number.isFinite(o)||Math.abs(this.nowMs()-o)>6e4)throw Object.assign(new Error("Node agent HMAC timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_HMAC_TIMESTAMP_INVALID"});if(!t.nonce)throw Object.assign(new Error("Node agent HMAC nonce is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_MISSING"});this.pruneNonces();const i=`${t.nodeId}:${t.keyId}:${t.nonce}`;if(this.usedNonces.has(i))throw Object.assign(new Error("Node agent HMAC nonce was already used."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_REPLAY"});const s=Xn(null==(a=e.body)?"":"string"==typeof a||Buffer.isBuffer(a)?a:JSON.stringify(a));var a;if(t.bodySha256!==s)throw Object.assign(new Error("Node agent HMAC body hash mismatch."),{statusCode:401,code:"NODE_AGENT_HMAC_BODY_HASH_INVALID"});if(!r.some(n=>function(e,t){const n=Buffer.from(e,"hex"),r=Buffer.from(t,"hex");return n.length===r.length&&p.timingSafeEqual(n,r)}(t.signature,Qn(n.secret,{keyId:t.keyId,method:e.method,pathWithQuery:e.url,timestamp:t.timestamp,nonce:t.nonce,bodySha256:s}))))throw Object.assign(new Error("Invalid node agent HMAC signature."),{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_INVALID"});return this.usedNonces.set(i,o+6e4+1),t.keyId}pruneNonces(){const e=this.nowMs();for(const[t,n]of this.usedNonces)n<=e&&this.usedNonces.delete(t)}}const Ir=u.z.string().trim().min(1).max(160),br=u.z.string().datetime(),Ar=u.z.string().min(1).max(4096),zr=u.z.string().trim().url().max(2048),wr=u.z.object({tokenHash:u.z.string().trim().min(1).max(256),expiresAt:br,createdAt:br,controlPlaneName:u.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:zr.optional()}).strip(),Sr=u.z.object({id:Ir,keyId:Ir,name:u.z.string().trim().min(1).max(160).optional(),url:zr.optional(),secret:Ar,pairedAt:br,updatedAt:br,active:u.z.boolean().default(!0)}).strip(),vr=u.z.object({controlPlaneName:u.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:u.z.string().trim().max(2048).optional(),expiresInMs:u.z.number().int().positive().max(36e5).optional()}).strict(),Nr=u.z.object({joinToken:u.z.string().trim().min(1).max(4096),controlPlaneId:u.z.string().trim().min(1).max(160).optional(),controlPlaneName:u.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:u.z.string().trim().max(2048).optional()}).strict(),Er=u.z.object({controlPlaneUrl:u.z.string().trim().url().max(2048),joinToken:u.z.string().trim().min(1).max(4096),controlPlaneName:u.z.string().trim().min(1).max(160).optional(),activate:u.z.boolean().optional()}).strict();function kr(){return(new Date).toISOString()}const _r=new Set(["nodeId","createdAt","updatedAt","pairingInvites","remoteControlPlanes"]),Or=new Set(["tokenHash","expiresAt","createdAt","controlPlaneName","controlPlaneUrl"]),xr=new Set(["id","keyId","name","url","secret","pairedAt","updatedAt","active"]);function Tr(e,t){console.warn(JSON.stringify({message:e,...t}))}function Cr(e,t){return Object.assign(new Error(e,void 0===t?void 0:{cause:t}),{code:"NODE_AGENT_IDENTITY_INVALID"})}function Rr(e,t){return Object.keys(e).filter(e=>!t.has(e))}function Dr(e,t,n){const r=br.safeParse(e);return r.success?r.data:(n("invalid node agent identity timestamp was replaced",{field:t}),kr())}function jr(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw Cr("Node agent identity must be a JSON object.");const n=e,r=Ir.safeParse(n.nodeId);if(!r.success)throw Cr("Node agent identity has an invalid nodeId.",r.error);const o=r.data,i=Rr(n,_r);return i.length&&t("unknown node agent identity fields were ignored",{fields:i}),{nodeId:o,createdAt:Dr(n.createdAt,"createdAt",t),updatedAt:Dr(n.updatedAt,"updatedAt",t),pairingInvites:Array.isArray(n.pairingInvites)?n.pairingInvites.flatMap(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return t("invalid node agent pairing invite was ignored",{}),[];const n=Rr(e,Or);n.length&&t("unknown node agent pairing invite fields were ignored",{fields:n});const r=wr.safeParse(e);return r.success?[r.data]:(t("invalid node agent pairing invite was ignored",{issues:r.error.issues}),[])}):[],remoteControlPlanes:Array.isArray(n.remoteControlPlanes)?n.remoteControlPlanes.flatMap(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return t("invalid node agent remote control-plane was ignored",{}),[];const n=Rr(e,xr);n.length&&t("unknown node agent remote control-plane fields were ignored",{fields:n});const r=Sr.safeParse(e);return r.success?[r.data]:(t("invalid node agent remote control-plane was ignored",{issues:r.error.issues}),[])}):[]}}class Pr{paths;logger;constructor(e,t={}){this.paths=e,this.logger=t.logger||Tr}read(){let e,n;try{e=t.readFileSync(this.paths.identityPath,"utf8")}catch(e){if("ENOENT"===e.code)return;throw Object.assign(new Error(`Node agent identity could not be read: ${this.paths.identityPath}`,{cause:e}),{code:"NODE_AGENT_IDENTITY_READ_FAILED"})}try{n=JSON.parse(e)}catch(e){throw Cr(`Node agent identity contains invalid JSON: ${this.paths.identityPath}`,e)}return jr(n,this.logger)}write(e){t.mkdirSync(n.dirname(this.paths.identityPath),{recursive:!0});const r=jr({...e,updatedAt:kr()},this.logger);g.sync(this.paths.identityPath,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384})}}function Mr(){return(new Date).toISOString()}class $r{store;constructor(e){this.store=new Pr(e)}resolveNodeId(e){if(e?.trim())return this.writeNodeId(e.trim()),e.trim();const t=this.store.read();if(t?.nodeId)return t.nodeId;const n=Tn("node");return this.writeNodeId(n),n}createPairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=Mr(),r=new Date(Date.now()+(e.expiresInMs||6e5)).toISOString(),o=Cn(),i={tokenHash:Xn(o),createdAt:n,expiresAt:r,...e.controlPlaneName?{controlPlaneName:e.controlPlaneName}:{},...e.controlPlaneUrl?{controlPlaneUrl:e.controlPlaneUrl}:{}};return this.store.write({...t,pairingInvites:[...t.pairingInvites||[],i]}),{...i,token:o}}completePairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=Xn(e.joinToken),r=(t.pairingInvites||[]).find(e=>e.tokenHash===n);if(!r){const e=new Error("Node agent pairing invite is invalid or expired.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_PAIRING_INVITE_INVALID"}),e}const o=Mr(),i={id:e.controlPlaneId||Tn("cp"),keyId:Tn("key"),...e.controlPlaneName||r.controlPlaneName?{name:e.controlPlaneName||r.controlPlaneName}:{},...e.controlPlaneUrl||r.controlPlaneUrl?{url:e.controlPlaneUrl||r.controlPlaneUrl}:{},secret:Cn(),pairedAt:o,updatedAt:o,active:!0};return this.store.write({...t,pairingInvites:(t.pairingInvites||[]).filter(e=>e.tokenHash!==n),remoteControlPlanes:[...(t.remoteControlPlanes||[]).filter(e=>e.id!==i.id),i]}),i}upsertRemoteControlPlane(e){const t=this.store.read()||this.newIdentity(),n=e.url?.replace(/\/$/,"");return this.store.write({...t,remoteControlPlanes:[...(t.remoteControlPlanes||[]).filter(t=>t.id!==e.id&&(!n||t.url?.replace(/\/$/,"")!==n)),e]}),e}createRemoteControlPlane(e){const t=Mr();return{id:Tn("cp"),keyId:Tn("key"),...e.name?{name:e.name}:{},url:e.url,secret:Cn(),pairedAt:t,updatedAt:t,active:!1!==e.active}}configuredRemoteControlPlanes(){return this.store.read()?.remoteControlPlanes||[]}listRemoteControlPlanes(e=""){return(this.store.read()?.remoteControlPlanes||[]).map(({secret:t,...n})=>({...n,current:Boolean(e&&n.keyId===e)})).sort((e,t)=>Number(t.current)-Number(e.current)||t.updatedAt.localeCompare(e.updatedAt))}deleteRemoteControlPlane(e,t=""){if(t&&e===t){const e=new Error("Cannot delete the key currently used by this control-plane connection.");throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_REMOTE_KEY_IN_USE"}),e}const n=this.store.read();if(!n)return!1;const r=(n.remoteControlPlanes||[]).filter(t=>t.keyId!==e);return r.length!==(n.remoteControlPlanes||[]).length&&(this.store.write({...n,remoteControlPlanes:r}),!0)}remoteSecrets(e,t){return[...(this.store.read()?.remoteControlPlanes||[]).map(e=>({keyId:e.keyId,secret:e.secret})),...e&&t?[{keyId:t,secret:e}]:[]]}reverseTunnelSecret(e,t,n){if(t&&n)return{keyId:n,secret:t};const r=this.store.read()?.remoteControlPlanes||[];if(!r.length)return;if(e){const t=e.replace(/\/$/,""),n=r.find(e=>e.url?.replace(/\/$/,"")===t);if(n)return{keyId:n.keyId,secret:n.secret}}const o=[...r].sort((e,t)=>t.updatedAt.localeCompare(e.updatedAt))[0];return o?{keyId:o.keyId,secret:o.secret}:void 0}newIdentity(){return{nodeId:this.resolveNodeId(),createdAt:Mr(),updatedAt:Mr(),pairingInvites:[],remoteControlPlanes:[]}}writeNodeId(e){const t=this.store.read();this.store.write({...t||{createdAt:Mr()},nodeId:e,updatedAt:Mr(),pairingInvites:t?.pairingInvites||[],remoteControlPlanes:t?.remoteControlPlanes||[]})}prunePairingInvites(e){const t=Date.now();return{...e,pairingInvites:(e.pairingInvites||[]).filter(e=>Date.parse(e.expiresAt)>t)}}}const Fr=new Set(["content-encoding","content-length","transfer-encoding"]),Lr=["codex","claude"],Ur=5e3,Hr=u.z.object({version:u.z.literal(1),externalListener:ft}).strict(),qr=u.z.object({}).strict().default({}),Kr=He.strict();function Br(e){const t=process.env[e]?.trim();return t||void 0}function Gr(){try{const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:f&&"SCRIPT"===f.tagName.toUpperCase()&&f.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:f&&"SCRIPT"===f.tagName.toUpperCase()&&f.src||new URL("cli.js",document.baseURI).href)):__dirname,r=[n.resolve(e,"..","package.json"),n.resolve(e,"..","..","package.json")].find(e=>t.existsSync(e));return r&&JSON.parse(t.readFileSync(r,"utf8")).version||"unknown"}catch{return"unknown"}}function Vr(e){return Object.fromEntries([...e.entries()].filter(([e])=>!Fr.has(e.toLowerCase())))}function Jr(e){return e.bodyBase64?Buffer.from(e.bodyBase64,"base64"):e.body}const Wr=67108864;function Yr(){const e=Number(process.env.TASK_HANDOFF_INSTANCE_PROXY_MAX_RESPONSE_BYTES);return Number.isSafeInteger(e)&&e>0?e:67108864}function Xr(e){const t=e.authorization;return"string"==typeof t&&t.startsWith("Bearer ")?t.slice(7).trim():void 0}function Qr(e){if("string"!=typeof e)return!1;const t=e.trim().toLowerCase();return"localhost"===t||"127.0.0.1"===t||"::1"===t||"::ffff:127.0.0.1"===t}function Zr(e){return!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}function eo(){return(new Date).toISOString()}function to(e){return"all-ipv4"===e?"0.0.0.0":"127.0.0.1"}function no(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=t.externalListener&&"object"==typeof t.externalListener&&!Array.isArray(t.externalListener)?t.externalListener:{},r=Object.keys(t).filter(e=>"version"!==e&&"externalListener"!==e),o=Object.keys(n).filter(e=>"bindScope"!==e&&"port"!==e);return(r.length||o.length)&&console.warn(JSON.stringify({message:"unknown stored node agent runtime setting fields were ignored",filePath:"runtime-settings.json",fields:[...r,...o.map(e=>`externalListener.${e}`)]})),{version:t.version,externalListener:{bindScope:n.bindScope,port:n.port}}}function ro(e){return"local-folder"===e.type?Je.parse({mode:"local-bind",path:"/workspace",readOnly:!1}):Je.parse({mode:"git-clone",path:"/workspace",readOnly:!1})}function oo(e){const t=Ve.parse(e.source),n=e.projectId||("local-folder"===t.type?t.localFolderId:void 0)||("git-repository"===t.type?t.repositoryId:void 0)||("git-template"===t.type?t.templateId:void 0)||`project_${e.id}`;return Ye.parse({id:n,name:"string"==typeof e.sourceSnapshot.name?e.sourceSnapshot.name:e.name,source:t,defaultImageId:e.imageId,defaultNodeId:e.nodeId,defaultRuntimeId:e.runtimeId,workspacePolicy:ro(t),labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt})}async function io(e,t){if(!t.target.web)return"unknown";const n=new AbortController,r=setTimeout(()=>n.abort(),2e3);try{return(await e(`${lo(t)}/api/health`,{signal:n.signal})).ok?"reachable":"endpoint-unreachable"}catch{return"endpoint-unreachable"}finally{clearTimeout(r)}}async function so(e,t,n,r){const o=new AbortController,i=setTimeout(()=>o.abort(),r);try{return await e(t,{...n,signal:o.signal})}finally{clearTimeout(i)}}async function ao(e,t,n,r){if(!t.config.autoImportAgentConfigs)return void r.diagnostic({instanceId:t.id,action:n},"node instance config auto-import skipped");if("reachable"!==t.targetStatus)return;const o=lo(t),i=Number(process.env.TASK_HANDOFF_CONFIG_AUTO_IMPORT_TIMEOUT_MS||Ur);for(const s of Lr)try{const a=await so(e,`${o}/api/config-sync/import/${encodeURIComponent(s)}`,{method:"POST"},Number.isFinite(i)&&i>0?i:Ur);if(!a.ok){r.warn({instanceId:t.id,action:n,preset:s,statusCode:a.status},"node instance config auto-import failed");continue}r.diagnostic({instanceId:t.id,action:n,preset:s},"node instance config auto-import completed")}catch(e){r.warn({instanceId:t.id,action:n,preset:s,error:e instanceof Error?e.message:String(e)},"node instance config auto-import failed")}}async function co(e,t,n){const r=t.requireInstance(n);if("reachable"!==r.targetStatus||!r.target.web)return!1;const o=await so(e,`${lo(r)}/api/internal/model-environment`,{method:"PUT",headers:{"content-type":"application/json",authorization:`Bearer ${r.registrationToken}`},body:JSON.stringify(t.resolvedAssignedModelEnvironment(n))},Ur);if(!o.ok)throw Object.assign(new Error(`Instance ${n} rejected its managed model environment with HTTP ${o.status}.`),{statusCode:502,code:"INSTANCE_MODEL_ENVIRONMENT_APPLY_FAILED"});return!0}function lo(e){const t=e.target.web;if(!t){const t=new Error(`Instance ${e.id} does not have a web endpoint.`);throw Object.assign(t,{statusCode:409,code:"NODE_INSTANCE_WEB_ENDPOINT_MISSING"}),t}if("direct-port"!==e.target.strategy)return t.replace(/\/$/,"");try{const e=new URL(t);return e.port?(e.hostname=process.env.TASK_HANDOFF_NODE_AGENT_PROXY_HOST||"127.0.0.1",e.toString().replace(/\/$/,"")):t.replace(/\/$/,"")}catch{return t.replace(/\/$/,"")}}function uo(e,t){const n=new Error(t);throw Object.assign(n,{statusCode:403,code:e}),n}function po(e,t){if(e===re)return;const n=new Error(`${t} protocol version ${e||"missing"} is not compatible with ${re}.`);throw Object.assign(n,{statusCode:409,code:"PROTOCOL_VERSION_MISMATCH"}),n}function mo(e={}){return{requiresImage:!1,supportsControlledInstanceApi:!0,supportsContainerLifecycle:!1,supportsAppSessions:!0,supportsHostSessions:!0,artifactKind:"none",isolation:"none",...e}}function go(){const e=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND?.trim();if(e)return e.split(/\s+/);const r=n.resolve(process.cwd(),"bin","task-handoff.js");return t.existsSync(r)?[process.execPath,r,"web"]:["task-handoff-controlled-instance","web"]}function ho(e){return new Promise(t=>{const n=o.createServer();n.once("error",()=>t(!1)),n.listen({host:"127.0.0.1",port:e},()=>{n.close(()=>t(!0))})})}function fo(e,t=3e3){return null!==e.exitCode||null!==e.signalCode?Promise.resolve():new Promise(n=>{const r=setTimeout(()=>{null===e.exitCode&&null===e.signalCode&&e.kill("SIGKILL"),n()},t);r.unref(),e.once("exit",()=>{clearTimeout(r),n()})})}async function yo(e,t){const n=t.get(e.id);if(n&&!n.killed)return n.kill("SIGTERM"),t.delete(e.id),void await fo(n);if(e.runtime.pid){try{process.kill(e.runtime.pid,"SIGTERM")}catch{return}const t=Date.now()+3e3;for(;Date.now()<t;){try{process.kill(e.runtime.pid,0)}catch{return}await new Promise(e=>setTimeout(e,50))}try{process.kill(e.runtime.pid,"SIGKILL")}catch{}}}const Io=new Set(["provisioning","starting","registering","registered","running"]);async function bo(e,t){try{const n=await e(t,["--version"]);return{available:!0,command:t,version:(n.stdout||n.stderr).split(/\r?\n/)[0]?.trim()||void 0}}catch{return{available:!1}}}class Ao{executor;constructor(e){this.executor=e}start(e){return this.executor.start(e)}stop(e){return this.executor.stop(e)}restart(e){return this.executor.restart(e)}delete(e){return this.executor.delete(e)}}class zo{runCommand;paths;nodeAgentUrl;processByInstanceId=new Map;constructor(e,t,n){this.runCommand=e,this.paths=t,this.nodeAgentUrl=n}async start(e){const r=function(e){if("local-folder"!==e.source.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}return n.resolve(e.source.path)}(e.instance),o=e.instance.runtime.port||await async function(){const e=Number(process.env.TASK_HANDOFF_LOCAL_INSTANCE_PORT_START||19e3),t=Number.isInteger(e)&&e>0?e:19e3;for(let e=t;e<t+1e3&&e<=65535;e+=1)if(await ho(e))return e;const n=new Error(`No free localhost port found in range ${t}-${Math.min(t+999,65535)}.`);throw Object.assign(n,{statusCode:503,code:"LOCAL_INSTANCE_PORT_UNAVAILABLE"}),n}(),i=n.join(this.paths.dataDir,"local-instances",e.instance.id),a=n.join(i,"logs");t.mkdirSync(a,{recursive:!0});const[c,...d]=go(),l=[...d,"--host","127.0.0.1","--port",String(o)],u=t.openSync(n.join(a,"controlled-instance.out.log"),"a"),p=t.openSync(n.join(a,"controlled-instance.err.log"),"a"),m=s.spawn(c,l,{cwd:r,detached:!1,stdio:["ignore",u,p],env:{...process.env,TASK_HANDOFF_CONTROL_MODE:"controlled",TASK_HANDOFF_NODE_AGENT_URL:this.nodeAgentUrl(),TASK_HANDOFF_INSTANCE_ID:e.instance.id,TASK_HANDOFF_INSTANCE_NAME:e.instance.name,TASK_HANDOFF_REGISTRATION_TOKEN:e.instance.registrationToken||"",TASK_HANDOFF_PROJECT_ID:e.project.id,TASK_HANDOFF_NODE_ID:e.node.id,TASK_HANDOFF_RUNTIME_ID:e.runtime.id,TASK_HANDOFF_WORKSPACE:r,TASK_HANDOFF_WORKSPACE_MODE:"local-bind",TASK_HANDOFF_DATA_DIR:i,TASK_HANDOFF_LOG_DIR:a,TASK_HANDOFF_APP_SESSION_PERSIST:"1",TASK_HANDOFF_CODEX_APP_SERVER:process.env.TASK_HANDOFF_CODEX_APP_SERVER||"1",TASK_HANDOFF_WEB_PORT:String(o),TASK_HANDOFF_WEB_HOST:"127.0.0.1",...e.modelEnv||{}}});t.closeSync(u),t.closeSync(p);try{await function(e){return new Promise((t,n)=>{const r=()=>{e.off("error",o),t()},o=t=>{e.off("spawn",r),n(t)};e.once("spawn",r),e.once("error",o)})}(m)}catch(e){const o=e instanceof Error?e.message:String(e);try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] spawn failed command=${c} cwd=${r} error=${o}\n`)}catch{}throw Object.assign(new Error(`Controlled instance process could not start with command ${c} in ${r}: ${o}`),{statusCode:500,code:"LOCAL_INSTANCE_PROCESS_SPAWN_FAILED"})}this.processByInstanceId.set(e.instance.id,m),m.on("error",e=>{try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] process error pid=${m.pid??""} error=${e.message}\n`)}catch{}}),m.once("exit",(r,o)=>{this.processByInstanceId.delete(e.instance.id);try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] exited pid=${m.pid??""} code=${r??""} signal=${o??""}\n`)}catch{}});const g=`http://127.0.0.1:${o}`;return await async function(e,t=3e3){const n=Date.now()+t;for(;Date.now()<n;){try{if((await fetch(`${e}/api/health`)).ok)return!0}catch{}await new Promise(e=>setTimeout(e,100))}return!1}(g),{status:"registering",health:"unknown",connectionStatus:"online",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:"unknown",web:g,api:`${g}/api`},workspace:{mode:"local-bind",status:"pending",path:r},runtime:{kind:"local",workspacePath:r,pid:m.pid,port:o,labels:{...e.instance.runtime.labels,"task-handoff.runtime-kind":"local"}}}}async stop(e){return await yo(e.instance,this.processByInstanceId),{status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async restart(e){return await yo(e.instance,this.processByInstanceId),this.start(e)}async delete(e){return this.stop(e)}async check(e){const[t,n]=await Promise.all([bo(this.runCommand,"codex"),bo(this.runCommand,"claude")]);return{status:"online",capabilities:mo({...e.capabilities,apps:{terminal:!0,codex:t,claude:n}})}}async stopAll(){const e=Array.from(this.processByInstanceId.values());for(const e of this.processByInstanceId.values())e.killed||e.kill("SIGTERM");this.processByInstanceId.clear(),await Promise.all(e.map(e=>fo(e)))}}class wo{docker;local;constructor(e,t){this.docker=e,this.local=t}forRuntime(e){if("docker"===e.type)return this.docker;if("local"===e.type)return this.local;const t=new Error(`Runtime type ${e.type} is not supported by this node agent.`);throw Object.assign(t,{statusCode:400,code:"NODE_RUNTIME_TYPE_UNSUPPORTED"}),t}async stopAll(){this.local instanceof zo&&await this.local.stopAll()}}class So{nodeId;paths;localFolders;nodeRuntimes;controlledInstances;models;modelAssignments;modelEnvironments;updateJobs;node;listenerPort;containerUrlOverride;constructor(e,t,n,r,o){this.paths=e,this.nodeId=t,this.localFolders=new Rn(e.localFoldersDir,{schema:Ge}),this.nodeRuntimes=new Rn(e.nodeRuntimesDir,{schema:gt}),this.controlledInstances=new Rn(e.controlledInstancesDir,{schema:vt,sanitize:e=>Et(e,e=>{console.warn(JSON.stringify({message:"legacy controlled instance field was ignored",...e}))})}),this.models=new hr(e.nodeModelsDir,t),this.modelAssignments=new fr(e.modelAssignmentsDir),this.modelEnvironments=new gr(e.modelEnvironmentsDir),this.updateJobs=new ur(e),this.listenerPort=o,this.containerUrlOverride=r;const i=eo();this.node=mt.parse({id:t,name:t,connectionMode:"direct-http",endpoint:n,controlEndpoint:n,containerEndpoint:this.containerUrl,publicWebBase:n?n.replace(/:\d+$/,""):void 0,status:"online",health:"ok",capabilities:{},labels:{},createdAt:i,updatedAt:i})}init(){if(this.localFolders.init(),this.nodeRuntimes.init(),this.controlledInstances.init(),this.models.init(),this.modelAssignments.init(),this.migrateLegacyModelEnvironments(),this.updateJobs.init(),!this.nodeRuntimes.get("runtime_local_docker")){const e=eo();this.nodeRuntimes.put(gt.parse({id:"runtime_local_docker",nodeId:this.nodeId,name:"Local Docker",type:"docker",status:"unknown",accessStrategy:"direct-port",capabilities:{},labels:{},createdAt:e,updatedAt:e}))}for(const e of this.nodeRuntimes.list())e.nodeId!==this.nodeId&&this.nodeRuntimes.put(gt.parse({...e,nodeId:this.nodeId,updatedAt:eo()}));for(const e of this.localFolders.list())e.nodeId!==this.nodeId&&this.localFolders.put(Ge.parse({...e,nodeId:this.nodeId,updatedAt:eo()}))}get localNodeAgentUrl(){return`http://127.0.0.1:${this.listenerPort}`}get currentListenerPort(){return this.listenerPort}get containerUrl(){return this.containerUrlOverride||`http://host.docker.internal:${this.listenerPort}`}setListenerPort(e){this.listenerPort=e,this.node.containerEndpoint=this.containerUrl,this.node.updatedAt=eo()}runningInstanceCount(){const e=new Set(["created","stopped","failed"]);return this.listInstances().filter(t=>!e.has(t.status)).length}createLocalFolder(e){const t=eo(),n=Ge.parse({...e,id:e.id||Tn("folder"),nodeId:this.nodeId,labels:e.labels||{},createdAt:t,updatedAt:t});return this.localFolders.put(n)}createRuntime(e){const t=eo(),n=gt.parse({...e,id:e.id||Tn("runtime"),nodeId:this.nodeId,accessStrategy:e.accessStrategy||(r=e.type,"docker"===r?"direct-port":"node-proxy"),capabilities:"local"===e.type?mo(e.capabilities):e.capabilities||{},labels:e.labels||{},createdAt:t,updatedAt:t});var r;return this.nodeRuntimes.put(n)}updateRuntime(e,t){const n=this.requireRuntime(e),r=gt.parse({...n,...t,id:n.id,nodeId:this.nodeId,accessStrategy:t.accessStrategy||n.accessStrategy,capabilities:"local"===(t.type||n.type)?mo(t.capabilities||n.capabilities):t.capabilities||n.capabilities,createdAt:n.createdAt,updatedAt:eo()});return this.nodeRuntimes.put(r)}deleteRuntime(e){this.requireRuntime(e);const t=this.listInstances().filter(t=>t.runtimeId===e);if(t.length){const n=new Error(`Runtime ${e} is used by ${t.length} instance${1===t.length?"":"s"}.`);throw Object.assign(n,{statusCode:409,code:"NODE_RUNTIME_IN_USE"}),n}return this.nodeRuntimes.delete(e)}checkRuntime(e,t){const n=this.requireRuntime(e);return t.check?t.check(n).then(e=>{const t=gt.parse({...n,...e,id:n.id,nodeId:this.nodeId,createdAt:n.createdAt,updatedAt:eo()});return this.nodeRuntimes.put(t)}):this.nodeRuntimes.put(gt.parse({...n,status:"unknown",updatedAt:eo()}))}requireRuntime(e){const t=this.nodeRuntimes.get(e);if(!t){const t=new Error(`Node runtime ${e} was not found.`);throw Object.assign(t,{statusCode:404,code:"NODE_RUNTIME_NOT_FOUND"}),t}return t}requireInstance(e){const t=this.controlledInstances.get(e);if(!t){const t=new Error(`Instance ${e} was not found on node ${this.nodeId}.`);throw Object.assign(t,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),t}const n=Nt(t);if(!n.success)throw function(e,t){const n=t.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; "),r=new Error(`Stored instance ${e} is not compatible with protocol ${re}: ${n}`);return Object.assign(r,{statusCode:409,code:"NODE_INSTANCE_PAYLOAD_INVALID"}),r}(e,n.error.issues);return n.data}listInstances(){return this.controlledInstances.list().flatMap(e=>{const t=Nt(e);return t.success?[t.data]:[]})}listModels(){const e=new Map;for(const t of this.listInstances()){const n=this.modelAssignments.get(t.id);for(const t of[n?.codexModelHash,n?.claudeModelHash])t&&e.set(t,(e.get(t)||0)+1)}return this.models.list().map(t=>this.publicModel(t,e.get(t.id)||0))}createModel(e){const t=eo(),n=dt(e),r=this.models.get(n),o=Qe.parse({...e,id:n,enabled:e.enabled??!0,order:e.order??this.nextModelOrder(),labels:e.labels||{},createdAt:r?.createdAt||t,updatedAt:t});return this.publicModel(this.models.put(o),this.modelReferenceIds(n).length)}deployModel(e){const t=dt(e);if(e.id!==t)throw Object.assign(new Error(`Model content hash ${t} does not match ${e.id}.`),{statusCode:400,code:"NODE_MODEL_HASH_MISMATCH"});const n=this.models.get(e.id)||this.models.put(Qe.parse(e));return this.publicModel(n,this.modelReferenceIds(n.id).length)}updateModel(e,t){const n=this.requireModel(e),r=Qe.parse({...n,...t,key:t.key?.trim()?t.key:n.key,createdAt:n.createdAt,updatedAt:eo()}),o=dt(r),i=this.models.put(Qe.parse({...r,id:o}));return this.publicModel(i,this.modelReferenceIds(o).length)}deleteModel(e){this.requireModel(e);const t=this.modelReferenceIds(e);if(t.length)throw Object.assign(new Error(`Model ${e} is assigned to ${t.length} instance${1===t.length?"":"s"}.`),{statusCode:409,code:"NODE_MODEL_IN_USE",instanceIds:t});return this.models.delete(e)}assignModels(e,t){const n=this.requireInstance(e);if(this.validateAssignmentRef("codex",t.codexModelHash),this.validateAssignmentRef("claude",t.claudeModelHash),void 0!==t.modelSelection.codexModelHash&&(t.modelSelection.codexModelHash??void 0)!==t.codexModelHash)throw Object.assign(new Error("Codex model selection does not match its node assignment."),{statusCode:400,code:"NODE_MODEL_SELECTION_MISMATCH"});if(void 0!==t.modelSelection.claudeModelHash&&(t.modelSelection.claudeModelHash??void 0)!==t.claudeModelHash)throw Object.assign(new Error("Claude model selection does not match its node assignment."),{statusCode:400,code:"NODE_MODEL_SELECTION_MISMATCH"});const r=this.modelAssignments.get(e),o=tt.parse({instanceId:e,codexModelHash:t.codexModelHash,claudeModelHash:t.claudeModelHash,updatedAt:eo()});this.modelAssignments.put(o);try{return{assignment:o,instance:this.controlledInstances.put(vt.parse({...n,modelSelection:t.modelSelection,updatedAt:eo()}))}}catch(t){throw r?this.modelAssignments.put(r):this.modelAssignments.delete(e),t}}resolvedAssignedModelEnvironment(e){const t=this.modelAssignments.get(e);if(!t){if(this.modelEnvironments.has(e))throw Object.assign(new Error(`Legacy model environment for instance ${e} requires manual migration.`),{statusCode:409,code:"NODE_MODEL_MIGRATION_REQUIRED"});return{}}return{...this.modelEnvironmentForRef("codex",t.codexModelHash),...this.modelEnvironmentForRef("claude",t.claudeModelHash)}}validateAssignmentRef(e,t){if(!t)return;const n=this.requireModel(t);if(n.app!==e)throw Object.assign(new Error(`Model ${n.id} belongs to ${n.app}, not ${e}.`),{statusCode:400,code:"NODE_MODEL_APP_MISMATCH"});if(!n.enabled)throw Object.assign(new Error(`Model ${n.id} is disabled.`),{statusCode:409,code:"NODE_MODEL_DISABLED"})}modelEnvironmentForRef(e,t){if(!t)return{};this.validateAssignmentRef(e,t);const n=this.requireModel(t);return"codex"===e?{OPENAI_API_KEY:n.key,OPENAI_BASE_URL:n.endpoint,TASK_HANDOFF_CODEX_BASE_URL:n.endpoint,TASK_HANDOFF_CODEX_MODEL:n.model}:{ANTHROPIC_API_KEY:n.key,ANTHROPIC_BASE_URL:n.endpoint,TASK_HANDOFF_CLAUDE_MODEL:n.model}}requireModel(e){const t=this.models.get(e);if(!t)throw Object.assign(new Error(`Model ${e} was not found on node ${this.nodeId}.`),{statusCode:404,code:"NODE_MODEL_NOT_FOUND"});if(dt(t)!==t.id)throw Object.assign(new Error(`Stored model ${e} does not match its content hash.`),{statusCode:409,code:"NODE_MODEL_HASH_INVALID"});return t}modelReferenceIds(e){return this.listInstances().filter(t=>{const n=this.modelAssignments.get(t.id);return n?.codexModelHash===e||n?.claudeModelHash===e}).map(e=>e.id)}publicModel(e,t){const{key:n,...r}=e;return et.parse({...r,keyPreview:n.length<=8?"set":`${n.slice(0,4)}...${n.slice(-4)}`,keySet:!0,referenceCount:t})}nextModelOrder(){return this.models.list().reduce((e,t)=>Math.max(e,t.order),0)+100}migrateLegacyModelEnvironments(){for(const e of this.modelEnvironments.listInstanceIds()){if(!this.controlledInstances.get(e)){this.warnLegacyModelMigration(e,"instance-not-found");continue}if(this.modelAssignments.get(e)){try{this.resolvedAssignedModelEnvironment(e),this.modelEnvironments.delete(e)}catch{this.warnLegacyModelMigration(e,"existing-assignment-invalid")}continue}let t;try{t=this.modelEnvironments.get(e)}catch{this.warnLegacyModelMigration(e,"sidecar-invalid");continue}if(!Object.keys(t).length){this.modelEnvironments.delete(e);continue}if(Object.keys(t).some(e=>!mr.has(e))){this.warnLegacyModelMigration(e,"unknown-fields");continue}const n=[];try{const r=this.migrateLegacyModelForApp(t,"codex",n),o=this.migrateLegacyModelForApp(t,"claude",n);if(!r&&!o)throw new Error("no complete model configuration");const i={...r?{codexModelHash:r}:{},...o?{claudeModelHash:o}:{}};this.assignModels(e,{modelSelection:i,codexModelHash:r,claudeModelHash:o}),this.resolvedAssignedModelEnvironment(e),this.modelEnvironments.delete(e)}catch{this.modelAssignments.delete(e);for(const e of n)this.models.delete(e);this.warnLegacyModelMigration(e,"mapping-failed")}}}migrateLegacyModelForApp(e,t,n){const r=e["codex"===t?"OPENAI_API_KEY":"ANTHROPIC_API_KEY"],o="codex"===t?e.TASK_HANDOFF_CODEX_BASE_URL||e.OPENAI_BASE_URL:e.ANTHROPIC_BASE_URL,i="codex"===t?e.TASK_HANDOFF_CODEX_MODEL||e.CODEX_MODEL:e.TASK_HANDOFF_CLAUDE_MODEL||e.CLAUDE_MODEL;if(!Boolean(r||o||i))return;if(!r||!o||!i)throw new Error("model configuration incomplete");const s=dt({app:t,endpoint:o,key:r,model:i}),a=this.models.get(s);if(a){if(a.app!==t||a.key!==r||a.endpoint!==o||a.model!==i||!a.enabled)throw new Error("existing model conflicts with legacy environment");return s}const c=eo();return this.models.put(Qe.parse({id:s,name:`Migrated ${"codex"===t?"Codex":"Claude"} model`,endpoint:o,key:r,model:i,app:t,enabled:!0,order:this.nextModelOrder(),labels:{migratedFrom:"instance-model-environment"},createdAt:c,updatedAt:c})),n.push(s),s}warnLegacyModelMigration(e,t){console.warn(JSON.stringify({message:"legacy model environment was preserved because it could not be migrated",nodeId:this.nodeId,instanceId:e,reason:t}))}createInstance(e){const t=this.requireRuntime(e.runtimeId),r=eo(),o=e.id||Tn("inst"),i=Ve.parse(e.source);if(function(e){return"boolean"==typeof e.capabilities.requiresImage?e.capabilities.requiresImage:"local"!==e.type}(t)&&(!e.imageId||!e.image)){const e=new Error(`Runtime ${t.name} requires an image.`);throw Object.assign(e,{statusCode:400,code:"NODE_RUNTIME_IMAGE_REQUIRED"}),e}if("local"===t.type&&"local-folder"!==i.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}if("local"===t.type&&this.listInstances().some(e=>"local"===this.requireRuntime(e.runtimeId).type)){const e=new Error("A localhost instance already exists on this node.");throw Object.assign(e,{statusCode:409,code:"LOCAL_RUNTIME_INSTANCE_EXISTS"}),e}const s=e.image?lt.parse(e.image):void 0,a=s?(()=>{const{reference:e,...t}=s;return ut.parse({...t,requestedReference:e})})():void 0,c="local"===t.type&&"local-folder"===i.type?n.resolve(i.path):void 0,d=vt.parse({id:o,name:e.name||`instance-${r.replace(/[-:.TZ]/g,"").slice(0,14)}-${o.replace(/^inst_?/,"").slice(0,4)}`,source:i,sourceSnapshot:e.sourceSnapshot||{},modelSelection:e.modelSelection,projectId:e.projectId,nodeId:this.nodeId,runtimeId:t.id,imageId:s?.id,imageSnapshot:a,imageProvisioning:a&&"docker"===t.type?{phase:"checking-image",requestedReference:a.requestedReference,generation:0,startedAt:r,updatedAt:r}:void 0,status:a&&"docker"===t.type?"provisioning":"created",health:"unknown",connectionStatus:"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",controlMode:"controlled",capabilities:{},config:e.config,workspace:"local"===t.type?{mode:"local-bind",status:"unknown",path:c}:{status:"unknown"},target:{strategy:"node-proxy",status:"unknown"},runtime:"local"===t.type?{kind:"local",workspacePath:c,labels:{"task-handoff.runtime-kind":"local"}}:{labels:{}},registrationToken:Cn(),createdAt:r,updatedAt:r});return this.controlledInstances.put(d)}registerInstance(e,t,n){const r=_t.parse(t),o=eo(),i=this.controlledInstances.get(e);if(!i){const t=new Error(`Instance ${e} was not found on node ${this.nodeId}.`);throw Object.assign(t,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),t}this.validateInstanceReport(i,r,n),po(r.protocolVersion,`Instance ${e}`);const s=vt.parse({...i,name:r.name,status:"registered",health:"ok",connectionStatus:"online",agentStatus:"online",targetStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":"reachable"===r.target.status?"reachable":i.targetStatus,uiAccessStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":i.uiAccessStatus,controlMode:r.controlMode,protocolVersion:r.protocolVersion,instanceVersion:r.instanceVersion,build:r.build,capabilities:r.capabilities,appInventory:r.appInventory,workspace:r.workspace,target:{...i.target,...r.target},registrationToken:i.registrationToken||r.registrationToken,lastHeartbeatAt:o,updatedAt:o});return this.controlledInstances.put(s)}heartbeatInstance(e,t,n){const r=this.requireInstance(e);this.validateInstanceToken(r,n);const o=Ot.parse(t);po(o.protocolVersion,`Instance ${e}`);const i=eo(),s=o.target?{...r.target,...o.target}:r.target,a={...s,status:"unknown"===s.status&&(s.web||s.api)?"reachable":s.status},c="endpoint-unreachable"===a.status?"endpoint-unreachable":"reachable"===a.status?"reachable":r.targetStatus,d=vt.parse({...r,...o,target:a,agentStatus:"online",targetStatus:c,uiAccessStatus:c,connectionStatus:"online",build:o.build||r.build,lastHeartbeatAt:i,updatedAt:i});return this.controlledInstances.put(d)}validateInstanceReport(e,t,n){this.validateInstanceToken(e,n||t.registrationToken),t.instanceId&&t.instanceId!==e.id&&uo("INSTANCE_ID_MISMATCH",`Instance ${t.instanceId} cannot register as ${e.id}.`),t.nodeId&&t.nodeId!==this.nodeId&&uo("INSTANCE_NODE_MISMATCH",`Instance ${e.id} belongs to node ${this.nodeId}.`),t.runtimeId&&t.runtimeId!==e.runtimeId&&uo("INSTANCE_RUNTIME_MISMATCH",`Instance ${e.id} belongs to runtime ${e.runtimeId}.`),t.imageId&&t.imageId!==e.imageId&&uo("INSTANCE_IMAGE_MISMATCH",`Instance ${e.id} belongs to image ${e.imageId}.`)}validateInstanceToken(e,t){e.registrationToken&&t===e.registrationToken||uo("INSTANCE_REGISTRATION_TOKEN_INVALID",`Invalid registration token for instance ${e.id}.`)}context(e,t=this.resolvedAssignedModelEnvironment(e.id)){const n=e.imageSnapshot||ut.parse({id:e.imageId||"img_localhost",name:e.imageId||"Localhost",requestedReference:"localhost:local",pullPolicy:"if-not-present",capabilities:[],optionalApps:[],defaultEnv:{},labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt});return{project:oo(e),image:n,node:this.node,runtime:this.requireRuntime(e.runtimeId),instance:e,nodeAgentUrl:this.containerUrl,modelEnv:t}}}class vo{app;state;settings;sockets=new Set;config;source;status="error";error;constructor(e){this.app=e.app,this.state=e.state,this.settings=e.settings,this.config=e.config,this.source=e.source,this.app.server.on("connection",e=>{this.sockets.add(e),e.once("close",()=>this.sockets.delete(e))})}current(){return yt.parse({...this.config,host:to(this.config.bindScope),status:this.status,source:this.source,...this.error?{error:this.error}:{}})}async start(){try{await this.listen(this.config),this.status="listening",this.error=void 0,this.state.setListenerPort(this.config.port)}catch(e){this.status="error",this.error=e instanceof Error?e.message:String(e),this.app.log.error({host:to(this.config.bindScope),port:this.config.port,error:this.error},"node agent TCP listener failed to start; Unix IPC remains available")}return this.current()}async update(e){const t=It.parse(e);if(t.bindScope===this.config.bindScope&&t.port===this.config.port)return this.current();if(t.port!==this.config.port){const e=this.state.runningInstanceCount();if(e>0){const t=new Error(`Cannot change the node agent port while ${e} controlled instance(s) are running.`);throw Object.assign(t,{statusCode:409,code:"NODE_AGENT_LISTENER_PORT_IN_USE_BY_INSTANCES",blockingInstanceCount:e}),t}}const n={config:this.config,source:this.source,status:this.status,error:this.error};await this.stop();try{await this.listen(t)}catch(e){await this.restore(n);const r=new Error(`Failed to bind node agent TCP listener at ${to(t.bindScope)}:${t.port}: ${e instanceof Error?e.message:String(e)}`);throw Object.assign(r,{statusCode:409,code:"NODE_AGENT_LISTENER_BIND_FAILED"}),r}try{this.settings.put({version:1,externalListener:t})}catch(e){await this.stop(),await this.restore(n);const t=new Error(`Failed to persist node agent TCP listener: ${e instanceof Error?e.message:String(e)}`);throw Object.assign(t,{statusCode:500,code:"NODE_AGENT_LISTENER_PERSIST_FAILED"}),t}return this.config=t,this.source="persisted",this.status="listening",this.error=void 0,this.state.setListenerPort(t.port),this.current()}async shutdown(){await this.stop()}async restore(e){if(this.config=e.config,this.source=e.source,this.status=e.status,this.error=e.error,"listening"===e.status)try{await this.listen(e.config)}catch(e){this.status="error",this.error=`Failed to restore previous listener: ${e instanceof Error?e.message:String(e)}`,this.app.log.error({error:this.error},"node agent TCP listener rollback failed")}this.state.setListenerPort(e.config.port)}async listen(e){await new Promise((t,n)=>{const r=e=>{this.app.server.off("listening",o),n(e)},o=()=>{this.app.server.off("error",r),t()};this.app.server.once("error",r),this.app.server.once("listening",o),this.app.server.listen({host:to(e.bindScope),port:e.port})})}async stop(){this.app.server.listening&&await new Promise((e,t)=>{this.app.server.close(n=>n?t(n):e());for(const e of this.sockets)e.destroy()})}}async function No(e,t,n,r,o,i){const s=t=>{const i=e.controlledInstances.get(n);i&&i.imageProvisioning?.generation===r&&i.imageSnapshot&&(e.controlledInstances.put(vt.parse({...i,status:"provisioning",imageProvisioning:{...i.imageProvisioning,phase:t,error:void 0,updatedAt:eo()},updatedAt:eo()})),o())},a=e.controlledInstances.get(n);if(a?.imageSnapshot&&a.imageProvisioning?.generation===r)try{const c=await t.ensure(a.imageSnapshot.requestedReference,s),d=e.controlledInstances.get(n);if(!d?.imageSnapshot||d.imageProvisioning?.generation!==r)return;e.controlledInstances.put(vt.parse({...d,status:"created",health:"unknown",imageSnapshot:{...d.imageSnapshot,requestedReference:c.requestedReference,resolvedDigest:c.resolvedDigest,resolvedReference:c.resolvedReference},imageProvisioning:{...d.imageProvisioning,phase:"ready",error:void 0,updatedAt:eo()},updatedAt:eo()})),o(),i.diagnostic({instanceId:n,action:"image.provision",reference:c.requestedReference,digest:c.resolvedDigest,pulled:c.pulled},"node instance image provisioning completed")}catch(t){const s=e.controlledInstances.get(n);if(!s?.imageProvisioning||s.imageProvisioning.generation!==r)return;const a=t instanceof Error?t.message:String(t);e.controlledInstances.put(vt.parse({...s,status:"failed",health:"failed",imageProvisioning:{...s.imageProvisioning,phase:"failed",error:a,updatedAt:eo()},updatedAt:eo()})),o(),i.warn({instanceId:n,action:"image.provision",reference:s.imageProvisioning.requestedReference,error:a},"node instance image provisioning failed")}}async function Eo(e,t,n,r,o,i="request"){const s=e.requireInstance(r);if(s.imageProvisioning&&"ready"!==s.imageProvisioning.phase&&"docker"===e.requireRuntime(s.runtimeId).type){const e=new Error(`Instance ${r} image is not ready.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_IMAGE_NOT_READY"}),e}o.diagnostic({instanceId:r,action:"start",reason:i,runtimeId:s.runtimeId,imageId:s.imageId},"node instance start requested");const a=e.controlledInstances.put(vt.parse({...s,status:"provisioning",updatedAt:eo()})),c=t.forRuntime(e.requireRuntime(a.runtimeId)),d=await c.start(e.context(a)),l=await io(n,vt.parse({...a,...d,target:d.target?{...a.target,...d.target}:a.target,workspace:d.workspace?{...a.workspace,error:void 0,...d.workspace}:a.workspace,runtime:d.runtime?{...a.runtime,...d.runtime}:a.runtime,updatedAt:eo()})),u=vt.parse({...a,...d,target:d.target?{...a.target,...d.target,status:l}:a.target,workspace:d.workspace?{...a.workspace,error:void 0,...d.workspace}:a.workspace,runtime:d.runtime?{...a.runtime,...d.runtime}:a.runtime,targetStatus:l,uiAccessStatus:l,connectionStatus:d.connectionStatus,updatedAt:eo()}),p=e.controlledInstances.put(u);return await ao(n,p,"start",o),o.diagnostic({instanceId:r,action:"start",reason:i,status:p.status,connectionStatus:p.connectionStatus,targetStatus:p.targetStatus,targetWeb:p.target.web,containerName:p.runtime.containerName},"node instance start completed"),p}function ko(e,t){const n=t instanceof Error?t.message:String(t);return vt.parse({...e,status:"failed",health:"failed",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",workspace:{...e.workspace,error:n},updatedAt:eo()})}function _o(e,t,n=eo()){if(!e.imageSnapshot)throw new Error(`Instance ${e.id} does not have an image snapshot.`);return{imageSnapshot:{...e.imageSnapshot,requestedReference:t.requestedReference,resolvedDigest:t.resolvedDigest,resolvedReference:t.resolvedReference,updatedAt:n},imageProvisioning:{phase:"ready",requestedReference:t.requestedReference,generation:(e.imageProvisioning?.generation||0)+1,startedAt:n,updatedAt:n}}}function Oo(e){return vt.parse({...e,health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:eo()})}async function xo(e={}){const r=e.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,o=Number(e.port||process.env.TASK_HANDOFF_NODE_AGENT_PORT||"8091"),s=`http://127.0.0.1:${o}`,p=e.containerUrl||process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,m=Nn(e.dataDir),g=e.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,h=e.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID,y=new $r(m),I=y.resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID),b=e.connectionMode||("local-ipc"===process.env.TASK_HANDOFF_NODE_AGENT_CONNECTION_MODE?"local-ipc":"local-loopback"),A=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||Wn(m.dataDir),z="local-ipc"===b?`ipc://${encodeURIComponent(A)}`:s,w=new So(m,I,s,p,o);w.node.connectionMode=b,w.node.controlEndpoint=z,w.node.endpoint=z,w.init();const S=e.dockerCommandRunner||jt,v=new Pt(S),N=new Mt(S,{publishHost:"127.0.0.1",imageService:v}),E=new wo(new Ao(N),new zo(e.dockerCommandRunner||jt,m,()=>w.localNodeAgentUrl)),k=e.updateCommandRunner||e.dockerCommandRunner||jt,_=new Pt(k),O=e.fetchImpl||fetch,x=c({logger:e.logger??!0}),T={requests:0,active:0,completed:0,aborted:0,limitRejected:0,responseBytes:0,totalDurationMs:0,maxResponseBytes:Yr()};x.decorate("nodeAgentState",w),await x.register(d);const C=new tn(w,r,{logger:x.log,safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0});C.start(),x.decorate("nodeAgentEventForwarder",C);const R=new an(S,()=>w.listInstances().filter(e=>"docker"===w.requireRuntime(e.runtimeId).type),e=>C.publish("instance.metrics.snapshot",e,{instanceId:e.instanceId}));R.start(),x.decorate("nodeAgentRuntimeMetrics",R);const D=(L=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(L||"").trim().toLowerCase())),j=new yr(y,I,g,h),P=(e,t)=>{D&&x.log.info(e,t)},M={diagnostic:P,warn:(e,t)=>x.log.warn(e,t)},$=async e=>{if("node-agent"===e.target.component)return async function(e){const t=await or(e.runCommand,"@task-handoff/node-agent",e.channel);return t?Le.parse({target:{component:"node-agent"},source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:t,updateAvailable:rr(e.currentVersion,t),checkedAt:nr()}):sr({target:{component:"node-agent"},channel:e.channel,currentVersion:e.currentVersion})}({channel:e.channel,currentVersion:Gr(),runCommand:k});const t=w.requireInstance(e.target.instanceId);return async function(e){const t={component:"controlled-instance",instanceId:e.instance.id};if("local"===e.runtime.type){const n=await or(e.runCommand,"@task-handoff/controlled-instance",e.channel),r=e.instance.build?.packageVersion||e.instance.instanceVersion;return n?Le.parse({target:t,source:"npm",channel:e.channel,currentVersion:r,availableVersion:n,updateAvailable:rr(r,n),checkedAt:nr()}):sr({target:t,channel:e.channel,currentVersion:r})}if("docker"===e.runtime.type){const n=e.instance.imageSnapshot?.requestedReference;if(!n)return Le.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:e.instance.build?.imageDigest,availableVersion:e.instance.build?.imageDigest||"unavailable",updateAvailable:!1,supported:!1,reason:"The instance does not use a remote Docker image.",checkedAt:nr()});const r=function(e,t){const n=e.split("@",1)[0],r=n.lastIndexOf("/"),o=n.lastIndexOf(":");return`${o>r?n.slice(0,o):n}:${ar(t)}`}(n,e.channel),o=await e.runCommand("docker",["manifest","inspect","--verbose",r]),i=function(e,t=process.arch){const n=e&&"object"==typeof e&&!Array.isArray(e)?e:void 0,r=Array.isArray(e)?e:Array.isArray(n?.manifests)?n.manifests:void 0;if(!r)return cr(e);const o=r.filter(e=>lr(e));if(!o.length)return cr(e);const i=o.find(e=>lr(e)===dr(t));return i?cr(i):void 0}(JSON.parse(o.stdout),e.arch);if(!i)throw new Error(`Docker registry did not return a digest for ${r} matching ${dr(e.arch||process.arch)}.`);const s=e.instance.build?.imageDigest;return Le.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:s,availableVersion:i,artifactRef:r,updateAvailable:s!==i,checkedAt:nr()})}return Le.parse({target:t,source:"npm",channel:e.channel,availableVersion:"unsupported",updateAvailable:!1,supported:!1,reason:`Runtime type ${e.runtime.type} does not support managed updates.`,checkedAt:nr()})}({channel:e.channel,instance:t,runtime:w.requireRuntime(t.runtimeId),runCommand:k})},F=async e=>{if("controlled-instance"!==e.target.component)return;const r=w.requireInstance(e.target.instanceId),o=w.requireRuntime(r.runtimeId),i=E.forRuntime(o);let s;if("local"===o.type){const r=ir(),o=(await k(r,["prefix","--global"])).stdout.trim(),[i]=go(),s=function(e){const r=function(e,r){if(!n.isAbsolute(e)||!t.existsSync(e))return;const o=t.realpathSync(e);let i=t.statSync(o).isDirectory()?o:n.dirname(o);for(;i!==n.dirname(i);){try{if(JSON.parse(t.readFileSync(n.join(i,"package.json"),"utf8")).name===r)return i}catch{}i=n.dirname(i)}}(e.executable,e.packageName),o=r&&function(e){let r=n.dirname(e);for(;r!==n.dirname(r);){try{if("@task-handoff/server"===JSON.parse(t.readFileSync(n.join(r,"package.json"),"utf8")).name)return r}catch{}r=n.dirname(r)}}(r);return o?{args:["install","--prefix",o,"--no-save",`${e.packageName}@${e.targetVersion}`],manifestPath:n.join(r,"package.json")}:{args:["install","--global","--prefix",e.globalPrefix,`${e.packageName}@${e.targetVersion}`],manifestPath:r?n.join(r,"package.json"):n.join(e.globalPrefix,"lib","node_modules",...e.packageName.split("/"),"package.json")}}({executable:i,globalPrefix:o,packageName:"@task-handoff/controlled-instance",targetVersion:e.toVersion});await k(r,s.args),function(e,n){let r;try{r=JSON.parse(t.readFileSync(e,"utf8")).version}catch(t){throw new Error(`Could not verify the updated package at ${e}: ${t instanceof Error?t.message:String(t)}`)}if(r!==n)throw new Error(`Updated package verification failed: expected ${n}, found ${String(r||"unknown")}.`)}(s.manifestPath,e.toVersion)}else{if("docker"!==o.type)throw Object.assign(new Error(`Runtime type ${o.type} does not support managed updates.`),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});{const t=e.artifactRef;if(!t||!r.imageSnapshot)throw Object.assign(new Error("The instance does not use an updateable remote Docker image."),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});await k("docker",["pull",t]),s=await _.ensure(t)}}await i.delete(w.context(r,{})),w.controlledInstances.put(function(e){return vt.parse({...e,status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:eo()})}(vt.parse({...r,..."docker"===o.type&&s&&r.imageSnapshot?{..._o(r,s)}:{}}))),await Eo(w,E,O,r.id,M,"update")};var L;x.setErrorHandler((e,t,n)=>{const r=function(e){if(e instanceof u.z.ZodError)return{statusCode:400,code:"VALIDATION_ERROR",message:e.issues.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; ")};const t=e&&"object"==typeof e?e:{};return{statusCode:"number"==typeof t.statusCode?t.statusCode:500,code:"string"==typeof t.code?t.code:"NODE_AGENT_ERROR",message:e instanceof Error?e.message:String(e)}}(e);(D||r.statusCode>=500)&&(r.statusCode>=500?x.log.error.bind(x.log):x.log.warn.bind(x.log))({method:t.method,url:t.url,statusCode:r.statusCode,errorCode:r.code,error:r.message},"node agent request failed"),n.code(r.statusCode).send({error:{code:r.code,message:r.message,...e&&"object"==typeof e&&"number"==typeof e.blockingInstanceCount?{blockingInstanceCount:e.blockingInstanceCount}:{}}})}),x.addHook("preHandler",async e=>{const t=j.verify(e);if(t)e.nodeAgentAuthKeyId=t;else if(Zr(e)){if(r&&Xr(e.headers)!==r){const e=new Error("Invalid node agent token.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),e}}else if("/api/node-agent/pairing/complete"!==e.url.split("?")[0]&&!function(e){const t=e.split("?")[0];return/^\/api\/node-agent\/instances\/[^/]+\/(register|heartbeat)$/.test(t)}(e.url)){if(j.hasRemoteSecrets()){const e=new Error("Node agent HMAC signature is required.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_REQUIRED"}),e}if(!function(e){return Qr(e.ip)||Qr(e.socket.remoteAddress)||!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}(e)){const e=new Error("Local node agent access is only accepted from loopback or local IPC connections.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_LOCAL_TOKEN_REQUIRES_LOOPBACK"}),e}if(r&&Xr(e.headers)!==r){const e=new Error("Invalid node agent token.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),e}}}),x.addHook("onClose",async()=>{R.stop(),C.stop(),await async function(e,t){await t.stopAll();for(const t of e.listInstances())"local"===e.requireRuntime(t.runtimeId).type&&e.controlledInstances.put(Oo(t))}(w,E)}),x.decorate("nodeAgentRestoreLocalInstances",async()=>{for(const e of w.listInstances().filter(e=>"provisioning"===e.status&&e.imageProvisioning&&"docker"===w.requireRuntime(e.runtimeId).type))No(w,v,e.id,e.imageProvisioning.generation,()=>C.syncNow(),M);const e=w.listInstances().filter(e=>"local"===w.requireRuntime(e.runtimeId).type&&Io.has(e.status));for(const t of e)try{await Eo(w,E,O,t.id,M,"restore")}catch(e){w.controlledInstances.put(ko(w.requireInstance(t.id),e)),P({instanceId:t.id,action:"restore",error:e instanceof Error?e.message:String(e)},"node local instance restore failed")}}),x.get("/api/node-agent/health",async()=>{return{data:{ok:!0,role:"node-agent",nodeId:I,protocolVersion:re,build:(e="node-agent",{component:e,packageName:"@task-handoff/node-agent",packageVersion:Gr(),protocolVersion:re,buildId:Br("TASK_HANDOFF_BUILD_ID"),builtAt:Br("TASK_HANDOFF_BUILT_AT"),gitCommit:Br("TASK_HANDOFF_GIT_COMMIT"),imageRef:Br("TASK_HANDOFF_IMAGE_REF"),imageDigest:Br("TASK_HANDOFF_IMAGE_DIGEST")}),instanceProxy:{...T},serverTime:(new Date).toISOString()}};var e});const U=e=>{if(!Zr(e)){const e=new Error("Node agent TCP listener settings are available only over local Unix IPC.");throw Object.assign(e,{statusCode:403,code:"NODE_AGENT_LISTENER_LOCAL_IPC_ONLY"}),e}if(!x.nodeAgentListenerManager){const e=new Error("Node agent TCP listener manager is not initialized.");throw Object.assign(e,{statusCode:503,code:"NODE_AGENT_LISTENER_UNAVAILABLE"}),e}return x.nodeAgentListenerManager};return x.get("/api/node-agent/settings/external-listener",async e=>({data:U(e).current()})),x.patch("/api/node-agent/settings/external-listener",async e=>({data:await U(e).update(e.body)})),x.get("/api/node-agent/updates/jobs",async()=>({data:w.updateJobs.list()})),x.post("/api/node-agent/updates/check",async e=>({data:await $(Fe.parse(e.body))})),x.post("/api/node-agent/updates/apply",async(e,r)=>{const o=Kr.parse(e.body),s=await $(o);if(!s.supported){const e=new Error(s.reason||"The requested update is not supported.");throw Object.assign(e,{statusCode:400,code:"UPDATE_UNSUPPORTED"}),e}if(!s.updateAvailable){const e=new Error(s.reason||"No update is available for the selected channel.");throw Object.assign(e,{statusCode:409,code:"UPDATE_NOT_AVAILABLE"}),e}const a=w.updateJobs.create(I,s);if("node-agent"===o.target.component){const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:f&&"SCRIPT"===f.tagName.toUpperCase()&&f.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:f&&"SCRIPT"===f.tagName.toUpperCase()&&f.src||new URL("cli.js",document.baseURI).href)):__dirname,{worker:r,packaged:o,expectedWorker:s}=function(e,r=t.existsSync){const o=[n.resolve(e,"..","bin","task-handoff-node-update-worker"),n.resolve(e,"..","..","bin","task-handoff-node-update-worker")],i=o.find(r),s=n.resolve(e,"..","..","..","..","scripts","node-update-worker.cjs");return i?{worker:i,packaged:!0,expectedWorker:o[0]}:r(s)?{worker:s,packaged:!1,expectedWorker:o[0]}:{worker:void 0,packaged:!1,expectedWorker:o[0]}}(e);if(!r){w.updateJobs.patch(a.id,{status:"failed",error:`Update worker was not found: ${s}`,completedAt:eo()});const e=new Error(`Node agent update worker was not found: ${s}`);throw Object.assign(e,{statusCode:500,code:"UPDATE_WORKER_NOT_FOUND"}),e}await k("systemd-run",["--unit",`task-handoff-update-${a.id}`,"--collect","--property=Type=exec",...o?[r]:[process.execPath,r],"--job-file",w.updateJobs.records.filePath(a.id),"--target-version",a.toVersion,"--npm-command",ir()])}else w.updateJobs.run(a,e=>F(e));return r.code(202).send({data:a})}),x.post("/api/node-agent/pairing/invites",async(e,t)=>{const n=y.createPairingInvite(vr.parse(e.body||{}));return t.code(201).send({data:{nodeId:I,joinToken:n.token,expiresAt:n.expiresAt}})}),x.post("/api/node-agent/pairing/complete",async(e,t)=>{const n=y.completePairingInvite(Nr.parse(e.body));return t.code(201).send({data:{nodeId:I,keyId:n.keyId,secret:n.secret,pairedAt:n.pairedAt}})}),x.get("/api/node-agent/remotes",async e=>({data:y.listRemoteControlPlanes(e.nodeAgentAuthKeyId)})),x.delete("/api/node-agent/remotes/:keyId",async e=>{const t=u.z.string().trim().min(1).max(160).parse(e.params.keyId);return{data:{deleted:y.deleteRemoteControlPlane(t,e.nodeAgentAuthKeyId)}}}),x.post("/api/node-agent/remotes/connect",async(e,t)=>{const n=Er.parse(e.body),r=function(e){const t=new URL(e);if("http:"!==t.protocol&&"https:"!==t.protocol){const e=new Error("Control-plane URL must use http or https.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_CONTROL_PLANE_URL_INVALID"}),e}return t.toString().replace(/\/$/,"")}(n.controlPlaneUrl),o=y.createRemoteControlPlane({url:r,...n.controlPlaneName?{name:n.controlPlaneName}:{},active:!1!==n.activate}),i=await async function(e,t,n){const r=await e(new URL("/api/node-join/complete",t).toString(),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),o=await r.json().catch(()=>({}));if(!r.ok){const e=new Error(o.error?.message||`Control-plane node join failed with HTTP ${r.status}.`);throw Object.assign(e,{statusCode:r.status,code:o.error?.code||"NODE_AGENT_REMOTE_JOIN_FAILED"}),e}return o.data||{}}(O,r,{joinToken:n.joinToken,nodeId:I,nodeName:w.node.name,keyId:o.keyId,secret:o.secret,pairedAt:o.pairedAt}),s=y.upsertRemoteControlPlane({...o,..."string"==typeof i.name&&i.name?{name:n.controlPlaneName||i.name}:{}});let a,c=!1!==s.active?"saved":"disabled";if(!1!==s.active&&x.nodeAgentReverseTunnels)try{x.nodeAgentReverseTunnels.connect({url:r,keyId:s.keyId,secret:s.secret}),c="connecting"}catch(e){c="failed",a=e instanceof Error?e.message:String(e)}return t.code(201).send({data:{remote:{id:s.id,url:s.url,keyId:s.keyId,pairedAt:s.pairedAt,active:!1!==s.active},tunnel:{status:c,...a?{error:a}:{}}}})}),x.get("/api/node-agent/runtimes",async()=>({data:w.nodeRuntimes.list()})),x.post("/api/node-agent/runtimes",async(e,t)=>t.code(201).send({data:w.createRuntime(Sn.parse(e.body))})),x.patch("/api/node-agent/runtimes/:id",async e=>({data:w.updateRuntime(e.params.id,vn.parse(e.body))})),x.delete("/api/node-agent/runtimes/:id",async e=>({data:{deleted:w.deleteRuntime(e.params.id)}})),x.post("/api/node-agent/runtimes/:id/check",async e=>{const t=w.requireRuntime(e.params.id);return{data:await w.checkRuntime(t.id,E.forRuntime(t))}}),x.get("/api/node-agent/local-folders",async()=>({data:w.localFolders.list()})),x.get("/api/node-agent/folders/tree",async e=>({data:yn(An.parse(e.query))})),x.post("/api/node-agent/local-folders",async(e,t)=>t.code(201).send({data:w.createLocalFolder(bn.parse(e.body))})),x.delete("/api/node-agent/local-folders/:id",async e=>({data:{deleted:w.localFolders.delete(e.params.id)}})),x.get("/api/node-agent/models",async()=>({data:w.listModels()})),x.post("/api/node-agent/models",async(e,t)=>t.code(201).send({data:w.createModel(it.parse(e.body))})),x.put("/api/node-agent/models/:id/deploy",async e=>{const t=e.params.id,n=at.parse(e.body);if(n.id!==t)throw Object.assign(new Error(`Model payload id ${n.id} does not match route id ${t}.`),{statusCode:400,code:"NODE_MODEL_ID_MISMATCH"});return{data:w.deployModel(n)}}),x.patch("/api/node-agent/models/:id",async e=>({data:w.updateModel(e.params.id,st.parse(e.body))})),x.delete("/api/node-agent/models/:id",async e=>({data:{deleted:w.deleteModel(e.params.id)}})),x.get("/api/node-agent/instances",async()=>({data:w.listInstances()})),x.post("/api/node-agent/instances",async(e,t)=>{const n=w.createInstance(zn.parse(e.body));return C.syncNow(),n.imageProvisioning&&No(w,v,n.id,n.imageProvisioning.generation,()=>C.syncNow(),M),t.code(201).send({data:n})}),x.post("/api/node-agent/instances/:id/image-provisioning/retry",async e=>{const t=function(e,t){const n=e.requireInstance(t);if("docker"!==e.requireRuntime(n.runtimeId).type||!n.imageSnapshot){const e=new Error(`Instance ${t} does not use a Docker image.`);throw Object.assign(e,{statusCode:400,code:"INSTANCE_IMAGE_PROVISIONING_UNSUPPORTED"}),e}if("failed"!==n.status||"failed"!==n.imageProvisioning?.phase){const e=new Error(`Instance ${t} does not have failed image provisioning to retry.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_IMAGE_PROVISIONING_NOT_FAILED"}),e}const r=eo();return e.controlledInstances.put(vt.parse({...n,status:"provisioning",health:"unknown",imageProvisioning:{phase:"checking-image",requestedReference:n.imageSnapshot.requestedReference,generation:(n.imageProvisioning?.generation||0)+1,startedAt:r,updatedAt:r},updatedAt:r}))}(w,e.params.id);return C.syncNow(),No(w,v,t.id,t.imageProvisioning.generation,()=>C.syncNow(),M),{data:t}}),x.patch("/api/node-agent/instances/:id",async e=>{const t=e.params.id,n=w.requireInstance(t),r=wn.parse(e.body),o=vt.parse({...n,...r,updatedAt:eo()}),i=w.controlledInstances.put(o);return C.syncNow(),{data:i}}),x.put("/api/node-agent/instances/:id/model-assignment",async e=>{const t=e.params.id,n=w.assignModels(t,ct.parse(e.body));return await co(O,w,t),{data:n}}),x.post("/api/node-agent/instances/:id/register",async(e,t)=>{const n=e.params.id,r=w.registerInstance(n,_t.parse(e.body),Xr(e.headers));return C.syncNow(),P({instanceId:n,action:"register",protocolVersion:r.protocolVersion,build:r.build,targetStatus:r.targetStatus,targetStrategy:r.target.strategy},"node instance registered"),t.code(201).send({data:r})}),x.post("/api/node-agent/instances/:id/heartbeat",async e=>{const t=e.params.id,n=w.heartbeatInstance(t,Ot.parse(e.body),Xr(e.headers));return C.syncNow(),P({instanceId:t,action:"heartbeat",status:n.status,health:n.health,protocolVersion:n.protocolVersion,build:n.build,targetStatus:n.targetStatus,apps:n.apps.runningCount},"node instance heartbeat accepted"),{data:n}}),x.get("/api/node-agent/events",{websocket:!0},e=>{const t=e;t.send(JSON.stringify({type:"node-agent.events.connected",nodeId:I,serverTime:(new Date).toISOString()}));const n=C.addOutput(t);t.on("close",n),t.on("error",n)}),x.get("/api/node-agent/docker/images",async()=>({data:await $t(e.dockerCommandRunner)})),x.get("/api/node-agent/instances/:id/metrics",async e=>{const t=e.params.id,n=w.requireInstance(t);if("docker"!==w.requireRuntime(n.runtimeId).type){const e=new Error(`Runtime type ${w.requireRuntime(n.runtimeId).type} does not provide resource metrics.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_METRICS_UNSUPPORTED"}),e}return{data:await R.snapshot(t)}}),x.post("/api/node-agent/instances/:id/start",async e=>{const t=e.params.id;qr.parse(e.body);const n=await Eo(w,E,O,t,M);return C.syncNow(),{data:n}}),x.post("/api/node-agent/instances/:id/stop",async e=>{const t=w.requireInstance(e.params.id);P({instanceId:t.id,action:"stop",runtimeId:t.runtimeId,containerName:t.runtime.containerName},"node instance stop requested");const n=E.forRuntime(w.requireRuntime(t.runtimeId)),r=await n.stop(w.context(t)),o=vt.parse({...t,...r,agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",updatedAt:eo()}),i=w.controlledInstances.put(o);return C.syncNow(),P({instanceId:t.id,action:"stop",status:i.status,connectionStatus:i.connectionStatus,containerName:i.runtime.containerName},"node instance stop completed"),{data:i}}),x.post("/api/node-agent/instances/:id/restart",async e=>{const t=e.params.id,n=w.requireInstance(t);qr.parse(e.body),P({instanceId:t,action:"restart",runtimeId:n.runtimeId,imageId:n.imageId,containerName:n.runtime.containerName},"node instance restart requested");const r=E.forRuntime(w.requireRuntime(n.runtimeId)),o=await r.restart(w.context(n)),i=vt.parse({...n,...o,target:o.target?{...n.target,...o.target}:n.target,updatedAt:eo()}),s=await io(O,i),a=vt.parse({...n,...o,target:o.target?{...n.target,...o.target,status:s}:n.target,targetStatus:s,uiAccessStatus:s,connectionStatus:o.connectionStatus,updatedAt:eo()}),c=w.controlledInstances.put(a);return await ao(O,c,"restart",M),C.syncNow(),P({instanceId:t,action:"restart",status:c.status,connectionStatus:c.connectionStatus,targetStatus:c.targetStatus,targetWeb:c.target.web,containerName:c.runtime.containerName},"node instance restart completed"),{data:c}}),x.post("/api/node-agent/instances/:id/delete",async e=>{const t=e.params.id,n=w.requireInstance(t);P({instanceId:t,action:"delete",runtimeId:n.runtimeId,containerName:n.runtime.containerName},"node instance delete requested");const r=E.forRuntime(w.requireRuntime(n.runtimeId));await r.delete(w.context(n,{})),P({instanceId:t,action:"delete"},"node instance delete completed");const o=w.controlledInstances.delete(t);return w.modelAssignments.delete(t),w.modelEnvironments.delete(t),C.syncNow(),{data:{deleted:o}}}),x.post("/api/node-agent/instances/:id/proxy",async(e,t)=>{const n=e.params.id,r=In.parse(e.body),o=lo(w.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;"POST"===r.method&&"/api/apps/sessions"===i&&await co(O,w,n),P({instanceId:n,action:"proxy",method:r.method,path:i,instanceBase:o},"node instance proxy requested");const s=await O(`${o}${i}`,{method:r.method,headers:{...r.headers},body:Jr(r)}),a=s.headers.get("content-type")||"application/octet-stream",c=await s.text();P({instanceId:n,action:"proxy",method:r.method,path:i,statusCode:s.status,contentType:a},"node instance proxy completed"),t.code(s.status).type(a).send(c)}),x.post("/api/node-agent/instances/:id/proxy/stream",{bodyLimit:Wr},async(e,t)=>{const n=Date.now();T.requests+=1,T.active+=1;const r=new AbortController;let o=0,i=!1,s=!1;const c=e=>{s||(s=!0,T.active-=1,T.totalDurationMs+=Date.now()-n,T.responseBytes+=o,T[e]+=1)},d=()=>{r.abort(),i&&c("aborted")};e.raw.once("aborted",d),t.raw.once("close",d);try{const n=e.params.id,s=In.parse(e.body),d=lo(w.requireInstance(n)),l=s.path.startsWith("/")?s.path:`/${s.path}`;P({instanceId:n,action:"proxy.stream",method:s.method,path:l,instanceBase:d},"node instance streaming proxy requested");const u=await O(`${d}${l}`,{method:s.method,headers:{...s.headers},body:Jr(s),signal:r.signal}),p=Number(u.headers.get("content-length"));if(Number.isFinite(p)&&p>T.maxResponseBytes)return T.limitRejected+=1,r.abort(),t.code(502).send({error:{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE",message:`Instance response exceeds ${T.maxResponseBytes} bytes.`}});t.code(u.status);for(const[e,n]of Object.entries(Vr(u.headers)))t.header(e,n);if(!u.body||"HEAD"===s.method)return c("completed"),t.send();const m=new a.Transform({transform(e,t,n){if(o+=e.length,o>T.maxResponseBytes)return T.limitRejected+=1,r.abort(),void n(Object.assign(new Error("Instance proxy response limit exceeded."),{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE"}));n(null,e)}});return i=!0,m.once("end",()=>c("completed")),m.once("error",()=>c("aborted")),t.send(a.Readable.fromWeb(u.body).pipe(m))}catch(e){throw c("aborted"),e}finally{e.raw.off("aborted",d),i||c("completed")}}),x.post("/api/node-agent/instances/:id/proxy/raw",{bodyLimit:Wr},async(e,t)=>{const n=e.params.id,r=In.parse(e.body),o=lo(w.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;P({instanceId:n,action:"proxy.raw",method:r.method,path:i,instanceBase:o},"node instance raw proxy requested");const s=await O(`${o}${i}`,{method:r.method,headers:{...r.headers},body:Jr(r)}),a=Number(s.headers.get("content-length"));if(Number.isFinite(a)&&a>T.maxResponseBytes)return T.limitRejected+=1,t.code(502).send({error:{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE",message:`Instance response exceeds ${T.maxResponseBytes} bytes.`}});let c;try{c=await async function(e,t){if(!e.body)return Buffer.alloc(0);const n=[];let r=0;const o=e.body.getReader();for(;;){const{done:e,value:i}=await o.read();if(e)break;if(r+=i.byteLength,r>t)throw await o.cancel("Instance proxy response limit exceeded.").catch(()=>{}),Object.assign(new Error("Instance proxy response limit exceeded."),{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE"});n.push(Buffer.from(i))}return Buffer.concat(n,r)}(s,T.maxResponseBytes)}catch(e){if(!(e instanceof Error)||"INSTANCE_PROXY_RESPONSE_TOO_LARGE"!==e.code)throw e;return T.limitRejected+=1,t.code(502).send({error:{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE",message:`Instance response exceeds ${T.maxResponseBytes} bytes.`}})}return P({instanceId:n,action:"proxy.raw",method:r.method,path:i,statusCode:s.status,byteLength:c.length},"node instance raw proxy completed"),{data:{status:s.status,headers:Vr(s.headers),bodyBase64:c.toString("base64")}}}),x.get("/api/node-agent/instances/:id/proxy/ws/*",{websocket:!0},(e,t)=>{const n=t.params.id,r=t.params["*"]||"",o=t.url.indexOf("?"),i=o>=0?t.url.slice(o):"";let s;try{const o=lo(w.requireInstance(n)),a=new URL(`/${r}${i}`,`${o}/`);a.protocol="https:"===a.protocol?"wss:":"ws:";const c=function(e){const t=e["sec-websocket-protocol"],n=(Array.isArray(t)?t.join(","):"string"==typeof t?t:"").split(",").map(e=>e.trim()).filter(Boolean);return n.length?n:void 0}(t.headers);P({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,upstreamUrl:a.toString(),protocols:c||[]},"node instance websocket proxy opening"),s=c?new l(a,c):new l(a),s.on("open",()=>{P({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy opened"),e.on("message",(e,t)=>s?.readyState===l.OPEN&&s.send(function(e,t){return t?e:e.toString()}(e,t))),e.on("close",()=>s?.close()),e.on("error",()=>s?.close())}),s.on("message",(t,n)=>{e.readyState===l.OPEN&&e.send(n?t:t.toString())}),s.on("close",()=>{P({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy closed"),e.close()}),s.on("error",t=>{P({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:t instanceof Error?t.message:String(t)},"node instance websocket proxy failed"),e.close(1011,"Instance websocket proxy failed.")})}catch(t){P({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:t instanceof Error?t.message:String(t)},"node instance websocket endpoint unavailable"),s?.close(),e.close(1011,"Instance websocket endpoint is not reachable.")}}),x}function To(e){const t=new URL("/api/node-agent/tunnel",e);return t.protocol="https:"===t.protocol?"wss:":"ws:",t.toString()}function Co(e,t){const n=new URL(t.tunnelUrl);n.searchParams.set("nodeId",t.nodeId);const r=t.secret?Zn({nodeId:t.nodeId,keyId:t.keyId,secret:t.secret,method:"GET",pathWithQuery:`${n.pathname}${n.search}`}):{},o=new l(n,{headers:r}),i=new Map,s=new Map;let a;const c=e=>{const n=e.startsWith("/")?e:`/${e}`,r="function"==typeof t.port?t.port():t.port,o=new URL(`/api/node-agent${n}`,`http://127.0.0.1:${r}`);return o.protocol="ws:",o},d=e=>t.secret?Zn({nodeId:t.nodeId,keyId:t.keyId,secret:t.secret,method:"GET",pathWithQuery:`${e.pathname}${e.search}`}):{},u=(e,t=1e3,n="")=>{const r=i.get(e);i.delete(e),r?.upstream?.close(t,n),r?.tunnel?.close(t,n)},p=(e,t,n=!1)=>new Promise((r,o)=>{e.send(t,{binary:n},e=>e?o(e):r())});return o.on("open",()=>{o.send(JSON.stringify({type:"node-agent.identify",nodeId:t.nodeId,serverTime:(new Date).toISOString()})),a=e.nodeAgentEventForwarder?.addOutput(o)}),o.on("close",()=>{for(const e of i.keys())u(e,1001,"Reverse tunnel disconnected.");for(const[e,t]of s)s.delete(e),t.controller.abort(),t.tunnel.close(1001,"Reverse tunnel disconnected.");a?.(),a=void 0}),o.on("message",async r=>{let a;try{a=JSON.parse(String(r))}catch{return void o.send(JSON.stringify({type:"node-agent.error",code:"INVALID_JSON"}))}const m=a&&"object"==typeof a?a:{};if("control-plane.http.open"===m.type){const e="string"==typeof m.streamId?m.streamId:"",r="string"==typeof m.route&&m.route.startsWith("/")?m.route:"/health",o=m.init&&"object"==typeof m.init?m.init:{},i=o.headers&&"object"==typeof o.headers?o.headers:{},a=new AbortController,c=(e=>{const t=new URL(n);return t.pathname=`${t.pathname.replace(/\/$/,"")}/http-streams/${encodeURIComponent(e)}`,t})(e),u=new l(c,{headers:d(c)});return s.set(e,{tunnel:u,controller:a}),u.on("open",async()=>{try{const n=await fetch((e=>{const n=e.startsWith("/")?e:`/${e}`,r="function"==typeof t.port?t.port():t.port;return new URL(`/api/node-agent${n}`,`http://127.0.0.1:${r}`)})(r),{method:tr(o.method),headers:{...i,...t.token?{authorization:`Bearer ${t.token}`}:{}},body:"string"==typeof o.body?o.body:void 0,signal:a.signal});if(await p(u,JSON.stringify({type:"node-agent.http.head",streamId:e,status:n.status,headers:Object.fromEntries(n.headers.entries())})),n.body){const e=n.body.getReader();for(;;){const{done:t,value:n}=await e.read();if(t)break;await p(u,Buffer.from(n),!0)}}await p(u,JSON.stringify({type:"node-agent.http.end",streamId:e})),u.close(1e3,"HTTP stream completed.")}catch(t){u.readyState===l.OPEN&&(await p(u,JSON.stringify({type:"node-agent.http.error",streamId:e,message:t instanceof Error?t.message:String(t)})).catch(()=>{}),u.close(1011,"HTTP stream failed."))}finally{s.delete(e)}}),u.on("close",()=>{a.abort(),s.delete(e)}),void u.on("error",()=>a.abort())}if("control-plane.websocket.open"===m.type){const e="string"==typeof m.streamId?m.streamId:"",r="string"==typeof m.route?m.route:"",s=Array.isArray(m.protocols)?m.protocols.filter(e=>"string"==typeof e):void 0;try{const a=t.token?{authorization:`Bearer ${t.token}`}:void 0,u=s?.length?new l(c(r),s,{headers:a}):new l(c(r),{headers:a}),p=(e=>{const t=new URL(n);return t.pathname=`${t.pathname.replace(/\/$/,"")}/streams/${encodeURIComponent(e)}`,t})(e),m=new l(p,{headers:d(p)});i.set(e,{upstream:u,tunnel:m,close:(e=1e3,t="")=>{u.close(e,t),m.close(e,t)}}),u.on("open",()=>{o.send(JSON.stringify({type:"node-agent.websocket.open",streamId:e,protocol:u.protocol}))}),function(e,t,n={}){const r=n.pendingFrameLimit??256;let o=!1,i=Tt(t);const s=[],a=[];let c;const d=()=>{c&&(clearTimeout(c),c=void 0)},l=(n,o)=>{if(n.length>=r)return Ct(e,1011,"WebSocket bridge pending frame limit exceeded."),void Ct(t,1011,"WebSocket bridge pending frame limit exceeded.");n.push(o)};n.upstreamOpenTimeoutMs&&!i&&(c=setTimeout(()=>{i||o||(Ct(e,1011,"Upstream websocket did not open."),Ct(t))},n.upstreamOpenTimeoutMs)),e.on("open",()=>Dt(e,a)),t.on("open",()=>{d(),i=!0,n.onUpstreamOpen?.(),Dt(t,s)}),e.on("message",(e,n=!1)=>{const r={data:e,isBinary:Boolean(n)};Tt(t)?Rt(t,r):l(s,r)}),t.on("message",(t,n=!1)=>{const r={data:t,isBinary:Boolean(n)};Tt(e)?Rt(e,r):l(a,r)}),t.on("close",(t,r)=>{d(),n.onUpstreamClose?.(t,r),(i||o||!n.onUpstreamCloseBeforeOpen?.())&&Ct(e,t,r)}),t.on("error",t=>{n.onUpstreamError?.(t),(i||o||!n.onUpstreamErrorBeforeOpen?.(t))&&Ct(e)}),e.on("close",(e,r)=>{d(),o=!0,n.onClientClose?.(e,r),Ct(t,e,r)}),e.on("error",e=>{d(),o=!0,n.onClientError?.(e),Ct(t,1011,"WebSocket bridge client failed.")})}(m,u,{pendingFrameLimit:256,upstreamOpenTimeoutMs:1e4,onClientClose:()=>{i.delete(e)},onClientError:t=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))},onUpstreamClose:(t,n)=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.close",streamId:e,code:"number"==typeof t?t:1e3,reason:Buffer.isBuffer(n)?n.toString("utf8"):String(n||"")}))},onUpstreamError:t=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))}})}catch(t){o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))}return}if("control-plane.websocket.close"===m.type){const e="string"==typeof m.streamId?m.streamId:"";return void u(e,"number"==typeof m.code?m.code:1e3,"string"==typeof m.reason?m.reason:"")}if("control-plane.request"===m.type){const n="string"==typeof m.requestId?m.requestId:"",r=m.init&&"object"==typeof m.init?m.init:{},i="string"==typeof m.route&&m.route.startsWith("/")?m.route:"/health",s=r.headers&&"object"==typeof r.headers?r.headers:{};try{const a=await e.inject({method:tr(r.method),url:`/api/node-agent${i}`,headers:{...s,...t.token?{authorization:`Bearer ${t.token}`}:{}},payload:"string"==typeof r.body?r.body:void 0});o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:a.statusCode,headers:a.headers,body:a.body}))}catch(e){o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:502,error:{code:"NODE_AGENT_REVERSE_INJECT_FAILED",message:e instanceof Error?e.message:String(e)}}))}}}),o}async function Ro(e){const o=Nn(e.dataDir),i=(s=e.host,a=e.port,ft.parse({bindScope:"0.0.0.0"===s.trim()?"all-ipv4":"loopback",port:a}));var s,a;const c=t.existsSync(o.settingsPath),d=function(e,t){return new Dn(e.settingsPath,()=>({version:1,externalListener:t}),{schema:Hr,sanitize:no})}(o,i),l=d.get().externalListener,u=function(e,r={}){return function(e,r={}){t.mkdirSync(n.dirname(e),{recursive:!0});const o={pid:process.pid,hostname:m.hostname(),component:r.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:p.randomUUID(),dataDir:r.dataDir,host:r.host,port:r.port};for(let n=0;n<5;n+=1)try{return t.mkdirSync(e),t.writeFileSync(Mn(e),`${JSON.stringify({token:o.token,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Un(e,o),{lockPath:e,owner:o,release(){const n=Fn(e);n?.token===o.token&&t.rmSync(e,{recursive:!0,force:!0})}}}catch(o){if("EEXIST"!==o.code)throw o;const i=Fn(e);if(Ln(i?.pid))throw new jn(e,i,r.error);if(!i&&Bn(e)<2e3){if(n+1<5){Kn();continue}throw new jn(e,void 0,r.error)}const s=p.randomUUID();if(!Gn(e,s)){if(n+1<5){Kn();continue}throw new jn(e,Fn(e),r.error)}const a=Fn(e);if(Ln(a?.pid))throw qn($n(e)),new jn(e,a,r.error);if(i?.token&&a?.token!==i.token){qn($n(e));continue}if(!Jn(e,s))continue;t.rmSync(e,{recursive:!0,force:!0})}const i=Fn(e);throw new jn(e,i,r.error)}(e,{...r,component:"node-agent",error:{label:"Node agent",code:"NODE_AGENT_ALREADY_RUNNING"}})}(n.join(m.tmpdir(),`task-handoff-node-agent-${process.getuid?.()??"user"}.lock`),{dataDir:o.dataDir,host:to(l.bindScope),port:l.port});try{const i={...e,port:l.port},s=await xo(i),a=function(e,t,n,r){const o=t.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,i=new $r(n),s=new Map,a=t=>{const n=t.url.replace(/\/$/,"");s.get(n)?.close(1e3,"Reverse tunnel reconnecting.");const i=Co(e,{tunnelUrl:To(t.url),nodeId:r,port:()=>e.nodeAgentState.currentListenerPort,token:o,keyId:t.keyId,secret:t.secret});return s.set(n,i),i.on("close",()=>{s.get(n)===i&&s.delete(n)}),i};return{connect:a,connectConfigured:()=>{const n=function(e){const t=e.controlPlaneTunnelUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_TUNNEL_URL;if(t)return t;const n=process.env.TASK_HANDOFF_CONTROL_PLANE_URL;if(!n)return;const r=new URL("/api/node-agent/tunnel",n);return r.protocol="https:"===r.protocol?"wss:":"ws:",r.toString()}(t);if(n){const a=i.reverseTunnelSecret(process.env.TASK_HANDOFF_CONTROL_PLANE_URL,t.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,t.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID),c=(process.env.TASK_HANDOFF_CONTROL_PLANE_URL||n).replace(/\/$/,"");s.get(c)?.close(1e3,"Reverse tunnel reconnecting.");const d=Co(e,{tunnelUrl:n,nodeId:r,port:()=>e.nodeAgentState.currentListenerPort,token:o,keyId:a?.keyId,secret:a?.secret});s.set(c,d)}for(const e of i.configuredRemoteControlPlanes())e.url&&!1!==e.active&&a({url:e.url,keyId:e.keyId,secret:e.secret})},closeAll:()=>{for(const e of s.values())e.close(1001,"Node agent shutting down.");s.clear()}}}(s,i,o,new $r(o).resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID));s.decorate("nodeAgentReverseTunnels",a);const p=new vo({app:s,state:s.nodeAgentState,settings:d,config:l,source:c?"persisted":"bootstrap"});let m;s.decorate("nodeAgentListenerManager",p),function(e){let t=!1;const n=async n=>{if(!t){t=!0,a.closeAll(),m?.close();try{await e.close()}finally{process.exitCode=n?0:process.exitCode}}},r=()=>{n("SIGINT")},o=()=>{n("SIGTERM")};process.once("SIGINT",r),process.once("SIGTERM",o),e.addHook("onClose",async()=>{process.off("SIGINT",r),process.off("SIGTERM",o)})}(s),s.addHook("onClose",async()=>{m?.close(),u.release()});try{await s.ready();const i=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||Wn(o.dataDir);m=await async function(e,o){!function(e,r=process.platform){if("win32"!==r){t.mkdirSync(n.dirname(e),{recursive:!0,mode:448});try{t.chmodSync(n.dirname(e),448)}catch{}try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}}(o);const i=r.createServer((t,n)=>{e.server.emit("request",t,n)});return i.on("upgrade",(t,n,r)=>{e.server.emit("upgrade",t,n,r)}),await new Promise((e,t)=>{i.once("error",t),i.listen(o,()=>{i.off("error",t),e()})}),i}(s,i),await p.start(),a.connectConfigured(),await(s.nodeAgentRestoreLocalInstances?.())}catch(e){throw await s.close(),e}}catch(e){throw u.release(),e}}function Do(e){const t=Number(e);if(!Number.isInteger(t)||t<=0||t>65535)throw new Error("Port must be a valid TCP port.");return t}function jo(e){if("local-ipc"!==e&&"local-loopback"!==e)throw new Error("Connection mode must be local-ipc or local-loopback.");return e}(async function(){const r=new e.Command;r.name("task-handoff-node-agent").description("Run a TaskHandoff node agent.").version(function(){try{return JSON.parse(t.readFileSync(n.resolve(__dirname,"..","package.json"),"utf8")).version||"unknown"}catch{return"unknown"}}()).option("--host <host>","Node agent host",process.env.TASK_HANDOFF_NODE_AGENT_HOST||"127.0.0.1").option("-p, --port <port>","Node agent port",Do,Number(process.env.TASK_HANDOFF_NODE_AGENT_PORT)||8091).option("--data-dir <path>","Node agent data directory").option("--token <token>","Bearer token required by the control plane").option("--connection-mode <mode>","Local control connection mode",jo).option("--ipc-path <path>","Unix socket path used by local-ipc mode").option("--control-plane-tunnel-url <url>","Control plane reverse WebSocket tunnel URL").action(async e=>{await Ro({host:e.host,port:e.port,dataDir:e.dataDir,token:e.token,connectionMode:e.connectionMode,ipcPath:e.ipcPath,controlPlaneTunnelUrl:e.controlPlaneTunnelUrl})}),await r.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@task-handoff/node-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Prebuilt TaskHandoff node agent runtime.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -24,9 +24,6 @@
|
|
|
24
24
|
"commander": "14.0.3",
|
|
25
25
|
"dingtalk-stream": "2.1.5",
|
|
26
26
|
"fastify": "5.8.5",
|
|
27
|
-
"marked": "15.0.12",
|
|
28
|
-
"marked-terminal": "7.3.0",
|
|
29
|
-
"ms": "2.1.3",
|
|
30
27
|
"semver": "7.8.5",
|
|
31
28
|
"telegramify-markdown": "1.3.3",
|
|
32
29
|
"write-file-atomic": "8.0.0",
|