@keystrokehq/cli 0.0.16 → 0.0.17

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.
@@ -45,7 +45,7 @@ function createInitCommand() {
45
45
  description: "Initialize a Keystroke project (creates keystroke.config.ts)",
46
46
  schema: InitOptionsSchema,
47
47
  optionsConfig: INIT_OPTIONS_CONFIG,
48
- loadHandler: async () => (await import("./init.handler-CdytFiFt.mjs")).handleInit
48
+ loadHandler: async () => (await import("./init.handler-BZSoM76V.mjs")).handleInit
49
49
  });
50
50
  }
51
51
  //#endregion
@@ -210,6 +210,7 @@ function createPackageJsonContent(projectName, options) {
210
210
  },
211
211
  devDependencies: {
212
212
  "@biomejs/biome": "2.4.13",
213
+ "@keystrokehq/testing": options.workflowCoreVersionRange,
213
214
  vitest: "^4.0.18",
214
215
  typescript: "^5.9.3"
215
216
  }
@@ -247,7 +248,7 @@ function createTsconfigContent() {
247
248
  */
248
249
  function createVitestConfigContent() {
249
250
  return `import { defineConfig } from 'vitest/config';
250
- import { keystrokeTestPlugin } from '@keystrokehq/testing/vitest';
251
+ import { keystrokeTestPlugin } from '@keystrokehq/testing';
251
252
 
252
253
  export default defineConfig({
253
254
  plugins: [keystrokeTestPlugin()],
@@ -321,6 +322,7 @@ async function ensureScaffoldPackageJson(targetDir, projectName, workflowCoreRan
321
322
  pkg.dependencies["@keystrokehq/core"] = workflowCoreRange;
322
323
  pkg.dependencies.zod = pkg.dependencies.zod ?? "^4.3.6";
323
324
  pkg.devDependencies = pkg.devDependencies ?? {};
325
+ pkg.devDependencies["@keystrokehq/testing"] = workflowCoreRange;
324
326
  pkg.devDependencies.vitest = pkg.devDependencies.vitest ?? "^4.0.18";
325
327
  pkg.devDependencies.typescript = pkg.devDependencies.typescript ?? "^5.9.3";
326
328
  if (!pkg.name) pkg.name = projectName;
@@ -8,7 +8,8 @@ import { z } from "zod";
8
8
  const IntegrationsListOptionsSchema = JsonOptionSchema.extend({
9
9
  kind: z.enum(ConnectionKindValues).optional(),
10
10
  search: z.string().optional(),
11
- connected: z.boolean().optional()
11
+ connected: z.boolean().optional(),
12
+ details: z.boolean().optional()
12
13
  });
13
14
  const INTEGRATIONS_LIST_OPTIONS_CONFIG = {
14
15
  ...JSON_OPTION_CONFIG,
@@ -23,6 +24,10 @@ const INTEGRATIONS_LIST_OPTIONS_CONFIG = {
23
24
  connected: {
24
25
  flag: "--connected",
25
26
  description: "Only show integrations with at least one configured connection in the current org"
27
+ },
28
+ details: {
29
+ flag: "--details",
30
+ description: "Show connection names, IDs, scope, status, and default selection"
26
31
  }
27
32
  };
28
33
  const IntegrationsRegisterOptionsSchema = JsonOptionSchema.extend({
@@ -47,13 +52,13 @@ function createIntegrationsCommand() {
47
52
  description: "List Keystroke integrations available to your organization",
48
53
  schema: IntegrationsListOptionsSchema,
49
54
  optionsConfig: INTEGRATIONS_LIST_OPTIONS_CONFIG,
50
- loadHandler: async () => (await import("./list.handler-BjutlIkE.mjs")).handleIntegrationsList,
55
+ loadHandler: async () => (await import("./list.handler-DYdNWjgk.mjs")).handleIntegrationsList,
51
56
  subcommands: [createTypedCommand({
52
57
  name: "list",
53
58
  description: "List Keystroke integrations available to your organization",
54
59
  schema: IntegrationsListOptionsSchema,
55
60
  optionsConfig: INTEGRATIONS_LIST_OPTIONS_CONFIG,
56
- loadHandler: async () => (await import("./list.handler-BjutlIkE.mjs")).handleIntegrationsList
61
+ loadHandler: async () => (await import("./list.handler-DYdNWjgk.mjs")).handleIntegrationsList
57
62
  }), createTypedCommand({
58
63
  name: "register",
59
64
  description: "Register a workspace provider app for future workspace-authored OAuth support",
@@ -13,7 +13,7 @@ import { config } from "dotenv";
13
13
  import { z } from "zod";
14
14
  import { log } from "@clack/prompts";
15
15
  //#region package.json
16
- var version = "0.0.16";
16
+ var version = "0.0.17";
17
17
  //#endregion
18
18
  //#region src/command-registry.ts
19
19
  const ROOT_OPTIONS_WITH_VALUES$1 = new Set([
@@ -58,11 +58,11 @@ const lazyCommandDefinitions = [
58
58
  },
59
59
  {
60
60
  name: "init",
61
- loadCommand: async () => (await import("./init-PTwX63_P.mjs")).createInitCommand()
61
+ loadCommand: async () => (await import("./init-CWFJdKNs.mjs")).createInitCommand()
62
62
  },
63
63
  {
64
64
  name: "integrations",
65
- loadCommand: async () => (await import("./integrations-B0Gv-L0s.mjs")).createIntegrationsCommand()
65
+ loadCommand: async () => (await import("./integrations-DKtl_aES.mjs")).createIntegrationsCommand()
66
66
  },
67
67
  {
68
68
  name: "invites",
@@ -94,7 +94,7 @@ const lazyCommandDefinitions = [
94
94
  },
95
95
  {
96
96
  name: "test",
97
- loadCommand: async () => (await import("./test-BISghlKg.mjs")).createTestCommand()
97
+ loadCommand: async () => (await import("./test-CKBpp1gg.mjs")).createTestCommand()
98
98
  },
99
99
  {
100
100
  name: "upgrade",
@@ -10,19 +10,64 @@ function summarizeConnections(entry, connections) {
10
10
  const matches = connections.filter((c) => c.integrationPublicId === entry.publicId);
11
11
  if (matches.length === 0) return {
12
12
  total: 0,
13
- defaultStatus: null
13
+ defaultStatus: null,
14
+ statusCounts: /* @__PURE__ */ new Map()
14
15
  };
15
16
  const preferred = matches.find((c) => c.isDefault) ?? matches[0];
17
+ const statusCounts = /* @__PURE__ */ new Map();
18
+ for (const connection of matches) statusCounts.set(connection.connectionStatus, (statusCounts.get(connection.connectionStatus) ?? 0) + 1);
16
19
  return {
17
20
  total: matches.length,
18
- defaultStatus: preferred?.connectionStatus ?? null
21
+ defaultStatus: preferred?.connectionStatus ?? null,
22
+ statusCounts
19
23
  };
20
24
  }
21
- function formatStatusCell(summary) {
25
+ function formatConnectionStatus(status) {
26
+ return status === "connected" ? style("connected", ANSI.green) : status === "broken" ? style("broken", ANSI.red) : status === "needs_reconnect" ? style("needs reconnect", ANSI.yellow) : status === "disconnected" ? style("disconnected", ANSI.dim) : status === "pending" ? style("pending", ANSI.dim) : status ?? style("(unknown)", ANSI.dim);
27
+ }
28
+ function pluralizeConnection(count) {
29
+ return `${count} connection${count === 1 ? "" : "s"}`;
30
+ }
31
+ function formatStatusCount(status, count) {
32
+ return `${count} ${formatConnectionStatus(status)}`;
33
+ }
34
+ function formatConnectionsCell(summary) {
22
35
  if (summary.total === 0) return style("—", ANSI.dim);
23
- const { defaultStatus } = summary;
24
- const statusLabel = defaultStatus === "connected" ? style("connected", ANSI.green) : defaultStatus === "broken" ? style("broken", ANSI.red) : defaultStatus === "needs_reconnect" ? style("needs reconnect", ANSI.yellow) : defaultStatus === "disconnected" ? style("disconnected", ANSI.dim) : defaultStatus === "pending" ? style("pending", ANSI.dim) : defaultStatus ?? style("(unknown)", ANSI.dim);
25
- return summary.total > 1 ? `${statusLabel} (+${summary.total - 1} more)` : statusLabel;
36
+ const statusParts = [...summary.statusCounts.entries()].map(([status, count]) => formatStatusCount(status, count));
37
+ if (statusParts.length === 1) return statusParts[0] ?? style("(unknown)", ANSI.dim);
38
+ return `${pluralizeConnection(summary.total)} ${style("·", ANSI.dim)} ${statusParts.join(", ")} ${style("·", ANSI.dim)} default ${formatConnectionStatus(summary.defaultStatus)}`;
39
+ }
40
+ function dimLabel(label, width = 18) {
41
+ return style(label.padEnd(width), ANSI.dim);
42
+ }
43
+ function formatConnectionLine(connection) {
44
+ return [
45
+ connection.name,
46
+ connection.credentialConnectionId,
47
+ formatConnectionStatus(connection.connectionStatus)
48
+ ].join(` ${style("·", ANSI.dim)} `);
49
+ }
50
+ function renderConnectionDetails(entries, connections) {
51
+ const connectedEntries = entries.map((entry) => ({
52
+ entry,
53
+ connections: connections.filter((c) => c.integrationPublicId === entry.publicId)
54
+ })).filter(({ connections: entryConnections }) => entryConnections.length > 0);
55
+ if (connectedEntries.length === 0) return;
56
+ ui.br();
57
+ ui.text(style("Connection details", ANSI.bold));
58
+ for (const { entry, connections: entryConnections } of connectedEntries) {
59
+ const defaultConnection = entryConnections.find((c) => c.isDefault) ?? entryConnections[0];
60
+ const otherConnections = entryConnections.filter((c) => c.id !== defaultConnection?.id);
61
+ ui.br();
62
+ ui.text(`${style(entry.publicId, ANSI.bold)} ${style("·", ANSI.dim)} ${entry.name}`);
63
+ if (defaultConnection) {
64
+ ui.text(` ${dimLabel("Default connection")}${formatConnectionLine(defaultConnection)}`);
65
+ ui.text(` ${dimLabel("Scope")}${defaultConnection.scope}`);
66
+ ui.text(` ${dimLabel("Credential set")}${defaultConnection.credentialSetId}`);
67
+ if (defaultConnection.expiresAt) ui.text(` ${dimLabel("Expires")}${defaultConnection.expiresAt}`);
68
+ }
69
+ for (const connection of otherConnections) ui.text(` ${dimLabel("Other connection")}${formatConnectionLine(connection)}`);
70
+ }
26
71
  }
27
72
  /**
28
73
  * Optionally fetches the caller's connections for join. Tolerates missing
@@ -79,7 +124,7 @@ async function handleIntegrationsList(options, ctx) {
79
124
  "ID",
80
125
  "Kind",
81
126
  "Name",
82
- "Status",
127
+ "Connections",
83
128
  "Aliases"
84
129
  ],
85
130
  style: { head: [] }
@@ -91,13 +136,14 @@ async function handleIntegrationsList(options, ctx) {
91
136
  style(entry.publicId, `${ANSI.bold}${ANSI.cyan}`),
92
137
  entry.connectionKind,
93
138
  entry.name,
94
- formatStatusCell(summary),
139
+ formatConnectionsCell(summary),
95
140
  otherAliases.length > 0 ? otherAliases.join(", ") : style("—", ANSI.dim)
96
141
  ]);
97
142
  }
98
143
  ui.text(table.toString());
144
+ if (options.details) renderConnectionDetails(entries, connections);
99
145
  const connectedCount = entries.reduce((acc, entry) => summarizeConnections(entry, connections).total > 0 ? acc + 1 : acc, 0);
100
- ui.hint(`\n${entries.length} integration${entries.length === 1 ? "" : "s"}` + (connections.length > 0 ? ` · ${connectedCount} with active connections` : "") + (options.kind || options.search || options.connected ? " (filtered)" : ""));
146
+ ui.hint(`\n${entries.length} integration${entries.length === 1 ? "" : "s"}` + (connections.length > 0 ? ` · ${connectedCount} integration${connectedCount === 1 ? "" : "s"} connected` : "") + (options.kind || options.search || options.connected ? " (filtered)" : ""));
101
147
  if (connections.length === 0 && !ctx.organizationId) ui.hint("Sign in and select an organization to see per-integration connection status.");
102
148
  }
103
149
  //#endregion
@@ -63,7 +63,7 @@ function createTestCommand() {
63
63
  description: "Agent tool name from the built manifest",
64
64
  key: "toolName"
65
65
  },
66
- loadHandler: async () => (await import("./tool.handler-Bu130Vcz.mjs")).handleTestTool
66
+ loadHandler: async () => (await import("./tool.handler--IzRGelu.mjs")).handleTestTool
67
67
  })]
68
68
  });
69
69
  cmd.enablePositionalOptions();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keystrokehq/cli",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "private": false,
5
5
  "description": "Command-line interface for creating, managing, and deploying Keystroke automations.",
6
6
  "type": "module",
@@ -42,16 +42,16 @@
42
42
  "@keystroke/env-utils": "0.0.0",
43
43
  "@keystroke/local-memory": "0.0.0",
44
44
  "@keystrokehq/config": "0.0.2",
45
+ "@keystroke/test-utils": "0.0.2",
45
46
  "@keystroke/typescript-config": "0.0.0",
46
47
  "@keystroke/shared-types": "0.0.3",
47
48
  "@keystroke/utils": "0.0.0",
48
49
  "@keystroke/workflow-builder": "0.0.5",
49
50
  "@keystrokehq/core": "0.0.6",
50
- "@keystrokehq/testing": "0.0.2",
51
- "@keystroke/workflow-deploy": "0.0.4",
52
51
  "@keystrokehq/workflow-build-contracts": "0.0.2",
52
+ "@keystroke/workflow-deploy": "0.0.4",
53
53
  "@keystroke/workflow-sdk": "0.0.2",
54
- "@keystroke/test-utils": "0.0.2"
54
+ "@keystrokehq/testing": "0.2.0"
55
55
  },
56
56
  "keywords": [
57
57
  "automation",
@@ -14,6 +14,87 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
14
14
  import { tmpdir } from "node:os";
15
15
  import path from "node:path";
16
16
  import { pathToFileURL } from "node:url";
17
+ //#region ../../packages/testing/src/shared/create-mock-hook.ts
18
+ /** Creates a Hook that resolves immediately when awaited. */
19
+ function createMockHook() {
20
+ const resolved = Promise.resolve(void 0);
21
+ return {
22
+ then: (onfulfilled, onrejected) => resolved.then(onfulfilled, onrejected),
23
+ token: Promise.resolve("mock-token"),
24
+ resumeUrl: Promise.resolve("http://mock/resume"),
25
+ cancelUrl: Promise.resolve("http://mock/cancel"),
26
+ approvalPageUrl: Promise.resolve("http://mock/approve")
27
+ };
28
+ }
29
+ //#endregion
30
+ //#region ../../packages/testing/src/test-runtime/helpers/create-test-workflow-context.ts
31
+ function createTestWorkflowContext(options = {}) {
32
+ return {
33
+ createHook: options.createHook ?? ((_name) => createMockHook()),
34
+ stepContext: options.stepContext,
35
+ wait: options.wait ?? (async () => {}),
36
+ hasCredentialSet: options.hasCredentialSet ?? (() => false),
37
+ workflowGlobals: options.workflowGlobals,
38
+ workflowId: options.workflowId ?? "test-workflow-run"
39
+ };
40
+ }
41
+ //#endregion
42
+ //#region ../../packages/core/dist/chunks/execution-scope-BCDb3CU7.mjs
43
+ const SCOPE_STACK_KEY = "__ks_execution_scope_stack__";
44
+ function getScopeStack() {
45
+ const g = globalThis;
46
+ if (!g[SCOPE_STACK_KEY]) g[SCOPE_STACK_KEY] = [];
47
+ return g[SCOPE_STACK_KEY];
48
+ }
49
+ function getCurrentScope() {
50
+ const stack = getScopeStack();
51
+ return stack[stack.length - 1] ?? getDefaultScope();
52
+ }
53
+ const DEFAULT_SCOPE_KEY = "__ks_execution_scope_default__";
54
+ function getDefaultScope() {
55
+ const g = globalThis;
56
+ if (!g[DEFAULT_SCOPE_KEY]) g[DEFAULT_SCOPE_KEY] = { stepContextProviders: [] };
57
+ return g[DEFAULT_SCOPE_KEY];
58
+ }
59
+ function setExecutionScopeWorkflowRuntime(provider) {
60
+ const scope = getCurrentScope();
61
+ scope.workflowRuntimeProvider = provider;
62
+ }
63
+ function pushExecutionScopeStepContext(provider) {
64
+ const scope = getCurrentScope();
65
+ scope.stepContextProviders = [...scope.stepContextProviders, provider];
66
+ }
67
+ function clearExecutionScopeStepContexts() {
68
+ const scope = getCurrentScope();
69
+ scope.stepContextProviders = [];
70
+ }
71
+ function setExecutionScopeStepCredentialResolver(resolver) {
72
+ const scope = getCurrentScope();
73
+ scope.stepCredentialResolver = resolver;
74
+ }
75
+ //#endregion
76
+ //#region ../../packages/core/dist/chunks/credential-resolver-registry-BRH30wCz.mjs
77
+ function registerOperationContext(registry) {
78
+ pushExecutionScopeStepContext(registry.getOperationContext);
79
+ }
80
+ function clearOperationContext() {
81
+ clearExecutionScopeStepContexts();
82
+ }
83
+ function registerOperationCredentialResolver(resolver) {
84
+ setExecutionScopeStepCredentialResolver(resolver.resolveOperationCredentials);
85
+ }
86
+ function clearOperationCredentialResolver() {
87
+ setExecutionScopeStepCredentialResolver(void 0);
88
+ }
89
+ //#endregion
90
+ //#region ../../packages/core/dist/chunks/context-registry-DqNWcTgK.mjs
91
+ function registerRuntime(registry) {
92
+ setExecutionScopeWorkflowRuntime(registry.getRuntime);
93
+ }
94
+ function clearRuntime() {
95
+ setExecutionScopeWorkflowRuntime(void 0);
96
+ }
97
+ //#endregion
17
98
  //#region ../../packages/testing/src/test-runtime/credentials/env-resolver.ts
18
99
  /**
19
100
  * Computes the env var name for a credential key.
@@ -169,87 +250,6 @@ async function createTestCredentialResolver(options, _runtimeOptions = {}) {
169
250
  //#region ../../packages/testing/src/test-runtime/credentials/options.ts
170
251
  const DEFAULT_TEST_CREDENTIAL_MODE = "auto";
171
252
  //#endregion
172
- //#region ../../packages/testing/src/shared/create-mock-hook.ts
173
- /** Creates a Hook that resolves immediately when awaited. */
174
- function createMockHook() {
175
- const resolved = Promise.resolve(void 0);
176
- return {
177
- then: (onfulfilled, onrejected) => resolved.then(onfulfilled, onrejected),
178
- token: Promise.resolve("mock-token"),
179
- resumeUrl: Promise.resolve("http://mock/resume"),
180
- cancelUrl: Promise.resolve("http://mock/cancel"),
181
- approvalPageUrl: Promise.resolve("http://mock/approve")
182
- };
183
- }
184
- //#endregion
185
- //#region ../../packages/testing/src/test-runtime/helpers/create-test-workflow-context.ts
186
- function createTestWorkflowContext(options = {}) {
187
- return {
188
- createHook: options.createHook ?? ((_name) => createMockHook()),
189
- stepContext: options.stepContext,
190
- wait: options.wait ?? (async () => {}),
191
- hasCredentialSet: options.hasCredentialSet ?? (() => false),
192
- workflowGlobals: options.workflowGlobals,
193
- workflowId: options.workflowId ?? "test-workflow-run"
194
- };
195
- }
196
- //#endregion
197
- //#region ../../packages/core/dist/chunks/execution-scope-BCDb3CU7.mjs
198
- const SCOPE_STACK_KEY = "__ks_execution_scope_stack__";
199
- function getScopeStack() {
200
- const g = globalThis;
201
- if (!g[SCOPE_STACK_KEY]) g[SCOPE_STACK_KEY] = [];
202
- return g[SCOPE_STACK_KEY];
203
- }
204
- function getCurrentScope() {
205
- const stack = getScopeStack();
206
- return stack[stack.length - 1] ?? getDefaultScope();
207
- }
208
- const DEFAULT_SCOPE_KEY = "__ks_execution_scope_default__";
209
- function getDefaultScope() {
210
- const g = globalThis;
211
- if (!g[DEFAULT_SCOPE_KEY]) g[DEFAULT_SCOPE_KEY] = { stepContextProviders: [] };
212
- return g[DEFAULT_SCOPE_KEY];
213
- }
214
- function setExecutionScopeWorkflowRuntime(provider) {
215
- const scope = getCurrentScope();
216
- scope.workflowRuntimeProvider = provider;
217
- }
218
- function pushExecutionScopeStepContext(provider) {
219
- const scope = getCurrentScope();
220
- scope.stepContextProviders = [...scope.stepContextProviders, provider];
221
- }
222
- function clearExecutionScopeStepContexts() {
223
- const scope = getCurrentScope();
224
- scope.stepContextProviders = [];
225
- }
226
- function setExecutionScopeStepCredentialResolver(resolver) {
227
- const scope = getCurrentScope();
228
- scope.stepCredentialResolver = resolver;
229
- }
230
- //#endregion
231
- //#region ../../packages/core/dist/chunks/credential-resolver-registry-BRH30wCz.mjs
232
- function registerOperationContext(registry) {
233
- pushExecutionScopeStepContext(registry.getOperationContext);
234
- }
235
- function clearOperationContext() {
236
- clearExecutionScopeStepContexts();
237
- }
238
- function registerOperationCredentialResolver(resolver) {
239
- setExecutionScopeStepCredentialResolver(resolver.resolveOperationCredentials);
240
- }
241
- function clearOperationCredentialResolver() {
242
- setExecutionScopeStepCredentialResolver(void 0);
243
- }
244
- //#endregion
245
- //#region ../../packages/core/dist/chunks/context-registry-DqNWcTgK.mjs
246
- function registerRuntime(registry) {
247
- setExecutionScopeWorkflowRuntime(registry.getRuntime);
248
- }
249
- function clearRuntime() {
250
- setExecutionScopeWorkflowRuntime(void 0);
251
- }
252
- //#endregion
253
253
  //#region ../../packages/testing/src/test-runtime/runtime/create-keystroke-test-runtime.ts
254
254
  async function createKeystrokeTestRuntime(options = {}) {
255
255
  clearOperationCredentialResolver();