@framers/agentos 0.13.63 → 0.13.64

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.
Files changed (30) hide show
  1. package/dist/api/types.d.ts +111 -81
  2. package/dist/api/types.d.ts.map +1 -1
  3. package/dist/api/types.js.map +1 -1
  4. package/dist/io/channels/meetings/IMeetingBotProvider.d.ts +128 -0
  5. package/dist/io/channels/meetings/IMeetingBotProvider.d.ts.map +1 -0
  6. package/dist/io/channels/meetings/IMeetingBotProvider.js +2 -0
  7. package/dist/io/channels/meetings/IMeetingBotProvider.js.map +1 -0
  8. package/dist/io/channels/meetings/providers/mock.d.ts +46 -0
  9. package/dist/io/channels/meetings/providers/mock.d.ts.map +1 -0
  10. package/dist/io/channels/meetings/providers/mock.js +101 -0
  11. package/dist/io/channels/meetings/providers/mock.js.map +1 -0
  12. package/dist/io/channels/meetings/signing.d.ts +102 -0
  13. package/dist/io/channels/meetings/signing.d.ts.map +1 -0
  14. package/dist/io/channels/meetings/signing.js +141 -0
  15. package/dist/io/channels/meetings/signing.js.map +1 -0
  16. package/dist/io/channels/meetings/stores/memory.d.ts +88 -0
  17. package/dist/io/channels/meetings/stores/memory.d.ts.map +1 -0
  18. package/dist/io/channels/meetings/stores/memory.js +273 -0
  19. package/dist/io/channels/meetings/stores/memory.js.map +1 -0
  20. package/dist/io/channels/meetings/stores/types.d.ts +224 -0
  21. package/dist/io/channels/meetings/stores/types.d.ts.map +1 -0
  22. package/dist/io/channels/meetings/stores/types.js +35 -0
  23. package/dist/io/channels/meetings/stores/types.js.map +1 -0
  24. package/dist/io/channels/meetings/types.d.ts +200 -0
  25. package/dist/io/channels/meetings/types.d.ts.map +1 -0
  26. package/dist/io/channels/meetings/types.js +89 -0
  27. package/dist/io/channels/meetings/types.js.map +1 -0
  28. package/dist/io/voice-pipeline/types.d.ts +30 -0
  29. package/dist/io/voice-pipeline/types.d.ts.map +1 -1
  30. package/package.json +1 -1
@@ -0,0 +1,46 @@
1
+ import type { DispatchInput, DispatchResult, IMeetingBotProvider, ProviderCommand, ProviderEventContext, ProviderParseResult, ProviderUsage, ProviderVerificationResult } from '../IMeetingBotProvider.js';
2
+ import type { MeetingBotCapabilities, MeetingPlatform, MeetingRunRecord, NormalizedMeetingEvent, ProviderEventType } from '../types.js';
3
+ /** Builds the context a worker's report would produce, for tests. */
4
+ export declare function mockEventContext(runId: string, type: ProviderEventType, data: NormalizedMeetingEvent['data'], at: number, workerToken: string): ProviderEventContext;
5
+ /** A provider for tests: it records dispatches and commands, and its reports are driven by the test through {@link mockEventContext}. */
6
+ export declare class MockMeetingBotProvider implements IMeetingBotProvider {
7
+ readonly name = "mock";
8
+ readonly platforms: readonly MeetingPlatform[];
9
+ readonly capabilities: MeetingBotCapabilities;
10
+ /** Each run's dispatch input, by run id. */
11
+ readonly dispatched: Map<string, DispatchInput>;
12
+ /** Each run's commands, in the order they were sent. */
13
+ readonly commands: Map<string, ProviderCommand[]>;
14
+ /** The run ids `deleteRun` was called with, in order. */
15
+ readonly deleted: string[];
16
+ /** What `usage()` answers; tests set it. */
17
+ alive: Map<string, number>;
18
+ /** When set, dispatch rejects with it (a worker with no slot). */
19
+ dispatchError: Error | undefined;
20
+ /** Runs inside `dispatch`, before it records anything: a test puts a race here (a cancel while the dispatch is in flight). */
21
+ beforeDispatch: ((input: DispatchInput) => Promise<void>) | undefined;
22
+ verifyEvent(ctx: ProviderEventContext, run: MeetingRunRecord | undefined): ProviderVerificationResult;
23
+ parseEvent(ctx: ProviderEventContext): ProviderParseResult;
24
+ checkJoinUrl(url: string): {
25
+ ok: true;
26
+ platform: MeetingPlatform;
27
+ } | {
28
+ ok: false;
29
+ message: string;
30
+ };
31
+ dispatch(input: DispatchInput): Promise<DispatchResult>;
32
+ command(runId: string, command: ProviderCommand): Promise<void>;
33
+ deleteRun(input: {
34
+ runId: string;
35
+ }): Promise<{
36
+ deleted: boolean;
37
+ }>;
38
+ usage(input: {
39
+ runIds?: string[];
40
+ }): Promise<ProviderUsage[]>;
41
+ /** The token a run was dispatched with, for a test that drives its reports. */
42
+ tokenOf(runId: string): string;
43
+ /** For a test that wants the hash a run store would hold. */
44
+ static hash(token: string): string;
45
+ }
46
+ //# sourceMappingURL=mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../../../../src/io/channels/meetings/providers/mock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,0BAA0B,EAC3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAExI,qEAAqE;AACrE,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,sBAAsB,CAAC,MAAM,CAAC,EACpC,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,GAClB,oBAAoB,CAStB;AAMD,yIAAyI;AACzI,qBAAa,sBAAuB,YAAW,mBAAmB;IAChE,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAsC;IACpF,QAAQ,CAAC,YAAY,EAAE,sBAAsB,CAS3C;IACF,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,6BAAoC;IACvD,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,iCAAwC;IACzD,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAM;IAChC,4CAA4C;IAC5C,KAAK,sBAA6B;IAClC,kEAAkE;IAClE,aAAa,EAAE,KAAK,GAAG,SAAS,CAAC;IACjC,8HAA8H;IAC9H,cAAc,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IAEtE,WAAW,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,gBAAgB,GAAG,SAAS,GAAG,0BAA0B;IASrG,UAAU,CAAC,GAAG,EAAE,oBAAoB,GAAG,mBAAmB;IAgB1D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,eAAe,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAM7F,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAQvD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/D,SAAS,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAKlE,KAAK,CAAC,KAAK,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAKnE,+EAA+E;IAC/E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAM9B,6DAA6D;IAC7D,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAGnC"}
@@ -0,0 +1,101 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { hashWorkerToken, workerTokenMatches } from '../signing.js';
3
+ /** Builds the context a worker's report would produce, for tests. */
4
+ export function mockEventContext(runId, type, data, at, workerToken) {
5
+ return {
6
+ providerName: 'mock',
7
+ method: 'POST',
8
+ url: `/events/mock/${runId}`,
9
+ headers: { authorization: `Bearer ${workerToken}` },
10
+ body: JSON.stringify({ runId, type, at, data }),
11
+ runId,
12
+ };
13
+ }
14
+ // The join links the mock accepts: a Meet meeting code link, and a Teams meeting link on either of Microsoft's hosts.
15
+ const MEET = /^https:\/\/meet\.google\.com\/[a-z]{3}-[a-z]{4}-[a-z]{3}(\?.*)?$/;
16
+ const TEAMS = /^https:\/\/teams\.(microsoft\.com|live\.com)\/(l\/)?meet(up-join)?\//;
17
+ /** A provider for tests: it records dispatches and commands, and its reports are driven by the test through {@link mockEventContext}. */
18
+ export class MockMeetingBotProvider {
19
+ constructor() {
20
+ this.name = 'mock';
21
+ this.platforms = ['google_meet', 'microsoft_teams'];
22
+ this.capabilities = {
23
+ joinsAsParticipant: true,
24
+ postsChat: true,
25
+ readsChat: true,
26
+ audioPath: 'livekit_room',
27
+ recordsAudio: false,
28
+ transcribes: false,
29
+ keeps: 'nothing',
30
+ stopPath: 'chat',
31
+ };
32
+ /** Each run's dispatch input, by run id. */
33
+ this.dispatched = new Map();
34
+ /** Each run's commands, in the order they were sent. */
35
+ this.commands = new Map();
36
+ /** The run ids `deleteRun` was called with, in order. */
37
+ this.deleted = [];
38
+ /** What `usage()` answers; tests set it. */
39
+ this.alive = new Map();
40
+ }
41
+ verifyEvent(ctx, run) {
42
+ const header = ctx.headers.authorization ?? ctx.headers.Authorization;
43
+ const value = Array.isArray(header) ? header[0] : header;
44
+ const token = value?.startsWith('Bearer ') ? value.slice(7) : undefined;
45
+ if (!token)
46
+ return { valid: false, error: 'no bearer token' };
47
+ if (!run?.dispatch)
48
+ return { valid: false, error: 'no dispatch record for the run' };
49
+ return workerTokenMatches(token, run.dispatch.workerTokenHash) ? { valid: true } : { valid: false, error: 'token mismatch' };
50
+ }
51
+ parseEvent(ctx) {
52
+ const body = typeof ctx.body === 'string' ? ctx.body : ctx.body.toString('utf8');
53
+ const raw = JSON.parse(body);
54
+ // the report's run, type and time, then a digest of the whole report: a repeated report keeps its id, and two reports of one type at one time do not share one
55
+ const digest = createHash('sha256').update(body).digest('hex').slice(0, 12);
56
+ return {
57
+ events: [{ eventId: `mock:${raw.runId}:${raw.type}:${raw.at}:${digest}`, runId: raw.runId, type: raw.type, at: raw.at, data: raw.data }],
58
+ rawData: raw,
59
+ };
60
+ }
61
+ checkJoinUrl(url) {
62
+ if (MEET.test(url))
63
+ return { ok: true, platform: 'google_meet' };
64
+ if (TEAMS.test(url))
65
+ return { ok: true, platform: 'microsoft_teams' };
66
+ return { ok: false, message: 'not a Google Meet or Microsoft Teams join link' };
67
+ }
68
+ async dispatch(input) {
69
+ if (this.dispatchError)
70
+ throw this.dispatchError;
71
+ await this.beforeDispatch?.(input);
72
+ this.dispatched.set(input.run.runId, input);
73
+ this.alive.set(input.run.runId, 0);
74
+ return { workerId: 'mock-worker', providerRunRef: `mock-${input.run.runId}` };
75
+ }
76
+ async command(runId, command) {
77
+ const list = this.commands.get(runId) ?? [];
78
+ list.push(command);
79
+ this.commands.set(runId, list);
80
+ }
81
+ async deleteRun(input) {
82
+ this.deleted.push(input.runId);
83
+ return { deleted: false };
84
+ }
85
+ async usage(input) {
86
+ const ids = input.runIds ?? [...this.alive.keys()];
87
+ return ids.map((runId) => ({ runId, alive: this.alive.has(runId), seconds: this.alive.get(runId) ?? 0 }));
88
+ }
89
+ /** The token a run was dispatched with, for a test that drives its reports. */
90
+ tokenOf(runId) {
91
+ const d = this.dispatched.get(runId);
92
+ if (!d)
93
+ throw new Error(`no dispatch for ${runId}`);
94
+ return d.workerToken;
95
+ }
96
+ /** For a test that wants the hash a run store would hold. */
97
+ static hash(token) {
98
+ return hashWorkerToken(token);
99
+ }
100
+ }
101
+ //# sourceMappingURL=mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock.js","sourceRoot":"","sources":["../../../../../src/io/channels/meetings/providers/mock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @module channels/meetings/signing
3
+ *
4
+ * Three ways the channel authenticates, kept apart: the envelope a host signs
5
+ * its outbound deliveries with, the per-run bearer token a worker presents on
6
+ * its reports, and the box that seals a run's meeting url at rest. A
7
+ * provider's own scheme (a platform's webhook signature) lives in that
8
+ * provider.
9
+ */
10
+ /** The three header names of the envelope. A host passes its own (a product's branded names are the product's policy, never the library's). */
11
+ export interface EnvelopeHeaderNames {
12
+ /** The header that carries the delivery's id. */
13
+ id: string;
14
+ /** The header that carries the time the delivery was signed, in milliseconds since the epoch. */
15
+ timestamp: string;
16
+ /** The header that carries the signature. */
17
+ signature: string;
18
+ }
19
+ /** The header names the envelope uses when a host passes none. */
20
+ export declare const DEFAULT_ENVELOPE_HEADERS: Readonly<EnvelopeHeaderNames>;
21
+ /** How far a delivery's timestamp may be from the receiver's clock. */
22
+ export declare const ENVELOPE_WINDOW_MS = 300000;
23
+ /**
24
+ * `v1,` then the base64 HMAC-SHA256 of `<id>.<timestamp>.<payload>` under the destination's secret.
25
+ *
26
+ * @param secret - The destination's secret.
27
+ * @param id - The delivery's id.
28
+ * @param timestampMs - The time the delivery is signed, in milliseconds since the epoch.
29
+ * @param payload - The body exactly as it is sent.
30
+ */
31
+ export declare function signEnvelope(secret: string, id: string, timestampMs: number, payload: string): string;
32
+ /**
33
+ * The three headers of one delivery: its id, its time and its {@link signEnvelope} signature.
34
+ *
35
+ * @param secret - The destination's secret.
36
+ * @param id - The delivery's id.
37
+ * @param timestampMs - The time the delivery is signed, in milliseconds since the epoch.
38
+ * @param payload - The body exactly as it is sent.
39
+ * @param names - The header names; {@link DEFAULT_ENVELOPE_HEADERS} when absent.
40
+ */
41
+ export declare function envelopeHeaders(secret: string, id: string, timestampMs: number, payload: string, names?: EnvelopeHeaderNames): Record<string, string>;
42
+ /**
43
+ * The receiving side: the three headers, the raw payload and the receiver's clock.
44
+ *
45
+ * @param secret - The destination's secret.
46
+ * @param headers - The delivery's headers.
47
+ * @param payload - The body exactly as it arrived, before any parse.
48
+ * @param nowMs - The receiver's clock, in milliseconds since the epoch.
49
+ * @param options - `windowMs`, how far the delivery's time may be from `nowMs` ({@link ENVELOPE_WINDOW_MS} when absent); `names`, the header names ({@link DEFAULT_ENVELOPE_HEADERS} when absent).
50
+ * @returns `{ valid: true }`, or `valid: false` with why: a header missing, a timestamp that is not a number, a timestamp outside the window, or a signature that does not match.
51
+ */
52
+ export declare function verifyEnvelope(secret: string, headers: Record<string, string | string[] | undefined>, payload: string, nowMs: number, options?: {
53
+ windowMs?: number;
54
+ names?: EnvelopeHeaderNames;
55
+ }): {
56
+ valid: boolean;
57
+ error?: string;
58
+ };
59
+ /** 32 random bytes, base64url: the bearer a worker presents on every report of one run. */
60
+ export declare function mintWorkerToken(): string;
61
+ /** The SHA-256 of a worker token, in hex: what the run's dispatch record keeps, never the token itself. */
62
+ export declare function hashWorkerToken(token: string): string;
63
+ /**
64
+ * Whether `token` is the one whose hash is `hash`, compared in constant time.
65
+ *
66
+ * @param token - The bearer a report presented.
67
+ * @param hash - The {@link hashWorkerToken} hash the run's dispatch record keeps.
68
+ * @returns false as well when `hash` is not 64 hex digits.
69
+ */
70
+ export declare function workerTokenMatches(token: string, hash: string): boolean;
71
+ /**
72
+ * Seals a run's meeting url at rest under one 32-byte key the host holds, with
73
+ * the library's AES-256-GCM sealing ({@link sealSecret}, {@link openSecret}): a
74
+ * fresh nonce per seal, so one value seals to a different text each time, and
75
+ * the text `v1.box.<base64url(nonce | ciphertext | tag)>`. A context given to
76
+ * `seal` (the run's id) is bound into the text, so a text moved to another
77
+ * run's row does not open there.
78
+ */
79
+ export declare class SecretBox {
80
+ private readonly key;
81
+ /**
82
+ * @param key - The host's key: 32 bytes. The box keeps a copy, so a host that wipes or reuses its buffer afterwards does not change the box's key.
83
+ * @throws RangeError when `key` is not 32 bytes.
84
+ */
85
+ constructor(key: Buffer);
86
+ /**
87
+ * Seals `plain`; the sealed text differs on every call.
88
+ *
89
+ * @param plain - The text to seal.
90
+ * @param context - What the text belongs to (the run's id), bound into the text as additional authenticated data; `open` must be given the same. Empty when absent.
91
+ */
92
+ seal(plain: string, context?: string): string;
93
+ /**
94
+ * Opens a text this box's key sealed.
95
+ *
96
+ * @param sealed - The sealed text.
97
+ * @param context - The context the text was sealed with; empty when absent.
98
+ * @throws SealedSecretError when the text is not a sealed text, was sealed under another key or with another context, or was changed.
99
+ */
100
+ open(sealed: string, context?: string): string;
101
+ }
102
+ //# sourceMappingURL=signing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../../../src/io/channels/meetings/signing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,+IAA+I;AAC/I,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,iGAAiG;IACjG,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,kEAAkE;AAClE,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,mBAAmB,CAA8F,CAAC;AAClK,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAE1C;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrG;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,mBAA8C,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM/K;AAQD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,EACtD,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,mBAAmB,CAAA;CAAO,GAC/D;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAcpC;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,2GAA2G;AAC3G,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAMvE;AAKD;;;;;;;GAOG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IAEjC;;;OAGG;gBACS,GAAG,EAAE,MAAM;IAKvB;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,MAAM;IAIzC;;;;;;OAMG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,MAAM;CAG3C"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @module channels/meetings/signing
3
+ *
4
+ * Three ways the channel authenticates, kept apart: the envelope a host signs
5
+ * its outbound deliveries with, the per-run bearer token a worker presents on
6
+ * its reports, and the box that seals a run's meeting url at rest. A
7
+ * provider's own scheme (a platform's webhook signature) lives in that
8
+ * provider.
9
+ */
10
+ import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
11
+ import { openSecret, sealSecret } from '../../../core/llm/auth/sealing.js';
12
+ /** The header names the envelope uses when a host passes none. */
13
+ export const DEFAULT_ENVELOPE_HEADERS = { id: 'x-webhook-id', timestamp: 'x-webhook-timestamp', signature: 'x-webhook-signature' };
14
+ /** How far a delivery's timestamp may be from the receiver's clock. */
15
+ export const ENVELOPE_WINDOW_MS = 300000;
16
+ /**
17
+ * `v1,` then the base64 HMAC-SHA256 of `<id>.<timestamp>.<payload>` under the destination's secret.
18
+ *
19
+ * @param secret - The destination's secret.
20
+ * @param id - The delivery's id.
21
+ * @param timestampMs - The time the delivery is signed, in milliseconds since the epoch.
22
+ * @param payload - The body exactly as it is sent.
23
+ */
24
+ export function signEnvelope(secret, id, timestampMs, payload) {
25
+ const mac = createHmac('sha256', secret).update(`${id}.${timestampMs}.${payload}`).digest('base64');
26
+ return `v1,${mac}`;
27
+ }
28
+ /**
29
+ * The three headers of one delivery: its id, its time and its {@link signEnvelope} signature.
30
+ *
31
+ * @param secret - The destination's secret.
32
+ * @param id - The delivery's id.
33
+ * @param timestampMs - The time the delivery is signed, in milliseconds since the epoch.
34
+ * @param payload - The body exactly as it is sent.
35
+ * @param names - The header names; {@link DEFAULT_ENVELOPE_HEADERS} when absent.
36
+ */
37
+ export function envelopeHeaders(secret, id, timestampMs, payload, names = DEFAULT_ENVELOPE_HEADERS) {
38
+ return {
39
+ [names.id]: id,
40
+ [names.timestamp]: String(timestampMs),
41
+ [names.signature]: signEnvelope(secret, id, timestampMs, payload),
42
+ };
43
+ }
44
+ // A header by its name, or by its name in lower case (Node's form for incoming headers); the first of a repeated header.
45
+ function header(headers, name) {
46
+ const v = headers[name] ?? headers[name.toLowerCase()];
47
+ return Array.isArray(v) ? v[0] : v;
48
+ }
49
+ /**
50
+ * The receiving side: the three headers, the raw payload and the receiver's clock.
51
+ *
52
+ * @param secret - The destination's secret.
53
+ * @param headers - The delivery's headers.
54
+ * @param payload - The body exactly as it arrived, before any parse.
55
+ * @param nowMs - The receiver's clock, in milliseconds since the epoch.
56
+ * @param options - `windowMs`, how far the delivery's time may be from `nowMs` ({@link ENVELOPE_WINDOW_MS} when absent); `names`, the header names ({@link DEFAULT_ENVELOPE_HEADERS} when absent).
57
+ * @returns `{ valid: true }`, or `valid: false` with why: a header missing, a timestamp that is not a number, a timestamp outside the window, or a signature that does not match.
58
+ */
59
+ export function verifyEnvelope(secret, headers, payload, nowMs, options = {}) {
60
+ const names = options.names ?? DEFAULT_ENVELOPE_HEADERS;
61
+ const windowMs = options.windowMs ?? ENVELOPE_WINDOW_MS;
62
+ const id = header(headers, names.id);
63
+ const ts = header(headers, names.timestamp);
64
+ const sig = header(headers, names.signature);
65
+ if (!id || !ts || !sig)
66
+ return { valid: false, error: 'missing envelope header' };
67
+ const timestamp = Number(ts);
68
+ if (!Number.isFinite(timestamp))
69
+ return { valid: false, error: 'bad timestamp' };
70
+ if (Math.abs(nowMs - timestamp) > windowMs)
71
+ return { valid: false, error: 'outside the window' };
72
+ const expected = Buffer.from(signEnvelope(secret, id, timestamp, payload));
73
+ const given = Buffer.from(sig);
74
+ if (expected.length !== given.length || !timingSafeEqual(expected, given))
75
+ return { valid: false, error: 'signature mismatch' };
76
+ return { valid: true };
77
+ }
78
+ /** 32 random bytes, base64url: the bearer a worker presents on every report of one run. */
79
+ export function mintWorkerToken() {
80
+ return randomBytes(32).toString('base64url');
81
+ }
82
+ /** The SHA-256 of a worker token, in hex: what the run's dispatch record keeps, never the token itself. */
83
+ export function hashWorkerToken(token) {
84
+ return createHash('sha256').update(token).digest('hex');
85
+ }
86
+ /**
87
+ * Whether `token` is the one whose hash is `hash`, compared in constant time.
88
+ *
89
+ * @param token - The bearer a report presented.
90
+ * @param hash - The {@link hashWorkerToken} hash the run's dispatch record keeps.
91
+ * @returns false as well when `hash` is not 64 hex digits.
92
+ */
93
+ export function workerTokenMatches(token, hash) {
94
+ // hex decoding stops at the first character that is not a hex digit, so a longer value that starts with the hash would decode to it
95
+ if (!/^[0-9a-f]{64}$/i.test(hash))
96
+ return false;
97
+ const a = Buffer.from(hashWorkerToken(token), 'hex');
98
+ const b = Buffer.from(hash, 'hex');
99
+ return a.length === b.length && timingSafeEqual(a, b);
100
+ }
101
+ // The key id the box writes into each sealed text. It is part of the stored form: a text sealed under one id opens only under that id.
102
+ const SECRET_BOX_KEY_ID = 'box';
103
+ /**
104
+ * Seals a run's meeting url at rest under one 32-byte key the host holds, with
105
+ * the library's AES-256-GCM sealing ({@link sealSecret}, {@link openSecret}): a
106
+ * fresh nonce per seal, so one value seals to a different text each time, and
107
+ * the text `v1.box.<base64url(nonce | ciphertext | tag)>`. A context given to
108
+ * `seal` (the run's id) is bound into the text, so a text moved to another
109
+ * run's row does not open there.
110
+ */
111
+ export class SecretBox {
112
+ /**
113
+ * @param key - The host's key: 32 bytes. The box keeps a copy, so a host that wipes or reuses its buffer afterwards does not change the box's key.
114
+ * @throws RangeError when `key` is not 32 bytes.
115
+ */
116
+ constructor(key) {
117
+ if (key.length !== 32)
118
+ throw new RangeError('SecretBox needs a 32-byte key');
119
+ this.key = { id: SECRET_BOX_KEY_ID, key: Buffer.from(key) };
120
+ }
121
+ /**
122
+ * Seals `plain`; the sealed text differs on every call.
123
+ *
124
+ * @param plain - The text to seal.
125
+ * @param context - What the text belongs to (the run's id), bound into the text as additional authenticated data; `open` must be given the same. Empty when absent.
126
+ */
127
+ seal(plain, context = '') {
128
+ return sealSecret(plain, this.key.key, this.key.id, context);
129
+ }
130
+ /**
131
+ * Opens a text this box's key sealed.
132
+ *
133
+ * @param sealed - The sealed text.
134
+ * @param context - The context the text was sealed with; empty when absent.
135
+ * @throws SealedSecretError when the text is not a sealed text, was sealed under another key or with another context, or was changed.
136
+ */
137
+ open(sealed, context = '') {
138
+ return Buffer.from(openSecret(sealed, [this.key], context)).toString('utf8');
139
+ }
140
+ }
141
+ //# sourceMappingURL=signing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.js","sourceRoot":"","sources":["../../../../src/io/channels/meetings/signing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { type MeetingEvent, type MeetingRunRecord, type MeetingRunState, type TranscriptTurn } from '../types.js';
2
+ import { type IClock, type IEventStore, type IJobStore, type IOutbox, type IRunStore, type ITranscriptStore, type MeetingJob, type OutboxDelivery, type RunFilter } from './types.js';
3
+ /** The wall clock. */
4
+ export declare class SystemClock implements IClock {
5
+ now(): number;
6
+ }
7
+ /** A clock a test sets and moves by hand. */
8
+ export declare class FakeClock implements IClock {
9
+ private t;
10
+ /** @param t - The starting time, in milliseconds. */
11
+ constructor(t: number);
12
+ now(): number;
13
+ /** Moves the clock forward by `ms`. */
14
+ advance(ms: number): void;
15
+ /** Sets the clock to `t`. */
16
+ set(t: number): void;
17
+ }
18
+ /** Runs in memory, for tests and single-process hosts. Rows are copied in and out, so a caller never holds the stored row. */
19
+ export declare class InMemoryRunStore implements IRunStore {
20
+ private readonly runs;
21
+ insert(run: MeetingRunRecord): Promise<void>;
22
+ get(runId: string): Promise<MeetingRunRecord | undefined>;
23
+ update(runId: string, patch: Partial<MeetingRunRecord>, expectState?: MeetingRunState | readonly MeetingRunState[]): Promise<MeetingRunRecord | undefined>;
24
+ replace(run: MeetingRunRecord): Promise<void>;
25
+ list(filter: RunFilter): Promise<MeetingRunRecord[]>;
26
+ findByIdempotencyKey(ownerRef: string, key: string): Promise<MeetingRunRecord | undefined>;
27
+ }
28
+ /** Dispatch jobs in memory, for tests and single-process hosts: its leases hold within one process. */
29
+ export declare class InMemoryJobStore implements IJobStore {
30
+ private readonly jobs;
31
+ schedule(job: {
32
+ runId: string;
33
+ dispatchAt: number;
34
+ }): Promise<void>;
35
+ lease(holder: string, now: number, ttlMs: number): Promise<MeetingJob | undefined>;
36
+ renew(runId: string, holder: string, now: number, ttlMs: number): Promise<boolean>;
37
+ markDispatched(runId: string, holder: string, workerId: string | undefined): Promise<{
38
+ recorded: boolean;
39
+ cancelled: boolean;
40
+ }>;
41
+ release(runId: string, holder: string, retryAt: number): Promise<boolean>;
42
+ cancel(runId: string): Promise<'cancelled' | 'already_dispatched' | 'missing'>;
43
+ get(runId: string): Promise<MeetingJob | undefined>;
44
+ listDispatched(): Promise<MeetingJob[]>;
45
+ remove(runId: string): Promise<void>;
46
+ }
47
+ /** Transcript turns in memory: for tests, single-process hosts and runs that are not kept. */
48
+ export declare class InMemoryTranscriptStore implements ITranscriptStore {
49
+ private readonly turns;
50
+ private readonly fenced;
51
+ append(turn: TranscriptTurn): Promise<void>;
52
+ read(runId: string, fromSeq: number, limit?: number): Promise<TranscriptTurn[]>;
53
+ fence(runId: string): Promise<void>;
54
+ isFenced(runId: string): Promise<boolean>;
55
+ purge(runId: string): Promise<void>;
56
+ /** Forgets a run whole, its turns and its fence, to free the memory of a run that is over. The manager does not call it: it removes a run's turns with `purge`, which keeps the fence. */
57
+ drop(runId: string): Promise<void>;
58
+ }
59
+ /**
60
+ * Events in memory, numbered per run and deduplicated by id: for tests, single-process hosts and runs that are not kept.
61
+ * Events are copied in and out, payloads included, so a caller never holds a stored event.
62
+ */
63
+ export declare class InMemoryEventStore implements IEventStore {
64
+ private readonly byRun;
65
+ private readonly byId;
66
+ private readonly fenced;
67
+ append(event: Omit<MeetingEvent, 'seq'>): Promise<{
68
+ inserted: boolean;
69
+ seq: number;
70
+ }>;
71
+ fence(runId: string): Promise<void>;
72
+ read(runId: string, fromSeq: number, limit?: number): Promise<MeetingEvent[]>;
73
+ purgePayloads(runId: string): Promise<void>;
74
+ }
75
+ /** Outbound deliveries in memory, for tests and single-process hosts. */
76
+ export declare class InMemoryOutbox implements IOutbox {
77
+ private readonly deliveries;
78
+ /** The runs whose content was purged: the outbox's fence. */
79
+ private readonly purgedRuns;
80
+ enqueue(d: Pick<OutboxDelivery, 'id' | 'runId' | 'destinationRef' | 'eventId' | 'type' | 'payload'>, now: number): Promise<void>;
81
+ due(now: number, limit?: number): Promise<OutboxDelivery[]>;
82
+ markDelivered(id: string): Promise<void>;
83
+ markFailed(id: string, now: number): Promise<void>;
84
+ replay(id: string, now: number): Promise<void>;
85
+ purgeContent(runId: string): Promise<void>;
86
+ listByRun(runId: string): Promise<OutboxDelivery[]>;
87
+ }
88
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../../../src/io/channels/meetings/stores/memory.ts"],"names":[],"mappings":"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"}