@llui/agent 0.0.30 → 0.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/cloudflare/durable-object.d.ts +58 -0
- package/dist/server/cloudflare/durable-object.d.ts.map +1 -0
- package/dist/server/cloudflare/durable-object.js +33 -0
- package/dist/server/cloudflare/durable-object.js.map +1 -0
- package/dist/server/cloudflare/index.d.ts +49 -0
- package/dist/server/cloudflare/index.d.ts.map +1 -0
- package/dist/server/cloudflare/index.js +49 -0
- package/dist/server/cloudflare/index.js.map +1 -0
- package/dist/server/cloudflare/worker.d.ts +40 -0
- package/dist/server/cloudflare/worker.d.ts.map +1 -0
- package/dist/server/cloudflare/worker.js +55 -0
- package/dist/server/cloudflare/worker.js.map +1 -0
- package/dist/server/core-entry.d.ts +27 -0
- package/dist/server/core-entry.d.ts.map +1 -0
- package/dist/server/core-entry.js +19 -0
- package/dist/server/core-entry.js.map +1 -0
- package/dist/server/core.d.ts +78 -0
- package/dist/server/core.d.ts.map +1 -0
- package/dist/server/core.js +84 -0
- package/dist/server/core.js.map +1 -0
- package/dist/server/factory.d.ts +5 -3
- package/dist/server/factory.d.ts.map +1 -1
- package/dist/server/factory.js +18 -58
- package/dist/server/factory.js.map +1 -1
- package/dist/server/http/mint.d.ts.map +1 -1
- package/dist/server/http/mint.js +2 -3
- package/dist/server/http/mint.js.map +1 -1
- package/dist/server/http/resume.js +1 -1
- package/dist/server/http/resume.js.map +1 -1
- package/dist/server/identity.d.ts +5 -1
- package/dist/server/identity.d.ts.map +1 -1
- package/dist/server/identity.js +49 -11
- package/dist/server/identity.js.map +1 -1
- package/dist/server/index.d.ts +16 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +13 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/lap/confirm-result.d.ts +2 -2
- package/dist/server/lap/confirm-result.d.ts.map +1 -1
- package/dist/server/lap/confirm-result.js +1 -1
- package/dist/server/lap/confirm-result.js.map +1 -1
- package/dist/server/lap/describe.d.ts +4 -4
- package/dist/server/lap/describe.d.ts.map +1 -1
- package/dist/server/lap/describe.js +4 -4
- package/dist/server/lap/describe.js.map +1 -1
- package/dist/server/lap/forward.d.ts +2 -2
- package/dist/server/lap/forward.d.ts.map +1 -1
- package/dist/server/lap/forward.js +1 -1
- package/dist/server/lap/forward.js.map +1 -1
- package/dist/server/lap/message.d.ts +2 -2
- package/dist/server/lap/message.d.ts.map +1 -1
- package/dist/server/lap/message.js +1 -1
- package/dist/server/lap/message.js.map +1 -1
- package/dist/server/lap/observe.d.ts +2 -2
- package/dist/server/lap/observe.d.ts.map +1 -1
- package/dist/server/lap/observe.js +1 -1
- package/dist/server/lap/observe.js.map +1 -1
- package/dist/server/lap/wait.d.ts +2 -2
- package/dist/server/lap/wait.d.ts.map +1 -1
- package/dist/server/lap/wait.js +1 -1
- package/dist/server/lap/wait.js.map +1 -1
- package/dist/server/options.d.ts +25 -1
- package/dist/server/options.d.ts.map +1 -1
- package/dist/server/options.js.map +1 -1
- package/dist/server/token.d.ts +7 -3
- package/dist/server/token.d.ts.map +1 -1
- package/dist/server/token.js +66 -26
- package/dist/server/token.js.map +1 -1
- package/dist/server/web/adapter.d.ts +16 -0
- package/dist/server/web/adapter.d.ts.map +1 -0
- package/dist/server/web/adapter.js +45 -0
- package/dist/server/web/adapter.js.map +1 -0
- package/dist/server/web/index.d.ts +12 -0
- package/dist/server/web/index.d.ts.map +1 -0
- package/dist/server/web/index.js +12 -0
- package/dist/server/web/index.js.map +1 -0
- package/dist/server/web/upgrade.d.ts +41 -0
- package/dist/server/web/upgrade.d.ts.map +1 -0
- package/dist/server/web/upgrade.js +96 -0
- package/dist/server/web/upgrade.js.map +1 -0
- package/dist/server/ws/pairing-registry.d.ts +84 -21
- package/dist/server/ws/pairing-registry.d.ts.map +1 -1
- package/dist/server/ws/pairing-registry.js +89 -151
- package/dist/server/ws/pairing-registry.js.map +1 -1
- package/dist/server/ws/rpc.d.ts +39 -0
- package/dist/server/ws/rpc.d.ts.map +1 -0
- package/dist/server/ws/rpc.js +126 -0
- package/dist/server/ws/rpc.js.map +1 -0
- package/dist/server/ws/upgrade.d.ts +3 -3
- package/dist/server/ws/upgrade.d.ts.map +1 -1
- package/dist/server/ws/upgrade.js +2 -2
- package/dist/server/ws/upgrade.js.map +1 -1
- package/package.json +13 -1
package/dist/server/factory.js
CHANGED
|
@@ -1,69 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { consoleAuditSink } from './audit.js';
|
|
3
|
-
import { defaultRateLimiter } from './rate-limit.js';
|
|
4
|
-
import { createHttpRouter } from './http/router.js';
|
|
5
|
-
import { createLapRouter } from './lap/router.js';
|
|
6
|
-
import { WsPairingRegistry } from './ws/pairing-registry.js';
|
|
1
|
+
import { createLluiAgentCore } from './core.js';
|
|
7
2
|
import { createWsUpgradeHandler } from './ws/upgrade.js';
|
|
8
|
-
const ANONYMOUS_RESOLVER = async () => null;
|
|
9
3
|
/**
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
4
|
+
* Node adapter. Wraps the runtime-neutral core with a Node-specific
|
|
5
|
+
* `wsUpgrade` handler that uses the `ws` library. Imports `ws`
|
|
6
|
+
* eagerly, so this module only works where `ws` is available — use
|
|
7
|
+
* `@llui/agent/server/web` for Cloudflare Workers, Deno, or other
|
|
8
|
+
* WHATWG runtimes.
|
|
13
9
|
*
|
|
14
10
|
* Spec §10.1, §10.4.
|
|
15
11
|
*/
|
|
16
12
|
export function createLluiAgentServer(opts) {
|
|
17
|
-
|
|
18
|
-
throw new Error('createLluiAgentServer: signingKey is required');
|
|
19
|
-
}
|
|
20
|
-
const tokenStore = opts.tokenStore ?? new InMemoryTokenStore();
|
|
21
|
-
const identityResolver = opts.identityResolver ?? ANONYMOUS_RESOLVER;
|
|
22
|
-
const auditSink = opts.auditSink ?? consoleAuditSink;
|
|
23
|
-
const rateLimiter = opts.rateLimiter ?? defaultRateLimiter({ perBucket: '30/minute' });
|
|
24
|
-
const lapBasePath = opts.lapBasePath ?? '/agent/lap/v1';
|
|
25
|
-
const registry = new WsPairingRegistry({
|
|
26
|
-
onLogAppend: (tid, entry) => {
|
|
27
|
-
void auditSink.write({
|
|
28
|
-
at: entry.at,
|
|
29
|
-
tid,
|
|
30
|
-
uid: null,
|
|
31
|
-
event: 'lap-call',
|
|
32
|
-
detail: {
|
|
33
|
-
source: 'client-log',
|
|
34
|
-
kind: entry.kind,
|
|
35
|
-
variant: entry.variant,
|
|
36
|
-
intent: entry.intent,
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
const httpRouter = createHttpRouter({
|
|
42
|
-
signingKey: opts.signingKey,
|
|
43
|
-
tokenStore,
|
|
44
|
-
identityResolver,
|
|
45
|
-
auditSink,
|
|
46
|
-
lapBasePath,
|
|
47
|
-
});
|
|
48
|
-
const lapRouter = createLapRouter({
|
|
49
|
-
signingKey: opts.signingKey,
|
|
50
|
-
tokenStore,
|
|
51
|
-
registry,
|
|
52
|
-
auditSink,
|
|
53
|
-
rateLimiter,
|
|
54
|
-
}, lapBasePath);
|
|
55
|
-
const router = async (req) => {
|
|
56
|
-
const lapRes = await lapRouter(req);
|
|
57
|
-
if (lapRes)
|
|
58
|
-
return lapRes;
|
|
59
|
-
return httpRouter(req);
|
|
60
|
-
};
|
|
13
|
+
const core = createLluiAgentCore(opts);
|
|
61
14
|
const wsUpgrade = createWsUpgradeHandler({
|
|
62
15
|
signingKey: opts.signingKey,
|
|
63
|
-
tokenStore,
|
|
64
|
-
registry,
|
|
65
|
-
auditSink,
|
|
16
|
+
tokenStore: core.tokenStore,
|
|
17
|
+
registry: core.registry,
|
|
18
|
+
auditSink: core.auditSink,
|
|
66
19
|
});
|
|
67
|
-
return {
|
|
20
|
+
return {
|
|
21
|
+
router: core.router,
|
|
22
|
+
wsUpgrade,
|
|
23
|
+
registry: core.registry,
|
|
24
|
+
tokenStore: core.tokenStore,
|
|
25
|
+
auditSink: core.auditSink,
|
|
26
|
+
acceptConnection: core.acceptConnection,
|
|
27
|
+
};
|
|
68
28
|
}
|
|
69
29
|
//# sourceMappingURL=factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAExD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAmB;IACvD,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAA;IAEtC,MAAM,SAAS,GAAG,sBAAsB,CAAC;QACvC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAA;IAEF,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS;QACT,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;KACxC,CAAA;AACH,CAAC","sourcesContent":["import type { ServerOptions, AgentServerHandle } from './options.js'\nimport { createLluiAgentCore } from './core.js'\nimport { createWsUpgradeHandler } from './ws/upgrade.js'\n\n/**\n * Node adapter. Wraps the runtime-neutral core with a Node-specific\n * `wsUpgrade` handler that uses the `ws` library. Imports `ws`\n * eagerly, so this module only works where `ws` is available — use\n * `@llui/agent/server/web` for Cloudflare Workers, Deno, or other\n * WHATWG runtimes.\n *\n * Spec §10.1, §10.4.\n */\nexport function createLluiAgentServer(opts: ServerOptions): AgentServerHandle {\n const core = createLluiAgentCore(opts)\n\n const wsUpgrade = createWsUpgradeHandler({\n signingKey: opts.signingKey,\n tokenStore: core.tokenStore,\n registry: core.registry,\n auditSink: core.auditSink,\n })\n\n return {\n router: core.router,\n wsUpgrade,\n registry: core.registry,\n tokenStore: core.tokenStore,\n auditSink: core.auditSink,\n acceptConnection: core.acceptConnection,\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mint.d.ts","sourceRoot":"","sources":["../../../src/server/http/mint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"mint.d.ts","sourceRoot":"","sources":["../../../src/server/http/mint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAI5C,MAAM,MAAM,QAAQ,GAAG;IACrB,UAAU,EAAE,MAAM,GAAG,UAAU,CAAA;IAC/B,UAAU,EAAE,UAAU,CAAA;IACtB,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,MAAM,CAAA;IACnB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwDhF"}
|
package/dist/server/http/mint.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { signToken } from '../token.js';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
2
|
/**
|
|
4
3
|
* POST /agent/mint — creates a pairing record and returns the mint
|
|
5
4
|
* response. See spec §6.2. The caller is responsible for routing
|
|
@@ -13,7 +12,7 @@ export async function handleMint(req, deps) {
|
|
|
13
12
|
});
|
|
14
13
|
}
|
|
15
14
|
const now = deps.now ?? (() => Date.now());
|
|
16
|
-
const uuid = deps.uuid ?? randomUUID;
|
|
15
|
+
const uuid = deps.uuid ?? (() => crypto.randomUUID());
|
|
17
16
|
const hardExpiryMs = deps.hardExpiryMs ?? 24 * 60 * 60 * 1000;
|
|
18
17
|
const uid = await deps.identityResolver(req);
|
|
19
18
|
const tid = uuid();
|
|
@@ -22,7 +21,7 @@ export async function handleMint(req, deps) {
|
|
|
22
21
|
const exp = Math.floor((nowMs + hardExpiryMs) / 1000);
|
|
23
22
|
const origin = new URL(req.url).origin;
|
|
24
23
|
const payload = { tid, iat, exp, scope: 'agent' };
|
|
25
|
-
const token = signToken(payload, deps.signingKey);
|
|
24
|
+
const token = await signToken(payload, deps.signingKey);
|
|
26
25
|
const record = {
|
|
27
26
|
tid,
|
|
28
27
|
uid,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mint.js","sourceRoot":"","sources":["../../../src/server/http/mint.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"mint.js","sourceRoot":"","sources":["../../../src/server/http/mint.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TokenStore } from '../token-store.js'\nimport type { IdentityResolver } from '../identity.js'\nimport type { AuditSink } from '../audit.js'\nimport { signToken } from '../token.js'\nimport type { MintResponse, TokenPayload, TokenRecord } from '../../protocol.js'\n\nexport type MintDeps = {\n signingKey: string | Uint8Array\n tokenStore: TokenStore\n identityResolver: IdentityResolver\n auditSink: AuditSink\n lapBasePath: string\n /** Wall-clock in milliseconds; injectable for tests. */\n now?: () => number\n /** UUID generator; injectable for tests. */\n uuid?: () => string\n /** Hard-expiry window, default 24 h. */\n hardExpiryMs?: number\n}\n\n/**\n * POST /agent/mint — creates a pairing record and returns the mint\n * response. See spec §6.2. The caller is responsible for routing\n * `/agent/mint` requests to this handler; `router.ts` composes that.\n */\nexport async function handleMint(req: Request, deps: MintDeps): Promise<Response> {\n if (req.method !== 'POST') {\n return new Response(JSON.stringify({ error: { code: 'method-not-allowed' } }), {\n status: 405,\n headers: { 'content-type': 'application/json' },\n })\n }\n\n const now = deps.now ?? (() => Date.now())\n const uuid = deps.uuid ?? (() => crypto.randomUUID())\n const hardExpiryMs = deps.hardExpiryMs ?? 24 * 60 * 60 * 1000\n\n const uid = await deps.identityResolver(req)\n const tid = uuid()\n const nowMs = now()\n const iat = Math.floor(nowMs / 1000)\n const exp = Math.floor((nowMs + hardExpiryMs) / 1000)\n const origin = new URL(req.url).origin\n\n const payload: TokenPayload = { tid, iat, exp, scope: 'agent' }\n const token = await signToken(payload, deps.signingKey)\n\n const record: TokenRecord = {\n tid,\n uid,\n status: 'awaiting-ws',\n createdAt: nowMs,\n lastSeenAt: nowMs,\n pendingResumeUntil: null,\n origin,\n label: null,\n }\n await deps.tokenStore.create(record)\n\n await deps.auditSink.write({\n at: nowMs,\n tid,\n uid,\n event: 'mint',\n detail: { origin },\n })\n\n const wsUrl = toWsUrl(new URL(req.url).origin) + '/agent/ws'\n const lapUrl = new URL(deps.lapBasePath, origin).toString()\n\n const body: MintResponse = {\n token,\n tid,\n wsUrl,\n lapUrl,\n expiresAt: exp,\n }\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: { 'content-type': 'application/json' },\n })\n}\n\nfunction toWsUrl(httpOrigin: string): string {\n return httpOrigin.startsWith('https://')\n ? 'wss://' + httpOrigin.slice('https://'.length)\n : 'ws://' + httpOrigin.slice('http://'.length)\n}\n"]}
|
|
@@ -53,7 +53,7 @@ export async function handleResumeClaim(req, deps) {
|
|
|
53
53
|
const iat = Math.floor(nowMs / 1000);
|
|
54
54
|
const exp = Math.floor((nowMs + hardExpiryMs) / 1000);
|
|
55
55
|
const payload = { tid: rec.tid, iat, exp, scope: 'agent' };
|
|
56
|
-
const token = signToken(payload, deps.signingKey);
|
|
56
|
+
const token = await signToken(payload, deps.signingKey);
|
|
57
57
|
await deps.tokenStore.markActive(rec.tid, rec.label ?? '(resumed)', nowMs);
|
|
58
58
|
await deps.auditSink.write({
|
|
59
59
|
at: nowMs,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resume.js","sourceRoot":"","sources":["../../../src/server/http/resume.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAmBvC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAY,EAAE,IAAgB;IACnE,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,gBAAgB,EAAE,CAAA;IACpD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAA6B,CAAA;IAC7E,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,UAAU,EAAE,CAAA;IAE3D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAC5C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAA;IAChD,MAAM,GAAG,GAAmB,EAAE,CAAA;IAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QAChD,IAAI,CAAC,GAAG;YAAE,SAAQ;QAClB,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG;YAAE,SAAQ;QAC7B,IAAI,GAAG,CAAC,MAAM,KAAK,gBAAgB;YAAE,SAAQ;QAC7C,IAAI,GAAG,CAAC,kBAAkB,KAAK,IAAI,IAAI,GAAG,CAAC,kBAAkB,GAAG,KAAK;YAAE,SAAQ;QAC/E,GAAG,CAAC,IAAI,CAAC;YACP,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,WAAW;YAC/B,MAAM,EAAE,gBAAgB;YACxB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,UAAU,EAAE,GAAG,CAAC,UAAU;SAC3B,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,OAAO,GAAuB,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAA;IACrD,OAAO,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAY,EAAE,IAAgB;IACpE,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,gBAAgB,EAAE,CAAA;IACpD,IAAI,CAAC,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAEhE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAA8B,CAAA;IAC9E,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ;QAAE,OAAO,UAAU,EAAE,CAAA;IAE9D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAC5C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACrD,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,EAAE,CAAA;IAC5B,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG;QAAE,OAAO,SAAS,EAAE,CAAA;IACvC,IAAI,GAAG,CAAC,MAAM,KAAK,gBAAgB;QAAE,OAAO,SAAS,EAAE,CAAA;IAEvD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAA;IACtC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,SAAS,EAAE,CAAA;IAE7C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAA;IAChD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC,CAAA;IACrD,MAAM,OAAO,GAAiB,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IACxE,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"resume.js","sourceRoot":"","sources":["../../../src/server/http/resume.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TokenStore } from '../token-store.js'\nimport type { IdentityResolver } from '../identity.js'\nimport type { AuditSink } from '../audit.js'\nimport { signToken } from '../token.js'\nimport type {\n ResumeListRequest,\n ResumeListResponse,\n ResumeClaimRequest,\n ResumeClaimResponse,\n TokenPayload,\n AgentSession,\n} from '../../protocol.js'\n\nexport type ResumeDeps = {\n tokenStore: TokenStore\n identityResolver: IdentityResolver\n auditSink: AuditSink\n signingKey?: string | Uint8Array\n now?: () => number\n hardExpiryMs?: number\n}\n\nexport async function handleResumeList(req: Request, deps: ResumeDeps): Promise<Response> {\n if (req.method !== 'POST') return methodNotAllowed()\n const body = (await req.json().catch(() => null)) as ResumeListRequest | null\n if (!body || !Array.isArray(body.tids)) return badRequest()\n\n const uid = await deps.identityResolver(req)\n const nowMs = (deps.now ?? (() => Date.now()))()\n const out: AgentSession[] = []\n for (const tid of body.tids) {\n const rec = await deps.tokenStore.findByTid(tid)\n if (!rec) continue\n if (rec.uid !== uid) continue\n if (rec.status !== 'pending-resume') continue\n if (rec.pendingResumeUntil === null || rec.pendingResumeUntil < nowMs) continue\n out.push({\n tid: rec.tid,\n label: rec.label ?? '(unknown)',\n status: 'pending-resume',\n createdAt: rec.createdAt,\n lastSeenAt: rec.lastSeenAt,\n })\n }\n\n const payload: ResumeListResponse = { sessions: out }\n return jsonResponse(payload, 200)\n}\n\nexport async function handleResumeClaim(req: Request, deps: ResumeDeps): Promise<Response> {\n if (req.method !== 'POST') return methodNotAllowed()\n if (!deps.signingKey) return new Response(null, { status: 500 })\n\n const body = (await req.json().catch(() => null)) as ResumeClaimRequest | null\n if (!body || typeof body.tid !== 'string') return badRequest()\n\n const uid = await deps.identityResolver(req)\n const rec = await deps.tokenStore.findByTid(body.tid)\n if (!rec) return forbidden()\n if (rec.uid !== uid) return forbidden()\n if (rec.status !== 'pending-resume') return forbidden()\n\n const origin = new URL(req.url).origin\n if (rec.origin !== origin) return forbidden()\n\n const nowMs = (deps.now ?? (() => Date.now()))()\n const hardExpiryMs = deps.hardExpiryMs ?? 24 * 60 * 60 * 1000\n const iat = Math.floor(nowMs / 1000)\n const exp = Math.floor((nowMs + hardExpiryMs) / 1000)\n const payload: TokenPayload = { tid: rec.tid, iat, exp, scope: 'agent' }\n const token = await signToken(payload, deps.signingKey)\n\n await deps.tokenStore.markActive(rec.tid, rec.label ?? '(resumed)', nowMs)\n\n await deps.auditSink.write({\n at: nowMs,\n tid: rec.tid,\n uid: rec.uid,\n event: 'claim',\n detail: { origin },\n })\n\n const wsUrl = toWsUrl(origin) + '/agent/ws'\n const out: ResumeClaimResponse = { token, wsUrl }\n return jsonResponse(out, 200)\n}\n\nfunction jsonResponse(body: unknown, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\nfunction methodNotAllowed(): Response {\n return jsonResponse({ error: { code: 'method-not-allowed' } }, 405)\n}\n\nfunction badRequest(): Response {\n return jsonResponse({ error: { code: 'invalid' } }, 400)\n}\n\nfunction forbidden(): Response {\n return jsonResponse({ error: { code: 'revoked' } }, 403)\n}\n\nfunction toWsUrl(httpOrigin: string): string {\n return httpOrigin.startsWith('https://')\n ? 'wss://' + httpOrigin.slice('https://'.length)\n : 'ws://' + httpOrigin.slice('http://'.length)\n}\n"]}
|
|
@@ -4,5 +4,9 @@ export type IdentityCookieConfig = {
|
|
|
4
4
|
signingKey: string | Uint8Array;
|
|
5
5
|
};
|
|
6
6
|
export declare function defaultIdentityResolver(cfg: IdentityCookieConfig): IdentityResolver;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Async because `crypto.subtle.sign` is the cross-runtime standard.
|
|
9
|
+
* Callers building a `Set-Cookie` header must `await` this.
|
|
10
|
+
*/
|
|
11
|
+
export declare function signCookieValue(value: string, signingKey: string | Uint8Array): Promise<string>;
|
|
8
12
|
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/server/identity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/server/identity.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AAEvE,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,UAAU,CAAA;CAChC,CAAA;AA4CD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,oBAAoB,GAAG,gBAAgB,CA4BnF;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GAAG,UAAU,GAC9B,OAAO,CAAC,MAAM,CAAC,CAIjB"}
|
package/dist/server/identity.js
CHANGED
|
@@ -1,11 +1,42 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Normalize to `Uint8Array<ArrayBuffer>` — WebCrypto rejects
|
|
3
|
+
* `Uint8Array<ArrayBufferLike>` which is what `TextEncoder.encode()`
|
|
4
|
+
* produces under newer TS lib types. One-shot copy is cheap.
|
|
5
|
+
*/
|
|
6
|
+
function toBytes(input) {
|
|
7
|
+
const raw = typeof input === 'string' ? new TextEncoder().encode(input) : input;
|
|
8
|
+
const buf = new ArrayBuffer(raw.byteLength);
|
|
9
|
+
const out = new Uint8Array(buf);
|
|
10
|
+
out.set(raw);
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
async function importHmacKey(key, usages) {
|
|
14
|
+
return crypto.subtle.importKey('raw', toBytes(key), { name: 'HMAC', hash: 'SHA-256' }, false, usages);
|
|
15
|
+
}
|
|
16
|
+
function toBase64Url(bytes) {
|
|
17
|
+
let bin = '';
|
|
18
|
+
for (let i = 0; i < bytes.byteLength; i++)
|
|
19
|
+
bin += String.fromCharCode(bytes[i]);
|
|
20
|
+
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
21
|
+
}
|
|
22
|
+
function fromBase64Url(s) {
|
|
23
|
+
try {
|
|
24
|
+
const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4);
|
|
25
|
+
const bin = atob(b64);
|
|
26
|
+
const buf = new ArrayBuffer(bin.length);
|
|
27
|
+
const bytes = new Uint8Array(buf);
|
|
28
|
+
for (let i = 0; i < bin.length; i++)
|
|
29
|
+
bytes[i] = bin.charCodeAt(i);
|
|
30
|
+
return bytes;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
2
36
|
export function defaultIdentityResolver(cfg) {
|
|
3
37
|
if (!cfg.signingKey || (typeof cfg.signingKey === 'string' && cfg.signingKey.length < 32)) {
|
|
4
38
|
throw new Error('IdentityCookie signingKey must be at least 32 bytes');
|
|
5
39
|
}
|
|
6
|
-
const keyBuf = typeof cfg.signingKey === 'string'
|
|
7
|
-
? Buffer.from(cfg.signingKey, 'utf8')
|
|
8
|
-
: Buffer.from(cfg.signingKey);
|
|
9
40
|
return async (req) => {
|
|
10
41
|
const cookie = req.headers.get('cookie');
|
|
11
42
|
if (!cookie)
|
|
@@ -24,18 +55,25 @@ export function defaultIdentityResolver(cfg) {
|
|
|
24
55
|
return null;
|
|
25
56
|
const rawValue = value.slice(0, dot);
|
|
26
57
|
const sigPart = value.slice(dot + 1);
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
58
|
+
const sigBytes = fromBase64Url(sigPart);
|
|
59
|
+
if (!sigBytes)
|
|
60
|
+
return null;
|
|
61
|
+
const cryptoKey = await importHmacKey(cfg.signingKey, ['verify']);
|
|
62
|
+
const ok = await crypto.subtle.verify('HMAC', cryptoKey, sigBytes, toBytes(rawValue));
|
|
63
|
+
if (!ok)
|
|
30
64
|
return null;
|
|
31
65
|
return rawValue;
|
|
32
66
|
}
|
|
33
67
|
return null;
|
|
34
68
|
};
|
|
35
69
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Async because `crypto.subtle.sign` is the cross-runtime standard.
|
|
72
|
+
* Callers building a `Set-Cookie` header must `await` this.
|
|
73
|
+
*/
|
|
74
|
+
export async function signCookieValue(value, signingKey) {
|
|
75
|
+
const cryptoKey = await importHmacKey(signingKey, ['sign']);
|
|
76
|
+
const macBuf = await crypto.subtle.sign('HMAC', cryptoKey, toBytes(value));
|
|
77
|
+
return `${value}.${toBase64Url(new Uint8Array(macBuf))}`;
|
|
40
78
|
}
|
|
41
79
|
//# sourceMappingURL=identity.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/server/identity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/server/identity.ts"],"names":[],"mappings":"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","sourcesContent":["export type IdentityResolver = (req: Request) => Promise<string | null>\n\nexport type IdentityCookieConfig = {\n name: string\n signingKey: string | Uint8Array\n}\n\n/**\n * Normalize to `Uint8Array<ArrayBuffer>` — WebCrypto rejects\n * `Uint8Array<ArrayBufferLike>` which is what `TextEncoder.encode()`\n * produces under newer TS lib types. One-shot copy is cheap.\n */\nfunction toBytes(input: string | Uint8Array): Uint8Array<ArrayBuffer> {\n const raw = typeof input === 'string' ? new TextEncoder().encode(input) : input\n const buf = new ArrayBuffer(raw.byteLength)\n const out = new Uint8Array(buf)\n out.set(raw)\n return out\n}\n\nasync function importHmacKey(key: string | Uint8Array, usages: KeyUsage[]): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n toBytes(key),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n usages,\n )\n}\n\nfunction toBase64Url(bytes: Uint8Array): string {\n let bin = ''\n for (let i = 0; i < bytes.byteLength; i++) bin += String.fromCharCode(bytes[i]!)\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromBase64Url(s: string): Uint8Array<ArrayBuffer> | null {\n try {\n const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4)\n const bin = atob(b64)\n const buf = new ArrayBuffer(bin.length)\n const bytes = new Uint8Array(buf)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes\n } catch {\n return null\n }\n}\n\nexport function defaultIdentityResolver(cfg: IdentityCookieConfig): IdentityResolver {\n if (!cfg.signingKey || (typeof cfg.signingKey === 'string' && cfg.signingKey.length < 32)) {\n throw new Error('IdentityCookie signingKey must be at least 32 bytes')\n }\n\n return async (req) => {\n const cookie = req.headers.get('cookie')\n if (!cookie) return null\n const pairs = cookie.split(';').map((s) => s.trim())\n for (const pair of pairs) {\n const eq = pair.indexOf('=')\n if (eq < 0) continue\n const name = pair.slice(0, eq)\n const value = pair.slice(eq + 1)\n if (name !== cfg.name) continue\n const dot = value.indexOf('.')\n if (dot < 0) return null\n const rawValue = value.slice(0, dot)\n const sigPart = value.slice(dot + 1)\n const sigBytes = fromBase64Url(sigPart)\n if (!sigBytes) return null\n const cryptoKey = await importHmacKey(cfg.signingKey, ['verify'])\n const ok = await crypto.subtle.verify('HMAC', cryptoKey, sigBytes, toBytes(rawValue))\n if (!ok) return null\n return rawValue\n }\n return null\n }\n}\n\n/**\n * Async because `crypto.subtle.sign` is the cross-runtime standard.\n * Callers building a `Set-Cookie` header must `await` this.\n */\nexport async function signCookieValue(\n value: string,\n signingKey: string | Uint8Array,\n): Promise<string> {\n const cryptoKey = await importHmacKey(signingKey, ['sign'])\n const macBuf = await crypto.subtle.sign('HMAC', cryptoKey, toBytes(value))\n return `${value}.${toBase64Url(new Uint8Array(macBuf))}`\n}\n"]}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default entry for Node server processes. Bundles the runtime-neutral
|
|
3
|
+
* core with a `ws`-library-based WebSocket upgrade handler. For web
|
|
4
|
+
* runtimes (Cloudflare Workers, Deno, Bun) where `ws` isn't
|
|
5
|
+
* available, use `@llui/agent/server/core` + `@llui/agent/server/web`
|
|
6
|
+
* instead.
|
|
7
|
+
*/
|
|
1
8
|
export { createLluiAgentServer } from './factory.js';
|
|
2
9
|
export type { ServerOptions, AgentServerHandle } from './options.js';
|
|
10
|
+
export { createLluiAgentCore } from './core.js';
|
|
11
|
+
export type { CoreOptions, AgentCoreHandle, AcceptResult } from './core.js';
|
|
12
|
+
export { InMemoryPairingRegistry } from './ws/pairing-registry.js';
|
|
13
|
+
export type { PairingConnection, PairingRegistry, FrameSubscriber } from './ws/pairing-registry.js';
|
|
14
|
+
export { rpc, waitForConfirm, waitForChange } from './ws/rpc.js';
|
|
15
|
+
export type { RpcOptions, RpcError } from './ws/rpc.js';
|
|
3
16
|
export { InMemoryTokenStore } from './token-store.js';
|
|
4
17
|
export type { TokenStore } from './token-store.js';
|
|
5
|
-
export { defaultIdentityResolver } from './identity.js';
|
|
18
|
+
export { defaultIdentityResolver, signCookieValue } from './identity.js';
|
|
6
19
|
export type { IdentityResolver } from './identity.js';
|
|
7
20
|
export { consoleAuditSink } from './audit.js';
|
|
8
21
|
export type { AuditSink } from './audit.js';
|
|
9
22
|
export { defaultRateLimiter } from './rate-limit.js';
|
|
10
23
|
export type { RateLimiter } from './rate-limit.js';
|
|
24
|
+
export { signToken, verifyToken } from './token.js';
|
|
25
|
+
export type { TokenPayload, VerifyResult } from './token.js';
|
|
11
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAC/C,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAClE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACnG,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACxE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACnD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default entry for Node server processes. Bundles the runtime-neutral
|
|
3
|
+
* core with a `ws`-library-based WebSocket upgrade handler. For web
|
|
4
|
+
* runtimes (Cloudflare Workers, Deno, Bun) where `ws` isn't
|
|
5
|
+
* available, use `@llui/agent/server/core` + `@llui/agent/server/web`
|
|
6
|
+
* instead.
|
|
7
|
+
*/
|
|
1
8
|
export { createLluiAgentServer } from './factory.js';
|
|
9
|
+
// Runtime-neutral core — re-exported so Node users don't need a second import.
|
|
10
|
+
export { createLluiAgentCore } from './core.js';
|
|
11
|
+
export { InMemoryPairingRegistry } from './ws/pairing-registry.js';
|
|
12
|
+
export { rpc, waitForConfirm, waitForChange } from './ws/rpc.js';
|
|
2
13
|
export { InMemoryTokenStore } from './token-store.js';
|
|
3
|
-
export { defaultIdentityResolver } from './identity.js';
|
|
14
|
+
export { defaultIdentityResolver, signCookieValue } from './identity.js';
|
|
4
15
|
export { consoleAuditSink } from './audit.js';
|
|
5
16
|
export { defaultRateLimiter } from './rate-limit.js';
|
|
17
|
+
export { signToken, verifyToken } from './token.js';
|
|
6
18
|
//# sourceMappingURL=index.js.map
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,+EAA+E;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAE/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAElE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA","sourcesContent":["/**\n * Default entry for Node server processes. Bundles the runtime-neutral\n * core with a `ws`-library-based WebSocket upgrade handler. For web\n * runtimes (Cloudflare Workers, Deno, Bun) where `ws` isn't\n * available, use `@llui/agent/server/core` + `@llui/agent/server/web`\n * instead.\n */\nexport { createLluiAgentServer } from './factory.js'\nexport type { ServerOptions, AgentServerHandle } from './options.js'\n// Runtime-neutral core — re-exported so Node users don't need a second import.\nexport { createLluiAgentCore } from './core.js'\nexport type { CoreOptions, AgentCoreHandle, AcceptResult } from './core.js'\nexport { InMemoryPairingRegistry } from './ws/pairing-registry.js'\nexport type { PairingConnection, PairingRegistry, FrameSubscriber } from './ws/pairing-registry.js'\nexport { rpc, waitForConfirm, waitForChange } from './ws/rpc.js'\nexport type { RpcOptions, RpcError } from './ws/rpc.js'\nexport { InMemoryTokenStore } from './token-store.js'\nexport type { TokenStore } from './token-store.js'\nexport { defaultIdentityResolver, signCookieValue } from './identity.js'\nexport type { IdentityResolver } from './identity.js'\nexport { consoleAuditSink } from './audit.js'\nexport type { AuditSink } from './audit.js'\nexport { defaultRateLimiter } from './rate-limit.js'\nexport type { RateLimiter } from './rate-limit.js'\nexport { signToken, verifyToken } from './token.js'\nexport type { TokenPayload, VerifyResult } from './token.js'\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { TokenStore } from '../token-store.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PairingRegistry } from '../ws/pairing-registry.js';
|
|
3
3
|
import type { AuditSink } from '../audit.js';
|
|
4
4
|
import type { RateLimiter } from '../rate-limit.js';
|
|
5
5
|
export type LapConfirmResultDeps = {
|
|
6
6
|
signingKey: string | Uint8Array;
|
|
7
7
|
tokenStore: TokenStore;
|
|
8
|
-
registry:
|
|
8
|
+
registry: PairingRegistry;
|
|
9
9
|
auditSink: AuditSink;
|
|
10
10
|
rateLimiter: RateLimiter;
|
|
11
11
|
now?: () => number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm-result.d.ts","sourceRoot":"","sources":["../../../src/server/lap/confirm-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"confirm-result.d.ts","sourceRoot":"","sources":["../../../src/server/lap/confirm-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAInD,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,MAAM,GAAG,UAAU,CAAA;IAC/B,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,eAAe,CAAA;IACzB,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,WAAW,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB,CAAA;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,QAAQ,CAAC,CA0DnB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { verifyAndReadTid } from './describe.js';
|
|
2
2
|
export async function handleLapConfirmResult(req, deps) {
|
|
3
|
-
const auth = verifyAndReadTid(req, deps.signingKey);
|
|
3
|
+
const auth = await verifyAndReadTid(req, deps.signingKey);
|
|
4
4
|
if (!auth.ok)
|
|
5
5
|
return json({ error: { code: auth.code } }, auth.status);
|
|
6
6
|
const rec = await deps.tokenStore.findByTid(auth.tid);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm-result.js","sourceRoot":"","sources":["../../../src/server/lap/confirm-result.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAYhD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,GAAY,EACZ,IAA0B;IAE1B,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"confirm-result.js","sourceRoot":"","sources":["../../../src/server/lap/confirm-result.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TokenStore } from '../token-store.js'\nimport type { PairingRegistry } from '../ws/pairing-registry.js'\nimport type { AuditSink } from '../audit.js'\nimport type { RateLimiter } from '../rate-limit.js'\nimport { verifyAndReadTid } from './describe.js'\nimport type { LapConfirmResultRequest, LapConfirmResultResponse } from '../../protocol.js'\n\nexport type LapConfirmResultDeps = {\n signingKey: string | Uint8Array\n tokenStore: TokenStore\n registry: PairingRegistry\n auditSink: AuditSink\n rateLimiter: RateLimiter\n now?: () => number\n}\n\nexport async function handleLapConfirmResult(\n req: Request,\n deps: LapConfirmResultDeps,\n): Promise<Response> {\n const auth = await verifyAndReadTid(req, deps.signingKey)\n if (!auth.ok) return json({ error: { code: auth.code } }, auth.status)\n\n const rec = await deps.tokenStore.findByTid(auth.tid)\n if (!rec || rec.status === 'revoked') return json({ error: { code: 'revoked' } }, 403)\n if (!deps.registry.isPaired(auth.tid)) return json({ error: { code: 'paused' } }, 503)\n\n const rlCheck = await deps.rateLimiter.check(auth.tid, 'token')\n if (!rlCheck.allowed) {\n return json({ error: { code: 'rate-limited', retryAfterMs: rlCheck.retryAfterMs } }, 429)\n }\n\n const body = (await req.json().catch(() => null)) as LapConfirmResultRequest | null\n if (!body || typeof body.confirmId !== 'string') return json({ error: { code: 'invalid' } }, 400)\n const timeoutMs = body.timeoutMs ?? 5_000\n\n // Spec: if the confirm was already resolved during the earlier long-poll on\n // /message, there's no second resolution to wait for. In the current design\n // /confirm-result is ONLY used when /message bailed out early with\n // pending-confirmation. So we call waitForConfirm with the given timeoutMs.\n // If no resolution arrives in time, we surface 'still-pending'.\n const result = await deps.registry.waitForConfirm(auth.tid, body.confirmId, timeoutMs)\n\n const nowMs = (deps.now ?? (() => Date.now()))()\n if (result.outcome === 'confirmed') {\n await deps.auditSink.write({\n at: nowMs,\n tid: auth.tid,\n uid: rec.uid,\n event: 'confirm-approved',\n detail: { confirmId: body.confirmId },\n })\n return json(\n { status: 'confirmed', stateAfter: result.stateAfter } satisfies LapConfirmResultResponse,\n 200,\n )\n }\n // user-cancelled OR timeout. WsPairingRegistry returns user-cancelled on timeout too;\n // we distinguish by checking whether the confirm is still in registry.pendingConfirm —\n // but pendingConfirm cleanup happens inside waitForConfirm's timer, so we can't peek.\n // For v1: treat user-cancelled as user-cancelled; treat explicit timeout as timeout by\n // comparing elapsed vs. timeoutMs. Simpler: just return 'still-pending' on the timeout\n // branch to let Claude poll again. Registry returns {outcome: 'user-cancelled'} on\n // both timer and actual cancel — so we can't distinguish. Punt: return 'user-cancelled'\n // (matches registry semantics). Spec §8.2 get_confirm_result allows 'user-cancelled' |\n // 'timeout' | 'still-pending' — a refinement to distinguish is follow-up work.\n await deps.auditSink.write({\n at: nowMs,\n tid: auth.tid,\n uid: rec.uid,\n event: 'confirm-rejected',\n detail: { confirmId: body.confirmId },\n })\n return json(\n { status: 'rejected', reason: 'user-cancelled' } satisfies LapConfirmResultResponse,\n 200,\n )\n}\n\nfunction json(b: unknown, s: number): Response {\n return new Response(JSON.stringify(b), {\n status: s,\n headers: { 'content-type': 'application/json' },\n })\n}\n"]}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import type { TokenStore } from '../token-store.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PairingRegistry } from '../ws/pairing-registry.js';
|
|
3
3
|
import type { AuditSink } from '../audit.js';
|
|
4
4
|
import type { RateLimiter } from '../rate-limit.js';
|
|
5
5
|
export type LapDescribeDeps = {
|
|
6
6
|
signingKey: string | Uint8Array;
|
|
7
7
|
tokenStore: TokenStore;
|
|
8
|
-
registry:
|
|
8
|
+
registry: PairingRegistry;
|
|
9
9
|
auditSink: AuditSink;
|
|
10
10
|
rateLimiter: RateLimiter;
|
|
11
11
|
now?: () => number;
|
|
12
12
|
};
|
|
13
13
|
export declare function handleLapDescribe(req: Request, deps: LapDescribeDeps): Promise<Response>;
|
|
14
|
-
export declare function verifyAndReadTid(req: Request, key: string | Uint8Array): {
|
|
14
|
+
export declare function verifyAndReadTid(req: Request, key: string | Uint8Array): Promise<{
|
|
15
15
|
ok: true;
|
|
16
16
|
tid: string;
|
|
17
17
|
} | {
|
|
18
18
|
ok: false;
|
|
19
19
|
status: number;
|
|
20
20
|
code: string;
|
|
21
|
-
}
|
|
21
|
+
}>;
|
|
22
22
|
//# sourceMappingURL=describe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../../src/server/lap/describe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../../src/server/lap/describe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,MAAM,GAAG,UAAU,CAAA;IAC/B,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,eAAe,CAAA;IACzB,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,WAAW,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB,CAAA;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoD9F;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,MAAM,GAAG,UAAU,GACvB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAOlF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { verifyToken } from '../token.js';
|
|
2
2
|
export async function handleLapDescribe(req, deps) {
|
|
3
|
-
const auth = verifyAndReadTid(req, deps.signingKey);
|
|
3
|
+
const auth = await verifyAndReadTid(req, deps.signingKey);
|
|
4
4
|
if (!auth.ok)
|
|
5
5
|
return json({ error: { code: auth.code } }, auth.status);
|
|
6
6
|
const rec = await deps.tokenStore.findByTid(auth.tid);
|
|
@@ -37,7 +37,7 @@ export async function handleLapDescribe(req, deps) {
|
|
|
37
37
|
await deps.tokenStore.markActive(auth.tid, label, nowMs);
|
|
38
38
|
// Fire the active signal to the browser only on the first transition.
|
|
39
39
|
if (wasAwaitingClaude) {
|
|
40
|
-
deps.registry.
|
|
40
|
+
deps.registry.send(auth.tid, { t: 'active' });
|
|
41
41
|
}
|
|
42
42
|
await deps.auditSink.write({
|
|
43
43
|
at: nowMs,
|
|
@@ -48,12 +48,12 @@ export async function handleLapDescribe(req, deps) {
|
|
|
48
48
|
});
|
|
49
49
|
return json(out, 200);
|
|
50
50
|
}
|
|
51
|
-
export function verifyAndReadTid(req, key) {
|
|
51
|
+
export async function verifyAndReadTid(req, key) {
|
|
52
52
|
const auth = req.headers.get('authorization');
|
|
53
53
|
if (!auth || !auth.startsWith('Bearer '))
|
|
54
54
|
return { ok: false, status: 401, code: 'auth-failed' };
|
|
55
55
|
const token = auth.slice('Bearer '.length);
|
|
56
|
-
const v = verifyToken(token, key);
|
|
56
|
+
const v = await verifyToken(token, key);
|
|
57
57
|
if (v.kind !== 'ok')
|
|
58
58
|
return { ok: false, status: 401, code: 'auth-failed' };
|
|
59
59
|
return { ok: true, tid: v.payload.tid };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../../../src/server/lap/describe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAgBzC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAY,EAAE,IAAqB;IACzE,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../../../src/server/lap/describe.ts"],"names":[],"mappings":"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","sourcesContent":["import { verifyToken } from '../token.js'\nimport type { TokenStore } from '../token-store.js'\nimport type { PairingRegistry } from '../ws/pairing-registry.js'\nimport type { AuditSink } from '../audit.js'\nimport type { RateLimiter } from '../rate-limit.js'\nimport type { LapDescribeResponse, MessageSchemaEntry } from '../../protocol.js'\n\nexport type LapDescribeDeps = {\n signingKey: string | Uint8Array\n tokenStore: TokenStore\n registry: PairingRegistry\n auditSink: AuditSink\n rateLimiter: RateLimiter\n now?: () => number\n}\n\nexport async function handleLapDescribe(req: Request, deps: LapDescribeDeps): Promise<Response> {\n const auth = await verifyAndReadTid(req, deps.signingKey)\n if (!auth.ok) return json({ error: { code: auth.code } }, auth.status)\n\n const rec = await deps.tokenStore.findByTid(auth.tid)\n if (!rec || rec.status === 'revoked') return json({ error: { code: 'revoked' } }, 403)\n if (!deps.registry.isPaired(auth.tid)) return json({ error: { code: 'paused' } }, 503)\n\n const rlCheck = await deps.rateLimiter.check(auth.tid, 'token')\n if (!rlCheck.allowed) {\n return json({ error: { code: 'rate-limited', retryAfterMs: rlCheck.retryAfterMs } }, 429)\n }\n\n const hello = deps.registry.getHello(auth.tid)\n if (!hello) return json({ error: { code: 'paused' } }, 503)\n\n const messages: Record<string, MessageSchemaEntry> = hello.msgSchema as Record<\n string,\n MessageSchemaEntry\n >\n const out: LapDescribeResponse = {\n name: hello.appName,\n version: hello.appVersion,\n stateSchema: hello.stateSchema,\n messages,\n docs: hello.docs,\n conventions: {\n dispatchModel: 'TEA',\n confirmationModel: 'runtime-mediated',\n readSurfaces: ['state', 'query_dom', 'describe_visible_content', 'describe_context'],\n },\n schemaHash: hello.schemaHash,\n }\n\n const nowMs = (deps.now ?? (() => Date.now()))()\n // Transition to active: Claude has made its first LAP call to /describe,\n // confirming both the browser WS and Claude are live.\n const wasAwaitingClaude = rec.status === 'awaiting-claude'\n const label = rec.uid ?? 'Claude'\n await deps.tokenStore.markActive(auth.tid, label, nowMs)\n // Fire the active signal to the browser only on the first transition.\n if (wasAwaitingClaude) {\n deps.registry.send(auth.tid, { t: 'active' })\n }\n await deps.auditSink.write({\n at: nowMs,\n tid: auth.tid,\n uid: rec.uid,\n event: 'lap-call',\n detail: { path: '/lap/v1/describe' },\n })\n return json(out, 200)\n}\n\nexport async function verifyAndReadTid(\n req: Request,\n key: string | Uint8Array,\n): Promise<{ ok: true; tid: string } | { ok: false; status: number; code: string }> {\n const auth = req.headers.get('authorization')\n if (!auth || !auth.startsWith('Bearer ')) return { ok: false, status: 401, code: 'auth-failed' }\n const token = auth.slice('Bearer '.length)\n const v = await verifyToken(token, key)\n if (v.kind !== 'ok') return { ok: false, status: 401, code: 'auth-failed' }\n return { ok: true, tid: v.payload.tid }\n}\n\nfunction json(b: unknown, s: number): Response {\n return new Response(JSON.stringify(b), {\n status: s,\n headers: { 'content-type': 'application/json' },\n })\n}\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { TokenStore } from '../token-store.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PairingRegistry } from '../ws/pairing-registry.js';
|
|
3
3
|
import type { AuditSink } from '../audit.js';
|
|
4
4
|
import type { RateLimiter } from '../rate-limit.js';
|
|
5
5
|
export type ForwardDeps = {
|
|
6
6
|
signingKey: string | Uint8Array;
|
|
7
7
|
tokenStore: TokenStore;
|
|
8
|
-
registry:
|
|
8
|
+
registry: PairingRegistry;
|
|
9
9
|
auditSink: AuditSink;
|
|
10
10
|
rateLimiter: RateLimiter;
|
|
11
11
|
now?: () => number;
|