@outlit/cli 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +492 -70
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -104,7 +104,7 @@ var package_default;
104
104
  var init_package = __esm(() => {
105
105
  package_default = {
106
106
  name: "@outlit/cli",
107
- version: "1.7.0",
107
+ version: "1.8.0",
108
108
  description: "CLI for Outlit customer intelligence platform",
109
109
  license: "Apache-2.0",
110
110
  repository: {
@@ -135,7 +135,7 @@ var init_package = __esm(() => {
135
135
  },
136
136
  dependencies: {
137
137
  "@clack/prompts": "^1.0.1",
138
- "@outlit/tools": "^0.2.0",
138
+ "@outlit/tools": "^0.2.1",
139
139
  citty: "^0.2.1"
140
140
  },
141
141
  devDependencies: {
@@ -183,14 +183,15 @@ function promptInput(label, opts) {
183
183
  function isInteractive() {
184
184
  if (!process.stdin.isTTY || !process.stdout.isTTY)
185
185
  return false;
186
- if (process.env.CI === "true" || process.env.CI === "1")
187
- return false;
188
- if (process.env.GITHUB_ACTIONS)
186
+ if (isCiEnvironment())
189
187
  return false;
190
188
  if (process.env.TERM === "dumb")
191
189
  return false;
192
190
  return true;
193
191
  }
192
+ function isCiEnvironment() {
193
+ return process.env.CI === "true" || process.env.CI === "1" || Boolean(process.env.GITHUB_ACTIONS);
194
+ }
194
195
  var isUnicodeSupported;
195
196
  var init_tty = __esm(() => {
196
197
  isUnicodeSupported = process.platform !== "win32" || Boolean(process.env.WT_SESSION) || process.env.TERM_PROGRAM === "vscode";
@@ -1602,7 +1603,7 @@ function createOutlitClient(options) {
1602
1603
  }
1603
1604
  };
1604
1605
  }
1605
- var customerToolNames, customerSourceTypes, customerSourceTypeAliases, customerSourceTypeInputs, notificationProviderValues, customerToolContracts, customerBillingStatuses, customerFactStatuses, customerFactTypes, unsupportedCustomerFactTypes, customerFactCategories, customerIncludeSections, customerTimeframes, notificationSeverityValues, timelineChannels, timelineTimeframes, userJourneyStages, customerToolNameSet, customerSourceTypeSet, customerSourceTypeAliasMap, iso8601UtcDateTimeRegex, DEFAULT_OUTLIT_API_URL = "https://app.outlit.ai", actionToolNames, defaultAgentToolNames, sqlToolNames, analyticalAgentToolNames;
1606
+ var customerToolNames, customerSourceTypes, customerSourceTypeAliases, customerSourceTypeInputs, customerToolContracts, customerBillingStatuses, customerFactStatuses, customerFactTypes, unsupportedCustomerFactTypes, customerFactCategories, customerIncludeSections, customerTimeframes, notificationProviderValues, notificationSeverityValues, timelineChannels, timelineTimeframes, userJourneyStages, customerToolNameSet, customerSourceTypeSet, customerSourceTypeAliasMap, iso8601UtcDateTimeRegex, DEFAULT_OUTLIT_API_URL = "https://app.outlit.ai", actionToolNames, defaultAgentToolNames, sqlToolNames, analyticalAgentToolNames;
1606
1607
  var init_dist4 = __esm(() => {
1607
1608
  customerToolNames = [
1608
1609
  "outlit_list_customers",
@@ -1612,6 +1613,7 @@ var init_dist4 = __esm(() => {
1612
1613
  "outlit_list_facts",
1613
1614
  "outlit_get_fact",
1614
1615
  "outlit_get_source",
1616
+ "outlit_list_sources",
1615
1617
  "outlit_search_customer_context",
1616
1618
  "outlit_query",
1617
1619
  "outlit_schema",
@@ -1634,7 +1636,6 @@ var init_dist4 = __esm(() => {
1634
1636
  "CRM",
1635
1637
  "CRM_OPPORTUNITY"
1636
1638
  ];
1637
- notificationProviderValues = ["slack"];
1638
1639
  customerToolContracts = {
1639
1640
  outlit_list_customers: {
1640
1641
  toolName: "outlit_list_customers",
@@ -2039,6 +2040,69 @@ var init_dist4 = __esm(() => {
2039
2040
  additionalProperties: false
2040
2041
  }
2041
2042
  },
2043
+ outlit_list_sources: {
2044
+ toolName: "outlit_list_sources",
2045
+ description: "List concrete source records deterministically. Use this instead of semantic search when you need enumerated calls, emails, calendar events, support tickets, or opportunities.",
2046
+ inputSchema: {
2047
+ $schema: "https://json-schema.org/draft/2020-12/schema",
2048
+ type: "object",
2049
+ properties: {
2050
+ sourceType: {
2051
+ description: "Generic source type to list.",
2052
+ type: "string",
2053
+ enum: customerSourceTypeInputs
2054
+ },
2055
+ customer: {
2056
+ description: "Customer ID, domain, or name to scope source listing.",
2057
+ type: "string",
2058
+ minLength: 1,
2059
+ maxLength: 200
2060
+ },
2061
+ after: {
2062
+ description: "ISO 8601 datetime lower bound.",
2063
+ type: "string",
2064
+ format: "date-time",
2065
+ pattern: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2066
+ },
2067
+ before: {
2068
+ description: "ISO 8601 datetime upper bound.",
2069
+ type: "string",
2070
+ format: "date-time",
2071
+ pattern: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2072
+ },
2073
+ participant: {
2074
+ description: "Participant email or name to filter source records.",
2075
+ type: "string",
2076
+ minLength: 1,
2077
+ maxLength: 500
2078
+ },
2079
+ provider: {
2080
+ description: "Provider identifier such as gmail, gong, or google-calendar.",
2081
+ type: "string",
2082
+ minLength: 1,
2083
+ maxLength: 200
2084
+ },
2085
+ hasTranscript: {
2086
+ description: "Filter call sources by transcript presence.",
2087
+ type: "boolean"
2088
+ },
2089
+ limit: {
2090
+ description: "Results per page.",
2091
+ default: 50,
2092
+ type: "integer",
2093
+ minimum: 1,
2094
+ maximum: 100
2095
+ },
2096
+ cursor: {
2097
+ description: "Pagination cursor.",
2098
+ type: "string",
2099
+ minLength: 1,
2100
+ maxLength: 2000
2101
+ }
2102
+ },
2103
+ additionalProperties: false
2104
+ }
2105
+ },
2042
2106
  outlit_search_customer_context: {
2043
2107
  toolName: "outlit_search_customer_context",
2044
2108
  description: "Search across all known customer context using a natural-language query. Returns grouped artifact-level results for matching sources and facts. Omit customer to search across all customers in the organization.",
@@ -2210,7 +2274,7 @@ Returns column definitions and example queries for each view.`,
2210
2274
  provider: {
2211
2275
  description: "Notification provider",
2212
2276
  type: "string",
2213
- enum: notificationProviderValues
2277
+ enum: ["slack"]
2214
2278
  },
2215
2279
  channelId: {
2216
2280
  description: "Provider-specific destination channel ID",
@@ -2321,6 +2385,7 @@ Returns column definitions and example queries for each view.`,
2321
2385
  "behaviorMetrics"
2322
2386
  ];
2323
2387
  customerTimeframes = ["7d", "14d", "30d", "90d"];
2388
+ notificationProviderValues = ["slack"];
2324
2389
  notificationSeverityValues = ["low", "medium", "high"];
2325
2390
  timelineChannels = [
2326
2391
  "SDK",
@@ -2541,10 +2606,12 @@ function renderTable(headers, rows, emptyMessage = "(no results)") {
2541
2606
  function renderPaginationHint(pagination, itemCount) {
2542
2607
  if (itemCount === 0)
2543
2608
  return "";
2609
+ const total = typeof pagination.total === "number" ? pagination.total : null;
2544
2610
  if (pagination.hasMore && pagination.nextCursor) {
2545
- return `Showing ${itemCount} of ${pagination.total} total. Next page: --cursor ${pagination.nextCursor}`;
2611
+ const countLabel = total === null ? `${itemCount} results` : `${itemCount} of ${total} total`;
2612
+ return `Showing ${countLabel}. Next page: --cursor ${pagination.nextCursor}`;
2546
2613
  }
2547
- return `Showing all ${pagination.total} results.`;
2614
+ return total === null ? `Showing ${itemCount} results.` : `Showing all ${total} results.`;
2548
2615
  }
2549
2616
  var BOX;
2550
2617
  var init_table = __esm(() => {
@@ -2601,10 +2668,11 @@ async function pingApiKey(apiKey) {
2601
2668
  const message = payload?.error ?? (text.length > 0 ? text : `API error (${response.status})`);
2602
2669
  throw new Error(message);
2603
2670
  }
2671
+ return payload;
2604
2672
  }
2605
2673
  async function validateKeyOrExit(apiKey, json) {
2606
2674
  try {
2607
- await pingApiKey(apiKey);
2675
+ return await pingApiKey(apiKey);
2608
2676
  } catch (err) {
2609
2677
  return outputError({
2610
2678
  message: `API key is invalid or expired: ${errorMessage(err, "unknown error")}`,
@@ -2659,17 +2727,173 @@ var init_api = __esm(() => {
2659
2727
  init_table();
2660
2728
  });
2661
2729
 
2730
+ // src/lib/poll.ts
2731
+ async function pollUntil(fn, predicate, opts = {}) {
2732
+ const intervalMs = opts.intervalMs ?? 2000;
2733
+ const timeoutMs = opts.timeoutMs ?? 300000;
2734
+ const start = Date.now();
2735
+ while (Date.now() - start < timeoutMs) {
2736
+ try {
2737
+ const result = await fn();
2738
+ if (predicate(result))
2739
+ return result;
2740
+ } catch {}
2741
+ if (opts.spinner && opts.spinnerMessage) {
2742
+ const elapsed = Math.floor((Date.now() - start) / 1000);
2743
+ opts.spinner.update(`${opts.spinnerMessage} (${elapsed}s)`);
2744
+ }
2745
+ await sleep(intervalMs);
2746
+ }
2747
+ return null;
2748
+ }
2749
+ function sleep(ms) {
2750
+ return new Promise((resolve) => setTimeout(resolve, ms));
2751
+ }
2752
+
2753
+ // src/lib/cli-auth.ts
2754
+ function buildApiUrl(baseUrl, path) {
2755
+ return new URL(path, baseUrl).toString();
2756
+ }
2757
+ function asObject(value) {
2758
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2759
+ throw new Error("Invalid response from Outlit API");
2760
+ }
2761
+ return value;
2762
+ }
2763
+ async function readJsonObject(response) {
2764
+ const text = await response.text();
2765
+ const parsed = text ? (() => {
2766
+ try {
2767
+ return JSON.parse(text);
2768
+ } catch {
2769
+ throw new Error(text);
2770
+ }
2771
+ })() : {};
2772
+ return asObject(parsed);
2773
+ }
2774
+ function errorFromPayload(payload, fallback) {
2775
+ return typeof payload.error === "string" ? payload.error : fallback;
2776
+ }
2777
+ async function startCliAuthRequest(baseUrl = process.env.OUTLIT_API_URL ?? DEFAULT_API_URL) {
2778
+ const response = await globalThis.fetch(buildApiUrl(baseUrl, "/api/cli-auth/start"), {
2779
+ method: "POST",
2780
+ headers: {
2781
+ "User-Agent": `outlit-cli/${CLI_VERSION2}`
2782
+ }
2783
+ });
2784
+ const payload = await readJsonObject(response);
2785
+ if (!response.ok) {
2786
+ throw new Error(errorFromPayload(payload, `CLI auth start failed (${response.status})`));
2787
+ }
2788
+ if (typeof payload.requestId !== "string" || typeof payload.pollToken !== "string" || typeof payload.userCode !== "string" || typeof payload.approveUrl !== "string" || typeof payload.expiresAt !== "string" || typeof payload.intervalSeconds !== "number") {
2789
+ throw new Error("Invalid CLI auth start response from Outlit API");
2790
+ }
2791
+ return {
2792
+ requestId: payload.requestId,
2793
+ pollToken: payload.pollToken,
2794
+ userCode: payload.userCode,
2795
+ approveUrl: payload.approveUrl,
2796
+ expiresAt: payload.expiresAt,
2797
+ intervalSeconds: payload.intervalSeconds
2798
+ };
2799
+ }
2800
+ async function pollCliAuthRequest(baseUrl, input) {
2801
+ const response = await globalThis.fetch(buildApiUrl(baseUrl, "/api/cli-auth/poll"), {
2802
+ method: "POST",
2803
+ headers: {
2804
+ "Content-Type": "application/json",
2805
+ "User-Agent": `outlit-cli/${CLI_VERSION2}`
2806
+ },
2807
+ body: JSON.stringify(input)
2808
+ });
2809
+ const payload = await readJsonObject(response);
2810
+ if (payload.status === "invalid") {
2811
+ return { status: "invalid" };
2812
+ }
2813
+ if (!response.ok) {
2814
+ throw new Error(errorFromPayload(payload, `CLI auth poll failed (${response.status})`));
2815
+ }
2816
+ if (payload.status === "approved") {
2817
+ if (typeof payload.apiKey !== "string" || typeof payload.keyPrefix !== "string") {
2818
+ throw new Error("Invalid approved CLI auth response from Outlit API");
2819
+ }
2820
+ return {
2821
+ status: "approved",
2822
+ apiKey: payload.apiKey,
2823
+ keyPrefix: payload.keyPrefix
2824
+ };
2825
+ }
2826
+ if (payload.status === "pending" || payload.status === "failed" || payload.status === "consumed" || payload.status === "expired" || payload.status === "invalid") {
2827
+ return payload;
2828
+ }
2829
+ throw new Error("Invalid CLI auth poll response from Outlit API");
2830
+ }
2831
+ async function waitForCliAuthApproval(baseUrl, request, opts = {}) {
2832
+ const expiresAtMs = new Date(request.expiresAt).getTime();
2833
+ const timeoutMs = opts.timeoutMs ?? Math.max(1000, expiresAtMs - Date.now());
2834
+ const intervalMs = opts.intervalMs ?? request.intervalSeconds * 1000;
2835
+ return pollUntil(() => pollCliAuthRequest(baseUrl, {
2836
+ requestId: request.requestId,
2837
+ pollToken: request.pollToken
2838
+ }), (result) => result.status !== "pending", {
2839
+ ...opts,
2840
+ intervalMs,
2841
+ timeoutMs
2842
+ });
2843
+ }
2844
+ var init_cli_auth = __esm(() => {
2845
+ init_config();
2846
+ });
2847
+
2662
2848
  // src/commands/auth/login.ts
2663
2849
  var exports_login = {};
2664
2850
  __export(exports_login, {
2665
2851
  default: () => login_default
2666
2852
  });
2853
+ async function runBrowserAuthFlow(json) {
2854
+ const baseUrl = process.env.OUTLIT_API_URL ?? DEFAULT_API_URL;
2855
+ const session = await startCliAuthRequest(baseUrl);
2856
+ const opened = isInteractive() ? openBrowser(session.approveUrl) : false;
2857
+ process.stderr.write([
2858
+ "",
2859
+ "Authorize Outlit CLI in your browser:",
2860
+ ` ${session.approveUrl}`,
2861
+ "",
2862
+ `Confirm this terminal code: ${session.userCode}`,
2863
+ opened ? "Waiting for browser approval..." : "Waiting after you approve the request...",
2864
+ ""
2865
+ ].join(`
2866
+ `));
2867
+ const spinner = isInteractive() ? bt2() : null;
2868
+ spinner?.start("Waiting for browser approval...");
2869
+ const result = await waitForCliAuthApproval(baseUrl, session, {
2870
+ spinnerMessage: "Waiting for browser approval..."
2871
+ });
2872
+ if (!result) {
2873
+ spinner?.stop("Approval timed out");
2874
+ return outputError({
2875
+ message: "Timed out waiting for browser approval. Run `outlit auth login --browser` again.",
2876
+ code: "auth_timeout"
2877
+ }, json);
2878
+ }
2879
+ if (result.status !== "approved") {
2880
+ spinner?.stop("Approval did not complete");
2881
+ const message = result.status === "failed" && result.error ? `Browser authorization failed: ${result.error}` : `Browser authorization ${result.status}. Run \`outlit auth login --browser\` again.`;
2882
+ return outputError({
2883
+ message,
2884
+ code: `auth_${result.status}`
2885
+ }, json);
2886
+ }
2887
+ spinner?.stop("Browser authorization approved");
2888
+ return result.apiKey;
2889
+ }
2667
2890
  var login_default;
2668
2891
  var init_login = __esm(() => {
2669
2892
  init_dist3();
2670
2893
  init_dist();
2671
2894
  init_output2();
2672
2895
  init_api();
2896
+ init_cli_auth();
2673
2897
  init_config();
2674
2898
  init_output();
2675
2899
  init_tty();
@@ -2686,6 +2910,7 @@ var init_login = __esm(() => {
2686
2910
  "",
2687
2911
  "Examples:",
2688
2912
  " outlit auth login # interactive (TTY)",
2913
+ " outlit auth login --browser # browser approval, works in agent shells",
2689
2914
  " outlit auth login --key ok_xxx # non-interactive (CI, scripts, agents)"
2690
2915
  ].join(`
2691
2916
  `)
@@ -2696,14 +2921,33 @@ var init_login = __esm(() => {
2696
2921
  type: "string",
2697
2922
  description: `API key to store (required in non-interactive / CI mode).
2698
2923
  Format: ok_ followed by 32+ alphanumeric characters.`
2924
+ },
2925
+ browser: {
2926
+ type: "boolean",
2927
+ description: "Create a scoped CLI key through browser approval instead of pasting a key."
2699
2928
  }
2700
2929
  },
2701
2930
  async run({ args }) {
2702
2931
  const json = !!args.json;
2703
2932
  let apiKey = args.key;
2933
+ const interactive = isInteractive();
2934
+ const shouldUseBrowserAuth = args.browser || !interactive && !isCiEnvironment();
2704
2935
  if (!apiKey) {
2705
- if (!isInteractive()) {
2706
- return outputError({ message: "--key <apiKey> is required in non-interactive mode", code: "missing_key" }, json);
2936
+ if (shouldUseBrowserAuth) {
2937
+ apiKey = await runBrowserAuthFlow(json);
2938
+ } else if (!interactive) {
2939
+ return outputError({
2940
+ message: "--key <apiKey> is required in non-interactive mode. For browser approval, run `outlit auth login --browser`.",
2941
+ code: "missing_key"
2942
+ }, json);
2943
+ }
2944
+ }
2945
+ if (!apiKey) {
2946
+ if (!interactive) {
2947
+ return outputError({
2948
+ message: "--key <apiKey> is required in non-interactive mode. For browser approval, run `outlit auth login --browser`.",
2949
+ code: "missing_key"
2950
+ }, json);
2707
2951
  }
2708
2952
  We("Outlit CLI -- Login");
2709
2953
  const existing = resolveApiKey();
@@ -2717,33 +2961,35 @@ Format: ok_ followed by 32+ alphanumeric characters.`
2717
2961
  const method = await Je({
2718
2962
  message: "How would you like to get your API key?",
2719
2963
  options: [
2720
- { value: "browser", label: "Open app.outlit.ai in browser" },
2964
+ { value: "browser", label: "Authorize in browser" },
2721
2965
  { value: "manual", label: "Enter API key manually" }
2722
2966
  ]
2723
2967
  });
2724
2968
  if (Ct(method))
2725
2969
  cancelLogin();
2726
2970
  if (method === "browser") {
2971
+ apiKey = await runBrowserAuthFlow(json);
2972
+ } else {
2727
2973
  const opened = openBrowser(OUTLIT_DASHBOARD_URL);
2728
2974
  if (opened) {
2729
2975
  R2.info("Opening browser... paste your key once you have it.");
2730
2976
  } else {
2731
2977
  R2.info(`Could not open browser. Visit ${OUTLIT_DASHBOARD_URL} manually.`);
2732
2978
  }
2979
+ const result = await He({
2980
+ message: "Paste your Outlit API key:",
2981
+ validate: (v) => {
2982
+ if (!v)
2983
+ return "API key is required";
2984
+ if (!v.startsWith("ok_"))
2985
+ return 'Outlit API keys start with "ok_"';
2986
+ return;
2987
+ }
2988
+ });
2989
+ if (Ct(result))
2990
+ cancelLogin();
2991
+ apiKey = result;
2733
2992
  }
2734
- const result = await He({
2735
- message: "Paste your Outlit API key:",
2736
- validate: (v) => {
2737
- if (!v)
2738
- return "API key is required";
2739
- if (!v.startsWith("ok_"))
2740
- return 'Outlit API keys start with "ok_"';
2741
- return;
2742
- }
2743
- });
2744
- if (Ct(result))
2745
- cancelLogin();
2746
- apiKey = result;
2747
2993
  }
2748
2994
  if (!apiKey.startsWith("ok_")) {
2749
2995
  return outputError({
@@ -2931,9 +3177,18 @@ var init_whoami = __esm(() => {
2931
3177
  const json = !!args.json;
2932
3178
  const credential = requireCredential(args["api-key"], json);
2933
3179
  const masked = maskKey(credential.key);
2934
- await validateKeyOrExit(credential.key, json);
3180
+ const validation = await validateKeyOrExit(credential.key, json);
2935
3181
  if (isJsonMode(json)) {
2936
- return outputResult({ key: masked, source: credential.source, valid: true });
3182
+ return outputResult({
3183
+ key: masked,
3184
+ source: credential.source,
3185
+ valid: true,
3186
+ organizationId: validation.organizationId,
3187
+ createdById: validation.createdById,
3188
+ organization: validation.organization,
3189
+ createdBy: validation.createdBy,
3190
+ apiKey: validation.apiKey
3191
+ });
2937
3192
  }
2938
3193
  process.stdout.write(`${masked} (${credential.source})
2939
3194
  `);
@@ -4877,12 +5132,183 @@ var init_facts = __esm(() => {
4877
5132
  });
4878
5133
  });
4879
5134
 
5135
+ // src/commands/sources/list.ts
5136
+ var exports_list4 = {};
5137
+ __export(exports_list4, {
5138
+ default: () => list_default4
5139
+ });
5140
+ function parseLimit(value, json) {
5141
+ if (!value)
5142
+ return;
5143
+ const limit = Number(value);
5144
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
5145
+ outputError({
5146
+ message: `--limit must be an integer between 1 and 100 (got: ${value})`,
5147
+ code: "invalid_input"
5148
+ }, json);
5149
+ return;
5150
+ }
5151
+ return limit;
5152
+ }
5153
+ function parseDateArg(name, value, json) {
5154
+ if (!value)
5155
+ return null;
5156
+ if (!ISO_8601_UTC_DATE_TIME_REGEX.test(value)) {
5157
+ outputError({
5158
+ message: `${name} must be a valid ISO 8601 UTC datetime`,
5159
+ code: "invalid_input"
5160
+ }, json);
5161
+ return null;
5162
+ }
5163
+ const date = new Date(value);
5164
+ if (Number.isNaN(date.getTime())) {
5165
+ outputError({
5166
+ message: `${name} must be a valid ISO 8601 datetime`,
5167
+ code: "invalid_input"
5168
+ }, json);
5169
+ return null;
5170
+ }
5171
+ return date;
5172
+ }
5173
+ var sourceTypeDescription2, ISO_8601_UTC_DATE_TIME_REGEX, list_default4;
5174
+ var init_list4 = __esm(() => {
5175
+ init_dist4();
5176
+ init_dist();
5177
+ init_auth();
5178
+ init_output2();
5179
+ init_api();
5180
+ init_output();
5181
+ sourceTypeDescription2 = `${customerSourceTypes.join(", ")} (aliases: ${customerSourceTypeAliases.join(", ")})`;
5182
+ ISO_8601_UTC_DATE_TIME_REGEX = /^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/;
5183
+ list_default4 = defineCommand2({
5184
+ meta: {
5185
+ name: "list",
5186
+ description: [
5187
+ "List concrete source records deterministically.",
5188
+ "",
5189
+ "Use this when you need enumerated calls, emails, calendar events, support tickets, or opportunities rather than semantic ranking.",
5190
+ "",
5191
+ "Examples:",
5192
+ " outlit sources list --customer acme.com --source-type CALL",
5193
+ " outlit sources list --participant alice@acme.com --source-type CALENDAR_EVENT --json",
5194
+ " outlit sources list --after 2026-01-01T00:00:00Z --limit 25",
5195
+ "",
5196
+ `Source types: ${sourceTypeDescription2}`,
5197
+ "",
5198
+ AGENT_JSON_HINT
5199
+ ].join(`
5200
+ `)
5201
+ },
5202
+ args: {
5203
+ ...authArgs,
5204
+ ...outputArgs,
5205
+ "source-type": {
5206
+ type: "string",
5207
+ description: `Source type (${sourceTypeDescription2})`
5208
+ },
5209
+ customer: {
5210
+ type: "string",
5211
+ description: "Customer UUID, domain, or name to scope source listing"
5212
+ },
5213
+ participant: {
5214
+ type: "string",
5215
+ description: "Participant email or name to filter source records"
5216
+ },
5217
+ provider: {
5218
+ type: "string",
5219
+ description: "Provider identifier such as gmail, gong, or google-calendar"
5220
+ },
5221
+ "has-transcript": {
5222
+ type: "boolean",
5223
+ description: "Only list call sources with a finalized transcript"
5224
+ },
5225
+ after: {
5226
+ type: "string",
5227
+ description: "Filter to sources occurring after this ISO 8601 datetime"
5228
+ },
5229
+ before: {
5230
+ type: "string",
5231
+ description: "Filter to sources occurring before this ISO 8601 datetime"
5232
+ },
5233
+ limit: {
5234
+ type: "string",
5235
+ description: "Results per page (1-100). Default: 50."
5236
+ },
5237
+ cursor: {
5238
+ type: "string",
5239
+ description: "Pagination cursor from a previous response (pagination.nextCursor)"
5240
+ }
5241
+ },
5242
+ async run({ args }) {
5243
+ const json = !!args.json;
5244
+ const sourceType = args["source-type"] ? normalizeCustomerSourceType(args["source-type"]) : undefined;
5245
+ if (args["source-type"] && !sourceType) {
5246
+ return outputError({
5247
+ message: `--source-type must be one of ${customerSourceTypeInputs.join(", ")}`,
5248
+ code: "invalid_input"
5249
+ }, json);
5250
+ }
5251
+ if (args["has-transcript"] && sourceType && sourceType !== "CALL") {
5252
+ return outputError({
5253
+ message: "--has-transcript can only be used with CALL sources",
5254
+ code: "invalid_input"
5255
+ }, json);
5256
+ }
5257
+ const afterDate = parseDateArg("--after", args.after, json);
5258
+ if (args.after && !afterDate)
5259
+ return;
5260
+ const beforeDate = parseDateArg("--before", args.before, json);
5261
+ if (args.before && !beforeDate)
5262
+ return;
5263
+ if (afterDate && beforeDate && afterDate.getTime() > beforeDate.getTime()) {
5264
+ return outputError({
5265
+ message: "--after must be before or equal to --before",
5266
+ code: "invalid_input"
5267
+ }, json);
5268
+ }
5269
+ const limit = parseLimit(args.limit, json);
5270
+ if (args.limit && !limit)
5271
+ return;
5272
+ const client = await getClientOrExit(args["api-key"], json);
5273
+ const params = {};
5274
+ if (sourceType)
5275
+ params.sourceType = sourceType;
5276
+ if (args.customer)
5277
+ params.customer = args.customer;
5278
+ if (args.participant)
5279
+ params.participant = args.participant;
5280
+ if (args.provider)
5281
+ params.provider = args.provider;
5282
+ if (args["has-transcript"])
5283
+ params.hasTranscript = true;
5284
+ if (args.after)
5285
+ params.after = args.after;
5286
+ if (args.before)
5287
+ params.before = args.before;
5288
+ if (limit)
5289
+ params.limit = limit;
5290
+ if (args.cursor)
5291
+ params.cursor = args.cursor;
5292
+ return runTool(client, customerToolContracts.outlit_list_sources.toolName, params, json, {
5293
+ table: {
5294
+ columns: [
5295
+ { header: "TYPE", key: "sourceType" },
5296
+ { header: "OCCURRED", key: "occurredAt" },
5297
+ { header: "TITLE", key: "title" },
5298
+ { header: "SOURCE ID", key: "sourceId" }
5299
+ ]
5300
+ }
5301
+ });
5302
+ }
5303
+ });
5304
+ });
5305
+
4880
5306
  // src/commands/sources/get.ts
4881
5307
  var exports_get3 = {};
4882
5308
  __export(exports_get3, {
4883
5309
  default: () => get_default3
4884
5310
  });
4885
- var sourceTypeDescription2, get_default3;
5311
+ var sourceTypeDescription3, get_default3;
4886
5312
  var init_get3 = __esm(() => {
4887
5313
  init_dist4();
4888
5314
  init_dist();
@@ -4890,19 +5316,21 @@ var init_get3 = __esm(() => {
4890
5316
  init_output2();
4891
5317
  init_api();
4892
5318
  init_output();
4893
- sourceTypeDescription2 = `${customerSourceTypes.join(", ")} (aliases: ${customerSourceTypeAliases.join(", ")})`;
5319
+ sourceTypeDescription3 = `${customerSourceTypes.join(", ")} (aliases: ${customerSourceTypeAliases.join(", ")})`;
4894
5320
  get_default3 = defineCommand2({
4895
5321
  meta: {
4896
5322
  name: "get",
4897
5323
  description: [
4898
5324
  "Get one exact source by source type and source id.",
5325
+ "Returns the same normalized source envelope as `sources list`, with more detailed source-specific fields when available.",
4899
5326
  "",
4900
5327
  "Examples:",
4901
5328
  " outlit sources get --source-type CALL --source-id call_123",
4902
5329
  " outlit sources get --source-type OPPORTUNITY --source-id opp_123",
4903
5330
  " outlit sources get --source-type SUPPORT_TICKET --source-id ticket_456 --json",
4904
5331
  "",
4905
- `Source types: ${sourceTypeDescription2}`,
5332
+ `Source types: ${sourceTypeDescription3}`,
5333
+ "Use `outlit sources list` when you need deterministic enumeration rather than exact lookup.",
4906
5334
  "",
4907
5335
  AGENT_JSON_HINT
4908
5336
  ].join(`
@@ -4913,7 +5341,7 @@ var init_get3 = __esm(() => {
4913
5341
  ...outputArgs,
4914
5342
  "source-type": {
4915
5343
  type: "string",
4916
- description: `Source type (${sourceTypeDescription2})`,
5344
+ description: `Source type (${sourceTypeDescription3})`,
4917
5345
  required: true
4918
5346
  },
4919
5347
  "source-id": {
@@ -4953,9 +5381,10 @@ var init_sources = __esm(() => {
4953
5381
  meta: {
4954
5382
  name: "sources",
4955
5383
  description: [
4956
- "Fetch concrete customer sources by type and id.",
5384
+ "List or fetch concrete customer source records.",
4957
5385
  "",
4958
5386
  "Subcommands:",
5387
+ " list -- list source records with deterministic filters",
4959
5388
  " get -- fetch one exact source by sourceType and sourceId",
4960
5389
  "",
4961
5390
  AGENT_JSON_HINT
@@ -4963,6 +5392,7 @@ var init_sources = __esm(() => {
4963
5392
  `)
4964
5393
  },
4965
5394
  subCommands: {
5395
+ list: () => Promise.resolve().then(() => (init_list4(), exports_list4)).then((m) => m.default),
4966
5396
  get: () => Promise.resolve().then(() => (init_get3(), exports_get3)).then((m) => m.default)
4967
5397
  }
4968
5398
  });
@@ -4973,7 +5403,7 @@ var exports_search = {};
4973
5403
  __export(exports_search, {
4974
5404
  default: () => search_default
4975
5405
  });
4976
- var sourceTypeDescription3, search_default;
5406
+ var sourceTypeDescription4, search_default;
4977
5407
  var init_search = __esm(() => {
4978
5408
  init_dist4();
4979
5409
  init_dist();
@@ -4982,7 +5412,7 @@ var init_search = __esm(() => {
4982
5412
  init_api();
4983
5413
  init_config();
4984
5414
  init_output();
4985
- sourceTypeDescription3 = `${customerSourceTypes.join(", ")} (aliases: ${customerSourceTypeAliases.join(", ")})`;
5415
+ sourceTypeDescription4 = `${customerSourceTypes.join(", ")} (aliases: ${customerSourceTypeAliases.join(", ")})`;
4986
5416
  search_default = defineCommand2({
4987
5417
  meta: {
4988
5418
  name: "search",
@@ -4990,6 +5420,7 @@ var init_search = __esm(() => {
4990
5420
  "Search customer context using natural language.",
4991
5421
  "",
4992
5422
  "Performs a semantic search over grouped source and fact results.",
5423
+ "Use `outlit sources list` when you need deterministic source enumeration rather than semantic ranking.",
4993
5424
  "Optionally scope to a specific customer with --customer.",
4994
5425
  "",
4995
5426
  "Examples:",
@@ -5029,7 +5460,7 @@ var init_search = __esm(() => {
5029
5460
  },
5030
5461
  "source-types": {
5031
5462
  type: "string",
5032
- description: `Comma-separated generic source type filter (${sourceTypeDescription3})`
5463
+ description: `Comma-separated generic source type filter (${sourceTypeDescription4})`
5033
5464
  }
5034
5465
  },
5035
5466
  async run({ args }) {
@@ -5548,17 +5979,17 @@ var init_providers = __esm(() => {
5548
5979
  });
5549
5980
 
5550
5981
  // src/commands/integrations/list.ts
5551
- var exports_list4 = {};
5552
- __export(exports_list4, {
5553
- default: () => list_default4
5982
+ var exports_list5 = {};
5983
+ __export(exports_list5, {
5984
+ default: () => list_default5
5554
5985
  });
5555
- var list_default4;
5556
- var init_list4 = __esm(() => {
5986
+ var list_default5;
5987
+ var init_list5 = __esm(() => {
5557
5988
  init_dist();
5558
5989
  init_auth();
5559
5990
  init_output2();
5560
5991
  init_api();
5561
- list_default4 = defineCommand2({
5992
+ list_default5 = defineCommand2({
5562
5993
  meta: {
5563
5994
  name: "list",
5564
5995
  description: [
@@ -5597,29 +6028,6 @@ var init_list4 = __esm(() => {
5597
6028
  });
5598
6029
  });
5599
6030
 
5600
- // src/lib/poll.ts
5601
- async function pollUntil(fn, predicate, opts = {}) {
5602
- const intervalMs = opts.intervalMs ?? 2000;
5603
- const timeoutMs = opts.timeoutMs ?? 300000;
5604
- const start = Date.now();
5605
- while (Date.now() - start < timeoutMs) {
5606
- try {
5607
- const result = await fn();
5608
- if (predicate(result))
5609
- return result;
5610
- } catch {}
5611
- if (opts.spinner && opts.spinnerMessage) {
5612
- const elapsed = Math.floor((Date.now() - start) / 1000);
5613
- opts.spinner.update(`${opts.spinnerMessage} (${elapsed}s)`);
5614
- }
5615
- await sleep(intervalMs);
5616
- }
5617
- return null;
5618
- }
5619
- function sleep(ms) {
5620
- return new Promise((resolve) => setTimeout(resolve, ms));
5621
- }
5622
-
5623
6031
  // src/commands/integrations/add.ts
5624
6032
  var exports_add = {};
5625
6033
  __export(exports_add, {
@@ -6032,7 +6440,7 @@ var init_integrations = __esm(() => {
6032
6440
  `)
6033
6441
  },
6034
6442
  subCommands: {
6035
- list: () => Promise.resolve().then(() => (init_list4(), exports_list4)).then((m) => m.default),
6443
+ list: () => Promise.resolve().then(() => (init_list5(), exports_list5)).then((m) => m.default),
6036
6444
  add: () => Promise.resolve().then(() => (init_add(), exports_add)).then((m) => m.default),
6037
6445
  remove: () => Promise.resolve().then(() => (init_remove(), exports_remove)).then((m) => m.default),
6038
6446
  status: () => Promise.resolve().then(() => (init_status2(), exports_status2)).then((m) => m.default)
@@ -6237,8 +6645,8 @@ function generateFish() {
6237
6645
  }
6238
6646
  }
6239
6647
  }
6240
- return lines.join(`
6241
- `) + `
6648
+ return `${lines.join(`
6649
+ `)}
6242
6650
  `;
6243
6651
  }
6244
6652
  var JSON_F, API_KEY_F, LIMIT_F, CURSOR_F, COMMON, PAGINATED, ACTIVITY_ORDER, COMMANDS, cmdsWithSubs, leafCmds, SCRIPTS, completions_default;
@@ -6361,8 +6769,22 @@ var init_completions = __esm(() => {
6361
6769
  },
6362
6770
  {
6363
6771
  name: "sources",
6364
- desc: "Get a concrete source by type and id",
6772
+ desc: "List or fetch concrete source records",
6365
6773
  subs: [
6774
+ {
6775
+ name: "list",
6776
+ desc: "List source records",
6777
+ flags: [
6778
+ ...PAGINATED,
6779
+ { name: "--source-type", desc: "Source type" },
6780
+ { name: "--customer", desc: "Scope to customer" },
6781
+ { name: "--participant", desc: "Filter by participant" },
6782
+ { name: "--provider", desc: "Filter by provider" },
6783
+ { name: "--has-transcript", desc: "Only calls with transcripts" },
6784
+ { name: "--after", desc: "Sources after date (ISO 8601)" },
6785
+ { name: "--before", desc: "Sources before date (ISO 8601)" }
6786
+ ]
6787
+ },
6366
6788
  {
6367
6789
  name: "get",
6368
6790
  desc: "Get one exact source record",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outlit/cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "CLI for Outlit customer intelligence platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@clack/prompts": "^1.0.1",
34
- "@outlit/tools": "^0.2.0",
34
+ "@outlit/tools": "^0.2.1",
35
35
  "citty": "^0.2.1"
36
36
  },
37
37
  "devDependencies": {