@hraness/sponge 0.2.5 → 0.3.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/README.md +23 -10
- package/bin/sponge.js +8 -7
- package/dist/contracts/index.js +24 -6
- package/dist/contracts/validate.js +106 -29
- package/dist/migrations/008.d.ts +3 -0
- package/dist/migrations/008.js +5 -0
- package/dist/migrations/009.d.ts +3 -0
- package/dist/migrations/009.js +8 -0
- package/dist/migrations/010.d.ts +3 -0
- package/dist/migrations/010.js +11 -0
- package/dist/migrations/index.d.ts +15 -0
- package/dist/migrations/index.js +39 -0
- package/dist/src/acquisition/capture.js +87 -28
- package/dist/src/acquisition/cleanup.d.ts +0 -4
- package/dist/src/acquisition/cleanup.js +3 -5
- package/dist/src/acquisition/errors.js +17 -2
- package/dist/src/acquisition/files.d.ts +0 -1
- package/dist/src/acquisition/files.js +14 -11
- package/dist/src/acquisition/index.d.ts +2 -3
- package/dist/src/acquisition/index.js +1 -2
- package/dist/src/acquisition/lock.js +1 -1
- package/dist/src/acquisition/normalize.js +57 -18
- package/dist/src/acquisition/pins.js +22 -4
- package/dist/src/acquisition/process.js +67 -29
- package/dist/src/acquisition/runtime.d.ts +5 -7
- package/dist/src/acquisition/runtime.js +129 -68
- package/dist/src/acquisition/verify-helper.js +10 -3
- package/dist/src/annotations/quote.js +40 -12
- package/dist/src/annotations/store.d.ts +0 -1
- package/dist/src/annotations/store.js +66 -14
- package/dist/src/app/article.d.ts +2 -1
- package/dist/src/app/article.js +66 -70
- package/dist/src/app/bundle-files.d.ts +0 -4
- package/dist/src/app/bundle-files.js +11 -14
- package/dist/src/app/markdown.js +18 -6
- package/dist/src/app/source.d.ts +0 -4
- package/dist/src/app/source.js +18 -14
- package/dist/src/cli/arguments.d.ts +2 -8
- package/dist/src/cli/arguments.js +67 -80
- package/dist/src/cli/commands.d.ts +42 -0
- package/dist/src/cli/commands.js +795 -0
- package/dist/src/cli/config.js +12 -9
- package/dist/src/cli/digest.d.ts +1 -1
- package/dist/src/cli/digest.js +39 -17
- package/dist/src/cli/import.js +25 -4
- package/dist/src/cli/indexing.d.ts +2 -0
- package/dist/src/cli/indexing.js +26 -9
- package/dist/src/cli/main.js +47 -386
- package/dist/src/cli/portability.d.ts +0 -2
- package/dist/src/cli/portability.js +13 -8
- package/dist/src/cli/queue.js +12 -8
- package/dist/src/cli/runtimes.d.ts +1 -3
- package/dist/src/cli/runtimes.js +13 -6
- package/dist/src/cli/terminal.js +1 -1
- package/dist/src/cli/worker.d.ts +7 -0
- package/dist/src/cli/worker.js +54 -15
- package/dist/src/derivations/kinds.d.ts +34 -0
- package/dist/src/derivations/kinds.js +14 -0
- package/dist/src/derivations/types.d.ts +48 -0
- package/dist/src/derivations/types.js +1 -0
- package/dist/src/digest/derivation.d.ts +25 -0
- package/dist/src/digest/derivation.js +124 -0
- package/dist/src/digest/editorial.d.ts +10 -46
- package/dist/src/digest/editorial.js +73 -59
- package/dist/src/digest/endpoint.js +63 -27
- package/dist/src/digest/pipeline.d.ts +53 -32
- package/dist/src/digest/pipeline.js +357 -125
- package/dist/src/digest/schema.d.ts +44 -17
- package/dist/src/digest/schema.js +132 -44
- package/dist/src/errors.d.ts +46 -0
- package/dist/src/errors.js +117 -0
- package/dist/src/identity/query-policy.d.ts +0 -5
- package/dist/src/identity/query-policy.js +61 -9
- package/dist/src/identity/uri.d.ts +6 -5
- package/dist/src/identity/uri.js +37 -18
- package/dist/src/identity/visibility.d.ts +11 -1
- package/dist/src/identity/visibility.js +81 -17
- package/dist/src/importers/apply.js +104 -36
- package/dist/src/importers/custody.js +27 -9
- package/dist/src/importers/inspect.js +125 -38
- package/dist/src/importers/jungle-digest.d.ts +31 -24
- package/dist/src/importers/jungle-digest.js +217 -115
- package/dist/src/importers/jungle-store.d.ts +20 -11
- package/dist/src/importers/jungle-store.js +136 -41
- package/dist/src/importers/jungle.d.ts +32 -16
- package/dist/src/importers/jungle.js +145 -32
- package/dist/src/importers/manifest.js +57 -21
- package/dist/src/importers/verify-helper.js +18 -4
- package/dist/src/indexing/image.js +29 -10
- package/dist/src/indexing/index.d.ts +37 -6
- package/dist/src/indexing/index.js +360 -124
- package/dist/src/indexing/jobs.js +156 -47
- package/dist/src/indexing/prune-run.d.ts +2 -0
- package/dist/src/indexing/prune-run.js +69 -26
- package/dist/src/indexing/prune.d.ts +12 -3
- package/dist/src/indexing/prune.js +85 -35
- package/dist/src/indexing/runner.d.ts +2 -1
- package/dist/src/indexing/runner.js +83 -40
- package/dist/src/indexing/upgrade.d.ts +9 -0
- package/dist/src/indexing/upgrade.js +79 -0
- package/dist/src/inference/asr/bridge.js +6 -2
- package/dist/src/inference/asr/protocol.js +109 -19
- package/dist/src/inference/asr/runtime.js +58 -21
- package/dist/src/inference/asr/windows.js +9 -5
- package/dist/src/inference/embedding/index.d.ts +2 -6
- package/dist/src/inference/embedding/index.js +89 -29
- package/dist/src/ingest/attempt.d.ts +29 -0
- package/dist/src/ingest/attempt.js +55 -0
- package/dist/src/media/capture-helper.js +7 -2
- package/dist/src/media/capture.d.ts +2 -1
- package/dist/src/media/capture.js +124 -43
- package/dist/src/media/handler.d.ts +4 -2
- package/dist/src/media/handler.js +88 -70
- package/dist/src/media/transcribe.js +63 -21
- package/dist/src/metadata/article.js +49 -17
- package/dist/src/models/artifacts.d.ts +10 -8
- package/dist/src/models/artifacts.js +237 -106
- package/dist/src/models/catalog.d.ts +2 -5
- package/dist/src/models/catalog.js +94 -25
- package/dist/src/models/download.js +55 -15
- package/dist/src/models/runtime.d.ts +0 -2
- package/dist/src/models/runtime.js +69 -33
- package/dist/src/pdf/capture-helper.js +3 -1
- package/dist/src/pdf/capture.d.ts +2 -1
- package/dist/src/pdf/capture.js +95 -27
- package/dist/src/pdf/errors.js +12 -1
- package/dist/src/pdf/handler.d.ts +2 -1
- package/dist/src/pdf/handler.js +70 -71
- package/dist/src/pdf/index.d.ts +1 -3
- package/dist/src/pdf/index.js +0 -2
- package/dist/src/pdf/input.js +12 -7
- package/dist/src/pdf/normalize.js +80 -21
- package/dist/src/pdf/source-identity.js +3 -2
- package/dist/src/pdf/source.js +12 -8
- package/dist/src/pdf/verify-helper.js +15 -3
- package/dist/src/portability/backup.d.ts +2 -9
- package/dist/src/portability/backup.js +108 -22
- package/dist/src/portability/database.js +51 -17
- package/dist/src/portability/export.js +35 -9
- package/dist/src/portability/files.d.ts +1 -4
- package/dist/src/portability/files.js +127 -67
- package/dist/src/portability/gc.js +65 -22
- package/dist/src/publish/component.js +1 -1
- package/dist/src/publish/feed.d.ts +2 -2
- package/dist/src/publish/feed.js +75 -29
- package/dist/src/publish/identity.d.ts +5 -1
- package/dist/src/publish/identity.js +31 -11
- package/dist/src/publish/index.d.ts +2 -3
- package/dist/src/publish/index.js +34 -21
- package/dist/src/publish/projection.d.ts +2 -9
- package/dist/src/publish/projection.js +21 -14
- package/dist/src/search/extension.js +3 -1
- package/dist/src/search/hybrid.d.ts +3 -8
- package/dist/src/search/hybrid.js +45 -11
- package/dist/src/search/lexical.js +18 -4
- package/dist/src/search/query.js +56 -25
- package/dist/src/search/vector.js +37 -10
- package/dist/src/service/launch-agent.d.ts +5 -3
- package/dist/src/service/launch-agent.js +272 -83
- package/dist/src/service/lifetime.js +47 -14
- package/dist/src/service/loop.d.ts +3 -3
- package/dist/src/service/loop.js +47 -19
- package/dist/src/store/annotations.d.ts +32 -0
- package/dist/src/store/annotations.js +44 -0
- package/dist/src/store/backups.d.ts +18 -0
- package/dist/src/store/backups.js +110 -0
- package/dist/src/store/blobs.d.ts +24 -0
- package/dist/src/store/blobs.js +139 -0
- package/dist/src/store/changes.d.ts +5 -12
- package/dist/src/store/changes.js +108 -56
- package/dist/src/store/context.d.ts +15 -0
- package/dist/src/store/context.js +19 -0
- package/dist/src/store/derivations.d.ts +42 -0
- package/dist/src/store/derivations.js +104 -0
- package/dist/src/store/files.d.ts +0 -2
- package/dist/src/store/files.js +19 -24
- package/dist/src/store/ids.d.ts +30 -0
- package/dist/src/store/ids.js +34 -0
- package/dist/src/store/imports.d.ts +26 -0
- package/dist/src/store/imports.js +57 -0
- package/dist/src/store/index.d.ts +3 -186
- package/dist/src/store/index.js +2 -809
- package/dist/src/store/lease.d.ts +20 -0
- package/dist/src/store/lease.js +62 -0
- package/dist/src/store/library.d.ts +75 -0
- package/dist/src/store/library.js +174 -0
- package/dist/src/store/open.d.ts +7 -0
- package/dist/src/store/open.js +183 -0
- package/dist/src/store/queue.d.ts +65 -0
- package/dist/src/store/queue.js +327 -0
- package/dist/src/store/revisions.d.ts +12 -0
- package/dist/src/store/revisions.js +189 -0
- package/dist/src/store/search-index.d.ts +63 -0
- package/dist/src/store/search-index.js +190 -0
- package/dist/src/store/sync-ready.d.ts +6 -0
- package/dist/src/store/sync-ready.js +121 -0
- package/dist/src/store/types.d.ts +1 -5
- package/dist/src/store/types.js +1 -8
- package/dist/src/util/canonical.d.ts +2 -0
- package/dist/src/util/canonical.js +17 -0
- package/dist/src/util/exact.d.ts +2 -0
- package/dist/src/util/exact.js +9 -0
- package/dist/src/util/fsync.d.ts +4 -0
- package/dist/src/util/fsync.js +22 -0
- package/dist/src/util/hash.d.ts +4 -0
- package/dist/src/util/hash.js +8 -0
- package/dist/src/worker/basic.d.ts +0 -1
- package/dist/src/worker/basic.js +23 -7
- package/dist/src/worker/custody.d.ts +4 -1
- package/dist/src/worker/custody.js +111 -38
- package/dist/src/worker/engine.d.ts +2 -1
- package/dist/src/worker/engine.js +42 -20
- package/dist/src/worker/identity.js +13 -5
- package/dist/src/worker/journal.d.ts +1 -1
- package/dist/src/worker/journal.js +54 -24
- package/dist/src/worker/lock.d.ts +18 -0
- package/dist/src/worker/lock.js +196 -0
- package/dist/src/worker/runtime-gc.d.ts +4 -3
- package/dist/src/worker/runtime-gc.js +68 -41
- package/dist/src/worker/supervisor-child.js +19 -6
- package/dist/src/worker/supervisor.js +41 -8
- package/native/runtime-manifest.json +4 -4
- package/package.json +10 -2
- package/dist/src/digest/store.d.ts +0 -57
- package/dist/src/digest/store.js +0 -77
- package/dist/src/store/foundation.d.ts +0 -7
- package/dist/src/store/foundation.js +0 -7
package/dist/src/cli/main.js
CHANGED
|
@@ -4,412 +4,73 @@ import { lstat } from 'node:fs/promises';
|
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
5
|
import { terminalSafe } from './terminal.js';
|
|
6
6
|
import { parseArguments } from './arguments.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { openLibrary, StoreError } from '../store/index.js';
|
|
11
|
-
import { defaultCapturePins, inspectCaptureRuntime, provisionCapture, captureSetupPlan } from '../acquisition/index.js';
|
|
12
|
-
import { queueAction } from './queue.js';
|
|
13
|
-
import { portabilityAction, restoreAction } from './portability.js';
|
|
14
|
-
import { workerAction, serveWorker, hasDueAcquisition } from './worker.js';
|
|
15
|
-
import { nativeSetupPlan, provisionNative, inspectNative } from '../models/runtime.js';
|
|
16
|
-
import { nativeDownloadAuthGuidance } from '../models/download.js';
|
|
17
|
-
import { drainIndexQueue } from '../indexing/runner.js';
|
|
18
|
-
import { indexAction } from './indexing.js';
|
|
19
|
-
import { pruneText } from '../indexing/prune-run.js';
|
|
20
|
-
import { importAction, jungleAction, jungleText } from './import.js';
|
|
21
|
-
import { digestText, reviewFromSpec } from './digest.js';
|
|
22
|
-
import { connectEndpoint } from '../digest/endpoint.js';
|
|
23
|
-
import { createDigest } from '../digest/pipeline.js';
|
|
24
|
-
import { createQueryEmbedder, createTextEmbedder } from '../search/query.js';
|
|
25
|
-
import { runtimeGcAction, runtimeGcText, setupRuntimeGc } from './runtimes.js';
|
|
26
|
-
import { drainLibrary } from '../worker/basic.js';
|
|
27
|
-
import { minAge, publishSite } from '../publish/index.js';
|
|
7
|
+
import { command, usageOf } from './commands.js';
|
|
8
|
+
import { openLibrary } from '../store/index.js';
|
|
9
|
+
import { defaultCapturePins } from '../acquisition/index.js';
|
|
28
10
|
import { failure } from '../acquisition/errors.js';
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
delete ITEM Hide an item and remove it from search
|
|
45
|
-
index create [--profile default] Queue a new embedding generation
|
|
46
|
-
index status GENERATION Inspect generation coverage
|
|
47
|
-
index run Drain queued embedding jobs
|
|
48
|
-
index rollback GENERATION Reactivate a compatible complete generation
|
|
49
|
-
index prune [--keep 1] [--dry-run] [--vacuum] Delete vectors of retired generations
|
|
50
|
-
doctor Check library and runtime states
|
|
51
|
-
export ITEM --output DIR Export Markdown and local assets
|
|
52
|
-
backup --output FILE Save a verified offline backup
|
|
53
|
-
restore FILE --into NEW_DIR Restore into a new private library
|
|
54
|
-
gc --dry-run Write a report of unreferenced blobs
|
|
55
|
-
gc --apply REPORT Recheck and remove reported blobs
|
|
56
|
-
gc --runtimes [--dry-run] Remove superseded runtimes, models and worker journals
|
|
57
|
-
worker install [--language en] Enable the optional background worker
|
|
58
|
-
worker status Inspect the background worker
|
|
59
|
-
worker uninstall Stop it and preserve the library
|
|
60
|
-
import MANIFEST [--apply] Preview or import existing Wordcell captures
|
|
61
|
-
import jungle DIR [--apply] Preview or import Jungle Reading notes, their captures and published state
|
|
62
|
-
visibility ITEM public|private|default Override or restore the default
|
|
63
|
-
highlight ITEM TEXT [--note BODY] [--occurrence N] Anchor a verbatim quote
|
|
64
|
-
comment ITEM BODY [--on ANNOTATION] Comment on an item or reply
|
|
65
|
-
publish --dry-run [--min-age-days N] Show the public reading-list entries
|
|
66
|
-
publish --output DIR [--title T] [--base-url https://...] [--min-age-days N]
|
|
67
|
-
Write a signed static feed site with <sponge-list>
|
|
68
|
-
digest ITEM --endpoint http://127.0.0.1:PORT --model NAME [--check-support]
|
|
69
|
-
Draft a digest with a local model server you started
|
|
70
|
-
digest ITEM Show the current digest and its review state
|
|
71
|
-
review DIGEST --accept SPEC --reject SPEC Review fields: all, gist, idea:N, quote:N.M
|
|
72
|
-
|
|
73
|
-
Global flags: --library /absolute/path --json
|
|
74
|
-
Items are public by default only when captured from a public source.
|
|
75
|
-
Use -- before an option-like search query. Capture setup is explicit.
|
|
76
|
-
Digest endpoints must be literal loopback HTTP (127.0.0.1 or [::1]); Sponge never starts a model server.
|
|
77
|
-
Media: --language en enables local English ASR; --force-local bypasses captions.
|
|
78
|
-
These flags apply to --wait, run --drain, or worker serve.
|
|
79
|
-
worker install --language en saves the English ASR choice for background work.
|
|
80
|
-
For trusted local sources, use --allow-private-network with --wait or run.
|
|
81
|
-
`;
|
|
82
|
-
const codes = new Set(['grant-permission', 'repair-auth', 'unsupported', 'unavailable', 'rate-limited', 'partial', 'cleanup-required', 'cancelled', 'invalid-input', 'unclassified', 'integrity', 'limit-exceeded', 'runtime-missing', 'schema-newer', 'owner-busy']);
|
|
83
|
-
function errorCode(error, fallback) { const code = error?.code; return code && codes.has(code) ? code : fallback; }
|
|
84
|
-
function exitFor(code) { return code === 'cancelled' ? 130 : code === 'invalid-input' ? 2 : code === 'partial' ? 3 : ['grant-permission', 'repair-auth', 'cleanup-required', 'runtime-missing', 'owner-busy'].includes(code) ? 4 : 1; }
|
|
11
|
+
import { SpongeError, codeOf, publicCode, exitFor, hints, debugChain } from '../errors.js';
|
|
12
|
+
/** Failures print one categorical JSON line: the public code, its specific `reason` when narrower, and a fixed hint.
|
|
13
|
+
* Causes, details, paths and upstream messages appear only with --debug or SPONGE_DEBUG=1. */
|
|
14
|
+
function report(error, requestId, debug) {
|
|
15
|
+
const reason = codeOf(error), code = publicCode(reason), issues = error?.issues, usage = error?.detail?.command;
|
|
16
|
+
if (Array.isArray(issues))
|
|
17
|
+
process.stderr.write(JSON.stringify({ issues }) + '\n');
|
|
18
|
+
const hint = code === 'invalid-input' && typeof usage === 'string' && command(usage)
|
|
19
|
+
? `Usage: ${usageOf(usage).replace(/\n/g, ' | ')}`
|
|
20
|
+
: hints[code];
|
|
21
|
+
process.stderr.write(JSON.stringify({ ...failure(requestId, code), ...(reason !== code ? { reason } : {}), hint }) + '\n');
|
|
22
|
+
if (debug)
|
|
23
|
+
process.stderr.write(debugChain(error));
|
|
24
|
+
return exitFor(code);
|
|
25
|
+
}
|
|
85
26
|
export async function main(argv, version) {
|
|
86
27
|
const requestId = randomUUID();
|
|
87
|
-
let store;
|
|
28
|
+
let store, args;
|
|
88
29
|
const controller = new AbortController();
|
|
89
30
|
const cancel = () => controller.abort();
|
|
90
31
|
process.on('SIGINT', cancel);
|
|
91
32
|
process.on('SIGTERM', cancel);
|
|
92
33
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
process.stdout.write(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
34
|
+
args = parseArguments(argv);
|
|
35
|
+
const json = args.json, spec = command(args.command);
|
|
36
|
+
const emit = (data, text) => {
|
|
37
|
+
const output = json
|
|
38
|
+
? JSON.stringify({ schemaVersion: 1, requestId, ok: true, ...data }) + '\n'
|
|
39
|
+
: (text ?? JSON.stringify(data)) + '\n';
|
|
40
|
+
process.stdout.write(process.stdout.isTTY ? terminalSafe(output) : output);
|
|
41
|
+
};
|
|
42
|
+
const context = {
|
|
43
|
+
args,
|
|
44
|
+
version,
|
|
45
|
+
signal: controller.signal,
|
|
46
|
+
emit,
|
|
47
|
+
write: (text) => {
|
|
48
|
+
process.stdout.write(text);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
if (spec.library === 'none')
|
|
52
|
+
return await spec.handler(context);
|
|
106
53
|
const root = args.library ?? join(homedir(), '.local', 'share', 'sponge');
|
|
107
|
-
if (
|
|
54
|
+
if (spec.library === 'open') {
|
|
108
55
|
try {
|
|
109
56
|
await lstat(join(root, '.sponge-library'));
|
|
110
57
|
}
|
|
111
58
|
catch (error) {
|
|
112
59
|
if (error.code === 'ENOENT')
|
|
113
|
-
throw new
|
|
60
|
+
throw new SpongeError('unavailable', { cause: error });
|
|
114
61
|
throw error;
|
|
115
62
|
}
|
|
116
63
|
}
|
|
117
64
|
store = await openLibrary(root);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return 0;
|
|
125
|
-
}
|
|
126
|
-
if (args.command === 'gc' && args.flags.runtimes) {
|
|
127
|
-
const report = await runtimeGcAction(store, version, args.flags['dry-run'] === true);
|
|
128
|
-
emit(report, runtimeGcText(report));
|
|
129
|
-
return 0;
|
|
130
|
-
}
|
|
131
|
-
if (args.command === 'export' || args.command === 'backup' || args.command === 'gc') {
|
|
132
|
-
emit(await portabilityAction(store, args.command, { itemId: args.positional[0], output: args.flags.output, dryRun: args.flags['dry-run'] === true, report: args.flags.apply }));
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
const runtimeRoot = join(store.root, 'runtime', 'capture', defaultCapturePins.id);
|
|
136
|
-
const nativeRoot = join(store.root, 'runtime', 'models');
|
|
137
|
-
if (args.command === 'setup' && !args.flags.capture) {
|
|
138
|
-
const kind = args.flags.asr ? 'asr' : 'embedding', plan = await nativeSetupPlan(kind, version);
|
|
139
|
-
process.stderr.write(JSON.stringify({ setup: kind, ...plan }) + '\n');
|
|
140
|
-
try {
|
|
141
|
-
await provisionNative(kind, nativeRoot, version, controller.signal);
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
if (error.code === 'repair-auth' && !args.json)
|
|
145
|
-
process.stderr.write(nativeDownloadAuthGuidance + '\n');
|
|
146
|
-
throw error;
|
|
147
|
-
}
|
|
148
|
-
const gc = await setupRuntimeGc(store, version);
|
|
149
|
-
emit({ [kind]: 'ready', gc });
|
|
150
|
-
return 0;
|
|
151
|
-
}
|
|
152
|
-
if (args.command === 'setup') {
|
|
153
|
-
const plan = captureSetupPlan(defaultCapturePins);
|
|
154
|
-
process.stderr.write(`Capture setup: ${plan.downloadBytes} pinned artifact bytes, plus frozen dependency downloads. Temporary installation limit: ${plan.stagingBudgetBytes} bytes; retained runtime limit: ${plan.installationBudgetBytes} bytes; dependency installation deadline: ${plan.installationDeadlineMs / 60000} minutes. ${plan.requiredFreeBytes} free bytes required.\n`);
|
|
155
|
-
await provisionCapture({ runtimeRoot: join(store.root, 'runtime', 'capture'), pins: defaultCapturePins, signal: controller.signal });
|
|
156
|
-
const gc = await setupRuntimeGc(store, version);
|
|
157
|
-
emit({ capture: 'ready', gc }, 'skipped' in gc ? `Capture ready\nRuntime GC skipped: ${gc.skipped}` : `Capture ready\n${runtimeGcText(gc)}`);
|
|
158
|
-
return 0;
|
|
159
|
-
}
|
|
160
|
-
if (args.command === 'list') {
|
|
161
|
-
const items = store.list();
|
|
162
|
-
emit({ items }, items.map(item => `${item.itemId}\t${item.title}`).join('\n'));
|
|
163
|
-
return 0;
|
|
164
|
-
}
|
|
165
|
-
if (args.command === 'show') {
|
|
166
|
-
const item = store.read(args.positional[0]);
|
|
167
|
-
if (!item)
|
|
168
|
-
throw new StoreError('unavailable');
|
|
169
|
-
emit({ item, identity: store.identity(item.itemId), annotations: store.listAnnotations(item.itemId), digest: store.currentDigest(item.itemId) ?? null, editorial: store.currentEditorial(item.itemId) ?? null }, item.markdown);
|
|
170
|
-
return item.status === 'partial' ? 3 : 0;
|
|
171
|
-
}
|
|
172
|
-
if (args.command === 'asset') {
|
|
173
|
-
const item = store.read(args.positional[0]), asset = item?.assets.find(a => a.sha256 === args.positional[1]);
|
|
174
|
-
if (!asset)
|
|
175
|
-
throw new StoreError('unavailable');
|
|
176
|
-
const bytes = store.readBlob(asset);
|
|
177
|
-
if (args.json)
|
|
178
|
-
emit({ asset });
|
|
179
|
-
else {
|
|
180
|
-
if (process.stdout.isTTY)
|
|
181
|
-
throw new InputError();
|
|
182
|
-
process.stdout.write(bytes);
|
|
183
|
-
}
|
|
184
|
-
return 0;
|
|
185
|
-
}
|
|
186
|
-
if (args.command === 'index') {
|
|
187
|
-
const action = args.positional[0], runtime = ['create', 'run'].includes(action) ? await inspectNative('embedding', nativeRoot, version) : undefined;
|
|
188
|
-
const result = await indexAction(store, action, args.positional[1], { runtime, profile: args.flags.profile, signal: controller.signal, ...(action === 'prune' ? { keep: args.flags.keep === undefined ? undefined : Number(args.flags.keep), dryRun: args.flags['dry-run'] === true, vacuum: args.flags.vacuum === true } : {}) });
|
|
189
|
-
emit(result, result.prune ? pruneText(result.prune) : undefined);
|
|
190
|
-
const failed = result.results?.find(r => !r.ok);
|
|
191
|
-
return failed ? exitFor(failed.code) : 0;
|
|
192
|
-
}
|
|
193
|
-
if (args.command === 'search') {
|
|
194
|
-
if (args.flags.mode === 'lexical') {
|
|
195
|
-
const items = store.search(args.positional[0], Number(args.flags.limit ?? 20));
|
|
196
|
-
emit({ items }, items.map(item => `${item.itemId}\t${item.title}`).join('\n'));
|
|
197
|
-
return 0;
|
|
198
|
-
}
|
|
199
|
-
let embedding;
|
|
200
|
-
try {
|
|
201
|
-
embedding = await inspectNative('embedding', nativeRoot, version);
|
|
202
|
-
}
|
|
203
|
-
catch { /* Hybrid explicitly reports semantic unavailability and preserves lexical results. */ }
|
|
204
|
-
const result = await store.searchHybrid(args.positional[0], { profileId: args.flags.profile, limit: Number(args.flags.limit ?? 20), signal: controller.signal, ...(embedding ? { embed: createQueryEmbedder(store, embedding) } : {}) });
|
|
205
|
-
emit({ items: result.hits, warnings: result.warnings, generation: result.generation }, [...result.hits.map(item => `${item.itemId}\t${item.title}`), ...result.warnings.map(warning => `Warning: ${warning}`)].join('\n'));
|
|
206
|
-
return controller.signal.aborted ? 130 : 0;
|
|
207
|
-
}
|
|
208
|
-
if (args.command === 'visibility') {
|
|
209
|
-
const value = args.positional[1] === 'default' ? null : args.positional[1];
|
|
210
|
-
const state = store.setVisibility(args.positional[0], value);
|
|
211
|
-
emit({ itemId: args.positional[0], visibility: state }, `${state.effective}${state.override ? ' (set)' : ' (default)'}`);
|
|
212
|
-
return 0;
|
|
213
|
-
}
|
|
214
|
-
if (args.command === 'highlight') {
|
|
215
|
-
const annotation = store.addHighlight({ itemId: args.positional[0], quote: args.positional[1], ...(typeof args.flags.note === 'string' ? { note: args.flags.note } : {}), ...(args.flags.occurrence !== undefined ? { occurrence: Number(args.flags.occurrence) } : {}) });
|
|
216
|
-
emit({ annotation }, annotation.id);
|
|
217
|
-
return 0;
|
|
218
|
-
}
|
|
219
|
-
if (args.command === 'comment') {
|
|
220
|
-
const annotation = store.addComment({ itemId: args.positional[0], body: args.positional[1], ...(typeof args.flags.on === 'string' ? { parentId: args.flags.on } : {}) });
|
|
221
|
-
emit({ annotation }, annotation.id);
|
|
222
|
-
return 0;
|
|
223
|
-
}
|
|
224
|
-
if (args.command === 'publish') {
|
|
225
|
-
const minAgeDays = Number(args.flags['min-age-days'] ?? 0);
|
|
226
|
-
if (typeof args.flags.output === 'string') {
|
|
227
|
-
const result = publishSite(store, { output: args.flags.output, minAgeDays, ...(typeof args.flags.title === 'string' ? { title: args.flags.title } : {}), ...(typeof args.flags['base-url'] === 'string' ? { baseUrl: args.flags['base-url'] } : {}) });
|
|
228
|
-
emit(result, `Published ${result.entries} entr${result.entries === 1 ? 'y' : 'ies'} to ${result.output}`);
|
|
229
|
-
return 0;
|
|
230
|
-
}
|
|
231
|
-
const all = store.publicProjection(), projection = { ...all, entries: minAge(all.entries, minAgeDays) };
|
|
232
|
-
emit({ dryRun: true, ...projection, ...(minAgeDays ? { held: all.entries.length - projection.entries.length } : {}) }, [...projection.entries.map(entry => `${entry.uri}\t${entry.title}`), ...(projection.skipped.length ? [`Skipped ${projection.skipped.length} public item(s) without a public link`] : [])].join('\n') || 'Nothing public');
|
|
233
|
-
return 0;
|
|
234
|
-
}
|
|
235
|
-
if (args.command === 'digest') {
|
|
236
|
-
if (!args.flags.endpoint) {
|
|
237
|
-
const digest = store.currentDigest(args.positional[0]);
|
|
238
|
-
if (!digest)
|
|
239
|
-
throw new StoreError('unavailable');
|
|
240
|
-
emit({ digest }, digestText(digest));
|
|
241
|
-
return 0;
|
|
242
|
-
}
|
|
243
|
-
const model = await connectEndpoint({ endpoint: String(args.flags.endpoint), model: String(args.flags.model), signal: controller.signal });
|
|
244
|
-
const embed = args.flags['check-support'] ? createTextEmbedder(store, await inspectNative('embedding', nativeRoot, version)) : undefined;
|
|
245
|
-
const result = await createDigest(store, args.positional[0], model, { signal: controller.signal, ...(embed ? { embed } : {}) });
|
|
246
|
-
emit({ digest: result.derivation, duplicate: result.duplicate, ...(result.trace ? { trace: result.trace } : {}) }, digestText(result.derivation));
|
|
247
|
-
return 0;
|
|
248
|
-
}
|
|
249
|
-
if (args.command === 'review') {
|
|
250
|
-
const prior = store.readDigest(args.positional[0]);
|
|
251
|
-
if (!prior)
|
|
252
|
-
throw new StoreError('unavailable');
|
|
253
|
-
const digest = store.reviewDigest(prior.id, reviewFromSpec(prior, args.flags.accept, args.flags.reject));
|
|
254
|
-
emit({ digest }, digestText(digest));
|
|
255
|
-
return 0;
|
|
256
|
-
}
|
|
257
|
-
if (args.command === 'delete') {
|
|
258
|
-
store.delete(args.positional[0]);
|
|
259
|
-
emit({ itemId: args.positional[0], deleted: true }, 'Deleted');
|
|
260
|
-
return 0;
|
|
261
|
-
}
|
|
262
|
-
if (args.command === 'queue') {
|
|
263
|
-
const result = await queueAction(store, args.positional[0], args.positional[1]);
|
|
264
|
-
emit(result);
|
|
265
|
-
return 0;
|
|
266
|
-
}
|
|
267
|
-
if (args.command === 'doctor') {
|
|
268
|
-
let capture = 'ready';
|
|
269
|
-
try {
|
|
270
|
-
await inspectCaptureRuntime(runtimeRoot, defaultCapturePins);
|
|
271
|
-
}
|
|
272
|
-
catch (error) {
|
|
273
|
-
capture = errorCode(error, 'runtime-missing');
|
|
274
|
-
}
|
|
275
|
-
const states = {};
|
|
276
|
-
for (const kind of ['asr', 'embedding']) {
|
|
277
|
-
try {
|
|
278
|
-
await inspectNative(kind, nativeRoot, version);
|
|
279
|
-
states[kind] = 'ready';
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
states[kind] = errorCode(error, 'runtime-missing');
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
const cleanup = store.pendingCleanupCount();
|
|
286
|
-
emit({ library: 'ready', capture, ...states, pendingCleanup: cleanup }, `Library: ready\nCapture: ${capture}\nASR: ${states.asr}\nEmbedding: ${states.embedding}\nPending cleanup: ${cleanup}`);
|
|
287
|
-
return cleanup || capture !== 'ready' ? 4 : 0;
|
|
288
|
-
}
|
|
289
|
-
if (args.command === 'run' && !hasDueAcquisition(store)) {
|
|
290
|
-
if (store.ownerSnapshot())
|
|
291
|
-
throw new StoreError('owner-busy');
|
|
292
|
-
if (store.pendingCleanupCount())
|
|
293
|
-
throw Object.assign(Error('cleanup-required'), { code: 'cleanup-required' });
|
|
294
|
-
if (!store.listJobs().some(job => job.operation === 'index' && job.state === 'queued')) {
|
|
295
|
-
emit({ results: [], indexResults: [] }, 'Queue empty');
|
|
296
|
-
return 0;
|
|
297
|
-
}
|
|
298
|
-
const runtime = await inspectNative('embedding', nativeRoot, version), indexResults = await drainIndexQueue(store, { runtime, signal: controller.signal });
|
|
299
|
-
emit({ results: [], indexResults });
|
|
300
|
-
const failed = indexResults.find(result => !result.ok);
|
|
301
|
-
return failed && !failed.ok ? exitFor(failed.code) : controller.signal.aborted ? 130 : 0;
|
|
302
|
-
}
|
|
303
|
-
const connections = await readConnections(store.root);
|
|
304
|
-
let jobId, itemId;
|
|
305
|
-
if (args.command === 'add') {
|
|
306
|
-
const connection = args.flags.connection ? connections.find(c => c.id === args.flags.connection) : undefined;
|
|
307
|
-
if (args.flags.connection && !connection)
|
|
308
|
-
throw new InputError();
|
|
309
|
-
const input = args.positional[0], kind = (args.flags.kind ?? (/\.pdf(?:[?#]|$)/i.test(input) || input.startsWith('file:') ? 'pdf' : 'article'));
|
|
310
|
-
const source = (kind === 'pdf' ? pdfSource : kind === 'media' ? mediaSource : articleSource)(input, connection?.realm ?? 'public');
|
|
311
|
-
const submission = store.submitSource({ kind, ...source, idempotencyKey: 'save:' + source.sourceKey });
|
|
312
|
-
jobId = submission.jobId;
|
|
313
|
-
itemId = submission.itemId;
|
|
314
|
-
const job = store.listJobs().find(j => j.id === jobId);
|
|
315
|
-
if (!args.flags.wait) {
|
|
316
|
-
emit({ ...submission, state: job?.state ?? 'queued' }, `${itemId}\t${job?.state ?? 'queued'}`);
|
|
317
|
-
return 0;
|
|
318
|
-
}
|
|
319
|
-
if (job?.state === 'succeeded') {
|
|
320
|
-
const item = store.read(itemId);
|
|
321
|
-
emit({ itemId, jobId, status: item.status }, `${itemId}\t${item.status}`);
|
|
322
|
-
return item.status === 'partial' ? 3 : 0;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
else if (args.command === 'refresh') {
|
|
326
|
-
const previous = store.listJobs().find(j => j.item_id === args.positional[0]), source = previous && store.sourceForJob(String(previous.id));
|
|
327
|
-
if (!source || !['article', 'pdf', 'media'].includes(source.kind))
|
|
328
|
-
throw new StoreError('unavailable');
|
|
329
|
-
const submission = store.submitSource({ ...source, kind: source.kind, intent: 'refresh', idempotencyKey: randomUUID() });
|
|
330
|
-
jobId = submission.jobId;
|
|
331
|
-
itemId = submission.itemId;
|
|
332
|
-
if (!args.flags.wait) {
|
|
333
|
-
emit(submission, `${itemId}\tqueued`);
|
|
334
|
-
return 0;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (jobId) {
|
|
338
|
-
const job = store.listJobs().find(j => j.id === jobId);
|
|
339
|
-
if (job && job.state !== 'queued') {
|
|
340
|
-
const code = job.state === 'cancelled' ? 'cancelled' : job.state === 'cleanup-pending' ? 'cleanup-required' : job.state === 'running' ? 'owner-busy' : errorCode({ code: job.reason }, 'unclassified');
|
|
341
|
-
throw Object.assign(Error(code), { code });
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
if (args.command === 'worker') {
|
|
345
|
-
emit(await serveWorker(store, { signal: controller.signal, language: args.flags.language, forceLocal: args.flags['force-local'] === true, resolveCaptureRuntime: () => inspectCaptureRuntime(runtimeRoot, defaultCapturePins), resolveConnection: (realm) => { if (realm === 'public')
|
|
346
|
-
return undefined; const connection = connections.find(c => c.realm === realm); if (!connection)
|
|
347
|
-
throw Object.assign(Error('repair-auth'), { code: 'repair-auth' }); return connection.capture; } }, version));
|
|
348
|
-
return 0;
|
|
349
|
-
}
|
|
350
|
-
if (args.command === 'import' && args.positional[0] === 'jungle') {
|
|
351
|
-
// A dry run reads Jungle and the library only; applying reverifies every capture with the released verifier.
|
|
352
|
-
const dryRun = args.flags.apply !== true, runtime = dryRun ? undefined : await inspectCaptureRuntime(runtimeRoot, defaultCapturePins).catch(error => { const code = errorCode(error, 'runtime-missing'); throw Object.assign(Error(code), { code }); });
|
|
353
|
-
const report = await jungleAction(store, args.positional[1], { dryRun, runtime, limit: Number(args.flags.limit ?? 1000), offset: Number(args.flags.offset ?? 0), signal: controller.signal });
|
|
354
|
-
emit({ report }, jungleText(report));
|
|
355
|
-
return controller.signal.aborted ? 130 : report.counts.skipped['capture-failed'] || report.counts.skipped['capture-unsupported'] ? 3 : 0;
|
|
356
|
-
}
|
|
357
|
-
let runtime;
|
|
358
|
-
try {
|
|
359
|
-
runtime = await inspectCaptureRuntime(runtimeRoot, defaultCapturePins);
|
|
360
|
-
}
|
|
361
|
-
catch (error) {
|
|
362
|
-
const code = errorCode(error, 'runtime-missing');
|
|
363
|
-
throw Object.assign(Error(code), { code });
|
|
364
|
-
}
|
|
365
|
-
if (args.command === 'import') {
|
|
366
|
-
const report = await importAction(store, args.positional[0], { runtime, dryRun: args.flags.apply !== true, limit: Number(args.flags.limit ?? 20), offset: Number(args.flags.offset ?? 0), signal: controller.signal });
|
|
367
|
-
emit({ report });
|
|
368
|
-
return controller.signal.aborted ? 130 : report.entries.some(entry => entry.status === 'failed' || entry.status === 'unsupported') ? 3 : 0;
|
|
369
|
-
}
|
|
370
|
-
let asrRuntime;
|
|
371
|
-
try {
|
|
372
|
-
asrRuntime = await inspectNative('asr', nativeRoot, version);
|
|
373
|
-
}
|
|
374
|
-
catch (error) {
|
|
375
|
-
if (!['runtime-missing', 'unsupported'].includes(errorCode(error, 'integrity')))
|
|
376
|
-
throw error;
|
|
377
|
-
}
|
|
378
|
-
const workerOptions = { asrRuntime, language: args.flags.language, forceLocal: args.flags['force-local'] === true, runtime, jobId, signal: controller.signal, allowPrivateNetwork: args.flags['allow-private-network'] === true, resolveConnection: (realm) => { if (realm === 'public')
|
|
379
|
-
return undefined; const connection = connections.find(c => c.realm === realm); if (!connection)
|
|
380
|
-
throw Object.assign(Error('repair-auth'), { code: 'repair-auth' }); return connection.capture; } };
|
|
381
|
-
const results = await drainLibrary(store, workerOptions);
|
|
382
|
-
if (jobId && !results.length && !controller.signal.aborted) {
|
|
383
|
-
const job = store.listJobs().find(j => j.id === jobId);
|
|
384
|
-
const code = job?.state === 'cancelled' ? 'cancelled' : job?.state === 'cleanup-pending' ? 'cleanup-required' : job?.state === 'running' ? 'owner-busy' : errorCode({ code: job?.reason }, 'unclassified');
|
|
385
|
-
throw Object.assign(Error(code), { code });
|
|
386
|
-
}
|
|
387
|
-
let indexResults = [];
|
|
388
|
-
if (args.command === 'run' && !controller.signal.aborted && store.listJobs().some(job => job.operation === 'index' && job.state === 'queued')) {
|
|
389
|
-
const embedding = await inspectNative('embedding', nativeRoot, version);
|
|
390
|
-
indexResults = await drainIndexQueue(store, { runtime: embedding, signal: controller.signal });
|
|
391
|
-
const indexFailed = indexResults.find(result => !result.ok);
|
|
392
|
-
if (indexFailed && !indexFailed.ok) {
|
|
393
|
-
process.stderr.write(JSON.stringify(indexFailed) + '\n');
|
|
394
|
-
return exitFor(indexFailed.code);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
const failed = results.find(result => !result.ok);
|
|
398
|
-
if (failed && !failed.ok) {
|
|
399
|
-
process.stderr.write(JSON.stringify(failed) + '\n');
|
|
400
|
-
return exitFor(failed.code);
|
|
401
|
-
}
|
|
402
|
-
if (controller.signal.aborted)
|
|
403
|
-
return 130;
|
|
404
|
-
emit({ results, indexResults }, results.map(result => result.ok ? `${result.itemId}\t${result.status}` : '').join('\n') || 'Queue empty');
|
|
405
|
-
return results.some(result => result.ok && result.status === 'partial') ? 3 : 0;
|
|
65
|
+
return await spec.handler({
|
|
66
|
+
...context,
|
|
67
|
+
store,
|
|
68
|
+
captureRoot: join(store.root, 'runtime', 'capture', defaultCapturePins.id),
|
|
69
|
+
nativeRoot: join(store.root, 'runtime', 'models'),
|
|
70
|
+
});
|
|
406
71
|
}
|
|
407
72
|
catch (error) {
|
|
408
|
-
|
|
409
|
-
if (Array.isArray(issues))
|
|
410
|
-
process.stderr.write(JSON.stringify({ issues }) + '\n');
|
|
411
|
-
process.stderr.write(JSON.stringify(failure(requestId, code)) + '\n');
|
|
412
|
-
return exitFor(code);
|
|
73
|
+
return report(error, requestId, process.env.SPONGE_DEBUG === '1' || (args ? args.debug : argv.includes('--debug')));
|
|
413
74
|
}
|
|
414
75
|
finally {
|
|
415
76
|
store?.close();
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { LibraryStore } from '../store/index.js';
|
|
2
|
-
import { restoreLibrary } from '../portability/backup.js';
|
|
3
2
|
export declare function portabilityAction(store: LibraryStore, action: 'export' | 'backup' | 'gc', options: {
|
|
4
3
|
itemId?: string;
|
|
5
4
|
output?: string;
|
|
6
5
|
dryRun?: boolean;
|
|
7
6
|
report?: string;
|
|
8
7
|
}): Promise<Record<string, unknown>>;
|
|
9
|
-
export declare const restoreAction: typeof restoreLibrary;
|
|
@@ -1,34 +1,40 @@
|
|
|
1
1
|
import { fstatSync, closeSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import { StoreError } from '../store/types.js';
|
|
5
4
|
import { openRead, readExact, writePrivateExclusive } from '../portability/files.js';
|
|
6
|
-
import { backupLibrary
|
|
5
|
+
import { backupLibrary } from '../portability/backup.js';
|
|
7
6
|
import { exportItem } from '../portability/export.js';
|
|
8
7
|
import { createGcReport, applyGcReport } from '../portability/gc.js';
|
|
8
|
+
import { SpongeError } from '../errors.js';
|
|
9
9
|
export async function portabilityAction(store, action, options) {
|
|
10
10
|
if (action === 'export') {
|
|
11
11
|
if (!options.itemId || !options.output)
|
|
12
|
-
throw new
|
|
12
|
+
throw new SpongeError('invalid-input');
|
|
13
13
|
return exportItem(store, options.itemId, options.output);
|
|
14
14
|
}
|
|
15
15
|
if (action === 'backup') {
|
|
16
16
|
if (!options.output)
|
|
17
|
-
throw new
|
|
17
|
+
throw new SpongeError('invalid-input');
|
|
18
18
|
return backupLibrary(store, options.output);
|
|
19
19
|
}
|
|
20
20
|
if (action !== 'gc' || Boolean(options.dryRun) === Boolean(options.report))
|
|
21
|
-
throw new
|
|
21
|
+
throw new SpongeError('invalid-input');
|
|
22
|
+
// A backup that crashed must not fence collection forever: expire fences whose holder is provably dead.
|
|
23
|
+
await store.backups.expireFences();
|
|
22
24
|
if (options.dryRun) {
|
|
23
25
|
const report = createGcReport(store), path = join(store.root, 'runtime', `gc-${randomUUID()}.json`);
|
|
24
26
|
writePrivateExclusive(path, JSON.stringify(report) + '\n');
|
|
25
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
report: path,
|
|
29
|
+
candidates: report.candidates.length,
|
|
30
|
+
bytes: report.candidates.reduce((n, c) => n + c.bytes, 0),
|
|
31
|
+
};
|
|
26
32
|
}
|
|
27
33
|
const fd = openRead(options.report);
|
|
28
34
|
try {
|
|
29
35
|
const bytes = fstatSync(fd).size;
|
|
30
36
|
if (bytes > 32 * 1024 ** 2)
|
|
31
|
-
throw new
|
|
37
|
+
throw new SpongeError('limit-exceeded');
|
|
32
38
|
return applyGcReport(store, JSON.parse(readExact(fd, bytes).toString('utf8')));
|
|
33
39
|
}
|
|
34
40
|
finally {
|
|
@@ -36,4 +42,3 @@ export async function portabilityAction(store, action, options) {
|
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
// Restore must dispatch before opening the normal library; it never targets the default root.
|
|
39
|
-
export const restoreAction = restoreLibrary;
|
package/dist/src/cli/queue.js
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
import { StoreError } from '../store/index.js';
|
|
2
1
|
import { recoverWorker } from '../worker/custody.js';
|
|
2
|
+
import { SpongeError } from '../errors.js';
|
|
3
3
|
export async function queueAction(store, action, jobId) {
|
|
4
4
|
if (action === 'status') {
|
|
5
5
|
if (jobId)
|
|
6
|
-
return { jobs: store.
|
|
7
|
-
return {
|
|
6
|
+
return { jobs: store.queue.list().filter((j) => j.id === jobId) };
|
|
7
|
+
return {
|
|
8
|
+
jobs: store.queue.list(),
|
|
9
|
+
owner: store.queue.ownerSnapshot(),
|
|
10
|
+
pendingCleanup: store.queue.pendingCleanup(),
|
|
11
|
+
};
|
|
8
12
|
}
|
|
9
13
|
if (action === 'recover') {
|
|
10
14
|
if (jobId)
|
|
11
|
-
throw new
|
|
15
|
+
throw new SpongeError('invalid-input');
|
|
12
16
|
return await recoverWorker(store);
|
|
13
17
|
}
|
|
14
18
|
if (!jobId)
|
|
15
|
-
throw new
|
|
19
|
+
throw new SpongeError('invalid-input');
|
|
16
20
|
if (action === 'cancel')
|
|
17
|
-
store.
|
|
21
|
+
store.queue.cancel(jobId);
|
|
18
22
|
else if (action === 'retry')
|
|
19
|
-
store.
|
|
23
|
+
store.queue.retry(jobId, { automatic: false });
|
|
20
24
|
else
|
|
21
|
-
throw new
|
|
25
|
+
throw new SpongeError('invalid-input');
|
|
22
26
|
return { jobId, action };
|
|
23
27
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { LibraryStore } from '../store/index.js';
|
|
2
|
-
import { type
|
|
3
|
-
/** References of this installed Sponge: its capture pin and its catalog's model packages. */
|
|
4
|
-
export declare function installedReferences(version: string): Promise<Omit<RuntimeGcOptions, 'dryRun'>>;
|
|
2
|
+
import { type RuntimeGcReport } from '../worker/runtime-gc.js';
|
|
5
3
|
export declare function runtimeGcAction(store: LibraryStore, version: string, dryRun: boolean): Promise<RuntimeGcReport>;
|
|
6
4
|
export declare function runtimeGcText(report: RuntimeGcReport): string;
|
|
7
5
|
/** Setup collection follows a verified install and never fails that install. */
|
package/dist/src/cli/runtimes.js
CHANGED
|
@@ -2,19 +2,26 @@ import { defaultCapturePins } from '../acquisition/index.js';
|
|
|
2
2
|
import { loadNativeCatalog, nativeModelPackage } from '../models/catalog.js';
|
|
3
3
|
import { modelSetupPlan } from '../models/artifacts.js';
|
|
4
4
|
import { collectRuntimes } from '../worker/runtime-gc.js';
|
|
5
|
+
import { codeOf } from '../errors.js';
|
|
5
6
|
/** References of this installed Sponge: its capture pin and its catalog's model packages. */
|
|
6
|
-
|
|
7
|
+
async function installedReferences(version) {
|
|
7
8
|
let models = null;
|
|
8
9
|
try {
|
|
9
10
|
const catalog = await loadNativeCatalog(version);
|
|
10
|
-
models = {
|
|
11
|
+
models = {
|
|
12
|
+
fingerprints: ['asr', 'embedding'].map((kind) => modelSetupPlan(nativeModelPackage(catalog, kind)).fingerprint),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
/* Without the catalog no model directory is provably superseded. */
|
|
11
17
|
}
|
|
12
|
-
catch { /* Without the catalog no model directory is provably superseded. */ }
|
|
13
18
|
return { captureIds: [defaultCapturePins.id], models };
|
|
14
19
|
}
|
|
15
|
-
export async function runtimeGcAction(store, version, dryRun) {
|
|
20
|
+
export async function runtimeGcAction(store, version, dryRun) {
|
|
21
|
+
return collectRuntimes(store, { ...(await installedReferences(version)), dryRun });
|
|
22
|
+
}
|
|
16
23
|
export function runtimeGcText(report) {
|
|
17
|
-
const lines = report.removed.map(e => `${report.dryRun ? 'would remove' : 'removed'}\t${e.kind}\t${e.name}\t${e.bytes}`);
|
|
24
|
+
const lines = report.removed.map((e) => `${report.dryRun ? 'would remove' : 'removed'}\t${e.kind}\t${e.name}\t${e.bytes}`);
|
|
18
25
|
for (const e of report.kept)
|
|
19
26
|
if (e.reason !== 'referenced')
|
|
20
27
|
lines.push(`kept\t${e.kind}\t${e.name}\t${e.reason}`);
|
|
@@ -27,6 +34,6 @@ export async function setupRuntimeGc(store, version) {
|
|
|
27
34
|
return await runtimeGcAction(store, version, false);
|
|
28
35
|
}
|
|
29
36
|
catch (error) {
|
|
30
|
-
return { skipped:
|
|
37
|
+
return { skipped: codeOf(error) };
|
|
31
38
|
}
|
|
32
39
|
}
|
package/dist/src/cli/terminal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** Neutralize terminal commands without changing redirected Markdown or stored content. */
|
|
2
2
|
export function terminalSafe(value) {
|
|
3
|
-
return value.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/gu, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'));
|
|
3
|
+
return value.replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/gu, (c) => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'));
|
|
4
4
|
}
|
package/dist/src/cli/worker.d.ts
CHANGED
|
@@ -4,6 +4,13 @@ import { type ServiceEnvironment } from '../service/launch-agent.js';
|
|
|
4
4
|
export declare function workerAction(store: LibraryStore, action: 'install' | 'status' | 'uninstall', version: string, environment?: ServiceEnvironment, options?: {
|
|
5
5
|
language?: 'en';
|
|
6
6
|
}): Promise<Record<string, unknown>>;
|
|
7
|
+
/** Doctor's read-only view of the background worker: never installs, repairs or signals anything. */
|
|
8
|
+
export declare function workerDoctor(store: LibraryStore, version: string, environment?: ServiceEnvironment): Promise<{
|
|
9
|
+
worker: string;
|
|
10
|
+
drift?: unknown;
|
|
11
|
+
repair?: unknown;
|
|
12
|
+
lastExit?: unknown;
|
|
13
|
+
}>;
|
|
7
14
|
type DrainOptions = Parameters<typeof drainLibrary>[1];
|
|
8
15
|
export type ServiceDrainOptions = Omit<DrainOptions, 'runtime'> & {
|
|
9
16
|
runtime?: DrainOptions['runtime'];
|