@task-handoff/control-plane 0.0.14 → 0.0.15
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 +2 -2
- package/package.json +1 -1
- package/ui/assets/{CodeBlockNode-AXJINd6M.js → CodeBlockNode-B6_PnEjE.js} +1 -1
- package/ui/assets/{Tooltip-BPj5fHzy.js → Tooltip-BbVFp_wQ.js} +1 -1
- package/ui/assets/{abnfDiagram-VRR7QNED-CMIKREwm.js → abnfDiagram-VRR7QNED-Z4lGAYgn.js} +1 -1
- package/ui/assets/{arc-C6BhVUxp.js → arc-f4WWu1E6.js} +1 -1
- package/ui/assets/{architectureDiagram-ZJ3FMSHR-Kkr949gd.js → architectureDiagram-ZJ3FMSHR-8nZ0f8Ei.js} +1 -1
- package/ui/assets/{blockDiagram-677ZJIJ3-CRhkabof.js → blockDiagram-677ZJIJ3-CYnBXjVF.js} +1 -1
- package/ui/assets/{c4Diagram-LMCZKHZV-DVRBIMAT.js → c4Diagram-LMCZKHZV-lwRL3_jd.js} +1 -1
- package/ui/assets/channel-BzUQbRSK.js +1 -0
- package/ui/assets/{chunk-2Q5K7J3B-B08XdHbg.js → chunk-2Q5K7J3B-DumxFqU8.js} +1 -1
- package/ui/assets/{chunk-32BRIVSS-BCeFt7xP.js → chunk-32BRIVSS-CaMAGfha.js} +1 -1
- package/ui/assets/{chunk-5VM5RSS4-BOvadOol.js → chunk-5VM5RSS4-Cgg_dfHa.js} +1 -1
- package/ui/assets/{chunk-EX3LRPZG-D1H2UH4A.js → chunk-EX3LRPZG-Gr_XRFAd.js} +1 -1
- package/ui/assets/{chunk-JWPE2WC7-DSGn7LhO.js → chunk-JWPE2WC7-B8zHhDnZ.js} +1 -1
- package/ui/assets/{chunk-MOJQB5TN-B8xXGYFO.js → chunk-MOJQB5TN-DP5_w4Wm.js} +1 -1
- package/ui/assets/{chunk-RYQCIY6F-O5LaeBkN.js → chunk-RYQCIY6F-D490Qa5J.js} +1 -1
- package/ui/assets/{chunk-V7JOEXUC-B-7v10gi.js → chunk-V7JOEXUC-BCmqqHOW.js} +1 -1
- package/ui/assets/{chunk-VR4S4FIN-OpgvoZKF.js → chunk-VR4S4FIN-B1fgeklP.js} +1 -1
- package/ui/assets/{chunk-XXDRQBXY-D4DKZFXe.js → chunk-XXDRQBXY-CtIumb6l.js} +1 -1
- package/ui/assets/classDiagram-OUVF2IWQ-Bt1VqvC6.js +1 -0
- package/ui/assets/classDiagram-v2-EOCWNBFH-Bt1VqvC6.js +1 -0
- package/ui/assets/{cose-bilkent-JH36ORCC-BDJ23Pc4.js → cose-bilkent-JH36ORCC-LmtvRTUX.js} +1 -1
- package/ui/assets/{cynefin-VYW2F7L2-Cxw2Q-RX.js → cynefin-VYW2F7L2-sPFQBJ6x.js} +1 -1
- package/ui/assets/{cynefinDiagram-TSTJHNR4-C9XzqmMM.js → cynefinDiagram-TSTJHNR4-CD8iyUzC.js} +1 -1
- package/ui/assets/{dagre-VKFMJZFB-ClWRN37b.js → dagre-VKFMJZFB-D2UjSEoP.js} +1 -1
- package/ui/assets/{diagram-FQU43EPY-C3oOZCCi.js → diagram-FQU43EPY-BW0FTksO.js} +1 -1
- package/ui/assets/{diagram-G47NLZAW-ByoAiZ3G.js → diagram-G47NLZAW-DfG7xQTS.js} +1 -1
- package/ui/assets/{diagram-NH7WQ7WH-ynyqeRFo.js → diagram-NH7WQ7WH-USdI6iG4.js} +1 -1
- package/ui/assets/{diagram-OA4YK3LP-B4dkuFe2.js → diagram-OA4YK3LP-BTLcyoTZ.js} +1 -1
- package/ui/assets/{diagram-WEI45ONY-iJuTjd25.js → diagram-WEI45ONY-Op-ceG5r.js} +1 -1
- package/ui/assets/{ebnfDiagram-CCIWWBDH-Dv9ujWnp.js → ebnfDiagram-CCIWWBDH-BCntasFd.js} +1 -1
- package/ui/assets/{erDiagram-Q63AITRT-Daw3_y0D.js → erDiagram-Q63AITRT-DWU-tKsS.js} +1 -1
- package/ui/assets/{flowDiagram-23GEKE2U-jkMKvRZ9.js → flowDiagram-23GEKE2U-uKzhOCXb.js} +1 -1
- package/ui/assets/{ganttDiagram-NO4QXBWP-B6ScIq3p.js → ganttDiagram-NO4QXBWP-Djk58-GD.js} +1 -1
- package/ui/assets/{gitGraphDiagram-IHSO6WYX-C2UPzB6M.js → gitGraphDiagram-IHSO6WYX-DOpvj6Ep.js} +1 -1
- package/ui/assets/{index-D8RnbKqw.js → index-C3qJHGCo.js} +225 -225
- package/ui/assets/{index-B9d0dls-.css → index-GMUw2JFi.css} +1 -1
- package/ui/assets/{index10-CkZGoGdL.js → index10-BPK1xRDl.js} +1 -1
- package/ui/assets/{index11-CVavxXP3.js → index11-DgYJqRPj.js} +1 -1
- package/ui/assets/{index5-sC_Mwo9x.js → index5-Dm638BkE.js} +1 -1
- package/ui/assets/{index6-BmAIOaZ3.js → index6-X27eZQET.js} +1 -1
- package/ui/assets/{index7-B7xAzabp.js → index7-DCMI2ZG1.js} +1 -1
- package/ui/assets/{index8-DliP9MhV.js → index8-LMSI92TH.js} +1 -1
- package/ui/assets/{infoDiagram-FWYZ7A6U-CjX3WY1i.js → infoDiagram-FWYZ7A6U-Cm3DJ5Ar.js} +1 -1
- package/ui/assets/{ishikawaDiagram-FXEZZL3T-CSSrWKjd.js → ishikawaDiagram-FXEZZL3T-BV1-yVBC.js} +1 -1
- package/ui/assets/{journeyDiagram-5HDEW3XC-Dulo5c1c.js → journeyDiagram-5HDEW3XC-CCJRBXb7.js} +1 -1
- package/ui/assets/{kanban-definition-HUTT4EX6-DbZhccUT.js → kanban-definition-HUTT4EX6-C2sFb4hf.js} +1 -1
- package/ui/assets/{linear-BnsKdBZT.js → linear-BDHLCx1-.js} +1 -1
- package/ui/assets/{mermaid.core-yz77l9Ym.js → mermaid.core-1eOLmtZH.js} +4 -4
- package/ui/assets/{mhchem-Ba1yB9c2.js → mhchem-DqtbVxMp.js} +1 -1
- package/ui/assets/{mindmap-definition-LN4V7U3C-fMtaWNha.js → mindmap-definition-LN4V7U3C-BGmanATT.js} +1 -1
- package/ui/assets/{pegDiagram-2B236MQR-B3NnOxTe.js → pegDiagram-2B236MQR-CrA4bblS.js} +1 -1
- package/ui/assets/{pieDiagram-ENE6RG2P-D7pybxr7.js → pieDiagram-ENE6RG2P-wERNTCuV.js} +1 -1
- package/ui/assets/{quadrantDiagram-ABIIQ3AL-BdMQginq.js → quadrantDiagram-ABIIQ3AL-C0YiROcc.js} +1 -1
- package/ui/assets/{railroadDiagram-RFXS5EU6-C0iYj6ez.js → railroadDiagram-RFXS5EU6-B9R4Rk9P.js} +1 -1
- package/ui/assets/{requirementDiagram-TGXJPOKE-DjBid_xq.js → requirementDiagram-TGXJPOKE-D242R4dw.js} +1 -1
- package/ui/assets/{sankeyDiagram-HTMAVEWB-D1JJ8OdL.js → sankeyDiagram-HTMAVEWB-DIcW6Ci9.js} +1 -1
- package/ui/assets/{sequenceDiagram-DBY2YBRQ-D7X3o-YC.js → sequenceDiagram-DBY2YBRQ-Dh1H53o5.js} +1 -1
- package/ui/assets/{sizeCapture-X5ZJPWSS-C8yo8xzu.js → sizeCapture-X5ZJPWSS-D_uxxjQ9.js} +1 -1
- package/ui/assets/{stateDiagram-2N3HPSRC-D_3m5ZDl.js → stateDiagram-2N3HPSRC-Dqceljay.js} +1 -1
- package/ui/assets/stateDiagram-v2-6OUMAXLB-DgL1u-wO.js +1 -0
- package/ui/assets/{swimlanes-5IMT3BWC-CFDuyHcW.js → swimlanes-5IMT3BWC-CuhXB6te.js} +2 -2
- package/ui/assets/swimlanesDiagram-G3AALYLV-D17nkAqk.js +8 -0
- package/ui/assets/{timeline-definition-FHXFAJF6-pSv2Pq7E.js → timeline-definition-FHXFAJF6-DIAN84g0.js} +1 -1
- package/ui/assets/{vennDiagram-L72KCM5P-BavtSHJ2.js → vennDiagram-L72KCM5P-4z9ota8_.js} +1 -1
- package/ui/assets/{wardleyDiagram-EHGQE667-C6QVtn7e.js → wardleyDiagram-EHGQE667-BNHizd7T.js} +1 -1
- package/ui/assets/{xychartDiagram-FW5EYKEG-DQFe-plf.js → xychartDiagram-FW5EYKEG-B3im9MzL.js} +1 -1
- package/ui/index.html +2 -2
- package/ui/assets/channel-DCgJLvTO.js +0 -1
- package/ui/assets/classDiagram-OUVF2IWQ-XMW8swSf.js +0 -1
- package/ui/assets/classDiagram-v2-EOCWNBFH-XMW8swSf.js +0 -1
- package/ui/assets/stateDiagram-v2-6OUMAXLB-BhgjogET.js +0 -1
- package/ui/assets/swimlanesDiagram-G3AALYLV-CZF7wJcn.js +0 -8
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var e=require("commander"),t=require("node:path"),n=require("node:fs"),s=require("fastify"),i=require("@fastify/cookie"),r=require("@fastify/websocket"),a=require("zod"),o=require("node:crypto"),d=require("node:util"),c=require("write-file-atomic"),l=require("ws"),p=require("node:http"),u=require("node:net"),g=require("node:stream"),m=require("telegramify-markdown"),h=require("node:fs/promises"),y=require("node:os"),f=require("dingtalk-stream"),I=require("node:child_process");function b(e,t){try{const s=JSON.parse(n.readFileSync(e,"utf8"));if(t&&s.name!==t)return;return"string"==typeof s.version&&s.version.trim()?s.version.trim():void 0}catch{return}}function v(e){if(e)try{return t.dirname(n.realpathSync(e))}catch{return}}function w(e,n=process.env,s=e){const i=n.TASK_HANDOFF_VERSION?.trim();if(i)return i;const r=s.split("/").at(-1),a=process.argv[1]?t.resolve(process.argv[1]):void 0,o=a?t.dirname(a):void 0,d=[v(a),o,process.cwd()].filter(e=>Boolean(e));for(const n of new Set(d)){let s=n;for(;;){const n=b(t.join(s,"package.json"),e);if(n)return n;const i=t.dirname(s);if(i===s)break;s=i}}let c;for(const e of new Set(d)){let n=e;for(;;){if(r){const e=b(t.join(n,"packages",r,"package.json"),s);if(e)return e}c||=b(t.join(n,"package.json"));const e=t.dirname(n);if(e===n)break;n=e}}return c||"unknown"}const S=20971520,A=512e3,k=41943040,C="ai-session.snapshot",x="ai-session.patch",T="ai-session.removed",z="ai-session.message-delta",N="ai-session.unread.updated",E=a.z.string().trim().min(1).max(80),_=a.z.enum(["running","waiting","idle","failed"]),P=a.z.object({instanceId:a.z.string().trim().min(1).max(160),sessionId:a.z.string().trim().min(1).max(120),unread:a.z.boolean(),sessionUpdatedAt:a.z.string().datetime(),updatedAt:a.z.string().datetime()}).strict(),O=a.z.enum(["thinking","tool","editing","approval","responding","unknown"]),R=a.z.object({id:a.z.string().trim().min(1).max(240).optional(),kind:a.z.string().trim().min(1).max(80).optional(),name:a.z.string().trim().min(1).max(120),inputPreview:a.z.string().trim().max(500).optional(),startedAt:a.z.string().datetime().optional()}).strict(),j=a.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),D=a.z.enum(["started","interacted","interrupted"]),M=a.z.object({threadId:a.z.string().trim().min(1).max(240),path:a.z.string().trim().max(4096).optional(),status:j,activity:D.optional(),message:a.z.string().trim().max(1e3).optional(),updatedAt:a.z.string().datetime()}).strict(),$=a.z.object({toolCalls:a.z.number().int().min(0).default(0),edits:a.z.number().int().min(0).default(0),approvals:a.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),q=a.z.object({send:a.z.boolean().optional(),interrupt:a.z.boolean().optional(),approval:a.z.boolean().optional()}).strict(),L=a.z.enum(["image","file"]),B=a.z.object({id:a.z.string().trim().min(1).max(120),kind:L,name:a.z.string().trim().min(1).max(240),mime:a.z.string().trim().min(1).max(120),size:a.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)}),U=a.z.union([B.extend({source:a.z.object({type:a.z.literal("inline"),encoding:a.z.literal("base64"),data:a.z.string().min(1).max(31457280)}).strict()}).strict().superRefine((e,t)=>{const n="image"===e.kind?S:A;(e.size<=0||("file"===e.kind?e.size>=n:e.size>n))&&t.addIssue({code:a.z.ZodIssueCode.custom,path:["size"],message:`Inline ${e.kind} must be between 1 byte and ${n} bytes.`})}),B.extend({source:a.z.object({type:a.z.literal("runtime-path"),path:a.z.string().trim().min(1).max(4096)}).strict()}).strict()]),F=B.extend({sourceType:a.z.enum(["inline","runtime-path"])}).strict(),H=a.z.union([a.z.object({id:a.z.string().trim().min(1).max(120),kind:L.optional(),source:a.z.object({type:a.z.literal("upload-ref")}).strict().default({type:"upload-ref"})}).strict(),a.z.object({id:a.z.string().trim().min(1).max(120),kind:L,name:a.z.string().trim().min(1).max(240),mime:a.z.string().trim().min(1).max(120),size:a.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),source:a.z.object({type:a.z.literal("runtime-path"),path:a.z.string().trim().min(1).max(4096)}).strict()}).strict()]),W=a.z.enum(["auto","queue","steer","immediate"]),G=a.z.enum(["ask","auto-review","full-access"]),V=a.z.enum(["review","rename","goal","compact"]),K=a.z.object({command:V,argument:a.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.`})}),J=a.z.object({command:V,value:a.z.string().max(4e3).optional(),turnId:a.z.string().trim().min(1).max(240).optional()}).strict();a.z.enum(["skill","app","plugin"]);const Q=a.z.object({name:a.z.string().trim().min(1).max(240),path:a.z.string().trim().min(1).max(4096)}),Y=a.z.discriminatedUnion("kind",[Q.extend({kind:a.z.literal("skill"),path:a.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),Q.extend({kind:a.z.literal("app"),path:a.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),Q.extend({kind:a.z.literal("plugin"),path:a.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),X=a.z.array(Y).max(20).default([]),Z=a.z.enum(["plugin","skill","file","directory","app"]),ee=a.z.object({kind:Z,name:a.z.string().trim().min(1).max(240),description:a.z.string().trim().max(1e3).optional(),path:a.z.string().trim().min(1).max(4096),icon:a.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."})}),te=a.z.object({category:a.z.enum(["skills","plugins","apps","files"]),code:a.z.string().trim().min(1).max(160),message:a.z.string().trim().min(1).max(2e3)}).strict(),ne=a.z.object({sessionId:a.z.string().trim().min(1).max(120),providerSessionId:a.z.string().trim().min(1).max(240),cwd:a.z.string().trim().min(1).max(4096),candidates:a.z.array(ee).max(1e3).default([]),diagnostics:a.z.array(te).max(20).default([])}).strict(),se=a.z.object({query:a.z.string().trim().max(240).default("")}).strict(),ie=a.z.object({sessionId:a.z.string().trim().min(1).max(120),cwd:a.z.string().trim().min(1).max(4096),query:a.z.string().trim().max(240),requestId:a.z.string().trim().min(1).max(160),candidates:a.z.array(ee.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:a.z.boolean().default(!1)}).strict(),re=a.z.object({message:a.z.string().trim().min(1).max(2e4),mode:W.optional(),permissionMode:G.optional()});re.extend({attachments:a.z.array(U).max(6).default([]),references:X}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>k&&t.addIssue({code:a.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})});const ae=re.extend({attachments:a.z.array(H).max(6).default([]),references:X}).strict(),oe=a.z.object({decision:a.z.enum(["allow","deny","skip"])}).strict();a.z.object({queueIds:a.z.array(a.z.string().trim().min(1).max(120)).max(100)}).strict(),a.z.object({code:a.z.string().trim().min(1).max(160),message:a.z.string(),instanceId:a.z.string().trim().min(1).max(160).optional(),nodeId:a.z.string().trim().min(1).max(160).optional(),route:a.z.string().trim().min(1).max(4096).optional()}).strict();const de=a.z.object({id:a.z.string().trim().min(1).max(120),message:a.z.string().trim().min(1).max(2e4),attachments:a.z.array(F).max(6).default([]),references:X,permissionMode:G.optional(),status:a.z.enum(["queued","sending","failed"]).default("queued"),createdAt:a.z.string().datetime(),updatedAt:a.z.string().datetime(),error:a.z.string().trim().max(4e3).optional()}).strict(),ce=a.z.object({pendingCount:a.z.number().int().min(0).default(0),items:a.z.array(de).max(100).default([])}).strict().default({pendingCount:0,items:[]}),le=a.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),pe=a.z.object({id:a.z.string().trim().min(1).max(240),status:a.z.enum(["running","completed"]),startedAt:a.z.string().datetime().optional(),completedAt:a.z.string().datetime().optional()}).strict(),ue=a.z.object({id:a.z.string().trim().min(1).max(240),providerTurnId:a.z.string().trim().max(240).optional(),source:le.optional(),userPrompt:a.z.string().trim().optional(),status:a.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:O.optional(),summary:a.z.string().trim().max(1e3).optional(),lastMessage:a.z.string().trim().optional(),lastMessageItemId:a.z.string().trim().max(240).optional(),contextCompactions:a.z.array(pe).max(20).optional(),revision:a.z.number().int().min(0).default(0),sourcePriority:a.z.number().int().min(0).max(100).optional(),snapshotVersion:a.z.number().int().min(0).optional(),observedAt:a.z.string().datetime().optional(),startedAt:a.z.string().datetime().optional(),updatedAt:a.z.string().datetime().optional(),completedAt:a.z.string().datetime().optional()}).strict(),ge=ue.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),me=a.z.object({id:a.z.string().trim().min(1).max(120),agent:E,appSessionId:a.z.string().trim().max(120).optional(),appId:a.z.string().trim().max(120).optional(),providerSessionId:a.z.string().trim().max(240).optional(),providerMeta:a.z.record(a.z.string(),a.z.unknown()).optional(),appBindingKeys:a.z.array(a.z.string().trim().min(1).max(240)).max(20).optional(),actions:q.optional(),activeTurnId:a.z.string().trim().max(240).optional(),title:a.z.string().trim().max(240).optional(),cwd:a.z.string().trim().max(4096).optional(),userPrompt:a.z.string().trim().optional(),turns:a.z.array(ue).max(50).optional(),status:_.default("running"),phase:O.default("unknown"),summary:a.z.string().trim().max(1e3).optional(),lastMessage:a.z.string().trim().optional(),lastMessageItemId:a.z.string().trim().max(240).optional(),currentTool:R.optional(),toolCallsSinceLastMessage:a.z.number().int().min(0).default(0),subAgents:a.z.array(M).max(50).default([]),transcriptPath:a.z.string().trim().max(4096).optional(),transcriptSize:a.z.number().int().min(0).optional(),startedAt:a.z.string().datetime(),updatedAt:a.z.string().datetime(),completedAt:a.z.string().datetime().optional(),error:a.z.string().trim().max(4e3).optional(),counters:$,queue:ce}).strict(),he=a.z.object({session:me,provider:a.z.string().trim().min(1).max(80),action:a.z.enum(["send","queue","steer","interrupt","approval"]),decision:a.z.enum(["allow","deny","skip"]).optional(),turnId:a.z.string().trim().min(1).max(240).optional(),providerTurnId:a.z.string().trim().min(1).max(240).optional(),queueId:a.z.string().trim().min(1).max(120).optional()}).strict(),ye=a.z.object({id:a.z.string().trim().min(1).max(120),agent:a.z.enum(["codex","claude"]),providerSessionId:a.z.string().trim().min(1).max(240),title:a.z.string().trim().max(240).optional(),userPrompt:a.z.string().trim().optional(),lastMessage:a.z.string().trim().optional(),cwd:a.z.string().trim().min(1).max(4096),lastActiveAt:a.z.string().datetime(),archivedAt:a.z.string().datetime()}).strict();a.z.object({schemaVersion:a.z.literal(1).default(1),items:a.z.array(ye).max(50).default([])}).strict();const fe=a.z.object({items:a.z.array(ye).max(50).default([])}).strict(),Ie=a.z.object({item:ye,turns:a.z.array(ge).max(50).default([])}).strict(),be=a.z.object({disposition:a.z.enum(["resumed","already-open"]),aiSessionId:a.z.string().trim().min(1).max(120),providerSessionId:a.z.string().trim().min(1).max(240),appSessionId:a.z.string().trim().min(1).max(120)}).strict(),ve=me.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}),we=a.z.object({runningCount:a.z.number().int().min(0).default(0),waitingCount:a.z.number().int().min(0).default(0),staleCount:a.z.number().int().min(0).default(0),sessions:a.z.array(ve).default([]),updatedAt:a.z.string().datetime()}).strict(),Se=a.z.object({streamId:a.z.string().trim().min(1).max(240),revision:a.z.number().int().min(0),lastEventAt:a.z.string().datetime(),snapshot:we}).strict(),Ae=we.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:a.z.number().int().min(0).default(0),problemCount:a.z.number().int().min(0).default(0),revision:a.z.number().int().min(0).optional()});function ke(e,t={}){const n=e.sessions.filter(e=>"idle"===e.status).length,s=e.sessions.filter(e=>"failed"===e.status).length;return Ae.parse({runningCount:e.runningCount,waitingCount:e.waitingCount,staleCount:e.staleCount,idleCount:n,problemCount:s,updatedAt:e.updatedAt,revision:t.revision})}const Ce=a.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),xe=a.z.object({streamId:a.z.string().trim().min(1).max(240),instanceId:a.z.string().trim().min(1).max(160),nodeId:a.z.string().trim().min(1).max(160).optional(),revision:a.z.number().int().min(0),previousRevision:a.z.number().int().min(0).optional(),traceId:a.z.string().trim().min(1).max(160),generatedAt:a.z.string().datetime(),reason:Ce}).strict(),Te=a.z.object({meta:xe,snapshot:we}).strict(),ze=a.z.object({meta:xe,upserted:a.z.array(ve).default([]),removed:a.z.array(a.z.string().trim().min(1).max(120)).default([])}).strict(),Ne=a.z.object({meta:xe,sessionIds:a.z.array(a.z.string().trim().min(1).max(120)).default([]),expiresAt:a.z.string().datetime()}).strict(),Ee=a.z.object({instanceId:a.z.string().trim().min(1).max(160),nodeId:a.z.string().trim().min(1).max(160).optional(),sessionId:a.z.string().trim().min(1).max(120),providerSessionId:a.z.string().trim().min(1).max(240),turnId:a.z.string().trim().min(1).max(240),itemId:a.z.string().trim().min(1).max(240),delta:a.z.string().min(1),generatedAt:a.z.string().datetime()}).strict(),_e=a.z.object({streamId:a.z.string().trim().min(1).max(240),instanceId:a.z.string().trim().min(1).max(160),sinceRevision:a.z.number().int().min(0),latestRevision:a.z.number().int().min(0),earliestRetainedRevision:a.z.number().int().min(0),syncRequired:a.z.boolean().default(!1),events:a.z.array(a.z.discriminatedUnion("type",[a.z.object({type:a.z.literal(C),payload:Te}).strict(),a.z.object({type:a.z.literal(x),payload:ze}).strict(),a.z.object({type:a.z.literal(T),payload:Ne}).strict()])).default([])}).strict(),Pe=a.z.object({source:le,sourcePriority:a.z.number().int().min(0).max(100).optional(),observedAt:a.z.string().datetime().optional(),snapshotVersion:a.z.number().int().min(0).optional()}).strict(),Oe=Pe.extend({type:a.z.literal("snapshot"),agent:a.z.string().trim().min(1).max(80),appSessionId:a.z.string().trim().max(120).optional(),appId:a.z.string().trim().max(120).optional(),providerSessionId:a.z.string().trim().max(240).optional(),providerMeta:a.z.record(a.z.string(),a.z.unknown()).optional(),appBindingKeys:a.z.array(a.z.string().trim().min(1).max(240)).max(20).optional(),actions:q.optional(),title:a.z.string().trim().max(240).optional(),cwd:a.z.string().trim().max(4096).optional(),activeTurnId:a.z.string().trim().max(240).optional(),userPrompt:a.z.string().trim().optional(),turns:a.z.array(ue).max(50).optional(),status:_.optional(),phase:O.optional(),summary:a.z.string().trim().max(1e3).optional(),lastMessage:a.z.string().trim().optional(),lastMessageItemId:a.z.string().trim().max(240).optional(),currentTool:R.optional(),toolCallsSinceLastMessage:a.z.number().int().min(0).optional(),subAgents:a.z.array(M).max(50).optional(),transcriptPath:a.z.string().trim().max(4096).optional(),transcriptSize:a.z.number().int().min(0).optional(),replaceActivity:a.z.boolean().optional()}).strict(),Re=Pe.extend({type:a.z.literal("event"),sessionId:a.z.string().trim().min(1).max(120),kind:a.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed","tool-activity","sub-agent-activity","context-compaction"]),activeTurnId:a.z.string().trim().max(240).optional(),providerTurnId:a.z.string().trim().max(240).optional(),userPrompt:a.z.string().trim().optional(),text:a.z.string().optional(),itemId:a.z.string().trim().max(240).optional(),status:_.optional(),phase:O.optional(),summary:a.z.string().trim().max(1e3).optional(),error:a.z.string().trim().max(4e3).optional(),currentTool:R.nullable().optional(),toolCallsSinceLastMessage:a.z.number().int().min(0).optional(),subAgents:a.z.array(M).max(50).optional(),contextCompaction:pe.optional(),counters:a.z.object({toolCalls:a.z.number().int().min(0).optional(),edits:a.z.number().int().min(0).optional(),approvals:a.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"})});a.z.discriminatedUnion("type",[Oe,Re]);const je=a.z.union([a.z.object({type:a.z.literal("schedule"),scheduleKind:a.z.literal("interval").default("interval"),intervalMs:a.z.number().int().min(1e3)}).strict(),a.z.object({type:a.z.literal("schedule"),scheduleKind:a.z.literal("daily"),timeOfDay:a.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:a.z.string().trim().min(1).max(120)}).strict(),a.z.object({type:a.z.literal("schedule"),scheduleKind:a.z.literal("weekly"),weekdays:a.z.array(a.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:a.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:a.z.string().trim().min(1).max(120)}).strict()]),De=a.z.union([je,a.z.object({type:a.z.literal("file-change"),roots:a.z.array(a.z.string().trim().min(1).max(4096)).min(1).max(50),globs:a.z.array(a.z.string().trim().min(1).max(500)).min(1).max(100),ignore:a.z.array(a.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:a.z.number().int().min(100).max(6e4).default(1500)}).strict(),a.z.object({type:a.z.literal("ai-session"),agent:a.z.string().trim().min(1).max(80).optional(),statuses:a.z.array(a.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:a.z.array(a.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),Me=a.z.object({promptTemplate:a.z.string().trim().min(1).max(2e4)}).strict(),$e=a.z.object({cooldownMs:a.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:a.z.number().int().min(1).max(20).default(1),whenBusy:a.z.enum(["skip","queue"]).default("skip")}).strict(),qe=a.z.object({configHash:a.z.string().trim().min(8).max(80),name:a.z.string().trim().min(1).max(160),description:a.z.string().trim().max(1e3).optional(),source:De,action:Me,policy:$e.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:a.z.string().datetime(),updatedAt:a.z.string().datetime()}).strict(),Le=a.z.object({type:a.z.literal("ai-session"),aiSessionId:a.z.string().trim().min(1).max(160)}).strict(),Be=a.z.object({configHash:a.z.string().trim().min(8).max(80),deploymentId:a.z.string().trim().min(1).max(240).optional(),instanceId:a.z.string().trim().min(1).max(160),origin:a.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:a.z.boolean().default(!0),target:Le,localName:a.z.string().trim().min(1).max(160).optional(),createdAt:a.z.string().datetime(),updatedAt:a.z.string().datetime()}).strict(),Ue=a.z.object({configHash:a.z.string().trim().min(8).max(80),deploymentId:a.z.string().trim().min(1).max(240).optional(),instanceId:a.z.string().trim().min(1).max(160),status:a.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:a.z.string().datetime().optional(),lastCompletedAt:a.z.string().datetime().optional(),lastSkippedAt:a.z.string().datetime().optional(),lastError:a.z.string().trim().max(4e3).optional(),runCount:a.z.number().int().min(0).default(0),skippedCount:a.z.number().int().min(0).default(0)}).strict(),Fe=a.z.object({id:a.z.string().trim().min(1).max(160),configHash:a.z.string().trim().min(8).max(80),deploymentId:a.z.string().trim().min(1).max(240).optional(),instanceId:a.z.string().trim().min(1).max(160),eventType:a.z.enum(["manual","schedule","file-change","ai-session"]),status:a.z.enum(["started","completed","failed","skipped"]),target:Le,promptPreview:a.z.string().trim().max(500),eventSummary:a.z.string().trim().max(1e3).optional(),error:a.z.string().trim().max(4e3).optional(),startedAt:a.z.string().datetime(),completedAt:a.z.string().datetime().optional()}).strict(),He=a.z.object({schemaVersion:a.z.literal(1).default(1),configs:a.z.array(qe).default([]),deployments:a.z.array(Be).default([]),runtime:a.z.array(Ue).default([]),recentRuns:a.z.array(Fe).default([])}).strict();function We(e){return Array.isArray(e)?e.map(We):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,We(t)])):e}const Ge="2026-08-01",Ve=a.z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"Proxy protocol version must use YYYY-MM-DD format."),Ke=a.z.string().trim().min(1).max(160).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),Je=a.z.string().datetime(),Qe=a.z.string().trim().min(32).max(4096),Ye=Ke,Xe=a.z.string().trim().max(2048).transform((e,t)=>{try{return function(e){const t=new URL(e.trim());if("https:"!==t.protocol||t.username||t.password||"/"!==t.pathname||t.search||t.hash)throw new Error("Proxy origin must be an HTTPS origin without credentials, path, query, or fragment.");return t.origin}(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),a.z.NEVER}}),Ze=a.z.string().trim().min(1).max(4096).transform((e,t)=>{try{return function(e){const t=e.trim();if(!t.startsWith("/")||t.startsWith("//")||t.includes("\\")||/%25/i.test(t))throw new Error("Proxy route must be a single-encoded node-agent relative route.");const n=t.split("?",1)[0];let s,i;try{s=n.split("/").map(e=>decodeURIComponent(e))}catch{throw new Error("Proxy route contains invalid percent encoding.")}if(s.some(e=>".."===e||"."===e))throw new Error("Proxy route cannot traverse outside the node-agent API.");try{i=new URL(t,"https://node-agent.invalid")}catch{throw new Error("Proxy route is invalid.")}if("https://node-agent.invalid"!==i.origin||i.username||i.password)throw new Error("Proxy route cannot select an absolute upstream URL.");return`${i.pathname}${i.search}`}(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),a.z.NEVER}}),et=new Set(["accept","accept-language","authorization","content-type","if-match","if-none-match","range","x-request-id"]),tt=new Set(["cache-control","content-disposition","content-language","content-length","content-range","content-type","etag","last-modified","x-request-id"]),nt="x-task-handoff-proxy-protocol-version",st="x-task-handoff-proxy-source-control-plane-id",it="x-task-handoff-proxy-binding-key-id",rt="x-task-handoff-proxy-credential",at={ProtocolUnsupported:"CONTROL_PLANE_PROXY_PROTOCOL_UNSUPPORTED",InviteInvalid:"CONTROL_PLANE_PROXY_INVITE_INVALID",InviteExpired:"CONTROL_PLANE_PROXY_INVITE_EXPIRED",InviteConsumed:"CONTROL_PLANE_PROXY_INVITE_CONSUMED",InviteRevoked:"CONTROL_PLANE_PROXY_INVITE_REVOKED",TargetUnavailable:"CONTROL_PLANE_PROXY_TARGET_UNAVAILABLE",TargetMismatch:"CONTROL_PLANE_PROXY_TARGET_MISMATCH",BindingUnknown:"CONTROL_PLANE_PROXY_BINDING_UNKNOWN",BindingRevoked:"CONTROL_PLANE_PROXY_BINDING_REVOKED",BindingIdentityConflict:"CONTROL_PLANE_PROXY_BINDING_IDENTITY_CONFLICT",AuthenticationFailed:"CONTROL_PLANE_PROXY_AUTHENTICATION_FAILED",RouteInvalid:"CONTROL_PLANE_PROXY_ROUTE_INVALID",HeaderInvalid:"CONTROL_PLANE_PROXY_HEADER_INVALID",ResourceLimit:"CONTROL_PLANE_PROXY_RESOURCE_LIMIT",SnapshotRequired:"CONTROL_PLANE_PROXY_SNAPSHOT_REQUIRED",Unavailable:"CONTROL_PLANE_PROXY_UNAVAILABLE",TransportFailed:"CONTROL_PLANE_PROXY_TRANSPORT_FAILED"},ot=a.z.enum(Object.values(at)),dt=a.z.object({code:ot,message:a.z.string().trim().min(1).max(2048),retryable:a.z.boolean(),details:a.z.record(a.z.string(),a.z.unknown()).optional()}).strict(),ct=a.z.enum(["active","consumed","revoked","expired"]),lt=a.z.object({id:Ke,targetNodeId:Ke,tokenHash:a.z.string().trim().min(32).max(256),status:ct,createdBy:Ke,expiresAt:Je,consumedByClaimId:Ke.optional(),consumedAt:Je.optional(),revokedAt:Je.optional(),createdAt:Je,updatedAt:Je}).strict(),pt=lt.omit({tokenHash:!0}),ut=a.z.object({targetNodeId:Ke,expiresInSeconds:a.z.number().int().min(60).max(3600).default(600)}).strict(),gt=a.z.object({invite:pt,token:a.z.string().trim().min(24).max(512),proxyOrigin:Xe,protocolVersion:Ve}).strict(),mt=a.z.enum(["active","revoked"]),ht=a.z.object({id:Ke,claimId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,bindingKeyId:Ke,credentialHash:a.z.string().trim().min(32).max(256),status:mt,revision:a.z.number().int().positive(),lastError:dt.optional(),revokedAt:Je.optional(),createdAt:Je,updatedAt:Je}).strict(),yt=ht.omit({credentialHash:!0}),ft=a.z.object({protocolVersion:Ve,inviteToken:a.z.string().trim().min(24).max(512).optional(),claimId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke.optional(),bindingKeyId:Ke,credential:Qe}).strict(),It=a.z.object({sourceControlPlaneId:Ke,bindingKeyId:Ke,credential:Qe}).strict(),bt=a.z.object({id:Ke,name:a.z.string().trim().min(1).max(160),status:a.z.enum(["unknown","online","offline","degraded"]),health:a.z.enum(["unknown","ok","degraded","failed"]),lastSeenAt:Je.optional(),capabilities:a.z.record(a.z.string(),a.z.unknown()).default({})}).strict(),vt=a.z.object({protocolVersion:Ve,binding:yt,streamId:Ke,revision:a.z.number().int().nonnegative(),observedAt:Je,target:bt}).strict(),wt=a.z.object({type:a.z.literal("control-plane-proxy.event"),protocolVersion:Ve,streamId:Ke,bindingId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,revision:a.z.number().int().positive(),source:a.z.object({id:a.z.string().trim().min(1).max(512),seq:a.z.number().int().nonnegative()}).strict(),target:bt,event:a.z.object({type:a.z.string().trim().min(1).max(240),topic:a.z.string().trim().min(1).max(240),createdAt:Je}).strict()}).strict(),St=a.z.object({type:a.z.literal("control-plane-proxy.events.ready"),protocolVersion:Ve,streamId:Ke,bindingId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,latestRevision:a.z.number().int().nonnegative(),earliestRetainedRevision:a.z.number().int().nonnegative()}).strict(),At=a.z.object({type:a.z.literal("control-plane-proxy.snapshot-required"),protocolVersion:Ve,streamId:Ke,bindingId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,sinceRevision:a.z.number().int().nonnegative(),latestRevision:a.z.number().int().nonnegative(),earliestRetainedRevision:a.z.number().int().nonnegative(),error:dt}).strict(),kt=a.z.discriminatedUnion("type",[wt,St,At]),Ct=a.z.object({protocolVersion:Ve,binding:yt,target:vt.shape.target}).strict(),xt=a.z.object({id:Ke,claimId:Ke,proxyOrigin:Xe,requestedName:a.z.string().trim().min(1).max(160).optional(),sourceControlPlaneId:Ke,targetNodeId:Ke.optional(),bindingKeyId:Ke,credential:Qe,status:a.z.enum(["pending","compensation-required"]),createdAt:Je,updatedAt:Je,expiresAt:Je}).strict();xt.omit({credential:!0});const Tt=a.z.object({id:Ke,nodeId:Ke,proxyOrigin:Xe,proxyBindingId:Ke,targetNodeId:Ke,sourceControlPlaneId:Ke,bindingKeyId:Ke,credential:Qe,createdAt:Je,updatedAt:Je}).strict(),zt="2026-08-01",Nt=a.z.string(),Et=a.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),_t=a.z.string().datetime(),Pt=a.z.record(a.z.string(),a.z.string()).default({}),Ot=a.z.record(a.z.string(),a.z.string()).default({}),Rt=a.z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"Protocol version must use YYYY-MM-DD format.");function jt(e){if(null!=e){if("string"==typeof e)return{encoding:"utf8",data:e};if(Buffer.isBuffer(e))return{encoding:"base64",data:e.toString("base64")};if(e instanceof ArrayBuffer)return{encoding:"base64",data:Buffer.from(e).toString("base64")};if(ArrayBuffer.isView(e))return{encoding:"base64",data:Buffer.from(e.buffer,e.byteOffset,e.byteLength).toString("base64")};throw new TypeError("Node tunnel request body must be a string or binary buffer.")}}a.z.object({encoding:a.z.enum(["utf8","base64"]),data:a.z.string()}).strict().superRefine((e,t)=>{"base64"===e.encoding&&Buffer.from(e.data,"base64").toString("base64")!==e.data&&t.addIssue({code:"custom",path:["data"],message:"Node tunnel binary body must use canonical base64 encoding."})});const Dt=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,Mt=/^sha256:([a-fA-F0-9]{64})$/,$t=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,qt=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function Lt(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 s=-1===n?void 0:t.slice(n+1),i=-1===n?t:t.slice(0,n),r=s?Mt.exec(s):void 0;if(s&&!r)throw new Error("Docker image digest must use sha256 followed by 64 hexadecimal characters.");const a=i.lastIndexOf("/"),o=i.lastIndexOf(":"),d=o>a,c=d?i.slice(o+1):void 0,l=d?i.slice(0,o):i;if(c&&!Dt.test(c))throw new Error("Docker image tag is invalid.");if(!c&&!r)throw new Error("Docker image reference must include an explicit tag or sha256 digest.");const p=l.split("/");if(!p.length||p.some(e=>!e))throw new Error("Docker image repository path is invalid.");const u=p.length>1&&(p[0].includes(".")||p[0].includes(":")||"localhost"===p[0]),g=u?p.slice(1):p;if(u&&!$t.test(p[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!g.length||g.some(e=>!qt.test(e)))throw new Error("Docker image repository names must be lowercase and use valid separators.");const m=p.map((e,t)=>u&&0===t?e.toLowerCase():e).join("/"),h=r?`sha256:${r[1].toLowerCase()}`:void 0;return`${m}${c?`:${c}`:""}${h?`@${h}`:""}`}function Bt(e){const t=Lt(e),n=t.indexOf("@"),s=-1===n?void 0:t.slice(n+1),i=-1===n?t:t.slice(0,n),r=i.lastIndexOf("/"),a=i.lastIndexOf(":"),o=a>r;return{reference:t,repository:o?i.slice(0,a):i,tag:o?i.slice(a+1):void 0,digest:s}}const Ut=a.z.string().trim().min(1).max(512).transform((e,t)=>{try{return Lt(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),a.z.NEVER}}),Ft=a.z.string().trim().transform((e,t)=>{const n=Mt.exec(e);return n?`sha256:${n[1].toLowerCase()}`:(t.addIssue({code:"custom",message:"Docker image digest must use sha256 followed by 64 hexadecimal characters."}),a.z.NEVER)}),Ht=a.z.object({codexModelHash:Et.nullable().optional(),claudeModelHash:Et.nullable().optional()}).strict().default({}),Wt=a.z.object({imageId:Et,tag:a.z.string().trim().min(1).max(128).regex(Dt).optional()}).strict(),Gt={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"};function Vt(e,t){if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;return{imageId:"string"==typeof t.imageId&&Gt[t.imageId]||t.imageId,..."string"==typeof t.tag?{tag:t.tag}:{}}}if("string"==typeof t)return{imageId:Gt[t]||t}}function Kt(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,{defaultImageId:n,...s}=t;return{...s,defaultImageSelection:Vt(t.defaultImageSelection,n)}}const Jt=a.z.object({id:Et,name:a.z.string().trim().min(1).max(120),kind:a.z.enum(["tty","gui","web"]),source:a.z.enum(["builtin","custom"]),availability:a.z.enum(["available","missing-dependency"]),capabilities:a.z.object({automation:a.z.enum(["cdp"]).optional(),supportsCwdSelection:a.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:a.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),Qt=a.z.object({code:a.z.enum(["APP_CATALOG_INVALID"]),message:a.z.string().trim().min(1).max(500)}).strict(),Yt=a.z.object({items:a.z.array(Jt).max(256),observedAt:_t,issues:a.z.array(Qt).max(32).default([])}).strict(),Xt=a.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),Zt=a.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),en=a.z.enum(["apt","dnf","brew","npm"]),tn=a.z.enum(["user","passwordless-sudo","root"]),nn=a.z.object({platform:Xt,arch:Zt,installers:a.z.array(en).max(8),privilege:tn,installerAccess:a.z.object({npmGlobalWritable:a.z.boolean().optional()}).strict().optional()}).strict(),sn=a.z.enum(["installed","not-installed","broken","unsupported"]),rn=a.z.enum(["recipe","bundled","external","none"]),an=a.z.object({code:a.z.enum(["BUNDLED","EXTERNALLY_MANAGED","ALREADY_INSTALLED","NOT_INSTALLED","UNSUPPORTED_PLATFORM","INSTALLER_UNAVAILABLE","INSTALLER_NOT_WRITABLE","INSUFFICIENT_PRIVILEGE","OPERATION_IN_PROGRESS"]),message:a.z.string().trim().min(1).max(500)}).strict(),on=a.z.object({id:Et,name:a.z.string().trim().min(1).max(120),kind:a.z.enum(["tty","gui","web"]),description:a.z.string().trim().max(500).optional(),state:sn,managementSource:rn,version:a.z.string().trim().max(120).optional(),canInstall:a.z.boolean(),canUninstall:a.z.boolean(),installReason:an.optional(),uninstallReason:an.optional(),activeJobId:Et.optional()}).strict(),dn=a.z.enum(["install","uninstall"]),cn=a.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),ln=a.z.object({current:a.z.number().finite().nonnegative().optional(),total:a.z.number().finite().positive().optional(),unit:a.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"]}),pn=a.z.object({executable:a.z.string().trim().min(1).max(4096),args:a.z.array(a.z.string().max(4096)).max(128)}).strict(),un=a.z.object({code:a.z.string().trim().min(1).max(120).regex(/^[a-z0-9][a-z0-9._-]*$/),message:a.z.string().trim().min(1).max(4096),retryable:a.z.boolean(),sessionIds:a.z.array(Et).max(64).optional()}).strict(),gn=a.z.object({id:Et,requestId:Et.optional(),appId:Et,operation:dn,state:cn,phase:a.z.string().trim().min(1).max(120).optional(),progress:ln.optional(),command:pn.optional(),logTail:a.z.string().max(32768).optional(),logTruncated:a.z.boolean().optional(),error:un.optional(),requestedAt:_t,startedAt:_t.optional(),finishedAt:_t.optional(),updatedAt:_t}).strict(),mn=a.z.object({streamId:Et,sequence:a.z.number().int().nonnegative(),capabilities:nn,apps:a.z.array(on).max(256),activeJobs:a.z.array(gn).max(256),recentJobs:a.z.array(gn).max(256),observedAt:_t}).strict(),hn=a.z.object({requestId:Et.optional()}).strict(),yn=a.z.object({job:gn}).strict();a.z.object({type:a.z.literal("app-management"),streamId:Et,sequence:a.z.number().int().nonnegative(),observedAt:_t,job:gn.optional(),snapshot:mn.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const fn=a.z.object({component:a.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:a.z.string().trim().max(160).optional(),packageVersion:a.z.string().trim().max(80).optional(),protocolVersion:Nt.optional(),buildId:a.z.string().trim().max(160).optional(),builtAt:a.z.string().trim().max(120).optional(),gitCommit:a.z.string().trim().max(120).optional(),imageRef:a.z.string().trim().max(512).optional(),imageDigest:a.z.string().trim().max(240).optional()}).strict(),In=a.z.enum(["stable","beta","alpha"]),bn=a.z.object({packageName:a.z.literal("@task-handoff/controlled-instance"),version:a.z.string().trim().min(1).max(80),platform:a.z.string().trim().min(1).max(40),arch:a.z.string().trim().min(1).max(40),formatVersion:a.z.number().int().positive(),launcherAbi:a.z.number().int().positive(),entrypoint:a.z.string().trim().min(1).max(512),sha256:a.z.string().regex(/^[a-f0-9]{64}$/)}).strict(),vn=a.z.object({code:a.z.enum(["INSTANCE_RUNTIME_VERSION_MISMATCH","INSTANCE_RUNTIME_ARTIFACT_UNAVAILABLE","INSTANCE_RUNTIME_ARTIFACT_INVALID","INSTANCE_RUNTIME_INSTALL_FAILED","INSTANCE_RUNTIME_RESTART_FAILED","INSTANCE_RUNTIME_VERIFICATION_FAILED","INSTANCE_BASE_RUNTIME_INCOMPATIBLE","NODE_UPDATE_PREFLIGHT_FAILED","NODE_UPDATE_FAILED","LEGACY_INSTANCE_UPDATE_RETIRED"]),message:a.z.string().trim().min(1).max(4096),expectedVersion:a.z.string().trim().min(1).max(80).optional(),actualVersion:a.z.string().trim().min(1).max(80).optional(),retryable:a.z.boolean().default(!1)}).strict(),wn=a.z.object({desiredVersion:a.z.string().trim().min(1).max(80),actualVersion:a.z.string().trim().min(1).max(80).optional(),phase:a.z.enum(["pending","draining","installing","restarting","verifying","matched","failed"]),attempt:a.z.number().int().nonnegative().default(0),lastAttemptAt:_t.optional(),matchedAt:_t.optional(),error:vn.optional()}).strict(),Sn=a.z.object({runningInstanceCount:a.z.number().int().nonnegative(),stoppedInstanceCount:a.z.number().int().nonnegative(),activeInstanceCount:a.z.number().int().nonnegative(),restartInstanceCount:a.z.number().int().nonnegative(),runningInstanceIds:a.z.array(Et).max(1024).default([]),stoppedInstanceIds:a.z.array(Et).max(1024).default([]),activeInstanceIds:a.z.array(Et).max(1024).default([])}).strict(),An=a.z.object({phase:a.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),desiredVersion:a.z.string().trim().min(1).max(80),nodeVersion:a.z.string().trim().min(1).max(80).optional(),expectedInstanceIds:a.z.array(Et).max(1024).default([]),expectedInstanceCount:a.z.number().int().nonnegative(),matchedInstanceCount:a.z.number().int().nonnegative(),pendingInstanceCount:a.z.number().int().nonnegative(),failedInstanceCount:a.z.number().int().nonnegative(),deferredInstanceCount:a.z.number().int().nonnegative().default(0)}).strict(),kn=a.z.object({channel:In.default("stable")}).strict(),Cn=a.z.object({source:a.z.literal("npm"),channel:In,currentVersion:a.z.string().trim().max(240).optional(),availableVersion:a.z.string().trim().min(1).max(240),artifactRef:a.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:a.z.array(bn).default([]),impact:Sn,updateAvailable:a.z.boolean(),supported:a.z.boolean().default(!0),reason:a.z.string().trim().max(2048).optional(),checkedAt:_t,preflightToken:a.z.string().trim().min(16).max(240).optional()}).strict(),xn=a.z.object({id:Et,nodeId:Et,source:a.z.literal("npm"),channel:In,fromVersion:a.z.string().trim().max(240).optional(),toVersion:a.z.string().trim().min(1).max(240),artifactRef:a.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:a.z.array(bn).default([]),impact:Sn,rollout:An,status:a.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),error:vn.optional(),startedAt:_t.optional(),completedAt:_t.optional(),createdAt:_t,updatedAt:_t}).strict(),Tn=a.z.object({channel:In,targetVersion:a.z.string().trim().min(1).max(240),preflightToken:a.z.string().trim().min(16).max(240)}).strict(),zn=a.z.object({type:a.z.enum(["branch","tag","commit"]),name:a.z.string().trim().min(1).max(240).optional(),commit:a.z.string().trim().max(80).optional()}).strict(),Nn=a.z.object({type:a.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:a.z.string().trim().min(1).max(120).optional()}).strict(),En=a.z.object({depth:a.z.number().int().positive().max(1e5).optional(),submodules:a.z.boolean().default(!1),lfs:a.z.boolean().default(!1),subdirectory:a.z.string().trim().max(240).default("")}).strict();a.z.object({id:Et,name:a.z.string().trim().min(1).max(160),url:a.z.string().trim().min(1).max(2048),provider:a.z.string().trim().max(80).optional(),ref:zn.default({type:"branch",name:"main"}),auth:Nn.default({type:"none"}),clone:En.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageSelection:Wt.optional(),labels:Pt,createdAt:_t,updatedAt:_t}).strict();const _n=a.z.object({id:Et,nodeId:Et,name:a.z.string().trim().min(1).max(160),path:a.z.string().trim().min(1).max(4096),defaultImageSelection:Wt.optional(),labels:Pt,createdAt:_t,updatedAt:_t}).strict(),Pn=a.z.discriminatedUnion("type",[a.z.object({type:a.z.literal("git-repository"),repositoryId:Et.optional(),url:a.z.string().trim().min(1).max(2048),provider:a.z.string().trim().max(80).optional(),ref:zn.default({type:"branch",name:"main"}),auth:Nn.default({type:"none"}),clone:En.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),a.z.object({type:a.z.literal("git-template"),url:a.z.string().trim().min(1).max(2048),templateId:a.z.string().trim().min(1).max(120).optional(),ref:zn.default({type:"branch",name:"main"}),auth:Nn.default({type:"none"}),clone:En.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),a.z.object({type:a.z.literal("local-folder"),localFolderId:Et.optional(),path:a.z.string().trim().min(1).max(4096),ownerNodeId:Et.optional()}).strict()]),On=a.z.object({mode:a.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:a.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:a.z.string().trim().max(240).optional(),readOnly:a.z.boolean().default(!1)}).strict(),Rn=a.z.enum(["codex","claude"]),jn=a.z.object({id:Et,name:a.z.string().trim().min(1).max(160),source:Pn,defaultImageSelection:Wt.optional(),defaultNodeId:Et.optional(),defaultRuntimeId:Et.optional(),workspacePolicy:On,labels:Pt,createdAt:_t,updatedAt:_t}).strict(),Dn=a.z.object({id:Et,name:a.z.string().trim().min(1).max(160),endpoint:a.z.string().trim().min(1).max(2048),key:a.z.string().trim().min(1).max(4096),model:a.z.string().trim().min(1).max(240),app:Rn,enabled:a.z.boolean().default(!0),order:a.z.number().int().min(0).max(1e6).default(0),labels:Pt,createdAt:_t,updatedAt:_t}).strict(),Mn=Dn.omit({key:!0}).extend({keyPreview:a.z.string().trim().min(1).max(160),keySet:a.z.boolean()}).strict(),$n=Mn.extend({referenceCount:a.z.number().int().min(0)}).strict(),qn=a.z.object({instanceId:Et,codexModelHash:Et.optional(),claudeModelHash:Et.optional(),updatedAt:_t}).strict(),Ln=a.z.discriminatedUnion("type",[a.z.object({type:a.z.literal("control-plane"),name:Dn.shape.name,enabled:Dn.shape.enabled,order:Dn.shape.order}).strict(),a.z.object({type:a.z.literal("node"),nodeId:Et,name:Dn.shape.name,enabled:Dn.shape.enabled,order:Dn.shape.order,referenceCount:a.z.number().int().min(0)}).strict()]),Bn=a.z.object({id:Et,model:Mn,locations:a.z.array(Ln).min(1),referenceCount:a.z.number().int().min(0).default(0)}).strict(),Un=a.z.object({nodeId:Et,code:a.z.string().trim().min(1).max(120),message:a.z.string().trim().min(1).max(2e3)}).strict(),Fn=a.z.object({models:a.z.array(Bn),nodeDiagnostics:a.z.array(Un).default([]),updatedAt:_t}).strict();function Hn(e){const t={app:Rn.parse(e.app),endpoint:Dn.shape.endpoint.parse(e.endpoint),key:Dn.shape.key.parse(e.key),model:Dn.shape.model.parse(e.model)};return`mdl_${o.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}Dn.omit({id:!0,createdAt:!0,updatedAt:!0}).strict().partial().strict(),a.z.object({modelSelection:Ht,codexModelHash:Et.optional(),claudeModelHash:Et.optional()}).strict();const Wn=a.z.enum(["market","custom"]),Gn=a.z.enum(["active","deprecated","yanked"]),Vn=a.z.discriminatedUnion("kind",[a.z.object({kind:a.z.literal("builtin"),key:a.z.string().trim().min(1).max(120)}).strict(),a.z.object({kind:a.z.literal("remote"),url:a.z.string().trim().url().max(2048),mediaType:a.z.enum(["image/png","image/jpeg","image/webp"]).optional(),digest:Ft.optional()}).strict()]),Kn={kind:"builtin",key:"default-image-cover"},Jn=a.z.array(a.z.string().trim().min(1).max(80)).default([]),Qn=a.z.array(a.z.string().trim().min(1).max(120)).default([]),Yn=a.z.record(a.z.string().trim().min(2).max(35),a.z.string().trim().min(1).max(4096)),Xn=a.z.object({os:a.z.string().trim().min(1).max(40),architecture:a.z.string().trim().min(1).max(40),digest:Ft.optional(),downloadSizeBytes:a.z.number().int().positive().optional(),unpackedSizeBytes:a.z.number().int().positive().optional()}).strict(),Zn=a.z.object({name:a.z.string().trim().min(1).max(128).regex(Dt),version:a.z.string().trim().min(1).max(160).optional(),reference:Ut,manifestDigest:Ft.optional(),publishedAt:_t.optional(),platforms:a.z.array(Xn).default([]),status:Gn.default("active")}).strict(),es=a.z.object({id:Et,publisher:a.z.string().trim().min(1).max(160),slug:a.z.string().trim().min(1).max(160),name:a.z.string().trim().min(1).max(160),description:a.z.string().trim().min(1).max(4096),localizedDescriptions:Yn.optional(),cover:Vn.optional(),repository:a.z.string().trim().min(1).max(512),defaultTag:a.z.string().trim().min(1).max(128).regex(Dt),tags:a.z.array(Zn).min(1),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,status:Gn.default("active")}).strict().superRefine((e,t)=>{e.tags.some(t=>t.name===e.defaultTag)||t.addIssue({code:"custom",path:["defaultTag"],message:"Market defaultTag must identify one of the image tags."})}),ts=a.z.object({protocolVersion:Rt,catalogId:Et,revision:a.z.string().trim().min(1).max(240),source:a.z.enum(["embedded","remote","cache"]),generatedAt:_t,expiresAt:_t.optional(),items:a.z.array(es)}).strict(),ns=a.z.object({source:a.z.enum(["embedded","remote","cache"]),state:a.z.enum(["ready","refreshing","stale","failed"]),revision:a.z.string().trim().min(1).max(240),updatedAt:_t,error:a.z.string().trim().max(4096).optional()}).strict(),ss=a.z.object({id:Et,origin:a.z.literal("custom"),name:a.z.string().trim().min(1).max(160),description:a.z.string().trim().max(4096).optional(),localizedDescriptions:Yn.optional(),cover:Vn.optional(),reference:Ut,repository:a.z.string().trim().min(1).max(512),tag:a.z.string().trim().min(1).max(128).regex(Dt).optional(),pullPolicy:a.z.literal("if-not-present").default("if-not-present"),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,createdAt:_t,updatedAt:_t}).strict(),is=a.z.object({name:a.z.string().trim().min(1).max(128).regex(Dt),version:a.z.string().trim().min(1).max(160).optional(),reference:Ut,manifestDigest:Ft.optional(),status:Gn}).strict(),rs=a.z.object({id:Et,origin:Wn,name:a.z.string().trim().min(1).max(160),description:a.z.string().trim().max(4096).optional(),localizedDescriptions:Yn.optional(),cover:Vn,repository:a.z.string().trim().min(1).max(512),tag:a.z.string().trim().min(1).max(128).regex(Dt).optional(),availableTags:a.z.array(is),reference:Ut,digest:Ft.optional(),downloadSizeBytes:a.z.number().int().positive().optional(),unpackedSizeBytes:a.z.number().int().positive().optional(),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,readOnly:a.z.boolean(),lifecycleStatus:Gn.optional(),market:a.z.object({catalogId:Et,catalogRevision:a.z.string().trim().min(1).max(240),publisher:a.z.string().trim().min(1).max(160),version:a.z.string().trim().min(1).max(160).optional()}).strict().optional()}).strict(),as=a.z.object({id:Et,origin:Wn,name:a.z.string().trim().min(1).max(160),description:a.z.string().trim().max(4096).optional(),localizedDescriptions:Yn.optional(),cover:Vn.optional(),repository:a.z.string().trim().min(1).max(512),tag:a.z.string().trim().min(1).max(128).regex(Dt).optional(),requestedReference:Ut,resolvedDigest:Ft.optional(),resolvedReference:Ut.optional(),downloadSizeBytes:a.z.number().int().positive().optional(),pullPolicy:a.z.literal("if-not-present").default("if-not-present"),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,market:a.z.object({catalogId:Et,catalogRevision:a.z.string().trim().min(1).max(240),publisher:a.z.string().trim().min(1).max(160),version:a.z.string().trim().min(1).max(160).optional()}).strict().optional(),createdAt:_t,updatedAt:_t}).strict(),os=a.z.object({phase:a.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:Ut,generation:a.z.number().int().nonnegative().default(0),error:a.z.string().trim().max(4096).optional(),startedAt:_t,updatedAt:_t}).strict(),ds="image.pull.terminal.output",cs="image.pull.terminal.finished",ls=a.z.object({instanceId:Et,generation:a.z.number().int().nonnegative(),requestedReference:Ut,sequence:a.z.number().int().nonnegative(),observedAt:_t}).strict(),ps=ls.extend({data:a.z.string().min(1).max(65536),replay:a.z.boolean().optional()}).strict(),us=ls.extend({outcome:a.z.enum(["succeeded","failed"])}).strict(),gs=ls.extend({status:a.z.enum(["connecting","pulling","extracting","complete","failed"]),layers:a.z.object({total:a.z.number().int().nonnegative(),completed:a.z.number().int().nonnegative(),downloaded:a.z.number().int().nonnegative(),downloading:a.z.number().int().nonnegative(),extracting:a.z.number().int().nonnegative()}).strict(),bytes:a.z.object({current:a.z.number().finite().nonnegative(),total:a.z.number().finite().positive()}).strict().optional(),percent:a.z.number().finite().min(0).max(100).optional(),message:a.z.string().trim().min(1).max(500),terminalTail:a.z.string().max(262144).optional(),terminalTruncated:a.z.boolean().optional()}).strict();function ms(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=new Set(["id","origin","name","description","localizedDescriptions","cover","reference","repository","tag","image","registry","pullPolicy","capabilities","optionalApps","defaultEnv","labels","createdAt","updatedAt"]);for(const e of Object.keys(n))s.has(e)||t?.({imageId:"string"==typeof n.id?n.id:void 0,field:e});const i="string"==typeof n.id?n.id:void 0,r="string"==typeof n.image?n.image:void 0;void 0!==r&&t?.({imageId:i,field:"image"}),Object.prototype.hasOwnProperty.call(n,"registry")&&t?.({imageId:i,field:"registry"});let a,o="string"==typeof n.reference?n.reference:r;if(o)try{o=Lt(o)}catch{if(!o.includes("@")&&o.lastIndexOf(":")<=o.lastIndexOf("/"))try{o=Lt(`${o}:latest`),t?.({imageId:i,field:"reference:implicit-latest"})}catch{}}try{a=o?Bt(o):void 0}catch{}return{id:n.id,origin:"custom",name:n.name,description:n.description,localizedDescriptions:n.localizedDescriptions,cover:Ls(n.cover,["kind","key","url","mediaType","digest"]),reference:a?.reference??o,repository:a?.repository??n.repository,tag:a?.tag??n.tag,pullPolicy:n.pullPolicy??"if-not-present",capabilities:n.capabilities,optionalApps:n.optionalApps,defaultEnv:n.defaultEnv,labels:n.labels,createdAt:n.createdAt,updatedAt:n.updatedAt}}const hs=a.z.discriminatedUnion("kind",[a.z.object({kind:a.z.literal("direct")}).strict(),a.z.object({kind:a.z.literal("control-plane-proxy"),proxyId:Et,proxyBindingId:Et,targetNodeId:Et}).strict()]),ys=a.z.object({reachability:a.z.enum(["unknown","reachable","unreachable"]),bindingStatus:a.z.enum(["unknown","active","revoked"]),bindingRevision:a.z.number().int().positive().optional(),streamId:a.z.string().trim().min(1).max(160).optional(),revision:a.z.number().int().nonnegative().optional(),observedAt:_t.optional(),target:bt.optional(),lastError:dt.optional(),updatedAt:_t}).strict(),fs=a.z.object({id:Et,name:a.z.string().trim().min(1).max(160),connectionMode:a.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss","control-plane-proxy"]).default("direct-http"),connectionPath:hs.default({kind:"direct"}),connectionEnabled:a.z.boolean().default(!0),auth:a.z.object({mode:a.z.enum(["local-static-key","paired-hmac","proxy-binding"]).default("local-static-key"),keyId:Et.optional(),secret:a.z.string().trim().max(4096).optional(),pairedAt:_t.optional(),pairing:a.z.object({status:a.z.enum(["pending","paired","expired"]).default("pending"),joinToken:a.z.string().trim().max(4096).optional(),expiresAt:_t.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:a.z.string().trim().max(2048).optional(),controlEndpoint:a.z.string().trim().max(2048).optional(),containerEndpoint:a.z.string().trim().max(2048).optional(),publicWebBase:a.z.string().trim().max(2048).optional(),status:a.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:a.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:a.z.record(a.z.string(),a.z.unknown()).default({}),proxyState:ys.optional(),appInventory:Yt.optional(),labels:Pt,lastSeenAt:_t.optional(),createdAt:_t,updatedAt:_t}).strict().superRefine((e,t)=>{"control-plane-proxy"===e.connectionPath.kind&&"control-plane-proxy"!==e.connectionMode&&t.addIssue({code:"custom",path:["connectionPath"],message:"Control-plane proxy paths require control-plane-proxy connection mode."}),"control-plane-proxy"===e.connectionMode&&"control-plane-proxy"!==e.connectionPath.kind&&t.addIssue({code:"custom",path:["connectionPath"],message:"Control-plane proxy nodes require a control-plane-proxy connection path."})}),Is=a.z.object({id:Et,nodeId:Et,name:a.z.string().trim().min(1).max(160),type:a.z.enum(["docker","kubernetes","local"]),status:a.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:a.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:a.z.record(a.z.string(),a.z.unknown()).default({}),labels:Pt,createdAt:_t,updatedAt:_t}).strict(),bs=a.z.object({instanceId:Et,runtimeKind:a.z.literal("docker"),state:a.z.enum(["pending","available","stopped","unavailable"]),sampledAt:_t,cpu:a.z.object({usagePercent:a.z.number().nonnegative()}).strict().optional(),memory:a.z.object({usageBytes:a.z.number().nonnegative(),limitBytes:a.z.number().nonnegative().optional(),usagePercent:a.z.number().nonnegative().optional()}).strict().optional(),network:a.z.object({rxBytes:a.z.number().nonnegative(),txBytes:a.z.number().nonnegative()}).strict().optional(),blockIo:a.z.object({readBytes:a.z.number().nonnegative(),writeBytes:a.z.number().nonnegative()}).strict().optional(),pids:a.z.number().int().nonnegative().optional(),error:a.z.string().trim().max(2048).optional()}).strict(),vs="instance.metrics.snapshot",ws=a.z.object({ok:a.z.boolean().optional(),role:a.z.string().optional(),nodeId:Et.optional(),platform:Xt.optional()}).passthrough(),Ss=a.z.object({bindScope:a.z.enum(["loopback","all-ipv4"]),port:a.z.number().int().min(1).max(65535)}).strict().extend({host:a.z.enum(["127.0.0.1","0.0.0.0"]),status:a.z.enum(["listening","error"]),source:a.z.enum(["bootstrap","persisted"]),error:a.z.string().trim().max(2048).optional()}).strict(),As=a.z.object({nodeId:Et,joinToken:a.z.string().trim().min(1),expiresAt:_t}).passthrough(),ks=a.z.object({id:Et,keyId:Et,name:a.z.string().trim().min(1).max(160).optional(),pairedAt:_t,updatedAt:_t,current:a.z.boolean()}).passthrough(),Cs=a.z.object({id:Et,pairingKeyId:Et,url:a.z.string().trim().min(1).max(2048),name:a.z.string().trim().min(1).max(160).optional(),enabled:a.z.boolean(),createdAt:_t,updatedAt:_t,status:a.z.enum(["disabled","connecting","connected","reconnecting","failed"]),lastConnectedAt:_t.optional(),lastDisconnectedAt:_t.optional(),error:a.z.string().optional()}).passthrough(),xs=a.z.object({pairing:ks,connection:a.z.object({id:Et,pairingKeyId:Et,url:a.z.string().trim().min(1).max(2048),name:a.z.string().trim().min(1).max(160).optional(),enabled:a.z.boolean(),createdAt:_t,updatedAt:_t}).passthrough(),tunnel:a.z.object({status:a.z.enum(["disabled","saved","connecting","connected","failed"]),error:a.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),Ts=a.z.object({deleted:a.z.boolean().optional()}).passthrough(),zs=a.z.object({repository:a.z.string().trim().optional(),tag:a.z.string().trim().optional(),id:a.z.string().trim(),createdSince:a.z.string().trim().optional(),size:a.z.string().trim().optional(),sizeBytes:a.z.number().int().nonnegative().optional(),reference:a.z.string().trim().min(1),repoDigests:a.z.array(a.z.string().trim().min(1).max(512)).default([])}).passthrough(),Ns=a.z.object({image:rs,status:a.z.enum(["available","pull-required","unknown"]),localImage:zs.optional(),localSizeBytes:a.z.number().int().nonnegative().optional(),downloadSizeBytes:a.z.number().int().positive().optional(),unpackedSizeBytes:a.z.number().int().positive().optional(),error:a.z.string().trim().max(2048).optional()}).strict(),Es=a.z.lazy(()=>a.z.object({name:a.z.string().trim().min(1),path:a.z.string().trim().min(1).max(4096),children:a.z.array(Es)}).passthrough()),_s=a.z.object({status:a.z.number().int().min(100).max(599).optional(),headers:a.z.record(a.z.string(),a.z.string()).optional(),bodyBase64:a.z.string().optional()}).passthrough(),Ps=a.z.object({strategy:a.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:a.z.string().trim().max(2048).optional(),api:a.z.string().trim().max(2048).optional(),vnc:a.z.string().trim().max(2048).optional(),tty:a.z.string().trim().max(2048).optional(),logs:a.z.string().trim().max(2048).optional(),status:a.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),Os=a.z.object({strategy:a.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:a.z.string().trim().max(2048).optional(),api:a.z.string().trim().max(2048).optional(),ws:a.z.string().trim().max(2048).optional(),status:a.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),Rs=a.z.object({mode:a.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:a.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:a.z.string().trim().max(4096).optional(),resolvedCommit:a.z.string().trim().max(120).optional(),error:a.z.string().trim().max(2048).optional()}).passthrough(),js=a.z.object({id:Et,name:a.z.string().trim().min(1).max(160),projectId:Et.optional(),source:Pn,sourceSnapshot:a.z.record(a.z.string(),a.z.unknown()).default({}),modelSelection:Ht,nodeId:Et,runtimeId:Et,imageSelection:Wt.optional(),imageSnapshot:as.optional(),imageProvisioning:os.optional(),stateRevision:a.z.number().int().min(0).default(0),processIncarnationId:a.z.string().trim().min(1).max(120).optional(),status:a.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:a.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:a.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:a.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:a.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:a.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:a.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:Nt.optional(),instanceVersion:a.z.string().trim().max(80).optional(),build:fn.optional(),runtimeVersion:wn.optional(),ready:a.z.boolean().default(!1),capabilities:a.z.record(a.z.string(),a.z.unknown()).default({}),appInventory:Yt.optional(),config:a.z.object({autoImportAgentConfigs:a.z.boolean().default(!0),defaultCodexPermissionMode:G.default("ask")}).strict().default({autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"}),workspace:Rs.default({status:"unknown"}),target:Ps.default({strategy:"direct-port",status:"unknown"}),access:Os,apps:a.z.object({runningCount:a.z.number().int().min(0).default(0),problemCount:a.z.number().int().min(0).default(0),updatedAt:_t.optional(),revision:a.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:we.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:a.z.object({enabledCount:a.z.number().int().min(0).default(0),runningCount:a.z.number().int().min(0).default(0),errorCount:a.z.number().int().min(0).default(0),configs:a.z.array(a.z.object({configHash:a.z.string().trim().min(1),config:qe,deployments:a.z.array(Be).default([]),runtime:a.z.array(Ue).default([])}).strict()).default([]),recentRuns:a.z.array(Fe).default([])}).strict().optional(),runtime:a.z.object({kind:a.z.string().trim().max(80).optional(),containerName:a.z.string().trim().max(240).optional(),containerId:a.z.string().trim().max(240).optional(),workspacePath:a.z.string().trim().max(4096).optional(),pid:a.z.number().int().positive().optional(),port:a.z.number().int().positive().max(65535).optional(),labels:a.z.record(a.z.string(),a.z.string()).default({})}).strict().default({labels:{}}),registrationToken:a.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:_t.optional(),createdAt:_t,updatedAt:_t}).strict(),Ds="instance.lifecycle.snapshot",Ms=a.z.object({instanceId:js.shape.id,revision:a.z.number().int().min(0),updatedAt:_t,status:js.shape.status.unwrap(),health:js.shape.health.unwrap(),connectionStatus:js.shape.connectionStatus.unwrap(),accessStatus:a.z.enum(["reachable","endpoint-unreachable"]),imageProvisioning:os.optional(),workspace:Rs,runtime:js.shape.runtime.unwrap(),runtimeVersion:wn.optional(),ready:a.z.boolean(),lastHeartbeatAt:_t.optional()}).strict();function $s(e,t,n,s,i){if(!t||"object"!=typeof t||Array.isArray(t))return;let r=structuredClone(t);for(let t=0;t<20;t+=1){const t=e.safeParse(r);if(t.success)return t.data;let n=!1;for(const e of t.error.issues){if("unrecognized_keys"!==e.code)continue;const t=qs(r,e.path);if(t&&"object"==typeof t&&!Array.isArray(t))for(const s of e.keys)Object.prototype.hasOwnProperty.call(t,s)&&(delete t[s],n=!0)}if(!n){const n=structuredClone(r);let s=!1;for(const e of t.error.issues){if(!e.path.length||"string"!=typeof e.path.at(-1))continue;const t=qs(n,e.path.slice(0,-1)),i=e.path.at(-1);t&&"object"==typeof t&&!Array.isArray(t)&&Object.prototype.hasOwnProperty.call(t,i)&&(delete t[i],s=!0)}if(s){const t=e.safeParse(n);if(t.success)return t.data}break}}}function qs(e,t){let n=e;for(const e of t){if(!n||"object"!=typeof n)return;n=n[e]}return n}function Ls(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]]))}a.z.object({instanceId:Et.optional(),projectId:Et.optional(),source:Pn.optional(),nodeId:Et.optional(),runtimeId:Et.optional(),imageSelection:Wt.optional(),instanceVersion:a.z.string().trim().max(80).optional(),protocolVersion:Nt,build:fn.optional(),controlMode:a.z.enum(["standalone","controlled"]).default("controlled"),capabilities:a.z.record(a.z.string(),a.z.unknown()).default({}),appInventory:Yt.optional(),target:Ps.default({strategy:"direct-port",status:"unknown"}),workspace:Rs.default({status:"unknown"}),registrationToken:a.z.string().trim().max(240).optional(),processIncarnationId:a.z.string().trim().min(1).max(120).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==zt||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),a.z.object({status:js.shape.status.optional(),health:js.shape.health.optional(),protocolVersion:Nt,build:fn.optional(),capabilities:js.shape.capabilities.optional(),appInventory:Yt.optional(),apps:js.shape.apps.optional(),aiSessions:js.shape.aiSessions.optional(),triggers:js.shape.triggers.optional(),workspace:Rs.optional(),target:Ps.partial().optional(),processIncarnationId:a.z.string().trim().min(1).max(120).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==zt||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})});const Bs=a.z.enum(["web","telegram","wechat","dingding"]),Us=a.z.object({id:Et,channel:Bs,name:a.z.string().trim().min(1).max(160),enabled:a.z.boolean().default(!1),token:a.z.string().trim().max(4096).optional(),defaultChatId:a.z.string().trim().max(240).optional(),allowedUserIds:a.z.array(a.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:a.z.number().int().positive().max(6e4).default(3e3),settings:a.z.record(a.z.string(),a.z.unknown()).default({}),createdAt:_t,updatedAt:_t}).strict(),Fs=a.z.object({id:Et,channel:Bs,bridgeId:Et.optional(),chatSessionId:a.z.string().trim().min(1).max(240),userId:a.z.string().trim().max(240).optional(),activeProjectId:Et.optional(),activeInstanceId:Et.optional(),activeAiSessionId:a.z.string().trim().min(1).max(120).optional(),lastUsedAt:_t,createdAt:_t,updatedAt:_t}).strict(),Hs=a.z.object({source:a.z.object({type:a.z.literal("chat-gateway").default("chat-gateway"),channel:Bs,bridgeId:Et.optional(),chatSessionId:a.z.string().trim().min(1).max(240),userId:a.z.string().trim().max(240).optional()}).strict(),message:a.z.object({text:a.z.string().trim().max(2e4).default(""),attachments:a.z.array(U).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>k&&t.addIssue({code:a.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),target:a.z.object({instanceId:Et,aiSessionId:a.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),Ws=a.z.object({id:Et,instanceId:Et,projectId:Et.optional(),aiSessionId:a.z.string().trim().min(1).max(120),providerSessionId:a.z.string().trim().max(240).optional(),result:a.z.string().default(""),timeoutMs:a.z.number().positive().optional(),source:a.z.string().trim().max(120).optional(),kind:a.z.enum(["task","approval"]).default("task"),status:a.z.enum(["pending","unreachable"]).default("pending"),error:a.z.string().trim().max(2048).optional(),lastSeenAt:_t}).strict();function Gs(e){return!1!==e.ready&&(!e.runtimeVersion||["matched","failed"].includes(e.runtimeVersion.phase))}function Vs(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":e.startsWith("market.")?"market":"system"}const Ks=a.z.enum(["ai.sessions","app.sessions"]),Js=a.z.object({topic:Ks,instanceId:a.z.string().trim().min(1).max(160),streamId:a.z.string().trim().min(1).max(240),latestRevision:a.z.number().int().min(0),earliestRetainedRevision:a.z.number().int().min(0)}).strict(),Qs=a.z.object({protocolVersion:a.z.literal(1),streams:a.z.array(Js)}).strict();function Ys(e,t){n.mkdirSync(e,{recursive:!0,...void 0===t?{}:{mode:t}}),void 0!==t&&n.chmodSync(e,t)}function Xs(e,s,i={}){Ys(t.dirname(e),i.directoryMode),c.sync(e,`${JSON.stringify(s,null,2)}\n`,{encoding:"utf8",...void 0===i.fileMode?{}:{mode:i.fileMode}}),void 0!==i.fileMode&&n.chmodSync(e,i.fileMode)}function Zs(e,t){console.warn(JSON.stringify({message:e,...t}))}function ei(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function ti(e,t){const s=t.logger||Zs;let i;try{i=JSON.parse(n.readFileSync(e,"utf8"))}catch(t){return void s("stored JSON could not be read",{filePath:e,...ei(t)})}if(!t.schema)return i;const r=t.sanitize?t.sanitize(i):i,a=t.schema.safeParse(r);if(a.success)return a.data;const o="strip"in t.schema&&"function"==typeof t.schema.strip?t.schema.strip().safeParse(r):void 0;if(o?.success)return s("unknown stored fields were ignored",{filePath:e,issues:a.error.issues}),o.data;s("stored record failed validation and was isolated",{filePath:e,issues:a.error.issues})}function ni(e){return`${e}_${o.randomUUID().replace(/-/g,"").slice(0,20)}`}function si(){return o.randomBytes(24).toString("base64url")}class ii{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){if(Ys(this.directory,this.options.directoryMode),void 0!==this.options.fileMode)for(const e of n.readdirSync(this.directory).filter(e=>e.endsWith(".json")))n.chmodSync(t.join(this.directory,e),this.options.fileMode)}filePath(e){return t.join(this.directory,`${e}.json`)}list(){return this.init(),n.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const n=ti(t.join(this.directory,e),this.options);return n?[n]:[]})}get(e){this.init();const t=this.filePath(e);return n.existsSync(t)?ti(t,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 Xs(this.filePath(n.id),n,this.options),n}patch(e,t){const n=this.get(e);if(!n)return;const s={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:(new Date).toISOString()};return this.put(s)}delete(e){const t=this.filePath(e);return!!n.existsSync(t)&&(n.unlinkSync(t),!0)}}class ri{filePathValue;defaults;options;constructor(e,t,n={}){this.filePathValue=e,this.defaults=t,this.options=n}init(){Ys(t.dirname(this.filePathValue),this.options.directoryMode),n.existsSync(this.filePathValue)?void 0!==this.options.fileMode&&n.chmodSync(this.filePathValue,this.options.fileMode):this.put(this.defaults())}get(){return this.init(),n.existsSync(this.filePathValue)?ti(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 Xs(this.filePathValue,n,this.options),n}}const ai=d.promisify(o.scrypt),oi="task_handoff_cp_session",di=a.z.enum(["disabled","password"]),ci=a.z.string().trim().min(1).max(80).regex(/^[a-zA-Z0-9_.@-]+$/),li=a.z.string().min(8).max(4096),pi=a.z.object({username:ci,password:li}).strict(),ui=a.z.object({username:a.z.string().trim().min(1).max(80),password:a.z.string().min(1).max(4096)}).strict(),gi=a.z.object({id:a.z.string().trim().min(1),createdAt:a.z.string().datetime(),updatedAt:a.z.string().datetime()}),mi=gi.extend({username:ci,passwordHash:a.z.string().trim().min(1),role:a.z.enum(["viewer","operator","admin"]),lastLoginAt:a.z.string().datetime().optional()}).strict(),hi=gi.extend({userId:a.z.string().trim().min(1),tokenHash:a.z.string().trim().min(1),expiresAt:a.z.string().datetime(),lastSeenAt:a.z.string().datetime().optional()}).strict();function yi(){return(new Date).toISOString()}function fi(e){return o.createHash("sha256").update(e).digest("base64url")}async function Ii(e){const t=o.randomBytes(16).toString("base64url");return`scrypt$${t}$${(await ai(e,t,64)).toString("base64url")}`}function bi(e){return{id:e.id,username:e.username,role:e.role,createdAt:e.createdAt,updatedAt:e.updatedAt,lastLoginAt:e.lastLoginAt}}class vi{mode;users;sessions;constructor(e,t={}){this.mode=function(e={}){return di.parse(e.mode||process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"disabled")}(t),this.users=new ii(e.authUsersDir,{schema:mi}),this.sessions=new ii(e.authSessionsDir,{schema:hi})}init(){this.users.init(),this.sessions.init()}enabled(){return"password"===this.mode}state(){return{mode:this.mode,enabled:this.enabled(),requiresBootstrap:this.enabled()&&0===this.users.list().length}}async bootstrapAdmin(e){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}if(this.users.list().length>0){const e=new Error("Control Plane admin user already exists.");throw Object.assign(e,{statusCode:409,code:"AUTH_BOOTSTRAP_ALREADY_DONE"}),e}const t=pi.parse(e),n=yi();return bi(this.users.put({id:ni("user"),username:t.username,passwordHash:await Ii(t.password),role:"admin",createdAt:n,updatedAt:n}))}async login(e){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}const t=ui.parse(e),n=this.users.list().find(e=>e.username===t.username);if(!n||!await async function(e,t){const[n,s,i]=t.split("$");if("scrypt"!==n||!s||!i)return!1;const r=await ai(e,s,64),a=Buffer.from(i,"base64url");return a.length===r.length&&o.timingSafeEqual(a,r)}(t.password,n.passwordHash)){const e=new Error("Invalid username or password.");throw Object.assign(e,{statusCode:401,code:"AUTH_LOGIN_FAILED"}),e}const s=yi(),i=si(),r=new Date(Date.now()+12096e5).toISOString(),a=this.sessions.put({id:ni("sess"),userId:n.id,tokenHash:fi(i),expiresAt:r,createdAt:s,updatedAt:s,lastSeenAt:s});return this.users.put({...n,lastLoginAt:s,updatedAt:s}),{user:bi({...n,lastLoginAt:s,updatedAt:s}),sessionToken:`${a.id}.${i}`,expiresAt:r}}async userForSessionToken(e){if(!this.enabled())return;if(!e)return;const[t,n]=e.split(".");if(!t||!n)return;const s=this.sessions.get(t);if(!s||s.expiresAt<=yi()||s.tokenHash!==fi(n))return;const i=this.users.get(s.userId);return i?(this.sessions.put({...s,lastSeenAt:yi(),updatedAt:yi()}),bi(i)):void 0}async currentSession(e){const t=this.state(),n=await this.userForSessionToken(e);return{...t,authenticated:!this.enabled()||Boolean(n),user:n}}logout(e){const t=e?.split(".")[0];return t&&this.sessions.delete(t),{ok:!0}}}const wi=a.z.enum(["import","export"]),Si=a.z.object({id:a.z.string().trim().min(1).max(120),label:a.z.string().trim().min(1).max(120),directoryName:a.z.string().trim().min(1).max(120)}).strict(),Ai=a.z.object({direction:wi,programIds:a.z.array(a.z.string().trim().min(1).max(120)).min(1).max(20).refine(e=>new Set(e).size===e.length,"Config sync program ids must be unique."),workspaceFolder:a.z.string().trim().min(1).max(1e3)}).strict(),ki=a.z.object({import:a.z.string().trim().min(1).max(1e3).default("."),export:a.z.string().trim().min(1).max(1e3).default(".")}).strict(),Ci=a.z.object({programs:a.z.array(Si),preferences:ki}).strict(),xi=a.z.object({id:a.z.string().trim().min(1).max(120),type:a.z.enum(["file","dir"]),projectPath:a.z.string().trim().min(1).max(1e3),containerPath:a.z.string().trim().min(1).max(1e3),source:a.z.string().trim().min(1).max(4096),target:a.z.string().trim().min(1).max(4096),status:a.z.enum(["copied","skipped_missing_source","failed"]),error:a.z.string().trim().max(4096).optional()}).strict(),Ti=a.z.object({preset:a.z.object({id:a.z.string().trim().min(1).max(120),label:a.z.string().trim().min(1).max(120),projectRoot:a.z.string().trim().min(1).max(1e3)}).strict(),direction:wi,items:a.z.array(xi).max(100)}).strict(),zi=a.z.object({direction:wi,workspaceFolder:a.z.string().trim().min(1).max(1e3),programs:a.z.array(Ti).min(1).max(20)}).strict(),Ni="app-session.snapshot",Ei="app-session.patch",_i="app-session.removed",Pi=a.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),Oi=a.z.object({type:a.z.enum(["app-session","provider-session","adapter-key"]),id:a.z.string().trim().min(1).max(240),agent:a.z.string().trim().min(1).max(80).optional(),adapter:a.z.string().trim().min(1).max(80).optional(),key:a.z.string().trim().min(1).max(240).optional()}).strict(),Ri=a.z.record(a.z.string(),a.z.unknown()).and(a.z.object({id:a.z.string().trim().min(1).max(160),appId:a.z.string().trim().min(1).max(160).optional(),title:a.z.string().trim().max(240).optional(),kind:a.z.string().trim().max(80).optional(),status:Pi.default("unknown"),bindings:a.z.array(Oi).max(40).default([]),workspace:a.z.object({cwd:a.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:a.z.string().datetime().optional(),updatedAt:a.z.string().datetime().optional()})),ji=a.z.object({runningCount:a.z.number().int().min(0).default(0),problemCount:a.z.number().int().min(0).default(0),sessions:a.z.array(Ri).default([]),updatedAt:a.z.string().datetime()}).strict(),Di=a.z.object({streamId:a.z.string().trim().min(1).max(240),revision:a.z.number().int().min(0),lastEventAt:a.z.string().datetime(),snapshot:ji}).strict();ji.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:a.z.number().int().min(0).optional()});const Mi=a.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),$i=a.z.object({streamId:a.z.string().trim().min(1).max(240),instanceId:a.z.string().trim().min(1).max(160),nodeId:a.z.string().trim().min(1).max(160).optional(),revision:a.z.number().int().min(0),previousRevision:a.z.number().int().min(0).optional(),traceId:a.z.string().trim().min(1).max(160),generatedAt:a.z.string().datetime(),reason:Mi}).strict(),qi=a.z.object({meta:$i,snapshot:ji}).strict(),Li=a.z.object({meta:$i,session:Ri}).strict(),Bi=a.z.object({meta:$i,sessionId:a.z.string().trim().min(1).max(160),tombstone:Ri.optional(),expiresAt:a.z.string().datetime().optional()}).strict(),Ui=a.z.object({streamId:a.z.string().trim().min(1).max(240),instanceId:a.z.string().trim().min(1).max(160),sinceRevision:a.z.number().int().min(0),latestRevision:a.z.number().int().min(0),earliestRetainedRevision:a.z.number().int().min(0),syncRequired:a.z.boolean().default(!1),events:a.z.array(a.z.discriminatedUnion("type",[a.z.object({type:a.z.literal(Ni),payload:qi}).strict(),a.z.object({type:a.z.literal(Ei),payload:Li}).strict(),a.z.object({type:a.z.literal(_i),payload:Bi}).strict()])).default([])}).strict();function Fi(e,t=(new Date).toISOString()){const n=e.map(e=>Hi(e));return ji.parse({runningCount:n.filter(e=>"running"===e.status).length,problemCount:n.filter(e=>"failed"===e.status).length,sessions:n,updatedAt:t})}function Hi(e){const t=Wi("string"==typeof e.status?e.status:void 0),n="string"==typeof e.id?e.id:"",s=function(e){const t=Qi(e.workspace),n=Qi(e.tty),s=Qi(e.launch),i=Qi(e.ai),r=Qi(i.claude),a=Yi(t.cwd)||Yi(n.cwd)||Yi(r.cwd)||Yi(s.cwd);return a?{cwd:a}:void 0}(e),{workspace:i,...r}=e;return Ri.parse({...r,status:t,bindings:Ji([...(a=e.bindings,Array.isArray(a)?a.flatMap(e=>{const t=Oi.safeParse(e);return t.success?[t.data]:[]}):[]),...n?[{type:"app-session",id:n}]:[],...Vi(e)]),...s?{workspace:s}:{}});var a}function Wi(e){const t=(e||"unknown").trim().toLowerCase();return"ended"===t?"closed":"terminating"===t?"stopping":Pi.options.includes(t)?t:"unknown"}function Gi(e){return"app-session"===e.type?`app:${e.id}`:"provider-session"===e.type?`provider:${e.agent||""}:${e.id}`:`adapter:${e.adapter||""}:${e.key||e.id}`}function Vi(e){const t=Qi(e.ai),n=Qi(t.claude),s=Qi(t.appServer),i=[],r=Yi(n.short);r&&i.push({type:"adapter-key",adapter:"claude",agent:"claude",id:`short:${r}`,key:`short:${r}`});const a=Yi(n.providerSessionId);a&&i.push({type:"provider-session",agent:"claude",id:a});const o=Array.isArray(t.threadIds)?t.threadIds.filter(e=>"string"==typeof e&&Boolean(e.trim())):[],d=Yi(t.activeThreadId);for(const e of[...new Set([d,...o].filter(Boolean))])i.push({type:"provider-session",agent:"codex",id:e});const c=Yi(s.socketPath);return c&&i.push({type:"adapter-key",adapter:"codex-app-server",agent:"codex",id:`socket:${c}`,key:`socket:${c}`}),i}function Ki(e){const t="string"==typeof e.id?e.id:"",n=Qi(e.ai),s=Yi(Qi(n.claude).short);return[t?`app:${t}`:"",s?`claude-short:${s}`:""]}function Ji(e){const t=new Map;for(const n of e)t.set(Gi(n),n);return[...t.values()]}function Qi(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Yi(e){return"string"==typeof e&&e.trim()?e.trim():""}function Xi(e=(new Date).toISOString()){return ji.parse({runningCount:0,problemCount:0,sessions:[],updatedAt:e})}function Zi(e,t,n,s){const i=s&&"object"==typeof s?s:{};return{nodeId:e.id,route:"string"==typeof i.route?i.route:t,method:n,code:"string"==typeof i.code?i.code:"NODE_AGENT_REQUEST_FAILED",message:s instanceof Error?s.message:String(s),statusCode:"number"==typeof i.statusCode?i.statusCode:void 0,issues:Array.isArray(i.issues)?i.issues:void 0}}class er{requestImpl;logger;constructor(e){this.requestImpl=e.request,this.logger=e.logger}async request(e,t,n={}){const s=n.method||"GET";let i,r;try{i=await this.requestImpl(e,t,n)}catch(n){throw this.logger?.warn?.({nodeId:e.id,route:t,method:s,error:n instanceof Error?n.message:String(n),errorCode:"NODE_AGENT_TRANSPORT_FAILED"},"node agent transport failed"),n}try{r=await i.json()}catch(n){this.logger?.warn?.({nodeId:e.id,route:t,method:s,statusCode:i.status,error:n instanceof Error?n.message:String(n),errorCode:"NODE_AGENT_RESPONSE_INVALID_JSON"},"node agent response was not valid json");const r=new Error(`Node agent ${e.id} returned invalid JSON for ${t}.`);throw Object.assign(r,{statusCode:502,code:"NODE_AGENT_RESPONSE_INVALID_JSON",nodeId:e.id,route:t}),r}if(!i.ok){const n=new Error(r.error?.message||`Node agent request failed with HTTP ${i.status}`);throw Object.assign(n,{statusCode:i.status,code:r.error?.code||"NODE_AGENT_REQUEST_FAILED",nodeId:e.id,route:t}),this.logger?.warn?.({nodeId:e.id,route:t,method:s,statusCode:i.status,errorCode:r.error?.code||"NODE_AGENT_REQUEST_FAILED",error:n.message},"node agent request failed"),n}return this.logger?.info?.({nodeId:e.id,route:t,method:s,statusCode:i.status},"node agent request ok"),r.data??r}async requestSchema(e,t,n,s={}){return this.parse(e,t,n,await this.request(e,t,s),s.method||"GET")}parse(e,t,n,s,i="GET"){const r=n.safeParse(s);if(r.success)return r.data;this.logger?.warn?.({nodeId:e.id,route:t,method:i,errorCode:"NODE_AGENT_PROTOCOL_INVALID",issues:r.error.issues},"node agent protocol response did not match schema");const a=new Error(`Node agent ${e.id} returned invalid protocol data for ${t}.`);throw Object.assign(a,{statusCode:502,code:"NODE_AGENT_PROTOCOL_INVALID",nodeId:e.id,route:t,issues:r.error.issues}),a}}class tr{client;constructor(e){this.client=e}request(e,t,n={}){return this.client.request(e,t,n)}health(e){return this.client.requestSchema(e,"/health",ws)}checkUpdate(e,t){return this.client.requestSchema(e,"/updates/check",Cn,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}applyUpdate(e,t){return this.client.requestSchema(e,"/updates/apply",xn,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listUpdateJobs(e){return this.client.requestSchema(e,"/updates/jobs",a.z.array(xn))}getExternalListener(e){return this.client.requestSchema(e,"/settings/external-listener",Ss)}updateExternalListener(e,t){return this.client.requestSchema(e,"/settings/external-listener",Ss,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}createPairingInvite(e,t={}){return this.client.requestSchema(e,"/pairing/invites",As,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t&&"object"==typeof t?t:{})})}createControlPlaneConnection(e,t){return this.client.requestSchema(e,"/control-plane-connections",xs,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listControlPlanePairings(e){return this.client.requestSchema(e,"/control-plane-pairings",a.z.array(ks))}deleteControlPlanePairing(e,t){return this.client.requestSchema(e,`/control-plane-pairings/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}listControlPlaneConnections(e){return this.client.requestSchema(e,"/control-plane-connections",a.z.array(Cs))}deleteControlPlaneConnection(e,t){return this.client.requestSchema(e,`/control-plane-connections/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}listDockerImages(e){return this.client.requestSchema(e,"/docker/images",a.z.array(zs))}listFolderTree(e,t={}){const n=new URLSearchParams;t.path&&n.set("path",t.path),void 0!==t.depth&&n.set("depth",String(t.depth));const s=n.toString();return this.client.requestSchema(e,"/folders/tree"+(s?`?${s}`:""),a.z.array(Es))}listLocalFolders(e,t={}){return this.client.requestSchema(e,"/local-folders",a.z.array(_n),t)}createLocalFolder(e,t){return this.client.requestSchema(e,"/local-folders",_n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}async requireLocalFolder(e,t){const n=(await this.listLocalFolders(e)).find(e=>e.id===t);if(!n){const n=new Error(`Local folder ${t} was not found on node ${e.id}.`);throw Object.assign(n,{statusCode:404,code:"NODE_LOCAL_FOLDER_NOT_FOUND",nodeId:e.id,route:"/local-folders"}),n}return n}listRuntimes(e,t={}){return this.client.requestSchema(e,"/runtimes",a.z.array(Is),t)}createRuntime(e,t){return this.client.requestSchema(e,"/runtimes",Is,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateRuntime(e,t,n){const s=`/runtimes/${encodeURIComponent(t)}`;return this.client.requestSchema(e,s,Is,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}checkRuntime(e,t){const n=`/runtimes/${encodeURIComponent(t)}/check`;return this.client.requestSchema(e,n,Is,{method:"POST"})}deleteRuntime(e,t){return this.client.requestSchema(e,`/runtimes/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}deleteLocalFolder(e,t){return this.client.requestSchema(e,`/local-folders/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}listModels(e,t={}){return this.client.requestSchema(e,"/models",a.z.array($n),t)}createModel(e,t){return this.client.requestSchema(e,"/models",$n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}deployModel(e,t,n){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}/deploy`,$n,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}updateModel(e,t,n){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,$n,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}deleteModel(e,t){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}assignInstanceModels(e,t,n){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/model-assignment`,a.z.object({assignment:qn,instance:js}).strict(),{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}async listFleetModels(e,t={}){return(await Promise.allSettled(e.map(async e=>({node:e,models:await this.listModels(e,t)})))).reduce((t,n,s)=>{if("fulfilled"===n.status)t.items.push(...n.value.models.map(e=>({nodeId:n.value.node.id,model:e})));else{const i=e[s];i&&t.nodeErrors.push(Zi(i,"/models","GET",n.reason))}return t},{items:[],nodeErrors:[]})}async listFleetRuntimes(e,t={}){return(await Promise.allSettled(e.map(async e=>({node:e,runtimes:await this.listRuntimes(e,t)})))).reduce((t,n,s)=>{if("fulfilled"===n.status)t.items.push(...n.value.runtimes);else{const i=e[s];i&&t.nodeErrors.push(Zi(i,"/runtimes","GET",n.reason))}return t},{items:[],nodeErrors:[]})}listInstances(e,t={}){return this.listInstancesWithDiagnostics(e,t).then(e=>e.items)}async listInstancesWithDiagnostics(e,t={}){return this.client.requestSchema(e,"/instances",a.z.array(a.z.unknown()),t).then(t=>t.reduce((t,n)=>{const s=this.normalizeInstance(e,n);return s.instance&&t.items.push(s.instance),s.error&&t.nodeErrors.push(s.error),t},{items:[],nodeErrors:[]}))}createInstance(e,t){return this.client.requestSchema(e,"/instances",js,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateInstance(e,t,n){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}`,js,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}deleteInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/delete`,Ts,{method:"POST"})}startInstance(e,t,n={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/start`,js,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}retryInstanceImageProvisioning(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/image-provisioning/retry`,js,{method:"POST"})}stopInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/stop`,js,{method:"POST"})}restartInstance(e,t,n={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/restart`,js,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}instanceResourceMetrics(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/metrics`,bs)}proxyRawInstance(e,t,n){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/proxy/raw`,_s,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}async listFleetInstances(e,t={}){return(await Promise.allSettled(e.map(async e=>({node:e,result:await this.listInstancesWithDiagnostics(e,t)})))).reduce((t,n,s)=>("fulfilled"===n.status?(t.items.push(...n.value.result.items),t.nodeErrors.push(...n.value.result.nodeErrors)):t.nodeErrors.push(Zi(e[s],"/instances","GET",n.reason)),t),{items:[],nodeErrors:[]})}normalizeInstance(e,t){const n=(s=t,js.safeParse(function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=new Set(Object.keys(js.shape)),s={};for(const[e,i]of Object.entries(t))n.has(e)&&(s[e]=i);if(t.capabilities&&"object"==typeof t.capabilities&&!Array.isArray(t.capabilities)){const e={...t.capabilities};Object.prototype.hasOwnProperty.call(e,"apps")&&delete e.apps,s.capabilities=e}else s.capabilities={};s.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["observedAt","items","issues"].includes(e));const n={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).some(e=>!["id","name","kind","source","availability","capabilities","diagnosticCode"].includes(e));const t=Ls(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Ls(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Ls(e,["code","message"])):t.issues},s=Yt.safeParse(n);return s.success?s.data:void 0}(t.appInventory,"string"==typeof t.id&&t.id),s.source=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=t.type,s=Pn.options.find(e=>e.shape.type.value===n);if(!s)return e;const i="local-folder"===n?["type","localFolderId","path","ownerNodeId"]:"git-template"===n?["type","url","templateId","ref","auth","clone"]:["type","repositoryId","url","provider","ref","auth","clone"],r=Ls(t,i);if("local-folder"!==n){const e=$s(zn,Ls(t.ref,["type","name","commit"])),n=$s(Nn,Ls(t.auth,["type","secretId"])),s=$s(En,Ls(t.clone,["depth","submodules","lfs","subdirectory"]));e?r.ref=e:delete r.ref,n?r.auth=n:delete r.auth,s?r.clone=s:delete r.clone}const a=$s(s,r);return a?(Object.keys(t).some(e=>!i.includes(e)),a):r}(t.source,"string"==typeof t.id&&t.id),s.sourceSnapshot=t.sourceSnapshot&&"object"==typeof t.sourceSnapshot&&!Array.isArray(t.sourceSnapshot)?t.sourceSnapshot:{},s.build=$s(fn,t.build,0,0,"string"==typeof t.id&&t.id),s.aiSessions=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n=Array.isArray(t.sessions)?t.sessions.flatMap((e,t)=>{const n=$s(ve,e);return n?[n]:[]}):[],s={runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,waitingCount:"number"==typeof t.waitingCount&&Number.isInteger(t.waitingCount)&&t.waitingCount>=0?t.waitingCount:0,staleCount:"number"==typeof t.staleCount&&Number.isInteger(t.staleCount)&&t.staleCount>=0?t.staleCount:0,sessions:n,updatedAt:_t.safeParse(t.updatedAt).success?t.updatedAt:(new Date).toISOString()};return Object.keys(t).some(e=>!["runningCount","waitingCount","staleCount","sessions","updatedAt"].includes(e)),we.parse(s)}(t.aiSessions,"string"==typeof t.id&&t.id),s.triggers=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["enabledCount","runningCount","errorCount","configs","recentRuns"].includes(e));const n=(e,t,n)=>Array.isArray(e)?e.flatMap((e,n)=>{const s=$s(t,e);return s?[s]:[]}):[],s=Array.isArray(t.configs)?t.configs.flatMap((e,t)=>{if(!e||"object"!=typeof e||Array.isArray(e))return[];const s=e;Object.keys(s).some(e=>!["configHash","config","deployments","runtime"].includes(e));const i=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n=Ls(t,["configHash","name","description","createdAt","updatedAt"]);if(t.source&&"object"==typeof t.source&&!Array.isArray(t.source)){const e=t.source,s="schedule"===e.type?"interval"===e.scheduleKind?["type","scheduleKind","intervalMs"]:"daily"===e.scheduleKind?["type","scheduleKind","timeOfDay","timezone"]:["type","scheduleKind","weekdays","timeOfDay","timezone"]:"file-change"===e.type?["type","roots","globs","ignore","debounceMs"]:["type","agent","statuses","phases"];n.source=Ls(e,s)}return n.action=Ls(t.action,["promptTemplate"]),n.policy=Ls(t.policy,["cooldownMs","maxConcurrentRuns","whenBusy"]),$s(qe,n)}(s.config);return"string"==typeof s.configHash&&i?[{configHash:s.configHash,config:i,deployments:n(s.deployments,Be),runtime:n(s.runtime,Ue)}]:[]}):[];return{enabledCount:"number"==typeof t.enabledCount&&Number.isInteger(t.enabledCount)&&t.enabledCount>=0?t.enabledCount:0,runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,errorCount:"number"==typeof t.errorCount&&Number.isInteger(t.errorCount)&&t.errorCount>=0?t.errorCount:0,configs:s,recentRuns:n(t.recentRuns,Fe)}}(t.triggers,"string"==typeof t.id&&t.id),s.apps=$s(js.shape.apps.unwrap(),Ls(t.apps,["runningCount","problemCount","updatedAt","revision"]),0,0,"string"==typeof t.id&&t.id)||{runningCount:0,problemCount:0},s.config=$s(js.shape.config.unwrap(),Ls(t.config,["autoImportAgentConfigs","defaultCodexPermissionMode"]),0,0,"string"==typeof t.id&&t.id)||{autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"},s.modelSelection=$s(Ht.unwrap(),Ls(t.modelSelection,["codexModelHash","claudeModelHash"]),0,0,"string"==typeof t.id&&t.id)||{},s.imageSnapshot=function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=ms(n),{reference:i,...r}=s,a=n.requestedReference??i;let o;try{o="string"==typeof a?Bt(a):void 0}catch{}const d={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"},c="string"==typeof n.id?n.id:"string"==typeof t?t:void 0,l="market"===n.origin||c&&d[c]?"market":"custom";return void 0!==n.image||n.reference,{...r,id:c&&d[c]?d[c]:c,origin:l,repository:o?.repository??n.repository,tag:o?.tag??n.tag,requestedReference:o?.reference??a,resolvedDigest:n.resolvedDigest,resolvedReference:n.resolvedReference,downloadSizeBytes:n.downloadSizeBytes,market:"market"===l?Ls(n.market,["catalogId","catalogRevision","publisher","version"])??{catalogId:"task_handoff_embedded",catalogRevision:"legacy",publisher:"task-handoff"}:void 0}}(t.imageSnapshot,t.imageId,"string"==typeof t.id&&t.id);const i="string"==typeof t.imageId?{img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"}[t.imageId]||t.imageId:void 0,r=Ls(t.imageSelection,["imageId","tag"]);s.imageSelection=r||(i?{imageId:i,tag:s.imageSnapshot?.tag}:void 0),s.imageProvisioning=$s(os,Ls(t.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),0,0,"string"==typeof t.id&&t.id),s.runtimeVersion=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n=e=>{if("string"!=typeof e)return;const t=e.trim();return t&&t.length<=80?t:void 0},s=n(t.desiredVersion);if(!s)return;const i=new Set(wn.shape.phase.options),r="string"==typeof t.phase?t.phase:"pending",a=i.has(r)?r:{idle:"pending",queued:"pending",checking:"pending",downloading:"installing",updating:"installing",completed:"matched",succeeded:"matched",ready:"matched",error:"failed"}[r]||"pending";let o;if(t.error&&"object"==typeof t.error&&!Array.isArray(t.error)){const e=t.error,s="string"==typeof e.message?e.message.trim().slice(0,4096):"";if(s){const t=new Set(vn.shape.code.options),i="string"==typeof e.code&&t.has(e.code)?e.code:"INSTANCE_RUNTIME_INSTALL_FAILED";e.code;const r=n(e.expectedVersion),a=n(e.actualVersion);o={code:i,message:s,...r?{expectedVersion:r}:{},...a?{actualVersion:a}:{},retryable:!0===e.retryable}}}"string"==typeof t.error&&t.error.trim()&&(o={code:"INSTANCE_RUNTIME_INSTALL_FAILED",message:t.error.trim().slice(0,4096),expectedVersion:s,...n(t.actualVersion)?{actualVersion:n(t.actualVersion)}:{},retryable:!1});const d=n(t.actualVersion),c=_t.safeParse(t.lastAttemptAt),l=_t.safeParse(t.matchedAt);return void 0!==t.lastAttemptAt&&c.success,void 0!==t.matchedAt&&l.success,{desiredVersion:s,...d?{actualVersion:d}:{},phase:a,attempt:"number"==typeof t.attempt&&Number.isInteger(t.attempt)&&t.attempt>=0?t.attempt:0,...c.success?{lastAttemptAt:c.data}:{},...l.success?{matchedAt:l.data}:{},...o?{error:o}:{}}}(t.runtimeVersion,"string"==typeof t.id&&t.id),s.workspace=$s(Rs,t.workspace,0,0,"string"==typeof t.id&&t.id)||{status:"unknown"},s.runtime=$s(js.shape.runtime.unwrap(),Ls(t.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),0,0,"string"==typeof t.id&&t.id)||{labels:{}},s.target=$s(Ps,Ls(t.target??t.endpoints,["strategy","web","api","vnc","tty","logs","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"direct-port",status:"unknown"},s.access=$s(Os,Ls(t.access,["strategy","web","api","ws","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"control-plane-proxy",status:"unknown"};for(const[e,t]of Object.entries(js.shape))Object.prototype.hasOwnProperty.call(s,e)&&!t.safeParse(s[e]).success&&t.safeParse(void 0).success&&delete s[e];return s}(s)));var s;if(n.success)return n.data.protocolVersion&&n.data.protocolVersion!==zt?(this.client.logger?.warn?.({nodeId:e.id,instanceId:n.data.id,expectedProtocolVersion:zt,actualProtocolVersion:n.data.protocolVersion,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node instance protocol version mismatch"),{instance:n.data}):{instance:n.data};const i=t&&"object"==typeof t?t:{},r="string"==typeof i.id?i.id:void 0;return this.client.logger?.warn?.({nodeId:e.id,instanceId:r,errorCode:"NODE_INSTANCE_PAYLOAD_INVALID",issues:n.error.issues.map(e=>({path:e.path.join("."),message:e.message}))},"node instance payload invalid"),{error:{nodeId:e.id,route:"/instances",method:"GET",code:"NODE_INSTANCE_PAYLOAD_INVALID",message:r?`Node instance ${r} payload is invalid.`:"Node instance payload is invalid.",statusCode:502,issues:n.error.issues}}}}function nr(e){return e.readyState===(e.OPEN??1)}function sr(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}function ir(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}function rr(e,t,n){e.close(ir(t),sr(n)||void 0)}function ar(e,t,n,s){if(nr(e))for(const e of t.splice(0))if(s(e),!n(e))return}function or(e,t,n={}){const s=n.pendingFrameLimit??256,i=n.maxFrameBytes??8388608,r=n.maxTotalBytes??1073741824,a=n.maxBufferedBytes??16777216;let o=!1,d=nr(t);const c=[],l=[];let p,u=0,g=0,m=0;const h=e=>Buffer.isBuffer(e)||e instanceof ArrayBuffer||ArrayBuffer.isView(e)?e.byteLength:Buffer.byteLength("string"==typeof e?e:String(e),"utf8"),y=(s,a)=>{const o=h(a);return m+=o,n.onFrame?.(s,o),!(o>i||m>r)||(rr(e,1009,"WebSocket bridge traffic limit exceeded."),rr(t,1009,"WebSocket bridge traffic limit exceeded."),!1)},f=()=>{p&&(clearTimeout(p),p=void 0)},I=(n,s)=>{const i=s.bytes;if((n.bufferedAmount??0)+i>a)return rr(e,1013,"WebSocket bridge consumer is too slow."),rr(t,1013,"WebSocket bridge consumer is too slow."),!1;try{return function(e,t){e.send(t.data,{binary:t.isBinary})}(n,s),!0}catch{return rr(e,1011,"WebSocket bridge send failed."),rr(t,1011,"WebSocket bridge send failed."),!1}},b=(n,i,r)=>{if(i.length>=s)return rr(e,1011,"WebSocket bridge pending frame limit exceeded."),void rr(t,1011,"WebSocket bridge pending frame limit exceeded.");const o="upstream"===n?u:g;if((("upstream"===n?t:e).bufferedAmount??0)+o+r.bytes>a)return rr(e,1013,"WebSocket bridge consumer is too slow."),void rr(t,1013,"WebSocket bridge consumer is too slow.");i.push(r),"upstream"===n?u+=r.bytes:g+=r.bytes};return n.upstreamOpenTimeoutMs&&!d&&(p=setTimeout(()=>{d||o||(rr(e,1011,"Upstream websocket did not open."),rr(t))},n.upstreamOpenTimeoutMs)),e.on("open",()=>ar(e,l,t=>I(e,t),e=>{g-=e.bytes})),t.on("open",()=>{f(),d=!0,n.onUpstreamOpen?.(),ar(t,c,e=>I(t,e),e=>{u-=e.bytes})}),e.on("message",(e,n=!1)=>{if(!y("client-to-upstream",e))return;const s={data:e,isBinary:Boolean(n),bytes:h(e)};nr(t)?I(t,s):b("upstream",c,s)}),t.on("message",(t,n=!1)=>{if(!y("upstream-to-client",t))return;const s={data:t,isBinary:Boolean(n),bytes:h(t)};nr(e)?I(e,s):b("client",l,s)}),t.on("close",(t,s)=>{f(),n.onUpstreamClose?.(t,s),(d||o||!n.onUpstreamCloseBeforeOpen?.())&&rr(e,t,s)}),t.on("error",t=>{n.onUpstreamError?.(t),(d||o||!n.onUpstreamErrorBeforeOpen?.(t))&&rr(e)}),e.on("close",(e,s)=>{f(),o=!0,n.onClientClose?.(e,s),rr(t,e,s)}),e.on("error",e=>{f(),o=!0,n.onClientError?.(e),rr(t,1011,"WebSocket bridge client failed.")}),{close:(n,s)=>{f(),rr(t,n,s),rr(e,n,s)}}}const dr="TASK_HANDOFF_NODE_AGENT_V1",cr=6e4,lr="/api/node-tunnel";function pr(e,t){if("paired-hmac"===e.auth.mode&&e.auth.secret){if(!e.auth.keyId){const t=new Error(`Node ${e.id} paired-HMAC auth is missing keyId.`);throw Object.assign(t,{statusCode:500,code:"NODE_AGENT_REMOTE_KEY_ID_MISSING"}),t}return function(e){const t=e.timestamp||(new Date).toISOString(),n=e.nonce||o.randomBytes(18).toString("base64url"),s=ur(e.body||""),i=gr(e.secret,{keyId:e.keyId,method:e.method,pathWithQuery:e.pathWithQuery,timestamp:t,nonce:n,bodySha256:s});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":s,"x-taskhandoff-signature":i}}({nodeId:e.id,keyId:e.auth.keyId,secret:e.auth.secret,method:t.method,pathWithQuery:t.pathWithQuery,body:t.body})}const n="local-ipc"!==e.connectionMode&&"local-loopback"!==e.connectionMode||"local-static-key"!==e.auth.mode?void 0:e.auth.secret;return n?{authorization:`Bearer ${n}`}:{}}function ur(e=""){return o.createHash("sha256").update(e).digest("hex")}function gr(e,t){return o.createHmac("sha256",e).update(function(e){return[dr,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function mr(e){if(e.startsWith("ipc://"))return decodeURIComponent(e.slice(6))}function hr(e,t=process.platform){if(!function(e,t=process.platform){if("win32"===t)return e.startsWith("\\\\.\\pipe\\");const s=n.statSync(e),i=process.getuid?.();return void 0===i||s.uid===i}(e,t)){const e=new Error("Local node agent IPC socket is not owned by the current user.");throw Object.assign(e,{statusCode:403,code:"NODE_AGENT_IPC_OWNER_INVALID"}),e}}function yr(e,t,n,s={}){const i=`/api/node-agent${t}`,r={createConnection:()=>u.connect(e),headers:{host:"task-handoff-node-agent.local",...s}};return n&&(Array.isArray(n)?n.length>0:n.trim().length>0)?new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,n,r):new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,r)}async function fr(e,t,n,s={}){const i=mr(t);return i?(hr(i),async function(e,t,n={}){const s=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),r=new Headers(n.headers||{});return void 0===i||r.has("content-length")||r.set("content-length",String(Buffer.byteLength(i))),new Promise((a,o)=>{const d=p.request({socketPath:e,method:s,path:`/api/node-agent${t}`,headers:Object.fromEntries(r.entries())},e=>{const t=e.statusCode||500;a(new Response("HEAD"!==s&&204!==t&&205!==t&&304!==t?g.Readable.toWeb(e):null,{status:t,headers:e.headers}))});d.on("error",o);const c=()=>d.destroy(new DOMException("The operation was aborted.","AbortError"));n.signal?.aborted?c():n.signal?.addEventListener("abort",c,{once:!0}),d.once("close",()=>n.signal?.removeEventListener("abort",c)),void 0!==i&&d.write(i),d.end()})}(i,n,s)):e(`${t.replace(/\/$/,"")}/api/node-agent${n}`,s)}function Ir(e){const t=e.controlEndpoint||e.endpoint;if(!t){const e=new Error("Node agent direct HTTP mode requires an endpoint.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"}),e}return t}function br(e,t,n,s){const i=Ir(e),r=mr(i),a=`/api/node-agent${t}`;if(r)return hr(r),yr(r,t,n,s);const o=new URL(a,i.replace(/\/$/,""));return o.protocol="https:"===o.protocol?"wss:":"ws:",n?new l.WebSocket(o.toString(),n,{headers:s}):new l.WebSocket(o.toString(),{headers:s})}function vr(e,t,n){const s=new Headers(n),i=pr(e,t);for(const[e,t]of Object.entries(i))"authorization"===e&&s.has(e)||s.set(e,t);return Object.fromEntries(s.entries())}function wr(e=fetch,t={}){const n=t.openWebSocket||br,s=async(t,n,s={})=>{const i=Ir(t),r=s.method||"GET",a="string"==typeof s.body||s.body instanceof Buffer?s.body:void 0===s.body||null===s.body?void 0:String(s.body),o=vr(t,{method:r,pathWithQuery:`/api/node-agent${n}`,body:a||""},s.headers);return fr(e,i,n,{...s,body:a,headers:o})};return{request:s,requestStream:s,proxyWebSocket(e,t,s,i,r={}){const a=vr(e,{method:"GET",pathWithQuery:`/api/node-agent${s}`},r);or(t,n(e,s,i,a),{onUpstreamError:()=>t.close(1011,"Instance websocket proxy failed."),onUpstreamErrorBeforeOpen:()=>!0})}}}class Sr{direct;tunnel;proxy;constructor(e){this.direct=e.direct,this.tunnel=e.tunnel,this.proxy=e.proxy}setTunnel(e){this.tunnel=e}setProxy(e){this.proxy=e}resolve(e){if(!1===e.connectionEnabled){const e=new Error("Node agent connection is disabled locally.");throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_CONNECTION_DISABLED"}),e}if("control-plane-proxy"===e.connectionMode){if(!this.proxy){const e=new Error("Control-plane proxy transport is not available.");throw Object.assign(e,{statusCode:503,code:"CONTROL_PLANE_PROXY_TRANSPORT_UNAVAILABLE"}),e}return this.proxy}if("reverse-wss"!==e.connectionMode)return this.direct;if(!this.tunnel){const e=new Error("Reverse node agent transport is not available.");throw Object.assign(e,{statusCode:503,code:"NODE_AGENT_REVERSE_TRANSPORT_UNAVAILABLE"}),e}return this.tunnel}}function Ar(e,t,n,s){const i=new Error(e,void 0===s?void 0:{cause:s});return Object.assign(i,{code:t,statusCode:n,retryable:n>=500}),i}function kr(e,t){if("control-plane-proxy"!==e.connectionPath.kind)throw Ar("Control-plane proxy transport requires a proxy connection path.","CONTROL_PLANE_PROXY_CONFIGURATION_INVALID",400);const n=t(e);if(!n)throw Ar("Control-plane proxy credential is missing.","CONTROL_PLANE_PROXY_CREDENTIAL_REQUIRED",503);if(n.nodeId!==e.id||n.proxyBindingId!==e.connectionPath.proxyBindingId||n.targetNodeId!==e.connectionPath.targetNodeId)throw Ar("Control-plane proxy credential does not match the node connection identity.","CONTROL_PLANE_PROXY_CREDENTIAL_MISMATCH",409);return n}function Cr(e,t){const n=new Headers(t);n.set(nt,Ge),n.set(st,e.sourceControlPlaneId),n.set(it,e.bindingKeyId),n.set(rt,e.credential);const s=Ye.safeParse(n.get("x-request-id"));return n.set("x-request-id",s.success?s.data:`proxy_request_${o.randomUUID().replace(/-/g,"")}`),Object.fromEntries(n.entries())}function xr(e){const t=Ze.safeParse(e);if(t.success)return t.data;const n=Ar("Control-plane proxy route is invalid.",at.RouteInvalid,400);throw Object.assign(n,{issues:t.error.issues}),n}function Tr(e,t,n){return t?new l.WebSocket(e,t,{headers:n}):new l.WebSocket(e,{headers:n})}class zr{credentialForNode;fetchImpl;openWebSocket;constructor(e){this.credentialForNode=e.credentialForNode,this.fetchImpl=e.fetchImpl||fetch,this.openWebSocket=e.openWebSocket||Tr}async fetch(e,t,n={}){const s=kr(e,this.credentialForNode),i=function(e,t){const n=new URL(xr(t),"https://node-agent.invalid"),s=new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/http${n.pathname}`,e.proxyOrigin);return s.search=n.search,s}(s,t);try{return await this.fetchImpl(i,{...n,headers:Cr(s,n.headers)})}catch(e){if(function(e){return e instanceof Error&&("AbortError"===e.name||"ABORT_ERR"===e.code)}(e))throw e;throw Ar(`Trusted control-plane proxy ${s.proxyOrigin} is unavailable.`,"CONTROL_PLANE_PROXY_UNAVAILABLE",503,e)}}request(e,t,n={}){return this.fetch(e,t,n)}requestStream(e,t,n={}){return this.fetch(e,t,n)}proxyWebSocket(e,t,n,s,i={}){const r=kr(e,this.credentialForNode),a=this.openWebSocket(function(e,t){const n=new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/websocket`,e.proxyOrigin);return n.protocol="wss:",n.searchParams.set("route",xr(t)),n}(r,n).toString(),s,Cr(r,i));or(t,a,{onUpstreamError:()=>t.close(1011,"Control-plane proxy websocket failed."),onUpstreamErrorBeforeOpen:()=>!0})}}const Nr=new Set(Object.keys(xt.shape)),Er=new Set(Object.keys(Tt.shape));function _r(e,t,n){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,i=Object.keys(s).filter(e=>!t.has(e));return i.length&&n?.(i),Object.fromEntries([...t].filter(e=>e in s).map(e=>[e,s[e]]))}function Pr(e){const{credential:t,...n}=e;return n}class Or{pendingClaims;nodeCredentials;identity;constructor(e,t){this.pendingClaims=new ii(e.pendingClaimsDir,{schema:xt,sanitize:e=>function(e){return _r(e,Nr,e=>t?.("unknown stored pending proxy claim fields were ignored",{fields:e}))}(e),logger:t,directoryMode:448,fileMode:384}),this.nodeCredentials=new ii(e.nodeCredentialsDir,{schema:Tt,sanitize:e=>function(e){return _r(e,Er,e=>t?.("unknown stored proxy node credential fields were ignored",{fields:e}))}(e),logger:t,directoryMode:448,fileMode:384}),this.identity=new ri(e.identityPath,()=>({controlPlaneId:ni("control_plane")}),{schema:a.z.object({controlPlaneId:a.z.string().trim().min(1).max(160)}).strip(),logger:t,directoryMode:448,fileMode:384})}init(e=Date.now()){return this.pendingClaims.init(),this.nodeCredentials.init(),this.identity.init(),this.gcPendingClaims(e)}controlPlaneId(){return this.identity.get().controlPlaneId}pendingClaimByClaimId(e){return this.pendingClaims.list().find(t=>t.claimId===e)}putPendingClaim(e){const t=this.pendingClaimByClaimId(e.claimId);if(!t)return this.pendingClaims.put(e);if(t.proxyOrigin!==e.proxyOrigin||t.requestedName!==e.requestedName||t.sourceControlPlaneId!==e.sourceControlPlaneId||t.targetNodeId!==e.targetNodeId||t.bindingKeyId!==e.bindingKeyId||t.credential!==e.credential){const e=new Error("Pending proxy claim identity conflicts with an existing claim.");throw Object.assign(e,{code:"CONTROL_PLANE_PROXY_BINDING_IDENTITY_CONFLICT",statusCode:409}),e}return t}publicPendingClaims(){return this.pendingClaims.list().map(Pr)}nodeCredential(e){return this.nodeCredentials.list().find(t=>t.nodeId===e)}putNodeCredential(e){const t=this.nodeCredential(e.nodeId);if(t&&(t.proxyOrigin!==e.proxyOrigin||t.proxyBindingId!==e.proxyBindingId||t.targetNodeId!==e.targetNodeId||t.sourceControlPlaneId!==e.sourceControlPlaneId||t.bindingKeyId!==e.bindingKeyId||t.credential!==e.credential)){const e=new Error("Proxy node credential conflicts with the existing node identity.");throw Object.assign(e,{code:"CONTROL_PLANE_PROXY_NODE_IDENTITY_CONFLICT",statusCode:409}),e}return t||this.nodeCredentials.put(e)}deleteNodeCredential(e){const t=this.nodeCredential(e);return!!t&&this.nodeCredentials.delete(t.id)}gcNodeCredentials(e){const t=[];for(const n of this.nodeCredentials.list())e(n)||(this.nodeCredentials.delete(n.id),t.push(n.id));return t}promotePendingClaim(e,t){const n=this.pendingClaimByClaimId(e);if(!n)return;if(n.proxyOrigin!==t.proxyOrigin||n.sourceControlPlaneId!==t.sourceControlPlaneId||void 0!==n.targetNodeId&&n.targetNodeId!==t.targetNodeId||n.bindingKeyId!==t.bindingKeyId||n.credential!==t.credential){const e=new Error("Proxy node credential does not match the pending claim identity.");throw Object.assign(e,{code:"CONTROL_PLANE_PROXY_BINDING_IDENTITY_CONFLICT",statusCode:409}),e}const s=this.putNodeCredential(t);return this.pendingClaims.delete(n.id),s}markCompensationRequired(e){const t=this.pendingClaimByClaimId(e);if(t)return this.pendingClaims.patch(t.id,{status:"compensation-required"})}cancelPendingClaim(e,t){const n=this.pendingClaimByClaimId(e);return n?t?(this.pendingClaims.patch(n.id,{status:"compensation-required"}),{deleted:!1,compensationRequired:!0}):{deleted:this.pendingClaims.delete(n.id),compensationRequired:!1}:{deleted:!1,compensationRequired:!1}}completePendingClaimCompensation(e){const t=this.pendingClaimByClaimId(e);return!(!t||"compensation-required"!==t.status)&&this.pendingClaims.delete(t.id)}gcPendingClaims(e=Date.now()){const t=[];for(const n of this.pendingClaims.list()){if("compensation-required"===n.status)continue;const s=Date.parse(n.expiresAt);!Number.isFinite(s)||s>e||(this.pendingClaims.delete(n.id),t.push(n.id))}return t}}function Rr(){return(new Date).toISOString()}function jr(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}function Dr(e){const t=String(e||"").split(":");if(3===t.length&&"ai"===t[1]&&t[0]&&t[2])return{instanceId:t[0],aiSessionId:t[2]}}function Mr(e){return e instanceof Error?e.message:String(e)}function $r(e,t){const n=new Error(t);throw Object.assign(n,{statusCode:404,code:e}),n}class qr{requireNode;nodeAgentTransport;constructor(e){this.requireNode=e.requireNode,this.nodeAgentTransport=e.nodeAgentTransport}async request(e,t,n={}){if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}Lr(e);const s=this.requireNode(e.nodeId),i=await this.nodeAgentTransport(s).request(s,`/instances/${encodeURIComponent(e.id)}/proxy`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:`/api/${t.replace(/^\/+/,"")}`,method:n.method||"GET",headers:jr(n.headers),body:(r=n.body,"string"==typeof r?r:null==r?void 0:String(r))})});var r;const a=await i.json().catch(()=>({}));if(!i.ok){const n=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n="string"==typeof t.code&&t.code.trim()?t.code:void 0,s="string"==typeof t.message&&t.message?t.message:void 0,i=t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?t.details:void 0;return n||s?{code:n,message:s,details:i}:void 0}(a.error),s=n?.message||function(e){if(!e||"object"!=typeof e||Array.isArray(e))return"";const t=e.message;return"string"==typeof t?t:""}(a.error)||`Instance request failed with HTTP ${i.status}`,r=new Error(s);throw Object.assign(r,{statusCode:i.status,code:n?.code||"INSTANCE_REQUEST_FAILED",...n?.details?{details:n.details}:{},instanceId:e.id,nodeId:e.nodeId,route:t}),r}return a.data??a}async proxyHttp(e,t,n={}){if("online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}if(!e.target.web){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}Lr(e);const s=this.requireNode(e.nodeId),i=await this.nodeAgentTransport(s).requestStream(s,`/instances/${encodeURIComponent(e.id)}/proxy/stream`,{method:"POST",headers:{"content-type":"application/json"},signal:n.signal,body:JSON.stringify({path:t,method:n.method||"GET",headers:jr(n.headers),...(r=n.body,null==r?{}:"string"==typeof r?{body:r}:Buffer.isBuffer(r)?{bodyBase64:r.toString("base64")}:r instanceof Uint8Array?{bodyBase64:Buffer.from(r).toString("base64")}:{body:String(r)})})});var r;return{status:i.status||502,headers:Object.fromEntries(i.headers.entries()),body:i.body}}proxyWebSocket(e,t,n,s,i={}){if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}Lr(e);const r=this.requireNode(e.nodeId),a=n.startsWith("/")?n.slice(1):n;this.nodeAgentTransport(r).proxyWebSocket(r,t,`/instances/${encodeURIComponent(e.id)}/proxy/ws/${a}`,s,i)}async reportHeartbeat(e,t){if(!e.registrationToken)return;const n=this.requireNode(e.nodeId),s=`/instances/${encodeURIComponent(e.id)}/heartbeat`,i={method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.registrationToken}`},body:JSON.stringify(t)},r=await this.nodeAgentTransport(n).request(n,s,i),a=await r.json().catch(()=>({}));if(!r.ok){const e=new Error(a.error?.message||`Instance heartbeat sync failed with HTTP ${r.status}`);throw Object.assign(e,{statusCode:r.status,code:"INSTANCE_HEARTBEAT_SYNC_FAILED"}),e}}}function Lr(e){if(Gs(e))return;const t=new Error(`Instance ${e.name} is not ready.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_NOT_READY",runtimePhase:e.runtimeVersion?.phase}),t}function Br(e){if(!e||"object"!=typeof e)return;const t=e,n=fn.safeParse(t.build),s=Xt.safeParse(t.platform),i=Zt.safeParse(t.arch);return{..."boolean"==typeof t.ok?{ok:t.ok}:{},..."string"==typeof t.role?{role:t.role}:{},..."string"==typeof t.nodeId?{nodeId:t.nodeId}:{},..."string"==typeof t.protocolVersion?{protocolVersion:t.protocolVersion}:{},...s.success?{platform:s.data}:{},...i.success?{arch:i.data}:{},...n.success?{build:n.data}:{},..."string"==typeof t.serverTime?{serverTime:t.serverTime}:{}}}function Ur(e){const{agent:t,...n}=e.capabilities,{secret:s,...i}=e.auth,r=i.pairing?{...i.pairing,joinToken:void 0}:void 0,a=Br(t);return{...e,auth:r?{...i,pairing:r}:i,capabilities:a?{...n,agent:a}:n}}function Fr(e){return"local-folder"===e.type?{mode:"local-bind",path:"/workspace",readOnly:!1}:{mode:"git-clone",path:"/workspace",readOnly:!1}}function Hr(e){return function(e){const{registrationToken:t,target:n,agentStatus:s,targetStatus:i,uiAccessStatus:r,...a}=e;return a}(function(e){const t=`/instances/${encodeURIComponent(e.id)}`,n={strategy:"control-plane-proxy",web:`${t}/`,api:`${t}/api`,ws:`${t}/api`,status:"online"===e.connectionStatus||"online"===e.agentStatus?"reachable":"endpoint-unreachable"};return{...e,access:n}}(e))}function Wr(e){const{key:t,...n}=e;return{...n,keyPreview:(s=e.key,s.length<=8?"set":`${s.slice(0,4)}...${s.slice(-4)}`),keySet:!0};var s}class Gr{read(e){const t=new Map(e.projects.map(e=>[e.id,e])),n=new Map(e.images.map(e=>[e.id,e])),s=new Map(e.nodes.map(e=>[e.id,e])),i=new Map(e.runtimes.map(e=>[`${e.nodeId}:${e.id}`,e])),r=Date.now();return{items:e.instances.map(e=>function(e){const t=e.instance.lastHeartbeatAt?e.currentTime-Date.parse(e.instance.lastHeartbeatAt):void 0,n=void 0!==t&&t>3e4?"offline":e.instance.connectionStatus,s=Hr(e.instance);return{...s,aiSessions:ke(s.aiSessions),connectionStatus:n,heartbeatAgeMs:t,project:e.project,image:e.image,node:e.node?Ur(e.node):void 0,runtime:e.runtime,protocolCompatible:!e.instance.protocolVersion||e.instance.protocolVersion===zt}}({instance:e,currentTime:r,project:e.projectId?t.get(e.projectId):void 0,image:e.imageSnapshot||(e.imageSelection?n.get(e.imageSelection.imageId):void 0),node:s.get(e.nodeId),runtime:i.get(`${e.nodeId}:${e.runtimeId}`)})),nodeErrors:e.nodeErrors||[]}}}const Vr=qe.extend({id:qe.shape.configHash}),Kr=a.z.object({name:qe.shape.name,description:qe.shape.description,source:De,action:Me,policy:$e.partial().optional()}).strict(),Jr=a.z.object({instanceIds:a.z.array(js.shape.id).min(1),target:Le,enabled:a.z.boolean().optional()}).strict(),Qr=a.z.object({configHash:qe.shape.configHash,enabled:a.z.boolean().optional()}).strict();class Yr{triggers;listInstances;requireInstance;instanceRequest;constructor(e){this.triggers=e.triggers,this.listInstances=e.listInstances,this.requireInstance=e.requireInstance,this.instanceRequest=e.instanceRequest}async listTriggers(){const e=await this.listInstances(),t=new Map;for(const e of this.triggers.list())t.set(e.configHash,{configHash:e.configHash,config:e,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!0,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]});for(const n of e){const e=n.triggers;for(const s of e?.configs||[]){const i=t.get(s.configHash)||{configHash:s.configHash,config:s.config,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!1,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]},r=(s.deployments||[]).map(e=>({instanceId:n.id,instanceName:n.name,deployment:e,runtime:(s.runtime||[]).find(t=>t.deploymentId?t.deploymentId===e.deploymentId:t.configHash===e.configHash)}));i.deploymentCount+=r.length,i.enabledCount+=r.filter(e=>Boolean(e.deployment.enabled)).length,i.controlPlaneDeploymentCount+=r.filter(e=>"control-plane"===e.deployment.origin).length,i.runningCount+=(s.runtime||[]).filter(e=>"running"===e.status).length,i.errorCount+=(s.runtime||[]).filter(e=>"error"===e.status).length,i.deployments.push(...r),i.recentRuns.push(...(e.recentRuns||[]).filter(e=>e.configHash===s.configHash).map(e=>({...e,instanceId:n.id,instanceName:n.name}))),t.set(s.configHash,i)}}return{updatedAt:(new Date).toISOString(),triggers:[...t.values()].sort((e,t)=>String(e.config.name||e.configHash).localeCompare(String(t.config.name||t.configHash)))}}createTrigger(e){const t=Kr.parse(e||{}),n=Rr(),s={maxConcurrentRuns:t.policy?.maxConcurrentRuns??1,whenBusy:t.policy?.whenBusy??"skip",cooldownMs:t.policy?.cooldownMs},i=function(e){const t=We({source:e.source,action:e.action,policy:$e.parse(e.policy||{})});return`trg_${o.createHash("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}({source:t.source,action:t.action,policy:s}),r=Vr.parse({id:i,configHash:i,name:t.name,description:t.description,source:t.source,action:t.action,policy:s,createdAt:n,updatedAt:n});return this.triggers.put(r)}async deleteTrigger(e){const t=this.triggers.delete(e),n=await this.listInstances(),s=[];for(const t of n){const n=t.triggers?.configs?.find(t=>t.configHash===e);if(!n)continue;const i=(n.deployments||[]).filter(e=>"control-plane"===e.origin);if(!i.length)continue;const r=n.deployments.every(e=>"control-plane"===e.origin);for(const n of i)try{const i=n.deploymentId?`/triggers/${encodeURIComponent(e)}/deployments/${encodeURIComponent(n.deploymentId)}`:r?`/triggers/${encodeURIComponent(e)}`:void 0;if(!i){s.push({instanceId:t.id,configHash:e,deploymentId:n.deploymentId,deleted:!1,skipped:"missing-deployment-id"});continue}const a=await this.instanceRequest(t,i,{method:"DELETE"});if(s.push({instanceId:t.id,configHash:e,deploymentId:n.deploymentId,deleted:!0,data:a}),!n.deploymentId)break}catch(i){s.push({instanceId:t.id,configHash:e,deploymentId:n.deploymentId,deleted:!1,error:i instanceof Error?i.message:String(i)})}}return t||s.length||$r("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),{configHash:e,deletedTemplate:t,results:s}}async applyTrigger(e,t){const n=this.requireTrigger(e),s=Jr.parse(t||{}),i=[];for(const e of s.instanceIds){const t=await this.requireInstance(e);i.push({instanceId:e,data:await this.instanceRequest(t,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:n.name,description:n.description,source:n.source,action:n.action,policy:n.policy,deployment:{origin:"control-plane",enabled:s.enabled??!0,target:s.target}})})})}return{configHash:e,results:i}}async listInstanceTriggers(e){const t=await this.requireInstance(e);return this.instanceRequest(t,"/triggers")}async bindAiSessionTrigger(e,t,n){const s=Qr.parse(n||{}),i=this.requireTrigger(s.configHash),r=await this.requireInstance(e);return this.instanceRequest(r,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:i.name,description:i.description,source:i.source,action:i.action,policy:i.policy,deployment:{deploymentId:Xr(t,s.configHash),origin:"control-plane",enabled:s.enabled??!0,target:{type:"ai-session",aiSessionId:t}}})})}async unbindAiSessionTrigger(e,t,n){const s=await this.requireInstance(e),i=Xr(t,n);return this.instanceRequest(s,`/triggers/${encodeURIComponent(n)}/deployments/${encodeURIComponent(i)}`,{method:"DELETE"})}async runInstanceTrigger(e,t,n={}){const s=await this.requireInstance(e);return this.instanceRequest(s,`/triggers/${encodeURIComponent(t)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n||{})})}requireTrigger(e){const t=this.triggers.get(e);return t||$r("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),t}}function Xr(e,t){return`ai_${Buffer.from(e).toString("base64url")}_${t}`}const Zr=a.z.object({id:jn.shape.id.optional(),name:jn.shape.name,source:Pn,defaultImageSelection:jn.shape.defaultImageSelection,defaultNodeId:jn.shape.defaultNodeId,defaultRuntimeId:jn.shape.defaultRuntimeId,workspacePolicy:On.optional(),labels:jn.shape.labels.optional()}).strict(),ea=Zr.omit({id:!0}).partial().strict();function ta(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"/"!==e&&"\\"!==e}const na=a.z.string().refine(ta,"Mention trigger must be one printable non-alphanumeric character other than / or \\.");function sa(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"\\"!==e}const ia=a.z.string().refine(sa,"Command trigger must be one printable non-alphanumeric character other than \\."),ra=a.z.object({publicBaseUrl:a.z.string().trim().max(2048).optional(),updateChannel:In.default("stable"),mentionTrigger:na.default("@"),commandTrigger:ia.default("/")}).strict().refine(e=>e.mentionTrigger!==e.commandTrigger,{path:["commandTrigger"],message:"Command and mention triggers must be different."}),aa=a.z.object({publicBaseUrl:a.z.string().trim().max(2048).optional(),updateChannel:In.optional(),mentionTrigger:na.optional(),commandTrigger:ia.optional()}).strict();function oa(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t={...e};return"string"==typeof t.mentionTrigger&&ta(t.mentionTrigger)||(t.mentionTrigger="@"),"string"==typeof t.commandTrigger&&sa(t.commandTrigger)&&t.commandTrigger!==t.mentionTrigger||(t.commandTrigger="/"),t}const da=a.z.object({id:ss.shape.id.optional(),name:ss.shape.name,description:ss.shape.description,cover:ss.shape.cover,reference:ss.shape.reference,pullPolicy:ss.shape.pullPolicy.optional(),capabilities:a.z.array(a.z.string().trim().min(1).max(80)).optional(),optionalApps:a.z.array(a.z.string().trim().min(1).max(120)).optional(),defaultEnv:ss.shape.defaultEnv.optional(),labels:ss.shape.labels.optional()}).strict(),ca=da.omit({id:!0}).partial().strict();class la{options;constructor(e){this.options=e}getSettings(){return ra.parse(this.options.settings.get())}updateSettings(e){const t=aa.parse(e),n=ra.parse({...this.getSettings(),...t,..."publicBaseUrl"in t?{publicBaseUrl:t.publicBaseUrl?.trim()||void 0}:{}});return this.options.settings.put(n)}seedDefaults(){}listProjects(){return this.options.projects.list().map(e=>this.normalizeProjectRecord(e))}getProject(e){const t=this.options.projects.get(e);return t?this.normalizeProjectRecord(t):void 0}createProject(e){const t=Zr.parse(e),n=Rr(),s=Pn.parse(t.source);return this.options.projects.put(jn.parse({...t,id:t.id||ni("proj"),source:s,defaultImageSelection:t.defaultImageSelection||{imageId:"market_taskhandoff_browser"},defaultNodeId:t.defaultNodeId||this.options.defaultNodeId(),defaultRuntimeId:t.defaultRuntimeId||"runtime_local_docker",workspacePolicy:On.parse(t.workspacePolicy||Fr(s)),labels:t.labels||{},createdAt:n,updatedAt:n}))}updateProject(e,t){const n=ea.parse(t),s=this.requireProject(e),i=n.source?Pn.parse(n.source):s.source;return this.options.projects.put(jn.parse({...s,...n,id:e,source:i,workspacePolicy:n.workspacePolicy?On.parse(n.workspacePolicy):s.workspacePolicy,createdAt:s.createdAt,updatedAt:Rr()}))}deleteProject(e){return this.options.projects.delete(e)}requireProject(e){const t=this.getProject(e);return t||$r("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}listImages(){return this.options.images.list()}getMarketCatalog(){return{catalog:this.options.market.getCatalog(),status:this.options.market.getStatus()}}listImageOptions(e){return[...this.options.market.getCatalog().items.map(t=>this.marketImageOption(t,void 0,e,!0)),...this.listImages().map(e=>this.customImageOption(e))]}resolveImageSelection(e,t){const n=this.options.market.getCatalog().items.find(t=>t.id===e.imageId);if(n)return this.marketImageOption(n,e.tag,t);const s=this.options.images.get(e.imageId);if(s||$r("IMAGE_NOT_FOUND",`Image ${e.imageId} was not found.`),e.tag&&e.tag!==s.tag)throw Object.assign(new Error(`Custom image ${s.id} cannot override its stored tag.`),{statusCode:400,code:"CUSTOM_IMAGE_TAG_OVERRIDE"});return this.customImageOption(s)}createImage(e){const t=da.parse(e),n=Rr(),s=Bt(t.reference);return this.options.images.put(ss.parse({...t,id:t.id||ni("img"),origin:"custom",reference:s.reference,repository:s.repository,tag:s.tag,pullPolicy:t.pullPolicy||"if-not-present",capabilities:t.capabilities||[],optionalApps:t.optionalApps||[],defaultEnv:t.defaultEnv||{},labels:t.labels||{},createdAt:n,updatedAt:n}))}updateImage(e,t){const n=ca.parse(t),s=this.requireImage(e),i=Bt(n.reference||s.reference);return this.options.images.put(ss.parse({...s,...n,id:e,origin:"custom",reference:i.reference,repository:i.repository,tag:i.tag,createdAt:s.createdAt,updatedAt:Rr()}))}deleteImage(e){return this.options.images.delete(e)}requireImage(e){if(this.options.market.getCatalog().items.some(t=>t.id===e))throw Object.assign(new Error(`Market image ${e} is read-only.`),{statusCode:403,code:"MARKET_IMAGE_READ_ONLY"});const t=this.options.images.get(e);return t||$r("IMAGE_NOT_FOUND",`Image ${e} was not found.`),t}normalizeProjectRecord(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...Kt(e)};return"defaultRuntimeId"in t||(t.defaultRuntimeId="runtime_local_docker"),jn.parse(t)}return jn.parse(e)}(e);return t!==e&&this.options.projects.put(t),t}customImageOption(e){return rs.parse({id:e.id,origin:"custom",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||Kn,repository:e.repository,tag:e.tag,availableTags:e.tag?[{name:e.tag,reference:e.reference,status:"active"}]:[],reference:e.reference,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!1})}marketImageOption(e,t,n,s=!1){if("yanked"===e.status&&!s)throw Object.assign(new Error(`Market image ${e.id} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_YANKED"});const i=t||e.defaultTag,r=e.tags.find(e=>e.name===i);if(!r)throw Object.assign(new Error(`Market image ${e.id} does not provide tag ${i}.`),{statusCode:400,code:"MARKET_IMAGE_TAG_NOT_FOUND"});if("yanked"===r.status&&!s)throw Object.assign(new Error(`Market image tag ${e.id}:${i} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_TAG_YANKED"});const a=r.platforms.find(e=>!(n?.os&&e.os!==n.os||n?.architecture&&e.architecture!==n.architecture)),o=this.options.market.getCatalog();return rs.parse({id:e.id,origin:"market",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||Kn,repository:e.repository,tag:r.name,availableTags:e.tags.map(e=>({name:e.name,version:e.version,reference:e.reference,manifestDigest:e.manifestDigest,status:e.status})),reference:r.reference,digest:a?.digest||r.manifestDigest,downloadSizeBytes:a?.downloadSizeBytes,unpackedSizeBytes:a?.unpackedSizeBytes,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!0,lifecycleStatus:"yanked"===r.status||"yanked"===e.status?"yanked":"deprecated"===r.status||"deprecated"===e.status?"deprecated":"active",market:{catalogId:o.catalogId,catalogRevision:o.revision,publisher:e.publisher,version:r.version}})}}function pa(e){const t=Bt(e.reference),n=t.tag||"latest";return{id:e.id,publisher:"task-handoff",slug:e.slug,name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:Kn,repository:t.repository,defaultTag:n,tags:[{name:n,reference:t.reference,platforms:[],status:"active"}],capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:{},labels:{"task-handoff.image.kind":"controlled-instance","task-handoff.image.profile":e.slug},status:"active"}}function ua(){const e=(t=process.env.TASK_HANDOFF_BUILT_AT)&&!Number.isNaN(Date.parse(t))?new Date(t).toISOString():(new Date).toISOString();var t;return ts.parse({protocolVersion:"2026-07-29",catalogId:"task_handoff_embedded",revision:process.env.TASK_HANDOFF_VERSION||"0.0.1",source:"embedded",generatedAt:e,items:[pa({id:"market_taskhandoff_codex",slug:"codex",name:"TaskHandoff Codex",description:"Minimal Codex runtime with terminal and Codex.",localizedDescriptions:{"zh-CN":"最小 Codex 运行环境,包含终端和 Codex。"},reference:process.env.TASK_HANDOFF_CONTROLLED_CODEX_IMAGE||"huadream/task-handoff-controlled-codex:latest",capabilities:["terminal","codex"],optionalApps:["terminal-tty"]}),pa({id:"market_taskhandoff_ai",slug:"ai",name:"TaskHandoff Codex + Claude",description:"AI development runtime with Codex, Claude, and terminal.",localizedDescriptions:{"zh-CN":"AI 开发运行环境,包含 Codex、Claude 和终端。"},reference:process.env.TASK_HANDOFF_CONTROLLED_AI_IMAGE||"huadream/task-handoff-controlled-ai:latest",capabilities:["terminal","codex","claude"],optionalApps:["terminal-tty"]}),pa({id:"market_taskhandoff_browser",slug:"browser",name:"TaskHandoff Browser",description:"Full runtime with Codex, Claude, Chromium, VNC, and code-server.",localizedDescriptions:{"zh-CN":"完整运行环境,包含 Codex、Claude、Chromium、VNC 和 code-server。"},reference:process.env.TASK_HANDOFF_CONTROLLED_BROWSER_IMAGE||"huadream/task-handoff-controlled-browser:latest",capabilities:["browser","terminal","gui-terminal","vscode-web","codex","claude"],optionalApps:["chromium","terminal-tty","gui-terminal","vscode-web"]})]})}class ga{async loadCatalog(){return ua()}}class ma{snapshot;status;constructor(e=ua()){this.snapshot=ts.parse(e),this.status=ns.parse({source:this.snapshot.source,state:"ready",revision:this.snapshot.revision,updatedAt:this.snapshot.generatedAt})}getCatalog(){return this.snapshot}getStatus(){return this.status}acceptCandidate(e){const t=ts.safeParse(e);return t.success?(this.snapshot=t.data,this.status=ns.parse({source:t.data.source,state:"ready",revision:t.data.revision,updatedAt:(new Date).toISOString()}),{accepted:!0,snapshot:t.data}):(this.status=ns.parse({...this.status,state:"failed",updatedAt:(new Date).toISOString(),error:t.error.message}),{accepted:!1,error:t.error})}recordRefreshFailure(e){return this.status=ns.parse({...this.status,state:this.snapshot?"stale":"failed",updatedAt:(new Date).toISOString(),error:e instanceof Error?e.message:String(e)}),this.snapshot}async refresh(e){try{const t=await e.loadCatalog();return this.acceptCandidate(t)}catch(e){return this.recordRefreshFailure(e),{accepted:!1,error:e}}}}class ha{records=new Map;options;constructor(e){this.options=e}create(e){const t=o.randomBytes(this.options.tokenBytes).toString("base64url"),n={...e,token:t,expiresAt:new Date(Date.now()+(e.ttlMs||this.options.ttlMs)).toISOString()};return this.records.set(t,n),n}resolve(e,t){const n=this.records.get(e);if(!n)throw this.invalidError();if(n.expiresAt<=(new Date).toISOString())throw this.records.delete(e),this.invalidError();if(t&&!t(n))throw this.invalidError();return n}invalidError(){const e=new Error(this.options.invalidMessage);return Object.assign(e,{statusCode:this.options.invalidStatusCode||400,code:this.options.invalidCode}),e}}class ya{requireInstance;listAppSessions;tokens=new ha({invalidMessage:"App access link is invalid or expired.",invalidCode:"APP_ACCESS_TOKEN_INVALID",invalidStatusCode:401,tokenBytes:24,ttlMs:9e5});constructor(e){this.requireInstance=e.requireInstance,this.listAppSessions=e.listAppSessions}createToken(e){return this.tokens.create({instanceId:e.instanceId,sessionId:e.sessionId,mode:e.mode,ttlMs:e.ttlMs})}resolveToken(e,t){return this.tokens.resolve(e,e=>!t||e.mode===t)}async proxyTarget(e,t,n=""){const s=this.resolveToken(e,t),i=await this.requireInstance(s.instanceId),r=await this.listAppSessions(),a=r.instances.find(e=>e.instanceId===s.instanceId)?.appSessions.sessions.find(e=>ba(e.id)===s.sessionId);if(!a){const e=new Error("App session was not found.");throw Object.assign(e,{statusCode:404,code:"APP_SESSION_NOT_FOUND"}),e}return{access:s,instance:i,session:a,path:fa(t,s.sessionId,a,n)}}}function fa(e,t,n,s=""){return"tty"===e?`/api/apps/sessions/${encodeURIComponent(t)}/tty`:"vnc"===e?s?function(e,t){const n=`/${t.replace(/^\/+/,"")}`,s=n.split("?",1)[0]||"/",i=encodeURIComponent(e);if(!["/api/novnc/",`/api/apps/sessions/${i}/web/`,`/api/apps/sessions/${i}/vnc`,`/api/apps/sessions/${i}/novnc/`].some(e=>s===e.replace(/\/$/,"")||s.startsWith(e))){const e=new Error("App access token cannot be used for this path.");throw Object.assign(e,{statusCode:403,code:"APP_ACCESS_PATH_FORBIDDEN"}),e}return n}(t,s):ba(Ia(n.vnc).noVncPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`:ba(Ia(n.web).webPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`}function Ia(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function ba(e){return"string"==typeof e&&e.trim()?e.trim():void 0}const va={allow:"a",deny:"d",skip:"s"};function wa(e){return o.createHash("sha256").update(e).digest("base64url").slice(0,Math.ceil(16))}class Sa{tokens=new ha({invalidMessage:"Chat action expired.",invalidCode:"CHAT_ACTION_TOKEN_INVALID",tokenBytes:9,ttlMs:6e5});create(e){return this.tokens.create({type:e.type,instanceId:"instanceId"in e?e.instanceId:void 0,appId:"appId"in e?e.appId:void 0,routeId:"routeId"in e?e.routeId:void 0,decision:"decision"in e?e.decision:void 0,ttlMs:e.ttlMs})}resolve(e,t){return this.tokens.resolve(e,e=>!t||e.type===t)}pendingDecisionCallbackData(e,t){return`task_handoff:cp_p:v1:${va[t]}:${wa(e)}`}}function Aa(e){const{token:t,...n}=e;return{...n,settings:Ca(e.settings),tokenSet:Boolean(e.token)}}function ka(e,t){if(!t)return e;const n={...e};for(const[e,s]of Object.entries(t))void 0!==s&&""!==s&&(n[e]=s);return n}function Ca(e){const t={...e};return"string"==typeof t.clientSecret&&t.clientSecret&&(delete t.clientSecret,t.clientSecretSet=!0),t}const xa=a.z.object({name:Us.shape.name.optional(),channel:Us.shape.channel.optional(),enabled:a.z.boolean().optional(),token:Us.shape.token,defaultChatId:Us.shape.defaultChatId,allowedUserIds:a.z.array(a.z.string().trim().min(1).max(240)).optional(),pollIntervalMs:Us.shape.pollIntervalMs.optional(),settings:Us.shape.settings.optional()}).strict(),Ta=xa.extend({channel:Us.shape.channel,name:Us.shape.name.optional()});class za{chatBridges;chatSessions;constructor(e){this.chatBridges=e.chatBridges,this.chatSessions=e.chatSessions}list(){return this.chatBridges.list().map(Aa)}require(e){const t=this.chatBridges.get(e);return t||$r("CHAT_BRIDGE_NOT_FOUND",`Chat bridge ${e} was not found.`),t}create(e){const t=Ta.parse(e),n=Rr(),s=Us.parse({...t,id:ni(`chat_${t.channel}`),name:t.name||(i=t.channel,r=this.chatBridges.list().filter(e=>e.channel===t.channel).length+1,`${"telegram"===i?"Telegram":"wechat"===i?"WeChat":"dingding"===i?"DingDing":"Web"} ${r}`),enabled:t.enabled??!1,allowedUserIds:t.allowedUserIds||[],pollIntervalMs:t.pollIntervalMs||3e3,settings:ka({},t.settings),createdAt:n,updatedAt:n});var i,r;return this.chatBridges.put(s),Aa(s)}update(e,t){const n=xa.parse(t),s=this.require(e),i=ka(s.settings,n.settings),r=Us.parse({...s,...n,id:e,channel:s.channel,name:n.name||s.name,settings:i,updatedAt:Rr()});return this.chatBridges.put(r),Aa(r)}delete(e){const t=this.chatBridges.delete(e);if(t)for(const t of this.chatSessions.list())t.bridgeId===e&&this.chatSessions.delete(t.id);return t}}class Na{chatSessions;constructor(e){this.chatSessions=e.chatSessions}list(){return this.chatSessions.list()}require(e){const t=this.chatSessions.get(e);return t||$r("CHAT_SESSION_NOT_FOUND",`Chat session ${e} was not found.`),t}upsert(e){const t=Rr(),n=(s=e.channel,i=e.chatSessionId,`${e.bridgeId||s}:${i.replace(/[^a-zA-Z0-9_.:-]/g,"_")}`);var s,i;const r=this.chatSessions.get(n),a=Fs.parse({...r||{},...e,id:n,channel:e.channel,bridgeId:e.bridgeId,chatSessionId:e.chatSessionId,lastUsedAt:t,createdAt:r?.createdAt||t,updatedAt:t});return this.chatSessions.put(a),a}}function Ea(e){return{inline_keyboard:e.map(e=>e.map(e=>({text:e.text,...e.url?{url:e.url}:{callback_data:e.callbackData||""}})))}}function _a(e,t=40){return Ea(e.slice(0,t).map(e=>[e]))}function Pa(e){return Buffer.from(e,"utf8").toString("base64url")}function Oa(e){const t=ja(e).inline_keyboard,n=Array.isArray(t)?t:[],s=[];for(const e of n)if(Array.isArray(e))for(const t of e){const e=ja(t),n=String(e.callback_data||""),i=String(e.text||"").trim();i&&n&&s.push({text:i,id:`th_cb_${Pa(n)}`})}return s.slice(0,40)}function Ra(e,t){const n=String(e||"").replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,Math.max(0,t-3))}...`}function ja(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}const Da=new Set(["stopped","failed","exited","closed","terminated"]);function Ma(e){return!function(e){return Da.has(Wi(e))}(e)}function $a(e){return(e.aiSessions?.sessions||[]).filter(t=>function(e,t){const n=(e.apps?.sessions||[]).filter(Ha);if(t.appSessionId&&n.some(e=>e.id===t.appSessionId))return!0;const s=new Set(t.appBindingKeys||[]);return Boolean(s.size&&n.some(e=>function(e){return[...Hi(e).bindings.map(Gi),...Ki(e)].filter(Boolean)}(e).some(e=>s.has(e))))}(e,t))}function qa(e){return"waiting"===e.status?4:"failed"===e.status?3:"running"===e.status?2:"idle"===e.status?1:0}function La(e){return[e.cwd||"",e.agent||"",e.providerSessionId||e.id].join("\0")}function Ba(e){const t=e.session.title||e.session.userPrompt||e.session.summary||e.session.lastMessage||"",n=t?` - ${Ra(t,18)}`:"";return`${e.instance.name} - ${e.session.agent} ${e.session.status}${n}`}function Ua(e,t=(e,t)=>`task_handoff:approval:${e.id}:${t}`){return _a(e.flatMap(e=>"approval"!==e.kind?[]:[{text:Ra(`Allow ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"allow")},{text:Ra(`Skip ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"skip")},{text:Ra(`Deny ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"deny")}]))}function Fa(e){const t=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,n="approval"===e.kind?`/approve ${e.id} | /deny ${e.id} | /skip ${e.id}`:`/reply ${e.id} <message>`;return`[${t}] ${e.kind} ${e.id}: ${e.result}\n${n}`}function Ha(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{};return Ma("string"==typeof t.status?t.status:void 0)}class Wa{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleAiSessionsCommand(e){const t=await this.targets.aiSessionChoices(e),n=t.find(e=>e.active),s=t.map(e=>function(e){const t=e.session.userPrompt||e.session.summary||e.session.lastMessage||"",n=e.active?"* ":"",s=e.session.phase&&"unknown"!==e.session.phase?`/${e.session.phase}`:"";return`${n}${e.index+1}. ${e.projectName} / ${e.instance.name} - ${e.session.agent} - ${e.session.status}${s} - ${e.session.id}${t?` - ${t}`:""}`}(e));return{accepted:!0,routed:!1,binding:e,instances:t.map(e=>e.instance),reply:t.length?["Select AI session",`Current ${n?`${n.projectName} / ${n.instance.name} / ${n.session.id}`:await this.targets.renderChatTarget(e)}, total ${t.length}`,"",...s].join("\n"):"No AI sessions found.",replyMarkup:t.length?_a(t.map(e=>({text:`${e.active?"✓ ":""}${Ba(e)}`,callbackData:`task_handoff:cp_session:${e.index}`}))):void 0}}async handleAiSessionCommand(e,t){const n=String(t[0]||"").trim();if(!n)return this.handleAiSessionsCommand(e);const s=await this.targets.findAiSession(n);if(!s)return{accepted:!1,routed:!1,binding:e,reply:`AI session ${n} was not found. Try /sessions.`};const i=s.instance.projectId?this.deps.getProject(s.instance.projectId):void 0;return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i?.id||e.activeProjectId,activeInstanceId:s.instance.id,activeAiSessionId:s.session.id}),instance:s.instance,aiSession:s.session,reply:`Active AI session: ${s.instance.name} / ${s.session.agent} ${s.session.id}`}}async handleAiSessionIndexAction(e,t){const n=(await this.targets.aiSessionChoices(e)).find(e=>e.index===t);if(!n)return{accepted:!1,routed:!1,binding:e,reply:"AI session was not found. Use /session to refresh the list.",replyMarkup:void 0};const s=this.deps.upsertChatSession({...e,activeProjectId:n.projectId||e.activeProjectId,activeInstanceId:n.instance.id,activeAiSessionId:n.session.id}),i=await this.handleAiSessionsCommand(s);return{...i,accepted:!0,binding:s,instance:n.instance,aiSession:n.session,message:`selected ${n.session.id}`,reply:[`Current chat is bound to AI session ${n.session.id}`,"",i.reply].join("\n")}}async handleAiSessionInterruptCommand(e){if(!e.activeInstanceId||!e.activeAiSessionId)return{accepted:!1,routed:!1,binding:e,reply:"No active AI session is selected. Use /sessions and /session <id> first."};const t=await this.deps.requireControlledInstance(e.activeInstanceId,!0),n=await this.deps.interruptAiSession(t.id,e.activeAiSessionId);return{accepted:!0,routed:!0,binding:e,instance:Hr(t),aiSession:n,reply:`Interrupt sent to ${t.name} / ${e.activeAiSessionId}.`}}}function Ga(e){return e.providerTurnId||e.turnId||""}function Va(e){return Ma(Xa(e.status))}function Ka(e,t){const n=Xa(t.appId)||"App",s=Xa(t.title).trim();if(s&&s!==n)return s;const i=function(e,t){for(const n of e.appInventory?.items||[])if(n.id===t)return n.name;return""}(e,n);return i||n}function Ja(e){const t=function(e){return"online"!==e.connectionStatus?[]:(e.appInventory?.items||[]).filter(e=>"available"===e.availability)}(e),n=new Map;for(const e of t){const t=Za(e);t&&!n.has(t.id)&&n.set(t.id,t)}return[...n.values()]}function Qa(e,t,n,s){const i=Xa(t.id)?function(e,t){return`/apps/access/${Ya(e)}?token=${encodeURIComponent(t())}`}(t,s):`/instances/${encodeURIComponent(e.id)}/`,r=function(e,t){const n=Xa(t);if(n&&new RegExp("^https?://","i").test(n))return new URL(e,n.endsWith("/")?n:n+"/").toString()}(i,n);return{text:r||i,url:r}}function Ya(e){const t=Xa(e.kind);return"gui"===t?"vnc":"web"===t?"web":"tty"}function Xa(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function Za(e){if("string"==typeof e&&e.trim())return{id:e,label:eo(e)};const t=function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e),n=Xa(t.id).trim();return n?{id:n,label:Xa(t.name).trim()||eo(n)}:void 0}function eo(e){return{"terminal-tty":"Terminal","gui-terminal":"GUI Terminal",chromium:"Chromium",browser:"Browser","vscode-web":"VS Code"}[e]||e.replace(/[-_]+/g," ").replace(/\b\w/g,e=>e.toUpperCase())}class to{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleInstancesCommand(e){const t=await this.targets.instancesForBinding(e);return{accepted:!0,routed:!1,binding:e,instances:t,reply:(n=t.length,0===n?"No controlled instances are registered yet.":`Instances: ${n}. Tap an instance to create an app.`),replyMarkup:t.length?_a(t.map(e=>{return{text:Ra(e.name||e.id,56),callbackData:(t=this.deps.createChatActionToken({type:"instance-app-menu",instanceId:e.id}).token,`task_handoff:cp_i:${t}`)};var t})):void 0};var n}async handleInstanceAppMenuAction(e,t){const n=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!n)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const s=Ja(n);return{accepted:!0,routed:!1,binding:e,instance:n,message:`Select an app for ${n.name}.`,reply:s.length?`New app for ${n.name}`:`No launchable apps for ${n.name}.`,replyMarkup:s.length?_a(s.map(e=>{return{text:Ra(e.label,56),callbackData:(t=this.deps.createChatActionToken({type:"launch-app",instanceId:n.id,appId:e.id}).token,`task_handoff:cp_a:${t}`)};var t})):void 0}}async handleLaunchAppAction(e,t,n){const s=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!s)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const i=Ja(s).find(e=>e.id===n);return i?{accepted:!0,routed:!1,binding:e,instance:s,session:await this.deps.launchAppSession(s.id,i.id),message:`${i.label} launched`,reply:`Launched ${i.label} on ${s.name}.`}:{accepted:!1,routed:!1,binding:e,reply:`App ${n} is not launchable for ${s.name}.`}}async handleAppsCommand(e){const t=await this.deps.boardAsync(),n=new Map(t.map(e=>[e.id,e])),s=(await this.deps.listAppSessions()).instances.flatMap(e=>{const t=n.get(e.instanceId);return t?e.appSessions.sessions.filter(Va).filter(e=>Boolean(Xa(e.id))).map(e=>({instance:t,session:e,link:Qa(t,e,this.deps.controlPlanePublicBaseUrl(),()=>this.deps.createAppAccessToken({instanceId:t.id,sessionId:Xa(e.id)||"",mode:Ya(e)}).token)})):[]}),i=s.filter(e=>e.link.url).map(e=>({text:Ra(`${e.instance.name} · ${Ka(e.instance,e.session)}`,56),url:e.link.url}));return{accepted:!0,routed:!1,binding:e,instances:t,reply:(r=s.length,a=i.length,0===r?"No app sessions found.":0===a?`App sessions: ${r}. Set the control plane public URL in Settings > Appearance to open them from chat.`:`App sessions: ${r}. Tap a button to open.`),replyMarkup:i.length?_a(i):void 0};var r,a}}class no{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async forwardChatMessage(e,t){const n=t.target?.instanceId||e.activeInstanceId||"",s=t.target?.aiSessionId||e.activeAiSessionId,i=await this.deps.requireControlledInstance(n,!0),r=i.projectId?this.deps.requireProject(i.projectId):void 0;if("online"!==i.connectionStatus){const e=new Error(`Instance ${i.name} is not reachable.`);throw Object.assign(e,{statusCode:409,code:"CHAT_TARGET_UNREACHABLE"}),e}if(s){const n=await this.trySendAiSessionMessage(e,i.id,s,t.message.text,t.message.attachments,!t.target);return n.ok?this.sentResult(e,r,i,s,n.aiSession):this.failedSendResult(n.binding,r,i,n.reply)}const a=await this.targets.defaultAiSessionIdForInstance(i.id);if(a){const n=this.deps.upsertChatSession({...e,activeAiSessionId:a}),s=await this.trySendAiSessionMessage(n,i.id,a,t.message.text,t.message.attachments);return s.ok?this.sentResult(n,r,i,a,s.aiSession):this.failedSendResult(s.binding,r,i,s.reply)}return{accepted:!1,routed:!1,binding:e,project:r||void 0,instance:Hr(i),reply:`No active AI session is selected for ${i.name}. Use /sessions and /session <id> first.`}}async trySendAiSessionMessage(e,t,n,s,i=[],r=!0){try{return{ok:!0,binding:e,aiSession:await this.deps.sendAiSessionMessage(t,n,s||"请查看附件。",void 0,i)}}catch(t){return function(e){return"AI_SESSION_NOT_FOUND"===(e&&"object"==typeof e?e:{}).code}(t)?{ok:!1,binding:r?this.deps.upsertChatSession({...e,activeAiSessionId:void 0}):e,reply:`The selected AI session ${n} no longer exists. Use /session to choose an active AI session.`}:{ok:!1,binding:e,reply:`Failed to send to AI session ${n}: ${Mr(t)}`}}}failedSendResult(e,t,n,s){return{accepted:!1,routed:!1,binding:e,project:t||void 0,instance:Hr(n),reply:s}}sentResult(e,t,n,s,i){return{accepted:!0,routed:!0,binding:e,project:t||void 0,instance:Hr(n),aiSession:i,instanceId:n.id,aiSessionId:s,turnId:(r=i,r.turnId||r.providerTurnId||r.session.activeTurnId||""),providerTurnId:Ga(i),reply:`Sent to ${t?.name||n.sourceSnapshot.name||"Source"} / ${n.name} / ${s}.`};var r}}class so{deps;constructor(e){this.deps=e}async handlePendingCommand(e){const t=await this.deps.listPendingRoutes(),n=e.activeInstanceId?t.filter(t=>t.instanceId===e.activeInstanceId):t;return{accepted:!0,routed:!1,binding:e,pending:n,reply:n.length?n.map(Fa).join("\n"):"No pending routes.",replyMarkup:n.length?Ua(n,(e,t)=>this.deps.pendingDecisionCallbackData(e.id,t)):void 0}}async handlePendingDecisionCommand(e,t,n){const s=Dr(t[0]);return s?s.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:s,aiSession:await this.deps.resolveAiSessionApproval(s.instanceId,s.aiSessionId,n),reply:`${n} sent for ${s.instanceId}:${s.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:s,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:`Usage: /${"allow"===n?"approve":n} <instance-id:pending-id>`}}async handlePendingReplyCommand(e,t){const n=Dr(t[0]),s=t.slice(1).join(" ").trim();return n&&s?n.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:n,aiSession:await this.deps.sendAiSessionMessage(n.instanceId,n.aiSessionId,s),reply:`Reply sent for ${n.instanceId}:${n.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:n,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:"Usage: /reply <instance-id:pending-id> <message>"}}}class io{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleUseCommand(e,t){if(0===t.length)return{accepted:!0,routed:!1,binding:e,instances:await this.targets.instancesForBinding(e),reply:await this.targets.renderUseHelp(e)};if(t.length>=2){const n=this.targets.resolveProject(t[0]),s=await this.targets.resolveInstance(t[1],n.id),i=this.deps.upsertChatSession({...e,activeProjectId:n.id,activeInstanceId:s.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(s.id)});return{accepted:!0,routed:!1,binding:i,reply:await this.targets.renderTargetSelected(i)}}const n=t[0],s=await this.targets.findInstance(n);if(s){const t=s.projectId?this.deps.requireProject(s.projectId):void 0,n=this.deps.upsertChatSession({...e,activeProjectId:t?.id,activeInstanceId:s.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(s.id)});return{accepted:!0,routed:!1,binding:n,reply:await this.targets.renderTargetSelected(n)}}const i=this.targets.resolveProject(n),r=(await this.deps.boardAsync()).filter(e=>e.projectId===i.id),a=r.filter(e=>"running"===e.status&&"online"===e.connectionStatus);if(1===a.length){const t=this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:a[0].id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(a[0].id)});return{accepted:!0,routed:!1,binding:t,reply:await this.targets.renderTargetSelected(t)}}return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:void 0,activeAiSessionId:void 0}),instances:r,reply:`${i.name} selected. Choose an instance with /use ${i.id} <instance-id-or-name>.\n${this.targets.renderInstanceLines(r)}`}}async handleProjectCommand(e,t){if(0===t.length)return{accepted:!1,routed:!1,binding:e,reply:"Usage: /project <project-id-or-name>"};const n=this.targets.resolveProject(t[0]),s=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,i=this.deps.upsertChatSession({...e,activeProjectId:n.id,activeInstanceId:s?.projectId===n.id?s.id:void 0,activeAiSessionId:s?.projectId===n.id?e.activeAiSessionId:void 0});return{accepted:!0,routed:!1,binding:i,reply:`Active project: ${n.name}${i.activeInstanceId?"":". Choose an instance with /use."}`}}}class ro{deps;constructor(e){this.deps=e}async instancesForBinding(e){const t=await this.deps.boardAsync();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async fullInstancesForBinding(e){const t=await this.deps.listNodeInstances();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async renderUseHelp(e){return`Active target: ${await this.renderChatTarget(e)}\nUse /instances to list targets, then /use <instance-id-or-name>. Use /sessions to choose an AI session when there is more than one.`}async renderChatStatus(e){const t=e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0;return t?`Active target: ${await this.renderChatTarget(e)}\nStatus: ${t.status}, connection: ${t.connectionStatus}`:`Active target: ${await this.renderChatTarget(e)}`}async renderChatHelp(e){return["TaskHandoff chat commands:","/help - show this help","/status or /target - show the active project, instance, and AI session","/instances - list controlled instances","/apps - list app sessions","/use <instance-id-or-name> - choose an active instance","/use <project-id-or-name> <instance-id-or-name> - choose a project and instance","/project <project-id-or-name> - narrow instance selection to a project","/sessions - list all AI sessions","/session <ai-session-id> - choose an active AI session","/pending - list waiting approvals","/approve <instance-id:pending-id>, /deny <...>, /skip <...> - answer approvals","/reply <instance-id:pending-id> <message> - reply to a waiting session","/cancel - interrupt the active AI session","","Send normal text after selecting a target; if there is only one AI session, it is selected automatically.",`Current target: ${await this.renderChatTarget(e)}`].join("\n")}renderInstanceLines(e){return e.map(e=>`${e.name} (${e.id}) - ${e.status}/${e.connectionStatus}`).join("\n")}async renderChatTarget(e){const t=e.activeProjectId?this.deps.getProject(e.activeProjectId):void 0,n=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,s=e.activeAiSessionId||"none";return`${t?.name||e.activeProjectId||"none"} / ${n?.name||e.activeInstanceId||"none"} / ${s}`}async renderTargetSelected(e){const t=await this.renderChatTarget(e);return e.activeAiSessionId?`Active target: ${t}`:`Active target: ${t}\nNo AI session selected yet. Use /sessions and /session <id>, or send a message once exactly one session is available.`}async defaultAiSessionIdForInstance(e){const t=await this.aiSessionSnapshotForInstance(e),n=(await this.deps.boardAsync()).find(t=>t.id===e),s=(await this.deps.listAppSessions()).instances.find(t=>t.instanceId===e),i=n?{...n,apps:{...n.apps,sessions:s?.appSessions.sessions||[]}}:void 0,r=t&&n?$a({...i,aiSessions:t}):t?.sessions||[];return 1===r.length?r[0].id:void 0}async aiSessionChoices(e){return(await this.boardInstancesForBinding({},{refresh:!0})).flatMap(t=>$a(t).map(n=>({index:0,projectId:t.projectId,projectName:t.project?.name||("string"==typeof t.sourceSnapshot.name?t.sourceSnapshot.name:"Source"),instance:t,session:n,active:e.activeInstanceId===t.id&&e.activeAiSessionId===n.id}))).sort((e,t)=>{const n=qa(t.session)-qa(e.session);return n||(String(e.instance.name||"").localeCompare(String(t.instance.name||""))||e.instance.id.localeCompare(t.instance.id)||La(e.session).localeCompare(La(t.session)))}).map((e,t)=>({...e,index:t}))}async boardInstancesForBinding(e,t={}){const n=await this.deps.boardAsync(),s=await this.deps.listAiSessions(t),i=await this.deps.listAppSessions(t),r=new Map(s.instances.map(e=>[e.instanceId,e.aiSessions])),a=new Map(i.instances.map(e=>[e.instanceId,e.appSessions])),o=n.map(e=>{return{...e,apps:{...e.apps,sessions:a.get(e.id)?.sessions||[]},aiSessions:r.get(e.id)||(t=e.aiSessions.updatedAt,{runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:t})};var t});return e.activeProjectId?o.filter(t=>t.projectId===e.activeProjectId):o}async findAiSession(e){const t=await this.boardInstancesForBinding({},{refresh:!0});for(const n of t){const t=(n.aiSessions?.sessions||[]).find(t=>t.id===e||t.providerSessionId===e);if(t)return{instance:n,session:t}}}async aiSessionSnapshotForInstance(e){const t=await this.deps.listAiSessions();return t.instances.find(t=>t.instanceId===e)?.aiSessions}resolveProject(e){const t=this.deps.getProject(e)||this.deps.listProjects().find(t=>t.name===e);return t||$r("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}async resolveInstance(e,t){const n=await this.findInstance(e,t);return n||$r("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`),n}async findInstance(e,t){return(await this.deps.listNodeInstances()).find(n=>!(n.id!==e&&n.name!==e||t&&n.projectId!==t))}}class ao{deps;targets;sender;appCommands;pendingCommands;aiSessionCommands;targetCommands;constructor(e){this.deps=e,this.targets=new ro(e),this.sender=new no(e,this.targets),this.appCommands=new to(e,this.targets),this.pendingCommands=new so(e),this.aiSessionCommands=new Wa(e,this.targets),this.targetCommands=new io(e,this.targets)}async handleChatGatewayMessage(e){const t=Hs.parse(e),n=this.deps.upsertChatSession({channel:t.source.channel,bridgeId:t.source.bridgeId,chatSessionId:t.source.chatSessionId,userId:t.source.userId}),s=t.message.text.trim();return s.startsWith("/")?this.handleChatCommand(n,s):s||t.message.attachments.length?n.activeInstanceId?this.sender.forwardChatMessage(n,t):{accepted:!1,routed:!1,binding:n,reply:await this.targets.renderUseHelp(n)}:{accepted:!1,routed:!1,binding:n,reply:""}}async handleChatGatewayAction(e){const t=this.deps.upsertChatSession({channel:e.source.channel,bridgeId:e.source.bridgeId,chatSessionId:e.source.chatSessionId,userId:e.source.userId});return"ai-session"===e.action.type?this.aiSessionCommands.handleAiSessionIndexAction(t,e.action.index):"instance-app-menu"===e.action.type?this.appCommands.handleInstanceAppMenuAction(t,e.action.instanceId):"launch-app"===e.action.type?this.appCommands.handleLaunchAppAction(t,e.action.instanceId,e.action.appId):"pending-decision"===e.action.type?this.pendingCommands.handlePendingDecisionCommand(t,[e.action.routeId],e.action.decision):{accepted:!1,routed:!1,binding:t,reply:"Unknown action."}}async handleChatCommand(e,t){const[n="",...s]=t.split(/\s+/),i=n.replace(/@[\w_]+$/,"");switch(i.toLowerCase()){case"/help":case"/start":return{accepted:!0,routed:!1,binding:e,reply:await this.targets.renderChatHelp(e)};case"/sessions":return this.aiSessionCommands.handleAiSessionsCommand(e);case"/session":return this.aiSessionCommands.handleAiSessionCommand(e,s);case"/cancel":case"/interrupt":return this.aiSessionCommands.handleAiSessionInterruptCommand(e);case"/pending":return this.pendingCommands.handlePendingCommand(e);case"/approve":return this.pendingCommands.handlePendingDecisionCommand(e,s,"allow");case"/deny":return this.pendingCommands.handlePendingDecisionCommand(e,s,"deny");case"/skip":return this.pendingCommands.handlePendingDecisionCommand(e,s,"skip");case"/reply":return this.pendingCommands.handlePendingReplyCommand(e,s);case"/instances":return this.appCommands.handleInstancesCommand(e);case"/apps":return this.appCommands.handleAppsCommand(e);case"/use":return this.targetCommands.handleUseCommand(e,s);case"/project":return this.targetCommands.handleProjectCommand(e,s);case"/status":case"/target":return{accepted:!0,routed:!1,binding:e,instance:e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0,reply:await this.targets.renderChatStatus(e)};default:return{accepted:!1,routed:!1,binding:e,reply:`Unknown command: ${i||n}. Try /help.`}}}}const oo=a.z.object({id:a.z.string().trim().min(1),preferences:ki,createdAt:a.z.string().datetime(),updatedAt:a.z.string().datetime()}).strict();function co(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=t.preferences&&"object"==typeof t.preferences&&!Array.isArray(t.preferences)?t.preferences:{};return{id:t.id,preferences:{import:"string"==typeof n.import&&n.import.trim()?n.import:".",export:"string"==typeof n.export&&n.export.trim()?n.export:"."},createdAt:t.createdAt,updatedAt:t.updatedAt}}function lo(){return ki.parse({})}function po(e){return/^[a-z]:[\\/]/i.test(e)||/^[\\/]{2}[^\\/]+[\\/]+[^\\/]+/.test(e)}function uo(e){return po(e)?t.win32:t.posix}const go=a.z.object({mode:a.z.enum(["local-static-key","paired-hmac","proxy-binding"]).default("local-static-key"),keyId:fs.shape.id.optional(),secret:a.z.string().trim().max(4096).optional(),pairedAt:fs.shape.createdAt.optional(),pairing:a.z.object({status:a.z.enum(["pending","paired","expired"]).default("pending"),joinToken:a.z.string().trim().max(4096).optional(),expiresAt:fs.shape.createdAt.optional()}).strict().optional()}).strict(),mo=a.z.object({autoImportAgentConfigs:a.z.boolean().optional(),defaultCodexPermissionMode:G.optional()}).strict(),ho=a.z.object({id:js.shape.id.optional(),name:js.shape.name.optional(),projectId:js.shape.projectId,source:Pn.optional(),sourceSnapshot:a.z.record(a.z.string(),a.z.unknown()).optional(),nodeId:js.shape.nodeId.optional(),runtimeId:js.shape.runtimeId.optional(),imageSelection:js.shape.imageSelection,config:mo.optional(),modelSelection:js.shape.modelSelection.optional(),start:a.z.boolean().default(!1)}).strict(),yo=a.z.object({name:js.shape.name.optional(),config:mo.optional(),modelSelection:js.shape.modelSelection.optional()}).strict(),fo=a.z.object({name:Dn.shape.name,endpoint:Dn.shape.endpoint,key:Dn.shape.key,model:Dn.shape.model,app:Dn.shape.app,enabled:Dn.shape.enabled.optional(),order:Dn.shape.order.optional(),labels:Dn.shape.labels.optional()}).strict(),Io=fo.partial().strict(),bo=a.z.object({id:fs.shape.id.optional(),name:fs.shape.name,connectionMode:fs.shape.connectionMode.optional(),connectionPath:fs.shape.connectionPath.optional(),connectionEnabled:fs.shape.connectionEnabled.optional(),auth:go.optional(),joinToken:a.z.string().trim().min(1).max(4096).optional(),endpoint:fs.shape.endpoint,controlEndpoint:fs.shape.controlEndpoint,containerEndpoint:fs.shape.containerEndpoint,publicWebBase:fs.shape.publicWebBase,status:a.z.enum(["unknown","online","offline","degraded"]).optional(),health:a.z.enum(["unknown","ok","degraded","failed"]).optional(),capabilities:fs.shape.capabilities.optional(),labels:fs.shape.labels.optional(),lastSeenAt:fs.shape.lastSeenAt}).strict(),vo=a.z.object({name:fs.shape.name.optional(),connectionMode:fs.shape.connectionMode.unwrap().optional(),connectionPath:fs.shape.connectionPath.unwrap().optional(),connectionEnabled:fs.shape.connectionEnabled.unwrap().optional(),auth:go.optional(),endpoint:fs.shape.endpoint,controlEndpoint:fs.shape.controlEndpoint,containerEndpoint:fs.shape.containerEndpoint,publicWebBase:fs.shape.publicWebBase,status:fs.shape.status.unwrap().optional(),health:fs.shape.health.unwrap().optional(),capabilities:fs.shape.capabilities.unwrap().optional(),labels:fs.shape.labels.unwrap().optional(),lastSeenAt:fs.shape.lastSeenAt}).strict(),wo=a.z.object({controlPlaneUrl:a.z.string().trim().url().max(2048),joinToken:a.z.string().trim().min(1).max(4096),controlPlaneName:a.z.string().trim().min(1).max(160).optional(),activate:a.z.boolean().optional()}).strict(),So=a.z.object({nodeName:a.z.string().trim().min(1).max(160).optional(),expiresInMs:a.z.number().int().positive().max(36e5).optional()}).strict();function Ao(e){return{enabledCount:e.deployments.filter(e=>e.enabled).length,runningCount:e.runtime.filter(e=>"running"===e.status).length,errorCount:e.runtime.filter(e=>"error"===e.status).length,configs:e.configs.map(t=>({configHash:t.configHash,config:t,deployments:e.deployments.filter(e=>e.configHash===t.configHash),runtime:e.runtime.filter(e=>e.configHash===t.configHash)})),recentRuns:e.recentRuns.slice(0,20)}}const ko=a.z.object({id:a.z.string().trim().min(1),tokenHash:a.z.string().trim().min(1),expiresAt:a.z.string().datetime(),nodeName:a.z.string().trim().min(1).max(160).optional(),createdAt:a.z.string().datetime(),updatedAt:a.z.string().datetime()}).strict(),Co="task-handoff.control-plane.local",xo="task-handoff.control-plane.builtin",To=a.z.object({proxyOrigin:Xe,inviteToken:a.z.string().trim().min(24).max(512),name:a.z.string().trim().min(1).max(160).optional()}).strict(),zo=a.z.object({data:a.z.object({binding:yt,closed:a.z.object({abortedRequests:a.z.number().int().nonnegative(),closedSockets:a.z.number().int().nonnegative()}).strict()}).strict()}).strict();function No(e){return"true"===e.labels[Co]}function Eo(e){return"true"===e.labels[xo]}class _o{projects;models;images;nodes;chatSessions;chatBridges;triggers;nodeJoinInvites;configSyncPreferences;settings;paths;fetchImpl;dockerCommandRunner;logger;appAccessService;chatActionTokenService=new Sa;nodeAgentTransportResolver;proxyPrivateStore;nodeAgentGateway;controlledInstanceGateway;instanceBoardReader=new Gr;controlPlaneTriggerService;catalogService;marketCatalogService;chatBridgeService;chatSessionService;chatSessionRuntime;proxyClaimOperations=new Map;aiSessionSnapshotProvider;appSessionSnapshotProvider;constructor(e,n={}){var s;this.paths=e,this.fetchImpl=n.fetchImpl||fetch,this.dockerCommandRunner=n.dockerCommandRunner,this.proxyPrivateStore=new Or(function(e){const n=t.resolve(e,"control-plane-proxy");return{pendingClaimsDir:t.join(n,"pending-claims"),nodeCredentialsDir:t.join(n,"node-credentials"),identityPath:t.join(n,"identity.json")}}(e.dataDir),(e,t)=>this.logWarn(t,e)),this.nodeAgentTransportResolver=new Sr({direct:wr(this.fetchImpl),tunnel:n.nodeAgentTransport,proxy:new zr({credentialForNode:e=>this.proxyPrivateStore.nodeCredential(e.id),fetchImpl:this.fetchImpl})}),this.logger=(s=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(s||"").trim().toLowerCase())?n.logger:void 0);const i=new er({request:(e,t,n)=>this.nodeAgentFetch(e,t,n),logger:this.logger});this.nodeAgentGateway=new tr(i),this.controlledInstanceGateway=new qr({requireNode:e=>this.requireNode(e),nodeAgentTransport:e=>this.nodeAgentTransportResolver.resolve(e)});const r=e=>({schema:e,logger:(e,t)=>this.logWarn(t,e)});this.projects=new ii(e.projectsDir,{...r(jn),sanitize:Kt}),this.models=new ii(e.modelsDir,r(Dn)),this.images=new ii(e.imagesDir,{...r(ss),sanitize:e=>ms(e,e=>this.logWarn(e,"legacy image profile field was migrated"))}),this.nodes=new ii(e.nodesDir,{...r(fs),sanitize:e=>function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=new Set(["id","name","connectionMode","connectionPath","connectionEnabled","auth","endpoint","controlEndpoint","containerEndpoint","publicWebBase","status","health","capabilities","proxyState","appInventory","labels","lastSeenAt","createdAt","updatedAt"]);for(const e of Object.keys(n))s.has(e)||t?.({field:e});const i=n.auth&&"object"==typeof n.auth&&!Array.isArray(n.auth)?Ls(n.auth,["mode","keyId","secret","pairedAt","pairing"]):n.auth;i&&"object"==typeof i&&"pairing"in i&&(i.pairing=Ls(i.pairing,["status","joinToken","expiresAt"]));const r=n.connectionPath&&"object"==typeof n.connectionPath&&!Array.isArray(n.connectionPath)?"control-plane-proxy"===n.connectionPath.kind?Ls(n.connectionPath,["kind","proxyId","proxyBindingId","targetNodeId"]):Ls(n.connectionPath,["kind"]):{kind:"direct"},a=n.proxyState&&"object"==typeof n.proxyState&&!Array.isArray(n.proxyState)?Ls(n.proxyState,["reachability","bindingStatus","bindingRevision","streamId","revision","observedAt","target","lastError","updatedAt"]):n.proxyState;if(a&&"object"==typeof a){const e=a;e.target=Ls(e.target,["id","name","status","health","lastSeenAt","capabilities"]),e.lastError=Ls(e.lastError,["code","message","retryable","details"])}return{...Object.fromEntries(Object.entries(n).filter(([e])=>s.has(e))),auth:i,connectionPath:r,proxyState:a}}(e,e=>this.logWarn(e,"unknown stored node field was ignored"))}),this.chatSessions=new ii(e.chatSessionsDir,r(Fs)),this.chatBridges=new ii(e.chatBridgesDir,r(Us)),this.triggers=new ii(e.triggersDir,r(Vr)),this.nodeJoinInvites=new ii(e.nodeJoinInvitesDir,r(ko)),this.configSyncPreferences=new ii(t.join(e.dataDir,"config-sync-preferences"),{...r(oo),sanitize:co}),this.settings=new ri(e.settingsPath,()=>ra.parse({}),{...r(ra),sanitize:oa}),this.marketCatalogService=new ma,this.catalogService=new la({projects:this.projects,images:this.images,market:this.marketCatalogService,settings:this.settings,defaultNodeId:()=>this.defaultNodeId()}),this.controlPlaneTriggerService=new Yr({triggers:this.triggers,listInstances:()=>this.listNodeInstances(),requireInstance:async e=>this.requireControlledInstance(e,!0),instanceRequest:(e,t,n)=>this.instanceRequest(e,t,n)}),this.appAccessService=new ya({requireInstance:async e=>this.requireControlledInstance(e,!0),listAppSessions:()=>this.listAppSessions()}),this.chatBridgeService=new za({chatBridges:this.chatBridges,chatSessions:this.chatSessions}),this.chatSessionService=new Na({chatSessions:this.chatSessions}),this.chatSessionRuntime=new ao({upsertChatSession:e=>this.upsertChatSession(e),boardAsync:()=>this.boardAsync(),listAiSessions:e=>this.listAiSessions(e),listAppSessions:e=>this.listAppSessions(e),listNodeInstances:()=>this.listNodeInstances(),listPendingRoutes:()=>this.listPendingRoutes(),pendingDecisionCallbackData:(e,t)=>this.pendingDecisionCallbackData(e,t),createChatActionToken:e=>this.createChatActionToken(e),createAppAccessToken:e=>this.createAppAccessToken(e),controlPlanePublicBaseUrl:()=>this.getSettings().publicBaseUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_URL||process.env.TASK_HANDOFF_PUBLIC_BASE||process.env.TASK_HANDOFF_CONTROL_PLANE_PUBLIC_URL,requireControlledInstance:(e,t)=>this.requireControlledInstance(e,t),requireProject:e=>this.requireProject(e),getProject:e=>this.catalogService.getProject(e),listProjects:()=>this.catalogService.listProjects(),resolveAiSessionApproval:(e,t,n)=>this.resolveAiSessionApproval(e,t,n),sendAiSessionMessage:(e,t,n,s,i)=>this.sendAiSessionMessage(e,t,n,s,i),interruptAiSession:(e,t)=>this.interruptAiSession(e,t),launchAppSession:(e,t,n)=>this.launchAppSession(e,t,n)})}logInfo(e,t){this.logger?.info?.(e,t)}logWarn(e,t){this.logger?.warn?.(e,t)}logError(e,t){this.logger?.error?.(e,t)}setNodeAgentTransport(e){this.nodeAgentTransportResolver.setTunnel(e)}resolveNodeAgentTransport(e){return this.nodeAgentTransportResolver.resolve(e)}setAiSessionSnapshotProvider(e){this.aiSessionSnapshotProvider=e}async listAiSessions(e={}){return this.aiSessionSnapshotProvider?this.aiSessionSnapshotProvider(e):this.bootstrapAiSessionsFromInstances()}setAppSessionSnapshotProvider(e){this.appSessionSnapshotProvider=e}async listAppSessions(e={}){return this.appSessionSnapshotProvider?this.appSessionSnapshotProvider(e):this.bootstrapAppSessionsFromInstances()}init(){this.projects.init(),this.models.init(),this.images.init(),this.nodes.init(),this.chatSessions.init(),this.chatBridges.init(),this.triggers.init(),this.nodeJoinInvites.init(),this.proxyPrivateStore.init(),this.proxyPrivateStore.gcNodeCredentials(e=>{const t=this.nodes.get(e.nodeId);return"control-plane-proxy"===t?.connectionMode&&"control-plane-proxy"===t.connectionPath.kind&&t.connectionPath.proxyBindingId===e.proxyBindingId&&t.connectionPath.targetNodeId===e.targetNodeId}),this.settings.init(),this.migrateLegacyImageCatalog(),this.seedDefaults()}migrateLegacyImageCatalog(){const e=Object.keys(Gt),s=[];for(const t of e){const e=this.images.filePath(t);if(n.existsSync(e))try{s.push({id:t,filePath:e,record:JSON.parse(n.readFileSync(e,"utf8"))})}catch(e){return this.writeMarketMigrationState("failed",{code:"LEGACY_IMAGE_BACKUP_READ_FAILED",legacyId:t,error:Mr(e)}),void this.logWarn({legacyId:t,error:Mr(e)},"legacy embedded image migration was not applied")}}if(!s.length)return;const i=n.readdirSync(this.paths.projectsDir).filter(e=>e.endsWith(".json")).sort().map(e=>t.join(this.paths.projectsDir,e)),r=[];for(const e of i)try{const t=JSON.parse(n.readFileSync(e,"utf8")),s=jn.parse(Kt(t));r.push({filePath:e,raw:t,migrated:s})}catch(t){return this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_VALIDATION_FAILED",filePath:e,error:Mr(t)}),void this.logWarn({filePath:e,error:Mr(t)},"legacy embedded image migration was not applied")}const a=r.find(({migrated:t})=>{const n=t.defaultImageSelection?.imageId;return!!n&&e.includes(n)});if(a)this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_MIGRATION_INCOMPLETE",filePath:a.filePath});else{n.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketMigrationBackupPath,`${JSON.stringify({version:zt,createdAt:Rr(),images:s.map(({id:e,record:t})=>({id:e,record:t})),projects:r.map(({filePath:e,raw:n})=>({file:t.basename(e),record:n}))},null,2)}\n`,{encoding:"utf8"});for(const{migrated:e}of r)this.projects.put(e);for(const{id:e}of s)this.images.delete(e),this.logWarn({legacyId:e,marketId:Gt[e]},"embedded image profile migrated to Market");this.writeMarketMigrationState("complete",{code:"LEGACY_IMAGE_MIGRATION_COMPLETE",migratedImageIds:s.map(({id:e})=>e),backupPath:this.paths.marketMigrationBackupPath})}}writeMarketMigrationState(e,t){n.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketStatePath,`${JSON.stringify({status:e,updatedAt:Rr(),...t},null,2)}\n`,{encoding:"utf8"})}getSettings(){return this.catalogService.getSettings()}updateSettings(e){return this.catalogService.updateSettings(e)}seedDefaults(){return this.catalogService.seedDefaults()}listProjects(){return this.catalogService.listProjects()}defaultNodeId(){return this.nodes.list().find(No)?.id||this.nodes.list()[0]?.id}async inspectNodeAgent(e,t){const n="/health",s=t?pr(t,{method:"GET",pathWithQuery:`/api/node-agent${n}`}):{},i=await fr(this.fetchImpl,e,n,{headers:s}),r=await i.json().catch(()=>({}));if(!i.ok){const t=new Error(r.error?.message||`Node agent health check failed with HTTP ${i.status}`);throw Object.assign(t,{statusCode:i.status,code:"NODE_AGENT_HEALTH_FAILED"}),this.logWarn({nodeEndpoint:e,statusCode:i.status,errorCode:"NODE_AGENT_HEALTH_FAILED"},"node agent health check failed"),t}const a="string"==typeof r.data?.nodeId&&r.data.nodeId.trim()?r.data.nodeId.trim():void 0;if(!a){const t=new Error("Node agent health response did not include nodeId.");throw Object.assign(t,{statusCode:502,code:"NODE_AGENT_ID_MISSING"}),this.logWarn({nodeEndpoint:e,payload:r.data,errorCode:"NODE_AGENT_ID_MISSING"},"node agent health response missing node id"),t}r.data?.protocolVersion!==zt&&this.logWarn({nodeId:a,nodeEndpoint:e,expectedProtocolVersion:zt,actualProtocolVersion:r.data?.protocolVersion,build:r.data?.build,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node agent protocol version mismatch");const o=fn.safeParse(r.data?.build);return this.logInfo({nodeId:a,nodeEndpoint:e,protocolVersion:r.data.protocolVersion,build:o.success?o.data:r.data?.build},"node agent health check ok"),{nodeId:a,data:r.data||{}}}async completeNodeAgentPairing(e,t){const n=await this.fetchImpl(`${e.replace(/\/$/,"")}/api/node-agent/pairing/complete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({joinToken:t.joinToken,controlPlaneName:t.controlPlaneName||"Control Plane"})}),s=await n.json().catch(()=>({}));if(!n.ok){const e=new Error(s.error?.message||`Node agent pairing failed with status ${n.status}.`);throw Object.assign(e,{statusCode:n.status,code:"NODE_AGENT_PAIRING_FAILED"}),e}if("string"!=typeof s.data?.nodeId||!s.data.nodeId.trim()||"string"!=typeof s.data?.keyId||!s.data.keyId.trim()||"string"!=typeof s.data?.secret||!s.data.secret.trim()){const e=new Error("Node agent pairing response did not include nodeId, keyId, and secret.");throw Object.assign(e,{statusCode:502,code:"NODE_AGENT_PAIRING_RESPONSE_INVALID"}),e}return{nodeId:s.data.nodeId.trim(),keyId:s.data.keyId.trim(),secret:s.data.secret.trim(),pairedAt:"string"==typeof s.data.pairedAt?s.data.pairedAt:Rr()}}async syncLocalNodeConnection(){const e={[Co]:"true",[xo]:"true"},t=process.env.TASK_HANDOFF_NODE_AGENT_TOKEN?.trim()||void 0,n=process.env.TASK_HANDOFF_NODE_AGENT_CONTROL_ENDPOINT||process.env.TASK_HANDOFF_NODE_AGENT_ENDPOINT||"http://127.0.0.1:8091",s=function(e){return mr(e)?"local-ipc":"local-loopback"}(n),i=fs.parse({id:"node_local_probe",name:"Local Node Probe",connectionMode:s,auth:{mode:"local-static-key",secret:t},labels:e,createdAt:Rr(),updatedAt:Rr()}),r=await this.inspectNodeAgent(n,i),a=Rr(),o=this.nodes.get(r.nodeId),d=this.nodes.list().filter(e=>No(e)&&e.id!==r.nodeId);for(const e of d)this.nodes.delete(e.id);const c=fs.parse({...o||{},id:r.nodeId,name:o?.name||"Local Node",connectionMode:s,auth:{mode:"local-static-key",secret:t},endpoint:n,controlEndpoint:n,containerEndpoint:process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,publicWebBase:process.env.TASK_HANDOFF_INSTANCE_PUBLIC_WEB_BASE,status:"online",health:"ok",capabilities:{...o?.capabilities||{},agent:Br(r.data)},labels:{...o?.labels||{},...e},createdAt:o?.createdAt||a,updatedAt:a});return this.nodes.put(c)}listModels(){return this.listAllModels().map(Wr)}async listFederatedModels(e){const t=this.listNodes(),n=await this.nodeAgentGateway.listFleetModels(t,{signal:e}),s=new Map;for(const e of this.listAllModels())s.set(e.id,{id:e.id,model:Wr(e),locations:[{type:"control-plane",name:e.name,enabled:e.enabled,order:e.order}],referenceCount:0});for(const{nodeId:e,model:t}of n.items){const{referenceCount:n,...i}=t,r=s.get(t.id);r?(r.locations.push({type:"node",nodeId:e,name:t.name,enabled:t.enabled,order:t.order,referenceCount:t.referenceCount}),r.referenceCount+=t.referenceCount):s.set(t.id,{id:t.id,model:i,locations:[{type:"node",nodeId:e,name:t.name,enabled:t.enabled,order:t.order,referenceCount:t.referenceCount}],referenceCount:t.referenceCount})}return Fn.parse({models:[...s.values()].sort((e,t)=>e.model.order-t.model.order||e.model.name.localeCompare(t.model.name)),nodeDiagnostics:n.nodeErrors.map(e=>({nodeId:e.nodeId,code:e.code,message:e.message})),updatedAt:Rr()})}listAllModels(){return this.models.list().map(e=>this.normalizeModelRecord(e)).sort((e,t)=>e.order-t.order||e.createdAt.localeCompare(t.createdAt)||e.id.localeCompare(t.id))}async createModel(e){const t=fo.parse(e),n=Rr(),s=this.nextModelOrder(),i=Hn(t),r=this.models.get(i),a=Dn.parse({...t,id:i,enabled:t.enabled??!0,order:t.order??s,labels:t.labels||{},createdAt:r?.createdAt||n,updatedAt:n});return Wr(this.models.put(a))}async updateModel(e,t){const n=Io.parse(t),s=this.requireModelSecret(e),i=Dn.parse({...s,...n,key:n.key?.trim()?n.key:s.key,createdAt:s.createdAt,updatedAt:Rr()}),r=Hn(i);return Wr(this.models.put(Dn.parse({...i,id:r})))}async deleteModel(e){return this.requireModelSecret(e),this.models.delete(e)}async reorderModels(e){const t=[...new Set(e)],n=new Map(this.models.list().map(e=>[e.id,e]));for(const e of t)n.has(e)||$r("MODEL_NOT_FOUND",`Model ${e} was not found.`);return t.forEach((e,t)=>{const s=n.get(e);this.models.put(Dn.parse({...s,order:100*(t+1),updatedAt:Rr()}))}),this.listModels()}createNodeModel(e,t){return this.nodeAgentGateway.createModel(this.requireNode(e),t)}updateNodeModel(e,t,n){return this.nodeAgentGateway.updateModel(this.requireNode(e),t,n)}deleteNodeModel(e,t){return this.nodeAgentGateway.deleteModel(this.requireNode(e),t)}createProject(e){return this.catalogService.createProject(e)}updateProject(e,t){return this.catalogService.updateProject(e,t)}deleteProject(e){return this.catalogService.deleteProject(e)}listImages(){return this.catalogService.listImages()}getMarketCatalog(){return this.catalogService.getMarketCatalog()}async refreshMarketCatalog(){return await this.marketCatalogService.refresh(new ga),this.getMarketCatalog()}listImageOptions(){return this.catalogService.listImageOptions()}createImage(e){return this.catalogService.createImage(e)}async listNodeDockerImages(e){const t=this.requireNode(e);return this.nodeAgentGateway.listDockerImages(t)}async listNodeImageAvailability(e){const t=this.requireNode(e),n=t.capabilities.agent&&"object"==typeof t.capabilities.agent?t.capabilities.agent:{},s={os:"string"==typeof n.platform?n.platform:void 0,architecture:"string"==typeof n.arch?"x64"===n.arch?"amd64":n.arch:void 0},i=this.catalogService.listImageOptions(s);try{const e=await this.nodeAgentGateway.listDockerImages(t);return i.map(t=>{const n=e.find(e=>e.reference===t.reference||!!t.digest&&e.repoDigests.some(e=>e.endsWith(`@${t.digest}`))||e.repoDigests.includes(t.reference));return Ns.parse({image:t,status:n?"available":"pull-required",localImage:n,localSizeBytes:n?.sizeBytes,downloadSizeBytes:n?void 0:t.downloadSizeBytes,unpackedSizeBytes:n?void 0:t.unpackedSizeBytes})})}catch(e){const t=e instanceof Error?e.message:String(e);return i.map(e=>Ns.parse({image:e,status:"unknown",error:t}))}}async listNodeLocalFolders(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.listLocalFolders(n,{signal:t})}async listNodeFolderTree(e,t={}){const n=this.requireNode(e);return this.nodeAgentGateway.listFolderTree(n,t)}async createNodeLocalFolder(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.createLocalFolder(n,t)}async deleteNodeLocalFolder(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteLocalFolder(n,t)}async checkNode(e){const t=this.requireNode(e);try{const e=await this.nodeAgentGateway.health(t),n=e&&"object"==typeof e?e:{},s="string"==typeof n.nodeId&&n.nodeId.trim()?n.nodeId.trim():t.id,i=fs.parse({...t,id:s,status:"online",health:"ok",capabilities:"object"==typeof e&&e?{...t.capabilities,agent:Br(e)}:t.capabilities,lastSeenAt:Rr(),updatedAt:Rr()});return s!==t.id&&this.nodes.delete(t.id),this.nodes.put(i),{id:i.id,status:"online",checkedAt:Rr(),agent:e}}catch(e){const n=fs.parse({...t,status:"offline",health:"failed",updatedAt:Rr()});return this.nodes.put(n),{id:t.id,status:"offline",checkedAt:Rr(),error:e instanceof Error?e.message:String(e)}}}async checkNodeUpdate(e,t){return this.nodeAgentGateway.checkUpdate(this.requireNode(e),t)}async applyNodeUpdate(e,t){return this.nodeAgentGateway.applyUpdate(this.requireNode(e),t)}async listNodeUpdateJobs(e){return this.nodeAgentGateway.listUpdateJobs(this.requireNode(e))}async getLocalNodeExternalListener(e){return this.nodeAgentGateway.getExternalListener(this.requireLocalListenerNode(e))}async updateLocalNodeExternalListener(e,t){return this.nodeAgentGateway.updateExternalListener(this.requireLocalListenerNode(e),t)}async createNodePairingInvite(e,t={}){const n=this.requireNode(e);return this.nodeAgentGateway.createPairingInvite(n,t)}requireLocalListenerNode(e){const t=this.requireNode(e);if(!Eo(t)||!No(t)){const e=new Error("Node agent TCP listener settings are available only for the built-in local node.");throw Object.assign(e,{statusCode:403,code:"LOCAL_NODE_LISTENER_ONLY"}),e}return t}pruneNodeJoinInvites(){const e=Date.now();for(const t of this.nodeJoinInvites.list())Date.parse(t.expiresAt)<=e&&this.nodeJoinInvites.delete(t.id)}createNodeJoinInvite(e={}){this.pruneNodeJoinInvites();const t=So.parse(e&&"object"==typeof e?e:{}),n=Rr(),s=si(),i={id:ni("node_join"),tokenHash:o.createHash("sha256").update(s).digest("hex"),expiresAt:new Date(Date.now()+(t.expiresInMs||6e5)).toISOString(),...t.nodeName?{nodeName:t.nodeName}:{},createdAt:n,updatedAt:n};return this.nodeJoinInvites.put(i),{id:i.id,joinToken:s,expiresAt:i.expiresAt}}completeNodeJoin(e){this.pruneNodeJoinInvites();const t=a.z.object({joinToken:a.z.string().trim().min(1).max(4096),nodeId:fs.shape.id,nodeName:fs.shape.name.optional(),keyId:a.z.string().trim().min(1).max(160),secret:a.z.string().trim().min(1).max(4096),pairedAt:a.z.string().datetime().optional()}).parse(e),n=o.createHash("sha256").update(t.joinToken).digest("hex"),s=this.nodeJoinInvites.list().find(e=>e.tokenHash===n&&Date.parse(e.expiresAt)>Date.now());if(!s){const e=new Error("Node join token is invalid or expired.");throw Object.assign(e,{statusCode:401,code:"NODE_JOIN_TOKEN_INVALID"}),e}this.nodeJoinInvites.delete(s.id);const i=Rr();if(this.nodes.get(t.nodeId)){const e=new Error(`Node ${t.nodeId} already exists in this control-plane.`);throw Object.assign(e,{statusCode:409,code:"NODE_JOIN_NODE_ALREADY_EXISTS"}),e}const r=fs.parse({id:t.nodeId,name:t.nodeName||s.nodeName||t.nodeId,connectionMode:"reverse-wss",auth:{mode:"paired-hmac",keyId:t.keyId,secret:t.secret,pairedAt:t.pairedAt||i,pairing:{status:"paired"}},status:"unknown",health:"unknown",capabilities:{},labels:{},createdAt:i,updatedAt:i});return this.nodes.put(r)}async connectNodeToControlPlane(e,t){const n=this.requireNode(e),s=wo.parse(t);return this.nodeAgentGateway.createControlPlaneConnection(n,s)}async listNodeControlPlanePairings(e){const t=this.requireNode(e);return this.nodeAgentGateway.listControlPlanePairings(t)}async deleteNodeControlPlanePairing(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteControlPlanePairing(n,t)}async listNodeControlPlaneConnections(e){const t=this.requireNode(e);return this.nodeAgentGateway.listControlPlaneConnections(t)}async deleteNodeControlPlaneConnection(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteControlPlaneConnection(n,t)}updateImage(e,t){return this.catalogService.updateImage(e,t)}async deleteImage(e){this.catalogService.requireImage(e);const t=this.listProjects().find(t=>t.defaultImageSelection?.imageId===e);if(t)throw Object.assign(new Error(`Image ${e} is the default for project ${t.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const n=(await Promise.all(this.listNodes().map(e=>this.nodeAgentGateway.listLocalFolders(e)))).flat().find(t=>t.defaultImageSelection?.imageId===e);if(n)throw Object.assign(new Error(`Image ${e} is the default for local folder ${n.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const s=(await this.listNodeInstances()).find(t=>t.imageSelection?.imageId===e);if(s)throw Object.assign(new Error(`Image ${e} is used by instance ${s.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});return this.catalogService.deleteImage(e)}listNodes(){return this.nodes.list()}listPublicNodes(){return this.listNodes().map(Ur)}async createNode(e){const t=bo.parse(e),n=Rr(),s=t.connectionMode||"direct-http";let i="auth"in(e&&"object"==typeof e&&!Array.isArray(e)?e:{})?t.auth:{mode:"local-ipc"===s||"local-loopback"===s?"local-static-key":"paired-hmac"};if(("direct-http"===s||"reverse-wss"===s)&&"paired-hmac"!==i.mode){const e=new Error("Remote node connections require paired-HMAC authentication.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_REQUIRES_PAIRED_HMAC"}),e}const r=t.controlEndpoint||t.endpoint;if(!r&&"reverse-wss"!==s){const e=new Error("Node agent direct HTTP mode requires an endpoint.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"}),e}const a=t.joinToken&&r?await this.completeNodeAgentPairing(r,{joinToken:t.joinToken}):void 0;if(a&&(i={mode:"paired-hmac",keyId:a.keyId,secret:a.secret,pairedAt:a.pairedAt,pairing:{status:"paired"}}),("direct-http"===s||"reverse-wss"===s)&&!i.secret){const e=new Error("Remote node connections require a paired node secret or join token.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_SECRET_REQUIRED"}),e}if(("direct-http"===s||"reverse-wss"===s)&&!i.keyId){const e=new Error("Remote node connections require a paired key id.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_KEY_ID_REQUIRED"}),e}const o=t.id||a?.nodeId||ni("node"),d=fs.parse({id:o,name:t.name,connectionMode:s,auth:i,labels:t.labels||{},createdAt:n,updatedAt:n}),c="reverse-wss"===s&&(t.id||a?.nodeId)?void 0:await this.inspectNodeAgent(r||"",d),l=t.id||a?.nodeId||c?.nodeId||o;if(!l){const e=new Error("Node id is required.");throw Object.assign(e,{statusCode:400,code:"NODE_ID_REQUIRED"}),e}if(t.id&&c?.nodeId&&c.nodeId!==t.id){const e=new Error(`Node agent id ${c.nodeId} does not match requested node id ${t.id}.`);throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ID_MISMATCH"}),e}const{joinToken:p,...u}=t,g=fs.parse({...u,id:l,connectionMode:s,auth:i,endpoint:r,controlEndpoint:r,status:t.status||(c?"online":"unknown"),health:t.health||(c?"ok":"unknown"),capabilities:{...t.capabilities||{},...c?{agent:Br(c.data)}:{}},labels:t.labels||{},createdAt:n,updatedAt:n});return this.nodes.put(g)}listPendingProxyClaims(){return this.proxyPrivateStore.publicPendingClaims()}async claimProxyNode(e){const t=To.parse(e),n=Rr(),s=ni("proxy_claim"),i={id:s,claimId:s,proxyOrigin:t.proxyOrigin,...t.name?{requestedName:t.name}:{},sourceControlPlaneId:this.proxyPrivateStore.controlPlaneId(),bindingKeyId:ni("proxy_key"),credential:si(),status:"pending",createdAt:n,updatedAt:n,expiresAt:new Date(Date.now()+864e5).toISOString()};return this.proxyPrivateStore.putPendingClaim(i),this.withProxyClaimLock(i.claimId,()=>this.completeProxyClaim(i,t.inviteToken))}async resumeProxyClaim(e){return this.withProxyClaimLock(e,async()=>{const t=this.proxyPrivateStore.pendingClaimByClaimId(e);return t||$r("CONTROL_PLANE_PROXY_CLAIM_NOT_FOUND",`Proxy claim ${e} was not found.`),this.completeProxyClaim(t)})}async cancelProxyClaim(e){return this.withProxyClaimLock(e,async()=>{const t=this.proxyPrivateStore.pendingClaimByClaimId(e);if(!t)return{deleted:!1,compensationRequired:!1,remoteRevoke:"not-required"};if("pending"===t.status)return{...this.proxyPrivateStore.cancelPendingClaim(e,!1),remoteRevoke:"not-required"};let n;try{n=await this.requestProxyClaim(t)}catch(e){throw this.proxyCompensationError(t,"Trusted control-plane proxy is unavailable; claim cancellation requires retry.",e)}const s=await this.proxyResponsePayload(n);if(!n.ok){const i=s?.error?.code;if(i===at.InviteInvalid||i===at.BindingRevoked)return{deleted:this.proxyPrivateStore.completePendingClaimCompensation(e),compensationRequired:!1,remoteRevoke:i===at.BindingRevoked?"already-revoked":"not-created"};throw this.proxyCompensationError(t,s?.error?.message||`Proxy claim recovery failed with HTTP ${n.status}.`,void 0,n.status,i,s?.error?.retryable)}const i=Ct.parse(s?.data);this.requireProxyClaimResultIdentity(t,i);const r={id:`proxy_compensation_${i.binding.id}`,nodeId:i.target.id,proxyOrigin:t.proxyOrigin,proxyBindingId:i.binding.id,targetNodeId:i.target.id,sourceControlPlaneId:t.sourceControlPlaneId,bindingKeyId:t.bindingKeyId,credential:t.credential,createdAt:t.createdAt,updatedAt:Rr()};let a;try{a=await this.fetchImpl(new URL(`/api/node-proxy/bindings/${encodeURIComponent(i.binding.id)}`,t.proxyOrigin),{method:"DELETE",headers:Cr(r)})}catch(e){throw this.proxyCompensationError(t,"Trusted control-plane proxy is unavailable; binding revocation requires retry.",e)}const o=await this.proxyResponsePayload(a);if(!a.ok&&o?.error?.code!==at.BindingRevoked)throw this.proxyCompensationError(t,o?.error?.message||`Proxy binding revoke failed with HTTP ${a.status}.`,void 0,a.status,o?.error?.code,o?.error?.retryable);if(a.ok)try{this.requireProxyRevocationReceipt(o,r)}catch(e){throw this.proxyCompensationError(t,"Proxy binding revocation returned an invalid receipt; compensation requires retry.",e,502,at.TransportFailed,!0)}return{deleted:this.proxyPrivateStore.completePendingClaimCompensation(e),compensationRequired:!1,remoteRevoke:a.ok?"revoked":"already-revoked"}})}async completeProxyClaim(e,t){let n;this.proxyPrivateStore.markCompensationRequired(e.claimId);try{n=await this.requestProxyClaim(e,t)}catch(t){const n=new Error(`Trusted control-plane proxy ${e.proxyOrigin} is unavailable.`,{cause:t});throw Object.assign(n,{statusCode:503,code:"CONTROL_PLANE_PROXY_UNAVAILABLE",retryable:!0,claimId:e.claimId}),n}const s=await this.proxyResponsePayload(n);if(!n.ok){const t=s?.error?.retryable??n.status>=500;t||this.proxyPrivateStore.completePendingClaimCompensation(e.claimId);const i=new Error(s?.error?.message||`Proxy claim failed with HTTP ${n.status}.`);throw Object.assign(i,{statusCode:n.status,code:s?.error?.code||"CONTROL_PLANE_PROXY_CLAIM_FAILED",retryable:t,details:s?.error?.details,claimId:e.claimId}),i}const i=Ct.parse(s?.data);this.requireProxyClaimResultIdentity(e,i);const r=this.nodes.get(i.target.id),a="control-plane-proxy"===r?.connectionMode&&"control-plane-proxy"===r.connectionPath.kind&&r.connectionPath.proxyBindingId===i.binding.id&&r.connectionPath.targetNodeId===i.target.id;if(r&&!a){this.proxyPrivateStore.markCompensationRequired(e.claimId);const t=new Error(`Node ${i.target.id} already exists with another connection identity.`);throw Object.assign(t,{statusCode:409,code:"CONTROL_PLANE_PROXY_NODE_IDENTITY_CONFLICT",claimId:e.claimId}),t}const o={id:`proxy_credential_${i.target.id}`,nodeId:i.target.id,proxyOrigin:e.proxyOrigin,proxyBindingId:i.binding.id,targetNodeId:i.target.id,sourceControlPlaneId:e.sourceControlPlaneId,bindingKeyId:e.bindingKeyId,credential:e.credential,createdAt:e.createdAt,updatedAt:Rr()},d=r||fs.parse({id:i.target.id,name:e.requestedName||i.target.name,connectionMode:"control-plane-proxy",connectionPath:{kind:"control-plane-proxy",proxyId:new URL(e.proxyOrigin).host,proxyBindingId:i.binding.id,targetNodeId:i.target.id},connectionEnabled:!0,auth:{mode:"proxy-binding"},status:i.target.status,health:i.target.health,capabilities:i.target.capabilities,proxyState:{reachability:"reachable",bindingStatus:i.binding.status,bindingRevision:i.binding.revision,observedAt:Rr(),target:i.target,updatedAt:Rr()},labels:{},lastSeenAt:i.target.lastSeenAt,createdAt:Rr(),updatedAt:Rr()});r||this.nodes.put(d);try{this.proxyPrivateStore.promotePendingClaim(e.claimId,o)}catch(e){throw r||this.nodes.delete(d.id),e}return{node:Ur(d),binding:i.binding}}applyProxyTargetSnapshot(e,t){const n=this.requireProxyNodeIdentity(e,t.binding.id,t.binding.targetNodeId);return this.putProxyTargetState(n,t.target,{reachability:"reachable",bindingStatus:t.binding.status,bindingRevision:t.binding.revision,streamId:t.streamId,revision:t.revision,observedAt:t.observedAt})}applyProxyTargetEvent(e,t){const n=this.requireProxyNodeIdentity(e,void 0,t.targetNodeId),s=n.proxyState;if(!s?.streamId||s.streamId!==t.streamId||void 0===s.revision||t.revision!==s.revision+1){const e=new Error("Proxy target event does not continue the authoritative snapshot cursor.");throw Object.assign(e,{code:at.SnapshotRequired,statusCode:409,retryable:!0}),e}return this.putProxyTargetState(n,t.target,{...s,reachability:"reachable",bindingStatus:"revoked"===s.bindingStatus?"revoked":"active",revision:t.revision,observedAt:t.event.createdAt})}markProxyUnavailable(e,t){const n=this.requireProxyNodeIdentity(e),s=Rr();return this.nodes.put(fs.parse({...n,status:"degraded",health:"degraded",proxyState:{...n.proxyState,reachability:"unreachable",bindingStatus:n.proxyState?.bindingStatus??"unknown",lastError:t,updatedAt:s},updatedAt:s}))}markProxyBindingRevoked(e,t){const n=this.requireProxyNodeIdentity(e),s=Rr();return this.nodes.put(fs.parse({...n,status:"degraded",health:"degraded",proxyState:{...n.proxyState,reachability:"reachable",bindingStatus:"revoked",lastError:t,updatedAt:s},updatedAt:s}))}requireProxyNodeIdentity(e,t,n){const s=this.requireNode(e);if("control-plane-proxy"!==s.connectionMode||"control-plane-proxy"!==s.connectionPath.kind||void 0!==t&&s.connectionPath.proxyBindingId!==t||void 0!==n&&s.connectionPath.targetNodeId!==n){const e=new Error("Proxy state identity does not match the node connection path.");throw Object.assign(e,{code:at.BindingIdentityConflict,statusCode:409,retryable:!1}),e}return s}putProxyTargetState(e,t,n){const s=Rr();return this.nodes.put(fs.parse({...e,name:t.name,status:t.status,health:t.health,capabilities:t.capabilities,lastSeenAt:t.lastSeenAt,proxyState:{...n,target:t,updatedAt:s},updatedAt:s}))}requestProxyClaim(e,t){return this.fetchImpl(new URL("/api/node-proxy/claims",e.proxyOrigin),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({protocolVersion:Ge,...t?{inviteToken:t}:{},claimId:e.claimId,sourceControlPlaneId:e.sourceControlPlaneId,...e.targetNodeId?{targetNodeId:e.targetNodeId}:{},bindingKeyId:e.bindingKeyId,credential:e.credential})})}async proxyResponsePayload(e){return e.json().catch(()=>{})}requireProxyRevocationReceipt(e,t){const n=zo.safeParse(e),s=n.success?n.data.data.binding:void 0;if(!s||s.id!==t.proxyBindingId||s.sourceControlPlaneId!==t.sourceControlPlaneId||s.targetNodeId!==t.targetNodeId||s.bindingKeyId!==t.bindingKeyId||"revoked"!==s.status){const e=new Error("Proxy binding revocation receipt does not match the local binding identity.");throw Object.assign(e,{statusCode:502,code:at.TransportFailed,retryable:!0,details:{bindingId:t.proxyBindingId}}),e}return s}requireProxyClaimResultIdentity(e,t){if(t.binding.claimId!==e.claimId||t.binding.sourceControlPlaneId!==e.sourceControlPlaneId||t.binding.bindingKeyId!==e.bindingKeyId||t.binding.targetNodeId!==t.target.id||void 0!==e.targetNodeId&&t.binding.targetNodeId!==e.targetNodeId){const t=new Error("Proxy claim response does not match the persisted pending identity.");throw Object.assign(t,{statusCode:409,code:at.BindingIdentityConflict,retryable:!1,claimId:e.claimId,compensationRequired:!0}),t}}proxyCompensationError(e,t,n,s=503,i="CONTROL_PLANE_PROXY_COMPENSATION_REQUIRED",r=!0){const a=new Error(t,void 0===n?void 0:{cause:n});return Object.assign(a,{statusCode:s,code:i,retryable:r,claimId:e.claimId,compensationRequired:!0}),a}async withProxyClaimLock(e,t){const n=(this.proxyClaimOperations.get(e)??Promise.resolve()).catch(()=>{}).then(t);this.proxyClaimOperations.set(e,n);try{return await n}finally{this.proxyClaimOperations.get(e)===n&&this.proxyClaimOperations.delete(e)}}updateNode(e,t){const n=vo.parse(t),s=this.requireNode(e);this.requireGenericNodePatchPreservesProxyIdentity(s,n);const i=fs.parse({...s,...n,id:e,createdAt:s.createdAt,updatedAt:Rr()});return this.nodes.put(i)}requireGenericNodePatchPreservesProxyIdentity(e,t){const n=t.connectionMode??e.connectionMode,s=t.connectionPath??e.connectionPath,i=t.auth??e.auth,r="control-plane-proxy"===e.connectionMode||"control-plane-proxy"===e.connectionPath.kind||"proxy-binding"===e.auth.mode,a="control-plane-proxy"===n||"control-plane-proxy"===s.kind||"proxy-binding"===i.mode,o=n!==e.connectionMode||JSON.stringify(s)!==JSON.stringify(e.connectionPath)||JSON.stringify(i)!==JSON.stringify(e.auth);if((r||a)&&o){const e=new Error("Proxy node connection identity can only be changed through the control-plane proxy lifecycle API.");throw Object.assign(e,{statusCode:409,code:"CONTROL_PLANE_PROXY_IDENTITY_IMMUTABLE",retryable:!1}),e}}deleteNode(e){if(Eo(this.requireNode(e))){const e=new Error("The built-in local node connection cannot be deleted.");throw Object.assign(e,{statusCode:400,code:"LOCAL_NODE_CANNOT_BE_DELETED"}),e}return this.nodes.delete(e)}async deleteNodeWithProxyLifecycle(e,t=!1){if("control-plane-proxy"!==this.requireNode(e).connectionMode)return{deleted:this.deleteNode(e),revoke:{mode:"not-proxied",orphanRisk:!1}};const n=this.proxyPrivateStore.nodeCredential(e);if(!n){if(!t){const e=new Error("Proxy node credential is missing; remote binding cannot be revoked.");throw Object.assign(e,{statusCode:409,code:"CONTROL_PLANE_PROXY_CREDENTIAL_REQUIRED",retryable:!1,details:{forceDeleteAllowed:!0,forceDeleteReason:"credential-missing"}}),e}return this.forceDeleteProxyNode(e)}let s;try{s=await this.fetchImpl(new URL(`/api/node-proxy/bindings/${encodeURIComponent(n.proxyBindingId)}`,n.proxyOrigin),{method:"DELETE",headers:Cr(n)})}catch(n){if(t)return this.forceDeleteProxyNode(e);const s=new Error("Trusted control-plane proxy is unavailable; the binding was not revoked.",{cause:n});throw Object.assign(s,{statusCode:503,code:"CONTROL_PLANE_PROXY_REVOKE_UNAVAILABLE",retryable:!0,details:{forceDeleteAllowed:!0,forceDeleteReason:"proxy-unavailable"}}),s}if(!s.ok){const t=await s.json().catch(()=>{});if(t?.error?.code===at.BindingRevoked){const s=t.error.details?.bindingId;if(s===n.proxyBindingId){const t=this.deleteNode(e);return this.proxyPrivateStore.deleteNodeCredential(e),{deleted:t,revoke:{mode:"revoked",orphanRisk:!1}}}}const i=new Error(t?.error?.message||`Proxy binding revoke failed with HTTP ${s.status}.`);throw Object.assign(i,{statusCode:s.status,code:t?.error?.code||"CONTROL_PLANE_PROXY_REVOKE_FAILED",retryable:t?.error?.retryable??s.status>=500,details:{...t?.error?.details||{},forceDeleteAllowed:!1}}),i}const i=await s.json().catch(()=>{});try{this.requireProxyRevocationReceipt(i,n)}catch(e){if(e&&"object"==typeof e){const t=e;t.details={...t.details||{},forceDeleteAllowed:!1}}throw e}const r=this.deleteNode(e);return this.proxyPrivateStore.deleteNodeCredential(e),{deleted:r,revoke:{mode:"revoked",orphanRisk:!1}}}forceDeleteProxyNode(e){const t=this.deleteNode(e);return this.proxyPrivateStore.deleteNodeCredential(e),{deleted:t,revoke:{mode:"forced",orphanRisk:!0}}}async listNodeRuntimes(e,t){const n=e?[this.requireNode(e)]:this.listNodes();return e?this.nodeAgentGateway.listRuntimes(n[0],{signal:t}):(await this.nodeAgentGateway.listFleetRuntimes(n,{signal:t})).items}async listNodeRuntimesWithDiagnostics(e){return this.nodeAgentGateway.listFleetRuntimes(this.listNodes(),{signal:e})}async createNodeRuntime(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.createRuntime(n,t)}async updateNodeRuntime(e,t,n){const s=this.requireNode(e);return this.nodeAgentGateway.updateRuntime(s,t,n)}async deleteNodeRuntime(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteRuntime(n,t)}async checkNodeRuntime(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.checkRuntime(n,t)}async listControlledInstances(){return(await this.listNodeInstances()).map(Hr)}async listControlledInstancesWithDiagnostics(){const e=await this.listNodeInstancesWithDiagnostics();return{items:e.items.map(Hr),nodeErrors:e.nodeErrors}}async instanceResourceMetrics(e){const t=await this.requireNodeInstance(e);return this.nodeAgentGateway.instanceResourceMetrics(this.requireNode(t.nodeId),t.id)}async nodeOwnsInstance(e,t){const n=this.requireNode(e);return(await this.nodeAgentGateway.listInstances(n)).some(n=>n.id===t&&n.nodeId===e)}async createControlledInstance(e){const t=ho.parse(e),n=t.projectId?this.requireProject(t.projectId):void 0,s=t.runtimeId||n?.defaultRuntimeId||"runtime_local_docker",i=t.nodeId||n?.defaultNodeId||this.defaultNodeId();if(!i){const e=new Error("At least one node connection is required before creating an instance.");throw Object.assign(e,{statusCode:400,code:"NODE_REQUIRED"}),e}const r=this.requireNode(i),a=await this.requireNodeRuntimeOnNode(r.id,s),o="boolean"==typeof a.capabilities.requiresImage?a.capabilities.requiresImage:"local"!==a.type,d=t.imageSelection||(o?n?.defaultImageSelection:void 0),c=d?this.catalogService.resolveImageSelection(d):void 0;if(o&&!c){const e=new Error(`Runtime ${a.name} requires an image.`);throw Object.assign(e,{statusCode:400,code:"RUNTIME_IMAGE_REQUIRED"}),e}let l=t.source||n?.source,p=t.sourceSnapshot||n||{};if(!l){const e=new Error("Instance source is required.");throw Object.assign(e,{statusCode:400,code:"INSTANCE_SOURCE_REQUIRED"}),e}if("local"===a.type&&"local-folder"!==l.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-folder"===l.type){if(l.ownerNodeId&&l.ownerNodeId!==r.id){const e=new Error(`Local folder ${l.path} belongs to node ${l.ownerNodeId}, not ${r.id}.`);throw Object.assign(e,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_OWNER_NODE"}),e}if(l.localFolderId){const e=await this.requireNodeLocalFolder(r,l.localFolderId);l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}else{const e=await this.nodeAgentGateway.createLocalFolder(r,{name:"string"==typeof p.name?p.name:"Local folder",path:l.path,defaultImageSelection:c?{imageId:c.id,tag:c.tag}:void 0});l={...l,localFolderId:e.id,ownerNodeId:e.nodeId,path:e.path},p=e}}const u=await this.prepareInstanceModels(r,t.modelSelection||{}),g=Rr(),m=c?{id:c.id,origin:c.origin,name:c.name,description:c.description,localizedDescriptions:c.localizedDescriptions,cover:c.cover,repository:c.repository,tag:c.tag,requestedReference:c.reference,resolvedDigest:c.digest,resolvedReference:c.digest?`${c.repository}@${c.digest}`:void 0,downloadSizeBytes:c.downloadSizeBytes,pullPolicy:"if-not-present",capabilities:c.capabilities,optionalApps:c.optionalApps,defaultEnv:c.defaultEnv,labels:c.labels,market:c.market,createdAt:g,updatedAt:g}:void 0,h=await this.nodeAgentGateway.createInstance(r,{id:t.id,name:t.name,runtimeId:s,...c&&m?{imageSelection:{imageId:c.id,tag:c.tag},image:m}:{},projectId:n?.id,source:l,sourceSnapshot:p,config:t.config,modelSelection:{}});let y=h;try{y=(await this.nodeAgentGateway.assignInstanceModels(r,h.id,u)).instance}catch(e){throw await this.nodeAgentGateway.deleteInstance(r,h.id).catch(()=>{}),e}let f={status:"not-requested"};if(t.start)try{y=await this.nodeAgentGateway.startInstance(r,y.id),f={status:"started"}}catch(e){f={status:"failed",error:Po(e,"INSTANCE_START_FAILED")},y=await this.nodeAgentGateway.listInstances(r).then(e=>e.find(e=>e.id===y.id)||y).catch(()=>y)}return{...Hr(y),registrationToken:y.registrationToken,startOutcome:f}}async updateControlledInstance(e,t){const n=yo.parse(t),s=await this.requireNodeInstance(e),i=this.requireNode(s.nodeId),{modelSelection:r,...a}=n;let o=Object.keys(a).length?await this.nodeAgentGateway.updateInstance(i,e,a):s;if(r){const t=await this.prepareInstanceModels(i,r);o=(await this.nodeAgentGateway.assignInstanceModels(i,e,t)).instance}return Hr(o)}async deleteControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId),s=await this.nodeAgentGateway.deleteInstance(n,e);return this.configSyncPreferences.delete(e),Boolean(s.deleted)}async startControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),Hr(await this.nodeAgentGateway.startInstance(n,e))}async stopControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId);return Hr(await this.nodeAgentGateway.stopInstance(n,e))}async restartControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId);return await this.ensureInstanceModelAssignment(t),Hr(await this.nodeAgentGateway.restartInstance(n,e))}async retryControlledInstanceImageProvisioning(e){const t=await this.requireNodeInstance(e);return Hr(await this.nodeAgentGateway.retryInstanceImageProvisioning(this.requireNode(t.nodeId),e))}async boardAsync(){return(await this.boardWithDiagnostics()).items}async boardWithDiagnostics(e){const[t,n]=await Promise.all([this.listNodeRuntimesWithDiagnostics(e),this.listNodeInstancesWithDiagnostics(e)]);return this.instanceBoardReader.read({projects:this.listProjects(),images:this.listImageOptions(),nodes:this.nodes.list(),runtimes:t.items,instances:n.items,nodeErrors:[...t.nodeErrors,...n.nodeErrors]})}async bootstrapAiSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!Gs(e)||!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus)return{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions};try{return Se.parse(await this.instanceRequest(e,"/ai-sessions/state"))}catch(t){return this.logWarn({instanceId:e.id,error:Mr(t),errorCode:"AI_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live AI sessions"),{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,n)=>({instanceId:e.id,streamId:t[n].streamId,aiSessions:t[n].snapshot,revision:t[n].revision,lastEventAt:t[n].lastEventAt}))}}async bootstrapAppSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!Gs(e)||!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=Xi();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:t.updatedAt,snapshot:t}}try{return Di.parse(await this.instanceRequest(e,"/apps/sessions/state"))}catch(t){this.logWarn({instanceId:e.id,error:Mr(t),errorCode:"APP_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live app sessions");const n=Xi();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:n.updatedAt,snapshot:n}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,n)=>({instanceId:e.id,streamId:t[n].streamId,appSessions:t[n].snapshot,revision:t[n].revision,lastEventAt:t[n].lastEventAt}))}}async recoverAiSessionDelta(e,t,n){const s=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(n)});return _e.parse(await this.instanceRequest(s,`/ai-sessions?${i}`))}async recoverAiSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return Se.parse(await this.instanceRequest(t,"/ai-sessions/state"))}async recoverAppSessionDelta(e,t,n){const s=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(n)});return Ui.parse(await this.instanceRequest(s,`/apps/sessions?${i}`))}async recoverAppSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return Di.parse(await this.instanceRequest(t,"/apps/sessions/state"))}async listTriggers(){return this.controlPlaneTriggerService.listTriggers()}createTrigger(e){return this.controlPlaneTriggerService.createTrigger(e)}async deleteTrigger(e){return this.controlPlaneTriggerService.deleteTrigger(e)}async applyTrigger(e,t){return this.controlPlaneTriggerService.applyTrigger(e,t)}async listInstanceTriggers(e){return this.controlPlaneTriggerService.listInstanceTriggers(e)}async bindAiSessionTrigger(e,t,n){return this.controlPlaneTriggerService.bindAiSessionTrigger(e,t,n)}async unbindAiSessionTrigger(e,t,n){return this.controlPlaneTriggerService.unbindAiSessionTrigger(e,t,n)}async runInstanceTrigger(e,t,n={}){return this.controlPlaneTriggerService.runInstanceTrigger(e,t,n)}listChatSessions(){return this.chatSessionService.list()}createAppAccessToken(e){return this.appAccessService.createToken(e)}resolveAppAccessToken(e,t){return this.appAccessService.resolveToken(e,t)}createChatActionToken(e){return this.chatActionTokenService.create(e)}resolveChatActionToken(e,t){return this.chatActionTokenService.resolve(e,t)}pendingDecisionCallbackData(e,t){return this.chatActionTokenService.pendingDecisionCallbackData(e,t)}async appAccessProxyTarget(e,t,n=""){return this.appAccessService.proxyTarget(e,t,n)}listChatBridges(){return this.chatBridgeService.list()}requireChatBridge(e){return this.chatBridgeService.require(e)}createChatBridge(e){return this.chatBridgeService.create(e)}updateChatBridge(e,t){return this.chatBridgeService.update(e,t)}deleteChatBridge(e){return this.chatBridgeService.delete(e)}requireChatSession(e){return this.chatSessionService.require(e)}upsertChatSession(e){return this.chatSessionService.upsert(e)}async handleChatGatewayMessage(e){return this.chatSessionRuntime.handleChatGatewayMessage(e)}async handleChatGatewayAction(e){return this.chatSessionRuntime.handleChatGatewayAction(e)}async handleChatCommand(e,t){return this.chatSessionRuntime.handleChatCommand(e,t)}async listPendingRoutes(){const e=Rr(),t=[],n=await this.listAiSessions(),s=new Map(n.instances.map(e=>[e.instanceId,e.aiSessions])),i=await this.nodeAgentGateway.listFleetInstances(this.listNodes());for(const n of i.items){if("online"!==n.connectionStatus&&"online"!==n.agentStatus||!n.target.web)continue;const i=n.projectId?this.catalogService.getProject(n.projectId):void 0;for(const r of s.get(n.id)?.sessions||[]){if("waiting"!==r.status)continue;const s=Ws.parse({id:`${n.id}:ai:${r.id}`,instanceId:n.id,projectId:n.projectId,aiSessionId:r.id,providerSessionId:r.providerSessionId,result:r.summary||r.currentTool?.inputPreview||r.lastMessage||`${r.agent} is waiting for approval`,source:r.agent,kind:"approval",status:"pending",lastSeenAt:e});t.push({...s,project:i,instance:Hr(n)})}}return t}async listAiSessionInstanceNames(){return(await this.nodeAgentGateway.listFleetInstances(this.listNodes())).items.map(e=>({id:e.id,name:e.name}))}async resolveAiSessionApproval(e,t,n){const s=await this.requireControlledInstance(e,!0);return he.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/approval`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({decision:n})}))}async listAiSessionHistory(e){const t=await this.requireControlledInstance(e,!0);return fe.parse(await this.instanceRequest(t,"/ai-sessions/history"))}async getAiSessionHistoryDetail(e,t){const n=await this.requireControlledInstance(e,!0);return Ie.parse(await this.instanceRequest(n,`/ai-sessions/history/${encodeURIComponent(t)}`))}async resumeAiSession(e,t){const n=await this.requireControlledInstance(e,!0),s=be.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/resume`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}));return await Promise.all([this.listAppSessions({refresh:!0}),this.listAiSessions({refresh:!0})]),s}async launchAppSession(e,t="terminal-tty",n={}){const s=await this.requireControlledInstance(e,!0);if("online"!==s.connectionStatus&&"online"!==s.agentStatus){const e=new Error(`Instance ${s.name} is still starting. Wait for it to connect before launching apps.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_NOT_CONNECTED"}),e}const i=await this.resolveAppLaunchOptions(s,n),r=await this.instanceRequest(s,"/apps/sessions",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appId:t,...i})});return await this.listAppSessions({refresh:!0}),r}async stopAppSession(e,t){const n=await this.requireControlledInstance(e,!0),s=await this.instanceRequest(n,`/apps/sessions/${encodeURIComponent(t)}/stop`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});return await this.listAppSessions({refresh:!0}),s}async instanceAppManagement(e){const t=await this.requireControlledInstance(e,!0);try{return function(e){try{return mn.parse(e)}catch(e){if(e instanceof a.z.ZodError){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}(await this.instanceRequest(t,"/apps/management"))}catch(e){const t=e&&"object"==typeof e?e:{};if(e instanceof a.z.ZodError||404===t.statusCode){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}async requestInstanceAppOperation(e,t,n,s={}){const i=await this.requireControlledInstance(e,!0);return yn.parse(await this.instanceRequest(i,`/apps/${encodeURIComponent(t)}/${n}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}))}async instanceAppManagementJob(e,t){const n=await this.requireControlledInstance(e,!0);return yn.parse(await this.instanceRequest(n,`/apps/jobs/${encodeURIComponent(t)}`))}async renameAppSession(e,t,n){const s=await this.requireControlledInstance(e,!0),i=await this.instanceRequest(s,`/apps/sessions/${encodeURIComponent(t)}`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({title:n})});return await this.listAppSessions({refresh:!0}),i}async sendAiSessionMessage(e,t,n,s,i=[],r=[],a){!function(e){if(e.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)<=k)return;const t=new Error("Inline attachments must be 41943040 bytes or less in total.");throw Object.assign(t,{statusCode:400,code:"AI_SESSION_ATTACHMENTS_TOO_LARGE"}),t}(i);const o=await this.requireControlledInstance(e,!0);i.some(e=>"runtime-path"===e.source.type)&&function(e,t){if("local"===t||!e.some(e=>"runtime-path"===e.source.type))return;const n=new Error("Runtime path attachments are currently available only for Local Runtime instances.");throw Object.assign(n,{statusCode:400,code:"AI_SESSION_RUNTIME_PATH_UNSUPPORTED"}),n}(i,(await this.requireNodeRuntimeOnNode(o.nodeId,o.runtimeId)).type);const d=o.aiSessions.sessions.find(e=>e.id===t),c=a||("codex"===d?.agent?o.config.defaultCodexPermissionMode:void 0),l={message:n,...s?{mode:s}:{},...i.length?{attachments:i}:{},...r.length?{references:r}:{},...c?{permissionMode:c}:{}};return he.parse(await this.instanceRequest(o,`/ai-sessions/${encodeURIComponent(t)}/messages`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(l)}))}async aiSessionMentionCatalog(e,t){const n=await this.requireControlledInstance(e,!0);return ne.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/mentions`))}async searchAiSessionMentionFiles(e,t,n){const s=await this.requireControlledInstance(e,!0);return ie.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/mentions/files`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:n})}))}async executeAiSessionCommand(e,t,n){const s=await this.requireControlledInstance(e,!0);return J.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/commands`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}))}async aiSessionQueue(e,t){const n=await this.requireControlledInstance(e,!0);return ce.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/queue`))}async steerAiSessionQueuedMessage(e,t,n){const s=await this.requireControlledInstance(e,!0);return he.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(n)}/steer`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async retryAiSessionQueuedMessage(e,t,n){const s=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(n)}/retry`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async removeAiSessionQueuedMessage(e,t,n){const s=await this.requireControlledInstance(e,!0);return me.parse(await this.instanceRequest(s,`/ai-sessions/${encodeURIComponent(t)}/queue/${encodeURIComponent(n)}`,{method:"DELETE"}))}async interruptAiSession(e,t){const n=await this.requireControlledInstance(e,!0);return he.parse(await this.instanceRequest(n,`/ai-sessions/${encodeURIComponent(t)}/interrupt`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})}))}async proxyInstanceHttp(e,t,n={}){const s=await this.requireControlledInstance(e,!0);return this.controlledInstanceGateway.proxyHttp(s,t,n)}async proxyInstanceWebSocket(e,t,n,s,i={}){const r=await this.requireControlledInstance(e,!0);this.controlledInstanceGateway.proxyWebSocket(r,t,n,s,i)}async instanceConfigSyncState(e){const t=await this.requireControlledInstance(e,!0),n=a.z.array(Si).parse(await this.instanceRequest(t,"/config-sync/programs"));return Ci.parse({programs:n,preferences:this.configSyncPreferences.get(e)?.preferences||lo()})}async listInstanceConfigSyncFolders(e,t={}){const n=await this.requireControlledInstance(e,!0),s=new URLSearchParams;return t.path&&s.set("path",t.path),void 0!==t.depth&&s.set("depth",String(t.depth)),a.z.array(Es).parse(await this.instanceRequest(n,"/config-sync/folders"+(s.size?`?${s}`:"")))}async syncInstanceConfigs(e,t){const n=Ai.parse(t),s=await this.requireControlledInstance(e,!0);if("export"===n.direction&&"local-folder"!==s.source.type){const e=new Error("Exporting instance config is only available for local folder projects.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_EXPORT_REQUIRES_LOCAL_PROJECT"}),e}const i=function(e){const t=function(e){const t=[];for(const n of e.split("/"))if(n&&"."!==n)if(".."===n){if(!t.length)return"..";t.pop()}else t.push(n);return t.join("/")||"."}(e.trim().replace(/\\/g,"/")||".");if(t.startsWith("/")||".."===t||t.startsWith("../")){const e=new Error("Config sync folder must stay inside the instance workspace.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_FOLDER_INVALID"}),e}return t||"."}(n.workspaceFolder),r={...n,workspaceFolder:i},a=zi.parse(await this.instanceRequest(s,"/config-sync",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(r)})),o=Rr(),d=this.configSyncPreferences.get(e);return this.configSyncPreferences.put({id:e,preferences:{...d?.preferences||lo(),[n.direction]:i},createdAt:d?.createdAt||o,updatedAt:o}),a}async instanceRequest(e,t,n={}){return this.controlledInstanceGateway.request(e,t,n)}async reportInstanceHeartbeat(e,t){await this.controlledInstanceGateway.reportHeartbeat(e,t)}async nodeAgentFetch(e,t,n={}){return this.nodeAgentTransportResolver.resolve(e).request(e,t,n)}async listNodeInstances(){return(await this.listNodeInstancesWithDiagnostics()).items}async listNodeInstancesWithDiagnostics(e){const t=await this.nodeAgentGateway.listFleetInstances(this.listNodes(),{signal:e});return{items:await Promise.all(t.items.map(t=>this.withFreshTriggerSnapshot(t,e))),nodeErrors:t.nodeErrors}}async withFreshTriggerSnapshot(e,t){if(!Gs(e)||"online"!==e.connectionStatus&&"online"!==e.agentStatus)return e;try{const n=He.parse(await this.instanceRequest(e,"/triggers",{signal:t}));return js.parse({...e,triggers:Ao(n)})}catch{return e}}async requireNodeInstance(e){for(const t of this.listNodes())try{const n=(await this.nodeAgentGateway.listInstances(t)).find(t=>t.id===e);if(n)return n}catch{}$r("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`)}async requireNodeRuntimeOnNode(e,t){const n=(await this.listNodeRuntimes(e)).find(e=>e.id===t);return n||$r("NODE_RUNTIME_NOT_FOUND",`Runtime ${t} was not found on node ${e}.`),n}async requireNodeLocalFolder(e,t){return this.nodeAgentGateway.requireLocalFolder(e,t)}async resolveAppLaunchOptions(e,t){const n="string"==typeof t.cwdFolderId?t.cwdFolderId.trim():"",s={...t};if(delete s.cwdFolderId,!n)return s;const i=this.requireNode(e.nodeId),r=await this.requireNodeLocalFolder(i,n);if(r.nodeId!==e.nodeId){const t=new Error(`Local folder ${r.id} belongs to node ${r.nodeId}, not ${e.nodeId}.`);throw Object.assign(t,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_INSTANCE_NODE"}),t}const a=await this.requireNodeRuntimeOnNode(e.nodeId,e.runtimeId);return{...s,cwd:this.appLaunchCwdForFolder(e,r,a)}}appLaunchCwdForFolder(e,n,s){if("local"===s.type)return uo(i=n.path).resolve(i);var i;if("local-folder"!==e.source.type){const e=new Error("App cwd folder selection is only supported for local-folder instances.");throw Object.assign(e,{statusCode:400,code:"APP_CWD_REQUIRES_LOCAL_FOLDER_SOURCE"}),e}const r=function(e,t){if(po(e)!==po(t))return;const n=uo(e),s=n.resolve(e),i=n.resolve(t),r=n.relative(s,i);return r?".."===r||r.startsWith(`..${n.sep}`)||n.isAbsolute(r)?void 0:r.split(n.sep).filter(Boolean):[]}(e.source.path,n.path);if(!r){const t=new Error(`Local folder ${n.path} is outside the instance workspace ${e.source.path}.`);throw Object.assign(t,{statusCode:400,code:"APP_CWD_OUTSIDE_WORKSPACE"}),t}const a=e.runtime.workspacePath||e.workspace.path||Fr(e.source).path||"/workspace";return r.length?t.posix.join(a,...r):a}async prepareInstanceModels(e,t){const n=await this.nodeAgentGateway.listModels(e),s={...null===t.codexModelHash?{codexModelHash:null}:t.codexModelHash?.trim()?{codexModelHash:t.codexModelHash.trim()}:{},...null===t.claudeModelHash?{claudeModelHash:null}:t.claudeModelHash?.trim()?{claudeModelHash:t.claudeModelHash.trim()}:{}},i=async(t,s)=>{if(null===s)return;const i=s?this.listAllModels().find(e=>e.id===s):this.listAllModels().find(e=>e.enabled&&e.app===t);if(i){if(i.app!==t)throw Object.assign(new Error(`Model ${i.id} is not a ${t} model.`),{statusCode:400,code:"MODEL_APP_MISMATCH"});if(!i.enabled)throw Object.assign(new Error(`Model ${i.id} is disabled.`),{statusCode:400,code:"MODEL_DISABLED"});return await this.nodeAgentGateway.deployModel(e,i.id,i),i.id}if(!s)return;const r=n.find(e=>e.id===s);if(r||$r("MODEL_NOT_FOUND",`Model ${s} was not found on control-plane or node ${e.id}.`),r.app!==t)throw Object.assign(new Error(`Model ${s} is not a ${t} model.`),{statusCode:400,code:"MODEL_APP_MISMATCH"});if(!r.enabled)throw Object.assign(new Error(`Model ${s} is disabled.`),{statusCode:400,code:"MODEL_DISABLED"});return r.id};return{modelSelection:s,codexModelHash:await i("codex",s.codexModelHash),claudeModelHash:await i("claude",s.claudeModelHash)}}async ensureInstanceModelAssignment(e){const t=this.requireNode(e.nodeId),n=await this.prepareInstanceModels(t,e.modelSelection);return this.nodeAgentGateway.assignInstanceModels(t,e.id,n)}nextModelOrder(){return this.models.list().reduce((e,t)=>Math.max(e,t.order),0)+100}requireProject(e){return this.catalogService.requireProject(e)}requireModel(e,t=!1){const n=this.models.get(e);n||$r("MODEL_NOT_FOUND",`Model ${e} was not found.`);const s=this.normalizeModelRecord(n);return t?s:Wr(s)}requireModelSecret(e){const t=this.models.get(e);return t||$r("MODEL_NOT_FOUND",`Model ${e} was not found.`),this.normalizeModelRecord(t)}normalizeModelRecord(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...e};if(!("app"in t)){const e=Array.isArray(t.apps)?t.apps:[];t.app=e.includes("claude")&&!e.includes("codex")?"claude":"codex"}return delete t.apps,Dn.parse(t)}return Dn.parse(e)}(e);return t!==e&&this.models.put(t),t}requireImage(e){return this.catalogService.requireImage(e)}requireNode(e){const t=this.nodes.get(e);return t||$r("NODE_NOT_FOUND",`Node ${e} was not found.`),t}requirePublicNode(e){return Ur(this.requireNode(e))}async requireControlledInstance(e,t=!1){const n=await this.requireNodeInstance(e);return t?n:Hr(n)}}function Po(e,t){const n=(s=e)&&"object"==typeof s&&!Array.isArray(s)?s:{};var s;return{code:"string"==typeof n.code&&n.code.trim()?n.code.trim():t,message:e instanceof Error&&e.message.trim()?e.message:"The operation failed."}}function Oo(e){return m(String(e),"escape").replace(/\n+$/,"")}function Ro(e){const[t,...n]=String(e).split("\n");return[`*${Oo(t)}*`,...n.map(Oo)].join("\n")}function jo(e,t=4e3){const n=String(e),s=Ro(n);if(s.length<=t)return s;const[i,...r]=n.split("\n"),a=r.join("\n"),o="...\n";let d=i;for(;Ro(`${d}\n${o}`).length>t&&d.length>0;)d=d.slice(0,Math.floor(.8*d.length));let c=0,l=a.length,p=0,u=Ro(`${d}\n${o}`);for(;c<=l;){const e=Math.floor((c+l)/2),n=Ro(`${d}\n${o}${a.slice(-e)}`);n.length<=t?(u=n,p=e,c=e+1):l=e-1}const g=a.slice(-p),m=g.indexOf("\n");return m>=0&&m<200&&(u=Ro(`${d}\n${o}${g.slice(m+1)}`)),u}function Do(e){return String(e).replace(/\\([_*\[\]()~`>#+\-=|{}.!\\])/g,"$1")}async function Mo(e,t,n,s,i={}){if(!t.token)return;const r=n=>e(`https://api.telegram.org/bot${t.token}/sendMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),a=function(e,t={}){const n=String(e),s=t.rawMarkdownV2?e=>e:Oo;if(s(n).length<=4e3)return[n];const i=[];let r=n;for(;r;){let e=1,t=r.length,n=1;for(;e<=t;){const i=Math.floor((e+t)/2);s(r.slice(0,i)).length<=4e3?(n=i,e=i+1):t=i-1}if(n<r.length){const e=r.slice(0,n),t=Math.max(e.lastIndexOf("\n"),e.lastIndexOf(" "));t>=Math.floor(.6*n)&&(n=t+1)}i.push(r.slice(0,n).trimEnd()),r=r.slice(n).trimStart()}return i.filter(Boolean)}(s,i);let o;for(const[e,t]of a.entries())o=await qo(r,"sendMessage",t,{chat_id:n,...0===e&&i.replyToMessageId?{reply_to_message_id:i.replyToMessageId,allow_sending_without_reply:!0}:{},...e===a.length-1&&i.replyMarkup?{reply_markup:i.replyMarkup}:{}},i);return o}async function $o(e,t,n){if(!t.token||!n)throw new Error("Telegram file id is required.");const s=await e(`https://api.telegram.org/bot${t.token}/getFile`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({file_id:n})}),i=await s.json().catch(()=>({}));if(!s.ok||!1===i.ok||!i.result?.file_path)throw new Error(i.description||`Telegram getFile failed with HTTP ${s.status}`);return`https://api.telegram.org/file/bot${t.token}/${i.result.file_path}`}async function qo(e,t,n,s,i){const r=i.parseMode||"MarkdownV2",a=i.rawMarkdownV2?String(n):Oo(n),o=await e({...s,text:a,disable_web_page_preview:!0,parse_mode:r}),d=await o.json().catch(()=>({}));if(o.ok&&!1!==d.ok)return d.result;if("editMessageText"===t&&Lo(d.description))return;if(i.rawMarkdownV2&&"MarkdownV2"===r){const i=await e({...s,text:Do(n),disable_web_page_preview:!0,parse_mode:"Markdown"}),r=await i.json().catch(()=>({}));if(i.ok&&!1!==r.ok)return r.result;if("editMessageText"===t&&Lo(r.description))return}const c=await e({...s,text:i.rawMarkdownV2?Do(n):String(n),disable_web_page_preview:!0}),l=await c.json().catch(()=>({}));if(c.ok&&!1!==l.ok)return l.result;if("editMessageText"!==t||!Lo(l.description))throw new Error(l.description||d.description||`Telegram ${t} failed with HTTP ${c.status}`)}function Lo(e){return"string"==typeof e&&e.toLowerCase().includes("message is not modified")}function Bo(e,t){if("string"==typeof e&&e.trim())return e.trim();Fo(`Chat action token is missing ${t}.`)}function Uo(e){if("allow"===e||"deny"===e||"skip"===e)return e;Fo("Chat action token has an invalid decision.")}function Fo(e){const t=new Error(e);throw Object.assign(t,{statusCode:400,code:"CHAT_ACTION_TOKEN_INVALID"}),t}function Ho(e){return Buffer.from(e,"base64url").toString("utf8")}function Wo(e){return String(e||"").split(/\r?\n/,1)[0].replace(/^#+\s*/,"").trim().slice(0,20)||"消息通知"}function Go(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,"string"==typeof t?t:JSON.stringify(t)]))}function Vo(e){return`dtv1.card//IM_GROUP.${e}`}function Ko(e){return`dtv1.card//IM_ROBOT.${e}`}function Jo(e,t){return ld(t.biz_conversation_id).trim()||ld(e.spaceId).replace(/^dtv1\.card\/\/(?:IM_GROUP|IM_SINGLE|IM_ROBOT)\./,"")}function Qo(e,t={}){const n=ld(e).trim().toUpperCase();return"2"===n||"IM_GROUP"===n?"IM_GROUP":"1"===n||"IM_SINGLE"===n?t.robotMessage?"IM_ROBOT":"IM_SINGLE":"IM_ROBOT"===n?"IM_ROBOT":void 0}function Yo(e){if(e.startsWith("task_handoff:"))return e;if(!e.startsWith("th_cb_"))return"";try{return Buffer.from(e.slice(6),"base64url").toString("utf8")}catch{return""}}function Xo(e,t,n,s,i){const r=Jo(s,i);return{cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},cardData:{cardParamMap:Go({type:"actions",title:"TaskHandoff",description:e,list:Oa(t),biz_out_track_id:s.outTrackId||i.biz_out_track_id||"",biz_conversation_id:r,biz_sender_id:i.biz_sender_id||s.userId||"",biz_session_webhook:i.biz_session_webhook||"",biz_conversation_type:i.biz_conversation_type||"",biz_step:n,error_msg:""})}}}function Zo(e,t,n){const s=[];return ed(s,n),ed(s,t),ed(s,e),s.find(e=>Yo(e))||""}function ed(e,t){for(const n of["actionIdList","actionIds"]){const s=t[n];Array.isArray(s)&&e.push(...s.map(e=>ld(e)).filter(Boolean))}for(const n of["actionId","action_id","id","actionValue"]){const s=ld(t[n]);s&&e.push(s)}}async function td(e,t,n,s){s?.("info",{target:"sessionWebhook",hasSessionWebhook:Boolean(t),textPreview:od(n)},"dingding webhook send requested");const i=await e(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({msgtype:"markdown",markdown:{title:Wo(n),text:n}})}),r=await i.json().catch(()=>{});if(s?.(i.ok?"info":"warn",{target:"sessionWebhook",status:i.status,response:ad(r)},"dingding webhook send completed"),!i.ok)throw new Error(`DingDing webhook failed with HTTP ${i.status}`)}async function nd(e){const t=Oa(e.replyMarkup);if(!t.length&&!e.forceCard)return e.sessionWebhook&&await td(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),e.sessionWebhook?{delivered:!0}:void 0;const n=e.bridge.settings||{},s=ld(n.robotCode),i=ld(n.cardTemplateId)||"13fc6717-12e4-43ed-8533-111a310d4995.schema",r=ld(n.cardCallbackRouteKey)||"bi_workflow_ticket",a=e.senderId||e.runtime.senderIds.get(e.chatId)||ld(n.senderId),o=(d=e.runtime,c=e.chatId,d.conversationTypes?.get(c)||"IM_GROUP");var d,c;if(!s||!i||!a){if(e.sessionWebhook&&!e.forceCard)return await td(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),{delivered:!0};throw new Error("DingDing card target is incomplete.")}const l=`task_handoff_cp_${Date.now()}_${Math.random().toString(16).slice(2)}`,p=Go({type:"actions",title:e.title||Wo(e.text),description:e.text,list:t,biz_out_track_id:l,biz_step:e.step||"task_handoff_actions",biz_conversation_id:e.chatId,biz_sender_id:a,biz_session_webhook:e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)||"",biz_conversation_type:o,error_msg:""}),u=function(e,t,n,s){return"IM_SINGLE"===e?{openSpaceId:(i=t,`dtv1.card//IM_SINGLE.${i}`),imSingleOpenSpaceModel:{},imSingleOpenDeliverModel:{extension:{}}}:"IM_ROBOT"===e?{openSpaceId:Ko(n),imRobotOpenSpaceModel:{supportForward:!1},imRobotOpenDeliverModel:{extension:{},robotCode:s,spaceType:"IM_ROBOT"}}:{openSpaceId:Vo(t),imGroupOpenSpaceModel:{supportForward:!1},imGroupOpenDeliverModel:{extension:{},robotCode:s}};var i}(o,e.chatId,a,s);return e.runtime.onLog?.("info",{target:"card",chatId:e.chatId,senderId:a,conversationType:o,openSpaceId:u.openSpaceId,hasSessionWebhook:Boolean(e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)),hasReplyMarkup:Boolean(e.replyMarkup),actionCount:t.length,textPreview:od(e.text)},"dingding card send requested"),await sd(e.fetchImpl,e.bridge,e.runtime,"POST","/v1.0/card/instances/createAndDeliver",{userId:a,cardTemplateId:i,outTrackId:l,callbackType:"STREAM",callbackRouteKey:r,cardData:{cardParamMap:p},userIdType:Number(n.cardUserIdType||1),...u}),{delivered:!0,outTrackId:l}}async function sd(e,t,n,s,i,r){const a=await async function(e,t,n){if(n.accessToken&&n.accessToken.expiresAt>Date.now()+6e4)return n.accessToken.value;const s=ld(t.settings.clientSecret);if(!t.token||!s)throw new Error("DingDing client id/secret is not configured.");const i=await e("https://api.dingtalk.com/v1.0/oauth2/accessToken",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appKey:t.token,appSecret:s})}),r=await i.json().catch(()=>({}));if(!i.ok||!r.accessToken)throw new Error(`DingDing access token failed with HTTP ${i.status}`);const a=Number(r.expireIn||7200);return n.accessToken={value:r.accessToken,expiresAt:Date.now()+1e3*Math.max(60,a-120)},n.accessToken.value}(e,t,n);n.onLog?.("info",{method:s,path:i,body:id(r)},"dingding api request");const o=await e(`https://api.dingtalk.com${i}`,{method:s,headers:{"content-type":"application/json","x-acs-dingtalk-access-token":a},body:JSON.stringify(r)}),d=await o.json().catch(()=>{});if(n.onLog?.(o.ok?"info":"warn",{method:s,path:i,status:o.status,response:ad(d)},"dingding api response"),!o.ok)throw new Error(`DingDing API failed with HTTP ${o.status}`);const c=dd(d),l=c.code??c.errcode,p=c.success;if(void 0!==l&&"0"!==String(l)||!1===p)throw new Error(ld(c.message||c.errmsg)||`DingDing API returned ${String(l||"failure")}`);const u=function(e){const t=dd(dd(e).result).deliverResults;if(!Array.isArray(t))return"";const n=t.find(e=>!1===dd(e).success);if(!n)return"";const s=dd(n),i=ld(s.spaceId),r=ld(s.errorMsg||s.message||s.errmsg)||"delivery failed";return i?`DingDing card delivery failed for ${i}: ${r}`:`DingDing card delivery failed: ${r}`}(d);if(u)throw new Error(u);return o}function id(e){const t=dd(e),n=dd(t.cardData),s=dd(n.cardParamMap);return{openSpaceId:ld(t.openSpaceId),outTrackId:ld(t.outTrackId),userId:ld(t.userId),userIdType:t.userIdType,callbackType:ld(t.callbackType),callbackRouteKey:ld(t.callbackRouteKey),hasImGroupTarget:Boolean(t.imGroupOpenSpaceModel||t.imGroupOpenDeliverModel),hasImSingleTarget:Boolean(t.imSingleOpenSpaceModel||t.imSingleOpenDeliverModel),hasImRobotTarget:Boolean(t.imRobotOpenSpaceModel||t.imRobotOpenDeliverModel),imGroupOpenDeliverModel:rd(t.imGroupOpenDeliverModel),imSingleOpenDeliverModel:rd(t.imSingleOpenDeliverModel),imRobotOpenDeliverModel:rd(t.imRobotOpenDeliverModel),bizConversationId:ld(s.biz_conversation_id),bizConversationType:ld(s.biz_conversation_type),bizSenderId:ld(s.biz_sender_id),hasBizSessionWebhook:Boolean(ld(s.biz_session_webhook))}}function rd(e){const t=dd(e);if(Object.keys(t).length)return{hasExtension:Boolean(t.extension),hasRobotCode:Boolean(ld(t.robotCode)),spaceType:ld(t.spaceType),recipients:Array.isArray(t.recipients)?t.recipients.length:void 0,atUserIds:t.atUserIds&&"object"==typeof t.atUserIds?Object.keys(t.atUserIds).length:void 0}}function ad(e){const t=dd(e),n=dd(t.result);return{code:t.code??t.errcode,message:od(t.message||t.errmsg,240),success:t.success,requestId:t.requestId,result:Object.keys(n).length?n:void 0}}function od(e,t=160){const n=String(e||"").replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,Math.max(0,t-3))}...`}function dd(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function cd(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:"string"==typeof e&&e.trim()?pd(e):{}}function ld(e){return"string"==typeof e?e:""}function pd(e){try{return dd(JSON.parse(String(e||"{}")))}catch{return{}}}function ud(e){return JSON.stringify(Oa(e))}function gd(e){return e instanceof Error?e.message:String(e)}function md(e){const t=Number(function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e).message_id);return Number.isInteger(t)&&t>0?t:void 0}function hd(e){return Array.isArray(e?.actionRows)?e.actionRows:(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e])}function yd(e,t){const n=hd(e),s=hd(t);return n.length===s.length&&n.every((e,t)=>{const n=s[t];return e.length===n.length&&e.every((e,t)=>{const s=n[t];return e.text===s?.text&&e.callbackData===s.callbackData})})}class fd{entries;updateIntervalMs;send;edit;deleteMessage;messageIdFromResult;retryAfterMs;isMessageTooLong;onLog;constructor(e){this.entries=e.entries||new Map,this.updateIntervalMs=e.updateIntervalMs||1e3,this.send=e.send,this.edit=e.edit,this.deleteMessage=e.delete,this.messageIdFromResult=e.messageIdFromResult||md,this.retryAfterMs=e.retryAfterMs,this.isMessageTooLong=e.isMessageTooLong,this.onLog=e.onLog}remember(e,t,n,s,i,r){const a=this.entries.get(e);return a&&(clearTimeout(a.timer),a.timer=void 0,a.pendingText=void 0),this.entries.set(e,{lastText:n,lastUpdateAt:Date.now(),messageId:t,route:s,options:i,lastOptions:i,renderOptions:r}),a}rekey(e,t){if(e===t)return this.entries.has(e);const n=this.entries.get(e);if(!n||this.entries.has(t))return!1;if(clearTimeout(n.timer),n.timer=void 0,this.entries.delete(e),this.entries.set(t,n),n.pendingText){const e=n.rateLimitedUntil?Math.max(0,n.rateLimitedUntil-Date.now()):Math.max(0,this.updateIntervalMs-(Date.now()-n.lastUpdateAt));this.scheduleFlush(t,e)}return!0}scheduleFlush(e,t){const n=this.entries.get(e);n&&!n.timer&&(n.timer=setTimeout(()=>{n.timer=void 0;const t=n.pendingText;n.pendingText=void 0,t&&this.applyUpdate(e,t,n.route,n.options)},t))}markRateLimited(e,t,n,s){const i=this.entries.get(e);i&&(i.rateLimitedUntil=Date.now()+n,i.pendingText=t,i.route=s||i.route,this.scheduleFlush(e,n))}async applyUpdate(e,t,n,s){let i=this.entries.get(e);const r=i,a=n||i?.route,o=s||i?.options;try{if(i&&(i.route=a,i.options=o),i?.pending&&await i.pending,i=this.entries.get(e),r&&i!==r)return!1;if(i?.messageId){if(i.lastText===t&&yd(i.lastOptions,o))return!0;const n=Date.now()-(i.lastUpdateAt||0);return n<this.updateIntervalMs?(i.pendingText=t,i.route=a||i.route,i.options=o||i.options,this.scheduleFlush(e,this.updateIntervalMs-n),!0):(await this.edit(i.messageId,t,a||i.route,o),i.lastText=t,i.lastOptions=o,i.lastUpdateAt=Date.now(),i.rateLimitedUntil=void 0,!0)}const n={lastText:t,lastUpdateAt:Date.now(),route:a,options:o,lastOptions:o};return n.pending=this.send(t,a,o).then(e=>{const t=this.messageIdFromResult(e);t&&(n.messageId=t)}).finally(()=>{n.pending=void 0}),this.entries.set(e,n),await n.pending,this.entries.get(e)===n&&(n.rateLimitedUntil=void 0,Boolean(n.messageId))}catch(n){const s=this.retryAfterMs?.(n);return s&&this.markRateLimited(e,t,s,a),this.onLog?.(`Telegram progress update skipped: ${gd(n)}`),!1}}update(e,t,n,s){const i=String(t||"").trim();if(!i)return;const r=this.entries.get(e);if(r&&(r.route=n||r.route,r.options=s||r.options),r?.lastText===i&&yd(r.lastOptions,s||r.options)&&!r.pending&&!r.pendingText)return;const a=Date.now();if(r?.rateLimitedUntil){if(r.rateLimitedUntil>a)return r.pendingText=i,void this.scheduleFlush(e,r.rateLimitedUntil-a);r.rateLimitedUntil=void 0}if(r?.pending&&!r.messageId)return r.pendingText=i,void this.scheduleFlush(e,this.updateIntervalMs);if(!r||!r.messageId)return void this.applyUpdate(e,i,n,s);const o=Date.now()-(r.lastUpdateAt||0);if(o>=this.updateIntervalMs)return clearTimeout(r.timer),r.timer=void 0,r.pendingText=void 0,void this.applyUpdate(e,i,n||r.route,s||r.options);r.pendingText=i,this.scheduleFlush(e,this.updateIntervalMs-o)}async finish(e,t,n,s,i){const r=this.entries.get(e);if(!r)return!1;try{if(clearTimeout(r.timer),r.timer=void 0,r.pendingText=void 0,r.pending&&await r.pending,this.entries.delete(e),!r.messageId)return!1;const a=s||r.options;return r.lastText===t&&yd(r.lastOptions,a)||await this.edit(r.messageId,t,n||r.route,a,i||r.renderOptions),!0}catch(t){return this.entries.delete(e),this.isMessageTooLong?.(t)?this.onLog?.("Telegram progress finish too long; sending result as multiple messages"):this.onLog?.(`Telegram progress finish skipped: ${gd(t)}`),!1}}async delete(e,t){const n=this.entries.get(e);if(!n)return!1;try{return clearTimeout(n.timer),n.timer=void 0,n.pendingText=void 0,n.pending&&await n.pending,this.entries.delete(e),!(!this.deleteMessage||!n.messageId)&&(await this.deleteMessage(n.messageId,t||n.route),!0)}catch(t){return this.entries.delete(e),this.onLog?.(`Telegram progress delete skipped: ${gd(t)}`),!1}}async flush(e){const t=this.entries.get(e);if(t?.pending&&await t.pending,t?.timer){clearTimeout(t.timer),t.timer=void 0;const n=t.pendingText;t.pendingText=void 0,n&&await this.applyUpdate(e,n,t.route,t.options)}}async wait(e){await function(e){return new Promise(t=>setTimeout(t,e))}(e)}}const Id=20971520;function bd(e){const t=[],n=[...e.photo||[]].filter(e=>e.file_id).sort((e,t)=>Number(t.file_size||t.width||0)-Number(e.file_size||e.width||0))[0];return n?.file_id&&t.push({fileId:n.file_id,fileName:"telegram-photo.jpg",fileSize:n.file_size}),e.document?.file_id&&String(e.document.mime_type||"").startsWith("image/")&&t.push({fileId:e.document.file_id,fileName:e.document.file_name,fileSize:e.document.file_size}),t}async function vd(e,n,s="telegram-image",i){if(i&&i>Id)throw new Error(`image is too large: ${i} bytes`);const r=await e.getFileLink(n),a=String(r),o=await(e.fetchFile||fetch)(a);if(!o.ok)throw new Error(`download failed with HTTP ${o.status}`);const d=Number(o.headers.get("content-length")||0);if(d>Id)throw new Error(`image is too large: ${d} bytes`);const c=t.extname(s)||".jpg";let l=c;try{l=t.extname(new URL(a).pathname)||c}catch{l=c}const p=t.join(y.tmpdir(),"task-handoff-images");await h.mkdir(p,{recursive:!0});const u=l.replace(/[^.\w-]/g,"")||".jpg",g=t.join(p,`telegram-${Date.now()}-${Math.random().toString(16).slice(2)}${u}`),m=Buffer.from(await o.arrayBuffer());if(m.length>Id)throw new Error(`image is too large: ${m.length} bytes`);return await h.writeFile(g,m),g}class wd{store;constructor(e){this.store=new fd({updateIntervalMs:e.updateIntervalMs,send:(t,n,s)=>e.send(e.requireBridge(n?.bridgeId||""),n?.chatId||"",jo(t),{rawMarkdownV2:!0,replyMarkup:Td(s)}),edit:(t,n,s,i)=>e.edit(e.requireBridge(s?.bridgeId||""),s?.chatId||"",t,jo(n),{rawMarkdownV2:!0,replyMarkup:Td(i)}),delete:(t,n)=>e.deleteMessage(e.requireBridge(n?.bridgeId||""),n?.chatId||"",t),onLog:e.onLog})}get entries(){return this.store.entries}remember(e,t,n,s){return this.store.remember(e,t,n,s)}rekey(e,t){return this.store.rekey(e,t)}delete(e){return this.store.delete(e)}async applyUpdate(e){return this.store.applyUpdate(e.key,e.text,{bridgeId:e.bridge.id,chatId:e.chatId},{actionRows:e.actionRows})}}async function Sd(e){return(await async function(e,t,n,s){const i=[];for(const r of bd(n))try{const n=await vd({getFileLink:n=>$o(e,t,n),fetchFile:e},r.fileId,r.fileName,r.fileSize);i.push(n)}catch(e){s?.({fileId:r.fileId,fileName:r.fileName,error:e})}return i}(e.fetchImpl,e.bridge,e.message,e.onImageDownloadError)).map((e,s)=>{const i=n.readFileSync(e);return{id:`tg_${o.randomUUID().replace(/-/g,"").slice(0,24)}`,kind:"image",name:t.basename(e)||`telegram-image-${s+1}`,mime:Ad(e),size:i.length,source:{type:"inline",encoding:"base64",data:i.toString("base64")}}})}function Ad(e){const n=t.extname(e).toLowerCase();return".png"===n?"image/png":".webp"===n?"image/webp":".gif"===n?"image/gif":".bmp"===n?"image/bmp":"image/jpeg"}function kd(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function Cd(e){return e&&"object"==typeof e?e:{}}function xd(e){return"string"==typeof e?e:""}function Td(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return Ea(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function zd(e){const t=Array.isArray(e.item_list)?e.item_list:[];for(const e of t){const t=Nd(e);if(1!==t.type)continue;const n=Ed(Nd(t.text_item).text).trim();if(n)return n}return""}function Nd(e){return e&&"object"==typeof e?e:{}}function Ed(e){return"string"==typeof e?e:""}function _d(e){const{fetchImpl:t,bridge:n,dingdingRuntime:s}=e;return"telegram"===n.channel?function(e,t){return{bridge:t,send:async(n,s,i={})=>{const r=await Mo(e,t,n,s,{replyMarkup:i.replyMarkup,replyToMessageId:i.replyToMessageId,rawMarkdownV2:i.rawMarkdownV2,parseMode:i.parseMode});return{provider:"telegram",raw:r,messageId:Number(Cd(r).message_id)||void 0}}}}(t,n):"dingding"===n.channel?function(e,t,n){return{bridge:t,send:async(s,i,r={})=>{const a=r.sessionWebhook||n?.chatWebhooks.get(s)||ld(t.settings.sessionWebhook);if(r.replyMarkup&&n){const o=await nd({fetchImpl:e,bridge:t,runtime:n,chatId:s,text:i,replyMarkup:r.replyMarkup,sessionWebhook:a,senderId:r.senderId||n.senderIds.get(s)||ld(t.settings.senderId)});if(o?.delivered)return{provider:"dingding",interactionId:o.outTrackId}}if(a)return await td(e,a,i,n?.onLog),{provider:"dingding"}}}}(t,n,s):"wechat"===n.channel?function(e,t){return{bridge:t,send:async(n,s,i={})=>{const r=i.contextToken||Ed(t.settings.contextToken);if(r)return await async function(e,t,n,s,i){const r=Ed(t.settings.baseUrl)||"https://ilinkai.weixin.qq.com",a=await e(`${r}/ilink/bot/sendmessage`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${t.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({msg:{from_user_id:"",to_user_id:n,client_id:o.randomUUID(),message_type:2,message_state:2,context_token:s,item_list:[{type:1,text_item:{text:i}}]},base_info:{channel_version:"1.0.2"}})}),d=await a.json().catch(()=>({}));if(!a.ok||d.errcode)throw new Error(Ed(d.errmsg)||`WeChat send failed with HTTP ${a.status}`)}(e,t,n,r,s),{provider:"wechat"}}}}(t,n):{bridge:n,send:async()=>{}}}class Pd{entries=new Map;fetchImpl;generations=new Map;constructor(e){this.fetchImpl=e}clear(){for(const e of new Set([...this.generations.keys(),...[...this.entries.values()].map(e=>e.bridgeId)]))this.clearBridge(e)}clearBridge(e){this.generations.set(e,this.generation(e)+1),this.clearEntries(t=>t.bridgeId===e)}generation(e){return this.generations.get(e)||0}isCurrent(e,t){return this.generation(e)===t}clearEntries(e){for(const[t,n]of this.entries)e(n)&&(n.timer&&clearTimeout(n.timer),n.cancelPending?.(new Error("DingDing progress update was cancelled.")),this.entries.delete(t))}async applyUpdate(e,t){const n=this.generation(e.bridge.id),s=ud(e.replyMarkup);let i=this.entries.get(e.key);if(i?.pending&&await i.pending,!this.isCurrent(e.bridge.id,n))return!1;if(i=this.entries.get(e.key),i?.outTrackId){if(i.lastText===e.text&&i.lastActionsFingerprint===s)return!0;const r=Date.now()-(i.lastUpdateAt||0);return r<1e3?(i.pendingText=e.text,i.pendingReplyMarkup=e.replyMarkup,i.timer||(i.pending=new Promise((s,a)=>{i.cancelPending=a,i.timer=setTimeout(()=>{i.timer=void 0,i.cancelPending=void 0;const r=i.pendingText,o=i.pendingReplyMarkup;if(i.pendingText=void 0,i.pendingReplyMarkup=void 0,!r)return s(void 0);this.updateExistingEntry(e,t,i,r,o,ud(o),n).then(s,a)},1e3-r)}).finally(()=>{i.pending=void 0,i.timer=void 0,i.cancelPending=void 0})),await i.pending,this.isCurrent(e.bridge.id,n)&&this.entries.has(e.key)):(await this.updateExistingEntry(e,t,i,e.text,e.replyMarkup,s,n),this.isCurrent(e.bridge.id,n))}const r={bridgeId:e.bridge.id,lastText:e.text,lastActionsFingerprint:s,lastUpdateAt:Date.now()};return r.pending=nd({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,chatId:e.chatId,text:e.text,replyMarkup:e.replyMarkup,sessionWebhook:t.chatWebhooks.get(e.chatId)||Od(e.bridge.settings.sessionWebhook),senderId:t.senderIds.get(e.chatId)||Od(e.bridge.settings.senderId),title:"TaskHandoff 执行中",step:"progress",forceCard:!0}).then(t=>{this.isCurrent(e.bridge.id,n)&&(t?.outTrackId&&(r.outTrackId=t.outTrackId),t?.delivered&&(r.delivered=!0,r.lastUpdateAt=Date.now()))}).catch(t=>{throw this.isCurrent(e.bridge.id,n)&&this.entries.delete(e.key),t}).finally(()=>{r.pending=void 0}),this.entries.set(e.key,r),await r.pending,this.isCurrent(e.bridge.id,n)&&Boolean(r.delivered)}async updateExistingEntry(e,t,n,s,i,r,a){n.outTrackId&&(await async function(e){await sd(e.fetchImpl,e.bridge,e.runtime,"PUT","/v1.0/card/instances",{outTrackId:e.outTrackId,cardData:{cardParamMap:Go({type:"actions",title:e.title,description:e.text,list:Oa(e.replyMarkup),biz_step:e.step,error_msg:""})},cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},userIdType:Number(e.bridge.settings.cardUserIdType||1)})}({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,outTrackId:n.outTrackId,text:s,replyMarkup:i,title:"TaskHandoff 执行中",step:"progress"}),this.isCurrent(e.bridge.id,a)&&(n.lastText=s,n.lastActionsFingerprint=r,n.lastUpdateAt=Date.now()))}}function Od(e){return"string"==typeof e?e:""}function Rd(e){const t=new f.DWClient({clientId:e.clientId,clientSecret:e.clientSecret,ua:"task-handoff-control-plane",keepAlive:!1,autoReconnect:!1});let n,s=!1;return{async connect(){s=!1,await t.getEndpoint(),await t._connect();const e=t.socket;e?.once("close",()=>{s||n?.(new Error("DingDing stream connection closed."))})},disconnect(){s=!0,t.disconnect()},onDisconnect(e){n=e},getConfig:()=>t.getConfig(),registerCallbackListener(e,n){t.registerCallbackListener(e,n)},socketCallBackResponse(e,n){t.socketCallBackResponse(e,n)}}}class jd{runtimes=new Map;connected=new Set;reconnectTimers=new Map;progress;options;createClient;constructor(e){this.options=e,this.createClient=e.createClient||Rd,this.progress=new Pd(e.fetchImpl)}has(e){return this.runtimes.has(e)}isRunning(e){return this.connected.has(e)}get(e){return this.runtimes.get(e)}ids(){return this.runtimes.keys()}start(e){const t=(n=e.settings?.clientSecret,"string"==typeof n?n:"");var n;if(!e.token||!t)return this.options.onError(e.id,"DingDing client id/secret is not configured."),!1;const s={client:this.createClient({clientId:e.token,clientSecret:t}),chatWebhooks:new Map,senderIds:new Map,conversationTypes:new Map,onLog:(t,n,s)=>{this.options.logger["warn"===t?"warn":"info"]({bridgeId:e.id,...n},s)}};return s.client.onDisconnect?.(t=>{this.runtimes.get(e.id)===s&&this.handleConnectionFailure(e,s,t)}),s.client.registerCallbackListener(f.TOPIC_ROBOT,t=>{this.options.onRobotMessage(e,s,t).catch(t=>{this.options.onError(e.id,t)})}),s.client.registerCallbackListener(f.TOPIC_CARD,t=>{const n=t;this.options.onCardCallback(e,s,n).then(e=>s.client.socketCallBackResponse(n.headers.messageId,e||{})).catch(t=>{this.options.onError(e.id,t),s.client.socketCallBackResponse(n.headers.messageId,{})})}),this.options.clearError(e.id),this.runtimes.set(e.id,s),this.connect(e,s),!0}connect(e,t){this.reconnectTimers.delete(e.id),this.connected.add(e.id),t.client.connect().then(()=>{this.runtimes.get(e.id)===t&&(this.connected.add(e.id),this.options.clearError(e.id))}).catch(n=>{this.runtimes.get(e.id)===t&&this.handleConnectionFailure(e,t,n)})}handleConnectionFailure(e,t,n){if(this.connected.delete(e.id),this.progress.clearBridge(e.id),this.options.onError(e.id,n||"DingDing stream connection failed."),this.reconnectTimers.has(e.id))return;const s=setTimeout(()=>{this.runtimes.get(e.id)===t&&this.connect(e,t)},this.options.reconnectDelayMs??1e3);s.unref?.(),this.reconnectTimers.set(e.id,s)}stop(e){const t=this.reconnectTimers.get(e);t&&clearTimeout(t),this.reconnectTimers.delete(e),this.connected.delete(e);const n=this.runtimes.get(e);if(n){try{n.client.disconnect()}catch{}this.runtimes.delete(e)}this.progress.clearBridge(e)}stopAll(){for(const e of[...this.runtimes.keys()])this.stop(e);this.progress.clear()}applyUpdate(e){const t=this.runtimes.get(e.bridge.id);return t?this.progress.applyUpdate(e,t):Promise.resolve(!1)}applyProgressUpdate(e){return this.applyUpdate(e)}}class Dd{ttlMs;load;now;cached;pending;constructor(e,t,n={}){this.ttlMs=e,this.load=t,this.now=n.now||Date.now}get(){if(this.cached&&this.cached.expiresAt>this.now())return Promise.resolve(this.cached.value);if(this.pending)return this.pending;const e=this.load().then(e=>(this.cached={expiresAt:this.now()+this.ttlMs,value:e},e)).finally(()=>{this.pending===e&&(this.pending=void 0)});return this.pending=e,e}invalidate(){this.cached=void 0}}class Md{service;fetchImpl;logger;bridgeTimers=new Map;dingdingBridges;bridgeErrors=new Map;telegramOffsets=new Map;seenTelegramUpdates=new Set;wechatCursors=new Map;announcedPending=new Set;deliveredAiSessionFingerprints=new Map;aiSessionCancelTokens=new Map;aiSessionQueueActionTokens=new Map;telegramMessageAggregates=new Map;telegramMessageAggregateEndTokens=new Map;telegramProgressMessageTargets=new Map;aiSessionInstanceNamesCache;aiSessionPendingRoutesCache;telegramProgress;stopAiSessionListener;constructor(e,t=fetch,n={}){var s;this.service=e,this.fetchImpl=t,this.logger=(s=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(s||"").trim().toLowerCase())?n.logger:void 0),this.aiSessionInstanceNamesCache=new Dd(3e4,async()=>{const e=await(this.service.listAiSessionInstanceNames?.()||this.service.boardAsync()).catch(()=>[]);return new Map(e.map(e=>[e.id,e.name||e.id]))}),this.aiSessionPendingRoutesCache=new Dd(1e3,()=>this.service.listPendingRoutes().catch(()=>[])),this.telegramProgress=new wd({updateIntervalMs:n.telegramProgressUpdateIntervalMs,requireBridge:e=>this.service.requireChatBridge(e),send:(e,t,n,s={})=>this.sendTelegramMessage(e,t,n,s),edit:(e,t,n,s,i={})=>this.editTelegramMessage(e,t,n,s,i),deleteMessage:(e,t,n)=>this.deleteTelegramMessage(e,t,n),onLog:e=>this.logWarn({message:e},"telegram progress store warning")}),this.dingdingBridges=new jd({fetchImpl:this.fetchImpl,createClient:n.createDingdingClient,logger:{info:(e,t)=>this.logInfo(e,t),warn:(e,t)=>this.logWarn(e,t)},onRobotMessage:(e,t,n)=>this.handleDingdingRobotMessage(e,t,n),onCardCallback:(e,t,n)=>this.handleDingdingCardCallback(e,t,n),onError:(e,t)=>this.bridgeErrors.set(e,Vd(t)),clearError:e=>this.bridgeErrors.delete(e)}),this.stopAiSessionListener=n.aiSessions?.onSnapshot(e=>{this.deliverAiSessionSnapshot(e.instanceId,e.aiSessions).catch(t=>{this.logWarn({instanceId:e.instanceId,snapshotRevision:e.revision,error:Vd(t)},"chat ai session snapshot delivery failed")})})}logInfo(e,t){this.logger?.info?.({component:"control-plane-chat-gateway",...e},t)}logWarn(e,t){this.logger?.warn?.({component:"control-plane-chat-gateway",...e},t)}updateChatBridge(e,t){this.service.updateChatBridge?.(e,t)}startEnabled(){this.logAiSessionDelivery({stage:"runtime-start-enabled",bridgeCount:this.service.listChatBridges().length,enabledBridgeCount:this.service.listChatBridges().filter(e=>e.enabled).length});for(const e of this.service.listChatBridges())e.enabled&&this.startBridge(e.id)}startBridge(e){if(this.bridgeTimers.has(e)||this.dingdingBridges.has(e))return this.logAiSessionDelivery({stage:"bridge-start-skipped-running",bridgeId:e}),this.status();const t=this.service.requireChatBridge(e);return this.logAiSessionDelivery({stage:"bridge-start",bridgeId:t.id,channel:t.channel,enabled:t.enabled,hasToken:Boolean(t.token)}),"wechat"===t.channel?this.startPollingBridge(t,e=>this.pollWechat(e)):"dingding"===t.channel?this.startDingdingBridge(t):"telegram"!==t.channel?(this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status()):t.token?this.startPollingBridge(t,e=>this.pollTelegram(e)):(this.bridgeErrors.set(e,"Telegram token is not configured."),this.status())}stopBridge(e){const t=this.bridgeTimers.get(e);return t&&(clearInterval(t),this.bridgeTimers.delete(e)),this.dingdingBridges.stop(e),this.status()}stopAll(){for(const e of new Set([...this.bridgeTimers.keys(),...this.dingdingBridges.ids()]))this.stopBridge(e);this.dingdingBridges.stopAll();for(const e of this.telegramMessageAggregates.values())e.timer&&clearTimeout(e.timer);this.telegramMessageAggregates.clear(),this.telegramMessageAggregateEndTokens.clear(),this.stopAiSessionListener?.(),this.stopAiSessionListener=void 0}async pollBridgeNow(e){const t=this.service.requireChatBridge(e);return"telegram"===t.channel?(await this.pollTelegram(t),this.status()):"wechat"===t.channel?(await this.pollWechat(t),this.status()):("dingding"===t.channel||this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status())}async pollAiSessionsNow(){return await this.pollAiSessionUpdates(),this.status()}status(){const e=this.service.listChatBridges().map(e=>({id:e.id,channel:e.channel,name:e.name,running:this.bridgeTimers.has(e.id)||this.dingdingBridges.isRunning(e.id),tokenSet:Boolean(e.tokenSet),defaultChatId:e.defaultChatId,lastUpdateId:this.telegramOffsets.get(e.id),error:this.bridgeErrors.get(e.id)}));return{running:e.some(e=>e.running),bridges:e}}startPollingBridge(e,t){if(!e.token)return this.bridgeErrors.set(e.id,`${e.channel} token is not configured.`),this.status();this.bridgeErrors.delete(e.id);const n=setInterval(()=>{const n=this.service.requireChatBridge(e.id);n.enabled?t(n).catch(t=>this.bridgeErrors.set(e.id,t instanceof Error?t.message:String(t))):this.stopBridge(e.id)},e.pollIntervalMs);if(this.bridgeTimers.set(e.id,n),this.logAiSessionDelivery({stage:"bridge-poll-started",bridgeId:e.id,channel:e.channel,intervalMs:e.pollIntervalMs}),e.enabled){const n=this.service.requireChatBridge(e.id);t(n).catch(t=>this.bridgeErrors.set(e.id,t instanceof Error?t.message:String(t)))}return this.status()}startDingdingBridge(e){return this.dingdingBridges.start(e),this.status()}async handleDingdingRobotMessage(e,t,n){const s=function(e){const t=pd(e),n=ld(t.conversationId),s=ld(dd(t.text).content).trim();if(!n||!s)return;const i=ld(t.senderStaffId||t.senderId);return{chatId:n,userId:i||void 0,senderId:i||void 0,sessionWebhook:ld(t.sessionWebhook)||void 0,conversationType:Qo(t.conversationType,{robotMessage:!0}),text:s,raw:t}}(n.data);if(t.client.socketCallBackResponse(n.headers.messageId,{}),!s||!this.dingdingAllowed(e,s.senderId))return void this.logWarn({bridgeId:e.id,hasEvent:Boolean(s),senderId:s?.senderId,allowed:s?this.dingdingAllowed(e,s.senderId):void 0},"dingding robot message ignored");const{chatId:i,text:r,senderId:a,sessionWebhook:o}=s;o&&t.chatWebhooks.set(i,o),s.conversationType&&t.conversationTypes.set(i,s.conversationType);const d=o||t.chatWebhooks.get(i)||Gd(e.settings.sessionWebhook);let c;a&&t.senderIds.set(i,a),e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:i}),this.logInfo({bridgeId:e.id,chatId:i,senderId:a,conversationType:s.conversationType,hasSessionWebhook:Boolean(o),hasCachedSessionWebhook:Boolean(t.chatWebhooks.get(i)),hasResolvedSessionWebhook:Boolean(d),textPreview:Jd(r)},"dingding chat gateway message received");try{c=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:a||void 0},message:{text:r,attachments:[]}})}catch(e){if(d)return void await td(this.fetchImpl,d,`Failed to handle message: ${Vd(e)}`,t.onLog);throw e}const l=$d(c);if(l){const n=Ld(c);if(await this.sendDingdingRoutedAiSessionProgress(c,e,t,i,l,{replyMarkup:n,sessionWebhook:d,senderId:a,sourcePrompt:r}))return;const s=_d({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:t}),o=await s.send(i,l,{replyMarkup:n,sessionWebhook:d,senderId:a});if(o)return void this.logInfo({bridgeId:e.id,chatId:i,senderId:a,provider:o.provider,interactionId:o.interactionId,routed:!0===Xd(c).routed,hasReplyMarkup:Boolean(n),replyPreview:Jd(l)},"dingding chat gateway reply sent");this.logWarn({bridgeId:e.id,chatId:i,senderId:a,hasReplyMarkup:Boolean(n),hasSessionWebhook:Boolean(d),hasRobotCode:Boolean(Gd(e.settings.robotCode)),replyPreview:Jd(l)},"dingding chat gateway reply skipped")}}async sendDingdingRoutedAiSessionProgress(e,t,n,s,i,r={}){if(!0!==e.routed)return!1;const a=tc(e),{instanceId:o,sessionId:d,turnId:c}=a;if(!o||!d||!c)return this.logWarn({bridgeId:t.id,chatId:s,instanceId:o,sessionId:d,turnId:c,turnIdSources:a.sources,routed:e.routed},"dingding ai session progress card not started"),!1;const l=dc(o,d,c,t.id,s);return!!await this.dingdingBridges.applyProgressUpdate({bridge:t,key:l,chatId:s,text:i,replyMarkup:r.replyMarkup})&&(this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:s,instanceId:o,sessionId:d,turnId:c,key:l,hasSessionWebhook:Boolean(r.sessionWebhook),hasSenderId:Boolean(r.senderId),sourcePrompt:Jd(r.sourcePrompt,80),textPreview:Jd(i)},"dingding ai session progress card started"),!0)}async handleDingdingCardCallback(e,t,n){const s=function(e){const t=pd(e),n=cd(t.cardActionData||t.content),s=cd(n.cardPrivateData||t.cardPrivateData||t.content),i=cd(s.params||n.params||t.params),r=ld(t.userId),a=ld(i.biz_sender_id);return{userId:r||void 0,chatId:Jo(t,i),senderId:r||void 0,deliverySenderId:a||void 0,sessionWebhook:ld(i.biz_session_webhook)||void 0,conversationType:Qo(i.biz_conversation_type||t.conversationType||t.conversation_type),callbackData:Yo(Zo(t,n,s)),body:t,params:i}}(n.data);if(!this.dingdingAllowed(e,s.userId))return Xo("not authorized",void 0,"unauthorized",s.body,s.params);const i=s.chatId||Gd(e.defaultChatId),r=s.deliverySenderId||s.senderId,a=s.sessionWebhook;let o;i&&a&&t.chatWebhooks.set(i,a),i&&s.conversationType&&t.conversationTypes.set(i,s.conversationType),i&&r&&t.senderIds.set(i,r);try{o=await this.parseChatGatewayCallbackAction(s.callbackData)}catch(e){if(Kd(e,"CHAT_PENDING_ACTION_STALE"))return Xo(Vd(e),void 0,"approval_stale",s.body,s.params);throw e}if(!o)return this.logWarn({bridgeId:e.id,chatId:i,userId:s.userId,senderId:r,...Qd(s)},"dingding card callback action unsupported"),Xo("Unsupported action",void 0,"unsupported",s.body,s.params);this.logInfo({bridgeId:e.id,chatId:i,userId:s.userId,senderId:r,actionType:o.type,...Qd(s)},"dingding card callback action parsed");const d=await this.service.handleChatGatewayAction({source:{channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:s.userId||void 0},action:o});return"pending-decision"===o.type&&qd(d)?Xo(`${o.decision} sent`,void 0,"approval_done",s.body,s.params):Xo($d(d)||Gd(d.message)||"updated",Ld(d),"updated",s.body,s.params)}async pollWechat(e){if(!e.token)return;const t=e.settings||{},n=this.wechatCursors.get(e.id)||Gd(t.updatesBuf),s=await async function(e){const t=Ed((e.bridge.settings||{}).baseUrl)||"https://ilinkai.weixin.qq.com",n=await e.fetchImpl(`${t}/ilink/bot/getupdates`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${e.bridge.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({get_updates_buf:e.cursor,base_info:{channel_version:"1.0.2"}})}),s=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`HTTP ${n.status}`);const i=Number(s.errcode??s.ret??0);if(0!==i)throw new Error(Ed(s.errmsg||s.msg)||`chat bridge API returned ${i}`);const r=[];for(const e of Array.isArray(s.msgs)?s.msgs:[]){const t=Nd(e);if(1!==t.message_type)continue;const n=Ed(t.from_user_id),s=zd(t);n&&s&&r.push({chatId:n,text:s,contextToken:Ed(t.context_token)})}return{cursor:Ed(s.get_updates_buf),messages:r}}({fetchImpl:this.fetchImpl,bridge:e,cursor:n});s.cursor&&(this.wechatCursors.set(e.id,s.cursor),this.updateChatBridge(e.id,{settings:{updatesBuf:s.cursor}}));for(const n of s.messages){const{chatId:s,contextToken:i,text:r}=n;if(e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:s,settings:{contextToken:i}}),e.defaultChatId&&e.defaultChatId!==s)continue;const a=$d(await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"wechat",bridgeId:e.id,chatSessionId:s},message:{text:r,attachments:[]}}));if(a){const n=_d({fetchImpl:this.fetchImpl,bridge:e});await n.send(s,a,{contextToken:i||Gd(t.contextToken)})}}}async pollPendingRoutes(){const e=await this.service.listPendingRoutes().catch(()=>[]);for(const t of e){if(this.announcedPending.has(t.id))continue;const e=nc(t,(e,t)=>this.service.pendingDecisionCallbackData(e,t)),n=t.aiSessionId?this.service.listChatSessions().filter(e=>e.activeInstanceId===t.instanceId&&e.activeAiSessionId===t.aiSessionId):[],s=new Set;for(const t of n)t.bridgeId&&await this.sendToChatBinding(t,e).then(e=>{e&&s.add(t.id)});(0!==s.size||await this.sendToDefaultBridgeChats(e))&&this.announcedPending.add(t.id)}}async sendToDefaultBridgeChats(e){let t=!1;for(const n of this.service.listChatBridges()){if(!n.enabled||!n.defaultChatId)continue;let s=!1;await this.sendViaBridge(n,n.defaultChatId,e).catch(e=>{this.bridgeErrors.set(n.id,e instanceof Error?e.message:String(e)),s=!0})?t=!0:s||this.bridgeErrors.set(n.id,"Chat bridge message was not delivered.")}return t}async sendToChatBinding(e,t){if(!e.bridgeId)return!1;const n=this.service.requireChatBridge(e.bridgeId);if(!n.enabled)return!1;const s=await this.sendViaBridge(n,e.chatSessionId,t).catch(e=>{this.bridgeErrors.set(n.id,e instanceof Error?e.message:String(e))});return Boolean(s)}async sendViaBridge(e,t,n){const s="string"==typeof n?n:n.text,i="string"==typeof n?void 0:n.replyMarkup;return _d({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:this.dingdingBridges.get(e.id)}).send(t,s,{replyMarkup:i})}async pollTelegram(e){if(!e.token)return;const t=Number(e.settings?.telegramLastUpdateId),n=this.telegramOffsets.get(e.id)||(Number.isInteger(t)?t:void 0),s=await async function(e){const t=new URLSearchParams({timeout:"0",...e.offset?{offset:String(e.offset+1)}:{}}),n=await e.fetchImpl(`https://api.telegram.org/bot${e.bridge.token}/getUpdates?${t.toString()}`),s=await n.json().catch(()=>({}));if(!n.ok||!1===s.ok){if(409===n.status||/terminated by other getUpdates request/i.test(String(s.description||"")))return{updates:[],conflict:s.description||"Telegram getUpdates conflict: another bot instance is polling this token."};throw new Error(s.description||`Telegram getUpdates failed with HTTP ${n.status}`)}const i=[];for(const t of s.result||[]){const n=Number(t.update_id),s=Number.isInteger(n)?n:void 0,r=Cd(t.callback_query);if(Object.keys(r).length>0){i.push({updateId:s,callbackQuery:r});continue}const a=Cd(t.message||t.edited_message);if(0===Object.keys(a).length)continue;const o=Cd(a.chat),d=Cd(a.from),c=void 0!==o.id?String(o.id):e.bridge.defaultChatId,l=void 0!==d.id?String(d.id):void 0;if(!c)continue;const p=xd(a.text??a.caption),u=bd(a);(p||0!==u.length)&&i.push({updateId:s,chatId:c,userId:l,text:p,rawText:p,message:a,messageId:kd(a),replyToMessageId:kd(Cd(a.reply_to_message)),quoteText:xd(Cd(a.quote).text),autoBegin:1===u.length&&!p.trim(),hasSingleImageWithoutText:1===u.length&&!p.trim()})}return{updates:i}}({fetchImpl:this.fetchImpl,bridge:e,offset:n});if(s.conflict)this.bridgeErrors.set(e.id,s.conflict);else for(const t of s.updates){if(void 0!==t.updateId){const n=`${e.id}:${t.updateId}`;if(this.seenTelegramUpdates.has(n)||t.updateId<=(this.telegramOffsets.get(e.id)||0))continue;this.seenTelegramUpdates.add(n),this.telegramOffsets.set(e.id,t.updateId),this.updateChatBridge(e.id,{settings:{...e.settings,telegramLastUpdateId:t.updateId}})}if("callbackQuery"in t){const n=t.callbackQuery;await this.handleTelegramCallback(e,n).catch(async t=>{const s=Xd(n.message),i=Xd(s.chat),r=void 0!==i.id?String(i.id):e.defaultChatId;this.bridgeErrors.set(e.id,Vd(t)),r&&await this.sendTelegramMessage(e,r,`Action failed: ${Vd(t)}`).catch(t=>{this.bridgeErrors.set(e.id,Vd(t))})});continue}if(!this.telegramAllowed(e,t.userId))continue;const n=await Sd({fetchImpl:this.fetchImpl,bridge:e,message:t.message,onImageDownloadError:({fileId:t,fileName:n,error:s})=>{this.logWarn({bridgeId:e.id,fileId:t,fileName:n,error:Vd(s)},"telegram image download skipped")}}),s=t.rawText.trim();(s||n.length)&&(this.logInfo({bridgeId:e.id,chatId:t.chatId,messageId:t.messageId,replyToMessageId:t.replyToMessageId,hasQuote:Boolean(t.quoteText),textPreview:Jd(s)},"telegram chat gateway message received"),await this.handleTelegramIncomingText(e,t.chatId,t.userId,s,n,{sourceMessageId:t.messageId,replyToMessageId:t.replyToMessageId,quoteText:t.quoteText},{autoBegin:t.autoBegin}))}}async handleTelegramIncomingText(e,t,n,s,i=[],r={},a={}){const o=s.trim(),d=o.split(/\s+/,1)[0]?.toLowerCase()||"",c=Fd(e.id,t,n),l=this.telegramMessageAggregates.get(c);return a.autoBegin&&!l?(this.startTelegramAggregate(c,e,t,n,r),this.enqueueTelegramAggregate(c,e,t,n,s,i,r,!0),void await this.sendTelegramMessage(e,t,"Image received. Continue sending messages, then send /end when done.",{replyToMessageId:r.sourceMessageId,replyMarkup:Ea([[{text:"/end",callbackData:this.telegramAggregateEndCallbackData(c)}]])})):"/begin"===d?(this.startTelegramAggregate(c,e,t,n,r),void await this.sendTelegramMessage(e,t,"Started collecting messages. Send /end when done.",{replyToMessageId:r.sourceMessageId,replyMarkup:Ea([[{text:"/end",callbackData:this.telegramAggregateEndCallbackData(c)}]])})):void("/end"!==d?l?.explicit?this.enqueueTelegramAggregate(c,e,t,n,s,i,r,!0):this.isImmediateTelegramCommand(o)?await this.dispatchTelegramGatewayMessage(e,t,n,s,i,r):this.enqueueTelegramAggregate(c,e,t,n,s,i,r,!1):await this.flushTelegramAggregate(c,"manual"))}isImmediateTelegramCommand(e){return e.trim().startsWith("/")}startTelegramAggregate(e,t,n,s,i={}){const r=this.telegramMessageAggregates.get(e);r?.timer&&clearTimeout(r.timer),this.telegramMessageAggregates.set(e,{bridgeId:t.id,chatId:n,userId:s,texts:[],attachments:[],context:i,explicit:!0})}enqueueTelegramAggregate(e,t,n,s,i,r,a,o){const d=this.telegramMessageAggregates.get(e);d?.timer&&clearTimeout(d.timer);const c=d||{bridgeId:t.id,chatId:n,userId:s,texts:[],attachments:[],context:a,explicit:o};var l,p;c.bridgeId=t.id,c.chatId=n,c.userId=s,c.context=(l=c.context,p=a,{sourceMessageId:l?.sourceMessageId??p.sourceMessageId,replyToMessageId:l?.replyToMessageId??p.replyToMessageId,quoteText:l?.quoteText||p.quoteText}),c.explicit=c.explicit||o,i.trim()&&c.texts.push(i),c.attachments.push(...r),c.explicit?c.timer=void 0:c.timer=setTimeout(()=>{this.flushTelegramAggregate(e,"timer")},1e3),this.telegramMessageAggregates.set(e,c)}async flushTelegramAggregate(e,t){const n=this.telegramMessageAggregates.get(e);if(!n)return!1;n.timer&&clearTimeout(n.timer),this.telegramMessageAggregates.delete(e);const s=this.service.requireChatBridge(n.bridgeId),i=n.texts.join("\n\n").trim();return i||n.attachments.length?(await this.dispatchTelegramGatewayMessage(s,n.chatId,n.userId,i,n.attachments,n.context),!0):("timer"!==t&&await this.sendTelegramMessage(s,n.chatId,"No messages collected.",{replyToMessageId:n.context?.sourceMessageId}),!1)}async handleTelegramAggregateEndCallback(e,t,n,s,i){const r=this.telegramMessageAggregateEndTokens.get(s)||"";if(r!==Fd(e.id,t,i))return void await this.answerTelegramCallback(e,n,"This collection is not active here");const a=await this.flushTelegramAggregate(r,"callback");await this.answerTelegramCallback(e,n,a?"Collected message sent":"No messages collected")}telegramAggregateEndCallbackData(e){const t=o.createHash("sha256").update(e).digest("base64url").slice(0,16);return this.telegramMessageAggregateEndTokens.set(t,e),`task_handoff:cp_msg_end:${t}`}async dispatchTelegramGatewayMessage(e,t,n,s,i=[],r={}){try{const a=void 0!==r.replyToMessageId?this.telegramReplyAiSessionTarget(e.id,t,r.replyToMessageId):void 0,o=function(e,t){const n=String(t||"").trim(),s=e.trim();return n?[`引用:${n}`,s].filter(Boolean).join("\n"):s}(s,r.quoteText),d=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"telegram",bridgeId:e.id,chatSessionId:t,userId:n},message:{text:o,attachments:i},...a?{target:{instanceId:a.instanceId,aiSessionId:a.sessionId}}:{}}),c=$d(d),l=Ld(d);if(c){const n=await this.sendTelegramMessage(e,t,c,{replyMarkup:l,replyToMessageId:r.sourceMessageId});this.logInfo({bridgeId:e.id,chatId:t,sourceMessageId:r.sourceMessageId,replyToMessageId:r.replyToMessageId,replyTargetInstanceId:a?.instanceId,replyTargetSessionId:a?.sessionId,sentMessageId:Number(Xd(n).message_id)||void 0,routed:!0===Xd(d).routed,replyPreview:Jd(c)},"telegram chat gateway reply sent"),await this.rememberTelegramProgressMessage(d,e,t,n,c)}}catch(n){this.bridgeErrors.set(e.id,Vd(n)),await this.sendTelegramMessage(e,t,`Failed to handle message: ${Vd(n)}`,{replyToMessageId:r.sourceMessageId}).catch(t=>{this.bridgeErrors.set(e.id,Vd(t))})}}async handleTelegramCallback(e,t){const n=Gd(t.data),s=Xd(t.from),i=Xd(t.message),r=Xd(i.chat),a=void 0!==r.id?String(r.id):e.defaultChatId,o=void 0!==s.id?String(s.id):void 0;if(!a||!this.telegramAllowed(e,o))return;const d=n.match(/^task_handoff:cp_ai_cancel:([^:]+)$/);if(d){const n=this.aiSessionCancelTokens.get(d[1]);return n?void await this.handleTelegramAiSessionCancelCallback(e,a,Gd(t.id),n.instanceId,n.sessionId,o,Ud(i)):void await this.answerTelegramCallback(e,Gd(t.id),"This AI session action expired")}const c=n.match(/^task_handoff:cp_ai_steer:([^:]+)$/);if(c){const n=this.aiSessionQueueActionTokens.get(c[1]);return n&&"steer"===n.type?void await this.handleTelegramAiSessionQueueSteerCallback(e,a,Gd(t.id),n.instanceId,n.sessionId,n.queueId,o,Ud(i)):void await this.answerTelegramCallback(e,Gd(t.id),"This AI session action expired")}const l=n.match(/^task_handoff:cp_ai_qdel_menu:([^:]+)$/);if(l){const n=this.aiSessionQueueActionTokens.get(l[1]);return n&&"delete-menu"===n.type?void await this.handleTelegramAiSessionQueueDeleteMenuCallback(e,a,Gd(t.id),n.instanceId,n.sessionId,o,Ud(i)):void await this.answerTelegramCallback(e,Gd(t.id),"This AI session action expired")}const p=n.match(/^task_handoff:cp_ai_qdel:([^:]+)$/);if(p){const n=this.aiSessionQueueActionTokens.get(p[1]);return n&&"delete-item"===n.type?void await this.handleTelegramAiSessionQueueDeleteItemCallback(e,a,Gd(t.id),Ud(i),n.instanceId,n.sessionId,n.queueId,o):void await this.answerTelegramCallback(e,Gd(t.id),"This AI session action expired")}const u=n.match(/^task_handoff:cp_msg_end:([^:]+)$/);if(u)return void await this.handleTelegramAggregateEndCallback(e,a,Gd(t.id),u[1],o);let g;try{g=await this.parseChatGatewayCallbackAction(n)}catch(n){if(Kd(n,"CHAT_PENDING_ACTION_STALE")){await this.answerTelegramCallback(e,Gd(t.id),Vd(n));const s=Ud(i);return void(void 0!==s&&await this.deleteTelegramStandaloneApprovalMessage(e,a,s))}throw n}if(!g)return void await this.answerTelegramCallback(e,Gd(t.id),"Unsupported action");const m=await this.service.handleChatGatewayAction({source:{channel:"telegram",bridgeId:e.id,chatSessionId:a,userId:o},action:g});if("pending-decision"===g.type&&qd(m)){await this.answerTelegramCallback(e,Gd(t.id),"");const n=Ud(i);return void(void 0!==n&&await this.deleteTelegramStandaloneApprovalMessage(e,a,n))}await this.answerTelegramCallback(e,Gd(t.id),Gd(m.message)||"updated");const h=$d(m),y=Ld(m),f=i.message_id;h&&"number"==typeof f?await this.editTelegramMessage(e,a,f,h,{replyMarkup:y}):h&&await this.sendTelegramMessage(e,a,h,{replyMarkup:y})}async deleteTelegramStandaloneApprovalMessage(e,t,n){[...this.telegramProgress.entries.values()].find(s=>s.messageId===n&&s.route?.bridgeId===e.id&&s.route?.chatId===t)||await this.deleteTelegramMessage(e,t,n).catch(s=>{this.logWarn({bridgeId:e.id,chatId:t,messageId:n,error:Vd(s)},"telegram callback message delete failed")})}async handleTelegramAiSessionCancelCallback(e,t,n,s,i,r,a){if(this.telegramAiSessionActionAllowed(e,t,s,i,a))try{await this.service.interruptAiSession(s,i),await this.answerTelegramCallback(e,n,"Interrupt sent"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:s,sessionId:i,userId:r},"telegram ai session cancel sent")}catch(t){this.bridgeErrors.set(e.id,Vd(t)),await this.answerTelegramCallback(e,n,`Interrupt failed: ${Jd(Vd(t),120)}`)}else await this.answerTelegramCallback(e,n,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueSteerCallback(e,t,n,s,i,r,a,o){if(this.telegramAiSessionActionAllowed(e,t,s,i,o))try{await this.service.steerAiSessionQueuedMessage(s,i,r),await this.answerTelegramCallback(e,n,"Steered queued message"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:s,sessionId:i,queueId:r,userId:a},"telegram ai session queued message steered")}catch(t){this.bridgeErrors.set(e.id,Vd(t)),await this.answerTelegramCallback(e,n,`Steer failed: ${Jd(Vd(t),120)}`)}else await this.answerTelegramCallback(e,n,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueDeleteMenuCallback(e,t,n,s,i,r,a){if(this.telegramAiSessionActionAllowed(e,t,s,i,a))try{const a=Xd(await this.service.aiSessionQueue(s,i)),o=(Array.isArray(a.items)?a.items:[]).map(e=>Xd(e)).filter(e=>"queued"===Gd(e.status)&&Gd(e.id));if(!o.length)return void await this.answerTelegramCallback(e,n,"Queue is empty");const d=o.slice(0,sc).map((e,t)=>[{text:oc(Gd(e.message),t),callbackData:this.aiSessionQueueDeleteItemCallbackData(s,i,Gd(e.id))}]);await this.sendTelegramMessage(e,t,"Delete queued message",{replyMarkup:Ea(d)}),await this.answerTelegramCallback(e,n,"Select a queued message"),this.logInfo({bridgeId:e.id,chatId:t,instanceId:s,sessionId:i,itemCount:o.length,userId:r},"telegram ai session queue delete menu sent")}catch(t){this.bridgeErrors.set(e.id,Vd(t)),await this.answerTelegramCallback(e,n,`Queue menu failed: ${Jd(Vd(t),120)}`)}else await this.answerTelegramCallback(e,n,"This chat is not bound to that AI session")}async handleTelegramAiSessionQueueDeleteItemCallback(e,t,n,s,i,r,a,o){if(this.telegramAiSessionActionAllowed(e,t,i,r,s))try{await this.service.removeAiSessionQueuedMessage(i,r,a),await this.answerTelegramCallback(e,n,"Queued message deleted"),Number.isInteger(s)&&await this.deleteTelegramMessage(e,t,s).catch(n=>{this.logWarn({bridgeId:e.id,chatId:t,instanceId:i,sessionId:r,queueId:a,messageId:s,error:Vd(n)},"telegram ai session queue delete menu message delete failed")}),this.logInfo({bridgeId:e.id,chatId:t,instanceId:i,sessionId:r,queueId:a,userId:o},"telegram ai session queued message deleted")}catch(t){this.bridgeErrors.set(e.id,Vd(t)),await this.answerTelegramCallback(e,n,`Delete failed: ${Jd(Vd(t),120)}`)}else await this.answerTelegramCallback(e,n,"This chat is not bound to that AI session")}telegramAiSessionBindingActive(e,t,n,s){return this.service.listChatSessions().some(i=>i.bridgeId===e.id&&i.chatSessionId===t&&i.activeInstanceId===n&&i.activeAiSessionId===s)}telegramAiSessionActionAllowed(e,t,n,s,i){if(this.telegramAiSessionBindingActive(e,t,n,s))return!0;if(!Number.isInteger(i))return!1;const r=this.telegramReplyAiSessionTarget(e.id,t,i);return Boolean(r&&r.instanceId===n&&r.sessionId===s)}async parseChatGatewayCallbackAction(e){const t=function(e){const t=e.match(/^task_handoff:cp_p:v1:([ads]):([A-Za-z0-9_-]{16})$/);if(!t)return;const n=Object.entries(va).find(([,e])=>e===t[1])?.[0];return"allow"===n||"deny"===n||"skip"===n?{routeFingerprint:t[2],decision:n}:void 0}(e);if(t){const e=(await this.service.listPendingRoutes()).filter(e=>"approval"===e.kind&&"pending"===e.status&&wa(e.id)===t.routeFingerprint);if(1!==e.length){const e=new Error("This approval is no longer pending.");throw Object.assign(e,{statusCode:409,code:"CHAT_PENDING_ACTION_STALE"}),e}return{type:"pending-decision",routeId:e[0].id,decision:t.decision}}return function(e,t){const n=e.match(/^task_handoff:cp_session:(\d+)$/);if(n)return{type:"ai-session",index:Number(n[1])};const s=e.match(/^task_handoff:cp_i:([^:]+)$/)||e.match(/^task_handoff:cp_instance_apps:([^:]+)$/);if(s)return s[0].startsWith("task_handoff:cp_i:")?{type:"instance-app-menu",instanceId:Bo(t(s[1],"instance-app-menu").instanceId,"instanceId")}:{type:"instance-app-menu",instanceId:Ho(s[1])};const i=e.match(/^task_handoff:cp_a:([^:]+)$/)||e.match(/^task_handoff:cp_launch_app:([^:]+):([^:]+)$/);if(i){if(i[0].startsWith("task_handoff:cp_a:")){const e=t(i[1],"launch-app");return{type:"launch-app",instanceId:Bo(e.instanceId,"instanceId"),appId:Bo(e.appId,"appId")}}return{type:"launch-app",instanceId:Ho(i[1]),appId:Ho(i[2])}}const r=e.match(/^task_handoff:cp_p:([^:]+)$/);if(r){const e=t(r[1],"pending-decision");return{type:"pending-decision",routeId:Bo(e.routeId,"routeId"),decision:Uo(e.decision)}}const a=e.match(/^task_handoff:approval:([^:]+:ai:[^:]+):(allow|deny|skip)$/);return a?{type:"pending-decision",routeId:a[1],decision:a[2]}:void 0}(e,(e,t)=>this.service.resolveChatActionToken(e,t))}dingdingAllowed(e,t){const n=String(t||"").trim();return!!n&&(0===e.allowedUserIds.length?(e.allowedUserIds=[n],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:n},"dingding user bound"),!0):e.allowedUserIds.includes(n))}telegramAllowed(e,t){const n=String(t||"").trim();return!!n&&(0===e.allowedUserIds.length?(e.allowedUserIds=[n],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:n},"telegram user bound"),!0):e.allowedUserIds.includes(n))}async answerTelegramCallback(e,t,n){return async function(e,t,n,s){t.token&&n&&await e(`https://api.telegram.org/bot${t.token}/answerCallbackQuery`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({callback_query_id:n,text:s.slice(0,200)})})}(this.fetchImpl,e,t,n)}async editTelegramMessage(e,t,n,s,i={}){return async function(e,t,n,s,i,r={}){if(t.token)return qo(n=>e(`https://api.telegram.org/bot${t.token}/editMessageText`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),"editMessageText",i,{chat_id:n,message_id:s,...r.replyMarkup?{reply_markup:r.replyMarkup}:{}},r)}(this.fetchImpl,e,t,n,s,i)}async sendTelegramMessage(e,t,n,s={}){return Mo(this.fetchImpl,e,t,n,s)}async deleteTelegramMessage(e,t,n){return async function(e,t,n,s){if(!t.token||!Number.isInteger(s))return;const i=await e(`https://api.telegram.org/bot${t.token}/deleteMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({chat_id:n,message_id:s})}),r=await i.json().catch(()=>({}));if(!i.ok||!1===r.ok)throw new Error(r.description||`Telegram deleteMessage failed with HTTP ${i.status}`)}(this.fetchImpl,e,t,n)}async rememberTelegramProgressMessage(e,t,n,s,i){if(!0!==e.routed)return;const r=tc(e),{instanceId:a,sessionId:o,turnId:d}=r,c=Number(Xd(s).message_id);if(!(a&&o&&d&&Number.isInteger(c)))return void this.logWarn({bridgeId:t.id,chatId:n,instanceId:a,sessionId:o,turnId:d,turnIdSources:r.sources,messageId:Number.isInteger(c)?c:void 0,routed:e.routed},"telegram ai session progress message not remembered");const l=dc(a,o,d,t.id,n),p=this.telegramProgress.remember(l,c,i,{bridgeId:t.id,chatId:n});this.telegramProgressMessageTargets.set(Hd(t.id,n,c),{instanceId:a,sessionId:o,turnId:d,messageId:c}),this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:n,instanceId:a,sessionId:o,turnId:d,turnIdSources:r.sources,key:l,messageId:c,progressKeys:this.telegramProgressKeysFor(a,o,t.id,n),textPreview:Jd(i)},"telegram ai session progress message remembered"),"queue"===function(e){return e.aiSession&&"action"in e.aiSession?e.aiSession.action:void 0}(e)&&p?.messageId&&p.messageId!==c&&(this.telegramProgressMessageTargets.delete(Hd(t.id,n,p.messageId)),await this.deleteTelegramMessage(t,n,p.messageId).catch(e=>{this.logWarn({bridgeId:t.id,chatId:n,instanceId:a,sessionId:o,turnId:d,replacedMessageId:p.messageId,replacementMessageId:c,error:Vd(e)},"telegram queued progress message replacement cleanup failed")}))}telegramReplyAiSessionTarget(e,t,n){const s=this.telegramProgressMessageTargets.get(Hd(e,t,n));if(s)return s;for(const[s,i]of this.telegramProgress.entries)if(i.messageId===n&&i.route?.bridgeId===e&&i.route?.chatId===t){const i=Wd(s,e,t);if(i){const s={...i,messageId:n};return this.telegramProgressMessageTargets.set(Hd(e,t,n),s),s}}}async pollAiSessionUpdates(){this.logAiSessionDelivery({stage:"poll-tick"});const e=await this.service.listAiSessions({refresh:!0}).catch(()=>{});if(!e)return void this.logAiSessionDelivery({stage:"poll-no-snapshot"});const t=new Map(e.instances.map(e=>[e.instanceId,e.aiSessions]));this.logAiSessionDelivery({stage:"poll-snapshot",instanceCount:t.size,instances:[...t.entries()].map(([e,t])=>({instanceId:e,sessionCount:t.sessions?.length||0,runningCount:t.runningCount,waitingCount:t.waitingCount,updatedAt:t.updatedAt}))});const n=await this.aiSessionInstanceNames();for(const[e,s]of t)await this.deliverAiSessionSnapshot(e,s,n)}async deliverAiSessionSnapshot(e,t,n){const s=Array.isArray(t.sessions)?t.sessions:[];if(this.pruneAiSessionDeliveryState(e,new Set(s.map(e=>e.id))),0===s.length)return;const i=n||await this.aiSessionInstanceNames(),r=await this.aiSessionPendingApprovalRoutes(e);await this.deliverOwnedTelegramProgress(e,s,i,r),await this.deliverActiveBindingTelegramProgress(e,s,i,r)}pruneAiSessionDeliveryState(e,t){for(const[n,s]of this.telegramProgress.entries){const i=s.route?.bridgeId&&s.route.chatId?this.telegramProgressMessageTargets.get(Hd(s.route.bridgeId,s.route.chatId,s.messageId||0))||Wd(n,s.route.bridgeId,s.route.chatId):void 0;i&&i.instanceId===e&&!t.has(i.sessionId)&&(s.timer&&clearTimeout(s.timer),this.telegramProgress.entries.delete(n),this.deliveredAiSessionFingerprints.delete(n),s.route?.bridgeId&&s.route.chatId&&s.messageId&&this.telegramProgressMessageTargets.delete(Hd(s.route.bridgeId,s.route.chatId,s.messageId)))}}async deliverActiveBindingTelegramProgress(e,t,n,s){for(const i of this.service.listChatSessions()){if(!i.bridgeId||i.activeInstanceId!==e||!i.activeAiSessionId)continue;const r=t.find(e=>e.id===i.activeAiSessionId);if(!r)continue;const a=gc(n.get(e)||e,r),o=uc(r,a);if(!o){this.logAiSessionDelivery({stage:"skip-empty-text",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:r.status,phase:r.phase,activeTurnId:r.activeTurnId,latestTurn:Yd(hc(r))});continue}const d=cc(r);if(!d){this.logAiSessionDelivery({stage:"skip-empty-turn-id",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:r.status,phase:r.phase,activeTurnId:r.activeTurnId,latestTurn:Yd(hc(r))});continue}if(this.listTelegramOwnedProgressEntries(e).some(e=>e.route.bridgeId===i.bridgeId&&e.route.chatId===i.chatSessionId&&e.owner.sessionId===r.id&&e.owner.turnId===d))continue;const c=dc(e,r.id,d,i.bridgeId||"",i.chatSessionId),l=lc(r,a);if(!l||this.deliveredAiSessionFingerprints.get(c)===l){this.logAiSessionDelivery({stage:"skip-duplicate-fingerprint",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,hasFingerprint:Boolean(l),fingerprintMatched:this.deliveredAiSessionFingerprints.get(c)===l,latestTurn:Yd(hc(r)),textPreview:Jd(o)});continue}const p=rc({cancelCallbackData:this.aiSessionCancelCallbackData(e,r.id),queueSteerCallbackData:t=>this.aiSessionQueueSteerCallbackData(e,r.id,t),queueDeleteMenuCallbackData:()=>this.aiSessionQueueDeleteMenuCallbackData(e,r.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:s.get(r.id)||ac(e,r),session:r});if(this.logAiSessionDelivery({stage:"attempt",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,r.id,i.bridgeId||"",i.chatSessionId),hasProgressEntry:this.telegramProgress.entries.has(c),status:r.status,phase:r.phase,activeTurnId:r.activeTurnId,latestTurn:Yd(hc(r)),textPreview:Jd(o)}),await this.updateChatAiSessionProgress(i,o,c,p).catch(t=>(this.bridgeErrors.set(i.bridgeId||"",t instanceof Error?t.message:String(t)),this.logWarn({bridgeId:i.bridgeId,chatId:i.chatSessionId,instanceId:e,sessionId:r.id,turnId:d,key:c,error:Vd(t)},"chat ai session progress update failed"),!1)))this.logAiSessionDelivery({stage:"delivered",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:Jd(o)}),this.deliveredAiSessionFingerprints.set(c,l);else{if("telegram"===this.service.requireChatBridge(i.bridgeId).channel){const t=this.telegramProgress.entries.get(c),n=this.service.requireChatBridge(i.bridgeId);this.logAiSessionDelivery({stage:t?"telegram-update-not-delivered":"telegram-no-progress-entry",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,r.id,i.bridgeId||"",i.chatSessionId),bridgeEnabled:n.enabled,bridgeChannel:n.channel,progressMessageId:t?.messageId,progressLastText:Jd(t?.lastText),progressHasPending:Boolean(t?.pending),latestTurn:Yd(hc(r)),textPreview:Jd(o)});continue}await this.sendToChatBinding(i,{text:o}).catch(e=>(this.bridgeErrors.set(i.bridgeId||"",e instanceof Error?e.message:String(e)),!1))&&(this.logAiSessionDelivery({stage:"fallback-sent",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:Jd(o)}),this.deliveredAiSessionFingerprints.set(c,l))}}}async deliverOwnedTelegramProgress(e,t,n,s){for(const i of this.listTelegramOwnedProgressEntries(e)){const r=this.service.requireChatBridge(i.route.bridgeId);if("telegram"!==r.channel||!r.enabled)continue;const a=this.matchTelegramOwnedProgressSession(i,t);if(!a)continue;const o={bridgeId:i.route.bridgeId,chatSessionId:i.route.chatId},d=gc(n.get(e)||e,a),c=uc(a,d),l=cc(a);if(!c||!l){this.logAiSessionDelivery({stage:"remembered-skip-turn-or-text",key:i.key,parsedTurnId:i.owner.turnId,currentTurnId:l,hasText:Boolean(c),instanceId:e,sessionId:a.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}if(i.owner.turnId&&l!==i.owner.turnId){this.logAiSessionDelivery({stage:"remembered-skip-different-active-turn",key:i.key,ownerTurnId:i.owner.turnId,currentTurnId:l,instanceId:e,sessionId:a.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const p=l===i.owner.turnId?i.key:dc(e,a.id,l,i.route.bridgeId,i.route.chatId);if(p!==i.key&&this.telegramProgress.entries.has(p))continue;if(p!==i.key&&!this.rekeyTelegramOwnedProgress(i,p,{instanceId:e,sessionId:a.id,turnId:l}))continue;const u=lc(a,d);if(!u||this.deliveredAiSessionFingerprints.get(p)===u){this.logAiSessionDelivery({stage:"remembered-skip-fingerprint",key:i.key,hasFingerprint:Boolean(u),fingerprintMatched:this.deliveredAiSessionFingerprints.get(p)===u,instanceId:e,sessionId:a.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const g=rc({cancelCallbackData:this.aiSessionCancelCallbackData(e,a.id),queueSteerCallbackData:t=>this.aiSessionQueueSteerCallbackData(e,a.id,t),queueDeleteMenuCallbackData:()=>this.aiSessionQueueDeleteMenuCallbackData(e,a.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:s.get(a.id)||ac(e,a),session:a}),m=await this.updateChatAiSessionProgress(o,c,p,g).catch(e=>(this.bridgeErrors.set(o.bridgeId||"",e instanceof Error?e.message:String(e)),!1));this.logAiSessionDelivery({stage:m?"remembered-delivered":"remembered-not-delivered",key:p,instanceId:e,sessionId:a.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId,textPreview:Jd(c)}),m&&(this.telegramProgressMessageTargets.set(Hd(i.route.bridgeId,i.route.chatId,i.messageId),{instanceId:e,sessionId:a.id,turnId:l,messageId:i.messageId}),this.deliveredAiSessionFingerprints.set(p,u))}}rekeyTelegramOwnedProgress(e,t,n){return!!this.telegramProgress.rekey(e.key,t)&&(this.deliveredAiSessionFingerprints.delete(e.key),this.telegramProgressMessageTargets.set(Hd(e.route.bridgeId,e.route.chatId,e.messageId),{...n,messageId:e.messageId}),!0)}logAiSessionDelivery(e){this.logInfo(e,"telegram ai session delivery flow")}telegramProgressKeysFor(e,t,n,s){const i=`${e}:${t}:`,r=`:${n}:${s}`;return[...this.telegramProgress.entries.keys()].filter(e=>e.startsWith(i)&&e.endsWith(r))}listTelegramOwnedProgressEntries(e){const t=[];for(const[n,s]of this.telegramProgress.entries){if(!s.route?.bridgeId||!s.route.chatId||!Number.isInteger(s.messageId))continue;const i=this.telegramProgressMessageTargets.get(Hd(s.route.bridgeId,s.route.chatId,s.messageId))||Wd(n,s.route.bridgeId,s.route.chatId);i&&i.instanceId===e&&t.push({key:n,route:{bridgeId:s.route.bridgeId,chatId:s.route.chatId},messageId:s.messageId,owner:{...i,messageId:s.messageId}})}return t}matchTelegramOwnedProgressSession(e,t){const n=t.find(t=>t.id===e.owner.sessionId);if(n)return n;if(e.owner.turnId){const n=t.find(t=>t.activeTurnId===e.owner.turnId||hc(t)?.id===e.owner.turnId||hc(t)?.providerTurnId===e.owner.turnId);if(n)return n}}async updateChatAiSessionProgress(e,t,n,s){if(!e.bridgeId)return this.logAiSessionDelivery({stage:"chat-update-skip-missing-bridge",chatId:e.chatSessionId,key:n}),!1;const i=this.service.requireChatBridge(e.bridgeId);if(!i.enabled)return this.logAiSessionDelivery({stage:"chat-update-skip-bridge-state",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,bridgeEnabled:i.enabled,bridgeChannel:i.channel}),!1;if("dingding"===i.channel){if(!this.dingdingBridges.get(i.id))return!1;const r=await this.dingdingBridges.applyProgressUpdate({bridge:i,key:n,chatId:e.chatSessionId,text:t,replyMarkup:Bd({actionRows:s})});return r&&this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,textPreview:Jd(t)},"dingding ai session progress updated"),r}if("telegram"!==i.channel)return!1;const r=this.telegramProgress.entries.get(n);if(!await this.telegramProgress.applyUpdate({bridge:i,key:n,chatId:e.chatSessionId,text:t,actionRows:s})){const t=this.telegramProgress.entries.get(n);return this.logAiSessionDelivery({stage:"telegram-update-returned-false",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,progressMessageId:r?.messageId||t?.messageId,progressLastText:Jd(r?.lastText||t?.lastText),progressHasPending:Boolean(r?.pending||t?.pending)}),!1}return this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,messageId:r?.messageId||this.telegramProgress.entries.get(n)?.messageId,textPreview:Jd(t)},r?"telegram ai session progress edited":"telegram ai session progress sent"),!0}async aiSessionInstanceNames(){return this.aiSessionInstanceNamesCache.get()}async aiSessionPendingApprovalRoutes(e){const t=await this.aiSessionPendingRoutesCache.get();return new Map(t.filter(t=>t.instanceId===e&&t.aiSessionId&&"approval"===t.kind).map(e=>[e.aiSessionId||"",e]))}aiSessionCancelCallbackData(e,t){const n=o.createHash("sha256").update(`${e}\0${t}`).digest("base64url").slice(0,16);return this.aiSessionCancelTokens.set(n,{instanceId:e,sessionId:t}),`task_handoff:cp_ai_cancel:${n}`}aiSessionQueueSteerCallbackData(e,t,n){return`task_handoff:cp_ai_steer:${this.aiSessionQueueActionToken({type:"steer",instanceId:e,sessionId:t,queueId:n})}`}aiSessionQueueDeleteMenuCallbackData(e,t){return`task_handoff:cp_ai_qdel_menu:${this.aiSessionQueueActionToken({type:"delete-menu",instanceId:e,sessionId:t})}`}aiSessionQueueDeleteItemCallbackData(e,t,n){return`task_handoff:cp_ai_qdel:${this.aiSessionQueueActionToken({type:"delete-item",instanceId:e,sessionId:t,queueId:n})}`}aiSessionQueueActionToken(e){const t=o.createHash("sha256").update([e.type,e.instanceId,e.sessionId,"queueId"in e?e.queueId:""].join("\0")).digest("base64url").slice(0,16);return this.aiSessionQueueActionTokens.set(t,e),t}}function $d(e){return"object"==typeof e&&e&&"reply"in e?String(e.reply||""):""}function qd(e){return"object"==typeof e&&null!==e&&!0===e.accepted}function Ld(e){const t="object"==typeof e&&e&&"replyMarkup"in e?e.replyMarkup:void 0;if(t&&"object"==typeof t&&!Array.isArray(t))return t}function Bd(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return Ea(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function Ud(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function Fd(e,t,n){return[e,t,n||""].join(":")}function Hd(e,t,n){return[e,t,String(n)].join(":")}function Wd(e,t,n){const s=`:${t}:${n}`;if(!e.endsWith(s))return;const i=e.slice(0,-s.length).split(":"),r=i[0]||"",a=i[1]||"",o=i.slice(2).join(":")||void 0;return r&&a?{instanceId:r,sessionId:a,turnId:o}:void 0}function Gd(e){return"string"==typeof e?e:""}function Vd(e){return e instanceof Error?e.message:String(e)}function Kd(e,t){return Boolean(e&&"object"==typeof e&&"code"in e&&e.code===t)}function Jd(e,t=160){const n=String(e||"").replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,Math.max(0,t-3))}...`}function Qd(e){const t=Zd(e.body.cardActionData||e.body.content),n=Zd(t.cardPrivateData||e.body.cardPrivateData||e.body.content);return{outTrackId:Gd(e.body.outTrackId),spaceId:Gd(e.body.spaceId),conversationType:e.conversationType,hasCallbackData:Boolean(e.callbackData),callbackDataPreview:Jd(e.callbackData,120),actionIds:[...ec(n.actionIdList),...ec(t.actionIdList),Gd(n.actionId),Gd(t.actionId),Gd(e.body.actionId)].filter(Boolean).slice(0,5),cardActionDataKeys:Object.keys(t).sort(),privateDataKeys:Object.keys(n).sort(),paramKeys:Object.keys(e.params).sort(),hasSessionWebhook:Boolean(e.sessionWebhook)}}function Yd(e){if(e)return{id:e.id,providerTurnId:e.providerTurnId,source:e.source,status:e.status,phase:e.phase,revision:e.revision,userPrompt:Jd(e.userPrompt,80),hasResponse:Boolean(e.lastMessage?.trim()||e.summary?.trim()),responsePreview:Jd(e.lastMessage||e.summary,80),updatedAt:e.updatedAt,observedAt:e.observedAt}}function Xd(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Zd(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e;if("string"==typeof e&&e.trim())try{return Xd(JSON.parse(e))}catch{return{}}return{}}function ec(e){return Array.isArray(e)?e.map(e=>Gd(e)).filter(Boolean):[]}function tc(e){const t=e.aiSession&&"session"in e.aiSession?e.aiSession:void 0,n=t?.session||(e.aiSession&&"id"in e.aiSession?e.aiSession:void 0);return{instanceId:e.instanceId||e.instance?.id||"",sessionId:e.aiSessionId||n?.id||e.binding?.activeAiSessionId||"",turnId:e.turnId||e.providerTurnId||t?.turnId||t?.providerTurnId||(n?cc(n):""),sources:{resultTurnId:e.turnId,resultProviderTurnId:e.providerTurnId,actionTurnId:t?.turnId,actionProviderTurnId:t?.providerTurnId,sessionActiveTurnId:n?.activeTurnId,latestTurnId:n?hc(n)?.id:void 0}}}function nc(e,t){const n=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,s="approval"===e.kind?`/approve ${e.id}\n/deny ${e.id}\n/skip ${e.id}`:`/reply ${e.id} <message>`,i=`[${n}]\n${e.result}\n\n${s}`;return"approval"!==e.kind?{text:i}:{text:i,replyMarkup:Ea([[{text:"Allow",callbackData:t(e.id,"allow")},{text:"Skip",callbackData:t(e.id,"skip")},{text:"Deny",callbackData:t(e.id,"deny")}]])}}const sc=5,ic=48;function rc(e){const{cancelCallbackData:t,queueSteerCallbackData:n,queueDeleteMenuCallbackData:s,permissionCallbackData:i,pendingApproval:r,session:a}=e,o="approval"===r?.kind?[[{text:"Allow",callbackData:i(r.id,"allow")},{text:"Skip",callbackData:i(r.id,"skip")},{text:"Deny",callbackData:i(r.id,"deny")}]]:[];if("running"!==a.status)return o;const d=(a.queue?.items||[]).filter(e=>"queued"===e.status).slice(0,sc);return[...d.map((e,t)=>[{text:oc(e.message,t),callbackData:n(e.id)}]),d.length?[{text:"Delete Queue",callbackData:s()},{text:"Cancel",callbackData:t}]:[{text:"Cancel",callbackData:t}]]}function ac(e,t){if("approval"===t.phase)return{id:`${e}:ai:${t.id}`,kind:"approval"}}function oc(e,t){const n=`${t+1}. `,s=e.replace(/\s+/g," ").trim()||"Queued message",i=Math.max(8,ic-n.length),r=Array.from(s);return`${n}${r.length>i?`${r.slice(0,Math.max(1,i-3)).join("")}...`:s}`}function dc(e,t,...n){return[e,t,...n].join(":")}function cc(e){const t=hc(e);return t?.id?t.id:Gd(e.activeTurnId)}function lc(e,t=""){const n=hc(e);return[t,n?.id||"",String(n?.revision??""),n?.status||e.status||"",n?.phase||e.phase||"",n?.lastMessage||"",n?.summary||"",n?.userPrompt||"",e.error||"",e.currentTool?.id||"",e.currentTool?.name||"",e.currentTool?.inputPreview||"",String(e.toolCallsSinceLastMessage??0),pc(e)].join("\0")}function pc(e){return(e.queue?.items||[]).map(e=>[e.id,e.status,e.message].join(":")).join("|")}function uc(e,t){const n=function(e,t){let n="";t?"waiting"===t.status&&"approval"===t.phase&&t.summary?.trim()?n=t.summary.trim():t.lastMessage?.trim()?n=t.lastMessage.trim():t.summary?.trim()&&(n=t.summary.trim()):"waiting"===e.status&&"approval"===e.phase&&e.summary?.trim()?n=e.summary.trim():e.lastMessage?.trim()?n=e.lastMessage.trim():e.summary?.trim()?n=e.summary.trim():e.error?.trim()&&(n="failed"===e.status?`${e.agent} session failed:\n${e.error.trim()}`:e.error.trim());const s=function(e){if(e.currentTool?.name)return`Thinking... · ${e.currentTool.name}${e.currentTool.inputPreview?` · ${e.currentTool.inputPreview}`:""}`;const t=e.toolCallsSinceLastMessage??0;return t>0?`Thinking... · ${t} ${1===t?"tool":"tools"} completed`:""}(e);return[n,s&&!mc(n,e)?s:""].filter(Boolean).join("\n\n")}(e,hc(e));return n?`${t}\n${n}`:""}function gc(e,t){const n=[t.status,t.phase&&"unknown"!==t.phase?t.phase:void 0].filter(Boolean).join("/");return`${e} · ${t.agent||"ai"} ${n||"unknown"}`}function mc(e,t){if("waiting"!==t.status||"approval"!==t.phase)return!1;const n=t.currentTool?.inputPreview?.trim();return Boolean(n&&e.includes(n))}function hc(e){return(e.turns||[]).filter(e=>e.id||e.userPrompt?.trim()||e.lastMessage?.trim()||e.summary?.trim()).at(-1)}class yc{clients=new Set;listeners=new Set;seq=0;connect(e){e.topics=new Set(["*"]),this.clients.add(e),e.on("close",()=>{this.clients.delete(e)}),e.on("message",t=>{const n=function(e){try{const t=JSON.parse(String(e||"{}"));return t&&"object"==typeof t?t:void 0}catch{return}}(t);"subscribe"===n?.type&&(e.topics=new Set(n.topics?.length?n.topics:["*"]))})}publish(e,t,n={}){const s=this.createEvent(e,t,n),i=s.topic,r=JSON.stringify(s);for(const e of this.listeners)try{e(s)}catch{this.listeners.delete(e)}for(const t of this.clients)if(t.readyState===t.OPEN&&fc(t.topics,i,e))try{t.send(r)}catch{this.clients.delete(t)}return s}on(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}send(e,t,n,s={}){const i=this.createEvent(t,n,s);return e.send(JSON.stringify(i)),i}createEvent(e,t,n={}){return{v:1,id:`evt_${Date.now().toString(36)}_${Math.random().toString(16).slice(2)}`,seq:++this.seq,type:e,topic:n.topic||Vs(e),createdAt:(new Date).toISOString(),payload:t,scope:n.scope}}}function fc(e,t,n){return!e||e.has("*")||e.has(t)||e.has(n)}const Ic={"image/bmp":".bmp","image/gif":".gif","image/jpeg":".jpg","image/png":".png","image/webp":".webp"},bc=a.z.object({instanceId:a.z.string().trim().min(1).max(120),sessionId:a.z.string().trim().min(1).max(120),kind:a.z.enum(["image","file"]),name:a.z.string().trim().min(1).max(240),mime:a.z.string().trim().min(1).max(120),data:a.z.string().min(1).max(31457280)}).strict();class vc{items=new Map;ttlMs;constructor(e=864e5){this.ttlMs=e}upload(e){this.pruneExpired();const s=bc.parse(e),i=function(e,t){const n=/^data:([^;,]+);base64,(.+)$/i.exec(e.trim()),s=n?.[1]||t,i=n?.[2]||e;return{mime:s,buffer:Buffer.from(i,"base64")}}(s.data,s.mime),r=i.mime.toLowerCase(),a=Ic[r]||"";if("image"===s.kind&&!a)throw new Error(`Unsupported image type: ${r}`);const d="image"===s.kind?i.buffer.length>S:i.buffer.length>=A;if(!i.buffer.length||d)throw new Error("image"===s.kind?"Image must be between 1 byte and 20971520 bytes.":"File must be smaller than 512000 bytes.");const c=`att_${o.randomUUID().replace(/-/g,"").slice(0,24)}`,l=new Date,p=new Date(l.getTime()+this.ttlMs),u=t.join(y.tmpdir(),"task-handoff-ai-session-attachments");n.mkdirSync(u,{recursive:!0});const g=function(e){return t.basename(e).replace(/[^\w.\- ()\u4e00-\u9fff]/g,"_").slice(0,160)||"attachment"}(s.name),m=t.join(u,`${c}-${g.endsWith(a)?g:`${g}${a}`}`);n.writeFileSync(m,i.buffer);const h={id:c,instanceId:s.instanceId,sessionId:s.sessionId,kind:s.kind,name:g,mime:r,size:i.buffer.length,source:{type:"inline",encoding:"base64",data:i.buffer.toString("base64")},path:m,createdAt:l.toISOString(),expiresAt:p.toISOString()};return this.items.set(c,h),this.publicAttachment(h)}resolveRefs(e,t,n){this.pruneExpired();const s=a.z.array(H).max(6).default([]).parse(e),i=s.map(e=>"runtime-path"===e.source.type?U.parse(e):this.requireAttachment(e,t,n));if(i.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>k)throw new Error("Inline attachments must be 41943040 bytes or less in total.");for(const e of s)"upload-ref"===e.source.type&&this.consumeAttachment(e.id);return i}requireAttachment(e,t,n){const s=this.items.get(e.id);if(!s)throw new Error(`AI session attachment not found or expired: ${e.id}`);if(e.kind&&e.kind!==s.kind)throw new Error(`AI session attachment kind mismatch: ${e.id}`);if(s.instanceId!==t||s.sessionId!==n)throw new Error(`AI session attachment scope mismatch: ${e.id}`);return U.parse({id:s.id,kind:s.kind,name:s.name,mime:s.mime,size:s.size,source:s.source})}consumeAttachment(e){const t=this.items.get(e);if(t){this.items.delete(e);try{n.unlinkSync(t.path)}catch{}}}publicAttachment(e){return{id:e.id,kind:e.kind,name:e.name,mime:e.mime,size:e.size,expiresAt:e.expiresAt}}pruneExpired(){const e=Date.now();for(const[t,s]of this.items)if(!(Date.parse(s.expiresAt)>e)){this.items.delete(t);try{n.unlinkSync(s.path)}catch{}}}}class wc{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 s=t.setTimeoutFn||setTimeout;return this.timer=s(()=>{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 Sc{transport;constructor(e){this.transport=e}attachMain(e,t){this.transport.attach(e,t),t.send(JSON.stringify({type:"control-plane.hello",protocolVersion:"2026-08-01",nodeId:e,serverTime:(new Date).toISOString(),capabilities:{reverseTunnel:"request-response",auxiliaryChannels:!0,httpResponseStreaming:!0,lifecycleCommands:!0,instanceApiProxy:!0}})),t.on("message",n=>this.handleMainMessage(e,t,n))}attachAuxiliary(e,t,n){return this.transport.attachAuxiliary(e,t,n)}handleMainMessage(e,t,n){if(!this.transport.isCurrentSocket(e,t))return;let s;try{s=JSON.parse(String(n))}catch{return void t.send(JSON.stringify({type:"control-plane.error",code:"INVALID_JSON"}))}const i=s&&"object"==typeof s?s:{},r=this.transport.handleSocketMessage(e,t,i);void 0===r||r||("node-agent.ping"!==i.type?"node-agent.identify"!==i.type?t.send(JSON.stringify({type:"control-plane.error",code:"UNSUPPORTED_MESSAGE"})):t.send(JSON.stringify({type:"control-plane.identified",nodeId:e,serverTime:(new Date).toISOString()})):t.send(JSON.stringify({type:"control-plane.pong",nodeId:e,serverTime:(new Date).toISOString()})))}}const Ac=a.z.object({nodeId:a.z.string().trim().min(1).max(120)}).strict(),kc=new Set([C,x,T,z,Ni,Ei,_i]),Cc={[C]:Te,[x]:ze,[T]:Ne,[z]:Ee,[Ni]:qi,[Ei]:Li,[_i]:Bi};function xc(){return`req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}const Tc=new Map;function zc(e,t){if("paired-hmac"!==e.auth.mode||!e.auth.keyId||!e.auth.secret){const e=new Error("Reverse tunnel requires paired-HMAC node auth.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_TUNNEL_REQUIRES_PAIRED_HMAC"}),e}const n=function(e){const t=new Map;for(const[n,s]of Object.entries(e)){const e=Array.isArray(s)?s.join(","):"string"==typeof s?s:void 0===s?"":String(s);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")||""}}(t.headers);if(n.nodeId!==e.id)throw Object.assign(new Error("Invalid reverse tunnel node id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NODE_MISMATCH"});if(n.keyId!==e.auth.keyId)throw Object.assign(new Error("Invalid reverse tunnel key id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_KEY_INVALID"});const s=Date.parse(n.timestamp);if(!Number.isFinite(s)||Math.abs(Date.now()-s)>cr)throw Object.assign(new Error("Reverse tunnel timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_TUNNEL_TIMESTAMP_INVALID"});const i=Date.now()-cr;for(const[e,t]of Tc)t<=i&&Tc.delete(e);const r=`${e.id}:${n.keyId}:${n.nonce}`;if(!n.nonce||Tc.has(r))throw Object.assign(new Error("Reverse tunnel nonce is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NONCE_INVALID"});const a=ur("");if(n.bodySha256!==a)throw Object.assign(new Error("Reverse tunnel body hash is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_BODY_HASH_INVALID"});const d=t.url||`${lr}?nodeId=${encodeURIComponent(e.id)}`,c=gr(e.auth.secret,{keyId:n.keyId,method:"GET",pathWithQuery:d,timestamp:n.timestamp,nonce:n.nonce,bodySha256:a});if(!function(e,t){const n=Buffer.from(e,"hex"),s=Buffer.from(t,"hex");return n.length===s.length&&o.timingSafeEqual(n,s)}(n.signature,c))throw Object.assign(new Error("Reverse tunnel signature is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_SIGNATURE_INVALID"});Tc.set(r,Date.now()+cr)}function Nc(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}class Ec{sockets=new Map;events;onStreamsHello;onSessionEvent;validateInstanceScope;validatedInstanceScopes=new Map;validatingInstanceScopes=new Map;instanceEventQueues=new Map;httpStreamHeaderTimeoutMs;requestTimeoutMs;auxiliaryAttachTimeoutMs;constructor(e,t={}){this.events=e,this.onStreamsHello=t.onStreamsHello,this.onSessionEvent=t.onSessionEvent,this.validateInstanceScope=t.validateInstanceScope,this.httpStreamHeaderTimeoutMs=t.httpStreamHeaderTimeoutMs??3e4,this.requestTimeoutMs=t.requestTimeoutMs??3e4,this.auxiliaryAttachTimeoutMs=t.auxiliaryAttachTimeoutMs??3e4}attach(e,t){const n=this.sockets.get(e);if(n?.socket===t)return;n&&this.invalidateInstanceScope({nodeId:e});const s=new Map,i={socket:t,pending:s,streams:new Map,httpStreams:new Map,pendingChannels:new Map};this.sockets.set(e,i),t.on?.("close",()=>this.detach(e,t)),t.on?.("error",()=>this.detach(e,t)),n&&n.socket!==t&&this.finalizeConnection(n,new Error(`Reverse tunnel for node ${e} was replaced.`),"Reverse tunnel was replaced.",!0)}detach(e,t){const n=this.sockets.get(e);!n||t&&n.socket!==t||(this.sockets.delete(e),this.invalidateInstanceScope({nodeId:e}),this.finalizeConnection(n,new Error(`Reverse tunnel for node ${e} disconnected.`),"Reverse tunnel disconnected.",!0))}disconnect(e,t="Node connection disabled locally."){const n=this.sockets.get(e);return!!n&&(this.detach(e,n.socket),n.socket.close?.(1e3,t),!0)}connected(e){return this.sockets.has(e)}finalizeConnection(e,t,n,s){for(const n of[...e.pending.keys()])this.cancelPendingRequest(e,n,t,!1);for(const[t,s]of e.streams)e.streams.delete(t),s.downstream.close(1011,n);for(const s of[...e.httpStreams.keys()])this.cancelHttpStream(e,s,t,n,!1);for(const t of e.pendingChannels.values())clearTimeout(t.timer);if(e.pendingChannels.clear(),s&&e.socket.close&&3!==e.socket.readyState)try{e.socket.close(1e3,n)}catch{}}isCurrentSocket(e,t){return this.sockets.get(e)?.socket===t}handleSocketMessage(e,t,n){if(this.isCurrentSocket(e,t))return this.handleMessage(e,n)}handleMessage(e,t){if(this.handleNodeAgentEvent(e,t))return!0;if(this.handleWebSocketMessage(e,t))return!0;if("node-agent.response"!==t.type)return!1;const n="string"==typeof t.requestId?t.requestId:"",s=this.sockets.get(e),i=s?.pending.get(n);if(!i||!s)return!0;s.pending.delete(n),clearTimeout(i.timer),i.signal&&i.onAbort&&i.signal.removeEventListener("abort",i.onAbort);const r=Number(t.status)||502,a=t.headers&&"object"==typeof t.headers?t.headers:{},o="string"==typeof t.body?t.body:"";if(t.error&&"object"==typeof t.error){const e=t.error,n=new Error("string"==typeof e.message?e.message:`Reverse tunnel request failed with HTTP ${r}`);return Object.assign(n,{statusCode:r,code:"string"==typeof e.code?e.code:"NODE_AGENT_REVERSE_REQUEST_FAILED"}),i.reject(n),!0}return i.resolve(new Response(o,{status:r,headers:a})),!0}handleNodeAgentEvent(e,t){const n="string"==typeof t.type?t.type:"";if("node-agent.streams.hello"===n){const e="string"==typeof t.instanceId?t.instanceId:"",n=Qs.safeParse(t.payload);return e&&n.success&&this.onStreamsHello?.(e,n.data),!0}if(!n.startsWith("node-agent.event."))return!1;const s=t.event&&"object"==typeof t.event&&!Array.isArray(t.event)?t.event:void 0,i="string"==typeof s?.type?s.type:"";if(!i)return!0;const r="payload"in s?s.payload:{},a=s.scope&&"object"==typeof s.scope&&!Array.isArray(s.scope)?s.scope:{},o="string"==typeof t.instanceId?t.instanceId:void 0;if(i===vs){const t=bs.safeParse(r),n="string"==typeof a.instanceId?a.instanceId:"string"==typeof s.instanceId?s.instanceId:o;return!t.success||t.data.instanceId!==n||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(vs,t.data,{topic:"instances",scope:{...a,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===Ds){const t=Ms.safeParse(r),n="string"==typeof a.instanceId?a.instanceId:"string"==typeof s.instanceId?s.instanceId:o;return!t.success||t.data.instanceId!==n||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(Ds,t.data,{topic:"instances",scope:{...a,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===ds||i===cs){const t=i===ds?ps.safeParse(r):us.safeParse(r),n="string"==typeof a.instanceId?a.instanceId:"string"==typeof s.instanceId?s.instanceId:o;return!t.success||t.data.instanceId!==n||(this.enqueueValidatedInstanceEvent(e,t.data.instanceId,()=>{this.events?.publish(i,t.data,{topic:"instances",scope:{...a,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(kc.has(i)){const t=function(e,t){const n=Cc[e];if(!n)return;const s=n.safeParse(t);if(!s.success)return;const i=s.data;return{instanceId:"meta"in i?i.meta.instanceId:i.instanceId,payload:i}}(i,r);if(!t)return!0;if([a.instanceId,s.instanceId,o].filter(e=>"string"==typeof e&&Boolean(e)).some(e=>e!==t.instanceId))return!0;const n=t.instanceId;return this.enqueueValidatedInstanceEvent(e,n,()=>{const r={...a,nodeId:e,instanceId:n},o={v:1,id:"string"==typeof s.id?s.id:`forwarded_${Date.now().toString(36)}`,seq:"number"==typeof s.seq?s.seq:0,type:i,topic:"string"==typeof s.topic?s.topic:i.startsWith("app-session.")?"app.sessions":"ai.sessions",createdAt:"string"==typeof s.createdAt?s.createdAt:(new Date).toISOString(),payload:t.payload,scope:r};this.onSessionEvent&&!this.onSessionEvent(o)||this.events?.publish(i,t.payload,{topic:o.topic,scope:r})}),!0}return this.events?.publish(i,r,{topic:"string"==typeof s.topic?s.topic:void 0,scope:{...a,nodeId:e,instanceId:"string"==typeof a.instanceId?a.instanceId:"string"==typeof s.instanceId?s.instanceId:o}}),!0}enqueueValidatedInstanceEvent(e,t,n){const s=`${e}:${t}`,i=(this.instanceEventQueues.get(s)||Promise.resolve()).then(async()=>{await this.isValidatedInstanceScope(e,t)&&n()});this.instanceEventQueues.set(s,i),i.finally(()=>{this.instanceEventQueues.get(s)===i&&this.instanceEventQueues.delete(s)})}async isValidatedInstanceScope(e,t){const n=`${e}:${t}`,s=Date.now();if((this.validatedInstanceScopes.get(n)?.expiresAt||0)>s)return!0;const i=this.validatingInstanceScopes.get(n);if(i)return i;const r=(async()=>{let s=!1;try{s=Boolean(await(this.validateInstanceScope?.(e,t)))}catch{s=!1}return s&&this.validatedInstanceScopes.set(n,{nodeId:e,instanceId:t,expiresAt:Date.now()+3e4}),s})();return this.validatingInstanceScopes.set(n,r),r.finally(()=>{this.validatingInstanceScopes.get(n)===r&&this.validatingInstanceScopes.delete(n)})}invalidateInstanceScope(e={}){for(const[t,n]of this.validatedInstanceScopes)e.nodeId&&n.nodeId!==e.nodeId||e.instanceId&&n.instanceId!==e.instanceId||this.validatedInstanceScopes.delete(t)}async request(e,t,n={}){const s=this.sockets.get(e.id);if(!s){const t=new Error(`Reverse tunnel for node ${e.id} is not connected.`);throw Object.assign(t,{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"}),t}const i=xc(),r=jt(n.body),a={type:"control-plane.request",requestId:i,route:t,init:{method:n.method||"GET",headers:Nc(n.headers),body:r}};return new Promise((r,o)=>{let d;if(d={resolve:r,reject:o,timer:setTimeout(()=>{const n=new Error(`Reverse tunnel request to ${e.id}${t} timed out.`);Object.assign(n,{statusCode:504,code:"NODE_AGENT_REVERSE_REQUEST_TIMEOUT"}),this.cancelPendingRequest(s,i,n)},this.requestTimeoutMs),signal:n.signal||void 0},n.signal&&(d.onAbort=()=>{this.cancelPendingRequest(s,i,Object.assign(new Error("Reverse tunnel request was aborted."),{name:"AbortError",code:"ABORT_ERR"}))},n.signal.addEventListener("abort",d.onAbort,{once:!0})),s.pending.set(i,d),n.signal?.aborted)d.onAbort?.();else try{s.socket.send(JSON.stringify(a))}catch(e){this.cancelPendingRequest(s,i,e instanceof Error?e:new Error(String(e)),!1)}})}cancelPendingRequest(e,t,n,s=!0){const i=e.pending.get(t);if(!i)return!1;if(e.pending.delete(t),clearTimeout(i.timer),i.signal&&i.onAbort&&i.signal.removeEventListener("abort",i.onAbort),i.reject(n),s)try{e.socket.send(JSON.stringify({type:"control-plane.request.cancel",requestId:t}))}catch{}return!0}async requestStream(e,t,n={}){const s=this.sockets.get(e.id);if(!s)throw Object.assign(new Error(`Reverse tunnel for node ${e.id} is not connected.`),{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"});const i=xc(),r=jt(n.body);return new Promise((a,o)=>{const d={resolve:a,reject:o,timer:setTimeout(()=>{this.cancelHttpStream(s,i,Object.assign(new Error(`Reverse tunnel stream to ${e.id}${t} timed out.`),{statusCode:504,code:"NODE_AGENT_REVERSE_STREAM_TIMEOUT"}),"Reverse HTTP stream header timeout.")},this.httpStreamHeaderTimeoutMs),settled:!1,finalized:!1,signal:n.signal||void 0};if(s.httpStreams.set(i,d),this.registerPendingChannel(s,{id:i,attach:t=>this.attachHttpStreamChannel(e.id,i,t),cancel:(e,t)=>{this.cancelHttpStream(s,i,e,t)}},"Reverse HTTP channel attach timed out."),n.signal&&(d.onAbort=()=>this.cancelHttpStream(s,i,Object.assign(new Error("Reverse HTTP stream consumer disconnected."),{name:"AbortError",code:"ABORT_ERR"}),"HTTP consumer disconnected."),n.signal.addEventListener("abort",d.onAbort,{once:!0}),n.signal.aborted))d.onAbort();else try{s.socket.send(JSON.stringify({type:"control-plane.http.open",streamId:i,route:t,init:{method:n.method||"GET",headers:Nc(n.headers),body:r}}))}catch(e){this.cancelHttpStream(s,i,e instanceof Error?e:new Error(String(e)),"Reverse tunnel send failed.",!1)}})}cancelHttpStream(e,t,n,s,i=!0){const r=e.httpStreams.get(t);if(!r||r.finalized)return!1;if(r.finalized=!0,e.httpStreams.delete(t),this.removePendingChannel(e,t),clearTimeout(r.timer),r.signal&&r.onAbort&&r.signal.removeEventListener("abort",r.onAbort),r.settled?r.body?.destroy(n):(r.settled=!0,r.reject(n)),1===r.socket?.readyState&&r.socket.close(1e3,s),i)try{e.socket.send(JSON.stringify({type:"control-plane.http.cancel",streamId:t,reason:s}))}catch{}return!0}completeHttpStream(e,t){const n=e.httpStreams.get(t);return!(!n||n.finalized||(n.finalized=!0,e.httpStreams.delete(t),this.removePendingChannel(e,t),clearTimeout(n.timer),n.signal&&n.onAbort&&n.signal.removeEventListener("abort",n.onAbort),n.body?.end(),0))}attachAuxiliary(e,t,n){const s=this.sockets.get(e),i=s?.pendingChannels.get(t);return s&&i?(this.removePendingChannel(s,t),i.attach(n)):(n.close(1008,"Unknown reverse tunnel channel."),!1)}registerPendingChannel(e,t,n){this.removePendingChannel(e,t.id),t.timer=setTimeout(()=>{e.pendingChannels.get(t.id)===t&&(e.pendingChannels.delete(t.id),t.cancel(Object.assign(new Error(n),{code:"NODE_AGENT_REVERSE_CHANNEL_ATTACH_TIMEOUT"}),n))},this.auxiliaryAttachTimeoutMs),t.timer.unref?.(),e.pendingChannels.set(t.id,t)}removePendingChannel(e,t){const n=e.pendingChannels.get(t);return n?.timer&&clearTimeout(n.timer),e.pendingChannels.delete(t),n}attachHttpStream(e,t,n){return this.attachAuxiliary(e,t,n)}attachHttpStreamChannel(e,t,n){const s=this.sockets.get(e),i=s?.httpStreams.get(t);if(!s||!i)return n.close(1008,"Unknown reverse HTTP stream."),!1;i.socket=n;const r=e=>{this.cancelHttpStream(s,t,e,"Reverse HTTP stream failed.")};return n.on("message",(e,a)=>{if(i.finalized||s.httpStreams.get(t)!==i)return;if(a)return i.body?void(i.body.write(Buffer.isBuffer(e)?e:Buffer.from(e))||(n.pause?.(),i.body.once("drain",()=>n.resume?.()))):r(new Error("Reverse HTTP stream received data before response headers."));let o;try{o=JSON.parse(String(e))}catch{return void r(new Error("Reverse HTTP stream sent invalid JSON control data."))}if("node-agent.http.head"===o.type){if(i.settled)return;clearTimeout(i.timer);const e=Number(o.status)||502,n=o.headers&&"object"==typeof o.headers?o.headers:{},r=new g.PassThrough({highWaterMark:65536});return i.body=r,i.settled=!0,r.once("close",()=>{this.cancelHttpStream(s,t,new Error("Reverse HTTP stream consumer closed."),"HTTP consumer closed.")}),void i.resolve(new Response(g.Readable.toWeb(r),{status:e,headers:n}))}"node-agent.http.end"!==o.type?"node-agent.http.error"===o.type&&r(new Error("string"==typeof o.message?o.message:"Reverse HTTP stream failed.")):i.settled?this.completeHttpStream(s,t):r(new Error("Reverse HTTP stream ended before response headers."))}),n.on("close",()=>{r(new Error("Reverse HTTP stream disconnected."))}),n.on("error",()=>r(new Error("Reverse HTTP stream failed."))),!0}proxyWebSocket(e,t,n,s,i={}){const r=e.id,a=this.sockets.get(r);if(!a)return void t.close(1011,"Reverse tunnel is not connected.");const o=xc(),d=e=>{try{a.socket.send(JSON.stringify(e))}catch{this.removePendingChannel(a,o),a.streams.delete(o),t.close(1011,"Reverse tunnel send failed.")}},c={downstream:t,pendingFrames:[]};a.streams.set(o,c),this.registerPendingChannel(a,{id:o,attach:e=>this.attachWebSocketStreamChannel(r,o,e),cancel:(e,n)=>{this.removePendingChannel(a,o),a.streams.delete(o),t.close(1011,n)}},"Reverse websocket channel attach timed out."),d({type:"control-plane.websocket.open",streamId:o,route:n,protocols:Array.isArray(s)?s:s?[s]:[],headers:i}),t.on("message",(e,n)=>{const s=a.streams.get(o);if(s&&!s.upstream)return s.pendingFrames.length>=256?(this.removePendingChannel(a,o),a.streams.delete(o),t.close(1011,"Reverse websocket stream pending frame limit exceeded."),void d({type:"control-plane.websocket.close",streamId:o,code:1011,reason:"Reverse websocket stream pending frame limit exceeded."})):void s.pendingFrames.push({data:e,isBinary:Boolean(n)})}),t.on("close",(e,t)=>{const n=a.streams.get(o);if(n){this.removePendingChannel(a,o),a.streams.delete(o);const s=ir(e),i=sr(t);n.upstream&&rr(n.upstream,s,i),d({type:"control-plane.websocket.close",streamId:o,...void 0===s?{}:{code:s},...i?{reason:i}:{}})}}),t.on("error",()=>{const e=a.streams.get(o);e&&(this.removePendingChannel(a,o),a.streams.delete(o),e.upstream?.close(1011,"Downstream websocket failed."),d({type:"control-plane.websocket.close",streamId:o,code:1011,reason:"Downstream websocket failed."}))})}attachWebSocketStream(e,t,n){return this.attachAuxiliary(e,t,n)}attachWebSocketStreamChannel(e,t,n){const s=this.sockets.get(e),i=s?.streams.get(t);if(!s||!i)return n.close(1008,"Unknown reverse websocket stream."),!1;i.upstream=n;for(const e of i.pendingFrames.splice(0))n.send(e.data,{binary:e.isBinary});return or(i.downstream,n,{pendingFrameLimit:256,onClientClose:()=>{this.removePendingChannel(s,t),s.streams.delete(t)},onClientError:()=>{this.removePendingChannel(s,t),s.streams.delete(t)},onUpstreamClose:()=>{this.removePendingChannel(s,t),s.streams.delete(t)},onUpstreamError:()=>{this.removePendingChannel(s,t),s.streams.delete(t)}}),!0}handleWebSocketMessage(e,t){const n="string"==typeof t.type?t.type:"";if(!n.startsWith("node-agent.websocket."))return!1;const s=this.sockets.get(e),i="string"==typeof t.streamId?t.streamId:"",r=s?.streams.get(i);return!r||("node-agent.websocket.close"===n?(s&&this.removePendingChannel(s,i),s?.streams.delete(i),rr(r.downstream,t.code,t.reason),!0):"node-agent.websocket.error"!==n||(s&&this.removePendingChannel(s,i),s?.streams.delete(i),r.downstream.close(1011,"string"==typeof t.message?t.message:"Reverse websocket proxy failed."),!0))}}class _c{sockets=new Map;socketUrls=new Map;retries=new Map;service;tunnel;safetyIntervalMs;logger;reconnectAttempts=0;safetyReconciliations=0;timer;constructor(e,t,n={}){var s;this.service=e,this.tunnel=t,this.safetyIntervalMs=(s=n.safetyIntervalMs,Math.min(6e4,Math.max(3e4,s??45e3))),this.logger=n.logger}start(){this.sync(),this.timer=setInterval(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(clearInterval(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()}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;for(const t of this.service.listNodes()){const n=this.nodeEventsUrl(t);if(!n)continue;e.add(t.id);const s=this.retries.get(t.id);s?.timer.pending&&s.url!==n&&(s.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,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=mr(t),s=n?void 0:new URL(t),i=pr(e,{method:"GET",pathWithQuery:s?`${s.pathname}${s.search}`:"/api/node-agent/events"}),r=n?yr(n,"/events",void 0,i):new l.WebSocket(t,{headers:i});this.sockets.set(e.id,r),this.socketUrls.set(e.id,t),r.on("open",()=>{const n=this.retries.get(e.id);n?.timer.reset(),this.retries.set(e.id,{timer:n?.timer||new wc,url:t})}),r.on("message",t=>{if(this.sockets.get(e.id)!==r)return;let n;try{n=JSON.parse(String(t))}catch{return}if(n&&"object"==typeof n&&!Array.isArray(n)){const t=n,s="string"==typeof t.type&&t.type.startsWith("node-agent.")?t:{type:"node-agent.event.forwarded",event:t};this.tunnel.handleMessage(e.id,s)}}),r.on("close",()=>{this.sockets.get(e.id)===r&&(this.sockets.delete(e.id),this.socketUrls.delete(e.id),this.scheduleReconnect(e.id))}),r.on("error",()=>{r.close()})}scheduleReconnect(e){const t=this.service.listNodes().find(t=>t.id===e),n=t&&this.nodeEventsUrl(t);if(!t||!n)return;const s=this.retries.get(e)??{timer:new wc};if(s.timer.pending)return;s.url=n;const i=s.timer.schedule(()=>{this.sockets.has(e)||this.connect(t,n)});i&&(this.reconnectAttempts+=1,this.logger?.info?.({nodeId:e,url:n,attempt:i.attempt,delay:i.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.node-connection.reconnect-scheduled")),this.retries.set(e,s)}nodeEventsUrl(e){if(!1===e.connectionEnabled)return;const t=e.controlEndpoint||e.endpoint;if(t&&"reverse-wss"!==e.connectionMode){if(mr(t))return t;try{const e=new URL("/api/node-agent/events",t.replace(/\/$/,""));return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}catch{return}}}}function Pc(e=function(){return process.env.TASK_HANDOFF_CONTROL_PLANE_DATA_DIR||t.join(y.homedir(),".config","task-handoff","control-plane")}()){const n=t.resolve(e);return{dataDir:n,settingsPath:t.join(n,"control-plane-settings.json"),projectsDir:t.join(n,"projects"),modelsDir:t.join(n,"models"),imagesDir:t.join(n,"images"),marketDir:t.join(n,"market"),marketCachePath:t.join(n,"market","catalog-cache.json"),marketStatePath:t.join(n,"market","catalog-state.json"),marketMigrationBackupPath:t.join(n,"market","legacy-image-migration-backup.json"),nodesDir:t.join(n,"nodes"),nodeRuntimesDir:t.join(n,"node-runtimes"),controlledInstancesDir:t.join(n,"controlled-instances"),chatSessionsDir:t.join(n,"chat-sessions"),chatBridgesDir:t.join(n,"chat-bridges"),triggersDir:t.join(n,"triggers"),nodeJoinInvitesDir:t.join(n,"node-join-invites"),proxyAuthorityPath:t.join(n,"control-plane-proxy","authority.json"),authUsersDir:t.join(n,"auth-users"),authSessionsDir:t.join(n,"auth-sessions"),aiSessionUnreadDir:t.join(n,"ai-session-unread"),logsDir:t.join(n,"logs")}}class Oc extends Error{code;lockPath;owner;constructor(e,t,n={}){const s=t?.pid?` pid ${t.pid}`:"";super(`${n.label||"Process"} is already running${s}. Lock: ${e}`),this.name="ProcessSingletonError",this.code=n.code||"PROCESS_ALREADY_RUNNING",this.lockPath=e,this.owner=t}}function Rc(e){return t.join(e,"owner.json")}function jc(e){return t.join(e,"initializing.json")}function Dc(e){return t.join(e,"recovering.json")}function Mc(e){return Number.isInteger(e)&&Number(e)>0}function $c(e,t=process.platform){if(Mc(e)){if("linux"===t)try{const t=n.readFileSync(`/proc/${e}/stat`,"utf8"),s=t.lastIndexOf(")");if(s<0)return;const i=t.slice(s+2).trim().split(/\s+/)[19];return i?`linux:${i}`:void 0}catch{return}if(["darwin","freebsd","openbsd","aix","sunos"].includes(t)){const n=I.spawnSync("ps",["-o","lstart=","-p",String(e)],{encoding:"utf8",timeout:1e3}),s=0===n.status?n.stdout.trim().replace(/\s+/g," "):"";return s?`${t}:${s}`:void 0}if("win32"===t){const t=`(Get-Process -Id ${e} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,n=I.spawnSync("powershell.exe",["-NoProfile","-NonInteractive","-Command",t],{encoding:"utf8",timeout:2e3}),s=0===n.status?n.stdout.trim():"";return/^\d+$/.test(s)?`win32:${s}`:void 0}}}const qc=function(e){try{const t=JSON.parse(n.readFileSync(Rc(e),"utf8"));if(Mc(t.pid)&&"string"==typeof t.startTime){const e=t;return{pid:e.pid,hostname:"string"==typeof e.hostname&&e.hostname?e.hostname:y.hostname(),component:e.component||"server-update",command:"string"==typeof e.command?e.command:"",acquiredAt:"string"==typeof e.createdAt&&Number.isFinite(Date.parse(e.createdAt))?e.createdAt:new Date(0).toISOString(),token:"string"==typeof e.token&&e.token?e.token:`legacy:${e.pid}:${e.startTime}`,startIdentity:e.startTime.startsWith("linux:")?e.startTime:`linux:${e.startTime}`}}if(!(Mc(t.pid)&&"string"==typeof t.hostname&&t.hostname&&"string"==typeof t.command&&"string"==typeof t.acquiredAt&&Number.isFinite(Date.parse(t.acquiredAt))&&"string"==typeof t.token&&t.token))return;return t}catch{return}};function Lc(e){if(!Mc(e))return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function Bc(e){if(!e||!Lc(e.pid))return!1;if(!e.startIdentity)return!0;const t=$c(e.pid);return void 0===t||t===e.startIdentity}function Uc(e,s){const i=t.join(e,`.owner-${s.token}.tmp`);n.writeFileSync(i,`${JSON.stringify(s,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const r=Fc(e);if(r?.token!==s.token)throw Wc(i),new Error(`Process lock initialization was superseded: ${e}`);n.renameSync(i,Rc(e)),Wc(jc(e))}function Fc(e){try{return JSON.parse(n.readFileSync(jc(e),"utf8"))}catch{return}}function Hc(e){const t=Fc(e);return Mc(t?.pid)&&Bc({pid:t.pid,startIdentity:t.startIdentity})}function Wc(e){try{n.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function Gc(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function Vc(e){try{const t=Fc(e),s=Date.parse(t?.startedAt||"");return Number.isFinite(s)?Math.max(0,Date.now()-s):Math.max(0,Date.now()-n.statSync(e).mtimeMs)}catch{return 0}}function Kc(e,s){try{return n.writeFileSync(Dc(e),`${JSON.stringify({token:s,pid:process.pid,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(i){if("EEXIST"===i.code)return!!function(e){const s=Dc(e),i=Jc(s),r=Date.parse(i?.startedAt||"");if(Lc(i?.pid)||Number.isFinite(r)&&Date.now()-r<2e3)return!1;const a=t.join(e,`.recovering-${o.randomUUID()}.stale`);try{n.renameSync(s,a)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const d=Jc(a);if(d?.token===i?.token)return Wc(a),!0;try{n.linkSync(a,s)}catch(e){if("EEXIST"!==e.code)throw e}finally{Wc(a)}return!1}(e)&&Kc(e,s);if("ENOENT"===i.code)return!1;throw i}}function Jc(e){try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return}}function Qc(e,t){return Jc(Dc(e))?.token===t}function Yc(e,t,n){if(function(e,t,n){return"system"===e.type||("chat-bridge"===e.type?function(e,t){return"ai-session"===t.type?["read","send-message","approve","interrupt"].includes(e):"instance"===t.type?["read","start","stop","restart"].includes(e):"read"===e}(t,n):"admin"===e.role||("viewer"===e.role?"read"===t:"operator"===e.role&&function(e,t){return"read"===e||("instance"===t.type?["create","update","delete","start","stop","restart","proxy"].includes(e):"ai-session"===t.type?["send-message","approve","interrupt"].includes(e):("trigger"===t.type||"runtime"===t.type)&&["create","update","delete"].includes(e))}(t,n)))}(e,t,n))return;const s=new Error(`Actor is not allowed to ${t} ${n.type}${n.id?` ${n.id}`:""}.`);throw Object.assign(s,{statusCode:403,code:"CONTROL_PLANE_FORBIDDEN"}),s}const Xc=a.z.string().trim().min(1),Zc=a.z.object({id:Xc}).strict(),el=a.z.object({id:Xc,keyId:Xc}).strict(),tl=a.z.object({id:Xc,connectionId:Xc}).strict(),nl=a.z.object({id:Xc,runtimeId:Xc}).strict(),sl=a.z.object({id:Xc,folderId:Xc}).strict(),il=a.z.object({id:Xc,sessionId:Xc}).strict(),rl=a.z.object({id:Xc,sessionId:Xc,queueId:Xc}).strict(),al=a.z.object({configHash:Xc}).strict(),ol=a.z.object({id:Xc,configHash:Xc}).strict(),dl=a.z.object({id:Xc,sessionId:Xc,configHash:Xc}).strict(),cl=a.z.object({path:a.z.string().optional(),depth:a.z.coerce.number().int().min(0).max(32).optional()}).strict();async function ll(e,t,n){const s=new AbortController,i=()=>s.abort(),r=()=>{t.raw.writableEnded||i()};e.raw.once("aborted",i),t.raw.once("close",r);try{return await n(s.signal)}finally{e.raw.off("aborted",i),t.raw.off("close",r)}}const pl=a.z.object({ids:a.z.array(a.z.string().trim().min(1).max(120)).default([])}).strict(),ul=a.z.object({nodeId:a.z.string().trim().min(1).max(120),modelId:a.z.string().trim().min(1).max(120).optional()}).strict(),gl=a.z.object({source:a.z.object({channel:Bs,bridgeId:a.z.string().trim().min(1).max(120).optional(),chatSessionId:a.z.string().trim().min(1).max(240),userId:a.z.string().trim().max(240).optional()}).strict(),action:a.z.discriminatedUnion("type",[a.z.object({type:a.z.literal("ai-session"),index:a.z.number().int().nonnegative()}).strict(),a.z.object({type:a.z.literal("instance-app-menu"),instanceId:a.z.string().trim().min(1).max(120)}).strict(),a.z.object({type:a.z.literal("launch-app"),instanceId:a.z.string().trim().min(1).max(120),appId:a.z.string().trim().min(1).max(120)}).strict(),a.z.object({type:a.z.literal("pending-decision"),routeId:a.z.string().trim().min(1).max(120),decision:a.z.enum(["allow","deny","skip"])}).strict()])}).strict();function ml(e){const t=gl.parse(e);if(!t.action)throw new Error("Chat gateway action is required.");return{source:t.source,action:t.action}}const hl=a.z.object({path:a.z.string().trim().max(1e3).optional(),depth:a.z.coerce.number().int().min(0).max(2).optional()}).strict(),yl=a.z.object({id:a.z.string().trim().min(1),appId:a.z.string().trim().min(1)}).strict(),fl=a.z.object({id:a.z.string().trim().min(1),jobId:a.z.string().trim().min(1)}).strict(),Il=a.z.object({force:a.z.enum(["true","false"]).optional()}).strict();const bl=a.z.object({appId:a.z.string().trim().min(1).max(120).default("terminal-tty"),cwdFolderId:a.z.string().trim().min(1).max(120).optional(),options:a.z.record(a.z.string(),a.z.unknown()).default({})}).strict().default({appId:"terminal-tty",options:{}}),vl=a.z.object({title:a.z.string().trim().min(1).max(120)}).strict(),wl=a.z.object({}).strict();function Sl(e){const{app:t,service:n,events:s,appSessionAggregator:i,aiSessionAggregator:r,aiSessionUnread:o,chatGateway:d,aiSessionAttachments:c,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}=e;!function({app:e,service:t,events:n}){e.get("/api/projects",async()=>({data:t.listProjects()})),e.post("/api/projects",async(e,s)=>{const i=t.createProject(e.body);return n.publish("project.created",{projectId:i.id}),s.code(201).send({data:i})}),e.get("/api/projects/:id",async e=>({data:t.requireProject(Zc.parse(e.params).id)})),e.patch("/api/projects/:id",async e=>{const s=t.updateProject(Zc.parse(e.params).id,e.body);return n.publish("project.updated",{projectId:s.id}),{data:s}}),e.delete("/api/projects/:id",async e=>{const s=Zc.parse(e.params).id,i=t.deleteProject(s);return n.publish("project.deleted",{projectId:s,deleted:i}),{data:{deleted:i}}}),e.get("/api/models",async(e,n)=>ll(e,n,async e=>({data:await t.listFederatedModels(e)}))),e.post("/api/models",async(e,s)=>{const i=await t.createModel(e.body);return n.publish("model.created",{modelId:i.id}),s.code(201).send({data:i})}),e.post("/api/models/reorder",async e=>{const s=pl.parse(e.body),i=await t.reorderModels(s.ids);return n.publish("model.reordered",{ids:s.ids}),{data:i}}),e.get("/api/models/:id",async e=>({data:t.requireModel(Zc.parse(e.params).id)})),e.patch("/api/models/:id",async e=>{const s=await t.updateModel(Zc.parse(e.params).id,e.body);return n.publish("model.updated",{modelId:s.id}),{data:s}}),e.delete("/api/models/:id",async e=>{const s=Zc.parse(e.params).id,i=await t.deleteModel(s);return n.publish("model.deleted",{modelId:s,deleted:i}),{data:{deleted:i}}}),e.post("/api/nodes/:nodeId/models",async(e,s)=>{const{nodeId:i}=ul.parse(e.params),r=await t.createNodeModel(i,e.body);return n.publish("model.created",{modelId:r.id,nodeId:i}),s.code(201).send({data:r})}),e.patch("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:s,modelId:i}=ul.parse(e.params),r=await t.updateNodeModel(s,i,e.body);return n.publish("model.updated",{modelId:i,nodeId:s}),{data:r}}),e.delete("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:s,modelId:i}=ul.parse(e.params),r=await t.deleteNodeModel(s,i);return n.publish("model.deleted",{modelId:i,nodeId:s,deleted:r.deleted}),{data:r}}),e.get("/api/market/catalog",async()=>({data:t.getMarketCatalog()})),e.post("/api/market/refresh",async()=>{const e=await t.refreshMarketCatalog();return n.publish("market.catalog.updated",{revision:e.catalog.revision,source:e.catalog.source}),{data:e}}),e.get("/api/image-options",async()=>({data:t.listImageOptions()})),e.get("/api/images",async()=>({data:t.listImages()})),e.post("/api/images",async(e,s)=>{const i=t.createImage(e.body);return n.publish("image.created",{imageId:i.id}),s.code(201).send({data:i})}),e.get("/api/images/:id",async e=>({data:t.requireImage(Zc.parse(e.params).id)})),e.patch("/api/images/:id",async e=>{const s=t.updateImage(Zc.parse(e.params).id,e.body);return n.publish("image.updated",{imageId:s.id}),{data:s}}),e.delete("/api/images/:id",async e=>{const s=Zc.parse(e.params).id,i=await t.deleteImage(s);return n.publish("image.deleted",{imageId:s,deleted:i}),{data:{deleted:i}}})}({app:t,service:n,events:s}),function({app:e,service:t,events:n,nodeAgentTunnel:s,nodeEventSubscriber:i,errorPayload:r}){const a=e=>{if(!e||"object"!=typeof e||Array.isArray(e)||!("target"in e))return;const t=e.target;if(!t||"object"!=typeof t||Array.isArray(t)||"controlled-instance"!==t.component)return;const n=new Error("Independent controlled-instance updates were retired; update the Node rollout instead.");throw Object.assign(n,{statusCode:404,code:"LEGACY_INSTANCE_UPDATE_RETIRED"}),n};e.get("/api/nodes",async()=>({data:t.listPublicNodes()})),e.post("/api/nodes/local/sync",async()=>{const e=await t.syncLocalNodeConnection();return i.syncNow(),n.publish("node.local-synced",{nodeId:e.id}),{data:t.requirePublicNode(e.id)}}),e.post("/api/nodes",async(e,s)=>{const r=await t.createNode(e.body);return i.syncNow(),n.publish("node.created",{nodeId:r.id}),s.code(201).send({data:t.requirePublicNode(r.id)})}),e.post("/api/nodes/:id/check",async e=>{const s=await t.checkNode(Zc.parse(e.params).id);return n.publish("node.checked",{nodeId:s.id},{topic:"node.state",scope:{nodeId:s.id}}),{data:s}}),e.get("/api/nodes/:id/updates/jobs",async e=>({data:await t.listNodeUpdateJobs(Zc.parse(e.params).id)})),e.get("/api/nodes/:id/settings/external-listener",async e=>({data:await t.getLocalNodeExternalListener(Zc.parse(e.params).id)})),e.patch("/api/nodes/:id/settings/external-listener",async e=>{const s=Zc.parse(e.params).id,i=await t.updateLocalNodeExternalListener(s,e.body);return n.publish("node.external-listener.updated",{nodeId:s,bindScope:i.bindScope,port:i.port}),{data:i}}),e.post("/api/nodes/:id/updates/check",async e=>{a(e.body);const s=Zc.parse(e.params).id,i=await t.checkNodeUpdate(s,kn.parse(e.body));return n.publish("node.update.checked",{nodeId:s,availableVersion:i.availableVersion,impact:i.impact}),{data:i}}),e.post("/api/nodes/:id/updates/apply",async(e,s)=>{a(e.body);const i=Zc.parse(e.params).id,r=await t.applyNodeUpdate(i,Tn.parse(e.body));return n.publish("node.update.queued",{nodeId:i,updateJobId:r.id,desiredVersion:r.toVersion,impact:r.impact}),s.code(202).send({data:r})}),e.post("/api/nodes/:id/pairing/invites",async(e,s)=>{const i=Zc.parse(e.params),r=await t.createNodePairingInvite(i.id,e.body);return n.publish("node.pairing-invite.created",{nodeId:i.id}),s.code(201).send({data:r})}),e.post("/api/nodes/:id/control-plane-connections",async e=>{const s=Zc.parse(e.params),i=await t.connectNodeToControlPlane(s.id,e.body);return n.publish("node.remote-connect.requested",{nodeId:s.id}),{data:i}}),e.get("/api/nodes/:id/control-plane-pairings",async e=>{const n=Zc.parse(e.params);return{data:await t.listNodeControlPlanePairings(n.id)}}),e.delete("/api/nodes/:id/control-plane-pairings/:keyId",async e=>{const s=el.parse(e.params),i=await t.deleteNodeControlPlanePairing(s.id,s.keyId);return n.publish("node.control-plane-pairing.deleted",{nodeId:s.id,keyId:s.keyId}),{data:i}}),e.get("/api/nodes/:id/control-plane-connections",async e=>{const n=Zc.parse(e.params);return{data:await t.listNodeControlPlaneConnections(n.id)}}),e.delete("/api/nodes/:id/control-plane-connections/:connectionId",async e=>{const s=tl.parse(e.params),i=await t.deleteNodeControlPlaneConnection(s.id,s.connectionId);return n.publish("node.control-plane-connection.deleted",{nodeId:s.id,connectionId:s.connectionId}),{data:i}}),e.post("/api/node-join/invites",async(e,s)=>{const i=t.createNodeJoinInvite(e.body);return n.publish("node-join.invite.created",{inviteId:i.id}),s.code(201).send({data:i})}),e.post("/api/node-join/complete",async(e,s)=>{const r=t.completeNodeJoin(e.body);return i.syncNow(),n.publish("node.joined",{nodeId:r.id}),s.code(201).send({data:t.requirePublicNode(r.id)})}),e.get("/api/nodes/:id/runtimes",async e=>({data:await t.listNodeRuntimes(Zc.parse(e.params).id)})),e.post("/api/nodes/:id/runtimes",async(e,s)=>{const i=Zc.parse(e.params),r=await t.createNodeRuntime(i.id,e.body);return n.publish("node.runtime.created",{nodeId:i.id,runtimeId:r.id}),s.code(201).send({data:r})}),e.patch("/api/nodes/:id/runtimes/:runtimeId",async e=>{const s=nl.parse(e.params),i=await t.updateNodeRuntime(s.id,s.runtimeId,e.body);return n.publish("node.runtime.updated",{nodeId:s.id,runtimeId:i.id}),{data:i}}),e.delete("/api/nodes/:id/runtimes/:runtimeId",async e=>{const s=nl.parse(e.params),i=await t.deleteNodeRuntime(s.id,s.runtimeId);return n.publish("node.runtime.deleted",{nodeId:s.id,runtimeId:s.runtimeId}),{data:i}}),e.post("/api/nodes/:id/runtimes/:runtimeId/check",async e=>{const s=nl.parse(e.params),i=await t.checkNodeRuntime(s.id,s.runtimeId);return n.publish("node.runtime.checked",{nodeId:s.id,runtimeId:i.id}),{data:i}}),e.get("/api/nodes/:id/docker/images",async e=>({data:await t.listNodeDockerImages(Zc.parse(e.params).id)})),e.get("/api/nodes/:id/image-options",async e=>({data:await t.listNodeImageAvailability(Zc.parse(e.params).id)})),e.get("/api/nodes/:id/local-folders",async(e,n)=>ll(e,n,async n=>({data:await t.listNodeLocalFolders(Zc.parse(e.params).id,n)}))),e.get("/api/nodes/:id/folders/tree",async e=>{const n=Zc.parse(e.params),s=cl.parse(e.query);return{data:await t.listNodeFolderTree(n.id,{path:s.path,depth:s.depth})}}),e.post("/api/nodes/:id/local-folders",async(e,s)=>{const i=Zc.parse(e.params),r=await t.createNodeLocalFolder(i.id,e.body);return n.publish("node.local-folder.created",{nodeId:i.id,folderId:r.id}),s.code(201).send({data:r})}),e.delete("/api/nodes/:id/local-folders/:folderId",async e=>{const s=sl.parse(e.params),i=await t.deleteNodeLocalFolder(s.id,s.folderId);return n.publish("node.local-folder.deleted",{nodeId:s.id,folderId:s.folderId}),{data:i}}),e.get("/api/nodes/:id",async e=>({data:t.requirePublicNode(Zc.parse(e.params).id)})),e.patch("/api/nodes/:id",async e=>{const r=t.updateNode(Zc.parse(e.params).id,e.body);return r.connectionEnabled||s.disconnect(r.id),i.syncNow(),n.publish("node.updated",{nodeId:r.id}),{data:t.requirePublicNode(r.id)}}),e.delete("/api/nodes/:id",async e=>{const s=Zc.parse(e.params).id,r=Il.parse(e.query),a=await t.deleteNodeWithProxyLifecycle(s,"true"===r.force);return i.syncNow(),n.publish("node.deleted",{nodeId:s,deleted:a.deleted,revoke:a.revoke}),{data:a}}),e.get("/api/node-runtimes",async(e,n)=>ll(e,n,async e=>{const n=await t.listNodeRuntimesWithDiagnostics(e);return{data:n.items,meta:{nodeErrors:n.nodeErrors}}})),function(e){const{app:t,service:n,nodeAgentTunnel:s,errorPayload:i}=e,r=new Sc(s),a=e=>{if(!1===e.connectionEnabled){const e=new Error("Node agent connection is disabled locally.");throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_CONNECTION_DISABLED"}),e}};t.get(lr,{websocket:!0},(e,t)=>{try{const s=Ac.parse(t.query),i=n.requireNode(s.nodeId);zc(i,t),a(i),r.attachMain(i.id,e)}catch(t){const n=i(t);e.send(JSON.stringify({type:"control-plane.error",code:n.code,message:n.message})),e.close(1008,n.message)}}),t.get(`${lr}/streams/:streamId`,{websocket:!0},(e,t)=>{try{const s=Ac.parse(t.query),i=n.requireNode(s.nodeId);zc(i,t),a(i);const o=t.params.streamId;r.attachAuxiliary(i.id,o,e)}catch(t){const n=i(t);e.close(1008,n.message)}}),t.get(`${lr}/channels/:channelId`,{websocket:!0},(e,t)=>{try{const s=Ac.parse(t.query),i=n.requireNode(s.nodeId);zc(i,t),a(i);const o=t.params.channelId;r.attachAuxiliary(i.id,o,e)}catch(t){const n=i(t);e.close(1008,n.message)}}),t.get(`${lr}/http-streams/:streamId`,{websocket:!0},(e,t)=>{try{const s=Ac.parse(t.query),i=n.requireNode(s.nodeId);zc(i,t),a(i);const o=t.params.streamId;r.attachAuxiliary(i.id,o,e)}catch(t){const n=i(t);e.close(1008,n.message)}})}({app:e,service:t,nodeAgentTunnel:s,errorPayload:r})}({app:t,service:n,events:s,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}),function({app:e,service:t,events:n}){e.get("/api/controlled-instances",async()=>({data:await t.listControlledInstances()})),e.post("/api/controlled-instances",async(e,s)=>{const i=await t.createControlledInstance(e.body);return n.publish("instance.created",{instanceId:i.id}),s.code(201).send({data:i})}),e.get("/api/controlled-instances/:id",async e=>({data:await t.requireControlledInstance(Zc.parse(e.params).id)})),e.get("/api/controlled-instances/:id/metrics",async e=>({data:await t.instanceResourceMetrics(Zc.parse(e.params).id)})),e.get("/api/controlled-instances/:id/apps/management",async e=>({data:await t.instanceAppManagement(Zc.parse(e.params).id)})),e.post("/api/controlled-instances/:id/apps/:appId/install",async e=>{const n=yl.parse(e.params),s=hn.parse(e.body||{});return{data:await t.requestInstanceAppOperation(n.id,n.appId,"install",s)}}),e.post("/api/controlled-instances/:id/apps/:appId/uninstall",async e=>{const n=yl.parse(e.params),s=hn.parse(e.body||{});return{data:await t.requestInstanceAppOperation(n.id,n.appId,"uninstall",s)}}),e.get("/api/controlled-instances/:id/apps/jobs/:jobId",async e=>{const n=fl.parse(e.params);return{data:await t.instanceAppManagementJob(n.id,n.jobId)}}),e.patch("/api/controlled-instances/:id",async e=>{const s=await t.updateControlledInstance(Zc.parse(e.params).id,e.body);return n.publish("instance.updated",{instanceId:s.id}),{data:s}}),e.delete("/api/controlled-instances/:id",async e=>{const s=Zc.parse(e.params).id,i=await t.deleteControlledInstance(s);return n.publish("instance.deleted",{instanceId:s,deleted:i}),{data:{deleted:i}}}),e.post("/api/controlled-instances/:id/start",async e=>{const s=await t.startControlledInstance(Zc.parse(e.params).id);return n.publish("instance.started",{instanceId:s.id}),{data:s}}),e.post("/api/controlled-instances/:id/stop",async e=>{const s=await t.stopControlledInstance(Zc.parse(e.params).id);return n.publish("instance.stopped",{instanceId:s.id}),{data:s}}),e.post("/api/controlled-instances/:id/restart",async e=>{const s=await t.restartControlledInstance(Zc.parse(e.params).id);return n.publish("instance.restarted",{instanceId:s.id}),{data:s}}),e.post("/api/controlled-instances/:id/image-provisioning/retry",async e=>{const s=await t.retryControlledInstanceImageProvisioning(Zc.parse(e.params).id);return n.publish("instance.image-provisioning-retried",{instanceId:s.id}),{data:s}}),e.get("/api/controlled-instances/:id/config-sync",async e=>({data:await t.instanceConfigSyncState(Zc.parse(e.params).id)})),e.get("/api/controlled-instances/:id/config-sync/folders",async e=>({data:await t.listInstanceConfigSyncFolders(Zc.parse(e.params).id,hl.parse(e.query))})),e.post("/api/controlled-instances/:id/config-sync",async e=>{const s=Zc.parse(e.params).id,i=await t.syncInstanceConfigs(s,e.body);return n.publish("instance.config-synced",{instanceId:s}),{data:i}}),e.get("/api/instance-board",async(e,n)=>ll(e,n,async e=>{const n=await t.boardWithDiagnostics(e);return{data:n.items,meta:{nodeErrors:n.nodeErrors}}}))}({app:t,service:n,events:s}),function({app:e,service:t,events:n,appSessionAggregator:s,aiSessionAggregator:i,aiSessionUnread:r,aiSessionAttachments:o}){e.get("/api/app-sessions",async e=>{const t=a.z.object({refresh:a.z.string().optional(),includeTombstones:a.z.string().optional(),instanceId:a.z.string().trim().min(1).optional(),streamId:a.z.string().trim().min(1).optional(),sinceRevision:a.z.string().optional()}).parse(e.query||{});if(void 0!==t.sinceRevision){const e=Number(t.sinceRevision);if(!Number.isInteger(e)||e<0){const e=new Error("sinceRevision must be a non-negative integer.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}if(!t.streamId){const e=new Error("streamId is required with sinceRevision.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}return{data:await s.delta({instanceId:t.instanceId,streamId:t.streamId,sinceRevision:e})}}return{data:await s.list({refresh:"true"===t.refresh||"1"===t.refresh,includeTombstones:"true"===t.includeTombstones||"1"===t.includeTombstones})}}),e.post("/api/controlled-instances/:id/apps/sessions",async e=>{const s=Zc.parse(e.params),i=bl.parse(e.body),r=await t.launchAppSession(s.id,i.appId,{...i.options,...i.cwdFolderId?{cwdFolderId:i.cwdFolderId}:{}});return n.publish("instance.app-session.launched",{instanceId:s.id,sessionId:"string"==typeof r.id?r.id:void 0,appId:i.appId}),{data:r}}),e.post("/api/controlled-instances/:id/apps/sessions/:sessionId/stop",async e=>{const s=il.parse(e.params),i=await t.stopAppSession(s.id,s.sessionId);return n.publish("instance.app-session.stopped",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.patch("/api/controlled-instances/:id/apps/sessions/:sessionId",async e=>{const s=il.parse(e.params),i=vl.parse(e.body),r=await t.renameAppSession(s.id,s.sessionId,i.title);return n.publish("instance.app-session.renamed",{instanceId:s.id,sessionId:s.sessionId,title:i.title}),{data:r}}),e.get("/api/controlled-instances/:id/app-sessions",async e=>{const t=Zc.parse(e.params),n=(await s.list()).instances.find(e=>e.instanceId===t.id);return{data:n?.appSessions||{runningCount:0,problemCount:0,sessions:[],updatedAt:(new Date).toISOString()}}}),e.get("/api/controlled-instances/:id/ai-sessions/history",async e=>{const n=Zc.parse(e.params);return{data:await t.listAiSessionHistory(n.id)}}),e.get("/api/controlled-instances/:id/ai-sessions/history/:sessionId",async e=>{const n=il.parse(e.params);return{data:await t.getAiSessionHistoryDetail(n.id,n.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/resume",async e=>{const s=il.parse(e.params);wl.parse(e.body||{});const i=await t.resumeAiSession(s.id,s.sessionId);return n.publish("instance.ai-session.resumed",{instanceId:s.id,sessionId:i.aiSessionId,providerSessionId:i.providerSessionId,appSessionId:i.appSessionId,disposition:i.disposition}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/messages",async e=>{const s=il.parse(e.params),i=ae.parse(e.body||{}),r=o.resolveRefs(i.attachments,s.id,s.sessionId),a=await t.sendAiSessionMessage(s.id,s.sessionId,i.message,i.mode,r,i.references,i.permissionMode);return n.publish("instance.ai-session.message-sent",{instanceId:s.id,sessionId:s.sessionId}),{data:a}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions",async e=>{const n=il.parse(e.params);return{data:await t.aiSessionMentionCatalog(n.id,n.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions/files",async e=>{const n=il.parse(e.params),s=se.parse(e.body||{});return{data:await t.searchAiSessionMentionFiles(n.id,n.sessionId,s.query)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/commands",async e=>{const s=il.parse(e.params),i=K.parse(e.body||{}),r=await t.executeAiSessionCommand(s.id,s.sessionId,i);return n.publish("instance.ai-session.command-executed",{instanceId:s.id,sessionId:s.sessionId,command:i.command}),{data:r}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/queue",async e=>{const n=il.parse(e.params);return{data:await t.aiSessionQueue(n.id,n.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/steer",async e=>{const s=rl.parse(e.params),i=await t.steerAiSessionQueuedMessage(s.id,s.sessionId,s.queueId);return n.publish("instance.ai-session.message-sent",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/retry",async e=>{const n=rl.parse(e.params);return{data:await t.retryAiSessionQueuedMessage(n.id,n.sessionId,n.queueId)}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId",async e=>{const n=rl.parse(e.params);return{data:await t.removeAiSessionQueuedMessage(n.id,n.sessionId,n.queueId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/interrupt",async e=>{const s=il.parse(e.params),i=await t.interruptAiSession(s.id,s.sessionId);return n.publish("instance.ai-session.interrupted",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/approval",async e=>{const s=il.parse(e.params),i=oe.parse(e.body||{}),r=await t.resolveAiSessionApproval(s.id,s.sessionId,i.decision);return n.publish("instance.ai-session.approval-resolved",{instanceId:s.id,sessionId:s.sessionId,decision:i.decision}),{data:r}}),e.get("/api/ai-sessions",async(e,t)=>{const n=e.query;if(void 0!==n.sinceRevision){const e=Number(n.sinceRevision);if(!Number.isInteger(e)||e<0)return t.code(400).send({error:"invalid_since_revision"});try{return n.streamId?{data:await i.delta({instanceId:n.instanceId,streamId:n.streamId,sinceRevision:e})}:t.code(400).send({error:"stream_id_required"})}catch(e){if("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED"===e.message)return t.code(400).send({error:"instance_id_required"});throw e}}const s=await i.list({refresh:"true"===n.refresh||"1"===n.refresh});for(const e of s.instances)r.reconcile(e.instanceId,e.aiSessions);return{data:{...s,instances:s.instances.map(e=>({...e,aiSessions:r.decorate(e.instanceId,e.aiSessions)}))}}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/read",async(e,t)=>{const s=il.parse(e.params),o=a.z.object({sessionUpdatedAt:a.z.string().datetime()}).strict().parse(e.body||{}),d=(await i.list()).instances.find(e=>e.instanceId===s.id),c=d?.aiSessions.sessions.find(e=>e.id===s.sessionId);if(!d||!c)return t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}});r.reconcile(s.id,d.aiSessions);const l=r.markRead(s.id,s.sessionId,o.sessionUpdatedAt);return l?(n.publish(N,l,{topic:"ai.sessions",scope:{instanceId:s.id,sessionId:s.sessionId}}),{data:l}):t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}})})}({app:t,service:n,events:s,appSessionAggregator:i,aiSessionAggregator:r,aiSessionUnread:o,aiSessionAttachments:c}),function({app:e,service:t,events:n}){e.get("/api/triggers",async()=>({data:await t.listTriggers()})),e.post("/api/triggers",async(e,s)=>{const i=t.createTrigger(e.body);return n.publish("trigger.created",{configHash:i.configHash}),s.code(201).send({data:i})}),e.delete("/api/triggers/:configHash",async e=>{const s=al.parse(e.params),i=await t.deleteTrigger(s.configHash);return n.publish("trigger.deleted",{configHash:s.configHash}),{data:i}}),e.post("/api/triggers/:configHash/apply",async e=>{const s=al.parse(e.params),i=await t.applyTrigger(s.configHash,e.body||{});return n.publish("trigger.applied",{configHash:s.configHash}),{data:i}}),e.get("/api/controlled-instances/:id/triggers",async e=>{const n=Zc.parse(e.params);return{data:await t.listInstanceTriggers(n.id)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers",async e=>{const s=il.parse(e.params),i=await t.bindAiSessionTrigger(s.id,s.sessionId,e.body||{});return n.publish("instance.ai-session.trigger-bound",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers/:configHash",async e=>{const s=dl.parse(e.params),i=await t.unbindAiSessionTrigger(s.id,s.sessionId,s.configHash);return n.publish("instance.ai-session.trigger-unbound",{instanceId:s.id,sessionId:s.sessionId,configHash:s.configHash}),{data:i}}),e.post("/api/controlled-instances/:id/triggers/:configHash/run",async e=>{const s=ol.parse(e.params),i=await t.runInstanceTrigger(s.id,s.configHash,e.body||{});return n.publish("instance.trigger.run",{instanceId:s.id,configHash:s.configHash}),{data:i}})}({app:t,service:n,events:s}),function({app:e,service:t,chatGateway:n}){e.get("/api/chat/sessions",async()=>({data:t.listChatSessions()})),e.get("/api/chat/sessions/:id",async e=>({data:t.requireChatSession(Zc.parse(e.params).id)})),e.get("/api/chat-gateway/status",async()=>({data:n.status()})),e.post("/api/chat-gateway/poll-ai-sessions",async()=>({data:await n.pollAiSessionsNow()})),e.get("/api/chat-gateway/bridges",async()=>({data:t.listChatBridges()})),e.post("/api/chat-gateway/bridges",async e=>({data:t.createChatBridge(e.body)})),e.patch("/api/chat-gateway/bridges/:id",async s=>{const i=Zc.parse(s.params).id,r=t.requireChatBridge(i),a=xa.parse(s.body),o=t.updateChatBridge(i,s.body),d="boolean"==typeof a.enabled?a.enabled:void 0;return e.log.info({component:"control-plane-chat-gateway",action:"bridge-patch",bridgeId:i,hasEnabled:void 0!==d,requestedEnabled:d,previousEnabled:r.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),void 0!==d&&d!==r.enabled&&(d?n.startBridge(i):n.stopBridge(i)),{data:o}}),e.post("/api/chat-gateway/bridges/:id/start",async s=>{const i=Zc.parse(s.params).id,r=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!0}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-start-request",bridgeId:i,previousEnabled:r.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:n.startBridge(i)}}),e.post("/api/chat-gateway/bridges/:id/stop",async s=>{const i=Zc.parse(s.params).id,r=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!1}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-stop-request",bridgeId:i,previousEnabled:r.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:n.stopBridge(i)}}),e.delete("/api/chat-gateway/bridges/:id",async e=>({data:{deleted:t.deleteChatBridge(Zc.parse(e.params).id)}})),e.post("/api/chat-gateway/messages",{bodyLimit:67108864},async e=>({data:await t.handleChatGatewayMessage(Hs.parse(e.body))})),e.post("/api/chat-gateway/actions",async e=>({data:await t.handleChatGatewayAction(ml(e.body))})),e.get("/api/pending-routes",async()=>({data:await t.listPendingRoutes()}))}({app:t,service:n,chatGateway:d})}const Al=new Set(["connection","content-length","keep-alive","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),kl=new Set(["content-encoding"]);function Cl(e){return Object.fromEntries(Object.entries(e).filter(([e,t])=>!Al.has(e.toLowerCase())&&void 0!==t).map(([e,t])=>[e,Array.isArray(t)?t.join(", "):String(t)]))}function xl(e){return Object.fromEntries(Object.entries(Cl(e)).filter(([e])=>{const t=e.toLowerCase();return"host"!==t&&"cookie"!==t&&"authorization"!==t&&(!t.startsWith("sec-websocket-")||"sec-websocket-origin"===t)}))}function Tl(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}function zl(e,t,n,s,i={}){const r=new AbortController;return t.raw.once("close",()=>r.abort()),e.proxyInstanceHttp(n,s,{...i,signal:r.signal})}function Nl(e,t,n){for(const[n,s]of Object.entries(t.headers)){const t=n.toLowerCase();Al.has(t)||kl.has(t)||e.header(n,s)}if(!t.body)return e.code(t.status).send();const s=g.Readable.fromWeb(t.body);return e.raw.once("close",()=>s.destroy()),e.code(t.status).send(n?s.pipe(n):s)}function El(e){return`/instances/${encodeURIComponent(e)}`}function _l(e,t,n,s){const i=function(e,t){const n=e.split("?",1)[0]||"/";return t.toLowerCase().includes("text/html")&&!n.startsWith("/api/")}(s,Object.entries(t.headers).find(([e])=>"content-type"===e.toLowerCase())?.[1]||"")?function(e){const t=[];let n=0,s=!1;const i=(i,r=!1)=>{if(s)return;const a=Buffer.concat(t,n).toString("utf8");!/<head(\s[^>]*)?>/i.exec(a)&&!r&&n<65536||(s=!0,i.push(function(e,t){if(e.includes("__TASK_HANDOFF_PUBLIC_BASE__"))return e;const n=`<script>window.__TASK_HANDOFF_PUBLIC_BASE__=${JSON.stringify(t)};<\/script>`;return/<head(\s[^>]*)?>/i.test(e)?e.replace(/<head(\s[^>]*)?>/i,e=>`${e}\n${n}`):`${n}\n${e}`}(a,e)),t.length=0,n=0)};return new g.Transform({transform(e,r,a){s?this.push(e):(t.push(Buffer.from(e)),n+=e.length,i(this)),a()},flush(e){i(this,!0),e()}})}(El(n)):void 0;return Nl(e,t,i)}function Pl(e){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):e instanceof URLSearchParams?e.toString():"string"==typeof e?e:null==e?void 0:JSON.stringify(e)}function Ol(e,t){if("string"!=typeof t||!e.hasContentTypeParser(t))try{e.addContentTypeParser(t,{parseAs:"buffer"},(e,t,n)=>n(null,t))}catch(e){if(e instanceof Error&&/Content type parser .* already present/i.test(e.message))return;throw e}}function Rl(e){return new URL(e,"http://control-plane.local").searchParams.get("token")||""}function jl(e,t,n){const s=encodeURIComponent(e),i=encodeURIComponent(t),r=n.vnc&&"object"==typeof n.vnc?n.vnc:{};return"kasmvnc"===("string"==typeof r.backend?r.backend:"")?`/apps/access/vnc/proxy/api/apps/sessions/${i}/web/?token=${s}`:`/apps/access/vnc/proxy/api/novnc/vnc.html?token=${s}&path=${encodeURIComponent(`apps/access/vnc/proxy/api/apps/sessions/${i}/vnc?token=${s}`)}&autoconnect=1&resize=scale`}class Dl{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;listeners=new Set;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){if(e.type===z){const t=Ee.safeParse(e.payload);return!!t.success||(this.logger?.warn?.({eventType:e.type,issues:t.error.issues,errorCode:"AI_SESSION_MESSAGE_DELTA_INVALID"},"ai-session.aggregator.message-delta.invalid"),!1)}const t=e.type===C?Te:e.type===x?ze:e.type===T?Ne:void 0;if(!t)return!1;const n=t.safeParse(e.payload);return n.success?e.type===C?this.applySnapshot(n.data):e.type===x?this.applyPatch(n.data):this.applyRemoved(n.data):(this.logger?.warn?.({eventType:e.type,issues:n.error.issues,errorCode:"AI_SESSION_EVENT_INVALID"},"ai-session.aggregator.event.invalid"),!1)}onSnapshot(e){return this.listeners.add(e),()=>this.listeners.delete(e)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"ai-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:C,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(n=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:n instanceof Error?n.message:String(n)},"ai-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:x,payload:e})}applyRemoved(e){return this.apply({type:T,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view()}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"ai.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const n=this.snapshots.get(t)?.revision??0,s=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??n+1;if(e.streamId!==s||e.sinceRevision>n)return _e.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===n)return _e.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!1,events:[]});const r=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),a=!r.length||r[0].payload.meta.revision!==e.sinceRevision+1;return _e.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:a,events:a?[]:r.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:n}=e.payload,s=this.advertisedStreams.get(n.instanceId)?.streamId;if(s&&s!==n.streamId)return this.logger?.warn?.({instanceId:n.instanceId,advertisedStreamId:s,streamId:n.streamId,revision:n.revision},"ai-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(n.instanceId);if(i&&!t)return i.streamId===n.streamId&&(i.highWater=Math.max(i.highWater,n.revision)),!1;const r=this.snapshots.get(n.instanceId);e.type===C&&r&&r.streamId!==n.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:n.instanceId,previousStreamId:r.streamId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision,traceId:n.traceId,resetCount:this.counters.streamResets},"ai-session.aggregator.stream-reset"));const a=function(e,t){const{meta:n}=t.payload;if(t.type===C)return e?.streamId===n.streamId&&n.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===n.streamId&&n.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:Se.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==n.streamId)return{kind:"snapshot-required",projection:e,streamId:n.streamId};if(n.revision===e.revision)return{kind:"duplicate",projection:e};if(n.revision<e.revision)return{kind:"stale",projection:e};if(n.revision!==e.revision+1||n.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:n.revision};const s=new Map(e.snapshot.sessions.map(e=>[e.id,e]));if(t.type===x){for(const e of t.payload.upserted)s.set(e.id,e);for(const e of t.payload.removed)s.delete(e)}else for(const e of t.payload.sessionIds)s.delete(e);const i=[...s.values()],r=we.parse({runningCount:i.filter(e=>"running"===e.status).length,waitingCount:i.filter(e=>"waiting"===e.status).length,staleCount:e.snapshot.staleCount,sessions:i,updatedAt:n.generatedAt});return{kind:"applied",projection:Se.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:r})}}(r,e);if("applied"!==a.kind)return this.logger?.warn?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,currentRevision:r?.revision,revision:n.revision,outcome:a.kind},`ai-session.aggregator.event.${a.kind}`),"gap"!==a.kind&&"snapshot-required"!==a.kind||("gap"===a.kind&&(this.counters.gaps+=1),this.recoverStream(n.instanceId,n.streamId,n.revision).catch(e=>{this.logger?.warn?.({instanceId:n.instanceId,streamId:n.streamId,revision:n.revision,error:e instanceof Error?e.message:String(e)},"ai-session.aggregator.recovery.failed")})),!1;const o=a.projection;this.snapshots.set(n.instanceId,o),this.rememberEvent(n.instanceId,e);const d={instanceId:n.instanceId,streamId:o.streamId,aiSessions:o.snapshot,revision:o.revision,lastEventAt:o.lastEventAt};for(const e of this.listeners)try{e(d)}catch{this.listeners.delete(e)}return this.logger?.info?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,revision:n.revision,reason:n.reason,sessionCount:o.snapshot.sessions.length},"ai-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"ai-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),n=new Set(t.instances.map(e=>e.instanceId));for(const[t,s]of e)n.has(t)||this.snapshots.get(t)!==s||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const n of t.instances){const t=this.snapshots.get(n.instanceId);if(t!==e.get(n.instanceId))continue;const s=this.advertisedStreams.get(n.instanceId)?.streamId;s&&s!==n.streamId||t?.streamId===n.streamId&&t.revision>=n.revision||(t?.streamId!==n.streamId&&this.history.delete(n.instanceId),this.snapshots.set(n.instanceId,{streamId:n.streamId,snapshot:n.aiSessions,revision:n.revision,lastEventAt:n.lastEventAt}))}}recoverStream(e,t,n){const s=this.recoveries.get(e);if(s)return s.streamId===t?s.highWater=Math.max(s.highWater,n):(s.streamId=t,s.highWater=n),s.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:n},r=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:n,recoveryNumber:r},"ai-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const n=this.snapshots.get(e);if(n&&n.streamId===t.streamId){if(n.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,n.streamId,n.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:C,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot-fallback"},"ai-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:r,outcome:"delta"},"ai-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:C,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot"},"ai-session.aggregator.recovery.snapshot")}const s=this.snapshots.get(e),a=this.advertisedStreams.get(e);if(s&&a&&s.streamId===a.streamId&&s.revision>=Math.max(a.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:s.streamId,revision:s.revision,recoveryNumber:r,outcome:"success"},"ai-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const n=this.history.get(e)??[],s=n.length&&n[0].payload.meta.streamId!==t.payload.meta.streamId?[]:n;s.push({...t,createdAtMs:Date.now()}),this.history.set(e,s),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const n=t.filter(e=>e.createdAtMs>=Date.now()-36e5);n.length?this.history.set(e,n):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED")}view(){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([e,t])=>({instanceId:e,streamId:t.streamId,aiSessions:t.snapshot,revision:t.revision,lastEventAt:t.lastEventAt}))}}}const Ml=a.z.object({id:a.z.string().trim().min(1),instanceId:a.z.string().trim().min(1).max(160),sessionId:a.z.string().trim().min(1).max(120),unread:a.z.boolean(),lastStatus:a.z.enum(["running","waiting","idle","failed"]),sessionUpdatedAt:a.z.string().datetime(),createdAt:a.z.string().datetime(),updatedAt:a.z.string().datetime()}).strict();class $l{records;bySession=new Map;listener;constructor(e,t={}){this.records=new ii(e.aiSessionUnreadDir,{schema:Ml}),this.listener=t.onChanged}init(){this.records.init(),this.bySession.clear();for(const e of this.records.list())this.bySession.set(Ll(e.instanceId,e.sessionId),e)}reconcile(e,t){const n=new Set;for(const s of t.sessions)n.add(s.id),this.reconcileSession(e,s);for(const t of[...this.bySession.values()])t.instanceId!==e||n.has(t.sessionId)||this.deleteRecord(t)}decorate(e,t){return{...t,sessions:t.sessions.map(t=>({...t,unread:this.bySession.get(Ll(e,t.id))?.unread||!1}))}}markRead(e,t,n){const s=this.bySession.get(Ll(e,t));return s&&s.sessionUpdatedAt===n&&s.unread?ql(this.store({...s,unread:!1,updatedAt:(new Date).toISOString()},!1)):s?ql(s):void 0}removeInstance(e){for(const t of[...this.bySession.values()])t.instanceId===e&&this.deleteRecord(t)}reconcileSession(e,t){const n=Ll(e,t.id),s=this.bySession.get(n),i=(new Date).toISOString();if(!s)return void this.store({id:Bl(e,t.id),instanceId:e,sessionId:t.id,unread:!1,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,createdAt:i,updatedAt:i},!1);const r=function(e,t,n){return"running"!==n&&"waiting"!==n&&("running"===t||"waiting"===t||e)}(s.unread,s.lastStatus,t.status),a=r!==s.unread;(a||s.lastStatus!==t.status||s.sessionUpdatedAt!==t.updatedAt)&&this.store({...s,unread:r,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,updatedAt:i},a)}store(e,t){const n=this.records.put(e);return this.bySession.set(Ll(n.instanceId,n.sessionId),n),t&&this.listener?.(ql(n)),n}deleteRecord(e){this.records.delete(e.id),this.bySession.delete(Ll(e.instanceId,e.sessionId))}}function ql(e){return P.parse({instanceId:e.instanceId,sessionId:e.sessionId,unread:e.unread,sessionUpdatedAt:e.sessionUpdatedAt,updatedAt:e.updatedAt})}function Ll(e,t){return`${e}\0${t}`}function Bl(e,t){return`ais_unread_${o.createHash("sha256").update(Ll(e,t)).digest("hex").slice(0,32)}`}class Ul{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){const t=e.type===Ni?qi:e.type===Ei?Li:e.type===_i?Bi:void 0;if(!t)return!1;const n=t.safeParse(e.payload);return n.success?e.type===Ni?this.applySnapshot(n.data):e.type===Ei?this.applyPatch(n.data):this.applyRemoved(n.data):(this.logger?.warn?.({eventType:e.type,issues:n.error.issues,errorCode:"APP_SESSION_EVENT_INVALID"},"app-session.aggregator.event.invalid"),!1)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"app-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:Ni,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(n=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:n instanceof Error?n.message:String(n)},"app-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:Ei,payload:e})}applyRemoved(e){return this.apply({type:_i,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view({includeTombstones:e.includeTombstones})}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"app.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const n=this.snapshots.get(t)?.revision??0,s=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??n+1;if(e.streamId!==s||e.sinceRevision>n)return Ui.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===n)return Ui.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!1,events:[]});const r=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),a=!r.length||r[0].payload.meta.revision!==e.sinceRevision+1;return Ui.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:a,events:a?[]:r.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:n}=e.payload,s=this.advertisedStreams.get(n.instanceId)?.streamId;if(s&&s!==n.streamId)return this.logger?.warn?.({instanceId:n.instanceId,advertisedStreamId:s,streamId:n.streamId,revision:n.revision},"app-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(n.instanceId);if(i&&!t)return i.streamId===n.streamId&&(i.highWater=Math.max(i.highWater,n.revision)),!1;const r=this.snapshots.get(n.instanceId);e.type===Ni&&r&&r.streamId!==n.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:n.instanceId,previousStreamId:r.streamId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision,traceId:n.traceId,resetCount:this.counters.streamResets},"app-session.aggregator.stream-reset"));const a=function(e,t){const{meta:n}=t.payload;if(t.type===Ni)return e?.streamId===n.streamId&&n.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===n.streamId&&n.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:Di.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==n.streamId)return{kind:"snapshot-required",projection:e,streamId:n.streamId};if(n.revision===e.revision)return{kind:"duplicate",projection:e};if(n.revision<e.revision)return{kind:"stale",projection:e};if(n.revision!==e.revision+1||n.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:n.revision};const s=[...e.snapshot.sessions];if(t.type===Ei){const e=s.findIndex(e=>e.id===t.payload.session.id);e<0?s.push(t.payload.session):s[e]=t.payload.session}else{const e=s.findIndex(e=>e.id===t.payload.sessionId);e>=0&&s.splice(e,1),t.payload.tombstone&&s.push(t.payload.tombstone)}const i=Fi(s,n.generatedAt);return{kind:"applied",projection:Di.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:i})}}(r,e);if("applied"!==a.kind)return this.logger?.warn?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,currentRevision:r?.revision,revision:n.revision,outcome:a.kind},`app-session.aggregator.event.${a.kind}`),"gap"!==a.kind&&"snapshot-required"!==a.kind||("gap"===a.kind&&(this.counters.gaps+=1),this.recoverStream(n.instanceId,n.streamId,n.revision).catch(e=>{this.logger?.warn?.({instanceId:n.instanceId,streamId:n.streamId,revision:n.revision,error:e instanceof Error?e.message:String(e)},"app-session.aggregator.recovery.failed")})),!1;const o=a.projection;return this.snapshots.set(n.instanceId,o),this.rememberEvent(n.instanceId,e),this.logger?.info?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,revision:n.revision,reason:n.reason,sessionCount:o.snapshot.sessions.length,runningCount:o.snapshot.runningCount,problemCount:o.snapshot.problemCount},"app-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"app-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),n=new Set(t.instances.map(e=>e.instanceId));for(const[t,s]of e)n.has(t)||this.snapshots.get(t)!==s||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const n of t.instances){const t=this.snapshots.get(n.instanceId);if(t!==e.get(n.instanceId))continue;const s=this.advertisedStreams.get(n.instanceId)?.streamId;s&&s!==n.streamId||t?.streamId===n.streamId&&t.revision>=n.revision||(t?.streamId!==n.streamId&&this.history.delete(n.instanceId),this.snapshots.set(n.instanceId,{streamId:n.streamId,snapshot:n.appSessions,revision:n.revision,lastEventAt:n.lastEventAt}))}}recoverStream(e,t,n){const s=this.recoveries.get(e);if(s)return s.streamId===t?s.highWater=Math.max(s.highWater,n):(s.streamId=t,s.highWater=n),s.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:n},r=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:n,recoveryNumber:r},"app-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const n=this.snapshots.get(e);if(n&&n.streamId===t.streamId){if(n.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,n.streamId,n.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:Ni,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot-fallback"},"app-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:r,outcome:"delta"},"app-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:Ni,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot"},"app-session.aggregator.recovery.snapshot")}const s=this.snapshots.get(e),a=this.advertisedStreams.get(e);if(s&&a&&s.streamId===a.streamId&&s.revision>=Math.max(a.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:s.streamId,revision:s.revision,recoveryNumber:r,outcome:"success"},"app-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const n=this.history.get(e)??[],s=n.length&&n[0].payload.meta.streamId!==t.payload.meta.streamId?[]:n;s.push({...t,createdAtMs:Date.now()}),this.history.set(e,s),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const n=t.filter(e=>e.createdAtMs>=Date.now()-36e5);n.length?this.history.set(e,n):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("APP_SESSION_DELTA_INSTANCE_ID_REQUIRED")}visibleSnapshot(e){return Fi(e.sessions.filter(e=>Ma("string"==typeof e.status?e.status:void 0)),e.updatedAt)}view(e={}){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([t,n])=>({instanceId:t,streamId:n.streamId,appSessions:e.includeTombstones?n.snapshot:this.visibleSnapshot(n.snapshot),revision:n.revision,lastEventAt:n.lastEventAt}))}}}function Fl(e){return e.raw[0].replace(/\\\$\{/g,"${")}class Hl{states=new Map;events;constructor(e){this.events=e}handle(e){if(e.type===Ds){const t=Ms.safeParse(e.payload);return void(t.success&&this.reconcileLifecycle(t.data))}if("instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";return void(t&&this.deleteState(t))}if(e.type===ds){const t=ps.safeParse(e.payload);return void(t.success&&this.applyOutput(t.data))}if(e.type===cs){const t=us.safeParse(e.payload);t.success&&this.applyFinished(t.data)}}snapshots(){return[...this.states.values()].filter(e=>e.progress).map(e=>gs.parse({...e.progress,terminalTail:e.terminalTail,...e.terminalTruncated?{terminalTruncated:!0}:{}}))}close(){for(const e of this.states.values())e.timer&&clearTimeout(e.timer);this.states.clear()}deleteState(e){const t=this.states.get(e);t?.timer&&clearTimeout(t.timer),this.states.delete(e)}reconcileLifecycle(e){const t=this.states.get(e.instanceId),n=e.imageProvisioning;t&&(!n||n.generation>t.identity.generation||n.generation===t.identity.generation&&"ready"===n.phase)&&this.deleteState(e.instanceId)}applyOutput(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&!e.replay&&e.sequence<=t.sequence)return;const n=this.requireState(e);e.replay&&(n.terminal=new Kl,n.terminalTail="",n.layers.clear(),n.terminalTruncated=!1),n.sequence=Math.max(n.sequence,e.sequence),n.observedAt=e.observedAt,n.terminal.write(e.data);const s=`${n.terminalTail}${e.data}`;n.terminalTruncated||=s.length>262144,n.terminalTail=s.slice(-262144),function(e,t){for(const n of t.split("\n")){const t=n.trim().match(/^([a-f0-9]{6,64}):\s+(.+)$/i);if(!t)continue;const[,s,i]=t,r=i.toLowerCase(),a=["pull complete","already exists","download complete","pulling fs layer","waiting","downloading","extracting"].find(e=>r.startsWith(e));if(!a)continue;const o=e.get(s)||{status:a},d=Gl(i);e.set(s,{...o,status:a,...d||{}})}}(n.layers,n.terminal.text()),n.progress=Wl(n),this.schedule(n)}applyFinished(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&e.sequence<=t.sequence)return;const n=this.requireState(e);n.sequence=Math.max(n.sequence,e.sequence),n.observedAt=e.observedAt,n.outcome=e.outcome,n.progress=Wl(n),this.publish(n,!0)}requireState(e){const t=this.states.get(e.instanceId);if(t&&t.identity.generation===e.generation)return t;t?.timer&&clearTimeout(t.timer);const n={identity:{instanceId:e.instanceId,generation:e.generation,requestedReference:e.requestedReference},sequence:e.sequence,observedAt:e.observedAt,terminal:new Kl,terminalTail:"",terminalTruncated:!1,layers:new Map};return this.states.set(e.instanceId,n),n}schedule(e){e.timer||(e.timer=setTimeout(()=>this.publish(e,!1),250),e.timer.unref?.())}publish(e,t){e.timer&&clearTimeout(e.timer),e.timer=void 0,e.progress&&(e.progress=Wl(e),this.events.publish("image.pull.progress",e.progress,{topic:"instances",scope:{instanceId:e.identity.instanceId}}),t&&(e.progress=Wl(e)))}}function Wl(e){const t=[...e.layers.values()],n=t.filter(e=>["pull complete","already exists"].includes(e.status)).length,s=t.filter(e=>"download complete"===e.status).length,i=t.filter(e=>"downloading"===e.status).length,r=t.filter(e=>"extracting"===e.status).length;let a=0,o=0;for(const e of t)e.total&&(o+=e.total,a+=["pull complete","already exists","download complete"].includes(e.status)?e.total:Math.min(e.current||0,e.total));const d="failed"===e.outcome?"failed":"succeeded"===e.outcome?"complete":r>0?"extracting":t.length>0?"pulling":"connecting",c=t.filter(e=>"already exists"!==e.status),l=c.length>0&&c.every(e=>Boolean(e.total)),p=l&&o>0?Math.max(0,Math.min(100,a/o*100)):void 0,u="failed"===d?"Image pull failed":"complete"===d?"Image pull complete":t.length?`${n}/${t.length} layers ready${void 0===p?"":` · ${Math.round(p)}%`}`:"Connecting to image registry";return gs.parse({...e.identity,sequence:e.sequence,observedAt:e.observedAt,status:d,layers:{total:t.length,completed:n,downloaded:s,downloading:i,extracting:r},...l&&o>0?{bytes:{current:a,total:o},percent:p}:{},message:u})}function Gl(e){const t=e.match(/([\d.]+)\s*([kmgt]?i?b)\s*\/\s*([\d.]+)\s*([kmgt]?i?b)/i);if(t)return{current:Vl(t[1],t[2]),total:Vl(t[3],t[4])}}function Vl(e,t){return Number(e)*1024**({b:0,kb:1,kib:1,mb:2,mib:2,gb:3,gib:3,tb:4,tib:4}[t.toLowerCase()]||0)}class Kl{lines=[[]];row=0;col=0;pending="";write(e){const t=`${this.pending}${e}`;this.pending="";for(let e=0;e<t.length;){const n=t[e];if(""===n&&"["===t[e+1]){let n=e+2;for(;n<t.length&&!/[\x40-\x7e]/.test(t[n]);)n+=1;if(n>=t.length){this.pending=t.slice(e);break}this.csi(t.slice(e+2,n),t[n]),e=n+1;continue}"\r"===n?this.col=0:"\n"===n?(this.row+=1,this.col=0,this.ensureLine()):"\b"===n?this.col=Math.max(0,this.col-1):n>=" "&&(this.ensureLine(),this.lines[this.row][this.col]=n,this.col+=1),e+=1}this.trim()}text(){return this.lines.map(e=>e.join("").trimEnd()).join("\n")}csi(e,t){const n=e.replace(/^\?/,"").split(";").map(e=>Number(e||1)),s=n[0]||1;"A"===t?this.row=Math.max(0,this.row-s):"B"===t?(this.row+=s,this.ensureLine()):"C"===t?this.col+=s:"D"===t?this.col=Math.max(0,this.col-s):"G"===t?this.col=Math.max(0,s-1):"H"===t||"f"===t?(this.row=Math.max(0,s-1),this.col=Math.max(0,(n[1]||1)-1),this.ensureLine()):"K"===t?(this.ensureLine(),2===(n[0]||0)?this.lines[this.row]=[]:this.lines[this.row].splice(this.col)):"J"===t&&2===(n[0]||0)&&(this.lines=[[]],this.row=0,this.col=0)}ensureLine(){for(;this.lines.length<=this.row;)this.lines.push([])}trim(){if(this.lines.length<=500)return;const e=this.lines.length-500;this.lines.splice(0,e),this.row=Math.max(0,this.row-e)}}const Jl=a.z.object({revision:a.z.number().int().nonnegative(),invites:a.z.array(lt),bindings:a.z.array(ht)}).strict(),Ql={invite:new Set(Object.keys(lt.shape)),binding:new Set(Object.keys(ht.shape))};function Yl(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e;return Object.fromEntries([...t].filter(e=>e in n).map(e=>[e,n[e]]))}function Xl(e,t,n){if(!t||"object"!=typeof t||Array.isArray(t))return t;const s=t,i=Ql[e],r=Object.keys(s).filter(e=>!i.has(e));r.length>0&&n?.({kind:e,fields:r});const a=Yl(s,i);return"binding"===e&&a.lastError&&(a.lastError=Yl(a.lastError,Object.keys(dt.shape))),a}function Zl(e){const{tokenHash:t,...n}=e;return pt.parse(n)}function ep(e){const{credentialHash:t,...n}=e;return yt.parse(n)}class tp{authority;constructor(e,t){this.authority=new ri(e,()=>({revision:0,invites:[],bindings:[]}),{schema:Jl,sanitize:e=>function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=new Set(["revision","invites","bindings"]),i=Object.keys(n).filter(e=>!s.has(e));return i.length>0&&t?.({kind:"authority",fields:i}),{...void 0===n.revision?{}:{revision:n.revision},invites:Array.isArray(n.invites)?n.invites.map(e=>Xl("invite",e,t)):n.invites,bindings:Array.isArray(n.bindings)?n.bindings.map(e=>Xl("binding",e,t)):n.bindings}}(e,e=>t?.("unknown stored control-plane proxy fields were ignored",e)),logger:t,directoryMode:448,fileMode:384})}init(){this.authority.init()}snapshot(){return this.authority.get()}transaction(e){const t=this.authority.get(),n=structuredClone(t),s=e(n);return JSON.stringify(n)!==JSON.stringify(t)&&this.authority.put({...n,revision:t.revision+1}),s}listInvites(){return this.snapshot().invites}getInvite(e){return this.snapshot().invites.find(t=>t.id===e)}listBindings(){return this.snapshot().bindings}getBinding(e){return this.snapshot().bindings.find(t=>t.id===e)}}class np extends Error{code;statusCode;retryable;details;constructor(e,t,n,s=!1,i={}){super(t),this.name="ControlPlaneProxyServiceError",this.code=e,this.statusCode=n,this.retryable=s,this.details=i}}const sp=e=>o.createHash("sha256").update(e).digest("base64url"),ip=()=>o.randomBytes(32).toString("base64url");function rp(e,t){const n=sp(t),s=Buffer.from(e),i=Buffer.from(n);return s.length===i.length&&o.timingSafeEqual(s,i)}function ap(e,t,n,s=!1,i={}){throw new np(e,t,n,s,i)}class op{store;targets;configuredProxyOrigin;proxyOriginProvider;now;createTokenValue;constructor(e,t,n){this.store=e,this.targets=t,this.configuredProxyOrigin=n.proxyOrigin,this.proxyOriginProvider=n.proxyOriginProvider,this.now=n.now??(()=>new Date),this.createTokenValue=n.createToken??ip}init(){this.store.init(),this.expireInvites()}createInvite(e,t){const n=this.resolveProxyOrigin();n||ap(at.Unavailable,"Configure an externally reachable HTTPS control-plane proxy origin before creating an invite.",409,!1);const s=ut.parse(e);this.requireManageableTarget(s.targetNodeId);const i=this.createTokenValue(),r=this.nowIso(),a={id:ni("proxy_invite"),targetNodeId:s.targetNodeId,tokenHash:sp(i),status:"active",createdBy:t,expiresAt:new Date(this.now().getTime()+1e3*s.expiresInSeconds).toISOString(),createdAt:r,updatedAt:r},o=gt.parse({invite:Zl(a),token:i,proxyOrigin:n,protocolVersion:Ge});return this.store.transaction(e=>e.invites.push(a)),o}resolveProxyOrigin(){const e=this.configuredProxyOrigin??this.proxyOriginProvider?.();if(!e)return;const t=Xe.safeParse(e);return t.success?t.data:void 0}listInvites(){return this.expireInvites(),this.store.listInvites().map(Zl)}revokeInvite(e){const t=this.requireInvite(e);if("consumed"===t.status&&ap(at.InviteConsumed,"A consumed proxy invite cannot be revoked independently of its binding.",409,!1,{inviteId:e}),"revoked"===t.status||"expired"===t.status)return Zl(t);const n=this.nowIso(),s={...t,status:"revoked",revokedAt:n,updatedAt:n};return this.store.transaction(t=>{t.invites[t.invites.findIndex(t=>t.id===e)]=s}),Zl(s)}claimInvite(e){const t=ft.parse(e);this.requireCurrentProtocol(t.protocolVersion),this.expireInvites();const n=sp(t.credential);return this.store.transaction(e=>{const s=e.bindings.find(e=>e.claimId===t.claimId);if(!t.inviteToken)return s||ap(at.InviteInvalid,"Proxy invite token is required for the first claim.",401),(s.sourceControlPlaneId!==t.sourceControlPlaneId||s.bindingKeyId!==t.bindingKeyId||s.credentialHash!==n||void 0!==t.targetNodeId&&s.targetNodeId!==t.targetNodeId)&&ap(at.BindingIdentityConflict,"Proxy claim conflicts with an existing binding identity.",409,!1,{claimId:t.claimId}),"revoked"===s.status&&ap(at.BindingRevoked,"Proxy binding is revoked.",403,!1,{bindingId:s.id}),this.claimResult(s,this.requireKnownTarget(s.targetNodeId));const i=e.invites.find(e=>rp(e.tokenHash,t.inviteToken));i||ap(at.InviteInvalid,"Proxy invite is invalid.",401),t.targetNodeId&&t.targetNodeId!==i.targetNodeId&&ap(at.TargetMismatch,"Proxy invite is bound to a different target.",409,!1,{inviteId:i.id,targetNodeId:t.targetNodeId});const r=(a=i.id,`proxy_binding_${sp(a).slice(0,32)}`);var a;const o=e.bindings.find(e=>e.id===r),d=e.bindings.find(e=>e.claimId===t.claimId),c=o??d;if(o&&d&&o.id!==d.id&&ap(at.BindingIdentityConflict,"Proxy claim conflicts with an existing binding identity.",409,!1,{claimId:t.claimId}),c)return this.requireExactIdentity(c,r,i.targetNodeId,t,n),"revoked"===c.status&&ap(at.BindingRevoked,"Proxy binding is revoked.",403,!1,{bindingId:c.id}),"consumed"===i.status&&i.consumedByClaimId===t.claimId||ap(at.BindingIdentityConflict,"Proxy binding and invite authority are inconsistent.",409,!1,{claimId:t.claimId}),this.claimResult(c,this.requireKnownTarget(c.targetNodeId));this.requireClaimableInvite(i);const l=this.requireManageableTarget(i.targetNodeId),p=this.nowIso(),u={id:r,claimId:t.claimId,sourceControlPlaneId:t.sourceControlPlaneId,targetNodeId:i.targetNodeId,bindingKeyId:t.bindingKeyId,credentialHash:n,status:"active",revision:1,createdAt:p,updatedAt:p};return e.bindings.push(u),Object.assign(i,{status:"consumed",consumedByClaimId:t.claimId,consumedAt:p,updatedAt:p}),this.claimResult(u,l)})}listBindings(){return this.store.listBindings().map(ep)}authenticateBinding(e,t){const n=It.parse(t),s=this.store.getBinding(e);return s||ap(at.BindingUnknown,"Proxy binding is unknown.",404,!1,{bindingId:e}),"active"!==s.status&&ap(at.BindingRevoked,"Proxy binding is revoked.",403,!1,{bindingId:e}),s.sourceControlPlaneId===n.sourceControlPlaneId&&s.bindingKeyId===n.bindingKeyId&&rp(s.credentialHash,n.credential)||ap(at.AuthenticationFailed,"Proxy binding authentication failed.",401,!1,{bindingId:e}),ep(s)}revokeBinding(e){const t=this.requireBinding(e);if("revoked"===t.status)return ep(t);const n=this.nowIso(),s={...t,status:"revoked",revision:t.revision+1,revokedAt:n,updatedAt:n};return this.store.transaction(t=>{t.bindings[t.bindings.findIndex(t=>t.id===e)]=s}),ep(s)}revokeTarget(e){return this.store.transaction(t=>{const n=this.nowIso();return{bindings:t.bindings.filter(t=>t.targetNodeId===e&&"active"===t.status).map(e=>(Object.assign(e,{status:"revoked",revision:e.revision+1,revokedAt:n,updatedAt:n}),ep(e))),invites:t.invites.filter(t=>t.targetNodeId===e&&"active"===t.status).map(e=>(Object.assign(e,{status:"revoked",revokedAt:n,updatedAt:n}),Zl(e)))}})}nowIso(){return this.now().toISOString()}requireCurrentProtocol(e){e!==Ge&&ap(at.ProtocolUnsupported,"Control-plane proxy protocol version is unsupported.",426,!1,{requestedVersion:e,supportedVersion:Ge})}requireKnownTarget(e){const t=this.targets.get(e);return t||ap(at.TargetUnavailable,"Proxy target no longer exists.",404,!1,{targetNodeId:e}),t}requireManageableTarget(e){const t=this.requireKnownTarget(e);return t.manageable&&"online"===t.status||ap(at.TargetUnavailable,"Proxy target is not currently manageable.",503,!0,{targetNodeId:e}),t}requireInvite(e){const t=this.store.getInvite(e);return t||ap(at.InviteInvalid,"Proxy invite is unknown.",404,!1,{inviteId:e}),t}requireBinding(e){const t=this.store.getBinding(e);return t||ap(at.BindingUnknown,"Proxy binding is unknown.",404,!1,{bindingId:e}),t}requireClaimableInvite(e){"active"!==e.status&&ap({expired:at.InviteExpired,consumed:at.InviteConsumed,revoked:at.InviteRevoked}[e.status],`Proxy invite is ${e.status}.`,"expired"===e.status?410:409,!1,{inviteId:e.id})}requireExactIdentity(e,t,n,s,i){e.id===t&&e.claimId===s.claimId&&e.sourceControlPlaneId===s.sourceControlPlaneId&&e.targetNodeId===n&&e.bindingKeyId===s.bindingKeyId&&e.credentialHash===i||ap(at.BindingIdentityConflict,"Proxy claim conflicts with an existing binding identity.",409,!1,{claimId:s.claimId})}claimResult(e,t){const{manageable:n,...s}=t;return Ct.parse({protocolVersion:Ge,binding:ep(e),target:s})}expireInvites(){const e=this.now().getTime();this.store.listInvites().some(t=>"active"===t.status&&Date.parse(t.expiresAt)<=e)&&this.store.transaction(t=>{const n=this.nowIso();for(const s of t.invites)"active"===s.status&&Date.parse(s.expiresAt)<=e&&(s.status="expired",s.updatedAt=n)})}}class dp extends Error{statusCode=429;code=at.ResourceLimit;retryable=!0;details;constructor(e,t){super(e),this.name="NodeProxyRuntimeError",this.details=t}}class cp{maxRequestBodyBytes;maxConcurrentHttpPerBinding;maxConcurrentStreamsPerBinding;maxHttpRequestsPerMinutePerBinding;maxConcurrentWebSocketsPerBinding;maxHttpResponseBytes;httpStreamIdleTimeoutMs;maxWebSocketFrameBytes;maxWebSocketBytes;webSocketIdleTimeoutMs;httpRequestWindowMs;bindings=new Map;requestWindows=new Map;requestWindowTimers=new Map;constructor(e={}){this.maxConcurrentHttpPerBinding=e.maxConcurrentHttpPerBinding??32,this.maxConcurrentStreamsPerBinding=e.maxConcurrentStreamsPerBinding??16,this.maxHttpRequestsPerMinutePerBinding=e.maxHttpRequestsPerMinutePerBinding??600,this.maxConcurrentWebSocketsPerBinding=e.maxConcurrentWebSocketsPerBinding??16,this.maxRequestBodyBytes=e.maxRequestBodyBytes??8388608,this.maxHttpResponseBytes=e.maxHttpResponseBytes??268435456,this.httpStreamIdleTimeoutMs=e.httpStreamIdleTimeoutMs??12e4,this.maxWebSocketFrameBytes=e.maxWebSocketFrameBytes??8388608,this.maxWebSocketBytes=e.maxWebSocketBytes??268435456,this.webSocketIdleTimeoutMs=e.webSocketIdleTimeoutMs??3e5,this.httpRequestWindowMs=e.httpRequestWindowMs??6e4}reserveHttp(e){this.consumeRequestRate(e);const t=this.activity(e);if(t.controllers.size>=this.maxConcurrentHttpPerBinding)throw this.deleteIfEmpty(e,t),this.limit("Proxy HTTP concurrency limit exceeded.",e,"http-concurrency",this.maxConcurrentHttpPerBinding);const n=new AbortController;t.controllers.add(n);let s,i=!1,r=!1,a=0;const o=()=>{r&&!i&&(s&&clearTimeout(s),s=setTimeout(()=>{n.abort(Object.assign(new Error("Proxy HTTP stream idle timeout."),{code:at.ResourceLimit}))},this.httpStreamIdleTimeoutMs),s.unref?.())};return{controller:n,acceptRequestBody:t=>{if(t>this.maxRequestBodyBytes)throw this.limit("Proxy request body limit exceeded.",e,"request-body",this.maxRequestBodyBytes)},beginResponseStream:()=>{if(!i&&!r){if(t.streams.size>=this.maxConcurrentStreamsPerBinding)throw this.limit("Proxy HTTP stream concurrency limit exceeded.",e,"stream-concurrency",this.maxConcurrentStreamsPerBinding);r=!0,t.streams.add(n),o()}},acceptResponseChunk:t=>{if(!i){if(a+=lp(t),a>this.maxHttpResponseBytes){const t=this.limit("Proxy HTTP response byte limit exceeded.",e,"response-bytes",this.maxHttpResponseBytes);throw n.abort(t),t}o()}},release:()=>{i||(i=!0,s&&clearTimeout(s),t.controllers.delete(n),t.streams.delete(n),this.deleteIfEmpty(e,t))}}}openHttp(e,t){const n=this.reserveHttp(e);try{return n.acceptRequestBody(t),n}catch(e){throw n.release(),e}}openWebSocket(e,t,n){const s=this.activity(e);if(s.sockets.size>=this.maxConcurrentWebSocketsPerBinding)throw this.limit("Proxy WebSocket concurrency limit exceeded.",e,"websocket-concurrency",this.maxConcurrentWebSocketsPerBinding);let i,r=!1,a=0;const o=()=>{r||(r=!0,i&&clearTimeout(i),s.sockets.delete(p),this.deleteIfEmpty(e,s),n?.())},d=e=>{o(),t.close(1009,e)},c=()=>{i&&clearTimeout(i),i=setTimeout(()=>{o(),t.close(1001,"Proxy WebSocket idle timeout.")},this.webSocketIdleTimeoutMs),i.unref?.()},l=e=>{const t=lp(e);return t>this.maxWebSocketFrameBytes?(d("Proxy WebSocket frame limit exceeded."),!1):(a+=t,a>this.maxWebSocketBytes?(d("Proxy WebSocket byte limit exceeded."),!1):(c(),!0))};c();const p={get readyState(){return t.readyState},send:e=>{!r&&l(e)&&t.send(e)},close:(e,n)=>{o(),t.close(e,n)},on:(e,n)=>{"message"!==e?"close"!==e&&"error"!==e?t.on(e,n):t.on(e,(...e)=>{o(),n(...e)}):t.on(e,(e,...t)=>{!r&&l(e)&&n(e,...t)})}};return s.sockets.add(p),p}closeBinding(e,t="Proxy binding was revoked."){const n=this.bindings.get(e);if(!n)return this.clearRequestWindow(e),{abortedRequests:0,closedSockets:0};const s=[...n.controllers],i=[...n.sockets],r=[...n.cleanup];this.bindings.delete(e);for(const e of s)e.abort(Object.assign(new Error(t),{code:at.BindingRevoked}));for(const e of r)e();for(const e of i)e.close(1008,t);return n.controllers.clear(),n.streams.clear(),n.sockets.clear(),n.cleanup.clear(),this.clearRequestWindow(e),{abortedRequests:s.length,closedSockets:i.length}}diagnostics(e){if(e){const t=this.bindings.get(e);return{bindingId:e,activeHttp:t?.controllers.size??0,activeStreams:t?.streams.size??0,activeWebSockets:t?.sockets.size??0}}return[...this.bindings].map(([e,t])=>({bindingId:e,activeHttp:t.controllers.size,activeStreams:t.streams.size,activeWebSockets:t.sockets.size}))}registerBindingCleanup(e,t){const n=this.activity(e);return n.cleanup.add(t),()=>{n.cleanup.delete(t),this.deleteIfEmpty(e,n)}}activity(e){const t=this.bindings.get(e);if(t)return t;const n={controllers:new Set,streams:new Set,sockets:new Set,cleanup:new Set};return this.bindings.set(e,n),n}deleteIfEmpty(e,t){0===t.controllers.size&&0===t.streams.size&&0===t.sockets.size&&0===t.cleanup.size&&this.bindings.get(e)===t&&this.bindings.delete(e)}limit(e,t,n,s){return new dp(e,{bindingId:t,resource:n,limit:s})}consumeRequestRate(e){const t=Date.now(),n=t-this.httpRequestWindowMs,s=(this.requestWindows.get(e)||[]).filter(e=>e>n);if(s.length>=this.maxHttpRequestsPerMinutePerBinding)throw this.limit("Proxy HTTP request rate limit exceeded.",e,"request-rate",this.maxHttpRequestsPerMinutePerBinding);s.push(t),this.requestWindows.set(e,s),this.scheduleRequestWindowCleanup(e)}scheduleRequestWindowCleanup(e){const t=this.requestWindowTimers.get(e);t&&clearTimeout(t);const n=this.requestWindows.get(e);if(!n?.length)return this.requestWindows.delete(e),void this.requestWindowTimers.delete(e);const s=Math.max(1,n[0]+this.httpRequestWindowMs-Date.now()+1),i=setTimeout(()=>{this.requestWindowTimers.delete(e);const t=Date.now()-this.httpRequestWindowMs,n=(this.requestWindows.get(e)||[]).filter(e=>e>t);n.length?(this.requestWindows.set(e,n),this.scheduleRequestWindowCleanup(e)):this.requestWindows.delete(e)},s);i.unref?.(),this.requestWindowTimers.set(e,i)}clearRequestWindow(e){this.requestWindows.delete(e);const t=this.requestWindowTimers.get(e);t&&clearTimeout(t),this.requestWindowTimers.delete(e)}}function lp(e){return Buffer.isBuffer(e)||e instanceof ArrayBuffer||ArrayBuffer.isView(e)?e.byteLength:Buffer.byteLength("string"==typeof e?e:String(e),"utf8")}class pp{streams=new Map;historyLimit;maxEventBytes;maxHistoryBytes;createStreamId;projectTarget;unsubscribeBus;constructor(e,t){this.historyLimit=Math.max(1,t.historyLimit??256),this.maxEventBytes=Math.max(1024,t.maxEventBytes??524288),this.maxHistoryBytes=Math.max(this.maxEventBytes,t.maxHistoryBytes??16777216),this.createStreamId=t.createStreamId??(e=>`proxy_events_${e}_${o.randomUUID().replace(/-/g,"").slice(0,16)}`),this.projectTarget=t.projectTarget,this.unsubscribeBus=e.on(e=>this.accept(e))}stop(){this.unsubscribeBus();for(const e of this.streams.values())e.subscribers.clear();this.streams.clear()}cursor(e){const t=this.stream(e);return{streamId:t.streamId,revision:t.revision,earliestRetainedRevision:up(t)}}subscribe(e,t,n,s=()=>{}){const{bindingId:i,sourceControlPlaneId:r,targetNodeId:a}=e,o=this.stream(a),d=up(o);if(t>o.revision||t<d-1)return n(At.parse({type:"control-plane-proxy.snapshot-required",protocolVersion:Ge,streamId:o.streamId,bindingId:i,sourceControlPlaneId:r,targetNodeId:a,sinceRevision:t,latestRevision:o.revision,earliestRetainedRevision:d,error:{code:at.SnapshotRequired,message:"Proxy target event history cannot continue from the requested revision; fetch a new snapshot.",retryable:!0,details:{targetNodeId:a}}})),{kind:"snapshot-required",close:()=>{}};n(St.parse({type:"control-plane-proxy.events.ready",protocolVersion:Ge,streamId:o.streamId,bindingId:i,sourceControlPlaneId:r,targetNodeId:a,latestRevision:o.revision,earliestRetainedRevision:d}));for(const e of o.history)e.event.revision>t&&n(gp(e.event,i,r));const c={bindingId:i,sourceControlPlaneId:r,send:n,onDeliveryFailure:s};o.subscribers.add(c);let l=!1;return{kind:"subscribed",close:()=>{l||(l=!0,o.subscribers.delete(c))}}}diagnostics(e){const t=this.stream(e);return{targetNodeId:e,streamId:t.streamId,revision:t.revision,earliestRetainedRevision:up(t),historySize:t.history.length,historyBytes:t.historyBytes,subscribers:t.subscribers.size,subscriberBindings:[...t.subscribers].map(({bindingId:e,sourceControlPlaneId:t})=>({bindingId:e,sourceControlPlaneId:t}))}}accept(e){const t=e.scope?.nodeId;if("string"!=typeof t||!t)return;let n;try{n=this.projectTarget(t)}catch{return}if(!n)return;const s=this.stream(t),i=s.revision+1,r=wt.safeParse({type:"control-plane-proxy.event",protocolVersion:Ge,streamId:s.streamId,bindingId:"proxy_projection",sourceControlPlaneId:"proxy_projection",targetNodeId:t,revision:i,source:{id:e.id,seq:e.seq},target:n,event:{type:e.type,topic:e.topic,createdAt:e.createdAt}});if(!r.success)return;const{bindingId:a,sourceControlPlaneId:o,...d}=r.data,c=mp(d);if(!(void 0===c||c>this.maxEventBytes-512)){for(s.revision=i,s.history.push({event:d,bytes:c}),s.historyBytes+=c;s.history.length>this.historyLimit||s.historyBytes>this.maxHistoryBytes;){const e=s.history.shift();e&&(s.historyBytes-=e.bytes)}for(const e of[...s.subscribers])try{const t=gp(d,e.bindingId,e.sourceControlPlaneId),n=mp(t);if(void 0===n||n>this.maxEventBytes)throw new Error("Proxy event byte limit exceeded.");e.send(t)}catch{s.subscribers.delete(e);try{e.onDeliveryFailure()}catch{}}}}stream(e){const t=this.streams.get(e);if(t)return t;const n={streamId:this.createStreamId(e),revision:0,history:[],historyBytes:0,subscribers:new Set};return this.streams.set(e,n),n}}function up(e){return e.history[0]?.event.revision??e.revision+1}function gp(e,t,n){return wt.parse({...e,bindingId:t,sourceControlPlaneId:n})}function mp(e){try{return Buffer.byteLength(JSON.stringify(e),"utf8")}catch{return}}function hp(e,t){const n=new WeakSet;let s=t,i=0;const r=e=>{if(s-=Buffer.byteLength(e,"utf8"),s<0)throw yp("Proxy target projection byte limit exceeded.",{resource:"target-projection-bytes",limit:t})},a=(e,t)=>{if(i+=1,i>16384||t>24)throw yp("Proxy target projection complexity limit exceeded.");if(null===e||"boolean"==typeof e)return e;if("string"==typeof e)return r(e),e;if("number"==typeof e)return Number.isFinite(e)?e:null;if("object"!=typeof e)return;if(n.has(e))throw yp("Proxy target projection contains a cycle.",void 0,!1);if(n.add(e),Array.isArray(e)){const s=e.map(e=>a(e,t+1)??null);return n.delete(e),s}const s={};for(const[n,i]of Object.entries(e)){if("__proto__"===n||"constructor"===n||"prototype"===n)continue;r(n);const e=a(i,t+1);void 0!==e&&(s[n]=e)}return n.delete(e),s};return a(e,0)}function yp(e,t,n=!0){return Object.assign(new Error(e),{code:"CONTROL_PLANE_PROXY_RESOURCE_LIMIT",statusCode:429,retryable:n,...t?{details:t}:{}})}function fp(e){const{manageable:t,...n}=e;return n}const Ip=a.z.object({bindingId:a.z.string().trim().min(1).max(160)}).passthrough(),bp=a.z.object({route:Ze}).strict(),vp=a.z.object({sinceRevision:a.z.coerce.number().int().nonnegative()}).strict(),wp=new Set(["connection","cookie","host","keep-alive","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),Sp=524288;function Ap(e,t,n){const s=Ep(n.headers,nt);if(s!==Ge)throw Np(at.ProtocolUnsupported,"Control-plane proxy protocol version is unsupported.",426,!1,{requestedVersion:s,supportedVersion:Ge});const i=Ep(n.headers,st),r=Ep(n.headers,it),a=Ep(n.headers,rt);if(!i||!r||!a)throw Np(at.AuthenticationFailed,"Proxy binding authentication headers are incomplete.",401);return e.authenticateBinding(t,{sourceControlPlaneId:i,bindingKeyId:r,credential:a})}function kp(e,t,n){const s=Ap(e,t.id,n);if(s.id!==t.id||s.revision!==t.revision||s.sourceControlPlaneId!==t.sourceControlPlaneId||s.targetNodeId!==t.targetNodeId||s.bindingKeyId!==t.bindingKeyId||"active"!==s.status)throw Np(at.BindingIdentityConflict,"Proxy binding changed while the request was being established.",409,!0,{bindingId:t.id,expectedRevision:t.revision,actualRevision:s.revision})}function Cp(e,t){if(t.id!==e.targetNodeId)throw Np(at.TargetMismatch,"Proxy target resolver returned another node.",409,!1,{bindingId:e.id,targetNodeId:e.targetNodeId})}function xp(e){for(const t of Object.keys(e)){const e=t.toLowerCase();if(e.startsWith("x-taskhandoff-node-")||e.startsWith("x-task-handoff-node-"))throw Np(at.HeaderInvalid,"Caller-supplied node transport authentication headers are forbidden.",400,!1,{header:e})}const t=Object.fromEntries([...et].flatMap(t=>{if(wp.has(t))return[];const n=e[t];return void 0===n?[]:[[t,Array.isArray(n)?n.join(", "):String(n)]]}));if(void 0!==t["x-request-id"]){const e=Ye.safeParse(t["x-request-id"]);if(!e.success)throw Np(at.HeaderInvalid,"Proxy request correlation id is invalid.",400,!1,{header:"x-request-id"});t["x-request-id"]=e.data}return t}function Tp(e,t,n){const s=zp(t),i=_p(n?.headers);return i&&e.header("x-request-id",i),e.code(s.statusCode).send({error:{code:s.code,message:s.message,retryable:s.retryable,...s.details?{details:s.details}:{}}})}function zp(e){const t=e&&"object"==typeof e?e:{},n="number"==typeof t.statusCode?t.statusCode:502;return{statusCode:n,code:("string"==typeof t.code?t.code:void 0)||at.TransportFailed,message:e instanceof Error?e.message:"Control-plane proxy transport failed.",retryable:"boolean"==typeof t.retryable?t.retryable:n>=500,details:t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?t.details:void 0}}function Np(e,t,n,s=!1,i){return Object.assign(new Error(t),{code:e,statusCode:n,retryable:s,...i?{details:i}:{}})}function Ep(e,t){const n=e[t];return Array.isArray(n)?n.join(","):"string"==typeof n?n:""}function _p(e){if(!e)return;const t=Ye.safeParse(Ep(e,"x-request-id"));return t.success?t.data:void 0}function Pp(e){let t="";for(const n of e){if(Buffer.byteLength(t+n)>123)break;t+=n}return t}const Op=e=>dt.parse({code:at.Unavailable,message:e,retryable:!0});class Rp{service;subscriptions=new Map;fetchImpl;openWebSocket;reconnectDelayMs;onStateChanged;logger;running=!1;constructor(e,t={}){this.service=e,this.fetchImpl=t.fetchImpl??fetch,this.openWebSocket=t.openWebSocket??((e,t)=>new l.WebSocket(e,{headers:t})),this.reconnectDelayMs=Math.max(0,t.reconnectDelayMs??2e3),this.onStateChanged=t.onStateChanged??(()=>{}),this.logger=t.logger}start(){this.running||(this.running=!0,this.syncNow())}syncNow(){if(!this.running)return;const e=new Map(this.service.listNodes().filter(e=>"control-plane-proxy"===e.connectionMode&&!1!==e.connectionEnabled).map(e=>[e.id,e]));for(const t of this.subscriptions.keys())e.has(t)||this.remove(t);for(const t of e.values()){const e=this.subscriptionIdentity(t),n=this.subscriptions.get(t.id);if(n?.identity===e)continue;n&&this.remove(t.id);const s={generation:1,identity:e};this.subscriptions.set(t.id,s),this.connect(t.id,s.generation)}}stop(){this.running=!1;for(const e of[...this.subscriptions.keys()])this.remove(e)}remove(e){const t=this.subscriptions.get(e);t&&(t.generation+=1,t.reconnect&&clearTimeout(t.reconnect),t.socket?.close(),this.subscriptions.delete(e))}async connect(e,t){if(!this.current(e,t))return;const n=this.service.proxyPrivateStore.nodeCredential(e);if(n)try{const s=await this.fetchImpl(function(e){return new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/snapshot`,e.proxyOrigin)}(n),{headers:Cr(n)});if(!this.current(e,t))return;const i=await s.json().catch(()=>{});if(!s.ok){const n=dt.safeParse(i?.error),r=n.success?n.data:Op(`Proxy snapshot failed with HTTP ${s.status}.`);return r.code===at.BindingRevoked?void this.onStateChanged(this.service.markProxyBindingRevoked(e,r)):void this.fail(e,t,r,r.retryable)}const r=vt.parse(i?.data);if(this.validateIdentity(n,r.binding.id,r.binding.targetNodeId),this.onStateChanged(this.service.applyProxyTargetSnapshot(e,r)),"revoked"===r.binding.status)return void this.onStateChanged(this.service.markProxyBindingRevoked(e,{code:at.BindingRevoked,message:"Control-plane proxy binding was revoked.",retryable:!1}));this.openEvents(e,t,n,r)}catch(n){if(!this.current(e,t))return;this.logger?.warn?.({nodeId:e,error:jp(n)},"control-plane proxy state bootstrap failed"),this.fail(e,t,Op("Trusted control-plane proxy is unavailable."),!0)}else this.fail(e,t,Op("Control-plane proxy credential is unavailable."),!1)}openEvents(e,t,n,s){const i=this.current(e,t);if(!i)return;const r=this.openWebSocket(function(e,t){const n=new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/events`,e.proxyOrigin);return n.protocol="wss:",n.searchParams.set("sinceRevision",String(t)),n.toString()}(n,s.revision),Cr(n));i.socket=r;let a=!1;const o=n=>{a||(a=!0,this.fail(e,t,Op(n),!0))};r.on("message",i=>{if(this.current(e,t))try{const r=kt.parse(JSON.parse(String(i)));if(r.bindingId!==n.proxyBindingId||r.sourceControlPlaneId!==n.sourceControlPlaneId||r.targetNodeId!==n.targetNodeId||r.streamId!==s.streamId)throw new Error("Proxy event stream identity changed.");if("control-plane-proxy.snapshot-required"===r.type)return void this.restart(e,t);if("control-plane-proxy.events.ready"===r.type)return;this.onStateChanged(this.service.applyProxyTargetEvent(e,r))}catch(n){this.logger?.warn?.({nodeId:e,error:jp(n)},"control-plane proxy event requires a new snapshot"),this.restart(e,t)}}),r.on("error",()=>o("Control-plane proxy event stream failed.")),r.on("close",()=>o("Control-plane proxy event stream disconnected."))}restart(e,t){const n=this.current(e,t);n&&(n.generation+=1,n.socket?.close(),n.socket=void 0,this.connect(e,n.generation))}fail(e,t,n,s){const i=this.current(e,t);if(i){if(this.onStateChanged(this.service.markProxyUnavailable(e,n)),!s)return i.generation+=1,i.socket?.close(),void this.subscriptions.delete(e);i.generation+=1,i.socket?.close(),i.socket=void 0,i.reconnect=setTimeout(()=>{i.reconnect=void 0,this.connect(e,i.generation)},this.reconnectDelayMs),i.reconnect.unref?.()}}current(e,t){const n=this.subscriptions.get(e);return this.running&&n?.generation===t?n:void 0}validateIdentity(e,t,n){if(t!==e.proxyBindingId||n!==e.targetNodeId){const e=new Error("Proxy snapshot identity does not match the private binding credential.");throw Object.assign(e,{code:at.BindingIdentityConflict}),e}}subscriptionIdentity(e){const t=this.service.proxyPrivateStore.nodeCredential(e.id);return JSON.stringify({connectionMode:e.connectionMode,connectionPath:e.connectionPath,credential:t&&{id:t.id,proxyOrigin:t.proxyOrigin,proxyBindingId:t.proxyBindingId,targetNodeId:t.targetNodeId,sourceControlPlaneId:t.sourceControlPlaneId,bindingKeyId:t.bindingKeyId,updatedAt:t.updatedAt}})}}function jp(e){const t=e&&"object"==typeof e?e:{};return{name:e instanceof Error?e.name:"Error",message:e instanceof Error?e.message:String(e),..."string"==typeof t.code?{code:t.code}:{}}}const Dp=a.z.object({id:a.z.string().trim().min(1).max(160)}).strict(),Mp=a.z.object({}).strict(),$p=a.z.object({code:a.z.string().trim().min(1),message:a.z.string(),details:a.z.record(a.z.string(),a.z.unknown()).optional(),retryable:a.z.boolean().optional()}).strict(),qp=a.z.object({error:$p}).strict();function Lp(e){const t=process.env[e]?.trim();return t||void 0}const Bp=function(e,t=process.env,n=e){let s;return()=>{const i=t.TASK_HANDOFF_VERSION?.trim();return i||(s||=w(e,t,n),s)}}("@task-handoff/control-plane");function Up(){return{component:"control-plane",packageName:"@task-handoff/control-plane",packageVersion:Bp(),protocolVersion:zt,buildId:Lp("TASK_HANDOFF_BUILD_ID"),builtAt:Lp("TASK_HANDOFF_BUILT_AT"),gitCommit:Lp("TASK_HANDOFF_GIT_COMMIT"),imageRef:Lp("TASK_HANDOFF_IMAGE_REF"),imageDigest:Lp("TASK_HANDOFF_IMAGE_DIGEST")}}const Fp={".css":"text/css; charset=utf-8",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".map":"application/json; charset=utf-8",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"};function Hp(e,s){const i=t.resolve(e),r=t.resolve(i,s);if(r===i||r.startsWith(`${i}${t.sep}`))return n.existsSync(r)&&n.statSync(r).isFile()?r:void 0}function Wp(e,s,i){const r=Hp(s,i);return r?(e.header("cache-control","no-store"),e.type(Fp[t.extname(r).toLowerCase()]||"application/octet-stream"),e.send(n.createReadStream(r))):e.code(404).send({error:{code:"STATIC_FILE_NOT_FOUND",message:`${i} was not found in the Control Plane UI static directory.`}})}function Gp(e){if(e instanceof a.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:"CONTROL_PLANE_ERROR",message:e instanceof Error?e.message:String(e),...t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?{details:t.details}:{},..."boolean"==typeof t.retryable?{retryable:t.retryable}:{}}}async function Vp(e,t){if(!e.enabled())return{type:"system",reason:"auth-disabled"};const n=await e.userForSessionToken(t);return n?{type:"user",userId:n.id,role:n.role}:void 0}function Kp(e,t){const n=t.split("?")[0]||"/";if(!n.startsWith("/api/"))return;if(n.startsWith("/api/auth/")||"/api/health"===n||"/api/events"===n)return;const s=function(e){return"GET"===e||"HEAD"===e?"read":"POST"===e?"create":"PATCH"===e||"PUT"===e?"update":"DELETE"===e?"delete":"read"}(e);return n.startsWith("/api/control-plane/settings")?{action:"GET"===e?"read":"manage-settings",resource:{type:"control-plane-settings"}}:n.startsWith("/api/models")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"model"}}:n.startsWith("/api/chat-gateway/bridges")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"chat-bridge"}}:"/api/node-join/invites"===n?{action:"manage-node-auth",resource:{type:"node"}}:n.startsWith("/api/control-plane-proxy")?{action:"GET"===e?"read":"manage-node-auth",resource:{type:"node"}}:n.startsWith("/api/nodes")?/\/(runtimes|local-folders)(\/|$)/.test(n)?{action:s,resource:{type:"runtime"}}:{action:"GET"===e?"read":"manage-node-auth",resource:{type:"node"}}:n.startsWith("/api/controlled-instances")?n.includes("/ai-sessions/")?n.endsWith("/approval")?{action:"approve",resource:{type:"ai-session"}}:n.endsWith("/interrupt")?{action:"interrupt",resource:{type:"ai-session"}}:n.includes("/messages")||n.includes("/queue/")?{action:"send-message",resource:{type:"ai-session"}}:{action:"read",resource:{type:"ai-session"}}:n.includes("/start")?{action:"start",resource:{type:"instance"}}:n.includes("/stop")?{action:"stop",resource:{type:"instance"}}:n.includes("/restart")?{action:"restart",resource:{type:"instance"}}:/\/config-sync(?:\/|$)/.test(n)?{action:"update",resource:{type:"instance"}}:{action:s,resource:{type:"instance"}}:n.startsWith("/api/projects")?{action:s,resource:{type:"project"}}:n.startsWith("/api/images")||n.startsWith("/api/image-options")||n.startsWith("/api/market")?{action:s,resource:{type:"runtime"}}:n.startsWith("/api/triggers")?{action:s,resource:{type:"trigger"}}:{action:"read",resource:{type:"control-plane-settings"}}}async function Jp(e={}){const a=Pc(e.dataDir),o=s({logger:e.logger??!0}),d=new _o(a,{...e.service,logger:o.log}),c=new vi(a,e.auth),l=new yc,p=new Hl(l),u=new $l(a,{onChanged:e=>queueMicrotask(()=>l.publish(N,e,{topic:"ai.sessions",scope:{instanceId:e.instanceId,sessionId:e.sessionId}}))});u.init();const m=new Dl({bootstrap:()=>d.bootstrapAiSessionsFromInstances(),logger:o.log,recoverDelta:(e,t,n)=>d.recoverAiSessionDelta(e,t,n),recoverSnapshot:e=>d.recoverAiSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});m.onSnapshot(e=>u.reconcile(e.instanceId,e.aiSessions));const h=new Ul({bootstrap:()=>d.bootstrapAppSessionsFromInstances(),logger:o.log,recoverDelta:(e,t,n)=>d.recoverAppSessionDelta(e,t,n),recoverSnapshot:e=>d.recoverAppSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});d.setAppSessionSnapshotProvider(e=>h.list(e)),d.setAiSessionSnapshotProvider(e=>m.list(e)),l.on(e=>{if(p.handle(e),"instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";t&&(h.removeInstance(t),m.removeInstance(t),u.removeInstance(t))}});const y=new vc,f=new Ec(l,{validateInstanceScope:(e,t)=>d.nodeOwnsInstance(e,t),onStreamsHello:(e,t)=>{for(const n of t.streams)"ai.sessions"===n.topic&&m.advertiseStream(e,n),"app.sessions"===n.topic&&h.advertiseStream(e,n)},onSessionEvent:e=>e.type.startsWith("app-session.")?h.handleEvent(e):m.handleEvent(e)}),I=e.proxyOrigin||process.env.TASK_HANDOFF_CONTROL_PLANE_PROXY_ORIGIN,b=e=>{const t=d.nodes.get(e);return t?function(e,t){const n="reverse-wss"===e.connectionMode&&t,s=n?"online":e.status;return{id:e.id,name:e.name||e.id,status:s,health:e.health,...e.lastSeenAt?{lastSeenAt:e.lastSeenAt}:{},capabilities:hp(e.capabilities||{},262144),manageable:!1!==e.connectionEnabled&&(n||"online"===s)}}(t,f.connected(t.id)):void 0},v=new op(new tp(a.proxyAuthorityPath,(e,t)=>o.log.warn(t,e)),{get:b},{proxyOrigin:I,proxyOriginProvider:()=>d.getSettings().publicBaseUrl}),w=new cp,S=new pp(l,{projectTarget:e=>{const t=b(e);return t?fp(t):void 0}}),A=new Rp(d,{fetchImpl:e.service?.fetchImpl,logger:o.log,onStateChanged:e=>l.publish("node.proxy-state.updated",{nodeId:e.id,proxyState:e.proxyState},{topic:"node.state",scope:{nodeId:e.id}})});l.on(e=>{if("node.created"!==e.type&&"node.updated"!==e.type&&"node.deleted"!==e.type||A.syncNow(),"instance.created"===e.type||"instance.updated"===e.type||"instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";t&&f.invalidateInstanceScope({instanceId:t})}if("node.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"nodeId"in e.payload?String(e.payload.nodeId||""):"";if(t){f.invalidateInstanceScope({nodeId:t});const e=v.revokeTarget(t);for(const n of e.bindings)w.closeBinding(n.id,"Proxy target was deleted."),l.publish("control-plane-proxy.binding.updated",{binding:n},{topic:"control-plane-proxy",scope:{nodeId:t}})}}}),d.setNodeAgentTransport(f),d.init(),v.init(),A.start(),await d.syncLocalNodeConnection().catch(()=>{}),c.init();const k=new Md(d,e.service?.fetchImpl,{aiSessions:m,logger:o.log});k.startEnabled();const C=new _c(d,f,{safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0,logger:o.log});C.start(),await o.register(i),await o.register(r),o.addHook("onClose",async()=>{p.close(),S.stop(),A.stop(),C.stop(),k.stopAll()});const x=(T=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(T||"").trim().toLowerCase()));var T;o.setErrorHandler((e,t,n)=>{const s=Gp(e);(x||s.statusCode>=500)&&(s.statusCode>=500?o.log.error.bind(o.log):o.log.warn.bind(o.log))({method:t.method,url:t.url,statusCode:s.statusCode,errorCode:s.code,error:s.message},"control plane request failed"),n.code(s.statusCode).send(qp.parse({error:{code:s.code,message:s.message,...s.details?{details:s.details}:{},...void 0!==s.retryable?{retryable:s.retryable}:{}}}))}),o.addHook("preHandler",async(e,t)=>{if(function(e,t){const n=t.split("?")[0];return"GET"===e&&(n===lr||n.startsWith(`${lr}/`))||n.startsWith("/api/node-proxy/bindings/")}(e.method,e.url))return;if(!c.enabled()||function(e,t){const n=t.split("?")[0];return!("/api/health"!==n&&!n.startsWith("/api/auth/"))||"GET"===e&&"/install-node-agent.sh"===n||"POST"===e&&"/api/node-join/complete"===n||"POST"===e&&"/api/node-proxy/claims"===n||"/api/app-access/session"===n||!!n.startsWith("/apps/access/")||!("/favicon.ico"!==n&&!n.startsWith("/assets/"))||"GET"===e&&!n.startsWith("/api/")&&!n.startsWith("/instances/")}(e.method,e.url)){const t=await Vp(c,e.cookies[oi]),n=Kp(e.method,e.url);return void(n&&Yc(t||{type:"system",reason:"auth-disabled"},n.action,n.resource))}const n=await Vp(c,e.cookies[oi]);if(!n)return t.code(401).send({error:{code:"CONTROL_PLANE_AUTH_REQUIRED",message:"Sign in to access the Control Plane."}});const s=Kp(e.method,e.url);s&&Yc(n,s.action,s.resource)});const z=t.resolve(e.staticDir||process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(process.cwd(),"packages","control-plane-ui","dist"));return n.existsSync(z)&&(o.get("/assets/*",async(e,n)=>{const s=e.params["*"];return Wp(n,z,t.join("assets",s||""))}),o.get("/favicon.ico",async(e,t)=>Hp(z,"favicon.ico")?Wp(t,z,"favicon.ico"):t.code(204).send())),o.get("/api/health",async()=>({data:{ok:!0,role:"control-plane",protocolVersion:zt,build:Up(),dataDir:a.dataDir,serverTime:(new Date).toISOString()}})),o.get("/api/session-streams/diagnostics",async()=>({data:{aiSessions:m.diagnostics(),appSessions:h.diagnostics(),nodeConnections:C.diagnostics()}})),o.get("/install-node-agent.sh",async(e,t)=>(t.header("cache-control","no-store"),t.type("text/x-shellscript; charset=utf-8"),Fl`#!/bin/sh
|
|
1
|
+
"use strict";var e=require("commander"),t=require("node:path"),n=require("node:fs"),s=require("fastify"),i=require("@fastify/cookie"),r=require("@fastify/websocket"),o=require("zod"),a=require("node:crypto"),d=require("node:util"),c=require("write-file-atomic"),l=require("ws"),p=require("node:http"),u=require("node:net"),g=require("node:stream"),m=require("telegramify-markdown"),h=require("node:fs/promises"),f=require("node:os"),y=require("dingtalk-stream"),I=require("node:child_process");function b(e,t){try{const s=JSON.parse(n.readFileSync(e,"utf8"));if(t&&s.name!==t)return;return"string"==typeof s.version&&s.version.trim()?s.version.trim():void 0}catch{return}}function v(e){if(e)try{return t.dirname(n.realpathSync(e))}catch{return}}function w(e,n=process.env,s=e){const i=n.TASK_HANDOFF_VERSION?.trim();if(i)return i;const r=s.split("/").at(-1),o=process.argv[1]?t.resolve(process.argv[1]):void 0,a=o?t.dirname(o):void 0,d=[v(o),a,process.cwd()].filter(e=>Boolean(e));for(const n of new Set(d)){let s=n;for(;;){const n=b(t.join(s,"package.json"),e);if(n)return n;const i=t.dirname(s);if(i===s)break;s=i}}let c;for(const e of new Set(d)){let n=e;for(;;){if(r){const e=b(t.join(n,"packages",r,"package.json"),s);if(e)return e}c||=b(t.join(n,"package.json"));const e=t.dirname(n);if(e===n)break;n=e}}return c||"unknown"}const S=20971520,A=512e3,k=41943040,C="ai-session.snapshot",x="ai-session.patch",z="ai-session.removed",T="ai-session.message-delta",N="ai-session.unread.updated",E=o.z.string().trim().min(1).max(80),_=o.z.enum(["running","waiting","idle","failed"]),P=o.z.object({instanceId:o.z.string().trim().min(1).max(160),sessionId:o.z.string().trim().min(1).max(120),unread:o.z.boolean(),sessionUpdatedAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),O=o.z.enum(["thinking","tool","editing","approval","responding","unknown"]),R=o.z.object({id:o.z.string().trim().min(1).max(240).optional(),kind:o.z.string().trim().min(1).max(80).optional(),name:o.z.string().trim().min(1).max(120),inputPreview:o.z.string().trim().max(500).optional(),startedAt:o.z.string().datetime().optional()}).strict(),j=o.z.enum(["pending-init","running","interrupted","completed","errored","shutdown","not-found"]),D=o.z.enum(["started","interacted","interrupted"]),M=o.z.object({threadId:o.z.string().trim().min(1).max(240),path:o.z.string().trim().max(4096).optional(),status:j,activity:D.optional(),message:o.z.string().trim().max(1e3).optional(),updatedAt:o.z.string().datetime()}).strict(),q=o.z.object({toolCalls:o.z.number().int().min(0).default(0),edits:o.z.number().int().min(0).default(0),approvals:o.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),$=o.z.object({send:o.z.boolean().optional(),interrupt:o.z.boolean().optional(),approval:o.z.boolean().optional()}).strict(),L=o.z.enum(["image","file"]),B=o.z.object({id:o.z.string().trim().min(1).max(120),kind:L,name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),size:o.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)}),U=o.z.union([B.extend({source:o.z.object({type:o.z.literal("inline"),encoding:o.z.literal("base64"),data:o.z.string().min(1).max(31457280)}).strict()}).strict().superRefine((e,t)=>{const n="image"===e.kind?S:A;(e.size<=0||("file"===e.kind?e.size>=n:e.size>n))&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["size"],message:`Inline ${e.kind} must be between 1 byte and ${n} bytes.`})}),B.extend({source:o.z.object({type:o.z.literal("runtime-path"),path:o.z.string().trim().min(1).max(4096)}).strict()}).strict()]),F=B.extend({sourceType:o.z.enum(["inline","runtime-path"])}).strict(),H=o.z.union([o.z.object({id:o.z.string().trim().min(1).max(120),kind:L.optional(),source:o.z.object({type:o.z.literal("upload-ref")}).strict().default({type:"upload-ref"})}).strict(),o.z.object({id:o.z.string().trim().min(1).max(120),kind:L,name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),size:o.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),source:o.z.object({type:o.z.literal("runtime-path"),path:o.z.string().trim().min(1).max(4096)}).strict()}).strict()]),W=o.z.enum(["auto","queue","steer","immediate"]),G=o.z.enum(["ask","auto-review","full-access"]),V=o.z.enum(["review","rename","goal","compact"]),K=o.z.object({command:V,argument:o.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.`})}),J=o.z.object({command:V,value:o.z.string().max(4e3).optional(),turnId:o.z.string().trim().min(1).max(240).optional()}).strict();o.z.enum(["skill","app","plugin"]);const Q=o.z.object({name:o.z.string().trim().min(1).max(240),path:o.z.string().trim().min(1).max(4096)}),Y=o.z.discriminatedUnion("kind",[Q.extend({kind:o.z.literal("skill"),path:o.z.string().trim().min(1).max(4096).refine(e=>e.startsWith("/"),"Skill paths must be absolute.")}).strict(),Q.extend({kind:o.z.literal("app"),path:o.z.string().trim().min(7).max(4096).regex(/^app:\/\/[^\s/]+$/,"App paths must use app://<id>.")}).strict(),Q.extend({kind:o.z.literal("plugin"),path:o.z.string().trim().min(10).max(4096).regex(/^plugin:\/\/[^\s/]+$/,"Plugin paths must use plugin://<id>.")}).strict()]),X=o.z.array(Y).max(20).default([]),Z=o.z.enum(["plugin","skill","file","directory","app"]),ee=o.z.object({kind:Z,name:o.z.string().trim().min(1).max(240),description:o.z.string().trim().max(1e3).optional(),path:o.z.string().trim().min(1).max(4096),icon:o.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."})}),te=o.z.object({category:o.z.enum(["skills","plugins","apps","files"]),code:o.z.string().trim().min(1).max(160),message:o.z.string().trim().min(1).max(2e3)}).strict(),ne=o.z.object({sessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),cwd:o.z.string().trim().min(1).max(4096),candidates:o.z.array(ee).max(1e3).default([]),diagnostics:o.z.array(te).max(20).default([])}).strict(),se=o.z.object({query:o.z.string().trim().max(240).default("")}).strict(),ie=o.z.object({sessionId:o.z.string().trim().min(1).max(120),cwd:o.z.string().trim().min(1).max(4096),query:o.z.string().trim().max(240),requestId:o.z.string().trim().min(1).max(160),candidates:o.z.array(ee.refine(e=>"file"===e.kind||"directory"===e.kind,"File search only returns files and directories.")).max(200).default([]),complete:o.z.boolean().default(!1)}).strict(),re=o.z.object({message:o.z.string().trim().min(1).max(2e4),mode:W.optional(),permissionMode:G.optional()});re.extend({attachments:o.z.array(U).max(6).default([]),references:X}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>k&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})});const oe=re.extend({attachments:o.z.array(H).max(6).default([]),references:X}).strict(),ae=o.z.object({decision:o.z.enum(["allow","deny","skip"])}).strict();o.z.object({queueIds:o.z.array(o.z.string().trim().min(1).max(120)).max(100)}).strict(),o.z.object({code:o.z.string().trim().min(1).max(160),message:o.z.string(),instanceId:o.z.string().trim().min(1).max(160).optional(),nodeId:o.z.string().trim().min(1).max(160).optional(),route:o.z.string().trim().min(1).max(4096).optional()}).strict();const de=o.z.object({id:o.z.string().trim().min(1).max(120),message:o.z.string().trim().min(1).max(2e4),attachments:o.z.array(F).max(6).default([]),references:X,permissionMode:G.optional(),status:o.z.enum(["queued","sending","failed"]).default("queued"),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime(),error:o.z.string().trim().max(4e3).optional()}).strict(),ce=o.z.object({pendingCount:o.z.number().int().min(0).default(0),items:o.z.array(de).max(100).default([])}).strict().default({pendingCount:0,items:[]}),le=o.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),pe=o.z.object({id:o.z.string().trim().min(1).max(240),status:o.z.enum(["running","completed"]),startedAt:o.z.string().datetime().optional(),completedAt:o.z.string().datetime().optional()}).strict(),ue=o.z.object({id:o.z.string().trim().min(1).max(240),providerTurnId:o.z.string().trim().max(240).optional(),source:le.optional(),userPrompt:o.z.string().trim().optional(),status:o.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:O.optional(),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),contextCompactions:o.z.array(pe).max(20).optional(),revision:o.z.number().int().min(0).default(0),sourcePriority:o.z.number().int().min(0).max(100).optional(),snapshotVersion:o.z.number().int().min(0).optional(),observedAt:o.z.string().datetime().optional(),startedAt:o.z.string().datetime().optional(),updatedAt:o.z.string().datetime().optional(),completedAt:o.z.string().datetime().optional()}).strict(),ge=ue.pick({id:!0,providerTurnId:!0,userPrompt:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,contextCompactions:!0,startedAt:!0,updatedAt:!0,completedAt:!0}).strict(),me=o.z.object({id:o.z.string().trim().min(1).max(120),agent:E,appSessionId:o.z.string().trim().max(120).optional(),appId:o.z.string().trim().max(120).optional(),providerSessionId:o.z.string().trim().max(240).optional(),providerMeta:o.z.record(o.z.string(),o.z.unknown()).optional(),appBindingKeys:o.z.array(o.z.string().trim().min(1).max(240)).max(20).optional(),actions:$.optional(),activeTurnId:o.z.string().trim().max(240).optional(),title:o.z.string().trim().max(240).optional(),cwd:o.z.string().trim().max(4096).optional(),userPrompt:o.z.string().trim().optional(),turns:o.z.array(ue).max(50).optional(),status:_.default("running"),phase:O.default("unknown"),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),currentTool:R.optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).default(0),subAgents:o.z.array(M).max(50).default([]),transcriptPath:o.z.string().trim().max(4096).optional(),transcriptSize:o.z.number().int().min(0).optional(),startedAt:o.z.string().datetime(),updatedAt:o.z.string().datetime(),completedAt:o.z.string().datetime().optional(),error:o.z.string().trim().max(4e3).optional(),counters:q,queue:ce}).strict(),he=o.z.object({session:me,provider:o.z.string().trim().min(1).max(80),action:o.z.enum(["send","queue","steer","interrupt","approval"]),decision:o.z.enum(["allow","deny","skip"]).optional(),turnId:o.z.string().trim().min(1).max(240).optional(),providerTurnId:o.z.string().trim().min(1).max(240).optional(),queueId:o.z.string().trim().min(1).max(120).optional()}).strict(),fe=o.z.object({id:o.z.string().trim().min(1).max(120),agent:o.z.enum(["codex","claude"]),providerSessionId:o.z.string().trim().min(1).max(240),title:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),lastMessage:o.z.string().trim().optional(),cwd:o.z.string().trim().min(1).max(4096),lastActiveAt:o.z.string().datetime(),archivedAt:o.z.string().datetime()}).strict();o.z.object({schemaVersion:o.z.literal(1).default(1),items:o.z.array(fe).max(50).default([])}).strict();const ye=o.z.object({items:o.z.array(fe).max(50).default([])}).strict(),Ie=o.z.object({item:fe,turns:o.z.array(ge).max(50).default([])}).strict(),be=o.z.object({disposition:o.z.enum(["resumed","already-open"]),aiSessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),appSessionId:o.z.string().trim().min(1).max(120)}).strict(),ve=me.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}),we=o.z.object({runningCount:o.z.number().int().min(0).default(0),waitingCount:o.z.number().int().min(0).default(0),staleCount:o.z.number().int().min(0).default(0),sessions:o.z.array(ve).default([]),updatedAt:o.z.string().datetime()}).strict(),Se=o.z.object({streamId:o.z.string().trim().min(1).max(240),revision:o.z.number().int().min(0),lastEventAt:o.z.string().datetime(),snapshot:we}).strict(),Ae=we.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),revision:o.z.number().int().min(0).optional()});function ke(e,t={}){const n=e.sessions.filter(e=>"idle"===e.status).length,s=e.sessions.filter(e=>"failed"===e.status).length;return Ae.parse({runningCount:e.runningCount,waitingCount:e.waitingCount,staleCount:e.staleCount,idleCount:n,problemCount:s,updatedAt:e.updatedAt,revision:t.revision})}const Ce=o.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),xe=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),revision:o.z.number().int().min(0),previousRevision:o.z.number().int().min(0).optional(),traceId:o.z.string().trim().min(1).max(160),generatedAt:o.z.string().datetime(),reason:Ce}).strict(),ze=o.z.object({meta:xe,snapshot:we}).strict(),Te=o.z.object({meta:xe,upserted:o.z.array(ve).default([]),removed:o.z.array(o.z.string().trim().min(1).max(120)).default([])}).strict(),Ne=o.z.object({meta:xe,sessionIds:o.z.array(o.z.string().trim().min(1).max(120)).default([]),expiresAt:o.z.string().datetime()}).strict(),Ee=o.z.object({instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),sessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().min(1).max(240),turnId:o.z.string().trim().min(1).max(240),itemId:o.z.string().trim().min(1).max(240),delta:o.z.string().min(1),generatedAt:o.z.string().datetime()}).strict(),_e=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),sinceRevision:o.z.number().int().min(0),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0),syncRequired:o.z.boolean().default(!1),events:o.z.array(o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal(C),payload:ze}).strict(),o.z.object({type:o.z.literal(x),payload:Te}).strict(),o.z.object({type:o.z.literal(z),payload:Ne}).strict()])).default([])}).strict(),Pe=o.z.object({source:le,sourcePriority:o.z.number().int().min(0).max(100).optional(),observedAt:o.z.string().datetime().optional(),snapshotVersion:o.z.number().int().min(0).optional()}).strict(),Oe=Pe.extend({type:o.z.literal("snapshot"),agent:o.z.string().trim().min(1).max(80),appSessionId:o.z.string().trim().max(120).optional(),appId:o.z.string().trim().max(120).optional(),providerSessionId:o.z.string().trim().max(240).optional(),providerMeta:o.z.record(o.z.string(),o.z.unknown()).optional(),appBindingKeys:o.z.array(o.z.string().trim().min(1).max(240)).max(20).optional(),actions:$.optional(),title:o.z.string().trim().max(240).optional(),cwd:o.z.string().trim().max(4096).optional(),activeTurnId:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),turns:o.z.array(ue).max(50).optional(),status:_.optional(),phase:O.optional(),summary:o.z.string().trim().max(1e3).optional(),lastMessage:o.z.string().trim().optional(),lastMessageItemId:o.z.string().trim().max(240).optional(),currentTool:R.optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).optional(),subAgents:o.z.array(M).max(50).optional(),transcriptPath:o.z.string().trim().max(4096).optional(),transcriptSize:o.z.number().int().min(0).optional(),replaceActivity:o.z.boolean().optional()}).strict(),Re=Pe.extend({type:o.z.literal("event"),sessionId:o.z.string().trim().min(1).max(120),kind:o.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed","tool-activity","sub-agent-activity","context-compaction"]),activeTurnId:o.z.string().trim().max(240).optional(),providerTurnId:o.z.string().trim().max(240).optional(),userPrompt:o.z.string().trim().optional(),text:o.z.string().optional(),itemId:o.z.string().trim().max(240).optional(),status:_.optional(),phase:O.optional(),summary:o.z.string().trim().max(1e3).optional(),error:o.z.string().trim().max(4e3).optional(),currentTool:R.nullable().optional(),toolCallsSinceLastMessage:o.z.number().int().min(0).optional(),subAgents:o.z.array(M).max(50).optional(),contextCompaction:pe.optional(),counters:o.z.object({toolCalls:o.z.number().int().min(0).optional(),edits:o.z.number().int().min(0).optional(),approvals:o.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"})});o.z.discriminatedUnion("type",[Oe,Re]);const je=o.z.union([o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("interval").default("interval"),intervalMs:o.z.number().int().min(1e3)}).strict(),o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("daily"),timeOfDay:o.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("schedule"),scheduleKind:o.z.literal("weekly"),weekdays:o.z.array(o.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:o.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:o.z.string().trim().min(1).max(120)}).strict()]),De=o.z.union([je,o.z.object({type:o.z.literal("file-change"),roots:o.z.array(o.z.string().trim().min(1).max(4096)).min(1).max(50),globs:o.z.array(o.z.string().trim().min(1).max(500)).min(1).max(100),ignore:o.z.array(o.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:o.z.number().int().min(100).max(6e4).default(1500)}).strict(),o.z.object({type:o.z.literal("ai-session"),agent:o.z.string().trim().min(1).max(80).optional(),statuses:o.z.array(o.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:o.z.array(o.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),Me=o.z.object({promptTemplate:o.z.string().trim().min(1).max(2e4)}).strict(),qe=o.z.object({cooldownMs:o.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:o.z.number().int().min(1).max(20).default(1),whenBusy:o.z.enum(["skip","queue"]).default("skip")}).strict(),$e=o.z.object({configHash:o.z.string().trim().min(8).max(80),name:o.z.string().trim().min(1).max(160),description:o.z.string().trim().max(1e3).optional(),source:De,action:Me,policy:qe.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),Le=o.z.object({type:o.z.literal("ai-session"),aiSessionId:o.z.string().trim().min(1).max(160)}).strict(),Be=o.z.object({configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),origin:o.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:o.z.boolean().default(!0),target:Le,localName:o.z.string().trim().min(1).max(160).optional(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),Ue=o.z.object({configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),status:o.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:o.z.string().datetime().optional(),lastCompletedAt:o.z.string().datetime().optional(),lastSkippedAt:o.z.string().datetime().optional(),lastError:o.z.string().trim().max(4e3).optional(),runCount:o.z.number().int().min(0).default(0),skippedCount:o.z.number().int().min(0).default(0)}).strict(),Fe=o.z.object({id:o.z.string().trim().min(1).max(160),configHash:o.z.string().trim().min(8).max(80),deploymentId:o.z.string().trim().min(1).max(240).optional(),instanceId:o.z.string().trim().min(1).max(160),eventType:o.z.enum(["manual","schedule","file-change","ai-session"]),status:o.z.enum(["started","completed","failed","skipped"]),target:Le,promptPreview:o.z.string().trim().max(500),eventSummary:o.z.string().trim().max(1e3).optional(),error:o.z.string().trim().max(4e3).optional(),startedAt:o.z.string().datetime(),completedAt:o.z.string().datetime().optional()}).strict(),He=o.z.object({schemaVersion:o.z.literal(1).default(1),configs:o.z.array($e).default([]),deployments:o.z.array(Be).default([]),runtime:o.z.array(Ue).default([]),recentRuns:o.z.array(Fe).default([])}).strict();function We(e){return Array.isArray(e)?e.map(We):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,We(t)])):e}const Ge="2026-08-01",Ve=o.z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"Proxy protocol version must use YYYY-MM-DD format."),Ke=o.z.string().trim().min(1).max(160).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),Je=o.z.string().datetime(),Qe=o.z.string().trim().min(32).max(4096),Ye=Ke,Xe=o.z.string().trim().max(2048).transform((e,t)=>{try{return function(e){const t=new URL(e.trim());if("https:"!==t.protocol||t.username||t.password||"/"!==t.pathname||t.search||t.hash)throw new Error("Proxy origin must be an HTTPS origin without credentials, path, query, or fragment.");return t.origin}(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),o.z.NEVER}}),Ze=o.z.string().trim().min(1).max(4096).transform((e,t)=>{try{return function(e){const t=e.trim();if(!t.startsWith("/")||t.startsWith("//")||t.includes("\\")||/%25/i.test(t))throw new Error("Proxy route must be a single-encoded node-agent relative route.");const n=t.split("?",1)[0];let s,i;try{s=n.split("/").map(e=>decodeURIComponent(e))}catch{throw new Error("Proxy route contains invalid percent encoding.")}if(s.some(e=>".."===e||"."===e))throw new Error("Proxy route cannot traverse outside the node-agent API.");try{i=new URL(t,"https://node-agent.invalid")}catch{throw new Error("Proxy route is invalid.")}if("https://node-agent.invalid"!==i.origin||i.username||i.password)throw new Error("Proxy route cannot select an absolute upstream URL.");return`${i.pathname}${i.search}`}(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),o.z.NEVER}}),et=new Set(["accept","accept-language","authorization","content-type","if-match","if-none-match","range","x-request-id"]),tt=new Set(["cache-control","content-disposition","content-language","content-length","content-range","content-type","etag","last-modified","x-request-id"]),nt="x-task-handoff-proxy-protocol-version",st="x-task-handoff-proxy-source-control-plane-id",it="x-task-handoff-proxy-binding-key-id",rt="x-task-handoff-proxy-credential",ot={ProtocolUnsupported:"CONTROL_PLANE_PROXY_PROTOCOL_UNSUPPORTED",InviteInvalid:"CONTROL_PLANE_PROXY_INVITE_INVALID",InviteExpired:"CONTROL_PLANE_PROXY_INVITE_EXPIRED",InviteConsumed:"CONTROL_PLANE_PROXY_INVITE_CONSUMED",InviteRevoked:"CONTROL_PLANE_PROXY_INVITE_REVOKED",TargetUnavailable:"CONTROL_PLANE_PROXY_TARGET_UNAVAILABLE",TargetMismatch:"CONTROL_PLANE_PROXY_TARGET_MISMATCH",BindingUnknown:"CONTROL_PLANE_PROXY_BINDING_UNKNOWN",BindingRevoked:"CONTROL_PLANE_PROXY_BINDING_REVOKED",BindingIdentityConflict:"CONTROL_PLANE_PROXY_BINDING_IDENTITY_CONFLICT",AuthenticationFailed:"CONTROL_PLANE_PROXY_AUTHENTICATION_FAILED",RouteInvalid:"CONTROL_PLANE_PROXY_ROUTE_INVALID",HeaderInvalid:"CONTROL_PLANE_PROXY_HEADER_INVALID",ResourceLimit:"CONTROL_PLANE_PROXY_RESOURCE_LIMIT",SnapshotRequired:"CONTROL_PLANE_PROXY_SNAPSHOT_REQUIRED",Unavailable:"CONTROL_PLANE_PROXY_UNAVAILABLE",TransportFailed:"CONTROL_PLANE_PROXY_TRANSPORT_FAILED"},at=o.z.enum(Object.values(ot)),dt=o.z.object({code:at,message:o.z.string().trim().min(1).max(2048),retryable:o.z.boolean(),details:o.z.record(o.z.string(),o.z.unknown()).optional()}).strict(),ct=o.z.enum(["active","consumed","revoked","expired"]),lt=o.z.object({id:Ke,targetNodeId:Ke,tokenHash:o.z.string().trim().min(32).max(256),status:ct,createdBy:Ke,expiresAt:Je,consumedByClaimId:Ke.optional(),consumedAt:Je.optional(),revokedAt:Je.optional(),createdAt:Je,updatedAt:Je}).strict(),pt=lt.omit({tokenHash:!0}),ut=o.z.object({targetNodeId:Ke,expiresInSeconds:o.z.number().int().min(60).max(3600).default(600)}).strict(),gt=o.z.object({invite:pt,token:o.z.string().trim().min(24).max(512),proxyOrigin:Xe,protocolVersion:Ve}).strict(),mt=o.z.enum(["active","revoked"]),ht=o.z.object({id:Ke,claimId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,bindingKeyId:Ke,credentialHash:o.z.string().trim().min(32).max(256),status:mt,revision:o.z.number().int().positive(),lastError:dt.optional(),revokedAt:Je.optional(),createdAt:Je,updatedAt:Je}).strict(),ft=ht.omit({credentialHash:!0}),yt=o.z.object({protocolVersion:Ve,inviteToken:o.z.string().trim().min(24).max(512).optional(),claimId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke.optional(),bindingKeyId:Ke,credential:Qe}).strict(),It=o.z.object({sourceControlPlaneId:Ke,bindingKeyId:Ke,credential:Qe}).strict(),bt=o.z.object({id:Ke,name:o.z.string().trim().min(1).max(160),status:o.z.enum(["unknown","online","offline","degraded"]),health:o.z.enum(["unknown","ok","degraded","failed"]),lastSeenAt:Je.optional(),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({})}).strict(),vt=o.z.object({protocolVersion:Ve,binding:ft,streamId:Ke,revision:o.z.number().int().nonnegative(),observedAt:Je,target:bt}).strict(),wt=o.z.object({type:o.z.literal("control-plane-proxy.event"),protocolVersion:Ve,streamId:Ke,bindingId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,revision:o.z.number().int().positive(),source:o.z.object({id:o.z.string().trim().min(1).max(512),seq:o.z.number().int().nonnegative()}).strict(),target:bt,event:o.z.object({type:o.z.string().trim().min(1).max(240),topic:o.z.string().trim().min(1).max(240),createdAt:Je}).strict()}).strict(),St=o.z.object({type:o.z.literal("control-plane-proxy.events.ready"),protocolVersion:Ve,streamId:Ke,bindingId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,latestRevision:o.z.number().int().nonnegative(),earliestRetainedRevision:o.z.number().int().nonnegative()}).strict(),At=o.z.object({type:o.z.literal("control-plane-proxy.snapshot-required"),protocolVersion:Ve,streamId:Ke,bindingId:Ke,sourceControlPlaneId:Ke,targetNodeId:Ke,sinceRevision:o.z.number().int().nonnegative(),latestRevision:o.z.number().int().nonnegative(),earliestRetainedRevision:o.z.number().int().nonnegative(),error:dt}).strict(),kt=o.z.discriminatedUnion("type",[wt,St,At]),Ct=o.z.object({protocolVersion:Ve,binding:ft,target:vt.shape.target}).strict(),xt=o.z.object({id:Ke,claimId:Ke,proxyOrigin:Xe,requestedName:o.z.string().trim().min(1).max(160).optional(),sourceControlPlaneId:Ke,targetNodeId:Ke.optional(),bindingKeyId:Ke,credential:Qe,status:o.z.enum(["pending","compensation-required"]),createdAt:Je,updatedAt:Je,expiresAt:Je}).strict();xt.omit({credential:!0});const zt=o.z.object({id:Ke,nodeId:Ke,proxyOrigin:Xe,proxyBindingId:Ke,targetNodeId:Ke,sourceControlPlaneId:Ke,bindingKeyId:Ke,credential:Qe,createdAt:Je,updatedAt:Je}).strict(),Tt="2026-08-01",Nt=o.z.string(),Et=o.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),_t=o.z.string().datetime(),Pt=o.z.record(o.z.string(),o.z.string()).default({}),Ot=o.z.record(o.z.string(),o.z.string()).default({}),Rt=o.z.string().regex(/^\d{4}-\d{2}-\d{2}$/,"Protocol version must use YYYY-MM-DD format.");function jt(e){if(null!=e){if("string"==typeof e)return{encoding:"utf8",data:e};if(Buffer.isBuffer(e))return{encoding:"base64",data:e.toString("base64")};if(e instanceof ArrayBuffer)return{encoding:"base64",data:Buffer.from(e).toString("base64")};if(ArrayBuffer.isView(e))return{encoding:"base64",data:Buffer.from(e.buffer,e.byteOffset,e.byteLength).toString("base64")};throw new TypeError("Node tunnel request body must be a string or binary buffer.")}}o.z.object({encoding:o.z.enum(["utf8","base64"]),data:o.z.string()}).strict().superRefine((e,t)=>{"base64"===e.encoding&&Buffer.from(e.data,"base64").toString("base64")!==e.data&&t.addIssue({code:"custom",path:["data"],message:"Node tunnel binary body must use canonical base64 encoding."})});const Dt=/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/,Mt=/^sha256:([a-fA-F0-9]{64})$/,qt=/^(?:localhost|[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::[1-9][0-9]{0,4})?$/,$t=/^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;function Lt(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 s=-1===n?void 0:t.slice(n+1),i=-1===n?t:t.slice(0,n),r=s?Mt.exec(s):void 0;if(s&&!r)throw new Error("Docker image digest must use sha256 followed by 64 hexadecimal characters.");const o=i.lastIndexOf("/"),a=i.lastIndexOf(":"),d=a>o,c=d?i.slice(a+1):void 0,l=d?i.slice(0,a):i;if(c&&!Dt.test(c))throw new Error("Docker image tag is invalid.");if(!c&&!r)throw new Error("Docker image reference must include an explicit tag or sha256 digest.");const p=l.split("/");if(!p.length||p.some(e=>!e))throw new Error("Docker image repository path is invalid.");const u=p.length>1&&(p[0].includes(".")||p[0].includes(":")||"localhost"===p[0]),g=u?p.slice(1):p;if(u&&!qt.test(p[0].toLowerCase()))throw new Error("Docker image registry host or port is invalid.");if(!g.length||g.some(e=>!$t.test(e)))throw new Error("Docker image repository names must be lowercase and use valid separators.");const m=p.map((e,t)=>u&&0===t?e.toLowerCase():e).join("/"),h=r?`sha256:${r[1].toLowerCase()}`:void 0;return`${m}${c?`:${c}`:""}${h?`@${h}`:""}`}function Bt(e){const t=Lt(e),n=t.indexOf("@"),s=-1===n?void 0:t.slice(n+1),i=-1===n?t:t.slice(0,n),r=i.lastIndexOf("/"),o=i.lastIndexOf(":"),a=o>r;return{reference:t,repository:a?i.slice(0,o):i,tag:a?i.slice(o+1):void 0,digest:s}}const Ut=o.z.string().trim().min(1).max(512).transform((e,t)=>{try{return Lt(e)}catch(e){return t.addIssue({code:"custom",message:e instanceof Error?e.message:String(e)}),o.z.NEVER}}),Ft=o.z.string().trim().transform((e,t)=>{const n=Mt.exec(e);return n?`sha256:${n[1].toLowerCase()}`:(t.addIssue({code:"custom",message:"Docker image digest must use sha256 followed by 64 hexadecimal characters."}),o.z.NEVER)}),Ht=o.z.object({codexModelHash:Et.nullable().optional(),claudeModelHash:Et.nullable().optional()}).strict().default({}),Wt=o.z.object({imageId:Et,tag:o.z.string().trim().min(1).max(128).regex(Dt).optional()}).strict(),Gt={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"};function Vt(e,t){if(e&&"object"==typeof e&&!Array.isArray(e)){const t=e;return{imageId:"string"==typeof t.imageId&&Gt[t.imageId]||t.imageId,..."string"==typeof t.tag?{tag:t.tag}:{}}}if("string"==typeof t)return{imageId:Gt[t]||t}}function Kt(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,{defaultImageId:n,...s}=t;return{...s,defaultImageSelection:Vt(t.defaultImageSelection,n)}}const Jt=o.z.object({id:Et,name:o.z.string().trim().min(1).max(120),kind:o.z.enum(["tty","gui","web"]),source:o.z.enum(["builtin","custom"]),availability:o.z.enum(["available","missing-dependency"]),capabilities:o.z.object({automation:o.z.enum(["cdp"]).optional(),supportsCwdSelection:o.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:o.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),Qt=o.z.object({code:o.z.enum(["APP_CATALOG_INVALID"]),message:o.z.string().trim().min(1).max(500)}).strict(),Yt=o.z.object({items:o.z.array(Jt).max(256),observedAt:_t,issues:o.z.array(Qt).max(32).default([])}).strict(),Xt=o.z.enum(["linux","darwin","win32","freebsd","openbsd","aix","sunos","unknown"]),Zt=o.z.enum(["x64","arm64","arm","ia32","ppc64","s390x","riscv64","unknown"]),en=o.z.enum(["apt","dnf","brew","npm"]),tn=o.z.enum(["user","passwordless-sudo","root"]),nn=o.z.object({platform:Xt,arch:Zt,installers:o.z.array(en).max(8),privilege:tn,installerAccess:o.z.object({npmGlobalWritable:o.z.boolean().optional()}).strict().optional()}).strict(),sn=o.z.enum(["installed","not-installed","broken","unsupported"]),rn=o.z.enum(["recipe","bundled","external","none"]),on=o.z.object({code:o.z.enum(["BUNDLED","EXTERNALLY_MANAGED","ALREADY_INSTALLED","NOT_INSTALLED","UNSUPPORTED_PLATFORM","INSTALLER_UNAVAILABLE","INSTALLER_NOT_WRITABLE","INSUFFICIENT_PRIVILEGE","OPERATION_IN_PROGRESS"]),message:o.z.string().trim().min(1).max(500)}).strict(),an=o.z.object({id:Et,name:o.z.string().trim().min(1).max(120),kind:o.z.enum(["tty","gui","web"]),description:o.z.string().trim().max(500).optional(),state:sn,managementSource:rn,version:o.z.string().trim().max(120).optional(),canInstall:o.z.boolean(),canUninstall:o.z.boolean(),installReason:on.optional(),uninstallReason:on.optional(),activeJobId:Et.optional()}).strict(),dn=o.z.enum(["install","uninstall"]),cn=o.z.enum(["queued","running","succeeded","failed","cancelled","interrupted"]),ln=o.z.object({current:o.z.number().finite().nonnegative().optional(),total:o.z.number().finite().positive().optional(),unit:o.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"]}),pn=o.z.object({executable:o.z.string().trim().min(1).max(4096),args:o.z.array(o.z.string().max(4096)).max(128)}).strict(),un=o.z.object({code:o.z.string().trim().min(1).max(120).regex(/^[a-z0-9][a-z0-9._-]*$/),message:o.z.string().trim().min(1).max(4096),retryable:o.z.boolean(),sessionIds:o.z.array(Et).max(64).optional()}).strict(),gn=o.z.object({id:Et,requestId:Et.optional(),appId:Et,operation:dn,state:cn,phase:o.z.string().trim().min(1).max(120).optional(),progress:ln.optional(),command:pn.optional(),logTail:o.z.string().max(32768).optional(),logTruncated:o.z.boolean().optional(),error:un.optional(),requestedAt:_t,startedAt:_t.optional(),finishedAt:_t.optional(),updatedAt:_t}).strict(),mn=o.z.object({streamId:Et,sequence:o.z.number().int().nonnegative(),capabilities:nn,apps:o.z.array(an).max(256),activeJobs:o.z.array(gn).max(256),recentJobs:o.z.array(gn).max(256),observedAt:_t}).strict(),hn=o.z.object({requestId:Et.optional()}).strict(),fn=o.z.object({job:gn}).strict();o.z.object({type:o.z.literal("app-management"),streamId:Et,sequence:o.z.number().int().nonnegative(),observedAt:_t,job:gn.optional(),snapshot:mn.optional()}).strict().refine(e=>void 0!==e.job||void 0!==e.snapshot,{message:"An app management event requires a job or snapshot."});const yn=o.z.object({component:o.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:o.z.string().trim().max(160).optional(),packageVersion:o.z.string().trim().max(80).optional(),protocolVersion:Nt.optional(),buildId:o.z.string().trim().max(160).optional(),builtAt:o.z.string().trim().max(120).optional(),gitCommit:o.z.string().trim().max(120).optional(),imageRef:o.z.string().trim().max(512).optional(),imageDigest:o.z.string().trim().max(240).optional()}).strict(),In=o.z.enum(["stable","beta","alpha"]),bn=o.z.object({packageName:o.z.literal("@task-handoff/controlled-instance"),version:o.z.string().trim().min(1).max(80),platform:o.z.string().trim().min(1).max(40),arch:o.z.string().trim().min(1).max(40),formatVersion:o.z.number().int().positive(),launcherAbi:o.z.number().int().positive(),entrypoint:o.z.string().trim().min(1).max(512),sha256:o.z.string().regex(/^[a-f0-9]{64}$/)}).strict(),vn=o.z.object({code:o.z.enum(["INSTANCE_RUNTIME_VERSION_MISMATCH","INSTANCE_RUNTIME_ARTIFACT_UNAVAILABLE","INSTANCE_RUNTIME_ARTIFACT_INVALID","INSTANCE_RUNTIME_INSTALL_FAILED","INSTANCE_RUNTIME_RESTART_FAILED","INSTANCE_RUNTIME_VERIFICATION_FAILED","INSTANCE_BASE_RUNTIME_INCOMPATIBLE","NODE_UPDATE_PREFLIGHT_FAILED","NODE_UPDATE_FAILED","LEGACY_INSTANCE_UPDATE_RETIRED"]),message:o.z.string().trim().min(1).max(4096),expectedVersion:o.z.string().trim().min(1).max(80).optional(),actualVersion:o.z.string().trim().min(1).max(80).optional(),retryable:o.z.boolean().default(!1)}).strict(),wn=o.z.object({desiredVersion:o.z.string().trim().min(1).max(80),actualVersion:o.z.string().trim().min(1).max(80).optional(),phase:o.z.enum(["pending","draining","installing","restarting","verifying","matched","failed"]),attempt:o.z.number().int().nonnegative().default(0),lastAttemptAt:_t.optional(),matchedAt:_t.optional(),error:vn.optional()}).strict(),Sn=o.z.object({runningInstanceCount:o.z.number().int().nonnegative(),stoppedInstanceCount:o.z.number().int().nonnegative(),activeInstanceCount:o.z.number().int().nonnegative(),restartInstanceCount:o.z.number().int().nonnegative(),runningInstanceIds:o.z.array(Et).max(1024).default([]),stoppedInstanceIds:o.z.array(Et).max(1024).default([]),activeInstanceIds:o.z.array(Et).max(1024).default([])}).strict(),An=o.z.object({phase:o.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),desiredVersion:o.z.string().trim().min(1).max(80),nodeVersion:o.z.string().trim().min(1).max(80).optional(),expectedInstanceIds:o.z.array(Et).max(1024).default([]),expectedInstanceCount:o.z.number().int().nonnegative(),matchedInstanceCount:o.z.number().int().nonnegative(),pendingInstanceCount:o.z.number().int().nonnegative(),failedInstanceCount:o.z.number().int().nonnegative(),deferredInstanceCount:o.z.number().int().nonnegative().default(0)}).strict(),kn=o.z.object({channel:In.default("stable")}).strict(),Cn=o.z.object({source:o.z.literal("npm"),channel:In,currentVersion:o.z.string().trim().max(240).optional(),availableVersion:o.z.string().trim().min(1).max(240),artifactRef:o.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:o.z.array(bn).default([]),impact:Sn,updateAvailable:o.z.boolean(),supported:o.z.boolean().default(!0),reason:o.z.string().trim().max(2048).optional(),checkedAt:_t,preflightToken:o.z.string().trim().min(16).max(240).optional()}).strict(),xn=o.z.object({id:Et,nodeId:Et,source:o.z.literal("npm"),channel:In,fromVersion:o.z.string().trim().max(240).optional(),toVersion:o.z.string().trim().min(1).max(240),artifactRef:o.z.string().trim().min(1).max(512).optional(),runtimeArtifacts:o.z.array(bn).default([]),impact:Sn,rollout:An,status:o.z.enum(["queued","updating-node","restarting-node","converging-instances","succeeded","degraded","failed"]),error:vn.optional(),startedAt:_t.optional(),completedAt:_t.optional(),createdAt:_t,updatedAt:_t}).strict(),zn=o.z.object({channel:In,targetVersion:o.z.string().trim().min(1).max(240),preflightToken:o.z.string().trim().min(16).max(240)}).strict(),Tn=o.z.object({type:o.z.enum(["branch","tag","commit"]),name:o.z.string().trim().min(1).max(240).optional(),commit:o.z.string().trim().max(80).optional()}).strict(),Nn=o.z.object({type:o.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:o.z.string().trim().min(1).max(120).optional()}).strict(),En=o.z.object({depth:o.z.number().int().positive().max(1e5).optional(),submodules:o.z.boolean().default(!1),lfs:o.z.boolean().default(!1),subdirectory:o.z.string().trim().max(240).default("")}).strict();o.z.object({id:Et,name:o.z.string().trim().min(1).max(160),url:o.z.string().trim().min(1).max(2048),provider:o.z.string().trim().max(80).optional(),ref:Tn.default({type:"branch",name:"main"}),auth:Nn.default({type:"none"}),clone:En.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageSelection:Wt.optional(),labels:Pt,createdAt:_t,updatedAt:_t}).strict();const _n=o.z.object({id:Et,nodeId:Et,name:o.z.string().trim().min(1).max(160),path:o.z.string().trim().min(1).max(4096),defaultImageSelection:Wt.optional(),labels:Pt,createdAt:_t,updatedAt:_t}).strict(),Pn=o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("git-repository"),repositoryId:Et.optional(),url:o.z.string().trim().min(1).max(2048),provider:o.z.string().trim().max(80).optional(),ref:Tn.default({type:"branch",name:"main"}),auth:Nn.default({type:"none"}),clone:En.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),o.z.object({type:o.z.literal("git-template"),url:o.z.string().trim().min(1).max(2048),templateId:o.z.string().trim().min(1).max(120).optional(),ref:Tn.default({type:"branch",name:"main"}),auth:Nn.default({type:"none"}),clone:En.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),o.z.object({type:o.z.literal("local-folder"),localFolderId:Et.optional(),path:o.z.string().trim().min(1).max(4096),ownerNodeId:Et.optional()}).strict()]),On=o.z.object({mode:o.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:o.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:o.z.string().trim().max(240).optional(),readOnly:o.z.boolean().default(!1)}).strict(),Rn=o.z.enum(["codex","claude"]),jn=o.z.object({id:Et,name:o.z.string().trim().min(1).max(160),source:Pn,defaultImageSelection:Wt.optional(),defaultNodeId:Et.optional(),defaultRuntimeId:Et.optional(),workspacePolicy:On,labels:Pt,createdAt:_t,updatedAt:_t}).strict(),Dn=o.z.object({id:Et,name:o.z.string().trim().min(1).max(160),endpoint:o.z.string().trim().min(1).max(2048),key:o.z.string().trim().min(1).max(4096),model:o.z.string().trim().min(1).max(240),app:Rn,enabled:o.z.boolean().default(!0),order:o.z.number().int().min(0).max(1e6).default(0),labels:Pt,createdAt:_t,updatedAt:_t}).strict(),Mn=Dn.omit({key:!0}).extend({keyPreview:o.z.string().trim().min(1).max(160),keySet:o.z.boolean()}).strict(),qn=Mn.extend({referenceCount:o.z.number().int().min(0)}).strict(),$n=o.z.object({instanceId:Et,codexModelHash:Et.optional(),claudeModelHash:Et.optional(),updatedAt:_t}).strict(),Ln=o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("control-plane"),name:Dn.shape.name,enabled:Dn.shape.enabled,order:Dn.shape.order}).strict(),o.z.object({type:o.z.literal("node"),nodeId:Et,name:Dn.shape.name,enabled:Dn.shape.enabled,order:Dn.shape.order,referenceCount:o.z.number().int().min(0)}).strict()]),Bn=o.z.object({id:Et,model:Mn,locations:o.z.array(Ln).min(1),referenceCount:o.z.number().int().min(0).default(0)}).strict(),Un=o.z.object({nodeId:Et,code:o.z.string().trim().min(1).max(120),message:o.z.string().trim().min(1).max(2e3)}).strict(),Fn=o.z.object({models:o.z.array(Bn),nodeDiagnostics:o.z.array(Un).default([]),updatedAt:_t}).strict();function Hn(e){const t={app:Rn.parse(e.app),endpoint:Dn.shape.endpoint.parse(e.endpoint),key:Dn.shape.key.parse(e.key),model:Dn.shape.model.parse(e.model)};return`mdl_${a.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}Dn.omit({id:!0,createdAt:!0,updatedAt:!0}).strict().partial().strict(),o.z.object({modelSelection:Ht,codexModelHash:Et.optional(),claudeModelHash:Et.optional()}).strict();const Wn=o.z.enum(["market","custom"]),Gn=o.z.enum(["active","deprecated","yanked"]),Vn=o.z.discriminatedUnion("kind",[o.z.object({kind:o.z.literal("builtin"),key:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({kind:o.z.literal("remote"),url:o.z.string().trim().url().max(2048),mediaType:o.z.enum(["image/png","image/jpeg","image/webp"]).optional(),digest:Ft.optional()}).strict()]),Kn={kind:"builtin",key:"default-image-cover"},Jn=o.z.array(o.z.string().trim().min(1).max(80)).default([]),Qn=o.z.array(o.z.string().trim().min(1).max(120)).default([]),Yn=o.z.record(o.z.string().trim().min(2).max(35),o.z.string().trim().min(1).max(4096)),Xn=o.z.object({os:o.z.string().trim().min(1).max(40),architecture:o.z.string().trim().min(1).max(40),digest:Ft.optional(),downloadSizeBytes:o.z.number().int().positive().optional(),unpackedSizeBytes:o.z.number().int().positive().optional()}).strict(),Zn=o.z.object({name:o.z.string().trim().min(1).max(128).regex(Dt),version:o.z.string().trim().min(1).max(160).optional(),reference:Ut,manifestDigest:Ft.optional(),publishedAt:_t.optional(),platforms:o.z.array(Xn).default([]),status:Gn.default("active")}).strict(),es=o.z.object({id:Et,publisher:o.z.string().trim().min(1).max(160),slug:o.z.string().trim().min(1).max(160),name:o.z.string().trim().min(1).max(160),description:o.z.string().trim().min(1).max(4096),localizedDescriptions:Yn.optional(),cover:Vn.optional(),repository:o.z.string().trim().min(1).max(512),defaultTag:o.z.string().trim().min(1).max(128).regex(Dt),tags:o.z.array(Zn).min(1),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,status:Gn.default("active")}).strict().superRefine((e,t)=>{e.tags.some(t=>t.name===e.defaultTag)||t.addIssue({code:"custom",path:["defaultTag"],message:"Market defaultTag must identify one of the image tags."})}),ts=o.z.object({protocolVersion:Rt,catalogId:Et,revision:o.z.string().trim().min(1).max(240),source:o.z.enum(["embedded","remote","cache"]),generatedAt:_t,expiresAt:_t.optional(),items:o.z.array(es)}).strict(),ns=o.z.object({source:o.z.enum(["embedded","remote","cache"]),state:o.z.enum(["ready","refreshing","stale","failed"]),revision:o.z.string().trim().min(1).max(240),updatedAt:_t,error:o.z.string().trim().max(4096).optional()}).strict(),ss=o.z.object({id:Et,origin:o.z.literal("custom"),name:o.z.string().trim().min(1).max(160),description:o.z.string().trim().max(4096).optional(),localizedDescriptions:Yn.optional(),cover:Vn.optional(),reference:Ut,repository:o.z.string().trim().min(1).max(512),tag:o.z.string().trim().min(1).max(128).regex(Dt).optional(),pullPolicy:o.z.literal("if-not-present").default("if-not-present"),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,createdAt:_t,updatedAt:_t}).strict(),is=o.z.object({name:o.z.string().trim().min(1).max(128).regex(Dt),version:o.z.string().trim().min(1).max(160).optional(),reference:Ut,manifestDigest:Ft.optional(),status:Gn}).strict(),rs=o.z.object({id:Et,origin:Wn,name:o.z.string().trim().min(1).max(160),description:o.z.string().trim().max(4096).optional(),localizedDescriptions:Yn.optional(),cover:Vn,repository:o.z.string().trim().min(1).max(512),tag:o.z.string().trim().min(1).max(128).regex(Dt).optional(),availableTags:o.z.array(is),reference:Ut,digest:Ft.optional(),downloadSizeBytes:o.z.number().int().positive().optional(),unpackedSizeBytes:o.z.number().int().positive().optional(),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,readOnly:o.z.boolean(),lifecycleStatus:Gn.optional(),market:o.z.object({catalogId:Et,catalogRevision:o.z.string().trim().min(1).max(240),publisher:o.z.string().trim().min(1).max(160),version:o.z.string().trim().min(1).max(160).optional()}).strict().optional()}).strict(),os=o.z.object({id:Et,origin:Wn,name:o.z.string().trim().min(1).max(160),description:o.z.string().trim().max(4096).optional(),localizedDescriptions:Yn.optional(),cover:Vn.optional(),repository:o.z.string().trim().min(1).max(512),tag:o.z.string().trim().min(1).max(128).regex(Dt).optional(),requestedReference:Ut,resolvedDigest:Ft.optional(),resolvedReference:Ut.optional(),downloadSizeBytes:o.z.number().int().positive().optional(),pullPolicy:o.z.literal("if-not-present").default("if-not-present"),capabilities:Jn,optionalApps:Qn,defaultEnv:Ot,labels:Pt,market:o.z.object({catalogId:Et,catalogRevision:o.z.string().trim().min(1).max(240),publisher:o.z.string().trim().min(1).max(160),version:o.z.string().trim().min(1).max(160).optional()}).strict().optional(),createdAt:_t,updatedAt:_t}).strict(),as=o.z.object({phase:o.z.enum(["checking-image","pulling-image","resolving-image","ready","failed"]),requestedReference:Ut,generation:o.z.number().int().nonnegative().default(0),error:o.z.string().trim().max(4096).optional(),startedAt:_t,updatedAt:_t}).strict(),ds="image.pull.terminal.output",cs="image.pull.terminal.finished",ls=o.z.object({instanceId:Et,generation:o.z.number().int().nonnegative(),requestedReference:Ut,sequence:o.z.number().int().nonnegative(),observedAt:_t}).strict(),ps=ls.extend({data:o.z.string().min(1).max(65536),replay:o.z.boolean().optional()}).strict(),us=ls.extend({outcome:o.z.enum(["succeeded","failed"])}).strict(),gs=ls.extend({status:o.z.enum(["connecting","pulling","extracting","complete","failed"]),layers:o.z.object({total:o.z.number().int().nonnegative(),completed:o.z.number().int().nonnegative(),downloaded:o.z.number().int().nonnegative(),downloading:o.z.number().int().nonnegative(),extracting:o.z.number().int().nonnegative()}).strict(),bytes:o.z.object({current:o.z.number().finite().nonnegative(),total:o.z.number().finite().positive()}).strict().optional(),percent:o.z.number().finite().min(0).max(100).optional(),message:o.z.string().trim().min(1).max(500),terminalTail:o.z.string().max(262144).optional(),terminalTruncated:o.z.boolean().optional()}).strict();function ms(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=new Set(["id","origin","name","description","localizedDescriptions","cover","reference","repository","tag","image","registry","pullPolicy","capabilities","optionalApps","defaultEnv","labels","createdAt","updatedAt"]);for(const e of Object.keys(n))s.has(e)||t?.({imageId:"string"==typeof n.id?n.id:void 0,field:e});const i="string"==typeof n.id?n.id:void 0,r="string"==typeof n.image?n.image:void 0;void 0!==r&&t?.({imageId:i,field:"image"}),Object.prototype.hasOwnProperty.call(n,"registry")&&t?.({imageId:i,field:"registry"});let o,a="string"==typeof n.reference?n.reference:r;if(a)try{a=Lt(a)}catch{if(!a.includes("@")&&a.lastIndexOf(":")<=a.lastIndexOf("/"))try{a=Lt(`${a}:latest`),t?.({imageId:i,field:"reference:implicit-latest"})}catch{}}try{o=a?Bt(a):void 0}catch{}return{id:n.id,origin:"custom",name:n.name,description:n.description,localizedDescriptions:n.localizedDescriptions,cover:Bs(n.cover,["kind","key","url","mediaType","digest"]),reference:o?.reference??a,repository:o?.repository??n.repository,tag:o?.tag??n.tag,pullPolicy:n.pullPolicy??"if-not-present",capabilities:n.capabilities,optionalApps:n.optionalApps,defaultEnv:n.defaultEnv,labels:n.labels,createdAt:n.createdAt,updatedAt:n.updatedAt}}const hs=o.z.discriminatedUnion("kind",[o.z.object({kind:o.z.literal("direct")}).strict(),o.z.object({kind:o.z.literal("control-plane-proxy"),proxyId:Et,proxyBindingId:Et,targetNodeId:Et}).strict()]),fs=o.z.object({reachability:o.z.enum(["unknown","reachable","unreachable"]),bindingStatus:o.z.enum(["unknown","active","revoked"]),bindingRevision:o.z.number().int().positive().optional(),streamId:o.z.string().trim().min(1).max(160).optional(),revision:o.z.number().int().nonnegative().optional(),observedAt:_t.optional(),target:bt.optional(),lastError:dt.optional(),updatedAt:_t}).strict(),ys=o.z.object({id:Et,name:o.z.string().trim().min(1).max(160),connectionMode:o.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss","control-plane-proxy"]).default("direct-http"),connectionPath:hs.default({kind:"direct"}),connectionEnabled:o.z.boolean().default(!0),auth:o.z.object({mode:o.z.enum(["local-static-key","paired-hmac","proxy-binding"]).default("local-static-key"),keyId:Et.optional(),secret:o.z.string().trim().max(4096).optional(),pairedAt:_t.optional(),pairing:o.z.object({status:o.z.enum(["pending","paired","expired"]).default("pending"),joinToken:o.z.string().trim().max(4096).optional(),expiresAt:_t.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:o.z.string().trim().max(2048).optional(),controlEndpoint:o.z.string().trim().max(2048).optional(),containerEndpoint:o.z.string().trim().max(2048).optional(),publicWebBase:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:o.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),proxyState:fs.optional(),appInventory:Yt.optional(),labels:Pt,lastSeenAt:_t.optional(),createdAt:_t,updatedAt:_t}).strict().superRefine((e,t)=>{"control-plane-proxy"===e.connectionPath.kind&&"control-plane-proxy"!==e.connectionMode&&t.addIssue({code:"custom",path:["connectionPath"],message:"Control-plane proxy paths require control-plane-proxy connection mode."}),"control-plane-proxy"===e.connectionMode&&"control-plane-proxy"!==e.connectionPath.kind&&t.addIssue({code:"custom",path:["connectionPath"],message:"Control-plane proxy nodes require a control-plane-proxy connection path."})}),Is=o.z.object({id:Et,nodeId:Et,name:o.z.string().trim().min(1).max(160),type:o.z.enum(["docker","kubernetes","local"]),status:o.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:o.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),labels:Pt,createdAt:_t,updatedAt:_t}).strict(),bs=o.z.object({instanceId:Et,runtimeKind:o.z.literal("docker"),state:o.z.enum(["pending","available","stopped","unavailable"]),sampledAt:_t,cpu:o.z.object({usagePercent:o.z.number().nonnegative()}).strict().optional(),memory:o.z.object({usageBytes:o.z.number().nonnegative(),limitBytes:o.z.number().nonnegative().optional(),usagePercent:o.z.number().nonnegative().optional()}).strict().optional(),network:o.z.object({rxBytes:o.z.number().nonnegative(),txBytes:o.z.number().nonnegative()}).strict().optional(),blockIo:o.z.object({readBytes:o.z.number().nonnegative(),writeBytes:o.z.number().nonnegative()}).strict().optional(),pids:o.z.number().int().nonnegative().optional(),error:o.z.string().trim().max(2048).optional()}).strict(),vs=o.z.object({ok:o.z.boolean().optional(),role:o.z.string().optional(),nodeId:Et.optional(),platform:Xt.optional()}).passthrough(),ws=o.z.object({bindScope:o.z.enum(["loopback","all-ipv4"]),port:o.z.number().int().min(1).max(65535)}).strict().extend({host:o.z.enum(["127.0.0.1","0.0.0.0"]),status:o.z.enum(["listening","error"]),source:o.z.enum(["bootstrap","persisted"]),error:o.z.string().trim().max(2048).optional()}).strict(),Ss=o.z.object({nodeId:Et,joinToken:o.z.string().trim().min(1),expiresAt:_t}).passthrough(),As=o.z.object({nodeId:Et,keyId:Et,secret:o.z.string().regex(/^[A-Za-z0-9_-]{32,128}$/),pairedAt:_t}).strict(),ks=o.z.object({id:Et,keyId:Et,name:o.z.string().trim().min(1).max(160).optional(),pairedAt:_t,updatedAt:_t,current:o.z.boolean()}).passthrough(),Cs=o.z.object({keyId:Et,revoked:o.z.literal(!0),revokedAt:_t}).strict(),xs=o.z.object({id:Et,pairingKeyId:Et,url:o.z.string().trim().min(1).max(2048),name:o.z.string().trim().min(1).max(160).optional(),enabled:o.z.boolean(),createdAt:_t,updatedAt:_t,status:o.z.enum(["disabled","connecting","connected","reconnecting","failed"]),lastConnectedAt:_t.optional(),lastDisconnectedAt:_t.optional(),error:o.z.string().optional()}).passthrough(),zs=o.z.object({pairing:ks,connection:o.z.object({id:Et,pairingKeyId:Et,url:o.z.string().trim().min(1).max(2048),name:o.z.string().trim().min(1).max(160).optional(),enabled:o.z.boolean(),createdAt:_t,updatedAt:_t}).passthrough(),tunnel:o.z.object({status:o.z.enum(["disabled","saved","connecting","connected","failed"]),error:o.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),Ts=o.z.object({deleted:o.z.boolean().optional()}).passthrough(),Ns=o.z.object({repository:o.z.string().trim().optional(),tag:o.z.string().trim().optional(),id:o.z.string().trim(),createdSince:o.z.string().trim().optional(),size:o.z.string().trim().optional(),sizeBytes:o.z.number().int().nonnegative().optional(),reference:o.z.string().trim().min(1),repoDigests:o.z.array(o.z.string().trim().min(1).max(512)).default([])}).passthrough(),Es=o.z.object({image:rs,status:o.z.enum(["available","pull-required","unknown"]),localImage:Ns.optional(),localSizeBytes:o.z.number().int().nonnegative().optional(),downloadSizeBytes:o.z.number().int().positive().optional(),unpackedSizeBytes:o.z.number().int().positive().optional(),error:o.z.string().trim().max(2048).optional()}).strict(),_s=o.z.lazy(()=>o.z.object({name:o.z.string().trim().min(1),path:o.z.string().trim().min(1).max(4096),children:o.z.array(_s)}).passthrough()),Ps=o.z.object({status:o.z.number().int().min(100).max(599).optional(),headers:o.z.record(o.z.string(),o.z.string()).optional(),bodyBase64:o.z.string().optional()}).passthrough(),Os=o.z.object({strategy:o.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:o.z.string().trim().max(2048).optional(),api:o.z.string().trim().max(2048).optional(),vnc:o.z.string().trim().max(2048).optional(),tty:o.z.string().trim().max(2048).optional(),logs:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),Rs=o.z.object({strategy:o.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:o.z.string().trim().max(2048).optional(),api:o.z.string().trim().max(2048).optional(),ws:o.z.string().trim().max(2048).optional(),status:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),js=o.z.object({mode:o.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:o.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:o.z.string().trim().max(4096).optional(),resolvedCommit:o.z.string().trim().max(120).optional(),error:o.z.string().trim().max(2048).optional()}).passthrough(),Ds=o.z.object({id:Et,name:o.z.string().trim().min(1).max(160),projectId:Et.optional(),source:Pn,sourceSnapshot:o.z.record(o.z.string(),o.z.unknown()).default({}),modelSelection:Ht,nodeId:Et,runtimeId:Et,imageSelection:Wt.optional(),imageSnapshot:os.optional(),imageProvisioning:as.optional(),stateRevision:o.z.number().int().min(0).default(0),processIncarnationId:o.z.string().trim().min(1).max(120).optional(),status:o.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:o.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:o.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:o.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:o.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:o.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:Nt.optional(),instanceVersion:o.z.string().trim().max(80).optional(),build:yn.optional(),runtimeVersion:wn.optional(),ready:o.z.boolean().default(!1),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:Yt.optional(),config:o.z.object({autoImportAgentConfigs:o.z.boolean().default(!0),defaultCodexPermissionMode:G.default("ask")}).strict().default({autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"}),workspace:js.default({status:"unknown"}),target:Os.default({strategy:"direct-port",status:"unknown"}),access:Rs,apps:o.z.object({runningCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),updatedAt:_t.optional(),revision:o.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:we.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:o.z.object({enabledCount:o.z.number().int().min(0).default(0),runningCount:o.z.number().int().min(0).default(0),errorCount:o.z.number().int().min(0).default(0),configs:o.z.array(o.z.object({configHash:o.z.string().trim().min(1),config:$e,deployments:o.z.array(Be).default([]),runtime:o.z.array(Ue).default([])}).strict()).default([]),recentRuns:o.z.array(Fe).default([])}).strict().optional(),runtime:o.z.object({kind:o.z.string().trim().max(80).optional(),containerName:o.z.string().trim().max(240).optional(),containerId:o.z.string().trim().max(240).optional(),workspacePath:o.z.string().trim().max(4096).optional(),pid:o.z.number().int().positive().optional(),port:o.z.number().int().positive().max(65535).optional(),labels:o.z.record(o.z.string(),o.z.string()).default({})}).strict().default({labels:{}}),registrationToken:o.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:_t.optional(),createdAt:_t,updatedAt:_t}).strict(),Ms="instance.lifecycle.snapshot",qs=o.z.object({instanceId:Ds.shape.id,revision:o.z.number().int().min(0),updatedAt:_t,status:Ds.shape.status.unwrap(),health:Ds.shape.health.unwrap(),connectionStatus:Ds.shape.connectionStatus.unwrap(),accessStatus:o.z.enum(["reachable","endpoint-unreachable"]),imageProvisioning:as.optional(),workspace:js,runtime:Ds.shape.runtime.unwrap(),runtimeVersion:wn.optional(),ready:o.z.boolean(),lastHeartbeatAt:_t.optional()}).strict();function $s(e,t,n,s,i){if(!t||"object"!=typeof t||Array.isArray(t))return;let r=structuredClone(t);for(let t=0;t<20;t+=1){const t=e.safeParse(r);if(t.success)return t.data;let n=!1;for(const e of t.error.issues){if("unrecognized_keys"!==e.code)continue;const t=Ls(r,e.path);if(t&&"object"==typeof t&&!Array.isArray(t))for(const s of e.keys)Object.prototype.hasOwnProperty.call(t,s)&&(delete t[s],n=!0)}if(!n){const n=structuredClone(r);let s=!1;for(const e of t.error.issues){if(!e.path.length||"string"!=typeof e.path.at(-1))continue;const t=Ls(n,e.path.slice(0,-1)),i=e.path.at(-1);t&&"object"==typeof t&&!Array.isArray(t)&&Object.prototype.hasOwnProperty.call(t,i)&&(delete t[i],s=!0)}if(s){const t=e.safeParse(n);if(t.success)return t.data}break}}}function Ls(e,t){let n=e;for(const e of t){if(!n||"object"!=typeof n)return;n=n[e]}return n}function Bs(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]]))}o.z.object({instanceId:Et.optional(),projectId:Et.optional(),source:Pn.optional(),nodeId:Et.optional(),runtimeId:Et.optional(),imageSelection:Wt.optional(),instanceVersion:o.z.string().trim().max(80).optional(),protocolVersion:Nt,build:yn.optional(),controlMode:o.z.enum(["standalone","controlled"]).default("controlled"),capabilities:o.z.record(o.z.string(),o.z.unknown()).default({}),appInventory:Yt.optional(),target:Os.default({strategy:"direct-port",status:"unknown"}),workspace:js.default({status:"unknown"}),registrationToken:o.z.string().trim().max(240).optional(),processIncarnationId:o.z.string().trim().min(1).max(120).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Tt||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),o.z.object({status:Ds.shape.status.optional(),health:Ds.shape.health.optional(),protocolVersion:Nt,build:yn.optional(),capabilities:Ds.shape.capabilities.optional(),appInventory:Yt.optional(),apps:Ds.shape.apps.optional(),aiSessions:Ds.shape.aiSessions.optional(),triggers:Ds.shape.triggers.optional(),workspace:js.optional(),target:Os.partial().optional(),processIncarnationId:o.z.string().trim().min(1).max(120).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Tt||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})});const Us=o.z.enum(["web","telegram","wechat","dingding"]),Fs=o.z.object({id:Et,channel:Us,name:o.z.string().trim().min(1).max(160),enabled:o.z.boolean().default(!1),token:o.z.string().trim().max(4096).optional(),defaultChatId:o.z.string().trim().max(240).optional(),allowedUserIds:o.z.array(o.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:o.z.number().int().positive().max(6e4).default(3e3),settings:o.z.record(o.z.string(),o.z.unknown()).default({}),createdAt:_t,updatedAt:_t}).strict(),Hs=o.z.object({id:Et,channel:Us,bridgeId:Et.optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional(),activeProjectId:Et.optional(),activeInstanceId:Et.optional(),activeAiSessionId:o.z.string().trim().min(1).max(120).optional(),lastUsedAt:_t,createdAt:_t,updatedAt:_t}).strict(),Ws=o.z.object({source:o.z.object({type:o.z.literal("chat-gateway").default("chat-gateway"),channel:Us,bridgeId:Et.optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional()}).strict(),message:o.z.object({text:o.z.string().trim().max(2e4).default(""),attachments:o.z.array(U).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>k&&t.addIssue({code:o.z.ZodIssueCode.custom,path:["attachments"],message:"Inline attachments must be 41943040 bytes or less in total."})}),target:o.z.object({instanceId:Et,aiSessionId:o.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),Gs=o.z.object({id:Et,instanceId:Et,projectId:Et.optional(),aiSessionId:o.z.string().trim().min(1).max(120),providerSessionId:o.z.string().trim().max(240).optional(),result:o.z.string().default(""),timeoutMs:o.z.number().positive().optional(),source:o.z.string().trim().max(120).optional(),kind:o.z.enum(["task","approval"]).default("task"),status:o.z.enum(["pending","unreachable"]).default("pending"),error:o.z.string().trim().max(2048).optional(),lastSeenAt:_t}).strict();function Vs(e){return!1!==e.ready&&(!e.runtimeVersion||["matched","failed"].includes(e.runtimeVersion.phase))}function Ks(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":e.startsWith("market.")?"market":"system"}const Js=o.z.enum(["ai.sessions","app.sessions"]),Qs=o.z.object({topic:Js,instanceId:o.z.string().trim().min(1).max(160),streamId:o.z.string().trim().min(1).max(240),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0)}).strict(),Ys=o.z.object({protocolVersion:o.z.literal(1),streams:o.z.array(Qs)}).strict();function Xs(e,t=448){n.mkdirSync(e,{recursive:!0,mode:t}),n.chmodSync(e,t)}function Zs(e,s,i={}){const r=i.directoryMode??448,o=i.fileMode??384;Xs(t.dirname(e),r),c.sync(e,`${JSON.stringify(s,null,2)}\n`,{encoding:"utf8",mode:o}),n.chmodSync(e,o)}function ei(e,t){console.warn(JSON.stringify({message:e,...t}))}function ti(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function ni(e,t){const s=t.logger||ei;let i;try{i=JSON.parse(n.readFileSync(e,"utf8"))}catch(t){return void s("stored JSON could not be read",{filePath:e,...ti(t)})}if(!t.schema)return i;const r=t.sanitize?t.sanitize(i):i,o=t.schema.safeParse(r);if(o.success)return o.data;const a="strip"in t.schema&&"function"==typeof t.schema.strip?t.schema.strip().safeParse(r):void 0;if(a?.success)return s("unknown stored fields were ignored",{filePath:e,issues:o.error.issues}),a.data;s("stored record failed validation and was isolated",{filePath:e,issues:o.error.issues})}function si(e){return`${e}_${a.randomUUID().replace(/-/g,"").slice(0,20)}`}function ii(){return a.randomBytes(24).toString("base64url")}class ri{directory;options;permissionsInitialized=!1;constructor(e,t={}){this.directory=e,this.options=t}init(){if(this.permissionsInitialized&&n.existsSync(this.directory))return;Xs(this.directory,this.options.directoryMode??448);const e=this.options.fileMode??384;for(const s of n.readdirSync(this.directory).filter(e=>e.endsWith(".json")))n.chmodSync(t.join(this.directory,s),e);this.permissionsInitialized=!0}filePath(e){return t.join(this.directory,`${e}.json`)}list(){return this.init(),n.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const n=ni(t.join(this.directory,e),this.options);return n?[n]:[]})}get(e){this.init();const t=this.filePath(e);return n.existsSync(t)?ni(t,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 Zs(this.filePath(n.id),n,this.options),n}patch(e,t){const n=this.get(e);if(!n)return;const s={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:(new Date).toISOString()};return this.put(s)}delete(e){const t=this.filePath(e);return!!n.existsSync(t)&&(n.unlinkSync(t),!0)}}class oi{filePathValue;defaults;options;constructor(e,t,n={}){this.filePathValue=e,this.defaults=t,this.options=n}init(){Xs(t.dirname(this.filePathValue),this.options.directoryMode??448),n.existsSync(this.filePathValue)?n.chmodSync(this.filePathValue,this.options.fileMode??384):this.put(this.defaults())}get(){return this.init(),n.existsSync(this.filePathValue)?ni(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 Zs(this.filePathValue,n,this.options),n}}const ai=d.promisify(a.scrypt),di="task_handoff_cp_session",ci=o.z.enum(["disabled","password"]),li=o.z.string().trim().min(1).max(80).regex(/^[a-zA-Z0-9_.@-]+$/),pi=o.z.string().min(8).max(4096),ui=o.z.object({username:li,password:pi}).strict(),gi=o.z.object({username:o.z.string().trim().min(1).max(80),password:o.z.string().min(1).max(4096)}).strict(),mi=o.z.object({id:o.z.string().trim().min(1),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}),hi=mi.extend({username:li,passwordHash:o.z.string().trim().min(1),role:o.z.enum(["viewer","operator","admin"]),lastLoginAt:o.z.string().datetime().optional()}).strict(),fi=mi.extend({userId:o.z.string().trim().min(1),tokenHash:o.z.string().trim().min(1),expiresAt:o.z.string().datetime(),lastSeenAt:o.z.string().datetime().optional()}).strict();function yi(){return(new Date).toISOString()}function Ii(e,t){return Number.isInteger(e)&&Number(e)>0?Number(e):t}class bi{options;sourceFailures=new Map;usernameFailures=new Map;concurrent=0;constructor(e={}){this.options=function(e={}){return{windowMs:Ii(e.windowMs,9e5),maxFailuresPerSource:Ii(e.maxFailuresPerSource,20),maxFailuresPerUsername:Ii(e.maxFailuresPerUsername,10),maxConcurrent:Ii(e.maxConcurrent,4)}}(e)}begin(e,t){const n=Date.now();if(this.assertBucketAvailable(this.sourceFailures,e,this.options.maxFailuresPerSource,n),this.assertBucketAvailable(this.usernameFailures,t,this.options.maxFailuresPerUsername,n),this.concurrent>=this.options.maxConcurrent)throw vi("Too many password verifications are already in progress.",1e3);this.concurrent+=1}finish(){this.concurrent=Math.max(0,this.concurrent-1)}recordFailure(e,t){const n=Date.now();this.record(this.sourceFailures,e,n),this.record(this.usernameFailures,t,n),this.sourceFailures.size+this.usernameFailures.size>1e4&&(this.pruneAll(n),this.trimOldest(this.sourceFailures,5e3),this.trimOldest(this.usernameFailures,5e3))}assertBucketAvailable(e,t,n,s){const i=this.currentFailures(e,t,s);if(!(i.length<n))throw vi("Too many failed sign-in attempts. Try again later.",i[0]+this.options.windowMs-s)}record(e,t,n){const s=this.currentFailures(e,t,n);s.push(n),e.set(t,s)}currentFailures(e,t,n){const s=n-this.options.windowMs,i=(e.get(t)||[]).filter(e=>e>s);return i.length?e.set(t,i):e.delete(t),i}pruneAll(e){for(const[t]of this.sourceFailures)this.currentFailures(this.sourceFailures,t,e);for(const[t]of this.usernameFailures)this.currentFailures(this.usernameFailures,t,e)}trimOldest(e,t){for(;e.size>t;){const t=e.keys().next().value;if(void 0===t)return;e.delete(t)}}}function vi(e,t){const n=new Error(e);return Object.assign(n,{statusCode:429,code:"AUTH_LOGIN_RATE_LIMITED",retryable:!0,retryAfterSeconds:Math.max(1,Math.ceil(t/1e3))}),n}function wi(e){return a.createHash("sha256").update(e).digest("base64url")}async function Si(e){const t=a.randomBytes(16).toString("base64url");return`scrypt$${t}$${(await ai(e,t,64)).toString("base64url")}`}function Ai(e){return{id:e.id,username:e.username,role:e.role,createdAt:e.createdAt,updatedAt:e.updatedAt,lastLoginAt:e.lastLoginAt}}class ki{mode;users;sessions;loginRateLimiter;bootstrapAdminInProgress=!1;constructor(e,t={}){this.mode=function(e={}){return ci.parse(e.mode||process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"disabled")}(t),this.users=new ri(e.authUsersDir,{schema:hi}),this.sessions=new ri(e.authSessionsDir,{schema:fi}),this.loginRateLimiter=new bi(t.loginRateLimit)}init(){this.users.init(),this.sessions.init()}enabled(){return"password"===this.mode}state(){return{mode:this.mode,enabled:this.enabled(),requiresBootstrap:this.enabled()&&0===this.users.list().length}}async bootstrapAdmin(e){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}if(this.bootstrapAdminInProgress){const e=new Error("Control Plane admin initialization is already in progress.");throw Object.assign(e,{statusCode:409,code:"AUTH_BOOTSTRAP_IN_PROGRESS"}),e}if(this.users.list().length>0){const e=new Error("Control Plane admin user already exists.");throw Object.assign(e,{statusCode:409,code:"AUTH_BOOTSTRAP_ALREADY_DONE"}),e}const t=ui.parse(e);this.bootstrapAdminInProgress=!0;try{const e=yi();return Ai(this.users.put({id:si("user"),username:t.username,passwordHash:await Si(t.password),role:"admin",createdAt:e,updatedAt:e}))}finally{this.bootstrapAdminInProgress=!1}}async login(e,t={}){if(!this.enabled()){const e=new Error("Control Plane authentication is disabled.");throw Object.assign(e,{statusCode:400,code:"AUTH_DISABLED"}),e}const n=gi.parse(e),s=String(t.sourceId||"unknown").trim()||"unknown",i=n.username.toLocaleLowerCase("en-US");this.loginRateLimiter.begin(s,i);try{const e=this.users.list().find(e=>e.username===n.username);if(!e||!await async function(e,t){const[n,s,i]=t.split("$");if("scrypt"!==n||!s||!i)return!1;const r=await ai(e,s,64),o=Buffer.from(i,"base64url");return o.length===r.length&&a.timingSafeEqual(o,r)}(n.password,e.passwordHash)){this.loginRateLimiter.recordFailure(s,i);const e=new Error("Invalid username or password.");throw Object.assign(e,{statusCode:401,code:"AUTH_LOGIN_FAILED"}),e}const t=yi(),r=ii(),o=new Date(Date.now()+12096e5).toISOString(),d=this.sessions.put({id:si("sess"),userId:e.id,tokenHash:wi(r),expiresAt:o,createdAt:t,updatedAt:t,lastSeenAt:t});return this.users.put({...e,lastLoginAt:t,updatedAt:t}),{user:Ai({...e,lastLoginAt:t,updatedAt:t}),sessionToken:`${d.id}.${r}`,expiresAt:o}}finally{this.loginRateLimiter.finish()}}async userForSessionToken(e){if(!this.enabled())return;if(!e)return;const[t,n]=e.split(".");if(!t||!n)return;const s=this.sessions.get(t);if(!s||s.expiresAt<=yi()||s.tokenHash!==wi(n))return;const i=this.users.get(s.userId);return i?(this.sessions.put({...s,lastSeenAt:yi(),updatedAt:yi()}),Ai(i)):void 0}async currentSession(e){const t=this.state(),n=await this.userForSessionToken(e);return{...t,authenticated:!this.enabled()||Boolean(n),user:n}}logout(e){const t=e?.split(".")[0];return t&&this.sessions.delete(t),{ok:!0}}}const Ci=o.z.enum(["import","export"]),xi=o.z.object({id:o.z.string().trim().min(1).max(120),label:o.z.string().trim().min(1).max(120),directoryName:o.z.string().trim().min(1).max(120)}).strict(),zi=o.z.object({direction:Ci,programIds:o.z.array(o.z.string().trim().min(1).max(120)).min(1).max(20).refine(e=>new Set(e).size===e.length,"Config sync program ids must be unique."),workspaceFolder:o.z.string().trim().min(1).max(1e3)}).strict(),Ti=o.z.object({import:o.z.string().trim().min(1).max(1e3).default("."),export:o.z.string().trim().min(1).max(1e3).default(".")}).strict(),Ni=o.z.object({programs:o.z.array(xi),preferences:Ti}).strict(),Ei=o.z.object({id:o.z.string().trim().min(1).max(120),type:o.z.enum(["file","dir"]),projectPath:o.z.string().trim().min(1).max(1e3),containerPath:o.z.string().trim().min(1).max(1e3),source:o.z.string().trim().min(1).max(4096),target:o.z.string().trim().min(1).max(4096),status:o.z.enum(["copied","skipped_missing_source","failed"]),error:o.z.string().trim().max(4096).optional()}).strict(),_i=o.z.object({preset:o.z.object({id:o.z.string().trim().min(1).max(120),label:o.z.string().trim().min(1).max(120),projectRoot:o.z.string().trim().min(1).max(1e3)}).strict(),direction:Ci,items:o.z.array(Ei).max(100)}).strict(),Pi=o.z.object({direction:Ci,workspaceFolder:o.z.string().trim().min(1).max(1e3),programs:o.z.array(_i).min(1).max(20)}).strict(),Oi="app-session.snapshot",Ri="app-session.patch",ji="app-session.removed",Di=o.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),Mi=o.z.object({type:o.z.enum(["app-session","provider-session","adapter-key"]),id:o.z.string().trim().min(1).max(240),agent:o.z.string().trim().min(1).max(80).optional(),adapter:o.z.string().trim().min(1).max(80).optional(),key:o.z.string().trim().min(1).max(240).optional()}).strict(),qi=o.z.record(o.z.string(),o.z.unknown()).and(o.z.object({id:o.z.string().trim().min(1).max(160),appId:o.z.string().trim().min(1).max(160).optional(),title:o.z.string().trim().max(240).optional(),kind:o.z.string().trim().max(80).optional(),status:Di.default("unknown"),bindings:o.z.array(Mi).max(40).default([]),workspace:o.z.object({cwd:o.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:o.z.string().datetime().optional(),updatedAt:o.z.string().datetime().optional()})),$i=o.z.object({runningCount:o.z.number().int().min(0).default(0),problemCount:o.z.number().int().min(0).default(0),sessions:o.z.array(qi).default([]),updatedAt:o.z.string().datetime()}).strict(),Li=o.z.object({streamId:o.z.string().trim().min(1).max(240),revision:o.z.number().int().min(0),lastEventAt:o.z.string().datetime(),snapshot:$i}).strict();$i.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:o.z.number().int().min(0).optional()});const Bi=o.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),Ui=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),nodeId:o.z.string().trim().min(1).max(160).optional(),revision:o.z.number().int().min(0),previousRevision:o.z.number().int().min(0).optional(),traceId:o.z.string().trim().min(1).max(160),generatedAt:o.z.string().datetime(),reason:Bi}).strict(),Fi=o.z.object({meta:Ui,snapshot:$i}).strict(),Hi=o.z.object({meta:Ui,session:qi}).strict(),Wi=o.z.object({meta:Ui,sessionId:o.z.string().trim().min(1).max(160),tombstone:qi.optional(),expiresAt:o.z.string().datetime().optional()}).strict(),Gi=o.z.object({streamId:o.z.string().trim().min(1).max(240),instanceId:o.z.string().trim().min(1).max(160),sinceRevision:o.z.number().int().min(0),latestRevision:o.z.number().int().min(0),earliestRetainedRevision:o.z.number().int().min(0),syncRequired:o.z.boolean().default(!1),events:o.z.array(o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal(Oi),payload:Fi}).strict(),o.z.object({type:o.z.literal(Ri),payload:Hi}).strict(),o.z.object({type:o.z.literal(ji),payload:Wi}).strict()])).default([])}).strict();function Vi(e,t=(new Date).toISOString()){const n=e.map(e=>Ki(e));return $i.parse({runningCount:n.filter(e=>"running"===e.status).length,problemCount:n.filter(e=>"failed"===e.status).length,sessions:n,updatedAt:t})}function Ki(e){const t=Ji("string"==typeof e.status?e.status:void 0),n="string"==typeof e.id?e.id:"",s=function(e){const t=er(e.workspace),n=er(e.tty),s=er(e.launch),i=er(e.ai),r=er(i.claude),o=tr(t.cwd)||tr(n.cwd)||tr(r.cwd)||tr(s.cwd);return o?{cwd:o}:void 0}(e),{workspace:i,...r}=e;return qi.parse({...r,status:t,bindings:Zi([...(o=e.bindings,Array.isArray(o)?o.flatMap(e=>{const t=Mi.safeParse(e);return t.success?[t.data]:[]}):[]),...n?[{type:"app-session",id:n}]:[],...Yi(e)]),...s?{workspace:s}:{}});var o}function Ji(e){const t=(e||"unknown").trim().toLowerCase();return"ended"===t?"closed":"terminating"===t?"stopping":Di.options.includes(t)?t:"unknown"}function Qi(e){return"app-session"===e.type?`app:${e.id}`:"provider-session"===e.type?`provider:${e.agent||""}:${e.id}`:`adapter:${e.adapter||""}:${e.key||e.id}`}function Yi(e){const t=er(e.ai),n=er(t.claude),s=er(t.appServer),i=[],r=tr(n.short);r&&i.push({type:"adapter-key",adapter:"claude",agent:"claude",id:`short:${r}`,key:`short:${r}`});const o=tr(n.providerSessionId);o&&i.push({type:"provider-session",agent:"claude",id:o});const a=Array.isArray(t.threadIds)?t.threadIds.filter(e=>"string"==typeof e&&Boolean(e.trim())):[],d=tr(t.activeThreadId);for(const e of[...new Set([d,...a].filter(Boolean))])i.push({type:"provider-session",agent:"codex",id:e});const c=tr(s.socketPath);return c&&i.push({type:"adapter-key",adapter:"codex-app-server",agent:"codex",id:`socket:${c}`,key:`socket:${c}`}),i}function Xi(e){const t="string"==typeof e.id?e.id:"",n=er(e.ai),s=tr(er(n.claude).short);return[t?`app:${t}`:"",s?`claude-short:${s}`:""]}function Zi(e){const t=new Map;for(const n of e)t.set(Qi(n),n);return[...t.values()]}function er(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function tr(e){return"string"==typeof e&&e.trim()?e.trim():""}function nr(e=(new Date).toISOString()){return $i.parse({runningCount:0,problemCount:0,sessions:[],updatedAt:e})}class sr{options;resumeSnapshotRefreshFailures=0;lastResumeSnapshotRefreshFailure;constructor(e){this.options=e}async resolveApproval(e,t,n){return he.parse(await this.post(e,or(t,"approval"),{decision:n}))}async listHistory(e){return ye.parse(await this.get(e,"/ai-sessions/history"))}async historyDetail(e,t){return Ie.parse(await this.get(e,`/ai-sessions/history/${encodeURIComponent(t)}`))}async resume(e,t){const n=be.parse(await this.post(e,or(t,"resume"),{}));return await this.refreshAfterCommittedResume(e,t),n}async refreshAfterCommittedResume(e,t){try{await this.options.refreshSnapshots()}catch(n){const s={instanceId:e,aiSessionId:t,code:ir(n),message:rr(n),occurredAt:(new Date).toISOString()};this.resumeSnapshotRefreshFailures+=1,this.lastResumeSnapshotRefreshFailure=s;try{this.options.warn?.(s,"AI session resumed but snapshot refresh failed")}catch{}}}diagnostics(){return{resumeSnapshotRefreshFailures:this.resumeSnapshotRefreshFailures,lastResumeSnapshotRefreshFailure:this.lastResumeSnapshotRefreshFailure}}async sendMessage(e,t,n,s,i=[],r=[],o){!function(e){if(e.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)<=k)return;const t=new Error("Inline attachments must be 41943040 bytes or less in total.");throw Object.assign(t,{statusCode:400,code:"AI_SESSION_ATTACHMENTS_TOO_LARGE"}),t}(i);const a=await this.options.requireInstance(e);i.some(e=>"runtime-path"===e.source.type)&&function(e,t){if(!e.some(e=>"runtime-path"===e.source.type)||"local"===t)return;const n=new Error("Runtime path attachments are currently available only for Local Runtime instances.");throw Object.assign(n,{statusCode:400,code:"AI_SESSION_RUNTIME_PATH_UNSUPPORTED"}),n}(i,(await this.options.requireRuntime(a.nodeId,a.runtimeId)).type);const d=a.aiSessions.sessions.find(e=>e.id===t),c=o||("codex"===d?.agent?a.config.defaultCodexPermissionMode:void 0);return he.parse(await this.options.request(a,or(t,"messages"),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({message:n,...s?{mode:s}:{},...i.length?{attachments:i}:{},...r.length?{references:r}:{},...c?{permissionMode:c}:{}})}))}async mentionCatalog(e,t){return ne.parse(await this.get(e,or(t,"mentions")))}async searchMentionFiles(e,t,n){return ie.parse(await this.post(e,or(t,"mentions/files"),{query:n}))}async executeCommand(e,t,n){return J.parse(await this.post(e,or(t,"commands"),n))}async queue(e,t){return ce.parse(await this.get(e,or(t,"queue")))}async steerQueuedMessage(e,t,n){return he.parse(await this.post(e,ar(t,n,"steer"),{}))}async retryQueuedMessage(e,t,n){return me.parse(await this.post(e,ar(t,n,"retry"),{}))}async removeQueuedMessage(e,t,n){const s=await this.options.requireInstance(e);return me.parse(await this.options.request(s,ar(t,n),{method:"DELETE"}))}async interrupt(e,t){return he.parse(await this.post(e,or(t,"interrupt"),{}))}async get(e,t){return this.options.request(await this.options.requireInstance(e),t)}async post(e,t,n){return this.options.request(await this.options.requireInstance(e),t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}}function ir(e){return e&&"object"==typeof e&&"code"in e&&"string"==typeof e.code?e.code:"AI_SESSION_SNAPSHOT_REFRESH_FAILED"}function rr(e){return e instanceof Error?e.message:String(e)}function or(e,t){return`/ai-sessions/${encodeURIComponent(e)}/${t}`}function ar(e,t,n){return`${or(e,"queue")}/${encodeURIComponent(t)}${n?`/${n}`:""}`}function dr(e,t,n,s){const i=s&&"object"==typeof s?s:{};return{nodeId:e.id,route:"string"==typeof i.route?i.route:t,method:n,code:"string"==typeof i.code?i.code:"NODE_AGENT_REQUEST_FAILED",message:s instanceof Error?s.message:String(s),statusCode:"number"==typeof i.statusCode?i.statusCode:void 0,issues:Array.isArray(i.issues)?i.issues:void 0}}class cr{requestImpl;logger;constructor(e){this.requestImpl=e.request,this.logger=e.logger}async request(e,t,n={}){const s=n.method||"GET";let i,r;try{i=await this.requestImpl(e,t,n)}catch(n){throw this.logger?.warn?.({nodeId:e.id,route:t,method:s,error:n instanceof Error?n.message:String(n),errorCode:"NODE_AGENT_TRANSPORT_FAILED"},"node agent transport failed"),n}try{r=await i.json()}catch(n){this.logger?.warn?.({nodeId:e.id,route:t,method:s,statusCode:i.status,error:n instanceof Error?n.message:String(n),errorCode:"NODE_AGENT_RESPONSE_INVALID_JSON"},"node agent response was not valid json");const r=new Error(`Node agent ${e.id} returned invalid JSON for ${t}.`);throw Object.assign(r,{statusCode:502,code:"NODE_AGENT_RESPONSE_INVALID_JSON",nodeId:e.id,route:t}),r}if(!i.ok){const n=new Error(r.error?.message||`Node agent request failed with HTTP ${i.status}`);throw Object.assign(n,{statusCode:i.status,code:r.error?.code||"NODE_AGENT_REQUEST_FAILED",nodeId:e.id,route:t}),this.logger?.warn?.({nodeId:e.id,route:t,method:s,statusCode:i.status,errorCode:r.error?.code||"NODE_AGENT_REQUEST_FAILED",error:n.message},"node agent request failed"),n}return this.logger?.info?.({nodeId:e.id,route:t,method:s,statusCode:i.status},"node agent request ok"),r.data??r}async requestSchema(e,t,n,s={}){return this.parse(e,t,n,await this.request(e,t,s),s.method||"GET")}parse(e,t,n,s,i="GET"){const r=n.safeParse(s);if(r.success)return r.data;this.logger?.warn?.({nodeId:e.id,route:t,method:i,errorCode:"NODE_AGENT_PROTOCOL_INVALID",issues:r.error.issues},"node agent protocol response did not match schema");const o=new Error(`Node agent ${e.id} returned invalid protocol data for ${t}.`);throw Object.assign(o,{statusCode:502,code:"NODE_AGENT_PROTOCOL_INVALID",nodeId:e.id,route:t,issues:r.error.issues}),o}}class lr{client;constructor(e){this.client=e}request(e,t,n={}){return this.client.request(e,t,n)}health(e){return this.client.requestSchema(e,"/health",vs)}checkUpdate(e,t){return this.client.requestSchema(e,"/updates/check",Cn,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}applyUpdate(e,t){return this.client.requestSchema(e,"/updates/apply",xn,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listUpdateJobs(e){return this.client.requestSchema(e,"/updates/jobs",o.z.array(xn))}getExternalListener(e){return this.client.requestSchema(e,"/settings/external-listener",ws)}updateExternalListener(e,t){return this.client.requestSchema(e,"/settings/external-listener",ws,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}createPairingInvite(e,t={}){return this.client.requestSchema(e,"/pairing/invites",Ss,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t&&"object"==typeof t?t:{})})}createControlPlaneConnection(e,t){return this.client.requestSchema(e,"/control-plane-connections",zs,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}listControlPlanePairings(e){return this.client.requestSchema(e,"/control-plane-pairings",o.z.array(ks))}deleteControlPlanePairing(e,t){return this.client.requestSchema(e,`/control-plane-pairings/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}listControlPlaneConnections(e){return this.client.requestSchema(e,"/control-plane-connections",o.z.array(xs))}deleteControlPlaneConnection(e,t){return this.client.requestSchema(e,`/control-plane-connections/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}listDockerImages(e){return this.client.requestSchema(e,"/docker/images",o.z.array(Ns))}listFolderTree(e,t={}){const n=new URLSearchParams;t.path&&n.set("path",t.path),void 0!==t.depth&&n.set("depth",String(t.depth));const s=n.toString();return this.client.requestSchema(e,"/folders/tree"+(s?`?${s}`:""),o.z.array(_s))}listLocalFolders(e,t={}){return this.client.requestSchema(e,"/local-folders",o.z.array(_n),t)}createLocalFolder(e,t){return this.client.requestSchema(e,"/local-folders",_n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}async requireLocalFolder(e,t){const n=(await this.listLocalFolders(e)).find(e=>e.id===t);if(!n){const n=new Error(`Local folder ${t} was not found on node ${e.id}.`);throw Object.assign(n,{statusCode:404,code:"NODE_LOCAL_FOLDER_NOT_FOUND",nodeId:e.id,route:"/local-folders"}),n}return n}listRuntimes(e,t={}){return this.client.requestSchema(e,"/runtimes",o.z.array(Is),t)}createRuntime(e,t){return this.client.requestSchema(e,"/runtimes",Is,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateRuntime(e,t,n){const s=`/runtimes/${encodeURIComponent(t)}`;return this.client.requestSchema(e,s,Is,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}checkRuntime(e,t){const n=`/runtimes/${encodeURIComponent(t)}/check`;return this.client.requestSchema(e,n,Is,{method:"POST"})}deleteRuntime(e,t){return this.client.requestSchema(e,`/runtimes/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}deleteLocalFolder(e,t){return this.client.requestSchema(e,`/local-folders/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}listModels(e,t={}){return this.client.requestSchema(e,"/models",o.z.array(qn),t)}createModel(e,t){return this.client.requestSchema(e,"/models",qn,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}deployModel(e,t,n){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}/deploy`,qn,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}updateModel(e,t,n){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,qn,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}deleteModel(e,t){return this.client.requestSchema(e,`/models/${encodeURIComponent(t)}`,Ts,{method:"DELETE"})}assignInstanceModels(e,t,n){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/model-assignment`,o.z.object({assignment:$n,instance:Ds}).strict(),{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}async listFleetModels(e,t={}){return(await Promise.allSettled(e.map(async e=>({node:e,models:await this.listModels(e,t)})))).reduce((t,n,s)=>{if("fulfilled"===n.status)t.items.push(...n.value.models.map(e=>({nodeId:n.value.node.id,model:e})));else{const i=e[s];i&&t.nodeErrors.push(dr(i,"/models","GET",n.reason))}return t},{items:[],nodeErrors:[]})}async listFleetRuntimes(e,t={}){return(await Promise.allSettled(e.map(async e=>({node:e,runtimes:await this.listRuntimes(e,t)})))).reduce((t,n,s)=>{if("fulfilled"===n.status)t.items.push(...n.value.runtimes);else{const i=e[s];i&&t.nodeErrors.push(dr(i,"/runtimes","GET",n.reason))}return t},{items:[],nodeErrors:[]})}listInstances(e,t={}){return this.listInstancesWithDiagnostics(e,t).then(e=>e.items)}async listInstancesWithDiagnostics(e,t={}){return this.client.requestSchema(e,"/instances",o.z.array(o.z.unknown()),t).then(t=>t.reduce((t,n)=>{const s=this.normalizeInstance(e,n);return s.instance&&t.items.push(s.instance),s.error&&t.nodeErrors.push(s.error),t},{items:[],nodeErrors:[]}))}createInstance(e,t){return this.client.requestSchema(e,"/instances",Ds,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)})}updateInstance(e,t,n){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}`,Ds,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}deleteInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/delete`,Ts,{method:"POST"})}startInstance(e,t,n={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/start`,Ds,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}retryInstanceImageProvisioning(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/image-provisioning/retry`,Ds,{method:"POST"})}stopInstance(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/stop`,Ds,{method:"POST"})}restartInstance(e,t,n={}){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/restart`,Ds,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}instanceResourceMetrics(e,t){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/metrics`,bs)}proxyRawInstance(e,t,n){return this.client.requestSchema(e,`/instances/${encodeURIComponent(t)}/proxy/raw`,Ps,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)})}async listFleetInstances(e,t={}){return(await Promise.allSettled(e.map(async e=>({node:e,result:await this.listInstancesWithDiagnostics(e,t)})))).reduce((t,n,s)=>("fulfilled"===n.status?(t.items.push(...n.value.result.items),t.nodeErrors.push(...n.value.result.nodeErrors)):t.nodeErrors.push(dr(e[s],"/instances","GET",n.reason)),t),{items:[],nodeErrors:[]})}normalizeInstance(e,t){const n=(s=t,Ds.safeParse(function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=new Set(Object.keys(Ds.shape)),s={};for(const[e,i]of Object.entries(t))n.has(e)&&(s[e]=i);if(t.capabilities&&"object"==typeof t.capabilities&&!Array.isArray(t.capabilities)){const e={...t.capabilities};Object.prototype.hasOwnProperty.call(e,"apps")&&delete e.apps,s.capabilities=e}else s.capabilities={};s.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["observedAt","items","issues"].includes(e));const n={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{e&&"object"==typeof e&&!Array.isArray(e)&&Object.keys(e).some(e=>!["id","name","kind","source","availability","capabilities","diagnosticCode"].includes(e));const t=Bs(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Bs(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Bs(e,["code","message"])):t.issues},s=Yt.safeParse(n);return s.success?s.data:void 0}(t.appInventory,"string"==typeof t.id&&t.id),s.source=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=t.type,s=Pn.options.find(e=>e.shape.type.value===n);if(!s)return e;const i="local-folder"===n?["type","localFolderId","path","ownerNodeId"]:"git-template"===n?["type","url","templateId","ref","auth","clone"]:["type","repositoryId","url","provider","ref","auth","clone"],r=Bs(t,i);if("local-folder"!==n){const e=$s(Tn,Bs(t.ref,["type","name","commit"])),n=$s(Nn,Bs(t.auth,["type","secretId"])),s=$s(En,Bs(t.clone,["depth","submodules","lfs","subdirectory"]));e?r.ref=e:delete r.ref,n?r.auth=n:delete r.auth,s?r.clone=s:delete r.clone}const o=$s(s,r);return o?(Object.keys(t).some(e=>!i.includes(e)),o):r}(t.source,"string"==typeof t.id&&t.id),s.sourceSnapshot=t.sourceSnapshot&&"object"==typeof t.sourceSnapshot&&!Array.isArray(t.sourceSnapshot)?t.sourceSnapshot:{},s.build=$s(yn,t.build,0,0,"string"==typeof t.id&&t.id),s.aiSessions=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n=Array.isArray(t.sessions)?t.sessions.flatMap((e,t)=>{const n=$s(ve,e);return n?[n]:[]}):[],s={runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,waitingCount:"number"==typeof t.waitingCount&&Number.isInteger(t.waitingCount)&&t.waitingCount>=0?t.waitingCount:0,staleCount:"number"==typeof t.staleCount&&Number.isInteger(t.staleCount)&&t.staleCount>=0?t.staleCount:0,sessions:n,updatedAt:_t.safeParse(t.updatedAt).success?t.updatedAt:(new Date).toISOString()};return Object.keys(t).some(e=>!["runningCount","waitingCount","staleCount","sessions","updatedAt"].includes(e)),we.parse(s)}(t.aiSessions,"string"==typeof t.id&&t.id),s.triggers=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e;Object.keys(t).some(e=>!["enabledCount","runningCount","errorCount","configs","recentRuns"].includes(e));const n=(e,t,n)=>Array.isArray(e)?e.flatMap((e,n)=>{const s=$s(t,e);return s?[s]:[]}):[],s=Array.isArray(t.configs)?t.configs.flatMap((e,t)=>{if(!e||"object"!=typeof e||Array.isArray(e))return[];const s=e;Object.keys(s).some(e=>!["configHash","config","deployments","runtime"].includes(e));const i=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n=Bs(t,["configHash","name","description","createdAt","updatedAt"]);if(t.source&&"object"==typeof t.source&&!Array.isArray(t.source)){const e=t.source,s="schedule"===e.type?"interval"===e.scheduleKind?["type","scheduleKind","intervalMs"]:"daily"===e.scheduleKind?["type","scheduleKind","timeOfDay","timezone"]:["type","scheduleKind","weekdays","timeOfDay","timezone"]:"file-change"===e.type?["type","roots","globs","ignore","debounceMs"]:["type","agent","statuses","phases"];n.source=Bs(e,s)}return n.action=Bs(t.action,["promptTemplate"]),n.policy=Bs(t.policy,["cooldownMs","maxConcurrentRuns","whenBusy"]),$s($e,n)}(s.config);return"string"==typeof s.configHash&&i?[{configHash:s.configHash,config:i,deployments:n(s.deployments,Be),runtime:n(s.runtime,Ue)}]:[]}):[];return{enabledCount:"number"==typeof t.enabledCount&&Number.isInteger(t.enabledCount)&&t.enabledCount>=0?t.enabledCount:0,runningCount:"number"==typeof t.runningCount&&Number.isInteger(t.runningCount)&&t.runningCount>=0?t.runningCount:0,errorCount:"number"==typeof t.errorCount&&Number.isInteger(t.errorCount)&&t.errorCount>=0?t.errorCount:0,configs:s,recentRuns:n(t.recentRuns,Fe)}}(t.triggers,"string"==typeof t.id&&t.id),s.apps=$s(Ds.shape.apps.unwrap(),Bs(t.apps,["runningCount","problemCount","updatedAt","revision"]),0,0,"string"==typeof t.id&&t.id)||{runningCount:0,problemCount:0},s.config=$s(Ds.shape.config.unwrap(),Bs(t.config,["autoImportAgentConfigs","defaultCodexPermissionMode"]),0,0,"string"==typeof t.id&&t.id)||{autoImportAgentConfigs:!0,defaultCodexPermissionMode:"ask"},s.modelSelection=$s(Ht.unwrap(),Bs(t.modelSelection,["codexModelHash","claudeModelHash"]),0,0,"string"==typeof t.id&&t.id)||{},s.imageSnapshot=function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=ms(n),{reference:i,...r}=s,o=n.requestedReference??i;let a;try{a="string"==typeof o?Bt(o):void 0}catch{}const d={img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"},c="string"==typeof n.id?n.id:"string"==typeof t?t:void 0,l="market"===n.origin||c&&d[c]?"market":"custom";return void 0!==n.image||n.reference,{...r,id:c&&d[c]?d[c]:c,origin:l,repository:a?.repository??n.repository,tag:a?.tag??n.tag,requestedReference:a?.reference??o,resolvedDigest:n.resolvedDigest,resolvedReference:n.resolvedReference,downloadSizeBytes:n.downloadSizeBytes,market:"market"===l?Bs(n.market,["catalogId","catalogRevision","publisher","version"])??{catalogId:"task_handoff_embedded",catalogRevision:"legacy",publisher:"task-handoff"}:void 0}}(t.imageSnapshot,t.imageId,"string"==typeof t.id&&t.id);const i="string"==typeof t.imageId?{img_default:"market_taskhandoff_browser",img_codex:"market_taskhandoff_codex",img_ai:"market_taskhandoff_ai"}[t.imageId]||t.imageId:void 0,r=Bs(t.imageSelection,["imageId","tag"]);s.imageSelection=r||(i?{imageId:i,tag:s.imageSnapshot?.tag}:void 0),s.imageProvisioning=$s(as,Bs(t.imageProvisioning,["phase","requestedReference","generation","error","startedAt","updatedAt"]),0,0,"string"==typeof t.id&&t.id),s.runtimeVersion=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n=e=>{if("string"!=typeof e)return;const t=e.trim();return t&&t.length<=80?t:void 0},s=n(t.desiredVersion);if(!s)return;const i=new Set(wn.shape.phase.options),r="string"==typeof t.phase?t.phase:"pending",o=i.has(r)?r:{idle:"pending",queued:"pending",checking:"pending",downloading:"installing",updating:"installing",completed:"matched",succeeded:"matched",ready:"matched",error:"failed"}[r]||"pending";let a;if(t.error&&"object"==typeof t.error&&!Array.isArray(t.error)){const e=t.error,s="string"==typeof e.message?e.message.trim().slice(0,4096):"";if(s){const t=new Set(vn.shape.code.options),i="string"==typeof e.code&&t.has(e.code)?e.code:"INSTANCE_RUNTIME_INSTALL_FAILED";e.code;const r=n(e.expectedVersion),o=n(e.actualVersion);a={code:i,message:s,...r?{expectedVersion:r}:{},...o?{actualVersion:o}:{},retryable:!0===e.retryable}}}"string"==typeof t.error&&t.error.trim()&&(a={code:"INSTANCE_RUNTIME_INSTALL_FAILED",message:t.error.trim().slice(0,4096),expectedVersion:s,...n(t.actualVersion)?{actualVersion:n(t.actualVersion)}:{},retryable:!1});const d=n(t.actualVersion),c=_t.safeParse(t.lastAttemptAt),l=_t.safeParse(t.matchedAt);return void 0!==t.lastAttemptAt&&c.success,void 0!==t.matchedAt&&l.success,{desiredVersion:s,...d?{actualVersion:d}:{},phase:o,attempt:"number"==typeof t.attempt&&Number.isInteger(t.attempt)&&t.attempt>=0?t.attempt:0,...c.success?{lastAttemptAt:c.data}:{},...l.success?{matchedAt:l.data}:{},...a?{error:a}:{}}}(t.runtimeVersion,"string"==typeof t.id&&t.id),s.workspace=$s(js,t.workspace,0,0,"string"==typeof t.id&&t.id)||{status:"unknown"},s.runtime=$s(Ds.shape.runtime.unwrap(),Bs(t.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),0,0,"string"==typeof t.id&&t.id)||{labels:{}},s.target=$s(Os,Bs(t.target??t.endpoints,["strategy","web","api","vnc","tty","logs","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"direct-port",status:"unknown"},s.access=$s(Rs,Bs(t.access,["strategy","web","api","ws","status"]),0,0,"string"==typeof t.id&&t.id)||{strategy:"control-plane-proxy",status:"unknown"};for(const[e,t]of Object.entries(Ds.shape))Object.prototype.hasOwnProperty.call(s,e)&&!t.safeParse(s[e]).success&&t.safeParse(void 0).success&&delete s[e];return s}(s)));var s;if(n.success)return n.data.protocolVersion&&n.data.protocolVersion!==Tt?(this.client.logger?.warn?.({nodeId:e.id,instanceId:n.data.id,expectedProtocolVersion:Tt,actualProtocolVersion:n.data.protocolVersion,errorCode:"PROTOCOL_VERSION_MISMATCH"},"node instance protocol version mismatch"),{instance:n.data}):{instance:n.data};const i=t&&"object"==typeof t?t:{},r="string"==typeof i.id?i.id:void 0;return this.client.logger?.warn?.({nodeId:e.id,instanceId:r,errorCode:"NODE_INSTANCE_PAYLOAD_INVALID",issues:n.error.issues.map(e=>({path:e.path.join("."),message:e.message}))},"node instance payload invalid"),{error:{nodeId:e.id,route:"/instances",method:"GET",code:"NODE_INSTANCE_PAYLOAD_INVALID",message:r?`Node instance ${r} payload is invalid.`:"Node instance payload is invalid.",statusCode:502,issues:n.error.issues}}}}function pr(e){return e.readyState===(e.OPEN??1)}function ur(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}function gr(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}function mr(e,t,n){e.close(gr(t),ur(n)||void 0)}function hr(e,t,n,s){if(pr(e))for(const e of t.splice(0))if(s(e),!n(e))return}function fr(e,t,n={}){const s=n.pendingFrameLimit??256,i=n.maxFrameBytes??8388608,r=n.maxTotalBytes??1073741824,o=n.maxBufferedBytes??16777216;let a=!1,d=pr(t);const c=[],l=[];let p,u=0,g=0,m=0;const h=e=>Buffer.isBuffer(e)||e instanceof ArrayBuffer||ArrayBuffer.isView(e)?e.byteLength:Buffer.byteLength("string"==typeof e?e:String(e),"utf8"),f=(s,o)=>{const a=h(o);return m+=a,n.onFrame?.(s,a),!(a>i||m>r)||(mr(e,1009,"WebSocket bridge traffic limit exceeded."),mr(t,1009,"WebSocket bridge traffic limit exceeded."),!1)},y=()=>{p&&(clearTimeout(p),p=void 0)},I=(n,s)=>{const i=s.bytes;if((n.bufferedAmount??0)+i>o)return mr(e,1013,"WebSocket bridge consumer is too slow."),mr(t,1013,"WebSocket bridge consumer is too slow."),!1;try{return function(e,t){e.send(t.data,{binary:t.isBinary})}(n,s),!0}catch{return mr(e,1011,"WebSocket bridge send failed."),mr(t,1011,"WebSocket bridge send failed."),!1}},b=(n,i,r)=>{if(i.length>=s)return mr(e,1011,"WebSocket bridge pending frame limit exceeded."),void mr(t,1011,"WebSocket bridge pending frame limit exceeded.");const a="upstream"===n?u:g;if((("upstream"===n?t:e).bufferedAmount??0)+a+r.bytes>o)return mr(e,1013,"WebSocket bridge consumer is too slow."),void mr(t,1013,"WebSocket bridge consumer is too slow.");i.push(r),"upstream"===n?u+=r.bytes:g+=r.bytes};return n.upstreamOpenTimeoutMs&&!d&&(p=setTimeout(()=>{d||a||(mr(e,1011,"Upstream websocket did not open."),mr(t))},n.upstreamOpenTimeoutMs)),e.on("open",()=>hr(e,l,t=>I(e,t),e=>{g-=e.bytes})),t.on("open",()=>{y(),d=!0,n.onUpstreamOpen?.(),hr(t,c,e=>I(t,e),e=>{u-=e.bytes})}),e.on("message",(e,n=!1)=>{if(!f("client-to-upstream",e))return;const s={data:e,isBinary:Boolean(n),bytes:h(e)};pr(t)?I(t,s):b("upstream",c,s)}),t.on("message",(t,n=!1)=>{if(!f("upstream-to-client",t))return;const s={data:t,isBinary:Boolean(n),bytes:h(t)};pr(e)?I(e,s):b("client",l,s)}),t.on("close",(t,s)=>{y(),n.onUpstreamClose?.(t,s),(d||a||!n.onUpstreamCloseBeforeOpen?.())&&mr(e,t,s)}),t.on("error",t=>{n.onUpstreamError?.(t),(d||a||!n.onUpstreamErrorBeforeOpen?.(t))&&mr(e)}),e.on("close",(e,s)=>{y(),a=!0,n.onClientClose?.(e,s),mr(t,e,s)}),e.on("error",e=>{y(),a=!0,n.onClientError?.(e),mr(t,1011,"WebSocket bridge client failed.")}),{close:(n,s)=>{y(),mr(t,n,s),mr(e,n,s)}}}const yr="TASK_HANDOFF_NODE_AGENT_V1",Ir=6e4,br="/api/node-tunnel";function vr(e,t){if("paired-hmac"===e.auth.mode&&e.auth.secret){if(!e.auth.keyId){const t=new Error(`Node ${e.id} paired-HMAC auth is missing keyId.`);throw Object.assign(t,{statusCode:500,code:"NODE_AGENT_REMOTE_KEY_ID_MISSING"}),t}return function(e){const t=e.timestamp||(new Date).toISOString(),n=e.nonce||a.randomBytes(18).toString("base64url"),s=wr(e.body||""),i=Sr(e.secret,{keyId:e.keyId,method:e.method,pathWithQuery:e.pathWithQuery,timestamp:t,nonce:n,bodySha256:s});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":s,"x-taskhandoff-signature":i}}({nodeId:e.id,keyId:e.auth.keyId,secret:e.auth.secret,method:t.method,pathWithQuery:t.pathWithQuery,body:t.body})}const n="local-ipc"!==e.connectionMode&&"local-loopback"!==e.connectionMode||"local-static-key"!==e.auth.mode?void 0:e.auth.secret;return n?{authorization:`Bearer ${n}`}:{}}function wr(e=""){return a.createHash("sha256").update(e).digest("hex")}function Sr(e,t){return a.createHmac("sha256",e).update(function(e){return[yr,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function Ar(e){if(e.startsWith("ipc://"))return decodeURIComponent(e.slice(6))}function kr(e,t=process.platform){if(!function(e,t=process.platform){if("win32"===t)return e.startsWith("\\\\.\\pipe\\");const s=n.statSync(e),i=process.getuid?.();return void 0===i||s.uid===i}(e,t)){const e=new Error("Local node agent IPC socket is not owned by the current user.");throw Object.assign(e,{statusCode:403,code:"NODE_AGENT_IPC_OWNER_INVALID"}),e}}function Cr(e,t,n,s={}){const i=`/api/node-agent${t}`,r={createConnection:()=>u.connect(e),headers:{host:"task-handoff-node-agent.local",...s}};return n&&(Array.isArray(n)?n.length>0:n.trim().length>0)?new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,n,r):new l.WebSocket(`ws://task-handoff-node-agent.local${i}`,r)}async function xr(e,t,n,s={}){const i=Ar(t);return i?(kr(i),async function(e,t,n={}){const s=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),r=new Headers(n.headers||{});return void 0===i||r.has("content-length")||r.set("content-length",String(Buffer.byteLength(i))),new Promise((o,a)=>{const d=p.request({socketPath:e,method:s,path:`/api/node-agent${t}`,headers:Object.fromEntries(r.entries())},e=>{const t=e.statusCode||500;o(new Response("HEAD"!==s&&204!==t&&205!==t&&304!==t?g.Readable.toWeb(e):null,{status:t,headers:e.headers}))});d.on("error",a);const c=()=>d.destroy(new DOMException("The operation was aborted.","AbortError"));n.signal?.aborted?c():n.signal?.addEventListener("abort",c,{once:!0}),d.once("close",()=>n.signal?.removeEventListener("abort",c)),void 0!==i&&d.write(i),d.end()})}(i,n,s)):e(`${t.replace(/\/$/,"")}/api/node-agent${n}`,s)}function zr(e){const t=e.controlEndpoint||e.endpoint;if(!t){const e=new Error("Node agent direct HTTP mode requires an endpoint.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_ENDPOINT_REQUIRED"}),e}return t}function Tr(e,t,n,s){const i=zr(e),r=Ar(i),o=`/api/node-agent${t}`;if(r)return kr(r),Cr(r,t,n,s);const a=new URL(o,i.replace(/\/$/,""));return a.protocol="https:"===a.protocol?"wss:":"ws:",n?new l.WebSocket(a.toString(),n,{headers:s}):new l.WebSocket(a.toString(),{headers:s})}function Nr(e,t,n){const s=new Headers(n),i=vr(e,t);for(const[e,t]of Object.entries(i))"authorization"===e&&s.has(e)||s.set(e,t);return Object.fromEntries(s.entries())}function Er(e=fetch,t={}){const n=t.openWebSocket||Tr,s=async(t,n,s={})=>{const i=zr(t),r=s.method||"GET",o="string"==typeof s.body||s.body instanceof Buffer?s.body:void 0===s.body||null===s.body?void 0:String(s.body),a=Nr(t,{method:r,pathWithQuery:`/api/node-agent${n}`,body:o||""},s.headers);return xr(e,i,n,{...s,body:o,headers:a})};return{request:s,requestStream:s,proxyWebSocket(e,t,s,i,r={}){const o=Nr(e,{method:"GET",pathWithQuery:`/api/node-agent${s}`},r);fr(t,n(e,s,i,o),{onUpstreamError:()=>t.close(1011,"Instance websocket proxy failed."),onUpstreamErrorBeforeOpen:()=>!0})}}}class _r{direct;tunnel;proxy;constructor(e){this.direct=e.direct,this.tunnel=e.tunnel,this.proxy=e.proxy}setTunnel(e){this.tunnel=e}setProxy(e){this.proxy=e}resolve(e){if(!1===e.connectionEnabled){const e=new Error("Node agent connection is disabled locally.");throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_CONNECTION_DISABLED"}),e}if("control-plane-proxy"===e.connectionMode){if(!this.proxy){const e=new Error("Control-plane proxy transport is not available.");throw Object.assign(e,{statusCode:503,code:"CONTROL_PLANE_PROXY_TRANSPORT_UNAVAILABLE"}),e}return this.proxy}if("reverse-wss"!==e.connectionMode)return this.direct;if(!this.tunnel){const e=new Error("Reverse node agent transport is not available.");throw Object.assign(e,{statusCode:503,code:"NODE_AGENT_REVERSE_TRANSPORT_UNAVAILABLE"}),e}return this.tunnel}}function Pr(e,t,n,s){const i=new Error(e,void 0===s?void 0:{cause:s});return Object.assign(i,{code:t,statusCode:n,retryable:n>=500}),i}function Or(e,t){if("control-plane-proxy"!==e.connectionPath.kind)throw Pr("Control-plane proxy transport requires a proxy connection path.","CONTROL_PLANE_PROXY_CONFIGURATION_INVALID",400);const n=t(e);if(!n)throw Pr("Control-plane proxy credential is missing.","CONTROL_PLANE_PROXY_CREDENTIAL_REQUIRED",503);if(n.nodeId!==e.id||n.proxyBindingId!==e.connectionPath.proxyBindingId||n.targetNodeId!==e.connectionPath.targetNodeId)throw Pr("Control-plane proxy credential does not match the node connection identity.","CONTROL_PLANE_PROXY_CREDENTIAL_MISMATCH",409);return n}function Rr(e,t){const n=new Headers(t);n.set(nt,Ge),n.set(st,e.sourceControlPlaneId),n.set(it,e.bindingKeyId),n.set(rt,e.credential);const s=Ye.safeParse(n.get("x-request-id"));return n.set("x-request-id",s.success?s.data:`proxy_request_${a.randomUUID().replace(/-/g,"")}`),Object.fromEntries(n.entries())}function jr(e){const t=Ze.safeParse(e);if(t.success)return t.data;const n=Pr("Control-plane proxy route is invalid.",ot.RouteInvalid,400);throw Object.assign(n,{issues:t.error.issues}),n}function Dr(e,t,n){return t?new l.WebSocket(e,t,{headers:n}):new l.WebSocket(e,{headers:n})}class Mr{credentialForNode;fetchImpl;openWebSocket;constructor(e){this.credentialForNode=e.credentialForNode,this.fetchImpl=e.fetchImpl||fetch,this.openWebSocket=e.openWebSocket||Dr}async fetch(e,t,n={}){const s=Or(e,this.credentialForNode),i=function(e,t){const n=new URL(jr(t),"https://node-agent.invalid"),s=new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/http${n.pathname}`,e.proxyOrigin);return s.search=n.search,s}(s,t);try{return await this.fetchImpl(i,{...n,headers:Rr(s,n.headers)})}catch(e){if(function(e){return e instanceof Error&&("AbortError"===e.name||"ABORT_ERR"===e.code)}(e))throw e;throw Pr(`Trusted control-plane proxy ${s.proxyOrigin} is unavailable.`,"CONTROL_PLANE_PROXY_UNAVAILABLE",503,e)}}request(e,t,n={}){return this.fetch(e,t,n)}requestStream(e,t,n={}){return this.fetch(e,t,n)}proxyWebSocket(e,t,n,s,i={}){const r=Or(e,this.credentialForNode),o=this.openWebSocket(function(e,t){const n=new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/websocket`,e.proxyOrigin);return n.protocol="wss:",n.searchParams.set("route",jr(t)),n}(r,n).toString(),s,Rr(r,i));fr(t,o,{onUpstreamError:()=>t.close(1011,"Control-plane proxy websocket failed."),onUpstreamErrorBeforeOpen:()=>!0})}}const qr=new Set(Object.keys(xt.shape)),$r=new Set(Object.keys(zt.shape));function Lr(e,t,n){if(!e||"object"!=typeof e||Array.isArray(e))return e;const s=e,i=Object.keys(s).filter(e=>!t.has(e));return i.length&&n?.(i),Object.fromEntries([...t].filter(e=>e in s).map(e=>[e,s[e]]))}function Br(e){const{credential:t,...n}=e;return n}class Ur{pendingClaims;nodeCredentials;identity;constructor(e,t){this.pendingClaims=new ri(e.pendingClaimsDir,{schema:xt,sanitize:e=>function(e){return Lr(e,qr,e=>t?.("unknown stored pending proxy claim fields were ignored",{fields:e}))}(e),logger:t,directoryMode:448,fileMode:384}),this.nodeCredentials=new ri(e.nodeCredentialsDir,{schema:zt,sanitize:e=>function(e){return Lr(e,$r,e=>t?.("unknown stored proxy node credential fields were ignored",{fields:e}))}(e),logger:t,directoryMode:448,fileMode:384}),this.identity=new oi(e.identityPath,()=>({controlPlaneId:si("control_plane")}),{schema:o.z.object({controlPlaneId:o.z.string().trim().min(1).max(160)}).strip(),logger:t,directoryMode:448,fileMode:384})}init(e=Date.now()){return this.pendingClaims.init(),this.nodeCredentials.init(),this.identity.init(),this.gcPendingClaims(e)}controlPlaneId(){return this.identity.get().controlPlaneId}pendingClaimByClaimId(e){return this.pendingClaims.list().find(t=>t.claimId===e)}putPendingClaim(e){const t=this.pendingClaimByClaimId(e.claimId);if(!t)return this.pendingClaims.put(e);if(t.proxyOrigin!==e.proxyOrigin||t.requestedName!==e.requestedName||t.sourceControlPlaneId!==e.sourceControlPlaneId||t.targetNodeId!==e.targetNodeId||t.bindingKeyId!==e.bindingKeyId||t.credential!==e.credential){const e=new Error("Pending proxy claim identity conflicts with an existing claim.");throw Object.assign(e,{code:"CONTROL_PLANE_PROXY_BINDING_IDENTITY_CONFLICT",statusCode:409}),e}return t}publicPendingClaims(){return this.pendingClaims.list().map(Br)}nodeCredential(e){return this.nodeCredentials.list().find(t=>t.nodeId===e)}putNodeCredential(e){const t=this.nodeCredential(e.nodeId);if(t&&(t.proxyOrigin!==e.proxyOrigin||t.proxyBindingId!==e.proxyBindingId||t.targetNodeId!==e.targetNodeId||t.sourceControlPlaneId!==e.sourceControlPlaneId||t.bindingKeyId!==e.bindingKeyId||t.credential!==e.credential)){const e=new Error("Proxy node credential conflicts with the existing node identity.");throw Object.assign(e,{code:"CONTROL_PLANE_PROXY_NODE_IDENTITY_CONFLICT",statusCode:409}),e}return t||this.nodeCredentials.put(e)}deleteNodeCredential(e){const t=this.nodeCredential(e);return!!t&&this.nodeCredentials.delete(t.id)}gcNodeCredentials(e){const t=[];for(const n of this.nodeCredentials.list())e(n)||(this.nodeCredentials.delete(n.id),t.push(n.id));return t}promotePendingClaim(e,t){const n=this.pendingClaimByClaimId(e);if(!n)return;if(n.proxyOrigin!==t.proxyOrigin||n.sourceControlPlaneId!==t.sourceControlPlaneId||void 0!==n.targetNodeId&&n.targetNodeId!==t.targetNodeId||n.bindingKeyId!==t.bindingKeyId||n.credential!==t.credential){const e=new Error("Proxy node credential does not match the pending claim identity.");throw Object.assign(e,{code:"CONTROL_PLANE_PROXY_BINDING_IDENTITY_CONFLICT",statusCode:409}),e}const s=this.putNodeCredential(t);return this.pendingClaims.delete(n.id),s}markCompensationRequired(e){const t=this.pendingClaimByClaimId(e);if(t)return this.pendingClaims.patch(t.id,{status:"compensation-required"})}cancelPendingClaim(e,t){const n=this.pendingClaimByClaimId(e);return n?t?(this.pendingClaims.patch(n.id,{status:"compensation-required"}),{deleted:!1,compensationRequired:!0}):{deleted:this.pendingClaims.delete(n.id),compensationRequired:!1}:{deleted:!1,compensationRequired:!1}}completePendingClaimCompensation(e){const t=this.pendingClaimByClaimId(e);return!(!t||"compensation-required"!==t.status)&&this.pendingClaims.delete(t.id)}gcPendingClaims(e=Date.now()){const t=[];for(const n of this.pendingClaims.list()){if("compensation-required"===n.status)continue;const s=Date.parse(n.expiresAt);!Number.isFinite(s)||s>e||(this.pendingClaims.delete(n.id),t.push(n.id))}return t}}function Fr(e){if(!e||"object"!=typeof e)return;const t=e,n=yn.safeParse(t.build),s=Xt.safeParse(t.platform),i=Zt.safeParse(t.arch);return{..."boolean"==typeof t.ok?{ok:t.ok}:{},..."string"==typeof t.role?{role:t.role}:{},..."string"==typeof t.nodeId?{nodeId:t.nodeId}:{},..."string"==typeof t.protocolVersion?{protocolVersion:t.protocolVersion}:{},...s.success?{platform:s.data}:{},...i.success?{arch:i.data}:{},...n.success?{build:n.data}:{},..."string"==typeof t.serverTime?{serverTime:t.serverTime}:{}}}function Hr(e){const{agent:t,...n}=e.capabilities,{secret:s,...i}=e.auth,r=i.pairing?{...i.pairing,joinToken:void 0}:void 0,o=Fr(t);return{...e,auth:r?{...i,pairing:r}:i,capabilities:o?{...n,agent:o}:n}}function Wr(e){return"local-folder"===e.type?{mode:"local-bind",path:"/workspace",readOnly:!1}:{mode:"git-clone",path:"/workspace",readOnly:!1}}function Gr(e){return function(e){const{registrationToken:t,target:n,agentStatus:s,targetStatus:i,uiAccessStatus:r,...o}=e;return o}(function(e){const t=`/instances/${encodeURIComponent(e.id)}`,n={strategy:"control-plane-proxy",web:`${t}/`,api:`${t}/api`,ws:`${t}/api`,status:"online"===e.connectionStatus||"online"===e.agentStatus?"reachable":"endpoint-unreachable"};return{...e,access:n}}(e))}function Vr(e){const{key:t,...n}=e;return{...n,keyPreview:(s=e.key,s.length<=8?"set":`${s.slice(0,4)}...${s.slice(-4)}`),keySet:!0};var s}function Kr(){return(new Date).toISOString()}function Jr(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}function Qr(e){const t=String(e||"").split(":");if(3===t.length&&"ai"===t[1]&&t[0]&&t[2])return{instanceId:t[0],aiSessionId:t[2]}}function Yr(e){return e instanceof Error?e.message:String(e)}function Xr(e,t){const n=new Error(t);throw Object.assign(n,{statusCode:404,code:e}),n}const Zr=o.z.object({proxyOrigin:Xe,inviteToken:o.z.string().trim().min(24).max(512),name:o.z.string().trim().min(1).max(160).optional()}).strict(),eo=o.z.object({data:o.z.object({binding:ft,closed:o.z.object({abortedRequests:o.z.number().int().nonnegative(),closedSockets:o.z.number().int().nonnegative()}).strict()}).strict()}).strict();class to{claimOperations=new Map;options;constructor(e){this.options=e}listPendingClaims(){return this.options.privateStore.publicPendingClaims()}async claimNode(e){const t=Zr.parse(e),n=Kr(),s=si("proxy_claim"),i={id:s,claimId:s,proxyOrigin:t.proxyOrigin,...t.name?{requestedName:t.name}:{},sourceControlPlaneId:this.options.privateStore.controlPlaneId(),bindingKeyId:si("proxy_key"),credential:ii(),status:"pending",createdAt:n,updatedAt:n,expiresAt:new Date(Date.now()+864e5).toISOString()};return this.options.privateStore.putPendingClaim(i),this.withClaimLock(i.claimId,()=>this.completeClaim(i,t.inviteToken))}async resumeClaim(e){return this.withClaimLock(e,async()=>{const t=this.options.privateStore.pendingClaimByClaimId(e);return t||Xr("CONTROL_PLANE_PROXY_CLAIM_NOT_FOUND",`Proxy claim ${e} was not found.`),this.completeClaim(t)})}async cancelClaim(e){return this.withClaimLock(e,async()=>{const t=this.options.privateStore.pendingClaimByClaimId(e);if(!t)return{deleted:!1,compensationRequired:!1,remoteRevoke:"not-required"};if("pending"===t.status)return{...this.options.privateStore.cancelPendingClaim(e,!1),remoteRevoke:"not-required"};let n;try{n=await this.requestClaim(t)}catch(e){throw this.compensationError(t,"Trusted control-plane proxy is unavailable; claim cancellation requires retry.",e)}const s=await this.responsePayload(n);if(!n.ok){const i=s?.error?.code;if(i===ot.InviteInvalid||i===ot.BindingRevoked)return{deleted:this.options.privateStore.completePendingClaimCompensation(e),compensationRequired:!1,remoteRevoke:i===ot.BindingRevoked?"already-revoked":"not-created"};throw this.compensationError(t,s?.error?.message||`Proxy claim recovery failed with HTTP ${n.status}.`,void 0,n.status,i,s?.error?.retryable)}const i=Ct.parse(s?.data);this.requireClaimResultIdentity(t,i);const r=this.compensationCredential(t,i);let o;try{o=await this.revokeBinding(r)}catch(e){throw this.compensationError(t,"Trusted control-plane proxy is unavailable; binding revocation requires retry.",e)}const a=await this.responsePayload(o);if(!o.ok&&a?.error?.code!==ot.BindingRevoked)throw this.compensationError(t,a?.error?.message||`Proxy binding revoke failed with HTTP ${o.status}.`,void 0,o.status,a?.error?.code,a?.error?.retryable);if(o.ok)try{this.requireRevocationReceipt(a,r)}catch(e){throw this.compensationError(t,"Proxy binding revocation returned an invalid receipt; compensation requires retry.",e,502,ot.TransportFailed,!0)}return{deleted:this.options.privateStore.completePendingClaimCompensation(e),compensationRequired:!1,remoteRevoke:o.ok?"revoked":"already-revoked"}})}applyTargetSnapshot(e,t){const n=this.requireProxyNodeIdentity(e,t.binding.id,t.binding.targetNodeId);return this.putTargetState(n,t.target,{reachability:"reachable",bindingStatus:t.binding.status,bindingRevision:t.binding.revision,streamId:t.streamId,revision:t.revision,observedAt:t.observedAt})}applyTargetEvent(e,t){const n=this.requireProxyNodeIdentity(e,void 0,t.targetNodeId),s=n.proxyState;if(!s?.streamId||s.streamId!==t.streamId||void 0===s.revision||t.revision!==s.revision+1){const e=new Error("Proxy target event does not continue the authoritative snapshot cursor.");throw Object.assign(e,{code:ot.SnapshotRequired,statusCode:409,retryable:!0}),e}return this.putTargetState(n,t.target,{...s,reachability:"reachable",bindingStatus:"revoked"===s.bindingStatus?"revoked":"active",revision:t.revision,observedAt:t.event.createdAt})}markUnavailable(e,t){const n=this.requireProxyNodeIdentity(e),s=Kr();return this.options.nodes.put(ys.parse({...n,status:"degraded",health:"degraded",proxyState:{...n.proxyState,reachability:"unreachable",bindingStatus:n.proxyState?.bindingStatus??"unknown",lastError:t,updatedAt:s},updatedAt:s}))}markBindingRevoked(e,t){const n=this.requireProxyNodeIdentity(e),s=Kr();return this.options.nodes.put(ys.parse({...n,status:"degraded",health:"degraded",proxyState:{...n.proxyState,reachability:"reachable",bindingStatus:"revoked",lastError:t,updatedAt:s},updatedAt:s}))}assertIdentityPatch(e,t){const n=t.connectionMode??e.connectionMode,s=t.connectionPath??e.connectionPath,i=t.auth??e.auth,r="control-plane-proxy"===e.connectionMode||"control-plane-proxy"===e.connectionPath.kind||"proxy-binding"===e.auth.mode,o="control-plane-proxy"===n||"control-plane-proxy"===s.kind||"proxy-binding"===i.mode,a=n!==e.connectionMode||JSON.stringify(s)!==JSON.stringify(e.connectionPath)||JSON.stringify(i)!==JSON.stringify(e.auth);if((r||o)&&a){const e=new Error("Proxy node connection identity can only be changed through the control-plane proxy lifecycle API.");throw Object.assign(e,{statusCode:409,code:"CONTROL_PLANE_PROXY_IDENTITY_IMMUTABLE",retryable:!1}),e}}async deleteNode(e,t=!1){if("control-plane-proxy"!==e.connectionMode)return{deleted:this.options.deleteNode(e.id),revoke:{mode:"not-proxied",orphanRisk:!1}};const n=this.options.privateStore.nodeCredential(e.id);if(!n){if(!t){const e=new Error("Proxy node credential is missing; remote binding cannot be revoked.");throw Object.assign(e,{statusCode:409,code:"CONTROL_PLANE_PROXY_CREDENTIAL_REQUIRED",retryable:!1,details:{forceDeleteAllowed:!0,forceDeleteReason:"credential-missing"}}),e}return this.forceDeleteNode(e.id)}let s;try{s=await this.revokeBinding(n)}catch(n){if(t)return this.forceDeleteNode(e.id);const s=new Error("Trusted control-plane proxy is unavailable; the binding was not revoked.",{cause:n});throw Object.assign(s,{statusCode:503,code:"CONTROL_PLANE_PROXY_REVOKE_UNAVAILABLE",retryable:!0,details:{forceDeleteAllowed:!0,forceDeleteReason:"proxy-unavailable"}}),s}const i=await this.responsePayload(s);if(!s.ok){if(i?.error?.code===ot.BindingRevoked){const t=i.error.details?.bindingId;if(t===n.proxyBindingId)return this.finishRevokedNodeDelete(e.id)}const t=new Error(i?.error?.message||`Proxy binding revoke failed with HTTP ${s.status}.`);throw Object.assign(t,{statusCode:s.status,code:i?.error?.code||"CONTROL_PLANE_PROXY_REVOKE_FAILED",retryable:i?.error?.retryable??s.status>=500,details:{...(r=i?.error?.details,r&&"object"==typeof r&&!Array.isArray(r)?r:{}),forceDeleteAllowed:!1}}),t}var r;try{this.requireRevocationReceipt(i,n)}catch(e){if(e&&"object"==typeof e){const t=e;t.details={...t.details||{},forceDeleteAllowed:!1}}throw e}return this.finishRevokedNodeDelete(e.id)}async completeClaim(e,t){let n;this.options.privateStore.markCompensationRequired(e.claimId);try{n=await this.requestClaim(e,t)}catch(t){const n=new Error(`Trusted control-plane proxy ${e.proxyOrigin} is unavailable.`,{cause:t});throw Object.assign(n,{statusCode:503,code:"CONTROL_PLANE_PROXY_UNAVAILABLE",retryable:!0,claimId:e.claimId}),n}const s=await this.responsePayload(n);if(!n.ok){const t=s?.error?.retryable??n.status>=500;t||this.options.privateStore.completePendingClaimCompensation(e.claimId);const i=new Error(s?.error?.message||`Proxy claim failed with HTTP ${n.status}.`);throw Object.assign(i,{statusCode:n.status,code:s?.error?.code||"CONTROL_PLANE_PROXY_CLAIM_FAILED",retryable:t,details:s?.error?.details,claimId:e.claimId}),i}const i=Ct.parse(s?.data);this.requireClaimResultIdentity(e,i);const r=this.options.nodes.get(i.target.id),o="control-plane-proxy"===r?.connectionMode&&"control-plane-proxy"===r.connectionPath.kind&&r.connectionPath.proxyBindingId===i.binding.id&&r.connectionPath.targetNodeId===i.target.id;if(r&&!o){this.options.privateStore.markCompensationRequired(e.claimId);const t=new Error(`Node ${i.target.id} already exists with another connection identity.`);throw Object.assign(t,{statusCode:409,code:"CONTROL_PLANE_PROXY_NODE_IDENTITY_CONFLICT",claimId:e.claimId}),t}const a={id:`proxy_credential_${i.target.id}`,nodeId:i.target.id,proxyOrigin:e.proxyOrigin,proxyBindingId:i.binding.id,targetNodeId:i.target.id,sourceControlPlaneId:e.sourceControlPlaneId,bindingKeyId:e.bindingKeyId,credential:e.credential,createdAt:e.createdAt,updatedAt:Kr()},d=r||ys.parse({id:i.target.id,name:e.requestedName||i.target.name,connectionMode:"control-plane-proxy",connectionPath:{kind:"control-plane-proxy",proxyId:new URL(e.proxyOrigin).host,proxyBindingId:i.binding.id,targetNodeId:i.target.id},connectionEnabled:!0,auth:{mode:"proxy-binding"},status:i.target.status,health:i.target.health,capabilities:i.target.capabilities,proxyState:{reachability:"reachable",bindingStatus:i.binding.status,bindingRevision:i.binding.revision,observedAt:Kr(),target:i.target,updatedAt:Kr()},labels:{},lastSeenAt:i.target.lastSeenAt,createdAt:Kr(),updatedAt:Kr()});r||this.options.nodes.put(d);try{this.options.privateStore.promotePendingClaim(e.claimId,a)}catch(e){throw r||this.options.nodes.delete(d.id),e}return{node:Hr(d),binding:i.binding}}requireProxyNodeIdentity(e,t,n){const s=this.options.requireNode(e);if("control-plane-proxy"!==s.connectionMode||"control-plane-proxy"!==s.connectionPath.kind||void 0!==t&&s.connectionPath.proxyBindingId!==t||void 0!==n&&s.connectionPath.targetNodeId!==n){const e=new Error("Proxy state identity does not match the node connection path.");throw Object.assign(e,{code:ot.BindingIdentityConflict,statusCode:409,retryable:!1}),e}return s}putTargetState(e,t,n){const s=Kr();return this.options.nodes.put(ys.parse({...e,name:t.name,status:t.status,health:t.health,capabilities:t.capabilities,lastSeenAt:t.lastSeenAt,proxyState:{...n,target:t,updatedAt:s},updatedAt:s}))}requestClaim(e,t){return this.options.fetchImpl(new URL("/api/node-proxy/claims",e.proxyOrigin),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({protocolVersion:Ge,...t?{inviteToken:t}:{},claimId:e.claimId,sourceControlPlaneId:e.sourceControlPlaneId,...e.targetNodeId?{targetNodeId:e.targetNodeId}:{},bindingKeyId:e.bindingKeyId,credential:e.credential})})}revokeBinding(e){return this.options.fetchImpl(new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}`,e.proxyOrigin),{method:"DELETE",headers:Rr(e)})}async responsePayload(e){return e.json().catch(()=>{})}requireRevocationReceipt(e,t){const n=eo.safeParse(e),s=n.success?n.data.data.binding:void 0;if(!s||s.id!==t.proxyBindingId||s.sourceControlPlaneId!==t.sourceControlPlaneId||s.targetNodeId!==t.targetNodeId||s.bindingKeyId!==t.bindingKeyId||"revoked"!==s.status){const e=new Error("Proxy binding revocation receipt does not match the local binding identity.");throw Object.assign(e,{statusCode:502,code:ot.TransportFailed,retryable:!0,details:{bindingId:t.proxyBindingId}}),e}return s}requireClaimResultIdentity(e,t){if(t.binding.claimId!==e.claimId||t.binding.sourceControlPlaneId!==e.sourceControlPlaneId||t.binding.bindingKeyId!==e.bindingKeyId||t.binding.targetNodeId!==t.target.id||void 0!==e.targetNodeId&&t.binding.targetNodeId!==e.targetNodeId){const t=new Error("Proxy claim response does not match the persisted pending identity.");throw Object.assign(t,{statusCode:409,code:ot.BindingIdentityConflict,retryable:!1,claimId:e.claimId,compensationRequired:!0}),t}}compensationCredential(e,t){return{id:`proxy_compensation_${t.binding.id}`,nodeId:t.target.id,proxyOrigin:e.proxyOrigin,proxyBindingId:t.binding.id,targetNodeId:t.target.id,sourceControlPlaneId:e.sourceControlPlaneId,bindingKeyId:e.bindingKeyId,credential:e.credential,createdAt:e.createdAt,updatedAt:Kr()}}compensationError(e,t,n,s=503,i="CONTROL_PLANE_PROXY_COMPENSATION_REQUIRED",r=!0){const o=new Error(t,void 0===n?void 0:{cause:n});return Object.assign(o,{statusCode:s,code:i,retryable:r,claimId:e.claimId,compensationRequired:!0}),o}async withClaimLock(e,t){const n=(this.claimOperations.get(e)??Promise.resolve()).catch(()=>{}).then(t);this.claimOperations.set(e,n);try{return await n}finally{this.claimOperations.get(e)===n&&this.claimOperations.delete(e)}}finishRevokedNodeDelete(e){const t=this.options.deleteNode(e);return this.options.privateStore.deleteNodeCredential(e),{deleted:t,revoke:{mode:"revoked",orphanRisk:!1}}}forceDeleteNode(e){const t=this.options.deleteNode(e);return this.options.privateStore.deleteNodeCredential(e),{deleted:t,revoke:{mode:"forced",orphanRisk:!0}}}}const no=o.z.object({id:jn.shape.id.optional(),name:jn.shape.name,source:Pn,defaultImageSelection:jn.shape.defaultImageSelection,defaultNodeId:jn.shape.defaultNodeId,defaultRuntimeId:jn.shape.defaultRuntimeId,workspacePolicy:On.optional(),labels:jn.shape.labels.optional()}).strict(),so=no.omit({id:!0}).partial().strict();function io(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"/"!==e&&"\\"!==e}const ro=o.z.string().refine(io,"Mention trigger must be one printable non-alphanumeric character other than / or \\.");function oo(e){return 1===Array.from(e).length&&!/[\p{L}\p{N}\s]/u.test(e)&&"\\"!==e}const ao=o.z.string().refine(oo,"Command trigger must be one printable non-alphanumeric character other than \\."),co=o.z.object({publicBaseUrl:o.z.string().trim().max(2048).optional(),updateChannel:In.default("stable"),mentionTrigger:ro.default("@"),commandTrigger:ao.default("/")}).strict().refine(e=>e.mentionTrigger!==e.commandTrigger,{path:["commandTrigger"],message:"Command and mention triggers must be different."}),lo=o.z.object({publicBaseUrl:o.z.string().trim().max(2048).optional(),updateChannel:In.optional(),mentionTrigger:ro.optional(),commandTrigger:ao.optional()}).strict();function po(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t={...e};return"string"==typeof t.mentionTrigger&&io(t.mentionTrigger)||(t.mentionTrigger="@"),"string"==typeof t.commandTrigger&&oo(t.commandTrigger)&&t.commandTrigger!==t.mentionTrigger||(t.commandTrigger="/"),t}const uo=o.z.object({id:ss.shape.id.optional(),name:ss.shape.name,description:ss.shape.description,cover:ss.shape.cover,reference:ss.shape.reference,pullPolicy:ss.shape.pullPolicy.optional(),capabilities:o.z.array(o.z.string().trim().min(1).max(80)).optional(),optionalApps:o.z.array(o.z.string().trim().min(1).max(120)).optional(),defaultEnv:ss.shape.defaultEnv.optional(),labels:ss.shape.labels.optional()}).strict(),go=uo.omit({id:!0}).partial().strict(),mo=o.z.object({name:Fs.shape.name.optional(),channel:Fs.shape.channel.optional(),enabled:o.z.boolean().optional(),token:Fs.shape.token,defaultChatId:Fs.shape.defaultChatId,allowedUserIds:o.z.array(o.z.string().trim().min(1).max(240)).optional(),pollIntervalMs:Fs.shape.pollIntervalMs.optional(),settings:Fs.shape.settings.optional()}).strict(),ho=mo.extend({channel:Fs.shape.channel,name:Fs.shape.name.optional()}),fo=$e.extend({id:$e.shape.configHash}),yo=o.z.object({name:$e.shape.name,description:$e.shape.description,source:De,action:Me,policy:qe.partial().optional()}).strict(),Io=o.z.object({instanceIds:o.z.array(Ds.shape.id).min(1),target:Le,enabled:o.z.boolean().optional()}).strict(),bo=o.z.object({configHash:$e.shape.configHash,enabled:o.z.boolean().optional()}).strict(),vo=o.z.object({mode:o.z.enum(["local-static-key","paired-hmac","proxy-binding"]).default("local-static-key"),keyId:ys.shape.id.optional(),secret:o.z.string().trim().max(4096).optional(),pairedAt:ys.shape.createdAt.optional(),pairing:o.z.object({status:o.z.enum(["pending","paired","expired"]).default("pending"),joinToken:o.z.string().trim().max(4096).optional(),expiresAt:ys.shape.createdAt.optional()}).strict().optional()}).strict(),wo=o.z.object({autoImportAgentConfigs:o.z.boolean().optional(),defaultCodexPermissionMode:G.optional()}).strict(),So=o.z.object({id:Ds.shape.id.optional(),name:Ds.shape.name.optional(),projectId:Ds.shape.projectId,source:Pn.optional(),sourceSnapshot:o.z.record(o.z.string(),o.z.unknown()).optional(),nodeId:Ds.shape.nodeId.optional(),runtimeId:Ds.shape.runtimeId.optional(),imageSelection:Ds.shape.imageSelection,config:wo.optional(),modelSelection:Ds.shape.modelSelection.optional(),start:o.z.boolean().default(!1)}).strict(),Ao=o.z.object({name:Ds.shape.name.optional(),config:wo.optional(),modelSelection:Ds.shape.modelSelection.optional()}).strict(),ko=o.z.object({name:Dn.shape.name,endpoint:Dn.shape.endpoint,key:Dn.shape.key,model:Dn.shape.model,app:Dn.shape.app,enabled:Dn.shape.enabled.optional(),order:Dn.shape.order.optional(),labels:Dn.shape.labels.optional()}).strict(),Co=ko.partial().strict(),xo=o.z.object({id:ys.shape.id.optional(),name:ys.shape.name,connectionMode:ys.shape.connectionMode.optional(),connectionPath:ys.shape.connectionPath.optional(),connectionEnabled:ys.shape.connectionEnabled.optional(),auth:vo.optional(),joinToken:o.z.string().trim().min(1).max(4096).optional(),endpoint:ys.shape.endpoint,controlEndpoint:ys.shape.controlEndpoint,containerEndpoint:ys.shape.containerEndpoint,publicWebBase:ys.shape.publicWebBase,status:o.z.enum(["unknown","online","offline","degraded"]).optional(),health:o.z.enum(["unknown","ok","degraded","failed"]).optional(),capabilities:ys.shape.capabilities.optional(),labels:ys.shape.labels.optional(),lastSeenAt:ys.shape.lastSeenAt}).strict(),zo=o.z.object({name:ys.shape.name.optional(),connectionMode:ys.shape.connectionMode.unwrap().optional(),connectionPath:ys.shape.connectionPath.unwrap().optional(),connectionEnabled:ys.shape.connectionEnabled.unwrap().optional(),auth:vo.optional(),endpoint:ys.shape.endpoint,controlEndpoint:ys.shape.controlEndpoint,containerEndpoint:ys.shape.containerEndpoint,publicWebBase:ys.shape.publicWebBase,status:ys.shape.status.unwrap().optional(),health:ys.shape.health.unwrap().optional(),capabilities:ys.shape.capabilities.unwrap().optional(),labels:ys.shape.labels.unwrap().optional(),lastSeenAt:ys.shape.lastSeenAt}).strict(),To=o.z.object({controlPlaneUrl:o.z.string().trim().url().max(2048),joinToken:o.z.string().trim().min(1).max(4096),controlPlaneName:o.z.string().trim().min(1).max(160).optional(),activate:o.z.boolean().optional()}).strict(),No=o.z.object({nodeName:o.z.string().trim().min(1).max(160).optional(),expiresInMs:o.z.number().int().positive().max(36e5).optional()}).strict(),Eo=o.z.object({id:o.z.string().min(1),endpoint:o.z.string().min(1),nodeId:o.z.string().min(1),keyId:o.z.string().min(1),secret:o.z.string().min(1),pairedAt:o.z.string().min(1),createdAt:o.z.string().min(1),updatedAt:o.z.string().min(1)}).strict();class _o{options;activePendingPairingRevokes=new Set;constructor(e){this.options=e}async syncLocal(){const e={[this.options.localNodeLabel]:"true",[this.options.builtinNodeLabel]:"true"},t=process.env.TASK_HANDOFF_NODE_AGENT_TOKEN?.trim()||void 0,n=process.env.TASK_HANDOFF_NODE_AGENT_CONTROL_ENDPOINT||process.env.TASK_HANDOFF_NODE_AGENT_ENDPOINT||"http://127.0.0.1:8091",s=function(e){return Ar(e)?"local-ipc":"local-loopback"}(n),i=ys.parse({id:"node_local_probe",name:"Local Node Probe",connectionMode:s,auth:{mode:"local-static-key",secret:t},labels:e,createdAt:Kr(),updatedAt:Kr()}),r=await this.inspect(n,i),o=Kr(),a=this.options.nodes.get(r.nodeId);for(const e of this.options.nodes.list())"true"===e.labels[this.options.localNodeLabel]&&e.id!==r.nodeId&&this.options.nodes.delete(e.id);return this.options.nodes.put(ys.parse({...a||{},id:r.nodeId,name:a?.name||"Local Node",connectionMode:s,auth:{mode:"local-static-key",secret:t},endpoint:n,controlEndpoint:n,containerEndpoint:process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,publicWebBase:process.env.TASK_HANDOFF_INSTANCE_PUBLIC_WEB_BASE,status:"online",health:"ok",capabilities:{...a?.capabilities||{},agent:Fr(r.data)},labels:{...a?.labels||{},...e},createdAt:a?.createdAt||o,updatedAt:o}))}async create(e){const t=xo.parse(e),n=Kr(),s=t.connectionMode||"direct-http";let i="auth"in(e&&"object"==typeof e&&!Array.isArray(e)?e:{})?t.auth:{mode:"local-ipc"===s||"local-loopback"===s?"local-static-key":"paired-hmac"};const r="direct-http"===s||"reverse-wss"===s;if(r&&"paired-hmac"!==i.mode)throw Po("Remote node connections require paired-HMAC authentication.","NODE_AGENT_REMOTE_REQUIRES_PAIRED_HMAC");const o=t.controlEndpoint||t.endpoint;if(!o&&"reverse-wss"!==s)throw Po("Node agent direct HTTP mode requires an endpoint.","NODE_AGENT_ENDPOINT_REQUIRED");const a=t.joinToken&&o?await this.completePairing(o,t.joinToken):void 0;let d,c=!1;try{if(d=a&&o?this.pendingPairingRevoke(o,a):void 0,d&&(this.activePendingPairingRevokes.add(d.id),this.options.pendingPairingRevokes?.put(d)),a){if(a.invalidResponse||a.unsafePublicIdentifiers)throw Object.assign(new Error("Node agent pairing response reflected secret material in a public identifier."),{statusCode:502,code:"NODE_AGENT_PAIRING_RESPONSE_INVALID"});i={mode:"paired-hmac",keyId:a.keyId,secret:a.secret,pairedAt:a.pairedAt,pairing:{status:"paired"}}}if(r&&!i.secret)throw Po("Remote node connections require a paired node secret or join token.","NODE_AGENT_REMOTE_SECRET_REQUIRED");if(r&&!i.keyId)throw Po("Remote node connections require a paired key id.","NODE_AGENT_REMOTE_KEY_ID_REQUIRED");const e=t.id||a?.nodeId||si("node"),u=ys.parse({id:e,name:t.name,connectionMode:s,auth:i,labels:t.labels||{},createdAt:n,updatedAt:n}),g="reverse-wss"===s&&(t.id||a?.nodeId)?void 0:await this.inspect(o||"",u),m=t.id||a?.nodeId||g?.nodeId||e;if(!m)throw Po("Node id is required.","NODE_ID_REQUIRED");if(a&&t.id&&t.id!==a.nodeId)throw Po(`Paired node id ${a.nodeId} does not match requested node id ${t.id}.`,"NODE_AGENT_ID_MISMATCH");if(t.id&&g?.nodeId&&g.nodeId!==t.id)throw Po(`Node agent id ${g.nodeId} does not match requested node id ${t.id}.`,"NODE_AGENT_ID_MISMATCH");if(a&&g?.nodeId&&g.nodeId!==a.nodeId)throw Object.assign(new Error("Node agent health identity did not match the completed pairing."),{statusCode:502,code:"NODE_AGENT_PAIRING_IDENTITY_MISMATCH"});const{joinToken:h,...f}=t,y=ys.parse({...f,id:m,connectionMode:s,auth:i,endpoint:o,controlEndpoint:o,status:t.status||(g?"online":"unknown"),health:t.health||(g?"ok":"unknown"),capabilities:{...t.capabilities||{},...g?{agent:Fr(g.data)}:{}},labels:t.labels||{},createdAt:n,updatedAt:n});if(a&&(l=y,p=a.secret,Mo(Hr(l),p)))throw Object.assign(new Error("Node agent health response reflected pairing secret material."),{statusCode:502,code:"NODE_AGENT_PAIRING_SECRET_REFLECTED"});c=!0;const I=this.options.nodes.put(y);return this.clearPendingPairingRevoke(d,a),I}catch(e){if(a&&o){const t=this.pairingPersistence(a);if("present"===t.state)return this.clearPendingPairingRevoke(d,a),t.node;if("unknown"===t.state&&c)throw qo(Object.assign(new Error("Node creation persistence outcome is uncertain; pairing compensation is pending."),{statusCode:503,code:"NODE_AGENT_PAIRING_PERSISTENCE_UNCERTAIN",retryable:!0}),a.secret);throw await this.compensatePairing(o,a,e),this.clearPendingPairingRevoke(d,a),qo(e,a.secret)}throw e}finally{d&&this.activePendingPairingRevokes.delete(d.id)}var l,p}async recoverPendingPairingRevokes(){const e=(this.options.pendingPairingRevokes?.list()||[]).filter(e=>!this.activePendingPairingRevokes.has(e.id));await Promise.all(e.map(async e=>{try{const t={nodeId:e.nodeId,keyId:e.keyId,secret:e.secret,pairedAt:e.pairedAt,invalidResponse:!1,unsafePublicIdentifiers:!1},n=this.pairingPersistence(t);if("present"===n.state)return void this.clearPendingPairingRevoke(e,t);if("unknown"===n.state)return;await this.compensatePairing(e.endpoint,t,Object.assign(new Error("Recovering a pending pairing revocation."),{code:"NODE_AGENT_PAIRING_REVOCATION_PENDING"})),this.clearPendingPairingRevoke(e,t)}catch{}}))}pendingPairingRevoke(e,t){if(!this.options.pendingPairingRevokes)return;const n=Kr();return Eo.parse({id:si("pairing_revoke"),endpoint:e,nodeId:t.nodeId,keyId:t.keyId,secret:t.secret,pairedAt:t.pairedAt,createdAt:n,updatedAt:n})}pairingPersistence(e){let t;try{t=this.options.nodes.get(e.nodeId)}catch{return{state:"unknown"}}return"paired-hmac"===t?.auth.mode&&t.auth.keyId===e.keyId&&t.auth.secret===e.secret?{state:"present",node:t}:{state:"absent"}}clearPendingPairingRevoke(e,t){if(e)try{this.options.pendingPairingRevokes?.delete(e.id)}catch(e){try{this.options.warn({nodeId:t?jo(t.nodeId,t.secret):void 0,keyId:t?jo(t.keyId,t.secret):void 0,errorCode:"NODE_AGENT_PAIRING_OUTBOX_CLEANUP_FAILED"},"node agent pairing outbox cleanup failed; recovery will retry")}catch{}}}async inspect(e,t){const n="/health",s=t?vr(t,{method:"GET",pathWithQuery:`/api/node-agent${n}`}):{},i=await xr(this.options.fetchImpl,e,n,{headers:s}),r=await i.json().catch(()=>({}));if(!i.ok)throw this.options.warn(Do({nodeEndpoint:e,statusCode:i.status,errorCode:"NODE_AGENT_HEALTH_FAILED"},t?.auth.secret),"node agent health check failed"),Object.assign(new Error(r.error?.message||`Node agent health check failed with HTTP ${i.status}`),{statusCode:i.status,code:"NODE_AGENT_HEALTH_FAILED"});const o="string"==typeof r.data?.nodeId&&r.data.nodeId.trim()?r.data.nodeId.trim():void 0;if(!o)throw this.options.warn(Do({nodeEndpoint:e,errorCode:"NODE_AGENT_ID_MISSING"},t?.auth.secret),"node agent health response missing node id"),Object.assign(new Error("Node agent health response did not include nodeId."),{statusCode:502,code:"NODE_AGENT_ID_MISSING"});const a=yn.safeParse(r.data?.build);return r.data?.protocolVersion!==Tt&&this.options.warn(Do({nodeId:o,nodeEndpoint:e,expectedProtocolVersion:Tt,actualProtocolVersion:r.data?.protocolVersion,build:a.success?a.data:void 0,errorCode:"PROTOCOL_VERSION_MISMATCH"},t?.auth.secret),"node agent protocol version mismatch"),this.options.info(Do({nodeId:o,nodeEndpoint:e,protocolVersion:r.data.protocolVersion,build:a.success?a.data:void 0},t?.auth.secret),"node agent health check ok"),{nodeId:o,data:r.data||{}}}async completePairing(e,t){const n=await this.options.fetchImpl(`${e.replace(/\/$/,"")}/api/node-agent/pairing/complete`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({joinToken:t,controlPlaneName:"Control Plane"})}),s=await n.json().catch(()=>({}));if(!n.ok)throw Object.assign(new Error(s.error?.message||`Node agent pairing failed with status ${n.status}.`),{statusCode:n.status,code:"NODE_AGENT_PAIRING_FAILED"});const i=s.data;if("string"!=typeof i?.nodeId||!i.nodeId.trim()||"string"!=typeof i?.keyId||!i.keyId.trim()||"string"!=typeof i?.secret||!i.secret.trim())throw Object.assign(new Error("Node agent pairing response did not include nodeId, keyId, and secret."),{statusCode:502,code:"NODE_AGENT_PAIRING_RESPONSE_INVALID"});const r=As.safeParse(i),o=i.nodeId.trim(),a=i.keyId.trim(),d=i.secret.trim();return{nodeId:o,keyId:a,secret:d,pairedAt:r.success?r.data.pairedAt:Kr(),invalidResponse:!r.success,unsafePublicIdentifiers:o.includes(d)||a.includes(d)}}async compensatePairing(e,t,n){const s="/pairing/current",i=`/api/node-agent${s}`,r={id:t.nodeId,connectionMode:"direct-http",auth:{mode:"paired-hmac",keyId:t.keyId,secret:t.secret}};try{const o=await xr(this.options.fetchImpl,e,s,{method:"DELETE",headers:vr(r,{method:"DELETE",pathWithQuery:i}),signal:AbortSignal.timeout(1e4)}),a=await o.json().catch(()=>({}));if(!o.ok)throw Object.assign(new Error(`Node agent pairing revoke failed with HTTP ${o.status}.`),{statusCode:o.status,code:Ro(a.error?.code,t.secret,"NODE_AGENT_PAIRING_REVOKE_FAILED"),retryable:408===o.status||429===o.status||o.status>=500});const d=Cs.safeParse(a.data);if(!d.success)throw Object.assign(new Error("Node agent pairing revoke receipt was invalid."),{statusCode:502,code:"NODE_AGENT_PAIRING_REVOKE_RECEIPT_INVALID",retryable:!1});if(d.data.keyId!==t.keyId)throw Object.assign(new Error("Node agent pairing revoke receipt did not match the paired key."),{statusCode:502,code:"NODE_AGENT_PAIRING_REVOKE_RECEIPT_MISMATCH",retryable:!1});try{this.options.info({nodeId:jo(t.nodeId,t.secret),keyId:jo(t.keyId,t.secret),originalErrorCode:Oo(n,t.secret)},"node agent pairing revoked after node creation failed")}catch{}}catch(e){const s={nodeId:jo(t.nodeId,t.secret),keyId:jo(t.keyId,t.secret),originalErrorCode:Oo(n,t.secret),compensationErrorCode:Oo(e,t.secret)};try{this.options.warn(s,"node agent pairing compensation failed")}catch{}throw Object.assign(new Error("Node creation failed after pairing, and the remote pairing could not be revoked."),{statusCode:502,code:"NODE_AGENT_PAIRING_COMPENSATION_FAILED",retryable:(o=e,!o||"object"!=typeof o||!("retryable"in o)||"boolean"!=typeof o.retryable||o.retryable),details:s})}var o}}function Po(e,t){return Object.assign(new Error(e),{statusCode:400,code:t})}function Oo(e,t){return Ro(e&&"object"==typeof e&&"code"in e?e.code:void 0,t,"UNKNOWN_ERROR")}function Ro(e,t,n){return"string"!=typeof e||!/^[A-Z][A-Z0-9_]{0,127}$/.test(e)||e.includes(t)?n:e}function jo(e,t){return e.includes(t)?"[redacted]":e}function Do(e,t){if(!t)return e;const n=e=>"string"==typeof e?e.includes(t)?"[redacted]":e:Array.isArray(e)?e.map(n):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).map(([e,s])=>[e.includes(t)?"[redacted-key]":e,n(s)])):e;return n(e)}function Mo(e,t){return"string"==typeof e?e.includes(t):Array.isArray(e)?e.some(e=>Mo(e,t)):!(!e||"object"!=typeof e)&&Object.entries(e).some(([e,n])=>e.includes(t)||Mo(n,t))}function qo(e,t){const n=e instanceof Error&&!e.message.includes(t)?e.message:"Node creation failed after pairing.",s=e&&"object"==typeof e&&"statusCode"in e&&"number"==typeof e.statusCode?e.statusCode:502,i=Object.assign(new Error(n),{statusCode:s,code:Oo(e,t)});return e&&"object"==typeof e&&"retryable"in e&&"boolean"==typeof e.retryable&&Object.assign(i,{retryable:e.retryable}),i}const $o=o.z.object({id:o.z.string().trim().min(1),tokenHash:o.z.string().trim().min(1),expiresAt:o.z.string().datetime(),nodeName:o.z.string().trim().min(1).max(160).optional(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict(),Lo=o.z.object({joinToken:o.z.string().trim().min(1).max(4096),nodeId:ys.shape.id,nodeName:ys.shape.name.optional(),keyId:o.z.string().trim().min(1).max(160),secret:o.z.string().trim().min(1).max(4096),pairedAt:o.z.string().datetime().optional()}).strict();class Bo{options;constructor(e){this.options=e}createInvite(e={}){this.pruneExpiredInvites();const t=No.parse(e&&"object"==typeof e?e:{}),n=Kr(),s=ii(),i={id:si("node_join"),tokenHash:Uo(s),expiresAt:new Date(Date.now()+(t.expiresInMs||6e5)).toISOString(),...t.nodeName?{nodeName:t.nodeName}:{},createdAt:n,updatedAt:n};return this.options.invites.put(i),{id:i.id,joinToken:s,expiresAt:i.expiresAt}}complete(e){this.pruneExpiredInvites();const t=Lo.parse(e),n=Uo(t.joinToken),s=this.options.invites.list().find(e=>e.tokenHash===n&&Date.parse(e.expiresAt)>Date.now());if(!s)throw Object.assign(new Error("Node join token is invalid or expired."),{statusCode:401,code:"NODE_JOIN_TOKEN_INVALID"});this.options.invites.delete(s.id);const i=Kr();if(this.options.nodes.get(t.nodeId))throw Object.assign(new Error(`Node ${t.nodeId} already exists in this control-plane.`),{statusCode:409,code:"NODE_JOIN_NODE_ALREADY_EXISTS"});return this.options.nodes.put(ys.parse({id:t.nodeId,name:t.nodeName||s.nodeName||t.nodeId,connectionMode:"reverse-wss",auth:{mode:"paired-hmac",keyId:t.keyId,secret:t.secret,pairedAt:t.pairedAt||i,pairing:{status:"paired"}},status:"unknown",health:"unknown",capabilities:{},labels:{},createdAt:i,updatedAt:i}))}pruneExpiredInvites(){const e=Date.now();for(const t of this.options.invites.list())Date.parse(t.expiresAt)<=e&&this.options.invites.delete(t.id)}}function Uo(e){return a.createHash("sha256").update(e).digest("hex")}class Fo{options;constructor(e){this.options=e}list(){return this.listAll().map(Vr)}async listFederated(e){const t=await this.options.gateway.listFleetModels(this.options.listNodes(),{signal:e}),n=new Map;for(const e of this.listAll())n.set(e.id,{id:e.id,model:Vr(e),locations:[{type:"control-plane",name:e.name,enabled:e.enabled,order:e.order}],referenceCount:0});for(const{nodeId:e,model:s}of t.items){const{referenceCount:t,...i}=s,r=n.get(s.id);r?(r.locations.push({type:"node",nodeId:e,name:s.name,enabled:s.enabled,order:s.order,referenceCount:s.referenceCount}),r.referenceCount+=s.referenceCount):n.set(s.id,{id:s.id,model:i,locations:[{type:"node",nodeId:e,name:s.name,enabled:s.enabled,order:s.order,referenceCount:s.referenceCount}],referenceCount:s.referenceCount})}return Fn.parse({models:[...n.values()].sort((e,t)=>e.model.order-t.model.order||e.model.name.localeCompare(t.model.name)),nodeDiagnostics:t.nodeErrors.map(e=>({nodeId:e.nodeId,code:e.code,message:e.message})),updatedAt:Kr()})}create(e){const t=ko.parse(e),n=Kr(),s=Hn(t),i=this.options.models.get(s),r=Dn.parse({...t,id:s,enabled:t.enabled??!0,order:t.order??this.nextOrder(),labels:t.labels||{},createdAt:i?.createdAt||n,updatedAt:n});return Vr(this.options.models.put(r))}update(e,t){const n=Co.parse(t),s=this.requireSecret(e),i=Dn.parse({...s,...n,key:n.key?.trim()?n.key:s.key,createdAt:s.createdAt,updatedAt:Kr()}),r=Hn(i);return Vr(this.options.models.put(Dn.parse({...i,id:r})))}delete(e){return this.requireSecret(e),this.options.models.delete(e)}reorder(e){const t=[...new Set(e)],n=new Map(this.options.models.list().map(e=>[e.id,e]));for(const e of t)n.has(e)||Xr("MODEL_NOT_FOUND",`Model ${e} was not found.`);return t.forEach((e,t)=>{const s=n.get(e);this.options.models.put(Dn.parse({...s,order:100*(t+1),updatedAt:Kr()}))}),this.list()}createOnNode(e,t){return this.options.gateway.createModel(this.options.requireNode(e),t)}updateOnNode(e,t,n){return this.options.gateway.updateModel(this.options.requireNode(e),t,n)}deleteOnNode(e,t){return this.options.gateway.deleteModel(this.options.requireNode(e),t)}require(e,t=!1){const n=this.requireSecret(e);return t?n:Vr(n)}async prepareAssignment(e,t){const n=await this.options.gateway.listModels(e),s={...null===t.codexModelHash?{codexModelHash:null}:t.codexModelHash?.trim()?{codexModelHash:t.codexModelHash.trim()}:{},...null===t.claudeModelHash?{claudeModelHash:null}:t.claudeModelHash?.trim()?{claudeModelHash:t.claudeModelHash.trim()}:{}},i=async(t,s)=>{if(null===s)return;const i=s?this.listAll().find(e=>e.id===s):this.listAll().find(e=>e.enabled&&e.app===t);if(i)return Ho(i,t),await this.options.gateway.deployModel(e,i.id,i),i.id;if(!s)return;const r=n.find(e=>e.id===s);return r||Xr("MODEL_NOT_FOUND",`Model ${s} was not found on control-plane or node ${e.id}.`),Ho(r,t),r.id};return{modelSelection:s,codexModelHash:await i("codex",s.codexModelHash),claudeModelHash:await i("claude",s.claudeModelHash)}}async ensureInstanceAssignment(e){const t=this.options.requireNode(e.nodeId),n=await this.prepareAssignment(t,e.modelSelection);return this.options.gateway.assignInstanceModels(t,e.id,n)}listAll(){return this.options.models.list().map(e=>this.normalize(e)).sort((e,t)=>e.order-t.order||e.createdAt.localeCompare(t.createdAt)||e.id.localeCompare(t.id))}requireSecret(e){const t=this.options.models.get(e);return t||Xr("MODEL_NOT_FOUND",`Model ${e} was not found.`),this.normalize(t)}normalize(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...e};if(!("app"in t)){const e=Array.isArray(t.apps)?t.apps:[];t.app=e.includes("claude")&&!e.includes("codex")?"claude":"codex"}return delete t.apps,Dn.parse(t)}return Dn.parse(e)}(e);return t!==e&&this.options.models.put(t),t}nextOrder(){return this.options.models.list().reduce((e,t)=>Math.max(e,t.order),0)+100}}function Ho(e,t){if(e.app!==t)throw Object.assign(new Error(`Model ${e.id} is not a ${t} model.`),{statusCode:400,code:"MODEL_APP_MISMATCH"});if(!e.enabled)throw Object.assign(new Error(`Model ${e.id} is disabled.`),{statusCode:400,code:"MODEL_DISABLED"})}class Wo{requireNode;nodeAgentTransport;constructor(e){this.requireNode=e.requireNode,this.nodeAgentTransport=e.nodeAgentTransport}async request(e,t,n={}){if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}Go(e);const s=this.requireNode(e.nodeId),i=await this.nodeAgentTransport(s).request(s,`/instances/${encodeURIComponent(e.id)}/proxy`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({path:`/api/${t.replace(/^\/+/,"")}`,method:n.method||"GET",headers:Jr(n.headers),body:(r=n.body,"string"==typeof r?r:null==r?void 0:String(r))})});var r;const o=await i.json().catch(()=>({}));if(!i.ok){const n=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n="string"==typeof t.code&&t.code.trim()?t.code:void 0,s="string"==typeof t.message&&t.message?t.message:void 0,i=t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?t.details:void 0;return n||s?{code:n,message:s,details:i}:void 0}(o.error),s=n?.message||function(e){if(!e||"object"!=typeof e||Array.isArray(e))return"";const t=e.message;return"string"==typeof t?t:""}(o.error)||`Instance request failed with HTTP ${i.status}`,r=new Error(s);throw Object.assign(r,{statusCode:i.status,code:n?.code||"INSTANCE_REQUEST_FAILED",...n?.details?{details:n.details}:{},instanceId:e.id,nodeId:e.nodeId,route:t}),r}return o.data??o}async proxyHttp(e,t,n={}){if("online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_UNREACHABLE"}),t}if(!e.target.web){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}Go(e);const s=this.requireNode(e.nodeId),i=await this.nodeAgentTransport(s).requestStream(s,`/instances/${encodeURIComponent(e.id)}/proxy/stream`,{method:"POST",headers:{"content-type":"application/json"},signal:n.signal,body:JSON.stringify({path:t,method:n.method||"GET",headers:Jr(n.headers),...(r=n.body,null==r?{}:"string"==typeof r?{body:r}:Buffer.isBuffer(r)?{bodyBase64:r.toString("base64")}:r instanceof Uint8Array?{bodyBase64:Buffer.from(r).toString("base64")}:{body:String(r)})})});var r;return{status:i.status||502,headers:Object.fromEntries(i.headers.entries()),body:i.body}}proxyWebSocket(e,t,n,s,i={}){if(!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=new Error(`Instance ${e.name} web endpoint is not reachable.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_WEB_UNREACHABLE"}),t}Go(e);const r=this.requireNode(e.nodeId),o=n.startsWith("/")?n.slice(1):n;this.nodeAgentTransport(r).proxyWebSocket(r,t,`/instances/${encodeURIComponent(e.id)}/proxy/ws/${o}`,s,i)}async reportHeartbeat(e,t){if(!e.registrationToken)return;const n=this.requireNode(e.nodeId),s=`/instances/${encodeURIComponent(e.id)}/heartbeat`,i={method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.registrationToken}`},body:JSON.stringify(t)},r=await this.nodeAgentTransport(n).request(n,s,i),o=await r.json().catch(()=>({}));if(!r.ok){const e=new Error(o.error?.message||`Instance heartbeat sync failed with HTTP ${r.status}`);throw Object.assign(e,{statusCode:r.status,code:"INSTANCE_HEARTBEAT_SYNC_FAILED"}),e}}}function Go(e){if(Vs(e))return;const t=new Error(`Instance ${e.name} is not ready.`);throw Object.assign(t,{statusCode:409,code:"INSTANCE_NOT_READY",runtimePhase:e.runtimeVersion?.phase}),t}class Vo{read(e){const t=new Map(e.projects.map(e=>[e.id,e])),n=new Map(e.images.map(e=>[e.id,e])),s=new Map(e.nodes.map(e=>[e.id,e])),i=new Map(e.runtimes.map(e=>[`${e.nodeId}:${e.id}`,e])),r=Date.now();return{items:e.instances.map(e=>function(e){const t=e.instance.lastHeartbeatAt?e.currentTime-Date.parse(e.instance.lastHeartbeatAt):void 0,n=void 0!==t&&t>3e4?"offline":e.instance.connectionStatus,s=Gr(e.instance);return{...s,aiSessions:ke(s.aiSessions),connectionStatus:n,heartbeatAgeMs:t,project:e.project,image:e.image,node:e.node?Hr(e.node):void 0,runtime:e.runtime,protocolCompatible:!e.instance.protocolVersion||e.instance.protocolVersion===Tt}}({instance:e,currentTime:r,project:e.projectId?t.get(e.projectId):void 0,image:e.imageSnapshot||(e.imageSelection?n.get(e.imageSelection.imageId):void 0),node:s.get(e.nodeId),runtime:i.get(`${e.nodeId}:${e.runtimeId}`)})),nodeErrors:e.nodeErrors||[]}}}class Ko{options;constructor(e){this.options=e}async create(e){const t=So.parse(e),n=t.projectId?this.options.requireProject(t.projectId):void 0,s=t.runtimeId||n?.defaultRuntimeId||"runtime_local_docker",i=t.nodeId||n?.defaultNodeId||this.options.defaultNodeId();if(!i)throw Qo("At least one node connection is required before creating an instance.",400,"NODE_REQUIRED");const r=this.options.requireNode(i),o=await this.options.requireRuntime(r.id,s),a=o.capabilities.requiresImage??"local"!==o.type,d=t.imageSelection||(a?n?.defaultImageSelection:void 0),c=d?this.options.resolveImageSelection(d):void 0;if(a&&!c)throw Qo(`Runtime ${o.name} requires an image.`,400,"RUNTIME_IMAGE_REQUIRED");const l=await this.resolveSource({node:r,runtime:o,project:n,source:t.source,sourceSnapshot:t.sourceSnapshot,imageOption:c}),p=await this.options.prepareModels(r,t.modelSelection||{}),u=c?function(e){const t=Kr();return{id:e.id,origin:e.origin,name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover,repository:e.repository,tag:e.tag,requestedReference:e.reference,resolvedDigest:e.digest,resolvedReference:e.digest?`${e.repository}@${e.digest}`:void 0,downloadSizeBytes:e.downloadSizeBytes,pullPolicy:"if-not-present",capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,market:e.market,createdAt:t,updatedAt:t}}(c):void 0,g=await this.options.gateway.createInstance(r,{id:t.id,name:t.name,runtimeId:s,...c&&u?{imageSelection:{imageId:c.id,tag:c.tag},image:u}:{},projectId:n?.id,source:l.value,sourceSnapshot:l.snapshot,config:t.config,modelSelection:{}});let m=g;try{m=(await this.options.gateway.assignInstanceModels(r,g.id,p)).instance}catch(e){throw await this.options.gateway.deleteInstance(r,g.id).catch(()=>{}),e}let h={status:"not-requested"};if(t.start)try{m=await this.options.gateway.startInstance(r,m.id),h={status:"started"}}catch(e){h={status:"failed",error:Jo(e,"INSTANCE_START_FAILED")},m=await this.options.gateway.listInstances(r).then(e=>e.find(e=>e.id===m.id)||m).catch(()=>m)}return{...Gr(m),registrationToken:m.registrationToken,startOutcome:h}}async resolveSource(e){let t=e.source||e.project?.source,n=e.sourceSnapshot||(e.project?e.project:{});if(!t)throw Qo("Instance source is required.",400,"INSTANCE_SOURCE_REQUIRED");if("local"===e.runtime.type&&"local-folder"!==t.type)throw Qo("Localhost runtime currently supports local folder sources only.",400,"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER");if("local-folder"!==t.type)return{value:t,snapshot:n};if(t.ownerNodeId&&t.ownerNodeId!==e.node.id)throw Qo(`Local folder ${t.path} belongs to node ${t.ownerNodeId}, not ${e.node.id}.`,400,"LOCAL_FOLDER_REQUIRES_OWNER_NODE");const s=t.localFolderId?await this.options.requireLocalFolder(e.node,t.localFolderId):await this.options.gateway.createLocalFolder(e.node,{name:"string"==typeof n.name?n.name:"Local folder",path:t.path,defaultImageSelection:e.imageOption?{imageId:e.imageOption.id,tag:e.imageOption.tag}:void 0});return t={...t,localFolderId:s.id,ownerNodeId:s.nodeId,path:s.path},n=s,{value:t,snapshot:n}}}function Jo(e,t){const n=e&&"object"==typeof e?e:{};return{code:"string"==typeof n.code&&n.code.trim()?n.code:t,message:"string"==typeof n.message&&n.message.trim()?n.message:String(e)}}function Qo(e,t,n){return Object.assign(new Error(e),{statusCode:t,code:n})}class Yo{triggers;listInstances;requireInstance;instanceRequest;constructor(e){this.triggers=e.triggers,this.listInstances=e.listInstances,this.requireInstance=e.requireInstance,this.instanceRequest=e.instanceRequest}async listTriggers(){const e=await this.listInstances(),t=new Map;for(const e of this.triggers.list())t.set(e.configHash,{configHash:e.configHash,config:e,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!0,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]});for(const n of e){const e=n.triggers;for(const s of e?.configs||[]){const i=t.get(s.configHash)||{configHash:s.configHash,config:s.config,deploymentCount:0,enabledCount:0,runningCount:0,errorCount:0,ownedByControlPlane:!1,controlPlaneDeploymentCount:0,deployments:[],recentRuns:[]},r=(s.deployments||[]).map(e=>({instanceId:n.id,instanceName:n.name,deployment:e,runtime:(s.runtime||[]).find(t=>t.deploymentId?t.deploymentId===e.deploymentId:t.configHash===e.configHash)}));i.deploymentCount+=r.length,i.enabledCount+=r.filter(e=>Boolean(e.deployment.enabled)).length,i.controlPlaneDeploymentCount+=r.filter(e=>"control-plane"===e.deployment.origin).length,i.runningCount+=(s.runtime||[]).filter(e=>"running"===e.status).length,i.errorCount+=(s.runtime||[]).filter(e=>"error"===e.status).length,i.deployments.push(...r),i.recentRuns.push(...(e.recentRuns||[]).filter(e=>e.configHash===s.configHash).map(e=>({...e,instanceId:n.id,instanceName:n.name}))),t.set(s.configHash,i)}}return{updatedAt:(new Date).toISOString(),triggers:[...t.values()].sort((e,t)=>String(e.config.name||e.configHash).localeCompare(String(t.config.name||t.configHash)))}}createTrigger(e){const t=yo.parse(e||{}),n=Kr(),s={maxConcurrentRuns:t.policy?.maxConcurrentRuns??1,whenBusy:t.policy?.whenBusy??"skip",cooldownMs:t.policy?.cooldownMs},i=function(e){const t=We({source:e.source,action:e.action,policy:qe.parse(e.policy||{})});return`trg_${a.createHash("sha256").update(JSON.stringify(t)).digest("hex").slice(0,24)}`}({source:t.source,action:t.action,policy:s}),r=fo.parse({id:i,configHash:i,name:t.name,description:t.description,source:t.source,action:t.action,policy:s,createdAt:n,updatedAt:n});return this.triggers.put(r)}async deleteTrigger(e){const t=this.triggers.delete(e),n=await this.listInstances(),s=[];for(const t of n){const n=t.triggers?.configs?.find(t=>t.configHash===e);if(!n)continue;const i=(n.deployments||[]).filter(e=>"control-plane"===e.origin);if(!i.length)continue;const r=n.deployments.every(e=>"control-plane"===e.origin);for(const n of i)try{const i=n.deploymentId?`/triggers/${encodeURIComponent(e)}/deployments/${encodeURIComponent(n.deploymentId)}`:r?`/triggers/${encodeURIComponent(e)}`:void 0;if(!i){s.push({instanceId:t.id,configHash:e,deploymentId:n.deploymentId,deleted:!1,skipped:"missing-deployment-id"});continue}const o=await this.instanceRequest(t,i,{method:"DELETE"});if(s.push({instanceId:t.id,configHash:e,deploymentId:n.deploymentId,deleted:!0,data:o}),!n.deploymentId)break}catch(i){s.push({instanceId:t.id,configHash:e,deploymentId:n.deploymentId,deleted:!1,error:i instanceof Error?i.message:String(i)})}}return t||s.length||Xr("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),{configHash:e,deletedTemplate:t,results:s}}async applyTrigger(e,t){const n=this.requireTrigger(e),s=Io.parse(t||{}),i=[];for(const e of s.instanceIds){const t=await this.requireInstance(e);i.push({instanceId:e,data:await this.instanceRequest(t,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:n.name,description:n.description,source:n.source,action:n.action,policy:n.policy,deployment:{origin:"control-plane",enabled:s.enabled??!0,target:s.target}})})})}return{configHash:e,results:i}}async listInstanceTriggers(e){const t=await this.requireInstance(e);return this.instanceRequest(t,"/triggers")}async bindAiSessionTrigger(e,t,n){const s=bo.parse(n||{}),i=this.requireTrigger(s.configHash),r=await this.requireInstance(e);return this.instanceRequest(r,"/triggers",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:i.name,description:i.description,source:i.source,action:i.action,policy:i.policy,deployment:{deploymentId:Xo(t,s.configHash),origin:"control-plane",enabled:s.enabled??!0,target:{type:"ai-session",aiSessionId:t}}})})}async unbindAiSessionTrigger(e,t,n){const s=await this.requireInstance(e),i=Xo(t,n);return this.instanceRequest(s,`/triggers/${encodeURIComponent(n)}/deployments/${encodeURIComponent(i)}`,{method:"DELETE"})}async runInstanceTrigger(e,t,n={}){const s=await this.requireInstance(e);return this.instanceRequest(s,`/triggers/${encodeURIComponent(t)}/run`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n||{})})}requireTrigger(e){const t=this.triggers.get(e);return t||Xr("TRIGGER_NOT_FOUND",`Trigger ${e} was not found.`),t}}function Xo(e,t){return`ai_${Buffer.from(e).toString("base64url")}_${t}`}class Zo{options;constructor(e){this.options=e}getSettings(){return co.parse(this.options.settings.get())}updateSettings(e){const t=lo.parse(e),n=co.parse({...this.getSettings(),...t,..."publicBaseUrl"in t?{publicBaseUrl:t.publicBaseUrl?.trim()||void 0}:{}});return this.options.settings.put(n)}seedDefaults(){}listProjects(){return this.options.projects.list().map(e=>this.normalizeProjectRecord(e))}getProject(e){const t=this.options.projects.get(e);return t?this.normalizeProjectRecord(t):void 0}createProject(e){const t=no.parse(e),n=Kr(),s=Pn.parse(t.source);return this.options.projects.put(jn.parse({...t,id:t.id||si("proj"),source:s,defaultImageSelection:t.defaultImageSelection||{imageId:"market_taskhandoff_browser"},defaultNodeId:t.defaultNodeId||this.options.defaultNodeId(),defaultRuntimeId:t.defaultRuntimeId||"runtime_local_docker",workspacePolicy:On.parse(t.workspacePolicy||Wr(s)),labels:t.labels||{},createdAt:n,updatedAt:n}))}updateProject(e,t){const n=so.parse(t),s=this.requireProject(e),i=n.source?Pn.parse(n.source):s.source;return this.options.projects.put(jn.parse({...s,...n,id:e,source:i,workspacePolicy:n.workspacePolicy?On.parse(n.workspacePolicy):s.workspacePolicy,createdAt:s.createdAt,updatedAt:Kr()}))}deleteProject(e){return this.options.projects.delete(e)}requireProject(e){const t=this.getProject(e);return t||Xr("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}listImages(){return this.options.images.list()}getMarketCatalog(){return{catalog:this.options.market.getCatalog(),status:this.options.market.getStatus()}}listImageOptions(e){return[...this.options.market.getCatalog().items.map(t=>this.marketImageOption(t,void 0,e,!0)),...this.listImages().map(e=>this.customImageOption(e))]}resolveImageSelection(e,t){const n=this.options.market.getCatalog().items.find(t=>t.id===e.imageId);if(n)return this.marketImageOption(n,e.tag,t);const s=this.options.images.get(e.imageId);if(s||Xr("IMAGE_NOT_FOUND",`Image ${e.imageId} was not found.`),e.tag&&e.tag!==s.tag)throw Object.assign(new Error(`Custom image ${s.id} cannot override its stored tag.`),{statusCode:400,code:"CUSTOM_IMAGE_TAG_OVERRIDE"});return this.customImageOption(s)}createImage(e){const t=uo.parse(e),n=Kr(),s=Bt(t.reference);return this.options.images.put(ss.parse({...t,id:t.id||si("img"),origin:"custom",reference:s.reference,repository:s.repository,tag:s.tag,pullPolicy:t.pullPolicy||"if-not-present",capabilities:t.capabilities||[],optionalApps:t.optionalApps||[],defaultEnv:t.defaultEnv||{},labels:t.labels||{},createdAt:n,updatedAt:n}))}updateImage(e,t){const n=go.parse(t),s=this.requireImage(e),i=Bt(n.reference||s.reference);return this.options.images.put(ss.parse({...s,...n,id:e,origin:"custom",reference:i.reference,repository:i.repository,tag:i.tag,createdAt:s.createdAt,updatedAt:Kr()}))}deleteImage(e){return this.options.images.delete(e)}requireImage(e){if(this.options.market.getCatalog().items.some(t=>t.id===e))throw Object.assign(new Error(`Market image ${e} is read-only.`),{statusCode:403,code:"MARKET_IMAGE_READ_ONLY"});const t=this.options.images.get(e);return t||Xr("IMAGE_NOT_FOUND",`Image ${e} was not found.`),t}normalizeProjectRecord(e){const t=function(e){if(e&&"object"==typeof e&&!Array.isArray(e)){const t={...Kt(e)};return"defaultRuntimeId"in t||(t.defaultRuntimeId="runtime_local_docker"),jn.parse(t)}return jn.parse(e)}(e);return t!==e&&this.options.projects.put(t),t}customImageOption(e){return rs.parse({id:e.id,origin:"custom",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||Kn,repository:e.repository,tag:e.tag,availableTags:e.tag?[{name:e.tag,reference:e.reference,status:"active"}]:[],reference:e.reference,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!1})}marketImageOption(e,t,n,s=!1){if("yanked"===e.status&&!s)throw Object.assign(new Error(`Market image ${e.id} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_YANKED"});const i=t||e.defaultTag,r=e.tags.find(e=>e.name===i);if(!r)throw Object.assign(new Error(`Market image ${e.id} does not provide tag ${i}.`),{statusCode:400,code:"MARKET_IMAGE_TAG_NOT_FOUND"});if("yanked"===r.status&&!s)throw Object.assign(new Error(`Market image tag ${e.id}:${i} is no longer selectable.`),{statusCode:409,code:"MARKET_IMAGE_TAG_YANKED"});const o=r.platforms.find(e=>!(n?.os&&e.os!==n.os||n?.architecture&&e.architecture!==n.architecture)),a=this.options.market.getCatalog();return rs.parse({id:e.id,origin:"market",name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:e.cover||Kn,repository:e.repository,tag:r.name,availableTags:e.tags.map(e=>({name:e.name,version:e.version,reference:e.reference,manifestDigest:e.manifestDigest,status:e.status})),reference:r.reference,digest:o?.digest||r.manifestDigest,downloadSizeBytes:o?.downloadSizeBytes,unpackedSizeBytes:o?.unpackedSizeBytes,capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:e.defaultEnv,labels:e.labels,readOnly:!0,lifecycleStatus:"yanked"===r.status||"yanked"===e.status?"yanked":"deprecated"===r.status||"deprecated"===e.status?"deprecated":"active",market:{catalogId:a.catalogId,catalogRevision:a.revision,publisher:e.publisher,version:r.version}})}}function ea(e){const t=Bt(e.reference),n=t.tag||"latest";return{id:e.id,publisher:"task-handoff",slug:e.slug,name:e.name,description:e.description,localizedDescriptions:e.localizedDescriptions,cover:Kn,repository:t.repository,defaultTag:n,tags:[{name:n,reference:t.reference,platforms:[],status:"active"}],capabilities:e.capabilities,optionalApps:e.optionalApps,defaultEnv:{},labels:{"task-handoff.image.kind":"controlled-instance","task-handoff.image.profile":e.slug},status:"active"}}function ta(){const e=(t=process.env.TASK_HANDOFF_BUILT_AT)&&!Number.isNaN(Date.parse(t))?new Date(t).toISOString():(new Date).toISOString();var t;return ts.parse({protocolVersion:"2026-07-29",catalogId:"task_handoff_embedded",revision:process.env.TASK_HANDOFF_VERSION||"0.0.1",source:"embedded",generatedAt:e,items:[ea({id:"market_taskhandoff_codex",slug:"codex",name:"TaskHandoff Codex",description:"Minimal Codex runtime with terminal and Codex.",localizedDescriptions:{"zh-CN":"最小 Codex 运行环境,包含终端和 Codex。"},reference:process.env.TASK_HANDOFF_CONTROLLED_CODEX_IMAGE||"huadream/task-handoff-controlled-codex:latest",capabilities:["terminal","codex"],optionalApps:["terminal-tty"]}),ea({id:"market_taskhandoff_ai",slug:"ai",name:"TaskHandoff Codex + Claude",description:"AI development runtime with Codex, Claude, and terminal.",localizedDescriptions:{"zh-CN":"AI 开发运行环境,包含 Codex、Claude 和终端。"},reference:process.env.TASK_HANDOFF_CONTROLLED_AI_IMAGE||"huadream/task-handoff-controlled-ai:latest",capabilities:["terminal","codex","claude"],optionalApps:["terminal-tty"]}),ea({id:"market_taskhandoff_browser",slug:"browser",name:"TaskHandoff Browser",description:"Full runtime with Codex, Claude, Chromium, VNC, and code-server.",localizedDescriptions:{"zh-CN":"完整运行环境,包含 Codex、Claude、Chromium、VNC 和 code-server。"},reference:process.env.TASK_HANDOFF_CONTROLLED_BROWSER_IMAGE||"huadream/task-handoff-controlled-browser:latest",capabilities:["browser","terminal","gui-terminal","vscode-web","codex","claude"],optionalApps:["chromium","terminal-tty","gui-terminal","vscode-web"]})]})}class na{async loadCatalog(){return ta()}}class sa{snapshot;status;constructor(e=ta()){this.snapshot=ts.parse(e),this.status=ns.parse({source:this.snapshot.source,state:"ready",revision:this.snapshot.revision,updatedAt:this.snapshot.generatedAt})}getCatalog(){return this.snapshot}getStatus(){return this.status}acceptCandidate(e){const t=ts.safeParse(e);return t.success?(this.snapshot=t.data,this.status=ns.parse({source:t.data.source,state:"ready",revision:t.data.revision,updatedAt:(new Date).toISOString()}),{accepted:!0,snapshot:t.data}):(this.status=ns.parse({...this.status,state:"failed",updatedAt:(new Date).toISOString(),error:t.error.message}),{accepted:!1,error:t.error})}recordRefreshFailure(e){return this.status=ns.parse({...this.status,state:this.snapshot?"stale":"failed",updatedAt:(new Date).toISOString(),error:e instanceof Error?e.message:String(e)}),this.snapshot}async refresh(e){try{const t=await e.loadCatalog();return this.acceptCandidate(t)}catch(e){return this.recordRefreshFailure(e),{accepted:!1,error:e}}}}class ia{records=new Map;options;constructor(e){this.options=e}create(e){const t=a.randomBytes(this.options.tokenBytes).toString("base64url"),n={...e,token:t,expiresAt:new Date(Date.now()+(e.ttlMs||this.options.ttlMs)).toISOString()};return this.records.set(t,n),n}resolve(e,t){const n=this.records.get(e);if(!n)throw this.invalidError();if(n.expiresAt<=(new Date).toISOString())throw this.records.delete(e),this.invalidError();if(t&&!t(n))throw this.invalidError();return n}invalidError(){const e=new Error(this.options.invalidMessage);return Object.assign(e,{statusCode:this.options.invalidStatusCode||400,code:this.options.invalidCode}),e}}class ra{requireInstance;listAppSessions;tokens=new ia({invalidMessage:"App access link is invalid or expired.",invalidCode:"APP_ACCESS_TOKEN_INVALID",invalidStatusCode:401,tokenBytes:24,ttlMs:9e5});constructor(e){this.requireInstance=e.requireInstance,this.listAppSessions=e.listAppSessions}createToken(e){return this.tokens.create({instanceId:e.instanceId,sessionId:e.sessionId,mode:e.mode,ttlMs:e.ttlMs})}resolveToken(e,t){return this.tokens.resolve(e,e=>!t||e.mode===t)}async proxyTarget(e,t,n=""){const s=this.resolveToken(e,t),i=await this.requireInstance(s.instanceId),r=await this.listAppSessions(),o=r.instances.find(e=>e.instanceId===s.instanceId)?.appSessions.sessions.find(e=>da(e.id)===s.sessionId);if(!o){const e=new Error("App session was not found.");throw Object.assign(e,{statusCode:404,code:"APP_SESSION_NOT_FOUND"}),e}return{access:s,instance:i,session:o,path:oa(t,s.sessionId,o,n)}}}function oa(e,t,n,s=""){return"tty"===e?`/api/apps/sessions/${encodeURIComponent(t)}/tty`:"vnc"===e?s?function(e,t){const n=`/${t.replace(/^\/+/,"")}`,s=n.split("?",1)[0]||"/",i=encodeURIComponent(e);if(!["/api/novnc/",`/api/apps/sessions/${i}/web/`,`/api/apps/sessions/${i}/vnc`,`/api/apps/sessions/${i}/novnc/`].some(e=>s===e.replace(/\/$/,"")||s.startsWith(e))){const e=new Error("App access token cannot be used for this path.");throw Object.assign(e,{statusCode:403,code:"APP_ACCESS_PATH_FORBIDDEN"}),e}return n}(t,s):da(aa(n.vnc).noVncPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`:da(aa(n.web).webPath)||`/api/apps/sessions/${encodeURIComponent(t)}/web/`}function aa(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function da(e){return"string"==typeof e&&e.trim()?e.trim():void 0}const ca={allow:"a",deny:"d",skip:"s"};function la(e){return a.createHash("sha256").update(e).digest("base64url").slice(0,Math.ceil(16))}class pa{tokens=new ia({invalidMessage:"Chat action expired.",invalidCode:"CHAT_ACTION_TOKEN_INVALID",tokenBytes:9,ttlMs:6e5});create(e){return this.tokens.create({type:e.type,instanceId:"instanceId"in e?e.instanceId:void 0,appId:"appId"in e?e.appId:void 0,routeId:"routeId"in e?e.routeId:void 0,decision:"decision"in e?e.decision:void 0,ttlMs:e.ttlMs})}resolve(e,t){return this.tokens.resolve(e,e=>!t||e.type===t)}pendingDecisionCallbackData(e,t){return`task_handoff:cp_p:v1:${ca[t]}:${la(e)}`}}function ua(e){const{token:t,...n}=e;return{...n,settings:ma(e.settings),tokenSet:Boolean(e.token)}}function ga(e,t){if(!t)return e;const n={...e};for(const[e,s]of Object.entries(t))void 0!==s&&""!==s&&(n[e]=s);return n}function ma(e){const t={...e};return"string"==typeof t.clientSecret&&t.clientSecret&&(delete t.clientSecret,t.clientSecretSet=!0),t}class ha{chatBridges;chatSessions;constructor(e){this.chatBridges=e.chatBridges,this.chatSessions=e.chatSessions}list(){return this.chatBridges.list().map(ua)}require(e){const t=this.chatBridges.get(e);return t||Xr("CHAT_BRIDGE_NOT_FOUND",`Chat bridge ${e} was not found.`),t}create(e){const t=ho.parse(e),n=Kr(),s=Fs.parse({...t,id:si(`chat_${t.channel}`),name:t.name||(i=t.channel,r=this.chatBridges.list().filter(e=>e.channel===t.channel).length+1,`${"telegram"===i?"Telegram":"wechat"===i?"WeChat":"dingding"===i?"DingDing":"Web"} ${r}`),enabled:t.enabled??!1,allowedUserIds:t.allowedUserIds||[],pollIntervalMs:t.pollIntervalMs||3e3,settings:ga({},t.settings),createdAt:n,updatedAt:n});var i,r;return this.chatBridges.put(s),ua(s)}update(e,t){const n=mo.parse(t),s=this.require(e),i=ga(s.settings,n.settings),r=Fs.parse({...s,...n,id:e,channel:s.channel,name:n.name||s.name,settings:i,updatedAt:Kr()});return this.chatBridges.put(r),ua(r)}delete(e){const t=this.chatBridges.delete(e);if(t)for(const t of this.chatSessions.list())t.bridgeId===e&&this.chatSessions.delete(t.id);return t}}class fa{chatSessions;constructor(e){this.chatSessions=e.chatSessions}list(){return this.chatSessions.list()}require(e){const t=this.chatSessions.get(e);return t||Xr("CHAT_SESSION_NOT_FOUND",`Chat session ${e} was not found.`),t}upsert(e){const t=Kr(),n=(s=e.channel,i=e.chatSessionId,`${e.bridgeId||s}:${i.replace(/[^a-zA-Z0-9_.:-]/g,"_")}`);var s,i;const r=this.chatSessions.get(n),o=Hs.parse({...r||{},...e,id:n,channel:e.channel,bridgeId:e.bridgeId,chatSessionId:e.chatSessionId,lastUsedAt:t,createdAt:r?.createdAt||t,updatedAt:t});return this.chatSessions.put(o),o}}function ya(e){return{inline_keyboard:e.map(e=>e.map(e=>({text:e.text,...e.url?{url:e.url}:{callback_data:e.callbackData||""}})))}}function Ia(e,t=40){return ya(e.slice(0,t).map(e=>[e]))}function ba(e){return Buffer.from(e,"utf8").toString("base64url")}function va(e){const t=Sa(e).inline_keyboard,n=Array.isArray(t)?t:[],s=[];for(const e of n)if(Array.isArray(e))for(const t of e){const e=Sa(t),n=String(e.callback_data||""),i=String(e.text||"").trim();i&&n&&s.push({text:i,id:`th_cb_${ba(n)}`})}return s.slice(0,40)}function wa(e,t){const n=String(e||"").replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,Math.max(0,t-3))}...`}function Sa(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}const Aa=new Set(["stopped","failed","exited","closed","terminated"]);function ka(e){return!function(e){return Aa.has(Ji(e))}(e)}function Ca(e){return(e.aiSessions?.sessions||[]).filter(t=>function(e,t){const n=(e.apps?.sessions||[]).filter(_a);if(t.appSessionId&&n.some(e=>e.id===t.appSessionId))return!0;const s=new Set(t.appBindingKeys||[]);return Boolean(s.size&&n.some(e=>function(e){return[...Ki(e).bindings.map(Qi),...Xi(e)].filter(Boolean)}(e).some(e=>s.has(e))))}(e,t))}function xa(e){return"waiting"===e.status?4:"failed"===e.status?3:"running"===e.status?2:"idle"===e.status?1:0}function za(e){return[e.cwd||"",e.agent||"",e.providerSessionId||e.id].join("\0")}function Ta(e){const t=e.session.title||e.session.userPrompt||e.session.summary||e.session.lastMessage||"",n=t?` - ${wa(t,18)}`:"";return`${e.instance.name} - ${e.session.agent} ${e.session.status}${n}`}function Na(e,t=(e,t)=>`task_handoff:approval:${e.id}:${t}`){return Ia(e.flatMap(e=>"approval"!==e.kind?[]:[{text:wa(`Allow ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"allow")},{text:wa(`Skip ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"skip")},{text:wa(`Deny ${e.instance?.name||e.instanceId} / ${e.aiSessionId}`,56),callbackData:t(e,"deny")}]))}function Ea(e){const t=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,n="approval"===e.kind?`/approve ${e.id} | /deny ${e.id} | /skip ${e.id}`:`/reply ${e.id} <message>`;return`[${t}] ${e.kind} ${e.id}: ${e.result}\n${n}`}function _a(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{};return ka("string"==typeof t.status?t.status:void 0)}class Pa{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleAiSessionsCommand(e){const t=await this.targets.aiSessionChoices(e),n=t.find(e=>e.active),s=t.map(e=>function(e){const t=e.session.userPrompt||e.session.summary||e.session.lastMessage||"",n=e.active?"* ":"",s=e.session.phase&&"unknown"!==e.session.phase?`/${e.session.phase}`:"";return`${n}${e.index+1}. ${e.projectName} / ${e.instance.name} - ${e.session.agent} - ${e.session.status}${s} - ${e.session.id}${t?` - ${t}`:""}`}(e));return{accepted:!0,routed:!1,binding:e,instances:t.map(e=>e.instance),reply:t.length?["Select AI session",`Current ${n?`${n.projectName} / ${n.instance.name} / ${n.session.id}`:await this.targets.renderChatTarget(e)}, total ${t.length}`,"",...s].join("\n"):"No AI sessions found.",replyMarkup:t.length?Ia(t.map(e=>({text:`${e.active?"✓ ":""}${Ta(e)}`,callbackData:`task_handoff:cp_session:${e.index}`}))):void 0}}async handleAiSessionCommand(e,t){const n=String(t[0]||"").trim();if(!n)return this.handleAiSessionsCommand(e);const s=await this.targets.findAiSession(n);if(!s)return{accepted:!1,routed:!1,binding:e,reply:`AI session ${n} was not found. Try /sessions.`};const i=s.instance.projectId?this.deps.getProject(s.instance.projectId):void 0;return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i?.id||e.activeProjectId,activeInstanceId:s.instance.id,activeAiSessionId:s.session.id}),instance:s.instance,aiSession:s.session,reply:`Active AI session: ${s.instance.name} / ${s.session.agent} ${s.session.id}`}}async handleAiSessionIndexAction(e,t){const n=(await this.targets.aiSessionChoices(e)).find(e=>e.index===t);if(!n)return{accepted:!1,routed:!1,binding:e,reply:"AI session was not found. Use /session to refresh the list.",replyMarkup:void 0};const s=this.deps.upsertChatSession({...e,activeProjectId:n.projectId||e.activeProjectId,activeInstanceId:n.instance.id,activeAiSessionId:n.session.id}),i=await this.handleAiSessionsCommand(s);return{...i,accepted:!0,binding:s,instance:n.instance,aiSession:n.session,message:`selected ${n.session.id}`,reply:[`Current chat is bound to AI session ${n.session.id}`,"",i.reply].join("\n")}}async handleAiSessionInterruptCommand(e){if(!e.activeInstanceId||!e.activeAiSessionId)return{accepted:!1,routed:!1,binding:e,reply:"No active AI session is selected. Use /sessions and /session <id> first."};const t=await this.deps.requireControlledInstance(e.activeInstanceId,!0),n=await this.deps.interruptAiSession(t.id,e.activeAiSessionId);return{accepted:!0,routed:!0,binding:e,instance:Gr(t),aiSession:n,reply:`Interrupt sent to ${t.name} / ${e.activeAiSessionId}.`}}}function Oa(e){return e.providerTurnId||e.turnId||""}function Ra(e){return ka($a(e.status))}function ja(e,t){const n=$a(t.appId)||"App",s=$a(t.title).trim();if(s&&s!==n)return s;const i=function(e,t){for(const n of e.appInventory?.items||[])if(n.id===t)return n.name;return""}(e,n);return i||n}function Da(e){const t=function(e){return"online"!==e.connectionStatus?[]:(e.appInventory?.items||[]).filter(e=>"available"===e.availability)}(e),n=new Map;for(const e of t){const t=La(e);t&&!n.has(t.id)&&n.set(t.id,t)}return[...n.values()]}function Ma(e,t,n,s){const i=$a(t.id)?function(e,t){return`/apps/access/${qa(e)}?token=${encodeURIComponent(t())}`}(t,s):`/instances/${encodeURIComponent(e.id)}/`,r=function(e,t){const n=$a(t);if(n&&new RegExp("^https?://","i").test(n))return new URL(e,n.endsWith("/")?n:n+"/").toString()}(i,n);return{text:r||i,url:r}}function qa(e){const t=$a(e.kind);return"gui"===t?"vnc":"web"===t?"web":"tty"}function $a(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function La(e){if("string"==typeof e&&e.trim())return{id:e,label:Ba(e)};const t=function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e),n=$a(t.id).trim();return n?{id:n,label:$a(t.name).trim()||Ba(n)}:void 0}function Ba(e){return{"terminal-tty":"Terminal","gui-terminal":"GUI Terminal",chromium:"Chromium",browser:"Browser","vscode-web":"VS Code"}[e]||e.replace(/[-_]+/g," ").replace(/\b\w/g,e=>e.toUpperCase())}class Ua{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleInstancesCommand(e){const t=await this.targets.instancesForBinding(e);return{accepted:!0,routed:!1,binding:e,instances:t,reply:(n=t.length,0===n?"No controlled instances are registered yet.":`Instances: ${n}. Tap an instance to create an app.`),replyMarkup:t.length?Ia(t.map(e=>{return{text:wa(e.name||e.id,56),callbackData:(t=this.deps.createChatActionToken({type:"instance-app-menu",instanceId:e.id}).token,`task_handoff:cp_i:${t}`)};var t})):void 0};var n}async handleInstanceAppMenuAction(e,t){const n=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!n)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const s=Da(n);return{accepted:!0,routed:!1,binding:e,instance:n,message:`Select an app for ${n.name}.`,reply:s.length?`New app for ${n.name}`:`No launchable apps for ${n.name}.`,replyMarkup:s.length?Ia(s.map(e=>{return{text:wa(e.label,56),callbackData:(t=this.deps.createChatActionToken({type:"launch-app",instanceId:n.id,appId:e.id}).token,`task_handoff:cp_a:${t}`)};var t})):void 0}}async handleLaunchAppAction(e,t,n){const s=(await this.targets.instancesForBinding(e)).find(e=>e.id===t);if(!s)return{accepted:!1,routed:!1,binding:e,reply:"Instance not found."};const i=Da(s).find(e=>e.id===n);return i?{accepted:!0,routed:!1,binding:e,instance:s,session:await this.deps.launchAppSession(s.id,i.id),message:`${i.label} launched`,reply:`Launched ${i.label} on ${s.name}.`}:{accepted:!1,routed:!1,binding:e,reply:`App ${n} is not launchable for ${s.name}.`}}async handleAppsCommand(e){const t=await this.deps.boardAsync(),n=new Map(t.map(e=>[e.id,e])),s=(await this.deps.listAppSessions()).instances.flatMap(e=>{const t=n.get(e.instanceId);return t?e.appSessions.sessions.filter(Ra).filter(e=>Boolean($a(e.id))).map(e=>({instance:t,session:e,link:Ma(t,e,this.deps.controlPlanePublicBaseUrl(),()=>this.deps.createAppAccessToken({instanceId:t.id,sessionId:$a(e.id)||"",mode:qa(e)}).token)})):[]}),i=s.filter(e=>e.link.url).map(e=>({text:wa(`${e.instance.name} · ${ja(e.instance,e.session)}`,56),url:e.link.url}));return{accepted:!0,routed:!1,binding:e,instances:t,reply:(r=s.length,o=i.length,0===r?"No app sessions found.":0===o?`App sessions: ${r}. Set the control plane public URL in Settings > Appearance to open them from chat.`:`App sessions: ${r}. Tap a button to open.`),replyMarkup:i.length?Ia(i):void 0};var r,o}}class Fa{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async forwardChatMessage(e,t){const n=t.target?.instanceId||e.activeInstanceId||"",s=t.target?.aiSessionId||e.activeAiSessionId,i=await this.deps.requireControlledInstance(n,!0),r=i.projectId?this.deps.requireProject(i.projectId):void 0;if("online"!==i.connectionStatus){const e=new Error(`Instance ${i.name} is not reachable.`);throw Object.assign(e,{statusCode:409,code:"CHAT_TARGET_UNREACHABLE"}),e}if(s){const n=await this.trySendAiSessionMessage(e,i.id,s,t.message.text,t.message.attachments,!t.target);return n.ok?this.sentResult(e,r,i,s,n.aiSession):this.failedSendResult(n.binding,r,i,n.reply)}const o=await this.targets.defaultAiSessionIdForInstance(i.id);if(o){const n=this.deps.upsertChatSession({...e,activeAiSessionId:o}),s=await this.trySendAiSessionMessage(n,i.id,o,t.message.text,t.message.attachments);return s.ok?this.sentResult(n,r,i,o,s.aiSession):this.failedSendResult(s.binding,r,i,s.reply)}return{accepted:!1,routed:!1,binding:e,project:r||void 0,instance:Gr(i),reply:`No active AI session is selected for ${i.name}. Use /sessions and /session <id> first.`}}async trySendAiSessionMessage(e,t,n,s,i=[],r=!0){try{return{ok:!0,binding:e,aiSession:await this.deps.sendAiSessionMessage(t,n,s||"请查看附件。",void 0,i)}}catch(t){return function(e){return"AI_SESSION_NOT_FOUND"===(e&&"object"==typeof e?e:{}).code}(t)?{ok:!1,binding:r?this.deps.upsertChatSession({...e,activeAiSessionId:void 0}):e,reply:`The selected AI session ${n} no longer exists. Use /session to choose an active AI session.`}:{ok:!1,binding:e,reply:`Failed to send to AI session ${n}: ${Yr(t)}`}}}failedSendResult(e,t,n,s){return{accepted:!1,routed:!1,binding:e,project:t||void 0,instance:Gr(n),reply:s}}sentResult(e,t,n,s,i){return{accepted:!0,routed:!0,binding:e,project:t||void 0,instance:Gr(n),aiSession:i,instanceId:n.id,aiSessionId:s,turnId:(r=i,r.turnId||r.providerTurnId||r.session.activeTurnId||""),providerTurnId:Oa(i),reply:`Sent to ${t?.name||n.sourceSnapshot.name||"Source"} / ${n.name} / ${s}.`};var r}}class Ha{deps;constructor(e){this.deps=e}async handlePendingCommand(e){const t=await this.deps.listPendingRoutes(),n=e.activeInstanceId?t.filter(t=>t.instanceId===e.activeInstanceId):t;return{accepted:!0,routed:!1,binding:e,pending:n,reply:n.length?n.map(Ea).join("\n"):"No pending routes.",replyMarkup:n.length?Na(n,(e,t)=>this.deps.pendingDecisionCallbackData(e.id,t)):void 0}}async handlePendingDecisionCommand(e,t,n){const s=Qr(t[0]);return s?s.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:s,aiSession:await this.deps.resolveAiSessionApproval(s.instanceId,s.aiSessionId,n),reply:`${n} sent for ${s.instanceId}:${s.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:s,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:`Usage: /${"allow"===n?"approve":n} <instance-id:pending-id>`}}async handlePendingReplyCommand(e,t){const n=Qr(t[0]),s=t.slice(1).join(" ").trim();return n&&s?n.aiSessionId?{accepted:!0,routed:!0,binding:e,pending:n,aiSession:await this.deps.sendAiSessionMessage(n.instanceId,n.aiSessionId,s),reply:`Reply sent for ${n.instanceId}:${n.aiSessionId}.`}:{accepted:!1,routed:!1,binding:e,pending:n,reply:"Only AI session pending routes are supported here."}:{accepted:!1,routed:!1,binding:e,reply:"Usage: /reply <instance-id:pending-id> <message>"}}}class Wa{deps;targets;constructor(e,t){this.deps=e,this.targets=t}async handleUseCommand(e,t){if(0===t.length)return{accepted:!0,routed:!1,binding:e,instances:await this.targets.instancesForBinding(e),reply:await this.targets.renderUseHelp(e)};if(t.length>=2){const n=this.targets.resolveProject(t[0]),s=await this.targets.resolveInstance(t[1],n.id),i=this.deps.upsertChatSession({...e,activeProjectId:n.id,activeInstanceId:s.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(s.id)});return{accepted:!0,routed:!1,binding:i,reply:await this.targets.renderTargetSelected(i)}}const n=t[0],s=await this.targets.findInstance(n);if(s){const t=s.projectId?this.deps.requireProject(s.projectId):void 0,n=this.deps.upsertChatSession({...e,activeProjectId:t?.id,activeInstanceId:s.id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(s.id)});return{accepted:!0,routed:!1,binding:n,reply:await this.targets.renderTargetSelected(n)}}const i=this.targets.resolveProject(n),r=(await this.deps.boardAsync()).filter(e=>e.projectId===i.id),o=r.filter(e=>"running"===e.status&&"online"===e.connectionStatus);if(1===o.length){const t=this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:o[0].id,activeAiSessionId:await this.targets.defaultAiSessionIdForInstance(o[0].id)});return{accepted:!0,routed:!1,binding:t,reply:await this.targets.renderTargetSelected(t)}}return{accepted:!0,routed:!1,binding:this.deps.upsertChatSession({...e,activeProjectId:i.id,activeInstanceId:void 0,activeAiSessionId:void 0}),instances:r,reply:`${i.name} selected. Choose an instance with /use ${i.id} <instance-id-or-name>.\n${this.targets.renderInstanceLines(r)}`}}async handleProjectCommand(e,t){if(0===t.length)return{accepted:!1,routed:!1,binding:e,reply:"Usage: /project <project-id-or-name>"};const n=this.targets.resolveProject(t[0]),s=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,i=this.deps.upsertChatSession({...e,activeProjectId:n.id,activeInstanceId:s?.projectId===n.id?s.id:void 0,activeAiSessionId:s?.projectId===n.id?e.activeAiSessionId:void 0});return{accepted:!0,routed:!1,binding:i,reply:`Active project: ${n.name}${i.activeInstanceId?"":". Choose an instance with /use."}`}}}class Ga{deps;constructor(e){this.deps=e}async instancesForBinding(e){const t=await this.deps.boardAsync();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async fullInstancesForBinding(e){const t=await this.deps.listNodeInstances();return e.activeProjectId?t.filter(t=>t.projectId===e.activeProjectId):t}async renderUseHelp(e){return`Active target: ${await this.renderChatTarget(e)}\nUse /instances to list targets, then /use <instance-id-or-name>. Use /sessions to choose an AI session when there is more than one.`}async renderChatStatus(e){const t=e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0;return t?`Active target: ${await this.renderChatTarget(e)}\nStatus: ${t.status}, connection: ${t.connectionStatus}`:`Active target: ${await this.renderChatTarget(e)}`}async renderChatHelp(e){return["TaskHandoff chat commands:","/help - show this help","/status or /target - show the active project, instance, and AI session","/instances - list controlled instances","/apps - list app sessions","/use <instance-id-or-name> - choose an active instance","/use <project-id-or-name> <instance-id-or-name> - choose a project and instance","/project <project-id-or-name> - narrow instance selection to a project","/sessions - list all AI sessions","/session <ai-session-id> - choose an active AI session","/pending - list waiting approvals","/approve <instance-id:pending-id>, /deny <...>, /skip <...> - answer approvals","/reply <instance-id:pending-id> <message> - reply to a waiting session","/cancel - interrupt the active AI session","","Send normal text after selecting a target; if there is only one AI session, it is selected automatically.",`Current target: ${await this.renderChatTarget(e)}`].join("\n")}renderInstanceLines(e){return e.map(e=>`${e.name} (${e.id}) - ${e.status}/${e.connectionStatus}`).join("\n")}async renderChatTarget(e){const t=e.activeProjectId?this.deps.getProject(e.activeProjectId):void 0,n=e.activeInstanceId?await this.deps.requireControlledInstance(e.activeInstanceId).catch(()=>{}):void 0,s=e.activeAiSessionId||"none";return`${t?.name||e.activeProjectId||"none"} / ${n?.name||e.activeInstanceId||"none"} / ${s}`}async renderTargetSelected(e){const t=await this.renderChatTarget(e);return e.activeAiSessionId?`Active target: ${t}`:`Active target: ${t}\nNo AI session selected yet. Use /sessions and /session <id>, or send a message once exactly one session is available.`}async defaultAiSessionIdForInstance(e){const t=await this.aiSessionSnapshotForInstance(e),n=(await this.deps.boardAsync()).find(t=>t.id===e),s=(await this.deps.listAppSessions()).instances.find(t=>t.instanceId===e),i=n?{...n,apps:{...n.apps,sessions:s?.appSessions.sessions||[]}}:void 0,r=t&&n?Ca({...i,aiSessions:t}):t?.sessions||[];return 1===r.length?r[0].id:void 0}async aiSessionChoices(e){return(await this.boardInstancesForBinding({},{refresh:!0})).flatMap(t=>Ca(t).map(n=>({index:0,projectId:t.projectId,projectName:t.project?.name||("string"==typeof t.sourceSnapshot.name?t.sourceSnapshot.name:"Source"),instance:t,session:n,active:e.activeInstanceId===t.id&&e.activeAiSessionId===n.id}))).sort((e,t)=>{const n=xa(t.session)-xa(e.session);return n||(String(e.instance.name||"").localeCompare(String(t.instance.name||""))||e.instance.id.localeCompare(t.instance.id)||za(e.session).localeCompare(za(t.session)))}).map((e,t)=>({...e,index:t}))}async boardInstancesForBinding(e,t={}){const n=await this.deps.boardAsync(),s=await this.deps.listAiSessions(t),i=await this.deps.listAppSessions(t),r=new Map(s.instances.map(e=>[e.instanceId,e.aiSessions])),o=new Map(i.instances.map(e=>[e.instanceId,e.appSessions])),a=n.map(e=>{return{...e,apps:{...e.apps,sessions:o.get(e.id)?.sessions||[]},aiSessions:r.get(e.id)||(t=e.aiSessions.updatedAt,{runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:t})};var t});return e.activeProjectId?a.filter(t=>t.projectId===e.activeProjectId):a}async findAiSession(e){const t=await this.boardInstancesForBinding({},{refresh:!0});for(const n of t){const t=(n.aiSessions?.sessions||[]).find(t=>t.id===e||t.providerSessionId===e);if(t)return{instance:n,session:t}}}async aiSessionSnapshotForInstance(e){const t=await this.deps.listAiSessions();return t.instances.find(t=>t.instanceId===e)?.aiSessions}resolveProject(e){const t=this.deps.getProject(e)||this.deps.listProjects().find(t=>t.name===e);return t||Xr("PROJECT_NOT_FOUND",`Project ${e} was not found.`),t}async resolveInstance(e,t){const n=await this.findInstance(e,t);return n||Xr("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`),n}async findInstance(e,t){return(await this.deps.listNodeInstances()).find(n=>!(n.id!==e&&n.name!==e||t&&n.projectId!==t))}}class Va{deps;targets;sender;appCommands;pendingCommands;aiSessionCommands;targetCommands;constructor(e){this.deps=e,this.targets=new Ga(e),this.sender=new Fa(e,this.targets),this.appCommands=new Ua(e,this.targets),this.pendingCommands=new Ha(e),this.aiSessionCommands=new Pa(e,this.targets),this.targetCommands=new Wa(e,this.targets)}async handleChatGatewayMessage(e){const t=Ws.parse(e),n=this.deps.upsertChatSession({channel:t.source.channel,bridgeId:t.source.bridgeId,chatSessionId:t.source.chatSessionId,userId:t.source.userId}),s=t.message.text.trim();return s.startsWith("/")?this.handleChatCommand(n,s):s||t.message.attachments.length?n.activeInstanceId?this.sender.forwardChatMessage(n,t):{accepted:!1,routed:!1,binding:n,reply:await this.targets.renderUseHelp(n)}:{accepted:!1,routed:!1,binding:n,reply:""}}async handleChatGatewayAction(e){const t=this.deps.upsertChatSession({channel:e.source.channel,bridgeId:e.source.bridgeId,chatSessionId:e.source.chatSessionId,userId:e.source.userId});return"ai-session"===e.action.type?this.aiSessionCommands.handleAiSessionIndexAction(t,e.action.index):"instance-app-menu"===e.action.type?this.appCommands.handleInstanceAppMenuAction(t,e.action.instanceId):"launch-app"===e.action.type?this.appCommands.handleLaunchAppAction(t,e.action.instanceId,e.action.appId):"pending-decision"===e.action.type?this.pendingCommands.handlePendingDecisionCommand(t,[e.action.routeId],e.action.decision):{accepted:!1,routed:!1,binding:t,reply:"Unknown action."}}async handleChatCommand(e,t){const[n="",...s]=t.split(/\s+/),i=n.replace(/@[\w_]+$/,"");switch(i.toLowerCase()){case"/help":case"/start":return{accepted:!0,routed:!1,binding:e,reply:await this.targets.renderChatHelp(e)};case"/sessions":return this.aiSessionCommands.handleAiSessionsCommand(e);case"/session":return this.aiSessionCommands.handleAiSessionCommand(e,s);case"/cancel":case"/interrupt":return this.aiSessionCommands.handleAiSessionInterruptCommand(e);case"/pending":return this.pendingCommands.handlePendingCommand(e);case"/approve":return this.pendingCommands.handlePendingDecisionCommand(e,s,"allow");case"/deny":return this.pendingCommands.handlePendingDecisionCommand(e,s,"deny");case"/skip":return this.pendingCommands.handlePendingDecisionCommand(e,s,"skip");case"/reply":return this.pendingCommands.handlePendingReplyCommand(e,s);case"/instances":return this.appCommands.handleInstancesCommand(e);case"/apps":return this.appCommands.handleAppsCommand(e);case"/use":return this.targetCommands.handleUseCommand(e,s);case"/project":return this.targetCommands.handleProjectCommand(e,s);case"/status":case"/target":return{accepted:!0,routed:!1,binding:e,instance:e.activeInstanceId?(await this.deps.boardAsync()).find(t=>t.id===e.activeInstanceId):void 0,reply:await this.targets.renderChatStatus(e)};default:return{accepted:!1,routed:!1,binding:e,reply:`Unknown command: ${i||n}. Try /help.`}}}}const Ka=o.z.object({id:o.z.string().trim().min(1),preferences:Ti,createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict();function Ja(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=t.preferences&&"object"==typeof t.preferences&&!Array.isArray(t.preferences)?t.preferences:{};return{id:t.id,preferences:{import:"string"==typeof n.import&&n.import.trim()?n.import:".",export:"string"==typeof n.export&&n.export.trim()?n.export:"."},createdAt:t.createdAt,updatedAt:t.updatedAt}}function Qa(){return Ti.parse({})}function Ya(e){return/^[a-z]:[\\/]/i.test(e)||/^[\\/]{2}[^\\/]+[\\/]+[^\\/]+/.test(e)}function Xa(e){return Ya(e)?t.win32:t.posix}function Za(e){return{enabledCount:e.deployments.filter(e=>e.enabled).length,runningCount:e.runtime.filter(e=>"running"===e.status).length,errorCount:e.runtime.filter(e=>"error"===e.status).length,configs:e.configs.map(t=>({configHash:t.configHash,config:t,deployments:e.deployments.filter(e=>e.configHash===t.configHash),runtime:e.runtime.filter(e=>e.configHash===t.configHash)})),recentRuns:e.recentRuns.slice(0,20)}}const ed="task-handoff.control-plane.local",td="task-handoff.control-plane.builtin";function nd(e){return"true"===e.labels[ed]}function sd(e){return"true"===e.labels[td]}class id{projects;models;modelService;images;nodes;pendingPairingRevokes;chatSessions;chatBridges;triggers;nodeJoinInvites;nodeJoinService;configSyncPreferences;settings;paths;fetchImpl;dockerCommandRunner;logger;appAccessService;chatActionTokenService=new pa;nodeAgentTransportResolver;proxyPrivateStore;proxyLifecycle;nodeConnectionManager;nodeAgentGateway;controlledInstanceGateway;aiSessionActionService;controlledInstanceCreator;instanceBoardReader=new Vo;controlPlaneTriggerService;catalogService;marketCatalogService;chatBridgeService;chatSessionService;chatSessionRuntime;aiSessionSnapshotProvider;appSessionSnapshotProvider;constructor(e,n={}){var s;this.paths=e,this.fetchImpl=n.fetchImpl||fetch,this.dockerCommandRunner=n.dockerCommandRunner,this.proxyPrivateStore=new Ur(function(e){const n=t.resolve(e,"control-plane-proxy");return{pendingClaimsDir:t.join(n,"pending-claims"),nodeCredentialsDir:t.join(n,"node-credentials"),identityPath:t.join(n,"identity.json")}}(e.dataDir),(e,t)=>this.logWarn(t,e)),this.nodeAgentTransportResolver=new _r({direct:Er(this.fetchImpl),tunnel:n.nodeAgentTransport,proxy:new Mr({credentialForNode:e=>this.proxyPrivateStore.nodeCredential(e.id),fetchImpl:this.fetchImpl})}),this.logger=(s=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(s||"").trim().toLowerCase())?n.logger:void 0);const i=new cr({request:(e,t,n)=>this.nodeAgentFetch(e,t,n),logger:this.logger});this.nodeAgentGateway=new lr(i),this.controlledInstanceGateway=new Wo({requireNode:e=>this.requireNode(e),nodeAgentTransport:e=>this.nodeAgentTransportResolver.resolve(e)}),this.aiSessionActionService=new sr({requireInstance:e=>this.requireControlledInstance(e,!0),request:(e,t,n)=>this.instanceRequest(e,t,n),requireRuntime:(e,t)=>this.requireNodeRuntimeOnNode(e,t),refreshSnapshots:()=>Promise.all([this.listAppSessions({refresh:!0}),this.listAiSessions({refresh:!0})]),warn:(e,t)=>this.logWarn(e,t)});const r=e=>({schema:e,logger:(e,t)=>this.logWarn(t,e)});this.projects=new ri(e.projectsDir,{...r(jn),sanitize:Kt}),this.models=new ri(e.modelsDir,r(Dn)),this.modelService=new Fo({models:this.models,gateway:this.nodeAgentGateway,listNodes:()=>this.listNodes(),requireNode:e=>this.requireNode(e)}),this.images=new ri(e.imagesDir,{...r(ss),sanitize:e=>ms(e,e=>this.logWarn(e,"legacy image profile field was migrated"))}),this.nodes=new ri(e.nodesDir,{...r(ys),sanitize:e=>function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=new Set(["id","name","connectionMode","connectionPath","connectionEnabled","auth","endpoint","controlEndpoint","containerEndpoint","publicWebBase","status","health","capabilities","proxyState","appInventory","labels","lastSeenAt","createdAt","updatedAt"]);for(const e of Object.keys(n))s.has(e)||t?.({field:e});const i=n.auth&&"object"==typeof n.auth&&!Array.isArray(n.auth)?Bs(n.auth,["mode","keyId","secret","pairedAt","pairing"]):n.auth;i&&"object"==typeof i&&"pairing"in i&&(i.pairing=Bs(i.pairing,["status","joinToken","expiresAt"]));const r=n.connectionPath&&"object"==typeof n.connectionPath&&!Array.isArray(n.connectionPath)?"control-plane-proxy"===n.connectionPath.kind?Bs(n.connectionPath,["kind","proxyId","proxyBindingId","targetNodeId"]):Bs(n.connectionPath,["kind"]):{kind:"direct"},o=n.proxyState&&"object"==typeof n.proxyState&&!Array.isArray(n.proxyState)?Bs(n.proxyState,["reachability","bindingStatus","bindingRevision","streamId","revision","observedAt","target","lastError","updatedAt"]):n.proxyState;if(o&&"object"==typeof o){const e=o;e.target=Bs(e.target,["id","name","status","health","lastSeenAt","capabilities"]),e.lastError=Bs(e.lastError,["code","message","retryable","details"])}return{...Object.fromEntries(Object.entries(n).filter(([e])=>s.has(e))),auth:i,connectionPath:r,proxyState:o}}(e,e=>this.logWarn(e,"unknown stored node field was ignored"))}),this.pendingPairingRevokes=new ri(e.pendingPairingRevokesDir,r(Eo)),this.proxyLifecycle=new to({nodes:this.nodes,privateStore:this.proxyPrivateStore,fetchImpl:this.fetchImpl,requireNode:e=>this.requireNode(e),deleteNode:e=>this.deleteNode(e)}),this.nodeConnectionManager=new _o({nodes:this.nodes,pendingPairingRevokes:this.pendingPairingRevokes,fetchImpl:this.fetchImpl,localNodeLabel:ed,builtinNodeLabel:td,info:(e,t)=>this.logInfo(e,t),warn:(e,t)=>this.logWarn(e,t)}),this.chatSessions=new ri(e.chatSessionsDir,r(Hs)),this.chatBridges=new ri(e.chatBridgesDir,r(Fs)),this.triggers=new ri(e.triggersDir,r(fo)),this.nodeJoinInvites=new ri(e.nodeJoinInvitesDir,r($o)),this.nodeJoinService=new Bo({invites:this.nodeJoinInvites,nodes:this.nodes}),this.configSyncPreferences=new ri(t.join(e.dataDir,"config-sync-preferences"),{...r(Ka),sanitize:Ja}),this.settings=new oi(e.settingsPath,()=>co.parse({}),{...r(co),sanitize:po}),this.marketCatalogService=new sa,this.catalogService=new Zo({projects:this.projects,images:this.images,market:this.marketCatalogService,settings:this.settings,defaultNodeId:()=>this.defaultNodeId()}),this.controlledInstanceCreator=new Ko({gateway:this.nodeAgentGateway,defaultNodeId:()=>this.defaultNodeId(),requireProject:e=>this.requireProject(e),requireNode:e=>this.requireNode(e),requireRuntime:(e,t)=>this.requireNodeRuntimeOnNode(e,t),requireLocalFolder:(e,t)=>this.requireNodeLocalFolder(e,t),resolveImageSelection:e=>this.catalogService.resolveImageSelection(e),prepareModels:(e,t)=>this.modelService.prepareAssignment(e,t)}),this.controlPlaneTriggerService=new Yo({triggers:this.triggers,listInstances:()=>this.listNodeInstances(),requireInstance:async e=>this.requireControlledInstance(e,!0),instanceRequest:(e,t,n)=>this.instanceRequest(e,t,n)}),this.appAccessService=new ra({requireInstance:async e=>this.requireControlledInstance(e,!0),listAppSessions:()=>this.listAppSessions()}),this.chatBridgeService=new ha({chatBridges:this.chatBridges,chatSessions:this.chatSessions}),this.chatSessionService=new fa({chatSessions:this.chatSessions}),this.chatSessionRuntime=new Va({upsertChatSession:e=>this.upsertChatSession(e),boardAsync:()=>this.boardAsync(),listAiSessions:e=>this.listAiSessions(e),listAppSessions:e=>this.listAppSessions(e),listNodeInstances:()=>this.listNodeInstances(),listPendingRoutes:()=>this.listPendingRoutes(),pendingDecisionCallbackData:(e,t)=>this.pendingDecisionCallbackData(e,t),createChatActionToken:e=>this.createChatActionToken(e),createAppAccessToken:e=>this.createAppAccessToken(e),controlPlanePublicBaseUrl:()=>this.getSettings().publicBaseUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_URL||process.env.TASK_HANDOFF_PUBLIC_BASE||process.env.TASK_HANDOFF_CONTROL_PLANE_PUBLIC_URL,requireControlledInstance:(e,t)=>this.requireControlledInstance(e,t),requireProject:e=>this.requireProject(e),getProject:e=>this.catalogService.getProject(e),listProjects:()=>this.catalogService.listProjects(),resolveAiSessionApproval:(e,t,n)=>this.resolveAiSessionApproval(e,t,n),sendAiSessionMessage:(e,t,n,s,i)=>this.sendAiSessionMessage(e,t,n,s,i),interruptAiSession:(e,t)=>this.interruptAiSession(e,t),launchAppSession:(e,t,n)=>this.launchAppSession(e,t,n)})}logInfo(e,t){this.logger?.info?.(e,t)}logWarn(e,t){this.logger?.warn?.(e,t)}logError(e,t){this.logger?.error?.(e,t)}setNodeAgentTransport(e){this.nodeAgentTransportResolver.setTunnel(e)}resolveNodeAgentTransport(e){return this.nodeAgentTransportResolver.resolve(e)}setAiSessionSnapshotProvider(e){this.aiSessionSnapshotProvider=e}async listAiSessions(e={}){return this.aiSessionSnapshotProvider?this.aiSessionSnapshotProvider(e):this.bootstrapAiSessionsFromInstances()}setAppSessionSnapshotProvider(e){this.appSessionSnapshotProvider=e}async listAppSessions(e={}){return this.appSessionSnapshotProvider?this.appSessionSnapshotProvider(e):this.bootstrapAppSessionsFromInstances()}init(){this.projects.init(),this.models.init(),this.images.init(),this.nodes.init(),this.pendingPairingRevokes.init(),this.chatSessions.init(),this.chatBridges.init(),this.triggers.init(),this.nodeJoinInvites.init(),this.proxyPrivateStore.init(),this.proxyPrivateStore.gcNodeCredentials(e=>{const t=this.nodes.get(e.nodeId);return"control-plane-proxy"===t?.connectionMode&&"control-plane-proxy"===t.connectionPath.kind&&t.connectionPath.proxyBindingId===e.proxyBindingId&&t.connectionPath.targetNodeId===e.targetNodeId}),this.settings.init(),this.migrateLegacyImageCatalog(),this.seedDefaults()}migrateLegacyImageCatalog(){const e=Object.keys(Gt),s=[];for(const t of e){const e=this.images.filePath(t);if(n.existsSync(e))try{s.push({id:t,filePath:e,record:JSON.parse(n.readFileSync(e,"utf8"))})}catch(e){return this.writeMarketMigrationState("failed",{code:"LEGACY_IMAGE_BACKUP_READ_FAILED",legacyId:t,error:Yr(e)}),void this.logWarn({legacyId:t,error:Yr(e)},"legacy embedded image migration was not applied")}}if(!s.length)return;const i=n.readdirSync(this.paths.projectsDir).filter(e=>e.endsWith(".json")).sort().map(e=>t.join(this.paths.projectsDir,e)),r=[];for(const e of i)try{const t=JSON.parse(n.readFileSync(e,"utf8")),s=jn.parse(Kt(t));r.push({filePath:e,raw:t,migrated:s})}catch(t){return this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_VALIDATION_FAILED",filePath:e,error:Yr(t)}),void this.logWarn({filePath:e,error:Yr(t)},"legacy embedded image migration was not applied")}const o=r.find(({migrated:t})=>{const n=t.defaultImageSelection?.imageId;return!!n&&e.includes(n)});if(o)this.writeMarketMigrationState("failed",{code:"PROJECT_REFERENCE_MIGRATION_INCOMPLETE",filePath:o.filePath});else{n.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketMigrationBackupPath,`${JSON.stringify({version:Tt,createdAt:Kr(),images:s.map(({id:e,record:t})=>({id:e,record:t})),projects:r.map(({filePath:e,raw:n})=>({file:t.basename(e),record:n}))},null,2)}\n`,{encoding:"utf8"});for(const{migrated:e}of r)this.projects.put(e);for(const{id:e}of s)this.images.delete(e),this.logWarn({legacyId:e,marketId:Gt[e]},"embedded image profile migrated to Market");this.writeMarketMigrationState("complete",{code:"LEGACY_IMAGE_MIGRATION_COMPLETE",migratedImageIds:s.map(({id:e})=>e),backupPath:this.paths.marketMigrationBackupPath})}}writeMarketMigrationState(e,t){n.mkdirSync(this.paths.marketDir,{recursive:!0}),c.sync(this.paths.marketStatePath,`${JSON.stringify({status:e,updatedAt:Kr(),...t},null,2)}\n`,{encoding:"utf8"})}getSettings(){return this.catalogService.getSettings()}updateSettings(e){return this.catalogService.updateSettings(e)}seedDefaults(){return this.catalogService.seedDefaults()}listProjects(){return this.catalogService.listProjects()}defaultNodeId(){return this.nodes.list().find(nd)?.id||this.nodes.list()[0]?.id}syncLocalNodeConnection(){return this.nodeConnectionManager.syncLocal()}recoverPendingPairingRevokes(){return this.nodeConnectionManager.recoverPendingPairingRevokes()}listModels(){return this.modelService.list()}listFederatedModels(e){return this.modelService.listFederated(e)}createModel(e){return this.modelService.create(e)}updateModel(e,t){return this.modelService.update(e,t)}deleteModel(e){return this.modelService.delete(e)}reorderModels(e){return this.modelService.reorder(e)}createNodeModel(e,t){return this.modelService.createOnNode(e,t)}updateNodeModel(e,t,n){return this.modelService.updateOnNode(e,t,n)}deleteNodeModel(e,t){return this.modelService.deleteOnNode(e,t)}createProject(e){return this.catalogService.createProject(e)}updateProject(e,t){return this.catalogService.updateProject(e,t)}deleteProject(e){return this.catalogService.deleteProject(e)}listImages(){return this.catalogService.listImages()}getMarketCatalog(){return this.catalogService.getMarketCatalog()}async refreshMarketCatalog(){return await this.marketCatalogService.refresh(new na),this.getMarketCatalog()}listImageOptions(){return this.catalogService.listImageOptions()}createImage(e){return this.catalogService.createImage(e)}async listNodeDockerImages(e){const t=this.requireNode(e);return this.nodeAgentGateway.listDockerImages(t)}async listNodeImageAvailability(e){const t=this.requireNode(e),n=t.capabilities.agent&&"object"==typeof t.capabilities.agent?t.capabilities.agent:{},s={os:"string"==typeof n.platform?n.platform:void 0,architecture:"string"==typeof n.arch?"x64"===n.arch?"amd64":n.arch:void 0},i=this.catalogService.listImageOptions(s);try{const e=await this.nodeAgentGateway.listDockerImages(t);return i.map(t=>{const n=e.find(e=>e.reference===t.reference||!!t.digest&&e.repoDigests.some(e=>e.endsWith(`@${t.digest}`))||e.repoDigests.includes(t.reference));return Es.parse({image:t,status:n?"available":"pull-required",localImage:n,localSizeBytes:n?.sizeBytes,downloadSizeBytes:n?void 0:t.downloadSizeBytes,unpackedSizeBytes:n?void 0:t.unpackedSizeBytes})})}catch(e){const t=e instanceof Error?e.message:String(e);return i.map(e=>Es.parse({image:e,status:"unknown",error:t}))}}async listNodeLocalFolders(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.listLocalFolders(n,{signal:t})}async listNodeFolderTree(e,t={}){const n=this.requireNode(e);return this.nodeAgentGateway.listFolderTree(n,t)}async createNodeLocalFolder(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.createLocalFolder(n,t)}async deleteNodeLocalFolder(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteLocalFolder(n,t)}async checkNode(e){const t=this.requireNode(e);try{const e=await this.nodeAgentGateway.health(t),n=e&&"object"==typeof e?e:{},s="string"==typeof n.nodeId&&n.nodeId.trim()?n.nodeId.trim():t.id,i=ys.parse({...t,id:s,status:"online",health:"ok",capabilities:"object"==typeof e&&e?{...t.capabilities,agent:Fr(e)}:t.capabilities,lastSeenAt:Kr(),updatedAt:Kr()});return s!==t.id&&this.nodes.delete(t.id),this.nodes.put(i),{id:i.id,status:"online",checkedAt:Kr(),agent:e}}catch(e){const n=ys.parse({...t,status:"offline",health:"failed",updatedAt:Kr()});return this.nodes.put(n),{id:t.id,status:"offline",checkedAt:Kr(),error:e instanceof Error?e.message:String(e)}}}async checkNodeUpdate(e,t){return this.nodeAgentGateway.checkUpdate(this.requireNode(e),t)}async applyNodeUpdate(e,t){return this.nodeAgentGateway.applyUpdate(this.requireNode(e),t)}async listNodeUpdateJobs(e){return this.nodeAgentGateway.listUpdateJobs(this.requireNode(e))}async getLocalNodeExternalListener(e){return this.nodeAgentGateway.getExternalListener(this.requireLocalListenerNode(e))}async updateLocalNodeExternalListener(e,t){return this.nodeAgentGateway.updateExternalListener(this.requireLocalListenerNode(e),t)}async createNodePairingInvite(e,t={}){const n=this.requireNode(e);return this.nodeAgentGateway.createPairingInvite(n,t)}requireLocalListenerNode(e){const t=this.requireNode(e);if(!sd(t)||!nd(t)){const e=new Error("Node agent TCP listener settings are available only for the built-in local node.");throw Object.assign(e,{statusCode:403,code:"LOCAL_NODE_LISTENER_ONLY"}),e}return t}createNodeJoinInvite(e={}){return this.nodeJoinService.createInvite(e)}completeNodeJoin(e){return this.nodeJoinService.complete(e)}async connectNodeToControlPlane(e,t){const n=this.requireNode(e),s=To.parse(t);return this.nodeAgentGateway.createControlPlaneConnection(n,s)}async listNodeControlPlanePairings(e){const t=this.requireNode(e);return this.nodeAgentGateway.listControlPlanePairings(t)}async deleteNodeControlPlanePairing(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteControlPlanePairing(n,t)}async listNodeControlPlaneConnections(e){const t=this.requireNode(e);return this.nodeAgentGateway.listControlPlaneConnections(t)}async deleteNodeControlPlaneConnection(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteControlPlaneConnection(n,t)}updateImage(e,t){return this.catalogService.updateImage(e,t)}async deleteImage(e){this.catalogService.requireImage(e);const t=this.listProjects().find(t=>t.defaultImageSelection?.imageId===e);if(t)throw Object.assign(new Error(`Image ${e} is the default for project ${t.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const n=(await Promise.all(this.listNodes().map(e=>this.nodeAgentGateway.listLocalFolders(e)))).flat().find(t=>t.defaultImageSelection?.imageId===e);if(n)throw Object.assign(new Error(`Image ${e} is the default for local folder ${n.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});const s=(await this.listNodeInstances()).find(t=>t.imageSelection?.imageId===e);if(s)throw Object.assign(new Error(`Image ${e} is used by instance ${s.name}.`),{statusCode:409,code:"IMAGE_IN_USE"});return this.catalogService.deleteImage(e)}listNodes(){return this.nodes.list()}listPublicNodes(){return this.listNodes().map(Hr)}createNode(e){return this.nodeConnectionManager.create(e)}listPendingProxyClaims(){return this.proxyLifecycle.listPendingClaims()}claimProxyNode(e){return this.proxyLifecycle.claimNode(e)}resumeProxyClaim(e){return this.proxyLifecycle.resumeClaim(e)}cancelProxyClaim(e){return this.proxyLifecycle.cancelClaim(e)}applyProxyTargetSnapshot(e,t){return this.proxyLifecycle.applyTargetSnapshot(e,t)}applyProxyTargetEvent(e,t){return this.proxyLifecycle.applyTargetEvent(e,t)}markProxyUnavailable(e,t){return this.proxyLifecycle.markUnavailable(e,t)}markProxyBindingRevoked(e,t){return this.proxyLifecycle.markBindingRevoked(e,t)}updateNode(e,t){const n=zo.parse(t),s=this.requireNode(e);this.proxyLifecycle.assertIdentityPatch(s,n);const i=ys.parse({...s,...n,id:e,createdAt:s.createdAt,updatedAt:Kr()});return this.nodes.put(i)}deleteNode(e){if(sd(this.requireNode(e))){const e=new Error("The built-in local node connection cannot be deleted.");throw Object.assign(e,{statusCode:400,code:"LOCAL_NODE_CANNOT_BE_DELETED"}),e}return this.nodes.delete(e)}async deleteNodeWithProxyLifecycle(e,t=!1){return this.proxyLifecycle.deleteNode(this.requireNode(e),t)}async listNodeRuntimes(e,t){const n=e?[this.requireNode(e)]:this.listNodes();return e?this.nodeAgentGateway.listRuntimes(n[0],{signal:t}):(await this.nodeAgentGateway.listFleetRuntimes(n,{signal:t})).items}async listNodeRuntimesWithDiagnostics(e){return this.nodeAgentGateway.listFleetRuntimes(this.listNodes(),{signal:e})}async createNodeRuntime(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.createRuntime(n,t)}async updateNodeRuntime(e,t,n){const s=this.requireNode(e);return this.nodeAgentGateway.updateRuntime(s,t,n)}async deleteNodeRuntime(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.deleteRuntime(n,t)}async checkNodeRuntime(e,t){const n=this.requireNode(e);return this.nodeAgentGateway.checkRuntime(n,t)}async listControlledInstances(){return(await this.listNodeInstances()).map(Gr)}async listControlledInstancesWithDiagnostics(){const e=await this.listNodeInstancesWithDiagnostics();return{items:e.items.map(Gr),nodeErrors:e.nodeErrors}}async instanceResourceMetrics(e){const t=await this.requireNodeInstance(e);return this.nodeAgentGateway.instanceResourceMetrics(this.requireNode(t.nodeId),t.id)}async nodeOwnsInstance(e,t){const n=this.requireNode(e);return(await this.nodeAgentGateway.listInstances(n)).some(n=>n.id===t&&n.nodeId===e)}createControlledInstance(e){return this.controlledInstanceCreator.create(e)}async updateControlledInstance(e,t){const n=Ao.parse(t),s=await this.requireNodeInstance(e),i=this.requireNode(s.nodeId),{modelSelection:r,...o}=n;let a=Object.keys(o).length?await this.nodeAgentGateway.updateInstance(i,e,o):s;if(r){const t=await this.modelService.prepareAssignment(i,r);a=(await this.nodeAgentGateway.assignInstanceModels(i,e,t)).instance}return Gr(a)}async deleteControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId),s=await this.nodeAgentGateway.deleteInstance(n,e);return this.configSyncPreferences.delete(e),Boolean(s.deleted)}async startControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId);return await this.modelService.ensureInstanceAssignment(t),Gr(await this.nodeAgentGateway.startInstance(n,e))}async stopControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId);return Gr(await this.nodeAgentGateway.stopInstance(n,e))}async restartControlledInstance(e){const t=await this.requireNodeInstance(e),n=this.requireNode(t.nodeId);return await this.modelService.ensureInstanceAssignment(t),Gr(await this.nodeAgentGateway.restartInstance(n,e))}async retryControlledInstanceImageProvisioning(e){const t=await this.requireNodeInstance(e);return Gr(await this.nodeAgentGateway.retryInstanceImageProvisioning(this.requireNode(t.nodeId),e))}async boardAsync(){return(await this.boardWithDiagnostics()).items}async boardWithDiagnostics(e){const[t,n]=await Promise.all([this.listNodeRuntimesWithDiagnostics(e),this.listNodeInstancesWithDiagnostics(e)]);return this.instanceBoardReader.read({projects:this.listProjects(),images:this.listImageOptions(),nodes:this.nodes.list(),runtimes:t.items,instances:n.items,nodeErrors:[...t.nodeErrors,...n.nodeErrors]})}async bootstrapAiSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!Vs(e)||!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus)return{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions};try{return Se.parse(await this.instanceRequest(e,"/ai-sessions/state"))}catch(t){return this.logWarn({instanceId:e.id,error:Yr(t),errorCode:"AI_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live AI sessions"),{streamId:`bootstrap:${e.id}:ai.sessions`,revision:0,lastEventAt:e.aiSessions.updatedAt,snapshot:e.aiSessions}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,n)=>({instanceId:e.id,streamId:t[n].streamId,aiSessions:t[n].snapshot,revision:t[n].revision,lastEventAt:t[n].lastEventAt}))}}async bootstrapAppSessionsFromInstances(){const e=await this.listNodeInstances(),t=await Promise.all(e.map(async e=>{if(!Vs(e)||!e.target.web||"online"!==e.connectionStatus&&"online"!==e.agentStatus){const t=nr();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:t.updatedAt,snapshot:t}}try{return Li.parse(await this.instanceRequest(e,"/apps/sessions/state"))}catch(t){this.logWarn({instanceId:e.id,error:Yr(t),errorCode:"APP_SESSION_LIVE_REFRESH_FAILED"},"control plane failed to refresh live app sessions");const n=nr();return{streamId:`bootstrap:${e.id}:app.sessions`,revision:0,lastEventAt:n.updatedAt,snapshot:n}}}));return{updatedAt:(new Date).toISOString(),instances:e.map((e,n)=>({instanceId:e.id,streamId:t[n].streamId,appSessions:t[n].snapshot,revision:t[n].revision,lastEventAt:t[n].lastEventAt}))}}async recoverAiSessionDelta(e,t,n){const s=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(n)});return _e.parse(await this.instanceRequest(s,`/ai-sessions?${i}`))}async recoverAiSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return Se.parse(await this.instanceRequest(t,"/ai-sessions/state"))}async recoverAppSessionDelta(e,t,n){const s=await this.requireControlledInstance(e,!0),i=new URLSearchParams({streamId:t,sinceRevision:String(n)});return Gi.parse(await this.instanceRequest(s,`/apps/sessions?${i}`))}async recoverAppSessionSnapshot(e){const t=await this.requireControlledInstance(e,!0);return Li.parse(await this.instanceRequest(t,"/apps/sessions/state"))}async listTriggers(){return this.controlPlaneTriggerService.listTriggers()}createTrigger(e){return this.controlPlaneTriggerService.createTrigger(e)}async deleteTrigger(e){return this.controlPlaneTriggerService.deleteTrigger(e)}async applyTrigger(e,t){return this.controlPlaneTriggerService.applyTrigger(e,t)}async listInstanceTriggers(e){return this.controlPlaneTriggerService.listInstanceTriggers(e)}async bindAiSessionTrigger(e,t,n){return this.controlPlaneTriggerService.bindAiSessionTrigger(e,t,n)}async unbindAiSessionTrigger(e,t,n){return this.controlPlaneTriggerService.unbindAiSessionTrigger(e,t,n)}async runInstanceTrigger(e,t,n={}){return this.controlPlaneTriggerService.runInstanceTrigger(e,t,n)}listChatSessions(){return this.chatSessionService.list()}createAppAccessToken(e){return this.appAccessService.createToken(e)}resolveAppAccessToken(e,t){return this.appAccessService.resolveToken(e,t)}createChatActionToken(e){return this.chatActionTokenService.create(e)}resolveChatActionToken(e,t){return this.chatActionTokenService.resolve(e,t)}pendingDecisionCallbackData(e,t){return this.chatActionTokenService.pendingDecisionCallbackData(e,t)}async appAccessProxyTarget(e,t,n=""){return this.appAccessService.proxyTarget(e,t,n)}listChatBridges(){return this.chatBridgeService.list()}requireChatBridge(e){return this.chatBridgeService.require(e)}createChatBridge(e){return this.chatBridgeService.create(e)}updateChatBridge(e,t){return this.chatBridgeService.update(e,t)}deleteChatBridge(e){return this.chatBridgeService.delete(e)}requireChatSession(e){return this.chatSessionService.require(e)}upsertChatSession(e){return this.chatSessionService.upsert(e)}async handleChatGatewayMessage(e){return this.chatSessionRuntime.handleChatGatewayMessage(e)}async handleChatGatewayAction(e){return this.chatSessionRuntime.handleChatGatewayAction(e)}async handleChatCommand(e,t){return this.chatSessionRuntime.handleChatCommand(e,t)}async listPendingRoutes(){const e=Kr(),t=[],n=await this.listAiSessions(),s=new Map(n.instances.map(e=>[e.instanceId,e.aiSessions])),i=await this.nodeAgentGateway.listFleetInstances(this.listNodes());for(const n of i.items){if("online"!==n.connectionStatus&&"online"!==n.agentStatus||!n.target.web)continue;const i=n.projectId?this.catalogService.getProject(n.projectId):void 0;for(const r of s.get(n.id)?.sessions||[]){if("waiting"!==r.status)continue;const s=Gs.parse({id:`${n.id}:ai:${r.id}`,instanceId:n.id,projectId:n.projectId,aiSessionId:r.id,providerSessionId:r.providerSessionId,result:r.summary||r.currentTool?.inputPreview||r.lastMessage||`${r.agent} is waiting for approval`,source:r.agent,kind:"approval",status:"pending",lastSeenAt:e});t.push({...s,project:i,instance:Gr(n)})}}return t}async listAiSessionInstanceNames(){return(await this.nodeAgentGateway.listFleetInstances(this.listNodes())).items.map(e=>({id:e.id,name:e.name}))}resolveAiSessionApproval(e,t,n){return this.aiSessionActionService.resolveApproval(e,t,n)}listAiSessionHistory(e){return this.aiSessionActionService.listHistory(e)}getAiSessionHistoryDetail(e,t){return this.aiSessionActionService.historyDetail(e,t)}resumeAiSession(e,t){return this.aiSessionActionService.resume(e,t)}aiSessionActionDiagnostics(){return this.aiSessionActionService.diagnostics()}async launchAppSession(e,t="terminal-tty",n={}){const s=await this.requireControlledInstance(e,!0);if("online"!==s.connectionStatus&&"online"!==s.agentStatus){const e=new Error(`Instance ${s.name} is still starting. Wait for it to connect before launching apps.`);throw Object.assign(e,{statusCode:409,code:"INSTANCE_NOT_CONNECTED"}),e}const i=await this.resolveAppLaunchOptions(s,n),r=await this.instanceRequest(s,"/apps/sessions",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appId:t,...i})});return await this.listAppSessions({refresh:!0}),r}async stopAppSession(e,t){const n=await this.requireControlledInstance(e,!0),s=await this.instanceRequest(n,`/apps/sessions/${encodeURIComponent(t)}/stop`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({})});return await this.listAppSessions({refresh:!0}),s}async instanceAppManagement(e){const t=await this.requireControlledInstance(e,!0);try{return function(e){try{return mn.parse(e)}catch(e){if(e instanceof o.z.ZodError){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}(await this.instanceRequest(t,"/apps/management"))}catch(e){const t=e&&"object"==typeof e?e:{};if(e instanceof o.z.ZodError||404===t.statusCode){const e=new Error("This controlled instance does not support managed app operations.");throw Object.assign(e,{statusCode:409,code:"INSTANCE_APP_MANAGEMENT_UNSUPPORTED"}),e}throw e}}async requestInstanceAppOperation(e,t,n,s={}){const i=await this.requireControlledInstance(e,!0);return fn.parse(await this.instanceRequest(i,`/apps/${encodeURIComponent(t)}/${n}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}))}async instanceAppManagementJob(e,t){const n=await this.requireControlledInstance(e,!0);return fn.parse(await this.instanceRequest(n,`/apps/jobs/${encodeURIComponent(t)}`))}async renameAppSession(e,t,n){const s=await this.requireControlledInstance(e,!0),i=await this.instanceRequest(s,`/apps/sessions/${encodeURIComponent(t)}`,{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({title:n})});return await this.listAppSessions({refresh:!0}),i}sendAiSessionMessage(e,t,n,s,i=[],r=[],o){return this.aiSessionActionService.sendMessage(e,t,n,s,i,r,o)}aiSessionMentionCatalog(e,t){return this.aiSessionActionService.mentionCatalog(e,t)}searchAiSessionMentionFiles(e,t,n){return this.aiSessionActionService.searchMentionFiles(e,t,n)}executeAiSessionCommand(e,t,n){return this.aiSessionActionService.executeCommand(e,t,n)}aiSessionQueue(e,t){return this.aiSessionActionService.queue(e,t)}steerAiSessionQueuedMessage(e,t,n){return this.aiSessionActionService.steerQueuedMessage(e,t,n)}retryAiSessionQueuedMessage(e,t,n){return this.aiSessionActionService.retryQueuedMessage(e,t,n)}removeAiSessionQueuedMessage(e,t,n){return this.aiSessionActionService.removeQueuedMessage(e,t,n)}interruptAiSession(e,t){return this.aiSessionActionService.interrupt(e,t)}async proxyInstanceHttp(e,t,n={}){const s=await this.requireControlledInstance(e,!0);return this.controlledInstanceGateway.proxyHttp(s,t,n)}async proxyInstanceWebSocket(e,t,n,s,i={}){const r=await this.requireControlledInstance(e,!0);this.controlledInstanceGateway.proxyWebSocket(r,t,n,s,i)}async instanceConfigSyncState(e){const t=await this.requireControlledInstance(e,!0),n=o.z.array(xi).parse(await this.instanceRequest(t,"/config-sync/programs"));return Ni.parse({programs:n,preferences:this.configSyncPreferences.get(e)?.preferences||Qa()})}async listInstanceConfigSyncFolders(e,t={}){const n=await this.requireControlledInstance(e,!0),s=new URLSearchParams;return t.path&&s.set("path",t.path),void 0!==t.depth&&s.set("depth",String(t.depth)),o.z.array(_s).parse(await this.instanceRequest(n,"/config-sync/folders"+(s.size?`?${s}`:"")))}async syncInstanceConfigs(e,t){const n=zi.parse(t),s=await this.requireControlledInstance(e,!0);if("export"===n.direction&&"local-folder"!==s.source.type){const e=new Error("Exporting instance config is only available for local folder projects.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_EXPORT_REQUIRES_LOCAL_PROJECT"}),e}const i=function(e){const t=function(e){const t=[];for(const n of e.split("/"))if(n&&"."!==n)if(".."===n){if(!t.length)return"..";t.pop()}else t.push(n);return t.join("/")||"."}(e.trim().replace(/\\/g,"/")||".");if(t.startsWith("/")||".."===t||t.startsWith("../")){const e=new Error("Config sync folder must stay inside the instance workspace.");throw Object.assign(e,{statusCode:400,code:"CONFIG_SYNC_FOLDER_INVALID"}),e}return t||"."}(n.workspaceFolder),r={...n,workspaceFolder:i},o=Pi.parse(await this.instanceRequest(s,"/config-sync",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(r)})),a=Kr(),d=this.configSyncPreferences.get(e);return this.configSyncPreferences.put({id:e,preferences:{...d?.preferences||Qa(),[n.direction]:i},createdAt:d?.createdAt||a,updatedAt:a}),o}async instanceRequest(e,t,n={}){return this.controlledInstanceGateway.request(e,t,n)}async reportInstanceHeartbeat(e,t){await this.controlledInstanceGateway.reportHeartbeat(e,t)}async nodeAgentFetch(e,t,n={}){return this.nodeAgentTransportResolver.resolve(e).request(e,t,n)}async listNodeInstances(){return(await this.listNodeInstancesWithDiagnostics()).items}async listNodeInstancesWithDiagnostics(e){const t=await this.nodeAgentGateway.listFleetInstances(this.listNodes(),{signal:e});return{items:await Promise.all(t.items.map(t=>this.withFreshTriggerSnapshot(t,e))),nodeErrors:t.nodeErrors}}async withFreshTriggerSnapshot(e,t){if(!Vs(e)||"online"!==e.connectionStatus&&"online"!==e.agentStatus)return e;try{const n=He.parse(await this.instanceRequest(e,"/triggers",{signal:t}));return Ds.parse({...e,triggers:Za(n)})}catch{return e}}async requireNodeInstance(e){for(const t of this.listNodes())try{const n=(await this.nodeAgentGateway.listInstances(t)).find(t=>t.id===e);if(n)return n}catch{}Xr("CONTROLLED_INSTANCE_NOT_FOUND",`Controlled instance ${e} was not found.`)}async requireNodeRuntimeOnNode(e,t){const n=(await this.listNodeRuntimes(e)).find(e=>e.id===t);return n||Xr("NODE_RUNTIME_NOT_FOUND",`Runtime ${t} was not found on node ${e}.`),n}async requireNodeLocalFolder(e,t){return this.nodeAgentGateway.requireLocalFolder(e,t)}async resolveAppLaunchOptions(e,t){const n="string"==typeof t.cwdFolderId?t.cwdFolderId.trim():"",s={...t};if(delete s.cwdFolderId,!n)return s;const i=this.requireNode(e.nodeId),r=await this.requireNodeLocalFolder(i,n);if(r.nodeId!==e.nodeId){const t=new Error(`Local folder ${r.id} belongs to node ${r.nodeId}, not ${e.nodeId}.`);throw Object.assign(t,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_INSTANCE_NODE"}),t}const o=await this.requireNodeRuntimeOnNode(e.nodeId,e.runtimeId);return{...s,cwd:this.appLaunchCwdForFolder(e,r,o)}}appLaunchCwdForFolder(e,n,s){if("local"===s.type)return Xa(i=n.path).resolve(i);var i;if("local-folder"!==e.source.type){const e=new Error("App cwd folder selection is only supported for local-folder instances.");throw Object.assign(e,{statusCode:400,code:"APP_CWD_REQUIRES_LOCAL_FOLDER_SOURCE"}),e}const r=function(e,t){if(Ya(e)!==Ya(t))return;const n=Xa(e),s=n.resolve(e),i=n.resolve(t),r=n.relative(s,i);return r?".."===r||r.startsWith(`..${n.sep}`)||n.isAbsolute(r)?void 0:r.split(n.sep).filter(Boolean):[]}(e.source.path,n.path);if(!r){const t=new Error(`Local folder ${n.path} is outside the instance workspace ${e.source.path}.`);throw Object.assign(t,{statusCode:400,code:"APP_CWD_OUTSIDE_WORKSPACE"}),t}const o=e.runtime.workspacePath||e.workspace.path||Wr(e.source).path||"/workspace";return r.length?t.posix.join(o,...r):o}requireProject(e){return this.catalogService.requireProject(e)}requireModel(e,t=!1){return this.modelService.require(e,t)}requireImage(e){return this.catalogService.requireImage(e)}requireNode(e){const t=this.nodes.get(e);return t||Xr("NODE_NOT_FOUND",`Node ${e} was not found.`),t}requirePublicNode(e){return Hr(this.requireNode(e))}async requireControlledInstance(e,t=!1){const n=await this.requireNodeInstance(e);return t?n:Gr(n)}}function rd(e){return m(String(e),"escape").replace(/\n+$/,"")}function od(e){const[t,...n]=String(e).split("\n");return[`*${rd(t)}*`,...n.map(rd)].join("\n")}function ad(e,t=4e3){const n=String(e),s=od(n);if(s.length<=t)return s;const[i,...r]=n.split("\n"),o=r.join("\n"),a="...\n";let d=i;for(;od(`${d}\n${a}`).length>t&&d.length>0;)d=d.slice(0,Math.floor(.8*d.length));let c=0,l=o.length,p=0,u=od(`${d}\n${a}`);for(;c<=l;){const e=Math.floor((c+l)/2),n=od(`${d}\n${a}${o.slice(-e)}`);n.length<=t?(u=n,p=e,c=e+1):l=e-1}const g=o.slice(-p),m=g.indexOf("\n");return m>=0&&m<200&&(u=od(`${d}\n${a}${g.slice(m+1)}`)),u}function dd(e){return String(e).replace(/\\([_*\[\]()~`>#+\-=|{}.!\\])/g,"$1")}async function cd(e,t,n,s,i={}){if(!t.token)return;const r=n=>e(`https://api.telegram.org/bot${t.token}/sendMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),o=function(e,t={}){const n=String(e),s=t.rawMarkdownV2?e=>e:rd;if(s(n).length<=4e3)return[n];const i=[];let r=n;for(;r;){let e=1,t=r.length,n=1;for(;e<=t;){const i=Math.floor((e+t)/2);s(r.slice(0,i)).length<=4e3?(n=i,e=i+1):t=i-1}if(n<r.length){const e=r.slice(0,n),t=Math.max(e.lastIndexOf("\n"),e.lastIndexOf(" "));t>=Math.floor(.6*n)&&(n=t+1)}i.push(r.slice(0,n).trimEnd()),r=r.slice(n).trimStart()}return i.filter(Boolean)}(s,i);let a;for(const[e,t]of o.entries())a=await pd(r,"sendMessage",t,{chat_id:n,...0===e&&i.replyToMessageId?{reply_to_message_id:i.replyToMessageId,allow_sending_without_reply:!0}:{},...e===o.length-1&&i.replyMarkup?{reply_markup:i.replyMarkup}:{}},i);return a}async function ld(e,t,n){if(!t.token||!n)throw new Error("Telegram file id is required.");const s=await e(`https://api.telegram.org/bot${t.token}/getFile`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({file_id:n})}),i=await s.json().catch(()=>({}));if(!s.ok||!1===i.ok||!i.result?.file_path)throw new Error(i.description||`Telegram getFile failed with HTTP ${s.status}`);return`https://api.telegram.org/file/bot${t.token}/${i.result.file_path}`}async function pd(e,t,n,s,i){const r=i.parseMode||"MarkdownV2",o=i.rawMarkdownV2?String(n):rd(n),a=await e({...s,text:o,disable_web_page_preview:!0,parse_mode:r}),d=await a.json().catch(()=>({}));if(a.ok&&!1!==d.ok)return d.result;if("editMessageText"===t&&ud(d.description))return;if(i.rawMarkdownV2&&"MarkdownV2"===r){const i=await e({...s,text:dd(n),disable_web_page_preview:!0,parse_mode:"Markdown"}),r=await i.json().catch(()=>({}));if(i.ok&&!1!==r.ok)return r.result;if("editMessageText"===t&&ud(r.description))return}const c=await e({...s,text:i.rawMarkdownV2?dd(n):String(n),disable_web_page_preview:!0}),l=await c.json().catch(()=>({}));if(c.ok&&!1!==l.ok)return l.result;if("editMessageText"!==t||!ud(l.description))throw new Error(l.description||d.description||`Telegram ${t} failed with HTTP ${c.status}`)}function ud(e){return"string"==typeof e&&e.toLowerCase().includes("message is not modified")}function gd(e,t){if("string"==typeof e&&e.trim())return e.trim();hd(`Chat action token is missing ${t}.`)}function md(e){if("allow"===e||"deny"===e||"skip"===e)return e;hd("Chat action token has an invalid decision.")}function hd(e){const t=new Error(e);throw Object.assign(t,{statusCode:400,code:"CHAT_ACTION_TOKEN_INVALID"}),t}function fd(e){return Buffer.from(e,"base64url").toString("utf8")}function yd(e){return String(e||"").split(/\r?\n/,1)[0].replace(/^#+\s*/,"").trim().slice(0,20)||"消息通知"}function Id(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,"string"==typeof t?t:JSON.stringify(t)]))}function bd(e){return`dtv1.card//IM_GROUP.${e}`}function vd(e){return`dtv1.card//IM_ROBOT.${e}`}function wd(e,t){return Dd(t.biz_conversation_id).trim()||Dd(e.spaceId).replace(/^dtv1\.card\/\/(?:IM_GROUP|IM_SINGLE|IM_ROBOT)\./,"")}function Sd(e,t={}){const n=Dd(e).trim().toUpperCase();return"2"===n||"IM_GROUP"===n?"IM_GROUP":"1"===n||"IM_SINGLE"===n?t.robotMessage?"IM_ROBOT":"IM_SINGLE":"IM_ROBOT"===n?"IM_ROBOT":void 0}function Ad(e){if(e.startsWith("task_handoff:"))return e;if(!e.startsWith("th_cb_"))return"";try{return Buffer.from(e.slice(6),"base64url").toString("utf8")}catch{return""}}function kd(e,t,n,s,i){const r=wd(s,i);return{cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},cardData:{cardParamMap:Id({type:"actions",title:"TaskHandoff",description:e,list:va(t),biz_out_track_id:s.outTrackId||i.biz_out_track_id||"",biz_conversation_id:r,biz_sender_id:i.biz_sender_id||s.userId||"",biz_session_webhook:i.biz_session_webhook||"",biz_conversation_type:i.biz_conversation_type||"",biz_step:n,error_msg:""})}}}function Cd(e,t,n){const s=[];return xd(s,n),xd(s,t),xd(s,e),s.find(e=>Ad(e))||""}function xd(e,t){for(const n of["actionIdList","actionIds"]){const s=t[n];Array.isArray(s)&&e.push(...s.map(e=>Dd(e)).filter(Boolean))}for(const n of["actionId","action_id","id","actionValue"]){const s=Dd(t[n]);s&&e.push(s)}}async function zd(e,t,n,s){s?.("info",{target:"sessionWebhook",hasSessionWebhook:Boolean(t),textPreview:Od(n)},"dingding webhook send requested");const i=await e(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({msgtype:"markdown",markdown:{title:yd(n),text:n}})}),r=await i.json().catch(()=>{});if(s?.(i.ok?"info":"warn",{target:"sessionWebhook",status:i.status,response:Pd(r)},"dingding webhook send completed"),!i.ok)throw new Error(`DingDing webhook failed with HTTP ${i.status}`)}async function Td(e){const t=va(e.replyMarkup);if(!t.length&&!e.forceCard)return e.sessionWebhook&&await zd(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),e.sessionWebhook?{delivered:!0}:void 0;const n=e.bridge.settings||{},s=Dd(n.robotCode),i=Dd(n.cardTemplateId)||"13fc6717-12e4-43ed-8533-111a310d4995.schema",r=Dd(n.cardCallbackRouteKey)||"bi_workflow_ticket",o=e.senderId||e.runtime.senderIds.get(e.chatId)||Dd(n.senderId),a=(d=e.runtime,c=e.chatId,d.conversationTypes?.get(c)||"IM_GROUP");var d,c;if(!s||!i||!o){if(e.sessionWebhook&&!e.forceCard)return await zd(e.fetchImpl,e.sessionWebhook,e.text,e.runtime.onLog),{delivered:!0};throw new Error("DingDing card target is incomplete.")}const l=`task_handoff_cp_${Date.now()}_${Math.random().toString(16).slice(2)}`,p=Id({type:"actions",title:e.title||yd(e.text),description:e.text,list:t,biz_out_track_id:l,biz_step:e.step||"task_handoff_actions",biz_conversation_id:e.chatId,biz_sender_id:o,biz_session_webhook:e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)||"",biz_conversation_type:a,error_msg:""}),u=function(e,t,n,s){return"IM_SINGLE"===e?{openSpaceId:(i=t,`dtv1.card//IM_SINGLE.${i}`),imSingleOpenSpaceModel:{},imSingleOpenDeliverModel:{extension:{}}}:"IM_ROBOT"===e?{openSpaceId:vd(n),imRobotOpenSpaceModel:{supportForward:!1},imRobotOpenDeliverModel:{extension:{},robotCode:s,spaceType:"IM_ROBOT"}}:{openSpaceId:bd(t),imGroupOpenSpaceModel:{supportForward:!1},imGroupOpenDeliverModel:{extension:{},robotCode:s}};var i}(a,e.chatId,o,s);return e.runtime.onLog?.("info",{target:"card",chatId:e.chatId,senderId:o,conversationType:a,openSpaceId:u.openSpaceId,hasSessionWebhook:Boolean(e.sessionWebhook||e.runtime.chatWebhooks.get(e.chatId)),hasReplyMarkup:Boolean(e.replyMarkup),actionCount:t.length,textPreview:Od(e.text)},"dingding card send requested"),await Nd(e.fetchImpl,e.bridge,e.runtime,"POST","/v1.0/card/instances/createAndDeliver",{userId:o,cardTemplateId:i,outTrackId:l,callbackType:"STREAM",callbackRouteKey:r,cardData:{cardParamMap:p},userIdType:Number(n.cardUserIdType||1),...u}),{delivered:!0,outTrackId:l}}async function Nd(e,t,n,s,i,r){const o=await async function(e,t,n){if(n.accessToken&&n.accessToken.expiresAt>Date.now()+6e4)return n.accessToken.value;const s=Dd(t.settings.clientSecret);if(!t.token||!s)throw new Error("DingDing client id/secret is not configured.");const i=await e("https://api.dingtalk.com/v1.0/oauth2/accessToken",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({appKey:t.token,appSecret:s})}),r=await i.json().catch(()=>({}));if(!i.ok||!r.accessToken)throw new Error(`DingDing access token failed with HTTP ${i.status}`);const o=Number(r.expireIn||7200);return n.accessToken={value:r.accessToken,expiresAt:Date.now()+1e3*Math.max(60,o-120)},n.accessToken.value}(e,t,n);n.onLog?.("info",{method:s,path:i,body:Ed(r)},"dingding api request");const a=await e(`https://api.dingtalk.com${i}`,{method:s,headers:{"content-type":"application/json","x-acs-dingtalk-access-token":o},body:JSON.stringify(r)}),d=await a.json().catch(()=>{});if(n.onLog?.(a.ok?"info":"warn",{method:s,path:i,status:a.status,response:Pd(d)},"dingding api response"),!a.ok)throw new Error(`DingDing API failed with HTTP ${a.status}`);const c=Rd(d),l=c.code??c.errcode,p=c.success;if(void 0!==l&&"0"!==String(l)||!1===p)throw new Error(Dd(c.message||c.errmsg)||`DingDing API returned ${String(l||"failure")}`);const u=function(e){const t=Rd(Rd(e).result).deliverResults;if(!Array.isArray(t))return"";const n=t.find(e=>!1===Rd(e).success);if(!n)return"";const s=Rd(n),i=Dd(s.spaceId),r=Dd(s.errorMsg||s.message||s.errmsg)||"delivery failed";return i?`DingDing card delivery failed for ${i}: ${r}`:`DingDing card delivery failed: ${r}`}(d);if(u)throw new Error(u);return a}function Ed(e){const t=Rd(e),n=Rd(t.cardData),s=Rd(n.cardParamMap);return{openSpaceId:Dd(t.openSpaceId),outTrackId:Dd(t.outTrackId),userId:Dd(t.userId),userIdType:t.userIdType,callbackType:Dd(t.callbackType),callbackRouteKey:Dd(t.callbackRouteKey),hasImGroupTarget:Boolean(t.imGroupOpenSpaceModel||t.imGroupOpenDeliverModel),hasImSingleTarget:Boolean(t.imSingleOpenSpaceModel||t.imSingleOpenDeliverModel),hasImRobotTarget:Boolean(t.imRobotOpenSpaceModel||t.imRobotOpenDeliverModel),imGroupOpenDeliverModel:_d(t.imGroupOpenDeliverModel),imSingleOpenDeliverModel:_d(t.imSingleOpenDeliverModel),imRobotOpenDeliverModel:_d(t.imRobotOpenDeliverModel),bizConversationId:Dd(s.biz_conversation_id),bizConversationType:Dd(s.biz_conversation_type),bizSenderId:Dd(s.biz_sender_id),hasBizSessionWebhook:Boolean(Dd(s.biz_session_webhook))}}function _d(e){const t=Rd(e);if(Object.keys(t).length)return{hasExtension:Boolean(t.extension),hasRobotCode:Boolean(Dd(t.robotCode)),spaceType:Dd(t.spaceType),recipients:Array.isArray(t.recipients)?t.recipients.length:void 0,atUserIds:t.atUserIds&&"object"==typeof t.atUserIds?Object.keys(t.atUserIds).length:void 0}}function Pd(e){const t=Rd(e),n=Rd(t.result);return{code:t.code??t.errcode,message:Od(t.message||t.errmsg,240),success:t.success,requestId:t.requestId,result:Object.keys(n).length?n:void 0}}function Od(e,t=160){const n=String(e||"").replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,Math.max(0,t-3))}...`}function Rd(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function jd(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:"string"==typeof e&&e.trim()?Md(e):{}}function Dd(e){return"string"==typeof e?e:""}function Md(e){try{return Rd(JSON.parse(String(e||"{}")))}catch{return{}}}function qd(e){return JSON.stringify(va(e))}function $d(e){return e instanceof Error?e.message:String(e)}function Ld(e){const t=Number(function(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}(e).message_id);return Number.isInteger(t)&&t>0?t:void 0}function Bd(e){return Array.isArray(e?.actionRows)?e.actionRows:(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e])}function Ud(e,t){const n=Bd(e),s=Bd(t);return n.length===s.length&&n.every((e,t)=>{const n=s[t];return e.length===n.length&&e.every((e,t)=>{const s=n[t];return e.text===s?.text&&e.callbackData===s.callbackData})})}class Fd{entries;updateIntervalMs;send;edit;deleteMessage;messageIdFromResult;retryAfterMs;isMessageTooLong;onLog;constructor(e){this.entries=e.entries||new Map,this.updateIntervalMs=e.updateIntervalMs||1e3,this.send=e.send,this.edit=e.edit,this.deleteMessage=e.delete,this.messageIdFromResult=e.messageIdFromResult||Ld,this.retryAfterMs=e.retryAfterMs,this.isMessageTooLong=e.isMessageTooLong,this.onLog=e.onLog}remember(e,t,n,s,i,r){const o=this.entries.get(e);return o&&(clearTimeout(o.timer),o.timer=void 0,o.pendingText=void 0),this.entries.set(e,{lastText:n,lastUpdateAt:Date.now(),messageId:t,route:s,options:i,lastOptions:i,renderOptions:r}),o}rekey(e,t){if(e===t)return this.entries.has(e);const n=this.entries.get(e);if(!n||this.entries.has(t))return!1;if(clearTimeout(n.timer),n.timer=void 0,this.entries.delete(e),this.entries.set(t,n),n.pendingText){const e=n.rateLimitedUntil?Math.max(0,n.rateLimitedUntil-Date.now()):Math.max(0,this.updateIntervalMs-(Date.now()-n.lastUpdateAt));this.scheduleFlush(t,e)}return!0}scheduleFlush(e,t){const n=this.entries.get(e);n&&!n.timer&&(n.timer=setTimeout(()=>{n.timer=void 0;const t=n.pendingText;n.pendingText=void 0,t&&this.applyUpdate(e,t,n.route,n.options)},t))}markRateLimited(e,t,n,s){const i=this.entries.get(e);i&&(i.rateLimitedUntil=Date.now()+n,i.pendingText=t,i.route=s||i.route,this.scheduleFlush(e,n))}async applyUpdate(e,t,n,s){let i=this.entries.get(e);const r=i,o=n||i?.route,a=s||i?.options;try{if(i&&(i.route=o,i.options=a),i?.pending&&await i.pending,i=this.entries.get(e),r&&i!==r)return!1;if(i?.messageId){if(i.lastText===t&&Ud(i.lastOptions,a))return!0;const n=Date.now()-(i.lastUpdateAt||0);return n<this.updateIntervalMs?(i.pendingText=t,i.route=o||i.route,i.options=a||i.options,this.scheduleFlush(e,this.updateIntervalMs-n),!0):(await this.edit(i.messageId,t,o||i.route,a),i.lastText=t,i.lastOptions=a,i.lastUpdateAt=Date.now(),i.rateLimitedUntil=void 0,!0)}const n={lastText:t,lastUpdateAt:Date.now(),route:o,options:a,lastOptions:a};return n.pending=this.send(t,o,a).then(e=>{const t=this.messageIdFromResult(e);t&&(n.messageId=t)}).finally(()=>{n.pending=void 0}),this.entries.set(e,n),await n.pending,this.entries.get(e)===n&&(n.rateLimitedUntil=void 0,Boolean(n.messageId))}catch(n){const s=this.retryAfterMs?.(n);return s&&this.markRateLimited(e,t,s,o),this.onLog?.(`Telegram progress update skipped: ${$d(n)}`),!1}}update(e,t,n,s){const i=String(t||"").trim();if(!i)return;const r=this.entries.get(e);if(r&&(r.route=n||r.route,r.options=s||r.options),r?.lastText===i&&Ud(r.lastOptions,s||r.options)&&!r.pending&&!r.pendingText)return;const o=Date.now();if(r?.rateLimitedUntil){if(r.rateLimitedUntil>o)return r.pendingText=i,void this.scheduleFlush(e,r.rateLimitedUntil-o);r.rateLimitedUntil=void 0}if(r?.pending&&!r.messageId)return r.pendingText=i,void this.scheduleFlush(e,this.updateIntervalMs);if(!r||!r.messageId)return void this.applyUpdate(e,i,n,s);const a=Date.now()-(r.lastUpdateAt||0);if(a>=this.updateIntervalMs)return clearTimeout(r.timer),r.timer=void 0,r.pendingText=void 0,void this.applyUpdate(e,i,n||r.route,s||r.options);r.pendingText=i,this.scheduleFlush(e,this.updateIntervalMs-a)}async finish(e,t,n,s,i){const r=this.entries.get(e);if(!r)return!1;try{if(clearTimeout(r.timer),r.timer=void 0,r.pendingText=void 0,r.pending&&await r.pending,this.entries.delete(e),!r.messageId)return!1;const o=s||r.options;return r.lastText===t&&Ud(r.lastOptions,o)||await this.edit(r.messageId,t,n||r.route,o,i||r.renderOptions),!0}catch(t){return this.entries.delete(e),this.isMessageTooLong?.(t)?this.onLog?.("Telegram progress finish too long; sending result as multiple messages"):this.onLog?.(`Telegram progress finish skipped: ${$d(t)}`),!1}}async delete(e,t){const n=this.entries.get(e);if(!n)return!1;try{return clearTimeout(n.timer),n.timer=void 0,n.pendingText=void 0,n.pending&&await n.pending,this.entries.delete(e),!(!this.deleteMessage||!n.messageId)&&(await this.deleteMessage(n.messageId,t||n.route),!0)}catch(t){return this.entries.delete(e),this.onLog?.(`Telegram progress delete skipped: ${$d(t)}`),!1}}async flush(e){const t=this.entries.get(e);if(t?.pending&&await t.pending,t?.timer){clearTimeout(t.timer),t.timer=void 0;const n=t.pendingText;t.pendingText=void 0,n&&await this.applyUpdate(e,n,t.route,t.options)}}async wait(e){await function(e){return new Promise(t=>setTimeout(t,e))}(e)}}const Hd=20971520;function Wd(e){const t=[],n=[...e.photo||[]].filter(e=>e.file_id).sort((e,t)=>Number(t.file_size||t.width||0)-Number(e.file_size||e.width||0))[0];return n?.file_id&&t.push({fileId:n.file_id,fileName:"telegram-photo.jpg",fileSize:n.file_size}),e.document?.file_id&&String(e.document.mime_type||"").startsWith("image/")&&t.push({fileId:e.document.file_id,fileName:e.document.file_name,fileSize:e.document.file_size}),t}async function Gd(e,n,s="telegram-image",i){if(i&&i>Hd)throw new Error(`image is too large: ${i} bytes`);const r=await e.getFileLink(n),o=String(r),a=await(e.fetchFile||fetch)(o);if(!a.ok)throw new Error(`download failed with HTTP ${a.status}`);const d=Number(a.headers.get("content-length")||0);if(d>Hd)throw new Error(`image is too large: ${d} bytes`);const c=t.extname(s)||".jpg";let l=c;try{l=t.extname(new URL(o).pathname)||c}catch{l=c}const p=t.join(f.tmpdir(),"task-handoff-images");await h.mkdir(p,{recursive:!0});const u=l.replace(/[^.\w-]/g,"")||".jpg",g=t.join(p,`telegram-${Date.now()}-${Math.random().toString(16).slice(2)}${u}`),m=Buffer.from(await a.arrayBuffer());if(m.length>Hd)throw new Error(`image is too large: ${m.length} bytes`);return await h.writeFile(g,m),g}class Vd{store;constructor(e){this.store=new Fd({updateIntervalMs:e.updateIntervalMs,send:(t,n,s)=>e.send(e.requireBridge(n?.bridgeId||""),n?.chatId||"",ad(t),{rawMarkdownV2:!0,replyMarkup:Zd(s)}),edit:(t,n,s,i)=>e.edit(e.requireBridge(s?.bridgeId||""),s?.chatId||"",t,ad(n),{rawMarkdownV2:!0,replyMarkup:Zd(i)}),delete:(t,n)=>e.deleteMessage(e.requireBridge(n?.bridgeId||""),n?.chatId||"",t),onLog:e.onLog})}get entries(){return this.store.entries}remember(e,t,n,s){return this.store.remember(e,t,n,s)}rekey(e,t){return this.store.rekey(e,t)}delete(e){return this.store.delete(e)}async applyUpdate(e){return this.store.applyUpdate(e.key,e.text,{bridgeId:e.bridge.id,chatId:e.chatId},{actionRows:e.actionRows})}}async function Kd(e){return(await async function(e,t,n,s){const i=[];for(const r of Wd(n))try{const n=await Gd({getFileLink:n=>ld(e,t,n),fetchFile:e},r.fileId,r.fileName,r.fileSize);i.push(n)}catch(e){s?.({fileId:r.fileId,fileName:r.fileName,error:e})}return i}(e.fetchImpl,e.bridge,e.message,e.onImageDownloadError)).map((e,s)=>{const i=n.readFileSync(e);return{id:`tg_${a.randomUUID().replace(/-/g,"").slice(0,24)}`,kind:"image",name:t.basename(e)||`telegram-image-${s+1}`,mime:Jd(e),size:i.length,source:{type:"inline",encoding:"base64",data:i.toString("base64")}}})}function Jd(e){const n=t.extname(e).toLowerCase();return".png"===n?"image/png":".webp"===n?"image/webp":".gif"===n?"image/gif":".bmp"===n?"image/bmp":"image/jpeg"}function Qd(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function Yd(e){return e&&"object"==typeof e?e:{}}function Xd(e){return"string"==typeof e?e:""}function Zd(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return ya(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function ec(e){const t=Array.isArray(e.item_list)?e.item_list:[];for(const e of t){const t=tc(e);if(1!==t.type)continue;const n=nc(tc(t.text_item).text).trim();if(n)return n}return""}function tc(e){return e&&"object"==typeof e?e:{}}function nc(e){return"string"==typeof e?e:""}function sc(e){const{fetchImpl:t,bridge:n,dingdingRuntime:s}=e;return"telegram"===n.channel?function(e,t){return{bridge:t,send:async(n,s,i={})=>{const r=await cd(e,t,n,s,{replyMarkup:i.replyMarkup,replyToMessageId:i.replyToMessageId,rawMarkdownV2:i.rawMarkdownV2,parseMode:i.parseMode});return{provider:"telegram",raw:r,messageId:Number(Yd(r).message_id)||void 0}}}}(t,n):"dingding"===n.channel?function(e,t,n){return{bridge:t,send:async(s,i,r={})=>{const o=r.sessionWebhook||n?.chatWebhooks.get(s)||Dd(t.settings.sessionWebhook);if(r.replyMarkup&&n){const a=await Td({fetchImpl:e,bridge:t,runtime:n,chatId:s,text:i,replyMarkup:r.replyMarkup,sessionWebhook:o,senderId:r.senderId||n.senderIds.get(s)||Dd(t.settings.senderId)});if(a?.delivered)return{provider:"dingding",interactionId:a.outTrackId}}if(o)return await zd(e,o,i,n?.onLog),{provider:"dingding"}}}}(t,n,s):"wechat"===n.channel?function(e,t){return{bridge:t,send:async(n,s,i={})=>{const r=i.contextToken||nc(t.settings.contextToken);if(r)return await async function(e,t,n,s,i){const r=nc(t.settings.baseUrl)||"https://ilinkai.weixin.qq.com",o=await e(`${r}/ilink/bot/sendmessage`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${t.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({msg:{from_user_id:"",to_user_id:n,client_id:a.randomUUID(),message_type:2,message_state:2,context_token:s,item_list:[{type:1,text_item:{text:i}}]},base_info:{channel_version:"1.0.2"}})}),d=await o.json().catch(()=>({}));if(!o.ok||d.errcode)throw new Error(nc(d.errmsg)||`WeChat send failed with HTTP ${o.status}`)}(e,t,n,r,s),{provider:"wechat"}}}}(t,n):{bridge:n,send:async()=>{}}}class ic{entries=new Map;fetchImpl;generations=new Map;constructor(e){this.fetchImpl=e}clear(){for(const e of new Set([...this.generations.keys(),...[...this.entries.values()].map(e=>e.bridgeId)]))this.clearBridge(e)}clearBridge(e){this.generations.set(e,this.generation(e)+1),this.clearEntries(t=>t.bridgeId===e)}generation(e){return this.generations.get(e)||0}isCurrent(e,t){return this.generation(e)===t}clearEntries(e){for(const[t,n]of this.entries)e(n)&&(n.timer&&clearTimeout(n.timer),n.cancelPending?.(new Error("DingDing progress update was cancelled.")),this.entries.delete(t))}async applyUpdate(e,t){const n=this.generation(e.bridge.id),s=qd(e.replyMarkup);let i=this.entries.get(e.key);if(i?.pending&&await i.pending,!this.isCurrent(e.bridge.id,n))return!1;if(i=this.entries.get(e.key),i?.outTrackId){if(i.lastText===e.text&&i.lastActionsFingerprint===s)return!0;const r=Date.now()-(i.lastUpdateAt||0);return r<1e3?(i.pendingText=e.text,i.pendingReplyMarkup=e.replyMarkup,i.timer||(i.pending=new Promise((s,o)=>{i.cancelPending=o,i.timer=setTimeout(()=>{i.timer=void 0,i.cancelPending=void 0;const r=i.pendingText,a=i.pendingReplyMarkup;if(i.pendingText=void 0,i.pendingReplyMarkup=void 0,!r)return s(void 0);this.updateExistingEntry(e,t,i,r,a,qd(a),n).then(s,o)},1e3-r)}).finally(()=>{i.pending=void 0,i.timer=void 0,i.cancelPending=void 0})),await i.pending,this.isCurrent(e.bridge.id,n)&&this.entries.has(e.key)):(await this.updateExistingEntry(e,t,i,e.text,e.replyMarkup,s,n),this.isCurrent(e.bridge.id,n))}const r={bridgeId:e.bridge.id,lastText:e.text,lastActionsFingerprint:s,lastUpdateAt:Date.now()};return r.pending=Td({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,chatId:e.chatId,text:e.text,replyMarkup:e.replyMarkup,sessionWebhook:t.chatWebhooks.get(e.chatId)||rc(e.bridge.settings.sessionWebhook),senderId:t.senderIds.get(e.chatId)||rc(e.bridge.settings.senderId),title:"TaskHandoff 执行中",step:"progress",forceCard:!0}).then(t=>{this.isCurrent(e.bridge.id,n)&&(t?.outTrackId&&(r.outTrackId=t.outTrackId),t?.delivered&&(r.delivered=!0,r.lastUpdateAt=Date.now()))}).catch(t=>{throw this.isCurrent(e.bridge.id,n)&&this.entries.delete(e.key),t}).finally(()=>{r.pending=void 0}),this.entries.set(e.key,r),await r.pending,this.isCurrent(e.bridge.id,n)&&Boolean(r.delivered)}async updateExistingEntry(e,t,n,s,i,r,o){n.outTrackId&&(await async function(e){await Nd(e.fetchImpl,e.bridge,e.runtime,"PUT","/v1.0/card/instances",{outTrackId:e.outTrackId,cardData:{cardParamMap:Id({type:"actions",title:e.title,description:e.text,list:va(e.replyMarkup),biz_step:e.step,error_msg:""})},cardUpdateOptions:{updateCardDataByKey:!0,updatePrivateDataByKey:!1},userIdType:Number(e.bridge.settings.cardUserIdType||1)})}({fetchImpl:this.fetchImpl,bridge:e.bridge,runtime:t,outTrackId:n.outTrackId,text:s,replyMarkup:i,title:"TaskHandoff 执行中",step:"progress"}),this.isCurrent(e.bridge.id,o)&&(n.lastText=s,n.lastActionsFingerprint=r,n.lastUpdateAt=Date.now()))}}function rc(e){return"string"==typeof e?e:""}function oc(e){const t=new y.DWClient({clientId:e.clientId,clientSecret:e.clientSecret,ua:"task-handoff-control-plane",keepAlive:!1,autoReconnect:!1});let n,s=!1;return{async connect(){s=!1,await t.getEndpoint(),await t._connect();const e=t.socket;e?.once("close",()=>{s||n?.(new Error("DingDing stream connection closed."))})},disconnect(){s=!0,t.disconnect()},onDisconnect(e){n=e},getConfig:()=>t.getConfig(),registerCallbackListener(e,n){t.registerCallbackListener(e,n)},socketCallBackResponse(e,n){t.socketCallBackResponse(e,n)}}}class ac{runtimes=new Map;connected=new Set;reconnectTimers=new Map;progress;options;createClient;constructor(e){this.options=e,this.createClient=e.createClient||oc,this.progress=new ic(e.fetchImpl)}has(e){return this.runtimes.has(e)}isRunning(e){return this.connected.has(e)}get(e){return this.runtimes.get(e)}ids(){return this.runtimes.keys()}start(e){const t=(n=e.settings?.clientSecret,"string"==typeof n?n:"");var n;if(!e.token||!t)return this.options.onError(e.id,"DingDing client id/secret is not configured."),!1;const s={client:this.createClient({clientId:e.token,clientSecret:t}),chatWebhooks:new Map,senderIds:new Map,conversationTypes:new Map,onLog:(t,n,s)=>{this.options.logger["warn"===t?"warn":"info"]({bridgeId:e.id,...n},s)}};return s.client.onDisconnect?.(t=>{this.runtimes.get(e.id)===s&&this.handleConnectionFailure(e,s,t)}),s.client.registerCallbackListener(y.TOPIC_ROBOT,t=>{this.options.onRobotMessage(e,s,t).catch(t=>{this.options.onError(e.id,t)})}),s.client.registerCallbackListener(y.TOPIC_CARD,t=>{const n=t;this.options.onCardCallback(e,s,n).then(e=>s.client.socketCallBackResponse(n.headers.messageId,e||{})).catch(t=>{this.options.onError(e.id,t),s.client.socketCallBackResponse(n.headers.messageId,{})})}),this.options.clearError(e.id),this.runtimes.set(e.id,s),this.connect(e,s),!0}connect(e,t){this.reconnectTimers.delete(e.id),this.connected.add(e.id),t.client.connect().then(()=>{this.runtimes.get(e.id)===t&&(this.connected.add(e.id),this.options.clearError(e.id))}).catch(n=>{this.runtimes.get(e.id)===t&&this.handleConnectionFailure(e,t,n)})}handleConnectionFailure(e,t,n){if(this.connected.delete(e.id),this.progress.clearBridge(e.id),this.options.onError(e.id,n||"DingDing stream connection failed."),this.reconnectTimers.has(e.id))return;const s=setTimeout(()=>{this.runtimes.get(e.id)===t&&this.connect(e,t)},this.options.reconnectDelayMs??1e3);s.unref?.(),this.reconnectTimers.set(e.id,s)}stop(e){const t=this.reconnectTimers.get(e);t&&clearTimeout(t),this.reconnectTimers.delete(e),this.connected.delete(e);const n=this.runtimes.get(e);if(n){try{n.client.disconnect()}catch{}this.runtimes.delete(e)}this.progress.clearBridge(e)}stopAll(){for(const e of[...this.runtimes.keys()])this.stop(e);this.progress.clear()}applyUpdate(e){const t=this.runtimes.get(e.bridge.id);return t?this.progress.applyUpdate(e,t):Promise.resolve(!1)}applyProgressUpdate(e){return this.applyUpdate(e)}}class dc{ttlMs;load;now;cached;pending;generation=0;constructor(e,t,n={}){this.ttlMs=e,this.load=t,this.now=n.now||Date.now}get(){if(this.cached&&this.cached.expiresAt>this.now())return Promise.resolve(this.cached.value);if(this.pending)return this.pending;const e=this.generation,t=this.load().then(t=>(this.generation===e&&(this.cached={expiresAt:this.now()+this.ttlMs,value:t}),t)).finally(()=>{this.pending===t&&(this.pending=void 0)});return this.pending=t,t}invalidate(){this.generation+=1,this.cached=void 0,this.pending=void 0}}class cc{aggregates=new Map;endTokens=new Map;options;delayMs;constructor(e){this.options=e,this.delayMs=e.delayMs??1e3}async handleIncoming(e,t,n,s,i=[],r={},o={}){const a=s.trim(),d=a.split(/\s+/,1)[0]?.toLowerCase()||"",c=lc(e.id,t,n),l=this.aggregates.get(c);return o.autoBegin&&!l?(this.start(c,e,t,n,r),this.enqueue(c,e,t,n,s,i,r,!0),void await this.sendCollectionStarted(e,t,c,r.sourceMessageId,"Image received. Continue sending messages, then send /end when done.")):"/begin"===d?(this.start(c,e,t,n,r),void await this.sendCollectionStarted(e,t,c,r.sourceMessageId,"Started collecting messages. Send /end when done.")):void("/end"!==d?l?.explicit?this.enqueue(c,e,t,n,s,i,r,!0):a.startsWith("/")?await this.options.dispatch(e,t,n,s,i,r):this.enqueue(c,e,t,n,s,i,r,!1):await this.flush(c,"manual"))}async handleEndCallback(e,t,n,s,i){const r=this.endTokens.get(s)||"";if(r!==lc(e.id,t,i))return void await this.options.answerCallback(e,n,"This collection is not active here");const o=await this.flush(r,"callback");await this.options.answerCallback(e,n,o?"Collected message sent":"No messages collected")}stop(){for(const e of this.aggregates.values())e.timer&&clearTimeout(e.timer);this.aggregates.clear(),this.endTokens.clear()}stopBridge(e){for(const[t,n]of this.aggregates)n.bridgeId===e&&(n.timer&&clearTimeout(n.timer),this.aggregates.delete(t));for(const[t,n]of this.endTokens)n.startsWith(`${e}:`)&&this.endTokens.delete(t)}start(e,t,n,s,i){const r=this.aggregates.get(e);r?.timer&&clearTimeout(r.timer),this.aggregates.set(e,{bridgeId:t.id,chatId:n,userId:s,texts:[],attachments:[],context:i,explicit:!0})}enqueue(e,t,n,s,i,r,o,a){const d=this.aggregates.get(e);d?.timer&&clearTimeout(d.timer);const c=d||{bridgeId:t.id,chatId:n,userId:s,texts:[],attachments:[],context:o,explicit:a};var l,p;c.bridgeId=t.id,c.chatId=n,c.userId=s,c.context=(l=c.context,p=o,{sourceMessageId:l?.sourceMessageId??p.sourceMessageId,replyToMessageId:l?.replyToMessageId??p.replyToMessageId,quoteText:l?.quoteText||p.quoteText}),c.explicit=c.explicit||a,i.trim()&&c.texts.push(i),c.attachments.push(...r),c.timer=c.explicit?void 0:setTimeout(()=>{this.flush(e,"timer").catch(e=>this.options.onError?.(c.bridgeId,e))},this.delayMs),this.aggregates.set(e,c)}async flush(e,t){const n=this.aggregates.get(e);if(!n)return!1;n.timer&&clearTimeout(n.timer),this.aggregates.delete(e);const s=this.options.requireBridge(n.bridgeId),i=n.texts.join("\n\n").trim();return i||n.attachments.length?(await this.options.dispatch(s,n.chatId,n.userId,i,n.attachments,n.context||{}),!0):("timer"!==t&&await this.options.send(s,n.chatId,"No messages collected.",{replyToMessageId:n.context?.sourceMessageId}),!1)}sendCollectionStarted(e,t,n,s,i){return this.options.send(e,t,i,{replyToMessageId:s,replyMarkup:ya([[{text:"/end",callbackData:this.endCallbackData(n)}]])})}endCallbackData(e){const t=a.createHash("sha256").update(e).digest("base64url").slice(0,16);return this.endTokens.set(t,e),`task_handoff:cp_msg_end:${t}`}}function lc(e,t,n){return[e,t,n||""].join(":")}class pc{cancelTokens=new Map;queueActionTokens=new Map;options;constructor(e){this.options=e}cancelCallbackData(e,t){this.pruneExpired();const n=gc([e,t]);return this.cancelTokens.set(n,this.expiring({instanceId:e,sessionId:t})),`task_handoff:cp_ai_cancel:${n}`}queueSteerCallbackData(e,t,n){return`task_handoff:cp_ai_steer:${this.queueActionToken({type:"steer",instanceId:e,sessionId:t,queueId:n})}`}queueDeleteMenuCallbackData(e,t){return`task_handoff:cp_ai_qdel_menu:${this.queueActionToken({type:"delete-menu",instanceId:e,sessionId:t})}`}async tryHandle(e,t){this.pruneExpired();const n=uc(e,"task_handoff:cp_ai_cancel:");if(void 0!==n){const e=this.cancelTokens.get(n)?.value;return e?(await this.cancel(t,e.instanceId,e.sessionId),!0):this.expired(t)}const s=uc(e,"task_handoff:cp_ai_steer:");if(void 0!==s){const e=this.queueActionTokens.get(s)?.value;return e&&"steer"===e.type?(await this.steer(t,e),!0):this.expired(t)}const i=uc(e,"task_handoff:cp_ai_qdel_menu:");if(void 0!==i){const e=this.queueActionTokens.get(i)?.value;return e&&"delete-menu"===e.type?(await this.deleteMenu(t,e),!0):this.expired(t)}const r=uc(e,"task_handoff:cp_ai_qdel:");if(void 0!==r){const e=this.queueActionTokens.get(r)?.value;return e&&"delete-item"===e.type?(await this.deleteItem(t,e),!0):this.expired(t)}return!1}async cancel(e,t,n){await this.requireAllowed(e,t,n)&&await this.run(e,{instanceId:t,sessionId:n,successText:"Interrupt sent",failurePrefix:"Interrupt failed",logMessage:"telegram ai session cancel sent",operation:()=>this.options.interrupt(t,n)})}async steer(e,t){await this.requireAllowed(e,t.instanceId,t.sessionId)&&await this.run(e,{...t,successText:"Steered queued message",failurePrefix:"Steer failed",logMessage:"telegram ai session queued message steered",operation:()=>this.options.steer(t.instanceId,t.sessionId,t.queueId)})}async deleteMenu(e,t){if(await this.requireAllowed(e,t.instanceId,t.sessionId))try{const n=hc(await this.options.queue(t.instanceId,t.sessionId)),s=(Array.isArray(n.items)?n.items:[]).map(hc).filter(e=>"queued"===fc(e.status)&&fc(e.id));if(!s.length)return void await this.options.answer(e.bridge,e.callbackQueryId,"Queue is empty");const i=s.slice(0,5).map((e,n)=>[{text:mc(fc(e.message),n),callbackData:this.queueDeleteItemCallbackData(t.instanceId,t.sessionId,fc(e.id))}]);await this.options.send(e.bridge,e.chatId,"Delete queued message",{replyMarkup:ya(i)}),await this.options.answer(e.bridge,e.callbackQueryId,"Select a queued message"),this.options.info(this.logData(e,t,{itemCount:s.length}),"telegram ai session queue delete menu sent")}catch(t){await this.fail(e,t,"Queue menu failed")}}async deleteItem(e,t){if(await this.requireAllowed(e,t.instanceId,t.sessionId))try{await this.options.remove(t.instanceId,t.sessionId,t.queueId),await this.options.answer(e.bridge,e.callbackQueryId,"Queued message deleted"),Number.isInteger(e.messageId)&&await this.options.deleteMessage(e.bridge,e.chatId,e.messageId).catch(n=>{this.options.warn(this.logData(e,t,{messageId:e.messageId,error:yc(n)}),"telegram ai session queue delete menu message delete failed")}),this.options.info(this.logData(e,t),"telegram ai session queued message deleted")}catch(t){await this.fail(e,t,"Delete failed")}}async run(e,t){try{await t.operation(),await this.options.answer(e.bridge,e.callbackQueryId,t.successText),this.options.info(this.logData(e,t),t.logMessage)}catch(n){await this.fail(e,n,t.failurePrefix)}}async requireAllowed(e,t,n){return!!this.options.actionAllowed(e.bridge,e.chatId,t,n,e.messageId)||(await this.options.answer(e.bridge,e.callbackQueryId,"This chat is not bound to that AI session"),!1)}async fail(e,t,n){const s=yc(t);this.options.setBridgeError(e.bridge.id,s),await this.options.answer(e.bridge,e.callbackQueryId,`${n}: ${function(e){const t=e.replace(/\s+/g," ").trim();return t.length<=120?t:`${t.slice(0,Math.max(0,119))}…`}(s)}`)}async expired(e){return await this.options.answer(e.bridge,e.callbackQueryId,"This AI session action expired"),!0}queueDeleteItemCallbackData(e,t,n){return`task_handoff:cp_ai_qdel:${this.queueActionToken({type:"delete-item",instanceId:e,sessionId:t,queueId:n})}`}queueActionToken(e){this.pruneExpired();const t=gc([e.type,e.instanceId,e.sessionId,"queueId"in e?e.queueId:""]);return this.queueActionTokens.set(t,this.expiring(e)),t}clear(){this.cancelTokens.clear(),this.queueActionTokens.clear()}expiring(e){return{value:e,expiresAt:(this.options.now||Date.now)()+(this.options.tokenTtlMs??864e5)}}pruneExpired(){const e=(this.options.now||Date.now)();for(const[t,n]of this.cancelTokens)n.expiresAt<=e&&this.cancelTokens.delete(t);for(const[t,n]of this.queueActionTokens)n.expiresAt<=e&&this.queueActionTokens.delete(t)}logData(e,t,n={}){return{bridgeId:e.bridge.id,chatId:e.chatId,instanceId:t.instanceId,sessionId:t.sessionId,...t.queueId?{queueId:t.queueId}:{},userId:e.userId,...n}}}function uc(e,t){if(!e.startsWith(t))return;const n=e.slice(t.length);return n&&!n.includes(":")?n:void 0}function gc(e){return a.createHash("sha256").update(e.join("\0")).digest("base64url").slice(0,16)}function mc(e,t){const n=`${t+1}. `,s=fc(e).replace(/\s+/g," ").trim()||"Queued message",i=Math.max(8,48-n.length),r=Array.from(s);return`${n}${r.length>i?`${r.slice(0,Math.max(1,i-3)).join("")}...`:s}`}function hc(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function fc(e){return"string"==typeof e?e.trim():""}function yc(e){return e instanceof Error?e.message:String(e)}class Ic{announced=new Set;options;constructor(e){this.options=e}async poll(){const e=await this.options.listRoutes().catch(()=>[]);for(const t of e){if(this.announced.has(t.id))continue;const e=bc(t,this.options.callbackData),n=t.aiSessionId?this.options.listBindings().filter(e=>e.activeInstanceId===t.instanceId&&e.activeAiSessionId===t.aiSessionId):[];(await this.sendToBindings(n,e)||await this.sendToDefaults(e))&&this.announced.add(t.id)}}async sendToBindings(e,t){let n=!1;for(const s of e){if(!s.bridgeId)continue;const e=this.options.requireBridge(s.bridgeId);if(!e.enabled)continue;const i=await this.send(e,s.chatSessionId,t);n||=i}return n}async sendToDefaults(e){let t=!1;for(const n of this.options.listBridges()){if(!n.enabled||!n.defaultChatId)continue;let s=!1;await this.options.send(n,n.defaultChatId,e).catch(e=>(this.options.setBridgeError(n.id,vc(e)),s=!0,!1))?t=!0:s||this.options.setBridgeError(n.id,"Chat bridge message was not delivered.")}return t}async send(e,t,n){return this.options.send(e,t,n).then(Boolean).catch(t=>(this.options.setBridgeError(e.id,vc(t)),!1))}}function bc(e,t){const n=`${e.project?.name||e.projectId} / ${e.instance?.name||e.instanceId}`,s="approval"===e.kind?`/approve ${e.id}\n/deny ${e.id}\n/skip ${e.id}`:`/reply ${e.id} <message>`,i=`[${n}]\n${e.result}\n\n${s}`;return"approval"!==e.kind?{text:i}:{text:i,replyMarkup:ya([[{text:"Allow",callbackData:t(e.id,"allow")},{text:"Skip",callbackData:t(e.id,"skip")},{text:"Deny",callbackData:t(e.id,"deny")}]])}}function vc(e){return e instanceof Error?e.message:String(e)}class wc{service;fetchImpl;logger;bridgeTimers=new Map;bridgePollingGenerations=new Map;bridgePolls=new Map;dingdingBridges;bridgeErrors=new Map;telegramOffsets=new Map;seenTelegramUpdates=new Set;wechatCursors=new Map;pendingRouteNotifier;deliveredAiSessionFingerprints=new Map;telegramAiSessionCallbacks;telegramMessageAggregator;telegramProgressMessageTargets=new Map;aiSessionInstanceNamesCache;aiSessionPendingRoutesCache;telegramProgress;stopAiSessionListener;constructor(e,t=fetch,n={}){var s;this.service=e,this.fetchImpl=t,this.logger=(s=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(s||"").trim().toLowerCase())?n.logger:void 0),this.aiSessionInstanceNamesCache=new dc(3e4,async()=>{const e=await(this.service.listAiSessionInstanceNames?.()||this.service.boardAsync()).catch(()=>[]);return new Map(e.map(e=>[e.id,e.name||e.id]))}),this.aiSessionPendingRoutesCache=new dc(1e3,()=>this.service.listPendingRoutes().catch(()=>[])),this.telegramMessageAggregator=new cc({requireBridge:e=>this.service.requireChatBridge(e),send:(e,t,n,s={})=>this.sendTelegramMessage(e,t,n,s),answerCallback:(e,t,n)=>this.answerTelegramCallback(e,t,n),dispatch:(e,t,n,s,i,r)=>this.dispatchTelegramGatewayMessage(e,t,n,s,i,r),onError:(e,t)=>this.bridgeErrors.set(e,Ec(t))}),this.telegramAiSessionCallbacks=new pc({interrupt:(e,t)=>this.service.interruptAiSession(e,t),queue:(e,t)=>this.service.aiSessionQueue(e,t),steer:(e,t,n)=>this.service.steerAiSessionQueuedMessage(e,t,n),remove:(e,t,n)=>this.service.removeAiSessionQueuedMessage(e,t,n),actionAllowed:(e,t,n,s,i)=>this.telegramAiSessionActionAllowed(e,t,n,s,i),answer:(e,t,n)=>this.answerTelegramCallback(e,t,n),send:(e,t,n,s={})=>this.sendTelegramMessage(e,t,n,s),deleteMessage:(e,t,n)=>this.deleteTelegramMessage(e,t,n),setBridgeError:(e,t)=>this.bridgeErrors.set(e,t),info:(e,t)=>this.logInfo(e,t),warn:(e,t)=>this.logWarn(e,t)}),this.pendingRouteNotifier=new Ic({listRoutes:()=>this.service.listPendingRoutes(),listBindings:()=>this.service.listChatSessions(),listBridges:()=>this.service.listChatBridges(),requireBridge:e=>this.service.requireChatBridge(e),callbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),send:(e,t,n)=>this.sendViaBridge(e,t,n),setBridgeError:(e,t)=>this.bridgeErrors.set(e,t)}),this.telegramProgress=new Vd({updateIntervalMs:n.telegramProgressUpdateIntervalMs,requireBridge:e=>this.service.requireChatBridge(e),send:(e,t,n,s={})=>this.sendTelegramMessage(e,t,n,s),edit:(e,t,n,s,i={})=>this.editTelegramMessage(e,t,n,s,i),deleteMessage:(e,t,n)=>this.deleteTelegramMessage(e,t,n),onLog:e=>this.logWarn({message:e},"telegram progress store warning")}),this.dingdingBridges=new ac({fetchImpl:this.fetchImpl,createClient:n.createDingdingClient,logger:{info:(e,t)=>this.logInfo(e,t),warn:(e,t)=>this.logWarn(e,t)},onRobotMessage:(e,t,n)=>this.handleDingdingRobotMessage(e,t,n),onCardCallback:(e,t,n)=>this.handleDingdingCardCallback(e,t,n),onError:(e,t)=>this.bridgeErrors.set(e,Ec(t)),clearError:e=>this.bridgeErrors.delete(e)}),this.stopAiSessionListener=n.aiSessions?.onSnapshot(e=>{this.deliverAiSessionSnapshot(e.instanceId,e.aiSessions).catch(t=>{this.logWarn({instanceId:e.instanceId,snapshotRevision:e.revision,error:Ec(t)},"chat ai session snapshot delivery failed")})})}logInfo(e,t){this.logger?.info?.({component:"control-plane-chat-gateway",...e},t)}logWarn(e,t){this.logger?.warn?.({component:"control-plane-chat-gateway",...e},t)}updateChatBridge(e,t){this.service.updateChatBridge?.(e,t)}startEnabled(){this.logAiSessionDelivery({stage:"runtime-start-enabled",bridgeCount:this.service.listChatBridges().length,enabledBridgeCount:this.service.listChatBridges().filter(e=>e.enabled).length});for(const e of this.service.listChatBridges())e.enabled&&this.startBridge(e.id)}startBridge(e){if(this.bridgeTimers.has(e)||this.dingdingBridges.has(e))return this.logAiSessionDelivery({stage:"bridge-start-skipped-running",bridgeId:e}),this.status();const t=this.service.requireChatBridge(e);return this.logAiSessionDelivery({stage:"bridge-start",bridgeId:t.id,channel:t.channel,enabled:t.enabled,hasToken:Boolean(t.token)}),"wechat"===t.channel?this.startPollingBridge(t,(e,t)=>this.pollWechat(e,t)):"dingding"===t.channel?this.startDingdingBridge(t):"telegram"!==t.channel?(this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status()):t.token?this.startPollingBridge(t,(e,t)=>this.pollTelegram(e,t)):(this.bridgeErrors.set(e,"Telegram token is not configured."),this.status())}stopBridge(e){this.advanceBridgePollingGeneration(e);const t=this.bridgeTimers.get(e);return t&&(clearInterval(t),this.bridgeTimers.delete(e)),this.dingdingBridges.stop(e),this.telegramMessageAggregator.stopBridge(e),this.status()}stopAll(){const e=new Set([...this.service.listChatBridges().map(e=>e.id),...this.bridgePollingGenerations.keys(),...this.bridgeTimers.keys(),...this.bridgePolls.keys(),...this.dingdingBridges.ids()]);for(const t of e)this.stopBridge(t);this.dingdingBridges.stopAll(),this.telegramMessageAggregator.stop(),this.telegramAiSessionCallbacks.clear(),this.stopAiSessionListener?.(),this.stopAiSessionListener=void 0}async pollBridgeNow(e){const t=this.service.requireChatBridge(e),n=this.bridgePollingGeneration(e);return"telegram"===t.channel?(await this.runBridgePoll(t,n,(e,t)=>this.pollTelegram(e,t)),this.status()):"wechat"===t.channel?(await this.runBridgePoll(t,n,(e,t)=>this.pollWechat(e,t)),this.status()):("dingding"===t.channel||this.bridgeErrors.set(e,`${t.channel} bridge is not supported in the control plane.`),this.status())}async pollAiSessionsNow(){return await this.pollAiSessionUpdates(),this.status()}status(){const e=this.service.listChatBridges().map(e=>({id:e.id,channel:e.channel,name:e.name,running:this.bridgeTimers.has(e.id)||this.dingdingBridges.isRunning(e.id),tokenSet:Boolean(e.tokenSet),defaultChatId:e.defaultChatId,lastUpdateId:this.telegramOffsets.get(e.id),error:this.bridgeErrors.get(e.id)}));return{running:e.some(e=>e.running),bridges:e}}startPollingBridge(e,t){if(!e.token)return this.bridgeErrors.set(e.id,`${e.channel} token is not configured.`),this.status();this.bridgeErrors.delete(e.id);const n=this.advanceBridgePollingGeneration(e.id),s=()=>this.bridgePollingGeneration(e.id)===n,i=i=>{s()&&this.runBridgePoll(i,n,t).catch(t=>{s()&&this.bridgeErrors.set(e.id,t instanceof Error?t.message:String(t))})},r=setInterval(()=>{if(!s())return;const t=this.service.requireChatBridge(e.id);t.enabled?i(t):this.stopBridge(e.id)},e.pollIntervalMs);if(this.bridgeTimers.set(e.id,r),this.logAiSessionDelivery({stage:"bridge-poll-started",bridgeId:e.id,channel:e.channel,intervalMs:e.pollIntervalMs}),e.enabled){const t=this.service.requireChatBridge(e.id);i(t)}return this.status()}runBridgePoll(e,t,n){const s=this.bridgePolls.get(e.id);if(s?.generation===t)return s.promise;const i=(async()=>{const s=()=>this.bridgePollingGeneration(e.id)===t;s()&&await n(e,s)})();this.bridgePolls.set(e.id,{generation:t,promise:i});const r=()=>{this.bridgePolls.get(e.id)?.promise===i&&this.bridgePolls.delete(e.id)};return i.then(r,r),i}bridgePollingGeneration(e){return this.bridgePollingGenerations.get(e)||0}advanceBridgePollingGeneration(e){const t=this.bridgePollingGeneration(e)+1;return this.bridgePollingGenerations.set(e,t),t}startDingdingBridge(e){return this.dingdingBridges.start(e),this.status()}async handleDingdingRobotMessage(e,t,n){const s=function(e){const t=Md(e),n=Dd(t.conversationId),s=Dd(Rd(t.text).content).trim();if(!n||!s)return;const i=Dd(t.senderStaffId||t.senderId);return{chatId:n,userId:i||void 0,senderId:i||void 0,sessionWebhook:Dd(t.sessionWebhook)||void 0,conversationType:Sd(t.conversationType,{robotMessage:!0}),text:s,raw:t}}(n.data);if(t.client.socketCallBackResponse(n.headers.messageId,{}),!s||!this.dingdingAllowed(e,s.senderId))return void this.logWarn({bridgeId:e.id,hasEvent:Boolean(s),senderId:s?.senderId,allowed:s?this.dingdingAllowed(e,s.senderId):void 0},"dingding robot message ignored");const{chatId:i,text:r,senderId:o,sessionWebhook:a}=s;a&&t.chatWebhooks.set(i,a),s.conversationType&&t.conversationTypes.set(i,s.conversationType);const d=a||t.chatWebhooks.get(i)||Nc(e.settings.sessionWebhook);let c;o&&t.senderIds.set(i,o),e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:i}),this.logInfo({bridgeId:e.id,chatId:i,senderId:o,conversationType:s.conversationType,hasSessionWebhook:Boolean(a),hasCachedSessionWebhook:Boolean(t.chatWebhooks.get(i)),hasResolvedSessionWebhook:Boolean(d),textPreview:Pc(r)},"dingding chat gateway message received");try{c=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:o||void 0},message:{text:r,attachments:[]}})}catch(e){if(d)return void await zd(this.fetchImpl,d,`Failed to handle message: ${Ec(e)}`,t.onLog);throw e}const l=Sc(c);if(l){const n=kc(c);if(await this.sendDingdingRoutedAiSessionProgress(c,e,t,i,l,{replyMarkup:n,sessionWebhook:d,senderId:o,sourcePrompt:r}))return;const s=sc({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:t}),a=await s.send(i,l,{replyMarkup:n,sessionWebhook:d,senderId:o});if(a)return void this.logInfo({bridgeId:e.id,chatId:i,senderId:o,provider:a.provider,interactionId:a.interactionId,routed:!0===jc(c).routed,hasReplyMarkup:Boolean(n),replyPreview:Pc(l)},"dingding chat gateway reply sent");this.logWarn({bridgeId:e.id,chatId:i,senderId:o,hasReplyMarkup:Boolean(n),hasSessionWebhook:Boolean(d),hasRobotCode:Boolean(Nc(e.settings.robotCode)),replyPreview:Pc(l)},"dingding chat gateway reply skipped")}}async sendDingdingRoutedAiSessionProgress(e,t,n,s,i,r={}){if(!0!==e.routed)return!1;const o=qc(e),{instanceId:a,sessionId:d,turnId:c}=o;if(!a||!d||!c)return this.logWarn({bridgeId:t.id,chatId:s,instanceId:a,sessionId:d,turnId:c,turnIdSources:o.sources,routed:e.routed},"dingding ai session progress card not started"),!1;const l=Hc(a,d,c,t.id,s);return!!await this.dingdingBridges.applyProgressUpdate({bridge:t,key:l,chatId:s,text:i,replyMarkup:r.replyMarkup})&&(this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:s,instanceId:a,sessionId:d,turnId:c,key:l,hasSessionWebhook:Boolean(r.sessionWebhook),hasSenderId:Boolean(r.senderId),sourcePrompt:Pc(r.sourcePrompt,80),textPreview:Pc(i)},"dingding ai session progress card started"),!0)}async handleDingdingCardCallback(e,t,n){const s=function(e){const t=Md(e),n=jd(t.cardActionData||t.content),s=jd(n.cardPrivateData||t.cardPrivateData||t.content),i=jd(s.params||n.params||t.params),r=Dd(t.userId),o=Dd(i.biz_sender_id);return{userId:r||void 0,chatId:wd(t,i),senderId:r||void 0,deliverySenderId:o||void 0,sessionWebhook:Dd(i.biz_session_webhook)||void 0,conversationType:Sd(i.biz_conversation_type||t.conversationType||t.conversation_type),callbackData:Ad(Cd(t,n,s)),body:t,params:i}}(n.data);if(!this.dingdingAllowed(e,s.userId))return kd("not authorized",void 0,"unauthorized",s.body,s.params);const i=s.chatId||Nc(e.defaultChatId),r=s.deliverySenderId||s.senderId,o=s.sessionWebhook;let a;i&&o&&t.chatWebhooks.set(i,o),i&&s.conversationType&&t.conversationTypes.set(i,s.conversationType),i&&r&&t.senderIds.set(i,r);try{a=await this.parseChatGatewayCallbackAction(s.callbackData)}catch(e){if(_c(e,"CHAT_PENDING_ACTION_STALE"))return kd(Ec(e),void 0,"approval_stale",s.body,s.params);throw e}if(!a)return this.logWarn({bridgeId:e.id,chatId:i,userId:s.userId,senderId:r,...Oc(s)},"dingding card callback action unsupported"),kd("Unsupported action",void 0,"unsupported",s.body,s.params);this.logInfo({bridgeId:e.id,chatId:i,userId:s.userId,senderId:r,actionType:a.type,...Oc(s)},"dingding card callback action parsed");const d=await this.service.handleChatGatewayAction({source:{channel:"dingding",bridgeId:e.id,chatSessionId:i,userId:s.userId||void 0},action:a});return"pending-decision"===a.type&&Ac(d)?kd(`${a.decision} sent`,void 0,"approval_done",s.body,s.params):kd(Sc(d)||Nc(d.message)||"updated",kc(d),"updated",s.body,s.params)}async pollWechat(e,t=()=>!0){if(!e.token)return;const n=e.settings||{},s=this.wechatCursors.get(e.id)||Nc(n.updatesBuf),i=await async function(e){const t=nc((e.bridge.settings||{}).baseUrl)||"https://ilinkai.weixin.qq.com",n=await e.fetchImpl(`${t}/ilink/bot/getupdates`,{method:"POST",headers:{"content-type":"application/json",AuthorizationType:"ilink_bot_token",Authorization:`Bearer ${e.bridge.token}`,"X-WECHAT-UIN":String(Math.floor(Math.random()*2**31))},body:JSON.stringify({get_updates_buf:e.cursor,base_info:{channel_version:"1.0.2"}})}),s=await n.json().catch(()=>({}));if(!n.ok)throw new Error(`HTTP ${n.status}`);const i=Number(s.errcode??s.ret??0);if(0!==i)throw new Error(nc(s.errmsg||s.msg)||`chat bridge API returned ${i}`);const r=[];for(const e of Array.isArray(s.msgs)?s.msgs:[]){const t=tc(e);if(1!==t.message_type)continue;const n=nc(t.from_user_id),s=ec(t);n&&s&&r.push({chatId:n,text:s,contextToken:nc(t.context_token)})}return{cursor:nc(s.get_updates_buf),messages:r}}({fetchImpl:this.fetchImpl,bridge:e,cursor:s});if(t()){i.cursor&&(this.wechatCursors.set(e.id,i.cursor),this.updateChatBridge(e.id,{settings:{updatesBuf:i.cursor}}));for(const s of i.messages){if(!t())return;const{chatId:i,contextToken:r,text:o}=s;if(e.defaultChatId||this.updateChatBridge(e.id,{defaultChatId:i,settings:{contextToken:r}}),e.defaultChatId&&e.defaultChatId!==i)continue;const a=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"wechat",bridgeId:e.id,chatSessionId:i},message:{text:o,attachments:[]}});if(!t())return;const d=Sc(a);if(d){const t=sc({fetchImpl:this.fetchImpl,bridge:e});await t.send(i,d,{contextToken:r||Nc(n.contextToken)})}}}}pollPendingRoutes(){return this.pendingRouteNotifier.poll()}async sendToChatBinding(e,t){if(!e.bridgeId)return!1;const n=this.service.requireChatBridge(e.bridgeId);if(!n.enabled)return!1;const s=await this.sendViaBridge(n,e.chatSessionId,t).catch(e=>{this.bridgeErrors.set(n.id,e instanceof Error?e.message:String(e))});return Boolean(s)}async sendViaBridge(e,t,n){const s="string"==typeof n?n:n.text,i="string"==typeof n?void 0:n.replyMarkup;return sc({fetchImpl:this.fetchImpl,bridge:e,dingdingRuntime:this.dingdingBridges.get(e.id)}).send(t,s,{replyMarkup:i})}async pollTelegram(e,t=()=>!0){if(!e.token)return;const n=Number(e.settings?.telegramLastUpdateId),s=this.telegramOffsets.get(e.id)||(Number.isInteger(n)?n:void 0),i=await async function(e){const t=new URLSearchParams({timeout:"0",...e.offset?{offset:String(e.offset+1)}:{}}),n=await e.fetchImpl(`https://api.telegram.org/bot${e.bridge.token}/getUpdates?${t.toString()}`),s=await n.json().catch(()=>({}));if(!n.ok||!1===s.ok){if(409===n.status||/terminated by other getUpdates request/i.test(String(s.description||"")))return{updates:[],conflict:s.description||"Telegram getUpdates conflict: another bot instance is polling this token."};throw new Error(s.description||`Telegram getUpdates failed with HTTP ${n.status}`)}const i=[];for(const t of s.result||[]){const n=Number(t.update_id),s=Number.isInteger(n)?n:void 0,r=Yd(t.callback_query);if(Object.keys(r).length>0){i.push({updateId:s,callbackQuery:r});continue}const o=Yd(t.message||t.edited_message);if(0===Object.keys(o).length)continue;const a=Yd(o.chat),d=Yd(o.from),c=void 0!==a.id?String(a.id):e.bridge.defaultChatId,l=void 0!==d.id?String(d.id):void 0;if(!c)continue;const p=Xd(o.text??o.caption),u=Wd(o);(p||0!==u.length)&&i.push({updateId:s,chatId:c,userId:l,text:p,rawText:p,message:o,messageId:Qd(o),replyToMessageId:Qd(Yd(o.reply_to_message)),quoteText:Xd(Yd(o.quote).text),autoBegin:1===u.length&&!p.trim(),hasSingleImageWithoutText:1===u.length&&!p.trim()})}return{updates:i}}({fetchImpl:this.fetchImpl,bridge:e,offset:s});if(t())if(i.conflict)this.bridgeErrors.set(e.id,i.conflict);else for(const n of i.updates){if(!t())return;if(void 0!==n.updateId){const t=`${e.id}:${n.updateId}`;if(this.seenTelegramUpdates.has(t)||n.updateId<=(this.telegramOffsets.get(e.id)||0))continue;this.seenTelegramUpdates.add(t),this.telegramOffsets.set(e.id,n.updateId),this.updateChatBridge(e.id,{settings:{...e.settings,telegramLastUpdateId:n.updateId}})}if("callbackQuery"in n){const t=n.callbackQuery;await this.handleTelegramCallback(e,t).catch(async n=>{const s=jc(t.message),i=jc(s.chat),r=void 0!==i.id?String(i.id):e.defaultChatId;this.bridgeErrors.set(e.id,Ec(n)),r&&await this.sendTelegramMessage(e,r,`Action failed: ${Ec(n)}`).catch(t=>{this.bridgeErrors.set(e.id,Ec(t))})});continue}if(!this.telegramAllowed(e,n.userId))continue;const s=await Kd({fetchImpl:this.fetchImpl,bridge:e,message:n.message,onImageDownloadError:({fileId:t,fileName:n,error:s})=>{this.logWarn({bridgeId:e.id,fileId:t,fileName:n,error:Ec(s)},"telegram image download skipped")}});if(!t())return;const i=n.rawText.trim();(i||s.length)&&(this.logInfo({bridgeId:e.id,chatId:n.chatId,messageId:n.messageId,replyToMessageId:n.replyToMessageId,hasQuote:Boolean(n.quoteText),textPreview:Pc(i)},"telegram chat gateway message received"),await this.telegramMessageAggregator.handleIncoming(e,n.chatId,n.userId,i,s,{sourceMessageId:n.messageId,replyToMessageId:n.replyToMessageId,quoteText:n.quoteText},{autoBegin:n.autoBegin}))}}async dispatchTelegramGatewayMessage(e,t,n,s,i=[],r={}){try{const o=void 0!==r.replyToMessageId?this.telegramReplyAiSessionTarget(e.id,t,r.replyToMessageId):void 0,a=function(e,t){const n=String(t||"").trim(),s=e.trim();return n?[`引用:${n}`,s].filter(Boolean).join("\n"):s}(s,r.quoteText),d=await this.service.handleChatGatewayMessage({source:{type:"chat-gateway",channel:"telegram",bridgeId:e.id,chatSessionId:t,userId:n},message:{text:a,attachments:i},...o?{target:{instanceId:o.instanceId,aiSessionId:o.sessionId}}:{}}),c=Sc(d),l=kc(d);if(c){const n=await this.sendTelegramMessage(e,t,c,{replyMarkup:l,replyToMessageId:r.sourceMessageId});this.logInfo({bridgeId:e.id,chatId:t,sourceMessageId:r.sourceMessageId,replyToMessageId:r.replyToMessageId,replyTargetInstanceId:o?.instanceId,replyTargetSessionId:o?.sessionId,sentMessageId:Number(jc(n).message_id)||void 0,routed:!0===jc(d).routed,replyPreview:Pc(c)},"telegram chat gateway reply sent"),await this.rememberTelegramProgressMessage(d,e,t,n,c)}}catch(n){this.bridgeErrors.set(e.id,Ec(n)),await this.sendTelegramMessage(e,t,`Failed to handle message: ${Ec(n)}`,{replyToMessageId:r.sourceMessageId}).catch(t=>{this.bridgeErrors.set(e.id,Ec(t))})}}async handleTelegramCallback(e,t){const n=Nc(t.data),s=jc(t.from),i=jc(t.message),r=jc(i.chat),o=void 0!==r.id?String(r.id):e.defaultChatId,a=void 0!==s.id?String(s.id):void 0;if(!o||!this.telegramAllowed(e,a))return;if(await this.telegramAiSessionCallbacks.tryHandle(n,{bridge:e,chatId:o,callbackQueryId:Nc(t.id),userId:a,messageId:xc(i)}))return;const d=n.match(/^task_handoff:cp_msg_end:([^:]+)$/);if(d)return void await this.telegramMessageAggregator.handleEndCallback(e,o,Nc(t.id),d[1],a);let c;try{c=await this.parseChatGatewayCallbackAction(n)}catch(n){if(_c(n,"CHAT_PENDING_ACTION_STALE")){await this.answerTelegramCallback(e,Nc(t.id),Ec(n));const s=xc(i);return void(void 0!==s&&await this.deleteTelegramStandaloneApprovalMessage(e,o,s))}throw n}if(!c)return void await this.answerTelegramCallback(e,Nc(t.id),"Unsupported action");const l=await this.service.handleChatGatewayAction({source:{channel:"telegram",bridgeId:e.id,chatSessionId:o,userId:a},action:c});if("pending-decision"===c.type&&Ac(l)){await this.answerTelegramCallback(e,Nc(t.id),"");const n=xc(i);return void(void 0!==n&&await this.deleteTelegramStandaloneApprovalMessage(e,o,n))}await this.answerTelegramCallback(e,Nc(t.id),Nc(l.message)||"updated");const p=Sc(l),u=kc(l),g=i.message_id;p&&"number"==typeof g?await this.editTelegramMessage(e,o,g,p,{replyMarkup:u}):p&&await this.sendTelegramMessage(e,o,p,{replyMarkup:u})}async deleteTelegramStandaloneApprovalMessage(e,t,n){[...this.telegramProgress.entries.values()].find(s=>s.messageId===n&&s.route?.bridgeId===e.id&&s.route?.chatId===t)||await this.deleteTelegramMessage(e,t,n).catch(s=>{this.logWarn({bridgeId:e.id,chatId:t,messageId:n,error:Ec(s)},"telegram callback message delete failed")})}telegramAiSessionBindingActive(e,t,n,s){return this.service.listChatSessions().some(i=>i.bridgeId===e.id&&i.chatSessionId===t&&i.activeInstanceId===n&&i.activeAiSessionId===s)}telegramAiSessionActionAllowed(e,t,n,s,i){if(this.telegramAiSessionBindingActive(e,t,n,s))return!0;if(!Number.isInteger(i))return!1;const r=this.telegramReplyAiSessionTarget(e.id,t,i);return Boolean(r&&r.instanceId===n&&r.sessionId===s)}async parseChatGatewayCallbackAction(e){const t=function(e){const t=e.match(/^task_handoff:cp_p:v1:([ads]):([A-Za-z0-9_-]{16})$/);if(!t)return;const n=Object.entries(ca).find(([,e])=>e===t[1])?.[0];return"allow"===n||"deny"===n||"skip"===n?{routeFingerprint:t[2],decision:n}:void 0}(e);if(t){const e=(await this.service.listPendingRoutes()).filter(e=>"approval"===e.kind&&"pending"===e.status&&la(e.id)===t.routeFingerprint);if(1!==e.length){const e=new Error("This approval is no longer pending.");throw Object.assign(e,{statusCode:409,code:"CHAT_PENDING_ACTION_STALE"}),e}return{type:"pending-decision",routeId:e[0].id,decision:t.decision}}return function(e,t){const n=e.match(/^task_handoff:cp_session:(\d+)$/);if(n)return{type:"ai-session",index:Number(n[1])};const s=e.match(/^task_handoff:cp_i:([^:]+)$/)||e.match(/^task_handoff:cp_instance_apps:([^:]+)$/);if(s)return s[0].startsWith("task_handoff:cp_i:")?{type:"instance-app-menu",instanceId:gd(t(s[1],"instance-app-menu").instanceId,"instanceId")}:{type:"instance-app-menu",instanceId:fd(s[1])};const i=e.match(/^task_handoff:cp_a:([^:]+)$/)||e.match(/^task_handoff:cp_launch_app:([^:]+):([^:]+)$/);if(i){if(i[0].startsWith("task_handoff:cp_a:")){const e=t(i[1],"launch-app");return{type:"launch-app",instanceId:gd(e.instanceId,"instanceId"),appId:gd(e.appId,"appId")}}return{type:"launch-app",instanceId:fd(i[1]),appId:fd(i[2])}}const r=e.match(/^task_handoff:cp_p:([^:]+)$/);if(r){const e=t(r[1],"pending-decision");return{type:"pending-decision",routeId:gd(e.routeId,"routeId"),decision:md(e.decision)}}const o=e.match(/^task_handoff:approval:([^:]+:ai:[^:]+):(allow|deny|skip)$/);return o?{type:"pending-decision",routeId:o[1],decision:o[2]}:void 0}(e,(e,t)=>this.service.resolveChatActionToken(e,t))}dingdingAllowed(e,t){const n=String(t||"").trim();return!!n&&(0===e.allowedUserIds.length?(e.allowedUserIds=[n],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:n},"dingding user bound"),!0):e.allowedUserIds.includes(n))}telegramAllowed(e,t){const n=String(t||"").trim();return!!n&&(0===e.allowedUserIds.length?(e.allowedUserIds=[n],this.updateChatBridge(e.id,{allowedUserIds:e.allowedUserIds}),this.logInfo({bridgeId:e.id,userId:n},"telegram user bound"),!0):e.allowedUserIds.includes(n))}async answerTelegramCallback(e,t,n){return async function(e,t,n,s){t.token&&n&&await e(`https://api.telegram.org/bot${t.token}/answerCallbackQuery`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({callback_query_id:n,text:s.slice(0,200)})})}(this.fetchImpl,e,t,n)}async editTelegramMessage(e,t,n,s,i={}){return async function(e,t,n,s,i,r={}){if(t.token)return pd(n=>e(`https://api.telegram.org/bot${t.token}/editMessageText`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),"editMessageText",i,{chat_id:n,message_id:s,...r.replyMarkup?{reply_markup:r.replyMarkup}:{}},r)}(this.fetchImpl,e,t,n,s,i)}async sendTelegramMessage(e,t,n,s={}){return cd(this.fetchImpl,e,t,n,s)}async deleteTelegramMessage(e,t,n){return async function(e,t,n,s){if(!t.token||!Number.isInteger(s))return;const i=await e(`https://api.telegram.org/bot${t.token}/deleteMessage`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({chat_id:n,message_id:s})}),r=await i.json().catch(()=>({}));if(!i.ok||!1===r.ok)throw new Error(r.description||`Telegram deleteMessage failed with HTTP ${i.status}`)}(this.fetchImpl,e,t,n)}async rememberTelegramProgressMessage(e,t,n,s,i){if(!0!==e.routed)return;const r=qc(e),{instanceId:o,sessionId:a,turnId:d}=r,c=Number(jc(s).message_id);if(!(o&&a&&d&&Number.isInteger(c)))return void this.logWarn({bridgeId:t.id,chatId:n,instanceId:o,sessionId:a,turnId:d,turnIdSources:r.sources,messageId:Number.isInteger(c)?c:void 0,routed:e.routed},"telegram ai session progress message not remembered");const l=Hc(o,a,d,t.id,n),p=this.telegramProgress.remember(l,c,i,{bridgeId:t.id,chatId:n});this.telegramProgressMessageTargets.set(zc(t.id,n,c),{instanceId:o,sessionId:a,turnId:d,messageId:c}),this.deliveredAiSessionFingerprints.delete(l),this.logInfo({bridgeId:t.id,chatId:n,instanceId:o,sessionId:a,turnId:d,turnIdSources:r.sources,key:l,messageId:c,progressKeys:this.telegramProgressKeysFor(o,a,t.id,n),textPreview:Pc(i)},"telegram ai session progress message remembered"),"queue"===function(e){return e.aiSession&&"action"in e.aiSession?e.aiSession.action:void 0}(e)&&p?.messageId&&p.messageId!==c&&(this.telegramProgressMessageTargets.delete(zc(t.id,n,p.messageId)),await this.deleteTelegramMessage(t,n,p.messageId).catch(e=>{this.logWarn({bridgeId:t.id,chatId:n,instanceId:o,sessionId:a,turnId:d,replacedMessageId:p.messageId,replacementMessageId:c,error:Ec(e)},"telegram queued progress message replacement cleanup failed")}))}telegramReplyAiSessionTarget(e,t,n){const s=this.telegramProgressMessageTargets.get(zc(e,t,n));if(s)return s;for(const[s,i]of this.telegramProgress.entries)if(i.messageId===n&&i.route?.bridgeId===e&&i.route?.chatId===t){const i=Tc(s,e,t);if(i){const s={...i,messageId:n};return this.telegramProgressMessageTargets.set(zc(e,t,n),s),s}}}async pollAiSessionUpdates(){this.logAiSessionDelivery({stage:"poll-tick"});const e=await this.service.listAiSessions({refresh:!0}).catch(()=>{});if(!e)return void this.logAiSessionDelivery({stage:"poll-no-snapshot"});const t=new Map(e.instances.map(e=>[e.instanceId,e.aiSessions]));this.logAiSessionDelivery({stage:"poll-snapshot",instanceCount:t.size,instances:[...t.entries()].map(([e,t])=>({instanceId:e,sessionCount:t.sessions?.length||0,runningCount:t.runningCount,waitingCount:t.waitingCount,updatedAt:t.updatedAt}))});const n=await this.aiSessionInstanceNames();for(const[e,s]of t)await this.deliverAiSessionSnapshot(e,s,n)}async deliverAiSessionSnapshot(e,t,n){const s=Array.isArray(t.sessions)?t.sessions:[];if(this.pruneAiSessionDeliveryState(e,new Set(s.map(e=>e.id))),0===s.length)return;const i=n||await this.aiSessionInstanceNames(),r=await this.aiSessionPendingApprovalRoutes(e);await this.deliverOwnedTelegramProgress(e,s,i,r),await this.deliverActiveBindingTelegramProgress(e,s,i,r)}pruneAiSessionDeliveryState(e,t){for(const[n,s]of this.telegramProgress.entries){const i=s.route?.bridgeId&&s.route.chatId?this.telegramProgressMessageTargets.get(zc(s.route.bridgeId,s.route.chatId,s.messageId||0))||Tc(n,s.route.bridgeId,s.route.chatId):void 0;i&&i.instanceId===e&&!t.has(i.sessionId)&&(s.timer&&clearTimeout(s.timer),this.telegramProgress.entries.delete(n),this.deliveredAiSessionFingerprints.delete(n),s.route?.bridgeId&&s.route.chatId&&s.messageId&&this.telegramProgressMessageTargets.delete(zc(s.route.bridgeId,s.route.chatId,s.messageId)))}}async deliverActiveBindingTelegramProgress(e,t,n,s){for(const i of this.service.listChatSessions()){if(!i.bridgeId||i.activeInstanceId!==e||!i.activeAiSessionId)continue;const r=t.find(e=>e.id===i.activeAiSessionId);if(!r)continue;const o=Jc(n.get(e)||e,r),a=Kc(r,o);if(!a){this.logAiSessionDelivery({stage:"skip-empty-text",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:r.status,phase:r.phase,activeTurnId:r.activeTurnId,latestTurn:Rc(Yc(r))});continue}const d=Wc(r);if(!d){this.logAiSessionDelivery({stage:"skip-empty-turn-id",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,status:r.status,phase:r.phase,activeTurnId:r.activeTurnId,latestTurn:Rc(Yc(r))});continue}if(this.listTelegramOwnedProgressEntries(e).some(e=>e.route.bridgeId===i.bridgeId&&e.route.chatId===i.chatSessionId&&e.owner.sessionId===r.id&&e.owner.turnId===d))continue;const c=Hc(e,r.id,d,i.bridgeId||"",i.chatSessionId),l=Gc(r,o);if(!l||this.deliveredAiSessionFingerprints.get(c)===l){this.logAiSessionDelivery({stage:"skip-duplicate-fingerprint",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,hasFingerprint:Boolean(l),fingerprintMatched:this.deliveredAiSessionFingerprints.get(c)===l,latestTurn:Rc(Yc(r)),textPreview:Pc(a)});continue}const p=Bc({cancelCallbackData:this.telegramAiSessionCallbacks.cancelCallbackData(e,r.id),queueSteerCallbackData:t=>this.telegramAiSessionCallbacks.queueSteerCallbackData(e,r.id,t),queueDeleteMenuCallbackData:()=>this.telegramAiSessionCallbacks.queueDeleteMenuCallbackData(e,r.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:s.get(r.id)||Uc(e,r),session:r});if(this.logAiSessionDelivery({stage:"attempt",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,r.id,i.bridgeId||"",i.chatSessionId),hasProgressEntry:this.telegramProgress.entries.has(c),status:r.status,phase:r.phase,activeTurnId:r.activeTurnId,latestTurn:Rc(Yc(r)),textPreview:Pc(a)}),await this.updateChatAiSessionProgress(i,a,c,p).catch(t=>(this.bridgeErrors.set(i.bridgeId||"",t instanceof Error?t.message:String(t)),this.logWarn({bridgeId:i.bridgeId,chatId:i.chatSessionId,instanceId:e,sessionId:r.id,turnId:d,key:c,error:Ec(t)},"chat ai session progress update failed"),!1)))this.logAiSessionDelivery({stage:"delivered",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:Pc(a)}),this.deliveredAiSessionFingerprints.set(c,l);else{if("telegram"===this.service.requireChatBridge(i.bridgeId).channel){const t=this.telegramProgress.entries.get(c),n=this.service.requireChatBridge(i.bridgeId);this.logAiSessionDelivery({stage:t?"telegram-update-not-delivered":"telegram-no-progress-entry",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,relatedProgressKeys:this.telegramProgressKeysFor(e,r.id,i.bridgeId||"",i.chatSessionId),bridgeEnabled:n.enabled,bridgeChannel:n.channel,progressMessageId:t?.messageId,progressLastText:Pc(t?.lastText),progressHasPending:Boolean(t?.pending),latestTurn:Rc(Yc(r)),textPreview:Pc(a)});continue}await this.sendToChatBinding(i,{text:a}).catch(e=>(this.bridgeErrors.set(i.bridgeId||"",e instanceof Error?e.message:String(e)),!1))&&(this.logAiSessionDelivery({stage:"fallback-sent",instanceId:e,sessionId:r.id,bridgeId:i.bridgeId,chatId:i.chatSessionId,turnId:d,key:c,textPreview:Pc(a)}),this.deliveredAiSessionFingerprints.set(c,l))}}}async deliverOwnedTelegramProgress(e,t,n,s){for(const i of this.listTelegramOwnedProgressEntries(e)){const r=this.service.requireChatBridge(i.route.bridgeId);if("telegram"!==r.channel||!r.enabled)continue;const o=this.matchTelegramOwnedProgressSession(i,t);if(!o)continue;const a={bridgeId:i.route.bridgeId,chatSessionId:i.route.chatId},d=Jc(n.get(e)||e,o),c=Kc(o,d),l=Wc(o);if(!c||!l){this.logAiSessionDelivery({stage:"remembered-skip-turn-or-text",key:i.key,parsedTurnId:i.owner.turnId,currentTurnId:l,hasText:Boolean(c),instanceId:e,sessionId:o.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}if(i.owner.turnId&&l!==i.owner.turnId){this.logAiSessionDelivery({stage:"remembered-skip-different-active-turn",key:i.key,ownerTurnId:i.owner.turnId,currentTurnId:l,instanceId:e,sessionId:o.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const p=l===i.owner.turnId?i.key:Hc(e,o.id,l,i.route.bridgeId,i.route.chatId);if(p!==i.key&&this.telegramProgress.entries.has(p))continue;if(p!==i.key&&!this.rekeyTelegramOwnedProgress(i,p,{instanceId:e,sessionId:o.id,turnId:l}))continue;const u=Gc(o,d);if(!u||this.deliveredAiSessionFingerprints.get(p)===u){this.logAiSessionDelivery({stage:"remembered-skip-fingerprint",key:i.key,hasFingerprint:Boolean(u),fingerprintMatched:this.deliveredAiSessionFingerprints.get(p)===u,instanceId:e,sessionId:o.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId});continue}const g=Bc({cancelCallbackData:this.telegramAiSessionCallbacks.cancelCallbackData(e,o.id),queueSteerCallbackData:t=>this.telegramAiSessionCallbacks.queueSteerCallbackData(e,o.id,t),queueDeleteMenuCallbackData:()=>this.telegramAiSessionCallbacks.queueDeleteMenuCallbackData(e,o.id),permissionCallbackData:(e,t)=>this.service.pendingDecisionCallbackData(e,t),pendingApproval:s.get(o.id)||Uc(e,o),session:o}),m=await this.updateChatAiSessionProgress(a,c,p,g).catch(e=>(this.bridgeErrors.set(a.bridgeId||"",e instanceof Error?e.message:String(e)),!1));this.logAiSessionDelivery({stage:m?"remembered-delivered":"remembered-not-delivered",key:p,instanceId:e,sessionId:o.id,bridgeId:i.route.bridgeId,chatId:i.route.chatId,textPreview:Pc(c)}),m&&(this.telegramProgressMessageTargets.set(zc(i.route.bridgeId,i.route.chatId,i.messageId),{instanceId:e,sessionId:o.id,turnId:l,messageId:i.messageId}),this.deliveredAiSessionFingerprints.set(p,u))}}rekeyTelegramOwnedProgress(e,t,n){return!!this.telegramProgress.rekey(e.key,t)&&(this.deliveredAiSessionFingerprints.delete(e.key),this.telegramProgressMessageTargets.set(zc(e.route.bridgeId,e.route.chatId,e.messageId),{...n,messageId:e.messageId}),!0)}logAiSessionDelivery(e){this.logInfo(e,"telegram ai session delivery flow")}telegramProgressKeysFor(e,t,n,s){const i=`${e}:${t}:`,r=`:${n}:${s}`;return[...this.telegramProgress.entries.keys()].filter(e=>e.startsWith(i)&&e.endsWith(r))}listTelegramOwnedProgressEntries(e){const t=[];for(const[n,s]of this.telegramProgress.entries){if(!s.route?.bridgeId||!s.route.chatId||!Number.isInteger(s.messageId))continue;const i=this.telegramProgressMessageTargets.get(zc(s.route.bridgeId,s.route.chatId,s.messageId))||Tc(n,s.route.bridgeId,s.route.chatId);i&&i.instanceId===e&&t.push({key:n,route:{bridgeId:s.route.bridgeId,chatId:s.route.chatId},messageId:s.messageId,owner:{...i,messageId:s.messageId}})}return t}matchTelegramOwnedProgressSession(e,t){const n=t.find(t=>t.id===e.owner.sessionId);if(n)return n;if(e.owner.turnId){const n=t.find(t=>t.activeTurnId===e.owner.turnId||Yc(t)?.id===e.owner.turnId||Yc(t)?.providerTurnId===e.owner.turnId);if(n)return n}}async updateChatAiSessionProgress(e,t,n,s){if(!e.bridgeId)return this.logAiSessionDelivery({stage:"chat-update-skip-missing-bridge",chatId:e.chatSessionId,key:n}),!1;const i=this.service.requireChatBridge(e.bridgeId);if(!i.enabled)return this.logAiSessionDelivery({stage:"chat-update-skip-bridge-state",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,bridgeEnabled:i.enabled,bridgeChannel:i.channel}),!1;if("dingding"===i.channel){if(!this.dingdingBridges.get(i.id))return!1;const r=await this.dingdingBridges.applyProgressUpdate({bridge:i,key:n,chatId:e.chatSessionId,text:t,replyMarkup:Cc({actionRows:s})});return r&&this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,textPreview:Pc(t)},"dingding ai session progress updated"),r}if("telegram"!==i.channel)return!1;const r=this.telegramProgress.entries.get(n);if(!await this.telegramProgress.applyUpdate({bridge:i,key:n,chatId:e.chatSessionId,text:t,actionRows:s})){const t=this.telegramProgress.entries.get(n);return this.logAiSessionDelivery({stage:"telegram-update-returned-false",bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,progressMessageId:r?.messageId||t?.messageId,progressLastText:Pc(r?.lastText||t?.lastText),progressHasPending:Boolean(r?.pending||t?.pending)}),!1}return this.logInfo({bridgeId:e.bridgeId,chatId:e.chatSessionId,key:n,messageId:r?.messageId||this.telegramProgress.entries.get(n)?.messageId,textPreview:Pc(t)},r?"telegram ai session progress edited":"telegram ai session progress sent"),!0}async aiSessionInstanceNames(){return this.aiSessionInstanceNamesCache.get()}async aiSessionPendingApprovalRoutes(e){const t=await this.aiSessionPendingRoutesCache.get();return new Map(t.filter(t=>t.instanceId===e&&t.aiSessionId&&"approval"===t.kind).map(e=>[e.aiSessionId||"",e]))}}function Sc(e){return"object"==typeof e&&e&&"reply"in e?String(e.reply||""):""}function Ac(e){return"object"==typeof e&&null!==e&&!0===e.accepted}function kc(e){const t="object"==typeof e&&e&&"replyMarkup"in e?e.replyMarkup:void 0;if(t&&"object"==typeof t&&!Array.isArray(t))return t}function Cc(e){const t=Array.isArray(e?.actionRows)?e.actionRows:void 0;return ya(t||(Array.isArray(e?.actions)?e.actions:[]).map(e=>[e]))}function xc(e){const t=Number(e?.message_id);return Number.isInteger(t)?t:void 0}function zc(e,t,n){return[e,t,String(n)].join(":")}function Tc(e,t,n){const s=`:${t}:${n}`;if(!e.endsWith(s))return;const i=e.slice(0,-s.length).split(":"),r=i[0]||"",o=i[1]||"",a=i.slice(2).join(":")||void 0;return r&&o?{instanceId:r,sessionId:o,turnId:a}:void 0}function Nc(e){return"string"==typeof e?e:""}function Ec(e){return e instanceof Error?e.message:String(e)}function _c(e,t){return Boolean(e&&"object"==typeof e&&"code"in e&&e.code===t)}function Pc(e,t=160){const n=String(e||"").replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,Math.max(0,t-3))}...`}function Oc(e){const t=Dc(e.body.cardActionData||e.body.content),n=Dc(t.cardPrivateData||e.body.cardPrivateData||e.body.content);return{outTrackId:Nc(e.body.outTrackId),spaceId:Nc(e.body.spaceId),conversationType:e.conversationType,hasCallbackData:Boolean(e.callbackData),callbackDataPreview:Pc(e.callbackData,120),actionIds:[...Mc(n.actionIdList),...Mc(t.actionIdList),Nc(n.actionId),Nc(t.actionId),Nc(e.body.actionId)].filter(Boolean).slice(0,5),cardActionDataKeys:Object.keys(t).sort(),privateDataKeys:Object.keys(n).sort(),paramKeys:Object.keys(e.params).sort(),hasSessionWebhook:Boolean(e.sessionWebhook)}}function Rc(e){if(e)return{id:e.id,providerTurnId:e.providerTurnId,source:e.source,status:e.status,phase:e.phase,revision:e.revision,userPrompt:Pc(e.userPrompt,80),hasResponse:Boolean(e.lastMessage?.trim()||e.summary?.trim()),responsePreview:Pc(e.lastMessage||e.summary,80),updatedAt:e.updatedAt,observedAt:e.observedAt}}function jc(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function Dc(e){if(e&&"object"==typeof e&&!Array.isArray(e))return e;if("string"==typeof e&&e.trim())try{return jc(JSON.parse(e))}catch{return{}}return{}}function Mc(e){return Array.isArray(e)?e.map(e=>Nc(e)).filter(Boolean):[]}function qc(e){const t=e.aiSession&&"session"in e.aiSession?e.aiSession:void 0,n=t?.session||(e.aiSession&&"id"in e.aiSession?e.aiSession:void 0);return{instanceId:e.instanceId||e.instance?.id||"",sessionId:e.aiSessionId||n?.id||e.binding?.activeAiSessionId||"",turnId:e.turnId||e.providerTurnId||t?.turnId||t?.providerTurnId||(n?Wc(n):""),sources:{resultTurnId:e.turnId,resultProviderTurnId:e.providerTurnId,actionTurnId:t?.turnId,actionProviderTurnId:t?.providerTurnId,sessionActiveTurnId:n?.activeTurnId,latestTurnId:n?Yc(n)?.id:void 0}}}const $c=5,Lc=48;function Bc(e){const{cancelCallbackData:t,queueSteerCallbackData:n,queueDeleteMenuCallbackData:s,permissionCallbackData:i,pendingApproval:r,session:o}=e,a="approval"===r?.kind?[[{text:"Allow",callbackData:i(r.id,"allow")},{text:"Skip",callbackData:i(r.id,"skip")},{text:"Deny",callbackData:i(r.id,"deny")}]]:[];if("running"!==o.status)return a;const d=(o.queue?.items||[]).filter(e=>"queued"===e.status).slice(0,$c);return[...d.map((e,t)=>[{text:Fc(e.message,t),callbackData:n(e.id)}]),d.length?[{text:"Delete Queue",callbackData:s()},{text:"Cancel",callbackData:t}]:[{text:"Cancel",callbackData:t}]]}function Uc(e,t){if("approval"===t.phase)return{id:`${e}:ai:${t.id}`,kind:"approval"}}function Fc(e,t){const n=`${t+1}. `,s=e.replace(/\s+/g," ").trim()||"Queued message",i=Math.max(8,Lc-n.length),r=Array.from(s);return`${n}${r.length>i?`${r.slice(0,Math.max(1,i-3)).join("")}...`:s}`}function Hc(e,t,...n){return[e,t,...n].join(":")}function Wc(e){const t=Yc(e);return t?.id?t.id:Nc(e.activeTurnId)}function Gc(e,t=""){const n=Yc(e);return[t,n?.id||"",String(n?.revision??""),n?.status||e.status||"",n?.phase||e.phase||"",n?.lastMessage||"",n?.summary||"",n?.userPrompt||"",e.error||"",e.currentTool?.id||"",e.currentTool?.name||"",e.currentTool?.inputPreview||"",String(e.toolCallsSinceLastMessage??0),Vc(e)].join("\0")}function Vc(e){return(e.queue?.items||[]).map(e=>[e.id,e.status,e.message].join(":")).join("|")}function Kc(e,t){const n=function(e,t){let n="";t?"waiting"===t.status&&"approval"===t.phase&&t.summary?.trim()?n=t.summary.trim():t.lastMessage?.trim()?n=t.lastMessage.trim():t.summary?.trim()&&(n=t.summary.trim()):"waiting"===e.status&&"approval"===e.phase&&e.summary?.trim()?n=e.summary.trim():e.lastMessage?.trim()?n=e.lastMessage.trim():e.summary?.trim()?n=e.summary.trim():e.error?.trim()&&(n="failed"===e.status?`${e.agent} session failed:\n${e.error.trim()}`:e.error.trim());const s=function(e){if(e.currentTool?.name)return`Thinking... · ${e.currentTool.name}${e.currentTool.inputPreview?` · ${e.currentTool.inputPreview}`:""}`;const t=e.toolCallsSinceLastMessage??0;return t>0?`Thinking... · ${t} ${1===t?"tool":"tools"} completed`:""}(e);return[n,s&&!Qc(n,e)?s:""].filter(Boolean).join("\n\n")}(e,Yc(e));return n?`${t}\n${n}`:""}function Jc(e,t){const n=[t.status,t.phase&&"unknown"!==t.phase?t.phase:void 0].filter(Boolean).join("/");return`${e} · ${t.agent||"ai"} ${n||"unknown"}`}function Qc(e,t){if("waiting"!==t.status||"approval"!==t.phase)return!1;const n=t.currentTool?.inputPreview?.trim();return Boolean(n&&e.includes(n))}function Yc(e){return(e.turns||[]).filter(e=>e.id||e.userPrompt?.trim()||e.lastMessage?.trim()||e.summary?.trim()).at(-1)}class Xc{clients=new Set;listeners=new Set;seq=0;connect(e){e.topics=new Set(["*"]),this.clients.add(e),e.on("close",()=>{this.clients.delete(e)}),e.on("message",t=>{const n=function(e){try{const t=JSON.parse(String(e||"{}"));return t&&"object"==typeof t?t:void 0}catch{return}}(t);"subscribe"===n?.type&&(e.topics=new Set(n.topics?.length?n.topics:["*"]))})}publish(e,t,n={}){const s=this.createEvent(e,t,n),i=s.topic,r=JSON.stringify(s);for(const e of this.listeners)try{e(s)}catch{this.listeners.delete(e)}for(const t of this.clients)if(t.readyState===t.OPEN&&Zc(t.topics,i,e))try{t.send(r)}catch{this.clients.delete(t)}return s}on(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}send(e,t,n,s={}){const i=this.createEvent(t,n,s);return e.send(JSON.stringify(i)),i}createEvent(e,t,n={}){return{v:1,id:`evt_${Date.now().toString(36)}_${Math.random().toString(16).slice(2)}`,seq:++this.seq,type:e,topic:n.topic||Ks(e),createdAt:(new Date).toISOString(),payload:t,scope:n.scope}}}function Zc(e,t,n){return!e||e.has("*")||e.has(t)||e.has(n)}const el={"image/bmp":".bmp","image/gif":".gif","image/jpeg":".jpg","image/png":".png","image/webp":".webp"},tl=o.z.object({instanceId:o.z.string().trim().min(1).max(120),sessionId:o.z.string().trim().min(1).max(120),kind:o.z.enum(["image","file"]),name:o.z.string().trim().min(1).max(240),mime:o.z.string().trim().min(1).max(120),data:o.z.string().min(1).max(31457280)}).strict();class nl{items=new Map;ttlMs;constructor(e=864e5){this.ttlMs=e}upload(e){this.pruneExpired();const s=tl.parse(e),i=function(e,t){const n=/^data:([^;,]+);base64,(.+)$/i.exec(e.trim()),s=n?.[1]||t,i=n?.[2]||e;return{mime:s,buffer:Buffer.from(i,"base64")}}(s.data,s.mime),r=i.mime.toLowerCase(),o=el[r]||"";if("image"===s.kind&&!o)throw new Error(`Unsupported image type: ${r}`);const d="image"===s.kind?i.buffer.length>S:i.buffer.length>=A;if(!i.buffer.length||d)throw new Error("image"===s.kind?"Image must be between 1 byte and 20971520 bytes.":"File must be smaller than 512000 bytes.");const c=`att_${a.randomUUID().replace(/-/g,"").slice(0,24)}`,l=new Date,p=new Date(l.getTime()+this.ttlMs),u=t.join(f.tmpdir(),"task-handoff-ai-session-attachments");n.mkdirSync(u,{recursive:!0});const g=function(e){return t.basename(e).replace(/[^\w.\- ()\u4e00-\u9fff]/g,"_").slice(0,160)||"attachment"}(s.name),m=t.join(u,`${c}-${g.endsWith(o)?g:`${g}${o}`}`);n.writeFileSync(m,i.buffer);const h={id:c,instanceId:s.instanceId,sessionId:s.sessionId,kind:s.kind,name:g,mime:r,size:i.buffer.length,source:{type:"inline",encoding:"base64",data:i.buffer.toString("base64")},path:m,createdAt:l.toISOString(),expiresAt:p.toISOString()};return this.items.set(c,h),this.publicAttachment(h)}resolveRefs(e,t,n){this.pruneExpired();const s=o.z.array(H).max(6).default([]).parse(e),i=s.map(e=>"runtime-path"===e.source.type?U.parse(e):this.requireAttachment(e,t,n));if(i.reduce((e,t)=>e+("inline"===t.source.type?t.size:0),0)>k)throw new Error("Inline attachments must be 41943040 bytes or less in total.");for(const e of s)"upload-ref"===e.source.type&&this.consumeAttachment(e.id);return i}requireAttachment(e,t,n){const s=this.items.get(e.id);if(!s)throw new Error(`AI session attachment not found or expired: ${e.id}`);if(e.kind&&e.kind!==s.kind)throw new Error(`AI session attachment kind mismatch: ${e.id}`);if(s.instanceId!==t||s.sessionId!==n)throw new Error(`AI session attachment scope mismatch: ${e.id}`);return U.parse({id:s.id,kind:s.kind,name:s.name,mime:s.mime,size:s.size,source:s.source})}consumeAttachment(e){const t=this.items.get(e);if(t){this.items.delete(e);try{n.unlinkSync(t.path)}catch{}}}publicAttachment(e){return{id:e.id,kind:e.kind,name:e.name,mime:e.mime,size:e.size,expiresAt:e.expiresAt}}pruneExpired(){const e=Date.now();for(const[t,s]of this.items)if(!(Date.parse(s.expiresAt)>e)){this.items.delete(t);try{n.unlinkSync(s.path)}catch{}}}}const sl={controlPlaneAuthBoundary:"public"},il={controlPlaneAuthBoundary:"public-ui"},rl={controlPlaneAuthBoundary:"proxy-binding"},ol={controlPlaneAuthBoundary:"node-tunnel"};class al{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 s=t.setTimeoutFn||setTimeout;return this.timer=s(()=>{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 dl{transport;constructor(e){this.transport=e}attachMain(e,t){this.transport.attach(e,t),t.send(JSON.stringify({type:"control-plane.hello",protocolVersion:"2026-08-01",nodeId:e,serverTime:(new Date).toISOString(),capabilities:{reverseTunnel:"request-response",auxiliaryChannels:!0,httpResponseStreaming:!0,lifecycleCommands:!0,instanceApiProxy:!0}})),t.on("message",n=>this.handleMainMessage(e,t,n))}attachAuxiliary(e,t,n){return this.transport.attachAuxiliary(e,t,n)}handleMainMessage(e,t,n){if(!this.transport.isCurrentSocket(e,t))return;let s;try{s=JSON.parse(String(n))}catch{return void t.send(JSON.stringify({type:"control-plane.error",code:"INVALID_JSON"}))}const i=s&&"object"==typeof s?s:{},r=this.transport.handleSocketMessage(e,t,i);void 0===r||r||("node-agent.ping"!==i.type?"node-agent.identify"!==i.type?t.send(JSON.stringify({type:"control-plane.error",code:"UNSUPPORTED_MESSAGE"})):t.send(JSON.stringify({type:"control-plane.identified",nodeId:e,serverTime:(new Date).toISOString()})):t.send(JSON.stringify({type:"control-plane.pong",nodeId:e,serverTime:(new Date).toISOString()})))}}const cl={[C]:ze,[x]:Te,[z]:Ne,[T]:Ee,[Oi]:Fi,[Ri]:Hi,[ji]:Wi},ll=new Set(Object.keys(cl));class pl{options;validatedScopes=new Map;validatingScopes=new Map;eventQueues=new Map;scopeEpochs=new Map;constructor(e={}){this.options=e}handle(e,t){const n="string"==typeof t.type?t.type:"";if("node-agent.streams.hello"===n){const n="string"==typeof t.instanceId?t.instanceId:"",s=Ys.safeParse(t.payload);return n&&s.success&&this.enqueue(e,n,()=>this.options.onStreamsHello?.(n,s.data)),!0}if(!n.startsWith("node-agent.event."))return!1;const s=ul(t.event),i="string"==typeof s?.type?s.type:"";if(!i)return!0;const r="payload"in s?s.payload:{},o=ul(s.scope)||{},a="string"==typeof t.instanceId?t.instanceId:void 0,d=function(...e){return e.find(e=>"string"==typeof e)}(o.instanceId,s.instanceId,a);if("instance.metrics.snapshot"===i){const t=bs.safeParse(r);return!t.success||t.data.instanceId!==d||(this.enqueue(e,t.data.instanceId,()=>{this.options.events?.publish(i,t.data,{topic:"instances",scope:{...o,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===Ms){const t=qs.safeParse(r);return!t.success||t.data.instanceId!==d||(this.enqueue(e,t.data.instanceId,()=>{this.options.events?.publish(i,t.data,{topic:"instances",scope:{...o,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(i===ds||i===cs){const t=i===ds?ps.safeParse(r):us.safeParse(r);return!t.success||t.data.instanceId!==d||(this.enqueue(e,t.data.instanceId,()=>{this.options.events?.publish(i,t.data,{topic:"instances",scope:{...o,nodeId:e,instanceId:t.data.instanceId}})}),!0)}if(ll.has(i)){const t=function(e,t){const n=cl[e];if(!n)return;const s=n.safeParse(t);if(!s.success)return;const i=s.data;return{instanceId:"meta"in i?i.meta.instanceId:i.instanceId,payload:i}}(i,r);return!t||([o.instanceId,s.instanceId,a].filter(e=>"string"==typeof e&&Boolean(e)).some(e=>e!==t.instanceId)||this.enqueue(e,t.instanceId,()=>{const n={...o,nodeId:e,instanceId:t.instanceId},r={v:1,id:"string"==typeof s.id?s.id:`forwarded_${Date.now().toString(36)}`,seq:"number"==typeof s.seq?s.seq:0,type:i,topic:"string"==typeof s.topic?s.topic:i.startsWith("app-session.")?"app.sessions":"ai.sessions",createdAt:"string"==typeof s.createdAt?s.createdAt:(new Date).toISOString(),payload:t.payload,scope:n};this.options.onSessionEvent&&!this.options.onSessionEvent(r)||this.options.events?.publish(i,t.payload,{topic:r.topic,scope:n})}),!0)}const c=()=>{this.options.events?.publish(i,r,{topic:"string"==typeof s.topic?s.topic:void 0,scope:{...o,nodeId:e,...d?{instanceId:d}:{}}})};return d?this.enqueue(e,d,c):c(),!0}invalidate(e={}){const t=new Set;for(const[n,s]of this.validatedScopes)e.nodeId&&s.nodeId!==e.nodeId||e.instanceId&&s.instanceId!==e.instanceId||t.add(n);for(const n of new Set([...this.validatingScopes.keys(),...this.eventQueues.keys()])){const[s,i]=ml(n);e.nodeId&&s!==e.nodeId||e.instanceId&&i!==e.instanceId||t.add(n)}for(const e of t)this.scopeEpochs.delete(e),this.validatedScopes.delete(e),this.validatingScopes.delete(e),this.eventQueues.delete(e)}diagnostics(){return{validatedScopes:this.validatedScopes.size,validatingScopes:this.validatingScopes.size,queuedScopes:this.eventQueues.size,scopeEpochs:this.scopeEpochs.size}}enqueue(e,t,n){const s=gl(e,t),i=this.scopeEpoch(s),r=(this.eventQueues.get(s)||Promise.resolve()).catch(()=>{}).then(async()=>{this.scopeEpochs.get(s)===i&&await this.isScopeValid(e,t,i)&&this.scopeEpochs.get(s)===i&&await n()});this.eventQueues.set(s,r);const o=()=>{this.eventQueues.get(s)===r&&this.eventQueues.delete(s),this.scopeEpochs.get(s)!==i||this.eventQueues.has(s)||this.validatingScopes.has(s)||this.scopeEpochs.delete(s)};r.then(o,o)}async isScopeValid(e,t,n){const s=gl(e,t);if((this.validatedScopes.get(s)?.expiresAt||0)>Date.now())return!0;const i=this.validatingScopes.get(s);if(i)return i;const r=Promise.resolve(this.options.validateInstanceScope?.(e,t)).then(Boolean).catch(()=>!1).then(i=>(i&&this.scopeEpochs.get(s)===n&&this.validatedScopes.set(s,{nodeId:e,instanceId:t,expiresAt:Date.now()+(this.options.scopeTtlMs??3e4)}),i));return this.validatingScopes.set(s,r),r.finally(()=>{this.validatingScopes.get(s)===r&&this.validatingScopes.delete(s)})}scopeEpoch(e){const t=this.scopeEpochs.get(e);if(t)return t;const n={};return this.scopeEpochs.set(e,n),n}}function ul(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:void 0}function gl(e,t){return JSON.stringify([e,t])}function ml(e){const t=JSON.parse(e);return Array.isArray(t)&&2===t.length?[String(t[0]),String(t[1])]:["",""]}const hl=o.z.object({nodeId:o.z.string().trim().min(1).max(120)}).strict();function fl(){return`req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2,10)}`}const yl=new Map;function Il(e,t){if("paired-hmac"!==e.auth.mode||!e.auth.keyId||!e.auth.secret){const e=new Error("Reverse tunnel requires paired-HMAC node auth.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_TUNNEL_REQUIRES_PAIRED_HMAC"}),e}const n=function(e){const t=new Map;for(const[n,s]of Object.entries(e)){const e=Array.isArray(s)?s.join(","):"string"==typeof s?s:void 0===s?"":String(s);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")||""}}(t.headers);if(n.nodeId!==e.id)throw Object.assign(new Error("Invalid reverse tunnel node id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NODE_MISMATCH"});if(n.keyId!==e.auth.keyId)throw Object.assign(new Error("Invalid reverse tunnel key id."),{statusCode:401,code:"NODE_AGENT_TUNNEL_KEY_INVALID"});const s=Date.parse(n.timestamp);if(!Number.isFinite(s)||Math.abs(Date.now()-s)>Ir)throw Object.assign(new Error("Reverse tunnel timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_TUNNEL_TIMESTAMP_INVALID"});const i=Date.now()-Ir;for(const[e,t]of yl)t<=i&&yl.delete(e);const r=`${e.id}:${n.keyId}:${n.nonce}`;if(!n.nonce||yl.has(r))throw Object.assign(new Error("Reverse tunnel nonce is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_NONCE_INVALID"});const o=wr("");if(n.bodySha256!==o)throw Object.assign(new Error("Reverse tunnel body hash is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_BODY_HASH_INVALID"});const d=t.url||`${br}?nodeId=${encodeURIComponent(e.id)}`,c=Sr(e.auth.secret,{keyId:n.keyId,method:"GET",pathWithQuery:d,timestamp:n.timestamp,nonce:n.nonce,bodySha256:o});if(!function(e,t){const n=Buffer.from(e,"hex"),s=Buffer.from(t,"hex");return n.length===s.length&&a.timingSafeEqual(n,s)}(n.signature,c))throw Object.assign(new Error("Reverse tunnel signature is invalid."),{statusCode:401,code:"NODE_AGENT_TUNNEL_SIGNATURE_INVALID"});yl.set(r,Date.now()+Ir)}function bl(e){return e?e instanceof Headers?Object.fromEntries(e.entries()):Array.isArray(e)?Object.fromEntries(e.map(([e,t])=>[e,t])):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,String(t)])):{}}class vl{sockets=new Map;eventRouter;httpStreamHeaderTimeoutMs;requestTimeoutMs;auxiliaryAttachTimeoutMs;constructor(e,t={}){this.eventRouter=new pl({events:e,onStreamsHello:t.onStreamsHello,onSessionEvent:t.onSessionEvent,validateInstanceScope:t.validateInstanceScope}),this.httpStreamHeaderTimeoutMs=t.httpStreamHeaderTimeoutMs??3e4,this.requestTimeoutMs=t.requestTimeoutMs??3e4,this.auxiliaryAttachTimeoutMs=t.auxiliaryAttachTimeoutMs??3e4}attach(e,t){const n=this.sockets.get(e);if(n?.socket===t)return;n&&this.invalidateInstanceScope({nodeId:e});const s=new Map,i={socket:t,pending:s,streams:new Map,httpStreams:new Map,pendingChannels:new Map};this.sockets.set(e,i),t.on?.("close",()=>this.detach(e,t)),t.on?.("error",()=>this.detach(e,t)),n&&n.socket!==t&&this.finalizeConnection(n,new Error(`Reverse tunnel for node ${e} was replaced.`),"Reverse tunnel was replaced.",!0)}detach(e,t){const n=this.sockets.get(e);!n||t&&n.socket!==t||(this.sockets.delete(e),this.invalidateInstanceScope({nodeId:e}),this.finalizeConnection(n,new Error(`Reverse tunnel for node ${e} disconnected.`),"Reverse tunnel disconnected.",!0))}disconnect(e,t="Node connection disabled locally."){const n=this.sockets.get(e);return!!n&&(this.detach(e,n.socket),n.socket.close?.(1e3,t),!0)}connected(e){return this.sockets.has(e)}finalizeConnection(e,t,n,s){for(const n of[...e.pending.keys()])this.cancelPendingRequest(e,n,t,!1);for(const[t,s]of e.streams)e.streams.delete(t),s.downstream.close(1011,n);for(const s of[...e.httpStreams.keys()])this.cancelHttpStream(e,s,t,n,!1);for(const t of e.pendingChannels.values())clearTimeout(t.timer);if(e.pendingChannels.clear(),s&&e.socket.close&&3!==e.socket.readyState)try{e.socket.close(1e3,n)}catch{}}isCurrentSocket(e,t){return this.sockets.get(e)?.socket===t}handleSocketMessage(e,t,n){if(this.isCurrentSocket(e,t))return this.handleMessage(e,n)}handleMessage(e,t){if(this.eventRouter.handle(e,t))return!0;if(this.handleWebSocketMessage(e,t))return!0;if("node-agent.response"!==t.type)return!1;const n="string"==typeof t.requestId?t.requestId:"",s=this.sockets.get(e),i=s?.pending.get(n);if(!i||!s)return!0;s.pending.delete(n),clearTimeout(i.timer),i.signal&&i.onAbort&&i.signal.removeEventListener("abort",i.onAbort);const r=Number(t.status)||502,o=t.headers&&"object"==typeof t.headers?t.headers:{},a="string"==typeof t.body?t.body:"";if(t.error&&"object"==typeof t.error){const e=t.error,n=new Error("string"==typeof e.message?e.message:`Reverse tunnel request failed with HTTP ${r}`);return Object.assign(n,{statusCode:r,code:"string"==typeof e.code?e.code:"NODE_AGENT_REVERSE_REQUEST_FAILED"}),i.reject(n),!0}return i.resolve(new Response(a,{status:r,headers:o})),!0}invalidateInstanceScope(e={}){this.eventRouter.invalidate(e)}instanceScopeDiagnostics(){return this.eventRouter.diagnostics()}async request(e,t,n={}){const s=this.sockets.get(e.id);if(!s){const t=new Error(`Reverse tunnel for node ${e.id} is not connected.`);throw Object.assign(t,{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"}),t}const i=fl(),r=jt(n.body),o={type:"control-plane.request",requestId:i,route:t,init:{method:n.method||"GET",headers:bl(n.headers),body:r}};return new Promise((r,a)=>{let d;if(d={resolve:r,reject:a,timer:setTimeout(()=>{const n=new Error(`Reverse tunnel request to ${e.id}${t} timed out.`);Object.assign(n,{statusCode:504,code:"NODE_AGENT_REVERSE_REQUEST_TIMEOUT"}),this.cancelPendingRequest(s,i,n)},this.requestTimeoutMs),signal:n.signal||void 0},n.signal&&(d.onAbort=()=>{this.cancelPendingRequest(s,i,Object.assign(new Error("Reverse tunnel request was aborted."),{name:"AbortError",code:"ABORT_ERR"}))},n.signal.addEventListener("abort",d.onAbort,{once:!0})),s.pending.set(i,d),n.signal?.aborted)d.onAbort?.();else try{s.socket.send(JSON.stringify(o))}catch(e){this.cancelPendingRequest(s,i,e instanceof Error?e:new Error(String(e)),!1)}})}cancelPendingRequest(e,t,n,s=!0){const i=e.pending.get(t);if(!i)return!1;if(e.pending.delete(t),clearTimeout(i.timer),i.signal&&i.onAbort&&i.signal.removeEventListener("abort",i.onAbort),i.reject(n),s)try{e.socket.send(JSON.stringify({type:"control-plane.request.cancel",requestId:t}))}catch{}return!0}async requestStream(e,t,n={}){const s=this.sockets.get(e.id);if(!s)throw Object.assign(new Error(`Reverse tunnel for node ${e.id} is not connected.`),{statusCode:503,code:"NODE_AGENT_REVERSE_TUNNEL_OFFLINE"});const i=fl(),r=jt(n.body);return new Promise((o,a)=>{const d={resolve:o,reject:a,timer:setTimeout(()=>{this.cancelHttpStream(s,i,Object.assign(new Error(`Reverse tunnel stream to ${e.id}${t} timed out.`),{statusCode:504,code:"NODE_AGENT_REVERSE_STREAM_TIMEOUT"}),"Reverse HTTP stream header timeout.")},this.httpStreamHeaderTimeoutMs),settled:!1,finalized:!1,signal:n.signal||void 0};if(s.httpStreams.set(i,d),this.registerPendingChannel(s,{id:i,attach:t=>this.attachHttpStreamChannel(e.id,i,t),cancel:(e,t)=>{this.cancelHttpStream(s,i,e,t)}},"Reverse HTTP channel attach timed out."),n.signal&&(d.onAbort=()=>this.cancelHttpStream(s,i,Object.assign(new Error("Reverse HTTP stream consumer disconnected."),{name:"AbortError",code:"ABORT_ERR"}),"HTTP consumer disconnected."),n.signal.addEventListener("abort",d.onAbort,{once:!0}),n.signal.aborted))d.onAbort();else try{s.socket.send(JSON.stringify({type:"control-plane.http.open",streamId:i,route:t,init:{method:n.method||"GET",headers:bl(n.headers),body:r}}))}catch(e){this.cancelHttpStream(s,i,e instanceof Error?e:new Error(String(e)),"Reverse tunnel send failed.",!1)}})}cancelHttpStream(e,t,n,s,i=!0){const r=e.httpStreams.get(t);if(!r||r.finalized)return!1;if(r.finalized=!0,e.httpStreams.delete(t),this.removePendingChannel(e,t),clearTimeout(r.timer),r.signal&&r.onAbort&&r.signal.removeEventListener("abort",r.onAbort),r.settled?r.body?.destroy(n):(r.settled=!0,r.reject(n)),1===r.socket?.readyState&&r.socket.close(1e3,s),i)try{e.socket.send(JSON.stringify({type:"control-plane.http.cancel",streamId:t,reason:s}))}catch{}return!0}completeHttpStream(e,t){const n=e.httpStreams.get(t);return!(!n||n.finalized||(n.finalized=!0,e.httpStreams.delete(t),this.removePendingChannel(e,t),clearTimeout(n.timer),n.signal&&n.onAbort&&n.signal.removeEventListener("abort",n.onAbort),n.body?.end(),0))}attachAuxiliary(e,t,n){const s=this.sockets.get(e),i=s?.pendingChannels.get(t);return s&&i?(this.removePendingChannel(s,t),i.attach(n)):(n.close(1008,"Unknown reverse tunnel channel."),!1)}registerPendingChannel(e,t,n){this.removePendingChannel(e,t.id),t.timer=setTimeout(()=>{e.pendingChannels.get(t.id)===t&&(e.pendingChannels.delete(t.id),t.cancel(Object.assign(new Error(n),{code:"NODE_AGENT_REVERSE_CHANNEL_ATTACH_TIMEOUT"}),n))},this.auxiliaryAttachTimeoutMs),t.timer.unref?.(),e.pendingChannels.set(t.id,t)}removePendingChannel(e,t){const n=e.pendingChannels.get(t);return n?.timer&&clearTimeout(n.timer),e.pendingChannels.delete(t),n}attachHttpStream(e,t,n){return this.attachAuxiliary(e,t,n)}attachHttpStreamChannel(e,t,n){const s=this.sockets.get(e),i=s?.httpStreams.get(t);if(!s||!i)return n.close(1008,"Unknown reverse HTTP stream."),!1;i.socket=n;const r=e=>{this.cancelHttpStream(s,t,e,"Reverse HTTP stream failed.")};return n.on("message",(e,o)=>{if(i.finalized||s.httpStreams.get(t)!==i)return;if(o)return i.body?void(i.body.write(Buffer.isBuffer(e)?e:Buffer.from(e))||(n.pause?.(),i.body.once("drain",()=>n.resume?.()))):r(new Error("Reverse HTTP stream received data before response headers."));let a;try{a=JSON.parse(String(e))}catch{return void r(new Error("Reverse HTTP stream sent invalid JSON control data."))}if("node-agent.http.head"===a.type){if(i.settled)return;clearTimeout(i.timer);const e=Number(a.status)||502,n=a.headers&&"object"==typeof a.headers?a.headers:{},r=new g.PassThrough({highWaterMark:65536});return i.body=r,i.settled=!0,r.once("close",()=>{this.cancelHttpStream(s,t,new Error("Reverse HTTP stream consumer closed."),"HTTP consumer closed.")}),void i.resolve(new Response(g.Readable.toWeb(r),{status:e,headers:n}))}"node-agent.http.end"!==a.type?"node-agent.http.error"===a.type&&r(new Error("string"==typeof a.message?a.message:"Reverse HTTP stream failed.")):i.settled?this.completeHttpStream(s,t):r(new Error("Reverse HTTP stream ended before response headers."))}),n.on("close",()=>{r(new Error("Reverse HTTP stream disconnected."))}),n.on("error",()=>r(new Error("Reverse HTTP stream failed."))),!0}proxyWebSocket(e,t,n,s,i={}){const r=e.id,o=this.sockets.get(r);if(!o)return void t.close(1011,"Reverse tunnel is not connected.");const a=fl(),d=e=>{try{o.socket.send(JSON.stringify(e))}catch{this.removePendingChannel(o,a),o.streams.delete(a),t.close(1011,"Reverse tunnel send failed.")}},c={downstream:t,pendingFrames:[]};o.streams.set(a,c),this.registerPendingChannel(o,{id:a,attach:e=>this.attachWebSocketStreamChannel(r,a,e),cancel:(e,n)=>{this.removePendingChannel(o,a),o.streams.delete(a),t.close(1011,n)}},"Reverse websocket channel attach timed out."),d({type:"control-plane.websocket.open",streamId:a,route:n,protocols:Array.isArray(s)?s:s?[s]:[],headers:i}),t.on("message",(e,n)=>{const s=o.streams.get(a);if(s&&!s.upstream)return s.pendingFrames.length>=256?(this.removePendingChannel(o,a),o.streams.delete(a),t.close(1011,"Reverse websocket stream pending frame limit exceeded."),void d({type:"control-plane.websocket.close",streamId:a,code:1011,reason:"Reverse websocket stream pending frame limit exceeded."})):void s.pendingFrames.push({data:e,isBinary:Boolean(n)})}),t.on("close",(e,t)=>{const n=o.streams.get(a);if(n){this.removePendingChannel(o,a),o.streams.delete(a);const s=gr(e),i=ur(t);n.upstream&&mr(n.upstream,s,i),d({type:"control-plane.websocket.close",streamId:a,...void 0===s?{}:{code:s},...i?{reason:i}:{}})}}),t.on("error",()=>{const e=o.streams.get(a);e&&(this.removePendingChannel(o,a),o.streams.delete(a),e.upstream?.close(1011,"Downstream websocket failed."),d({type:"control-plane.websocket.close",streamId:a,code:1011,reason:"Downstream websocket failed."}))})}attachWebSocketStream(e,t,n){return this.attachAuxiliary(e,t,n)}attachWebSocketStreamChannel(e,t,n){const s=this.sockets.get(e),i=s?.streams.get(t);if(!s||!i)return n.close(1008,"Unknown reverse websocket stream."),!1;i.upstream=n;for(const e of i.pendingFrames.splice(0))n.send(e.data,{binary:e.isBinary});return fr(i.downstream,n,{pendingFrameLimit:256,onClientClose:()=>{this.removePendingChannel(s,t),s.streams.delete(t)},onClientError:()=>{this.removePendingChannel(s,t),s.streams.delete(t)},onUpstreamClose:()=>{this.removePendingChannel(s,t),s.streams.delete(t)},onUpstreamError:()=>{this.removePendingChannel(s,t),s.streams.delete(t)}}),!0}handleWebSocketMessage(e,t){const n="string"==typeof t.type?t.type:"";if(!n.startsWith("node-agent.websocket."))return!1;const s=this.sockets.get(e),i="string"==typeof t.streamId?t.streamId:"",r=s?.streams.get(i);return!r||("node-agent.websocket.close"===n?(s&&this.removePendingChannel(s,i),s?.streams.delete(i),mr(r.downstream,t.code,t.reason),!0):"node-agent.websocket.error"!==n||(s&&this.removePendingChannel(s,i),s?.streams.delete(i),r.downstream.close(1011,"string"==typeof t.message?t.message:"Reverse websocket proxy failed."),!0))}}class wl{sockets=new Map;socketUrls=new Map;retries=new Map;service;tunnel;safetyIntervalMs;logger;reconnectAttempts=0;safetyReconciliations=0;timer;constructor(e,t,n={}){var s;this.service=e,this.tunnel=t,this.safetyIntervalMs=(s=n.safetyIntervalMs,Math.min(6e4,Math.max(3e4,s??45e3))),this.logger=n.logger}start(){this.sync(),this.timer=setInterval(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(clearInterval(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()}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;for(const t of this.service.listNodes()){const n=this.nodeEventsUrl(t);if(!n)continue;e.add(t.id);const s=this.retries.get(t.id);s?.timer.pending&&s.url!==n&&(s.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,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=Ar(t),s=n?void 0:new URL(t),i=vr(e,{method:"GET",pathWithQuery:s?`${s.pathname}${s.search}`:"/api/node-agent/events"}),r=n?Cr(n,"/events",void 0,i):new l.WebSocket(t,{headers:i});this.sockets.set(e.id,r),this.socketUrls.set(e.id,t),r.on("open",()=>{const n=this.retries.get(e.id);n?.timer.reset(),this.retries.set(e.id,{timer:n?.timer||new al,url:t})}),r.on("message",t=>{if(this.sockets.get(e.id)!==r)return;let n;try{n=JSON.parse(String(t))}catch{return}if(n&&"object"==typeof n&&!Array.isArray(n)){const t=n,s="string"==typeof t.type&&t.type.startsWith("node-agent.")?t:{type:"node-agent.event.forwarded",event:t};this.tunnel.handleMessage(e.id,s)}}),r.on("close",()=>{this.sockets.get(e.id)===r&&(this.sockets.delete(e.id),this.socketUrls.delete(e.id),this.scheduleReconnect(e.id))}),r.on("error",()=>{r.close()})}scheduleReconnect(e){const t=this.service.listNodes().find(t=>t.id===e),n=t&&this.nodeEventsUrl(t);if(!t||!n)return;const s=this.retries.get(e)??{timer:new al};if(s.timer.pending)return;s.url=n;const i=s.timer.schedule(()=>{this.sockets.has(e)||this.connect(t,n)});i&&(this.reconnectAttempts+=1,this.logger?.info?.({nodeId:e,url:n,attempt:i.attempt,delay:i.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.node-connection.reconnect-scheduled")),this.retries.set(e,s)}nodeEventsUrl(e){if(!1===e.connectionEnabled)return;const t=e.controlEndpoint||e.endpoint;if(t&&"reverse-wss"!==e.connectionMode){if(Ar(t))return t;try{const e=new URL("/api/node-agent/events",t.replace(/\/$/,""));return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}catch{return}}}}function Sl(e=function(){return process.env.TASK_HANDOFF_CONTROL_PLANE_DATA_DIR||t.join(f.homedir(),".config","task-handoff","control-plane")}()){const n=t.resolve(e);return{dataDir:n,settingsPath:t.join(n,"control-plane-settings.json"),projectsDir:t.join(n,"projects"),modelsDir:t.join(n,"models"),imagesDir:t.join(n,"images"),marketDir:t.join(n,"market"),marketCachePath:t.join(n,"market","catalog-cache.json"),marketStatePath:t.join(n,"market","catalog-state.json"),marketMigrationBackupPath:t.join(n,"market","legacy-image-migration-backup.json"),nodesDir:t.join(n,"nodes"),pendingPairingRevokesDir:t.join(n,"pending-pairing-revokes"),nodeRuntimesDir:t.join(n,"node-runtimes"),controlledInstancesDir:t.join(n,"controlled-instances"),chatSessionsDir:t.join(n,"chat-sessions"),chatBridgesDir:t.join(n,"chat-bridges"),triggersDir:t.join(n,"triggers"),nodeJoinInvitesDir:t.join(n,"node-join-invites"),proxyAuthorityPath:t.join(n,"control-plane-proxy","authority.json"),authUsersDir:t.join(n,"auth-users"),authSessionsDir:t.join(n,"auth-sessions"),aiSessionUnreadDir:t.join(n,"ai-session-unread"),logsDir:t.join(n,"logs")}}class Al extends Error{code;lockPath;owner;constructor(e,t,n={}){const s=t?.pid?` pid ${t.pid}`:"";super(`${n.label||"Process"} is already running${s}. Lock: ${e}`),this.name="ProcessSingletonError",this.code=n.code||"PROCESS_ALREADY_RUNNING",this.lockPath=e,this.owner=t}}function kl(e){return t.join(e,"owner.json")}function Cl(e){return t.join(e,"initializing.json")}function xl(e){return t.join(e,"recovering.json")}function zl(e){return Number.isInteger(e)&&Number(e)>0}function Tl(e,t=process.platform){if(zl(e)){if("linux"===t)try{const t=n.readFileSync(`/proc/${e}/stat`,"utf8"),s=t.lastIndexOf(")");if(s<0)return;const i=t.slice(s+2).trim().split(/\s+/)[19];return i?`linux:${i}`:void 0}catch{return}if(["darwin","freebsd","openbsd","aix","sunos"].includes(t)){const n=I.spawnSync("ps",["-o","lstart=","-p",String(e)],{encoding:"utf8",timeout:1e3}),s=0===n.status?n.stdout.trim().replace(/\s+/g," "):"";return s?`${t}:${s}`:void 0}if("win32"===t){const t=`(Get-Process -Id ${e} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks`,n=I.spawnSync("powershell.exe",["-NoProfile","-NonInteractive","-Command",t],{encoding:"utf8",timeout:2e3}),s=0===n.status?n.stdout.trim():"";return/^\d+$/.test(s)?`win32:${s}`:void 0}}}const Nl=function(e){try{const t=JSON.parse(n.readFileSync(kl(e),"utf8"));if(zl(t.pid)&&"string"==typeof t.startTime){const e=t;return{pid:e.pid,hostname:"string"==typeof e.hostname&&e.hostname?e.hostname:f.hostname(),component:e.component||"server-update",command:"string"==typeof e.command?e.command:"",acquiredAt:"string"==typeof e.createdAt&&Number.isFinite(Date.parse(e.createdAt))?e.createdAt:new Date(0).toISOString(),token:"string"==typeof e.token&&e.token?e.token:`legacy:${e.pid}:${e.startTime}`,startIdentity:e.startTime.startsWith("linux:")?e.startTime:`linux:${e.startTime}`}}if(!(zl(t.pid)&&"string"==typeof t.hostname&&t.hostname&&"string"==typeof t.command&&"string"==typeof t.acquiredAt&&Number.isFinite(Date.parse(t.acquiredAt))&&"string"==typeof t.token&&t.token))return;return t}catch{return}};function El(e){if(!zl(e))return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function _l(e){if(!e||!El(e.pid))return!1;if(!e.startIdentity)return!0;const t=Tl(e.pid);return void 0===t||t===e.startIdentity}function Pl(e,s){const i=t.join(e,`.owner-${s.token}.tmp`);n.writeFileSync(i,`${JSON.stringify(s,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const r=Ol(e);if(r?.token!==s.token)throw jl(i),new Error(`Process lock initialization was superseded: ${e}`);n.renameSync(i,kl(e)),jl(Cl(e))}function Ol(e){try{return JSON.parse(n.readFileSync(Cl(e),"utf8"))}catch{return}}function Rl(e){const t=Ol(e);return zl(t?.pid)&&_l({pid:t.pid,startIdentity:t.startIdentity})}function jl(e){try{n.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function Dl(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function Ml(e){try{const t=Ol(e),s=Date.parse(t?.startedAt||"");return Number.isFinite(s)?Math.max(0,Date.now()-s):Math.max(0,Date.now()-n.statSync(e).mtimeMs)}catch{return 0}}function ql(e,s){try{return n.writeFileSync(xl(e),`${JSON.stringify({token:s,pid:process.pid,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(i){if("EEXIST"===i.code)return!!function(e){const s=xl(e),i=$l(s),r=Date.parse(i?.startedAt||"");if(El(i?.pid)||Number.isFinite(r)&&Date.now()-r<2e3)return!1;const o=t.join(e,`.recovering-${a.randomUUID()}.stale`);try{n.renameSync(s,o)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const d=$l(o);if(d?.token===i?.token)return jl(o),!0;try{n.linkSync(o,s)}catch(e){if("EEXIST"!==e.code)throw e}finally{jl(o)}return!1}(e)&&ql(e,s);if("ENOENT"===i.code)return!1;throw i}}function $l(e){try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return}}function Ll(e,t){return $l(xl(e))?.token===t}function Bl(e,t,n){if(function(e,t,n){return"system"===e.type||("chat-bridge"===e.type?function(e,t){return"ai-session"===t.type?["read","send-message","approve","interrupt"].includes(e):"instance"===t.type?["read","start","stop","restart"].includes(e):"read"===e}(t,n):"admin"===e.role||("viewer"===e.role?"read"===t:"operator"===e.role&&function(e,t){return"read"===e||("instance"===t.type?["create","update","delete","start","stop","restart","proxy"].includes(e):"ai-session"===t.type?["send-message","approve","interrupt"].includes(e):("trigger"===t.type||"runtime"===t.type)&&["create","update","delete"].includes(e))}(t,n)))}(e,t,n))return;const s=new Error(`Actor is not allowed to ${t} ${n.type}${n.id?` ${n.id}`:""}.`);throw Object.assign(s,{statusCode:403,code:"CONTROL_PLANE_FORBIDDEN"}),s}const Ul=o.z.string().trim().min(1),Fl=o.z.object({id:Ul}).strict(),Hl=o.z.object({id:Ul,keyId:Ul}).strict(),Wl=o.z.object({id:Ul,connectionId:Ul}).strict(),Gl=o.z.object({id:Ul,runtimeId:Ul}).strict(),Vl=o.z.object({id:Ul,folderId:Ul}).strict(),Kl=o.z.object({id:Ul,sessionId:Ul}).strict(),Jl=o.z.object({id:Ul,sessionId:Ul,queueId:Ul}).strict(),Ql=o.z.object({configHash:Ul}).strict(),Yl=o.z.object({id:Ul,configHash:Ul}).strict(),Xl=o.z.object({id:Ul,sessionId:Ul,configHash:Ul}).strict(),Zl=o.z.object({path:o.z.string().optional(),depth:o.z.coerce.number().int().min(0).max(32).optional()}).strict();async function ep(e,t,n){const s=new AbortController,i=()=>s.abort(),r=()=>{t.raw.writableEnded||i()};e.raw.once("aborted",i),t.raw.once("close",r);try{return await n(s.signal)}finally{e.raw.off("aborted",i),t.raw.off("close",r)}}const tp=o.z.object({ids:o.z.array(o.z.string().trim().min(1).max(120)).default([])}).strict(),np=o.z.object({nodeId:o.z.string().trim().min(1).max(120),modelId:o.z.string().trim().min(1).max(120).optional()}).strict(),sp=o.z.object({source:o.z.object({channel:Us,bridgeId:o.z.string().trim().min(1).max(120).optional(),chatSessionId:o.z.string().trim().min(1).max(240),userId:o.z.string().trim().max(240).optional()}).strict(),action:o.z.discriminatedUnion("type",[o.z.object({type:o.z.literal("ai-session"),index:o.z.number().int().nonnegative()}).strict(),o.z.object({type:o.z.literal("instance-app-menu"),instanceId:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("launch-app"),instanceId:o.z.string().trim().min(1).max(120),appId:o.z.string().trim().min(1).max(120)}).strict(),o.z.object({type:o.z.literal("pending-decision"),routeId:o.z.string().trim().min(1).max(120),decision:o.z.enum(["allow","deny","skip"])}).strict()])}).strict();function ip(e){const t=sp.parse(e);if(!t.action)throw new Error("Chat gateway action is required.");return{source:t.source,action:t.action}}const rp=o.z.object({path:o.z.string().trim().max(1e3).optional(),depth:o.z.coerce.number().int().min(0).max(2).optional()}).strict(),op=o.z.object({id:o.z.string().trim().min(1),appId:o.z.string().trim().min(1)}).strict(),ap=o.z.object({id:o.z.string().trim().min(1),jobId:o.z.string().trim().min(1)}).strict(),dp=o.z.object({force:o.z.enum(["true","false"]).optional()}).strict();const cp=o.z.object({appId:o.z.string().trim().min(1).max(120).default("terminal-tty"),cwdFolderId:o.z.string().trim().min(1).max(120).optional(),options:o.z.record(o.z.string(),o.z.unknown()).default({})}).strict().default({appId:"terminal-tty",options:{}}),lp=o.z.object({title:o.z.string().trim().min(1).max(120)}).strict(),pp=o.z.object({}).strict();function up(e){const{app:t,service:n,events:s,appSessionAggregator:i,aiSessionAggregator:r,aiSessionUnread:a,chatGateway:d,aiSessionAttachments:c,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}=e;!function({app:e,service:t,events:n}){e.get("/api/projects",async()=>({data:t.listProjects()})),e.post("/api/projects",async(e,s)=>{const i=t.createProject(e.body);return n.publish("project.created",{projectId:i.id}),s.code(201).send({data:i})}),e.get("/api/projects/:id",async e=>({data:t.requireProject(Fl.parse(e.params).id)})),e.patch("/api/projects/:id",async e=>{const s=t.updateProject(Fl.parse(e.params).id,e.body);return n.publish("project.updated",{projectId:s.id}),{data:s}}),e.delete("/api/projects/:id",async e=>{const s=Fl.parse(e.params).id,i=t.deleteProject(s);return n.publish("project.deleted",{projectId:s,deleted:i}),{data:{deleted:i}}}),e.get("/api/models",async(e,n)=>ep(e,n,async e=>({data:await t.listFederatedModels(e)}))),e.post("/api/models",async(e,s)=>{const i=await t.createModel(e.body);return n.publish("model.created",{modelId:i.id}),s.code(201).send({data:i})}),e.post("/api/models/reorder",async e=>{const s=tp.parse(e.body),i=await t.reorderModels(s.ids);return n.publish("model.reordered",{ids:s.ids}),{data:i}}),e.get("/api/models/:id",async e=>({data:t.requireModel(Fl.parse(e.params).id)})),e.patch("/api/models/:id",async e=>{const s=await t.updateModel(Fl.parse(e.params).id,e.body);return n.publish("model.updated",{modelId:s.id}),{data:s}}),e.delete("/api/models/:id",async e=>{const s=Fl.parse(e.params).id,i=await t.deleteModel(s);return n.publish("model.deleted",{modelId:s,deleted:i}),{data:{deleted:i}}}),e.post("/api/nodes/:nodeId/models",async(e,s)=>{const{nodeId:i}=np.parse(e.params),r=await t.createNodeModel(i,e.body);return n.publish("model.created",{modelId:r.id,nodeId:i}),s.code(201).send({data:r})}),e.patch("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:s,modelId:i}=np.parse(e.params),r=await t.updateNodeModel(s,i,e.body);return n.publish("model.updated",{modelId:i,nodeId:s}),{data:r}}),e.delete("/api/nodes/:nodeId/models/:modelId",async e=>{const{nodeId:s,modelId:i}=np.parse(e.params),r=await t.deleteNodeModel(s,i);return n.publish("model.deleted",{modelId:i,nodeId:s,deleted:r.deleted}),{data:r}}),e.get("/api/market/catalog",async()=>({data:t.getMarketCatalog()})),e.post("/api/market/refresh",async()=>{const e=await t.refreshMarketCatalog();return n.publish("market.catalog.updated",{revision:e.catalog.revision,source:e.catalog.source}),{data:e}}),e.get("/api/image-options",async()=>({data:t.listImageOptions()})),e.get("/api/images",async()=>({data:t.listImages()})),e.post("/api/images",async(e,s)=>{const i=t.createImage(e.body);return n.publish("image.created",{imageId:i.id}),s.code(201).send({data:i})}),e.get("/api/images/:id",async e=>({data:t.requireImage(Fl.parse(e.params).id)})),e.patch("/api/images/:id",async e=>{const s=t.updateImage(Fl.parse(e.params).id,e.body);return n.publish("image.updated",{imageId:s.id}),{data:s}}),e.delete("/api/images/:id",async e=>{const s=Fl.parse(e.params).id,i=await t.deleteImage(s);return n.publish("image.deleted",{imageId:s,deleted:i}),{data:{deleted:i}}})}({app:t,service:n,events:s}),function({app:e,service:t,events:n,nodeAgentTunnel:s,nodeEventSubscriber:i,errorPayload:r}){const o=e=>{if(!e||"object"!=typeof e||Array.isArray(e)||!("target"in e))return;const t=e.target;if(!t||"object"!=typeof t||Array.isArray(t)||"controlled-instance"!==t.component)return;const n=new Error("Independent controlled-instance updates were retired; update the Node rollout instead.");throw Object.assign(n,{statusCode:404,code:"LEGACY_INSTANCE_UPDATE_RETIRED"}),n};e.get("/api/nodes",async()=>({data:t.listPublicNodes()})),e.post("/api/nodes/local/sync",async()=>{const e=await t.syncLocalNodeConnection();return i.syncNow(),n.publish("node.local-synced",{nodeId:e.id}),{data:t.requirePublicNode(e.id)}}),e.post("/api/nodes",async(e,s)=>{const r=await t.createNode(e.body);return i.syncNow(),n.publish("node.created",{nodeId:r.id}),s.code(201).send({data:t.requirePublicNode(r.id)})}),e.post("/api/nodes/:id/check",async e=>{const s=await t.checkNode(Fl.parse(e.params).id);return n.publish("node.checked",{nodeId:s.id},{topic:"node.state",scope:{nodeId:s.id}}),{data:s}}),e.get("/api/nodes/:id/updates/jobs",async e=>({data:await t.listNodeUpdateJobs(Fl.parse(e.params).id)})),e.get("/api/nodes/:id/settings/external-listener",async e=>({data:await t.getLocalNodeExternalListener(Fl.parse(e.params).id)})),e.patch("/api/nodes/:id/settings/external-listener",async e=>{const s=Fl.parse(e.params).id,i=await t.updateLocalNodeExternalListener(s,e.body);return n.publish("node.external-listener.updated",{nodeId:s,bindScope:i.bindScope,port:i.port}),{data:i}}),e.post("/api/nodes/:id/updates/check",async e=>{o(e.body);const s=Fl.parse(e.params).id,i=await t.checkNodeUpdate(s,kn.parse(e.body));return n.publish("node.update.checked",{nodeId:s,availableVersion:i.availableVersion,impact:i.impact}),{data:i}}),e.post("/api/nodes/:id/updates/apply",async(e,s)=>{o(e.body);const i=Fl.parse(e.params).id,r=await t.applyNodeUpdate(i,zn.parse(e.body));return n.publish("node.update.queued",{nodeId:i,updateJobId:r.id,desiredVersion:r.toVersion,impact:r.impact}),s.code(202).send({data:r})}),e.post("/api/nodes/:id/pairing/invites",async(e,s)=>{const i=Fl.parse(e.params),r=await t.createNodePairingInvite(i.id,e.body);return n.publish("node.pairing-invite.created",{nodeId:i.id}),s.code(201).send({data:r})}),e.post("/api/nodes/:id/control-plane-connections",async e=>{const s=Fl.parse(e.params),i=await t.connectNodeToControlPlane(s.id,e.body);return n.publish("node.remote-connect.requested",{nodeId:s.id}),{data:i}}),e.get("/api/nodes/:id/control-plane-pairings",async e=>{const n=Fl.parse(e.params);return{data:await t.listNodeControlPlanePairings(n.id)}}),e.delete("/api/nodes/:id/control-plane-pairings/:keyId",async e=>{const s=Hl.parse(e.params),i=await t.deleteNodeControlPlanePairing(s.id,s.keyId);return n.publish("node.control-plane-pairing.deleted",{nodeId:s.id,keyId:s.keyId}),{data:i}}),e.get("/api/nodes/:id/control-plane-connections",async e=>{const n=Fl.parse(e.params);return{data:await t.listNodeControlPlaneConnections(n.id)}}),e.delete("/api/nodes/:id/control-plane-connections/:connectionId",async e=>{const s=Wl.parse(e.params),i=await t.deleteNodeControlPlaneConnection(s.id,s.connectionId);return n.publish("node.control-plane-connection.deleted",{nodeId:s.id,connectionId:s.connectionId}),{data:i}}),e.post("/api/node-join/invites",async(e,s)=>{const i=t.createNodeJoinInvite(e.body);return n.publish("node-join.invite.created",{inviteId:i.id}),s.code(201).send({data:i})}),e.post("/api/node-join/complete",{config:sl},async(e,s)=>{const r=t.completeNodeJoin(e.body);return i.syncNow(),n.publish("node.joined",{nodeId:r.id}),s.code(201).send({data:t.requirePublicNode(r.id)})}),e.get("/api/nodes/:id/runtimes",async e=>({data:await t.listNodeRuntimes(Fl.parse(e.params).id)})),e.post("/api/nodes/:id/runtimes",async(e,s)=>{const i=Fl.parse(e.params),r=await t.createNodeRuntime(i.id,e.body);return n.publish("node.runtime.created",{nodeId:i.id,runtimeId:r.id}),s.code(201).send({data:r})}),e.patch("/api/nodes/:id/runtimes/:runtimeId",async e=>{const s=Gl.parse(e.params),i=await t.updateNodeRuntime(s.id,s.runtimeId,e.body);return n.publish("node.runtime.updated",{nodeId:s.id,runtimeId:i.id}),{data:i}}),e.delete("/api/nodes/:id/runtimes/:runtimeId",async e=>{const s=Gl.parse(e.params),i=await t.deleteNodeRuntime(s.id,s.runtimeId);return n.publish("node.runtime.deleted",{nodeId:s.id,runtimeId:s.runtimeId}),{data:i}}),e.post("/api/nodes/:id/runtimes/:runtimeId/check",async e=>{const s=Gl.parse(e.params),i=await t.checkNodeRuntime(s.id,s.runtimeId);return n.publish("node.runtime.checked",{nodeId:s.id,runtimeId:i.id}),{data:i}}),e.get("/api/nodes/:id/docker/images",async e=>({data:await t.listNodeDockerImages(Fl.parse(e.params).id)})),e.get("/api/nodes/:id/image-options",async e=>({data:await t.listNodeImageAvailability(Fl.parse(e.params).id)})),e.get("/api/nodes/:id/local-folders",async(e,n)=>ep(e,n,async n=>({data:await t.listNodeLocalFolders(Fl.parse(e.params).id,n)}))),e.get("/api/nodes/:id/folders/tree",async e=>{const n=Fl.parse(e.params),s=Zl.parse(e.query);return{data:await t.listNodeFolderTree(n.id,{path:s.path,depth:s.depth})}}),e.post("/api/nodes/:id/local-folders",async(e,s)=>{const i=Fl.parse(e.params),r=await t.createNodeLocalFolder(i.id,e.body);return n.publish("node.local-folder.created",{nodeId:i.id,folderId:r.id}),s.code(201).send({data:r})}),e.delete("/api/nodes/:id/local-folders/:folderId",async e=>{const s=Vl.parse(e.params),i=await t.deleteNodeLocalFolder(s.id,s.folderId);return n.publish("node.local-folder.deleted",{nodeId:s.id,folderId:s.folderId}),{data:i}}),e.get("/api/nodes/:id",async e=>({data:t.requirePublicNode(Fl.parse(e.params).id)})),e.patch("/api/nodes/:id",async e=>{const r=t.updateNode(Fl.parse(e.params).id,e.body);return r.connectionEnabled||s.disconnect(r.id),i.syncNow(),n.publish("node.updated",{nodeId:r.id}),{data:t.requirePublicNode(r.id)}}),e.delete("/api/nodes/:id",async e=>{const s=Fl.parse(e.params).id,r=dp.parse(e.query),o=await t.deleteNodeWithProxyLifecycle(s,"true"===r.force);return i.syncNow(),n.publish("node.deleted",{nodeId:s,deleted:o.deleted,revoke:o.revoke}),{data:o}}),e.get("/api/node-runtimes",async(e,n)=>ep(e,n,async e=>{const n=await t.listNodeRuntimesWithDiagnostics(e);return{data:n.items,meta:{nodeErrors:n.nodeErrors}}})),function(e){const{app:t,service:n,nodeAgentTunnel:s,errorPayload:i}=e,r=new dl(s),o=e=>{if(!1===e.connectionEnabled){const e=new Error("Node agent connection is disabled locally.");throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_CONNECTION_DISABLED"}),e}};t.get(br,{websocket:!0,config:ol},(e,t)=>{try{const s=hl.parse(t.query),i=n.requireNode(s.nodeId);Il(i,t),o(i),r.attachMain(i.id,e)}catch(t){const n=i(t);e.send(JSON.stringify({type:"control-plane.error",code:n.code,message:n.message})),e.close(1008,n.message)}}),t.get(`${br}/streams/:streamId`,{websocket:!0,config:ol},(e,t)=>{try{const s=hl.parse(t.query),i=n.requireNode(s.nodeId);Il(i,t),o(i);const a=t.params.streamId;r.attachAuxiliary(i.id,a,e)}catch(t){const n=i(t);e.close(1008,n.message)}}),t.get(`${br}/channels/:channelId`,{websocket:!0,config:ol},(e,t)=>{try{const s=hl.parse(t.query),i=n.requireNode(s.nodeId);Il(i,t),o(i);const a=t.params.channelId;r.attachAuxiliary(i.id,a,e)}catch(t){const n=i(t);e.close(1008,n.message)}}),t.get(`${br}/http-streams/:streamId`,{websocket:!0,config:ol},(e,t)=>{try{const s=hl.parse(t.query),i=n.requireNode(s.nodeId);Il(i,t),o(i);const a=t.params.streamId;r.attachAuxiliary(i.id,a,e)}catch(t){const n=i(t);e.close(1008,n.message)}})}({app:e,service:t,nodeAgentTunnel:s,errorPayload:r})}({app:t,service:n,events:s,nodeAgentTunnel:l,nodeEventSubscriber:p,errorPayload:u}),function({app:e,service:t,events:n}){e.get("/api/controlled-instances",async()=>({data:await t.listControlledInstances()})),e.post("/api/controlled-instances",async(e,s)=>{const i=await t.createControlledInstance(e.body);return n.publish("instance.created",{instanceId:i.id}),s.code(201).send({data:i})}),e.get("/api/controlled-instances/:id",async e=>({data:await t.requireControlledInstance(Fl.parse(e.params).id)})),e.get("/api/controlled-instances/:id/metrics",async e=>({data:await t.instanceResourceMetrics(Fl.parse(e.params).id)})),e.get("/api/controlled-instances/:id/apps/management",async e=>({data:await t.instanceAppManagement(Fl.parse(e.params).id)})),e.post("/api/controlled-instances/:id/apps/:appId/install",async e=>{const n=op.parse(e.params),s=hn.parse(e.body||{});return{data:await t.requestInstanceAppOperation(n.id,n.appId,"install",s)}}),e.post("/api/controlled-instances/:id/apps/:appId/uninstall",async e=>{const n=op.parse(e.params),s=hn.parse(e.body||{});return{data:await t.requestInstanceAppOperation(n.id,n.appId,"uninstall",s)}}),e.get("/api/controlled-instances/:id/apps/jobs/:jobId",async e=>{const n=ap.parse(e.params);return{data:await t.instanceAppManagementJob(n.id,n.jobId)}}),e.patch("/api/controlled-instances/:id",async e=>{const s=await t.updateControlledInstance(Fl.parse(e.params).id,e.body);return n.publish("instance.updated",{instanceId:s.id}),{data:s}}),e.delete("/api/controlled-instances/:id",async e=>{const s=Fl.parse(e.params).id,i=await t.deleteControlledInstance(s);return n.publish("instance.deleted",{instanceId:s,deleted:i}),{data:{deleted:i}}}),e.post("/api/controlled-instances/:id/start",async e=>{const s=await t.startControlledInstance(Fl.parse(e.params).id);return n.publish("instance.started",{instanceId:s.id}),{data:s}}),e.post("/api/controlled-instances/:id/stop",async e=>{const s=await t.stopControlledInstance(Fl.parse(e.params).id);return n.publish("instance.stopped",{instanceId:s.id}),{data:s}}),e.post("/api/controlled-instances/:id/restart",async e=>{const s=await t.restartControlledInstance(Fl.parse(e.params).id);return n.publish("instance.restarted",{instanceId:s.id}),{data:s}}),e.post("/api/controlled-instances/:id/image-provisioning/retry",async e=>{const s=await t.retryControlledInstanceImageProvisioning(Fl.parse(e.params).id);return n.publish("instance.image-provisioning-retried",{instanceId:s.id}),{data:s}}),e.get("/api/controlled-instances/:id/config-sync",async e=>({data:await t.instanceConfigSyncState(Fl.parse(e.params).id)})),e.get("/api/controlled-instances/:id/config-sync/folders",async e=>({data:await t.listInstanceConfigSyncFolders(Fl.parse(e.params).id,rp.parse(e.query))})),e.post("/api/controlled-instances/:id/config-sync",async e=>{const s=Fl.parse(e.params).id,i=await t.syncInstanceConfigs(s,e.body);return n.publish("instance.config-synced",{instanceId:s}),{data:i}}),e.get("/api/instance-board",async(e,n)=>ep(e,n,async e=>{const n=await t.boardWithDiagnostics(e);return{data:n.items,meta:{nodeErrors:n.nodeErrors}}}))}({app:t,service:n,events:s}),function({app:e,service:t,events:n,appSessionAggregator:s,aiSessionAggregator:i,aiSessionUnread:r,aiSessionAttachments:a}){e.get("/api/app-sessions",async e=>{const t=o.z.object({refresh:o.z.string().optional(),includeTombstones:o.z.string().optional(),instanceId:o.z.string().trim().min(1).optional(),streamId:o.z.string().trim().min(1).optional(),sinceRevision:o.z.string().optional()}).parse(e.query||{});if(void 0!==t.sinceRevision){const e=Number(t.sinceRevision);if(!Number.isInteger(e)||e<0){const e=new Error("sinceRevision must be a non-negative integer.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}if(!t.streamId){const e=new Error("streamId is required with sinceRevision.");throw Object.assign(e,{statusCode:400,code:"APP_SESSION_DELTA_INVALID"}),e}return{data:await s.delta({instanceId:t.instanceId,streamId:t.streamId,sinceRevision:e})}}return{data:await s.list({refresh:"true"===t.refresh||"1"===t.refresh,includeTombstones:"true"===t.includeTombstones||"1"===t.includeTombstones})}}),e.post("/api/controlled-instances/:id/apps/sessions",async e=>{const s=Fl.parse(e.params),i=cp.parse(e.body),r=await t.launchAppSession(s.id,i.appId,{...i.options,...i.cwdFolderId?{cwdFolderId:i.cwdFolderId}:{}});return n.publish("instance.app-session.launched",{instanceId:s.id,sessionId:"string"==typeof r.id?r.id:void 0,appId:i.appId}),{data:r}}),e.post("/api/controlled-instances/:id/apps/sessions/:sessionId/stop",async e=>{const s=Kl.parse(e.params),i=await t.stopAppSession(s.id,s.sessionId);return n.publish("instance.app-session.stopped",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.patch("/api/controlled-instances/:id/apps/sessions/:sessionId",async e=>{const s=Kl.parse(e.params),i=lp.parse(e.body),r=await t.renameAppSession(s.id,s.sessionId,i.title);return n.publish("instance.app-session.renamed",{instanceId:s.id,sessionId:s.sessionId,title:i.title}),{data:r}}),e.get("/api/controlled-instances/:id/app-sessions",async e=>{const t=Fl.parse(e.params),n=(await s.list()).instances.find(e=>e.instanceId===t.id);return{data:n?.appSessions||{runningCount:0,problemCount:0,sessions:[],updatedAt:(new Date).toISOString()}}}),e.get("/api/controlled-instances/:id/ai-sessions/history",async e=>{const n=Fl.parse(e.params);return{data:await t.listAiSessionHistory(n.id)}}),e.get("/api/controlled-instances/:id/ai-sessions/history/:sessionId",async e=>{const n=Kl.parse(e.params);return{data:await t.getAiSessionHistoryDetail(n.id,n.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/resume",async e=>{const s=Kl.parse(e.params);pp.parse(e.body||{});const i=await t.resumeAiSession(s.id,s.sessionId);return n.publish("instance.ai-session.resumed",{instanceId:s.id,sessionId:i.aiSessionId,providerSessionId:i.providerSessionId,appSessionId:i.appSessionId,disposition:i.disposition}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/messages",async e=>{const s=Kl.parse(e.params),i=oe.parse(e.body||{}),r=a.resolveRefs(i.attachments,s.id,s.sessionId),o=await t.sendAiSessionMessage(s.id,s.sessionId,i.message,i.mode,r,i.references,i.permissionMode);return n.publish("instance.ai-session.message-sent",{instanceId:s.id,sessionId:s.sessionId}),{data:o}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions",async e=>{const n=Kl.parse(e.params);return{data:await t.aiSessionMentionCatalog(n.id,n.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/mentions/files",async e=>{const n=Kl.parse(e.params),s=se.parse(e.body||{});return{data:await t.searchAiSessionMentionFiles(n.id,n.sessionId,s.query)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/commands",async e=>{const s=Kl.parse(e.params),i=K.parse(e.body||{}),r=await t.executeAiSessionCommand(s.id,s.sessionId,i);return n.publish("instance.ai-session.command-executed",{instanceId:s.id,sessionId:s.sessionId,command:i.command}),{data:r}}),e.get("/api/controlled-instances/:id/ai-sessions/:sessionId/queue",async e=>{const n=Kl.parse(e.params);return{data:await t.aiSessionQueue(n.id,n.sessionId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/steer",async e=>{const s=Jl.parse(e.params),i=await t.steerAiSessionQueuedMessage(s.id,s.sessionId,s.queueId);return n.publish("instance.ai-session.message-sent",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId/retry",async e=>{const n=Jl.parse(e.params);return{data:await t.retryAiSessionQueuedMessage(n.id,n.sessionId,n.queueId)}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/queue/:queueId",async e=>{const n=Jl.parse(e.params);return{data:await t.removeAiSessionQueuedMessage(n.id,n.sessionId,n.queueId)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/interrupt",async e=>{const s=Kl.parse(e.params),i=await t.interruptAiSession(s.id,s.sessionId);return n.publish("instance.ai-session.interrupted",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/approval",async e=>{const s=Kl.parse(e.params),i=ae.parse(e.body||{}),r=await t.resolveAiSessionApproval(s.id,s.sessionId,i.decision);return n.publish("instance.ai-session.approval-resolved",{instanceId:s.id,sessionId:s.sessionId,decision:i.decision}),{data:r}}),e.get("/api/ai-sessions",async(e,t)=>{const n=e.query;if(void 0!==n.sinceRevision){const e=Number(n.sinceRevision);if(!Number.isInteger(e)||e<0)return t.code(400).send({error:"invalid_since_revision"});try{return n.streamId?{data:await i.delta({instanceId:n.instanceId,streamId:n.streamId,sinceRevision:e})}:t.code(400).send({error:"stream_id_required"})}catch(e){if("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED"===e.message)return t.code(400).send({error:"instance_id_required"});throw e}}const s=await i.list({refresh:"true"===n.refresh||"1"===n.refresh});for(const e of s.instances)r.reconcile(e.instanceId,e.aiSessions);return{data:{...s,instances:s.instances.map(e=>({...e,aiSessions:r.decorate(e.instanceId,e.aiSessions)}))}}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/read",async(e,t)=>{const s=Kl.parse(e.params),a=o.z.object({sessionUpdatedAt:o.z.string().datetime()}).strict().parse(e.body||{}),d=(await i.list()).instances.find(e=>e.instanceId===s.id),c=d?.aiSessions.sessions.find(e=>e.id===s.sessionId);if(!d||!c)return t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}});r.reconcile(s.id,d.aiSessions);const l=r.markRead(s.id,s.sessionId,a.sessionUpdatedAt);return l?(n.publish(N,l,{topic:"ai.sessions",scope:{instanceId:s.id,sessionId:s.sessionId}}),{data:l}):t.code(404).send({error:{code:"AI_SESSION_NOT_FOUND",message:"AI session was not found."}})})}({app:t,service:n,events:s,appSessionAggregator:i,aiSessionAggregator:r,aiSessionUnread:a,aiSessionAttachments:c}),function({app:e,service:t,events:n}){e.get("/api/triggers",async()=>({data:await t.listTriggers()})),e.post("/api/triggers",async(e,s)=>{const i=t.createTrigger(e.body);return n.publish("trigger.created",{configHash:i.configHash}),s.code(201).send({data:i})}),e.delete("/api/triggers/:configHash",async e=>{const s=Ql.parse(e.params),i=await t.deleteTrigger(s.configHash);return n.publish("trigger.deleted",{configHash:s.configHash}),{data:i}}),e.post("/api/triggers/:configHash/apply",async e=>{const s=Ql.parse(e.params),i=await t.applyTrigger(s.configHash,e.body||{});return n.publish("trigger.applied",{configHash:s.configHash}),{data:i}}),e.get("/api/controlled-instances/:id/triggers",async e=>{const n=Fl.parse(e.params);return{data:await t.listInstanceTriggers(n.id)}}),e.post("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers",async e=>{const s=Kl.parse(e.params),i=await t.bindAiSessionTrigger(s.id,s.sessionId,e.body||{});return n.publish("instance.ai-session.trigger-bound",{instanceId:s.id,sessionId:s.sessionId}),{data:i}}),e.delete("/api/controlled-instances/:id/ai-sessions/:sessionId/triggers/:configHash",async e=>{const s=Xl.parse(e.params),i=await t.unbindAiSessionTrigger(s.id,s.sessionId,s.configHash);return n.publish("instance.ai-session.trigger-unbound",{instanceId:s.id,sessionId:s.sessionId,configHash:s.configHash}),{data:i}}),e.post("/api/controlled-instances/:id/triggers/:configHash/run",async e=>{const s=Yl.parse(e.params),i=await t.runInstanceTrigger(s.id,s.configHash,e.body||{});return n.publish("instance.trigger.run",{instanceId:s.id,configHash:s.configHash}),{data:i}})}({app:t,service:n,events:s}),function({app:e,service:t,chatGateway:n}){e.get("/api/chat/sessions",async()=>({data:t.listChatSessions()})),e.get("/api/chat/sessions/:id",async e=>({data:t.requireChatSession(Fl.parse(e.params).id)})),e.get("/api/chat-gateway/status",async()=>({data:n.status()})),e.post("/api/chat-gateway/poll-ai-sessions",async()=>({data:await n.pollAiSessionsNow()})),e.get("/api/chat-gateway/bridges",async()=>({data:t.listChatBridges()})),e.post("/api/chat-gateway/bridges",async e=>({data:t.createChatBridge(e.body)})),e.patch("/api/chat-gateway/bridges/:id",async s=>{const i=Fl.parse(s.params).id,r=t.requireChatBridge(i),o=mo.parse(s.body),a=t.updateChatBridge(i,s.body),d="boolean"==typeof o.enabled?o.enabled:void 0;return e.log.info({component:"control-plane-chat-gateway",action:"bridge-patch",bridgeId:i,hasEnabled:void 0!==d,requestedEnabled:d,previousEnabled:r.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),void 0!==d&&d!==r.enabled&&(d?n.startBridge(i):n.stopBridge(i)),{data:a}}),e.post("/api/chat-gateway/bridges/:id/start",async s=>{const i=Fl.parse(s.params).id,r=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!0}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-start-request",bridgeId:i,previousEnabled:r.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:n.startBridge(i)}}),e.post("/api/chat-gateway/bridges/:id/stop",async s=>{const i=Fl.parse(s.params).id,r=t.requireChatBridge(i);return t.updateChatBridge(i,{enabled:!1}),e.log.info({component:"control-plane-chat-gateway",action:"bridge-stop-request",bridgeId:i,previousEnabled:r.enabled,updatedEnabled:t.requireChatBridge(i).enabled},"chat gateway bridge state changed"),{data:n.stopBridge(i)}}),e.delete("/api/chat-gateway/bridges/:id",async e=>{const s=Fl.parse(e.params).id;return n.stopBridge(s),{data:{deleted:t.deleteChatBridge(s)}}}),e.post("/api/chat-gateway/messages",{bodyLimit:67108864},async e=>({data:await t.handleChatGatewayMessage(Ws.parse(e.body))})),e.post("/api/chat-gateway/actions",async e=>({data:await t.handleChatGatewayAction(ip(e.body))})),e.get("/api/pending-routes",async()=>({data:await t.listPendingRoutes()}))}({app:t,service:n,chatGateway:d})}const gp=new Set(["connection","content-length","keep-alive","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),mp=new Set(["authorization"]),hp=new Set(["content-encoding"]);function fp(e){return Object.fromEntries(Object.entries(e).flatMap(([e,t])=>{const n=e.toLowerCase();if(gp.has(n)||mp.has(n)||void 0===t)return[];const s=Array.isArray(t)?t.join(", "):String(t);if("cookie"!==n)return[[e,s]];const i=function(e){return e.split(";").map(e=>e.trim()).filter(e=>e&&e.slice(0,e.indexOf("=")).trim()!==di).join("; ")}(s);return i?[[e,i]]:[]}))}function yp(e){return Object.fromEntries(Object.entries(fp(e)).filter(([e])=>{const t=e.toLowerCase();return"host"!==t&&"cookie"!==t&&"authorization"!==t&&(!t.startsWith("sec-websocket-")||"sec-websocket-origin"===t)}))}function Ip(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}function bp(e,t,n,s,i={}){const r=new AbortController;return t.raw.once("close",()=>r.abort()),e.proxyInstanceHttp(n,s,{...i,signal:r.signal})}function vp(e,t,n){for(const[n,s]of Object.entries(t.headers)){const t=n.toLowerCase();gp.has(t)||hp.has(t)||e.header(n,s)}if(!t.body)return e.code(t.status).send();const s=g.Readable.fromWeb(t.body);return e.raw.once("close",()=>s.destroy()),e.code(t.status).send(n?s.pipe(n):s)}function wp(e){return`/instances/${encodeURIComponent(e)}`}function Sp(e,t,n,s){const i=function(e,t){const n=e.split("?",1)[0]||"/";return t.toLowerCase().includes("text/html")&&!n.startsWith("/api/")}(s,Object.entries(t.headers).find(([e])=>"content-type"===e.toLowerCase())?.[1]||"")?function(e){const t=[];let n=0,s=!1;const i=(i,r=!1)=>{if(s)return;const o=Buffer.concat(t,n).toString("utf8");!/<head(\s[^>]*)?>/i.exec(o)&&!r&&n<65536||(s=!0,i.push(function(e,t){if(e.includes("__TASK_HANDOFF_PUBLIC_BASE__"))return e;const n=`<script>window.__TASK_HANDOFF_PUBLIC_BASE__=${JSON.stringify(t)};<\/script>`;return/<head(\s[^>]*)?>/i.test(e)?e.replace(/<head(\s[^>]*)?>/i,e=>`${e}\n${n}`):`${n}\n${e}`}(o,e)),t.length=0,n=0)};return new g.Transform({transform(e,r,o){s?this.push(e):(t.push(Buffer.from(e)),n+=e.length,i(this)),o()},flush(e){i(this,!0),e()}})}(wp(n)):void 0;return vp(e,t,i)}function Ap(e){return Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):e instanceof URLSearchParams?e.toString():"string"==typeof e?e:null==e?void 0:JSON.stringify(e)}function kp(e,t){if("string"!=typeof t||!e.hasContentTypeParser(t))try{e.addContentTypeParser(t,{parseAs:"buffer"},(e,t,n)=>n(null,t))}catch(e){if(e instanceof Error&&/Content type parser .* already present/i.test(e.message))return;throw e}}function Cp(e){return new URL(e,"http://control-plane.local").searchParams.get("token")||""}function xp(e,t,n){const s=encodeURIComponent(e),i=encodeURIComponent(t),r=n.vnc&&"object"==typeof n.vnc?n.vnc:{};return"kasmvnc"===("string"==typeof r.backend?r.backend:"")?`/apps/access/vnc/proxy/api/apps/sessions/${i}/web/?token=${s}`:`/apps/access/vnc/proxy/api/novnc/vnc.html?token=${s}&path=${encodeURIComponent(`apps/access/vnc/proxy/api/apps/sessions/${i}/vnc?token=${s}`)}&autoconnect=1&resize=scale`}class zp{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;listeners=new Set;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){if(e.type===T){const t=Ee.safeParse(e.payload);return!!t.success||(this.logger?.warn?.({eventType:e.type,issues:t.error.issues,errorCode:"AI_SESSION_MESSAGE_DELTA_INVALID"},"ai-session.aggregator.message-delta.invalid"),!1)}const t=e.type===C?ze:e.type===x?Te:e.type===z?Ne:void 0;if(!t)return!1;const n=t.safeParse(e.payload);return n.success?e.type===C?this.applySnapshot(n.data):e.type===x?this.applyPatch(n.data):this.applyRemoved(n.data):(this.logger?.warn?.({eventType:e.type,issues:n.error.issues,errorCode:"AI_SESSION_EVENT_INVALID"},"ai-session.aggregator.event.invalid"),!1)}onSnapshot(e){return this.listeners.add(e),()=>this.listeners.delete(e)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"ai-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:C,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(n=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:n instanceof Error?n.message:String(n)},"ai-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:x,payload:e})}applyRemoved(e){return this.apply({type:z,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view()}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"ai.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const n=this.snapshots.get(t)?.revision??0,s=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??n+1;if(e.streamId!==s||e.sinceRevision>n)return _e.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===n)return _e.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!1,events:[]});const r=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),o=!r.length||r[0].payload.meta.revision!==e.sinceRevision+1;return _e.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:o,events:o?[]:r.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:n}=e.payload,s=this.advertisedStreams.get(n.instanceId)?.streamId;if(s&&s!==n.streamId)return this.logger?.warn?.({instanceId:n.instanceId,advertisedStreamId:s,streamId:n.streamId,revision:n.revision},"ai-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(n.instanceId);if(i&&!t)return i.streamId===n.streamId&&(i.highWater=Math.max(i.highWater,n.revision)),!1;const r=this.snapshots.get(n.instanceId);e.type===C&&r&&r.streamId!==n.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:n.instanceId,previousStreamId:r.streamId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision,traceId:n.traceId,resetCount:this.counters.streamResets},"ai-session.aggregator.stream-reset"));const o=function(e,t){const{meta:n}=t.payload;if(t.type===C)return e?.streamId===n.streamId&&n.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===n.streamId&&n.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:Se.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==n.streamId)return{kind:"snapshot-required",projection:e,streamId:n.streamId};if(n.revision===e.revision)return{kind:"duplicate",projection:e};if(n.revision<e.revision)return{kind:"stale",projection:e};if(n.revision!==e.revision+1||n.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:n.revision};const s=new Map(e.snapshot.sessions.map(e=>[e.id,e]));if(t.type===x){for(const e of t.payload.upserted)s.set(e.id,e);for(const e of t.payload.removed)s.delete(e)}else for(const e of t.payload.sessionIds)s.delete(e);const i=[...s.values()],r=we.parse({runningCount:i.filter(e=>"running"===e.status).length,waitingCount:i.filter(e=>"waiting"===e.status).length,staleCount:e.snapshot.staleCount,sessions:i,updatedAt:n.generatedAt});return{kind:"applied",projection:Se.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:r})}}(r,e);if("applied"!==o.kind)return this.logger?.warn?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,currentRevision:r?.revision,revision:n.revision,outcome:o.kind},`ai-session.aggregator.event.${o.kind}`),"gap"!==o.kind&&"snapshot-required"!==o.kind||("gap"===o.kind&&(this.counters.gaps+=1),this.recoverStream(n.instanceId,n.streamId,n.revision).catch(e=>{this.logger?.warn?.({instanceId:n.instanceId,streamId:n.streamId,revision:n.revision,error:e instanceof Error?e.message:String(e)},"ai-session.aggregator.recovery.failed")})),!1;const a=o.projection;this.snapshots.set(n.instanceId,a),this.rememberEvent(n.instanceId,e);const d={instanceId:n.instanceId,streamId:a.streamId,aiSessions:a.snapshot,revision:a.revision,lastEventAt:a.lastEventAt};for(const e of this.listeners)try{e(d)}catch{this.listeners.delete(e)}return this.logger?.info?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,revision:n.revision,reason:n.reason,sessionCount:a.snapshot.sessions.length},"ai-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"ai-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),n=new Set(t.instances.map(e=>e.instanceId));for(const[t,s]of e)n.has(t)||this.snapshots.get(t)!==s||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const n of t.instances){const t=this.snapshots.get(n.instanceId);if(t!==e.get(n.instanceId))continue;const s=this.advertisedStreams.get(n.instanceId)?.streamId;s&&s!==n.streamId||t?.streamId===n.streamId&&t.revision>=n.revision||(t?.streamId!==n.streamId&&this.history.delete(n.instanceId),this.snapshots.set(n.instanceId,{streamId:n.streamId,snapshot:n.aiSessions,revision:n.revision,lastEventAt:n.lastEventAt}))}}recoverStream(e,t,n){const s=this.recoveries.get(e);if(s)return s.streamId===t?s.highWater=Math.max(s.highWater,n):(s.streamId=t,s.highWater=n),s.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:n},r=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:n,recoveryNumber:r},"ai-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const n=this.snapshots.get(e);if(n&&n.streamId===t.streamId){if(n.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,n.streamId,n.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:C,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot-fallback"},"ai-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:r,outcome:"delta"},"ai-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:C,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`ais_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot"},"ai-session.aggregator.recovery.snapshot")}const s=this.snapshots.get(e),o=this.advertisedStreams.get(e);if(s&&o&&s.streamId===o.streamId&&s.revision>=Math.max(o.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:s.streamId,revision:s.revision,recoveryNumber:r,outcome:"success"},"ai-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const n=this.history.get(e)??[],s=n.length&&n[0].payload.meta.streamId!==t.payload.meta.streamId?[]:n;s.push({...t,createdAtMs:Date.now()}),this.history.set(e,s),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const n=t.filter(e=>e.createdAtMs>=Date.now()-36e5);n.length?this.history.set(e,n):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("AI_SESSION_DELTA_INSTANCE_ID_REQUIRED")}view(){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([e,t])=>({instanceId:e,streamId:t.streamId,aiSessions:t.snapshot,revision:t.revision,lastEventAt:t.lastEventAt}))}}}const Tp=o.z.object({id:o.z.string().trim().min(1),instanceId:o.z.string().trim().min(1).max(160),sessionId:o.z.string().trim().min(1).max(120),unread:o.z.boolean(),lastStatus:o.z.enum(["running","waiting","idle","failed"]),sessionUpdatedAt:o.z.string().datetime(),createdAt:o.z.string().datetime(),updatedAt:o.z.string().datetime()}).strict();class Np{records;bySession=new Map;listener;constructor(e,t={}){this.records=new ri(e.aiSessionUnreadDir,{schema:Tp}),this.listener=t.onChanged}init(){this.records.init(),this.bySession.clear();for(const e of this.records.list())this.bySession.set(_p(e.instanceId,e.sessionId),e)}reconcile(e,t){const n=new Set;for(const s of t.sessions)n.add(s.id),this.reconcileSession(e,s);for(const t of[...this.bySession.values()])t.instanceId!==e||n.has(t.sessionId)||this.deleteRecord(t)}decorate(e,t){return{...t,sessions:t.sessions.map(t=>({...t,unread:this.bySession.get(_p(e,t.id))?.unread||!1}))}}markRead(e,t,n){const s=this.bySession.get(_p(e,t));return s&&s.sessionUpdatedAt===n&&s.unread?Ep(this.store({...s,unread:!1,updatedAt:(new Date).toISOString()},!1)):s?Ep(s):void 0}removeInstance(e){for(const t of[...this.bySession.values()])t.instanceId===e&&this.deleteRecord(t)}reconcileSession(e,t){const n=_p(e,t.id),s=this.bySession.get(n),i=(new Date).toISOString();if(!s)return void this.store({id:Pp(e,t.id),instanceId:e,sessionId:t.id,unread:!1,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,createdAt:i,updatedAt:i},!1);const r=function(e,t,n){return"running"!==n&&"waiting"!==n&&("running"===t||"waiting"===t||e)}(s.unread,s.lastStatus,t.status),o=r!==s.unread;(o||s.lastStatus!==t.status||s.sessionUpdatedAt!==t.updatedAt)&&this.store({...s,unread:r,lastStatus:t.status,sessionUpdatedAt:t.updatedAt,updatedAt:i},o)}store(e,t){const n=this.records.put(e);return this.bySession.set(_p(n.instanceId,n.sessionId),n),t&&this.listener?.(Ep(n)),n}deleteRecord(e){this.records.delete(e.id),this.bySession.delete(_p(e.instanceId,e.sessionId))}}function Ep(e){return P.parse({instanceId:e.instanceId,sessionId:e.sessionId,unread:e.unread,sessionUpdatedAt:e.sessionUpdatedAt,updatedAt:e.updatedAt})}function _p(e,t){return`${e}\0${t}`}function Pp(e,t){return`ais_unread_${a.createHash("sha256").update(_p(e,t)).digest("hex").slice(0,32)}`}class Op{snapshots=new Map;history=new Map;advertisedStreams=new Map;recoveries=new Map;bootstrap;logger;recoverDelta;recoverSnapshot;onRecoveredEvent;recoveryCount=0;counters={streamResets:0,gaps:0,deltaRecoveries:0,snapshotRecoveries:0,recoveryFailures:0};constructor(e){this.bootstrap=e.bootstrap,this.logger=e.logger,this.recoverDelta=e.recoverDelta,this.recoverSnapshot=e.recoverSnapshot,this.onRecoveredEvent=e.onRecoveredEvent}handleEvent(e){const t=e.type===Oi?Fi:e.type===Ri?Hi:e.type===ji?Wi:void 0;if(!t)return!1;const n=t.safeParse(e.payload);return n.success?e.type===Oi?this.applySnapshot(n.data):e.type===Ri?this.applyPatch(n.data):this.applyRemoved(n.data):(this.logger?.warn?.({eventType:e.type,issues:n.error.issues,errorCode:"APP_SESSION_EVENT_INVALID"},"app-session.aggregator.event.invalid"),!1)}applySnapshot(e){const t=this.advertisedStreams.get(e.meta.instanceId)?.streamId;return t&&t!==e.meta.streamId?(this.logger?.warn?.({instanceId:e.meta.instanceId,advertisedStreamId:t,streamId:e.meta.streamId,revision:e.meta.revision},"app-session.aggregator.snapshot.obsolete-stream"),!1):this.apply({type:Oi,payload:e})}advertiseStream(e,t){return this.advertisedStreams.set(e,t),this.recoverStream(e,t.streamId,t.latestRevision).catch(n=>{this.counters.recoveryFailures+=1,this.logger?.warn?.({instanceId:e,streamId:t.streamId,error:n instanceof Error?n.message:String(n)},"app-session.aggregator.recovery.failed")})}diagnostics(){return{...this.counters,recoveriesStarted:this.recoveryCount,activeRecoveries:this.recoveries.size}}removeInstance(e){this.snapshots.delete(e),this.history.delete(e),this.advertisedStreams.delete(e)}applyPatch(e){return this.apply({type:Ri,payload:e})}applyRemoved(e){return this.apply({type:ji,payload:e})}async list(e={}){return(e.refresh||0===this.snapshots.size)&&await this.bootstrapFromInstances(),this.view({includeTombstones:e.includeTombstones})}async streamDescriptors(){return 0===this.snapshots.size&&await this.bootstrapFromInstances(),[...this.snapshots.entries()].map(([e,t])=>({topic:"app.sessions",instanceId:e,streamId:t.streamId,latestRevision:t.revision,earliestRetainedRevision:this.history.get(e)?.[0]?.payload.meta.revision??t.revision+1}))}async delta(e){0===this.snapshots.size&&await this.bootstrapFromInstances();const t=this.resolveDeltaInstanceId(e.instanceId);this.pruneHistory(t);const n=this.snapshots.get(t)?.revision??0,s=this.snapshots.get(t)?.streamId||"unavailable",i=this.history.get(t)?.[0]?.payload.meta.revision??n+1;if(e.streamId!==s||e.sinceRevision>n)return Gi.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!0,events:[]});if(e.sinceRevision===n)return Gi.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:!1,events:[]});const r=(this.history.get(t)??[]).filter(t=>t.payload.meta.revision>e.sinceRevision),o=!r.length||r[0].payload.meta.revision!==e.sinceRevision+1;return Gi.parse({streamId:s,instanceId:t,sinceRevision:e.sinceRevision,latestRevision:n,earliestRetainedRevision:i,syncRequired:o,events:o?[]:r.map(({type:e,payload:t})=>({type:e,payload:t}))})}apply(e,t=!1){const{meta:n}=e.payload,s=this.advertisedStreams.get(n.instanceId)?.streamId;if(s&&s!==n.streamId)return this.logger?.warn?.({instanceId:n.instanceId,advertisedStreamId:s,streamId:n.streamId,revision:n.revision},"app-session.aggregator.event.obsolete-stream"),!1;const i=this.recoveries.get(n.instanceId);if(i&&!t)return i.streamId===n.streamId&&(i.highWater=Math.max(i.highWater,n.revision)),!1;const r=this.snapshots.get(n.instanceId);e.type===Oi&&r&&r.streamId!==n.streamId&&(this.counters.streamResets+=1,this.logger?.info?.({instanceId:n.instanceId,previousStreamId:r.streamId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision,traceId:n.traceId,resetCount:this.counters.streamResets},"app-session.aggregator.stream-reset"));const o=function(e,t){const{meta:n}=t.payload;if(t.type===Oi)return e?.streamId===n.streamId&&n.revision===e.revision?{kind:"duplicate",projection:e}:e?.streamId===n.streamId&&n.revision<e.revision?{kind:"stale",projection:e}:{kind:"applied",projection:Li.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:t.payload.snapshot})};if(!e||e.streamId!==n.streamId)return{kind:"snapshot-required",projection:e,streamId:n.streamId};if(n.revision===e.revision)return{kind:"duplicate",projection:e};if(n.revision<e.revision)return{kind:"stale",projection:e};if(n.revision!==e.revision+1||n.previousRevision!==e.revision)return{kind:"gap",projection:e,expectedRevision:e.revision+1,receivedRevision:n.revision};const s=[...e.snapshot.sessions];if(t.type===Ri){const e=s.findIndex(e=>e.id===t.payload.session.id);e<0?s.push(t.payload.session):s[e]=t.payload.session}else{const e=s.findIndex(e=>e.id===t.payload.sessionId);e>=0&&s.splice(e,1),t.payload.tombstone&&s.push(t.payload.tombstone)}const i=Vi(s,n.generatedAt);return{kind:"applied",projection:Li.parse({streamId:n.streamId,revision:n.revision,lastEventAt:n.generatedAt,snapshot:i})}}(r,e);if("applied"!==o.kind)return this.logger?.warn?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,currentRevision:r?.revision,revision:n.revision,outcome:o.kind},`app-session.aggregator.event.${o.kind}`),"gap"!==o.kind&&"snapshot-required"!==o.kind||("gap"===o.kind&&(this.counters.gaps+=1),this.recoverStream(n.instanceId,n.streamId,n.revision).catch(e=>{this.logger?.warn?.({instanceId:n.instanceId,streamId:n.streamId,revision:n.revision,error:e instanceof Error?e.message:String(e)},"app-session.aggregator.recovery.failed")})),!1;const a=o.projection;return this.snapshots.set(n.instanceId,a),this.rememberEvent(n.instanceId,e),this.logger?.info?.({traceId:n.traceId,streamId:n.streamId,instanceId:n.instanceId,revision:n.revision,reason:n.reason,sessionCount:a.snapshot.sessions.length,runningCount:a.snapshot.runningCount,problemCount:a.snapshot.problemCount},"app-session.aggregator.event.accepted"),!0}async bootstrapFromInstances(){this.logger?.info?.({},"app-session.aggregator.bootstrap");const e=new Map(this.snapshots),t=await this.bootstrap(),n=new Set(t.instances.map(e=>e.instanceId));for(const[t,s]of e)n.has(t)||this.snapshots.get(t)!==s||(this.snapshots.delete(t),this.history.delete(t),this.advertisedStreams.delete(t));for(const n of t.instances){const t=this.snapshots.get(n.instanceId);if(t!==e.get(n.instanceId))continue;const s=this.advertisedStreams.get(n.instanceId)?.streamId;s&&s!==n.streamId||t?.streamId===n.streamId&&t.revision>=n.revision||(t?.streamId!==n.streamId&&this.history.delete(n.instanceId),this.snapshots.set(n.instanceId,{streamId:n.streamId,snapshot:n.appSessions,revision:n.revision,lastEventAt:n.lastEventAt}))}}recoverStream(e,t,n){const s=this.recoveries.get(e);if(s)return s.streamId===t?s.highWater=Math.max(s.highWater,n):(s.streamId=t,s.highWater=n),s.promise;if(!this.recoverDelta||!this.recoverSnapshot)return Promise.resolve();const i={promise:Promise.resolve(),streamId:t,highWater:n},r=++this.recoveryCount;return this.logger?.info?.({instanceId:e,highWater:n,recoveryNumber:r},"app-session.aggregator.recovery.started"),i.promise=(async()=>{for(;;){const t=this.advertisedStreams.get(e);if(!t)return;i.streamId!==t.streamId&&(i.streamId=t.streamId,i.highWater=t.latestRevision);const n=this.snapshots.get(e);if(n&&n.streamId===t.streamId){if(n.revision<Math.max(t.latestRevision,i.highWater)){const t=await this.recoverDelta(e,n.streamId,n.revision);if(this.advertisedStreams.get(e)?.streamId!==t.streamId||i.streamId!==t.streamId)continue;if(t.syncRequired){const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:Oi,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot-fallback"},"app-session.aggregator.recovery.snapshot")}else{for(const e of t.events)this.apply(e,!0)&&this.onRecoveredEvent?.(e);this.counters.deltaRecoveries+=1,i.highWater=Math.max(i.highWater,t.latestRevision),this.logger?.info?.({instanceId:e,streamId:t.streamId,sinceRevision:t.sinceRevision,latestRevision:t.latestRevision,eventCount:t.events.length,recoveryNumber:r,outcome:"delta"},"app-session.aggregator.recovery.delta")}}}else{const t=await this.recoverSnapshot(e);if(this.advertisedStreams.get(e)?.streamId!==t.streamId)continue;const n={type:Oi,payload:{meta:{streamId:t.streamId,instanceId:e,revision:t.revision,traceId:`aps_recovery_${Date.now().toString(36)}`,generatedAt:t.lastEventAt,reason:"startup"},snapshot:t.snapshot}};this.apply(n,!0)&&this.onRecoveredEvent?.(n),this.counters.snapshotRecoveries+=1,this.logger?.info?.({instanceId:e,streamId:t.streamId,revision:t.revision,recoveryNumber:r,outcome:"snapshot"},"app-session.aggregator.recovery.snapshot")}const s=this.snapshots.get(e),o=this.advertisedStreams.get(e);if(s&&o&&s.streamId===o.streamId&&s.revision>=Math.max(o.latestRevision,i.highWater))return void this.logger?.info?.({instanceId:e,streamId:s.streamId,revision:s.revision,recoveryNumber:r,outcome:"success"},"app-session.aggregator.recovery.completed")}})().finally(()=>this.recoveries.delete(e)),this.recoveries.set(e,i),i.promise}rememberEvent(e,t){const n=this.history.get(e)??[],s=n.length&&n[0].payload.meta.streamId!==t.payload.meta.streamId?[]:n;s.push({...t,createdAtMs:Date.now()}),this.history.set(e,s),this.pruneHistory(e)}pruneHistory(e){const t=this.history.get(e);if(!t?.length)return;const n=t.filter(e=>e.createdAtMs>=Date.now()-36e5);n.length?this.history.set(e,n):this.history.delete(e)}resolveDeltaInstanceId(e){if(e)return e;const t=[...this.snapshots.keys()];if(1===t.length)return t[0];throw new Error("APP_SESSION_DELTA_INSTANCE_ID_REQUIRED")}visibleSnapshot(e){return Vi(e.sessions.filter(e=>ka("string"==typeof e.status?e.status:void 0)),e.updatedAt)}view(e={}){return{updatedAt:(new Date).toISOString(),instances:[...this.snapshots.entries()].map(([t,n])=>({instanceId:t,streamId:n.streamId,appSessions:e.includeTombstones?n.snapshot:this.visibleSnapshot(n.snapshot),revision:n.revision,lastEventAt:n.lastEventAt}))}}}function Rp(e){return e.raw[0].replace(/\\\$\{/g,"${")}class jp{states=new Map;events;constructor(e){this.events=e}handle(e){if(e.type===Ms){const t=qs.safeParse(e.payload);return void(t.success&&this.reconcileLifecycle(t.data))}if("instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";return void(t&&this.deleteState(t))}if(e.type===ds){const t=ps.safeParse(e.payload);return void(t.success&&this.applyOutput(t.data))}if(e.type===cs){const t=us.safeParse(e.payload);t.success&&this.applyFinished(t.data)}}snapshots(){return[...this.states.values()].filter(e=>e.progress).map(e=>gs.parse({...e.progress,terminalTail:e.terminalTail,...e.terminalTruncated?{terminalTruncated:!0}:{}}))}close(){for(const e of this.states.values())e.timer&&clearTimeout(e.timer);this.states.clear()}deleteState(e){const t=this.states.get(e);t?.timer&&clearTimeout(t.timer),this.states.delete(e)}reconcileLifecycle(e){const t=this.states.get(e.instanceId),n=e.imageProvisioning;t&&(!n||n.generation>t.identity.generation||n.generation===t.identity.generation&&"ready"===n.phase)&&this.deleteState(e.instanceId)}applyOutput(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&!e.replay&&e.sequence<=t.sequence)return;const n=this.requireState(e);e.replay&&(n.terminal=new $p,n.terminalTail="",n.layers.clear(),n.terminalTruncated=!1),n.sequence=Math.max(n.sequence,e.sequence),n.observedAt=e.observedAt,n.terminal.write(e.data);const s=`${n.terminalTail}${e.data}`;n.terminalTruncated||=s.length>262144,n.terminalTail=s.slice(-262144),function(e,t){for(const n of t.split("\n")){const t=n.trim().match(/^([a-f0-9]{6,64}):\s+(.+)$/i);if(!t)continue;const[,s,i]=t,r=i.toLowerCase(),o=["pull complete","already exists","download complete","pulling fs layer","waiting","downloading","extracting"].find(e=>r.startsWith(e));if(!o)continue;const a=e.get(s)||{status:o},d=Mp(i);e.set(s,{...a,status:o,...d||{}})}}(n.layers,n.terminal.text()),n.progress=Dp(n),this.schedule(n)}applyFinished(e){const t=this.states.get(e.instanceId);if(t&&e.generation<t.identity.generation)return;if(t&&e.generation===t.identity.generation&&e.sequence<=t.sequence)return;const n=this.requireState(e);n.sequence=Math.max(n.sequence,e.sequence),n.observedAt=e.observedAt,n.outcome=e.outcome,n.progress=Dp(n),this.publish(n,!0)}requireState(e){const t=this.states.get(e.instanceId);if(t&&t.identity.generation===e.generation)return t;t?.timer&&clearTimeout(t.timer);const n={identity:{instanceId:e.instanceId,generation:e.generation,requestedReference:e.requestedReference},sequence:e.sequence,observedAt:e.observedAt,terminal:new $p,terminalTail:"",terminalTruncated:!1,layers:new Map};return this.states.set(e.instanceId,n),n}schedule(e){e.timer||(e.timer=setTimeout(()=>this.publish(e,!1),250),e.timer.unref?.())}publish(e,t){e.timer&&clearTimeout(e.timer),e.timer=void 0,e.progress&&(e.progress=Dp(e),this.events.publish("image.pull.progress",e.progress,{topic:"instances",scope:{instanceId:e.identity.instanceId}}),t&&(e.progress=Dp(e)))}}function Dp(e){const t=[...e.layers.values()],n=t.filter(e=>["pull complete","already exists"].includes(e.status)).length,s=t.filter(e=>"download complete"===e.status).length,i=t.filter(e=>"downloading"===e.status).length,r=t.filter(e=>"extracting"===e.status).length;let o=0,a=0;for(const e of t)e.total&&(a+=e.total,o+=["pull complete","already exists","download complete"].includes(e.status)?e.total:Math.min(e.current||0,e.total));const d="failed"===e.outcome?"failed":"succeeded"===e.outcome?"complete":r>0?"extracting":t.length>0?"pulling":"connecting",c=t.filter(e=>"already exists"!==e.status),l=c.length>0&&c.every(e=>Boolean(e.total)),p=l&&a>0?Math.max(0,Math.min(100,o/a*100)):void 0,u="failed"===d?"Image pull failed":"complete"===d?"Image pull complete":t.length?`${n}/${t.length} layers ready${void 0===p?"":` · ${Math.round(p)}%`}`:"Connecting to image registry";return gs.parse({...e.identity,sequence:e.sequence,observedAt:e.observedAt,status:d,layers:{total:t.length,completed:n,downloaded:s,downloading:i,extracting:r},...l&&a>0?{bytes:{current:o,total:a},percent:p}:{},message:u})}function Mp(e){const t=e.match(/([\d.]+)\s*([kmgt]?i?b)\s*\/\s*([\d.]+)\s*([kmgt]?i?b)/i);if(t)return{current:qp(t[1],t[2]),total:qp(t[3],t[4])}}function qp(e,t){return Number(e)*1024**({b:0,kb:1,kib:1,mb:2,mib:2,gb:3,gib:3,tb:4,tib:4}[t.toLowerCase()]||0)}class $p{lines=[[]];row=0;col=0;pending="";write(e){const t=`${this.pending}${e}`;this.pending="";for(let e=0;e<t.length;){const n=t[e];if(""===n&&"["===t[e+1]){let n=e+2;for(;n<t.length&&!/[\x40-\x7e]/.test(t[n]);)n+=1;if(n>=t.length){this.pending=t.slice(e);break}this.csi(t.slice(e+2,n),t[n]),e=n+1;continue}"\r"===n?this.col=0:"\n"===n?(this.row+=1,this.col=0,this.ensureLine()):"\b"===n?this.col=Math.max(0,this.col-1):n>=" "&&(this.ensureLine(),this.lines[this.row][this.col]=n,this.col+=1),e+=1}this.trim()}text(){return this.lines.map(e=>e.join("").trimEnd()).join("\n")}csi(e,t){const n=e.replace(/^\?/,"").split(";").map(e=>Number(e||1)),s=n[0]||1;"A"===t?this.row=Math.max(0,this.row-s):"B"===t?(this.row+=s,this.ensureLine()):"C"===t?this.col+=s:"D"===t?this.col=Math.max(0,this.col-s):"G"===t?this.col=Math.max(0,s-1):"H"===t||"f"===t?(this.row=Math.max(0,s-1),this.col=Math.max(0,(n[1]||1)-1),this.ensureLine()):"K"===t?(this.ensureLine(),2===(n[0]||0)?this.lines[this.row]=[]:this.lines[this.row].splice(this.col)):"J"===t&&2===(n[0]||0)&&(this.lines=[[]],this.row=0,this.col=0)}ensureLine(){for(;this.lines.length<=this.row;)this.lines.push([])}trim(){if(this.lines.length<=500)return;const e=this.lines.length-500;this.lines.splice(0,e),this.row=Math.max(0,this.row-e)}}const Lp=o.z.object({revision:o.z.number().int().nonnegative(),invites:o.z.array(lt),bindings:o.z.array(ht)}).strict(),Bp={invite:new Set(Object.keys(lt.shape)),binding:new Set(Object.keys(ht.shape))};function Up(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e;return Object.fromEntries([...t].filter(e=>e in n).map(e=>[e,n[e]]))}function Fp(e,t,n){if(!t||"object"!=typeof t||Array.isArray(t))return t;const s=t,i=Bp[e],r=Object.keys(s).filter(e=>!i.has(e));r.length>0&&n?.({kind:e,fields:r});const o=Up(s,i);return"binding"===e&&o.lastError&&(o.lastError=Up(o.lastError,Object.keys(dt.shape))),o}function Hp(e){const{tokenHash:t,...n}=e;return pt.parse(n)}function Wp(e){const{credentialHash:t,...n}=e;return ft.parse(n)}class Gp{authority;constructor(e,t){this.authority=new oi(e,()=>({revision:0,invites:[],bindings:[]}),{schema:Lp,sanitize:e=>function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e,s=new Set(["revision","invites","bindings"]),i=Object.keys(n).filter(e=>!s.has(e));return i.length>0&&t?.({kind:"authority",fields:i}),{...void 0===n.revision?{}:{revision:n.revision},invites:Array.isArray(n.invites)?n.invites.map(e=>Fp("invite",e,t)):n.invites,bindings:Array.isArray(n.bindings)?n.bindings.map(e=>Fp("binding",e,t)):n.bindings}}(e,e=>t?.("unknown stored control-plane proxy fields were ignored",e)),logger:t,directoryMode:448,fileMode:384})}init(){this.authority.init()}snapshot(){return this.authority.get()}transaction(e){const t=this.authority.get(),n=structuredClone(t),s=e(n);return JSON.stringify(n)!==JSON.stringify(t)&&this.authority.put({...n,revision:t.revision+1}),s}listInvites(){return this.snapshot().invites}getInvite(e){return this.snapshot().invites.find(t=>t.id===e)}listBindings(){return this.snapshot().bindings}getBinding(e){return this.snapshot().bindings.find(t=>t.id===e)}}class Vp extends Error{code;statusCode;retryable;details;constructor(e,t,n,s=!1,i={}){super(t),this.name="ControlPlaneProxyServiceError",this.code=e,this.statusCode=n,this.retryable=s,this.details=i}}const Kp=e=>a.createHash("sha256").update(e).digest("base64url"),Jp=()=>a.randomBytes(32).toString("base64url");function Qp(e,t){const n=Kp(t),s=Buffer.from(e),i=Buffer.from(n);return s.length===i.length&&a.timingSafeEqual(s,i)}function Yp(e,t,n,s=!1,i={}){throw new Vp(e,t,n,s,i)}class Xp{store;targets;configuredProxyOrigin;proxyOriginProvider;now;createTokenValue;constructor(e,t,n){this.store=e,this.targets=t,this.configuredProxyOrigin=n.proxyOrigin,this.proxyOriginProvider=n.proxyOriginProvider,this.now=n.now??(()=>new Date),this.createTokenValue=n.createToken??Jp}init(){this.store.init(),this.expireInvites()}createInvite(e,t){const n=this.resolveProxyOrigin();n||Yp(ot.Unavailable,"Configure an externally reachable HTTPS control-plane proxy origin before creating an invite.",409,!1);const s=ut.parse(e);this.requireManageableTarget(s.targetNodeId);const i=this.createTokenValue(),r=this.nowIso(),o={id:si("proxy_invite"),targetNodeId:s.targetNodeId,tokenHash:Kp(i),status:"active",createdBy:t,expiresAt:new Date(this.now().getTime()+1e3*s.expiresInSeconds).toISOString(),createdAt:r,updatedAt:r},a=gt.parse({invite:Hp(o),token:i,proxyOrigin:n,protocolVersion:Ge});return this.store.transaction(e=>e.invites.push(o)),a}resolveProxyOrigin(){const e=this.configuredProxyOrigin??this.proxyOriginProvider?.();if(!e)return;const t=Xe.safeParse(e);return t.success?t.data:void 0}listInvites(){return this.expireInvites(),this.store.listInvites().map(Hp)}revokeInvite(e){const t=this.requireInvite(e);if("consumed"===t.status&&Yp(ot.InviteConsumed,"A consumed proxy invite cannot be revoked independently of its binding.",409,!1,{inviteId:e}),"revoked"===t.status||"expired"===t.status)return Hp(t);const n=this.nowIso(),s={...t,status:"revoked",revokedAt:n,updatedAt:n};return this.store.transaction(t=>{t.invites[t.invites.findIndex(t=>t.id===e)]=s}),Hp(s)}claimInvite(e){const t=yt.parse(e);this.requireCurrentProtocol(t.protocolVersion),this.expireInvites();const n=Kp(t.credential);return this.store.transaction(e=>{const s=e.bindings.find(e=>e.claimId===t.claimId);if(!t.inviteToken)return s||Yp(ot.InviteInvalid,"Proxy invite token is required for the first claim.",401),(s.sourceControlPlaneId!==t.sourceControlPlaneId||s.bindingKeyId!==t.bindingKeyId||s.credentialHash!==n||void 0!==t.targetNodeId&&s.targetNodeId!==t.targetNodeId)&&Yp(ot.BindingIdentityConflict,"Proxy claim conflicts with an existing binding identity.",409,!1,{claimId:t.claimId}),"revoked"===s.status&&Yp(ot.BindingRevoked,"Proxy binding is revoked.",403,!1,{bindingId:s.id}),this.claimResult(s,this.requireKnownTarget(s.targetNodeId));const i=e.invites.find(e=>Qp(e.tokenHash,t.inviteToken));i||Yp(ot.InviteInvalid,"Proxy invite is invalid.",401),t.targetNodeId&&t.targetNodeId!==i.targetNodeId&&Yp(ot.TargetMismatch,"Proxy invite is bound to a different target.",409,!1,{inviteId:i.id,targetNodeId:t.targetNodeId});const r=(o=i.id,`proxy_binding_${Kp(o).slice(0,32)}`);var o;const a=e.bindings.find(e=>e.id===r),d=e.bindings.find(e=>e.claimId===t.claimId),c=a??d;if(a&&d&&a.id!==d.id&&Yp(ot.BindingIdentityConflict,"Proxy claim conflicts with an existing binding identity.",409,!1,{claimId:t.claimId}),c)return this.requireExactIdentity(c,r,i.targetNodeId,t,n),"revoked"===c.status&&Yp(ot.BindingRevoked,"Proxy binding is revoked.",403,!1,{bindingId:c.id}),"consumed"===i.status&&i.consumedByClaimId===t.claimId||Yp(ot.BindingIdentityConflict,"Proxy binding and invite authority are inconsistent.",409,!1,{claimId:t.claimId}),this.claimResult(c,this.requireKnownTarget(c.targetNodeId));this.requireClaimableInvite(i);const l=this.requireManageableTarget(i.targetNodeId),p=this.nowIso(),u={id:r,claimId:t.claimId,sourceControlPlaneId:t.sourceControlPlaneId,targetNodeId:i.targetNodeId,bindingKeyId:t.bindingKeyId,credentialHash:n,status:"active",revision:1,createdAt:p,updatedAt:p};return e.bindings.push(u),Object.assign(i,{status:"consumed",consumedByClaimId:t.claimId,consumedAt:p,updatedAt:p}),this.claimResult(u,l)})}listBindings(){return this.store.listBindings().map(Wp)}authenticateBinding(e,t){const n=It.parse(t),s=this.store.getBinding(e);return s||Yp(ot.BindingUnknown,"Proxy binding is unknown.",404,!1,{bindingId:e}),"active"!==s.status&&Yp(ot.BindingRevoked,"Proxy binding is revoked.",403,!1,{bindingId:e}),s.sourceControlPlaneId===n.sourceControlPlaneId&&s.bindingKeyId===n.bindingKeyId&&Qp(s.credentialHash,n.credential)||Yp(ot.AuthenticationFailed,"Proxy binding authentication failed.",401,!1,{bindingId:e}),Wp(s)}revokeBinding(e){const t=this.requireBinding(e);if("revoked"===t.status)return Wp(t);const n=this.nowIso(),s={...t,status:"revoked",revision:t.revision+1,revokedAt:n,updatedAt:n};return this.store.transaction(t=>{t.bindings[t.bindings.findIndex(t=>t.id===e)]=s}),Wp(s)}revokeTarget(e){return this.store.transaction(t=>{const n=this.nowIso();return{bindings:t.bindings.filter(t=>t.targetNodeId===e&&"active"===t.status).map(e=>(Object.assign(e,{status:"revoked",revision:e.revision+1,revokedAt:n,updatedAt:n}),Wp(e))),invites:t.invites.filter(t=>t.targetNodeId===e&&"active"===t.status).map(e=>(Object.assign(e,{status:"revoked",revokedAt:n,updatedAt:n}),Hp(e)))}})}nowIso(){return this.now().toISOString()}requireCurrentProtocol(e){e!==Ge&&Yp(ot.ProtocolUnsupported,"Control-plane proxy protocol version is unsupported.",426,!1,{requestedVersion:e,supportedVersion:Ge})}requireKnownTarget(e){const t=this.targets.get(e);return t||Yp(ot.TargetUnavailable,"Proxy target no longer exists.",404,!1,{targetNodeId:e}),t}requireManageableTarget(e){const t=this.requireKnownTarget(e);return t.manageable&&"online"===t.status||Yp(ot.TargetUnavailable,"Proxy target is not currently manageable.",503,!0,{targetNodeId:e}),t}requireInvite(e){const t=this.store.getInvite(e);return t||Yp(ot.InviteInvalid,"Proxy invite is unknown.",404,!1,{inviteId:e}),t}requireBinding(e){const t=this.store.getBinding(e);return t||Yp(ot.BindingUnknown,"Proxy binding is unknown.",404,!1,{bindingId:e}),t}requireClaimableInvite(e){"active"!==e.status&&Yp({expired:ot.InviteExpired,consumed:ot.InviteConsumed,revoked:ot.InviteRevoked}[e.status],`Proxy invite is ${e.status}.`,"expired"===e.status?410:409,!1,{inviteId:e.id})}requireExactIdentity(e,t,n,s,i){e.id===t&&e.claimId===s.claimId&&e.sourceControlPlaneId===s.sourceControlPlaneId&&e.targetNodeId===n&&e.bindingKeyId===s.bindingKeyId&&e.credentialHash===i||Yp(ot.BindingIdentityConflict,"Proxy claim conflicts with an existing binding identity.",409,!1,{claimId:s.claimId})}claimResult(e,t){const{manageable:n,...s}=t;return Ct.parse({protocolVersion:Ge,binding:Wp(e),target:s})}expireInvites(){const e=this.now().getTime();this.store.listInvites().some(t=>"active"===t.status&&Date.parse(t.expiresAt)<=e)&&this.store.transaction(t=>{const n=this.nowIso();for(const s of t.invites)"active"===s.status&&Date.parse(s.expiresAt)<=e&&(s.status="expired",s.updatedAt=n)})}}class Zp extends Error{statusCode=429;code=ot.ResourceLimit;retryable=!0;details;constructor(e,t){super(e),this.name="NodeProxyRuntimeError",this.details=t}}class eu{maxRequestBodyBytes;maxConcurrentHttpPerBinding;maxConcurrentStreamsPerBinding;maxHttpRequestsPerMinutePerBinding;maxConcurrentWebSocketsPerBinding;maxHttpResponseBytes;httpStreamIdleTimeoutMs;maxWebSocketFrameBytes;maxWebSocketBytes;webSocketIdleTimeoutMs;httpRequestWindowMs;bindings=new Map;requestWindows=new Map;requestWindowTimers=new Map;constructor(e={}){this.maxConcurrentHttpPerBinding=e.maxConcurrentHttpPerBinding??32,this.maxConcurrentStreamsPerBinding=e.maxConcurrentStreamsPerBinding??16,this.maxHttpRequestsPerMinutePerBinding=e.maxHttpRequestsPerMinutePerBinding??600,this.maxConcurrentWebSocketsPerBinding=e.maxConcurrentWebSocketsPerBinding??16,this.maxRequestBodyBytes=e.maxRequestBodyBytes??8388608,this.maxHttpResponseBytes=e.maxHttpResponseBytes??268435456,this.httpStreamIdleTimeoutMs=e.httpStreamIdleTimeoutMs??12e4,this.maxWebSocketFrameBytes=e.maxWebSocketFrameBytes??8388608,this.maxWebSocketBytes=e.maxWebSocketBytes??268435456,this.webSocketIdleTimeoutMs=e.webSocketIdleTimeoutMs??3e5,this.httpRequestWindowMs=e.httpRequestWindowMs??6e4}reserveHttp(e){this.consumeRequestRate(e);const t=this.activity(e);if(t.controllers.size>=this.maxConcurrentHttpPerBinding)throw this.deleteIfEmpty(e,t),this.limit("Proxy HTTP concurrency limit exceeded.",e,"http-concurrency",this.maxConcurrentHttpPerBinding);const n=new AbortController;t.controllers.add(n);let s,i=!1,r=!1,o=0;const a=()=>{r&&!i&&(s&&clearTimeout(s),s=setTimeout(()=>{n.abort(Object.assign(new Error("Proxy HTTP stream idle timeout."),{code:ot.ResourceLimit}))},this.httpStreamIdleTimeoutMs),s.unref?.())};return{controller:n,acceptRequestBody:t=>{if(t>this.maxRequestBodyBytes)throw this.limit("Proxy request body limit exceeded.",e,"request-body",this.maxRequestBodyBytes)},beginResponseStream:()=>{if(!i&&!r){if(t.streams.size>=this.maxConcurrentStreamsPerBinding)throw this.limit("Proxy HTTP stream concurrency limit exceeded.",e,"stream-concurrency",this.maxConcurrentStreamsPerBinding);r=!0,t.streams.add(n),a()}},acceptResponseChunk:t=>{if(!i){if(o+=tu(t),o>this.maxHttpResponseBytes){const t=this.limit("Proxy HTTP response byte limit exceeded.",e,"response-bytes",this.maxHttpResponseBytes);throw n.abort(t),t}a()}},release:()=>{i||(i=!0,s&&clearTimeout(s),t.controllers.delete(n),t.streams.delete(n),this.deleteIfEmpty(e,t))}}}openHttp(e,t){const n=this.reserveHttp(e);try{return n.acceptRequestBody(t),n}catch(e){throw n.release(),e}}openWebSocket(e,t,n){const s=this.activity(e);if(s.sockets.size>=this.maxConcurrentWebSocketsPerBinding)throw this.limit("Proxy WebSocket concurrency limit exceeded.",e,"websocket-concurrency",this.maxConcurrentWebSocketsPerBinding);let i,r=!1,o=0;const a=()=>{r||(r=!0,i&&clearTimeout(i),s.sockets.delete(p),this.deleteIfEmpty(e,s),n?.())},d=e=>{a(),t.close(1009,e)},c=()=>{i&&clearTimeout(i),i=setTimeout(()=>{a(),t.close(1001,"Proxy WebSocket idle timeout.")},this.webSocketIdleTimeoutMs),i.unref?.()},l=e=>{const t=tu(e);return t>this.maxWebSocketFrameBytes?(d("Proxy WebSocket frame limit exceeded."),!1):(o+=t,o>this.maxWebSocketBytes?(d("Proxy WebSocket byte limit exceeded."),!1):(c(),!0))};c();const p={get readyState(){return t.readyState},send:e=>{!r&&l(e)&&t.send(e)},close:(e,n)=>{a(),t.close(e,n)},on:(e,n)=>{"message"!==e?"close"!==e&&"error"!==e?t.on(e,n):t.on(e,(...e)=>{a(),n(...e)}):t.on(e,(e,...t)=>{!r&&l(e)&&n(e,...t)})}};return s.sockets.add(p),p}closeBinding(e,t="Proxy binding was revoked."){const n=this.bindings.get(e);if(!n)return this.clearRequestWindow(e),{abortedRequests:0,closedSockets:0};const s=[...n.controllers],i=[...n.sockets],r=[...n.cleanup];this.bindings.delete(e);for(const e of s)e.abort(Object.assign(new Error(t),{code:ot.BindingRevoked}));for(const e of r)e();for(const e of i)e.close(1008,t);return n.controllers.clear(),n.streams.clear(),n.sockets.clear(),n.cleanup.clear(),this.clearRequestWindow(e),{abortedRequests:s.length,closedSockets:i.length}}diagnostics(e){if(e){const t=this.bindings.get(e);return{bindingId:e,activeHttp:t?.controllers.size??0,activeStreams:t?.streams.size??0,activeWebSockets:t?.sockets.size??0}}return[...this.bindings].map(([e,t])=>({bindingId:e,activeHttp:t.controllers.size,activeStreams:t.streams.size,activeWebSockets:t.sockets.size}))}registerBindingCleanup(e,t){const n=this.activity(e);return n.cleanup.add(t),()=>{n.cleanup.delete(t),this.deleteIfEmpty(e,n)}}activity(e){const t=this.bindings.get(e);if(t)return t;const n={controllers:new Set,streams:new Set,sockets:new Set,cleanup:new Set};return this.bindings.set(e,n),n}deleteIfEmpty(e,t){0===t.controllers.size&&0===t.streams.size&&0===t.sockets.size&&0===t.cleanup.size&&this.bindings.get(e)===t&&this.bindings.delete(e)}limit(e,t,n,s){return new Zp(e,{bindingId:t,resource:n,limit:s})}consumeRequestRate(e){const t=Date.now(),n=t-this.httpRequestWindowMs,s=(this.requestWindows.get(e)||[]).filter(e=>e>n);if(s.length>=this.maxHttpRequestsPerMinutePerBinding)throw this.limit("Proxy HTTP request rate limit exceeded.",e,"request-rate",this.maxHttpRequestsPerMinutePerBinding);s.push(t),this.requestWindows.set(e,s),this.scheduleRequestWindowCleanup(e)}scheduleRequestWindowCleanup(e){const t=this.requestWindowTimers.get(e);t&&clearTimeout(t);const n=this.requestWindows.get(e);if(!n?.length)return this.requestWindows.delete(e),void this.requestWindowTimers.delete(e);const s=Math.max(1,n[0]+this.httpRequestWindowMs-Date.now()+1),i=setTimeout(()=>{this.requestWindowTimers.delete(e);const t=Date.now()-this.httpRequestWindowMs,n=(this.requestWindows.get(e)||[]).filter(e=>e>t);n.length?(this.requestWindows.set(e,n),this.scheduleRequestWindowCleanup(e)):this.requestWindows.delete(e)},s);i.unref?.(),this.requestWindowTimers.set(e,i)}clearRequestWindow(e){this.requestWindows.delete(e);const t=this.requestWindowTimers.get(e);t&&clearTimeout(t),this.requestWindowTimers.delete(e)}}function tu(e){return Buffer.isBuffer(e)||e instanceof ArrayBuffer||ArrayBuffer.isView(e)?e.byteLength:Buffer.byteLength("string"==typeof e?e:String(e),"utf8")}class nu{streams=new Map;historyLimit;maxEventBytes;maxHistoryBytes;createStreamId;projectTarget;unsubscribeBus;constructor(e,t){this.historyLimit=Math.max(1,t.historyLimit??256),this.maxEventBytes=Math.max(1024,t.maxEventBytes??524288),this.maxHistoryBytes=Math.max(this.maxEventBytes,t.maxHistoryBytes??16777216),this.createStreamId=t.createStreamId??(e=>`proxy_events_${e}_${a.randomUUID().replace(/-/g,"").slice(0,16)}`),this.projectTarget=t.projectTarget,this.unsubscribeBus=e.on(e=>this.accept(e))}stop(){this.unsubscribeBus();for(const e of this.streams.values())e.subscribers.clear();this.streams.clear()}cursor(e){const t=this.stream(e);return{streamId:t.streamId,revision:t.revision,earliestRetainedRevision:su(t)}}subscribe(e,t,n,s=()=>{}){const{bindingId:i,sourceControlPlaneId:r,targetNodeId:o}=e,a=this.stream(o),d=su(a);if(t>a.revision||t<d-1)return n(At.parse({type:"control-plane-proxy.snapshot-required",protocolVersion:Ge,streamId:a.streamId,bindingId:i,sourceControlPlaneId:r,targetNodeId:o,sinceRevision:t,latestRevision:a.revision,earliestRetainedRevision:d,error:{code:ot.SnapshotRequired,message:"Proxy target event history cannot continue from the requested revision; fetch a new snapshot.",retryable:!0,details:{targetNodeId:o}}})),{kind:"snapshot-required",close:()=>{}};n(St.parse({type:"control-plane-proxy.events.ready",protocolVersion:Ge,streamId:a.streamId,bindingId:i,sourceControlPlaneId:r,targetNodeId:o,latestRevision:a.revision,earliestRetainedRevision:d}));for(const e of a.history)e.event.revision>t&&n(iu(e.event,i,r));const c={bindingId:i,sourceControlPlaneId:r,send:n,onDeliveryFailure:s};a.subscribers.add(c);let l=!1;return{kind:"subscribed",close:()=>{l||(l=!0,a.subscribers.delete(c))}}}diagnostics(e){const t=this.stream(e);return{targetNodeId:e,streamId:t.streamId,revision:t.revision,earliestRetainedRevision:su(t),historySize:t.history.length,historyBytes:t.historyBytes,subscribers:t.subscribers.size,subscriberBindings:[...t.subscribers].map(({bindingId:e,sourceControlPlaneId:t})=>({bindingId:e,sourceControlPlaneId:t}))}}accept(e){const t=e.scope?.nodeId;if("string"!=typeof t||!t)return;let n;try{n=this.projectTarget(t)}catch{return}if(!n)return;const s=this.stream(t),i=s.revision+1,r=wt.safeParse({type:"control-plane-proxy.event",protocolVersion:Ge,streamId:s.streamId,bindingId:"proxy_projection",sourceControlPlaneId:"proxy_projection",targetNodeId:t,revision:i,source:{id:e.id,seq:e.seq},target:n,event:{type:e.type,topic:e.topic,createdAt:e.createdAt}});if(!r.success)return;const{bindingId:o,sourceControlPlaneId:a,...d}=r.data,c=ru(d);if(!(void 0===c||c>this.maxEventBytes-512)){for(s.revision=i,s.history.push({event:d,bytes:c}),s.historyBytes+=c;s.history.length>this.historyLimit||s.historyBytes>this.maxHistoryBytes;){const e=s.history.shift();e&&(s.historyBytes-=e.bytes)}for(const e of[...s.subscribers])try{const t=iu(d,e.bindingId,e.sourceControlPlaneId),n=ru(t);if(void 0===n||n>this.maxEventBytes)throw new Error("Proxy event byte limit exceeded.");e.send(t)}catch{s.subscribers.delete(e);try{e.onDeliveryFailure()}catch{}}}}stream(e){const t=this.streams.get(e);if(t)return t;const n={streamId:this.createStreamId(e),revision:0,history:[],historyBytes:0,subscribers:new Set};return this.streams.set(e,n),n}}function su(e){return e.history[0]?.event.revision??e.revision+1}function iu(e,t,n){return wt.parse({...e,bindingId:t,sourceControlPlaneId:n})}function ru(e){try{return Buffer.byteLength(JSON.stringify(e),"utf8")}catch{return}}function ou(e,t){const n=new WeakSet;let s=t,i=0;const r=e=>{if(s-=Buffer.byteLength(e,"utf8"),s<0)throw au("Proxy target projection byte limit exceeded.",{resource:"target-projection-bytes",limit:t})},o=(e,t)=>{if(i+=1,i>16384||t>24)throw au("Proxy target projection complexity limit exceeded.");if(null===e||"boolean"==typeof e)return e;if("string"==typeof e)return r(e),e;if("number"==typeof e)return Number.isFinite(e)?e:null;if("object"!=typeof e)return;if(n.has(e))throw au("Proxy target projection contains a cycle.",void 0,!1);if(n.add(e),Array.isArray(e)){const s=e.map(e=>o(e,t+1)??null);return n.delete(e),s}const s={};for(const[n,i]of Object.entries(e)){if("__proto__"===n||"constructor"===n||"prototype"===n)continue;r(n);const e=o(i,t+1);void 0!==e&&(s[n]=e)}return n.delete(e),s};return o(e,0)}function au(e,t,n=!0){return Object.assign(new Error(e),{code:"CONTROL_PLANE_PROXY_RESOURCE_LIMIT",statusCode:429,retryable:n,...t?{details:t}:{}})}function du(e){const{manageable:t,...n}=e;return n}const cu=o.z.object({bindingId:o.z.string().trim().min(1).max(160)}).passthrough(),lu=o.z.object({route:Ze}).strict(),pu=o.z.object({sinceRevision:o.z.coerce.number().int().nonnegative()}).strict(),uu=new Set(["connection","cookie","host","keep-alive","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),gu=524288;function mu(e,t,n){const s=wu(n.headers,nt);if(s!==Ge)throw vu(ot.ProtocolUnsupported,"Control-plane proxy protocol version is unsupported.",426,!1,{requestedVersion:s,supportedVersion:Ge});const i=wu(n.headers,st),r=wu(n.headers,it),o=wu(n.headers,rt);if(!i||!r||!o)throw vu(ot.AuthenticationFailed,"Proxy binding authentication headers are incomplete.",401);return e.authenticateBinding(t,{sourceControlPlaneId:i,bindingKeyId:r,credential:o})}function hu(e,t,n){const s=mu(e,t.id,n);if(s.id!==t.id||s.revision!==t.revision||s.sourceControlPlaneId!==t.sourceControlPlaneId||s.targetNodeId!==t.targetNodeId||s.bindingKeyId!==t.bindingKeyId||"active"!==s.status)throw vu(ot.BindingIdentityConflict,"Proxy binding changed while the request was being established.",409,!0,{bindingId:t.id,expectedRevision:t.revision,actualRevision:s.revision})}function fu(e,t){if(t.id!==e.targetNodeId)throw vu(ot.TargetMismatch,"Proxy target resolver returned another node.",409,!1,{bindingId:e.id,targetNodeId:e.targetNodeId})}function yu(e){for(const t of Object.keys(e)){const e=t.toLowerCase();if(e.startsWith("x-taskhandoff-node-")||e.startsWith("x-task-handoff-node-"))throw vu(ot.HeaderInvalid,"Caller-supplied node transport authentication headers are forbidden.",400,!1,{header:e})}const t=Object.fromEntries([...et].flatMap(t=>{if(uu.has(t))return[];const n=e[t];return void 0===n?[]:[[t,Array.isArray(n)?n.join(", "):String(n)]]}));if(void 0!==t["x-request-id"]){const e=Ye.safeParse(t["x-request-id"]);if(!e.success)throw vu(ot.HeaderInvalid,"Proxy request correlation id is invalid.",400,!1,{header:"x-request-id"});t["x-request-id"]=e.data}return t}function Iu(e,t,n){const s=bu(t),i=Su(n?.headers);return i&&e.header("x-request-id",i),e.code(s.statusCode).send({error:{code:s.code,message:s.message,retryable:s.retryable,...s.details?{details:s.details}:{}}})}function bu(e){const t=e&&"object"==typeof e?e:{},n="number"==typeof t.statusCode?t.statusCode:502;return{statusCode:n,code:("string"==typeof t.code?t.code:void 0)||ot.TransportFailed,message:e instanceof Error?e.message:"Control-plane proxy transport failed.",retryable:"boolean"==typeof t.retryable?t.retryable:n>=500,details:t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?t.details:void 0}}function vu(e,t,n,s=!1,i){return Object.assign(new Error(t),{code:e,statusCode:n,retryable:s,...i?{details:i}:{}})}function wu(e,t){const n=e[t];return Array.isArray(n)?n.join(","):"string"==typeof n?n:""}function Su(e){if(!e)return;const t=Ye.safeParse(wu(e,"x-request-id"));return t.success?t.data:void 0}function Au(e){let t="";for(const n of e){if(Buffer.byteLength(t+n)>123)break;t+=n}return t}const ku=e=>dt.parse({code:ot.Unavailable,message:e,retryable:!0});class Cu{service;subscriptions=new Map;fetchImpl;openWebSocket;reconnectDelayMs;onStateChanged;logger;running=!1;constructor(e,t={}){this.service=e,this.fetchImpl=t.fetchImpl??fetch,this.openWebSocket=t.openWebSocket??((e,t)=>new l.WebSocket(e,{headers:t})),this.reconnectDelayMs=Math.max(0,t.reconnectDelayMs??2e3),this.onStateChanged=t.onStateChanged??(()=>{}),this.logger=t.logger}start(){this.running||(this.running=!0,this.syncNow())}syncNow(){if(!this.running)return;const e=new Map(this.service.listNodes().filter(e=>"control-plane-proxy"===e.connectionMode&&!1!==e.connectionEnabled).map(e=>[e.id,e]));for(const t of this.subscriptions.keys())e.has(t)||this.remove(t);for(const t of e.values()){const e=this.subscriptionIdentity(t),n=this.subscriptions.get(t.id);if(n?.identity===e)continue;n&&this.remove(t.id);const s={generation:1,identity:e};this.subscriptions.set(t.id,s),this.connect(t.id,s.generation)}}stop(){this.running=!1;for(const e of[...this.subscriptions.keys()])this.remove(e)}remove(e){const t=this.subscriptions.get(e);t&&(t.generation+=1,t.reconnect&&clearTimeout(t.reconnect),t.socket?.close(),this.subscriptions.delete(e))}async connect(e,t){if(!this.current(e,t))return;const n=this.service.proxyPrivateStore.nodeCredential(e);if(n)try{const s=await this.fetchImpl(function(e){return new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/snapshot`,e.proxyOrigin)}(n),{headers:Rr(n)});if(!this.current(e,t))return;const i=await s.json().catch(()=>{});if(!s.ok){const n=dt.safeParse(i?.error),r=n.success?n.data:ku(`Proxy snapshot failed with HTTP ${s.status}.`);return r.code===ot.BindingRevoked?void this.onStateChanged(this.service.markProxyBindingRevoked(e,r)):void this.fail(e,t,r,r.retryable)}const r=vt.parse(i?.data);if(this.validateIdentity(n,r.binding.id,r.binding.targetNodeId),this.onStateChanged(this.service.applyProxyTargetSnapshot(e,r)),"revoked"===r.binding.status)return void this.onStateChanged(this.service.markProxyBindingRevoked(e,{code:ot.BindingRevoked,message:"Control-plane proxy binding was revoked.",retryable:!1}));this.openEvents(e,t,n,r)}catch(n){if(!this.current(e,t))return;this.logger?.warn?.({nodeId:e,error:xu(n)},"control-plane proxy state bootstrap failed"),this.fail(e,t,ku("Trusted control-plane proxy is unavailable."),!0)}else this.fail(e,t,ku("Control-plane proxy credential is unavailable."),!1)}openEvents(e,t,n,s){const i=this.current(e,t);if(!i)return;const r=this.openWebSocket(function(e,t){const n=new URL(`/api/node-proxy/bindings/${encodeURIComponent(e.proxyBindingId)}/events`,e.proxyOrigin);return n.protocol="wss:",n.searchParams.set("sinceRevision",String(t)),n.toString()}(n,s.revision),Rr(n));i.socket=r;let o=!1;const a=n=>{o||(o=!0,this.fail(e,t,ku(n),!0))};r.on("message",i=>{if(this.current(e,t))try{const r=kt.parse(JSON.parse(String(i)));if(r.bindingId!==n.proxyBindingId||r.sourceControlPlaneId!==n.sourceControlPlaneId||r.targetNodeId!==n.targetNodeId||r.streamId!==s.streamId)throw new Error("Proxy event stream identity changed.");if("control-plane-proxy.snapshot-required"===r.type)return void this.restart(e,t);if("control-plane-proxy.events.ready"===r.type)return;this.onStateChanged(this.service.applyProxyTargetEvent(e,r))}catch(n){this.logger?.warn?.({nodeId:e,error:xu(n)},"control-plane proxy event requires a new snapshot"),this.restart(e,t)}}),r.on("error",()=>a("Control-plane proxy event stream failed.")),r.on("close",()=>a("Control-plane proxy event stream disconnected."))}restart(e,t){const n=this.current(e,t);n&&(n.generation+=1,n.socket?.close(),n.socket=void 0,this.connect(e,n.generation))}fail(e,t,n,s){const i=this.current(e,t);if(i){if(this.onStateChanged(this.service.markProxyUnavailable(e,n)),!s)return i.generation+=1,i.socket?.close(),void this.subscriptions.delete(e);i.generation+=1,i.socket?.close(),i.socket=void 0,i.reconnect=setTimeout(()=>{i.reconnect=void 0,this.connect(e,i.generation)},this.reconnectDelayMs),i.reconnect.unref?.()}}current(e,t){const n=this.subscriptions.get(e);return this.running&&n?.generation===t?n:void 0}validateIdentity(e,t,n){if(t!==e.proxyBindingId||n!==e.targetNodeId){const e=new Error("Proxy snapshot identity does not match the private binding credential.");throw Object.assign(e,{code:ot.BindingIdentityConflict}),e}}subscriptionIdentity(e){const t=this.service.proxyPrivateStore.nodeCredential(e.id);return JSON.stringify({connectionMode:e.connectionMode,connectionPath:e.connectionPath,credential:t&&{id:t.id,proxyOrigin:t.proxyOrigin,proxyBindingId:t.proxyBindingId,targetNodeId:t.targetNodeId,sourceControlPlaneId:t.sourceControlPlaneId,bindingKeyId:t.bindingKeyId,updatedAt:t.updatedAt}})}}function xu(e){const t=e&&"object"==typeof e?e:{};return{name:e instanceof Error?e.name:"Error",message:e instanceof Error?e.message:String(e),..."string"==typeof t.code?{code:t.code}:{}}}const zu=o.z.object({id:o.z.string().trim().min(1).max(160)}).strict(),Tu=o.z.object({}).strict(),Nu=o.z.object({code:o.z.string().trim().min(1),message:o.z.string(),details:o.z.record(o.z.string(),o.z.unknown()).optional(),retryable:o.z.boolean().optional()}).strict(),Eu=o.z.object({error:Nu}).strict();function _u(e){const t=process.env[e]?.trim();return t||void 0}const Pu=function(e,t=process.env,n=e){let s;return()=>{const i=t.TASK_HANDOFF_VERSION?.trim();return i||(s||=w(e,t,n),s)}}("@task-handoff/control-plane");function Ou(){return{component:"control-plane",packageName:"@task-handoff/control-plane",packageVersion:Pu(),protocolVersion:Tt,buildId:_u("TASK_HANDOFF_BUILD_ID"),builtAt:_u("TASK_HANDOFF_BUILT_AT"),gitCommit:_u("TASK_HANDOFF_GIT_COMMIT"),imageRef:_u("TASK_HANDOFF_IMAGE_REF"),imageDigest:_u("TASK_HANDOFF_IMAGE_DIGEST")}}const Ru={".css":"text/css; charset=utf-8",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".map":"application/json; charset=utf-8",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"};function ju(e,s){const i=t.resolve(e),r=t.resolve(i,s);if(r===i||r.startsWith(`${i}${t.sep}`))return n.existsSync(r)&&n.statSync(r).isFile()?r:void 0}function Du(e,s,i){const r=ju(s,i);return r?(e.header("cache-control","no-store"),e.type(Ru[t.extname(r).toLowerCase()]||"application/octet-stream"),e.send(n.createReadStream(r))):e.code(404).send({error:{code:"STATIC_FILE_NOT_FOUND",message:`${i} was not found in the Control Plane UI static directory.`}})}function Mu(e){if(e instanceof o.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:"CONTROL_PLANE_ERROR",message:e instanceof Error?e.message:String(e),...t.details&&"object"==typeof t.details&&!Array.isArray(t.details)?{details:t.details}:{},..."boolean"==typeof t.retryable?{retryable:t.retryable}:{},..."number"==typeof t.retryAfterSeconds?{retryAfterSeconds:t.retryAfterSeconds}:{}}}async function qu(e,t){if(!e.enabled())return{type:"system",reason:"auth-disabled"};const n=await e.userForSessionToken(t);return n?{type:"user",userId:n.id,role:n.role}:void 0}const $u=new Set(["/api/auth/session","/api/auth/bootstrap-admin","/api/auth/login","/api/auth/logout","/api/health","/api/events"]);function Lu(e,t){const n=t.split("?")[0]||"/";if(!n.startsWith("/api/"))return;if($u.has(n))return;const s=function(e){return"GET"===e||"HEAD"===e?"read":"POST"===e?"create":"PATCH"===e||"PUT"===e?"update":"DELETE"===e?"delete":"read"}(e);return n.startsWith("/api/control-plane/settings")?{action:"GET"===e?"read":"manage-settings",resource:{type:"control-plane-settings"}}:n.startsWith("/api/models")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"model"}}:n.startsWith("/api/chat-gateway/bridges")?{action:"GET"===e?"read":"manage-secrets",resource:{type:"chat-bridge"}}:"/api/chat-gateway/messages"===n||"/api/chat-gateway/actions"===n?{action:"send-message",resource:{type:"ai-session"}}:"/api/chat-gateway/poll-ai-sessions"===n?{action:"manage-settings",resource:{type:"chat-bridge"}}:"/api/ai-session-attachments"===n?{action:"send-message",resource:{type:"ai-session"}}:"/api/node-join/invites"===n?{action:"manage-node-auth",resource:{type:"node"}}:n.startsWith("/api/control-plane-proxy")?{action:"GET"===e?"read":"manage-node-auth",resource:{type:"node"}}:n.startsWith("/api/nodes")?/\/(runtimes|local-folders)(\/|$)/.test(n)?{action:s,resource:{type:"runtime"}}:{action:"GET"===e?"read":"manage-node-auth",resource:{type:"node"}}:n.startsWith("/api/controlled-instances")?n.includes("/ai-sessions/")?n.includes("/triggers")?{action:s,resource:{type:"trigger"}}:n.endsWith("/approval")?{action:"approve",resource:{type:"ai-session"}}:n.endsWith("/interrupt")?{action:"interrupt",resource:{type:"ai-session"}}:n.endsWith("/resume")||n.endsWith("/commands")||n.includes("/messages")||n.includes("/queue/")?{action:"send-message",resource:{type:"ai-session"}}:n.endsWith("/read")||n.endsWith("/mentions/files")?{action:"read",resource:{type:"ai-session"}}:{action:s,resource:{type:"ai-session"}}:n.includes("/start")?{action:"start",resource:{type:"instance"}}:n.includes("/stop")?{action:"stop",resource:{type:"instance"}}:n.includes("/restart")?{action:"restart",resource:{type:"instance"}}:/\/config-sync(?:\/|$)/.test(n)?{action:"update",resource:{type:"instance"}}:{action:s,resource:{type:"instance"}}:n.startsWith("/api/projects")?{action:s,resource:{type:"project"}}:n.startsWith("/api/images")||n.startsWith("/api/image-options")||n.startsWith("/api/market")?{action:s,resource:{type:"runtime"}}:n.startsWith("/api/triggers")?{action:s,resource:{type:"trigger"}}:{action:s,resource:{type:"control-plane-settings"}}}async function Bu(e={}){const o=Sl(e.dataDir),a=s({logger:e.logger??!0}),d=new id(o,{...e.service,logger:a.log}),c=new ki(o,e.auth),l=new Xc,p=new jp(l),u=new Np(o,{onChanged:e=>queueMicrotask(()=>l.publish(N,e,{topic:"ai.sessions",scope:{instanceId:e.instanceId,sessionId:e.sessionId}}))});u.init();const m=new zp({bootstrap:()=>d.bootstrapAiSessionsFromInstances(),logger:a.log,recoverDelta:(e,t,n)=>d.recoverAiSessionDelta(e,t,n),recoverSnapshot:e=>d.recoverAiSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});m.onSnapshot(e=>u.reconcile(e.instanceId,e.aiSessions));const h=new Op({bootstrap:()=>d.bootstrapAppSessionsFromInstances(),logger:a.log,recoverDelta:(e,t,n)=>d.recoverAppSessionDelta(e,t,n),recoverSnapshot:e=>d.recoverAppSessionSnapshot(e),onRecoveredEvent:e=>l.publish(e.type,e.payload)});d.setAppSessionSnapshotProvider(e=>h.list(e)),d.setAiSessionSnapshotProvider(e=>m.list(e)),l.on(e=>{if(p.handle(e),"instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";t&&(h.removeInstance(t),m.removeInstance(t),u.removeInstance(t))}});const f=new nl,y=new vl(l,{validateInstanceScope:(e,t)=>d.nodeOwnsInstance(e,t),onStreamsHello:(e,t)=>{for(const n of t.streams)"ai.sessions"===n.topic&&m.advertiseStream(e,n),"app.sessions"===n.topic&&h.advertiseStream(e,n)},onSessionEvent:e=>e.type.startsWith("app-session.")?h.handleEvent(e):m.handleEvent(e)}),I=e.proxyOrigin||process.env.TASK_HANDOFF_CONTROL_PLANE_PROXY_ORIGIN,b=e=>{const t=d.nodes.get(e);return t?function(e,t){const n="reverse-wss"===e.connectionMode&&t,s=n?"online":e.status;return{id:e.id,name:e.name||e.id,status:s,health:e.health,...e.lastSeenAt?{lastSeenAt:e.lastSeenAt}:{},capabilities:ou(e.capabilities||{},262144),manageable:!1!==e.connectionEnabled&&(n||"online"===s)}}(t,y.connected(t.id)):void 0},v=new Xp(new Gp(o.proxyAuthorityPath,(e,t)=>a.log.warn(t,e)),{get:b},{proxyOrigin:I,proxyOriginProvider:()=>d.getSettings().publicBaseUrl}),w=new eu,S=new nu(l,{projectTarget:e=>{const t=b(e);return t?du(t):void 0}}),A=new Cu(d,{fetchImpl:e.service?.fetchImpl,logger:a.log,onStateChanged:e=>l.publish("node.proxy-state.updated",{nodeId:e.id,proxyState:e.proxyState},{topic:"node.state",scope:{nodeId:e.id}})});let k,C;l.on(e=>{if("node.created"!==e.type&&"node.updated"!==e.type&&"node.deleted"!==e.type||A.syncNow(),"instance.created"===e.type||"instance.updated"===e.type||"instance.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"instanceId"in e.payload?String(e.payload.instanceId||""):"";t&&y.invalidateInstanceScope({instanceId:t})}if("node.deleted"===e.type){const t=e.payload&&"object"==typeof e.payload&&"nodeId"in e.payload?String(e.payload.nodeId||""):"";if(t){y.invalidateInstanceScope({nodeId:t});const e=v.revokeTarget(t);for(const n of e.bindings)w.closeBinding(n.id,"Proxy target was deleted."),l.publish("control-plane-proxy.binding.updated",{binding:n},{topic:"control-plane-proxy",scope:{nodeId:t}})}}}),d.setNodeAgentTransport(y),d.init();const x=()=>{if(k)return k;const e=d.recoverPendingPairingRevokes().then(()=>{});return k=e,e.then(()=>{k===e&&(k=void 0)},()=>{k===e&&(k=void 0)}),e};await x().catch(e=>a.log.warn({error:e},"pending node pairing recovery failed")),v.init(),A.start(),await d.syncLocalNodeConnection().catch(()=>{}),c.init();const z=new wc(d,e.service?.fetchImpl,{aiSessions:m,logger:a.log});z.startEnabled();const T=new wl(d,y,{safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0,logger:a.log});T.start(),await a.register(i),await a.register(r),a.addHook("onClose",async()=>{C&&clearInterval(C),await(k?.catch(()=>{})),p.close(),S.stop(),A.stop(),T.stop(),z.stopAll()});const E=(_=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(_||"").trim().toLowerCase()));var _;a.setErrorHandler((e,t,n)=>{const s=Mu(e);void 0!==s.retryAfterSeconds&&n.header("retry-after",String(s.retryAfterSeconds)),(E||s.statusCode>=500)&&(s.statusCode>=500?a.log.error.bind(a.log):a.log.warn.bind(a.log))({method:t.method,url:t.url,statusCode:s.statusCode,errorCode:s.code,error:s.message},"control plane request failed"),n.code(s.statusCode).send(Eu.parse({error:{code:s.code,message:s.message,...s.details?{details:s.details}:{},...void 0!==s.retryable?{retryable:s.retryable}:{}}}))}),a.addHook("preHandler",async(e,t)=>{const n=e.routeOptions.url,s=n&&"*"!==n?n:e.url,i=e.routeOptions.config.controlPlaneAuthBoundary;if("proxy-binding"===i||"node-tunnel"===i)return;const r="public"===i||"public-ui"===i&&function(e){const t=e.split("?")[0]||"/";return!t.startsWith("/api/")&&"/api"!==t&&!t.startsWith("/instances/")&&"/instances"!==t}(e.url);if(!c.enabled()||r){const t=await qu(c,e.cookies[di]),n=Lu(e.method,s);return void(n&&Bl(t||{type:"system",reason:"auth-disabled"},n.action,n.resource))}const o=await qu(c,e.cookies[di]);if(!o)return t.code(401).send({error:{code:"CONTROL_PLANE_AUTH_REQUIRED",message:"Sign in to access the Control Plane."}});const a=Lu(e.method,s);a&&Bl(o,a.action,a.resource)});const P=t.resolve(e.staticDir||process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(process.cwd(),"packages","control-plane-ui","dist"));return n.existsSync(P)&&(a.get("/assets/*",{config:sl},async(e,n)=>{const s=e.params["*"];return Du(n,P,t.join("assets",s||""))}),a.get("/favicon.ico",{config:sl},async(e,t)=>ju(P,"favicon.ico")?Du(t,P,"favicon.ico"):t.code(204).send())),a.get("/api/health",{config:sl},async()=>({data:{ok:!0,role:"control-plane",protocolVersion:Tt,build:Ou(),dataDir:o.dataDir,serverTime:(new Date).toISOString()}})),a.get("/api/session-streams/diagnostics",async()=>({data:{aiSessions:m.diagnostics(),aiSessionActions:d.aiSessionActionDiagnostics(),appSessions:h.diagnostics(),nodeConnections:T.diagnostics()}})),a.get("/install-node-agent.sh",{config:sl},async(e,t)=>(t.header("cache-control","no-store"),t.type("text/x-shellscript; charset=utf-8"),Rp`#!/bin/sh
|
|
2
2
|
set -eu
|
|
3
3
|
|
|
4
4
|
CONTROL_PLANE_URL=""
|
|
@@ -220,4 +220,4 @@ fi
|
|
|
220
220
|
echo "TaskHandoff node-agent is installed and running."
|
|
221
221
|
echo "Service: task-handoff-node-agent.service"
|
|
222
222
|
echo "Pairing token: sudo task-handoff-node-agent invite --ipc-path $IPC_PATH"
|
|
223
|
-
`)),o.get("/api/events",{websocket:!0},async e=>{const t=[];let n=!1;const s={get readyState(){return e.readyState},OPEN:e.OPEN,send(s){n?e.send(s):t.push(s)},on(t,n){e.on(t,n)}};l.connect(s);const[i,r]=await Promise.all([m.streamDescriptors(),h.streamDescriptors()]);l.send(e,"streams.hello",{protocolVersion:1,streams:[...i,...r]}),n=!0;for(const n of t)e.send(n);for(const t of p.snapshots())l.send(e,"image.pull.snapshot",t,{topic:"instances",scope:{instanceId:t.instanceId}})}),o.get("/api/auth/session",async e=>({data:await c.currentSession(e.cookies[oi])})),o.post("/api/auth/bootstrap-admin",async(e,t)=>{const n=await c.bootstrapAdmin(e.body);return t.code(201).send({data:n})}),o.post("/api/auth/login",async(e,t)=>{const n=await c.login(e.body);return t.setCookie(oi,n.sessionToken,{path:"/",httpOnly:!0,sameSite:"lax",expires:new Date(n.expiresAt)}),{data:{user:n.user}}}),o.post("/api/auth/logout",async(e,t)=>{const n=c.logout(e.cookies[oi]);return t.clearCookie(oi,{path:"/"}),{data:n}}),o.get("/api/control-plane/status",async()=>({data:{protocolVersion:zt,build:Up(),storage:a}})),o.get("/api/control-plane/settings",async()=>({data:d.getSettings()})),o.patch("/api/control-plane/settings",async e=>({data:d.updateSettings(e.body||{})})),o.post("/api/ai-session-attachments",{bodyLimit:33554432},async(e,t)=>{try{return t.code(201).send({data:y.upload(e.body)})}catch(e){return t.code(400).send({error:{code:"AI_SESSION_ATTACHMENT_INVALID",message:e instanceof Error?e.message:String(e)}})}}),Sl({app:o,service:d,events:l,appSessionAggregator:h,aiSessionAggregator:m,aiSessionUnread:u,chatGateway:k,aiSessionAttachments:y,nodeAgentTunnel:f,nodeEventSubscriber:C,errorPayload:Gp}),function(e){const{app:t,authority:n,resolveTarget:s,projectTarget:i,eventHub:r}=e,a=e.runtime??new cp,o=new WeakMap,d=(e,t)=>{const n=o.get(e);n&&(o.delete(e),n.tracked.release(),e.raw.removeListener("aborted",n.abort),t.raw.removeListener("close",n.abort))};t.all("/api/node-proxy/bindings/:bindingId/http/*",{bodyLimit:a.maxRequestBodyBytes,logLevel:"silent",async onRequest(e,t){try{const s=Ip.parse(e.params).bindingId,i=Ap(n,s,e),r=function(e){const t=e.raw.url||e.url,n=/^\/api\/node-proxy\/bindings\/[^/]+\/http(\/[^#]*)$/.exec(t);if(!n||/%(?:2f|5c)/i.test(n[1]))throw Np(at.RouteInvalid,"Proxy route is invalid.",400);const s=Ze.safeParse(n[1]);if(!s.success)throw Np(at.RouteInvalid,s.error.issues[0]?.message||"Proxy route is invalid.",400);return s.data}(e),c=xp(e.headers),l=a.reserveHttp(i.id),p=()=>{l.controller.abort(Object.assign(new Error("Proxy HTTP consumer disconnected."),{name:"AbortError",code:"ABORT_ERR"})),d(e,t)};o.set(e,{binding:i,route:r,forwardedHeaders:c,tracked:l,abort:p}),e.raw.once("aborted",p),t.raw.once("close",p)}catch(n){return Tp(t,n,e)}},async onError(e,t){d(e,t);const n=_p(e.headers);n&&t.header("x-request-id",n)},async onResponse(e,t){d(e,t)}},async(e,t)=>{try{const r=o.get(e);if(!r)throw Np(at.TransportFailed,"Proxy request context is unavailable.",500,!0);const{binding:a,route:c,forwardedHeaders:l,tracked:p,abort:u}=r;!function(e,t,n){const s=new URL(t,"https://node-agent.invalid").searchParams,i=[s.get("nodeId"),s.get("targetNodeId")];if(n&&"object"==typeof n&&!Array.isArray(n)){const e=n;"string"==typeof e.nodeId&&i.push(e.nodeId),"string"==typeof e.targetNodeId&&i.push(e.targetNodeId)}const r=i.find(t=>null!==t&&t!==e.targetNodeId);if(void 0!==r)throw Np(at.TargetMismatch,"Proxy request attempts to select a target outside its binding.",409,!1,{bindingId:e.id,targetNodeId:e.targetNodeId})}(a,c,e.body);const m=null==(i=e.body)?void 0:Buffer.isBuffer(i)?i:i instanceof Uint8Array||"string"==typeof i?Buffer.from(i):Buffer.from(JSON.stringify(i));p.acceptRequestBody(m?.byteLength??0);try{const{node:i,transport:r}=await s(a.targetNodeId);return kp(n,a,e),Cp(a,i),function(e,t,n,s,i,r){t.body&&n.beginResponseStream();for(const[n,s]of Object.entries(function(e,t){const n=Object.fromEntries([...tt].flatMap(t=>{const n=e.headers.get(t);return null===n?[]:[[t,n]]}));return t&&(n["x-request-id"]=t),n}(t,r)))e.header(n,s);const a=()=>{n.release(),i.raw.removeListener("aborted",s),e.raw.removeListener("close",s)};if(!t.body)return a(),e.code(t.status).send();const o=g.Readable.fromWeb(t.body);return n.controller.signal.addEventListener("abort",()=>{const e=n.controller.signal.reason;o.destroy(e instanceof Error?e:new Error("Proxy HTTP stream aborted."))},{once:!0}),o.on("data",e=>{try{n.acceptResponseChunk(e)}catch(e){o.destroy(e)}}),o.once("end",a),o.once("error",a),o.once("close",a),e.raw.once("close",()=>o.destroy()),e.code(t.status).send(o)}(t,await r.requestStream(i,c,{method:e.method,headers:l,...m?{body:m}:{},signal:p.controller.signal}),p,u,e,l["x-request-id"])}catch(n){throw d(e,t),n}}catch(n){return d(e,t),Tp(t,n,e)}var i}),t.get("/api/node-proxy/bindings/:bindingId/websocket",{websocket:!0,logLevel:"silent"},async(e,t)=>{let i;try{const r=Ip.parse(t.params).bindingId,o=Ap(n,r,t),d=bp.parse(t.query).route,{node:c,transport:l}=await s(o.targetNodeId);kp(n,o,t),Cp(o,c),i=a.openWebSocket(o.id,e),l.proxyWebSocket(c,i,d,function(e){const t=Ep(e,"sec-websocket-protocol").split(",").map(e=>e.trim()).filter(Boolean);return t.length?t:void 0}(t.headers),xp(t.headers))}catch(t){const n=zp(t);i?.close(1008,n.message),i||e.close(429===n.statusCode?1013:n.statusCode<500?1008:1011,Pp(n.message))}}),t.get("/api/node-proxy/bindings/:bindingId/snapshot",async(e,t)=>{try{const t=Ip.parse(e.params).bindingId,o=Ap(n,t,e),{node:d}=await s(o.targetNodeId);kp(n,o,e),Cp(o,d);const c=i(o.targetNodeId);if(!c)throw Np(at.TargetUnavailable,"Proxy target no longer exists.",404,!1,{targetNodeId:o.targetNodeId});const l=r.cursor(o.targetNodeId),p=vt.parse({protocolVersion:Ge,binding:o,streamId:l.streamId,revision:l.revision,observedAt:(new Date).toISOString(),target:fp(c)});if(a=p,Buffer.byteLength(JSON.stringify(a),"utf8")>Sp)throw Np(at.ResourceLimit,"Proxy target snapshot byte limit exceeded.",429,!0,{bindingId:o.id,resource:"snapshot-bytes",limit:Sp});return{data:p}}catch(n){return Tp(t,n,e)}var a}),t.get("/api/node-proxy/bindings/:bindingId/events",{websocket:!0,logLevel:"silent"},async(e,t)=>{let i,o,d;try{const c=Ip.parse(t.params).bindingId,l=Ap(n,c,t),p=vp.parse(t.query),{node:u}=await s(l.targetNodeId);kp(n,l,t),Cp(l,u),i=a.openWebSocket(l.id,e,()=>o?.()),i.on("message",()=>i?.close(1008,"Proxy event streams do not accept client messages."));const g=r.subscribe({bindingId:l.id,sourceControlPlaneId:l.sourceControlPlaneId,targetNodeId:l.targetNodeId},p.sinceRevision,e=>{i?.send(JSON.stringify(e))},()=>{i?.close(1011,"Proxy event delivery failed.")});o=g.close,d=a.registerBindingCleanup(l.id,o);const m=()=>{o?.(),d?.()};i.on("close",m),i.on("error",m),"snapshot-required"===g.kind?(o(),i.close(1008,"Proxy target snapshot is required.")):1!==i.readyState&&o()}catch(t){o?.(),d?.();const n=zp(t);i?.close(1008,n.message),i||e.close(429===n.statusCode?1013:n.statusCode<500?1008:1011,Pp(n.message))}}),t.delete("/api/node-proxy/bindings/:bindingId",async(t,s)=>{try{const s=Ip.parse(t.params).bindingId;Ap(n,s,t);const i=n.revokeBinding(s),r=a.closeBinding(s);return e.onBindingRevoked?.(i),{data:{binding:i,closed:r}}}catch(e){return Tp(s,e,t)}})}({app:o,authority:v,resolveTarget:e=>{const t=d.requireNode(e);return{node:t,transport:d.resolveNodeAgentTransport(t)}},projectTarget:b,runtime:w,eventHub:S,onBindingRevoked:e=>l.publish("control-plane-proxy.binding.updated",{binding:e,audit:{action:"binding.revoke",actor:`control-plane:${e.sourceControlPlaneId}`}},{topic:"control-plane-proxy",scope:{nodeId:e.targetNodeId}})}),function(e){const{app:t,service:n,proxy:s,runtime:i,events:r,actorId:a}=e;t.get("/api/control-plane-proxy/invites",async()=>({data:s.listInvites()})),t.post("/api/control-plane-proxy/invites",async(e,t)=>{const n=await a(e),i=s.createInvite(e.body,n);return r.publish("control-plane-proxy.invite.created",{invite:i.invite,audit:{action:"invite.create",actor:n}},{topic:"control-plane-proxy",scope:{nodeId:i.invite.targetNodeId}}),t.code(201).send({data:i})}),t.delete("/api/control-plane-proxy/invites/:id",async e=>{const t=await a(e),n=s.revokeInvite(Dp.parse(e.params).id);return r.publish("control-plane-proxy.invite.updated",{invite:n,audit:{action:"invite.revoke",actor:t}},{topic:"control-plane-proxy",scope:{nodeId:n.targetNodeId}}),{data:n}}),t.get("/api/control-plane-proxy/bindings",async()=>({data:s.listBindings()})),t.delete("/api/control-plane-proxy/bindings/:id",async e=>{const t=await a(e),n=Dp.parse(e.params).id,o=s.revokeBinding(n),d=i.closeBinding(n);return r.publish("control-plane-proxy.binding.updated",{binding:o,audit:{action:"binding.revoke",actor:t}},{topic:"control-plane-proxy",scope:{nodeId:o.targetNodeId}}),{data:{binding:o,closed:d}}}),t.get("/api/control-plane-proxy/diagnostics",async()=>({data:i.diagnostics()})),t.post("/api/node-proxy/claims",async(e,t)=>{const n=s.claimInvite(e.body);return r.publish("control-plane-proxy.binding.created",{binding:n.binding,audit:{action:"binding.claim",actor:`control-plane:${n.binding.sourceControlPlaneId}`}},{topic:"control-plane-proxy",scope:{nodeId:n.binding.targetNodeId}}),t.code(201).send({data:n})}),t.get("/api/control-plane-proxy/pending-claims",async()=>({data:n.listPendingProxyClaims()})),t.post("/api/control-plane-proxy/claims",async(e,t)=>{const s=await n.claimProxyNode(e.body);return r.publish("node.created",{nodeId:s.node.id}),t.code(201).send({data:s})}),t.post("/api/control-plane-proxy/pending-claims/:id/resume",async e=>{const t=Dp.parse(e.params).id;Mp.parse(e.body||{});const s=await n.resumeProxyClaim(t);return r.publish("node.created",{nodeId:s.node.id}),{data:s}}),t.delete("/api/control-plane-proxy/pending-claims/:id",async e=>({data:await n.cancelProxyClaim(Dp.parse(e.params).id)}))}({app:o,service:d,proxy:v,runtime:w,events:l,actorId:async e=>{const t=await Vp(c,e.cookies[oi]);return t?"user"===t.type?`user:${t.userId}`:"system"===t.type?`system:${t.reason}`:`chat-bridge:${t.bridgeId}`:"system:unknown"}}),function({app:e,service:t}){!function(e){for(const t of["application/octet-stream","application/x-www-form-urlencoded"])Ol(e,t);Ol(e,/^multipart\/form-data(?:;.*)?$/i)}(e),e.get("/api/app-access/session",async e=>{const n=Rl(e.url),s="vnc"===e.query.mode?"vnc":"web"===e.query.mode?"web":"tty",i=await t.appAccessProxyTarget(n,s);return{data:{mode:s,instance:{id:i.instance.id,name:i.instance.name,projectId:i.instance.projectId,projectName:"string"==typeof i.instance.sourceSnapshot.name?i.instance.sourceSnapshot.name:void 0},session:i.session,expiresAt:i.access.expiresAt,ttySocketPath:"tty"===s?`/apps/access/tty/ws?token=${encodeURIComponent(n)}`:void 0,vncFramePath:"vnc"===s?jl(n,i.access.sessionId,i.session):void 0}}}),e.get("/apps/access/tty/ws",{websocket:!0},async(e,n)=>{try{const s=await t.appAccessProxyTarget(Rl(n.url),"tty");await t.proxyInstanceWebSocket(s.instance.id,e,s.path,Tl(n.headers),xl(n.headers))}catch{e.close(1011,"TTY access link is invalid or expired.")}}),e.route({method:"GET",url:"/apps/access/vnc/proxy/*",wsHandler:async(e,n)=>{const s=n.params;try{const i=await t.appAccessProxyTarget(Rl(n.url),"vnc",s["*"]||"");await t.proxyInstanceWebSocket(i.instance.id,e,i.path,Tl(n.headers),xl(n.headers))}catch{e.close(1011,"VNC access link is invalid or expired.")}},handler:async(e,n)=>{const s=e.params,i=await t.appAccessProxyTarget(Rl(e.url),"vnc",s["*"]||"");return _l(n,await zl(t,n,i.instance.id,i.path,{method:e.method,headers:Cl(e.headers)}),i.instance.id,i.path)}}),e.all("/instances/:id",{bodyLimit:67108864},async(e,n)=>{const s=e.params;return"GET"===e.method||"HEAD"===e.method?n.redirect(`${El(s.id)}/`):_l(n,await zl(t,n,s.id,"/",{method:e.method,headers:Cl(e.headers),body:Pl(e.body)}),s.id,"/")}),e.route({method:"GET",url:"/instances/:id/*",wsHandler:async(e,n)=>{const s=n.params,i=s["*"]||"",r=n.url.indexOf("?"),a=r>=0?n.url.slice(r):"";try{await t.proxyInstanceWebSocket(s.id,e,`/${i}${a}`,Tl(n.headers),xl(n.headers))}catch{e.close(1011,"Instance websocket endpoint is not reachable.")}},handler:async(e,n)=>{const s=e.params,i=s["*"]||"",r=e.url.indexOf("?"),a=`/${i}${r>=0?e.url.slice(r):""}`;return _l(n,await zl(t,n,s.id,a,{method:e.method,headers:Cl(e.headers)}),s.id,a)}}),e.route({method:["POST","PUT","PATCH","DELETE","OPTIONS"],url:"/instances/:id/*",bodyLimit:67108864,handler:async(e,n)=>{const s=e.params,i=s["*"]||"",r=e.url.indexOf("?"),a=r>=0?e.url.slice(r):"";return Nl(n,await zl(t,n,s.id,`/${i}${a}`,{method:e.method,headers:Cl(e.headers),body:Pl(e.body)}))}})}({app:o,service:d}),o.get("*",async(e,t)=>n.existsSync(z)?Wp(t,z,"index.html"):t.code(404).send({error:{code:"CONTROL_PLANE_UI_NOT_BUILT",message:"Control plane API is running, but the Web UI static directory was not found."}})),o}async function Qp(e){const s=Pc(e.dataDir),i=function(e,s={}){return function(e,s={}){n.mkdirSync(t.dirname(e),{recursive:!0});const i={pid:process.pid,hostname:y.hostname(),component:s.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:o.randomUUID(),startIdentity:$c(process.pid),dataDir:s.dataDir,host:s.host,port:s.port,instanceId:s.instanceId};for(let t=0;t<5;t+=1)try{return n.mkdirSync(e),n.writeFileSync(jc(e),`${JSON.stringify({token:i.token,pid:i.pid,startIdentity:i.startIdentity,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Uc(e,i),{lockPath:e,owner:i,release(){const t=qc(e);t?.token===i.token&&n.rmSync(e,{recursive:!0,force:!0})}}}catch(i){if("EEXIST"!==i.code)throw i;const r=qc(e);if(Bc(r))throw new Oc(e,r,s.error);if(!r&&(Hc(e)||Vc(e)<(s.incompleteLockStaleMs??2e3))){if(t+1<5){Gc();continue}throw new Oc(e,void 0,s.error)}const a=o.randomUUID();if(!Kc(e,a)){if(t+1<5){Gc();continue}throw new Oc(e,qc(e),s.error)}const d=qc(e);if(Bc(d))throw Wc(Dc(e)),new Oc(e,d,s.error);if(r?.token&&d?.token!==r.token){Wc(Dc(e));continue}if(!Qc(e,a))continue;n.rmSync(e,{recursive:!0,force:!0})}const r=qc(e);throw new Oc(e,r,s.error)}(e,{...s,component:"control-plane",error:{label:"Control plane",code:"CONTROL_PLANE_ALREADY_RUNNING"}})}(t.join(y.tmpdir(),`task-handoff-control-plane-${process.getuid?.()??"user"}.lock`),{dataDir:s.dataDir,host:e.host,port:e.port});try{const t=await Jp(e);t.addHook("onClose",async()=>{i.release()}),await t.listen({host:e.host,port:e.port})}catch(e){throw i.release(),e}}function Yp(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 Xp(e){if("disabled"!==e&&"password"!==e)throw new Error("Auth mode must be disabled or password.");return e}(async function(){const n=new e.Command;n.name("task-handoff-control-plane").description("Run the TaskHandoff control plane.").version(w("@task-handoff/cli")).option("--host <host>","Control plane host",process.env.TASK_HANDOFF_CONTROL_PLANE_HOST||"127.0.0.1").option("-p, --port <port>","Control plane port",Yp,Number(process.env.TASK_HANDOFF_CONTROL_PLANE_PORT)||8081).option("--data-dir <path>","Control plane data directory").option("--static-dir <path>","Control plane Web UI static directory",process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(__dirname,"..","ui")).option("--auth-mode <mode>","Control plane auth mode: disabled or password",Xp,process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"password").action(async e=>{await Qp({host:e.host,port:e.port,dataDir:e.dataDir,staticDir:e.staticDir,auth:{mode:e.authMode}})}),await n.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});
|
|
223
|
+
`)),a.get("/api/events",{websocket:!0},async e=>{const t=[];let n=!1;const s={get readyState(){return e.readyState},OPEN:e.OPEN,send(s){n?e.send(s):t.push(s)},on(t,n){e.on(t,n)}};l.connect(s);const[i,r]=await Promise.all([m.streamDescriptors(),h.streamDescriptors()]);l.send(e,"streams.hello",{protocolVersion:1,streams:[...i,...r]}),n=!0;for(const n of t)e.send(n);for(const t of p.snapshots())l.send(e,"image.pull.snapshot",t,{topic:"instances",scope:{instanceId:t.instanceId}})}),a.get("/api/auth/session",{config:sl},async e=>({data:await c.currentSession(e.cookies[di])})),a.post("/api/auth/bootstrap-admin",{config:sl},async(e,t)=>{const n=await c.bootstrapAdmin(e.body);return t.code(201).send({data:n})}),a.post("/api/auth/login",{config:sl},async(e,t)=>{const n=await c.login(e.body,{sourceId:e.ip});return t.setCookie(di,n.sessionToken,{path:"/",httpOnly:!0,sameSite:"lax",expires:new Date(n.expiresAt)}),{data:{user:n.user}}}),a.post("/api/auth/logout",{config:sl},async(e,t)=>{const n=c.logout(e.cookies[di]);return t.clearCookie(di,{path:"/"}),{data:n}}),a.get("/api/control-plane/status",async()=>({data:{protocolVersion:Tt,build:Ou(),storage:o}})),a.get("/api/control-plane/settings",async()=>({data:d.getSettings()})),a.patch("/api/control-plane/settings",async e=>({data:d.updateSettings(e.body||{})})),a.post("/api/ai-session-attachments",{bodyLimit:33554432},async(e,t)=>{try{return t.code(201).send({data:f.upload(e.body)})}catch(e){return t.code(400).send({error:{code:"AI_SESSION_ATTACHMENT_INVALID",message:e instanceof Error?e.message:String(e)}})}}),up({app:a,service:d,events:l,appSessionAggregator:h,aiSessionAggregator:m,aiSessionUnread:u,chatGateway:z,aiSessionAttachments:f,nodeAgentTunnel:y,nodeEventSubscriber:T,errorPayload:Mu}),function(e){const{app:t,authority:n,resolveTarget:s,projectTarget:i,eventHub:r}=e,o=e.runtime??new eu,a=new WeakMap,d=(e,t)=>{const n=a.get(e);n&&(a.delete(e),n.tracked.release(),e.raw.removeListener("aborted",n.abort),t.raw.removeListener("close",n.abort))};t.all("/api/node-proxy/bindings/:bindingId/http/*",{config:rl,bodyLimit:o.maxRequestBodyBytes,logLevel:"silent",async onRequest(e,t){try{const s=cu.parse(e.params).bindingId,i=mu(n,s,e),r=function(e){const t=e.raw.url||e.url,n=/^\/api\/node-proxy\/bindings\/[^/]+\/http(\/[^#]*)$/.exec(t);if(!n||/%(?:2f|5c)/i.test(n[1]))throw vu(ot.RouteInvalid,"Proxy route is invalid.",400);const s=Ze.safeParse(n[1]);if(!s.success)throw vu(ot.RouteInvalid,s.error.issues[0]?.message||"Proxy route is invalid.",400);return s.data}(e),c=yu(e.headers),l=o.reserveHttp(i.id),p=()=>{l.controller.abort(Object.assign(new Error("Proxy HTTP consumer disconnected."),{name:"AbortError",code:"ABORT_ERR"})),d(e,t)};a.set(e,{binding:i,route:r,forwardedHeaders:c,tracked:l,abort:p}),e.raw.once("aborted",p),t.raw.once("close",p)}catch(n){return Iu(t,n,e)}},async onError(e,t){d(e,t);const n=Su(e.headers);n&&t.header("x-request-id",n)},async onResponse(e,t){d(e,t)}},async(e,t)=>{try{const r=a.get(e);if(!r)throw vu(ot.TransportFailed,"Proxy request context is unavailable.",500,!0);const{binding:o,route:c,forwardedHeaders:l,tracked:p,abort:u}=r;!function(e,t,n){const s=new URL(t,"https://node-agent.invalid").searchParams,i=[s.get("nodeId"),s.get("targetNodeId")];if(n&&"object"==typeof n&&!Array.isArray(n)){const e=n;"string"==typeof e.nodeId&&i.push(e.nodeId),"string"==typeof e.targetNodeId&&i.push(e.targetNodeId)}const r=i.find(t=>null!==t&&t!==e.targetNodeId);if(void 0!==r)throw vu(ot.TargetMismatch,"Proxy request attempts to select a target outside its binding.",409,!1,{bindingId:e.id,targetNodeId:e.targetNodeId})}(o,c,e.body);const m=null==(i=e.body)?void 0:Buffer.isBuffer(i)?i:i instanceof Uint8Array||"string"==typeof i?Buffer.from(i):Buffer.from(JSON.stringify(i));p.acceptRequestBody(m?.byteLength??0);try{const{node:i,transport:r}=await s(o.targetNodeId);return hu(n,o,e),fu(o,i),function(e,t,n,s,i,r){t.body&&n.beginResponseStream();for(const[n,s]of Object.entries(function(e,t){const n=Object.fromEntries([...tt].flatMap(t=>{const n=e.headers.get(t);return null===n?[]:[[t,n]]}));return t&&(n["x-request-id"]=t),n}(t,r)))e.header(n,s);const o=()=>{n.release(),i.raw.removeListener("aborted",s),e.raw.removeListener("close",s)};if(!t.body)return o(),e.code(t.status).send();const a=g.Readable.fromWeb(t.body);return n.controller.signal.addEventListener("abort",()=>{const e=n.controller.signal.reason;a.destroy(e instanceof Error?e:new Error("Proxy HTTP stream aborted."))},{once:!0}),a.on("data",e=>{try{n.acceptResponseChunk(e)}catch(e){a.destroy(e)}}),a.once("end",o),a.once("error",o),a.once("close",o),e.raw.once("close",()=>a.destroy()),e.code(t.status).send(a)}(t,await r.requestStream(i,c,{method:e.method,headers:l,...m?{body:m}:{},signal:p.controller.signal}),p,u,e,l["x-request-id"])}catch(n){throw d(e,t),n}}catch(n){return d(e,t),Iu(t,n,e)}var i}),t.get("/api/node-proxy/bindings/:bindingId/websocket",{websocket:!0,logLevel:"silent",config:rl},async(e,t)=>{let i;try{const r=cu.parse(t.params).bindingId,a=mu(n,r,t),d=lu.parse(t.query).route,{node:c,transport:l}=await s(a.targetNodeId);hu(n,a,t),fu(a,c),i=o.openWebSocket(a.id,e),l.proxyWebSocket(c,i,d,function(e){const t=wu(e,"sec-websocket-protocol").split(",").map(e=>e.trim()).filter(Boolean);return t.length?t:void 0}(t.headers),yu(t.headers))}catch(t){const n=bu(t);i?.close(1008,n.message),i||e.close(429===n.statusCode?1013:n.statusCode<500?1008:1011,Au(n.message))}}),t.get("/api/node-proxy/bindings/:bindingId/snapshot",{config:rl},async(e,t)=>{try{const t=cu.parse(e.params).bindingId,a=mu(n,t,e),{node:d}=await s(a.targetNodeId);hu(n,a,e),fu(a,d);const c=i(a.targetNodeId);if(!c)throw vu(ot.TargetUnavailable,"Proxy target no longer exists.",404,!1,{targetNodeId:a.targetNodeId});const l=r.cursor(a.targetNodeId),p=vt.parse({protocolVersion:Ge,binding:a,streamId:l.streamId,revision:l.revision,observedAt:(new Date).toISOString(),target:du(c)});if(o=p,Buffer.byteLength(JSON.stringify(o),"utf8")>gu)throw vu(ot.ResourceLimit,"Proxy target snapshot byte limit exceeded.",429,!0,{bindingId:a.id,resource:"snapshot-bytes",limit:gu});return{data:p}}catch(n){return Iu(t,n,e)}var o}),t.get("/api/node-proxy/bindings/:bindingId/events",{websocket:!0,logLevel:"silent",config:rl},async(e,t)=>{let i,a,d;try{const c=cu.parse(t.params).bindingId,l=mu(n,c,t),p=pu.parse(t.query),{node:u}=await s(l.targetNodeId);hu(n,l,t),fu(l,u),i=o.openWebSocket(l.id,e,()=>a?.()),i.on("message",()=>i?.close(1008,"Proxy event streams do not accept client messages."));const g=r.subscribe({bindingId:l.id,sourceControlPlaneId:l.sourceControlPlaneId,targetNodeId:l.targetNodeId},p.sinceRevision,e=>{i?.send(JSON.stringify(e))},()=>{i?.close(1011,"Proxy event delivery failed.")});a=g.close,d=o.registerBindingCleanup(l.id,a);const m=()=>{a?.(),d?.()};i.on("close",m),i.on("error",m),"snapshot-required"===g.kind?(a(),i.close(1008,"Proxy target snapshot is required.")):1!==i.readyState&&a()}catch(t){a?.(),d?.();const n=bu(t);i?.close(1008,n.message),i||e.close(429===n.statusCode?1013:n.statusCode<500?1008:1011,Au(n.message))}}),t.delete("/api/node-proxy/bindings/:bindingId",{config:rl},async(t,s)=>{try{const s=cu.parse(t.params).bindingId;mu(n,s,t);const i=n.revokeBinding(s),r=o.closeBinding(s);return e.onBindingRevoked?.(i),{data:{binding:i,closed:r}}}catch(e){return Iu(s,e,t)}})}({app:a,authority:v,resolveTarget:e=>{const t=d.requireNode(e);return{node:t,transport:d.resolveNodeAgentTransport(t)}},projectTarget:b,runtime:w,eventHub:S,onBindingRevoked:e=>l.publish("control-plane-proxy.binding.updated",{binding:e,audit:{action:"binding.revoke",actor:`control-plane:${e.sourceControlPlaneId}`}},{topic:"control-plane-proxy",scope:{nodeId:e.targetNodeId}})}),function(e){const{app:t,service:n,proxy:s,runtime:i,events:r,actorId:o}=e;t.get("/api/control-plane-proxy/invites",async()=>({data:s.listInvites()})),t.post("/api/control-plane-proxy/invites",async(e,t)=>{const n=await o(e),i=s.createInvite(e.body,n);return r.publish("control-plane-proxy.invite.created",{invite:i.invite,audit:{action:"invite.create",actor:n}},{topic:"control-plane-proxy",scope:{nodeId:i.invite.targetNodeId}}),t.code(201).send({data:i})}),t.delete("/api/control-plane-proxy/invites/:id",async e=>{const t=await o(e),n=s.revokeInvite(zu.parse(e.params).id);return r.publish("control-plane-proxy.invite.updated",{invite:n,audit:{action:"invite.revoke",actor:t}},{topic:"control-plane-proxy",scope:{nodeId:n.targetNodeId}}),{data:n}}),t.get("/api/control-plane-proxy/bindings",async()=>({data:s.listBindings()})),t.delete("/api/control-plane-proxy/bindings/:id",async e=>{const t=await o(e),n=zu.parse(e.params).id,a=s.revokeBinding(n),d=i.closeBinding(n);return r.publish("control-plane-proxy.binding.updated",{binding:a,audit:{action:"binding.revoke",actor:t}},{topic:"control-plane-proxy",scope:{nodeId:a.targetNodeId}}),{data:{binding:a,closed:d}}}),t.get("/api/control-plane-proxy/diagnostics",async()=>({data:i.diagnostics()})),t.post("/api/node-proxy/claims",{config:sl},async(e,t)=>{const n=s.claimInvite(e.body);return r.publish("control-plane-proxy.binding.created",{binding:n.binding,audit:{action:"binding.claim",actor:`control-plane:${n.binding.sourceControlPlaneId}`}},{topic:"control-plane-proxy",scope:{nodeId:n.binding.targetNodeId}}),t.code(201).send({data:n})}),t.get("/api/control-plane-proxy/pending-claims",async()=>({data:n.listPendingProxyClaims()})),t.post("/api/control-plane-proxy/claims",async(e,t)=>{const s=await n.claimProxyNode(e.body);return r.publish("node.created",{nodeId:s.node.id}),t.code(201).send({data:s})}),t.post("/api/control-plane-proxy/pending-claims/:id/resume",async e=>{const t=zu.parse(e.params).id;Tu.parse(e.body||{});const s=await n.resumeProxyClaim(t);return r.publish("node.created",{nodeId:s.node.id}),{data:s}}),t.delete("/api/control-plane-proxy/pending-claims/:id",async e=>({data:await n.cancelProxyClaim(zu.parse(e.params).id)}))}({app:a,service:d,proxy:v,runtime:w,events:l,actorId:async e=>{const t=await qu(c,e.cookies[di]);return t?"user"===t.type?`user:${t.userId}`:"system"===t.type?`system:${t.reason}`:`chat-bridge:${t.bridgeId}`:"system:unknown"}}),function({app:e,service:t}){!function(e){for(const t of["application/octet-stream","application/x-www-form-urlencoded"])kp(e,t);kp(e,/^multipart\/form-data(?:;.*)?$/i)}(e),e.get("/api/app-access/session",{config:sl},async e=>{const n=Cp(e.url),s="vnc"===e.query.mode?"vnc":"web"===e.query.mode?"web":"tty",i=await t.appAccessProxyTarget(n,s);return{data:{mode:s,instance:{id:i.instance.id,name:i.instance.name,projectId:i.instance.projectId,projectName:"string"==typeof i.instance.sourceSnapshot.name?i.instance.sourceSnapshot.name:void 0},session:i.session,expiresAt:i.access.expiresAt,ttySocketPath:"tty"===s?`/apps/access/tty/ws?token=${encodeURIComponent(n)}`:void 0,vncFramePath:"vnc"===s?xp(n,i.access.sessionId,i.session):void 0}}}),e.get("/apps/access/tty/ws",{websocket:!0,config:sl},async(e,n)=>{try{const s=await t.appAccessProxyTarget(Cp(n.url),"tty");await t.proxyInstanceWebSocket(s.instance.id,e,s.path,Ip(n.headers),yp(n.headers))}catch{e.close(1011,"TTY access link is invalid or expired.")}}),e.route({method:"GET",url:"/apps/access/vnc/proxy/*",config:sl,wsHandler:async(e,n)=>{const s=n.params;try{const i=await t.appAccessProxyTarget(Cp(n.url),"vnc",s["*"]||"");await t.proxyInstanceWebSocket(i.instance.id,e,i.path,Ip(n.headers),yp(n.headers))}catch{e.close(1011,"VNC access link is invalid or expired.")}},handler:async(e,n)=>{const s=e.params,i=await t.appAccessProxyTarget(Cp(e.url),"vnc",s["*"]||"");return Sp(n,await bp(t,n,i.instance.id,i.path,{method:e.method,headers:fp(e.headers)}),i.instance.id,i.path)}}),e.all("/instances/:id",{bodyLimit:67108864},async(e,n)=>{const s=e.params;return"GET"===e.method||"HEAD"===e.method?n.redirect(`${wp(s.id)}/`):Sp(n,await bp(t,n,s.id,"/",{method:e.method,headers:fp(e.headers),body:Ap(e.body)}),s.id,"/")}),e.route({method:"GET",url:"/instances/:id/*",wsHandler:async(e,n)=>{const s=n.params,i=s["*"]||"",r=n.url.indexOf("?"),o=r>=0?n.url.slice(r):"";try{await t.proxyInstanceWebSocket(s.id,e,`/${i}${o}`,Ip(n.headers),yp(n.headers))}catch{e.close(1011,"Instance websocket endpoint is not reachable.")}},handler:async(e,n)=>{const s=e.params,i=s["*"]||"",r=e.url.indexOf("?"),o=`/${i}${r>=0?e.url.slice(r):""}`;return Sp(n,await bp(t,n,s.id,o,{method:e.method,headers:fp(e.headers)}),s.id,o)}}),e.route({method:["POST","PUT","PATCH","DELETE","OPTIONS"],url:"/instances/:id/*",bodyLimit:67108864,handler:async(e,n)=>{const s=e.params,i=s["*"]||"",r=e.url.indexOf("?"),o=r>=0?e.url.slice(r):"";return vp(n,await bp(t,n,s.id,`/${i}${o}`,{method:e.method,headers:fp(e.headers),body:Ap(e.body)}))}})}({app:a,service:d}),a.get("*",{config:il},async(e,t)=>n.existsSync(P)?Du(t,P,"index.html"):t.code(404).send({error:{code:"CONTROL_PLANE_UI_NOT_BUILT",message:"Control plane API is running, but the Web UI static directory was not found."}})),C=setInterval(()=>{x().catch(e=>a.log.warn({error:e},"pending node pairing recovery failed"))},3e4),C.unref(),a}async function Uu(e){const s=Sl(e.dataDir),i=function(e,s={}){return function(e,s={}){n.mkdirSync(t.dirname(e),{recursive:!0});const i={pid:process.pid,hostname:f.hostname(),component:s.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:a.randomUUID(),startIdentity:Tl(process.pid),dataDir:s.dataDir,host:s.host,port:s.port,instanceId:s.instanceId};for(let t=0;t<5;t+=1)try{return n.mkdirSync(e),n.writeFileSync(Cl(e),`${JSON.stringify({token:i.token,pid:i.pid,startIdentity:i.startIdentity,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Pl(e,i),{lockPath:e,owner:i,release(){const t=Nl(e);t?.token===i.token&&n.rmSync(e,{recursive:!0,force:!0})}}}catch(i){if("EEXIST"!==i.code)throw i;const r=Nl(e);if(_l(r))throw new Al(e,r,s.error);if(!r&&(Rl(e)||Ml(e)<(s.incompleteLockStaleMs??2e3))){if(t+1<5){Dl();continue}throw new Al(e,void 0,s.error)}const o=a.randomUUID();if(!ql(e,o)){if(t+1<5){Dl();continue}throw new Al(e,Nl(e),s.error)}const d=Nl(e);if(_l(d))throw jl(xl(e)),new Al(e,d,s.error);if(r?.token&&d?.token!==r.token){jl(xl(e));continue}if(!Ll(e,o))continue;n.rmSync(e,{recursive:!0,force:!0})}const r=Nl(e);throw new Al(e,r,s.error)}(e,{...s,component:"control-plane",error:{label:"Control plane",code:"CONTROL_PLANE_ALREADY_RUNNING"}})}(t.join(f.tmpdir(),`task-handoff-control-plane-${process.getuid?.()??"user"}.lock`),{dataDir:s.dataDir,host:e.host,port:e.port});try{const t=await Bu(e);t.addHook("onClose",async()=>{i.release()}),await t.listen({host:e.host,port:e.port})}catch(e){throw i.release(),e}}function Fu(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 Hu(e){if("disabled"!==e&&"password"!==e)throw new Error("Auth mode must be disabled or password.");return e}(async function(){const n=new e.Command;n.name("task-handoff-control-plane").description("Run the TaskHandoff control plane.").version(w("@task-handoff/cli")).option("--host <host>","Control plane host",process.env.TASK_HANDOFF_CONTROL_PLANE_HOST||"127.0.0.1").option("-p, --port <port>","Control plane port",Fu,Number(process.env.TASK_HANDOFF_CONTROL_PLANE_PORT)||8081).option("--data-dir <path>","Control plane data directory").option("--static-dir <path>","Control plane Web UI static directory",process.env.TASK_HANDOFF_CONTROL_PLANE_STATIC_DIR||t.resolve(__dirname,"..","ui")).option("--auth-mode <mode>","Control plane auth mode: disabled or password",Hu,process.env.TASK_HANDOFF_CONTROL_PLANE_AUTH_MODE||"password").action(async e=>{await Uu({host:e.host,port:e.port,dataDir:e.dataDir,staticDir:e.staticDir,auth:{mode:e.authMode}})}),await n.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});
|