fauna-api 0.0.0-stage → 1.0.679
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/brands.d.ts +2 -0
- package/dist/brands.js +2 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +121 -0
- package/dist/src/ant/chat.d.ts +130 -0
- package/dist/src/ant/chat.js +467 -0
- package/dist/src/ant/format.d.ts +40 -0
- package/dist/src/ant/format.js +143 -0
- package/dist/src/ant/graph.d.ts +130 -0
- package/dist/src/ant/graph.js +449 -0
- package/dist/src/ant/index.d.ts +16 -0
- package/dist/src/ant/index.js +61 -0
- package/dist/src/ant/persona.d.ts +151 -0
- package/dist/src/ant/persona.js +136 -0
- package/dist/src/ant/timeline.d.ts +283 -0
- package/dist/src/ant/timeline.js +1025 -0
- package/dist/src/authentification.d.ts +45 -0
- package/dist/src/authentification.js +194 -0
- package/dist/src/billing.d.ts +135 -0
- package/dist/src/billing.js +128 -0
- package/dist/src/calendar.d.ts +83 -0
- package/dist/src/calendar.js +133 -0
- package/dist/src/chats.d.ts +113 -0
- package/dist/src/chats.js +100 -0
- package/dist/src/cicada/contacts.d.ts +31 -0
- package/dist/src/cicada/contacts.js +28 -0
- package/dist/src/cicada/conversations.d.ts +73 -0
- package/dist/src/cicada/conversations.js +236 -0
- package/dist/src/cicada/inboxes.d.ts +48 -0
- package/dist/src/cicada/inboxes.js +82 -0
- package/dist/src/cicada/index.d.ts +16 -0
- package/dist/src/cicada/index.js +61 -0
- package/dist/src/cicada/phone.d.ts +48 -0
- package/dist/src/cicada/phone.js +306 -0
- package/dist/src/drive.d.ts +80 -0
- package/dist/src/drive.js +472 -0
- package/dist/src/extensions/index.d.ts +4 -0
- package/dist/src/extensions/index.js +5 -0
- package/dist/src/extensions/web/index.d.ts +6 -0
- package/dist/src/extensions/web/index.js +7 -0
- package/dist/src/extensions/web/session.d.ts +108 -0
- package/dist/src/extensions/web/session.js +470 -0
- package/dist/src/extensions/web/tools.d.ts +13 -0
- package/dist/src/extensions/web/tools.js +72 -0
- package/dist/src/friends.d.ts +39 -0
- package/dist/src/friends.js +119 -0
- package/dist/src/groups.d.ts +82 -0
- package/dist/src/groups.js +269 -0
- package/dist/src/inbox.d.ts +11 -0
- package/dist/src/inbox.js +35 -0
- package/dist/src/integrations.d.ts +52 -0
- package/dist/src/integrations.js +205 -0
- package/dist/src/magpie/explorer.d.ts +14 -0
- package/dist/src/magpie/explorer.js +36 -0
- package/dist/src/magpie/index.d.ts +16 -0
- package/dist/src/magpie/index.js +61 -0
- package/dist/src/magpie/portfolio.d.ts +52 -0
- package/dist/src/magpie/portfolio.js +179 -0
- package/dist/src/magpie/quotes.d.ts +35 -0
- package/dist/src/magpie/quotes.js +47 -0
- package/dist/src/magpie/room.d.ts +61 -0
- package/dist/src/magpie/room.js +238 -0
- package/dist/src/maps.d.ts +159 -0
- package/dist/src/maps.js +171 -0
- package/dist/src/marmot/browser.d.ts +139 -0
- package/dist/src/marmot/browser.js +389 -0
- package/dist/src/marmot/index.d.ts +20 -0
- package/dist/src/marmot/index.js +65 -0
- package/dist/src/marmot/media.d.ts +56 -0
- package/dist/src/marmot/media.js +265 -0
- package/dist/src/marmot/research.d.ts +337 -0
- package/dist/src/marmot/research.js +1138 -0
- package/dist/src/marmot/scraper.d.ts +59 -0
- package/dist/src/marmot/scraper.js +94 -0
- package/dist/src/marmot/web.d.ts +402 -0
- package/dist/src/marmot/web.js +2586 -0
- package/dist/src/marmot/workspace.d.ts +81 -0
- package/dist/src/marmot/workspace.js +263 -0
- package/dist/src/parrot/agent.d.ts +53 -0
- package/dist/src/parrot/agent.js +93 -0
- package/dist/src/parrot/chat.d.ts +84 -0
- package/dist/src/parrot/chat.js +237 -0
- package/dist/src/parrot/format.d.ts +128 -0
- package/dist/src/parrot/format.js +639 -0
- package/dist/src/parrot/generative.d.ts +261 -0
- package/dist/src/parrot/generative.js +185 -0
- package/dist/src/parrot/index.d.ts +22 -0
- package/dist/src/parrot/index.js +67 -0
- package/dist/src/parrot/room.d.ts +225 -0
- package/dist/src/parrot/room.js +466 -0
- package/dist/src/parrot/routing.d.ts +90 -0
- package/dist/src/parrot/routing.js +78 -0
- package/dist/src/parrot/share.d.ts +126 -0
- package/dist/src/parrot/share.js +261 -0
- package/dist/src/parrot/socket.d.ts +41 -0
- package/dist/src/parrot/socket.js +478 -0
- package/dist/src/payments.d.ts +73 -0
- package/dist/src/payments.js +107 -0
- package/dist/src/private/cards.css +75 -0
- package/dist/src/rooms.d.ts +76 -0
- package/dist/src/rooms.js +168 -0
- package/dist/src/shared/schema.d.ts +24 -0
- package/dist/src/shared/schema.js +1 -0
- package/dist/src/shared/server.d.ts +65 -0
- package/dist/src/shared/server.js +302 -0
- package/dist/src/termite/environment.d.ts +110 -0
- package/dist/src/termite/environment.js +263 -0
- package/dist/src/termite/index.d.ts +14 -0
- package/dist/src/termite/index.js +59 -0
- package/dist/src/termite/os.d.ts +326 -0
- package/dist/src/termite/os.js +572 -0
- package/dist/src/termite/versioning.d.ts +94 -0
- package/dist/src/termite/versioning.js +217 -0
- package/dist/src/tickets.d.ts +43 -0
- package/dist/src/tickets.js +138 -0
- package/dist/src/travel.d.ts +85 -0
- package/dist/src/travel.js +44 -0
- package/dist/src/ui/authentification.d.ts +24 -0
- package/dist/src/ui/authentification.js +177 -0
- package/dist/src/ui/brands.css +11 -0
- package/dist/src/ui/brands.d.ts +7 -0
- package/dist/src/ui/brands.js +14 -0
- package/dist/src/ui/browser.d.ts +74 -0
- package/dist/src/ui/browser.js +470 -0
- package/dist/src/ui/chat.d.ts +183 -0
- package/dist/src/ui/chat.js +2379 -0
- package/dist/src/ui/chatPanel.d.ts +109 -0
- package/dist/src/ui/chatPanel.js +768 -0
- package/dist/src/ui/chatSequence.d.ts +36 -0
- package/dist/src/ui/chatSequence.js +42 -0
- package/dist/src/ui/crown.d.ts +19 -0
- package/dist/src/ui/crown.js +199 -0
- package/dist/src/ui/cursor.d.ts +73 -0
- package/dist/src/ui/cursor.js +175 -0
- package/dist/src/ui/device.d.ts +33 -0
- package/dist/src/ui/device.js +49 -0
- package/dist/src/ui/drive.d.ts +69 -0
- package/dist/src/ui/drive.js +1243 -0
- package/dist/src/ui/generative.d.ts +39 -0
- package/dist/src/ui/generative.js +313 -0
- package/dist/src/ui/hover.d.ts +6 -0
- package/dist/src/ui/hover.js +210 -0
- package/dist/src/ui/index.d.ts +42 -0
- package/dist/src/ui/index.js +87 -0
- package/dist/src/ui/integrations.d.ts +13 -0
- package/dist/src/ui/integrations.js +175 -0
- package/dist/src/ui/maps.d.ts +87 -0
- package/dist/src/ui/maps.js +547 -0
- package/dist/src/ui/roster.d.ts +32 -0
- package/dist/src/ui/roster.js +163 -0
- package/dist/src/ui/talk.d.ts +12 -0
- package/dist/src/ui/talk.js +560 -0
- package/dist/src/ui/tickets.d.ts +13 -0
- package/dist/src/ui/tickets.js +966 -0
- package/dist/src/ui/toast.d.ts +7 -0
- package/dist/src/ui/toast.js +258 -0
- package/dist/src/ui/tokenText.d.ts +18 -0
- package/dist/src/ui/tokenText.js +96 -0
- package/dist/src/ui/tokenized.d.ts +42 -0
- package/dist/src/ui/tokenized.js +622 -0
- package/dist/src/ui/ui.css +4763 -0
- package/dist/src/ui/view.d.ts +23 -0
- package/dist/src/ui/view.js +584 -0
- package/dist/src/view.d.ts +113 -0
- package/dist/src/view.js +386 -0
- package/dist/src/webhooks.d.ts +66 -0
- package/dist/src/webhooks.js +78 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +1 -0
- package/package.json +87 -4
- package/README.md +0 -3
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { connectRoom } from "../../parrot/room.js";
|
|
2
|
+
import { openRoom } from "../../parrot/share.js";
|
|
3
|
+
import { Marmot } from "../../marmot/index.js";
|
|
4
|
+
import { connectWebRoom, DomRecorder, DomReplayer, toHtml, collectAllAssetUrls, rewriteMediaUrls, extractAssetCodes, materializeAssetUrls, fetchMediaBytes, absoluteUrl, WEB_ASSET_SCHEME, uploadBootstrap, downloadBootstrap, uploadAsset, downloadAsset, weightedProgress, } from "../../marmot/web.js";
|
|
5
|
+
import { attachHostTools, createHostPageToolHandler, pageTools } from "./tools.js";
|
|
6
|
+
/** Public join URL for a web-room access code (chat joins via ensure, not the URL). */
|
|
7
|
+
export function shareUrl(code, origin = "https://app.faunakit.com") {
|
|
8
|
+
// Trailing slash is required: without it Vite (and many static hosts) serve
|
|
9
|
+
// the app root `index.html` instead of `extensions/web/index.html`.
|
|
10
|
+
return `${origin.replace(/\/$/, "")}/extensions/web/?access-code=${encodeURIComponent(code)}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A paired live session: one `WebRoom` (live page DOM) + one `ChatRoom`.
|
|
14
|
+
* Share links carry only the web access code; chat uses ensure + Parrot code.
|
|
15
|
+
*/
|
|
16
|
+
export class ExtensionWebSession {
|
|
17
|
+
constructor(parts) {
|
|
18
|
+
Object.defineProperty(this, "web", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true,
|
|
22
|
+
value: void 0
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(this, "chat", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: void 0
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(this, "webSelf", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: void 0
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(this, "chatSelf", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: true,
|
|
40
|
+
value: void 0
|
|
41
|
+
});
|
|
42
|
+
/** Bare web access code used in the share URL. */
|
|
43
|
+
Object.defineProperty(this, "code", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
writable: true,
|
|
47
|
+
value: void 0
|
|
48
|
+
});
|
|
49
|
+
Object.defineProperty(this, "recorder", {
|
|
50
|
+
enumerable: true,
|
|
51
|
+
configurable: true,
|
|
52
|
+
writable: true,
|
|
53
|
+
value: null
|
|
54
|
+
});
|
|
55
|
+
Object.defineProperty(this, "replayer", {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
configurable: true,
|
|
58
|
+
writable: true,
|
|
59
|
+
value: null
|
|
60
|
+
});
|
|
61
|
+
Object.defineProperty(this, "detachTools", {
|
|
62
|
+
enumerable: true,
|
|
63
|
+
configurable: true,
|
|
64
|
+
writable: true,
|
|
65
|
+
value: null
|
|
66
|
+
});
|
|
67
|
+
this.web = parts.web;
|
|
68
|
+
this.chat = parts.chat;
|
|
69
|
+
this.webSelf = parts.webSelf;
|
|
70
|
+
this.chatSelf = parts.chatSelf;
|
|
71
|
+
this.code = parts.code;
|
|
72
|
+
}
|
|
73
|
+
shareCode() {
|
|
74
|
+
return this.code;
|
|
75
|
+
}
|
|
76
|
+
/** Page tools routed through the web room (host executes). */
|
|
77
|
+
pageTools() {
|
|
78
|
+
return pageTools((name, params) => this.web.invokeTool(name, params));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Begin hosting: upload HTML + assets, bind ids, stream mutations.
|
|
82
|
+
* Prefer `startWebShare` from the extension for progress UX.
|
|
83
|
+
*/
|
|
84
|
+
async startHosting(root = document.documentElement, onProgress) {
|
|
85
|
+
// Host target is the connected room's server authorization.
|
|
86
|
+
await publishPageBootstrap(this.web, this.web.getAuthorization(), root, onProgress);
|
|
87
|
+
this.recorder = new DomRecorder(root);
|
|
88
|
+
this.recorder.bindIds();
|
|
89
|
+
this.detachTools = attachHostTools(this.web, createHostPageToolHandler(root));
|
|
90
|
+
let mutationChain = Promise.resolve();
|
|
91
|
+
this.recorder.start((mutation) => {
|
|
92
|
+
mutationChain = mutationChain.then(async () => {
|
|
93
|
+
const next = await maybeRewriteMutationAsset(this.web, this.web.getAuthorization(), mutation);
|
|
94
|
+
this.web.publishMutation(next);
|
|
95
|
+
}).catch((err) => {
|
|
96
|
+
console.error("[extensions/web] mutation publish failed:", err);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
stopHosting() {
|
|
101
|
+
this.recorder?.stop();
|
|
102
|
+
this.recorder = null;
|
|
103
|
+
this.detachTools?.();
|
|
104
|
+
this.detachTools = null;
|
|
105
|
+
}
|
|
106
|
+
async startViewing(mount, onProgress) {
|
|
107
|
+
this.replayer = new DomReplayer(mount);
|
|
108
|
+
const meta = this.web.getBootstrap();
|
|
109
|
+
if (meta) {
|
|
110
|
+
await loadAndApplyBootstrap(this.web, this.web.getAuthorization(), this.replayer, meta, onProgress);
|
|
111
|
+
}
|
|
112
|
+
else if (this.web.getSnapshot()) {
|
|
113
|
+
this.replayer.apply(this.web.getSnapshot());
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
renderSnapshot(snapshot) {
|
|
117
|
+
this.replayer?.apply(snapshot);
|
|
118
|
+
}
|
|
119
|
+
renderMutation(mutation) {
|
|
120
|
+
void this.renderMutationAsync(mutation);
|
|
121
|
+
}
|
|
122
|
+
async renderMutationAsync(mutation) {
|
|
123
|
+
if (!this.replayer)
|
|
124
|
+
return;
|
|
125
|
+
const next = await materializeMutationAssets(this.web.getAuthorization(), mutation);
|
|
126
|
+
this.replayer.mutate(next);
|
|
127
|
+
}
|
|
128
|
+
close() {
|
|
129
|
+
try {
|
|
130
|
+
this.web.publishClosed();
|
|
131
|
+
}
|
|
132
|
+
catch { /* noop */ }
|
|
133
|
+
this.stopHosting();
|
|
134
|
+
this.replayer = null;
|
|
135
|
+
try {
|
|
136
|
+
this.web.disconnect();
|
|
137
|
+
}
|
|
138
|
+
catch { /* noop */ }
|
|
139
|
+
try {
|
|
140
|
+
this.chat.disconnect();
|
|
141
|
+
}
|
|
142
|
+
catch { /* noop */ }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function wireViewerHooks(hooks, getSession, getReplayer) {
|
|
146
|
+
return {
|
|
147
|
+
...hooks,
|
|
148
|
+
onBootstrap(room, bootstrap) {
|
|
149
|
+
hooks?.onBootstrap?.(room, bootstrap);
|
|
150
|
+
const replayer = getReplayer();
|
|
151
|
+
if (replayer) {
|
|
152
|
+
void loadAndApplyBootstrap(room, room.getAuthorization(), replayer, bootstrap);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
onSnapshot(room, snapshot) {
|
|
156
|
+
hooks?.onSnapshot?.(room, snapshot);
|
|
157
|
+
getSession()?.renderSnapshot(snapshot);
|
|
158
|
+
},
|
|
159
|
+
onMutation(room, mutation) {
|
|
160
|
+
hooks?.onMutation?.(room, mutation);
|
|
161
|
+
const session = getSession();
|
|
162
|
+
if (session)
|
|
163
|
+
session.renderMutation(mutation);
|
|
164
|
+
else {
|
|
165
|
+
const replayer = getReplayer();
|
|
166
|
+
if (replayer) {
|
|
167
|
+
void materializeMutationAssets(room.getAuthorization(), mutation).then((m) => replayer.mutate(m));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
onClosed(room) {
|
|
172
|
+
const session = getSession();
|
|
173
|
+
if (session)
|
|
174
|
+
session.replayer = null;
|
|
175
|
+
hooks?.onClosed?.(room);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Open (or return) a shared Parrot chat room for this tab. Lives until the host
|
|
181
|
+
* leaves the page — panel close must not destroy it. Does not connect a socket
|
|
182
|
+
* here; call `connectRoom` from the chat UI with `code`.
|
|
183
|
+
*/
|
|
184
|
+
export async function ensureChatRoom(authorization, cached) {
|
|
185
|
+
if (cached?.code)
|
|
186
|
+
return cached;
|
|
187
|
+
const opened = await openRoom(authorization, { share: true });
|
|
188
|
+
return {
|
|
189
|
+
reference: opened.reference,
|
|
190
|
+
roomId: opened.roomId,
|
|
191
|
+
code: opened.code ?? "",
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Bind (host) or open the Parrot room paired with a web room. The host binds a
|
|
196
|
+
* room it opened (`room` is its reference). Always returns a fresh Parrot access
|
|
197
|
+
* code for `connectRoom({ code })`.
|
|
198
|
+
*/
|
|
199
|
+
export async function ensureChatFromWebAccess(authorization, scope, opts) {
|
|
200
|
+
const headers = {
|
|
201
|
+
"Content-Type": "application/json",
|
|
202
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
203
|
+
};
|
|
204
|
+
if ("accessCode" in scope)
|
|
205
|
+
headers["Access-Code"] = scope.accessCode;
|
|
206
|
+
else
|
|
207
|
+
headers["Server"] = scope.reference;
|
|
208
|
+
const response = await fetch(`${Marmot.getURL()}/extensions/web/chat/ensure`, {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers,
|
|
211
|
+
body: JSON.stringify(opts?.room ? { room: opts.room } : {}),
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok)
|
|
214
|
+
throw new Error(await response.text());
|
|
215
|
+
const json = await response.json();
|
|
216
|
+
if (!json.room || !json.code)
|
|
217
|
+
throw new Error("Invalid web chat ensure response.");
|
|
218
|
+
return { room: json.room, code: json.code };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Start the web room only (chat room must already exist). Uploads HTML + assets
|
|
222
|
+
* with progress, publishes bootstrap meta, starts mutation streaming + tools.
|
|
223
|
+
* Pairs the chat room with the web room so guests can ensure a Parrot code later.
|
|
224
|
+
*/
|
|
225
|
+
export async function startWebShare(authorization, chat, options = {}) {
|
|
226
|
+
const reference = `web:${crypto.randomUUID()}`;
|
|
227
|
+
const root = options.root ?? document.documentElement;
|
|
228
|
+
let recorder = null;
|
|
229
|
+
let detachTools = null;
|
|
230
|
+
const { room: web, self: webSelf } = await connectWebRoom(authorization, { reference }, options.hooks ?? {});
|
|
231
|
+
const webCode = await web.generateAccessCode();
|
|
232
|
+
// Durable web → chat pairing (not encoded in the share URL).
|
|
233
|
+
await ensureChatFromWebAccess(authorization, { reference }, { room: chat.reference });
|
|
234
|
+
await publishPageBootstrap(web, authorization, root, options.onProgress, { reference });
|
|
235
|
+
recorder = new DomRecorder(root);
|
|
236
|
+
recorder.bindIds();
|
|
237
|
+
detachTools = attachHostTools(web, createHostPageToolHandler(root));
|
|
238
|
+
let mutationChain = Promise.resolve();
|
|
239
|
+
recorder.start((mutation) => {
|
|
240
|
+
mutationChain = mutationChain.then(async () => {
|
|
241
|
+
const next = await maybeRewriteMutationAsset(web, authorization, mutation, { reference });
|
|
242
|
+
web.publishMutation(next);
|
|
243
|
+
}).catch((err) => {
|
|
244
|
+
console.error("[extensions/web] mutation publish failed:", err);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
web,
|
|
249
|
+
webSelf,
|
|
250
|
+
room: chat.reference,
|
|
251
|
+
webCode,
|
|
252
|
+
reference,
|
|
253
|
+
shareCode: () => webCode,
|
|
254
|
+
close() {
|
|
255
|
+
// Order matters on tab close: announce before tearing the socket down
|
|
256
|
+
// so guests (and the edge broadcast) see web-closed when possible.
|
|
257
|
+
try {
|
|
258
|
+
web.publishClosed();
|
|
259
|
+
}
|
|
260
|
+
catch { /* noop */ }
|
|
261
|
+
recorder?.stop();
|
|
262
|
+
recorder = null;
|
|
263
|
+
detachTools?.();
|
|
264
|
+
detachTools = null;
|
|
265
|
+
try {
|
|
266
|
+
web.disconnect();
|
|
267
|
+
}
|
|
268
|
+
catch { /* noop */ }
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
export async function openSession(authorization, hooks = {}) {
|
|
273
|
+
let session;
|
|
274
|
+
let replayer = null;
|
|
275
|
+
const openedChat = await openRoom(authorization, { share: true });
|
|
276
|
+
const chatCode = openedChat.code ?? "";
|
|
277
|
+
const webHooks = wireViewerHooks(hooks.web, () => session, () => replayer);
|
|
278
|
+
const reference = `web:${crypto.randomUUID()}`;
|
|
279
|
+
const { room: web, self: webSelf } = await connectWebRoom(authorization, { reference }, webHooks);
|
|
280
|
+
const webCode = await web.generateAccessCode();
|
|
281
|
+
await ensureChatFromWebAccess(authorization, { reference }, { room: openedChat.reference });
|
|
282
|
+
const { room: chat, self: chatSelf } = await connectRoom(authorization, { code: chatCode }, hooks.chat ?? {});
|
|
283
|
+
session = new ExtensionWebSession({
|
|
284
|
+
web, chat, webSelf, chatSelf,
|
|
285
|
+
code: webCode,
|
|
286
|
+
});
|
|
287
|
+
return session;
|
|
288
|
+
}
|
|
289
|
+
export async function joinRoom(authorization, webCode, hooks = {}) {
|
|
290
|
+
let session;
|
|
291
|
+
let replayer = null;
|
|
292
|
+
const { room: web, self: webSelf } = await connectWebRoom(authorization, { code: webCode }, wireViewerHooks(hooks.web, () => session, () => replayer));
|
|
293
|
+
const ensured = await ensureChatFromWebAccess(authorization, { accessCode: webCode });
|
|
294
|
+
const { room: chat, self: chatSelf } = await connectRoom(authorization, { code: ensured.code }, hooks.chat ?? {});
|
|
295
|
+
session = new ExtensionWebSession({ web, chat, webSelf, chatSelf, code: webCode });
|
|
296
|
+
const original = session.startViewing.bind(session);
|
|
297
|
+
session.startViewing = async (mount, onProgress) => {
|
|
298
|
+
await original(mount, onProgress);
|
|
299
|
+
replayer = session?.replayer ?? null;
|
|
300
|
+
};
|
|
301
|
+
return session;
|
|
302
|
+
}
|
|
303
|
+
export async function invite(authorization, email, joinUrl, scope) {
|
|
304
|
+
const headers = {
|
|
305
|
+
"Content-Type": "application/json",
|
|
306
|
+
Authorization: "email" in authorization
|
|
307
|
+
? `Token ${authorization.token}`
|
|
308
|
+
: `ApiKey ${authorization.id}`,
|
|
309
|
+
};
|
|
310
|
+
if ("accessCode" in scope)
|
|
311
|
+
headers["Access-Code"] = scope.accessCode;
|
|
312
|
+
else
|
|
313
|
+
headers["Server"] = scope.reference;
|
|
314
|
+
const response = await fetch(`${Marmot.getURL()}/extensions/web/invite`, {
|
|
315
|
+
method: "POST",
|
|
316
|
+
headers,
|
|
317
|
+
body: JSON.stringify({ email, joinUrl }),
|
|
318
|
+
});
|
|
319
|
+
if (!response.ok)
|
|
320
|
+
throw new Error(await response.text());
|
|
321
|
+
return await response.json();
|
|
322
|
+
}
|
|
323
|
+
// ── Bootstrap helpers ────────────────────────────────────────────────────────
|
|
324
|
+
async function publishPageBootstrap(web, authorization, root, onProgress, targetOverride) {
|
|
325
|
+
const target = targetOverride
|
|
326
|
+
? { reference: targetOverride.reference }
|
|
327
|
+
: webTargetFromRoom(web);
|
|
328
|
+
const prepared = toHtml(root);
|
|
329
|
+
const urls = collectAllAssetUrls(prepared.clone, prepared.cssUrls);
|
|
330
|
+
const rewrite = new Map();
|
|
331
|
+
let uploadedCount = 0;
|
|
332
|
+
let skippedCount = 0;
|
|
333
|
+
const stages = [];
|
|
334
|
+
for (const url of urls) {
|
|
335
|
+
stages.push({
|
|
336
|
+
weight: 1,
|
|
337
|
+
run: async (p) => {
|
|
338
|
+
try {
|
|
339
|
+
const abs = absoluteUrl(url);
|
|
340
|
+
const fetched = await fetchMediaBytes(abs);
|
|
341
|
+
if (!fetched) {
|
|
342
|
+
skippedCount++;
|
|
343
|
+
p(1);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const uploaded = await uploadAsset(authorization, target, fetched.data, fetched.contentType.split(";")[0]?.trim() || "application/octet-stream", { onProgress: p });
|
|
347
|
+
rewrite.set(url, uploaded.accessCode);
|
|
348
|
+
if (abs !== url)
|
|
349
|
+
rewrite.set(abs, uploaded.accessCode);
|
|
350
|
+
uploadedCount++;
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
// One bad image must not abort the whole page share.
|
|
354
|
+
skippedCount++;
|
|
355
|
+
console.warn("[extensions/web] asset upload skipped:", url, err);
|
|
356
|
+
p(1);
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
stages.push({
|
|
362
|
+
weight: Math.max(2, urls.length),
|
|
363
|
+
run: async (p) => {
|
|
364
|
+
rewriteMediaUrls(prepared.clone, rewrite);
|
|
365
|
+
const html = prepared.clone.outerHTML;
|
|
366
|
+
await uploadBootstrap(authorization, target, html, {
|
|
367
|
+
version: 1,
|
|
368
|
+
viewport: prepared.viewport,
|
|
369
|
+
onProgress: p,
|
|
370
|
+
});
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
await weightedProgress(stages, onProgress);
|
|
374
|
+
console.info(`[extensions/web] bootstrap assets: ${uploadedCount} uploaded, ${skippedCount} skipped, ${urls.length} total`);
|
|
375
|
+
const meta = {
|
|
376
|
+
version: 1,
|
|
377
|
+
viewport: prepared.viewport,
|
|
378
|
+
contentType: "text/html; charset=utf-8",
|
|
379
|
+
};
|
|
380
|
+
web.publishBootstrap(meta);
|
|
381
|
+
return meta;
|
|
382
|
+
}
|
|
383
|
+
async function loadAndApplyBootstrap(web, authorization, replayer, meta, onProgress) {
|
|
384
|
+
const target = webTargetFromRoom(web);
|
|
385
|
+
const { html } = await downloadBootstrap(authorization, target, (p) => onProgress?.(p * 0.35));
|
|
386
|
+
const codes = extractAssetCodes(html);
|
|
387
|
+
const blobs = new Map();
|
|
388
|
+
let i = 0;
|
|
389
|
+
for (const code of codes) {
|
|
390
|
+
const { data, contentType } = await downloadAsset(authorization, code, (p) => {
|
|
391
|
+
const base = 0.35 + (i / Math.max(codes.length, 1)) * 0.55;
|
|
392
|
+
const span = 0.55 / Math.max(codes.length, 1);
|
|
393
|
+
onProgress?.(base + p * span);
|
|
394
|
+
});
|
|
395
|
+
const blob = new Blob([new Uint8Array(data)], { type: contentType });
|
|
396
|
+
blobs.set(code, URL.createObjectURL(blob));
|
|
397
|
+
i++;
|
|
398
|
+
}
|
|
399
|
+
const materialized = materializeAssetUrls(html, blobs);
|
|
400
|
+
await replayer.applyHtml(materialized, meta.viewport);
|
|
401
|
+
onProgress?.(1);
|
|
402
|
+
}
|
|
403
|
+
function webTargetFromRoom(web) {
|
|
404
|
+
const packed = web.authorization;
|
|
405
|
+
if ("accessCode" in packed)
|
|
406
|
+
return { code: packed.accessCode };
|
|
407
|
+
return { reference: packed.server };
|
|
408
|
+
}
|
|
409
|
+
async function maybeRewriteMutationAsset(web, authorization, mutation, targetOverride) {
|
|
410
|
+
if (mutation.kind !== "attribute")
|
|
411
|
+
return mutation;
|
|
412
|
+
if (mutation.name !== "src" && mutation.name !== "poster" && mutation.name !== "href" && mutation.name !== "srcset") {
|
|
413
|
+
return mutation;
|
|
414
|
+
}
|
|
415
|
+
if (!mutation.value || mutation.value.startsWith(WEB_ASSET_SCHEME))
|
|
416
|
+
return mutation;
|
|
417
|
+
const target = targetOverride
|
|
418
|
+
? { reference: targetOverride.reference }
|
|
419
|
+
: webTargetFromRoom(web);
|
|
420
|
+
if (mutation.name === "srcset") {
|
|
421
|
+
const parts = mutation.value.split(",").map((part) => part.trim());
|
|
422
|
+
const out = [];
|
|
423
|
+
for (const part of parts) {
|
|
424
|
+
const bits = part.split(/\s+/);
|
|
425
|
+
const url = bits[0] ?? "";
|
|
426
|
+
const fetched = await fetchMediaBytes(absoluteUrl(url));
|
|
427
|
+
if (!fetched) {
|
|
428
|
+
out.push(part);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const uploaded = await uploadAsset(authorization, target, fetched.data, fetched.contentType);
|
|
432
|
+
out.push([`${WEB_ASSET_SCHEME}${uploaded.accessCode}`, ...bits.slice(1)].join(" "));
|
|
433
|
+
}
|
|
434
|
+
return { ...mutation, value: out.join(", ") };
|
|
435
|
+
}
|
|
436
|
+
const fetched = await fetchMediaBytes(absoluteUrl(mutation.value));
|
|
437
|
+
if (!fetched)
|
|
438
|
+
return mutation;
|
|
439
|
+
const uploaded = await uploadAsset(authorization, target, fetched.data, fetched.contentType);
|
|
440
|
+
return { ...mutation, value: `${WEB_ASSET_SCHEME}${uploaded.accessCode}` };
|
|
441
|
+
}
|
|
442
|
+
async function materializeMutationAssets(authorization, mutation) {
|
|
443
|
+
if (mutation.kind !== "attribute" || !mutation.value)
|
|
444
|
+
return mutation;
|
|
445
|
+
if (mutation.name === "srcset" && mutation.value.includes(WEB_ASSET_SCHEME)) {
|
|
446
|
+
const parts = mutation.value.split(",").map((part) => part.trim());
|
|
447
|
+
const out = [];
|
|
448
|
+
for (const part of parts) {
|
|
449
|
+
const bits = part.split(/\s+/);
|
|
450
|
+
const url = bits[0] ?? "";
|
|
451
|
+
if (!url.startsWith(WEB_ASSET_SCHEME)) {
|
|
452
|
+
out.push(part);
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
const code = url.slice(WEB_ASSET_SCHEME.length);
|
|
456
|
+
const { data, contentType } = await downloadAsset(authorization, code);
|
|
457
|
+
const blobUrl = URL.createObjectURL(new Blob([new Uint8Array(data)], { type: contentType }));
|
|
458
|
+
out.push([blobUrl, ...bits.slice(1)].join(" "));
|
|
459
|
+
}
|
|
460
|
+
return { ...mutation, value: out.join(", ") };
|
|
461
|
+
}
|
|
462
|
+
if ((mutation.name === "src" || mutation.name === "poster" || mutation.name === "href")
|
|
463
|
+
&& mutation.value.startsWith(WEB_ASSET_SCHEME)) {
|
|
464
|
+
const code = mutation.value.slice(WEB_ASSET_SCHEME.length);
|
|
465
|
+
const { data, contentType } = await downloadAsset(authorization, code);
|
|
466
|
+
const blobUrl = URL.createObjectURL(new Blob([new Uint8Array(data)], { type: contentType }));
|
|
467
|
+
return { ...mutation, value: blobUrl };
|
|
468
|
+
}
|
|
469
|
+
return mutation;
|
|
470
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Tool } from "../../parrot/generative.js";
|
|
2
|
+
import type { WebRoom } from "../../marmot/web.js";
|
|
3
|
+
export type PageToolName = "read_page_html" | "read_page_text" | "read_selection";
|
|
4
|
+
export type PageToolHandler = (name: PageToolName, params: Record<string, unknown>) => Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Host-side executors against the live document. Not ChatRoom-native — the web
|
|
7
|
+
* room (or content script) owns fulfillment.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createHostPageToolHandler(root?: Node): PageToolHandler;
|
|
10
|
+
/** Parrot `Tool[]` wired through a web-room invoke (or local host handler). */
|
|
11
|
+
export declare function pageTools(invoke: (name: PageToolName, params: Record<string, unknown>) => Promise<string>): Tool[];
|
|
12
|
+
/** Bind host tool fulfillment on a connected WebRoom. */
|
|
13
|
+
export declare function attachHostTools(room: WebRoom, handler: PageToolHandler): () => void;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { toHtml } from "../../marmot/web.js";
|
|
2
|
+
const MAX_HTML = 200000;
|
|
3
|
+
const MAX_TEXT = 80000;
|
|
4
|
+
/**
|
|
5
|
+
* Host-side executors against the live document. Not ChatRoom-native — the web
|
|
6
|
+
* room (or content script) owns fulfillment.
|
|
7
|
+
*/
|
|
8
|
+
export function createHostPageToolHandler(root = document.documentElement) {
|
|
9
|
+
return async (name, params) => {
|
|
10
|
+
switch (name) {
|
|
11
|
+
case "read_page_html": {
|
|
12
|
+
const { html } = toHtml(root);
|
|
13
|
+
const clipped = html.length > MAX_HTML ? html.slice(0, MAX_HTML) + "\n<!-- truncated -->" : html;
|
|
14
|
+
return clipped;
|
|
15
|
+
}
|
|
16
|
+
case "read_page_text": {
|
|
17
|
+
const el = root.nodeType === Node.ELEMENT_NODE
|
|
18
|
+
? root
|
|
19
|
+
: document.body;
|
|
20
|
+
const text = (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
21
|
+
return text.length > MAX_TEXT ? text.slice(0, MAX_TEXT) + "…" : text;
|
|
22
|
+
}
|
|
23
|
+
case "read_selection": {
|
|
24
|
+
const sel = typeof window !== "undefined" ? window.getSelection()?.toString() ?? "" : "";
|
|
25
|
+
const max = typeof params.max === "number" ? params.max : MAX_TEXT;
|
|
26
|
+
return sel.slice(0, max) || "(no selection)";
|
|
27
|
+
}
|
|
28
|
+
default:
|
|
29
|
+
return `Unknown page tool: ${name}`;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Parrot `Tool[]` wired through a web-room invoke (or local host handler). */
|
|
34
|
+
export function pageTools(invoke) {
|
|
35
|
+
return [
|
|
36
|
+
{
|
|
37
|
+
function: {
|
|
38
|
+
name: "read_page_html",
|
|
39
|
+
description: "Read the cleaned HTML of the currently shared page.",
|
|
40
|
+
parameters: { type: "OBJECT", properties: {} },
|
|
41
|
+
},
|
|
42
|
+
evaluate: async () => invoke("read_page_html", {}),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
function: {
|
|
46
|
+
name: "read_page_text",
|
|
47
|
+
description: "Read the visible text of the currently shared page.",
|
|
48
|
+
parameters: { type: "OBJECT", properties: {} },
|
|
49
|
+
},
|
|
50
|
+
evaluate: async () => invoke("read_page_text", {}),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
function: {
|
|
54
|
+
name: "read_selection",
|
|
55
|
+
description: "Read the host's current text selection on the shared page.",
|
|
56
|
+
parameters: {
|
|
57
|
+
type: "OBJECT",
|
|
58
|
+
properties: {
|
|
59
|
+
max: { type: "INTEGER", description: "Max characters to return." },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
evaluate: async (params) => invoke("read_selection", params ?? {}),
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
/** Bind host tool fulfillment on a connected WebRoom. */
|
|
68
|
+
export function attachHostTools(room, handler) {
|
|
69
|
+
return room.setToolHandler(async (name, params) => {
|
|
70
|
+
return handler(name, params ?? {});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ApiKey, Credentials, Profile } from "./authentification.js";
|
|
2
|
+
import { ChatSummary } from "./parrot/chat.js";
|
|
3
|
+
/** Someone you have people only chats with. Open a chat with `UI.Chat.Chat` and its id. */
|
|
4
|
+
export type Friend = {
|
|
5
|
+
id: string;
|
|
6
|
+
person: Profile;
|
|
7
|
+
/** Under Hidden: they started at no trust while you hide strangers, and you haven't replied or shown them. */
|
|
8
|
+
hidden: boolean;
|
|
9
|
+
chats: ChatSummary[];
|
|
10
|
+
created: Date;
|
|
11
|
+
};
|
|
12
|
+
export type Settings = {
|
|
13
|
+
/** Keep chats from people you have no trust in under Hidden until you reply or show them. On by default. */
|
|
14
|
+
hideStrangers: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** How much you trust someone or a group (`credentials:{id}`, `group:{id}`), in [0, 1). */
|
|
17
|
+
export type TrustEdge = {
|
|
18
|
+
target: string;
|
|
19
|
+
weight: number;
|
|
20
|
+
};
|
|
21
|
+
/** Your friends, each with your chats together and what you haven't read. An API key reads them too. */
|
|
22
|
+
export declare function list(authorization: Credentials | ApiKey): Promise<Friend[]>;
|
|
23
|
+
/** Start a chat with someone by their Fauna email. The first chat makes you friends. */
|
|
24
|
+
export declare function startChat(credentials: Credentials, email: string, name?: string): Promise<{
|
|
25
|
+
friend: Friend;
|
|
26
|
+
chat: string;
|
|
27
|
+
}>;
|
|
28
|
+
/** Another chat with a friend. Resolves to its id. */
|
|
29
|
+
export declare function createChat(credentials: Credentials, friend: string, name?: string): Promise<string>;
|
|
30
|
+
/** Delete a chat with a friend, for both of you. */
|
|
31
|
+
export declare function deleteChat(credentials: Credentials, friend: string, chat: string): Promise<void>;
|
|
32
|
+
/** Bring a hidden friend out of Hidden. */
|
|
33
|
+
export declare function show(credentials: Credentials, friend: string): Promise<Friend>;
|
|
34
|
+
/** End a friendship. Its chats and their files go with it, for both of you. */
|
|
35
|
+
export declare function end(credentials: Credentials, friend: string): Promise<void>;
|
|
36
|
+
export declare function settings(credentials: Credentials): Promise<Settings>;
|
|
37
|
+
export declare function updateSettings(credentials: Credentials, update: Partial<Settings>): Promise<Settings>;
|
|
38
|
+
/** Your own trust edges, strongest first. Nobody else can read them; your API keys can. */
|
|
39
|
+
export declare function trust(authorization: Credentials | ApiKey): Promise<TrustEdge[]>;
|