@gmickel/gno 1.37.0 → 1.38.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/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v1.37.0.zip → gno-browser-clipper-v1.38.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.38.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +35 -15
- package/src/cli/commands/cleanup.ts +8 -2
- package/src/cli/commands/collection/clear-embeddings.ts +6 -1
- package/src/cli/commands/doctor-activation.ts +5 -1
- package/src/cli/commands/doctor.ts +72 -2
- package/src/cli/commands/embed.ts +227 -194
- package/src/cli/commands/index-cmd.ts +74 -50
- package/src/cli/commands/init.ts +5 -1
- package/src/cli/commands/profile-apply.ts +5 -1
- package/src/cli/commands/setup-activation.ts +2 -1
- package/src/cli/commands/setup.ts +2 -1
- package/src/cli/commands/shared.ts +5 -1
- package/src/cli/commands/status.ts +5 -1
- package/src/cli/commands/tags.ts +18 -3
- package/src/cli/commands/update.ts +34 -27
- package/src/cli/commands/vec.ts +13 -4
- package/src/cli/errors.ts +3 -2
- package/src/cli/program.ts +345 -194
- package/src/config/defaults.ts +2 -0
- package/src/config/index.ts +3 -0
- package/src/config/types.ts +32 -1
- package/src/core/file-lock.ts +16 -4
- package/src/core/write-lease.ts +354 -0
- package/src/embed/backlog.ts +9 -1
- package/src/embed/retry.ts +116 -3
- package/src/sdk/client.ts +3 -1
- package/src/sdk/embed.ts +8 -3
- package/src/sdk/types.ts +2 -0
- package/src/serve/embed-scheduler.ts +8 -0
- package/src/serve/resident-runtime.ts +5 -1
- package/src/serve/spa-production-build.ts +53 -7
- package/src/store/sqlite/adapter.ts +28 -4
- package/src/store/sqlite/scoped-index.ts +5 -1
- package/src/store/vector/sqlite-vec.ts +2 -1
- package/browser-extension/artifacts/gno-browser-clipper-v1.37.0.zip.sha256 +0 -1
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
59f8dc960d1910f7b9dec3c4fea286c46bfe63487a43212fbb8ba61a207753ae gno-browser-clipper-v1.38.0.zip
|
package/package.json
CHANGED
package/spec/cli.md
CHANGED
|
@@ -15,6 +15,7 @@ This document specifies the command-line interface for GNO, a local knowledge in
|
|
|
15
15
|
| 1 | VALIDATION | Validation or usage error (bad args, missing required params) |
|
|
16
16
|
| 2 | RUNTIME | Runtime failure (IO, DB, conversion, model, network) |
|
|
17
17
|
| 3 | NOT_RUNNING | `--status`/`--stop` found no live matching process |
|
|
18
|
+
| 4 | BUSY | Write-lease contention on `index` / `update` / `embed` |
|
|
18
19
|
|
|
19
20
|
### Global Flags
|
|
20
21
|
|
|
@@ -957,7 +958,7 @@ Sync files from disk into the index (ingestion without embedding).
|
|
|
957
958
|
**Synopsis:**
|
|
958
959
|
|
|
959
960
|
```bash
|
|
960
|
-
gno update [--git-pull] [--json]
|
|
961
|
+
gno update [--git-pull] [--json] [--lock-wait <duration>] [--no-wait]
|
|
961
962
|
```
|
|
962
963
|
|
|
963
964
|
**Options:**
|
|
@@ -965,6 +966,10 @@ gno update [--git-pull] [--json]
|
|
|
965
966
|
|--------|------|-------------|
|
|
966
967
|
| `--git-pull` | boolean | Run `git pull` in git repositories before scanning |
|
|
967
968
|
| `--json` | boolean | Emit the complete deterministic sync result on stdout |
|
|
969
|
+
| `--lock-wait <duration>` | duration | How long to wait for the index write lease (default: `120s`). Accepts `120`, `120s`, or `2m`. |
|
|
970
|
+
| `--no-wait` | boolean | Do not wait; fail immediately if another writer holds the lease |
|
|
971
|
+
|
|
972
|
+
**Concurrency:** One writer at a time on the shared index database. `update` waits up to `--lock-wait` for the lease (the same `.mcp-write.lock` MCP write tools use); `index`, `embed`, `cleanup`, `vec sync`, `vec rebuild`, `collection clear-embeddings`, `tags add`, and `tags rm` follow the same contract; `capture` takes the same lock internally, and single-row writes such as `collection policy set` are absorbed by `busy_timeout`. `--no-wait` opts out. Reads (`search`, `query`, `get`) never take the lease. External serialising wrappers are no longer required for CLI-vs-CLI and CLI-vs-MCP overlap. Residual window: a resident (`gno serve`/`gno daemon`) watch or embed flush writes without the lease; those short transactions are absorbed by the raised `busy_timeout` and the SQLITE_BUSY retry, and a deferred chunk is reported as contention, never as an embedding failure.
|
|
968
973
|
|
|
969
974
|
**Behavior:**
|
|
970
975
|
|
|
@@ -986,6 +991,7 @@ record failure was reported. Human progress and diagnostics remain on stderr.
|
|
|
986
991
|
|
|
987
992
|
- 0: Success (conversion warnings do not affect exit code)
|
|
988
993
|
- 2: DB failure or critical IO error
|
|
994
|
+
- 4: Write lease busy (contention after `--lock-wait`, or immediately with `--no-wait`)
|
|
989
995
|
|
|
990
996
|
---
|
|
991
997
|
|
|
@@ -996,7 +1002,7 @@ Build or update the index end-to-end (update + embed).
|
|
|
996
1002
|
**Synopsis:**
|
|
997
1003
|
|
|
998
1004
|
```bash
|
|
999
|
-
gno index [collection] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes]
|
|
1005
|
+
gno index [collection] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes] [--lock-wait <duration>] [--no-wait]
|
|
1000
1006
|
```
|
|
1001
1007
|
|
|
1002
1008
|
**Options:**
|
|
@@ -1008,24 +1014,29 @@ gno index [collection] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes
|
|
|
1008
1014
|
| `--git-pull` | boolean | Run `git pull` in git repositories |
|
|
1009
1015
|
| `--json` | boolean | Emit the complete deterministic sync and embedding result on stdout |
|
|
1010
1016
|
| `--yes` | boolean | Accept defaults, no prompts |
|
|
1017
|
+
| `--lock-wait <duration>` | duration | How long to wait for the index write lease (default: `120s`). Accepts `120`, `120s`, or `2m`. |
|
|
1018
|
+
| `--no-wait` | boolean | Do not wait; fail immediately if another writer holds the lease |
|
|
1011
1019
|
|
|
1012
1020
|
**Behavior:**
|
|
1013
1021
|
|
|
1014
1022
|
- Runs `update` then `embed` by default
|
|
1015
1023
|
- With `--no-embed`, runs `update` only
|
|
1024
|
+
- Waits by default for the shared write lease; `--no-wait` fails immediately with exit 4
|
|
1016
1025
|
|
|
1017
1026
|
**JSON output:** Emits `{ syncResult, embedSkipped, embedResult? }`.
|
|
1018
1027
|
`syncResult.collections[].files[].recordImport`, when present, conforms to
|
|
1019
1028
|
[`record-import@1.0`](./output-schemas/record-import.schema.json) with the same
|
|
1020
1029
|
deterministic ordering, bounds, truncation disclosure, and partial-snapshot
|
|
1021
1030
|
warnings as `gno update --json`. Human progress and diagnostics remain on
|
|
1022
|
-
stderr.
|
|
1031
|
+
stderr. On lease timeout, stdout is one object `{ success: false, error, contention }`
|
|
1032
|
+
and the process exits 4.
|
|
1023
1033
|
|
|
1024
1034
|
**Exit Codes:**
|
|
1025
1035
|
|
|
1026
1036
|
- 0: Success
|
|
1027
|
-
- 1: Invalid collection name
|
|
1037
|
+
- 1: Invalid collection name or invalid `--lock-wait`
|
|
1028
1038
|
- 2: DB or model failure
|
|
1039
|
+
- 4: Write lease busy (contention, not corruption)
|
|
1029
1040
|
|
|
1030
1041
|
**Examples:**
|
|
1031
1042
|
|
|
@@ -1060,25 +1071,30 @@ memory pressure prevents creating the full pool.
|
|
|
1060
1071
|
**Synopsis:**
|
|
1061
1072
|
|
|
1062
1073
|
```bash
|
|
1063
|
-
gno embed [--force] [--model <uri>] [--batch-size <n>] [--dry-run] [--yes] [--json]
|
|
1074
|
+
gno embed [--force] [--model <uri>] [--batch-size <n>] [--dry-run] [--yes] [--json] [--lock-wait <duration>] [--no-wait]
|
|
1064
1075
|
```
|
|
1065
1076
|
|
|
1066
1077
|
**Options:**
|
|
1067
1078
|
|
|
1068
|
-
| Option
|
|
1069
|
-
|
|
|
1070
|
-
| `--force`
|
|
1071
|
-
| `--model`
|
|
1072
|
-
| `--batch-size`
|
|
1073
|
-
| `--dry-run`
|
|
1074
|
-
| `--yes`, `-y`
|
|
1075
|
-
| `--json`
|
|
1079
|
+
| Option | Type | Default | Description |
|
|
1080
|
+
| ------------------------ | -------- | ------- | --------------------------------------------------------------------------- |
|
|
1081
|
+
| `--force` | boolean | false | Re-embed all chunks (ignore existing vectors) |
|
|
1082
|
+
| `--model` | string | config | Override embedding model URI |
|
|
1083
|
+
| `--batch-size` | integer | 32 | Chunks per batch |
|
|
1084
|
+
| `--dry-run` | boolean | false | Show what would be embedded without doing it |
|
|
1085
|
+
| `--yes`, `-y` | boolean | false | Skip confirmation prompts |
|
|
1086
|
+
| `--json` | boolean | false | Output result as JSON |
|
|
1087
|
+
| `--lock-wait <duration>` | duration | `120s` | How long to wait for the index write lease. Accepts `120`, `120s`, or `2m`. |
|
|
1088
|
+
| `--no-wait` | boolean | false | Do not wait; fail immediately if another writer holds the lease |
|
|
1089
|
+
|
|
1090
|
+
Waits by default for the same write lease as `index` / `update` / MCP writers. `--no-wait` opts out.
|
|
1076
1091
|
|
|
1077
1092
|
**Exit Codes:**
|
|
1078
1093
|
|
|
1079
1094
|
- 0: Success
|
|
1080
|
-
- 1: User cancelled
|
|
1095
|
+
- 1: User cancelled or invalid `--lock-wait`
|
|
1081
1096
|
- 2: Model not available or embedding failure
|
|
1097
|
+
- 4: Write lease busy (contention, not corruption)
|
|
1082
1098
|
|
|
1083
1099
|
**JSON Output:**
|
|
1084
1100
|
|
|
@@ -3615,7 +3631,11 @@ Errors are written to stderr. With `--json` flag, errors are also returned as:
|
|
|
3615
3631
|
}
|
|
3616
3632
|
```
|
|
3617
3633
|
|
|
3618
|
-
Error codes match exit codes: `VALIDATION` (exit 1), `RUNTIME` (exit 2), `NOT_RUNNING` (exit 3).
|
|
3634
|
+
Error codes match exit codes: `VALIDATION` (exit 1), `RUNTIME` (exit 2), `NOT_RUNNING` (exit 3), `BUSY` (exit 4).
|
|
3635
|
+
|
|
3636
|
+
Write-lease contention on `index` / `update` / `embed` does not use the generic envelope. Text mode writes the dedicated "index is busy" message to stderr; `--json` writes `{ success: false, error, contention }` to stdout. Both exit 4. `gno audit` also uses exit 4 for findings.
|
|
3637
|
+
|
|
3638
|
+
**`NOT_RUNNING` is not an error envelope.**
|
|
3619
3639
|
|
|
3620
3640
|
**`NOT_RUNNING` is not an error envelope.** `gno serve|daemon --status --json` returns a `process-status`-shaped payload on stdout with exit 3 when no live matching process is found (it reports observable state, not failure). `--stop` exits 3 silently when there is nothing to stop and does not accept `--json`. The error envelope above is reserved for `VALIDATION` and `RUNTIME` failures where the command could not produce its structured output at all.
|
|
3621
3641
|
|
|
@@ -17,6 +17,8 @@ import { SqliteAdapter } from "../../store/sqlite/adapter";
|
|
|
17
17
|
export interface CleanupOptions {
|
|
18
18
|
/** Override config path */
|
|
19
19
|
configPath?: string;
|
|
20
|
+
/** Index name */
|
|
21
|
+
indexName?: string;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -47,9 +49,13 @@ export async function cleanup(
|
|
|
47
49
|
|
|
48
50
|
// Open database
|
|
49
51
|
const store = new SqliteAdapter();
|
|
50
|
-
const dbPath = getIndexDbPath();
|
|
52
|
+
const dbPath = getIndexDbPath(options.indexName);
|
|
51
53
|
|
|
52
|
-
const openResult = await store.open(
|
|
54
|
+
const openResult = await store.open(
|
|
55
|
+
dbPath,
|
|
56
|
+
config.ftsTokenizer,
|
|
57
|
+
config.busyTimeoutMs
|
|
58
|
+
);
|
|
53
59
|
if (!openResult.ok) {
|
|
54
60
|
return { success: false, error: openResult.error.message };
|
|
55
61
|
}
|
|
@@ -10,6 +10,7 @@ import { CliError } from "../../errors";
|
|
|
10
10
|
|
|
11
11
|
interface ClearEmbeddingsOptions {
|
|
12
12
|
all?: boolean;
|
|
13
|
+
indexName?: string;
|
|
13
14
|
json?: boolean;
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -39,7 +40,11 @@ export async function collectionClearEmbeddings(
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
const store = new SqliteAdapter();
|
|
42
|
-
const openResult = await store.open(
|
|
43
|
+
const openResult = await store.open(
|
|
44
|
+
getIndexDbPath(options.indexName),
|
|
45
|
+
config.ftsTokenizer,
|
|
46
|
+
config.busyTimeoutMs
|
|
47
|
+
);
|
|
43
48
|
if (!openResult.ok) {
|
|
44
49
|
throw new CliError("RUNTIME", openResult.error.message);
|
|
45
50
|
}
|
|
@@ -43,7 +43,11 @@ export async function buildDoctorActivation(
|
|
|
43
43
|
|
|
44
44
|
const store = new SqliteAdapter();
|
|
45
45
|
store.setConfigPath(options.configPath ?? "");
|
|
46
|
-
const opened = await store.open(
|
|
46
|
+
const opened = await store.open(
|
|
47
|
+
dbPath,
|
|
48
|
+
config.ftsTokenizer,
|
|
49
|
+
config.busyTimeoutMs
|
|
50
|
+
);
|
|
47
51
|
if (!opened.ok) {
|
|
48
52
|
return unavailableActivation(config);
|
|
49
53
|
}
|
|
@@ -14,7 +14,12 @@ import type { Config } from "../../config/types";
|
|
|
14
14
|
import type { ActivationStatus } from "../../core/activation-status";
|
|
15
15
|
|
|
16
16
|
import { getIndexDbPath, getModelsCachePath } from "../../app/constants";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
DEFAULT_BUSY_TIMEOUT_MS,
|
|
19
|
+
getConfigPaths,
|
|
20
|
+
isInitialized,
|
|
21
|
+
loadConfig,
|
|
22
|
+
} from "../../config";
|
|
18
23
|
import { isConnectorActivationComplete } from "../../core/activation-connector-health";
|
|
19
24
|
import { getCodeChunkingStatus } from "../../ingestion/chunker";
|
|
20
25
|
import { ModelCache } from "../../llm/cache";
|
|
@@ -140,6 +145,64 @@ async function checkDatabase(indexName?: string): Promise<DoctorCheck> {
|
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Report the live SQLite busy_timeout (PRAGMA), not the config echo.
|
|
150
|
+
*/
|
|
151
|
+
async function checkBusyTimeout(
|
|
152
|
+
config: Config,
|
|
153
|
+
indexName?: string
|
|
154
|
+
): Promise<DoctorCheck> {
|
|
155
|
+
const dbPath = getIndexDbPath(indexName);
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
await stat(dbPath);
|
|
159
|
+
} catch {
|
|
160
|
+
return {
|
|
161
|
+
name: "busy-timeout",
|
|
162
|
+
status: "warn",
|
|
163
|
+
message: "Database not found. Run: gno init",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const store = new SqliteAdapter();
|
|
168
|
+
const paths = getConfigPaths();
|
|
169
|
+
store.setConfigPath(paths.configFile);
|
|
170
|
+
|
|
171
|
+
const openResult = await store.open(
|
|
172
|
+
dbPath,
|
|
173
|
+
config.ftsTokenizer,
|
|
174
|
+
config.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS
|
|
175
|
+
);
|
|
176
|
+
if (!openResult.ok) {
|
|
177
|
+
return {
|
|
178
|
+
name: "busy-timeout",
|
|
179
|
+
status: "warn",
|
|
180
|
+
message: `busy_timeout unavailable: ${openResult.error.message}`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const timeout = store
|
|
186
|
+
.getRawDb()
|
|
187
|
+
.query<{ timeout: number }, []>("PRAGMA busy_timeout")
|
|
188
|
+
.get()?.timeout;
|
|
189
|
+
if (timeout === undefined) {
|
|
190
|
+
return {
|
|
191
|
+
name: "busy-timeout",
|
|
192
|
+
status: "warn",
|
|
193
|
+
message: "PRAGMA busy_timeout returned no value",
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
name: "busy-timeout",
|
|
198
|
+
status: "ok",
|
|
199
|
+
message: `busy_timeout ${timeout}ms`,
|
|
200
|
+
};
|
|
201
|
+
} finally {
|
|
202
|
+
await store.close();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
143
206
|
async function checkModels(config: Config): Promise<DoctorCheck[]> {
|
|
144
207
|
const checks: DoctorCheck[] = [];
|
|
145
208
|
const cache = new ModelCache(getModelsCachePath());
|
|
@@ -209,7 +272,11 @@ async function checkEmbeddingFingerprints(
|
|
|
209
272
|
const paths = getConfigPaths();
|
|
210
273
|
store.setConfigPath(paths.configFile);
|
|
211
274
|
|
|
212
|
-
const openResult = await store.open(
|
|
275
|
+
const openResult = await store.open(
|
|
276
|
+
dbPath,
|
|
277
|
+
config.ftsTokenizer,
|
|
278
|
+
config.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS
|
|
279
|
+
);
|
|
213
280
|
if (!openResult.ok) {
|
|
214
281
|
return {
|
|
215
282
|
name: "embedding-fingerprint",
|
|
@@ -527,6 +594,9 @@ export async function doctor(
|
|
|
527
594
|
const sqliteChecks = await checkSqliteExtensions();
|
|
528
595
|
checks.push(...sqliteChecks);
|
|
529
596
|
|
|
597
|
+
// Live busy_timeout from the open index (not the config echo)
|
|
598
|
+
checks.push(await checkBusyTimeout(config, options.indexName));
|
|
599
|
+
|
|
530
600
|
// Code chunking capability
|
|
531
601
|
checks.push(checkCodeChunking());
|
|
532
602
|
|