@canaryai/cli 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/{chunk-2T64Z2NI.js → chunk-7R4YFGP6.js} +53 -2
  2. package/dist/chunk-7R4YFGP6.js.map +1 -0
  3. package/dist/chunk-DXJNFJ3A.js +64 -0
  4. package/dist/chunk-DXJNFJ3A.js.map +1 -0
  5. package/dist/{chunk-V7U52ISX.js → chunk-HOYYXZPV.js} +136 -131
  6. package/dist/chunk-HOYYXZPV.js.map +1 -0
  7. package/dist/{chunk-ROTCL5WO.js → chunk-TO66FC4R.js} +688 -479
  8. package/dist/chunk-TO66FC4R.js.map +1 -0
  9. package/dist/{feature-flag-ESPSOSKG.js → feature-flag-ZDLDYRSF.js} +15 -92
  10. package/dist/feature-flag-ZDLDYRSF.js.map +1 -0
  11. package/dist/index.js +17 -65
  12. package/dist/index.js.map +1 -1
  13. package/dist/{knobs-HKONHY55.js → knobs-3MKMOXIV.js} +19 -104
  14. package/dist/knobs-3MKMOXIV.js.map +1 -0
  15. package/dist/{local-browser-MKKPBTYI.js → local-browser-GG5GUXDS.js} +2 -2
  16. package/dist/{mcp-4F4HI7L2.js → mcp-AD67OLQM.js} +3 -3
  17. package/dist/{psql-6IFVXM3A.js → psql-IVAPNYZV.js} +2 -2
  18. package/dist/{redis-HZC32IEO.js → redis-LWY7L6AS.js} +2 -2
  19. package/dist/{release-WOD3DAX4.js → release-KQFCTAXA.js} +5 -35
  20. package/dist/release-KQFCTAXA.js.map +1 -0
  21. package/dist/runner/preload.js +7 -323
  22. package/dist/runner/preload.js.map +1 -1
  23. package/dist/test.js +5 -340
  24. package/dist/test.js.map +1 -1
  25. package/package.json +1 -1
  26. package/dist/chunk-2T64Z2NI.js.map +0 -1
  27. package/dist/chunk-ROTCL5WO.js.map +0 -1
  28. package/dist/chunk-V7U52ISX.js.map +0 -1
  29. package/dist/feature-flag-ESPSOSKG.js.map +0 -1
  30. package/dist/knobs-HKONHY55.js.map +0 -1
  31. package/dist/release-WOD3DAX4.js.map +0 -1
  32. /package/dist/{local-browser-MKKPBTYI.js.map → local-browser-GG5GUXDS.js.map} +0 -0
  33. /package/dist/{mcp-4F4HI7L2.js.map → mcp-AD67OLQM.js.map} +0 -0
  34. /package/dist/{psql-6IFVXM3A.js.map → psql-IVAPNYZV.js.map} +0 -0
  35. /package/dist/{redis-HZC32IEO.js.map → redis-LWY7L6AS.js.map} +0 -0
@@ -2,8 +2,24 @@
2
2
  import fs from "fs/promises";
3
3
  import os from "os";
4
4
  import path from "path";
5
+ import process from "process";
5
6
  var AUTH_DIR = path.join(os.homedir(), ".config", "canary-cli");
6
7
  var AUTH_FILE = path.join(AUTH_DIR, "auth.json");
8
+ function getArgValue(argv, key) {
9
+ const index = argv.indexOf(key);
10
+ if (index === -1 || index >= argv.length - 1) return void 0;
11
+ return argv[index + 1];
12
+ }
13
+ function hasFlag(argv, ...flags) {
14
+ return flags.some((flag) => argv.includes(flag));
15
+ }
16
+ function envToProfile(env) {
17
+ if (env === "prod" || env === "production") return "production";
18
+ if (env === "dev") return "dev";
19
+ if (env === "local") return "local";
20
+ return env;
21
+ }
22
+ var VALID_ENVS = ["prod", "dev", "local"];
7
23
  var ENV_URLS = {
8
24
  prod: {
9
25
  api: "https://api.trycanary.ai",
@@ -85,13 +101,48 @@ async function saveAuth(auth, profile) {
85
101
  await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: "utf8", mode: 384 });
86
102
  return AUTH_FILE;
87
103
  }
104
+ async function resolveConfig(argv) {
105
+ const env = getArgValue(argv, "--env");
106
+ const profile = env ? envToProfile(env) : void 0;
107
+ if (env && !ENV_URLS[env]) {
108
+ console.error(`Unknown environment: ${env}`);
109
+ console.error(`Valid environments: ${VALID_ENVS.join(", ")}`);
110
+ process.exit(1);
111
+ }
112
+ const storedApiUrl = await readStoredApiUrl(profile);
113
+ const apiUrl = getArgValue(argv, "--api-url") ?? (env ? ENV_URLS[env].api : void 0) ?? process.env.CANARY_API_URL ?? storedApiUrl ?? "https://api.trycanary.ai";
114
+ const explicitToken = getArgValue(argv, "--token");
115
+ let token;
116
+ if (explicitToken) {
117
+ token = explicitToken;
118
+ } else if (profile) {
119
+ token = await readStoredToken(profile) ?? process.env.CANARY_API_TOKEN ?? null;
120
+ if (!token) {
121
+ console.error(`Error: No token found for "${env}" profile.`);
122
+ console.error(`Run: canary login --env ${env}`);
123
+ process.exit(1);
124
+ }
125
+ } else {
126
+ token = process.env.CANARY_API_TOKEN ?? await readStoredToken() ?? null;
127
+ if (!token) {
128
+ console.error("Error: No API token found.");
129
+ console.error("Run: canary login");
130
+ process.exit(1);
131
+ }
132
+ }
133
+ return { apiUrl, token };
134
+ }
88
135
 
89
136
  export {
137
+ getArgValue,
138
+ hasFlag,
139
+ envToProfile,
90
140
  ENV_URLS,
91
141
  readStoredAuth,
92
142
  readStoredToken,
93
143
  readStoredApiUrl,
94
144
  readAllStoredTokens,
95
- saveAuth
145
+ saveAuth,
146
+ resolveConfig
96
147
  };
97
- //# sourceMappingURL=chunk-2T64Z2NI.js.map
148
+ //# sourceMappingURL=chunk-7R4YFGP6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport process from \"node:process\";\n\ntype StoredAuth = {\n token?: string;\n apiUrl?: string;\n orgId?: string;\n orgName?: string;\n};\n\n/** Multi-profile auth file format */\ntype MultiProfileAuth = {\n profiles: Record<string, StoredAuth>;\n default: string;\n};\n\n/** Legacy flat format (single profile) */\ntype LegacyAuth = StoredAuth;\n\nconst AUTH_DIR = path.join(os.homedir(), \".config\", \"canary-cli\");\nconst AUTH_FILE = path.join(AUTH_DIR, \"auth.json\");\n\n/* ── Shared argv helpers ─────────────────────────────────────────────── */\n\nexport function 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\nexport function hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\n/* ── Environment / profile mapping ───────────────────────────────────── */\n\n/** Map --env values to profile names for multi-profile auth storage */\nexport function 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\nconst VALID_ENVS = [\"prod\", \"dev\", \"local\"];\n\n/** Environment URL mappings shared across CLI commands */\nexport const 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 isMultiProfile(data: unknown): data is MultiProfileAuth {\n return typeof data === \"object\" && data !== null && \"profiles\" in data;\n}\n\n/** Read the raw auth file and normalize to multi-profile format */\nasync function readAuthFile(): Promise<MultiProfileAuth | null> {\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n return data;\n }\n\n // Legacy flat format — treat as the \"default\" profile\n const legacy = data as LegacyAuth;\n return {\n profiles: { default: legacy },\n default: \"default\",\n };\n } catch {\n return null;\n }\n}\n\nexport async function readStoredAuth(profile?: string): Promise<StoredAuth | null> {\n const auth = await readAuthFile();\n if (!auth) return null;\n\n const key = profile ?? auth.default;\n return auth.profiles[key] ?? null;\n}\n\nexport async function readStoredToken(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.token ?? null;\n}\n\nexport async function readStoredApiUrl(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.apiUrl ?? null;\n}\n\n/** Return all stored tokens across every profile */\nexport async function readAllStoredTokens(): Promise<string[]> {\n const auth = await readAuthFile();\n if (!auth) return [];\n return Object.values(auth.profiles)\n .map((p) => p.token)\n .filter((t): t is string => !!t);\n}\n\nexport async function saveAuth(auth: StoredAuth, profile?: string): Promise<string> {\n await fs.mkdir(AUTH_DIR, { recursive: true, mode: 0o700 });\n\n // Read existing file (may be legacy or multi-profile)\n let existing: MultiProfileAuth;\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n existing = data;\n } else {\n // Migrate legacy flat format into profiles.default\n existing = {\n profiles: { default: data as LegacyAuth },\n default: \"default\",\n };\n }\n } catch {\n existing = { profiles: {}, default: \"default\" };\n }\n\n const key = profile ?? \"default\";\n existing.profiles[key] = auth;\n\n // Set default to this profile if no default exists yet\n if (!existing.profiles[existing.default]) {\n existing.default = key;\n }\n\n await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: \"utf8\", mode: 0o600 });\n return AUTH_FILE;\n}\n\n/* ── Shared config resolution ────────────────────────────────────────── */\n\nexport type ResolvedConfig = { apiUrl: string; token: string };\n\n/**\n * Resolve API URL and auth token from argv flags, env vars, and stored profiles.\n * Used by knobs, feature-flag, release, and debug-session commands.\n */\nexport async function resolveConfig(argv: string[]): Promise<ResolvedConfig> {\n const env = getArgValue(argv, \"--env\");\n const profile = env ? envToProfile(env) : undefined;\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(`Valid environments: ${VALID_ENVS.join(\", \")}`);\n process.exit(1);\n }\n\n const storedApiUrl = await readStoredApiUrl(profile);\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env].api : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n // Explicit --token flag always wins\n const explicitToken = getArgValue(argv, \"--token\");\n\n let token: string | null;\n if (explicitToken) {\n token = explicitToken;\n } else if (profile) {\n // --env was specified: profile token takes priority, env var is fallback\n token = (await readStoredToken(profile)) ?? process.env.CANARY_API_TOKEN ?? null;\n if (!token) {\n console.error(`Error: No token found for \"${env}\" profile.`);\n console.error(`Run: canary login --env ${env}`);\n process.exit(1);\n }\n } else {\n // No --env: env var first, then default profile\n token = process.env.CANARY_API_TOKEN ?? (await readStoredToken()) ?? null;\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n }\n\n return { apiUrl, token };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,64 @@
1
+ import {
2
+ getArgValue
3
+ } from "./chunk-7R4YFGP6.js";
4
+
5
+ // src/cli-helpers.ts
6
+ import process from "process";
7
+ function toLifecycleLabel(stage) {
8
+ switch (stage) {
9
+ case "deprecated":
10
+ return "deprecated";
11
+ case "ready_for_cleanup":
12
+ return "ready_for_cleanup";
13
+ default:
14
+ return "active";
15
+ }
16
+ }
17
+ function parseLifecycleStage(argv) {
18
+ const stage = getArgValue(argv, "--stage");
19
+ if (!stage || !["active", "deprecated", "ready_for_cleanup"].includes(stage)) {
20
+ console.error("Error: --stage is required and must be one of: active, deprecated, ready_for_cleanup");
21
+ process.exit(1);
22
+ }
23
+ return stage;
24
+ }
25
+ async function apiRequest(apiUrl, token, method, path, body) {
26
+ const res = await fetch(`${apiUrl}${path}`, {
27
+ method,
28
+ headers: {
29
+ Authorization: `Bearer ${token}`,
30
+ "Content-Type": "application/json"
31
+ },
32
+ ...body ? { body: JSON.stringify(body) } : {}
33
+ });
34
+ if (res.status === 401) {
35
+ console.error("Error: Unauthorized. Your session may have expired.");
36
+ console.error("Run: canary login");
37
+ process.exit(1);
38
+ }
39
+ return await res.json();
40
+ }
41
+ async function fetchList(apiUrl, token, path, listKey) {
42
+ const res = await fetch(`${apiUrl}${path}`, {
43
+ headers: { Authorization: `Bearer ${token}` }
44
+ });
45
+ if (res.status === 401) {
46
+ console.error("Error: Unauthorized. Your session may have expired.");
47
+ console.error("Run: canary login");
48
+ process.exit(1);
49
+ }
50
+ const json = await res.json();
51
+ if (!json.ok) {
52
+ console.error(`Error: ${json.error}`);
53
+ process.exit(1);
54
+ }
55
+ return json[listKey] ?? [];
56
+ }
57
+
58
+ export {
59
+ toLifecycleLabel,
60
+ parseLifecycleStage,
61
+ apiRequest,
62
+ fetchList
63
+ };
64
+ //# sourceMappingURL=chunk-DXJNFJ3A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-helpers.ts"],"sourcesContent":["/**\n * Shared CLI helpers for superadmin management commands (knobs, feature-flags).\n */\n\nimport process from \"node:process\";\nimport { getArgValue } from \"./auth.js\";\n\nexport type LifecycleStage = \"active\" | \"deprecated\" | \"ready_for_cleanup\";\n\nexport function toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case \"deprecated\":\n return \"deprecated\";\n case \"ready_for_cleanup\":\n return \"ready_for_cleanup\";\n default:\n return \"active\";\n }\n}\n\nexport function parseLifecycleStage(argv: string[]): LifecycleStage {\n const stage = getArgValue(argv, \"--stage\");\n if (!stage || ![\"active\", \"deprecated\", \"ready_for_cleanup\"].includes(stage)) {\n console.error(\"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\nexport async function apiRequest<T extends { ok: boolean; error?: string }>(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<T> {\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 return (await res.json()) as T;\n}\n\nexport async function fetchList<T>(\n apiUrl: string,\n token: string,\n path: string,\n listKey: string\n): Promise<T[]> {\n const res = await fetch(`${apiUrl}${path}`, {\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 Record<string, unknown>;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return (json[listKey] as T[]) ?? [];\n}\n"],"mappings":";;;;;AAIA,OAAO,aAAa;AAKb,SAAS,iBAAiB,OAA+B;AAC9D,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,oBAAoB,MAAgC;AAClE,QAAM,QAAQ,YAAY,MAAM,SAAS;AACzC,MAAI,CAAC,SAAS,CAAC,CAAC,UAAU,cAAc,mBAAmB,EAAE,SAAS,KAAK,GAAG;AAC5E,YAAQ,MAAM,sFAAsF;AACpG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAsB,WACpB,QACA,OACA,QACA,MACA,MACY;AACZ,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,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAsB,UACpB,QACA,OACA,MACA,SACc;AACd,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI;AAAA,IAC1C,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,SAAQ,KAAK,OAAO,KAAa,CAAC;AACpC;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readStoredToken
3
- } from "./chunk-2T64Z2NI.js";
3
+ } from "./chunk-7R4YFGP6.js";
4
4
 
5
5
  // src/local-run.ts
6
6
  import process from "process";
@@ -99,11 +99,7 @@ async function runTunnel(argv) {
99
99
  let reconnectAttempts = 0;
100
100
  const connect = async () => {
101
101
  try {
102
- const data = await createTunnel({
103
- apiUrl,
104
- token,
105
- port
106
- });
102
+ const data = await createTunnel({ apiUrl, token, port });
107
103
  console.log(`Tunnel connected: ${data.publicUrl ?? data.tunnelId}`);
108
104
  if (data.publicUrl) {
109
105
  console.log(`Public URL: ${data.publicUrl}`);
@@ -174,13 +170,128 @@ async function createTunnel(input) {
174
170
  }
175
171
  return { tunnelId: data.tunnelId, publicUrl: data.publicUrl, token: data.token };
176
172
  }
173
+ function handleHttpRequest(ctx, request) {
174
+ const targetUrl = `http://localhost:${ctx.port}${request.path.startsWith("/") ? request.path : `/${request.path}`}`;
175
+ const body = request.bodyBase64 ? Buffer.from(request.bodyBase64, "base64") : void 0;
176
+ const headers = { ...request.headers };
177
+ delete headers.host;
178
+ delete headers["content-length"];
179
+ fetch(targetUrl, { method: request.method, headers, body: body ?? void 0 }).then(async (res) => {
180
+ const resBody = await res.arrayBuffer();
181
+ const resHeaders = Object.fromEntries(res.headers.entries());
182
+ delete resHeaders["set-cookie"];
183
+ const getSetCookie = res.headers.getSetCookie;
184
+ const setCookieValues = typeof getSetCookie === "function" ? getSetCookie.call(res.headers) : [];
185
+ const fallbackSetCookie = res.headers.get("set-cookie");
186
+ if (setCookieValues.length === 0 && fallbackSetCookie) {
187
+ setCookieValues.push(fallbackSetCookie);
188
+ }
189
+ if (setCookieValues.length > 0) {
190
+ resHeaders["set-cookie"] = setCookieValues;
191
+ }
192
+ const responsePayload = {
193
+ type: "http_response",
194
+ id: request.id,
195
+ status: res.status,
196
+ headers: resHeaders,
197
+ bodyBase64: resBody.byteLength ? Buffer.from(resBody).toString("base64") : null
198
+ };
199
+ ctx.ws.send(JSON.stringify(responsePayload));
200
+ }).catch((error) => {
201
+ const responsePayload = {
202
+ type: "http_response",
203
+ id: request.id,
204
+ status: 502,
205
+ headers: { "content-type": "text/plain" },
206
+ bodyBase64: Buffer.from(`Tunnel error: ${String(error)}`).toString("base64")
207
+ };
208
+ ctx.ws.send(JSON.stringify(responsePayload));
209
+ });
210
+ }
211
+ function handleWsOpen(ctx, request) {
212
+ const targetUrl = `ws://localhost:${ctx.port}${request.path.startsWith("/") ? request.path : `/${request.path}`}`;
213
+ const protocolsHeader = request.headers["sec-websocket-protocol"] ?? request.headers["Sec-WebSocket-Protocol"];
214
+ const protocols = protocolsHeader ? protocolsHeader.split(",").map((v) => v.trim()).filter(Boolean) : void 0;
215
+ const localWs = new WebSocket(targetUrl, protocols);
216
+ ctx.wsConnections.set(request.id, localWs);
217
+ localWs.onopen = () => {
218
+ ctx.ws.send(JSON.stringify({ type: "ws_ready", id: request.id }));
219
+ const queued = ctx.wsQueues.get(request.id);
220
+ if (queued) {
221
+ for (const message of queued) {
222
+ ctx.ws.send(JSON.stringify(message));
223
+ }
224
+ ctx.wsQueues.delete(request.id);
225
+ }
226
+ };
227
+ localWs.onmessage = (event) => {
228
+ const data = typeof event.data === "string" ? Buffer.from(event.data) : Buffer.from(event.data);
229
+ const response = {
230
+ type: "ws_message",
231
+ id: request.id,
232
+ dataBase64: data.toString("base64"),
233
+ isBinary: typeof event.data !== "string"
234
+ };
235
+ ctx.ws.send(JSON.stringify(response));
236
+ };
237
+ localWs.onclose = (event) => {
238
+ ctx.wsConnections.delete(request.id);
239
+ const response = {
240
+ type: "ws_close",
241
+ id: request.id,
242
+ code: event.code,
243
+ reason: event.reason
244
+ };
245
+ ctx.ws.send(JSON.stringify(response));
246
+ };
247
+ localWs.onerror = () => {
248
+ ctx.wsConnections.delete(request.id);
249
+ const response = {
250
+ type: "ws_close",
251
+ id: request.id,
252
+ code: 1011,
253
+ reason: "local_ws_error"
254
+ };
255
+ ctx.ws.send(JSON.stringify(response));
256
+ };
257
+ }
258
+ function handleWsMessage(ctx, message) {
259
+ const localWs = ctx.wsConnections.get(message.id);
260
+ const data = Buffer.from(message.dataBase64, "base64");
261
+ if (!localWs || localWs.readyState !== WebSocket.OPEN) {
262
+ const queued = ctx.wsQueues.get(message.id) ?? [];
263
+ queued.push(message);
264
+ ctx.wsQueues.set(message.id, queued);
265
+ return;
266
+ }
267
+ if (message.isBinary) {
268
+ localWs.send(data);
269
+ } else {
270
+ localWs.send(data.toString());
271
+ }
272
+ }
273
+ function handleWsClose(ctx, message) {
274
+ const localWs = ctx.wsConnections.get(message.id);
275
+ if (!localWs) {
276
+ const queued = ctx.wsQueues.get(message.id) ?? [];
277
+ queued.push(message);
278
+ ctx.wsQueues.set(message.id, queued);
279
+ return;
280
+ }
281
+ localWs.close(message.code ?? 1e3, message.reason ?? "");
282
+ ctx.wsConnections.delete(message.id);
283
+ }
177
284
  function connectTunnel(input) {
178
285
  const wsUrl = toWebSocketUrl(
179
286
  `${input.apiUrl}/local-tests/tunnels/${input.tunnelId}/connect?token=${input.token}`
180
287
  );
181
288
  const ws = new WebSocket(wsUrl);
182
- const wsConnections = /* @__PURE__ */ new Map();
183
- const wsQueues = /* @__PURE__ */ new Map();
289
+ const ctx = {
290
+ ws,
291
+ port: input.port,
292
+ wsConnections: /* @__PURE__ */ new Map(),
293
+ wsQueues: /* @__PURE__ */ new Map()
294
+ };
184
295
  ws.onopen = () => {
185
296
  input.onReady?.();
186
297
  };
@@ -194,128 +305,22 @@ function connectTunnel(input) {
194
305
  try {
195
306
  const raw = typeof event.data === "string" ? event.data : Buffer.from(event.data).toString();
196
307
  const payload = JSON.parse(raw);
197
- if (payload.type === "http_request") {
198
- const request = payload;
199
- const targetUrl = `http://localhost:${input.port}${request.path.startsWith("/") ? request.path : `/${request.path}`}`;
200
- const body = request.bodyBase64 ? Buffer.from(request.bodyBase64, "base64") : void 0;
201
- const headers = { ...request.headers };
202
- delete headers.host;
203
- delete headers["content-length"];
204
- try {
205
- const res = await fetch(targetUrl, {
206
- method: request.method,
207
- headers,
208
- body: body ?? void 0
209
- });
210
- const resBody = await res.arrayBuffer();
211
- const resHeaders = Object.fromEntries(res.headers.entries());
212
- delete resHeaders["set-cookie"];
213
- const getSetCookie = res.headers.getSetCookie;
214
- const setCookieValues = typeof getSetCookie === "function" ? getSetCookie.call(res.headers) : [];
215
- const fallbackSetCookie = res.headers.get("set-cookie");
216
- if (setCookieValues.length === 0 && fallbackSetCookie) {
217
- setCookieValues.push(fallbackSetCookie);
218
- }
219
- if (setCookieValues.length > 0) {
220
- resHeaders["set-cookie"] = setCookieValues;
221
- }
222
- const responsePayload = {
223
- type: "http_response",
224
- id: request.id,
225
- status: res.status,
226
- headers: resHeaders,
227
- bodyBase64: resBody.byteLength ? Buffer.from(resBody).toString("base64") : null
228
- };
229
- ws.send(JSON.stringify(responsePayload));
230
- } catch (error) {
231
- const responsePayload = {
232
- type: "http_response",
233
- id: request.id,
234
- status: 502,
235
- headers: { "content-type": "text/plain" },
236
- bodyBase64: Buffer.from(`Tunnel error: ${String(error)}`).toString("base64")
237
- };
238
- ws.send(JSON.stringify(responsePayload));
239
- }
240
- }
241
- if (payload.type === "ws_open") {
242
- const request = payload;
243
- const targetUrl = `ws://localhost:${input.port}${request.path.startsWith("/") ? request.path : `/${request.path}`}`;
244
- const protocolsHeader = request.headers["sec-websocket-protocol"] ?? request.headers["Sec-WebSocket-Protocol"];
245
- const protocols = protocolsHeader ? protocolsHeader.split(",").map((value) => value.trim()).filter(Boolean) : void 0;
246
- const localWs = new WebSocket(targetUrl, protocols);
247
- wsConnections.set(request.id, localWs);
248
- localWs.onopen = () => {
249
- ws.send(JSON.stringify({ type: "ws_ready", id: request.id }));
250
- const queued = wsQueues.get(request.id);
251
- if (queued) {
252
- for (const message of queued) {
253
- ws.send(JSON.stringify(message));
254
- }
255
- wsQueues.delete(request.id);
256
- }
257
- };
258
- localWs.onmessage = (event2) => {
259
- const data = typeof event2.data === "string" ? Buffer.from(event2.data) : Buffer.from(event2.data);
260
- const response = {
261
- type: "ws_message",
262
- id: request.id,
263
- dataBase64: data.toString("base64"),
264
- isBinary: typeof event2.data !== "string"
265
- };
266
- ws.send(JSON.stringify(response));
267
- };
268
- localWs.onclose = (event2) => {
269
- wsConnections.delete(request.id);
270
- const response = {
271
- type: "ws_close",
272
- id: request.id,
273
- code: event2.code,
274
- reason: event2.reason
275
- };
276
- ws.send(JSON.stringify(response));
277
- };
278
- localWs.onerror = () => {
279
- wsConnections.delete(request.id);
280
- const response = {
281
- type: "ws_close",
282
- id: request.id,
283
- code: 1011,
284
- reason: "local_ws_error"
285
- };
286
- ws.send(JSON.stringify(response));
287
- };
288
- }
289
- if (payload.type === "ws_message") {
290
- const message = payload;
291
- const localWs = wsConnections.get(message.id);
292
- const data = Buffer.from(message.dataBase64, "base64");
293
- if (!localWs || localWs.readyState !== WebSocket.OPEN) {
294
- const queued = wsQueues.get(message.id) ?? [];
295
- queued.push(message);
296
- wsQueues.set(message.id, queued);
297
- return;
298
- }
299
- if (message.isBinary) {
300
- localWs.send(data);
301
- } else {
302
- localWs.send(data.toString());
303
- }
304
- }
305
- if (payload.type === "ws_close") {
306
- const message = payload;
307
- const localWs = wsConnections.get(message.id);
308
- if (!localWs) {
309
- const queued = wsQueues.get(message.id) ?? [];
310
- queued.push(message);
311
- wsQueues.set(message.id, queued);
312
- return;
313
- }
314
- localWs.close(message.code ?? 1e3, message.reason ?? "");
315
- wsConnections.delete(message.id);
316
- }
317
- if (payload.type === "health_ping") {
318
- ws.send(JSON.stringify({ type: "health_pong" }));
308
+ switch (payload.type) {
309
+ case "http_request":
310
+ handleHttpRequest(ctx, payload);
311
+ break;
312
+ case "ws_open":
313
+ handleWsOpen(ctx, payload);
314
+ break;
315
+ case "ws_message":
316
+ handleWsMessage(ctx, payload);
317
+ break;
318
+ case "ws_close":
319
+ handleWsClose(ctx, payload);
320
+ break;
321
+ case "health_ping":
322
+ ws.send(JSON.stringify({ type: "health_pong" }));
323
+ break;
319
324
  }
320
325
  } catch (error) {
321
326
  console.error("Tunnel message error", error);
@@ -331,4 +336,4 @@ export {
331
336
  createTunnel,
332
337
  connectTunnel
333
338
  };
334
- //# sourceMappingURL=chunk-V7U52ISX.js.map
339
+ //# sourceMappingURL=chunk-HOYYXZPV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/local-run.ts","../src/tunnel.ts"],"sourcesContent":["import process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\n\ntype LocalRunOptions = {\n apiUrl: string;\n token?: string;\n title?: string;\n featureSpec?: string;\n startUrl?: string;\n tunnelUrl?: string;\n};\n\ntype LocalRunResponse = {\n ok: boolean;\n runId?: string;\n watchUrl?: string;\n error?: string;\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\nexport async function runLocalTest(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 const title = getArgValue(argv, \"--title\");\n const featureSpec = getArgValue(argv, \"--feature\");\n const startUrl = getArgValue(argv, \"--start-url\");\n const tunnelUrl = getArgValue(argv, \"--tunnel-url\");\n\n if (!tunnelUrl && !startUrl) {\n console.error(\"Missing --tunnel-url or --start-url\");\n process.exit(1);\n }\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 result = await createLocalRun({\n apiUrl,\n token,\n title,\n featureSpec,\n startUrl,\n tunnelUrl,\n });\n\n console.log(`Local test queued: ${result.runId}`);\n if (result.watchUrl) {\n console.log(`Watch: ${result.watchUrl}`);\n }\n}\n\nexport async function createLocalRun(input: {\n apiUrl: string;\n token: string;\n title?: string;\n featureSpec?: string;\n startUrl?: string;\n tunnelUrl?: string;\n}): Promise<{ runId: string; watchUrl?: string }> {\n const body = {\n title: input.title ?? null,\n featureSpec: input.featureSpec ?? null,\n startUrl: input.startUrl ?? null,\n tunnelPublicUrl: input.tunnelUrl ?? null,\n };\n\n const response = await fetch(`${input.apiUrl}/local-tests/runs`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${input.token}`,\n },\n body: JSON.stringify(body),\n });\n\n const json = (await response.json()) as LocalRunResponse;\n if (!response.ok || !json.ok || !json.runId) {\n throw new Error(json.error ?? response.statusText);\n }\n\n return { runId: json.runId, watchUrl: json.watchUrl };\n}\n","import { createHash } from \"node:crypto\";\nimport os from \"node:os\";\nimport process from \"node:process\";\nimport { readStoredToken } from \"./auth\";\n\nexport type CreateTunnelResponse = {\n ok: boolean;\n tunnelId?: string;\n publicUrl?: string;\n token?: string;\n error?: string;\n};\n\ntype ProxyRequest = {\n type: \"http_request\";\n id: string;\n method: string;\n path: string;\n headers: Record<string, string>;\n bodyBase64?: string | null;\n};\n\ntype ProxyResponse = {\n type: \"http_response\";\n id: string;\n status: number;\n headers?: Record<string, string | string[]>;\n bodyBase64?: string | null;\n};\n\ntype WsOpen = {\n type: \"ws_open\";\n id: string;\n path: string;\n headers: Record<string, string>;\n};\n\ntype WsReady = {\n type: \"ws_ready\";\n id: string;\n};\n\ntype WsMessage = {\n type: \"ws_message\";\n id: string;\n dataBase64: string;\n isBinary: boolean;\n};\n\ntype WsClose = {\n type: \"ws_close\";\n id: string;\n code?: number;\n reason?: string;\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 toWebSocketUrl(apiUrl: string): string {\n const url = new URL(apiUrl);\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return url.toString();\n}\n\nfunction createFingerprint(): string {\n const raw = `${os.hostname()}-${os.userInfo().username}-${process.version}`;\n return createHash(\"sha256\").update(raw).digest(\"hex\").slice(0, 16);\n}\n\nexport async function runTunnel(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 const portRaw = getArgValue(argv, \"--port\") ?? process.env.CANARY_LOCAL_PORT;\n\n if (!portRaw) {\n console.error(\"Missing --port\");\n process.exit(1);\n }\n\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 if (!token) {\n console.error(\"Missing token. Run `canary login` first or set CANARY_API_TOKEN.\");\n process.exit(1);\n }\n\n const maxReconnectAttempts = 10;\n const baseReconnectDelayMs = 1000;\n let reconnectAttempts = 0;\n\n const connect = async (): Promise<void> => {\n try {\n const data = await createTunnel({ apiUrl, token, port });\n\n console.log(`Tunnel connected: ${data.publicUrl ?? data.tunnelId}`);\n if (data.publicUrl) {\n console.log(`Public URL: ${data.publicUrl}`);\n console.log(\"\");\n console.log(\"To use this tunnel for sandbox agent callbacks, add to apps/api/.env:\");\n console.log(` SANDBOX_AGENT_API_URL=${data.publicUrl}`);\n console.log(\"\");\n }\n\n const ws = connectTunnel({\n apiUrl,\n tunnelId: data.tunnelId,\n token: data.token,\n port,\n onReady: () => {\n reconnectAttempts = 0;\n },\n });\n\n return new Promise<void>((resolve, reject) => {\n ws.onclose = (event) => {\n console.log(`Tunnel closed (code: ${event.code})`);\n\n if (reconnectAttempts < maxReconnectAttempts) {\n const delay = Math.min(baseReconnectDelayMs * Math.pow(2, reconnectAttempts), 30000);\n reconnectAttempts++;\n console.log(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})...`);\n setTimeout(() => {\n connect().then(resolve).catch(reject);\n }, delay);\n } else {\n console.error(\"Max reconnection attempts reached. Exiting.\");\n process.exit(1);\n }\n };\n\n ws.onerror = (event) => {\n console.error(\"Tunnel error:\", event);\n };\n });\n } catch (error) {\n if (reconnectAttempts < maxReconnectAttempts) {\n const delay = Math.min(baseReconnectDelayMs * Math.pow(2, reconnectAttempts), 30000);\n reconnectAttempts++;\n console.error(`Failed to create tunnel: ${error}`);\n console.log(`Retrying in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})...`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n return connect();\n } else {\n console.error(\"Max reconnection attempts reached. Exiting.\");\n process.exit(1);\n }\n }\n };\n\n await connect();\n}\n\nexport async function createTunnel(input: {\n apiUrl: string;\n token: string;\n port: number;\n}): Promise<{ tunnelId: string; publicUrl?: string; token: string }> {\n const response = await fetch(`${input.apiUrl}/local-tests/tunnels`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${input.token}`,\n },\n body: JSON.stringify({\n requestedPort: input.port,\n clientFingerprint: createFingerprint(),\n }),\n });\n\n const data = (await response.json()) as CreateTunnelResponse;\n if (!response.ok || !data.ok || !data.tunnelId || !data.token) {\n throw new Error(data.error ?? response.statusText);\n }\n\n return { tunnelId: data.tunnelId, publicUrl: data.publicUrl, token: data.token };\n}\n\n// === Tunnel Message Handlers ===\n\ntype TunnelContext = {\n ws: WebSocket;\n port: number;\n wsConnections: Map<string, WebSocket>;\n wsQueues: Map<string, Array<WsMessage | WsClose>>;\n};\n\nfunction handleHttpRequest(ctx: TunnelContext, request: ProxyRequest): void {\n const targetUrl = `http://localhost:${ctx.port}${\n request.path.startsWith(\"/\") ? request.path : `/${request.path}`\n }`;\n const body = request.bodyBase64 ? Buffer.from(request.bodyBase64, \"base64\") : undefined;\n const headers = { ...request.headers };\n delete headers.host;\n delete headers[\"content-length\"];\n\n fetch(targetUrl, { method: request.method, headers, body: body ?? undefined })\n .then(async (res) => {\n const resBody = await res.arrayBuffer();\n const resHeaders: Record<string, string | string[]> = Object.fromEntries(res.headers.entries());\n delete resHeaders[\"set-cookie\"];\n\n const getSetCookie = (res.headers as Headers & { getSetCookie?: () => string[] }).getSetCookie;\n const setCookieValues = typeof getSetCookie === \"function\" ? getSetCookie.call(res.headers) : [];\n const fallbackSetCookie = res.headers.get(\"set-cookie\");\n if (setCookieValues.length === 0 && fallbackSetCookie) {\n setCookieValues.push(fallbackSetCookie);\n }\n if (setCookieValues.length > 0) {\n resHeaders[\"set-cookie\"] = setCookieValues;\n }\n\n const responsePayload: ProxyResponse = {\n type: \"http_response\",\n id: request.id,\n status: res.status,\n headers: resHeaders,\n bodyBase64: resBody.byteLength ? Buffer.from(resBody).toString(\"base64\") : null,\n };\n ctx.ws.send(JSON.stringify(responsePayload));\n })\n .catch((error) => {\n const responsePayload: ProxyResponse = {\n type: \"http_response\",\n id: request.id,\n status: 502,\n headers: { \"content-type\": \"text/plain\" },\n bodyBase64: Buffer.from(`Tunnel error: ${String(error)}`).toString(\"base64\"),\n };\n ctx.ws.send(JSON.stringify(responsePayload));\n });\n}\n\nfunction handleWsOpen(ctx: TunnelContext, request: WsOpen): void {\n const targetUrl = `ws://localhost:${ctx.port}${\n request.path.startsWith(\"/\") ? request.path : `/${request.path}`\n }`;\n const protocolsHeader =\n request.headers[\"sec-websocket-protocol\"] ?? request.headers[\"Sec-WebSocket-Protocol\"];\n const protocols = protocolsHeader\n ? protocolsHeader.split(\",\").map((v) => v.trim()).filter(Boolean)\n : undefined;\n const localWs = new WebSocket(targetUrl, protocols);\n ctx.wsConnections.set(request.id, localWs);\n\n localWs.onopen = () => {\n ctx.ws.send(JSON.stringify({ type: \"ws_ready\", id: request.id } satisfies WsReady));\n const queued = ctx.wsQueues.get(request.id);\n if (queued) {\n for (const message of queued) {\n ctx.ws.send(JSON.stringify(message));\n }\n ctx.wsQueues.delete(request.id);\n }\n };\n\n localWs.onmessage = (event) => {\n const data =\n typeof event.data === \"string\"\n ? Buffer.from(event.data)\n : Buffer.from(event.data as ArrayBuffer);\n const response: WsMessage = {\n type: \"ws_message\",\n id: request.id,\n dataBase64: data.toString(\"base64\"),\n isBinary: typeof event.data !== \"string\",\n };\n ctx.ws.send(JSON.stringify(response));\n };\n\n localWs.onclose = (event) => {\n ctx.wsConnections.delete(request.id);\n const response: WsClose = {\n type: \"ws_close\",\n id: request.id,\n code: event.code,\n reason: event.reason,\n };\n ctx.ws.send(JSON.stringify(response));\n };\n\n localWs.onerror = () => {\n ctx.wsConnections.delete(request.id);\n const response: WsClose = {\n type: \"ws_close\",\n id: request.id,\n code: 1011,\n reason: \"local_ws_error\",\n };\n ctx.ws.send(JSON.stringify(response));\n };\n}\n\nfunction handleWsMessage(ctx: TunnelContext, message: WsMessage): void {\n const localWs = ctx.wsConnections.get(message.id);\n const data = Buffer.from(message.dataBase64, \"base64\");\n if (!localWs || localWs.readyState !== WebSocket.OPEN) {\n const queued = ctx.wsQueues.get(message.id) ?? [];\n queued.push(message);\n ctx.wsQueues.set(message.id, queued);\n return;\n }\n if (message.isBinary) {\n localWs.send(data);\n } else {\n localWs.send(data.toString());\n }\n}\n\nfunction handleWsClose(ctx: TunnelContext, message: WsClose): void {\n const localWs = ctx.wsConnections.get(message.id);\n if (!localWs) {\n const queued = ctx.wsQueues.get(message.id) ?? [];\n queued.push(message);\n ctx.wsQueues.set(message.id, queued);\n return;\n }\n localWs.close(message.code ?? 1000, message.reason ?? \"\");\n ctx.wsConnections.delete(message.id);\n}\n\n// === Connect ===\n\nexport function connectTunnel(input: {\n apiUrl: string;\n tunnelId: string;\n token: string;\n port: number;\n onReady?: () => void;\n}): WebSocket {\n const wsUrl = toWebSocketUrl(\n `${input.apiUrl}/local-tests/tunnels/${input.tunnelId}/connect?token=${input.token}`\n );\n const ws = new WebSocket(wsUrl);\n const ctx: TunnelContext = {\n ws,\n port: input.port,\n wsConnections: new Map(),\n wsQueues: new Map(),\n };\n\n ws.onopen = () => {\n input.onReady?.();\n };\n\n ws.onerror = (event) => {\n console.error(\"Tunnel error\", event);\n };\n\n ws.onclose = () => {\n console.log(\"Tunnel closed\");\n };\n\n ws.onmessage = async (event) => {\n try {\n const raw =\n typeof event.data === \"string\"\n ? event.data\n : Buffer.from(event.data as ArrayBuffer).toString();\n const payload = JSON.parse(raw) as { type: string };\n\n switch (payload.type) {\n case \"http_request\":\n handleHttpRequest(ctx, payload as ProxyRequest);\n break;\n case \"ws_open\":\n handleWsOpen(ctx, payload as WsOpen);\n break;\n case \"ws_message\":\n handleWsMessage(ctx, payload as WsMessage);\n break;\n case \"ws_close\":\n handleWsClose(ctx, payload as WsClose);\n break;\n case \"health_ping\":\n ws.send(JSON.stringify({ type: \"health_pong\" }));\n break;\n }\n } catch (error) {\n console.error(\"Tunnel message error\", error);\n }\n };\n\n return ws;\n}\n"],"mappings":"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","names":["process","getArgValue","process"]}