@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,67 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from "node:path";
4
+ //#region ../../packages/project-config/src/layout.ts
5
+ /** Canonical name for the build output directory relative to the project root. */
6
+ const BUILD_OUTPUT_DIR_NAME = "dist";
7
+ const BUILD_DIR_NAME = "build";
8
+ const MANIFEST_FILE_NAME = "manifest.json";
9
+ const FLOW_FILE_NAME = "flow.json";
10
+ const BUNDLE_FILE_NAME = "workflow.js";
11
+ const SOURCE_MAP_FILE_NAME = "workflow.js.map";
12
+ const METADATA_ROOT_DIR_NAME = "metadata";
13
+ const METADATA_FILE_EXTENSION = ".json";
14
+ const TRIGGERS_DIR_NAME = "triggers";
15
+ /** Resolves the canonical build output directory for a project root (e.g. `<projectRoot>/dist`). */
16
+ function resolveBuildOutputDir(projectRoot) {
17
+ return path.resolve(projectRoot, BUILD_OUTPUT_DIR_NAME);
18
+ }
19
+ /** Resolves all file paths (manifest, flow graph, bundle, source map) for a workflow's build artifacts. */
20
+ function getWorkflowArtifactPaths(outputDir, workflowId) {
21
+ const workflowDir = path.join(outputDir, workflowId);
22
+ const buildDir = path.join(workflowDir, BUILD_DIR_NAME);
23
+ return {
24
+ workflowDir,
25
+ buildDir,
26
+ manifestPath: path.join(workflowDir, MANIFEST_FILE_NAME),
27
+ flowPath: path.join(workflowDir, FLOW_FILE_NAME),
28
+ bundlePath: path.join(buildDir, BUNDLE_FILE_NAME),
29
+ sourceMapPath: path.join(buildDir, SOURCE_MAP_FILE_NAME)
30
+ };
31
+ }
32
+ /** Resolves the manifest path for a workflow directory entry within a build output dir. */
33
+ function getWorkflowManifestPath(outputDir, workflowDirName) {
34
+ return path.join(outputDir, workflowDirName, MANIFEST_FILE_NAME);
35
+ }
36
+ /** Lists all output file paths (relative) for a workflow artifact. Includes source map only if present. */
37
+ function createArtifactOutputFiles(workflowId, hasSourceMap) {
38
+ const paths = getWorkflowArtifactPaths("", workflowId);
39
+ const outputFiles = [
40
+ path.relative(".", paths.manifestPath),
41
+ path.relative(".", paths.flowPath),
42
+ path.relative(".", paths.bundlePath)
43
+ ];
44
+ if (hasSourceMap) outputFiles.push(path.relative(".", paths.sourceMapPath));
45
+ return outputFiles.sort((left, right) => left.localeCompare(right));
46
+ }
47
+ /** Resolves the root directory where per-file metadata JSON is written (outputDir/metadata). */
48
+ function getMetadataRoot(outputDir) {
49
+ return path.join(outputDir, METADATA_ROOT_DIR_NAME);
50
+ }
51
+ /** Resolves the triggers subdirectory within a workflow's build directory. */
52
+ function getTriggersDir(buildDir) {
53
+ return path.join(buildDir, TRIGGERS_DIR_NAME);
54
+ }
55
+ /** Maps a source file's relative path to its metadata output path (replaces .ts/.tsx/.mts with .json). */
56
+ function createMetadataOutputFile(relativeFilePath) {
57
+ return path.join(METADATA_ROOT_DIR_NAME, replaceSourceExtension(relativeFilePath)).replaceAll("\\", "/");
58
+ }
59
+ /** Resolves the full filesystem path for a source file's metadata JSON output. */
60
+ function getFileMetadataPath(outputDir, relativeFilePath) {
61
+ return path.join(outputDir, createMetadataOutputFile(relativeFilePath));
62
+ }
63
+ function replaceSourceExtension(relativeFilePath) {
64
+ return relativeFilePath.replace(/\.(ts|tsx|mts)$/u, METADATA_FILE_EXTENSION);
65
+ }
66
+ //#endregion
67
+ export { MANIFEST_FILE_NAME as a, createArtifactOutputFiles as c, getMetadataRoot as d, getTriggersDir as f, resolveBuildOutputDir as h, FLOW_FILE_NAME as i, createMetadataOutputFile as l, getWorkflowManifestPath as m, BUILD_OUTPUT_DIR_NAME as n, METADATA_ROOT_DIR_NAME as o, getWorkflowArtifactPaths as p, BUNDLE_FILE_NAME as r, SOURCE_MAP_FILE_NAME as s, BUILD_DIR_NAME as t, getFileMetadataPath as u };
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { o as ANSI, s as style, t as ui } from "./keystroke.mjs";
4
+ import { n as resolveCredentialValuesFromEnv, t as groupCredentialRequirements } from "./credentials-OfVHOtG3.mjs";
5
+ import { readFile } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import { parse } from "dotenv";
8
+ //#region src/commands/credentials/list/list-display.ts
9
+ /** Visible width for layout; avoids unusably narrow or absurdly wide lines. */
10
+ function getTerminalContentWidth() {
11
+ const cols = process.stdout.columns;
12
+ if (!cols || !Number.isFinite(cols)) return 78;
13
+ return Math.max(40, Math.min(cols - 2, 100));
14
+ }
15
+ function truncateWithEllipsis(text, maxLen) {
16
+ if (maxLen < 4) return text.slice(0, maxLen);
17
+ if (text.length <= maxLen) return text;
18
+ return `${text.slice(0, maxLen - 1)}…`;
19
+ }
20
+ function formatWhere(where) {
21
+ switch (where) {
22
+ case "both": return style("Both", ANSI.green);
23
+ case "cloud": return style("Cloud", ANSI.dim);
24
+ case "local": return style("Local", ANSI.green);
25
+ case "missing": return style("Missing", ANSI.dim);
26
+ default: return "—";
27
+ }
28
+ }
29
+ function shortCredProjectLabel(projectId) {
30
+ if (projectId === "—") return "—";
31
+ return projectId.length > 14 ? `${projectId.slice(0, 8)}…` : projectId;
32
+ }
33
+ function dimLabel(label, width = 10) {
34
+ return style(label.padEnd(width), ANSI.dim);
35
+ }
36
+ /**
37
+ * Renders one credential per compact block (fits narrow terminals better than a wide table).
38
+ */
39
+ function renderCredentialListBlocks(rows, formatCreated, footer) {
40
+ const w = getTerminalContentWidth();
41
+ const indent = " ";
42
+ let first = true;
43
+ for (const r of rows) {
44
+ if (!first) ui.br();
45
+ first = false;
46
+ const parts = [style(r.credentialSetId, ANSI.bold), formatWhere(r.where)];
47
+ if (r.isDefault === true) parts.push(style("default", ANSI.green));
48
+ ui.text(parts.join(` ${style("·", ANSI.dim)} `));
49
+ if (r.name !== "—") ui.text(`${indent}${dimLabel("Name")}${r.name}`);
50
+ const proj = shortCredProjectLabel(r.projectId);
51
+ const scopeRest = proj === "—" ? "" : ` ${style("·", ANSI.dim)} Cred project ${proj}`;
52
+ ui.text(`${indent}${dimLabel("Scope")}${r.scope}${scopeRest}`);
53
+ const wfDisplay = truncateWithEllipsis(r.workflows.length === 0 ? "—" : r.workflows.join(", "), Math.max(12, w - 14));
54
+ ui.text(`${indent}${dimLabel("Workflows")}${wfDisplay}`);
55
+ if (r.createdAt) ui.text(`${indent}${dimLabel("Created")}${formatCreated(r.createdAt)}`);
56
+ }
57
+ if (footer) {
58
+ const { pagination, syntheticManifestCount } = footer;
59
+ const { total, limit, offset } = pagination;
60
+ if (total > 0) {
61
+ const start = offset + 1;
62
+ const end = Math.min(offset + limit, total);
63
+ ui.hint(`Server list: ${start}–${end} of ${total} credential set(s).`);
64
+ }
65
+ if (offset + limit < total) ui.hint(`Next page: --offset ${offset + limit} --limit ${limit}`);
66
+ if (syntheticManifestCount > 0) ui.hint(`Plus ${syntheticManifestCount} from local manifests with no matching credential set on the server.`);
67
+ }
68
+ }
69
+ //#endregion
70
+ //#region src/commands/credentials/list/list-enrichment.ts
71
+ /**
72
+ * Maps manifest credential scope strings to API/server scope.
73
+ * Default and user-provided flows resolve to user-scoped credential sets.
74
+ */
75
+ function normalizeManifestScopeToServer(scope) {
76
+ if (scope === "organization") return "organization";
77
+ if (scope === "project") return "project";
78
+ return "user";
79
+ }
80
+ /** Stable key for a manifest group (credential set + server scope). */
81
+ function manifestGroupKey(g) {
82
+ return `${g.credentialSetId}\0${normalizeManifestScopeToServer(g.scope)}`;
83
+ }
84
+ function manifestGroupMatchesServerRow(cs, g) {
85
+ if (cs.credentialSetId !== g.credentialSetId) return false;
86
+ return cs.scope === normalizeManifestScopeToServer(g.scope);
87
+ }
88
+ /**
89
+ * Merges manifest groups across all workflows (deduped by integration + scope, keys unioned).
90
+ * Order follows first-seen group order while iterating manifests.
91
+ */
92
+ function collectMergedManifestGroups(manifests) {
93
+ const map = /* @__PURE__ */ new Map();
94
+ for (const { manifest } of manifests) for (const g of groupCredentialRequirements(manifest)) {
95
+ const k = manifestGroupKey(g);
96
+ const existing = map.get(k);
97
+ if (!existing) map.set(k, {
98
+ credentialSetId: g.credentialSetId,
99
+ scope: g.scope,
100
+ keys: [...g.keys]
101
+ });
102
+ else existing.keys = [...new Set([...existing.keys, ...g.keys])].sort();
103
+ }
104
+ return [...map.values()];
105
+ }
106
+ /** Workflows that reference each manifest group (integration + scope), sorted by workflow label. */
107
+ function buildCredentialWorkflowConsumersByGroup(manifests) {
108
+ const map = /* @__PURE__ */ new Map();
109
+ for (const { manifest } of manifests) {
110
+ const label = manifest.name ?? manifest.id;
111
+ for (const g of groupCredentialRequirements(manifest)) {
112
+ const k = manifestGroupKey(g);
113
+ let set = map.get(k);
114
+ if (!set) {
115
+ set = /* @__PURE__ */ new Set();
116
+ map.set(k, set);
117
+ }
118
+ set.add(label);
119
+ }
120
+ }
121
+ return new Map([...map].map(([id, labels]) => [id, [...labels].sort()]));
122
+ }
123
+ /**
124
+ * Loads `.env` from the workflow project root (if present). Does not mutate `process.env`.
125
+ */
126
+ async function loadProjectDotenvFile(workflowsDir) {
127
+ const envPath = path.join(workflowsDir, ".env");
128
+ try {
129
+ return parse(await readFile(envPath, "utf-8"));
130
+ } catch {
131
+ return {};
132
+ }
133
+ }
134
+ function resolveKeysForServerRow(cs, manifestGroups) {
135
+ if (cs.keys && cs.keys.length > 0) return cs.keys;
136
+ const matching = manifestGroups.filter((g) => manifestGroupMatchesServerRow(cs, g));
137
+ if (matching.length === 0) return [];
138
+ const keys = /* @__PURE__ */ new Set();
139
+ for (const m of matching) for (const k of m.keys) keys.add(k);
140
+ return [...keys].sort();
141
+ }
142
+ function resolveKeysForSet(serverKeys, manifestKeys) {
143
+ if (serverKeys && serverKeys.length > 0) return serverKeys;
144
+ return manifestKeys ?? [];
145
+ }
146
+ function computeCredentialWhere(params) {
147
+ const { onServer, keys, mergedEnv, keyToEnv } = params;
148
+ if (keys.length === 0) return "unknown";
149
+ const localComplete = resolveCredentialValuesFromEnv(keys, mergedEnv, keyToEnv ?? void 0) !== null;
150
+ if (onServer) return localComplete ? "both" : "cloud";
151
+ return localComplete ? "local" : "missing";
152
+ }
153
+ function enrichServerCredentialRow(params) {
154
+ const { cs, manifestGroups, mergedEnv, keyToEnv, workflowLabels } = params;
155
+ const where = computeCredentialWhere({
156
+ onServer: true,
157
+ keys: resolveKeysForSet(cs.keys, resolveKeysForServerRow(cs, manifestGroups)),
158
+ mergedEnv,
159
+ keyToEnv: keyToEnv ?? void 0
160
+ });
161
+ return {
162
+ workflows: workflowLabels ?? [],
163
+ where
164
+ };
165
+ }
166
+ function buildSyntheticNotOnServerRows(params) {
167
+ const { serverRows, manifestGroups, workflowConsumersByGroup, mergedEnv, credentialEnvMap } = params;
168
+ const out = [];
169
+ for (const g of manifestGroups) {
170
+ if (serverRows.some((cs) => manifestGroupMatchesServerRow(cs, g))) continue;
171
+ const keys = g.keys;
172
+ const keyToEnv = credentialEnvMap?.[g.credentialSetId];
173
+ const where = computeCredentialWhere({
174
+ onServer: false,
175
+ keys,
176
+ mergedEnv,
177
+ keyToEnv
178
+ });
179
+ out.push({
180
+ credentialSetId: g.credentialSetId,
181
+ scope: normalizeManifestScopeToServer(g.scope),
182
+ workflows: workflowConsumersByGroup.get(manifestGroupKey(g)) ?? [],
183
+ where
184
+ });
185
+ }
186
+ return out;
187
+ }
188
+ //#endregion
189
+ export { loadProjectDotenvFile as a, getTerminalContentWidth as c, enrichServerCredentialRow as i, renderCredentialListBlocks as l, buildSyntheticNotOnServerRows as n, manifestGroupKey as o, collectMergedManifestGroups as r, normalizeManifestScopeToServer as s, buildCredentialWorkflowConsumersByGroup as t, truncateWithEllipsis as u };
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { D as throwReportedCliExit, o as ANSI, s as style, 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 { t as requireWorkflowsDir } from "./resolve-project-DDJ29sCF.mjs";
7
+ import { t as createSpinnerProgress } from "./spinner-progress-DMVwgqO9.mjs";
8
+ import { a as runWorkflowBuild, n as renderBuildFailure } from "./workflow-build-DBQaBfnn.mjs";
9
+ //#region src/commands/workflows/list.handler.ts
10
+ function formatWorkflow(artifact) {
11
+ const name = style(artifact.manifest.name, `${ANSI.bold}${ANSI.cyan}`);
12
+ const filePath = style(artifact.workflow.resolvedFilePath, ANSI.dim);
13
+ const lines = [` ${name}`, ` ${filePath}`];
14
+ if (artifact.manifest.description) lines.push(` ${artifact.manifest.description}`);
15
+ lines.push(` ${style(`$ keystroke workflows try-deploy "${artifact.manifest.name}"`, ANSI.dim)}`);
16
+ return lines.join("\n");
17
+ }
18
+ async function handleWorkflowsList(options, ctx) {
19
+ const workflowsDir = await requireWorkflowsDir(options.path);
20
+ await trackProject(workflowsDir);
21
+ const spinner = !ctx.jsonMode ? createSpinnerProgress("[list]") : void 0;
22
+ spinner?.start("Loading workflows...");
23
+ try {
24
+ const { result } = await runWorkflowBuild({
25
+ workflowsDir,
26
+ verbose: false
27
+ });
28
+ spinner?.stop();
29
+ if (ctx.jsonMode) {
30
+ writeJson(result.artifacts.map((artifact) => ({
31
+ name: artifact.manifest.name,
32
+ filePath: artifact.workflow.resolvedFilePath,
33
+ description: artifact.manifest.description ?? null,
34
+ exportName: artifact.workflow.exportName
35
+ })));
36
+ return;
37
+ }
38
+ if (result.artifacts.length === 0) {
39
+ ui.hint(`No workflows found in ${workflowsDir}`);
40
+ return;
41
+ }
42
+ ui.text(result.artifacts.map(formatWorkflow).join("\n\n"));
43
+ ui.br();
44
+ ui.hint(`${result.artifacts.length} workflow(s) found`);
45
+ } catch (error) {
46
+ spinner?.stop();
47
+ renderBuildFailure(error);
48
+ throwReportedCliExit("Failed to list workflows.", { cause: error });
49
+ }
50
+ }
51
+ //#endregion
52
+ export { handleWorkflowsList };
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { t as ui } from "./keystroke.mjs";
4
+ import { i as requireClient } from "./context-T7HZuB97.mjs";
5
+ //#region src/commands/org/list.handler.ts
6
+ async function handleOrgList(_options, ctx) {
7
+ const { user } = await requireClient(ctx).users.getMe();
8
+ const orgs = user.organizations ?? [];
9
+ const currentOrgId = ctx.organizationId;
10
+ const storedOrgIds = new Set(ctx.storedCredentials?.orgs.map((o) => o.organizationId) ?? []);
11
+ if (orgs.length === 0) {
12
+ ui.warn("You do not belong to any organization.");
13
+ return;
14
+ }
15
+ for (const org of orgs) {
16
+ const parts = [];
17
+ if (org.id === currentOrgId) parts.push("current");
18
+ if (storedOrgIds.has(org.id)) parts.push("key stored");
19
+ const suffix = parts.length > 0 ? ` (${parts.join(", ")})` : "";
20
+ ui.text(` ${org.name}${suffix}`);
21
+ }
22
+ }
23
+ //#endregion
24
+ export { handleOrgList };
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { o as ANSI, s as style, t as ui } from "./keystroke.mjs";
4
+ import { s as readStoredProjects } from "./dist-CUK7yBM0.mjs";
5
+ import { i as readProjectConfig } from "./project-config-D1qsQlO7.mjs";
6
+ import { i as writeJson } from "./output-DM4b7KgY.mjs";
7
+ import { access } from "node:fs/promises";
8
+ import Table from "cli-table3";
9
+ //#region src/commands/projects/list.handler.ts
10
+ async function getProjectNameFromConfig(projectPath) {
11
+ return (await readProjectConfig(projectPath))?.name;
12
+ }
13
+ function relativeTime(isoDate) {
14
+ const diff = Date.now() - new Date(isoDate).getTime();
15
+ const seconds = Math.floor(diff / 1e3);
16
+ if (seconds < 60) return `${seconds}s ago`;
17
+ const minutes = Math.floor(seconds / 60);
18
+ if (minutes < 60) return `${minutes}m ago`;
19
+ const hours = Math.floor(minutes / 60);
20
+ if (hours < 24) return `${hours}h ago`;
21
+ return `${Math.floor(hours / 24)}d ago`;
22
+ }
23
+ async function pathExists(p) {
24
+ try {
25
+ await access(p);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+ async function handleProjectsList(_options, ctx) {
32
+ const stored = await readStoredProjects();
33
+ if (!stored || Object.keys(stored.projects).length === 0) {
34
+ if (ctx.jsonMode) {
35
+ writeJson([]);
36
+ return;
37
+ }
38
+ ui.hint("No tracked projects. Run `keystroke init` to get started.");
39
+ return;
40
+ }
41
+ const entries = Object.entries(stored.projects).sort(([, a], [, b]) => new Date(b.lastAccessed).getTime() - new Date(a.lastAccessed).getTime());
42
+ if (ctx.jsonMode) {
43
+ writeJson(entries.map(([projectPath, entry]) => ({
44
+ name: entry.name ?? null,
45
+ path: projectPath,
46
+ lastAccessed: entry.lastAccessed,
47
+ isRecent: stored.lastProject === projectPath
48
+ })));
49
+ return;
50
+ }
51
+ const table = new Table({
52
+ head: [
53
+ "Name",
54
+ "Path",
55
+ "Last Accessed"
56
+ ],
57
+ style: { head: [] }
58
+ });
59
+ for (const [projectPath, entry] of entries) {
60
+ const exists = await pathExists(projectPath);
61
+ const nameCol = entry.name ?? (exists ? await getProjectNameFromConfig(projectPath) : void 0) ?? style("(unnamed)", ANSI.dim);
62
+ const pathCol = exists ? projectPath : `${projectPath} ${style("(missing)", ANSI.yellow)}`;
63
+ const timeCol = relativeTime(entry.lastAccessed);
64
+ const indicator = stored.lastProject === projectPath ? ` ${style("(recent)", ANSI.cyan)}` : "";
65
+ table.push([
66
+ nameCol,
67
+ `${pathCol}${indicator}`,
68
+ timeCol
69
+ ]);
70
+ }
71
+ ui.text(table.toString());
72
+ ui.hint(`\n${entries.length} project(s) tracked`);
73
+ }
74
+ //#endregion
75
+ export { handleProjectsList };
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { D as throwReportedCliExit, h as toErrorMessage, o as ANSI, s as style, t as ui } from "./keystroke.mjs";
4
+ import { i as writeJson } from "./output-DM4b7KgY.mjs";
5
+ import { i as requireClient } from "./context-T7HZuB97.mjs";
6
+ import Table from "cli-table3";
7
+ //#region src/commands/api-keys/list.handler.ts
8
+ async function handleApiKeysList(_options, ctx) {
9
+ const client = requireClient(ctx);
10
+ try {
11
+ const { apiKeys } = await client.apiKeys.list();
12
+ if (ctx.jsonMode) {
13
+ writeJson(apiKeys.map((key) => ({
14
+ id: key.id,
15
+ name: key.name,
16
+ ownerEmail: key.ownerEmail,
17
+ organizationId: key.organizationId,
18
+ createdAt: key.createdAt,
19
+ lastUsedAt: key.lastUsedAt,
20
+ expiresAt: key.expiresAt
21
+ })));
22
+ return;
23
+ }
24
+ if (apiKeys.length === 0) {
25
+ ui.hint("No API keys found.");
26
+ return;
27
+ }
28
+ const table = new Table({
29
+ head: [
30
+ "Name",
31
+ "ID",
32
+ "Owner",
33
+ "Created"
34
+ ],
35
+ style: { head: [] }
36
+ });
37
+ for (const key of apiKeys) table.push([
38
+ key.name ?? style("(unnamed)", ANSI.dim),
39
+ key.id,
40
+ key.ownerEmail,
41
+ new Date(key.createdAt).toLocaleDateString()
42
+ ]);
43
+ ui.text(table.toString());
44
+ ui.hint(`${apiKeys.length} API key(s) total`);
45
+ } catch (error) {
46
+ ui.error(`Failed to list API keys: ${toErrorMessage(error)}`);
47
+ throwReportedCliExit(`Failed to list API keys: ${toErrorMessage(error)}`, { cause: error });
48
+ }
49
+ }
50
+ //#endregion
51
+ export { handleApiKeysList };
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { D as throwReportedCliExit, t as ui } from "./keystroke.mjs";
4
+ import { i as writeJson } from "./output-DM4b7KgY.mjs";
5
+ import { a as readManifestsFromOutDir } from "./dist-BkJUoBiG.mjs";
6
+ import { n as resolveWorkflowsDir } from "./resolve-project-DDJ29sCF.mjs";
7
+ import { n as getProcessEnv } from "./env-91KwMKov.mjs";
8
+ import { i as requireClient } from "./context-T7HZuB97.mjs";
9
+ import { t as readCredentialEnvMap } from "./credential-env-map-CI8yWHVy.mjs";
10
+ import { a as loadProjectDotenvFile, i as enrichServerCredentialRow, l as renderCredentialListBlocks, n as buildSyntheticNotOnServerRows, r as collectMergedManifestGroups, t as buildCredentialWorkflowConsumersByGroup } from "./list-enrichment-y-cwizLr.mjs";
11
+ import dayjs from "dayjs";
12
+ //#region src/commands/credentials/list/fetch-all-credential-sets.ts
13
+ /**
14
+ * Pages through GET /credentials until all accessible credential sets are loaded.
15
+ * Used so manifest-only synthetic rows match the full server inventory (not just the current page).
16
+ */
17
+ async function fetchAllAccessibleCredentialSets(client) {
18
+ const all = [];
19
+ let offset = 0;
20
+ const limit = 100;
21
+ let total = 0;
22
+ for (;;) {
23
+ const res = await client.credentials.list({
24
+ limit,
25
+ offset
26
+ });
27
+ total = res.pagination.total;
28
+ const batch = res.credentialSets ?? [];
29
+ all.push(...batch);
30
+ if (batch.length === 0 || all.length >= total) break;
31
+ offset += limit;
32
+ }
33
+ return all;
34
+ }
35
+ //#endregion
36
+ //#region src/commands/credentials/list/list.handler.ts
37
+ /** Compact date for block layout (full precision in `--json`). */
38
+ function formatCreatedAtForDisplay(iso) {
39
+ const d = dayjs(iso);
40
+ return d.isValid() ? d.format("MMM D, YYYY") : iso;
41
+ }
42
+ function workflowLabelsForServerRow(cs, workflowConsumersByGroup) {
43
+ return workflowConsumersByGroup.get(`${cs.credentialSetId}\0${cs.scope}`);
44
+ }
45
+ async function resolveServerRowsForManifestMatch(client, manifestGroupsLength, offset, pageRows, paginationTotal) {
46
+ if (manifestGroupsLength === 0) return pageRows;
47
+ if (!(offset > 0 || pageRows.length < paginationTotal)) return pageRows;
48
+ return fetchAllAccessibleCredentialSets(client);
49
+ }
50
+ async function handleCredentialsList(options, ctx) {
51
+ const client = requireClient(ctx);
52
+ const limit = options.limit ?? 50;
53
+ const offset = options.offset ?? 0;
54
+ const params = {
55
+ limit,
56
+ offset
57
+ };
58
+ if (options.projectId && options.scope && options.scope !== "project") {
59
+ ui.error("--project-id can only be used with --scope project");
60
+ throwReportedCliExit("--project-id can only be used with --scope project");
61
+ }
62
+ if (options.scope) params.scope = options.scope;
63
+ if (options.credentialSetId) params.credentialSetId = options.credentialSetId;
64
+ if (options.projectId) {
65
+ params.projectId = options.projectId;
66
+ params.scope ??= "project";
67
+ }
68
+ const response = await client.credentials.list(params);
69
+ const credentialSets = response.credentialSets ?? [];
70
+ const workflowsDir = await resolveWorkflowsDir(options.path);
71
+ const manifests = workflowsDir ? await readManifestsFromOutDir(workflowsDir) : [];
72
+ const credentialEnvMap = workflowsDir ? await readCredentialEnvMap(workflowsDir) : null;
73
+ const projectDotenv = workflowsDir ? await loadProjectDotenvFile(workflowsDir) : {};
74
+ const mergedEnv = {
75
+ ...getProcessEnv(),
76
+ ...projectDotenv
77
+ };
78
+ const manifestGroups = collectMergedManifestGroups(manifests);
79
+ const workflowConsumersByGroup = buildCredentialWorkflowConsumersByGroup(manifests);
80
+ const serverRowsForSyntheticMatch = await resolveServerRowsForManifestMatch(client, manifestGroups.length, offset, credentialSets, response.pagination.total);
81
+ if (ctx.jsonMode) {
82
+ const serverRows = credentialSets.map((cs) => {
83
+ const { workflows, where } = enrichServerCredentialRow({
84
+ cs,
85
+ manifestGroups,
86
+ mergedEnv,
87
+ keyToEnv: credentialEnvMap?.[cs.credentialSetId] ?? null,
88
+ workflowLabels: workflowLabelsForServerRow(cs, workflowConsumersByGroup)
89
+ });
90
+ return {
91
+ source: "server",
92
+ id: cs.id,
93
+ credentialSetId: cs.credentialSetId,
94
+ name: cs.name,
95
+ scope: cs.scope,
96
+ projectId: cs.projectId,
97
+ isDefault: cs.isDefault,
98
+ createdAt: cs.createdAt,
99
+ updatedAt: cs.updatedAt,
100
+ workflows,
101
+ where
102
+ };
103
+ });
104
+ const synthetic = buildSyntheticNotOnServerRows({
105
+ serverRows: serverRowsForSyntheticMatch,
106
+ manifestGroups,
107
+ workflowConsumersByGroup,
108
+ mergedEnv,
109
+ credentialEnvMap
110
+ }).map((row) => ({
111
+ source: "manifest",
112
+ id: null,
113
+ credentialSetId: row.credentialSetId,
114
+ name: null,
115
+ scope: row.scope,
116
+ projectId: null,
117
+ isDefault: null,
118
+ createdAt: null,
119
+ updatedAt: null,
120
+ workflows: row.workflows,
121
+ where: row.where
122
+ }));
123
+ writeJson([...serverRows, ...synthetic], response.pagination);
124
+ return;
125
+ }
126
+ if (credentialSets.length === 0 && manifestGroups.length === 0) {
127
+ ui.text("No credential sets found.");
128
+ return;
129
+ }
130
+ const displayRows = [];
131
+ let hasUnknownWhere = false;
132
+ for (const cs of credentialSets) {
133
+ const { workflows, where } = enrichServerCredentialRow({
134
+ cs,
135
+ manifestGroups,
136
+ mergedEnv,
137
+ keyToEnv: credentialEnvMap?.[cs.credentialSetId] ?? null,
138
+ workflowLabels: workflowLabelsForServerRow(cs, workflowConsumersByGroup)
139
+ });
140
+ if (where === "unknown") hasUnknownWhere = true;
141
+ displayRows.push({
142
+ credentialSetId: cs.credentialSetId,
143
+ name: cs.name,
144
+ scope: cs.scope,
145
+ projectId: cs.projectId ?? "—",
146
+ workflows,
147
+ where,
148
+ isDefault: cs.isDefault,
149
+ createdAt: cs.createdAt
150
+ });
151
+ }
152
+ const syntheticRows = buildSyntheticNotOnServerRows({
153
+ serverRows: serverRowsForSyntheticMatch,
154
+ manifestGroups,
155
+ workflowConsumersByGroup,
156
+ mergedEnv,
157
+ credentialEnvMap
158
+ });
159
+ for (const row of syntheticRows) {
160
+ if (row.where === "unknown") hasUnknownWhere = true;
161
+ displayRows.push({
162
+ credentialSetId: row.credentialSetId,
163
+ name: "—",
164
+ scope: row.scope,
165
+ projectId: "—",
166
+ workflows: row.workflows,
167
+ where: row.where,
168
+ isDefault: null,
169
+ createdAt: null
170
+ });
171
+ }
172
+ renderCredentialListBlocks(displayRows, formatCreatedAtForDisplay, {
173
+ pagination: response.pagination,
174
+ syntheticManifestCount: syntheticRows.length
175
+ });
176
+ if (!workflowsDir) ui.hint("No Keystroke project in cwd: workflow names and env checks need --path or run from a project with keystroke.config.ts.");
177
+ if (hasUnknownWhere) ui.hint("Where is \"—\" when keys are unknown: run `keystroke workflows build`, use --path, or rely on server-reported keys.");
178
+ }
179
+ //#endregion
180
+ export { handleCredentialsList };