@outlit/cli 1.7.1 → 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 +199 -43
  2. package/package.json +1 -1
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.1",
107
+ version: "1.8.0",
108
108
  description: "CLI for Outlit customer intelligence platform",
109
109
  license: "Apache-2.0",
110
110
  repository: {
@@ -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";
@@ -2726,17 +2727,173 @@ var init_api = __esm(() => {
2726
2727
  init_table();
2727
2728
  });
2728
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
+
2729
2848
  // src/commands/auth/login.ts
2730
2849
  var exports_login = {};
2731
2850
  __export(exports_login, {
2732
2851
  default: () => login_default
2733
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
+ }
2734
2890
  var login_default;
2735
2891
  var init_login = __esm(() => {
2736
2892
  init_dist3();
2737
2893
  init_dist();
2738
2894
  init_output2();
2739
2895
  init_api();
2896
+ init_cli_auth();
2740
2897
  init_config();
2741
2898
  init_output();
2742
2899
  init_tty();
@@ -2753,6 +2910,7 @@ var init_login = __esm(() => {
2753
2910
  "",
2754
2911
  "Examples:",
2755
2912
  " outlit auth login # interactive (TTY)",
2913
+ " outlit auth login --browser # browser approval, works in agent shells",
2756
2914
  " outlit auth login --key ok_xxx # non-interactive (CI, scripts, agents)"
2757
2915
  ].join(`
2758
2916
  `)
@@ -2763,14 +2921,33 @@ var init_login = __esm(() => {
2763
2921
  type: "string",
2764
2922
  description: `API key to store (required in non-interactive / CI mode).
2765
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."
2766
2928
  }
2767
2929
  },
2768
2930
  async run({ args }) {
2769
2931
  const json = !!args.json;
2770
2932
  let apiKey = args.key;
2933
+ const interactive = isInteractive();
2934
+ const shouldUseBrowserAuth = args.browser || !interactive && !isCiEnvironment();
2935
+ if (!apiKey) {
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
+ }
2771
2945
  if (!apiKey) {
2772
- if (!isInteractive()) {
2773
- return outputError({ message: "--key <apiKey> is required in non-interactive mode", code: "missing_key" }, json);
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);
2774
2951
  }
2775
2952
  We("Outlit CLI -- Login");
2776
2953
  const existing = resolveApiKey();
@@ -2784,33 +2961,35 @@ Format: ok_ followed by 32+ alphanumeric characters.`
2784
2961
  const method = await Je({
2785
2962
  message: "How would you like to get your API key?",
2786
2963
  options: [
2787
- { value: "browser", label: "Open app.outlit.ai in browser" },
2964
+ { value: "browser", label: "Authorize in browser" },
2788
2965
  { value: "manual", label: "Enter API key manually" }
2789
2966
  ]
2790
2967
  });
2791
2968
  if (Ct(method))
2792
2969
  cancelLogin();
2793
2970
  if (method === "browser") {
2971
+ apiKey = await runBrowserAuthFlow(json);
2972
+ } else {
2794
2973
  const opened = openBrowser(OUTLIT_DASHBOARD_URL);
2795
2974
  if (opened) {
2796
2975
  R2.info("Opening browser... paste your key once you have it.");
2797
2976
  } else {
2798
2977
  R2.info(`Could not open browser. Visit ${OUTLIT_DASHBOARD_URL} manually.`);
2799
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;
2800
2992
  }
2801
- const result = await He({
2802
- message: "Paste your Outlit API key:",
2803
- validate: (v) => {
2804
- if (!v)
2805
- return "API key is required";
2806
- if (!v.startsWith("ok_"))
2807
- return 'Outlit API keys start with "ok_"';
2808
- return;
2809
- }
2810
- });
2811
- if (Ct(result))
2812
- cancelLogin();
2813
- apiKey = result;
2814
2993
  }
2815
2994
  if (!apiKey.startsWith("ok_")) {
2816
2995
  return outputError({
@@ -5849,29 +6028,6 @@ var init_list5 = __esm(() => {
5849
6028
  });
5850
6029
  });
5851
6030
 
5852
- // src/lib/poll.ts
5853
- async function pollUntil(fn, predicate, opts = {}) {
5854
- const intervalMs = opts.intervalMs ?? 2000;
5855
- const timeoutMs = opts.timeoutMs ?? 300000;
5856
- const start = Date.now();
5857
- while (Date.now() - start < timeoutMs) {
5858
- try {
5859
- const result = await fn();
5860
- if (predicate(result))
5861
- return result;
5862
- } catch {}
5863
- if (opts.spinner && opts.spinnerMessage) {
5864
- const elapsed = Math.floor((Date.now() - start) / 1000);
5865
- opts.spinner.update(`${opts.spinnerMessage} (${elapsed}s)`);
5866
- }
5867
- await sleep(intervalMs);
5868
- }
5869
- return null;
5870
- }
5871
- function sleep(ms) {
5872
- return new Promise((resolve) => setTimeout(resolve, ms));
5873
- }
5874
-
5875
6031
  // src/commands/integrations/add.ts
5876
6032
  var exports_add = {};
5877
6033
  __export(exports_add, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outlit/cli",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "CLI for Outlit customer intelligence platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {