@gmickel/gno 1.25.1 → 1.26.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 +10 -4
- package/assets/skill/SKILL.md +29 -17
- package/browser-extension/artifacts/{gno-browser-clipper-v1.25.1.zip → gno-browser-clipper-v1.26.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +146 -4
- package/spec/db/schema.sql +1 -1
- package/spec/output-schemas/project-profile-apply.schema.json +209 -0
- package/spec/output-schemas/project-profile-command.schema.json +160 -0
- package/spec/output-schemas/query-diagnose.schema.json +7 -1
- package/spec/output-schemas/setup-profile-result.schema.json +87 -0
- package/spec/project-profile.schema.json +301 -0
- package/src/cli/commands/collection/add.ts +39 -45
- package/src/cli/commands/collection/remove.ts +28 -28
- package/src/cli/commands/collection/rename.ts +55 -73
- package/src/cli/commands/context/add.ts +37 -26
- package/src/cli/commands/context/rm.ts +47 -20
- package/src/cli/commands/init.ts +55 -125
- package/src/cli/commands/models/use.ts +43 -38
- package/src/cli/commands/profile-apply.ts +334 -0
- package/src/cli/commands/profile.ts +409 -0
- package/src/cli/commands/setup-activation.ts +205 -54
- package/src/cli/commands/setup-profile.ts +223 -0
- package/src/cli/commands/setup.ts +3 -0
- package/src/cli/program.ts +110 -9
- package/src/config/index.ts +3 -0
- package/src/config/project-profile.ts +367 -0
- package/src/config/saver.ts +16 -7
- package/src/config/types.ts +42 -0
- package/src/core/config-mutation.ts +138 -76
- package/src/core/config-write-lock.ts +89 -0
- package/src/core/context-identity.ts +16 -0
- package/src/core/context-resolver.ts +2 -12
- package/src/core/folder-setup-planning.ts +6 -21
- package/src/core/folder-setup.ts +30 -2
- package/src/core/path-rules.ts +53 -0
- package/src/core/project-affinity-surface.ts +102 -7
- package/src/core/project-profile-apply-state.ts +268 -0
- package/src/core/project-profile-apply-validation.ts +95 -0
- package/src/core/project-profile-apply.ts +408 -0
- package/src/core/project-profile-canonical.ts +71 -0
- package/src/core/project-profile-diff.ts +302 -0
- package/src/core/project-profile-discovery.ts +519 -0
- package/src/core/project-profile-file.ts +37 -0
- package/src/core/project-profile-parser.ts +98 -0
- package/src/core/project-profile.ts +490 -0
- package/src/ingestion/walker.ts +85 -44
- package/src/llm/cache.ts +21 -0
- package/src/serve/config-sync.ts +2 -2
- package/src/serve/resident-runtime.ts +1 -0
- package/src/serve/routes/api.ts +1 -0
- package/src/store/migrations/021-multi-context-identity.ts +37 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/sqlite/adapter.ts +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.25.1.zip.sha256 +0 -1
|
@@ -3,43 +3,43 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { removeCollection } from "../../../collection";
|
|
6
|
-
import {
|
|
6
|
+
import { applyConfigFileChange } from "../../../core/config-mutation";
|
|
7
7
|
import { CliError } from "../../errors";
|
|
8
8
|
|
|
9
|
-
export async function collectionRemove(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
export async function collectionRemove(
|
|
10
|
+
name: string,
|
|
11
|
+
options: { configPath?: string } = {}
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
const mutation = await applyConfigFileChange(
|
|
14
|
+
{ configPath: options.configPath },
|
|
15
|
+
(config) => {
|
|
16
|
+
const result = removeCollection(config, { name });
|
|
17
|
+
return result.ok
|
|
18
|
+
? {
|
|
19
|
+
ok: true as const,
|
|
20
|
+
config: result.config,
|
|
21
|
+
value: result.collection,
|
|
22
|
+
}
|
|
23
|
+
: {
|
|
24
|
+
ok: false as const,
|
|
25
|
+
error: result.message,
|
|
26
|
+
code: result.code,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
);
|
|
21
30
|
|
|
22
|
-
if (!
|
|
31
|
+
if (!mutation.ok) {
|
|
23
32
|
// Map collection error codes to CLI error codes
|
|
24
33
|
const cliCode =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
mutation.code === "VALIDATION" ||
|
|
35
|
+
mutation.code === "NOT_FOUND" ||
|
|
36
|
+
mutation.code === "HAS_REFERENCES"
|
|
28
37
|
? "VALIDATION"
|
|
29
38
|
: "RUNTIME";
|
|
30
|
-
throw new CliError(cliCode,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Save config
|
|
34
|
-
const saveResult = await saveConfig(result.config);
|
|
35
|
-
if (!saveResult.ok) {
|
|
36
|
-
throw new CliError(
|
|
37
|
-
"RUNTIME",
|
|
38
|
-
`Failed to save config: ${saveResult.error.message}`
|
|
39
|
-
);
|
|
39
|
+
throw new CliError(cliCode, mutation.error);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
process.stdout.write(
|
|
43
|
-
`Collection "${
|
|
43
|
+
`Collection "${mutation.value?.name}" removed successfully\n`
|
|
44
44
|
);
|
|
45
45
|
}
|
|
@@ -2,90 +2,72 @@
|
|
|
2
2
|
* gno collection rename - Rename a collection
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
getCollectionFromScope,
|
|
8
|
-
loadConfig,
|
|
9
|
-
saveConfig,
|
|
10
|
-
} from "../../../config";
|
|
5
|
+
import { CollectionSchema, getCollectionFromScope } from "../../../config";
|
|
6
|
+
import { applyConfigFileChange } from "../../../core/config-mutation";
|
|
11
7
|
import { CliError } from "../../errors";
|
|
12
8
|
|
|
13
9
|
export async function collectionRename(
|
|
14
10
|
oldName: string,
|
|
15
|
-
newName: string
|
|
11
|
+
newName: string,
|
|
12
|
+
options: { configPath?: string } = {}
|
|
16
13
|
): Promise<void> {
|
|
17
14
|
const oldCollectionName = oldName.toLowerCase();
|
|
18
15
|
const newCollectionName = newName.toLowerCase();
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const collection = config.collections.find(
|
|
33
|
-
(c) => c.name === oldCollectionName
|
|
34
|
-
);
|
|
35
|
-
if (!collection) {
|
|
36
|
-
throw new CliError(
|
|
37
|
-
"VALIDATION",
|
|
38
|
-
`Collection "${oldCollectionName}" not found`
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Check if new name already exists
|
|
43
|
-
const existingNew = config.collections.find(
|
|
44
|
-
(c) => c.name === newCollectionName
|
|
45
|
-
);
|
|
46
|
-
if (existingNew) {
|
|
47
|
-
throw new CliError(
|
|
48
|
-
"VALIDATION",
|
|
49
|
-
`Collection "${newCollectionName}" already exists`
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Validate new name
|
|
54
|
-
const testCollection = { ...collection, name: newCollectionName };
|
|
55
|
-
const validation = CollectionSchema.safeParse(testCollection);
|
|
56
|
-
if (!validation.success) {
|
|
57
|
-
throw new CliError(
|
|
58
|
-
"VALIDATION",
|
|
59
|
-
`Invalid collection name: ${validation.error.issues[0]?.message ?? "unknown error"}`
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Rename collection
|
|
64
|
-
collection.name = newCollectionName;
|
|
65
|
-
|
|
66
|
-
// Update contexts that reference this collection
|
|
67
|
-
for (const context of config.contexts) {
|
|
68
|
-
const collFromScope = getCollectionFromScope(context.scopeKey);
|
|
69
|
-
if (collFromScope === oldCollectionName) {
|
|
70
|
-
// Update scope key
|
|
71
|
-
if (context.scopeType === "collection") {
|
|
72
|
-
context.scopeKey = `${newCollectionName}:`;
|
|
73
|
-
} else if (context.scopeType === "prefix") {
|
|
74
|
-
// Replace collection name in URI
|
|
75
|
-
context.scopeKey = context.scopeKey.replace(
|
|
76
|
-
`gno://${oldCollectionName}/`,
|
|
77
|
-
`gno://${newCollectionName}/`
|
|
78
|
-
);
|
|
17
|
+
const mutation = await applyConfigFileChange(
|
|
18
|
+
{ configPath: options.configPath },
|
|
19
|
+
(config) => {
|
|
20
|
+
const collection = config.collections.find(
|
|
21
|
+
(item) => item.name === oldCollectionName
|
|
22
|
+
);
|
|
23
|
+
if (!collection) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false as const,
|
|
26
|
+
error: `Collection "${oldCollectionName}" not found`,
|
|
27
|
+
code: "NOT_FOUND",
|
|
28
|
+
};
|
|
79
29
|
}
|
|
30
|
+
if (config.collections.some((item) => item.name === newCollectionName)) {
|
|
31
|
+
return {
|
|
32
|
+
ok: false as const,
|
|
33
|
+
error: `Collection "${newCollectionName}" already exists`,
|
|
34
|
+
code: "DUPLICATE",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const validation = CollectionSchema.safeParse({
|
|
38
|
+
...collection,
|
|
39
|
+
name: newCollectionName,
|
|
40
|
+
});
|
|
41
|
+
if (!validation.success) {
|
|
42
|
+
return {
|
|
43
|
+
ok: false as const,
|
|
44
|
+
error: `Invalid collection name: ${validation.error.issues[0]?.message ?? "unknown error"}`,
|
|
45
|
+
code: "VALIDATION",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
collection.name = newCollectionName;
|
|
49
|
+
for (const context of config.contexts) {
|
|
50
|
+
if (getCollectionFromScope(context.scopeKey) !== oldCollectionName) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (context.scopeType === "collection") {
|
|
54
|
+
context.scopeKey = `${newCollectionName}:`;
|
|
55
|
+
} else if (context.scopeType === "prefix") {
|
|
56
|
+
context.scopeKey = context.scopeKey.replace(
|
|
57
|
+
`gno://${oldCollectionName}/`,
|
|
58
|
+
`gno://${newCollectionName}/`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { ok: true as const, config };
|
|
80
63
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Save config
|
|
84
|
-
const saveResult = await saveConfig(config);
|
|
85
|
-
if (!saveResult.ok) {
|
|
64
|
+
);
|
|
65
|
+
if (!mutation.ok) {
|
|
86
66
|
throw new CliError(
|
|
87
|
-
"
|
|
88
|
-
|
|
67
|
+
["NOT_FOUND", "DUPLICATE", "VALIDATION"].includes(mutation.code)
|
|
68
|
+
? "VALIDATION"
|
|
69
|
+
: "RUNTIME",
|
|
70
|
+
mutation.error
|
|
89
71
|
);
|
|
90
72
|
}
|
|
91
73
|
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* @module src/cli/commands/context/add
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { parseScope } from "../../../config";
|
|
10
|
+
import { applyConfigFileChange } from "../../../core/config-mutation";
|
|
11
|
+
import { normalizePersistedContextText } from "../../../core/context-identity";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Exit codes
|
|
@@ -21,7 +23,11 @@ const EXIT_VALIDATION = 1;
|
|
|
21
23
|
* @param text - Context description text
|
|
22
24
|
* @returns Exit code
|
|
23
25
|
*/
|
|
24
|
-
export async function contextAdd(
|
|
26
|
+
export async function contextAdd(
|
|
27
|
+
scope: string,
|
|
28
|
+
text: string,
|
|
29
|
+
options: { configPath?: string } = {}
|
|
30
|
+
): Promise<number> {
|
|
25
31
|
// Parse scope
|
|
26
32
|
const parsed = parseScope(scope);
|
|
27
33
|
if (!parsed) {
|
|
@@ -32,33 +38,38 @@ export async function contextAdd(scope: string, text: string): Promise<number> {
|
|
|
32
38
|
return EXIT_VALIDATION;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.error(`Error: ${configResult.error.message}`);
|
|
41
|
+
const normalizedText = normalizePersistedContextText(text);
|
|
42
|
+
if (!normalizedText) {
|
|
43
|
+
console.error("Error: Context text must not be empty");
|
|
39
44
|
return EXIT_VALIDATION;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
const mutation = await applyConfigFileChange(
|
|
48
|
+
{ configPath: options.configPath },
|
|
49
|
+
(config) => {
|
|
50
|
+
const duplicate = config.contexts.some(
|
|
51
|
+
(context) =>
|
|
52
|
+
context.scopeType === parsed.type &&
|
|
53
|
+
context.scopeKey === parsed.key &&
|
|
54
|
+
normalizePersistedContextText(context.text) === normalizedText
|
|
55
|
+
);
|
|
56
|
+
if (duplicate) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false as const,
|
|
59
|
+
error: `Context for scope "${scope}" with that text already exists`,
|
|
60
|
+
code: "DUPLICATE",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
config.contexts.push({
|
|
64
|
+
scopeType: parsed.type,
|
|
65
|
+
scopeKey: parsed.key,
|
|
66
|
+
text: normalizedText,
|
|
67
|
+
});
|
|
68
|
+
return { ok: true as const, config };
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
if (!mutation.ok) {
|
|
72
|
+
console.error(`Error: ${mutation.error}`);
|
|
62
73
|
return EXIT_VALIDATION;
|
|
63
74
|
}
|
|
64
75
|
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* @module src/cli/commands/context/rm
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { parseScope } from "../../../config";
|
|
10
|
+
import { applyConfigFileChange } from "../../../core/config-mutation";
|
|
11
|
+
import { normalizePersistedContextText } from "../../../core/context-identity";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Exit codes
|
|
@@ -20,30 +22,55 @@ const EXIT_VALIDATION = 1;
|
|
|
20
22
|
* @param scope - Scope key to remove
|
|
21
23
|
* @returns Exit code
|
|
22
24
|
*/
|
|
23
|
-
export async function contextRm(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
export async function contextRm(
|
|
26
|
+
scope: string,
|
|
27
|
+
text?: string,
|
|
28
|
+
options: { configPath?: string } = {}
|
|
29
|
+
): Promise<number> {
|
|
30
|
+
const parsed = parseScope(scope);
|
|
31
|
+
if (!parsed) {
|
|
32
|
+
console.error(`Error: Invalid scope format: ${scope}`);
|
|
28
33
|
return EXIT_VALIDATION;
|
|
29
34
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const index = config.contexts.findIndex((ctx) => ctx.scopeKey === scope);
|
|
35
|
-
if (index === -1) {
|
|
36
|
-
console.error(`Error: Context for scope "${scope}" not found`);
|
|
35
|
+
const normalizedText =
|
|
36
|
+
text === undefined ? undefined : normalizePersistedContextText(text);
|
|
37
|
+
if (normalizedText === "") {
|
|
38
|
+
console.error("Error: Context text must not be empty");
|
|
37
39
|
return EXIT_VALIDATION;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
const mutation = await applyConfigFileChange(
|
|
43
|
+
{ configPath: options.configPath },
|
|
44
|
+
(config) => {
|
|
45
|
+
const matches = config.contexts
|
|
46
|
+
.map((context, index) => ({ context, index }))
|
|
47
|
+
.filter(
|
|
48
|
+
({ context }) =>
|
|
49
|
+
context.scopeType === parsed.type &&
|
|
50
|
+
context.scopeKey === parsed.key &&
|
|
51
|
+
(normalizedText === undefined ||
|
|
52
|
+
normalizePersistedContextText(context.text) === normalizedText)
|
|
53
|
+
);
|
|
54
|
+
if (matches.length === 0) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false as const,
|
|
57
|
+
error: `Context for scope "${scope}" not found`,
|
|
58
|
+
code: "NOT_FOUND",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (normalizedText === undefined && matches.length > 1) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false as const,
|
|
64
|
+
error: `Multiple contexts exist for scope "${scope}"; pass the exact text to remove`,
|
|
65
|
+
code: "AMBIGUOUS",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
config.contexts.splice(matches[0]!.index, 1);
|
|
69
|
+
return { ok: true as const, config };
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
if (!mutation.ok) {
|
|
73
|
+
console.error(`Error: ${mutation.error}`);
|
|
47
74
|
return EXIT_VALIDATION;
|
|
48
75
|
}
|
|
49
76
|
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -17,13 +17,12 @@ import {
|
|
|
17
17
|
FTS_TOKENIZERS,
|
|
18
18
|
type FtsTokenizer,
|
|
19
19
|
getConfigPaths,
|
|
20
|
-
isInitialized,
|
|
21
20
|
isValidLanguageHint,
|
|
22
|
-
loadConfigOrNull,
|
|
23
21
|
pathExists,
|
|
24
|
-
saveConfig,
|
|
25
22
|
toAbsolutePath,
|
|
26
23
|
} from "../../config";
|
|
24
|
+
import { applyConfigFileChange } from "../../core/config-mutation";
|
|
25
|
+
import { SqliteAdapter } from "../../store/sqlite/adapter";
|
|
27
26
|
|
|
28
27
|
/** Pattern to replace invalid chars in collection names with hyphens */
|
|
29
28
|
const INVALID_NAME_CHARS = /[^a-z0-9_-]/g;
|
|
@@ -70,166 +69,97 @@ export interface InitResult {
|
|
|
70
69
|
error?: string;
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
/**
|
|
74
|
-
* Handle case when already initialized.
|
|
75
|
-
*/
|
|
76
|
-
async function handleAlreadyInitialized(
|
|
77
|
-
options: InitOptions,
|
|
78
|
-
paths: ReturnType<typeof getConfigPaths>
|
|
79
|
-
): Promise<InitResult> {
|
|
80
|
-
// Ensure directories exist (may have been deleted by reset)
|
|
81
|
-
await ensureDirectories();
|
|
82
|
-
|
|
83
|
-
const config = await loadConfigOrNull(options.configPath);
|
|
84
|
-
const dbPath = getIndexDbPath();
|
|
85
|
-
|
|
86
|
-
if (!options.path) {
|
|
87
|
-
return {
|
|
88
|
-
success: true,
|
|
89
|
-
alreadyInitialized: true,
|
|
90
|
-
configPath: paths.configFile,
|
|
91
|
-
dataDir: paths.dataDir,
|
|
92
|
-
dbPath,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!config) {
|
|
97
|
-
return {
|
|
98
|
-
success: false,
|
|
99
|
-
configPath: paths.configFile,
|
|
100
|
-
dataDir: paths.dataDir,
|
|
101
|
-
dbPath,
|
|
102
|
-
error: "Config exists but could not be loaded",
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const collectionResult = await addCollectionToConfig(config, options);
|
|
107
|
-
if (!collectionResult.success) {
|
|
108
|
-
return {
|
|
109
|
-
success: false,
|
|
110
|
-
configPath: paths.configFile,
|
|
111
|
-
dataDir: paths.dataDir,
|
|
112
|
-
dbPath,
|
|
113
|
-
error: collectionResult.error,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const saveResult = await saveConfig(config, options.configPath);
|
|
118
|
-
if (!saveResult.ok) {
|
|
119
|
-
return {
|
|
120
|
-
success: false,
|
|
121
|
-
configPath: paths.configFile,
|
|
122
|
-
dataDir: paths.dataDir,
|
|
123
|
-
dbPath,
|
|
124
|
-
error: saveResult.error.message,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
success: true,
|
|
130
|
-
alreadyInitialized: true,
|
|
131
|
-
configPath: paths.configFile,
|
|
132
|
-
dataDir: paths.dataDir,
|
|
133
|
-
dbPath,
|
|
134
|
-
collectionAdded: collectionResult.collectionName,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
72
|
/**
|
|
139
73
|
* Execute gno init command.
|
|
140
74
|
*/
|
|
141
75
|
export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
142
76
|
const paths = getConfigPaths();
|
|
77
|
+
const configPath = toAbsolutePath(options.configPath ?? paths.configFile);
|
|
78
|
+
const dbPath = getIndexDbPath();
|
|
143
79
|
|
|
144
|
-
// Check if already initialized
|
|
145
|
-
const initialized = await isInitialized(options.configPath);
|
|
146
|
-
if (initialized) {
|
|
147
|
-
return handleAlreadyInitialized(options, paths);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Create directories
|
|
151
80
|
const dirResult = await ensureDirectories();
|
|
152
81
|
if (!dirResult.ok) {
|
|
153
82
|
return {
|
|
154
83
|
success: false,
|
|
155
|
-
configPath
|
|
84
|
+
configPath,
|
|
156
85
|
dataDir: paths.dataDir,
|
|
157
|
-
dbPath
|
|
86
|
+
dbPath,
|
|
158
87
|
error: dirResult.error.message,
|
|
159
88
|
};
|
|
160
89
|
}
|
|
161
90
|
|
|
162
|
-
// Validate tokenizer option if provided
|
|
163
91
|
if (options.tokenizer && !FTS_TOKENIZERS.includes(options.tokenizer)) {
|
|
164
92
|
return {
|
|
165
93
|
success: false,
|
|
166
|
-
configPath
|
|
94
|
+
configPath,
|
|
167
95
|
dataDir: paths.dataDir,
|
|
168
|
-
dbPath
|
|
96
|
+
dbPath,
|
|
169
97
|
error: `Invalid tokenizer: ${options.tokenizer}. Valid: ${FTS_TOKENIZERS.join(", ")}`,
|
|
170
98
|
};
|
|
171
99
|
}
|
|
172
100
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
config
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
101
|
+
const mutation = await applyConfigFileChange(
|
|
102
|
+
{
|
|
103
|
+
configPath,
|
|
104
|
+
createConfigIfMissing: createDefaultConfig,
|
|
105
|
+
},
|
|
106
|
+
async (config, state) => {
|
|
107
|
+
if (state.created && options.tokenizer) {
|
|
108
|
+
config.ftsTokenizer = options.tokenizer;
|
|
109
|
+
}
|
|
110
|
+
let collectionName: string | undefined;
|
|
111
|
+
if (options.path) {
|
|
112
|
+
const collectionResult = await addCollectionToConfig(config, options);
|
|
113
|
+
if (!collectionResult.success) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false as const,
|
|
116
|
+
error: collectionResult.error,
|
|
117
|
+
code: "VALIDATION",
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
collectionName = collectionResult.collectionName;
|
|
121
|
+
}
|
|
186
122
|
return {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
123
|
+
ok: true as const,
|
|
124
|
+
config,
|
|
125
|
+
skipSave: !state.created && !options.path,
|
|
126
|
+
value: {
|
|
127
|
+
alreadyInitialized: !state.created,
|
|
128
|
+
collectionName,
|
|
129
|
+
},
|
|
192
130
|
};
|
|
193
131
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// Save config
|
|
198
|
-
const saveResult = await saveConfig(config, options.configPath);
|
|
199
|
-
if (!saveResult.ok) {
|
|
132
|
+
);
|
|
133
|
+
if (!mutation.ok) {
|
|
200
134
|
return {
|
|
201
135
|
success: false,
|
|
202
|
-
configPath
|
|
136
|
+
configPath,
|
|
203
137
|
dataDir: paths.dataDir,
|
|
204
|
-
dbPath
|
|
205
|
-
error:
|
|
138
|
+
dbPath,
|
|
139
|
+
error: mutation.error,
|
|
206
140
|
};
|
|
207
141
|
}
|
|
208
142
|
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
configPath: paths.configFile,
|
|
220
|
-
dataDir: paths.dataDir,
|
|
221
|
-
dbPath,
|
|
222
|
-
error: `Failed to create database file: ${error instanceof Error ? error.message : String(error)}`,
|
|
223
|
-
};
|
|
224
|
-
}
|
|
143
|
+
const store = new SqliteAdapter();
|
|
144
|
+
const opened = await store.open(dbPath, mutation.config.ftsTokenizer);
|
|
145
|
+
if (!opened.ok) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
configPath,
|
|
149
|
+
dataDir: paths.dataDir,
|
|
150
|
+
dbPath,
|
|
151
|
+
error: `Failed to initialize database: ${opened.error.message}`,
|
|
152
|
+
};
|
|
225
153
|
}
|
|
154
|
+
await store.close();
|
|
226
155
|
|
|
227
156
|
return {
|
|
228
157
|
success: true,
|
|
229
|
-
|
|
158
|
+
alreadyInitialized: mutation.value?.alreadyInitialized || undefined,
|
|
159
|
+
configPath,
|
|
230
160
|
dataDir: paths.dataDir,
|
|
231
161
|
dbPath,
|
|
232
|
-
collectionAdded: collectionName,
|
|
162
|
+
collectionAdded: mutation.value?.collectionName,
|
|
233
163
|
};
|
|
234
164
|
}
|
|
235
165
|
|