@opendatalabs/connect 0.9.2 → 0.9.4-canary.888d13a

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.
@@ -40,6 +40,8 @@ export declare function buildDataListNextSteps(datasetRecords: Array<{
40
40
  authMode?: "automated" | "interactive" | "legacy";
41
41
  }>): string[];
42
42
  export declare function buildDataShowNextSteps(source: string, datasetCount: number, sourceLabels?: SourceLabelMap): string[];
43
+ /** Derive a human-readable message from a stored `connectionHealthReason`. */
44
+ export declare function formatHealthMessage(reason: string | undefined): string | null;
43
45
  export declare function getCliVersion(): string;
44
46
  export declare function getCliChannel(version?: string): "stable" | "canary";
45
47
  export declare function getCliInstallMethod(execPath?: string): CliInstallMethod;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -33,7 +33,7 @@ import { detectPersonalServerTarget, ingestResult, } from "../personal-server/in
33
33
  import { findDataConnectorsDir, ManagedPlaywrightRuntime, } from "../runtime/index.js";
34
34
  import { listAvailableSkills, installSkill, readInstalledSkills, } from "../skills/index.js";
35
35
  import { queryStatus, querySources, queryDataList, queryDataShow, queryDoctor, } from "./queries.js";
36
- import { readUpdateCheck, isNewerVersion, spawnUpdateCheck, } from "./update-check.js";
36
+ import { checkForUpdate, readUpdateCheck, isNewerVersion, } from "./update-check.js";
37
37
  function cleanDescription(desc) {
38
38
  return desc
39
39
  .replace(/ using Playwright browser automation\.?/i, ".")
@@ -51,6 +51,7 @@ export async function runCli(argv = process.argv) {
51
51
  }
52
52
  const parsedOptions = extractGlobalOptions(normalizedArgv);
53
53
  const cliVersion = getCliVersion();
54
+ const installMethod = getCliInstallMethod();
54
55
  // Non-blocking update check — compute suppression flags early
55
56
  const shouldNotify = !parsedOptions.json &&
56
57
  process.stdout.isTTY &&
@@ -58,23 +59,12 @@ export async function runCli(argv = process.argv) {
58
59
  !process.env.CI &&
59
60
  !process.env.AGENT &&
60
61
  !process.env.VANA_DETACHED;
61
- let updateNotice;
62
- if (shouldNotify) {
63
- try {
64
- const cached = await readUpdateCheck();
65
- if (cached && isNewerVersion(cliVersion, cached.latestVersion)) {
66
- const lifecycle = getLifecycleCommands(getCliInstallMethod(), getCliChannel());
67
- updateNotice = `\nUpdate available: ${cliVersion} → ${cached.latestVersion}\nRun: ${lifecycle.upgrade}\n`;
68
- }
69
- else if (!cached) {
70
- // Cache missing or expired — spawn background check
71
- spawnUpdateCheck(cliVersion, getCliInstallMethod());
72
- }
73
- }
74
- catch {
75
- // Suppress all errors — update check is purely informational
76
- }
77
- }
62
+ // Fire update check concurrently — no await, runs in the background.
63
+ // If it finishes before the command completes, the cache is written and
64
+ // the SAME run can read it for the notification below.
65
+ const updateCheckPromise = shouldNotify
66
+ ? checkForUpdate(cliVersion, installMethod).catch(() => { })
67
+ : undefined;
78
68
  const program = new Command();
79
69
  program
80
70
  .name("vana")
@@ -415,29 +405,41 @@ Examples:
415
405
  process.exitCode = await runScheduleRemove(parsedOptions);
416
406
  });
417
407
  try {
418
- try {
419
- await program.parseAsync(normalizedArgv);
420
- }
421
- catch (error) {
422
- if (error instanceof CommanderError) {
423
- if (error.code === "commander.help" ||
424
- error.code === "commander.helpDisplayed" ||
425
- error.code === "commander.version") {
426
- process.exitCode = error.exitCode;
427
- return Number(process.exitCode ?? 0);
428
- }
429
- // Commander already printed to stderr; just set exit code.
408
+ await program.parseAsync(normalizedArgv);
409
+ }
410
+ catch (error) {
411
+ if (error instanceof CommanderError) {
412
+ if (error.code === "commander.help" ||
413
+ error.code === "commander.helpDisplayed" ||
414
+ error.code === "commander.version") {
430
415
  process.exitCode = error.exitCode;
431
- return Number(process.exitCode ?? 1);
416
+ return Number(process.exitCode ?? 0);
432
417
  }
433
- throw error;
418
+ // Commander already printed to stderr; just set exit code.
419
+ process.exitCode = error.exitCode;
420
+ return Number(process.exitCode ?? 1);
434
421
  }
435
- return Number(process.exitCode ?? 0);
422
+ throw error;
436
423
  }
437
- finally {
438
- if (updateNotice)
439
- process.stderr.write(updateNotice);
424
+ // Show update notification if a newer version is available.
425
+ // The concurrent check may have populated the cache during this run.
426
+ if (shouldNotify) {
427
+ try {
428
+ await Promise.race([
429
+ updateCheckPromise,
430
+ new Promise((resolve) => setTimeout(resolve, 2000)),
431
+ ]);
432
+ const cache = await readUpdateCheck();
433
+ if (cache && isNewerVersion(cliVersion, cache.latestVersion)) {
434
+ const { upgrade } = getLifecycleCommands(installMethod, getCliChannel(cliVersion));
435
+ process.stderr.write(`\nUpdate available: ${cliVersion} → ${cache.latestVersion}\nRun: ${upgrade}\n`);
436
+ }
437
+ }
438
+ catch {
439
+ // Never block exit for update notification failures
440
+ }
440
441
  }
442
+ return Number(process.exitCode ?? 0);
441
443
  }
442
444
  async function runConnect(rawSource, options) {
443
445
  const source = rawSource.toLowerCase();
@@ -450,7 +452,7 @@ async function runConnect(rawSource, options) {
450
452
  let setupLogPath;
451
453
  let fetchLogPath;
452
454
  let runLogPath;
453
- let terminalExitCode = null;
455
+ let pendingExitCode = null;
454
456
  try {
455
457
  // Title
456
458
  renderer?.title(displayName);
@@ -688,7 +690,7 @@ async function runConnect(rawSource, options) {
688
690
  if (event.logPath) {
689
691
  runLogPath = event.logPath;
690
692
  }
691
- if (terminalExitCode !== null) {
693
+ if (pendingExitCode !== null && event.type !== "collection-complete") {
692
694
  continue;
693
695
  }
694
696
  if (event.type === "needs-input") {
@@ -698,6 +700,9 @@ async function runConnect(rawSource, options) {
698
700
  lastError: event.message ?? "Input required.",
699
701
  lastLogPath: event.logPath,
700
702
  connectionHealth: "needs_reauth",
703
+ connectionHealthChangedAt: new Date().toISOString(),
704
+ connectionHealthReason: `needs-input: ${event.message ?? "Input required."}`,
705
+ connectionHealthRetryable: false,
701
706
  });
702
707
  emit.event({
703
708
  type: "outcome",
@@ -705,8 +710,7 @@ async function runConnect(rawSource, options) {
705
710
  source: resolution.source,
706
711
  });
707
712
  renderer?.fail(`${displayName} needs credentials. Run without --no-input to authenticate.`);
708
- terminalExitCode = 1;
709
- continue;
713
+ pendingExitCode = 1;
710
714
  }
711
715
  if (event.type === "progress-update") {
712
716
  // Drive the renderer with scope information from the event
@@ -735,6 +739,9 @@ async function runConnect(rawSource, options) {
735
739
  lastError: event.message ?? "Connector run failed.",
736
740
  lastLogPath: event.logPath,
737
741
  connectionHealth: "error",
742
+ connectionHealthChangedAt: new Date().toISOString(),
743
+ connectionHealthReason: `runtime-error: ${event.message ?? "Connector run failed."}`,
744
+ connectionHealthRetryable: /timeout|ECONNREFUSED|ENOTFOUND|rate.?limit|50[234]|socket hang up/i.test(event.message ?? ""),
738
745
  });
739
746
  renderer?.fail(`Problem connecting ${displayName}.`);
740
747
  renderer?.detail(event.message ?? "Connector run failed.");
@@ -744,7 +751,7 @@ async function runConnect(rawSource, options) {
744
751
  status: CliOutcomeStatus.RUNTIME_ERROR,
745
752
  source: resolution.source,
746
753
  });
747
- terminalExitCode = 1;
754
+ pendingExitCode = 1;
748
755
  continue;
749
756
  }
750
757
  if (event.type === "headed-required") {
@@ -760,6 +767,9 @@ async function runConnect(rawSource, options) {
760
767
  lastResultPath: null,
761
768
  lastLogPath: event.logPath,
762
769
  connectionHealth: "needs_reauth",
770
+ connectionHealthChangedAt: new Date().toISOString(),
771
+ connectionHealthReason: `legacy-auth: ${event.message ?? "Legacy authentication is required."}`,
772
+ connectionHealthRetryable: false,
763
773
  });
764
774
  renderer?.fail(`Manual step required for ${displayName}.`);
765
775
  renderer?.detail(`Complete the browser step locally, then rerun vana connect ${source}.`);
@@ -768,8 +778,7 @@ async function runConnect(rawSource, options) {
768
778
  status: CliOutcomeStatus.LEGACY_AUTH,
769
779
  source: resolution.source,
770
780
  });
771
- terminalExitCode = 1;
772
- continue;
781
+ pendingExitCode = 1;
773
782
  }
774
783
  if (event.type === "collection-complete" && event.resultPath) {
775
784
  // Check if the result is actually an error object
@@ -781,13 +790,17 @@ async function runConnect(rawSource, options) {
781
790
  "error" in parsed &&
782
791
  Object.keys(parsed).length <= 2) {
783
792
  // Connector returned an error, not real data
793
+ const errorMsg = typeof parsed.error === "string"
794
+ ? parsed.error
795
+ : "Collection returned an error";
784
796
  await updateSourceState(source, {
785
797
  lastRunAt: new Date().toISOString(),
786
798
  lastRunOutcome: CliOutcomeStatus.RUNTIME_ERROR,
787
799
  connectionHealth: "error",
788
- lastError: typeof parsed.error === "string"
789
- ? parsed.error
790
- : "Collection returned an error",
800
+ connectionHealthChangedAt: new Date().toISOString(),
801
+ connectionHealthReason: `error-result: ${errorMsg}`,
802
+ connectionHealthRetryable: false,
803
+ lastError: errorMsg,
791
804
  lastLogPath: runLogPath ?? fetchLogPath,
792
805
  });
793
806
  renderer?.fail(`Problem connecting ${displayName}.`);
@@ -799,12 +812,15 @@ async function runConnect(rawSource, options) {
799
812
  status: CliOutcomeStatus.RUNTIME_ERROR,
800
813
  source,
801
814
  });
802
- terminalExitCode = 1;
815
+ pendingExitCode = 1;
803
816
  continue;
804
817
  }
805
818
  }
806
- catch {
807
- // Can't read/parse result proceed normally, let downstream handle it
819
+ catch (parseError) {
820
+ const msg = parseError instanceof Error ? parseError.message : "Unknown error";
821
+ await updateSourceState(source, {
822
+ lastError: `Failed to parse result file (${event.resultPath}): ${msg}`,
823
+ });
808
824
  }
809
825
  collectedResult = true;
810
826
  resultPath = event.resultPath;
@@ -845,8 +861,8 @@ async function runConnect(rawSource, options) {
845
861
  }));
846
862
  }
847
863
  }
848
- if (terminalExitCode !== null) {
849
- return terminalExitCode;
864
+ if (pendingExitCode !== null && !collectedResult) {
865
+ return pendingExitCode;
850
866
  }
851
867
  if (!collectedResult) {
852
868
  await updateSourceState(resolution.source, {
@@ -881,7 +897,10 @@ async function runConnect(rawSource, options) {
881
897
  lastError: ingestFailureMessage,
882
898
  lastResultPath: resultPath,
883
899
  lastLogPath: runLogPath ?? fetchLogPath ?? setupLogPath ?? null,
884
- connectionHealth: "healthy",
900
+ connectionHealth: pendingExitCode !== null ? undefined : "healthy",
901
+ connectionHealthChangedAt: pendingExitCode !== null ? undefined : new Date().toISOString(),
902
+ connectionHealthReason: pendingExitCode !== null ? undefined : "collection-complete",
903
+ connectionHealthRetryable: undefined,
885
904
  ingestScopes: ingestScopeResults,
886
905
  });
887
906
  // Build scope-aware success summary
@@ -904,6 +923,8 @@ async function runConnect(rawSource, options) {
904
923
  else {
905
924
  successSummary = `Collected your ${displayName} data and saved it locally.`;
906
925
  }
926
+ // Auto-schedule collection if no schedule exists (non-blocking)
927
+ await maybeAutoSchedule(emit, options).catch(() => { });
907
928
  // --- Phase 7: Success summary ---
908
929
  renderer?.success(`Connected ${displayName}.`);
909
930
  renderer?.detail(successSummary);
@@ -941,9 +962,9 @@ async function runConnect(rawSource, options) {
941
962
  source: resolution.source,
942
963
  resultPath,
943
964
  });
944
- // Auto-schedule collection if no schedule exists (non-blocking).
945
- // Runs after renderer is done to avoid cursor collision.
946
- await maybeAutoSchedule(options).catch(() => { });
965
+ if (pendingExitCode !== null) {
966
+ return pendingExitCode;
967
+ }
947
968
  return 0;
948
969
  }
949
970
  catch (error) {
@@ -1120,7 +1141,12 @@ async function runStatus(options) {
1120
1141
  if (stored) {
1121
1142
  sourceHealthMap[sourceId] = {
1122
1143
  connectionHealth: stored.connectionHealth,
1144
+ connectionHealthChangedAt: stored.connectionHealthChangedAt,
1145
+ connectionHealthReason: stored.connectionHealthReason,
1146
+ connectionHealthRetryable: stored.connectionHealthRetryable,
1123
1147
  lastCollectedAt: stored.lastCollectedAt,
1148
+ lastLogPath: stored.lastLogPath,
1149
+ lastError: stored.lastError,
1124
1150
  };
1125
1151
  }
1126
1152
  }
@@ -1187,6 +1213,16 @@ async function runStatus(options) {
1187
1213
  ? `collected ${formatRelativeTime(stored.lastCollectedAt)}`
1188
1214
  : "";
1189
1215
  emit.keyValue(` ${displayName}`, `${healthLabel}${staleTag} ${collectedAgo}`, healthTone);
1216
+ if ((health === "needs_reauth" || health === "error") &&
1217
+ stored?.connectionHealthReason) {
1218
+ const msg = formatHealthMessage(stored.connectionHealthReason);
1219
+ const ago = stored.connectionHealthChangedAt
1220
+ ? ` (${formatRelativeTime(stored.connectionHealthChangedAt)})`
1221
+ : "";
1222
+ if (msg) {
1223
+ emit.detail(` \u21b3 ${msg}${ago} Run \`vana connect ${sourceId}\``);
1224
+ }
1225
+ }
1190
1226
  if (health === "needs_reauth" && !needsReauthSource) {
1191
1227
  needsReauthSource = sourceId;
1192
1228
  }
@@ -2038,14 +2074,16 @@ async function runServerData(scope, options) {
2038
2074
  }
2039
2075
  // If PS is available, try to list remote scopes via client
2040
2076
  let remoteScopes = [];
2077
+ let remoteScopeFallbackReason;
2041
2078
  if (target.state === "available" && target.url) {
2042
2079
  try {
2043
2080
  const { createPersonalServerClient: createClient } = await import("../personal-server/client.js");
2044
2081
  const client = createClient({ url: target.url });
2045
2082
  remoteScopes = await client.listScopes(scope);
2046
2083
  }
2047
- catch {
2048
- // Auth required or PS unavailable — fall back to local
2084
+ catch (err) {
2085
+ remoteScopeFallbackReason =
2086
+ err instanceof Error ? err.message : "unknown error";
2049
2087
  }
2050
2088
  }
2051
2089
  // Use remote scopes if available, otherwise fall back to local
@@ -2063,6 +2101,7 @@ async function runServerData(scope, options) {
2063
2101
  count: scopeList.length,
2064
2102
  scopes: scopeList,
2065
2103
  source: remoteScopes.length > 0 ? "remote" : "local",
2104
+ ...(remoteScopeFallbackReason ? { remoteScopeFallbackReason } : {}),
2066
2105
  })}\n`);
2067
2106
  return 0;
2068
2107
  }
@@ -2321,6 +2360,10 @@ export async function gatherSourceStatuses(storedSources, metadata = {}) {
2321
2360
  connectorVersion: stored.connectorVersion,
2322
2361
  exportFrequency: stored.exportFrequency,
2323
2362
  lastCollectedAt: stored.lastCollectedAt,
2363
+ connectionHealth: stored.connectionHealth,
2364
+ connectionHealthChangedAt: stored.connectionHealthChangedAt,
2365
+ connectionHealthReason: stored.connectionHealthReason,
2366
+ connectionHealthRetryable: stored.connectionHealthRetryable,
2324
2367
  installed,
2325
2368
  sessionPresent: stored.sessionPresent ?? false,
2326
2369
  lastRunAt: stored.lastRunAt ?? null,
@@ -2472,6 +2515,14 @@ function formatSourceStatusDetails(source) {
2472
2515
  tone: "muted",
2473
2516
  });
2474
2517
  }
2518
+ if (source.connectionHealthReason && source.connectionHealth !== "healthy") {
2519
+ details.push({
2520
+ kind: "row",
2521
+ label: "Cause",
2522
+ value: source.connectionHealthReason,
2523
+ tone: "muted",
2524
+ });
2525
+ }
2475
2526
  if (source.lastRunAt) {
2476
2527
  details.push({
2477
2528
  kind: "row",
@@ -2632,6 +2683,28 @@ function buildLogsNextSteps(records) {
2632
2683
  "Check overall status with `vana status`.",
2633
2684
  ];
2634
2685
  }
2686
+ /** Derive a human-readable message from a stored `connectionHealthReason`. */
2687
+ export function formatHealthMessage(reason) {
2688
+ if (!reason)
2689
+ return null;
2690
+ const colonIndex = reason.indexOf(": ");
2691
+ const prefix = colonIndex > 0 ? reason.slice(0, colonIndex) : reason;
2692
+ const detail = colonIndex > 0 ? reason.slice(colonIndex + 2).replace(/\.$/, "") : "";
2693
+ switch (prefix) {
2694
+ case "needs-input":
2695
+ return `Requires interactive login${detail ? `: ${detail}` : ""}.`;
2696
+ case "legacy-auth":
2697
+ return `Needed a browser window${detail ? `: ${detail}` : ""}. Reconnect interactively.`;
2698
+ case "runtime-error":
2699
+ return `Collection failed${detail ? ` — ${detail}` : ""}.`;
2700
+ case "error-result":
2701
+ return `Connector returned an error${detail ? `: ${detail}` : ""}.`;
2702
+ case "collection-complete":
2703
+ return null; // No message needed for healthy state
2704
+ default:
2705
+ return reason; // Graceful fallback for unknown prefixes
2706
+ }
2707
+ }
2635
2708
  /** Extract a `vana ...` command from a next-step sentence wrapped in backticks. */
2636
2709
  function extractCommand(sentence) {
2637
2710
  const match = sentence.match(/`(vana\s[^`]+)`/);
@@ -3280,7 +3353,7 @@ async function getExistingScheduleInterval() {
3280
3353
  }
3281
3354
  return null;
3282
3355
  }
3283
- async function maybeAutoSchedule(options) {
3356
+ async function maybeAutoSchedule(emit, options) {
3284
3357
  // Skip if --no-input (detached/agent context shouldn't create schedules)
3285
3358
  if (options.noInput)
3286
3359
  return;
@@ -3291,6 +3364,7 @@ async function maybeAutoSchedule(options) {
3291
3364
  if (existing !== null)
3292
3365
  return; // Schedule already exists
3293
3366
  await runScheduleAdd("daily", { json: false, quiet: true });
3367
+ emit.detail("Auto-scheduled daily collection.");
3294
3368
  }
3295
3369
  function generateLaunchdPlist(vanaBinary, intervalSeconds) {
3296
3370
  const logsPath = path.join(getLogsDir(), "schedule.log");