sealkeep 0.9.0 → 0.11.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/ARCHITECTURE.md +163 -14
- package/CHANGELOG.md +223 -1
- package/CONTROL_PLANE.md +2 -2
- package/LICENSE +1 -1
- package/README.md +108 -22
- package/THIRD_PARTY.md +2 -2
- package/THREAT_MODEL.md +23 -4
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/chunk-access.d.ts +26 -4
- package/dist/packages/sealkeep-crypto/src/chunk-access.js +219 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.d.ts +11 -1
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.js +58 -21
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.d.ts +2 -2
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.d.ts +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.js +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.js +4 -4
- package/dist/packages/sealkeep-crypto/src/squeeze.d.ts +6 -0
- package/dist/packages/sealkeep-crypto/src/squeeze.js +39 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.d.ts +36 -3
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.js +196 -37
- package/dist/site/index.html +1808 -1904
- package/dist/site/llms.txt +67 -0
- package/dist/site/trust/architecture-data-flow.html +53 -0
- package/dist/site/trust/audit-roadmap.html +37 -0
- package/dist/site/trust/deployment-responsibility.html +11 -0
- package/dist/site/trust/dpa-sample.html +30 -0
- package/dist/site/trust/release-provenance.html +21 -0
- package/dist/site/trust/subprocessors.html +15 -0
- package/dist/site/trust/threat-model.html +34 -0
- package/dist/site/trust/toms.html +41 -0
- package/dist/site/trust-document.css +32 -0
- package/dist/site/trust.html +73 -0
- package/dist/site/visual/assets/index-8Nxnkc7o.js +207 -0
- package/dist/site/visual/assets/index-oxLy1bpy.css +1 -0
- package/dist/site/visual/index.html +18 -0
- package/dist/site.zip +0 -0
- package/dist/src/activity.d.ts +9 -0
- package/dist/src/activity.js +90 -1
- package/dist/src/adapters.d.ts +175 -5
- package/dist/src/adapters.js +961 -73
- package/dist/src/agent-context.d.ts +135 -0
- package/dist/src/agent-context.js +1059 -0
- package/dist/src/archive-copies.d.ts +47 -0
- package/dist/src/archive-copies.js +179 -0
- package/dist/src/audit.d.ts +1 -1
- package/dist/src/audit.js +29 -4
- package/dist/src/autopilot.d.ts +52 -7
- package/dist/src/autopilot.js +143 -25
- package/dist/src/background-bandwidth.d.ts +46 -0
- package/dist/src/background-bandwidth.js +301 -0
- package/dist/src/background-cpu.d.ts +82 -0
- package/dist/src/background-cpu.js +212 -0
- package/dist/src/background-worker-error.d.ts +12 -0
- package/dist/src/background-worker-error.js +18 -0
- package/dist/src/branding.d.ts +24 -1
- package/dist/src/branding.js +31 -1
- package/dist/src/bridge.d.ts +233 -0
- package/dist/src/bridge.js +604 -0
- package/dist/src/byte-stream.d.ts +91 -0
- package/dist/src/byte-stream.js +385 -0
- package/dist/src/chunk-store.d.ts +41 -8
- package/dist/src/chunk-store.js +161 -65
- package/dist/src/cli.js +1698 -163
- package/dist/src/cloud.d.ts +841 -31
- package/dist/src/cloud.js +3196 -277
- package/dist/src/context-background.d.ts +37 -0
- package/dist/src/context-background.js +309 -0
- package/dist/src/context-drain-child.d.ts +1 -0
- package/dist/src/context-drain-child.js +98 -0
- package/dist/src/context-reader.d.ts +118 -0
- package/dist/src/context-reader.js +447 -0
- package/dist/src/control-plane/auth.d.ts +32 -4
- package/dist/src/control-plane/auth.js +85 -24
- package/dist/src/control-plane/server.js +19 -6
- package/dist/src/control-plane.d.ts +17 -1
- package/dist/src/control-plane.js +32 -6
- package/dist/src/crypto.d.ts +1 -1
- package/dist/src/crypto.js +5 -5
- package/dist/src/daemon-lease.d.ts +70 -0
- package/dist/src/daemon-lease.js +420 -0
- package/dist/src/daemon.d.ts +94 -1
- package/dist/src/daemon.js +1082 -105
- package/dist/src/darwin-service-policy.d.ts +41 -0
- package/dist/src/darwin-service-policy.js +60 -0
- package/dist/src/dashboard-cli.js +15 -15
- package/dist/src/device-authorization.d.ts +37 -0
- package/dist/src/device-authorization.js +199 -0
- package/dist/src/device-enrollment.d.ts +91 -0
- package/dist/src/device-enrollment.js +349 -0
- package/dist/src/disk.d.ts +17 -12
- package/dist/src/disk.js +43 -17
- package/dist/src/doctor.d.ts +35 -1
- package/dist/src/doctor.js +316 -41
- package/dist/src/durable-ticket-lock.d.ts +24 -0
- package/dist/src/durable-ticket-lock.js +232 -0
- package/dist/src/enroll.d.ts +1 -1
- package/dist/src/enroll.js +13 -7
- package/dist/src/env.d.ts +10 -1
- package/dist/src/env.js +11 -3
- package/dist/src/errors.d.ts +8 -8
- package/dist/src/errors.js +6 -6
- package/dist/src/flush.d.ts +12 -0
- package/dist/src/flush.js +37 -0
- package/dist/src/heartbeat.d.ts +86 -12
- package/dist/src/heartbeat.js +415 -29
- package/dist/src/index-background-watchdog.d.ts +1 -0
- package/dist/src/index-background-watchdog.js +94 -0
- package/dist/src/index-background-work.d.ts +21 -0
- package/dist/src/index-background-work.js +25 -0
- package/dist/src/index-background.d.ts +64 -0
- package/dist/src/index-background.js +394 -0
- package/dist/src/index-build-child.d.ts +1 -0
- package/dist/src/index-build-child.js +109 -0
- package/dist/src/index-manifest.d.ts +52 -0
- package/dist/src/index-manifest.js +444 -0
- package/dist/src/index-publication-proof.d.ts +84 -0
- package/dist/src/index-publication-proof.js +380 -0
- package/dist/src/index-publication-state.d.ts +149 -0
- package/dist/src/index-publication-state.js +696 -0
- package/dist/src/index-publication-verifier.d.ts +89 -0
- package/dist/src/index-publication-verifier.js +341 -0
- package/dist/src/index-publish.d.ts +62 -0
- package/dist/src/index-publish.js +540 -0
- package/dist/src/index-scratch-cleanup.d.ts +19 -0
- package/dist/src/index-scratch-cleanup.js +166 -0
- package/dist/src/index-segment-types.d.ts +132 -0
- package/dist/src/index-segment-types.js +21 -0
- package/dist/src/index-segments.d.ts +9 -0
- package/dist/src/index-segments.js +516 -0
- package/dist/src/index-store.d.ts +123 -0
- package/dist/src/index-store.js +495 -0
- package/dist/src/index-sync.d.ts +91 -15
- package/dist/src/index-sync.js +286 -53
- package/dist/src/index-upgrade-publication.d.ts +30 -0
- package/dist/src/index-upgrade-publication.js +179 -0
- package/dist/src/integration-manager.d.ts +32 -0
- package/dist/src/integration-manager.js +394 -0
- package/dist/src/leakscan.js +1 -1
- package/dist/src/local-api.d.ts +56 -15
- package/dist/src/local-api.js +4979 -541
- package/dist/src/machine-settings.d.ts +51 -0
- package/dist/src/machine-settings.js +166 -0
- package/dist/src/managed-chunks.d.ts +5 -2
- package/dist/src/managed-chunks.js +14 -14
- package/dist/src/mcp-install.d.ts +11 -9
- package/dist/src/mcp-install.js +73 -28
- package/dist/src/mcp-workspace.d.ts +18 -0
- package/dist/src/mcp-workspace.js +50 -0
- package/dist/src/mcp.js +294 -25
- package/dist/src/migrate.js +27 -21
- package/dist/src/notify.d.ts +1 -1
- package/dist/src/notify.js +5 -5
- package/dist/src/offload.d.ts +201 -14
- package/dist/src/offload.js +1848 -140
- package/dist/src/onboarding.d.ts +8 -1
- package/dist/src/onboarding.js +4 -4
- package/dist/src/packages.d.ts +2 -2
- package/dist/src/packages.js +10 -2
- package/dist/src/passkey.d.ts +0 -1
- package/dist/src/passkey.js +2 -7
- package/dist/src/password-lock.d.ts +2 -2
- package/dist/src/password-lock.js +6 -6
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/presence.d.ts +86 -0
- package/dist/src/presence.js +240 -0
- package/dist/src/progress-deadline.d.ts +21 -0
- package/dist/src/progress-deadline.js +91 -0
- package/dist/src/project-repair.d.ts +55 -0
- package/dist/src/project-repair.js +131 -0
- package/dist/src/providers/gcs.d.ts +28 -7
- package/dist/src/providers/gcs.js +35 -24
- package/dist/src/providers/gdrive.d.ts +71 -8
- package/dist/src/providers/gdrive.js +223 -50
- package/dist/src/providers/index.d.ts +11 -3
- package/dist/src/providers/index.js +30 -10
- package/dist/src/providers/s3.d.ts +30 -8
- package/dist/src/providers/s3.js +41 -30
- package/dist/src/providers/safe-storage-fetch.d.ts +12 -0
- package/dist/src/providers/safe-storage-fetch.js +72 -0
- package/dist/src/queue.d.ts +136 -19
- package/dist/src/queue.js +862 -96
- package/dist/src/reclaim-transaction.d.ts +156 -0
- package/dist/src/reclaim-transaction.js +1027 -0
- package/dist/src/recovery-codes.d.ts +32 -0
- package/dist/src/recovery-codes.js +338 -0
- package/dist/src/recovery.js +12 -9
- package/dist/src/rehydrate.d.ts +25 -22
- package/dist/src/rehydrate.js +319 -23
- package/dist/src/restore.d.ts +57 -4
- package/dist/src/restore.js +272 -36
- package/dist/src/resume-stub.d.ts +92 -0
- package/dist/src/resume-stub.js +417 -0
- package/dist/src/retention.d.ts +98 -7
- package/dist/src/retention.js +1066 -62
- package/dist/src/rotate.js +3 -3
- package/dist/src/search.d.ts +566 -8
- package/dist/src/search.js +5897 -290
- package/dist/src/secrets.d.ts +51 -7
- package/dist/src/secrets.js +316 -24
- package/dist/src/service.d.ts +49 -11
- package/dist/src/service.js +776 -35
- package/dist/src/share.js +3 -3
- package/dist/src/shared-spaces.d.ts +98 -0
- package/dist/src/shared-spaces.js +214 -0
- package/dist/src/source-reader.d.ts +73 -0
- package/dist/src/source-reader.js +715 -0
- package/dist/src/spool.d.ts +1 -1
- package/dist/src/spool.js +1 -1
- package/dist/src/start-tui.js +2 -1
- package/dist/src/start.js +2 -2
- package/dist/src/storage-endpoint.d.ts +21 -0
- package/dist/src/storage-endpoint.js +122 -0
- package/dist/src/storage-setup.js +12 -12
- package/dist/src/storage-targets.d.ts +109 -6
- package/dist/src/storage-targets.js +975 -67
- package/dist/src/stream-to-cloud.d.ts +5 -1
- package/dist/src/stream-to-cloud.js +34 -14
- package/dist/src/sync-rules.d.ts +31 -6
- package/dist/src/sync-rules.js +153 -14
- package/dist/src/team-backfill-scheduling.d.ts +8 -0
- package/dist/src/team-backfill-scheduling.js +33 -0
- package/dist/src/team-backfill.d.ts +116 -0
- package/dist/src/team-backfill.js +1429 -0
- package/dist/src/team-index-cache.d.ts +16 -0
- package/dist/src/team-index-cache.js +152 -0
- package/dist/src/team-offboarding.d.ts +38 -0
- package/dist/src/team-offboarding.js +1043 -0
- package/dist/src/team-presence.d.ts +127 -0
- package/dist/src/team-presence.js +904 -0
- package/dist/src/team-publication-policy.d.ts +20 -0
- package/dist/src/team-publication-policy.js +140 -0
- package/dist/src/team-realtime.d.ts +68 -0
- package/dist/src/team-realtime.js +816 -0
- package/dist/src/team-source-facts-cache.d.ts +23 -0
- package/dist/src/team-source-facts-cache.js +255 -0
- package/dist/src/trash.d.ts +1 -1
- package/dist/src/trash.js +2 -2
- package/dist/src/tui.js +11 -12
- package/dist/src/types.d.ts +173 -7
- package/dist/src/types.js +20 -0
- package/dist/src/ui-server.d.ts +163 -35
- package/dist/src/ui-server.js +712 -72
- package/dist/src/ui.d.ts +1 -2
- package/dist/src/ui.js +1 -2
- package/dist/src/upload.d.ts +27 -0
- package/dist/src/upload.js +383 -43
- package/dist/src/vault.d.ts +226 -30
- package/dist/src/vault.js +1776 -192
- package/dist/src/watcher.d.ts +7 -1
- package/dist/src/watcher.js +198 -55
- package/dist/src/worker.d.ts +27 -3
- package/dist/src/worker.js +274 -55
- package/package.json +33 -12
- package/scripts/native-reboot-rehearsal.mjs +90 -0
- package/web/app.js +6032 -343
- package/web/bootstrap.js +17 -0
- package/web/index.html +255 -57
- package/web/rail.js +317 -40
- package/web/retention.html +2 -2
- package/web/rules-view.js +188 -16
- package/web/sessions-view.js +485 -62
- package/web/sessions.html +2 -2
- package/web/setup-api.js +152 -29
- package/web/setup-logic.js +68 -9
- package/web/setup.html +113 -44
- package/web/setup.js +604 -71
- package/web/style.css +513 -98
- package/dist/packages/vaultline-crypto/src/chunk-access.js +0 -93
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.js +0 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/** Marker understood by the daemon bootstrap and the standalone manager. */
|
|
2
|
+
export const INTEGRATION_MANAGER_GENERATION_ENV = "SEALKEEP_SERVICE_MANAGER_GENERATION";
|
|
3
|
+
export const INTEGRATION_MANAGER_GENERATION = "darwin-app-policy-v1";
|
|
4
|
+
/**
|
|
5
|
+
* The durable service manager's exact child command.
|
|
6
|
+
*
|
|
7
|
+
* A launchd job registered by an older Sealkeep release can retain its cached
|
|
8
|
+
* ProcessType=Background policy even after the plist is corrected. Starting
|
|
9
|
+
* the daemon through taskpolicy's application profile escapes that inherited
|
|
10
|
+
* job policy before Node evaluates the CLI; every helper the daemon later
|
|
11
|
+
* starts inherits the corrected policy too. Other platforms keep their byte-
|
|
12
|
+
* for-byte command because their native service definitions reload normally.
|
|
13
|
+
*/
|
|
14
|
+
export function integrationManagerChildInvocation(receipt, target = process.platform) {
|
|
15
|
+
return target === "darwin" && receipt.kind === "launchd"
|
|
16
|
+
? { executable: "/usr/sbin/taskpolicy", args: ["-a", receipt.executable, ...receipt.args] }
|
|
17
|
+
: { executable: receipt.executable, args: receipt.args };
|
|
18
|
+
}
|
|
19
|
+
const integrationManagerChildInvocationSource = integrationManagerChildInvocation.toString();
|
|
20
|
+
/**
|
|
21
|
+
* Source copied outside the npm package when the background service is
|
|
22
|
+
* installed. npm 7+ deliberately runs no uninstall lifecycle scripts, so a
|
|
23
|
+
* package cannot clean hooks or services from inside code npm just removed.
|
|
24
|
+
* This tiny disclosed supervisor survives long enough to notice that the
|
|
25
|
+
* package target stayed absent beyond an upgrade grace period, remove only the
|
|
26
|
+
* integration paths named in its receipt, write a tombstone, then remove
|
|
27
|
+
* itself. Vaults, encrypted archives, outboxes, and keychain items are kept.
|
|
28
|
+
*/
|
|
29
|
+
export const INTEGRATION_MANAGER_SOURCE = String.raw `#!/usr/bin/env node
|
|
30
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
31
|
+
import { existsSync, statSync } from 'node:fs';
|
|
32
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
33
|
+
import { dirname, join } from 'node:path';
|
|
34
|
+
import { randomUUID } from 'node:crypto';
|
|
35
|
+
|
|
36
|
+
const receiptPath = process.argv[2];
|
|
37
|
+
if (!receiptPath) process.exit(64);
|
|
38
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
39
|
+
const readReceipt = async () => JSON.parse(await readFile(receiptPath, 'utf8'));
|
|
40
|
+
const atomicJson = async (path, value) => {
|
|
41
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
42
|
+
const temp = path + '.' + randomUUID() + '.tmp';
|
|
43
|
+
await writeFile(temp, JSON.stringify(value, null, 2) + '\n', { mode: 0o600 });
|
|
44
|
+
await rename(temp, path);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Keep this standalone manager's launch plan generated from the same function
|
|
48
|
+
// exported to the package tests. It cannot import package code: surviving a
|
|
49
|
+
// briefly absent npm target during upgrades is its reason for existing.
|
|
50
|
+
const childInvocationFor = (${integrationManagerChildInvocationSource});
|
|
51
|
+
|
|
52
|
+
const control = (file, args, options = {}) => spawnSync(file, args, {
|
|
53
|
+
timeout: 5000,
|
|
54
|
+
killSignal: 'SIGTERM',
|
|
55
|
+
...options,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function ownedCommand(command) {
|
|
59
|
+
return typeof command === 'string' && /\bhook (enqueue|rehydrate|context|context-sync) --agent (codex|claude)\b/.test(command);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function prune(value) {
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
const kept = value.map(prune).filter((item) => item !== null);
|
|
65
|
+
return kept.length ? kept : null;
|
|
66
|
+
}
|
|
67
|
+
if (!value || typeof value !== 'object') return value;
|
|
68
|
+
if (ownedCommand(value.command)) return null;
|
|
69
|
+
const next = { ...value };
|
|
70
|
+
if (Object.prototype.hasOwnProperty.call(value, 'hooks')) {
|
|
71
|
+
const hooks = prune(value.hooks);
|
|
72
|
+
if (hooks === null) return null;
|
|
73
|
+
next.hooks = hooks;
|
|
74
|
+
}
|
|
75
|
+
return next;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function cleanHook(path, stillCurrent) {
|
|
79
|
+
try {
|
|
80
|
+
const raw = await readFile(path, 'utf8');
|
|
81
|
+
const document = JSON.parse(raw);
|
|
82
|
+
if (!document || typeof document !== 'object' || Array.isArray(document)) return false;
|
|
83
|
+
const next = { ...document };
|
|
84
|
+
const hooks = {};
|
|
85
|
+
for (const [event, entries] of Object.entries(document.hooks ?? {})) {
|
|
86
|
+
const kept = prune(entries);
|
|
87
|
+
if (kept !== null) hooks[event] = kept;
|
|
88
|
+
}
|
|
89
|
+
next.hooks = hooks;
|
|
90
|
+
const before = JSON.stringify(document);
|
|
91
|
+
if (JSON.stringify(next) === before) return false;
|
|
92
|
+
if (!await stillCurrent()) return null;
|
|
93
|
+
const backup = path + '.sealkeep-uninstall-backup-' + new Date().toISOString().replace(/[:.]/g, '-');
|
|
94
|
+
await writeFile(backup, raw, { mode: 0o600 });
|
|
95
|
+
if (!await stillCurrent()) return null;
|
|
96
|
+
await atomicJson(path, next);
|
|
97
|
+
return true;
|
|
98
|
+
} catch { return false; }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function canonical(value) {
|
|
102
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
103
|
+
if (!value || typeof value !== 'object') return value;
|
|
104
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])]));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function receiptGeneration(value) {
|
|
108
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
109
|
+
// missingSince is this manager's own monitor heartbeat, not an installation
|
|
110
|
+
// generation. Every install has a fresh installedAt and this full canonical
|
|
111
|
+
// fallback also keeps legacy receipts fail-closed across replacements.
|
|
112
|
+
const { missingSince: _missingSince, ...stable } = value;
|
|
113
|
+
return JSON.stringify(canonical(stable));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function property(output, name) {
|
|
117
|
+
const line = String(output ?? '').split(/\r?\n/).find((candidate) => candidate.startsWith(name + '='));
|
|
118
|
+
return line === undefined ? null : line.slice(name.length + 1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A service/task name is user-global while receipts are vault-local. Before a
|
|
123
|
+
* surviving old manager edits current hooks or a unit, prove that the loaded
|
|
124
|
+
* registration is this exact process, unit, manager, and receipt. A stale
|
|
125
|
+
* manager must never dismantle a newer vault's registration merely because it
|
|
126
|
+
* inherited the same product label.
|
|
127
|
+
*/
|
|
128
|
+
function ownsCurrentRegistration(receipt) {
|
|
129
|
+
if (receipt.kind === 'launchd') {
|
|
130
|
+
const domain = typeof process.getuid === 'function' ? 'gui/' + process.getuid() + '/' + receipt.label : null;
|
|
131
|
+
if (!domain) return false;
|
|
132
|
+
const loaded = control('launchctl', ['print', domain], { encoding: 'utf8' });
|
|
133
|
+
const loadedPid = Number(/^\s*pid\s*=\s*(\d+)\s*$/m.exec(loaded.stdout)?.[1]);
|
|
134
|
+
return loaded.status === 0
|
|
135
|
+
&& loadedPid === process.pid
|
|
136
|
+
&& process.argv[1] === receipt.managerPath
|
|
137
|
+
&& process.argv[2] === receipt.receiptPath
|
|
138
|
+
&& loaded.stdout.includes(receipt.managerPath)
|
|
139
|
+
&& loaded.stdout.includes(receipt.receiptPath);
|
|
140
|
+
}
|
|
141
|
+
if (receipt.kind === 'systemd') {
|
|
142
|
+
const loaded = control('systemctl', [
|
|
143
|
+
'--user', 'show', receipt.label,
|
|
144
|
+
'--property=MainPID', '--property=FragmentPath', '--property=ExecStart',
|
|
145
|
+
], { encoding: 'utf8' });
|
|
146
|
+
if (loaded.status !== 0) return false;
|
|
147
|
+
const mainPid = Number(property(loaded.stdout, 'MainPID'));
|
|
148
|
+
const fragmentPath = property(loaded.stdout, 'FragmentPath');
|
|
149
|
+
const execStart = property(loaded.stdout, 'ExecStart') ?? '';
|
|
150
|
+
// MainPID makes process.argv the kernel's exact argument vector for the
|
|
151
|
+
// current ExecStart, avoiding ambiguous substring parsing (and supporting
|
|
152
|
+
// paths systemctl renders with C escapes). FragmentPath independently
|
|
153
|
+
// proves which unit owns that process; a non-empty ExecStart proves the
|
|
154
|
+
// loaded registration exposes the property we asked systemd to verify.
|
|
155
|
+
return mainPid === process.pid
|
|
156
|
+
&& fragmentPath === receipt.unitPath
|
|
157
|
+
&& process.argv[1] === receipt.managerPath
|
|
158
|
+
&& process.argv[2] === receipt.receiptPath
|
|
159
|
+
&& execStart.length > 0;
|
|
160
|
+
}
|
|
161
|
+
// Task Scheduler is itself the durable registration. Keep its established
|
|
162
|
+
// cleanup behavior until it can expose the same process/unit identity proof.
|
|
163
|
+
return receipt.kind === 'schtasks';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function cleanup(receipt) {
|
|
167
|
+
const expectedGeneration = receiptGeneration(receipt);
|
|
168
|
+
const stillCurrent = async () => {
|
|
169
|
+
if (!expectedGeneration || targetPresent()) return false;
|
|
170
|
+
const current = await readReceipt().catch(() => null);
|
|
171
|
+
return receiptGeneration(current) === expectedGeneration && !targetPresent();
|
|
172
|
+
};
|
|
173
|
+
// Resolve ownership before touching hooks. Hook commands are intentionally
|
|
174
|
+
// recognizable across package versions, which also means a stale receipt's
|
|
175
|
+
// broad matcher would otherwise delete a newer install's current entries.
|
|
176
|
+
if (!await stillCurrent() || !ownsCurrentRegistration(receipt) || !await stillCurrent()) return;
|
|
177
|
+
|
|
178
|
+
const removedHooks = [];
|
|
179
|
+
let ownedLingerUid = null;
|
|
180
|
+
for (const path of receipt.hookFiles ?? []) {
|
|
181
|
+
const result = await cleanHook(path, stillCurrent);
|
|
182
|
+
if (result === null) return;
|
|
183
|
+
if (result) removedHooks.push(path);
|
|
184
|
+
}
|
|
185
|
+
for (const path of receipt.ownedFragments ?? []) {
|
|
186
|
+
if (!await stillCurrent()) return;
|
|
187
|
+
await rm(path, { force: true }).catch(() => undefined);
|
|
188
|
+
}
|
|
189
|
+
for (const path of receipt.ownedRuntimeFiles ?? []) {
|
|
190
|
+
if (!await stillCurrent()) return;
|
|
191
|
+
await rm(path, { force: true }).catch(() => undefined);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (receipt.kind === 'launchd') {
|
|
195
|
+
if (!await stillCurrent()) return;
|
|
196
|
+
await rm(receipt.unitPath, { force: true }).catch(() => undefined);
|
|
197
|
+
if (!await stillCurrent()) return;
|
|
198
|
+
control('launchctl', ['remove', receipt.label], { stdio: 'ignore' });
|
|
199
|
+
} else if (receipt.kind === 'systemd') {
|
|
200
|
+
if (!await stillCurrent()) return;
|
|
201
|
+
control('systemctl', ['--user', 'disable', receipt.label], { stdio: 'ignore' });
|
|
202
|
+
if (!await stillCurrent()) return;
|
|
203
|
+
await rm(receipt.unitPath, { force: true }).catch(() => undefined);
|
|
204
|
+
if (!await stillCurrent()) return;
|
|
205
|
+
control('systemctl', ['--user', 'daemon-reload'], { stdio: 'ignore' });
|
|
206
|
+
// Linger is user-wide, not unit-local. Reverse it only when the owner-only
|
|
207
|
+
// receipt says Sealkeep proved it was off and enabled it, and only for the
|
|
208
|
+
// numeric account currently executing this manager. A forged/stale receipt
|
|
209
|
+
// can therefore never make a privileged cleanup target another user.
|
|
210
|
+
const currentUid = typeof process.getuid === 'function' ? process.getuid() : null;
|
|
211
|
+
const ownedLinger = receipt.linuxLinger?.enabledBySealkeep === true
|
|
212
|
+
&& Number.isInteger(receipt.linuxLinger.uid)
|
|
213
|
+
&& receipt.linuxLinger.uid === currentUid;
|
|
214
|
+
if (ownedLinger) ownedLingerUid = currentUid;
|
|
215
|
+
} else if (receipt.kind === 'schtasks') {
|
|
216
|
+
if (!await stillCurrent()) return;
|
|
217
|
+
control('schtasks', ['/delete', '/f', '/tn', receipt.label], { stdio: 'ignore' });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const tombstonePath = join(receipt.dataDir, 'integrations', 'uninstalled.json');
|
|
221
|
+
const tombstone = {
|
|
222
|
+
version: 1,
|
|
223
|
+
cleanedAt: new Date().toISOString(),
|
|
224
|
+
reason: 'package absent after upgrade grace period',
|
|
225
|
+
removedHooks,
|
|
226
|
+
removedService: receipt.unitPath,
|
|
227
|
+
removedLinger: false,
|
|
228
|
+
lingerCleanup: ownedLingerUid === null ? 'not-owned' : 'requested',
|
|
229
|
+
preserved: ['encrypted archives', 'unsynced outbox', 'recovery material in native keychain', 'remote objects'],
|
|
230
|
+
};
|
|
231
|
+
if (!await stillCurrent()) return;
|
|
232
|
+
await atomicJson(tombstonePath, tombstone);
|
|
233
|
+
if (!await stillCurrent()) return;
|
|
234
|
+
await rm(receipt.managerPath, { force: true }).catch(() => undefined);
|
|
235
|
+
if (!await stillCurrent()) return;
|
|
236
|
+
await rm(receiptPath, { force: true }).catch(() => undefined);
|
|
237
|
+
if (ownedLingerUid !== null) {
|
|
238
|
+
// Do this strictly last. On a logged-out headless host, disabling linger
|
|
239
|
+
// may stop the user manager — including this process — immediately. By
|
|
240
|
+
// then hooks, unit, receipt, manager, and the requested-cleanup tombstone
|
|
241
|
+
// are already durable. If logind leaves us alive, refine the receipt with
|
|
242
|
+
// the command's verified exit status.
|
|
243
|
+
const removedLinger = control('loginctl', ['disable-linger', String(ownedLingerUid)], { stdio: 'ignore' }).status === 0;
|
|
244
|
+
await atomicJson(tombstonePath, {
|
|
245
|
+
...tombstone,
|
|
246
|
+
removedLinger,
|
|
247
|
+
lingerCleanup: removedLinger ? 'completed' : 'failed',
|
|
248
|
+
}).catch(() => undefined);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const receipt = await readReceipt();
|
|
253
|
+
const targetPresent = () => existsSync(receipt.packageTarget);
|
|
254
|
+
const targetIdentity = () => {
|
|
255
|
+
try {
|
|
256
|
+
const entry = statSync(receipt.packageTarget);
|
|
257
|
+
return [entry.dev, entry.ino, entry.size, entry.mtimeMs].join(':');
|
|
258
|
+
} catch { return null; }
|
|
259
|
+
};
|
|
260
|
+
if (!targetPresent()) {
|
|
261
|
+
await delay(Math.max(0, receipt.cleanupGraceMs ?? 300000));
|
|
262
|
+
if (!targetPresent()) await cleanup(receipt);
|
|
263
|
+
process.exit(0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const childInvocation = childInvocationFor(receipt, process.platform);
|
|
267
|
+
const child = spawn(childInvocation.executable, childInvocation.args, {
|
|
268
|
+
env: {
|
|
269
|
+
...process.env,
|
|
270
|
+
// Exact paths, not a generic boolean: service reconciliation may only
|
|
271
|
+
// defer a self-restart when it is running under this precise manager and
|
|
272
|
+
// ownership receipt.
|
|
273
|
+
SEALKEEP_SERVICE_MANAGER_PATH: receipt.managerPath,
|
|
274
|
+
SEALKEEP_SERVICE_RECEIPT_PATH: receipt.receiptPath,
|
|
275
|
+
${JSON.stringify(INTEGRATION_MANAGER_GENERATION_ENV)}: ${JSON.stringify(INTEGRATION_MANAGER_GENERATION)},
|
|
276
|
+
},
|
|
277
|
+
stdio: 'inherit'
|
|
278
|
+
});
|
|
279
|
+
// Attach before the first await. A daemon that exits immediately (for example
|
|
280
|
+
// because an older generation still owns the vault) can otherwise disappear
|
|
281
|
+
// while this manager is publishing its startup heartbeat. EventEmitter does
|
|
282
|
+
// not replay the exit event, leaving a childless supervisor alive forever.
|
|
283
|
+
const childExit = new Promise((resolve) => child.once('exit', (code, signal) => resolve({ code, signal })));
|
|
284
|
+
let launchedTargetIdentity = targetIdentity();
|
|
285
|
+
let restartingForUpgrade = false;
|
|
286
|
+
let removingForUninstall = false;
|
|
287
|
+
let forceStopTimer = null;
|
|
288
|
+
const scheduleForcedStop = () => {
|
|
289
|
+
if (forceStopTimer) return;
|
|
290
|
+
// A daemon normally drains its current bounded unit before exiting. A bad
|
|
291
|
+
// provider or inherited old build must not strand an upgrade forever: after
|
|
292
|
+
// the grace period the supervisor ends only its own child, then the native
|
|
293
|
+
// service manager launches the new package. Tests may shorten the internal
|
|
294
|
+
// receipt value; installed receipts use the ten-second default.
|
|
295
|
+
const graceMs = Math.max(50, Math.min(10000, receipt.forceStopMs ?? 10000));
|
|
296
|
+
forceStopTimer = setTimeout(() => child.kill('SIGKILL'), graceMs);
|
|
297
|
+
forceStopTimer.unref?.();
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// A freshly logged-in machine can spend a while loading the package and
|
|
301
|
+
// discovering a large agent-history tree before the daemon writes its first
|
|
302
|
+
// detailed heartbeat. The service manager already knows the child exists, so
|
|
303
|
+
// publish that narrow fact immediately. If the child exits, the normal
|
|
304
|
+
// liveness check sees that this pid is gone; once the daemon is ready it
|
|
305
|
+
// replaces this record with its real roots and progress.
|
|
306
|
+
const heartbeatPath = join(receipt.dataDir, 'daemon-state.json');
|
|
307
|
+
const previousHeartbeat = await readFile(heartbeatPath, 'utf8')
|
|
308
|
+
.then((raw) => JSON.parse(raw))
|
|
309
|
+
.catch(() => null);
|
|
310
|
+
const startedAt = new Date().toISOString();
|
|
311
|
+
await atomicJson(heartbeatPath, {
|
|
312
|
+
version: 1,
|
|
313
|
+
pid: child.pid ?? process.pid,
|
|
314
|
+
startedAt,
|
|
315
|
+
lastTickAt: startedAt,
|
|
316
|
+
intervalMs: 30000,
|
|
317
|
+
totals: previousHeartbeat?.totals ?? { archived: 0, indexed: 0, uploaded: 0, reclaimed: 0, failed: 0 },
|
|
318
|
+
last: { archived: 0, indexed: 0, uploaded: 0, reclaimed: 0, failed: 0, notes: ['starting automatic memory preservation'] },
|
|
319
|
+
paused: false,
|
|
320
|
+
watching: []
|
|
321
|
+
}).catch(() => undefined);
|
|
322
|
+
|
|
323
|
+
let missingSince = null;
|
|
324
|
+
const intervalMs = Math.max(250, Math.min(30000, receipt.monitorIntervalMs ?? 30000));
|
|
325
|
+
const monitor = setInterval(async () => {
|
|
326
|
+
if (targetPresent()) {
|
|
327
|
+
missingSince = null;
|
|
328
|
+
const currentIdentity = targetIdentity();
|
|
329
|
+
if (launchedTargetIdentity && currentIdentity && currentIdentity !== launchedTargetIdentity) {
|
|
330
|
+
// npm replaces the CLI while an existing Node process keeps the old
|
|
331
|
+
// JavaScript in memory. End this supervisor with a non-zero upgrade code
|
|
332
|
+
// so launchd/systemd starts it again and the next child loads the newly
|
|
333
|
+
// installed package. No person, login, or machine restart is required.
|
|
334
|
+
restartingForUpgrade = true;
|
|
335
|
+
clearInterval(monitor);
|
|
336
|
+
if (!child.kill('SIGTERM')) process.exit(75);
|
|
337
|
+
else scheduleForcedStop();
|
|
338
|
+
} else if (!launchedTargetIdentity && currentIdentity) {
|
|
339
|
+
launchedTargetIdentity = currentIdentity;
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (missingSince === null) {
|
|
344
|
+
missingSince = Date.now();
|
|
345
|
+
await atomicJson(receiptPath, { ...receipt, missingSince: new Date(missingSince).toISOString() }).catch(() => undefined);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (Date.now() - missingSince < (receipt.cleanupGraceMs ?? 300000)) return;
|
|
349
|
+
removingForUninstall = true;
|
|
350
|
+
clearInterval(monitor);
|
|
351
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
352
|
+
child.kill('SIGTERM');
|
|
353
|
+
scheduleForcedStop();
|
|
354
|
+
}
|
|
355
|
+
// Ownership files are the evidence needed to stop this exact child. Never
|
|
356
|
+
// erase them while a SIGTERM-ignoring historical daemon can remain alive;
|
|
357
|
+
// the bounded timer escalates only this manager's child to SIGKILL.
|
|
358
|
+
await childExit;
|
|
359
|
+
if (forceStopTimer) clearTimeout(forceStopTimer);
|
|
360
|
+
// A slow upgrade can outlast the absence grace while the old child is still
|
|
361
|
+
// quiescing. Recheck at the destructive boundary: if the package returned,
|
|
362
|
+
// preserve its manager, receipt, hooks and native unit, then ask the service
|
|
363
|
+
// manager to launch the restored generation just like an in-place upgrade.
|
|
364
|
+
if (targetPresent()) process.exit(75);
|
|
365
|
+
await cleanup(receipt);
|
|
366
|
+
process.exit(0);
|
|
367
|
+
}, intervalMs);
|
|
368
|
+
|
|
369
|
+
// launchd/systemd stop the supervisor, not the daemon child directly. Without
|
|
370
|
+
// forwarding the signal the manager exits while Node leaves its child running
|
|
371
|
+
// as an orphan; the service manager then starts a second daemon beside it.
|
|
372
|
+
// Give the child enough time to close its queue/index writers cleanly, but do
|
|
373
|
+
// not let a stuck historical task make uninstall or upgrade hang forever.
|
|
374
|
+
let stoppingSignal = null;
|
|
375
|
+
for (const signal of ['SIGINT', 'SIGTERM']) process.on(signal, () => {
|
|
376
|
+
if (stoppingSignal) return;
|
|
377
|
+
stoppingSignal = signal;
|
|
378
|
+
clearInterval(monitor);
|
|
379
|
+
if (!child.kill(signal)) process.exit(0);
|
|
380
|
+
scheduleForcedStop();
|
|
381
|
+
});
|
|
382
|
+
void childExit.then(async ({ code }) => {
|
|
383
|
+
clearInterval(monitor);
|
|
384
|
+
if (forceStopTimer) clearTimeout(forceStopTimer);
|
|
385
|
+
if (removingForUninstall) return;
|
|
386
|
+
if (stoppingSignal) process.exit(0);
|
|
387
|
+
if (restartingForUpgrade) process.exit(75);
|
|
388
|
+
if (!targetPresent()) {
|
|
389
|
+
await delay(Math.max(0, receipt.cleanupGraceMs ?? 300000));
|
|
390
|
+
if (!targetPresent()) await cleanup(receipt);
|
|
391
|
+
}
|
|
392
|
+
process.exit(code ?? 1);
|
|
393
|
+
});
|
|
394
|
+
`;
|
package/dist/src/leakscan.js
CHANGED
|
@@ -120,7 +120,7 @@ const BENIGN_VALUE_SHAPES = [
|
|
|
120
120
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, // UUIDs: session ids, request ids, vault ids
|
|
121
121
|
/^[0-9a-f]{32,}$/i, // hex digests (sha256 and friends): integrity data, not credentials
|
|
122
122
|
/^AAAA/, // base64 of leading zero bytes: well-known placeholders and ssh key headers
|
|
123
|
-
/^(?:vlt|vault)_/i //
|
|
123
|
+
/^(?:vlt|vault)_/i // Sealkeep's own refs (vlt_…, vault_ref ids), which are public by design
|
|
124
124
|
];
|
|
125
125
|
function scanLine(line, lineNumber) {
|
|
126
126
|
const findings = [];
|
package/dist/src/local-api.d.ts
CHANGED
|
@@ -2,8 +2,12 @@ import { type Server } from "node:http";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { type AgentId } from "./adapters.js";
|
|
4
4
|
import { ArchiveQueue } from "./queue.js";
|
|
5
|
-
import {
|
|
5
|
+
import { drainQueue } from "./worker.js";
|
|
6
|
+
import { localApiTokenPath, localUiProofKeyPath } from "./paths.js";
|
|
7
|
+
type GdriveConnector = typeof cloud.connectGdriveViaCloud;
|
|
6
8
|
import { type ServiceOptions } from "./service.js";
|
|
9
|
+
import * as cloud from "./cloud.js";
|
|
10
|
+
import type { McpExec } from "./mcp-install.js";
|
|
7
11
|
declare const setupStorageSchema: z.ZodObject<{
|
|
8
12
|
provider: z.ZodEnum<{
|
|
9
13
|
s3: "s3";
|
|
@@ -36,13 +40,15 @@ declare const setupStorageSchema: z.ZodObject<{
|
|
|
36
40
|
* the B2 key is a native one rather than S3-compatible. All of that is knowable
|
|
37
41
|
* in a second while the person is still looking at the form.
|
|
38
42
|
*
|
|
39
|
-
* It writes a small object, reads it back,
|
|
40
|
-
*
|
|
41
|
-
*
|
|
43
|
+
* It writes a small object, reads it back, compares the bytes, deletes it, and
|
|
44
|
+
* confirms it is gone. Anything less proves less: a successful PUT says
|
|
45
|
+
* nothing about whether GET is permitted, while a target that cannot delete
|
|
46
|
+
* cannot support move/cleanup workflows safely.
|
|
42
47
|
*
|
|
43
|
-
*
|
|
44
|
-
* round trip, and dropped — the caller has not committed
|
|
45
|
-
* and a test that saved a broken configuration would be
|
|
48
|
+
* No probe object or credential is retained. The credential is used to build a
|
|
49
|
+
* client, held for the round trip, and dropped — the caller has not committed
|
|
50
|
+
* to this target yet, and a test that saved a broken configuration would be
|
|
51
|
+
* worse than no test.
|
|
46
52
|
*/
|
|
47
53
|
export declare function testStorageTarget(input: z.infer<typeof setupStorageSchema>): Promise<{
|
|
48
54
|
ok: boolean;
|
|
@@ -56,16 +62,27 @@ export type SessionRow = {
|
|
|
56
62
|
modifiedAt: string;
|
|
57
63
|
/** True when an archive already covers this file at exactly this length. */
|
|
58
64
|
archived: boolean;
|
|
59
|
-
/** True when
|
|
65
|
+
/** True when one exact archive and every delta-chain link it needs have verified copies off this machine. */
|
|
60
66
|
stored: boolean;
|
|
61
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Exact V2 archive head whose complete chain is present on this machine and
|
|
69
|
+
* can therefore enter normal storage routing. Null unless the live session
|
|
70
|
+
* is archived-but-not-stored; the dashboard must never infer this from
|
|
71
|
+
* `archived` alone.
|
|
72
|
+
*/
|
|
73
|
+
sendableArchiveId: string | null;
|
|
74
|
+
/** True while a queued or in-flight job exists for this file. It may be waiting; it must not be selected again. */
|
|
62
75
|
queued: boolean;
|
|
63
|
-
/** Why a queued job is waiting instead of running
|
|
76
|
+
/** Why a queued job is waiting instead of running. Null when it is genuinely being worked. A held row saying "sealing…" would be a lie the queue knows better than. */
|
|
64
77
|
held: string | null;
|
|
78
|
+
/** What is holding a ready job. Policy pauses do not need a three-second browser retry loop; disk/retry holds still do. */
|
|
79
|
+
heldKind: "policy" | "space" | "retry" | null;
|
|
65
80
|
/** Whole percentages of the source read so far while sealing, else null. The number a person actually watches on a 1.6 GB row. */
|
|
66
81
|
sealingPct: number | null;
|
|
67
82
|
/** The project this session ran in, when its metadata says — the Sessions page filters and labels on it. Null when nothing says. */
|
|
68
83
|
project: string | null;
|
|
84
|
+
/** Stable identity used for team mapping; the display label above is never an access key. */
|
|
85
|
+
projectKey: string | null;
|
|
69
86
|
/** The working directory behind `project`, best-effort decoded; a tooltip, never a filter. */
|
|
70
87
|
projectPath: string | null;
|
|
71
88
|
};
|
|
@@ -83,13 +100,15 @@ export type SessionRow = {
|
|
|
83
100
|
* done" is as useful as "this one is not", and hiding them would make the list
|
|
84
101
|
* shrink as you work and lose your place.
|
|
85
102
|
*/
|
|
86
|
-
export declare function listSessions(dataDir: string, queue?: ArchiveQueue): Promise<{
|
|
103
|
+
export declare function listSessions(dataDir: string, queue?: ArchiveQueue, home?: string): Promise<{
|
|
87
104
|
sessions: SessionRow[];
|
|
88
105
|
totalBytes: number;
|
|
89
106
|
unarchivedBytes: number;
|
|
90
107
|
}>;
|
|
91
|
-
export declare function ensureDraining(dataDir: string, queue: ArchiveQueue
|
|
92
|
-
|
|
108
|
+
export declare function ensureDraining(dataDir: string, queue: ArchiveQueue, operations?: {
|
|
109
|
+
drainQueue?: typeof drainQueue;
|
|
110
|
+
}): Promise<void>;
|
|
111
|
+
export declare function archiveSelected(dataDir: string, paths: string[], queue: ArchiveQueue, home?: string): Promise<{
|
|
93
112
|
requested: number;
|
|
94
113
|
queued: number;
|
|
95
114
|
bytes: number;
|
|
@@ -101,23 +120,34 @@ export declare function archiveSelected(dataDir: string, paths: string[], queue:
|
|
|
101
120
|
error?: string;
|
|
102
121
|
}[];
|
|
103
122
|
}>;
|
|
104
|
-
export { localApiTokenPath };
|
|
123
|
+
export { localApiTokenPath, localUiProofKeyPath };
|
|
105
124
|
/** Mints the loopback token once and keeps it owner-readable. It is never logged or returned by an endpoint. */
|
|
106
125
|
export declare function localApiToken(dataDir: string): Promise<string>;
|
|
126
|
+
/** Keyed listener identity proof; it grants no access to any /v1 data. */
|
|
127
|
+
export declare function localUiProofKey(dataDir: string): Promise<string>;
|
|
128
|
+
export declare const LOCAL_UI_PROOF_VERSION = 1;
|
|
129
|
+
export declare const localUiProof: (secret: string, nonce: string, port: number, instanceId: string) => string;
|
|
130
|
+
/** Owner-authenticated request for a short-lived browser bootstrap ticket. */
|
|
131
|
+
export declare const localUiBootstrapProof: (secret: string, nonce: string, port: number, instanceId: string) => string;
|
|
107
132
|
/** One row of GET /v1/cloud/copies: a copy held off this machine, wherever it is held. */
|
|
108
133
|
export type StoredCopy = {
|
|
109
134
|
ref: string;
|
|
135
|
+
archiveId?: string;
|
|
136
|
+
copyId?: string;
|
|
137
|
+
provider?: string;
|
|
138
|
+
targetId?: string | null;
|
|
110
139
|
bytes: number;
|
|
111
140
|
addedAt: string | null;
|
|
112
141
|
state: string;
|
|
113
142
|
/** True when this machine no longer holds the archive's bytes, so this stored copy is the archive. */
|
|
114
143
|
lastCopy: boolean;
|
|
115
144
|
};
|
|
116
|
-
/** Test
|
|
145
|
+
/** Test seams: setup acts on the real machine, so tests need a home and command runners of their own. */
|
|
117
146
|
export type SetupEnvironment = {
|
|
118
147
|
home?: string;
|
|
119
148
|
platform?: NodeJS.Platform;
|
|
120
149
|
exec?: ServiceOptions["exec"];
|
|
150
|
+
mcpExec?: McpExec;
|
|
121
151
|
};
|
|
122
152
|
/**
|
|
123
153
|
* The loopback API shared by the CLI, dashboard, and MCP surfaces. It is the only
|
|
@@ -129,4 +159,15 @@ export type SetupEnvironment = {
|
|
|
129
159
|
export declare function createLocalApiServer(dataDir: string, token: string, options?: {
|
|
130
160
|
queue?: ArchiveQueue;
|
|
131
161
|
setup?: SetupEnvironment;
|
|
162
|
+
gdriveConnector?: GdriveConnector;
|
|
163
|
+
listenerInstanceId?: string;
|
|
164
|
+
/** True hands post-unlock draining to the supervised daemon that is waiting to start. */
|
|
165
|
+
onRecoveryPhraseAvailable?: () => boolean;
|
|
166
|
+
/** Deterministic transfer seam; production dynamically loads uploadArchive. */
|
|
167
|
+
uploadArchive?: typeof import("./upload.js")["uploadArchive"];
|
|
168
|
+
/** Maximum silence between real transfer byte/checkpoint progress. */
|
|
169
|
+
uploadIdleMs?: number;
|
|
170
|
+
/** Short seams keep browser handoff expiry tests fast; production uses the defaults. */
|
|
171
|
+
bootstrapRequestTtlMs?: number;
|
|
172
|
+
bootstrapTicketTtlMs?: number;
|
|
132
173
|
}): Server;
|