@keystrokehq/cli 0.0.1

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.
Files changed (122) hide show
  1. package/AGENTS-blurb.md +123 -0
  2. package/LICENSE +42 -0
  3. package/README.md +177 -0
  4. package/THIRD_PARTY_NOTICES.md +16 -0
  5. package/bin/keystroke.mjs +107 -0
  6. package/dist/_manifest-JSRE3H8k.mjs +385 -0
  7. package/dist/agent-bundle-package-DWV6B_5q-BtV7Xycc.mjs +2344 -0
  8. package/dist/agent-manifest-CDnbkR2f.mjs +245 -0
  9. package/dist/agents-CZJGxVqV.mjs +228 -0
  10. package/dist/api-keys-D2lgguuY.mjs +40 -0
  11. package/dist/auth-DN2VusyU.mjs +59 -0
  12. package/dist/auth.handler-CT1BQUvu.mjs +340 -0
  13. package/dist/browser-qwFrUH82.mjs +24 -0
  14. package/dist/build-agents-BmM_AsSd-BGi9wtzt.mjs +514 -0
  15. package/dist/build-metadata-BWS7uhd_-DR8gJjTX.mjs +1422 -0
  16. package/dist/build-progress-DgYKb4hB.mjs +183 -0
  17. package/dist/build-tasks-CdihpudT-D5r5HUHe.mjs +91 -0
  18. package/dist/build-workflows-CfxBnIWh-CdYPv8w2.mjs +370 -0
  19. package/dist/build.handler-4799CjWH.mjs +36 -0
  20. package/dist/chunk-CH6r78ws.mjs +37 -0
  21. package/dist/clear-cache.handler-B9tqSoSM.mjs +11 -0
  22. package/dist/clear.handler-BTIXXPTJ.mjs +42 -0
  23. package/dist/clear.handler-BydlX-zE.mjs +11 -0
  24. package/dist/commander-DfTVqQ-3.mjs +133 -0
  25. package/dist/concurrency-gXn9Rw8x-DNl2YtrS.mjs +20 -0
  26. package/dist/connect-BUXkeH0F.mjs +43 -0
  27. package/dist/connect.handler-CYel9cy6.mjs +430 -0
  28. package/dist/constants-CPpPdSNg.mjs +8 -0
  29. package/dist/context-T7HZuB97.mjs +138 -0
  30. package/dist/credential-env-map-CI8yWHVy.mjs +28 -0
  31. package/dist/credential-schema-mismatch-BKo5PjcQ.mjs +76 -0
  32. package/dist/credentials-CvmjU0lK.mjs +171 -0
  33. package/dist/credentials-OfVHOtG3.mjs +151216 -0
  34. package/dist/current-deployment-workflow-poHt27i3.mjs +94 -0
  35. package/dist/current.handler-B8zKzfPp.mjs +21 -0
  36. package/dist/delete.handler-bAu1iXVQ.mjs +17 -0
  37. package/dist/deploy-7Jjls436.mjs +26 -0
  38. package/dist/deploy-BOPIpRWm.mjs +74 -0
  39. package/dist/deploy-progress-BmGUNFKg.mjs +70 -0
  40. package/dist/deploy.handler-BAzgiNhd.mjs +370 -0
  41. package/dist/detect-env-access-CwkOYeYM-D_BCZqV6.mjs +209 -0
  42. package/dist/diff-utils-NEfcjqxt.mjs +185 -0
  43. package/dist/diff.handler-Du7SY8K4.mjs +47 -0
  44. package/dist/dist-BkJUoBiG.mjs +1116 -0
  45. package/dist/dist-CUK7yBM0.mjs +308 -0
  46. package/dist/env-91KwMKov.mjs +140 -0
  47. package/dist/env.handler-BAzBuMzQ.mjs +277 -0
  48. package/dist/error-boundary-VL-JLfIa.mjs +34 -0
  49. package/dist/file-metadata-D1vm-XY2.mjs +191 -0
  50. package/dist/get-intrinsic-zLxwtrLK.mjs +658 -0
  51. package/dist/import-module-CV84H5fZ-B_CBCmb4.mjs +1747 -0
  52. package/dist/init-DpMCotSK.mjs +45 -0
  53. package/dist/init.handler-CPRnif52.mjs +585 -0
  54. package/dist/inspect.handler-DT_cD036.mjs +146 -0
  55. package/dist/integration-catalog-Bt-L3GjF.mjs +104 -0
  56. package/dist/integrations-DlatPK4W.mjs +79 -0
  57. package/dist/keystroke.d.mts +3 -0
  58. package/dist/keystroke.mjs +707 -0
  59. package/dist/layout-CbMtQ2tm.mjs +67 -0
  60. package/dist/list-enrichment-y-cwizLr.mjs +189 -0
  61. package/dist/list.handler-BTWvCyjA.mjs +52 -0
  62. package/dist/list.handler-CWF_Dj15.mjs +24 -0
  63. package/dist/list.handler-CZ6G2x_G.mjs +75 -0
  64. package/dist/list.handler-DWaQkJaR.mjs +51 -0
  65. package/dist/list.handler-DqbFcBW7.mjs +180 -0
  66. package/dist/list.handler-lq3ZGAn4.mjs +104 -0
  67. package/dist/logs-BEg9L5l8.mjs +28 -0
  68. package/dist/logs.handler-6hoMBzqw.mjs +35 -0
  69. package/dist/logs.handler-BD_dXiL1.mjs +231 -0
  70. package/dist/metadata-layout-GUYIUo0i-_aG2zjue.mjs +5877 -0
  71. package/dist/normalize-path-CojS-CgQ-DLCOvnD1.mjs +20 -0
  72. package/dist/options-CeaTcFxP.mjs +43 -0
  73. package/dist/org-xLzBtt2_.mjs +41 -0
  74. package/dist/output-DM4b7KgY.mjs +72 -0
  75. package/dist/oxc-B3KI3rf_-n9d1hKNq.mjs +119 -0
  76. package/dist/paused.handler-BMFm9Cff.mjs +94 -0
  77. package/dist/project-config-D1qsQlO7.mjs +107 -0
  78. package/dist/projects-CHkRE9rS.mjs +1574 -0
  79. package/dist/projects-Cjb7sovS.mjs +30 -0
  80. package/dist/read-credential-keys-77a91T8M-KA0Iw0Z1.mjs +9 -0
  81. package/dist/register.handler-BPCdor1_.mjs +86 -0
  82. package/dist/requirements.handler-DPXdSks3.mjs +201 -0
  83. package/dist/resolve-project-DDJ29sCF.mjs +35 -0
  84. package/dist/rolldown-runtime-twds-ZHy-BWWzu8VG.mjs +15 -0
  85. package/dist/run-polling-CAgFRdK3.mjs +20 -0
  86. package/dist/runs-D9hNLb9A.mjs +259 -0
  87. package/dist/schedule-BXx3uXwr.mjs +1142 -0
  88. package/dist/schema-17qMfNyI.mjs +18 -0
  89. package/dist/schema-display-CgmeKigW.mjs +130 -0
  90. package/dist/schemas-CDib1RhE.mjs +125 -0
  91. package/dist/skills-sync.handler-DIy8GR16.mjs +34 -0
  92. package/dist/skills.command-CrjI2dN9.mjs +35 -0
  93. package/dist/skills.handler-Bz8bJKql.mjs +9 -0
  94. package/dist/source-analysis-Cj-ADyu--BJQcFPCG.mjs +144 -0
  95. package/dist/spinner-progress-DMVwgqO9.mjs +173 -0
  96. package/dist/src-C0X6u_Mw.mjs +1340 -0
  97. package/dist/src-eHwu-Gfw.mjs +369 -0
  98. package/dist/status.handler-BO4nwvWn.mjs +101 -0
  99. package/dist/switch.handler-D_9213Vf.mjs +51 -0
  100. package/dist/sync-BL_Mo5st.mjs +39 -0
  101. package/dist/sync-keystroke-agent-skills-Kx_H7UTd.mjs +70 -0
  102. package/dist/sync.handler-BUFPdzWz.mjs +82 -0
  103. package/dist/task-B2sZMaZu.mjs +8 -0
  104. package/dist/task-target-build-CBeCKbu2.mjs +432 -0
  105. package/dist/task-target-deploy-C5X-USeR.mjs +4 -0
  106. package/dist/task-target-deploy-CA6elFpF-BEr4gkol.mjs +271 -0
  107. package/dist/task-target-deploy-runner.d.mts +3 -0
  108. package/dist/task-target-deploy-runner.mjs +202 -0
  109. package/dist/test-BHTgR3UA.mjs +698 -0
  110. package/dist/test.handler-BcPQ8b74.mjs +13 -0
  111. package/dist/trigger-artifacts-DQPbQNqC-B4yeeFBY.mjs +239 -0
  112. package/dist/trigger-manifest-CY7brZeg.mjs +30 -0
  113. package/dist/try-deploy.handler-DqybNhXx.mjs +490 -0
  114. package/dist/upload-CkU--iDC.mjs +207 -0
  115. package/dist/upload.handler-DCtiznQp.mjs +441 -0
  116. package/dist/utils-CywxCDM7.mjs +14 -0
  117. package/dist/validate.handler-DOcTaJL0.mjs +280 -0
  118. package/dist/workflow-build-DBQaBfnn.mjs +1819 -0
  119. package/dist/workflow-bundler-BPiqVscj-X1PFFAuP.mjs +167 -0
  120. package/dist/workflows-g9z87AJJ.mjs +799 -0
  121. package/dist/writer-BG8poUm3-BbXlU2kI.mjs +426 -0
  122. package/package.json +87 -0
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { n as __exportAll } from "./chunk-CH6r78ws.mjs";
4
+ import { t as __exportAll$1 } from "./rolldown-runtime-twds-ZHy-BWWzu8VG.mjs";
5
+ import path from "node:path";
6
+ //#region ../../packages/workflow-builder/dist/detect-env-access-CwkOYeYM.mjs
7
+ var detect_env_access_CwkOYeYM_exports = /* @__PURE__ */ __exportAll({
8
+ n: () => detect_env_access_exports,
9
+ r: () => formatEnvAccessError,
10
+ t: () => createEnvAccessPlugin
11
+ });
12
+ /**
13
+ * Detects environment variable access patterns that must not appear in
14
+ * workflow/agent code shipped to the server (user env is not available at runtime).
15
+ */
16
+ var detect_env_access_exports = /* @__PURE__ */ __exportAll$1({
17
+ createEnvAccessPlugin: () => createEnvAccessPlugin,
18
+ detectEnvAccess: () => detectEnvAccess,
19
+ formatEnvAccessError: () => formatEnvAccessError,
20
+ maskQuotedStrings: () => maskQuotedStrings,
21
+ shouldScanModuleId: () => shouldScanModuleId,
22
+ stripTrailingLineComment: () => stripTrailingLineComment
23
+ });
24
+ const PATTERNS = [
25
+ {
26
+ name: "process.env",
27
+ regex: /\bprocess\.env\b/
28
+ },
29
+ {
30
+ name: "Bun.env",
31
+ regex: /\bBun\.env\b/
32
+ },
33
+ {
34
+ name: "Deno.env",
35
+ regex: /\bDeno\.env\b/
36
+ },
37
+ {
38
+ name: "import.meta.env",
39
+ regex: /\bimport\.meta\.env\b/
40
+ }
41
+ ];
42
+ /**
43
+ * Returns true when the module id is user workflow project source (not node_modules,
44
+ * not virtual modules, and under the Keystroke project root — excludes other monorepo packages
45
+ * that may be bundled into sandbox agents).
46
+ */
47
+ function shouldScanModuleId(moduleId, projectRoot) {
48
+ if (moduleId.includes("\0")) return false;
49
+ if (moduleId.includes("node_modules")) return false;
50
+ const root = path.resolve(projectRoot);
51
+ const id = path.resolve(moduleId);
52
+ if (id === root) return true;
53
+ const prefix = root.endsWith(path.sep) ? root : `${root}${path.sep}`;
54
+ return id.startsWith(prefix);
55
+ }
56
+ /**
57
+ * Strips `// ...` line comments that are not inside single-quoted, double-quoted,
58
+ * or template literal strings (simple state machine; sufficient for workflow TS).
59
+ */
60
+ function stripTrailingLineComment(line) {
61
+ let i = 0;
62
+ let inSingle = false;
63
+ let inDouble = false;
64
+ let inTemplate = false;
65
+ while (i < line.length) {
66
+ const c = line[i];
67
+ const next = line[i + 1];
68
+ if (!inSingle && !inDouble && !inTemplate) {
69
+ if (c === "/" && next === "/") return line.slice(0, i);
70
+ if (c === "'") {
71
+ inSingle = true;
72
+ i += 1;
73
+ continue;
74
+ }
75
+ if (c === "\"") {
76
+ inDouble = true;
77
+ i += 1;
78
+ continue;
79
+ }
80
+ if (c === "`") {
81
+ inTemplate = true;
82
+ i += 1;
83
+ continue;
84
+ }
85
+ i += 1;
86
+ continue;
87
+ }
88
+ if (inSingle) {
89
+ if (c === "\\") {
90
+ i += 2;
91
+ continue;
92
+ }
93
+ if (c === "'") inSingle = false;
94
+ i += 1;
95
+ continue;
96
+ }
97
+ if (inDouble) {
98
+ if (c === "\\") {
99
+ i += 2;
100
+ continue;
101
+ }
102
+ if (c === "\"") inDouble = false;
103
+ i += 1;
104
+ continue;
105
+ }
106
+ if (inTemplate) {
107
+ if (c === "\\") {
108
+ i += 2;
109
+ continue;
110
+ }
111
+ if (c === "`") inTemplate = false;
112
+ i += 1;
113
+ continue;
114
+ }
115
+ i += 1;
116
+ }
117
+ return line;
118
+ }
119
+ /**
120
+ * Replaces characters inside single- and double-quoted strings with spaces so regex
121
+ * does not match `process.env` etc. when it appears only as a string literal.
122
+ */
123
+ function maskQuotedStrings(line) {
124
+ let result = "";
125
+ let i = 0;
126
+ while (i < line.length) {
127
+ const c = line[i];
128
+ if (c === "'" || c === "\"") {
129
+ const q = c;
130
+ result += q;
131
+ i += 1;
132
+ while (i < line.length) {
133
+ if (line[i] === "\\") {
134
+ result += " ";
135
+ i += 1;
136
+ if (i < line.length) {
137
+ result += " ";
138
+ i += 1;
139
+ }
140
+ continue;
141
+ }
142
+ if (line[i] === q) {
143
+ result += q;
144
+ i += 1;
145
+ break;
146
+ }
147
+ result += " ";
148
+ i += 1;
149
+ }
150
+ continue;
151
+ }
152
+ result += c;
153
+ i += 1;
154
+ }
155
+ return result;
156
+ }
157
+ /**
158
+ * Scans source for disallowed env access patterns. Returns violations with 1-based line numbers.
159
+ */
160
+ function detectEnvAccess(code, filePath) {
161
+ const violations = [];
162
+ const lines = code.split(/\r?\n/);
163
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
164
+ const rawLine = lines[lineIndex];
165
+ if (rawLine === void 0) continue;
166
+ const withoutComment = stripTrailingLineComment(rawLine);
167
+ if (withoutComment.trimStart().startsWith("//")) continue;
168
+ const scanLine = maskQuotedStrings(withoutComment);
169
+ for (const { name, regex } of PATTERNS) if (regex.test(scanLine)) {
170
+ violations.push({
171
+ filePath,
172
+ line: lineIndex + 1,
173
+ pattern: name,
174
+ sourceText: rawLine.trimEnd()
175
+ });
176
+ break;
177
+ }
178
+ }
179
+ return violations;
180
+ }
181
+ function compareViolations(a, b) {
182
+ const pathCompare = a.filePath.localeCompare(b.filePath);
183
+ if (pathCompare !== 0) return pathCompare;
184
+ return a.line - b.line;
185
+ }
186
+ /** Rolldown/Rollup plugin that collects env-access violations from local source modules. */
187
+ function createEnvAccessPlugin(violations, projectRoot) {
188
+ return {
189
+ name: "workflow-no-env-access",
190
+ transform(code, id) {
191
+ if (!shouldScanModuleId(id, projectRoot)) return null;
192
+ violations.push(...detectEnvAccess(code, id));
193
+ return null;
194
+ }
195
+ };
196
+ }
197
+ /**
198
+ * Formats aggregated violations for the bundler to throw.
199
+ */
200
+ function formatEnvAccessError(violations) {
201
+ const sorted = [...violations].sort(compareViolations);
202
+ const lines = ["Environment variable access is not allowed in workflow code (code runs on the server, not locally).", ""];
203
+ for (const v of sorted) lines.push(` ${v.filePath}:${v.line} ${v.pattern}`);
204
+ lines.push("");
205
+ lines.push("Use credential sets to securely provide secrets to your workflows.");
206
+ return lines.join("\n");
207
+ }
208
+ //#endregion
209
+ export { detect_env_access_CwkOYeYM_exports as n, formatEnvAccessError as r, createEnvAccessPlugin as t };
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { o as ANSI, s as style, t as ui } from "./keystroke.mjs";
4
+ //#region src/commands/workflows/diff/diff-utils.ts
5
+ function computeWorkflowDiff(local, deployed, meta) {
6
+ if (!deployed) return {
7
+ workflowName: meta.workflowName,
8
+ hasChanges: true,
9
+ notYetDeployed: true,
10
+ metadata: {
11
+ name: {
12
+ status: "added",
13
+ local: local.name,
14
+ deployed: void 0
15
+ },
16
+ description: {
17
+ status: "added",
18
+ local: local.description,
19
+ deployed: void 0
20
+ }
21
+ },
22
+ inputSchema: diffSchemaProps(local.workflowSchemas?.input, null),
23
+ outputSchema: diffSchemaProps(local.workflowSchemas?.output, null),
24
+ steps: Object.values(local.steps ?? {}).filter((s) => s.callKind === "workflow-step").map((s) => ({
25
+ stepName: s.stepName,
26
+ status: "added",
27
+ localLabel: s.label,
28
+ localCallKind: s.callKind
29
+ })),
30
+ credentials: {
31
+ added: local.credentials?.required ?? [],
32
+ removed: [],
33
+ unchanged: []
34
+ },
35
+ deployedVersion: null
36
+ };
37
+ const nameDiff = {
38
+ status: local.name === deployed.name ? "unchanged" : "changed",
39
+ local: local.name,
40
+ deployed: deployed.name
41
+ };
42
+ const descDiff = {
43
+ status: local.description === deployed.description ? "unchanged" : "changed",
44
+ local: local.description,
45
+ deployed: deployed.description
46
+ };
47
+ const inputSchema = diffSchemaProps(local.workflowSchemas?.input, deployed.workflowSchemas?.input);
48
+ const outputSchema = diffSchemaProps(local.workflowSchemas?.output, deployed.workflowSchemas?.output);
49
+ const steps = diffSteps(local.steps ?? {}, deployed.steps ?? {});
50
+ const credentials = diffStringSet(local.credentials?.required ?? [], deployed.credentials?.required ?? []);
51
+ const hasChanges = nameDiff.status !== "unchanged" || descDiff.status !== "unchanged" || inputSchema.added.length > 0 || inputSchema.removed.length > 0 || inputSchema.changed.length > 0 || outputSchema.added.length > 0 || outputSchema.removed.length > 0 || outputSchema.changed.length > 0 || steps.some((s) => s.status !== "unchanged") || credentials.added.length > 0 || credentials.removed.length > 0;
52
+ return {
53
+ workflowName: meta.workflowName,
54
+ hasChanges,
55
+ notYetDeployed: false,
56
+ metadata: {
57
+ name: nameDiff,
58
+ description: descDiff
59
+ },
60
+ inputSchema,
61
+ outputSchema,
62
+ steps,
63
+ credentials,
64
+ deployedVersion: meta.deployedVersion
65
+ };
66
+ }
67
+ function diffSchemaProps(local, deployed) {
68
+ const localSchema = local;
69
+ const deployedSchema = deployed;
70
+ const localProps = Object.keys(localSchema?.properties ?? {});
71
+ const deployedPropsSet = new Set(Object.keys(deployedSchema?.properties ?? {}));
72
+ const added = [];
73
+ const removed = [];
74
+ const changed = [];
75
+ const unchanged = [];
76
+ for (const prop of localProps) if (!deployedPropsSet.has(prop)) added.push(prop);
77
+ else {
78
+ if (JSON.stringify(localSchema?.properties?.[prop]) !== JSON.stringify(deployedSchema?.properties?.[prop])) changed.push(prop);
79
+ else unchanged.push(prop);
80
+ deployedPropsSet.delete(prop);
81
+ }
82
+ for (const prop of deployedPropsSet) removed.push(prop);
83
+ return {
84
+ added,
85
+ removed,
86
+ changed,
87
+ unchanged
88
+ };
89
+ }
90
+ function diffSteps(local, deployed) {
91
+ const localByName = new Map(Object.values(local).map((s) => [s.stepName, s]));
92
+ const deployedByName = new Map(Object.values(deployed).map((s) => [s.stepName, s]));
93
+ const result = [];
94
+ for (const [name, ls] of localByName) {
95
+ const ds = deployedByName.get(name);
96
+ if (!ds) result.push({
97
+ stepName: name,
98
+ status: "added",
99
+ localLabel: ls.label,
100
+ localCallKind: ls.callKind
101
+ });
102
+ else {
103
+ const changed = ls.label !== ds.label || ls.callKind !== ds.callKind;
104
+ result.push({
105
+ stepName: name,
106
+ status: changed ? "changed" : "unchanged",
107
+ localLabel: ls.label,
108
+ deployedLabel: ds.label,
109
+ localCallKind: ls.callKind,
110
+ deployedCallKind: ds.callKind
111
+ });
112
+ deployedByName.delete(name);
113
+ }
114
+ }
115
+ for (const [name, ds] of deployedByName) result.push({
116
+ stepName: name,
117
+ status: "removed",
118
+ deployedLabel: ds.label,
119
+ deployedCallKind: ds.callKind
120
+ });
121
+ return result;
122
+ }
123
+ function diffStringSet(local, deployed) {
124
+ const deployedSet = new Set(deployed);
125
+ const localSet = new Set(local);
126
+ return {
127
+ added: local.filter((s) => !deployedSet.has(s)),
128
+ removed: deployed.filter((s) => !localSet.has(s)),
129
+ unchanged: local.filter((s) => deployedSet.has(s))
130
+ };
131
+ }
132
+ function renderDiff(diff) {
133
+ if (diff.notYetDeployed) {
134
+ ui.warn(`"${diff.workflowName}" is not part of the current deployment.`);
135
+ ui.hint("Run `keystroke deploy` to include it in the current deployment.");
136
+ ui.br();
137
+ return;
138
+ }
139
+ const versionLabel = diff.deployedVersion !== null ? ` #${diff.deployedVersion}` : "";
140
+ ui.header(`Diffing "${diff.workflowName}" against the current deployment${versionLabel}`);
141
+ ui.br();
142
+ ui.text(style("Metadata", ANSI.bold));
143
+ renderFieldDiff("name", diff.metadata.name);
144
+ renderFieldDiff("description", diff.metadata.description);
145
+ ui.br();
146
+ ui.text(style("Input Schema", ANSI.bold));
147
+ renderSchemaDiff(diff.inputSchema);
148
+ ui.br();
149
+ ui.text(style("Output Schema", ANSI.bold));
150
+ renderSchemaDiff(diff.outputSchema);
151
+ ui.br();
152
+ ui.text(style("Steps", ANSI.bold));
153
+ if (diff.steps.length === 0) ui.hint(" (no steps)");
154
+ else for (const step of diff.steps) if (step.status === "added") ui.text(style(` + ${step.stepName} (${step.localCallKind ?? ""})`, ANSI.green));
155
+ else if (step.status === "removed") ui.text(style(` - ${step.stepName} (${step.deployedCallKind ?? ""})`, ANSI.red));
156
+ else if (step.status === "changed") ui.text(style(` ~ ${step.stepName}: label "${step.deployedLabel}" -> "${step.localLabel}"`, ANSI.yellow));
157
+ else ui.text(style(` ${step.stepName}`, ANSI.dim));
158
+ ui.br();
159
+ if (diff.credentials.added.length > 0 || diff.credentials.removed.length > 0 || diff.credentials.unchanged.length > 0) {
160
+ ui.text(style("Credentials Required", ANSI.bold));
161
+ for (const c of diff.credentials.added) ui.text(style(` + ${c}`, ANSI.green));
162
+ for (const c of diff.credentials.removed) ui.text(style(` - ${c}`, ANSI.red));
163
+ for (const c of diff.credentials.unchanged) ui.text(style(` ${c}`, ANSI.dim));
164
+ ui.br();
165
+ }
166
+ if (diff.hasChanges) ui.warn("Changes detected against the current deployment. Run `keystroke deploy` to update it.");
167
+ else ui.success("No changes detected.");
168
+ }
169
+ function renderFieldDiff(label, diff) {
170
+ if (diff.status === "unchanged") ui.text(style(` ${label.padEnd(14)} unchanged "${diff.local}"`, ANSI.dim));
171
+ else if (diff.status === "changed") ui.text(style(` ${label.padEnd(14)} `, "") + style("changed ", ANSI.yellow) + `"${diff.deployed}" -> "${diff.local}"`);
172
+ else if (diff.status === "added") ui.text(`${style(` ${label.padEnd(14)} `, "") + style("added ", ANSI.green)}"${diff.local}"`);
173
+ }
174
+ function renderSchemaDiff(diff) {
175
+ if (diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0) {
176
+ ui.text(style(" unchanged", ANSI.dim));
177
+ return;
178
+ }
179
+ for (const f of diff.added) ui.text(style(` + ${f}`, ANSI.green));
180
+ for (const f of diff.removed) ui.text(style(` - ${f}`, ANSI.red));
181
+ for (const f of diff.changed) ui.text(style(` ~ ${f}`, ANSI.yellow));
182
+ for (const f of diff.unchanged) ui.text(style(` ${f}`, ANSI.dim));
183
+ }
184
+ //#endregion
185
+ export { renderDiff as n, computeWorkflowDiff as t };
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { C as CliExitError, D as throwReportedCliExit, h as toErrorMessage, t as ui } from "./keystroke.mjs";
4
+ import { d as trackProject } from "./dist-CUK7yBM0.mjs";
5
+ import { i as writeJson } from "./output-DM4b7KgY.mjs";
6
+ import { i as requireClient, t as assertProjectConfigMatchesAuthenticatedOrg } from "./context-T7HZuB97.mjs";
7
+ import { n as resolveLocalWorkflowManifest, t as lookupCurrentDeploymentWorkflow } from "./current-deployment-workflow-poHt27i3.mjs";
8
+ import { n as renderDiff, t as computeWorkflowDiff } from "./diff-utils-NEfcjqxt.mjs";
9
+ //#region src/commands/workflows/diff/diff.handler.ts
10
+ async function handleWorkflowsDiff(options, ctx) {
11
+ const resolved = await resolveLocalWorkflowManifest(options.workflow, options.path, { jsonMode: ctx.jsonMode });
12
+ trackProject(resolved.workflowsDir);
13
+ const client = requireClient(ctx);
14
+ await assertProjectConfigMatchesAuthenticatedOrg(client, { organizationId: resolved.organizationId });
15
+ let deploymentLookup;
16
+ try {
17
+ deploymentLookup = await lookupCurrentDeploymentWorkflow(client, {
18
+ projectId: resolved.projectId,
19
+ authoredWorkflowId: resolved.authoredWorkflowId
20
+ });
21
+ } catch (error) {
22
+ ui.error(`Failed to load current deployment workflow: ${toErrorMessage(error)}`);
23
+ throwReportedCliExit(`Failed to load current deployment workflow: ${toErrorMessage(error)}`, { cause: error });
24
+ }
25
+ if (deploymentLookup.status === "missingCurrentDeployment") {
26
+ ui.error("This project has no current deployment.");
27
+ ui.hint("Run `keystroke deploy` to create the current deployment, then try again.");
28
+ throwReportedCliExit("This project has no current deployment.");
29
+ }
30
+ if (deploymentLookup.status === "missingWorkflow") {
31
+ ui.error(`Workflow "${options.workflow}" is not part of the current deployment.`);
32
+ ui.hint("Run `keystroke deploy` to include it in the current deployment, then try again.");
33
+ throwReportedCliExit(`Workflow "${options.workflow}" is not part of the current deployment.`);
34
+ }
35
+ const diff = computeWorkflowDiff(resolved.manifest, deploymentLookup.workflow.manifest, {
36
+ workflowName: resolved.manifest.name,
37
+ deployedVersion: null
38
+ });
39
+ if (ctx.jsonMode) writeJson(diff);
40
+ else renderDiff(diff);
41
+ if (diff.hasChanges) throw new CliExitError("Workflow diff has changes.", {
42
+ exitCode: 1,
43
+ reported: true
44
+ });
45
+ }
46
+ //#endregion
47
+ export { handleWorkflowsDiff };