@hasna/connectors 1.3.45 → 1.3.46

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 (114) hide show
  1. package/README.md +25 -0
  2. package/bin/index.js +792 -130
  3. package/bin/mcp.js +438 -52
  4. package/bin/serve.js +438 -52
  5. package/connectors/cloudflare-workers/.env.example +5 -0
  6. package/connectors/cloudflare-workers/AGENTS.md +11 -0
  7. package/connectors/cloudflare-workers/CLAUDE.md +11 -0
  8. package/connectors/cloudflare-workers/README.md +45 -0
  9. package/connectors/cloudflare-workers/package.json +47 -0
  10. package/connectors/cloudflare-workers/src/api/client.test.ts +144 -0
  11. package/connectors/cloudflare-workers/src/api/client.ts +386 -0
  12. package/connectors/cloudflare-workers/src/api/index.ts +1 -0
  13. package/connectors/cloudflare-workers/src/cli/index.ts +206 -0
  14. package/connectors/cloudflare-workers/src/index.ts +16 -0
  15. package/connectors/cloudflare-workers/src/types/index.ts +53 -0
  16. package/connectors/cloudflare-workers/src/utils/config.ts +61 -0
  17. package/connectors/cloudflare-workers/src/utils/output.ts +26 -0
  18. package/connectors/cloudflare-workers/tsconfig.build.json +4 -0
  19. package/connectors/cloudflare-workers/tsconfig.json +14 -0
  20. package/connectors/tidio/.env.example +3 -2
  21. package/connectors/tidio/CLAUDE.md +21 -96
  22. package/connectors/tidio/README.md +22 -18
  23. package/connectors/tidio/package.json +4 -2
  24. package/connectors/tidio/src/api/client.ts +26 -15
  25. package/connectors/tidio/src/api/index.ts +185 -128
  26. package/connectors/tidio/src/api/tidio.test.ts +175 -44
  27. package/connectors/tidio/src/cli/index.ts +154 -388
  28. package/connectors/tidio/src/index.ts +5 -3
  29. package/connectors/tidio/src/types/index.ts +135 -82
  30. package/connectors/tidio/src/utils/config.ts +90 -44
  31. package/connectors/wandb/.env.example +7 -0
  32. package/connectors/wandb/AGENTS.md +43 -0
  33. package/connectors/wandb/CLAUDE.md +67 -0
  34. package/connectors/wandb/README.md +65 -0
  35. package/connectors/wandb/package.json +52 -0
  36. package/connectors/wandb/src/api/client.test.ts +116 -0
  37. package/connectors/wandb/src/api/client.ts +83 -0
  38. package/connectors/wandb/src/api/graphql.ts +9 -0
  39. package/connectors/wandb/src/api/index.ts +36 -0
  40. package/connectors/wandb/src/api/projects.ts +36 -0
  41. package/connectors/wandb/src/api/viewer.ts +20 -0
  42. package/connectors/wandb/src/cli/index.ts +265 -0
  43. package/connectors/wandb/src/index.ts +22 -0
  44. package/connectors/wandb/src/types/index.ts +75 -0
  45. package/connectors/wandb/src/utils/config.ts +164 -0
  46. package/connectors/wandb/src/utils/output.ts +119 -0
  47. package/connectors/wandb/tsconfig.json +16 -0
  48. package/connectors/weights-biases/.env.example +7 -0
  49. package/connectors/weights-biases/CLAUDE.md +65 -0
  50. package/connectors/weights-biases/README.md +62 -0
  51. package/connectors/weights-biases/package.json +53 -0
  52. package/connectors/weights-biases/src/api/client.test.ts +46 -0
  53. package/connectors/weights-biases/src/api/client.ts +119 -0
  54. package/connectors/weights-biases/src/api/events.ts +18 -0
  55. package/connectors/weights-biases/src/api/index.ts +56 -0
  56. package/connectors/weights-biases/src/api/runs.ts +28 -0
  57. package/connectors/weights-biases/src/api/search.ts +10 -0
  58. package/connectors/weights-biases/src/cli/index.ts +289 -0
  59. package/connectors/weights-biases/src/index.ts +19 -0
  60. package/connectors/weights-biases/src/types/index.ts +92 -0
  61. package/connectors/weights-biases/src/utils/config.ts +168 -0
  62. package/connectors/weights-biases/src/utils/output.ts +116 -0
  63. package/connectors/weights-biases/tsconfig.json +16 -0
  64. package/connectors/zoominfo/.env.example +4 -0
  65. package/connectors/zoominfo/AGENTS.md +26 -0
  66. package/connectors/zoominfo/CLAUDE.md +20 -0
  67. package/connectors/zoominfo/README.md +69 -0
  68. package/connectors/zoominfo/package.json +52 -0
  69. package/connectors/zoominfo/src/api/client.test.ts +140 -0
  70. package/connectors/zoominfo/src/api/client.ts +228 -0
  71. package/connectors/zoominfo/src/api/index.ts +1 -0
  72. package/connectors/zoominfo/src/cli/index.ts +179 -0
  73. package/connectors/zoominfo/src/index.ts +3 -0
  74. package/connectors/zoominfo/src/types/index.ts +39 -0
  75. package/connectors/zoominfo/src/utils/config.ts +61 -0
  76. package/connectors/zoominfo/src/utils/output.ts +27 -0
  77. package/connectors/zoominfo/tsconfig.json +20 -0
  78. package/connectors/zotero/.env.example +7 -0
  79. package/connectors/zotero/.npmrc.example +2 -0
  80. package/connectors/zotero/AGENTS.md +40 -0
  81. package/connectors/zotero/CLAUDE.md +69 -0
  82. package/connectors/zotero/README.md +91 -0
  83. package/connectors/zotero/package.json +43 -0
  84. package/connectors/zotero/src/api/attachments.ts +130 -0
  85. package/connectors/zotero/src/api/client.test.ts +166 -0
  86. package/connectors/zotero/src/api/client.ts +169 -0
  87. package/connectors/zotero/src/api/collections.ts +26 -0
  88. package/connectors/zotero/src/api/index.ts +45 -0
  89. package/connectors/zotero/src/api/items.ts +64 -0
  90. package/connectors/zotero/src/cli/index.ts +386 -0
  91. package/connectors/zotero/src/index.ts +28 -0
  92. package/connectors/zotero/src/types/index.ts +129 -0
  93. package/connectors/zotero/src/utils/config.ts +245 -0
  94. package/connectors/zotero/src/utils/output.ts +119 -0
  95. package/connectors/zotero/tsconfig.json +16 -0
  96. package/connectors/zymbly/.env.example +2 -0
  97. package/connectors/zymbly/AGENTS.md +11 -0
  98. package/connectors/zymbly/CLAUDE.md +5 -0
  99. package/connectors/zymbly/README.md +35 -0
  100. package/connectors/zymbly/package.json +52 -0
  101. package/connectors/zymbly/src/api/client.test.ts +110 -0
  102. package/connectors/zymbly/src/api/client.ts +166 -0
  103. package/connectors/zymbly/src/api/index.ts +1 -0
  104. package/connectors/zymbly/src/cli/index.ts +156 -0
  105. package/connectors/zymbly/src/index.ts +12 -0
  106. package/connectors/zymbly/src/types/index.ts +34 -0
  107. package/connectors/zymbly/src/utils/config.ts +52 -0
  108. package/connectors/zymbly/src/utils/output.ts +17 -0
  109. package/connectors/zymbly/tsconfig.json +13 -0
  110. package/dist/index.js +45 -2
  111. package/dist/lib/compact-output.d.ts +40 -0
  112. package/dist/mcp/tools/jobs.d.ts +32 -0
  113. package/dist/mcp/tools/jobs.test.d.ts +1 -0
  114. package/package.json +3 -2
package/bin/index.js CHANGED
@@ -1909,7 +1909,7 @@ var package_default;
1909
1909
  var init_package = __esm(() => {
1910
1910
  package_default = {
1911
1911
  name: "@hasna/connectors",
1912
- version: "1.3.45",
1912
+ version: "1.3.46",
1913
1913
  description: "Open source connector library - Install API connectors with a single command",
1914
1914
  type: "module",
1915
1915
  bin: {
@@ -1941,11 +1941,12 @@ var init_package = __esm(() => {
1941
1941
  "build:dashboard": "cd dashboard && bun run build",
1942
1942
  postinstall: 'mkdir -p $HOME/.hasna/connectors 2>/dev/null || true; [ "$SKIP_DASHBOARD" = "1" ] || [ ! -f dashboard/package.json ] || [ -d dashboard/node_modules ] || (cd dashboard && bun install)',
1943
1943
  dev: "bun run ./src/cli/index.tsx",
1944
+ "check:package-secrets": "bun run scripts/check-package-secrets.ts",
1944
1945
  typecheck: "tsc --noEmit && tsc -p tsconfig.social.json --noEmit",
1945
1946
  test: "bun test",
1946
1947
  "secrets:scan:npmrc": "bun run scripts/scan-npmrc-auth.mjs",
1947
1948
  "sdk:build": "cd sdk && bun run build",
1948
- prepublishOnly: "bun run secrets:scan:npmrc && bun run build && bun test"
1949
+ prepublishOnly: "bun run secrets:scan:npmrc && bun run check:package-secrets && bun run build && bun test"
1949
1950
  },
1950
1951
  keywords: [
1951
1952
  "connectors",
@@ -22235,6 +22236,13 @@ var init_ai_ml = __esm(() => {
22235
22236
  category: "AI & ML",
22236
22237
  tags: ["transcription", "meetings", "ai"]
22237
22238
  },
22239
+ {
22240
+ name: "wandb",
22241
+ displayName: "Weights & Biases",
22242
+ description: "ML experiment tracking and run management via GraphQL",
22243
+ category: "AI & ML",
22244
+ tags: ["ml", "experiments", "tracking"]
22245
+ },
22238
22246
  {
22239
22247
  name: "wato",
22240
22248
  displayName: "Wato",
@@ -22242,6 +22250,13 @@ var init_ai_ml = __esm(() => {
22242
22250
  category: "AI & ML",
22243
22251
  tags: ["ai", "agents", "memory", "workflows"]
22244
22252
  },
22253
+ {
22254
+ name: "weights-biases",
22255
+ displayName: "Weights & Biases",
22256
+ description: "ML experiment tracking (runs, events, search)",
22257
+ category: "AI & ML",
22258
+ tags: ["ai", "ml", "wandb", "experiment-tracking"]
22259
+ },
22245
22260
  {
22246
22261
  name: "wavelineextract",
22247
22262
  displayName: "Waveline Extract",
@@ -23886,6 +23901,20 @@ var init_business_tools = __esm(() => {
23886
23901
  description: "HR platform for employees, leave, attendance, and timesheets",
23887
23902
  category: "Business Tools",
23888
23903
  tags: ["hr", "employees", "zoho"]
23904
+ },
23905
+ {
23906
+ name: "zymbly",
23907
+ displayName: "Zymbly",
23908
+ description: "Aircraft maintenance and CMMS platform for work orders, parts, and maintenance notes",
23909
+ category: "Business Tools",
23910
+ tags: ["maintenance", "aircraft", "work-orders", "cmms"]
23911
+ },
23912
+ {
23913
+ name: "zotero",
23914
+ displayName: "Zotero",
23915
+ description: "Bibliography and reference library API for items, collections, and attachments",
23916
+ category: "Business Tools",
23917
+ tags: ["bibliography", "citations", "research", "library", "references"]
23889
23918
  }
23890
23919
  ];
23891
23920
  });
@@ -26414,6 +26443,13 @@ var init_developer_tools = __esm(() => {
26414
26443
  category: "Developer Tools",
26415
26444
  tags: ["cdn", "dns", "edge"]
26416
26445
  },
26446
+ {
26447
+ name: "cloudflare-workers",
26448
+ displayName: "Cloudflare Workers",
26449
+ description: "Cloudflare Workers REST API connector for scripts, multipart uploads, routes, deployments, versions, secrets, cron schedules, tails, subdomains, beta Workers, and scoped raw account or zone requests",
26450
+ category: "Developer Tools",
26451
+ tags: ["cloudflare", "workers", "serverless", "edge", "deployments", "developer-tools"]
26452
+ },
26417
26453
  {
26418
26454
  name: "googlecloud",
26419
26455
  displayName: "Google Cloud",
@@ -28821,6 +28857,13 @@ var init_marketing_sales = __esm(() => {
28821
28857
  description: "Reviews and user-generated content platform for e-commerce",
28822
28858
  category: "Marketing & Sales",
28823
28859
  tags: ["reviews", "ugc", "ecommerce", "marketing"]
28860
+ },
28861
+ {
28862
+ name: "zoominfo",
28863
+ displayName: "ZoomInfo",
28864
+ description: "ZoomInfo B2B sales intelligence API for contact and company search, enrichment, lookup, and output field discovery",
28865
+ category: "Marketing & Sales",
28866
+ tags: ["sales-intelligence", "enrichment", "contacts", "companies", "b2b"]
28824
28867
  }
28825
28868
  ];
28826
28869
  });
@@ -31471,6 +31514,79 @@ var init_installer = __esm(() => {
31471
31514
  CONNECTORS_DIR = resolveConnectorsDir();
31472
31515
  });
31473
31516
 
31517
+ // src/lib/compact-output.ts
31518
+ function parseNonNegativeInt(raw, flag) {
31519
+ if (raw === undefined)
31520
+ return { value: undefined };
31521
+ if (!/^\d+$/.test(raw)) {
31522
+ return {
31523
+ value: undefined,
31524
+ error: `Invalid value for ${flag}: '${raw}'. Expected a non-negative integer.`
31525
+ };
31526
+ }
31527
+ return { value: parseInt(raw, 10) };
31528
+ }
31529
+ function normalizeLimit(value, fallback, max = MAX_COMPACT_LIMIT) {
31530
+ if (value === undefined || !Number.isFinite(value))
31531
+ return fallback;
31532
+ return Math.min(Math.max(1, Math.floor(value)), max);
31533
+ }
31534
+ function parseCursor(raw) {
31535
+ if (raw === undefined || raw === "")
31536
+ return { value: 0 };
31537
+ return parseNonNegativeInt(raw, "--cursor");
31538
+ }
31539
+ function pageItems(items, options) {
31540
+ const offset = Math.max(0, options.offset ?? 0);
31541
+ const limit = options.limit === undefined ? undefined : Math.max(1, Math.floor(options.limit));
31542
+ const paged = limit === undefined ? items.slice(offset) : items.slice(offset, offset + limit);
31543
+ const nextOffset = limit === undefined || offset + paged.length >= items.length ? null : offset + paged.length;
31544
+ return {
31545
+ items: paged,
31546
+ total: items.length,
31547
+ offset,
31548
+ limit: limit ?? null,
31549
+ nextOffset
31550
+ };
31551
+ }
31552
+ function truncateText(value, max = DEFAULT_TEXT_WIDTH) {
31553
+ const text = (value ?? "").replace(/\s+/g, " ").trim();
31554
+ if (text.length <= max)
31555
+ return text;
31556
+ if (max <= 3)
31557
+ return text.slice(0, max);
31558
+ return `${text.slice(0, max - 3).trimEnd()}...`;
31559
+ }
31560
+ function firstNonEmptyLines(value, maxLines, maxWidth = DEFAULT_TEXT_WIDTH) {
31561
+ return (value ?? "").split(`
31562
+ `).map((line) => line.trim()).filter(Boolean).slice(0, maxLines).map((line) => truncateText(line, maxWidth));
31563
+ }
31564
+ function compactConnector(connector, descriptionWidth = DEFAULT_TEXT_WIDTH) {
31565
+ return {
31566
+ name: connector.name,
31567
+ displayName: connector.displayName,
31568
+ version: connector.version,
31569
+ category: connector.category,
31570
+ description: truncateText(connector.description, descriptionWidth)
31571
+ };
31572
+ }
31573
+ function maybeTruncateOutput(text, options = {}) {
31574
+ const enabled = options.enabled ?? true;
31575
+ const maxChars = options.maxChars ?? DEFAULT_OUTPUT_CHARS;
31576
+ if (!enabled || text.length <= maxChars) {
31577
+ return { text, truncated: false };
31578
+ }
31579
+ const hint = options.hint ?? "Use --verbose for full output.";
31580
+ const omitted = text.length - maxChars;
31581
+ return {
31582
+ text: `${text.slice(0, maxChars).trimEnd()}
31583
+
31584
+ [truncated ${omitted} chars] ${hint}`,
31585
+ truncated: true
31586
+ };
31587
+ }
31588
+ var DEFAULT_COMPACT_LIMIT = 20, DEFAULT_MCP_LIMIT = 20, MAX_COMPACT_LIMIT = 100, DEFAULT_TEXT_WIDTH = 96, DEFAULT_OUTPUT_CHARS = 6000;
31589
+
31474
31590
  // src/lib/lock.ts
31475
31591
  import { openSync, closeSync, unlinkSync as unlinkSync2, existsSync as existsSync14, statSync as statSync6 } from "fs";
31476
31592
  import { mkdirSync as mkdirSync7 } from "fs";
@@ -49612,26 +49728,44 @@ function registerDiscoveryTools(server, stripped) {
49612
49728
  server.registerTool("search_connectors", {
49613
49729
  title: "Search Connectors",
49614
49730
  description: "Search connectors by name or keyword.",
49615
- inputSchema: { query: exports_external.string() }
49616
- }, async ({ query }) => {
49731
+ inputSchema: {
49732
+ query: exports_external.string(),
49733
+ limit: exports_external.number().optional(),
49734
+ verbose: exports_external.boolean().optional()
49735
+ }
49736
+ }, async ({ query, limit, verbose }) => {
49617
49737
  const { getInstalledConnectors: getInstalledConnectors2 } = await Promise.resolve().then(() => (init_installer(), exports_installer));
49618
49738
  const { getPromotedConnectors: getPromotedConnectors2 } = await Promise.resolve().then(() => (init_promotions(), exports_promotions));
49619
49739
  const { getUsageMap: getUsageMap2 } = await Promise.resolve().then(() => (init_usage(), exports_usage));
49740
+ const resultLimit = normalizeLimit(limit, DEFAULT_MCP_LIMIT);
49620
49741
  const results = searchConnectors(query, {
49621
49742
  installed: getInstalledConnectors2(),
49622
49743
  promoted: getPromotedConnectors2(),
49623
- usage: getUsageMap2()
49744
+ usage: getUsageMap2(),
49745
+ limit: resultLimit
49624
49746
  });
49625
- return stripped(JSON.stringify(results.map((c) => ({ name: c.name, displayName: c.displayName, version: c.version, category: c.category, description: c.description, score: c.score, badges: c.badges })), null, 2));
49747
+ return stripped(JSON.stringify(results.map((c) => ({
49748
+ name: c.name,
49749
+ displayName: c.displayName,
49750
+ version: c.version,
49751
+ category: c.category,
49752
+ description: verbose ? c.description : truncateText(c.description, 120),
49753
+ score: c.score,
49754
+ badges: c.badges,
49755
+ ...verbose ? { matchReasons: c.matchReasons } : {}
49756
+ })), null, 2));
49626
49757
  });
49627
49758
  server.registerTool("list_connectors", {
49628
49759
  title: "List Connectors",
49629
- description: "List connectors. category filters; compact=true returns names only.",
49760
+ description: "List connectors with compact, paged defaults. Use verbose=true or compact=false for full metadata.",
49630
49761
  inputSchema: {
49631
49762
  category: exports_external.string().optional(),
49632
- compact: exports_external.boolean().optional()
49763
+ compact: exports_external.boolean().optional(),
49764
+ limit: exports_external.number().optional(),
49765
+ cursor: exports_external.string().optional(),
49766
+ verbose: exports_external.boolean().optional()
49633
49767
  }
49634
- }, async ({ category, compact }) => {
49768
+ }, async ({ category, compact, limit, cursor, verbose }) => {
49635
49769
  let connectors18 = CONNECTORS;
49636
49770
  if (category) {
49637
49771
  const matched = CATEGORIES.find((c) => c.toLowerCase() === category.toLowerCase());
@@ -49648,23 +49782,42 @@ function registerDiscoveryTools(server, stripped) {
49648
49782
  }
49649
49783
  connectors18 = getConnectorsByCategory(matched);
49650
49784
  }
49651
- const data = compact ? connectors18.map((c) => c.name) : connectors18.map((c) => ({
49785
+ const parsedCursor = parseCursor(cursor);
49786
+ if (parsedCursor.error) {
49787
+ return {
49788
+ content: [{ type: "text", text: JSON.stringify({ error: parsedCursor.error }) }],
49789
+ isError: true
49790
+ };
49791
+ }
49792
+ const page = pageItems(connectors18, {
49793
+ offset: parsedCursor.value ?? 0,
49794
+ limit: normalizeLimit(limit, DEFAULT_MCP_LIMIT)
49795
+ });
49796
+ const data = compact === true ? page.items.map((c) => c.name) : verbose || compact === false ? page.items.map((c) => ({
49652
49797
  name: c.name,
49653
49798
  displayName: c.displayName,
49654
49799
  version: c.version,
49655
49800
  category: c.category,
49656
- description: c.description
49657
- }));
49658
- return stripped(JSON.stringify(data, null, 2));
49801
+ description: c.description,
49802
+ tags: c.tags
49803
+ })) : page.items.map((c) => compactConnector(c, 120));
49804
+ return stripped(JSON.stringify({
49805
+ connectors: data,
49806
+ total: connectors18.length,
49807
+ count: data.length,
49808
+ nextCursor: page.nextOffset === null ? null : String(page.nextOffset),
49809
+ hint: page.nextOffset === null ? "Use verbose=true or compact=false for full connector metadata." : `Call again with cursor="${page.nextOffset}", or use verbose=true / compact=false for full connector metadata.`
49810
+ }, null, 2));
49659
49811
  });
49660
49812
  server.registerTool("connector_docs", {
49661
49813
  title: "Connector Documentation",
49662
- description: "Get connector docs. essential=true returns auth+envVars only.",
49814
+ description: "Get compact connector docs by default. Use verbose=true for full parsed docs or essential=true for auth+env only.",
49663
49815
  inputSchema: {
49664
49816
  name: exports_external.string(),
49665
- essential: exports_external.boolean().optional()
49817
+ essential: exports_external.boolean().optional(),
49818
+ verbose: exports_external.boolean().optional()
49666
49819
  }
49667
- }, async ({ name, essential }) => {
49820
+ }, async ({ name, essential, verbose }) => {
49668
49821
  const meta = getConnector(name);
49669
49822
  if (!meta) {
49670
49823
  return {
@@ -49680,7 +49833,7 @@ function registerDiscoveryTools(server, stripped) {
49680
49833
  isError: true
49681
49834
  };
49682
49835
  }
49683
- const data = essential ? { name: meta.name, auth: docs.auth, envVars: docs.envVars } : {
49836
+ const data = essential ? { name: meta.name, auth: docs.auth, envVars: docs.envVars } : verbose ? {
49684
49837
  name: meta.name,
49685
49838
  displayName: meta.displayName,
49686
49839
  version: meta.version,
@@ -49691,6 +49844,19 @@ function registerDiscoveryTools(server, stripped) {
49691
49844
  envVars: docs.envVars,
49692
49845
  cliCommands: docs.cliCommands,
49693
49846
  dataStorage: docs.dataStorage
49847
+ } : {
49848
+ name: meta.name,
49849
+ displayName: meta.displayName,
49850
+ version: meta.version,
49851
+ category: meta.category,
49852
+ description: truncateText(meta.description, 120),
49853
+ overview: firstNonEmptyLines(docs.overview, 1, 140)[0] ?? "",
49854
+ auth: firstNonEmptyLines(docs.auth, 4, 140),
49855
+ envVars: docs.envVars.slice(0, 8),
49856
+ envVarCount: docs.envVars.length,
49857
+ cliCommands: firstNonEmptyLines(docs.cliCommands, 8, 140),
49858
+ dataStorage: firstNonEmptyLines(docs.dataStorage, 2, 140),
49859
+ hint: `Use connector_docs({ name: "${name}", verbose: true }) for full parsed docs, or raw CLI docs with connectors docs ${name} --raw.`
49694
49860
  };
49695
49861
  return {
49696
49862
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
@@ -50125,9 +50291,11 @@ function registerOperationsTools(server, stripped) {
50125
50291
  description: "Discover available API operations for a connector. Returns CLI commands the connector supports (e.g. messages, products, customers). Use this before run_connector_operation to know what's available.",
50126
50292
  inputSchema: {
50127
50293
  name: exports_external.string().describe("Connector name (e.g. stripe, gmail, anthropic)"),
50128
- command: exports_external.string().optional().describe("Get detailed help for a specific subcommand (e.g. products, messages)")
50294
+ command: exports_external.string().optional().describe("Get detailed help for a specific subcommand (e.g. products, messages)"),
50295
+ verbose: exports_external.boolean().optional().describe("Include full auth status and raw help text."),
50296
+ limit: exports_external.number().optional().describe("Limit operation rows in compact mode.")
50129
50297
  }
50130
- }, async ({ name, command }) => {
50298
+ }, async ({ name, command, verbose, limit }) => {
50131
50299
  const meta = getConnector(name);
50132
50300
  if (!meta) {
50133
50301
  return {
@@ -50159,6 +50327,32 @@ function registerOperationsTools(server, stripped) {
50159
50327
  }
50160
50328
  const ops = await getConnectorOperations(name);
50161
50329
  const auth = getAuthStatus(name);
50330
+ if (!verbose) {
50331
+ const operationLimit = normalizeLimit(limit, 50);
50332
+ const operations = ops.operations.slice(0, operationLimit).map((operation) => ({
50333
+ name: operation.name,
50334
+ aliases: operation.aliases,
50335
+ usage: operation.usage,
50336
+ summary: truncateText(operation.summary || "", 140),
50337
+ source: operation.source
50338
+ }));
50339
+ return {
50340
+ content: [
50341
+ {
50342
+ type: "text",
50343
+ text: JSON.stringify({
50344
+ connector: name,
50345
+ displayName: meta.displayName,
50346
+ auth: { type: auth.type, configured: auth.configured },
50347
+ commands: ops.commands,
50348
+ operations,
50349
+ totalOperations: ops.operations.length,
50350
+ hint: `Use list_connector_operations({ name: "${name}", verbose: true }) for full help text, or command="<subcommand>" for one command.`
50351
+ }, null, 2)
50352
+ }
50353
+ ]
50354
+ };
50355
+ }
50162
50356
  return {
50163
50357
  content: [
50164
50358
  {
@@ -50182,9 +50376,11 @@ function registerOperationsTools(server, stripped) {
50182
50376
  name: exports_external.string().describe("Connector name (e.g. stripe, gmail, anthropic)"),
50183
50377
  args: exports_external.array(exports_external.string()).describe("CLI arguments for the connector command (e.g. ['products', 'list', '--limit', '5'])"),
50184
50378
  format: exports_external.enum(["json", "pretty"]).optional().describe("Output format (default: json for structured parsing)"),
50185
- timeout: exports_external.number().optional().describe("Timeout in milliseconds (default: 30000)")
50379
+ timeout: exports_external.number().optional().describe("Timeout in milliseconds (default: 30000)"),
50380
+ verbose: exports_external.boolean().optional().describe("Return full stdout/stderr without output truncation."),
50381
+ maxOutputChars: exports_external.number().optional().describe("Maximum output characters when verbose is false (default: 6000).")
50186
50382
  }
50187
- }, async ({ name, args, format, timeout }) => {
50383
+ }, async ({ name, args, format, timeout, verbose, maxOutputChars }) => {
50188
50384
  const meta = getConnector(name);
50189
50385
  if (!meta) {
50190
50386
  return {
@@ -50207,6 +50403,11 @@ function registerOperationsTools(server, stripped) {
50207
50403
  ${result.stderr}`;
50208
50404
  const looksLikeHelp = /Usage:|Commands:|Options:/i.test(combinedOutput);
50209
50405
  if (!result.success && !looksLikeHelp) {
50406
+ const output = maybeTruncateOutput(result.stderr || result.stdout, {
50407
+ enabled: !verbose,
50408
+ maxChars: maxOutputChars ?? DEFAULT_OUTPUT_CHARS,
50409
+ hint: "Set verbose=true or maxOutputChars higher for full error output."
50410
+ });
50210
50411
  return {
50211
50412
  content: [
50212
50413
  {
@@ -50214,7 +50415,9 @@ ${result.stderr}`;
50214
50415
  text: JSON.stringify({
50215
50416
  connector: name,
50216
50417
  success: false,
50217
- error: result.stderr || result.stdout
50418
+ error: output.text,
50419
+ outputTruncated: output.truncated,
50420
+ hint: verbose ? undefined : "MCP error output is compact by default. Set verbose=true or maxOutputChars for more output."
50218
50421
  }, null, 2)
50219
50422
  }
50220
50423
  ],
@@ -50228,7 +50431,11 @@ ${result.stderr}`;
50228
50431
  text: JSON.stringify({
50229
50432
  connector: name,
50230
50433
  success: true,
50231
- output: looksLikeHelp ? (result.stdout || result.stderr).trim() : result.stdout
50434
+ output: maybeTruncateOutput(looksLikeHelp ? (result.stdout || result.stderr).trim() : result.stdout, {
50435
+ enabled: !verbose,
50436
+ maxChars: maxOutputChars ?? DEFAULT_OUTPUT_CHARS,
50437
+ hint: "Set verbose=true or maxOutputChars higher for full output."
50438
+ }).text
50232
50439
  }, null, 2)
50233
50440
  }
50234
50441
  ]
@@ -50249,11 +50456,14 @@ function registerSearchTools(server, stripped) {
50249
50456
  description: "Search for available operations across installed connectors.",
50250
50457
  inputSchema: {
50251
50458
  query: exports_external.string(),
50252
- installedOnly: exports_external.boolean().optional()
50459
+ installedOnly: exports_external.boolean().optional(),
50460
+ limit: exports_external.number().optional(),
50461
+ verbose: exports_external.boolean().optional()
50253
50462
  }
50254
- }, async ({ query, installedOnly }) => {
50463
+ }, async ({ query, installedOnly, limit, verbose }) => {
50255
50464
  const installed = installedOnly ? getInstalledConnectors() : null;
50256
50465
  const results = [];
50466
+ const resultLimit = normalizeLimit(limit, DEFAULT_MCP_LIMIT);
50257
50467
  const connectors18 = installed || getInstalledConnectors();
50258
50468
  for (const name of connectors18) {
50259
50469
  try {
@@ -50272,22 +50482,31 @@ function registerSearchTools(server, stripped) {
50272
50482
  results.push({
50273
50483
  connector: name,
50274
50484
  operation: operation.name,
50275
- description: operation.summary || ops.helpText || ""
50485
+ description: verbose ? operation.summary || ops.helpText || "" : truncateText(operation.summary || ops.helpText || "", 160)
50276
50486
  });
50487
+ if (results.length >= resultLimit)
50488
+ break;
50277
50489
  }
50278
50490
  }
50491
+ if (results.length >= resultLimit)
50492
+ break;
50279
50493
  } catch {}
50280
50494
  }
50281
- return stripped(JSON.stringify(results, null, 2));
50495
+ return stripped(JSON.stringify({
50496
+ results,
50497
+ count: results.length,
50498
+ hint: "Use limit=<n> for more rows and verbose=true for longer descriptions."
50499
+ }, null, 2));
50282
50500
  });
50283
50501
  server.registerTool("describe_tools", {
50284
50502
  title: "Describe Tools",
50285
50503
  description: "Get detailed documentation for a connector's operations.",
50286
50504
  inputSchema: {
50287
50505
  name: exports_external.string(),
50288
- operation: exports_external.string().optional()
50506
+ operation: exports_external.string().optional(),
50507
+ verbose: exports_external.boolean().optional()
50289
50508
  }
50290
- }, async ({ name, operation }) => {
50509
+ }, async ({ name, operation, verbose }) => {
50291
50510
  const meta = getConnector(name);
50292
50511
  if (!meta) {
50293
50512
  return {
@@ -50303,7 +50522,23 @@ function registerSearchTools(server, stripped) {
50303
50522
  };
50304
50523
  }
50305
50524
  const ops = await getConnectorOperations(name);
50306
- return stripped(JSON.stringify({ connector: name, displayName: meta.displayName, ...ops }, null, 2));
50525
+ if (verbose) {
50526
+ return stripped(JSON.stringify({ connector: name, displayName: meta.displayName, ...ops }, null, 2));
50527
+ }
50528
+ return stripped(JSON.stringify({
50529
+ connector: name,
50530
+ displayName: meta.displayName,
50531
+ commands: ops.commands,
50532
+ operations: ops.operations.map((item) => ({
50533
+ name: item.name,
50534
+ aliases: item.aliases,
50535
+ usage: item.usage,
50536
+ summary: truncateText(item.summary || "", 140),
50537
+ source: item.source
50538
+ })),
50539
+ totalOperations: ops.operations.length,
50540
+ hint: `Use describe_tools({ name: "${name}", verbose: true }) for full help text.`
50541
+ }, null, 2));
50307
50542
  });
50308
50543
  }
50309
50544
  var init_search = __esm(() => {
@@ -50357,62 +50592,180 @@ var init_ranking = __esm(() => {
50357
50592
  });
50358
50593
 
50359
50594
  // src/mcp/tools/jobs.ts
50595
+ function normalizeOutputChars(value) {
50596
+ return normalizeLimit(value, DEFAULT_EXECUTION_OUTPUT_CHARS, MAX_EXECUTION_OUTPUT_CHARS);
50597
+ }
50598
+ function compactExecutionText(text, options, label) {
50599
+ return maybeTruncateOutput(text, {
50600
+ enabled: !options.verbose,
50601
+ maxChars: normalizeOutputChars(options.maxOutputChars),
50602
+ hint: `Set verbose=true or maxOutputChars higher for full ${label} output.`
50603
+ });
50604
+ }
50605
+ function compactJobExecutionResult(result, options = {}) {
50606
+ const output = compactExecutionText(result.output, options, "job");
50607
+ return {
50608
+ ...result,
50609
+ output: output.text,
50610
+ outputTruncated: output.truncated,
50611
+ hint: options.verbose ? undefined : "MCP job output is compact by default. Set verbose=true or maxOutputChars for more output."
50612
+ };
50613
+ }
50614
+ function compactWorkflowExecutionResult(result, options = {}) {
50615
+ let truncatedOutputs = 0;
50616
+ const steps = result.steps.map((step) => {
50617
+ const output = compactExecutionText(step.output, options, "workflow step");
50618
+ if (output.truncated)
50619
+ truncatedOutputs++;
50620
+ return {
50621
+ ...step,
50622
+ output: output.text,
50623
+ outputTruncated: output.truncated
50624
+ };
50625
+ });
50626
+ const finalOutput = compactExecutionText(result.final_output, options, "workflow final");
50627
+ if (finalOutput.truncated)
50628
+ truncatedOutputs++;
50629
+ return {
50630
+ ...result,
50631
+ steps,
50632
+ final_output: finalOutput.text,
50633
+ finalOutputTruncated: finalOutput.truncated,
50634
+ truncatedOutputs,
50635
+ hint: options.verbose ? undefined : "MCP workflow output is compact by default. Set verbose=true or maxOutputChars for more output."
50636
+ };
50637
+ }
50360
50638
  function registerJobsTools(server, stripped) {
50361
50639
  server.registerTool("list_jobs", {
50362
50640
  title: "List Jobs",
50363
- description: "List scheduled connector jobs.",
50364
- inputSchema: {}
50365
- }, async () => {
50641
+ description: "List scheduled connector jobs with compact, paged defaults.",
50642
+ inputSchema: {
50643
+ limit: exports_external.number().optional(),
50644
+ cursor: exports_external.string().optional(),
50645
+ verbose: exports_external.boolean().optional()
50646
+ }
50647
+ }, async ({ limit, cursor, verbose }) => {
50648
+ const parsedCursor = parseCursor(cursor);
50649
+ if (parsedCursor.error) {
50650
+ return { content: [{ type: "text", text: JSON.stringify({ error: parsedCursor.error }) }], isError: true };
50651
+ }
50366
50652
  const db = getDatabase();
50367
50653
  const jobs = listJobs(db);
50368
- return stripped(JSON.stringify(jobs, null, 2));
50654
+ const page = pageItems(jobs, {
50655
+ offset: parsedCursor.value ?? 0,
50656
+ limit: normalizeLimit(limit, DEFAULT_MCP_LIMIT)
50657
+ });
50658
+ const data = verbose ? page.items : page.items.map((job) => ({
50659
+ name: job.name,
50660
+ connector: job.connector,
50661
+ command: `${job.command}${job.args.length ? ` ${job.args.join(" ")}` : ""}`,
50662
+ cron: job.cron,
50663
+ enabled: job.enabled,
50664
+ strip: job.strip,
50665
+ lastRunAt: job.last_run_at
50666
+ }));
50667
+ return stripped(JSON.stringify({
50668
+ jobs: data,
50669
+ total: jobs.length,
50670
+ count: data.length,
50671
+ nextCursor: page.nextOffset === null ? null : String(page.nextOffset),
50672
+ hint: "Use verbose=true for full job records, get_latest_job_run for run output, or connectors jobs show <name>."
50673
+ }, null, 2));
50369
50674
  });
50370
50675
  server.registerTool("get_latest_job_run", {
50371
50676
  title: "Get Latest Job Run",
50372
- description: "Get the most recent run output for a job.",
50373
- inputSchema: { name: exports_external.string() }
50374
- }, async ({ name }) => {
50677
+ description: "Get the most recent run output for a job. Output is truncated unless verbose=true.",
50678
+ inputSchema: {
50679
+ name: exports_external.string(),
50680
+ verbose: exports_external.boolean().optional(),
50681
+ maxOutputChars: exports_external.number().optional()
50682
+ }
50683
+ }, async ({ name, verbose, maxOutputChars }) => {
50375
50684
  const db = getDatabase();
50376
50685
  const job = getJobByName(name, db);
50377
50686
  if (!job)
50378
50687
  return { content: [{ type: "text", text: JSON.stringify({ error: `Job "${name}" not found` }) }], isError: true };
50379
50688
  const run = getLatestRun(job.id, db);
50380
- return stripped(JSON.stringify(run ?? { message: "No runs yet" }, null, 2));
50689
+ if (!run)
50690
+ return stripped(JSON.stringify({ message: "No runs yet" }, null, 2));
50691
+ const outputLimit = normalizeLimit(maxOutputChars, 2000, 50000);
50692
+ return stripped(JSON.stringify({
50693
+ ...run,
50694
+ raw_output: run.raw_output && !verbose ? truncateText(run.raw_output, outputLimit) : run.raw_output,
50695
+ stripped_output: run.stripped_output && !verbose ? truncateText(run.stripped_output, outputLimit) : run.stripped_output,
50696
+ hint: verbose ? undefined : "Long run output is truncated in MCP. Use verbose=true or maxOutputChars for more output."
50697
+ }, null, 2));
50381
50698
  });
50382
50699
  server.registerTool("run_job", {
50383
50700
  title: "Run Job",
50384
50701
  description: "Manually trigger a scheduled job.",
50385
- inputSchema: { name: exports_external.string() }
50386
- }, async ({ name }) => {
50702
+ inputSchema: {
50703
+ name: exports_external.string(),
50704
+ verbose: exports_external.boolean().optional().describe("Return full job output without truncation."),
50705
+ maxOutputChars: exports_external.number().optional().describe("Maximum output characters when verbose is false (default: 2000).")
50706
+ }
50707
+ }, async ({ name, verbose, maxOutputChars }) => {
50387
50708
  const db = getDatabase();
50388
50709
  const job = getJobByName(name, db);
50389
50710
  if (!job)
50390
50711
  return { content: [{ type: "text", text: JSON.stringify({ error: `Job "${name}" not found` }) }], isError: true };
50391
50712
  const result = await triggerJob(job, db);
50392
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
50713
+ return stripped(JSON.stringify(compactJobExecutionResult(result, { verbose, maxOutputChars }), null, 2));
50393
50714
  });
50394
50715
  server.registerTool("list_workflows", {
50395
50716
  title: "List Workflows",
50396
- description: "List connector workflows.",
50397
- inputSchema: {}
50398
- }, async () => {
50717
+ description: "List connector workflows with compact, paged defaults.",
50718
+ inputSchema: {
50719
+ limit: exports_external.number().optional(),
50720
+ cursor: exports_external.string().optional(),
50721
+ verbose: exports_external.boolean().optional()
50722
+ }
50723
+ }, async ({ limit, cursor, verbose }) => {
50724
+ const parsedCursor = parseCursor(cursor);
50725
+ if (parsedCursor.error) {
50726
+ return { content: [{ type: "text", text: JSON.stringify({ error: parsedCursor.error }) }], isError: true };
50727
+ }
50399
50728
  const db = getDatabase();
50400
50729
  const workflows = listWorkflows(db);
50401
- return stripped(JSON.stringify(workflows, null, 2));
50730
+ const page = pageItems(workflows, {
50731
+ offset: parsedCursor.value ?? 0,
50732
+ limit: normalizeLimit(limit, DEFAULT_MCP_LIMIT)
50733
+ });
50734
+ const data = verbose ? page.items : page.items.map((workflow) => ({
50735
+ name: workflow.name,
50736
+ enabled: workflow.enabled,
50737
+ stepCount: workflow.steps.length,
50738
+ steps: workflow.steps.slice(0, 5).map((step) => ({
50739
+ connector: step.connector,
50740
+ command: step.command
50741
+ }))
50742
+ }));
50743
+ return stripped(JSON.stringify({
50744
+ workflows: data,
50745
+ total: workflows.length,
50746
+ count: data.length,
50747
+ nextCursor: page.nextOffset === null ? null : String(page.nextOffset),
50748
+ hint: "Use verbose=true for full workflow records, or connectors workflows show <name>."
50749
+ }, null, 2));
50402
50750
  });
50403
50751
  server.registerTool("run_workflow", {
50404
50752
  title: "Run Workflow",
50405
50753
  description: "Execute a connector workflow pipeline.",
50406
- inputSchema: { name: exports_external.string() }
50407
- }, async ({ name }) => {
50754
+ inputSchema: {
50755
+ name: exports_external.string(),
50756
+ verbose: exports_external.boolean().optional().describe("Return full workflow step output without truncation."),
50757
+ maxOutputChars: exports_external.number().optional().describe("Maximum output characters per output field when verbose is false (default: 2000).")
50758
+ }
50759
+ }, async ({ name, verbose, maxOutputChars }) => {
50408
50760
  const db = getDatabase();
50409
50761
  const wf = getWorkflowByName(name, db);
50410
50762
  if (!wf)
50411
50763
  return { content: [{ type: "text", text: JSON.stringify({ error: `Workflow "${name}" not found` }) }], isError: true };
50412
50764
  const result = await runWorkflow(wf);
50413
- return stripped(JSON.stringify(result, null, 2));
50765
+ return stripped(JSON.stringify(compactWorkflowExecutionResult(result, { verbose, maxOutputChars }), null, 2));
50414
50766
  });
50415
50767
  }
50768
+ var DEFAULT_EXECUTION_OUTPUT_CHARS = 2000, MAX_EXECUTION_OUTPUT_CHARS = 50000;
50416
50769
  var init_jobs2 = __esm(() => {
50417
50770
  init_zod();
50418
50771
  init_database();
@@ -50472,11 +50825,38 @@ function registerAgentTools(server, stripped) {
50472
50825
  });
50473
50826
  server.registerTool("list_agents", {
50474
50827
  title: "List Agents",
50475
- description: "List all registered agents.",
50476
- inputSchema: {}
50477
- }, async () => {
50828
+ description: "List registered agents with compact, paged defaults.",
50829
+ inputSchema: {
50830
+ limit: exports_external.number().optional(),
50831
+ cursor: exports_external.string().optional(),
50832
+ verbose: exports_external.boolean().optional()
50833
+ }
50834
+ }, async ({ limit, cursor, verbose }) => {
50835
+ const parsedCursor = parseCursor(cursor);
50836
+ if (parsedCursor.error) {
50837
+ return { content: [{ type: "text", text: JSON.stringify({ error: parsedCursor.error }) }], isError: true };
50838
+ }
50478
50839
  const agents = listAgents();
50479
- return { content: [{ type: "text", text: JSON.stringify(agents, null, 2) }] };
50840
+ const page = pageItems(agents, {
50841
+ offset: parsedCursor.value ?? 0,
50842
+ limit: normalizeLimit(limit, DEFAULT_MCP_LIMIT)
50843
+ });
50844
+ const data = verbose ? page.items : page.items.map((agent) => ({
50845
+ id: agent.id,
50846
+ name: agent.name,
50847
+ role: agent.role,
50848
+ lastSeenAt: agent.last_seen_at,
50849
+ projectId: agent.project_id
50850
+ }));
50851
+ return {
50852
+ content: [{ type: "text", text: JSON.stringify({
50853
+ agents: data,
50854
+ total: agents.length,
50855
+ count: data.length,
50856
+ nextCursor: page.nextOffset === null ? null : String(page.nextOffset),
50857
+ hint: "Use verbose=true for full agent records."
50858
+ }, null, 2) }]
50859
+ };
50480
50860
  });
50481
50861
  server.registerTool("heartbeat", {
50482
50862
  title: "Heartbeat",
@@ -53679,7 +54059,7 @@ function listFilesRecursive(dir, base = dir) {
53679
54059
  return files;
53680
54060
  }
53681
54061
  function registerCommands(program2) {
53682
- program2.command("install").alias("add").argument("[connectors...]", "Connectors to install").option("-o, --overwrite", "Overwrite existing connectors", false).option("-d, --dry-run", "Preview what would be installed without making changes", false).option("-c, --category <category>", "Install all connectors in a category").option("--preset <preset>", "Install a preset bundle (e.g. ai, fullstack, google)").option("--json", "Output results as JSON", false).description("Install one or more connectors").action((connectors18, options) => {
54062
+ program2.command("install").alias("add").argument("[connectors...]", "Connectors to install").option("-o, --overwrite", "Overwrite existing connectors", false).option("-d, --dry-run", "Preview what would be installed without making changes", false).option("-c, --category <category>", "Install all connectors in a category").option("--preset <preset>", "Install a preset bundle (e.g. ai, fullstack, google)").option("--json", "Output results as JSON", false).option("-v, --verbose", "Show full dry-run file lists", false).description("Install one or more connectors").action((connectors18, options) => {
53683
54063
  if (options.category) {
53684
54064
  const category = CATEGORIES.find((c) => c.toLowerCase() === options.category.toLowerCase());
53685
54065
  if (!category) {
@@ -53783,9 +54163,13 @@ Dry run \u2014 no changes will be made
53783
54163
  console.log(chalk2.dim(` manifest: ${a.manifestPath}`));
53784
54164
  console.log(chalk2.dim(` index: ${a.indexPath}`));
53785
54165
  if (a.files && a.files.length > 0) {
54166
+ const visibleFiles = options.verbose ? a.files : a.files.slice(0, 12);
53786
54167
  console.log(chalk2.dim(` packaged runtime files (${a.files.length}):`));
53787
- for (const f of a.files) {
53788
- console.log(chalk2.dim(` ${f}`));
54168
+ for (const f of visibleFiles) {
54169
+ console.log(chalk2.dim(` ${truncateText(f, 120)}`));
54170
+ }
54171
+ if (!options.verbose && a.files.length > visibleFiles.length) {
54172
+ console.log(chalk2.dim(` ... ${a.files.length - visibleFiles.length} more files (use --verbose)`));
53789
54173
  }
53790
54174
  }
53791
54175
  console.log();
@@ -53915,20 +54299,42 @@ init_registry2();
53915
54299
  init_installer();
53916
54300
  init_auth();
53917
54301
  import chalk3 from "chalk";
53918
- function parseNonNegativeInt(raw, flag) {
53919
- if (raw === undefined)
53920
- return { value: undefined };
53921
- if (!/^\d+$/.test(raw)) {
53922
- return { value: undefined, error: `Invalid value for ${flag}: '${raw}'. Expected a non-negative integer.` };
54302
+ function printConnectorRows(connectors18, options = {}) {
54303
+ const nameWidth = 22;
54304
+ const versionWidth = 10;
54305
+ const categoryWidth = options.includeCategory ? 22 : 0;
54306
+ const descWidth = options.verbose ? 160 : 80;
54307
+ const categoryHeader = options.includeCategory ? chalk3.dim("Category".padEnd(categoryWidth)) : "";
54308
+ console.log(` ${chalk3.dim("Name".padEnd(nameWidth))}` + `${chalk3.dim("Version".padEnd(versionWidth))}` + categoryHeader + `${chalk3.dim("Description")}`);
54309
+ console.log(chalk3.dim(` ${"-".repeat(nameWidth + versionWidth + categoryWidth + 40)}`));
54310
+ for (const c of connectors18) {
54311
+ const description = options.verbose ? c.description : truncateText(c.description, descWidth);
54312
+ const category = options.includeCategory ? chalk3.dim(c.category.padEnd(categoryWidth)) : "";
54313
+ console.log(` ${chalk3.cyan(c.name.padEnd(nameWidth))}` + `${chalk3.dim((c.version || "-").padEnd(versionWidth))}` + category + description);
54314
+ }
54315
+ }
54316
+ function printPaginationHint(command, total, nextOffset, detailHint = true) {
54317
+ const parts = [
54318
+ nextOffset !== null ? `${command} --cursor ${nextOffset}` : null,
54319
+ detailHint ? "connectors info <name>" : null,
54320
+ `${command} --verbose`,
54321
+ `${command} --json`
54322
+ ].filter(Boolean);
54323
+ if (parts.length > 0) {
54324
+ console.log(chalk3.dim(`
54325
+ More detail: ${parts.join(" | ")}`));
54326
+ }
54327
+ if (nextOffset !== null) {
54328
+ console.log(chalk3.dim(` Showing a compact page of ${total} total results.`));
53923
54329
  }
53924
- return { value: parseInt(raw, 10) };
53925
54330
  }
53926
54331
  function registerCommands2(program2) {
53927
- program2.command("list").alias("ls").option("-c, --category <category>", "Filter by category").option("-a, --all", "Show all available connectors", false).option("-i, --installed", "Show only installed connectors", false).option("-b, --brief", "Output only connector names", false).option("--limit <n>", "Limit results").option("--offset <n>", "Skip first N results").option("--json", "Output as JSON", false).description("List available or installed connectors").action((options) => {
54332
+ program2.command("list").alias("ls").option("-c, --category <category>", "Filter by category").option("-a, --all", "Show all available connectors", false).option("-i, --installed", "Show only installed connectors", false).option("-b, --brief", "Output only connector names", false).option("--limit <n>", "Limit results").option("--offset <n>", "Skip first N results").option("--cursor <n>", "Cursor returned by compact output (same as --offset)").option("--json", "Output as JSON", false).option("-v, --verbose", "Show full human output instead of compact pages", false).description("List available or installed connectors").action((options) => {
53928
54333
  const parsedLimit = parseNonNegativeInt(options.limit, "--limit");
53929
54334
  const parsedOffset = parseNonNegativeInt(options.offset, "--offset");
53930
- if (parsedLimit.error || parsedOffset.error) {
53931
- const error2 = parsedLimit.error || parsedOffset.error || "Invalid pagination options";
54335
+ const parsedCursor = parseNonNegativeInt(options.cursor, "--cursor");
54336
+ if (parsedLimit.error || parsedOffset.error || parsedCursor.error) {
54337
+ const error2 = parsedLimit.error || parsedOffset.error || parsedCursor.error || "Invalid pagination options";
53932
54338
  if (options.json) {
53933
54339
  console.log(JSON.stringify({ error: error2 }));
53934
54340
  } else {
@@ -53938,11 +54344,12 @@ function registerCommands2(program2) {
53938
54344
  return;
53939
54345
  }
53940
54346
  const limit = parsedLimit.value;
53941
- const offset = parsedOffset.value ?? 0;
54347
+ const offset = parsedCursor.value ?? parsedOffset.value ?? 0;
54348
+ const jsonLimit = limit === undefined ? undefined : Math.max(1, Math.floor(limit));
53942
54349
  const page = (items) => {
53943
- if (limit === undefined)
54350
+ if (jsonLimit === undefined)
53944
54351
  return items.slice(offset);
53945
- return items.slice(offset, offset + limit);
54352
+ return items.slice(offset, offset + jsonLimit);
53946
54353
  };
53947
54354
  if (options.brief) {
53948
54355
  if (options.installed) {
@@ -53954,9 +54361,10 @@ function registerCommands2(program2) {
53954
54361
  console.log(name);
53955
54362
  }
53956
54363
  } else if (options.category) {
53957
- const category = CATEGORIES.find((c) => c.toLowerCase() === options.category.toLowerCase());
54364
+ const requestedCategory = options.category;
54365
+ const category = CATEGORIES.find((c) => c.toLowerCase() === requestedCategory.toLowerCase());
53958
54366
  if (!category) {
53959
- console.error(`Unknown category: ${options.category}`);
54367
+ console.error(`Unknown category: ${requestedCategory}`);
53960
54368
  process.exit(1);
53961
54369
  return;
53962
54370
  }
@@ -53979,7 +54387,12 @@ function registerCommands2(program2) {
53979
54387
  return;
53980
54388
  }
53981
54389
  if (options.installed) {
53982
- const installed = page(getInstalledConnectors());
54390
+ const installedSource = getInstalledConnectors();
54391
+ const installedPage = pageItems(installedSource, {
54392
+ offset,
54393
+ limit: limit ?? (options.verbose || options.all ? undefined : DEFAULT_COMPACT_LIMIT)
54394
+ });
54395
+ const installed = options.json ? page(installedSource) : installedPage.items;
53983
54396
  if (installed.length === 0) {
53984
54397
  if (options.json) {
53985
54398
  console.log(JSON.stringify([]));
@@ -54026,8 +54439,9 @@ function registerCommands2(program2) {
54026
54439
  const nameWidth = Math.max(6, ...statuses.map((s) => s.name.length)) + 2;
54027
54440
  const catWidth = Math.max(10, ...statuses.map((s) => s.category.length)) + 2;
54028
54441
  const authWidth = 10;
54442
+ const installedTitle = installedPage.limit === null ? `Installed connectors (${installedSource.length})` : `Installed connectors (showing ${installed.length} of ${installedSource.length})`;
54029
54443
  console.log(chalk3.bold(`
54030
- Installed connectors (${installed.length}):
54444
+ ${installedTitle}:
54031
54445
  `));
54032
54446
  console.log(` ${chalk3.dim("Name".padEnd(nameWidth))}` + `${chalk3.dim("Category".padEnd(catWidth))}` + `${chalk3.dim("Auth Type".padEnd(authWidth))}` + `${chalk3.dim("Status")}`);
54033
54447
  console.log(chalk3.dim(` ${"\u2500".repeat(nameWidth + catWidth + authWidth + 24)}`));
@@ -54048,49 +54462,55 @@ Installed connectors (${installed.length}):
54048
54462
  console.log(` ${chalk3.cyan(s.name.padEnd(nameWidth))}` + `${s.category.padEnd(catWidth)}` + `${authTypeLabel.padEnd(authWidth)}` + `${statusLabel}${expiryStr}`);
54049
54463
  }
54050
54464
  console.log();
54465
+ printPaginationHint("connectors list --installed", installedSource.length, installedPage.nextOffset);
54051
54466
  return;
54052
54467
  }
54053
54468
  if (options.category) {
54054
- const category = CATEGORIES.find((c) => c.toLowerCase() === options.category.toLowerCase());
54469
+ const requestedCategory = options.category;
54470
+ const category = CATEGORIES.find((c) => c.toLowerCase() === requestedCategory.toLowerCase());
54055
54471
  if (!category) {
54056
54472
  if (options.json) {
54057
- console.log(JSON.stringify({ error: `Unknown category: ${options.category}` }));
54473
+ console.log(JSON.stringify({ error: `Unknown category: ${requestedCategory}` }));
54058
54474
  process.exit(1);
54059
54475
  }
54060
- console.log(chalk3.red(`Unknown category: ${options.category}`));
54476
+ console.log(chalk3.red(`Unknown category: ${requestedCategory}`));
54061
54477
  console.log(chalk3.dim(`Available: ${CATEGORIES.join(", ")}`));
54062
54478
  return;
54063
54479
  }
54064
54480
  const connectors18 = getConnectorsByCategory(category);
54065
- const pagedConnectors = page(connectors18);
54481
+ const humanPage = pageItems(connectors18, {
54482
+ offset,
54483
+ limit: limit ?? (options.verbose || options.all ? undefined : DEFAULT_COMPACT_LIMIT)
54484
+ });
54485
+ const pagedConnectors = options.json ? page(connectors18) : humanPage.items;
54066
54486
  if (options.json) {
54067
54487
  console.log(JSON.stringify(pagedConnectors));
54068
54488
  return;
54069
54489
  }
54490
+ const title = humanPage.limit === null ? `${category} (${connectors18.length})` : `${category} (showing ${pagedConnectors.length} of ${connectors18.length})`;
54070
54491
  console.log(chalk3.bold(`
54071
- ${category} (${pagedConnectors.length}):
54492
+ ${title}:
54072
54493
  `));
54073
- console.log(` ${chalk3.dim("Name".padEnd(20))}${chalk3.dim("Version".padEnd(10))}${chalk3.dim("Description")}`);
54074
- console.log(chalk3.dim(` ${"\u2500".repeat(60)}`));
54075
- for (const c of pagedConnectors) {
54076
- console.log(` ${chalk3.cyan(c.name.padEnd(20))}${chalk3.dim((c.version || "-").padEnd(10))}${c.description}`);
54077
- }
54494
+ printConnectorRows(pagedConnectors, { verbose: options.verbose });
54495
+ printPaginationHint(`connectors list --category "${category}"`, connectors18.length, humanPage.nextOffset);
54078
54496
  return;
54079
54497
  }
54080
54498
  if (options.json) {
54081
54499
  console.log(JSON.stringify(page(CONNECTORS)));
54082
54500
  return;
54083
54501
  }
54084
- if (options.limit || options.offset) {
54085
- const connectors18 = page(CONNECTORS);
54502
+ if (options.limit || options.offset || options.cursor || !options.all) {
54503
+ const humanPage = pageItems(CONNECTORS, {
54504
+ offset,
54505
+ limit: limit ?? (options.verbose || options.all ? undefined : DEFAULT_COMPACT_LIMIT)
54506
+ });
54507
+ const connectors18 = humanPage.items;
54508
+ const title = humanPage.limit === null ? `Available connectors (${CONNECTORS.length})` : `Available connectors (showing ${connectors18.length} of ${CONNECTORS.length})`;
54086
54509
  console.log(chalk3.bold(`
54087
- Available connectors (${connectors18.length}):
54510
+ ${title}:
54088
54511
  `));
54089
- console.log(` ${chalk3.dim("Name".padEnd(20))}${chalk3.dim("Version".padEnd(10))}${chalk3.dim("Category".padEnd(24))}${chalk3.dim("Description")}`);
54090
- console.log(chalk3.dim(` ${"\u2500".repeat(96)}`));
54091
- for (const c of connectors18) {
54092
- console.log(` ${chalk3.cyan(c.name.padEnd(20))}` + `${chalk3.dim((c.version || "-").padEnd(10))}` + `${chalk3.dim(c.category.padEnd(24))}` + `${c.description}`);
54093
- }
54512
+ printConnectorRows(connectors18, { includeCategory: true, verbose: options.verbose });
54513
+ printPaginationHint("connectors list", CONNECTORS.length, humanPage.nextOffset);
54094
54514
  return;
54095
54515
  }
54096
54516
  console.log(chalk3.bold(`
@@ -54099,15 +54519,11 @@ Available connectors (${CONNECTORS.length}):
54099
54519
  for (const category of CATEGORIES) {
54100
54520
  const connectors18 = getConnectorsByCategory(category);
54101
54521
  console.log(chalk3.bold(`${category} (${connectors18.length}):`));
54102
- console.log(` ${chalk3.dim("Name".padEnd(20))}${chalk3.dim("Version".padEnd(10))}${chalk3.dim("Description")}`);
54103
- console.log(chalk3.dim(` ${"\u2500".repeat(60)}`));
54104
- for (const c of connectors18) {
54105
- console.log(` ${chalk3.cyan(c.name.padEnd(20))}${chalk3.dim((c.version || "-").padEnd(10))}${c.description}`);
54106
- }
54522
+ printConnectorRows(connectors18, { verbose: true });
54107
54523
  console.log();
54108
54524
  }
54109
54525
  });
54110
- program2.command("search").argument("<query>", "Search term").option("--json", "Output as JSON", false).option("--limit <n>", "Max results", "20").description("Search for connectors (ranked with fuzzy matching)").action((query, options) => {
54526
+ program2.command("search").argument("<query>", "Search term").option("--json", "Output as JSON", false).option("--limit <n>", "Max results", "20").option("-v, --verbose", "Show match details and full descriptions", false).description("Search for connectors (ranked with fuzzy matching)").action((query, options) => {
54111
54527
  const parsedLimit = parseNonNegativeInt(options.limit, "--limit");
54112
54528
  if (parsedLimit.error || parsedLimit.value === undefined) {
54113
54529
  const error2 = parsedLimit.error || "Invalid --limit value";
@@ -54140,12 +54556,16 @@ Available connectors (${CONNECTORS.length}):
54140
54556
  Found ${results.length} connector(s):
54141
54557
  `));
54142
54558
  console.log(` ${chalk3.dim("Name".padEnd(22))}${chalk3.dim("Score".padEnd(7))}${chalk3.dim("Category".padEnd(20))}${chalk3.dim("Description")}`);
54143
- console.log(chalk3.dim(` ${"\u2500".repeat(75)}`));
54559
+ console.log(chalk3.dim(` ${"-".repeat(75)}`));
54144
54560
  for (const c of results) {
54145
54561
  const badges = c.badges.map((b) => b === "installed" ? chalk3.green("[INS]") : b === "hot" ? chalk3.red("[HOT]") : b === "promoted" ? chalk3.yellow("[PRO]") : "").join(" ");
54146
54562
  const badgeStr = badges ? " " + badges : "";
54147
- console.log(` ${chalk3.cyan(c.name.padEnd(22))}${String(c.score).padEnd(7)}${chalk3.dim(c.category.padEnd(20))}${c.description}${badgeStr}`);
54563
+ const description = options.verbose ? c.description : truncateText(c.description, 72);
54564
+ const reasons = options.verbose && c.matchReasons.length > 0 ? chalk3.dim(` matches: ${c.matchReasons.join(", ")}`) : "";
54565
+ console.log(` ${chalk3.cyan(c.name.padEnd(22))}${String(c.score).padEnd(7)}${chalk3.dim(c.category.padEnd(20))}${description}${badgeStr}${reasons}`);
54148
54566
  }
54567
+ console.log(chalk3.dim(`
54568
+ More detail: connectors info <name> | connectors search "${query}" --verbose | connectors search "${query}" --json`));
54149
54569
  });
54150
54570
  program2.command("info").argument("<connector>", "Connector name").option("--json", "Output as JSON", false).description("Show detailed info about a connector").action((connector, options) => {
54151
54571
  const meta = getConnector(connector);
@@ -54176,7 +54596,7 @@ ${meta.displayName}`));
54176
54596
  console.log(` Installed: ${isInstalled ? chalk3.green("yes") : "no"}`);
54177
54597
  console.log(` Package: @hasna/connect-${meta.name}`);
54178
54598
  });
54179
- program2.command("docs").argument("<connector>", "Connector name").option("--json", "Output as structured JSON", false).option("--raw", "Output raw markdown", false).option("--essential", "Show auth and env vars only (no full docs)", false).description("Show connector documentation (auth, env vars, API, CLI commands)").action((connector, options) => {
54599
+ program2.command("docs").argument("<connector>", "Connector name").option("--json", "Output as structured JSON", false).option("--raw", "Output raw markdown", false).option("--essential", "Show auth and env vars only (no full docs)", false).option("-v, --verbose", "Show full parsed documentation sections", false).description("Show connector documentation (auth, env vars, API, CLI commands)").action((connector, options) => {
54180
54600
  const meta = getConnector(connector);
54181
54601
  if (!meta) {
54182
54602
  if (options.json) {
@@ -54244,9 +54664,60 @@ Environment Variables`));
54244
54664
  }, null, 2));
54245
54665
  return;
54246
54666
  }
54667
+ if (!options.verbose) {
54668
+ console.log(chalk3.bold(`
54669
+ ${meta.displayName} - Documentation Summary`));
54670
+ console.log(chalk3.dim("-".repeat(50)));
54671
+ console.log(` Name: ${chalk3.cyan(meta.name)}`);
54672
+ console.log(` Version: ${meta.version || "-"}`);
54673
+ console.log(` Category: ${meta.category}`);
54674
+ console.log(` Description: ${truncateText(meta.description, 100)}`);
54675
+ const overview = firstNonEmptyLines(docs.overview, 1, 100);
54676
+ if (overview.length > 0) {
54677
+ console.log(chalk3.bold(`
54678
+ Overview`));
54679
+ console.log(` ${overview[0]}`);
54680
+ }
54681
+ const authLines = firstNonEmptyLines(docs.auth, 4, 100);
54682
+ if (authLines.length > 0) {
54683
+ console.log(chalk3.bold(`
54684
+ Authentication`));
54685
+ for (const line of authLines)
54686
+ console.log(` ${line}`);
54687
+ }
54688
+ if (docs.envVars.length > 0) {
54689
+ const visible = docs.envVars.slice(0, 8);
54690
+ console.log(chalk3.bold(`
54691
+ Environment Variables`));
54692
+ for (const v of visible) {
54693
+ console.log(` ${chalk3.cyan(v.variable.padEnd(30))}${truncateText(v.description, 80)}`);
54694
+ }
54695
+ if (docs.envVars.length > visible.length) {
54696
+ console.log(chalk3.dim(` ... ${docs.envVars.length - visible.length} more env vars`));
54697
+ }
54698
+ }
54699
+ const commandLines = firstNonEmptyLines(docs.cliCommands, 8, 100);
54700
+ if (commandLines.length > 0) {
54701
+ console.log(chalk3.bold(`
54702
+ CLI Commands`));
54703
+ for (const line of commandLines)
54704
+ console.log(` ${line}`);
54705
+ }
54706
+ const storageLines = firstNonEmptyLines(docs.dataStorage, 2, 100);
54707
+ if (storageLines.length > 0) {
54708
+ console.log(chalk3.bold(`
54709
+ Data Storage`));
54710
+ for (const line of storageLines)
54711
+ console.log(` ${line}`);
54712
+ }
54713
+ console.log(chalk3.dim(`
54714
+ More detail: connectors docs ${connector} --verbose | connectors docs ${connector} --raw | connectors docs ${connector} --json`));
54715
+ console.log();
54716
+ return;
54717
+ }
54247
54718
  console.log(chalk3.bold(`
54248
54719
  ${meta.displayName} \u2014 Documentation`));
54249
- console.log(chalk3.dim("\u2500".repeat(50)));
54720
+ console.log(chalk3.dim("-".repeat(50)));
54250
54721
  if (docs.overview) {
54251
54722
  console.log(chalk3.bold(`
54252
54723
  Overview`));
@@ -54343,7 +54814,18 @@ import chalk4 from "chalk";
54343
54814
  import { existsSync as existsSync20, readFileSync as readFileSync11 } from "fs";
54344
54815
  import { join as join20 } from "path";
54345
54816
  function registerCommands3(program2) {
54346
- program2.command("status").option("--json", "Output as JSON", false).description("Show auth status of all configured connectors (project + global)").action((options) => {
54817
+ program2.command("status").option("--json", "Output as JSON", false).option("--limit <n>", "Limit rows per section in human output").option("--offset <n>", "Skip first N rows per section in human output").option("-v, --verbose", "Show all human rows", false).description("Show auth status of all configured connectors (project + global)").action((options) => {
54818
+ const parsedLimit = parseNonNegativeInt(options.limit, "--limit");
54819
+ const parsedOffset = parseNonNegativeInt(options.offset, "--offset");
54820
+ if (parsedLimit.error || parsedOffset.error) {
54821
+ const error2 = parsedLimit.error || parsedOffset.error || "Invalid pagination options";
54822
+ if (options.json)
54823
+ console.log(JSON.stringify({ error: error2 }));
54824
+ else
54825
+ console.log(chalk4.red(error2));
54826
+ process.exit(1);
54827
+ return;
54828
+ }
54347
54829
  const installed = getInstalledConnectors();
54348
54830
  const configDir = getConnectorsHome();
54349
54831
  const seen = new Set;
@@ -54463,28 +54945,52 @@ function registerCommands3(program2) {
54463
54945
  console.log(chalk4.bold(`
54464
54946
  Connector Status
54465
54947
  `));
54948
+ const humanLimit = options.verbose ? undefined : parsedLimit.value ?? DEFAULT_COMPACT_LIMIT;
54949
+ const humanOffset = parsedOffset.value ?? 0;
54466
54950
  if (configuredList.length > 0) {
54467
- console.log(chalk4.green.bold(` Configured (${configuredList.length})
54951
+ const page = pageItems(configuredList, { offset: humanOffset, limit: humanLimit });
54952
+ const title = page.limit === null ? ` Configured (${configuredList.length})` : ` Configured (showing ${page.items.length} of ${configuredList.length})`;
54953
+ console.log(chalk4.green.bold(`${title}
54468
54954
  `));
54469
54955
  printHeader();
54470
- for (const s of configuredList) {
54956
+ for (const s of page.items) {
54471
54957
  printRow(s);
54472
54958
  }
54959
+ if (page.nextOffset !== null) {
54960
+ console.log(chalk4.dim(` More configured rows: connectors status --offset ${page.nextOffset}`));
54961
+ }
54473
54962
  console.log();
54474
54963
  }
54475
54964
  if (unconfiguredList.length > 0) {
54476
- console.log(chalk4.red.bold(` Unconfigured (${unconfiguredList.length})
54965
+ const page = pageItems(unconfiguredList, { offset: humanOffset, limit: humanLimit });
54966
+ const title = page.limit === null ? ` Unconfigured (${unconfiguredList.length})` : ` Unconfigured (showing ${page.items.length} of ${unconfiguredList.length})`;
54967
+ console.log(chalk4.red.bold(`${title}
54477
54968
  `));
54478
54969
  printHeader();
54479
- for (const s of unconfiguredList) {
54970
+ for (const s of page.items) {
54480
54971
  printRow(s);
54481
54972
  }
54973
+ if (page.nextOffset !== null) {
54974
+ console.log(chalk4.dim(` More unconfigured rows: connectors status --offset ${page.nextOffset}`));
54975
+ }
54482
54976
  console.log();
54483
54977
  }
54484
54978
  console.log(chalk4.dim(` Total: ${allStatuses.length} | Configured: ${configuredList.length} | Unconfigured: ${unconfiguredList.length}`));
54979
+ console.log(chalk4.dim(" More detail: connectors status --verbose | connectors status --json"));
54485
54980
  console.log();
54486
54981
  });
54487
- program2.command("doctor").option("--json", "Output as JSON", false).description("Check all installed connectors for issues and output a health report").action((options) => {
54982
+ program2.command("doctor").option("--json", "Output as JSON", false).option("--limit <n>", "Limit rows in human output").option("--offset <n>", "Skip first N rows in human output").option("-v, --verbose", "Show all human rows and full suggestions", false).description("Check all installed connectors for issues and output a health report").action((options) => {
54983
+ const parsedLimit = parseNonNegativeInt(options.limit, "--limit");
54984
+ const parsedOffset = parseNonNegativeInt(options.offset, "--offset");
54985
+ if (parsedLimit.error || parsedOffset.error) {
54986
+ const error2 = parsedLimit.error || parsedOffset.error || "Invalid pagination options";
54987
+ if (options.json)
54988
+ console.log(JSON.stringify({ error: error2 }));
54989
+ else
54990
+ console.log(chalk4.red(error2));
54991
+ process.exit(1);
54992
+ return;
54993
+ }
54488
54994
  const installed = getInstalledConnectors();
54489
54995
  if (installed.length === 0) {
54490
54996
  if (options.json) {
@@ -54556,7 +55062,11 @@ Connector Status
54556
55062
  console.log(chalk4.bold(`
54557
55063
  Connector Health Report
54558
55064
  `));
54559
- for (const r of results) {
55065
+ const page = pageItems(results, {
55066
+ offset: parsedOffset.value ?? 0,
55067
+ limit: options.verbose ? undefined : parsedLimit.value ?? DEFAULT_COMPACT_LIMIT
55068
+ });
55069
+ for (const r of page.items) {
54560
55070
  let icon;
54561
55071
  if (r.level === "healthy") {
54562
55072
  icon = chalk4.green("\u2713");
@@ -54569,12 +55079,20 @@ Connector Health Report
54569
55079
  if (r.issues.length === 0) {
54570
55080
  console.log(` ${icon} ${nameStr} \u2014 ${chalk4.green("healthy")}`);
54571
55081
  } else {
54572
- console.log(` ${icon} ${nameStr} \u2014 ${r.issues.join(", ")}`);
54573
- for (const suggestion of r.suggestions) {
54574
- console.log(chalk4.dim(` \u2192 ${suggestion}`));
55082
+ console.log(` ${icon} ${nameStr} \u2014 ${truncateText(r.issues.join(", "), options.verbose ? 200 : 120)}`);
55083
+ const suggestions = options.verbose ? r.suggestions : r.suggestions.slice(0, 2);
55084
+ for (const suggestion of suggestions) {
55085
+ console.log(chalk4.dim(` -> ${truncateText(suggestion, options.verbose ? 200 : 120)}`));
55086
+ }
55087
+ if (!options.verbose && r.suggestions.length > suggestions.length) {
55088
+ console.log(chalk4.dim(` ... ${r.suggestions.length - suggestions.length} more suggestion(s)`));
54575
55089
  }
54576
55090
  }
54577
55091
  }
55092
+ if (page.nextOffset !== null) {
55093
+ console.log(chalk4.dim(`
55094
+ More rows: connectors doctor --offset ${page.nextOffset}`));
55095
+ }
54578
55096
  const parts = [];
54579
55097
  if (summary.healthy > 0)
54580
55098
  parts.push(chalk4.green(`${summary.healthy} healthy`));
@@ -55746,7 +56264,7 @@ complete -F _connectors connectors`);
55746
56264
  console.log(output);
55747
56265
  }
55748
56266
  });
55749
- program2.command("presets").option("--json", "Output as JSON", false).description("List available connector preset bundles").action((options) => {
56267
+ program2.command("presets").option("--json", "Output as JSON", false).option("-v, --verbose", "Show every connector in each preset", false).description("List available connector preset bundles").action((options) => {
55750
56268
  if (options.json) {
55751
56269
  console.log(JSON.stringify(Object.entries(PRESETS).map(([name, p]) => ({
55752
56270
  name,
@@ -55761,13 +56279,16 @@ Available presets:
55761
56279
  `));
55762
56280
  for (const [name, preset] of Object.entries(PRESETS)) {
55763
56281
  console.log(` ${chalk6.cyan(name.padEnd(12))} ${preset.description}`);
55764
- console.log(chalk6.dim(` ${"".padEnd(12)} ${preset.connectors.join(", ")}`));
56282
+ const visible = options.verbose ? preset.connectors : preset.connectors.slice(0, 5);
56283
+ const suffix = !options.verbose && preset.connectors.length > visible.length ? `, ... +${preset.connectors.length - visible.length} more` : "";
56284
+ console.log(chalk6.dim(` ${"".padEnd(12)} ${visible.join(", ")}${suffix}`));
55765
56285
  console.log();
55766
56286
  }
55767
- console.log(chalk6.dim(` Install with: connectors install --preset <name>
56287
+ console.log(chalk6.dim(` Install with: connectors install --preset <name>`));
56288
+ console.log(chalk6.dim(` More detail: connectors presets --verbose | connectors presets --json
55768
56289
  `));
55769
56290
  });
55770
- program2.command("whoami").option("--json", "Output as JSON", false).description("Show current setup: config dir, installed connectors, auth status").action((options) => {
56291
+ program2.command("whoami").option("--json", "Output as JSON", false).option("-v, --verbose", "Show all connector rows", false).description("Show current setup: config dir, installed connectors, auth status").action((options) => {
55771
56292
  const configDir = getConnectorsHome();
55772
56293
  const installed = getInstalledConnectors();
55773
56294
  const version2 = "0.3.1";
@@ -55840,32 +56361,46 @@ Connectors Setup
55840
56361
  const projectConnectors = connectorDetails.filter((c) => c.source === "project");
55841
56362
  const globalConnectors = connectorDetails.filter((c) => c.source === "global");
55842
56363
  if (projectConnectors.length > 0) {
56364
+ const page = pageItems(projectConnectors, {
56365
+ limit: options.verbose ? undefined : DEFAULT_COMPACT_LIMIT
56366
+ });
55843
56367
  console.log(chalk6.bold(`
55844
- Project Connectors:
56368
+ Project Connectors (${page.items.length}/${projectConnectors.length}):
55845
56369
  `));
55846
56370
  const nameWidth = Math.max(10, ...projectConnectors.map((c) => c.name.length)) + 2;
55847
- for (const c of projectConnectors) {
56371
+ for (const c of page.items) {
55848
56372
  const status = c.configured ? chalk6.green("\u2713") : chalk6.red("\u2717");
55849
56373
  const profileLabel = c.profile !== "default" ? chalk6.dim(` [${c.profile}]`) : "";
55850
- console.log(` ${status} ${chalk6.cyan(c.name.padEnd(nameWidth))}${c.authType.padEnd(8)}${profileLabel}`);
56374
+ console.log(` ${status} ${chalk6.cyan(truncateText(c.name, nameWidth - 2).padEnd(nameWidth))}${c.authType.padEnd(8)}${profileLabel}`);
56375
+ }
56376
+ if (page.nextOffset !== null) {
56377
+ console.log(chalk6.dim(` ... ${projectConnectors.length - page.items.length} more (use --verbose)`));
55851
56378
  }
55852
56379
  }
55853
56380
  if (globalConnectors.length > 0) {
56381
+ const page = pageItems(globalConnectors, {
56382
+ limit: options.verbose ? undefined : DEFAULT_COMPACT_LIMIT
56383
+ });
55854
56384
  console.log(chalk6.bold(`
55855
- Global Connectors`) + chalk6.dim(" (~/.hasna/connectors)") + chalk6.bold(`:
56385
+ Global Connectors (${page.items.length}/${globalConnectors.length})`) + chalk6.dim(" (~/.hasna/connectors)") + chalk6.bold(`:
55856
56386
  `));
55857
56387
  const nameWidth = Math.max(10, ...globalConnectors.map((c) => c.name.length)) + 2;
55858
- for (const c of globalConnectors) {
56388
+ for (const c of page.items) {
55859
56389
  const status = c.configured ? chalk6.green("\u2713") : chalk6.red("\u2717");
55860
56390
  const profileLabel = c.profile !== "default" ? chalk6.dim(` [${c.profile}]`) : "";
55861
- console.log(` ${status} ${chalk6.cyan(c.name.padEnd(nameWidth))}${c.authType.padEnd(8)}${profileLabel}`);
56391
+ console.log(` ${status} ${chalk6.cyan(truncateText(c.name, nameWidth - 2).padEnd(nameWidth))}${c.authType.padEnd(8)}${profileLabel}`);
56392
+ }
56393
+ if (page.nextOffset !== null) {
56394
+ console.log(chalk6.dim(` ... ${globalConnectors.length - page.items.length} more (use --verbose)`));
55862
56395
  }
55863
56396
  }
55864
56397
  if (connectorDetails.length === 0) {
55865
56398
  console.log(chalk6.dim(`
55866
56399
  No connectors installed or configured.`));
55867
56400
  }
55868
- console.log();
56401
+ console.log(chalk6.dim(`
56402
+ More detail: connectors whoami --verbose | connectors whoami --json
56403
+ `));
55869
56404
  });
55870
56405
  program2.command("test").argument("[connector]", "Connector to test (default: all installed)").option("--json", "Output as JSON", false).option("--timeout <ms>", "Request timeout in milliseconds", "10000").description("Verify API credentials by making a real request to the connector's API").action(async (connector, options) => {
55871
56406
  const timeout = parseInt(options.timeout, 10) || 1e4;
@@ -56058,6 +56593,18 @@ init_registry2();
56058
56593
  init_auth();
56059
56594
  init_runner();
56060
56595
  import chalk7 from "chalk";
56596
+ function argsRequestJson(args) {
56597
+ for (let i = 0;i < args.length; i++) {
56598
+ const arg = args[i];
56599
+ if (arg === "--json")
56600
+ return true;
56601
+ if ((arg === "--format" || arg === "-f") && args[i + 1] === "json")
56602
+ return true;
56603
+ if (arg === "--format=json" || arg === "-f=json")
56604
+ return true;
56605
+ }
56606
+ return false;
56607
+ }
56061
56608
  function registerCommands6(program2) {
56062
56609
  program2.command("ops").description("List available API operations for a connector").argument("<name>", "Connector name (e.g. stripe, gmail)").argument("[command]", "Get detailed help for a specific subcommand").option("--json", "Output as JSON").action(async (name, command, options) => {
56063
56610
  const meta = getConnector(name);
@@ -56111,7 +56658,7 @@ ${meta.displayName} operations:
56111
56658
  }
56112
56659
  }
56113
56660
  });
56114
- program2.command("run").description("Execute an API operation on a connector").argument("<name>", "Connector name (e.g. stripe, gmail)").argument("[args...]", "Command arguments (e.g. products list --limit 5)").option("--timeout <ms>", "Timeout in milliseconds", "30000").passThroughOptions().action(async (name, args, options) => {
56661
+ program2.command("run").description("Execute an API operation on a connector").argument("<name>", "Connector name (e.g. stripe, gmail)").argument("[args...]", "Command arguments (e.g. products list --limit 5)").option("--timeout <ms>", "Timeout in milliseconds", "30000").option("-v, --verbose", "Print full command output without truncation", false).passThroughOptions().action(async (name, args, options) => {
56115
56662
  const meta = getConnector(name);
56116
56663
  if (!meta) {
56117
56664
  console.error(chalk7.red(`Connector '${name}' not found.`));
@@ -56129,7 +56676,12 @@ ${meta.displayName} operations:
56129
56676
  }
56130
56677
  const result = await runConnectorCommand2(name, args, parseInt(options.timeout));
56131
56678
  if (result.stdout) {
56132
- console.log(result.stdout);
56679
+ const preserveMachineOutput = argsRequestJson(args);
56680
+ const output = maybeTruncateOutput(result.stdout, {
56681
+ enabled: !options.verbose && !preserveMachineOutput,
56682
+ hint: "Use `connectors run --verbose <connector> ...` for full output."
56683
+ });
56684
+ console.log(output.text);
56133
56685
  }
56134
56686
  if (result.stderr) {
56135
56687
  console.error(result.stderr);
@@ -56346,6 +56898,15 @@ init_workflow_runner();
56346
56898
  init_database();
56347
56899
  init_registry2();
56348
56900
  import chalk8 from "chalk";
56901
+ function parsePagingOptions(options) {
56902
+ const parsedLimit = parseNonNegativeInt(options.limit, "--limit");
56903
+ const parsedOffset = parseNonNegativeInt(options.offset, "--offset");
56904
+ return {
56905
+ limit: parsedLimit.value,
56906
+ offset: parsedOffset.value ?? 0,
56907
+ error: parsedLimit.error || parsedOffset.error
56908
+ };
56909
+ }
56349
56910
  function registerCommands7(program2) {
56350
56911
  program2.command("hot").description("Show top connectors by usage").option("--limit <n>", "Max results", "10").option("--days <n>", "Time window in days", "7").option("--json", "Output as JSON").action((options) => {
56351
56912
  const { getTopConnectors: getTopConnectors2 } = (init_usage(), __toCommonJS(exports_usage));
@@ -56403,21 +56964,74 @@ Top connectors (last ${options.days} days):
56403
56964
  }
56404
56965
  console.log(chalk8.green("\u2713") + ` Job created: ${job.name} (${job.cron})`);
56405
56966
  });
56406
- jobsCmd.command("list").description("List all jobs").option("--json", "Output as JSON").action((options) => {
56967
+ jobsCmd.command("list").description("List all jobs").option("--limit <n>", "Limit rows").option("--offset <n>", "Skip first N rows").option("-v, --verbose", "Show all rows in human output", false).option("--json", "Output as JSON").action((options) => {
56968
+ const paging = parsePagingOptions(options);
56969
+ if (paging.error) {
56970
+ if (options.json)
56971
+ console.log(JSON.stringify({ error: paging.error }));
56972
+ else
56973
+ console.log(chalk8.red(paging.error));
56974
+ process.exit(1);
56975
+ return;
56976
+ }
56407
56977
  const jobs = listJobs(getDatabase());
56978
+ const page = pageItems(jobs, {
56979
+ offset: paging.offset,
56980
+ limit: paging.limit ?? (options.verbose ? undefined : DEFAULT_COMPACT_LIMIT)
56981
+ });
56408
56982
  if (options.json) {
56409
- console.log(JSON.stringify(jobs));
56983
+ const data = options.limit || options.offset ? page.items : jobs;
56984
+ console.log(JSON.stringify(data));
56410
56985
  return;
56411
56986
  }
56412
56987
  if (jobs.length === 0) {
56413
56988
  console.log(chalk8.dim("No jobs configured."));
56414
56989
  return;
56415
56990
  }
56416
- for (const j of jobs) {
56991
+ console.log(chalk8.bold(`
56992
+ Jobs (showing ${page.items.length} of ${jobs.length})
56993
+ `));
56994
+ for (const j of page.items) {
56417
56995
  const status = j.enabled ? chalk8.green("enabled") : chalk8.dim("disabled");
56418
56996
  const strip = j.strip ? chalk8.cyan(" [strip]") : "";
56419
56997
  console.log(` ${j.name.padEnd(20)} ${j.connector}.${j.command.padEnd(16)} ${j.cron.padEnd(15)} ${status}${strip}`);
56420
56998
  }
56999
+ if (page.nextOffset !== null)
57000
+ console.log(chalk8.dim(`
57001
+ More rows: connectors jobs list --offset ${page.nextOffset}`));
57002
+ console.log(chalk8.dim(" More detail: connectors jobs show <name> | connectors jobs list --verbose | connectors jobs list --json"));
57003
+ });
57004
+ jobsCmd.command("show").alias("inspect").description("Show full details for one job").argument("<name>", "Job name").option("--json", "Output as JSON").action((name, options) => {
57005
+ const db = getDatabase();
57006
+ const job = getJobByName(name, db);
57007
+ if (!job) {
57008
+ console.error(chalk8.red(`Job "${name}" not found`));
57009
+ process.exit(1);
57010
+ }
57011
+ const latestRun = getLatestRun(job.id, db);
57012
+ if (options.json) {
57013
+ console.log(JSON.stringify({ job, latestRun }, null, 2));
57014
+ return;
57015
+ }
57016
+ console.log(chalk8.bold(`
57017
+ Job: ${job.name}
57018
+ `));
57019
+ console.log(` Connector: ${job.connector}`);
57020
+ console.log(` Command: ${job.command}${job.args.length ? ` ${job.args.join(" ")}` : ""}`);
57021
+ console.log(` Cron: ${job.cron}`);
57022
+ console.log(` Enabled: ${job.enabled ? "yes" : "no"}`);
57023
+ console.log(` Strip: ${job.strip ? "yes" : "no"}`);
57024
+ if (latestRun) {
57025
+ console.log(chalk8.bold(`
57026
+ Latest Run`));
57027
+ console.log(` Started: ${latestRun.started_at}`);
57028
+ console.log(` Exit: ${latestRun.exit_code ?? "?"}`);
57029
+ const latestOutput = latestRun.stripped_output ?? latestRun.raw_output;
57030
+ if (latestOutput)
57031
+ console.log(` Output: ${truncateText(latestOutput, 160)}`);
57032
+ }
57033
+ console.log(chalk8.dim(`
57034
+ More detail: connectors jobs show ${name} --json | connectors jobs logs ${name}`));
56421
57035
  });
56422
57036
  jobsCmd.command("run").description("Manually trigger a job").argument("<name>", "Job name").option("--json", "Output as JSON").action(async (name, options) => {
56423
57037
  const db = getDatabase();
@@ -56435,8 +57049,13 @@ Top connectors (last ${options.days} days):
56435
57049
  }
56436
57050
  const icon = result.exit_code === 0 ? chalk8.green("\u2713") : chalk8.red("\u2717");
56437
57051
  console.log(`${icon} Run ${result.run_id} \u2014 exit ${result.exit_code}`);
56438
- if (result.output)
56439
- console.log(result.output.slice(0, 2000));
57052
+ if (result.output) {
57053
+ const output = maybeTruncateOutput(result.output, {
57054
+ maxChars: 2000,
57055
+ hint: `Run connectors jobs run ${name} --json for structured full output.`
57056
+ });
57057
+ console.log(output.text);
57058
+ }
56440
57059
  });
56441
57060
  jobsCmd.command("logs").description("Show recent runs for a job").argument("<name>", "Job name").option("--limit <n>", "Max results", "10").option("--json", "Output as JSON").action((name, options) => {
56442
57061
  const db = getDatabase();
@@ -56505,20 +57124,63 @@ Top connectors (last ${options.days} days):
56505
57124
  }
56506
57125
  console.log(chalk8.green("\u2713") + ` Workflow "${wf.name}" created (${wf.steps.length} steps)`);
56507
57126
  });
56508
- workflowsCmd.command("list").description("List all workflows").option("--json", "Output as JSON").action((options) => {
57127
+ workflowsCmd.command("list").description("List all workflows").option("--limit <n>", "Limit rows").option("--offset <n>", "Skip first N rows").option("-v, --verbose", "Show all rows in human output", false).option("--json", "Output as JSON").action((options) => {
57128
+ const paging = parsePagingOptions(options);
57129
+ if (paging.error) {
57130
+ if (options.json)
57131
+ console.log(JSON.stringify({ error: paging.error }));
57132
+ else
57133
+ console.log(chalk8.red(paging.error));
57134
+ process.exit(1);
57135
+ return;
57136
+ }
56509
57137
  const wfs = listWorkflows(getDatabase());
57138
+ const page = pageItems(wfs, {
57139
+ offset: paging.offset,
57140
+ limit: paging.limit ?? (options.verbose ? undefined : DEFAULT_COMPACT_LIMIT)
57141
+ });
56510
57142
  if (options.json) {
56511
- console.log(JSON.stringify(wfs));
57143
+ const data = options.limit || options.offset ? page.items : wfs;
57144
+ console.log(JSON.stringify(data));
56512
57145
  return;
56513
57146
  }
56514
57147
  if (wfs.length === 0) {
56515
57148
  console.log(chalk8.dim("No workflows configured."));
56516
57149
  return;
56517
57150
  }
56518
- for (const wf of wfs) {
57151
+ console.log(chalk8.bold(`
57152
+ Workflows (showing ${page.items.length} of ${wfs.length})
57153
+ `));
57154
+ for (const wf of page.items) {
56519
57155
  const status = wf.enabled ? chalk8.green("enabled") : chalk8.dim("disabled");
56520
57156
  console.log(` ${wf.name.padEnd(20)} ${String(wf.steps.length).padStart(2)} steps ${status}`);
56521
57157
  }
57158
+ if (page.nextOffset !== null)
57159
+ console.log(chalk8.dim(`
57160
+ More rows: connectors workflows list --offset ${page.nextOffset}`));
57161
+ console.log(chalk8.dim(" More detail: connectors workflows show <name> | connectors workflows list --verbose | connectors workflows list --json"));
57162
+ });
57163
+ workflowsCmd.command("show").alias("inspect").description("Show full details for one workflow").argument("<name>", "Workflow name").option("--json", "Output as JSON").action((name, options) => {
57164
+ const wf = getWorkflowByName(name, getDatabase());
57165
+ if (!wf) {
57166
+ console.error(chalk8.red(`Workflow "${name}" not found`));
57167
+ process.exit(1);
57168
+ }
57169
+ if (options.json) {
57170
+ console.log(JSON.stringify(wf, null, 2));
57171
+ return;
57172
+ }
57173
+ console.log(chalk8.bold(`
57174
+ Workflow: ${wf.name}
57175
+ `));
57176
+ console.log(` Enabled: ${wf.enabled ? "yes" : "no"}`);
57177
+ console.log(` Steps: ${wf.steps.length}`);
57178
+ for (const [index, step] of wf.steps.entries()) {
57179
+ const args = Array.isArray(step.args) && step.args.length > 0 ? ` ${step.args.join(" ")}` : "";
57180
+ console.log(` ${String(index + 1).padStart(2)}. ${step.connector} ${step.command}${args}`);
57181
+ }
57182
+ console.log(chalk8.dim(`
57183
+ More detail: connectors workflows show ${name} --json`));
56522
57184
  });
56523
57185
  workflowsCmd.command("run").description("Run a workflow").argument("<name>", "Workflow name").option("--json", "Output as JSON").action(async (name, options) => {
56524
57186
  const wf = getWorkflowByName(name, getDatabase());