@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.
- package/dist/cli.js +199 -43
- 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.
|
|
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 (
|
|
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 (!
|
|
2773
|
-
return outputError({
|
|
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: "
|
|
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, {
|