@runcofactor/cli 0.10.0 → 0.15.0
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/dist/account-22LMP5PX.js +255 -0
- package/dist/account-22LMP5PX.js.map +1 -0
- package/dist/{api-catalog-GFGCLVS4.js → api-catalog-WTFQNLK6.js} +5 -4
- package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-WTFQNLK6.js.map} +1 -1
- package/dist/{attachment-SJC7D7FY.js → attachment-U3NKVPEY.js} +5 -4
- package/dist/{attachment-SJC7D7FY.js.map → attachment-U3NKVPEY.js.map} +1 -1
- package/dist/augmentor-5GMNAX4M.js +198 -0
- package/dist/augmentor-5GMNAX4M.js.map +1 -0
- package/dist/{auth-EN6NQCJY.js → auth-VQG2CDUD.js} +3 -2
- package/dist/{chunk-A4OFTUVV.js → chunk-3X3NW7QA.js} +7 -4
- package/dist/chunk-3X3NW7QA.js.map +1 -0
- package/dist/{chunk-N7HFALX3.js → chunk-46IUJXSD.js} +2 -2
- package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
- package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
- package/dist/{chunk-B2VISN2V.js → chunk-7VMO2WOZ.js} +238 -68
- package/dist/chunk-7VMO2WOZ.js.map +1 -0
- package/dist/{chunk-QFDOWABX.js → chunk-CMGI7UZD.js} +6 -3
- package/dist/chunk-CMGI7UZD.js.map +1 -0
- package/dist/{chunk-XQXVG7RA.js → chunk-FNPMWDUQ.js} +3 -3
- package/dist/chunk-G4UYGRZR.js +2 -0
- package/dist/{chunk-I4QLI3JO.js → chunk-HDFYUFBI.js} +2 -2
- package/dist/chunk-JMJVGMXI.js +92 -0
- package/dist/chunk-JMJVGMXI.js.map +1 -0
- package/dist/{chunk-SJJW77WN.js → chunk-JYGNMKZ5.js} +129 -38
- package/dist/chunk-JYGNMKZ5.js.map +1 -0
- package/dist/{chunk-52IO7ACJ.js → chunk-KCHKFYVX.js} +68 -10
- package/dist/{chunk-52IO7ACJ.js.map → chunk-KCHKFYVX.js.map} +1 -1
- package/dist/{chunk-ZD3KHESI.js → chunk-KRWRUU6R.js} +27 -7
- package/dist/chunk-KRWRUU6R.js.map +1 -0
- package/dist/{chunk-CLAWT7BJ.js → chunk-NHTEKEYE.js} +3 -3
- package/dist/{chunk-WOHL4FTB.js → chunk-PRBRSIZP.js} +16 -10
- package/dist/chunk-PRBRSIZP.js.map +1 -0
- package/dist/{chunk-GKY2KXO5.js → chunk-UETBLPKM.js} +154 -37
- package/dist/chunk-UETBLPKM.js.map +1 -0
- package/dist/{chunk-4PNOV7OV.js → chunk-VGYN2KU2.js} +9284 -1421
- package/dist/chunk-VGYN2KU2.js.map +1 -0
- package/dist/{chunk-Q4BO4Z5V.js → chunk-XJ5YYYGC.js} +4 -4
- package/dist/{credential-COLTBV6D.js → credential-E6KUHRGU.js} +24 -20
- package/dist/credential-E6KUHRGU.js.map +1 -0
- package/dist/{credential-template-EEPYFG7R.js → credential-template-4GPOPBGD.js} +30 -28
- package/dist/credential-template-4GPOPBGD.js.map +1 -0
- package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-XCLCY6S7.js} +5 -4
- package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-XCLCY6S7.js.map} +1 -1
- package/dist/{docs-3SSORHB5.js → docs-COCZYMW6.js} +95 -10
- package/dist/docs-COCZYMW6.js.map +1 -0
- package/dist/{doctor-OTSIWV6G.js → doctor-ERIVTCQS.js} +9 -5
- package/dist/doctor-ERIVTCQS.js.map +1 -0
- package/dist/environment-7Y4BV6XY.js +221 -0
- package/dist/environment-7Y4BV6XY.js.map +1 -0
- package/dist/{feature-flag-T2LRZME3.js → feature-flag-MHONEX6T.js} +164 -16
- package/dist/feature-flag-MHONEX6T.js.map +1 -0
- package/dist/index.js +123 -58
- package/dist/index.js.map +1 -1
- package/dist/{init-TGWRECT2.js → init-OROOM4HF.js} +5 -4
- package/dist/{init-TGWRECT2.js.map → init-OROOM4HF.js.map} +1 -1
- package/dist/{install-DXSNWZKO.js → install-OJFXHY6U.js} +7 -7
- package/dist/{issues-XIZOPX6T.js → issues-32GONR6C.js} +5 -4
- package/dist/{issues-XIZOPX6T.js.map → issues-32GONR6C.js.map} +1 -1
- package/dist/{knobs-NBINZIUB.js → knobs-UQGGY3CP.js} +7 -5
- package/dist/knobs-UQGGY3CP.js.map +1 -0
- package/dist/{list-3TDHK637.js → list-ZMIKEC5P.js} +5 -4
- package/dist/{list-3TDHK637.js.map → list-ZMIKEC5P.js.map} +1 -1
- package/dist/{local-HICVD6PU.js → local-PUD24GGQ.js} +7 -6
- package/dist/{local-HICVD6PU.js.map → local-PUD24GGQ.js.map} +1 -1
- package/dist/{login-UOIUXVUK.js → login-BSO7YX2D.js} +5 -4
- package/dist/{login-UOIUXVUK.js.map → login-BSO7YX2D.js.map} +1 -1
- package/dist/{mcp-54IJEOUA.js → mcp-4LS7OJUT.js} +24 -25
- package/dist/mcp-4LS7OJUT.js.map +1 -0
- package/dist/{pdf-extract-ABWV3L5X.js → pdf-extract-YHE54XU4.js} +2 -2
- package/dist/product-3JKSJCBC.js +346 -0
- package/dist/product-3JKSJCBC.js.map +1 -0
- package/dist/{property-UXLOLPTD.js → property-K5DBL4UE.js} +76 -4
- package/dist/property-K5DBL4UE.js.map +1 -0
- package/dist/{psql-BYUUNLGG.js → psql-PQFUFTSE.js} +3 -2
- package/dist/{psql-BYUUNLGG.js.map → psql-PQFUFTSE.js.map} +1 -1
- package/dist/{record-4TR2UIIS.js → record-EIIYLFJ4.js} +16 -17
- package/dist/record-EIIYLFJ4.js.map +1 -0
- package/dist/{redis-35VBUYW7.js → redis-BU36EZZI.js} +3 -2
- package/dist/redis-BU36EZZI.js.map +1 -0
- package/dist/{release-OOLTUBUT.js → release-LYC2P7DZ.js} +5 -4
- package/dist/{release-OOLTUBUT.js.map → release-LYC2P7DZ.js.map} +1 -1
- package/dist/{sandbox-XGSUU6G7.js → sandbox-CKHXXZHU.js} +55 -10
- package/dist/sandbox-CKHXXZHU.js.map +1 -0
- package/dist/{scenarios-GWLJEGHA.js → scenarios-2XJ2G3KR.js} +5 -4
- package/dist/{scenarios-GWLJEGHA.js.map → scenarios-2XJ2G3KR.js.map} +1 -1
- package/dist/{session-MYFNZPRP.js → session-UTOD5ZYH.js} +77 -10
- package/dist/session-UTOD5ZYH.js.map +1 -0
- package/dist/{skill-KM34LDMO.js → skill-GMAERMQL.js} +220 -70
- package/dist/skill-GMAERMQL.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +40 -23
- package/dist/skills/debug-run/history/1.2.0.md +262 -0
- package/dist/skills/debug-run/history/1.3.0.md +273 -0
- package/dist/skills/debug-run/history/1.3.1.md +279 -0
- package/dist/skills/debug-run/version.json +1 -1
- package/dist/skills/optimize-cache/SKILL.md +233 -0
- package/dist/{spec-RA3CFEKG.js → spec-2NP36U3Y.js} +54 -25
- package/dist/spec-2NP36U3Y.js.map +1 -0
- package/dist/{src-LB4HBTJQ.js → src-4T6GHXVO.js} +19 -11
- package/dist/src-4T6GHXVO.js.map +1 -0
- package/dist/{src-N3TPKGTY.js → src-CG52HEE7.js} +10 -7
- package/dist/{src-UVDX5UP6.js → src-PK5OZXCM.js} +4 -2
- package/dist/src-UEOOC425.js +5941 -0
- package/dist/src-UEOOC425.js.map +1 -0
- package/dist/{start-PSEQSQA3.js → start-3C7EEFW2.js} +5 -4
- package/dist/{start-PSEQSQA3.js.map → start-3C7EEFW2.js.map} +1 -1
- package/dist/{tag-YY6YAABQ.js → tag-JSHOMLBR.js} +6 -5
- package/dist/{task-IQC3YR5K.js → task-YGWVYTBJ.js} +5 -4
- package/dist/{task-IQC3YR5K.js.map → task-YGWVYTBJ.js.map} +1 -1
- package/dist/{test-suite-DZNJFXDW.js → test-suite-LI7ZH367.js} +12 -7
- package/dist/{test-suite-DZNJFXDW.js.map → test-suite-LI7ZH367.js.map} +1 -1
- package/dist/trace-UMLOSIIR.js +16 -0
- package/dist/{triage-ORG45R5W.js → triage-ZWJG4NBJ.js} +6 -5
- package/dist/triage-ZWJG4NBJ.js.map +1 -0
- package/dist/{upgrade-L2VYMP2V.js → upgrade-S7CGEK6H.js} +3 -3
- package/dist/{workflow-XYFH5LTP.js → workflow-T4HSAOGW.js} +316 -241
- package/dist/workflow-T4HSAOGW.js.map +1 -0
- package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-XVEZ36KF.js} +140 -7
- package/dist/workflow-cache-XVEZ36KF.js.map +1 -0
- package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-T2BULXZ7.js} +9 -8
- package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-T2BULXZ7.js.map} +1 -1
- package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-XXEZICN2.js} +5 -6
- package/dist/workflow-proposals-XXEZICN2.js.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-4PNOV7OV.js.map +0 -1
- package/dist/chunk-A4OFTUVV.js.map +0 -1
- package/dist/chunk-B2VISN2V.js.map +0 -1
- package/dist/chunk-GKY2KXO5.js.map +0 -1
- package/dist/chunk-JICSYFRJ.js +0 -46
- package/dist/chunk-JICSYFRJ.js.map +0 -1
- package/dist/chunk-QFDOWABX.js.map +0 -1
- package/dist/chunk-SJJW77WN.js.map +0 -1
- package/dist/chunk-WOHL4FTB.js.map +0 -1
- package/dist/chunk-ZD3KHESI.js.map +0 -1
- package/dist/credential-COLTBV6D.js.map +0 -1
- package/dist/credential-template-EEPYFG7R.js.map +0 -1
- package/dist/docs-3SSORHB5.js.map +0 -1
- package/dist/doctor-OTSIWV6G.js.map +0 -1
- package/dist/feature-flag-T2LRZME3.js.map +0 -1
- package/dist/knobs-NBINZIUB.js.map +0 -1
- package/dist/mcp-54IJEOUA.js.map +0 -1
- package/dist/property-UXLOLPTD.js.map +0 -1
- package/dist/record-4TR2UIIS.js.map +0 -1
- package/dist/redis-35VBUYW7.js.map +0 -1
- package/dist/sandbox-XGSUU6G7.js.map +0 -1
- package/dist/session-MYFNZPRP.js.map +0 -1
- package/dist/skill-KM34LDMO.js.map +0 -1
- package/dist/spec-RA3CFEKG.js.map +0 -1
- package/dist/src-LB4HBTJQ.js.map +0 -1
- package/dist/trace-HXIW7U2D.js +0 -14
- package/dist/workflow-XYFH5LTP.js.map +0 -1
- package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
- package/dist/workflow-proposals-MTUDX46O.js.map +0 -1
- /package/dist/{auth-EN6NQCJY.js.map → auth-VQG2CDUD.js.map} +0 -0
- /package/dist/{chunk-N7HFALX3.js.map → chunk-46IUJXSD.js.map} +0 -0
- /package/dist/{chunk-XQXVG7RA.js.map → chunk-FNPMWDUQ.js.map} +0 -0
- /package/dist/{pdf-extract-ABWV3L5X.js.map → chunk-G4UYGRZR.js.map} +0 -0
- /package/dist/{chunk-I4QLI3JO.js.map → chunk-HDFYUFBI.js.map} +0 -0
- /package/dist/{chunk-CLAWT7BJ.js.map → chunk-NHTEKEYE.js.map} +0 -0
- /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-XJ5YYYGC.js.map} +0 -0
- /package/dist/{install-DXSNWZKO.js.map → install-OJFXHY6U.js.map} +0 -0
- /package/dist/{src-N3TPKGTY.js.map → pdf-extract-YHE54XU4.js.map} +0 -0
- /package/dist/{src-UVDX5UP6.js.map → src-CG52HEE7.js.map} +0 -0
- /package/dist/{tag-YY6YAABQ.js.map → src-PK5OZXCM.js.map} +0 -0
- /package/dist/{trace-HXIW7U2D.js.map → tag-JSHOMLBR.js.map} +0 -0
- /package/dist/{triage-ORG45R5W.js.map → trace-UMLOSIIR.js.map} +0 -0
- /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-S7CGEK6H.js.map} +0 -0
|
@@ -43,7 +43,21 @@ async function extractPdfTextFromBuffer(buffer) {
|
|
|
43
43
|
}
|
|
44
44
|
const { extractText } = await import("./dist-44PZR7BQ.js");
|
|
45
45
|
const uint8 = new Uint8Array(buffer);
|
|
46
|
-
|
|
46
|
+
let invalidPdf = false;
|
|
47
|
+
const result = await extractText(uint8, { mergePages: true }).catch((err) => {
|
|
48
|
+
if (isInvalidPdfError(err)) {
|
|
49
|
+
invalidPdf = true;
|
|
50
|
+
_logger.warn("[PdfExtract] Invalid PDF structure", {
|
|
51
|
+
sizeBytes: buffer.length,
|
|
52
|
+
error: err instanceof Error ? err.message : String(err)
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
text: "[PDF text extraction failed: the file is not a valid PDF or its structure is corrupted]",
|
|
56
|
+
totalPages: 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
});
|
|
47
61
|
let text = result.text ?? "";
|
|
48
62
|
const totalPages = result.totalPages ?? 0;
|
|
49
63
|
if (text.length > MAX_OUTPUT_CHARS) {
|
|
@@ -56,11 +70,13 @@ async function extractPdfTextFromBuffer(buffer) {
|
|
|
56
70
|
|
|
57
71
|
[Text truncated at ${MAX_OUTPUT_CHARS} characters]`;
|
|
58
72
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
if (!invalidPdf) {
|
|
74
|
+
_logger.info("[PdfExtract] Text extracted successfully", {
|
|
75
|
+
textLength: text.length,
|
|
76
|
+
totalPages,
|
|
77
|
+
sizeBytes: buffer.length
|
|
78
|
+
});
|
|
79
|
+
}
|
|
64
80
|
return { text, totalPages };
|
|
65
81
|
}
|
|
66
82
|
async function extractPdfText(filePath) {
|
|
@@ -84,6 +100,10 @@ function formatBytes(bytes) {
|
|
|
84
100
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
85
101
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
86
102
|
}
|
|
103
|
+
function isInvalidPdfError(err) {
|
|
104
|
+
if (!(err instanceof Error)) return false;
|
|
105
|
+
return err.name === "InvalidPDFException" || err.message.includes("Invalid PDF structure");
|
|
106
|
+
}
|
|
87
107
|
|
|
88
108
|
export {
|
|
89
109
|
consoleLogger,
|
|
@@ -91,4 +111,4 @@ export {
|
|
|
91
111
|
extractPdfTextFromBuffer,
|
|
92
112
|
extractPdfText
|
|
93
113
|
};
|
|
94
|
-
//# sourceMappingURL=chunk-
|
|
114
|
+
//# sourceMappingURL=chunk-KRWRUU6R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../browser-core/src/pdf-extract.ts","../../browser-core/src/logger.ts"],"sourcesContent":["/**\n * PDF text extraction utility.\n *\n * Uses `unpdf` to extract text content from PDF files and buffers.\n * Includes size guards to prevent excessive memory usage.\n *\n * @module pdf-extract\n */\n\nimport * as fs from 'fs/promises';\nimport { consoleLogger, type BrowserLogger } from './logger.js';\n\nlet _logger: BrowserLogger = consoleLogger;\n\nexport function setPdfExtractLogger(l: BrowserLogger): void {\n _logger = l;\n}\n\nconst MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50MB\nconst MAX_OUTPUT_CHARS = 100_000;\n\n/**\n * Extract text content from a PDF buffer.\n * Returns the extracted text and total page count.\n */\nexport async function extractPdfTextFromBuffer(\n buffer: Buffer\n): Promise<{ text: string; totalPages: number }> {\n if (buffer.length > MAX_FILE_SIZE_BYTES) {\n _logger.warn('[PdfExtract] PDF too large for text extraction', {\n sizeBytes: buffer.length,\n maxBytes: MAX_FILE_SIZE_BYTES,\n });\n return {\n text: `[PDF too large for text extraction: ${formatBytes(buffer.length)} exceeds ${formatBytes(MAX_FILE_SIZE_BYTES)} limit]`,\n totalPages: 0,\n };\n }\n\n const { extractText } = await import('unpdf');\n const uint8 = new Uint8Array(buffer);\n let invalidPdf = false;\n const result = await extractText(uint8, { mergePages: true }).catch((err: unknown) => {\n if (isInvalidPdfError(err)) {\n invalidPdf = true;\n _logger.warn('[PdfExtract] Invalid PDF structure', {\n sizeBytes: buffer.length,\n error: err instanceof Error ? err.message : String(err),\n });\n return {\n text:\n '[PDF text extraction failed: the file is not a valid PDF or its structure is corrupted]',\n totalPages: 0,\n };\n }\n throw err;\n });\n\n let text = result.text ?? '';\n const totalPages = result.totalPages ?? 0;\n\n if (text.length > MAX_OUTPUT_CHARS) {\n _logger.info('[PdfExtract] Truncating extracted text', {\n originalLength: text.length,\n truncatedTo: MAX_OUTPUT_CHARS,\n totalPages,\n });\n text = text.slice(0, MAX_OUTPUT_CHARS) + `\\n\\n[Text truncated at ${MAX_OUTPUT_CHARS} characters]`;\n }\n\n if (!invalidPdf) {\n _logger.info('[PdfExtract] Text extracted successfully', {\n textLength: text.length,\n totalPages,\n sizeBytes: buffer.length,\n });\n }\n\n return { text, totalPages };\n}\n\n/**\n * Extract text content from a PDF file on disk.\n */\nexport async function extractPdfText(\n filePath: string\n): Promise<{ text: string; totalPages: number }> {\n const stat = await fs.stat(filePath);\n\n if (stat.size > MAX_FILE_SIZE_BYTES) {\n _logger.warn('[PdfExtract] PDF file too large for text extraction', {\n filePath,\n sizeBytes: stat.size,\n maxBytes: MAX_FILE_SIZE_BYTES,\n });\n return {\n text: `[PDF too large for text extraction: ${formatBytes(stat.size)} exceeds ${formatBytes(MAX_FILE_SIZE_BYTES)} limit]`,\n totalPages: 0,\n };\n }\n\n const buffer = await fs.readFile(filePath);\n return extractPdfTextFromBuffer(Buffer.from(buffer));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\nfunction isInvalidPdfError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n return err.name === 'InvalidPDFException' || err.message.includes('Invalid PDF structure');\n}\n","/**\n * Browser Logger Interface\n *\n * Abstracts logging so browser-core works in any environment:\n * - Cloud (apps/api): Wraps pino logger\n * - CLI (canary-cli): Uses console logger\n *\n * @module logger\n */\n\nexport interface BrowserLogger {\n debug(msg: string, data?: Record<string, unknown>): void;\n info(msg: string, data?: Record<string, unknown>): void;\n warn(msg: string, data?: Record<string, unknown>): void;\n error(msg: string, data?: Record<string, unknown>): void;\n}\n\n/**\n * Default console-based logger. Works everywhere without dependencies.\n */\nexport const consoleLogger: BrowserLogger = {\n debug: (msg, data) => {\n if (data) console.debug(msg, data);\n else console.debug(msg);\n },\n info: (msg, data) => {\n if (data) console.info(msg, data);\n else console.info(msg);\n },\n warn: (msg, data) => {\n if (data) console.warn(msg, data);\n else console.warn(msg);\n },\n error: (msg, data) => {\n if (data) console.error(msg, data);\n else console.error(msg);\n },\n};\n"],"mappings":"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","names":["stat"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
apiRequest
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PRBRSIZP.js";
|
|
5
5
|
import {
|
|
6
6
|
getArgValue,
|
|
7
7
|
hasFlag,
|
|
8
8
|
resolveConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3X3NW7QA.js";
|
|
10
10
|
|
|
11
11
|
// src/triage.ts
|
|
12
12
|
import process from "process";
|
|
@@ -268,4 +268,4 @@ export {
|
|
|
268
268
|
isTriageEnabledForExecution,
|
|
269
269
|
runTriage
|
|
270
270
|
};
|
|
271
|
-
//# sourceMappingURL=chunk-
|
|
271
|
+
//# sourceMappingURL=chunk-NHTEKEYE.js.map
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
VALID_MACRO_NAMES
|
|
4
|
-
} from "./chunk-SJJW77WN.js";
|
|
5
2
|
import {
|
|
6
3
|
apiFetch,
|
|
7
4
|
exitOnFailure,
|
|
@@ -9,17 +6,27 @@ import {
|
|
|
9
6
|
} from "./chunk-3CZU7XCS.js";
|
|
10
7
|
import {
|
|
11
8
|
getArgValue
|
|
12
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3X3NW7QA.js";
|
|
13
10
|
import {
|
|
14
11
|
getCanaryTmpDir
|
|
15
12
|
} from "./chunk-JWHYXEA2.js";
|
|
13
|
+
import {
|
|
14
|
+
VALID_MACRO_NAMES
|
|
15
|
+
} from "./chunk-JYGNMKZ5.js";
|
|
16
16
|
|
|
17
17
|
// src/cli-helpers.ts
|
|
18
18
|
import fs from "fs/promises";
|
|
19
19
|
import path from "path";
|
|
20
20
|
import process from "process";
|
|
21
|
+
var KNOB_LIFECYCLE_STAGES = [
|
|
22
|
+
"active",
|
|
23
|
+
"deprecated",
|
|
24
|
+
"ready_for_cleanup"
|
|
25
|
+
];
|
|
21
26
|
function toLifecycleLabel(stage) {
|
|
22
27
|
switch (stage) {
|
|
28
|
+
case "baking":
|
|
29
|
+
return "baking";
|
|
23
30
|
case "deprecated":
|
|
24
31
|
return "deprecated";
|
|
25
32
|
case "ready_for_cleanup":
|
|
@@ -28,12 +35,10 @@ function toLifecycleLabel(stage) {
|
|
|
28
35
|
return "active";
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
|
-
function parseLifecycleStage(argv) {
|
|
38
|
+
function parseLifecycleStage(argv, allowedStages = ["active", "baking", "deprecated", "ready_for_cleanup"]) {
|
|
32
39
|
const stage = getArgValue(argv, "--stage");
|
|
33
|
-
if (!stage || !
|
|
34
|
-
console.error(
|
|
35
|
-
"Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup"
|
|
36
|
-
);
|
|
40
|
+
if (!stage || !allowedStages.includes(stage)) {
|
|
41
|
+
console.error(`Error: --stage is required and must be one of: ${allowedStages.join(", ")}`);
|
|
37
42
|
process.exit(1);
|
|
38
43
|
}
|
|
39
44
|
return stage;
|
|
@@ -335,6 +340,7 @@ async function fetchList(apiUrl, token, path2, listKey) {
|
|
|
335
340
|
}
|
|
336
341
|
|
|
337
342
|
export {
|
|
343
|
+
KNOB_LIFECYCLE_STAGES,
|
|
338
344
|
toLifecycleLabel,
|
|
339
345
|
parseLifecycleStage,
|
|
340
346
|
apiRequest,
|
|
@@ -350,4 +356,4 @@ export {
|
|
|
350
356
|
uploadStorageState,
|
|
351
357
|
fetchList
|
|
352
358
|
};
|
|
353
|
-
//# sourceMappingURL=chunk-
|
|
359
|
+
//# sourceMappingURL=chunk-PRBRSIZP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-helpers.ts"],"sourcesContent":["/**\n * Shared CLI helpers for superadmin management commands (knobs, feature-flags).\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { getCanaryTmpDir } from '@chatsdet/tmp';\nimport { VALID_MACRO_NAMES } from '@chatsdet/types';\nimport { getArgValue } from './auth.js';\nimport { apiFetch, exitOnFailure, isTerminalFailure } from './api-fetch.js';\n\nexport type LifecycleStage = 'active' | 'baking' | 'deprecated' | 'ready_for_cleanup';\n\n/** Stages knobs may use — `baking` (the oven) is flag-only. */\nexport const KNOB_LIFECYCLE_STAGES: LifecycleStage[] = [\n 'active',\n 'deprecated',\n 'ready_for_cleanup',\n];\n\nexport function toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case 'baking':\n return 'baking';\n case 'deprecated':\n return 'deprecated';\n case 'ready_for_cleanup':\n return 'ready_for_cleanup';\n default:\n return 'active';\n }\n}\n\nexport function parseLifecycleStage(\n argv: string[],\n allowedStages: LifecycleStage[] = ['active', 'baking', 'deprecated', 'ready_for_cleanup']\n): LifecycleStage {\n const stage = getArgValue(argv, '--stage');\n if (!stage || !allowedStages.includes(stage as LifecycleStage)) {\n console.error(`Error: --stage is required and must be one of: ${allowedStages.join(', ')}`);\n process.exit(1);\n }\n return stage as LifecycleStage;\n}\n\nexport async function apiRequest<T extends { ok: boolean; error?: string }>(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>,\n options?: { timeoutMs?: number }\n): Promise<T> {\n const result = await apiFetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n token,\n ...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!result.ok) {\n const { failure } = result;\n // Terminal kinds (auth/proxy/TLS) surface the full diagnostic and exit;\n // http-error falls through so the caller can pretty-print structured\n // user errors (unresolved vars, invalid macros, etc.).\n if (isTerminalFailure(failure)) exitOnFailure(failure);\n const text = failure.body ?? '';\n let errorMessage = failure.message;\n let parsed: Record<string, unknown> | null = null;\n try {\n const data = JSON.parse(text);\n if (data && typeof data === 'object') {\n parsed = data as Record<string, unknown>;\n if (typeof parsed.error === 'string') errorMessage = parsed.error;\n }\n } catch {\n /* keep failure.message */\n }\n return { ...parsed, ok: false, error: errorMessage } as T;\n }\n\n try {\n return (await result.response.json()) as T;\n } catch {\n return { ok: false, error: `Invalid JSON response (${result.response.status})` } as T;\n }\n}\n\ntype UnresolvedRef = {\n nodeId?: string;\n nodeType?: string;\n field?: string;\n variable: string;\n};\n\n/**\n * Render a structured `userError` response from the workflow API into a\n * multi-line, actionable message. Recognizes `WORKFLOW_UNRESOLVED_VARIABLES`\n * and falls back to `✖ {code}: {message}` for other codes. Returns `null` if\n * the response body isn't a recognized structured error.\n */\nexport function renderWorkflowUserError(result: Record<string, unknown>): string | null {\n const code = typeof result.code === 'string' ? result.code : null;\n const message = typeof result.error === 'string' ? result.error : null;\n if (!code) return null;\n\n if (code === 'WORKFLOW_VALIDATION_FAILED') {\n type ValidationIssue = {\n nodeId?: string | null;\n nodeType?: string | null;\n field?: string;\n message?: string;\n severity?: 'error' | 'warning';\n };\n const issues = Array.isArray(result.issues) ? (result.issues as ValidationIssue[]) : [];\n const errors = issues.filter((i) => (i.severity ?? 'error') === 'error');\n const warnings = issues.filter((i) => i.severity === 'warning');\n\n const lines: string[] = [\n `✖ Validation failed (${errors.length} error${errors.length === 1 ? '' : 's'}` +\n (warnings.length > 0\n ? `, ${warnings.length} warning${warnings.length === 1 ? '' : 's'}`\n : '') +\n ')',\n ];\n if (message) lines.push(` ${message}`);\n\n // Group by nodeId (null = flow-level).\n const byNode = new Map<string, ValidationIssue[]>();\n for (const issue of issues) {\n const key = issue.nodeId ?? '__flow__';\n const arr = byNode.get(key) ?? [];\n arr.push(issue);\n byNode.set(key, arr);\n }\n for (const [nodeKey, nodeIssues] of byNode) {\n const heading =\n nodeKey === '__flow__'\n ? 'Flow'\n : `Node ${nodeKey}${nodeIssues[0]?.nodeType ? ` (${nodeIssues[0].nodeType})` : ''}`;\n lines.push(` ${heading}:`);\n for (const issue of nodeIssues) {\n const sev = issue.severity === 'warning' ? '⚠' : '•';\n const field = issue.field ? `${issue.field} — ` : '';\n lines.push(` ${sev} ${field}${issue.message ?? ''}`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_UNRESOLVED_VARIABLES') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const refs = Array.isArray(result.refs) ? (result.refs as UnresolvedRef[]) : [];\n const variables = Array.isArray(result.variables)\n ? (result.variables as string[])\n : refs.map((r) => r.variable);\n\n if (refs.length > 0) {\n lines.push(' Unresolved references:');\n for (const ref of refs) {\n const loc = ref.field && ref.nodeId ? ` (node ${ref.nodeId}, ${ref.field})` : '';\n lines.push(` • $var.${ref.variable}${loc}`);\n }\n } else if (variables.length > 0) {\n lines.push(' Unresolved:');\n for (const v of variables) lines.push(` • $var.${v}`);\n }\n\n const macroHints = variables.filter((v) => VALID_MACRO_NAMES.has(v.toUpperCase()));\n if (macroHints.length > 0) {\n lines.push(' Hint: these look like macro names, not variables:');\n for (const v of macroHints) {\n lines.push(` $var.${v} → $${v.toUpperCase()}()`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_INVALID_INPUT_VALUES') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const issues = Array.isArray(result.issues)\n ? (result.issues as Array<{\n key?: string;\n value?: string;\n reason?: string;\n variables?: string[];\n }>)\n : [];\n for (const issue of issues) {\n const key = issue.key ?? '?';\n const value = issue.value ?? '';\n if (issue.reason === 'self_referential') {\n lines.push(` • ${key}: self-referential → ${value}`);\n lines.push(` Use a literal or point at a different upstream variable.`);\n } else if (issue.reason === 'macro_name_used_as_var') {\n lines.push(` • ${key}: macro name used as $var → ${value}`);\n for (const v of issue.variables ?? []) {\n lines.push(` $var.${v} → $${v.toUpperCase()}()`);\n }\n } else {\n lines.push(` • ${key}: ${issue.reason ?? 'invalid'} → ${value}`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_INVALID_MACRO') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const invalid = Array.isArray(result.invalid) ? (result.invalid as string[]) : [];\n const valid = Array.isArray(result.valid) ? (result.valid as string[]) : [];\n if (invalid.length > 0) {\n lines.push(` Invalid macro(s): ${invalid.map((n) => `$${n}()`).join(', ')}`);\n }\n if (valid.length > 0) {\n lines.push(` Valid macros: ${valid.map((n) => `$${n}()`).join(', ')}`);\n }\n return lines.join('\\n');\n }\n\n return `✖ ${code}${message ? `: ${message}` : ''}`;\n}\n\nexport async function downloadStorageState(opts: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n prefix?: string;\n}): Promise<string | undefined> {\n const tmpFile = path.join(getCanaryTmpDir(), `${opts.prefix ?? 'canary-ss'}-${Date.now()}.json`);\n try {\n const res = await fetch(\n `${opts.apiUrl}/org/properties/${opts.propertyId}/credentials/${opts.credentialId}/storage-state/download`,\n {\n headers: { Authorization: `Bearer ${opts.token}` },\n redirect: 'follow',\n }\n );\n if (res.ok) {\n const body = await res.text();\n await fs.writeFile(tmpFile, body, 'utf-8');\n return tmpFile;\n }\n } catch {\n // Caller handles missing storage state\n }\n return undefined;\n}\n\n/* ── Credential selection ─────────────────────────────────────────────── */\n\nexport interface CredentialListItem {\n id: string;\n name: string;\n propertyId: string;\n propertyName?: string;\n propertyBaseUrl?: string;\n environmentId?: string | null;\n environmentName?: string | null;\n storageStateS3Key?: string | null;\n isDefault?: boolean;\n isEphemeral?: boolean;\n}\n\n/**\n * Fetch org credentials and let the user pick one interactively.\n *\n * @param credentialArg - Credential name or ID to auto-select, or undefined for interactive\n * @returns The selected credential, or null if none found / invalid selection\n */\nexport async function selectCredential(\n apiUrl: string,\n token: string,\n credentialArg?: string\n): Promise<CredentialListItem | null> {\n const credentials = await fetchList<CredentialListItem>(\n apiUrl,\n token,\n '/org/credentials',\n 'items'\n );\n\n if (credentials.length === 0) {\n console.error('No credentials found. Create one first in the web app.');\n return null;\n }\n\n if (credentialArg) {\n const match = credentials.find(\n (c) => c.id === credentialArg || c.name.toLowerCase() === credentialArg.toLowerCase()\n );\n if (!match) {\n console.error(`Credential \"${credentialArg}\" not found.`);\n console.error('Available credentials:');\n for (const c of credentials) {\n const env = c.environmentName ? ` — ${c.environmentName}` : '';\n const baseUrl = c.propertyBaseUrl ? ` — ${c.propertyBaseUrl}` : '';\n console.error(` - ${c.name} [${c.propertyName ?? c.propertyId}${env}${baseUrl}]`);\n }\n return null;\n }\n return match;\n }\n\n // Interactive selection\n console.log('\\nSelect a credential:\\n');\n const labels = credentials.map((c) => {\n const property = c.propertyName ?? c.propertyId;\n const env = c.environmentName ? ` — ${c.environmentName}` : '';\n const baseUrl = c.propertyBaseUrl ? ` — ${c.propertyBaseUrl}` : '';\n return `${c.name} [${property}${env}${baseUrl}]`;\n });\n\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n for (let i = 0; i < labels.length; i++) {\n console.log(` ${i + 1}. ${labels[i]}`);\n }\n rl.question('\\nEnter number: ', (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= credentials.length) {\n console.error('Invalid selection.');\n return null;\n }\n\n return credentials[idx];\n}\n\n/* ── Property selection ──────────────────────────────────────────────── */\n\ninterface PropertyListItem {\n id: string;\n name: string;\n baseUrl: string;\n}\n\nexport async function fetchProperties(apiUrl: string, token: string): Promise<PropertyListItem[]> {\n return fetchList<PropertyListItem>(apiUrl, token, '/org/properties', 'items');\n}\n\n/**\n * Read the application/property selector flag. `--application` is the user-facing\n * name; `--property` is kept as a backwards-compat alias (read second). Internally\n * this is still a \"property\" — the rename is cosmetic.\n */\nexport function getApplicationArg(argv: string[]): string | undefined {\n return getArgValue(argv, '--application') ?? getArgValue(argv, '--property');\n}\n\n/**\n * Read the application/property UUID flag. `--application-id` is the user-facing\n * name; `--property-id` is kept as a backwards-compat alias.\n */\nexport function getApplicationIdArg(argv: string[]): string | undefined {\n return getArgValue(argv, '--application-id') ?? getArgValue(argv, '--property-id');\n}\n\nexport async function selectProperty(\n apiUrl: string,\n token: string,\n hint?: string\n): Promise<PropertyListItem | null> {\n const properties = await fetchProperties(apiUrl, token);\n\n if (properties.length === 0) {\n console.error('No applications found. Create one first in the web app.');\n return null;\n }\n\n if (hint) {\n const match = properties.find(\n (p) => p.id === hint || p.name.toLowerCase() === hint.toLowerCase()\n );\n if (!match) {\n console.error(`Application \"${hint}\" not found.`);\n console.error('Available applications:');\n for (const p of properties) {\n console.error(` - ${p.name} (${p.baseUrl})`);\n }\n return null;\n }\n return match;\n }\n\n if (properties.length === 1) {\n console.log(`Auto-selected application: ${properties[0].name}`);\n return properties[0];\n }\n\n return promptChoice(\n 'Select an application:',\n properties.map((p) => ({ label: `${p.name} — ${p.baseUrl}`, value: p }))\n );\n}\n\n/* ── Interactive prompt helpers ──────────────────────────────────────── */\n\nexport async function promptInput(question: string, defaultValue?: string): Promise<string> {\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;\n const answer = await new Promise<string>((resolve) => {\n rl.question(prompt, (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n return answer || defaultValue || '';\n}\n\nexport async function promptChoice<T>(\n question: string,\n options: Array<{ label: string; value: T }>\n): Promise<T | null> {\n console.log(`\\n${question}\\n`);\n for (let i = 0; i < options.length; i++) {\n console.log(` ${i + 1}. ${options[i].label}`);\n }\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n rl.question('\\nEnter number: ', (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= options.length) {\n console.error('Invalid selection.');\n return null;\n }\n return options[idx].value;\n}\n\nexport async function uploadStorageState(opts: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n storageState: unknown;\n}): Promise<{ ok: boolean; s3Key?: string; error?: string }> {\n const result = await apiFetch(\n `${opts.apiUrl}/org/properties/${opts.propertyId}/credentials/${opts.credentialId}/storage-state/upload`,\n {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${opts.token}`,\n 'Content-Type': 'application/json',\n },\n token: opts.token,\n body: JSON.stringify({ storageState: opts.storageState }),\n }\n );\n\n if (!result.ok) exitOnFailure(result.failure);\n\n return (await result.response.json()) as { ok: boolean; s3Key?: string; error?: string };\n}\n\nexport async function fetchList<T>(\n apiUrl: string,\n token: string,\n path: string,\n listKey: string\n): Promise<T[]> {\n const result = await apiFetch(`${apiUrl}${path}`, {\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n\n if (!result.ok) exitOnFailure(result.failure);\n\n const json = (await result.response.json()) as Record<string, unknown>;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return (json[listKey] as T[]) ?? [];\n}\n"],"mappings":"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","names":["path"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
isTriageEnabledForExecution
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NHTEKEYE.js";
|
|
5
5
|
import {
|
|
6
6
|
resolveConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3X3NW7QA.js";
|
|
8
8
|
|
|
9
9
|
// src/remote-test.ts
|
|
10
10
|
import process from "process";
|
|
@@ -26,6 +26,21 @@ function formatFailedTests(failedTests, appUrl) {
|
|
|
26
26
|
}
|
|
27
27
|
return lines.join("\n");
|
|
28
28
|
}
|
|
29
|
+
function formatStreamReadFailure(error) {
|
|
30
|
+
const detail = error instanceof Error && error.message.trim() ? ` (${error.message})` : "";
|
|
31
|
+
return [
|
|
32
|
+
`Test result stream ended unexpectedly${detail}.`,
|
|
33
|
+
"The test suite may still be running in Canary.",
|
|
34
|
+
"Re-run the command or check the test suite in the app for final status."
|
|
35
|
+
].join(" ");
|
|
36
|
+
}
|
|
37
|
+
function formatStreamClosedBeforeCompletion() {
|
|
38
|
+
return [
|
|
39
|
+
"Test result stream ended before the suite completed.",
|
|
40
|
+
"The test suite may still be running in Canary.",
|
|
41
|
+
"Re-run the command or check the test suite in the app for final status."
|
|
42
|
+
].join(" ");
|
|
43
|
+
}
|
|
29
44
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
30
45
|
async function resolveTestConfig(apiUrl, token, propertyArg, environmentArg) {
|
|
31
46
|
if (!propertyArg && !environmentArg) return {};
|
|
@@ -97,13 +112,75 @@ ${available}`
|
|
|
97
112
|
}
|
|
98
113
|
return result;
|
|
99
114
|
}
|
|
115
|
+
async function triggerNamedSuiteRun(apiUrl, token, suiteArg, environmentId, environmentMode, verbose) {
|
|
116
|
+
const authHeaders = {
|
|
117
|
+
Authorization: `Bearer ${token}`,
|
|
118
|
+
"Content-Type": "application/json"
|
|
119
|
+
};
|
|
120
|
+
const listRes = await fetch(`${apiUrl}/test-suites`, { headers: authHeaders });
|
|
121
|
+
if (!listRes.ok) {
|
|
122
|
+
console.error(`Failed to list test suites: ${listRes.status}`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
const listJson = await listRes.json();
|
|
126
|
+
const suites = listJson.data ?? [];
|
|
127
|
+
const match = suites.find((s) => s.id === suiteArg) ?? suites.find((s) => s.name.toLowerCase() === suiteArg.toLowerCase());
|
|
128
|
+
if (!match) {
|
|
129
|
+
console.error(`Suite not found: "${suiteArg}"`);
|
|
130
|
+
if (suites.length > 0) {
|
|
131
|
+
console.error("Available suites:");
|
|
132
|
+
for (const s of suites) console.error(` - ${s.name} (${s.id})`);
|
|
133
|
+
}
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
console.log(`Starting test suite: ${match.name}`);
|
|
137
|
+
console.log(` Env mode: ${environmentMode}`);
|
|
138
|
+
if (environmentMode === "force" && environmentId) console.log(` Environment: ${environmentId}`);
|
|
139
|
+
console.log("");
|
|
140
|
+
const runBody = {
|
|
141
|
+
environmentMode
|
|
142
|
+
};
|
|
143
|
+
if (environmentMode === "force" && environmentId) runBody.environmentId = environmentId;
|
|
144
|
+
const runRes = await fetch(`${apiUrl}/test-suites/${match.id}/run`, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: authHeaders,
|
|
147
|
+
body: JSON.stringify(runBody)
|
|
148
|
+
});
|
|
149
|
+
if (!runRes.ok) {
|
|
150
|
+
console.error(`Failed to start suite: ${runRes.status}`);
|
|
151
|
+
console.error(await runRes.text());
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
const runJson = await runRes.json();
|
|
155
|
+
if (!runJson.ok || !runJson.data) {
|
|
156
|
+
console.error(`Failed to start suite: ${runJson.error ?? "Unknown error"}`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
if (verbose) {
|
|
160
|
+
console.log(`Execution ID: ${runJson.data.executionId}`);
|
|
161
|
+
console.log(`Job ID: ${runJson.data.jobId}`);
|
|
162
|
+
console.log("");
|
|
163
|
+
}
|
|
164
|
+
return runJson.data;
|
|
165
|
+
}
|
|
100
166
|
async function runRemoteTest(argv) {
|
|
101
167
|
const { apiUrl, token } = await resolveConfig(argv);
|
|
102
168
|
const tag = getArgValue(argv, "--tag");
|
|
103
169
|
const namePattern = getArgValue(argv, "--name-pattern");
|
|
104
170
|
const propertyArg = getArgValue(argv, "--application") ?? getArgValue(argv, "--property");
|
|
105
171
|
const environmentArg = getArgValue(argv, "--environment");
|
|
172
|
+
const suiteArg = getArgValue(argv, "--suite");
|
|
106
173
|
const verbose = hasFlag(argv, "--verbose", "-v");
|
|
174
|
+
const envModeArg = getArgValue(argv, "--env-mode");
|
|
175
|
+
if (envModeArg !== void 0 && envModeArg !== "mixed" && envModeArg !== "force") {
|
|
176
|
+
console.error(`Error: --env-mode must be "mixed" or "force" (got "${envModeArg}").`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const environmentMode = envModeArg ?? (environmentArg ? "force" : "mixed");
|
|
180
|
+
if (environmentMode === "force" && !environmentArg) {
|
|
181
|
+
console.error("Error: --env-mode force requires --environment <name|id>.");
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
107
184
|
let resolvedConfig = {};
|
|
108
185
|
if (propertyArg || environmentArg) {
|
|
109
186
|
try {
|
|
@@ -119,39 +196,64 @@ async function runRemoteTest(argv) {
|
|
|
119
196
|
if (tag) console.log(` Filtering by tag: ${tag}`);
|
|
120
197
|
if (namePattern) console.log(` Filtering by name pattern: ${namePattern}`);
|
|
121
198
|
console.log("");
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
console.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
199
|
+
let suiteId;
|
|
200
|
+
let jobId;
|
|
201
|
+
let appUrl;
|
|
202
|
+
if (suiteArg) {
|
|
203
|
+
const triggered = await triggerNamedSuiteRun(
|
|
204
|
+
apiUrl,
|
|
205
|
+
token,
|
|
206
|
+
suiteArg,
|
|
207
|
+
resolvedConfig.environmentId,
|
|
208
|
+
environmentMode,
|
|
209
|
+
verbose
|
|
210
|
+
);
|
|
211
|
+
suiteId = triggered.executionId;
|
|
212
|
+
jobId = triggered.jobId;
|
|
213
|
+
} else {
|
|
214
|
+
console.log("Starting remote workflow tests...");
|
|
215
|
+
if (resolvedConfig.propertyId) console.log(` Property: ${propertyArg}`);
|
|
216
|
+
if (resolvedConfig.environmentId) console.log(` Environment: ${environmentArg}`);
|
|
217
|
+
if (tag) console.log(` Filtering by tag: ${tag}`);
|
|
218
|
+
if (namePattern) console.log(` Filtering by name pattern: ${namePattern}`);
|
|
219
|
+
console.log("");
|
|
220
|
+
const queryParams = new URLSearchParams();
|
|
221
|
+
if (tag) queryParams.set("tag", tag);
|
|
222
|
+
if (namePattern) queryParams.set("namePattern", namePattern);
|
|
223
|
+
const triggerUrl = `${apiUrl}/workflows/test-runs${queryParams.toString() ? `?${queryParams}` : ""}`;
|
|
224
|
+
const body = { environmentMode };
|
|
225
|
+
if (resolvedConfig.propertyId) body.propertyId = resolvedConfig.propertyId;
|
|
226
|
+
if (environmentMode === "force" && resolvedConfig.environmentId)
|
|
227
|
+
body.environmentId = resolvedConfig.environmentId;
|
|
228
|
+
let triggerRes;
|
|
229
|
+
try {
|
|
230
|
+
triggerRes = await fetch(triggerUrl, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: `Bearer ${token}`,
|
|
234
|
+
"Content-Type": "application/json"
|
|
235
|
+
},
|
|
236
|
+
body: JSON.stringify(body)
|
|
237
|
+
});
|
|
238
|
+
} catch (err) {
|
|
239
|
+
console.error(`Failed to connect to API: ${err}`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
if (!triggerRes.ok) {
|
|
243
|
+
const errorText = await triggerRes.text();
|
|
244
|
+
console.error(`Failed to start tests: ${triggerRes.status}`);
|
|
245
|
+
console.error(errorText);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
const triggerData = await triggerRes.json();
|
|
249
|
+
if (!triggerData.ok || !triggerData.suiteId) {
|
|
250
|
+
console.error(`Failed to start tests: ${triggerData.error ?? "Unknown error"}`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
suiteId = triggerData.suiteId;
|
|
254
|
+
jobId = triggerData.jobId;
|
|
255
|
+
appUrl = triggerData.appUrl;
|
|
153
256
|
}
|
|
154
|
-
const { suiteId, jobId, appUrl } = triggerData;
|
|
155
257
|
if (verbose) {
|
|
156
258
|
console.log(`Suite ID: ${suiteId}`);
|
|
157
259
|
console.log(`Job ID: ${jobId}`);
|
|
@@ -182,6 +284,7 @@ async function runRemoteTest(argv) {
|
|
|
182
284
|
let completedWorkflows = 0;
|
|
183
285
|
let failedWorkflows = 0;
|
|
184
286
|
let successfulWorkflows = 0;
|
|
287
|
+
let streamInterrupted = false;
|
|
185
288
|
const parser = createParser({
|
|
186
289
|
onEvent: (event) => {
|
|
187
290
|
if (!event.data) return;
|
|
@@ -238,9 +341,20 @@ async function runRemoteTest(argv) {
|
|
|
238
341
|
try {
|
|
239
342
|
while (!hasCompleted) {
|
|
240
343
|
const { done, value } = await reader.read();
|
|
241
|
-
if (done)
|
|
344
|
+
if (done) {
|
|
345
|
+
if (!hasCompleted) {
|
|
346
|
+
streamInterrupted = true;
|
|
347
|
+
exitCode = 1;
|
|
348
|
+
console.error(formatStreamClosedBeforeCompletion());
|
|
349
|
+
}
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
242
352
|
parser.feed(decoder.decode(value, { stream: true }));
|
|
243
353
|
}
|
|
354
|
+
} catch (err) {
|
|
355
|
+
streamInterrupted = true;
|
|
356
|
+
exitCode = 1;
|
|
357
|
+
console.error(formatStreamReadFailure(err));
|
|
244
358
|
} finally {
|
|
245
359
|
reader.releaseLock();
|
|
246
360
|
}
|
|
@@ -248,7 +362,7 @@ async function runRemoteTest(argv) {
|
|
|
248
362
|
console.log("\u2500".repeat(50));
|
|
249
363
|
if (totalWorkflows === 0) {
|
|
250
364
|
console.log("No workflows found matching the filter criteria.");
|
|
251
|
-
process.exit(0);
|
|
365
|
+
process.exit(streamInterrupted ? 1 : 0);
|
|
252
366
|
}
|
|
253
367
|
const passRate = totalWorkflows > 0 ? Math.round(successfulWorkflows / totalWorkflows * 100) : 0;
|
|
254
368
|
if (failedWorkflows > 0) {
|
|
@@ -256,6 +370,9 @@ async function runRemoteTest(argv) {
|
|
|
256
370
|
exitCode = 1;
|
|
257
371
|
} else {
|
|
258
372
|
console.log(`PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`);
|
|
373
|
+
if (!streamInterrupted) {
|
|
374
|
+
exitCode = 0;
|
|
375
|
+
}
|
|
259
376
|
}
|
|
260
377
|
const waitingWorkflows = totalWorkflows - completedWorkflows;
|
|
261
378
|
if (waitingWorkflows > 0) {
|
|
@@ -278,4 +395,4 @@ async function runRemoteTest(argv) {
|
|
|
278
395
|
export {
|
|
279
396
|
runRemoteTest
|
|
280
397
|
};
|
|
281
|
-
//# sourceMappingURL=chunk-
|
|
398
|
+
//# sourceMappingURL=chunk-UETBLPKM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/remote-test.ts"],"sourcesContent":["/**\n * Remote Test Command\n *\n * Triggers workflow tests on the Canary platform and streams results via SSE.\n * Designed for CI/CD pipelines - exits with code 0 on success, 1 on failure.\n *\n * Usage:\n * canary test-suite run [options]\n *\n * Options:\n * --token <key> API key (or set CANARY_API_TOKEN env var)\n * --api-url <url> API URL (default: https://api.trycanary.ai)\n * --tag <tag> Filter workflows by tag\n * --name-pattern <pat> Filter workflows by name pattern\n * --verbose, -v Show all events (not just results)\n */\n\nimport process from \"node:process\";\nimport { createParser, type EventSourceMessage } from \"eventsource-parser\";\nimport { resolveConfig } from \"./auth\";\nimport { isTriageEnabledForExecution } from \"./triage.js\";\n\ntype WorkflowTestStatus = \"queued\" | \"running\" | \"waiting\" | \"success\" | \"failed\";\n\ntype WorkflowTestEvent = {\n type: \"workflow-test\";\n suiteId: string;\n workflowId: string;\n testRunId: string;\n workflowRunId: string | null;\n status: WorkflowTestStatus;\n startedAt: string | null;\n finishedAt: string | null;\n durationMs: number | null;\n errorMessage?: string | null;\n linkedIssueId?: string | null;\n message?: string;\n resumeAt?: string | null;\n segmentIndex?: number | null;\n totalSegments?: number | null;\n};\n\ntype WorkflowTestSuiteEvent = {\n type: \"workflow-test-suite\";\n suiteId: string;\n status: \"running\" | \"completed\";\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n totalWorkflows: number;\n completedWorkflows: number;\n failedWorkflows: number;\n successfulWorkflows: number;\n errorMessage?: string | null;\n message?: string;\n};\n\ntype TriggerResponse = {\n ok: boolean;\n jobId?: string;\n suiteId?: string;\n startedAt?: string;\n appUrl?: string;\n error?: string;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nfunction buildQueryParams(tag?: string, namePattern?: string): string {\n const params = new URLSearchParams();\n if (tag) params.set(\"tag\", tag);\n if (namePattern) params.set(\"namePattern\", namePattern);\n return params.toString();\n}\n\nfunction extractWorkflowName(message: string | undefined, workflowId: string): string {\n if (!message) return workflowId;\n const match = message.match(/^Flow \"(.+?)\" /);\n return match ? match[1] : workflowId;\n}\n\ntype FailedTest = { name: string; testRunId: string };\n\nfunction formatFailedTests(\n failedTests: FailedTest[],\n appUrl: string | undefined,\n): string | null {\n if (failedTests.length === 0 || !appUrl) return null;\n\n const lines = [\"\", \"Failed tests:\"];\n for (const t of failedTests) {\n lines.push(` \\u2717 ${t.name}`);\n lines.push(` ${appUrl}/runs/tests/${t.testRunId}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction formatSummary(stats: {\n totalWorkflows: number;\n successfulWorkflows: number;\n failedWorkflows: number;\n completedWorkflows: number;\n}): { message: string; exitCode: number } {\n const { totalWorkflows, successfulWorkflows, failedWorkflows, completedWorkflows } = stats;\n\n if (totalWorkflows === 0) {\n return { message: \"No workflows found matching the filter criteria.\", exitCode: 0 };\n }\n\n const passRate = Math.round((successfulWorkflows / totalWorkflows) * 100);\n const waitingWorkflows = totalWorkflows - completedWorkflows;\n\n let message: string;\n let exitCode: number;\n\n if (failedWorkflows > 0) {\n message = `FAILED: ${failedWorkflows} of ${totalWorkflows} workflows failed (${passRate}% pass rate)`;\n exitCode = 1;\n } else {\n message = `PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`;\n exitCode = 0;\n }\n\n if (waitingWorkflows > 0) {\n message += `\\nNote: ${waitingWorkflows} workflow(s) are still waiting (scheduled for later)`;\n }\n\n return { message, exitCode };\n}\n\nfunction formatStreamReadFailure(error: unknown): string {\n const detail = error instanceof Error && error.message.trim() ? ` (${error.message})` : \"\";\n return [\n `Test result stream ended unexpectedly${detail}.`,\n \"The test suite may still be running in Canary.\",\n \"Re-run the command or check the test suite in the app for final status.\",\n ].join(\" \");\n}\n\nfunction formatStreamClosedBeforeCompletion(): string {\n return [\n \"Test result stream ended before the suite completed.\",\n \"The test suite may still be running in Canary.\",\n \"Re-run the command or check the test suite in the app for final status.\",\n ].join(\" \");\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ntype TestConfigProperty = {\n id: string;\n name: string;\n baseUrl: string;\n environments: Array<{ id: string; name: string; url: string; isDefault: boolean }>;\n};\n\ntype TestConfigResponse = {\n ok: boolean;\n properties: TestConfigProperty[];\n error?: string;\n};\n\ntype ResolvedConfig = {\n propertyId?: string;\n environmentId?: string;\n};\n\nasync function resolveTestConfig(\n apiUrl: string,\n token: string,\n propertyArg?: string,\n environmentArg?: string,\n): Promise<ResolvedConfig> {\n if (!propertyArg && !environmentArg) return {};\n\n const res = await fetch(`${apiUrl}/workflows/test-config`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Failed to fetch test config: ${res.status} ${text}`);\n }\n\n const data = (await res.json()) as TestConfigResponse;\n if (!data.ok || !data.properties) {\n throw new Error(`Failed to fetch test config: ${data.error ?? \"Unknown error\"}`);\n }\n\n const { properties } = data;\n let property: TestConfigProperty | undefined;\n\n if (propertyArg) {\n if (UUID_RE.test(propertyArg)) {\n property = properties.find((p) => p.id === propertyArg);\n } else {\n property = properties.find(\n (p) => p.name.toLowerCase() === propertyArg.toLowerCase()\n );\n }\n if (!property) {\n const available = properties.map((p) => ` - ${p.name} (${p.id})`).join(\"\\n\");\n throw new Error(\n `Application \"${propertyArg}\" not found.\\nAvailable applications:\\n${available}`\n );\n }\n } else if (environmentArg) {\n // --environment without --application: auto-resolve if single application\n if (properties.length === 0) {\n throw new Error(\"No applications found in your organization.\");\n }\n if (properties.length > 1) {\n const available = properties.map((p) => ` - ${p.name} (${p.id})`).join(\"\\n\");\n throw new Error(\n `--environment requires --application when multiple applications exist.\\nAvailable applications:\\n${available}`\n );\n }\n property = properties[0];\n }\n\n const result: ResolvedConfig = {};\n if (property) {\n result.propertyId = property.id;\n\n if (environmentArg) {\n let env: TestConfigProperty[\"environments\"][number] | undefined;\n if (UUID_RE.test(environmentArg)) {\n env = property.environments.find((e) => e.id === environmentArg);\n } else {\n env = property.environments.find(\n (e) => e.name.toLowerCase() === environmentArg.toLowerCase()\n );\n }\n if (!env) {\n const available = property.environments\n .map((e) => ` - ${e.name}${e.isDefault ? \" (default)\" : \"\"} (${e.id})`)\n .join(\"\\n\");\n throw new Error(\n `Environment \"${environmentArg}\" not found for application \"${property.name}\".\\nAvailable environments:\\n${available}`\n );\n }\n result.environmentId = env.id;\n }\n }\n\n return result;\n}\n\n// Export internals for testing\nexport const __internals = {\n getArgValue,\n hasFlag,\n buildQueryParams,\n extractWorkflowName,\n formatSummary,\n formatFailedTests,\n formatStreamReadFailure,\n formatStreamClosedBeforeCompletion,\n resolveTestConfig,\n UUID_RE,\n};\n\ntype SuiteListItem = { id: string; name: string };\n\n/**\n * Resolve a `--suite <name|id>` argument to a suite id and trigger a layered\n * run via POST /test-suites/:id/run. Returns the execution id (used as the SSE\n * stream key) and the orchestrator job id.\n */\nasync function triggerNamedSuiteRun(\n apiUrl: string,\n token: string,\n suiteArg: string,\n environmentId: string | undefined,\n environmentMode: \"mixed\" | \"force\",\n verbose: boolean\n): Promise<{ executionId: string; jobId: string }> {\n const authHeaders = {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n };\n\n // Resolve name → id (exact id is accepted as-is; otherwise match by name).\n const listRes = await fetch(`${apiUrl}/test-suites`, { headers: authHeaders });\n if (!listRes.ok) {\n console.error(`Failed to list test suites: ${listRes.status}`);\n process.exit(1);\n }\n const listJson = (await listRes.json()) as { ok: boolean; data?: SuiteListItem[] };\n const suites = listJson.data ?? [];\n const match =\n suites.find((s) => s.id === suiteArg) ??\n suites.find((s) => s.name.toLowerCase() === suiteArg.toLowerCase());\n if (!match) {\n console.error(`Suite not found: \"${suiteArg}\"`);\n if (suites.length > 0) {\n console.error(\"Available suites:\");\n for (const s of suites) console.error(` - ${s.name} (${s.id})`);\n }\n process.exit(1);\n }\n\n console.log(`Starting test suite: ${match.name}`);\n console.log(` Env mode: ${environmentMode}`);\n if (environmentMode === \"force\" && environmentId) console.log(` Environment: ${environmentId}`);\n console.log(\"\");\n\n const runBody: { environmentId?: string; environmentMode: \"mixed\" | \"force\" } = {\n environmentMode,\n };\n if (environmentMode === \"force\" && environmentId) runBody.environmentId = environmentId;\n const runRes = await fetch(`${apiUrl}/test-suites/${match.id}/run`, {\n method: \"POST\",\n headers: authHeaders,\n body: JSON.stringify(runBody),\n });\n if (!runRes.ok) {\n console.error(`Failed to start suite: ${runRes.status}`);\n console.error(await runRes.text());\n process.exit(1);\n }\n const runJson = (await runRes.json()) as {\n ok: boolean;\n data?: { executionId: string; jobId: string };\n error?: string;\n };\n if (!runJson.ok || !runJson.data) {\n console.error(`Failed to start suite: ${runJson.error ?? \"Unknown error\"}`);\n process.exit(1);\n }\n if (verbose) {\n console.log(`Execution ID: ${runJson.data.executionId}`);\n console.log(`Job ID: ${runJson.data.jobId}`);\n console.log(\"\");\n }\n return runJson.data;\n}\n\nexport async function runRemoteTest(argv: string[]): Promise<void> {\n const { apiUrl, token } = await resolveConfig(argv);\n\n const tag = getArgValue(argv, \"--tag\");\n const namePattern = getArgValue(argv, \"--name-pattern\");\n // \"--application\" is the user-facing flag; \"--property\" stays as a backwards-compat alias.\n const propertyArg = getArgValue(argv, \"--application\") ?? getArgValue(argv, \"--property\");\n const environmentArg = getArgValue(argv, \"--environment\");\n const suiteArg = getArgValue(argv, \"--suite\");\n const verbose = hasFlag(argv, \"--verbose\", \"-v\");\n\n // Env mode: 'mixed' (default) runs each flow on its own preferred env; 'force'\n // pins the whole suite to --environment (which then becomes required).\n const envModeArg = getArgValue(argv, \"--env-mode\");\n if (envModeArg !== undefined && envModeArg !== \"mixed\" && envModeArg !== \"force\") {\n console.error(`Error: --env-mode must be \"mixed\" or \"force\" (got \"${envModeArg}\").`);\n process.exit(1);\n }\n const environmentMode: \"mixed\" | \"force\" =\n (envModeArg as \"mixed\" | \"force\" | undefined) ?? (environmentArg ? \"force\" : \"mixed\");\n if (environmentMode === \"force\" && !environmentArg) {\n console.error(\"Error: --env-mode force requires --environment <name|id>.\");\n process.exit(1);\n }\n\n // 0. Resolve property/environment if specified\n let resolvedConfig: ResolvedConfig = {};\n if (propertyArg || environmentArg) {\n try {\n resolvedConfig = await resolveTestConfig(apiUrl, token, propertyArg, environmentArg);\n } catch (err) {\n console.error(`${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n }\n\n console.log(\"Starting remote workflow tests...\");\n if (resolvedConfig.propertyId) console.log(` Application: ${propertyArg}`);\n if (resolvedConfig.environmentId) console.log(` Environment: ${environmentArg}`);\n if (tag) console.log(` Filtering by tag: ${tag}`);\n if (namePattern) console.log(` Filtering by name pattern: ${namePattern}`);\n console.log(\"\");\n\n let suiteId: string;\n let jobId: string | undefined;\n let appUrl: string | undefined;\n\n if (suiteArg) {\n // Named, layered suite run. Resolve name→id, then POST /test-suites/:id/run.\n const triggered = await triggerNamedSuiteRun(\n apiUrl,\n token,\n suiteArg,\n resolvedConfig.environmentId,\n environmentMode,\n verbose\n );\n suiteId = triggered.executionId;\n jobId = triggered.jobId;\n } else {\n console.log(\"Starting remote workflow tests...\");\n if (resolvedConfig.propertyId) console.log(` Property: ${propertyArg}`);\n if (resolvedConfig.environmentId) console.log(` Environment: ${environmentArg}`);\n if (tag) console.log(` Filtering by tag: ${tag}`);\n if (namePattern) console.log(` Filtering by name pattern: ${namePattern}`);\n console.log(\"\");\n\n // 1. Trigger test run\n const queryParams = new URLSearchParams();\n if (tag) queryParams.set(\"tag\", tag);\n if (namePattern) queryParams.set(\"namePattern\", namePattern);\n\n const triggerUrl = `${apiUrl}/workflows/test-runs${queryParams.toString() ? `?${queryParams}` : \"\"}`;\n\n const body: Record<string, string> = { environmentMode };\n if (resolvedConfig.propertyId) body.propertyId = resolvedConfig.propertyId;\n if (environmentMode === \"force\" && resolvedConfig.environmentId)\n body.environmentId = resolvedConfig.environmentId;\n\n let triggerRes: Response;\n try {\n triggerRes = await fetch(triggerUrl, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n } catch (err) {\n console.error(`Failed to connect to API: ${err}`);\n process.exit(1);\n }\n\n if (!triggerRes.ok) {\n const errorText = await triggerRes.text();\n console.error(`Failed to start tests: ${triggerRes.status}`);\n console.error(errorText);\n process.exit(1);\n }\n\n const triggerData = (await triggerRes.json()) as TriggerResponse;\n if (!triggerData.ok || !triggerData.suiteId) {\n console.error(`Failed to start tests: ${triggerData.error ?? \"Unknown error\"}`);\n process.exit(1);\n }\n suiteId = triggerData.suiteId;\n jobId = triggerData.jobId;\n appUrl = triggerData.appUrl;\n }\n\n if (verbose) {\n console.log(`Suite ID: ${suiteId}`);\n console.log(`Job ID: ${jobId}`);\n console.log(\"\");\n }\n\n // 2. Subscribe to SSE stream\n const streamUrl = `${apiUrl}/workflows/test-runs/stream?suiteId=${suiteId}`;\n\n let streamRes: Response;\n try {\n streamRes = await fetch(streamUrl, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"text/event-stream\",\n },\n });\n } catch (err) {\n console.error(`Failed to connect to event stream: ${err}`);\n process.exit(1);\n }\n\n if (!streamRes.ok || !streamRes.body) {\n console.error(`Failed to connect to event stream: ${streamRes.status}`);\n process.exit(1);\n }\n\n // Track state\n let exitCode = 0;\n let hasCompleted = false;\n const workflowNames = new Map<string, string>();\n const failedTests: FailedTest[] = [];\n let totalWorkflows = 0;\n let completedWorkflows = 0;\n let failedWorkflows = 0;\n let successfulWorkflows = 0;\n let streamInterrupted = false;\n\n // 3. Parse SSE events\n const parser = createParser({\n onEvent: (event: EventSourceMessage) => {\n if (!event.data) return;\n\n try {\n const data = JSON.parse(event.data) as\n | WorkflowTestEvent\n | WorkflowTestSuiteEvent\n | { error?: string };\n\n if (verbose) {\n console.log(`[${event.event}] ${JSON.stringify(data)}`);\n }\n\n if (event.event === \"workflow-test\") {\n const testEvent = data as WorkflowTestEvent;\n const { status, workflowId, message, errorMessage } = testEvent;\n const name = workflowNames.get(workflowId) || message?.replace(/^Flow \"(.+)\" .*$/, \"$1\") || workflowId;\n\n if (message?.startsWith('Flow \"')) {\n const match = message.match(/^Flow \"(.+?)\" /);\n if (match) workflowNames.set(workflowId, match[1]);\n }\n\n if (!verbose) {\n if (status === \"success\") {\n console.log(` \\u2713 ${name}`);\n } else if (status === \"failed\") {\n console.log(` \\u2717 ${name}`);\n if (errorMessage) {\n console.log(` Error: ${errorMessage.slice(0, 200)}`);\n }\n failedTests.push({ name, testRunId: testEvent.testRunId });\n exitCode = 1;\n } else if (status === \"running\") {\n // Don't log running status in non-verbose mode\n } else if (status === \"waiting\") {\n console.log(` \\u23F3 ${name} (waiting for scheduled time)`);\n }\n }\n }\n\n if (event.event === \"workflow-test-suite\") {\n const suiteEvent = data as WorkflowTestSuiteEvent;\n totalWorkflows = suiteEvent.totalWorkflows;\n completedWorkflows = suiteEvent.completedWorkflows;\n failedWorkflows = suiteEvent.failedWorkflows;\n successfulWorkflows = suiteEvent.successfulWorkflows;\n\n if (suiteEvent.status === \"completed\") {\n hasCompleted = true;\n }\n }\n\n if (event.event === \"error\") {\n const errorData = data as { error?: string };\n console.error(`Stream error: ${errorData.error ?? \"Unknown error\"}`);\n exitCode = 1;\n }\n } catch {\n // Ignore parse errors for keepalive, etc.\n }\n },\n });\n\n const reader = streamRes.body.getReader();\n const decoder = new TextDecoder();\n\n try {\n while (!hasCompleted) {\n const { done, value } = await reader.read();\n if (done) {\n if (!hasCompleted) {\n streamInterrupted = true;\n exitCode = 1;\n console.error(formatStreamClosedBeforeCompletion());\n }\n break;\n }\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } catch (err) {\n streamInterrupted = true;\n exitCode = 1;\n console.error(formatStreamReadFailure(err));\n } finally {\n reader.releaseLock();\n }\n\n // 4. Print summary\n console.log(\"\");\n console.log(\"─\".repeat(50));\n\n if (totalWorkflows === 0) {\n console.log(\"No workflows found matching the filter criteria.\");\n process.exit(streamInterrupted ? 1 : 0);\n }\n\n const passRate = totalWorkflows > 0 ? Math.round((successfulWorkflows / totalWorkflows) * 100) : 0;\n\n if (failedWorkflows > 0) {\n console.log(`FAILED: ${failedWorkflows} of ${totalWorkflows} workflows failed (${passRate}% pass rate)`);\n exitCode = 1;\n } else {\n console.log(`PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`);\n if (!streamInterrupted) {\n exitCode = 0;\n }\n }\n\n const waitingWorkflows = totalWorkflows - completedWorkflows;\n if (waitingWorkflows > 0) {\n console.log(`Note: ${waitingWorkflows} workflow(s) are still waiting (scheduled for later)`);\n }\n\n const failedSection = formatFailedTests(failedTests, appUrl);\n if (failedSection) {\n console.log(failedSection);\n }\n\n if (failedWorkflows > 0) {\n const triageEnabled = await isTriageEnabledForExecution(apiUrl, token, suiteId);\n if (triageEnabled) {\n console.log(\"\");\n console.log(`Triage: canary triage wait ${suiteId}`);\n }\n }\n\n process.exit(exitCode);\n}\n"],"mappings":"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","names":[]}
|