@keystrokehq/cli 0.0.21 → 0.0.22
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/{api-J9UL8pqZ.mjs → api-O5tdGdzc.mjs} +29 -3
- package/dist/{connect-DzVxjeYr.mjs → connect-_l7Lkfxi.mjs} +25 -1
- package/dist/{connect.handler-DFQdxkWZ.mjs → connect.handler-tqk-DOv8.mjs} +37 -2
- package/dist/{integrations-cwRfplNG.mjs → integrations-Cdq7iSLj.mjs} +1 -1
- package/dist/keystroke.mjs +3 -3
- package/package.json +9 -9
|
@@ -29,7 +29,33 @@ const InitiateConnectionRequestSchema = z.object({
|
|
|
29
29
|
* tenant-specific authorize URL.
|
|
30
30
|
*/
|
|
31
31
|
input: z.record(z.string(), z.unknown()).optional(),
|
|
32
|
-
requestedScopes: z.array(z.string()).optional()
|
|
32
|
+
requestedScopes: z.array(z.string()).optional(),
|
|
33
|
+
/**
|
|
34
|
+
* Scope target for the resulting credential set. Optional — the
|
|
35
|
+
* server resolves the default at request time: an explicit value
|
|
36
|
+
* wins; absent that, the presence of `projectId` selects
|
|
37
|
+
* `project`, and otherwise the request defaults to
|
|
38
|
+
* `organization`. User scope must always be explicit.
|
|
39
|
+
*/
|
|
40
|
+
scope: CredentialScopeSchema.optional(),
|
|
41
|
+
/**
|
|
42
|
+
* Required when `scope === 'project'` (whether explicit or
|
|
43
|
+
* resolved). Must not be present for the other scopes.
|
|
44
|
+
*/
|
|
45
|
+
projectId: z.uuid().optional(),
|
|
46
|
+
/**
|
|
47
|
+
* Optional caller-supplied display name for the resulting
|
|
48
|
+
* connection. Falls back to the integration's display name on
|
|
49
|
+
* persist; collisions at the same scope target are auto-suffixed
|
|
50
|
+
* server-side.
|
|
51
|
+
*/
|
|
52
|
+
name: z.string().min(1).max(255).optional()
|
|
53
|
+
}).refine((data) => !(data.scope === "project" && !data.projectId), {
|
|
54
|
+
message: "projectId is required when scope is 'project'",
|
|
55
|
+
path: ["projectId"]
|
|
56
|
+
}).refine((data) => !(data.projectId && data.scope && data.scope !== "project"), {
|
|
57
|
+
message: "projectId must not be supplied when scope is not 'project'",
|
|
58
|
+
path: ["projectId"]
|
|
33
59
|
});
|
|
34
60
|
const InitiateConnectionResponseSchema = z.object({
|
|
35
61
|
authUrl: z.string(),
|
|
@@ -56,8 +82,8 @@ const ConnectionStatusFailedSchema = z.object({
|
|
|
56
82
|
/**
|
|
57
83
|
* Optional human-readable detail. Truncated by the server to
|
|
58
84
|
* {@link CONNECTION_FAILURE_DETAIL_MAX_CHARS} characters before being
|
|
59
|
-
* persisted in `
|
|
60
|
-
*
|
|
85
|
+
* persisted in `connection_flows.error_detail` and before being
|
|
86
|
+
* echoed in the callback redirect query.
|
|
61
87
|
*/
|
|
62
88
|
message: z.string().max(300).optional(),
|
|
63
89
|
/** ISO-8601 timestamp of when the failure was recorded. */
|
|
@@ -8,6 +8,14 @@ const ConnectOptionsSchema = JsonOptionSchema.extend({
|
|
|
8
8
|
integrationId: z.string().optional(),
|
|
9
9
|
connection: z.string().optional().describe("Credential connection path to use when an integration has multiple OAuth paths"),
|
|
10
10
|
input: z.array(z.string()).optional().describe("Connection input as key=value. Repeat for multiple fields."),
|
|
11
|
+
scope: z.enum([
|
|
12
|
+
"organization",
|
|
13
|
+
"project",
|
|
14
|
+
"user"
|
|
15
|
+
]).optional().describe("Connection scope (organization | project | user). Defaults to project when a project is detected, otherwise organization. Use 'user' for personal credentials."),
|
|
16
|
+
projectId: z.string().optional().describe("Project to scope the connection to. Auto-detected from keystroke.config.ts when run inside a project directory."),
|
|
17
|
+
path: z.string().optional().describe("Path to the project root (default: nearest keystroke.config.ts walking up from cwd)"),
|
|
18
|
+
name: z.string().optional().describe("Display name for the resulting connection. Defaults to the integration name; duplicates are auto-suffixed."),
|
|
11
19
|
timeout: z.coerce.number().int().min(30).max(600).default(300).describe("Timeout in seconds (default: 5 minutes)"),
|
|
12
20
|
noOpen: z.boolean().optional().describe("Print the authorization URL instead of opening a browser")
|
|
13
21
|
});
|
|
@@ -21,6 +29,22 @@ const CONNECT_OPTIONS_CONFIG = {
|
|
|
21
29
|
description: "Connection input as key=value. Repeat for multiple fields.",
|
|
22
30
|
collect: true
|
|
23
31
|
},
|
|
32
|
+
scope: {
|
|
33
|
+
flag: "--scope <organization|project|user>",
|
|
34
|
+
description: "Connection scope. Defaults to 'project' when a project is detected, otherwise 'organization'."
|
|
35
|
+
},
|
|
36
|
+
projectId: {
|
|
37
|
+
flag: "--project-id <uuid>",
|
|
38
|
+
description: "Project to scope the connection to. Required when --scope project is used outside a project directory."
|
|
39
|
+
},
|
|
40
|
+
path: {
|
|
41
|
+
flag: "--path <dir>",
|
|
42
|
+
description: "Path to the project root (default: walk up from cwd looking for keystroke.config.ts)"
|
|
43
|
+
},
|
|
44
|
+
name: {
|
|
45
|
+
flag: "--name <name>",
|
|
46
|
+
description: "Display name for the resulting connection."
|
|
47
|
+
},
|
|
24
48
|
timeout: {
|
|
25
49
|
flag: "--timeout <seconds>",
|
|
26
50
|
description: "Timeout in seconds (default: 300)"
|
|
@@ -37,7 +61,7 @@ function createConnectCommand() {
|
|
|
37
61
|
description: "Connect an official integration via OAuth (e.g., keystroke connect slack)",
|
|
38
62
|
schema: ConnectOptionsSchema,
|
|
39
63
|
optionsConfig: CONNECT_OPTIONS_CONFIG,
|
|
40
|
-
loadHandler: async () => (await import("./connect.handler-
|
|
64
|
+
loadHandler: async () => (await import("./connect.handler-tqk-DOv8.mjs")).handleConnect,
|
|
41
65
|
argument: {
|
|
42
66
|
name: "integrationId",
|
|
43
67
|
description: "Integration to connect (e.g., slack, linear)",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { a as ui, j as throwReportedCliExit, p as AUTH_HINT, v as isNetworkError, y as toErrorMessage } from "./keystroke.mjs";
|
|
4
|
+
import { t as assertWorkflowProjectRoot } from "./project-config-DudGRFPO.mjs";
|
|
4
5
|
import { i as writeJson } from "./output-BWcVRt-T.mjs";
|
|
6
|
+
import { n as resolveWorkflowsDir } from "./resolve-project-Cj3MFnU0.mjs";
|
|
5
7
|
import { t as openBrowser } from "./browser-Dvv5OQrt.mjs";
|
|
6
|
-
import { i as InitiateConnectionResponseSchema, n as ConnectionStatusResponseSchema, r as InitiateConnectionRequestSchema } from "./api-
|
|
8
|
+
import { i as InitiateConnectionResponseSchema, n as ConnectionStatusResponseSchema, r as InitiateConnectionRequestSchema } from "./api-O5tdGdzc.mjs";
|
|
7
9
|
import { t as getIntegrationCatalog } from "./integration-catalog-cYlTmOSb.mjs";
|
|
8
10
|
//#region src/commands/connect/connect.handler.ts
|
|
9
11
|
function formatIntegrationLabel(catalog, integrationId) {
|
|
@@ -75,6 +77,29 @@ function exitWithError(ctx, message, opts) {
|
|
|
75
77
|
if (opts?.hint) ui.hint(opts.hint);
|
|
76
78
|
throwReportedCliExit(message);
|
|
77
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve the project id for this connect attempt.
|
|
82
|
+
*
|
|
83
|
+
* Precedence:
|
|
84
|
+
* 1. Explicit `--project-id <uuid>` flag.
|
|
85
|
+
* 2. `keystroke.config.ts` discovered from `--path` or by walking up
|
|
86
|
+
* from cwd. Best-effort — silently returns `null` when nothing is
|
|
87
|
+
* found.
|
|
88
|
+
*
|
|
89
|
+
* Caller decides whether `null` is fatal: with `--scope project` it's
|
|
90
|
+
* a usage error; with the default resolution it falls back to
|
|
91
|
+
* organization scope.
|
|
92
|
+
*/
|
|
93
|
+
async function resolveConnectProjectId(options) {
|
|
94
|
+
if (options.projectId) return options.projectId;
|
|
95
|
+
const workflowsDir = await resolveWorkflowsDir(options.path);
|
|
96
|
+
if (!workflowsDir) return null;
|
|
97
|
+
try {
|
|
98
|
+
return (await assertWorkflowProjectRoot(workflowsDir)).projectId;
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
78
103
|
function parseConnectionInput(rawInputs, ctx) {
|
|
79
104
|
const input = {};
|
|
80
105
|
for (const rawInput of rawInputs) {
|
|
@@ -127,12 +152,22 @@ async function handleConnect(options, ctx) {
|
|
|
127
152
|
if (oauthConnection && ((catalogEntry?.connections.length ?? 0) > 1 || options.connection)) ui.text(`Connection: ${formatConnectionLabel(oauthConnection)} (id: ${oauthConnection.id}${formatConnectionFlags(oauthConnection)})`);
|
|
128
153
|
ui.br();
|
|
129
154
|
}
|
|
155
|
+
const resolvedProjectId = await resolveConnectProjectId(options);
|
|
156
|
+
if (options.scope === "project" && !resolvedProjectId) exitWithError(ctx, "--scope project requires a project context. Pass --project-id <uuid>, --path <dir>, or run inside a project directory.", {
|
|
157
|
+
code: "USAGE_ERROR",
|
|
158
|
+
hint: "Use `keystroke init` to create a project, or pick a different scope."
|
|
159
|
+
});
|
|
160
|
+
const scopeForRequest = options.scope;
|
|
161
|
+
const projectIdForRequest = scopeForRequest === "user" || scopeForRequest === "organization" ? void 0 : resolvedProjectId ?? void 0;
|
|
130
162
|
let authUrl;
|
|
131
163
|
let initiatedAt;
|
|
132
164
|
try {
|
|
133
165
|
const requestBody = InitiateConnectionRequestSchema.parse({
|
|
134
166
|
...oauthConnection ? { credentialConnectionId: oauthConnection.id } : {},
|
|
135
|
-
...Object.keys(connectionInput).length > 0 ? { input: connectionInput } : {}
|
|
167
|
+
...Object.keys(connectionInput).length > 0 ? { input: connectionInput } : {},
|
|
168
|
+
...scopeForRequest ? { scope: scopeForRequest } : {},
|
|
169
|
+
...projectIdForRequest ? { projectId: projectIdForRequest } : {},
|
|
170
|
+
...options.name ? { name: options.name } : {}
|
|
136
171
|
});
|
|
137
172
|
const hasRequestBody = Object.keys(requestBody).length > 0;
|
|
138
173
|
const response = await fetch(`${baseUrl}/api/v1/connections/${integrationId}/initiate`, {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { n as JsonOptionSchema, t as JSON_OPTION_CONFIG } from "./output-BWcVRt-T.mjs";
|
|
4
4
|
import { t as createTypedCommand } from "./commander-9Kro0Dl3.mjs";
|
|
5
|
-
import { t as ConnectionKindValues } from "./api-
|
|
5
|
+
import { t as ConnectionKindValues } from "./api-O5tdGdzc.mjs";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
//#region ../../packages/shared-types/src/platform-providers/index.ts
|
|
8
8
|
const platformProviderRegistry = {
|
package/dist/keystroke.mjs
CHANGED
|
@@ -521,7 +521,7 @@ const logger = {
|
|
|
521
521
|
};
|
|
522
522
|
//#endregion
|
|
523
523
|
//#region package.json
|
|
524
|
-
var version = "0.0.
|
|
524
|
+
var version = "0.0.22";
|
|
525
525
|
//#endregion
|
|
526
526
|
//#region src/command-registry.ts
|
|
527
527
|
const ROOT_OPTIONS_WITH_VALUES$1 = new Set([
|
|
@@ -549,7 +549,7 @@ const lazyCommandDefinitions = [
|
|
|
549
549
|
},
|
|
550
550
|
{
|
|
551
551
|
name: "connect",
|
|
552
|
-
loadCommand: async () => (await import("./connect-
|
|
552
|
+
loadCommand: async () => (await import("./connect-_l7Lkfxi.mjs")).createConnectCommand()
|
|
553
553
|
},
|
|
554
554
|
{
|
|
555
555
|
name: "credentials",
|
|
@@ -570,7 +570,7 @@ const lazyCommandDefinitions = [
|
|
|
570
570
|
},
|
|
571
571
|
{
|
|
572
572
|
name: "integrations",
|
|
573
|
-
loadCommand: async () => (await import("./integrations-
|
|
573
|
+
loadCommand: async () => (await import("./integrations-Cdq7iSLj.mjs")).createIntegrationsCommand()
|
|
574
574
|
},
|
|
575
575
|
{
|
|
576
576
|
name: "invites",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keystrokehq/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Command-line interface for creating, managing, and deploying Keystroke automations.",
|
|
6
6
|
"type": "module",
|
|
@@ -39,19 +39,19 @@
|
|
|
39
39
|
"tsdown": "0.21.10",
|
|
40
40
|
"typescript": "^5.9.3",
|
|
41
41
|
"vitest": "^4.1.5",
|
|
42
|
-
"@keystrokehq/config": "0.0.2",
|
|
43
|
-
"@keystroke/shared-types": "0.0.4",
|
|
44
|
-
"@keystroke/local-memory": "0.0.1",
|
|
45
42
|
"@keystroke/env-utils": "0.0.0",
|
|
43
|
+
"@keystroke/local-memory": "0.0.1",
|
|
44
|
+
"@keystrokehq/config": "0.0.2",
|
|
45
|
+
"@keystroke/shared-types": "0.0.5",
|
|
46
46
|
"@keystroke/typescript-config": "0.0.0",
|
|
47
|
-
"@keystroke/test-utils": "0.0.3",
|
|
48
47
|
"@keystroke/utils": "0.0.0",
|
|
49
|
-
"@keystroke/
|
|
50
|
-
"@
|
|
48
|
+
"@keystroke/test-utils": "0.0.3",
|
|
49
|
+
"@keystroke/workflow-builder": "0.0.7",
|
|
51
50
|
"@keystrokehq/core": "0.0.7",
|
|
52
|
-
"@keystroke/workflow-deploy": "0.0.5",
|
|
53
51
|
"@keystrokehq/workflow-build-contracts": "0.0.3",
|
|
54
|
-
"@
|
|
52
|
+
"@keystrokehq/testing": "0.2.2",
|
|
53
|
+
"@keystroke/workflow-deploy": "0.0.6",
|
|
54
|
+
"@keystroke/workflow-sdk": "0.0.4"
|
|
55
55
|
},
|
|
56
56
|
"keywords": [
|
|
57
57
|
"automation",
|