@keystrokehq/cli 0.0.2 → 0.0.3
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.
|
@@ -25,7 +25,7 @@ function createAuthCommand() {
|
|
|
25
25
|
description: "Authenticate Keystroke CLI via device flow",
|
|
26
26
|
schema: AuthOptionsSchema,
|
|
27
27
|
optionsConfig: AUTH_OPTIONS_CONFIG,
|
|
28
|
-
loadHandler: async () => (await import("./auth.handler-
|
|
28
|
+
loadHandler: async () => (await import("./auth.handler-CbhiLOG1.mjs")).handleAuth,
|
|
29
29
|
subcommands: [
|
|
30
30
|
createTypedCommand({
|
|
31
31
|
name: "clear",
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { n as DEFAULT_CLI_WEB_URL, t as DEFAULT_CLI_SERVER_URL } from "./default-urls-BS4twrsS.mjs";
|
|
4
|
+
import { C as CliExitError, _ as AUTH_URL_PATH, b as CALLBACK_PATH, h as toErrorMessage, i as logger, t as ui, v as CALLBACK_LOOPBACK_HOST, x as CLI_AUTH_COMMAND, y as CALLBACK_LOOPBACK_ORIGIN } from "./keystroke.mjs";
|
|
4
5
|
import { f as upsertOrgCredentials, i as getCredentialsFilePath } from "./dist-BF6r1hfv.mjs";
|
|
5
6
|
import { a as resolveCliWebUrl, i as resolveCliServerUrl } from "./env-YTZGKGIu.mjs";
|
|
6
7
|
import { t as openBrowser } from "./browser-gddMccBQ.mjs";
|
|
7
8
|
import { hostname } from "node:os";
|
|
8
9
|
import { z } from "zod";
|
|
10
|
+
import { confirm, isCancel } from "@clack/prompts";
|
|
9
11
|
import { randomBytes } from "node:crypto";
|
|
10
12
|
import { createServer } from "node:http";
|
|
11
13
|
//#region src/commands/auth/device-auth.ts
|
|
@@ -237,6 +239,74 @@ async function createDeviceCallbackListener(expectedState, timeoutMs) {
|
|
|
237
239
|
}
|
|
238
240
|
//#endregion
|
|
239
241
|
//#region src/commands/auth/auth.handler.ts
|
|
242
|
+
const LOOPBACK_HOSTNAMES = new Set([
|
|
243
|
+
"localhost",
|
|
244
|
+
"127.0.0.1",
|
|
245
|
+
"::1"
|
|
246
|
+
]);
|
|
247
|
+
function isLoopbackUrl(rawUrl) {
|
|
248
|
+
try {
|
|
249
|
+
const parsed = new URL(rawUrl);
|
|
250
|
+
const host = parsed.hostname.startsWith("[") ? parsed.hostname.slice(1, -1) : parsed.hostname;
|
|
251
|
+
return LOOPBACK_HOSTNAMES.has(host);
|
|
252
|
+
} catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function resolveAuthHosts(options, ctx) {
|
|
257
|
+
const savedWeb = ctx.storedCredentials?.webUrl;
|
|
258
|
+
const savedServer = ctx.storedCredentials?.serverUrl;
|
|
259
|
+
const webUrl = resolveCliWebUrl(options.webUrl, savedWeb);
|
|
260
|
+
const serverUrl = resolveCliServerUrl(ctx.baseUrl);
|
|
261
|
+
return {
|
|
262
|
+
webUrl,
|
|
263
|
+
serverUrl,
|
|
264
|
+
webFromFlag: typeof options.webUrl === "string" && options.webUrl.length > 0,
|
|
265
|
+
webMatchesSaved: savedWeb === webUrl,
|
|
266
|
+
serverMatchesSaved: savedServer === serverUrl
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function shouldConfirmLoopback(hosts) {
|
|
270
|
+
if (isLoopbackUrl("https://keystroke.ai") && isLoopbackUrl("https://api.kestroke.ai")) return false;
|
|
271
|
+
const webNeedsConfirm = !hosts.webFromFlag && isLoopbackUrl(hosts.webUrl);
|
|
272
|
+
const serverNeedsConfirm = isLoopbackUrl(hosts.serverUrl);
|
|
273
|
+
return webNeedsConfirm || serverNeedsConfirm;
|
|
274
|
+
}
|
|
275
|
+
function describeHostSource(matchesSaved, fromFlag = false) {
|
|
276
|
+
if (fromFlag) return "flag";
|
|
277
|
+
if (matchesSaved) return "saved credentials";
|
|
278
|
+
return "env or default";
|
|
279
|
+
}
|
|
280
|
+
function printResolvedHosts(hosts) {
|
|
281
|
+
ui.hint(`Web URL: ${hosts.webUrl} (${describeHostSource(hosts.webMatchesSaved, hosts.webFromFlag)})`);
|
|
282
|
+
ui.hint(`API URL: ${hosts.serverUrl} (${describeHostSource(hosts.serverMatchesSaved)})`);
|
|
283
|
+
}
|
|
284
|
+
async function confirmLoopbackHostsOrExit(hosts) {
|
|
285
|
+
ui.br();
|
|
286
|
+
ui.warn("About to authenticate against a local development environment");
|
|
287
|
+
if (hosts.webMatchesSaved || hosts.serverMatchesSaved) ui.hint(`Saved at: ${getCredentialsFilePath()} (from a previous keystroke auth run)`);
|
|
288
|
+
else ui.hint("Resolved from your environment (WEB_URL / SERVER_URL).");
|
|
289
|
+
ui.hint(`CLI default is ${DEFAULT_CLI_WEB_URL} / ${DEFAULT_CLI_SERVER_URL}.`);
|
|
290
|
+
ui.hint("To switch hosts:");
|
|
291
|
+
ui.hint(` - Reset and re-auth: ${CLI_AUTH_COMMAND} clear, then ${CLI_AUTH_COMMAND}`);
|
|
292
|
+
ui.hint(` - Override once: ${CLI_AUTH_COMMAND} --web-url <url> --server-url <url>`);
|
|
293
|
+
ui.br();
|
|
294
|
+
if (process.stdin.isTTY !== true) {
|
|
295
|
+
ui.warn("Non-interactive shell detected — continuing with the local URLs.");
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const proceed = await confirm({
|
|
299
|
+
message: "Continue signing in against these local URLs?",
|
|
300
|
+
initialValue: false
|
|
301
|
+
});
|
|
302
|
+
if (isCancel(proceed) || proceed !== true) {
|
|
303
|
+
ui.hint("Cancelled. No credentials were changed.");
|
|
304
|
+
throw new CliExitError("Auth cancelled by user.", {
|
|
305
|
+
exitCode: 0,
|
|
306
|
+
reported: true
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
240
310
|
function getVerificationCode(state) {
|
|
241
311
|
return state.slice(0, 8).toUpperCase();
|
|
242
312
|
}
|
|
@@ -266,17 +336,22 @@ async function warnIfAlreadyAuthenticated(ctx) {
|
|
|
266
336
|
} catch {
|
|
267
337
|
return;
|
|
268
338
|
}
|
|
269
|
-
const
|
|
339
|
+
const stored = ctx.storedCredentials;
|
|
340
|
+
const identity = stored?.user?.email ?? "unknown user";
|
|
341
|
+
const activeOrg = stored?.orgs.find((o) => o.organizationId === stored?.activeOrgId);
|
|
270
342
|
ui.br();
|
|
271
343
|
ui.warn("Already authenticated");
|
|
272
|
-
ui.hint(`Signed in as ${identity}`);
|
|
344
|
+
ui.hint(`Signed in as ${identity}${activeOrg ? ` (${activeOrg.organizationName})` : ""}`);
|
|
345
|
+
if (stored?.webUrl || stored?.serverUrl) ui.hint(`Saved hosts: web ${stored.webUrl ?? "(none)"} | api ${stored.serverUrl ?? "(none)"}`);
|
|
273
346
|
ui.hint("Continuing will add or replace credentials for the selected organization.");
|
|
274
347
|
ui.br();
|
|
275
348
|
}
|
|
276
349
|
async function handleAuth(options, ctx, overrides) {
|
|
277
350
|
await warnIfAlreadyAuthenticated(ctx);
|
|
278
|
-
const
|
|
279
|
-
|
|
351
|
+
const hosts = resolveAuthHosts(options, ctx);
|
|
352
|
+
printResolvedHosts(hosts);
|
|
353
|
+
if (shouldConfirmLoopback(hosts)) await confirmLoopbackHostsOrExit(hosts);
|
|
354
|
+
const { webUrl, serverUrl } = hosts;
|
|
280
355
|
const timeoutMs = options.timeout * 1e3;
|
|
281
356
|
const state = createDeviceAuthState();
|
|
282
357
|
logger.info("Starting auth flow", {
|
package/dist/keystroke.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import * as path$1 from "node:path";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { log } from "@clack/prompts";
|
|
11
11
|
//#region package.json
|
|
12
|
-
var version = "0.0.
|
|
12
|
+
var version = "0.0.3";
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/command-registry.ts
|
|
15
15
|
const ROOT_OPTIONS_WITH_VALUES = new Set([
|
|
@@ -33,7 +33,7 @@ const lazyCommandDefinitions = [
|
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
name: "auth",
|
|
36
|
-
loadCommand: async () => (await import("./auth-
|
|
36
|
+
loadCommand: async () => (await import("./auth-CY0Gg9sN.mjs")).createAuthCommand()
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
name: "connect",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keystrokehq/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Command-line interface for creating, managing, and deploying Keystroke automations.",
|
|
6
6
|
"type": "module",
|
|
@@ -39,17 +39,17 @@
|
|
|
39
39
|
"typescript": "^5.9.3",
|
|
40
40
|
"vitest": "^4.1.5",
|
|
41
41
|
"@keystroke/env-utils": "0.0.0",
|
|
42
|
-
"@keystroke/skills": "0.0.1",
|
|
43
|
-
"@keystroke/project-config": "0.0.0",
|
|
44
42
|
"@keystroke/local-memory": "0.0.0",
|
|
43
|
+
"@keystroke/shared-types": "0.0.0",
|
|
45
44
|
"@keystroke/typescript-config": "0.0.0",
|
|
46
|
-
"@keystroke/utils": "0.0.0",
|
|
45
|
+
"@keystroke/test-utils": "0.0.0",
|
|
47
46
|
"@keystroke/workflow-builder": "0.0.1",
|
|
47
|
+
"@keystroke/skills": "0.0.1",
|
|
48
|
+
"@keystroke/utils": "0.0.0",
|
|
48
49
|
"@keystrokehq/core": "0.0.2",
|
|
49
|
-
"@keystroke/shared-types": "0.0.0",
|
|
50
50
|
"@keystroke/workflow-deploy": "0.0.0",
|
|
51
51
|
"@keystroke/workflow-sdk": "0.0.0",
|
|
52
|
-
"@keystroke/
|
|
52
|
+
"@keystroke/project-config": "0.0.0"
|
|
53
53
|
},
|
|
54
54
|
"keywords": [
|
|
55
55
|
"automation",
|