@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
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* @module src/cli/commands/indexCmd
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
type CliWriteLeaseOptions,
|
|
10
|
+
type WriteLeaseContention,
|
|
11
|
+
withCliWriteLease,
|
|
12
|
+
} from "../../core/write-lease";
|
|
8
13
|
import {
|
|
9
14
|
defaultSyncService,
|
|
10
15
|
type SyncResult,
|
|
@@ -15,7 +20,7 @@ import { formatSyncResultLines, initStore } from "./shared";
|
|
|
15
20
|
/**
|
|
16
21
|
* Options for index command.
|
|
17
22
|
*/
|
|
18
|
-
export interface IndexOptions {
|
|
23
|
+
export interface IndexOptions extends CliWriteLeaseOptions {
|
|
19
24
|
/** Override config path */
|
|
20
25
|
configPath?: string;
|
|
21
26
|
/** Index name */
|
|
@@ -44,66 +49,80 @@ export type IndexResult =
|
|
|
44
49
|
success: true;
|
|
45
50
|
syncResult: SyncResult;
|
|
46
51
|
embedSkipped: boolean;
|
|
47
|
-
embedResult?: {
|
|
52
|
+
embedResult?: {
|
|
53
|
+
embedded: number;
|
|
54
|
+
errors: number;
|
|
55
|
+
contentionErrors: number;
|
|
56
|
+
duration: number;
|
|
57
|
+
};
|
|
48
58
|
}
|
|
49
|
-
| { success: false; error: string };
|
|
59
|
+
| { success: false; error: string; contention?: WriteLeaseContention };
|
|
50
60
|
|
|
51
61
|
/**
|
|
52
62
|
* Execute gno index command.
|
|
53
63
|
*/
|
|
54
64
|
export async function index(options: IndexOptions = {}): Promise<IndexResult> {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
return await withCliWriteLease(options, async () => {
|
|
66
|
+
const initResult = await initStore({
|
|
67
|
+
configPath: options.configPath,
|
|
68
|
+
indexName: options.indexName,
|
|
69
|
+
collection: options.collection,
|
|
70
|
+
});
|
|
71
|
+
if (!initResult.ok) {
|
|
72
|
+
return { success: false, error: initResult.error };
|
|
73
|
+
}
|
|
63
74
|
|
|
64
|
-
|
|
75
|
+
const { store, collections, config } = initResult;
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
try {
|
|
78
|
+
// Run sync service (update phase)
|
|
79
|
+
const syncResult = await defaultSyncService.syncAll(
|
|
80
|
+
collections,
|
|
81
|
+
store,
|
|
82
|
+
withContentTypeRules(
|
|
83
|
+
{
|
|
84
|
+
gitPull: options.gitPull,
|
|
85
|
+
runUpdateCmd: true,
|
|
86
|
+
},
|
|
87
|
+
config
|
|
88
|
+
)
|
|
89
|
+
);
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
// Embedding phase
|
|
92
|
+
const embedSkipped = options.noEmbed ?? false;
|
|
93
|
+
let embedResult:
|
|
94
|
+
| {
|
|
95
|
+
embedded: number;
|
|
96
|
+
errors: number;
|
|
97
|
+
contentionErrors: number;
|
|
98
|
+
duration: number;
|
|
99
|
+
}
|
|
100
|
+
| undefined;
|
|
85
101
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
if (!embedSkipped) {
|
|
103
|
+
const { embed } = await import("./embed");
|
|
104
|
+
const result = await embed({
|
|
105
|
+
configPath: options.configPath,
|
|
106
|
+
indexName: options.indexName,
|
|
107
|
+
collection: options.collection,
|
|
108
|
+
verbose: options.verbose,
|
|
109
|
+
skipWriteLease: true,
|
|
110
|
+
});
|
|
111
|
+
if (result.success) {
|
|
112
|
+
embedResult = {
|
|
113
|
+
embedded: result.embedded,
|
|
114
|
+
errors: result.errors,
|
|
115
|
+
contentionErrors: result.contentionErrors,
|
|
116
|
+
duration: result.duration,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
100
119
|
}
|
|
101
|
-
}
|
|
102
120
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
return { success: true, syncResult, embedSkipped, embedResult };
|
|
122
|
+
} finally {
|
|
123
|
+
await store.close();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
107
126
|
}
|
|
108
127
|
|
|
109
128
|
/**
|
|
@@ -147,13 +166,18 @@ export function formatIndex(
|
|
|
147
166
|
lines.push("Embedding skipped (--no-embed)");
|
|
148
167
|
} else if (result.embedResult) {
|
|
149
168
|
lines.push("");
|
|
150
|
-
const { embedded, errors, duration } = result.embedResult;
|
|
169
|
+
const { embedded, errors, contentionErrors, duration } = result.embedResult;
|
|
151
170
|
lines.push(
|
|
152
171
|
`Embedded ${embedded.toLocaleString()} chunks in ${formatDuration(duration)}`
|
|
153
172
|
);
|
|
154
173
|
if (errors > 0) {
|
|
155
174
|
lines.push(`${errors.toLocaleString()} chunks failed to embed.`);
|
|
156
175
|
}
|
|
176
|
+
if (contentionErrors > 0) {
|
|
177
|
+
lines.push(
|
|
178
|
+
`${contentionErrors.toLocaleString()} chunks deferred by index contention (SQLITE_BUSY) — not embedding failures. Rerun \`gno embed\` when the other writer finishes.`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
157
181
|
}
|
|
158
182
|
|
|
159
183
|
return lines.join("\n");
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -141,7 +141,11 @@ export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
const store = new SqliteAdapter();
|
|
144
|
-
const opened = await store.open(
|
|
144
|
+
const opened = await store.open(
|
|
145
|
+
dbPath,
|
|
146
|
+
mutation.config.ftsTokenizer,
|
|
147
|
+
mutation.config.busyTimeoutMs
|
|
148
|
+
);
|
|
145
149
|
if (!opened.ok) {
|
|
146
150
|
return {
|
|
147
151
|
success: false,
|
|
@@ -218,7 +218,11 @@ export async function runProjectProfileApplyCommand(
|
|
|
218
218
|
try {
|
|
219
219
|
await mkdir(dataDir, { recursive: true });
|
|
220
220
|
store.setConfigPath(configPath);
|
|
221
|
-
const opened = await store.open(
|
|
221
|
+
const opened = await store.open(
|
|
222
|
+
indexPath,
|
|
223
|
+
startingConfig.ftsTokenizer,
|
|
224
|
+
startingConfig.busyTimeoutMs
|
|
225
|
+
);
|
|
222
226
|
if (!opened.ok) {
|
|
223
227
|
return {
|
|
224
228
|
result: failedApplyResult("failed", discovery.summary, [
|
|
@@ -409,7 +409,8 @@ export async function setupWithActivation(
|
|
|
409
409
|
store.setConfigPath(configPath);
|
|
410
410
|
const opened = await store.open(
|
|
411
411
|
getIndexDbPath(indexName),
|
|
412
|
-
configResult.value.ftsTokenizer
|
|
412
|
+
configResult.value.ftsTokenizer,
|
|
413
|
+
configResult.value.busyTimeoutMs
|
|
413
414
|
);
|
|
414
415
|
if (!opened.ok) {
|
|
415
416
|
return withProfileResult(
|
|
@@ -259,7 +259,8 @@ async function executeSetup(
|
|
|
259
259
|
store.setConfigPath(configPath);
|
|
260
260
|
const opened = await store.open(
|
|
261
261
|
getIndexDbPath(indexName),
|
|
262
|
-
configResult.value.ftsTokenizer
|
|
262
|
+
configResult.value.ftsTokenizer,
|
|
263
|
+
configResult.value.busyTimeoutMs
|
|
263
264
|
);
|
|
264
265
|
if (!opened.ok) {
|
|
265
266
|
await store.close();
|
|
@@ -105,7 +105,11 @@ export async function initStore(
|
|
|
105
105
|
// Set configPath for status output
|
|
106
106
|
store.setConfigPath(actualConfigPath);
|
|
107
107
|
|
|
108
|
-
const openResult = await store.open(
|
|
108
|
+
const openResult = await store.open(
|
|
109
|
+
dbPath,
|
|
110
|
+
config.ftsTokenizer,
|
|
111
|
+
config.busyTimeoutMs
|
|
112
|
+
);
|
|
109
113
|
if (!openResult.ok) {
|
|
110
114
|
return { ok: false, error: openResult.error.message };
|
|
111
115
|
}
|
|
@@ -247,7 +247,11 @@ export async function status(
|
|
|
247
247
|
// Set configPath for status output
|
|
248
248
|
store.setConfigPath(options.configPath ?? paths.configFile);
|
|
249
249
|
|
|
250
|
-
const openResult = await store.open(
|
|
250
|
+
const openResult = await store.open(
|
|
251
|
+
dbPath,
|
|
252
|
+
config.ftsTokenizer,
|
|
253
|
+
config.busyTimeoutMs
|
|
254
|
+
);
|
|
251
255
|
if (!openResult.ok) {
|
|
252
256
|
return { success: false, error: openResult.error.message };
|
|
253
257
|
}
|
package/src/cli/commands/tags.ts
CHANGED
|
@@ -18,6 +18,8 @@ import { initStore } from "./shared";
|
|
|
18
18
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
19
|
|
|
20
20
|
export interface TagsListOptions {
|
|
21
|
+
/** Index name */
|
|
22
|
+
indexName?: string;
|
|
21
23
|
/** Override config path */
|
|
22
24
|
configPath?: string;
|
|
23
25
|
/** Filter by collection */
|
|
@@ -46,6 +48,8 @@ export interface TagsListResponse {
|
|
|
46
48
|
export interface TagsAddOptions {
|
|
47
49
|
/** Override config path */
|
|
48
50
|
configPath?: string;
|
|
51
|
+
/** Index name */
|
|
52
|
+
indexName?: string;
|
|
49
53
|
/** JSON output */
|
|
50
54
|
json?: boolean;
|
|
51
55
|
}
|
|
@@ -60,6 +64,8 @@ export type TagsAddResult =
|
|
|
60
64
|
export interface TagsRmOptions {
|
|
61
65
|
/** Override config path */
|
|
62
66
|
configPath?: string;
|
|
67
|
+
/** Index name */
|
|
68
|
+
indexName?: string;
|
|
63
69
|
/** JSON output */
|
|
64
70
|
json?: boolean;
|
|
65
71
|
}
|
|
@@ -339,7 +345,10 @@ function removeTagFromFrontmatter(content: string, tag: string): string {
|
|
|
339
345
|
export async function tagsList(
|
|
340
346
|
options: TagsListOptions = {}
|
|
341
347
|
): Promise<TagsListResult> {
|
|
342
|
-
const initResult = await initStore({
|
|
348
|
+
const initResult = await initStore({
|
|
349
|
+
configPath: options.configPath,
|
|
350
|
+
indexName: options.indexName,
|
|
351
|
+
});
|
|
343
352
|
if (!initResult.ok) {
|
|
344
353
|
return { success: false, error: initResult.error };
|
|
345
354
|
}
|
|
@@ -394,7 +403,10 @@ export async function tagsAdd(
|
|
|
394
403
|
};
|
|
395
404
|
}
|
|
396
405
|
|
|
397
|
-
const initResult = await initStore({
|
|
406
|
+
const initResult = await initStore({
|
|
407
|
+
configPath: options.configPath,
|
|
408
|
+
indexName: options.indexName,
|
|
409
|
+
});
|
|
398
410
|
if (!initResult.ok) {
|
|
399
411
|
return { success: false, error: initResult.error };
|
|
400
412
|
}
|
|
@@ -495,7 +507,10 @@ export async function tagsRm(
|
|
|
495
507
|
): Promise<TagsRmResult> {
|
|
496
508
|
const normalized = normalizeTag(tag);
|
|
497
509
|
|
|
498
|
-
const initResult = await initStore({
|
|
510
|
+
const initResult = await initStore({
|
|
511
|
+
configPath: options.configPath,
|
|
512
|
+
indexName: options.indexName,
|
|
513
|
+
});
|
|
499
514
|
if (!initResult.ok) {
|
|
500
515
|
return { success: false, error: initResult.error };
|
|
501
516
|
}
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* @module src/cli/commands/update
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
type CliWriteLeaseOptions,
|
|
10
|
+
type WriteLeaseContention,
|
|
11
|
+
withCliWriteLease,
|
|
12
|
+
} from "../../core/write-lease";
|
|
8
13
|
import {
|
|
9
14
|
defaultSyncService,
|
|
10
15
|
type SyncResult,
|
|
@@ -15,7 +20,7 @@ import { formatSyncResultLines, initStore } from "./shared";
|
|
|
15
20
|
/**
|
|
16
21
|
* Options for update command.
|
|
17
22
|
*/
|
|
18
|
-
export interface UpdateOptions {
|
|
23
|
+
export interface UpdateOptions extends CliWriteLeaseOptions {
|
|
19
24
|
/** Override config path */
|
|
20
25
|
configPath?: string;
|
|
21
26
|
/** Index name */
|
|
@@ -33,7 +38,7 @@ export interface UpdateOptions {
|
|
|
33
38
|
*/
|
|
34
39
|
export type UpdateResult =
|
|
35
40
|
| { success: true; result: SyncResult }
|
|
36
|
-
| { success: false; error: string };
|
|
41
|
+
| { success: false; error: string; contention?: WriteLeaseContention };
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
44
|
* Execute gno update command.
|
|
@@ -41,34 +46,36 @@ export type UpdateResult =
|
|
|
41
46
|
export async function update(
|
|
42
47
|
options: UpdateOptions = {}
|
|
43
48
|
): Promise<UpdateResult> {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
return await withCliWriteLease(options, async () => {
|
|
50
|
+
const initResult = await initStore({
|
|
51
|
+
configPath: options.configPath,
|
|
52
|
+
indexName: options.indexName,
|
|
53
|
+
});
|
|
54
|
+
if (!initResult.ok) {
|
|
55
|
+
return { success: false, error: initResult.error };
|
|
56
|
+
}
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
const { store, collections, config } = initResult;
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
try {
|
|
61
|
+
// Run sync service
|
|
62
|
+
const result = await defaultSyncService.syncAll(
|
|
63
|
+
collections,
|
|
64
|
+
store,
|
|
65
|
+
withContentTypeRules(
|
|
66
|
+
{
|
|
67
|
+
gitPull: options.gitPull,
|
|
68
|
+
runUpdateCmd: true,
|
|
69
|
+
},
|
|
70
|
+
config
|
|
71
|
+
)
|
|
72
|
+
);
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
return { success: true, result };
|
|
75
|
+
} finally {
|
|
76
|
+
await store.close();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/**
|
package/src/cli/commands/vec.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
|
|
21
21
|
export interface VecOptions {
|
|
22
22
|
configPath?: string;
|
|
23
|
+
indexName?: string;
|
|
23
24
|
json?: boolean;
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -91,11 +92,15 @@ export async function vecSync(
|
|
|
91
92
|
const modelUri = preset.embed;
|
|
92
93
|
|
|
93
94
|
const store = new SqliteAdapter();
|
|
94
|
-
const dbPath = getIndexDbPath();
|
|
95
|
+
const dbPath = getIndexDbPath(options.indexName);
|
|
95
96
|
const paths = getConfigPaths();
|
|
96
97
|
store.setConfigPath(paths.configFile);
|
|
97
98
|
|
|
98
|
-
const openResult = await store.open(
|
|
99
|
+
const openResult = await store.open(
|
|
100
|
+
dbPath,
|
|
101
|
+
config.ftsTokenizer,
|
|
102
|
+
config.busyTimeoutMs
|
|
103
|
+
);
|
|
99
104
|
if (!openResult.ok) {
|
|
100
105
|
return { success: false, error: openResult.error.message };
|
|
101
106
|
}
|
|
@@ -165,11 +170,15 @@ export async function vecRebuild(
|
|
|
165
170
|
const modelUri = preset.embed;
|
|
166
171
|
|
|
167
172
|
const store = new SqliteAdapter();
|
|
168
|
-
const dbPath = getIndexDbPath();
|
|
173
|
+
const dbPath = getIndexDbPath(options.indexName);
|
|
169
174
|
const paths = getConfigPaths();
|
|
170
175
|
store.setConfigPath(paths.configFile);
|
|
171
176
|
|
|
172
|
-
const openResult = await store.open(
|
|
177
|
+
const openResult = await store.open(
|
|
178
|
+
dbPath,
|
|
179
|
+
config.ftsTokenizer,
|
|
180
|
+
config.busyTimeoutMs
|
|
181
|
+
);
|
|
173
182
|
if (!openResult.ok) {
|
|
174
183
|
return { success: false, error: openResult.error.message };
|
|
175
184
|
}
|
package/src/cli/errors.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CLI error model aligned to spec.
|
|
3
|
-
* Exit codes: 0=success, 1=validation, 2=runtime
|
|
3
|
+
* Exit codes: 0=success, 1=validation, 2=runtime, 3=not-running, 4=busy/audit-findings, 5=audit-partial
|
|
4
4
|
*
|
|
5
5
|
* @module src/cli/errors
|
|
6
6
|
*/
|
|
@@ -13,6 +13,7 @@ export type CliErrorCode =
|
|
|
13
13
|
| "VALIDATION"
|
|
14
14
|
| "RUNTIME"
|
|
15
15
|
| "NOT_RUNNING"
|
|
16
|
+
| "BUSY"
|
|
16
17
|
| "AUDIT_FINDINGS"
|
|
17
18
|
| "AUDIT_PARTIAL";
|
|
18
19
|
|
|
@@ -74,7 +75,7 @@ export function exitCodeFor(err: CliError): 1 | 2 | 3 | 4 | 5 {
|
|
|
74
75
|
if (err.code === "NOT_RUNNING") {
|
|
75
76
|
return 3;
|
|
76
77
|
}
|
|
77
|
-
if (err.code === "AUDIT_FINDINGS") return 4;
|
|
78
|
+
if (err.code === "BUSY" || err.code === "AUDIT_FINDINGS") return 4;
|
|
78
79
|
if (err.code === "AUDIT_PARTIAL") return 5;
|
|
79
80
|
return 2;
|
|
80
81
|
}
|