@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
|
@@ -1 +0,0 @@
|
|
|
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\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 resolveTestConfig,\n UUID_RE,\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 verbose = hasFlag(argv, \"--verbose\", \"-v\");\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 // 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> = {};\n if (resolvedConfig.propertyId) body.propertyId = resolvedConfig.propertyId;\n if (resolvedConfig.environmentId) 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\n const { suiteId, jobId, appUrl } = triggerData;\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\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) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\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(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 }\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":[]}
|
package/dist/chunk-JICSYFRJ.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
-
|
|
3
|
-
// src/brand.ts
|
|
4
|
-
import { createRequire } from "module";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import process from "process";
|
|
7
|
-
var require2 = createRequire(import.meta.url);
|
|
8
|
-
var pkg = require2("../package.json");
|
|
9
|
-
function resolveBrandFromArgv(argv1) {
|
|
10
|
-
if (argv1) {
|
|
11
|
-
const base = path.basename(argv1).replace(/\.[^./\\]+$/, "").toLowerCase();
|
|
12
|
-
if (base.includes("cofactor")) return "cofactor";
|
|
13
|
-
}
|
|
14
|
-
return "canary";
|
|
15
|
-
}
|
|
16
|
-
var command = resolveBrandFromArgv(process.argv[1]);
|
|
17
|
-
var packageName = pkg.name;
|
|
18
|
-
var version = pkg.version;
|
|
19
|
-
var INVOCATION_RE = /(^|[^\w-])canary(?= [a-z])/g;
|
|
20
|
-
var PROSE_RE = /\bCanary\b/g;
|
|
21
|
-
function brandTextFor(text, brand) {
|
|
22
|
-
if (brand === "canary") return text;
|
|
23
|
-
const display = brand === "cofactor" ? "Cofactor" : "Canary";
|
|
24
|
-
return text.replace(INVOCATION_RE, (_m, prefix) => `${prefix}${brand}`).replace(PROSE_RE, display);
|
|
25
|
-
}
|
|
26
|
-
function brandText(text) {
|
|
27
|
-
return brandTextFor(text, command);
|
|
28
|
-
}
|
|
29
|
-
function installConsoleBranding() {
|
|
30
|
-
if (command === "canary") return;
|
|
31
|
-
const methods = ["log", "error", "warn", "info"];
|
|
32
|
-
for (const method of methods) {
|
|
33
|
-
const original = console[method].bind(console);
|
|
34
|
-
console[method] = (...args) => {
|
|
35
|
-
original(...args.map((arg) => typeof arg === "string" ? brandText(arg) : arg));
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export {
|
|
41
|
-
command,
|
|
42
|
-
packageName,
|
|
43
|
-
version,
|
|
44
|
-
installConsoleBranding
|
|
45
|
-
};
|
|
46
|
-
//# sourceMappingURL=chunk-JICSYFRJ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/brand.ts"],"sourcesContent":["/**\n * Brand resolution for the dual-published CLI.\n *\n * We ship a single build to two npm packages:\n * - `@canaryai/cli` → binary `canary` (legacy brand)\n * - `@runcofactor/cli` → binary `cofactor` (new brand)\n *\n * Both packages contain the identical artifact; they differ only in their\n * `name` and `bin` map (rewritten at publish time — see\n * `.github/workflows/publish-canary-cli.yml`). Everything brand-specific is\n * therefore derived at runtime:\n *\n * - `command` comes from the invoked binary name (`basename(process.argv[1])`),\n * so output reflects how the user called it (`brandText` rewrites legacy\n * `canary` display strings to the active brand).\n * - `packageName` comes from the installed package's own `package.json`, so\n * self-upgrade and the update check target whatever package was installed.\n *\n * The legacy `canary` brand is the default for every non-`cofactor` invocation\n * (`node dist/index.js`, dev scripts, tests), which keeps existing behaviour\n * byte-for-byte unchanged.\n *\n * @module\n */\n\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport process from 'node:process';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { name: string; version: string };\n\nexport type Brand = 'canary' | 'cofactor';\n\n/**\n * Resolve the brand from the invoked binary path (`process.argv[1]`). The\n * global-install shim is named `canary` or `cofactor` (npm/bun/pnpm bin link),\n * so the basename is the brand. Everything else (`node dist/index.js`, dev\n * scripts, tests) defaults to the legacy `canary` brand. Pure for testing.\n */\nexport function resolveBrandFromArgv(argv1: string | undefined): Brand {\n if (argv1) {\n const base = path.basename(argv1).replace(/\\.[^./\\\\]+$/, '').toLowerCase();\n if (base.includes('cofactor')) return 'cofactor';\n }\n return 'canary';\n}\n\n/** The invoked command / binary name, e.g. `canary` or `cofactor`. */\nexport const command: Brand = resolveBrandFromArgv(process.argv[1]);\n\n/** npm package name of the installed CLI, read from its own package.json. */\nexport const packageName: string = pkg.name;\n\n/** CLI version, read from package.json. */\nexport const version: string = pkg.version;\n\n/**\n * Rewrite display strings authored against the legacy `canary` brand to the\n * active brand. Identity for the `canary` brand (so legacy output never\n * changes). Two precise, word-anchored rules:\n *\n * 1. The invocation token `canary ` followed by a lowercase letter (i.e. a\n * sub-command or `canary v1.2.3`). Anchored on a non-word, non-hyphen\n * boundary so it never touches `trycanary.ai`, `x-canary-request-id`,\n * `canary-cli`, or `CANARY_API_TOKEN`.\n * 2. The prose noun `Canary` (capitalized, standalone word) → display name.\n */\nconst INVOCATION_RE = /(^|[^\\w-])canary(?= [a-z])/g;\nconst PROSE_RE = /\\bCanary\\b/g;\n\n/** Pure brand rewrite for a given target brand. Identity for `canary`. */\nexport function brandTextFor(text: string, brand: Brand): string {\n if (brand === 'canary') return text;\n const display = brand === 'cofactor' ? 'Cofactor' : 'Canary';\n return text\n .replace(INVOCATION_RE, (_m, prefix: string) => `${prefix}${brand}`)\n .replace(PROSE_RE, display);\n}\n\nfunction brandText(text: string): string {\n return brandTextFor(text, command);\n}\n\n/**\n * Install brand rewriting over the `console` methods used for human-readable\n * output. No-op for the `canary` brand. Skipped by the caller for machine\n * output (`--json`/`--format`) and the `mcp` JSON-RPC transport, where rewriting\n * could corrupt structured payloads — display branding only.\n */\nexport function installConsoleBranding(): void {\n if (command === 'canary') return;\n const methods = ['log', 'error', 'warn', 'info'] as const;\n for (const method of methods) {\n const original = console[method].bind(console);\n console[method] = (...args: unknown[]) => {\n original(...args.map((arg) => (typeof arg === 'string' ? brandText(arg) : arg)));\n };\n }\n}\n"],"mappings":";;;AAyBA,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AACjB,OAAO,aAAa;AAEpB,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,MAAMA,SAAQ,iBAAiB;AAU9B,SAAS,qBAAqB,OAAkC;AACrE,MAAI,OAAO;AACT,UAAM,OAAO,KAAK,SAAS,KAAK,EAAE,QAAQ,eAAe,EAAE,EAAE,YAAY;AACzE,QAAI,KAAK,SAAS,UAAU,EAAG,QAAO;AAAA,EACxC;AACA,SAAO;AACT;AAGO,IAAM,UAAiB,qBAAqB,QAAQ,KAAK,CAAC,CAAC;AAG3D,IAAM,cAAsB,IAAI;AAGhC,IAAM,UAAkB,IAAI;AAanC,IAAM,gBAAgB;AACtB,IAAM,WAAW;AAGV,SAAS,aAAa,MAAc,OAAsB;AAC/D,MAAI,UAAU,SAAU,QAAO;AAC/B,QAAM,UAAU,UAAU,aAAa,aAAa;AACpD,SAAO,KACJ,QAAQ,eAAe,CAAC,IAAI,WAAmB,GAAG,MAAM,GAAG,KAAK,EAAE,EAClE,QAAQ,UAAU,OAAO;AAC9B;AAEA,SAAS,UAAU,MAAsB;AACvC,SAAO,aAAa,MAAM,OAAO;AACnC;AAQO,SAAS,yBAA+B;AAC7C,MAAI,YAAY,SAAU;AAC1B,QAAM,UAAU,CAAC,OAAO,SAAS,QAAQ,MAAM;AAC/C,aAAW,UAAU,SAAS;AAC5B,UAAM,WAAW,QAAQ,MAAM,EAAE,KAAK,OAAO;AAC7C,YAAQ,MAAM,IAAI,IAAI,SAAoB;AACxC,eAAS,GAAG,KAAK,IAAI,CAAC,QAAS,OAAO,QAAQ,WAAW,UAAU,GAAG,IAAI,GAAI,CAAC;AAAA,IACjF;AAAA,EACF;AACF;","names":["require"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/login.ts"],"sourcesContent":["import process from \"node:process\";\nimport readline from \"node:readline\";\nimport { spawn } from \"node:child_process\";\nimport { saveAuth, ENV_URLS, envToProfile, getArgValue } from \"./auth\";\n\nexport type StartResponse = {\n ok: boolean;\n deviceCode?: string;\n userCode?: string;\n verificationUrl?: string;\n expiresAt?: string;\n intervalSeconds?: number;\n error?: string;\n};\n\nexport type PollResponse = {\n ok: boolean;\n status?: \"pending\" | \"approved\" | \"rejected\" | \"expired\";\n accessToken?: string;\n orgId?: string;\n error?: string;\n};\n\nexport type OrgsResponse = {\n ok: boolean;\n currentOrgId?: string;\n organizations?: Array<{ id: string; name: string; role: string }>;\n error?: string;\n};\n\nexport type SwitchOrgResponse = {\n ok: boolean;\n accessToken?: string;\n orgId?: string;\n orgName?: string;\n role?: string;\n error?: string;\n};\n\nfunction shouldOpenBrowser(argv: string[]): boolean {\n return !argv.includes(\"--no-open\");\n}\n\nexport function openUrl(url: string) {\n const platform = process.platform;\n if (platform === \"darwin\") {\n spawn(\"open\", [url], { stdio: \"ignore\" });\n return;\n }\n if (platform === \"win32\") {\n spawn(\"cmd\", [\"/c\", \"start\", \"\", url], { stdio: \"ignore\" });\n return;\n }\n spawn(\"xdg-open\", [url], { stdio: \"ignore\" });\n}\n\nfunction promptChoice(question: string): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nexport async function fetchOrgs(apiUrl: string, token: string): Promise<OrgsResponse | null> {\n try {\n const res = await fetch(`${apiUrl}/cli-login/orgs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) return null;\n return (await res.json()) as OrgsResponse;\n } catch {\n return null;\n }\n}\n\nexport async function switchOrg(apiUrl: string, token: string, orgId: string): Promise<SwitchOrgResponse> {\n const res = await fetch(`${apiUrl}/cli-login/switch-org`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ orgId }),\n });\n return (await res.json()) as SwitchOrgResponse;\n}\n\nexport async function runLogin(argv: string[]) {\n const env = getArgValue(argv, \"--env\");\n const envUrls = env ? ENV_URLS[env] : undefined;\n\n if (env && !envUrls) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n envUrls?.api ??\n process.env.CANARY_API_URL ??\n \"https://api.trycanary.ai\";\n const appUrl =\n getArgValue(argv, \"--app-url\") ??\n envUrls?.app ??\n process.env.CANARY_APP_URL ??\n \"https://app.trycanary.ai\";\n\n const orgFlag = getArgValue(argv, \"--org\");\n\n const startRes = await fetch(`${apiUrl}/cli-login/start`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ appUrl }),\n });\n\n const startJson = (await startRes.json()) as StartResponse;\n if (!startRes.ok || !startJson.ok || !startJson.deviceCode || !startJson.userCode) {\n console.error(\"Login start failed\", startJson.error ?? startRes.statusText);\n process.exit(1);\n }\n\n console.log(\"Login required.\");\n console.log(`User code: ${startJson.userCode}`);\n if (startJson.verificationUrl) {\n console.log(`Open: ${startJson.verificationUrl}`);\n if (shouldOpenBrowser(argv)) {\n try {\n openUrl(startJson.verificationUrl);\n } catch {\n console.log(\"Unable to open browser automatically. Please open the URL manually.\");\n }\n }\n }\n\n const intervalMs = (startJson.intervalSeconds ?? 3) * 1000;\n const expiresAt = startJson.expiresAt ? new Date(startJson.expiresAt).getTime() : null;\n\n let token: string | undefined;\n let initialOrgId: string | undefined;\n\n while (true) {\n if (expiresAt && Date.now() > expiresAt) {\n console.error(\"Login code expired.\");\n process.exit(1);\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\n const pollRes = await fetch(`${apiUrl}/cli-login/poll`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ deviceCode: startJson.deviceCode }),\n });\n\n const pollJson = (await pollRes.json()) as PollResponse;\n if (!pollRes.ok || !pollJson.ok) {\n console.error(\"Login poll failed\", pollJson.error ?? pollRes.statusText);\n process.exit(1);\n }\n\n if (pollJson.status === \"approved\" && pollJson.accessToken) {\n token = pollJson.accessToken;\n initialOrgId = pollJson.orgId;\n break;\n }\n\n if (pollJson.status === \"rejected\") {\n console.error(\"Login rejected.\");\n process.exit(1);\n }\n\n if (pollJson.status === \"expired\") {\n console.error(\"Login expired.\");\n process.exit(1);\n }\n }\n\n // Fetch user's organizations (gracefully skip if endpoint not available)\n const orgsData = await fetchOrgs(apiUrl, token);\n const orgs = orgsData?.organizations ?? [];\n\n let finalToken = token;\n let finalOrgId = initialOrgId;\n let finalOrgName: string | undefined;\n\n if (orgs.length <= 1) {\n // Single org (or none) — use as-is\n finalOrgName = orgs[0]?.name;\n } else {\n // Multiple orgs — determine which one to use\n let selectedOrg: (typeof orgs)[number] | undefined;\n\n if (orgFlag) {\n // Match --org flag by name (case-insensitive) or ID\n selectedOrg = orgs.find(\n (o) => o.name.toLowerCase() === orgFlag.toLowerCase() || o.id === orgFlag\n );\n if (!selectedOrg) {\n console.error(`Organization \"${orgFlag}\" not found. Available orgs:`);\n for (const o of orgs) {\n console.error(` - ${o.name} (${o.id})`);\n }\n process.exit(1);\n }\n } else if (process.stdin.isTTY) {\n // Interactive prompt\n console.log(\"\\nYou belong to multiple organizations. Select one:\");\n for (let i = 0; i < orgs.length; i++) {\n const marker = orgs[i].id === initialOrgId ? \" (current)\" : \"\";\n console.log(` ${i + 1}. ${orgs[i].name}${marker}`);\n }\n\n const answer = await promptChoice(`\\nChoice [1-${orgs.length}]: `);\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= orgs.length) {\n console.error(\"Invalid selection.\");\n process.exit(1);\n }\n selectedOrg = orgs[idx];\n } else {\n // Non-interactive — use default from JWT\n const defaultOrg = orgs.find((o) => o.id === initialOrgId);\n console.log(\n `Warning: Multiple organizations available but running non-interactively. Using \"${defaultOrg?.name ?? initialOrgId}\".`\n );\n console.log('Tip: Use --org <name> to select a specific organization.');\n selectedOrg = defaultOrg;\n }\n\n if (selectedOrg && selectedOrg.id !== initialOrgId) {\n // Switch to selected org\n const switchRes = await switchOrg(apiUrl, token, selectedOrg.id);\n if (!switchRes.ok || !switchRes.accessToken) {\n console.error(\"Failed to switch organization:\", switchRes.error ?? \"Unknown error\");\n process.exit(1);\n }\n finalToken = switchRes.accessToken;\n finalOrgId = switchRes.orgId;\n finalOrgName = switchRes.orgName;\n } else if (selectedOrg) {\n finalOrgName = selectedOrg.name;\n }\n }\n\n const profileName = env ? envToProfile(env) : undefined;\n const filePath = await saveAuth({ token: finalToken, apiUrl, orgId: finalOrgId, orgName: finalOrgName }, profileName);\n const displayName = finalOrgName ? ` to ${finalOrgName}` : \"\";\n const profileLabel = profileName ? ` (profile: ${profileName})` : \"\";\n console.log(`Login successful${displayName}${profileLabel}. Token saved to ${filePath}`);\n console.log(\"Set CANARY_API_TOKEN to use the CLI without re-login.\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../types/src/workflows.ts","../../types/src/sandbox-instance-size.ts","../../types/src/node-type-labels.ts","../../types/src/index.ts","../../types/src/credentials.ts","../../types/src/issues.ts","../../types/src/local-browser.ts","../../types/src/benchmark.ts","../../types/src/sandbox-tasks.ts","../../types/src/macros.ts","../../types/src/workflow-node-schemas.ts","../../types/src/browser-viewport.ts"],"sourcesContent":["export const WORKFLOW_RUN_SEGMENT_JOB_TYPE = 'workflows.run-segment' as const;\n\n// Suite Runner Orchestrator Pattern\nexport const WORKFLOW_TEST_SUITE_ORCHESTRATOR_JOB_TYPE = 'workflows.test-suite-orchestrator' as const;\nexport const WORKFLOW_TEST_SUITE_WORKFLOW_JOB_TYPE = 'workflows.test-suite-workflow' as const;\nexport const WORKFLOW_SUITE_SETUP_JOB_TYPE = 'workflows.suite-setup' as const;\nexport const WORKFLOW_SEED_JOB_TYPE = 'workflow.seed' as const;\nexport const WORKFLOW_TEARDOWN_JOB_TYPE = 'workflow.teardown' as const;\n\nexport type WorkflowSuiteRunPayload = {\n workflowId?: string; // For single-workflow authoring\n /** Filter to specific workflow IDs (e.g. CSV import auto-run) */\n workflowIds?: string[];\n /**\n * Restrict the suite to workflows whose credential references all resolve\n * to this property. When set, login nodes' `credentialTemplateId` /\n * `credentialId` are resolved to their backing property and the suite skips\n * (a) workflows with zero resolvable credentials, and (b) workflows whose\n * credentials resolve to any other property. Null/undefined = no filter.\n */\n propertyId?: string | null;\n /** Target environment for credential resolution and base-URL substitution. */\n environmentId?: string | null;\n triggeredByUserId?: string | null;\n triggeredReason?: string | null;\n /**\n * Filter workflows by tag(s) — any-of semantics. A workflow runs if it has\n * at least one of the supplied tag names. Empty/undefined means \"no tag filter\".\n */\n tags?: string[] | null;\n /** Filter workflows by name pattern (case-insensitive substring match) */\n namePattern?: string | null;\n};\n\n/** @deprecated Use WorkflowSuiteRunPayload — kept as alias during in-flight job migration. */\nexport type WorkflowPropertyTestRunPayload = WorkflowSuiteRunPayload;\n\nexport type WorkflowRunSegmentPayload = {\n runId: string;\n};\n\nexport type WorkflowTestStatus =\n | 'queued'\n | 'running'\n | 'waiting'\n | 'success'\n | 'failed'\n | 'skipped'\n | 'flaky_success'\n | 'canceled'\n // Not run because the workflow's health is degraded (suite runs only).\n | 'degraded';\n\n/**\n * Workflow reliability dimension, orthogonal to {@link WorkflowDefinitionStatus}\n * (lifecycle). Recomputed after each suite run from the workflow's test-run history.\n *\n * - `unverified` — no terminal test runs yet\n * - `never_passed` — has run, never a single pass\n * - `passing` — recent window all green, streak below the reliable threshold\n * - `reliable` — enough consecutive clean passes\n * - `flaky` — recent window mixes passes & failures\n * - `degraded` — enough consecutive failures; skipped in suite runs (\"degraded — not run\")\n * until the workflow is edited or manually re-enabled\n */\nexport const WORKFLOW_HEALTH = [\n 'unverified',\n 'never_passed',\n 'passing',\n 'reliable',\n 'flaky',\n 'degraded',\n] as const;\n\nexport type WorkflowHealth = (typeof WORKFLOW_HEALTH)[number];\n\n/**\n * Full lifecycle states for a workflow definition. This is the *lifecycle* axis only;\n * reliability lives in the orthogonal {@link WorkflowHealth} dimension.\n *\n * User-settable via the API/CLI: `draft`, `published`\n * (see {@link USER_SETTABLE_WORKFLOW_STATUSES}).\n *\n * Lifecycle-internal (set by the system, not by users):\n * - `generating` — the workflow is being built from a recording/import\n * - `building` — a builder validation run is in progress\n * - `diagnostic` — builder validation failed; the workflow is parked for inspection\n * - `deleted` — soft-deleted\n *\n * @deprecated `quarantined` — retained in the enum for back-compat only (dropped in a\n * follow-up). Reliability is now tracked by {@link WorkflowHealth}; nothing sets this value.\n */\nexport const WORKFLOW_STATUSES = [\n 'draft',\n 'generating',\n 'building',\n 'published',\n 'quarantined',\n 'deleted',\n 'diagnostic',\n] as const;\n\nexport type WorkflowDefinitionStatus = (typeof WORKFLOW_STATUSES)[number];\n\n/**\n * Statuses a user (or agent) can set directly via the API or CLI.\n *\n * - `draft` — editable, not run by suites\n * - `published` — active, included in regression runs\n *\n * Reliability (whether a published flow is trusted, flaky, or degraded) is the separate\n * {@link WorkflowHealth} dimension, computed automatically — not user-set here.\n */\nexport const USER_SETTABLE_WORKFLOW_STATUSES = [\n 'draft',\n 'published',\n] as const satisfies readonly WorkflowDefinitionStatus[];\n\nexport type UserSettableWorkflowStatus =\n (typeof USER_SETTABLE_WORKFLOW_STATUSES)[number];\n\n/** Context provided by the agent when a workflow step fails */\nexport type WorkflowFailureContext = {\n /** What the agent observed happening when the step failed */\n observedBehavior: string;\n /** What was expected to happen */\n expectedBehavior: string;\n /** Steps to reproduce the failure */\n reproSteps: string;\n /** Specific questions the agent poses to help improve instructions (debug mode only) */\n questions?: string[];\n};\n\nexport type WorkspaceRedactionReason = 'secret' | 'binary' | 'large' | 'system';\n\nexport type WorkspaceManifestEntry = {\n path: string;\n type: 'file' | 'dir';\n size?: number;\n mtime?: string;\n language?: string;\n redacted?: boolean;\n redactionReason?: WorkspaceRedactionReason;\n};\n\nexport type WorkspaceManifest = {\n version: string;\n root: string;\n generatedAt: string;\n entries: WorkspaceManifestEntry[];\n};\n\nexport type WorkspaceStreamEvent =\n | {\n type: 'workspace-manifest';\n data: WorkspaceManifest;\n }\n | {\n type: 'workspace-file-created';\n data: WorkspaceManifestEntry;\n }\n | {\n type: 'workspace-file-updated';\n data: {\n path: string;\n size?: number;\n mtime?: string;\n version?: string;\n };\n }\n | {\n type: 'workspace-file-deleted';\n data: { path: string };\n }\n | {\n type: 'workspace-file-content';\n data: {\n path: string;\n content: string;\n encoding?: 'utf-8';\n version?: string;\n };\n };\n\nexport type WorkflowTestRunSummary = {\n id: string;\n workflowId: string;\n status: WorkflowTestStatus;\n snapshotVersion: number;\n errorMessage: string | null;\n linkedIssueId: string | null;\n startedAt: string | null;\n finishedAt: string | null;\n createdAt: string;\n};\n\nexport type WorkflowTestSuiteStatus = 'queued' | 'running' | 'completed';\n\nexport type WorkflowTestEvent = {\n type: 'workflow-test';\n suiteId: string;\n emittedAt: 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 skipReason?: string | null;\n linkedIssueId?: string | null;\n message?: string;\n /** The workflow's reliability dimension at emit time (drives the UI health badge). */\n health?: WorkflowHealth;\n // Wait/segment tracking\n resumeAt?: string | null;\n segmentIndex?: number | null;\n totalSegments?: number | null;\n /** Monotonic sequence number for ordering concurrent events */\n _seq?: number;\n};\n\nexport type WorkflowTestSuiteEvent = {\n type: 'workflow-test-suite';\n suiteId: string;\n emittedAt: string;\n status: WorkflowTestSuiteStatus;\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n totalWorkflows: number;\n completedWorkflows: number;\n failedWorkflows: number;\n successfulWorkflows: number;\n skippedWorkflows: number;\n waitingWorkflows: number;\n /** @deprecated Quarantine removed; always 0. Drop with the column in a follow-up. */\n quarantinedWorkflows: number;\n /** Workflows skipped because their health is `degraded` (shown \"degraded — not run\"). */\n degradedWorkflows: number;\n seedTotal: number;\n seedSucceeded: number;\n seedFailed: number;\n teardownTotal: number;\n teardownSucceeded: number;\n teardownFailed: number;\n errorMessage?: string | null;\n message?: string;\n /** Monotonic sequence number for ordering concurrent events */\n _seq?: number;\n};\n\nexport type WorkflowTestStreamEvent = WorkflowTestEvent | WorkflowTestSuiteEvent;\n","/**\n * Sandbox Instance Sizing\n *\n * Closed preset enum of EC2 sizes a template can run on, plus a per-size\n * resource spec consumed by provisioning, compose generation, and prompts.\n *\n * Single source of truth for: launch-template instance type override,\n * docker compose mem_limit/cpus per role, prompt-template variables.\n *\n * @module types/sandbox-instance-size\n */\n\nexport const INSTANCE_SIZES = ['small', 'medium', 'large', 'xlarge', '2xlarge'] as const;\nexport type InstanceSize = (typeof INSTANCE_SIZES)[number];\n\nexport const DEFAULT_INSTANCE_SIZE: InstanceSize = 'medium';\n\nexport interface InstanceSizeSpec {\n /** EC2 instance type for `RunInstancesCommand` override. */\n ec2InstanceType: string;\n vcpu: number;\n memoryMiB: number;\n /**\n * Per-role memory caps (MiB) consumed by `resourceLimitsFor()`. Must cover\n * every key in compose-helpers.ts STATIC_LIMITS.\n *\n * Sum × per-role multiplicity should fit within `memoryMiB × 1.5` to keep\n * swap pressure bounded. See INSTANCE_SIZE_SUM_LIMIT_RATIO.\n */\n agentCapMiB: number;\n appSoftCapMiB: number;\n proxyCapMiB: number;\n emulationCapMiB: number;\n storageCapMiB: number;\n /**\n * Per-size hard CPU quota (cgroup `cpus`) for the user app tier. Other\n * roles (agent, proxy, emulation, storage) keep fixed CPU caps from\n * STATIC_LIMITS — their workloads don't scale with host vCPU. App is the\n * only role that sees a real benefit from a larger instance, so it's the\n * only one keyed here.\n *\n * Formula: max('1.0', vcpu - 1.5) — reserve 1.5 vCPU for the agent.\n * Stored as a string so docker compose receives `'1.0'` (not `'1'`),\n * matching the format of STATIC_LIMITS.cpus values.\n */\n appCpus: string;\n}\n\n/** Cap on (sum of role caps) ÷ memoryMiB — anything above this swaps too hard. */\nexport const INSTANCE_SIZE_SUM_LIMIT_RATIO = 1.5;\n\nexport const INSTANCE_SIZE_SPEC: Record<InstanceSize, InstanceSizeSpec> = {\n small: {\n ec2InstanceType: 't3.small',\n vcpu: 2,\n memoryMiB: 2048,\n // 2 GiB instance — agent capped at min(2048, 0.4 × 2048) = 819\n agentCapMiB: 819,\n appSoftCapMiB: 1024,\n proxyCapMiB: 256,\n emulationCapMiB: 128,\n storageCapMiB: 256,\n appCpus: '1.0',\n },\n medium: {\n ec2InstanceType: 't3.medium',\n vcpu: 2,\n memoryMiB: 4096,\n agentCapMiB: 1536,\n appSoftCapMiB: 1536,\n proxyCapMiB: 256,\n emulationCapMiB: 128,\n storageCapMiB: 256,\n appCpus: '1.0',\n },\n large: {\n ec2InstanceType: 't3.large',\n vcpu: 2,\n memoryMiB: 8192,\n agentCapMiB: 2048,\n appSoftCapMiB: 5120,\n proxyCapMiB: 256,\n emulationCapMiB: 128,\n storageCapMiB: 512,\n // t3.large is memory-only — same 2 vCPU as medium.\n appCpus: '1.0',\n },\n xlarge: {\n ec2InstanceType: 't3.xlarge',\n vcpu: 4,\n memoryMiB: 16384,\n agentCapMiB: 2048,\n appSoftCapMiB: 12288,\n proxyCapMiB: 256,\n emulationCapMiB: 256,\n storageCapMiB: 512,\n appCpus: '2.5',\n },\n '2xlarge': {\n ec2InstanceType: 't3.2xlarge',\n vcpu: 8,\n memoryMiB: 32768,\n agentCapMiB: 2048,\n appSoftCapMiB: 28672,\n proxyCapMiB: 512,\n emulationCapMiB: 512,\n storageCapMiB: 1024,\n appCpus: '6.5',\n },\n};\n\nconst INSTANCE_SIZE_SET = new Set<string>(INSTANCE_SIZES);\n\n/**\n * Narrowing helper for the repo boundary. Returns null on unknown input so\n * callers can surface a typed user error without `as never` / `as any`.\n */\nexport function parseInstanceSize(s: string | null | undefined): InstanceSize | null {\n return s && INSTANCE_SIZE_SET.has(s) ? (s as InstanceSize) : null;\n}\n\nconst RECOMMENDATION_MAP: Record<string, InstanceSize> = {\n // Exact preset names\n small: 'small',\n medium: 'medium',\n large: 'large',\n xlarge: 'xlarge',\n '2xlarge': '2xlarge',\n // Common AWS instance types the authoring agent may emit\n 't3.nano': 'small',\n 't3.micro': 'small',\n 't3.small': 'small',\n 't3.medium': 'medium',\n 't3.large': 'large',\n 't3.xlarge': 'xlarge',\n 't3.2xlarge': '2xlarge',\n 't2.small': 'small',\n 't2.medium': 'medium',\n 't2.large': 'large',\n 't2.xlarge': 'xlarge',\n 't2.2xlarge': '2xlarge',\n};\n\n/**\n * Maps a free-form `recommendedInstanceType` string (often produced by the\n * authoring agent before this enum existed) to the closest preset.\n *\n * Strategy: exact match against known names first, then fall back to nearest\n * memoryMiB. Ties broken toward cheaper (lower `memoryMiB`).\n */\nexport function inferInstanceSizeFromRecommendation(\n s: string | null | undefined\n): InstanceSize | null {\n if (!s) return null;\n const trimmed = s.trim().toLowerCase();\n if (!trimmed) return null;\n const exact = RECOMMENDATION_MAP[trimmed];\n if (exact) return exact;\n\n // Last-resort heuristic: parse a memory hint like \"4 GiB\" / \"8GB\" / \"2g\".\n const match = trimmed.match(/(\\d+(?:\\.\\d+)?)\\s*(g|gi|gb|gib|m|mi|mb|mib)\\b/);\n if (!match) return null;\n const value = Number.parseFloat(match[1]!);\n if (!Number.isFinite(value)) return null;\n const memMiB = match[2]!.startsWith('g') ? value * 1024 : value;\n\n let best: InstanceSize | null = null;\n let bestDelta = Number.POSITIVE_INFINITY;\n for (const size of INSTANCE_SIZES) {\n const delta = Math.abs(INSTANCE_SIZE_SPEC[size].memoryMiB - memMiB);\n if (delta < bestDelta) {\n best = size;\n bestDelta = delta;\n }\n }\n return best;\n}\n","/**\n * Display labels for workflow node types. The JSON / persisted `node.type`\n * discriminator stays as-is (e.g. `'login'`) — only the human-facing label\n * changes here. Centralizing this lets us rename copy in one place.\n */\nexport const NODE_TYPE_LABEL = {\n login: 'Acting As',\n navigate: 'Navigate',\n action: 'Action',\n assertion: 'Assertion',\n condition: 'Condition',\n wait: 'Wait',\n api_sequence: 'API Sequence',\n seed: 'Seed Data',\n setup: 'Setup Flow',\n notification: 'Notification',\n audio: 'Audio input',\n 'audio-assertion': 'Audio assertion',\n end: 'End',\n} as const;\n\nexport type NodeTypeKey = keyof typeof NODE_TYPE_LABEL;\nexport type NodeTypeLabel = (typeof NODE_TYPE_LABEL)[NodeTypeKey];\n\n/**\n * Normalizes legacy/alias node-type keywords to the canonical persisted type.\n * Accepts `'acting-as'` / `'acting_as'` as aliases for `'login'` so CLI\n * callers can use the new name while persisted data stays `'login'`.\n */\nexport function normalizeNodeType(input: string): string {\n if (input === 'acting-as' || input === 'acting_as') return 'login';\n return input;\n}\n","import { z } from 'zod';\n\nexport type ActionInputType =\n | 'text'\n | 'email'\n | 'password'\n | 'number'\n | 'textarea'\n | 'select'\n | 'checkbox'\n | 'multiselect'\n | 'file';\n\nexport interface ActionInputOption {\n value: string;\n label: string;\n}\n\nexport interface ActionDataRequirement {\n /**\n * Unique field identifier used to store the provided input value.\n */\n name: string;\n /**\n * Human-friendly label to display in the UI.\n */\n label: string;\n /**\n * Expected input type. Defaults to 'text' if omitted.\n */\n type?: ActionInputType;\n /**\n * Whether the field is required for the action to succeed.\n */\n required?: boolean;\n /**\n * Helpful description shown to the operator.\n */\n description?: string;\n /**\n * Placeholder text for the input.\n */\n placeholder?: string;\n /**\n * Suggested example value provided by the agent.\n */\n exampleValue?: string;\n /**\n * Optional select options for dropdown-style inputs.\n */\n options?: ActionInputOption[];\n}\n\nexport type ActionInputValues = Record<string, string>;\n\n// ── Workflow-level input definitions ──\n\n/** Value input — a simple typed field (text, number, email, select, etc.) */\nexport interface WorkflowValueInputDefinition {\n kind: 'value';\n /** Variable name (snake_case, e.g. \"user_email\") */\n name: string;\n /** Human-friendly label */\n label: string;\n /** Expected input type. Defaults to 'text' if omitted. */\n type?: ActionInputType;\n required?: boolean;\n description?: string;\n placeholder?: string;\n /** Pre-filled default value */\n defaultValue?: string;\n options?: ActionInputOption[];\n}\n\n/** Credential input — represents a login identity (future: full credential provisioning) */\nexport interface WorkflowCredentialInputDefinition {\n kind: 'credential';\n /** Namespace for credential vars (e.g. \"admin_user\" → $var.admin_user.username) */\n name: string;\n /** Human-friendly label */\n label: string;\n required?: boolean;\n description?: string;\n /** Which credential fields this input needs */\n credentialFields: {\n username?: boolean;\n password?: boolean;\n loginUrl?: boolean;\n mfaSecret?: boolean;\n };\n /** Default credential values (for scenarios) */\n defaults?: {\n username?: string;\n password?: string;\n loginUrl?: string;\n };\n /** Default logical credential template used when a scenario does not override it */\n defaultCredentialTemplateId?: string;\n}\n\nexport type WorkflowInputDefinition =\n | WorkflowValueInputDefinition\n | WorkflowCredentialInputDefinition;\n\n// ── Workflow scenarios (named input-value sets) ──\n\n/** Value-input overrides keyed by WorkflowValueInputDefinition.name */\nexport type WorkflowScenarioValueInputs = Record<string, string>;\n\n/** Credential-input overrides keyed by WorkflowCredentialInputDefinition.name */\nexport type WorkflowScenarioCredentialInputs = Record<\n string,\n {\n /** Logical credential template to resolve against the run environment */\n credentialTemplateId?: string;\n /** Legacy inline fields; retained for backward-compatible imports/storage */\n username?: string;\n password?: string;\n loginUrl?: string;\n mfaSecret?: string;\n }\n>;\n\n/**\n * A named set of workflow input values — parameterized test case for a workflow.\n * Scenarios reference existing `inputsJson` entries by name; orphan keys are\n * surfaced as warnings in the UI but are tolerated in storage.\n */\nexport interface WorkflowScenario {\n /** Stable uuid (survives renames) */\n id: string;\n /** Human-friendly name, e.g. \"Happy path\" */\n name: string;\n description?: string;\n /** At most one scenario per workflow may be default; UI enforces this. */\n isDefault?: boolean;\n /**\n * When true (or undefined — defaults to true for backwards compatibility),\n * this scenario is included when the workflow runs as part of a test suite,\n * causing the suite to fan the workflow out to one run per included scenario.\n */\n includeInSuite?: boolean;\n values?: WorkflowScenarioValueInputs;\n credentials?: WorkflowScenarioCredentialInputs;\n}\n\n// ── Workflow environment eligibility ──\n\nexport type WorkflowEnvironmentType = 'permanent' | 'ephemeral' | 'local';\n\n/**\n * Explicit environment eligibility constraints for a workflow.\n * Null/absent means \"any\" — workflow can run against any environment.\n * `only`: pin to specific environment IDs (e.g. prod smoke).\n * `envTypes`: allow only listed environment types (e.g. permanent only).\n */\nexport type WorkflowEnvironmentConstraints =\n | { mode: 'only'; environmentIds: string[] }\n | { mode: 'envTypes'; types: WorkflowEnvironmentType[] };\n\n/**\n * Metadata about input values, including whether synthetic data was used.\n * @deprecated Synthetic tracking has been removed. Form field reporting is\n * now unified via {@link ActionFormField} in debug mode only.\n */\nexport interface ActionInputMetadata {\n /**\n * True if any input values were synthetically generated by the agent\n * rather than provided by the user.\n */\n usedSyntheticData?: boolean;\n /**\n * List of field names that were synthetically generated.\n */\n syntheticFields?: string[];\n}\n\n/**\n * Unified form field representation for debug-mode agent reporting.\n * Combines field metadata (schema) with the value used, so the agent\n * reports everything in a single array instead of separate\n * `dataRequirements` + `inputData` fields.\n */\nexport interface ActionFormField {\n /** Unique field identifier (snake_case, e.g. task_title). */\n name: string;\n /** Human-friendly label from the UI. */\n label: string;\n /** Input type. Defaults to 'text' if omitted. */\n type?: ActionInputType;\n /** Whether the field is required. */\n required?: boolean;\n /** The value entered/used for this field. */\n value: string;\n /** Select/multiselect options, if applicable. */\n options?: ActionInputOption[];\n}\n\nexport const PLAYBOOK_STEP_ACTIONS = [\n 'click',\n 'fill',\n 'fill_form',\n 'select',\n 'navigate',\n 'wait',\n 'scroll',\n 'hover',\n 'upload',\n 'verify',\n 'press_key',\n 'drag',\n 'resize',\n 'capture',\n 'extract_table',\n 'python_execute',\n 'verify_download',\n 'verify_download_content',\n 'screenshot',\n 'agent_loop',\n] as const;\n\nexport type PlaybookStepAction = (typeof PLAYBOOK_STEP_ACTIONS)[number];\nexport type LegacyPlaybookStepAction =\n | PlaybookStepAction\n | 'other'\n | 'handle_native_dialog'\n | 'toggle'\n | 'focus';\n\nfunction isPositiveIntegerString(value: string | undefined): value is string {\n if (!value || !/^\\d+$/.test(value)) return false;\n return Number(value) > 0;\n}\n\n/**\n * Zod schema for the agent-settable fields of a playbook step. This is the\n * single source of truth for what the LLM can author via the\n * `updateWorkflowStep` tool — `apps/api` imports this directly instead of\n * keeping a parallel hard-coded schema (which used to drift, e.g. duplicate\n * action enum and missing `expectedDialogs`).\n *\n * Internal fields the *runtime* attaches (actionRefsSource, screenshotS3Key)\n * are NOT in this schema — they belong on the wider {@link PlaybookStep}\n * type below. Keeping them out keeps the agent's input surface tight and\n * prevents prompt injection of internal-only fields.\n *\n * Field descriptions are LLM-tuned: they are read directly into the tool's\n * function-calling schema. Don't shorten them without considering the agent.\n */\nexport const playbookStepAgentSchema = z\n .object({\n order: z.number().describe('Step number (1-based).'),\n action: z\n .enum(PLAYBOOK_STEP_ACTIONS)\n .describe(\n 'The type of browser interaction. PREFER DETERMINISTIC VERBS even when verbose — three deterministic steps beat one agentic step, because deterministic steps replay via Playwright with no agent dispatch. ' +\n 'Each replayable playbook step must be 1:1 with an exposed browser tool you actually used and must correspond to zero or one recorded actionRef. If you cannot accomplish the intent with the browser tools you have, do not add a playbook step for it; report the limitation outside playbookSteps. Do not summarize several recorded actions into one \"other\" step; report the same 1:1 browser primitives you actually used. The only multi-field exception is one fill_form step for one browser_fill_form action. ' +\n 'Use \"verify\" for assertion checks — both inline assertions and completion assertions proving the action succeeded. ' +\n 'Use \"verify\" for inspection language such as \"inspect whether\", \"confirm\", \"observe\", or \"check\" when the result is visible in the DOM. Use \"capture\" instead only when the observed value is needed by later steps. ' +\n 'Use \"fill\" for single-field inputs. Use \"fill_form\" when browser_fill_form was called to fill multiple fields at once — one fill_form step per call. ' +\n 'Use \"click\" for checkboxes, switches, toggle buttons, and click-to-focus interactions. There is no separate \"toggle\" or \"focus\" action. ' +\n 'Use \"upload\" for file upload actions, including generated CSV/file, attach file, choose file, import file, and document/image attachment intents (browser_action action=\"upload\"). Set params.path to the staged attachment handle, generated file variable, or filename reference used by the upload tool (e.g. {path: \"$var.csv_file\"}), and include actionRefs pointing to the recorded file_upload action. Do NOT use \"other\" for file attachment/upload/import intents. ' +\n 'Use \"scroll\" only when scrolling is the user-visible action being tested; routine browser_scroll calls for visibility, inspection, or video framing are observation steps and should usually be omitted from playbookSteps. ' +\n 'Use \"press_key\" for keyboard actions (Enter, Escape, Tab, arrows). ' +\n 'Use \"press_key\" with params.key=\"Escape\" to dismiss a popover, dropdown, panel, or modal when Escape was the recorded action; use \"click\" if the recorded action clicked a close button or outside target. There is no separate \"dismiss\" action. ' +\n 'Use \"drag\" for drag-and-drop interactions. ' +\n 'Use \"resize\" when browser_resize was used to resize the viewport. Set params.width and params.height to positive integer pixel values and include the actionRef pointing to the recorded resize action. ' +\n 'Use \"capture\" to extract app-generated values (IDs, codes, URLs) for later steps via $var.name, but only from a visible DOM target or the current URL. Do not capture from performance/network timing, storage, hidden DOM, or app-global state. ' +\n 'Use \"screenshot\" only when browser_screenshot itself is the requested evidence capture. Set params.label when a label was used, optionally params.fullPage=\"true\". Do not use screenshot for routine visual checks after actions; use verify when a browser_assert proves visible state. ' +\n 'Use \"extract_table\" when browser_extract_table was used to extract tabular data from the page. ' +\n 'Use \"python_execute\" when python_execute was used with cache_label to run a data validation script. ' +\n 'Use \"verify_download\" when browser_download(action=\"list\") / browser_list_downloads proves that a file matching some pattern was downloaded. Set params.pattern to a glob (supports *, ?) or substring; optionally params.timeoutMs. This is currently agent-only at replay, so do not set actionRefs. ' +\n 'Use \"verify_download_content\" when browser_download(action=\"read\") / browser_read_download content proves CSV headers, PDF text, exported rows, or similar downloaded-file content. Set params.contains or params.pattern to the expected content predicate. This is currently agent-only at replay, so do not set actionRefs. ' +\n 'Use \"agent_loop\" ONLY when the action set is genuinely unknown until runtime — variable count of items (e.g. \"toggle row checkboxes until 3 selected\"), conditional retry that depends on UI state, or a runtime predicate over non-DOM state. Set params.goal to a one-sentence checkable predicate (e.g. params.goal = \"3 grid row checkboxes are selected\"). DO NOT use agent_loop for: known-N iteration (write N deterministic steps), single click that might double-fire (use \"click\"), retry-on-flake (the runtime handles that). agent_loop steps replay via a Playwright override authored after first run; they are non-deterministic on first run, so the bar is high. ' +\n 'Network/devtools/API inspections and unsupported browser environment mutations are diagnostic-only unless there is an exposed browser tool for the action. If visible page state proves the condition, record that as \"verify\"; otherwise omit the diagnostic observation from playbookSteps. ' +\n 'Do not use \"other\". If no concrete browser-tool-backed action above fits, omit the observation from playbookSteps and report the limitation outside the playbook.'\n ),\n target: z\n .string()\n .describe(\n 'Human-readable element description using visible labels, placeholders, or aria-labels. ' +\n 'Examples: \"\\'Submit\\' button\", \"\\'Email\\' input field\", \"\\'Status\\' dropdown\". ' +\n 'For upload actions, describe the file input and include the attachment name (e.g. \"CSV file input for \\'testing-checklist.csv\\'\"). ' +\n 'Never use coordinates, ref IDs, or internal identifiers.'\n ),\n params: z\n .record(z.string(), z.string())\n .optional()\n .describe(\n 'Data values using $var.name references and $MACRO() expressions — NEVER hard-coded literals. ' +\n 'Common keys: value (for fill: {value: \"$EMAIL()\"}), option (for select: {option: \"Admin\"} — ' +\n 'fixed app constants like dropdown options are allowed as literals), url (for navigate: {url: \"$var.target_url\"}), ' +\n 'path (for upload: {path: \"$var.file_path\"}), key (for press_key: {key: \"Enter\"}, {key: \"Escape\"}), ' +\n 'width and height (for resize: {width: \"390\", height: \"844\"}), ' +\n 'label and fullPage (for screenshot: {label: \"seats-table\", fullPage: \"true\"}), ' +\n 'pattern or contains (for download verification: {pattern: \"report-*.csv\"} or {contains: \"Expected Header\"}), ' +\n 'expression + saveAs (for capture: prefer a visible DOM ref expression or \"() => window.location.href\"; do not use performance.getEntries() or other network/storage/global state; e.g. {saveAs: \"order_confirmation_id\"}). ' +\n 'For fill_form: keys are field names from the fill_form call, values are $var.name or $MACRO() (e.g. {company: \"$var.company\", email: \"$EMAIL()\"}). ' +\n 'Available macros: $UUID(), $EMAIL(), $FIRST_NAME(), $LAST_NAME(), $PHONE(), $RANDOM_STRING(n), ' +\n '$RANDOM_INT(min,max), $TODAY(), $DATE(days), $NOW(), $TIMESTAMP(), $SEQUENCE(\"name\").'\n ),\n note: z\n .string()\n .optional()\n .describe(\n 'Timing or quirk notes ONLY (e.g. \"Wait 1s for modal animation\", \"Scroll into view first\"). ' +\n 'Do NOT put data values, optional markers, or verification notes here.'\n ),\n actionRefs: z\n .array(z.number())\n .max(1)\n .optional()\n .describe(\n 'Zero or one recorded browser action index that corresponds to this playbook step. ' +\n 'Interaction tools, browser_resize, capture evaluates (browser_evaluate with saveAs), and browser_assert produce [Action #N] tags — collect those indices here. ' +\n 'Read-only browser_snapshot and plain browser_evaluate without saveAs do NOT produce action indices. browser_scroll and browser_screenshot may produce action indices when they are the primary requested tool call; omit actionRefs when no action tag was emitted. ' +\n 'If you took multiple cacheable UI actions, report multiple 1:1 playbook steps instead of putting multiple refs on one step or summarizing them as \"other\". ' +\n 'A fill_form call is a single action — use one fill_form playbook step with one actionRef. ' +\n 'For a click that triggers a native dialog, keep one click actionRef on the click step and set expectedDialogs rather than adding a separate dialog actionRef. ' +\n 'Actions not referenced by any playbook step are considered exploratory and will be discarded by the cache compiler.'\n ),\n expectedDialogs: z\n .enum(['accept', 'dismiss'])\n .optional()\n .describe(\n 'Set when this step is known to fire a native browser dialog (alert/confirm/prompt) — ' +\n 'e.g. clicking a \"Delete\" button that prompts \"Are you sure?\". On replay the runtime ' +\n 'pre-arms the dialog handler with this action automatically. In free-form runs, you ' +\n 'should call the dialog tool (browser_dialog for consolidated tools, browser_handle_dialog for legacy tools) with this action immediately before the step. ' +\n 'Do not add a separate dialog step — keeping the pre-arm on the triggering step keeps the ' +\n 'pre-arm and the triggering action in one place.'\n ),\n })\n .superRefine((step, ctx) => {\n if (step.action === 'agent_loop') {\n const goal = step.params?.goal;\n if (!goal || goal.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'goal'],\n message:\n 'agent_loop steps must set params.goal to a one-sentence checkable predicate (e.g. \"3 grid row checkboxes are selected\").',\n });\n }\n }\n if (step.action === 'verify_download') {\n const pattern = step.params?.pattern;\n if (!pattern || pattern.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'pattern'],\n message:\n 'verify_download steps must set params.pattern to a glob (supports *, ?) or substring matching the expected filename.',\n });\n }\n }\n if (step.action === 'verify_download_content') {\n const pattern = step.params?.pattern;\n const contains = step.params?.contains;\n if (\n (!pattern || pattern.trim().length === 0) &&\n (!contains || contains.trim().length === 0)\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params'],\n message:\n 'verify_download_content steps must set params.contains or params.pattern for the expected downloaded content.',\n });\n }\n }\n if (step.action === 'resize') {\n const width = step.params?.width;\n const height = step.params?.height;\n if (!isPositiveIntegerString(width)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'width'],\n message: 'resize steps must set params.width to a positive integer pixel value.',\n });\n }\n if (!isPositiveIntegerString(height)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'height'],\n message: 'resize steps must set params.height to a positive integer pixel value.',\n });\n }\n }\n });\n\n/** Inferred type of the agent-authored fields of a playbook step. */\nexport type PlaybookStepAgentInput = z.infer<typeof playbookStepAgentSchema>;\n\n/**\n * A single step in a workflow playbook.\n *\n * Composed of {@link PlaybookStepAgentInput} (everything the agent can set\n * via `updateWorkflowStep`) plus internal fields the runtime attaches during\n * compilation/healing. Splitting these keeps the agent's input surface\n * tight while letting the runtime carry provenance + screenshot keys.\n */\nexport interface PlaybookStep extends Omit<PlaybookStepAgentInput, 'action'> {\n /** Legacy persisted rows may still contain `other`; new agent-authored steps cannot. */\n action: LegacyPlaybookStepAction;\n\n /**\n * Provenance of `actionRefs`:\n * `'recording'` (default when omitted) — emitted by the initial recording\n * compilation; transient session-specific indices that should be stripped\n * after the cache row is built.\n * `'healing'` — emitted by the heal agent during cache-failure recovery and\n * proven correct by replay. These should be preserved across compiles so\n * the next compile lands the entry as `matchStrategy='actionRef'`.\n */\n actionRefsSource?: 'recording' | 'healing';\n /** S3 key for screenshot captured after this playbook step completed. */\n screenshotS3Key?: string;\n /**\n * Where this playbook came from.\n * `'production'` (default when omitted) — emitted by a production-mode run\n * and eligible for cache replay if it carries `actionRefs`.\n * `'validation'` — emitted by a validation-mode discovery run; advisory\n * only. Never replayed deterministically (no `actionRefs`); used as a\n * seed for the next production run on this node, which can then promote\n * the playbook by attaching `actionRefs` on success.\n */\n playbookSource?: 'production' | 'validation';\n}\n\n// ---------------------------------------------------------------------------\n// Validation run findings\n// ---------------------------------------------------------------------------\n\n/**\n * One assumption an agent made during a validation run. Surfaces as a finding\n * the test author can review and either bake into the spec or reject.\n */\nexport interface ValidationAssumption {\n /** Plain-language description of what was assumed. */\n description: string;\n /** Why the agent thought this was the right interpretation. */\n basis: string;\n /** What might go wrong on a future run if the assumption is incorrect. */\n riskIfWrong: string;\n}\n\n/**\n * One open question for the test author surfaced during a validation run.\n */\nexport interface ValidationQuestion {\n /** The specific question. */\n question: string;\n /**\n * When true, this is a question that — if unanswered — will likely cause\n * future production runs to fail or behave inconsistently. UIs should\n * surface these prominently in the post-run proposal.\n */\n blockingFutureRuns: boolean;\n}\n\n/**\n * Suggested edit to a workflow node's config, produced by a validation run.\n *\n * Intentionally narrow: validation findings should propose targeted patches\n * (e.g. \"add a precondition\", \"tighten this assertion\", \"rename the data\n * requirement\"), not free-form rewrites. The shape of `value` depends on\n * `field`. This is consumed by the workflow_proposals UI for accept/reject.\n */\nexport interface NodeConfigPatch {\n /** Which area of the node config this patch touches. */\n field:\n | 'instructions'\n | 'assertions'\n | 'preconditions'\n | 'dataRequirements'\n | 'description'\n | 'other';\n /** What kind of edit is being proposed. */\n op: 'add' | 'replace' | 'remove';\n /** Human-readable summary of the proposed change. */\n summary: string;\n /**\n * Structured patch payload. Shape varies with `field` + `op`. The proposal\n * UI knows how to render each combination; the type is intentionally\n * `unknown` here so the catalog can grow without a types package change.\n */\n value?: unknown;\n}\n\n/**\n * The validation-run-specific result for a single workflow step. Persisted\n * onto `workflow_run_steps.metadataJson.validation` and aggregated into the\n * post-run `validation_findings` proposal.\n */\nexport interface ValidationFindings {\n outcome: 'valid' | 'passed_with_concerns' | 'could_not_complete';\n /** True/false branch decision for conditional nodes. */\n branch?: 'true' | 'false';\n assumptions: ValidationAssumption[];\n questions: ValidationQuestion[];\n /**\n * Concrete config edits the agent suggests for this node. Empty for\n * `valid` outcomes; required for `passed_with_concerns`; optional for\n * `could_not_complete` (the failure itself is the primary signal).\n */\n suggestedNodeConfigPatches: NodeConfigPatch[];\n}\n\n/**\n * An assertion attached directly to an action node.\n * Verified after the action completes; cacheable assertions are replayed mechanically.\n */\nexport interface InlineAssertion {\n /** Unique ID within this node */\n id: string;\n /** Freeform description of what to verify */\n condition: string;\n /** warn = log and continue; fail = stop the test */\n severity: 'warn' | 'fail';\n /** When true, requires agent evaluation (e.g. computer vision) and cannot be cached */\n agentic?: boolean;\n}\n\nexport type BrowserMode = 'headed' | 'headless';\n\nexport * from './crawler/analysis.js';\nexport * from './api-catalog.js';\nexport * from './credentials.js';\nexport * from './environment-kinds.js';\nexport * from './trace-uploads.js';\nexport * from './qa/testing.js';\nexport * from './qa/events.js';\nexport * from './issues.js';\nexport * from './pagination.js';\nexport * from './workflows.js';\nexport * from './browser-stream.js';\nexport * from './attachments.js';\nexport * from './permissions.js';\nexport * from './workflow-cache.js';\nexport * from './cache-telemetry.js';\nexport * from './instance-logs.js';\nexport * from './local-browser.js';\nexport * from './chaos.js';\nexport * from './benchmark.js';\n\nexport * from './api-sequence.js';\nexport * from './diagnostics.js';\nexport * from './conversation-stream.js';\nexport * from './sandbox-tasks.js';\nexport * from './sandbox-instance-size.js';\nexport * from './routes/index.js';\nexport * from './macros.js';\nexport * from './recording.js';\nexport * from './variable-utils.js';\nexport * from './node-outputs.js';\nexport * from './credential-output.js';\nexport * from './adhoc-test.js';\nexport * from './mentions.js';\nexport * from './element-hints.js';\nexport * from './ai-routing.js';\nexport * from './workflow-node-schemas.js';\nexport * from './node-type-labels.js';\nexport * from './browser-viewport.js';\n","/**\n * Credential Types\n *\n * Strongly-typed credential data structures for each auth provider\n */\n\nimport { z } from 'zod';\n\nexport type AuthProvider =\n | 'basic'\n | 'google'\n | 'github'\n | 'microsoft'\n | 'api_key'\n | 'twitter'\n | 'custom'\n | 'api_login'\n | 'api_basic';\n\nexport type CredentialRole = 'admin' | 'user' | 'viewer' | 'custom';\n\n/**\n * Custom HTTP headers for browser automation.\n * Stored encrypted alongside credential data.\n * Useful for bypassing bot detection (e.g., User-Agent, Accept-Language).\n */\nexport type CustomHttpHeaders = Record<string, string>;\n\n/**\n * HTTP Basic Authentication credentials for browser-level auth popups.\n * Passed to Playwright via browser.newContext({ httpCredentials }).\n */\nexport type HttpCredentials = {\n username: string;\n password: string;\n};\n\n// Individual credential data types\n\nexport type BasicAuthCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type GoogleCredential = {\n email: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type GithubCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type MicrosoftCredential = {\n email: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type ApiKeyCredential = {\n key: string;\n prefix?: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type TwitterCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type ApiLoginCredential = {\n loginFields: Record<string, string>;\n cachedToken?: string;\n cachedTokenExpiresAt?: string;\n cachedTokenObtainedAt?: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type ApiBasicCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type CustomCredential = Record<string, any> & {\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\n// Union type for all credential data\nexport type CredentialData =\n | { provider: 'basic'; data: BasicAuthCredential }\n | { provider: 'google'; data: GoogleCredential }\n | { provider: 'github'; data: GithubCredential }\n | { provider: 'microsoft'; data: MicrosoftCredential }\n | { provider: 'api_key'; data: ApiKeyCredential }\n | { provider: 'twitter'; data: TwitterCredential }\n | { provider: 'api_login'; data: ApiLoginCredential }\n | { provider: 'api_basic'; data: ApiBasicCredential }\n | { provider: 'custom'; data: CustomCredential };\n\n/**\n * Login validity tracking data for session duration prediction\n */\nexport type LoginValidityInfo = {\n /** When a login was last attempted */\n lastLoginAt: Date | null;\n /** When a login last succeeded */\n lastLoginSucceededAt: Date | null;\n /** When the session was detected as expired (requiring re-login) */\n nextLoginRequiredAt: Date | null;\n /** Calculated session validity duration in minutes */\n loginValidityDurationMinutes: number | null;\n /** Estimated session expiry based on last login + known validity */\n estimatedExpiresAt: Date | null;\n};\n\n/**\n * HeaderExtractor: declarative way to derive an HTTP header value from a\n * credential's saved storage state (Playwright cookies + localStorage +\n * extracted IndexedDB). Materialized at request time by the executor and\n * merged into the same `extraHeaders` map as `customHeaders`.\n *\n * Modern SPAs (Firebase Auth, Auth0, Clerk, …) stash JWTs in localStorage or\n * IndexedDB and rely on page JS to forward them as request headers. Playwright\n * APIRequestContext only carries cookies, so those headers go missing when we\n * fetch through the browser context. Extractors close that gap.\n */\nexport const tokenRefreshPolicySchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('firebase'),\n refreshTokenPath: z.string().default('$.value.stsTokenManager.refreshToken'),\n expirationPath: z.string().default('$.value.stsTokenManager.expirationTime'),\n accessTokenPath: z.string().default('$.value.stsTokenManager.accessToken'),\n apiKeyPath: z.string().default('$.value.apiKey'),\n }),\n]);\n\nexport type TokenRefreshPolicy = z.infer<typeof tokenRefreshPolicySchema>;\n\nconst localStorageSourceSchema = z.object({\n kind: z.literal('localStorage'),\n origin: z.string().min(1),\n key: z.string().min(1),\n jsonPath: z.string().optional(),\n});\n\nconst indexedDBSourceSchema = z.object({\n kind: z.literal('indexedDB'),\n database: z.string().min(1),\n store: z.string().min(1),\n key: z.string().min(1),\n jsonPath: z.string().min(1),\n});\n\nconst cookieSourceSchema = z.object({\n kind: z.literal('cookie'),\n domain: z.string().min(1),\n name: z.string().min(1),\n});\n\nexport const headerExtractorSourceSchema = z.discriminatedUnion('kind', [\n localStorageSourceSchema,\n indexedDBSourceSchema,\n cookieSourceSchema,\n]);\n\nexport const headerExtractorSchema = z.object({\n name: z.string().min(1),\n header: z.string().min(1).optional(),\n prefix: z.string().optional(),\n source: headerExtractorSourceSchema,\n refresh: tokenRefreshPolicySchema.optional(),\n});\n\nexport type HeaderExtractorSource = z.infer<typeof headerExtractorSourceSchema>;\nexport type HeaderExtractor = z.infer<typeof headerExtractorSchema>;\n\n/**\n * Typed view of credential.metadata. Existing fields (headerName / token*)\n * predate extractors and remain supported. `headerExtractors` is the new\n * generalized lane.\n */\nexport const credentialMetadataSchema = z\n .object({\n headerName: z.string().optional(),\n tokenHeaderName: z.string().optional(),\n tokenHeaderPrefix: z.string().optional(),\n headerExtractors: z.array(headerExtractorSchema).optional(),\n })\n .passthrough();\n\nexport type CredentialMetadata = z.infer<typeof credentialMetadataSchema>;\n\n// Extract email from credential data if available\nexport function extractEmail(provider: AuthProvider, data: any): string | null {\n switch (provider) {\n case 'basic':\n return data.username || null;\n case 'google':\n case 'microsoft':\n return data.email || null;\n case 'github':\n case 'twitter': {\n // May be username or email\n const val = data.username || null;\n return val && val.includes('@') ? val : null;\n }\n case 'api_login':\n return data.loginFields?.username || data.loginFields?.email || null;\n case 'api_basic': {\n const val = data.username || null;\n return val && val.includes('@') ? val : null;\n }\n default:\n return null;\n }\n}\n","import { z } from 'zod';\nimport type { Pagination } from './pagination.js';\nexport type { Pagination } from './pagination.js';\n\nconst clamp = (value: string, max: number): string =>\n value.length > max ? value.slice(0, max) : value;\n\nconst requiredLimitedString = (label: string, max: number) =>\n z\n .string()\n .transform((value) => value.trim())\n .pipe(z.string().min(1, `${label} is required`))\n .transform((value) => clamp(value, max));\n\nconst optionalLimitedString = (max: number) =>\n z\n .string()\n .optional()\n .nullable()\n .transform((value) => {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n if (!trimmed) return null;\n return clamp(trimmed, max);\n });\n\nconst ISO_DATETIME = z.string().datetime({ offset: true });\nconst UUID = z.string().uuid();\n\nexport const IssueSeveritySchema = z.enum(['low', 'medium', 'high', 'unknown']);\nexport type IssueSeverity = z.infer<typeof IssueSeveritySchema>;\n\nexport const IssueStatusSchema = z.enum(['open', 'closed', 'not_a_bug']);\nexport type IssueStatus = z.infer<typeof IssueStatusSchema>;\n\nexport interface IssueCategorySummary {\n id: string;\n key: string;\n label: string;\n description: string | null;\n instructions: string | null;\n}\n\nexport interface IssueCategorySetting extends IssueCategorySummary {\n isActive: boolean;\n isSystem: boolean;\n sortOrder: number;\n}\n\nexport const IssueSourceKindSchema = z.enum([\n 'crawler_analysis',\n 'exploratory_attempt',\n 'qa_page_test',\n 'workflow_test',\n 'chaos_test',\n 'adhoc_test',\n]);\nexport type IssueSourceKind = z.infer<typeof IssueSourceKindSchema>;\n\n/**\n * A single network request captured from a Playwright trace.\n * Bodies are truncated to 100KB.\n */\nexport interface NetworkRequestEntry {\n url: string;\n method: string;\n status: number | null;\n durationMs: number | null;\n requestBody: string | null;\n responseBody: string | null;\n requestHeaders: Record<string, string> | null;\n responseHeaders: Record<string, string> | null;\n contentType: string | null;\n}\n\n/**\n * A single console log entry captured from a Playwright trace.\n */\nexport interface ConsoleLogEntry {\n type: 'log' | 'warning' | 'error' | 'info' | 'debug';\n text: string;\n timestamp: number | null;\n location: string | null;\n}\n\n/**\n * A timed narration transcript segment for video overlays.\n */\nexport interface NarrationTranscriptSegment {\n startMs: number;\n endMs: number;\n text: string;\n}\n\n/**\n * Category of failure root cause — add new values to this array (type is derived).\n *\n * `app_changed` is distinct from both `software_bug` and `test_underspecified`: the\n * application's behavior/labels/flow changed versus the last passing run while the\n * authored test stayed the same, and the agent cannot verify from inside the sandbox\n * whether the change was intentional. The recommendation pattern is \"X appears to have\n * changed to Y; update the test to match if intentional.\" Reserve `software_bug` for\n * behavior deltas that are clearly defects (error page, 5xx, broken layout).\n */\nexport const FAILURE_DIAGNOSTIC_CATEGORIES = ['software_bug', 'dependency_issue', 'agent_confusion', 'platform_error', 'test_underspecified', 'data_collision', 'app_changed'] as const;\nexport type FailureDiagnosticCategory = typeof FAILURE_DIAGNOSTIC_CATEGORIES[number];\n\n/** Evidence item from diagnostic analysis */\nexport interface FailureDiagnosticEvidence {\n type:\n | 'network_error'\n | 'console_error'\n | 'visual'\n | 'agent_thought'\n | 'timeline_event'\n | 'history_comparison'\n | 'before_after';\n timestamp?: number;\n description: string;\n detail?: string;\n /** S3 storage key for an extracted video frame (visual evidence only) */\n frameStorageKey?: string;\n\n /** Before/after comparison fields */\n beforeFrameStorageKey?: string;\n beforeCaption?: string;\n beforeTimestamp?: number;\n beforeRunId?: string;\n afterFrameStorageKey?: string;\n afterCaption?: string;\n afterTimestamp?: number;\n afterRunId?: string;\n}\n\n/** A structured per-node improvement suggestion produced by the diagnostic agent */\nexport interface FlowSuggestion {\n id: string;\n orderIndex: number;\n nodeId: string;\n nodeType: string;\n title: string;\n rationale: string;\n configPatch: Record<string, unknown> | null;\n sourceRunId?: string;\n sourceAuditIds?: string[];\n evidenceIndexes?: number[];\n status: 'pending' | 'accepted' | 'dismissed';\n decidedAt?: string;\n decidedByUserId?: string;\n resolutionSource?: 'user' | 'system';\n resolutionReason?: 'accepted' | 'dismissed' | 'node_deleted' | 'workflow_stale';\n}\n\n/** Structured diagnostic analysis produced by the failure diagnostic agent */\nexport interface FailureDiagnostic {\n category: FailureDiagnosticCategory;\n confidence: number;\n /** One-sentence thesis — the QA tester reads only this 80% of the time. */\n title: string;\n /**\n * Legacy freeform Markdown narrative (kept for back-compat with already-persisted rows).\n * The current prompt emits `failurePoints` bullets instead; new diagnostics may omit `analysis`.\n */\n analysis?: string;\n /** 2–4 concise bullets describing the failure (current report shape). */\n failurePoints?: string[];\n evidence: FailureDiagnosticEvidence[];\n smokingGun?: {\n type: string;\n description: string;\n detail?: string;\n };\n recommendation: string;\n comparisonWithHistory?: string;\n /** Things the agent could not determine from available evidence (display-only for now). */\n openQuestions?: string[];\n completedAt: string;\n agentRunId?: string;\n /** Optional structured per-node improvement suggestions */\n suggestions?: FlowSuggestion[];\n}\n\nexport interface IssueOccurrence {\n id: string;\n issueId: string;\n orgId: string;\n propertyId: string;\n canonicalPageId: string | null;\n credentialId: string | null;\n sourceKind: IssueSourceKind;\n sourceId: string;\n detectedAt: string;\n primaryUrl: string;\n tldr: string | null;\n summary: string | null;\n errorDetails: string | null;\n screenshotStorageKey: string | null;\n screenshotCaption: string | null;\n traceStorageKey: string | null;\n videoStorageKey: string | null;\n /** Action timeline for video overlay synchronization */\n videoTimelineJson: unknown | null;\n /** Timed narration transcript for video overlay */\n narrationTranscriptJson: NarrationTranscriptSegment[] | null;\n consoleLogExcerpt: string | null;\n networkFailureUrl: string | null;\n networkFailureStatus: number | null;\n ticketReproSteps: string | null;\n ticketUserFacingSummary: string | null;\n ticketDiagnosticsAnalysis: string | null;\n dedupReasoning: string | null;\n /** Status of async failure diagnostic analysis */\n diagnosticStatus: string | null;\n /** Structured diagnostic analysis from the failure diagnostic agent */\n diagnosticJson: FailureDiagnostic | null;\n /** Resolved workflow IDs when sourceKind is 'workflow_test' */\n workflowSourceInfo?: { workflowId: string; workflowRunId: string } | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface IssueAssignee {\n id: string;\n displayName: string | null;\n primaryEmail: string | null;\n}\n\nexport interface IssueSummary {\n id: string;\n orgId: string;\n propertyId: string;\n credentialId: string | null;\n title: string;\n tldr: string | null;\n severity: IssueSeverity;\n category: IssueCategorySummary;\n confidence: number | null;\n firstSeenAt: string;\n lastSeenAt: string;\n occurrenceCount: number;\n status: IssueStatus;\n statusUpdatedAt: string;\n closedAt: string | null;\n isArchived: boolean;\n assignedToId: string | null;\n assignedTo: IssueAssignee | null;\n createdAt: string;\n updatedAt: string;\n latestOccurrence: IssueOccurrence | null;\n tags?: Array<{ id: string; name: string; color: string }>;\n}\n\nexport interface IssueDetail extends IssueSummary {\n occurrences: IssueOccurrence[];\n}\n\nexport interface PaginatedIssuesResponse {\n data: IssueSummary[];\n pagination: Pagination;\n}\n\nexport interface IssueDetailResponse {\n issue: IssueSummary;\n occurrences: IssueOccurrence[];\n occurrencesPagination: Pagination;\n commentCount: number;\n}\n\nexport interface PaginatedIssueOccurrencesResponse {\n data: IssueOccurrence[];\n pagination: Pagination;\n}\n\nexport interface IssueCommentAuthor {\n id: string;\n displayName: string | null;\n primaryEmail: string | null;\n}\n\nexport interface IssueComment {\n id: string;\n issueId: string;\n orgId: string;\n propertyId: string;\n authorId: string;\n author: IssueCommentAuthor | null;\n body: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface PaginatedIssueCommentsResponse {\n data: IssueComment[];\n pagination: Pagination;\n}\n\nconst TITLE_LIMIT = 512;\nconst TLDR_LIMIT = 512;\nconst URL_LIMIT = 2048;\nconst STORAGE_KEY_LIMIT = 1024;\nconst DIAGNOSTIC_LIMIT = 4000;\n\nconst NarrationTranscriptSegmentSchema = z.object({\n startMs: z.number().int().min(0),\n endMs: z.number().int().min(1),\n text: z.string().min(1),\n});\n\nexport const RecordIssueOccurrencePayloadSchema = z.object({\n orgId: UUID,\n propertyId: UUID,\n canonicalPageId: UUID.optional()\n .nullable()\n .transform((value) => value ?? null),\n credentialId: UUID.optional()\n .nullable()\n .transform((value) => value ?? null),\n issueTitle: requiredLimitedString('Issue title', TITLE_LIMIT),\n issueSeverity: IssueSeveritySchema,\n issueCategoryId: UUID.optional()\n .nullable()\n .transform((value) => value ?? null),\n issueCategoryKey: z\n .string()\n .min(1)\n .max(64)\n .optional()\n .nullable()\n .transform((value) => {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n return trimmed.length ? trimmed : null;\n }),\n issueConfidence: z\n .number()\n .int()\n .min(0)\n .max(100)\n .optional()\n .nullable()\n .transform((value) => {\n if (value === null || value === undefined) return null;\n return value;\n }),\n detectedAt: ISO_DATETIME,\n sourceKind: IssueSourceKindSchema,\n sourceId: requiredLimitedString('Source identifier', STORAGE_KEY_LIMIT),\n primaryUrl: requiredLimitedString('Primary URL', URL_LIMIT),\n summary: requiredLimitedString('Summary', DIAGNOSTIC_LIMIT),\n tldr: requiredLimitedString('TLDR', TLDR_LIMIT),\n screenshotStorageKey: requiredLimitedString('Screenshot storage key', STORAGE_KEY_LIMIT),\n screenshotCaption: optionalLimitedString(DIAGNOSTIC_LIMIT),\n traceStorageKey: optionalLimitedString(STORAGE_KEY_LIMIT),\n videoStorageKey: optionalLimitedString(STORAGE_KEY_LIMIT),\n /** Action timeline for video overlay synchronization */\n videoTimelineJson: z.unknown().optional().nullable(),\n /** Timed narration transcript for video overlay */\n narrationTranscriptJson: z.array(NarrationTranscriptSegmentSchema).optional().nullable(),\n consoleLogExcerpt: optionalLimitedString(DIAGNOSTIC_LIMIT),\n networkFailureUrl: optionalLimitedString(URL_LIMIT),\n networkFailureStatus: z\n .number()\n .int()\n .optional()\n .nullable()\n .transform((value) => value ?? null),\n /** Structured network requests captured from Playwright trace */\n networkRequestsJson: z.array(z.unknown()).optional().nullable(),\n /** Structured console logs captured from Playwright trace */\n consoleLogsJson: z.array(z.unknown()).optional().nullable(),\n ticketReproSteps: optionalLimitedString(DIAGNOSTIC_LIMIT),\n ticketUserFacingSummary: requiredLimitedString('Ticket summary', DIAGNOSTIC_LIMIT),\n ticketDiagnosticsAnalysis: requiredLimitedString('Ticket diagnostics analysis', DIAGNOSTIC_LIMIT),\n});\n\nexport type RecordIssueOccurrencePayload = z.infer<typeof RecordIssueOccurrencePayloadSchema>;\n","/**\n * Local Browser Protocol Types\n *\n * WebSocket message protocol for communication between the CLI (LocalBrowserHost)\n * and the cloud API (RemoteBrowserClient). Enables cloud agents to control a\n * browser running on the user's local machine.\n */\n\n// ============================================================================\n// Session Types\n// ============================================================================\n\nexport type LocalBrowserMode = 'playwright' | 'cdp';\n\nexport type LocalBrowserSessionStatus =\n | 'pending' // Session created, waiting for CLI to connect\n | 'connecting' // CLI is connecting\n | 'connected' // CLI connected, browser ready\n | 'running' // Agent is actively using browser\n | 'disconnected' // CLI disconnected\n | 'expired' // Session timed out\n | 'error'; // Error occurred\n\nexport type LocalBrowserSession = {\n id: string;\n orgId: string;\n createdBy: string | null;\n status: LocalBrowserSessionStatus;\n browserMode: LocalBrowserMode;\n instructions: string | null;\n startUrl: string | null;\n jobId: string | null;\n connectedAt: string | null;\n disconnectedAt: string | null;\n expiresAt: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\n// ============================================================================\n// WebSocket Message Types\n// ============================================================================\n\n/**\n * Base message envelope for all WebSocket messages.\n */\ninterface BaseMessage {\n /** Unique message ID for request/response correlation */\n id: string;\n /** Unix timestamp in milliseconds */\n timestamp: number;\n}\n\n/**\n * Command sent from cloud API to CLI to execute a browser action.\n */\nexport interface BrowserCommand extends BaseMessage {\n type: 'command';\n /** Method name from IBrowserClient interface */\n method: string;\n /** Serialized method arguments */\n args: unknown[];\n /** Optional context ID for multi-context support. Omitted = default context. */\n contextId?: string;\n}\n\n/**\n * Response from CLI to cloud API with execution result.\n */\nexport interface BrowserResponse extends BaseMessage {\n type: 'response';\n /** ID of the original command */\n requestId: string;\n /** Whether the command executed successfully */\n success: boolean;\n /** Return value from the method (if success=true) */\n result?: unknown;\n /** Error message (if success=false) */\n error?: string;\n /** Error stack trace for debugging */\n stack?: string;\n /** Echoed context ID from the originating command */\n contextId?: string;\n}\n\n/**\n * Binary data message for large payloads (screenshots, traces).\n * Data is base64-encoded to work over JSON WebSocket.\n */\nexport interface BinaryDataMessage extends BaseMessage {\n type: 'binary';\n /** ID of the original command this data belongs to */\n requestId: string;\n /** Type of binary data */\n dataType: 'screenshot' | 'video' | 'trace';\n /** Base64-encoded binary data */\n data: string;\n /** MIME type of the data */\n mimeType: string;\n}\n\n/**\n * Heartbeat message for connection health monitoring.\n */\nexport interface HeartbeatMessage extends BaseMessage {\n type: 'heartbeat';\n /** Direction of the heartbeat */\n direction: 'ping' | 'pong';\n}\n\n/**\n * Session lifecycle event message.\n */\nexport interface SessionMessage extends BaseMessage {\n type: 'session';\n /** Type of session event */\n event:\n | 'connected' // CLI successfully connected\n | 'disconnected' // CLI disconnected\n | 'browser_ready' // Browser launched and ready\n | 'browser_closed' // Browser was closed\n | 'error'; // Session error occurred\n /** Browser mode being used */\n browserMode?: LocalBrowserMode;\n /** Error message if event='error' */\n error?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Union of all WebSocket message types.\n */\nexport type LocalBrowserMessage =\n | BrowserCommand\n | BrowserResponse\n | BinaryDataMessage\n | HeartbeatMessage\n | SessionMessage;\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\nexport function isBrowserCommand(msg: unknown): msg is BrowserCommand {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as BrowserCommand).type === 'command' &&\n typeof (msg as BrowserCommand).method === 'string'\n );\n}\n\nexport function isBrowserResponse(msg: unknown): msg is BrowserResponse {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as BrowserResponse).type === 'response' &&\n typeof (msg as BrowserResponse).requestId === 'string'\n );\n}\n\nexport function isBinaryDataMessage(msg: unknown): msg is BinaryDataMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as BinaryDataMessage).type === 'binary' &&\n typeof (msg as BinaryDataMessage).data === 'string'\n );\n}\n\nexport function isHeartbeatMessage(msg: unknown): msg is HeartbeatMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as HeartbeatMessage).type === 'heartbeat'\n );\n}\n\nexport function isSessionMessage(msg: unknown): msg is SessionMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as SessionMessage).type === 'session'\n );\n}\n\n// ============================================================================\n// API Request/Response Types\n// ============================================================================\n\nexport type CreateLocalBrowserSessionRequest = {\n /** Browser mode: 'playwright' for fresh browser, 'cdp' for existing Chrome */\n browserMode?: LocalBrowserMode;\n /** Instructions for the cloud agent */\n instructions?: string | null;\n /** Initial URL to navigate to */\n startUrl?: string | null;\n};\n\nexport type CreateLocalBrowserSessionResponse = {\n /** Session ID */\n sessionId: string;\n /** WebSocket token for CLI connection */\n wsToken: string;\n /** WebSocket URL for CLI to connect to */\n wsUrl: string;\n /** Session expiration time */\n expiresAt: string;\n};\n\nexport type LocalBrowserSessionDetail = {\n session: LocalBrowserSession;\n createdBy: { id: string; name: string | null; email: string | null } | null;\n};\n\nexport type ListLocalBrowserSessionsResponse = {\n items: LocalBrowserSessionDetail[];\n pagination: {\n page: number;\n pageSize: number;\n totalItems: number;\n totalPages: number;\n };\n};\n\n// ============================================================================\n// Job Types\n// ============================================================================\n\nexport const LOCAL_BROWSER_JOB_TYPE = 'local-browser.run' as const;\n\nexport type LocalBrowserJobPayload = {\n sessionId: string;\n instructions: string;\n startUrl?: string | null;\n};\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default timeout for browser commands in milliseconds */\nexport const BROWSER_COMMAND_TIMEOUT_MS = 60_000;\n\n/** Timeout for navigation commands in milliseconds */\nexport const BROWSER_NAVIGATION_TIMEOUT_MS = 120_000;\n\n/** Timeout for screenshot commands in milliseconds */\nexport const BROWSER_SCREENSHOT_TIMEOUT_MS = 30_000;\n\n/** Session expiration time in milliseconds (1 hour) */\nexport const SESSION_EXPIRATION_MS = 60 * 60 * 1000;\n\n/** Heartbeat interval in milliseconds */\nexport const HEARTBEAT_INTERVAL_MS = 30_000;\n","/**\n * Benchmark System Types\n *\n * Shared TypeScript types for the benchmark system. Used by:\n * - `@chatsdet/benchmark` package (expectations)\n * - `@chatsdet/db` (repository types)\n * - `apps/api/src/benchmark` (service, routes, scoring)\n * - `apps/web/src/ui/pages/superadmin` (frontend UI)\n *\n * @module @chatsdet/types/benchmark\n * @see docs/api/services/benchmark.md\n */\n\n// Job types that can be benchmarked\nexport type BenchmarkJobType = 'workflow_suite';\n\n// Run status\nexport type BenchmarkRunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'timeout';\n\n// Trigger type\nexport type BenchmarkTriggerType = 'scheduled' | 'manual';\n\n// ---------------------------------------------------------------------------\n// Workflow Suite Benchmark Types\n// ---------------------------------------------------------------------------\n\nexport type BenchmarkWorkflowRunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';\n\n/**\n * Configuration for workflow suite benchmarks (stored in jobConfig jsonb)\n */\nexport interface BenchmarkWorkflowSuiteConfig {\n runsPerWorkflow?: number;\n runTimeoutMinutes?: number;\n suiteTimeoutHours?: number;\n sendSlackSummary?: boolean;\n}\n\nimport { z } from 'zod';\n\nexport const benchmarkWorkflowSuiteConfigSchema = z.object({\n runsPerWorkflow: z.number().int().min(1).max(20).optional(),\n runTimeoutMinutes: z.number().int().min(1).max(720).optional(),\n suiteTimeoutHours: z.number().int().min(1).max(24).optional(),\n sendSlackSummary: z.boolean().optional(),\n});\n\n/**\n * Per-node metrics captured during a benchmark workflow run\n */\nexport interface BenchmarkNodeMetrics {\n nodeId: string;\n nodeName: string;\n nodeType: string;\n durationMs: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n status: string;\n cacheOutcome: 'hit' | 'miss' | 'fallback' | 'skipped' | null;\n cacheHitSource: 'replay' | 'recovery' | 'l2_gap' | 'wait_shortcut' | null;\n cacheOutcomeReason: string | null;\n compilationScore?: number | null;\n gapCount?: number | null;\n repairApplied?: boolean | null;\n maxInputTokens: number | null;\n contextWindowTokens: number | null;\n}\n\n/**\n * Aggregated metrics for a single benchmark workflow run\n */\nexport interface BenchmarkWorkflowRunMetrics {\n id: string;\n benchmarkRunId: string;\n sourceWorkflowId: string;\n copyWorkflowId: string;\n workflowName: string;\n runNumber: number;\n workflowRunId: string | null;\n status: BenchmarkWorkflowRunStatus;\n isCached: boolean;\n durationMs: number | null;\n totalTokens: number | null;\n inputTokens: number | null;\n outputTokens: number | null;\n totalNodes: number | null;\n successfulNodes: number | null;\n failedNodes: number | null;\n nodeMetrics: BenchmarkNodeMetrics[] | null;\n errorMessage: string | null;\n startedAt: string | null;\n completedAt: string | null;\n createdAt: string;\n}\n\n/**\n * Result of importing a workflow into the benchmark org\n */\nexport interface BenchmarkImportResult {\n workflowId: string;\n workflowName: string;\n propertyId: string;\n propertyName: string;\n credentialMappings: Array<{\n sourceCredentialId: string;\n newCredentialId: string;\n credentialName: string;\n }>;\n}\n\n// ---------------------------------------------------------------------------\n// Environment Snapshot (captured at suite run time for comparison)\n// ---------------------------------------------------------------------------\n\n/**\n * Structured snapshot of the full execution environment captured at benchmark run time.\n * Enables multi-dimensional comparison between runs to attribute result changes.\n */\nexport interface EnvironmentSnapshot {\n /** Workflow-relevant prompt versions and content hashes */\n prompts: Record<string, { version: number; contentHash: string }>;\n /** Resolved AI provider/model configuration */\n modelConfig: {\n baseProvider: string;\n defaultModel: string;\n contextWindowTokens: number;\n providerOverride: string | null;\n modelOverride: string | null;\n };\n /** Per-workflow flow graph metadata (keyed by source workflow ID) */\n workflows: Record<\n string,\n {\n name: string;\n snapshotVersion: number;\n snapshotHash: string;\n nodeCount: number;\n edgeCount: number;\n }\n >;\n /** Benchmark config metadata at run time */\n benchmarkConfig: {\n version: number;\n provider: string | null;\n model: string | null;\n promptVersion: string | null;\n jobConfig: Record<string, unknown>;\n };\n}\n\nexport interface BenchmarkWorkflowTrendPoint {\n date: string;\n benchmarkRunId: string;\n configId: string;\n workflowName: string;\n runNumber: number;\n isCached: boolean;\n status: BenchmarkWorkflowRunStatus;\n triggerType: BenchmarkTriggerType;\n durationMs: number | null;\n totalTokens: number | null;\n successfulNodes: number | null;\n failedNodes: number | null;\n totalNodes: number | null;\n cachedSteps: number | null;\n l2RecoverySteps: number | null;\n ejectedSteps: number | null;\n agentSteps: number | null;\n skippedSteps: number | null;\n}\n","/**\n * Sandbox Task & Checklist Catalog\n *\n * Code-defined task/checklist definitions shared between frontend and backend.\n * The legacy SANDBOX_TASKS is preserved for backward compat.\n * The new checklist system uses profile-driven, DAG-ordered items.\n *\n * @module types/sandbox-tasks\n */\n\n// ---------------------------------------------------------------------------\n// Artifact types\n// ---------------------------------------------------------------------------\n\n/** Artifact types with special platform behavior (template sync, file path mapping). */\nexport type WellKnownArtifactType =\n | 'docker-compose'\n | 'dockerfile'\n | 'startup-script'\n | 'setup-script'\n | 'seed-script'\n | 'environment-config';\n\n/** Any string is a valid artifact type. Well-known types get special treatment. */\nexport type SandboxArtifactType = WellKnownArtifactType | (string & {});\n\n// ---------------------------------------------------------------------------\n// Legacy task types (backward compat)\n// ---------------------------------------------------------------------------\n\nexport interface SandboxTaskDefinition {\n id: string;\n label: string;\n description: string;\n icon: string;\n producesArtifacts: SandboxArtifactType[];\n order: number;\n /** Prompt template ID in apps/api/src/ai/prompts/ */\n promptTemplate: string;\n}\n\nexport interface SandboxTaskStatus {\n status: 'pending' | 'running' | 'completed' | 'failed';\n startedAt?: string;\n completedAt?: string;\n summary?: string;\n error?: string;\n}\n\n/** A task definition merged with its runtime status and artifact info */\nexport interface SandboxTaskWithStatus extends SandboxTaskDefinition {\n taskStatus: SandboxTaskStatus;\n artifacts: Array<{\n artifactType: string;\n filename: string;\n }>;\n}\n\n/** Task SSE events are defined in conversation-stream.ts */\nimport type { TaskArtifactPublishedEvent, TaskCompletedEvent } from './conversation-stream.js';\n\nexport type TaskStreamEvent =\n | TaskArtifactPublishedEvent\n | TaskCompletedEvent;\n\nexport const SANDBOX_TASKS: SandboxTaskDefinition[] = [\n {\n id: 'setup-environment',\n label: 'Setup Environment',\n description: 'Create Docker Compose, Dockerfile, and startup script to run the app',\n icon: 'GearSix',\n producesArtifacts: ['docker-compose', 'dockerfile', 'startup-script', 'seed-script'],\n order: 1,\n promptTemplate: 'env-template-setup-environment',\n },\n];\n\n// ---------------------------------------------------------------------------\n// Checklist system — profile-driven, DAG-ordered setup items\n// ---------------------------------------------------------------------------\n\nexport type ChecklistItemId =\n | 'provisioned'\n | 'code-cloned'\n | 'environment-analyzed'\n | 'setup-script-created'\n | 'dockerfile-created'\n | 'docker-compose-created'\n | 'server-running'\n | 'server-accessible'\n | 'adhoc-test-ready'\n | 'seed-script-created'\n | 'unit-tests-runnable'\n | 'integration-tests-runnable'\n | 'template-verified';\n\nexport interface ChecklistItemDefinition {\n id: ChecklistItemId;\n label: string;\n description: string;\n dependsOn: ChecklistItemId[];\n producesArtifacts: SandboxArtifactType[];\n agentAutomatable: boolean;\n /** Whether this item must be completed/skipped before the template can flip to verified. */\n countsTowardVerification?: boolean;\n /** Prompt template ID in apps/api/src/ai/prompts/ (only for agent-automatable items) */\n promptTemplate?: string;\n}\n\nexport type ChecklistProfile = 'minimal' | 'ci-ready' | 'full-stack';\n\nexport interface ChecklistProfileDefinition {\n id: ChecklistProfile;\n label: string;\n description: string;\n items: ChecklistItemId[];\n}\n\nexport interface ChecklistItemStatus {\n status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';\n startedAt?: string;\n completedAt?: string;\n summary?: string;\n error?: string;\n}\n\n/** A checklist item definition merged with its runtime status and artifact info */\nexport interface ChecklistItemWithStatus extends ChecklistItemDefinition {\n itemStatus: ChecklistItemStatus;\n /** True when all deps are met and item is not yet completed/running/skipped */\n actionable: boolean;\n artifacts: Array<{ artifactType: string; filename: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// Checklist item definitions (DAG)\n// ---------------------------------------------------------------------------\n\nexport const CHECKLIST_ITEMS: ChecklistItemDefinition[] = [\n {\n id: 'provisioned',\n label: 'Provisioned',\n description: 'Sandbox instance has been provisioned',\n dependsOn: [],\n producesArtifacts: [],\n agentAutomatable: false,\n },\n {\n id: 'code-cloned',\n label: 'Code Cloned',\n description: 'Repository has been cloned into the sandbox',\n dependsOn: ['provisioned'],\n producesArtifacts: [],\n agentAutomatable: false,\n },\n {\n id: 'environment-analyzed',\n label: 'Environment Analyzed',\n description: 'Repository analyzed for runtime, services, and dependencies',\n dependsOn: ['code-cloned'],\n producesArtifacts: ['environment-config'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-analyze-environment',\n },\n {\n id: 'setup-script-created',\n label: 'Setup Script Created',\n description: 'Environment setup script for installing dependencies and build prerequisites',\n dependsOn: ['environment-analyzed'],\n producesArtifacts: ['setup-script'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-setup-script',\n },\n {\n id: 'unit-tests-runnable',\n label: 'Unit Tests Runnable',\n description: 'Unit test suite can be executed successfully',\n dependsOn: ['setup-script-created'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-unit-tests',\n },\n {\n id: 'dockerfile-created',\n label: 'Dockerfile Created',\n description: 'Dockerfile generated from environment analysis',\n dependsOn: ['environment-analyzed'],\n producesArtifacts: ['dockerfile'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-dockerfile',\n },\n {\n id: 'docker-compose-created',\n label: 'Docker Compose Created',\n description: 'Docker Compose file with application and service containers',\n dependsOn: ['dockerfile-created'],\n producesArtifacts: ['docker-compose'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-docker-compose',\n },\n {\n id: 'integration-tests-runnable',\n label: 'Integration Tests Runnable',\n description: 'Integration tests can connect to services and run',\n dependsOn: ['docker-compose-created'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-integration-tests',\n },\n {\n id: 'seed-script-created',\n label: 'Seed Script Created',\n description: 'Database seed script for initial data population',\n dependsOn: ['docker-compose-created'],\n producesArtifacts: ['seed-script'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-seed-script',\n },\n {\n id: 'server-running',\n label: 'Server Running',\n description: 'Application server started via startup script',\n dependsOn: ['docker-compose-created'],\n producesArtifacts: ['startup-script'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-start-server',\n },\n {\n id: 'server-accessible',\n label: 'Server Accessible',\n description: 'Application server responds to health check requests',\n dependsOn: ['server-running'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-server',\n },\n {\n id: 'adhoc-test-ready',\n label: 'Ad Hoc Test Ready',\n description:\n 'Credential-producing sandbox action validated against a running instance for sandbox-backed ad hoc tests',\n dependsOn: ['server-accessible'],\n producesArtifacts: [],\n agentAutomatable: true,\n countsTowardVerification: false,\n promptTemplate: 'env-checklist-enable-adhoc-testing',\n },\n {\n id: 'template-verified',\n label: 'Template Verified',\n description: 'A fresh instance provisioned from this template comes up healthy',\n dependsOn: ['server-accessible'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-template',\n },\n];\n\n/** O(1) lookup by checklist item ID */\nexport const CHECKLIST_ITEM_MAP: Record<ChecklistItemId, ChecklistItemDefinition> =\n Object.fromEntries(CHECKLIST_ITEMS.map((item) => [item.id, item])) as Record<\n ChecklistItemId,\n ChecklistItemDefinition\n >;\n\n// ---------------------------------------------------------------------------\n// Profile definitions\n// ---------------------------------------------------------------------------\n\nconst ALL_CHECKLIST_ITEM_IDS: ChecklistItemId[] = CHECKLIST_ITEMS.map((item) => item.id);\n\nexport const CHECKLIST_PROFILES: Record<ChecklistProfile, ChecklistProfileDefinition> = {\n minimal: {\n id: 'minimal',\n label: 'Minimal',\n description: 'Provisioned sandbox with code cloned — git access only',\n items: ['provisioned', 'code-cloned'],\n },\n 'ci-ready': {\n id: 'ci-ready',\n label: 'CI Ready',\n description: 'Code cloned with unit tests runnable — commit PRs and let CI handle the rest',\n items: ['provisioned', 'code-cloned', 'environment-analyzed', 'setup-script-created', 'unit-tests-runnable'],\n },\n 'full-stack': {\n id: 'full-stack',\n label: 'Full Stack',\n description: 'Complete containerized environment with all services, tests, and server running',\n items: ALL_CHECKLIST_ITEM_IDS,\n },\n};\n\nexport function getVerificationChecklistItemIds(profile: ChecklistProfile): ChecklistItemId[] {\n const profileItems = new Set(CHECKLIST_PROFILES[profile].items);\n return CHECKLIST_ITEMS.filter(\n (item) => profileItems.has(item.id) && item.countsTowardVerification !== false\n ).map((item) => item.id);\n}\n","/**\n * Macro system shared types and catalog.\n *\n * @module macros\n * @see apps/docs/src/content/docs/reference/macros.mdx\n *\n * Macros allow dynamic data generation in workflow test inputs.\n * Format: $MACRO_NAME(args) — resolved at execution time to fresh values.\n */\n\nexport interface MacroCatalogEntry {\n /** Macro name (uppercase, e.g. \"UUID\") */\n name: string;\n /** Human-readable description */\n description: string;\n /** Argument definitions (empty for no-arg macros) */\n args: Array<{\n name: string;\n type: 'number' | 'string';\n description: string;\n }>;\n /** Example resolved output */\n example: string;\n}\n\n/**\n * Full catalog of available macros — shared by backend resolver and frontend typeahead.\n */\nexport const MACRO_CATALOG: MacroCatalogEntry[] = [\n {\n name: 'UUID',\n description: 'Generate a random UUID v4',\n args: [],\n example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',\n },\n {\n name: 'NOW',\n description: 'Current ISO 8601 timestamp',\n args: [],\n example: '2026-02-19T14:30:00.000Z',\n },\n {\n name: 'TODAY',\n description: 'Current date (YYYY-MM-DD)',\n args: [],\n example: '2026-02-19',\n },\n {\n name: 'DATE',\n description: 'Date offset from today by N days (negative for past, positive for future)',\n args: [{ name: 'days', type: 'number', description: 'Number of days offset (e.g. -7, +30)' }],\n example: '2026-02-26',\n },\n {\n name: 'TIMESTAMP',\n description: 'Current Unix timestamp in milliseconds',\n args: [],\n example: '1771520400000',\n },\n {\n name: 'RANDOM_INT',\n description: 'Random integer in range (inclusive)',\n args: [\n { name: 'min', type: 'number', description: 'Minimum value' },\n { name: 'max', type: 'number', description: 'Maximum value' },\n ],\n example: '42',\n },\n {\n name: 'RANDOM_STRING',\n description: 'Random alphanumeric string',\n args: [{ name: 'length', type: 'number', description: 'Character count' }],\n example: 'xK9mQ',\n },\n {\n name: 'EMAIL',\n description: 'Generate a unique test email address',\n args: [],\n example: 'test-a1b2@example-test.com',\n },\n {\n name: 'FIRST_NAME',\n description: 'Random first name',\n args: [],\n example: 'Jordan',\n },\n {\n name: 'LAST_NAME',\n description: 'Random last name',\n args: [],\n example: 'Chen',\n },\n {\n name: 'PHONE',\n description: 'Random US phone number',\n args: [],\n example: '+1-555-382-7491',\n },\n {\n name: 'SEQUENCE',\n description: 'Auto-incrementing counter (resets per run)',\n args: [{ name: 'name', type: 'string', description: 'Counter name' }],\n example: '1',\n },\n {\n name: 'AI',\n description: 'AI-generated string for the given prompt (resolved once per run, cached by prompt)',\n args: [{ name: 'prompt', type: 'string', description: 'Quoted description of what to generate, e.g. \"email subject\"' }],\n example: 'Quick question about your refund',\n },\n];\n\n/** Names of all valid macros, derived from MACRO_CATALOG. */\nexport const VALID_MACRO_NAMES = new Set(MACRO_CATALOG.map((m) => m.name));\n\n/**\n * Validate all macro expressions in a string. Returns array of invalid macro names.\n * Empty array = all macros valid (or no macros present).\n */\nexport function findInvalidMacros(value: string): string[] {\n const pattern = new RegExp(MACRO_PATTERN.source, MACRO_PATTERN.flags);\n const invalid: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(value)) !== null) {\n const name = match[1]!;\n if (!VALID_MACRO_NAMES.has(name)) {\n invalid.push(name);\n }\n }\n return [...new Set(invalid)];\n}\n\n/** Expected arg count per macro, derived from MACRO_CATALOG. */\nconst MACRO_ARG_COUNTS = new Map(MACRO_CATALOG.map((m) => [m.name, m.args.length]));\n\n/**\n * Normalize macro expressions in a string by stripping extra arguments\n * beyond what the catalog defines. LLMs sometimes add extra hints\n * (e.g. \"$RANDOM_STRING(10, alphanumeric)\") that crash the resolver.\n */\nexport function normalizeMacroArgs(value: string): string {\n const pattern = new RegExp(MACRO_PATTERN.source, MACRO_PATTERN.flags);\n return value.replace(pattern, (fullMatch, name: string, argsStr: string) => {\n const expectedArgCount = MACRO_ARG_COUNTS.get(name);\n if (expectedArgCount === undefined) return fullMatch;\n const args = argsStr.length > 0 ? argsStr.split(',') : [];\n if (args.length <= expectedArgCount) return fullMatch;\n const trimmed = args.slice(0, expectedArgCount).join(',');\n return `$${name}(${trimmed})`;\n });\n}\n\n/** Regex for matching macro expressions. Negative lookbehind for \\ handles escaping. */\nexport const MACRO_PATTERN = /(?<!\\\\)\\$([A-Z_]+)\\(([^)]*)\\)/g;\n\n/** Regex for matching $var.{name} variable references (simple, dotted, and step-qualified). */\nexport const VARIABLE_PATTERN = /(?<!\\\\)\\$var\\.(\\w+(?:\\.\\w+)*)/g;\n\n/**\n * Regex for matching step-qualified variable references: `$var.StepN_Label.name`.\n * Group 1 = step qualifier (e.g. \"Step2_Create_Client\"), Group 2 = variable name.\n */\nexport const QUALIFIED_VARIABLE_PATTERN = /(?<!\\\\)\\$var\\.(\\w+)\\.(\\w+)/g;\n\n// ── Typeahead entry types (used by macro/variable dropdown) ──\n\nexport type VariableSource = 'inputValue' | 'macro' | 'extracted' | 'credential' | 'seed' | 'workflowInput' | 'unknown';\n\nexport interface VariableTypeaheadEntry {\n kind: 'variable';\n /** Variable name without $var. prefix (e.g. \"project_id\") */\n name: string;\n source: VariableSource;\n description: string;\n /** Step-qualified name for disambiguation (e.g. \"Step2_Create_Client.client\") */\n qualifiedName?: string;\n /** Human-readable step label (e.g. \"Step 2: Create Client\") */\n stepLabel?: string;\n /** Design-time template or expression (e.g. \"Acme Test - $RANDOM_STRING(6)\") */\n designTimeValue?: string;\n}\n\nexport interface MacroTypeaheadEntry extends MacroCatalogEntry {\n kind: 'macro';\n}\n\nexport type TypeaheadEntry = MacroTypeaheadEntry | VariableTypeaheadEntry;\n\n/** Map macro names to the variable keys they produce at runtime. */\nexport const MACRO_TO_VAR_KEY: Record<string, string> = {\n UUID: 'uuid',\n EMAIL: 'email',\n FIRST_NAME: 'first_name',\n LAST_NAME: 'last_name',\n PHONE: 'phone',\n NOW: 'now',\n TODAY: 'today',\n DATE: 'date',\n TIMESTAMP: 'timestamp',\n RANDOM_INT: 'random_int',\n RANDOM_STRING: 'random_string',\n SEQUENCE: 'sequence',\n AI: 'ai',\n};\n\n/**\n * Scan a string for all `$var.{name}` references, returning the variable names.\n * Also handles step-qualified references like `$var.Step2_Create_Client.name`,\n * extracting just the variable name for backward-compatible tracking.\n */\nexport function scanStringForVarRefs(text: string): string[] {\n const names: string[] = [];\n\n // First extract step-qualified references ($var.Step2_Label.name)\n const qualifiedPositions = new Set<number>();\n QUALIFIED_VARIABLE_PATTERN.lastIndex = 0;\n let qMatch: RegExpExecArray | null;\n while ((qMatch = QUALIFIED_VARIABLE_PATTERN.exec(text)) !== null) {\n if (/^Step\\d+/.test(qMatch[1]!)) {\n names.push(qMatch[2]!);\n qualifiedPositions.add(qMatch.index);\n }\n }\n\n // Then extract all references, skipping positions already matched as step-qualified\n VARIABLE_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = VARIABLE_PATTERN.exec(text)) !== null) {\n if (!qualifiedPositions.has(match.index)) {\n names.push(match[1]!);\n }\n }\n return names;\n}\n\n/** Scan a string for all `$MACRO()` expressions, returning the macro names. */\nexport function scanStringForMacros(text: string): string[] {\n const names: string[] = [];\n MACRO_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = MACRO_PATTERN.exec(text)) !== null) {\n names.push(match[1]!);\n }\n return names;\n}\n","/**\n * Shared zod schemas for workflow node config primitives.\n *\n * Lives in @chatsdet/types so both `apps/api` (server-side validation +\n * tool schemas) and `packages/canary-cli` (the spec compiler) can import\n * the same source of truth without canary-cli taking a dependency on apps/api.\n *\n * @module workflow-node-schemas\n */\n\nimport { z } from 'zod';\n\nexport const FormFieldSchema = z.object({\n name: z\n .string()\n .describe('Unique field identifier (snake_case, e.g., \"search_query\", \"recipient_email\")'),\n label: z.string().describe('Human-friendly label (e.g., \"Search Query\", \"Recipient Email\")'),\n type: z\n .enum([\n 'text',\n 'email',\n 'password',\n 'number',\n 'textarea',\n 'select',\n 'checkbox',\n 'multiselect',\n 'file',\n ])\n .optional()\n .describe('Input type, defaults to \"text\"'),\n required: z.boolean().optional().describe('Whether this field is required'),\n value: z.string().describe('The value to use for this field'),\n options: z\n .array(z.object({ value: z.string(), label: z.string() }))\n .optional()\n .describe('If type is select/multiselect, provide allowed options here'),\n});\n\nexport const InlineAssertionSchema = z.object({\n condition: z.string().describe('What to verify after the action'),\n severity: z\n .enum(['warn', 'fail'])\n .describe('warn = log and continue on failure, fail = stop the test'),\n agentic: z\n .boolean()\n .optional()\n .describe('true if requires visual/complex evaluation that cannot be cached'),\n});\n\nexport const NodeTypeEnum = z.enum([\n 'login',\n 'navigate',\n 'action',\n 'assertion',\n 'seed',\n 'setup',\n 'wait',\n 'condition',\n 'api_sequence',\n 'end',\n 'notification',\n 'audio',\n 'audio-assertion',\n]);\n\nexport type NodeType = z.infer<typeof NodeTypeEnum>;\nexport type FormField = z.infer<typeof FormFieldSchema>;\nexport type InlineAssertion = z.infer<typeof InlineAssertionSchema>;\n","/**\n * Browser viewport presets for agent browser sessions.\n *\n * Organizations pick a default preset (Settings → Agent Browser) and individual\n * runs may override it (Flow Designer → Run with config). The stored value is\n * the preset *key*; `resolveBrowserViewport` maps it to concrete dimensions\n * before reaching `PlaywrightClient.connect({ viewport })`.\n *\n * The `desktop` default (1280×800) matches `DEFAULT_VIEWPORT` in\n * `@chatsdet/browser-core` — the resolution recommended for AI computer-use\n * models.\n *\n * @module browser-viewport\n */\n\n/** Device family for a preset — drives the icon shown in the visual picker. */\nexport type BrowserViewportDevice = 'desktop' | 'laptop' | 'tablet' | 'mobile';\n\nexport interface BrowserViewportPresetSpec {\n /** Human-friendly label shown in the UI. */\n label: string;\n width: number;\n height: number;\n device: BrowserViewportDevice;\n}\n\nexport const BROWSER_VIEWPORT_PRESETS = {\n desktop: { label: 'Desktop', width: 1280, height: 800, device: 'desktop' },\n desktop_large: { label: 'Large desktop', width: 1920, height: 1080, device: 'desktop' },\n laptop: { label: 'Laptop', width: 1440, height: 900, device: 'laptop' },\n tablet: { label: 'Tablet', width: 768, height: 1024, device: 'tablet' },\n mobile: { label: 'Mobile', width: 390, height: 844, device: 'mobile' },\n} as const satisfies Record<string, BrowserViewportPresetSpec>;\n\nexport type BrowserViewportPreset = keyof typeof BROWSER_VIEWPORT_PRESETS;\n\nexport const DEFAULT_BROWSER_VIEWPORT_PRESET: BrowserViewportPreset = 'desktop';\n\nexport const BROWSER_VIEWPORT_PRESET_KEYS = Object.keys(\n BROWSER_VIEWPORT_PRESETS\n) as BrowserViewportPreset[];\n\n/** Type guard: is `value` a known preset key? */\nexport function isBrowserViewportPreset(value: unknown): value is BrowserViewportPreset {\n return typeof value === 'string' && value in BROWSER_VIEWPORT_PRESETS;\n}\n\n/**\n * Map a stored preset key (possibly null/unknown) to concrete viewport\n * dimensions, falling back to the desktop default.\n */\nexport function resolveBrowserViewport(preset: string | null | undefined): {\n width: number;\n height: number;\n} {\n const spec = isBrowserViewportPreset(preset)\n ? BROWSER_VIEWPORT_PRESETS[preset]\n : BROWSER_VIEWPORT_PRESETS[DEFAULT_BROWSER_VIEWPORT_PRESET];\n return { width: spec.width, height: spec.height };\n}\n"],"mappings":"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","names":["z","z","z","z","z","z"]}
|
|
@@ -1 +0,0 @@
|
|
|
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' | 'deprecated' | 'ready_for_cleanup';\n\nexport function toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\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(argv: string[]): LifecycleStage {\n const stage = getArgValue(argv, '--stage');\n if (!stage || !['active', 'deprecated', 'ready_for_cleanup'].includes(stage)) {\n console.error(\n 'Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup'\n );\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 loginUrl?: string;\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 +0,0 @@
|
|
|
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 const result = await extractText(uint8, { mergePages: true });\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 _logger.info('[PdfExtract] Text extracted successfully', {\n textLength: text.length,\n totalPages,\n sizeBytes: buffer.length,\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","/**\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":";;;AASA,YAAY,QAAQ;;;ACWb,IAAM,gBAA+B;AAAA,EAC1C,OAAO,CAAC,KAAK,SAAS;AACpB,QAAI,KAAM,SAAQ,MAAM,KAAK,IAAI;AAAA,QAC5B,SAAQ,MAAM,GAAG;AAAA,EACxB;AAAA,EACA,MAAM,CAAC,KAAK,SAAS;AACnB,QAAI,KAAM,SAAQ,KAAK,KAAK,IAAI;AAAA,QAC3B,SAAQ,KAAK,GAAG;AAAA,EACvB;AAAA,EACA,MAAM,CAAC,KAAK,SAAS;AACnB,QAAI,KAAM,SAAQ,KAAK,KAAK,IAAI;AAAA,QAC3B,SAAQ,KAAK,GAAG;AAAA,EACvB;AAAA,EACA,OAAO,CAAC,KAAK,SAAS;AACpB,QAAI,KAAM,SAAQ,MAAM,KAAK,IAAI;AAAA,QAC5B,SAAQ,MAAM,GAAG;AAAA,EACxB;AACF;;;ADzBA,IAAI,UAAyB;AAEtB,SAAS,oBAAoB,GAAwB;AAC1D,YAAU;AACZ;AAEA,IAAM,sBAAsB,KAAK,OAAO;AACxC,IAAM,mBAAmB;AAMzB,eAAsB,yBACpB,QAC+C;AAC/C,MAAI,OAAO,SAAS,qBAAqB;AACvC,YAAQ,KAAK,kDAAkD;AAAA,MAC7D,WAAW,OAAO;AAAA,MAClB,UAAU;AAAA,IACZ,CAAC;AACD,WAAO;AAAA,MACL,MAAM,uCAAuC,YAAY,OAAO,MAAM,CAAC,YAAY,YAAY,mBAAmB,CAAC;AAAA,MACnH,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAO;AAC5C,QAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,QAAM,SAAS,MAAM,YAAY,OAAO,EAAE,YAAY,KAAK,CAAC;AAE5D,MAAI,OAAO,OAAO,QAAQ;AAC1B,QAAM,aAAa,OAAO,cAAc;AAExC,MAAI,KAAK,SAAS,kBAAkB;AAClC,YAAQ,KAAK,0CAA0C;AAAA,MACrD,gBAAgB,KAAK;AAAA,MACrB,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,WAAO,KAAK,MAAM,GAAG,gBAAgB,IAAI;AAAA;AAAA,qBAA0B,gBAAgB;AAAA,EACrF;AAEA,UAAQ,KAAK,4CAA4C;AAAA,IACvD,YAAY,KAAK;AAAA,IACjB;AAAA,IACA,WAAW,OAAO;AAAA,EACpB,CAAC;AAED,SAAO,EAAE,MAAM,WAAW;AAC5B;AAKA,eAAsB,eACpB,UAC+C;AAC/C,QAAMA,QAAO,MAAS,QAAK,QAAQ;AAEnC,MAAIA,MAAK,OAAO,qBAAqB;AACnC,YAAQ,KAAK,uDAAuD;AAAA,MAClE;AAAA,MACA,WAAWA,MAAK;AAAA,MAChB,UAAU;AAAA,IACZ,CAAC;AACD,WAAO;AAAA,MACL,MAAM,uCAAuC,YAAYA,MAAK,IAAI,CAAC,YAAY,YAAY,mBAAmB,CAAC;AAAA,MAC/G,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,SAAS,MAAS,YAAS,QAAQ;AACzC,SAAO,yBAAyB,OAAO,KAAK,MAAM,CAAC;AACrD;AAEA,SAAS,YAAY,OAAuB;AAC1C,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC9C;","names":["stat"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/credential.ts"],"sourcesContent":["/**\n * CLI Credential Commands\n *\n * List, inspect, and update credentials for workflow authoring.\n *\n * @module credential\n */\n\nimport process from 'node:process';\nimport { createParser, type EventSourceMessage } from 'eventsource-parser';\nimport { resolveConfig, getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, fetchProperties, getApplicationArg, type CredentialListItem } from './cli-helpers.js';\nimport { apiFetch, exitOnFailure } from './api-fetch.js';\n\nasync function fetchCredentials(apiUrl: string, token: string): Promise<CredentialListItem[]> {\n const result = await apiFetch(`${apiUrl}/org/credentials`, {\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n\n if (!result.ok) exitOnFailure(result.failure);\n\n const data = (await result.response.json()) as { ok: boolean; items: CredentialListItem[] };\n return data.items ?? [];\n}\n\nfunction printCredentialHelp(): void {\n console.log(\n [\n 'Usage: canary credential <sub-command> [options]',\n '',\n 'Sub-commands:',\n ' list [options] List credentials with environment info',\n ' get <credId|name> [options] Print credential login bundle (URL + instructions + username/password)',\n ' create [options] Create a new credential',\n ' update <credId> [options] Update credential fields',\n ' test <credId> [options] Run the browser login test for a credential',\n '',\n 'List options:',\n ' --application <name> Filter by application name (alias: --property)',\n ' --json Output raw JSON',\n '',\n 'Create options:',\n ' --application <name|id> Application to attach to (required; alias: --property)',\n ' --name <name> Credential name (required)',\n ' --login-url <url> Login URL',\n ' --login-instructions <text> Login instructions',\n ' --username <user> Username (stored encrypted)',\n ' --password <pw> Password (or set CANARY_CRED_PASSWORD)',\n ' --api-key <key> API key (or set CANARY_CRED_API_KEY)',\n ' --mfa-secret <secret> MFA seed (or set CANARY_CRED_MFA_SECRET)',\n ' --json Output raw JSON',\n '',\n 'Update options:',\n ' --login-url <url> Set the login URL',\n ' --name <name> Set the credential name',\n ' --role <role> Set the role (admin, user, viewer, custom)',\n ' --login-instructions <text> Set login instructions',\n ' --json Output raw JSON',\n '',\n 'Test options:',\n ' --application <name|id> Disambiguate when <credId> is a name (alias: --property)',\n ' --json Stream raw SSE events as JSON',\n '',\n 'Common options:',\n ' --env <env> Target environment (prod, dev, local)',\n ' --api-url <url> API URL override',\n ' --token <key> API token override',\n ].join('\\n')\n );\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonFlag = hasFlag(argv, '--json');\n const propertyFilter = getApplicationArg(argv);\n\n let items = await fetchCredentials(apiUrl, token);\n\n if (propertyFilter) {\n const lower = propertyFilter.toLowerCase();\n items = items.filter(\n (c) =>\n c.propertyId === propertyFilter ||\n (c.propertyName && c.propertyName.toLowerCase().includes(lower))\n );\n }\n\n // Filter out ephemeral credentials for cleaner output\n items = items.filter((c) => !(c as unknown as { isEphemeral?: boolean }).isEphemeral);\n\n if (jsonFlag) {\n console.log(JSON.stringify(items, null, 2));\n return;\n }\n\n if (items.length === 0) {\n console.log('No credentials found.');\n return;\n }\n\n console.log(`\\nCredentials (${items.length}):\\n`);\n for (const c of items) {\n const property = c.propertyName ?? c.propertyId;\n const env = c.environmentName ? ` | ${c.environmentName}` : '';\n const baseUrl = c.propertyBaseUrl ? ` | ${c.propertyBaseUrl}` : '';\n const loginUrl = c.loginUrl ? ` Login URL: ${c.loginUrl}\\n` : '';\n console.log(` ${c.name}`);\n console.log(` ID: ${c.id}`);\n console.log(` Application: ${property}${env}${baseUrl}`);\n if (loginUrl) process.stdout.write(loginUrl);\n console.log('');\n }\n}\n\nasync function handleUpdate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const credId = argv.find((a) => !a.startsWith('-'));\n if (!credId) {\n console.error('Error: Missing credential ID.');\n console.error('Usage: canary credential update <credId> --login-url <url> [--name <name>] [--role <role>]');\n process.exit(1);\n }\n\n const loginUrl = getArgValue(argv, '--login-url');\n const name = getArgValue(argv, '--name');\n const role = getArgValue(argv, '--role');\n const loginInstructions = getArgValue(argv, '--login-instructions');\n const jsonOutput = hasFlag(argv, '--json');\n\n if (!loginUrl && !name && !role && !loginInstructions) {\n console.error(\n 'Error: No fields to update. Provide at least one of: --login-url, --name, --role, --login-instructions'\n );\n process.exit(1);\n }\n\n const validRoles = ['admin', 'user', 'viewer', 'custom'];\n if (role && !validRoles.includes(role)) {\n console.error(`Error: Invalid role \"${role}\". Must be one of: ${validRoles.join(', ')}`);\n process.exit(1);\n }\n\n // Auto-resolve propertyId from credential list\n const credentials = await fetchCredentials(apiUrl, token);\n const credential = credentials.find((c) => c.id === credId);\n if (!credential) {\n console.error(`Error: Credential ${credId} not found.`);\n if (credentials.length > 0) {\n console.error('\\nAvailable credentials:');\n for (const c of credentials.slice(0, 10)) {\n console.error(` ${c.id} ${c.name} (${c.propertyName ?? c.propertyId})`);\n }\n }\n process.exit(1);\n }\n\n const body: Record<string, unknown> = {};\n if (loginUrl !== undefined) body.loginUrl = loginUrl;\n if (name !== undefined) body.name = name;\n if (role !== undefined) body.role = role;\n if (loginInstructions !== undefined) body.loginInstructions = loginInstructions;\n\n const result = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'PUT',\n `/org/properties/${credential.propertyId}/credentials/${credId}`,\n body\n );\n\n if (!result.ok) {\n if (jsonOutput) {\n console.log(JSON.stringify({ ok: false, error: result.error ?? 'Unknown error' }, null, 2));\n } else {\n console.error(`Error: ${result.error ?? 'Unknown error'}`);\n }\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify({ ok: true, credentialId: credId, updated: body }, null, 2));\n return;\n }\n\n console.log(`Updated \"${credential.name}\" (${credId}):`);\n if (loginUrl !== undefined) {\n console.log(` loginUrl: ${credential.loginUrl ?? '(none)'} -> ${loginUrl}`);\n }\n if (name !== undefined) {\n console.log(` name: ${credential.name} -> ${name}`);\n }\n if (role !== undefined) {\n console.log(` role: -> ${role}`);\n }\n if (loginInstructions !== undefined) {\n console.log(` loginInstructions: updated`);\n }\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, '--json');\n const propertyArg = getApplicationArg(argv);\n const name = getArgValue(argv, '--name');\n const loginUrl = getArgValue(argv, '--login-url');\n const loginInstructions = getArgValue(argv, '--login-instructions');\n const username = getArgValue(argv, '--username');\n const password = getArgValue(argv, '--password') ?? process.env.CANARY_CRED_PASSWORD;\n const apiKey = getArgValue(argv, '--api-key') ?? process.env.CANARY_CRED_API_KEY;\n const mfaSecret = getArgValue(argv, '--mfa-secret') ?? process.env.CANARY_CRED_MFA_SECRET;\n\n if (!propertyArg || !name) {\n console.error('Error: --application and --name are required (alias: --property).');\n console.error('Usage: canary credential create --application <name|id> --name <name> [options]');\n process.exit(1);\n }\n\n const properties = await fetchProperties(apiUrl, token);\n const property = properties.find(\n (p) => p.id === propertyArg || p.name.toLowerCase() === propertyArg.toLowerCase()\n );\n if (!property) {\n console.error(`Error: Application \"${propertyArg}\" not found.`);\n if (properties.length > 0) {\n console.error('Available applications:');\n for (const p of properties) console.error(` - ${p.name} (${p.id})`);\n }\n process.exit(1);\n }\n\n const data: Record<string, unknown> = {};\n if (username !== undefined) data.username = username;\n if (password !== undefined) data.password = password;\n if (apiKey !== undefined) data.apiKey = apiKey;\n if (mfaSecret !== undefined) data.mfaSecret = mfaSecret;\n\n const body: Record<string, unknown> = { name, data };\n if (loginUrl !== undefined) body.loginUrl = loginUrl;\n if (loginInstructions !== undefined) body.loginInstructions = loginInstructions;\n\n const result = await apiRequest<{\n ok: boolean;\n id?: string;\n credential?: { id: string };\n error?: string;\n }>(apiUrl, token, 'POST', `/org/properties/${property.id}/credentials`, body);\n\n if (!result.ok) {\n if (jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.error(`Error: ${result.error ?? 'Unknown error'}`);\n }\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(`Created credential \"${name}\" (${result.id}) on application \"${property.name}\"`);\n}\n\ntype CredentialDetail = {\n id: string;\n name: string;\n authProvider?: string;\n loginUrl?: string | null;\n loginInstructions?: string | null;\n postLoginStartUrl?: string | null;\n data?: {\n username?: string;\n password?: string;\n email?: string;\n user?: string;\n mfaSecret?: string;\n apiKey?: string;\n customHeaders?: Record<string, string>;\n [key: string]: unknown;\n } | null;\n};\n\nasync function fetchCredentialDetail(\n apiUrl: string,\n token: string,\n propertyId: string,\n credId: string\n): Promise<CredentialDetail | null> {\n const result = await apiRequest<{ ok: boolean; credential?: CredentialDetail; error?: string }>(\n apiUrl,\n token,\n 'GET',\n `/org/properties/${propertyId}/credentials/${credId}`\n );\n if (!result.ok || !result.credential) return null;\n return result.credential;\n}\n\nasync function handleGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, '--json');\n const propertyArg = getApplicationArg(argv);\n const credArg = argv.find((a) => !a.startsWith('-'));\n\n if (!credArg) {\n console.error('Error: Missing credential ID or name.');\n console.error('Usage: canary credential get <credId|name> [--application <name|id>] [--json]');\n process.exit(1);\n }\n\n const credentials = await fetchCredentials(apiUrl, token);\n const candidates = credentials.filter(\n (c) => c.id === credArg || c.name.toLowerCase() === credArg.toLowerCase()\n );\n\n let credSummary: CredentialListItem | undefined;\n if (candidates.length === 0) {\n console.error(`Error: Credential \"${credArg}\" not found.`);\n process.exit(1);\n } else if (candidates.length === 1) {\n credSummary = candidates[0];\n } else {\n if (!propertyArg) {\n console.error(`Error: Multiple credentials match \"${credArg}\". Pass --application to disambiguate.`);\n for (const c of candidates) {\n console.error(` - ${c.id} (${c.propertyName ?? c.propertyId})`);\n }\n process.exit(1);\n }\n const lower = propertyArg.toLowerCase();\n credSummary = candidates.find(\n (c) => c.propertyId === propertyArg || (c.propertyName && c.propertyName.toLowerCase() === lower)\n );\n if (!credSummary) {\n console.error(`Error: No credential named \"${credArg}\" on application \"${propertyArg}\".`);\n process.exit(1);\n }\n }\n\n const detail = await fetchCredentialDetail(apiUrl, token, credSummary.propertyId, credSummary.id);\n if (!detail) {\n console.error(`Error: Failed to load credential ${credSummary.id}.`);\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify(detail, null, 2));\n return;\n }\n\n const data = detail.data ?? {};\n const username = data.username ?? data.email ?? data.user ?? '';\n console.log(`Credential: ${detail.name} (${detail.id})`);\n console.log(` Application: ${credSummary.propertyName ?? credSummary.propertyId}`);\n console.log(` Auth provider: ${detail.authProvider ?? '(unknown)'}`);\n console.log(` Login URL: ${detail.loginUrl ?? '(not set)'}`);\n console.log(` Post-login URL: ${detail.postLoginStartUrl ?? '(not set)'}`);\n if (username) console.log(` Username: ${username}`);\n if (data.password) console.log(` Password: ${data.password}`);\n if (data.mfaSecret) console.log(` MFA secret: ${data.mfaSecret}`);\n console.log('');\n console.log(' Login instructions:');\n if (detail.loginInstructions) {\n for (const line of detail.loginInstructions.split('\\n')) {\n console.log(` ${line}`);\n }\n } else {\n console.log(' (none)');\n }\n}\n\nasync function handleTest(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, '--json');\n const propertyArg = getApplicationArg(argv);\n const credArg = argv.find((a) => !a.startsWith('-'));\n\n if (!credArg) {\n console.error('Error: Missing credential ID or name.');\n console.error('Usage: canary credential test <credId> [--application <name|id>]');\n process.exit(1);\n }\n\n const credentials = await fetchCredentials(apiUrl, token);\n const candidates = credentials.filter(\n (c) => c.id === credArg || c.name.toLowerCase() === credArg.toLowerCase()\n );\n let credSummary: CredentialListItem | undefined;\n if (candidates.length === 0) {\n console.error(`Error: Credential \"${credArg}\" not found.`);\n process.exit(1);\n } else if (candidates.length === 1) {\n credSummary = candidates[0];\n } else {\n if (!propertyArg) {\n console.error(`Error: Multiple credentials match \"${credArg}\". Pass --application to disambiguate.`);\n for (const c of candidates) {\n console.error(` - ${c.id} (${c.propertyName ?? c.propertyId})`);\n }\n process.exit(1);\n }\n const lower = propertyArg.toLowerCase();\n credSummary = candidates.find(\n (c) => c.propertyId === propertyArg || (c.propertyName && c.propertyName.toLowerCase() === lower)\n );\n if (!credSummary) {\n console.error(`Error: No credential named \"${credArg}\" on application \"${propertyArg}\".`);\n process.exit(1);\n }\n }\n\n const detail = await fetchCredentialDetail(apiUrl, token, credSummary.propertyId, credSummary.id);\n if (!detail) {\n console.error(`Error: Failed to load credential ${credSummary.id}.`);\n process.exit(1);\n }\n\n if (detail.authProvider === 'api_key') {\n console.error(\n `Error: Credential \"${detail.name}\" uses the api_key auth provider. The browser login test is only valid for browser credentials.`\n );\n process.exit(1);\n }\n\n const startRes = await apiRequest<{ ok: boolean; streamId?: string; error?: string }>(\n apiUrl,\n token,\n 'POST',\n `/org/properties/${credSummary.propertyId}/credentials/${credSummary.id}/mcp-test/browser-stream`\n );\n\n if (!startRes.ok || !startRes.streamId) {\n console.error(`Error: ${startRes.error ?? 'Failed to start browser login test'}`);\n process.exit(1);\n }\n\n const { streamId } = startRes;\n if (!jsonOutput) {\n console.log(`Starting browser login test for \"${detail.name}\" (stream ${streamId})...`);\n }\n\n const streamRes = await fetch(`${apiUrl}/browser-stream/${streamId}/stream`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'text/event-stream' },\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 let exitCode = 0;\n let done = false;\n\n const parser = createParser({\n onEvent: (event: EventSourceMessage) => {\n if (!event.data) return;\n let data: unknown;\n try {\n data = JSON.parse(event.data);\n } catch {\n return;\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify({ event: event.event, data }));\n } else if (event.event === 'browser.action') {\n const action = data as { type?: string; description?: string };\n console.log(` → ${action.type ?? 'action'}${action.description ? `: ${action.description}` : ''}`);\n } else if (event.event === 'browser.status') {\n const status = data as { status?: string; message?: string };\n if (status.status) {\n console.log(`[${status.status}]${status.message ? ` ${status.message}` : ''}`);\n }\n }\n\n if (event.event === 'browser.status') {\n const status = data as { status?: string };\n if (status.status === 'disconnected' || status.status === 'error') {\n done = true;\n if (status.status === 'error') exitCode = 1;\n }\n }\n },\n });\n\n const reader = streamRes.body.getReader();\n const decoder = new TextDecoder();\n while (!done) {\n const { value, done: streamDone } = await reader.read();\n if (streamDone) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n\n process.exit(exitCode);\n}\n\nexport async function runCredential(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === 'help' || hasFlag(argv, '--help', '-h')) {\n printCredentialHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(rest);\n\n switch (subCommand) {\n case 'list':\n await handleList(rest, apiUrl, token);\n return;\n case 'get':\n await handleGet(rest, apiUrl, token);\n return;\n case 'create':\n await handleCreate(rest, apiUrl, token);\n return;\n case 'update':\n await handleUpdate(rest, apiUrl, token);\n return;\n case 'test':\n await handleTest(rest, apiUrl, token);\n return;\n default:\n console.error(`Unknown credential sub-command: ${subCommand}`);\n printCredentialHelp();\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAQA,OAAO,aAAa;AACpB,SAAS,oBAA6C;AAKtD,eAAe,iBAAiB,QAAgB,OAA8C;AAC5F,QAAM,SAAS,MAAM,SAAS,GAAG,MAAM,oBAAoB;AAAA,IACzD,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,IAC5C;AAAA,EACF,CAAC;AAED,MAAI,CAAC,OAAO,GAAI,eAAc,OAAO,OAAO;AAE5C,QAAM,OAAQ,MAAM,OAAO,SAAS,KAAK;AACzC,SAAO,KAAK,SAAS,CAAC;AACxB;AAEA,SAAS,sBAA4B;AACnC,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAEA,eAAe,WAAW,MAAgB,QAAgB,OAA8B;AACtF,QAAM,WAAW,QAAQ,MAAM,QAAQ;AACvC,QAAM,iBAAiB,kBAAkB,IAAI;AAE7C,MAAI,QAAQ,MAAM,iBAAiB,QAAQ,KAAK;AAEhD,MAAI,gBAAgB;AAClB,UAAM,QAAQ,eAAe,YAAY;AACzC,YAAQ,MAAM;AAAA,MACZ,CAAC,MACC,EAAE,eAAe,kBAChB,EAAE,gBAAgB,EAAE,aAAa,YAAY,EAAE,SAAS,KAAK;AAAA,IAClE;AAAA,EACF;AAGA,UAAQ,MAAM,OAAO,CAAC,MAAM,CAAE,EAA2C,WAAW;AAEpF,MAAI,UAAU;AACZ,YAAQ,IAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC1C;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,uBAAuB;AACnC;AAAA,EACF;AAEA,UAAQ,IAAI;AAAA,eAAkB,MAAM,MAAM;AAAA,CAAM;AAChD,aAAW,KAAK,OAAO;AACrB,UAAM,WAAW,EAAE,gBAAgB,EAAE;AACrC,UAAM,MAAM,EAAE,kBAAkB,MAAM,EAAE,eAAe,KAAK;AAC5D,UAAM,UAAU,EAAE,kBAAkB,MAAM,EAAE,eAAe,KAAK;AAChE,UAAM,WAAW,EAAE,WAAW,kBAAkB,EAAE,QAAQ;AAAA,IAAO;AACjE,YAAQ,IAAI,KAAK,EAAE,IAAI,EAAE;AACzB,YAAQ,IAAI,WAAW,EAAE,EAAE,EAAE;AAC7B,YAAQ,IAAI,oBAAoB,QAAQ,GAAG,GAAG,GAAG,OAAO,EAAE;AAC1D,QAAI,SAAU,SAAQ,OAAO,MAAM,QAAQ;AAC3C,YAAQ,IAAI,EAAE;AAAA,EAChB;AACF;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AAClD,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,+BAA+B;AAC7C,YAAQ,MAAM,4FAA4F;AAC1G,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,WAAW,YAAY,MAAM,aAAa;AAChD,QAAM,OAAO,YAAY,MAAM,QAAQ;AACvC,QAAM,OAAO,YAAY,MAAM,QAAQ;AACvC,QAAM,oBAAoB,YAAY,MAAM,sBAAsB;AAClE,QAAM,aAAa,QAAQ,MAAM,QAAQ;AAEzC,MAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,mBAAmB;AACrD,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,CAAC,SAAS,QAAQ,UAAU,QAAQ;AACvD,MAAI,QAAQ,CAAC,WAAW,SAAS,IAAI,GAAG;AACtC,YAAQ,MAAM,wBAAwB,IAAI,sBAAsB,WAAW,KAAK,IAAI,CAAC,EAAE;AACvF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,cAAc,MAAM,iBAAiB,QAAQ,KAAK;AACxD,QAAM,aAAa,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAC1D,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,qBAAqB,MAAM,aAAa;AACtD,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ,MAAM,0BAA0B;AACxC,iBAAW,KAAK,YAAY,MAAM,GAAG,EAAE,GAAG;AACxC,gBAAQ,MAAM,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,MAAM,EAAE,gBAAgB,EAAE,UAAU,GAAG;AAAA,MAC3E;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAgC,CAAC;AACvC,MAAI,aAAa,OAAW,MAAK,WAAW;AAC5C,MAAI,SAAS,OAAW,MAAK,OAAO;AACpC,MAAI,SAAS,OAAW,MAAK,OAAO;AACpC,MAAI,sBAAsB,OAAW,MAAK,oBAAoB;AAE9D,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,WAAW,UAAU,gBAAgB,MAAM;AAAA,IAC9D;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,QAAI,YAAY;AACd,cAAQ,IAAI,KAAK,UAAU,EAAE,IAAI,OAAO,OAAO,OAAO,SAAS,gBAAgB,GAAG,MAAM,CAAC,CAAC;AAAA,IAC5F,OAAO;AACL,cAAQ,MAAM,UAAU,OAAO,SAAS,eAAe,EAAE;AAAA,IAC3D;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,YAAY;AACd,YAAQ,IAAI,KAAK,UAAU,EAAE,IAAI,MAAM,cAAc,QAAQ,SAAS,KAAK,GAAG,MAAM,CAAC,CAAC;AACtF;AAAA,EACF;AAEA,UAAQ,IAAI,YAAY,WAAW,IAAI,MAAM,MAAM,IAAI;AACvD,MAAI,aAAa,QAAW;AAC1B,YAAQ,IAAI,eAAe,WAAW,YAAY,QAAQ,OAAO,QAAQ,EAAE;AAAA,EAC7E;AACA,MAAI,SAAS,QAAW;AACtB,YAAQ,IAAI,WAAW,WAAW,IAAI,OAAO,IAAI,EAAE;AAAA,EACrD;AACA,MAAI,SAAS,QAAW;AACtB,YAAQ,IAAI,cAAc,IAAI,EAAE;AAAA,EAClC;AACA,MAAI,sBAAsB,QAAW;AACnC,YAAQ,IAAI,8BAA8B;AAAA,EAC5C;AACF;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,aAAa,QAAQ,MAAM,QAAQ;AACzC,QAAM,cAAc,kBAAkB,IAAI;AAC1C,QAAM,OAAO,YAAY,MAAM,QAAQ;AACvC,QAAM,WAAW,YAAY,MAAM,aAAa;AAChD,QAAM,oBAAoB,YAAY,MAAM,sBAAsB;AAClE,QAAM,WAAW,YAAY,MAAM,YAAY;AAC/C,QAAM,WAAW,YAAY,MAAM,YAAY,KAAK,QAAQ,IAAI;AAChE,QAAM,SAAS,YAAY,MAAM,WAAW,KAAK,QAAQ,IAAI;AAC7D,QAAM,YAAY,YAAY,MAAM,cAAc,KAAK,QAAQ,IAAI;AAEnE,MAAI,CAAC,eAAe,CAAC,MAAM;AACzB,YAAQ,MAAM,mEAAmE;AACjF,YAAQ,MAAM,iFAAiF;AAC/F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,MAAM,gBAAgB,QAAQ,KAAK;AACtD,QAAM,WAAW,WAAW;AAAA,IAC1B,CAAC,MAAM,EAAE,OAAO,eAAe,EAAE,KAAK,YAAY,MAAM,YAAY,YAAY;AAAA,EAClF;AACA,MAAI,CAAC,UAAU;AACb,YAAQ,MAAM,uBAAuB,WAAW,cAAc;AAC9D,QAAI,WAAW,SAAS,GAAG;AACzB,cAAQ,MAAM,yBAAyB;AACvC,iBAAW,KAAK,WAAY,SAAQ,MAAM,OAAO,EAAE,IAAI,KAAK,EAAE,EAAE,GAAG;AAAA,IACrE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAgC,CAAC;AACvC,MAAI,aAAa,OAAW,MAAK,WAAW;AAC5C,MAAI,aAAa,OAAW,MAAK,WAAW;AAC5C,MAAI,WAAW,OAAW,MAAK,SAAS;AACxC,MAAI,cAAc,OAAW,MAAK,YAAY;AAE9C,QAAM,OAAgC,EAAE,MAAM,KAAK;AACnD,MAAI,aAAa,OAAW,MAAK,WAAW;AAC5C,MAAI,sBAAsB,OAAW,MAAK,oBAAoB;AAE9D,QAAM,SAAS,MAAM,WAKlB,QAAQ,OAAO,QAAQ,mBAAmB,SAAS,EAAE,gBAAgB,IAAI;AAE5E,MAAI,CAAC,OAAO,IAAI;AACd,QAAI,YAAY;AACd,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC7C,OAAO;AACL,cAAQ,MAAM,UAAU,OAAO,SAAS,eAAe,EAAE;AAAA,IAC3D;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,YAAY;AACd,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C;AAAA,EACF;AAEA,UAAQ,IAAI,uBAAuB,IAAI,MAAM,OAAO,EAAE,qBAAqB,SAAS,IAAI,GAAG;AAC7F;AAqBA,eAAe,sBACb,QACA,OACA,YACA,QACkC;AAClC,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,UAAU,gBAAgB,MAAM;AAAA,EACrD;AACA,MAAI,CAAC,OAAO,MAAM,CAAC,OAAO,WAAY,QAAO;AAC7C,SAAO,OAAO;AAChB;AAEA,eAAe,UAAU,MAAgB,QAAgB,OAA8B;AACrF,QAAM,aAAa,QAAQ,MAAM,QAAQ;AACzC,QAAM,cAAc,kBAAkB,IAAI;AAC1C,QAAM,UAAU,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AAEnD,MAAI,CAAC,SAAS;AACZ,YAAQ,MAAM,uCAAuC;AACrD,YAAQ,MAAM,+EAA+E;AAC7F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,MAAM,iBAAiB,QAAQ,KAAK;AACxD,QAAM,aAAa,YAAY;AAAA,IAC7B,CAAC,MAAM,EAAE,OAAO,WAAW,EAAE,KAAK,YAAY,MAAM,QAAQ,YAAY;AAAA,EAC1E;AAEA,MAAI;AACJ,MAAI,WAAW,WAAW,GAAG;AAC3B,YAAQ,MAAM,sBAAsB,OAAO,cAAc;AACzD,YAAQ,KAAK,CAAC;AAAA,EAChB,WAAW,WAAW,WAAW,GAAG;AAClC,kBAAc,WAAW,CAAC;AAAA,EAC5B,OAAO;AACL,QAAI,CAAC,aAAa;AAChB,cAAQ,MAAM,sCAAsC,OAAO,wCAAwC;AACnG,iBAAW,KAAK,YAAY;AAC1B,gBAAQ,MAAM,OAAO,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,UAAU,GAAG;AAAA,MAClE;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,QAAQ,YAAY,YAAY;AACtC,kBAAc,WAAW;AAAA,MACvB,CAAC,MAAM,EAAE,eAAe,eAAgB,EAAE,gBAAgB,EAAE,aAAa,YAAY,MAAM;AAAA,IAC7F;AACA,QAAI,CAAC,aAAa;AAChB,cAAQ,MAAM,+BAA+B,OAAO,qBAAqB,WAAW,IAAI;AACxF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,sBAAsB,QAAQ,OAAO,YAAY,YAAY,YAAY,EAAE;AAChG,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,oCAAoC,YAAY,EAAE,GAAG;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,YAAY;AACd,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C;AAAA,EACF;AAEA,QAAM,OAAO,OAAO,QAAQ,CAAC;AAC7B,QAAM,WAAW,KAAK,YAAY,KAAK,SAAS,KAAK,QAAQ;AAC7D,UAAQ,IAAI,eAAe,OAAO,IAAI,MAAM,OAAO,EAAE,GAAG;AACxD,UAAQ,IAAI,2BAA2B,YAAY,gBAAgB,YAAY,UAAU,EAAE;AAC3F,UAAQ,IAAI,2BAA2B,OAAO,gBAAgB,WAAW,EAAE;AAC3E,UAAQ,IAAI,2BAA2B,OAAO,YAAY,WAAW,EAAE;AACvE,UAAQ,IAAI,2BAA2B,OAAO,qBAAqB,WAAW,EAAE;AAChF,MAAI,SAAU,SAAQ,IAAI,2BAA2B,QAAQ,EAAE;AAC/D,MAAI,KAAK,SAAU,SAAQ,IAAI,2BAA2B,KAAK,QAAQ,EAAE;AACzE,MAAI,KAAK,UAAW,SAAQ,IAAI,2BAA2B,KAAK,SAAS,EAAE;AAC3E,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,uBAAuB;AACnC,MAAI,OAAO,mBAAmB;AAC5B,eAAW,QAAQ,OAAO,kBAAkB,MAAM,IAAI,GAAG;AACvD,cAAQ,IAAI,OAAO,IAAI,EAAE;AAAA,IAC3B;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,YAAY;AAAA,EAC1B;AACF;AAEA,eAAe,WAAW,MAAgB,QAAgB,OAA8B;AACtF,QAAM,aAAa,QAAQ,MAAM,QAAQ;AACzC,QAAM,cAAc,kBAAkB,IAAI;AAC1C,QAAM,UAAU,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC;AAEnD,MAAI,CAAC,SAAS;AACZ,YAAQ,MAAM,uCAAuC;AACrD,YAAQ,MAAM,kEAAkE;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,MAAM,iBAAiB,QAAQ,KAAK;AACxD,QAAM,aAAa,YAAY;AAAA,IAC7B,CAAC,MAAM,EAAE,OAAO,WAAW,EAAE,KAAK,YAAY,MAAM,QAAQ,YAAY;AAAA,EAC1E;AACA,MAAI;AACJ,MAAI,WAAW,WAAW,GAAG;AAC3B,YAAQ,MAAM,sBAAsB,OAAO,cAAc;AACzD,YAAQ,KAAK,CAAC;AAAA,EAChB,WAAW,WAAW,WAAW,GAAG;AAClC,kBAAc,WAAW,CAAC;AAAA,EAC5B,OAAO;AACL,QAAI,CAAC,aAAa;AAChB,cAAQ,MAAM,sCAAsC,OAAO,wCAAwC;AACnG,iBAAW,KAAK,YAAY;AAC1B,gBAAQ,MAAM,OAAO,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,UAAU,GAAG;AAAA,MAClE;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,QAAQ,YAAY,YAAY;AACtC,kBAAc,WAAW;AAAA,MACvB,CAAC,MAAM,EAAE,eAAe,eAAgB,EAAE,gBAAgB,EAAE,aAAa,YAAY,MAAM;AAAA,IAC7F;AACA,QAAI,CAAC,aAAa;AAChB,cAAQ,MAAM,+BAA+B,OAAO,qBAAqB,WAAW,IAAI;AACxF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,sBAAsB,QAAQ,OAAO,YAAY,YAAY,YAAY,EAAE;AAChG,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,oCAAoC,YAAY,EAAE,GAAG;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,OAAO,iBAAiB,WAAW;AACrC,YAAQ;AAAA,MACN,sBAAsB,OAAO,IAAI;AAAA,IACnC;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,WAAW,MAAM;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,YAAY,UAAU,gBAAgB,YAAY,EAAE;AAAA,EACzE;AAEA,MAAI,CAAC,SAAS,MAAM,CAAC,SAAS,UAAU;AACtC,YAAQ,MAAM,UAAU,SAAS,SAAS,oCAAoC,EAAE;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,EAAE,SAAS,IAAI;AACrB,MAAI,CAAC,YAAY;AACf,YAAQ,IAAI,oCAAoC,OAAO,IAAI,aAAa,QAAQ,MAAM;AAAA,EACxF;AAEA,QAAM,YAAY,MAAM,MAAM,GAAG,MAAM,mBAAmB,QAAQ,WAAW;AAAA,IAC3E,SAAS,EAAE,eAAe,UAAU,KAAK,IAAI,QAAQ,oBAAoB;AAAA,EAC3E,CAAC;AAED,MAAI,CAAC,UAAU,MAAM,CAAC,UAAU,MAAM;AACpC,YAAQ,MAAM,sCAAsC,UAAU,MAAM,EAAE;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,WAAW;AACf,MAAI,OAAO;AAEX,QAAM,SAAS,aAAa;AAAA,IAC1B,SAAS,CAAC,UAA8B;AACtC,UAAI,CAAC,MAAM,KAAM;AACjB,UAAI;AACJ,UAAI;AACF,eAAO,KAAK,MAAM,MAAM,IAAI;AAAA,MAC9B,QAAQ;AACN;AAAA,MACF;AAEA,UAAI,YAAY;AACd,gBAAQ,IAAI,KAAK,UAAU,EAAE,OAAO,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MAC1D,WAAW,MAAM,UAAU,kBAAkB;AAC3C,cAAM,SAAS;AACf,gBAAQ,IAAI,YAAO,OAAO,QAAQ,QAAQ,GAAG,OAAO,cAAc,KAAK,OAAO,WAAW,KAAK,EAAE,EAAE;AAAA,MACpG,WAAW,MAAM,UAAU,kBAAkB;AAC3C,cAAM,SAAS;AACf,YAAI,OAAO,QAAQ;AACjB,kBAAQ,IAAI,IAAI,OAAO,MAAM,IAAI,OAAO,UAAU,IAAI,OAAO,OAAO,KAAK,EAAE,EAAE;AAAA,QAC/E;AAAA,MACF;AAEA,UAAI,MAAM,UAAU,kBAAkB;AACpC,cAAM,SAAS;AACf,YAAI,OAAO,WAAW,kBAAkB,OAAO,WAAW,SAAS;AACjE,iBAAO;AACP,cAAI,OAAO,WAAW,QAAS,YAAW;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,SAAS,UAAU,KAAK,UAAU;AACxC,QAAM,UAAU,IAAI,YAAY;AAChC,SAAO,CAAC,MAAM;AACZ,UAAM,EAAE,OAAO,MAAM,WAAW,IAAI,MAAM,OAAO,KAAK;AACtD,QAAI,WAAY;AAChB,WAAO,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;AAAA,EACrD;AAEA,UAAQ,KAAK,QAAQ;AACvB;AAEA,eAAsB,cAAc,MAA+B;AACjE,QAAM,CAAC,YAAY,GAAG,IAAI,IAAI;AAE9B,MAAI,CAAC,cAAc,eAAe,UAAU,QAAQ,MAAM,UAAU,IAAI,GAAG;AACzE,wBAAoB;AACpB;AAAA,EACF;AAEA,QAAM,EAAE,QAAQ,MAAM,IAAI,MAAM,cAAc,IAAI;AAElD,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,YAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,IACF,KAAK;AACH,YAAM,UAAU,MAAM,QAAQ,KAAK;AACnC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,IACF;AACE,cAAQ,MAAM,mCAAmC,UAAU,EAAE;AAC7D,0BAAoB;AACpB,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;","names":[]}
|