@gmickel/gno 1.22.0 → 1.24.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.
Files changed (59) hide show
  1. package/README.md +33 -12
  2. package/assets/skill/SKILL.md +41 -19
  3. package/package.json +1 -1
  4. package/spec/cli.md +127 -20
  5. package/spec/evals-agentic.md +35 -0
  6. package/spec/evals.md +6 -0
  7. package/spec/mcp.md +18 -0
  8. package/spec/output-schemas/query-diagnose-v1.schema.json +123 -0
  9. package/spec/output-schemas/query-diagnose.schema.json +89 -2
  10. package/spec/output-schemas/setup-activation-result.schema.json +456 -0
  11. package/spec/output-schemas/setup-command-result.schema.json +93 -0
  12. package/spec/output-schemas/setup-receipt.schema.json +258 -0
  13. package/spec/output-schemas/setup-semantic-receipt.schema.json +195 -0
  14. package/src/app/context-runtime-types.ts +3 -0
  15. package/src/app/context-runtime.ts +1 -0
  16. package/src/app/context-surface.ts +4 -2
  17. package/src/cli/commands/ask.ts +31 -20
  18. package/src/cli/commands/completion/scripts.ts +2 -0
  19. package/src/cli/commands/context-build.ts +17 -7
  20. package/src/cli/commands/embed.ts +7 -2
  21. package/src/cli/commands/query.ts +58 -37
  22. package/src/cli/commands/search.ts +29 -19
  23. package/src/cli/commands/setup-activation.ts +324 -0
  24. package/src/cli/commands/setup-semantic.ts +591 -0
  25. package/src/cli/commands/setup.ts +410 -0
  26. package/src/cli/commands/vsearch.ts +31 -22
  27. package/src/cli/options.ts +39 -0
  28. package/src/cli/program.ts +112 -0
  29. package/src/cli/setup-semantic-worker.ts +177 -0
  30. package/src/config/defaults.ts +10 -1
  31. package/src/config/types.ts +71 -0
  32. package/src/core/config-mutation.ts +94 -64
  33. package/src/core/file-lock.ts +70 -31
  34. package/src/core/folder-setup-planning.ts +453 -0
  35. package/src/core/folder-setup.ts +490 -0
  36. package/src/core/project-affinity-surface.ts +114 -0
  37. package/src/core/project-affinity.ts +330 -0
  38. package/src/core/setup-activation.ts +309 -0
  39. package/src/core/setup-receipt.ts +321 -0
  40. package/src/core/validation.ts +20 -1
  41. package/src/mcp/tools/ask.ts +10 -1
  42. package/src/mcp/tools/context.ts +18 -0
  43. package/src/mcp/tools/index.ts +13 -2
  44. package/src/mcp/tools/query.ts +12 -0
  45. package/src/mcp/tools/search.ts +7 -0
  46. package/src/mcp/tools/vsearch.ts +7 -0
  47. package/src/pipeline/diagnose.ts +48 -3
  48. package/src/pipeline/explain.ts +54 -13
  49. package/src/pipeline/hybrid.ts +100 -59
  50. package/src/pipeline/project-affinity.ts +162 -0
  51. package/src/pipeline/search.ts +76 -10
  52. package/src/pipeline/types.ts +9 -0
  53. package/src/pipeline/vsearch.ts +117 -91
  54. package/src/sdk/client.ts +80 -20
  55. package/src/sdk/index.ts +2 -0
  56. package/src/sdk/types.ts +20 -7
  57. package/src/serve/connectors.ts +29 -2
  58. package/src/serve/context-capsule.ts +18 -1
  59. package/src/serve/routes/api.ts +69 -0
@@ -1,6 +1,7 @@
1
1
  /** Context Capsule build command over the shared application runtime. */
2
2
 
3
3
  import type { ContextCapsuleBuildInput } from "../../app/context-runtime";
4
+ import type { CliProjectAffinityRequest } from "../../core/project-affinity-surface";
4
5
  import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
5
6
  import type { EmbeddingPort, RerankPort } from "../../llm/types";
6
7
  import type { VectorIndexPort } from "../../store/vector";
@@ -11,6 +12,7 @@ import {
11
12
  canonicalBuiltContextCapsuleJson,
12
13
  validateContextCapsuleBuildInput,
13
14
  } from "../../app/context-runtime";
15
+ import { resolveCliProjectAffinity } from "../../core/project-affinity-surface";
14
16
  import {
15
17
  finishRetrievalTraceAfterError,
16
18
  startRetrievalTraceRequest,
@@ -27,10 +29,8 @@ import {
27
29
  } from "../progress";
28
30
  import { initStore } from "./shared";
29
31
 
30
- export interface ContextBuildCommandOptions extends Omit<
31
- ContextCapsuleBuildInput,
32
- "goal"
33
- > {
32
+ export interface ContextBuildCommandOptions
33
+ extends Omit<ContextCapsuleBuildInput, "goal">, CliProjectAffinityRequest {
34
34
  configPath?: string;
35
35
  format: "json" | "md";
36
36
  }
@@ -64,8 +64,12 @@ export const contextBuild = async (
64
64
  goal: string,
65
65
  options: ContextBuildCommandOptions
66
66
  ): Promise<string> => {
67
+ const { projectAffinityDisabled, projectRoots, ...contextOptions } = options;
67
68
  try {
68
- validateContextCapsuleBuildInput({ goal, ...options }, options.indexName);
69
+ validateContextCapsuleBuildInput(
70
+ { goal, ...contextOptions },
71
+ options.indexName
72
+ );
69
73
  } catch (error) {
70
74
  throw contextCliError(error);
71
75
  }
@@ -84,8 +88,13 @@ export const contextBuild = async (
84
88
  let vectorIndex: VectorIndexPort | null = null;
85
89
  let traceSession: RetrievalTraceSession | undefined;
86
90
  try {
91
+ const projectAffinity = await resolveCliProjectAffinity(config, {
92
+ cwd: process.cwd(),
93
+ disabled: projectAffinityDisabled,
94
+ projectRoots,
95
+ });
87
96
  validateContextCapsuleBuildInput(
88
- { goal, ...options },
97
+ { goal, ...contextOptions },
89
98
  options.indexName,
90
99
  config.collections.map((collection) => collection.name)
91
100
  );
@@ -163,7 +172,7 @@ export const contextBuild = async (
163
172
  if (showProgress && progress) process.stderr.write("\n");
164
173
  }
165
174
  const capsule = await buildContextCapsule(
166
- { goal, ...options },
175
+ { goal, ...contextOptions },
167
176
  {
168
177
  store,
169
178
  config,
@@ -171,6 +180,7 @@ export const contextBuild = async (
171
180
  vectorIndex,
172
181
  embedPort,
173
182
  rerankPort,
183
+ projectAffinity,
174
184
  traceSession,
175
185
  }
176
186
  );
@@ -68,6 +68,8 @@ export interface EmbedOptions {
68
68
  json?: boolean;
69
69
  /** Verbose error logging */
70
70
  verbose?: boolean;
71
+ /** Use cached models only (also used by the standalone setup worker). */
72
+ offline?: boolean;
71
73
  }
72
74
 
73
75
  export type EmbedResult =
@@ -469,9 +471,12 @@ export async function embed(options: EmbedOptions = {}): Promise<EmbedResult> {
469
471
  }
470
472
 
471
473
  // Create LLM adapter and embedding port with auto-download
472
- const globals = getGlobals();
474
+ let offline = options.offline;
475
+ if (offline === undefined) {
476
+ offline = getGlobals().offline;
477
+ }
473
478
  const policy = resolveDownloadPolicy(process.env, {
474
- offline: globals.offline,
479
+ offline,
475
480
  });
476
481
 
477
482
  // Create progress renderer for model download (throttled to avoid spam)
@@ -5,6 +5,7 @@
5
5
  * @module src/cli/commands/query
6
6
  */
7
7
 
8
+ import type { CliProjectAffinityRequest } from "../../core/project-affinity-surface";
8
9
  import type { RetrievalTraceSurfaceMetadata } from "../../core/retrieval-trace-session";
9
10
  import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
10
11
  import type {
@@ -18,6 +19,7 @@ import {
18
19
  fingerprintContentTypeRules,
19
20
  normalizeContentTypes,
20
21
  } from "../../config";
22
+ import { resolveCliProjectAffinity } from "../../core/project-affinity-surface";
21
23
  import {
22
24
  finishRetrievalTraceAfterError,
23
25
  retrievalTraceFilters,
@@ -46,30 +48,31 @@ import { decorateSearchResultsForIndex, initStore } from "./shared";
46
48
  // Types
47
49
  // ─────────────────────────────────────────────────────────────────────────────
48
50
 
49
- export type QueryCommandOptions = HybridSearchOptions & {
50
- /** Override config path */
51
- configPath?: string;
52
- /** Index name */
53
- indexName?: string;
54
- /** Override embedding model */
55
- embedModel?: string;
56
- /** Override expansion model */
57
- expandModel?: string;
58
- /** Deprecated alias for expansion model */
59
- genModel?: string;
60
- /** Override rerank model */
61
- rerankModel?: string;
62
- /** Output as JSON */
63
- json?: boolean;
64
- /** Output as Markdown */
65
- md?: boolean;
66
- /** Output as CSV */
67
- csv?: boolean;
68
- /** Output as XML */
69
- xml?: boolean;
70
- /** Output files only */
71
- files?: boolean;
72
- };
51
+ export type QueryCommandOptions = Omit<HybridSearchOptions, "projectAffinity"> &
52
+ CliProjectAffinityRequest & {
53
+ /** Override config path */
54
+ configPath?: string;
55
+ /** Index name */
56
+ indexName?: string;
57
+ /** Override embedding model */
58
+ embedModel?: string;
59
+ /** Override expansion model */
60
+ expandModel?: string;
61
+ /** Deprecated alias for expansion model */
62
+ genModel?: string;
63
+ /** Override rerank model */
64
+ rerankModel?: string;
65
+ /** Output as JSON */
66
+ json?: boolean;
67
+ /** Output as Markdown */
68
+ md?: boolean;
69
+ /** Output as CSV */
70
+ csv?: boolean;
71
+ /** Output as XML */
72
+ xml?: boolean;
73
+ /** Output files only */
74
+ files?: boolean;
75
+ };
73
76
 
74
77
  export interface QueryFormatOptions {
75
78
  format: "terminal" | "json" | "files" | "csv" | "md" | "xml";
@@ -86,16 +89,20 @@ export type QueryResult =
86
89
  }
87
90
  | { success: false; error: string };
88
91
 
89
- export type QueryDiagnoseCommandOptions = HybridSearchOptions & {
90
- target: string;
91
- configPath?: string;
92
- indexName?: string;
93
- embedModel?: string;
94
- expandModel?: string;
95
- genModel?: string;
96
- rerankModel?: string;
97
- json?: boolean;
98
- };
92
+ export type QueryDiagnoseCommandOptions = Omit<
93
+ HybridSearchOptions,
94
+ "projectAffinity"
95
+ > &
96
+ CliProjectAffinityRequest & {
97
+ target: string;
98
+ configPath?: string;
99
+ indexName?: string;
100
+ embedModel?: string;
101
+ expandModel?: string;
102
+ genModel?: string;
103
+ rerankModel?: string;
104
+ json?: boolean;
105
+ };
99
106
 
100
107
  export interface QueryDiagnoseFormatOptions {
101
108
  format: "terminal" | "json";
@@ -140,6 +147,12 @@ export async function query(
140
147
  let traceSession: RetrievalTraceSession | undefined;
141
148
 
142
149
  try {
150
+ const { projectAffinityDisabled, projectRoots, ...queryOptions } = options;
151
+ const projectAffinity = await resolveCliProjectAffinity(config, {
152
+ cwd: process.cwd(),
153
+ disabled: projectAffinityDisabled,
154
+ projectRoots,
155
+ });
143
156
  const embedUri = resolveModelUri(
144
157
  config,
145
158
  "embed",
@@ -167,7 +180,7 @@ export async function query(
167
180
  store,
168
181
  config,
169
182
  query: queryText,
170
- filters: retrievalTraceFilters({ ...options, limit }),
183
+ filters: retrievalTraceFilters({ ...queryOptions, limit }),
171
184
  pipeline: "hybrid",
172
185
  indexName: options.indexName,
173
186
  modelUris: [embedUri, expandUri, rerankUri].filter(
@@ -261,8 +274,9 @@ export async function query(
261
274
  rerankPort,
262
275
  };
263
276
  const result = await searchHybrid(deps, queryText, {
264
- ...options,
277
+ ...queryOptions,
265
278
  limit,
279
+ projectAffinity,
266
280
  traceSession,
267
281
  });
268
282
 
@@ -318,6 +332,12 @@ export async function queryDiagnose(
318
332
  let rerankPort: RerankPort | null = null;
319
333
 
320
334
  try {
335
+ const { projectAffinityDisabled, projectRoots, ...queryOptions } = options;
336
+ const projectAffinity = await resolveCliProjectAffinity(config, {
337
+ cwd: process.cwd(),
338
+ disabled: projectAffinityDisabled,
339
+ projectRoots,
340
+ });
321
341
  const globals = getGlobals();
322
342
  const policy = resolveDownloadPolicy(process.env, {
323
343
  offline: globals.offline,
@@ -415,7 +435,8 @@ export async function queryDiagnose(
415
435
  rerankPort,
416
436
  };
417
437
  const result = await diagnoseQueryTarget(deps, queryText, {
418
- ...options,
438
+ ...queryOptions,
439
+ projectAffinity,
419
440
  contentTypeRules,
420
441
  contentTypeRulesFingerprint:
421
442
  fingerprintContentTypeRules(contentTypeRules),
@@ -5,12 +5,14 @@
5
5
  * @module src/cli/commands/search
6
6
  */
7
7
 
8
+ import type { CliProjectAffinityRequest } from "../../core/project-affinity-surface";
8
9
  import type {
9
10
  RetrievalTraceSession,
10
11
  RetrievalTraceSurfaceMetadata,
11
12
  } from "../../core/retrieval-trace-session";
12
13
  import type { SearchOptions, SearchResults } from "../../pipeline/types";
13
14
 
15
+ import { resolveCliProjectAffinity } from "../../core/project-affinity-surface";
14
16
  import {
15
17
  finishRetrievalTraceAfterError,
16
18
  startRetrievalTraceRequest,
@@ -26,24 +28,25 @@ import { decorateSearchResultsForIndex, initStore } from "./shared";
26
28
  // Types
27
29
  // ─────────────────────────────────────────────────────────────────────────────
28
30
 
29
- export type SearchCommandOptions = SearchOptions & {
30
- /** Override config path */
31
- configPath?: string;
32
- /** Index name */
33
- indexName?: string;
34
- /** Output as JSON */
35
- json?: boolean;
36
- /** Output as Markdown */
37
- md?: boolean;
38
- /** Output as CSV */
39
- csv?: boolean;
40
- /** Output as XML */
41
- xml?: boolean;
42
- /** Output files only */
43
- files?: boolean;
44
- /** Terminal hyperlink policy */
45
- terminalLinks?: FormatOptions["terminalLinks"];
46
- };
31
+ export type SearchCommandOptions = Omit<SearchOptions, "projectAffinity"> &
32
+ CliProjectAffinityRequest & {
33
+ /** Override config path */
34
+ configPath?: string;
35
+ /** Index name */
36
+ indexName?: string;
37
+ /** Output as JSON */
38
+ json?: boolean;
39
+ /** Output as Markdown */
40
+ md?: boolean;
41
+ /** Output as CSV */
42
+ csv?: boolean;
43
+ /** Output as XML */
44
+ xml?: boolean;
45
+ /** Output files only */
46
+ files?: boolean;
47
+ /** Terminal hyperlink policy */
48
+ terminalLinks?: FormatOptions["terminalLinks"];
49
+ };
47
50
 
48
51
  export type SearchResult =
49
52
  | {
@@ -84,6 +87,12 @@ export async function search(
84
87
  let traceSession: RetrievalTraceSession | undefined;
85
88
 
86
89
  try {
90
+ const { projectAffinityDisabled, projectRoots, ...searchOptions } = options;
91
+ const projectAffinity = await resolveCliProjectAffinity(config, {
92
+ cwd: process.cwd(),
93
+ disabled: projectAffinityDisabled,
94
+ projectRoots,
95
+ });
87
96
  const started = await startRetrievalTraceRequest({
88
97
  store,
89
98
  config,
@@ -109,8 +118,9 @@ export async function search(
109
118
  if (!started.ok) return { success: false, error: started.error.message };
110
119
  traceSession = started.value ?? undefined;
111
120
  const result = await searchBm25(store, query, {
112
- ...options,
121
+ ...searchOptions,
113
122
  limit,
123
+ projectAffinity,
114
124
  traceSession,
115
125
  });
116
126
 
@@ -0,0 +1,324 @@
1
+ import type {
2
+ SetupConnectorCompositionDeps,
3
+ SetupConnectorDefinition,
4
+ SetupConnectorResult,
5
+ SetupConnectorState,
6
+ } from "../../core/setup-activation";
7
+ import type { SqliteAdapter } from "../../store/sqlite/adapter";
8
+ import type {
9
+ SetupCommandOptions,
10
+ SetupCommandOutcome,
11
+ SetupCommandResult,
12
+ } from "./setup";
13
+
14
+ import { getIndexDbPath } from "../../app/constants";
15
+ import { getConfigPaths, loadConfig, toAbsolutePath } from "../../config";
16
+ import {
17
+ composeSetupConnectors,
18
+ SETUP_ACTIVATION_SCHEMA_VERSION,
19
+ unavailableSetupConnectorComposition,
20
+ } from "../../core/setup-activation";
21
+ import {
22
+ getConnectorDefinition,
23
+ getConnectorStatuses,
24
+ installConnector,
25
+ verifyInstalledConnector,
26
+ } from "../../serve/connectors";
27
+ import { SqliteAdapter as DefaultSqliteAdapter } from "../../store/sqlite/adapter";
28
+ import {
29
+ formatSetupResult,
30
+ lexicalSuccessIsProven,
31
+ SETUP_COMMAND_SCHEMA_VERSION,
32
+ setup,
33
+ } from "./setup";
34
+
35
+ export interface SetupActivationResult {
36
+ schemaVersion: typeof SETUP_ACTIVATION_SCHEMA_VERSION;
37
+ status: "completed" | "completed_with_actions" | "failed";
38
+ setup: SetupCommandResult;
39
+ connectors: SetupConnectorResult[];
40
+ }
41
+
42
+ export type SetupOutputResult = SetupCommandResult | SetupActivationResult;
43
+
44
+ export interface SetupOutputOutcome {
45
+ result: SetupOutputResult;
46
+ exitCode: 0 | 1 | 2;
47
+ }
48
+
49
+ export interface SetupProfileAdvisoryInput {
50
+ folder: string;
51
+ collection: string;
52
+ }
53
+
54
+ export interface SetupActivationCommandOptions extends SetupCommandOptions {
55
+ connectorIds?: string[];
56
+ connectorWorkspace?: { cwd?: string; homeDir?: string };
57
+ connectorDeps?: SetupConnectorCompositionDeps;
58
+ createActivationStore?: () => SqliteAdapter;
59
+ discoverProfileAdvisory?: (
60
+ input: SetupProfileAdvisoryInput
61
+ ) => Promise<unknown>;
62
+ }
63
+
64
+ function dedupeConnectorIds(connectorIds: string[]): string[] {
65
+ return [...new Set(connectorIds)];
66
+ }
67
+
68
+ function connectorStateFromStatus(
69
+ status: Awaited<ReturnType<typeof getConnectorStatuses>>[number]
70
+ ): SetupConnectorState {
71
+ return {
72
+ id: status.id,
73
+ kind: status.installKind,
74
+ target: status.target,
75
+ scope: status.scope,
76
+ installed: status.installed,
77
+ configurationError: Boolean(status.error),
78
+ };
79
+ }
80
+
81
+ function connectorDefinitions(
82
+ connectorIds: string[]
83
+ ): SetupConnectorDefinition[] | null {
84
+ const definitions: SetupConnectorDefinition[] = [];
85
+ for (const connectorId of connectorIds) {
86
+ const definition = getConnectorDefinition(connectorId);
87
+ if (!definition) {
88
+ return null;
89
+ }
90
+ definitions.push(definition);
91
+ }
92
+ return definitions;
93
+ }
94
+
95
+ function invalidConnectorOutcome(connectorIds: string[]): SetupCommandOutcome {
96
+ const unknown = connectorIds.find(
97
+ (connectorId) => getConnectorDefinition(connectorId) === null
98
+ );
99
+ return {
100
+ result: {
101
+ schemaVersion: SETUP_COMMAND_SCHEMA_VERSION,
102
+ status: "failed",
103
+ lexical: {
104
+ receipt: null,
105
+ error: {
106
+ code: "invalid_connector",
107
+ message: `Unknown connector: ${unknown ?? ""}`.trim(),
108
+ remediation:
109
+ "Run `gno setup --help` and pass one documented connector ID.",
110
+ },
111
+ },
112
+ semantic: null,
113
+ },
114
+ exitCode: 1,
115
+ };
116
+ }
117
+
118
+ function failedSetupActivationOutcome(
119
+ setupOutcome: SetupCommandOutcome
120
+ ): SetupOutputOutcome {
121
+ return {
122
+ result: {
123
+ schemaVersion: SETUP_ACTIVATION_SCHEMA_VERSION,
124
+ status: "failed",
125
+ setup: setupOutcome.result,
126
+ connectors: [],
127
+ },
128
+ exitCode: setupOutcome.exitCode,
129
+ };
130
+ }
131
+
132
+ function defaultConnectorDeps(
133
+ workspace: { cwd?: string; homeDir?: string } | undefined
134
+ ): SetupConnectorCompositionDeps {
135
+ return {
136
+ getStates: async () =>
137
+ (await getConnectorStatuses(workspace)).map(connectorStateFromStatus),
138
+ install: async (connectorId, context) =>
139
+ connectorStateFromStatus(
140
+ await installConnector(
141
+ connectorId,
142
+ { reinstall: false },
143
+ {
144
+ ...workspace,
145
+ indexName: context.indexName,
146
+ configPath: context.configPath,
147
+ }
148
+ )
149
+ ),
150
+ verify: async (connectorId, store, collection) =>
151
+ verifyInstalledConnector(
152
+ connectorId,
153
+ store,
154
+ collection,
155
+ undefined,
156
+ workspace
157
+ ),
158
+ };
159
+ }
160
+
161
+ async function discoverProfileAdvisory(
162
+ discover: SetupActivationCommandOptions["discoverProfileAdvisory"],
163
+ input: SetupProfileAdvisoryInput
164
+ ): Promise<void> {
165
+ if (!discover) {
166
+ return;
167
+ }
168
+ try {
169
+ await discover(input);
170
+ } catch {
171
+ // Advisory discovery cannot mutate or fail verified setup.
172
+ }
173
+ }
174
+
175
+ function withUnavailableConnectors(
176
+ setupResult: SetupCommandResult,
177
+ definitions: SetupConnectorDefinition[]
178
+ ): SetupOutputOutcome {
179
+ const unavailable = unavailableSetupConnectorComposition(definitions);
180
+ return {
181
+ result: {
182
+ schemaVersion: SETUP_ACTIVATION_SCHEMA_VERSION,
183
+ status: unavailable.status,
184
+ setup: setupResult,
185
+ connectors: unavailable.connectors,
186
+ },
187
+ exitCode: 0,
188
+ };
189
+ }
190
+
191
+ async function closeActivationStore(
192
+ store: SqliteAdapter | null
193
+ ): Promise<void> {
194
+ if (!store) {
195
+ return;
196
+ }
197
+ try {
198
+ await store.close();
199
+ } catch {
200
+ // Connector cleanup cannot replace proven lexical success.
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Add opt-in connector onboarding beside the unchanged setup result.
206
+ * No-connector calls retain setup-command-result@1.0; connector-mode failures
207
+ * wrap the unchanged failed setup result without running connector actions.
208
+ */
209
+ export async function setupWithActivation(
210
+ options: SetupActivationCommandOptions
211
+ ): Promise<SetupOutputOutcome> {
212
+ const requestedIds = dedupeConnectorIds(options.connectorIds ?? []);
213
+ const definitions = connectorDefinitions(requestedIds);
214
+ if (!definitions) {
215
+ return failedSetupActivationOutcome(invalidConnectorOutcome(requestedIds));
216
+ }
217
+
218
+ const {
219
+ connectorIds: _connectorIds,
220
+ connectorWorkspace: _connectorWorkspace,
221
+ connectorDeps: _connectorDeps,
222
+ createActivationStore: _createActivationStore,
223
+ discoverProfileAdvisory: _discoverProfileAdvisory,
224
+ ...setupOptions
225
+ } = options;
226
+ const setupOutcome = await setup(setupOptions);
227
+ if (
228
+ setupOutcome.exitCode !== 0 ||
229
+ setupOutcome.result.status !== "completed"
230
+ ) {
231
+ return definitions.length > 0
232
+ ? failedSetupActivationOutcome(setupOutcome)
233
+ : setupOutcome;
234
+ }
235
+
236
+ const lexicalReceipt = setupOutcome.result.lexical.receipt;
237
+ const collection = lexicalReceipt?.collection.name;
238
+ if (
239
+ !lexicalReceipt ||
240
+ !collection ||
241
+ !lexicalSuccessIsProven(lexicalReceipt)
242
+ ) {
243
+ return setupOutcome;
244
+ }
245
+
246
+ await discoverProfileAdvisory(options.discoverProfileAdvisory, {
247
+ folder: lexicalReceipt.input.folder,
248
+ collection,
249
+ });
250
+ if (definitions.length === 0) {
251
+ return setupOutcome;
252
+ }
253
+
254
+ let store: SqliteAdapter | null = null;
255
+ try {
256
+ const paths = getConfigPaths();
257
+ const configPath = toAbsolutePath(options.configPath ?? paths.configFile);
258
+ const indexName = options.indexName ?? "default";
259
+ const configResult = await loadConfig(configPath);
260
+ if (!configResult.ok) {
261
+ return withUnavailableConnectors(setupOutcome.result, definitions);
262
+ }
263
+
264
+ store =
265
+ options.createActivationStore?.() ??
266
+ (new DefaultSqliteAdapter() as SqliteAdapter);
267
+ store.setConfigPath(configPath);
268
+ const opened = await store.open(
269
+ getIndexDbPath(indexName),
270
+ configResult.value.ftsTokenizer
271
+ );
272
+ if (!opened.ok) {
273
+ return withUnavailableConnectors(setupOutcome.result, definitions);
274
+ }
275
+
276
+ const composition = await composeSetupConnectors({
277
+ connectorIds: requestedIds,
278
+ definitions,
279
+ collection,
280
+ store,
281
+ installContext: { indexName, configPath },
282
+ deps:
283
+ options.connectorDeps ??
284
+ defaultConnectorDeps(options.connectorWorkspace),
285
+ });
286
+ return {
287
+ result: {
288
+ schemaVersion: SETUP_ACTIVATION_SCHEMA_VERSION,
289
+ status: composition.status,
290
+ setup: setupOutcome.result,
291
+ connectors: composition.connectors,
292
+ },
293
+ exitCode: 0,
294
+ };
295
+ } catch {
296
+ return withUnavailableConnectors(setupOutcome.result, definitions);
297
+ } finally {
298
+ await closeActivationStore(store);
299
+ }
300
+ }
301
+
302
+ function isSetupActivationResult(
303
+ result: SetupOutputResult
304
+ ): result is SetupActivationResult {
305
+ return "setup" in result;
306
+ }
307
+
308
+ export function formatSetupOutputResult(
309
+ result: SetupOutputResult,
310
+ options: { json: boolean }
311
+ ): string {
312
+ if (options.json) {
313
+ return JSON.stringify(result, null, 2);
314
+ }
315
+ if (!isSetupActivationResult(result)) {
316
+ return formatSetupResult(result, options);
317
+ }
318
+ const setupOutput = formatSetupResult(result.setup, options);
319
+ const connectorOutput = result.connectors.map(
320
+ (connector) =>
321
+ `connector=${connector.connectorId} installation=${connector.installation} verification=${connector.verification} code=${connector.code} remediation=${connector.remediation}`
322
+ );
323
+ return [setupOutput, ...connectorOutput].join("\n");
324
+ }