@opendatalabs/connect 0.9.2 → 0.9.4

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,38 @@ 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 updateCheckPromise;
429
+ const cache = await readUpdateCheck();
430
+ if (cache && isNewerVersion(cliVersion, cache.latestVersion)) {
431
+ const { upgrade } = getLifecycleCommands(installMethod, getCliChannel(cliVersion));
432
+ process.stderr.write(`\nUpdate available: ${cliVersion} → ${cache.latestVersion}\nRun: ${upgrade}\n`);
433
+ }
434
+ }
435
+ catch {
436
+ // Never block exit for update notification failures
437
+ }
440
438
  }
439
+ return Number(process.exitCode ?? 0);
441
440
  }
442
441
  async function runConnect(rawSource, options) {
443
442
  const source = rawSource.toLowerCase();
@@ -450,7 +449,7 @@ async function runConnect(rawSource, options) {
450
449
  let setupLogPath;
451
450
  let fetchLogPath;
452
451
  let runLogPath;
453
- let terminalExitCode = null;
452
+ let pendingExitCode = null;
454
453
  try {
455
454
  // Title
456
455
  renderer?.title(displayName);
@@ -688,7 +687,7 @@ async function runConnect(rawSource, options) {
688
687
  if (event.logPath) {
689
688
  runLogPath = event.logPath;
690
689
  }
691
- if (terminalExitCode !== null) {
690
+ if (pendingExitCode !== null && event.type !== "collection-complete") {
692
691
  continue;
693
692
  }
694
693
  if (event.type === "needs-input") {
@@ -698,6 +697,9 @@ async function runConnect(rawSource, options) {
698
697
  lastError: event.message ?? "Input required.",
699
698
  lastLogPath: event.logPath,
700
699
  connectionHealth: "needs_reauth",
700
+ connectionHealthChangedAt: new Date().toISOString(),
701
+ connectionHealthReason: `needs-input: ${event.message ?? "Input required."}`,
702
+ connectionHealthRetryable: false,
701
703
  });
702
704
  emit.event({
703
705
  type: "outcome",
@@ -705,8 +707,7 @@ async function runConnect(rawSource, options) {
705
707
  source: resolution.source,
706
708
  });
707
709
  renderer?.fail(`${displayName} needs credentials. Run without --no-input to authenticate.`);
708
- terminalExitCode = 1;
709
- continue;
710
+ pendingExitCode = 1;
710
711
  }
711
712
  if (event.type === "progress-update") {
712
713
  // Drive the renderer with scope information from the event
@@ -735,6 +736,9 @@ async function runConnect(rawSource, options) {
735
736
  lastError: event.message ?? "Connector run failed.",
736
737
  lastLogPath: event.logPath,
737
738
  connectionHealth: "error",
739
+ connectionHealthChangedAt: new Date().toISOString(),
740
+ connectionHealthReason: `runtime-error: ${event.message ?? "Connector run failed."}`,
741
+ connectionHealthRetryable: /timeout|ECONNREFUSED|ENOTFOUND|rate.?limit|50[234]|socket hang up/i.test(event.message ?? ""),
738
742
  });
739
743
  renderer?.fail(`Problem connecting ${displayName}.`);
740
744
  renderer?.detail(event.message ?? "Connector run failed.");
@@ -744,7 +748,7 @@ async function runConnect(rawSource, options) {
744
748
  status: CliOutcomeStatus.RUNTIME_ERROR,
745
749
  source: resolution.source,
746
750
  });
747
- terminalExitCode = 1;
751
+ pendingExitCode = 1;
748
752
  continue;
749
753
  }
750
754
  if (event.type === "headed-required") {
@@ -760,6 +764,9 @@ async function runConnect(rawSource, options) {
760
764
  lastResultPath: null,
761
765
  lastLogPath: event.logPath,
762
766
  connectionHealth: "needs_reauth",
767
+ connectionHealthChangedAt: new Date().toISOString(),
768
+ connectionHealthReason: `legacy-auth: ${event.message ?? "Legacy authentication is required."}`,
769
+ connectionHealthRetryable: false,
763
770
  });
764
771
  renderer?.fail(`Manual step required for ${displayName}.`);
765
772
  renderer?.detail(`Complete the browser step locally, then rerun vana connect ${source}.`);
@@ -768,8 +775,7 @@ async function runConnect(rawSource, options) {
768
775
  status: CliOutcomeStatus.LEGACY_AUTH,
769
776
  source: resolution.source,
770
777
  });
771
- terminalExitCode = 1;
772
- continue;
778
+ pendingExitCode = 1;
773
779
  }
774
780
  if (event.type === "collection-complete" && event.resultPath) {
775
781
  // Check if the result is actually an error object
@@ -781,13 +787,17 @@ async function runConnect(rawSource, options) {
781
787
  "error" in parsed &&
782
788
  Object.keys(parsed).length <= 2) {
783
789
  // Connector returned an error, not real data
790
+ const errorMsg = typeof parsed.error === "string"
791
+ ? parsed.error
792
+ : "Collection returned an error";
784
793
  await updateSourceState(source, {
785
794
  lastRunAt: new Date().toISOString(),
786
795
  lastRunOutcome: CliOutcomeStatus.RUNTIME_ERROR,
787
796
  connectionHealth: "error",
788
- lastError: typeof parsed.error === "string"
789
- ? parsed.error
790
- : "Collection returned an error",
797
+ connectionHealthChangedAt: new Date().toISOString(),
798
+ connectionHealthReason: `error-result: ${errorMsg}`,
799
+ connectionHealthRetryable: false,
800
+ lastError: errorMsg,
791
801
  lastLogPath: runLogPath ?? fetchLogPath,
792
802
  });
793
803
  renderer?.fail(`Problem connecting ${displayName}.`);
@@ -799,12 +809,15 @@ async function runConnect(rawSource, options) {
799
809
  status: CliOutcomeStatus.RUNTIME_ERROR,
800
810
  source,
801
811
  });
802
- terminalExitCode = 1;
812
+ pendingExitCode = 1;
803
813
  continue;
804
814
  }
805
815
  }
806
- catch {
807
- // Can't read/parse result proceed normally, let downstream handle it
816
+ catch (parseError) {
817
+ const msg = parseError instanceof Error ? parseError.message : "Unknown error";
818
+ await updateSourceState(source, {
819
+ lastError: `Failed to parse result file (${event.resultPath}): ${msg}`,
820
+ });
808
821
  }
809
822
  collectedResult = true;
810
823
  resultPath = event.resultPath;
@@ -845,8 +858,8 @@ async function runConnect(rawSource, options) {
845
858
  }));
846
859
  }
847
860
  }
848
- if (terminalExitCode !== null) {
849
- return terminalExitCode;
861
+ if (pendingExitCode !== null && !collectedResult) {
862
+ return pendingExitCode;
850
863
  }
851
864
  if (!collectedResult) {
852
865
  await updateSourceState(resolution.source, {
@@ -881,7 +894,10 @@ async function runConnect(rawSource, options) {
881
894
  lastError: ingestFailureMessage,
882
895
  lastResultPath: resultPath,
883
896
  lastLogPath: runLogPath ?? fetchLogPath ?? setupLogPath ?? null,
884
- connectionHealth: "healthy",
897
+ connectionHealth: pendingExitCode !== null ? undefined : "healthy",
898
+ connectionHealthChangedAt: pendingExitCode !== null ? undefined : new Date().toISOString(),
899
+ connectionHealthReason: pendingExitCode !== null ? undefined : "collection-complete",
900
+ connectionHealthRetryable: undefined,
885
901
  ingestScopes: ingestScopeResults,
886
902
  });
887
903
  // Build scope-aware success summary
@@ -904,6 +920,8 @@ async function runConnect(rawSource, options) {
904
920
  else {
905
921
  successSummary = `Collected your ${displayName} data and saved it locally.`;
906
922
  }
923
+ // Auto-schedule collection if no schedule exists (non-blocking)
924
+ await maybeAutoSchedule(emit, options).catch(() => { });
907
925
  // --- Phase 7: Success summary ---
908
926
  renderer?.success(`Connected ${displayName}.`);
909
927
  renderer?.detail(successSummary);
@@ -941,9 +959,9 @@ async function runConnect(rawSource, options) {
941
959
  source: resolution.source,
942
960
  resultPath,
943
961
  });
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(() => { });
962
+ if (pendingExitCode !== null) {
963
+ return pendingExitCode;
964
+ }
947
965
  return 0;
948
966
  }
949
967
  catch (error) {
@@ -1120,7 +1138,12 @@ async function runStatus(options) {
1120
1138
  if (stored) {
1121
1139
  sourceHealthMap[sourceId] = {
1122
1140
  connectionHealth: stored.connectionHealth,
1141
+ connectionHealthChangedAt: stored.connectionHealthChangedAt,
1142
+ connectionHealthReason: stored.connectionHealthReason,
1143
+ connectionHealthRetryable: stored.connectionHealthRetryable,
1123
1144
  lastCollectedAt: stored.lastCollectedAt,
1145
+ lastLogPath: stored.lastLogPath,
1146
+ lastError: stored.lastError,
1124
1147
  };
1125
1148
  }
1126
1149
  }
@@ -1187,6 +1210,16 @@ async function runStatus(options) {
1187
1210
  ? `collected ${formatRelativeTime(stored.lastCollectedAt)}`
1188
1211
  : "";
1189
1212
  emit.keyValue(` ${displayName}`, `${healthLabel}${staleTag} ${collectedAgo}`, healthTone);
1213
+ if ((health === "needs_reauth" || health === "error") &&
1214
+ stored?.connectionHealthReason) {
1215
+ const msg = formatHealthMessage(stored.connectionHealthReason);
1216
+ const ago = stored.connectionHealthChangedAt
1217
+ ? ` (${formatRelativeTime(stored.connectionHealthChangedAt)})`
1218
+ : "";
1219
+ if (msg) {
1220
+ emit.detail(` \u21b3 ${msg}${ago} Run \`vana connect ${sourceId}\``);
1221
+ }
1222
+ }
1190
1223
  if (health === "needs_reauth" && !needsReauthSource) {
1191
1224
  needsReauthSource = sourceId;
1192
1225
  }
@@ -2038,14 +2071,16 @@ async function runServerData(scope, options) {
2038
2071
  }
2039
2072
  // If PS is available, try to list remote scopes via client
2040
2073
  let remoteScopes = [];
2074
+ let remoteScopeFallbackReason;
2041
2075
  if (target.state === "available" && target.url) {
2042
2076
  try {
2043
2077
  const { createPersonalServerClient: createClient } = await import("../personal-server/client.js");
2044
2078
  const client = createClient({ url: target.url });
2045
2079
  remoteScopes = await client.listScopes(scope);
2046
2080
  }
2047
- catch {
2048
- // Auth required or PS unavailable — fall back to local
2081
+ catch (err) {
2082
+ remoteScopeFallbackReason =
2083
+ err instanceof Error ? err.message : "unknown error";
2049
2084
  }
2050
2085
  }
2051
2086
  // Use remote scopes if available, otherwise fall back to local
@@ -2063,6 +2098,7 @@ async function runServerData(scope, options) {
2063
2098
  count: scopeList.length,
2064
2099
  scopes: scopeList,
2065
2100
  source: remoteScopes.length > 0 ? "remote" : "local",
2101
+ ...(remoteScopeFallbackReason ? { remoteScopeFallbackReason } : {}),
2066
2102
  })}\n`);
2067
2103
  return 0;
2068
2104
  }
@@ -2321,6 +2357,10 @@ export async function gatherSourceStatuses(storedSources, metadata = {}) {
2321
2357
  connectorVersion: stored.connectorVersion,
2322
2358
  exportFrequency: stored.exportFrequency,
2323
2359
  lastCollectedAt: stored.lastCollectedAt,
2360
+ connectionHealth: stored.connectionHealth,
2361
+ connectionHealthChangedAt: stored.connectionHealthChangedAt,
2362
+ connectionHealthReason: stored.connectionHealthReason,
2363
+ connectionHealthRetryable: stored.connectionHealthRetryable,
2324
2364
  installed,
2325
2365
  sessionPresent: stored.sessionPresent ?? false,
2326
2366
  lastRunAt: stored.lastRunAt ?? null,
@@ -2472,6 +2512,14 @@ function formatSourceStatusDetails(source) {
2472
2512
  tone: "muted",
2473
2513
  });
2474
2514
  }
2515
+ if (source.connectionHealthReason && source.connectionHealth !== "healthy") {
2516
+ details.push({
2517
+ kind: "row",
2518
+ label: "Cause",
2519
+ value: source.connectionHealthReason,
2520
+ tone: "muted",
2521
+ });
2522
+ }
2475
2523
  if (source.lastRunAt) {
2476
2524
  details.push({
2477
2525
  kind: "row",
@@ -2632,6 +2680,28 @@ function buildLogsNextSteps(records) {
2632
2680
  "Check overall status with `vana status`.",
2633
2681
  ];
2634
2682
  }
2683
+ /** Derive a human-readable message from a stored `connectionHealthReason`. */
2684
+ export function formatHealthMessage(reason) {
2685
+ if (!reason)
2686
+ return null;
2687
+ const colonIndex = reason.indexOf(": ");
2688
+ const prefix = colonIndex > 0 ? reason.slice(0, colonIndex) : reason;
2689
+ const detail = colonIndex > 0 ? reason.slice(colonIndex + 2) : "";
2690
+ switch (prefix) {
2691
+ case "needs-input":
2692
+ return `Requires interactive login${detail ? `: ${detail}` : ""}.`;
2693
+ case "legacy-auth":
2694
+ return `Needed a browser window${detail ? `: ${detail}` : ""}. Reconnect interactively.`;
2695
+ case "runtime-error":
2696
+ return `Collection failed${detail ? ` — ${detail}` : ""}.`;
2697
+ case "error-result":
2698
+ return `Connector returned an error${detail ? `: ${detail}` : ""}.`;
2699
+ case "collection-complete":
2700
+ return null; // No message needed for healthy state
2701
+ default:
2702
+ return reason; // Graceful fallback for unknown prefixes
2703
+ }
2704
+ }
2635
2705
  /** Extract a `vana ...` command from a next-step sentence wrapped in backticks. */
2636
2706
  function extractCommand(sentence) {
2637
2707
  const match = sentence.match(/`(vana\s[^`]+)`/);
@@ -3280,7 +3350,7 @@ async function getExistingScheduleInterval() {
3280
3350
  }
3281
3351
  return null;
3282
3352
  }
3283
- async function maybeAutoSchedule(options) {
3353
+ async function maybeAutoSchedule(emit, options) {
3284
3354
  // Skip if --no-input (detached/agent context shouldn't create schedules)
3285
3355
  if (options.noInput)
3286
3356
  return;
@@ -3291,6 +3361,7 @@ async function maybeAutoSchedule(options) {
3291
3361
  if (existing !== null)
3292
3362
  return; // Schedule already exists
3293
3363
  await runScheduleAdd("daily", { json: false, quiet: true });
3364
+ emit.detail("Auto-scheduled daily collection.");
3294
3365
  }
3295
3366
  function generateLaunchdPlist(vanaBinary, intervalSeconds) {
3296
3367
  const logsPath = path.join(getLogsDir(), "schedule.log");