@canaryai/cli 0.1.9 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2T64Z2NI.js +97 -0
- package/dist/chunk-2T64Z2NI.js.map +1 -0
- package/dist/{chunk-G2X3H7AM.js → chunk-L26U3BST.js} +231 -132
- package/dist/chunk-L26U3BST.js.map +1 -0
- package/dist/{chunk-NRMZHITS.js → chunk-V7U52ISX.js} +2 -2
- package/dist/{feature-flag-43WAHIUZ.js → feature-flag-MFTRYRYX.js} +17 -4
- package/dist/feature-flag-MFTRYRYX.js.map +1 -0
- package/dist/index.js +42 -37
- package/dist/index.js.map +1 -1
- package/dist/knobs-T3O4Z3ZB.js +286 -0
- package/dist/knobs-T3O4Z3ZB.js.map +1 -0
- package/dist/{local-browser-REU2RIYX.js → local-browser-SYPTG6IQ.js} +3 -3
- package/dist/{mcp-5N5Z343W.js → mcp-TMD2R5Z6.js} +4 -4
- package/dist/{psql-7AEFGJWI.js → psql-6IFVXM3A.js} +6 -5
- package/dist/psql-6IFVXM3A.js.map +1 -0
- package/dist/{redis-BXYEPX4T.js → redis-HZC32IEO.js} +6 -5
- package/dist/redis-HZC32IEO.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-G2X3H7AM.js.map +0 -1
- package/dist/chunk-SGNA6N2N.js +0 -36
- package/dist/chunk-SGNA6N2N.js.map +0 -1
- package/dist/feature-flag-43WAHIUZ.js.map +0 -1
- package/dist/psql-7AEFGJWI.js.map +0 -1
- package/dist/redis-BXYEPX4T.js.map +0 -1
- /package/dist/{chunk-NRMZHITS.js.map → chunk-V7U52ISX.js.map} +0 -0
- /package/dist/{local-browser-REU2RIYX.js.map → local-browser-SYPTG6IQ.js.map} +0 -0
- /package/dist/{mcp-5N5Z343W.js.map → mcp-TMD2R5Z6.js.map} +0 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readStoredApiUrl,
|
|
3
|
+
readStoredToken
|
|
4
|
+
} from "./chunk-2T64Z2NI.js";
|
|
5
|
+
import "./chunk-DGUM43GV.js";
|
|
6
|
+
|
|
7
|
+
// src/knobs.ts
|
|
8
|
+
import process from "process";
|
|
9
|
+
var ENV_URLS = {
|
|
10
|
+
prod: "https://api.trycanary.ai",
|
|
11
|
+
production: "https://api.trycanary.ai",
|
|
12
|
+
dev: "https://api.dev.trycanary.ai",
|
|
13
|
+
local: "http://localhost:3000"
|
|
14
|
+
};
|
|
15
|
+
function getArgValue(argv, key) {
|
|
16
|
+
const index = argv.indexOf(key);
|
|
17
|
+
if (index === -1 || index >= argv.length - 1) return void 0;
|
|
18
|
+
return argv[index + 1];
|
|
19
|
+
}
|
|
20
|
+
function hasFlag(argv, ...flags) {
|
|
21
|
+
return flags.some((flag) => argv.includes(flag));
|
|
22
|
+
}
|
|
23
|
+
async function resolveConfig(argv) {
|
|
24
|
+
const storedApiUrl = await readStoredApiUrl();
|
|
25
|
+
const env = getArgValue(argv, "--env");
|
|
26
|
+
if (env && !ENV_URLS[env]) {
|
|
27
|
+
console.error(`Unknown environment: ${env}`);
|
|
28
|
+
console.error("Valid environments: prod, dev, local");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const apiUrl = getArgValue(argv, "--api-url") ?? (env ? ENV_URLS[env] : void 0) ?? process.env.CANARY_API_URL ?? storedApiUrl ?? "https://api.trycanary.ai";
|
|
32
|
+
const token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken();
|
|
33
|
+
if (!token) {
|
|
34
|
+
console.error("Error: No API token found.");
|
|
35
|
+
console.error("Run: canary login");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
return { apiUrl, token };
|
|
39
|
+
}
|
|
40
|
+
async function apiRequest(apiUrl, token, method, path, body) {
|
|
41
|
+
const res = await fetch(`${apiUrl}${path}`, {
|
|
42
|
+
method,
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: `Bearer ${token}`,
|
|
45
|
+
"Content-Type": "application/json"
|
|
46
|
+
},
|
|
47
|
+
...body ? { body: JSON.stringify(body) } : {}
|
|
48
|
+
});
|
|
49
|
+
if (res.status === 401) {
|
|
50
|
+
console.error("Error: Unauthorized. Your session may have expired.");
|
|
51
|
+
console.error("Run: canary login");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
const json = await res.json();
|
|
55
|
+
return json;
|
|
56
|
+
}
|
|
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");
|
|
65
|
+
const res = await fetch(`${apiUrl}/superadmin/knobs`, {
|
|
66
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
67
|
+
});
|
|
68
|
+
if (res.status === 401) {
|
|
69
|
+
console.error("Error: Unauthorized. Your session may have expired.");
|
|
70
|
+
console.error("Run: canary login");
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const json = await res.json();
|
|
74
|
+
if (!json.ok) {
|
|
75
|
+
console.error(`Error: ${json.error}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const knobs = json.knobs ?? [];
|
|
79
|
+
if (jsonOutput) {
|
|
80
|
+
console.log(JSON.stringify(knobs, null, 2));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (knobs.length === 0) {
|
|
84
|
+
console.log("No knobs found.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const knob of knobs) {
|
|
88
|
+
const desc = knob.description ? ` ${knob.description}` : "";
|
|
89
|
+
console.log(` ${knob.key} [${knob.valueType}] = ${formatValue(knob)}${desc}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function handleGet(argv, apiUrl, token) {
|
|
93
|
+
const key = argv[0];
|
|
94
|
+
if (!key || key.startsWith("--")) {
|
|
95
|
+
console.error("Error: Missing knob key.");
|
|
96
|
+
console.error("Usage: canary knobs get <key>");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
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);
|
|
114
|
+
if (!knob) {
|
|
115
|
+
console.error(`Knob not found: ${key}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
if (jsonOutput) {
|
|
119
|
+
console.log(JSON.stringify(knob, null, 2));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
console.log(` Key: ${knob.key}`);
|
|
123
|
+
console.log(` Type: ${knob.valueType}`);
|
|
124
|
+
console.log(` Value: ${formatValue(knob)}`);
|
|
125
|
+
console.log(` Description: ${knob.description ?? "(none)"}`);
|
|
126
|
+
console.log(` Updated: ${knob.updatedAt ?? "(never)"}`);
|
|
127
|
+
}
|
|
128
|
+
async function handleSet(argv, apiUrl, token) {
|
|
129
|
+
const key = argv[0];
|
|
130
|
+
if (!key || key.startsWith("--")) {
|
|
131
|
+
console.error("Error: Missing knob key.");
|
|
132
|
+
console.error(
|
|
133
|
+
"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]"
|
|
134
|
+
);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
const rawValue = argv[1];
|
|
138
|
+
if (rawValue === void 0 || rawValue.startsWith("--")) {
|
|
139
|
+
console.error("Error: Missing knob value.");
|
|
140
|
+
console.error(
|
|
141
|
+
"Usage: canary knobs set <key> <value> --type <boolean|string|number|json> [--description <text>]"
|
|
142
|
+
);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const valueType = getArgValue(argv, "--type");
|
|
146
|
+
if (!valueType || !["boolean", "string", "number", "json"].includes(valueType)) {
|
|
147
|
+
console.error("Error: --type is required and must be one of: boolean, string, number, json");
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
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
|
+
}
|
|
180
|
+
const description = getArgValue(argv, "--description") ?? void 0;
|
|
181
|
+
const result = await apiRequest(apiUrl, token, "POST", "/superadmin/knobs", {
|
|
182
|
+
key,
|
|
183
|
+
valueType,
|
|
184
|
+
value,
|
|
185
|
+
description
|
|
186
|
+
});
|
|
187
|
+
if (!result.ok) {
|
|
188
|
+
console.error(`Error: ${result.error}`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
console.log(`Set knob: ${key} = ${formatValue(result.knob)}`);
|
|
192
|
+
}
|
|
193
|
+
async function handleDelete(argv, apiUrl, token) {
|
|
194
|
+
const key = argv[0];
|
|
195
|
+
if (!key || key.startsWith("--")) {
|
|
196
|
+
console.error("Error: Missing knob key.");
|
|
197
|
+
console.error("Usage: canary knobs delete <key>");
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
const result = await apiRequest(
|
|
201
|
+
apiUrl,
|
|
202
|
+
token,
|
|
203
|
+
"DELETE",
|
|
204
|
+
`/superadmin/knobs/${encodeURIComponent(key)}`
|
|
205
|
+
);
|
|
206
|
+
if (!result.ok) {
|
|
207
|
+
console.error(`Error: ${result.error}`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
console.log(`Deleted knob: ${key}`);
|
|
211
|
+
}
|
|
212
|
+
async function handleToggle(argv, apiUrl, token) {
|
|
213
|
+
const key = argv[0];
|
|
214
|
+
if (!key || key.startsWith("--")) {
|
|
215
|
+
console.error("Error: Missing knob key.");
|
|
216
|
+
console.error("Usage: canary knobs toggle <key>");
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
const result = await apiRequest(
|
|
220
|
+
apiUrl,
|
|
221
|
+
token,
|
|
222
|
+
"POST",
|
|
223
|
+
`/superadmin/knobs/${encodeURIComponent(key)}/toggle`
|
|
224
|
+
);
|
|
225
|
+
if (!result.ok) {
|
|
226
|
+
console.error(`Error: ${result.error}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
console.log(`Toggled knob: ${key} \u2192 ${result.knob?.value}`);
|
|
230
|
+
}
|
|
231
|
+
function printKnobsHelp() {
|
|
232
|
+
console.log(
|
|
233
|
+
[
|
|
234
|
+
"Usage: canary knobs <sub-command> [options]",
|
|
235
|
+
"",
|
|
236
|
+
"Sub-commands:",
|
|
237
|
+
" list List all knobs",
|
|
238
|
+
" get <key> Get a knob value",
|
|
239
|
+
" set <key> <value> --type <type> [--description <text>]",
|
|
240
|
+
" Set a knob value",
|
|
241
|
+
" delete <key> Delete a knob",
|
|
242
|
+
" toggle <key> Toggle a boolean knob",
|
|
243
|
+
"",
|
|
244
|
+
"Types: boolean, string, number, json",
|
|
245
|
+
"",
|
|
246
|
+
"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"
|
|
251
|
+
].join("\n")
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
async function runKnobs(argv) {
|
|
255
|
+
const [subCommand, ...rest] = argv;
|
|
256
|
+
if (!subCommand || subCommand === "help" || hasFlag(argv, "--help", "-h")) {
|
|
257
|
+
printKnobsHelp();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const { apiUrl, token } = await resolveConfig(argv);
|
|
261
|
+
switch (subCommand) {
|
|
262
|
+
case "list":
|
|
263
|
+
await handleList(rest, apiUrl, token);
|
|
264
|
+
break;
|
|
265
|
+
case "get":
|
|
266
|
+
await handleGet(rest, apiUrl, token);
|
|
267
|
+
break;
|
|
268
|
+
case "set":
|
|
269
|
+
await handleSet(rest, apiUrl, token);
|
|
270
|
+
break;
|
|
271
|
+
case "delete":
|
|
272
|
+
await handleDelete(rest, apiUrl, token);
|
|
273
|
+
break;
|
|
274
|
+
case "toggle":
|
|
275
|
+
await handleToggle(rest, apiUrl, token);
|
|
276
|
+
break;
|
|
277
|
+
default:
|
|
278
|
+
console.error(`Unknown sub-command: ${subCommand}`);
|
|
279
|
+
printKnobsHelp();
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
export {
|
|
284
|
+
runKnobs
|
|
285
|
+
};
|
|
286
|
+
//# sourceMappingURL=knobs-T3O4Z3ZB.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 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":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LocalBrowserHost
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-L26U3BST.js";
|
|
4
4
|
import {
|
|
5
5
|
readStoredToken
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2T64Z2NI.js";
|
|
7
7
|
import "./chunk-DGUM43GV.js";
|
|
8
8
|
|
|
9
9
|
// src/local-browser/index.ts
|
|
@@ -137,4 +137,4 @@ async function runLocalBrowser(args) {
|
|
|
137
137
|
export {
|
|
138
138
|
runLocalBrowser
|
|
139
139
|
};
|
|
140
|
-
//# sourceMappingURL=local-browser-
|
|
140
|
+
//# sourceMappingURL=local-browser-SYPTG6IQ.js.map
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
connectTunnel,
|
|
3
3
|
createLocalRun,
|
|
4
4
|
createTunnel
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-V7U52ISX.js";
|
|
6
6
|
import {
|
|
7
7
|
LocalBrowserHost
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-L26U3BST.js";
|
|
9
9
|
import {
|
|
10
10
|
readStoredToken
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-2T64Z2NI.js";
|
|
12
12
|
import "./chunk-DGUM43GV.js";
|
|
13
13
|
|
|
14
14
|
// src/mcp.ts
|
|
@@ -381,4 +381,4 @@ function formatReport(input) {
|
|
|
381
381
|
export {
|
|
382
382
|
runMcp
|
|
383
383
|
};
|
|
384
|
-
//# sourceMappingURL=mcp-
|
|
384
|
+
//# sourceMappingURL=mcp-TMD2R5Z6.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readStoredApiUrl,
|
|
3
3
|
readStoredToken
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2T64Z2NI.js";
|
|
5
5
|
import "./chunk-DGUM43GV.js";
|
|
6
6
|
|
|
7
7
|
// src/psql.ts
|
|
@@ -29,13 +29,14 @@ function formatTable(data) {
|
|
|
29
29
|
return [header, separator, ...rows, `(${data.length} rows)`].join("\n");
|
|
30
30
|
}
|
|
31
31
|
async function runPsql(argv) {
|
|
32
|
-
const
|
|
32
|
+
const profile = getArgValue(argv, "--profile");
|
|
33
|
+
const storedApiUrl = await readStoredApiUrl(profile);
|
|
33
34
|
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 token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken(profile);
|
|
35
36
|
const jsonOutput = hasFlag(argv, "--json");
|
|
36
37
|
let query = getArgValue(argv, "--query");
|
|
37
38
|
if (!query) {
|
|
38
|
-
const flagsWithValues = ["--api-url", "--token", "--query"];
|
|
39
|
+
const flagsWithValues = ["--api-url", "--token", "--query", "--profile"];
|
|
39
40
|
const queryParts = [];
|
|
40
41
|
for (let i = 0; i < argv.length; i++) {
|
|
41
42
|
if (flagsWithValues.includes(argv[i])) {
|
|
@@ -120,4 +121,4 @@ Time: ${json.durationMs}ms`);
|
|
|
120
121
|
export {
|
|
121
122
|
runPsql
|
|
122
123
|
};
|
|
123
|
-
//# sourceMappingURL=psql-
|
|
124
|
+
//# sourceMappingURL=psql-6IFVXM3A.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 profile = getArgValue(argv, \"--profile\");\n const storedApiUrl = await readStoredApiUrl(profile);\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(profile));\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\", \"--profile\"];\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":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readStoredApiUrl,
|
|
3
3
|
readStoredToken
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2T64Z2NI.js";
|
|
5
5
|
import "./chunk-DGUM43GV.js";
|
|
6
6
|
|
|
7
7
|
// src/redis.ts
|
|
@@ -37,11 +37,12 @@ function formatOutput(data) {
|
|
|
37
37
|
return String(data);
|
|
38
38
|
}
|
|
39
39
|
async function runRedis(argv) {
|
|
40
|
-
const
|
|
40
|
+
const profile = getArgValue(argv, "--profile");
|
|
41
|
+
const storedApiUrl = await readStoredApiUrl(profile);
|
|
41
42
|
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 token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken(profile);
|
|
43
44
|
const jsonOutput = hasFlag(argv, "--json");
|
|
44
|
-
const flagsWithValues = ["--api-url", "--token"];
|
|
45
|
+
const flagsWithValues = ["--api-url", "--token", "--profile"];
|
|
45
46
|
const commandParts = [];
|
|
46
47
|
for (let i = 0; i < argv.length; i++) {
|
|
47
48
|
if (flagsWithValues.includes(argv[i])) {
|
|
@@ -126,4 +127,4 @@ Time: ${json_response.durationMs}ms`);
|
|
|
126
127
|
export {
|
|
127
128
|
runRedis
|
|
128
129
|
};
|
|
129
|
-
//# sourceMappingURL=redis-
|
|
130
|
+
//# sourceMappingURL=redis-HZC32IEO.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 profile = getArgValue(argv, \"--profile\");\n const storedApiUrl = await readStoredApiUrl(profile);\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(profile));\n\n const jsonOutput = hasFlag(argv, \"--json\");\n\n // Get command: remaining args after filtering flags\n const flagsWithValues = [\"--api-url\", \"--token\", \"--profile\"];\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":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/local-browser/host.ts"],"sourcesContent":["/**\n * Local Browser Host\n *\n * Manages a local browser instance and handles commands from the cloud API\n * via WebSocket. This enables cloud agents to control a browser running\n * on the user's local machine.\n */\n\nimport { chromium, type Browser, type BrowserContext, type Page, type Dialog } from \"playwright\";\nimport type {\n BrowserCommand,\n BrowserResponse,\n HeartbeatMessage,\n SessionMessage,\n LocalBrowserMode,\n} from \"./protocol\";\n\nconst HEARTBEAT_INTERVAL_MS = 30_000;\nconst RECONNECT_DELAY_MS = 1000;\nconst MAX_RECONNECT_DELAY_MS = 30_000;\nconst MAX_RECONNECT_ATTEMPTS = 10;\n\nexport interface LocalBrowserHostOptions {\n apiUrl: string;\n wsToken: string;\n sessionId: string;\n browserMode: LocalBrowserMode;\n cdpUrl?: string;\n headless?: boolean;\n storageStatePath?: string;\n onLog?: (level: \"info\" | \"warn\" | \"error\" | \"debug\", message: string, data?: unknown) => void;\n}\n\ninterface PageWithAISnapshot extends Page {\n _snapshotForAI(options: { mode: \"full\" | \"compact\" }): Promise<string>;\n}\n\n/**\n * LocalBrowserHost manages the browser and WebSocket connection to the cloud.\n */\nexport class LocalBrowserHost {\n private options: LocalBrowserHostOptions;\n private ws: WebSocket | null = null;\n private browser: Browser | null = null;\n private context: BrowserContext | null = null;\n private page: Page | null = null;\n private pendingDialogs: Dialog[] = [];\n private heartbeatTimer: NodeJS.Timeout | null = null;\n private reconnectAttempts = 0;\n private isShuttingDown = false;\n private lastSnapshotYaml = \"\";\n\n constructor(options: LocalBrowserHostOptions) {\n this.options = options;\n }\n\n private log(level: \"info\" | \"warn\" | \"error\" | \"debug\", message: string, data?: unknown) {\n if (this.options.onLog) {\n this.options.onLog(level, message, data);\n } else {\n const fn = level === \"error\" ? console.error : level === \"warn\" ? console.warn : console.log;\n fn(`[LocalBrowserHost] ${message}`, data ?? \"\");\n }\n }\n\n // =========================================================================\n // Lifecycle\n // =========================================================================\n\n async start(): Promise<void> {\n this.log(\"info\", \"Starting local browser host\", {\n browserMode: this.options.browserMode,\n sessionId: this.options.sessionId,\n });\n\n // Connect to WebSocket first\n await this.connectWebSocket();\n\n // Then launch browser\n await this.launchBrowser();\n\n // Notify cloud that browser is ready\n this.sendSessionEvent(\"browser_ready\");\n }\n\n async stop(): Promise<void> {\n this.isShuttingDown = true;\n this.log(\"info\", \"Stopping local browser host\");\n\n this.stopHeartbeat();\n\n if (this.ws) {\n try {\n this.ws.close(1000, \"Shutdown\");\n } catch {}\n this.ws = null;\n }\n\n if (this.context) {\n try {\n await this.context.close();\n } catch {}\n this.context = null;\n }\n\n if (this.browser) {\n try {\n await this.browser.close();\n } catch {}\n this.browser = null;\n }\n\n this.page = null;\n this.log(\"info\", \"Local browser host stopped\");\n }\n\n // =========================================================================\n // WebSocket Connection\n // =========================================================================\n\n private async connectWebSocket(): Promise<void> {\n return new Promise((resolve, reject) => {\n const wsUrl = `${this.options.apiUrl.replace(\"http\", \"ws\")}/local-browser/sessions/${this.options.sessionId}/connect?token=${this.options.wsToken}`;\n\n this.log(\"info\", \"Connecting to cloud API\", { url: wsUrl.replace(/token=.*/, \"token=***\") });\n\n const ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n this.log(\"info\", \"Connected to cloud API\");\n this.ws = ws;\n this.reconnectAttempts = 0;\n this.startHeartbeat();\n resolve();\n };\n\n ws.onmessage = (event) => {\n this.handleMessage(event.data as string);\n };\n\n ws.onerror = (event) => {\n this.log(\"error\", \"WebSocket error\", event);\n };\n\n ws.onclose = () => {\n this.log(\"info\", \"WebSocket closed\");\n this.stopHeartbeat();\n this.ws = null;\n\n if (!this.isShuttingDown) {\n this.scheduleReconnect();\n }\n };\n\n // Timeout after 30 seconds\n setTimeout(() => {\n if (!this.ws) {\n reject(new Error(\"WebSocket connection timeout\"));\n }\n }, 30_000);\n });\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n this.log(\"error\", \"Max reconnection attempts reached, giving up\");\n this.stop();\n return;\n }\n\n const delay = Math.min(\n RECONNECT_DELAY_MS * Math.pow(2, this.reconnectAttempts),\n MAX_RECONNECT_DELAY_MS\n );\n\n this.reconnectAttempts++;\n this.log(\"info\", `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n\n setTimeout(async () => {\n try {\n await this.connectWebSocket();\n this.sendSessionEvent(\"connected\");\n if (this.page) {\n this.sendSessionEvent(\"browser_ready\");\n }\n } catch (error) {\n this.log(\"error\", \"Reconnection failed\", error);\n this.scheduleReconnect();\n }\n }, delay);\n }\n\n // =========================================================================\n // Heartbeat\n // =========================================================================\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n const ping: HeartbeatMessage = {\n type: \"heartbeat\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n direction: \"pong\",\n };\n this.ws.send(JSON.stringify(ping));\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n // =========================================================================\n // Browser Management\n // =========================================================================\n\n private async launchBrowser(): Promise<void> {\n const { browserMode, cdpUrl, headless = true, storageStatePath } = this.options;\n\n if (browserMode === \"cdp\" && cdpUrl) {\n this.log(\"info\", \"Connecting to existing Chrome via CDP\", { cdpUrl });\n this.browser = await chromium.connectOverCDP(cdpUrl);\n const contexts = this.browser.contexts();\n this.context = contexts[0] ?? (await this.browser.newContext());\n const pages = this.context.pages();\n this.page = pages[0] ?? (await this.context.newPage());\n } else {\n this.log(\"info\", \"Launching new Playwright browser\", { headless });\n this.browser = await chromium.launch({\n headless,\n args: [\"--no-sandbox\"],\n });\n\n const contextOptions: { viewport: { width: number; height: number }; storageState?: string } =\n {\n viewport: { width: 1920, height: 1080 },\n };\n\n if (storageStatePath) {\n try {\n await Bun.file(storageStatePath).exists();\n contextOptions.storageState = storageStatePath;\n this.log(\"info\", \"Loading storage state\", { storageStatePath });\n } catch {\n this.log(\"debug\", \"Storage state file not found, starting fresh\");\n }\n }\n\n this.context = await this.browser.newContext(contextOptions);\n this.page = await this.context.newPage();\n }\n\n // Setup dialog handler\n this.page.on(\"dialog\", (dialog) => {\n this.pendingDialogs.push(dialog);\n });\n\n this.log(\"info\", \"Browser ready\");\n }\n\n // =========================================================================\n // Message Handling\n // =========================================================================\n\n private handleMessage(data: string): void {\n try {\n const message = JSON.parse(data);\n\n if (message.type === \"heartbeat\" && message.direction === \"ping\") {\n // Respond to ping with pong\n const pong: HeartbeatMessage = {\n type: \"heartbeat\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n direction: \"pong\",\n };\n this.ws?.send(JSON.stringify(pong));\n return;\n }\n\n if (message.type === \"command\") {\n this.handleCommand(message as BrowserCommand);\n return;\n }\n\n this.log(\"debug\", \"Received unknown message type\", message);\n } catch (error) {\n this.log(\"error\", \"Failed to parse message\", { error, data });\n }\n }\n\n private async handleCommand(command: BrowserCommand): Promise<void> {\n const startTime = Date.now();\n this.log(\"debug\", `Executing command: ${command.method}`, { id: command.id });\n\n try {\n const result = await this.executeMethod(command.method, command.args);\n const response: BrowserResponse = {\n type: \"response\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n requestId: command.id,\n success: true,\n result,\n };\n this.ws?.send(JSON.stringify(response));\n\n this.log(\"debug\", `Command completed: ${command.method}`, {\n id: command.id,\n durationMs: Date.now() - startTime,\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const response: BrowserResponse = {\n type: \"response\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n requestId: command.id,\n success: false,\n error: errorMessage,\n stack: error instanceof Error ? error.stack : undefined,\n };\n this.ws?.send(JSON.stringify(response));\n\n this.log(\"error\", `Command failed: ${command.method}`, {\n id: command.id,\n error: errorMessage,\n });\n }\n }\n\n private sendSessionEvent(\n event: \"connected\" | \"disconnected\" | \"browser_ready\" | \"browser_closed\" | \"error\",\n error?: string\n ): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\n\n const message: SessionMessage = {\n type: \"session\",\n id: crypto.randomUUID(),\n timestamp: Date.now(),\n event,\n browserMode: this.options.browserMode,\n error,\n };\n this.ws.send(JSON.stringify(message));\n }\n\n // =========================================================================\n // Method Execution\n // =========================================================================\n\n private async executeMethod(method: string, args: unknown[]): Promise<unknown> {\n // Route to appropriate handler\n switch (method) {\n // Lifecycle\n case \"connect\":\n return this.connect(args[0] as any);\n case \"disconnect\":\n return this.disconnect();\n\n // Navigation\n case \"navigate\":\n return this.navigate(args[0] as string, args[1] as any);\n case \"navigateBack\":\n return this.navigateBack(args[0] as any);\n\n // Page Inspection\n case \"snapshot\":\n return this.snapshot(args[0] as any);\n case \"takeScreenshot\":\n return this.takeScreenshot(args[0] as any);\n case \"evaluate\":\n return this.evaluate(args[0] as string, args[1] as any);\n case \"runCode\":\n return this.runCode(args[0] as string, args[1] as any);\n case \"consoleMessages\":\n return this.consoleMessages(args[0] as any);\n case \"networkRequests\":\n return this.networkRequests(args[0] as any);\n\n // Interaction\n case \"click\":\n return this.click(args[0] as string, args[1] as string, args[2] as any);\n case \"clickAtCoordinates\":\n return this.clickAtCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as string,\n args[3] as any\n );\n case \"moveToCoordinates\":\n return this.moveToCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as string,\n args[3] as any\n );\n case \"dragCoordinates\":\n return this.dragCoordinates(\n args[0] as number,\n args[1] as number,\n args[2] as number,\n args[3] as number,\n args[4] as string,\n args[5] as any\n );\n case \"hover\":\n return this.hover(args[0] as string, args[1] as string, args[2] as any);\n case \"drag\":\n return this.drag(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as string,\n args[4] as any\n );\n case \"type\":\n return this.type(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as boolean,\n args[4] as any\n );\n case \"pressKey\":\n return this.pressKey(args[0] as string, args[1] as any);\n case \"fillForm\":\n return this.fillForm(args[0] as any[], args[1] as any);\n case \"selectOption\":\n return this.selectOption(\n args[0] as string,\n args[1] as string,\n args[2] as string,\n args[3] as any\n );\n case \"fileUpload\":\n return this.fileUpload(args[0] as string[], args[1] as any);\n\n // Dialogs\n case \"handleDialog\":\n return this.handleDialog(args[0] as \"accept\" | \"dismiss\", args[1] as string, args[2] as any);\n\n // Waiting\n case \"waitFor\":\n return this.waitFor(args[0] as any);\n\n // Browser Management\n case \"close\":\n return this.closePage(args[0] as any);\n case \"resize\":\n return this.resize(args[0] as number, args[1] as number, args[2] as any);\n case \"tabs\":\n return this.tabs(args[0] as any, args[1] as number, args[2] as any);\n\n // Storage\n case \"getStorageState\":\n return this.getStorageState(args[0] as any);\n case \"getCurrentUrl\":\n return this.getCurrentUrl(args[0] as any);\n case \"getTitle\":\n return this.getTitle(args[0] as any);\n case \"getLinks\":\n return this.getLinks(args[0] as any);\n case \"getElementBoundingBox\":\n return this.getElementBoundingBox(args[0] as string, args[1] as any);\n\n // Tracing\n case \"startTracing\":\n return this.startTracing(args[0] as any);\n case \"stopTracing\":\n return this.stopTracing(args[0] as any);\n\n // Video\n case \"isVideoRecordingEnabled\":\n return false; // Video not supported in CLI host currently\n case \"saveVideo\":\n return null;\n case \"getVideoPath\":\n return null;\n\n default:\n throw new Error(`Unknown method: ${method}`);\n }\n }\n\n // =========================================================================\n // IBrowserClient Method Implementations\n // =========================================================================\n\n private getPage(): Page {\n if (!this.page) throw new Error(\"No page available\");\n return this.page;\n }\n\n private resolveRef(ref: string) {\n return this.getPage().locator(`aria-ref=${ref}`);\n }\n\n private async connect(_options: any): Promise<void> {\n // Browser already launched in start()\n return;\n }\n\n private async disconnect(): Promise<void> {\n await this.stop();\n }\n\n private async navigate(url: string, _opts?: any): Promise<string> {\n const page = this.getPage();\n await page.goto(url, { waitUntil: \"domcontentloaded\" });\n await page.waitForLoadState(\"load\", { timeout: 5000 }).catch(() => {});\n return this.captureSnapshot();\n }\n\n private async navigateBack(_opts?: any): Promise<string> {\n await this.getPage().goBack();\n return this.captureSnapshot();\n }\n\n private async snapshot(_opts?: any): Promise<string> {\n return this.captureSnapshot();\n }\n\n private async captureSnapshot(): Promise<string> {\n const page = this.getPage() as PageWithAISnapshot;\n this.lastSnapshotYaml = await page._snapshotForAI({ mode: \"full\" });\n return this.lastSnapshotYaml;\n }\n\n private async takeScreenshot(opts?: any): Promise<string | null> {\n const page = this.getPage();\n const buffer = await page.screenshot({\n type: opts?.type ?? \"jpeg\",\n fullPage: opts?.fullPage ?? false,\n });\n const mime = opts?.type === \"png\" ? \"image/png\" : \"image/jpeg\";\n return `data:${mime};base64,${buffer.toString(\"base64\")}`;\n }\n\n private async evaluate<T>(fn: string, _opts?: any): Promise<T> {\n const page = this.getPage();\n return page.evaluate(new Function(`return (${fn})()`) as () => T);\n }\n\n private async runCode(code: string, _opts?: any): Promise<unknown> {\n const page = this.getPage();\n const fn = new Function(\"page\", `return (async () => { ${code} })()`) as (\n p: Page\n ) => Promise<unknown>;\n return fn(page);\n }\n\n private async consoleMessages(_opts?: any): Promise<string> {\n // Console messages would need to be captured over time\n return \"Console message capture not implemented in CLI host\";\n }\n\n private async networkRequests(_opts?: any): Promise<string> {\n // Network requests would need to be captured over time\n return \"Network request capture not implemented in CLI host\";\n }\n\n private async click(ref: string, _elementDesc?: string, opts?: any): Promise<void> {\n const locator = this.resolveRef(ref);\n await locator.scrollIntoViewIfNeeded({ timeout: 5000 }).catch(() => {});\n\n const box = await locator.boundingBox();\n if (box) {\n const centerX = box.x + box.width / 2;\n const centerY = box.y + box.height / 2;\n const page = this.getPage();\n\n if (opts?.modifiers?.length) {\n for (const mod of opts.modifiers) {\n await page.keyboard.down(mod);\n }\n }\n\n if (opts?.doubleClick) {\n await page.mouse.dblclick(centerX, centerY);\n } else {\n await page.mouse.click(centerX, centerY);\n }\n\n if (opts?.modifiers?.length) {\n for (const mod of opts.modifiers) {\n await page.keyboard.up(mod);\n }\n }\n } else {\n // Fallback to locator click\n if (opts?.doubleClick) {\n await locator.dblclick({ timeout: opts?.timeoutMs ?? 30000 });\n } else {\n await locator.click({ timeout: opts?.timeoutMs ?? 30000 });\n }\n }\n }\n\n private async clickAtCoordinates(\n x: number,\n y: number,\n _elementDesc: string,\n opts?: any\n ): Promise<void> {\n const page = this.getPage();\n if (opts?.doubleClick) {\n await page.mouse.dblclick(x, y);\n } else {\n await page.mouse.click(x, y);\n }\n }\n\n private async moveToCoordinates(\n x: number,\n y: number,\n _elementDesc: string,\n _opts?: any\n ): Promise<void> {\n await this.getPage().mouse.move(x, y);\n }\n\n private async dragCoordinates(\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n _elementDesc: string,\n _opts?: any\n ): Promise<void> {\n const page = this.getPage();\n await page.mouse.move(startX, startY);\n await page.mouse.down();\n await page.mouse.move(endX, endY);\n await page.mouse.up();\n }\n\n private async hover(ref: string, _elementDesc?: string, opts?: any): Promise<void> {\n await this.resolveRef(ref).hover({ timeout: opts?.timeoutMs ?? 30000 });\n }\n\n private async drag(\n startRef: string,\n _startElement: string,\n endRef: string,\n _endElement: string,\n opts?: any\n ): Promise<void> {\n const startLocator = this.resolveRef(startRef);\n const endLocator = this.resolveRef(endRef);\n await startLocator.dragTo(endLocator, { timeout: opts?.timeoutMs ?? 60000 });\n }\n\n private async type(\n ref: string,\n text: string,\n _elementDesc?: string,\n submit?: boolean,\n opts?: any\n ): Promise<void> {\n const locator = this.resolveRef(ref);\n await locator.clear();\n await locator.pressSequentially(text, {\n delay: opts?.delay ?? 0,\n timeout: opts?.timeoutMs ?? 30000,\n });\n if (submit) {\n await locator.press(\"Enter\");\n }\n }\n\n private async pressKey(key: string, _opts?: any): Promise<void> {\n await this.getPage().keyboard.press(key);\n }\n\n private async fillForm(fields: any[], opts?: any): Promise<void> {\n for (const field of fields) {\n const locator = this.resolveRef(field.ref);\n const fieldType = field.type ?? \"textbox\";\n\n switch (fieldType) {\n case \"checkbox\": {\n const isChecked = await locator.isChecked();\n const shouldBeChecked = field.value === \"true\";\n if (shouldBeChecked !== isChecked) {\n await locator.click({ timeout: opts?.timeoutMs ?? 30000 });\n }\n break;\n }\n case \"radio\":\n await locator.check({ timeout: opts?.timeoutMs ?? 30000 });\n break;\n case \"combobox\":\n await locator.selectOption(field.value, { timeout: opts?.timeoutMs ?? 30000 });\n break;\n default:\n await locator.fill(field.value, { timeout: opts?.timeoutMs ?? 30000 });\n }\n }\n }\n\n private async selectOption(\n ref: string,\n value: string,\n _elementDesc?: string,\n opts?: any\n ): Promise<void> {\n await this.resolveRef(ref).selectOption(value, { timeout: opts?.timeoutMs ?? 30000 });\n }\n\n private async fileUpload(paths: string[], opts?: any): Promise<void> {\n const fileChooser = await this.getPage().waitForEvent(\"filechooser\", {\n timeout: opts?.timeoutMs ?? 30000,\n });\n await fileChooser.setFiles(paths);\n }\n\n private async handleDialog(\n action: \"accept\" | \"dismiss\",\n promptText?: string,\n _opts?: any\n ): Promise<void> {\n const dialog = this.pendingDialogs.shift();\n if (dialog) {\n if (action === \"accept\") {\n await dialog.accept(promptText);\n } else {\n await dialog.dismiss();\n }\n }\n }\n\n private async waitFor(opts?: any): Promise<void> {\n const page = this.getPage();\n const timeout = opts?.timeout ?? opts?.timeoutMs ?? 30000;\n\n if (opts?.timeSec) {\n await page.waitForTimeout(opts.timeSec * 1000);\n return;\n }\n if (opts?.text) {\n await page.getByText(opts.text).first().waitFor({ state: \"visible\", timeout });\n return;\n }\n if (opts?.textGone) {\n await page.getByText(opts.textGone).first().waitFor({ state: \"hidden\", timeout });\n return;\n }\n if (opts?.selector) {\n await page.locator(opts.selector).waitFor({\n state: opts.state ?? \"visible\",\n timeout,\n });\n }\n }\n\n private async closePage(_opts?: any): Promise<void> {\n await this.getPage().close();\n this.page = null;\n }\n\n private async resize(width: number, height: number, _opts?: any): Promise<void> {\n await this.getPage().setViewportSize({ width, height });\n }\n\n private async tabs(action: string, index?: number, _opts?: any): Promise<unknown> {\n if (!this.context) throw new Error(\"No context available\");\n const pages = this.context.pages();\n\n switch (action) {\n case \"list\":\n return Promise.all(\n pages.map(async (p, i) => ({\n index: i,\n url: p.url(),\n title: await p.title().catch(() => \"\"),\n }))\n );\n case \"new\": {\n const newPage = await this.context.newPage();\n this.page = newPage;\n newPage.on(\"dialog\", (dialog) => this.pendingDialogs.push(dialog));\n return { index: pages.length };\n }\n case \"close\":\n if (index !== undefined && pages[index]) {\n await pages[index].close();\n } else {\n await this.page?.close();\n }\n this.page = this.context.pages()[0] ?? null;\n break;\n case \"select\":\n if (index !== undefined && pages[index]) {\n this.page = pages[index];\n }\n break;\n }\n return null;\n }\n\n private async getStorageState(_opts?: any): Promise<unknown> {\n if (!this.context) throw new Error(\"No context available\");\n return this.context.storageState();\n }\n\n private async getCurrentUrl(_opts?: any): Promise<string> {\n return this.getPage().url();\n }\n\n private async getTitle(_opts?: any): Promise<string> {\n return this.getPage().title();\n }\n\n private async getLinks(_opts?: any): Promise<string[]> {\n const page = this.getPage();\n return page.$$eval(\"a[href]\", (links) =>\n links\n .map((a) => (a as HTMLAnchorElement).href)\n .filter((h): h is string => !!h && (h.startsWith(\"http://\") || h.startsWith(\"https://\")))\n );\n }\n\n private async getElementBoundingBox(\n ref: string,\n _opts?: any\n ): Promise<{ x: number; y: number; width: number; height: number } | null> {\n const locator = this.resolveRef(ref);\n const box = await locator.boundingBox();\n if (!box) return null;\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n }\n\n private async startTracing(_opts?: any): Promise<void> {\n if (!this.context) throw new Error(\"No context available\");\n await this.context.tracing.start({ screenshots: true, snapshots: true });\n }\n\n private async stopTracing(_opts?: any): Promise<{\n trace: string;\n network: string;\n resources: string;\n directory: string | null;\n legend: string | null;\n }> {\n if (!this.context) throw new Error(\"No context available\");\n const tracePath = `/tmp/trace-${Date.now()}.zip`;\n await this.context.tracing.stop({ path: tracePath });\n return {\n trace: tracePath,\n network: \"\",\n resources: \"\",\n directory: null,\n legend: `Trace saved to ${tracePath}`,\n };\n }\n}\n"],"mappings":";AAQA,SAAS,gBAA2E;AASpF,IAAM,wBAAwB;AAC9B,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;AAC/B,IAAM,yBAAyB;AAoBxB,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA,KAAuB;AAAA,EACvB,UAA0B;AAAA,EAC1B,UAAiC;AAAA,EACjC,OAAoB;AAAA,EACpB,iBAA2B,CAAC;AAAA,EAC5B,iBAAwC;AAAA,EACxC,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EAE3B,YAAY,SAAkC;AAC5C,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,IAAI,OAA4C,SAAiB,MAAgB;AACvF,QAAI,KAAK,QAAQ,OAAO;AACtB,WAAK,QAAQ,MAAM,OAAO,SAAS,IAAI;AAAA,IACzC,OAAO;AACL,YAAM,KAAK,UAAU,UAAU,QAAQ,QAAQ,UAAU,SAAS,QAAQ,OAAO,QAAQ;AACzF,SAAG,sBAAsB,OAAO,IAAI,QAAQ,EAAE;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAuB;AAC3B,SAAK,IAAI,QAAQ,+BAA+B;AAAA,MAC9C,aAAa,KAAK,QAAQ;AAAA,MAC1B,WAAW,KAAK,QAAQ;AAAA,IAC1B,CAAC;AAGD,UAAM,KAAK,iBAAiB;AAG5B,UAAM,KAAK,cAAc;AAGzB,SAAK,iBAAiB,eAAe;AAAA,EACvC;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,iBAAiB;AACtB,SAAK,IAAI,QAAQ,6BAA6B;AAE9C,SAAK,cAAc;AAEnB,QAAI,KAAK,IAAI;AACX,UAAI;AACF,aAAK,GAAG,MAAM,KAAM,UAAU;AAAA,MAChC,QAAQ;AAAA,MAAC;AACT,WAAK,KAAK;AAAA,IACZ;AAEA,QAAI,KAAK,SAAS;AAChB,UAAI;AACF,cAAM,KAAK,QAAQ,MAAM;AAAA,MAC3B,QAAQ;AAAA,MAAC;AACT,WAAK,UAAU;AAAA,IACjB;AAEA,QAAI,KAAK,SAAS;AAChB,UAAI;AACF,cAAM,KAAK,QAAQ,MAAM;AAAA,MAC3B,QAAQ;AAAA,MAAC;AACT,WAAK,UAAU;AAAA,IACjB;AAEA,SAAK,OAAO;AACZ,SAAK,IAAI,QAAQ,4BAA4B;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBAAkC;AAC9C,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,QAAQ,GAAG,KAAK,QAAQ,OAAO,QAAQ,QAAQ,IAAI,CAAC,2BAA2B,KAAK,QAAQ,SAAS,kBAAkB,KAAK,QAAQ,OAAO;AAEjJ,WAAK,IAAI,QAAQ,2BAA2B,EAAE,KAAK,MAAM,QAAQ,YAAY,WAAW,EAAE,CAAC;AAE3F,YAAM,KAAK,IAAI,UAAU,KAAK;AAE9B,SAAG,SAAS,MAAM;AAChB,aAAK,IAAI,QAAQ,wBAAwB;AACzC,aAAK,KAAK;AACV,aAAK,oBAAoB;AACzB,aAAK,eAAe;AACpB,gBAAQ;AAAA,MACV;AAEA,SAAG,YAAY,CAAC,UAAU;AACxB,aAAK,cAAc,MAAM,IAAc;AAAA,MACzC;AAEA,SAAG,UAAU,CAAC,UAAU;AACtB,aAAK,IAAI,SAAS,mBAAmB,KAAK;AAAA,MAC5C;AAEA,SAAG,UAAU,MAAM;AACjB,aAAK,IAAI,QAAQ,kBAAkB;AACnC,aAAK,cAAc;AACnB,aAAK,KAAK;AAEV,YAAI,CAAC,KAAK,gBAAgB;AACxB,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF;AAGA,iBAAW,MAAM;AACf,YAAI,CAAC,KAAK,IAAI;AACZ,iBAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,QAClD;AAAA,MACF,GAAG,GAAM;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEQ,oBAA0B;AAChC,QAAI,KAAK,qBAAqB,wBAAwB;AACpD,WAAK,IAAI,SAAS,8CAA8C;AAChE,WAAK,KAAK;AACV;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK;AAAA,MACjB,qBAAqB,KAAK,IAAI,GAAG,KAAK,iBAAiB;AAAA,MACvD;AAAA,IACF;AAEA,SAAK;AACL,SAAK,IAAI,QAAQ,mBAAmB,KAAK,eAAe,KAAK,iBAAiB,GAAG;AAEjF,eAAW,YAAY;AACrB,UAAI;AACF,cAAM,KAAK,iBAAiB;AAC5B,aAAK,iBAAiB,WAAW;AACjC,YAAI,KAAK,MAAM;AACb,eAAK,iBAAiB,eAAe;AAAA,QACvC;AAAA,MACF,SAAS,OAAO;AACd,aAAK,IAAI,SAAS,uBAAuB,KAAK;AAC9C,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF,GAAG,KAAK;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAMQ,iBAAuB;AAC7B,SAAK,cAAc;AACnB,SAAK,iBAAiB,YAAY,MAAM;AACtC,UAAI,KAAK,IAAI,eAAe,UAAU,MAAM;AAC1C,cAAM,OAAyB;AAAA,UAC7B,MAAM;AAAA,UACN,IAAI,OAAO,WAAW;AAAA,UACtB,WAAW,KAAK,IAAI;AAAA,UACpB,WAAW;AAAA,QACb;AACA,aAAK,GAAG,KAAK,KAAK,UAAU,IAAI,CAAC;AAAA,MACnC;AAAA,IACF,GAAG,qBAAqB;AAAA,EAC1B;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,gBAAgB;AACvB,oBAAc,KAAK,cAAc;AACjC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,gBAA+B;AAC3C,UAAM,EAAE,aAAa,QAAQ,WAAW,MAAM,iBAAiB,IAAI,KAAK;AAExE,QAAI,gBAAgB,SAAS,QAAQ;AACnC,WAAK,IAAI,QAAQ,yCAAyC,EAAE,OAAO,CAAC;AACpE,WAAK,UAAU,MAAM,SAAS,eAAe,MAAM;AACnD,YAAM,WAAW,KAAK,QAAQ,SAAS;AACvC,WAAK,UAAU,SAAS,CAAC,KAAM,MAAM,KAAK,QAAQ,WAAW;AAC7D,YAAM,QAAQ,KAAK,QAAQ,MAAM;AACjC,WAAK,OAAO,MAAM,CAAC,KAAM,MAAM,KAAK,QAAQ,QAAQ;AAAA,IACtD,OAAO;AACL,WAAK,IAAI,QAAQ,oCAAoC,EAAE,SAAS,CAAC;AACjE,WAAK,UAAU,MAAM,SAAS,OAAO;AAAA,QACnC;AAAA,QACA,MAAM,CAAC,cAAc;AAAA,MACvB,CAAC;AAED,YAAM,iBACJ;AAAA,QACE,UAAU,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,MACxC;AAEF,UAAI,kBAAkB;AACpB,YAAI;AACF,gBAAM,IAAI,KAAK,gBAAgB,EAAE,OAAO;AACxC,yBAAe,eAAe;AAC9B,eAAK,IAAI,QAAQ,yBAAyB,EAAE,iBAAiB,CAAC;AAAA,QAChE,QAAQ;AACN,eAAK,IAAI,SAAS,8CAA8C;AAAA,QAClE;AAAA,MACF;AAEA,WAAK,UAAU,MAAM,KAAK,QAAQ,WAAW,cAAc;AAC3D,WAAK,OAAO,MAAM,KAAK,QAAQ,QAAQ;AAAA,IACzC;AAGA,SAAK,KAAK,GAAG,UAAU,CAAC,WAAW;AACjC,WAAK,eAAe,KAAK,MAAM;AAAA,IACjC,CAAC;AAED,SAAK,IAAI,QAAQ,eAAe;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,MAAoB;AACxC,QAAI;AACF,YAAM,UAAU,KAAK,MAAM,IAAI;AAE/B,UAAI,QAAQ,SAAS,eAAe,QAAQ,cAAc,QAAQ;AAEhE,cAAM,OAAyB;AAAA,UAC7B,MAAM;AAAA,UACN,IAAI,OAAO,WAAW;AAAA,UACtB,WAAW,KAAK,IAAI;AAAA,UACpB,WAAW;AAAA,QACb;AACA,aAAK,IAAI,KAAK,KAAK,UAAU,IAAI,CAAC;AAClC;AAAA,MACF;AAEA,UAAI,QAAQ,SAAS,WAAW;AAC9B,aAAK,cAAc,OAAyB;AAC5C;AAAA,MACF;AAEA,WAAK,IAAI,SAAS,iCAAiC,OAAO;AAAA,IAC5D,SAAS,OAAO;AACd,WAAK,IAAI,SAAS,2BAA2B,EAAE,OAAO,KAAK,CAAC;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,SAAwC;AAClE,UAAM,YAAY,KAAK,IAAI;AAC3B,SAAK,IAAI,SAAS,sBAAsB,QAAQ,MAAM,IAAI,EAAE,IAAI,QAAQ,GAAG,CAAC;AAE5E,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,cAAc,QAAQ,QAAQ,QAAQ,IAAI;AACpE,YAAM,WAA4B;AAAA,QAChC,MAAM;AAAA,QACN,IAAI,OAAO,WAAW;AAAA,QACtB,WAAW,KAAK,IAAI;AAAA,QACpB,WAAW,QAAQ;AAAA,QACnB,SAAS;AAAA,QACT;AAAA,MACF;AACA,WAAK,IAAI,KAAK,KAAK,UAAU,QAAQ,CAAC;AAEtC,WAAK,IAAI,SAAS,sBAAsB,QAAQ,MAAM,IAAI;AAAA,QACxD,IAAI,QAAQ;AAAA,QACZ,YAAY,KAAK,IAAI,IAAI;AAAA,MAC3B,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,YAAM,WAA4B;AAAA,QAChC,MAAM;AAAA,QACN,IAAI,OAAO,WAAW;AAAA,QACtB,WAAW,KAAK,IAAI;AAAA,QACpB,WAAW,QAAQ;AAAA,QACnB,SAAS;AAAA,QACT,OAAO;AAAA,QACP,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;AAAA,MAChD;AACA,WAAK,IAAI,KAAK,KAAK,UAAU,QAAQ,CAAC;AAEtC,WAAK,IAAI,SAAS,mBAAmB,QAAQ,MAAM,IAAI;AAAA,QACrD,IAAI,QAAQ;AAAA,QACZ,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,iBACN,OACA,OACM;AACN,QAAI,CAAC,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,KAAM;AAEvD,UAAM,UAA0B;AAAA,MAC9B,MAAM;AAAA,MACN,IAAI,OAAO,WAAW;AAAA,MACtB,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA,aAAa,KAAK,QAAQ;AAAA,MAC1B;AAAA,IACF;AACA,SAAK,GAAG,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,cAAc,QAAgB,MAAmC;AAE7E,YAAQ,QAAQ;AAAA;AAAA,MAEd,KAAK;AACH,eAAO,KAAK,QAAQ,KAAK,CAAC,CAAQ;AAAA,MACpC,KAAK;AACH,eAAO,KAAK,WAAW;AAAA;AAAA,MAGzB,KAAK;AACH,eAAO,KAAK,SAAS,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA,MACxD,KAAK;AACH,eAAO,KAAK,aAAa,KAAK,CAAC,CAAQ;AAAA;AAAA,MAGzC,KAAK;AACH,eAAO,KAAK,SAAS,KAAK,CAAC,CAAQ;AAAA,MACrC,KAAK;AACH,eAAO,KAAK,eAAe,KAAK,CAAC,CAAQ;AAAA,MAC3C,KAAK;AACH,eAAO,KAAK,SAAS,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA,MACxD,KAAK;AACH,eAAO,KAAK,QAAQ,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA,MACvD,KAAK;AACH,eAAO,KAAK,gBAAgB,KAAK,CAAC,CAAQ;AAAA,MAC5C,KAAK;AACH,eAAO,KAAK,gBAAgB,KAAK,CAAC,CAAQ;AAAA;AAAA,MAG5C,KAAK;AACH,eAAO,KAAK,MAAM,KAAK,CAAC,GAAa,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA,MACxE,KAAK;AACH,eAAO,KAAK;AAAA,UACV,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,QACR;AAAA,MACF,KAAK;AACH,eAAO,KAAK;AAAA,UACV,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,QACR;AAAA,MACF,KAAK;AACH,eAAO,KAAK;AAAA,UACV,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,QACR;AAAA,MACF,KAAK;AACH,eAAO,KAAK,MAAM,KAAK,CAAC,GAAa,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA,MACxE,KAAK;AACH,eAAO,KAAK;AAAA,UACV,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,QACR;AAAA,MACF,KAAK;AACH,eAAO,KAAK;AAAA,UACV,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,QACR;AAAA,MACF,KAAK;AACH,eAAO,KAAK,SAAS,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA,MACxD,KAAK;AACH,eAAO,KAAK,SAAS,KAAK,CAAC,GAAY,KAAK,CAAC,CAAQ;AAAA,MACvD,KAAK;AACH,eAAO,KAAK;AAAA,UACV,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,UACN,KAAK,CAAC;AAAA,QACR;AAAA,MACF,KAAK;AACH,eAAO,KAAK,WAAW,KAAK,CAAC,GAAe,KAAK,CAAC,CAAQ;AAAA;AAAA,MAG5D,KAAK;AACH,eAAO,KAAK,aAAa,KAAK,CAAC,GAA2B,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA;AAAA,MAG7F,KAAK;AACH,eAAO,KAAK,QAAQ,KAAK,CAAC,CAAQ;AAAA;AAAA,MAGpC,KAAK;AACH,eAAO,KAAK,UAAU,KAAK,CAAC,CAAQ;AAAA,MACtC,KAAK;AACH,eAAO,KAAK,OAAO,KAAK,CAAC,GAAa,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA,MACzE,KAAK;AACH,eAAO,KAAK,KAAK,KAAK,CAAC,GAAU,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA;AAAA,MAGpE,KAAK;AACH,eAAO,KAAK,gBAAgB,KAAK,CAAC,CAAQ;AAAA,MAC5C,KAAK;AACH,eAAO,KAAK,cAAc,KAAK,CAAC,CAAQ;AAAA,MAC1C,KAAK;AACH,eAAO,KAAK,SAAS,KAAK,CAAC,CAAQ;AAAA,MACrC,KAAK;AACH,eAAO,KAAK,SAAS,KAAK,CAAC,CAAQ;AAAA,MACrC,KAAK;AACH,eAAO,KAAK,sBAAsB,KAAK,CAAC,GAAa,KAAK,CAAC,CAAQ;AAAA;AAAA,MAGrE,KAAK;AACH,eAAO,KAAK,aAAa,KAAK,CAAC,CAAQ;AAAA,MACzC,KAAK;AACH,eAAO,KAAK,YAAY,KAAK,CAAC,CAAQ;AAAA;AAAA,MAGxC,KAAK;AACH,eAAO;AAAA;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MAET;AACE,cAAM,IAAI,MAAM,mBAAmB,MAAM,EAAE;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAgB;AACtB,QAAI,CAAC,KAAK,KAAM,OAAM,IAAI,MAAM,mBAAmB;AACnD,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,WAAW,KAAa;AAC9B,WAAO,KAAK,QAAQ,EAAE,QAAQ,YAAY,GAAG,EAAE;AAAA,EACjD;AAAA,EAEA,MAAc,QAAQ,UAA8B;AAElD;AAAA,EACF;AAAA,EAEA,MAAc,aAA4B;AACxC,UAAM,KAAK,KAAK;AAAA,EAClB;AAAA,EAEA,MAAc,SAAS,KAAa,OAA8B;AAChE,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,KAAK,KAAK,KAAK,EAAE,WAAW,mBAAmB,CAAC;AACtD,UAAM,KAAK,iBAAiB,QAAQ,EAAE,SAAS,IAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACrE,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA,EAEA,MAAc,aAAa,OAA8B;AACvD,UAAM,KAAK,QAAQ,EAAE,OAAO;AAC5B,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA,EAEA,MAAc,SAAS,OAA8B;AACnD,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA,EAEA,MAAc,kBAAmC;AAC/C,UAAM,OAAO,KAAK,QAAQ;AAC1B,SAAK,mBAAmB,MAAM,KAAK,eAAe,EAAE,MAAM,OAAO,CAAC;AAClE,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,eAAe,MAAoC;AAC/D,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,SAAS,MAAM,KAAK,WAAW;AAAA,MACnC,MAAM,MAAM,QAAQ;AAAA,MACpB,UAAU,MAAM,YAAY;AAAA,IAC9B,CAAC;AACD,UAAM,OAAO,MAAM,SAAS,QAAQ,cAAc;AAClD,WAAO,QAAQ,IAAI,WAAW,OAAO,SAAS,QAAQ,CAAC;AAAA,EACzD;AAAA,EAEA,MAAc,SAAY,IAAY,OAAyB;AAC7D,UAAM,OAAO,KAAK,QAAQ;AAC1B,WAAO,KAAK,SAAS,IAAI,SAAS,WAAW,EAAE,KAAK,CAAY;AAAA,EAClE;AAAA,EAEA,MAAc,QAAQ,MAAc,OAA+B;AACjE,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,KAAK,IAAI,SAAS,QAAQ,yBAAyB,IAAI,OAAO;AAGpE,WAAO,GAAG,IAAI;AAAA,EAChB;AAAA,EAEA,MAAc,gBAAgB,OAA8B;AAE1D,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,gBAAgB,OAA8B;AAE1D,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,MAAM,KAAa,cAAuB,MAA2B;AACjF,UAAM,UAAU,KAAK,WAAW,GAAG;AACnC,UAAM,QAAQ,uBAAuB,EAAE,SAAS,IAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAEtE,UAAM,MAAM,MAAM,QAAQ,YAAY;AACtC,QAAI,KAAK;AACP,YAAM,UAAU,IAAI,IAAI,IAAI,QAAQ;AACpC,YAAM,UAAU,IAAI,IAAI,IAAI,SAAS;AACrC,YAAM,OAAO,KAAK,QAAQ;AAE1B,UAAI,MAAM,WAAW,QAAQ;AAC3B,mBAAW,OAAO,KAAK,WAAW;AAChC,gBAAM,KAAK,SAAS,KAAK,GAAG;AAAA,QAC9B;AAAA,MACF;AAEA,UAAI,MAAM,aAAa;AACrB,cAAM,KAAK,MAAM,SAAS,SAAS,OAAO;AAAA,MAC5C,OAAO;AACL,cAAM,KAAK,MAAM,MAAM,SAAS,OAAO;AAAA,MACzC;AAEA,UAAI,MAAM,WAAW,QAAQ;AAC3B,mBAAW,OAAO,KAAK,WAAW;AAChC,gBAAM,KAAK,SAAS,GAAG,GAAG;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,OAAO;AAEL,UAAI,MAAM,aAAa;AACrB,cAAM,QAAQ,SAAS,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAAA,MAC9D,OAAO;AACL,cAAM,QAAQ,MAAM,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,mBACZ,GACA,GACA,cACA,MACe;AACf,UAAM,OAAO,KAAK,QAAQ;AAC1B,QAAI,MAAM,aAAa;AACrB,YAAM,KAAK,MAAM,SAAS,GAAG,CAAC;AAAA,IAChC,OAAO;AACL,YAAM,KAAK,MAAM,MAAM,GAAG,CAAC;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAc,kBACZ,GACA,GACA,cACA,OACe;AACf,UAAM,KAAK,QAAQ,EAAE,MAAM,KAAK,GAAG,CAAC;AAAA,EACtC;AAAA,EAEA,MAAc,gBACZ,QACA,QACA,MACA,MACA,cACA,OACe;AACf,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,KAAK,MAAM,KAAK,QAAQ,MAAM;AACpC,UAAM,KAAK,MAAM,KAAK;AACtB,UAAM,KAAK,MAAM,KAAK,MAAM,IAAI;AAChC,UAAM,KAAK,MAAM,GAAG;AAAA,EACtB;AAAA,EAEA,MAAc,MAAM,KAAa,cAAuB,MAA2B;AACjF,UAAM,KAAK,WAAW,GAAG,EAAE,MAAM,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAAA,EACxE;AAAA,EAEA,MAAc,KACZ,UACA,eACA,QACA,aACA,MACe;AACf,UAAM,eAAe,KAAK,WAAW,QAAQ;AAC7C,UAAM,aAAa,KAAK,WAAW,MAAM;AACzC,UAAM,aAAa,OAAO,YAAY,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAAA,EAC7E;AAAA,EAEA,MAAc,KACZ,KACA,MACA,cACA,QACA,MACe;AACf,UAAM,UAAU,KAAK,WAAW,GAAG;AACnC,UAAM,QAAQ,MAAM;AACpB,UAAM,QAAQ,kBAAkB,MAAM;AAAA,MACpC,OAAO,MAAM,SAAS;AAAA,MACtB,SAAS,MAAM,aAAa;AAAA,IAC9B,CAAC;AACD,QAAI,QAAQ;AACV,YAAM,QAAQ,MAAM,OAAO;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAc,SAAS,KAAa,OAA4B;AAC9D,UAAM,KAAK,QAAQ,EAAE,SAAS,MAAM,GAAG;AAAA,EACzC;AAAA,EAEA,MAAc,SAAS,QAAe,MAA2B;AAC/D,eAAW,SAAS,QAAQ;AAC1B,YAAM,UAAU,KAAK,WAAW,MAAM,GAAG;AACzC,YAAM,YAAY,MAAM,QAAQ;AAEhC,cAAQ,WAAW;AAAA,QACjB,KAAK,YAAY;AACf,gBAAM,YAAY,MAAM,QAAQ,UAAU;AAC1C,gBAAM,kBAAkB,MAAM,UAAU;AACxC,cAAI,oBAAoB,WAAW;AACjC,kBAAM,QAAQ,MAAM,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAAA,UAC3D;AACA;AAAA,QACF;AAAA,QACA,KAAK;AACH,gBAAM,QAAQ,MAAM,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AACzD;AAAA,QACF,KAAK;AACH,gBAAM,QAAQ,aAAa,MAAM,OAAO,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAC7E;AAAA,QACF;AACE,gBAAM,QAAQ,KAAK,MAAM,OAAO,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,aACZ,KACA,OACA,cACA,MACe;AACf,UAAM,KAAK,WAAW,GAAG,EAAE,aAAa,OAAO,EAAE,SAAS,MAAM,aAAa,IAAM,CAAC;AAAA,EACtF;AAAA,EAEA,MAAc,WAAW,OAAiB,MAA2B;AACnE,UAAM,cAAc,MAAM,KAAK,QAAQ,EAAE,aAAa,eAAe;AAAA,MACnE,SAAS,MAAM,aAAa;AAAA,IAC9B,CAAC;AACD,UAAM,YAAY,SAAS,KAAK;AAAA,EAClC;AAAA,EAEA,MAAc,aACZ,QACA,YACA,OACe;AACf,UAAM,SAAS,KAAK,eAAe,MAAM;AACzC,QAAI,QAAQ;AACV,UAAI,WAAW,UAAU;AACvB,cAAM,OAAO,OAAO,UAAU;AAAA,MAChC,OAAO;AACL,cAAM,OAAO,QAAQ;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,QAAQ,MAA2B;AAC/C,UAAM,OAAO,KAAK,QAAQ;AAC1B,UAAM,UAAU,MAAM,WAAW,MAAM,aAAa;AAEpD,QAAI,MAAM,SAAS;AACjB,YAAM,KAAK,eAAe,KAAK,UAAU,GAAI;AAC7C;AAAA,IACF;AACA,QAAI,MAAM,MAAM;AACd,YAAM,KAAK,UAAU,KAAK,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,WAAW,QAAQ,CAAC;AAC7E;AAAA,IACF;AACA,QAAI,MAAM,UAAU;AAClB,YAAM,KAAK,UAAU,KAAK,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,UAAU,QAAQ,CAAC;AAChF;AAAA,IACF;AACA,QAAI,MAAM,UAAU;AAClB,YAAM,KAAK,QAAQ,KAAK,QAAQ,EAAE,QAAQ;AAAA,QACxC,OAAO,KAAK,SAAS;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,OAA4B;AAClD,UAAM,KAAK,QAAQ,EAAE,MAAM;AAC3B,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,MAAc,OAAO,OAAe,QAAgB,OAA4B;AAC9E,UAAM,KAAK,QAAQ,EAAE,gBAAgB,EAAE,OAAO,OAAO,CAAC;AAAA,EACxD;AAAA,EAEA,MAAc,KAAK,QAAgB,OAAgB,OAA+B;AAChF,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,sBAAsB;AACzD,UAAM,QAAQ,KAAK,QAAQ,MAAM;AAEjC,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO,QAAQ;AAAA,UACb,MAAM,IAAI,OAAO,GAAG,OAAO;AAAA,YACzB,OAAO;AAAA,YACP,KAAK,EAAE,IAAI;AAAA,YACX,OAAO,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE;AAAA,UACvC,EAAE;AAAA,QACJ;AAAA,MACF,KAAK,OAAO;AACV,cAAM,UAAU,MAAM,KAAK,QAAQ,QAAQ;AAC3C,aAAK,OAAO;AACZ,gBAAQ,GAAG,UAAU,CAAC,WAAW,KAAK,eAAe,KAAK,MAAM,CAAC;AACjE,eAAO,EAAE,OAAO,MAAM,OAAO;AAAA,MAC/B;AAAA,MACA,KAAK;AACH,YAAI,UAAU,UAAa,MAAM,KAAK,GAAG;AACvC,gBAAM,MAAM,KAAK,EAAE,MAAM;AAAA,QAC3B,OAAO;AACL,gBAAM,KAAK,MAAM,MAAM;AAAA,QACzB;AACA,aAAK,OAAO,KAAK,QAAQ,MAAM,EAAE,CAAC,KAAK;AACvC;AAAA,MACF,KAAK;AACH,YAAI,UAAU,UAAa,MAAM,KAAK,GAAG;AACvC,eAAK,OAAO,MAAM,KAAK;AAAA,QACzB;AACA;AAAA,IACJ;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,gBAAgB,OAA+B;AAC3D,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,sBAAsB;AACzD,WAAO,KAAK,QAAQ,aAAa;AAAA,EACnC;AAAA,EAEA,MAAc,cAAc,OAA8B;AACxD,WAAO,KAAK,QAAQ,EAAE,IAAI;AAAA,EAC5B;AAAA,EAEA,MAAc,SAAS,OAA8B;AACnD,WAAO,KAAK,QAAQ,EAAE,MAAM;AAAA,EAC9B;AAAA,EAEA,MAAc,SAAS,OAAgC;AACrD,UAAM,OAAO,KAAK,QAAQ;AAC1B,WAAO,KAAK;AAAA,MAAO;AAAA,MAAW,CAAC,UAC7B,MACG,IAAI,CAAC,MAAO,EAAwB,IAAI,EACxC,OAAO,CAAC,MAAmB,CAAC,CAAC,MAAM,EAAE,WAAW,SAAS,KAAK,EAAE,WAAW,UAAU,EAAE;AAAA,IAC5F;AAAA,EACF;AAAA,EAEA,MAAc,sBACZ,KACA,OACyE;AACzE,UAAM,UAAU,KAAK,WAAW,GAAG;AACnC,UAAM,MAAM,MAAM,QAAQ,YAAY;AACtC,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,EAAE,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO;AAAA,EACpE;AAAA,EAEA,MAAc,aAAa,OAA4B;AACrD,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,sBAAsB;AACzD,UAAM,KAAK,QAAQ,QAAQ,MAAM,EAAE,aAAa,MAAM,WAAW,KAAK,CAAC;AAAA,EACzE;AAAA,EAEA,MAAc,YAAY,OAMvB;AACD,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,sBAAsB;AACzD,UAAM,YAAY,cAAc,KAAK,IAAI,CAAC;AAC1C,UAAM,KAAK,QAAQ,QAAQ,KAAK,EAAE,MAAM,UAAU,CAAC;AACnD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,MACT,WAAW;AAAA,MACX,WAAW;AAAA,MACX,QAAQ,kBAAkB,SAAS;AAAA,IACrC;AAAA,EACF;AACF;","names":[]}
|
package/dist/chunk-SGNA6N2N.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// src/auth.ts
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import os from "os";
|
|
4
|
-
import path from "path";
|
|
5
|
-
async function readStoredAuth() {
|
|
6
|
-
try {
|
|
7
|
-
const filePath = path.join(os.homedir(), ".config", "canary-cli", "auth.json");
|
|
8
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
9
|
-
return JSON.parse(content);
|
|
10
|
-
} catch {
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
async function readStoredToken() {
|
|
15
|
-
const auth = await readStoredAuth();
|
|
16
|
-
return auth?.token ?? null;
|
|
17
|
-
}
|
|
18
|
-
async function readStoredApiUrl() {
|
|
19
|
-
const auth = await readStoredAuth();
|
|
20
|
-
return auth?.apiUrl ?? null;
|
|
21
|
-
}
|
|
22
|
-
async function saveAuth(auth) {
|
|
23
|
-
const dir = path.join(os.homedir(), ".config", "canary-cli");
|
|
24
|
-
const filePath = path.join(dir, "auth.json");
|
|
25
|
-
await fs.mkdir(dir, { recursive: true, mode: 448 });
|
|
26
|
-
await fs.writeFile(filePath, JSON.stringify(auth, null, 2), { encoding: "utf8", mode: 384 });
|
|
27
|
-
return filePath;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
readStoredAuth,
|
|
32
|
-
readStoredToken,
|
|
33
|
-
readStoredApiUrl,
|
|
34
|
-
saveAuth
|
|
35
|
-
};
|
|
36
|
-
//# sourceMappingURL=chunk-SGNA6N2N.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\ntype StoredAuth = {\n token?: string;\n apiUrl?: string;\n orgId?: string;\n orgName?: string;\n};\n\nexport async function readStoredAuth(): Promise<StoredAuth | null> {\n try {\n const filePath = path.join(os.homedir(), \".config\", \"canary-cli\", \"auth.json\");\n const content = await fs.readFile(filePath, \"utf8\");\n return JSON.parse(content) as StoredAuth;\n } catch {\n return null;\n }\n}\n\nexport async function readStoredToken(): Promise<string | null> {\n const auth = await readStoredAuth();\n return auth?.token ?? null;\n}\n\nexport async function readStoredApiUrl(): Promise<string | null> {\n const auth = await readStoredAuth();\n return auth?.apiUrl ?? null;\n}\n\nexport async function saveAuth(auth: StoredAuth): Promise<string> {\n const dir = path.join(os.homedir(), \".config\", \"canary-cli\");\n const filePath = path.join(dir, \"auth.json\");\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n await fs.writeFile(filePath, JSON.stringify(auth, null, 2), { encoding: \"utf8\", mode: 0o600 });\n return filePath;\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AASjB,eAAsB,iBAA6C;AACjE,MAAI;AACF,UAAM,WAAW,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,cAAc,WAAW;AAC7E,UAAM,UAAU,MAAM,GAAG,SAAS,UAAU,MAAM;AAClD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,kBAA0C;AAC9D,QAAM,OAAO,MAAM,eAAe;AAClC,SAAO,MAAM,SAAS;AACxB;AAEA,eAAsB,mBAA2C;AAC/D,QAAM,OAAO,MAAM,eAAe;AAClC,SAAO,MAAM,UAAU;AACzB;AAEA,eAAsB,SAAS,MAAmC;AAChE,QAAM,MAAM,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY;AAC3D,QAAM,WAAW,KAAK,KAAK,KAAK,WAAW;AAC3C,QAAM,GAAG,MAAM,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACpD,QAAM,GAAG,UAAU,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAC7F,SAAO;AACT;","names":[]}
|