@pellux/goodvibes-daemon-sdk 0.30.2 → 0.33.0
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 +2 -2
- package/dist/api-router.d.ts +4 -2
- package/dist/api-router.d.ts.map +1 -1
- package/dist/api-router.js +18 -10
- package/dist/artifact-upload.d.ts +9 -9
- package/dist/artifact-upload.d.ts.map +1 -1
- package/dist/artifact-upload.js +27 -19
- package/dist/auth-helpers.d.ts +9 -0
- package/dist/auth-helpers.d.ts.map +1 -0
- package/dist/auth-helpers.js +10 -0
- package/dist/automation.js +10 -10
- package/dist/channel-route-types.d.ts +22 -23
- package/dist/channel-route-types.d.ts.map +1 -1
- package/dist/channel-routes.d.ts.map +1 -1
- package/dist/channel-routes.js +37 -42
- package/dist/context.d.ts +27 -27
- package/dist/context.d.ts.map +1 -1
- package/dist/control-routes.d.ts +12 -13
- package/dist/control-routes.d.ts.map +1 -1
- package/dist/control-routes.js +55 -26
- package/dist/error-response.d.ts +10 -3
- package/dist/error-response.d.ts.map +1 -1
- package/dist/error-response.js +102 -11
- package/dist/http-policy.d.ts +3 -4
- package/dist/http-policy.d.ts.map +1 -1
- package/dist/http-policy.js +2 -1
- package/dist/index.d.ts +11 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/integration-routes.d.ts +1 -1
- package/dist/integration-routes.d.ts.map +1 -1
- package/dist/integration-routes.js +72 -33
- package/dist/knowledge-refinement-routes.d.ts.map +1 -1
- package/dist/knowledge-refinement-routes.js +22 -15
- package/dist/knowledge-route-types.d.ts +16 -15
- package/dist/knowledge-route-types.d.ts.map +1 -1
- package/dist/knowledge-routes.d.ts.map +1 -1
- package/dist/knowledge-routes.js +144 -89
- package/dist/media-route-types.d.ts +2 -1
- package/dist/media-route-types.d.ts.map +1 -1
- package/dist/media-routes.d.ts.map +1 -1
- package/dist/media-routes.js +119 -55
- package/dist/operator.d.ts +16 -0
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +105 -61
- package/dist/otlp-protobuf.d.ts.map +1 -1
- package/dist/otlp-protobuf.js +28 -10
- package/dist/remote-routes.d.ts +9 -3
- package/dist/remote-routes.d.ts.map +1 -1
- package/dist/remote-routes.js +259 -163
- package/dist/route-helpers.d.ts +13 -2
- package/dist/route-helpers.d.ts.map +1 -1
- package/dist/route-helpers.js +38 -1
- package/dist/runtime-automation-routes.d.ts.map +1 -1
- package/dist/runtime-automation-routes.js +88 -54
- package/dist/runtime-route-types.d.ts +87 -93
- package/dist/runtime-route-types.d.ts.map +1 -1
- package/dist/runtime-session-routes.d.ts +6 -4
- package/dist/runtime-session-routes.d.ts.map +1 -1
- package/dist/runtime-session-routes.js +132 -88
- package/dist/sessions.js +3 -3
- package/dist/system-route-types.d.ts +25 -24
- package/dist/system-route-types.d.ts.map +1 -1
- package/dist/system-routes.d.ts +1 -1
- package/dist/system-routes.d.ts.map +1 -1
- package/dist/system-routes.js +126 -92
- package/dist/tasks.d.ts.map +1 -1
- package/dist/tasks.js +2 -0
- package/dist/telemetry-routes.d.ts +14 -14
- package/dist/telemetry-routes.d.ts.map +1 -1
- package/dist/telemetry-routes.js +54 -29
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @pellux/goodvibes-daemon-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Public GoodVibes daemon package for embeddable route contracts, dispatchers, handler builders, and daemon-side helpers.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Most applications should install `@pellux/goodvibes-sdk` and import `@pellux/goodvibes-sdk/daemon`. Install this package directly when you only need the daemon embedding subset.
|
|
6
6
|
|
|
7
7
|
Consumer import:
|
|
8
8
|
|
package/dist/api-router.d.ts
CHANGED
|
@@ -2,8 +2,10 @@ import type { DaemonApiRouteHandlers } from './context.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Optional extension dispatchers injected alongside the standard route set.
|
|
4
4
|
* Each dispatcher is tried in order after the built-in routes; the first
|
|
5
|
-
* non-null result wins.
|
|
6
|
-
*
|
|
5
|
+
* non-null result wins. Exceptions thrown by an extension propagate to the
|
|
6
|
+
* caller, matching built-in route behavior. Use this to wire companion-chat,
|
|
7
|
+
* provider, or other feature routes into the standalone daemon without
|
|
8
|
+
* modifying core route files.
|
|
7
9
|
*/
|
|
8
10
|
export type DaemonApiRouteExtension = (req: Request) => Promise<Response | null> | Response | null;
|
|
9
11
|
export declare function dispatchDaemonApiRoutes(req: Request, handlers: DaemonApiRouteHandlers, extensions?: readonly DaemonApiRouteExtension[]): Promise<Response | null>;
|
package/dist/api-router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-router.d.ts","sourceRoot":"","sources":["../src/api-router.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3D
|
|
1
|
+
{"version":3,"file":"api-router.d.ts","sourceRoot":"","sources":["../src/api-router.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEnG,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,sBAAsB,EAChC,UAAU,CAAC,EAAE,SAAS,uBAAuB,EAAE,GAC9C,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAkB1B"}
|
package/dist/api-router.js
CHANGED
|
@@ -4,18 +4,26 @@ import { dispatchRemoteRoutes } from './remote.js';
|
|
|
4
4
|
import { dispatchSessionRoutes } from './sessions.js';
|
|
5
5
|
import { dispatchTaskRoutes } from './tasks.js';
|
|
6
6
|
export async function dispatchDaemonApiRoutes(req, handlers, extensions) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
let result = await dispatchRemoteRoutes(req, handlers);
|
|
8
|
+
if (result !== null)
|
|
9
|
+
return result;
|
|
10
|
+
result = await dispatchOperatorRoutes(req, handlers);
|
|
11
|
+
if (result !== null)
|
|
12
|
+
return result;
|
|
13
|
+
result = await dispatchAutomationRoutes(req, handlers);
|
|
14
|
+
if (result !== null)
|
|
15
|
+
return result;
|
|
16
|
+
result = await dispatchSessionRoutes(req, handlers);
|
|
17
|
+
if (result !== null)
|
|
18
|
+
return result;
|
|
19
|
+
result = await dispatchTaskRoutes(req, handlers);
|
|
20
|
+
if (result !== null)
|
|
21
|
+
return result;
|
|
14
22
|
if (extensions) {
|
|
15
23
|
for (const extension of extensions) {
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
return
|
|
24
|
+
const extResult = await extension(req);
|
|
25
|
+
if (extResult !== null)
|
|
26
|
+
return extResult;
|
|
19
27
|
}
|
|
20
28
|
}
|
|
21
29
|
return null;
|
|
@@ -4,15 +4,15 @@ export interface ArtifactStoreUploadLike {
|
|
|
4
4
|
getMaxBytes?(): number;
|
|
5
5
|
createFromStream?(input: {
|
|
6
6
|
readonly stream: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array | Buffer | string> | Iterable<Uint8Array | Buffer | string>;
|
|
7
|
-
readonly kind?: string;
|
|
8
|
-
readonly mimeType?: string;
|
|
9
|
-
readonly filename?: string;
|
|
10
|
-
readonly sourceUri?: string;
|
|
11
|
-
readonly sizeBytes?: number;
|
|
12
|
-
readonly retentionMs?: number;
|
|
13
|
-
readonly acquisitionMode?: string;
|
|
14
|
-
readonly fetchMode?: string;
|
|
15
|
-
readonly metadata?: Record<string, unknown
|
|
7
|
+
readonly kind?: string | undefined;
|
|
8
|
+
readonly mimeType?: string | undefined;
|
|
9
|
+
readonly filename?: string | undefined;
|
|
10
|
+
readonly sourceUri?: string | undefined;
|
|
11
|
+
readonly sizeBytes?: number | undefined;
|
|
12
|
+
readonly retentionMs?: number | undefined;
|
|
13
|
+
readonly acquisitionMode?: string | undefined;
|
|
14
|
+
readonly fetchMode?: string | undefined;
|
|
15
|
+
readonly metadata?: Record<string, unknown> | undefined;
|
|
16
16
|
}): Promise<unknown>;
|
|
17
17
|
}
|
|
18
18
|
export interface ArtifactUploadResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-upload.d.ts","sourceRoot":"","sources":["../src/artifact-upload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"artifact-upload.d.ts","sourceRoot":"","sources":["../src/artifact-upload.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE7D,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,WAAW,CAAC,IAAI,MAAM,CAAC;IACvB,gBAAgB,CAAC,CAAC,KAAK,EAAE;QACvB,QAAQ,CAAC,MAAM,EACX,cAAc,CAAC,UAAU,CAAC,GAC1B,aAAa,CAAC,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC,GAC3C,QAAQ,CAAC,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC;QAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;KACzD,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;CACzC;AA+BD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAGrE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE7D;AAED,wBAAsB,+BAA+B,CACnD,aAAa,EAAE,uBAAuB,EACtC,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,oBAAoB,GAAG,QAAQ,CAAC,CAM1C"}
|
package/dist/artifact-upload.js
CHANGED
|
@@ -3,6 +3,7 @@ import { mkdtemp, open, rm } from 'node:fs/promises';
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { GoodVibesSdkError } from '@pellux/goodvibes-errors';
|
|
6
|
+
import { jsonErrorResponse } from './error-response.js';
|
|
6
7
|
const JSON_FIELD_NAMES = new Set(['metadata', 'target', 'options']);
|
|
7
8
|
const MAX_MULTIPART_FIELD_BYTES = 1024 * 1024;
|
|
8
9
|
const MAX_MULTIPART_BODY_OVERHEAD_BYTES = MAX_MULTIPART_FIELD_BYTES;
|
|
@@ -61,8 +62,7 @@ function parseJsonField(value, fieldName) {
|
|
|
61
62
|
try {
|
|
62
63
|
return JSON.parse(value);
|
|
63
64
|
}
|
|
64
|
-
catch
|
|
65
|
-
void error;
|
|
65
|
+
catch {
|
|
66
66
|
throw artifactUploadError(`Invalid JSON in multipart field ${fieldName}.`);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -86,7 +86,7 @@ function readArtifactId(artifact) {
|
|
|
86
86
|
if (typeof id === 'string' && id.trim().length > 0)
|
|
87
87
|
return id;
|
|
88
88
|
}
|
|
89
|
-
return
|
|
89
|
+
return jsonErrorResponse({ error: 'Artifact store returned an artifact without an id.' }, { status: 500 });
|
|
90
90
|
}
|
|
91
91
|
function isFileLike(value) {
|
|
92
92
|
return typeof value === 'object'
|
|
@@ -101,21 +101,23 @@ async function createArtifactFromMultipart(artifactStore, req) {
|
|
|
101
101
|
try {
|
|
102
102
|
form = await req.formData();
|
|
103
103
|
}
|
|
104
|
-
catch
|
|
105
|
-
|
|
106
|
-
return Response.json({ error: 'Invalid multipart upload.' }, { status: 400 });
|
|
104
|
+
catch {
|
|
105
|
+
return jsonErrorResponse({ error: 'Invalid multipart upload.' }, { status: 400 });
|
|
107
106
|
}
|
|
108
107
|
const fields = {};
|
|
109
108
|
let file = null;
|
|
110
109
|
for (const [name, value] of form.entries()) {
|
|
111
110
|
if (typeof value === 'string') {
|
|
111
|
+
if (Buffer.byteLength(value, 'utf8') > MAX_MULTIPART_FIELD_BYTES) {
|
|
112
|
+
return jsonErrorResponse({ error: `Multipart field ${name} is too large.` }, { status: 413 });
|
|
113
|
+
}
|
|
112
114
|
try {
|
|
113
115
|
const parsed = parseUploadField(name, value);
|
|
114
116
|
if (parsed !== undefined)
|
|
115
117
|
fields[name] = parsed;
|
|
116
118
|
}
|
|
117
119
|
catch (error) {
|
|
118
|
-
return
|
|
120
|
+
return jsonErrorResponse({ error: error instanceof Error ? error.message : String(error) }, { status: 400 });
|
|
119
121
|
}
|
|
120
122
|
continue;
|
|
121
123
|
}
|
|
@@ -124,11 +126,13 @@ async function createArtifactFromMultipart(artifactStore, req) {
|
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
if (!file)
|
|
127
|
-
return
|
|
129
|
+
return jsonErrorResponse({ error: 'Multipart upload requires a file field.' }, { status: 400 });
|
|
128
130
|
const maxBytes = artifactStore.getMaxBytes?.();
|
|
129
131
|
if (typeof maxBytes === 'number' && typeof file.size === 'number' && file.size > maxBytes) {
|
|
130
|
-
return
|
|
132
|
+
return jsonErrorResponse({ error: `Artifact exceeds the ${maxBytes}-byte limit.` }, { status: 413 });
|
|
131
133
|
}
|
|
134
|
+
// Some FormData implementations omit `File.size`; createFromStream performs
|
|
135
|
+
// the authoritative streaming byte-count check for those uploads.
|
|
132
136
|
const artifact = await createArtifactFromStream(artifactStore, {
|
|
133
137
|
stream: file.stream(),
|
|
134
138
|
fields,
|
|
@@ -156,7 +160,7 @@ async function createArtifactFromStreamingMultipart(artifactStore, req) {
|
|
|
156
160
|
return artifactId instanceof Response ? artifactId : { artifact, artifactId, fields: spooled.fields };
|
|
157
161
|
}
|
|
158
162
|
catch (error) {
|
|
159
|
-
return
|
|
163
|
+
return jsonErrorResponse(error, { status: error instanceof GoodVibesSdkError && error.status ? error.status : 400 });
|
|
160
164
|
}
|
|
161
165
|
finally {
|
|
162
166
|
await spooled?.cleanup();
|
|
@@ -164,14 +168,14 @@ async function createArtifactFromStreamingMultipart(artifactStore, req) {
|
|
|
164
168
|
}
|
|
165
169
|
async function createArtifactFromRawBody(artifactStore, req) {
|
|
166
170
|
if (!req.body)
|
|
167
|
-
return
|
|
171
|
+
return jsonErrorResponse({ error: 'Raw artifact upload requires a request body.' }, { status: 400 });
|
|
168
172
|
const url = new URL(req.url);
|
|
169
173
|
const fields = readFieldsFromSearchParams(url.searchParams);
|
|
170
174
|
const contentType = req.headers.get('content-type')?.split(';')[0]?.trim();
|
|
171
175
|
const maxBytes = artifactStore.getMaxBytes?.();
|
|
172
176
|
const sizeBytes = readContentLength(req);
|
|
173
177
|
if (typeof maxBytes === 'number' && typeof sizeBytes === 'number' && sizeBytes > maxBytes) {
|
|
174
|
-
return
|
|
178
|
+
return jsonErrorResponse({ error: `Artifact exceeds the ${maxBytes}-byte limit.` }, { status: 413 });
|
|
175
179
|
}
|
|
176
180
|
const artifact = await createArtifactFromStream(artifactStore, {
|
|
177
181
|
stream: req.body,
|
|
@@ -209,8 +213,7 @@ function readFieldsFromSearchParams(params) {
|
|
|
209
213
|
if (parsed !== undefined)
|
|
210
214
|
fields[name] = parsed;
|
|
211
215
|
}
|
|
212
|
-
catch
|
|
213
|
-
void error;
|
|
216
|
+
catch {
|
|
214
217
|
fields[name] = value;
|
|
215
218
|
}
|
|
216
219
|
}
|
|
@@ -223,14 +226,16 @@ function readContentLength(req) {
|
|
|
223
226
|
function filenameFromContentDisposition(header) {
|
|
224
227
|
if (!header)
|
|
225
228
|
return undefined;
|
|
229
|
+
// Minimal RFC 5987 support for filename*=UTF-8''... values. Complex
|
|
230
|
+
// parameter continuations are intentionally unsupported by this lightweight
|
|
231
|
+
// daemon helper; callers should prefer multipart filename metadata.
|
|
226
232
|
const match = header.match(/filename\*?=(?:UTF-8''|")?([^";]+)/i);
|
|
227
233
|
if (!match?.[1])
|
|
228
234
|
return undefined;
|
|
229
235
|
try {
|
|
230
236
|
return decodeURIComponent(match[1].replace(/^"|"$/g, ''));
|
|
231
237
|
}
|
|
232
|
-
catch
|
|
233
|
-
void error;
|
|
238
|
+
catch {
|
|
234
239
|
return match[1].replace(/^"|"$/g, '');
|
|
235
240
|
}
|
|
236
241
|
}
|
|
@@ -244,8 +249,7 @@ function decodeHeaderValue(value) {
|
|
|
244
249
|
try {
|
|
245
250
|
return decodeURIComponent(unquoted.replace(/^UTF-8''/i, ''));
|
|
246
251
|
}
|
|
247
|
-
catch
|
|
248
|
-
void error;
|
|
252
|
+
catch {
|
|
249
253
|
return unquoted;
|
|
250
254
|
}
|
|
251
255
|
}
|
|
@@ -259,6 +263,10 @@ function parseMultipartPartHeaders(headerText) {
|
|
|
259
263
|
}
|
|
260
264
|
const disposition = headers.get('content-disposition') ?? '';
|
|
261
265
|
const output = {};
|
|
266
|
+
// Lightweight multipart parser: supports ordinary parameters without
|
|
267
|
+
// embedded semicolons plus filename*=UTF-8''... values. RFC 5987
|
|
268
|
+
// continuations and semicolons inside quoted values are intentionally out of
|
|
269
|
+
// scope for this daemon helper.
|
|
262
270
|
for (const segment of disposition.split(';').slice(1)) {
|
|
263
271
|
const [rawKey, ...rawValue] = segment.split('=');
|
|
264
272
|
const key = rawKey?.trim().toLowerCase();
|
|
@@ -272,7 +280,7 @@ function parseMultipartPartHeaders(headerText) {
|
|
|
272
280
|
}
|
|
273
281
|
const contentType = headers.get('content-type');
|
|
274
282
|
if (contentType)
|
|
275
|
-
output.contentType = contentType.split(';')[0]?.trim();
|
|
283
|
+
output.contentType = contentType.split(';')[0]?.trim() ?? '';
|
|
276
284
|
return output;
|
|
277
285
|
}
|
|
278
286
|
async function spoolMultipartUpload(req, maxFileBytes) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DaemonRuntimeRouteContext } from './runtime-route-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Guard helper: returns the admin-denied response if the caller lacks admin
|
|
4
|
+
* privileges, otherwise calls `next()` and returns its result.
|
|
5
|
+
*
|
|
6
|
+
* Shared across all daemon runtime route files to avoid duplication.
|
|
7
|
+
*/
|
|
8
|
+
export declare function withAdmin(context: DaemonRuntimeRouteContext, req: Request, next: () => Response | Promise<Response>): Response | Promise<Response>;
|
|
9
|
+
//# sourceMappingURL=auth-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-helpers.d.ts","sourceRoot":"","sources":["../src/auth-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAE1E;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,yBAAyB,EAClC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GACvC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAG9B"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guard helper: returns the admin-denied response if the caller lacks admin
|
|
3
|
+
* privileges, otherwise calls `next()` and returns its result.
|
|
4
|
+
*
|
|
5
|
+
* Shared across all daemon runtime route files to avoid duplication.
|
|
6
|
+
*/
|
|
7
|
+
export function withAdmin(context, req, next) {
|
|
8
|
+
const denied = context.requireAdmin(req);
|
|
9
|
+
return denied ?? next();
|
|
10
|
+
}
|
package/dist/automation.js
CHANGED
|
@@ -25,32 +25,32 @@ export async function dispatchAutomationRoutes(req, handlers) {
|
|
|
25
25
|
if (automationJobMatch && method === 'PATCH')
|
|
26
26
|
return handlers.patchAutomationJob(automationJobMatch[1], req);
|
|
27
27
|
if (automationJobMatch && method === 'DELETE')
|
|
28
|
-
return handlers.deleteAutomationJob(automationJobMatch[1]);
|
|
28
|
+
return handlers.deleteAutomationJob(automationJobMatch[1], req);
|
|
29
29
|
const automationJobActionMatch = pathname.match(/^\/api\/automation\/jobs\/([^/]+)\/(enable|disable|pause|resume|run)$/);
|
|
30
30
|
if (automationJobActionMatch && method === 'POST') {
|
|
31
31
|
const [, jobId, action] = automationJobActionMatch;
|
|
32
32
|
if (action === 'run')
|
|
33
|
-
return handlers.runAutomationJobNow(jobId);
|
|
34
|
-
return handlers.setAutomationJobEnabled(jobId, action === 'enable' || action === 'resume');
|
|
33
|
+
return handlers.runAutomationJobNow(jobId, req);
|
|
34
|
+
return handlers.setAutomationJobEnabled(jobId, action === 'enable' || action === 'resume', req);
|
|
35
35
|
}
|
|
36
36
|
if (pathname === '/api/deliveries' && method === 'GET')
|
|
37
37
|
return handlers.getDeliveries();
|
|
38
38
|
const deliveryMatch = pathname.match(/^\/api\/deliveries\/([^/]+)$/);
|
|
39
39
|
if (deliveryMatch && method === 'GET')
|
|
40
40
|
return handlers.getDelivery(deliveryMatch[1]);
|
|
41
|
-
if (pathname === '/schedules' && method === 'GET')
|
|
41
|
+
if (pathname === '/api/automation/schedules' && method === 'GET')
|
|
42
42
|
return handlers.getSchedules();
|
|
43
|
-
if (pathname === '/schedules' && method === 'POST')
|
|
43
|
+
if (pathname === '/api/automation/schedules' && method === 'POST')
|
|
44
44
|
return handlers.postSchedule(req);
|
|
45
|
-
const scheduleIdMatch = pathname.match(/^\/schedules\/([^/]+)$/);
|
|
45
|
+
const scheduleIdMatch = pathname.match(/^\/api\/automation\/schedules\/([^/]+)$/);
|
|
46
46
|
if (scheduleIdMatch && method === 'DELETE')
|
|
47
|
-
return handlers.deleteSchedule(scheduleIdMatch[1]);
|
|
48
|
-
const scheduleActionMatch = pathname.match(/^\/schedules\/([^/]+)\/(enable|disable|run)$/);
|
|
47
|
+
return handlers.deleteSchedule(scheduleIdMatch[1], req);
|
|
48
|
+
const scheduleActionMatch = pathname.match(/^\/api\/automation\/schedules\/([^/]+)\/(enable|disable|run)$/);
|
|
49
49
|
if (scheduleActionMatch && method === 'POST') {
|
|
50
50
|
const [, scheduleId, action] = scheduleActionMatch;
|
|
51
51
|
if (action === 'run')
|
|
52
|
-
return handlers.runScheduleNow(scheduleId);
|
|
53
|
-
return handlers.setScheduleEnabled(scheduleId, action === 'enable');
|
|
52
|
+
return handlers.runScheduleNow(scheduleId, req);
|
|
53
|
+
return handlers.setScheduleEnabled(scheduleId, action === 'enable', req);
|
|
54
54
|
}
|
|
55
55
|
return null;
|
|
56
56
|
}
|
|
@@ -6,36 +6,36 @@ export interface ChannelAgentToolDefinitionLike {
|
|
|
6
6
|
}
|
|
7
7
|
export interface ChannelTargetResolutionInput {
|
|
8
8
|
readonly input: string;
|
|
9
|
-
readonly accountId?: string;
|
|
10
|
-
readonly preferredKind?: ChannelConversationKind;
|
|
11
|
-
readonly threadId?: string;
|
|
12
|
-
readonly sessionId?: string;
|
|
13
|
-
readonly createIfMissing?: boolean;
|
|
14
|
-
readonly live?: boolean;
|
|
15
|
-
readonly metadata?: Record<string, unknown
|
|
9
|
+
readonly accountId?: string | undefined;
|
|
10
|
+
readonly preferredKind?: ChannelConversationKind | undefined;
|
|
11
|
+
readonly threadId?: string | undefined;
|
|
12
|
+
readonly sessionId?: string | undefined;
|
|
13
|
+
readonly createIfMissing?: boolean | undefined;
|
|
14
|
+
readonly live?: boolean | undefined;
|
|
15
|
+
readonly metadata?: Record<string, unknown> | undefined;
|
|
16
16
|
}
|
|
17
17
|
export interface ChannelAuthorizeActionInput {
|
|
18
18
|
readonly actionId: string;
|
|
19
|
-
readonly actorId?: string;
|
|
20
|
-
readonly accountId?: string;
|
|
21
|
-
readonly target?: unknown;
|
|
22
|
-
readonly metadata?: Record<string, unknown
|
|
19
|
+
readonly actorId?: string | undefined;
|
|
20
|
+
readonly accountId?: string | undefined;
|
|
21
|
+
readonly target?: unknown | undefined;
|
|
22
|
+
readonly metadata?: Record<string, unknown> | undefined;
|
|
23
23
|
}
|
|
24
24
|
export interface ChannelAllowlistInput {
|
|
25
|
-
readonly add?: readonly string[];
|
|
26
|
-
readonly remove?: readonly string[];
|
|
27
|
-
readonly groupId?: string;
|
|
28
|
-
readonly channelId?: string;
|
|
29
|
-
readonly workspaceId?: string;
|
|
30
|
-
readonly kind?: 'user' | 'channel' | 'group';
|
|
31
|
-
readonly metadata?: Record<string, unknown
|
|
25
|
+
readonly add?: readonly string[] | undefined;
|
|
26
|
+
readonly remove?: readonly string[] | undefined;
|
|
27
|
+
readonly groupId?: string | undefined;
|
|
28
|
+
readonly channelId?: string | undefined;
|
|
29
|
+
readonly workspaceId?: string | undefined;
|
|
30
|
+
readonly kind?: 'user' | 'channel' | 'group' | undefined;
|
|
31
|
+
readonly metadata?: Record<string, unknown> | undefined;
|
|
32
32
|
}
|
|
33
33
|
export interface ChannelDirectoryQuery {
|
|
34
34
|
readonly query: string;
|
|
35
|
-
readonly scope?: ChannelDirectoryScope;
|
|
36
|
-
readonly groupId?: string;
|
|
37
|
-
readonly limit?: number;
|
|
38
|
-
readonly live?: boolean;
|
|
35
|
+
readonly scope?: ChannelDirectoryScope | undefined;
|
|
36
|
+
readonly groupId?: string | undefined;
|
|
37
|
+
readonly limit?: number | undefined;
|
|
38
|
+
readonly live?: boolean | undefined;
|
|
39
39
|
}
|
|
40
40
|
export interface ChannelPluginServiceLike {
|
|
41
41
|
listAccounts(surface?: ChannelSurface): Promise<readonly unknown[]>;
|
|
@@ -44,7 +44,6 @@ export interface ChannelPluginServiceLike {
|
|
|
44
44
|
doctor(surface: ChannelSurface, accountId?: string): Promise<unknown | null>;
|
|
45
45
|
listRepairActions(surface: ChannelSurface, accountId?: string): Promise<readonly unknown[]>;
|
|
46
46
|
getLifecycleState(surface: ChannelSurface, accountId?: string): Promise<unknown | null>;
|
|
47
|
-
migrateLifecycle(surface: ChannelSurface, accountId?: string, input?: JsonRecord | null): Promise<unknown | null>;
|
|
48
47
|
runAccountAction(surface: ChannelSurface, action: ChannelLifecycleAction, accountId: string | undefined, input?: JsonRecord): Promise<unknown | null>;
|
|
49
48
|
listCapabilities(surface?: ChannelSurface): Promise<readonly unknown[]>;
|
|
50
49
|
listTools(surface?: ChannelSurface): Promise<readonly unknown[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-route-types.d.ts","sourceRoot":"","sources":["../src/channel-route-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEtG,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AACpC,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"channel-route-types.d.ts","sourceRoot":"","sources":["../src/channel-route-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-routes.d.ts","sourceRoot":"","sources":["../src/channel-routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"channel-routes.d.ts","sourceRoot":"","sources":["../src/channel-routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAU/D,OAAO,KAAK,EAAyC,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAkBjH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,yBAAyB,GACjC,0BAA0B,CAyS5B"}
|
package/dist/channel-routes.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { jsonErrorResponse } from './error-response.js';
|
|
3
|
+
import { createRouteBodySchema, createRouteBodySchemaRegistry, readBoundedPositiveInteger, readChannelConversationKind, readChannelLifecycleAction, } from './route-helpers.js';
|
|
4
|
+
const channelBodySchemas = createRouteBodySchemaRegistry({
|
|
5
|
+
optional: createRouteBodySchema('POST /api/channels/* optional body', (body) => body),
|
|
6
|
+
});
|
|
7
|
+
async function readOptionalChannelBody(context, req) {
|
|
8
|
+
const body = await context.parseOptionalJsonBody(req);
|
|
9
|
+
if (body instanceof Response)
|
|
10
|
+
return body;
|
|
11
|
+
if (body === null)
|
|
12
|
+
return undefined;
|
|
13
|
+
return channelBodySchemas.optional.parse(body);
|
|
14
|
+
}
|
|
3
15
|
export function createDaemonChannelRouteHandlers(context) {
|
|
4
16
|
return {
|
|
5
17
|
getSurfaces: () => Response.json({ surfaces: context.surfaceRegistry.list() }),
|
|
@@ -11,19 +23,19 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
11
23
|
const account = await context.channelPlugins.getAccount(surface, accountId);
|
|
12
24
|
return account
|
|
13
25
|
? Response.json(account)
|
|
14
|
-
:
|
|
26
|
+
: jsonErrorResponse({ error: 'Unknown channel account' }, { status: 404 });
|
|
15
27
|
},
|
|
16
28
|
getChannelSetupSchema: async (surface, url) => {
|
|
17
29
|
const schema = await context.channelPlugins.getSetupSchema(surface, url.searchParams.get('accountId') ?? undefined);
|
|
18
30
|
return schema
|
|
19
31
|
? Response.json(schema)
|
|
20
|
-
:
|
|
32
|
+
: jsonErrorResponse({ error: 'Unknown channel setup schema' }, { status: 404 });
|
|
21
33
|
},
|
|
22
34
|
getChannelDoctor: async (surface, url) => {
|
|
23
35
|
const report = await context.channelPlugins.doctor(surface, url.searchParams.get('accountId') ?? undefined);
|
|
24
36
|
return report
|
|
25
37
|
? Response.json(report)
|
|
26
|
-
:
|
|
38
|
+
: jsonErrorResponse({ error: 'Unknown channel doctor surface' }, { status: 404 });
|
|
27
39
|
},
|
|
28
40
|
getChannelRepairActions: async (surface, url) => Response.json({
|
|
29
41
|
actions: await context.channelPlugins.listRepairActions(surface, url.searchParams.get('accountId') ?? undefined),
|
|
@@ -32,37 +44,23 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
32
44
|
const state = await context.channelPlugins.getLifecycleState(surface, url.searchParams.get('accountId') ?? undefined);
|
|
33
45
|
return state
|
|
34
46
|
? Response.json(state)
|
|
35
|
-
:
|
|
36
|
-
},
|
|
37
|
-
postChannelLifecycleMigrate: async (surface, req) => {
|
|
38
|
-
const admin = context.requireAdmin(req);
|
|
39
|
-
if (admin)
|
|
40
|
-
return admin;
|
|
41
|
-
const body = await context.parseOptionalJsonBody(req);
|
|
42
|
-
if (body instanceof Response)
|
|
43
|
-
return body;
|
|
44
|
-
const state = await context.channelPlugins.migrateLifecycle(surface, typeof body?.accountId === 'string' ? body.accountId : undefined, body ?? undefined);
|
|
45
|
-
return state
|
|
46
|
-
? Response.json(state)
|
|
47
|
-
: Response.json({ error: 'Unknown channel lifecycle surface' }, { status: 404 });
|
|
47
|
+
: jsonErrorResponse({ error: 'Unknown channel lifecycle surface' }, { status: 404 });
|
|
48
48
|
},
|
|
49
49
|
postChannelAccountAction: async (surface, accountId, action, req) => {
|
|
50
50
|
const admin = context.requireAdmin(req);
|
|
51
51
|
if (admin)
|
|
52
52
|
return admin;
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return body;
|
|
57
|
-
}
|
|
53
|
+
const input = await readOptionalChannelBody(context, req);
|
|
54
|
+
if (input instanceof Response)
|
|
55
|
+
return input;
|
|
58
56
|
const lifecycleAction = readChannelLifecycleAction(action);
|
|
59
57
|
if (!lifecycleAction) {
|
|
60
|
-
return
|
|
58
|
+
return jsonErrorResponse({ error: 'Unknown channel account action' }, { status: 400 });
|
|
61
59
|
}
|
|
62
60
|
const result = await context.channelPlugins.runAccountAction(surface, lifecycleAction, accountId ?? (typeof input?.accountId === 'string' ? input.accountId : undefined), input);
|
|
63
61
|
return result !== null
|
|
64
62
|
? Response.json({ surface, accountId, action: lifecycleAction, result })
|
|
65
|
-
:
|
|
63
|
+
: jsonErrorResponse({ error: 'Unknown channel account action' }, { status: 404 });
|
|
66
64
|
},
|
|
67
65
|
getChannelCapabilities: () => context.channelPlugins.listCapabilities().then((capabilities) => Response.json({ capabilities })),
|
|
68
66
|
getChannelSurfaceCapabilities: (surface) => context.channelPlugins
|
|
@@ -82,15 +80,13 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
82
80
|
const admin = context.requireAdmin(req);
|
|
83
81
|
if (admin)
|
|
84
82
|
return admin;
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return body;
|
|
89
|
-
}
|
|
83
|
+
const input = await readOptionalChannelBody(context, req);
|
|
84
|
+
if (input instanceof Response)
|
|
85
|
+
return input;
|
|
90
86
|
const result = await context.channelPlugins.runTool(surface, toolId, input);
|
|
91
87
|
return result !== null
|
|
92
88
|
? Response.json({ toolId, surface, result })
|
|
93
|
-
:
|
|
89
|
+
: jsonErrorResponse({ error: 'Unknown channel tool' }, { status: 404 });
|
|
94
90
|
},
|
|
95
91
|
getChannelActions: () => context.channelPlugins.listOperatorActions().then((actions) => Response.json({ actions })),
|
|
96
92
|
getChannelSurfaceActions: (surface) => context.channelPlugins
|
|
@@ -100,15 +96,13 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
100
96
|
const admin = context.requireAdmin(req);
|
|
101
97
|
if (admin)
|
|
102
98
|
return admin;
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return body;
|
|
107
|
-
}
|
|
99
|
+
const input = await readOptionalChannelBody(context, req);
|
|
100
|
+
if (input instanceof Response)
|
|
101
|
+
return input;
|
|
108
102
|
const result = await context.channelPlugins.runOperatorAction(surface, actionId, input);
|
|
109
103
|
return result !== null
|
|
110
104
|
? Response.json({ actionId, surface, result })
|
|
111
|
-
:
|
|
105
|
+
: jsonErrorResponse({ error: 'Unknown channel action' }, { status: 404 });
|
|
112
106
|
},
|
|
113
107
|
postChannelResolveTarget: async (surface, req) => {
|
|
114
108
|
const admin = context.requireAdmin(req);
|
|
@@ -125,7 +119,7 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
125
119
|
? body.query
|
|
126
120
|
: '';
|
|
127
121
|
if (!targetInput.trim()) {
|
|
128
|
-
return
|
|
122
|
+
return jsonErrorResponse({ error: 'Target resolution requires target, input, or query.' }, { status: 400 });
|
|
129
123
|
}
|
|
130
124
|
const preferredKind = readChannelConversationKind(body.preferredKind);
|
|
131
125
|
const target = await context.channelPlugins.resolveTarget(surface, {
|
|
@@ -140,7 +134,7 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
140
134
|
});
|
|
141
135
|
return target
|
|
142
136
|
? Response.json({ surface, target })
|
|
143
|
-
:
|
|
137
|
+
: jsonErrorResponse({ error: 'Unable to resolve channel target' }, { status: 404 });
|
|
144
138
|
},
|
|
145
139
|
postChannelAuthorize: async (surface, req) => {
|
|
146
140
|
const admin = context.requireAdmin(req);
|
|
@@ -165,7 +159,7 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
165
159
|
});
|
|
166
160
|
return result
|
|
167
161
|
? Response.json({ surface, result })
|
|
168
|
-
:
|
|
162
|
+
: jsonErrorResponse({ error: 'Unable to authorize channel action' }, { status: 404 });
|
|
169
163
|
},
|
|
170
164
|
postChannelAllowlistResolve: async (surface, req) => {
|
|
171
165
|
const admin = context.requireAdmin(req);
|
|
@@ -185,7 +179,7 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
185
179
|
});
|
|
186
180
|
return result
|
|
187
181
|
? Response.json(result)
|
|
188
|
-
:
|
|
182
|
+
: jsonErrorResponse({ error: 'Unknown channel allowlist surface' }, { status: 404 });
|
|
189
183
|
},
|
|
190
184
|
postChannelAllowlistEdit: async (surface, req) => {
|
|
191
185
|
const admin = context.requireAdmin(req);
|
|
@@ -205,7 +199,7 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
205
199
|
});
|
|
206
200
|
return result
|
|
207
201
|
? Response.json(result)
|
|
208
|
-
:
|
|
202
|
+
: jsonErrorResponse({ error: 'Unknown channel allowlist surface' }, { status: 404 });
|
|
209
203
|
},
|
|
210
204
|
getChannelPolicies: () => Response.json({ policies: context.channelPolicy.listPolicies() }),
|
|
211
205
|
patchChannelPolicy: async (surface, req) => {
|
|
@@ -256,7 +250,8 @@ export function createDaemonChannelRouteHandlers(context) {
|
|
|
256
250
|
groupPolicies: body.groupPolicies
|
|
257
251
|
.filter((value) => typeof value === 'object' && value !== null)
|
|
258
252
|
.map((value) => ({
|
|
259
|
-
|
|
253
|
+
// use the full UUID to avoid birthday-paradox collisions at scale.
|
|
254
|
+
id: typeof value.id === 'string' ? value.id : `group-policy-${randomUUID()}`,
|
|
260
255
|
...(typeof value.label === 'string' ? { label: value.label } : {}),
|
|
261
256
|
...(typeof value.groupId === 'string' ? { groupId: value.groupId } : {}),
|
|
262
257
|
...(typeof value.channelId === 'string' ? { channelId: value.channelId } : {}),
|