@task-handoff/node-agent 0.0.3 → 0.0.5
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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/bin/task-handoff-node-update-worker +9 -0
- package/dist/cli.js +1 -1
- package/package.json +5 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -26,6 +26,7 @@ const jobFile = options.jobFile;
|
|
|
26
26
|
const targetVersion = options.targetVersion;
|
|
27
27
|
const service = options.service;
|
|
28
28
|
const npmCommand = options.npmCommand;
|
|
29
|
+
const nodeAgentManifest = path.resolve(__dirname, "..", "package.json");
|
|
29
30
|
|
|
30
31
|
function updateJob(patch) {
|
|
31
32
|
const current = JSON.parse(fs.readFileSync(jobFile, "utf8"));
|
|
@@ -38,6 +39,13 @@ function run(command, args) {
|
|
|
38
39
|
if (result.status !== 0) throw new Error(`${command} exited with status ${result.status ?? "unknown"}`);
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
function verifyInstalledVersion() {
|
|
43
|
+
const installedVersion = JSON.parse(fs.readFileSync(nodeAgentManifest, "utf8")).version;
|
|
44
|
+
if (installedVersion !== targetVersion) {
|
|
45
|
+
throw new Error(`Updated node-agent verification failed: expected ${targetVersion}, found ${installedVersion || "unknown"}.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
try {
|
|
42
50
|
updateJob({ status: "updating", startedAt: new Date().toISOString(), error: undefined });
|
|
43
51
|
const prefixResult = spawnSync(npmCommand, ["prefix", "--global"], { encoding: "utf8" });
|
|
@@ -59,6 +67,7 @@ try {
|
|
|
59
67
|
updateJob({ status: "restarting" });
|
|
60
68
|
run("systemctl", ["restart", service]);
|
|
61
69
|
}
|
|
70
|
+
verifyInstalledVersion();
|
|
62
71
|
updateJob({ status: "succeeded", completedAt: new Date().toISOString() });
|
|
63
72
|
} catch (error) {
|
|
64
73
|
updateJob({ status: "failed", error: error instanceof Error ? error.message : String(error), completedAt: new Date().toISOString() });
|
package/dist/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("commander"),e=require("node:fs"),n=require("node:path"),r=require("node:http"),o=require("node:net"),i=require("node:child_process"),s=require("fastify"),a=require("@fastify/websocket"),c=require("ws"),d=require("zod"),l=require("node:crypto"),u=require("node:os"),p=require("write-file-atomic"),m=require("semver");const g=41943040,h="ai.sessions",f=d.z.string().trim().min(1).max(80),y=d.z.enum(["running","waiting","idle","failed"]),I=d.z.enum(["thinking","tool","editing","approval","responding","unknown"]),z=d.z.object({name:d.z.string().trim().min(1).max(120),inputPreview:d.z.string().trim().max(500).optional(),startedAt:d.z.string().datetime().optional()}).strict(),b=d.z.object({toolCalls:d.z.number().int().min(0).default(0),edits:d.z.number().int().min(0).default(0),approvals:d.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),A=d.z.object({send:d.z.boolean().optional(),interrupt:d.z.boolean().optional(),approval:d.z.boolean().optional()}).strict(),w=d.z.object({id:d.z.string().trim().min(1).max(120),kind:d.z.enum(["image"]),name:d.z.string().trim().min(1).max(240),mime:d.z.string().trim().min(1).max(120),size:d.z.number().int().min(0).max(20971520),data:d.z.string().min(1).max(31457280)}).strict(),S=w.omit({data:!0}),k=d.z.object({id:d.z.string().trim().min(1).max(120),kind:d.z.enum(["image"]).optional()}).strict(),N=d.z.enum(["auto","queue","steer","immediate"]),_=d.z.object({message:d.z.string().trim().min(1).max(2e4),mode:N.optional()});_.extend({attachments:d.z.array(w).max(6).default([])}).strict().superRefine((t,e)=>{t.attachments.reduce((t,e)=>t+e.size,0)>g&&e.addIssue({code:d.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),_.extend({attachments:d.z.array(k).max(6).default([])}).strict(),d.z.object({decision:d.z.enum(["allow","deny","skip"])}).strict(),d.z.object({queueIds:d.z.array(d.z.string().trim().min(1).max(120)).max(100)}).strict(),d.z.object({code:d.z.string().trim().min(1).max(160),message:d.z.string(),instanceId:d.z.string().trim().min(1).max(160).optional(),nodeId:d.z.string().trim().min(1).max(160).optional(),route:d.z.string().trim().min(1).max(4096).optional()}).strict();const v=d.z.object({id:d.z.string().trim().min(1).max(120),message:d.z.string().trim().min(1).max(2e4),attachments:d.z.array(S).max(6).default([]),status:d.z.enum(["queued","sending","failed"]).default("queued"),createdAt:d.z.string().datetime(),updatedAt:d.z.string().datetime(),error:d.z.string().trim().max(4e3).optional()}).strict(),E=d.z.object({pendingCount:d.z.number().int().min(0).default(0),items:d.z.array(v).max(100).default([])}).strict().default({pendingCount:0,items:[]}),O=d.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),x=d.z.object({id:d.z.string().trim().min(1).max(240),providerTurnId:d.z.string().trim().max(240).optional(),source:O.optional(),userPrompt:d.z.string().trim().optional(),status:d.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:I.optional(),summary:d.z.string().trim().max(1e3).optional(),lastMessage:d.z.string().trim().optional(),revision:d.z.number().int().min(0).default(0),sourcePriority:d.z.number().int().min(0).max(100).optional(),snapshotVersion:d.z.number().int().min(0).optional(),observedAt:d.z.string().datetime().optional(),startedAt:d.z.string().datetime().optional(),updatedAt:d.z.string().datetime().optional(),completedAt:d.z.string().datetime().optional()}).strict(),T=d.z.object({id:d.z.string().trim().min(1).max(120),agent:f,appSessionId:d.z.string().trim().max(120).optional(),appId:d.z.string().trim().max(120).optional(),providerSessionId:d.z.string().trim().max(240).optional(),providerMeta:d.z.record(d.z.string(),d.z.unknown()).optional(),appBindingKeys:d.z.array(d.z.string().trim().min(1).max(240)).max(20).optional(),actions:A.optional(),activeTurnId:d.z.string().trim().max(240).optional(),conversationId:d.z.number().int().positive().optional(),title:d.z.string().trim().max(240).optional(),cwd:d.z.string().trim().max(4096).optional(),userPrompt:d.z.string().trim().optional(),turns:d.z.array(x).max(50).optional(),status:y.default("running"),phase:I.default("unknown"),summary:d.z.string().trim().max(1e3).optional(),lastMessage:d.z.string().trim().optional(),currentTool:z.optional(),transcriptPath:d.z.string().trim().max(4096).optional(),transcriptSize:d.z.number().int().min(0).optional(),startedAt:d.z.string().datetime(),updatedAt:d.z.string().datetime(),completedAt:d.z.string().datetime().optional(),error:d.z.string().trim().max(4e3).optional(),counters:b,queue:E}).strict();d.z.object({session:T,provider:d.z.string().trim().min(1).max(80),action:d.z.enum(["send","queue","steer","interrupt","approval"]),decision:d.z.enum(["allow","deny","skip"]).optional(),turnId:d.z.string().trim().min(1).max(240).optional(),providerTurnId:d.z.string().trim().min(1).max(240).optional(),queueId:d.z.string().trim().min(1).max(120).optional()}).strict();const C=T.pick({id:!0,agent:!0,appSessionId:!0,appId:!0,providerSessionId:!0,providerMeta:!0,appBindingKeys:!0,actions:!0,activeTurnId:!0,conversationId:!0,title:!0,cwd:!0,userPrompt:!0,turns:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,currentTool:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),j=d.z.object({runningCount:d.z.number().int().min(0).default(0),waitingCount:d.z.number().int().min(0).default(0),staleCount:d.z.number().int().min(0).default(0),sessions:d.z.array(C).default([]),updatedAt:d.z.string().datetime()}).strict();d.z.object({streamId:d.z.string().trim().min(1).max(240),revision:d.z.number().int().min(0),lastEventAt:d.z.string().datetime(),snapshot:j}).strict(),j.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:d.z.number().int().min(0).default(0),problemCount:d.z.number().int().min(0).default(0),revision:d.z.number().int().min(0).optional()});const D=d.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),R=d.z.object({streamId:d.z.string().trim().min(1).max(240),instanceId:d.z.string().trim().min(1).max(160),nodeId:d.z.string().trim().min(1).max(160).optional(),revision:d.z.number().int().min(0),previousRevision:d.z.number().int().min(0).optional(),traceId:d.z.string().trim().min(1).max(160),generatedAt:d.z.string().datetime(),reason:D}).strict(),P=d.z.object({meta:R,snapshot:j}).strict(),F=d.z.object({meta:R,upserted:d.z.array(C).default([]),removed:d.z.array(d.z.string().trim().min(1).max(120)).default([])}).strict(),$=d.z.object({meta:R,sessionIds:d.z.array(d.z.string().trim().min(1).max(120)).default([]),expiresAt:d.z.string().datetime()}).strict();d.z.object({streamId:d.z.string().trim().min(1).max(240),instanceId:d.z.string().trim().min(1).max(160),sinceRevision:d.z.number().int().min(0),latestRevision:d.z.number().int().min(0),earliestRetainedRevision:d.z.number().int().min(0),syncRequired:d.z.boolean().default(!1),events:d.z.array(d.z.discriminatedUnion("type",[d.z.object({type:d.z.literal("ai-session.snapshot"),payload:P}).strict(),d.z.object({type:d.z.literal("ai-session.patch"),payload:F}).strict(),d.z.object({type:d.z.literal("ai-session.removed"),payload:$}).strict()])).default([])}).strict();const L=d.z.object({source:O,sourcePriority:d.z.number().int().min(0).max(100).optional(),observedAt:d.z.string().datetime().optional(),snapshotVersion:d.z.number().int().min(0).optional()}).strict(),U=L.extend({type:d.z.literal("snapshot"),agent:d.z.string().trim().min(1).max(80),appSessionId:d.z.string().trim().max(120).optional(),appId:d.z.string().trim().max(120).optional(),providerSessionId:d.z.string().trim().max(240).optional(),providerMeta:d.z.record(d.z.string(),d.z.unknown()).optional(),appBindingKeys:d.z.array(d.z.string().trim().min(1).max(240)).max(20).optional(),actions:A.optional(),conversationId:d.z.number().int().positive().optional(),title:d.z.string().trim().max(240).optional(),cwd:d.z.string().trim().max(4096).optional(),activeTurnId:d.z.string().trim().max(240).optional(),userPrompt:d.z.string().trim().optional(),turns:d.z.array(x).max(50).optional(),status:y.optional(),phase:I.optional(),summary:d.z.string().trim().max(1e3).optional(),lastMessage:d.z.string().trim().optional(),transcriptPath:d.z.string().trim().max(4096).optional(),transcriptSize:d.z.number().int().min(0).optional(),replaceActivity:d.z.boolean().optional()}).strict(),M=L.extend({type:d.z.literal("event"),sessionId:d.z.string().trim().min(1).max(120),kind:d.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed"]),activeTurnId:d.z.string().trim().max(240).optional(),providerTurnId:d.z.string().trim().max(240).optional(),userPrompt:d.z.string().trim().optional(),text:d.z.string().optional(),status:y.optional(),phase:I.optional(),summary:d.z.string().trim().max(1e3).optional(),error:d.z.string().trim().max(4e3).optional(),counters:d.z.object({toolCalls:d.z.number().int().min(0).optional(),edits:d.z.number().int().min(0).optional(),approvals:d.z.number().int().min(0).optional()}).strict().optional()}).strict();d.z.discriminatedUnion("type",[U,M]);const H=d.z.union([d.z.object({type:d.z.literal("schedule"),scheduleKind:d.z.literal("interval").default("interval"),intervalMs:d.z.number().int().min(1e3)}).strict(),d.z.object({type:d.z.literal("schedule"),scheduleKind:d.z.literal("daily"),timeOfDay:d.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:d.z.string().trim().min(1).max(120)}).strict(),d.z.object({type:d.z.literal("schedule"),scheduleKind:d.z.literal("weekly"),weekdays:d.z.array(d.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:d.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:d.z.string().trim().min(1).max(120)}).strict()]),K=d.z.union([H,d.z.object({type:d.z.literal("file-change"),roots:d.z.array(d.z.string().trim().min(1).max(4096)).min(1).max(50),globs:d.z.array(d.z.string().trim().min(1).max(500)).min(1).max(100),ignore:d.z.array(d.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:d.z.number().int().min(100).max(6e4).default(1500)}).strict(),d.z.object({type:d.z.literal("ai-session"),conversationId:d.z.number().int().positive().optional(),agent:d.z.string().trim().min(1).max(80).optional(),statuses:d.z.array(d.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:d.z.array(d.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),G=d.z.object({promptTemplate:d.z.string().trim().min(1).max(2e4)}).strict(),q=d.z.object({cooldownMs:d.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:d.z.number().int().min(1).max(20).default(1),whenBusy:d.z.enum(["skip","queue"]).default("skip")}).strict(),V=d.z.object({configHash:d.z.string().trim().min(8).max(80),name:d.z.string().trim().min(1).max(160),description:d.z.string().trim().max(1e3).optional(),source:K,action:G,policy:q.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:d.z.string().datetime(),updatedAt:d.z.string().datetime()}).strict(),B=d.z.discriminatedUnion("type",[d.z.object({type:d.z.literal("conversation"),conversationId:d.z.number().int().positive()}).strict(),d.z.object({type:d.z.literal("ai-session"),aiSessionId:d.z.string().trim().min(1).max(160)}).strict()]),J=d.z.object({configHash:d.z.string().trim().min(8).max(80),deploymentId:d.z.string().trim().min(1).max(240).optional(),instanceId:d.z.string().trim().min(1).max(160),origin:d.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:d.z.boolean().default(!0),target:B,localName:d.z.string().trim().min(1).max(160).optional(),createdAt:d.z.string().datetime(),updatedAt:d.z.string().datetime()}).strict(),W=d.z.object({configHash:d.z.string().trim().min(8).max(80),deploymentId:d.z.string().trim().min(1).max(240).optional(),instanceId:d.z.string().trim().min(1).max(160),status:d.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:d.z.string().datetime().optional(),lastCompletedAt:d.z.string().datetime().optional(),lastSkippedAt:d.z.string().datetime().optional(),lastError:d.z.string().trim().max(4e3).optional(),runCount:d.z.number().int().min(0).default(0),skippedCount:d.z.number().int().min(0).default(0)}).strict(),Y=d.z.object({id:d.z.string().trim().min(1).max(160),configHash:d.z.string().trim().min(8).max(80),deploymentId:d.z.string().trim().min(1).max(240).optional(),instanceId:d.z.string().trim().min(1).max(160),eventType:d.z.enum(["manual","schedule","file-change","ai-session"]),status:d.z.enum(["started","completed","failed","skipped"]),target:B,promptPreview:d.z.string().trim().max(500),eventSummary:d.z.string().trim().max(1e3).optional(),error:d.z.string().trim().max(4e3).optional(),startedAt:d.z.string().datetime(),completedAt:d.z.string().datetime().optional()}).strict();d.z.object({schemaVersion:d.z.literal(1).default(1),configs:d.z.array(V).default([]),deployments:d.z.array(J).default([]),runtime:d.z.array(W).default([]),recentRuns:d.z.array(Y).default([])}).strict();const Q="2026-07-13",Z=d.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),X=d.z.string().datetime(),tt=d.z.record(d.z.string(),d.z.string()).default({}),et=d.z.record(d.z.string(),d.z.string()).default({}),nt=d.z.object({codexModelId:Z.optional(),claudeModelId:Z.optional()}).strict().default({}),rt=d.z.object({component:d.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:d.z.string().trim().max(160).optional(),packageVersion:d.z.string().trim().max(80).optional(),protocolVersion:d.z.string().trim().max(80).optional(),buildId:d.z.string().trim().max(160).optional(),builtAt:d.z.string().trim().max(120).optional(),gitCommit:d.z.string().trim().max(120).optional(),imageRef:d.z.string().trim().max(512).optional(),imageDigest:d.z.string().trim().max(240).optional()}).strict(),ot=d.z.enum(["stable","beta","alpha"]),it=d.z.discriminatedUnion("component",[d.z.object({component:d.z.literal("node-agent")}).strict(),d.z.object({component:d.z.literal("controlled-instance"),instanceId:Z}).strict()]),st=d.z.object({target:it,channel:ot.default("stable")}).strict(),at=d.z.object({target:it,source:d.z.enum(["npm","docker-registry"]),channel:ot,currentVersion:d.z.string().trim().max(240).optional(),availableVersion:d.z.string().trim().min(1).max(240),artifactRef:d.z.string().trim().min(1).max(512).optional(),updateAvailable:d.z.boolean(),supported:d.z.boolean().default(!0),reason:d.z.string().trim().max(2048).optional(),checkedAt:X}).strict(),ct=d.z.object({id:Z,nodeId:Z,target:it,source:d.z.enum(["npm","docker-registry"]),channel:ot,fromVersion:d.z.string().trim().max(240).optional(),toVersion:d.z.string().trim().min(1).max(240),artifactRef:d.z.string().trim().min(1).max(512).optional(),status:d.z.enum(["queued","updating","restarting","succeeded","failed"]),error:d.z.string().trim().max(4096).optional(),startedAt:X.optional(),completedAt:X.optional(),createdAt:X,updatedAt:X}).strict(),dt=st,lt=d.z.object({type:d.z.enum(["branch","tag","commit"]),name:d.z.string().trim().min(1).max(240).optional(),commit:d.z.string().trim().max(80).optional()}).strict(),ut=d.z.object({type:d.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:d.z.string().trim().min(1).max(120).optional()}).strict(),pt=d.z.object({depth:d.z.number().int().positive().max(1e5).optional(),submodules:d.z.boolean().default(!1),lfs:d.z.boolean().default(!1),subdirectory:d.z.string().trim().max(240).default("")}).strict();d.z.object({id:Z,name:d.z.string().trim().min(1).max(160),url:d.z.string().trim().min(1).max(2048),provider:d.z.string().trim().max(80).optional(),ref:lt.default({type:"branch",name:"main"}),auth:ut.default({type:"none"}),clone:pt.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:Z.optional(),modelSelection:nt,labels:tt,createdAt:X,updatedAt:X}).strict();const mt=d.z.object({id:Z,nodeId:Z,name:d.z.string().trim().min(1).max(160),path:d.z.string().trim().min(1).max(4096),defaultImageId:Z.optional(),modelSelection:nt,labels:tt,createdAt:X,updatedAt:X}).strict(),gt=d.z.discriminatedUnion("type",[d.z.object({type:d.z.literal("git-repository"),repositoryId:Z.optional(),url:d.z.string().trim().min(1).max(2048),provider:d.z.string().trim().max(80).optional(),ref:lt.default({type:"branch",name:"main"}),auth:ut.default({type:"none"}),clone:pt.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),d.z.object({type:d.z.literal("git-template"),url:d.z.string().trim().min(1).max(2048),templateId:d.z.string().trim().min(1).max(120).optional(),ref:lt.default({type:"branch",name:"main"}),auth:ut.default({type:"none"}),clone:pt.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),d.z.object({type:d.z.literal("local-folder"),localFolderId:Z.optional(),path:d.z.string().trim().min(1).max(4096),ownerNodeId:Z.optional()}).strict()]),ht=d.z.object({mode:d.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:d.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:d.z.string().trim().max(240).optional(),readOnly:d.z.boolean().default(!1)}).strict(),ft=d.z.enum(["codex","claude"]),yt=d.z.object({id:Z,name:d.z.string().trim().min(1).max(160),source:gt,defaultImageId:Z.optional(),defaultNodeId:Z.optional(),defaultRuntimeId:Z.optional(),modelSelection:nt,workspacePolicy:ht,labels:tt,createdAt:X,updatedAt:X}).strict();d.z.object({id:Z,name:d.z.string().trim().min(1).max(160),endpoint:d.z.string().trim().min(1).max(2048),key:d.z.string().trim().min(1).max(4096),model:d.z.string().trim().min(1).max(240),app:ft,enabled:d.z.boolean().default(!0),order:d.z.number().int().min(0).max(1e6).default(0),labels:tt,createdAt:X,updatedAt:X}).strict();const It=d.z.object({id:Z,name:d.z.string().trim().min(1).max(160),image:d.z.string().trim().min(1).max(512),registry:d.z.string().trim().min(1).max(160).default("local"),capabilities:d.z.array(d.z.string().trim().min(1).max(80)).default([]),optionalApps:d.z.array(d.z.string().trim().min(1).max(120)).default([]),defaultEnv:et,labels:tt,createdAt:X,updatedAt:X}).strict(),zt=d.z.object({id:Z,name:d.z.string().trim().min(1).max(160),connectionMode:d.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:d.z.object({mode:d.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:Z.optional(),secret:d.z.string().trim().max(4096).optional(),pairedAt:X.optional(),pairing:d.z.object({status:d.z.enum(["pending","paired","expired"]).default("pending"),joinToken:d.z.string().trim().max(4096).optional(),expiresAt:X.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:d.z.string().trim().max(2048).optional(),controlEndpoint:d.z.string().trim().max(2048).optional(),containerEndpoint:d.z.string().trim().max(2048).optional(),publicWebBase:d.z.string().trim().max(2048).optional(),status:d.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:d.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:d.z.record(d.z.string(),d.z.unknown()).default({}),labels:tt,lastSeenAt:X.optional(),createdAt:X,updatedAt:X}).strict(),bt=d.z.object({id:Z,nodeId:Z,name:d.z.string().trim().min(1).max(160),type:d.z.enum(["docker","kubernetes","local"]),status:d.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:d.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:d.z.record(d.z.string(),d.z.unknown()).default({}),labels:tt,createdAt:X,updatedAt:X}).strict();d.z.object({ok:d.z.boolean().optional(),role:d.z.string().optional(),nodeId:Z.optional()}).passthrough();const At=d.z.object({bindScope:d.z.enum(["loopback","all-ipv4"]),port:d.z.number().int().min(1).max(65535)}).strict(),wt=At.extend({host:d.z.enum(["127.0.0.1","0.0.0.0"]),status:d.z.enum(["listening","error"]),source:d.z.enum(["bootstrap","persisted"]),error:d.z.string().trim().max(2048).optional()}).strict(),St=At;d.z.object({nodeId:Z,joinToken:d.z.string().trim().min(1),expiresAt:X}).passthrough(),d.z.object({id:Z,keyId:Z,url:d.z.string().trim().min(1).max(2048).optional(),name:d.z.string().trim().min(1).max(160).optional(),pairedAt:X,updatedAt:X.optional(),active:d.z.boolean().optional(),current:d.z.boolean().optional()}).passthrough(),d.z.object({remote:d.z.object({id:Z,url:d.z.string().trim().min(1).max(2048).optional(),keyId:Z,pairedAt:X,active:d.z.boolean()}).passthrough(),tunnel:d.z.object({status:d.z.enum(["disabled","saved","connecting","connected","failed"]),error:d.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),d.z.object({deleted:d.z.boolean().optional()}).passthrough(),d.z.object({repository:d.z.string().trim().optional(),tag:d.z.string().trim().optional(),id:d.z.string().trim(),createdSince:d.z.string().trim().optional(),size:d.z.string().trim().optional(),reference:d.z.string().trim().min(1)}).passthrough();const kt=d.z.lazy(()=>d.z.object({name:d.z.string().trim().min(1),path:d.z.string().trim().min(1).max(4096),children:d.z.array(kt)}).passthrough());d.z.object({status:d.z.number().int().min(100).max(599).optional(),headers:d.z.record(d.z.string(),d.z.string()).optional(),bodyBase64:d.z.string().optional()}).passthrough();const Nt=d.z.object({strategy:d.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:d.z.string().trim().max(2048).optional(),api:d.z.string().trim().max(2048).optional(),vnc:d.z.string().trim().max(2048).optional(),tty:d.z.string().trim().max(2048).optional(),logs:d.z.string().trim().max(2048).optional(),status:d.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),_t=d.z.object({strategy:d.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:d.z.string().trim().max(2048).optional(),api:d.z.string().trim().max(2048).optional(),ws:d.z.string().trim().max(2048).optional(),status:d.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),vt=d.z.object({mode:d.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:d.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:d.z.string().trim().max(4096).optional(),resolvedCommit:d.z.string().trim().max(120).optional(),error:d.z.string().trim().max(2048).optional()}).passthrough(),Et=d.z.object({id:Z,name:d.z.string().trim().min(1).max(160),projectId:Z.optional(),source:gt,sourceSnapshot:d.z.record(d.z.string(),d.z.unknown()).default({}),nodeId:Z,runtimeId:Z,imageId:Z.optional(),imageSnapshot:It.optional(),status:d.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:d.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:d.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:d.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:d.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:d.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:d.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:d.z.string().trim().max(80).optional(),instanceVersion:d.z.string().trim().max(80).optional(),build:rt.optional(),capabilities:d.z.record(d.z.string(),d.z.unknown()).default({}),config:d.z.object({autoImportAgentConfigs:d.z.boolean().default(!0)}).strict().default({autoImportAgentConfigs:!0}),workspace:vt.default({status:"unknown"}),target:Nt.default({strategy:"direct-port",status:"unknown"}),access:_t,receiver:d.z.object({status:d.z.string().trim().max(80).default("unknown"),pendingCount:d.z.number().int().min(0).default(0)}).strict().default({status:"unknown",pendingCount:0}),apps:d.z.object({runningCount:d.z.number().int().min(0).default(0),problemCount:d.z.number().int().min(0).default(0),updatedAt:X.optional(),revision:d.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:j.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:d.z.object({enabledCount:d.z.number().int().min(0).default(0),runningCount:d.z.number().int().min(0).default(0),errorCount:d.z.number().int().min(0).default(0),configs:d.z.array(d.z.object({configHash:d.z.string().trim().min(1),config:V,deployments:d.z.array(J).default([]),runtime:d.z.array(W).default([])}).strict()).default([]),recentRuns:d.z.array(Y).default([])}).strict().optional(),runtime:d.z.object({kind:d.z.string().trim().max(80).optional(),containerName:d.z.string().trim().max(240).optional(),containerId:d.z.string().trim().max(240).optional(),workspacePath:d.z.string().trim().max(4096).optional(),pid:d.z.number().int().positive().optional(),port:d.z.number().int().positive().max(65535).optional(),labels:d.z.record(d.z.string(),d.z.string()).default({})}).strict().default({labels:{}}),registrationToken:d.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:X.optional(),createdAt:X,updatedAt:X}).strict();function Ot(t){return Et.safeParse(xt(t))}function xt(t){if(!t||"object"!=typeof t||Array.isArray(t))return t;const e=t,n=new Set(Object.keys(Et.shape)),r={};for(const[t,o]of Object.entries(e))n.has(t)&&(r[t]=o);return r.apps=Tt(e.apps,["runningCount","problemCount","updatedAt","revision"]),r.config=Tt(e.config,["autoImportAgentConfigs"]),r.receiver=Tt(e.receiver,["status","pendingCount"]),r.runtime=Tt(e.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),r.target=Tt(e.target??e.endpoints,["strategy","web","api","status"]),r.access=Tt(e.access,["strategy","web","api","ws","status"]),r}function Tt(t,e){if(!t||"object"!=typeof t||Array.isArray(t))return t;const n=t;return Object.fromEntries(e.filter(t=>Object.prototype.hasOwnProperty.call(n,t)).map(t=>[t,n[t]]))}const Ct=d.z.object({instanceId:Z.optional(),name:d.z.string().trim().min(1).max(160),projectId:Z.optional(),source:gt.optional(),nodeId:Z.optional(),runtimeId:Z.optional(),imageId:Z.optional(),instanceVersion:d.z.string().trim().max(80).optional(),protocolVersion:d.z.string().trim().max(80),build:rt.optional(),controlMode:d.z.enum(["standalone","controlled"]).default("controlled"),capabilities:d.z.record(d.z.string(),d.z.unknown()).default({}),target:Nt.default({strategy:"direct-port",status:"unknown"}),workspace:vt.default({status:"unknown"}),registrationToken:d.z.string().trim().max(240).optional()}).strict(),jt=d.z.object({status:Et.shape.status.optional(),health:Et.shape.health.optional(),protocolVersion:d.z.string().trim().max(80),build:rt.optional(),capabilities:Et.shape.capabilities.optional(),receiver:Et.shape.receiver.optional(),apps:Et.shape.apps.optional(),aiSessions:Et.shape.aiSessions.optional(),triggers:Et.shape.triggers.optional(),workspace:vt.optional(),target:Nt.partial().optional()}).strict(),Dt=d.z.enum(["web","telegram","wechat","dingding"]);function Rt(t){return t.readyState===(t.OPEN??1)}function Pt(t,e,n){t.close(function(t){if("number"==typeof t&&Number.isInteger(t))return t>=3e3&&t<=4999||t>=1e3&&t<=1014&&1004!==t&&1005!==t&&1006!==t?t:void 0}(e),function(t){const e=Buffer.isBuffer(t)?t.toString("utf8"):"string"==typeof t?t:"";if(Buffer.byteLength(e,"utf8")<=123)return e;let n="";for(const t of e){const e=`${n}${t}`;if(Buffer.byteLength(e,"utf8")>123)break;n=e}return n}(n)||void 0)}function Ft(t,e){t.send(e.data,{binary:e.isBinary})}function $t(t,e){if(Rt(t))for(const n of e.splice(0))Ft(t,n)}function Lt(t,e){return new Promise((n,r)=>{const o=i.spawn(t,e,{stdio:["ignore","pipe","pipe"]}),s=[],a=[];o.stdout.on("data",t=>s.push(Buffer.from(t))),o.stderr.on("data",t=>a.push(Buffer.from(t))),o.on("error",r),o.on("close",e=>{const o={stdout:Buffer.concat(s).toString("utf8").trim(),stderr:Buffer.concat(a).toString("utf8").trim()};if(0===e)return void n(o);const i=new Error(o.stderr||`${t} exited with code ${e}`);Object.assign(i,{statusCode:502,code:"RUNTIME_EXECUTOR_FAILED",details:o}),r(i)})})}d.z.object({id:Z,channel:Dt,name:d.z.string().trim().min(1).max(160),enabled:d.z.boolean().default(!1),token:d.z.string().trim().max(4096).optional(),defaultChatId:d.z.string().trim().max(240).optional(),allowedUserIds:d.z.array(d.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:d.z.number().int().positive().max(6e4).default(3e3),settings:d.z.record(d.z.string(),d.z.unknown()).default({}),createdAt:X,updatedAt:X}).strict(),d.z.object({id:Z,channel:Dt,bridgeId:Z.optional(),chatSessionId:d.z.string().trim().min(1).max(240),userId:d.z.string().trim().max(240).optional(),activeProjectId:Z.optional(),activeInstanceId:Z.optional(),activeAiSessionId:d.z.string().trim().min(1).max(120).optional(),lastUsedAt:X,createdAt:X,updatedAt:X}).strict(),d.z.object({source:d.z.object({type:d.z.literal("chat-gateway").default("chat-gateway"),channel:Dt,bridgeId:Z.optional(),chatSessionId:d.z.string().trim().min(1).max(240),userId:d.z.string().trim().max(240).optional()}).strict(),message:d.z.object({text:d.z.string().trim().max(2e4).default(""),attachments:d.z.array(w).max(6).default([])}).strict().superRefine((t,e)=>{t.attachments.reduce((t,e)=>t+e.size,0)>g&&e.addIssue({code:d.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),target:d.z.object({instanceId:Z,aiSessionId:d.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),d.z.object({id:Z,instanceId:Z,projectId:Z.optional(),aiSessionId:d.z.string().trim().min(1).max(120),providerSessionId:d.z.string().trim().max(240).optional(),result:d.z.string().default(""),timeoutMs:d.z.number().positive().optional(),source:d.z.string().trim().max(120).optional(),kind:d.z.enum(["task","approval"]).default("task"),status:d.z.enum(["pending","unreachable"]).default("pending"),error:d.z.string().trim().max(2048).optional(),lastSeenAt:X}).strict();class Ut{runCommand;publishHost;constructor(t=Lt,e={}){this.runCommand=t,this.publishHost=e.publishHost||"127.0.0.1"}async start(t){Kt(t);const e=t.instance.runtime.containerName||Ht(t.instance.id);if(await this.runCommand("docker",["start",e]).catch(()=>{}))return this.runningResult(t,e,t.instance.runtime.containerId);await this.ensureImageAvailable(t.image);const n=await this.runCommand("docker",function(t,e,n={}){const r=n.publishHost||"127.0.0.1",o=t.node?.id||"node_unset",i=t.runtime?.id||"runtime_local_docker",s=["run","-d","--name",e,"--label",`task-handoff.instance-id=${t.instance.id}`,"--label",`task-handoff.project-id=${t.project.id}`,"--label",`task-handoff.node-id=${o}`,"--label",`task-handoff.runtime-id=${i}`,"--label",`task-handoff.image-id=${t.image.id}`,"--shm-size","1gb","--security-opt","seccomp=unconfined","--network","bridge","--add-host","host.docker.internal:host-gateway","--tmpfs","/tmp:rw,mode=1777","-v",`${e}-data:/data`,"-v",`${e}-agent-home:/home/agent`,"-p",`${r}::8080`,"-e","TASK_HANDOFF_CONTROL_MODE=controlled","-e",`TASK_HANDOFF_NODE_AGENT_URL=${t.nodeAgentUrl||t.node?.endpoint||""}`,"-e",`TASK_HANDOFF_INSTANCE_ID=${t.instance.id}`,"-e",`TASK_HANDOFF_INSTANCE_NAME=${t.instance.name}`,"-e",`TASK_HANDOFF_REGISTRATION_TOKEN=${t.instance.registrationToken||""}`,"-e",`TASK_HANDOFF_PROJECT_ID=${t.project.id}`,"-e",`TASK_HANDOFF_NODE_ID=${o}`,"-e",`TASK_HANDOFF_RUNTIME_ID=${i}`,"-e",`TASK_HANDOFF_IMAGE_ID=${t.image.id}`,"-e","TASK_HANDOFF_CHAT_BRIDGES=none","-e",`TASK_HANDOFF_WORKSPACE=${t.project.workspacePolicy.path||"/workspace"}`,"-e",`TASK_HANDOFF_WORKSPACE_MODE=${t.project.workspacePolicy.mode}`];"local-folder"===t.project.source.type?s.push("-v",`${t.project.source.path}:${t.project.workspacePolicy.path||"/workspace"}:${t.project.workspacePolicy.readOnly?"ro":"rw"}`):(s.push("-e",`TASK_HANDOFF_PROJECT_SOURCE=${JSON.stringify(t.project.source)}`),s.push("-e",`TASK_HANDOFF_WORKSPACE_POLICY=${JSON.stringify(t.project.workspacePolicy)}`),s.push("-e",`TASK_HANDOFF_GIT_URL=${t.project.source.url}`),t.project.source.ref?.commit?s.push("-e",`TASK_HANDOFF_GIT_COMMIT=${t.project.source.ref.commit}`):t.project.source.ref?.name&&s.push("-e",`TASK_HANDOFF_GIT_REF=${t.project.source.ref.name}`),t.project.source.clone?.depth&&s.push("-e",`TASK_HANDOFF_GIT_DEPTH=${t.project.source.clone.depth}`),s.push("-e","TASK_HANDOFF_GIT_SUBMODULES="+(t.project.source.clone?.submodules?"true":"false")),s.push("-e","TASK_HANDOFF_GIT_LFS="+(t.project.source.clone?.lfs?"true":"false")),t.project.source.clone?.subdirectory&&s.push("-e",`TASK_HANDOFF_WORKSPACE_SUBDIRECTORY=${t.project.source.clone.subdirectory}`));for(const[e,n]of Object.entries(t.image.defaultEnv))s.push("-e",`${e}=${n}`);for(const[e,n]of Object.entries(t.modelEnv||{}))s.push("-e",`${e}=${n}`);return s.push(t.image.image),s}(t,e,{publishHost:this.publishHost}));return this.runningResult(t,e,n.stdout||void 0)}async runningResult(t,e,n){const r=await this.containerPort(e,"8080/tcp"),o=r?`http://127.0.0.1:${r}`:void 0,i=t.node?.id||"node_unset",s=t.runtime?.id||"runtime_local_docker";return{status:"registering",health:"unknown",connectionStatus:o?"online":"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:o?"reachable":"unknown",...o?{web:o,api:`${o}/api`}:{}},workspace:{mode:t.project.workspacePolicy.mode,status:"pending",path:t.project.workspacePolicy.path},runtime:{...t.instance.runtime,kind:"docker",containerName:e,...n?{containerId:n}:{},labels:{...t.instance.runtime.labels,"task-handoff.instance-id":t.instance.id,"task-handoff.project-id":t.project.id,"task-handoff.node-id":i,"task-handoff.runtime-id":s,"task-handoff.image-id":t.image.id}}}}async stop(t){return await this.runCommand("docker",["stop",t.instance.runtime.containerName||Ht(t.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...t.instance.target,status:"unknown"},runtime:t.instance.runtime}}async restart(t){Kt(t);const e=t.instance.runtime.containerName||Ht(t.instance.id);return await this.runCommand("docker",["restart",e]),this.runningResult(t,e,t.instance.runtime.containerId)}async delete(t){return await this.runCommand("docker",["rm","-f",t.instance.runtime.containerName||Ht(t.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...t.instance.target,status:"unknown"},runtime:t.instance.runtime}}async containerPort(t,e){const n=(await this.runCommand("docker",["port",t,e]).catch(()=>({stdout:"",stderr:""}))).stdout.split(/\r?\n/).find(Boolean),r=n?.match(/:(\d+)$/);return r?.[1]}async ensureImageAvailable(t){if(!await this.runCommand("docker",["image","inspect",t.image]).then(()=>!0,()=>!1)){if("local"===t.registry){const e=new Error(`Local Docker image ${t.image} was not found. Build it or add an existing local image before starting an instance.`);throw Object.assign(e,{statusCode:400,code:"LOCAL_IMAGE_NOT_FOUND"}),e}await this.runCommand("docker",["pull",t.image])}}}async function Mt(t=Lt){return(await t("docker",["images","--format","{{json .}}"])).stdout.split(/\r?\n/).map(t=>t.trim()).filter(Boolean).map(t=>{const e=JSON.parse(t),n=e.Repository||"<none>",r=e.Tag||"<none>",o=e.ID||e.IDShort||"",i="<none>"!==n&&"<none>"!==r?`${n}:${r}`:o;return{repository:n,tag:r,id:o,createdSince:e.CreatedSince,size:e.Size,reference:i}}).filter(t=>Boolean(t.reference))}function Ht(t){return`task-handoff-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`}function Kt(t){if("local-folder"===t.project.source.type&&t.project.source.ownerNodeId&&t.project.source.ownerNodeId!==t.node.id){const t=new Error("Local folder projects can only start on the node that owns the folder.");throw Object.assign(t,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_OWNER_NODE"}),t}if(!t.image.image.trim()){const t=new Error("Image profile must reference a registered image.");throw Object.assign(t,{statusCode:400,code:"IMAGE_PROFILE_INVALID"}),t}}const Gt=d.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),qt=d.z.object({type:d.z.enum(["app-session","provider-session","adapter-key"]),id:d.z.string().trim().min(1).max(240),agent:d.z.string().trim().min(1).max(80).optional(),adapter:d.z.string().trim().min(1).max(80).optional(),key:d.z.string().trim().min(1).max(240).optional()}).strict(),Vt=d.z.record(d.z.string(),d.z.unknown()).and(d.z.object({id:d.z.string().trim().min(1).max(160),appId:d.z.string().trim().min(1).max(160).optional(),title:d.z.string().trim().max(240).optional(),kind:d.z.string().trim().max(80).optional(),status:Gt.default("unknown"),bindings:d.z.array(qt).max(40).default([]),workspace:d.z.object({cwd:d.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:d.z.string().datetime().optional(),updatedAt:d.z.string().datetime().optional()})),Bt=d.z.object({runningCount:d.z.number().int().min(0).default(0),problemCount:d.z.number().int().min(0).default(0),sessions:d.z.array(Vt).default([]),updatedAt:d.z.string().datetime()}).strict();d.z.object({streamId:d.z.string().trim().min(1).max(240),revision:d.z.number().int().min(0),lastEventAt:d.z.string().datetime(),snapshot:Bt}).strict(),Bt.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:d.z.number().int().min(0).optional()});const Jt=d.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),Wt=d.z.object({streamId:d.z.string().trim().min(1).max(240),instanceId:d.z.string().trim().min(1).max(160),nodeId:d.z.string().trim().min(1).max(160).optional(),revision:d.z.number().int().min(0),previousRevision:d.z.number().int().min(0).optional(),traceId:d.z.string().trim().min(1).max(160),generatedAt:d.z.string().datetime(),reason:Jt}).strict(),Yt=d.z.object({meta:Wt,snapshot:Bt}).strict(),Qt=d.z.object({meta:Wt,session:Vt}).strict(),Zt=d.z.object({meta:Wt,sessionId:d.z.string().trim().min(1).max(160),tombstone:Vt.optional(),expiresAt:d.z.string().datetime().optional()}).strict();function Xt(t){return t.startsWith("ai-session.")||t.startsWith("ai.session.")?"ai.sessions":t.startsWith("app-session.")?"app.sessions":t.startsWith("receiver.")?"receiver":t.startsWith("conversation.")?"conversations":t.startsWith("trigger.")?"triggers":t.startsWith("channel.")?"channels":t.startsWith("instance.")?"instances":t.startsWith("node.")?"nodes":t.startsWith("project.")?"projects":t.startsWith("model.")?"models":t.startsWith("image.")?"images":"system"}d.z.object({streamId:d.z.string().trim().min(1).max(240),instanceId:d.z.string().trim().min(1).max(160),sinceRevision:d.z.number().int().min(0),latestRevision:d.z.number().int().min(0),earliestRetainedRevision:d.z.number().int().min(0),syncRequired:d.z.boolean().default(!1),events:d.z.array(d.z.discriminatedUnion("type",[d.z.object({type:d.z.literal("app-session.snapshot"),payload:Yt}).strict(),d.z.object({type:d.z.literal("app-session.patch"),payload:Qt}).strict(),d.z.object({type:d.z.literal("app-session.removed"),payload:Zt}).strict()])).default([])}).strict();const te=d.z.enum(["ai.sessions","app.sessions"]),ee=d.z.object({topic:te,instanceId:d.z.string().trim().min(1).max(160),streamId:d.z.string().trim().min(1).max(240),latestRevision:d.z.number().int().min(0),earliestRetainedRevision:d.z.number().int().min(0)}).strict(),ne=d.z.object({protocolVersion:d.z.literal(1),streams:d.z.array(ee)}).strict();class re{attempt=0;timer;get pending(){return Boolean(this.timer)}get attempts(){return this.attempt}schedule(t,e={}){if(this.timer)return;const n=function(t,e=Math.random){const n=Math.min(3e4,1e3*2**Math.max(0,t));return Math.min(3e4,Math.round(n*(.75+.5*e())))}(this.attempt,e.random);this.attempt+=1;const r=e.setTimeoutFn||setTimeout;return this.timer=r(()=>{this.timer=void 0,t()},n),{attempt:this.attempt,delay:n}}reset(t=clearTimeout){this.timer&&t(this.timer),this.timer=void 0,this.attempt=0}cancel(t=clearTimeout){this.timer&&t(this.timer),this.timer=void 0}}class oe{sockets=new Map;socketUrls=new Map;retries=new Map;outputs=new Set;state;token;logger;safetyIntervalMs;lastAiSessionEventByInstance=new Map;lastAppSessionEventByInstance=new Map;timer;reconnectAttempts=0;safetyReconciliations=0;createSocket;setIntervalFn;clearIntervalFn;constructor(t,e,n={}){var r;this.state=t,this.token=e,this.logger=n.logger,this.safetyIntervalMs=(r=n.safetyIntervalMs,Math.min(6e4,Math.max(3e4,r??45e3))),this.createSocket=n.createSocket||((t,e)=>new c(t,e)),this.setIntervalFn=n.setIntervalFn||setInterval,this.clearIntervalFn=n.clearIntervalFn||clearInterval}start(){this.sync(),this.timer=this.setIntervalFn(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(this.clearIntervalFn(this.timer),this.timer=void 0);for(const t of this.sockets.values())t.close();this.sockets.clear(),this.socketUrls.clear();for(const t of this.retries.values())t.timer.cancel();this.retries.clear(),this.outputs.clear()}addOutput(t){return this.outputs.add(t),t.on("close",()=>this.outputs.delete(t)),t.on("error",()=>this.outputs.delete(t)),this.syncNow(),()=>{this.outputs.delete(t)}}syncNow(){this.sync()}diagnostics(){return{reconnectAttempts:this.reconnectAttempts,safetyReconciliations:this.safetyReconciliations,activeConnections:this.sockets.size,pendingRetries:[...this.retries.values()].filter(t=>t.timer.pending).length,safetyIntervalMs:this.safetyIntervalMs}}sync(){if(!this.outputs.size){for(const t of this.sockets.values())t.close();this.sockets.clear(),this.socketUrls.clear();for(const t of this.retries.values())t.timer.cancel();return void this.retries.clear()}const t=new Set;for(const e of this.state.listInstances()){const n=se(e);if(!n)continue;t.add(e.id);const r=this.retries.get(e.id);r?.timer.pending&&r.url!==n&&(r.timer.cancel(),this.retries.delete(e.id)),this.socketUrls.get(e.id)!==n&&this.sockets.has(e.id)&&(this.sockets.get(e.id)?.close(),this.sockets.delete(e.id)),this.sockets.has(e.id)||this.retries.get(e.id)?.timer.pending||this.connect(e.id,n)}for(const[e,n]of this.sockets)if(!t.has(e)){n.close(),this.sockets.delete(e),this.socketUrls.delete(e);const t=this.retries.get(e);t?.timer.cancel(),this.retries.delete(e)}for(const[e,n]of this.retries)t.has(e)||(n.timer.cancel(),this.retries.delete(e),this.socketUrls.delete(e))}connect(t,e){const n=this.token?{authorization:`Bearer ${this.token}`}:void 0,r=this.createSocket(e,{headers:n});this.sockets.set(t,r),this.socketUrls.set(t,e),r.on("open",()=>{const n=this.retries.get(t);n?.timer.reset(),this.retries.set(t,{timer:n?.timer||new re,url:e}),r.readyState===c.OPEN&&(r.send(JSON.stringify({v:1,type:"subscribe",topics:[h,"app.sessions","receiver","instances"]})),this.logger?.info?.({instanceId:t,url:e},"ai-session.event.forward.connect"),this.logger?.info?.({instanceId:t,url:e},"app-session.event.forward.connect"))}),r.on("message",e=>{const n=function(t,e){try{const n=JSON.parse(String(t));if(!n||"object"!=typeof n||Array.isArray(n))return;const r=n,o="string"==typeof r.type?r.type:"";if(!o||"events.connected"===o)return;const i=r.scope&&"object"==typeof r.scope&&!Array.isArray(r.scope)?r.scope:{};return{v:1,id:"string"==typeof r.id?r.id:`evt_${Date.now().toString(36)}`,seq:Number(r.seq)||0,type:o,topic:"string"==typeof r.topic?r.topic:Xt(o),createdAt:"string"==typeof r.createdAt?r.createdAt:(new Date).toISOString(),payload:"payload"in r?r.payload:{},scope:{...i,instanceId:e}}}catch{return}}(e,t);if(!n)return;if("streams.hello"===n.type){const e=ne.safeParse(n.payload);if(!e.success)return this.logger?.warn?.({instanceId:t,issues:e.error.issues},"session-stream.handshake.invalid"),void r.close(1002,"Incompatible session stream handshake.");const o=JSON.stringify({type:"node-agent.streams.hello",instanceId:t,payload:e.data});for(const t of this.outputs)t.readyState===c.OPEN&&t.send(o);return}this.recordAiSessionEvent(t,n),this.recordAppSessionEvent(t,n);const o=JSON.stringify({type:"node-agent.event.forwarded",event:n});for(const t of this.outputs)t.readyState===c.OPEN&&t.send(o)}),r.on("close",()=>{this.sockets.get(t)===r&&(this.sockets.delete(t),this.socketUrls.delete(t),this.scheduleReconnect(t))}),r.on("error",()=>{this.logger?.warn?.({instanceId:t,url:e},"ai-session.event.forward.error"),this.logger?.warn?.({instanceId:t,url:e},"app-session.event.forward.error"),r.close()})}scheduleReconnect(t){const e=this.state.listInstances().find(e=>e.id===t),n=e&&se(e);if(!n||!this.outputs.size)return;const r=this.retries.get(t)??{timer:new re};if(r.timer.pending)return;r.url=n;const o=r.timer.schedule(()=>{this.sockets.has(t)||this.connect(t,n)});o&&(this.reconnectAttempts+=1,this.logger?.info?.({instanceId:t,url:n,attempt:o.attempt,delay:o.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.connection.reconnect-scheduled")),this.retries.set(t,r)}recordAiSessionEvent(t,e){if(e.topic!==h)return;const n=ie(e.payload),r=this.lastAiSessionEventByInstance.get(t);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:t,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"ai-session.event.forward.gap"),this.lastAiSessionEventByInstance.set(t,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:t,traceId:n.traceId,eventType:e.type,streamId:n.streamId,revision:n.revision},"ai-session.event.forward.message")}recordAppSessionEvent(t,e){if("app.sessions"!==e.topic)return;const n=ie(e.payload),r=this.lastAppSessionEventByInstance.get(t);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:t,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"app-session.event.forward.gap"),this.lastAppSessionEventByInstance.set(t,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:t,traceId:n.traceId,eventType:e.type,streamId:n.streamId,revision:n.revision},"app-session.event.forward.message")}}function ie(t){const e=t&&"object"==typeof t&&!Array.isArray(t)?t:{},n=e.meta&&"object"==typeof e.meta&&!Array.isArray(e.meta)?e.meta:{};return{streamId:"string"==typeof n.streamId?n.streamId:void 0,revision:"number"==typeof n.revision?n.revision:void 0,traceId:"string"==typeof n.traceId?n.traceId:void 0}}function se(t){const e=t.target?.api||t.target?.web;if(e)try{const t=new URL("/api/events",e);return t.protocol="https:"===t.protocol?"wss:":"ws:",t.toString()}catch{return}}function ae(t){try{return e.statSync(t).isDirectory()}catch{return!1}}function ce(t,r){let o;try{o=e.statSync(t)}catch{return}if(!o.isDirectory())return;const i=n.resolve(t),s=n.basename(i)||i;let a=[];try{a=r>0?e.readdirSync(i,{withFileTypes:!0}):[]}catch{a=[]}const c=r>0?a.filter(t=>t.isDirectory()||t.isSymbolicLink()&&ae(n.join(i,t.name))).sort((t,e)=>t.name.localeCompare(e.name)).slice(0,80).flatMap(t=>{const e=ce(n.join(i,t.name),r-1);return e?[e]:[]}):[];return{name:s,path:i,children:c}}function de(t){return(t.path?[t.path]:function(){if("win32"!==process.platform)return[n.parse(n.resolve(process.cwd())).root];const t=n.parse(n.resolve(process.cwd())).root,e=Array.from({length:26},(t,e)=>`${String.fromCharCode(65+e)}:\\`);return[...new Set([t,...e])].filter(ae)}()).flatMap(e=>{const n=ce(e,t.depth);return n?[n]:[]})}const le=d.z.object({path:d.z.string().trim().min(1).max(2048),method:d.z.string().trim().min(1).max(20).default("GET"),headers:d.z.record(d.z.string(),d.z.string()).default({}),body:d.z.string().optional(),bodyBase64:d.z.string().optional()}).strict(),ue=d.z.object({id:d.z.string().trim().min(1).max(120).optional(),name:d.z.string().trim().min(1).max(160),path:d.z.string().trim().min(1).max(4096),defaultImageId:d.z.string().trim().min(1).max(120).optional(),modelSelection:nt,labels:d.z.record(d.z.string(),d.z.string()).default({})}).strict(),pe=d.z.object({path:d.z.string().trim().min(1).max(4096).optional(),depth:d.z.coerce.number().int().min(0).max(1).default(0)}).strict(),me=d.z.object({id:d.z.string().trim().min(1).max(120).optional(),name:d.z.string().trim().min(1).max(160).optional(),runtimeId:d.z.string().trim().min(1).max(120),imageId:d.z.string().trim().min(1).max(120).optional(),projectId:d.z.string().trim().min(1).max(120).optional(),image:It.optional(),source:gt,sourceSnapshot:d.z.record(d.z.string(),d.z.unknown()).default({}),config:Et.shape.config.optional(),modelEnv:d.z.record(d.z.string(),d.z.string()).default({})}).strict(),ge=d.z.object({name:d.z.string().trim().min(1).max(160).optional()}).strict(),he=d.z.object({id:d.z.string().trim().min(1).max(120).optional(),name:d.z.string().trim().min(1).max(160),type:d.z.enum(["docker","kubernetes","local"]),status:d.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:d.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).optional(),capabilities:d.z.record(d.z.string(),d.z.unknown()).default({}),labels:d.z.record(d.z.string(),d.z.string()).default({})}).strict(),fe=he.omit({id:!0}).partial().strict();function ye(t=function(){return process.env.TASK_HANDOFF_NODE_AGENT_DATA_DIR||n.join(u.homedir(),".config","task-handoff","node-agent")}()){const e=n.resolve(t);return{dataDir:e,identityPath:n.join(e,"identity.json"),settingsPath:n.join(e,"runtime-settings.json"),localFoldersDir:n.join(e,"local-folders"),nodeRuntimesDir:n.join(e,"node-runtimes"),controlledInstancesDir:n.join(e,"controlled-instances"),updatesDir:n.join(e,"updates"),logsDir:n.join(e,"logs")}}function Ie(t){e.mkdirSync(t,{recursive:!0})}function ze(t,e){Ie(n.dirname(t)),p.sync(t,`${JSON.stringify(e,null,2)}\n`,{encoding:"utf8"})}function be(t,e){console.warn(JSON.stringify({message:t,...e}))}function Ae(t){return t&&"object"==typeof t&&"issues"in t?{issues:t.issues}:{error:t instanceof Error?t.message:String(t)}}function we(t,n){const r=n.logger||be;let o;try{o=JSON.parse(e.readFileSync(t,"utf8"))}catch(e){return void r("stored JSON could not be read",{filePath:t,...Ae(e)})}if(!n.schema)return o;const i=n.sanitize?n.sanitize(o):o,s=n.schema.safeParse(i);if(s.success)return s.data;const a="strip"in n.schema&&"function"==typeof n.schema.strip?n.schema.strip().safeParse(i):void 0;if(a?.success)return r("unknown stored fields were ignored",{filePath:t,issues:s.error.issues}),a.data;r("stored record failed validation and was isolated",{filePath:t,issues:s.error.issues})}function Se(t){return`${t}_${l.randomUUID().replace(/-/g,"").slice(0,20)}`}function ke(){return l.randomBytes(24).toString("base64url")}class Ne{directory;options;constructor(t,e={}){this.directory=t,this.options=e}init(){Ie(this.directory)}filePath(t){return n.join(this.directory,`${t}.json`)}list(){return this.init(),e.readdirSync(this.directory).filter(t=>t.endsWith(".json")).sort().flatMap(t=>{const e=we(n.join(this.directory,t),this.options);return e?[e]:[]})}get(t){const n=this.filePath(t);return e.existsSync(n)?we(n,this.options):void 0}put(t){const e=this.options.sanitize?this.options.sanitize(t):t,n=this.options.schema?this.options.schema.parse(e):e;return ze(this.filePath(n.id),n),n}patch(t,e){const n=this.get(t);if(!n)return;const r={...n,...e,id:n.id,createdAt:n.createdAt,updatedAt:(new Date).toISOString()};return this.put(r)}delete(t){const n=this.filePath(t);return!!e.existsSync(n)&&(e.unlinkSync(n),!0)}}class _e{filePathValue;defaults;options;constructor(t,e,n={}){this.filePathValue=t,this.defaults=e,this.options=n}init(){Ie(n.dirname(this.filePathValue)),e.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),e.existsSync(this.filePathValue)?we(this.filePathValue,this.options)??this.defaults():this.defaults()}put(t){const e=this.options.sanitize?this.options.sanitize(t):t,n=this.options.schema?this.options.schema.parse(e):e;return ze(this.filePathValue,n),n}}class ve extends Error{code;lockPath;owner;constructor(t,e,n="process"){super(`${"control-plane"===n?"Control plane":"node-agent"===n?"Node agent":"Process"} is already running${e?.pid?` pid ${e.pid}`:""}. Lock: ${t}`),this.name="ProcessSingletonError",this.code="control-plane"===n?"CONTROL_PLANE_ALREADY_RUNNING":"node-agent"===n?"NODE_AGENT_ALREADY_RUNNING":"PROCESS_ALREADY_RUNNING",this.lockPath=t,this.owner=e}}function Ee(t){return n.join(t,"owner.json")}function Oe(t){try{return JSON.parse(e.readFileSync(Ee(t),"utf8"))}catch{return}}function xe(t){if(!Number.isInteger(t)||Number(t)<=0)return!1;try{return process.kill(Number(t),0),!0}catch(t){return"EPERM"===t.code}}function Te(t,n){e.writeFileSync(Ee(t),`${JSON.stringify(n,null,2)}\n`,"utf8")}function Ce(t,e=process.platform){const r=n.resolve(t),o=l.createHash("sha256").update(r).digest("hex").slice(0,16);return"win32"===e?`\\\\.\\pipe\\task-handoff-node-agent-${o}`:n.join("/tmp",`task-handoff-node-agent-${process.getuid?.()??"user"}`,`${o}.sock`)}const je="TASK_HANDOFF_NODE_AGENT_V1",De=6e4;function Re(t=""){return l.createHash("sha256").update(t).digest("hex")}function Pe(t,e){return l.createHmac("sha256",t).update(function(t){return[je,t.keyId,t.method.toUpperCase(),t.pathWithQuery,t.timestamp,t.nonce,t.bodySha256].join("\n")}(e)).digest("hex")}function Fe(t){const e=t.timestamp||(new Date).toISOString(),n=t.nonce||l.randomBytes(18).toString("base64url"),r=Re(t.body||""),o=Pe(t.secret,{keyId:t.keyId,method:t.method,pathWithQuery:t.pathWithQuery,timestamp:e,nonce:n,bodySha256:r});return{"x-taskhandoff-node-id":t.nodeId,"x-taskhandoff-key-id":t.keyId,"x-taskhandoff-timestamp":e,"x-taskhandoff-nonce":n,"x-taskhandoff-body-sha256":r,"x-taskhandoff-signature":o}}const $e=new Set(["DELETE","GET","HEAD","PATCH","POST","PUT","OPTIONS"]);function Le(){return(new Date).toISOString()}function Ue(t,e){if(!t)return!0;const n=m.valid(t),r=m.valid(e);return n&&r?m.gt(r,n):t!==e}async function Me(t,e,n){const r=Ge(n),o=await t(He(),["view",`${e}@${r}`,"version","--json"]).catch(t=>{if(!function(t){if(!t||"object"!=typeof t)return!1;const e=t,n=[e.message,e.details?.stdout,e.details?.stderr].filter(t=>"string"==typeof t).join("\n");return/\bE404\b/i.test(n)}(t))throw t});if(!o)return;const i=JSON.parse(o.stdout);if("string"!=typeof i)throw new Error(`npm did not resolve ${e}@${r} to one version.`);return i}function He(){return process.env.TASK_HANDOFF_NPM_COMMAND||"npm"}function Ke(t){return at.parse({target:t.target,source:"npm",channel:t.channel,currentVersion:t.currentVersion,availableVersion:t.currentVersion||"unavailable",updateAvailable:!1,reason:`No ${t.channel} release is currently published.`,checkedAt:Le()})}function Ge(t){return"stable"===t?"latest":t}function qe(t){if(Array.isArray(t))return t.map(qe).find(Boolean);if(!t||"object"!=typeof t)return;const e=t;return"string"==typeof e.digest&&e.digest.startsWith("sha256:")?e.digest:"string"==typeof e.Digest&&e.Digest.startsWith("sha256:")?e.Digest:Object.values(e).map(qe).find(Boolean)}class Ve{records;constructor(t){this.records=new Ne(t.updatesDir,{schema:ct})}init(){this.records.init()}list(){return this.records.list().sort((t,e)=>e.createdAt.localeCompare(t.createdAt))}create(t,e){const n=Le();return this.records.put(ct.parse({id:Se("update"),nodeId:t,target:e.target,source:e.source,channel:e.channel,fromVersion:e.currentVersion,toVersion:e.availableVersion,artifactRef:e.artifactRef,status:"queued",createdAt:n,updatedAt:n}))}patch(t,e){const n=this.records.get(t);if(!n)throw Object.assign(new Error(`Update job ${t} was not found.`),{statusCode:404,code:"UPDATE_JOB_NOT_FOUND"});return this.records.put(ct.parse({...n,...e,id:t,createdAt:n.createdAt,updatedAt:Le()}))}run(t,e){(async()=>{this.patch(t.id,{status:"updating",startedAt:Le(),error:void 0});try{await e(t),this.patch(t.id,{status:"succeeded",completedAt:Le()})}catch(e){this.patch(t.id,{status:"failed",error:e instanceof Error?e.message:String(e),completedAt:Le()})}})()}}const Be=new Set(["content-encoding","content-length","transfer-encoding"]),Je=["codex","claude"],We=d.z.object({version:d.z.literal(1),externalListener:At}).strict(),Ye=d.z.object({controlPlaneName:d.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:d.z.string().trim().max(2048).optional(),expiresInMs:d.z.number().int().positive().max(36e5).optional()}).strict(),Qe=d.z.object({joinToken:d.z.string().trim().min(1).max(4096),controlPlaneId:d.z.string().trim().min(1).max(160).optional(),controlPlaneName:d.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:d.z.string().trim().max(2048).optional()}).strict(),Ze=d.z.object({modelEnv:d.z.record(d.z.string(),d.z.string()).default({})}).strict().default({modelEnv:{}}),Xe=dt.extend({modelEnv:d.z.record(d.z.string(),d.z.string()).default({})}).strict(),tn=d.z.object({controlPlaneUrl:d.z.string().trim().url().max(2048),joinToken:d.z.string().trim().min(1).max(4096),controlPlaneName:d.z.string().trim().min(1).max(160).optional(),activate:d.z.boolean().optional()}).strict();function en(t){const e=process.env[t]?.trim();return e||void 0}function nn(){try{const t=n.resolve(__dirname,"..","package.json");return JSON.parse(e.readFileSync(t,"utf8")).version||"unknown"}catch{return"unknown"}}function rn(t){return t.bodyBase64?Buffer.from(t.bodyBase64,"base64"):t.body}function on(t){const e=t.authorization;return"string"==typeof e&&e.startsWith("Bearer ")?e.slice(7).trim():void 0}function sn(t){if("string"!=typeof t)return!1;const e=t.trim().toLowerCase();return"localhost"===e||"127.0.0.1"===e||"::1"===e||"::ffff:127.0.0.1"===e}function an(t){return!t.ip&&!t.socket.remoteAddress&&!t.socket.remoteFamily}function cn(){return(new Date).toISOString()}function dn(t){return"all-ipv4"===t?"0.0.0.0":"127.0.0.1"}function ln(t){if(!t||"object"!=typeof t||Array.isArray(t))return t;const e=t,n=e.externalListener&&"object"==typeof e.externalListener&&!Array.isArray(e.externalListener)?e.externalListener:{},r=Object.keys(e).filter(t=>"version"!==t&&"externalListener"!==t),o=Object.keys(n).filter(t=>"bindScope"!==t&&"port"!==t);return(r.length||o.length)&&console.warn(JSON.stringify({message:"unknown stored node agent runtime setting fields were ignored",filePath:"runtime-settings.json",fields:[...r,...o.map(t=>`externalListener.${t}`)]})),{version:e.version,externalListener:{bindScope:n.bindScope,port:n.port}}}function un(t){try{return function(t){if(!t||"object"!=typeof t)return;const e=t,n="string"==typeof e.nodeId&&e.nodeId.trim()?e.nodeId.trim():void 0;return n?{nodeId:n,createdAt:"string"==typeof e.createdAt?e.createdAt:cn(),updatedAt:"string"==typeof e.updatedAt?e.updatedAt:cn(),pairingInvites:Array.isArray(e.pairingInvites)?e.pairingInvites.filter(t=>{if(!t||"object"!=typeof t)return!1;const e=t;return"string"==typeof e.tokenHash&&"string"==typeof e.expiresAt&&"string"==typeof e.createdAt}):[],remoteControlPlanes:Array.isArray(e.remoteControlPlanes)?e.remoteControlPlanes.filter(t=>{if(!t||"object"!=typeof t)return!1;const e=t;return"string"==typeof e.id&&"string"==typeof e.keyId&&"string"==typeof e.secret&&"string"==typeof e.pairedAt&&"string"==typeof e.updatedAt}).map(t=>({...t,active:"boolean"!=typeof t.active||t.active})):[]}:void 0}(JSON.parse(e.readFileSync(t.identityPath,"utf8")))}catch{return}}function pn(t,r){e.mkdirSync(n.dirname(t.identityPath),{recursive:!0}),e.writeFileSync(t.identityPath,`${JSON.stringify({...r,updatedAt:cn()},null,2)}\n`,{mode:384})}function mn(t,e){const n=un(t);pn(t,{...n||{createdAt:cn()},nodeId:e,updatedAt:cn(),pairingInvites:n?.pairingInvites||[],remoteControlPlanes:n?.remoteControlPlanes||[]})}function gn(t,e){if(e?.trim())return mn(t,e.trim()),e.trim();const n=un(t);if(n?.nodeId)return n.nodeId;const r=Se("node");return mn(t,r),r}function hn(t){const e=Date.now();return{...t,pairingInvites:(t.pairingInvites||[]).filter(t=>Date.parse(t.expiresAt)>e)}}function fn(t,e=""){return(un(t)?.remoteControlPlanes||[]).map(({secret:t,...n})=>({...n,current:Boolean(e&&n.keyId===e)})).sort((t,e)=>Number(e.current)-Number(t.current)||e.updatedAt.localeCompare(t.updatedAt))}function yn(t,e,n=""){if(n&&e===n){const t=new Error("Cannot delete the key currently used by this control-plane connection.");throw Object.assign(t,{statusCode:409,code:"NODE_AGENT_REMOTE_KEY_IN_USE"}),t}const r=un(t);if(!r)return!1;const o=(r.remoteControlPlanes||[]).filter(t=>t.keyId!==e);return o.length!==(r.remoteControlPlanes||[]).length&&(pn(t,{...r,remoteControlPlanes:o}),!0)}function In(t,e,n){return[...(un(t)?.remoteControlPlanes||[]).map(t=>({keyId:t.keyId,secret:t.secret})),...e&&n?[{keyId:n,secret:e}]:[]]}function zn(t){return"local-folder"===t.type?ht.parse({mode:"local-bind",path:"/workspace",readOnly:!1}):ht.parse({mode:"git-clone",path:"/workspace",readOnly:!1})}function bn(t){const e=gt.parse(t.source),n=t.projectId||("local-folder"===e.type?e.localFolderId:void 0)||("git-repository"===e.type?e.repositoryId:void 0)||("git-template"===e.type?e.templateId:void 0)||`project_${t.id}`;return yt.parse({id:n,name:"string"==typeof t.sourceSnapshot.name?t.sourceSnapshot.name:t.name,source:e,defaultImageId:t.imageId,defaultNodeId:t.nodeId,defaultRuntimeId:t.runtimeId,modelSelection:t.sourceSnapshot.modelSelection&&"object"==typeof t.sourceSnapshot.modelSelection&&!Array.isArray(t.sourceSnapshot.modelSelection)?t.sourceSnapshot.modelSelection:{},workspacePolicy:zn(e),labels:{},createdAt:t.createdAt,updatedAt:t.updatedAt})}async function An(t,e){if(!e.target.web)return"unknown";const n=new AbortController,r=setTimeout(()=>n.abort(),2e3);try{return(await t(`${kn(e)}/api/health`,{signal:n.signal})).ok?"reachable":"endpoint-unreachable"}catch{return"endpoint-unreachable"}finally{clearTimeout(r)}}async function wn(t,e,n,r){const o=new AbortController,i=setTimeout(()=>o.abort(),r);try{return await t(e,{...n,signal:o.signal})}finally{clearTimeout(i)}}async function Sn(t,e,n,r){if(!e.config.autoImportAgentConfigs)return void r.diagnostic({instanceId:e.id,action:n},"node instance config auto-import skipped");if("reachable"!==e.targetStatus)return;const o=kn(e),i=Number(process.env.TASK_HANDOFF_CONFIG_AUTO_IMPORT_TIMEOUT_MS||5e3);for(const s of Je)try{const a=await wn(t,`${o}/api/config-sync/import/${encodeURIComponent(s)}`,{method:"POST"},Number.isFinite(i)&&i>0?i:5e3);if(!a.ok){r.warn({instanceId:e.id,action:n,preset:s,statusCode:a.status},"node instance config auto-import failed");continue}r.diagnostic({instanceId:e.id,action:n,preset:s},"node instance config auto-import completed")}catch(t){r.warn({instanceId:e.id,action:n,preset:s,error:t instanceof Error?t.message:String(t)},"node instance config auto-import failed")}}function kn(t){const e=t.target.web;if(!e){const e=new Error(`Instance ${t.id} does not have a web endpoint.`);throw Object.assign(e,{statusCode:409,code:"NODE_INSTANCE_WEB_ENDPOINT_MISSING"}),e}if("direct-port"!==t.target.strategy)return e.replace(/\/$/,"");try{const t=new URL(e);return t.port?(t.hostname=process.env.TASK_HANDOFF_NODE_AGENT_PROXY_HOST||"127.0.0.1",t.toString().replace(/\/$/,"")):e.replace(/\/$/,"")}catch{return e.replace(/\/$/,"")}}function Nn(t,e){const n=new Error(e);throw Object.assign(n,{statusCode:403,code:t}),n}function _n(t,e){if(t===Q)return;const n=new Error(`${e} protocol version ${t||"missing"} is not compatible with ${Q}.`);throw Object.assign(n,{statusCode:409,code:"PROTOCOL_VERSION_MISMATCH"}),n}function vn(t={}){return{requiresImage:!1,supportsControlledInstanceApi:!0,supportsContainerLifecycle:!1,supportsAppSessions:!0,supportsHostSessions:!0,artifactKind:"none",isolation:"none",...t}}function En(t){return new Promise(e=>{const n=o.createServer();n.once("error",()=>e(!1)),n.listen({host:"127.0.0.1",port:t},()=>{n.close(()=>e(!0))})})}function On(t,e=3e3){return null!==t.exitCode||null!==t.signalCode?Promise.resolve():new Promise(n=>{const r=setTimeout(()=>{null===t.exitCode&&null===t.signalCode&&t.kill("SIGKILL"),n()},e);r.unref(),t.once("exit",()=>{clearTimeout(r),n()})})}const xn=new Set(["provisioning","starting","registering","registered","running"]);async function Tn(t,e){try{const n=await t(e,["--version"]);return{available:!0,command:e,version:(n.stdout||n.stderr).split(/\r?\n/)[0]?.trim()||void 0}}catch{return{available:!1}}}class Cn{executor;constructor(t){this.executor=t}start(t){return this.executor.start(t)}stop(t){return this.executor.stop(t)}restart(t){return this.executor.restart(t)}delete(t){return this.executor.delete(t)}}class jn{runCommand;paths;nodeAgentUrl;processByInstanceId=new Map;constructor(t,e,n){this.runCommand=t,this.paths=e,this.nodeAgentUrl=n}async start(t){const r=function(t){if("local-folder"!==t.source.type){const t=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(t,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),t}return n.resolve(t.source.path)}(t.instance),o=t.instance.runtime.port||await async function(){const t=Number(process.env.TASK_HANDOFF_LOCAL_INSTANCE_PORT_START||19e3),e=Number.isInteger(t)&&t>0?t:19e3;for(let t=e;t<e+1e3&&t<=65535;t+=1)if(await En(t))return t;const n=new Error(`No free localhost port found in range ${e}-${Math.min(e+999,65535)}.`);throw Object.assign(n,{statusCode:503,code:"LOCAL_INSTANCE_PORT_UNAVAILABLE"}),n}(),s=n.join(this.paths.dataDir,"local-instances",t.instance.id),a=n.join(s,"logs");e.mkdirSync(a,{recursive:!0});const[c,...d]=function(){const t=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND?.trim();if(t)return t.split(/\s+/);const r=n.resolve(process.cwd(),"bin","task-handoff.js");return e.existsSync(r)?[process.execPath,r,"web"]:["task-handoff-controlled-instance","web"]}(),l=[...d,"--host","127.0.0.1","--port",String(o)],u=e.openSync(n.join(a,"controlled-instance.out.log"),"a"),p=e.openSync(n.join(a,"controlled-instance.err.log"),"a"),m=i.spawn(c,l,{cwd:r,detached:!1,stdio:["ignore",u,p],env:{...process.env,TASK_HANDOFF_CONTROL_MODE:"controlled",TASK_HANDOFF_NODE_AGENT_URL:this.nodeAgentUrl(),TASK_HANDOFF_INSTANCE_ID:t.instance.id,TASK_HANDOFF_INSTANCE_NAME:t.instance.name,TASK_HANDOFF_REGISTRATION_TOKEN:t.instance.registrationToken||"",TASK_HANDOFF_PROJECT_ID:t.project.id,TASK_HANDOFF_NODE_ID:t.node.id,TASK_HANDOFF_RUNTIME_ID:t.runtime.id,TASK_HANDOFF_WORKSPACE:r,TASK_HANDOFF_WORKSPACE_MODE:"local-bind",TASK_HANDOFF_DATA_DIR:s,TASK_HANDOFF_LOG_DIR:a,TASK_HANDOFF_APP_SESSION_PERSIST:"1",TASK_HANDOFF_CODEX_APP_SERVER:process.env.TASK_HANDOFF_CODEX_APP_SERVER||"1",TASK_HANDOFF_WEB_PORT:String(o),TASK_HANDOFF_WEB_HOST:"127.0.0.1"}});e.closeSync(u),e.closeSync(p);try{await function(t){return new Promise((e,n)=>{const r=()=>{t.off("error",o),e()},o=e=>{t.off("spawn",r),n(e)};t.once("spawn",r),t.once("error",o)})}(m)}catch(t){const o=t instanceof Error?t.message:String(t);try{e.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] spawn failed command=${c} cwd=${r} error=${o}\n`)}catch{}throw Object.assign(new Error(`Controlled instance process could not start with command ${c} in ${r}: ${o}`),{statusCode:500,code:"LOCAL_INSTANCE_PROCESS_SPAWN_FAILED"})}this.processByInstanceId.set(t.instance.id,m),m.on("error",t=>{try{e.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] process error pid=${m.pid??""} error=${t.message}\n`)}catch{}}),m.once("exit",(r,o)=>{this.processByInstanceId.delete(t.instance.id);try{e.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] exited pid=${m.pid??""} code=${r??""} signal=${o??""}\n`)}catch{}});const g=`http://127.0.0.1:${o}`;return await async function(t,e=3e3){const n=Date.now()+e;for(;Date.now()<n;){try{if((await fetch(`${t}/api/health`)).ok)return!0}catch{}await new Promise(t=>setTimeout(t,100))}return!1}(g),{status:"registering",health:"unknown",connectionStatus:"online",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:"unknown",web:g,api:`${g}/api`},workspace:{mode:"local-bind",status:"pending",path:r},runtime:{kind:"local",workspacePath:r,pid:m.pid,port:o,labels:{...t.instance.runtime.labels,"task-handoff.runtime-kind":"local"}}}}async stop(t){return await async function(t,e){const n=e.get(t.id);if(n&&!n.killed)return n.kill("SIGTERM"),e.delete(t.id),void await On(n);if(t.runtime.pid)try{process.kill(t.runtime.pid,"SIGTERM")}catch{}}(t.instance,this.processByInstanceId),{status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...t.instance.target,status:"unknown"},runtime:t.instance.runtime}}async restart(t){return this.start(t)}async delete(t){return this.stop(t)}async check(t){const[e,n]=await Promise.all([Tn(this.runCommand,"codex"),Tn(this.runCommand,"claude")]);return{status:"online",capabilities:vn({...t.capabilities,apps:{terminal:!0,codex:e,claude:n}})}}async stopAll(){const t=Array.from(this.processByInstanceId.values());for(const t of this.processByInstanceId.values())t.killed||t.kill("SIGTERM");this.processByInstanceId.clear(),await Promise.all(t.map(t=>On(t)))}}class Dn{docker;local;constructor(t,e){this.docker=t,this.local=e}forRuntime(t){if("docker"===t.type)return this.docker;if("local"===t.type)return this.local;const e=new Error(`Runtime type ${t.type} is not supported by this node agent.`);throw Object.assign(e,{statusCode:400,code:"NODE_RUNTIME_TYPE_UNSUPPORTED"}),e}async stopAll(){this.local instanceof jn&&await this.local.stopAll()}}class Rn{nodeId;paths;localFolders;nodeRuntimes;controlledInstances;updateJobs;node;listenerPort;containerUrlOverride;constructor(t,e,n,r,o){this.paths=t,this.nodeId=e,this.localFolders=new Ne(t.localFoldersDir,{schema:mt}),this.nodeRuntimes=new Ne(t.nodeRuntimesDir,{schema:bt}),this.controlledInstances=new Ne(t.controlledInstancesDir,{schema:Et,sanitize:xt}),this.updateJobs=new Ve(t),this.listenerPort=o,this.containerUrlOverride=r;const i=cn();this.node=zt.parse({id:e,name:e,connectionMode:"direct-http",endpoint:n,controlEndpoint:n,containerEndpoint:this.containerUrl,publicWebBase:n?n.replace(/:\d+$/,""):void 0,status:"online",health:"ok",capabilities:{},labels:{},createdAt:i,updatedAt:i})}init(){if(this.localFolders.init(),this.nodeRuntimes.init(),this.controlledInstances.init(),this.updateJobs.init(),!this.nodeRuntimes.get("runtime_local_docker")){const t=cn();this.nodeRuntimes.put(bt.parse({id:"runtime_local_docker",nodeId:this.nodeId,name:"Local Docker",type:"docker",status:"unknown",accessStrategy:"direct-port",capabilities:{},labels:{},createdAt:t,updatedAt:t}))}for(const t of this.nodeRuntimes.list())t.nodeId!==this.nodeId&&this.nodeRuntimes.put(bt.parse({...t,nodeId:this.nodeId,updatedAt:cn()}));for(const t of this.localFolders.list())t.nodeId!==this.nodeId&&this.localFolders.put(mt.parse({...t,nodeId:this.nodeId,updatedAt:cn()}))}get localNodeAgentUrl(){return`http://127.0.0.1:${this.listenerPort}`}get currentListenerPort(){return this.listenerPort}get containerUrl(){return this.containerUrlOverride||`http://host.docker.internal:${this.listenerPort}`}setListenerPort(t){this.listenerPort=t,this.node.containerEndpoint=this.containerUrl,this.node.updatedAt=cn()}runningInstanceCount(){const t=new Set(["created","stopped","failed"]);return this.listInstances().filter(e=>!t.has(e.status)).length}createLocalFolder(t){const e=cn(),n=mt.parse({...t,id:t.id||Se("folder"),nodeId:this.nodeId,modelSelection:t.modelSelection||{},labels:t.labels||{},createdAt:e,updatedAt:e});return this.localFolders.put(n)}createRuntime(t){const e=cn(),n=bt.parse({...t,id:t.id||Se("runtime"),nodeId:this.nodeId,accessStrategy:t.accessStrategy||(r=t.type,"docker"===r?"direct-port":"node-proxy"),capabilities:"local"===t.type?vn(t.capabilities):t.capabilities||{},labels:t.labels||{},createdAt:e,updatedAt:e});var r;return this.nodeRuntimes.put(n)}updateRuntime(t,e){const n=this.requireRuntime(t),r=bt.parse({...n,...e,id:n.id,nodeId:this.nodeId,accessStrategy:e.accessStrategy||n.accessStrategy,capabilities:"local"===(e.type||n.type)?vn(e.capabilities||n.capabilities):e.capabilities||n.capabilities,createdAt:n.createdAt,updatedAt:cn()});return this.nodeRuntimes.put(r)}deleteRuntime(t){this.requireRuntime(t);const e=this.listInstances().filter(e=>e.runtimeId===t);if(e.length){const n=new Error(`Runtime ${t} is used by ${e.length} instance${1===e.length?"":"s"}.`);throw Object.assign(n,{statusCode:409,code:"NODE_RUNTIME_IN_USE"}),n}return this.nodeRuntimes.delete(t)}checkRuntime(t,e){const n=this.requireRuntime(t);return e.check?e.check(n).then(t=>{const e=bt.parse({...n,...t,id:n.id,nodeId:this.nodeId,createdAt:n.createdAt,updatedAt:cn()});return this.nodeRuntimes.put(e)}):this.nodeRuntimes.put(bt.parse({...n,status:"unknown",updatedAt:cn()}))}requireRuntime(t){const e=this.nodeRuntimes.get(t);if(!e){const e=new Error(`Node runtime ${t} was not found.`);throw Object.assign(e,{statusCode:404,code:"NODE_RUNTIME_NOT_FOUND"}),e}return e}requireInstance(t){const e=this.controlledInstances.get(t);if(!e){const e=new Error(`Instance ${t} was not found on node ${this.nodeId}.`);throw Object.assign(e,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),e}const n=Ot(e);if(!n.success)throw function(t,e){const n=e.map(t=>`${t.path.join(".")||"body"}: ${t.message}`).join("; "),r=new Error(`Stored instance ${t} is not compatible with protocol ${Q}: ${n}`);return Object.assign(r,{statusCode:409,code:"NODE_INSTANCE_PAYLOAD_INVALID"}),r}(t,n.error.issues);return n.data}listInstances(){return this.controlledInstances.list().flatMap(t=>{const e=Ot(t);return e.success?[e.data]:[]})}createInstance(t){const e=this.requireRuntime(t.runtimeId),r=cn(),o=t.id||Se("inst"),i=gt.parse(t.source);if(function(t){return"boolean"==typeof t.capabilities.requiresImage?t.capabilities.requiresImage:"local"!==t.type}(e)&&(!t.imageId||!t.image)){const t=new Error(`Runtime ${e.name} requires an image.`);throw Object.assign(t,{statusCode:400,code:"NODE_RUNTIME_IMAGE_REQUIRED"}),t}if("local"===e.type&&"local-folder"!==i.type){const t=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(t,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),t}if("local"===e.type&&this.listInstances().some(t=>"local"===this.requireRuntime(t.runtimeId).type)){const t=new Error("A localhost instance already exists on this node.");throw Object.assign(t,{statusCode:409,code:"LOCAL_RUNTIME_INSTANCE_EXISTS"}),t}const s=t.image?It.parse(t.image):void 0,a="local"===e.type&&"local-folder"===i.type?n.resolve(i.path):void 0,c=Et.parse({id:o,name:t.name||`instance-${r.replace(/[-:.TZ]/g,"").slice(0,14)}-${o.replace(/^inst_?/,"").slice(0,4)}`,source:i,sourceSnapshot:t.sourceSnapshot||{},projectId:t.projectId,nodeId:this.nodeId,runtimeId:e.id,imageId:s?.id,imageSnapshot:s,status:"created",health:"unknown",connectionStatus:"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",controlMode:"controlled",capabilities:"local"===e.type?{apps:e.capabilities.apps||{}}:{},config:t.config,workspace:"local"===e.type?{mode:"local-bind",status:"unknown",path:a}:{status:"unknown"},target:{strategy:"node-proxy",status:"unknown"},runtime:"local"===e.type?{kind:"local",workspacePath:a,labels:{"task-handoff.runtime-kind":"local"}}:{labels:{}},registrationToken:ke(),createdAt:r,updatedAt:r});return this.controlledInstances.put(c)}registerInstance(t,e,n){const r=Ct.parse(e),o=cn(),i=this.controlledInstances.get(t);if(!i){const e=new Error(`Instance ${t} was not found on node ${this.nodeId}.`);throw Object.assign(e,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),e}this.validateInstanceReport(i,r,n),_n(r.protocolVersion,`Instance ${t}`);const s=Et.parse({...i,name:r.name,status:"registered",health:"ok",connectionStatus:"online",agentStatus:"online",targetStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":"reachable"===r.target.status?"reachable":i.targetStatus,uiAccessStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":i.uiAccessStatus,controlMode:r.controlMode,protocolVersion:r.protocolVersion,instanceVersion:r.instanceVersion,build:r.build,capabilities:r.capabilities,workspace:r.workspace,target:{...i.target,...r.target},registrationToken:i.registrationToken||r.registrationToken,lastHeartbeatAt:o,updatedAt:o});return this.controlledInstances.put(s)}heartbeatInstance(t,e,n){const r=this.requireInstance(t);this.validateInstanceToken(r,n);const o=jt.parse(e);_n(o.protocolVersion,`Instance ${t}`);const i=cn(),s=o.target?{...r.target,...o.target}:r.target,a={...s,status:"unknown"===s.status&&(s.web||s.api)?"reachable":s.status},c="endpoint-unreachable"===a.status?"endpoint-unreachable":"reachable"===a.status?"reachable":r.targetStatus,d=Et.parse({...r,...o,target:a,agentStatus:"online",targetStatus:c,uiAccessStatus:c,connectionStatus:"online",build:o.build||r.build,lastHeartbeatAt:i,updatedAt:i});return this.controlledInstances.put(d)}validateInstanceReport(t,e,n){this.validateInstanceToken(t,n||e.registrationToken),e.instanceId&&e.instanceId!==t.id&&Nn("INSTANCE_ID_MISMATCH",`Instance ${e.instanceId} cannot register as ${t.id}.`),e.nodeId&&e.nodeId!==this.nodeId&&Nn("INSTANCE_NODE_MISMATCH",`Instance ${t.id} belongs to node ${this.nodeId}.`),e.runtimeId&&e.runtimeId!==t.runtimeId&&Nn("INSTANCE_RUNTIME_MISMATCH",`Instance ${t.id} belongs to runtime ${t.runtimeId}.`),e.imageId&&e.imageId!==t.imageId&&Nn("INSTANCE_IMAGE_MISMATCH",`Instance ${t.id} belongs to image ${t.imageId}.`)}validateInstanceToken(t,e){t.registrationToken&&e===t.registrationToken||Nn("INSTANCE_REGISTRATION_TOKEN_INVALID",`Invalid registration token for instance ${t.id}.`)}context(t,e={}){const n=t.imageSnapshot||It.parse({id:t.imageId||"img_localhost",name:t.imageId||"Localhost",image:t.imageId||"localhost",registry:"local",capabilities:[],optionalApps:[],defaultEnv:{},labels:{},createdAt:t.createdAt,updatedAt:t.updatedAt});return{project:bn(t),image:n,node:this.node,runtime:this.requireRuntime(t.runtimeId),instance:t,nodeAgentUrl:this.containerUrl,modelEnv:e}}}class Pn{app;state;settings;sockets=new Set;config;source;status="error";error;constructor(t){this.app=t.app,this.state=t.state,this.settings=t.settings,this.config=t.config,this.source=t.source,this.app.server.on("connection",t=>{this.sockets.add(t),t.once("close",()=>this.sockets.delete(t))})}current(){return wt.parse({...this.config,host:dn(this.config.bindScope),status:this.status,source:this.source,...this.error?{error:this.error}:{}})}async start(){try{await this.listen(this.config),this.status="listening",this.error=void 0,this.state.setListenerPort(this.config.port)}catch(t){this.status="error",this.error=t instanceof Error?t.message:String(t),this.app.log.error({host:dn(this.config.bindScope),port:this.config.port,error:this.error},"node agent TCP listener failed to start; Unix IPC remains available")}return this.current()}async update(t){const e=St.parse(t);if(e.bindScope===this.config.bindScope&&e.port===this.config.port)return this.current();if(e.port!==this.config.port){const t=this.state.runningInstanceCount();if(t>0){const e=new Error(`Cannot change the node agent port while ${t} controlled instance(s) are running.`);throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_LISTENER_PORT_IN_USE_BY_INSTANCES",blockingInstanceCount:t}),e}}const n={config:this.config,source:this.source,status:this.status,error:this.error};await this.stop();try{await this.listen(e)}catch(t){await this.restore(n);const r=new Error(`Failed to bind node agent TCP listener at ${dn(e.bindScope)}:${e.port}: ${t instanceof Error?t.message:String(t)}`);throw Object.assign(r,{statusCode:409,code:"NODE_AGENT_LISTENER_BIND_FAILED"}),r}try{this.settings.put({version:1,externalListener:e})}catch(t){await this.stop(),await this.restore(n);const e=new Error(`Failed to persist node agent TCP listener: ${t instanceof Error?t.message:String(t)}`);throw Object.assign(e,{statusCode:500,code:"NODE_AGENT_LISTENER_PERSIST_FAILED"}),e}return this.config=e,this.source="persisted",this.status="listening",this.error=void 0,this.state.setListenerPort(e.port),this.current()}async shutdown(){await this.stop()}async restore(t){if(this.config=t.config,this.source=t.source,this.status=t.status,this.error=t.error,"listening"===t.status)try{await this.listen(t.config)}catch(t){this.status="error",this.error=`Failed to restore previous listener: ${t instanceof Error?t.message:String(t)}`,this.app.log.error({error:this.error},"node agent TCP listener rollback failed")}this.state.setListenerPort(t.config.port)}async listen(t){await new Promise((e,n)=>{const r=t=>{this.app.server.off("listening",o),n(t)},o=()=>{this.app.server.off("error",r),e()};this.app.server.once("error",r),this.app.server.once("listening",o),this.app.server.listen({host:dn(t.bindScope),port:t.port})})}async stop(){this.app.server.listening&&await new Promise((t,e)=>{this.app.server.close(n=>n?e(n):t());for(const t of this.sockets)t.destroy()})}}async function Fn(t,e,n,r,o,i,s="request"){const a=t.requireInstance(r);i.diagnostic({instanceId:r,action:"start",reason:s,runtimeId:a.runtimeId,imageId:a.imageId},"node instance start requested");const c=t.controlledInstances.put(Et.parse({...a,status:"provisioning",updatedAt:cn()})),d=e.forRuntime(t.requireRuntime(c.runtimeId)),l=await d.start(t.context(c,o)),u=await An(n,Et.parse({...c,...l,target:l.target?{...c.target,...l.target}:c.target,workspace:l.workspace?{...c.workspace,error:void 0,...l.workspace}:c.workspace,runtime:l.runtime?{...c.runtime,...l.runtime}:c.runtime,updatedAt:cn()})),p=Et.parse({...c,...l,target:l.target?{...c.target,...l.target,status:u}:c.target,workspace:l.workspace?{...c.workspace,error:void 0,...l.workspace}:c.workspace,runtime:l.runtime?{...c.runtime,...l.runtime}:c.runtime,targetStatus:u,uiAccessStatus:u,connectionStatus:l.connectionStatus,updatedAt:cn()}),m=t.controlledInstances.put(p);return await Sn(n,m,"start",i),i.diagnostic({instanceId:r,action:"start",reason:s,status:m.status,connectionStatus:m.connectionStatus,targetStatus:m.targetStatus,targetWeb:m.target.web,containerName:m.runtime.containerName},"node instance start completed"),m}function $n(t,e){const n=e instanceof Error?e.message:String(e);return Et.parse({...t,status:"failed",health:"failed",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",workspace:{...t.workspace,error:n},updatedAt:cn()})}function Ln(t){return Et.parse({...t,health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...t.target,status:"unknown"},updatedAt:cn()})}async function Un(t={}){const r=t.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,o=Number(t.port||process.env.TASK_HANDOFF_NODE_AGENT_PORT||"8091"),i=`http://127.0.0.1:${o}`,u=t.containerUrl||process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,p=ye(t.dataDir),m=t.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,g=t.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID,h=gn(p,t.nodeId||process.env.TASK_HANDOFF_NODE_ID),f=t.connectionMode||("local-ipc"===process.env.TASK_HANDOFF_NODE_AGENT_CONNECTION_MODE?"local-ipc":"local-loopback"),y=t.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||Ce(p.dataDir),I="local-ipc"===f?`ipc://${encodeURIComponent(y)}`:i,z=new Rn(p,h,i,u,o);z.node.connectionMode=f,z.node.controlEndpoint=I,z.node.endpoint=I,z.init();const b=new Ut(t.dockerCommandRunner,{publishHost:"127.0.0.1"}),A=new Dn(new Cn(b),new jn(t.dockerCommandRunner||Lt,p,()=>z.localNodeAgentUrl)),w=t.updateCommandRunner||t.dockerCommandRunner||Lt,S=t.fetchImpl||fetch,k=s({logger:t.logger??!0});k.decorate("nodeAgentState",z),await k.register(a);const N=new oe(z,r,{logger:k.log,safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0});N.start(),k.decorate("nodeAgentEventForwarder",N);const _=(T=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(T||"").trim().toLowerCase())),v=new Map,E=(t,e)=>{_&&k.log.info(t,e)},O={diagnostic:E,warn:(t,e)=>k.log.warn(t,e)},x=async t=>{if("node-agent"===t.target.component)return async function(t){const e=await Me(t.runCommand,"@task-handoff/node-agent",t.channel);return e?at.parse({target:{component:"node-agent"},source:"npm",channel:t.channel,currentVersion:t.currentVersion,availableVersion:e,updateAvailable:Ue(t.currentVersion,e),checkedAt:Le()}):Ke({target:{component:"node-agent"},channel:t.channel,currentVersion:t.currentVersion})}({channel:t.channel,currentVersion:nn(),runCommand:w});const e=z.requireInstance(t.target.instanceId);return async function(t){const e={component:"controlled-instance",instanceId:t.instance.id};if("local"===t.runtime.type){const n=await Me(t.runCommand,"@task-handoff/controlled-instance",t.channel),r=t.instance.build?.packageVersion||t.instance.instanceVersion;return n?at.parse({target:e,source:"npm",channel:t.channel,currentVersion:r,availableVersion:n,updateAvailable:Ue(r,n),checkedAt:Le()}):Ke({target:e,channel:t.channel,currentVersion:r})}if("docker"===t.runtime.type){const n=t.instance.imageSnapshot?.image;if(!n||"local"===t.instance.imageSnapshot?.registry)return at.parse({target:e,source:"docker-registry",channel:t.channel,currentVersion:t.instance.build?.imageDigest,availableVersion:t.instance.build?.imageDigest||"unavailable",updateAvailable:!1,supported:!1,reason:"The instance does not use a remote Docker image.",checkedAt:Le()});const r=function(t,e){const n=t.split("@",1)[0],r=n.lastIndexOf("/"),o=n.lastIndexOf(":");return`${o>r?n.slice(0,o):n}:${Ge(e)}`}(n,t.channel),o=await t.runCommand("docker",["manifest","inspect","--verbose",r]),i=qe(JSON.parse(o.stdout));if(!i)throw new Error(`Docker registry did not return a digest for ${r}.`);const s=t.instance.build?.imageDigest;return at.parse({target:e,source:"docker-registry",channel:t.channel,currentVersion:s,availableVersion:i,artifactRef:r,updateAvailable:s!==i,checkedAt:Le()})}return at.parse({target:e,source:"npm",channel:t.channel,availableVersion:"unsupported",updateAvailable:!1,supported:!1,reason:`Runtime type ${t.runtime.type} does not support managed updates.`,checkedAt:Le()})}({channel:t.channel,instance:e,runtime:z.requireRuntime(e.runtimeId),runCommand:w})};var T;k.setErrorHandler((t,e,n)=>{const r=function(t){if(t instanceof d.z.ZodError)return{statusCode:400,code:"VALIDATION_ERROR",message:t.issues.map(t=>`${t.path.join(".")||"body"}: ${t.message}`).join("; ")};const e=t&&"object"==typeof t?t:{};return{statusCode:"number"==typeof e.statusCode?e.statusCode:500,code:"string"==typeof e.code?e.code:"NODE_AGENT_ERROR",message:t instanceof Error?t.message:String(t)}}(t);(_||r.statusCode>=500)&&(r.statusCode>=500?k.log.error.bind(k.log):k.log.warn.bind(k.log))({method:e.method,url:e.url,statusCode:r.statusCode,errorCode:r.code,error:r.message},"node agent request failed"),n.code(r.statusCode).send({error:{code:r.code,message:r.message,...t&&"object"==typeof t&&"number"==typeof t.blockingInstanceCount?{blockingInstanceCount:t.blockingInstanceCount}:{}}})}),k.addHook("preHandler",async t=>{const e=function(t){const e=function(t){const e=new Map;for(const[n,r]of Object.entries(t)){const t=Array.isArray(r)?r.join(","):"string"==typeof r?r:void 0===r?"":String(r);e.set(n.toLowerCase(),t)}return{nodeId:e.get("x-taskhandoff-node-id")||"",keyId:e.get("x-taskhandoff-key-id")||"",timestamp:e.get("x-taskhandoff-timestamp")||"",nonce:e.get("x-taskhandoff-nonce")||"",bodySha256:e.get("x-taskhandoff-body-sha256")||"",signature:e.get("x-taskhandoff-signature")||""}}(t.headers);if(!e.nodeId&&!e.signature)return!1;const n=In(p,m,g);if(!n.length)return!1;if(e.nodeId!==h)throw Object.assign(new Error("Invalid node agent HMAC node id."),{statusCode:401,code:"NODE_AGENT_HMAC_NODE_MISMATCH"});if(!e.keyId)throw Object.assign(new Error("Node agent HMAC key id is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_MISSING"});const r=n.filter(t=>e.keyId===t.keyId);if(!r.length)throw Object.assign(new Error("Invalid node agent HMAC key id."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_INVALID"});const o=Date.parse(e.timestamp);if(!Number.isFinite(o)||Math.abs(Date.now()-o)>De)throw Object.assign(new Error("Node agent HMAC timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_HMAC_TIMESTAMP_INVALID"});if(!e.nonce)throw Object.assign(new Error("Node agent HMAC nonce is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_MISSING"});!function(){const t=Date.now()-De;for(const[e,n]of v)n<=t&&v.delete(e)}();const i=`${e.nodeId}:${e.keyId}:${e.nonce}`;if(v.has(i))throw Object.assign(new Error("Node agent HMAC nonce was already used."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_REPLAY"});const s=Re(null==(a=t.body)?"":"string"==typeof a||Buffer.isBuffer(a)?a:JSON.stringify(a));var a;if(e.bodySha256!==s)throw Object.assign(new Error("Node agent HMAC body hash mismatch."),{statusCode:401,code:"NODE_AGENT_HMAC_BODY_HASH_INVALID"});if(!r.some(n=>function(t,e){const n=Buffer.from(t,"hex"),r=Buffer.from(e,"hex");return n.length===r.length&&l.timingSafeEqual(n,r)}(e.signature,Pe(n.secret,{keyId:e.keyId,method:t.method,pathWithQuery:t.url,timestamp:e.timestamp,nonce:e.nonce,bodySha256:s}))))throw Object.assign(new Error("Invalid node agent HMAC signature."),{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_INVALID"});return v.set(i,Date.now()+De),e.keyId}(t);if(e)t.nodeAgentAuthKeyId=e;else if(an(t)){if(r&&on(t.headers)!==r){const t=new Error("Invalid node agent token.");throw Object.assign(t,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),t}}else if("/api/node-agent/pairing/complete"!==t.url.split("?")[0]&&!function(t){const e=t.split("?")[0];return/^\/api\/node-agent\/instances\/[^/]+\/(register|heartbeat)$/.test(e)}(t.url)){if(In(p,m,g).length){const t=new Error("Node agent HMAC signature is required.");throw Object.assign(t,{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_REQUIRED"}),t}if(!function(t){return sn(t.ip)||sn(t.socket.remoteAddress)||!t.ip&&!t.socket.remoteAddress&&!t.socket.remoteFamily}(t)){const t=new Error("Local node agent access is only accepted from loopback or local IPC connections.");throw Object.assign(t,{statusCode:401,code:"NODE_AGENT_LOCAL_TOKEN_REQUIRES_LOOPBACK"}),t}if(r&&on(t.headers)!==r){const t=new Error("Invalid node agent token.");throw Object.assign(t,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),t}}}),k.addHook("onClose",async()=>{N.stop(),await async function(t,e){await e.stopAll();for(const e of t.listInstances())"local"===t.requireRuntime(e.runtimeId).type&&t.controlledInstances.put(Ln(e))}(z,A)}),k.decorate("nodeAgentRestoreLocalInstances",async()=>{const t=z.listInstances().filter(t=>"local"===z.requireRuntime(t.runtimeId).type&&xn.has(t.status));for(const e of t)try{await Fn(z,A,S,e.id,{},O,"restore")}catch(t){z.controlledInstances.put($n(z.requireInstance(e.id),t)),E({instanceId:e.id,action:"restore",error:t instanceof Error?t.message:String(t)},"node local instance restore failed")}}),k.get("/api/node-agent/health",async()=>{return{data:{ok:!0,role:"node-agent",nodeId:h,protocolVersion:Q,build:(t="node-agent",{component:t,packageName:"@task-handoff/node-agent",packageVersion:nn(),protocolVersion:Q,buildId:en("TASK_HANDOFF_BUILD_ID"),builtAt:en("TASK_HANDOFF_BUILT_AT"),gitCommit:en("TASK_HANDOFF_GIT_COMMIT"),imageRef:en("TASK_HANDOFF_IMAGE_REF"),imageDigest:en("TASK_HANDOFF_IMAGE_DIGEST")}),serverTime:(new Date).toISOString()}};var t});const C=t=>{if(!an(t)){const t=new Error("Node agent TCP listener settings are available only over local Unix IPC.");throw Object.assign(t,{statusCode:403,code:"NODE_AGENT_LISTENER_LOCAL_IPC_ONLY"}),t}if(!k.nodeAgentListenerManager){const t=new Error("Node agent TCP listener manager is not initialized.");throw Object.assign(t,{statusCode:503,code:"NODE_AGENT_LISTENER_UNAVAILABLE"}),t}return k.nodeAgentListenerManager};return k.get("/api/node-agent/settings/external-listener",async t=>({data:C(t).current()})),k.patch("/api/node-agent/settings/external-listener",async t=>({data:await C(t).update(t.body)})),k.get("/api/node-agent/updates/jobs",async()=>({data:z.updateJobs.list()})),k.post("/api/node-agent/updates/check",async t=>({data:await x(st.parse(t.body))})),k.post("/api/node-agent/updates/apply",async(t,r)=>{const o=Xe.parse(t.body),i=await x(o);if(!i.supported){const t=new Error(i.reason||"The requested update is not supported.");throw Object.assign(t,{statusCode:400,code:"UPDATE_UNSUPPORTED"}),t}if(!i.updateAvailable){const t=new Error(i.reason||"No update is available for the selected channel.");throw Object.assign(t,{statusCode:409,code:"UPDATE_NOT_AVAILABLE"}),t}const s=z.updateJobs.create(h,i);if("node-agent"===o.target.component){const t=n.resolve(__dirname,"..","bin","task-handoff-node-update-worker");if(!e.existsSync(t)){z.updateJobs.patch(s.id,{status:"failed",error:`Update worker was not found: ${t}`,completedAt:cn()});const e=new Error(`Node agent update worker was not found: ${t}`);throw Object.assign(e,{statusCode:500,code:"UPDATE_WORKER_NOT_FOUND"}),e}await w("systemd-run",["--unit",`task-handoff-update-${s.id}`,"--collect","--property=Type=exec",t,"--job-file",z.updateJobs.records.filePath(s.id),"--target-version",s.toVersion,"--npm-command",He()])}else z.updateJobs.run(s,t=>(async(t,e)=>{if("controlled-instance"!==t.target.component)return;const n=z.requireInstance(t.target.instanceId),r=z.requireRuntime(n.runtimeId),o=A.forRuntime(r);if("local"===r.type){const e=He(),n=(await w(e,["prefix","--global"])).stdout.trim();await w(e,["install","--global","--prefix",n,`@task-handoff/controlled-instance@${t.toVersion}`])}else{if("docker"!==r.type)throw Object.assign(new Error(`Runtime type ${r.type} does not support managed updates.`),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});{const e=t.artifactRef;if(!e||"local"===n.imageSnapshot?.registry)throw Object.assign(new Error("The instance does not use an updateable remote Docker image."),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});await w("docker",["pull",e])}}await o.delete(z.context(n,e)),z.controlledInstances.put(function(t){return Et.parse({...t,status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...t.target,status:"unknown"},updatedAt:cn()})}(Et.parse({...n,..."docker"===r.type&&t.artifactRef&&n.imageSnapshot?{imageSnapshot:{...n.imageSnapshot,image:t.artifactRef,updatedAt:cn()}}:{}}))),await Fn(z,A,S,n.id,e,O,"update")})(t,o.modelEnv));return r.code(202).send({data:s})}),k.post("/api/node-agent/pairing/invites",async(t,e)=>{const n=function(t,e){const n=hn(un(t)||{nodeId:gn(t),createdAt:cn(),updatedAt:cn()}),r=cn(),o=new Date(Date.now()+(e.expiresInMs||6e5)).toISOString(),i=ke(),s={tokenHash:Re(i),createdAt:r,expiresAt:o,...e.controlPlaneName?{controlPlaneName:e.controlPlaneName}:{},...e.controlPlaneUrl?{controlPlaneUrl:e.controlPlaneUrl}:{}};return pn(t,{...n,pairingInvites:[...n.pairingInvites||[],s]}),{...s,token:i}}(p,Ye.parse(t.body||{}));return e.code(201).send({data:{nodeId:h,joinToken:n.token,expiresAt:n.expiresAt}})}),k.post("/api/node-agent/pairing/complete",async(t,e)=>{const n=function(t,e){const n=hn(un(t)||{nodeId:gn(t),createdAt:cn(),updatedAt:cn()}),r=Re(e.joinToken),o=(n.pairingInvites||[]).find(t=>t.tokenHash===r);if(!o){const t=new Error("Node agent pairing invite is invalid or expired.");throw Object.assign(t,{statusCode:401,code:"NODE_AGENT_PAIRING_INVITE_INVALID"}),t}const i=cn(),s={id:e.controlPlaneId||Se("cp"),keyId:Se("key"),...e.controlPlaneName||o.controlPlaneName?{name:e.controlPlaneName||o.controlPlaneName}:{},...e.controlPlaneUrl||o.controlPlaneUrl?{url:e.controlPlaneUrl||o.controlPlaneUrl}:{},secret:ke(),pairedAt:i,updatedAt:i,active:!0};return pn(t,{...n,pairingInvites:(n.pairingInvites||[]).filter(t=>t.tokenHash!==r),remoteControlPlanes:[...(n.remoteControlPlanes||[]).filter(t=>t.id!==s.id),s]}),s}(p,Qe.parse(t.body));return e.code(201).send({data:{nodeId:h,keyId:n.keyId,secret:n.secret,pairedAt:n.pairedAt}})}),k.get("/api/node-agent/remotes",async t=>({data:fn(p,t.nodeAgentAuthKeyId)})),k.delete("/api/node-agent/remotes/:keyId",async t=>{const e=d.z.string().trim().min(1).max(160).parse(t.params.keyId);return{data:{deleted:yn(p,e,t.nodeAgentAuthKeyId)}}}),k.post("/api/node-agent/remotes/connect",async(t,e)=>{const n=tn.parse(t.body),r=function(t){const e=new URL(t);if("http:"!==e.protocol&&"https:"!==e.protocol){const t=new Error("Control-plane URL must use http or https.");throw Object.assign(t,{statusCode:400,code:"NODE_AGENT_REMOTE_CONTROL_PLANE_URL_INVALID"}),t}return e.toString().replace(/\/$/,"")}(n.controlPlaneUrl),o=cn(),i={id:Se("cp"),keyId:Se("key"),...n.controlPlaneName?{name:n.controlPlaneName}:{},url:r,secret:ke(),pairedAt:o,updatedAt:o,active:!1!==n.activate},s=await async function(t,e,n){const r=await t(new URL("/api/node-join/complete",e).toString(),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),o=await r.json().catch(()=>({}));if(!r.ok){const t=new Error(o.error?.message||`Control-plane node join failed with HTTP ${r.status}.`);throw Object.assign(t,{statusCode:r.status,code:o.error?.code||"NODE_AGENT_REMOTE_JOIN_FAILED"}),t}return o.data||{}}(S,r,{joinToken:n.joinToken,nodeId:h,nodeName:z.node.name,keyId:i.keyId,secret:i.secret,pairedAt:i.pairedAt}),a=function(t,e){const n=un(t)||{nodeId:gn(t),createdAt:cn(),updatedAt:cn(),pairingInvites:[],remoteControlPlanes:[]},r=e.url?.replace(/\/$/,"");return pn(t,{...n,remoteControlPlanes:[...(n.remoteControlPlanes||[]).filter(t=>t.id!==e.id&&(!r||t.url?.replace(/\/$/,"")!==r)),e]}),e}(p,{...i,..."string"==typeof s.name&&s.name?{name:n.controlPlaneName||s.name}:{}});let c,d=!1!==a.active?"saved":"disabled";if(!1!==a.active&&k.nodeAgentReverseTunnels)try{k.nodeAgentReverseTunnels.connect({url:r,keyId:a.keyId,secret:a.secret}),d="connecting"}catch(t){d="failed",c=t instanceof Error?t.message:String(t)}return e.code(201).send({data:{remote:{id:a.id,url:a.url,keyId:a.keyId,pairedAt:a.pairedAt,active:!1!==a.active},tunnel:{status:d,...c?{error:c}:{}}}})}),k.get("/api/node-agent/runtimes",async()=>({data:z.nodeRuntimes.list()})),k.post("/api/node-agent/runtimes",async(t,e)=>e.code(201).send({data:z.createRuntime(he.parse(t.body))})),k.patch("/api/node-agent/runtimes/:id",async t=>({data:z.updateRuntime(t.params.id,fe.parse(t.body))})),k.delete("/api/node-agent/runtimes/:id",async t=>({data:{deleted:z.deleteRuntime(t.params.id)}})),k.post("/api/node-agent/runtimes/:id/check",async t=>{const e=z.requireRuntime(t.params.id);return{data:await z.checkRuntime(e.id,A.forRuntime(e))}}),k.get("/api/node-agent/local-folders",async()=>({data:z.localFolders.list()})),k.get("/api/node-agent/folders/tree",async t=>({data:de(pe.parse(t.query))})),k.post("/api/node-agent/local-folders",async(t,e)=>e.code(201).send({data:z.createLocalFolder(ue.parse(t.body))})),k.delete("/api/node-agent/local-folders/:id",async t=>({data:{deleted:z.localFolders.delete(t.params.id)}})),k.get("/api/node-agent/instances",async()=>({data:z.listInstances()})),k.post("/api/node-agent/instances",async(t,e)=>{const n=z.createInstance(me.parse(t.body));return N.syncNow(),e.code(201).send({data:n})}),k.patch("/api/node-agent/instances/:id",async t=>{const e=t.params.id,n=z.requireInstance(e),r=ge.parse(t.body),o=Et.parse({...n,...r,updatedAt:cn()}),i=z.controlledInstances.put(o);return N.syncNow(),{data:i}}),k.post("/api/node-agent/instances/:id/register",async(t,e)=>{const n=t.params.id,r=z.registerInstance(n,Ct.parse(t.body),on(t.headers));return N.syncNow(),E({instanceId:n,action:"register",protocolVersion:r.protocolVersion,build:r.build,targetStatus:r.targetStatus,targetStrategy:r.target.strategy},"node instance registered"),e.code(201).send({data:r})}),k.post("/api/node-agent/instances/:id/heartbeat",async t=>{const e=t.params.id,n=z.heartbeatInstance(e,jt.parse(t.body),on(t.headers));return N.syncNow(),E({instanceId:e,action:"heartbeat",status:n.status,health:n.health,protocolVersion:n.protocolVersion,build:n.build,targetStatus:n.targetStatus,apps:n.apps.runningCount},"node instance heartbeat accepted"),{data:n}}),k.get("/api/node-agent/events",{websocket:!0},t=>{const e=t;e.send(JSON.stringify({type:"node-agent.events.connected",nodeId:h,serverTime:(new Date).toISOString()}));const n=N.addOutput(e);e.on("close",n),e.on("error",n)}),k.get("/api/node-agent/docker/images",async()=>({data:await Mt(t.dockerCommandRunner)})),k.post("/api/node-agent/instances/:id/start",async t=>{const e=t.params.id,n=Ze.parse(t.body),r=await Fn(z,A,S,e,n.modelEnv,O);return N.syncNow(),{data:r}}),k.post("/api/node-agent/instances/:id/stop",async t=>{const e=z.requireInstance(t.params.id);E({instanceId:e.id,action:"stop",runtimeId:e.runtimeId,containerName:e.runtime.containerName},"node instance stop requested");const n=A.forRuntime(z.requireRuntime(e.runtimeId)),r=await n.stop(z.context(e)),o=Et.parse({...e,...r,agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",updatedAt:cn()}),i=z.controlledInstances.put(o);return N.syncNow(),E({instanceId:e.id,action:"stop",status:i.status,connectionStatus:i.connectionStatus,containerName:i.runtime.containerName},"node instance stop completed"),{data:i}}),k.post("/api/node-agent/instances/:id/restart",async t=>{const e=t.params.id,n=z.requireInstance(e),r=Ze.parse(t.body);E({instanceId:e,action:"restart",runtimeId:n.runtimeId,imageId:n.imageId,containerName:n.runtime.containerName},"node instance restart requested");const o=A.forRuntime(z.requireRuntime(n.runtimeId)),i=await o.restart(z.context(n,r.modelEnv)),s=Et.parse({...n,...i,target:i.target?{...n.target,...i.target}:n.target,updatedAt:cn()}),a=await An(S,s),c=Et.parse({...n,...i,target:i.target?{...n.target,...i.target,status:a}:n.target,targetStatus:a,uiAccessStatus:a,connectionStatus:i.connectionStatus,updatedAt:cn()}),d=z.controlledInstances.put(c);return await Sn(S,d,"restart",O),N.syncNow(),E({instanceId:e,action:"restart",status:d.status,connectionStatus:d.connectionStatus,targetStatus:d.targetStatus,targetWeb:d.target.web,containerName:d.runtime.containerName},"node instance restart completed"),{data:d}}),k.post("/api/node-agent/instances/:id/delete",async t=>{const e=t.params.id,n=z.requireInstance(e);E({instanceId:e,action:"delete",runtimeId:n.runtimeId,containerName:n.runtime.containerName},"node instance delete requested");const r=A.forRuntime(z.requireRuntime(n.runtimeId));await r.delete(z.context(n)),E({instanceId:e,action:"delete"},"node instance delete completed");const o=z.controlledInstances.delete(e);return N.syncNow(),{data:{deleted:o}}}),k.post("/api/node-agent/instances/:id/proxy",async(t,e)=>{const n=t.params.id,r=le.parse(t.body),o=kn(z.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;E({instanceId:n,action:"proxy",method:r.method,path:i,instanceBase:o},"node instance proxy requested");const s=await S(`${o}${i}`,{method:r.method,headers:{...r.headers},body:rn(r)}),a=s.headers.get("content-type")||"application/octet-stream",c=await s.text();E({instanceId:n,action:"proxy",method:r.method,path:i,statusCode:s.status,contentType:a},"node instance proxy completed"),e.code(s.status).type(a).send(c)}),k.post("/api/node-agent/instances/:id/proxy/raw",async t=>{const e=t.params.id,n=le.parse(t.body),r=kn(z.requireInstance(e)),o=n.path.startsWith("/")?n.path:`/${n.path}`;E({instanceId:e,action:"proxy.raw",method:n.method,path:o,instanceBase:r},"node instance raw proxy requested");const i=await S(`${r}${o}`,{method:n.method,headers:{...n.headers},body:rn(n)}),s=Buffer.from(await i.arrayBuffer());return E({instanceId:e,action:"proxy.raw",method:n.method,path:o,statusCode:i.status,byteLength:s.length},"node instance raw proxy completed"),{data:{status:i.status,headers:(a=i.headers,Object.fromEntries([...a.entries()].filter(([t])=>!Be.has(t.toLowerCase())))),bodyBase64:s.toString("base64")}};var a}),k.get("/api/node-agent/instances/:id/proxy/ws/*",{websocket:!0},(t,e)=>{const n=e.params.id,r=e.params["*"]||"",o=e.url.indexOf("?"),i=o>=0?e.url.slice(o):"";let s;try{const o=kn(z.requireInstance(n)),a=new URL(`/${r}${i}`,`${o}/`);a.protocol="https:"===a.protocol?"wss:":"ws:";const d=function(t){const e=t["sec-websocket-protocol"],n=(Array.isArray(e)?e.join(","):"string"==typeof e?e:"").split(",").map(t=>t.trim()).filter(Boolean);return n.length?n:void 0}(e.headers);E({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,upstreamUrl:a.toString(),protocols:d||[]},"node instance websocket proxy opening"),s=d?new c(a,d):new c(a),s.on("open",()=>{E({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy opened"),t.on("message",(t,e)=>s?.readyState===c.OPEN&&s.send(function(t,e){return e?t:t.toString()}(t,e))),t.on("close",()=>s?.close()),t.on("error",()=>s?.close())}),s.on("message",(e,n)=>{t.readyState===c.OPEN&&t.send(n?e:e.toString())}),s.on("close",()=>{E({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy closed"),t.close()}),s.on("error",e=>{E({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:e instanceof Error?e.message:String(e)},"node instance websocket proxy failed"),t.close(1011,"Instance websocket proxy failed.")})}catch(e){E({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:e instanceof Error?e.message:String(e)},"node instance websocket endpoint unavailable"),s?.close(),t.close(1011,"Instance websocket endpoint is not reachable.")}}),k}function Mn(t){const e=new URL("/api/node-agent/tunnel",t);return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}function Hn(t,e){const n=new URL(e.tunnelUrl);n.searchParams.set("nodeId",e.nodeId);const r=e.secret?Fe({nodeId:e.nodeId,keyId:e.keyId,secret:e.secret,method:"GET",pathWithQuery:`${n.pathname}${n.search}`}):{},o=new c(n,{headers:r}),i=new Map;let s;const a=t=>{const n=t.startsWith("/")?t:`/${t}`,r="function"==typeof e.port?e.port():e.port,o=new URL(`/api/node-agent${n}`,`http://127.0.0.1:${r}`);return o.protocol="ws:",o},d=t=>e.secret?Fe({nodeId:e.nodeId,keyId:e.keyId,secret:e.secret,method:"GET",pathWithQuery:`${t.pathname}${t.search}`}):{},l=(t,e=1e3,n="")=>{const r=i.get(t);i.delete(t),r?.upstream?.close(e,n),r?.tunnel?.close(e,n)};return o.on("open",()=>{o.send(JSON.stringify({type:"node-agent.identify",nodeId:e.nodeId,serverTime:(new Date).toISOString()})),s=t.nodeAgentEventForwarder?.addOutput(o)}),o.on("close",()=>{for(const t of i.keys())l(t,1001,"Reverse tunnel disconnected.");s?.(),s=void 0}),o.on("message",async r=>{let s;try{s=JSON.parse(String(r))}catch{return void o.send(JSON.stringify({type:"node-agent.error",code:"INVALID_JSON"}))}const u=s&&"object"==typeof s?s:{};if("control-plane.websocket.open"===u.type){const t="string"==typeof u.streamId?u.streamId:"",r="string"==typeof u.route?u.route:"",s=Array.isArray(u.protocols)?u.protocols.filter(t=>"string"==typeof t):void 0;try{const l=e.token?{authorization:`Bearer ${e.token}`}:void 0,u=s?.length?new c(a(r),s,{headers:l}):new c(a(r),{headers:l}),p=(t=>{const e=new URL(n);return e.pathname=`${e.pathname.replace(/\/$/,"")}/streams/${encodeURIComponent(t)}`,e})(t),m=new c(p,{headers:d(p)});i.set(t,{upstream:u,tunnel:m,close:(t=1e3,e="")=>{u.close(t,e),m.close(t,e)}}),u.on("open",()=>{o.send(JSON.stringify({type:"node-agent.websocket.open",streamId:t,protocol:u.protocol}))}),function(t,e,n={}){const r=n.pendingFrameLimit??256;let o=!1,i=Rt(e);const s=[],a=[];let c;const d=()=>{c&&(clearTimeout(c),c=void 0)},l=(n,o)=>{if(n.length>=r)return Pt(t,1011,"WebSocket bridge pending frame limit exceeded."),void Pt(e,1011,"WebSocket bridge pending frame limit exceeded.");n.push(o)};n.upstreamOpenTimeoutMs&&!i&&(c=setTimeout(()=>{i||o||(Pt(t,1011,"Upstream websocket did not open."),Pt(e))},n.upstreamOpenTimeoutMs)),t.on("open",()=>$t(t,a)),e.on("open",()=>{d(),i=!0,n.onUpstreamOpen?.(),$t(e,s)}),t.on("message",(t,n=!1)=>{const r={data:t,isBinary:Boolean(n)};Rt(e)?Ft(e,r):l(s,r)}),e.on("message",(e,n=!1)=>{const r={data:e,isBinary:Boolean(n)};Rt(t)?Ft(t,r):l(a,r)}),e.on("close",(e,r)=>{d(),n.onUpstreamClose?.(e,r),(i||o||!n.onUpstreamCloseBeforeOpen?.())&&Pt(t,e,r)}),e.on("error",e=>{n.onUpstreamError?.(e),(i||o||!n.onUpstreamErrorBeforeOpen?.(e))&&Pt(t)}),t.on("close",(t,r)=>{d(),o=!0,n.onClientClose?.(t,r),Pt(e,t,r)}),t.on("error",t=>{d(),o=!0,n.onClientError?.(t),Pt(e,1011,"WebSocket bridge client failed.")})}(m,u,{pendingFrameLimit:256,upstreamOpenTimeoutMs:1e4,onClientClose:()=>{i.delete(t)},onClientError:e=>{i.delete(t),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:t,message:e instanceof Error?e.message:String(e)}))},onUpstreamClose:(e,n)=>{i.delete(t),o.send(JSON.stringify({type:"node-agent.websocket.close",streamId:t,code:"number"==typeof e?e:1e3,reason:Buffer.isBuffer(n)?n.toString("utf8"):String(n||"")}))},onUpstreamError:e=>{i.delete(t),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:t,message:e instanceof Error?e.message:String(e)}))}})}catch(e){o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:t,message:e instanceof Error?e.message:String(e)}))}return}if("control-plane.websocket.close"===u.type){const t="string"==typeof u.streamId?u.streamId:"";return void l(t,"number"==typeof u.code?u.code:1e3,"string"==typeof u.reason?u.reason:"")}if("control-plane.request"===u.type){const n="string"==typeof u.requestId?u.requestId:"",r=u.init&&"object"==typeof u.init?u.init:{},i="string"==typeof u.route&&u.route.startsWith("/")?u.route:"/health",s=r.headers&&"object"==typeof r.headers?r.headers:{};try{const a=await t.inject({method:(p=r.method,"string"==typeof p&&$e.has(p.toUpperCase())?p.toUpperCase():"GET"),url:`/api/node-agent${i}`,headers:{...s,...e.token?{authorization:`Bearer ${e.token}`}:{}},payload:"string"==typeof r.body?r.body:void 0});o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:a.statusCode,headers:a.headers,body:a.body}))}catch(t){o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:502,error:{code:"NODE_AGENT_REVERSE_INJECT_FAILED",message:t instanceof Error?t.message:String(t)}}))}}var p}),o}async function Kn(t){const o=ye(t.dataDir),i=(s=t.host,a=t.port,At.parse({bindScope:"0.0.0.0"===s.trim()?"all-ipv4":"loopback",port:a}));var s,a;const c=e.existsSync(o.settingsPath),d=function(t,e){return new _e(t.settingsPath,()=>({version:1,externalListener:e}),{schema:We,sanitize:ln})}(o,i),p=d.get().externalListener,m=function(t,r={}){e.mkdirSync(n.dirname(t),{recursive:!0});const o={pid:process.pid,hostname:u.hostname(),component:r.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:l.randomUUID(),dataDir:r.dataDir,host:r.host,port:r.port};for(let n=0;n<2;n+=1)try{return e.mkdirSync(t),Te(t,o),{lockPath:t,owner:o,release(){const n=Oe(t);n?.token===o.token&&e.rmSync(t,{recursive:!0,force:!0})}}}catch(n){if("EEXIST"!==n.code)throw n;const o=Oe(t);if(xe(o?.pid))throw new ve(t,o,r.component);e.rmSync(t,{recursive:!0,force:!0})}const i=Oe(t);throw new ve(t,i,r.component)}(n.join(u.tmpdir(),`task-handoff-node-agent-${process.getuid?.()??"user"}.lock`),{component:"node-agent",dataDir:o.dataDir,host:dn(p.bindScope),port:p.port});try{const i={...t,port:p.port},s=await Un(i),a=function(t,e,n,r){const o=e.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,i=new Map,s=e=>{const n=e.url.replace(/\/$/,"");i.get(n)?.close(1e3,"Reverse tunnel reconnecting.");const s=Hn(t,{tunnelUrl:Mn(e.url),nodeId:r,port:()=>t.nodeAgentState.currentListenerPort,token:o,keyId:e.keyId,secret:e.secret});return i.set(n,s),s.on("close",()=>{i.get(n)===s&&i.delete(n)}),s};return{connect:s,connectConfigured:()=>{const a=function(t){const e=t.controlPlaneTunnelUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_TUNNEL_URL;if(e)return e;const n=process.env.TASK_HANDOFF_CONTROL_PLANE_URL;if(!n)return;const r=new URL("/api/node-agent/tunnel",n);return r.protocol="https:"===r.protocol?"wss:":"ws:",r.toString()}(e);if(a){const s=function(t,e,n,r){if(n&&r)return{keyId:r,secret:n};const o=un(t)?.remoteControlPlanes||[];if(!o.length)return;if(e){const t=o.find(t=>t.url&&t.url.replace(/\/$/,"")===e.replace(/\/$/,""));if(t)return{keyId:t.keyId,secret:t.secret}}const i=[...o].sort((t,e)=>e.updatedAt.localeCompare(t.updatedAt))[0];return i?{keyId:i.keyId,secret:i.secret}:void 0}(n,process.env.TASK_HANDOFF_CONTROL_PLANE_URL,e.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,e.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID),c=(process.env.TASK_HANDOFF_CONTROL_PLANE_URL||a).replace(/\/$/,"");i.get(c)?.close(1e3,"Reverse tunnel reconnecting.");const d=Hn(t,{tunnelUrl:a,nodeId:r,port:()=>t.nodeAgentState.currentListenerPort,token:o,keyId:s?.keyId,secret:s?.secret});i.set(c,d)}for(const t of un(n)?.remoteControlPlanes||[])t.url&&!1!==t.active&&s({url:t.url,keyId:t.keyId,secret:t.secret})},closeAll:()=>{for(const t of i.values())t.close(1001,"Node agent shutting down.");i.clear()}}}(s,i,o,gn(o,t.nodeId||process.env.TASK_HANDOFF_NODE_ID));s.decorate("nodeAgentReverseTunnels",a);const l=new Pn({app:s,state:s.nodeAgentState,settings:d,config:p,source:c?"persisted":"bootstrap"});let u;s.decorate("nodeAgentListenerManager",l),function(t){let e=!1;const n=async n=>{if(!e){e=!0,a.closeAll(),u?.close();try{await t.close()}finally{process.exitCode=n?0:process.exitCode}}},r=()=>{n("SIGINT")},o=()=>{n("SIGTERM")};process.once("SIGINT",r),process.once("SIGTERM",o),t.addHook("onClose",async()=>{process.off("SIGINT",r),process.off("SIGTERM",o)})}(s),s.addHook("onClose",async()=>{u?.close(),m.release()});try{await s.ready();const i=t.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||Ce(o.dataDir);u=await async function(t,o){!function(t,r=process.platform){if("win32"!==r){e.mkdirSync(n.dirname(t),{recursive:!0,mode:448});try{e.chmodSync(n.dirname(t),448)}catch{}try{e.unlinkSync(t)}catch(t){if("ENOENT"!==t.code)throw t}}}(o);const i=r.createServer((e,n)=>{t.server.emit("request",e,n)});return i.on("upgrade",(e,n,r)=>{t.server.emit("upgrade",e,n,r)}),await new Promise((t,e)=>{i.once("error",e),i.listen(o,()=>{i.off("error",e),t()})}),i}(s,i),await l.start(),a.connectConfigured(),await(s.nodeAgentRestoreLocalInstances?.())}catch(t){throw await s.close(),t}}catch(t){throw m.release(),t}}function Gn(t){const e=Number(t);if(!Number.isInteger(e)||e<=0||e>65535)throw new Error("Port must be a valid TCP port.");return e}function qn(t){if("local-ipc"!==t&&"local-loopback"!==t)throw new Error("Connection mode must be local-ipc or local-loopback.");return t}(async function(){const r=new t.Command;r.name("task-handoff-node-agent").description("Run a TaskHandoff node agent.").version(function(){try{return JSON.parse(e.readFileSync(n.resolve(__dirname,"..","package.json"),"utf8")).version||"unknown"}catch{return"unknown"}}()).option("--host <host>","Node agent host",process.env.TASK_HANDOFF_NODE_AGENT_HOST||"127.0.0.1").option("-p, --port <port>","Node agent port",Gn,Number(process.env.TASK_HANDOFF_NODE_AGENT_PORT)||8091).option("--data-dir <path>","Node agent data directory").option("--token <token>","Bearer token required by the control plane").option("--connection-mode <mode>","Local control connection mode",qn).option("--ipc-path <path>","Unix socket path used by local-ipc mode").option("--control-plane-tunnel-url <url>","Control plane reverse WebSocket tunnel URL").action(async t=>{await Kn({host:t.host,port:t.port,dataDir:t.dataDir,token:t.token,connectionMode:t.connectionMode,ipcPath:t.ipcPath,controlPlaneTunnelUrl:t.controlPlaneTunnelUrl})}),await r.parseAsync(process.argv)})().catch(t=>{console.error(t instanceof Error?t.message:String(t)),process.exitCode=1});
|
|
1
|
+
"use strict";var e=require("commander"),t=require("node:fs"),n=require("node:path"),r=require("node:http"),o=require("node:net"),i=require("node:url"),s=require("node:child_process"),a=require("fastify"),c=require("@fastify/websocket"),d=require("ws"),l=require("zod"),u=require("node:crypto"),p=require("node:os"),m=require("write-file-atomic"),g=require("semver"),h="undefined"!=typeof document?document.currentScript:null;const f=41943040,y="ai.sessions",I=l.z.string().trim().min(1).max(80),b=l.z.enum(["running","waiting","idle","failed"]),A=l.z.enum(["thinking","tool","editing","approval","responding","unknown"]),z=l.z.object({name:l.z.string().trim().min(1).max(120),inputPreview:l.z.string().trim().max(500).optional(),startedAt:l.z.string().datetime().optional()}).strict(),w=l.z.object({toolCalls:l.z.number().int().min(0).default(0),edits:l.z.number().int().min(0).default(0),approvals:l.z.number().int().min(0).default(0)}).strict().default({toolCalls:0,edits:0,approvals:0}),S=l.z.object({send:l.z.boolean().optional(),interrupt:l.z.boolean().optional(),approval:l.z.boolean().optional()}).strict(),k=l.z.object({id:l.z.string().trim().min(1).max(120),kind:l.z.enum(["image"]),name:l.z.string().trim().min(1).max(240),mime:l.z.string().trim().min(1).max(120),size:l.z.number().int().min(0).max(20971520),data:l.z.string().min(1).max(31457280)}).strict(),N=k.omit({data:!0}),E=l.z.object({id:l.z.string().trim().min(1).max(120),kind:l.z.enum(["image"]).optional()}).strict(),_=l.z.enum(["auto","queue","steer","immediate"]),v=l.z.object({message:l.z.string().trim().min(1).max(2e4),mode:_.optional()});v.extend({attachments:l.z.array(k).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+t.size,0)>f&&t.addIssue({code:l.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),v.extend({attachments:l.z.array(E).max(6).default([])}).strict(),l.z.object({decision:l.z.enum(["allow","deny","skip"])}).strict(),l.z.object({queueIds:l.z.array(l.z.string().trim().min(1).max(120)).max(100)}).strict(),l.z.object({code:l.z.string().trim().min(1).max(160),message:l.z.string(),instanceId:l.z.string().trim().min(1).max(160).optional(),nodeId:l.z.string().trim().min(1).max(160).optional(),route:l.z.string().trim().min(1).max(4096).optional()}).strict();const O=l.z.object({id:l.z.string().trim().min(1).max(120),message:l.z.string().trim().min(1).max(2e4),attachments:l.z.array(N).max(6).default([]),status:l.z.enum(["queued","sending","failed"]).default("queued"),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime(),error:l.z.string().trim().max(4e3).optional()}).strict(),x=l.z.object({pendingCount:l.z.number().int().min(0).default(0),items:l.z.array(O).max(100).default([])}).strict().default({pendingCount:0,items:[]}),T=l.z.enum(["control","realtime","adapter-snapshot","transcript-tail","transcript-scan","process-scan","app-session"]),C=l.z.object({id:l.z.string().trim().min(1).max(240),providerTurnId:l.z.string().trim().max(240).optional(),source:T.optional(),userPrompt:l.z.string().trim().optional(),status:l.z.enum(["queued","running","waiting","completed","failed"]).default("running"),phase:A.optional(),summary:l.z.string().trim().max(1e3).optional(),lastMessage:l.z.string().trim().optional(),revision:l.z.number().int().min(0).default(0),sourcePriority:l.z.number().int().min(0).max(100).optional(),snapshotVersion:l.z.number().int().min(0).optional(),observedAt:l.z.string().datetime().optional(),startedAt:l.z.string().datetime().optional(),updatedAt:l.z.string().datetime().optional(),completedAt:l.z.string().datetime().optional()}).strict(),D=l.z.object({id:l.z.string().trim().min(1).max(120),agent:I,appSessionId:l.z.string().trim().max(120).optional(),appId:l.z.string().trim().max(120).optional(),providerSessionId:l.z.string().trim().max(240).optional(),providerMeta:l.z.record(l.z.string(),l.z.unknown()).optional(),appBindingKeys:l.z.array(l.z.string().trim().min(1).max(240)).max(20).optional(),actions:S.optional(),activeTurnId:l.z.string().trim().max(240).optional(),conversationId:l.z.number().int().positive().optional(),title:l.z.string().trim().max(240).optional(),cwd:l.z.string().trim().max(4096).optional(),userPrompt:l.z.string().trim().optional(),turns:l.z.array(C).max(50).optional(),status:b.default("running"),phase:A.default("unknown"),summary:l.z.string().trim().max(1e3).optional(),lastMessage:l.z.string().trim().optional(),currentTool:z.optional(),transcriptPath:l.z.string().trim().max(4096).optional(),transcriptSize:l.z.number().int().min(0).optional(),startedAt:l.z.string().datetime(),updatedAt:l.z.string().datetime(),completedAt:l.z.string().datetime().optional(),error:l.z.string().trim().max(4e3).optional(),counters:w,queue:x}).strict();l.z.object({session:D,provider:l.z.string().trim().min(1).max(80),action:l.z.enum(["send","queue","steer","interrupt","approval"]),decision:l.z.enum(["allow","deny","skip"]).optional(),turnId:l.z.string().trim().min(1).max(240).optional(),providerTurnId:l.z.string().trim().min(1).max(240).optional(),queueId:l.z.string().trim().min(1).max(120).optional()}).strict();const j=D.pick({id:!0,agent:!0,appSessionId:!0,appId:!0,providerSessionId:!0,providerMeta:!0,appBindingKeys:!0,actions:!0,activeTurnId:!0,conversationId:!0,title:!0,cwd:!0,userPrompt:!0,turns:!0,status:!0,phase:!0,summary:!0,lastMessage:!0,currentTool:!0,queue:!0,startedAt:!0,updatedAt:!0,error:!0}),R=l.z.object({runningCount:l.z.number().int().min(0).default(0),waitingCount:l.z.number().int().min(0).default(0),staleCount:l.z.number().int().min(0).default(0),sessions:l.z.array(j).default([]),updatedAt:l.z.string().datetime()}).strict();l.z.object({streamId:l.z.string().trim().min(1).max(240),revision:l.z.number().int().min(0),lastEventAt:l.z.string().datetime(),snapshot:R}).strict(),R.pick({runningCount:!0,waitingCount:!0,staleCount:!0,updatedAt:!0}).extend({idleCount:l.z.number().int().min(0).default(0),problemCount:l.z.number().int().min(0).default(0),revision:l.z.number().int().min(0).optional()});const P=l.z.enum(["provider-event","app-session-created","app-session-updated","app-session-deleted","discovery-scan","control-action","heartbeat-sync","startup"]),M=l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),nodeId:l.z.string().trim().min(1).max(160).optional(),revision:l.z.number().int().min(0),previousRevision:l.z.number().int().min(0).optional(),traceId:l.z.string().trim().min(1).max(160),generatedAt:l.z.string().datetime(),reason:P}).strict(),F=l.z.object({meta:M,snapshot:R}).strict(),$=l.z.object({meta:M,upserted:l.z.array(j).default([]),removed:l.z.array(l.z.string().trim().min(1).max(120)).default([])}).strict(),L=l.z.object({meta:M,sessionIds:l.z.array(l.z.string().trim().min(1).max(120)).default([]),expiresAt:l.z.string().datetime()}).strict();l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),sinceRevision:l.z.number().int().min(0),latestRevision:l.z.number().int().min(0),earliestRetainedRevision:l.z.number().int().min(0),syncRequired:l.z.boolean().default(!1),events:l.z.array(l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("ai-session.snapshot"),payload:F}).strict(),l.z.object({type:l.z.literal("ai-session.patch"),payload:$}).strict(),l.z.object({type:l.z.literal("ai-session.removed"),payload:L}).strict()])).default([])}).strict();const U=l.z.object({source:T,sourcePriority:l.z.number().int().min(0).max(100).optional(),observedAt:l.z.string().datetime().optional(),snapshotVersion:l.z.number().int().min(0).optional()}).strict(),H=U.extend({type:l.z.literal("snapshot"),agent:l.z.string().trim().min(1).max(80),appSessionId:l.z.string().trim().max(120).optional(),appId:l.z.string().trim().max(120).optional(),providerSessionId:l.z.string().trim().max(240).optional(),providerMeta:l.z.record(l.z.string(),l.z.unknown()).optional(),appBindingKeys:l.z.array(l.z.string().trim().min(1).max(240)).max(20).optional(),actions:S.optional(),conversationId:l.z.number().int().positive().optional(),title:l.z.string().trim().max(240).optional(),cwd:l.z.string().trim().max(4096).optional(),activeTurnId:l.z.string().trim().max(240).optional(),userPrompt:l.z.string().trim().optional(),turns:l.z.array(C).max(50).optional(),status:b.optional(),phase:A.optional(),summary:l.z.string().trim().max(1e3).optional(),lastMessage:l.z.string().trim().optional(),transcriptPath:l.z.string().trim().max(4096).optional(),transcriptSize:l.z.number().int().min(0).optional(),replaceActivity:l.z.boolean().optional()}).strict(),K=U.extend({type:l.z.literal("event"),sessionId:l.z.string().trim().min(1).max(120),kind:l.z.enum(["lifecycle","send-ack","turn-started","user-message","assistant-message","approval-requested","turn-completed"]),activeTurnId:l.z.string().trim().max(240).optional(),providerTurnId:l.z.string().trim().max(240).optional(),userPrompt:l.z.string().trim().optional(),text:l.z.string().optional(),status:b.optional(),phase:A.optional(),summary:l.z.string().trim().max(1e3).optional(),error:l.z.string().trim().max(4e3).optional(),counters:l.z.object({toolCalls:l.z.number().int().min(0).optional(),edits:l.z.number().int().min(0).optional(),approvals:l.z.number().int().min(0).optional()}).strict().optional()}).strict();l.z.discriminatedUnion("type",[H,K]);const q=l.z.union([l.z.object({type:l.z.literal("schedule"),scheduleKind:l.z.literal("interval").default("interval"),intervalMs:l.z.number().int().min(1e3)}).strict(),l.z.object({type:l.z.literal("schedule"),scheduleKind:l.z.literal("daily"),timeOfDay:l.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:l.z.string().trim().min(1).max(120)}).strict(),l.z.object({type:l.z.literal("schedule"),scheduleKind:l.z.literal("weekly"),weekdays:l.z.array(l.z.number().int().min(0).max(6)).min(1).max(7),timeOfDay:l.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/),timezone:l.z.string().trim().min(1).max(120)}).strict()]),B=l.z.union([q,l.z.object({type:l.z.literal("file-change"),roots:l.z.array(l.z.string().trim().min(1).max(4096)).min(1).max(50),globs:l.z.array(l.z.string().trim().min(1).max(500)).min(1).max(100),ignore:l.z.array(l.z.string().trim().min(1).max(500)).max(100).optional(),debounceMs:l.z.number().int().min(100).max(6e4).default(1500)}).strict(),l.z.object({type:l.z.literal("ai-session"),agent:l.z.string().trim().min(1).max(80).optional(),statuses:l.z.array(l.z.enum(["running","waiting","idle","failed"])).max(20).optional(),phases:l.z.array(l.z.enum(["thinking","tool","editing","approval","responding","unknown"])).max(20).optional()}).strict()]),V=l.z.object({promptTemplate:l.z.string().trim().min(1).max(2e4)}).strict(),G=l.z.object({cooldownMs:l.z.number().int().min(0).max(864e5).optional(),maxConcurrentRuns:l.z.number().int().min(1).max(20).default(1),whenBusy:l.z.enum(["skip","queue"]).default("skip")}).strict(),J=l.z.object({configHash:l.z.string().trim().min(8).max(80),name:l.z.string().trim().min(1).max(160),description:l.z.string().trim().max(1e3).optional(),source:B,action:V,policy:G.default({maxConcurrentRuns:1,whenBusy:"skip"}),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}).strict(),W=l.z.object({type:l.z.literal("ai-session"),aiSessionId:l.z.string().trim().min(1).max(160)}).strict(),Y=l.z.object({configHash:l.z.string().trim().min(8).max(80),deploymentId:l.z.string().trim().min(1).max(240).optional(),instanceId:l.z.string().trim().min(1).max(160),origin:l.z.enum(["control-plane","controlled-instance"]).default("controlled-instance"),enabled:l.z.boolean().default(!0),target:W,localName:l.z.string().trim().min(1).max(160).optional(),createdAt:l.z.string().datetime(),updatedAt:l.z.string().datetime()}).strict(),X=l.z.object({configHash:l.z.string().trim().min(8).max(80),deploymentId:l.z.string().trim().min(1).max(240).optional(),instanceId:l.z.string().trim().min(1).max(160),status:l.z.enum(["idle","running","disabled","error"]).default("idle"),lastTriggeredAt:l.z.string().datetime().optional(),lastCompletedAt:l.z.string().datetime().optional(),lastSkippedAt:l.z.string().datetime().optional(),lastError:l.z.string().trim().max(4e3).optional(),runCount:l.z.number().int().min(0).default(0),skippedCount:l.z.number().int().min(0).default(0)}).strict(),Q=l.z.object({id:l.z.string().trim().min(1).max(160),configHash:l.z.string().trim().min(8).max(80),deploymentId:l.z.string().trim().min(1).max(240).optional(),instanceId:l.z.string().trim().min(1).max(160),eventType:l.z.enum(["manual","schedule","file-change","ai-session"]),status:l.z.enum(["started","completed","failed","skipped"]),target:W,promptPreview:l.z.string().trim().max(500),eventSummary:l.z.string().trim().max(1e3).optional(),error:l.z.string().trim().max(4e3).optional(),startedAt:l.z.string().datetime(),completedAt:l.z.string().datetime().optional()}).strict();l.z.object({schemaVersion:l.z.literal(1).default(1),configs:l.z.array(J).default([]),deployments:l.z.array(Y).default([]),runtime:l.z.array(X).default([]),recentRuns:l.z.array(Q).default([])}).strict();const Z="2026-07-15",ee=l.z.string(),te=l.z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/),ne=l.z.string().datetime(),re=l.z.record(l.z.string(),l.z.string()).default({}),oe=l.z.record(l.z.string(),l.z.string()).default({}),ie=l.z.object({codexModelHash:te.optional(),claudeModelHash:te.optional()}).strict().default({}),se=l.z.object({id:te,name:l.z.string().trim().min(1).max(120),kind:l.z.enum(["tty","gui","web"]),source:l.z.enum(["builtin","custom"]),availability:l.z.enum(["available","missing-dependency"]),capabilities:l.z.object({automation:l.z.enum(["cdp"]).optional(),supportsCwdSelection:l.z.boolean().default(!1)}).strict().default({supportsCwdSelection:!1}),diagnosticCode:l.z.enum(["APP_EXECUTABLE_NOT_FOUND"]).optional()}).strict(),ae=l.z.object({code:l.z.enum(["APP_CATALOG_INVALID"]),message:l.z.string().trim().min(1).max(500)}).strict(),ce=l.z.object({items:l.z.array(se).max(256),observedAt:ne,issues:l.z.array(ae).max(32).default([])}).strict(),de=l.z.object({component:l.z.enum(["control-plane","node-agent","controlled-instance"]),packageName:l.z.string().trim().max(160).optional(),packageVersion:l.z.string().trim().max(80).optional(),protocolVersion:ee.optional(),buildId:l.z.string().trim().max(160).optional(),builtAt:l.z.string().trim().max(120).optional(),gitCommit:l.z.string().trim().max(120).optional(),imageRef:l.z.string().trim().max(512).optional(),imageDigest:l.z.string().trim().max(240).optional()}).strict(),le=l.z.enum(["stable","beta","alpha"]),ue=l.z.discriminatedUnion("component",[l.z.object({component:l.z.literal("node-agent")}).strict(),l.z.object({component:l.z.literal("controlled-instance"),instanceId:te}).strict()]),pe=l.z.object({target:ue,channel:le.default("stable")}).strict(),me=l.z.object({target:ue,source:l.z.enum(["npm","docker-registry"]),channel:le,currentVersion:l.z.string().trim().max(240).optional(),availableVersion:l.z.string().trim().min(1).max(240),artifactRef:l.z.string().trim().min(1).max(512).optional(),updateAvailable:l.z.boolean(),supported:l.z.boolean().default(!0),reason:l.z.string().trim().max(2048).optional(),checkedAt:ne}).strict(),ge=l.z.object({id:te,nodeId:te,target:ue,source:l.z.enum(["npm","docker-registry"]),channel:le,fromVersion:l.z.string().trim().max(240).optional(),toVersion:l.z.string().trim().min(1).max(240),artifactRef:l.z.string().trim().min(1).max(512).optional(),status:l.z.enum(["queued","updating","restarting","succeeded","failed"]),error:l.z.string().trim().max(4096).optional(),startedAt:ne.optional(),completedAt:ne.optional(),createdAt:ne,updatedAt:ne}).strict(),he=pe,fe=l.z.object({type:l.z.enum(["branch","tag","commit"]),name:l.z.string().trim().min(1).max(240).optional(),commit:l.z.string().trim().max(80).optional()}).strict(),ye=l.z.object({type:l.z.enum(["none","ssh-key","https-token"]).default("none"),secretId:l.z.string().trim().min(1).max(120).optional()}).strict(),Ie=l.z.object({depth:l.z.number().int().positive().max(1e5).optional(),submodules:l.z.boolean().default(!1),lfs:l.z.boolean().default(!1),subdirectory:l.z.string().trim().max(240).default("")}).strict();l.z.object({id:te,name:l.z.string().trim().min(1).max(160),url:l.z.string().trim().min(1).max(2048),provider:l.z.string().trim().max(80).optional(),ref:fe.default({type:"branch",name:"main"}),auth:ye.default({type:"none"}),clone:Ie.default({submodules:!1,lfs:!1,subdirectory:""}),defaultImageId:te.optional(),labels:re,createdAt:ne,updatedAt:ne}).strict();const be=l.z.object({id:te,nodeId:te,name:l.z.string().trim().min(1).max(160),path:l.z.string().trim().min(1).max(4096),defaultImageId:te.optional(),labels:re,createdAt:ne,updatedAt:ne}).strict(),Ae=l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("git-repository"),repositoryId:te.optional(),url:l.z.string().trim().min(1).max(2048),provider:l.z.string().trim().max(80).optional(),ref:fe.default({type:"branch",name:"main"}),auth:ye.default({type:"none"}),clone:Ie.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),l.z.object({type:l.z.literal("git-template"),url:l.z.string().trim().min(1).max(2048),templateId:l.z.string().trim().min(1).max(120).optional(),ref:fe.default({type:"branch",name:"main"}),auth:ye.default({type:"none"}),clone:Ie.default({submodules:!1,lfs:!1,subdirectory:""})}).strict(),l.z.object({type:l.z.literal("local-folder"),localFolderId:te.optional(),path:l.z.string().trim().min(1).max(4096),ownerNodeId:te.optional()}).strict()]),ze=l.z.object({mode:l.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]),path:l.z.string().trim().min(1).max(4096).default("/workspace"),volumeName:l.z.string().trim().max(240).optional(),readOnly:l.z.boolean().default(!1)}).strict(),we=l.z.enum(["codex","claude"]),Se=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),source:Ae,defaultImageId:te.optional(),defaultNodeId:te.optional(),defaultRuntimeId:te.optional(),workspacePolicy:ze,labels:re,createdAt:ne,updatedAt:ne}).strict(),ke=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),endpoint:l.z.string().trim().min(1).max(2048),key:l.z.string().trim().min(1).max(4096),model:l.z.string().trim().min(1).max(240),app:we,enabled:l.z.boolean().default(!0),order:l.z.number().int().min(0).max(1e6).default(0),labels:re,createdAt:ne,updatedAt:ne}).strict(),Ne=ke,Ee=ke.omit({key:!0}).extend({keyPreview:l.z.string().trim().min(1).max(160),keySet:l.z.boolean()}).strict(),_e=Ee.extend({referenceCount:l.z.number().int().min(0)}).strict(),ve=l.z.object({instanceId:te,codexModelHash:te.optional(),claudeModelHash:te.optional(),updatedAt:ne}).strict(),Oe=l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("control-plane"),name:ke.shape.name,enabled:ke.shape.enabled,order:ke.shape.order}).strict(),l.z.object({type:l.z.literal("node"),nodeId:te,name:ke.shape.name,enabled:ke.shape.enabled,order:ke.shape.order,referenceCount:l.z.number().int().min(0)}).strict()]),xe=l.z.object({id:te,model:Ee,locations:l.z.array(Oe).min(1),referenceCount:l.z.number().int().min(0).default(0)}).strict(),Te=l.z.object({nodeId:te,code:l.z.string().trim().min(1).max(120),message:l.z.string().trim().min(1).max(2e3)}).strict();l.z.object({models:l.z.array(xe),nodeDiagnostics:l.z.array(Te).default([]),updatedAt:ne}).strict();const Ce=ke.omit({id:!0,createdAt:!0,updatedAt:!0}).strict(),De=Ce.partial().strict(),je=ke,Re=l.z.object({modelSelection:ie,codexModelHash:te.optional(),claudeModelHash:te.optional()}).strict();function Pe(e){const t={app:we.parse(e.app),endpoint:ke.shape.endpoint.parse(e.endpoint),key:ke.shape.key.parse(e.key),model:ke.shape.model.parse(e.model)};return`mdl_${u.createHash("sha256").update(JSON.stringify(t)).digest("hex")}`}const Me=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),image:l.z.string().trim().min(1).max(512),registry:l.z.string().trim().min(1).max(160).default("local"),capabilities:l.z.array(l.z.string().trim().min(1).max(80)).default([]),optionalApps:l.z.array(l.z.string().trim().min(1).max(120)).default([]),defaultEnv:oe,labels:re,createdAt:ne,updatedAt:ne}).strict(),Fe=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),connectionMode:l.z.enum(["local-ipc","local-loopback","direct-http","reverse-wss"]).default("direct-http"),auth:l.z.object({mode:l.z.enum(["local-static-key","paired-hmac"]).default("local-static-key"),keyId:te.optional(),secret:l.z.string().trim().max(4096).optional(),pairedAt:ne.optional(),pairing:l.z.object({status:l.z.enum(["pending","paired","expired"]).default("pending"),joinToken:l.z.string().trim().max(4096).optional(),expiresAt:ne.optional()}).strict().optional()}).strict().default({mode:"local-static-key"}),endpoint:l.z.string().trim().max(2048).optional(),controlEndpoint:l.z.string().trim().max(2048).optional(),containerEndpoint:l.z.string().trim().max(2048).optional(),publicWebBase:l.z.string().trim().max(2048).optional(),status:l.z.enum(["unknown","online","offline","degraded"]).default("unknown"),health:l.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),appInventory:ce.optional(),labels:re,lastSeenAt:ne.optional(),createdAt:ne,updatedAt:ne}).strict(),$e=l.z.object({id:te,nodeId:te,name:l.z.string().trim().min(1).max(160),type:l.z.enum(["docker","kubernetes","local"]),status:l.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:l.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),labels:re,createdAt:ne,updatedAt:ne}).strict();l.z.object({ok:l.z.boolean().optional(),role:l.z.string().optional(),nodeId:te.optional()}).passthrough();const Le=l.z.object({bindScope:l.z.enum(["loopback","all-ipv4"]),port:l.z.number().int().min(1).max(65535)}).strict(),Ue=Le.extend({host:l.z.enum(["127.0.0.1","0.0.0.0"]),status:l.z.enum(["listening","error"]),source:l.z.enum(["bootstrap","persisted"]),error:l.z.string().trim().max(2048).optional()}).strict(),He=Le;l.z.object({nodeId:te,joinToken:l.z.string().trim().min(1),expiresAt:ne}).passthrough(),l.z.object({id:te,keyId:te,url:l.z.string().trim().min(1).max(2048).optional(),name:l.z.string().trim().min(1).max(160).optional(),pairedAt:ne,updatedAt:ne.optional(),active:l.z.boolean().optional(),current:l.z.boolean().optional()}).passthrough(),l.z.object({remote:l.z.object({id:te,url:l.z.string().trim().min(1).max(2048).optional(),keyId:te,pairedAt:ne,active:l.z.boolean()}).passthrough(),tunnel:l.z.object({status:l.z.enum(["disabled","saved","connecting","connected","failed"]),error:l.z.string().trim().max(2048).optional()}).passthrough()}).passthrough(),l.z.object({deleted:l.z.boolean().optional()}).passthrough(),l.z.object({repository:l.z.string().trim().optional(),tag:l.z.string().trim().optional(),id:l.z.string().trim(),createdSince:l.z.string().trim().optional(),size:l.z.string().trim().optional(),reference:l.z.string().trim().min(1)}).passthrough();const Ke=l.z.lazy(()=>l.z.object({name:l.z.string().trim().min(1),path:l.z.string().trim().min(1).max(4096),children:l.z.array(Ke)}).passthrough());l.z.object({status:l.z.number().int().min(100).max(599).optional(),headers:l.z.record(l.z.string(),l.z.string()).optional(),bodyBase64:l.z.string().optional()}).passthrough();const qe=l.z.object({strategy:l.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).default("node-proxy"),web:l.z.string().trim().max(2048).optional(),api:l.z.string().trim().max(2048).optional(),vnc:l.z.string().trim().max(2048).optional(),tty:l.z.string().trim().max(2048).optional(),logs:l.z.string().trim().max(2048).optional(),status:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict(),Be=l.z.object({strategy:l.z.enum(["control-plane-proxy","direct-port","node-proxy","kubernetes-ingress","kubernetes-port-forward"]).default("control-plane-proxy"),web:l.z.string().trim().max(2048).optional(),api:l.z.string().trim().max(2048).optional(),ws:l.z.string().trim().max(2048).optional(),status:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown")}).strict().default({strategy:"control-plane-proxy",status:"unknown"}),Ve=l.z.object({mode:l.z.enum(["local-bind","git-clone","empty-volume","persistent-volume"]).optional(),status:l.z.enum(["unknown","pending","ready","failed"]).default("unknown"),path:l.z.string().trim().max(4096).optional(),resolvedCommit:l.z.string().trim().max(120).optional(),error:l.z.string().trim().max(2048).optional()}).passthrough(),Ge=l.z.object({id:te,name:l.z.string().trim().min(1).max(160),projectId:te.optional(),source:Ae,sourceSnapshot:l.z.record(l.z.string(),l.z.unknown()).default({}),modelSelection:ie,nodeId:te,runtimeId:te,imageId:te.optional(),imageSnapshot:Me.optional(),status:l.z.enum(["created","provisioning","starting","registering","registered","running","stopping","stopped","failed","unhealthy"]).default("created"),health:l.z.enum(["unknown","ok","degraded","failed"]).default("unknown"),connectionStatus:l.z.enum(["unknown","online","offline","endpoint-unreachable"]).default("unknown"),agentStatus:l.z.enum(["unknown","online","offline"]).default("unknown"),targetStatus:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),uiAccessStatus:l.z.enum(["unknown","reachable","endpoint-unreachable"]).default("unknown"),controlMode:l.z.enum(["standalone","controlled"]).default("controlled"),protocolVersion:ee.optional(),instanceVersion:l.z.string().trim().max(80).optional(),build:de.optional(),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),appInventory:ce.optional(),config:l.z.object({autoImportAgentConfigs:l.z.boolean().default(!0)}).strict().default({autoImportAgentConfigs:!0}),workspace:Ve.default({status:"unknown"}),target:qe.default({strategy:"direct-port",status:"unknown"}),access:Be,apps:l.z.object({runningCount:l.z.number().int().min(0).default(0),problemCount:l.z.number().int().min(0).default(0),updatedAt:ne.optional(),revision:l.z.number().int().min(0).optional()}).strict().default({runningCount:0,problemCount:0}),aiSessions:R.default(()=>({runningCount:0,waitingCount:0,staleCount:0,sessions:[],updatedAt:(new Date).toISOString()})),triggers:l.z.object({enabledCount:l.z.number().int().min(0).default(0),runningCount:l.z.number().int().min(0).default(0),errorCount:l.z.number().int().min(0).default(0),configs:l.z.array(l.z.object({configHash:l.z.string().trim().min(1),config:J,deployments:l.z.array(Y).default([]),runtime:l.z.array(X).default([])}).strict()).default([]),recentRuns:l.z.array(Q).default([])}).strict().optional(),runtime:l.z.object({kind:l.z.string().trim().max(80).optional(),containerName:l.z.string().trim().max(240).optional(),containerId:l.z.string().trim().max(240).optional(),workspacePath:l.z.string().trim().max(4096).optional(),pid:l.z.number().int().positive().optional(),port:l.z.number().int().positive().max(65535).optional(),labels:l.z.record(l.z.string(),l.z.string()).default({})}).strict().default({labels:{}}),registrationToken:l.z.string().trim().min(1).max(240).optional(),lastHeartbeatAt:ne.optional(),createdAt:ne,updatedAt:ne}).strict();function Je(e,t){return Ge.safeParse(We(e,t))}function We(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e;Object.prototype.hasOwnProperty.call(n,"receiver")&&t?.({instanceId:"string"==typeof n.id?n.id:void 0,field:"receiver"});const r=new Set(Object.keys(Ge.shape)),o={};for(const[e,t]of Object.entries(n))r.has(e)&&(o[e]=t);if(n.capabilities&&"object"==typeof n.capabilities&&!Array.isArray(n.capabilities)){const e={...n.capabilities};Object.prototype.hasOwnProperty.call(e,"apps")&&(t?.({instanceId:"string"==typeof n.id?n.id:void 0,field:"capabilities.apps"}),delete e.apps),o.capabilities=e}return o.appInventory=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return;const t=e,n={observedAt:t.observedAt,items:Array.isArray(t.items)?t.items.map(e=>{const t=Ye(e,["id","name","kind","source","availability","diagnosticCode"]);return!t||"object"!=typeof t||Array.isArray(t)?t:{...t,capabilities:Ye(e.capabilities,["automation","supportsCwdSelection"])}}):t.items,issues:Array.isArray(t.issues)?t.issues.map(e=>Ye(e,["code","message"])):t.issues},r=ce.safeParse(n);return r.success?r.data:void 0}(n.appInventory),o.apps=Ye(n.apps,["runningCount","problemCount","updatedAt","revision"]),o.config=Ye(n.config,["autoImportAgentConfigs"]),o.modelSelection=Ye(n.modelSelection,["codexModelHash","claudeModelHash"]),o.runtime=Ye(n.runtime,["kind","containerName","containerId","workspacePath","pid","port","labels"]),o.target=Ye(n.target??n.endpoints,["strategy","web","api","status"]),o.access=Ye(n.access,["strategy","web","api","ws","status"]),o}function Ye(e,t){if(!e||"object"!=typeof e||Array.isArray(e))return e;const n=e;return Object.fromEntries(t.filter(e=>Object.prototype.hasOwnProperty.call(n,e)).map(e=>[e,n[e]]))}const Xe=l.z.object({instanceId:te.optional(),name:l.z.string().trim().min(1).max(160),projectId:te.optional(),source:Ae.optional(),nodeId:te.optional(),runtimeId:te.optional(),imageId:te.optional(),instanceVersion:l.z.string().trim().max(80).optional(),protocolVersion:ee,build:de.optional(),controlMode:l.z.enum(["standalone","controlled"]).default("controlled"),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),appInventory:ce.optional(),target:qe.default({strategy:"direct-port",status:"unknown"}),workspace:Ve.default({status:"unknown"}),registrationToken:l.z.string().trim().max(240).optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Z||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol register payload requires appInventory."})}),Qe=l.z.object({status:Ge.shape.status.optional(),health:Ge.shape.health.optional(),protocolVersion:ee,build:de.optional(),capabilities:Ge.shape.capabilities.optional(),appInventory:ce.optional(),apps:Ge.shape.apps.optional(),aiSessions:Ge.shape.aiSessions.optional(),triggers:Ge.shape.triggers.optional(),workspace:Ve.optional(),target:qe.partial().optional()}).strict().superRefine((e,t)=>{e.protocolVersion!==Z||e.appInventory||t.addIssue({code:"custom",path:["appInventory"],message:"Current protocol heartbeat payload requires appInventory."})}),Ze=l.z.enum(["web","telegram","wechat","dingding"]);function et(e){return e.readyState===(e.OPEN??1)}function tt(e,t,n){e.close(function(e){if("number"==typeof e&&Number.isInteger(e))return e>=3e3&&e<=4999||e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e?e:void 0}(t),function(e){const t=Buffer.isBuffer(e)?e.toString("utf8"):"string"==typeof e?e:"";if(Buffer.byteLength(t,"utf8")<=123)return t;let n="";for(const e of t){const t=`${n}${e}`;if(Buffer.byteLength(t,"utf8")>123)break;n=t}return n}(n)||void 0)}function nt(e,t){e.send(t.data,{binary:t.isBinary})}function rt(e,t){if(et(e))for(const n of t.splice(0))nt(e,n)}function ot(e,t){return new Promise((n,r)=>{const o=s.spawn(e,t,{stdio:["ignore","pipe","pipe"]}),i=[],a=[];o.stdout.on("data",e=>i.push(Buffer.from(e))),o.stderr.on("data",e=>a.push(Buffer.from(e))),o.on("error",r),o.on("close",t=>{const o={stdout:Buffer.concat(i).toString("utf8").trim(),stderr:Buffer.concat(a).toString("utf8").trim()};if(0===t)return void n(o);const s=new Error(o.stderr||`${e} exited with code ${t}`);Object.assign(s,{statusCode:502,code:"RUNTIME_EXECUTOR_FAILED",details:o}),r(s)})})}l.z.object({id:te,channel:Ze,name:l.z.string().trim().min(1).max(160),enabled:l.z.boolean().default(!1),token:l.z.string().trim().max(4096).optional(),defaultChatId:l.z.string().trim().max(240).optional(),allowedUserIds:l.z.array(l.z.string().trim().min(1).max(240)).default([]),pollIntervalMs:l.z.number().int().positive().max(6e4).default(3e3),settings:l.z.record(l.z.string(),l.z.unknown()).default({}),createdAt:ne,updatedAt:ne}).strict(),l.z.object({id:te,channel:Ze,bridgeId:te.optional(),chatSessionId:l.z.string().trim().min(1).max(240),userId:l.z.string().trim().max(240).optional(),activeProjectId:te.optional(),activeInstanceId:te.optional(),activeAiSessionId:l.z.string().trim().min(1).max(120).optional(),lastUsedAt:ne,createdAt:ne,updatedAt:ne}).strict(),l.z.object({source:l.z.object({type:l.z.literal("chat-gateway").default("chat-gateway"),channel:Ze,bridgeId:te.optional(),chatSessionId:l.z.string().trim().min(1).max(240),userId:l.z.string().trim().max(240).optional()}).strict(),message:l.z.object({text:l.z.string().trim().max(2e4).default(""),attachments:l.z.array(k).max(6).default([])}).strict().superRefine((e,t)=>{e.attachments.reduce((e,t)=>e+t.size,0)>f&&t.addIssue({code:l.z.ZodIssueCode.custom,path:["attachments"],message:"Images must be 41943040 bytes or less in total."})}),target:l.z.object({instanceId:te,aiSessionId:l.z.string().trim().min(1).max(120)}).strict().optional()}).strict(),l.z.object({id:te,instanceId:te,projectId:te.optional(),aiSessionId:l.z.string().trim().min(1).max(120),providerSessionId:l.z.string().trim().max(240).optional(),result:l.z.string().default(""),timeoutMs:l.z.number().positive().optional(),source:l.z.string().trim().max(120).optional(),kind:l.z.enum(["task","approval"]).default("task"),status:l.z.enum(["pending","unreachable"]).default("pending"),error:l.z.string().trim().max(2048).optional(),lastSeenAt:ne}).strict();class it{runCommand;publishHost;constructor(e=ot,t={}){this.runCommand=e,this.publishHost=t.publishHost||"127.0.0.1"}async start(e){ct(e);const t=e.instance.runtime.containerName||at(e.instance.id);if(await this.runCommand("docker",["start",t]).catch(()=>{}))return this.runningResult(e,t,e.instance.runtime.containerId);await this.ensureImageAvailable(e.image);const n=await this.runCommand("docker",function(e,t,n={}){const r=n.publishHost||"127.0.0.1",o=e.node?.id||"node_unset",i=e.runtime?.id||"runtime_local_docker",s=["run","-d","--name",t,"--label",`task-handoff.instance-id=${e.instance.id}`,"--label",`task-handoff.project-id=${e.project.id}`,"--label",`task-handoff.node-id=${o}`,"--label",`task-handoff.runtime-id=${i}`,"--label",`task-handoff.image-id=${e.image.id}`,"--shm-size","1gb","--security-opt","seccomp=unconfined","--network","bridge","--add-host","host.docker.internal:host-gateway","--tmpfs","/tmp:rw,mode=1777","-v",`${t}-data:/data`,"-v",`${t}-agent-home:/home/agent`,"-p",`${r}::8080`,"-e","TASK_HANDOFF_CONTROL_MODE=controlled","-e",`TASK_HANDOFF_NODE_AGENT_URL=${e.nodeAgentUrl||e.node?.endpoint||""}`,"-e",`TASK_HANDOFF_INSTANCE_ID=${e.instance.id}`,"-e",`TASK_HANDOFF_INSTANCE_NAME=${e.instance.name}`,"-e",`TASK_HANDOFF_REGISTRATION_TOKEN=${e.instance.registrationToken||""}`,"-e",`TASK_HANDOFF_PROJECT_ID=${e.project.id}`,"-e",`TASK_HANDOFF_NODE_ID=${o}`,"-e",`TASK_HANDOFF_RUNTIME_ID=${i}`,"-e",`TASK_HANDOFF_IMAGE_ID=${e.image.id}`,"-e","TASK_HANDOFF_CHAT_BRIDGES=none","-e",`TASK_HANDOFF_WORKSPACE=${e.project.workspacePolicy.path||"/workspace"}`,"-e",`TASK_HANDOFF_WORKSPACE_MODE=${e.project.workspacePolicy.mode}`];"local-folder"===e.project.source.type?s.push("-v",`${e.project.source.path}:${e.project.workspacePolicy.path||"/workspace"}:${e.project.workspacePolicy.readOnly?"ro":"rw"}`):(s.push("-e",`TASK_HANDOFF_PROJECT_SOURCE=${JSON.stringify(e.project.source)}`),s.push("-e",`TASK_HANDOFF_WORKSPACE_POLICY=${JSON.stringify(e.project.workspacePolicy)}`),s.push("-e",`TASK_HANDOFF_GIT_URL=${e.project.source.url}`),e.project.source.ref?.commit?s.push("-e",`TASK_HANDOFF_GIT_COMMIT=${e.project.source.ref.commit}`):e.project.source.ref?.name&&s.push("-e",`TASK_HANDOFF_GIT_REF=${e.project.source.ref.name}`),e.project.source.clone?.depth&&s.push("-e",`TASK_HANDOFF_GIT_DEPTH=${e.project.source.clone.depth}`),s.push("-e","TASK_HANDOFF_GIT_SUBMODULES="+(e.project.source.clone?.submodules?"true":"false")),s.push("-e","TASK_HANDOFF_GIT_LFS="+(e.project.source.clone?.lfs?"true":"false")),e.project.source.clone?.subdirectory&&s.push("-e",`TASK_HANDOFF_WORKSPACE_SUBDIRECTORY=${e.project.source.clone.subdirectory}`));for(const[t,n]of Object.entries(e.image.defaultEnv))s.push("-e",`${t}=${n}`);for(const[t,n]of Object.entries(e.modelEnv||{}))s.push("-e",`${t}=${n}`);return s.push(e.image.image),s}(e,t,{publishHost:this.publishHost}));return this.runningResult(e,t,n.stdout||void 0)}async runningResult(e,t,n){const r=await this.containerPort(t,"8080/tcp"),o=r?`http://127.0.0.1:${r}`:void 0,i=e.node?.id||"node_unset",s=e.runtime?.id||"runtime_local_docker";return{status:"registering",health:"unknown",connectionStatus:o?"online":"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:o?"reachable":"unknown",...o?{web:o,api:`${o}/api`}:{}},workspace:{mode:e.project.workspacePolicy.mode,status:"pending",path:e.project.workspacePolicy.path},runtime:{...e.instance.runtime,kind:"docker",containerName:t,...n?{containerId:n}:{},labels:{...e.instance.runtime.labels,"task-handoff.instance-id":e.instance.id,"task-handoff.project-id":e.project.id,"task-handoff.node-id":i,"task-handoff.runtime-id":s,"task-handoff.image-id":e.image.id}}}}async stop(e){return await this.runCommand("docker",["stop",e.instance.runtime.containerName||at(e.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async restart(e){ct(e);const t=e.instance.runtime.containerName||at(e.instance.id);return await this.runCommand("docker",["restart",t]),this.runningResult(e,t,e.instance.runtime.containerId)}async delete(e){return await this.runCommand("docker",["rm","-f",e.instance.runtime.containerName||at(e.instance.id)]).catch(()=>({stdout:"",stderr:""})),{status:"stopped",health:"unknown",connectionStatus:"offline",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async containerPort(e,t){const n=(await this.runCommand("docker",["port",e,t]).catch(()=>({stdout:"",stderr:""}))).stdout.split(/\r?\n/).find(Boolean),r=n?.match(/:(\d+)$/);return r?.[1]}async ensureImageAvailable(e){if(!await this.runCommand("docker",["image","inspect",e.image]).then(()=>!0,()=>!1)){if("local"===e.registry){const t=new Error(`Local Docker image ${e.image} was not found. Build it or add an existing local image before starting an instance.`);throw Object.assign(t,{statusCode:400,code:"LOCAL_IMAGE_NOT_FOUND"}),t}await this.runCommand("docker",["pull",e.image])}}}async function st(e=ot){return(await e("docker",["images","--format","{{json .}}"])).stdout.split(/\r?\n/).map(e=>e.trim()).filter(Boolean).map(e=>{const t=JSON.parse(e),n=t.Repository||"<none>",r=t.Tag||"<none>",o=t.ID||t.IDShort||"",i="<none>"!==n&&"<none>"!==r?`${n}:${r}`:o;return{repository:n,tag:r,id:o,createdSince:t.CreatedSince,size:t.Size,reference:i}}).filter(e=>Boolean(e.reference))}function at(e){return`task-handoff-${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`}function ct(e){if("local-folder"===e.project.source.type&&e.project.source.ownerNodeId&&e.project.source.ownerNodeId!==e.node.id){const e=new Error("Local folder projects can only start on the node that owns the folder.");throw Object.assign(e,{statusCode:400,code:"LOCAL_FOLDER_REQUIRES_OWNER_NODE"}),e}if(!e.image.image.trim()){const e=new Error("Image profile must reference a registered image.");throw Object.assign(e,{statusCode:400,code:"IMAGE_PROFILE_INVALID"}),e}}const dt=l.z.enum(["created","starting","running","stopping","stopped","exited","failed","closed","terminated","unknown"]),lt=l.z.object({type:l.z.enum(["app-session","provider-session","adapter-key"]),id:l.z.string().trim().min(1).max(240),agent:l.z.string().trim().min(1).max(80).optional(),adapter:l.z.string().trim().min(1).max(80).optional(),key:l.z.string().trim().min(1).max(240).optional()}).strict(),ut=l.z.record(l.z.string(),l.z.unknown()).and(l.z.object({id:l.z.string().trim().min(1).max(160),appId:l.z.string().trim().min(1).max(160).optional(),title:l.z.string().trim().max(240).optional(),kind:l.z.string().trim().max(80).optional(),status:dt.default("unknown"),bindings:l.z.array(lt).max(40).default([]),workspace:l.z.object({cwd:l.z.string().trim().max(4096).optional()}).strict().optional(),createdAt:l.z.string().datetime().optional(),updatedAt:l.z.string().datetime().optional()})),pt=l.z.object({runningCount:l.z.number().int().min(0).default(0),problemCount:l.z.number().int().min(0).default(0),sessions:l.z.array(ut).default([]),updatedAt:l.z.string().datetime()}).strict();l.z.object({streamId:l.z.string().trim().min(1).max(240),revision:l.z.number().int().min(0),lastEventAt:l.z.string().datetime(),snapshot:pt}).strict(),pt.pick({runningCount:!0,problemCount:!0,updatedAt:!0}).extend({revision:l.z.number().int().min(0).optional()});const mt=l.z.enum(["app-session-created","app-session-updated","app-session-deleted","app-session-recovered","control-action","heartbeat-sync","startup"]),gt=l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),nodeId:l.z.string().trim().min(1).max(160).optional(),revision:l.z.number().int().min(0),previousRevision:l.z.number().int().min(0).optional(),traceId:l.z.string().trim().min(1).max(160),generatedAt:l.z.string().datetime(),reason:mt}).strict(),ht=l.z.object({meta:gt,snapshot:pt}).strict(),ft=l.z.object({meta:gt,session:ut}).strict(),yt=l.z.object({meta:gt,sessionId:l.z.string().trim().min(1).max(160),tombstone:ut.optional(),expiresAt:l.z.string().datetime().optional()}).strict();function It(e){return e.startsWith("ai-session.")||e.startsWith("ai.session.")?"ai.sessions":e.startsWith("app-session.")?"app.sessions":e.startsWith("receiver.")?"receiver":e.startsWith("conversation.")?"conversations":e.startsWith("trigger.")?"triggers":e.startsWith("channel.")?"channels":e.startsWith("instance.")?"instances":e.startsWith("node.")?"nodes":e.startsWith("project.")?"projects":e.startsWith("model.")?"models":e.startsWith("image.")?"images":"system"}l.z.object({streamId:l.z.string().trim().min(1).max(240),instanceId:l.z.string().trim().min(1).max(160),sinceRevision:l.z.number().int().min(0),latestRevision:l.z.number().int().min(0),earliestRetainedRevision:l.z.number().int().min(0),syncRequired:l.z.boolean().default(!1),events:l.z.array(l.z.discriminatedUnion("type",[l.z.object({type:l.z.literal("app-session.snapshot"),payload:ht}).strict(),l.z.object({type:l.z.literal("app-session.patch"),payload:ft}).strict(),l.z.object({type:l.z.literal("app-session.removed"),payload:yt}).strict()])).default([])}).strict();const bt=l.z.enum(["ai.sessions","app.sessions"]),At=l.z.object({topic:bt,instanceId:l.z.string().trim().min(1).max(160),streamId:l.z.string().trim().min(1).max(240),latestRevision:l.z.number().int().min(0),earliestRetainedRevision:l.z.number().int().min(0)}).strict(),zt=l.z.object({protocolVersion:l.z.literal(1),streams:l.z.array(At)}).strict();class wt{attempt=0;timer;get pending(){return Boolean(this.timer)}get attempts(){return this.attempt}schedule(e,t={}){if(this.timer)return;const n=function(e,t=Math.random){const n=Math.min(3e4,1e3*2**Math.max(0,e));return Math.min(3e4,Math.round(n*(.75+.5*t())))}(this.attempt,t.random);this.attempt+=1;const r=t.setTimeoutFn||setTimeout;return this.timer=r(()=>{this.timer=void 0,e()},n),{attempt:this.attempt,delay:n}}reset(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0,this.attempt=0}cancel(e=clearTimeout){this.timer&&e(this.timer),this.timer=void 0}}class St{sockets=new Map;socketUrls=new Map;retries=new Map;outputs=new Set;state;token;logger;safetyIntervalMs;lastAiSessionEventByInstance=new Map;lastAppSessionEventByInstance=new Map;timer;reconnectAttempts=0;safetyReconciliations=0;createSocket;setIntervalFn;clearIntervalFn;constructor(e,t,n={}){var r;this.state=e,this.token=t,this.logger=n.logger,this.safetyIntervalMs=(r=n.safetyIntervalMs,Math.min(6e4,Math.max(3e4,r??45e3))),this.createSocket=n.createSocket||((e,t)=>new d(e,t)),this.setIntervalFn=n.setIntervalFn||setInterval,this.clearIntervalFn=n.clearIntervalFn||clearInterval}start(){this.sync(),this.timer=this.setIntervalFn(()=>{this.safetyReconciliations+=1,this.sync()},this.safetyIntervalMs)}stop(){this.timer&&(this.clearIntervalFn(this.timer),this.timer=void 0);for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();this.retries.clear(),this.outputs.clear()}addOutput(e){return this.outputs.add(e),e.on("close",()=>this.outputs.delete(e)),e.on("error",()=>this.outputs.delete(e)),this.syncNow(),()=>{this.outputs.delete(e)}}syncNow(){this.sync()}diagnostics(){return{reconnectAttempts:this.reconnectAttempts,safetyReconciliations:this.safetyReconciliations,activeConnections:this.sockets.size,pendingRetries:[...this.retries.values()].filter(e=>e.timer.pending).length,safetyIntervalMs:this.safetyIntervalMs}}sync(){if(!this.outputs.size){for(const e of this.sockets.values())e.close();this.sockets.clear(),this.socketUrls.clear();for(const e of this.retries.values())e.timer.cancel();return void this.retries.clear()}const e=new Set;for(const t of this.state.listInstances()){const n=Nt(t);if(!n)continue;e.add(t.id);const r=this.retries.get(t.id);r?.timer.pending&&r.url!==n&&(r.timer.cancel(),this.retries.delete(t.id)),this.socketUrls.get(t.id)!==n&&this.sockets.has(t.id)&&(this.sockets.get(t.id)?.close(),this.sockets.delete(t.id)),this.sockets.has(t.id)||this.retries.get(t.id)?.timer.pending||this.connect(t.id,n)}for(const[t,n]of this.sockets)if(!e.has(t)){n.close(),this.sockets.delete(t),this.socketUrls.delete(t);const e=this.retries.get(t);e?.timer.cancel(),this.retries.delete(t)}for(const[t,n]of this.retries)e.has(t)||(n.timer.cancel(),this.retries.delete(t),this.socketUrls.delete(t))}connect(e,t){const n=this.token?{authorization:`Bearer ${this.token}`}:void 0,r=this.createSocket(t,{headers:n});this.sockets.set(e,r),this.socketUrls.set(e,t),r.on("open",()=>{const n=this.retries.get(e);n?.timer.reset(),this.retries.set(e,{timer:n?.timer||new wt,url:t}),r.readyState===d.OPEN&&(r.send(JSON.stringify({v:1,type:"subscribe",topics:[y,"app.sessions","instances"]})),this.logger?.info?.({instanceId:e,url:t},"ai-session.event.forward.connect"),this.logger?.info?.({instanceId:e,url:t},"app-session.event.forward.connect"))}),r.on("message",t=>{const n=function(e,t){try{const n=JSON.parse(String(e));if(!n||"object"!=typeof n||Array.isArray(n))return;const r=n,o="string"==typeof r.type?r.type:"";if(!o||"events.connected"===o)return;const i=r.scope&&"object"==typeof r.scope&&!Array.isArray(r.scope)?r.scope:{};return{v:1,id:"string"==typeof r.id?r.id:`evt_${Date.now().toString(36)}`,seq:Number(r.seq)||0,type:o,topic:"string"==typeof r.topic?r.topic:It(o),createdAt:"string"==typeof r.createdAt?r.createdAt:(new Date).toISOString(),payload:"payload"in r?r.payload:{},scope:{...i,instanceId:t}}}catch{return}}(t,e);if(!n)return;if("streams.hello"===n.type){const t=zt.safeParse(n.payload);if(!t.success)return this.logger?.warn?.({instanceId:e,issues:t.error.issues},"session-stream.handshake.invalid"),void r.close(1002,"Incompatible session stream handshake.");const o=JSON.stringify({type:"node-agent.streams.hello",instanceId:e,payload:t.data});for(const e of this.outputs)e.readyState===d.OPEN&&e.send(o);return}this.recordAiSessionEvent(e,n),this.recordAppSessionEvent(e,n);const o=JSON.stringify({type:"node-agent.event.forwarded",event:n});for(const e of this.outputs)e.readyState===d.OPEN&&e.send(o)}),r.on("close",()=>{this.sockets.get(e)===r&&(this.sockets.delete(e),this.socketUrls.delete(e),this.scheduleReconnect(e))}),r.on("error",()=>{this.logger?.warn?.({instanceId:e,url:t},"ai-session.event.forward.error"),this.logger?.warn?.({instanceId:e,url:t},"app-session.event.forward.error"),r.close()})}scheduleReconnect(e){const t=this.state.listInstances().find(t=>t.id===e),n=t&&Nt(t);if(!n||!this.outputs.size)return;const r=this.retries.get(e)??{timer:new wt};if(r.timer.pending)return;r.url=n;const o=r.timer.schedule(()=>{this.sockets.has(e)||this.connect(e,n)});o&&(this.reconnectAttempts+=1,this.logger?.info?.({instanceId:e,url:n,attempt:o.attempt,delay:o.delay,reconnectAttempts:this.reconnectAttempts},"session-stream.connection.reconnect-scheduled")),this.retries.set(e,r)}recordAiSessionEvent(e,t){if(t.topic!==y)return;const n=kt(t.payload),r=this.lastAiSessionEventByInstance.get(e);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:e,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"ai-session.event.forward.gap"),this.lastAiSessionEventByInstance.set(e,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:e,traceId:n.traceId,eventType:t.type,streamId:n.streamId,revision:n.revision},"ai-session.event.forward.message")}recordAppSessionEvent(e,t){if("app.sessions"!==t.topic)return;const n=kt(t.payload),r=this.lastAppSessionEventByInstance.get(e);r?.streamId===n.streamId&&void 0!==r.revision&&void 0!==n.revision&&n.revision>r.revision+1&&this.logger?.warn?.({instanceId:e,traceId:n.traceId,streamId:n.streamId,previousRevision:r.revision,revision:n.revision},"app-session.event.forward.gap"),this.lastAppSessionEventByInstance.set(e,{streamId:n.streamId??r?.streamId,revision:n.revision??r?.revision}),this.logger?.info?.({instanceId:e,traceId:n.traceId,eventType:t.type,streamId:n.streamId,revision:n.revision},"app-session.event.forward.message")}}function kt(e){const t=e&&"object"==typeof e&&!Array.isArray(e)?e:{},n=t.meta&&"object"==typeof t.meta&&!Array.isArray(t.meta)?t.meta:{};return{streamId:"string"==typeof n.streamId?n.streamId:void 0,revision:"number"==typeof n.revision?n.revision:void 0,traceId:"string"==typeof n.traceId?n.traceId:void 0}}function Nt(e){const t=e.target?.api||e.target?.web;if(t)try{const e=new URL("/api/events",t);return e.protocol="https:"===e.protocol?"wss:":"ws:",e.toString()}catch{return}}function Et(e){try{return t.statSync(e).isDirectory()}catch{return!1}}function _t(e,r){let o;try{o=t.statSync(e)}catch{return}if(!o.isDirectory())return;const i=n.resolve(e),s=n.basename(i)||i;let a=[];try{a=r>0?t.readdirSync(i,{withFileTypes:!0}):[]}catch{a=[]}const c=r>0?a.filter(e=>e.isDirectory()||e.isSymbolicLink()&&Et(n.join(i,e.name))).sort((e,t)=>e.name.localeCompare(t.name)).slice(0,80).flatMap(e=>{const t=_t(n.join(i,e.name),r-1);return t?[t]:[]}):[];return{name:s,path:i,children:c}}function vt(e){return(e.path?[e.path]:function(){if("win32"!==process.platform)return[n.parse(n.resolve(process.cwd())).root];const e=n.parse(n.resolve(process.cwd())).root,t=Array.from({length:26},(e,t)=>`${String.fromCharCode(65+t)}:\\`);return[...new Set([e,...t])].filter(Et)}()).flatMap(t=>{const n=_t(t,e.depth);return n?[n]:[]})}const Ot=l.z.object({path:l.z.string().trim().min(1).max(2048),method:l.z.string().trim().min(1).max(20).default("GET"),headers:l.z.record(l.z.string(),l.z.string()).default({}),body:l.z.string().optional(),bodyBase64:l.z.string().optional()}).strict(),xt=l.z.object({id:l.z.string().trim().min(1).max(120).optional(),name:l.z.string().trim().min(1).max(160),path:l.z.string().trim().min(1).max(4096),defaultImageId:l.z.string().trim().min(1).max(120).optional(),labels:l.z.record(l.z.string(),l.z.string()).default({})}).strict(),Tt=l.z.object({path:l.z.string().trim().min(1).max(4096).optional(),depth:l.z.coerce.number().int().min(0).max(1).default(0)}).strict(),Ct=l.z.object({id:l.z.string().trim().min(1).max(120).optional(),name:l.z.string().trim().min(1).max(160).optional(),runtimeId:l.z.string().trim().min(1).max(120),imageId:l.z.string().trim().min(1).max(120).optional(),projectId:l.z.string().trim().min(1).max(120).optional(),image:Me.optional(),source:Ae,sourceSnapshot:l.z.record(l.z.string(),l.z.unknown()).default({}),config:Ge.shape.config.optional(),modelSelection:Ge.shape.modelSelection}).strict(),Dt=l.z.object({name:l.z.string().trim().min(1).max(160).optional(),config:Ge.shape.config.optional(),modelSelection:Ge.shape.modelSelection.optional()}).strict(),jt=l.z.object({id:l.z.string().trim().min(1).max(120).optional(),name:l.z.string().trim().min(1).max(160),type:l.z.enum(["docker","kubernetes","local"]),status:l.z.enum(["unknown","online","offline","degraded"]).default("unknown"),accessStrategy:l.z.enum(["node-proxy","direct-port","kubernetes-ingress","kubernetes-port-forward"]).optional(),capabilities:l.z.record(l.z.string(),l.z.unknown()).default({}),labels:l.z.record(l.z.string(),l.z.string()).default({})}).strict(),Rt=jt.omit({id:!0}).partial().strict();function Pt(e=function(){return process.env.TASK_HANDOFF_NODE_AGENT_DATA_DIR||n.join(p.homedir(),".config","task-handoff","node-agent")}()){const t=n.resolve(e);return{dataDir:t,identityPath:n.join(t,"identity.json"),settingsPath:n.join(t,"runtime-settings.json"),localFoldersDir:n.join(t,"local-folders"),nodeRuntimesDir:n.join(t,"node-runtimes"),controlledInstancesDir:n.join(t,"controlled-instances"),nodeModelsDir:n.join(t,"models"),modelAssignmentsDir:n.join(t,"model-assignments"),modelEnvironmentsDir:n.join(t,"model-environments"),updatesDir:n.join(t,"updates"),logsDir:n.join(t,"logs")}}function Mt(e){t.mkdirSync(e,{recursive:!0})}function Ft(e,t){Mt(n.dirname(e)),m.sync(e,`${JSON.stringify(t,null,2)}\n`,{encoding:"utf8"})}function $t(e,t){console.warn(JSON.stringify({message:e,...t}))}function Lt(e){return e&&"object"==typeof e&&"issues"in e?{issues:e.issues}:{error:e instanceof Error?e.message:String(e)}}function Ut(e,n){const r=n.logger||$t;let o;try{o=JSON.parse(t.readFileSync(e,"utf8"))}catch(t){return void r("stored JSON could not be read",{filePath:e,...Lt(t)})}if(!n.schema)return o;const i=n.sanitize?n.sanitize(o):o,s=n.schema.safeParse(i);if(s.success)return s.data;const a="strip"in n.schema&&"function"==typeof n.schema.strip?n.schema.strip().safeParse(i):void 0;if(a?.success)return r("unknown stored fields were ignored",{filePath:e,issues:s.error.issues}),a.data;r("stored record failed validation and was isolated",{filePath:e,issues:s.error.issues})}function Ht(e){return`${e}_${u.randomUUID().replace(/-/g,"").slice(0,20)}`}function Kt(){return u.randomBytes(24).toString("base64url")}class qt{directory;options;constructor(e,t={}){this.directory=e,this.options=t}init(){Mt(this.directory)}filePath(e){return n.join(this.directory,`${e}.json`)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=Ut(n.join(this.directory,e),this.options);return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?Ut(n,this.options):void 0}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,n=this.options.schema?this.options.schema.parse(t):t;return Ft(this.filePath(n.id),n),n}patch(e,t){const n=this.get(e);if(!n)return;const r={...n,...t,id:n.id,createdAt:n.createdAt,updatedAt:(new Date).toISOString()};return this.put(r)}delete(e){const n=this.filePath(e);return!!t.existsSync(n)&&(t.unlinkSync(n),!0)}}class Bt{filePathValue;defaults;options;constructor(e,t,n={}){this.filePathValue=e,this.defaults=t,this.options=n}init(){Mt(n.dirname(this.filePathValue)),t.existsSync(this.filePathValue)||this.put(this.defaults())}get(){return this.init(),t.existsSync(this.filePathValue)?Ut(this.filePathValue,this.options)??this.defaults():this.defaults()}put(e){const t=this.options.sanitize?this.options.sanitize(e):e,n=this.options.schema?this.options.schema.parse(t):t;return Ft(this.filePathValue,n),n}}class Vt extends Error{code;lockPath;owner;constructor(e,t,n={}){const r=t?.pid?` pid ${t.pid}`:"";super(`${n.label||"Process"} is already running${r}. Lock: ${e}`),this.name="ProcessSingletonError",this.code=n.code||"PROCESS_ALREADY_RUNNING",this.lockPath=e,this.owner=t}}function Gt(e){return n.join(e,"owner.json")}function Jt(e){return n.join(e,"initializing.json")}function Wt(e){return n.join(e,"recovering.json")}function Yt(e){try{const n=JSON.parse(t.readFileSync(Gt(e),"utf8"));if(!Number.isInteger(n.pid)||Number(n.pid)<=0||"string"!=typeof n.hostname||!n.hostname||"string"!=typeof n.command||"string"!=typeof n.acquiredAt||!Number.isFinite(Date.parse(n.acquiredAt))||"string"!=typeof n.token||!n.token)return;return n}catch{return}}function Xt(e){if(!Number.isInteger(e)||Number(e)<=0)return!1;try{return process.kill(Number(e),0),!0}catch(e){return"EPERM"===e.code}}function Qt(e,r){const o=n.join(e,`.owner-${r.token}.tmp`);t.writeFileSync(o,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384,flag:"wx"});const i=Zt(e);if(i?.token!==r.token)throw en(o),new Error(`Process lock initialization was superseded: ${e}`);t.renameSync(o,Gt(e)),en(Jt(e))}function Zt(e){try{return JSON.parse(t.readFileSync(Jt(e),"utf8"))}catch{return}}function en(e){try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}function tn(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,20)}function nn(e){try{const n=Zt(e),r=Date.parse(n?.startedAt||"");return Number.isFinite(r)?Math.max(0,Date.now()-r):Math.max(0,Date.now()-t.statSync(e).mtimeMs)}catch{return 0}}function rn(e,r){try{return t.writeFileSync(Wt(e),`${JSON.stringify({token:r,pid:process.pid,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),!0}catch(o){if("EEXIST"===o.code)return!!function(e){const r=Wt(e),o=on(r),i=Date.parse(o?.startedAt||"");if(Xt(o?.pid)||Number.isFinite(i)&&Date.now()-i<2e3)return!1;const s=n.join(e,`.recovering-${u.randomUUID()}.stale`);try{t.renameSync(r,s)}catch(e){if(["ENOENT","EEXIST"].includes(e.code||""))return!1;throw e}const a=on(s);if(a?.token===o?.token)return en(s),!0;try{t.linkSync(s,r)}catch(e){if("EEXIST"!==e.code)throw e}finally{en(s)}return!1}(e)&&rn(e,r);if("ENOENT"===o.code)return!1;throw o}}function on(e){try{return JSON.parse(t.readFileSync(e,"utf8"))}catch{return}}function sn(e,t){return on(Wt(e))?.token===t}function an(e,t=process.platform){const r=n.resolve(e),o=u.createHash("sha256").update(r).digest("hex").slice(0,16);return"win32"===t?`\\\\.\\pipe\\task-handoff-node-agent-${o}`:n.join("/tmp",`task-handoff-node-agent-${process.getuid?.()??"user"}`,`${o}.sock`)}const cn="TASK_HANDOFF_NODE_AGENT_V1";function dn(e=""){return u.createHash("sha256").update(e).digest("hex")}function ln(e,t){return u.createHmac("sha256",e).update(function(e){return[cn,e.keyId,e.method.toUpperCase(),e.pathWithQuery,e.timestamp,e.nonce,e.bodySha256].join("\n")}(t)).digest("hex")}function un(e){const t=e.timestamp||(new Date).toISOString(),n=e.nonce||u.randomBytes(18).toString("base64url"),r=dn(e.body||""),o=ln(e.secret,{keyId:e.keyId,method:e.method,pathWithQuery:e.pathWithQuery,timestamp:t,nonce:n,bodySha256:r});return{"x-taskhandoff-node-id":e.nodeId,"x-taskhandoff-key-id":e.keyId,"x-taskhandoff-timestamp":t,"x-taskhandoff-nonce":n,"x-taskhandoff-body-sha256":r,"x-taskhandoff-signature":o}}const pn=new Set(["DELETE","GET","HEAD","PATCH","POST","PUT","OPTIONS"]);function mn(){return(new Date).toISOString()}function gn(e,t){if(!e)return!0;const n=g.valid(e),r=g.valid(t);return n&&r?g.gt(r,n):e!==t}async function hn(e,t,n){const r=In(n),o=await e(fn(),["view",`${t}@${r}`,"version","--json"]).catch(e=>{if(!function(e){if(!e||"object"!=typeof e)return!1;const t=e,n=[t.message,t.details?.stdout,t.details?.stderr].filter(e=>"string"==typeof e).join("\n");return/\bE404\b/i.test(n)}(e))throw e});if(!o)return;const i=JSON.parse(o.stdout);if("string"!=typeof i)throw new Error(`npm did not resolve ${t}@${r} to one version.`);return i}function fn(){return process.env.TASK_HANDOFF_NPM_COMMAND||"npm"}function yn(e){return me.parse({target:e.target,source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:e.currentVersion||"unavailable",updateAvailable:!1,reason:`No ${e.channel} release is currently published.`,checkedAt:mn()})}function In(e){return"stable"===e?"latest":e}function bn(e){if(Array.isArray(e))return e.map(bn).find(Boolean);if(!e||"object"!=typeof e)return;const t=e;return"string"==typeof t.digest&&t.digest.startsWith("sha256:")?t.digest:"string"==typeof t.Digest&&t.Digest.startsWith("sha256:")?t.Digest:Object.values(t).map(bn).find(Boolean)}class An{records;constructor(e){this.records=new qt(e.updatesDir,{schema:ge})}init(){this.records.init()}list(){return this.records.list().sort((e,t)=>t.createdAt.localeCompare(e.createdAt))}create(e,t){const n=mn();return this.records.put(ge.parse({id:Ht("update"),nodeId:e,target:t.target,source:t.source,channel:t.channel,fromVersion:t.currentVersion,toVersion:t.availableVersion,artifactRef:t.artifactRef,status:"queued",createdAt:n,updatedAt:n}))}patch(e,t){const n=this.records.get(e);if(!n)throw Object.assign(new Error(`Update job ${e} was not found.`),{statusCode:404,code:"UPDATE_JOB_NOT_FOUND"});return this.records.put(ge.parse({...n,...t,id:e,createdAt:n.createdAt,updatedAt:mn()}))}run(e,t){(async()=>{this.patch(e.id,{status:"updating",startedAt:mn(),error:void 0});try{await t(e),this.patch(e.id,{status:"succeeded",completedAt:mn()})}catch(t){this.patch(e.id,{status:"failed",error:t instanceof Error?t.message:String(t),completedAt:mn()})}})()}}const zn=l.z.record(l.z.string(),l.z.string()),wn=new Set(["OPENAI_API_KEY","OPENAI_BASE_URL","CODEX_MODEL","TASK_HANDOFF_CODEX_BASE_URL","TASK_HANDOFF_CODEX_MODEL","TASK_HANDOFF_CODEX_MODEL_ID","ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","CLAUDE_MODEL","TASK_HANDOFF_CLAUDE_MODEL","TASK_HANDOFF_CLAUDE_MODEL_ID"]);class Sn{directory;constructor(e){this.directory=e}get(e){try{return zn.parse(JSON.parse(t.readFileSync(this.filePath(e),"utf8")))}catch(t){if(t&&"object"==typeof t&&"code"in t&&"ENOENT"===t.code)return{};throw new Error(`Stored model environment for instance ${e} is invalid: ${t instanceof Error?t.message:String(t)}`)}}has(e){return t.existsSync(this.filePath(e))}listInstanceIds(){return t.existsSync(this.directory)?t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).map(e=>e.slice(0,-5)).sort():[]}put(e,n){const r=zn.parse(n);t.mkdirSync(this.directory,{recursive:!0,mode:448});const o=this.filePath(e);return m.sync(o,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(o,384),r}delete(e){t.rmSync(this.filePath(e),{force:!0})}filePath(e){return n.join(this.directory,`${e}.json`)}}class kn{directory;nodeId;constructor(e,t){this.directory=e,this.nodeId=t}init(){t.mkdirSync(this.directory,{recursive:!0,mode:448}),t.chmodSync(this.directory,448)}list(){return this.init(),t.readdirSync(this.directory).filter(e=>e.endsWith(".json")).sort().flatMap(e=>{const t=this.read(n.join(this.directory,e));return t?[t]:[]})}get(e){const n=this.filePath(e);return t.existsSync(n)?this.read(n):void 0}put(e){const n=Ne.parse(e);this.init();const r=this.filePath(n.id);return m.sync(r,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(r,384),n}delete(e){const n=this.filePath(e);return!!t.existsSync(n)&&(t.unlinkSync(n),!0)}read(e){try{const r=JSON.parse(t.readFileSync(e,"utf8")),o=n.basename(e,".json"),i=Ne.safeParse(r);if(i.success&&i.data.id===o&&Pe(i.data)===i.data.id)return i.data;const s=Ne.strip().safeParse(r);return s.success&&s.data.id===o&&Pe(s.data)===s.data.id?(console.warn(JSON.stringify({message:"unknown stored node model fields were ignored",nodeId:this.nodeId,filePath:e})),s.data):void console.warn(JSON.stringify({message:"stored node model failed validation and was isolated",nodeId:this.nodeId,filePath:e}))}catch(t){return void console.warn(JSON.stringify({message:"stored node model could not be read",nodeId:this.nodeId,filePath:e,error:t instanceof Error?t.message:String(t)}))}}filePath(e){return n.join(this.directory,`${e}.json`)}}class Nn{directory;constructor(e){this.directory=e}init(){t.mkdirSync(this.directory,{recursive:!0,mode:448}),t.chmodSync(this.directory,448)}get(e){const n=this.filePath(e);if(t.existsSync(n))try{const r=JSON.parse(t.readFileSync(n,"utf8")),o=ve.safeParse(r);if(o.success)return o.data;const i=ve.strip().safeParse(r);if(i.success)return console.warn(JSON.stringify({message:"unknown stored model assignment fields were ignored",instanceId:e,filePath:n})),i.data;throw o.error}catch(t){throw Object.assign(new Error(`Stored model assignment for instance ${e} is invalid.`),{statusCode:409,code:"NODE_MODEL_ASSIGNMENT_INVALID",cause:t})}}put(e){const n=ve.parse(e);this.init();const r=this.filePath(n.instanceId);return m.sync(r,`${JSON.stringify(n,null,2)}\n`,{encoding:"utf8",mode:384}),t.chmodSync(r,384),n}delete(e){t.rmSync(this.filePath(e),{force:!0})}filePath(e){return n.join(this.directory,`${e}.json`)}}class En{usedNonces=new Map;identity;nodeId;overrideSecret;overrideKeyId;nowMs;constructor(e,t,n,r,o=Date.now){this.identity=e,this.nodeId=t,this.overrideSecret=n,this.overrideKeyId=r,this.nowMs=o}hasRemoteSecrets(){return this.identity.remoteSecrets(this.overrideSecret,this.overrideKeyId).length>0}verify(e){const t=function(e){const t=new Map;for(const[n,r]of Object.entries(e)){const e=Array.isArray(r)?r.join(","):"string"==typeof r?r:void 0===r?"":String(r);t.set(n.toLowerCase(),e)}return{nodeId:t.get("x-taskhandoff-node-id")||"",keyId:t.get("x-taskhandoff-key-id")||"",timestamp:t.get("x-taskhandoff-timestamp")||"",nonce:t.get("x-taskhandoff-nonce")||"",bodySha256:t.get("x-taskhandoff-body-sha256")||"",signature:t.get("x-taskhandoff-signature")||""}}(e.headers);if(!t.nodeId&&!t.signature)return!1;const n=this.identity.remoteSecrets(this.overrideSecret,this.overrideKeyId);if(!n.length)return!1;if(t.nodeId!==this.nodeId)throw Object.assign(new Error("Invalid node agent HMAC node id."),{statusCode:401,code:"NODE_AGENT_HMAC_NODE_MISMATCH"});if(!t.keyId)throw Object.assign(new Error("Node agent HMAC key id is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_MISSING"});const r=n.filter(e=>t.keyId===e.keyId);if(!r.length)throw Object.assign(new Error("Invalid node agent HMAC key id."),{statusCode:401,code:"NODE_AGENT_HMAC_KEY_INVALID"});const o=Date.parse(t.timestamp);if(!Number.isFinite(o)||Math.abs(this.nowMs()-o)>6e4)throw Object.assign(new Error("Node agent HMAC timestamp is outside the allowed window."),{statusCode:401,code:"NODE_AGENT_HMAC_TIMESTAMP_INVALID"});if(!t.nonce)throw Object.assign(new Error("Node agent HMAC nonce is missing."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_MISSING"});this.pruneNonces();const i=`${t.nodeId}:${t.keyId}:${t.nonce}`;if(this.usedNonces.has(i))throw Object.assign(new Error("Node agent HMAC nonce was already used."),{statusCode:401,code:"NODE_AGENT_HMAC_NONCE_REPLAY"});const s=dn(null==(a=e.body)?"":"string"==typeof a||Buffer.isBuffer(a)?a:JSON.stringify(a));var a;if(t.bodySha256!==s)throw Object.assign(new Error("Node agent HMAC body hash mismatch."),{statusCode:401,code:"NODE_AGENT_HMAC_BODY_HASH_INVALID"});if(!r.some(n=>function(e,t){const n=Buffer.from(e,"hex"),r=Buffer.from(t,"hex");return n.length===r.length&&u.timingSafeEqual(n,r)}(t.signature,ln(n.secret,{keyId:t.keyId,method:e.method,pathWithQuery:e.url,timestamp:t.timestamp,nonce:t.nonce,bodySha256:s}))))throw Object.assign(new Error("Invalid node agent HMAC signature."),{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_INVALID"});return this.usedNonces.set(i,o+6e4+1),t.keyId}pruneNonces(){const e=this.nowMs();for(const[t,n]of this.usedNonces)n<=e&&this.usedNonces.delete(t)}}const _n=l.z.string().trim().min(1).max(160),vn=l.z.string().datetime(),On=l.z.string().min(1).max(4096),xn=l.z.string().trim().url().max(2048),Tn=l.z.object({tokenHash:l.z.string().trim().min(1).max(256),expiresAt:vn,createdAt:vn,controlPlaneName:l.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:xn.optional()}).strip(),Cn=l.z.object({id:_n,keyId:_n,name:l.z.string().trim().min(1).max(160).optional(),url:xn.optional(),secret:On,pairedAt:vn,updatedAt:vn,active:l.z.boolean().default(!0)}).strip(),Dn=l.z.object({controlPlaneName:l.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:l.z.string().trim().max(2048).optional(),expiresInMs:l.z.number().int().positive().max(36e5).optional()}).strict(),jn=l.z.object({joinToken:l.z.string().trim().min(1).max(4096),controlPlaneId:l.z.string().trim().min(1).max(160).optional(),controlPlaneName:l.z.string().trim().min(1).max(160).optional(),controlPlaneUrl:l.z.string().trim().max(2048).optional()}).strict(),Rn=l.z.object({controlPlaneUrl:l.z.string().trim().url().max(2048),joinToken:l.z.string().trim().min(1).max(4096),controlPlaneName:l.z.string().trim().min(1).max(160).optional(),activate:l.z.boolean().optional()}).strict();function Pn(){return(new Date).toISOString()}const Mn=new Set(["nodeId","createdAt","updatedAt","pairingInvites","remoteControlPlanes"]),Fn=new Set(["tokenHash","expiresAt","createdAt","controlPlaneName","controlPlaneUrl"]),$n=new Set(["id","keyId","name","url","secret","pairedAt","updatedAt","active"]);function Ln(e,t){console.warn(JSON.stringify({message:e,...t}))}function Un(e,t){return Object.assign(new Error(e,void 0===t?void 0:{cause:t}),{code:"NODE_AGENT_IDENTITY_INVALID"})}function Hn(e,t){return Object.keys(e).filter(e=>!t.has(e))}function Kn(e,t,n){const r=vn.safeParse(e);return r.success?r.data:(n("invalid node agent identity timestamp was replaced",{field:t}),Pn())}function qn(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw Un("Node agent identity must be a JSON object.");const n=e,r=_n.safeParse(n.nodeId);if(!r.success)throw Un("Node agent identity has an invalid nodeId.",r.error);const o=r.data,i=Hn(n,Mn);return i.length&&t("unknown node agent identity fields were ignored",{fields:i}),{nodeId:o,createdAt:Kn(n.createdAt,"createdAt",t),updatedAt:Kn(n.updatedAt,"updatedAt",t),pairingInvites:Array.isArray(n.pairingInvites)?n.pairingInvites.flatMap(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return t("invalid node agent pairing invite was ignored",{}),[];const n=Hn(e,Fn);n.length&&t("unknown node agent pairing invite fields were ignored",{fields:n});const r=Tn.safeParse(e);return r.success?[r.data]:(t("invalid node agent pairing invite was ignored",{issues:r.error.issues}),[])}):[],remoteControlPlanes:Array.isArray(n.remoteControlPlanes)?n.remoteControlPlanes.flatMap(e=>{if(!e||"object"!=typeof e||Array.isArray(e))return t("invalid node agent remote control-plane was ignored",{}),[];const n=Hn(e,$n);n.length&&t("unknown node agent remote control-plane fields were ignored",{fields:n});const r=Cn.safeParse(e);return r.success?[r.data]:(t("invalid node agent remote control-plane was ignored",{issues:r.error.issues}),[])}):[]}}class Bn{paths;logger;constructor(e,t={}){this.paths=e,this.logger=t.logger||Ln}read(){let e,n;try{e=t.readFileSync(this.paths.identityPath,"utf8")}catch(e){if("ENOENT"===e.code)return;throw Object.assign(new Error(`Node agent identity could not be read: ${this.paths.identityPath}`,{cause:e}),{code:"NODE_AGENT_IDENTITY_READ_FAILED"})}try{n=JSON.parse(e)}catch(e){throw Un(`Node agent identity contains invalid JSON: ${this.paths.identityPath}`,e)}return qn(n,this.logger)}write(e){t.mkdirSync(n.dirname(this.paths.identityPath),{recursive:!0});const r=qn({...e,updatedAt:Pn()},this.logger);m.sync(this.paths.identityPath,`${JSON.stringify(r,null,2)}\n`,{encoding:"utf8",mode:384})}}function Vn(){return(new Date).toISOString()}class Gn{store;constructor(e){this.store=new Bn(e)}resolveNodeId(e){if(e?.trim())return this.writeNodeId(e.trim()),e.trim();const t=this.store.read();if(t?.nodeId)return t.nodeId;const n=Ht("node");return this.writeNodeId(n),n}createPairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=Vn(),r=new Date(Date.now()+(e.expiresInMs||6e5)).toISOString(),o=Kt(),i={tokenHash:dn(o),createdAt:n,expiresAt:r,...e.controlPlaneName?{controlPlaneName:e.controlPlaneName}:{},...e.controlPlaneUrl?{controlPlaneUrl:e.controlPlaneUrl}:{}};return this.store.write({...t,pairingInvites:[...t.pairingInvites||[],i]}),{...i,token:o}}completePairingInvite(e){const t=this.prunePairingInvites(this.store.read()||this.newIdentity()),n=dn(e.joinToken),r=(t.pairingInvites||[]).find(e=>e.tokenHash===n);if(!r){const e=new Error("Node agent pairing invite is invalid or expired.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_PAIRING_INVITE_INVALID"}),e}const o=Vn(),i={id:e.controlPlaneId||Ht("cp"),keyId:Ht("key"),...e.controlPlaneName||r.controlPlaneName?{name:e.controlPlaneName||r.controlPlaneName}:{},...e.controlPlaneUrl||r.controlPlaneUrl?{url:e.controlPlaneUrl||r.controlPlaneUrl}:{},secret:Kt(),pairedAt:o,updatedAt:o,active:!0};return this.store.write({...t,pairingInvites:(t.pairingInvites||[]).filter(e=>e.tokenHash!==n),remoteControlPlanes:[...(t.remoteControlPlanes||[]).filter(e=>e.id!==i.id),i]}),i}upsertRemoteControlPlane(e){const t=this.store.read()||this.newIdentity(),n=e.url?.replace(/\/$/,"");return this.store.write({...t,remoteControlPlanes:[...(t.remoteControlPlanes||[]).filter(t=>t.id!==e.id&&(!n||t.url?.replace(/\/$/,"")!==n)),e]}),e}createRemoteControlPlane(e){const t=Vn();return{id:Ht("cp"),keyId:Ht("key"),...e.name?{name:e.name}:{},url:e.url,secret:Kt(),pairedAt:t,updatedAt:t,active:!1!==e.active}}configuredRemoteControlPlanes(){return this.store.read()?.remoteControlPlanes||[]}listRemoteControlPlanes(e=""){return(this.store.read()?.remoteControlPlanes||[]).map(({secret:t,...n})=>({...n,current:Boolean(e&&n.keyId===e)})).sort((e,t)=>Number(t.current)-Number(e.current)||t.updatedAt.localeCompare(e.updatedAt))}deleteRemoteControlPlane(e,t=""){if(t&&e===t){const e=new Error("Cannot delete the key currently used by this control-plane connection.");throw Object.assign(e,{statusCode:409,code:"NODE_AGENT_REMOTE_KEY_IN_USE"}),e}const n=this.store.read();if(!n)return!1;const r=(n.remoteControlPlanes||[]).filter(t=>t.keyId!==e);return r.length!==(n.remoteControlPlanes||[]).length&&(this.store.write({...n,remoteControlPlanes:r}),!0)}remoteSecrets(e,t){return[...(this.store.read()?.remoteControlPlanes||[]).map(e=>({keyId:e.keyId,secret:e.secret})),...e&&t?[{keyId:t,secret:e}]:[]]}reverseTunnelSecret(e,t,n){if(t&&n)return{keyId:n,secret:t};const r=this.store.read()?.remoteControlPlanes||[];if(!r.length)return;if(e){const t=e.replace(/\/$/,""),n=r.find(e=>e.url?.replace(/\/$/,"")===t);if(n)return{keyId:n.keyId,secret:n.secret}}const o=[...r].sort((e,t)=>t.updatedAt.localeCompare(e.updatedAt))[0];return o?{keyId:o.keyId,secret:o.secret}:void 0}newIdentity(){return{nodeId:this.resolveNodeId(),createdAt:Vn(),updatedAt:Vn(),pairingInvites:[],remoteControlPlanes:[]}}writeNodeId(e){const t=this.store.read();this.store.write({...t||{createdAt:Vn()},nodeId:e,updatedAt:Vn(),pairingInvites:t?.pairingInvites||[],remoteControlPlanes:t?.remoteControlPlanes||[]})}prunePairingInvites(e){const t=Date.now();return{...e,pairingInvites:(e.pairingInvites||[]).filter(e=>Date.parse(e.expiresAt)>t)}}}const Jn=new Set(["content-encoding","content-length","transfer-encoding"]),Wn=["codex","claude"],Yn=5e3,Xn=l.z.object({version:l.z.literal(1),externalListener:Le}).strict(),Qn=l.z.object({}).strict().default({}),Zn=he.strict();function er(e){const t=process.env[e]?.trim();return t||void 0}function tr(){try{const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)):__dirname,r=[n.resolve(e,"..","package.json"),n.resolve(e,"..","..","package.json")].find(e=>t.existsSync(e));return r&&JSON.parse(t.readFileSync(r,"utf8")).version||"unknown"}catch{return"unknown"}}function nr(e){return e.bodyBase64?Buffer.from(e.bodyBase64,"base64"):e.body}function rr(e){const t=e.authorization;return"string"==typeof t&&t.startsWith("Bearer ")?t.slice(7).trim():void 0}function or(e){if("string"!=typeof e)return!1;const t=e.trim().toLowerCase();return"localhost"===t||"127.0.0.1"===t||"::1"===t||"::ffff:127.0.0.1"===t}function ir(e){return!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}function sr(){return(new Date).toISOString()}function ar(e){return"all-ipv4"===e?"0.0.0.0":"127.0.0.1"}function cr(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;const t=e,n=t.externalListener&&"object"==typeof t.externalListener&&!Array.isArray(t.externalListener)?t.externalListener:{},r=Object.keys(t).filter(e=>"version"!==e&&"externalListener"!==e),o=Object.keys(n).filter(e=>"bindScope"!==e&&"port"!==e);return(r.length||o.length)&&console.warn(JSON.stringify({message:"unknown stored node agent runtime setting fields were ignored",filePath:"runtime-settings.json",fields:[...r,...o.map(e=>`externalListener.${e}`)]})),{version:t.version,externalListener:{bindScope:n.bindScope,port:n.port}}}function dr(e){return"local-folder"===e.type?ze.parse({mode:"local-bind",path:"/workspace",readOnly:!1}):ze.parse({mode:"git-clone",path:"/workspace",readOnly:!1})}function lr(e){const t=Ae.parse(e.source),n=e.projectId||("local-folder"===t.type?t.localFolderId:void 0)||("git-repository"===t.type?t.repositoryId:void 0)||("git-template"===t.type?t.templateId:void 0)||`project_${e.id}`;return Se.parse({id:n,name:"string"==typeof e.sourceSnapshot.name?e.sourceSnapshot.name:e.name,source:t,defaultImageId:e.imageId,defaultNodeId:e.nodeId,defaultRuntimeId:e.runtimeId,workspacePolicy:dr(t),labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt})}async function ur(e,t){if(!t.target.web)return"unknown";const n=new AbortController,r=setTimeout(()=>n.abort(),2e3);try{return(await e(`${hr(t)}/api/health`,{signal:n.signal})).ok?"reachable":"endpoint-unreachable"}catch{return"endpoint-unreachable"}finally{clearTimeout(r)}}async function pr(e,t,n,r){const o=new AbortController,i=setTimeout(()=>o.abort(),r);try{return await e(t,{...n,signal:o.signal})}finally{clearTimeout(i)}}async function mr(e,t,n,r){if(!t.config.autoImportAgentConfigs)return void r.diagnostic({instanceId:t.id,action:n},"node instance config auto-import skipped");if("reachable"!==t.targetStatus)return;const o=hr(t),i=Number(process.env.TASK_HANDOFF_CONFIG_AUTO_IMPORT_TIMEOUT_MS||Yn);for(const s of Wn)try{const a=await pr(e,`${o}/api/config-sync/import/${encodeURIComponent(s)}`,{method:"POST"},Number.isFinite(i)&&i>0?i:Yn);if(!a.ok){r.warn({instanceId:t.id,action:n,preset:s,statusCode:a.status},"node instance config auto-import failed");continue}r.diagnostic({instanceId:t.id,action:n,preset:s},"node instance config auto-import completed")}catch(e){r.warn({instanceId:t.id,action:n,preset:s,error:e instanceof Error?e.message:String(e)},"node instance config auto-import failed")}}async function gr(e,t,n){const r=t.requireInstance(n);if("reachable"!==r.targetStatus||!r.target.web)return!1;const o=await pr(e,`${hr(r)}/api/internal/model-environment`,{method:"PUT",headers:{"content-type":"application/json",authorization:`Bearer ${r.registrationToken}`},body:JSON.stringify(t.resolvedAssignedModelEnvironment(n))},Yn);if(!o.ok)throw Object.assign(new Error(`Instance ${n} rejected its managed model environment with HTTP ${o.status}.`),{statusCode:502,code:"INSTANCE_MODEL_ENVIRONMENT_APPLY_FAILED"});return!0}function hr(e){const t=e.target.web;if(!t){const t=new Error(`Instance ${e.id} does not have a web endpoint.`);throw Object.assign(t,{statusCode:409,code:"NODE_INSTANCE_WEB_ENDPOINT_MISSING"}),t}if("direct-port"!==e.target.strategy)return t.replace(/\/$/,"");try{const e=new URL(t);return e.port?(e.hostname=process.env.TASK_HANDOFF_NODE_AGENT_PROXY_HOST||"127.0.0.1",e.toString().replace(/\/$/,"")):t.replace(/\/$/,"")}catch{return t.replace(/\/$/,"")}}function fr(e,t){const n=new Error(t);throw Object.assign(n,{statusCode:403,code:e}),n}function yr(e,t){if(e===Z)return;const n=new Error(`${t} protocol version ${e||"missing"} is not compatible with ${Z}.`);throw Object.assign(n,{statusCode:409,code:"PROTOCOL_VERSION_MISMATCH"}),n}function Ir(e={}){return{requiresImage:!1,supportsControlledInstanceApi:!0,supportsContainerLifecycle:!1,supportsAppSessions:!0,supportsHostSessions:!0,artifactKind:"none",isolation:"none",...e}}function br(){const e=process.env.TASK_HANDOFF_LOCAL_CONTROLLED_COMMAND?.trim();if(e)return e.split(/\s+/);const r=n.resolve(process.cwd(),"bin","task-handoff.js");return t.existsSync(r)?[process.execPath,r,"web"]:["task-handoff-controlled-instance","web"]}function Ar(e){return new Promise(t=>{const n=o.createServer();n.once("error",()=>t(!1)),n.listen({host:"127.0.0.1",port:e},()=>{n.close(()=>t(!0))})})}function zr(e,t=3e3){return null!==e.exitCode||null!==e.signalCode?Promise.resolve():new Promise(n=>{const r=setTimeout(()=>{null===e.exitCode&&null===e.signalCode&&e.kill("SIGKILL"),n()},t);r.unref(),e.once("exit",()=>{clearTimeout(r),n()})})}async function wr(e,t){const n=t.get(e.id);if(n&&!n.killed)return n.kill("SIGTERM"),t.delete(e.id),void await zr(n);if(e.runtime.pid){try{process.kill(e.runtime.pid,"SIGTERM")}catch{return}const t=Date.now()+3e3;for(;Date.now()<t;){try{process.kill(e.runtime.pid,0)}catch{return}await new Promise(e=>setTimeout(e,50))}try{process.kill(e.runtime.pid,"SIGKILL")}catch{}}}const Sr=new Set(["provisioning","starting","registering","registered","running"]);async function kr(e,t){try{const n=await e(t,["--version"]);return{available:!0,command:t,version:(n.stdout||n.stderr).split(/\r?\n/)[0]?.trim()||void 0}}catch{return{available:!1}}}class Nr{executor;constructor(e){this.executor=e}start(e){return this.executor.start(e)}stop(e){return this.executor.stop(e)}restart(e){return this.executor.restart(e)}delete(e){return this.executor.delete(e)}}class Er{runCommand;paths;nodeAgentUrl;processByInstanceId=new Map;constructor(e,t,n){this.runCommand=e,this.paths=t,this.nodeAgentUrl=n}async start(e){const r=function(e){if("local-folder"!==e.source.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}return n.resolve(e.source.path)}(e.instance),o=e.instance.runtime.port||await async function(){const e=Number(process.env.TASK_HANDOFF_LOCAL_INSTANCE_PORT_START||19e3),t=Number.isInteger(e)&&e>0?e:19e3;for(let e=t;e<t+1e3&&e<=65535;e+=1)if(await Ar(e))return e;const n=new Error(`No free localhost port found in range ${t}-${Math.min(t+999,65535)}.`);throw Object.assign(n,{statusCode:503,code:"LOCAL_INSTANCE_PORT_UNAVAILABLE"}),n}(),i=n.join(this.paths.dataDir,"local-instances",e.instance.id),a=n.join(i,"logs");t.mkdirSync(a,{recursive:!0});const[c,...d]=br(),l=[...d,"--host","127.0.0.1","--port",String(o)],u=t.openSync(n.join(a,"controlled-instance.out.log"),"a"),p=t.openSync(n.join(a,"controlled-instance.err.log"),"a"),m=s.spawn(c,l,{cwd:r,detached:!1,stdio:["ignore",u,p],env:{...process.env,TASK_HANDOFF_CONTROL_MODE:"controlled",TASK_HANDOFF_NODE_AGENT_URL:this.nodeAgentUrl(),TASK_HANDOFF_INSTANCE_ID:e.instance.id,TASK_HANDOFF_INSTANCE_NAME:e.instance.name,TASK_HANDOFF_REGISTRATION_TOKEN:e.instance.registrationToken||"",TASK_HANDOFF_PROJECT_ID:e.project.id,TASK_HANDOFF_NODE_ID:e.node.id,TASK_HANDOFF_RUNTIME_ID:e.runtime.id,TASK_HANDOFF_WORKSPACE:r,TASK_HANDOFF_WORKSPACE_MODE:"local-bind",TASK_HANDOFF_DATA_DIR:i,TASK_HANDOFF_LOG_DIR:a,TASK_HANDOFF_APP_SESSION_PERSIST:"1",TASK_HANDOFF_CODEX_APP_SERVER:process.env.TASK_HANDOFF_CODEX_APP_SERVER||"1",TASK_HANDOFF_WEB_PORT:String(o),TASK_HANDOFF_WEB_HOST:"127.0.0.1",...e.modelEnv||{}}});t.closeSync(u),t.closeSync(p);try{await function(e){return new Promise((t,n)=>{const r=()=>{e.off("error",o),t()},o=t=>{e.off("spawn",r),n(t)};e.once("spawn",r),e.once("error",o)})}(m)}catch(e){const o=e instanceof Error?e.message:String(e);try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] spawn failed command=${c} cwd=${r} error=${o}\n`)}catch{}throw Object.assign(new Error(`Controlled instance process could not start with command ${c} in ${r}: ${o}`),{statusCode:500,code:"LOCAL_INSTANCE_PROCESS_SPAWN_FAILED"})}this.processByInstanceId.set(e.instance.id,m),m.on("error",e=>{try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] process error pid=${m.pid??""} error=${e.message}\n`)}catch{}}),m.once("exit",(r,o)=>{this.processByInstanceId.delete(e.instance.id);try{t.appendFileSync(n.join(a,"controlled-instance.lifecycle.log"),`[${(new Date).toISOString()}] exited pid=${m.pid??""} code=${r??""} signal=${o??""}\n`)}catch{}});const g=`http://127.0.0.1:${o}`;return await async function(e,t=3e3){const n=Date.now()+t;for(;Date.now()<n;){try{if((await fetch(`${e}/api/health`)).ok)return!0}catch{}await new Promise(e=>setTimeout(e,100))}return!1}(g),{status:"registering",health:"unknown",connectionStatus:"online",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",target:{strategy:"direct-port",status:"unknown",web:g,api:`${g}/api`},workspace:{mode:"local-bind",status:"pending",path:r},runtime:{kind:"local",workspacePath:r,pid:m.pid,port:o,labels:{...e.instance.runtime.labels,"task-handoff.runtime-kind":"local"}}}}async stop(e){return await wr(e.instance,this.processByInstanceId),{status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.instance.target,status:"unknown"},runtime:e.instance.runtime}}async restart(e){return await wr(e.instance,this.processByInstanceId),this.start(e)}async delete(e){return this.stop(e)}async check(e){const[t,n]=await Promise.all([kr(this.runCommand,"codex"),kr(this.runCommand,"claude")]);return{status:"online",capabilities:Ir({...e.capabilities,apps:{terminal:!0,codex:t,claude:n}})}}async stopAll(){const e=Array.from(this.processByInstanceId.values());for(const e of this.processByInstanceId.values())e.killed||e.kill("SIGTERM");this.processByInstanceId.clear(),await Promise.all(e.map(e=>zr(e)))}}class _r{docker;local;constructor(e,t){this.docker=e,this.local=t}forRuntime(e){if("docker"===e.type)return this.docker;if("local"===e.type)return this.local;const t=new Error(`Runtime type ${e.type} is not supported by this node agent.`);throw Object.assign(t,{statusCode:400,code:"NODE_RUNTIME_TYPE_UNSUPPORTED"}),t}async stopAll(){this.local instanceof Er&&await this.local.stopAll()}}class vr{nodeId;paths;localFolders;nodeRuntimes;controlledInstances;models;modelAssignments;modelEnvironments;updateJobs;node;listenerPort;containerUrlOverride;constructor(e,t,n,r,o){this.paths=e,this.nodeId=t,this.localFolders=new qt(e.localFoldersDir,{schema:be}),this.nodeRuntimes=new qt(e.nodeRuntimesDir,{schema:$e}),this.controlledInstances=new qt(e.controlledInstancesDir,{schema:Ge,sanitize:e=>We(e,e=>{console.warn(JSON.stringify({message:"legacy controlled instance field was ignored",...e}))})}),this.models=new kn(e.nodeModelsDir,t),this.modelAssignments=new Nn(e.modelAssignmentsDir),this.modelEnvironments=new Sn(e.modelEnvironmentsDir),this.updateJobs=new An(e),this.listenerPort=o,this.containerUrlOverride=r;const i=sr();this.node=Fe.parse({id:t,name:t,connectionMode:"direct-http",endpoint:n,controlEndpoint:n,containerEndpoint:this.containerUrl,publicWebBase:n?n.replace(/:\d+$/,""):void 0,status:"online",health:"ok",capabilities:{},labels:{},createdAt:i,updatedAt:i})}init(){if(this.localFolders.init(),this.nodeRuntimes.init(),this.controlledInstances.init(),this.models.init(),this.modelAssignments.init(),this.migrateLegacyModelEnvironments(),this.updateJobs.init(),!this.nodeRuntimes.get("runtime_local_docker")){const e=sr();this.nodeRuntimes.put($e.parse({id:"runtime_local_docker",nodeId:this.nodeId,name:"Local Docker",type:"docker",status:"unknown",accessStrategy:"direct-port",capabilities:{},labels:{},createdAt:e,updatedAt:e}))}for(const e of this.nodeRuntimes.list())e.nodeId!==this.nodeId&&this.nodeRuntimes.put($e.parse({...e,nodeId:this.nodeId,updatedAt:sr()}));for(const e of this.localFolders.list())e.nodeId!==this.nodeId&&this.localFolders.put(be.parse({...e,nodeId:this.nodeId,updatedAt:sr()}))}get localNodeAgentUrl(){return`http://127.0.0.1:${this.listenerPort}`}get currentListenerPort(){return this.listenerPort}get containerUrl(){return this.containerUrlOverride||`http://host.docker.internal:${this.listenerPort}`}setListenerPort(e){this.listenerPort=e,this.node.containerEndpoint=this.containerUrl,this.node.updatedAt=sr()}runningInstanceCount(){const e=new Set(["created","stopped","failed"]);return this.listInstances().filter(t=>!e.has(t.status)).length}createLocalFolder(e){const t=sr(),n=be.parse({...e,id:e.id||Ht("folder"),nodeId:this.nodeId,labels:e.labels||{},createdAt:t,updatedAt:t});return this.localFolders.put(n)}createRuntime(e){const t=sr(),n=$e.parse({...e,id:e.id||Ht("runtime"),nodeId:this.nodeId,accessStrategy:e.accessStrategy||(r=e.type,"docker"===r?"direct-port":"node-proxy"),capabilities:"local"===e.type?Ir(e.capabilities):e.capabilities||{},labels:e.labels||{},createdAt:t,updatedAt:t});var r;return this.nodeRuntimes.put(n)}updateRuntime(e,t){const n=this.requireRuntime(e),r=$e.parse({...n,...t,id:n.id,nodeId:this.nodeId,accessStrategy:t.accessStrategy||n.accessStrategy,capabilities:"local"===(t.type||n.type)?Ir(t.capabilities||n.capabilities):t.capabilities||n.capabilities,createdAt:n.createdAt,updatedAt:sr()});return this.nodeRuntimes.put(r)}deleteRuntime(e){this.requireRuntime(e);const t=this.listInstances().filter(t=>t.runtimeId===e);if(t.length){const n=new Error(`Runtime ${e} is used by ${t.length} instance${1===t.length?"":"s"}.`);throw Object.assign(n,{statusCode:409,code:"NODE_RUNTIME_IN_USE"}),n}return this.nodeRuntimes.delete(e)}checkRuntime(e,t){const n=this.requireRuntime(e);return t.check?t.check(n).then(e=>{const t=$e.parse({...n,...e,id:n.id,nodeId:this.nodeId,createdAt:n.createdAt,updatedAt:sr()});return this.nodeRuntimes.put(t)}):this.nodeRuntimes.put($e.parse({...n,status:"unknown",updatedAt:sr()}))}requireRuntime(e){const t=this.nodeRuntimes.get(e);if(!t){const t=new Error(`Node runtime ${e} was not found.`);throw Object.assign(t,{statusCode:404,code:"NODE_RUNTIME_NOT_FOUND"}),t}return t}requireInstance(e){const t=this.controlledInstances.get(e);if(!t){const t=new Error(`Instance ${e} was not found on node ${this.nodeId}.`);throw Object.assign(t,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),t}const n=Je(t);if(!n.success)throw function(e,t){const n=t.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; "),r=new Error(`Stored instance ${e} is not compatible with protocol ${Z}: ${n}`);return Object.assign(r,{statusCode:409,code:"NODE_INSTANCE_PAYLOAD_INVALID"}),r}(e,n.error.issues);return n.data}listInstances(){return this.controlledInstances.list().flatMap(e=>{const t=Je(e);return t.success?[t.data]:[]})}listModels(){const e=new Map;for(const t of this.listInstances()){const n=this.modelAssignments.get(t.id);for(const t of[n?.codexModelHash,n?.claudeModelHash])t&&e.set(t,(e.get(t)||0)+1)}return this.models.list().map(t=>this.publicModel(t,e.get(t.id)||0))}createModel(e){const t=sr(),n=Pe(e),r=this.models.get(n),o=Ne.parse({...e,id:n,enabled:e.enabled??!0,order:e.order??this.nextModelOrder(),labels:e.labels||{},createdAt:r?.createdAt||t,updatedAt:t});return this.publicModel(this.models.put(o),this.modelReferenceIds(n).length)}deployModel(e){const t=Pe(e);if(e.id!==t)throw Object.assign(new Error(`Model content hash ${t} does not match ${e.id}.`),{statusCode:400,code:"NODE_MODEL_HASH_MISMATCH"});const n=this.models.get(e.id)||this.models.put(Ne.parse(e));return this.publicModel(n,this.modelReferenceIds(n.id).length)}updateModel(e,t){const n=this.requireModel(e),r=Ne.parse({...n,...t,key:t.key?.trim()?t.key:n.key,createdAt:n.createdAt,updatedAt:sr()}),o=Pe(r),i=this.models.put(Ne.parse({...r,id:o}));return this.publicModel(i,this.modelReferenceIds(o).length)}deleteModel(e){this.requireModel(e);const t=this.modelReferenceIds(e);if(t.length)throw Object.assign(new Error(`Model ${e} is assigned to ${t.length} instance${1===t.length?"":"s"}.`),{statusCode:409,code:"NODE_MODEL_IN_USE",instanceIds:t});return this.models.delete(e)}assignModels(e,t){const n=this.requireInstance(e);if(this.validateAssignmentRef("codex",t.codexModelHash),this.validateAssignmentRef("claude",t.claudeModelHash),t.modelSelection.codexModelHash&&t.modelSelection.codexModelHash!==t.codexModelHash)throw Object.assign(new Error("Codex model selection does not match its node assignment."),{statusCode:400,code:"NODE_MODEL_SELECTION_MISMATCH"});if(t.modelSelection.claudeModelHash&&t.modelSelection.claudeModelHash!==t.claudeModelHash)throw Object.assign(new Error("Claude model selection does not match its node assignment."),{statusCode:400,code:"NODE_MODEL_SELECTION_MISMATCH"});const r=this.modelAssignments.get(e),o=ve.parse({instanceId:e,codexModelHash:t.codexModelHash,claudeModelHash:t.claudeModelHash,updatedAt:sr()});this.modelAssignments.put(o);try{return{assignment:o,instance:this.controlledInstances.put(Ge.parse({...n,modelSelection:t.modelSelection,updatedAt:sr()}))}}catch(t){throw r?this.modelAssignments.put(r):this.modelAssignments.delete(e),t}}resolvedAssignedModelEnvironment(e){const t=this.modelAssignments.get(e);if(!t){if(this.modelEnvironments.has(e))throw Object.assign(new Error(`Legacy model environment for instance ${e} requires manual migration.`),{statusCode:409,code:"NODE_MODEL_MIGRATION_REQUIRED"});return{}}return{...this.modelEnvironmentForRef("codex",t.codexModelHash),...this.modelEnvironmentForRef("claude",t.claudeModelHash)}}validateAssignmentRef(e,t){if(!t)return;const n=this.requireModel(t);if(n.app!==e)throw Object.assign(new Error(`Model ${n.id} belongs to ${n.app}, not ${e}.`),{statusCode:400,code:"NODE_MODEL_APP_MISMATCH"});if(!n.enabled)throw Object.assign(new Error(`Model ${n.id} is disabled.`),{statusCode:409,code:"NODE_MODEL_DISABLED"})}modelEnvironmentForRef(e,t){if(!t)return{};this.validateAssignmentRef(e,t);const n=this.requireModel(t);return"codex"===e?{OPENAI_API_KEY:n.key,OPENAI_BASE_URL:n.endpoint,TASK_HANDOFF_CODEX_BASE_URL:n.endpoint,TASK_HANDOFF_CODEX_MODEL:n.model}:{ANTHROPIC_API_KEY:n.key,ANTHROPIC_BASE_URL:n.endpoint,TASK_HANDOFF_CLAUDE_MODEL:n.model}}requireModel(e){const t=this.models.get(e);if(!t)throw Object.assign(new Error(`Model ${e} was not found on node ${this.nodeId}.`),{statusCode:404,code:"NODE_MODEL_NOT_FOUND"});if(Pe(t)!==t.id)throw Object.assign(new Error(`Stored model ${e} does not match its content hash.`),{statusCode:409,code:"NODE_MODEL_HASH_INVALID"});return t}modelReferenceIds(e){return this.listInstances().filter(t=>{const n=this.modelAssignments.get(t.id);return n?.codexModelHash===e||n?.claudeModelHash===e}).map(e=>e.id)}publicModel(e,t){const{key:n,...r}=e;return _e.parse({...r,keyPreview:n.length<=8?"set":`${n.slice(0,4)}...${n.slice(-4)}`,keySet:!0,referenceCount:t})}nextModelOrder(){return this.models.list().reduce((e,t)=>Math.max(e,t.order),0)+100}migrateLegacyModelEnvironments(){for(const e of this.modelEnvironments.listInstanceIds()){if(!this.controlledInstances.get(e)){this.warnLegacyModelMigration(e,"instance-not-found");continue}if(this.modelAssignments.get(e)){try{this.resolvedAssignedModelEnvironment(e),this.modelEnvironments.delete(e)}catch{this.warnLegacyModelMigration(e,"existing-assignment-invalid")}continue}let t;try{t=this.modelEnvironments.get(e)}catch{this.warnLegacyModelMigration(e,"sidecar-invalid");continue}if(!Object.keys(t).length){this.modelEnvironments.delete(e);continue}if(Object.keys(t).some(e=>!wn.has(e))){this.warnLegacyModelMigration(e,"unknown-fields");continue}const n=[];try{const r=this.migrateLegacyModelForApp(t,"codex",n),o=this.migrateLegacyModelForApp(t,"claude",n);if(!r&&!o)throw new Error("no complete model configuration");const i={...r?{codexModelHash:r}:{},...o?{claudeModelHash:o}:{}};this.assignModels(e,{modelSelection:i,codexModelHash:r,claudeModelHash:o}),this.resolvedAssignedModelEnvironment(e),this.modelEnvironments.delete(e)}catch{this.modelAssignments.delete(e);for(const e of n)this.models.delete(e);this.warnLegacyModelMigration(e,"mapping-failed")}}}migrateLegacyModelForApp(e,t,n){const r=e["codex"===t?"OPENAI_API_KEY":"ANTHROPIC_API_KEY"],o="codex"===t?e.TASK_HANDOFF_CODEX_BASE_URL||e.OPENAI_BASE_URL:e.ANTHROPIC_BASE_URL,i="codex"===t?e.TASK_HANDOFF_CODEX_MODEL||e.CODEX_MODEL:e.TASK_HANDOFF_CLAUDE_MODEL||e.CLAUDE_MODEL;if(!Boolean(r||o||i))return;if(!r||!o||!i)throw new Error("model configuration incomplete");const s=Pe({app:t,endpoint:o,key:r,model:i}),a=this.models.get(s);if(a){if(a.app!==t||a.key!==r||a.endpoint!==o||a.model!==i||!a.enabled)throw new Error("existing model conflicts with legacy environment");return s}const c=sr();return this.models.put(Ne.parse({id:s,name:`Migrated ${"codex"===t?"Codex":"Claude"} model`,endpoint:o,key:r,model:i,app:t,enabled:!0,order:this.nextModelOrder(),labels:{migratedFrom:"instance-model-environment"},createdAt:c,updatedAt:c})),n.push(s),s}warnLegacyModelMigration(e,t){console.warn(JSON.stringify({message:"legacy model environment was preserved because it could not be migrated",nodeId:this.nodeId,instanceId:e,reason:t}))}createInstance(e){const t=this.requireRuntime(e.runtimeId),r=sr(),o=e.id||Ht("inst"),i=Ae.parse(e.source);if(function(e){return"boolean"==typeof e.capabilities.requiresImage?e.capabilities.requiresImage:"local"!==e.type}(t)&&(!e.imageId||!e.image)){const e=new Error(`Runtime ${t.name} requires an image.`);throw Object.assign(e,{statusCode:400,code:"NODE_RUNTIME_IMAGE_REQUIRED"}),e}if("local"===t.type&&"local-folder"!==i.type){const e=new Error("Localhost runtime currently supports local folder sources only.");throw Object.assign(e,{statusCode:400,code:"LOCAL_RUNTIME_REQUIRES_LOCAL_FOLDER"}),e}if("local"===t.type&&this.listInstances().some(e=>"local"===this.requireRuntime(e.runtimeId).type)){const e=new Error("A localhost instance already exists on this node.");throw Object.assign(e,{statusCode:409,code:"LOCAL_RUNTIME_INSTANCE_EXISTS"}),e}const s=e.image?Me.parse(e.image):void 0,a="local"===t.type&&"local-folder"===i.type?n.resolve(i.path):void 0,c=Ge.parse({id:o,name:e.name||`instance-${r.replace(/[-:.TZ]/g,"").slice(0,14)}-${o.replace(/^inst_?/,"").slice(0,4)}`,source:i,sourceSnapshot:e.sourceSnapshot||{},modelSelection:e.modelSelection,projectId:e.projectId,nodeId:this.nodeId,runtimeId:t.id,imageId:s?.id,imageSnapshot:s,status:"created",health:"unknown",connectionStatus:"unknown",agentStatus:"unknown",targetStatus:"unknown",uiAccessStatus:"unknown",controlMode:"controlled",capabilities:{},config:e.config,workspace:"local"===t.type?{mode:"local-bind",status:"unknown",path:a}:{status:"unknown"},target:{strategy:"node-proxy",status:"unknown"},runtime:"local"===t.type?{kind:"local",workspacePath:a,labels:{"task-handoff.runtime-kind":"local"}}:{labels:{}},registrationToken:Kt(),createdAt:r,updatedAt:r});return this.controlledInstances.put(c)}registerInstance(e,t,n){const r=Xe.parse(t),o=sr(),i=this.controlledInstances.get(e);if(!i){const t=new Error(`Instance ${e} was not found on node ${this.nodeId}.`);throw Object.assign(t,{statusCode:404,code:"NODE_INSTANCE_NOT_FOUND"}),t}this.validateInstanceReport(i,r,n),yr(r.protocolVersion,`Instance ${e}`);const s=Ge.parse({...i,name:r.name,status:"registered",health:"ok",connectionStatus:"online",agentStatus:"online",targetStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":"reachable"===r.target.status?"reachable":i.targetStatus,uiAccessStatus:"endpoint-unreachable"===r.target.status?"endpoint-unreachable":i.uiAccessStatus,controlMode:r.controlMode,protocolVersion:r.protocolVersion,instanceVersion:r.instanceVersion,build:r.build,capabilities:r.capabilities,appInventory:r.appInventory,workspace:r.workspace,target:{...i.target,...r.target},registrationToken:i.registrationToken||r.registrationToken,lastHeartbeatAt:o,updatedAt:o});return this.controlledInstances.put(s)}heartbeatInstance(e,t,n){const r=this.requireInstance(e);this.validateInstanceToken(r,n);const o=Qe.parse(t);yr(o.protocolVersion,`Instance ${e}`);const i=sr(),s=o.target?{...r.target,...o.target}:r.target,a={...s,status:"unknown"===s.status&&(s.web||s.api)?"reachable":s.status},c="endpoint-unreachable"===a.status?"endpoint-unreachable":"reachable"===a.status?"reachable":r.targetStatus,d=Ge.parse({...r,...o,target:a,agentStatus:"online",targetStatus:c,uiAccessStatus:c,connectionStatus:"online",build:o.build||r.build,lastHeartbeatAt:i,updatedAt:i});return this.controlledInstances.put(d)}validateInstanceReport(e,t,n){this.validateInstanceToken(e,n||t.registrationToken),t.instanceId&&t.instanceId!==e.id&&fr("INSTANCE_ID_MISMATCH",`Instance ${t.instanceId} cannot register as ${e.id}.`),t.nodeId&&t.nodeId!==this.nodeId&&fr("INSTANCE_NODE_MISMATCH",`Instance ${e.id} belongs to node ${this.nodeId}.`),t.runtimeId&&t.runtimeId!==e.runtimeId&&fr("INSTANCE_RUNTIME_MISMATCH",`Instance ${e.id} belongs to runtime ${e.runtimeId}.`),t.imageId&&t.imageId!==e.imageId&&fr("INSTANCE_IMAGE_MISMATCH",`Instance ${e.id} belongs to image ${e.imageId}.`)}validateInstanceToken(e,t){e.registrationToken&&t===e.registrationToken||fr("INSTANCE_REGISTRATION_TOKEN_INVALID",`Invalid registration token for instance ${e.id}.`)}context(e,t=this.resolvedAssignedModelEnvironment(e.id)){const n=e.imageSnapshot||Me.parse({id:e.imageId||"img_localhost",name:e.imageId||"Localhost",image:e.imageId||"localhost",registry:"local",capabilities:[],optionalApps:[],defaultEnv:{},labels:{},createdAt:e.createdAt,updatedAt:e.updatedAt});return{project:lr(e),image:n,node:this.node,runtime:this.requireRuntime(e.runtimeId),instance:e,nodeAgentUrl:this.containerUrl,modelEnv:t}}}class Or{app;state;settings;sockets=new Set;config;source;status="error";error;constructor(e){this.app=e.app,this.state=e.state,this.settings=e.settings,this.config=e.config,this.source=e.source,this.app.server.on("connection",e=>{this.sockets.add(e),e.once("close",()=>this.sockets.delete(e))})}current(){return Ue.parse({...this.config,host:ar(this.config.bindScope),status:this.status,source:this.source,...this.error?{error:this.error}:{}})}async start(){try{await this.listen(this.config),this.status="listening",this.error=void 0,this.state.setListenerPort(this.config.port)}catch(e){this.status="error",this.error=e instanceof Error?e.message:String(e),this.app.log.error({host:ar(this.config.bindScope),port:this.config.port,error:this.error},"node agent TCP listener failed to start; Unix IPC remains available")}return this.current()}async update(e){const t=He.parse(e);if(t.bindScope===this.config.bindScope&&t.port===this.config.port)return this.current();if(t.port!==this.config.port){const e=this.state.runningInstanceCount();if(e>0){const t=new Error(`Cannot change the node agent port while ${e} controlled instance(s) are running.`);throw Object.assign(t,{statusCode:409,code:"NODE_AGENT_LISTENER_PORT_IN_USE_BY_INSTANCES",blockingInstanceCount:e}),t}}const n={config:this.config,source:this.source,status:this.status,error:this.error};await this.stop();try{await this.listen(t)}catch(e){await this.restore(n);const r=new Error(`Failed to bind node agent TCP listener at ${ar(t.bindScope)}:${t.port}: ${e instanceof Error?e.message:String(e)}`);throw Object.assign(r,{statusCode:409,code:"NODE_AGENT_LISTENER_BIND_FAILED"}),r}try{this.settings.put({version:1,externalListener:t})}catch(e){await this.stop(),await this.restore(n);const t=new Error(`Failed to persist node agent TCP listener: ${e instanceof Error?e.message:String(e)}`);throw Object.assign(t,{statusCode:500,code:"NODE_AGENT_LISTENER_PERSIST_FAILED"}),t}return this.config=t,this.source="persisted",this.status="listening",this.error=void 0,this.state.setListenerPort(t.port),this.current()}async shutdown(){await this.stop()}async restore(e){if(this.config=e.config,this.source=e.source,this.status=e.status,this.error=e.error,"listening"===e.status)try{await this.listen(e.config)}catch(e){this.status="error",this.error=`Failed to restore previous listener: ${e instanceof Error?e.message:String(e)}`,this.app.log.error({error:this.error},"node agent TCP listener rollback failed")}this.state.setListenerPort(e.config.port)}async listen(e){await new Promise((t,n)=>{const r=e=>{this.app.server.off("listening",o),n(e)},o=()=>{this.app.server.off("error",r),t()};this.app.server.once("error",r),this.app.server.once("listening",o),this.app.server.listen({host:ar(e.bindScope),port:e.port})})}async stop(){this.app.server.listening&&await new Promise((e,t)=>{this.app.server.close(n=>n?t(n):e());for(const e of this.sockets)e.destroy()})}}async function xr(e,t,n,r,o,i="request"){const s=e.requireInstance(r);o.diagnostic({instanceId:r,action:"start",reason:i,runtimeId:s.runtimeId,imageId:s.imageId},"node instance start requested");const a=e.controlledInstances.put(Ge.parse({...s,status:"provisioning",updatedAt:sr()})),c=t.forRuntime(e.requireRuntime(a.runtimeId)),d=await c.start(e.context(a)),l=await ur(n,Ge.parse({...a,...d,target:d.target?{...a.target,...d.target}:a.target,workspace:d.workspace?{...a.workspace,error:void 0,...d.workspace}:a.workspace,runtime:d.runtime?{...a.runtime,...d.runtime}:a.runtime,updatedAt:sr()})),u=Ge.parse({...a,...d,target:d.target?{...a.target,...d.target,status:l}:a.target,workspace:d.workspace?{...a.workspace,error:void 0,...d.workspace}:a.workspace,runtime:d.runtime?{...a.runtime,...d.runtime}:a.runtime,targetStatus:l,uiAccessStatus:l,connectionStatus:d.connectionStatus,updatedAt:sr()}),p=e.controlledInstances.put(u);return await mr(n,p,"start",o),o.diagnostic({instanceId:r,action:"start",reason:i,status:p.status,connectionStatus:p.connectionStatus,targetStatus:p.targetStatus,targetWeb:p.target.web,containerName:p.runtime.containerName},"node instance start completed"),p}function Tr(e,t){const n=t instanceof Error?t.message:String(t);return Ge.parse({...e,status:"failed",health:"failed",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",workspace:{...e.workspace,error:n},updatedAt:sr()})}function Cr(e){return Ge.parse({...e,health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:sr()})}async function Dr(e={}){const r=e.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,o=Number(e.port||process.env.TASK_HANDOFF_NODE_AGENT_PORT||"8091"),s=`http://127.0.0.1:${o}`,u=e.containerUrl||process.env.TASK_HANDOFF_NODE_AGENT_CONTAINER_URL,p=Pt(e.dataDir),m=e.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,g=e.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID,f=new Gn(p),y=f.resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID),I=e.connectionMode||("local-ipc"===process.env.TASK_HANDOFF_NODE_AGENT_CONNECTION_MODE?"local-ipc":"local-loopback"),b=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||an(p.dataDir),A="local-ipc"===I?`ipc://${encodeURIComponent(b)}`:s,z=new vr(p,y,s,u,o);z.node.connectionMode=I,z.node.controlEndpoint=A,z.node.endpoint=A,z.init();const w=new it(e.dockerCommandRunner,{publishHost:"127.0.0.1"}),S=new _r(new Nr(w),new Er(e.dockerCommandRunner||ot,p,()=>z.localNodeAgentUrl)),k=e.updateCommandRunner||e.dockerCommandRunner||ot,N=e.fetchImpl||fetch,E=a({logger:e.logger??!0});E.decorate("nodeAgentState",z),await E.register(c);const _=new St(z,r,{logger:E.log,safetyIntervalMs:Number(process.env.TASK_HANDOFF_EVENT_CONNECTION_SAFETY_INTERVAL_MS)||void 0});_.start(),E.decorate("nodeAgentEventForwarder",_);const v=(j=process.env.TASK_HANDOFF_DIAGNOSTIC_LOGS,["1","true","yes","on"].includes(String(j||"").trim().toLowerCase())),O=new En(f,y,m,g),x=(e,t)=>{v&&E.log.info(e,t)},T={diagnostic:x,warn:(e,t)=>E.log.warn(e,t)},C=async e=>{if("node-agent"===e.target.component)return async function(e){const t=await hn(e.runCommand,"@task-handoff/node-agent",e.channel);return t?me.parse({target:{component:"node-agent"},source:"npm",channel:e.channel,currentVersion:e.currentVersion,availableVersion:t,updateAvailable:gn(e.currentVersion,t),checkedAt:mn()}):yn({target:{component:"node-agent"},channel:e.channel,currentVersion:e.currentVersion})}({channel:e.channel,currentVersion:tr(),runCommand:k});const t=z.requireInstance(e.target.instanceId);return async function(e){const t={component:"controlled-instance",instanceId:e.instance.id};if("local"===e.runtime.type){const n=await hn(e.runCommand,"@task-handoff/controlled-instance",e.channel),r=e.instance.build?.packageVersion||e.instance.instanceVersion;return n?me.parse({target:t,source:"npm",channel:e.channel,currentVersion:r,availableVersion:n,updateAvailable:gn(r,n),checkedAt:mn()}):yn({target:t,channel:e.channel,currentVersion:r})}if("docker"===e.runtime.type){const n=e.instance.imageSnapshot?.image;if(!n||"local"===e.instance.imageSnapshot?.registry)return me.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:e.instance.build?.imageDigest,availableVersion:e.instance.build?.imageDigest||"unavailable",updateAvailable:!1,supported:!1,reason:"The instance does not use a remote Docker image.",checkedAt:mn()});const r=function(e,t){const n=e.split("@",1)[0],r=n.lastIndexOf("/"),o=n.lastIndexOf(":");return`${o>r?n.slice(0,o):n}:${In(t)}`}(n,e.channel),o=await e.runCommand("docker",["manifest","inspect","--verbose",r]),i=bn(JSON.parse(o.stdout));if(!i)throw new Error(`Docker registry did not return a digest for ${r}.`);const s=e.instance.build?.imageDigest;return me.parse({target:t,source:"docker-registry",channel:e.channel,currentVersion:s,availableVersion:i,artifactRef:r,updateAvailable:s!==i,checkedAt:mn()})}return me.parse({target:t,source:"npm",channel:e.channel,availableVersion:"unsupported",updateAvailable:!1,supported:!1,reason:`Runtime type ${e.runtime.type} does not support managed updates.`,checkedAt:mn()})}({channel:e.channel,instance:t,runtime:z.requireRuntime(t.runtimeId),runCommand:k})},D=async e=>{if("controlled-instance"!==e.target.component)return;const r=z.requireInstance(e.target.instanceId),o=z.requireRuntime(r.runtimeId),i=S.forRuntime(o);if("local"===o.type){const r=fn(),o=(await k(r,["prefix","--global"])).stdout.trim(),[i]=br(),s=function(e){const r=function(e,r){if(!n.isAbsolute(e)||!t.existsSync(e))return;const o=t.realpathSync(e);let i=t.statSync(o).isDirectory()?o:n.dirname(o);for(;i!==n.dirname(i);){try{if(JSON.parse(t.readFileSync(n.join(i,"package.json"),"utf8")).name===r)return i}catch{}i=n.dirname(i)}}(e.executable,e.packageName),o=r&&function(e){let r=n.dirname(e);for(;r!==n.dirname(r);){try{if("@task-handoff/server"===JSON.parse(t.readFileSync(n.join(r,"package.json"),"utf8")).name)return r}catch{}r=n.dirname(r)}}(r);return o?{args:["install","--prefix",o,"--no-save",`${e.packageName}@${e.targetVersion}`],manifestPath:n.join(r,"package.json")}:{args:["install","--global","--prefix",e.globalPrefix,`${e.packageName}@${e.targetVersion}`],manifestPath:r?n.join(r,"package.json"):n.join(e.globalPrefix,"lib","node_modules",...e.packageName.split("/"),"package.json")}}({executable:i,globalPrefix:o,packageName:"@task-handoff/controlled-instance",targetVersion:e.toVersion});await k(r,s.args),function(e,n){let r;try{r=JSON.parse(t.readFileSync(e,"utf8")).version}catch(t){throw new Error(`Could not verify the updated package at ${e}: ${t instanceof Error?t.message:String(t)}`)}if(r!==n)throw new Error(`Updated package verification failed: expected ${n}, found ${String(r||"unknown")}.`)}(s.manifestPath,e.toVersion)}else{if("docker"!==o.type)throw Object.assign(new Error(`Runtime type ${o.type} does not support managed updates.`),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});{const t=e.artifactRef;if(!t||"local"===r.imageSnapshot?.registry)throw Object.assign(new Error("The instance does not use an updateable remote Docker image."),{code:"INSTANCE_UPDATE_UNSUPPORTED",statusCode:400});await k("docker",["pull",t])}}await i.delete(z.context(r,{})),z.controlledInstances.put(function(e){return Ge.parse({...e,status:"stopped",health:"unknown",connectionStatus:"offline",agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",target:{...e.target,status:"unknown"},updatedAt:sr()})}(Ge.parse({...r,..."docker"===o.type&&e.artifactRef&&r.imageSnapshot?{imageSnapshot:{...r.imageSnapshot,image:e.artifactRef,updatedAt:sr()}}:{}}))),await xr(z,S,N,r.id,T,"update")};var j;E.setErrorHandler((e,t,n)=>{const r=function(e){if(e instanceof l.z.ZodError)return{statusCode:400,code:"VALIDATION_ERROR",message:e.issues.map(e=>`${e.path.join(".")||"body"}: ${e.message}`).join("; ")};const t=e&&"object"==typeof e?e:{};return{statusCode:"number"==typeof t.statusCode?t.statusCode:500,code:"string"==typeof t.code?t.code:"NODE_AGENT_ERROR",message:e instanceof Error?e.message:String(e)}}(e);(v||r.statusCode>=500)&&(r.statusCode>=500?E.log.error.bind(E.log):E.log.warn.bind(E.log))({method:t.method,url:t.url,statusCode:r.statusCode,errorCode:r.code,error:r.message},"node agent request failed"),n.code(r.statusCode).send({error:{code:r.code,message:r.message,...e&&"object"==typeof e&&"number"==typeof e.blockingInstanceCount?{blockingInstanceCount:e.blockingInstanceCount}:{}}})}),E.addHook("preHandler",async e=>{const t=O.verify(e);if(t)e.nodeAgentAuthKeyId=t;else if(ir(e)){if(r&&rr(e.headers)!==r){const e=new Error("Invalid node agent token.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),e}}else if("/api/node-agent/pairing/complete"!==e.url.split("?")[0]&&!function(e){const t=e.split("?")[0];return/^\/api\/node-agent\/instances\/[^/]+\/(register|heartbeat)$/.test(t)}(e.url)){if(O.hasRemoteSecrets()){const e=new Error("Node agent HMAC signature is required.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_HMAC_SIGNATURE_REQUIRED"}),e}if(!function(e){return or(e.ip)||or(e.socket.remoteAddress)||!e.ip&&!e.socket.remoteAddress&&!e.socket.remoteFamily}(e)){const e=new Error("Local node agent access is only accepted from loopback or local IPC connections.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_LOCAL_TOKEN_REQUIRES_LOOPBACK"}),e}if(r&&rr(e.headers)!==r){const e=new Error("Invalid node agent token.");throw Object.assign(e,{statusCode:401,code:"NODE_AGENT_UNAUTHORIZED"}),e}}}),E.addHook("onClose",async()=>{_.stop(),await async function(e,t){await t.stopAll();for(const t of e.listInstances())"local"===e.requireRuntime(t.runtimeId).type&&e.controlledInstances.put(Cr(t))}(z,S)}),E.decorate("nodeAgentRestoreLocalInstances",async()=>{const e=z.listInstances().filter(e=>"local"===z.requireRuntime(e.runtimeId).type&&Sr.has(e.status));for(const t of e)try{await xr(z,S,N,t.id,T,"restore")}catch(e){z.controlledInstances.put(Tr(z.requireInstance(t.id),e)),x({instanceId:t.id,action:"restore",error:e instanceof Error?e.message:String(e)},"node local instance restore failed")}}),E.get("/api/node-agent/health",async()=>{return{data:{ok:!0,role:"node-agent",nodeId:y,protocolVersion:Z,build:(e="node-agent",{component:e,packageName:"@task-handoff/node-agent",packageVersion:tr(),protocolVersion:Z,buildId:er("TASK_HANDOFF_BUILD_ID"),builtAt:er("TASK_HANDOFF_BUILT_AT"),gitCommit:er("TASK_HANDOFF_GIT_COMMIT"),imageRef:er("TASK_HANDOFF_IMAGE_REF"),imageDigest:er("TASK_HANDOFF_IMAGE_DIGEST")}),serverTime:(new Date).toISOString()}};var e});const R=e=>{if(!ir(e)){const e=new Error("Node agent TCP listener settings are available only over local Unix IPC.");throw Object.assign(e,{statusCode:403,code:"NODE_AGENT_LISTENER_LOCAL_IPC_ONLY"}),e}if(!E.nodeAgentListenerManager){const e=new Error("Node agent TCP listener manager is not initialized.");throw Object.assign(e,{statusCode:503,code:"NODE_AGENT_LISTENER_UNAVAILABLE"}),e}return E.nodeAgentListenerManager};return E.get("/api/node-agent/settings/external-listener",async e=>({data:R(e).current()})),E.patch("/api/node-agent/settings/external-listener",async e=>({data:await R(e).update(e.body)})),E.get("/api/node-agent/updates/jobs",async()=>({data:z.updateJobs.list()})),E.post("/api/node-agent/updates/check",async e=>({data:await C(pe.parse(e.body))})),E.post("/api/node-agent/updates/apply",async(e,r)=>{const o=Zn.parse(e.body),s=await C(o);if(!s.supported){const e=new Error(s.reason||"The requested update is not supported.");throw Object.assign(e,{statusCode:400,code:"UPDATE_UNSUPPORTED"}),e}if(!s.updateAvailable){const e=new Error(s.reason||"No update is available for the selected channel.");throw Object.assign(e,{statusCode:409,code:"UPDATE_NOT_AVAILABLE"}),e}const a=z.updateJobs.create(y,s);if("node-agent"===o.target.component){const e=("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)?n.dirname(i.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:h&&"SCRIPT"===h.tagName.toUpperCase()&&h.src||new URL("cli.js",document.baseURI).href)):__dirname,{worker:r,packaged:o,expectedWorker:s}=function(e,r=t.existsSync){const o=[n.resolve(e,"..","bin","task-handoff-node-update-worker"),n.resolve(e,"..","..","bin","task-handoff-node-update-worker")],i=o.find(r),s=n.resolve(e,"..","..","..","..","scripts","node-update-worker.cjs");return i?{worker:i,packaged:!0,expectedWorker:o[0]}:r(s)?{worker:s,packaged:!1,expectedWorker:o[0]}:{worker:void 0,packaged:!1,expectedWorker:o[0]}}(e);if(!r){z.updateJobs.patch(a.id,{status:"failed",error:`Update worker was not found: ${s}`,completedAt:sr()});const e=new Error(`Node agent update worker was not found: ${s}`);throw Object.assign(e,{statusCode:500,code:"UPDATE_WORKER_NOT_FOUND"}),e}await k("systemd-run",["--unit",`task-handoff-update-${a.id}`,"--collect","--property=Type=exec",...o?[r]:[process.execPath,r],"--job-file",z.updateJobs.records.filePath(a.id),"--target-version",a.toVersion,"--npm-command",fn()])}else z.updateJobs.run(a,e=>D(e));return r.code(202).send({data:a})}),E.post("/api/node-agent/pairing/invites",async(e,t)=>{const n=f.createPairingInvite(Dn.parse(e.body||{}));return t.code(201).send({data:{nodeId:y,joinToken:n.token,expiresAt:n.expiresAt}})}),E.post("/api/node-agent/pairing/complete",async(e,t)=>{const n=f.completePairingInvite(jn.parse(e.body));return t.code(201).send({data:{nodeId:y,keyId:n.keyId,secret:n.secret,pairedAt:n.pairedAt}})}),E.get("/api/node-agent/remotes",async e=>({data:f.listRemoteControlPlanes(e.nodeAgentAuthKeyId)})),E.delete("/api/node-agent/remotes/:keyId",async e=>{const t=l.z.string().trim().min(1).max(160).parse(e.params.keyId);return{data:{deleted:f.deleteRemoteControlPlane(t,e.nodeAgentAuthKeyId)}}}),E.post("/api/node-agent/remotes/connect",async(e,t)=>{const n=Rn.parse(e.body),r=function(e){const t=new URL(e);if("http:"!==t.protocol&&"https:"!==t.protocol){const e=new Error("Control-plane URL must use http or https.");throw Object.assign(e,{statusCode:400,code:"NODE_AGENT_REMOTE_CONTROL_PLANE_URL_INVALID"}),e}return t.toString().replace(/\/$/,"")}(n.controlPlaneUrl),o=f.createRemoteControlPlane({url:r,...n.controlPlaneName?{name:n.controlPlaneName}:{},active:!1!==n.activate}),i=await async function(e,t,n){const r=await e(new URL("/api/node-join/complete",t).toString(),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)}),o=await r.json().catch(()=>({}));if(!r.ok){const e=new Error(o.error?.message||`Control-plane node join failed with HTTP ${r.status}.`);throw Object.assign(e,{statusCode:r.status,code:o.error?.code||"NODE_AGENT_REMOTE_JOIN_FAILED"}),e}return o.data||{}}(N,r,{joinToken:n.joinToken,nodeId:y,nodeName:z.node.name,keyId:o.keyId,secret:o.secret,pairedAt:o.pairedAt}),s=f.upsertRemoteControlPlane({...o,..."string"==typeof i.name&&i.name?{name:n.controlPlaneName||i.name}:{}});let a,c=!1!==s.active?"saved":"disabled";if(!1!==s.active&&E.nodeAgentReverseTunnels)try{E.nodeAgentReverseTunnels.connect({url:r,keyId:s.keyId,secret:s.secret}),c="connecting"}catch(e){c="failed",a=e instanceof Error?e.message:String(e)}return t.code(201).send({data:{remote:{id:s.id,url:s.url,keyId:s.keyId,pairedAt:s.pairedAt,active:!1!==s.active},tunnel:{status:c,...a?{error:a}:{}}}})}),E.get("/api/node-agent/runtimes",async()=>({data:z.nodeRuntimes.list()})),E.post("/api/node-agent/runtimes",async(e,t)=>t.code(201).send({data:z.createRuntime(jt.parse(e.body))})),E.patch("/api/node-agent/runtimes/:id",async e=>({data:z.updateRuntime(e.params.id,Rt.parse(e.body))})),E.delete("/api/node-agent/runtimes/:id",async e=>({data:{deleted:z.deleteRuntime(e.params.id)}})),E.post("/api/node-agent/runtimes/:id/check",async e=>{const t=z.requireRuntime(e.params.id);return{data:await z.checkRuntime(t.id,S.forRuntime(t))}}),E.get("/api/node-agent/local-folders",async()=>({data:z.localFolders.list()})),E.get("/api/node-agent/folders/tree",async e=>({data:vt(Tt.parse(e.query))})),E.post("/api/node-agent/local-folders",async(e,t)=>t.code(201).send({data:z.createLocalFolder(xt.parse(e.body))})),E.delete("/api/node-agent/local-folders/:id",async e=>({data:{deleted:z.localFolders.delete(e.params.id)}})),E.get("/api/node-agent/models",async()=>({data:z.listModels()})),E.post("/api/node-agent/models",async(e,t)=>t.code(201).send({data:z.createModel(Ce.parse(e.body))})),E.put("/api/node-agent/models/:id/deploy",async e=>{const t=e.params.id,n=je.parse(e.body);if(n.id!==t)throw Object.assign(new Error(`Model payload id ${n.id} does not match route id ${t}.`),{statusCode:400,code:"NODE_MODEL_ID_MISMATCH"});return{data:z.deployModel(n)}}),E.patch("/api/node-agent/models/:id",async e=>({data:z.updateModel(e.params.id,De.parse(e.body))})),E.delete("/api/node-agent/models/:id",async e=>({data:{deleted:z.deleteModel(e.params.id)}})),E.get("/api/node-agent/instances",async()=>({data:z.listInstances()})),E.post("/api/node-agent/instances",async(e,t)=>{const n=z.createInstance(Ct.parse(e.body));return _.syncNow(),t.code(201).send({data:n})}),E.patch("/api/node-agent/instances/:id",async e=>{const t=e.params.id,n=z.requireInstance(t),r=Dt.parse(e.body),o=Ge.parse({...n,...r,updatedAt:sr()}),i=z.controlledInstances.put(o);return _.syncNow(),{data:i}}),E.put("/api/node-agent/instances/:id/model-assignment",async e=>{const t=e.params.id,n=z.assignModels(t,Re.parse(e.body));return await gr(N,z,t),{data:n}}),E.post("/api/node-agent/instances/:id/register",async(e,t)=>{const n=e.params.id,r=z.registerInstance(n,Xe.parse(e.body),rr(e.headers));return _.syncNow(),x({instanceId:n,action:"register",protocolVersion:r.protocolVersion,build:r.build,targetStatus:r.targetStatus,targetStrategy:r.target.strategy},"node instance registered"),t.code(201).send({data:r})}),E.post("/api/node-agent/instances/:id/heartbeat",async e=>{const t=e.params.id,n=z.heartbeatInstance(t,Qe.parse(e.body),rr(e.headers));return _.syncNow(),x({instanceId:t,action:"heartbeat",status:n.status,health:n.health,protocolVersion:n.protocolVersion,build:n.build,targetStatus:n.targetStatus,apps:n.apps.runningCount},"node instance heartbeat accepted"),{data:n}}),E.get("/api/node-agent/events",{websocket:!0},e=>{const t=e;t.send(JSON.stringify({type:"node-agent.events.connected",nodeId:y,serverTime:(new Date).toISOString()}));const n=_.addOutput(t);t.on("close",n),t.on("error",n)}),E.get("/api/node-agent/docker/images",async()=>({data:await st(e.dockerCommandRunner)})),E.post("/api/node-agent/instances/:id/start",async e=>{const t=e.params.id;Qn.parse(e.body);const n=await xr(z,S,N,t,T);return _.syncNow(),{data:n}}),E.post("/api/node-agent/instances/:id/stop",async e=>{const t=z.requireInstance(e.params.id);x({instanceId:t.id,action:"stop",runtimeId:t.runtimeId,containerName:t.runtime.containerName},"node instance stop requested");const n=S.forRuntime(z.requireRuntime(t.runtimeId)),r=await n.stop(z.context(t)),o=Ge.parse({...t,...r,agentStatus:"offline",targetStatus:"unknown",uiAccessStatus:"unknown",updatedAt:sr()}),i=z.controlledInstances.put(o);return _.syncNow(),x({instanceId:t.id,action:"stop",status:i.status,connectionStatus:i.connectionStatus,containerName:i.runtime.containerName},"node instance stop completed"),{data:i}}),E.post("/api/node-agent/instances/:id/restart",async e=>{const t=e.params.id,n=z.requireInstance(t);Qn.parse(e.body),x({instanceId:t,action:"restart",runtimeId:n.runtimeId,imageId:n.imageId,containerName:n.runtime.containerName},"node instance restart requested");const r=S.forRuntime(z.requireRuntime(n.runtimeId)),o=await r.restart(z.context(n)),i=Ge.parse({...n,...o,target:o.target?{...n.target,...o.target}:n.target,updatedAt:sr()}),s=await ur(N,i),a=Ge.parse({...n,...o,target:o.target?{...n.target,...o.target,status:s}:n.target,targetStatus:s,uiAccessStatus:s,connectionStatus:o.connectionStatus,updatedAt:sr()}),c=z.controlledInstances.put(a);return await mr(N,c,"restart",T),_.syncNow(),x({instanceId:t,action:"restart",status:c.status,connectionStatus:c.connectionStatus,targetStatus:c.targetStatus,targetWeb:c.target.web,containerName:c.runtime.containerName},"node instance restart completed"),{data:c}}),E.post("/api/node-agent/instances/:id/delete",async e=>{const t=e.params.id,n=z.requireInstance(t);x({instanceId:t,action:"delete",runtimeId:n.runtimeId,containerName:n.runtime.containerName},"node instance delete requested");const r=S.forRuntime(z.requireRuntime(n.runtimeId));await r.delete(z.context(n,{})),x({instanceId:t,action:"delete"},"node instance delete completed");const o=z.controlledInstances.delete(t);return z.modelAssignments.delete(t),z.modelEnvironments.delete(t),_.syncNow(),{data:{deleted:o}}}),E.post("/api/node-agent/instances/:id/proxy",async(e,t)=>{const n=e.params.id,r=Ot.parse(e.body),o=hr(z.requireInstance(n)),i=r.path.startsWith("/")?r.path:`/${r.path}`;"POST"===r.method&&"/api/apps/sessions"===i&&await gr(N,z,n),x({instanceId:n,action:"proxy",method:r.method,path:i,instanceBase:o},"node instance proxy requested");const s=await N(`${o}${i}`,{method:r.method,headers:{...r.headers},body:nr(r)}),a=s.headers.get("content-type")||"application/octet-stream",c=await s.text();x({instanceId:n,action:"proxy",method:r.method,path:i,statusCode:s.status,contentType:a},"node instance proxy completed"),t.code(s.status).type(a).send(c)}),E.post("/api/node-agent/instances/:id/proxy/raw",async e=>{const t=e.params.id,n=Ot.parse(e.body),r=hr(z.requireInstance(t)),o=n.path.startsWith("/")?n.path:`/${n.path}`;x({instanceId:t,action:"proxy.raw",method:n.method,path:o,instanceBase:r},"node instance raw proxy requested");const i=await N(`${r}${o}`,{method:n.method,headers:{...n.headers},body:nr(n)}),s=Buffer.from(await i.arrayBuffer());return x({instanceId:t,action:"proxy.raw",method:n.method,path:o,statusCode:i.status,byteLength:s.length},"node instance raw proxy completed"),{data:{status:i.status,headers:(a=i.headers,Object.fromEntries([...a.entries()].filter(([e])=>!Jn.has(e.toLowerCase())))),bodyBase64:s.toString("base64")}};var a}),E.get("/api/node-agent/instances/:id/proxy/ws/*",{websocket:!0},(e,t)=>{const n=t.params.id,r=t.params["*"]||"",o=t.url.indexOf("?"),i=o>=0?t.url.slice(o):"";let s;try{const o=hr(z.requireInstance(n)),a=new URL(`/${r}${i}`,`${o}/`);a.protocol="https:"===a.protocol?"wss:":"ws:";const c=function(e){const t=e["sec-websocket-protocol"],n=(Array.isArray(t)?t.join(","):"string"==typeof t?t:"").split(",").map(e=>e.trim()).filter(Boolean);return n.length?n:void 0}(t.headers);x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,upstreamUrl:a.toString(),protocols:c||[]},"node instance websocket proxy opening"),s=c?new d(a,c):new d(a),s.on("open",()=>{x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy opened"),e.on("message",(e,t)=>s?.readyState===d.OPEN&&s.send(function(e,t){return t?e:e.toString()}(e,t))),e.on("close",()=>s?.close()),e.on("error",()=>s?.close())}),s.on("message",(t,n)=>{e.readyState===d.OPEN&&e.send(n?t:t.toString())}),s.on("close",()=>{x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`},"node instance websocket proxy closed"),e.close()}),s.on("error",t=>{x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:t instanceof Error?t.message:String(t)},"node instance websocket proxy failed"),e.close(1011,"Instance websocket proxy failed.")})}catch(t){x({instanceId:n,action:"proxy.ws",path:`/${r}${i}`,error:t instanceof Error?t.message:String(t)},"node instance websocket endpoint unavailable"),s?.close(),e.close(1011,"Instance websocket endpoint is not reachable.")}}),E}function jr(e){const t=new URL("/api/node-agent/tunnel",e);return t.protocol="https:"===t.protocol?"wss:":"ws:",t.toString()}function Rr(e,t){const n=new URL(t.tunnelUrl);n.searchParams.set("nodeId",t.nodeId);const r=t.secret?un({nodeId:t.nodeId,keyId:t.keyId,secret:t.secret,method:"GET",pathWithQuery:`${n.pathname}${n.search}`}):{},o=new d(n,{headers:r}),i=new Map;let s;const a=e=>{const n=e.startsWith("/")?e:`/${e}`,r="function"==typeof t.port?t.port():t.port,o=new URL(`/api/node-agent${n}`,`http://127.0.0.1:${r}`);return o.protocol="ws:",o},c=e=>t.secret?un({nodeId:t.nodeId,keyId:t.keyId,secret:t.secret,method:"GET",pathWithQuery:`${e.pathname}${e.search}`}):{},l=(e,t=1e3,n="")=>{const r=i.get(e);i.delete(e),r?.upstream?.close(t,n),r?.tunnel?.close(t,n)};return o.on("open",()=>{o.send(JSON.stringify({type:"node-agent.identify",nodeId:t.nodeId,serverTime:(new Date).toISOString()})),s=e.nodeAgentEventForwarder?.addOutput(o)}),o.on("close",()=>{for(const e of i.keys())l(e,1001,"Reverse tunnel disconnected.");s?.(),s=void 0}),o.on("message",async r=>{let s;try{s=JSON.parse(String(r))}catch{return void o.send(JSON.stringify({type:"node-agent.error",code:"INVALID_JSON"}))}const u=s&&"object"==typeof s?s:{};if("control-plane.websocket.open"===u.type){const e="string"==typeof u.streamId?u.streamId:"",r="string"==typeof u.route?u.route:"",s=Array.isArray(u.protocols)?u.protocols.filter(e=>"string"==typeof e):void 0;try{const l=t.token?{authorization:`Bearer ${t.token}`}:void 0,u=s?.length?new d(a(r),s,{headers:l}):new d(a(r),{headers:l}),p=(e=>{const t=new URL(n);return t.pathname=`${t.pathname.replace(/\/$/,"")}/streams/${encodeURIComponent(e)}`,t})(e),m=new d(p,{headers:c(p)});i.set(e,{upstream:u,tunnel:m,close:(e=1e3,t="")=>{u.close(e,t),m.close(e,t)}}),u.on("open",()=>{o.send(JSON.stringify({type:"node-agent.websocket.open",streamId:e,protocol:u.protocol}))}),function(e,t,n={}){const r=n.pendingFrameLimit??256;let o=!1,i=et(t);const s=[],a=[];let c;const d=()=>{c&&(clearTimeout(c),c=void 0)},l=(n,o)=>{if(n.length>=r)return tt(e,1011,"WebSocket bridge pending frame limit exceeded."),void tt(t,1011,"WebSocket bridge pending frame limit exceeded.");n.push(o)};n.upstreamOpenTimeoutMs&&!i&&(c=setTimeout(()=>{i||o||(tt(e,1011,"Upstream websocket did not open."),tt(t))},n.upstreamOpenTimeoutMs)),e.on("open",()=>rt(e,a)),t.on("open",()=>{d(),i=!0,n.onUpstreamOpen?.(),rt(t,s)}),e.on("message",(e,n=!1)=>{const r={data:e,isBinary:Boolean(n)};et(t)?nt(t,r):l(s,r)}),t.on("message",(t,n=!1)=>{const r={data:t,isBinary:Boolean(n)};et(e)?nt(e,r):l(a,r)}),t.on("close",(t,r)=>{d(),n.onUpstreamClose?.(t,r),(i||o||!n.onUpstreamCloseBeforeOpen?.())&&tt(e,t,r)}),t.on("error",t=>{n.onUpstreamError?.(t),(i||o||!n.onUpstreamErrorBeforeOpen?.(t))&&tt(e)}),e.on("close",(e,r)=>{d(),o=!0,n.onClientClose?.(e,r),tt(t,e,r)}),e.on("error",e=>{d(),o=!0,n.onClientError?.(e),tt(t,1011,"WebSocket bridge client failed.")})}(m,u,{pendingFrameLimit:256,upstreamOpenTimeoutMs:1e4,onClientClose:()=>{i.delete(e)},onClientError:t=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))},onUpstreamClose:(t,n)=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.close",streamId:e,code:"number"==typeof t?t:1e3,reason:Buffer.isBuffer(n)?n.toString("utf8"):String(n||"")}))},onUpstreamError:t=>{i.delete(e),o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))}})}catch(t){o.send(JSON.stringify({type:"node-agent.websocket.error",streamId:e,message:t instanceof Error?t.message:String(t)}))}return}if("control-plane.websocket.close"===u.type){const e="string"==typeof u.streamId?u.streamId:"";return void l(e,"number"==typeof u.code?u.code:1e3,"string"==typeof u.reason?u.reason:"")}if("control-plane.request"===u.type){const n="string"==typeof u.requestId?u.requestId:"",r=u.init&&"object"==typeof u.init?u.init:{},i="string"==typeof u.route&&u.route.startsWith("/")?u.route:"/health",s=r.headers&&"object"==typeof r.headers?r.headers:{};try{const a=await e.inject({method:(p=r.method,"string"==typeof p&&pn.has(p.toUpperCase())?p.toUpperCase():"GET"),url:`/api/node-agent${i}`,headers:{...s,...t.token?{authorization:`Bearer ${t.token}`}:{}},payload:"string"==typeof r.body?r.body:void 0});o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:a.statusCode,headers:a.headers,body:a.body}))}catch(e){o.send(JSON.stringify({type:"node-agent.response",requestId:n,status:502,error:{code:"NODE_AGENT_REVERSE_INJECT_FAILED",message:e instanceof Error?e.message:String(e)}}))}}var p}),o}async function Pr(e){const o=Pt(e.dataDir),i=(s=e.host,a=e.port,Le.parse({bindScope:"0.0.0.0"===s.trim()?"all-ipv4":"loopback",port:a}));var s,a;const c=t.existsSync(o.settingsPath),d=function(e,t){return new Bt(e.settingsPath,()=>({version:1,externalListener:t}),{schema:Xn,sanitize:cr})}(o,i),l=d.get().externalListener,m=function(e,r={}){return function(e,r={}){t.mkdirSync(n.dirname(e),{recursive:!0});const o={pid:process.pid,hostname:p.hostname(),component:r.component,command:process.argv.join(" "),acquiredAt:(new Date).toISOString(),token:u.randomUUID(),dataDir:r.dataDir,host:r.host,port:r.port};for(let n=0;n<5;n+=1)try{return t.mkdirSync(e),t.writeFileSync(Jt(e),`${JSON.stringify({token:o.token,startedAt:(new Date).toISOString()})}\n`,{encoding:"utf8",mode:384,flag:"wx"}),Qt(e,o),{lockPath:e,owner:o,release(){const n=Yt(e);n?.token===o.token&&t.rmSync(e,{recursive:!0,force:!0})}}}catch(o){if("EEXIST"!==o.code)throw o;const i=Yt(e);if(Xt(i?.pid))throw new Vt(e,i,r.error);if(!i&&nn(e)<2e3){if(n+1<5){tn();continue}throw new Vt(e,void 0,r.error)}const s=u.randomUUID();if(!rn(e,s)){if(n+1<5){tn();continue}throw new Vt(e,Yt(e),r.error)}const a=Yt(e);if(Xt(a?.pid))throw en(Wt(e)),new Vt(e,a,r.error);if(i?.token&&a?.token!==i.token){en(Wt(e));continue}if(!sn(e,s))continue;t.rmSync(e,{recursive:!0,force:!0})}const i=Yt(e);throw new Vt(e,i,r.error)}(e,{...r,component:"node-agent",error:{label:"Node agent",code:"NODE_AGENT_ALREADY_RUNNING"}})}(n.join(p.tmpdir(),`task-handoff-node-agent-${process.getuid?.()??"user"}.lock`),{dataDir:o.dataDir,host:ar(l.bindScope),port:l.port});try{const i={...e,port:l.port},s=await Dr(i),a=function(e,t,n,r){const o=t.token||process.env.TASK_HANDOFF_NODE_AGENT_TOKEN,i=new Gn(n),s=new Map,a=t=>{const n=t.url.replace(/\/$/,"");s.get(n)?.close(1e3,"Reverse tunnel reconnecting.");const i=Rr(e,{tunnelUrl:jr(t.url),nodeId:r,port:()=>e.nodeAgentState.currentListenerPort,token:o,keyId:t.keyId,secret:t.secret});return s.set(n,i),i.on("close",()=>{s.get(n)===i&&s.delete(n)}),i};return{connect:a,connectConfigured:()=>{const n=function(e){const t=e.controlPlaneTunnelUrl||process.env.TASK_HANDOFF_CONTROL_PLANE_TUNNEL_URL;if(t)return t;const n=process.env.TASK_HANDOFF_CONTROL_PLANE_URL;if(!n)return;const r=new URL("/api/node-agent/tunnel",n);return r.protocol="https:"===r.protocol?"wss:":"ws:",r.toString()}(t);if(n){const a=i.reverseTunnelSecret(process.env.TASK_HANDOFF_CONTROL_PLANE_URL,t.remoteSecret||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_SECRET,t.remoteKeyId||process.env.TASK_HANDOFF_NODE_AGENT_REMOTE_KEY_ID),c=(process.env.TASK_HANDOFF_CONTROL_PLANE_URL||n).replace(/\/$/,"");s.get(c)?.close(1e3,"Reverse tunnel reconnecting.");const d=Rr(e,{tunnelUrl:n,nodeId:r,port:()=>e.nodeAgentState.currentListenerPort,token:o,keyId:a?.keyId,secret:a?.secret});s.set(c,d)}for(const e of i.configuredRemoteControlPlanes())e.url&&!1!==e.active&&a({url:e.url,keyId:e.keyId,secret:e.secret})},closeAll:()=>{for(const e of s.values())e.close(1001,"Node agent shutting down.");s.clear()}}}(s,i,o,new Gn(o).resolveNodeId(e.nodeId||process.env.TASK_HANDOFF_NODE_ID));s.decorate("nodeAgentReverseTunnels",a);const u=new Or({app:s,state:s.nodeAgentState,settings:d,config:l,source:c?"persisted":"bootstrap"});let p;s.decorate("nodeAgentListenerManager",u),function(e){let t=!1;const n=async n=>{if(!t){t=!0,a.closeAll(),p?.close();try{await e.close()}finally{process.exitCode=n?0:process.exitCode}}},r=()=>{n("SIGINT")},o=()=>{n("SIGTERM")};process.once("SIGINT",r),process.once("SIGTERM",o),e.addHook("onClose",async()=>{process.off("SIGINT",r),process.off("SIGTERM",o)})}(s),s.addHook("onClose",async()=>{p?.close(),m.release()});try{await s.ready();const i=e.ipcPath||process.env.TASK_HANDOFF_NODE_AGENT_IPC_PATH||an(o.dataDir);p=await async function(e,o){!function(e,r=process.platform){if("win32"!==r){t.mkdirSync(n.dirname(e),{recursive:!0,mode:448});try{t.chmodSync(n.dirname(e),448)}catch{}try{t.unlinkSync(e)}catch(e){if("ENOENT"!==e.code)throw e}}}(o);const i=r.createServer((t,n)=>{e.server.emit("request",t,n)});return i.on("upgrade",(t,n,r)=>{e.server.emit("upgrade",t,n,r)}),await new Promise((e,t)=>{i.once("error",t),i.listen(o,()=>{i.off("error",t),e()})}),i}(s,i),await u.start(),a.connectConfigured(),await(s.nodeAgentRestoreLocalInstances?.())}catch(e){throw await s.close(),e}}catch(e){throw m.release(),e}}function Mr(e){const t=Number(e);if(!Number.isInteger(t)||t<=0||t>65535)throw new Error("Port must be a valid TCP port.");return t}function Fr(e){if("local-ipc"!==e&&"local-loopback"!==e)throw new Error("Connection mode must be local-ipc or local-loopback.");return e}(async function(){const r=new e.Command;r.name("task-handoff-node-agent").description("Run a TaskHandoff node agent.").version(function(){try{return JSON.parse(t.readFileSync(n.resolve(__dirname,"..","package.json"),"utf8")).version||"unknown"}catch{return"unknown"}}()).option("--host <host>","Node agent host",process.env.TASK_HANDOFF_NODE_AGENT_HOST||"127.0.0.1").option("-p, --port <port>","Node agent port",Mr,Number(process.env.TASK_HANDOFF_NODE_AGENT_PORT)||8091).option("--data-dir <path>","Node agent data directory").option("--token <token>","Bearer token required by the control plane").option("--connection-mode <mode>","Local control connection mode",Fr).option("--ipc-path <path>","Unix socket path used by local-ipc mode").option("--control-plane-tunnel-url <url>","Control plane reverse WebSocket tunnel URL").action(async e=>{await Pr({host:e.host,port:e.port,dataDir:e.dataDir,token:e.token,connectionMode:e.connectionMode,ipcPath:e.ipcPath,controlPlaneTunnelUrl:e.controlPlaneTunnelUrl})}),await r.parseAsync(process.argv)})().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@task-handoff/node-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Prebuilt TaskHandoff node agent runtime.",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|
|
8
8
|
"task-handoff-node-agent": "bin/task-handoff-node-agent"
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"bin",
|
|
12
12
|
"dist",
|
|
13
|
-
"README.md"
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"NOTICE"
|
|
14
16
|
],
|
|
15
17
|
"engines": {
|
|
16
18
|
"node": ">=22.22.2"
|