@canaryai/cli 0.1.5 → 0.1.9

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.
@@ -0,0 +1,123 @@
1
+ import {
2
+ readStoredApiUrl,
3
+ readStoredToken
4
+ } from "./chunk-SGNA6N2N.js";
5
+ import "./chunk-DGUM43GV.js";
6
+
7
+ // src/psql.ts
8
+ import process from "process";
9
+ var MAX_QUERY_SIZE = 1e4;
10
+ function getArgValue(argv, key) {
11
+ const index = argv.indexOf(key);
12
+ if (index === -1 || index >= argv.length - 1) return void 0;
13
+ return argv[index + 1];
14
+ }
15
+ function hasFlag(argv, ...flags) {
16
+ return flags.some((flag) => argv.includes(flag));
17
+ }
18
+ function formatTable(data) {
19
+ if (data.length === 0) return "(0 rows)";
20
+ const columns = Object.keys(data[0]);
21
+ const widths = columns.map(
22
+ (col) => Math.max(col.length, ...data.map((row) => String(row[col] ?? "").length))
23
+ );
24
+ const header = columns.map((col, i) => col.padEnd(widths[i])).join(" | ");
25
+ const separator = widths.map((w) => "-".repeat(w)).join("-+-");
26
+ const rows = data.map(
27
+ (row) => columns.map((col, i) => String(row[col] ?? "").padEnd(widths[i])).join(" | ")
28
+ );
29
+ return [header, separator, ...rows, `(${data.length} rows)`].join("\n");
30
+ }
31
+ async function runPsql(argv) {
32
+ const storedApiUrl = await readStoredApiUrl();
33
+ const apiUrl = getArgValue(argv, "--api-url") ?? process.env.CANARY_API_URL ?? storedApiUrl ?? "https://api.trycanary.ai";
34
+ const token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken();
35
+ const jsonOutput = hasFlag(argv, "--json");
36
+ let query = getArgValue(argv, "--query");
37
+ if (!query) {
38
+ const flagsWithValues = ["--api-url", "--token", "--query"];
39
+ const queryParts = [];
40
+ for (let i = 0; i < argv.length; i++) {
41
+ if (flagsWithValues.includes(argv[i])) {
42
+ i++;
43
+ continue;
44
+ }
45
+ if (argv[i].startsWith("--")) continue;
46
+ queryParts.push(argv[i]);
47
+ }
48
+ query = queryParts.join(" ");
49
+ }
50
+ if (!query) {
51
+ console.error("Error: No query provided.");
52
+ console.error("");
53
+ console.error("Usage: canary psql <query> [--json]");
54
+ console.error(' canary psql --query "SELECT * FROM users LIMIT 10"');
55
+ console.error("");
56
+ console.error("Examples:");
57
+ console.error(" canary psql SELECT id, status FROM jobs LIMIT 5");
58
+ console.error(` canary psql "SELECT * FROM jobs WHERE status = 'running'" --json`);
59
+ process.exit(1);
60
+ }
61
+ if (query.length > MAX_QUERY_SIZE) {
62
+ console.error(`Error: Query too large (${query.length} chars, max ${MAX_QUERY_SIZE})`);
63
+ console.error("For large queries, consider using psql directly with appropriate credentials.");
64
+ process.exit(1);
65
+ }
66
+ if (!token) {
67
+ console.error("Error: No API token found.");
68
+ console.error("Run: canary login");
69
+ process.exit(1);
70
+ }
71
+ try {
72
+ const res = await fetch(`${apiUrl}/superadmin/psql`, {
73
+ method: "POST",
74
+ headers: {
75
+ Authorization: `Bearer ${token}`,
76
+ "Content-Type": "application/json"
77
+ },
78
+ body: JSON.stringify({ query })
79
+ });
80
+ if (!res.ok) {
81
+ const text = await res.text();
82
+ if (res.status === 401) {
83
+ console.error("Error: Unauthorized. Your session may have expired.");
84
+ console.error("Run: canary login");
85
+ process.exit(1);
86
+ }
87
+ if (res.status === 404) {
88
+ console.error("Error: Endpoint not found. The psql feature may not be deployed to this environment.");
89
+ process.exit(1);
90
+ }
91
+ try {
92
+ const errorJson = JSON.parse(text);
93
+ console.error(`Error: ${errorJson.error ?? text}`);
94
+ } catch {
95
+ console.error(`Error (${res.status}): ${text || res.statusText}`);
96
+ }
97
+ process.exit(1);
98
+ }
99
+ const json = await res.json();
100
+ if (!json.ok) {
101
+ console.error(`Error: ${json.error}`);
102
+ process.exit(1);
103
+ }
104
+ if (jsonOutput) {
105
+ console.log(JSON.stringify(json.data, null, 2));
106
+ } else {
107
+ console.log(formatTable(json.data ?? []));
108
+ if (json.truncated) {
109
+ console.log(`
110
+ Note: Results truncated to ${json.rowCount} rows`);
111
+ }
112
+ console.log(`
113
+ Time: ${json.durationMs}ms`);
114
+ }
115
+ } catch (err) {
116
+ console.error(`Failed to execute query: ${err}`);
117
+ process.exit(1);
118
+ }
119
+ }
120
+ export {
121
+ runPsql
122
+ };
123
+ //# sourceMappingURL=psql-7AEFGJWI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/psql.ts"],"sourcesContent":["/**\n * CLI PSQL Passthrough\n *\n * Allows superadmins to execute read-only SQL queries against the production database.\n */\n\nimport process from \"node:process\";\nimport { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst MAX_QUERY_SIZE = 10_000; // 10KB - reasonable for interactive use\n\ntype PsqlResponse = {\n ok: boolean;\n data?: Record<string, unknown>[];\n rowCount?: number;\n truncated?: boolean;\n durationMs?: number;\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\n/**\n * Formats query results as a psql-style table.\n */\nfunction formatTable(data: Record<string, unknown>[]): string {\n if (data.length === 0) return \"(0 rows)\";\n\n const columns = Object.keys(data[0]);\n\n // Calculate column widths\n const widths = columns.map((col) =>\n Math.max(col.length, ...data.map((row) => String(row[col] ?? \"\").length))\n );\n\n // Build header\n const header = columns.map((col, i) => col.padEnd(widths[i])).join(\" | \");\n const separator = widths.map((w) => \"-\".repeat(w)).join(\"-+-\");\n\n // Build rows\n const rows = data.map((row) =>\n columns.map((col, i) => String(row[col] ?? \"\").padEnd(widths[i])).join(\" | \")\n );\n\n return [header, separator, ...rows, `(${data.length} rows)`].join(\"\\n\");\n}\n\nexport async function runPsql(argv: string[]): Promise<void> {\n const storedApiUrl = await readStoredApiUrl();\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\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 // Get query: either --query value or remaining args joined\n let query = getArgValue(argv, \"--query\");\n if (!query) {\n // Filter out flags and their values\n const flagsWithValues = [\"--api-url\", \"--token\", \"--query\"];\n const queryParts: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n if (flagsWithValues.includes(argv[i])) {\n i++; // Skip flag value\n continue;\n }\n if (argv[i].startsWith(\"--\")) continue;\n queryParts.push(argv[i]);\n }\n query = queryParts.join(\" \");\n }\n\n if (!query) {\n console.error(\"Error: No query provided.\");\n console.error(\"\");\n console.error(\"Usage: canary psql <query> [--json]\");\n console.error(' canary psql --query \"SELECT * FROM users LIMIT 10\"');\n console.error(\"\");\n console.error(\"Examples:\");\n console.error(\" canary psql SELECT id, status FROM jobs LIMIT 5\");\n console.error(' canary psql \"SELECT * FROM jobs WHERE status = \\'running\\'\" --json');\n process.exit(1);\n }\n\n if (query.length > MAX_QUERY_SIZE) {\n console.error(`Error: Query too large (${query.length} chars, max ${MAX_QUERY_SIZE})`);\n console.error(\"For large queries, consider using psql directly with appropriate credentials.\");\n process.exit(1);\n }\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 try {\n const res = await fetch(`${apiUrl}/superadmin/psql`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ query }),\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 === 404) {\n console.error(\"Error: Endpoint not found. The psql feature may not be deployed to this environment.\");\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 };\n console.error(`Error: ${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 PsqlResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify(json.data, null, 2));\n } else {\n console.log(formatTable(json.data ?? []));\n if (json.truncated) {\n console.log(`\\nNote: Results truncated to ${json.rowCount} rows`);\n }\n console.log(`\\nTime: ${json.durationMs}ms`);\n }\n } catch (err) {\n console.error(`Failed to execute query: ${err}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,129 @@
1
+ import {
2
+ readStoredApiUrl,
3
+ readStoredToken
4
+ } from "./chunk-SGNA6N2N.js";
5
+ import "./chunk-DGUM43GV.js";
6
+
7
+ // src/redis.ts
8
+ import process from "process";
9
+ var MAX_COMMAND_SIZE = 1e4;
10
+ function getArgValue(argv, key) {
11
+ const index = argv.indexOf(key);
12
+ if (index === -1 || index >= argv.length - 1) return void 0;
13
+ return argv[index + 1];
14
+ }
15
+ function hasFlag(argv, ...flags) {
16
+ return flags.some((flag) => argv.includes(flag));
17
+ }
18
+ function formatOutput(data) {
19
+ if (data === null) {
20
+ return "(nil)";
21
+ }
22
+ if (typeof data === "string") {
23
+ return data;
24
+ }
25
+ if (typeof data === "number") {
26
+ return `(integer) ${data}`;
27
+ }
28
+ if (Array.isArray(data)) {
29
+ if (data.length === 0) {
30
+ return "(empty array)";
31
+ }
32
+ return data.map((item, index) => `${index + 1}) ${formatOutput(item)}`).join("\n");
33
+ }
34
+ if (typeof data === "object") {
35
+ return JSON.stringify(data, null, 2);
36
+ }
37
+ return String(data);
38
+ }
39
+ async function runRedis(argv) {
40
+ const storedApiUrl = await readStoredApiUrl();
41
+ const apiUrl = getArgValue(argv, "--api-url") ?? process.env.CANARY_API_URL ?? storedApiUrl ?? "https://api.trycanary.ai";
42
+ const token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken();
43
+ const jsonOutput = hasFlag(argv, "--json");
44
+ const flagsWithValues = ["--api-url", "--token"];
45
+ const commandParts = [];
46
+ for (let i = 0; i < argv.length; i++) {
47
+ if (flagsWithValues.includes(argv[i])) {
48
+ i++;
49
+ continue;
50
+ }
51
+ if (argv[i].startsWith("--")) continue;
52
+ commandParts.push(argv[i]);
53
+ }
54
+ const command = commandParts.join(" ");
55
+ if (!command) {
56
+ console.error("Error: No command provided.");
57
+ console.error("");
58
+ console.error("Usage: canary redis <command> [--json]");
59
+ console.error(' canary redis KEYS "job:*"');
60
+ console.error("");
61
+ console.error("Examples:");
62
+ console.error(" canary redis PING");
63
+ console.error(" canary redis INFO");
64
+ console.error(' canary redis KEYS "job:*"');
65
+ console.error(' canary redis HGETALL "job:123:status"');
66
+ console.error(' canary redis GET "some:key" --json');
67
+ process.exit(1);
68
+ }
69
+ if (command.length > MAX_COMMAND_SIZE) {
70
+ console.error(`Error: Command too large (${command.length} chars, max ${MAX_COMMAND_SIZE})`);
71
+ process.exit(1);
72
+ }
73
+ if (!token) {
74
+ console.error("Error: No API token found.");
75
+ console.error("Run: canary login");
76
+ process.exit(1);
77
+ }
78
+ try {
79
+ const res = await fetch(`${apiUrl}/superadmin/redis`, {
80
+ method: "POST",
81
+ headers: {
82
+ Authorization: `Bearer ${token}`,
83
+ "Content-Type": "application/json"
84
+ },
85
+ body: JSON.stringify({ command })
86
+ });
87
+ if (!res.ok) {
88
+ const text = await res.text();
89
+ if (res.status === 401) {
90
+ console.error("Error: Unauthorized. Your session may have expired.");
91
+ console.error("Run: canary login");
92
+ process.exit(1);
93
+ }
94
+ if (res.status === 404) {
95
+ console.error("Error: Endpoint not found. The redis feature may not be deployed to this environment.");
96
+ process.exit(1);
97
+ }
98
+ try {
99
+ const errorJson = JSON.parse(text);
100
+ console.error(`Error: ${errorJson.error ?? text}`);
101
+ } catch {
102
+ console.error(`Error (${res.status}): ${text || res.statusText}`);
103
+ }
104
+ process.exit(1);
105
+ }
106
+ const json_response = await res.json();
107
+ if (!json_response.ok) {
108
+ console.error(`Error: ${json_response.error}`);
109
+ if (json_response.code === "BLOCKED") {
110
+ console.error("The Redis debugging feature has been disabled due to this blocked command.");
111
+ }
112
+ process.exit(1);
113
+ }
114
+ if (jsonOutput) {
115
+ console.log(JSON.stringify(json_response.data, null, 2));
116
+ } else {
117
+ console.log(formatOutput(json_response.data));
118
+ console.log(`
119
+ Time: ${json_response.durationMs}ms`);
120
+ }
121
+ } catch (err) {
122
+ console.error(`Failed to execute command: ${err}`);
123
+ process.exit(1);
124
+ }
125
+ }
126
+ export {
127
+ runRedis
128
+ };
129
+ //# sourceMappingURL=redis-BXYEPX4T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/redis.ts"],"sourcesContent":["/**\n * CLI Redis Passthrough\n *\n * Allows superadmins to execute read-only Redis commands against the production cache.\n */\n\nimport process from \"node:process\";\nimport { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst MAX_COMMAND_SIZE = 10_000; // 10KB - reasonable for interactive use\n\ntype RedisResponse = {\n ok: boolean;\n data?: unknown;\n durationMs?: number;\n error?: string;\n code?: \"DISABLED\" | \"BLOCKED\";\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\n/**\n * Formats Redis response for display.\n */\nfunction formatOutput(data: unknown): string {\n if (data === null) {\n return \"(nil)\";\n }\n\n if (typeof data === \"string\") {\n return data;\n }\n\n if (typeof data === \"number\") {\n return `(integer) ${data}`;\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) {\n return \"(empty array)\";\n }\n return data\n .map((item, index) => `${index + 1}) ${formatOutput(item)}`)\n .join(\"\\n\");\n }\n\n if (typeof data === \"object\") {\n return JSON.stringify(data, null, 2);\n }\n\n return String(data);\n}\n\nexport async function runRedis(argv: string[]): Promise<void> {\n const storedApiUrl = await readStoredApiUrl();\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\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 // Get command: remaining args after filtering flags\n const flagsWithValues = [\"--api-url\", \"--token\"];\n const commandParts: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n if (flagsWithValues.includes(argv[i])) {\n i++; // Skip flag value\n continue;\n }\n if (argv[i].startsWith(\"--\")) continue;\n commandParts.push(argv[i]);\n }\n const command = commandParts.join(\" \");\n\n if (!command) {\n console.error(\"Error: No command provided.\");\n console.error(\"\");\n console.error(\"Usage: canary redis <command> [--json]\");\n console.error(' canary redis KEYS \"job:*\"');\n console.error(\"\");\n console.error(\"Examples:\");\n console.error(\" canary redis PING\");\n console.error(\" canary redis INFO\");\n console.error(' canary redis KEYS \"job:*\"');\n console.error(' canary redis HGETALL \"job:123:status\"');\n console.error(' canary redis GET \"some:key\" --json');\n process.exit(1);\n }\n\n if (command.length > MAX_COMMAND_SIZE) {\n console.error(`Error: Command too large (${command.length} chars, max ${MAX_COMMAND_SIZE})`);\n process.exit(1);\n }\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 try {\n const res = await fetch(`${apiUrl}/superadmin/redis`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ command }),\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 === 404) {\n console.error(\"Error: Endpoint not found. The redis feature may not be deployed to this environment.\");\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 };\n console.error(`Error: ${errorJson.error ?? text}`);\n } catch {\n console.error(`Error (${res.status}): ${text || res.statusText}`);\n }\n process.exit(1);\n }\n\n const json_response = (await res.json()) as RedisResponse;\n\n if (!json_response.ok) {\n console.error(`Error: ${json_response.error}`);\n if (json_response.code === \"BLOCKED\") {\n console.error(\"The Redis debugging feature has been disabled due to this blocked command.\");\n }\n process.exit(1);\n }\n\n if (jsonOutput) {\n console.log(JSON.stringify(json_response.data, null, 2));\n } else {\n console.log(formatOutput(json_response.data));\n console.log(`\\nTime: ${json_response.durationMs}ms`);\n }\n } catch (err) {\n console.error(`Failed to execute command: ${err}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}