@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.
@@ -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 `credential_sets.last_callback_error_detail` and
60
- * before being echoed in the callback redirect query.
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-DFQdxkWZ.mjs")).handleConnect,
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-J9UL8pqZ.mjs";
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-J9UL8pqZ.mjs";
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 = {
@@ -521,7 +521,7 @@ const logger = {
521
521
  };
522
522
  //#endregion
523
523
  //#region package.json
524
- var version = "0.0.21";
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-DzVxjeYr.mjs")).createConnectCommand()
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-cwRfplNG.mjs")).createIntegrationsCommand()
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.21",
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/workflow-builder": "0.0.6",
50
- "@keystrokehq/testing": "0.2.2",
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
- "@keystroke/workflow-sdk": "0.0.3"
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",