@canaryai/cli 0.1.13 → 0.1.14

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.
@@ -20,6 +20,24 @@ function getArgValue(argv, key) {
20
20
  function hasFlag(argv, ...flags) {
21
21
  return flags.some((flag) => argv.includes(flag));
22
22
  }
23
+ function toLifecycleLabel(stage) {
24
+ switch (stage) {
25
+ case "deprecated":
26
+ return "deprecated";
27
+ case "ready_for_cleanup":
28
+ return "ready_for_cleanup";
29
+ default:
30
+ return "active";
31
+ }
32
+ }
33
+ function parseLifecycleStage(argv) {
34
+ const stage = getArgValue(argv, "--stage");
35
+ if (!stage || !["active", "deprecated", "ready_for_cleanup"].includes(stage)) {
36
+ console.error("Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup");
37
+ process.exit(1);
38
+ }
39
+ return stage;
40
+ }
23
41
  async function resolveConfig(argv) {
24
42
  const storedApiUrl = await readStoredApiUrl();
25
43
  const env = getArgValue(argv, "--env");
@@ -54,8 +72,7 @@ async function apiRequest(apiUrl, token, method, path, body) {
54
72
  const json = await res.json();
55
73
  return json;
56
74
  }
57
- async function handleList(argv, apiUrl, token) {
58
- const jsonOutput = hasFlag(argv, "--json");
75
+ async function fetchFlags(apiUrl, token) {
59
76
  const res = await fetch(`${apiUrl}/superadmin/feature-flags`, {
60
77
  headers: { Authorization: `Bearer ${token}` }
61
78
  });
@@ -69,7 +86,11 @@ async function handleList(argv, apiUrl, token) {
69
86
  console.error(`Error: ${json.error}`);
70
87
  process.exit(1);
71
88
  }
72
- const flags = json.flags ?? [];
89
+ return json.flags ?? [];
90
+ }
91
+ async function handleList(argv, apiUrl, token) {
92
+ const jsonOutput = hasFlag(argv, "--json");
93
+ const flags = await fetchFlags(apiUrl, token);
73
94
  if (jsonOutput) {
74
95
  console.log(JSON.stringify(flags, null, 2));
75
96
  return;
@@ -79,9 +100,10 @@ async function handleList(argv, apiUrl, token) {
79
100
  return;
80
101
  }
81
102
  for (const flag of flags) {
82
- const gates = flag.gates ?? [];
83
- const gateStr = gates.length > 0 ? gates.map((g) => `${g.gateType}:${g.gateValue}`).join(", ") : "(no gates)";
84
- console.log(` ${flag.name} ${flag.description ?? ""} [${gateStr}]`);
103
+ const orgCount = flag.organizations?.length ?? 0;
104
+ const lifecycle = `lifecycle=${toLifecycleLabel(flag.lifecycleStage)}`;
105
+ const finalValue = flag.lifecycleStage === "active" ? "" : ` final=${flag.finalValue === true ? "true" : "false"}`;
106
+ console.log(` ${flag.name} ${flag.description ?? ""} (${lifecycle}${finalValue}) [orgs=${orgCount}]`);
85
107
  }
86
108
  }
87
109
  async function handleCreate(argv, apiUrl, token) {
@@ -171,23 +193,84 @@ async function handleDisable(argv, apiUrl, token) {
171
193
  }
172
194
  console.log(`Disabled ${name} for org ${orgId}`);
173
195
  }
196
+ async function handleLifecycle(argv, apiUrl, token) {
197
+ const name = argv[0];
198
+ if (!name || name.startsWith("--")) {
199
+ console.error("Error: Missing flag name.");
200
+ console.error(
201
+ "Usage: canary feature-flag lifecycle <name> --stage <active|deprecated|ready_for_cleanup> [--final-value true|false]"
202
+ );
203
+ process.exit(1);
204
+ }
205
+ const stage = parseLifecycleStage(argv);
206
+ const rawFinalValue = getArgValue(argv, "--final-value");
207
+ const clearFinalValue = hasFlag(argv, "--clear-final-value");
208
+ if (rawFinalValue !== void 0 && clearFinalValue) {
209
+ console.error("Error: use either --final-value or --clear-final-value, not both.");
210
+ process.exit(1);
211
+ }
212
+ let finalValue = void 0;
213
+ if (stage === "active") {
214
+ if (rawFinalValue !== void 0) {
215
+ console.error("Error: active stage does not accept --final-value.");
216
+ process.exit(1);
217
+ }
218
+ finalValue = void 0;
219
+ } else {
220
+ if (clearFinalValue) {
221
+ console.error("Error: --clear-final-value can only be used with --stage active.");
222
+ process.exit(1);
223
+ }
224
+ if (!rawFinalValue) {
225
+ console.error("Error: --final-value true|false is required for deprecated or ready_for_cleanup.");
226
+ process.exit(1);
227
+ }
228
+ if (rawFinalValue !== "true" && rawFinalValue !== "false") {
229
+ console.error("Error: --final-value must be true or false.");
230
+ process.exit(1);
231
+ }
232
+ finalValue = rawFinalValue === "true";
233
+ }
234
+ const result = await apiRequest(
235
+ apiUrl,
236
+ token,
237
+ "POST",
238
+ `/superadmin/feature-flags/${encodeURIComponent(name)}/lifecycle`,
239
+ {
240
+ stage,
241
+ finalValue
242
+ }
243
+ );
244
+ if (!result.ok || !result.flag) {
245
+ console.error(`Error: ${result.error ?? "Failed to update lifecycle"}`);
246
+ process.exit(1);
247
+ }
248
+ const final = result.flag.lifecycleStage === "active" ? "(none)" : result.flag.finalValue === true ? "true" : "false";
249
+ console.log(`Updated lifecycle for ${name}: stage=${toLifecycleLabel(result.flag.lifecycleStage)}, final=${final}`);
250
+ }
174
251
  function printFeatureFlagHelp() {
175
252
  console.log(
176
253
  [
177
254
  "Usage: canary feature-flag <sub-command> [options]",
178
255
  "",
179
256
  "Sub-commands:",
180
- " list List all feature flags",
181
- " create <name> [--description <text>] Create a new flag",
182
- " delete <name> Delete a flag and all its gates",
183
- " enable <name> --org <orgId> Enable a flag for an organization",
184
- " disable <name> --org <orgId> Disable a flag for an organization",
257
+ " list List all feature flags",
258
+ " create <name> [--description <text>] Create a new flag",
259
+ " delete <name> Delete a flag and all its gates",
260
+ " enable <name> --org <orgId> Enable a flag for an organization",
261
+ " disable <name> --org <orgId> Disable a flag for an organization",
262
+ " lifecycle <name> --stage <stage> [--final-value true|false]",
263
+ " Mark lifecycle + final value",
264
+ "",
265
+ "Stages: active, deprecated, ready_for_cleanup",
185
266
  "",
186
267
  "Options:",
187
- " --env <env> Target environment (prod, dev, local)",
188
- " --json Output as JSON (list only)",
189
- " --api-url <url> API URL override (takes precedence over --env)",
190
- " --token <key> API token override"
268
+ " --final-value <true|false> Final value for deprecated/ready_for_cleanup",
269
+ " --clear-final-value Clear final value (only valid with --stage active)",
270
+ " --env <env> Target environment (prod, dev, local)",
271
+ " --json Output as JSON (list only)",
272
+ " --api-url <url> API URL override (takes precedence over --env)",
273
+ " --token <key> API token override"
191
274
  ].join("\n")
192
275
  );
193
276
  }
@@ -214,6 +297,9 @@ async function runFeatureFlag(argv) {
214
297
  case "disable":
215
298
  await handleDisable(rest, apiUrl, token);
216
299
  break;
300
+ case "lifecycle":
301
+ await handleLifecycle(rest, apiUrl, token);
302
+ break;
217
303
  default:
218
304
  console.error(`Unknown sub-command: ${subCommand}`);
219
305
  printFeatureFlagHelp();
@@ -223,4 +309,4 @@ async function runFeatureFlag(argv) {
223
309
  export {
224
310
  runFeatureFlag
225
311
  };
226
- //# sourceMappingURL=feature-flag-MFTRYRYX.js.map
312
+ //# sourceMappingURL=feature-flag-ESPSOSKG.js.map
@@ -0,0 +1 @@
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 { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst ENV_URLS: Record<string, string> = {\n prod: \"https://api.trycanary.ai\",\n production: \"https://api.trycanary.ai\",\n dev: \"https://api.dev.trycanary.ai\",\n local: \"http://localhost:3000\",\n};\n\ntype LifecycleStage = \"active\" | \"deprecated\" | \"ready_for_cleanup\";\n\ntype FeatureFlag = {\n id: string;\n name: string;\n description: string | null;\n lifecycleStage: LifecycleStage;\n finalValue: boolean | null;\n createdAt: string;\n organizations?: Array<{\n gateId: string;\n orgId: string;\n orgName: string;\n createdAt: string;\n }>;\n};\n\ntype FeatureFlagListResponse = {\n ok: boolean;\n flags?: FeatureFlag[];\n error?: string;\n};\n\ntype ApiResponse = {\n ok: boolean;\n error?: string;\n flag?: FeatureFlag;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nfunction toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case \"deprecated\":\n return \"deprecated\";\n case \"ready_for_cleanup\":\n return \"ready_for_cleanup\";\n default:\n return \"active\";\n }\n}\n\nfunction parseLifecycleStage(argv: string[]): LifecycleStage {\n const stage = getArgValue(argv, \"--stage\");\n if (!stage || ![\"active\", \"deprecated\", \"ready_for_cleanup\"].includes(stage)) {\n console.error(\"Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup\");\n process.exit(1);\n }\n return stage as LifecycleStage;\n}\n\nasync function resolveConfig(argv: string[]) {\n const storedApiUrl = await readStoredApiUrl();\n const env = getArgValue(argv, \"--env\");\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env] : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n return { apiUrl, token };\n}\n\nasync function apiRequest(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<ApiResponse> {\n const res = await fetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as ApiResponse;\n return json;\n}\n\nasync function fetchFlags(apiUrl: string, token: string): Promise<FeatureFlag[]> {\n const res = await fetch(`${apiUrl}/superadmin/feature-flags`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as FeatureFlagListResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return json.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 lifecycle = `lifecycle=${toLifecycleLabel(flag.lifecycleStage)}`;\n const finalValue =\n flag.lifecycleStage === \"active\" ? \"\" : ` final=${flag.finalValue === true ? \"true\" : \"false\"}`;\n console.log(` ${flag.name} ${flag.description ?? \"\"} (${lifecycle}${finalValue}) [orgs=${orgCount}]`);\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(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(\n apiUrl,\n token,\n \"DELETE\",\n `/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\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag enable <name> --org <orgId>\");\n process.exit(1);\n }\n\n if (!orgId) {\n console.error(\"Error: Missing --org <orgId>.\");\n console.error(\"Usage: canary feature-flag enable <name> --org <orgId>\");\n process.exit(1);\n }\n\n const result = await apiRequest(\n apiUrl,\n token,\n \"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}\n\nasync function handleDisable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag disable <name> --org <orgId>\");\n process.exit(1);\n }\n\n if (!orgId) {\n console.error(\"Error: Missing --org <orgId>.\");\n console.error(\"Usage: canary feature-flag disable <name> --org <orgId>\");\n process.exit(1);\n }\n\n const result = await apiRequest(\n apiUrl,\n token,\n \"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}\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(\n \"Usage: canary feature-flag lifecycle <name> --stage <active|deprecated|ready_for_cleanup> [--final-value true|false]\"\n );\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(\n apiUrl,\n token,\n \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/lifecycle`,\n {\n stage,\n finalValue,\n }\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> Enable a flag for an organization\",\n \" disable <name> --org <orgId> Disable a flag for an organization\",\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 \" --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":[]}
package/dist/index.js CHANGED
@@ -1066,13 +1066,17 @@ function printHelp({ isSuperadmin }) {
1066
1066
  " delete <name> Delete a flag and its gates",
1067
1067
  " enable <name> --org <orgId> Enable for an org",
1068
1068
  " disable <name> --org <orgId> Disable for an org",
1069
+ " lifecycle <name> --stage <stage> [--final-value true|false]",
1070
+ " Set lifecycle metadata",
1069
1071
  "",
1070
1072
  "Knobs sub-commands:",
1071
1073
  " list List all knobs",
1072
1074
  " get <key> Get a knob value",
1073
1075
  " set <key> <value> --type <type> Set a knob value",
1074
1076
  " delete <key> Delete a knob",
1075
- " toggle <key> Toggle a boolean knob"
1077
+ " toggle <key> Toggle a boolean knob",
1078
+ " lifecycle <key> --stage <stage> [--final-value <value>]",
1079
+ " Set lifecycle metadata"
1076
1080
  );
1077
1081
  }
1078
1082
  lines.push(
@@ -1206,12 +1210,12 @@ async function main(argv) {
1206
1210
  return;
1207
1211
  }
1208
1212
  if (command === "feature-flag") {
1209
- const { runFeatureFlag } = await import("./feature-flag-MFTRYRYX.js");
1213
+ const { runFeatureFlag } = await import("./feature-flag-ESPSOSKG.js");
1210
1214
  await runFeatureFlag(rest);
1211
1215
  return;
1212
1216
  }
1213
1217
  if (command === "knobs") {
1214
- const { runKnobs } = await import("./knobs-T3O4Z3ZB.js");
1218
+ const { runKnobs } = await import("./knobs-HKONHY55.js");
1215
1219
  await runKnobs(rest);
1216
1220
  return;
1217
1221
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/runner/common.ts","../src/run.ts","../src/login.ts","../src/orgs.ts","../src/run-local.ts","../src/remote-test.ts","../src/debug-session.ts","../src/jwt.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport process from \"node:process\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { makeRequire, resolveRunner } from \"./runner/common\";\nimport { run as runCanary } from \"./run\";\nimport { runLocalTest } from \"./local-run\";\nimport { runTunnel } from \"./tunnel\";\nimport { runLogin } from \"./login\";\nimport { runOrgs } from \"./orgs\";\nimport { runLocalSession } from \"./run-local\";\nimport { runRemoteTest } from \"./remote-test\";\nimport { readAllStoredTokens, readStoredToken } from \"./auth\";\nimport { runDebugSession } from \"./debug-session\";\nimport { isSuperadminToken } from \"./jwt\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\n// Lazy-load playwright-dependent modules\nconst loadMcp = () => import(\"./mcp\").then((m) => m.runMcp);\nconst loadLocalBrowser = () => import(\"./local-browser/index\").then((m) => m.runLocalBrowser);\n\nexport const canary = { run: runCanary };\nexport { runCanary as run };\n\nconst baseDir =\n typeof __dirname !== \"undefined\" ? __dirname : path.dirname(fileURLToPath(import.meta.url));\nconst preloadPath = path.join(baseDir, \"runner\", \"preload.js\");\nconst requireFn = makeRequire();\n\nfunction runPlaywrightTests(args: string[]) {\n // Resolve the local @playwright/test CLI directly (drop-in replacement)\n const playwrightCli = requireFn.resolve(\"@playwright/test/cli\");\n const { runnerBin, preloadFlag } = resolveRunner(preloadPath);\n\n const nodeOptions =\n process.env.NODE_OPTIONS && preloadFlag\n ? `${process.env.NODE_OPTIONS} ${preloadFlag}`\n : preloadFlag ?? process.env.NODE_OPTIONS;\n\n const env = {\n ...process.env,\n CANARY_ENABLED: process.env.CANARY_ENABLED ?? \"1\",\n CANARY_RUNNER: \"canary\",\n ...(nodeOptions ? { NODE_OPTIONS: nodeOptions } : {}),\n };\n\n const result = spawnSync(runnerBin, [playwrightCli, \"test\", ...args], {\n env,\n stdio: \"inherit\",\n cwd: process.cwd(),\n });\n\n if (result.error) {\n console.error(\"canary failed to launch Playwright:\", result.error);\n process.exit(1);\n }\n\n process.exit(result.status ?? 1);\n}\n\nfunction printVersion() {\n console.log(`canary v${pkg.version}`);\n}\n\nfunction printHelp({ isSuperadmin }: { isSuperadmin: boolean }) {\n const lines: string[] = [\n `canary v${pkg.version}: Local and remote testing CLI`,\n \"\",\n \"Usage:\",\n \" canary test [playwright options] Run local Playwright tests\",\n \" canary test --remote [options] Run remote workflow tests\",\n \" canary local-run --tunnel-url <url> [options]\",\n \" canary tunnel --port <localPort> [options]\",\n \" canary run --port <localPort> [options]\",\n \" canary mcp\",\n \" canary browser [--mode playwright|cdp] [--cdp-url <url>] [--no-headless]\",\n \" canary login [--org <name>] [--app-url https://app.trycanary.ai] [--no-open]\",\n \" canary orgs List organizations\",\n ];\n\n lines.push(\n \" canary release <sub-command> Release QA gate (CI/CD)\",\n );\n\n if (isSuperadmin) {\n lines.push(\n \" canary debug-session [--env dev|local] [--json] Create browser debug session\",\n \" canary psql <query> [--json] Execute read-only SQL\",\n \" canary redis <command> [--json] Execute read-only Redis commands\",\n \" canary feature-flag <sub-command> Manage feature flags\",\n \" canary knobs <sub-command> Manage knobs (global config)\",\n );\n }\n\n lines.push(\n \" canary version Show version\",\n \" canary help\",\n \"\",\n \"Remote test options:\",\n \" --token <key> API key (or set CANARY_API_TOKEN)\",\n \" --api-url <url> API URL (default: https://api.trycanary.ai)\",\n \" --tag <tag> Filter workflows by tag\",\n \" --name-pattern <pat> Filter workflows by name pattern\",\n \" --verbose, -v Show all events\",\n \"\",\n \"Browser options:\",\n \" --mode <playwright|cdp> Browser mode (default: playwright)\",\n \" --cdp-url <url> CDP endpoint for existing Chrome\",\n \" --no-headless Run browser with visible UI\",\n \" --storage-state <path> Path to storage state JSON\",\n \" --instructions <text> Instructions for the cloud agent\",\n \"\",\n \"Login options:\",\n \" --org <name> Select organization by name or ID (for multi-org users)\",\n \"\",\n \"Login environments:\",\n \" Production: canary login\",\n \" Dev: canary login --app-url https://app.dev.trycanary.ai --api-url https://api.dev.trycanary.ai\",\n \" Local: canary login --app-url http://localhost:5173 --api-url http://localhost:3000\",\n \"\",\n \" Or set CANARY_API_URL env var for non-production environments:\",\n \" export CANARY_API_URL=http://localhost:3000\",\n );\n\n lines.push(\n \"\",\n \"Release sub-commands:\",\n \" trigger --property-id <uuid> Trigger a Release QA run\",\n \" status <run-id> [--json] Check run status\",\n \" run --property-id <uuid> [--timeout N] Trigger and poll until complete\",\n );\n\n if (isSuperadmin) {\n lines.push(\n \"\",\n \"PSQL options:\",\n \" --json Output results as JSON\",\n \" --query <sql> SQL query (alternative to positional)\",\n \"\",\n \"Redis options:\",\n \" --json Output results as JSON\",\n \"\",\n \"Feature flag sub-commands:\",\n \" list List all flags\",\n \" create <name> [--description <text>] Create a flag\",\n \" delete <name> Delete a flag and its gates\",\n \" enable <name> --org <orgId> Enable for an org\",\n \" disable <name> --org <orgId> Disable for an org\",\n \"\",\n \"Knobs sub-commands:\",\n \" list List all knobs\",\n \" get <key> Get a knob value\",\n \" set <key> <value> --type <type> Set a knob value\",\n \" delete <key> Delete a knob\",\n \" toggle <key> Toggle a boolean knob\",\n );\n }\n\n lines.push(\n \"\",\n \"Flags:\",\n \" -h, --help Show help\",\n \" -V, --version Show version\",\n );\n\n console.log(lines.join(\"\\n\"));\n}\n\nfunction printTestHelp() {\n console.log(\n [\n `canary v${pkg.version}: Test command`,\n \"\",\n \"Usage:\",\n \" canary test [playwright options] Run local Playwright tests\",\n \" canary test --remote [options] Run remote workflow tests\",\n \"\",\n \"Local Playwright options (passed through to Playwright):\",\n \" --grep <pattern> Only run tests matching pattern\",\n \" --headed Run in headed browser mode\",\n \" --workers <n> Number of parallel workers\",\n \" --project <name> Run specific project\",\n \" --reporter <reporter> Use a specific reporter\",\n \" --retries <n> Number of retries for failed tests\",\n \" --timeout <ms> Test timeout in milliseconds\",\n \"\",\n \"Remote test options:\",\n \" --remote Run tests remotely (required)\",\n \" --token <key> API key (or set CANARY_API_TOKEN)\",\n \" --api-url <url> API URL (default: https://api.trycanary.ai)\",\n \" --tag <tag> Filter workflows by tag\",\n \" --name-pattern <pat> Filter workflows by name pattern\",\n \" --verbose, -v Show all events\",\n \"\",\n \"Examples:\",\n \" canary test Run all local tests\",\n ' canary test --grep \"login\" Run tests matching \"login\"',\n \" canary test --headed --workers 1 Debug with visible browser\",\n \" canary test --remote --tag smoke Run remote smoke tests\",\n ].join(\"\\n\")\n );\n}\n\n/** Commands that handle --help themselves instead of showing global help. */\nconst COMMANDS_WITH_HELP = new Set([\"test\"]);\n\nasync function resolveToken(): Promise<string | null> {\n return process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n}\n\n/** Check if any stored profile (or env token) has superadmin privileges */\nasync function resolveIsSuperadmin(): Promise<boolean> {\n const envToken = process.env.CANARY_API_TOKEN;\n if (envToken) return isSuperadminToken(envToken);\n\n const tokens = await readAllStoredTokens();\n return tokens.some((t) => isSuperadminToken(t));\n}\n\nexport async function main(argv: string[]) {\n if (argv.includes(\"--version\") || argv.includes(\"-V\")) {\n printVersion();\n return;\n }\n\n const [command, ...rest] = argv;\n const hasHelpFlag = argv.includes(\"--help\") || argv.includes(\"-h\");\n\n // Global help: --help/‑h without a command that handles it, or explicit \"help\"\n if (hasHelpFlag && (!command || !COMMANDS_WITH_HELP.has(command))) {\n printHelp({ isSuperadmin: await resolveIsSuperadmin() });\n return;\n }\n\n if (!command || command === \"help\") {\n printHelp({ isSuperadmin: await resolveIsSuperadmin() });\n return;\n }\n\n if (command === \"version\") {\n printVersion();\n return;\n }\n\n if (command === \"test\") {\n // Sub-command help\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) {\n printTestHelp();\n return;\n }\n // Check for --remote flag to run remote workflow tests\n if (rest.includes(\"--remote\")) {\n const remoteArgs = rest.filter((arg) => arg !== \"--remote\");\n await runRemoteTest(remoteArgs);\n return;\n }\n runPlaywrightTests(rest);\n return;\n }\n\n if (command === \"local-run\") {\n await runLocalTest(rest);\n return;\n }\n\n if (command === \"run\") {\n await runLocalSession(rest);\n return;\n }\n\n if (command === \"mcp\") {\n const runMcp = await loadMcp();\n await runMcp(rest);\n return;\n }\n\n if (command === \"tunnel\") {\n await runTunnel(rest);\n return;\n }\n\n if (command === \"login\") {\n await runLogin(rest);\n return;\n }\n\n if (command === \"orgs\") {\n await runOrgs(rest);\n return;\n }\n\n if (command === \"browser\") {\n const runLocalBrowser = await loadLocalBrowser();\n await runLocalBrowser(rest);\n return;\n }\n\n if (command === \"debug-session\") {\n await runDebugSession(rest);\n return;\n }\n\n if (command === \"psql\") {\n const { runPsql } = await import(\"./psql.js\");\n await runPsql(rest);\n return;\n }\n\n if (command === \"redis\") {\n const { runRedis } = await import(\"./redis.js\");\n await runRedis(rest);\n return;\n }\n\n if (command === \"release\") {\n const { runRelease } = await import(\"./release.js\");\n await runRelease(rest);\n return;\n }\n\n if (command === \"feature-flag\") {\n const { runFeatureFlag } = await import(\"./feature-flag.js\");\n await runFeatureFlag(rest);\n return;\n }\n\n if (command === \"knobs\") {\n const { runKnobs } = await import(\"./knobs.js\");\n await runKnobs(rest);\n return;\n }\n\n console.log(`Unknown command \"${command}\".`);\n printHelp({ isSuperadmin: await resolveIsSuperadmin() });\n process.exit(1);\n}\n\nif (import.meta.url === pathToFileURL(process.argv[1]).href) {\n void main(process.argv.slice(2));\n}\n","import { spawnSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\n\nexport function makeRequire() {\n try {\n return createRequire(import.meta.url);\n } catch {\n try {\n return createRequire(process.cwd());\n } catch {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return typeof require !== \"undefined\" ? (require as any) : createRequire(\".\");\n }\n }\n}\n\nexport function resolveRunner(preloadPath?: string): { runnerBin: string; preloadFlag?: string } {\n const { bin, version } = pickNodeBinary();\n const supportsImport = typeof version === \"number\" && version >= 18;\n\n if (supportsImport && preloadPath && fs.existsSync(preloadPath)) {\n return { runnerBin: bin, preloadFlag: `--import=${pathToFileURL(preloadPath).href}` };\n }\n\n if (preloadPath) {\n console.warn(\"[canary] Warning: no preload module found; instrumentation may be disabled.\");\n }\n\n return { runnerBin: bin };\n}\n\nexport function pickNodeBinary(): { bin: string; version?: number } {\n const candidates = collectNodeCandidates();\n\n // Prefer the first viable candidate with version >=18; otherwise pick highest version found.\n let best: { bin: string; version?: number } | undefined;\n let fallback: { bin: string; version?: number } | undefined;\n\n for (const bin of candidates) {\n const version = getNodeMajor(bin);\n if (!version) continue;\n const current = { bin, version };\n if (version >= 18 && !fallback) {\n fallback = current;\n }\n if (!best || version > (best.version ?? 0)) {\n best = current;\n }\n }\n\n if (fallback) return fallback;\n if (best) return best;\n\n // Last resort\n return { bin: candidates[0] ?? \"node\" };\n}\n\nexport function collectNodeCandidates(): string[] {\n const seen = new Set<string>();\n const push = (value?: string) => {\n if (!value) return;\n if (seen.has(value)) return;\n seen.add(value);\n };\n\n const isBun = path.basename(process.execPath).includes(\"bun\");\n\n push(process.env.CANARY_NODE_BIN);\n push(isBun ? undefined : process.execPath);\n push(\"node\"); // default PATH lookup\n\n // Collect from `which -a node` if available.\n try {\n const which = spawnSync(\"which\", [\"-a\", \"node\"], { encoding: \"utf-8\" });\n which.stdout\n ?.toString()\n .split(\"\\n\")\n .map((line) => line.trim())\n .forEach((line) => push(line));\n } catch {\n // ignore\n }\n\n // NVM installations (grab highest versions)\n const nvmDir = process.env.NVM_DIR || (process.env.HOME ? path.join(process.env.HOME, \".nvm\") : undefined);\n if (nvmDir) {\n const versionsDir = path.join(nvmDir, \"versions\", \"node\");\n if (fs.existsSync(versionsDir)) {\n try {\n const versions = fs.readdirSync(versionsDir);\n versions\n .sort((a, b) => (a > b ? -1 : 1))\n .forEach((v) => push(path.join(versionsDir, v, \"bin\", \"node\")));\n } catch {\n // ignore\n }\n }\n }\n\n return Array.from(seen);\n}\n\nexport function getNodeMajor(bin: string): number | undefined {\n try {\n const result = spawnSync(bin, [\"-v\"], { encoding: \"utf-8\" });\n const output = (result.stdout || result.stderr || \"\").toString().trim();\n const match = output.match(/^v(\\d+)/);\n if (match) return Number(match[1]);\n } catch {\n // ignore\n }\n return undefined;\n}\n","import { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { makeRequire, resolveRunner } from \"./runner/common\";\n\nexport type HealingOptions = {\n apiKey?: string;\n provider?: string;\n model?: string;\n timeoutMs?: number;\n maxActions?: number;\n vision?: boolean;\n dryRun?: boolean;\n warnOnly?: boolean;\n debug?: boolean;\n readOnly?: boolean;\n allowEvaluate?: boolean;\n allowRunCode?: boolean;\n maxPayloadBytes?: number;\n};\n\nexport type RunRequest = {\n projectRoot?: string;\n testDir?: string | string[];\n configFile?: string;\n cliArgs?: string[];\n env?: Record<string, string>;\n healing?: HealingOptions;\n reporter?: \"default\" | \"json\" | string;\n timeoutMs?: number;\n nodeBin?: string;\n stdio?: \"inherit\" | \"pipe\";\n};\n\nexport type RunResult = {\n ok: boolean;\n exitCode: number;\n summary: {\n total: number;\n passed: number;\n failed: number;\n flaky: number;\n skipped: number;\n healed?: number;\n warned?: number;\n durationMs: number;\n };\n artifactsDir?: string;\n rawOutput?: string;\n error?: Error;\n};\n\ntype JsonReport = {\n suites?: JsonSuite[];\n duration?: number;\n};\n\ntype JsonSuite = {\n suites?: JsonSuite[];\n tests?: Array<{\n title: string;\n results: Array<{\n status: \"passed\" | \"failed\" | \"timedOut\" | \"skipped\" | \"interrupted\";\n duration: number;\n errors?: unknown[];\n }>;\n }>;\n};\n\nexport async function run(request: RunRequest = {}): Promise<RunResult> {\n const cwd = request.projectRoot ?? process.cwd();\n const stdio = request.stdio ?? \"inherit\";\n const requireFn = makeRequire();\n const playwrightCli = requireFn.resolve(\"@playwright/test/cli\");\n const baseDir = path.dirname(fileURLToPath(import.meta.url));\n const preloadPath = path.join(baseDir, \"runner\", \"preload.js\");\n const { runnerBin, preloadFlag } = resolveRunner(preloadPath);\n\n const { jsonReportPath, eventLogPath, artifactsDir } = prepareArtifactsDir(cwd);\n const reporter = buildReporterArgs(request.reporter, jsonReportPath);\n const args = buildArgs({\n testDir: request.testDir,\n configFile: request.configFile,\n cliArgs: request.cliArgs,\n reporter,\n });\n\n const nodeOptions =\n process.env.NODE_OPTIONS && preloadFlag\n ? `${process.env.NODE_OPTIONS} ${preloadFlag}`\n : preloadFlag ?? process.env.NODE_OPTIONS;\n\n const env = buildEnv({\n base: process.env,\n overrides: request.env,\n healing: request.healing,\n eventLogPath,\n nodeOptions,\n });\n\n const runResult = await spawnPlaywright({\n bin: request.nodeBin ?? runnerBin,\n args: [playwrightCli, ...args],\n cwd,\n env,\n stdio,\n timeoutMs: request.timeoutMs,\n });\n\n const summary = summarize(jsonReportPath, eventLogPath, runResult.durationMs);\n\n return {\n ok: runResult.exitCode === 0,\n exitCode: runResult.exitCode,\n summary,\n artifactsDir,\n rawOutput: runResult.output,\n error: runResult.error,\n };\n}\n\nfunction buildArgs(opts: {\n testDir?: string | string[];\n configFile?: string;\n cliArgs?: string[];\n reporter: string;\n}): string[] {\n const args = [\"test\"];\n\n if (opts.testDir) {\n const dirs = Array.isArray(opts.testDir) ? opts.testDir : [opts.testDir];\n args.push(...dirs);\n }\n\n if (opts.configFile) {\n args.push(\"--config\", opts.configFile);\n }\n\n args.push(\"--reporter\", opts.reporter);\n\n if (opts.cliArgs?.length) {\n args.push(...opts.cliArgs);\n }\n\n return args;\n}\n\nfunction buildReporterArgs(requested: RunRequest[\"reporter\"], jsonReportPath: string): string {\n if (requested === \"json\") return `json=${jsonReportPath}`;\n if (requested && requested !== \"default\") return requested;\n // Default: keep list output plus JSON for programmatic summary\n return `list,json=${jsonReportPath}`;\n}\n\nfunction prepareArtifactsDir(cwd: string): { jsonReportPath: string; eventLogPath: string; artifactsDir: string } {\n const dir = fs.mkdtempSync(path.join(os.tmpdir(), \"canary-run-\"));\n const jsonReportPath = path.join(dir, \"report.json\");\n const eventLogPath = path.join(dir, \"events-worker-0.jsonl\");\n const artifactsDir = path.join(cwd, \"test-results\", \"ai-healer\");\n return { jsonReportPath, eventLogPath, artifactsDir: dir };\n}\n\nfunction buildEnv(params: {\n base: NodeJS.ProcessEnv;\n overrides?: Record<string, string>;\n healing?: HealingOptions;\n eventLogPath: string;\n nodeOptions?: string;\n}): NodeJS.ProcessEnv {\n const healing = params.healing ?? {};\n const env: NodeJS.ProcessEnv = {\n ...params.base,\n CANARY_ENABLED: params.base.CANARY_ENABLED ?? \"1\",\n CANARY_RUNNER: \"canary\",\n CANARY_EVENT_LOG: params.eventLogPath,\n ...(params.nodeOptions ? { NODE_OPTIONS: params.nodeOptions } : {}),\n ...(healing.apiKey ? { AI_API_KEY: healing.apiKey } : {}),\n ...(healing.provider ? { AI_PROVIDER: healing.provider } : {}),\n ...(healing.model ? { AI_MODEL: healing.model } : {}),\n ...(healing.timeoutMs ? { AI_TIMEOUT_MS: String(healing.timeoutMs) } : {}),\n ...(healing.maxActions ? { CANARY_MAX_ACTIONS: String(healing.maxActions) } : {}),\n ...(healing.vision ? { CANARY_VISION: \"1\" } : {}),\n ...(healing.dryRun ? { CANARY_DRY_RUN: \"1\" } : {}),\n ...(healing.warnOnly ? { CANARY_WARN_ONLY: \"1\" } : {}),\n ...(healing.debug ? { CANARY_DEBUG: \"1\" } : {}),\n ...(healing.readOnly ? { CANARY_READ_ONLY: \"1\" } : {}),\n ...(healing.allowEvaluate === false ? { CANARY_ALLOW_EVALUATE: \"0\" } : {}),\n ...(healing.allowRunCode ? { CANARY_ALLOW_RUN_CODE: \"1\" } : {}),\n ...(healing.maxPayloadBytes ? { CANARY_MAX_PAYLOAD_BYTES: String(healing.maxPayloadBytes) } : {}),\n ...params.overrides,\n };\n return env;\n}\n\nasync function spawnPlaywright(opts: {\n bin: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n stdio: \"inherit\" | \"pipe\";\n timeoutMs?: number;\n}): Promise<{ exitCode: number; output?: string; durationMs: number; error?: Error }> {\n return new Promise((resolve) => {\n const started = Date.now();\n const child = spawn(opts.bin, opts.args, {\n cwd: opts.cwd,\n env: opts.env,\n stdio: opts.stdio === \"inherit\" ? \"inherit\" : [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let timer: NodeJS.Timeout | undefined;\n let output = \"\";\n let error: Error | undefined;\n\n if (opts.stdio === \"pipe\") {\n child.stdout?.on(\"data\", (chunk) => {\n output += chunk.toString();\n });\n child.stderr?.on(\"data\", (chunk) => {\n output += chunk.toString();\n });\n }\n\n if (opts.timeoutMs && opts.timeoutMs > 0) {\n timer = setTimeout(() => {\n error = new Error(`canary.run timed out after ${opts.timeoutMs}ms`);\n child.kill(\"SIGKILL\");\n }, opts.timeoutMs);\n }\n\n child.on(\"close\", (code) => {\n if (timer) clearTimeout(timer);\n resolve({ exitCode: code ?? 1, output: output || undefined, durationMs: Date.now() - started, error });\n });\n });\n}\n\nfunction summarize(jsonReportPath: string, eventLogPath: string, durationMs: number): RunResult[\"summary\"] {\n const base = {\n total: 0,\n passed: 0,\n failed: 0,\n flaky: 0,\n skipped: 0,\n durationMs,\n };\n\n const jsonReport = readJsonReport(jsonReportPath);\n if (jsonReport) {\n const counts = countTests(jsonReport);\n base.total = counts.total;\n base.passed = counts.passed;\n base.failed = counts.failed;\n base.flaky = counts.flaky;\n base.skipped = counts.skipped;\n base.durationMs = jsonReport.duration ?? durationMs;\n }\n\n const healed = countHealed(eventLogPath);\n if (healed) {\n return { ...base, healed };\n }\n return base;\n}\n\nfunction readJsonReport(reportPath: string): JsonReport | undefined {\n try {\n if (fs.existsSync(reportPath)) {\n const raw = fs.readFileSync(reportPath, \"utf-8\");\n return JSON.parse(raw) as JsonReport;\n }\n } catch {\n // ignore parse issues\n }\n return undefined;\n}\n\nfunction countTests(report: JsonReport): {\n total: number;\n passed: number;\n failed: number;\n flaky: number;\n skipped: number;\n} {\n let total = 0;\n let passed = 0;\n let failed = 0;\n let flaky = 0;\n let skipped = 0;\n\n const visitSuite = (suite?: JsonSuite) => {\n if (!suite) return;\n suite.tests?.forEach((test) => {\n total += 1;\n const statuses = test.results.map((r) => r.status);\n const hasFailed = statuses.includes(\"failed\") || statuses.includes(\"interrupted\");\n const hasPassed = statuses.includes(\"passed\");\n const hasTimedOut = statuses.includes(\"timedOut\");\n const allSkipped = statuses.every((s) => s === \"skipped\");\n\n if (allSkipped) {\n skipped += 1;\n } else if ((hasFailed || hasTimedOut) && hasPassed) {\n flaky += 1;\n } else if (hasFailed || hasTimedOut) {\n failed += 1;\n } else if (hasPassed && statuses.length > 1) {\n flaky += 1;\n } else if (hasPassed) {\n passed += 1;\n }\n });\n\n suite.suites?.forEach(visitSuite);\n };\n\n report.suites?.forEach(visitSuite);\n\n return { total, passed, failed, flaky, skipped };\n}\n\nfunction countHealed(eventLogPath: string): number | undefined {\n try {\n if (!fs.existsSync(eventLogPath)) return undefined;\n const raw = fs.readFileSync(eventLogPath, \"utf-8\").trim();\n if (!raw) return undefined;\n const lines = raw.split(\"\\n\");\n let healed = 0;\n for (const line of lines) {\n try {\n const event = JSON.parse(line);\n if (event?.healed === true) healed += 1;\n } catch {\n // ignore bad lines\n }\n }\n return healed;\n } catch {\n return undefined;\n }\n}\n\nexport const __internals = {\n buildArgs,\n buildReporterArgs,\n buildEnv,\n countTests,\n countHealed,\n summarize,\n};\n","import process from \"node:process\";\nimport readline from \"node:readline\";\nimport { spawn } from \"node:child_process\";\nimport { saveAuth, ENV_URLS } from \"./auth\";\n\ntype StartResponse = {\n ok: boolean;\n deviceCode?: string;\n userCode?: string;\n verificationUrl?: string;\n expiresAt?: string;\n intervalSeconds?: number;\n error?: string;\n};\n\ntype PollResponse = {\n ok: boolean;\n status?: \"pending\" | \"approved\" | \"rejected\" | \"expired\";\n accessToken?: string;\n orgId?: string;\n error?: string;\n};\n\ntype OrgsResponse = {\n ok: boolean;\n currentOrgId?: string;\n organizations?: Array<{ id: string; name: string; role: string }>;\n error?: string;\n};\n\ntype SwitchOrgResponse = {\n ok: boolean;\n accessToken?: string;\n orgId?: string;\n orgName?: string;\n role?: string;\n error?: string;\n};\n\n/** Map --env values to profile names for multi-profile auth storage */\nfunction envToProfile(env: string): string {\n if (env === \"prod\" || env === \"production\") return \"production\";\n if (env === \"dev\") return \"dev\";\n if (env === \"local\") return \"local\";\n return env;\n}\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nfunction shouldOpenBrowser(argv: string[]): boolean {\n return !argv.includes(\"--no-open\");\n}\n\nfunction openUrl(url: string) {\n const platform = process.platform;\n if (platform === \"darwin\") {\n spawn(\"open\", [url], { stdio: \"ignore\" });\n return;\n }\n if (platform === \"win32\") {\n spawn(\"cmd\", [\"/c\", \"start\", \"\", url], { stdio: \"ignore\" });\n return;\n }\n spawn(\"xdg-open\", [url], { stdio: \"ignore\" });\n}\n\nfunction promptChoice(question: string): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nasync function fetchOrgs(apiUrl: string, token: string): Promise<OrgsResponse | null> {\n try {\n const res = await fetch(`${apiUrl}/cli-login/orgs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) return null;\n return (await res.json()) as OrgsResponse;\n } catch {\n return null;\n }\n}\n\nasync function switchOrg(apiUrl: string, token: string, orgId: string): Promise<SwitchOrgResponse> {\n const res = await fetch(`${apiUrl}/cli-login/switch-org`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ orgId }),\n });\n return (await res.json()) as SwitchOrgResponse;\n}\n\nexport async function runLogin(argv: string[]) {\n const env = getArgValue(argv, \"--env\");\n const envUrls = env ? ENV_URLS[env] : undefined;\n\n if (env && !envUrls) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n envUrls?.api ??\n process.env.CANARY_API_URL ??\n \"https://api.trycanary.ai\";\n const appUrl =\n getArgValue(argv, \"--app-url\") ??\n envUrls?.app ??\n process.env.CANARY_APP_URL ??\n \"https://app.trycanary.ai\";\n\n const orgFlag = getArgValue(argv, \"--org\");\n\n const startRes = await fetch(`${apiUrl}/cli-login/start`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ appUrl }),\n });\n\n const startJson = (await startRes.json()) as StartResponse;\n if (!startRes.ok || !startJson.ok || !startJson.deviceCode || !startJson.userCode) {\n console.error(\"Login start failed\", startJson.error ?? startRes.statusText);\n process.exit(1);\n }\n\n console.log(\"Login required.\");\n console.log(`User code: ${startJson.userCode}`);\n if (startJson.verificationUrl) {\n console.log(`Open: ${startJson.verificationUrl}`);\n if (shouldOpenBrowser(argv)) {\n try {\n openUrl(startJson.verificationUrl);\n } catch {\n console.log(\"Unable to open browser automatically. Please open the URL manually.\");\n }\n }\n }\n\n const intervalMs = (startJson.intervalSeconds ?? 3) * 1000;\n const expiresAt = startJson.expiresAt ? new Date(startJson.expiresAt).getTime() : null;\n\n let token: string | undefined;\n let initialOrgId: string | undefined;\n\n while (true) {\n if (expiresAt && Date.now() > expiresAt) {\n console.error(\"Login code expired.\");\n process.exit(1);\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\n const pollRes = await fetch(`${apiUrl}/cli-login/poll`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ deviceCode: startJson.deviceCode }),\n });\n\n const pollJson = (await pollRes.json()) as PollResponse;\n if (!pollRes.ok || !pollJson.ok) {\n console.error(\"Login poll failed\", pollJson.error ?? pollRes.statusText);\n process.exit(1);\n }\n\n if (pollJson.status === \"approved\" && pollJson.accessToken) {\n token = pollJson.accessToken;\n initialOrgId = pollJson.orgId;\n break;\n }\n\n if (pollJson.status === \"rejected\") {\n console.error(\"Login rejected.\");\n process.exit(1);\n }\n\n if (pollJson.status === \"expired\") {\n console.error(\"Login expired.\");\n process.exit(1);\n }\n }\n\n // Fetch user's organizations (gracefully skip if endpoint not available)\n const orgsData = await fetchOrgs(apiUrl, token);\n const orgs = orgsData?.organizations ?? [];\n\n let finalToken = token;\n let finalOrgId = initialOrgId;\n let finalOrgName: string | undefined;\n\n if (orgs.length <= 1) {\n // Single org (or none) — use as-is\n finalOrgName = orgs[0]?.name;\n } else {\n // Multiple orgs — determine which one to use\n let selectedOrg: (typeof orgs)[number] | undefined;\n\n if (orgFlag) {\n // Match --org flag by name (case-insensitive) or ID\n selectedOrg = orgs.find(\n (o) => o.name.toLowerCase() === orgFlag.toLowerCase() || o.id === orgFlag\n );\n if (!selectedOrg) {\n console.error(`Organization \"${orgFlag}\" not found. Available orgs:`);\n for (const o of orgs) {\n console.error(` - ${o.name} (${o.id})`);\n }\n process.exit(1);\n }\n } else if (process.stdin.isTTY) {\n // Interactive prompt\n console.log(\"\\nYou belong to multiple organizations. Select one:\");\n for (let i = 0; i < orgs.length; i++) {\n const marker = orgs[i].id === initialOrgId ? \" (current)\" : \"\";\n console.log(` ${i + 1}. ${orgs[i].name}${marker}`);\n }\n\n const answer = await promptChoice(`\\nChoice [1-${orgs.length}]: `);\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= orgs.length) {\n console.error(\"Invalid selection.\");\n process.exit(1);\n }\n selectedOrg = orgs[idx];\n } else {\n // Non-interactive — use default from JWT\n const defaultOrg = orgs.find((o) => o.id === initialOrgId);\n console.log(\n `Warning: Multiple organizations available but running non-interactively. Using \"${defaultOrg?.name ?? initialOrgId}\".`\n );\n console.log('Tip: Use --org <name> to select a specific organization.');\n selectedOrg = defaultOrg;\n }\n\n if (selectedOrg && selectedOrg.id !== initialOrgId) {\n // Switch to selected org\n const switchRes = await switchOrg(apiUrl, token, selectedOrg.id);\n if (!switchRes.ok || !switchRes.accessToken) {\n console.error(\"Failed to switch organization:\", switchRes.error ?? \"Unknown error\");\n process.exit(1);\n }\n finalToken = switchRes.accessToken;\n finalOrgId = switchRes.orgId;\n finalOrgName = switchRes.orgName;\n } else if (selectedOrg) {\n finalOrgName = selectedOrg.name;\n }\n }\n\n const profileName = env ? envToProfile(env) : undefined;\n const filePath = await saveAuth({ token: finalToken, apiUrl, orgId: finalOrgId, orgName: finalOrgName }, profileName);\n const displayName = finalOrgName ? ` to ${finalOrgName}` : \"\";\n const profileLabel = profileName ? ` (profile: ${profileName})` : \"\";\n console.log(`Login successful${displayName}${profileLabel}. Token saved to ${filePath}`);\n console.log(\"Set CANARY_API_TOKEN to use the CLI without re-login.\");\n}\n","import process from \"node:process\";\nimport { readStoredAuth, readStoredToken } from \"./auth\";\n\ntype OrgsResponse = {\n ok: boolean;\n currentOrgId?: string;\n organizations?: Array<{ id: string; name: string; role: string }>;\n error?: string;\n};\n\nexport async function runOrgs(argv: string[]) {\n const token = process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n if (!token) {\n console.error(\"Not logged in. Run: canary login\");\n process.exit(1);\n }\n\n const auth = await readStoredAuth();\n const apiUrl =\n process.env.CANARY_API_URL ?? auth?.apiUrl ?? \"https://api.trycanary.ai\";\n\n const res = await fetch(`${apiUrl}/cli-login/orgs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) {\n console.error(\"Failed to fetch organizations. You may need to re-login: canary login\");\n process.exit(1);\n }\n\n const data = (await res.json()) as OrgsResponse;\n if (!data.ok || !data.organizations) {\n console.error(\"Failed to fetch organizations:\", data.error ?? \"Unknown error\");\n process.exit(1);\n }\n\n const currentOrgId = auth?.orgId ?? data.currentOrgId;\n\n console.log(\"Organizations:\\n\");\n for (const org of data.organizations) {\n const marker = org.id === currentOrgId ? \" *\" : \"\";\n console.log(` ${org.name} (${org.role})${marker}`);\n }\n\n console.log(\"\\n* = current organization\");\n console.log(\"To switch: canary login --org <name>\");\n}\n","import process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\nimport { createLocalRun } from \"./local-run\";\nimport { connectTunnel, createTunnel } from \"./tunnel\";\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nexport async function runLocalSession(argv: string[]) {\n const apiUrl = getArgValue(argv, \"--api-url\") ?? process.env.CANARY_API_URL ?? \"https://api.trycanary.ai\";\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n\n if (!token) {\n console.error(\"Missing token. Run `canary login` first or set CANARY_API_TOKEN.\");\n process.exit(1);\n }\n\n const portRaw = getArgValue(argv, \"--port\") ?? process.env.CANARY_LOCAL_PORT;\n const tunnelUrl = getArgValue(argv, \"--tunnel-url\");\n const title = getArgValue(argv, \"--title\");\n const featureSpec = getArgValue(argv, \"--feature\");\n const startUrl = getArgValue(argv, \"--start-url\");\n\n if (!tunnelUrl && !portRaw) {\n console.error(\"Missing --port or --tunnel-url\");\n process.exit(1);\n }\n\n let publicUrl = tunnelUrl;\n let ws: WebSocket | null = null;\n\n if (!publicUrl && portRaw) {\n const port = Number(portRaw);\n if (Number.isNaN(port) || port <= 0) {\n console.error(\"Invalid --port value\");\n process.exit(1);\n }\n\n const tunnel = await createTunnel({ apiUrl, token, port });\n publicUrl = tunnel.publicUrl;\n\n ws = connectTunnel({\n apiUrl,\n tunnelId: tunnel.tunnelId,\n token: tunnel.token,\n port,\n onReady: () => {\n console.log(`Tunnel connected: ${publicUrl ?? tunnel.tunnelId}`);\n },\n });\n }\n\n if (!publicUrl) {\n console.error(\"Failed to resolve tunnel URL\");\n process.exit(1);\n }\n\n const run = await createLocalRun({\n apiUrl,\n token,\n title,\n featureSpec,\n startUrl,\n tunnelUrl: publicUrl,\n });\n\n console.log(`Local test queued: ${run.runId}`);\n if (run.watchUrl) {\n console.log(`Watch: ${run.watchUrl}`);\n }\n\n if (ws) {\n console.log(\"Tunnel active. Press Ctrl+C to stop.\");\n process.on(\"SIGINT\", () => {\n ws?.close();\n process.exit(0);\n });\n await new Promise<void>(() => undefined);\n }\n}\n","/**\n * Remote Test Command\n *\n * Triggers workflow tests on the Canary platform and streams results via SSE.\n * Designed for CI/CD pipelines - exits with code 0 on success, 1 on failure.\n *\n * Usage:\n * canary test --remote [options]\n *\n * Options:\n * --token <key> API key (or set CANARY_API_TOKEN env var)\n * --api-url <url> API URL (default: https://api.trycanary.ai)\n * --tag <tag> Filter workflows by tag\n * --name-pattern <pat> Filter workflows by name pattern\n * --verbose, -v Show all events (not just results)\n */\n\nimport process from \"node:process\";\nimport { createParser, type EventSourceMessage } from \"eventsource-parser\";\nimport { readStoredToken } from \"./auth\";\n\ntype WorkflowTestStatus = \"queued\" | \"running\" | \"waiting\" | \"success\" | \"failed\";\n\ntype WorkflowTestEvent = {\n type: \"workflow-test\";\n suiteId: string;\n workflowId: string;\n testRunId: string;\n workflowRunId: string | null;\n status: WorkflowTestStatus;\n startedAt: string | null;\n finishedAt: string | null;\n durationMs: number | null;\n errorMessage?: string | null;\n linkedIssueId?: string | null;\n message?: string;\n resumeAt?: string | null;\n segmentIndex?: number | null;\n totalSegments?: number | null;\n};\n\ntype WorkflowTestSuiteEvent = {\n type: \"workflow-test-suite\";\n suiteId: string;\n status: \"running\" | \"completed\";\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n totalWorkflows: number;\n completedWorkflows: number;\n failedWorkflows: number;\n successfulWorkflows: number;\n errorMessage?: string | null;\n message?: string;\n};\n\ntype TriggerResponse = {\n ok: boolean;\n jobId?: string;\n suiteId?: string;\n startedAt?: string;\n appUrl?: string;\n error?: string;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nfunction buildQueryParams(tag?: string, namePattern?: string): string {\n const params = new URLSearchParams();\n if (tag) params.set(\"tag\", tag);\n if (namePattern) params.set(\"namePattern\", namePattern);\n return params.toString();\n}\n\nfunction extractWorkflowName(message: string | undefined, workflowId: string): string {\n if (!message) return workflowId;\n const match = message.match(/^Flow \"(.+?)\" /);\n return match ? match[1] : workflowId;\n}\n\ntype FailedTest = { name: string; testRunId: string };\n\nfunction formatFailedTests(\n failedTests: FailedTest[],\n appUrl: string | undefined,\n): string | null {\n if (failedTests.length === 0 || !appUrl) return null;\n\n const lines = [\"\", \"Failed tests:\"];\n for (const t of failedTests) {\n lines.push(` \\u2717 ${t.name}`);\n lines.push(` ${appUrl}/runs/tests/${t.testRunId}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction formatSummary(stats: {\n totalWorkflows: number;\n successfulWorkflows: number;\n failedWorkflows: number;\n completedWorkflows: number;\n}): { message: string; exitCode: number } {\n const { totalWorkflows, successfulWorkflows, failedWorkflows, completedWorkflows } = stats;\n\n if (totalWorkflows === 0) {\n return { message: \"No workflows found matching the filter criteria.\", exitCode: 0 };\n }\n\n const passRate = Math.round((successfulWorkflows / totalWorkflows) * 100);\n const waitingWorkflows = totalWorkflows - completedWorkflows;\n\n let message: string;\n let exitCode: number;\n\n if (failedWorkflows > 0) {\n message = `FAILED: ${failedWorkflows} of ${totalWorkflows} workflows failed (${passRate}% pass rate)`;\n exitCode = 1;\n } else {\n message = `PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`;\n exitCode = 0;\n }\n\n if (waitingWorkflows > 0) {\n message += `\\nNote: ${waitingWorkflows} workflow(s) are still waiting (scheduled for later)`;\n }\n\n return { message, exitCode };\n}\n\n// Export internals for testing\nexport const __internals = {\n getArgValue,\n hasFlag,\n buildQueryParams,\n extractWorkflowName,\n formatSummary,\n formatFailedTests,\n};\n\nexport async function runRemoteTest(argv: string[]): Promise<void> {\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n process.env.CANARY_API_URL ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n\n const tag = getArgValue(argv, \"--tag\");\n const namePattern = getArgValue(argv, \"--name-pattern\");\n const verbose = hasFlag(argv, \"--verbose\", \"-v\");\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"\");\n console.error(\"Set CANARY_API_TOKEN environment variable or run:\");\n console.error(\" canary login\");\n console.error(\"\");\n console.error(\"Or create an API key in Settings > API Keys and pass it:\");\n console.error(\" canary test --remote --token cnry_...\");\n process.exit(1);\n }\n\n console.log(\"Starting remote workflow tests...\");\n if (tag) console.log(` Filtering by tag: ${tag}`);\n if (namePattern) console.log(` Filtering by name pattern: ${namePattern}`);\n console.log(\"\");\n\n // 1. Trigger test run\n const queryParams = new URLSearchParams();\n if (tag) queryParams.set(\"tag\", tag);\n if (namePattern) queryParams.set(\"namePattern\", namePattern);\n\n const triggerUrl = `${apiUrl}/workflows/test-runs${queryParams.toString() ? `?${queryParams}` : \"\"}`;\n\n let triggerRes: Response;\n try {\n triggerRes = await fetch(triggerUrl, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n } catch (err) {\n console.error(`Failed to connect to API: ${err}`);\n process.exit(1);\n }\n\n if (!triggerRes.ok) {\n const errorText = await triggerRes.text();\n console.error(`Failed to start tests: ${triggerRes.status}`);\n console.error(errorText);\n process.exit(1);\n }\n\n const triggerData = (await triggerRes.json()) as TriggerResponse;\n if (!triggerData.ok || !triggerData.suiteId) {\n console.error(`Failed to start tests: ${triggerData.error ?? \"Unknown error\"}`);\n process.exit(1);\n }\n\n const { suiteId, jobId, appUrl } = triggerData;\n if (verbose) {\n console.log(`Suite ID: ${suiteId}`);\n console.log(`Job ID: ${jobId}`);\n console.log(\"\");\n }\n\n // 2. Subscribe to SSE stream\n const streamUrl = `${apiUrl}/workflows/test-runs/stream?suiteId=${suiteId}`;\n\n let streamRes: Response;\n try {\n streamRes = await fetch(streamUrl, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"text/event-stream\",\n },\n });\n } catch (err) {\n console.error(`Failed to connect to event stream: ${err}`);\n process.exit(1);\n }\n\n if (!streamRes.ok || !streamRes.body) {\n console.error(`Failed to connect to event stream: ${streamRes.status}`);\n process.exit(1);\n }\n\n // Track state\n let exitCode = 0;\n let hasCompleted = false;\n const workflowNames = new Map<string, string>();\n const failedTests: FailedTest[] = [];\n let totalWorkflows = 0;\n let completedWorkflows = 0;\n let failedWorkflows = 0;\n let successfulWorkflows = 0;\n\n // 3. Parse SSE events\n const parser = createParser({\n onEvent: (event: EventSourceMessage) => {\n if (!event.data) return;\n\n try {\n const data = JSON.parse(event.data) as\n | WorkflowTestEvent\n | WorkflowTestSuiteEvent\n | { error?: string };\n\n if (verbose) {\n console.log(`[${event.event}] ${JSON.stringify(data)}`);\n }\n\n if (event.event === \"workflow-test\") {\n const testEvent = data as WorkflowTestEvent;\n const { status, workflowId, message, errorMessage } = testEvent;\n const name = workflowNames.get(workflowId) || message?.replace(/^Flow \"(.+)\" .*$/, \"$1\") || workflowId;\n\n if (message?.startsWith('Flow \"')) {\n const match = message.match(/^Flow \"(.+?)\" /);\n if (match) workflowNames.set(workflowId, match[1]);\n }\n\n if (!verbose) {\n if (status === \"success\") {\n console.log(` \\u2713 ${name}`);\n } else if (status === \"failed\") {\n console.log(` \\u2717 ${name}`);\n if (errorMessage) {\n console.log(` Error: ${errorMessage.slice(0, 200)}`);\n }\n failedTests.push({ name, testRunId: testEvent.testRunId });\n exitCode = 1;\n } else if (status === \"running\") {\n // Don't log running status in non-verbose mode\n } else if (status === \"waiting\") {\n console.log(` \\u23F3 ${name} (waiting for scheduled time)`);\n }\n }\n }\n\n if (event.event === \"workflow-test-suite\") {\n const suiteEvent = data as WorkflowTestSuiteEvent;\n totalWorkflows = suiteEvent.totalWorkflows;\n completedWorkflows = suiteEvent.completedWorkflows;\n failedWorkflows = suiteEvent.failedWorkflows;\n successfulWorkflows = suiteEvent.successfulWorkflows;\n\n if (suiteEvent.status === \"completed\") {\n hasCompleted = true;\n }\n }\n\n if (event.event === \"error\") {\n const errorData = data as { error?: string };\n console.error(`Stream error: ${errorData.error ?? \"Unknown error\"}`);\n exitCode = 1;\n }\n } catch {\n // Ignore parse errors for keepalive, etc.\n }\n },\n });\n\n const reader = streamRes.body.getReader();\n const decoder = new TextDecoder();\n\n try {\n while (!hasCompleted) {\n const { done, value } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } finally {\n reader.releaseLock();\n }\n\n // 4. Print summary\n console.log(\"\");\n console.log(\"─\".repeat(50));\n\n if (totalWorkflows === 0) {\n console.log(\"No workflows found matching the filter criteria.\");\n process.exit(0);\n }\n\n const passRate = totalWorkflows > 0 ? Math.round((successfulWorkflows / totalWorkflows) * 100) : 0;\n\n if (failedWorkflows > 0) {\n console.log(`FAILED: ${failedWorkflows} of ${totalWorkflows} workflows failed (${passRate}% pass rate)`);\n exitCode = 1;\n } else {\n console.log(`PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`);\n }\n\n const waitingWorkflows = totalWorkflows - completedWorkflows;\n if (waitingWorkflows > 0) {\n console.log(`Note: ${waitingWorkflows} workflow(s) are still waiting (scheduled for later)`);\n }\n\n const failedSection = formatFailedTests(failedTests, appUrl);\n if (failedSection) {\n console.log(failedSection);\n }\n\n process.exit(exitCode);\n}\n","/**\n * CLI Debug Session Generator\n *\n * Creates a debug login token for browser-based debugging with Playwright.\n * Requires superadmin authentication.\n *\n * Usage:\n * canary debug-session [--env dev|prod|local] [--api-url <url>]\n *\n * Outputs the login URL that can be used with Playwright to authenticate\n * as the debug agent user for read-only browser access.\n */\n\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\ntype DebugSessionResponse = {\n ok: boolean;\n loginUrl?: string;\n expiresAt?: string;\n error?: string;\n message?: string;\n};\n\nconst ENV_URLS: Record<string, { api: string; app: string }> = {\n prod: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n production: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n dev: {\n api: \"https://api.dev.trycanary.ai\",\n app: \"https://app.dev.trycanary.ai\",\n },\n local: {\n api: \"http://localhost:3000\",\n app: \"http://localhost:5173\",\n },\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nasync function writeDebugSession(loginUrl: string, expiresAt: string, apiUrl: string) {\n const dir = path.join(os.homedir(), \".config\", \"canary-cli\");\n const filePath = path.join(dir, \"debug-session.json\");\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n await fs.writeFile(\n filePath,\n JSON.stringify({ loginUrl, expiresAt, apiUrl, createdAt: new Date().toISOString() }, null, 2),\n { encoding: \"utf8\", mode: 0o600 }\n );\n return filePath;\n}\n\nexport async function runDebugSession(argv: string[]): Promise<void> {\n // Check for --env flag\n const env = getArgValue(argv, \"--env\");\n const envUrls = env ? ENV_URLS[env] : undefined;\n\n if (env && !envUrls) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const storedApiUrl = await readStoredApiUrl();\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n envUrls?.api ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n const jsonOutput = hasFlag(argv, \"--json\");\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login [--env <env>]\");\n process.exit(1);\n }\n\n try {\n const res = await fetch(`${apiUrl}/auth/debug-session/create`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n // Handle HTTP errors before parsing JSON\n if (!res.ok) {\n const text = await res.text();\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n if (res.status === 403) {\n console.error(\"Error: Forbidden. Debug session creation requires superadmin access.\");\n process.exit(1);\n }\n\n if (res.status === 404) {\n console.error(\n \"Error: Endpoint not found. The debug-session feature may not be deployed to this environment.\"\n );\n process.exit(1);\n }\n\n // Try to parse error as JSON, fallback to raw text\n try {\n const errorJson = JSON.parse(text) as { error?: string; message?: string };\n console.error(`Error: ${errorJson.message ?? errorJson.error ?? text}`);\n } catch {\n console.error(`Error (${res.status}): ${text || res.statusText}`);\n }\n process.exit(1);\n }\n\n const json = (await res.json()) as DebugSessionResponse;\n\n if (!json.ok || !json.loginUrl) {\n console.error(`Error: ${json.message ?? json.error ?? \"Failed to create debug session\"}`);\n process.exit(1);\n }\n\n // Save session info\n const filePath = await writeDebugSession(json.loginUrl, json.expiresAt ?? \"\", apiUrl);\n\n if (jsonOutput) {\n console.log(\n JSON.stringify(\n {\n loginUrl: json.loginUrl,\n expiresAt: json.expiresAt,\n sessionFile: filePath,\n },\n null,\n 2\n )\n );\n } else {\n console.log(\"Debug session created successfully.\");\n console.log(\"\");\n console.log(`Login URL: ${json.loginUrl}`);\n console.log(`Expires: ${json.expiresAt}`);\n console.log(`Session saved to: ${filePath}`);\n console.log(\"\");\n console.log(\"Use this URL with Playwright to authenticate as the debug agent.\");\n console.log(\"The token is single-use and expires in 5 minutes.\");\n }\n } catch (err) {\n console.error(`Failed to create debug session: ${err}`);\n process.exit(1);\n }\n}\n","/**\n * JWT payload decoder for CLI superadmin detection.\n *\n * Decodes the payload section of a JWT locally (no signature verification,\n * no network calls). Used to conditionally show superadmin CLI commands.\n *\n * @module\n */\n\n/**\n * Decode the payload (second segment) of a JWT without verifying the signature.\n * Returns the parsed JSON object, or `null` if the token is malformed.\n */\nexport function decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n\n const base64 = parts[1]!.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const json = Buffer.from(base64, \"base64\").toString(\"utf8\");\n const payload: unknown = JSON.parse(json);\n\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n return null;\n }\n return payload as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Returns `true` when the JWT's `is_superadmin` claim is exactly `true`.\n * Returns `false` for any invalid, missing, or non-superadmin token.\n */\nexport function isSuperadminToken(token: string | null | undefined): boolean {\n if (!token) return false;\n const payload = decodeJwtPayload(token);\n return payload?.is_superadmin === true;\n}\n"],"mappings":"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","names":["spawnSync","createRequire","process","path","fileURLToPath","pathToFileURL","preloadPath","fs","path","requireFn","baseDir","path","preloadPath","fs","process","spawn","process","spawn","process","process","process","getArgValue","process","run","process","getArgValue","getArgValue","process","fs","os","path","process","ENV_URLS","getArgValue","hasFlag","path","os","fs","process","require","createRequire","path","fileURLToPath","process","spawnSync","process","pathToFileURL"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/runner/common.ts","../src/run.ts","../src/login.ts","../src/orgs.ts","../src/run-local.ts","../src/remote-test.ts","../src/debug-session.ts","../src/jwt.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport process from \"node:process\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { makeRequire, resolveRunner } from \"./runner/common\";\nimport { run as runCanary } from \"./run\";\nimport { runLocalTest } from \"./local-run\";\nimport { runTunnel } from \"./tunnel\";\nimport { runLogin } from \"./login\";\nimport { runOrgs } from \"./orgs\";\nimport { runLocalSession } from \"./run-local\";\nimport { runRemoteTest } from \"./remote-test\";\nimport { readAllStoredTokens, readStoredToken } from \"./auth\";\nimport { runDebugSession } from \"./debug-session\";\nimport { isSuperadminToken } from \"./jwt\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\n// Lazy-load playwright-dependent modules\nconst loadMcp = () => import(\"./mcp\").then((m) => m.runMcp);\nconst loadLocalBrowser = () => import(\"./local-browser/index\").then((m) => m.runLocalBrowser);\n\nexport const canary = { run: runCanary };\nexport { runCanary as run };\n\nconst baseDir =\n typeof __dirname !== \"undefined\" ? __dirname : path.dirname(fileURLToPath(import.meta.url));\nconst preloadPath = path.join(baseDir, \"runner\", \"preload.js\");\nconst requireFn = makeRequire();\n\nfunction runPlaywrightTests(args: string[]) {\n // Resolve the local @playwright/test CLI directly (drop-in replacement)\n const playwrightCli = requireFn.resolve(\"@playwright/test/cli\");\n const { runnerBin, preloadFlag } = resolveRunner(preloadPath);\n\n const nodeOptions =\n process.env.NODE_OPTIONS && preloadFlag\n ? `${process.env.NODE_OPTIONS} ${preloadFlag}`\n : preloadFlag ?? process.env.NODE_OPTIONS;\n\n const env = {\n ...process.env,\n CANARY_ENABLED: process.env.CANARY_ENABLED ?? \"1\",\n CANARY_RUNNER: \"canary\",\n ...(nodeOptions ? { NODE_OPTIONS: nodeOptions } : {}),\n };\n\n const result = spawnSync(runnerBin, [playwrightCli, \"test\", ...args], {\n env,\n stdio: \"inherit\",\n cwd: process.cwd(),\n });\n\n if (result.error) {\n console.error(\"canary failed to launch Playwright:\", result.error);\n process.exit(1);\n }\n\n process.exit(result.status ?? 1);\n}\n\nfunction printVersion() {\n console.log(`canary v${pkg.version}`);\n}\n\nfunction printHelp({ isSuperadmin }: { isSuperadmin: boolean }) {\n const lines: string[] = [\n `canary v${pkg.version}: Local and remote testing CLI`,\n \"\",\n \"Usage:\",\n \" canary test [playwright options] Run local Playwright tests\",\n \" canary test --remote [options] Run remote workflow tests\",\n \" canary local-run --tunnel-url <url> [options]\",\n \" canary tunnel --port <localPort> [options]\",\n \" canary run --port <localPort> [options]\",\n \" canary mcp\",\n \" canary browser [--mode playwright|cdp] [--cdp-url <url>] [--no-headless]\",\n \" canary login [--org <name>] [--app-url https://app.trycanary.ai] [--no-open]\",\n \" canary orgs List organizations\",\n ];\n\n lines.push(\n \" canary release <sub-command> Release QA gate (CI/CD)\",\n );\n\n if (isSuperadmin) {\n lines.push(\n \" canary debug-session [--env dev|local] [--json] Create browser debug session\",\n \" canary psql <query> [--json] Execute read-only SQL\",\n \" canary redis <command> [--json] Execute read-only Redis commands\",\n \" canary feature-flag <sub-command> Manage feature flags\",\n \" canary knobs <sub-command> Manage knobs (global config)\",\n );\n }\n\n lines.push(\n \" canary version Show version\",\n \" canary help\",\n \"\",\n \"Remote test options:\",\n \" --token <key> API key (or set CANARY_API_TOKEN)\",\n \" --api-url <url> API URL (default: https://api.trycanary.ai)\",\n \" --tag <tag> Filter workflows by tag\",\n \" --name-pattern <pat> Filter workflows by name pattern\",\n \" --verbose, -v Show all events\",\n \"\",\n \"Browser options:\",\n \" --mode <playwright|cdp> Browser mode (default: playwright)\",\n \" --cdp-url <url> CDP endpoint for existing Chrome\",\n \" --no-headless Run browser with visible UI\",\n \" --storage-state <path> Path to storage state JSON\",\n \" --instructions <text> Instructions for the cloud agent\",\n \"\",\n \"Login options:\",\n \" --org <name> Select organization by name or ID (for multi-org users)\",\n \"\",\n \"Login environments:\",\n \" Production: canary login\",\n \" Dev: canary login --app-url https://app.dev.trycanary.ai --api-url https://api.dev.trycanary.ai\",\n \" Local: canary login --app-url http://localhost:5173 --api-url http://localhost:3000\",\n \"\",\n \" Or set CANARY_API_URL env var for non-production environments:\",\n \" export CANARY_API_URL=http://localhost:3000\",\n );\n\n lines.push(\n \"\",\n \"Release sub-commands:\",\n \" trigger --property-id <uuid> Trigger a Release QA run\",\n \" status <run-id> [--json] Check run status\",\n \" run --property-id <uuid> [--timeout N] Trigger and poll until complete\",\n );\n\n if (isSuperadmin) {\n lines.push(\n \"\",\n \"PSQL options:\",\n \" --json Output results as JSON\",\n \" --query <sql> SQL query (alternative to positional)\",\n \"\",\n \"Redis options:\",\n \" --json Output results as JSON\",\n \"\",\n \"Feature flag sub-commands:\",\n \" list List all flags\",\n \" create <name> [--description <text>] Create a flag\",\n \" delete <name> Delete a flag and its gates\",\n \" enable <name> --org <orgId> Enable for an org\",\n \" disable <name> --org <orgId> Disable for an org\",\n \" lifecycle <name> --stage <stage> [--final-value true|false]\",\n \" Set lifecycle metadata\",\n \"\",\n \"Knobs sub-commands:\",\n \" list List all knobs\",\n \" get <key> Get a knob value\",\n \" set <key> <value> --type <type> 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 \" Set lifecycle metadata\",\n );\n }\n\n lines.push(\n \"\",\n \"Flags:\",\n \" -h, --help Show help\",\n \" -V, --version Show version\",\n );\n\n console.log(lines.join(\"\\n\"));\n}\n\nfunction printTestHelp() {\n console.log(\n [\n `canary v${pkg.version}: Test command`,\n \"\",\n \"Usage:\",\n \" canary test [playwright options] Run local Playwright tests\",\n \" canary test --remote [options] Run remote workflow tests\",\n \"\",\n \"Local Playwright options (passed through to Playwright):\",\n \" --grep <pattern> Only run tests matching pattern\",\n \" --headed Run in headed browser mode\",\n \" --workers <n> Number of parallel workers\",\n \" --project <name> Run specific project\",\n \" --reporter <reporter> Use a specific reporter\",\n \" --retries <n> Number of retries for failed tests\",\n \" --timeout <ms> Test timeout in milliseconds\",\n \"\",\n \"Remote test options:\",\n \" --remote Run tests remotely (required)\",\n \" --token <key> API key (or set CANARY_API_TOKEN)\",\n \" --api-url <url> API URL (default: https://api.trycanary.ai)\",\n \" --tag <tag> Filter workflows by tag\",\n \" --name-pattern <pat> Filter workflows by name pattern\",\n \" --verbose, -v Show all events\",\n \"\",\n \"Examples:\",\n \" canary test Run all local tests\",\n ' canary test --grep \"login\" Run tests matching \"login\"',\n \" canary test --headed --workers 1 Debug with visible browser\",\n \" canary test --remote --tag smoke Run remote smoke tests\",\n ].join(\"\\n\")\n );\n}\n\n/** Commands that handle --help themselves instead of showing global help. */\nconst COMMANDS_WITH_HELP = new Set([\"test\"]);\n\nasync function resolveToken(): Promise<string | null> {\n return process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n}\n\n/** Check if any stored profile (or env token) has superadmin privileges */\nasync function resolveIsSuperadmin(): Promise<boolean> {\n const envToken = process.env.CANARY_API_TOKEN;\n if (envToken) return isSuperadminToken(envToken);\n\n const tokens = await readAllStoredTokens();\n return tokens.some((t) => isSuperadminToken(t));\n}\n\nexport async function main(argv: string[]) {\n if (argv.includes(\"--version\") || argv.includes(\"-V\")) {\n printVersion();\n return;\n }\n\n const [command, ...rest] = argv;\n const hasHelpFlag = argv.includes(\"--help\") || argv.includes(\"-h\");\n\n // Global help: --help/‑h without a command that handles it, or explicit \"help\"\n if (hasHelpFlag && (!command || !COMMANDS_WITH_HELP.has(command))) {\n printHelp({ isSuperadmin: await resolveIsSuperadmin() });\n return;\n }\n\n if (!command || command === \"help\") {\n printHelp({ isSuperadmin: await resolveIsSuperadmin() });\n return;\n }\n\n if (command === \"version\") {\n printVersion();\n return;\n }\n\n if (command === \"test\") {\n // Sub-command help\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) {\n printTestHelp();\n return;\n }\n // Check for --remote flag to run remote workflow tests\n if (rest.includes(\"--remote\")) {\n const remoteArgs = rest.filter((arg) => arg !== \"--remote\");\n await runRemoteTest(remoteArgs);\n return;\n }\n runPlaywrightTests(rest);\n return;\n }\n\n if (command === \"local-run\") {\n await runLocalTest(rest);\n return;\n }\n\n if (command === \"run\") {\n await runLocalSession(rest);\n return;\n }\n\n if (command === \"mcp\") {\n const runMcp = await loadMcp();\n await runMcp(rest);\n return;\n }\n\n if (command === \"tunnel\") {\n await runTunnel(rest);\n return;\n }\n\n if (command === \"login\") {\n await runLogin(rest);\n return;\n }\n\n if (command === \"orgs\") {\n await runOrgs(rest);\n return;\n }\n\n if (command === \"browser\") {\n const runLocalBrowser = await loadLocalBrowser();\n await runLocalBrowser(rest);\n return;\n }\n\n if (command === \"debug-session\") {\n await runDebugSession(rest);\n return;\n }\n\n if (command === \"psql\") {\n const { runPsql } = await import(\"./psql.js\");\n await runPsql(rest);\n return;\n }\n\n if (command === \"redis\") {\n const { runRedis } = await import(\"./redis.js\");\n await runRedis(rest);\n return;\n }\n\n if (command === \"release\") {\n const { runRelease } = await import(\"./release.js\");\n await runRelease(rest);\n return;\n }\n\n if (command === \"feature-flag\") {\n const { runFeatureFlag } = await import(\"./feature-flag.js\");\n await runFeatureFlag(rest);\n return;\n }\n\n if (command === \"knobs\") {\n const { runKnobs } = await import(\"./knobs.js\");\n await runKnobs(rest);\n return;\n }\n\n console.log(`Unknown command \"${command}\".`);\n printHelp({ isSuperadmin: await resolveIsSuperadmin() });\n process.exit(1);\n}\n\nif (import.meta.url === pathToFileURL(process.argv[1]).href) {\n void main(process.argv.slice(2));\n}\n","import { spawnSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\n\nexport function makeRequire() {\n try {\n return createRequire(import.meta.url);\n } catch {\n try {\n return createRequire(process.cwd());\n } catch {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return typeof require !== \"undefined\" ? (require as any) : createRequire(\".\");\n }\n }\n}\n\nexport function resolveRunner(preloadPath?: string): { runnerBin: string; preloadFlag?: string } {\n const { bin, version } = pickNodeBinary();\n const supportsImport = typeof version === \"number\" && version >= 18;\n\n if (supportsImport && preloadPath && fs.existsSync(preloadPath)) {\n return { runnerBin: bin, preloadFlag: `--import=${pathToFileURL(preloadPath).href}` };\n }\n\n if (preloadPath) {\n console.warn(\"[canary] Warning: no preload module found; instrumentation may be disabled.\");\n }\n\n return { runnerBin: bin };\n}\n\nexport function pickNodeBinary(): { bin: string; version?: number } {\n const candidates = collectNodeCandidates();\n\n // Prefer the first viable candidate with version >=18; otherwise pick highest version found.\n let best: { bin: string; version?: number } | undefined;\n let fallback: { bin: string; version?: number } | undefined;\n\n for (const bin of candidates) {\n const version = getNodeMajor(bin);\n if (!version) continue;\n const current = { bin, version };\n if (version >= 18 && !fallback) {\n fallback = current;\n }\n if (!best || version > (best.version ?? 0)) {\n best = current;\n }\n }\n\n if (fallback) return fallback;\n if (best) return best;\n\n // Last resort\n return { bin: candidates[0] ?? \"node\" };\n}\n\nexport function collectNodeCandidates(): string[] {\n const seen = new Set<string>();\n const push = (value?: string) => {\n if (!value) return;\n if (seen.has(value)) return;\n seen.add(value);\n };\n\n const isBun = path.basename(process.execPath).includes(\"bun\");\n\n push(process.env.CANARY_NODE_BIN);\n push(isBun ? undefined : process.execPath);\n push(\"node\"); // default PATH lookup\n\n // Collect from `which -a node` if available.\n try {\n const which = spawnSync(\"which\", [\"-a\", \"node\"], { encoding: \"utf-8\" });\n which.stdout\n ?.toString()\n .split(\"\\n\")\n .map((line) => line.trim())\n .forEach((line) => push(line));\n } catch {\n // ignore\n }\n\n // NVM installations (grab highest versions)\n const nvmDir = process.env.NVM_DIR || (process.env.HOME ? path.join(process.env.HOME, \".nvm\") : undefined);\n if (nvmDir) {\n const versionsDir = path.join(nvmDir, \"versions\", \"node\");\n if (fs.existsSync(versionsDir)) {\n try {\n const versions = fs.readdirSync(versionsDir);\n versions\n .sort((a, b) => (a > b ? -1 : 1))\n .forEach((v) => push(path.join(versionsDir, v, \"bin\", \"node\")));\n } catch {\n // ignore\n }\n }\n }\n\n return Array.from(seen);\n}\n\nexport function getNodeMajor(bin: string): number | undefined {\n try {\n const result = spawnSync(bin, [\"-v\"], { encoding: \"utf-8\" });\n const output = (result.stdout || result.stderr || \"\").toString().trim();\n const match = output.match(/^v(\\d+)/);\n if (match) return Number(match[1]);\n } catch {\n // ignore\n }\n return undefined;\n}\n","import { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { makeRequire, resolveRunner } from \"./runner/common\";\n\nexport type HealingOptions = {\n apiKey?: string;\n provider?: string;\n model?: string;\n timeoutMs?: number;\n maxActions?: number;\n vision?: boolean;\n dryRun?: boolean;\n warnOnly?: boolean;\n debug?: boolean;\n readOnly?: boolean;\n allowEvaluate?: boolean;\n allowRunCode?: boolean;\n maxPayloadBytes?: number;\n};\n\nexport type RunRequest = {\n projectRoot?: string;\n testDir?: string | string[];\n configFile?: string;\n cliArgs?: string[];\n env?: Record<string, string>;\n healing?: HealingOptions;\n reporter?: \"default\" | \"json\" | string;\n timeoutMs?: number;\n nodeBin?: string;\n stdio?: \"inherit\" | \"pipe\";\n};\n\nexport type RunResult = {\n ok: boolean;\n exitCode: number;\n summary: {\n total: number;\n passed: number;\n failed: number;\n flaky: number;\n skipped: number;\n healed?: number;\n warned?: number;\n durationMs: number;\n };\n artifactsDir?: string;\n rawOutput?: string;\n error?: Error;\n};\n\ntype JsonReport = {\n suites?: JsonSuite[];\n duration?: number;\n};\n\ntype JsonSuite = {\n suites?: JsonSuite[];\n tests?: Array<{\n title: string;\n results: Array<{\n status: \"passed\" | \"failed\" | \"timedOut\" | \"skipped\" | \"interrupted\";\n duration: number;\n errors?: unknown[];\n }>;\n }>;\n};\n\nexport async function run(request: RunRequest = {}): Promise<RunResult> {\n const cwd = request.projectRoot ?? process.cwd();\n const stdio = request.stdio ?? \"inherit\";\n const requireFn = makeRequire();\n const playwrightCli = requireFn.resolve(\"@playwright/test/cli\");\n const baseDir = path.dirname(fileURLToPath(import.meta.url));\n const preloadPath = path.join(baseDir, \"runner\", \"preload.js\");\n const { runnerBin, preloadFlag } = resolveRunner(preloadPath);\n\n const { jsonReportPath, eventLogPath, artifactsDir } = prepareArtifactsDir(cwd);\n const reporter = buildReporterArgs(request.reporter, jsonReportPath);\n const args = buildArgs({\n testDir: request.testDir,\n configFile: request.configFile,\n cliArgs: request.cliArgs,\n reporter,\n });\n\n const nodeOptions =\n process.env.NODE_OPTIONS && preloadFlag\n ? `${process.env.NODE_OPTIONS} ${preloadFlag}`\n : preloadFlag ?? process.env.NODE_OPTIONS;\n\n const env = buildEnv({\n base: process.env,\n overrides: request.env,\n healing: request.healing,\n eventLogPath,\n nodeOptions,\n });\n\n const runResult = await spawnPlaywright({\n bin: request.nodeBin ?? runnerBin,\n args: [playwrightCli, ...args],\n cwd,\n env,\n stdio,\n timeoutMs: request.timeoutMs,\n });\n\n const summary = summarize(jsonReportPath, eventLogPath, runResult.durationMs);\n\n return {\n ok: runResult.exitCode === 0,\n exitCode: runResult.exitCode,\n summary,\n artifactsDir,\n rawOutput: runResult.output,\n error: runResult.error,\n };\n}\n\nfunction buildArgs(opts: {\n testDir?: string | string[];\n configFile?: string;\n cliArgs?: string[];\n reporter: string;\n}): string[] {\n const args = [\"test\"];\n\n if (opts.testDir) {\n const dirs = Array.isArray(opts.testDir) ? opts.testDir : [opts.testDir];\n args.push(...dirs);\n }\n\n if (opts.configFile) {\n args.push(\"--config\", opts.configFile);\n }\n\n args.push(\"--reporter\", opts.reporter);\n\n if (opts.cliArgs?.length) {\n args.push(...opts.cliArgs);\n }\n\n return args;\n}\n\nfunction buildReporterArgs(requested: RunRequest[\"reporter\"], jsonReportPath: string): string {\n if (requested === \"json\") return `json=${jsonReportPath}`;\n if (requested && requested !== \"default\") return requested;\n // Default: keep list output plus JSON for programmatic summary\n return `list,json=${jsonReportPath}`;\n}\n\nfunction prepareArtifactsDir(cwd: string): { jsonReportPath: string; eventLogPath: string; artifactsDir: string } {\n const dir = fs.mkdtempSync(path.join(os.tmpdir(), \"canary-run-\"));\n const jsonReportPath = path.join(dir, \"report.json\");\n const eventLogPath = path.join(dir, \"events-worker-0.jsonl\");\n const artifactsDir = path.join(cwd, \"test-results\", \"ai-healer\");\n return { jsonReportPath, eventLogPath, artifactsDir: dir };\n}\n\nfunction buildEnv(params: {\n base: NodeJS.ProcessEnv;\n overrides?: Record<string, string>;\n healing?: HealingOptions;\n eventLogPath: string;\n nodeOptions?: string;\n}): NodeJS.ProcessEnv {\n const healing = params.healing ?? {};\n const env: NodeJS.ProcessEnv = {\n ...params.base,\n CANARY_ENABLED: params.base.CANARY_ENABLED ?? \"1\",\n CANARY_RUNNER: \"canary\",\n CANARY_EVENT_LOG: params.eventLogPath,\n ...(params.nodeOptions ? { NODE_OPTIONS: params.nodeOptions } : {}),\n ...(healing.apiKey ? { AI_API_KEY: healing.apiKey } : {}),\n ...(healing.provider ? { AI_PROVIDER: healing.provider } : {}),\n ...(healing.model ? { AI_MODEL: healing.model } : {}),\n ...(healing.timeoutMs ? { AI_TIMEOUT_MS: String(healing.timeoutMs) } : {}),\n ...(healing.maxActions ? { CANARY_MAX_ACTIONS: String(healing.maxActions) } : {}),\n ...(healing.vision ? { CANARY_VISION: \"1\" } : {}),\n ...(healing.dryRun ? { CANARY_DRY_RUN: \"1\" } : {}),\n ...(healing.warnOnly ? { CANARY_WARN_ONLY: \"1\" } : {}),\n ...(healing.debug ? { CANARY_DEBUG: \"1\" } : {}),\n ...(healing.readOnly ? { CANARY_READ_ONLY: \"1\" } : {}),\n ...(healing.allowEvaluate === false ? { CANARY_ALLOW_EVALUATE: \"0\" } : {}),\n ...(healing.allowRunCode ? { CANARY_ALLOW_RUN_CODE: \"1\" } : {}),\n ...(healing.maxPayloadBytes ? { CANARY_MAX_PAYLOAD_BYTES: String(healing.maxPayloadBytes) } : {}),\n ...params.overrides,\n };\n return env;\n}\n\nasync function spawnPlaywright(opts: {\n bin: string;\n args: string[];\n cwd: string;\n env: NodeJS.ProcessEnv;\n stdio: \"inherit\" | \"pipe\";\n timeoutMs?: number;\n}): Promise<{ exitCode: number; output?: string; durationMs: number; error?: Error }> {\n return new Promise((resolve) => {\n const started = Date.now();\n const child = spawn(opts.bin, opts.args, {\n cwd: opts.cwd,\n env: opts.env,\n stdio: opts.stdio === \"inherit\" ? \"inherit\" : [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let timer: NodeJS.Timeout | undefined;\n let output = \"\";\n let error: Error | undefined;\n\n if (opts.stdio === \"pipe\") {\n child.stdout?.on(\"data\", (chunk) => {\n output += chunk.toString();\n });\n child.stderr?.on(\"data\", (chunk) => {\n output += chunk.toString();\n });\n }\n\n if (opts.timeoutMs && opts.timeoutMs > 0) {\n timer = setTimeout(() => {\n error = new Error(`canary.run timed out after ${opts.timeoutMs}ms`);\n child.kill(\"SIGKILL\");\n }, opts.timeoutMs);\n }\n\n child.on(\"close\", (code) => {\n if (timer) clearTimeout(timer);\n resolve({ exitCode: code ?? 1, output: output || undefined, durationMs: Date.now() - started, error });\n });\n });\n}\n\nfunction summarize(jsonReportPath: string, eventLogPath: string, durationMs: number): RunResult[\"summary\"] {\n const base = {\n total: 0,\n passed: 0,\n failed: 0,\n flaky: 0,\n skipped: 0,\n durationMs,\n };\n\n const jsonReport = readJsonReport(jsonReportPath);\n if (jsonReport) {\n const counts = countTests(jsonReport);\n base.total = counts.total;\n base.passed = counts.passed;\n base.failed = counts.failed;\n base.flaky = counts.flaky;\n base.skipped = counts.skipped;\n base.durationMs = jsonReport.duration ?? durationMs;\n }\n\n const healed = countHealed(eventLogPath);\n if (healed) {\n return { ...base, healed };\n }\n return base;\n}\n\nfunction readJsonReport(reportPath: string): JsonReport | undefined {\n try {\n if (fs.existsSync(reportPath)) {\n const raw = fs.readFileSync(reportPath, \"utf-8\");\n return JSON.parse(raw) as JsonReport;\n }\n } catch {\n // ignore parse issues\n }\n return undefined;\n}\n\nfunction countTests(report: JsonReport): {\n total: number;\n passed: number;\n failed: number;\n flaky: number;\n skipped: number;\n} {\n let total = 0;\n let passed = 0;\n let failed = 0;\n let flaky = 0;\n let skipped = 0;\n\n const visitSuite = (suite?: JsonSuite) => {\n if (!suite) return;\n suite.tests?.forEach((test) => {\n total += 1;\n const statuses = test.results.map((r) => r.status);\n const hasFailed = statuses.includes(\"failed\") || statuses.includes(\"interrupted\");\n const hasPassed = statuses.includes(\"passed\");\n const hasTimedOut = statuses.includes(\"timedOut\");\n const allSkipped = statuses.every((s) => s === \"skipped\");\n\n if (allSkipped) {\n skipped += 1;\n } else if ((hasFailed || hasTimedOut) && hasPassed) {\n flaky += 1;\n } else if (hasFailed || hasTimedOut) {\n failed += 1;\n } else if (hasPassed && statuses.length > 1) {\n flaky += 1;\n } else if (hasPassed) {\n passed += 1;\n }\n });\n\n suite.suites?.forEach(visitSuite);\n };\n\n report.suites?.forEach(visitSuite);\n\n return { total, passed, failed, flaky, skipped };\n}\n\nfunction countHealed(eventLogPath: string): number | undefined {\n try {\n if (!fs.existsSync(eventLogPath)) return undefined;\n const raw = fs.readFileSync(eventLogPath, \"utf-8\").trim();\n if (!raw) return undefined;\n const lines = raw.split(\"\\n\");\n let healed = 0;\n for (const line of lines) {\n try {\n const event = JSON.parse(line);\n if (event?.healed === true) healed += 1;\n } catch {\n // ignore bad lines\n }\n }\n return healed;\n } catch {\n return undefined;\n }\n}\n\nexport const __internals = {\n buildArgs,\n buildReporterArgs,\n buildEnv,\n countTests,\n countHealed,\n summarize,\n};\n","import process from \"node:process\";\nimport readline from \"node:readline\";\nimport { spawn } from \"node:child_process\";\nimport { saveAuth, ENV_URLS } from \"./auth\";\n\ntype StartResponse = {\n ok: boolean;\n deviceCode?: string;\n userCode?: string;\n verificationUrl?: string;\n expiresAt?: string;\n intervalSeconds?: number;\n error?: string;\n};\n\ntype PollResponse = {\n ok: boolean;\n status?: \"pending\" | \"approved\" | \"rejected\" | \"expired\";\n accessToken?: string;\n orgId?: string;\n error?: string;\n};\n\ntype OrgsResponse = {\n ok: boolean;\n currentOrgId?: string;\n organizations?: Array<{ id: string; name: string; role: string }>;\n error?: string;\n};\n\ntype SwitchOrgResponse = {\n ok: boolean;\n accessToken?: string;\n orgId?: string;\n orgName?: string;\n role?: string;\n error?: string;\n};\n\n/** Map --env values to profile names for multi-profile auth storage */\nfunction envToProfile(env: string): string {\n if (env === \"prod\" || env === \"production\") return \"production\";\n if (env === \"dev\") return \"dev\";\n if (env === \"local\") return \"local\";\n return env;\n}\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nfunction shouldOpenBrowser(argv: string[]): boolean {\n return !argv.includes(\"--no-open\");\n}\n\nfunction openUrl(url: string) {\n const platform = process.platform;\n if (platform === \"darwin\") {\n spawn(\"open\", [url], { stdio: \"ignore\" });\n return;\n }\n if (platform === \"win32\") {\n spawn(\"cmd\", [\"/c\", \"start\", \"\", url], { stdio: \"ignore\" });\n return;\n }\n spawn(\"xdg-open\", [url], { stdio: \"ignore\" });\n}\n\nfunction promptChoice(question: string): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nasync function fetchOrgs(apiUrl: string, token: string): Promise<OrgsResponse | null> {\n try {\n const res = await fetch(`${apiUrl}/cli-login/orgs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) return null;\n return (await res.json()) as OrgsResponse;\n } catch {\n return null;\n }\n}\n\nasync function switchOrg(apiUrl: string, token: string, orgId: string): Promise<SwitchOrgResponse> {\n const res = await fetch(`${apiUrl}/cli-login/switch-org`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ orgId }),\n });\n return (await res.json()) as SwitchOrgResponse;\n}\n\nexport async function runLogin(argv: string[]) {\n const env = getArgValue(argv, \"--env\");\n const envUrls = env ? ENV_URLS[env] : undefined;\n\n if (env && !envUrls) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n envUrls?.api ??\n process.env.CANARY_API_URL ??\n \"https://api.trycanary.ai\";\n const appUrl =\n getArgValue(argv, \"--app-url\") ??\n envUrls?.app ??\n process.env.CANARY_APP_URL ??\n \"https://app.trycanary.ai\";\n\n const orgFlag = getArgValue(argv, \"--org\");\n\n const startRes = await fetch(`${apiUrl}/cli-login/start`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ appUrl }),\n });\n\n const startJson = (await startRes.json()) as StartResponse;\n if (!startRes.ok || !startJson.ok || !startJson.deviceCode || !startJson.userCode) {\n console.error(\"Login start failed\", startJson.error ?? startRes.statusText);\n process.exit(1);\n }\n\n console.log(\"Login required.\");\n console.log(`User code: ${startJson.userCode}`);\n if (startJson.verificationUrl) {\n console.log(`Open: ${startJson.verificationUrl}`);\n if (shouldOpenBrowser(argv)) {\n try {\n openUrl(startJson.verificationUrl);\n } catch {\n console.log(\"Unable to open browser automatically. Please open the URL manually.\");\n }\n }\n }\n\n const intervalMs = (startJson.intervalSeconds ?? 3) * 1000;\n const expiresAt = startJson.expiresAt ? new Date(startJson.expiresAt).getTime() : null;\n\n let token: string | undefined;\n let initialOrgId: string | undefined;\n\n while (true) {\n if (expiresAt && Date.now() > expiresAt) {\n console.error(\"Login code expired.\");\n process.exit(1);\n }\n\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\n const pollRes = await fetch(`${apiUrl}/cli-login/poll`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ deviceCode: startJson.deviceCode }),\n });\n\n const pollJson = (await pollRes.json()) as PollResponse;\n if (!pollRes.ok || !pollJson.ok) {\n console.error(\"Login poll failed\", pollJson.error ?? pollRes.statusText);\n process.exit(1);\n }\n\n if (pollJson.status === \"approved\" && pollJson.accessToken) {\n token = pollJson.accessToken;\n initialOrgId = pollJson.orgId;\n break;\n }\n\n if (pollJson.status === \"rejected\") {\n console.error(\"Login rejected.\");\n process.exit(1);\n }\n\n if (pollJson.status === \"expired\") {\n console.error(\"Login expired.\");\n process.exit(1);\n }\n }\n\n // Fetch user's organizations (gracefully skip if endpoint not available)\n const orgsData = await fetchOrgs(apiUrl, token);\n const orgs = orgsData?.organizations ?? [];\n\n let finalToken = token;\n let finalOrgId = initialOrgId;\n let finalOrgName: string | undefined;\n\n if (orgs.length <= 1) {\n // Single org (or none) — use as-is\n finalOrgName = orgs[0]?.name;\n } else {\n // Multiple orgs — determine which one to use\n let selectedOrg: (typeof orgs)[number] | undefined;\n\n if (orgFlag) {\n // Match --org flag by name (case-insensitive) or ID\n selectedOrg = orgs.find(\n (o) => o.name.toLowerCase() === orgFlag.toLowerCase() || o.id === orgFlag\n );\n if (!selectedOrg) {\n console.error(`Organization \"${orgFlag}\" not found. Available orgs:`);\n for (const o of orgs) {\n console.error(` - ${o.name} (${o.id})`);\n }\n process.exit(1);\n }\n } else if (process.stdin.isTTY) {\n // Interactive prompt\n console.log(\"\\nYou belong to multiple organizations. Select one:\");\n for (let i = 0; i < orgs.length; i++) {\n const marker = orgs[i].id === initialOrgId ? \" (current)\" : \"\";\n console.log(` ${i + 1}. ${orgs[i].name}${marker}`);\n }\n\n const answer = await promptChoice(`\\nChoice [1-${orgs.length}]: `);\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= orgs.length) {\n console.error(\"Invalid selection.\");\n process.exit(1);\n }\n selectedOrg = orgs[idx];\n } else {\n // Non-interactive — use default from JWT\n const defaultOrg = orgs.find((o) => o.id === initialOrgId);\n console.log(\n `Warning: Multiple organizations available but running non-interactively. Using \"${defaultOrg?.name ?? initialOrgId}\".`\n );\n console.log('Tip: Use --org <name> to select a specific organization.');\n selectedOrg = defaultOrg;\n }\n\n if (selectedOrg && selectedOrg.id !== initialOrgId) {\n // Switch to selected org\n const switchRes = await switchOrg(apiUrl, token, selectedOrg.id);\n if (!switchRes.ok || !switchRes.accessToken) {\n console.error(\"Failed to switch organization:\", switchRes.error ?? \"Unknown error\");\n process.exit(1);\n }\n finalToken = switchRes.accessToken;\n finalOrgId = switchRes.orgId;\n finalOrgName = switchRes.orgName;\n } else if (selectedOrg) {\n finalOrgName = selectedOrg.name;\n }\n }\n\n const profileName = env ? envToProfile(env) : undefined;\n const filePath = await saveAuth({ token: finalToken, apiUrl, orgId: finalOrgId, orgName: finalOrgName }, profileName);\n const displayName = finalOrgName ? ` to ${finalOrgName}` : \"\";\n const profileLabel = profileName ? ` (profile: ${profileName})` : \"\";\n console.log(`Login successful${displayName}${profileLabel}. Token saved to ${filePath}`);\n console.log(\"Set CANARY_API_TOKEN to use the CLI without re-login.\");\n}\n","import process from \"node:process\";\nimport { readStoredAuth, readStoredToken } from \"./auth\";\n\ntype OrgsResponse = {\n ok: boolean;\n currentOrgId?: string;\n organizations?: Array<{ id: string; name: string; role: string }>;\n error?: string;\n};\n\nexport async function runOrgs(argv: string[]) {\n const token = process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n if (!token) {\n console.error(\"Not logged in. Run: canary login\");\n process.exit(1);\n }\n\n const auth = await readStoredAuth();\n const apiUrl =\n process.env.CANARY_API_URL ?? auth?.apiUrl ?? \"https://api.trycanary.ai\";\n\n const res = await fetch(`${apiUrl}/cli-login/orgs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) {\n console.error(\"Failed to fetch organizations. You may need to re-login: canary login\");\n process.exit(1);\n }\n\n const data = (await res.json()) as OrgsResponse;\n if (!data.ok || !data.organizations) {\n console.error(\"Failed to fetch organizations:\", data.error ?? \"Unknown error\");\n process.exit(1);\n }\n\n const currentOrgId = auth?.orgId ?? data.currentOrgId;\n\n console.log(\"Organizations:\\n\");\n for (const org of data.organizations) {\n const marker = org.id === currentOrgId ? \" *\" : \"\";\n console.log(` ${org.name} (${org.role})${marker}`);\n }\n\n console.log(\"\\n* = current organization\");\n console.log(\"To switch: canary login --org <name>\");\n}\n","import process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\nimport { createLocalRun } from \"./local-run\";\nimport { connectTunnel, createTunnel } from \"./tunnel\";\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1) return undefined;\n return argv[index + 1];\n}\n\nexport async function runLocalSession(argv: string[]) {\n const apiUrl = getArgValue(argv, \"--api-url\") ?? process.env.CANARY_API_URL ?? \"https://api.trycanary.ai\";\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n\n if (!token) {\n console.error(\"Missing token. Run `canary login` first or set CANARY_API_TOKEN.\");\n process.exit(1);\n }\n\n const portRaw = getArgValue(argv, \"--port\") ?? process.env.CANARY_LOCAL_PORT;\n const tunnelUrl = getArgValue(argv, \"--tunnel-url\");\n const title = getArgValue(argv, \"--title\");\n const featureSpec = getArgValue(argv, \"--feature\");\n const startUrl = getArgValue(argv, \"--start-url\");\n\n if (!tunnelUrl && !portRaw) {\n console.error(\"Missing --port or --tunnel-url\");\n process.exit(1);\n }\n\n let publicUrl = tunnelUrl;\n let ws: WebSocket | null = null;\n\n if (!publicUrl && portRaw) {\n const port = Number(portRaw);\n if (Number.isNaN(port) || port <= 0) {\n console.error(\"Invalid --port value\");\n process.exit(1);\n }\n\n const tunnel = await createTunnel({ apiUrl, token, port });\n publicUrl = tunnel.publicUrl;\n\n ws = connectTunnel({\n apiUrl,\n tunnelId: tunnel.tunnelId,\n token: tunnel.token,\n port,\n onReady: () => {\n console.log(`Tunnel connected: ${publicUrl ?? tunnel.tunnelId}`);\n },\n });\n }\n\n if (!publicUrl) {\n console.error(\"Failed to resolve tunnel URL\");\n process.exit(1);\n }\n\n const run = await createLocalRun({\n apiUrl,\n token,\n title,\n featureSpec,\n startUrl,\n tunnelUrl: publicUrl,\n });\n\n console.log(`Local test queued: ${run.runId}`);\n if (run.watchUrl) {\n console.log(`Watch: ${run.watchUrl}`);\n }\n\n if (ws) {\n console.log(\"Tunnel active. Press Ctrl+C to stop.\");\n process.on(\"SIGINT\", () => {\n ws?.close();\n process.exit(0);\n });\n await new Promise<void>(() => undefined);\n }\n}\n","/**\n * Remote Test Command\n *\n * Triggers workflow tests on the Canary platform and streams results via SSE.\n * Designed for CI/CD pipelines - exits with code 0 on success, 1 on failure.\n *\n * Usage:\n * canary test --remote [options]\n *\n * Options:\n * --token <key> API key (or set CANARY_API_TOKEN env var)\n * --api-url <url> API URL (default: https://api.trycanary.ai)\n * --tag <tag> Filter workflows by tag\n * --name-pattern <pat> Filter workflows by name pattern\n * --verbose, -v Show all events (not just results)\n */\n\nimport process from \"node:process\";\nimport { createParser, type EventSourceMessage } from \"eventsource-parser\";\nimport { readStoredToken } from \"./auth\";\n\ntype WorkflowTestStatus = \"queued\" | \"running\" | \"waiting\" | \"success\" | \"failed\";\n\ntype WorkflowTestEvent = {\n type: \"workflow-test\";\n suiteId: string;\n workflowId: string;\n testRunId: string;\n workflowRunId: string | null;\n status: WorkflowTestStatus;\n startedAt: string | null;\n finishedAt: string | null;\n durationMs: number | null;\n errorMessage?: string | null;\n linkedIssueId?: string | null;\n message?: string;\n resumeAt?: string | null;\n segmentIndex?: number | null;\n totalSegments?: number | null;\n};\n\ntype WorkflowTestSuiteEvent = {\n type: \"workflow-test-suite\";\n suiteId: string;\n status: \"running\" | \"completed\";\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n totalWorkflows: number;\n completedWorkflows: number;\n failedWorkflows: number;\n successfulWorkflows: number;\n errorMessage?: string | null;\n message?: string;\n};\n\ntype TriggerResponse = {\n ok: boolean;\n jobId?: string;\n suiteId?: string;\n startedAt?: string;\n appUrl?: string;\n error?: string;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nfunction buildQueryParams(tag?: string, namePattern?: string): string {\n const params = new URLSearchParams();\n if (tag) params.set(\"tag\", tag);\n if (namePattern) params.set(\"namePattern\", namePattern);\n return params.toString();\n}\n\nfunction extractWorkflowName(message: string | undefined, workflowId: string): string {\n if (!message) return workflowId;\n const match = message.match(/^Flow \"(.+?)\" /);\n return match ? match[1] : workflowId;\n}\n\ntype FailedTest = { name: string; testRunId: string };\n\nfunction formatFailedTests(\n failedTests: FailedTest[],\n appUrl: string | undefined,\n): string | null {\n if (failedTests.length === 0 || !appUrl) return null;\n\n const lines = [\"\", \"Failed tests:\"];\n for (const t of failedTests) {\n lines.push(` \\u2717 ${t.name}`);\n lines.push(` ${appUrl}/runs/tests/${t.testRunId}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction formatSummary(stats: {\n totalWorkflows: number;\n successfulWorkflows: number;\n failedWorkflows: number;\n completedWorkflows: number;\n}): { message: string; exitCode: number } {\n const { totalWorkflows, successfulWorkflows, failedWorkflows, completedWorkflows } = stats;\n\n if (totalWorkflows === 0) {\n return { message: \"No workflows found matching the filter criteria.\", exitCode: 0 };\n }\n\n const passRate = Math.round((successfulWorkflows / totalWorkflows) * 100);\n const waitingWorkflows = totalWorkflows - completedWorkflows;\n\n let message: string;\n let exitCode: number;\n\n if (failedWorkflows > 0) {\n message = `FAILED: ${failedWorkflows} of ${totalWorkflows} workflows failed (${passRate}% pass rate)`;\n exitCode = 1;\n } else {\n message = `PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`;\n exitCode = 0;\n }\n\n if (waitingWorkflows > 0) {\n message += `\\nNote: ${waitingWorkflows} workflow(s) are still waiting (scheduled for later)`;\n }\n\n return { message, exitCode };\n}\n\n// Export internals for testing\nexport const __internals = {\n getArgValue,\n hasFlag,\n buildQueryParams,\n extractWorkflowName,\n formatSummary,\n formatFailedTests,\n};\n\nexport async function runRemoteTest(argv: string[]): Promise<void> {\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n process.env.CANARY_API_URL ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ??\n process.env.CANARY_API_TOKEN ??\n (await readStoredToken());\n\n const tag = getArgValue(argv, \"--tag\");\n const namePattern = getArgValue(argv, \"--name-pattern\");\n const verbose = hasFlag(argv, \"--verbose\", \"-v\");\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"\");\n console.error(\"Set CANARY_API_TOKEN environment variable or run:\");\n console.error(\" canary login\");\n console.error(\"\");\n console.error(\"Or create an API key in Settings > API Keys and pass it:\");\n console.error(\" canary test --remote --token cnry_...\");\n process.exit(1);\n }\n\n console.log(\"Starting remote workflow tests...\");\n if (tag) console.log(` Filtering by tag: ${tag}`);\n if (namePattern) console.log(` Filtering by name pattern: ${namePattern}`);\n console.log(\"\");\n\n // 1. Trigger test run\n const queryParams = new URLSearchParams();\n if (tag) queryParams.set(\"tag\", tag);\n if (namePattern) queryParams.set(\"namePattern\", namePattern);\n\n const triggerUrl = `${apiUrl}/workflows/test-runs${queryParams.toString() ? `?${queryParams}` : \"\"}`;\n\n let triggerRes: Response;\n try {\n triggerRes = await fetch(triggerUrl, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n } catch (err) {\n console.error(`Failed to connect to API: ${err}`);\n process.exit(1);\n }\n\n if (!triggerRes.ok) {\n const errorText = await triggerRes.text();\n console.error(`Failed to start tests: ${triggerRes.status}`);\n console.error(errorText);\n process.exit(1);\n }\n\n const triggerData = (await triggerRes.json()) as TriggerResponse;\n if (!triggerData.ok || !triggerData.suiteId) {\n console.error(`Failed to start tests: ${triggerData.error ?? \"Unknown error\"}`);\n process.exit(1);\n }\n\n const { suiteId, jobId, appUrl } = triggerData;\n if (verbose) {\n console.log(`Suite ID: ${suiteId}`);\n console.log(`Job ID: ${jobId}`);\n console.log(\"\");\n }\n\n // 2. Subscribe to SSE stream\n const streamUrl = `${apiUrl}/workflows/test-runs/stream?suiteId=${suiteId}`;\n\n let streamRes: Response;\n try {\n streamRes = await fetch(streamUrl, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"text/event-stream\",\n },\n });\n } catch (err) {\n console.error(`Failed to connect to event stream: ${err}`);\n process.exit(1);\n }\n\n if (!streamRes.ok || !streamRes.body) {\n console.error(`Failed to connect to event stream: ${streamRes.status}`);\n process.exit(1);\n }\n\n // Track state\n let exitCode = 0;\n let hasCompleted = false;\n const workflowNames = new Map<string, string>();\n const failedTests: FailedTest[] = [];\n let totalWorkflows = 0;\n let completedWorkflows = 0;\n let failedWorkflows = 0;\n let successfulWorkflows = 0;\n\n // 3. Parse SSE events\n const parser = createParser({\n onEvent: (event: EventSourceMessage) => {\n if (!event.data) return;\n\n try {\n const data = JSON.parse(event.data) as\n | WorkflowTestEvent\n | WorkflowTestSuiteEvent\n | { error?: string };\n\n if (verbose) {\n console.log(`[${event.event}] ${JSON.stringify(data)}`);\n }\n\n if (event.event === \"workflow-test\") {\n const testEvent = data as WorkflowTestEvent;\n const { status, workflowId, message, errorMessage } = testEvent;\n const name = workflowNames.get(workflowId) || message?.replace(/^Flow \"(.+)\" .*$/, \"$1\") || workflowId;\n\n if (message?.startsWith('Flow \"')) {\n const match = message.match(/^Flow \"(.+?)\" /);\n if (match) workflowNames.set(workflowId, match[1]);\n }\n\n if (!verbose) {\n if (status === \"success\") {\n console.log(` \\u2713 ${name}`);\n } else if (status === \"failed\") {\n console.log(` \\u2717 ${name}`);\n if (errorMessage) {\n console.log(` Error: ${errorMessage.slice(0, 200)}`);\n }\n failedTests.push({ name, testRunId: testEvent.testRunId });\n exitCode = 1;\n } else if (status === \"running\") {\n // Don't log running status in non-verbose mode\n } else if (status === \"waiting\") {\n console.log(` \\u23F3 ${name} (waiting for scheduled time)`);\n }\n }\n }\n\n if (event.event === \"workflow-test-suite\") {\n const suiteEvent = data as WorkflowTestSuiteEvent;\n totalWorkflows = suiteEvent.totalWorkflows;\n completedWorkflows = suiteEvent.completedWorkflows;\n failedWorkflows = suiteEvent.failedWorkflows;\n successfulWorkflows = suiteEvent.successfulWorkflows;\n\n if (suiteEvent.status === \"completed\") {\n hasCompleted = true;\n }\n }\n\n if (event.event === \"error\") {\n const errorData = data as { error?: string };\n console.error(`Stream error: ${errorData.error ?? \"Unknown error\"}`);\n exitCode = 1;\n }\n } catch {\n // Ignore parse errors for keepalive, etc.\n }\n },\n });\n\n const reader = streamRes.body.getReader();\n const decoder = new TextDecoder();\n\n try {\n while (!hasCompleted) {\n const { done, value } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } finally {\n reader.releaseLock();\n }\n\n // 4. Print summary\n console.log(\"\");\n console.log(\"─\".repeat(50));\n\n if (totalWorkflows === 0) {\n console.log(\"No workflows found matching the filter criteria.\");\n process.exit(0);\n }\n\n const passRate = totalWorkflows > 0 ? Math.round((successfulWorkflows / totalWorkflows) * 100) : 0;\n\n if (failedWorkflows > 0) {\n console.log(`FAILED: ${failedWorkflows} of ${totalWorkflows} workflows failed (${passRate}% pass rate)`);\n exitCode = 1;\n } else {\n console.log(`PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`);\n }\n\n const waitingWorkflows = totalWorkflows - completedWorkflows;\n if (waitingWorkflows > 0) {\n console.log(`Note: ${waitingWorkflows} workflow(s) are still waiting (scheduled for later)`);\n }\n\n const failedSection = formatFailedTests(failedTests, appUrl);\n if (failedSection) {\n console.log(failedSection);\n }\n\n process.exit(exitCode);\n}\n","/**\n * CLI Debug Session Generator\n *\n * Creates a debug login token for browser-based debugging with Playwright.\n * Requires superadmin authentication.\n *\n * Usage:\n * canary debug-session [--env dev|prod|local] [--api-url <url>]\n *\n * Outputs the login URL that can be used with Playwright to authenticate\n * as the debug agent user for read-only browser access.\n */\n\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\ntype DebugSessionResponse = {\n ok: boolean;\n loginUrl?: string;\n expiresAt?: string;\n error?: string;\n message?: string;\n};\n\nconst ENV_URLS: Record<string, { api: string; app: string }> = {\n prod: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n production: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n dev: {\n api: \"https://api.dev.trycanary.ai\",\n app: \"https://app.dev.trycanary.ai\",\n },\n local: {\n api: \"http://localhost:3000\",\n app: \"http://localhost:5173\",\n },\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nasync function writeDebugSession(loginUrl: string, expiresAt: string, apiUrl: string) {\n const dir = path.join(os.homedir(), \".config\", \"canary-cli\");\n const filePath = path.join(dir, \"debug-session.json\");\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n await fs.writeFile(\n filePath,\n JSON.stringify({ loginUrl, expiresAt, apiUrl, createdAt: new Date().toISOString() }, null, 2),\n { encoding: \"utf8\", mode: 0o600 }\n );\n return filePath;\n}\n\nexport async function runDebugSession(argv: string[]): Promise<void> {\n // Check for --env flag\n const env = getArgValue(argv, \"--env\");\n const envUrls = env ? ENV_URLS[env] : undefined;\n\n if (env && !envUrls) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const storedApiUrl = await readStoredApiUrl();\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n envUrls?.api ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n const jsonOutput = hasFlag(argv, \"--json\");\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login [--env <env>]\");\n process.exit(1);\n }\n\n try {\n const res = await fetch(`${apiUrl}/auth/debug-session/create`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n // Handle HTTP errors before parsing JSON\n if (!res.ok) {\n const text = await res.text();\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n if (res.status === 403) {\n console.error(\"Error: Forbidden. Debug session creation requires superadmin access.\");\n process.exit(1);\n }\n\n if (res.status === 404) {\n console.error(\n \"Error: Endpoint not found. The debug-session feature may not be deployed to this environment.\"\n );\n process.exit(1);\n }\n\n // Try to parse error as JSON, fallback to raw text\n try {\n const errorJson = JSON.parse(text) as { error?: string; message?: string };\n console.error(`Error: ${errorJson.message ?? errorJson.error ?? text}`);\n } catch {\n console.error(`Error (${res.status}): ${text || res.statusText}`);\n }\n process.exit(1);\n }\n\n const json = (await res.json()) as DebugSessionResponse;\n\n if (!json.ok || !json.loginUrl) {\n console.error(`Error: ${json.message ?? json.error ?? \"Failed to create debug session\"}`);\n process.exit(1);\n }\n\n // Save session info\n const filePath = await writeDebugSession(json.loginUrl, json.expiresAt ?? \"\", apiUrl);\n\n if (jsonOutput) {\n console.log(\n JSON.stringify(\n {\n loginUrl: json.loginUrl,\n expiresAt: json.expiresAt,\n sessionFile: filePath,\n },\n null,\n 2\n )\n );\n } else {\n console.log(\"Debug session created successfully.\");\n console.log(\"\");\n console.log(`Login URL: ${json.loginUrl}`);\n console.log(`Expires: ${json.expiresAt}`);\n console.log(`Session saved to: ${filePath}`);\n console.log(\"\");\n console.log(\"Use this URL with Playwright to authenticate as the debug agent.\");\n console.log(\"The token is single-use and expires in 5 minutes.\");\n }\n } catch (err) {\n console.error(`Failed to create debug session: ${err}`);\n process.exit(1);\n }\n}\n","/**\n * JWT payload decoder for CLI superadmin detection.\n *\n * Decodes the payload section of a JWT locally (no signature verification,\n * no network calls). Used to conditionally show superadmin CLI commands.\n *\n * @module\n */\n\n/**\n * Decode the payload (second segment) of a JWT without verifying the signature.\n * Returns the parsed JSON object, or `null` if the token is malformed.\n */\nexport function decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n\n const base64 = parts[1]!.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const json = Buffer.from(base64, \"base64\").toString(\"utf8\");\n const payload: unknown = JSON.parse(json);\n\n if (typeof payload !== \"object\" || payload === null || Array.isArray(payload)) {\n return null;\n }\n return payload as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Returns `true` when the JWT's `is_superadmin` claim is exactly `true`.\n * Returns `false` for any invalid, missing, or non-superadmin token.\n */\nexport function isSuperadminToken(token: string | null | undefined): boolean {\n if (!token) return false;\n const payload = decodeJwtPayload(token);\n return payload?.is_superadmin === true;\n}\n"],"mappings":"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","names":["spawnSync","createRequire","process","path","fileURLToPath","pathToFileURL","preloadPath","fs","path","requireFn","baseDir","path","preloadPath","fs","process","spawn","process","spawn","process","process","process","getArgValue","process","run","process","getArgValue","getArgValue","process","fs","os","path","process","ENV_URLS","getArgValue","hasFlag","path","os","fs","process","require","createRequire","path","fileURLToPath","process","spawnSync","process","pathToFileURL"]}
@@ -20,6 +20,63 @@ function getArgValue(argv, key) {
20
20
  function hasFlag(argv, ...flags) {
21
21
  return flags.some((flag) => argv.includes(flag));
22
22
  }
23
+ function toLifecycleLabel(stage) {
24
+ switch (stage) {
25
+ case "deprecated":
26
+ return "deprecated";
27
+ case "ready_for_cleanup":
28
+ return "ready_for_cleanup";
29
+ default:
30
+ return "active";
31
+ }
32
+ }
33
+ function parseLifecycleStage(argv) {
34
+ const stage = getArgValue(argv, "--stage");
35
+ if (!stage || !["active", "deprecated", "ready_for_cleanup"].includes(stage)) {
36
+ console.error("Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup");
37
+ process.exit(1);
38
+ }
39
+ return stage;
40
+ }
41
+ function formatValue(valueType, value) {
42
+ if (valueType === "json") {
43
+ return JSON.stringify(value);
44
+ }
45
+ return String(value);
46
+ }
47
+ function formatFinalValue(knob) {
48
+ if (knob.lifecycleStage === "active") {
49
+ return "(none)";
50
+ }
51
+ return formatValue(knob.valueType, knob.finalValue);
52
+ }
53
+ function parseTypedValue(raw, valueType) {
54
+ switch (valueType) {
55
+ case "boolean":
56
+ if (raw !== "true" && raw !== "false") {
57
+ console.error('Error: Boolean value must be "true" or "false".');
58
+ process.exit(1);
59
+ }
60
+ return raw === "true";
61
+ case "string":
62
+ return raw;
63
+ case "number": {
64
+ const num = parseFloat(raw);
65
+ if (Number.isNaN(num)) {
66
+ console.error(`Error: Invalid number: ${raw}`);
67
+ process.exit(1);
68
+ }
69
+ return num;
70
+ }
71
+ case "json":
72
+ try {
73
+ return JSON.parse(raw);
74
+ } catch {
75
+ console.error(`Error: Invalid JSON: ${raw}`);
76
+ process.exit(1);
77
+ }
78
+ }
79
+ }
23
80
  async function resolveConfig(argv) {
24
81
  const storedApiUrl = await readStoredApiUrl();
25
82
  const env = getArgValue(argv, "--env");
@@ -54,14 +111,7 @@ async function apiRequest(apiUrl, token, method, path, body) {
54
111
  const json = await res.json();
55
112
  return json;
56
113
  }
57
- function formatValue(knob) {
58
- if (knob.valueType === "json") {
59
- return JSON.stringify(knob.value);
60
- }
61
- return String(knob.value);
62
- }
63
- async function handleList(argv, apiUrl, token) {
64
- const jsonOutput = hasFlag(argv, "--json");
114
+ async function fetchKnobs(apiUrl, token) {
65
115
  const res = await fetch(`${apiUrl}/superadmin/knobs`, {
66
116
  headers: { Authorization: `Bearer ${token}` }
67
117
  });
@@ -75,7 +125,11 @@ async function handleList(argv, apiUrl, token) {
75
125
  console.error(`Error: ${json.error}`);
76
126
  process.exit(1);
77
127
  }
78
- const knobs = json.knobs ?? [];
128
+ return json.knobs ?? [];
129
+ }
130
+ async function handleList(argv, apiUrl, token) {
131
+ const jsonOutput = hasFlag(argv, "--json");
132
+ const knobs = await fetchKnobs(apiUrl, token);
79
133
  if (jsonOutput) {
80
134
  console.log(JSON.stringify(knobs, null, 2));
81
135
  return;
@@ -86,7 +140,11 @@ async function handleList(argv, apiUrl, token) {
86
140
  }
87
141
  for (const knob of knobs) {
88
142
  const desc = knob.description ? ` ${knob.description}` : "";
89
- console.log(` ${knob.key} [${knob.valueType}] = ${formatValue(knob)}${desc}`);
143
+ const lifecycle = `lifecycle=${toLifecycleLabel(knob.lifecycleStage)}`;
144
+ const finalValue = knob.lifecycleStage === "active" ? "" : ` final=${formatFinalValue(knob)}`;
145
+ console.log(
146
+ ` ${knob.key} [${knob.valueType}] = ${formatValue(knob.valueType, knob.value)} (${lifecycle}${finalValue})${desc}`
147
+ );
90
148
  }
91
149
  }
92
150
  async function handleGet(argv, apiUrl, token) {
@@ -97,20 +155,8 @@ async function handleGet(argv, apiUrl, token) {
97
155
  process.exit(1);
98
156
  }
99
157
  const jsonOutput = hasFlag(argv, "--json");
100
- const res = await fetch(`${apiUrl}/superadmin/knobs`, {
101
- headers: { Authorization: `Bearer ${token}` }
102
- });
103
- if (res.status === 401) {
104
- console.error("Error: Unauthorized. Your session may have expired.");
105
- console.error("Run: canary login");
106
- process.exit(1);
107
- }
108
- const json = await res.json();
109
- if (!json.ok) {
110
- console.error(`Error: ${json.error}`);
111
- process.exit(1);
112
- }
113
- const knob = (json.knobs ?? []).find((k) => k.key === key);
158
+ const knobs = await fetchKnobs(apiUrl, token);
159
+ const knob = knobs.find((k) => k.key === key);
114
160
  if (!knob) {
115
161
  console.error(`Knob not found: ${key}`);
116
162
  process.exit(1);
@@ -121,7 +167,9 @@ async function handleGet(argv, apiUrl, token) {
121
167
  }
122
168
  console.log(` Key: ${knob.key}`);
123
169
  console.log(` Type: ${knob.valueType}`);
124
- console.log(` Value: ${formatValue(knob)}`);
170
+ console.log(` Value: ${formatValue(knob.valueType, knob.value)}`);
171
+ console.log(` Lifecycle: ${toLifecycleLabel(knob.lifecycleStage)}`);
172
+ console.log(` Final value: ${formatFinalValue(knob)}`);
125
173
  console.log(` Description: ${knob.description ?? "(none)"}`);
126
174
  console.log(` Updated: ${knob.updatedAt ?? "(never)"}`);
127
175
  }
@@ -147,36 +195,7 @@ async function handleSet(argv, apiUrl, token) {
147
195
  console.error("Error: --type is required and must be one of: boolean, string, number, json");
148
196
  process.exit(1);
149
197
  }
150
- let value;
151
- switch (valueType) {
152
- case "boolean":
153
- if (rawValue !== "true" && rawValue !== "false") {
154
- console.error('Error: Boolean value must be "true" or "false".');
155
- process.exit(1);
156
- }
157
- value = rawValue === "true";
158
- break;
159
- case "string":
160
- value = rawValue;
161
- break;
162
- case "number": {
163
- const num = parseFloat(rawValue);
164
- if (Number.isNaN(num)) {
165
- console.error(`Error: Invalid number: ${rawValue}`);
166
- process.exit(1);
167
- }
168
- value = num;
169
- break;
170
- }
171
- case "json":
172
- try {
173
- value = JSON.parse(rawValue);
174
- } catch {
175
- console.error(`Error: Invalid JSON: ${rawValue}`);
176
- process.exit(1);
177
- }
178
- break;
179
- }
198
+ const value = parseTypedValue(rawValue, valueType);
180
199
  const description = getArgValue(argv, "--description") ?? void 0;
181
200
  const result = await apiRequest(apiUrl, token, "POST", "/superadmin/knobs", {
182
201
  key,
@@ -188,7 +207,7 @@ async function handleSet(argv, apiUrl, token) {
188
207
  console.error(`Error: ${result.error}`);
189
208
  process.exit(1);
190
209
  }
191
- console.log(`Set knob: ${key} = ${formatValue(result.knob)}`);
210
+ console.log(`Set knob: ${key} = ${formatValue(valueType, result.knob?.value)}`);
192
211
  }
193
212
  async function handleDelete(argv, apiUrl, token) {
194
213
  const key = argv[0];
@@ -226,7 +245,66 @@ async function handleToggle(argv, apiUrl, token) {
226
245
  console.error(`Error: ${result.error}`);
227
246
  process.exit(1);
228
247
  }
229
- console.log(`Toggled knob: ${key} \u2192 ${result.knob?.value}`);
248
+ const knob = result.knob;
249
+ console.log(`Toggled knob: ${key} -> ${formatValue(knob?.valueType ?? "boolean", knob?.value)}`);
250
+ }
251
+ async function handleLifecycle(argv, apiUrl, token) {
252
+ const key = argv[0];
253
+ if (!key || key.startsWith("--")) {
254
+ console.error("Error: Missing knob key.");
255
+ console.error(
256
+ "Usage: canary knobs lifecycle <key> --stage <active|deprecated|ready_for_cleanup> [--final-value <value>]"
257
+ );
258
+ process.exit(1);
259
+ }
260
+ const stage = parseLifecycleStage(argv);
261
+ const rawFinalValue = getArgValue(argv, "--final-value");
262
+ const clearFinalValue = hasFlag(argv, "--clear-final-value");
263
+ if (rawFinalValue !== void 0 && clearFinalValue) {
264
+ console.error("Error: use either --final-value or --clear-final-value, not both.");
265
+ process.exit(1);
266
+ }
267
+ const knobs = await fetchKnobs(apiUrl, token);
268
+ const knob = knobs.find((k) => k.key === key);
269
+ if (!knob) {
270
+ console.error(`Knob not found: ${key}`);
271
+ process.exit(1);
272
+ }
273
+ let finalValue = void 0;
274
+ if (stage === "active") {
275
+ if (rawFinalValue !== void 0) {
276
+ console.error("Error: active stage does not accept --final-value. Use --stage deprecated|ready_for_cleanup.");
277
+ process.exit(1);
278
+ }
279
+ finalValue = clearFinalValue ? void 0 : void 0;
280
+ } else {
281
+ if (clearFinalValue) {
282
+ console.error("Error: --clear-final-value can only be used with --stage active.");
283
+ process.exit(1);
284
+ }
285
+ if (rawFinalValue === void 0) {
286
+ console.error("Error: --final-value is required when stage is deprecated or ready_for_cleanup.");
287
+ process.exit(1);
288
+ }
289
+ finalValue = parseTypedValue(rawFinalValue, knob.valueType);
290
+ }
291
+ const result = await apiRequest(
292
+ apiUrl,
293
+ token,
294
+ "POST",
295
+ `/superadmin/knobs/${encodeURIComponent(key)}/lifecycle`,
296
+ {
297
+ stage,
298
+ finalValue
299
+ }
300
+ );
301
+ if (!result.ok || !result.knob) {
302
+ console.error(`Error: ${result.error ?? "Failed to update lifecycle"}`);
303
+ process.exit(1);
304
+ }
305
+ console.log(
306
+ `Updated lifecycle for ${key}: stage=${toLifecycleLabel(result.knob.lifecycleStage)}, final=${formatFinalValue(result.knob)}`
307
+ );
230
308
  }
231
309
  function printKnobsHelp() {
232
310
  console.log(
@@ -240,14 +318,19 @@ function printKnobsHelp() {
240
318
  " Set a knob value",
241
319
  " delete <key> Delete a knob",
242
320
  " toggle <key> Toggle a boolean knob",
321
+ " lifecycle <key> --stage <stage> [--final-value <value>]",
322
+ " Mark knob lifecycle + final value",
243
323
  "",
244
324
  "Types: boolean, string, number, json",
325
+ "Stages: active, deprecated, ready_for_cleanup",
245
326
  "",
246
327
  "Options:",
247
- " --env <env> Target environment (prod, dev, local)",
248
- " --json Output as JSON (list/get only)",
249
- " --api-url <url> API URL override (takes precedence over --env)",
250
- " --token <key> API token override"
328
+ " --final-value <value> Final value for deprecated/ready_for_cleanup",
329
+ " --clear-final-value Clear final value (only valid with --stage active)",
330
+ " --env <env> Target environment (prod, dev, local)",
331
+ " --json Output as JSON (list/get only)",
332
+ " --api-url <url> API URL override (takes precedence over --env)",
333
+ " --token <key> API token override"
251
334
  ].join("\n")
252
335
  );
253
336
  }
@@ -274,6 +357,9 @@ async function runKnobs(argv) {
274
357
  case "toggle":
275
358
  await handleToggle(rest, apiUrl, token);
276
359
  break;
360
+ case "lifecycle":
361
+ await handleLifecycle(rest, apiUrl, token);
362
+ break;
277
363
  default:
278
364
  console.error(`Unknown sub-command: ${subCommand}`);
279
365
  printKnobsHelp();
@@ -283,4 +369,4 @@ async function runKnobs(argv) {
283
369
  export {
284
370
  runKnobs
285
371
  };
286
- //# sourceMappingURL=knobs-T3O4Z3ZB.js.map
372
+ //# sourceMappingURL=knobs-HKONHY55.js.map
@@ -0,0 +1 @@
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 { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst ENV_URLS: Record<string, string> = {\n prod: \"https://api.trycanary.ai\",\n production: \"https://api.trycanary.ai\",\n dev: \"https://api.dev.trycanary.ai\",\n local: \"http://localhost:3000\",\n};\n\ntype LifecycleStage = \"active\" | \"deprecated\" | \"ready_for_cleanup\";\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};\n\ntype KnobListResponse = {\n ok: boolean;\n knobs?: SerializedKnob[];\n error?: string;\n};\n\ntype ApiResponse = {\n ok: boolean;\n error?: string;\n knob?: SerializedKnob;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nfunction toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case \"deprecated\":\n return \"deprecated\";\n case \"ready_for_cleanup\":\n return \"ready_for_cleanup\";\n default:\n return \"active\";\n }\n}\n\nfunction parseLifecycleStage(argv: string[]): LifecycleStage {\n const stage = getArgValue(argv, \"--stage\");\n if (!stage || ![\"active\", \"deprecated\", \"ready_for_cleanup\"].includes(stage)) {\n console.error(\"Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup\");\n process.exit(1);\n }\n return stage as LifecycleStage;\n}\n\nfunction formatValue(valueType: SerializedKnob[\"valueType\"], value: unknown): string {\n if (valueType === \"json\") {\n return JSON.stringify(value);\n }\n return String(value);\n}\n\nfunction formatFinalValue(knob: SerializedKnob): string {\n if (knob.lifecycleStage === \"active\") {\n return \"(none)\";\n }\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\nasync function resolveConfig(argv: string[]) {\n const storedApiUrl = await readStoredApiUrl();\n const env = getArgValue(argv, \"--env\");\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env] : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n return { apiUrl, token };\n}\n\nasync function apiRequest(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<ApiResponse> {\n const res = await fetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as ApiResponse;\n return json;\n}\n\nasync function fetchKnobs(apiUrl: string, token: string): Promise<SerializedKnob[]> {\n const res = await fetch(`${apiUrl}/superadmin/knobs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as KnobListResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return json.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 console.log(\n ` ${knob.key} [${knob.valueType}] = ${formatValue(knob.valueType, knob.value)} (${lifecycle}${finalValue})${desc}`\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\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}\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(\n \"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\"\n );\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(\n \"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\"\n );\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(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(\n apiUrl,\n token,\n \"DELETE\",\n `/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(\n apiUrl,\n token,\n \"POST\",\n `/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(\n \"Usage: canary knobs lifecycle <key> --stage <active|deprecated|ready_for_cleanup> [--final-value <value>]\"\n );\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(\n apiUrl,\n token,\n \"POST\",\n `/superadmin/knobs/${encodeURIComponent(key)}/lifecycle`,\n {\n stage,\n finalValue,\n }\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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canaryai/cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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 { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst ENV_URLS: Record<string, string> = {\n prod: \"https://api.trycanary.ai\",\n production: \"https://api.trycanary.ai\",\n dev: \"https://api.dev.trycanary.ai\",\n local: \"http://localhost:3000\",\n};\n\ntype FeatureFlag = {\n id: string;\n name: string;\n description: string | null;\n createdAt: string;\n};\n\ntype FeatureFlagListResponse = {\n ok: boolean;\n flags?: Array<FeatureFlag & { gates?: Array<{ gateType: string; gateValue: string }> }>;\n error?: string;\n};\n\ntype ApiResponse = {\n ok: boolean;\n error?: string;\n flag?: FeatureFlag;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nasync function resolveConfig(argv: string[]) {\n const storedApiUrl = await readStoredApiUrl();\n const env = getArgValue(argv, \"--env\");\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env] : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n return { apiUrl, token };\n}\n\nasync function apiRequest(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<ApiResponse> {\n const res = await fetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as ApiResponse;\n return json;\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const res = await fetch(`${apiUrl}/superadmin/feature-flags`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as FeatureFlagListResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n const flags = json.flags ?? [];\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 gates = flag.gates ?? [];\n const gateStr =\n gates.length > 0\n ? gates.map((g) => `${g.gateType}:${g.gateValue}`).join(\", \")\n : \"(no gates)\";\n console.log(` ${flag.name} ${flag.description ?? \"\"} [${gateStr}]`);\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(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(\n apiUrl,\n token,\n \"DELETE\",\n `/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\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag enable <name> --org <orgId>\");\n process.exit(1);\n }\n\n if (!orgId) {\n console.error(\"Error: Missing --org <orgId>.\");\n console.error(\"Usage: canary feature-flag enable <name> --org <orgId>\");\n process.exit(1);\n }\n\n const result = await apiRequest(\n apiUrl,\n token,\n \"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}\n\nasync function handleDisable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag disable <name> --org <orgId>\");\n process.exit(1);\n }\n\n if (!orgId) {\n console.error(\"Error: Missing --org <orgId>.\");\n console.error(\"Usage: canary feature-flag disable <name> --org <orgId>\");\n process.exit(1);\n }\n\n const result = await apiRequest(\n apiUrl,\n token,\n \"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}\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> Enable a flag for an organization\",\n \" disable <name> --org <orgId> Disable a flag for an organization\",\n \"\",\n \"Options:\",\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 default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printFeatureFlagHelp();\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;AAMA,OAAO,aAAa;AAGpB,IAAM,WAAmC;AAAA,EACvC,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,OAAO;AACT;AAqBA,SAAS,YAAY,MAAgB,KAAiC;AACpE,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,UAAU,MAAM,SAAS,KAAK,SAAS,EAAG,QAAO;AACrD,SAAO,KAAK,QAAQ,CAAC;AACvB;AAEA,SAAS,QAAQ,SAAmB,OAA0B;AAC5D,SAAO,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC;AACjD;AAEA,eAAe,cAAc,MAAgB;AAC3C,QAAM,eAAe,MAAM,iBAAiB;AAC5C,QAAM,MAAM,YAAY,MAAM,OAAO;AAErC,MAAI,OAAO,CAAC,SAAS,GAAG,GAAG;AACzB,YAAQ,MAAM,wBAAwB,GAAG,EAAE;AAC3C,YAAQ,MAAM,sCAAsC;AACpD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SACJ,YAAY,MAAM,WAAW,MAC5B,MAAM,SAAS,GAAG,IAAI,WACvB,QAAQ,IAAI,kBACZ,gBACA;AAEF,QAAM,QACJ,YAAY,MAAM,SAAS,KAAK,QAAQ,IAAI,oBAAqB,MAAM,gBAAgB;AAEzF,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,4BAA4B;AAC1C,YAAQ,MAAM,mBAAmB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,EAAE,QAAQ,MAAM;AACzB;AAEA,eAAe,WACb,QACA,OACA,QACA,MACA,MACsB;AACtB,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI;AAAA,IAC1C;AAAA,IACA,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,GAAI,OAAO,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE,IAAI,CAAC;AAAA,EAC/C,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,MAAM,mBAAmB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,SAAO;AACT;AAEA,eAAe,WAAW,MAAgB,QAAgB,OAA8B;AACtF,QAAM,aAAa,QAAQ,MAAM,QAAQ;AACzC,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,6BAA6B;AAAA,IAC5D,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,EAC9C,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,MAAM,mBAAmB;AACjC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,MAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,MAAM,UAAU,KAAK,KAAK,EAAE;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,KAAK,SAAS,CAAC;AAE7B,MAAI,YAAY;AACd,YAAQ,IAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC1C;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAI,yBAAyB;AACrC;AAAA,EACF;AAEA,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,UAAM,UACJ,MAAM,SAAS,IACX,MAAM,IAAI,CAAC,MAAM,GAAG,EAAE,QAAQ,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,IAAI,IAC1D;AACN,YAAQ,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,eAAe,EAAE,MAAM,OAAO,GAAG;AAAA,EACvE;AACF;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,OAAO,KAAK,CAAC;AACnB,MAAI,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG;AAClC,YAAQ,MAAM,2BAA2B;AACzC,YAAQ,MAAM,iEAAiE;AAC/E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,YAAY,MAAM,eAAe,KAAK;AAC1D,QAAM,SAAS,MAAM,WAAW,QAAQ,OAAO,QAAQ,6BAA6B;AAAA,IAClF;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,CAAC,OAAO,IAAI;AACd,YAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AACtC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,yBAAyB,IAAI,EAAE;AAC7C;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,OAAO,KAAK,CAAC;AACnB,MAAI,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG;AAClC,YAAQ,MAAM,2BAA2B;AACzC,YAAQ,MAAM,0CAA0C;AACxD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,6BAA6B,mBAAmB,IAAI,CAAC;AAAA,EACvD;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,YAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AACtC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,yBAAyB,IAAI,EAAE;AAC7C;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,OAAO,KAAK,CAAC;AACnB,QAAM,QAAQ,YAAY,MAAM,OAAO;AAEvC,MAAI,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG;AAClC,YAAQ,MAAM,2BAA2B;AACzC,YAAQ,MAAM,wDAAwD;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,+BAA+B;AAC7C,YAAQ,MAAM,wDAAwD;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,6BAA6B,mBAAmB,IAAI,CAAC,kBAAkB,mBAAmB,KAAK,CAAC;AAAA,EAClG;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,YAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AACtC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,WAAW,IAAI,YAAY,KAAK,EAAE;AAChD;AAEA,eAAe,cAAc,MAAgB,QAAgB,OAA8B;AACzF,QAAM,OAAO,KAAK,CAAC;AACnB,QAAM,QAAQ,YAAY,MAAM,OAAO;AAEvC,MAAI,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG;AAClC,YAAQ,MAAM,2BAA2B;AACzC,YAAQ,MAAM,yDAAyD;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,+BAA+B;AAC7C,YAAQ,MAAM,yDAAyD;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,6BAA6B,mBAAmB,IAAI,CAAC,kBAAkB,mBAAmB,KAAK,CAAC;AAAA,EAClG;AAEA,MAAI,CAAC,OAAO,IAAI;AACd,YAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AACtC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,YAAY,IAAI,YAAY,KAAK,EAAE;AACjD;AAEA,SAAS,uBAA6B;AACpC,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAEA,eAAsB,eAAe,MAA+B;AAClE,QAAM,CAAC,YAAY,GAAG,IAAI,IAAI;AAE9B,MAAI,CAAC,cAAc,eAAe,UAAU,QAAQ,MAAM,UAAU,IAAI,GAAG;AACzE,yBAAqB;AACrB;AAAA,EACF;AAEA,QAAM,EAAE,QAAQ,MAAM,IAAI,MAAM,cAAc,IAAI;AAElD,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,YAAM,WAAW,MAAM,QAAQ,KAAK;AACpC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,cAAc,MAAM,QAAQ,KAAK;AACvC;AAAA,IACF;AACE,cAAQ,MAAM,wBAAwB,UAAU,EAAE;AAClD,2BAAqB;AACrB,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;","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 { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst ENV_URLS: Record<string, string> = {\n prod: \"https://api.trycanary.ai\",\n production: \"https://api.trycanary.ai\",\n dev: \"https://api.dev.trycanary.ai\",\n local: \"http://localhost:3000\",\n};\n\ntype SerializedKnob = {\n key: string;\n description: string | null;\n valueType: string;\n value: unknown;\n updatedAt: string | null;\n updatedBy: string | null;\n createdAt: string | null;\n};\n\ntype KnobListResponse = {\n ok: boolean;\n knobs?: SerializedKnob[];\n error?: string;\n};\n\ntype ApiResponse = {\n ok: boolean;\n error?: string;\n knob?: SerializedKnob;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nasync function resolveConfig(argv: string[]) {\n const storedApiUrl = await readStoredApiUrl();\n const env = getArgValue(argv, \"--env\");\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env] : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n return { apiUrl, token };\n}\n\nasync function apiRequest(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<ApiResponse> {\n const res = await fetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as ApiResponse;\n return json;\n}\n\nfunction formatValue(knob: SerializedKnob): string {\n if (knob.valueType === \"json\") {\n return JSON.stringify(knob.value);\n }\n return String(knob.value);\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const res = await fetch(`${apiUrl}/superadmin/knobs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as KnobListResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n const knobs = json.knobs ?? [];\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 console.log(` ${knob.key} [${knob.valueType}] = ${formatValue(knob)}${desc}`);\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 res = await fetch(`${apiUrl}/superadmin/knobs`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as KnobListResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n const knob = (json.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)}`);\n console.log(` Description: ${knob.description ?? \"(none)\"}`);\n console.log(` Updated: ${knob.updatedAt ?? \"(never)\"}`);\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(\n \"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\"\n );\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(\n \"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]\"\n );\n process.exit(1);\n }\n\n const valueType = getArgValue(argv, \"--type\");\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 let value: unknown;\n switch (valueType) {\n case \"boolean\":\n if (rawValue !== \"true\" && rawValue !== \"false\") {\n console.error('Error: Boolean value must be \"true\" or \"false\".');\n process.exit(1);\n }\n value = rawValue === \"true\";\n break;\n case \"string\":\n value = rawValue;\n break;\n case \"number\": {\n const num = parseFloat(rawValue);\n if (Number.isNaN(num)) {\n console.error(`Error: Invalid number: ${rawValue}`);\n process.exit(1);\n }\n value = num;\n break;\n }\n case \"json\":\n try {\n value = JSON.parse(rawValue);\n } catch {\n console.error(`Error: Invalid JSON: ${rawValue}`);\n process.exit(1);\n }\n break;\n }\n\n const description = getArgValue(argv, \"--description\") ?? undefined;\n\n const result = await apiRequest(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(result.knob!)}`);\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(\n apiUrl,\n token,\n \"DELETE\",\n `/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(\n apiUrl,\n token,\n \"POST\",\n `/superadmin/knobs/${encodeURIComponent(key)}/toggle`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Toggled knob: ${key} → ${result.knob?.value}`);\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 \"\",\n \"Types: boolean, string, number, json\",\n \"\",\n \"Options:\",\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 default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printKnobsHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}