@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.
Files changed (166) hide show
  1. package/dist/account-22LMP5PX.js +255 -0
  2. package/dist/account-22LMP5PX.js.map +1 -0
  3. package/dist/{api-catalog-GFGCLVS4.js → api-catalog-WTFQNLK6.js} +5 -4
  4. package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-WTFQNLK6.js.map} +1 -1
  5. package/dist/{attachment-SJC7D7FY.js → attachment-U3NKVPEY.js} +5 -4
  6. package/dist/{attachment-SJC7D7FY.js.map → attachment-U3NKVPEY.js.map} +1 -1
  7. package/dist/augmentor-5GMNAX4M.js +198 -0
  8. package/dist/augmentor-5GMNAX4M.js.map +1 -0
  9. package/dist/{auth-EN6NQCJY.js → auth-VQG2CDUD.js} +3 -2
  10. package/dist/{chunk-A4OFTUVV.js → chunk-3X3NW7QA.js} +7 -4
  11. package/dist/chunk-3X3NW7QA.js.map +1 -0
  12. package/dist/{chunk-N7HFALX3.js → chunk-46IUJXSD.js} +2 -2
  13. package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
  14. package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
  15. package/dist/{chunk-B2VISN2V.js → chunk-7VMO2WOZ.js} +238 -68
  16. package/dist/chunk-7VMO2WOZ.js.map +1 -0
  17. package/dist/{chunk-QFDOWABX.js → chunk-CMGI7UZD.js} +6 -3
  18. package/dist/chunk-CMGI7UZD.js.map +1 -0
  19. package/dist/{chunk-XQXVG7RA.js → chunk-FNPMWDUQ.js} +3 -3
  20. package/dist/chunk-G4UYGRZR.js +2 -0
  21. package/dist/{chunk-I4QLI3JO.js → chunk-HDFYUFBI.js} +2 -2
  22. package/dist/chunk-JMJVGMXI.js +92 -0
  23. package/dist/chunk-JMJVGMXI.js.map +1 -0
  24. package/dist/{chunk-SJJW77WN.js → chunk-JYGNMKZ5.js} +129 -38
  25. package/dist/chunk-JYGNMKZ5.js.map +1 -0
  26. package/dist/{chunk-52IO7ACJ.js → chunk-KCHKFYVX.js} +68 -10
  27. package/dist/{chunk-52IO7ACJ.js.map → chunk-KCHKFYVX.js.map} +1 -1
  28. package/dist/{chunk-ZD3KHESI.js → chunk-KRWRUU6R.js} +27 -7
  29. package/dist/chunk-KRWRUU6R.js.map +1 -0
  30. package/dist/{chunk-CLAWT7BJ.js → chunk-NHTEKEYE.js} +3 -3
  31. package/dist/{chunk-WOHL4FTB.js → chunk-PRBRSIZP.js} +16 -10
  32. package/dist/chunk-PRBRSIZP.js.map +1 -0
  33. package/dist/{chunk-GKY2KXO5.js → chunk-UETBLPKM.js} +154 -37
  34. package/dist/chunk-UETBLPKM.js.map +1 -0
  35. package/dist/{chunk-4PNOV7OV.js → chunk-VGYN2KU2.js} +9284 -1421
  36. package/dist/chunk-VGYN2KU2.js.map +1 -0
  37. package/dist/{chunk-Q4BO4Z5V.js → chunk-XJ5YYYGC.js} +4 -4
  38. package/dist/{credential-COLTBV6D.js → credential-E6KUHRGU.js} +24 -20
  39. package/dist/credential-E6KUHRGU.js.map +1 -0
  40. package/dist/{credential-template-EEPYFG7R.js → credential-template-4GPOPBGD.js} +30 -28
  41. package/dist/credential-template-4GPOPBGD.js.map +1 -0
  42. package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-XCLCY6S7.js} +5 -4
  43. package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-XCLCY6S7.js.map} +1 -1
  44. package/dist/{docs-3SSORHB5.js → docs-COCZYMW6.js} +95 -10
  45. package/dist/docs-COCZYMW6.js.map +1 -0
  46. package/dist/{doctor-OTSIWV6G.js → doctor-ERIVTCQS.js} +9 -5
  47. package/dist/doctor-ERIVTCQS.js.map +1 -0
  48. package/dist/environment-7Y4BV6XY.js +221 -0
  49. package/dist/environment-7Y4BV6XY.js.map +1 -0
  50. package/dist/{feature-flag-T2LRZME3.js → feature-flag-MHONEX6T.js} +164 -16
  51. package/dist/feature-flag-MHONEX6T.js.map +1 -0
  52. package/dist/index.js +123 -58
  53. package/dist/index.js.map +1 -1
  54. package/dist/{init-TGWRECT2.js → init-OROOM4HF.js} +5 -4
  55. package/dist/{init-TGWRECT2.js.map → init-OROOM4HF.js.map} +1 -1
  56. package/dist/{install-DXSNWZKO.js → install-OJFXHY6U.js} +7 -7
  57. package/dist/{issues-XIZOPX6T.js → issues-32GONR6C.js} +5 -4
  58. package/dist/{issues-XIZOPX6T.js.map → issues-32GONR6C.js.map} +1 -1
  59. package/dist/{knobs-NBINZIUB.js → knobs-UQGGY3CP.js} +7 -5
  60. package/dist/knobs-UQGGY3CP.js.map +1 -0
  61. package/dist/{list-3TDHK637.js → list-ZMIKEC5P.js} +5 -4
  62. package/dist/{list-3TDHK637.js.map → list-ZMIKEC5P.js.map} +1 -1
  63. package/dist/{local-HICVD6PU.js → local-PUD24GGQ.js} +7 -6
  64. package/dist/{local-HICVD6PU.js.map → local-PUD24GGQ.js.map} +1 -1
  65. package/dist/{login-UOIUXVUK.js → login-BSO7YX2D.js} +5 -4
  66. package/dist/{login-UOIUXVUK.js.map → login-BSO7YX2D.js.map} +1 -1
  67. package/dist/{mcp-54IJEOUA.js → mcp-4LS7OJUT.js} +24 -25
  68. package/dist/mcp-4LS7OJUT.js.map +1 -0
  69. package/dist/{pdf-extract-ABWV3L5X.js → pdf-extract-YHE54XU4.js} +2 -2
  70. package/dist/product-3JKSJCBC.js +346 -0
  71. package/dist/product-3JKSJCBC.js.map +1 -0
  72. package/dist/{property-UXLOLPTD.js → property-K5DBL4UE.js} +76 -4
  73. package/dist/property-K5DBL4UE.js.map +1 -0
  74. package/dist/{psql-BYUUNLGG.js → psql-PQFUFTSE.js} +3 -2
  75. package/dist/{psql-BYUUNLGG.js.map → psql-PQFUFTSE.js.map} +1 -1
  76. package/dist/{record-4TR2UIIS.js → record-EIIYLFJ4.js} +16 -17
  77. package/dist/record-EIIYLFJ4.js.map +1 -0
  78. package/dist/{redis-35VBUYW7.js → redis-BU36EZZI.js} +3 -2
  79. package/dist/redis-BU36EZZI.js.map +1 -0
  80. package/dist/{release-OOLTUBUT.js → release-LYC2P7DZ.js} +5 -4
  81. package/dist/{release-OOLTUBUT.js.map → release-LYC2P7DZ.js.map} +1 -1
  82. package/dist/{sandbox-XGSUU6G7.js → sandbox-CKHXXZHU.js} +55 -10
  83. package/dist/sandbox-CKHXXZHU.js.map +1 -0
  84. package/dist/{scenarios-GWLJEGHA.js → scenarios-2XJ2G3KR.js} +5 -4
  85. package/dist/{scenarios-GWLJEGHA.js.map → scenarios-2XJ2G3KR.js.map} +1 -1
  86. package/dist/{session-MYFNZPRP.js → session-UTOD5ZYH.js} +77 -10
  87. package/dist/session-UTOD5ZYH.js.map +1 -0
  88. package/dist/{skill-KM34LDMO.js → skill-GMAERMQL.js} +220 -70
  89. package/dist/skill-GMAERMQL.js.map +1 -0
  90. package/dist/skills/debug-run/SKILL.md +40 -23
  91. package/dist/skills/debug-run/history/1.2.0.md +262 -0
  92. package/dist/skills/debug-run/history/1.3.0.md +273 -0
  93. package/dist/skills/debug-run/history/1.3.1.md +279 -0
  94. package/dist/skills/debug-run/version.json +1 -1
  95. package/dist/skills/optimize-cache/SKILL.md +233 -0
  96. package/dist/{spec-RA3CFEKG.js → spec-2NP36U3Y.js} +54 -25
  97. package/dist/spec-2NP36U3Y.js.map +1 -0
  98. package/dist/{src-LB4HBTJQ.js → src-4T6GHXVO.js} +19 -11
  99. package/dist/src-4T6GHXVO.js.map +1 -0
  100. package/dist/{src-N3TPKGTY.js → src-CG52HEE7.js} +10 -7
  101. package/dist/{src-UVDX5UP6.js → src-PK5OZXCM.js} +4 -2
  102. package/dist/src-UEOOC425.js +5941 -0
  103. package/dist/src-UEOOC425.js.map +1 -0
  104. package/dist/{start-PSEQSQA3.js → start-3C7EEFW2.js} +5 -4
  105. package/dist/{start-PSEQSQA3.js.map → start-3C7EEFW2.js.map} +1 -1
  106. package/dist/{tag-YY6YAABQ.js → tag-JSHOMLBR.js} +6 -5
  107. package/dist/{task-IQC3YR5K.js → task-YGWVYTBJ.js} +5 -4
  108. package/dist/{task-IQC3YR5K.js.map → task-YGWVYTBJ.js.map} +1 -1
  109. package/dist/{test-suite-DZNJFXDW.js → test-suite-LI7ZH367.js} +12 -7
  110. package/dist/{test-suite-DZNJFXDW.js.map → test-suite-LI7ZH367.js.map} +1 -1
  111. package/dist/trace-UMLOSIIR.js +16 -0
  112. package/dist/{triage-ORG45R5W.js → triage-ZWJG4NBJ.js} +6 -5
  113. package/dist/triage-ZWJG4NBJ.js.map +1 -0
  114. package/dist/{upgrade-L2VYMP2V.js → upgrade-S7CGEK6H.js} +3 -3
  115. package/dist/{workflow-XYFH5LTP.js → workflow-T4HSAOGW.js} +316 -241
  116. package/dist/workflow-T4HSAOGW.js.map +1 -0
  117. package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-XVEZ36KF.js} +140 -7
  118. package/dist/workflow-cache-XVEZ36KF.js.map +1 -0
  119. package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-T2BULXZ7.js} +9 -8
  120. package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-T2BULXZ7.js.map} +1 -1
  121. package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-XXEZICN2.js} +5 -6
  122. package/dist/workflow-proposals-XXEZICN2.js.map +1 -0
  123. package/package.json +2 -1
  124. package/dist/chunk-4PNOV7OV.js.map +0 -1
  125. package/dist/chunk-A4OFTUVV.js.map +0 -1
  126. package/dist/chunk-B2VISN2V.js.map +0 -1
  127. package/dist/chunk-GKY2KXO5.js.map +0 -1
  128. package/dist/chunk-JICSYFRJ.js +0 -46
  129. package/dist/chunk-JICSYFRJ.js.map +0 -1
  130. package/dist/chunk-QFDOWABX.js.map +0 -1
  131. package/dist/chunk-SJJW77WN.js.map +0 -1
  132. package/dist/chunk-WOHL4FTB.js.map +0 -1
  133. package/dist/chunk-ZD3KHESI.js.map +0 -1
  134. package/dist/credential-COLTBV6D.js.map +0 -1
  135. package/dist/credential-template-EEPYFG7R.js.map +0 -1
  136. package/dist/docs-3SSORHB5.js.map +0 -1
  137. package/dist/doctor-OTSIWV6G.js.map +0 -1
  138. package/dist/feature-flag-T2LRZME3.js.map +0 -1
  139. package/dist/knobs-NBINZIUB.js.map +0 -1
  140. package/dist/mcp-54IJEOUA.js.map +0 -1
  141. package/dist/property-UXLOLPTD.js.map +0 -1
  142. package/dist/record-4TR2UIIS.js.map +0 -1
  143. package/dist/redis-35VBUYW7.js.map +0 -1
  144. package/dist/sandbox-XGSUU6G7.js.map +0 -1
  145. package/dist/session-MYFNZPRP.js.map +0 -1
  146. package/dist/skill-KM34LDMO.js.map +0 -1
  147. package/dist/spec-RA3CFEKG.js.map +0 -1
  148. package/dist/src-LB4HBTJQ.js.map +0 -1
  149. package/dist/trace-HXIW7U2D.js +0 -14
  150. package/dist/workflow-XYFH5LTP.js.map +0 -1
  151. package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
  152. package/dist/workflow-proposals-MTUDX46O.js.map +0 -1
  153. /package/dist/{auth-EN6NQCJY.js.map → auth-VQG2CDUD.js.map} +0 -0
  154. /package/dist/{chunk-N7HFALX3.js.map → chunk-46IUJXSD.js.map} +0 -0
  155. /package/dist/{chunk-XQXVG7RA.js.map → chunk-FNPMWDUQ.js.map} +0 -0
  156. /package/dist/{pdf-extract-ABWV3L5X.js.map → chunk-G4UYGRZR.js.map} +0 -0
  157. /package/dist/{chunk-I4QLI3JO.js.map → chunk-HDFYUFBI.js.map} +0 -0
  158. /package/dist/{chunk-CLAWT7BJ.js.map → chunk-NHTEKEYE.js.map} +0 -0
  159. /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-XJ5YYYGC.js.map} +0 -0
  160. /package/dist/{install-DXSNWZKO.js.map → install-OJFXHY6U.js.map} +0 -0
  161. /package/dist/{src-N3TPKGTY.js.map → pdf-extract-YHE54XU4.js.map} +0 -0
  162. /package/dist/{src-UVDX5UP6.js.map → src-CG52HEE7.js.map} +0 -0
  163. /package/dist/{tag-YY6YAABQ.js.map → src-PK5OZXCM.js.map} +0 -0
  164. /package/dist/{trace-HXIW7U2D.js.map → tag-JSHOMLBR.js.map} +0 -0
  165. /package/dist/{triage-ORG45R5W.js.map → trace-UMLOSIIR.js.map} +0 -0
  166. /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-S7CGEK6H.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/credential-template.ts"],"sourcesContent":["/**\n * CLI Credential Template Commands\n *\n * Manage property-scoped credential templates (logical credential identities\n * that group per-environment concrete credential bindings).\n *\n * @module credential-template\n */\n\nimport process from 'node:process';\nimport { resolveConfig, getArgValue, hasFlag } from './auth.js';\nimport { apiRequest, fetchProperties, getApplicationArg } from './cli-helpers.js';\n\ntype Template = {\n id: string;\n propertyId: string;\n name: string;\n description: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\ntype Binding = {\n id: string;\n name: string;\n environmentId: string | null;\n isEphemeral: boolean;\n templateId: string | null;\n};\n\nfunction printHelp(): void {\n console.log(\n [\n 'Usage: canary credential-template <sub-command> [options]',\n '',\n 'Sub-commands: (--application accepted as --app; --property is a backwards-compat alias)',\n ' list --application <name|id> List templates on an application',\n ' create --application <name|id> --name <name> [--description <text>]',\n ' show --application <name|id> <templateId> Show template + its bindings',\n ' rename --application <name|id> <templateId> --name <new-name>',\n ' delete --application <name|id> <templateId>',\n ' link --application <name|id> <templateId> --credential <credId>',\n ' unlink --application <name|id> <templateId> --credential <credId>',\n '',\n 'Common options:',\n ' --json Output raw JSON',\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\n/**\n * Return the first positional argument, excluding values consumed by the given\n * `--flag value` pairs. Prior implementation used `argv.find(a => !a.startsWith('-'))`\n * which incorrectly returned the value of `--property` (the first flag value) as\n * the templateId.\n */\nfunction getPositionalArg(argv: string[], flagsWithValues: string[]): string | undefined {\n const skip = new Set<number>();\n for (let i = 0; i < argv.length; i++) {\n if (flagsWithValues.includes(argv[i]!) && i + 1 < argv.length) {\n skip.add(i);\n skip.add(i + 1);\n }\n }\n for (let i = 0; i < argv.length; i++) {\n if (skip.has(i)) continue;\n const a = argv[i]!;\n if (!a.startsWith('-')) return a;\n }\n return undefined;\n}\n\nconst TEMPLATE_FLAGS = [\n '--application',\n '--property',\n '--credential',\n '--name',\n '--description',\n '--env',\n '--api-url',\n '--token',\n];\n\nasync function resolveProperty(\n apiUrl: string,\n token: string,\n arg: string\n): Promise<{ id: string; name: string }> {\n const properties = await fetchProperties(apiUrl, token);\n const property = properties.find(\n (p) => p.id === arg || p.name.toLowerCase() === arg.toLowerCase()\n );\n if (!property) {\n console.error(`Error: Application \"${arg}\" not found.`);\n process.exit(1);\n }\n return property;\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyArg = getApplicationArg(argv);\n const jsonFlag = hasFlag(argv, '--json');\n if (!propertyArg) {\n console.error('Error: --application is required (alias: --property).');\n process.exit(1);\n }\n const property = await resolveProperty(apiUrl, token, propertyArg);\n const result = await apiRequest<{ ok: boolean; items?: Template[]; error?: string }>(\n apiUrl,\n token,\n 'GET',\n `/org/properties/${property.id}/credential-templates`\n );\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'Failed to list templates'}`);\n process.exit(1);\n }\n const items = result.items ?? [];\n if (jsonFlag) {\n console.log(JSON.stringify(items, null, 2));\n return;\n }\n if (items.length === 0) {\n console.log(`No credential templates on application \"${property.name}\".`);\n return;\n }\n for (const t of items) {\n console.log(`${t.id} ${t.name}${t.description ? ` — ${t.description}` : ''}`);\n }\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyArg = getApplicationArg(argv);\n const name = getArgValue(argv, '--name');\n const description = getArgValue(argv, '--description');\n const jsonFlag = hasFlag(argv, '--json');\n\n if (!propertyArg || !name) {\n console.error('Error: --application and --name are required.');\n process.exit(1);\n }\n const property = await resolveProperty(apiUrl, token, propertyArg);\n const result = await apiRequest<{ ok: boolean; template?: Template; error?: string }>(\n apiUrl,\n token,\n 'POST',\n `/org/properties/${property.id}/credential-templates`,\n { name, description: description ?? null }\n );\n if (!result.ok || !result.template) {\n console.error(`Error: ${result.error ?? 'Failed to create template'}`);\n process.exit(1);\n }\n if (jsonFlag) {\n console.log(JSON.stringify(result.template, null, 2));\n return;\n }\n console.log(`Created template \"${result.template.name}\" (${result.template.id})`);\n}\n\nasync function handleShow(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyArg = getApplicationArg(argv);\n const templateId = getPositionalArg(argv, TEMPLATE_FLAGS);\n const jsonFlag = hasFlag(argv, '--json');\n\n if (!propertyArg || !templateId) {\n console.error('Error: --application and <templateId> are required.');\n process.exit(1);\n }\n const property = await resolveProperty(apiUrl, token, propertyArg);\n const result = await apiRequest<{\n ok: boolean;\n template?: Template;\n bindings?: Binding[];\n error?: string;\n }>(\n apiUrl,\n token,\n 'GET',\n `/org/properties/${property.id}/credential-templates/${templateId}`\n );\n if (!result.ok || !result.template) {\n console.error(`Error: ${result.error ?? 'Failed to fetch template'}`);\n process.exit(1);\n }\n if (jsonFlag) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n const t = result.template;\n console.log(`${t.name} (${t.id})`);\n if (t.description) console.log(` ${t.description}`);\n console.log(`Bindings (${result.bindings?.length ?? 0}):`);\n for (const b of result.bindings ?? []) {\n const env = b.environmentId ?? '(no env)';\n const eph = b.isEphemeral ? ' [ephemeral]' : '';\n console.log(` ${b.id} env=${env} ${b.name}${eph}`);\n }\n}\n\nasync function handleRename(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyArg = getApplicationArg(argv);\n const newName = getArgValue(argv, '--name');\n const templateId = getPositionalArg(argv, TEMPLATE_FLAGS);\n if (!propertyArg || !templateId || !newName) {\n console.error('Error: --application, <templateId>, and --name are required.');\n process.exit(1);\n }\n const property = await resolveProperty(apiUrl, token, propertyArg);\n const result = await apiRequest<{ ok: boolean; template?: Template; error?: string }>(\n apiUrl,\n token,\n 'PATCH',\n `/org/properties/${property.id}/credential-templates/${templateId}`,\n { name: newName }\n );\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'Failed to rename template'}`);\n process.exit(1);\n }\n console.log(`Renamed template ${templateId} → \"${newName}\"`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyArg = getApplicationArg(argv);\n const templateId = getPositionalArg(argv, TEMPLATE_FLAGS);\n if (!propertyArg || !templateId) {\n console.error('Error: --application and <templateId> are required.');\n process.exit(1);\n }\n const property = await resolveProperty(apiUrl, token, propertyArg);\n const result = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'DELETE',\n `/org/properties/${property.id}/credential-templates/${templateId}`\n );\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'Failed to delete template'}`);\n process.exit(1);\n }\n console.log(`Deleted template ${templateId}`);\n}\n\nasync function handleLink(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyArg = getApplicationArg(argv);\n const credentialId = getArgValue(argv, '--credential');\n const templateId = getPositionalArg(argv, TEMPLATE_FLAGS);\n if (!propertyArg || !templateId || !credentialId) {\n console.error('Error: --application, <templateId>, and --credential are required.');\n process.exit(1);\n }\n const property = await resolveProperty(apiUrl, token, propertyArg);\n const result = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'POST',\n `/org/properties/${property.id}/credential-templates/${templateId}/bindings`,\n { credentialId }\n );\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'Failed to link credential'}`);\n process.exit(1);\n }\n console.log(`Linked credential ${credentialId} to template ${templateId}`);\n}\n\nasync function handleUnlink(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyArg = getApplicationArg(argv);\n const credentialId = getArgValue(argv, '--credential');\n const templateId = getPositionalArg(argv, TEMPLATE_FLAGS);\n if (!propertyArg || !templateId || !credentialId) {\n console.error('Error: --application, <templateId>, and --credential are required.');\n process.exit(1);\n }\n const property = await resolveProperty(apiUrl, token, propertyArg);\n const result = await apiRequest<{ ok: boolean; error?: string }>(\n apiUrl,\n token,\n 'DELETE',\n `/org/properties/${property.id}/credential-templates/${templateId}/bindings/${credentialId}`\n );\n if (!result.ok) {\n console.error(`Error: ${result.error ?? 'Failed to unlink credential'}`);\n process.exit(1);\n }\n console.log(`Unlinked credential ${credentialId} from template ${templateId}`);\n}\n\nexport async function runCredentialTemplate(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === 'help' || hasFlag(argv, '--help', '-h')) {\n printHelp();\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 'create':\n await handleCreate(rest, apiUrl, token);\n return;\n case 'show':\n await handleShow(rest, apiUrl, token);\n return;\n case 'rename':\n await handleRename(rest, apiUrl, token);\n return;\n case 'delete':\n await handleDelete(rest, apiUrl, token);\n return;\n case 'link':\n await handleLink(rest, apiUrl, token);\n return;\n case 'unlink':\n await handleUnlink(rest, apiUrl, token);\n return;\n default:\n console.error(`Unknown credential-template sub-command: ${subCommand}`);\n printHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/docs.ts"],"sourcesContent":["/**\n * CLI Documentation Management\n *\n * Allows agents and superadmins to list, pull, edit, and push documentation pages.\n * Supports both global (public/superadmin) and org-scoped (--org) modes.\n */\n\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport { getSubDir } from '@chatsdet/tmp';\nimport process from 'node:process';\nimport { resolveConfig, getArgValue, hasFlag } from './auth.js';\nimport { apiRequest } from './cli-helpers.js';\n\ntype DocsPageListItem = {\n slug: string;\n title: string;\n description: string | null;\n updatedAt: string;\n publishedBy: string | null;\n};\n\ntype DocsVersionItem = {\n version: number;\n title: string;\n checksum: string;\n message: string | null;\n createdBy: string;\n createdAt: string;\n};\n\ntype DocsApiResponse = {\n ok: boolean;\n error?: string;\n data?: {\n slug?: string;\n title?: string;\n pages?: DocsPageListItem[];\n content?: string;\n versions?: DocsVersionItem[];\n };\n};\n\nconst DEFAULT_PULL_DIR = getSubDir('docs');\n\n/**\n * Parse the optional data-driven sidebar-placement flags. Only present flags\n * are included, so `push`/`create` without them leaves existing values intact.\n */\nfunction parseNavFields(argv: string[]): {\n navSection?: string;\n navGroup?: string;\n navOrder?: number;\n} {\n const out: { navSection?: string; navGroup?: string; navOrder?: number } = {};\n const section = getArgValue(argv, '--section');\n const group = getArgValue(argv, '--group');\n const order = getArgValue(argv, '--order');\n if (section != null) out.navSection = section;\n if (group != null) out.navGroup = group;\n if (order != null) {\n const n = Number.parseInt(order, 10);\n if (Number.isNaN(n)) {\n console.error('Error: --order must be an integer.');\n process.exit(1);\n }\n out.navOrder = n;\n }\n return out;\n}\n\n/** Return the base path prefix for read endpoints based on --org flag */\nfunction readPrefix(orgMode: boolean): string {\n return orgMode ? '/docs' : '/public/docs';\n}\n\n/** Return the base path prefix for write endpoints based on --org flag */\nfunction writePrefix(orgMode: boolean): string {\n return orgMode ? '/docs' : '/superadmin/docs';\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const jsonOutput = hasFlag(argv, '--json');\n const result = await apiRequest<DocsApiResponse>(apiUrl, token, 'GET', `${readPrefix(orgMode)}/pages`);\n const pages: DocsPageListItem[] = result.data?.pages ?? [];\n\n if (jsonOutput) {\n console.log(JSON.stringify(pages, null, 2));\n return;\n }\n\n if (pages.length === 0) {\n console.log('No documentation pages found.');\n return;\n }\n\n // Default: markdown table for easy consumption by agents and humans\n const slugWidth = Math.max(4, ...pages.map((p) => p.slug.length));\n const titleWidth = Math.max(5, ...pages.map((p) => p.title.length));\n const header = `| ${'Slug'.padEnd(slugWidth)} | ${'Title'.padEnd(titleWidth)} | Updated | Published By |`;\n const sep = `| ${'-'.repeat(slugWidth)} | ${'-'.repeat(titleWidth)} | ---------- | ------------ |`;\n console.log(header);\n console.log(sep);\n for (const page of pages) {\n const date = new Date(page.updatedAt).toISOString().slice(0, 10);\n const by = page.publishedBy ?? '';\n console.log(`| ${page.slug.padEnd(slugWidth)} | ${page.title.padEnd(titleWidth)} | ${date} | ${by.padEnd(12)} |`);\n }\n}\n\nasync function fetchDocContent(slug: string, apiUrl: string, token: string, orgMode: boolean): Promise<string> {\n if (orgMode) {\n const result = await apiRequest<DocsApiResponse>(\n apiUrl,\n token,\n 'GET',\n `/docs/pages/content?slug=${encodeURIComponent(slug)}`\n );\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n return result.data?.content ?? '';\n }\n\n const res = await fetch(`${apiUrl}/public/docs/pages/raw?slug=${encodeURIComponent(slug)}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 404) {\n console.error(`Error: Page not found: ${slug}`);\n process.exit(1);\n }\n\n if (!res.ok) {\n console.error(`Error: ${res.statusText}`);\n process.exit(1);\n }\n\n return res.text();\n}\n\nasync function handleGet(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const slug = argv[0];\n if (!slug || slug.startsWith('--')) {\n console.error('Error: Missing slug.');\n console.error('Usage: canary docs get <slug>');\n process.exit(1);\n }\n\n const content = await fetchDocContent(slug, apiUrl, token, orgMode);\n console.log(content);\n}\n\nasync function handlePull(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const slug = argv[0];\n if (!slug || slug.startsWith('--')) {\n console.error('Error: Missing slug.');\n console.error('Usage: canary docs pull <slug> [--output <path>]');\n process.exit(1);\n }\n\n const outputPath = getArgValue(argv, '--output') ?? path.join(DEFAULT_PULL_DIR, `${slug}.md`);\n const content = await fetchDocContent(slug, apiUrl, token, orgMode);\n\n await mkdir(path.dirname(outputPath), { recursive: true });\n await writeFile(outputPath, content, 'utf8');\n console.log(`Pulled ${slug} → ${outputPath}`);\n}\n\nasync function handlePush(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const slug = argv[0];\n if (!slug || slug.startsWith('--')) {\n console.error('Error: Missing slug.');\n console.error('Usage: canary docs push <slug> --file <path> [--message <text>]');\n process.exit(1);\n }\n\n const filePath = getArgValue(argv, '--file');\n if (!filePath) {\n console.error('Error: Missing --file <path>.');\n process.exit(1);\n }\n\n const message = getArgValue(argv, '--message') ?? undefined;\n const createdBy = getArgValue(argv, '--created-by') ?? undefined;\n\n let content: string;\n try {\n content = await readFile(filePath, 'utf8');\n } catch {\n console.error(`Error: Could not read file: ${filePath}`);\n process.exit(1);\n }\n\n // Strip `export const meta = { ... };` block so raw JS isn't stored as doc content\n content = content.replace(/^\\s*export\\s+const\\s+meta\\s*=\\s*\\{[^}]*\\};\\s*/, '');\n\n const result = await apiRequest<DocsApiResponse>(\n apiUrl,\n token,\n 'PUT',\n `${writePrefix(orgMode)}/pages?slug=${encodeURIComponent(slug)}`,\n { content, message, createdBy, ...parseNavFields(argv) }\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Published new version of ${slug}`);\n}\n\nasync function handleHistory(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const slug = argv[0];\n if (!slug || slug.startsWith('--')) {\n console.error('Error: Missing slug.');\n console.error('Usage: canary docs history <slug>');\n process.exit(1);\n }\n\n const jsonOutput = hasFlag(argv, '--json');\n\n const res = await fetch(`${apiUrl}${readPrefix(orgMode)}/pages/versions?slug=${encodeURIComponent(slug)}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({ error: res.statusText }));\n console.error(`Error: ${(body as { error?: string }).error ?? res.statusText}`);\n process.exit(1);\n }\n\n const body = (await res.json()) as DocsApiResponse;\n const versions = body.data?.versions ?? [];\n\n if (jsonOutput) {\n console.log(JSON.stringify(versions, null, 2));\n return;\n }\n\n if (versions.length === 0) {\n console.log(`No versions found for ${slug}.`);\n return;\n }\n\n for (const v of versions) {\n const date = new Date(v.createdAt).toISOString().slice(0, 19).replace('T', ' ');\n const msg = v.message ? ` — ${v.message}` : '';\n console.log(` v${v.version} ${date} ${v.createdBy}${msg}`);\n }\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const slug = argv[0];\n if (!slug || slug.startsWith('--')) {\n console.error('Error: Missing slug.');\n console.error('Usage: canary docs create <slug> --file <path> --title <text>');\n process.exit(1);\n }\n\n const filePath = getArgValue(argv, '--file');\n const title = getArgValue(argv, '--title');\n\n if (!filePath) {\n console.error('Error: Missing --file <path>.');\n process.exit(1);\n }\n if (!title) {\n console.error('Error: Missing --title <text>.');\n process.exit(1);\n }\n\n const description = getArgValue(argv, '--description') ?? undefined;\n const message = getArgValue(argv, '--message') ?? undefined;\n\n let content: string;\n try {\n content = await readFile(filePath, 'utf8');\n } catch {\n console.error(`Error: Could not read file: ${filePath}`);\n process.exit(1);\n }\n\n // Strip `export const meta = { ... };` block so raw JS isn't stored as doc content\n content = content.replace(/^\\s*export\\s+const\\s+meta\\s*=\\s*\\{[^}]*\\};\\s*/, '');\n\n const result = await apiRequest<DocsApiResponse>(\n apiUrl,\n token,\n 'POST',\n `${writePrefix(orgMode)}/pages`,\n {\n slug,\n title,\n description,\n content,\n message,\n ...parseNavFields(argv),\n }\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Created page: ${slug}`);\n}\n\ntype DocsSearchResult = {\n path: string;\n title: string;\n description: string | null;\n excerpt: string;\n};\n\ntype DocsSearchResponse = {\n ok: boolean;\n error?: string;\n data?: { results: DocsSearchResult[] };\n};\n\nasync function handleSearch(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const query = argv.filter((a) => !a.startsWith('--')).join(' ').trim();\n if (!query) {\n console.error('Error: Missing search query.');\n console.error('Usage: canary docs search <query> [--limit <n>] [--json]');\n process.exit(1);\n }\n\n const jsonOutput = hasFlag(argv, '--json');\n const limitArg = getArgValue(argv, '--limit');\n const params = new URLSearchParams({ q: query });\n if (limitArg) params.set('limit', limitArg);\n\n const searchPath = orgMode ? '/docs/search' : '/public/docs/search';\n const res = await fetch(`${apiUrl}${searchPath}?${params.toString()}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({ error: res.statusText }));\n console.error(`Error: ${(body as { error?: string }).error ?? res.statusText}`);\n process.exit(1);\n }\n\n const body = (await res.json()) as DocsSearchResponse;\n const results = body.data?.results ?? [];\n\n if (jsonOutput) {\n console.log(JSON.stringify(results, null, 2));\n return;\n }\n\n if (results.length === 0) {\n console.log('No results found.');\n return;\n }\n\n for (const r of results) {\n const desc = r.description ? ` ${r.description}` : '';\n const excerpt = r.excerpt\n ? ` ${r.excerpt.replace(/<\\/?mark>/g, '').replace(/\\n/g, ' ').slice(0, 120)}`\n : '';\n console.log(` ${r.path ?? r.title}`);\n if (r.title) console.log(` ${r.title}${desc}`);\n if (excerpt) console.log(` ${excerpt}`);\n console.log();\n }\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string, orgMode: boolean): Promise<void> {\n const slug = argv[0];\n if (!slug || slug.startsWith('--')) {\n console.error('Error: Missing slug.');\n console.error('Usage: canary docs delete <slug>');\n process.exit(1);\n }\n\n const result = await apiRequest<DocsApiResponse>(\n apiUrl,\n token,\n 'DELETE',\n `${writePrefix(orgMode)}/pages?slug=${encodeURIComponent(slug)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Deleted page: ${slug}`);\n}\n\nfunction printDocsHelp(): void {\n console.log(\n [\n 'Usage: canary docs <sub-command> [options]',\n '',\n 'Sub-commands:',\n ' list List all doc pages',\n ' get <slug> Print content to stdout',\n ' search <query> Full-text search docs',\n ' pull <slug> [--output <path>] Download to local file',\n ' push <slug> --file <path> [--message] Publish new version',\n ' history <slug> Show version history',\n ' create <slug> --file <path> --title ... Create new page',\n ' delete <slug> Soft-delete a page',\n '',\n 'Options:',\n ' --org Use org-scoped endpoints (requires auth)',\n ' --file <path> Path to markdown file',\n ' --title <text> Page title (create only)',\n ' --description <text> Page description (create only)',\n ' --section <text> Sidebar section (data-driven nav; create/push)',\n ' --group <text> Sidebar sub-group within the section (create/push)',\n ' --order <n> Sort order within the section/group (create/push)',\n ' --message <text> Version commit message',\n ' --limit <n> Max results for search (default: 8, max: 20)',\n ' --output <path> Output path for pull (default: <tmpdir>/canary/docs/<slug>.md)',\n ' --json Output as JSON (list, history, search)',\n ' --env <env> Target environment (prod, dev)',\n ' --api-url <url> API URL override',\n ' --token <key> API token override',\n ].join('\\n')\n );\n}\n\nexport async function runDocs(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === 'help' || hasFlag(argv, '--help', '-h')) {\n printDocsHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n const orgMode = hasFlag(rest, '--org');\n\n switch (subCommand) {\n case 'list':\n await handleList(rest, apiUrl, token, orgMode);\n break;\n case 'get':\n await handleGet(rest, apiUrl, token, orgMode);\n break;\n case 'search':\n await handleSearch(rest, apiUrl, token, orgMode);\n break;\n case 'pull':\n await handlePull(rest, apiUrl, token, orgMode);\n break;\n case 'push':\n await handlePush(rest, apiUrl, token, orgMode);\n break;\n case 'history':\n await handleHistory(rest, apiUrl, token, orgMode);\n break;\n case 'create':\n await handleCreate(rest, apiUrl, token, orgMode);\n break;\n case 'delete':\n await handleDelete(rest, apiUrl, token, orgMode);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printDocsHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["body"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/doctor.ts"],"sourcesContent":["/**\n * `canary auth doctor` — print everything a user or IT admin needs to see\n * when the CLI is misbehaving against a corporate proxy (Zscaler, Netskope,\n * ZPA, Cloudflare Access). Reads the stored auth, decodes the JWT locally,\n * and probes a curated set of endpoints so we can tell apart:\n * - TLS interception (Node doesn't trust the proxy CA)\n * - URL-category filtering (some paths fail, others pass)\n * - Proxy-injected block pages (HTML body / missing request-id header)\n * - Real auth failures (401 with our header present)\n */\n\nimport process from 'node:process';\nimport { getArgValue, readStoredAuth, envToProfile, ENV_URLS } from './auth.js';\nimport { decodeJwtPayload } from './jwt.js';\nimport { apiFetch } from './api-fetch.js';\n\ninterface Probe {\n name: string;\n path: string;\n method?: string;\n}\n\nconst DEFAULT_PROBES: Probe[] = [\n { name: 'GET /me', path: '/me' },\n { name: 'GET /org/credentials', path: '/org/credentials' },\n { name: 'GET /org/properties', path: '/org/properties' },\n];\n\nfunction fmtExp(expSec: number | undefined): string {\n if (!expSec) return 'unknown';\n const now = Math.floor(Date.now() / 1000);\n const deltaSec = expSec - now;\n const date = new Date(expSec * 1000).toISOString();\n if (deltaSec < 0) return `${date} (expired ${Math.abs(deltaSec)}s ago)`;\n const days = Math.round(deltaSec / 86400);\n return `${date} (in ~${days} days)`;\n}\n\n/** Keep the first 8 chars of an identifier so users pasting doctor output\n * to IT don't leak full user/org IDs, but we still have enough for triage. */\nfunction truncateId(id: string | undefined): string {\n if (!id) return '(n/a)';\n return id.length > 8 ? `${id.slice(0, 8)}…` : id;\n}\n\nexport async function runAuthDoctor(argv: string[]): Promise<void> {\n const env = getArgValue(argv, '--env');\n const profile = env ? envToProfile(env) : undefined;\n const apiUrlOverride = getArgValue(argv, '--api-url') ?? process.env.CANARY_API_URL;\n\n const stored = await readStoredAuth(profile);\n const token = process.env.CANARY_API_TOKEN ?? stored?.token;\n const apiUrl =\n apiUrlOverride ?? (env && ENV_URLS[env]?.api) ?? stored?.apiUrl ?? 'https://api.trycanary.ai';\n\n console.log('── canary auth doctor ──');\n console.log(`node: ${process.version}`);\n console.log(`platform: ${process.platform}`);\n console.log(`api url: ${apiUrl}`);\n console.log(`profile: ${profile ?? '(default)'}`);\n console.log(`token source: ${process.env.CANARY_API_TOKEN ? 'CANARY_API_TOKEN env' : stored?.token ? 'auth.json' : 'NONE'}`);\n console.log(\n `NODE_EXTRA_CA_CERTS: ${process.env.NODE_EXTRA_CA_CERTS ?? '(not set)'}`\n );\n console.log(`NODE_OPTIONS: ${process.env.NODE_OPTIONS ?? '(not set)'}`);\n\n if (!token) {\n console.log('');\n console.log('No token found. Run: canary login' + (env ? ` --env ${env}` : ''));\n process.exit(1);\n }\n\n const payload = decodeJwtPayload(token);\n const exp = typeof payload?.exp === 'number' ? (payload.exp as number) : undefined;\n const sub = typeof payload?.sub === 'string' ? (payload.sub as string) : undefined;\n const orgId = typeof payload?.org_id === 'string' ? (payload.org_id as string) : undefined;\n console.log('');\n console.log('── token ──');\n console.log(`decoded: ${payload ? 'ok' : 'UNDECODABLE (likely corrupted file)'}`);\n console.log(`sub: ${truncateId(sub)}`);\n console.log(`org_id: ${truncateId(orgId)}`);\n console.log(`exp: ${fmtExp(exp)}`);\n\n console.log('');\n console.log('── probes ──');\n console.log('Each probe exercises a different path — mixed pass/fail here is the');\n console.log('signal for Zscaler URL-category or DLP filtering.');\n console.log('');\n\n let hadProxyInjection = false;\n let hadTls = false;\n let passed = 0;\n\n for (const probe of DEFAULT_PROBES) {\n const url = `${apiUrl}${probe.path}`;\n const result = await apiFetch(url, {\n method: probe.method ?? 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n // Short per-probe timeout so one stalled endpoint doesn't block diagnosis.\n timeoutMs: 10_000,\n });\n if (result.ok) {\n const rid = result.requestId ?? '(absent)';\n console.log(` ✓ ${probe.name.padEnd(28)} 200 rid=${rid}`);\n passed += 1;\n } else {\n const f = result.failure;\n const rid = f.requestId ?? '(absent)';\n console.log(\n ` ✗ ${probe.name.padEnd(28)} ${f.status ?? 'ERR'} [${f.kind}] rid=${rid}`\n );\n const firstLine = f.message.split('\\n')[0];\n console.log(` ${firstLine}`);\n if (f.kind === 'tls') hadTls = true;\n if (f.kind === 'html-body' || f.kind === 'proxy-injected-4xx') hadProxyInjection = true;\n }\n }\n\n console.log('');\n console.log('── diagnosis ──');\n if (hadTls) {\n console.log('TLS verification failed on at least one probe.');\n console.log(' → A corporate proxy is intercepting HTTPS. Trust the proxy root CA:');\n console.log(' export NODE_EXTRA_CA_CERTS=/path/to/corporate-root.pem');\n console.log(' or on Node 20+:');\n console.log(' export NODE_OPTIONS=--use-system-ca');\n } else if (hadProxyInjection) {\n console.log('At least one response was NOT from our API (HTML body or missing');\n console.log('x-canary-request-id). A proxy block page or captive portal is in the way.');\n console.log(' → Share this output with your IT/Zscaler admin and ask them to');\n console.log(` allowlist ${apiUrl}.`);\n } else if (passed === DEFAULT_PROBES.length) {\n console.log('All probes reached our API successfully. Auth + connectivity look healthy.');\n console.log('If you are still seeing intermittent issues, re-run with --debug on the');\n console.log('failing command: CANARY_DEBUG=1 canary <cmd> …');\n } else if (passed > 0) {\n console.log('Mixed results: some probes reached our API, others did not.');\n console.log(' → This is the classic signature of URL-category filtering at the proxy');\n console.log(' (certain paths like /run, /execute are blocked while others pass)');\n console.log(' or partial domain allowlisting.');\n console.log(' → Share the probe table with your IT/Zscaler admin; include the exact');\n console.log(' failing paths and the \"[kind]\" column.');\n } else {\n console.log('Every probe failed. Check your network connection and proxy settings,');\n console.log('then retry with CANARY_DEBUG=1 canary auth doctor.');\n }\n\n if (passed < DEFAULT_PROBES.length) process.exit(1);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/feature-flag.ts"],"sourcesContent":["/**\n * CLI Feature Flag Management\n *\n * Allows superadmins to manage feature flags via the CLI.\n */\n\nimport process from \"node:process\";\nimport { resolveConfig, getArgValue, hasFlag } from \"./auth.js\";\nimport {\n type LifecycleStage,\n toLifecycleLabel,\n parseLifecycleStage,\n apiRequest,\n fetchList,\n} from \"./cli-helpers.js\";\n\ntype FeatureFlag = {\n id: string;\n name: string;\n description: string | null;\n lifecycleStage: LifecycleStage;\n finalValue: boolean | null;\n createdAt: string;\n lastToggledAt: string;\n isStale: boolean;\n organizations?: Array<{\n gateId: string;\n orgId: string;\n orgName: string;\n createdAt: string;\n }>;\n users?: Array<{\n gateId: string;\n userId: string;\n userEmail: string | null;\n createdAt: string;\n }>;\n};\n\ntype FlagApiResponse = {\n ok: boolean;\n error?: string;\n flag?: FeatureFlag;\n};\n\nfunction fetchFlags(apiUrl: string, token: string): Promise<FeatureFlag[]> {\n return fetchList<FeatureFlag>(apiUrl, token, \"/superadmin/feature-flags\", \"flags\");\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const flags = await fetchFlags(apiUrl, token);\n\n if (jsonOutput) {\n console.log(JSON.stringify(flags, null, 2));\n return;\n }\n\n if (flags.length === 0) {\n console.log(\"No feature flags found.\");\n return;\n }\n\n for (const flag of flags) {\n const orgCount = flag.organizations?.length ?? 0;\n const userCount = flag.users?.length ?? 0;\n const lifecycle = `lifecycle=${toLifecycleLabel(flag.lifecycleStage)}`;\n const finalValue =\n flag.lifecycleStage === \"active\" ? \"\" : ` final=${flag.finalValue === true ? \"true\" : \"false\"}`;\n const stale = flag.isStale ? \" [STALE]\" : \"\";\n console.log(` ${flag.name} ${flag.description ?? \"\"} (${lifecycle}${finalValue}) [orgs=${orgCount}, users=${userCount}]${stale}`);\n }\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag create <name> [--description <text>]\");\n process.exit(1);\n }\n\n const description = getArgValue(argv, \"--description\") ?? null;\n const result = await apiRequest<FlagApiResponse>(apiUrl, token, \"POST\", \"/superadmin/feature-flags\", {\n name,\n description,\n });\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Created feature flag: ${name}`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag delete <name>\");\n process.exit(1);\n }\n\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"DELETE\", `/superadmin/feature-flags/${encodeURIComponent(name)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Deleted feature flag: ${name}`);\n}\n\nasync function handleEnable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n const userId = getArgValue(argv, \"--user\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag enable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId && userId) {\n console.error(\"Error: Provide either --org or --user, not both.\");\n process.exit(1);\n }\n\n if (!orgId && !userId) {\n console.error(\"Error: Provide either --org <orgId> or --user <userId>.\");\n console.error(\"Usage: canary feature-flag enable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId) {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/organizations/${encodeURIComponent(orgId)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Enabled ${name} for org ${orgId}`);\n } else {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/users/${encodeURIComponent(userId!)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Enabled ${name} for user ${userId}`);\n }\n}\n\nasync function handleDisable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n const userId = getArgValue(argv, \"--user\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag disable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId && userId) {\n console.error(\"Error: Provide either --org or --user, not both.\");\n process.exit(1);\n }\n\n if (!orgId && !userId) {\n console.error(\"Error: Provide either --org <orgId> or --user <userId>.\");\n console.error(\"Usage: canary feature-flag disable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId) {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"DELETE\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/organizations/${encodeURIComponent(orgId)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Disabled ${name} for org ${orgId}`);\n } else {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"DELETE\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/users/${encodeURIComponent(userId!)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Disabled ${name} for user ${userId}`);\n }\n}\n\nasync function handleLifecycle(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag lifecycle <name> --stage <active|deprecated|ready_for_cleanup> [--final-value true|false]\");\n process.exit(1);\n }\n\n const stage = parseLifecycleStage(argv);\n const rawFinalValue = getArgValue(argv, \"--final-value\");\n const clearFinalValue = hasFlag(argv, \"--clear-final-value\");\n\n if (rawFinalValue !== undefined && clearFinalValue) {\n console.error(\"Error: use either --final-value or --clear-final-value, not both.\");\n process.exit(1);\n }\n\n let finalValue: boolean | undefined = undefined;\n\n if (stage === \"active\") {\n if (rawFinalValue !== undefined) {\n console.error(\"Error: active stage does not accept --final-value.\");\n process.exit(1);\n }\n finalValue = undefined;\n } else {\n if (clearFinalValue) {\n console.error(\"Error: --clear-final-value can only be used with --stage active.\");\n process.exit(1);\n }\n if (!rawFinalValue) {\n console.error(\"Error: --final-value true|false is required for deprecated or ready_for_cleanup.\");\n process.exit(1);\n }\n if (rawFinalValue !== \"true\" && rawFinalValue !== \"false\") {\n console.error(\"Error: --final-value must be true or false.\");\n process.exit(1);\n }\n finalValue = rawFinalValue === \"true\";\n }\n\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/lifecycle`,\n { stage, finalValue }\n );\n\n if (!result.ok || !result.flag) {\n console.error(`Error: ${result.error ?? \"Failed to update lifecycle\"}`);\n process.exit(1);\n }\n\n const final =\n result.flag.lifecycleStage === \"active\"\n ? \"(none)\"\n : result.flag.finalValue === true\n ? \"true\"\n : \"false\";\n console.log(`Updated lifecycle for ${name}: stage=${toLifecycleLabel(result.flag.lifecycleStage)}, final=${final}`);\n}\n\nfunction printFeatureFlagHelp(): void {\n console.log(\n [\n \"Usage: canary feature-flag <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" list List all feature flags\",\n \" create <name> [--description <text>] Create a new flag\",\n \" delete <name> Delete a flag and all its gates\",\n \" enable <name> (--org <orgId> | --user <userId>)\",\n \" Enable a flag for an org or user\",\n \" disable <name> (--org <orgId> | --user <userId>)\",\n \" Disable a flag for an org or user\",\n \" lifecycle <name> --stage <stage> [--final-value true|false]\",\n \" Mark lifecycle + final value\",\n \"\",\n \"Stages: active, deprecated, ready_for_cleanup\",\n \"\",\n \"Options:\",\n \" --org <orgId> Target organization\",\n \" --user <userId> Target user\",\n \" --final-value <true|false> Final value for deprecated/ready_for_cleanup\",\n \" --clear-final-value Clear final value (only valid with --stage active)\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --json Output as JSON (list only)\",\n \" --api-url <url> API URL override (takes precedence over --env)\",\n \" --token <key> API token override\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runFeatureFlag(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printFeatureFlagHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"list\":\n await handleList(rest, apiUrl, token);\n break;\n case \"create\":\n await handleCreate(rest, apiUrl, token);\n break;\n case \"delete\":\n await handleDelete(rest, apiUrl, token);\n break;\n case \"enable\":\n await handleEnable(rest, apiUrl, token);\n break;\n case \"disable\":\n await handleDisable(rest, apiUrl, token);\n break;\n case \"lifecycle\":\n await handleLifecycle(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printFeatureFlagHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/knobs.ts"],"sourcesContent":["/**\n * CLI Knobs Management\n *\n * Allows superadmins to manage knobs (global config) via the CLI.\n */\n\nimport process from \"node:process\";\nimport { resolveConfig, getArgValue, hasFlag } from \"./auth.js\";\nimport {\n type LifecycleStage,\n toLifecycleLabel,\n parseLifecycleStage,\n apiRequest,\n fetchList,\n} from \"./cli-helpers.js\";\n\ntype SerializedKnob = {\n key: string;\n description: string | null;\n valueType: \"boolean\" | \"string\" | \"number\" | \"json\";\n value: unknown;\n lifecycleStage: LifecycleStage;\n finalValue: unknown;\n updatedAt: string | null;\n updatedBy: string | null;\n createdAt: string | null;\n isStale: boolean;\n};\n\ntype KnobApiResponse = {\n ok: boolean;\n error?: string;\n knob?: SerializedKnob;\n};\n\nfunction formatValue(valueType: SerializedKnob[\"valueType\"], value: unknown): string {\n if (valueType === \"json\") return JSON.stringify(value);\n return String(value);\n}\n\nfunction formatFinalValue(knob: SerializedKnob): string {\n if (knob.lifecycleStage === \"active\") return \"(none)\";\n return formatValue(knob.valueType, knob.finalValue);\n}\n\nfunction parseTypedValue(raw: string, valueType: SerializedKnob[\"valueType\"]): unknown {\n switch (valueType) {\n case \"boolean\":\n if (raw !== \"true\" && raw !== \"false\") {\n console.error('Error: Boolean value must be \"true\" or \"false\".');\n process.exit(1);\n }\n return raw === \"true\";\n case \"string\":\n return raw;\n case \"number\": {\n const num = parseFloat(raw);\n if (Number.isNaN(num)) {\n console.error(`Error: Invalid number: ${raw}`);\n process.exit(1);\n }\n return num;\n }\n case \"json\":\n try {\n return JSON.parse(raw);\n } catch {\n console.error(`Error: Invalid JSON: ${raw}`);\n process.exit(1);\n }\n }\n}\n\nfunction fetchKnobs(apiUrl: string, token: string): Promise<SerializedKnob[]> {\n return fetchList<SerializedKnob>(apiUrl, token, \"/superadmin/knobs\", \"knobs\");\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const knobs = await fetchKnobs(apiUrl, token);\n\n if (jsonOutput) {\n console.log(JSON.stringify(knobs, null, 2));\n return;\n }\n\n if (knobs.length === 0) {\n console.log(\"No knobs found.\");\n return;\n }\n\n for (const knob of knobs) {\n const desc = knob.description ? ` ${knob.description}` : \"\";\n const lifecycle = `lifecycle=${toLifecycleLabel(knob.lifecycleStage)}`;\n const finalValue =\n knob.lifecycleStage === \"active\" ? \"\" : ` final=${formatFinalValue(knob)}`;\n const stale = knob.isStale ? \" [STALE]\" : \"\";\n console.log(\n ` ${knob.key} [${knob.valueType}] = ${formatValue(knob.valueType, knob.value)} (${lifecycle}${finalValue})${desc}${stale}`\n );\n }\n}\n\nasync function handleGet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs get <key>\");\n process.exit(1);\n }\n\n const jsonOutput = hasFlag(argv, \"--json\");\n const knobs = await fetchKnobs(apiUrl, token);\n const knob = knobs.find((k) => k.key === key);\n\n if (!knob) {\n console.error(`Knob not found: ${key}`);\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify(knob, null, 2));\n return;\n }\n\n console.log(` Key: ${knob.key}`);\n console.log(` Type: ${knob.valueType}`);\n console.log(` Value: ${formatValue(knob.valueType, knob.value)}`);\n console.log(` Lifecycle: ${toLifecycleLabel(knob.lifecycleStage)}`);\n console.log(` Final value: ${formatFinalValue(knob)}`);\n console.log(` Description: ${knob.description ?? \"(none)\"}`);\n console.log(` Updated: ${knob.updatedAt ?? \"(never)\"}`);\n if (knob.isStale) {\n console.log(` Status: STALE (no changes in 30+ days)`);\n }\n}\n\nasync function handleSet(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\");\n process.exit(1);\n }\n\n const rawValue = argv[1];\n if (rawValue === undefined || rawValue.startsWith(\"--\")) {\n console.error(\"Error: Missing knob value.\");\n console.error(\"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\");\n process.exit(1);\n }\n\n const valueType = getArgValue(argv, \"--type\") as SerializedKnob[\"valueType\"] | undefined;\n if (!valueType || ![\"boolean\", \"string\", \"number\", \"json\"].includes(valueType)) {\n console.error(\"Error: --type is required and must be one of: boolean, string, number, json\");\n process.exit(1);\n }\n\n const value = parseTypedValue(rawValue, valueType);\n const description = getArgValue(argv, \"--description\") ?? undefined;\n\n const result = await apiRequest<KnobApiResponse>(apiUrl, token, \"POST\", \"/superadmin/knobs\", {\n key,\n valueType,\n value,\n description,\n });\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Set knob: ${key} = ${formatValue(valueType, result.knob?.value)}`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs delete <key>\");\n process.exit(1);\n }\n\n const result = await apiRequest<KnobApiResponse>(\n apiUrl, token, \"DELETE\", `/superadmin/knobs/${encodeURIComponent(key)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Deleted knob: ${key}`);\n}\n\nasync function handleToggle(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs toggle <key>\");\n process.exit(1);\n }\n\n const result = await apiRequest<KnobApiResponse>(\n apiUrl, token, \"POST\", `/superadmin/knobs/${encodeURIComponent(key)}/toggle`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n const knob = result.knob;\n console.log(`Toggled knob: ${key} -> ${formatValue(knob?.valueType ?? \"boolean\", knob?.value)}`);\n}\n\nasync function handleLifecycle(argv: string[], apiUrl: string, token: string): Promise<void> {\n const key = argv[0];\n if (!key || key.startsWith(\"--\")) {\n console.error(\"Error: Missing knob key.\");\n console.error(\"Usage: canary knobs lifecycle <key> --stage <active|deprecated|ready_for_cleanup> [--final-value <value>]\");\n process.exit(1);\n }\n\n const stage = parseLifecycleStage(argv);\n const rawFinalValue = getArgValue(argv, \"--final-value\");\n const clearFinalValue = hasFlag(argv, \"--clear-final-value\");\n\n if (rawFinalValue !== undefined && clearFinalValue) {\n console.error(\"Error: use either --final-value or --clear-final-value, not both.\");\n process.exit(1);\n }\n\n const knobs = await fetchKnobs(apiUrl, token);\n const knob = knobs.find((k) => k.key === key);\n\n if (!knob) {\n console.error(`Knob not found: ${key}`);\n process.exit(1);\n }\n\n let finalValue: unknown = undefined;\n\n if (stage === \"active\") {\n if (rawFinalValue !== undefined) {\n console.error(\"Error: active stage does not accept --final-value. Use --stage deprecated|ready_for_cleanup.\");\n process.exit(1);\n }\n finalValue = clearFinalValue ? undefined : undefined;\n } else {\n if (clearFinalValue) {\n console.error(\"Error: --clear-final-value can only be used with --stage active.\");\n process.exit(1);\n }\n if (rawFinalValue === undefined) {\n console.error(\"Error: --final-value is required when stage is deprecated or ready_for_cleanup.\");\n process.exit(1);\n }\n finalValue = parseTypedValue(rawFinalValue, knob.valueType);\n }\n\n const result = await apiRequest<KnobApiResponse>(\n apiUrl, token, \"POST\", `/superadmin/knobs/${encodeURIComponent(key)}/lifecycle`,\n { stage, finalValue }\n );\n\n if (!result.ok || !result.knob) {\n console.error(`Error: ${result.error ?? \"Failed to update lifecycle\"}`);\n process.exit(1);\n }\n\n console.log(\n `Updated lifecycle for ${key}: stage=${toLifecycleLabel(result.knob.lifecycleStage)}, final=${formatFinalValue(result.knob)}`\n );\n}\n\nfunction printKnobsHelp(): void {\n console.log(\n [\n \"Usage: canary knobs <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" list List all knobs\",\n \" get <key> Get a knob value\",\n \" set <key> <value> --type <type> [--description <text>]\",\n \" Set a knob value\",\n \" delete <key> Delete a knob\",\n \" toggle <key> Toggle a boolean knob\",\n \" lifecycle <key> --stage <stage> [--final-value <value>]\",\n \" Mark knob lifecycle + final value\",\n \"\",\n \"Types: boolean, string, number, json\",\n \"Stages: active, deprecated, ready_for_cleanup\",\n \"\",\n \"Options:\",\n \" --final-value <value> Final value for deprecated/ready_for_cleanup\",\n \" --clear-final-value Clear final value (only valid with --stage active)\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --json Output as JSON (list/get only)\",\n \" --api-url <url> API URL override (takes precedence over --env)\",\n \" --token <key> API token override\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runKnobs(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printKnobsHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"list\":\n await handleList(rest, apiUrl, token);\n break;\n case \"get\":\n await handleGet(rest, apiUrl, token);\n break;\n case \"set\":\n await handleSet(rest, apiUrl, token);\n break;\n case \"delete\":\n await handleDelete(rest, apiUrl, token);\n break;\n case \"toggle\":\n await handleToggle(rest, apiUrl, token);\n break;\n case \"lifecycle\":\n await handleLifecycle(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printKnobsHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/mcp/index.ts","../../mcp-tools/src/context.ts","../../mcp-tools/src/api-client.ts","../../mcp-tools/src/tool-helpers.ts","../../mcp-tools/src/registry.ts","../../mcp-tools/src/groups/workflows.ts","../../mcp-tools/src/groups/workflow-proposals.ts","../../mcp-tools/src/groups/workflow-runs.ts","../../mcp-tools/src/groups/test-suites.ts","../../mcp-tools/src/groups/credentials.ts","../../mcp-tools/src/groups/issues.ts","../../mcp-tools/src/groups/index.ts","../src/mcp/api-url.ts","../src/mcp/tool-helpers.ts","../src/mcp/session-tools.ts","../src/mcp/browser-tools.ts","../src/mcp/login-tool.ts","../src/mcp/upgrade-tool.ts","../src/mcp/instructions.ts"],"sourcesContent":["/**\n * MCP Server for Canary CLI.\n *\n * Combines:\n * - HTTP-only shared tools from @chatsdet/mcp-tools (workflows, runs, suites,\n * credentials, issues, proposals)\n * - CLI-local tools (login, upgrade, browser daemon, cloud-relay sessions)\n * that depend on local state and can't be hosted remotely.\n *\n * @module\n */\n\nimport process from 'node:process';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { buildRegistry, sharedGroups } from '@chatsdet/mcp-tools';\nimport { readStoredToken } from '../auth';\nimport { resolveApiUrl } from './api-url';\nimport { toolText } from './tool-helpers';\nimport { getSessionToolDefinitions, handleSessionTool } from './session-tools';\nimport { getBrowserMCPToolDefinitions, handleBrowserTool } from './browser-tools';\nimport { getLoginToolDefinitions, handleLoginTool } from './login-tool';\nimport { getUpgradeToolDefinitions, handleUpgradeTool } from './upgrade-tool';\nimport { CANARY_MCP_INSTRUCTIONS } from './instructions';\n\nasync function resolveCtx() {\n const apiToken = process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n if (!apiToken) {\n throw new Error('Missing token. Call the canary_login tool or set CANARY_API_TOKEN.');\n }\n return { apiUrl: resolveApiUrl(), apiToken };\n}\n\nexport async function runMcp(_argv: string[]) {\n const registry = buildRegistry(sharedGroups);\n\n const server = new Server(\n { name: 'canary-cli', version: '0.1.0' },\n {\n capabilities: { tools: {} },\n instructions: CANARY_MCP_INSTRUCTIONS,\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n ...getLoginToolDefinitions(),\n ...getUpgradeToolDefinitions(),\n ...getSessionToolDefinitions(),\n ...getBrowserMCPToolDefinitions(),\n ...registry.list(),\n ],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n const tool = req.params.name;\n const args = (req.params.arguments ?? {}) as Record<string, unknown>;\n\n // CLI-local groups try first; each returns null if the tool is not theirs.\n const loginResult = await handleLoginTool(tool, args);\n if (loginResult) return loginResult;\n\n const upgradeResult = await handleUpgradeTool(tool, args);\n if (upgradeResult) return upgradeResult;\n\n const browserResult = await handleBrowserTool(tool, args);\n if (browserResult) return browserResult;\n\n const sessionResult = await handleSessionTool(tool, args);\n if (sessionResult) return sessionResult;\n\n // Shared HTTP-only tools — provide ctx for this call.\n try {\n const ctx = await resolveCtx();\n return await registry.call(tool, args, ctx);\n } catch (err) {\n return toolText(err instanceof Error ? err.message : String(err));\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n return new Promise<void>(() => undefined);\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Per-request context for MCP tool handlers. Threaded via AsyncLocalStorage so\n * handler bodies don't have to take ctx as a parameter — `mcpApiRequest()`\n * pulls it from `currentToolCtx()`.\n */\nexport type ToolCtx = {\n apiUrl: string;\n apiToken: string;\n};\n\nconst storage = new AsyncLocalStorage<ToolCtx>();\n\nexport function withToolCtx<T>(ctx: ToolCtx, fn: () => Promise<T>): Promise<T> {\n return storage.run(ctx, fn);\n}\n\nexport function currentToolCtx(): ToolCtx | undefined {\n return storage.getStore();\n}\n","import { currentToolCtx } from './context';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Authenticated API request. Pulls `{ apiUrl, apiToken }` from the\n * AsyncLocalStorage-backed ToolCtx — every tool handler runs inside a\n * `withToolCtx()` frame set up by the registry dispatcher.\n *\n * Throws on non-2xx with a prefixed message MCP clients can pattern-match\n * (`[unauthorized]`, `[timeout]`, `[http-N]`).\n */\nexport async function mcpApiRequest<T>(\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<T> {\n const ctx = currentToolCtx();\n if (!ctx) {\n throw new Error('[no-context] mcpApiRequest called outside of withToolCtx()');\n }\n\n const url = `${ctx.apiUrl}${path}`;\n let res: Response;\n try {\n res = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${ctx.apiToken}`,\n 'Content-Type': 'application/json',\n },\n signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n } catch (err) {\n if (err instanceof Error && err.name === 'TimeoutError') {\n throw new Error(`[timeout] upstream API did not respond within ${DEFAULT_TIMEOUT_MS}ms`);\n }\n throw new Error(`[network] ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error('[unauthorized] Your session may have expired.');\n }\n const text = await res.text().catch(() => '');\n throw new Error(`[http-${res.status}] ${text || res.statusText}`);\n }\n\n return (await res.json()) as T;\n}\n","export function toolText(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nexport function toolJson(data: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] };\n}\n","import type { ToolCallResult, ToolDefinition, ToolGroup } from './types';\nimport { type ToolCtx, withToolCtx } from './context';\nimport { toolText } from './tool-helpers';\n\nexport type Registry = {\n list(): ToolDefinition[];\n call(tool: string, args: Record<string, unknown>, ctx: ToolCtx): Promise<ToolCallResult>;\n};\n\n/**\n * Bundle a set of ToolGroups into a Registry. The Registry runs each tool call\n * inside an `AsyncLocalStorage` frame so handler bodies can call\n * `mcpApiRequest()` without taking ctx as a parameter.\n */\nexport function buildRegistry(groups: ToolGroup[]): Registry {\n return {\n list() {\n return groups.flatMap((g) => g.definitions());\n },\n\n async call(tool, args, ctx) {\n return withToolCtx(ctx, async () => {\n for (const group of groups) {\n const result = await group.handle(tool, args);\n if (result) return result;\n }\n return toolText(`Unknown tool: ${tool}`);\n });\n },\n };\n}\n","/**\n * MCP tools for workflow management.\n *\n * Mirrors `canary workflow list` and `canary workflow get`.\n *\n * @module\n */\n\nimport { mcpApiRequest } from '../api-client';\nimport { toolJson } from '../tool-helpers';\n\nexport function getWorkflowToolDefinitions() {\n return [\n {\n name: 'canary_workflow_list',\n description:\n 'List workflows in the current organization. Supports filtering by name, type, and status with pagination.',\n inputSchema: {\n type: 'object',\n properties: {\n search: {\n type: 'string',\n description: 'Search workflows by name',\n },\n flowType: {\n type: 'string',\n enum: ['standard', 'setup', 'seed', 'teardown'],\n description: 'Filter by flow type',\n },\n status: {\n type: 'string',\n enum: ['draft', 'published'],\n description:\n \"Filter by lifecycle status. 'draft' = editable, not run by suites. 'published' = active, included in regression runs. Reliability (passing/flaky/degraded) is the separate `health` dimension.\",\n },\n page: {\n type: 'number',\n description: 'Page number (default: 1)',\n },\n pageSize: {\n type: 'number',\n description: 'Page size (default: 25)',\n },\n },\n },\n },\n {\n name: 'canary_workflow_get',\n description:\n 'Get a workflow definition with its steps, nodes, and configuration details.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n },\n required: ['workflowId'],\n },\n },\n {\n name: 'canary_workflow_patch',\n description:\n 'Update workflow properties using merge-patch. Only provided fields are updated; omitted fields are left unchanged.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n name: {\n type: 'string',\n description: 'New workflow name',\n },\n description: {\n type: 'string',\n description: 'New description',\n },\n status: {\n type: 'string',\n enum: ['draft', 'published'],\n description:\n \"New lifecycle status. 'draft' = editable, not run by suites. 'published' = active, included in regression runs. Reliability is tracked automatically by the `health` dimension; a flow that keeps failing becomes `degraded` and is skipped in suites until edited or re-enabled.\",\n },\n flowType: {\n type: 'string',\n enum: ['standard', 'setup', 'seed', 'teardown'],\n description: 'New flow type',\n },\n },\n required: ['workflowId'],\n },\n },\n {\n name: 'canary_workflow_run',\n description:\n 'Trigger a workflow execution. Returns the run ID for tracking status via canary_workflow_runs_get.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID to execute',\n },\n },\n required: ['workflowId'],\n },\n },\n {\n name: 'canary_workflow_sync',\n description:\n 'Atomically replace all nodes and edges in a workflow. Automatically creates a draft snapshot of the current state before overwriting so changes can be reverted. Use canary_workflow_get to read the current DAG first, modify nodes/edges, then sync back. When using propose=true on an existing workflow, REUSE each existing node\\'s `id` for steps you keep (omit `id` only for genuinely new steps). Without ID reuse the proposal will diff as a full rewrite — every step shown as Removed+Added in the review UI even when only configs changed.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n nodes: {\n type: 'array',\n description: 'Array of node objects with id, nodeType, orderIndex, configJson',\n items: { type: 'object' },\n },\n edges: {\n type: 'array',\n description: 'Array of edge objects with id, sourceNodeId, targetNodeId, edgeType',\n items: { type: 'object' },\n },\n createSnapshot: {\n type: 'boolean',\n description: 'Create a draft snapshot before syncing (default: true)',\n default: true,\n },\n propose: {\n type: 'boolean',\n description: 'Create a proposal instead of applying directly. The proposal can be reviewed and accepted/rejected via the UI. IMPORTANT: when proposing on an existing workflow, reuse each existing node\\'s `id` for steps you intend to keep — only omit `id` for genuinely new steps. Otherwise the diff will show every step as Removed+Added.',\n default: false,\n },\n proposalTitle: {\n type: 'string',\n description: 'Title for the proposal (used with propose: true)',\n },\n proposalDescription: {\n type: 'string',\n description: 'Description/rationale for the proposal (used with propose: true)',\n },\n },\n required: ['workflowId', 'nodes', 'edges'],\n },\n },\n ];\n}\n\nexport async function handleWorkflowTool(\n tool: string,\n args: Record<string, unknown>\n) {\n if (tool === 'canary_workflow_list') {\n try {\n const params = new URLSearchParams();\n if (args.search) params.set('search', String(args.search));\n if (args.flowType) params.set('flowType', String(args.flowType));\n if (args.status) params.set('status', String(args.status));\n if (args.page) params.set('page', String(args.page));\n if (args.pageSize) params.set('pageSize', String(args.pageSize));\n\n const qs = params.toString();\n const path = `/workflows${qs ? `?${qs}` : ''}`;\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: unknown[];\n pagination: unknown;\n }>('GET', path);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, data: result.data, pagination: result.pagination });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_workflow_get') {\n try {\n const workflowId = args.workflowId as string;\n const result = await mcpApiRequest<{\n ok?: boolean;\n error?: string;\n workflow?: unknown;\n targetOrgId?: string;\n targetOrgName?: string;\n }>('GET', `/workflows/${workflowId}`);\n\n if (result.error) {\n return toolJson({ ok: false, error: result.error });\n }\n\n return toolJson({ ok: true, workflow: result.workflow });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_workflow_patch') {\n try {\n const workflowId = args.workflowId as string;\n const body: Record<string, unknown> = {};\n if (args.name) body.name = String(args.name);\n if (args.description) body.description = String(args.description);\n if (args.status) body.status = String(args.status);\n if (args.flowType) body.flowType = String(args.flowType);\n\n const result = await mcpApiRequest<{\n ok?: boolean;\n error?: string;\n workflow?: unknown;\n }>('PUT', `/workflows/${workflowId}`, body);\n\n if (result.error) {\n return toolJson({ ok: false, error: result.error });\n }\n\n return toolJson({ ok: true, workflow: result.workflow });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_workflow_run') {\n try {\n const workflowId = args.workflowId as string;\n\n const result = await mcpApiRequest<{\n ok?: boolean;\n error?: string;\n runId?: string;\n streamId?: string;\n }>('POST', `/workflows/${workflowId}/execute`);\n\n if (result.error) {\n return toolJson({ ok: false, error: result.error });\n }\n\n return toolJson({ ok: true, runId: result.runId, streamId: result.streamId });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_workflow_sync') {\n try {\n const workflowId = args.workflowId as string;\n const nodes = args.nodes as unknown[];\n const edges = args.edges as unknown[];\n\n // --propose: create a proposal instead of applying directly\n if (args.propose) {\n const title = (args.proposalTitle as string) ?? `Sync proposal (${nodes.length} nodes)`;\n const description = (args.proposalDescription as string) ?? null;\n\n const result = await mcpApiRequest<{\n ok?: boolean;\n error?: string;\n data?: { id: string; title: string; status: string };\n }>('POST', `/workflows/${workflowId}/proposals`, {\n title,\n description,\n snapshotJson: { nodes, edges },\n source: 'api',\n });\n\n if (result.error) {\n return toolJson({ ok: false, error: result.error });\n }\n\n return toolJson({\n ok: true,\n proposalId: result.data?.id,\n title: result.data?.title,\n status: result.data?.status,\n });\n }\n\n const createSnapshot = args.createSnapshot !== false;\n\n const result = await mcpApiRequest<{\n ok?: boolean;\n error?: string;\n snapshotVersion?: string;\n }>('PUT', `/workflows/${workflowId}/draft-state`, {\n nodes,\n edges,\n createSnapshot,\n });\n\n if (result.error) {\n return toolJson({ ok: false, error: result.error });\n }\n\n return toolJson({\n ok: true,\n ...(result.snapshotVersion ? { snapshotVersion: result.snapshotVersion } : {}),\n });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n return null;\n}\n","/**\n * MCP tools for workflow proposals: list / get / diff / accept / reject /\n * revert. Mirrors `canary workflow proposals <verb>` with the same JSON\n * output shape so agents get identical responses whether they shell-out or\n * call MCP directly.\n *\n * @module\n */\n\nimport {\n diffSnapshots,\n fromApiSnapshot,\n type SnapshotDiff,\n} from '@chatsdet/workflow-diff';\nimport { mcpApiRequest } from '../api-client';\nimport { toolJson } from '../tool-helpers';\n\n// Types duplicated here to avoid the MCP server importing from the CLI's\n// `workflow-proposals.ts` which would pull in TTY-detection + process.exit\n// paths that don't belong in MCP land.\ninterface ProposalSummary {\n id: string;\n workflowId: string;\n title: string;\n description: string | null;\n source: string;\n status: string;\n createdByUserId: string | null;\n decidedByUserId: string | null;\n decidedAt: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface ProposalDetail extends ProposalSummary {\n snapshotJson: {\n nodes?: Array<{ id: string; nodeType: string; orderIndex?: number; configJson?: Record<string, unknown> }>;\n edges?: Array<{ id: string; sourceNodeId: string; targetNodeId: string; edgeType?: string | null }>;\n };\n sourceMetadata: Record<string, unknown> | null;\n}\n\ninterface WorkflowDetail {\n id: string;\n nodes: Array<{ id: string; nodeType: string; orderIndex?: number; configJson?: Record<string, unknown> }>;\n edges: Array<{ id: string; sourceNodeId: string; targetNodeId: string; edgeType?: string | null }>;\n}\n\nexport function getWorkflowProposalsToolDefinitions() {\n return [\n {\n name: 'canary_workflow_proposals_list',\n description:\n 'List workflow proposals. By default returns pending proposals; pass status=\"all\" for every status.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: { type: 'string', description: 'Workflow UUID' },\n status: {\n type: 'string',\n enum: ['pending', 'accepted', 'rejected', 'stale', 'all'],\n description: 'Filter by status (default: pending)',\n },\n },\n required: ['workflowId'],\n },\n },\n {\n name: 'canary_workflow_proposals_get',\n description: 'Fetch a proposal including its full snapshot JSON.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: { type: 'string' },\n proposalId: { type: 'string' },\n },\n required: ['workflowId', 'proposalId'],\n },\n },\n {\n name: 'canary_workflow_proposals_diff',\n description:\n 'Compute a per-node diff between a proposal and the current workflow draft. Returns summary counts plus per-field changes for changed nodes.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: { type: 'string' },\n proposalId: { type: 'string' },\n },\n required: ['workflowId', 'proposalId'],\n },\n },\n {\n name: 'canary_workflow_proposals_accept',\n description:\n 'Apply a proposal to the workflow draft. Omit acceptedNodeIds for atomic (all-or-nothing) accept; provide it to cherry-pick a subset.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: { type: 'string' },\n proposalId: { type: 'string' },\n acceptedNodeIds: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Optional: node ids from the diff to include. Omit for atomic accept of every change.',\n },\n },\n required: ['workflowId', 'proposalId'],\n },\n },\n {\n name: 'canary_workflow_proposals_reject',\n description: 'Reject a pending proposal. Does not change the workflow draft.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: { type: 'string' },\n proposalId: { type: 'string' },\n },\n required: ['workflowId', 'proposalId'],\n },\n },\n {\n name: 'canary_workflow_proposals_revert',\n description:\n 'Revert a previously-accepted proposal back to pending. Only the user who accepted it may revert.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: { type: 'string' },\n proposalId: { type: 'string' },\n },\n required: ['workflowId', 'proposalId'],\n },\n },\n ];\n}\n\nexport async function handleWorkflowProposalsTool(\n tool: string,\n args: Record<string, unknown>,\n) {\n if (!tool.startsWith('canary_workflow_proposals_')) return null;\n\n try {\n switch (tool) {\n case 'canary_workflow_proposals_list':\n return await listProposals(args);\n case 'canary_workflow_proposals_get':\n return await getProposal(args);\n case 'canary_workflow_proposals_diff':\n return await diffProposal(args);\n case 'canary_workflow_proposals_accept':\n return await acceptProposal(args);\n case 'canary_workflow_proposals_reject':\n return await rejectProposal(args);\n case 'canary_workflow_proposals_revert':\n return await revertProposal(args);\n default:\n return null;\n }\n } catch (err) {\n return toolJson({\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n}\n\n// ── Tool implementations ─────────────────────────────────────────────────\n\nasync function listProposals(args: Record<string, unknown>) {\n const workflowId = String(args.workflowId);\n const status = typeof args.status === 'string' ? args.status : 'pending';\n const query = status === 'all' ? '' : `?status=${encodeURIComponent(status)}`;\n const res = await mcpApiRequest<{\n ok: boolean;\n data?: ProposalSummary[];\n error?: string;\n }>('GET', `/workflows/${workflowId}/proposals${query}`);\n return toolJson({\n ok: res.ok,\n workflowId,\n count: res.data?.length ?? 0,\n proposals: res.data ?? [],\n error: res.error,\n });\n}\n\nasync function getProposal(args: Record<string, unknown>) {\n const workflowId = String(args.workflowId);\n const proposalId = String(args.proposalId);\n const res = await mcpApiRequest<{\n ok: boolean;\n data?: ProposalDetail;\n error?: string;\n }>('GET', `/workflows/${workflowId}/proposals/${proposalId}`);\n return toolJson(res);\n}\n\nasync function diffProposal(args: Record<string, unknown>) {\n const workflowId = String(args.workflowId);\n const proposalId = String(args.proposalId);\n const [propRes, wfRes] = await Promise.all([\n mcpApiRequest<{ ok: boolean; data?: ProposalDetail; error?: string }>(\n 'GET',\n `/workflows/${workflowId}/proposals/${proposalId}`,\n ),\n mcpApiRequest<{ ok: boolean; workflow?: WorkflowDetail; error?: string }>(\n 'GET',\n `/workflows/${workflowId}`,\n ),\n ]);\n if (!propRes.ok || !propRes.data) {\n return toolJson({ ok: false, error: propRes.error ?? 'proposal not found' });\n }\n if (!wfRes.workflow) {\n return toolJson({ ok: false, error: wfRes.error ?? 'workflow not found' });\n }\n const current = fromApiSnapshot({\n nodes: wfRes.workflow.nodes,\n edges: wfRes.workflow.edges,\n });\n const proposed = fromApiSnapshot(propRes.data.snapshotJson);\n const diff = diffSnapshots(current, proposed);\n return toolJson(buildDiffPayload(propRes.data, diff));\n}\n\nasync function acceptProposal(args: Record<string, unknown>) {\n const workflowId = String(args.workflowId);\n const proposalId = String(args.proposalId);\n const acceptedNodeIds = Array.isArray(args.acceptedNodeIds)\n ? (args.acceptedNodeIds as string[]).filter((s) => typeof s === 'string')\n : null;\n const body = acceptedNodeIds\n ? { selection: { acceptedNodeIds } }\n : undefined;\n const res = await mcpApiRequest<{ ok: boolean; error?: string }>(\n 'POST',\n `/workflows/${workflowId}/proposals/${proposalId}/accept`,\n body,\n );\n return toolJson({\n ...res,\n workflowId,\n proposalId,\n partial: acceptedNodeIds !== null,\n });\n}\n\nasync function rejectProposal(args: Record<string, unknown>) {\n const workflowId = String(args.workflowId);\n const proposalId = String(args.proposalId);\n const res = await mcpApiRequest<{ ok: boolean; error?: string }>(\n 'POST',\n `/workflows/${workflowId}/proposals/${proposalId}/reject`,\n );\n return toolJson({ ...res, workflowId, proposalId });\n}\n\nasync function revertProposal(args: Record<string, unknown>) {\n const workflowId = String(args.workflowId);\n const proposalId = String(args.proposalId);\n const res = await mcpApiRequest<{ ok: boolean; error?: string }>(\n 'POST',\n `/workflows/${workflowId}/proposals/${proposalId}/revert`,\n );\n return toolJson({ ...res, workflowId, proposalId });\n}\n\n// ── Diff payload shaper (shared shape with the CLI) ──────────────────────\n\nfunction buildDiffPayload(proposal: ProposalDetail, diff: SnapshotDiff) {\n return {\n proposal: {\n id: proposal.id,\n title: proposal.title,\n source: proposal.source,\n status: proposal.status,\n stale: proposal.status === 'stale',\n createdAt: proposal.createdAt,\n decidedAt: proposal.decidedAt,\n },\n summary: {\n addedNodes: diff.addedNodes.length,\n removedNodes: diff.removedNodes.length,\n changedNodes: diff.changedNodes.length,\n addedEdges: diff.addedEdges.length,\n removedEdges: diff.removedEdges.length,\n touchedCount: diff.touchedCount,\n isEmpty: diff.isEmpty,\n },\n nodes: {\n added: diff.addedNodes.map((n) => ({\n id: n.id,\n nodeType: n.nodeType,\n configJson: n.configJson,\n })),\n removed: diff.removedNodes.map((n) => ({\n id: n.id,\n nodeType: n.nodeType,\n configJson: n.configJson,\n })),\n changed: diff.changedNodes.map((c) => ({\n id: c.id,\n nodeType: c.nodeType,\n typeChanged: c.typeChanged,\n beforeType: c.beforeType,\n afterType: c.afterType,\n fieldChanges: c.fieldChanges,\n })),\n },\n edges: {\n added: diff.addedEdges.map((e) => ({\n sourceNodeId: e.sourceNodeId,\n targetNodeId: e.targetNodeId,\n edgeType: e.edgeType ?? null,\n })),\n removed: diff.removedEdges.map((e) => ({\n sourceNodeId: e.sourceNodeId,\n targetNodeId: e.targetNodeId,\n edgeType: e.edgeType ?? null,\n })),\n },\n };\n}\n","/**\n * MCP tools for workflow run results.\n *\n * Mirrors `canary workflow runs list`, `get`, and `diagnostics`.\n *\n * @module\n */\n\nimport { mcpApiRequest } from '../api-client';\nimport { toolJson } from '../tool-helpers';\n\nexport function getWorkflowRunsToolDefinitions() {\n return [\n {\n name: 'canary_workflow_runs_list',\n description:\n 'List runs for a specific workflow with status, duration, and pagination.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n page: {\n type: 'number',\n description: 'Page number (default: 1)',\n },\n pageSize: {\n type: 'number',\n description: 'Page size (default: 10)',\n },\n mode: {\n type: 'string',\n enum: ['browser', 'api'],\n description: 'Filter by run mode',\n },\n },\n required: ['workflowId'],\n },\n },\n {\n name: 'canary_workflow_runs_get',\n description:\n 'Get workflow run detail including steps, status, duration, and error messages. Also serves as a status check for in-progress runs.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n runId: {\n type: 'string',\n description: 'The workflow run UUID',\n },\n },\n required: ['workflowId', 'runId'],\n },\n },\n {\n name: 'canary_workflow_runs_diagnostics',\n description:\n 'Get failure diagnostic for a workflow run. Returns AI-generated analysis if available, otherwise falls back to console logs and network requests.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n runId: {\n type: 'string',\n description: 'The workflow run UUID',\n },\n },\n required: ['workflowId', 'runId'],\n },\n },\n ];\n}\n\nexport async function handleWorkflowRunsTool(\n tool: string,\n args: Record<string, unknown>\n) {\n if (tool === 'canary_workflow_runs_list') {\n try {\n const workflowId = args.workflowId as string;\n const params = new URLSearchParams();\n params.set('page', String(args.page ?? 1));\n params.set('pageSize', String(args.pageSize ?? 10));\n if (args.mode) params.set('mode', String(args.mode));\n\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: { runs: unknown[] };\n pagination: unknown;\n }>('GET', `/workflows/${workflowId}/runs?${params}`);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, runs: result.data.runs, pagination: result.pagination });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_workflow_runs_get') {\n try {\n const workflowId = args.workflowId as string;\n const runId = args.runId as string;\n\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: unknown;\n }>('GET', `/workflows/${workflowId}/runs/${runId}`);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, data: result.data });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_workflow_runs_diagnostics') {\n try {\n const workflowId = args.workflowId as string;\n const runId = args.runId as string;\n\n // Try AI failure diagnostic first\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: unknown;\n }>('GET', `/workflows/${workflowId}/runs/${runId}/failure-diagnostic`);\n\n if (result.ok && result.data) {\n return toolJson({ ok: true, data: result.data });\n }\n\n // Fall back to raw diagnostics\n const rawResult = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n consoleLogs: unknown[];\n networkRequests: unknown[];\n }>('GET', `/workflows/${workflowId}/runs/${runId}/diagnostics`);\n\n if (!rawResult.ok) {\n return toolJson({ ok: true, data: null, message: 'No diagnostic available' });\n }\n\n return toolJson({\n ok: true,\n consoleLogs: rawResult.consoleLogs,\n networkRequests: rawResult.networkRequests,\n });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n return null;\n}\n","/**\n * MCP tools for test suite (workflow test execution) results and triggering.\n *\n * Mirrors `canary test-suite run`, `list`, `get`, `diagnostics`, and `steps`.\n *\n * @module\n */\n\nimport { mcpApiRequest } from '../api-client';\nimport { toolJson } from '../tool-helpers';\n\nexport function getTestSuiteToolDefinitions() {\n return [\n {\n name: 'canary_test_suite_run',\n description:\n 'Trigger a test suite execution. Returns the suite ID and job ID. Use canary_test_suite_get to check results after triggering.',\n inputSchema: {\n type: 'object',\n properties: {\n tag: {\n type: 'string',\n description: 'Filter workflows by tag',\n },\n namePattern: {\n type: 'string',\n description: 'Filter workflows by name pattern (regex)',\n },\n propertyId: {\n type: 'string',\n description: 'Target a specific property by UUID',\n },\n environmentId: {\n type: 'string',\n description: 'Target a specific environment by UUID',\n },\n },\n },\n },\n {\n name: 'canary_test_suite_list',\n description:\n 'List recent test suite executions with pass/fail counts, ordered by most recent first.',\n inputSchema: {\n type: 'object',\n properties: {\n page: {\n type: 'number',\n description: 'Page number (default: 1)',\n },\n pageSize: {\n type: 'number',\n description: 'Page size (default: 20)',\n },\n },\n },\n },\n {\n name: 'canary_test_suite_get',\n description:\n 'Get test suite detail including all individual test runs and their results.',\n inputSchema: {\n type: 'object',\n properties: {\n executionId: {\n type: 'string',\n description: 'The test execution UUID',\n },\n },\n required: ['executionId'],\n },\n },\n {\n name: 'canary_test_suite_diagnostics',\n description:\n 'Get AI-generated failure diagnostic for a specific workflow run, including root cause analysis and recommendations.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n runId: {\n type: 'string',\n description: 'The workflow run UUID',\n },\n },\n required: ['workflowId', 'runId'],\n },\n },\n {\n name: 'canary_test_suite_steps',\n description:\n 'Get the individual steps of a workflow run with their status, duration, and error messages.',\n inputSchema: {\n type: 'object',\n properties: {\n workflowId: {\n type: 'string',\n description: 'The workflow UUID',\n },\n runId: {\n type: 'string',\n description: 'The workflow run UUID',\n },\n },\n required: ['workflowId', 'runId'],\n },\n },\n ];\n}\n\nexport async function handleTestSuiteTool(\n tool: string,\n args: Record<string, unknown>\n) {\n if (tool === 'canary_test_suite_run') {\n try {\n const params = new URLSearchParams();\n if (args.tag) params.set('tag', String(args.tag));\n if (args.namePattern) params.set('namePattern', String(args.namePattern));\n\n const queryStr = params.toString();\n const path = `/workflows/test-runs${queryStr ? `?${queryStr}` : ''}`;\n\n const body: Record<string, string> = {};\n if (args.propertyId) body.propertyId = String(args.propertyId);\n if (args.environmentId) body.environmentId = String(args.environmentId);\n\n const data = await mcpApiRequest<{\n ok: boolean;\n suiteId?: string;\n jobId?: string;\n appUrl?: string;\n error?: string;\n }>('POST', path, body);\n\n if (!data.ok) {\n return toolJson({ ok: false, error: data.error ?? 'Unknown error' });\n }\n\n return toolJson({\n ok: true,\n suiteId: data.suiteId,\n jobId: data.jobId,\n appUrl: data.appUrl,\n message: `Test suite triggered. Use canary_test_suite_get or canary_test_suite_list to check results.`,\n });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_test_suite_list') {\n try {\n const params = new URLSearchParams();\n params.set('page', String(args.page ?? 1));\n params.set('pageSize', String(args.pageSize ?? 20));\n params.set('sortBy', 'createdAt');\n params.set('sortOrder', 'desc');\n\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: unknown[];\n pagination: unknown;\n }>('GET', `/workflows/test-executions?${params}`);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, data: result.data, pagination: result.pagination });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_test_suite_get') {\n try {\n const executionId = args.executionId as string;\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n execution: unknown;\n testRuns: unknown[];\n }>('GET', `/workflows/test-executions/${executionId}`);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, execution: result.execution, testRuns: result.testRuns });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_test_suite_diagnostics') {\n try {\n const workflowId = args.workflowId as string;\n const runId = args.runId as string;\n\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: unknown;\n }>('GET', `/workflows/${workflowId}/runs/${runId}/failure-diagnostic`);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, data: result.data });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_test_suite_steps') {\n try {\n const workflowId = args.workflowId as string;\n const runId = args.runId as string;\n\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: unknown;\n }>('GET', `/workflows/${workflowId}/runs/${runId}`);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, data: result.data });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n return null;\n}\n","/**\n * MCP tools for credential management.\n *\n * Mirrors `canary credential list`.\n *\n * @module\n */\n\nimport { mcpApiRequest } from '../api-client';\nimport { toolJson } from '../tool-helpers';\n\nexport function getCredentialToolDefinitions() {\n return [\n {\n name: 'canary_credential_list',\n description:\n 'List credentials in the current organization with their property and environment info.',\n inputSchema: {\n type: 'object',\n properties: {\n property: {\n type: 'string',\n description: 'Filter by property name or ID',\n },\n },\n },\n },\n ];\n}\n\nexport async function handleCredentialTool(\n tool: string,\n args: Record<string, unknown>\n) {\n if (tool === 'canary_credential_list') {\n try {\n const result = await mcpApiRequest<{\n ok: boolean;\n items: Array<{\n id: string;\n name: string;\n propertyId: string;\n propertyName?: string;\n propertyBaseUrl?: string;\n environmentId?: string | null;\n environmentName?: string | null;\n isEphemeral?: boolean;\n }>;\n }>('GET', '/org/credentials');\n\n let items = result.items ?? [];\n\n // Filter out ephemeral credentials\n items = items.filter((c) => !c.isEphemeral);\n\n // Apply property filter if provided\n if (args.property) {\n const filter = String(args.property).toLowerCase();\n items = items.filter(\n (c) =>\n c.propertyId === args.property ||\n (c.propertyName && c.propertyName.toLowerCase().includes(filter))\n );\n }\n\n return toolJson({ ok: true, credentials: items });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n return null;\n}\n","/**\n * MCP tools for issue management.\n *\n * Mirrors `canary issues list` and `canary issues get`.\n *\n * @module\n */\n\nimport { mcpApiRequest } from '../api-client';\nimport { toolJson } from '../tool-helpers';\n\nexport function getIssueToolDefinitions() {\n return [\n {\n name: 'canary_issues_list',\n description:\n 'List and search issues in the current organization. Supports full-text search, severity/status filtering, and pagination.',\n inputSchema: {\n type: 'object',\n properties: {\n search: {\n type: 'string',\n description: 'Full-text search query',\n },\n severity: {\n type: 'string',\n enum: ['low', 'medium', 'high', 'unknown'],\n description: 'Filter by severity level',\n },\n statuses: {\n type: 'string',\n description: 'Comma-separated status filter: open, closed, not_a_bug',\n },\n propertyId: {\n type: 'string',\n description: 'Filter by property UUID',\n },\n page: {\n type: 'number',\n description: 'Page number (default: 1)',\n },\n pageSize: {\n type: 'number',\n description: 'Page size (default: 25)',\n },\n },\n },\n },\n {\n name: 'canary_issues_get',\n description:\n 'Get issue detail with diagnostics, including console errors and network failures from the latest occurrence.',\n inputSchema: {\n type: 'object',\n properties: {\n issueId: {\n type: 'string',\n description: 'The issue UUID',\n },\n },\n required: ['issueId'],\n },\n },\n ];\n}\n\nexport async function handleIssueTool(\n tool: string,\n args: Record<string, unknown>\n) {\n if (tool === 'canary_issues_list') {\n try {\n const params = new URLSearchParams();\n if (args.search) params.set('search', String(args.search));\n if (args.severity) params.set('severity', String(args.severity));\n if (args.statuses) params.set('statuses', String(args.statuses));\n if (args.propertyId) params.set('propertyId', String(args.propertyId));\n if (args.page) params.set('page', String(args.page));\n if (args.pageSize) params.set('pageSize', String(args.pageSize));\n\n const qs = params.toString();\n const path = `/v2/issues${qs ? `?${qs}` : ''}`;\n const result = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: unknown[];\n pagination: unknown;\n }>('GET', path);\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error ?? 'Unknown error' });\n }\n\n return toolJson({ ok: true, data: result.data, pagination: result.pagination });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (tool === 'canary_issues_get') {\n try {\n const issueId = args.issueId as string;\n\n const detailResult = await mcpApiRequest<{\n ok: boolean;\n error?: string;\n data: {\n issue: { id: string; latestOccurrence?: { id: string } | null };\n occurrences: unknown[];\n commentCount: number;\n };\n }>('GET', `/v2/issues/${issueId}`);\n\n if (!detailResult.ok) {\n return toolJson({ ok: false, error: detailResult.error ?? 'Unknown error' });\n }\n\n const { issue } = detailResult.data;\n const occ = issue.latestOccurrence;\n\n let consoleErrors: unknown[] = [];\n let networkErrors: unknown[] = [];\n\n if (occ) {\n try {\n const [consoleRes, networkRes] = await Promise.all([\n mcpApiRequest<{ ok: boolean; data: unknown[] }>(\n 'GET',\n `/v2/issues/${issueId}/occurrences/${occ.id}/console-logs`\n ),\n mcpApiRequest<{ ok: boolean; data: unknown[] }>(\n 'GET',\n `/v2/issues/${issueId}/occurrences/${occ.id}/network-requests`\n ),\n ]);\n\n if (consoleRes.ok && Array.isArray(consoleRes.data)) {\n consoleErrors = (consoleRes.data as Array<{ type: string }>).filter(\n (e) => e.type === 'error'\n );\n }\n\n if (networkRes.ok && Array.isArray(networkRes.data)) {\n networkErrors = (networkRes.data as Array<{ status: number | null }>).filter(\n (e) => e.status === null || e.status === 0 || (e.status !== null && e.status >= 400)\n );\n }\n } catch {\n // Diagnostics are best-effort\n }\n }\n\n return toolJson({\n ok: true,\n ...detailResult.data,\n consoleErrors,\n networkErrors,\n });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n return null;\n}\n","import type { ToolGroup } from '../types';\nimport { getWorkflowToolDefinitions, handleWorkflowTool } from './workflows';\nimport {\n getWorkflowProposalsToolDefinitions,\n handleWorkflowProposalsTool,\n} from './workflow-proposals';\nimport { getWorkflowRunsToolDefinitions, handleWorkflowRunsTool } from './workflow-runs';\nimport { getTestSuiteToolDefinitions, handleTestSuiteTool } from './test-suites';\nimport { getCredentialToolDefinitions, handleCredentialTool } from './credentials';\nimport { getIssueToolDefinitions, handleIssueTool } from './issues';\n\nexport const workflowsGroup: ToolGroup = {\n id: 'workflows',\n definitions: getWorkflowToolDefinitions,\n handle: (tool, args) => handleWorkflowTool(tool, args),\n};\n\nexport const workflowProposalsGroup: ToolGroup = {\n id: 'workflow-proposals',\n definitions: getWorkflowProposalsToolDefinitions,\n handle: (tool, args) => handleWorkflowProposalsTool(tool, args),\n};\n\nexport const workflowRunsGroup: ToolGroup = {\n id: 'workflow-runs',\n definitions: getWorkflowRunsToolDefinitions,\n handle: (tool, args) => handleWorkflowRunsTool(tool, args),\n};\n\nexport const testSuitesGroup: ToolGroup = {\n id: 'test-suites',\n definitions: getTestSuiteToolDefinitions,\n handle: (tool, args) => handleTestSuiteTool(tool, args),\n};\n\nexport const credentialsGroup: ToolGroup = {\n id: 'credentials',\n definitions: getCredentialToolDefinitions,\n handle: (tool, args) => handleCredentialTool(tool, args),\n};\n\nexport const issuesGroup: ToolGroup = {\n id: 'issues',\n definitions: getIssueToolDefinitions,\n handle: (tool, args) => handleIssueTool(tool, args),\n};\n\n/**\n * All HTTP-only groups (no daemon, no CLI auth state). Safe to use from any\n * MCP host that has an `apiToken` for the canary API.\n */\nexport const sharedGroups: ToolGroup[] = [\n workflowsGroup,\n workflowProposalsGroup,\n workflowRunsGroup,\n testSuitesGroup,\n credentialsGroup,\n issuesGroup,\n];\n","import process from 'node:process';\n\nexport const DEFAULT_API_URL = 'https://api.trycanary.ai';\n\nexport function resolveApiUrl(): string {\n return process.env.CANARY_API_URL ?? DEFAULT_API_URL;\n}\n","/**\n * MCP tool response helpers.\n *\n * @module\n */\n\nimport type { ToolResult } from '@chatsdet/browser-core';\n\nexport function toolText(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nexport function toolJson(data: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] };\n}\n\n/**\n * Convert a BrowserToolExecutor result to MCP SDK content blocks.\n */\nexport function formatMCPContent(result: ToolResult) {\n if (typeof result === 'string') {\n return { content: [{ type: 'text' as const, text: result }] };\n }\n\n // MCPContentResult with text and optional images\n const content: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> = [];\n\n if (result.text) {\n content.push({ type: 'text', text: result.text });\n }\n\n for (const img of result.images ?? []) {\n content.push({ type: 'image', data: img.data, mimeType: img.mimeType });\n }\n\n return { content };\n}\n","/**\n * Cloud-relay session tools for the MCP server.\n *\n * These tools proxy browser control through the cloud API via WebSocket.\n *\n * @module\n */\n\nimport { readStoredToken } from '../auth';\nimport { LocalBrowserHost } from '../local-browser/host';\nimport type { LocalBrowserMode } from '../local-browser/protocol';\nimport { toolJson } from './tool-helpers';\n\nimport process from 'node:process';\n\ntype LocalBrowserStartInput = {\n mode?: LocalBrowserMode;\n cdpUrl?: string;\n headless?: boolean;\n storageStatePath?: string;\n instructions?: string;\n};\n\ntype LocalBrowserStopInput = {\n sessionId: string;\n};\n\ntype LocalBrowserStatusInput = {\n sessionId: string;\n};\n\ninterface BrowserSession {\n sessionId: string;\n host: LocalBrowserHost;\n startedAt: number;\n mode: LocalBrowserMode;\n}\n\n// Global browser sessions managed by MCP\nconst browserSessions = new Map<string, BrowserSession>();\n\nconst DEFAULT_API_URL = 'https://api.trycanary.ai';\n\nfunction resolveApiUrl(input?: string): string {\n return input ?? process.env.CANARY_API_URL ?? DEFAULT_API_URL;\n}\n\nasync function resolveToken(): Promise<string> {\n const token = process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n if (!token) {\n throw new Error('Missing token. Run `canary login` first or set CANARY_API_TOKEN.');\n }\n return token;\n}\n\nexport function getSessionToolDefinitions() {\n return [\n {\n name: 'local_browser_start',\n description:\n 'Start a local browser session that connects to the cloud agent. The cloud agent can then control this browser to test local applications. Returns sessionId for tracking.',\n inputSchema: {\n type: 'object',\n properties: {\n mode: {\n type: 'string',\n enum: ['playwright', 'cdp'],\n description:\n \"Browser mode: 'playwright' for fresh browser, 'cdp' to connect to existing Chrome\",\n },\n cdpUrl: {\n type: 'string',\n description: \"CDP endpoint URL when mode is 'cdp' (e.g. http://localhost:9222)\",\n },\n headless: {\n type: 'boolean',\n description: 'Run browser headless (default: true for playwright mode)',\n },\n storageStatePath: {\n type: 'string',\n description: 'Path to Playwright storage state JSON for pre-authenticated sessions',\n },\n instructions: {\n type: 'string',\n description: 'Instructions for the cloud agent on what to test',\n },\n },\n },\n },\n {\n name: 'local_browser_status',\n description: 'Check the status of a local browser session.',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string' },\n },\n required: ['sessionId'],\n },\n },\n {\n name: 'local_browser_stop',\n description: 'Stop a local browser session and close the browser.',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string' },\n },\n required: ['sessionId'],\n },\n },\n {\n name: 'local_browser_list',\n description: 'List all active local browser sessions.',\n inputSchema: {\n type: 'object',\n properties: {},\n },\n },\n {\n name: 'local_browser_run',\n description:\n 'Start a test run on an active local browser session. The cloud agent will control the local browser according to the instructions.',\n inputSchema: {\n type: 'object',\n properties: {\n sessionId: {\n type: 'string',\n description: 'The session ID from local_browser_start',\n },\n instructions: {\n type: 'string',\n description: 'Instructions for the cloud agent on what to test',\n },\n startUrl: {\n type: 'string',\n description: 'Optional URL to navigate to before starting',\n },\n },\n required: ['sessionId', 'instructions'],\n },\n },\n ];\n}\n\nexport async function handleSessionTool(tool: string, args: Record<string, unknown>) {\n const token = await resolveToken();\n\n if (tool === 'local_browser_start') {\n const input = args as unknown as LocalBrowserStartInput;\n const apiUrl = resolveApiUrl();\n const mode = input.mode ?? 'playwright';\n\n const sessionResponse = await fetch(`${apiUrl}/local-browser/sessions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({\n browserMode: mode,\n instructions: input.instructions ?? null,\n }),\n });\n\n if (!sessionResponse.ok) {\n const text = await sessionResponse.text();\n return toolJson({ ok: false, error: `Failed to create session: ${text}` });\n }\n\n const session = (await sessionResponse.json()) as {\n ok: boolean;\n sessionId: string;\n wsToken: string;\n expiresAt: string;\n };\n\n const host = new LocalBrowserHost({\n apiUrl,\n wsToken: session.wsToken,\n sessionId: session.sessionId,\n browserMode: mode,\n cdpUrl: input.cdpUrl,\n headless: input.headless ?? true,\n storageStatePath: input.storageStatePath,\n onLog: (level, message) => {\n if (level === 'error') {\n console.error(`[LocalBrowser] ${message}`);\n }\n },\n });\n\n host.start().catch((err) => {\n console.error('Failed to start local browser:', err);\n browserSessions.delete(session.sessionId);\n });\n\n browserSessions.set(session.sessionId, {\n sessionId: session.sessionId,\n host,\n startedAt: Date.now(),\n mode,\n });\n\n return toolJson({\n ok: true,\n sessionId: session.sessionId,\n mode,\n expiresAt: session.expiresAt,\n note: 'Browser session started. The cloud agent can now control this browser. Use local_browser_stop to end the session.',\n });\n }\n\n if (tool === 'local_browser_status') {\n const input = args as unknown as LocalBrowserStatusInput;\n const session = browserSessions.get(input.sessionId);\n\n if (!session) {\n return toolJson({ ok: false, error: 'Session not found', sessionId: input.sessionId });\n }\n\n return toolJson({\n ok: true,\n sessionId: session.sessionId,\n mode: session.mode,\n startedAt: new Date(session.startedAt).toISOString(),\n uptimeMs: Date.now() - session.startedAt,\n });\n }\n\n if (tool === 'local_browser_stop') {\n const input = args as unknown as LocalBrowserStopInput;\n const session = browserSessions.get(input.sessionId);\n\n if (!session) {\n return toolJson({ ok: false, error: 'Session not found', sessionId: input.sessionId });\n }\n\n await session.host.stop();\n browserSessions.delete(input.sessionId);\n\n return toolJson({\n ok: true,\n sessionId: input.sessionId,\n note: 'Browser session stopped and browser closed.',\n });\n }\n\n if (tool === 'local_browser_list') {\n const sessions = Array.from(browserSessions.values()).map((s) => ({\n sessionId: s.sessionId,\n mode: s.mode,\n startedAt: new Date(s.startedAt).toISOString(),\n uptimeMs: Date.now() - s.startedAt,\n }));\n\n return toolJson({\n ok: true,\n count: sessions.length,\n sessions,\n });\n }\n\n if (tool === 'local_browser_run') {\n const input = args as unknown as {\n sessionId: string;\n instructions: string;\n startUrl?: string;\n };\n const apiUrl = resolveApiUrl();\n\n const session = browserSessions.get(input.sessionId);\n if (!session) {\n return toolJson({\n ok: false,\n error: 'Session not found locally. Make sure you started it with local_browser_start.',\n sessionId: input.sessionId,\n });\n }\n\n const response = await fetch(`${apiUrl}/local-browser/sessions/${input.sessionId}/run`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({\n instructions: input.instructions,\n startUrl: input.startUrl ?? null,\n }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n return toolJson({ ok: false, error: `Failed to start run: ${text}` });\n }\n\n const result = (await response.json()) as { ok: boolean; jobId: string; sessionId: string };\n\n return toolJson({\n ok: true,\n jobId: result.jobId,\n sessionId: result.sessionId,\n note: 'Test run started. The cloud agent is now controlling your local browser. You can watch the browser to see the test in action.',\n });\n }\n\n return null; // Not handled\n}\n","/**\n * Browser MCP tool registration — daemon adapter.\n *\n * Thin layer that maps MCP tool calls to the session daemon,\n * ensuring the CLI gets the SAME semantic diff, snapshot formatting,\n * click validation, auto-snapshot, and recovery notices as the cloud agent.\n *\n * The daemon owns all browser sessions. MCP is just a protocol adapter.\n *\n * @module\n */\n\nimport { getBrowserToolDefinitionsWithLifecycle } from '@chatsdet/browser-core';\nimport {\n createSession,\n deleteSession,\n deleteAllSessions,\n callTool,\n resolveTargetSession,\n} from '../session/daemon-client.js';\nimport { formatMCPContent, toolJson, toolText } from './tool-helpers';\nimport { USER_PAUSED_ERROR } from '../session/types.js';\nimport type { ToolResult } from '@chatsdet/browser-core';\n\n/** Track the active session ID within this MCP server instance */\nlet activeSessionId: string | null = null;\n\n/**\n * Get tool definitions for browser MCP tools (including lifecycle).\n */\nexport function getBrowserMCPToolDefinitions() {\n return getBrowserToolDefinitionsWithLifecycle();\n}\n\n/**\n * Handle a browser tool call.\n * Returns null if the tool name is not a browser tool.\n */\nexport async function handleBrowserTool(name: string, args: Record<string, unknown>) {\n // Lifecycle tools\n if (name === 'browser_start') {\n try {\n const result = await createSession({\n headless: (args.headless as boolean) ?? false,\n storageStatePath: args.storageStatePath as string | undefined,\n });\n\n if (!result.ok) {\n return toolJson({ ok: false, error: result.error });\n }\n\n activeSessionId = result.data!.id;\n return toolJson({\n ok: true,\n sessionId: activeSessionId,\n note: 'Browser started. You can now use browser_navigate, browser_click, and other browser tools.',\n });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n if (name === 'browser_stop') {\n try {\n if (activeSessionId) {\n await deleteSession(activeSessionId);\n activeSessionId = null;\n } else {\n // Stop all sessions if no specific one tracked\n await deleteAllSessions();\n }\n return toolJson({ ok: true, note: 'Browser stopped and cleaned up.' });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n // All other browser_* tools require an active session\n if (!name.startsWith('browser_')) return null;\n\n try {\n // Resolve target session: prefer tracked MCP session, then auto-resolve\n let sessionId = activeSessionId;\n if (!sessionId) {\n try {\n const target = await resolveTargetSession();\n sessionId = target.id;\n activeSessionId = sessionId;\n } catch {\n return toolJson({\n ok: false,\n error: 'No active browser session. Start one with browser_start first.',\n });\n }\n }\n\n // Strip 'browser_' prefix for the daemon tool name\n const toolName = name.replace(/^browser_/, '');\n const result = await callTool(sessionId, toolName, args);\n\n if (!result.ok) {\n // If session not found, clear tracking and report error\n if (result.error?.includes('not found')) {\n activeSessionId = null;\n }\n // User has taken control via the in-browser badge — return as\n // informational text rather than an error so Claude waits instead of retrying\n if (result.errorCode === USER_PAUSED_ERROR) {\n return toolText(result.error ?? 'User has taken control of the browser.');\n }\n return toolJson({ ok: false, error: result.error, tool: name });\n }\n\n // Convert daemon response back to ToolResult format for MCP formatting\n const toolResult: ToolResult = result.images?.length\n ? { text: result.text ?? '', images: result.images }\n : (result.text ?? '');\n\n return formatMCPContent(toolResult);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return toolJson({ ok: false, error: message, tool: name });\n }\n}\n","/**\n * MCP tool for device code OAuth login.\n *\n * Allows AI coding tools to trigger authentication without\n * requiring the user to run `canary login` in a separate terminal.\n *\n * @module\n */\n\nimport { ENV_URLS, envToProfile, resolveAppUrl, saveAuth } from '../auth';\nimport { openUrl, fetchOrgs, switchOrg } from '../login';\nimport type { StartResponse, PollResponse } from '../login';\nimport { resolveApiUrl } from './api-url';\nimport { toolJson } from './tool-helpers';\n\nexport function getLoginToolDefinitions() {\n return [\n {\n name: 'canary_login',\n description:\n 'Authenticate with Canary using the device code OAuth flow. Opens a browser window for the user to approve. ' +\n 'May take 30-120 seconds while waiting for the user to complete login. ' +\n 'Returns the verification URL and user code in case the browser does not open automatically.',\n inputSchema: {\n type: 'object',\n properties: {\n env: {\n type: 'string',\n enum: ['prod', 'dev', 'local'],\n description: 'Target environment (default: prod)',\n },\n org: {\n type: 'string',\n description:\n 'Organization name or ID to select if the user belongs to multiple orgs. If omitted, uses the default org.',\n },\n },\n },\n },\n ];\n}\n\nexport async function handleLoginTool(\n tool: string,\n args: Record<string, unknown>\n) {\n if (tool !== 'canary_login') return null;\n\n try {\n const env = args.env as string | undefined;\n const orgFlag = args.org as string | undefined;\n const envUrls = env ? ENV_URLS[env] : undefined;\n\n if (env && !envUrls) {\n return toolJson({ ok: false, error: `Unknown environment: ${env}. Valid: prod, dev, local` });\n }\n\n const apiUrl = envUrls?.api ?? resolveApiUrl();\n const appUrl = envUrls?.app ?? resolveAppUrl(apiUrl);\n\n // Step 1: Start device code flow\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 return toolJson({\n ok: false,\n error: `Login start failed: ${startJson.error ?? startRes.statusText}`,\n });\n }\n\n // Step 2: Open browser (non-fatal if it fails)\n if (startJson.verificationUrl) {\n try {\n openUrl(startJson.verificationUrl);\n } catch {\n // Browser open failed — user will need to open the URL manually\n }\n }\n\n // Step 3: Poll until approved/rejected/expired\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 return toolJson({\n ok: false,\n error: 'Login code expired. Please try again.',\n userCode: startJson.userCode,\n verificationUrl: startJson.verificationUrl,\n });\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 return toolJson({\n ok: false,\n error: `Login poll failed: ${pollJson.error ?? pollRes.statusText}`,\n });\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 return toolJson({ ok: false, error: 'Login rejected by user.' });\n }\n\n if (pollJson.status === 'expired') {\n return toolJson({ ok: false, error: 'Login expired. Please try again.' });\n }\n }\n\n // Step 4: Handle org selection\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 finalOrgName = orgs[0]?.name;\n } else if (orgFlag) {\n // Match by name (case-insensitive) or ID\n const selectedOrg = orgs.find(\n (o) => o.name.toLowerCase() === orgFlag.toLowerCase() || o.id === orgFlag\n );\n if (!selectedOrg) {\n // Save the default token so they're at least logged in\n const profileName = env ? envToProfile(env) : undefined;\n await saveAuth({ token: finalToken, apiUrl, orgId: finalOrgId, orgName: orgs.find((o) => o.id === initialOrgId)?.name }, profileName);\n\n return toolJson({\n ok: false,\n error: `Organization \"${orgFlag}\" not found.`,\n loggedIn: true,\n currentOrg: orgs.find((o) => o.id === initialOrgId)?.name,\n availableOrgs: orgs.map((o) => ({ id: o.id, name: o.name, role: o.role })),\n });\n }\n\n if (selectedOrg.id !== initialOrgId) {\n const switchRes = await switchOrg(apiUrl, token, selectedOrg.id);\n if (!switchRes.ok || !switchRes.accessToken) {\n return toolJson({\n ok: false,\n error: `Failed to switch organization: ${switchRes.error ?? 'Unknown error'}`,\n });\n }\n finalToken = switchRes.accessToken;\n finalOrgId = switchRes.orgId;\n finalOrgName = switchRes.orgName;\n } else {\n finalOrgName = selectedOrg.name;\n }\n } else {\n // Multiple orgs, no flag — use default\n const defaultOrg = orgs.find((o) => o.id === initialOrgId);\n finalOrgName = defaultOrg?.name;\n }\n\n // Step 5: Save auth\n const profileName = env ? envToProfile(env) : undefined;\n await saveAuth({ token: finalToken, apiUrl, orgId: finalOrgId, orgName: finalOrgName }, profileName);\n\n return toolJson({\n ok: true,\n message: 'Login successful. Token saved.',\n orgName: finalOrgName,\n orgId: finalOrgId,\n ...(orgs.length > 1\n ? { availableOrgs: orgs.map((o) => ({ id: o.id, name: o.name, role: o.role })) }\n : {}),\n });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n}\n","/**\n * MCP tool for upgrading the Canary CLI to the latest version.\n *\n * Adapts the `canary upgrade` command for MCP context where\n * stdio is the transport and process.exit() must be avoided.\n *\n * @module\n */\n\nimport fs from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport process from 'node:process';\nimport { packageName } from '../brand.js';\nimport { clearUpdateCache } from '../update-check.js';\nimport { toolJson } from './tool-helpers';\n\ntype PackageManager = 'bun' | 'pnpm' | 'npm';\n\nfunction detectPackageManager(): PackageManager {\n try {\n const binPath = fs.realpathSync(process.argv[1]).replace(/\\\\/g, '/');\n if (binPath.includes('.bun/') || binPath.includes('/bun/')) return 'bun';\n if (binPath.includes('.pnpm/') || binPath.includes('/pnpm/')) return 'pnpm';\n } catch {\n // realpathSync can fail if symlink is broken\n }\n return 'npm';\n}\n\nconst UPGRADE_COMMANDS: Record<PackageManager, string[]> = {\n bun: ['bun', 'add', '-g', `${packageName}@latest`],\n pnpm: ['pnpm', 'add', '-g', `${packageName}@latest`],\n npm: ['npm', 'install', '-g', `${packageName}@latest`],\n};\n\nexport function getUpgradeToolDefinitions() {\n return [\n {\n name: 'canary_upgrade',\n description:\n 'Upgrade the Canary CLI to the latest version. ' +\n 'Detects the package manager (npm, bun, pnpm) and runs the appropriate global install command.',\n inputSchema: {\n type: 'object',\n properties: {},\n },\n },\n ];\n}\n\nexport async function handleUpgradeTool(\n tool: string,\n _args: Record<string, unknown>\n) {\n if (tool !== 'canary_upgrade') return null;\n\n try {\n const pm = detectPackageManager();\n const cmd = UPGRADE_COMMANDS[pm];\n\n const result = spawnSync(cmd[0], cmd.slice(1), {\n stdio: ['pipe', 'pipe', 'pipe'],\n cwd: process.cwd(),\n timeout: 120_000,\n shell: process.platform === 'win32',\n windowsHide: true,\n });\n\n if (result.error) {\n return toolJson({\n ok: false,\n error: `Failed to run ${pm}: ${result.error.message}`,\n });\n }\n\n const stdout = result.stdout?.toString().trim() ?? '';\n const stderr = result.stderr?.toString().trim() ?? '';\n\n if (result.status !== 0) {\n return toolJson({\n ok: false,\n error: `Upgrade failed (exit code ${result.status}).`,\n output: stderr || stdout,\n });\n }\n\n await clearUpdateCache();\n\n return toolJson({\n ok: true,\n message: `Successfully upgraded ${packageName} to the latest version.`,\n packageManager: pm,\n output: stdout || undefined,\n });\n } catch (err) {\n return toolJson({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n}\n","/**\n * MCP server instructions for Claude Cowork.\n *\n * These instructions are returned in the InitializeResult and injected\n * into the agent's system prompt before any tools or user messages.\n * They teach Cowork how to use the canary toolset effectively.\n *\n * @module\n */\n\nexport const CANARY_MCP_INSTRUCTIONS = `\n# Canary — AI-Powered QA Platform\n\nYou have access to the Canary MCP tools for testing web applications, managing test workflows, and investigating issues. This guide teaches you how to use them effectively.\n\n## Authentication & Defaults\n\n- **Always use env: \"prod\"** when calling canary_login unless the user explicitly asks for dev or local.\n- After login, if multiple organizations are available, ask the user which one to use. Pass the org name via the \\`org\\` parameter.\n- To switch orgs later, call canary_login again with the \\`org\\` parameter — no need to re-authenticate.\n\n## Browser Sessions — Exploring Apps\n\nUse browser tools to interactively explore and test web applications. The workflow is:\n\n### Starting a Session\n\n1. **List credentials first:** Call \\`canary_credential_list\\` to see available credentials with their property names and base URLs.\n2. **Pick the right credential:**\n - Prefer the **default credential** (\\`isDefault: true\\`)\n - **Never** use a credential with \"local\" or \"manual\" in the name — these are for local development and won't work in deployed environments\n - If multiple non-local credentials exist and you're unsure, ask the user\n3. **Start the browser with a credential:** Use \\`browser_start\\` to launch a headed browser, then load the credential's storage state. Or use the session tools (\\`local_browser_start\\`) for cloud-relay sessions.\n\n### The Core Interaction Loop\n\n**snapshot → read → act → snapshot → verify**\n\nThis is the fundamental pattern. Never skip the verification snapshot.\n\n1. **Snapshot the page** with \\`browser_snapshot\\` to see what's on screen. The accessibility tree shows elements with refs like \\`[ref=e15]\\`.\n2. **Find elements efficiently** using the \\`search\\` parameter: \\`browser_snapshot\\` with \\`search: \"Submit\"\\` or \\`search: \"Email|Password\"\\` (OR-matched, case-insensitive).\n3. **Interact using refs** from the snapshot — click, type, fill, select. Always use refs, not coordinates. Fall back to coordinate-based clicking only when refs fail (overlays, canvas elements).\n4. **Snapshot again** after every action to verify it succeeded. Never assume an action worked.\n5. **Check for errors** after form submissions or navigation — look at the page content for error messages, and use console/network tools if available.\n\n### Tips\n\n- Use \\`search\\` to locate elements quickly rather than scanning the full accessibility tree.\n- Use \\`browser_snapshot\\` with \\`mode: \"screenshot\"\\` for visual verification when you need to see the actual rendering.\n- Wait for async operations (API calls, animations) before snapshotting — look for loading indicators.\n- Group related observations: take one snapshot and read the full tree rather than searching for every element individually.\n\n## Building Tests — Workflow Authoring\n\nAfter exploring an app via browser session, you can persist what you discovered as a reusable test workflow. The flow is: **explore → author → create**.\n\n### Step 1: Gather Credential Info\n\nCall \\`canary_credential_list\\` to get the credential UUID. You'll need it for the login node.\n- Always use \\`credentialId\\` (UUID) — never just the credential name\n- Set both \\`credentialId\\` and \\`credentialName\\` in the login node\n\n### Step 2: Design the Workflow\n\nA workflow is a directed graph of **nodes** connected by **edges**.\n\n#### Node Types\n\n| Type | Purpose | Required configJson |\n|------|---------|-------------------|\n| \\`login\\` | Authenticate with a credential | \\`credentialId\\`, \\`credentialName\\` |\n| \\`navigate\\` | Go to a URL | \\`pageUrl\\`, \\`pageTitle\\` |\n| \\`action\\` | Interact with elements | \\`customActionTitle\\`, \\`playbookSteps\\`, optionally \\`inlineAssertions\\` |\n| \\`assertion\\` | Standalone page verification | \\`condition\\` text |\n| \\`end\\` | Terminal node | (none) |\n\n#### Action Node Guidelines\n\n- Every action node **must** have a \\`customActionTitle\\` — a short (3-8 word) human-readable title describing the step (e.g., \"Create a new invoice\", \"Fill registration form\")\n- **Group related actions** into a single action node (one node per logical step, not per click)\n- **Prefer inline assertions** on action nodes over separate assertion nodes. Add \\`inlineAssertions\\` to verify outcomes right after the action. **Do not author an \\`id\\` field** — the server stamps a UUID on persist:\n \\`\\`\\`json\n {\n \"inlineAssertions\": [\n {\n \"condition\": \"Success message 'Invoice created' is visible\",\n \"severity\": \"fail\"\n }\n ]\n }\n \\`\\`\\`\n- Use separate \\`assertion\\` nodes only for standalone page-level verifications (e.g., initial page state before any action)\n\n#### Playbook Steps (inside action nodes)\n\nPlaybook steps describe what to do within an action node. Each step has:\n- \\`order\\`: sequence number (1, 2, 3...)\n- \\`action\\`: what to do — \\`click\\`, \\`fill\\`, \\`fill_form\\`, \\`select\\`, \\`navigate\\`, \\`wait\\`, \\`scroll\\`, \\`hover\\`, \\`upload\\`, \\`verify\\`, \\`press_key\\`, \\`drag\\`, \\`resize\\`, \\`capture\\`, \\`extract_table\\`, \\`python_execute\\`, \\`verify_download\\`, \\`verify_download_content\\`, \\`screenshot\\`, \\`agent_loop\\`\n- \\`target\\`: **semantic description** from the accessibility tree — button text, field labels, placeholder text. Describe as a human would: \"Submit button\", \"Email input field\", \"Status dropdown\". **Never use refs, CSS selectors, or XPaths.**\n- \\`params\\`: for \\`fill\\` actions, include \\`{ \"value\": \"...\" }\\`\n\nEach playbook step maps to at most one recorded browser tool call. If the user intent took multiple clicks, fills, key presses, or assertions, write multiple playbook steps instead of bundling them. Do not use \\`other\\`; if no concrete browser-tool-backed action fits, omit that diagnostic observation from \\`playbookSteps\\`. Use \\`click\\` for switches, checkboxes, and click-to-focus behavior. Use \\`press_key\\` with \\`{ \"key\": \"Escape\" }\\` or an explicit \\`click\\` to close popovers and panels; there is no \\`dismiss\\`, \\`toggle\\`, \\`focus\\`, or \\`handle_native_dialog\\` action.\n\n### Macros — Always Prefer Over Hardcoded Data\n\nMacros generate fresh values each run, preventing flaky tests from stale or colliding data. **Never hardcode emails, names, phone numbers, or dates when a macro exists.**\n\n| Macro | Output | Example |\n|-------|--------|---------|\n| \\`$EMAIL()\\` | Unique test email | \\`test-xk9mq2@example-test.com\\` |\n| \\`$FIRST_NAME()\\` | Random first name | \\`Jordan\\` |\n| \\`$LAST_NAME()\\` | Random last name | \\`Chen\\` |\n| \\`$PHONE()\\` | US phone (555 prefix) | \\`+1-555-382-7491\\` |\n| \\`$UUID()\\` | Random UUID v4 | \\`a1b2c3d4-...\\` |\n| \\`$RANDOM_INT(min, max)\\` | Random integer | \\`$RANDOM_INT(100,999)\\` → \\`427\\` |\n| \\`$RANDOM_STRING(length)\\` | Alphanumeric string | \\`$RANDOM_STRING(8)\\` → \\`xK9mQ3bR\\` |\n| \\`$TODAY()\\` | Current date (YYYY-MM-DD) | \\`2026-04-06\\` |\n| \\`$DATE(days)\\` | Date offset from today | \\`$DATE(-7)\\` → last week |\n| \\`$NOW()\\` | ISO 8601 timestamp | \\`2026-04-06T14:30:00.000Z\\` |\n| \\`$TIMESTAMP()\\` | Unix milliseconds | \\`1771520400000\\` |\n| \\`$SEQUENCE(\"name\")\\` | Auto-incrementing per run | \\`1\\`, \\`2\\`, \\`3\\`... |\n\nMacros combine inline: \\`\"QA Test $FIRST_NAME() $LAST_NAME()\"\\`, \\`\"order-$RANDOM_STRING(6)\"\\`\n\n**Prefix test data with \"QA Test\"** for identifiability.\n\n### Step 3: Create the Workflow\n\nUse \\`canary_workflow_sync\\` for existing workflows or construct the full workflow JSON and create via the API. Then verify with \\`canary_workflow_get\\`.\n\n### Example Workflow JSON\n\n\\`\\`\\`json\n{\n \"name\": \"Verify user can create an invoice\",\n \"description\": \"Tests the invoice creation flow end-to-end\",\n \"nodes\": [\n {\n \"nodeType\": \"login\",\n \"configJson\": {\n \"credentialId\": \"4f6db838-16be-49e8-8887-8d0be1c83a87\",\n \"credentialName\": \"QA User\"\n }\n },\n {\n \"nodeType\": \"navigate\",\n \"configJson\": { \"pageUrl\": \"/invoices\", \"pageTitle\": \"Invoices\" }\n },\n {\n \"nodeType\": \"action\",\n \"configJson\": {\n \"customActionTitle\": \"Create a new invoice\",\n \"actionDescription\": \"Fill out the new invoice form and save\",\n \"playbookSteps\": [\n { \"order\": 1, \"action\": \"click\", \"target\": \"New Invoice button\" },\n { \"order\": 2, \"action\": \"fill\", \"target\": \"Client name field\", \"params\": { \"value\": \"QA Test $FIRST_NAME()\" } },\n { \"order\": 3, \"action\": \"fill\", \"target\": \"Amount field\", \"params\": { \"value\": \"$RANDOM_INT(100,999)\" } },\n { \"order\": 4, \"action\": \"click\", \"target\": \"Save button\" }\n ],\n \"inlineAssertions\": [\n {\n \"condition\": \"Success message 'Invoice created' is visible and the new invoice appears in the list\",\n \"severity\": \"fail\"\n }\n ]\n }\n }\n ]\n}\n\\`\\`\\`\n\n## Managing Workflows\n\n- **canary_workflow_list** — Browse existing tests. Filter by \\`status\\` (draft = editable, published = active in regression runs), \\`flowType\\` (standard/setup/seed/teardown), or \\`search\\` by name. Reliability is tracked separately by the workflow's \\`health\\`.\n- **canary_workflow_get** — Inspect a test's full definition: nodes, edges, playbook steps, assertions.\n- **canary_workflow_patch** — Update name, description, status, or flow type. Only provided fields change.\n- **canary_workflow_run** — Trigger execution. Returns a \\`runId\\` — use it to track progress.\n- **canary_workflow_sync** — Atomically replace all nodes and edges. Automatically snapshots current state first so changes are reversible. Read the current DAG with \\`canary_workflow_get\\` before syncing.\n\n## Monitoring Runs & Debugging Failures\n\nAfter triggering a workflow run, monitor and debug with:\n\n- **canary_workflow_runs_list** — Recent runs for a workflow with status and duration.\n- **canary_workflow_runs_get** — Detailed run view: step-by-step status, duration, errors. Also works for in-progress runs to check status.\n- **canary_workflow_runs_diagnostics** — AI-generated failure analysis. Returns root cause and recommendations. Falls back to raw console logs and network requests if no AI analysis is available.\n\n### Regression Suite\n\n- **canary_test_suite_run** — Trigger a regression suite execution. Returns suiteId for tracking. Use canary_test_suite_list or canary_test_suite_get to check results.\n- **canary_test_suite_list** — Recent regression suite executions with pass/fail counts.\n- **canary_test_suite_get** — Individual test results within a suite execution.\n- **canary_test_suite_diagnostics** — Failure diagnostics for a specific test in a suite run.\n- **canary_test_suite_steps** — Step-by-step breakdown of a test run within a suite.\n\n## Investigating Issues\n\nIssues are bugs discovered by Canary during test runs.\n\n- **canary_issues_list** — Search and filter issues by severity (\\`low\\`/\\`medium\\`/\\`high\\`), status (\\`open\\`/\\`closed\\`/\\`not_a_bug\\`), or property. Supports full-text search.\n- **canary_issues_get** — Full issue detail including console errors and network failures from the latest occurrence. Use this to understand what went wrong.\n\n## API Sequences (API-only workflows)\n\nFor APIs that need deterministic, browser-free smoke testing — composed of ordered HTTP calls that chain outputs into inputs.\n\n- **Create** with the same command as any workflow, with \\`--api-only\\`:\n \\`canary workflow create --api-only --name \"<n>\" --sequence-from-file seq.json\\`\n Returns \\`{ workflowId, apiSequenceId, nodeId }\\`. No \\`--property\\` — api-sequences are org-scoped.\n- **Inspect runs** with the same command as any runs, with \\`--side api\\` (or \\`--side all\\` for both):\n \\`canary workflow runs list <wf> --side api\\`\n \\`canary workflow runs get <wf> <runId>\\`\n- **Edit the sequence itself** via \\`canary workflow api-sequence <verb>\\`:\n - \\`get <wf>\\` — dump config + steps\n - \\`update <wf> --from-file seq.json\\` — replace config\n - \\`execute <wf>\\` — run it\n - \\`generate <wf> [--prompt \"...\"]\\` — AI-draft a config, then refine with \\`update\\`\n- **Discover endpoints** before composing steps:\n \\`canary api-catalog sources list\\`\n \\`canary api-catalog endpoints list --search <q>\\`\n \\`canary api-catalog endpoints create --from-file ep.json\\` (add a one-off)\n- **Read the reference first** for the config/step shape and template syntax:\n \\`canary workflow guide api-sequences\\`\n\nTemplates in step fields use \\`{{stepId.response.path}}\\` to reference prior-step outputs and \\`$MACRO()\\` for dynamic test data (same macros as browser workflows).\n\n## Common Workflows\n\n### \"Let's explore the app\"\n1. \\`canary_login\\` (env: prod) → switch to the right org\n2. \\`canary_credential_list\\` → find the right credential\n3. \\`browser_start\\` → launch browser\n4. Load credential and navigate to the app\n5. Snapshot, interact, explore\n\n### \"Write a test for this feature\"\n1. Explore the feature via browser (above workflow)\n2. \\`canary_credential_list\\` → get credentialId\n3. Construct workflow JSON with nodes, edges, playbook steps, macros\n4. Create or sync the workflow\n5. \\`canary_workflow_run\\` → execute it\n6. \\`canary_workflow_runs_get\\` → verify it passes\n\n### \"Why did this test fail?\"\n1. \\`canary_workflow_runs_list\\` → find the failed run\n2. \\`canary_workflow_runs_get\\` → see which step failed\n3. \\`canary_workflow_runs_diagnostics\\` → AI analysis of the failure\n4. If needed, open a browser session to reproduce and investigate\n\n### \"What issues have been found?\"\n1. \\`canary_issues_list\\` → browse open issues\n2. \\`canary_issues_get\\` → deep dive with console/network errors\n`.trim();\n"],"mappings":"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","names":["process","result","toolText","toolJson","process","DEFAULT_API_URL","resolveApiUrl","toolJson","toolJson","toolText","toolJson","profileName","process","process","toolJson","process","toolText"]}