@pellux/goodvibes-daemon-sdk 0.18.3
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/README.md +32 -0
- package/dist/api-router.d.ts +3 -0
- package/dist/api-router.d.ts.map +1 -0
- package/dist/api-router.js +13 -0
- package/dist/automation.d.ts +3 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +56 -0
- package/dist/channel-route-types.d.ts +78 -0
- package/dist/channel-route-types.d.ts.map +1 -0
- package/dist/channel-route-types.js +1 -0
- package/dist/channel-routes.d.ts +4 -0
- package/dist/channel-routes.d.ts.map +1 -0
- package/dist/channel-routes.js +264 -0
- package/dist/context.d.ts +207 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +1 -0
- package/dist/control-routes.d.ts +57 -0
- package/dist/control-routes.d.ts.map +1 -0
- package/dist/control-routes.js +120 -0
- package/dist/error-response.d.ts +10 -0
- package/dist/error-response.d.ts.map +1 -0
- package/dist/error-response.js +84 -0
- package/dist/http-policy.d.ts +33 -0
- package/dist/http-policy.d.ts.map +1 -0
- package/dist/http-policy.js +30 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/integration-route-types.d.ts +64 -0
- package/dist/integration-route-types.d.ts.map +1 -0
- package/dist/integration-route-types.js +1 -0
- package/dist/integration-routes.d.ts +4 -0
- package/dist/integration-routes.d.ts.map +1 -0
- package/dist/integration-routes.js +172 -0
- package/dist/knowledge-route-types.d.ts +114 -0
- package/dist/knowledge-route-types.d.ts.map +1 -0
- package/dist/knowledge-route-types.js +1 -0
- package/dist/knowledge-routes.d.ts +4 -0
- package/dist/knowledge-routes.d.ts.map +1 -0
- package/dist/knowledge-routes.js +582 -0
- package/dist/media-route-types.d.ts +66 -0
- package/dist/media-route-types.d.ts.map +1 -0
- package/dist/media-route-types.js +1 -0
- package/dist/media-routes.d.ts +4 -0
- package/dist/media-routes.d.ts.map +1 -0
- package/dist/media-routes.js +328 -0
- package/dist/operator.d.ts +3 -0
- package/dist/operator.d.ts.map +1 -0
- package/dist/operator.js +395 -0
- package/dist/remote-routes.d.ts +40 -0
- package/dist/remote-routes.d.ts.map +1 -0
- package/dist/remote-routes.js +238 -0
- package/dist/remote.d.ts +3 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +35 -0
- package/dist/route-helpers.d.ts +11 -0
- package/dist/route-helpers.d.ts.map +1 -0
- package/dist/route-helpers.js +54 -0
- package/dist/runtime-automation-routes.d.ts +4 -0
- package/dist/runtime-automation-routes.d.ts.map +1 -0
- package/dist/runtime-automation-routes.js +165 -0
- package/dist/runtime-route-types.d.ts +240 -0
- package/dist/runtime-route-types.d.ts.map +1 -0
- package/dist/runtime-route-types.js +1 -0
- package/dist/runtime-routes.d.ts +5 -0
- package/dist/runtime-routes.d.ts.map +1 -0
- package/dist/runtime-routes.js +8 -0
- package/dist/runtime-session-routes.d.ts +4 -0
- package/dist/runtime-session-routes.d.ts.map +1 -0
- package/dist/runtime-session-routes.js +387 -0
- package/dist/sessions.d.ts +3 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +37 -0
- package/dist/system-route-types.d.ts +124 -0
- package/dist/system-route-types.d.ts.map +1 -0
- package/dist/system-route-types.js +1 -0
- package/dist/system-routes.d.ts +4 -0
- package/dist/system-routes.d.ts.map +1 -0
- package/dist/system-routes.js +327 -0
- package/dist/tasks.d.ts +3 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +22 -0
- package/dist/telemetry-routes.d.ts +44 -0
- package/dist/telemetry-routes.d.ts.map +1 -0
- package/dist/telemetry-routes.js +227 -0
- package/package.json +42 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { jsonErrorResponse } from './error-response.js';
|
|
2
|
+
import { serializableJsonResponse } from './route-helpers.js';
|
|
3
|
+
export function createDaemonRemoteRouteHandlers(context) {
|
|
4
|
+
return {
|
|
5
|
+
getRemotePairRequests: () => Response.json({ requests: context.distributedRuntime.listPairRequests() }),
|
|
6
|
+
approveRemotePairRequest: async (requestId, request) => handleApproveRemotePairRequest(context, requestId, request),
|
|
7
|
+
rejectRemotePairRequest: async (requestId, request) => handleRejectRemotePairRequest(context, requestId, request),
|
|
8
|
+
getRemotePeers: () => Response.json({ peers: context.distributedRuntime.listPeers() }),
|
|
9
|
+
rotateRemotePeerToken: async (peerId, request) => handleRotateRemotePeerToken(context, peerId, request),
|
|
10
|
+
revokeRemotePeerToken: async (peerId, request) => handleRevokeRemotePeerToken(context, peerId, request),
|
|
11
|
+
disconnectRemotePeer: async (peerId, request) => handleDisconnectRemotePeer(context, peerId, request),
|
|
12
|
+
getRemoteWork: () => Response.json({ work: context.distributedRuntime.listWork() }),
|
|
13
|
+
invokeRemotePeer: async (peerId, request) => handleInvokeRemotePeer(context, peerId, request),
|
|
14
|
+
cancelRemoteWork: async (workId, request) => handleCancelRemoteWork(context, workId, request),
|
|
15
|
+
getRemoteNodeHostContract: () => serializableJsonResponse({ contract: context.distributedRuntime.getNodeHostContract() }),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export async function handleRemotePairRequest(context, req) {
|
|
19
|
+
const body = await context.parseJsonBody(req);
|
|
20
|
+
if (body instanceof Response)
|
|
21
|
+
return body;
|
|
22
|
+
const peerKind = body.peerKind === 'device' ? 'device' : 'node';
|
|
23
|
+
const label = typeof body.label === 'string' ? body.label.trim() : '';
|
|
24
|
+
if (!label) {
|
|
25
|
+
return Response.json({ error: 'Missing remote peer label' }, { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
const created = await context.distributedRuntime.requestPairing({
|
|
28
|
+
peerKind,
|
|
29
|
+
requestedId: typeof body.requestedId === 'string' ? body.requestedId : undefined,
|
|
30
|
+
label,
|
|
31
|
+
platform: typeof body.platform === 'string' ? body.platform : undefined,
|
|
32
|
+
deviceFamily: typeof body.deviceFamily === 'string' ? body.deviceFamily : undefined,
|
|
33
|
+
version: typeof body.version === 'string' ? body.version : undefined,
|
|
34
|
+
clientMode: typeof body.clientMode === 'string' ? body.clientMode : undefined,
|
|
35
|
+
capabilities: Array.isArray(body.capabilities) ? body.capabilities.filter((value) => typeof value === 'string') : [],
|
|
36
|
+
commands: Array.isArray(body.commands) ? body.commands.filter((value) => typeof value === 'string') : [],
|
|
37
|
+
metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
|
|
38
|
+
requestedBy: 'remote',
|
|
39
|
+
remoteAddress: req.headers.get('x-forwarded-for') ?? undefined,
|
|
40
|
+
ttlMs: typeof body.ttlMs === 'number' ? body.ttlMs : undefined,
|
|
41
|
+
});
|
|
42
|
+
return Response.json(created, { status: 201 });
|
|
43
|
+
}
|
|
44
|
+
export async function handleRemotePairVerify(context, req) {
|
|
45
|
+
const body = await context.parseJsonBody(req);
|
|
46
|
+
if (body instanceof Response)
|
|
47
|
+
return body;
|
|
48
|
+
const requestId = typeof body.requestId === 'string' ? body.requestId : '';
|
|
49
|
+
const challenge = typeof body.challenge === 'string' ? body.challenge : '';
|
|
50
|
+
if (!requestId || !challenge) {
|
|
51
|
+
return Response.json({ error: 'Missing requestId or challenge' }, { status: 400 });
|
|
52
|
+
}
|
|
53
|
+
const verified = await context.distributedRuntime.verifyPairRequest(requestId, challenge, {
|
|
54
|
+
remoteAddress: req.headers.get('x-forwarded-for') ?? undefined,
|
|
55
|
+
metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
|
|
56
|
+
});
|
|
57
|
+
return verified
|
|
58
|
+
? Response.json(verified)
|
|
59
|
+
: Response.json({ error: 'Pair request not approved, expired, or invalid' }, { status: 404 });
|
|
60
|
+
}
|
|
61
|
+
export async function handleRemotePeerHeartbeat(context, req) {
|
|
62
|
+
const auth = await context.requireRemotePeer(req, 'remote:heartbeat');
|
|
63
|
+
if (auth instanceof Response)
|
|
64
|
+
return auth;
|
|
65
|
+
const body = await context.parseJsonBody(req);
|
|
66
|
+
if (body instanceof Response)
|
|
67
|
+
return body;
|
|
68
|
+
const peer = await context.distributedRuntime.heartbeatPeer(auth, {
|
|
69
|
+
remoteAddress: req.headers.get('x-forwarded-for') ?? undefined,
|
|
70
|
+
capabilities: Array.isArray(body.capabilities) ? body.capabilities.filter((value) => typeof value === 'string') : undefined,
|
|
71
|
+
commands: Array.isArray(body.commands) ? body.commands.filter((value) => typeof value === 'string') : undefined,
|
|
72
|
+
version: typeof body.version === 'string' ? body.version : undefined,
|
|
73
|
+
clientMode: typeof body.clientMode === 'string' ? body.clientMode : undefined,
|
|
74
|
+
metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
|
|
75
|
+
});
|
|
76
|
+
return Response.json({ peer });
|
|
77
|
+
}
|
|
78
|
+
export async function handleRemotePeerWorkPull(context, req) {
|
|
79
|
+
const auth = await context.requireRemotePeer(req, 'remote:pull');
|
|
80
|
+
if (auth instanceof Response)
|
|
81
|
+
return auth;
|
|
82
|
+
const body = await context.parseJsonBody(req);
|
|
83
|
+
if (body instanceof Response)
|
|
84
|
+
return body;
|
|
85
|
+
const work = await context.distributedRuntime.claimWork(auth, {
|
|
86
|
+
maxItems: typeof body.maxItems === 'number' ? body.maxItems : undefined,
|
|
87
|
+
leaseMs: typeof body.leaseMs === 'number' ? body.leaseMs : undefined,
|
|
88
|
+
});
|
|
89
|
+
return Response.json({ work });
|
|
90
|
+
}
|
|
91
|
+
export async function handleRemotePeerWorkComplete(context, workId, req) {
|
|
92
|
+
const auth = await context.requireRemotePeer(req, 'remote:complete');
|
|
93
|
+
if (auth instanceof Response)
|
|
94
|
+
return auth;
|
|
95
|
+
const body = await context.parseJsonBody(req);
|
|
96
|
+
if (body instanceof Response)
|
|
97
|
+
return body;
|
|
98
|
+
const work = await context.distributedRuntime.completeWork(auth, workId, {
|
|
99
|
+
status: body.status === 'failed' || body.status === 'cancelled' ? body.status : body.status === 'completed' ? 'completed' : undefined,
|
|
100
|
+
result: body.result,
|
|
101
|
+
error: typeof body.error === 'string' ? body.error : undefined,
|
|
102
|
+
metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
|
|
103
|
+
});
|
|
104
|
+
return work
|
|
105
|
+
? Response.json({ work })
|
|
106
|
+
: Response.json({ error: 'Unknown or unclaimed remote work item' }, { status: 404 });
|
|
107
|
+
}
|
|
108
|
+
function operatorActor(context, req) {
|
|
109
|
+
return context.authToken ? 'shared-token' : context.requireAuthenticatedSession(req)?.username ?? 'operator';
|
|
110
|
+
}
|
|
111
|
+
async function handleApproveRemotePairRequest(context, requestId, req) {
|
|
112
|
+
const admin = context.requireAdmin(req);
|
|
113
|
+
if (admin)
|
|
114
|
+
return admin;
|
|
115
|
+
const body = await context.parseJsonBody(req);
|
|
116
|
+
if (body instanceof Response)
|
|
117
|
+
return body;
|
|
118
|
+
const approved = await context.distributedRuntime.approvePairRequest(requestId, {
|
|
119
|
+
actor: operatorActor(context, req),
|
|
120
|
+
note: typeof body.note === 'string' ? body.note : undefined,
|
|
121
|
+
label: typeof body.label === 'string' ? body.label : undefined,
|
|
122
|
+
metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
|
|
123
|
+
});
|
|
124
|
+
return approved
|
|
125
|
+
? Response.json(approved)
|
|
126
|
+
: Response.json({ error: 'Unknown remote pair request' }, { status: 404 });
|
|
127
|
+
}
|
|
128
|
+
async function handleRejectRemotePairRequest(context, requestId, req) {
|
|
129
|
+
const admin = context.requireAdmin(req);
|
|
130
|
+
if (admin)
|
|
131
|
+
return admin;
|
|
132
|
+
const body = await context.parseJsonBody(req);
|
|
133
|
+
if (body instanceof Response)
|
|
134
|
+
return body;
|
|
135
|
+
const rejected = await context.distributedRuntime.rejectPairRequest(requestId, {
|
|
136
|
+
actor: operatorActor(context, req),
|
|
137
|
+
note: typeof body.note === 'string' ? body.note : undefined,
|
|
138
|
+
});
|
|
139
|
+
return rejected
|
|
140
|
+
? Response.json(rejected)
|
|
141
|
+
: Response.json({ error: 'Unknown remote pair request' }, { status: 404 });
|
|
142
|
+
}
|
|
143
|
+
async function handleRotateRemotePeerToken(context, peerId, req) {
|
|
144
|
+
const admin = context.requireAdmin(req);
|
|
145
|
+
if (admin)
|
|
146
|
+
return admin;
|
|
147
|
+
const body = await context.parseJsonBody(req);
|
|
148
|
+
if (body instanceof Response)
|
|
149
|
+
return body;
|
|
150
|
+
const rotated = await context.distributedRuntime.rotatePeerToken(peerId, {
|
|
151
|
+
actor: operatorActor(context, req),
|
|
152
|
+
label: typeof body.label === 'string' ? body.label : undefined,
|
|
153
|
+
scopes: Array.isArray(body.scopes) ? body.scopes.filter((value) => typeof value === 'string') : undefined,
|
|
154
|
+
});
|
|
155
|
+
return rotated
|
|
156
|
+
? Response.json(rotated)
|
|
157
|
+
: Response.json({ error: 'Unknown distributed peer' }, { status: 404 });
|
|
158
|
+
}
|
|
159
|
+
async function handleRevokeRemotePeerToken(context, peerId, req) {
|
|
160
|
+
const admin = context.requireAdmin(req);
|
|
161
|
+
if (admin)
|
|
162
|
+
return admin;
|
|
163
|
+
const body = await context.parseJsonBody(req);
|
|
164
|
+
if (body instanceof Response)
|
|
165
|
+
return body;
|
|
166
|
+
const peer = await context.distributedRuntime.revokePeerToken(peerId, {
|
|
167
|
+
actor: operatorActor(context, req),
|
|
168
|
+
tokenId: typeof body.tokenId === 'string' ? body.tokenId : undefined,
|
|
169
|
+
note: typeof body.note === 'string' ? body.note : undefined,
|
|
170
|
+
});
|
|
171
|
+
return peer
|
|
172
|
+
? Response.json({ peer })
|
|
173
|
+
: Response.json({ error: 'Unknown distributed peer' }, { status: 404 });
|
|
174
|
+
}
|
|
175
|
+
async function handleDisconnectRemotePeer(context, peerId, req) {
|
|
176
|
+
const admin = context.requireAdmin(req);
|
|
177
|
+
if (admin)
|
|
178
|
+
return admin;
|
|
179
|
+
const body = await context.parseJsonBody(req);
|
|
180
|
+
if (body instanceof Response)
|
|
181
|
+
return body;
|
|
182
|
+
const peer = await context.distributedRuntime.disconnectPeer(peerId, {
|
|
183
|
+
actor: operatorActor(context, req),
|
|
184
|
+
note: typeof body.note === 'string' ? body.note : undefined,
|
|
185
|
+
requeueClaimedWork: typeof body.requeueClaimedWork === 'boolean' ? body.requeueClaimedWork : undefined,
|
|
186
|
+
});
|
|
187
|
+
return peer
|
|
188
|
+
? Response.json({ peer })
|
|
189
|
+
: Response.json({ error: 'Unknown distributed peer' }, { status: 404 });
|
|
190
|
+
}
|
|
191
|
+
async function handleInvokeRemotePeer(context, peerId, req) {
|
|
192
|
+
const admin = context.requireAdmin(req);
|
|
193
|
+
if (admin)
|
|
194
|
+
return admin;
|
|
195
|
+
const body = await context.parseJsonBody(req);
|
|
196
|
+
if (body instanceof Response)
|
|
197
|
+
return body;
|
|
198
|
+
const command = typeof body.command === 'string' ? body.command.trim() : '';
|
|
199
|
+
if (!command) {
|
|
200
|
+
return Response.json({ error: 'Missing remote invoke command' }, { status: 400 });
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const invoked = await context.distributedRuntime.invokePeer({
|
|
204
|
+
peerId,
|
|
205
|
+
command,
|
|
206
|
+
payload: body.payload,
|
|
207
|
+
priority: body.priority === 'high' || body.priority === 'default' ? body.priority : 'normal',
|
|
208
|
+
actor: operatorActor(context, req),
|
|
209
|
+
waitMs: typeof body.waitMs === 'number' ? body.waitMs : undefined,
|
|
210
|
+
timeoutMs: typeof body.timeoutMs === 'number' ? body.timeoutMs : undefined,
|
|
211
|
+
sessionId: typeof body.sessionId === 'string' ? body.sessionId : undefined,
|
|
212
|
+
routeId: typeof body.routeId === 'string' ? body.routeId : undefined,
|
|
213
|
+
automationRunId: typeof body.automationRunId === 'string' ? body.automationRunId : undefined,
|
|
214
|
+
automationJobId: typeof body.automationJobId === 'string' ? body.automationJobId : undefined,
|
|
215
|
+
approvalId: typeof body.approvalId === 'string' ? body.approvalId : undefined,
|
|
216
|
+
metadata: typeof body.metadata === 'object' && body.metadata !== null ? body.metadata : {},
|
|
217
|
+
});
|
|
218
|
+
return Response.json(invoked, { status: 202 });
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
return jsonErrorResponse(error, { status: 404 });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async function handleCancelRemoteWork(context, workId, req) {
|
|
225
|
+
const admin = context.requireAdmin(req);
|
|
226
|
+
if (admin)
|
|
227
|
+
return admin;
|
|
228
|
+
const body = await context.parseJsonBody(req);
|
|
229
|
+
if (body instanceof Response)
|
|
230
|
+
return body;
|
|
231
|
+
const work = await context.distributedRuntime.cancelWork(workId, {
|
|
232
|
+
actor: operatorActor(context, req),
|
|
233
|
+
reason: typeof body.reason === 'string' ? body.reason : undefined,
|
|
234
|
+
});
|
|
235
|
+
return work
|
|
236
|
+
? Response.json({ work })
|
|
237
|
+
: Response.json({ error: 'Unknown remote work item' }, { status: 404 });
|
|
238
|
+
}
|
package/dist/remote.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DaemonApiRouteHandlers } from './context.js';
|
|
2
|
+
export declare function dispatchRemoteRoutes(req: Request, handlers: Pick<DaemonApiRouteHandlers, 'getRemote' | 'getRemotePairRequests' | 'approveRemotePairRequest' | 'rejectRemotePairRequest' | 'getRemotePeers' | 'rotateRemotePeerToken' | 'revokeRemotePeerToken' | 'disconnectRemotePeer' | 'getRemoteWork' | 'invokeRemotePeer' | 'cancelRemoteWork'>): Promise<Response | null>;
|
|
3
|
+
//# sourceMappingURL=remote.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3D,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,IAAI,CACZ,sBAAsB,EACpB,WAAW,GACX,uBAAuB,GACvB,0BAA0B,GAC1B,yBAAyB,GACzB,gBAAgB,GAChB,uBAAuB,GACvB,uBAAuB,GACvB,sBAAsB,GACtB,eAAe,GACf,kBAAkB,GAClB,kBAAkB,CACrB,GACA,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA6B1B"}
|
package/dist/remote.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export async function dispatchRemoteRoutes(req, handlers) {
|
|
2
|
+
const url = new URL(req.url);
|
|
3
|
+
const { pathname } = url;
|
|
4
|
+
const method = req.method;
|
|
5
|
+
if (pathname === '/api/remote' && method === 'GET')
|
|
6
|
+
return handlers.getRemote();
|
|
7
|
+
if (pathname === '/api/remote/pair/requests' && method === 'GET')
|
|
8
|
+
return handlers.getRemotePairRequests();
|
|
9
|
+
const pairActionMatch = pathname.match(/^\/api\/remote\/pair\/requests\/([^/]+)\/(approve|reject)$/);
|
|
10
|
+
if (pairActionMatch && method === 'POST') {
|
|
11
|
+
return pairActionMatch[2] === 'approve'
|
|
12
|
+
? handlers.approveRemotePairRequest(pairActionMatch[1], req)
|
|
13
|
+
: handlers.rejectRemotePairRequest(pairActionMatch[1], req);
|
|
14
|
+
}
|
|
15
|
+
if (pathname === '/api/remote/peers' && method === 'GET')
|
|
16
|
+
return handlers.getRemotePeers();
|
|
17
|
+
const peerTokenActionMatch = pathname.match(/^\/api\/remote\/peers\/([^/]+)\/token\/(rotate|revoke)$/);
|
|
18
|
+
if (peerTokenActionMatch && method === 'POST') {
|
|
19
|
+
return peerTokenActionMatch[2] === 'rotate'
|
|
20
|
+
? handlers.rotateRemotePeerToken(peerTokenActionMatch[1], req)
|
|
21
|
+
: handlers.revokeRemotePeerToken(peerTokenActionMatch[1], req);
|
|
22
|
+
}
|
|
23
|
+
const peerDisconnectMatch = pathname.match(/^\/api\/remote\/peers\/([^/]+)\/disconnect$/);
|
|
24
|
+
if (peerDisconnectMatch && method === 'POST')
|
|
25
|
+
return handlers.disconnectRemotePeer(peerDisconnectMatch[1], req);
|
|
26
|
+
const peerInvokeMatch = pathname.match(/^\/api\/remote\/peers\/([^/]+)\/invoke$/);
|
|
27
|
+
if (peerInvokeMatch && method === 'POST')
|
|
28
|
+
return handlers.invokeRemotePeer(peerInvokeMatch[1], req);
|
|
29
|
+
if (pathname === '/api/remote/work' && method === 'GET')
|
|
30
|
+
return handlers.getRemoteWork();
|
|
31
|
+
const workCancelMatch = pathname.match(/^\/api\/remote\/work\/([^/]+)\/cancel$/);
|
|
32
|
+
if (workCancelMatch && method === 'POST')
|
|
33
|
+
return handlers.cancelRemoteWork(workCancelMatch[1], req);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type JsonRecord = Record<string, unknown>;
|
|
2
|
+
export declare function isJsonRecord(value: unknown): value is JsonRecord;
|
|
3
|
+
export declare function toSerializableJson(value: unknown, stack?: Map<object, string>, path?: string): unknown;
|
|
4
|
+
export declare function serializableJsonResponse(body: unknown, init?: ResponseInit): Response;
|
|
5
|
+
export declare function scopeMatches(granted: string, required: string): boolean;
|
|
6
|
+
export declare function missingScopes(grantedScopes: readonly string[] | undefined, requiredScopes: readonly string[]): string[];
|
|
7
|
+
export type ChannelLifecycleAction = 'inspect' | 'setup' | 'retest' | 'connect' | 'disconnect' | 'start' | 'stop' | 'login' | 'logout' | 'wait_login';
|
|
8
|
+
export type ChannelConversationKind = 'direct' | 'group' | 'channel' | 'thread' | 'service';
|
|
9
|
+
export declare function readChannelLifecycleAction(value: unknown): ChannelLifecycleAction | null;
|
|
10
|
+
export declare function readChannelConversationKind(value: unknown): ChannelConversationKind | null;
|
|
11
|
+
//# sourceMappingURL=route-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../src/route-helpers.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,sBAA4B,EAAE,IAAI,SAAM,GAAG,OAAO,CAiBzG;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,CAErF;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMvE;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAAE,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAGvH;AAED,MAAM,MAAM,sBAAsB,GAC9B,SAAS,GACT,OAAO,GACP,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,OAAO,GACP,MAAM,GACN,OAAO,GACP,QAAQ,GACR,YAAY,CAAC;AAEjB,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5F,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,GAAG,IAAI,CAaxF;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI,CAI1F"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function isJsonRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function toSerializableJson(value, stack = new Map(), path = '$') {
|
|
5
|
+
if (!value || typeof value !== 'object')
|
|
6
|
+
return value;
|
|
7
|
+
const prior = stack.get(value);
|
|
8
|
+
if (prior) {
|
|
9
|
+
return { $ref: prior };
|
|
10
|
+
}
|
|
11
|
+
const next = new Map(stack);
|
|
12
|
+
next.set(value, path);
|
|
13
|
+
if (Array.isArray(value)) {
|
|
14
|
+
return value.map((entry, index) => toSerializableJson(entry, next, `${path}[${index}]`));
|
|
15
|
+
}
|
|
16
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
17
|
+
key,
|
|
18
|
+
toSerializableJson(entry, next, `${path}.${key}`),
|
|
19
|
+
]));
|
|
20
|
+
}
|
|
21
|
+
export function serializableJsonResponse(body, init) {
|
|
22
|
+
return Response.json(toSerializableJson(body), init);
|
|
23
|
+
}
|
|
24
|
+
export function scopeMatches(granted, required) {
|
|
25
|
+
if (granted === '*' || granted === required)
|
|
26
|
+
return true;
|
|
27
|
+
if (granted.endsWith(':*')) {
|
|
28
|
+
return required.startsWith(granted.slice(0, -1));
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
export function missingScopes(grantedScopes, requiredScopes) {
|
|
33
|
+
const granted = grantedScopes ?? [];
|
|
34
|
+
return requiredScopes.filter((required) => !granted.some((entry) => scopeMatches(entry, required)));
|
|
35
|
+
}
|
|
36
|
+
export function readChannelLifecycleAction(value) {
|
|
37
|
+
return value === 'inspect'
|
|
38
|
+
|| value === 'setup'
|
|
39
|
+
|| value === 'retest'
|
|
40
|
+
|| value === 'connect'
|
|
41
|
+
|| value === 'disconnect'
|
|
42
|
+
|| value === 'start'
|
|
43
|
+
|| value === 'stop'
|
|
44
|
+
|| value === 'login'
|
|
45
|
+
|| value === 'logout'
|
|
46
|
+
|| value === 'wait_login'
|
|
47
|
+
? value
|
|
48
|
+
: null;
|
|
49
|
+
}
|
|
50
|
+
export function readChannelConversationKind(value) {
|
|
51
|
+
return value === 'direct' || value === 'group' || value === 'channel' || value === 'thread' || value === 'service'
|
|
52
|
+
? value
|
|
53
|
+
: null;
|
|
54
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DaemonApiRouteHandlers } from './context.js';
|
|
2
|
+
import type { DaemonRuntimeRouteContext } from './runtime-route-types.js';
|
|
3
|
+
export declare function createDaemonRuntimeAutomationRouteHandlers(context: DaemonRuntimeRouteContext): Pick<DaemonApiRouteHandlers, 'getAutomationJobs' | 'postAutomationJob' | 'getAutomationRuns' | 'getAutomationRun' | 'getAutomationHeartbeat' | 'postAutomationHeartbeat' | 'automationRunAction' | 'patchAutomationJob' | 'deleteAutomationJob' | 'setAutomationJobEnabled' | 'runAutomationJobNow' | 'getSchedules' | 'postSchedule' | 'deleteSchedule' | 'setScheduleEnabled' | 'runScheduleNow'>;
|
|
4
|
+
//# sourceMappingURL=runtime-automation-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-automation-routes.d.ts","sourceRoot":"","sources":["../src/runtime-automation-routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAG1E,wBAAgB,0CAA0C,CACxD,OAAO,EAAE,yBAAyB,GACjC,IAAI,CACL,sBAAsB,EACpB,mBAAmB,GACnB,mBAAmB,GACnB,mBAAmB,GACnB,kBAAkB,GAClB,wBAAwB,GACxB,yBAAyB,GACzB,qBAAqB,GACrB,oBAAoB,GACpB,qBAAqB,GACrB,yBAAyB,GACzB,qBAAqB,GACrB,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,oBAAoB,GACpB,gBAAgB,CACnB,CAmBA"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { jsonErrorResponse } from './error-response.js';
|
|
2
|
+
export function createDaemonRuntimeAutomationRouteHandlers(context) {
|
|
3
|
+
return {
|
|
4
|
+
getAutomationJobs: () => Response.json({ jobs: context.automationManager.listJobs() }),
|
|
5
|
+
postAutomationJob: async (request) => handlePostSchedule(context, request),
|
|
6
|
+
getAutomationRuns: () => Response.json({ runs: context.automationManager.listRuns() }),
|
|
7
|
+
getAutomationRun: (runId) => handleGetAutomationRun(context, runId),
|
|
8
|
+
getAutomationHeartbeat: () => Response.json({ pending: [] }),
|
|
9
|
+
postAutomationHeartbeat: async (request) => handlePostAutomationHeartbeat(context, request),
|
|
10
|
+
automationRunAction: async (runId, action, request) => handleAutomationRunAction(context, runId, action, request),
|
|
11
|
+
patchAutomationJob: async (jobId, request) => handlePatchSchedule(context, jobId, request),
|
|
12
|
+
deleteAutomationJob: async (jobId) => handleDeleteSchedule(context, jobId),
|
|
13
|
+
setAutomationJobEnabled: async (jobId, enabled) => handleSetScheduleEnabled(context, jobId, enabled),
|
|
14
|
+
runAutomationJobNow: async (jobId) => handleRunScheduleNow(context, jobId),
|
|
15
|
+
getSchedules: () => handleGetSchedules(context),
|
|
16
|
+
postSchedule: (request) => handlePostSchedule(context, request),
|
|
17
|
+
deleteSchedule: async (scheduleId) => handleDeleteSchedule(context, scheduleId),
|
|
18
|
+
setScheduleEnabled: (scheduleId, enabled) => handleSetScheduleEnabled(context, scheduleId, enabled),
|
|
19
|
+
runScheduleNow: (scheduleId) => handleRunScheduleNow(context, scheduleId),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function handleGetSchedules(context) {
|
|
23
|
+
const jobs = context.automationManager.listJobs();
|
|
24
|
+
const runs = context.automationManager.listRuns().slice(0, 50);
|
|
25
|
+
return Response.json({ jobs, runs });
|
|
26
|
+
}
|
|
27
|
+
async function handlePostSchedule(context, req) {
|
|
28
|
+
const body = await context.parseJsonBody(req);
|
|
29
|
+
if (body instanceof Response)
|
|
30
|
+
return body;
|
|
31
|
+
const prompt = typeof body.prompt === 'string' ? body.prompt.trim() : undefined;
|
|
32
|
+
const kind = typeof body.kind === 'string' ? body.kind : 'cron';
|
|
33
|
+
const cron = typeof body.cron === 'string' ? body.cron : undefined;
|
|
34
|
+
const every = typeof body.every === 'string' ? body.every : undefined;
|
|
35
|
+
const at = typeof body.at === 'string' || typeof body.at === 'number' ? body.at : undefined;
|
|
36
|
+
const timezone = typeof body.timezone === 'string' ? body.timezone : undefined;
|
|
37
|
+
if (!prompt) {
|
|
38
|
+
return Response.json({ error: 'Missing required field: prompt (string)' }, { status: 400 });
|
|
39
|
+
}
|
|
40
|
+
if (prompt.length > 10_000) {
|
|
41
|
+
return Response.json({ error: 'prompt exceeds maximum length of 10000 characters' }, { status: 400 });
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const fallbackModelsSource = body.fallbackModels ?? body.fallbacks;
|
|
45
|
+
const fallbackModels = Array.isArray(fallbackModelsSource)
|
|
46
|
+
? fallbackModelsSource.filter((value) => typeof value === 'string')
|
|
47
|
+
: undefined;
|
|
48
|
+
const schedule = kind === 'every'
|
|
49
|
+
? context.normalizeEverySchedule(every ?? '')
|
|
50
|
+
: kind === 'at'
|
|
51
|
+
? context.normalizeAtSchedule(typeof at === 'number' ? at : Date.parse(String(at)))
|
|
52
|
+
: context.normalizeCronSchedule(cron ?? '', timezone, body.staggerMs ?? body.stagger);
|
|
53
|
+
const job = await context.automationManager.createJob({
|
|
54
|
+
name: typeof body.name === 'string' ? body.name : prompt.slice(0, 40),
|
|
55
|
+
prompt,
|
|
56
|
+
schedule,
|
|
57
|
+
description: prompt,
|
|
58
|
+
model: typeof body.model === 'string' ? body.model : undefined,
|
|
59
|
+
provider: typeof body.provider === 'string' ? body.provider : undefined,
|
|
60
|
+
fallbackModels,
|
|
61
|
+
template: typeof body.template === 'string' ? body.template : undefined,
|
|
62
|
+
target: typeof body.target === 'object' && body.target !== null ? body.target : undefined,
|
|
63
|
+
reasoningEffort: body.reasoningEffort,
|
|
64
|
+
thinking: typeof body.thinking === 'string' ? body.thinking : undefined,
|
|
65
|
+
wakeMode: body.wakeMode,
|
|
66
|
+
timeoutMs: typeof body.timeoutMs === 'number' ? body.timeoutMs : undefined,
|
|
67
|
+
toolAllowlist: Array.isArray(body.toolAllowlist) ? body.toolAllowlist.filter((value) => typeof value === 'string') : undefined,
|
|
68
|
+
autoApprove: typeof body.autoApprove === 'boolean' ? body.autoApprove : undefined,
|
|
69
|
+
allowUnsafeExternalContent: typeof body.allowUnsafeExternalContent === 'boolean' ? body.allowUnsafeExternalContent : undefined,
|
|
70
|
+
externalContentSource: body.externalContentSource,
|
|
71
|
+
lightContext: typeof body.lightContext === 'boolean' ? body.lightContext : undefined,
|
|
72
|
+
delivery: typeof body.delivery === 'object' && body.delivery !== null ? body.delivery : undefined,
|
|
73
|
+
failure: typeof body.failure === 'object' && body.failure !== null ? body.failure : undefined,
|
|
74
|
+
enabled: body.enabled !== false,
|
|
75
|
+
deleteAfterRun: typeof body.deleteAfterRun === 'boolean' ? body.deleteAfterRun : undefined,
|
|
76
|
+
});
|
|
77
|
+
return Response.json(job, { status: 201 });
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return jsonErrorResponse(e, { status: 400, fallbackMessage: 'Failed to create schedule' });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function handlePatchSchedule(context, id, req) {
|
|
84
|
+
const job = findAutomationJob(context, id);
|
|
85
|
+
if (!job)
|
|
86
|
+
return Response.json({ error: `Schedule not found: ${id}` }, { status: 404 });
|
|
87
|
+
const body = await context.parseJsonBody(req);
|
|
88
|
+
if (body instanceof Response)
|
|
89
|
+
return body;
|
|
90
|
+
try {
|
|
91
|
+
const updated = await context.automationManager.updateJob(job.id, body);
|
|
92
|
+
return updated
|
|
93
|
+
? Response.json(updated)
|
|
94
|
+
: Response.json({ error: `Schedule not found: ${id}` }, { status: 404 });
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return jsonErrorResponse(error, { status: 400, fallbackMessage: 'Failed to update schedule' });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function handleDeleteSchedule(context, id) {
|
|
101
|
+
const job = findAutomationJob(context, id);
|
|
102
|
+
if (!job)
|
|
103
|
+
return Response.json({ error: `Schedule not found: ${id}` }, { status: 404 });
|
|
104
|
+
await context.automationManager.removeJob(job.id);
|
|
105
|
+
return Response.json({ removed: true, id: job.id });
|
|
106
|
+
}
|
|
107
|
+
async function handleSetScheduleEnabled(context, id, enabled) {
|
|
108
|
+
const job = findAutomationJob(context, id);
|
|
109
|
+
if (!job)
|
|
110
|
+
return Response.json({ error: `Schedule not found: ${id}` }, { status: 404 });
|
|
111
|
+
const updated = await context.automationManager.setEnabled(job.id, enabled);
|
|
112
|
+
return Response.json(updated ?? { id: job.id, enabled });
|
|
113
|
+
}
|
|
114
|
+
async function handleRunScheduleNow(context, id) {
|
|
115
|
+
const job = findAutomationJob(context, id);
|
|
116
|
+
if (!job)
|
|
117
|
+
return Response.json({ error: `Schedule not found: ${id}` }, { status: 404 });
|
|
118
|
+
try {
|
|
119
|
+
const run = await context.automationManager.runNow(job.id);
|
|
120
|
+
return Response.json({ jobId: job.id, runId: run.id, agentId: run.agentId, status: run.status });
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
return jsonErrorResponse(e, { status: 500, fallbackMessage: 'Failed to run schedule' });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function handlePostAutomationHeartbeat(context, req) {
|
|
127
|
+
const body = await context.parseOptionalJsonBody(req);
|
|
128
|
+
if (body instanceof Response)
|
|
129
|
+
return body;
|
|
130
|
+
const result = await context.automationManager.triggerHeartbeat({
|
|
131
|
+
source: body && typeof body.source === 'string' ? body.source : 'api',
|
|
132
|
+
});
|
|
133
|
+
return Response.json(result);
|
|
134
|
+
}
|
|
135
|
+
async function handleAutomationRunAction(context, runId, action, req) {
|
|
136
|
+
if (action === 'cancel') {
|
|
137
|
+
const body = await context.parseOptionalJsonBody(req);
|
|
138
|
+
const reason = body instanceof Response
|
|
139
|
+
? 'operator-cancelled'
|
|
140
|
+
: body && typeof body.reason === 'string'
|
|
141
|
+
? body.reason
|
|
142
|
+
: 'operator-cancelled';
|
|
143
|
+
const run = await context.automationManager.cancelRun(runId, reason);
|
|
144
|
+
return run
|
|
145
|
+
? context.recordApiResponse(req, `/api/automation/runs/${runId}/${action}`, Response.json({ run }))
|
|
146
|
+
: context.recordApiResponse(req, `/api/automation/runs/${runId}/${action}`, Response.json({ error: 'Unknown automation run' }, { status: 404 }));
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const run = await context.automationManager.retryRun(runId);
|
|
150
|
+
return context.recordApiResponse(req, `/api/automation/runs/${runId}/${action}`, Response.json({ run }, { status: 202 }));
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return context.recordApiResponse(req, `/api/automation/runs/${runId}/${action}`, jsonErrorResponse(error, { status: 400, fallbackMessage: 'Failed to retry automation run' }));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function handleGetAutomationRun(context, runId) {
|
|
157
|
+
const run = context.automationManager.getRun(runId);
|
|
158
|
+
if (!run) {
|
|
159
|
+
return Response.json({ error: 'Unknown automation run' }, { status: 404 });
|
|
160
|
+
}
|
|
161
|
+
return Response.json({ run, deliveries: [] });
|
|
162
|
+
}
|
|
163
|
+
function findAutomationJob(context, id) {
|
|
164
|
+
return context.automationManager.listJobs().find((entry) => entry.id === id || entry.id.startsWith(id));
|
|
165
|
+
}
|