@runappsai/sdk 0.5.1 → 0.6.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/README.md +29 -0
- package/dist/activity-tracker.d.ts +8 -0
- package/dist/activity-tracker.d.ts.map +1 -1
- package/dist/activity-tracker.js +3 -0
- package/dist/activity-tracker.js.map +1 -1
- package/dist/browser-auth.d.ts +7 -0
- package/dist/browser-auth.d.ts.map +1 -1
- package/dist/browser-auth.js +105 -15
- package/dist/browser-auth.js.map +1 -1
- package/dist/client.d.ts +24 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +22 -0
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +20 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/files.d.ts +3 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +4 -1
- package/dist/files.js.map +1 -1
- package/dist/idb-sync/codec.d.ts +39 -0
- package/dist/idb-sync/codec.d.ts.map +1 -0
- package/dist/idb-sync/codec.js +364 -0
- package/dist/idb-sync/codec.js.map +1 -0
- package/dist/idb-sync/engine.d.ts +143 -0
- package/dist/idb-sync/engine.d.ts.map +1 -0
- package/dist/idb-sync/engine.js +727 -0
- package/dist/idb-sync/engine.js.map +1 -0
- package/dist/idb-sync/hook.d.ts +42 -0
- package/dist/idb-sync/hook.d.ts.map +1 -0
- package/dist/idb-sync/hook.js +426 -0
- package/dist/idb-sync/hook.js.map +1 -0
- package/dist/idb-sync/index.d.ts +6 -0
- package/dist/idb-sync/index.d.ts.map +1 -0
- package/dist/idb-sync/index.js +6 -0
- package/dist/idb-sync/index.js.map +1 -0
- package/dist/idb-sync/log.d.ts +41 -0
- package/dist/idb-sync/log.d.ts.map +1 -0
- package/dist/idb-sync/log.js +143 -0
- package/dist/idb-sync/log.js.map +1 -0
- package/dist/idb-sync/remote.d.ts +38 -0
- package/dist/idb-sync/remote.d.ts.map +1 -0
- package/dist/idb-sync/remote.js +61 -0
- package/dist/idb-sync/remote.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/sdk.umd.js +2 -2
- package/dist/sdk.umd.js.map +1 -1
- package/dist/transport.d.ts +5 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +19 -6
- package/dist/transport.js.map +1 -1
- package/package.json +2 -1
- package/src/activity-tracker.ts +5 -0
- package/src/browser-auth.ts +103 -16
- package/src/client.ts +36 -0
- package/src/events.ts +22 -0
- package/src/files.ts +7 -5
- package/src/idb-sync/codec.ts +354 -0
- package/src/idb-sync/engine.ts +818 -0
- package/src/idb-sync/hook.ts +495 -0
- package/src/idb-sync/index.ts +5 -0
- package/src/idb-sync/log.ts +170 -0
- package/src/idb-sync/remote.ts +59 -0
- package/src/index.ts +13 -0
- package/src/transport.ts +20 -8
package/dist/transport.d.ts
CHANGED
|
@@ -83,10 +83,13 @@ export declare class Transport {
|
|
|
83
83
|
* and the ETag of those bytes, when the server sends one. */
|
|
84
84
|
getRaw(path: string, init?: {
|
|
85
85
|
signal?: AbortSignal;
|
|
86
|
+
ifNoneMatch?: string;
|
|
86
87
|
}): Promise<{
|
|
87
88
|
data: Uint8Array;
|
|
88
89
|
contentType: string;
|
|
89
90
|
etag?: string;
|
|
91
|
+
notModified?: boolean;
|
|
92
|
+
serverTime?: number;
|
|
90
93
|
}>;
|
|
91
94
|
/** POST a multipart/form-data body. Used by audio.transcribe and image.edit. */
|
|
92
95
|
postMultipart<T>(path: string, form: FormData, init?: {
|
|
@@ -123,4 +126,6 @@ export declare class Transport {
|
|
|
123
126
|
private authHeaders;
|
|
124
127
|
private parseError;
|
|
125
128
|
}
|
|
129
|
+
/** The server's clock from a Date header, as ms since epoch. */
|
|
130
|
+
export declare function parseServerDate(h: string | null): number | undefined;
|
|
126
131
|
//# sourceMappingURL=transport.d.ts.map
|
package/dist/transport.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;gDAK4C;IAC5C,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,mEAAmE;IACnE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,qBAAa,SAAS;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+C;IAC9E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;IAC1E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,IAAI,EAAE,gBAAgB;IAmBlC,qDAAqD;YACvC,YAAY;IAK1B;;;;;;;OAOG;YACW,kBAAkB;IAiBhC;;;;;;;;;;;;OAYG;YACW,SAAS;IAKvB,2CAA2C;IACrC,QAAQ,CAAC,CAAC,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC9B,OAAO,CAAC,CAAC,CAAC;IAab,qCAAqC;IAC/B,OAAO,CAAC,CAAC,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC9B,OAAO,CAAC,CAAC,CAAC;IAYb;;6DAEyD;IACnD,aAAa,CAAC,CAAC,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC9B,OAAO,CAAC,CAAC,CAAC;IAYb;kEAC8D;IACxD,MAAM,CACV,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/transport.js
CHANGED
|
@@ -109,18 +109,24 @@ export class Transport {
|
|
|
109
109
|
/** GET path; return raw bytes + content-type (for video/audio downloads)
|
|
110
110
|
* and the ETag of those bytes, when the server sends one. */
|
|
111
111
|
async getRaw(path, init) {
|
|
112
|
-
const resp = await this.fetchWithAuthRetry(path, async () =>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
const resp = await this.fetchWithAuthRetry(path, async () => {
|
|
113
|
+
const headers = await this.authHeaders();
|
|
114
|
+
if (init?.ifNoneMatch)
|
|
115
|
+
headers["If-None-Match"] = `"${init.ifNoneMatch.replace(/"/g, "")}"`;
|
|
116
|
+
return { method: "GET", headers, signal: init?.signal };
|
|
117
|
+
});
|
|
118
|
+
const serverTime = parseServerDate(resp.headers.get("date"));
|
|
119
|
+
if (resp.status === 304) {
|
|
120
|
+
const etag = (resp.headers.get("etag") ?? "").replace(/"/g, "") || init?.ifNoneMatch;
|
|
121
|
+
return { data: new Uint8Array(0), contentType: "", etag, notModified: true, serverTime };
|
|
122
|
+
}
|
|
117
123
|
if (!resp.ok) {
|
|
118
124
|
throw await this.parseError(resp);
|
|
119
125
|
}
|
|
120
126
|
const buf = new Uint8Array(await resp.arrayBuffer());
|
|
121
127
|
const contentType = resp.headers.get("content-type") ?? "";
|
|
122
128
|
const etag = (resp.headers.get("etag") ?? "").replace(/"/g, "") || undefined;
|
|
123
|
-
return { data: buf, contentType, etag };
|
|
129
|
+
return { data: buf, contentType, etag, serverTime };
|
|
124
130
|
}
|
|
125
131
|
/** POST a multipart/form-data body. Used by audio.transcribe and image.edit. */
|
|
126
132
|
async postMultipart(path, form, init) {
|
|
@@ -225,4 +231,11 @@ export class Transport {
|
|
|
225
231
|
return new APIError(resp.status, type, message);
|
|
226
232
|
}
|
|
227
233
|
}
|
|
234
|
+
/** The server's clock from a Date header, as ms since epoch. */
|
|
235
|
+
export function parseServerDate(h) {
|
|
236
|
+
if (!h)
|
|
237
|
+
return undefined;
|
|
238
|
+
const t = Date.parse(h);
|
|
239
|
+
return Number.isNaN(t) ? undefined : t;
|
|
240
|
+
}
|
|
228
241
|
//# sourceMappingURL=transport.js.map
|
package/dist/transport.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runappsai/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "JavaScript / TypeScript client for the RunApps AI Gateway",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "RunApps",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^20.0.0",
|
|
53
|
+
"fake-indexeddb": "^6.2.5",
|
|
53
54
|
"tsup": "^8.5.1",
|
|
54
55
|
"typescript": "^5.5.0"
|
|
55
56
|
}
|
package/src/activity-tracker.ts
CHANGED
|
@@ -85,6 +85,8 @@ export interface ActivitySnapshot {
|
|
|
85
85
|
active: ActiveCall[];
|
|
86
86
|
recent: CompletedCall[];
|
|
87
87
|
session: SessionStats;
|
|
88
|
+
/** IndexedDB sync state, when sync is on (see `client.sync`). */
|
|
89
|
+
sync?: { state: "off" | "idle" | "syncing" | "offline" | "error"; pending: number; lastSyncedAt?: number; error?: string };
|
|
88
90
|
/** Highest-priority status colour for the LED dot:
|
|
89
91
|
* "error" → recent failure within ERROR_HIGHLIGHT_MS
|
|
90
92
|
* "active" → at least one in-flight call
|
|
@@ -116,6 +118,7 @@ export class ActivityTracker {
|
|
|
116
118
|
* ring/LED stayed idle until the user opened the popover —
|
|
117
119
|
* events landed in the tracker but nobody told the DOM. */
|
|
118
120
|
private changeListeners = new Set<() => void>();
|
|
121
|
+
private sync: ActivitySnapshot["sync"] | undefined = undefined;
|
|
119
122
|
|
|
120
123
|
/**
|
|
121
124
|
* Subscribe the tracker to a bus. Returns a single disposer that
|
|
@@ -128,6 +131,7 @@ export class ActivityTracker {
|
|
|
128
131
|
events.on("request:streamDelta", (e) => { this.onDelta(e); this.notify(); }),
|
|
129
132
|
events.on("request:end", (e) => { this.onEnd(e); this.notify(); }),
|
|
130
133
|
events.on("request:error", (e) => { this.onError(e); this.notify(); }),
|
|
134
|
+
events.on("sync:state", (e) => { this.sync = { ...e }; this.notify(); }),
|
|
131
135
|
];
|
|
132
136
|
return () => {
|
|
133
137
|
for (const o of offs) o();
|
|
@@ -165,6 +169,7 @@ export class ActivityTracker {
|
|
|
165
169
|
active: Array.from(this.active.values()),
|
|
166
170
|
recent: this.recent.slice(),
|
|
167
171
|
session: { ...this.session },
|
|
172
|
+
...(this.sync ? { sync: { ...this.sync } } : {}),
|
|
168
173
|
status:
|
|
169
174
|
now - this.lastErrorAt < ERROR_HIGHLIGHT_MS
|
|
170
175
|
? "error"
|
package/src/browser-auth.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { ActivityTracker, type ActivitySnapshot, type SessionStats } from "./activity-tracker.js";
|
|
25
25
|
import type { SDKEvents } from "./events.js";
|
|
26
|
+
import { hash32 } from "./idb-sync/codec.js";
|
|
26
27
|
|
|
27
28
|
export interface BrowserAuthOptions {
|
|
28
29
|
/**
|
|
@@ -310,6 +311,18 @@ export class BrowserAuth {
|
|
|
310
311
|
return this.tokenIsFresh();
|
|
311
312
|
}
|
|
312
313
|
|
|
314
|
+
/**
|
|
315
|
+
* A stable id for whose data is on this browser, or null when nobody
|
|
316
|
+
* is signed in. The user's id when the grant carried one; otherwise
|
|
317
|
+
* derived from the token, which is bound to one user and one app and
|
|
318
|
+
* never rotates. Used by IndexedDB sync to tell an account switch.
|
|
319
|
+
*/
|
|
320
|
+
ownerId(): string | null {
|
|
321
|
+
if (!this.tokenIsFresh()) return null;
|
|
322
|
+
if (this.userInfo?.id) return this.userInfo.id;
|
|
323
|
+
return "t" + hash32(this.token as string).toString(36);
|
|
324
|
+
}
|
|
325
|
+
|
|
313
326
|
/**
|
|
314
327
|
* Clear the cached token / user / badge and the persisted copy in
|
|
315
328
|
* localStorage. Next gateway call will trigger a redirect to the
|
|
@@ -544,7 +557,7 @@ export class BrowserAuth {
|
|
|
544
557
|
.split("&")
|
|
545
558
|
.forEach((kv) => {
|
|
546
559
|
const i = kv.indexOf("=");
|
|
547
|
-
if (i >= 0) params[decodeURIComponent(kv.slice(0, i))] = decodeURIComponent(kv.slice(i + 1));
|
|
560
|
+
if (i >= 0) params[decodeURIComponent(kv.slice(0, i))] = decodeURIComponent(kv.slice(i + 1).replace(/\+/g, " "));
|
|
548
561
|
});
|
|
549
562
|
const token = params.runapps_token || params.runjobs_token;
|
|
550
563
|
if (!token) return false;
|
|
@@ -870,7 +883,7 @@ function mountActivityBadge(opts: MountOpts): HTMLButtonElement {
|
|
|
870
883
|
const el = document.createElement("button");
|
|
871
884
|
el.id = id;
|
|
872
885
|
el.type = "button";
|
|
873
|
-
el.title = "RunApps
|
|
886
|
+
el.title = user.name || "RunApps";
|
|
874
887
|
el.setAttribute("aria-label", "RunApps activity status");
|
|
875
888
|
el.style.cssText = [
|
|
876
889
|
"position:fixed",
|
|
@@ -878,9 +891,9 @@ function mountActivityBadge(opts: MountOpts): HTMLButtonElement {
|
|
|
878
891
|
"z-index:2147483647",
|
|
879
892
|
"display:flex",
|
|
880
893
|
"align-items:center",
|
|
881
|
-
"
|
|
882
|
-
"padding:
|
|
883
|
-
"border-radius:
|
|
894
|
+
"justify-content:center",
|
|
895
|
+
"padding:5px",
|
|
896
|
+
"border-radius:50%",
|
|
884
897
|
"font:500 12px/1 -apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif",
|
|
885
898
|
"color:#fff",
|
|
886
899
|
"background:rgba(15,15,20,0.55)",
|
|
@@ -889,7 +902,6 @@ function mountActivityBadge(opts: MountOpts): HTMLButtonElement {
|
|
|
889
902
|
"border:1px solid rgba(255,255,255,0.12)",
|
|
890
903
|
"box-shadow:0 4px 16px rgba(0,0,0,0.15)",
|
|
891
904
|
"user-select:none",
|
|
892
|
-
"max-width:220px",
|
|
893
905
|
"overflow:visible", // popover spills out the top
|
|
894
906
|
"cursor:pointer",
|
|
895
907
|
"appearance:none",
|
|
@@ -982,12 +994,7 @@ function mountActivityBadge(opts: MountOpts): HTMLButtonElement {
|
|
|
982
994
|
avatarWrap.appendChild(avatar);
|
|
983
995
|
avatarWrap.appendChild(led);
|
|
984
996
|
|
|
985
|
-
const name = document.createElement("span");
|
|
986
|
-
name.textContent = user.name || "Signed in";
|
|
987
|
-
name.style.cssText = "white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none";
|
|
988
|
-
|
|
989
997
|
el.appendChild(avatarWrap);
|
|
990
|
-
el.appendChild(name);
|
|
991
998
|
|
|
992
999
|
// ─── Popover (lazy, append on first open) ──────────────────
|
|
993
1000
|
//
|
|
@@ -1035,7 +1042,7 @@ function mountActivityBadge(opts: MountOpts): HTMLButtonElement {
|
|
|
1035
1042
|
return;
|
|
1036
1043
|
}
|
|
1037
1044
|
if (!popover) {
|
|
1038
|
-
popover = createPopover(dashboardUrl, currentPopoverCss, () => {
|
|
1045
|
+
popover = createPopover(user, dashboardUrl, currentPopoverCss, () => {
|
|
1039
1046
|
// Tear down our DOM before the auth flip runs so the popover
|
|
1040
1047
|
// doesn't briefly show "signed out" state to a user who's
|
|
1041
1048
|
// about to navigate away. closePopover() also removes the
|
|
@@ -1344,10 +1351,26 @@ function updateRing(
|
|
|
1344
1351
|
|
|
1345
1352
|
function updateLED(led: HTMLSpanElement, snap: ActivitySnapshot): void {
|
|
1346
1353
|
switch (snap.status) {
|
|
1347
|
-
case "idle":
|
|
1348
|
-
|
|
1349
|
-
|
|
1354
|
+
case "idle": {
|
|
1355
|
+
// Nothing in flight: the LED shows the sync instead — blue pulse
|
|
1356
|
+
// while syncing, amber while changes wait offline or after a
|
|
1357
|
+
// failed attempt, grey when everything is on the server.
|
|
1358
|
+
const sync = snap.sync;
|
|
1359
|
+
if (sync && sync.state === "syncing") {
|
|
1360
|
+
led.style.background = "#3b82f6";
|
|
1361
|
+
led.style.boxShadow = "0 0 0 0 rgba(59,130,246,0.7)";
|
|
1362
|
+
led.style.animation = "__runapps_led_pulse__ 1.2s ease-out infinite";
|
|
1363
|
+
} else if (sync && (sync.state === "offline" || sync.state === "error")) {
|
|
1364
|
+
led.style.background = "#f59e0b";
|
|
1365
|
+
led.style.boxShadow = "0 0 0 2px rgba(245,158,11,0.3)";
|
|
1366
|
+
led.style.animation = "";
|
|
1367
|
+
} else {
|
|
1368
|
+
led.style.background = "rgba(255,255,255,0.2)";
|
|
1369
|
+
led.style.boxShadow = "0 0 0 0 transparent";
|
|
1370
|
+
led.style.animation = "";
|
|
1371
|
+
}
|
|
1350
1372
|
break;
|
|
1373
|
+
}
|
|
1351
1374
|
case "active":
|
|
1352
1375
|
led.style.background = "#3b82f6";
|
|
1353
1376
|
// CSS-driven pulse via box-shadow ring expanding outward —
|
|
@@ -1363,7 +1386,7 @@ function updateLED(led: HTMLSpanElement, snap: ActivitySnapshot): void {
|
|
|
1363
1386
|
}
|
|
1364
1387
|
}
|
|
1365
1388
|
|
|
1366
|
-
function createPopover(dashboardUrl: string, anchorCss: string, onSignOut: () => void): HTMLDivElement {
|
|
1389
|
+
function createPopover(user: BrowserUser, dashboardUrl: string, anchorCss: string, onSignOut: () => void): HTMLDivElement {
|
|
1367
1390
|
const pop = document.createElement("div");
|
|
1368
1391
|
pop.style.cssText = [
|
|
1369
1392
|
"position:absolute",
|
|
@@ -1387,6 +1410,37 @@ function createPopover(dashboardUrl: string, anchorCss: string, onSignOut: () =>
|
|
|
1387
1410
|
pop.setAttribute("data-runapps-popover", "1");
|
|
1388
1411
|
pop.addEventListener("click", (e) => e.stopPropagation());
|
|
1389
1412
|
|
|
1413
|
+
// Who is signed in. The badge itself is only the avatar circle, so
|
|
1414
|
+
// the full name lives here, above the activity sections.
|
|
1415
|
+
const header = document.createElement("div");
|
|
1416
|
+
header.style.cssText = [
|
|
1417
|
+
"display:flex;align-items:center;gap:8px",
|
|
1418
|
+
"margin-bottom:10px;padding-bottom:8px",
|
|
1419
|
+
"border-bottom:1px solid rgba(255,255,255,0.08)",
|
|
1420
|
+
].join(";");
|
|
1421
|
+
const headAvatar = document.createElement("div");
|
|
1422
|
+
headAvatar.textContent = (user.name || "?").charAt(0).toUpperCase();
|
|
1423
|
+
headAvatar.style.cssText = [
|
|
1424
|
+
"width:24px;height:24px;border-radius:50%;flex-shrink:0",
|
|
1425
|
+
"display:flex;align-items:center;justify-content:center",
|
|
1426
|
+
"background:linear-gradient(135deg,#3b82f6,#8b5cf6)",
|
|
1427
|
+
"font-size:11px;font-weight:600;color:#fff",
|
|
1428
|
+
].join(";");
|
|
1429
|
+
const headName = document.createElement("div");
|
|
1430
|
+
headName.textContent = user.name || "Signed in";
|
|
1431
|
+
headName.title = user.name || "";
|
|
1432
|
+
headName.style.cssText = "font-size:12px;font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
1433
|
+
const headSub = document.createElement("div");
|
|
1434
|
+
headSub.textContent = "Signed in with RunApps";
|
|
1435
|
+
headSub.style.cssText = "font-size:10px;color:rgba(255,255,255,0.5)";
|
|
1436
|
+
const headText = document.createElement("div");
|
|
1437
|
+
headText.style.cssText = "min-width:0";
|
|
1438
|
+
headText.appendChild(headName);
|
|
1439
|
+
headText.appendChild(headSub);
|
|
1440
|
+
header.appendChild(headAvatar);
|
|
1441
|
+
header.appendChild(headText);
|
|
1442
|
+
pop.appendChild(header);
|
|
1443
|
+
|
|
1390
1444
|
// The popover's content is rebuilt on every redraw to keep DOM
|
|
1391
1445
|
// diffing simple. ~3 sections × ~6 children = ~20 nodes, no
|
|
1392
1446
|
// measurable cost at the 6 Hz redraw rate.
|
|
@@ -1448,9 +1502,42 @@ function updatePopover(pop: HTMLDivElement, snap: ActivitySnapshot): void {
|
|
|
1448
1502
|
const now = Date.now();
|
|
1449
1503
|
inner.appendChild(renderActiveSection(snap, now));
|
|
1450
1504
|
inner.appendChild(renderRecentSection(snap, now));
|
|
1505
|
+
if (snap.sync) inner.appendChild(renderSyncSection(snap.sync, now));
|
|
1451
1506
|
inner.appendChild(renderSessionSection(snap.session, now));
|
|
1452
1507
|
}
|
|
1453
1508
|
|
|
1509
|
+
function renderSyncSection(sync: NonNullable<ActivitySnapshot["sync"]>, now: number): HTMLDivElement {
|
|
1510
|
+
const sec = document.createElement("div");
|
|
1511
|
+
sec.style.marginBottom = "10px";
|
|
1512
|
+
const hint = sync.lastSyncedAt ? `synced ${relativeTime(now - sync.lastSyncedAt)}` : "—";
|
|
1513
|
+
sec.appendChild(sectionLabel("Sync", hint));
|
|
1514
|
+
const row = document.createElement("div");
|
|
1515
|
+
row.style.cssText = "display:flex;justify-content:space-between;gap:8px;padding:3px 0;font-size:10px";
|
|
1516
|
+
const left = document.createElement("span");
|
|
1517
|
+
const dot = document.createElement("span");
|
|
1518
|
+
dot.textContent = "● ";
|
|
1519
|
+
const label: Record<string, [string, string]> = {
|
|
1520
|
+
off: ["Not signed in", "rgba(255,255,255,0.4)"],
|
|
1521
|
+
idle: ["Up to date", "#10b981"],
|
|
1522
|
+
syncing: ["Syncing…", "#3b82f6"],
|
|
1523
|
+
offline: ["Offline", "#f59e0b"],
|
|
1524
|
+
error: ["Retrying", "#f59e0b"],
|
|
1525
|
+
};
|
|
1526
|
+
const [text, color] = label[sync.state] ?? ["", "#fff"];
|
|
1527
|
+
dot.style.color = color;
|
|
1528
|
+
left.style.color = "#fff";
|
|
1529
|
+
left.appendChild(dot);
|
|
1530
|
+
left.appendChild(document.createTextNode(text));
|
|
1531
|
+
const right = document.createElement("span");
|
|
1532
|
+
right.style.cssText = "color:rgba(255,255,255,0.7);font-variant-numeric:tabular-nums";
|
|
1533
|
+
right.textContent = sync.pending > 0 ? `${sync.pending} pending` : "";
|
|
1534
|
+
right.title = sync.error ?? "";
|
|
1535
|
+
row.appendChild(left);
|
|
1536
|
+
row.appendChild(right);
|
|
1537
|
+
sec.appendChild(row);
|
|
1538
|
+
return sec;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1454
1541
|
function renderActiveSection(snap: ActivitySnapshot, now: number): HTMLDivElement {
|
|
1455
1542
|
const sec = document.createElement("div");
|
|
1456
1543
|
sec.style.marginBottom = "10px";
|
package/src/client.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { ComputerService } from "./computer.js";
|
|
|
8
8
|
import { FilesService } from "./files.js";
|
|
9
9
|
import { EmbeddingsService } from "./embeddings.js";
|
|
10
10
|
import { BrowserAuth, type BrowserUser } from "./browser-auth.js";
|
|
11
|
+
import { FilesRemote, SyncEngine, type SyncOptions, type SyncState } from "./idb-sync/index.js";
|
|
11
12
|
import { SDKEvents } from "./events.js";
|
|
12
13
|
|
|
13
14
|
// The platform, not the bare gateway: personal rk_ keys are checked and
|
|
@@ -94,6 +95,16 @@ export interface ClientOptions {
|
|
|
94
95
|
baseURL?: string;
|
|
95
96
|
/** Optional fetch override (e.g. node-fetch with custom agent). */
|
|
96
97
|
fetch?: typeof fetch;
|
|
98
|
+
/**
|
|
99
|
+
* Keep the app's IndexedDB in sync with the signed-in user's cloud
|
|
100
|
+
* copy. **On by default** in an app on RunApps (browser sign-in mode):
|
|
101
|
+
* the SDK observes every database the app opens, pushes committed
|
|
102
|
+
* writes a few seconds later, pulls other devices' changes when the
|
|
103
|
+
* page is opened, focused or comes back online, and shows the state
|
|
104
|
+
* on the badge. Pass `false` to turn it off, or options to limit it
|
|
105
|
+
* to some databases / leave stores out. See `client.sync`.
|
|
106
|
+
*/
|
|
107
|
+
syncIndexedDB?: boolean | SyncOptions;
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
/**
|
|
@@ -136,6 +147,13 @@ export class RunApps {
|
|
|
136
147
|
* subscription, etc.).
|
|
137
148
|
*/
|
|
138
149
|
readonly auth: BrowserAuth | null = null;
|
|
150
|
+
/**
|
|
151
|
+
* IndexedDB sync (see `ClientOptions.syncIndexedDB`), or null when it
|
|
152
|
+
* is off. `status()` gives the state the badge shows; `now()` pushes
|
|
153
|
+
* and pulls immediately; `client.events.on("db:change", …)` tells the
|
|
154
|
+
* app which store received remote changes so it can re-read.
|
|
155
|
+
*/
|
|
156
|
+
readonly sync: { status(): SyncState; now(): Promise<void>; pause(): void; resume(): void; dispose(): void } | null = null;
|
|
139
157
|
|
|
140
158
|
/**
|
|
141
159
|
* Runtime event bus for SDK-call telemetry. Every LLM-ish service
|
|
@@ -231,6 +249,24 @@ export class RunApps {
|
|
|
231
249
|
// legacy unscoped /v1/files/... paths.
|
|
232
250
|
this.files = new FilesService(transport, options.project);
|
|
233
251
|
this.embeddings = new EmbeddingsService(transport, this.events);
|
|
252
|
+
|
|
253
|
+
// IndexedDB sync: an app on RunApps gets it unless it says no. It
|
|
254
|
+
// stores under the signed-in user, so only browser sign-in mode has
|
|
255
|
+
// someone to sync for.
|
|
256
|
+
const wantSync = options.syncIndexedDB ?? this.auth !== null;
|
|
257
|
+
if (wantSync && typeof indexedDB !== "undefined" && this.auth) {
|
|
258
|
+
const auth = this.auth;
|
|
259
|
+
const engine = new SyncEngine(
|
|
260
|
+
{
|
|
261
|
+
remote: new FilesRemote(this.files),
|
|
262
|
+
events: this.events,
|
|
263
|
+
userId: () => auth.ownerId(),
|
|
264
|
+
onAuthChange: (cb) => auth.onTokenChange(() => cb()),
|
|
265
|
+
},
|
|
266
|
+
typeof wantSync === "object" ? wantSync : {},
|
|
267
|
+
);
|
|
268
|
+
(this as { sync: RunApps["sync"] }).sync = engine;
|
|
269
|
+
}
|
|
234
270
|
}
|
|
235
271
|
|
|
236
272
|
/** Force a redirect to the runapps.ai grant page. No-op in static
|
package/src/events.ts
CHANGED
|
@@ -93,11 +93,33 @@ export interface RequestErrorEvent {
|
|
|
93
93
|
/** Compile-time map of event name → payload shape. Drives handler
|
|
94
94
|
* inference in `on()` / `emit()` so callers can't subscribe to a
|
|
95
95
|
* typo'd name or read a non-existent field. */
|
|
96
|
+
/** Fired whenever the IndexedDB sync's state, pending count or last
|
|
97
|
+
* sync time changes. See `client.sync`. */
|
|
98
|
+
export interface SyncStateEvent {
|
|
99
|
+
state: "off" | "idle" | "syncing" | "offline" | "error";
|
|
100
|
+
pending: number;
|
|
101
|
+
lastSyncedAt?: number;
|
|
102
|
+
error?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Fired after the sync wrote remote changes into a local object store,
|
|
106
|
+
* in this tab and (through BroadcastChannel) in the app's other tabs.
|
|
107
|
+
* Re-read the store to show the new data. */
|
|
108
|
+
export interface DbChangeEvent {
|
|
109
|
+
db: string;
|
|
110
|
+
store: string;
|
|
111
|
+
keys: IDBValidKey[];
|
|
112
|
+
cleared: boolean;
|
|
113
|
+
source: "remote";
|
|
114
|
+
}
|
|
115
|
+
|
|
96
116
|
export interface SDKEventMap {
|
|
97
117
|
"request:start": RequestStartEvent;
|
|
98
118
|
"request:streamDelta": RequestStreamDeltaEvent;
|
|
99
119
|
"request:end": RequestEndEvent;
|
|
100
120
|
"request:error": RequestErrorEvent;
|
|
121
|
+
"sync:state": SyncStateEvent;
|
|
122
|
+
"db:change": DbChangeEvent;
|
|
101
123
|
}
|
|
102
124
|
|
|
103
125
|
/** Disposer returned by `on()` — call to unsubscribe. */
|
package/src/files.ts
CHANGED
|
@@ -177,12 +177,13 @@ export class FilesService {
|
|
|
177
177
|
* `put(path, body, { ifMatch: etag })` writes only if the file is
|
|
178
178
|
* still the version that was read.
|
|
179
179
|
*/
|
|
180
|
-
async read(path: string, init?: { signal?: AbortSignal }): Promise<FileRead> {
|
|
181
|
-
const { data, contentType, etag } = await this.transport.getRaw(pathToURL(this.base, path), init);
|
|
180
|
+
async read(path: string, init?: { signal?: AbortSignal; ifNoneMatch?: string }): Promise<FileRead> {
|
|
181
|
+
const { data, contentType, etag, notModified } = await this.transport.getRaw(pathToURL(this.base, path), init);
|
|
182
182
|
return {
|
|
183
183
|
blob: new Blob([new Uint8Array(data)], { type: contentType || "application/octet-stream" }),
|
|
184
184
|
contentType,
|
|
185
185
|
etag,
|
|
186
|
+
...(notModified ? { notModified: true } : {}),
|
|
186
187
|
};
|
|
187
188
|
}
|
|
188
189
|
|
|
@@ -193,9 +194,9 @@ export class FilesService {
|
|
|
193
194
|
*/
|
|
194
195
|
async readJSON<T = unknown>(
|
|
195
196
|
path: string,
|
|
196
|
-
init?: { signal?: AbortSignal },
|
|
197
|
-
): Promise<{ value: T | undefined; etag?: string }> {
|
|
198
|
-
let r: FileRead;
|
|
197
|
+
init?: { signal?: AbortSignal; ifNoneMatch?: string },
|
|
198
|
+
): Promise<{ value: T | undefined; etag?: string; notModified?: boolean }> {
|
|
199
|
+
let r: FileRead & { notModified?: boolean };
|
|
199
200
|
try {
|
|
200
201
|
r = await this.read(path, init);
|
|
201
202
|
} catch (e) {
|
|
@@ -205,6 +206,7 @@ export class FilesService {
|
|
|
205
206
|
}
|
|
206
207
|
throw e;
|
|
207
208
|
}
|
|
209
|
+
if (r.notModified) return { value: undefined, etag: r.etag, notModified: true };
|
|
208
210
|
return { value: JSON.parse(await r.blob.text()) as T, etag: r.etag };
|
|
209
211
|
}
|
|
210
212
|
|