@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.
- package/AGENTS-blurb.md +123 -0
- package/LICENSE +42 -0
- package/README.md +177 -0
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/bin/keystroke.mjs +107 -0
- package/dist/_manifest-JSRE3H8k.mjs +385 -0
- package/dist/agent-bundle-package-DWV6B_5q-BtV7Xycc.mjs +2344 -0
- package/dist/agent-manifest-CDnbkR2f.mjs +245 -0
- package/dist/agents-CZJGxVqV.mjs +228 -0
- package/dist/api-keys-D2lgguuY.mjs +40 -0
- package/dist/auth-DN2VusyU.mjs +59 -0
- package/dist/auth.handler-CT1BQUvu.mjs +340 -0
- package/dist/browser-qwFrUH82.mjs +24 -0
- package/dist/build-agents-BmM_AsSd-BGi9wtzt.mjs +514 -0
- package/dist/build-metadata-BWS7uhd_-DR8gJjTX.mjs +1422 -0
- package/dist/build-progress-DgYKb4hB.mjs +183 -0
- package/dist/build-tasks-CdihpudT-D5r5HUHe.mjs +91 -0
- package/dist/build-workflows-CfxBnIWh-CdYPv8w2.mjs +370 -0
- package/dist/build.handler-4799CjWH.mjs +36 -0
- package/dist/chunk-CH6r78ws.mjs +37 -0
- package/dist/clear-cache.handler-B9tqSoSM.mjs +11 -0
- package/dist/clear.handler-BTIXXPTJ.mjs +42 -0
- package/dist/clear.handler-BydlX-zE.mjs +11 -0
- package/dist/commander-DfTVqQ-3.mjs +133 -0
- package/dist/concurrency-gXn9Rw8x-DNl2YtrS.mjs +20 -0
- package/dist/connect-BUXkeH0F.mjs +43 -0
- package/dist/connect.handler-CYel9cy6.mjs +430 -0
- package/dist/constants-CPpPdSNg.mjs +8 -0
- package/dist/context-T7HZuB97.mjs +138 -0
- package/dist/credential-env-map-CI8yWHVy.mjs +28 -0
- package/dist/credential-schema-mismatch-BKo5PjcQ.mjs +76 -0
- package/dist/credentials-CvmjU0lK.mjs +171 -0
- package/dist/credentials-OfVHOtG3.mjs +151216 -0
- package/dist/current-deployment-workflow-poHt27i3.mjs +94 -0
- package/dist/current.handler-B8zKzfPp.mjs +21 -0
- package/dist/delete.handler-bAu1iXVQ.mjs +17 -0
- package/dist/deploy-7Jjls436.mjs +26 -0
- package/dist/deploy-BOPIpRWm.mjs +74 -0
- package/dist/deploy-progress-BmGUNFKg.mjs +70 -0
- package/dist/deploy.handler-BAzgiNhd.mjs +370 -0
- package/dist/detect-env-access-CwkOYeYM-D_BCZqV6.mjs +209 -0
- package/dist/diff-utils-NEfcjqxt.mjs +185 -0
- package/dist/diff.handler-Du7SY8K4.mjs +47 -0
- package/dist/dist-BkJUoBiG.mjs +1116 -0
- package/dist/dist-CUK7yBM0.mjs +308 -0
- package/dist/env-91KwMKov.mjs +140 -0
- package/dist/env.handler-BAzBuMzQ.mjs +277 -0
- package/dist/error-boundary-VL-JLfIa.mjs +34 -0
- package/dist/file-metadata-D1vm-XY2.mjs +191 -0
- package/dist/get-intrinsic-zLxwtrLK.mjs +658 -0
- package/dist/import-module-CV84H5fZ-B_CBCmb4.mjs +1747 -0
- package/dist/init-DpMCotSK.mjs +45 -0
- package/dist/init.handler-CPRnif52.mjs +585 -0
- package/dist/inspect.handler-DT_cD036.mjs +146 -0
- package/dist/integration-catalog-Bt-L3GjF.mjs +104 -0
- package/dist/integrations-DlatPK4W.mjs +79 -0
- package/dist/keystroke.d.mts +3 -0
- package/dist/keystroke.mjs +707 -0
- package/dist/layout-CbMtQ2tm.mjs +67 -0
- package/dist/list-enrichment-y-cwizLr.mjs +189 -0
- package/dist/list.handler-BTWvCyjA.mjs +52 -0
- package/dist/list.handler-CWF_Dj15.mjs +24 -0
- package/dist/list.handler-CZ6G2x_G.mjs +75 -0
- package/dist/list.handler-DWaQkJaR.mjs +51 -0
- package/dist/list.handler-DqbFcBW7.mjs +180 -0
- package/dist/list.handler-lq3ZGAn4.mjs +104 -0
- package/dist/logs-BEg9L5l8.mjs +28 -0
- package/dist/logs.handler-6hoMBzqw.mjs +35 -0
- package/dist/logs.handler-BD_dXiL1.mjs +231 -0
- package/dist/metadata-layout-GUYIUo0i-_aG2zjue.mjs +5877 -0
- package/dist/normalize-path-CojS-CgQ-DLCOvnD1.mjs +20 -0
- package/dist/options-CeaTcFxP.mjs +43 -0
- package/dist/org-xLzBtt2_.mjs +41 -0
- package/dist/output-DM4b7KgY.mjs +72 -0
- package/dist/oxc-B3KI3rf_-n9d1hKNq.mjs +119 -0
- package/dist/paused.handler-BMFm9Cff.mjs +94 -0
- package/dist/project-config-D1qsQlO7.mjs +107 -0
- package/dist/projects-CHkRE9rS.mjs +1574 -0
- package/dist/projects-Cjb7sovS.mjs +30 -0
- package/dist/read-credential-keys-77a91T8M-KA0Iw0Z1.mjs +9 -0
- package/dist/register.handler-BPCdor1_.mjs +86 -0
- package/dist/requirements.handler-DPXdSks3.mjs +201 -0
- package/dist/resolve-project-DDJ29sCF.mjs +35 -0
- package/dist/rolldown-runtime-twds-ZHy-BWWzu8VG.mjs +15 -0
- package/dist/run-polling-CAgFRdK3.mjs +20 -0
- package/dist/runs-D9hNLb9A.mjs +259 -0
- package/dist/schedule-BXx3uXwr.mjs +1142 -0
- package/dist/schema-17qMfNyI.mjs +18 -0
- package/dist/schema-display-CgmeKigW.mjs +130 -0
- package/dist/schemas-CDib1RhE.mjs +125 -0
- package/dist/skills-sync.handler-DIy8GR16.mjs +34 -0
- package/dist/skills.command-CrjI2dN9.mjs +35 -0
- package/dist/skills.handler-Bz8bJKql.mjs +9 -0
- package/dist/source-analysis-Cj-ADyu--BJQcFPCG.mjs +144 -0
- package/dist/spinner-progress-DMVwgqO9.mjs +173 -0
- package/dist/src-C0X6u_Mw.mjs +1340 -0
- package/dist/src-eHwu-Gfw.mjs +369 -0
- package/dist/status.handler-BO4nwvWn.mjs +101 -0
- package/dist/switch.handler-D_9213Vf.mjs +51 -0
- package/dist/sync-BL_Mo5st.mjs +39 -0
- package/dist/sync-keystroke-agent-skills-Kx_H7UTd.mjs +70 -0
- package/dist/sync.handler-BUFPdzWz.mjs +82 -0
- package/dist/task-B2sZMaZu.mjs +8 -0
- package/dist/task-target-build-CBeCKbu2.mjs +432 -0
- package/dist/task-target-deploy-C5X-USeR.mjs +4 -0
- package/dist/task-target-deploy-CA6elFpF-BEr4gkol.mjs +271 -0
- package/dist/task-target-deploy-runner.d.mts +3 -0
- package/dist/task-target-deploy-runner.mjs +202 -0
- package/dist/test-BHTgR3UA.mjs +698 -0
- package/dist/test.handler-BcPQ8b74.mjs +13 -0
- package/dist/trigger-artifacts-DQPbQNqC-B4yeeFBY.mjs +239 -0
- package/dist/trigger-manifest-CY7brZeg.mjs +30 -0
- package/dist/try-deploy.handler-DqybNhXx.mjs +490 -0
- package/dist/upload-CkU--iDC.mjs +207 -0
- package/dist/upload.handler-DCtiznQp.mjs +441 -0
- package/dist/utils-CywxCDM7.mjs +14 -0
- package/dist/validate.handler-DOcTaJL0.mjs +280 -0
- package/dist/workflow-build-DBQaBfnn.mjs +1819 -0
- package/dist/workflow-bundler-BPiqVscj-X1PFFAuP.mjs +167 -0
- package/dist/workflows-g9z87AJJ.mjs +799 -0
- package/dist/writer-BG8poUm3-BbXlU2kI.mjs +426 -0
- package/package.json +87 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { r as RelativeFilePathSchema } from "./file-metadata-D1vm-XY2.mjs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
//#region ../../packages/workflow-builder/dist/normalize-path-CojS-CgQ.mjs
|
|
6
|
+
function normalizeRelativeFilePath(relativePath) {
|
|
7
|
+
const normalizedPath = relativePath.replaceAll("\\", "/");
|
|
8
|
+
return RelativeFilePathSchema.parse(normalizedPath);
|
|
9
|
+
}
|
|
10
|
+
/** Parse a string as a `RelativeFilePath`, throwing if it's not a valid normalized relative path. */
|
|
11
|
+
function parseRelativeFilePath(raw) {
|
|
12
|
+
return RelativeFilePathSchema.parse(raw);
|
|
13
|
+
}
|
|
14
|
+
function toRelativeFilePath(projectRoot, filePath) {
|
|
15
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
16
|
+
const resolvedFilePath = path.resolve(filePath);
|
|
17
|
+
return normalizeRelativeFilePath(path.relative(resolvedProjectRoot, resolvedFilePath));
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { parseRelativeFilePath as n, toRelativeFilePath as r, normalizeRelativeFilePath as t };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/commands/workflows/_shared/options.ts
|
|
5
|
+
/**
|
|
6
|
+
* Options schema for `workflows try-deploy` (build, upload, and run on server).
|
|
7
|
+
*
|
|
8
|
+
* Resolution order for workflow ref: authored workflow id (preferred) then name. If multiple workflows share a
|
|
9
|
+
* name, all are run sequentially. Use workflow id for exact targeting.
|
|
10
|
+
*/
|
|
11
|
+
const WorkflowsRunOptionsSchema = z.object({
|
|
12
|
+
/** Authored workflow id (preferred) or workflow name. */
|
|
13
|
+
workflow: z.string(),
|
|
14
|
+
input: z.string().optional(),
|
|
15
|
+
inputFile: z.string().optional(),
|
|
16
|
+
path: z.string().optional(),
|
|
17
|
+
verbose: z.boolean().default(false),
|
|
18
|
+
timeout: z.coerce.number().int().default(120)
|
|
19
|
+
});
|
|
20
|
+
const RUN_OPTIONS_CONFIG = {
|
|
21
|
+
input: {
|
|
22
|
+
flag: "--input <json>",
|
|
23
|
+
description: "Workflow input as inline JSON string"
|
|
24
|
+
},
|
|
25
|
+
inputFile: {
|
|
26
|
+
flag: "--input-file <path>",
|
|
27
|
+
description: "Path to a JSON file containing workflow input"
|
|
28
|
+
},
|
|
29
|
+
path: {
|
|
30
|
+
flag: "--path <dir>",
|
|
31
|
+
description: "Path to project root (directory containing keystroke.config.ts); auto-discovered from CWD if omitted"
|
|
32
|
+
},
|
|
33
|
+
verbose: {
|
|
34
|
+
flag: "--verbose",
|
|
35
|
+
description: "Show detailed build and execution logs"
|
|
36
|
+
},
|
|
37
|
+
timeout: {
|
|
38
|
+
flag: "--timeout <seconds>",
|
|
39
|
+
description: "Max seconds to wait for workflow completion (default: 120)"
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { WorkflowsRunOptionsSchema as n, RUN_OPTIONS_CONFIG as t };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { t as createTypedCommand } from "./commander-DfTVqQ-3.mjs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/commands/org/org.command.ts
|
|
6
|
+
const OrgOptionsSchema = z.object({});
|
|
7
|
+
const ORG_OPTIONS_CONFIG = {};
|
|
8
|
+
function createOrgCommand() {
|
|
9
|
+
return createTypedCommand({
|
|
10
|
+
name: "org",
|
|
11
|
+
description: "Manage organization context",
|
|
12
|
+
schema: OrgOptionsSchema,
|
|
13
|
+
optionsConfig: ORG_OPTIONS_CONFIG,
|
|
14
|
+
loadHandler: async () => (await import("./list.handler-CWF_Dj15.mjs")).handleOrgList,
|
|
15
|
+
subcommands: [
|
|
16
|
+
createTypedCommand({
|
|
17
|
+
name: "list",
|
|
18
|
+
description: "List your organizations",
|
|
19
|
+
schema: OrgOptionsSchema,
|
|
20
|
+
optionsConfig: ORG_OPTIONS_CONFIG,
|
|
21
|
+
loadHandler: async () => (await import("./list.handler-CWF_Dj15.mjs")).handleOrgList
|
|
22
|
+
}),
|
|
23
|
+
createTypedCommand({
|
|
24
|
+
name: "current",
|
|
25
|
+
description: "Show the current organization",
|
|
26
|
+
schema: OrgOptionsSchema,
|
|
27
|
+
optionsConfig: ORG_OPTIONS_CONFIG,
|
|
28
|
+
loadHandler: async () => (await import("./current.handler-B8zKzfPp.mjs")).handleOrgCurrent
|
|
29
|
+
}),
|
|
30
|
+
createTypedCommand({
|
|
31
|
+
name: "switch",
|
|
32
|
+
description: "Switch to a different organization",
|
|
33
|
+
schema: OrgOptionsSchema,
|
|
34
|
+
optionsConfig: ORG_OPTIONS_CONFIG,
|
|
35
|
+
loadHandler: async () => (await import("./switch.handler-D_9213Vf.mjs")).handleOrgSwitch
|
|
36
|
+
})
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { createOrgCommand };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { D as throwReportedCliExit } from "./keystroke.mjs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/lib/output.ts
|
|
6
|
+
/**
|
|
7
|
+
* Shared Zod schema for the `--json` flag.
|
|
8
|
+
* Use `.extend()` or spread `.shape` to add to command schemas.
|
|
9
|
+
*
|
|
10
|
+
* The field is `optional()` (no default) so we can distinguish:
|
|
11
|
+
* - `true` → explicit `--json`
|
|
12
|
+
* - `false` → explicit `--no-json`
|
|
13
|
+
* - `undefined` → not specified → auto-detect via TTY
|
|
14
|
+
*/
|
|
15
|
+
const JsonOptionSchema = z.object({ json: z.boolean().optional() });
|
|
16
|
+
/** Shared option config entry for --json. Spread into command option configs. */
|
|
17
|
+
const JSON_OPTION_CONFIG = { json: {
|
|
18
|
+
flag: "--json",
|
|
19
|
+
description: "Output machine-readable JSON"
|
|
20
|
+
} };
|
|
21
|
+
/**
|
|
22
|
+
* Determines whether the current command should output JSON.
|
|
23
|
+
*
|
|
24
|
+
* - `--json` → always true
|
|
25
|
+
* - `--no-json` → always false
|
|
26
|
+
* - (omitted) → true when stdout is NOT a TTY (piped / captured)
|
|
27
|
+
*/
|
|
28
|
+
function isJsonMode(options) {
|
|
29
|
+
if (options.json === true) return true;
|
|
30
|
+
if (options.json === false) return false;
|
|
31
|
+
return !process.stdout.isTTY;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Writes structured JSON to stdout.
|
|
35
|
+
*
|
|
36
|
+
* - With `pagination`: wraps data as `{ items: data, pagination: { ...meta, hasMore } }`
|
|
37
|
+
* - Without pagination: writes `data` directly (caller shapes the object)
|
|
38
|
+
*/
|
|
39
|
+
function writeJson(data, pagination) {
|
|
40
|
+
if (pagination) {
|
|
41
|
+
const output = {
|
|
42
|
+
items: data,
|
|
43
|
+
pagination: {
|
|
44
|
+
limit: pagination.limit,
|
|
45
|
+
offset: pagination.offset,
|
|
46
|
+
total: pagination.total,
|
|
47
|
+
hasMore: pagination.offset + pagination.limit < pagination.total
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
51
|
+
} else process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Writes a structured JSON error to stdout and stops command execution.
|
|
55
|
+
*
|
|
56
|
+
* Output shape: `{ error: string, code?: string, hint?: string }`
|
|
57
|
+
* Agents detect errors by checking for the `error` key in the response.
|
|
58
|
+
*
|
|
59
|
+
* `cause` is forwarded onto the thrown `CliExitError` so the underlying error
|
|
60
|
+
* is preserved for logging and telemetry; it is deliberately not written to
|
|
61
|
+
* stdout (agents should parse only the documented `{ error, code?, hint? }`
|
|
62
|
+
* shape).
|
|
63
|
+
*/
|
|
64
|
+
function writeJsonError(message, opts) {
|
|
65
|
+
const output = { error: message };
|
|
66
|
+
if (opts?.code) output.code = opts.code;
|
|
67
|
+
if (opts?.hint) output.hint = opts.hint;
|
|
68
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
69
|
+
throwReportedCliExit(message, opts?.cause !== void 0 ? { cause: opts.cause } : {});
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { writeJsonError as a, writeJson as i, JsonOptionSchema as n, isJsonMode as r, JSON_OPTION_CONFIG as t };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { parseSync, visitorKeys } from "oxc-parser";
|
|
4
|
+
//#region ../../packages/workflow-builder/dist/oxc-B3KI3rf_.mjs
|
|
5
|
+
function parseSourceFile(filePath, sourceText) {
|
|
6
|
+
const result = parseSync(filePath, sourceText, {
|
|
7
|
+
lang: filePath.endsWith(".tsx") ? "tsx" : "ts",
|
|
8
|
+
sourceType: "module",
|
|
9
|
+
astType: "ts",
|
|
10
|
+
preserveParens: true
|
|
11
|
+
});
|
|
12
|
+
if (result.errors.length > 0) throw new Error(`Failed to parse ${filePath}: ${result.errors.map((error) => error.message).join("; ")}`);
|
|
13
|
+
const lineStarts = computeLineStarts(sourceText);
|
|
14
|
+
const program = result.program;
|
|
15
|
+
return {
|
|
16
|
+
filePath,
|
|
17
|
+
sourceText,
|
|
18
|
+
program,
|
|
19
|
+
statements: program.body,
|
|
20
|
+
getLineAndCharacterOfPosition(position) {
|
|
21
|
+
return getLineAndCharacterOfPosition(lineStarts, position);
|
|
22
|
+
},
|
|
23
|
+
getText(node) {
|
|
24
|
+
return sourceText.slice(node.start, node.end);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function visitNode(node, visitor, parent = null) {
|
|
29
|
+
if (!isNode(node)) return;
|
|
30
|
+
if (visitor(node, parent) === false) return;
|
|
31
|
+
for (const key of visitorKeys[node.type] ?? []) {
|
|
32
|
+
const value = node[key];
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
for (const child of value) if (isNode(child)) visitNode(child, visitor, node);
|
|
35
|
+
} else if (isNode(value)) visitNode(value, visitor, node);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function isNode(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && typeof value.type === "string" && typeof value.start === "number" && typeof value.end === "number";
|
|
40
|
+
}
|
|
41
|
+
function identifierName(node) {
|
|
42
|
+
return isNode(node) && node.type === "Identifier" && typeof node.name === "string" ? node.name : void 0;
|
|
43
|
+
}
|
|
44
|
+
function literalString(node) {
|
|
45
|
+
return isNode(node) && node.type === "Literal" && typeof node.value === "string" ? node.value : void 0;
|
|
46
|
+
}
|
|
47
|
+
function literalNumber(node) {
|
|
48
|
+
return isNode(node) && node.type === "Literal" && typeof node.value === "number" ? node.value : void 0;
|
|
49
|
+
}
|
|
50
|
+
function propertyKeyName(node) {
|
|
51
|
+
if (!isNode(node)) return;
|
|
52
|
+
return identifierName(node) ?? literalString(node);
|
|
53
|
+
}
|
|
54
|
+
function unwrapExpression(expression) {
|
|
55
|
+
let current = expression;
|
|
56
|
+
while (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "ParenthesizedExpression") {
|
|
57
|
+
const nested = current.expression;
|
|
58
|
+
if (!isNode(nested)) break;
|
|
59
|
+
current = nested;
|
|
60
|
+
}
|
|
61
|
+
return current;
|
|
62
|
+
}
|
|
63
|
+
function getFirstObjectArgument(expression) {
|
|
64
|
+
if (expression.type !== "NewExpression" && expression.type !== "CallExpression") return null;
|
|
65
|
+
const [firstArgument] = Array.isArray(expression.arguments) ? expression.arguments : [];
|
|
66
|
+
const unwrapped = isNode(firstArgument) ? unwrapExpression(firstArgument) : null;
|
|
67
|
+
return unwrapped?.type === "ObjectExpression" ? unwrapped : null;
|
|
68
|
+
}
|
|
69
|
+
function readStringProperty(objectExpression, propertyName, stringBindings) {
|
|
70
|
+
const value = readPropertyValue(objectExpression, propertyName);
|
|
71
|
+
return value ? readStringExpression(value, stringBindings) : void 0;
|
|
72
|
+
}
|
|
73
|
+
function readPropertyValue(objectExpression, propertyName) {
|
|
74
|
+
if (!objectExpression) return null;
|
|
75
|
+
for (const property of objectExpression.properties) {
|
|
76
|
+
if (property.type !== "Property" || property.kind !== "init") continue;
|
|
77
|
+
if (propertyKeyName(property.key) === propertyName && isNode(property.value)) return property.value;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
function readStringExpression(expression, stringBindings) {
|
|
82
|
+
const unwrapped = unwrapExpression(expression);
|
|
83
|
+
const literal = literalString(unwrapped);
|
|
84
|
+
if (literal !== void 0) return literal;
|
|
85
|
+
const identifier = identifierName(unwrapped);
|
|
86
|
+
if (identifier && stringBindings) return stringBindings.get(identifier);
|
|
87
|
+
if (unwrapped.type === "TemplateLiteral" && Array.isArray(unwrapped.expressions) && unwrapped.expressions.length === 0 && Array.isArray(unwrapped.quasis)) {
|
|
88
|
+
const [quasi] = unwrapped.quasis;
|
|
89
|
+
const value = isNode(quasi) ? quasi.value : void 0;
|
|
90
|
+
return typeof value === "object" && value !== null && "cooked" in value ? value.cooked ?? void 0 : void 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function computeLineStarts(sourceText) {
|
|
94
|
+
const starts = [0];
|
|
95
|
+
for (let index = 0; index < sourceText.length; index += 1) if (sourceText.charCodeAt(index) === 10) starts.push(index + 1);
|
|
96
|
+
return starts;
|
|
97
|
+
}
|
|
98
|
+
function getLineAndCharacterOfPosition(lineStarts, position) {
|
|
99
|
+
let low = 0;
|
|
100
|
+
let high = lineStarts.length - 1;
|
|
101
|
+
while (low <= high) {
|
|
102
|
+
const middle = Math.floor((low + high) / 2);
|
|
103
|
+
const start = lineStarts[middle] ?? 0;
|
|
104
|
+
const next = lineStarts[middle + 1] ?? Number.POSITIVE_INFINITY;
|
|
105
|
+
if (position < start) high = middle - 1;
|
|
106
|
+
else if (position >= next) low = middle + 1;
|
|
107
|
+
else return {
|
|
108
|
+
line: middle,
|
|
109
|
+
character: position - start
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const lastLine = Math.max(0, lineStarts.length - 1);
|
|
113
|
+
return {
|
|
114
|
+
line: lastLine,
|
|
115
|
+
character: position - (lineStarts[lastLine] ?? 0)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { literalString as a, readStringProperty as c, literalNumber as i, unwrapExpression as l, identifierName as n, parseSourceFile as o, isNode as r, readPropertyValue as s, getFirstObjectArgument as t, visitNode as u };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { D as throwReportedCliExit, h as toErrorMessage, 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
|
+
//#region src/commands/workflows/paused/paused.handler.ts
|
|
7
|
+
async function handleWorkflowsPaused(options, ctx) {
|
|
8
|
+
const client = requireClient(ctx);
|
|
9
|
+
try {
|
|
10
|
+
const match = await listPausedRuns(client, options);
|
|
11
|
+
if (ctx.jsonMode) {
|
|
12
|
+
writeJson(match.result);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (match.result.runs.length === 0) {
|
|
16
|
+
if (options.workflow) {
|
|
17
|
+
ui.hint(`No paused runs found for workflow "${options.workflow}".`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
ui.hint("No paused workflow runs found.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (options.workflow) {
|
|
24
|
+
if (match.kind === "authoredId") ui.hint(`Showing paused runs matching workflow id "${options.workflow}".`);
|
|
25
|
+
else if (match.kind === "name") ui.hint(`Showing paused runs matching workflow name "${options.workflow}".`);
|
|
26
|
+
}
|
|
27
|
+
for (const [index, run] of match.result.runs.entries()) {
|
|
28
|
+
ui.text(renderPausedRun(run));
|
|
29
|
+
if (index < match.result.runs.length - 1) ui.br();
|
|
30
|
+
}
|
|
31
|
+
ui.hint(`Showing ${match.result.runs.length} paused run(s)${options.type ? ` filtered to ${options.type}` : ""}.`);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
ui.error(`Failed to list paused workflow runs: ${toErrorMessage(error)}`);
|
|
34
|
+
throwReportedCliExit(`Failed to list paused workflow runs: ${toErrorMessage(error)}`, { cause: error });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function listPausedRuns(client, options) {
|
|
38
|
+
if (!options.workflow) return {
|
|
39
|
+
kind: "all",
|
|
40
|
+
result: await client.runs.listPausedRuns({
|
|
41
|
+
type: options.type,
|
|
42
|
+
limit: options.limit
|
|
43
|
+
})
|
|
44
|
+
};
|
|
45
|
+
const byAuthoredId = await client.runs.listPausedRuns({
|
|
46
|
+
authoredWorkflowId: options.workflow,
|
|
47
|
+
type: options.type,
|
|
48
|
+
limit: options.limit
|
|
49
|
+
});
|
|
50
|
+
if (byAuthoredId.runs.length > 0) return {
|
|
51
|
+
kind: "authoredId",
|
|
52
|
+
result: byAuthoredId
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
kind: "name",
|
|
56
|
+
result: await client.runs.listPausedRuns({
|
|
57
|
+
workflowName: options.workflow,
|
|
58
|
+
type: options.type,
|
|
59
|
+
limit: options.limit
|
|
60
|
+
})
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function renderPausedRun(run) {
|
|
64
|
+
const lines = [
|
|
65
|
+
`Workflow: ${run.workflowName} (${run.authoredWorkflowId})`,
|
|
66
|
+
`Run: ${run.id}`,
|
|
67
|
+
`Pause: ${run.pause.type} since ${formatTimestamp(run.pausedAt)} (${formatAge(run.ageMs)})`,
|
|
68
|
+
`Status: ${run.status} | Holds slot: ${run.holdsExecutionSlot ? "yes" : "no"}`,
|
|
69
|
+
`Progress: ${run.progress.completedStepsCount} completed${run.progress.lastCompletedStep ? ` | Last: ${run.progress.lastCompletedStep.stepName}` : ""}`
|
|
70
|
+
];
|
|
71
|
+
if (run.progress.completedSteps.length > 0) lines.push(`Completed steps: ${run.progress.completedSteps.map((step) => step.stepName).join(", ")}`);
|
|
72
|
+
if (run.activePauses.length > 1) lines.push(`Active pauses: ${run.activePauses.map((pause) => pause.type).join(", ")} (${run.activePauses.length})`);
|
|
73
|
+
if (run.pause.type === "wait") {
|
|
74
|
+
lines.push(`Resume at: ${formatTimestamp(run.pause.resumeAt)}`);
|
|
75
|
+
lines.push(`Wait duration: ${formatAge(run.pause.durationMs)}`);
|
|
76
|
+
lines.push(`Overdue: ${run.pause.overdue ? "yes" : "no"}`);
|
|
77
|
+
} else {
|
|
78
|
+
lines.push(`Approvals: ${run.pause.approvals}/${run.pause.requiredApprovals}`);
|
|
79
|
+
if (run.pause.expiresAt) lines.push(`Expires at: ${formatTimestamp(run.pause.expiresAt)}`);
|
|
80
|
+
}
|
|
81
|
+
return lines.join("\n");
|
|
82
|
+
}
|
|
83
|
+
function formatTimestamp(value) {
|
|
84
|
+
return new Date(value).toLocaleString();
|
|
85
|
+
}
|
|
86
|
+
function formatAge(ms) {
|
|
87
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
88
|
+
if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
|
|
89
|
+
if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
|
|
90
|
+
if (ms < 864e5) return `${Math.floor(ms / 36e5)}h`;
|
|
91
|
+
return `${Math.floor(ms / 864e5)}d`;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { handleWorkflowsPaused };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { access, mkdir, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
//#region ../../packages/workflow-core/src/config.ts
|
|
8
|
+
/**
|
|
9
|
+
* Canonical schema for the `build` field of a `keystroke.config.ts` file.
|
|
10
|
+
*
|
|
11
|
+
* This is the single source of truth for project-level build options.
|
|
12
|
+
* Platform loaders (`@keystroke/project-config`) and type consumers re-use
|
|
13
|
+
* this schema; nobody should redefine it downstream.
|
|
14
|
+
*/
|
|
15
|
+
const KeystrokeProjectBuildConfigSchema = z.object({ outDir: z.string().min(1).optional() });
|
|
16
|
+
/**
|
|
17
|
+
* Canonical schema for a Keystroke project's `keystroke.config.ts` file.
|
|
18
|
+
*
|
|
19
|
+
* This schema is the single source of truth for the shape a project
|
|
20
|
+
* author writes with `defineConfig(...)`. Platform loaders
|
|
21
|
+
* (`@keystroke/project-config`) re-use this schema to validate loaded
|
|
22
|
+
* configs, and API / DB boundaries re-derive their contracts from it.
|
|
23
|
+
*/
|
|
24
|
+
const KeystrokeProjectConfigSchema = z.object({
|
|
25
|
+
name: z.string().min(1),
|
|
26
|
+
organizationId: z.uuid(),
|
|
27
|
+
projectId: z.uuid(),
|
|
28
|
+
description: z.string().min(1).optional(),
|
|
29
|
+
build: KeystrokeProjectBuildConfigSchema.optional()
|
|
30
|
+
});
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region ../../packages/project-config/src/config-content.ts
|
|
33
|
+
function serializeConfigObject(config) {
|
|
34
|
+
return JSON.stringify(config, null, 2);
|
|
35
|
+
}
|
|
36
|
+
function createProjectConfigContent(config) {
|
|
37
|
+
return [
|
|
38
|
+
"import { defineConfig } from '@keystroke/workflow-core/config';",
|
|
39
|
+
"",
|
|
40
|
+
`export default defineConfig(${serializeConfigObject(config)});`,
|
|
41
|
+
""
|
|
42
|
+
].join("\n");
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region ../../packages/project-config/src/constants.ts
|
|
46
|
+
const KEYSTROKE_PROJECT_CONFIG_FILE_NAME = "keystroke.config.ts";
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region ../../packages/project-config/src/project-config.ts
|
|
49
|
+
/** Walks up from startDir looking for keystroke.config.ts. Returns the containing directory, or null if not found. */
|
|
50
|
+
async function findProjectRoot(startDir) {
|
|
51
|
+
let currentDir = path.resolve(startDir);
|
|
52
|
+
while (true) {
|
|
53
|
+
try {
|
|
54
|
+
await access(path.join(currentDir, KEYSTROKE_PROJECT_CONFIG_FILE_NAME));
|
|
55
|
+
return currentDir;
|
|
56
|
+
} catch {}
|
|
57
|
+
const parentDir = path.dirname(currentDir);
|
|
58
|
+
if (parentDir === currentDir) return null;
|
|
59
|
+
currentDir = parentDir;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Reads and validates keystroke.config.ts from projectRoot. Returns null if the file is missing or invalid. */
|
|
63
|
+
async function readProjectConfig(projectRoot) {
|
|
64
|
+
try {
|
|
65
|
+
return await loadProjectConfig(projectRoot);
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Validates and writes keystroke.config.ts to projectRoot. Creates parent directories if needed. */
|
|
71
|
+
async function writeProjectConfig(projectRoot, config) {
|
|
72
|
+
const validatedConfig = KeystrokeProjectConfigSchema.parse(config);
|
|
73
|
+
const configPath = getProjectConfigPath(projectRoot);
|
|
74
|
+
await mkdir(path.dirname(configPath), { recursive: true });
|
|
75
|
+
await writeFile(configPath, createProjectConfigContent(validatedConfig), "utf-8");
|
|
76
|
+
}
|
|
77
|
+
/** Reads and validates keystroke.config.ts, throwing a descriptive error if missing or invalid. */
|
|
78
|
+
async function assertWorkflowProjectRoot(projectRoot) {
|
|
79
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
80
|
+
const configPath = getProjectConfigPath(resolvedProjectRoot);
|
|
81
|
+
try {
|
|
82
|
+
await access(configPath);
|
|
83
|
+
} catch {
|
|
84
|
+
throw new Error(`Missing ${KEYSTROKE_PROJECT_CONFIG_FILE_NAME} in workflow root: ${resolvedProjectRoot}`);
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
return await loadProjectConfig(resolvedProjectRoot);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const message = error instanceof Error ? error.message : "Unknown config load failure";
|
|
90
|
+
throw new Error(`Invalid ${KEYSTROKE_PROJECT_CONFIG_FILE_NAME}: ${message} (in ${resolvedProjectRoot})`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getProjectConfigPath(projectRoot) {
|
|
94
|
+
return path.join(path.resolve(projectRoot), KEYSTROKE_PROJECT_CONFIG_FILE_NAME);
|
|
95
|
+
}
|
|
96
|
+
async function loadProjectConfig(projectRoot) {
|
|
97
|
+
const configModule = await importConfigModule(getProjectConfigPath(projectRoot));
|
|
98
|
+
return KeystrokeProjectConfigSchema.parse(configModule.default);
|
|
99
|
+
}
|
|
100
|
+
async function importConfigModule(configPath) {
|
|
101
|
+
const configUrl = pathToFileURL(configPath);
|
|
102
|
+
const configStat = await stat(configPath);
|
|
103
|
+
configUrl.searchParams.set("t", String(configStat.mtimeMs));
|
|
104
|
+
return await import(configUrl.href);
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { writeProjectConfig as a, readProjectConfig as i, findProjectRoot as n, getProjectConfigPath as r, assertWorkflowProjectRoot as t };
|