@parall/daemon 1.34.0 → 1.36.0
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/bundle/manifest.json +13 -9
- package/bundle/parall-browser-pod.js +40665 -0
- package/bundle/parall-claude-agent.js +564 -91
- package/bundle/parall-codex-agent.js +559 -90
- package/bundle/parall-daemon.js +1000 -66
- package/dist/browser-pod.d.ts +294 -0
- package/dist/browser-pod.d.ts.map +1 -0
- package/dist/browser-pod.js +765 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts +10 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
- package/dist/clip-runtime/browser-profile-manager.js +38 -26
- package/dist/clip-runtime/browser-state-store.d.ts +157 -0
- package/dist/clip-runtime/browser-state-store.d.ts.map +1 -0
- package/dist/clip-runtime/browser-state-store.js +370 -0
- package/dist/clip-runtime/browser-viewer-streamer.d.ts +222 -0
- package/dist/clip-runtime/browser-viewer-streamer.d.ts.map +1 -0
- package/dist/clip-runtime/browser-viewer-streamer.js +691 -0
- package/dist/clip-runtime/clip-provider.d.ts +56 -0
- package/dist/clip-runtime/clip-provider.d.ts.map +1 -1
- package/dist/clip-runtime/clip-provider.js +141 -17
- package/dist/clip-runtime/subprocess.d.ts +11 -0
- package/dist/clip-runtime/subprocess.d.ts.map +1 -0
- package/dist/clip-runtime/subprocess.js +33 -0
- package/dist/supervisor.d.ts +10 -2
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +61 -19
- package/package.json +11 -8
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ViewerTurnConfig } from './browser-viewer-streamer.js';
|
|
1
2
|
type BrowserProfileStatus = 'running' | 'stopped' | 'error';
|
|
2
3
|
interface BrowserProfileManagerOptions {
|
|
3
4
|
homeDir: string;
|
|
@@ -22,6 +23,7 @@ export declare class BrowserProfileManager {
|
|
|
22
23
|
private readonly ensuredAccounts;
|
|
23
24
|
private readonly ensuringAccounts;
|
|
24
25
|
private readonly reportedStatuses;
|
|
26
|
+
private readonly viewer;
|
|
25
27
|
constructor(opts: BrowserProfileManagerOptions);
|
|
26
28
|
invoke({ profileId, account, command, input }: BrowserInvokeRequest): Promise<unknown>;
|
|
27
29
|
ensureRuntime(profileId: string): Promise<void>;
|
|
@@ -29,6 +31,14 @@ export declare class BrowserProfileManager {
|
|
|
29
31
|
stopProfile(profileId: string): Promise<void>;
|
|
30
32
|
resetProfile(profileId: string): Promise<void>;
|
|
31
33
|
stop(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Viewer control command entrypoint — delegates to BrowserViewerStreamer
|
|
36
|
+
* (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
|
|
37
|
+
* subprocesses and viewer tab navigation.
|
|
38
|
+
*/
|
|
39
|
+
handleViewerCommand(profileId: string, sessionId: string, command: string, input?: Record<string, unknown>, turn?: ViewerTurnConfig): Promise<Record<string, unknown>>;
|
|
40
|
+
/** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
|
|
41
|
+
private cdpEndpoint;
|
|
32
42
|
private prepareOpenProfile;
|
|
33
43
|
private openProfileTabOnce;
|
|
34
44
|
private resetProfileOnce;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browser-profile-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-profile-manager.ts"],"names":[],"mappings":"AAMA,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAG5F,KAAK,oBAAoB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5D,UAAU,4BAA4B;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACpF,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7F;AAeD,UAAU,oBAAoB;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AA0BD,qBAAa,qBAAqB;IAYpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuC;IACxE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAI9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAElB,IAAI,EAAE,4BAA4B;IAWzD,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0CtF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU7C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;;OAIG;IACG,mBAAmB,CACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAInC,qEAAqE;YACvD,WAAW;YAWX,kBAAkB;YAYlB,kBAAkB;YAUlB,gBAAgB;YAOhB,oBAAoB;YAKpB,yBAAyB;YAezB,aAAa;YAWb,mBAAmB;IAqBjC;;;;;;;;;;;;OAYG;YACW,uBAAuB;IAiCrC,oFAAoF;YACtE,oBAAoB;YAuBpB,aAAa;YAiCb,qBAAqB;YAKrB,wBAAwB;YAQxB,kBAAkB;YAQlB,yBAAyB;IAqBvC,OAAO,CAAC,YAAY;YAON,WAAW;YAaX,YAAY;YAmBZ,kBAAkB;YAUlB,aAAa;YAUb,iBAAiB;YAsBjB,WAAW;YAmDX,IAAI;CA2BnB"}
|
|
@@ -2,9 +2,10 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
|
-
import * as net from 'node:net';
|
|
6
5
|
import * as path from 'node:path';
|
|
7
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { BrowserViewerStreamer } from './browser-viewer-streamer.js';
|
|
8
|
+
import { findFreePort, formatErrorForLog, sleep } from './subprocess.js';
|
|
8
9
|
const BB_BROWSER_DAEMON_START_TIMEOUT_MS = 15_000;
|
|
9
10
|
class BrowserCommandError extends Error {
|
|
10
11
|
method;
|
|
@@ -24,9 +25,20 @@ export class BrowserProfileManager {
|
|
|
24
25
|
ensuredAccounts = new Set();
|
|
25
26
|
ensuringAccounts = new Map();
|
|
26
27
|
reportedStatuses = new Map();
|
|
28
|
+
// Live viewer orchestration (bb-viewer streamer subprocesses + viewer nav)
|
|
29
|
+
// lives in BrowserViewerStreamer; the manager hands it the account-scoped
|
|
30
|
+
// bb-browser command path via the host interface.
|
|
31
|
+
viewer;
|
|
27
32
|
constructor(opts) {
|
|
28
33
|
this.opts = opts;
|
|
29
34
|
mkdirSync(this.opts.homeDir, { recursive: true });
|
|
35
|
+
this.viewer = new BrowserViewerStreamer({
|
|
36
|
+
log: this.opts.log,
|
|
37
|
+
sendBrowserCommand: (request) => this.sendCommand(request),
|
|
38
|
+
ensureAccount: (account) => this.ensureAccount(account),
|
|
39
|
+
withDaemonRecovery: (operation, label) => this.withDaemonRecovery(operation, label),
|
|
40
|
+
cdpEndpoint: () => this.cdpEndpoint(),
|
|
41
|
+
});
|
|
30
42
|
}
|
|
31
43
|
async invoke({ profileId, account, command, input }) {
|
|
32
44
|
if (!profileId)
|
|
@@ -105,6 +117,8 @@ export class BrowserProfileManager {
|
|
|
105
117
|
if (!profileId)
|
|
106
118
|
throw new Error('browser profile id is required');
|
|
107
119
|
this.reportedStatuses.delete(profileId); // see openProfile — lifecycle outcomes always report
|
|
120
|
+
// A stopped profile can have no live viewer — tear down its streamer.
|
|
121
|
+
this.viewer.killProfileStreamer(profileId);
|
|
108
122
|
// bb-browser-pro does not expose a single-account "dispose context" command.
|
|
109
123
|
// Server-side stopped status is the authority that gates future invokes.
|
|
110
124
|
this.reportStatus(profileId, 'stopped');
|
|
@@ -113,6 +127,8 @@ export class BrowserProfileManager {
|
|
|
113
127
|
if (!profileId)
|
|
114
128
|
throw new Error('browser profile id is required');
|
|
115
129
|
this.reportedStatuses.delete(profileId); // see openProfile — lifecycle outcomes always report
|
|
130
|
+
// Reset destroys the account context the viewer is connected to — kill it.
|
|
131
|
+
this.viewer.killProfileStreamer(profileId);
|
|
116
132
|
try {
|
|
117
133
|
await this.withDaemonRecovery(() => this.resetProfileOnce(profileId), `reset profile ${profileId}`);
|
|
118
134
|
this.reportStatus(profileId, 'stopped');
|
|
@@ -124,6 +140,8 @@ export class BrowserProfileManager {
|
|
|
124
140
|
}
|
|
125
141
|
}
|
|
126
142
|
async stop() {
|
|
143
|
+
// Tear down all per-profile bb-viewer streamers before the bb-browser daemon.
|
|
144
|
+
this.viewer.shutdown();
|
|
127
145
|
const daemon = this.daemon;
|
|
128
146
|
this.daemon = null;
|
|
129
147
|
this.starting = null;
|
|
@@ -142,6 +160,25 @@ export class BrowserProfileManager {
|
|
|
142
160
|
daemon.child.kill('SIGTERM');
|
|
143
161
|
}
|
|
144
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Viewer control command entrypoint — delegates to BrowserViewerStreamer
|
|
165
|
+
* (browser-viewer-streamer.ts), which owns the per-profile bb-viewer
|
|
166
|
+
* subprocesses and viewer tab navigation.
|
|
167
|
+
*/
|
|
168
|
+
async handleViewerCommand(profileId, sessionId, command, input, turn) {
|
|
169
|
+
return this.viewer.handleViewerCommand(profileId, sessionId, command, input, turn);
|
|
170
|
+
}
|
|
171
|
+
/** Read cdpHost/cdpPort from the bb-browser daemon `GET /status`. */
|
|
172
|
+
async cdpEndpoint() {
|
|
173
|
+
const daemon = await this.ensureDaemon();
|
|
174
|
+
const status = await this.post('/status', daemon, undefined, 5_000);
|
|
175
|
+
const host = status.cdpHost?.trim();
|
|
176
|
+
const port = status.cdpPort;
|
|
177
|
+
if (!host || typeof port !== 'number' || !Number.isFinite(port)) {
|
|
178
|
+
throw new Error('bb-browser /status did not report a CDP endpoint');
|
|
179
|
+
}
|
|
180
|
+
return { host, port };
|
|
181
|
+
}
|
|
145
182
|
async prepareOpenProfile(profileId, url) {
|
|
146
183
|
const accountCreated = await this.ensureAccount(profileId, url !== 'about:blank' ? url : undefined);
|
|
147
184
|
await this.sendCommand({ method: 'tab_list', account: profileId });
|
|
@@ -558,10 +595,6 @@ function comparableUrl(url) {
|
|
|
558
595
|
return null;
|
|
559
596
|
}
|
|
560
597
|
}
|
|
561
|
-
function formatErrorForLog(err) {
|
|
562
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
563
|
-
return message.replace(/\s+/g, ' ').slice(0, 300);
|
|
564
|
-
}
|
|
565
598
|
function resolveBbBrowserDaemonPath() {
|
|
566
599
|
// Bundle channels (CDN self-update, desktop, npm bin — all run the flat
|
|
567
600
|
// esbuild artifact with NO node_modules) ship bb-browser as a sibling flat
|
|
@@ -582,27 +615,6 @@ function resolveBbBrowserDaemonPath() {
|
|
|
582
615
|
}
|
|
583
616
|
return full;
|
|
584
617
|
}
|
|
585
|
-
async function findFreePort() {
|
|
586
|
-
return new Promise((resolve, reject) => {
|
|
587
|
-
const server = net.createServer();
|
|
588
|
-
server.unref();
|
|
589
|
-
server.on('error', reject);
|
|
590
|
-
server.listen(0, '127.0.0.1', () => {
|
|
591
|
-
const address = server.address();
|
|
592
|
-
server.close(() => {
|
|
593
|
-
if (address && typeof address === 'object') {
|
|
594
|
-
resolve(address.port);
|
|
595
|
-
}
|
|
596
|
-
else {
|
|
597
|
-
reject(new Error('failed to allocate free port'));
|
|
598
|
-
}
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
618
|
function randomToken() {
|
|
604
619
|
return randomBytes(16).toString('hex');
|
|
605
620
|
}
|
|
606
|
-
function sleep(ms) {
|
|
607
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
608
|
-
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser-state-store.ts — S3 externalization of a hosted browser profile's
|
|
3
|
+
* bb-browser state (design hosted-browser-multitenant §3.2, PR4).
|
|
4
|
+
*
|
|
5
|
+
* A hosted browser pod is ephemeral: its Chromium user-data dir (BB_BROWSER_HOME /
|
|
6
|
+
* `homeDir`, where cookies / localStorage / IndexedDB persist) lives on a pod
|
|
7
|
+
* `emptyDir` that vanishes on restart. To make the profile durable across pod
|
|
8
|
+
* restarts we snapshot that dir to S3, so a pod HYDRATES on start and CHECKPOINTS
|
|
9
|
+
* periodically + on drain. Layout (SSOT shared with the Go controller's
|
|
10
|
+
* state_store.go): `s3://{bucket}/browser-state/{org_id}/{profile_id}/state.tgz`.
|
|
11
|
+
*
|
|
12
|
+
* Correctness under pod churn (the hard part):
|
|
13
|
+
* - Integrity: each object carries a sha256 + the generation in metadata. Hydrate
|
|
14
|
+
* verifies the checksum; a mismatch is treated as "no state" (start fresh),
|
|
15
|
+
* never a crash.
|
|
16
|
+
* - Fencing (anti-clobber): a stale/fenced pod (older activation generation) must
|
|
17
|
+
* NOT overwrite a newer pod's state. checkpoint() reads the current object's
|
|
18
|
+
* generation; if it is newer than ours we abort. The write is additionally a
|
|
19
|
+
* conditional PUT (IfMatch on the read ETag / IfNoneMatch on create) so a CAS
|
|
20
|
+
* race fails closed (412 → abort).
|
|
21
|
+
*
|
|
22
|
+
* Graceful degradation: with no bucket configured (feature not provisioned) the
|
|
23
|
+
* store is DISABLED — hydrate/checkpoint are no-ops and the pod runs stateless.
|
|
24
|
+
* Read/credential failures never crash the pod; they degrade to "fresh start" on
|
|
25
|
+
* hydrate and "checkpoint failed" (logged) on write. Durability is best-effort +
|
|
26
|
+
* on-close (design §3.2 "simplified durability"): a failed checkpoint is bounded
|
|
27
|
+
* loss (the delta since the last successful checkpoint), logged at the pod — there
|
|
28
|
+
* is no controller-side final-checkpoint verify or auto-repair.
|
|
29
|
+
*
|
|
30
|
+
* This module is imported ONLY by the browser-pod entrypoint, so @aws-sdk/client-s3
|
|
31
|
+
* lands only in the parall-browser-pod bundle, never the BYOC daemon bundle.
|
|
32
|
+
*/
|
|
33
|
+
export interface BrowserStateLogger {
|
|
34
|
+
info(msg: string): void;
|
|
35
|
+
warn(msg: string): void;
|
|
36
|
+
error(msg: string): void;
|
|
37
|
+
}
|
|
38
|
+
/** buildStateKey is the SSOT key layout, mirrored by the Go controller. */
|
|
39
|
+
export declare function buildStateKey(orgId: string, profileId: string): string;
|
|
40
|
+
/** Thrown by a StateBackend.put when a conditional write precondition fails (S3
|
|
41
|
+
* 412). The store treats it as "lost the CAS race" and aborts the checkpoint. */
|
|
42
|
+
export declare class PreconditionFailedError extends Error {
|
|
43
|
+
constructor(message?: string);
|
|
44
|
+
}
|
|
45
|
+
export interface HeadResult {
|
|
46
|
+
etag: string;
|
|
47
|
+
metadata: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* StateBackend is the narrow object-store surface the store needs, so the CAS +
|
|
51
|
+
* checksum logic is unit-testable against an in-memory fake without the real SDK.
|
|
52
|
+
* S3Backend is the live implementation.
|
|
53
|
+
*/
|
|
54
|
+
export interface StateBackend {
|
|
55
|
+
/** Returns object metadata + ETag, or null when the object does not exist. */
|
|
56
|
+
head(key: string): Promise<HeadResult | null>;
|
|
57
|
+
get(key: string): Promise<Buffer>;
|
|
58
|
+
/** Conditional write: throws PreconditionFailedError on a 412. */
|
|
59
|
+
put(key: string, body: Buffer, opts: {
|
|
60
|
+
metadata: Record<string, string>;
|
|
61
|
+
ifMatch?: string;
|
|
62
|
+
ifNoneMatch?: string;
|
|
63
|
+
}): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export interface BrowserStateStoreConfig {
|
|
66
|
+
bucket: string;
|
|
67
|
+
region: string;
|
|
68
|
+
orgId: string;
|
|
69
|
+
profileId: string;
|
|
70
|
+
/** Lease activation generation — the monotonic fencing token (design §3.2). */
|
|
71
|
+
generation: number;
|
|
72
|
+
/** bb-browser user-data dir (BB_BROWSER_HOME) to snapshot/restore. */
|
|
73
|
+
homeDir: string;
|
|
74
|
+
/** Optional S3 endpoint (MinIO/dev); empty → AWS S3. */
|
|
75
|
+
endpoint?: string;
|
|
76
|
+
log: BrowserStateLogger;
|
|
77
|
+
}
|
|
78
|
+
export interface CheckpointResult {
|
|
79
|
+
ok: boolean;
|
|
80
|
+
/** skipped because the store is disabled. */
|
|
81
|
+
disabled?: boolean;
|
|
82
|
+
/** aborted: a newer generation already holds the object (we are fenced). */
|
|
83
|
+
fenced?: boolean;
|
|
84
|
+
/** aborted: conditional-write CAS race (another writer won). */
|
|
85
|
+
raced?: boolean;
|
|
86
|
+
/** skipped: a periodic checkpoint whose content is unchanged since the last upload. */
|
|
87
|
+
unchanged?: boolean;
|
|
88
|
+
error?: unknown;
|
|
89
|
+
}
|
|
90
|
+
export interface HydrateResult {
|
|
91
|
+
hydrated: boolean;
|
|
92
|
+
disabled?: boolean;
|
|
93
|
+
checksumMismatch?: boolean;
|
|
94
|
+
error?: unknown;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* BrowserStateStore orchestrates hydrate/checkpoint over a StateBackend. All
|
|
98
|
+
* checkpoints (periodic + final) are serialized through a single promise chain so
|
|
99
|
+
* a drain's final checkpoint can never race an in-flight periodic one.
|
|
100
|
+
*/
|
|
101
|
+
export declare class BrowserStateStore {
|
|
102
|
+
private readonly cfg;
|
|
103
|
+
private readonly backend;
|
|
104
|
+
private readonly key;
|
|
105
|
+
private chain;
|
|
106
|
+
private lastUploadedSha;
|
|
107
|
+
constructor(cfg: BrowserStateStoreConfig, backend?: StateBackend);
|
|
108
|
+
enabled(): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Download + verify + extract the profile's snapshot into homeDir. MUST run
|
|
111
|
+
* before bb-browser can launch (the pod calls it at startup, before the provider
|
|
112
|
+
* stream opens). Never throws: any failure (no object, checksum mismatch,
|
|
113
|
+
* network/cred error) degrades to a fresh start, since a read failure should not
|
|
114
|
+
* keep the browser from coming up.
|
|
115
|
+
*/
|
|
116
|
+
hydrate(): Promise<HydrateResult>;
|
|
117
|
+
/**
|
|
118
|
+
* Snapshot homeDir to S3. `final` marks the drain (on-close) checkpoint — the
|
|
119
|
+
* last best-effort flush before the pod exits. Serialized via the chain so
|
|
120
|
+
* periodic and final checkpoints never overlap. Never throws — returns a result
|
|
121
|
+
* the caller logs; a failure is bounded loss, not enforced (simplified durability:
|
|
122
|
+
* no controller-side verify or auto-repair).
|
|
123
|
+
*/
|
|
124
|
+
checkpoint(opts: {
|
|
125
|
+
final: boolean;
|
|
126
|
+
}): Promise<CheckpointResult>;
|
|
127
|
+
private doCheckpoint;
|
|
128
|
+
}
|
|
129
|
+
/** sha256 hex of a buffer — the snapshot integrity checksum. */
|
|
130
|
+
export declare function sha256(buf: Buffer): string;
|
|
131
|
+
/**
|
|
132
|
+
* archiveDir tars a directory (excluding lock/cache cruft) then gzips it in-memory.
|
|
133
|
+
* Uses the system `tar` (present in the browser-docker image and on dev machines —
|
|
134
|
+
* no extra npm dep) for the tar, and node:zlib for gzip. Gzip is done by zlib (not
|
|
135
|
+
* `tar -z`) deliberately: zlib's gzip header has a zeroed mtime, so identical
|
|
136
|
+
* directory content produces byte-identical archives — which lets a periodic
|
|
137
|
+
* checkpoint skip a redundant upload when nothing changed. Buffering in memory is
|
|
138
|
+
* fine for browser state (cookies/localStorage/IndexedDB are small); streaming
|
|
139
|
+
* multipart upload is a fast-follow if profiles grow large.
|
|
140
|
+
*/
|
|
141
|
+
export declare function archiveDir(dir: string): Promise<Buffer>;
|
|
142
|
+
/** extractArchive ungzips then untars a buffer into destDir (created if missing). */
|
|
143
|
+
export declare function extractArchive(buf: Buffer, destDir: string): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* A 403 from HeadObject. A profile-scoped pod deliberately lacks s3:ListBucket (the
|
|
146
|
+
* per-profile tenant-isolation boundary), and S3 answers a MISSING object with 403
|
|
147
|
+
* (not 404) when the caller can't list the bucket — it won't reveal existence. The
|
|
148
|
+
* pod still holds s3:GetObject on its own prefix, so an existing object HEADs as 200;
|
|
149
|
+
* a 403 there therefore means "no object", not a real denial. Treating it as
|
|
150
|
+
* not-found lets a brand-new profile hydrate fresh AND still checkpoint. This can
|
|
151
|
+
* never overwrite good state: an existing object never 403s (it 200s), and a
|
|
152
|
+
* genuinely bad/expired credential 403s the subsequent PutObject too, so the
|
|
153
|
+
* checkpoint fails — bounded loss, logged at the pod (simplified durability: no
|
|
154
|
+
* controller-side verify or auto-repair).
|
|
155
|
+
*/
|
|
156
|
+
export declare function isAccessDenied(err: unknown): boolean;
|
|
157
|
+
//# sourceMappingURL=browser-state-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-state-store.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/browser-state-store.ts"],"names":[],"mappings":"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"}
|