@parlor/server 0.3.3 → 0.4.1
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/access.d.ts +3 -3
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +4 -4
- package/dist/access.js.map +1 -1
- package/dist/actions.d.ts +6 -1
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +35 -12
- package/dist/actions.js.map +1 -1
- package/dist/auth.d.ts +22 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +3 -1
- package/dist/auth.js.map +1 -1
- package/dist/caller.d.ts +7 -1
- package/dist/caller.d.ts.map +1 -1
- package/dist/caller.js +24 -6
- package/dist/caller.js.map +1 -1
- package/dist/create-server.d.ts +16 -1
- package/dist/create-server.d.ts.map +1 -1
- package/dist/create-server.js +29 -10
- package/dist/create-server.js.map +1 -1
- package/dist/dev-auth.d.ts.map +1 -1
- package/dist/dev-auth.js +2 -0
- package/dist/dev-auth.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +8 -3
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +38 -18
- package/dist/mcp.js.map +1 -1
- package/dist/plugin.d.ts +7 -3
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/scope.d.ts +4 -1
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +1 -1
- package/dist/scope.js.map +1 -1
- package/dist/visibility.d.ts +16 -0
- package/dist/visibility.d.ts.map +1 -0
- package/dist/visibility.js +32 -0
- package/dist/visibility.js.map +1 -0
- package/package.json +5 -5
package/dist/access.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type Principal, type PrincipalKind } from "@parlor/core";
|
|
2
2
|
import type { Caller } from "./caller.js";
|
|
3
3
|
export declare const ROUTE_ACCESS: {
|
|
4
|
-
readonly manifest: readonly ["user", "agent"];
|
|
5
|
-
readonly actions: readonly ["user", "agent"];
|
|
4
|
+
readonly manifest: readonly ["user", "agent", "key"];
|
|
5
|
+
readonly actions: readonly ["user", "agent", "key"];
|
|
6
6
|
readonly pluginActions: readonly ["user", "host"];
|
|
7
|
-
readonly mcp: readonly ["agent"];
|
|
7
|
+
readonly mcp: readonly ["agent", "key"];
|
|
8
8
|
readonly sync: readonly ["user", "host"];
|
|
9
9
|
readonly channels: readonly ["user"];
|
|
10
10
|
readonly ui: readonly ["user"];
|
package/dist/access.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAE/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,eAAO,MAAM,YAAY;;;;;;;;CAQmC,CAAC;AAE7D,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,YAAY,CAAC;AAKhD,0FAA0F;AAC1F,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,OAAO,IAAI,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAExF,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,aAAa,IAAI,MAAM,GAAG;IACvD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAC9D,CAAC;AAEF,wBAAgB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAIpF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,OAAO,EAC9C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,CAAC,EACV,SAAS,EAAE,OAAO,GACjB,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAKtB;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,aAAa,EACnD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,SAAS,EAAE,OAAO,GACjB,QAAQ,CAAC,CAAC,CAAC,CAQb"}
|
package/dist/access.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ActionError
|
|
1
|
+
import { ActionError } from "@parlor/core";
|
|
2
2
|
export const ROUTE_ACCESS = {
|
|
3
|
-
manifest: ["user", "agent"],
|
|
4
|
-
actions: ["user", "agent"],
|
|
3
|
+
manifest: ["user", "agent", "key"],
|
|
4
|
+
actions: ["user", "agent", "key"],
|
|
5
5
|
pluginActions: ["user", "host"],
|
|
6
|
-
mcp: ["agent"],
|
|
6
|
+
mcp: ["agent", "key"],
|
|
7
7
|
sync: ["user", "host"],
|
|
8
8
|
channels: ["user"],
|
|
9
9
|
ui: ["user"],
|
package/dist/access.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAsC,MAAM,cAAc,CAAC;AAM/E,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;IAClC,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;IACjC,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IAC/B,GAAG,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;IACrB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,MAAM,CAAC;IAClB,EAAE,EAAE,CAAC,MAAM,CAAC;CAC8C,CAAC;AAI7D,kGAAkG;AAClG,MAAM,mBAAmB,GAAyB,IAAI,GAAG,CAAU,CAAC,SAAS,CAAC,CAAC,CAAC;AAShF,MAAM,UAAU,MAAM,CAAC,OAAgB,EAAE,MAAc,EAAE,SAAkB;IACzE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;IAClC,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,CAAC,SAAS,IAAI,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChF,OAAQ,YAAY,CAAC,OAAO,CAA6B,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAc,EACd,OAAU,EACV,SAAkB;IAElB,IAAI,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC;QAAE,OAAO,MAA8B,CAAC;IAC9E,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;IAClC,IAAI,IAAI,KAAK,WAAW;QAAE,MAAM,IAAI,WAAW,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;IACrF,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,yBAAyB,OAAO,GAAG,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,KAAmB,EACnB,SAAkB;IAElB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;IAClC,MAAM,MAAM,GAAI,KAAkC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAClE,IAAI,IAAI,KAAK,WAAW,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,WAAW,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,6BAA6B,CAAC,CAAC;IACxF,OAAO,MAAqB,CAAC;AAC/B,CAAC","sourcesContent":["import { ActionError, type Principal, type PrincipalKind } from \"@parlor/core\";\n\nimport type { Caller } from \"./caller.js\";\n\ntype SignedInKind = Exclude<PrincipalKind, \"anonymous\">;\n\nexport const ROUTE_ACCESS = {\n manifest: [\"user\", \"agent\", \"key\"],\n actions: [\"user\", \"agent\", \"key\"],\n pluginActions: [\"user\", \"host\"],\n mcp: [\"agent\", \"key\"],\n sync: [\"user\", \"host\"],\n channels: [\"user\"],\n ui: [\"user\"],\n} as const satisfies Record<string, readonly SignedInKind[]>;\n\nexport type Surface = keyof typeof ROUTE_ACCESS;\n\n/** Reached by anonymous callers even under an enforcing provider; each action checks `public`. */\nconst OPEN_WHEN_ENFORCING: ReadonlySet<Surface> = new Set<Surface>([\"actions\"]);\n\n/** An anonymous caller reaches every surface while no enforcing provider is installed. */\nexport type KindsOf<S extends Surface> = \"anonymous\" | (typeof ROUTE_ACCESS)[S][number];\n\nexport type CallerOf<K extends PrincipalKind> = Caller & {\n readonly principal: Extract<Principal, { readonly kind: K }>;\n};\n\nexport function admits(surface: Surface, caller: Caller, enforcing: boolean): boolean {\n const { kind } = caller.principal;\n if (kind === \"anonymous\") return !enforcing || OPEN_WHEN_ENFORCING.has(surface);\n return (ROUTE_ACCESS[surface] as readonly SignedInKind[]).includes(kind);\n}\n\n/**\n * Narrows a caller to the kinds a surface admits, or refuses it: `unauthenticated` for an\n * anonymous caller, `forbidden` for a signed-in principal of another kind.\n */\nexport function requireSurface<S extends Surface>(\n caller: Caller,\n surface: S,\n enforcing: boolean,\n): CallerOf<KindsOf<S>> {\n if (admits(surface, caller, enforcing)) return caller as CallerOf<KindsOf<S>>;\n const { kind } = caller.principal;\n if (kind === \"anonymous\") throw new ActionError(\"unauthenticated\", \"Sign in first.\");\n throw new ActionError(\"forbidden\", `A ${kind} principal cannot use ${surface}.`);\n}\n\nexport function requireAccess<K extends PrincipalKind>(\n caller: Caller,\n kinds: readonly K[],\n enforcing: boolean,\n): CallerOf<K> {\n const { kind } = caller.principal;\n const listed = (kinds as readonly PrincipalKind[]).includes(kind);\n if (kind === \"anonymous\" && (enforcing || !listed)) {\n throw new ActionError(\"unauthenticated\", \"Sign in first.\");\n }\n if (!listed) throw new ActionError(\"forbidden\", `A ${kind} principal cannot use this.`);\n return caller as CallerOf<K>;\n}\n"]}
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { AgentOrigin } from "@parlor/agent-ui";
|
|
2
2
|
import type { ConfirmationOutcome, ConfirmationRequest } from "@parlor/agent-ui/server";
|
|
3
|
-
import { type ActionMap, type ActionSource, type DataProvider } from "@parlor/core";
|
|
3
|
+
import { type ActionMap, type ActionSource, type ConfirmationPolicy, type DataProvider } from "@parlor/core";
|
|
4
4
|
import { type ActionRegistry, type InvokeResult } from "@parlor/core/registry";
|
|
5
5
|
import { type Surface } from "./access.js";
|
|
6
6
|
import { type Caller, type CallerResolver } from "./caller.js";
|
|
7
7
|
import type { ActionAudit } from "./plugin.js";
|
|
8
8
|
import { type ScopeResolver } from "./scope.js";
|
|
9
|
+
import type { RegistryResolver } from "./visibility.js";
|
|
9
10
|
export { clientOf, sourceOf } from "./caller.js";
|
|
10
11
|
export type Confirm = (request: ConfirmationRequest) => Promise<ConfirmationOutcome>;
|
|
11
12
|
export interface ActionRunnerOptions<T extends ActionMap> {
|
|
@@ -17,9 +18,13 @@ export interface ActionRunnerOptions<T extends ActionMap> {
|
|
|
17
18
|
readonly callers?: CallerResolver | undefined;
|
|
18
19
|
readonly onClient?: ((clientId: string | undefined, audience: string) => void) | undefined;
|
|
19
20
|
readonly confirm?: Confirm | undefined;
|
|
21
|
+
/** Which calls are asked about, run or refused; `defaultConfirmationPolicy` when absent. */
|
|
22
|
+
readonly confirmation?: ConfirmationPolicy | undefined;
|
|
20
23
|
readonly audit?: ((record: ActionAudit) => void) | undefined;
|
|
21
24
|
/** Throws for a caller the surface admits but the actions' owner does not; audited like any refusal. */
|
|
22
25
|
readonly admit?: ((caller: Caller) => Promise<void>) | undefined;
|
|
26
|
+
/** The actions a request may call; a hidden one answers `not_found` like an unknown one. */
|
|
27
|
+
readonly registryFor?: RegistryResolver | undefined;
|
|
23
28
|
/** Awaited before every call, so it must memoize; defaults to creating the sync tables. */
|
|
24
29
|
readonly ready?: (() => Promise<void>) | undefined;
|
|
25
30
|
}
|
package/dist/actions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,YAAY,EACjB,KAAK,YAAY,EAGlB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAU/B,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EACL,KAAK,MAAM,EACX,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
|
package/dist/actions.js
CHANGED
|
@@ -4,12 +4,16 @@ import { encodeCursor } from "@parlor/sync";
|
|
|
4
4
|
import { createPruner, ensureSyncTables, readSyncCursor, recordSyncEvent, } from "@parlor/sync/server";
|
|
5
5
|
import { requireSurface } from "./access.js";
|
|
6
6
|
import { AuthFailure } from "./auth.js";
|
|
7
|
-
import { createCallerResolver, sourceFor, } from "./caller.js";
|
|
7
|
+
import { createCallerResolver, sourceFor, streamsOf, } from "./caller.js";
|
|
8
8
|
import { DEFAULT_SCOPE } from "./scope.js";
|
|
9
9
|
export { clientOf, sourceOf } from "./caller.js";
|
|
10
10
|
function originOf(source, principal) {
|
|
11
11
|
const via = source === "agent" ? "agent" : "mcp";
|
|
12
|
-
|
|
12
|
+
if (principal.kind === "agent")
|
|
13
|
+
return { via, name: principal.agent.name };
|
|
14
|
+
if (principal.kind === "key")
|
|
15
|
+
return { via, name: principal.name };
|
|
16
|
+
return { via };
|
|
13
17
|
}
|
|
14
18
|
function confirmationGate(gate) {
|
|
15
19
|
return async ({ action, name, input, ctx }) => {
|
|
@@ -47,10 +51,12 @@ const INTERNAL_CALLER = {
|
|
|
47
51
|
principal: ANONYMOUS,
|
|
48
52
|
scope: DEFAULT_SCOPE,
|
|
49
53
|
audience: DEFAULT_SCOPE,
|
|
54
|
+
account: undefined,
|
|
50
55
|
clientId: undefined,
|
|
51
56
|
};
|
|
52
57
|
export function createActionRunner(options) {
|
|
53
|
-
const { actions, scope, database, sync, onClient, confirm, audit, admit } = options;
|
|
58
|
+
const { actions, scope, database, sync, onClient, confirm, confirmation, audit, admit, } = options;
|
|
59
|
+
const registryFor = options.registryFor ?? (() => Promise.resolve(actions));
|
|
54
60
|
const callers = options.callers ?? createCallerResolver(undefined, scope);
|
|
55
61
|
const pruner = sync && database ? createPruner(database) : undefined;
|
|
56
62
|
let prepared;
|
|
@@ -62,28 +68,35 @@ export function createActionRunner(options) {
|
|
|
62
68
|
: Promise.resolve();
|
|
63
69
|
return prepared;
|
|
64
70
|
});
|
|
65
|
-
const cursorBefore = async (name, caller) => {
|
|
66
|
-
const action =
|
|
71
|
+
const cursorBefore = async (registry, name, caller) => {
|
|
72
|
+
const action = registry.get(name);
|
|
67
73
|
if (!sync || !database || !action?.reads || isMutating(action))
|
|
68
74
|
return undefined;
|
|
69
|
-
|
|
70
|
-
return encodeCursor(await readSyncCursor(database.read(), streams));
|
|
75
|
+
return encodeCursor(await readSyncCursor(database.read(), streamsOf(caller)));
|
|
71
76
|
};
|
|
72
77
|
const invoke = async (name, input, request, caller, source, runOptions, trace) => {
|
|
73
78
|
if (runOptions.surface)
|
|
74
79
|
requireSurface(caller, runOptions.surface, callers.enforcing);
|
|
75
80
|
await admit?.(caller);
|
|
81
|
+
const registry = await registryFor(request);
|
|
76
82
|
const client = caller.clientId;
|
|
77
|
-
|
|
83
|
+
if (caller.principal.kind !== "key")
|
|
84
|
+
onClient?.(client, caller.audience);
|
|
78
85
|
const signal = runOptions.signal ?? request?.signal;
|
|
79
86
|
const resolvedScope = caller.scope;
|
|
80
|
-
const cursor = runOptions.cursor
|
|
81
|
-
|
|
87
|
+
const cursor = runOptions.cursor
|
|
88
|
+
? await cursorBefore(registry, name, caller)
|
|
89
|
+
: undefined;
|
|
90
|
+
const result = await invokeAction(registry, name, input, { scope: resolvedScope, source, principal: caller.principal, signal, request }, {
|
|
82
91
|
data: database,
|
|
83
92
|
enforcing: callers.enforcing,
|
|
84
93
|
onRun: () => {
|
|
85
94
|
trace.stage = "ran";
|
|
86
95
|
},
|
|
96
|
+
confirmation,
|
|
97
|
+
onWaived: () => {
|
|
98
|
+
trace.confirmation = "waived";
|
|
99
|
+
},
|
|
87
100
|
confirm: confirm
|
|
88
101
|
? confirmationGate({
|
|
89
102
|
confirm,
|
|
@@ -101,7 +114,7 @@ export function createActionRunner(options) {
|
|
|
101
114
|
if (tags.length === 0)
|
|
102
115
|
return;
|
|
103
116
|
await recordSyncEvent(tx, {
|
|
104
|
-
scope: action.notifies
|
|
117
|
+
scope: notifiedStream(action.notifies, caller),
|
|
105
118
|
tags,
|
|
106
119
|
source: client,
|
|
107
120
|
});
|
|
@@ -117,7 +130,7 @@ export function createActionRunner(options) {
|
|
|
117
130
|
async run(name, input, request, declared, runOptions = {}) {
|
|
118
131
|
await ready();
|
|
119
132
|
const caller = request ? await callers(request) : INTERNAL_CALLER;
|
|
120
|
-
const source = sourceFor(declared, caller, callers.enforcing);
|
|
133
|
+
const source = sourceFor(declared, caller, callers.enforcing, runOptions.surface);
|
|
121
134
|
const trace = { stage: "refused", confirmation: undefined };
|
|
122
135
|
if (!audit)
|
|
123
136
|
return invoke(name, input, request, caller, source, runOptions, trace);
|
|
@@ -166,4 +179,14 @@ async function digest(input) {
|
|
|
166
179
|
const hash = await crypto.subtle.digest("SHA-256", bytes);
|
|
167
180
|
return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
168
181
|
}
|
|
182
|
+
function notifiedStream(notifies, caller) {
|
|
183
|
+
switch (notifies) {
|
|
184
|
+
case "user":
|
|
185
|
+
return caller.audience;
|
|
186
|
+
case "account":
|
|
187
|
+
return caller.account ?? caller.audience;
|
|
188
|
+
default:
|
|
189
|
+
return caller.scope;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
169
192
|
//# sourceMappingURL=actions.js.map
|
package/dist/actions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentOrigin } from \"@parlor/agent-ui\";\nimport type { ConfirmationOutcome, ConfirmationRequest } from \"@parlor/agent-ui/server\";\nimport {\n ActionError,\n type ActionMap,\n ANONYMOUS,\n type ActionSource,\n type DataProvider,\n isMutating,\n type Principal,\n} from \"@parlor/core\";\nimport {\n type ActionRegistry,\n invokeAction,\n type InvokeResult,\n type PreparedCall,\n} from \"@parlor/core/registry\";\nimport { encodeCursor } from \"@parlor/sync\";\nimport {\n createPruner,\n ensureSyncTables,\n readSyncCursor,\n recordSyncEvent,\n type SyncDatabase,\n} from \"@parlor/sync/server\";\n\nimport { requireSurface, type Surface } from \"./access.js\";\nimport { AuthFailure } from \"./auth.js\";\nimport {\n type Caller,\n type CallerResolver,\n createCallerResolver,\n sourceFor,\n} from \"./caller.js\";\nimport type { ActionAudit, ConfirmationAnswer } from \"./plugin.js\";\nimport { DEFAULT_SCOPE, type ScopeResolver } from \"./scope.js\";\n\nexport { clientOf, sourceOf } from \"./caller.js\";\n\nexport type Confirm = (request: ConfirmationRequest) => Promise<ConfirmationOutcome>;\n\nexport interface ActionRunnerOptions<T extends ActionMap> {\n readonly actions: ActionRegistry<T>;\n readonly scope: ScopeResolver;\n readonly database?: DataProvider | undefined;\n readonly sync: boolean;\n /** Resolves each request's identity once; without it every caller is anonymous. */\n readonly callers?: CallerResolver | undefined;\n readonly onClient?:\n | ((clientId: string | undefined, audience: string) => void)\n | undefined;\n readonly confirm?: Confirm | undefined;\n readonly audit?: ((record: ActionAudit) => void) | undefined;\n /** Throws for a caller the surface admits but the actions' owner does not; audited like any refusal. */\n readonly admit?: ((caller: Caller) => Promise<void>) | undefined;\n /** Awaited before every call, so it must memoize; defaults to creating the sync tables. */\n readonly ready?: (() => Promise<void>) | undefined;\n}\n\nexport interface RunOptions {\n readonly signal?: AbortSignal | undefined;\n readonly origin?: AgentOrigin | undefined;\n /** Refuses callers the surface does not admit, inside the call so the refusal is audited. */\n readonly surface?: Surface | undefined;\n /** Also answer the sync cursor a read's result reflects, for a client that keeps it fresh. */\n readonly cursor?: boolean | undefined;\n}\n\nexport interface ActionRun extends InvokeResult {\n /** Set for a read with `cursor`: every sync event of the caller's streams up to it is in `data`. */\n readonly cursor?: string | undefined;\n}\n\nexport interface ActionRunner {\n run(\n name: string,\n input: unknown,\n request: Request | undefined,\n source: ActionSource,\n options?: RunOptions,\n ): Promise<ActionRun>;\n ready(): Promise<void>;\n}\n\ninterface Gate {\n readonly confirm: Confirm;\n readonly answered: (answer: ConfirmationAnswer) => void;\n readonly scope: string;\n readonly clientId: string | undefined;\n readonly origin: AgentOrigin | undefined;\n readonly signal: AbortSignal | undefined;\n}\n\nfunction originOf(source: ActionSource, principal: Principal): AgentOrigin {\n const via = source === \"agent\" ? \"agent\" : \"mcp\";\n return principal.kind === \"agent\" ? { via, name: principal.agent.name } : { via };\n}\n\nfunction confirmationGate(gate: Gate): (call: PreparedCall) => Promise<void> {\n return async ({ action, name, input, ctx }) => {\n const turn = ctx.principal?.kind === \"agent\" ? ctx.principal.turn : undefined;\n const outcome = await gate.confirm({\n scope: gate.scope,\n clientId: turn?.clientId ?? gate.clientId,\n ...(turn ? { thread: { threadId: turn.threadId, turnId: turn.turnId } } : {}),\n action: name,\n description: action.description,\n input,\n origin: gate.origin ?? originOf(ctx.source, ctx.principal ?? ANONYMOUS),\n signal: gate.signal,\n });\n if (outcome !== \"cancelled\") {\n gate.answered(outcome === \"shutdown\" ? \"unavailable\" : outcome);\n }\n switch (outcome) {\n case \"approved\":\n return;\n case \"declined\":\n throw new ActionError(\n \"declined\",\n `The user declined ${name} in the app; nothing ran. Do not retry unless they ask again.`,\n );\n case \"timeout\":\n throw new ActionError(\n \"confirmation_timeout\",\n `The user did not answer in time; ${name} did not run.`,\n );\n case \"unavailable\":\n throw new ActionError(\n \"confirmation_unavailable\",\n `No open app can ask the user to confirm ${name}. Ask the user to do it in the app themselves.`,\n );\n case \"shutdown\":\n throw new ActionError(\n \"confirmation_unavailable\",\n `The server shut down before the user answered; ${name} did not run. Try again once it is back, or ask the user to do it in the app themselves.`,\n );\n case \"cancelled\":\n throw (\n gate.signal?.reason ?? new DOMException(\"The call was cancelled.\", \"AbortError\")\n );\n }\n };\n}\n\nconst INTERNAL_CALLER: Caller = {\n principal: ANONYMOUS,\n scope: DEFAULT_SCOPE,\n audience: DEFAULT_SCOPE,\n clientId: undefined,\n};\n\nexport function createActionRunner<T extends ActionMap>(\n options: ActionRunnerOptions<T>,\n): ActionRunner {\n const { actions, scope, database, sync, onClient, confirm, audit, admit } = options;\n const callers = options.callers ?? createCallerResolver(undefined, scope);\n const pruner = sync && database ? createPruner(database) : undefined;\n let prepared: Promise<void> | undefined;\n const ready =\n options.ready ??\n (() => {\n prepared ??=\n sync && database\n ? ensureSyncTables(database.read() as SyncDatabase)\n : Promise.resolve();\n return prepared;\n });\n\n const cursorBefore = async (\n name: string,\n caller: Caller,\n ): Promise<string | undefined> => {\n const action = actions.get(name);\n if (!sync || !database || !action?.reads || isMutating(action)) return undefined;\n const streams =\n caller.audience === caller.scope ? [caller.scope] : [caller.scope, caller.audience];\n return encodeCursor(await readSyncCursor(database.read() as SyncDatabase, streams));\n };\n\n const invoke = async (\n name: string,\n input: unknown,\n request: Request | undefined,\n caller: Caller,\n source: ActionSource,\n runOptions: RunOptions,\n trace: Trace,\n ): Promise<ActionRun> => {\n if (runOptions.surface) requireSurface(caller, runOptions.surface, callers.enforcing);\n await admit?.(caller);\n const client = caller.clientId;\n onClient?.(client, caller.audience);\n const signal = runOptions.signal ?? request?.signal;\n const resolvedScope = caller.scope;\n const cursor = runOptions.cursor ? await cursorBefore(name, caller) : undefined;\n const result = await invokeAction(\n actions,\n name,\n input,\n { scope: resolvedScope, source, principal: caller.principal, signal, request },\n {\n data: database,\n enforcing: callers.enforcing,\n onRun: () => {\n trace.stage = \"ran\";\n },\n confirm: confirm\n ? confirmationGate({\n confirm,\n answered: (answer) => {\n trace.confirmation = answer;\n },\n scope: caller.audience,\n clientId: client,\n origin: runOptions.origin,\n signal,\n })\n : undefined,\n onWrite:\n sync && database\n ? async ({ action, tags, tx }) => {\n if (tags.length === 0) return;\n await recordSyncEvent(tx as SyncDatabase, {\n scope: action.notifies === \"user\" ? caller.audience : resolvedScope,\n tags,\n source: client,\n });\n }\n : undefined,\n },\n );\n if (result.writes.length > 0) pruner?.touch();\n return cursor === undefined ? result : { ...result, cursor };\n };\n\n return {\n ready,\n async run(name, input, request, declared, runOptions = {}) {\n await ready();\n const caller = request ? await callers(request) : INTERNAL_CALLER;\n const source = sourceFor(declared, caller, callers.enforcing);\n const trace: Trace = { stage: \"refused\", confirmation: undefined };\n if (!audit) return invoke(name, input, request, caller, source, runOptions, trace);\n const at = Date.now();\n const record = async (outcome: ActionAudit[\"outcome\"]) => {\n try {\n audit({\n at,\n action: name,\n principal: caller.principal,\n source,\n scope: caller.scope,\n inputDigest: await digest(input),\n stage: trace.stage,\n outcome,\n ...(trace.confirmation ? { confirmation: trace.confirmation } : {}),\n });\n } catch (error) {\n console.error(\"[parlor] audit failed:\", error);\n }\n };\n try {\n const result = await invoke(\n name,\n input,\n request,\n caller,\n source,\n runOptions,\n trace,\n );\n await record(\"ok\");\n return result;\n } catch (error) {\n await record(outcomeOf(error));\n throw error;\n }\n },\n };\n}\n\ninterface Trace {\n stage: ActionAudit[\"stage\"];\n confirmation: ConfirmationAnswer | undefined;\n}\n\nfunction outcomeOf(error: unknown): ActionAudit[\"outcome\"] {\n if (error instanceof ActionError) return error.code;\n if (error instanceof AuthFailure) return \"unauthenticated\";\n if (error instanceof Error && error.name === \"AbortError\") return \"cancelled\";\n return \"internal\";\n}\n\nasync function digest(input: unknown): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify(input ?? null));\n const hash = await crypto.subtle.digest(\"SHA-256\", bytes);\n return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentOrigin } from \"@parlor/agent-ui\";\nimport type { ConfirmationOutcome, ConfirmationRequest } from \"@parlor/agent-ui/server\";\nimport {\n ActionError,\n type ActionMap,\n ANONYMOUS,\n type ActionSource,\n type ConfirmationPolicy,\n type DataProvider,\n isMutating,\n type Principal,\n} from \"@parlor/core\";\nimport {\n type ActionRegistry,\n invokeAction,\n type InvokeResult,\n type PreparedCall,\n} from \"@parlor/core/registry\";\nimport { encodeCursor } from \"@parlor/sync\";\nimport {\n createPruner,\n ensureSyncTables,\n readSyncCursor,\n recordSyncEvent,\n type SyncDatabase,\n} from \"@parlor/sync/server\";\n\nimport { requireSurface, type Surface } from \"./access.js\";\nimport { AuthFailure } from \"./auth.js\";\nimport {\n type Caller,\n type CallerResolver,\n createCallerResolver,\n sourceFor,\n streamsOf,\n} from \"./caller.js\";\nimport type { ActionAudit, ConfirmationAnswer } from \"./plugin.js\";\nimport { DEFAULT_SCOPE, type ScopeResolver } from \"./scope.js\";\nimport type { RegistryResolver } from \"./visibility.js\";\n\nexport { clientOf, sourceOf } from \"./caller.js\";\n\nexport type Confirm = (request: ConfirmationRequest) => Promise<ConfirmationOutcome>;\n\nexport interface ActionRunnerOptions<T extends ActionMap> {\n readonly actions: ActionRegistry<T>;\n readonly scope: ScopeResolver;\n readonly database?: DataProvider | undefined;\n readonly sync: boolean;\n /** Resolves each request's identity once; without it every caller is anonymous. */\n readonly callers?: CallerResolver | undefined;\n readonly onClient?:\n | ((clientId: string | undefined, audience: string) => void)\n | undefined;\n readonly confirm?: Confirm | undefined;\n /** Which calls are asked about, run or refused; `defaultConfirmationPolicy` when absent. */\n readonly confirmation?: ConfirmationPolicy | undefined;\n readonly audit?: ((record: ActionAudit) => void) | undefined;\n /** Throws for a caller the surface admits but the actions' owner does not; audited like any refusal. */\n readonly admit?: ((caller: Caller) => Promise<void>) | undefined;\n /** The actions a request may call; a hidden one answers `not_found` like an unknown one. */\n readonly registryFor?: RegistryResolver | undefined;\n /** Awaited before every call, so it must memoize; defaults to creating the sync tables. */\n readonly ready?: (() => Promise<void>) | undefined;\n}\n\nexport interface RunOptions {\n readonly signal?: AbortSignal | undefined;\n readonly origin?: AgentOrigin | undefined;\n /** Refuses callers the surface does not admit, inside the call so the refusal is audited. */\n readonly surface?: Surface | undefined;\n /** Also answer the sync cursor a read's result reflects, for a client that keeps it fresh. */\n readonly cursor?: boolean | undefined;\n}\n\nexport interface ActionRun extends InvokeResult {\n /** Set for a read with `cursor`: every sync event of the caller's streams up to it is in `data`. */\n readonly cursor?: string | undefined;\n}\n\nexport interface ActionRunner {\n run(\n name: string,\n input: unknown,\n request: Request | undefined,\n source: ActionSource,\n options?: RunOptions,\n ): Promise<ActionRun>;\n ready(): Promise<void>;\n}\n\ninterface Gate {\n readonly confirm: Confirm;\n readonly answered: (answer: ConfirmationAnswer) => void;\n readonly scope: string;\n readonly clientId: string | undefined;\n readonly origin: AgentOrigin | undefined;\n readonly signal: AbortSignal | undefined;\n}\n\nfunction originOf(source: ActionSource, principal: Principal): AgentOrigin {\n const via = source === \"agent\" ? \"agent\" : \"mcp\";\n if (principal.kind === \"agent\") return { via, name: principal.agent.name };\n if (principal.kind === \"key\") return { via, name: principal.name };\n return { via };\n}\n\nfunction confirmationGate(gate: Gate): (call: PreparedCall) => Promise<void> {\n return async ({ action, name, input, ctx }) => {\n const turn = ctx.principal?.kind === \"agent\" ? ctx.principal.turn : undefined;\n const outcome = await gate.confirm({\n scope: gate.scope,\n clientId: turn?.clientId ?? gate.clientId,\n ...(turn ? { thread: { threadId: turn.threadId, turnId: turn.turnId } } : {}),\n action: name,\n description: action.description,\n input,\n origin: gate.origin ?? originOf(ctx.source, ctx.principal ?? ANONYMOUS),\n signal: gate.signal,\n });\n if (outcome !== \"cancelled\") {\n gate.answered(outcome === \"shutdown\" ? \"unavailable\" : outcome);\n }\n switch (outcome) {\n case \"approved\":\n return;\n case \"declined\":\n throw new ActionError(\n \"declined\",\n `The user declined ${name} in the app; nothing ran. Do not retry unless they ask again.`,\n );\n case \"timeout\":\n throw new ActionError(\n \"confirmation_timeout\",\n `The user did not answer in time; ${name} did not run.`,\n );\n case \"unavailable\":\n throw new ActionError(\n \"confirmation_unavailable\",\n `No open app can ask the user to confirm ${name}. Ask the user to do it in the app themselves.`,\n );\n case \"shutdown\":\n throw new ActionError(\n \"confirmation_unavailable\",\n `The server shut down before the user answered; ${name} did not run. Try again once it is back, or ask the user to do it in the app themselves.`,\n );\n case \"cancelled\":\n throw (\n gate.signal?.reason ?? new DOMException(\"The call was cancelled.\", \"AbortError\")\n );\n }\n };\n}\n\nconst INTERNAL_CALLER: Caller = {\n principal: ANONYMOUS,\n scope: DEFAULT_SCOPE,\n audience: DEFAULT_SCOPE,\n account: undefined,\n clientId: undefined,\n};\n\nexport function createActionRunner<T extends ActionMap>(\n options: ActionRunnerOptions<T>,\n): ActionRunner {\n const {\n actions,\n scope,\n database,\n sync,\n onClient,\n confirm,\n confirmation,\n audit,\n admit,\n } = options;\n const registryFor = options.registryFor ?? (() => Promise.resolve(actions));\n const callers = options.callers ?? createCallerResolver(undefined, scope);\n const pruner = sync && database ? createPruner(database) : undefined;\n let prepared: Promise<void> | undefined;\n const ready =\n options.ready ??\n (() => {\n prepared ??=\n sync && database\n ? ensureSyncTables(database.read() as SyncDatabase)\n : Promise.resolve();\n return prepared;\n });\n\n const cursorBefore = async (\n registry: ActionRegistry,\n name: string,\n caller: Caller,\n ): Promise<string | undefined> => {\n const action = registry.get(name);\n if (!sync || !database || !action?.reads || isMutating(action)) return undefined;\n return encodeCursor(\n await readSyncCursor(database.read() as SyncDatabase, streamsOf(caller)),\n );\n };\n\n const invoke = async (\n name: string,\n input: unknown,\n request: Request | undefined,\n caller: Caller,\n source: ActionSource,\n runOptions: RunOptions,\n trace: Trace,\n ): Promise<ActionRun> => {\n if (runOptions.surface) requireSurface(caller, runOptions.surface, callers.enforcing);\n await admit?.(caller);\n const registry = await registryFor(request);\n const client = caller.clientId;\n if (caller.principal.kind !== \"key\") onClient?.(client, caller.audience);\n const signal = runOptions.signal ?? request?.signal;\n const resolvedScope = caller.scope;\n const cursor = runOptions.cursor\n ? await cursorBefore(registry, name, caller)\n : undefined;\n const result = await invokeAction(\n registry,\n name,\n input,\n { scope: resolvedScope, source, principal: caller.principal, signal, request },\n {\n data: database,\n enforcing: callers.enforcing,\n onRun: () => {\n trace.stage = \"ran\";\n },\n confirmation,\n onWaived: () => {\n trace.confirmation = \"waived\";\n },\n confirm: confirm\n ? confirmationGate({\n confirm,\n answered: (answer) => {\n trace.confirmation = answer;\n },\n scope: caller.audience,\n clientId: client,\n origin: runOptions.origin,\n signal,\n })\n : undefined,\n onWrite:\n sync && database\n ? async ({ action, tags, tx }) => {\n if (tags.length === 0) return;\n await recordSyncEvent(tx as SyncDatabase, {\n scope: notifiedStream(action.notifies, caller),\n tags,\n source: client,\n });\n }\n : undefined,\n },\n );\n if (result.writes.length > 0) pruner?.touch();\n return cursor === undefined ? result : { ...result, cursor };\n };\n\n return {\n ready,\n async run(name, input, request, declared, runOptions = {}) {\n await ready();\n const caller = request ? await callers(request) : INTERNAL_CALLER;\n const source = sourceFor(declared, caller, callers.enforcing, runOptions.surface);\n const trace: Trace = { stage: \"refused\", confirmation: undefined };\n if (!audit) return invoke(name, input, request, caller, source, runOptions, trace);\n const at = Date.now();\n const record = async (outcome: ActionAudit[\"outcome\"]) => {\n try {\n audit({\n at,\n action: name,\n principal: caller.principal,\n source,\n scope: caller.scope,\n inputDigest: await digest(input),\n stage: trace.stage,\n outcome,\n ...(trace.confirmation ? { confirmation: trace.confirmation } : {}),\n });\n } catch (error) {\n console.error(\"[parlor] audit failed:\", error);\n }\n };\n try {\n const result = await invoke(\n name,\n input,\n request,\n caller,\n source,\n runOptions,\n trace,\n );\n await record(\"ok\");\n return result;\n } catch (error) {\n await record(outcomeOf(error));\n throw error;\n }\n },\n };\n}\n\ninterface Trace {\n stage: ActionAudit[\"stage\"];\n confirmation: ConfirmationAnswer | undefined;\n}\n\nfunction outcomeOf(error: unknown): ActionAudit[\"outcome\"] {\n if (error instanceof ActionError) return error.code;\n if (error instanceof AuthFailure) return \"unauthenticated\";\n if (error instanceof Error && error.name === \"AbortError\") return \"cancelled\";\n return \"internal\";\n}\n\nasync function digest(input: unknown): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify(input ?? null));\n const hash = await crypto.subtle.digest(\"SHA-256\", bytes);\n return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nfunction notifiedStream(\n notifies: \"scope\" | \"user\" | \"account\" | undefined,\n caller: Caller,\n): string {\n switch (notifies) {\n case \"user\":\n return caller.audience;\n case \"account\":\n return caller.account ?? caller.audience;\n default:\n return caller.scope;\n }\n}\n"]}
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthenticatedPrincipal, DeviceId, GrantId, HostPrincipal, Principal, ThreadId, TurnId, UserId } from "@parlor/core";
|
|
1
|
+
import type { AuthenticatedPrincipal, DeviceId, GrantId, HostPrincipal, KeyPrincipal, Principal, ThreadId, TurnId, UserId } from "@parlor/core";
|
|
2
2
|
import type { ServerPlugin } from "./plugin.js";
|
|
3
3
|
/**
|
|
4
4
|
* Where identity comes from. The server calls `authenticate` once per request and hands
|
|
@@ -43,6 +43,16 @@ export interface AuthProvider extends ServerPlugin {
|
|
|
43
43
|
/** Who the agent works for, for the turn's context block. */
|
|
44
44
|
account?(userId: UserId): Promise<AccountProfile | undefined>;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The app's own API keys, which an auth provider routes to it: an `authorization: Bearer`
|
|
48
|
+
* token that starts with `prefix` is a key, checked before any other credential.
|
|
49
|
+
*/
|
|
50
|
+
export interface KeyVerifier {
|
|
51
|
+
/** Must hold a character outside `[A-Za-z0-9]`, such as `_`, so no session token starts with it. */
|
|
52
|
+
readonly prefix: string;
|
|
53
|
+
/** The key's principal, or undefined for an unknown or revoked key, which is answered 401. */
|
|
54
|
+
verify(secret: string, request: Request): Promise<KeyPrincipal | undefined>;
|
|
55
|
+
}
|
|
46
56
|
/** Announced when a computer's grant is added or removed; whatever lists computers reads it. */
|
|
47
57
|
export declare const HOST_GRANTS_TAG = "parlor_auth_hosts";
|
|
48
58
|
export interface TurnDelegation {
|
|
@@ -51,6 +61,8 @@ export interface TurnDelegation {
|
|
|
51
61
|
readonly clientId: string;
|
|
52
62
|
readonly agent: string;
|
|
53
63
|
readonly ttlMs: number;
|
|
64
|
+
/** The conversation's scope; a provider puts it in the token as `TurnBinding.scope`. */
|
|
65
|
+
readonly scope?: string | undefined;
|
|
54
66
|
}
|
|
55
67
|
export interface AgentDelegation {
|
|
56
68
|
/** How the app names it, such as "Claude Code on Alice's MacBook". */
|
|
@@ -99,9 +111,17 @@ export interface AccountProfile {
|
|
|
99
111
|
readonly email?: string | undefined;
|
|
100
112
|
}
|
|
101
113
|
export type AuthFailureReason = "invalid" | "expired" | "revoked";
|
|
114
|
+
export interface AuthFailureOptions {
|
|
115
|
+
/**
|
|
116
|
+
* False when signing in cannot fix it, such as a bad API key: MCP then answers a plain 401
|
|
117
|
+
* instead of the provider's `challenge`.
|
|
118
|
+
*/
|
|
119
|
+
readonly challenge?: boolean | undefined;
|
|
120
|
+
}
|
|
102
121
|
export declare class AuthFailure extends Error {
|
|
103
122
|
readonly reason: AuthFailureReason;
|
|
104
123
|
readonly name = "AuthFailure";
|
|
105
|
-
|
|
124
|
+
readonly challenge: boolean;
|
|
125
|
+
constructor(reason: AuthFailureReason, message: string, options?: AuthFailureOptions);
|
|
106
126
|
}
|
|
107
127
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,QAAQ,EACR,OAAO,EACP,aAAa,EACb,SAAS,EACT,QAAQ,EACR,MAAM,EACN,MAAM,EACP,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACnD,mFAAmF;IACnF,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,SAAS,EAAE,sBAAsB,GAAG,WAAW,CAAC;IACxD;;;;OAIG;IACH,QAAQ,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1E;;;OAGG;IACH,aAAa,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjF;;;OAGG;IACH,UAAU,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,+FAA+F;IAC/F,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAC7E,6DAA6D;IAC7D,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;CAC/D;AAED,gGAAgG;AAChG,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.js
CHANGED
|
@@ -3,9 +3,11 @@ export const HOST_GRANTS_TAG = "parlor_auth_hosts";
|
|
|
3
3
|
export class AuthFailure extends Error {
|
|
4
4
|
reason;
|
|
5
5
|
name = "AuthFailure";
|
|
6
|
-
|
|
6
|
+
challenge;
|
|
7
|
+
constructor(reason, message, options = {}) {
|
|
7
8
|
super(message);
|
|
8
9
|
this.reason = reason;
|
|
10
|
+
this.challenge = options.challenge ?? true;
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAqEA,gGAAgG;AAChG,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAAC;AA4EnD,MAAM,OAAO,WAAY,SAAQ,KAAK;IAIzB,MAAM;IAHC,IAAI,GAAG,aAAa,CAAC;IAC9B,SAAS,CAAU;IAC5B,YACW,MAAyB,EAClC,OAAe,EACf,OAAO,GAAuB,EAAE;QAEhC,KAAK,CAAC,OAAO,CAAC,CAAC;sBAJN,MAAM;QAKf,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IAC7C,CAAC;CACF","sourcesContent":["import type {\n AuthenticatedPrincipal,\n DeviceId,\n GrantId,\n HostPrincipal,\n KeyPrincipal,\n Principal,\n ThreadId,\n TurnId,\n UserId,\n} from \"@parlor/core\";\n\nimport type { ServerPlugin } from \"./plugin.js\";\n\n/**\n * Where identity comes from. The server calls `authenticate` once per request and hands\n * the principal to actions, MCP, sync and agent UI. It installs like any plugin, first.\n */\nexport interface AuthProvider extends ServerPlugin {\n /**\n * When true, an anonymous caller is refused by non-public actions, by MCP (with\n * `challenge`) and by `/ui`, and `x-parlor-source` no longer decides an action's source.\n */\n readonly enforcing: boolean;\n /**\n * Missing credentials resolve to `anonymous`. Present but invalid, expired or revoked\n * credentials throw `AuthFailure`, which every surface answers with 401.\n */\n authenticate(request: Request): Promise<Principal>;\n /** The 401 an MCP client reads to discover the authorization server (RFC 9728). */\n challenge(request: Request): Response;\n /**\n * Aborts when the session or grant behind the principal is revoked. Held connections\n * (sync streams) close on it.\n */\n revoked(principal: AuthenticatedPrincipal): AbortSignal;\n /**\n * Mints the turn token an inline agent's MCP calls carry while one chat turn runs,\n * bound to the host's grant. Optional so providers written before chat still compile;\n * `chat()` refuses to start on a provider without it.\n */\n delegate?(host: HostPrincipal, turn: TurnDelegation): Promise<Delegation>;\n /**\n * Mints the token an agent on the host's computer carries outside any turn, such as\n * Claude Code in a terminal: an external MCP client of the user, revoked with the grant.\n */\n delegateAgent?(host: HostPrincipal, agent: AgentDelegation): Promise<Delegation>;\n /**\n * Whether the host grant still holds. Lists of computers drop a grant that answers\n * false at once, without waiting for the host to notice. Absent means every grant holds.\n */\n hostActive?(grantId: GrantId): Promise<boolean>;\n /** A device's public keys as the account registered them, for prompt and answer signatures. */\n device?(userId: UserId, deviceId: DeviceId): Promise<DeviceKeys | undefined>;\n /** Who the agent works for, for the turn's context block. */\n account?(userId: UserId): Promise<AccountProfile | undefined>;\n}\n\n/**\n * The app's own API keys, which an auth provider routes to it: an `authorization: Bearer`\n * token that starts with `prefix` is a key, checked before any other credential.\n */\nexport interface KeyVerifier {\n /** Must hold a character outside `[A-Za-z0-9]`, such as `_`, so no session token starts with it. */\n readonly prefix: string;\n /** The key's principal, or undefined for an unknown or revoked key, which is answered 401. */\n verify(secret: string, request: Request): Promise<KeyPrincipal | undefined>;\n}\n\n/** Announced when a computer's grant is added or removed; whatever lists computers reads it. */\nexport const HOST_GRANTS_TAG = \"parlor_auth_hosts\";\n\nexport interface TurnDelegation {\n readonly threadId: ThreadId;\n readonly turnId: TurnId;\n readonly clientId: string;\n readonly agent: string;\n readonly ttlMs: number;\n /** The conversation's scope; a provider puts it in the token as `TurnBinding.scope`. */\n readonly scope?: string | undefined;\n}\n\nexport interface AgentDelegation {\n /** How the app names it, such as \"Claude Code on Alice's MacBook\". */\n readonly agent: string;\n readonly ttlMs: number;\n}\n\nexport interface Delegation {\n readonly token: string;\n readonly expiresAt: number;\n}\n\nexport interface DeviceKeys {\n readonly id: DeviceId;\n readonly name: string;\n readonly platform: string;\n readonly signingKey: JsonWebKey;\n readonly presenceKey?: PresenceKey | undefined;\n /** Linked devices' signed word that this one is the user's; each computer checks them itself. */\n readonly vouches?: readonly DeviceVouch[] | undefined;\n}\n\n/** A `VouchStatement` as the voucher signed it, with its presence key. */\nexport interface DeviceVouch {\n readonly voucher: DeviceId;\n readonly commitment: string;\n readonly at: number;\n readonly signature: {\n readonly keyId: string;\n readonly presence: \"none\" | \"user\";\n readonly format: \"es256-raw\" | \"webauthn\";\n readonly signature: string;\n readonly authenticatorData?: string | undefined;\n readonly clientDataJSON?: string | undefined;\n };\n}\n\nexport type PresenceKey =\n | {\n readonly format: \"webauthn\";\n readonly credentialId: string;\n readonly rpId: string;\n readonly publicKey: JsonWebKey;\n }\n | {\n readonly format: \"es256-raw\";\n readonly keyId: string;\n readonly publicKey: JsonWebKey;\n };\n\nexport interface AccountProfile {\n readonly name: string;\n readonly email?: string | undefined;\n}\n\nexport type AuthFailureReason = \"invalid\" | \"expired\" | \"revoked\";\n\nexport interface AuthFailureOptions {\n /**\n * False when signing in cannot fix it, such as a bad API key: MCP then answers a plain 401\n * instead of the provider's `challenge`.\n */\n readonly challenge?: boolean | undefined;\n}\n\nexport class AuthFailure extends Error {\n override readonly name = \"AuthFailure\";\n readonly challenge: boolean;\n constructor(\n readonly reason: AuthFailureReason,\n message: string,\n options: AuthFailureOptions = {},\n ) {\n super(message);\n this.challenge = options.challenge ?? true;\n }\n}\n"]}
|
package/dist/caller.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ActionSource, type Principal } from "@parlor/core";
|
|
2
|
+
import type { Surface } from "./access.js";
|
|
2
3
|
import type { AuthProvider } from "./auth.js";
|
|
3
4
|
import type { ScopeResolver } from "./scope.js";
|
|
4
5
|
export interface Caller {
|
|
@@ -6,16 +7,21 @@ export interface Caller {
|
|
|
6
7
|
readonly scope: string;
|
|
7
8
|
/** Scope plus user: whose screens this caller may see and drive (`audienceOf`). */
|
|
8
9
|
readonly audience: string;
|
|
10
|
+
/** The user's stream across scopes (`accountAudience`); undefined for a key or no user. */
|
|
11
|
+
readonly account: string | undefined;
|
|
9
12
|
/** The tab or app the request came from: a routing hint, never an identity. */
|
|
10
13
|
readonly clientId: string | undefined;
|
|
11
14
|
}
|
|
15
|
+
/** The sync streams a caller hears: its scope's, its own in the scope, and its account's. */
|
|
16
|
+
export declare function streamsOf(caller: Caller): string[];
|
|
12
17
|
export interface CallerResolver {
|
|
13
18
|
(request: Request): Promise<Caller>;
|
|
14
19
|
readonly enforcing: boolean;
|
|
15
20
|
}
|
|
16
21
|
export declare function sourceOf(request: Request): ActionSource;
|
|
17
22
|
export declare function clientOf(request: Request): string | undefined;
|
|
18
|
-
|
|
23
|
+
/** A key's source follows the surface it calls through: `mcp` on MCP, `http` elsewhere. */
|
|
24
|
+
export declare function sourceFor(declared: ActionSource, caller: Caller, enforcing: boolean, surface?: Surface): ActionSource;
|
|
19
25
|
/** `prepare` runs before the provider sees a request, so its tables exist by then. */
|
|
20
26
|
export declare function createCallerResolver(auth: AuthProvider | undefined, scope: ScopeResolver, prepare?: () => Promise<void>): CallerResolver;
|
|
21
27
|
//# sourceMappingURL=caller.d.ts.map
|
package/dist/caller.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caller.d.ts","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"caller.d.ts","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,SAAS,EAGf,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,6FAA6F;AAC7F,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAQlD;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AASD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAGvD;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE7D;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CACvB,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,EAClB,OAAO,CAAC,EAAE,OAAO,GAChB,YAAY,CAKd;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,YAAY,GAAG,SAAS,EAC9B,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC5B,cAAc,CAmChB"}
|
package/dist/caller.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { audienceOf } from "@parlor/agent-ui/server";
|
|
2
|
-
import { ANONYMOUS, sourceOfPrincipal, } from "@parlor/core";
|
|
1
|
+
import { accountAudience, audienceOf } from "@parlor/agent-ui/server";
|
|
2
|
+
import { ActionError, ANONYMOUS, sourceOfPrincipal, userOf, } from "@parlor/core";
|
|
3
3
|
import { CLIENT_HEADER, SOURCE_HEADER } from "./protocol.js";
|
|
4
|
+
/** The sync streams a caller hears: its scope's, its own in the scope, and its account's. */
|
|
5
|
+
export function streamsOf(caller) {
|
|
6
|
+
return [
|
|
7
|
+
...new Set([caller.scope, caller.audience, caller.account].filter((stream) => stream !== undefined)),
|
|
8
|
+
];
|
|
9
|
+
}
|
|
4
10
|
const SOURCES = new Set([
|
|
5
11
|
"ui",
|
|
6
12
|
"agent",
|
|
@@ -14,10 +20,14 @@ export function sourceOf(request) {
|
|
|
14
20
|
export function clientOf(request) {
|
|
15
21
|
return request.headers.get(CLIENT_HEADER) ?? undefined;
|
|
16
22
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
/** A key's source follows the surface it calls through: `mcp` on MCP, `http` elsewhere. */
|
|
24
|
+
export function sourceFor(declared, caller, enforcing, surface) {
|
|
25
|
+
const { principal } = caller;
|
|
26
|
+
if (principal.kind === "anonymous" && !enforcing)
|
|
27
|
+
return declared;
|
|
28
|
+
if (principal.kind === "key")
|
|
29
|
+
return surface === "mcp" ? "mcp" : "http";
|
|
30
|
+
return sourceOfPrincipal(principal);
|
|
21
31
|
}
|
|
22
32
|
/** `prepare` runs before the provider sees a request, so its tables exist by then. */
|
|
23
33
|
export function createCallerResolver(auth, scope, prepare) {
|
|
@@ -27,10 +37,18 @@ export function createCallerResolver(auth, scope, prepare) {
|
|
|
27
37
|
await prepare?.();
|
|
28
38
|
const principal = auth ? await auth.authenticate(request) : ANONYMOUS;
|
|
29
39
|
const resolvedScope = await scope(request, principal);
|
|
40
|
+
if (principal.kind === "key" && resolvedScope !== principal.scope) {
|
|
41
|
+
throw new ActionError("forbidden", "This key acts only in its own scope.");
|
|
42
|
+
}
|
|
43
|
+
const turnScope = principal.kind === "agent" ? principal.turn?.scope : undefined;
|
|
44
|
+
if (turnScope !== undefined && resolvedScope !== turnScope) {
|
|
45
|
+
throw new ActionError("forbidden", "This turn acts only in its conversation's scope.");
|
|
46
|
+
}
|
|
30
47
|
return {
|
|
31
48
|
principal,
|
|
32
49
|
scope: resolvedScope,
|
|
33
50
|
audience: audienceOf(resolvedScope, principal),
|
|
51
|
+
account: principal.kind === "key" ? undefined : accountAudience(userOf(principal)),
|
|
34
52
|
clientId: clientOf(request),
|
|
35
53
|
};
|
|
36
54
|
};
|
package/dist/caller.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caller.js","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"caller.js","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"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","sourcesContent":["import { accountAudience, audienceOf } from \"@parlor/agent-ui/server\";\nimport {\n ActionError,\n ANONYMOUS,\n type ActionSource,\n type Principal,\n sourceOfPrincipal,\n userOf,\n} from \"@parlor/core\";\n\nimport type { Surface } from \"./access.js\";\nimport type { AuthProvider } from \"./auth.js\";\nimport { CLIENT_HEADER, SOURCE_HEADER } from \"./protocol.js\";\nimport type { ScopeResolver } from \"./scope.js\";\n\nexport interface Caller {\n readonly principal: Principal;\n readonly scope: string;\n /** Scope plus user: whose screens this caller may see and drive (`audienceOf`). */\n readonly audience: string;\n /** The user's stream across scopes (`accountAudience`); undefined for a key or no user. */\n readonly account: string | undefined;\n /** The tab or app the request came from: a routing hint, never an identity. */\n readonly clientId: string | undefined;\n}\n\n/** The sync streams a caller hears: its scope's, its own in the scope, and its account's. */\nexport function streamsOf(caller: Caller): string[] {\n return [\n ...new Set(\n [caller.scope, caller.audience, caller.account].filter(\n (stream) => stream !== undefined,\n ),\n ),\n ];\n}\n\nexport interface CallerResolver {\n (request: Request): Promise<Caller>;\n readonly enforcing: boolean;\n}\n\nconst SOURCES: ReadonlySet<string> = new Set<ActionSource>([\n \"ui\",\n \"agent\",\n \"http\",\n \"mcp\",\n]);\n\nexport function sourceOf(request: Request): ActionSource {\n const header = request.headers.get(SOURCE_HEADER);\n return header && SOURCES.has(header) ? (header as ActionSource) : \"http\";\n}\n\nexport function clientOf(request: Request): string | undefined {\n return request.headers.get(CLIENT_HEADER) ?? undefined;\n}\n\n/** A key's source follows the surface it calls through: `mcp` on MCP, `http` elsewhere. */\nexport function sourceFor(\n declared: ActionSource,\n caller: Caller,\n enforcing: boolean,\n surface?: Surface,\n): ActionSource {\n const { principal } = caller;\n if (principal.kind === \"anonymous\" && !enforcing) return declared;\n if (principal.kind === \"key\") return surface === \"mcp\" ? \"mcp\" : \"http\";\n return sourceOfPrincipal(principal);\n}\n\n/** `prepare` runs before the provider sees a request, so its tables exist by then. */\nexport function createCallerResolver(\n auth: AuthProvider | undefined,\n scope: ScopeResolver,\n prepare?: () => Promise<void>,\n): CallerResolver {\n const callers = new WeakMap<Request, Promise<Caller>>();\n const resolve = async (request: Request): Promise<Caller> => {\n if (auth) await prepare?.();\n const principal = auth ? await auth.authenticate(request) : ANONYMOUS;\n const resolvedScope = await scope(request, principal);\n if (principal.kind === \"key\" && resolvedScope !== principal.scope) {\n throw new ActionError(\"forbidden\", \"This key acts only in its own scope.\");\n }\n const turnScope = principal.kind === \"agent\" ? principal.turn?.scope : undefined;\n if (turnScope !== undefined && resolvedScope !== turnScope) {\n throw new ActionError(\n \"forbidden\",\n \"This turn acts only in its conversation's scope.\",\n );\n }\n return {\n principal,\n scope: resolvedScope,\n audience: audienceOf(resolvedScope, principal),\n account: principal.kind === \"key\" ? undefined : accountAudience(userOf(principal)),\n clientId: clientOf(request),\n };\n };\n return Object.assign(\n (request: Request) => {\n let caller = callers.get(request);\n if (!caller) {\n caller = resolve(request);\n callers.set(request, caller);\n }\n return caller;\n },\n { enforcing: auth?.enforcing === true },\n );\n}\n"]}
|
package/dist/create-server.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type AgentUiOptions, type UiManifestSource } from "@parlor/agent-ui/server";
|
|
2
|
-
import { type ActionMap, type DataProvider } from "@parlor/core";
|
|
2
|
+
import { type ActionMap, type ConfirmationPolicy, type DataProvider } from "@parlor/core";
|
|
3
3
|
import type { ActionRegistry } from "@parlor/core/registry";
|
|
4
4
|
import { type SyncTransport } from "@parlor/sync/server";
|
|
5
5
|
import { type AuthProvider } from "./auth.js";
|
|
6
6
|
import { type CorsConfig } from "./cors.js";
|
|
7
7
|
import type { ServerPlugin } from "./plugin.js";
|
|
8
8
|
import { type ScopeResolver } from "./scope.js";
|
|
9
|
+
import { type ActionVisibility } from "./visibility.js";
|
|
9
10
|
export interface SyncOptions {
|
|
10
11
|
readonly transport?: SyncTransport | undefined;
|
|
11
12
|
readonly pollIntervalMs?: number | undefined;
|
|
@@ -29,6 +30,20 @@ export interface ServerOptions<T extends ActionMap> {
|
|
|
29
30
|
} | false | undefined;
|
|
30
31
|
readonly sync?: SyncOptions | false | undefined;
|
|
31
32
|
readonly agentUi?: AgentUiOptions | false | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Decides, for each call, whether the user approves it in the app first (`ask`), it runs
|
|
35
|
+
* (`proceed`) or it fails with `confirmation_unavailable` (`refuse`). Defaults to
|
|
36
|
+
* `defaultConfirmationPolicy`: agents are asked about `confirm: true` actions and keys are
|
|
37
|
+
* refused them.
|
|
38
|
+
*/
|
|
39
|
+
readonly confirmation?: ConfirmationPolicy | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Which of `actions` each caller may see and call, decided once per request. A hidden
|
|
42
|
+
* action is left out of the manifest and MCP `tools/list`, and a call to it answers
|
|
43
|
+
* `not_found` like an action that does not exist. Plugins' actions are never filtered.
|
|
44
|
+
* Absent, every action is visible.
|
|
45
|
+
*/
|
|
46
|
+
readonly visible?: ActionVisibility | undefined;
|
|
32
47
|
}
|
|
33
48
|
export interface FrameworkServer {
|
|
34
49
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-server.d.ts","sourceRoot":"","sources":["../src/create-server.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EAInB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"create-server.d.ts","sourceRoot":"","sources":["../src/create-server.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EAInB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,kBAAkB,EACvB,KAAK,YAAY,EAIlB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAQ3D,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,WAAW,CAAC;AAI5D,OAAO,KAAK,EAMV,YAAY,EACb,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EACL,KAAK,gBAAgB,EAGtB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9C;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,SAAS;IAChD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACzC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,KAAK,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,GAAG,CAAC,EACT;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAC7E,KAAK,GACL,SAAS,CAAC;IACd,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,KAAK,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,KAAK,GAAG,SAAS,CAAC;IACtD;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACvD;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACjD;AAKD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC9E,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAoND,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAC9C,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,eAAe,CA+TjB"}
|