@task-handoff/node-agent 0.0.7 → 0.0.8
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 +2 -1
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("node:stream"),c=require("fastify"),d=require("@fastify/websocket"),l=require("ws"),u=require("zod"),m=require("node:crypto"),p=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),z=u.z.enum(["running","waiting","idle","failed"]),A=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.enum(["review","rename","goal","compact"]);u.z.object({command:C,argument:u.z.string().trim().max(4e3).optional()}).strict().superRefine((e,t)=>{"rename"!==e.command||e.argument||t.addIssue({code:"custom",path:["argument"],message:"Rename requires a thread name."}),"review"!==e.command&&"compact"!==e.command||!e.argument||t.addIssue({code:"custom",path:["argument"],message:`${e.command} does not accept an argument.`})}),u.z.object({command:C,value:u.z.string().max(4e3).optional(),turnId:u.z.string().trim().min(1).max(240).optional()}).strict(),u.z.enum(["skill","app","plugin"]);const D=u.z.object({name:u.z.string().trim().min(1).max(240),path:u.z.string().trim().min(1).max(4096)}),R=u.z.discriminatedUnion("kind",[D.extend({kind:u.z.literal("skill"),path:u.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),D.extend({kind:u.z.literal("app"),path:u.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),D.extend({kind:u.z.literal("plugin"),path:u.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),j=u.z.array(R).max(20).default([]),P=u.z.enum(["plugin","skill","file","directory","app"]),M=u.z.object({kind:P,name:u.z.string().trim().min(1).max(240),description:u.z.string().trim().max(1e3).optional(),path:u.z.string().trim().min(1).max(4096),icon:u.z.string().trim().max(4096).optional()}).strict().superRefine((e,t)=>{"skill"!==e.kind||e.path.startsWith("/")||t.addIssue({code:"custom",path:["path"],message:"Skill paths must be absolute."}),"app"!==e.kind||/^app:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"App paths must use app://<id>."}),"plugin"!==e.kind||/^plugin:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"Plugin paths must use plugin://<id>."}),"file"!==e.kind&&"directory"!==e.kind||!e.path.startsWith("/")&&!e.path.split(/[\\/]+/).includes("..")||t.addIssue({code:"custom",path:["path"],message:"File and directory paths must be relative to the session cwd."})}),L=u.z.object({category:u.z.enum(["skills","plugins","apps","files"]),code:u.z.string().trim().min(1).max(160),message:u.z.string().trim().min(1).max(2e3)}).strict();u.z.object({sessionId:u.z.string().trim().min(1).max(120),providerSessionId:u.z.string().trim().min(1).max(240),cwd:u.z.string().trim().min(1).max(4096),candidates:u.z.array(M).max(1e3).default([]),diagnostics:u.z.array(L).max(20).default([])}).strict(),u.z.object({query:u.z.string().trim().max(240).default("")}).strict(),u.z.object({sessionId:u.z.string().trim().min(1).max(120),cwd:u.z.string().trim().min(1).max(4096),query:u.z.string().trim().max(240),requestId:u.z.string().trim().min(1).max(160),candidates:u.z.array(M.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:u.z.boolean().default(!1)}).strict();const F=u.z.object({message:u.z.string().trim().min(1).max(2e4),mode:T.optional()});F.extend({attachments:u.z.array(_).max(6).default([]),references:j}).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."})}),F.extend({attachments:u.z.array(x).max(6).default([]),references:j}).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 $=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([]),references:j,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(),U=u.z.object({pendingCount:u.z.number().int().min(0).default(0),items:u.z.array($).max(100).default([])}).strict().default({pendingCount:0,items:[]}),H=u.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),q=u.z.object({id:u.z.string().trim().min(1).max(240),status:u.z.enum(["running","completed"]),startedAt:u.z.string().datetime().optional(),completedAt:u.z.string().datetime().optional()}).strict(),K=u.z.object({id:u.z.string().trim().min(1).max(240),providerTurnId:u.z.string().trim().max(240).optional(),source:H.optional(),userPrompt:u.z.string().trim().optional(),status:u.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:A.optional(),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),lastMessageItemId:u.z.string().trim().max(240).optional(),contextCompactions:u.z.array(q).max(20).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(),B=K.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),G=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(K).max(50).optional(),status:z.default("running"),phase:A.default("unknown"),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),lastMessageItemId:u.z.string().trim().max(240).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:U}).strict();u.z.object({session:G,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 V=u.z.object({id:u.z.string().trim().min(1).max(120),agent:u.z.enum(["codex","claude"]),providerSessionId:u.z.string().trim().min(1).max(240),title:u.z.string().trim().max(240).optional(),userPrompt:u.z.string().trim().optional(),lastMessage:u.z.string().trim().optional(),cwd:u.z.string().trim().min(1).max(4096),lastActiveAt:u.z.string().datetime(),archivedAt:u.z.string().datetime()}).strict();u.z.object({schemaVersion:u.z.literal(1).default(1),items:u.z.array(V).max(50).default([])}).strict(),u.z.object({items:u.z.array(V).max(50).default([])}).strict(),u.z.object({item:V,turns:u.z.array(B).max(50).default([])}).strict(),u.z.object({disposition:u.z.enum(["resumed","already-open"]),aiSessionId:u.z.string().trim().min(1).max(120),providerSessionId:u.z.string().trim().min(1).max(240),appSessionId:u.z.string().trim().min(1).max(120)}).strict();const J=G.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,lastMessageItemId:!0,currentTool:!0,toolCallsSinceLastMessage:!0,subAgents:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),W=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(J).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:W}).strict(),W.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 Y=u.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),X=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:Y}).strict(),Q=u.z.object({meta:X,snapshot:W}).strict(),Z=u.z.object({meta:X,upserted:u.z.array(J).default([]),removed:u.z.array(u.z.string().trim().min(1).max(120)).default([])}).strict(),ee=u.z.object({meta:X,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),itemId:u.z.string().trim().min(1).max(240),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:Q}).strict(),u.z.object({type:u.z.literal("ai-session.patch"),payload:Z}).strict(),u.z.object({type:u.z.literal("ai-session.removed"),payload:ee}).strict()])).default([])}).strict();const te=u.z.object({source:H,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(),ne=te.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(K).max(50).optional(),status:z.optional(),phase:A.optional(),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),lastMessageItemId:u.z.string().trim().max(240).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(),re=te.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","context-compaction"]),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(),itemId:u.z.string().trim().max(240).optional(),status:z.optional(),phase:A.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(),contextCompaction:q.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("context-compaction"===e.kind?e.contextCompaction||t.addIssue({code:"custom",path:["contextCompaction"],message:"context-compaction events require contextCompaction"}):void 0!==e.contextCompaction&&t.addIssue({code:"custom",path:["contextCompaction"],message:"contextCompaction is only valid for context-compaction events"}),"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",[ne,re]);const oe=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()]),ie=u.z.union([oe,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()]),se=u.z.object({promptTemplate:u.z.string().trim().min(1).max(2e4)}).strict(),ae=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(),ce=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:ie,action:se,policy:ae.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:u.z.string().datetime(),updatedAt:u.z.string().datetime()}).strict(),de=u.z.object({type:u.z.literal("ai-session"),aiSessionId:u.z.string().trim().min(1).max(160)}).strict(),le=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:de,localName:u.z.string().trim().min(1).max(160).optional(),createdAt:u.z.string().datetime(),updatedAt:u.z.string().datetime()}).strict(),ue=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(),me=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:de,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(ce).default([]),deployments:u.z.array(le).default([]),runtime:u.z.array(ue).default([]),recentRuns:u.z.array(me).default([])}).strict();const pe="2026-07-25",ge=u.z.string(),he=u.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),fe=u.z.string().datetime(),ye=u.z.record(u.z.string(),u.z.string()).default({}),Ie=u.z.record(u.z.string(),u.z.string()).default({}),be=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,ze=/^sha256:([a-fA-F0-9]{64})$/,Ae=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,we=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function Se(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?ze.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&&!be.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 m=u.length>1&&(u[0].includes(".")||u[0].includes(":")||"localhost"===u[0]),p=m?u.slice(1):u;if(m&&!Ae.test(u[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!p.length||p.some(e=>!we.test(e)))throw new Error("Docker image repository names must be lowercase and use valid separators.");const g=u.map((e,t)=>m&&0===t?e.toLowerCase():e).join("/"),h=i?`sha256:${i[1].toLowerCase()}`:void 0;return`${g}${d?`:${d}`:""}${h?`@${h}`:""}`}const ve=u.z.string().trim().min(1).max(512).transform((e,t)=>{try{return Se(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),u.z.NEVER}}),Ne=u.z.string().trim().transform((e,t)=>{const n=ze.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)}),Ee=u.z.object({codexModelHash:he.nullable().optional(),claudeModelHash:he.nullable().optional()}).strict().default({}),ke=u.z.object({id:he,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(),_e=u.z.object({code:u.z.enum(["APP_CATALOG_INVALID"]),message:u.z.string().trim().min(1).max(500)}).strict(),Oe=u.z.object({items:u.z.array(ke).max(256),observedAt:fe,issues:u.z.array(_e).max(32).default([])}).strict(),xe=u.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),Te=u.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),Ce=u.z.enum(["apt","dnf","brew","npm"]),De=u.z.enum(["user","passwordless-sudo","root"]),Re=u.z.object({platform:xe,arch:Te,installers:u.z.array(Ce).max(8),privilege:De,installerAccess:u.z.object({npmGlobalWritable:u.z.boolean().optional()}).strict().optional()}).strict(),je=u.z.enum(["installed","not-installed","broken","unsupported"]),Pe=u.z.enum(["recipe","bundled","external","none"]),Me=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(),Le=u.z.object({id:he,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:je,managementSource:Pe,version:u.z.string().trim().max(120).optional(),canInstall:u.z.boolean(),canUninstall:u.z.boolean(),installReason:Me.optional(),uninstallReason:Me.optional(),activeJobId:he.optional()}).strict(),Fe=u.z.enum(["install","uninstall"]),$e=u.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),Ue=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"]}),He=u.z.object({executable:u.z.string().trim().min(1).max(4096),args:u.z.array(u.z.string().max(4096)).max(128)}).strict(),qe=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(he).max(64).optional()}).strict(),Ke=u.z.object({id:he,requestId:he.optional(),appId:he,operation:Fe,state:$e,phase:u.z.string().trim().min(1).max(120).optional(),progress:Ue.optional(),command:He.optional(),logTail:u.z.string().max(32768).optional(),logTruncated:u.z.boolean().optional(),error:qe.optional(),requestedAt:fe,startedAt:fe.optional(),finishedAt:fe.optional(),updatedAt:fe}).strict(),Be=u.z.object({streamId:he,sequence:u.z.number().int().nonnegative(),capabilities:Re,apps:u.z.array(Le).max(256),activeJobs:u.z.array(Ke).max(256),recentJobs:u.z.array(Ke).max(256),observedAt:fe}).strict();u.z.object({requestId:he.optional()}).strict(),u.z.object({job:Ke}).strict(),u.z.object({type:u.z.literal("app-management"),streamId:he,sequence:u.z.number().int().nonnegative(),observedAt:fe,job:Ke.optional(),snapshot:Be.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const Ge=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:ge.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(),Ve=u.z.enum(["stable","beta","alpha"]),Je=u.z.discriminatedUnion("component",[u.z.object({component:u.z.literal("node-agent")}).strict(),u.z.object({component:u.z.literal("controlled-instance"),instanceId:he}).strict()]),We=u.z.object({target:Je,channel:Ve.default("stable")}).strict(),Ye=u.z.object({target:Je,source:u.z.enum(["npm","docker-registry"]),channel:Ve,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:fe}).strict(),Xe=u.z.object({id:he,nodeId:he,target:Je,source:u.z.enum(["npm","docker-registry"]),channel:Ve,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:fe.optional(),completedAt:fe.optional(),createdAt:fe,updatedAt:fe}).strict(),Qe=We,Ze=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(),et=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(),tt=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:he,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:Ze.default({type:"branch",name:"main"}),auth:et.default({type:"none"}),clone:tt.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:he.optional(),labels:ye,createdAt:fe,updatedAt:fe}).strict();const nt=u.z.object({id:he,nodeId:he,name:u.z.string().trim().min(1).max(160),path:u.z.string().trim().min(1).max(4096),defaultImageId:he.optional(),labels:ye,createdAt:fe,updatedAt:fe}).strict(),rt=u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("git-repository"),repositoryId:he.optional(),url:u.z.string().trim().min(1).max(2048),provider:u.z.string().trim().max(80).optional(),ref:Ze.default({type:"branch",name:"main"}),auth:et.default({type:"none"}),clone:tt.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:Ze.default({type:"branch",name:"main"}),auth:et.default({type:"none"}),clone:tt.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),u.z.object({type:u.z.literal("local-folder"),localFolderId:he.optional(),path:u.z.string().trim().min(1).max(4096),ownerNodeId:he.optional()}).strict()]),ot=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(),it=u.z.enum(["codex","claude"]),st=u.z.object({id:he,name:u.z.string().trim().min(1).max(160),source:rt,defaultImageId:he.optional(),defaultNodeId:he.optional(),defaultRuntimeId:he.optional(),workspacePolicy:ot,labels:ye,createdAt:fe,updatedAt:fe}).strict(),at=u.z.object({id:he,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:it,enabled:u.z.boolean().default(!0),order:u.z.number().int().min(0).max(1e6).default(0),labels:ye,createdAt:fe,updatedAt:fe}).strict(),ct=at,dt=at.omit({key:!0}).extend({keyPreview:u.z.string().trim().min(1).max(160),keySet:u.z.boolean()}).strict(),lt=dt.extend({referenceCount:u.z.number().int().min(0)}).strict(),ut=u.z.object({instanceId:he,codexModelHash:he.optional(),claudeModelHash:he.optional(),updatedAt:fe}).strict(),mt=u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("control-plane"),name:at.shape.name,enabled:at.shape.enabled,order:at.shape.order}).strict(),u.z.object({type:u.z.literal("node"),nodeId:he,name:at.shape.name,enabled:at.shape.enabled,order:at.shape.order,referenceCount:u.z.number().int().min(0)}).strict()]),pt=u.z.object({id:he,model:dt,locations:u.z.array(mt).min(1),referenceCount:u.z.number().int().min(0).default(0)}).strict(),gt=u.z.object({nodeId:he,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(pt),nodeDiagnostics:u.z.array(gt).default([]),updatedAt:fe}).strict();const ht=at.omit({id:!0,createdAt:!0,updatedAt:!0}).strict(),ft=ht.partial().strict(),yt=at,It=u.z.object({modelSelection:Ee,codexModelHash:he.optional(),claudeModelHash:he.optional()}).strict();function bt(e){const t={app:it.parse(e.app),endpoint:at.shape.endpoint.parse(e.endpoint),key:at.shape.key.parse(e.key),model:at.shape.model.parse(e.model)};return`mdl_${m.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}const zt=u.z.object({id:he,name:u.z.string().trim().min(1).max(160),reference:ve,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:Ie,labels:ye,createdAt:fe,updatedAt:fe}).strict(),At=zt.omit({reference:!0}).extend({requestedReference:ve,resolvedDigest:Ne.optional(),resolvedReference:ve.optional()}).strict(),wt=u.z.object({phase:u.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:ve,generation:u.z.number().int().nonnegative().default(0),error:u.z.string().trim().max(4096).optional(),startedAt:fe,updatedAt:fe}).strict();const St=u.z.object({id:he,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:he.optional(),secret:u.z.string().trim().max(4096).optional(),pairedAt:fe.optional(),pairing:u.z.object({status:u.z.enum(["pending","paired","expired"]).default("pending"),joinToken:u.z.string().trim().max(4096).optional(),expiresAt:fe.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:Oe.optional(),labels:ye,lastSeenAt:fe.optional(),createdAt:fe,updatedAt:fe}).strict(),vt=u.z.object({id:he,nodeId:he,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:ye,createdAt:fe,updatedAt:fe}).strict(),Nt=u.z.object({instanceId:he,runtimeKind:u.z.literal("docker"),state:u.z.enum(["pending","available","stopped","unavailable"]),sampledAt:fe,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:he.optional()}).passthrough();const Et=u.z.object({bindScope:u.z.enum(["loopback","all-ipv4"]),port:u.z.number().int().min(1).max(65535)}).strict(),kt=Et.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(),_t=Et;u.z.object({nodeId:he,joinToken:u.z.string().trim().min(1),expiresAt:fe}).passthrough(),u.z.object({id:he,keyId:he,url:u.z.string().trim().min(1).max(2048).optional(),name:u.z.string().trim().min(1).max(160).optional(),pairedAt:fe,updatedAt:fe.optional(),active:u.z.boolean().optional(),current:u.z.boolean().optional()}).passthrough(),u.z.object({remote:u.z.object({id:he,url:u.z.string().trim().min(1).max(2048).optional(),keyId:he,pairedAt:fe,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 Ot=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:zt,status:u.z.enum(["available","pull-required","unknown"]),localImage:Ot.optional(),error:u.z.string().trim().max(2048).optional()}).strict();const xt=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(xt)}).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 Tt=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(),Ct=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"}),Dt=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(),Rt=u.z.object({id:he,name:u.z.string().trim().min(1).max(160),projectId:he.optional(),source:rt,sourceSnapshot:u.z.record(u.z.string(),u.z.unknown()).default({}),modelSelection:Ee,nodeId:he,runtimeId:he,imageId:he.optional(),imageSnapshot:At.optional(),imageProvisioning:wt.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:ge.optional(),instanceVersion:u.z.string().trim().max(80).optional(),build:Ge.optional(),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),appInventory:Oe.optional(),config:u.z.object({autoImportAgentConfigs:u.z.boolean().default(!0)}).strict().default({autoImportAgentConfigs:!0}),workspace:Dt.default({status:"unknown"}),target:Tt.default({strategy:"direct-port",status:"unknown"}),access:Ct,apps:u.z.object({runningCount:u.z.number().int().min(0).default(0),problemCount:u.z.number().int().min(0).default(0),updatedAt:fe.optional(),revision:u.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:W.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:ce,deployments:u.z.array(le).default([]),runtime:u.z.array(ue).default([])}).strict()).default([]),recentRuns:u.z.array(me).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:fe.optional(),createdAt:fe,updatedAt:fe}).strict();function jt(e,t){return Rt.safeParse(Pt(e,t))}function Pt(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,r=new Set(Object.keys(Rt.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=Mt(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Mt(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Mt(e,["code","message"])):t.issues},r=Oe.safeParse(n);return r.success?r.data:void 0}(n.appInventory),o.apps=Mt(n.apps,["runningCount","problemCount","updatedAt","revision"]),o.config=Mt(n.config,["autoImportAgentConfigs"]),o.modelSelection=Mt(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=Se(r)}catch{if(!r.includes("@")&&r.lastIndexOf(":")<=r.lastIndexOf("/"))try{r=Se(`${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=Mt(n.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),o.runtime=Mt(n.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),o.target=Mt(n.target??n.endpoints,["strategy","web","api","status"]),o.access=Mt(n.access,["strategy","web","api","ws","status"]),o}function Mt(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 Lt=u.z.object({instanceId:he.optional(),name:u.z.string().trim().min(1).max(160),projectId:he.optional(),source:rt.optional(),nodeId:he.optional(),runtimeId:he.optional(),imageId:he.optional(),instanceVersion:u.z.string().trim().max(80).optional(),protocolVersion:ge,build:Ge.optional(),controlMode:u.z.enum(["standalone","controlled"]).default("controlled"),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),appInventory:Oe.optional(),target:Tt.default({strategy:"direct-port",status:"unknown"}),workspace:Dt.default({status:"unknown"}),registrationToken:u.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==pe||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),Ft=u.z.object({status:Rt.shape.status.optional(),health:Rt.shape.health.optional(),protocolVersion:ge,build:Ge.optional(),capabilities:Rt.shape.capabilities.optional(),appInventory:Oe.optional(),apps:Rt.shape.apps.optional(),aiSessions:Rt.shape.aiSessions.optional(),triggers:Rt.shape.triggers.optional(),workspace:Dt.optional(),target:Tt.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==pe||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})}),$t=u.z.enum(["web","telegram","wechat","dingding"]);function Ut(e){return e.readyState===(e.OPEN??1)}function Ht(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 qt(e,t){e.send(t.data,{binary:t.isBinary})}function Kt(e,t){if(Ut(e))for(const n of t.splice(0))qt(e,n)}function Bt(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:he,channel:$t,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:fe,updatedAt:fe}).strict(),u.z.object({id:he,channel:$t,bridgeId:he.optional(),chatSessionId:u.z.string().trim().min(1).max(240),userId:u.z.string().trim().max(240).optional(),activeProjectId:he.optional(),activeInstanceId:he.optional(),activeAiSessionId:u.z.string().trim().min(1).max(120).optional(),lastUsedAt:fe,createdAt:fe,updatedAt:fe}).strict(),u.z.object({source:u.z.object({type:u.z.literal("chat-gateway").default("chat-gateway"),channel:$t,bridgeId:he.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:he,aiSessionId:u.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),u.z.object({id:he,instanceId:he,projectId:he.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:fe}).strict();class Gt{inFlight=new Map;runCommand;constructor(e=Bt){this.runCommand=e}ensure(e,t){const n=Se(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 Vt{runCommand;publishHost;images;constructor(e=Bt,t={}){this.runCommand=e,this.images=t.imageService||new Gt(e),this.publishHost=t.publishHost||"127.0.0.1"}async start(e){Yt(e);const t=e.instance.runtime.containerName||Wt(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||Wt(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){Yt(e);const t=e.instance.runtime.containerName||Wt(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||Wt(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 Jt(e=Bt){return async function(e=Bt){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 Wt(e){return`task-handoff-${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`}function Yt(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 Xt=u.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),Qt=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(),Zt=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:Xt.default("unknown"),bindings:u.z.array(Qt).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()})),en=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(Zt).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:en}).strict(),en.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:u.z.number().int().min(0).optional()});const tn=u.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),nn=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:tn}).strict(),rn=u.z.object({meta:nn,snapshot:en}).strict(),on=u.z.object({meta:nn,session:Zt}).strict(),sn=u.z.object({meta:nn,sessionId:u.z.string().trim().min(1).max(160),tombstone:Zt.optional(),expiresAt:u.z.string().datetime().optional()}).strict();function an(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:rn}).strict(),u.z.object({type:u.z.literal("app-session.patch"),payload:on}).strict(),u.z.object({type:u.z.literal("app-session.removed"),payload:sn}).strict()])).default([])}).strict();const cn=u.z.enum(["ai.sessions","app.sessions"]),dn=u.z.object({topic:cn,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(),ln=u.z.object({protocolVersion:u.z.literal(1),streams:u.z.array(dn)}).strict();class un{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 mn{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:an(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=gn(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 un,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:an(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=ln.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&&gn(t);if(!n||!this.outputs.size)return;const r=this.retries.get(e)??{timer:new un};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=pn(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=pn(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 pn(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 gn(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 hn=new Set(["provisioning","starting","registering","registered","running","unhealthy"]),fn=JSON.stringify({id:"{{.ID}}",name:"{{.Name}}",cpu:"{{.CPUPerc}}",memory:"{{.MemUsage}}",memoryPercent:"{{.MemPerc}}",network:"{{.NetIO}}",blockIo:"{{.BlockIO}}",pids:"{{.PIDs}}"});class yn{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 hn.has(t.status)?(await this.collect([t]),this.snapshots.get(e)):wn(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=>hn.has(e.status)&&(e.runtime.containerId||e.runtime.containerName));for(const t of e.filter(e=>hn.has(e.status)&&!e.runtime.containerId&&!e.runtime.containerName))this.update(Sn(t.id));for(const t of e.filter(e=>!hn.has(e.status)))this.update(wn(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",fn,...n],{timeoutMs:5e3});this.applyDockerOutput(t,e.stdout,r)}catch(e){if(1===t.length)return void this.update(vn(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",fn,t],{timeoutMs:5e3});this.applyDockerOutput([e],n.stdout,r)}catch(t){this.update(vn(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=An(e.id),i=An(e.name),s=o||i;if(!s)continue;const a=In(An(e.memory)),c=In(An(e.network)),d=In(An(e.blockIo)),l=Nt.parse({instanceId:s,runtimeKind:"docker",state:"available",sampledAt:t,cpu:bn(e.cpu,"usagePercent"),memory:a?{usageBytes:a[0],...void 0!==a[1]?{limitBytes:a[1]}:{},...bn(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:zn(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}:vn(t.id,n,"Container metrics were not returned by Docker."))}}update(e){const t=Nt.parse(e);this.snapshots.set(t.instanceId,t),this.publish?.(t)}}function In(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 bn(e,t){const n=Number(An(e).replace(/%$/,""));return Number.isFinite(n)&&n>=0?{[t]:n}:void 0}function zn(e){const t=Number(An(e));return Number.isFinite(t)&&t>=0?t:void 0}function An(e){return"string"==typeof e||"number"==typeof e?String(e).trim():""}function wn(e){return{instanceId:e,runtimeKind:"docker",state:"stopped",sampledAt:(new Date).toISOString()}}function Sn(e){return{instanceId:e,runtimeKind:"docker",state:"pending",sampledAt:(new Date).toISOString()}}function vn(e,t,n){return{instanceId:e,runtimeKind:"docker",state:"unavailable",sampledAt:t,error:n.slice(0,2048)}}function Nn(e){try{return t.statSync(e).isDirectory()}catch{return!1}}function En(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()&&Nn(n.join(i,e.name))).sort((e,t)=>e.name.localeCompare(t.name)).slice(0,80).flatMap(e=>{const t=En(n.join(i,e.name),r-1);return t?[t]:[]}):[];return{name:s,path:i,children:c}}function kn(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(Nn)}()).flatMap(t=>{const n=En(t,e.depth);return n?[n]:[]})}const _n=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(),On=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(),xn=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(),Tn=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:zt.optional(),source:rt,sourceSnapshot:u.z.record(u.z.string(),u.z.unknown()).default({}),config:Rt.shape.config.optional(),modelSelection:Rt.shape.modelSelection}).strict(),Cn=u.z.object({name:u.z.string().trim().min(1).max(160).optional(),config:Rt.shape.config.optional(),modelSelection:Rt.shape.modelSelection.optional()}).strict(),Dn=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(),Rn=Dn.omit({id:!0}).partial().strict();function jn(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 Pn(e){t.mkdirSync(e,{recursive:!0})}function Mn(e,t){Pn(n.dirname(e)),g.sync(e,`${JSON.stringify(t,null,2)}\n`,{encoding:"utf8"})}function Ln(e,t){console.warn(JSON.stringify({message:e,...t}))}function Fn(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function $n(e,n){const r=n.logger||Ln;let o;try{o=JSON.parse(t.readFileSync(e,"utf8"))}catch(t){return void r("stored JSON could not be read",{filePath:e,...Fn(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 Un(e){return`${e}_${m.randomUUID().replace(/-/g,"").slice(0,20)}`}function Hn(){return m.randomBytes(24).toString("base64url")}class qn{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){Pn(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=$n(n.join(this.directory,e),this.options);return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?$n(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 Mn(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 Kn{filePathValue;defaults;options;constructor(e,t,n={}){this.filePathValue=e,this.defaults=t,this.options=n}init(){Pn(n.dirname(this.filePathValue)),t.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),t.existsSync(this.filePathValue)?$n(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 Mn(this.filePathValue,n),n}}class Bn 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 Gn(e){return n.join(e,"owner.json")}function Vn(e){return n.join(e,"initializing.json")}function Jn(e){return n.join(e,"recovering.json")}function Wn(e){try{const n=JSON.parse(t.readFileSync(Gn(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 Yn(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 Xn(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=Qn(e);if(i?.token!==r.token)throw Zn(o),new Error(`Process lock initialization was superseded: ${e}`);t.renameSync(o,Gn(e)),Zn(Vn(e))}function Qn(e){try{return JSON.parse(t.readFileSync(Vn(e),"utf8"))}catch{return}}function Zn(e){try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function er(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function tr(e){try{const n=Qn(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 nr(e,r){try{return t.writeFileSync(Jn(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=Jn(e),o=rr(r),i=Date.parse(o?.startedAt||"");if(Yn(o?.pid)||Number.isFinite(i)&&Date.now()-i<2e3)return!1;const s=n.join(e,`.recovering-${m.randomUUID()}.stale`);try{t.renameSync(r,s)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const a=rr(s);if(a?.token===o?.token)return Zn(s),!0;try{t.linkSync(s,r)}catch(e){if("EEXIST"!==e.code)throw e}finally{Zn(s)}return!1}(e)&&nr(e,r);if("ENOENT"===o.code)return!1;throw o}}function rr(e){try{return JSON.parse(t.readFileSync(e,"utf8"))}catch{return}}function or(e,t){return rr(Jn(e))?.token===t}function ir(e,t=process.platform){const r=n.resolve(e),o=m.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 sr="TASK_HANDOFF_NODE_AGENT_V1";function ar(e=""){return m.createHash("sha256").update(e).digest("hex")}function cr(e,t){return m.createHmac("sha256",e).update(function(e){return[sr,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function dr(e){const t=e.timestamp||(new Date).toISOString(),n=e.nonce||m.randomBytes(18).toString("base64url"),r=ar(e.body||""),o=cr(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 lr=new Set(["DELETE","GET","HEAD","PATCH","POST","PUT","OPTIONS"]);function ur(e){return"string"==typeof e&&lr.has(e.toUpperCase())?e.toUpperCase():"GET"}function mr(){return(new Date).toISOString()}function pr(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 gr(e,t,n){const r=yr(n),o=await e(hr(),["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 hr(){return process.env.TASK_HANDOFF_NPM_COMMAND||"npm"}function fr(e){return Ye.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:mr()})}function yr(e){return"stable"===e?"latest":e}function Ir(e){if(Array.isArray(e))return e.map(Ir).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(Ir).find(Boolean)}function br(e){return{x64:"amd64",ia32:"386",arm:"arm",arm64:"arm64",ppc64:"ppc64le",s390x:"s390x",riscv64:"riscv64"}[e]||e}function zr(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 Ar{records;constructor(e){this.records=new qn(e.updatesDir,{schema:Xe})}init(){this.records.init()}list(){return this.records.list().sort((e,t)=>t.createdAt.localeCompare(e.createdAt))}create(e,t){const n=mr();return this.records.put(Xe.parse({id:Un("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(Xe.parse({...n,...t,id:e,createdAt:n.createdAt,updatedAt:mr()}))}run(e,t){(async()=>{this.patch(e.id,{status:"updating",startedAt:mr(),error:void 0});try{await t(e),this.patch(e.id,{status:"succeeded",completedAt:mr()})}catch(t){this.patch(e.id,{status:"failed",error:t instanceof Error?t.message:String(t),completedAt:mr()})}})()}}const wr=u.z.record(u.z.string(),u.z.string()),Sr=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 vr{directory;constructor(e){this.directory=e}get(e){try{return wr.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=wr.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 Nr{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=ct.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=ct.safeParse(r);if(i.success&&i.data.id===o&&bt(i.data)===i.data.id)return i.data;const s=ct.strip().safeParse(r);return s.success&&s.data.id===o&&bt(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 Er{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=ut.safeParse(r);if(o.success)return o.data;const i=ut.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=ut.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 kr{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=ar(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&&m.timingSafeEqual(n,r)}(t.signature,cr(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 _r=u.z.string().trim().min(1).max(160),Or=u.z.string().datetime(),xr=u.z.string().min(1).max(4096),Tr=u.z.string().trim().url().max(2048),Cr=u.z.object({tokenHash:u.z.string().trim().min(1).max(256),expiresAt:Or,createdAt:Or,controlPlaneName:u.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:Tr.optional()}).strip(),Dr=u.z.object({id:_r,keyId:_r,name:u.z.string().trim().min(1).max(160).optional(),url:Tr.optional(),secret:xr,pairedAt:Or,updatedAt:Or,active:u.z.boolean().default(!0)}).strip(),Rr=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(),jr=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(),Pr=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 Mr(){return(new Date).toISOString()}const Lr=new Set(["nodeId","createdAt","updatedAt","pairingInvites","remoteControlPlanes"]),Fr=new Set(["tokenHash","expiresAt","createdAt","controlPlaneName","controlPlaneUrl"]),$r=new Set(["id","keyId","name","url","secret","pairedAt","updatedAt","active"]);function Ur(e,t){console.warn(JSON.stringify({message:e,...t}))}function Hr(e,t){return Object.assign(new Error(e,void 0===t?void 0:{cause:t}),{code:"NODE_AGENT_IDENTITY_INVALID"})}function qr(e,t){return Object.keys(e).filter(e=>!t.has(e))}function Kr(e,t,n){const r=Or.safeParse(e);return r.success?r.data:(n("invalid node agent identity timestamp was replaced",{field:t}),Mr())}function Br(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw Hr("Node agent identity must be a JSON object.");const n=e,r=_r.safeParse(n.nodeId);if(!r.success)throw Hr("Node agent identity has an invalid nodeId.",r.error);const o=r.data,i=qr(n,Lr);return i.length&&t("unknown node agent identity fields were ignored",{fields:i}),{nodeId:o,createdAt:Kr(n.createdAt,"createdAt",t),updatedAt:Kr(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=qr(e,Fr);n.length&&t("unknown node agent pairing invite fields were ignored",{fields:n});const r=Cr.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=qr(e,$r);n.length&&t("unknown node agent remote control-plane fields were ignored",{fields:n});const r=Dr.safeParse(e);return r.success?[r.data]:(t("invalid node agent remote control-plane was ignored",{issues:r.error.issues}),[])}):[]}}class Gr{paths;logger;constructor(e,t={}){this.paths=e,this.logger=t.logger||Ur}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 Hr(`Node agent identity contains invalid JSON: ${this.paths.identityPath}`,e)}return Br(n,this.logger)}write(e){t.mkdirSync(n.dirname(this.paths.identityPath),{recursive:!0});const r=Br({...e,updatedAt:Mr()},this.logger);g.sync(this.paths.identityPath,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384})}}function Vr(){return(new Date).toISOString()}class Jr{store;constructor(e){this.store=new Gr(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=Un("node");return this.writeNodeId(n),n}createPairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=Vr(),r=new Date(Date.now()+(e.expiresInMs||6e5)).toISOString(),o=Hn(),i={tokenHash:ar(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=ar(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=Vr(),i={id:e.controlPlaneId||Un("cp"),keyId:Un("key"),...e.controlPlaneName||r.controlPlaneName?{name:e.controlPlaneName||r.controlPlaneName}:{},...e.controlPlaneUrl||r.controlPlaneUrl?{url:e.controlPlaneUrl||r.controlPlaneUrl}:{},secret:Hn(),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=Vr();return{id:Un("cp"),keyId:Un("key"),...e.name?{name:e.name}:{},url:e.url,secret:Hn(),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:Vr(),updatedAt:Vr(),pairingInvites:[],remoteControlPlanes:[]}}writeNodeId(e){const t=this.store.read();this.store.write({...t||{createdAt:Vr()},nodeId:e,updatedAt:Vr(),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 Wr=new Set(["content-encoding","content-length","transfer-encoding"]),Yr=["codex","claude"],Xr=5e3,Qr=u.z.object({version:u.z.literal(1),externalListener:Et}).strict(),Zr=u.z.object({}).strict().default({}),eo=Qe.strict();function to(e){const t=process.env[e]?.trim();return t||void 0}function no(){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 ro(e){return Object.fromEntries([...e.entries()].filter(([e])=>!Wr.has(e.toLowerCase())))}function oo(e){return e.bodyBase64?Buffer.from(e.bodyBase64,"base64"):e.body}const io=67108864;function so(){const e=Number(process.env.TASK_HANDOFF_INSTANCE_PROXY_MAX_RESPONSE_BYTES);return Number.isSafeInteger(e)&&e>0?e:67108864}function ao(e){const t=e.authorization;return"string"==typeof t&&t.startsWith("Bearer ")?t.slice(7).trim():void 0}function co(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 lo(e){return!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}function uo(){return(new Date).toISOString()}function mo(e){return"all-ipv4"===e?"0.0.0.0":"127.0.0.1"}function po(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 go(e){return"local-folder"===e.type?ot.parse({mode:"local-bind",path:"/workspace",readOnly:!1}):ot.parse({mode:"git-clone",path:"/workspace",readOnly:!1})}function ho(e){const t=rt.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 st.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:go(t),labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt})}async function fo(e,t){if(!t.target.web)return"unknown";const n=new AbortController,r=setTimeout(()=>n.abort(),2e3);try{return(await e(`${zo(t)}/api/health`,{signal:n.signal})).ok?"reachable":"endpoint-unreachable"}catch{return"endpoint-unreachable"}finally{clearTimeout(r)}}async function yo(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 Io(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=zo(t),i=Number(process.env.TASK_HANDOFF_CONFIG_AUTO_IMPORT_TIMEOUT_MS||Xr);for(const s of Yr)try{const a=await yo(e,`${o}/api/config-sync/import/${encodeURIComponent(s)}`,{method:"POST"},Number.isFinite(i)&&i>0?i:Xr);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 bo(e,t,n){const r=t.requireInstance(n);if("reachable"!==r.targetStatus||!r.target.web)return!1;const o=await yo(e,`${zo(r)}/api/internal/model-environment`,{method:"PUT",headers:{"content-type":"application/json",authorization:`Bearer ${r.registrationToken}`},body:JSON.stringify(t.resolvedAssignedModelEnvironment(n))},Xr);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 zo(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 Ao(e,t){const n=new Error(t);throw Object.assign(n,{statusCode:403,code:e}),n}function wo(e,t){e!==pe&&console.warn(JSON.stringify({message:"protocol version mismatch",peer:t,expectedProtocolVersion:pe,actualProtocolVersion:e||"missing",errorCode:"PROTOCOL_VERSION_MISMATCH"}))}function So(e={}){return{requiresImage:!1,supportsControlledInstanceApi:!0,supportsContainerLifecycle:!1,supportsAppSessions:!0,supportsHostSessions:!0,artifactKind:"none",isolation:"none",...e}}function vo(){const e=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV?.trim();if(e){let t;try{t=JSON.parse(e)}catch{const e=new Error("TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV must be a JSON array of command arguments.");throw Object.assign(e,{statusCode:500,code:"LOCAL_CONTROLLED_COMMAND_INVALID"}),e}if(!Array.isArray(t)||0===t.length||t.some(e=>"string"!=typeof e||0===e.length)){const e=new Error("TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV must be a non-empty JSON array of non-empty strings.");throw Object.assign(e,{statusCode:500,code:"LOCAL_CONTROLLED_COMMAND_INVALID"}),e}return t}const r=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND?.trim();if(r)return r.split(/\s+/);const o=n.resolve(process.cwd(),"bin","task-handoff.js");if(t.existsSync(o))return[process.execPath,o,"web"];const i=new Error("No local controlled-instance command is configured. Set TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV to an explicit command argv.");throw Object.assign(i,{statusCode:500,code:"LOCAL_CONTROLLED_COMMAND_MISSING"}),i}function No(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 Eo(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 ko(e,t){const n=t.get(e.id);if(n&&!n.killed)return n.kill("SIGTERM"),t.delete(e.id),void await Eo(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 _o=new Set(["provisioning","starting","registering","registered","running"]);async function Oo(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 xo{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 To{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 No(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]=vo(),l=[...d,"--host","127.0.0.1","--port",String(o)],u=t.openSync(n.join(a,"controlled-instance.out.log"),"a"),m=t.openSync(n.join(a,"controlled-instance.err.log"),"a"),p=s.spawn(c,l,{cwd:r,detached:!1,stdio:["ignore",u,m],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(m);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)})}(p)}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,p),p.on("error",e=>{try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] process error pid=${p.pid??""} error=${e.message}\n`)}catch{}}),p.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=${p.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:p.pid,port:o,labels:{...e.instance.runtime.labels,"task-handoff.runtime-kind":"local"}}}}async stop(e){return await ko(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 ko(e.instance,this.processByInstanceId),this.start(e)}async delete(e){return this.stop(e)}async check(e){const[t,n]=await Promise.all([Oo(this.runCommand,"codex"),Oo(this.runCommand,"claude")]);return{status:"online",capabilities:So({...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=>Eo(e)))}}class Co{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 To&&await this.local.stopAll()}}class Do{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 qn(e.localFoldersDir,{schema:nt}),this.nodeRuntimes=new qn(e.nodeRuntimesDir,{schema:vt}),this.controlledInstances=new qn(e.controlledInstancesDir,{schema:Rt,sanitize:e=>Pt(e,e=>{console.warn(JSON.stringify({message:"legacy controlled instance field was ignored",...e}))})}),this.models=new Nr(e.nodeModelsDir,t),this.modelAssignments=new Er(e.modelAssignmentsDir),this.modelEnvironments=new vr(e.modelEnvironmentsDir),this.updateJobs=new Ar(e),this.listenerPort=o,this.containerUrlOverride=r;const i=uo();this.node=St.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=uo();this.nodeRuntimes.put(vt.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(vt.parse({...e,nodeId:this.nodeId,updatedAt:uo()}));for(const e of this.localFolders.list())e.nodeId!==this.nodeId&&this.localFolders.put(nt.parse({...e,nodeId:this.nodeId,updatedAt:uo()}))}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=uo()}runningInstanceCount(){const e=new Set(["created","stopped","failed"]);return this.listInstances().filter(t=>!e.has(t.status)).length}createLocalFolder(e){const t=uo(),n=nt.parse({...e,id:e.id||Un("folder"),nodeId:this.nodeId,labels:e.labels||{},createdAt:t,updatedAt:t});return this.localFolders.put(n)}createRuntime(e){const t=uo(),n=vt.parse({...e,id:e.id||Un("runtime"),nodeId:this.nodeId,accessStrategy:e.accessStrategy||(r=e.type,"docker"===r?"direct-port":"node-proxy"),capabilities:"local"===e.type?So(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=vt.parse({...n,...t,id:n.id,nodeId:this.nodeId,accessStrategy:t.accessStrategy||n.accessStrategy,capabilities:"local"===(t.type||n.type)?So(t.capabilities||n.capabilities):t.capabilities||n.capabilities,createdAt:n.createdAt,updatedAt:uo()});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=vt.parse({...n,...e,id:n.id,nodeId:this.nodeId,createdAt:n.createdAt,updatedAt:uo()});return this.nodeRuntimes.put(t)}):this.nodeRuntimes.put(vt.parse({...n,status:"unknown",updatedAt:uo()}))}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=jt(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 ${pe}: ${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=jt(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=uo(),n=bt(e),r=this.models.get(n),o=ct.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=bt(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(ct.parse(e));return this.publicModel(n,this.modelReferenceIds(n.id).length)}updateModel(e,t){const n=this.requireModel(e),r=ct.parse({...n,...t,key:t.key?.trim()?t.key:n.key,createdAt:n.createdAt,updatedAt:uo()}),o=bt(r),i=this.models.put(ct.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=ut.parse({instanceId:e,codexModelHash:t.codexModelHash,claudeModelHash:t.claudeModelHash,updatedAt:uo()});this.modelAssignments.put(o);try{return{assignment:o,instance:this.controlledInstances.put(Rt.parse({...n,modelSelection:t.modelSelection,updatedAt:uo()}))}}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(bt(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 lt.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=>!Sr.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=bt({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=uo();return this.models.put(ct.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=uo(),o=e.id||Un("inst"),i=rt.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?zt.parse(e.image):void 0,a=s?(()=>{const{reference:e,...t}=s;return At.parse({...t,requestedReference:e})})():void 0,c="local"===t.type&&"local-folder"===i.type?n.resolve(i.path):void 0,d=Rt.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:Hn(),createdAt:r,updatedAt:r});return this.controlledInstances.put(d)}registerInstance(e,t,n){const r=Lt.parse(t),o=uo(),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),wo(r.protocolVersion,`Instance ${e}`);const s=Rt.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=Ft.parse(t);wo(o.protocolVersion,`Instance ${e}`);const i=uo(),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=Rt.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&&Ao("INSTANCE_ID_MISMATCH",`Instance ${t.instanceId} cannot register as ${e.id}.`),t.nodeId&&t.nodeId!==this.nodeId&&Ao("INSTANCE_NODE_MISMATCH",`Instance ${e.id} belongs to node ${this.nodeId}.`),t.runtimeId&&t.runtimeId!==e.runtimeId&&Ao("INSTANCE_RUNTIME_MISMATCH",`Instance ${e.id} belongs to runtime ${e.runtimeId}.`),t.imageId&&t.imageId!==e.imageId&&Ao("INSTANCE_IMAGE_MISMATCH",`Instance ${e.id} belongs to image ${e.imageId}.`)}validateInstanceToken(e,t){e.registrationToken&&t===e.registrationToken||Ao("INSTANCE_REGISTRATION_TOKEN_INVALID",`Invalid registration token for instance ${e.id}.`)}context(e,t=this.resolvedAssignedModelEnvironment(e.id)){const n=e.imageSnapshot||At.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:ho(e),image:n,node:this.node,runtime:this.requireRuntime(e.runtimeId),instance:e,nodeAgentUrl:this.containerUrl,modelEnv:t}}}class Ro{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 kt.parse({...this.config,host:mo(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:mo(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=_t.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 ${mo(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:mo(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 jo(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(Rt.parse({...i,status:"provisioning",imageProvisioning:{...i.imageProvisioning,phase:t,error:void 0,updatedAt:uo()},updatedAt:uo()})),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(Rt.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:uo()},updatedAt:uo()})),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(Rt.parse({...s,status:"failed",health:"failed",imageProvisioning:{...s.imageProvisioning,phase:"failed",error:a,updatedAt:uo()},updatedAt:uo()})),o(),i.warn({instanceId:n,action:"image.provision",reference:s.imageProvisioning.requestedReference,error:a},"node instance image provisioning failed")}}async function Po(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(Rt.parse({...s,status:"provisioning",updatedAt:uo()})),c=t.forRuntime(e.requireRuntime(a.runtimeId)),d=await c.start(e.context(a)),l=await fo(n,Rt.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:uo()})),u=Rt.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:uo()}),m=e.controlledInstances.put(u);return await Io(n,m,"start",o),o.diagnostic({instanceId:r,action:"start",reason:i,status:m.status,connectionStatus:m.connectionStatus,targetStatus:m.targetStatus,targetWeb:m.target.web,containerName:m.runtime.containerName},"node instance start completed"),m}function Mo(e,t){const n=t instanceof Error?t.message:String(t);return Rt.parse({...e,status:"failed",health:"failed",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",workspace:{...e.workspace,error:n},updatedAt:uo()})}function Lo(e,t,n=uo()){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 Fo(e){return Rt.parse({...e,health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:uo()})}async function $o(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}`,m=e.containerUrl||process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,p=jn(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 Jr(p),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"),z=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||ir(p.dataDir),A="local-ipc"===b?`ipc://${encodeURIComponent(z)}`:s,w=new Do(p,I,s,m,o);w.node.connectionMode=b,w.node.controlEndpoint=A,w.node.endpoint=A,w.init();const S=e.dockerCommandRunner||Bt,v=new Gt(S),N=new Vt(S,{publishHost:"127.0.0.1",imageService:v}),E=new Co(new xo(N),new To(e.dockerCommandRunner||Bt,p,()=>w.localNodeAgentUrl)),k=e.updateCommandRunner||e.dockerCommandRunner||Bt,_=new Gt(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:so()};x.decorate("nodeAgentState",w),await x.register(d);const C=new mn(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 D=new yn(S,()=>w.listInstances().filter(e=>"docker"===w.requireRuntime(e.runtimeId).type),e=>C.publish("instance.metrics.snapshot",e,{instanceId:e.instanceId}));D.start(),x.decorate("nodeAgentRuntimeMetrics",D);const R=($=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String($||"").trim().toLowerCase())),j=new kr(y,I,g,h),P=(e,t)=>{R&&x.log.info(e,t)},M={diagnostic:P,warn:(e,t)=>x.log.warn(e,t)},L=async e=>{if("node-agent"===e.target.component)return async function(e){const t=await gr(e.runCommand,"@task-handoff/node-agent",e.channel);return t?Ye.parse({target:{component:"node-agent"},source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:t,updateAvailable:pr(e.currentVersion,t),checkedAt:mr()}):fr({target:{component:"node-agent"},channel:e.channel,currentVersion:e.currentVersion})}({channel:e.channel,currentVersion:no(),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 gr(e.runCommand,"@task-handoff/controlled-instance",e.channel),r=e.instance.build?.packageVersion||e.instance.instanceVersion;return n?Ye.parse({target:t,source:"npm",channel:e.channel,currentVersion:r,availableVersion:n,updateAvailable:pr(r,n),checkedAt:mr()}):fr({target:t,channel:e.channel,currentVersion:r})}if("docker"===e.runtime.type){const n=e.instance.imageSnapshot?.requestedReference;if(!n)return Ye.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:mr()});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}:${yr(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 Ir(e);const o=r.filter(e=>zr(e));if(!o.length)return Ir(e);const i=o.find(e=>zr(e)===br(t));return i?Ir(i):void 0}(JSON.parse(o.stdout),e.arch);if(!i)throw new Error(`Docker registry did not return a digest for ${r} matching ${br(e.arch||process.arch)}.`);const s=e.instance.build?.imageDigest;return Ye.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:s,availableVersion:i,artifactRef:r,updateAvailable:s!==i,checkedAt:mr()})}return Ye.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:mr()})}({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=hr(),o=(await k(r,["prefix","--global"])).stdout.trim(),[i]=vo(),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 Rt.parse({...e,status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:uo()})}(Rt.parse({...r,..."docker"===o.type&&s&&r.imageSnapshot?{...Lo(r,s)}:{}}))),await Po(w,E,O,r.id,M,"update")};var $;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);(R||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(lo(e)){if(r&&ao(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 co(e.ip)||co(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&&ao(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()=>{D.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(Fo(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))jo(w,v,e.id,e.imageProvisioning.generation,()=>C.syncNow(),M);const e=w.listInstances().filter(e=>"local"===w.requireRuntime(e.runtimeId).type&&_o.has(e.status));for(const t of e)try{await Po(w,E,O,t.id,M,"restore")}catch(e){w.controlledInstances.put(Mo(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:pe,build:(e="node-agent",{component:e,packageName:"@task-handoff/node-agent",packageVersion:no(),protocolVersion:pe,buildId:to("TASK_HANDOFF_BUILD_ID"),builtAt:to("TASK_HANDOFF_BUILT_AT"),gitCommit:to("TASK_HANDOFF_GIT_COMMIT"),imageRef:to("TASK_HANDOFF_IMAGE_REF"),imageDigest:to("TASK_HANDOFF_IMAGE_DIGEST")}),instanceProxy:{...T},serverTime:(new Date).toISOString()}};var e});const U=e=>{if(!lo(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 L(We.parse(e.body))})),x.post("/api/node-agent/updates/apply",async(e,r)=>{const o=eo.parse(e.body),s=await L(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:uo()});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",hr()])}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(Rr.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(jr.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=Pr.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(Dn.parse(e.body))})),x.patch("/api/node-agent/runtimes/:id",async e=>({data:w.updateRuntime(e.params.id,Rn.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:kn(xn.parse(e.query))})),x.post("/api/node-agent/local-folders",async(e,t)=>t.code(201).send({data:w.createLocalFolder(On.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(ht.parse(e.body))})),x.put("/api/node-agent/models/:id/deploy",async e=>{const t=e.params.id,n=yt.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,ft.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(Tn.parse(e.body));return C.syncNow(),n.imageProvisioning&&jo(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=uo();return e.controlledInstances.put(Rt.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(),jo(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=Cn.parse(e.body),o=Rt.parse({...n,...r,updatedAt:uo()}),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,It.parse(e.body));return await bo(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,Lt.parse(e.body),ao(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,Ft.parse(e.body),ao(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 Jt(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 D.snapshot(t)}}),x.post("/api/node-agent/instances/:id/start",async e=>{const t=e.params.id;Zr.parse(e.body);const n=await Po(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=Rt.parse({...t,...r,agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",updatedAt:uo()}),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);Zr.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=Rt.parse({...n,...o,target:o.target?{...n.target,...o.target}:n.target,updatedAt:uo()}),s=await fo(O,i),a=Rt.parse({...n,...o,target:o.target?{...n.target,...o.target,status:s}:n.target,targetStatus:s,uiAccessStatus:s,connectionStatus:o.connectionStatus,updatedAt:uo()}),c=w.controlledInstances.put(a);return await Io(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=_n.parse(e.body),o=zo(w.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;"POST"===r.method&&"/api/apps/sessions"===i&&await bo(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:oo(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:io},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=_n.parse(e.body),d=zo(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:oo(s),signal:r.signal}),m=Number(u.headers.get("content-length"));if(Number.isFinite(m)&&m>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(ro(u.headers)))t.header(e,n);if(!u.body||"HEAD"===s.method)return c("completed"),t.send();const p=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,p.once("end",()=>c("completed")),p.once("error",()=>c("aborted")),t.send(a.Readable.fromWeb(u.body).pipe(p))}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:io},async(e,t)=>{const n=e.params.id,r=_n.parse(e.body),o=zo(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:oo(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:ro(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=zo(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 Uo(e){const t=new URL("/api/node-agent/tunnel",e);return t.protocol="https:"===t.protocol?"wss:":"ws:",t.toString()}function Ho(e,t){const n=new URL(t.tunnelUrl);n.searchParams.set("nodeId",t.nodeId);const r=t.secret?dr({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?dr({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)},m=(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 p=a&&"object"==typeof a?a:{};if("control-plane.http.open"===p.type){const e="string"==typeof p.streamId?p.streamId:"",r="string"==typeof p.route&&p.route.startsWith("/")?p.route:"/health",o=p.init&&"object"==typeof p.init?p.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:ur(o.method),headers:{...i,...t.token?{authorization:`Bearer ${t.token}`}:{}},body:"string"==typeof o.body?o.body:void 0,signal:a.signal});if(await m(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 m(u,Buffer.from(n),!0)}}await m(u,JSON.stringify({type:"node-agent.http.end",streamId:e})),u.close(1e3,"HTTP stream completed.")}catch(t){u.readyState===l.OPEN&&(await m(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"===p.type){const e="string"==typeof p.streamId?p.streamId:"",r="string"==typeof p.route?p.route:"",s=Array.isArray(p.protocols)?p.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}),m=(e=>{const t=new URL(n);return t.pathname=`${t.pathname.replace(/\/$/,"")}/streams/${encodeURIComponent(e)}`,t})(e),p=new l(m,{headers:d(m)});i.set(e,{upstream:u,tunnel:p,close:(e=1e3,t="")=>{u.close(e,t),p.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=Ut(t);const s=[],a=[];let c;const d=()=>{c&&(clearTimeout(c),c=void 0)},l=(n,o)=>{if(n.length>=r)return Ht(e,1011,"WebSocket bridge pending frame limit exceeded."),void Ht(t,1011,"WebSocket bridge pending frame limit exceeded.");n.push(o)};n.upstreamOpenTimeoutMs&&!i&&(c=setTimeout(()=>{i||o||(Ht(e,1011,"Upstream websocket did not open."),Ht(t))},n.upstreamOpenTimeoutMs)),e.on("open",()=>Kt(e,a)),t.on("open",()=>{d(),i=!0,n.onUpstreamOpen?.(),Kt(t,s)}),e.on("message",(e,n=!1)=>{const r={data:e,isBinary:Boolean(n)};Ut(t)?qt(t,r):l(s,r)}),t.on("message",(t,n=!1)=>{const r={data:t,isBinary:Boolean(n)};Ut(e)?qt(e,r):l(a,r)}),t.on("close",(t,r)=>{d(),n.onUpstreamClose?.(t,r),(i||o||!n.onUpstreamCloseBeforeOpen?.())&&Ht(e,t,r)}),t.on("error",t=>{n.onUpstreamError?.(t),(i||o||!n.onUpstreamErrorBeforeOpen?.(t))&&Ht(e)}),e.on("close",(e,r)=>{d(),o=!0,n.onClientClose?.(e,r),Ht(t,e,r)}),e.on("error",e=>{d(),o=!0,n.onClientError?.(e),Ht(t,1011,"WebSocket bridge client failed.")})}(p,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"===p.type){const e="string"==typeof p.streamId?p.streamId:"";return void u(e,"number"==typeof p.code?p.code:1e3,"string"==typeof p.reason?p.reason:"")}if("control-plane.request"===p.type){const n="string"==typeof p.requestId?p.requestId:"",r=p.init&&"object"==typeof p.init?p.init:{},i="string"==typeof p.route&&p.route.startsWith("/")?p.route:"/health",s=r.headers&&"object"==typeof r.headers?r.headers:{};try{const a=await e.inject({method:ur(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 qo(e){const o=jn(e.dataDir),i=(s=e.host,a=e.port,Et.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 Kn(e.settingsPath,()=>({version:1,externalListener:t}),{schema:Qr,sanitize:po})}(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:p.hostname(),component:r.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:m.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(Vn(e),`${JSON.stringify({token:o.token,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Xn(e,o),{lockPath:e,owner:o,release(){const n=Wn(e);n?.token===o.token&&t.rmSync(e,{recursive:!0,force:!0})}}}catch(o){if("EEXIST"!==o.code)throw o;const i=Wn(e);if(Yn(i?.pid))throw new Bn(e,i,r.error);if(!i&&tr(e)<2e3){if(n+1<5){er();continue}throw new Bn(e,void 0,r.error)}const s=m.randomUUID();if(!nr(e,s)){if(n+1<5){er();continue}throw new Bn(e,Wn(e),r.error)}const a=Wn(e);if(Yn(a?.pid))throw Zn(Jn(e)),new Bn(e,a,r.error);if(i?.token&&a?.token!==i.token){Zn(Jn(e));continue}if(!or(e,s))continue;t.rmSync(e,{recursive:!0,force:!0})}const i=Wn(e);throw new Bn(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:mo(l.bindScope),port:l.port});try{const i={...e,port:l.port},s=await $o(i),a=function(e,t,n,r){const o=t.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,i=new Jr(n),s=new Map,a=t=>{const n=t.url.replace(/\/$/,"");s.get(n)?.close(1e3,"Reverse tunnel reconnecting.");const i=Ho(e,{tunnelUrl:Uo(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=Ho(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 Jr(o).resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID));s.decorate("nodeAgentReverseTunnels",a);const m=new Ro({app:s,state:s.nodeAgentState,settings:d,config:l,source:c?"persisted":"bootstrap"});let p;s.decorate("nodeAgentListenerManager",m),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(),u.release()});try{await s.ready();const i=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||ir(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 m.start(),a.connectConfigured(),await(s.nodeAgentRestoreLocalInstances?.())}catch(e){throw await s.close(),e}}catch(e){throw u.release(),e}}function Ko(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 Bo(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",Ko,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",Bo).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 qo({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"),m=require("node:crypto"),p=require("node-pty"),g=require("node:os"),h=require("write-file-atomic"),f=require("semver"),y="undefined"!=typeof document?document.currentScript:null;const I=41943040,b="ai.sessions",z=u.z.string().trim().min(1).max(80),A=u.z.enum(["running","waiting","idle","failed"]);u.z.object({instanceId:u.z.string().trim().min(1).max(160),sessionId:u.z.string().trim().min(1).max(120),unread:u.z.boolean(),sessionUpdatedAt:u.z.string().datetime(),updatedAt:u.z.string().datetime()}).strict();const w=u.z.enum(["thinking","tool","editing","approval","responding","unknown"]),S=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(),v=u.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),N=u.z.enum(["started","interacted","interrupted"]),E=u.z.object({threadId:u.z.string().trim().min(1).max(240),path:u.z.string().trim().max(4096).optional(),status:v,activity:N.optional(),message:u.z.string().trim().max(1e3).optional(),updatedAt:u.z.string().datetime()}).strict(),k=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}),_=u.z.object({send:u.z.boolean().optional(),interrupt:u.z.boolean().optional(),approval:u.z.boolean().optional()}).strict(),O=u.z.enum(["image","file"]),x=u.z.object({id:u.z.string().trim().min(1).max(120),kind:O,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(Number.MAX_SAFE_INTEGER)}),T=u.z.union([x.extend({source:u.z.object({type:u.z.literal("inline"),encoding:u.z.literal("base64"),data:u.z.string().min(1).max(31457280)}).strict()}).strict().superRefine((e,t)=>{const n="image"===e.kind?20971520:512e3;(e.size<=0||("file"===e.kind?e.size>=n:e.size>n))&&t.addIssue({code:u.z.ZodIssueCode.custom,path:["size"],message:`Inline ${e.kind} must be between 1 byte and ${n} bytes.`})}),x.extend({source:u.z.object({type:u.z.literal("runtime-path"),path:u.z.string().trim().min(1).max(4096)}).strict()}).strict()]),C=x.extend({sourceType:u.z.enum(["inline","runtime-path"])}).strict(),R=u.z.union([u.z.object({id:u.z.string().trim().min(1).max(120),kind:O.optional(),source:u.z.object({type:u.z.literal("upload-ref")}).strict().default({type:"upload-ref"})}).strict(),u.z.object({id:u.z.string().trim().min(1).max(120),kind:O,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(Number.MAX_SAFE_INTEGER),source:u.z.object({type:u.z.literal("runtime-path"),path:u.z.string().trim().min(1).max(4096)}).strict()}).strict()]),D=u.z.enum(["auto","queue","steer","immediate"]),j=u.z.enum(["ask","auto-review","full-access"]),P=u.z.enum(["review","rename","goal","compact"]);u.z.object({command:P,argument:u.z.string().trim().max(4e3).optional()}).strict().superRefine((e,t)=>{"rename"!==e.command||e.argument||t.addIssue({code:"custom",path:["argument"],message:"Rename requires a thread name."}),"review"!==e.command&&"compact"!==e.command||!e.argument||t.addIssue({code:"custom",path:["argument"],message:`${e.command} does not accept an argument.`})}),u.z.object({command:P,value:u.z.string().max(4e3).optional(),turnId:u.z.string().trim().min(1).max(240).optional()}).strict(),u.z.enum(["skill","app","plugin"]);const M=u.z.object({name:u.z.string().trim().min(1).max(240),path:u.z.string().trim().min(1).max(4096)}),L=u.z.discriminatedUnion("kind",[M.extend({kind:u.z.literal("skill"),path:u.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),M.extend({kind:u.z.literal("app"),path:u.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),M.extend({kind:u.z.literal("plugin"),path:u.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),F=u.z.array(L).max(20).default([]),$=u.z.enum(["plugin","skill","file","directory","app"]),U=u.z.object({kind:$,name:u.z.string().trim().min(1).max(240),description:u.z.string().trim().max(1e3).optional(),path:u.z.string().trim().min(1).max(4096),icon:u.z.string().trim().max(4096).optional()}).strict().superRefine((e,t)=>{"skill"!==e.kind||e.path.startsWith("/")||t.addIssue({code:"custom",path:["path"],message:"Skill paths must be absolute."}),"app"!==e.kind||/^app:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"App paths must use app://<id>."}),"plugin"!==e.kind||/^plugin:\/\/[^\s/]+$/.test(e.path)||t.addIssue({code:"custom",path:["path"],message:"Plugin paths must use plugin://<id>."}),"file"!==e.kind&&"directory"!==e.kind||!e.path.startsWith("/")&&!e.path.split(/[\\/]+/).includes("..")||t.addIssue({code:"custom",path:["path"],message:"File and directory paths must be relative to the session cwd."})}),H=u.z.object({category:u.z.enum(["skills","plugins","apps","files"]),code:u.z.string().trim().min(1).max(160),message:u.z.string().trim().min(1).max(2e3)}).strict();u.z.object({sessionId:u.z.string().trim().min(1).max(120),providerSessionId:u.z.string().trim().min(1).max(240),cwd:u.z.string().trim().min(1).max(4096),candidates:u.z.array(U).max(1e3).default([]),diagnostics:u.z.array(H).max(20).default([])}).strict(),u.z.object({query:u.z.string().trim().max(240).default("")}).strict(),u.z.object({sessionId:u.z.string().trim().min(1).max(120),cwd:u.z.string().trim().min(1).max(4096),query:u.z.string().trim().max(240),requestId:u.z.string().trim().min(1).max(160),candidates:u.z.array(U.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:u.z.boolean().default(!1)}).strict();const q=u.z.object({message:u.z.string().trim().min(1).max(2e4),mode:D.optional(),permissionMode:j.optional()});q.extend({attachments:u.z.array(T).max(6).default([]),references:F}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>I&&t.addIssue({code:u.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),q.extend({attachments:u.z.array(R).max(6).default([]),references:F}).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 B=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(C).max(6).default([]),references:F,permissionMode:j.optional(),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(),K=u.z.object({pendingCount:u.z.number().int().min(0).default(0),items:u.z.array(B).max(100).default([])}).strict().default({pendingCount:0,items:[]}),G=u.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),V=u.z.object({id:u.z.string().trim().min(1).max(240),status:u.z.enum(["running","completed"]),startedAt:u.z.string().datetime().optional(),completedAt:u.z.string().datetime().optional()}).strict(),J=u.z.object({id:u.z.string().trim().min(1).max(240),providerTurnId:u.z.string().trim().max(240).optional(),source:G.optional(),userPrompt:u.z.string().trim().optional(),status:u.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:w.optional(),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),lastMessageItemId:u.z.string().trim().max(240).optional(),contextCompactions:u.z.array(V).max(20).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(),W=J.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),Y=u.z.object({id:u.z.string().trim().min(1).max(120),agent:z,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:_.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(J).max(50).optional(),status:A.default("running"),phase:w.default("unknown"),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),lastMessageItemId:u.z.string().trim().max(240).optional(),currentTool:S.optional(),toolCallsSinceLastMessage:u.z.number().int().min(0).default(0),subAgents:u.z.array(E).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:k,queue:K}).strict();u.z.object({session:Y,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 X=u.z.object({id:u.z.string().trim().min(1).max(120),agent:u.z.enum(["codex","claude"]),providerSessionId:u.z.string().trim().min(1).max(240),title:u.z.string().trim().max(240).optional(),userPrompt:u.z.string().trim().optional(),lastMessage:u.z.string().trim().optional(),cwd:u.z.string().trim().min(1).max(4096),lastActiveAt:u.z.string().datetime(),archivedAt:u.z.string().datetime()}).strict();u.z.object({schemaVersion:u.z.literal(1).default(1),items:u.z.array(X).max(50).default([])}).strict(),u.z.object({items:u.z.array(X).max(50).default([])}).strict(),u.z.object({item:X,turns:u.z.array(W).max(50).default([])}).strict(),u.z.object({disposition:u.z.enum(["resumed","already-open"]),aiSessionId:u.z.string().trim().min(1).max(120),providerSessionId:u.z.string().trim().min(1).max(240),appSessionId:u.z.string().trim().min(1).max(120)}).strict();const Q=Y.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,lastMessageItemId:!0,currentTool:!0,toolCallsSinceLastMessage:!0,subAgents:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),Z=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(Q).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:Z}).strict(),Z.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 ee=u.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),te=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:ee}).strict(),ne=u.z.object({meta:te,snapshot:Z}).strict(),re=u.z.object({meta:te,upserted:u.z.array(Q).default([]),removed:u.z.array(u.z.string().trim().min(1).max(120)).default([])}).strict(),oe=u.z.object({meta:te,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),itemId:u.z.string().trim().min(1).max(240),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:ne}).strict(),u.z.object({type:u.z.literal("ai-session.patch"),payload:re}).strict(),u.z.object({type:u.z.literal("ai-session.removed"),payload:oe}).strict()])).default([])}).strict();const ie=u.z.object({source:G,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(),se=ie.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:_.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(J).max(50).optional(),status:A.optional(),phase:w.optional(),summary:u.z.string().trim().max(1e3).optional(),lastMessage:u.z.string().trim().optional(),lastMessageItemId:u.z.string().trim().max(240).optional(),currentTool:S.optional(),toolCallsSinceLastMessage:u.z.number().int().min(0).optional(),subAgents:u.z.array(E).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(),ae=ie.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","context-compaction"]),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(),itemId:u.z.string().trim().max(240).optional(),status:A.optional(),phase:w.optional(),summary:u.z.string().trim().max(1e3).optional(),error:u.z.string().trim().max(4e3).optional(),currentTool:S.nullable().optional(),toolCallsSinceLastMessage:u.z.number().int().min(0).optional(),subAgents:u.z.array(E).max(50).optional(),contextCompaction:V.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("context-compaction"===e.kind?e.contextCompaction||t.addIssue({code:"custom",path:["contextCompaction"],message:"context-compaction events require contextCompaction"}):void 0!==e.contextCompaction&&t.addIssue({code:"custom",path:["contextCompaction"],message:"contextCompaction is only valid for context-compaction events"}),"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",[se,ae]);const ce=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()]),de=u.z.union([ce,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()]),le=u.z.object({promptTemplate:u.z.string().trim().min(1).max(2e4)}).strict(),ue=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(),me=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:de,action:le,policy:ue.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:u.z.string().datetime(),updatedAt:u.z.string().datetime()}).strict(),pe=u.z.object({type:u.z.literal("ai-session"),aiSessionId:u.z.string().trim().min(1).max(160)}).strict(),ge=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:pe,localName:u.z.string().trim().min(1).max(160).optional(),createdAt:u.z.string().datetime(),updatedAt:u.z.string().datetime()}).strict(),he=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(),fe=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:pe,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(me).default([]),deployments:u.z.array(ge).default([]),runtime:u.z.array(he).default([]),recentRuns:u.z.array(fe).default([])}).strict();const ye="2026-07-26",Ie=u.z.string(),be=u.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),ze=u.z.string().datetime(),Ae=u.z.record(u.z.string(),u.z.string()).default({}),we=u.z.record(u.z.string(),u.z.string()).default({}),Se=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,ve=/^sha256:([a-fA-F0-9]{64})$/,Ne=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,Ee=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function ke(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?ve.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&&!Se.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 m=u.length>1&&(u[0].includes(".")||u[0].includes(":")||"localhost"===u[0]),p=m?u.slice(1):u;if(m&&!Ne.test(u[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!p.length||p.some(e=>!Ee.test(e)))throw new Error("Docker image repository names must be lowercase and use valid separators.");const g=u.map((e,t)=>m&&0===t?e.toLowerCase():e).join("/"),h=i?`sha256:${i[1].toLowerCase()}`:void 0;return`${g}${d?`:${d}`:""}${h?`@${h}`:""}`}const _e=u.z.string().trim().min(1).max(512).transform((e,t)=>{try{return ke(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),u.z.NEVER}}),Oe=u.z.string().trim().transform((e,t)=>{const n=ve.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)}),xe=u.z.object({codexModelHash:be.nullable().optional(),claudeModelHash:be.nullable().optional()}).strict().default({}),Te=u.z.object({id:be,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(),Ce=u.z.object({code:u.z.enum(["APP_CATALOG_INVALID"]),message:u.z.string().trim().min(1).max(500)}).strict(),Re=u.z.object({items:u.z.array(Te).max(256),observedAt:ze,issues:u.z.array(Ce).max(32).default([])}).strict(),De=u.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),je=u.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),Pe=u.z.enum(["apt","dnf","brew","npm"]),Me=u.z.enum(["user","passwordless-sudo","root"]),Le=u.z.object({platform:De,arch:je,installers:u.z.array(Pe).max(8),privilege:Me,installerAccess:u.z.object({npmGlobalWritable:u.z.boolean().optional()}).strict().optional()}).strict(),Fe=u.z.enum(["installed","not-installed","broken","unsupported"]),$e=u.z.enum(["recipe","bundled","external","none"]),Ue=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(),He=u.z.object({id:be,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:Fe,managementSource:$e,version:u.z.string().trim().max(120).optional(),canInstall:u.z.boolean(),canUninstall:u.z.boolean(),installReason:Ue.optional(),uninstallReason:Ue.optional(),activeJobId:be.optional()}).strict(),qe=u.z.enum(["install","uninstall"]),Be=u.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),Ke=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"]}),Ge=u.z.object({executable:u.z.string().trim().min(1).max(4096),args:u.z.array(u.z.string().max(4096)).max(128)}).strict(),Ve=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(be).max(64).optional()}).strict(),Je=u.z.object({id:be,requestId:be.optional(),appId:be,operation:qe,state:Be,phase:u.z.string().trim().min(1).max(120).optional(),progress:Ke.optional(),command:Ge.optional(),logTail:u.z.string().max(32768).optional(),logTruncated:u.z.boolean().optional(),error:Ve.optional(),requestedAt:ze,startedAt:ze.optional(),finishedAt:ze.optional(),updatedAt:ze}).strict(),We=u.z.object({streamId:be,sequence:u.z.number().int().nonnegative(),capabilities:Le,apps:u.z.array(He).max(256),activeJobs:u.z.array(Je).max(256),recentJobs:u.z.array(Je).max(256),observedAt:ze}).strict();u.z.object({requestId:be.optional()}).strict(),u.z.object({job:Je}).strict(),u.z.object({type:u.z.literal("app-management"),streamId:be,sequence:u.z.number().int().nonnegative(),observedAt:ze,job:Je.optional(),snapshot:We.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const Ye=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:Ie.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(),Xe=u.z.enum(["stable","beta","alpha"]),Qe=u.z.discriminatedUnion("component",[u.z.object({component:u.z.literal("node-agent")}).strict(),u.z.object({component:u.z.literal("controlled-instance"),instanceId:be}).strict()]),Ze=u.z.object({target:Qe,channel:Xe.default("stable")}).strict(),et=u.z.object({target:Qe,source:u.z.enum(["npm","docker-registry"]),channel:Xe,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:ze}).strict(),tt=u.z.object({id:be,nodeId:be,target:Qe,source:u.z.enum(["npm","docker-registry"]),channel:Xe,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:ze.optional(),completedAt:ze.optional(),createdAt:ze,updatedAt:ze}).strict(),nt=Ze,rt=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(),ot=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(),it=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:be,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:rt.default({type:"branch",name:"main"}),auth:ot.default({type:"none"}),clone:it.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:be.optional(),labels:Ae,createdAt:ze,updatedAt:ze}).strict();const st=u.z.object({id:be,nodeId:be,name:u.z.string().trim().min(1).max(160),path:u.z.string().trim().min(1).max(4096),defaultImageId:be.optional(),labels:Ae,createdAt:ze,updatedAt:ze}).strict(),at=u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("git-repository"),repositoryId:be.optional(),url:u.z.string().trim().min(1).max(2048),provider:u.z.string().trim().max(80).optional(),ref:rt.default({type:"branch",name:"main"}),auth:ot.default({type:"none"}),clone:it.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:rt.default({type:"branch",name:"main"}),auth:ot.default({type:"none"}),clone:it.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),u.z.object({type:u.z.literal("local-folder"),localFolderId:be.optional(),path:u.z.string().trim().min(1).max(4096),ownerNodeId:be.optional()}).strict()]),ct=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(),dt=u.z.enum(["codex","claude"]),lt=u.z.object({id:be,name:u.z.string().trim().min(1).max(160),source:at,defaultImageId:be.optional(),defaultNodeId:be.optional(),defaultRuntimeId:be.optional(),workspacePolicy:ct,labels:Ae,createdAt:ze,updatedAt:ze}).strict(),ut=u.z.object({id:be,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:dt,enabled:u.z.boolean().default(!0),order:u.z.number().int().min(0).max(1e6).default(0),labels:Ae,createdAt:ze,updatedAt:ze}).strict(),mt=ut,pt=ut.omit({key:!0}).extend({keyPreview:u.z.string().trim().min(1).max(160),keySet:u.z.boolean()}).strict(),gt=pt.extend({referenceCount:u.z.number().int().min(0)}).strict(),ht=u.z.object({instanceId:be,codexModelHash:be.optional(),claudeModelHash:be.optional(),updatedAt:ze}).strict(),ft=u.z.discriminatedUnion("type",[u.z.object({type:u.z.literal("control-plane"),name:ut.shape.name,enabled:ut.shape.enabled,order:ut.shape.order}).strict(),u.z.object({type:u.z.literal("node"),nodeId:be,name:ut.shape.name,enabled:ut.shape.enabled,order:ut.shape.order,referenceCount:u.z.number().int().min(0)}).strict()]),yt=u.z.object({id:be,model:pt,locations:u.z.array(ft).min(1),referenceCount:u.z.number().int().min(0).default(0)}).strict(),It=u.z.object({nodeId:be,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(yt),nodeDiagnostics:u.z.array(It).default([]),updatedAt:ze}).strict();const bt=ut.omit({id:!0,createdAt:!0,updatedAt:!0}).strict(),zt=bt.partial().strict(),At=ut,wt=u.z.object({modelSelection:xe,codexModelHash:be.optional(),claudeModelHash:be.optional()}).strict();function St(e){const t={app:dt.parse(e.app),endpoint:ut.shape.endpoint.parse(e.endpoint),key:ut.shape.key.parse(e.key),model:ut.shape.model.parse(e.model)};return`mdl_${m.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}const vt=u.z.object({id:be,name:u.z.string().trim().min(1).max(160),reference:_e,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:we,labels:Ae,createdAt:ze,updatedAt:ze}).strict(),Nt=vt.omit({reference:!0}).extend({requestedReference:_e,resolvedDigest:Oe.optional(),resolvedReference:_e.optional()}).strict(),Et=u.z.object({phase:u.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:_e,generation:u.z.number().int().nonnegative().default(0),error:u.z.string().trim().max(4096).optional(),startedAt:ze,updatedAt:ze}).strict(),kt="image.pull.terminal.output",_t="image.pull.terminal.finished",Ot=u.z.object({instanceId:be,generation:u.z.number().int().nonnegative(),requestedReference:_e,sequence:u.z.number().int().nonnegative(),observedAt:ze}).strict(),xt=Ot.extend({data:u.z.string().min(1).max(65536),replay:u.z.boolean().optional()}).strict(),Tt=Ot.extend({outcome:u.z.enum(["succeeded","failed"])}).strict();Ot.extend({status:u.z.enum(["connecting","pulling","extracting","complete","failed"]),layers:u.z.object({total:u.z.number().int().nonnegative(),completed:u.z.number().int().nonnegative(),downloaded:u.z.number().int().nonnegative(),downloading:u.z.number().int().nonnegative(),extracting:u.z.number().int().nonnegative()}).strict(),bytes:u.z.object({current:u.z.number().finite().nonnegative(),total:u.z.number().finite().positive()}).strict().optional(),percent:u.z.number().finite().min(0).max(100).optional(),message:u.z.string().trim().min(1).max(500),terminalTail:u.z.string().max(262144).optional(),terminalTruncated:u.z.boolean().optional()}).strict();const Ct=u.z.object({id:be,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:be.optional(),secret:u.z.string().trim().max(4096).optional(),pairedAt:ze.optional(),pairing:u.z.object({status:u.z.enum(["pending","paired","expired"]).default("pending"),joinToken:u.z.string().trim().max(4096).optional(),expiresAt:ze.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:Re.optional(),labels:Ae,lastSeenAt:ze.optional(),createdAt:ze,updatedAt:ze}).strict(),Rt=u.z.object({id:be,nodeId:be,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:ze,updatedAt:ze}).strict(),Dt=u.z.object({instanceId:be,runtimeKind:u.z.literal("docker"),state:u.z.enum(["pending","available","stopped","unavailable"]),sampledAt:ze,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:be.optional(),platform:De.optional()}).passthrough();const jt=u.z.object({bindScope:u.z.enum(["loopback","all-ipv4"]),port:u.z.number().int().min(1).max(65535)}).strict(),Pt=jt.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(),Mt=jt;u.z.object({nodeId:be,joinToken:u.z.string().trim().min(1),expiresAt:ze}).passthrough(),u.z.object({id:be,keyId:be,url:u.z.string().trim().min(1).max(2048).optional(),name:u.z.string().trim().min(1).max(160).optional(),pairedAt:ze,updatedAt:ze.optional(),active:u.z.boolean().optional(),current:u.z.boolean().optional()}).passthrough(),u.z.object({remote:u.z.object({id:be,url:u.z.string().trim().min(1).max(2048).optional(),keyId:be,pairedAt:ze,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 Lt=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:vt,status:u.z.enum(["available","pull-required","unknown"]),localImage:Lt.optional(),error:u.z.string().trim().max(2048).optional()}).strict();const Ft=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(Ft)}).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 $t=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(),Ut=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"}),Ht=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(),qt=u.z.object({id:be,name:u.z.string().trim().min(1).max(160),projectId:be.optional(),source:at,sourceSnapshot:u.z.record(u.z.string(),u.z.unknown()).default({}),modelSelection:xe,nodeId:be,runtimeId:be,imageId:be.optional(),imageSnapshot:Nt.optional(),imageProvisioning:Et.optional(),stateRevision:u.z.number().int().min(0).default(0),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:Ie.optional(),instanceVersion:u.z.string().trim().max(80).optional(),build:Ye.optional(),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),appInventory:Re.optional(),config:u.z.object({autoImportAgentConfigs:u.z.boolean().default(!0),defaultCodexPermissionMode:j.default("ask")}).strict().default({autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"}),workspace:Ht.default({status:"unknown"}),target:$t.default({strategy:"direct-port",status:"unknown"}),access:Ut,apps:u.z.object({runningCount:u.z.number().int().min(0).default(0),problemCount:u.z.number().int().min(0).default(0),updatedAt:ze.optional(),revision:u.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:Z.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:me,deployments:u.z.array(ge).default([]),runtime:u.z.array(he).default([])}).strict()).default([]),recentRuns:u.z.array(fe).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:ze.optional(),createdAt:ze,updatedAt:ze}).strict(),Bt="instance.lifecycle.snapshot",Kt=u.z.object({instanceId:qt.shape.id,revision:u.z.number().int().min(0),updatedAt:ze,status:qt.shape.status.unwrap(),health:qt.shape.health.unwrap(),connectionStatus:qt.shape.connectionStatus.unwrap(),accessStatus:u.z.enum(["reachable","endpoint-unreachable"]),imageProvisioning:Et.optional(),workspace:Ht,runtime:qt.shape.runtime.unwrap(),lastHeartbeatAt:ze.optional()}).strict();function Gt(e,t){return qt.safeParse(Vt(e,t))}function Vt(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,r=new Set(Object.keys(qt.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=Jt(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Jt(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Jt(e,["code","message"])):t.issues},r=Re.safeParse(n);return r.success?r.data:void 0}(n.appInventory),o.apps=Jt(n.apps,["runningCount","problemCount","updatedAt","revision"]),o.config=Jt(n.config,["autoImportAgentConfigs","defaultCodexPermissionMode"]),o.modelSelection=Jt(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=ke(r)}catch{if(!r.includes("@")&&r.lastIndexOf(":")<=r.lastIndexOf("/"))try{r=ke(`${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=Jt(n.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),o.runtime=Jt(n.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),o.target=Jt(n.target??n.endpoints,["strategy","web","api","status"]),o.access=Jt(n.access,["strategy","web","api","ws","status"]),o}function Jt(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 Wt=u.z.object({instanceId:be.optional(),name:u.z.string().trim().min(1).max(160),projectId:be.optional(),source:at.optional(),nodeId:be.optional(),runtimeId:be.optional(),imageId:be.optional(),instanceVersion:u.z.string().trim().max(80).optional(),protocolVersion:Ie,build:Ye.optional(),controlMode:u.z.enum(["standalone","controlled"]).default("controlled"),capabilities:u.z.record(u.z.string(),u.z.unknown()).default({}),appInventory:Re.optional(),target:$t.default({strategy:"direct-port",status:"unknown"}),workspace:Ht.default({status:"unknown"}),registrationToken:u.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==ye||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),Yt=u.z.object({status:qt.shape.status.optional(),health:qt.shape.health.optional(),protocolVersion:Ie,build:Ye.optional(),capabilities:qt.shape.capabilities.optional(),appInventory:Re.optional(),apps:qt.shape.apps.optional(),aiSessions:qt.shape.aiSessions.optional(),triggers:qt.shape.triggers.optional(),workspace:Ht.optional(),target:$t.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==ye||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})}),Xt=u.z.enum(["web","telegram","wechat","dingding"]);function Qt(e){return e.readyState===(e.OPEN??1)}function Zt(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 en(e,t){e.send(t.data,{binary:t.isBinary})}function tn(e,t){if(Qt(e))for(const n of t.splice(0))en(e,n)}function nn(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:be,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:ze,updatedAt:ze}).strict(),u.z.object({id:be,channel:Xt,bridgeId:be.optional(),chatSessionId:u.z.string().trim().min(1).max(240),userId:u.z.string().trim().max(240).optional(),activeProjectId:be.optional(),activeInstanceId:be.optional(),activeAiSessionId:u.z.string().trim().min(1).max(120).optional(),lastUsedAt:ze,createdAt:ze,updatedAt:ze}).strict(),u.z.object({source:u.z.object({type:u.z.literal("chat-gateway").default("chat-gateway"),channel:Xt,bridgeId:be.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(T).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>I&&t.addIssue({code:u.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),target:u.z.object({instanceId:be,aiSessionId:u.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),u.z.object({id:be,instanceId:be,projectId:be.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:ze}).strict();const rn=(e,t,n={})=>new Promise((r,o)=>{const i=[];let s,a=!1;try{s=p.spawn(e,t,{name:"xterm-256color",cols:n.cols||120,rows:n.rows||40,cwd:process.cwd(),env:process.env})}catch(e){return void o(e)}const c=n.timeoutMs?setTimeout(()=>{a=!0,s.kill("SIGKILL")},n.timeoutMs):void 0;c?.unref?.(),s.onData(e=>{i.push(e),n.onData?.(e)}),s.onExit(({exitCode:t})=>{c&&clearTimeout(c);const s={stdout:i.join(""),stderr:""};a?o(Object.assign(new Error(`${e} timed out after ${n.timeoutMs}ms`),{statusCode:504,code:"RUNTIME_COMMAND_TIMEOUT",details:s})):0!==t?o(Object.assign(new Error(s.stdout.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g,"").split(/\r?\n/).map(e=>e.trim()).filter(Boolean).at(-1)||`${e} exited with code ${t}`),{statusCode:502,code:"RUNTIME_EXECUTOR_FAILED",details:s})):r(s)})});class on{inFlight=new Map;runCommand;runTerminalCommand;constructor(e=nn,t){this.runCommand=e,this.runTerminalCommand=t||function(e){return e===nn?rn:async(t,n,r={})=>{const o=await e(t,n,{timeoutMs:r.timeoutMs});return o.stdout&&r.onData?.(o.stdout),o.stderr&&r.onData?.(o.stderr),o}}(e)}ensure(e,t,n){const r=ke(e),o=this.inFlight.get(r);if(o)return t&&(o.phaseListeners.add(t),o.phase&&t(o.phase)),n&&(o.terminalListeners.add(n),o.terminalTail&&n({sequence:o.terminalSequence,data:o.terminalTail,replay:!0})),o.promise;const i=new Set,s=new Set;t&&i.add(t),n&&s.add(n);const a={promise:Promise.resolve(void 0),phaseListeners:i,terminalListeners:s,terminalSequence:0,terminalTail:""};return a.promise=this.ensureOnce(r,e=>{a.phase=e,i.forEach(t=>t(e))},e=>{a.terminalSequence+=1,a.terminalTail=`${a.terminalTail}${e}`.slice(-262144);const t={sequence:a.terminalSequence,data:e};s.forEach(e=>e(t))}).finally(()=>this.inFlight.delete(r)),this.inFlight.set(r,a),a.promise}async ensureOnce(e,t,n){t("checking-image");let r=await this.inspect(e).catch(()=>{}),o=!1;r||(t("pulling-image"),await this.runTerminalCommand("docker",["pull",e],{cols:120,rows:40,onData:n}),o=!0,r=await this.inspect(e)),t("resolving-image");const i=(s=r,Array.isArray(s.RepoDigests)?s.RepoDigests.filter(e=>"string"==typeof e):[]).find(e=>/@sha256:[a-fA-F0-9]{64}$/.test(e));var s;const a=i?.split("@").at(-1)?.toLowerCase();return{requestedReference:e,...a?{resolvedDigest:a}:{},resolvedReference:i||e,pulled:o}}async inspect(e){const t=await this.runCommand("docker",["image","inspect",e,"--format","{{json .}}"]);return JSON.parse(t.stdout.trim()||"{}")}}class sn{runCommand;publishHost;images;constructor(e=nn,t={}){this.runCommand=e,this.images=t.imageService||new on(e),this.publishHost=t.publishHost||"127.0.0.1"}async start(e){dn(e);const t=e.instance.runtime.containerName||cn(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||cn(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){dn(e);const t=e.instance.runtime.containerName||cn(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||cn(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 an(e=nn){return async function(e=nn){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 cn(e){return`task-handoff-${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`}function dn(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 ln=u.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),un=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(),mn=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:ln.default("unknown"),bindings:u.z.array(un).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()})),pn=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(mn).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:pn}).strict(),pn.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:u.z.number().int().min(0).optional()});const gn=u.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),hn=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:gn}).strict(),fn=u.z.object({meta:hn,snapshot:pn}).strict(),yn=u.z.object({meta:hn,session:mn}).strict(),In=u.z.object({meta:hn,sessionId:u.z.string().trim().min(1).max(160),tombstone:mn.optional(),expiresAt:u.z.string().datetime().optional()}).strict();function bn(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:fn}).strict(),u.z.object({type:u.z.literal("app-session.patch"),payload:yn}).strict(),u.z.object({type:u.z.literal("app-session.removed"),payload:In}).strict()])).default([])}).strict();const zn=u.z.enum(["ai.sessions","app.sessions"]),An=u.z.object({topic:zn,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(),wn=u.z.object({protocolVersion:u.z.literal(1),streams:u.z.array(An)}).strict();class Sn{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}}function vn(e,t=6e4){const n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}class Nn{sockets=new Map;socketUrls=new Map;retries=new Map;outputs=new Set;imagePullTerminalByInstance=new Map;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){this.outputs.add(e),e.on("close",()=>this.outputs.delete(e)),e.on("error",()=>this.outputs.delete(e));for(const t of this.state.listInstances()){const n=En(t);n?this.sendForwarded(e,this.createEvent(Bt,n,{instanceId:t.id})):this.logger?.warn?.({instanceId:t.id},"instance.lifecycle.snapshot.invalid")}for(const[t,n]of this.imagePullTerminalByInstance)vn(n.tail).forEach((r,o)=>this.sendForwarded(e,this.createEvent(kt,{...n.output,sequence:n.output.sequence+o,data:r,...0===o?{replay:!0}:{}},{instanceId:t}))),n.finished&&this.sendForwarded(e,this.createEvent(_t,n.finished,{instanceId:t}));return this.syncNow(),()=>{this.outputs.delete(e)}}publish(e,t,n={}){this.rememberImagePullTerminal(e,t);const r=this.createEvent(e,t,n);for(const e of this.outputs)this.sendForwarded(e,r)}rememberImagePullTerminal(e,t){if(e===kt){const e=xt.safeParse(t);if(!e.success)return;const n=e.data,r=this.imagePullTerminalByInstance.get(n.instanceId);if(r&&n.generation<r.output.generation)return;const o=r?.output.generation===n.generation,i=n.replay||!o?n.data:`${r.tail}${n.data}`.slice(-262144);return void this.imagePullTerminalByInstance.set(n.instanceId,{output:n,tail:i})}if(e===_t){const e=Tt.safeParse(t);if(!e.success)return;const n=e.data,r=this.imagePullTerminalByInstance.get(n.instanceId);r?.output.generation===n.generation&&(r.finished=n)}}publishInstanceLifecycle(e){this.publish(Bt,function(e){return Kt.parse(kn(e))}(e),{instanceId:e.id});const t=this.imagePullTerminalByInstance.get(e.id);"ready"===e.imageProvisioning?.phase&&"succeeded"===t?.finished?.outcome&&this.imagePullTerminalByInstance.delete(e.id)}createEvent(e,t,n){const r=++this.localSequence;return{v:1,id:`node_evt_${Date.now().toString(36)}_${r.toString(36)}`,seq:r,type:e,topic:bn(e),createdAt:(new Date).toISOString(),payload:t,scope:n}}sendForwarded(e,t){const n=JSON.stringify({type:"node-agent.event.forwarded",event:t});e.readyState===l.OPEN&&e.send(n)}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(){const e=new Set(this.state.listInstances().map(e=>e.id));for(const t of this.imagePullTerminalByInstance.keys())e.has(t)||this.imagePullTerminalByInstance.delete(t);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 t=new Set;for(const e of this.state.listInstances()){const n=On(e);if(!n)continue;t.add(e.id);const r=this.retries.get(e.id);r?.timer.pending&&r.url!==n&&(r.timer.cancel(),this.retries.delete(e.id)),this.socketUrls.get(e.id)!==n&&this.sockets.has(e.id)&&(this.sockets.get(e.id)?.close(),this.sockets.delete(e.id)),this.sockets.has(e.id)||this.retries.get(e.id)?.timer.pending||this.connect(e.id,n)}for(const[e,n]of this.sockets)if(!t.has(e)){n.close(),this.sockets.delete(e),this.socketUrls.delete(e);const t=this.retries.get(e);t?.timer.cancel(),this.retries.delete(e)}for(const[e,n]of this.retries)t.has(e)||(n.timer.cancel(),this.retries.delete(e),this.socketUrls.delete(e))}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 Sn,url:t}),r.readyState===l.OPEN&&(r.send(JSON.stringify({v:1,type:"subscribe",topics:[b,"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:bn(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=wn.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&&On(t);if(!n||!this.outputs.size)return;const r=this.retries.get(e)??{timer:new Sn};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!==b)return;if("ai-session.message-delta"===t.type)return;const n=_n(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=_n(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 En(e){const t=Kt.safeParse(kn(e));return t.success?t.data:void 0}function kn(e){return{instanceId:e.id,revision:e.stateRevision,updatedAt:e.updatedAt,status:e.status,health:e.health,connectionStatus:e.connectionStatus,accessStatus:"online"===e.connectionStatus||"online"===e.agentStatus?"reachable":"endpoint-unreachable",imageProvisioning:e.imageProvisioning,workspace:e.workspace,runtime:e.runtime,lastHeartbeatAt:e.lastHeartbeatAt}}function _n(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 On(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 xn=new Set(["provisioning","starting","registering","registered","running","unhealthy"]),Tn=JSON.stringify({id:"{{.ID}}",name:"{{.Name}}",cpu:"{{.CPUPerc}}",memory:"{{.MemUsage}}",memoryPercent:"{{.MemPerc}}",network:"{{.NetIO}}",blockIo:"{{.BlockIO}}",pids:"{{.PIDs}}"});class Cn{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 xn.has(t.status)?(await this.collect([t]),this.snapshots.get(e)):Mn(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=>xn.has(e.status)&&(e.runtime.containerId||e.runtime.containerName));for(const t of e.filter(e=>xn.has(e.status)&&!e.runtime.containerId&&!e.runtime.containerName))this.update(Ln(t.id));for(const t of e.filter(e=>!xn.has(e.status)))this.update(Mn(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",Tn,...n],{timeoutMs:5e3});this.applyDockerOutput(t,e.stdout,r)}catch(e){if(1===t.length)return void this.update(Fn(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",Tn,t],{timeoutMs:5e3});this.applyDockerOutput([e],n.stdout,r)}catch(t){this.update(Fn(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=Pn(e.id),i=Pn(e.name),s=o||i;if(!s)continue;const a=Rn(Pn(e.memory)),c=Rn(Pn(e.network)),d=Rn(Pn(e.blockIo)),l=Dt.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:jn(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}:Fn(t.id,n,"Container metrics were not returned by Docker."))}}update(e){const t=Dt.parse(e);this.snapshots.set(t.instanceId,t),this.publish?.(t)}}function Rn(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(Pn(e).replace(/%$/,""));return Number.isFinite(n)&&n>=0?{[t]:n}:void 0}function jn(e){const t=Number(Pn(e));return Number.isFinite(t)&&t>=0?t:void 0}function Pn(e){return"string"==typeof e||"number"==typeof e?String(e).trim():""}function Mn(e){return{instanceId:e,runtimeKind:"docker",state:"stopped",sampledAt:(new Date).toISOString()}}function Ln(e){return{instanceId:e,runtimeKind:"docker",state:"pending",sampledAt:(new Date).toISOString()}}function Fn(e,t,n){return{instanceId:e,runtimeKind:"docker",state:"unavailable",sampledAt:t,error:n.slice(0,2048)}}function $n(e){try{return t.statSync(e).isDirectory()}catch{return!1}}function Un(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()&&$n(n.join(i,e.name))).sort((e,t)=>e.name.localeCompare(t.name)).slice(0,80).flatMap(e=>{const t=Un(n.join(i,e.name),r-1);return t?[t]:[]}):[];return{name:s,path:i,children:c}}function Hn(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($n)}()).flatMap(t=>{const n=Un(t,e.depth);return n?[n]:[]})}const qn=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(),Kn=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(),Gn=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:vt.optional(),source:at,sourceSnapshot:u.z.record(u.z.string(),u.z.unknown()).default({}),config:u.z.object({autoImportAgentConfigs:u.z.boolean().optional(),defaultCodexPermissionMode:j.optional()}).strict().optional(),modelSelection:qt.shape.modelSelection}).strict(),Vn=u.z.object({name:u.z.string().trim().min(1).max(160).optional(),config:u.z.object({autoImportAgentConfigs:u.z.boolean().optional(),defaultCodexPermissionMode:j.optional()}).strict().optional(),modelSelection:qt.shape.modelSelection.optional()}).strict(),Jn=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(),Wn=Jn.omit({id:!0}).partial().strict();function Yn(){return process.env.TASK_HANDOFF_NODE_AGENT_DATA_DIR||n.join(g.homedir(),".config","task-handoff","node-agent")}function Xn(e=Yn()){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 Qn(e){t.mkdirSync(e,{recursive:!0})}function Zn(e,t){Qn(n.dirname(e)),h.sync(e,`${JSON.stringify(t,null,2)}\n`,{encoding:"utf8"})}function er(e,t){console.warn(JSON.stringify({message:e,...t}))}function tr(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function nr(e,n){const r=n.logger||er;let o;try{o=JSON.parse(t.readFileSync(e,"utf8"))}catch(t){return void r("stored JSON could not be read",{filePath:e,...tr(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 rr(e){return`${e}_${m.randomUUID().replace(/-/g,"").slice(0,20)}`}function or(){return m.randomBytes(24).toString("base64url")}class ir{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){Qn(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=nr(n.join(this.directory,e),this.options);return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?nr(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 Zn(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 sr{filePathValue;defaults;options;constructor(e,t,n={}){this.filePathValue=e,this.defaults=t,this.options=n}init(){Qn(n.dirname(this.filePathValue)),t.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),t.existsSync(this.filePathValue)?nr(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 Zn(this.filePathValue,n),n}}class ar 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 cr(e){return n.join(e,"owner.json")}function dr(e){return n.join(e,"initializing.json")}function lr(e){return n.join(e,"recovering.json")}function ur(e){try{const n=JSON.parse(t.readFileSync(cr(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 mr(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 pr(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=gr(e);if(i?.token!==r.token)throw hr(o),new Error(`Process lock initialization was superseded: ${e}`);t.renameSync(o,cr(e)),hr(dr(e))}function gr(e){try{return JSON.parse(t.readFileSync(dr(e),"utf8"))}catch{return}}function hr(e){try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function fr(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function yr(e){try{const n=gr(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 Ir(e,r){try{return t.writeFileSync(lr(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=lr(e),o=br(r),i=Date.parse(o?.startedAt||"");if(mr(o?.pid)||Number.isFinite(i)&&Date.now()-i<2e3)return!1;const s=n.join(e,`.recovering-${m.randomUUID()}.stale`);try{t.renameSync(r,s)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const a=br(s);if(a?.token===o?.token)return hr(s),!0;try{t.linkSync(s,r)}catch(e){if("EEXIST"!==e.code)throw e}finally{hr(s)}return!1}(e)&&Ir(e,r);if("ENOENT"===o.code)return!1;throw o}}function br(e){try{return JSON.parse(t.readFileSync(e,"utf8"))}catch{return}}function zr(e,t){return br(lr(e))?.token===t}function Ar(e,t=process.platform){const r=n.resolve(e),o=m.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 wr="TASK_HANDOFF_NODE_AGENT_V1";function Sr(e=""){return m.createHash("sha256").update(e).digest("hex")}function vr(e,t){return m.createHmac("sha256",e).update(function(e){return[wr,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function Nr(e){const t=e.timestamp||(new Date).toISOString(),n=e.nonce||m.randomBytes(18).toString("base64url"),r=Sr(e.body||""),o=vr(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 kr(e){return"string"==typeof e&&Er.has(e.toUpperCase())?e.toUpperCase():"GET"}function _r(){return(new Date).toISOString()}function Or(e,t){if(!e)return!0;const n=f.valid(e),r=f.valid(t);return n&&r?f.gt(r,n):e!==t}async function xr(e,t,n){const r=Rr(n),o=await e(Tr(),["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 Tr(){return process.env.TASK_HANDOFF_NPM_COMMAND||"npm"}function Cr(e){return et.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:_r()})}function Rr(e){return"stable"===e?"latest":e}function Dr(e){if(Array.isArray(e))return e.map(Dr).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(Dr).find(Boolean)}function jr(e){return{x64:"amd64",ia32:"386",arm:"arm",arm64:"arm64",ppc64:"ppc64le",s390x:"s390x",riscv64:"riscv64"}[e]||e}function Pr(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 Mr{records;constructor(e){this.records=new ir(e.updatesDir,{schema:tt})}init(){this.records.init()}list(){return this.records.list().sort((e,t)=>t.createdAt.localeCompare(e.createdAt))}create(e,t){const n=_r();return this.records.put(tt.parse({id:rr("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(tt.parse({...n,...t,id:e,createdAt:n.createdAt,updatedAt:_r()}))}run(e,t){(async()=>{this.patch(e.id,{status:"updating",startedAt:_r(),error:void 0});try{await t(e),this.patch(e.id,{status:"succeeded",completedAt:_r()})}catch(t){this.patch(e.id,{status:"failed",error:t instanceof Error?t.message:String(t),completedAt:_r()})}})()}}const Lr=u.z.record(u.z.string(),u.z.string()),Fr=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 $r{directory;constructor(e){this.directory=e}get(e){try{return Lr.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=Lr.parse(n);t.mkdirSync(this.directory,{recursive:!0,mode:448});const o=this.filePath(e);return h.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 Ur{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=mt.parse(e);this.init();const r=this.filePath(n.id);return h.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=mt.safeParse(r);if(i.success&&i.data.id===o&&St(i.data)===i.data.id)return i.data;const s=mt.strip().safeParse(r);return s.success&&s.data.id===o&&St(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 Hr{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=ht.safeParse(r);if(o.success)return o.data;const i=ht.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=ht.parse(e);this.init();const r=this.filePath(n.instanceId);return h.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 qr{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=Sr(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&&m.timingSafeEqual(n,r)}(t.signature,vr(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 Br=u.z.string().trim().min(1).max(160),Kr=u.z.string().datetime(),Gr=u.z.string().min(1).max(4096),Vr=u.z.string().trim().url().max(2048),Jr=u.z.object({tokenHash:u.z.string().trim().min(1).max(256),expiresAt:Kr,createdAt:Kr,controlPlaneName:u.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:Vr.optional()}).strip(),Wr=u.z.object({id:Br,keyId:Br,name:u.z.string().trim().min(1).max(160).optional(),url:Vr.optional(),secret:Gr,pairedAt:Kr,updatedAt:Kr,active:u.z.boolean().default(!0)}).strip(),Yr=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(),Xr=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(),Qr=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 Zr(){return(new Date).toISOString()}const eo=new Set(["nodeId","createdAt","updatedAt","pairingInvites","remoteControlPlanes"]),to=new Set(["tokenHash","expiresAt","createdAt","controlPlaneName","controlPlaneUrl"]),no=new Set(["id","keyId","name","url","secret","pairedAt","updatedAt","active"]);function ro(e,t){console.warn(JSON.stringify({message:e,...t}))}function oo(e,t){return Object.assign(new Error(e,void 0===t?void 0:{cause:t}),{code:"NODE_AGENT_IDENTITY_INVALID"})}function io(e,t){return Object.keys(e).filter(e=>!t.has(e))}function so(e,t,n){const r=Kr.safeParse(e);return r.success?r.data:(n("invalid node agent identity timestamp was replaced",{field:t}),Zr())}function ao(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw oo("Node agent identity must be a JSON object.");const n=e,r=Br.safeParse(n.nodeId);if(!r.success)throw oo("Node agent identity has an invalid nodeId.",r.error);const o=r.data,i=io(n,eo);return i.length&&t("unknown node agent identity fields were ignored",{fields:i}),{nodeId:o,createdAt:so(n.createdAt,"createdAt",t),updatedAt:so(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=io(e,to);n.length&&t("unknown node agent pairing invite fields were ignored",{fields:n});const r=Jr.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=io(e,no);n.length&&t("unknown node agent remote control-plane fields were ignored",{fields:n});const r=Wr.safeParse(e);return r.success?[r.data]:(t("invalid node agent remote control-plane was ignored",{issues:r.error.issues}),[])}):[]}}class co{paths;logger;constructor(e,t={}){this.paths=e,this.logger=t.logger||ro}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 oo(`Node agent identity contains invalid JSON: ${this.paths.identityPath}`,e)}return ao(n,this.logger)}write(e){t.mkdirSync(n.dirname(this.paths.identityPath),{recursive:!0});const r=ao({...e,updatedAt:Zr()},this.logger);h.sync(this.paths.identityPath,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384})}}function lo(){return(new Date).toISOString()}class uo{store;constructor(e){this.store=new co(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=rr("node");return this.writeNodeId(n),n}createPairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=lo(),r=new Date(Date.now()+(e.expiresInMs||6e5)).toISOString(),o=or(),i={tokenHash:Sr(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=Sr(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=lo(),i={id:e.controlPlaneId||rr("cp"),keyId:rr("key"),...e.controlPlaneName||r.controlPlaneName?{name:e.controlPlaneName||r.controlPlaneName}:{},...e.controlPlaneUrl||r.controlPlaneUrl?{url:e.controlPlaneUrl||r.controlPlaneUrl}:{},secret:or(),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=lo();return{id:rr("cp"),keyId:rr("key"),...e.name?{name:e.name}:{},url:e.url,secret:or(),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:lo(),updatedAt:lo(),pairingInvites:[],remoteControlPlanes:[]}}writeNodeId(e){const t=this.store.read();this.store.write({...t||{createdAt:lo()},nodeId:e,updatedAt:lo(),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 mo=new Set(["content-encoding","content-length","transfer-encoding"]),po=["codex","claude"],go=5e3,ho="runtime_local_host",fo="task-handoff.node-agent.builtin",yo=new Set(["linux","darwin","win32","freebsd","openbsd","aix","sunos"]);function Io(e){return yo.has(e)?e:"unknown"}function bo(e){const t={...e};return delete t[fo],t}const zo=u.z.object({version:u.z.literal(1),externalListener:jt}).strict(),Ao=u.z.object({}).strict().default({}),wo=nt.strict();function So(e){const t=process.env[e]?.trim();return t||void 0}function vo(){try{const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&"SCRIPT"===y.tagName.toUpperCase()&&y.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&"SCRIPT"===y.tagName.toUpperCase()&&y.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 No(e){return Object.fromEntries([...e.entries()].filter(([e])=>!mo.has(e.toLowerCase())))}function Eo(e){return e.bodyBase64?Buffer.from(e.bodyBase64,"base64"):e.body}const ko=67108864;function _o(){const e=Number(process.env.TASK_HANDOFF_INSTANCE_PROXY_MAX_RESPONSE_BYTES);return Number.isSafeInteger(e)&&e>0?e:67108864}function Oo(e){const t=e.authorization;return"string"==typeof t&&t.startsWith("Bearer ")?t.slice(7).trim():void 0}function xo(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 To(e){return!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}function Co(){return(new Date).toISOString()}function Ro(e){return"all-ipv4"===e?"0.0.0.0":"127.0.0.1"}function Do(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 jo(e){return"local-folder"===e.type?ct.parse({mode:"local-bind",path:"/workspace",readOnly:!1}):ct.parse({mode:"git-clone",path:"/workspace",readOnly:!1})}function Po(e){const t=at.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 lt.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:jo(t),labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt})}async function Mo(e,t){if(!t.target.web)return"unknown";const n=new AbortController,r=setTimeout(()=>n.abort(),2e3);try{return(await e(`${Uo(t)}/api/health`,{signal:n.signal})).ok?"reachable":"endpoint-unreachable"}catch{return"endpoint-unreachable"}finally{clearTimeout(r)}}async function Lo(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 Fo(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=Uo(t),i=Number(process.env.TASK_HANDOFF_CONFIG_AUTO_IMPORT_TIMEOUT_MS||go);for(const s of po)try{const a=await Lo(e,`${o}/api/config-sync/import/${encodeURIComponent(s)}`,{method:"POST"},Number.isFinite(i)&&i>0?i:go);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 $o(e,t,n){const r=t.requireInstance(n);if("reachable"!==r.targetStatus||!r.target.web)return!1;const o=await Lo(e,`${Uo(r)}/api/internal/model-environment`,{method:"PUT",headers:{"content-type":"application/json",authorization:`Bearer ${r.registrationToken}`},body:JSON.stringify(t.resolvedAssignedModelEnvironment(n))},go);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 Uo(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 Ho(e,t){const n=new Error(t);throw Object.assign(n,{statusCode:403,code:e}),n}function qo(e,t){e!==ye&&console.warn(JSON.stringify({message:"protocol version mismatch",peer:t,expectedProtocolVersion:ye,actualProtocolVersion:e||"missing",errorCode:"PROTOCOL_VERSION_MISMATCH"}))}function Bo(e={}){return{requiresImage:!1,supportsControlledInstanceApi:!0,supportsContainerLifecycle:!1,supportsAppSessions:!0,supportsHostSessions:!0,artifactKind:"none",isolation:"none",...e}}function Ko(){const e=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV?.trim();if(e){let t;try{t=JSON.parse(e)}catch{const e=new Error("TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV must be a JSON array of command arguments.");throw Object.assign(e,{statusCode:500,code:"LOCAL_CONTROLLED_COMMAND_INVALID"}),e}if(!Array.isArray(t)||0===t.length||t.some(e=>"string"!=typeof e||0===e.length)){const e=new Error("TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV must be a non-empty JSON array of non-empty strings.");throw Object.assign(e,{statusCode:500,code:"LOCAL_CONTROLLED_COMMAND_INVALID"}),e}return t}const r=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND?.trim();if(r)return r.split(/\s+/);const o=n.resolve(process.cwd(),"bin","task-handoff.js");if(t.existsSync(o))return[process.execPath,o,"web"];const i=new Error("No local controlled-instance command is configured. Set TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND_ARGV to an explicit command argv.");throw Object.assign(i,{statusCode:500,code:"LOCAL_CONTROLLED_COMMAND_MISSING"}),i}function Go(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 Vo(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 Jo(e,t){const n=t.get(e.id);if(n&&!n.killed)return n.kill("SIGTERM"),t.delete(e.id),void await Vo(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 Wo=new Set(["provisioning","starting","registering","registered","running"]);async function Yo(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 Xo{executor;platform;runCommand;constructor(e,t,n){this.executor=e,this.runCommand=t,this.platform=n}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)}async check(e){try{const t=(await this.runCommand("docker",["version","--format","{{.Server.Version}}"],{timeoutMs:5e3})).stdout.trim();return{status:"online",capabilities:{...e.capabilities,daemon:{status:"online",hostPlatform:Io(this.platform),...t?{serverVersion:t}:{}}}}}catch(t){return{status:"offline",capabilities:{...e.capabilities,daemon:{status:"offline",hostPlatform:Io(this.platform),error:t instanceof Error?t.message:String(t)}}}}}}class Qo{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 Go(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]=Ko(),l=[...d,"--host","127.0.0.1","--port",String(o)],u=t.openSync(n.join(a,"controlled-instance.out.log"),"a"),m=t.openSync(n.join(a,"controlled-instance.err.log"),"a"),p=s.spawn(c,l,{cwd:r,detached:!1,stdio:["ignore",u,m],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(m);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)})}(p)}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,p),p.on("error",e=>{try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] process error pid=${p.pid??""} error=${e.message}\n`)}catch{}}),p.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=${p.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:p.pid,port:o,labels:{...e.instance.runtime.labels,"task-handoff.runtime-kind":"local"}}}}async stop(e){return await Jo(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 Jo(e.instance,this.processByInstanceId),this.start(e)}async delete(e){return this.stop(e)}async check(e){const[t,n]=await Promise.all([Yo(this.runCommand,"codex"),Yo(this.runCommand,"claude")]);return{status:"online",capabilities:Bo({...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=>Vo(e)))}}class Zo{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 Qo&&await this.local.stopAll()}}class ei extends ir{onStored;setOnStored(e){this.onStored=e}put(e){const t=super.get(e.id)?.stateRevision||0,n=super.put(qt.parse({...e,stateRevision:Math.max(e.stateRevision||0,t)+1}));return this.onStored?.(n),n}}class ti{nodeId;paths;localFolders;nodeRuntimes;controlledInstances;models;modelAssignments;modelEnvironments;updateJobs;node;listenerPort;containerUrlOverride;platform;constructor(e,t,n,r,o,i){this.paths=e,this.nodeId=t,this.localFolders=new ir(e.localFoldersDir,{schema:st}),this.nodeRuntimes=new ir(e.nodeRuntimesDir,{schema:Rt}),this.controlledInstances=new ei(e.controlledInstancesDir,{schema:qt,sanitize:e=>Vt(e,e=>{console.warn(JSON.stringify({message:"legacy controlled instance field was ignored",...e}))})}),this.models=new Ur(e.nodeModelsDir,t),this.modelAssignments=new Hr(e.modelAssignmentsDir),this.modelEnvironments=new $r(e.modelEnvironmentsDir),this.updateJobs=new Mr(e),this.listenerPort=o,this.platform=i,this.containerUrlOverride=r;const s=Co();this.node=Ct.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:s,updatedAt:s})}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=Co();this.nodeRuntimes.put(Rt.parse({id:"runtime_local_docker",nodeId:this.nodeId,name:"Local Docker",type:"docker",status:"unknown",accessStrategy:"direct-port",capabilities:{},labels:{},createdAt:e,updatedAt:e}))}if("win32"!==this.platform){const e=this.nodeRuntimes.get(ho),t=Co();this.nodeRuntimes.put(Rt.parse({id:ho,nodeId:this.nodeId,name:"Local Runtime",type:"local",status:e?.status||"unknown",accessStrategy:"node-proxy",capabilities:Bo(e?.capabilities),labels:{...e?.labels,[fo]:"true"},createdAt:e?.createdAt||t,updatedAt:e?.updatedAt||t}))}for(const e of this.nodeRuntimes.list())e.nodeId!==this.nodeId&&this.nodeRuntimes.put(Rt.parse({...e,nodeId:this.nodeId,updatedAt:Co()}));for(const e of this.localFolders.list())e.nodeId!==this.nodeId&&this.localFolders.put(st.parse({...e,nodeId:this.nodeId,updatedAt:Co()}))}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=Co()}runningInstanceCount(){const e=new Set(["created","stopped","failed"]);return this.listInstances().filter(t=>!e.has(t.status)).length}createLocalFolder(e){const t=Co(),n=st.parse({...e,id:e.id||rr("folder"),nodeId:this.nodeId,labels:e.labels||{},createdAt:t,updatedAt:t});return this.localFolders.put(n)}createRuntime(e){if("local"===e.type){const e="win32"===this.platform,t=new Error(e?"Local Runtime is not supported on Windows.":"Local Runtime is built in and cannot be added manually.");throw Object.assign(t,{statusCode:e?400:409,code:e?"LOCAL_RUNTIME_UNSUPPORTED":"LOCAL_RUNTIME_BUILTIN"}),t}const t=Co(),n=Rt.parse({...e,id:e.id||rr("runtime"),nodeId:this.nodeId,accessStrategy:e.accessStrategy||(r=e.type,"docker"===r?"direct-port":"node-proxy"),capabilities:e.capabilities||{},labels:bo(e.labels),createdAt:t,updatedAt:t});var r;return this.nodeRuntimes.put(n)}updateRuntime(e,t){const n=this.requireRuntime(e);if("true"===n.labels[fo]){const t=new Error(`Built-in runtime ${e} cannot be modified.`);throw Object.assign(t,{statusCode:400,code:"NODE_RUNTIME_BUILTIN"}),t}if("local"===n.type||"local"===t.type){const e="win32"===this.platform,t=new Error(e?"Local Runtime is not supported on Windows.":"Local Runtime is built in and cannot be configured manually.");throw Object.assign(t,{statusCode:e?400:409,code:e?"LOCAL_RUNTIME_UNSUPPORTED":"LOCAL_RUNTIME_BUILTIN"}),t}const r=Rt.parse({...n,...t,id:n.id,nodeId:this.nodeId,accessStrategy:t.accessStrategy||n.accessStrategy,capabilities:t.capabilities||n.capabilities,labels:t.labels?bo(t.labels):n.labels,createdAt:n.createdAt,updatedAt:Co()});return this.nodeRuntimes.put(r)}deleteRuntime(e){if("true"===this.requireRuntime(e).labels[fo]){const t=new Error(`Built-in runtime ${e} cannot be deleted.`);throw Object.assign(t,{statusCode:400,code:"NODE_RUNTIME_BUILTIN"}),t}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=Rt.parse({...n,...e,id:n.id,nodeId:this.nodeId,createdAt:n.createdAt,updatedAt:Co()});return this.nodeRuntimes.put(t)}):this.nodeRuntimes.put(Rt.parse({...n,status:"unknown",updatedAt:Co()}))}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=Gt(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 ${ye}: ${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=Gt(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=Co(),n=St(e),r=this.models.get(n),o=mt.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=St(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(mt.parse(e));return this.publicModel(n,this.modelReferenceIds(n.id).length)}updateModel(e,t){const n=this.requireModel(e),r=mt.parse({...n,...t,key:t.key?.trim()?t.key:n.key,createdAt:n.createdAt,updatedAt:Co()}),o=St(r),i=this.models.put(mt.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=ht.parse({instanceId:e,codexModelHash:t.codexModelHash,claudeModelHash:t.claudeModelHash,updatedAt:Co()});this.modelAssignments.put(o);try{return{assignment:o,instance:this.controlledInstances.put(qt.parse({...n,modelSelection:t.modelSelection,updatedAt:Co()}))}}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(St(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 gt.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=>!Fr.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=St({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=Co();return this.models.put(mt.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=Co(),o=e.id||rr("inst"),i=at.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?vt.parse(e.image):void 0,a=s?(()=>{const{reference:e,...t}=s;return Nt.parse({...t,requestedReference:e})})():void 0,c="local"===t.type&&"local-folder"===i.type?n.resolve(i.path):void 0,d=qt.parse({id:o,name:e.name||`instance-${o.replace(/^inst_?/,"").slice(0,6)}`,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:{autoImportAgentConfigs:e.config?.autoImportAgentConfigs??!0,defaultCodexPermissionMode:e.config?.defaultCodexPermissionMode??("docker"===t.type?"full-access":"ask")},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:or(),createdAt:r,updatedAt:r});return this.controlledInstances.put(d)}registerInstance(e,t,n){const r=Wt.parse(t),o=Co(),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),qo(r.protocolVersion,`Instance ${e}`);const s=qt.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=Yt.parse(t);qo(o.protocolVersion,`Instance ${e}`);const i=Co(),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=qt.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&&Ho("INSTANCE_ID_MISMATCH",`Instance ${t.instanceId} cannot register as ${e.id}.`),t.nodeId&&t.nodeId!==this.nodeId&&Ho("INSTANCE_NODE_MISMATCH",`Instance ${e.id} belongs to node ${this.nodeId}.`),t.runtimeId&&t.runtimeId!==e.runtimeId&&Ho("INSTANCE_RUNTIME_MISMATCH",`Instance ${e.id} belongs to runtime ${e.runtimeId}.`),t.imageId&&t.imageId!==e.imageId&&Ho("INSTANCE_IMAGE_MISMATCH",`Instance ${e.id} belongs to image ${e.imageId}.`)}validateInstanceToken(e,t){e.registrationToken&&t===e.registrationToken||Ho("INSTANCE_REGISTRATION_TOKEN_INVALID",`Invalid registration token for instance ${e.id}.`)}context(e,t=this.resolvedAssignedModelEnvironment(e.id)){const n=e.imageSnapshot||Nt.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:Po(e),image:n,node:this.node,runtime:this.requireRuntime(e.runtimeId),instance:e,nodeAgentUrl:this.containerUrl,modelEnv:t}}}class ni{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 Pt.parse({...this.config,host:Ro(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:Ro(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=Mt.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 ${Ro(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:Ro(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 ri(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)return o.diagnostic({instanceId:r,action:"start",reason:i,runtimeId:s.runtimeId,imageId:s.imageId,imagePhase:s.imageProvisioning.phase},"node instance start queued until image provisioning completes"),e.controlledInstances.put(qt.parse({...s,status:"starting",updatedAt:Co()}));o.diagnostic({instanceId:r,action:"start",reason:i,runtimeId:s.runtimeId,imageId:s.imageId},"node instance start requested");const a=e.controlledInstances.put(qt.parse({...s,status:"starting",updatedAt:Co()})),c=t.forRuntime(e.requireRuntime(a.runtimeId)),d=await c.start(e.context(a)),l=await Mo(n,qt.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:Co()})),u=qt.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:Co()}),m=e.controlledInstances.put(u);return await Fo(n,m,"start",o),o.diagnostic({instanceId:r,action:"start",reason:i,status:m.status,connectionStatus:m.connectionStatus,targetStatus:m.targetStatus,targetWeb:m.target.web,containerName:m.runtime.containerName},"node instance start completed"),m}function oi(e,t){const n=t instanceof Error?t.message:String(t);return qt.parse({...e,status:"failed",health:"failed",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",workspace:{...e.workspace,error:n},updatedAt:Co()})}function ii(e,t,n=Co()){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 si(e){return qt.parse({...e,health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:Co()})}async function ai(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}`,m=e.containerUrl||process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,p=Xn(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,f=new uo(p),I=f.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"),z=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||Ar(p.dataDir),A="local-ipc"===b?`ipc://${encodeURIComponent(z)}`:s,w=e.platform||process.platform,S=new ti(p,I,s,m,o,w);S.node.connectionMode=b,S.node.controlEndpoint=A,S.node.endpoint=A,S.init();const v=e.dockerCommandRunner||nn,N=new on(v,e.dockerTerminalCommandRunner),E=new sn(v,{publishHost:"127.0.0.1",imageService:N}),k=new Zo(new Xo(E,v,w),new Qo(e.dockerCommandRunner||nn,p,()=>S.localNodeAgentUrl)),_=e.updateCommandRunner||e.dockerCommandRunner||nn,O=new on(_),x=e.fetchImpl||fetch,T=c({logger:e.logger??!0}),C={requests:0,active:0,completed:0,aborted:0,limitRejected:0,responseBytes:0,totalDurationMs:0,maxResponseBytes:_o()};T.decorate("nodeAgentState",S),await T.register(d);const R=new Nn(S,r,{logger:T.log,safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0});S.controlledInstances.setOnStored(e=>R.publishInstanceLifecycle(e)),R.start(),T.decorate("nodeAgentEventForwarder",R);const D=new Cn(v,()=>S.listInstances().filter(e=>"docker"===S.requireRuntime(e.runtimeId).type),e=>R.publish("instance.metrics.snapshot",e,{instanceId:e.instanceId}));D.start(),T.decorate("nodeAgentRuntimeMetrics",D);const j=(q=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(q||"").trim().toLowerCase())),P=new qr(f,I,g,h),M=(e,t)=>{j&&T.log.info(e,t)},L={diagnostic:M,warn:(e,t)=>T.log.warn(e,t)},F=async(e,t)=>{try{const t=await ri(S,k,x,e,L);return R.syncNow(),t}catch(n){const r=oi(S.requireInstance(e),n);throw S.controlledInstances.put(r),R.syncNow(),L.warn({instanceId:e,action:"start",reason:t,error:n instanceof Error?n.message:String(n)},"node instance start failed"),n}},$=e=>{e.imageProvisioning&&async function(t,n,r,o,i,s,a){const c=e=>{const n=t.controlledInstances.get(r);n&&n.imageProvisioning?.generation===o&&n.imageSnapshot&&(t.controlledInstances.put(qt.parse({...n,status:"starting"===n.status?"starting":"provisioning",imageProvisioning:{...n.imageProvisioning,phase:e,error:void 0,updatedAt:Co()},updatedAt:Co()})),i())},d=t.controlledInstances.get(r);if(!d?.imageSnapshot||d.imageProvisioning?.generation!==o)return;let l=0,u=!1;try{const m=await n.ensure(d.imageSnapshot.requestedReference,c,e=>{u=!0,l=e.sequence,a?.(e)});u&&a?.({sequence:l+1,outcome:"succeeded"});const p=t.controlledInstances.get(r);if(!p?.imageSnapshot||p.imageProvisioning?.generation!==o)return;t.controlledInstances.put(qt.parse({...p,status:"starting"===p.status?"starting":"created",health:"unknown",imageSnapshot:{...p.imageSnapshot,requestedReference:m.requestedReference,resolvedDigest:m.resolvedDigest,resolvedReference:m.resolvedReference},imageProvisioning:{...p.imageProvisioning,phase:"ready",error:void 0,updatedAt:Co()},updatedAt:Co()})),i(),s.diagnostic({instanceId:r,action:"image.provision",reference:m.requestedReference,digest:m.resolvedDigest,pulled:m.pulled},"node instance image provisioning completed"),"starting"===p.status&&await((async e=>{await F(e,"image-ready").catch(()=>{})})(e.id))}catch(e){u&&a?.({sequence:l+1,outcome:"failed"});const n=t.controlledInstances.get(r);if(!n?.imageProvisioning||n.imageProvisioning.generation!==o)return;const c=e instanceof Error?e.message:String(e);t.controlledInstances.put(qt.parse({...n,status:"failed",health:"failed",imageProvisioning:{...n.imageProvisioning,phase:"failed",error:c,updatedAt:Co()},updatedAt:Co()})),i(),s.warn({instanceId:r,action:"image.provision",reference:n.imageProvisioning.requestedReference,error:c},"node instance image provisioning failed")}}(S,N,e.id,e.imageProvisioning.generation,()=>R.syncNow(),L,t=>{const n={instanceId:e.id,generation:e.imageProvisioning.generation,requestedReference:e.imageProvisioning.requestedReference,observedAt:Co()};if("outcome"in t)R.publish(_t,{...n,sequence:1e3*t.sequence,outcome:t.outcome},{instanceId:e.id});else for(const[r,o]of function(e,t=6e4){const n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}(t.data).entries())R.publish(kt,{...n,sequence:1e3*t.sequence+r,data:o,...t.replay?{replay:!0}:{}},{instanceId:e.id})})},U=async e=>{if("node-agent"===e.target.component)return async function(e){const t=await xr(e.runCommand,"@task-handoff/node-agent",e.channel);return t?et.parse({target:{component:"node-agent"},source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:t,updateAvailable:Or(e.currentVersion,t),checkedAt:_r()}):Cr({target:{component:"node-agent"},channel:e.channel,currentVersion:e.currentVersion})}({channel:e.channel,currentVersion:vo(),runCommand:_});const t=S.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 xr(e.runCommand,"@task-handoff/controlled-instance",e.channel),r=e.instance.build?.packageVersion||e.instance.instanceVersion;return n?et.parse({target:t,source:"npm",channel:e.channel,currentVersion:r,availableVersion:n,updateAvailable:Or(r,n),checkedAt:_r()}):Cr({target:t,channel:e.channel,currentVersion:r})}if("docker"===e.runtime.type){const n=e.instance.imageSnapshot?.requestedReference;if(!n)return et.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:_r()});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}:${Rr(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 Dr(e);const o=r.filter(e=>Pr(e));if(!o.length)return Dr(e);const i=o.find(e=>Pr(e)===jr(t));return i?Dr(i):void 0}(JSON.parse(o.stdout),e.arch);if(!i)throw new Error(`Docker registry did not return a digest for ${r} matching ${jr(e.arch||process.arch)}.`);const s=e.instance.build?.imageDigest;return et.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:s,availableVersion:i,artifactRef:r,updateAvailable:s!==i,checkedAt:_r()})}return et.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:_r()})}({channel:e.channel,instance:t,runtime:S.requireRuntime(t.runtimeId),runCommand:_})},H=async e=>{if("controlled-instance"!==e.target.component)return;const r=S.requireInstance(e.target.instanceId),o=S.requireRuntime(r.runtimeId),i=k.forRuntime(o);let s;if("local"===o.type){const r=Tr(),o=(await _(r,["prefix","--global"])).stdout.trim(),[i]=Ko(),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 _(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 _("docker",["pull",t]),s=await O.ensure(t)}}await i.delete(S.context(r,{})),S.controlledInstances.put(function(e){return qt.parse({...e,status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:Co()})}(qt.parse({...r,..."docker"===o.type&&s&&r.imageSnapshot?{...ii(r,s)}:{}}))),await ri(S,k,x,r.id,L,"update")};var q;T.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);(j||r.statusCode>=500)&&(r.statusCode>=500?T.log.error.bind(T.log):T.log.warn.bind(T.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}:{}}})}),T.addHook("preHandler",async e=>{const t=P.verify(e);if(t)e.nodeAgentAuthKeyId=t;else if(To(e)){if(r&&Oo(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(P.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 xo(e.ip)||xo(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&&Oo(e.headers)!==r){const e=new Error("Invalid node agent token.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),e}}}),T.addHook("onClose",async()=>{D.stop(),R.stop(),await async function(e,t){await t.stopAll();for(const t of e.listInstances())"local"===e.requireRuntime(t.runtimeId).type&&e.controlledInstances.put(si(t))}(S,k)}),T.decorate("nodeAgentRestoreLocalInstances",async()=>{for(const e of S.listInstances().filter(e=>["provisioning","starting"].includes(e.status)&&"ready"!==e.imageProvisioning?.phase&&"docker"===S.requireRuntime(e.runtimeId).type))$(e);const e=S.listInstances().filter(e=>"local"===S.requireRuntime(e.runtimeId).type&&Wo.has(e.status));for(const t of e)try{await ri(S,k,x,t.id,L,"restore")}catch(e){S.controlledInstances.put(oi(S.requireInstance(t.id),e)),M({instanceId:t.id,action:"restore",error:e instanceof Error?e.message:String(e)},"node local instance restore failed")}}),T.get("/api/node-agent/health",async()=>{return{data:{ok:!0,role:"node-agent",nodeId:I,platform:Io(w),protocolVersion:ye,build:(e="node-agent",{component:e,packageName:"@task-handoff/node-agent",packageVersion:vo(),protocolVersion:ye,buildId:So("TASK_HANDOFF_BUILD_ID"),builtAt:So("TASK_HANDOFF_BUILT_AT"),gitCommit:So("TASK_HANDOFF_GIT_COMMIT"),imageRef:So("TASK_HANDOFF_IMAGE_REF"),imageDigest:So("TASK_HANDOFF_IMAGE_DIGEST")}),instanceProxy:{...C},serverTime:(new Date).toISOString()}};var e});const B=e=>{if(!To(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(!T.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 T.nodeAgentListenerManager};return T.get("/api/node-agent/settings/external-listener",async e=>({data:B(e).current()})),T.patch("/api/node-agent/settings/external-listener",async e=>({data:await B(e).update(e.body)})),T.get("/api/node-agent/updates/jobs",async()=>({data:S.updateJobs.list()})),T.post("/api/node-agent/updates/check",async e=>({data:await U(Ze.parse(e.body))})),T.post("/api/node-agent/updates/apply",async(e,r)=>{const o=wo.parse(e.body),s=await U(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=S.updateJobs.create(I,s);if("node-agent"===o.target.component){const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&"SCRIPT"===y.tagName.toUpperCase()&&y.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&"SCRIPT"===y.tagName.toUpperCase()&&y.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){S.updateJobs.patch(a.id,{status:"failed",error:`Update worker was not found: ${s}`,completedAt:Co()});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 _("systemd-run",["--unit",`task-handoff-update-${a.id}`,"--collect","--property=Type=exec",...o?[r]:[process.execPath,r],"--job-file",S.updateJobs.records.filePath(a.id),"--target-version",a.toVersion,"--npm-command",Tr()])}else S.updateJobs.run(a,e=>H(e));return r.code(202).send({data:a})}),T.post("/api/node-agent/pairing/invites",async(e,t)=>{const n=f.createPairingInvite(Yr.parse(e.body||{}));return t.code(201).send({data:{nodeId:I,joinToken:n.token,expiresAt:n.expiresAt}})}),T.post("/api/node-agent/pairing/complete",async(e,t)=>{const n=f.completePairingInvite(Xr.parse(e.body));return t.code(201).send({data:{nodeId:I,keyId:n.keyId,secret:n.secret,pairedAt:n.pairedAt}})}),T.get("/api/node-agent/remotes",async e=>({data:f.listRemoteControlPlanes(e.nodeAgentAuthKeyId)})),T.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:f.deleteRemoteControlPlane(t,e.nodeAgentAuthKeyId)}}}),T.post("/api/node-agent/remotes/connect",async(e,t)=>{const n=Qr.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||{}}(x,r,{joinToken:n.joinToken,nodeId:I,nodeName:S.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&&T.nodeAgentReverseTunnels)try{T.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}:{}}}})}),T.get("/api/node-agent/runtimes",async()=>({data:S.nodeRuntimes.list()})),T.post("/api/node-agent/runtimes",async(e,t)=>t.code(201).send({data:S.createRuntime(Jn.parse(e.body))})),T.patch("/api/node-agent/runtimes/:id",async e=>({data:S.updateRuntime(e.params.id,Wn.parse(e.body))})),T.delete("/api/node-agent/runtimes/:id",async e=>({data:{deleted:S.deleteRuntime(e.params.id)}})),T.post("/api/node-agent/runtimes/:id/check",async e=>{const t=S.requireRuntime(e.params.id);return{data:await S.checkRuntime(t.id,k.forRuntime(t))}}),T.get("/api/node-agent/local-folders",async()=>({data:S.localFolders.list()})),T.get("/api/node-agent/folders/tree",async e=>({data:Hn(Kn.parse(e.query))})),T.post("/api/node-agent/local-folders",async(e,t)=>t.code(201).send({data:S.createLocalFolder(Bn.parse(e.body))})),T.delete("/api/node-agent/local-folders/:id",async e=>({data:{deleted:S.localFolders.delete(e.params.id)}})),T.get("/api/node-agent/models",async()=>({data:S.listModels()})),T.post("/api/node-agent/models",async(e,t)=>t.code(201).send({data:S.createModel(bt.parse(e.body))})),T.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:S.deployModel(n)}}),T.patch("/api/node-agent/models/:id",async e=>({data:S.updateModel(e.params.id,zt.parse(e.body))})),T.delete("/api/node-agent/models/:id",async e=>({data:{deleted:S.deleteModel(e.params.id)}})),T.get("/api/node-agent/instances",async()=>({data:S.listInstances()})),T.post("/api/node-agent/instances",async(e,t)=>{const n=S.createInstance(Gn.parse(e.body));return R.syncNow(),n.imageProvisioning&&$(n),t.code(201).send({data:n})}),T.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=Co();return e.controlledInstances.put(qt.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}))}(S,e.params.id);return R.syncNow(),$(t),{data:t}}),T.patch("/api/node-agent/instances/:id",async e=>{const t=e.params.id,n=S.requireInstance(t),r=Vn.parse(e.body),o=qt.parse({...n,...r,...r.config?{config:{...n.config,...r.config}}:{},updatedAt:Co()}),i=S.controlledInstances.put(o);return R.syncNow(),{data:i}}),T.put("/api/node-agent/instances/:id/model-assignment",async e=>{const t=e.params.id,n=S.assignModels(t,wt.parse(e.body));return await $o(x,S,t),{data:n}}),T.post("/api/node-agent/instances/:id/register",async(e,t)=>{const n=e.params.id,r=S.registerInstance(n,Wt.parse(e.body),Oo(e.headers));return R.syncNow(),M({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})}),T.post("/api/node-agent/instances/:id/heartbeat",async e=>{const t=e.params.id,n=S.heartbeatInstance(t,Yt.parse(e.body),Oo(e.headers));return R.syncNow(),M({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}}),T.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=R.addOutput(t);t.on("close",n),t.on("error",n)}),T.get("/api/node-agent/docker/images",async()=>({data:await an(e.dockerCommandRunner)})),T.get("/api/node-agent/instances/:id/metrics",async e=>{const t=e.params.id,n=S.requireInstance(t);if("docker"!==S.requireRuntime(n.runtimeId).type){const e=new Error(`Runtime type ${S.requireRuntime(n.runtimeId).type} does not provide resource metrics.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_METRICS_UNSUPPORTED"}),e}return{data:await D.snapshot(t)}}),T.post("/api/node-agent/instances/:id/start",async e=>{const t=e.params.id;return Ao.parse(e.body),{data:await F(t,"request")}}),T.post("/api/node-agent/instances/:id/stop",async e=>{const t=S.requireInstance(e.params.id);M({instanceId:t.id,action:"stop",runtimeId:t.runtimeId,containerName:t.runtime.containerName},"node instance stop requested");const n=k.forRuntime(S.requireRuntime(t.runtimeId)),r=await n.stop(S.context(t)),o=qt.parse({...t,...r,agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",updatedAt:Co()}),i=S.controlledInstances.put(o);return R.syncNow(),M({instanceId:t.id,action:"stop",status:i.status,connectionStatus:i.connectionStatus,containerName:i.runtime.containerName},"node instance stop completed"),{data:i}}),T.post("/api/node-agent/instances/:id/restart",async e=>{const t=e.params.id,n=S.requireInstance(t);Ao.parse(e.body),M({instanceId:t,action:"restart",runtimeId:n.runtimeId,imageId:n.imageId,containerName:n.runtime.containerName},"node instance restart requested");const r=k.forRuntime(S.requireRuntime(n.runtimeId)),o=await r.restart(S.context(n)),i=qt.parse({...n,...o,target:o.target?{...n.target,...o.target}:n.target,updatedAt:Co()}),s=await Mo(x,i),a=qt.parse({...n,...o,target:o.target?{...n.target,...o.target,status:s}:n.target,targetStatus:s,uiAccessStatus:s,connectionStatus:o.connectionStatus,updatedAt:Co()}),c=S.controlledInstances.put(a);return await Fo(x,c,"restart",L),R.syncNow(),M({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}}),T.post("/api/node-agent/instances/:id/delete",async e=>{const t=e.params.id,n=S.requireInstance(t);M({instanceId:t,action:"delete",runtimeId:n.runtimeId,containerName:n.runtime.containerName},"node instance delete requested");const r=k.forRuntime(S.requireRuntime(n.runtimeId));await r.delete(S.context(n,{})),M({instanceId:t,action:"delete"},"node instance delete completed");const o=S.controlledInstances.delete(t);return S.modelAssignments.delete(t),S.modelEnvironments.delete(t),R.syncNow(),{data:{deleted:o}}}),T.post("/api/node-agent/instances/:id/proxy",async(e,t)=>{const n=e.params.id,r=qn.parse(e.body),o=Uo(S.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;"POST"===r.method&&"/api/apps/sessions"===i&&await $o(x,S,n),M({instanceId:n,action:"proxy",method:r.method,path:i,instanceBase:o},"node instance proxy requested");const s=await x(`${o}${i}`,{method:r.method,headers:{...r.headers},body:Eo(r)}),a=s.headers.get("content-type")||"application/octet-stream",c=await s.text();M({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)}),T.post("/api/node-agent/instances/:id/proxy/stream",{bodyLimit:ko},async(e,t)=>{const n=Date.now();C.requests+=1,C.active+=1;const r=new AbortController;let o=0,i=!1,s=!1;const c=e=>{s||(s=!0,C.active-=1,C.totalDurationMs+=Date.now()-n,C.responseBytes+=o,C[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=qn.parse(e.body),d=Uo(S.requireInstance(n)),l=s.path.startsWith("/")?s.path:`/${s.path}`;M({instanceId:n,action:"proxy.stream",method:s.method,path:l,instanceBase:d},"node instance streaming proxy requested");const u=await x(`${d}${l}`,{method:s.method,headers:{...s.headers},body:Eo(s),signal:r.signal}),m=Number(u.headers.get("content-length"));if(Number.isFinite(m)&&m>C.maxResponseBytes)return C.limitRejected+=1,r.abort(),t.code(502).send({error:{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE",message:`Instance response exceeds ${C.maxResponseBytes} bytes.`}});t.code(u.status);for(const[e,n]of Object.entries(No(u.headers)))t.header(e,n);if(!u.body||"HEAD"===s.method)return c("completed"),t.send();const p=new a.Transform({transform(e,t,n){if(o+=e.length,o>C.maxResponseBytes)return C.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,p.once("end",()=>c("completed")),p.once("error",()=>c("aborted")),t.send(a.Readable.fromWeb(u.body).pipe(p))}catch(e){throw c("aborted"),e}finally{e.raw.off("aborted",d),i||c("completed")}}),T.post("/api/node-agent/instances/:id/proxy/raw",{bodyLimit:ko},async(e,t)=>{const n=e.params.id,r=qn.parse(e.body),o=Uo(S.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;M({instanceId:n,action:"proxy.raw",method:r.method,path:i,instanceBase:o},"node instance raw proxy requested");const s=await x(`${o}${i}`,{method:r.method,headers:{...r.headers},body:Eo(r)}),a=Number(s.headers.get("content-length"));if(Number.isFinite(a)&&a>C.maxResponseBytes)return C.limitRejected+=1,t.code(502).send({error:{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE",message:`Instance response exceeds ${C.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,C.maxResponseBytes)}catch(e){if(!(e instanceof Error)||"INSTANCE_PROXY_RESPONSE_TOO_LARGE"!==e.code)throw e;return C.limitRejected+=1,t.code(502).send({error:{code:"INSTANCE_PROXY_RESPONSE_TOO_LARGE",message:`Instance response exceeds ${C.maxResponseBytes} bytes.`}})}return M({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:No(s.headers),bodyBase64:c.toString("base64")}}}),T.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=Uo(S.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);M({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",()=>{M({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",()=>{M({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy closed"),e.close()}),s.on("error",t=>{M({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){M({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.")}}),T}function ci(e){const t=new URL("/api/node-agent/tunnel",e);return t.protocol="https:"===t.protocol?"wss:":"ws:",t.toString()}function di(e,t){const n=new URL(t.tunnelUrl);n.searchParams.set("nodeId",t.nodeId);const r=t.secret?Nr({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?Nr({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)},m=(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 p=a&&"object"==typeof a?a:{};if("control-plane.http.open"===p.type){const e="string"==typeof p.streamId?p.streamId:"",r="string"==typeof p.route&&p.route.startsWith("/")?p.route:"/health",o=p.init&&"object"==typeof p.init?p.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:kr(o.method),headers:{...i,...t.token?{authorization:`Bearer ${t.token}`}:{}},body:"string"==typeof o.body?o.body:void 0,signal:a.signal});if(await m(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 m(u,Buffer.from(n),!0)}}await m(u,JSON.stringify({type:"node-agent.http.end",streamId:e})),u.close(1e3,"HTTP stream completed.")}catch(t){u.readyState===l.OPEN&&(await m(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"===p.type){const e="string"==typeof p.streamId?p.streamId:"",r="string"==typeof p.route?p.route:"",s=Array.isArray(p.protocols)?p.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}),m=(e=>{const t=new URL(n);return t.pathname=`${t.pathname.replace(/\/$/,"")}/streams/${encodeURIComponent(e)}`,t})(e),p=new l(m,{headers:d(m)});i.set(e,{upstream:u,tunnel:p,close:(e=1e3,t="")=>{u.close(e,t),p.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=Qt(t);const s=[],a=[];let c;const d=()=>{c&&(clearTimeout(c),c=void 0)},l=(n,o)=>{if(n.length>=r)return Zt(e,1011,"WebSocket bridge pending frame limit exceeded."),void Zt(t,1011,"WebSocket bridge pending frame limit exceeded.");n.push(o)};n.upstreamOpenTimeoutMs&&!i&&(c=setTimeout(()=>{i||o||(Zt(e,1011,"Upstream websocket did not open."),Zt(t))},n.upstreamOpenTimeoutMs)),e.on("open",()=>tn(e,a)),t.on("open",()=>{d(),i=!0,n.onUpstreamOpen?.(),tn(t,s)}),e.on("message",(e,n=!1)=>{const r={data:e,isBinary:Boolean(n)};Qt(t)?en(t,r):l(s,r)}),t.on("message",(t,n=!1)=>{const r={data:t,isBinary:Boolean(n)};Qt(e)?en(e,r):l(a,r)}),t.on("close",(t,r)=>{d(),n.onUpstreamClose?.(t,r),(i||o||!n.onUpstreamCloseBeforeOpen?.())&&Zt(e,t,r)}),t.on("error",t=>{n.onUpstreamError?.(t),(i||o||!n.onUpstreamErrorBeforeOpen?.(t))&&Zt(e)}),e.on("close",(e,r)=>{d(),o=!0,n.onClientClose?.(e,r),Zt(t,e,r)}),e.on("error",e=>{d(),o=!0,n.onClientError?.(e),Zt(t,1011,"WebSocket bridge client failed.")})}(p,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"===p.type){const e="string"==typeof p.streamId?p.streamId:"";return void u(e,"number"==typeof p.code?p.code:1e3,"string"==typeof p.reason?p.reason:"")}if("control-plane.request"===p.type){const n="string"==typeof p.requestId?p.requestId:"",r=p.init&&"object"==typeof p.init?p.init:{},i="string"==typeof p.route&&p.route.startsWith("/")?p.route:"/health",s=r.headers&&"object"==typeof r.headers?r.headers:{};try{const a=await e.inject({method:kr(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 li(e){const o=Xn(e.dataDir),i=(s=e.host,a=e.port,jt.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 sr(e.settingsPath,()=>({version:1,externalListener:t}),{schema:zo,sanitize:Do})}(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:g.hostname(),component:r.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:m.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(dr(e),`${JSON.stringify({token:o.token,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),pr(e,o),{lockPath:e,owner:o,release(){const n=ur(e);n?.token===o.token&&t.rmSync(e,{recursive:!0,force:!0})}}}catch(o){if("EEXIST"!==o.code)throw o;const i=ur(e);if(mr(i?.pid))throw new ar(e,i,r.error);if(!i&&yr(e)<2e3){if(n+1<5){fr();continue}throw new ar(e,void 0,r.error)}const s=m.randomUUID();if(!Ir(e,s)){if(n+1<5){fr();continue}throw new ar(e,ur(e),r.error)}const a=ur(e);if(mr(a?.pid))throw hr(lr(e)),new ar(e,a,r.error);if(i?.token&&a?.token!==i.token){hr(lr(e));continue}if(!zr(e,s))continue;t.rmSync(e,{recursive:!0,force:!0})}const i=ur(e);throw new ar(e,i,r.error)}(e,{...r,component:"node-agent",error:{label:"Node agent",code:"NODE_AGENT_ALREADY_RUNNING"}})}(n.join(g.tmpdir(),`task-handoff-node-agent-${process.getuid?.()??"user"}.lock`),{dataDir:o.dataDir,host:Ro(l.bindScope),port:l.port});try{const i={...e,port:l.port},s=await ai(i),a=function(e,t,n,r){const o=t.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,i=new uo(n),s=new Map,a=t=>{const n=t.url.replace(/\/$/,"");s.get(n)?.close(1e3,"Reverse tunnel reconnecting.");const i=di(e,{tunnelUrl:ci(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=di(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 uo(o).resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID));s.decorate("nodeAgentReverseTunnels",a);const m=new ni({app:s,state:s.nodeAgentState,settings:d,config:l,source:c?"persisted":"bootstrap"});let p;s.decorate("nodeAgentListenerManager",m),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(),u.release()});try{await s.ready();const i=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||Ar(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 m.start(),a.connectConfigured(),await(s.nodeAgentRestoreLocalInstances?.())}catch(e){throw await s.close(),e}}catch(e){throw u.release(),e}}function ui(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 mi(e){if("local-ipc"!==e&&"local-loopback"!==e)throw new Error("Connection mode must be local-ipc or local-loopback.");return e}function pi(e){const t=Number(e);if(!Number.isInteger(t)||t<=0)throw new Error("Expiry must be a positive integer.");return t}(async function(){const o=new e.Command;o.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",ui,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",mi).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 li({host:e.host,port:e.port,dataDir:e.dataDir,token:e.token,connectionMode:e.connectionMode,ipcPath:e.ipcPath,controlPlaneTunnelUrl:e.controlPlaneTunnelUrl})}),o.command("invite").description("Create a one-time token for pairing this node agent with a control plane.").option("--endpoint <url>","Use the node-agent HTTP endpoint instead of its local IPC socket").option("--ipc-path <path>","Local node-agent IPC socket path").option("--data-dir <path>","Node-agent data directory used to locate its IPC socket").option("--token <token>","Bearer token for local node-agent access",process.env.TASK_HANDOFF_NODE_AGENT_TOKEN).option("--expires-in-ms <ms>","Invite expiry in milliseconds",pi).option("--json","Print raw JSON output").action(async e=>{const t=o.opts(),n=e.ipcPath||t.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH,i=e.dataDir||t.dataDir||process.env.TASK_HANDOFF_NODE_AGENT_DATA_DIR,s=e.endpoint?void 0:n||Ar(i||Yn()),c=await async function(e){const t={"content-type":"application/json",...e.token?{authorization:`Bearer ${e.token}`}:{}},n=JSON.stringify({...e.expiresInMs?{expiresInMs:e.expiresInMs}:{}}),o=e.endpoint?await fetch(`${e.endpoint.replace(/\/$/,"")}/api/node-agent/pairing/invites`,{method:"POST",headers:t,body:n}):await async function(e,t,n={}){const o=n.method||"GET",i="string"==typeof n.body||n.body instanceof Buffer?n.body:void 0===n.body||null===n.body?void 0:String(n.body),s=new Headers(n.headers||{});return void 0===i||s.has("content-length")||s.set("content-length",String(Buffer.byteLength(i))),new Promise((c,d)=>{const l=r.request({socketPath:e,method:o,path:`/api/node-agent${t}`,headers:Object.fromEntries(s.entries())},e=>{const t=e.statusCode||500;c(new Response("HEAD"!==o&&204!==t&&205!==t&&304!==t?a.Readable.toWeb(e):null,{status:t,headers:e.headers}))});l.on("error",d);const u=()=>l.destroy(new DOMException("The operation was aborted.","AbortError"));n.signal?.aborted?u():n.signal?.addEventListener("abort",u,{once:!0}),l.once("close",()=>n.signal?.removeEventListener("abort",u)),void 0!==i&&l.write(i),l.end()})}(e.ipcPath,"/pairing/invites",{method:"POST",headers:t,body:n}),i=await o.json().catch(()=>({}));if(!o.ok)throw new Error(i.error?.message||`Node agent pairing invite failed with HTTP ${o.status}`);const s="string"==typeof i.data?.joinToken?i.data.joinToken:"";if(!s)throw new Error("Node agent pairing invite response did not include a join token.");return{nodeId:"string"==typeof i.data?.nodeId?i.data.nodeId:"",joinToken:s,expiresAt:"string"==typeof i.data?.expiresAt?i.data.expiresAt:""}}({endpoint:e.endpoint,ipcPath:s,token:e.token,expiresInMs:e.expiresInMs});e.json?console.log(JSON.stringify(c,null,2)):(c.nodeId&&console.log(`Node: ${c.nodeId}`),console.log(`Join token: ${c.joinToken}`),c.expiresAt&&console.log(`Expires: ${c.expiresAt}`))}),await o.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.8",
|
|
4
4
|
"description": "Prebuilt TaskHandoff node agent runtime.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"commander": "14.0.3",
|
|
25
25
|
"dingtalk-stream": "2.1.5",
|
|
26
26
|
"fastify": "5.8.5",
|
|
27
|
+
"node-pty": "1.1.0",
|
|
27
28
|
"semver": "7.8.5",
|
|
28
29
|
"telegramify-markdown": "1.3.3",
|
|
29
30
|
"write-file-atomic": "8.0.0",
|