@cavelang/cli 0.33.0 → 0.35.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 +15 -3
- package/dist/internal/automate/main.d.ts.map +1 -1
- package/dist/internal/automate/main.js +9 -2
- package/dist/internal/automate/main.js.map +1 -1
- package/dist/internal/connect/declared.d.ts +225 -0
- package/dist/internal/connect/declared.d.ts.map +1 -0
- package/dist/internal/connect/declared.js +521 -0
- package/dist/internal/connect/declared.js.map +1 -0
- package/dist/internal/connect/index.d.ts +4 -2
- package/dist/internal/connect/index.d.ts.map +1 -1
- package/dist/internal/connect/index.js +3 -1
- package/dist/internal/connect/index.js.map +1 -1
- package/dist/internal/connect/main.d.ts.map +1 -1
- package/dist/internal/connect/main.js +356 -10
- package/dist/internal/connect/main.js.map +1 -1
- package/dist/internal/connect/run.d.ts +47 -1
- package/dist/internal/connect/run.d.ts.map +1 -1
- package/dist/internal/connect/run.js +81 -33
- package/dist/internal/connect/run.js.map +1 -1
- package/dist/internal/connect/source.d.ts +33 -2
- package/dist/internal/connect/source.d.ts.map +1 -1
- package/dist/internal/connect/source.js +168 -8
- package/dist/internal/connect/source.js.map +1 -1
- package/dist/internal/connect/template.d.ts +18 -0
- package/dist/internal/connect/template.d.ts.map +1 -1
- package/dist/internal/connect/template.js +28 -0
- package/dist/internal/connect/template.js.map +1 -1
- package/dist/internal/ingest/main.d.ts.map +1 -1
- package/dist/internal/ingest/main.js +19 -2
- package/dist/internal/ingest/main.js.map +1 -1
- package/dist/internal/mcp/main.d.ts +1 -1
- package/dist/internal/mcp/main.d.ts.map +1 -1
- package/dist/internal/mcp/main.js +15 -3
- package/dist/internal/mcp/main.js.map +1 -1
- package/dist/internal/rules/rule.d.ts.map +1 -1
- package/dist/internal/rules/rule.js +1 -22
- package/dist/internal/rules/rule.js.map +1 -1
- package/dist/internal/sync/sync.d.ts +2 -3
- package/dist/internal/sync/sync.d.ts.map +1 -1
- package/dist/internal/sync/sync.js +3 -15
- package/dist/internal/sync/sync.js.map +1 -1
- package/dist/internal/view/main.d.ts.map +1 -1
- package/dist/internal/view/main.js +4 -3
- package/dist/internal/view/main.js.map +1 -1
- package/dist/src/cli.d.ts +15 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +227 -54
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands.d.ts +2 -2
- package/dist/src/commands.js +2 -2
- package/dist/src/commands.js.map +1 -1
- package/dist/src/dispatch.d.ts.map +1 -1
- package/dist/src/dispatch.js +7 -1
- package/dist/src/dispatch.js.map +1 -1
- package/dist/src/doctor.d.ts +1 -1
- package/dist/src/doctor.d.ts.map +1 -1
- package/dist/src/doctor.js +27 -2
- package/dist/src/doctor.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/package.json +19 -19
- package/src/cli.ts +258 -53
- package/src/commands.ts +2 -2
- package/src/dispatch.ts +7 -1
- package/src/doctor.ts +29 -3
- package/src/index.ts +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -40,7 +40,8 @@ import { parseArgs } from 'node:util';
|
|
|
40
40
|
import { Version } from '@cavelang/core';
|
|
41
41
|
import { parseDocument, Token } from '@cavelang/parser';
|
|
42
42
|
import { Registry, standardRegistry } from '@cavelang/canonical';
|
|
43
|
-
import {
|
|
43
|
+
import { backup as backupStore, defaultDbPath, kindOf, LocateError, openAt, restoreBackup, Sensitivity, verifyBackup } from '@cavelang/store';
|
|
44
|
+
import { Declared, assemble as assembleSources } from '@cavelang/cli/connect';
|
|
44
45
|
import { defaultLimit as defaultQueryLimit, maxLimit as maxQueryLimit, page as caveQueryPage } from '@cavelang/query';
|
|
45
46
|
import { check as caveCheck, defaultMinScore, defaultStaleDays, gatedIngest, judgePrompt, parseJudgeReply, generateClient, suggestAliases, writeSuggestions } from '@cavelang/cli/shape';
|
|
46
47
|
import { declareRules, defaultMaxPasses, defaultMinConf, derive, listRules, retractRule } from '@cavelang/cli/rules';
|
|
@@ -115,9 +116,11 @@ Usage:
|
|
|
115
116
|
cave help [command] this text, or one command's options and examples
|
|
116
117
|
|
|
117
118
|
Every command answers --help. --db defaults to $CAVE_DB, or cave.db in
|
|
118
|
-
the current directory
|
|
119
|
+
the current directory, and names a SQLite store or a CAVE text file: text
|
|
120
|
+
replays into an in-memory store for commands that only read (writes refuse
|
|
121
|
+
it), and a read never creates a missing database. The spec lives in the .claude/skills/ directory at
|
|
119
122
|
the repository root (section index in README.md).`;
|
|
120
|
-
const dbHelp = `--db <path>
|
|
123
|
+
const dbHelp = `--db <path> store: a SQLite file, or CAVE text read into memory (default: $CAVE_DB, or cave.db)`;
|
|
121
124
|
/** Per-command help, printed for \`cave <command> --help\` and \`cave help <command>\`. */
|
|
122
125
|
export const commandHelp = {
|
|
123
126
|
doctor: `cave doctor — diagnose runtime, installation, and store health
|
|
@@ -216,7 +219,7 @@ Examples:
|
|
|
216
219
|
query: `cave query — run a CAVE-Q pattern against a store
|
|
217
220
|
|
|
218
221
|
Usage:
|
|
219
|
-
cave query [--db <path>] <pattern> [WHERE <filter>] [--limit <n>] [--cursor <token>] [--json] [--all] [--aliases] [--as-of <t>] [--at <t>] [--resolve] [--no-prelude]
|
|
222
|
+
cave query [--db <path>] <pattern> [WHERE <filter>] [--limit <n>] [--cursor <token>] [--json] [--all] [--aliases] [--as-of <t>] [--at <t>] [--resolve] [--sources] [--no-prelude]
|
|
220
223
|
|
|
221
224
|
Options:
|
|
222
225
|
${dbHelp}
|
|
@@ -242,6 +245,12 @@ Options:
|
|
|
242
245
|
collapse to the row the resolution policy picks
|
|
243
246
|
(precedence class, reliability-weighted confidence, tx);
|
|
244
247
|
incompatible with --all
|
|
248
|
+
--sources overlay the store's declared sources (spec §23.4) inside a
|
|
249
|
+
rolled-back transaction — federation-lite: external data is
|
|
250
|
+
consulted at query time, nothing persists. The answer is
|
|
251
|
+
whole (no --cursor, --limit is not a page size); a text
|
|
252
|
+
store followed its local sources on open, so only URL
|
|
253
|
+
sources are loaded here
|
|
245
254
|
--no-prelude open the store without the standard verb registry
|
|
246
255
|
|
|
247
256
|
Patterns are claim triples with ?variables and optional metadata filters
|
|
@@ -252,6 +261,7 @@ with the answer; fully bound patterns print the raw line, comment included.
|
|
|
252
261
|
|
|
253
262
|
Examples:
|
|
254
263
|
cave query '?x USES jwt'
|
|
264
|
+
cave query --db notes.cave '?x USES jwt'
|
|
255
265
|
cave query --db k.db '?x HAS bug: ?bug #security'
|
|
256
266
|
cave query --db k.db '?cause CAUSE app/crash' 'WHERE conf >= 0.5'
|
|
257
267
|
cave query --db k.db 'terrier EXTENDS+ animal'
|
|
@@ -260,6 +270,7 @@ Examples:
|
|
|
260
270
|
cave query --db k.db 'revenue IS' --at 2026-07
|
|
261
271
|
cave query --db k.db 'alice WORKS-AT ?org' --at 2021
|
|
262
272
|
cave query --db k.db 'service HAS owner: ?who' --resolve
|
|
273
|
+
cave query --db k.db '?who WORKS-AT acme' --sources
|
|
263
274
|
cave query --db k.db '?x ?verb ?y @production' --json`,
|
|
264
275
|
search: `cave search — full-text search over claims and comments (spec §13.2)
|
|
265
276
|
|
|
@@ -733,6 +744,54 @@ const readStdin = () => {
|
|
|
733
744
|
}
|
|
734
745
|
}
|
|
735
746
|
};
|
|
747
|
+
/**
|
|
748
|
+
* Opens the store `--db` names (spec §13.7): a SQLite file, or a CAVE text
|
|
749
|
+
* file replayed into memory. `read` never creates, migrates, or changes a
|
|
750
|
+
* file — a missing path is an error, not an empty database; `scratch` is a
|
|
751
|
+
* dry run that appends inside a rolled-back transaction, so it may write
|
|
752
|
+
* but creates and migrates nothing; `write` creates a missing SQLite store,
|
|
753
|
+
* migrates an older one, and refuses text files with the materialization
|
|
754
|
+
* hint.
|
|
755
|
+
*/
|
|
756
|
+
const openDb = (values, intent) => openAt(values.db ?? defaultDbPath(), {
|
|
757
|
+
intent,
|
|
758
|
+
// A text store follows its declared sources on open (spec §23.4).
|
|
759
|
+
assemble: assembleSources,
|
|
760
|
+
...values['no-prelude'] === true ? { registry: Registry.empty } : {}
|
|
761
|
+
});
|
|
762
|
+
const sourcesRollback = Symbol('cave query --sources rollback');
|
|
763
|
+
/**
|
|
764
|
+
* Runs `body` with the store's declared sources overlaid (spec §23.4): the
|
|
765
|
+
* sources, loaded beforehand (URLs included), append inside a transaction
|
|
766
|
+
* that always rolls back, so external data is consulted at query time and
|
|
767
|
+
* nothing persists, digests included.
|
|
768
|
+
*/
|
|
769
|
+
const withSources = (store, discovered, body) => {
|
|
770
|
+
let result;
|
|
771
|
+
try {
|
|
772
|
+
store.transaction(() => {
|
|
773
|
+
// Discovered on a snapshot: if a writer changed the declarations
|
|
774
|
+
// since, the sequence is stale and the caller rediscovers.
|
|
775
|
+
if (!Declared.sameDeclarations(discovered.baseline, Declared.declarationState(store))) {
|
|
776
|
+
throw sourcesRollback;
|
|
777
|
+
}
|
|
778
|
+
for (const source of discovered.sequence) {
|
|
779
|
+
const report = Declared.run(store, source, { force: true });
|
|
780
|
+
if (report.failures.length > 0) {
|
|
781
|
+
throw new LocateError(`source/${source.declared.name}: ${report.failures.map(failure => `${failure.record}: ${failure.problems.join('; ')}`).join('; ')}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
result = { value: body() };
|
|
785
|
+
throw sourcesRollback;
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
catch (error) {
|
|
789
|
+
if (error !== sourcesRollback) {
|
|
790
|
+
throw error;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return result;
|
|
794
|
+
};
|
|
736
795
|
const readInput = (files) => files.length === 0 || (files.length === 1 && files[0] === '-') ?
|
|
737
796
|
readStdin() :
|
|
738
797
|
files.map(file => readFileSync(file, 'utf8')).join('\n');
|
|
@@ -809,7 +868,7 @@ const ingestCommand = (name) => (argv) => {
|
|
|
809
868
|
allowPositionals: true
|
|
810
869
|
});
|
|
811
870
|
const input = readInput(positionals);
|
|
812
|
-
const store =
|
|
871
|
+
const store = openDb(values, 'write');
|
|
813
872
|
try {
|
|
814
873
|
const options = {
|
|
815
874
|
strict: values.strict === true,
|
|
@@ -854,39 +913,125 @@ export const addCommand = ingestCommand('add');
|
|
|
854
913
|
* identity.
|
|
855
914
|
*/
|
|
856
915
|
export const importCommand = ingestCommand('import');
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
allowPositionals: true
|
|
873
|
-
});
|
|
916
|
+
const queryOptions = {
|
|
917
|
+
db: { type: 'string' },
|
|
918
|
+
json: { type: 'boolean' },
|
|
919
|
+
all: { type: 'boolean' },
|
|
920
|
+
aliases: { type: 'boolean' },
|
|
921
|
+
'as-of': { type: 'string' },
|
|
922
|
+
at: { type: 'string' },
|
|
923
|
+
resolve: { type: 'boolean' },
|
|
924
|
+
sources: { type: 'boolean' },
|
|
925
|
+
limit: { type: 'string' },
|
|
926
|
+
cursor: { type: 'string' },
|
|
927
|
+
'no-prelude': { type: 'boolean' }
|
|
928
|
+
};
|
|
929
|
+
const parseQuery = (argv) => {
|
|
930
|
+
const { values, positionals } = parseArgs({ args: [...argv], options: queryOptions, allowPositionals: true });
|
|
874
931
|
if (positionals.length === 0) {
|
|
875
|
-
return fail('cave query: a pattern is required (spec §12.1)\n');
|
|
932
|
+
return { failure: fail('cave query: a pattern is required (spec §12.1)\n') };
|
|
876
933
|
}
|
|
877
|
-
|
|
878
|
-
|
|
934
|
+
if (values.sources === true && values.cursor !== undefined) {
|
|
935
|
+
return { failure: fail('cave query: --cursor does not apply to --sources — the overlay exists for one invocation and is answered whole\n') };
|
|
936
|
+
}
|
|
937
|
+
return { values, pattern: positionals.join('\n') };
|
|
938
|
+
};
|
|
939
|
+
/**
|
|
940
|
+
* `cave query --sources` — the store with its declared sources overlaid
|
|
941
|
+
* (spec §23.4). Asynchronous because the sources load first, URLs
|
|
942
|
+
* included, before the overlay appends inside a transaction that rolls
|
|
943
|
+
* back; the dispatcher routes `--sources` here. A text store followed its
|
|
944
|
+
* local sources on open, so only what assembly could not follow — URL
|
|
945
|
+
* sources and what they declare — is loaded. The overlay exists for this
|
|
946
|
+
* invocation alone, so the answer is whole: pages are followed inside the
|
|
947
|
+
* transaction and no cursor is handed out.
|
|
948
|
+
*/
|
|
949
|
+
export const querySourcesCommand = async (argv, runtime = {}) => {
|
|
950
|
+
const parsed = parseQuery(argv);
|
|
951
|
+
if ('failure' in parsed) {
|
|
952
|
+
return parsed.failure;
|
|
953
|
+
}
|
|
954
|
+
const { values, pattern } = parsed;
|
|
955
|
+
if (values.sources !== true) {
|
|
956
|
+
return queryCommand(argv);
|
|
957
|
+
}
|
|
958
|
+
const root = values.db ?? defaultDbPath();
|
|
959
|
+
// The overlay appends inside a transaction it rolls back: writable, but
|
|
960
|
+
// never creating or migrating (spec §13.7).
|
|
961
|
+
const store = openDb(values, 'scratch');
|
|
879
962
|
try {
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
963
|
+
for (let attempt = 0; attempt < Declared.overlayAttempts; attempt += 1) {
|
|
964
|
+
const discovered = await Declared.discovery(store, root, {
|
|
965
|
+
// The overlay applies every source it loads (force), so discovery
|
|
966
|
+
// simulates that; a text store skips what assembly already followed.
|
|
967
|
+
force: true,
|
|
968
|
+
...kindOf(root) === 'text' ? { skipFollowed: true } : {},
|
|
969
|
+
...runtime.fetchImpl === undefined ? {} : { fetchImpl: runtime.fetchImpl }
|
|
970
|
+
});
|
|
971
|
+
const page = withSources(store, discovered, () => wholeQuery(store, values, pattern));
|
|
972
|
+
if (page !== undefined) {
|
|
973
|
+
return renderQueryPage(store, values, pattern, () => page.value);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
throw Declared.staleOverlay();
|
|
977
|
+
}
|
|
978
|
+
catch (error) {
|
|
979
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
980
|
+
}
|
|
981
|
+
finally {
|
|
982
|
+
store.close();
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
export const queryCommand = (argv) => {
|
|
986
|
+
const parsed = parseQuery(argv);
|
|
987
|
+
if ('failure' in parsed) {
|
|
988
|
+
return parsed.failure;
|
|
989
|
+
}
|
|
990
|
+
const { values, pattern } = parsed;
|
|
991
|
+
if (values.sources === true) {
|
|
992
|
+
// Loading sources cannot be synchronous: the in-process entry for the
|
|
993
|
+
// overlay is `querySourcesCommand`, which the dispatcher uses.
|
|
994
|
+
return fail('cave query: --sources loads the declared sources first and runs asynchronously — use querySourcesCommand\n');
|
|
995
|
+
}
|
|
996
|
+
const store = openDb(values, 'read');
|
|
997
|
+
try {
|
|
998
|
+
return renderQueryPage(store, values, pattern, () => queryPage(store, values, pattern));
|
|
999
|
+
}
|
|
1000
|
+
catch (error) {
|
|
1001
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
1002
|
+
}
|
|
1003
|
+
finally {
|
|
1004
|
+
store.close();
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
/** Every match of the overlay: the pages are followed here, inside the transaction that holds it. */
|
|
1008
|
+
const wholeQuery = (store, values, pattern) => {
|
|
1009
|
+
const matches = [];
|
|
1010
|
+
let first;
|
|
1011
|
+
let cursor;
|
|
1012
|
+
for (;;) {
|
|
1013
|
+
const page = queryPage(store, { ...values, limit: '1000', ...cursor === undefined ? {} : { cursor } }, pattern);
|
|
1014
|
+
first ??= page;
|
|
1015
|
+
matches.push(...page.matches);
|
|
1016
|
+
if (page.next === undefined) {
|
|
1017
|
+
const { next: _next, ...rest } = first;
|
|
1018
|
+
return { ...rest, matches };
|
|
1019
|
+
}
|
|
1020
|
+
cursor = page.next;
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
const queryPage = (store, values, pattern) => caveQueryPage(store, pattern, {
|
|
1024
|
+
all: values.all === true,
|
|
1025
|
+
aliases: values.aliases === true,
|
|
1026
|
+
resolve: values.resolve === true,
|
|
1027
|
+
limit: values.limit === undefined ? defaultQueryLimit : Number(values.limit),
|
|
1028
|
+
...values.cursor === undefined ? {} : { cursor: values.cursor },
|
|
1029
|
+
...values['as-of'] === undefined ? {} : { asOf: values['as-of'] },
|
|
1030
|
+
...values.at === undefined ? {} : { at: values.at }
|
|
1031
|
+
});
|
|
1032
|
+
const renderQueryPage = (store, values, pattern, page) => {
|
|
1033
|
+
{
|
|
1034
|
+
const result = page();
|
|
890
1035
|
if (values.json === true) {
|
|
891
1036
|
return ok(`${JSON.stringify(result, undefined, 2)}\n`);
|
|
892
1037
|
}
|
|
@@ -916,12 +1061,6 @@ export const queryCommand = (argv) => {
|
|
|
916
1061
|
});
|
|
917
1062
|
return ok(`${lines.join('\n')}\n${result.next === undefined ? '' : `next: ${result.next}\n`}`);
|
|
918
1063
|
}
|
|
919
|
-
catch (error) {
|
|
920
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
921
|
-
}
|
|
922
|
-
finally {
|
|
923
|
-
store.close();
|
|
924
|
-
}
|
|
925
1064
|
};
|
|
926
1065
|
export const searchFormat = 'cave.search';
|
|
927
1066
|
export const searchVersion = 1;
|
|
@@ -950,7 +1089,7 @@ export const searchCommand = (argv) => {
|
|
|
950
1089
|
if (!Number.isInteger(limit) || limit < 1 || limit > maxQueryLimit) {
|
|
951
1090
|
return fail(`cave search: --limit must be an integer from 1 to ${maxQueryLimit}\n`);
|
|
952
1091
|
}
|
|
953
|
-
const store =
|
|
1092
|
+
const store = openDb(values, 'read');
|
|
954
1093
|
try {
|
|
955
1094
|
const raw = values.raw === true;
|
|
956
1095
|
const found = store.search(query, { raw, limit: limit + 1 });
|
|
@@ -1001,7 +1140,7 @@ export const resolveCommand = (argv) => {
|
|
|
1001
1140
|
allowPositionals: false
|
|
1002
1141
|
});
|
|
1003
1142
|
const percent = (value) => `${Math.round(value * 1000) / 10}%`;
|
|
1004
|
-
const store =
|
|
1143
|
+
const store = openDb(values, 'read');
|
|
1005
1144
|
try {
|
|
1006
1145
|
if (values.policy === true) {
|
|
1007
1146
|
const policy = store.resolutionPolicy();
|
|
@@ -1063,7 +1202,11 @@ export const deriveCommand = (argv) => {
|
|
|
1063
1202
|
if (maxPasses !== undefined && (!Number.isInteger(maxPasses) || maxPasses < 1)) {
|
|
1064
1203
|
return fail(`cave derive: --max-passes expects a positive integer, got ${JSON.stringify(values['max-passes'])}\n`);
|
|
1065
1204
|
}
|
|
1066
|
-
|
|
1205
|
+
// The intent follows the branch order below: listing reads, retracting
|
|
1206
|
+
// writes, a dry run appends inside a rolled-back transaction.
|
|
1207
|
+
const store = openDb(values, values.list === true ? 'read' :
|
|
1208
|
+
values.retract !== undefined ? 'write' :
|
|
1209
|
+
values['dry-run'] === true ? 'scratch' : 'write');
|
|
1067
1210
|
try {
|
|
1068
1211
|
if (values.list === true) {
|
|
1069
1212
|
const rules = listRules(store);
|
|
@@ -1217,7 +1360,13 @@ export const actCommand = (argv) => {
|
|
|
1217
1360
|
},
|
|
1218
1361
|
allowPositionals: true
|
|
1219
1362
|
});
|
|
1220
|
-
|
|
1363
|
+
// The intent follows the branch order below: declaring and retracting
|
|
1364
|
+
// write, listing reads, a dry-run execution appends inside a rolled-back
|
|
1365
|
+
// transaction.
|
|
1366
|
+
const store = openDb(values, values.declare === true ? 'write' :
|
|
1367
|
+
values.list === true ? 'read' :
|
|
1368
|
+
values.retract !== undefined ? 'write' :
|
|
1369
|
+
values['dry-run'] === true ? 'scratch' : 'write');
|
|
1221
1370
|
try {
|
|
1222
1371
|
if (values.declare === true) {
|
|
1223
1372
|
const declaration = declareActions(store, readInput(positionals));
|
|
@@ -1335,7 +1484,7 @@ export const checkCommand = (argv) => {
|
|
|
1335
1484
|
if (!Number.isFinite(staleDays) || staleDays < 0) {
|
|
1336
1485
|
return fail(`cave check: --stale expects a non-negative number of days, got ${JSON.stringify(values.stale)}\n`);
|
|
1337
1486
|
}
|
|
1338
|
-
const store =
|
|
1487
|
+
const store = openDb(values, 'read');
|
|
1339
1488
|
try {
|
|
1340
1489
|
const report = caveCheck(store, { staleDays });
|
|
1341
1490
|
const out = values.json === true ?
|
|
@@ -1379,7 +1528,15 @@ export const backupCommand = (argv) => {
|
|
|
1379
1528
|
if (values.out === undefined) {
|
|
1380
1529
|
return fail('cave backup: --out <file> is required\n');
|
|
1381
1530
|
}
|
|
1382
|
-
const
|
|
1531
|
+
const dbPath = values.db ?? defaultDbPath();
|
|
1532
|
+
// A text store is replayed into memory, so the backup API cannot see that
|
|
1533
|
+
// the destination aliases the source: refuse before the file could be
|
|
1534
|
+
// replaced by a SQLite snapshot of itself (a SQLite source is covered by
|
|
1535
|
+
// the API's own check).
|
|
1536
|
+
if (sameFile(dbPath, values.out)) {
|
|
1537
|
+
return fail(`cave backup: --out '${values.out}' is the source database — refusing to overwrite it\n`);
|
|
1538
|
+
}
|
|
1539
|
+
const store = openAt(dbPath, { intent: 'read', assemble: assembleSources });
|
|
1383
1540
|
try {
|
|
1384
1541
|
return ok(snapshotLine('created', backupStore(store, values.out, { force: values.force === true })));
|
|
1385
1542
|
}
|
|
@@ -1460,7 +1617,7 @@ export const suggestAliasCommand = async (argv, runtime = {}) => {
|
|
|
1460
1617
|
if (timeoutSeconds !== undefined && (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)) {
|
|
1461
1618
|
return fail(`cave suggest-alias: --timeout must be a positive number of seconds, got '${values.timeout}'\n`);
|
|
1462
1619
|
}
|
|
1463
|
-
const store =
|
|
1620
|
+
const store = openDb(values, values.write === true ? 'write' : 'read');
|
|
1464
1621
|
try {
|
|
1465
1622
|
let suggestions = suggestAliases(store, { minScore, ...limit === undefined ? {} : { limit } });
|
|
1466
1623
|
if (values.agent !== undefined && suggestions.length > 0) {
|
|
@@ -1516,7 +1673,7 @@ export const syncCommand = (argv) => {
|
|
|
1516
1673
|
return fail('cave sync: exactly one source is required — a CAVE store file, ;@-annotated text, or - for stdin (spec §28.5)\n');
|
|
1517
1674
|
}
|
|
1518
1675
|
const dbPath = values.db ?? defaultDbPath();
|
|
1519
|
-
const store =
|
|
1676
|
+
const store = openDb(values, values['dry-run'] === true ? 'scratch' : 'write');
|
|
1520
1677
|
try {
|
|
1521
1678
|
const options = {
|
|
1522
1679
|
into: values.into === undefined ? labelOf(dbPath) : sanitizeLabel(values.into),
|
|
@@ -1578,7 +1735,10 @@ export const reportCommand = (argv) => {
|
|
|
1578
1735
|
if (maximum === undefined) {
|
|
1579
1736
|
return fail(`cave report: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`);
|
|
1580
1737
|
}
|
|
1581
|
-
|
|
1738
|
+
if (values.out !== undefined && sameFile(values.db ?? defaultDbPath(), values.out)) {
|
|
1739
|
+
return fail(`cave report: --out '${values.out}' is the source database — refusing to overwrite it\n`);
|
|
1740
|
+
}
|
|
1741
|
+
const store = openDb(values, 'read');
|
|
1582
1742
|
try {
|
|
1583
1743
|
const rendered = caveReport(store, template, {
|
|
1584
1744
|
aliases: values.aliases === true,
|
|
@@ -1647,7 +1807,7 @@ export const exportCommand = (argv) => {
|
|
|
1647
1807
|
if (values.out !== undefined && sameFile(db, values.out)) {
|
|
1648
1808
|
return fail(`cave export: --out '${values.out}' is the source database — refusing to overwrite it\n`);
|
|
1649
1809
|
}
|
|
1650
|
-
const store =
|
|
1810
|
+
const store = openDb(values, 'read');
|
|
1651
1811
|
try {
|
|
1652
1812
|
const text = store.exportText({ current: values.current === true, tx: values.tx === true, maxSensitivity: maximum });
|
|
1653
1813
|
if (values.out === undefined) {
|
|
@@ -1687,7 +1847,7 @@ export const generateCommand = (argv) => {
|
|
|
1687
1847
|
if (values.out !== undefined && sameFile(db, values.out)) {
|
|
1688
1848
|
return fail(`cave generate: --out '${values.out}' is the source database — refusing to overwrite it\n`);
|
|
1689
1849
|
}
|
|
1690
|
-
const store =
|
|
1850
|
+
const store = openDb(values, 'read');
|
|
1691
1851
|
try {
|
|
1692
1852
|
const generated = generateClient(store, version === undefined ? {} : { version });
|
|
1693
1853
|
if (!generated.ok) {
|
|
@@ -1750,7 +1910,7 @@ export const reconstructCommand = async (argv, runtime = {}) => {
|
|
|
1750
1910
|
if (timeoutSeconds !== undefined && (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)) {
|
|
1751
1911
|
return fail(`cave reconstruct: --timeout must be a positive number of seconds, got '${values.timeout}'\n`);
|
|
1752
1912
|
}
|
|
1753
|
-
const store =
|
|
1913
|
+
const store = openDb(values, 'read');
|
|
1754
1914
|
try {
|
|
1755
1915
|
const graph = sqliteStore(store);
|
|
1756
1916
|
const result = values.agent === undefined ?
|
|
@@ -1825,6 +1985,19 @@ export const cave = (argv) => {
|
|
|
1825
1985
|
(rest.includes('--help') || rest.includes('-h'))) {
|
|
1826
1986
|
return ok(`${commandHelp[canonical]}\n`);
|
|
1827
1987
|
}
|
|
1988
|
+
try {
|
|
1989
|
+
return run(canonical, rest);
|
|
1990
|
+
}
|
|
1991
|
+
catch (error) {
|
|
1992
|
+
// A --db path that is not what the command needs (spec §13.7) is a
|
|
1993
|
+
// usage failure, reported the way dispatch reports thrown errors.
|
|
1994
|
+
if (error instanceof LocateError) {
|
|
1995
|
+
return fail(`cave ${canonical}: ${error.message}\n`);
|
|
1996
|
+
}
|
|
1997
|
+
throw error;
|
|
1998
|
+
}
|
|
1999
|
+
};
|
|
2000
|
+
const run = (canonical, rest) => {
|
|
1828
2001
|
switch (canonical) {
|
|
1829
2002
|
case 'parse':
|
|
1830
2003
|
return parseCommand(rest);
|
|
@@ -1871,7 +2044,7 @@ export const cave = (argv) => {
|
|
|
1871
2044
|
case '-h':
|
|
1872
2045
|
return ok(`${usage}\n`);
|
|
1873
2046
|
default:
|
|
1874
|
-
return fail(`cave: unknown command ${JSON.stringify(
|
|
2047
|
+
return fail(`cave: unknown command ${JSON.stringify(canonical)}\n\n${usage}\n`, 2);
|
|
1875
2048
|
}
|
|
1876
2049
|
};
|
|
1877
2050
|
//# sourceMappingURL=cli.js.map
|