@parlor/client 0.2.0 → 0.3.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/action-client.d.ts +7 -1
- package/dist/action-client.d.ts.map +1 -1
- package/dist/action-client.js +10 -2
- package/dist/action-client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/chat/chat-ask.d.ts +3 -2
- package/dist/react/chat/chat-ask.d.ts.map +1 -1
- package/dist/react/chat/chat-ask.js +3 -2
- package/dist/react/chat/chat-ask.js.map +1 -1
- package/dist/react/chat/runtime.d.ts +17 -3
- package/dist/react/chat/runtime.d.ts.map +1 -1
- package/dist/react/chat/runtime.js +117 -16
- package/dist/react/chat/runtime.js.map +1 -1
- package/dist/react/chat/use-chat.d.ts.map +1 -1
- package/dist/react/chat/use-chat.js +2 -0
- package/dist/react/chat/use-chat.js.map +1 -1
- package/dist/react/chat/use-streaming-text.d.ts +7 -0
- package/dist/react/chat/use-streaming-text.d.ts.map +1 -0
- package/dist/react/chat/use-streaming-text.js +59 -0
- package/dist/react/chat/use-streaming-text.js.map +1 -0
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/hooks.js +3 -6
- package/dist/react/hooks.js.map +1 -1
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/write-refresh.d.ts +27 -0
- package/dist/react/write-refresh.d.ts.map +1 -0
- package/dist/react/write-refresh.js +80 -0
- package/dist/react/write-refresh.js.map +1 -0
- package/dist/session.d.ts +7 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js.map +1 -1
- package/package.json +6 -6
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAC;AAqB3C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAA0B,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAwB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAwB,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAEL,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAIL,iBAAiB,EACjB,cAAc,EACd,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,UAAU,GACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAwB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC","sourcesContent":["export type {\n AgentActivity,\n AgentActivityKind,\n AgentOutcome,\n AgentConfirmation,\n} from \"@parlor/agent-ui/client\";\nexport type { AgentActivityState } from \"./agent-ui/activity-state.js\";\nexport * as Chat from \"./agent-ui/chat.js\";\nexport type {\n ChatAskChoiceProps,\n ChatAskFormProps,\n ChatAskProps,\n} from \"./chat/chat-ask.js\";\nexport type { ChatRootProps } from \"./chat/chat-root.js\";\nexport type { ChatThreadProps } from \"./chat/chat-thread.js\";\nexport type {\n AgentChoice,\n AgentChoiceOption,\n ChatCommands,\n ChatState,\n ChatStatus,\n ChatUnavailable,\n ChatWait,\n ConnectOption,\n DeviceSetup,\n DeviceSetupCommands,\n LinkState,\n} from \"./chat/runtime.js\";\nexport { useChat } from \"./chat/use-chat.js\";\nexport { useStreamingText } from \"./chat/use-streaming-text.js\";\nexport { useDeviceSetup } from \"./chat/use-device-setup.js\";\nexport { type RemoveDeviceState, useDevices } from \"./use-devices.js\";\nexport { type LinkDeviceState, useLinkDevice } from \"./use-link-device.js\";\nexport type { ChatItem } from \"@parlor/chat\";\nexport { useAgentActivity } from \"./agent-ui/use-agent-activity.js\";\nexport { useAgentConfirmations } from \"./agent-ui/use-agent-confirmations.js\";\nexport { type AgentViewHandle, useAgentView } from \"./agent-ui/use-agent-view.js\";\nexport {\n type ActionQueryContext,\n actionQueryOptions,\n ensureActionQueryData,\n} from \"./action-query.js\";\nexport { callAndRefresh } from \"./write-refresh.js\";\nexport {\n type ActionMutationOptions,\n type ActionQueryOptions,\n type ActionSuspenseQueryOptions,\n useActionMutation,\n useActionQuery,\n useActionSuspenseQuery,\n} from \"./hooks.js\";\nexport {\n FrameworkProvider,\n type FrameworkProviderProps,\n useActionClient,\n useSession,\n} from \"./provider.js\";\nexport { type CapabilityState, useCapability } from \"./use-capability.js\";\nexport { invalidateForEvents, ReadTagRegistry } from \"./sync.js\";\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ActionInput, type ActionOutput, type ReadRequest } from "@parlor/core";
|
|
2
|
+
import type { Query, QueryClient } from "@tanstack/react-query";
|
|
3
|
+
import type { ActionEnvelope, CallOptions } from "../action-client.js";
|
|
4
|
+
import type { RegisteredActions } from "../register.js";
|
|
5
|
+
import type { ActionQueryContext } from "./action-query.js";
|
|
6
|
+
export interface ShownRead {
|
|
7
|
+
readonly query: Query;
|
|
8
|
+
readonly request: ReadRequest;
|
|
9
|
+
}
|
|
10
|
+
/** The action queries the page shows, to name on a write so it answers with them fresh. */
|
|
11
|
+
export declare function shownReads(queryClient: QueryClient): ShownRead[];
|
|
12
|
+
/**
|
|
13
|
+
* Brings the cache up to date after a write. The reads the server ran again go straight in;
|
|
14
|
+
* a shown read it could not run, or one whose reads are unknown here, is fetched again, and
|
|
15
|
+
* this waits for it; data nothing shows that the write changed is marked out of date. A
|
|
16
|
+
* server that did not say what the write changed gets every action query fetched again.
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyWrite(queryClient: QueryClient, shown: readonly ShownRead[], envelope: ActionEnvelope<unknown>): Promise<void>;
|
|
19
|
+
type Name = keyof RegisteredActions & string;
|
|
20
|
+
/**
|
|
21
|
+
* Calls an action and brings the page up to date with what it changed, in the same round
|
|
22
|
+
* trip: the write answers with the reads the page shows that it touched. What
|
|
23
|
+
* `useActionMutation` runs; for a flow of several calls behind one button, call each with it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function callAndRefresh<K extends Name>({ client, queryClient }: ActionQueryContext, name: K, input: ActionInput<RegisteredActions[K]>, options?: Omit<CallOptions, "refresh">): Promise<ActionOutput<RegisteredActions[K]>>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=write-refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-refresh.d.ts","sourceRoot":"","sources":["../../src/react/write-refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EAEjB,KAAK,WAAW,EAEjB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS,EAAE,CAMhE;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAsDf;AAED,KAAK,IAAI,GAAG,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAE7C;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,CAAC,SAAS,IAAI,EACjD,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,kBAAkB,EAC3C,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,EACxC,OAAO,GAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAM,GACzC,OAAO,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAU7C"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { MAX_REFRESHED_READS, tagsOverlap, } from "@parlor/core";
|
|
2
|
+
import { ACTION_QUERY_PREFIX } from "../query-keys.js";
|
|
3
|
+
import { readTagsOf } from "./sync.js";
|
|
4
|
+
/** The action queries the page shows, to name on a write so it answers with them fresh. */
|
|
5
|
+
export function shownReads(queryClient) {
|
|
6
|
+
return queryClient
|
|
7
|
+
.getQueryCache()
|
|
8
|
+
.findAll({ queryKey: ACTION_QUERY_PREFIX, type: "active" })
|
|
9
|
+
.slice(0, MAX_REFRESHED_READS)
|
|
10
|
+
.map((query) => ({ query, request: [String(query.queryKey[2]), query.queryKey[3]] }));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Brings the cache up to date after a write. The reads the server ran again go straight in;
|
|
14
|
+
* a shown read it could not run, or one whose reads are unknown here, is fetched again, and
|
|
15
|
+
* this waits for it; data nothing shows that the write changed is marked out of date. A
|
|
16
|
+
* server that did not say what the write changed gets every action query fetched again.
|
|
17
|
+
*/
|
|
18
|
+
export async function applyWrite(queryClient, shown, envelope) {
|
|
19
|
+
const { writes, refreshed } = envelope;
|
|
20
|
+
if (!writes || !refreshed) {
|
|
21
|
+
await queryClient.invalidateQueries({ queryKey: ACTION_QUERY_PREFIX });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const registry = readTagsOf(queryClient);
|
|
25
|
+
const changed = (query) => {
|
|
26
|
+
const tags = registry.tagsFor(query.queryHash);
|
|
27
|
+
return (tags === undefined ||
|
|
28
|
+
tags.some((read) => writes.some((write) => tagsOverlap(read, write))));
|
|
29
|
+
};
|
|
30
|
+
const refetch = [];
|
|
31
|
+
await Promise.all(shown.map(async ({ query }, index) => {
|
|
32
|
+
const fresh = refreshed[index];
|
|
33
|
+
// The server judged it untouched by the write.
|
|
34
|
+
if (fresh === null)
|
|
35
|
+
return;
|
|
36
|
+
if (fresh === undefined) {
|
|
37
|
+
if (changed(query))
|
|
38
|
+
refetch.push(query);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if ("error" in fresh) {
|
|
42
|
+
refetch.push(query);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// A fetch that started before the write would land older data over it.
|
|
46
|
+
if (query.state.fetchStatus === "fetching") {
|
|
47
|
+
await queryClient.cancelQueries({ queryKey: query.queryKey, exact: true });
|
|
48
|
+
}
|
|
49
|
+
registry.record(query.queryHash, fresh.tags, fresh.cursor);
|
|
50
|
+
queryClient.setQueryData(query.queryKey, fresh.data);
|
|
51
|
+
}));
|
|
52
|
+
const named = new Set(shown.map(({ query }) => query.queryHash));
|
|
53
|
+
for (const query of queryClient
|
|
54
|
+
.getQueryCache()
|
|
55
|
+
.findAll({ queryKey: ACTION_QUERY_PREFIX })) {
|
|
56
|
+
if (named.has(query.queryHash) || !changed(query))
|
|
57
|
+
continue;
|
|
58
|
+
void queryClient.invalidateQueries({
|
|
59
|
+
queryKey: query.queryKey,
|
|
60
|
+
exact: true,
|
|
61
|
+
refetchType: "none",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
await Promise.all(refetch.map((query) => queryClient.invalidateQueries({ queryKey: query.queryKey, exact: true })));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Calls an action and brings the page up to date with what it changed, in the same round
|
|
68
|
+
* trip: the write answers with the reads the page shows that it touched. What
|
|
69
|
+
* `useActionMutation` runs; for a flow of several calls behind one button, call each with it.
|
|
70
|
+
*/
|
|
71
|
+
export async function callAndRefresh({ client, queryClient }, name, input, options = {}) {
|
|
72
|
+
const shown = shownReads(queryClient);
|
|
73
|
+
const envelope = await client.invoke(name, input, {
|
|
74
|
+
...options,
|
|
75
|
+
refresh: shown.map(({ request }) => request),
|
|
76
|
+
});
|
|
77
|
+
await applyWrite(queryClient, shown, envelope).catch(() => queryClient.invalidateQueries({ queryKey: ACTION_QUERY_PREFIX }));
|
|
78
|
+
return envelope.data;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=write-refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-refresh.js","sourceRoot":"","sources":["../../src/react/write-refresh.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ActionInput,\n type ActionOutput,\n MAX_REFRESHED_READS,\n type ReadRequest,\n tagsOverlap,\n} from \"@parlor/core\";\nimport type { Query, QueryClient } from \"@tanstack/react-query\";\n\nimport type { ActionEnvelope, CallOptions } from \"../action-client.js\";\nimport { ACTION_QUERY_PREFIX } from \"../query-keys.js\";\nimport type { RegisteredActions } from \"../register.js\";\nimport type { ActionQueryContext } from \"./action-query.js\";\nimport { readTagsOf } from \"./sync.js\";\n\nexport interface ShownRead {\n readonly query: Query;\n readonly request: ReadRequest;\n}\n\n/** The action queries the page shows, to name on a write so it answers with them fresh. */\nexport function shownReads(queryClient: QueryClient): ShownRead[] {\n return queryClient\n .getQueryCache()\n .findAll({ queryKey: ACTION_QUERY_PREFIX, type: \"active\" })\n .slice(0, MAX_REFRESHED_READS)\n .map((query) => ({ query, request: [String(query.queryKey[2]), query.queryKey[3]] }));\n}\n\n/**\n * Brings the cache up to date after a write. The reads the server ran again go straight in;\n * a shown read it could not run, or one whose reads are unknown here, is fetched again, and\n * this waits for it; data nothing shows that the write changed is marked out of date. A\n * server that did not say what the write changed gets every action query fetched again.\n */\nexport async function applyWrite(\n queryClient: QueryClient,\n shown: readonly ShownRead[],\n envelope: ActionEnvelope<unknown>,\n): Promise<void> {\n const { writes, refreshed } = envelope;\n if (!writes || !refreshed) {\n await queryClient.invalidateQueries({ queryKey: ACTION_QUERY_PREFIX });\n return;\n }\n const registry = readTagsOf(queryClient);\n const changed = (query: Query) => {\n const tags = registry.tagsFor(query.queryHash);\n return (\n tags === undefined ||\n tags.some((read) => writes.some((write) => tagsOverlap(read, write)))\n );\n };\n\n const refetch: Query[] = [];\n await Promise.all(\n shown.map(async ({ query }, index) => {\n const fresh = refreshed[index];\n // The server judged it untouched by the write.\n if (fresh === null) return;\n if (fresh === undefined) {\n if (changed(query)) refetch.push(query);\n return;\n }\n if (\"error\" in fresh) {\n refetch.push(query);\n return;\n }\n // A fetch that started before the write would land older data over it.\n if (query.state.fetchStatus === \"fetching\") {\n await queryClient.cancelQueries({ queryKey: query.queryKey, exact: true });\n }\n registry.record(query.queryHash, fresh.tags, fresh.cursor);\n queryClient.setQueryData(query.queryKey, fresh.data);\n }),\n );\n\n const named = new Set(shown.map(({ query }) => query.queryHash));\n for (const query of queryClient\n .getQueryCache()\n .findAll({ queryKey: ACTION_QUERY_PREFIX })) {\n if (named.has(query.queryHash) || !changed(query)) continue;\n void queryClient.invalidateQueries({\n queryKey: query.queryKey,\n exact: true,\n refetchType: \"none\",\n });\n }\n await Promise.all(\n refetch.map((query) =>\n queryClient.invalidateQueries({ queryKey: query.queryKey, exact: true }),\n ),\n );\n}\n\ntype Name = keyof RegisteredActions & string;\n\n/**\n * Calls an action and brings the page up to date with what it changed, in the same round\n * trip: the write answers with the reads the page shows that it touched. What\n * `useActionMutation` runs; for a flow of several calls behind one button, call each with it.\n */\nexport async function callAndRefresh<K extends Name>(\n { client, queryClient }: ActionQueryContext,\n name: K,\n input: ActionInput<RegisteredActions[K]>,\n options: Omit<CallOptions, \"refresh\"> = {},\n): Promise<ActionOutput<RegisteredActions[K]>> {\n const shown = shownReads(queryClient);\n const envelope = await client.invoke(name, input, {\n ...options,\n refresh: shown.map(({ request }) => request),\n });\n await applyWrite(queryClient, shown, envelope).catch(() =>\n queryClient.invalidateQueries({ queryKey: ACTION_QUERY_PREFIX }),\n );\n return envelope.data;\n}\n"]}
|
package/dist/session.d.ts
CHANGED
|
@@ -90,6 +90,8 @@ export interface DeviceLinker {
|
|
|
90
90
|
/** It approved `connect` on the computer, so its first prompt links it. */
|
|
91
91
|
readonly pairing: boolean;
|
|
92
92
|
}>;
|
|
93
|
+
/** Called when another tab changes what `linkState` reads, such as approving `connect`. */
|
|
94
|
+
subscribeLink?(listener: () => void): () => void;
|
|
93
95
|
/** Enrolls this device, with Face ID or Touch ID unless `presence` is false, and shows its code. */
|
|
94
96
|
startLink(options?: {
|
|
95
97
|
readonly presence?: boolean;
|
|
@@ -116,6 +118,10 @@ export interface DeviceLinker {
|
|
|
116
118
|
/** Removes a device, confirmed with Face ID or Touch ID; throws `LinkOnComputer` without them. */
|
|
117
119
|
removeDevice(id: string): Promise<void>;
|
|
118
120
|
}
|
|
121
|
+
/** Ends a computer's connection: its sidebar turns and its coding agents both lose the app. */
|
|
122
|
+
export interface ComputerDisconnector {
|
|
123
|
+
disconnectComputer(hostId: string): Promise<void>;
|
|
124
|
+
}
|
|
119
125
|
/** What `FrameworkProvider` takes as `auth`; signing, host pairing and linking are optional. */
|
|
120
|
-
export type FrameworkAuth = SessionSource & Partial<DeviceSource & HostAuthorizer & DeviceLinker>;
|
|
126
|
+
export type FrameworkAuth = SessionSource & Partial<DeviceSource & HostAuthorizer & DeviceLinker & ComputerDisconnector>;
|
|
121
127
|
//# sourceMappingURL=session.d.ts.map
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,4FAA4F;AAC5F,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACjC;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,IAAI,eAAe,CAAC;IAC3B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,mFAAmF;IACnF,UAAU,CACR,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GACxC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,mFAAmF;IACnF,UAAU,CACR,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,GACtC,OAAO,CAAC,aAAa,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IACtF,kFAAkF;IAClF,WAAW,CAAC,IAAI,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,sFAAsF;IACtF,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACtD;AAED,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mBAAmB;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,6FAA6F;AAC7F,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAkB,IAAI,oBAAoB;CAC3C;AAED,0FAA0F;AAC1F,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,SAAS,IAAI,OAAO,CAAC;QACnB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,2EAA2E;QAC3E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;KAC3B,CAAC,CAAC;IACH,oGAAoG;IACpG,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;QAC5D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,kEAAkE;QAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC,CAAC;IACH;;;OAGG;IACH,WAAW,CACT,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GACxE,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,yFAAyF;IACzF,KAAK,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,2EAA2E;IAC3E,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,WAAW,IAAI,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IACjD,kGAAkG;IAClG,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED,gGAAgG;AAChG,MAAM,MAAM,aAAa,GAAG,aAAa,GACvC,OAAO,CAAC,YAAY,GAAG,cAAc,GAAG,YAAY,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAmFA,6FAA6F;AAC7F,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrB,IAAI,GAAG,gBAAgB,CAAC;CAC3C","sourcesContent":["import type { HostPairing, PairingDevice } from \"@parlor/capabilities\";\nimport type { AnswerToSign, ItemSignature, PromptToSign } from \"@parlor/chat\";\nimport type { DeviceId } from \"@parlor/core\";\n\nexport interface SessionUser {\n readonly id: string;\n readonly name: string;\n readonly email: string;\n}\n\n/** `anonymous`: the server signs nobody in, so everyone uses the app without an account. */\nexport type SessionSnapshot =\n | { readonly status: \"loading\" }\n | { readonly status: \"anonymous\" }\n | { readonly status: \"signed-out\" }\n | { readonly status: \"signed-in\"; readonly user: SessionUser };\n\n/**\n * Who is signed in on this device, as the app's auth client sees it. `current` returns the\n * same object until the session changes.\n */\nexport interface SessionSource {\n current(): SessionSnapshot;\n subscribe(listener: () => void): () => void;\n}\n\n/** This device's signing identity, as the chat needs it. */\nexport interface SessionDevice {\n readonly id: DeviceId;\n /** The public half of its keys, as the account registered them. */\n readonly keys: PairingDevice;\n /** With `presence`, the platform asks for Touch ID, Face ID or a passkey first. */\n signPrompt(\n prompt: PromptToSign,\n options?: { readonly presence?: boolean },\n ): Promise<ItemSignature>;\n /** With `presence`, the platform asks for Touch ID, Face ID or a passkey first. */\n signAnswer(\n answer: AnswerToSign,\n options: { readonly presence: boolean },\n ): Promise<ItemSignature>;\n}\n\nexport interface DeviceSource {\n /**\n * Enrolls this device with the account on first use; undefined where it cannot sign.\n * `presence: false` enrolls it without a presence key. Rejects with the capability's\n * `PresenceSetupCancelled` when the user dismisses the passkey prompt.\n */\n device(options?: { readonly presence?: boolean }): Promise<SessionDevice | undefined>;\n /** `needed` while the first `device()` would ask the user to create a passkey. */\n deviceSetup?(): Promise<\"needed\" | \"ready\">;\n}\n\nexport interface HostAuthorizer {\n /** For pairing the host on this computer: a code for it, approved by this session. */\n authorizeHost(pairing: HostPairing): Promise<string>;\n}\n\n/** A device waiting to be linked, its keys checked against the code it shows. */\nexport interface PendingLink {\n readonly id: string;\n readonly name: string;\n readonly platform: string;\n readonly commitment: string;\n /** The short code the device shows, for `parlor-host link` on the computer. */\n readonly code: string;\n}\n\n/** One of the account's devices, as the devices list shows it. */\nexport interface AccountDevice {\n readonly id: string;\n readonly name: string;\n readonly platform: string;\n /** This device. */\n readonly current: boolean;\n readonly createdAt: string;\n readonly vouched: boolean;\n readonly linking: boolean;\n /** The device whose vouch linked it last. */\n readonly linkedBy?: string | undefined;\n}\n\n/** Thrown by `vouch` on a device with no presence key: the computer itself links it then. */\nexport class LinkOnComputer extends Error {\n override readonly name = \"LinkOnComputer\";\n}\n\n/** Linking devices: a new one shows a code, a linked one checks it and vouches for it. */\nexport interface DeviceLinker {\n /** Never prompts: a device not created yet reads as none. */\n linkState(): Promise<{\n readonly deviceId?: string | undefined;\n readonly vouched: boolean;\n /** It approved `connect` on the computer, so its first prompt links it. */\n readonly pairing: boolean;\n }>;\n /** Enrolls this device, with Face ID or Touch ID unless `presence` is false, and shows its code. */\n startLink(options?: { readonly presence?: boolean }): Promise<{\n readonly deviceId: string;\n readonly code: string;\n /** The whole hash the code is the start of; the QR carries it. */\n readonly commitment: string;\n readonly expiresAt: string;\n }>;\n /**\n * The device showing `code`, or the one whose QR named its id and whole commitment; throws\n * when the keys the app returns do not match.\n */\n resolveLink(\n from: string | { readonly deviceId: string; readonly commitment: string },\n ): Promise<PendingLink>;\n /** Vouches with this device's presence key; throws `LinkOnComputer` when it has none. */\n vouch(link: PendingLink): Promise<void>;\n /** Whether this device has a presence key to vouch with; never prompts. */\n canVouch(): Promise<boolean>;\n listDevices(): Promise<readonly AccountDevice[]>;\n /** Removes a device, confirmed with Face ID or Touch ID; throws `LinkOnComputer` without them. */\n removeDevice(id: string): Promise<void>;\n}\n\n/** What `FrameworkProvider` takes as `auth`; signing, host pairing and linking are optional. */\nexport type FrameworkAuth = SessionSource &\n Partial<DeviceSource & HostAuthorizer & DeviceLinker>;\n"]}
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAmFA,6FAA6F;AAC7F,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrB,IAAI,GAAG,gBAAgB,CAAC;CAC3C","sourcesContent":["import type { HostPairing, PairingDevice } from \"@parlor/capabilities\";\nimport type { AnswerToSign, ItemSignature, PromptToSign } from \"@parlor/chat\";\nimport type { DeviceId } from \"@parlor/core\";\n\nexport interface SessionUser {\n readonly id: string;\n readonly name: string;\n readonly email: string;\n}\n\n/** `anonymous`: the server signs nobody in, so everyone uses the app without an account. */\nexport type SessionSnapshot =\n | { readonly status: \"loading\" }\n | { readonly status: \"anonymous\" }\n | { readonly status: \"signed-out\" }\n | { readonly status: \"signed-in\"; readonly user: SessionUser };\n\n/**\n * Who is signed in on this device, as the app's auth client sees it. `current` returns the\n * same object until the session changes.\n */\nexport interface SessionSource {\n current(): SessionSnapshot;\n subscribe(listener: () => void): () => void;\n}\n\n/** This device's signing identity, as the chat needs it. */\nexport interface SessionDevice {\n readonly id: DeviceId;\n /** The public half of its keys, as the account registered them. */\n readonly keys: PairingDevice;\n /** With `presence`, the platform asks for Touch ID, Face ID or a passkey first. */\n signPrompt(\n prompt: PromptToSign,\n options?: { readonly presence?: boolean },\n ): Promise<ItemSignature>;\n /** With `presence`, the platform asks for Touch ID, Face ID or a passkey first. */\n signAnswer(\n answer: AnswerToSign,\n options: { readonly presence: boolean },\n ): Promise<ItemSignature>;\n}\n\nexport interface DeviceSource {\n /**\n * Enrolls this device with the account on first use; undefined where it cannot sign.\n * `presence: false` enrolls it without a presence key. Rejects with the capability's\n * `PresenceSetupCancelled` when the user dismisses the passkey prompt.\n */\n device(options?: { readonly presence?: boolean }): Promise<SessionDevice | undefined>;\n /** `needed` while the first `device()` would ask the user to create a passkey. */\n deviceSetup?(): Promise<\"needed\" | \"ready\">;\n}\n\nexport interface HostAuthorizer {\n /** For pairing the host on this computer: a code for it, approved by this session. */\n authorizeHost(pairing: HostPairing): Promise<string>;\n}\n\n/** A device waiting to be linked, its keys checked against the code it shows. */\nexport interface PendingLink {\n readonly id: string;\n readonly name: string;\n readonly platform: string;\n readonly commitment: string;\n /** The short code the device shows, for `parlor-host link` on the computer. */\n readonly code: string;\n}\n\n/** One of the account's devices, as the devices list shows it. */\nexport interface AccountDevice {\n readonly id: string;\n readonly name: string;\n readonly platform: string;\n /** This device. */\n readonly current: boolean;\n readonly createdAt: string;\n readonly vouched: boolean;\n readonly linking: boolean;\n /** The device whose vouch linked it last. */\n readonly linkedBy?: string | undefined;\n}\n\n/** Thrown by `vouch` on a device with no presence key: the computer itself links it then. */\nexport class LinkOnComputer extends Error {\n override readonly name = \"LinkOnComputer\";\n}\n\n/** Linking devices: a new one shows a code, a linked one checks it and vouches for it. */\nexport interface DeviceLinker {\n /** Never prompts: a device not created yet reads as none. */\n linkState(): Promise<{\n readonly deviceId?: string | undefined;\n readonly vouched: boolean;\n /** It approved `connect` on the computer, so its first prompt links it. */\n readonly pairing: boolean;\n }>;\n /** Called when another tab changes what `linkState` reads, such as approving `connect`. */\n subscribeLink?(listener: () => void): () => void;\n /** Enrolls this device, with Face ID or Touch ID unless `presence` is false, and shows its code. */\n startLink(options?: { readonly presence?: boolean }): Promise<{\n readonly deviceId: string;\n readonly code: string;\n /** The whole hash the code is the start of; the QR carries it. */\n readonly commitment: string;\n readonly expiresAt: string;\n }>;\n /**\n * The device showing `code`, or the one whose QR named its id and whole commitment; throws\n * when the keys the app returns do not match.\n */\n resolveLink(\n from: string | { readonly deviceId: string; readonly commitment: string },\n ): Promise<PendingLink>;\n /** Vouches with this device's presence key; throws `LinkOnComputer` when it has none. */\n vouch(link: PendingLink): Promise<void>;\n /** Whether this device has a presence key to vouch with; never prompts. */\n canVouch(): Promise<boolean>;\n listDevices(): Promise<readonly AccountDevice[]>;\n /** Removes a device, confirmed with Face ID or Touch ID; throws `LinkOnComputer` without them. */\n removeDevice(id: string): Promise<void>;\n}\n\n/** Ends a computer's connection: its sidebar turns and its coding agents both lose the app. */\nexport interface ComputerDisconnector {\n disconnectComputer(hostId: string): Promise<void>;\n}\n\n/** What `FrameworkProvider` takes as `auth`; signing, host pairing and linking are optional. */\nexport type FrameworkAuth = SessionSource &\n Partial<DeviceSource & HostAuthorizer & DeviceLinker & ComputerDisconnector>;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parlor/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"homepage": "https://cychien.github.io/parlor",
|
|
5
5
|
"bugs": "https://github.com/cychien/parlor/issues",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@parlor/
|
|
37
|
-
"@parlor/chat": "0.
|
|
38
|
-
"@parlor/
|
|
39
|
-
"@parlor/
|
|
40
|
-
"@parlor/
|
|
36
|
+
"@parlor/core": "0.3.1",
|
|
37
|
+
"@parlor/chat": "0.3.1",
|
|
38
|
+
"@parlor/sync": "0.3.1",
|
|
39
|
+
"@parlor/agent-ui": "0.3.1",
|
|
40
|
+
"@parlor/capabilities": "0.3.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@tanstack/react-query": "^5.104.1",
|