@opendatalabs/connect 0.8.0 → 0.8.1-canary.400bcfc

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 (77) hide show
  1. package/README.md +67 -0
  2. package/dist/cli/bin.d.ts +3 -0
  3. package/dist/cli/bin.d.ts.map +1 -0
  4. package/dist/cli/bin.js +7 -0
  5. package/dist/cli/bin.js.map +1 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +567 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/cli/main.d.ts +2 -0
  11. package/dist/cli/main.d.ts.map +1 -0
  12. package/dist/cli/main.js +2 -0
  13. package/dist/cli/main.js.map +1 -0
  14. package/dist/connectors/index.d.ts +2 -0
  15. package/dist/connectors/index.d.ts.map +1 -0
  16. package/dist/connectors/index.js +2 -0
  17. package/dist/connectors/index.js.map +1 -0
  18. package/dist/connectors/registry.d.ts +29 -0
  19. package/dist/connectors/registry.d.ts.map +1 -0
  20. package/dist/connectors/registry.js +134 -0
  21. package/dist/connectors/registry.js.map +1 -0
  22. package/dist/core/cli-types.d.ts +110 -0
  23. package/dist/core/cli-types.d.ts.map +1 -0
  24. package/dist/core/cli-types.js +61 -0
  25. package/dist/core/cli-types.js.map +1 -0
  26. package/dist/core/index.d.ts +3 -0
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/index.js +3 -0
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/paths.d.ts +9 -0
  31. package/dist/core/paths.d.ts.map +1 -0
  32. package/dist/core/paths.js +28 -0
  33. package/dist/core/paths.js.map +1 -0
  34. package/dist/core/state-store.d.ts +16 -0
  35. package/dist/core/state-store.d.ts.map +1 -0
  36. package/dist/core/state-store.js +23 -0
  37. package/dist/core/state-store.js.map +1 -0
  38. package/dist/core/types.d.ts +4 -0
  39. package/dist/core/types.d.ts.map +1 -1
  40. package/dist/personal-server/index.d.ts +8 -0
  41. package/dist/personal-server/index.d.ts.map +1 -0
  42. package/dist/personal-server/index.js +76 -0
  43. package/dist/personal-server/index.js.map +1 -0
  44. package/dist/react/useVanaData.d.ts +1 -1
  45. package/dist/react/useVanaData.d.ts.map +1 -1
  46. package/dist/react/useVanaData.js +22 -9
  47. package/dist/react/useVanaData.js.map +1 -1
  48. package/dist/runtime/bundled-assets.d.ts +6 -0
  49. package/dist/runtime/bundled-assets.d.ts.map +1 -0
  50. package/dist/runtime/bundled-assets.js +44 -0
  51. package/dist/runtime/bundled-assets.js.map +1 -0
  52. package/dist/runtime/index.d.ts +3 -0
  53. package/dist/runtime/index.d.ts.map +1 -0
  54. package/dist/runtime/index.js +3 -0
  55. package/dist/runtime/index.js.map +1 -0
  56. package/dist/runtime/managed-playwright.d.ts +33 -0
  57. package/dist/runtime/managed-playwright.d.ts.map +1 -0
  58. package/dist/runtime/managed-playwright.js +237 -0
  59. package/dist/runtime/managed-playwright.js.map +1 -0
  60. package/dist/runtime/repo-paths.d.ts +2 -0
  61. package/dist/runtime/repo-paths.d.ts.map +1 -0
  62. package/dist/runtime/repo-paths.js +19 -0
  63. package/dist/runtime/repo-paths.js.map +1 -0
  64. package/dist/server/config.d.ts +2 -0
  65. package/dist/server/config.d.ts.map +1 -1
  66. package/dist/server/config.js +1 -0
  67. package/dist/server/config.js.map +1 -1
  68. package/dist/server/connect.d.ts.map +1 -1
  69. package/dist/server/connect.js +7 -0
  70. package/dist/server/connect.js.map +1 -1
  71. package/package.json +29 -4
  72. package/runtime-assets/playwright-runner/entitlements.plist +12 -0
  73. package/runtime-assets/playwright-runner/index.cjs +1243 -0
  74. package/runtime-assets/playwright-runner/package-lock.json +1242 -0
  75. package/runtime-assets/playwright-runner/package.json +29 -0
  76. package/runtime-assets/playwright-runner/scripts/build.js +182 -0
  77. package/runtime-assets/run-connector.cjs +355 -0
package/README.md CHANGED
@@ -72,6 +72,73 @@ Then, in your dev version of DataConnect (likely built from the `main` branch) y
72
72
 
73
73
  If you prefer to integrate the SDK into an existing project, follow the steps below.
74
74
 
75
+ ## Headless CLI
76
+
77
+ `vana-connect` now also ships a local collection CLI for connector setup and data export flows.
78
+
79
+ ### Install
80
+
81
+ Use the published package directly:
82
+
83
+ ```bash
84
+ pnpm dlx @opendatalabs/connect status
85
+ ```
86
+
87
+ Or install it globally:
88
+
89
+ ```bash
90
+ pnpm add -g @opendatalabs/connect
91
+ vana status
92
+ ```
93
+
94
+ ### Commands
95
+
96
+ ```bash
97
+ vana sources
98
+ vana connect github
99
+ vana status
100
+ vana setup
101
+ ```
102
+
103
+ ### Local development
104
+
105
+ From this repo:
106
+
107
+ ```bash
108
+ pnpm install
109
+ pnpm build
110
+ node dist/cli/bin.js status
111
+ ```
112
+
113
+ The CLI installs its local browser runtime under `~/.dataconnect/`.
114
+ That runtime is bundled from `vana-connect` itself, so `vana setup` does not require a separate `data-connect` checkout.
115
+
116
+ To build a standalone SEA executable locally:
117
+
118
+ ```bash
119
+ pnpm build
120
+ pnpm build:sea
121
+ ./artifacts/sea/vana-linux-x64 status --json
122
+ ```
123
+
124
+ `pnpm build:sea` uses Node 25's `--build-sea` flow and embeds the runtime assets needed for `vana setup`.
125
+ It also produces `artifacts/sea/vana-linux-x64.tar.gz` and a matching `.sha256` checksum file for release/distribution.
126
+
127
+ ### Programmatic runtime access
128
+
129
+ If you are building an app surface like DataConnect Desktop or a hosted orchestration layer, use the SDK modules instead of shelling out to the CLI where possible.
130
+
131
+ ```ts
132
+ import { ManagedPlaywrightRuntime } from "@opendatalabs/connect/runtime";
133
+ import { listAvailableSources } from "@opendatalabs/connect/connectors";
134
+ ```
135
+
136
+ Intended split:
137
+
138
+ - app surfaces consume SDK/runtime APIs
139
+ - agent skills consume the CLI
140
+ - `data-connectors` remains the connector and schema source of truth
141
+
75
142
  ### Installation
76
143
 
77
144
  ```bash
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./index.js";
3
+ const exitCode = await runCli(process.argv);
4
+ if (typeof exitCode === "number") {
5
+ process.exitCode = exitCode;
6
+ }
7
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC5C,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;IACjC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runCli(argv?: string[]): Promise<number>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAkDA,wBAAsB,MAAM,CAAC,IAAI,WAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA2CjE"}
@@ -0,0 +1,567 @@
1
+ import fs from "node:fs";
2
+ import fsp from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { confirm, input, password } from "@inquirer/prompts";
5
+ import { Command } from "commander";
6
+ import { CliOutcomeStatus, getBrowserProfilesDir, getConnectorCacheDir, getLastResultPath, readCliState, updateSourceState, } from "../core/index.js";
7
+ import { listAvailableSources } from "../connectors/registry.js";
8
+ import { detectPersonalServerTarget, ingestResult, } from "../personal-server/index.js";
9
+ import { findDataConnectorsDir, ManagedPlaywrightRuntime, } from "../runtime/index.js";
10
+ export async function runCli(argv = process.argv) {
11
+ const normalizedArgv = normalizeArgv(argv);
12
+ const parsedOptions = extractGlobalOptions(normalizedArgv);
13
+ const program = new Command();
14
+ program.name("vana").description("Vana Connect CLI");
15
+ program
16
+ .command("connect <source>")
17
+ .option("--json", "Output machine-readable JSON")
18
+ .option("--no-input", "Fail instead of prompting for input")
19
+ .option("--yes", "Approve safe setup prompts automatically")
20
+ .option("--quiet", "Reduce non-essential output")
21
+ .action(async (source) => {
22
+ process.exitCode = await runConnect(source, parsedOptions);
23
+ });
24
+ program
25
+ .command("sources")
26
+ .description("List supported sources")
27
+ .option("--json", "Output machine-readable JSON")
28
+ .action(async () => {
29
+ process.exitCode = await runList(parsedOptions);
30
+ });
31
+ program
32
+ .command("status")
33
+ .description("Show runtime and Personal Server status")
34
+ .option("--json", "Output machine-readable JSON")
35
+ .action(async () => {
36
+ process.exitCode = await runStatus(parsedOptions);
37
+ });
38
+ program
39
+ .command("setup")
40
+ .description("Install or repair the local runtime")
41
+ .option("--json", "Output machine-readable JSON")
42
+ .option("--yes", "Approve safe setup prompts automatically")
43
+ .action(async () => {
44
+ process.exitCode = await runSetup(parsedOptions);
45
+ });
46
+ await program.parseAsync(normalizedArgv);
47
+ return Number(process.exitCode ?? 0);
48
+ }
49
+ async function runConnect(source, options) {
50
+ const runtime = new ManagedPlaywrightRuntime();
51
+ const emit = createEmitter(options);
52
+ const registrySources = await loadRegistrySources();
53
+ const sourceLabels = createSourceLabelMap(registrySources);
54
+ let setupLogPath;
55
+ let fetchLogPath;
56
+ let runLogPath;
57
+ try {
58
+ emit.info(`Finding a connector for ${displaySource(source, sourceLabels)}...`);
59
+ const target = await detectPersonalServerTarget();
60
+ if (runtime.state !== "installed") {
61
+ emit.info(`Vana Connect needs a local browser runtime before it can connect ${displaySource(source, sourceLabels)}.`);
62
+ emit.info("");
63
+ emit.info("This will install:");
64
+ emit.info("- the connector runner");
65
+ emit.info("- a Chromium browser engine");
66
+ emit.info("- local runtime files under ~/.dataconnect/");
67
+ emit.info("");
68
+ emit.info("Your credentials stay on this machine. Nothing is sent anywhere except the platform you’re connecting to.");
69
+ if (!options.yes) {
70
+ if (options.noInput) {
71
+ emit.event({
72
+ type: "outcome",
73
+ status: CliOutcomeStatus.SETUP_REQUIRED,
74
+ source,
75
+ });
76
+ return 1;
77
+ }
78
+ const shouldContinue = await confirm({
79
+ message: "Continue?",
80
+ default: true,
81
+ });
82
+ if (!shouldContinue) {
83
+ emit.event({
84
+ type: "outcome",
85
+ status: CliOutcomeStatus.SETUP_REQUIRED,
86
+ source,
87
+ reason: "setup_declined",
88
+ });
89
+ return 1;
90
+ }
91
+ }
92
+ const installResult = await runtime.ensureInstalled(Boolean(options.yes));
93
+ setupLogPath = installResult.logPath;
94
+ emit.event({
95
+ type: "setup-complete",
96
+ runtime: installResult.runtime,
97
+ logPath: installResult.logPath,
98
+ });
99
+ emit.info("Runtime ready.");
100
+ if (installResult.logPath) {
101
+ emit.info(`Setup log: ${installResult.logPath}`);
102
+ }
103
+ }
104
+ else {
105
+ emit.event({
106
+ type: "setup-check",
107
+ runtime: runtime.state,
108
+ });
109
+ }
110
+ const fetched = await runtime.fetchConnector(source);
111
+ fetchLogPath = fetched.logPath;
112
+ const sourceDetails = registrySources.find((item) => item.id === source);
113
+ const resolution = {
114
+ source,
115
+ connectorPath: fetched.connectorPath,
116
+ };
117
+ emit.event({
118
+ type: "connector-resolved",
119
+ source: resolution.source,
120
+ connectorPath: resolution.connectorPath,
121
+ logPath: fetched.logPath,
122
+ });
123
+ emit.info("Connector ready.");
124
+ if (sourceDetails?.description) {
125
+ emit.info(sourceDetails.description);
126
+ }
127
+ const profilePath = path.join(getBrowserProfilesDir(), `${path.basename(resolution.connectorPath, path.extname(resolution.connectorPath))}`);
128
+ if (fs.existsSync(profilePath)) {
129
+ emit.info(`Found an existing ${displaySource(source, sourceLabels)} session. Trying that first...`);
130
+ }
131
+ await updateSourceState(resolution.source, {
132
+ connectorInstalled: true,
133
+ sessionPresent: fs.existsSync(profilePath),
134
+ lastError: null,
135
+ });
136
+ emit.info(`Connecting to ${displaySource(source, sourceLabels)}...`);
137
+ emit.info("Collecting your data...");
138
+ let finalStatus = CliOutcomeStatus.UNEXPECTED_INTERNAL_ERROR;
139
+ let resultPath = getLastResultPath();
140
+ let collectedResult = false;
141
+ for await (const event of runtime.runConnector({
142
+ connectorPath: resolution.connectorPath,
143
+ source: resolution.source,
144
+ noInput: options.noInput,
145
+ onNeedInput: async (needInput) => {
146
+ emit.info("");
147
+ emit.info(`To connect ${displaySource(source, sourceLabels)}, Vana Connect will open a local browser session on this machine.`);
148
+ emit.info("Your credentials stay local.");
149
+ emit.info("");
150
+ emit.info(needInput.message ??
151
+ `${displaySource(source, sourceLabels)} needs additional details to continue.`);
152
+ const values = {};
153
+ for (const field of needInput.fields) {
154
+ if (field.toLowerCase().includes("password")) {
155
+ values[field] = await password({ message: humanizeField(field) });
156
+ }
157
+ else {
158
+ values[field] = await input({ message: humanizeField(field) });
159
+ }
160
+ }
161
+ return values;
162
+ },
163
+ })) {
164
+ emit.event(event);
165
+ if (event.logPath) {
166
+ runLogPath = event.logPath;
167
+ }
168
+ if (event.type === "needs-input") {
169
+ await updateSourceState(resolution.source, {
170
+ lastRunAt: new Date().toISOString(),
171
+ lastRunOutcome: CliOutcomeStatus.NEEDS_INPUT,
172
+ lastError: event.message ?? "Input required.",
173
+ });
174
+ emit.event({
175
+ type: "outcome",
176
+ status: CliOutcomeStatus.NEEDS_INPUT,
177
+ source: resolution.source,
178
+ });
179
+ if (!options.json) {
180
+ emit.info(`${displaySource(source, sourceLabels)} needs additional input before it can connect.`);
181
+ emit.info(`Next: run \`vana connect ${source}\` without \`--no-input\`.`);
182
+ }
183
+ return 1;
184
+ }
185
+ if (event.type === "runtime-error") {
186
+ await updateSourceState(resolution.source, {
187
+ lastRunAt: new Date().toISOString(),
188
+ lastRunOutcome: CliOutcomeStatus.RUNTIME_ERROR,
189
+ lastError: event.message ?? "Connector run failed.",
190
+ });
191
+ emit.info(event.message ?? "Connector run failed.");
192
+ emit.event({
193
+ type: "outcome",
194
+ status: CliOutcomeStatus.RUNTIME_ERROR,
195
+ source: resolution.source,
196
+ });
197
+ if (event.logPath) {
198
+ emit.info(`Run log: ${event.logPath}`);
199
+ }
200
+ return 1;
201
+ }
202
+ if (event.type === "legacy-auth") {
203
+ await updateSourceState(resolution.source, {
204
+ lastRunAt: new Date().toISOString(),
205
+ lastRunOutcome: CliOutcomeStatus.LEGACY_AUTH,
206
+ lastError: event.message ?? "Legacy authentication is required.",
207
+ dataState: "none",
208
+ });
209
+ emit.info(event.message ??
210
+ "This connector requires legacy headed authentication that is not available in batch mode.");
211
+ emit.info(`Next: establish a reusable ${displaySource(source, sourceLabels)} session manually, or migrate this connector to requestInput.`);
212
+ if (event.logPath) {
213
+ emit.info(`Run log: ${event.logPath}`);
214
+ }
215
+ emit.event({
216
+ type: "outcome",
217
+ status: CliOutcomeStatus.LEGACY_AUTH,
218
+ source: resolution.source,
219
+ });
220
+ return 1;
221
+ }
222
+ if (event.type === "collection-complete" && event.resultPath) {
223
+ collectedResult = true;
224
+ resultPath = event.resultPath;
225
+ const ingestEvents = await ingestResult(resolution.source, resultPath, target);
226
+ for (const ingestEvent of ingestEvents) {
227
+ emit.event(ingestEvent);
228
+ }
229
+ const ingestCompleted = ingestEvents.some((ingestEvent) => ingestEvent.type === "ingest-complete");
230
+ finalStatus = ingestCompleted
231
+ ? CliOutcomeStatus.CONNECTED_AND_INGESTED
232
+ : CliOutcomeStatus.CONNECTED_LOCAL_ONLY;
233
+ }
234
+ }
235
+ if (!collectedResult) {
236
+ await updateSourceState(resolution.source, {
237
+ connectorInstalled: true,
238
+ sessionPresent: fs.existsSync(profilePath),
239
+ lastRunAt: new Date().toISOString(),
240
+ lastRunOutcome: CliOutcomeStatus.UNEXPECTED_INTERNAL_ERROR,
241
+ dataState: "none",
242
+ lastError: "Connector run ended without a result.",
243
+ });
244
+ emit.event({
245
+ type: "outcome",
246
+ status: CliOutcomeStatus.UNEXPECTED_INTERNAL_ERROR,
247
+ source: resolution.source,
248
+ reason: "Connector run ended without a result.",
249
+ });
250
+ if (runLogPath) {
251
+ emit.info(`Run log: ${runLogPath}`);
252
+ }
253
+ return 1;
254
+ }
255
+ const dataState = finalStatus === CliOutcomeStatus.CONNECTED_AND_INGESTED
256
+ ? "ingested_personal_server"
257
+ : "collected_local";
258
+ await updateSourceState(resolution.source, {
259
+ connectorInstalled: true,
260
+ sessionPresent: true,
261
+ lastRunAt: new Date().toISOString(),
262
+ lastRunOutcome: finalStatus,
263
+ dataState,
264
+ lastError: null,
265
+ });
266
+ if (finalStatus === CliOutcomeStatus.CONNECTED_AND_INGESTED) {
267
+ emit.info(`Connected ${displaySource(source, sourceLabels)}.`);
268
+ emit.info(`Collected your ${displaySource(source, sourceLabels)} data and synced it to your Personal Server.`);
269
+ }
270
+ else {
271
+ if (target.state !== "available") {
272
+ emit.info(`No Personal Server is available right now, so your ${displaySource(source, sourceLabels)} data was saved locally.`);
273
+ }
274
+ emit.info(`Connected ${displaySource(source, sourceLabels)}.`);
275
+ emit.info(`Collected your ${displaySource(source, sourceLabels)} data and saved it locally.`);
276
+ emit.info(`Local result: ${resultPath}`);
277
+ }
278
+ if (runLogPath) {
279
+ emit.info(`Run log: ${runLogPath}`);
280
+ }
281
+ else if (fetchLogPath) {
282
+ emit.info(`Fetch log: ${fetchLogPath}`);
283
+ }
284
+ else if (setupLogPath) {
285
+ emit.info(`Setup log: ${setupLogPath}`);
286
+ }
287
+ emit.info("Next: run `vana status` to inspect your current connection state.");
288
+ emit.event({
289
+ type: "outcome",
290
+ status: finalStatus,
291
+ source: resolution.source,
292
+ resultPath,
293
+ });
294
+ return 0;
295
+ }
296
+ catch (error) {
297
+ const message = error instanceof Error ? error.message : "Unexpected error.";
298
+ emit.info(message);
299
+ emit.event({
300
+ type: "outcome",
301
+ status: CliOutcomeStatus.UNEXPECTED_INTERNAL_ERROR,
302
+ source,
303
+ reason: message,
304
+ });
305
+ if (runLogPath) {
306
+ emit.info(`Run log: ${runLogPath}`);
307
+ }
308
+ else if (fetchLogPath) {
309
+ emit.info(`Fetch log: ${fetchLogPath}`);
310
+ }
311
+ else if (setupLogPath) {
312
+ emit.info(`Setup log: ${setupLogPath}`);
313
+ }
314
+ return 1;
315
+ }
316
+ }
317
+ async function runList(options) {
318
+ const sources = await loadRegistrySources();
319
+ const installedSourceIds = new Set((await listInstalledConnectorFiles()).map((source) => source.source));
320
+ const enrichedSources = sources.map((source) => ({
321
+ ...source,
322
+ installed: installedSourceIds.has(source.id),
323
+ }));
324
+ if (options.json) {
325
+ process.stdout.write(`${JSON.stringify({ sources: enrichedSources })}\n`);
326
+ return 0;
327
+ }
328
+ const emit = createEmitter(options);
329
+ for (const source of enrichedSources) {
330
+ const description = source.description ? ` - ${source.description}` : "";
331
+ const installed = source.installed ? " [installed]" : "";
332
+ emit.info(`${source.name}${installed}${description}`);
333
+ }
334
+ return 0;
335
+ }
336
+ async function runStatus(options) {
337
+ const emit = createEmitter(options);
338
+ const runtime = new ManagedPlaywrightRuntime();
339
+ const personalServer = await detectPersonalServerTarget();
340
+ const state = await readCliState();
341
+ const registrySources = await loadRegistrySources();
342
+ const sourceLabels = createSourceLabelMap(registrySources);
343
+ const sourceMetadata = createSourceMetadataMap(registrySources);
344
+ const sources = await gatherSourceStatuses(state.sources, sourceMetadata);
345
+ const status = {
346
+ runtime: runtime.state,
347
+ runtimePath: runtime.state === "installed" ? runtime.runnerDir : null,
348
+ personalServer: personalServer.state,
349
+ personalServerUrl: personalServer.url,
350
+ sources,
351
+ };
352
+ if (options.json) {
353
+ process.stdout.write(`${JSON.stringify(status)}\n`);
354
+ return 0;
355
+ }
356
+ emit.info("Vana Connect status");
357
+ emit.info("");
358
+ emit.info(`Runtime: ${status.runtime}`);
359
+ emit.info(`Personal Server: ${status.personalServer}`);
360
+ emit.info("");
361
+ for (const source of status.sources) {
362
+ emit.info(formatSourceStatus(source, sourceLabels));
363
+ const details = formatSourceStatusDetail(source);
364
+ if (details) {
365
+ emit.info(` ${details}`);
366
+ }
367
+ }
368
+ return 0;
369
+ }
370
+ async function runSetup(options) {
371
+ const emit = createEmitter(options);
372
+ const runtime = new ManagedPlaywrightRuntime();
373
+ if (runtime.state === "installed") {
374
+ emit.info("Vana Connect runtime is already installed.");
375
+ emit.event({ type: "setup-check", runtime: runtime.state });
376
+ return 0;
377
+ }
378
+ try {
379
+ const result = await runtime.ensureInstalled(Boolean(options.yes));
380
+ emit.info("Runtime ready.");
381
+ if (result.logPath) {
382
+ emit.info(`Setup log: ${result.logPath}`);
383
+ }
384
+ emit.event({
385
+ type: "setup-complete",
386
+ runtime: result.runtime,
387
+ logPath: result.logPath,
388
+ });
389
+ return 0;
390
+ }
391
+ catch (error) {
392
+ const message = error instanceof Error
393
+ ? error.message
394
+ : "Vana Connect could not finish installing the local runtime.";
395
+ emit.info(message);
396
+ emit.event({
397
+ type: "outcome",
398
+ status: CliOutcomeStatus.RUNTIME_ERROR,
399
+ reason: message,
400
+ });
401
+ return 1;
402
+ }
403
+ }
404
+ function createEmitter(options) {
405
+ return {
406
+ event(event) {
407
+ if (options.json) {
408
+ process.stdout.write(`${JSON.stringify(event)}\n`);
409
+ }
410
+ },
411
+ info(message) {
412
+ if (options.json || options.quiet) {
413
+ return;
414
+ }
415
+ process.stdout.write(`${message}\n`);
416
+ },
417
+ };
418
+ }
419
+ function displaySource(source, labels = {}) {
420
+ return labels[source] ?? source.charAt(0).toUpperCase() + source.slice(1);
421
+ }
422
+ function humanizeField(value) {
423
+ return value
424
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
425
+ .replace(/[_-]/g, " ")
426
+ .replace(/^\w/, (match) => match.toUpperCase());
427
+ }
428
+ async function gatherSourceStatuses(storedSources, metadata = {}) {
429
+ const installedFiles = await listInstalledConnectorFiles();
430
+ const sourceNames = new Set([
431
+ ...Object.keys(storedSources),
432
+ ...installedFiles.map((file) => file.source),
433
+ ]);
434
+ return [...sourceNames]
435
+ .sort((left, right) => left.localeCompare(right))
436
+ .map((source) => {
437
+ const stored = storedSources[source] ?? {};
438
+ const installed = installedFiles.some((file) => file.source === source);
439
+ const details = metadata[source];
440
+ return {
441
+ source,
442
+ name: details?.name,
443
+ company: details?.company,
444
+ description: details?.description,
445
+ installed,
446
+ sessionPresent: stored.sessionPresent ?? false,
447
+ lastRunAt: stored.lastRunAt ?? null,
448
+ lastRunOutcome: stored.lastRunOutcome ?? null,
449
+ dataState: stored.dataState === "ingested_personal_server"
450
+ ? "ingested_personal_server"
451
+ : stored.dataState === "collected_local"
452
+ ? "collected_local"
453
+ : "none",
454
+ lastError: stored.lastError ?? null,
455
+ };
456
+ });
457
+ }
458
+ async function listInstalledConnectorFiles() {
459
+ const connectorsDir = getConnectorCacheDir();
460
+ try {
461
+ const results = [];
462
+ const entries = await fsp.readdir(connectorsDir, { withFileTypes: true });
463
+ for (const entry of entries) {
464
+ if (!entry.isDirectory()) {
465
+ continue;
466
+ }
467
+ const companyDir = path.join(connectorsDir, entry.name);
468
+ const files = await fsp.readdir(companyDir);
469
+ for (const file of files) {
470
+ if (!file.endsWith("-playwright.js")) {
471
+ continue;
472
+ }
473
+ results.push({
474
+ source: file.replace(/-playwright\.js$/, ""),
475
+ path: path.join(companyDir, file),
476
+ });
477
+ }
478
+ }
479
+ return results;
480
+ }
481
+ catch {
482
+ return [];
483
+ }
484
+ }
485
+ function formatSourceStatus(source, labels = {}) {
486
+ if (!source.installed) {
487
+ return `${displaySource(source.source, labels)}: not connected`;
488
+ }
489
+ if (!source.lastRunOutcome) {
490
+ return `${displaySource(source.source, labels)}: installed`;
491
+ }
492
+ if (source.lastRunOutcome === CliOutcomeStatus.NEEDS_INPUT) {
493
+ return `${displaySource(source.source, labels)}: needs input`;
494
+ }
495
+ if (source.lastRunOutcome === CliOutcomeStatus.RUNTIME_ERROR) {
496
+ return `${displaySource(source.source, labels)}: error`;
497
+ }
498
+ if (source.lastRunOutcome === CliOutcomeStatus.LEGACY_AUTH) {
499
+ return `${displaySource(source.source, labels)}: legacy auth required`;
500
+ }
501
+ if (source.dataState === "ingested_personal_server") {
502
+ return `${displaySource(source.source, labels)}: connected, synced`;
503
+ }
504
+ if (source.dataState === "collected_local") {
505
+ return `${displaySource(source.source, labels)}: connected, local only`;
506
+ }
507
+ return `${displaySource(source.source, labels)}: connected`;
508
+ }
509
+ function formatSourceStatusDetail(source) {
510
+ if (source.lastRunOutcome === CliOutcomeStatus.NEEDS_INPUT) {
511
+ return source.lastError
512
+ ? `${source.lastError}. Run \`vana connect ${source.source}\` interactively.`
513
+ : `Run \`vana connect ${source.source}\` interactively.`;
514
+ }
515
+ if (source.lastRunOutcome === CliOutcomeStatus.LEGACY_AUTH) {
516
+ return "This source still uses legacy headed auth and cannot complete in batch mode.";
517
+ }
518
+ if (source.lastRunOutcome === CliOutcomeStatus.RUNTIME_ERROR) {
519
+ return source.lastError ?? "The last connector run failed.";
520
+ }
521
+ if (!source.lastRunOutcome && source.installed) {
522
+ return `Run \`vana connect ${source.source}\` to collect data.`;
523
+ }
524
+ return null;
525
+ }
526
+ function normalizeArgv(argv) {
527
+ if (argv[2] === "connect" &&
528
+ ["list", "status", "setup"].includes(argv[3] ?? "")) {
529
+ const mapping = {
530
+ list: "sources",
531
+ status: "status",
532
+ setup: "setup",
533
+ };
534
+ return [argv[0], argv[1], mapping[argv[3]], ...argv.slice(4)];
535
+ }
536
+ return argv;
537
+ }
538
+ function extractGlobalOptions(argv) {
539
+ return {
540
+ json: argv.includes("--json"),
541
+ noInput: argv.includes("--no-input"),
542
+ yes: argv.includes("--yes"),
543
+ quiet: argv.includes("--quiet"),
544
+ };
545
+ }
546
+ function createSourceLabelMap(sources) {
547
+ return Object.fromEntries(sources.map((source) => [source.id, source.name]));
548
+ }
549
+ function createSourceMetadataMap(sources) {
550
+ return Object.fromEntries(sources.map((source) => [
551
+ source.id,
552
+ {
553
+ name: source.name,
554
+ company: source.company,
555
+ description: source.description,
556
+ },
557
+ ]));
558
+ }
559
+ async function loadRegistrySources() {
560
+ try {
561
+ return ((await listAvailableSources(findDataConnectorsDir() ?? undefined)) ?? []);
562
+ }
563
+ catch {
564
+ return [];
565
+ }
566
+ }
567
+ //# sourceMappingURL=index.js.map