@canaryai/cli 0.1.9 → 0.1.11
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-HJ2JWIJ7.js +91 -0
- package/dist/chunk-HJ2JWIJ7.js.map +1 -0
- package/dist/{chunk-NRMZHITS.js → chunk-VYBCH4ZP.js} +2 -2
- package/dist/{feature-flag-43WAHIUZ.js → feature-flag-PN5IFFQR.js} +17 -4
- package/dist/feature-flag-PN5IFFQR.js.map +1 -0
- package/dist/index.js +33 -29
- package/dist/index.js.map +1 -1
- package/dist/knobs-DAG7HD2F.js +286 -0
- package/dist/knobs-DAG7HD2F.js.map +1 -0
- package/dist/{local-browser-REU2RIYX.js → local-browser-VOBIUIGT.js} +2 -2
- package/dist/{mcp-5N5Z343W.js → mcp-I6FCGDDR.js} +3 -3
- package/dist/{psql-7AEFGJWI.js → psql-A3BADRQN.js} +6 -5
- package/dist/psql-A3BADRQN.js.map +1 -0
- package/dist/{redis-BXYEPX4T.js → redis-N2DSDDQU.js} +6 -5
- package/dist/redis-N2DSDDQU.js.map +1 -0
- package/package.json +1 -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-VYBCH4ZP.js.map} +0 -0
- /package/dist/{local-browser-REU2RIYX.js.map → local-browser-VOBIUIGT.js.map} +0 -0
- /package/dist/{mcp-5N5Z343W.js.map → mcp-I6FCGDDR.js.map} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/auth.ts
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
var AUTH_DIR = path.join(os.homedir(), ".config", "canary-cli");
|
|
6
|
+
var AUTH_FILE = path.join(AUTH_DIR, "auth.json");
|
|
7
|
+
var ENV_URLS = {
|
|
8
|
+
prod: {
|
|
9
|
+
api: "https://api.trycanary.ai",
|
|
10
|
+
app: "https://app.trycanary.ai"
|
|
11
|
+
},
|
|
12
|
+
production: {
|
|
13
|
+
api: "https://api.trycanary.ai",
|
|
14
|
+
app: "https://app.trycanary.ai"
|
|
15
|
+
},
|
|
16
|
+
dev: {
|
|
17
|
+
api: "https://api.dev.trycanary.ai",
|
|
18
|
+
app: "https://app.dev.trycanary.ai"
|
|
19
|
+
},
|
|
20
|
+
local: {
|
|
21
|
+
api: "http://localhost:3000",
|
|
22
|
+
app: "http://localhost:5173"
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function isMultiProfile(data) {
|
|
26
|
+
return typeof data === "object" && data !== null && "profiles" in data;
|
|
27
|
+
}
|
|
28
|
+
async function readAuthFile() {
|
|
29
|
+
try {
|
|
30
|
+
const content = await fs.readFile(AUTH_FILE, "utf8");
|
|
31
|
+
const data = JSON.parse(content);
|
|
32
|
+
if (isMultiProfile(data)) {
|
|
33
|
+
return data;
|
|
34
|
+
}
|
|
35
|
+
const legacy = data;
|
|
36
|
+
return {
|
|
37
|
+
profiles: { default: legacy },
|
|
38
|
+
default: "default"
|
|
39
|
+
};
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function readStoredAuth(profile) {
|
|
45
|
+
const auth = await readAuthFile();
|
|
46
|
+
if (!auth) return null;
|
|
47
|
+
const key = profile ?? auth.default;
|
|
48
|
+
return auth.profiles[key] ?? null;
|
|
49
|
+
}
|
|
50
|
+
async function readStoredToken(profile) {
|
|
51
|
+
const auth = await readStoredAuth(profile);
|
|
52
|
+
return auth?.token ?? null;
|
|
53
|
+
}
|
|
54
|
+
async function readStoredApiUrl(profile) {
|
|
55
|
+
const auth = await readStoredAuth(profile);
|
|
56
|
+
return auth?.apiUrl ?? null;
|
|
57
|
+
}
|
|
58
|
+
async function saveAuth(auth, profile) {
|
|
59
|
+
await fs.mkdir(AUTH_DIR, { recursive: true, mode: 448 });
|
|
60
|
+
let existing;
|
|
61
|
+
try {
|
|
62
|
+
const content = await fs.readFile(AUTH_FILE, "utf8");
|
|
63
|
+
const data = JSON.parse(content);
|
|
64
|
+
if (isMultiProfile(data)) {
|
|
65
|
+
existing = data;
|
|
66
|
+
} else {
|
|
67
|
+
existing = {
|
|
68
|
+
profiles: { default: data },
|
|
69
|
+
default: "default"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
existing = { profiles: {}, default: "default" };
|
|
74
|
+
}
|
|
75
|
+
const key = profile ?? "default";
|
|
76
|
+
existing.profiles[key] = auth;
|
|
77
|
+
if (!existing.profiles[existing.default]) {
|
|
78
|
+
existing.default = key;
|
|
79
|
+
}
|
|
80
|
+
await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: "utf8", mode: 384 });
|
|
81
|
+
return AUTH_FILE;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
ENV_URLS,
|
|
86
|
+
readStoredAuth,
|
|
87
|
+
readStoredToken,
|
|
88
|
+
readStoredApiUrl,
|
|
89
|
+
saveAuth
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=chunk-HJ2JWIJ7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\ntype StoredAuth = {\n token?: string;\n apiUrl?: string;\n orgId?: string;\n orgName?: string;\n};\n\n/** Multi-profile auth file format */\ntype MultiProfileAuth = {\n profiles: Record<string, StoredAuth>;\n default: string;\n};\n\n/** Legacy flat format (single profile) */\ntype LegacyAuth = StoredAuth;\n\nconst AUTH_DIR = path.join(os.homedir(), \".config\", \"canary-cli\");\nconst AUTH_FILE = path.join(AUTH_DIR, \"auth.json\");\n\n/** Environment URL mappings shared across CLI commands */\nexport const ENV_URLS: Record<string, { api: string; app: string }> = {\n prod: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n production: {\n api: \"https://api.trycanary.ai\",\n app: \"https://app.trycanary.ai\",\n },\n dev: {\n api: \"https://api.dev.trycanary.ai\",\n app: \"https://app.dev.trycanary.ai\",\n },\n local: {\n api: \"http://localhost:3000\",\n app: \"http://localhost:5173\",\n },\n};\n\nfunction isMultiProfile(data: unknown): data is MultiProfileAuth {\n return typeof data === \"object\" && data !== null && \"profiles\" in data;\n}\n\n/** Read the raw auth file and normalize to multi-profile format */\nasync function readAuthFile(): Promise<MultiProfileAuth | null> {\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n return data;\n }\n\n // Legacy flat format — treat as the \"default\" profile\n const legacy = data as LegacyAuth;\n return {\n profiles: { default: legacy },\n default: \"default\",\n };\n } catch {\n return null;\n }\n}\n\nexport async function readStoredAuth(profile?: string): Promise<StoredAuth | null> {\n const auth = await readAuthFile();\n if (!auth) return null;\n\n const key = profile ?? auth.default;\n return auth.profiles[key] ?? null;\n}\n\nexport async function readStoredToken(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.token ?? null;\n}\n\nexport async function readStoredApiUrl(profile?: string): Promise<string | null> {\n const auth = await readStoredAuth(profile);\n return auth?.apiUrl ?? null;\n}\n\nexport async function saveAuth(auth: StoredAuth, profile?: string): Promise<string> {\n await fs.mkdir(AUTH_DIR, { recursive: true, mode: 0o700 });\n\n // Read existing file (may be legacy or multi-profile)\n let existing: MultiProfileAuth;\n try {\n const content = await fs.readFile(AUTH_FILE, \"utf8\");\n const data = JSON.parse(content) as unknown;\n\n if (isMultiProfile(data)) {\n existing = data;\n } else {\n // Migrate legacy flat format into profiles.default\n existing = {\n profiles: { default: data as LegacyAuth },\n default: \"default\",\n };\n }\n } catch {\n existing = { profiles: {}, default: \"default\" };\n }\n\n const key = profile ?? \"default\";\n existing.profiles[key] = auth;\n\n // Set default to this profile if no default exists yet\n if (!existing.profiles[existing.default]) {\n existing.default = key;\n }\n\n await fs.writeFile(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: \"utf8\", mode: 0o600 });\n return AUTH_FILE;\n}\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AAkBjB,IAAM,WAAW,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY;AAChE,IAAM,YAAY,KAAK,KAAK,UAAU,WAAW;AAG1C,IAAM,WAAyD;AAAA,EACpE,MAAM;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,YAAY;AAAA,IACV,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,KAAK;AAAA,IACH,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA,OAAO;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACF;AAEA,SAAS,eAAe,MAAyC;AAC/D,SAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,cAAc;AACpE;AAGA,eAAe,eAAiD;AAC9D,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,WAAW,MAAM;AACnD,UAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,QAAI,eAAe,IAAI,GAAG;AACxB,aAAO;AAAA,IACT;AAGA,UAAM,SAAS;AACf,WAAO;AAAA,MACL,UAAU,EAAE,SAAS,OAAO;AAAA,MAC5B,SAAS;AAAA,IACX;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,eAAe,SAA8C;AACjF,QAAM,OAAO,MAAM,aAAa;AAChC,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,MAAM,WAAW,KAAK;AAC5B,SAAO,KAAK,SAAS,GAAG,KAAK;AAC/B;AAEA,eAAsB,gBAAgB,SAA0C;AAC9E,QAAM,OAAO,MAAM,eAAe,OAAO;AACzC,SAAO,MAAM,SAAS;AACxB;AAEA,eAAsB,iBAAiB,SAA0C;AAC/E,QAAM,OAAO,MAAM,eAAe,OAAO;AACzC,SAAO,MAAM,UAAU;AACzB;AAEA,eAAsB,SAAS,MAAkB,SAAmC;AAClF,QAAM,GAAG,MAAM,UAAU,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAGzD,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,WAAW,MAAM;AACnD,UAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,QAAI,eAAe,IAAI,GAAG;AACxB,iBAAW;AAAA,IACb,OAAO;AAEL,iBAAW;AAAA,QACT,UAAU,EAAE,SAAS,KAAmB;AAAA,QACxC,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF,QAAQ;AACN,eAAW,EAAE,UAAU,CAAC,GAAG,SAAS,UAAU;AAAA,EAChD;AAEA,QAAM,MAAM,WAAW;AACvB,WAAS,SAAS,GAAG,IAAI;AAGzB,MAAI,CAAC,SAAS,SAAS,SAAS,OAAO,GAAG;AACxC,aAAS,UAAU;AAAA,EACrB;AAEA,QAAM,GAAG,UAAU,WAAW,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAClG,SAAO;AACT;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readStoredToken
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HJ2JWIJ7.js";
|
|
4
4
|
|
|
5
5
|
// src/local-run.ts
|
|
6
6
|
import process from "process";
|
|
@@ -331,4 +331,4 @@ export {
|
|
|
331
331
|
createTunnel,
|
|
332
332
|
connectTunnel
|
|
333
333
|
};
|
|
334
|
-
//# sourceMappingURL=chunk-
|
|
334
|
+
//# sourceMappingURL=chunk-VYBCH4ZP.js.map
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readStoredApiUrl,
|
|
3
3
|
readStoredToken
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HJ2JWIJ7.js";
|
|
5
5
|
import "./chunk-DGUM43GV.js";
|
|
6
6
|
|
|
7
7
|
// src/feature-flag.ts
|
|
8
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
|
+
};
|
|
9
15
|
function getArgValue(argv, key) {
|
|
10
16
|
const index = argv.indexOf(key);
|
|
11
17
|
if (index === -1 || index >= argv.length - 1) return void 0;
|
|
@@ -16,7 +22,13 @@ function hasFlag(argv, ...flags) {
|
|
|
16
22
|
}
|
|
17
23
|
async function resolveConfig(argv) {
|
|
18
24
|
const storedApiUrl = await readStoredApiUrl();
|
|
19
|
-
const
|
|
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";
|
|
20
32
|
const token = getArgValue(argv, "--token") ?? process.env.CANARY_API_TOKEN ?? await readStoredToken();
|
|
21
33
|
if (!token) {
|
|
22
34
|
console.error("Error: No API token found.");
|
|
@@ -172,8 +184,9 @@ function printFeatureFlagHelp() {
|
|
|
172
184
|
" disable <name> --org <orgId> Disable a flag for an organization",
|
|
173
185
|
"",
|
|
174
186
|
"Options:",
|
|
187
|
+
" --env <env> Target environment (prod, dev, local)",
|
|
175
188
|
" --json Output as JSON (list only)",
|
|
176
|
-
" --api-url <url> API URL override",
|
|
189
|
+
" --api-url <url> API URL override (takes precedence over --env)",
|
|
177
190
|
" --token <key> API token override"
|
|
178
191
|
].join("\n")
|
|
179
192
|
);
|
|
@@ -210,4 +223,4 @@ async function runFeatureFlag(argv) {
|
|
|
210
223
|
export {
|
|
211
224
|
runFeatureFlag
|
|
212
225
|
};
|
|
213
|
-
//# sourceMappingURL=feature-flag-
|
|
226
|
+
//# sourceMappingURL=feature-flag-PN5IFFQR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/feature-flag.ts"],"sourcesContent":["/**\n * CLI Feature Flag Management\n *\n * Allows superadmins to manage feature flags via the CLI.\n */\n\nimport process from \"node:process\";\nimport { readStoredToken, readStoredApiUrl } from \"./auth.js\";\n\nconst ENV_URLS: Record<string, string> = {\n prod: \"https://api.trycanary.ai\",\n production: \"https://api.trycanary.ai\",\n dev: \"https://api.dev.trycanary.ai\",\n local: \"http://localhost:3000\",\n};\n\ntype FeatureFlag = {\n id: string;\n name: string;\n description: string | null;\n createdAt: string;\n};\n\ntype FeatureFlagListResponse = {\n ok: boolean;\n flags?: Array<FeatureFlag & { gates?: Array<{ gateType: string; gateValue: string }> }>;\n error?: string;\n};\n\ntype ApiResponse = {\n ok: boolean;\n error?: string;\n flag?: FeatureFlag;\n};\n\nfunction getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nfunction hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nasync function resolveConfig(argv: string[]) {\n const storedApiUrl = await readStoredApiUrl();\n const env = getArgValue(argv, \"--env\");\n\n if (env && !ENV_URLS[env]) {\n console.error(`Unknown environment: ${env}`);\n console.error(\"Valid environments: prod, dev, local\");\n process.exit(1);\n }\n\n const apiUrl =\n getArgValue(argv, \"--api-url\") ??\n (env ? ENV_URLS[env] : undefined) ??\n process.env.CANARY_API_URL ??\n storedApiUrl ??\n \"https://api.trycanary.ai\";\n\n const token =\n getArgValue(argv, \"--token\") ?? process.env.CANARY_API_TOKEN ?? (await readStoredToken());\n\n if (!token) {\n console.error(\"Error: No API token found.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n return { apiUrl, token };\n}\n\nasync function apiRequest(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>\n): Promise<ApiResponse> {\n const res = await fetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as ApiResponse;\n return json;\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const res = await fetch(`${apiUrl}/superadmin/feature-flags`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Your session may have expired.\");\n console.error(\"Run: canary login\");\n process.exit(1);\n }\n\n const json = (await res.json()) as FeatureFlagListResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n const flags = json.flags ?? [];\n\n if (jsonOutput) {\n console.log(JSON.stringify(flags, null, 2));\n return;\n }\n\n if (flags.length === 0) {\n console.log(\"No feature flags found.\");\n return;\n }\n\n for (const flag of flags) {\n const gates = flag.gates ?? [];\n const gateStr =\n gates.length > 0\n ? gates.map((g) => `${g.gateType}:${g.gateValue}`).join(\", \")\n : \"(no gates)\";\n console.log(` ${flag.name} ${flag.description ?? \"\"} [${gateStr}]`);\n }\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag create <name> [--description <text>]\");\n process.exit(1);\n }\n\n const description = getArgValue(argv, \"--description\") ?? null;\n const result = await apiRequest(apiUrl, token, \"POST\", \"/superadmin/feature-flags\", {\n name,\n description,\n });\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Created feature flag: ${name}`);\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag delete <name>\");\n process.exit(1);\n }\n\n const result = await apiRequest(\n apiUrl,\n token,\n \"DELETE\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Deleted feature flag: ${name}`);\n}\n\nasync function handleEnable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag enable <name> --org <orgId>\");\n process.exit(1);\n }\n\n if (!orgId) {\n console.error(\"Error: Missing --org <orgId>.\");\n console.error(\"Usage: canary feature-flag enable <name> --org <orgId>\");\n process.exit(1);\n }\n\n const result = await apiRequest(\n apiUrl,\n token,\n \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/organizations/${encodeURIComponent(orgId)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Enabled ${name} for org ${orgId}`);\n}\n\nasync function handleDisable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag disable <name> --org <orgId>\");\n process.exit(1);\n }\n\n if (!orgId) {\n console.error(\"Error: Missing --org <orgId>.\");\n console.error(\"Usage: canary feature-flag disable <name> --org <orgId>\");\n process.exit(1);\n }\n\n const result = await apiRequest(\n apiUrl,\n token,\n \"DELETE\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/organizations/${encodeURIComponent(orgId)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Disabled ${name} for org ${orgId}`);\n}\n\nfunction printFeatureFlagHelp(): void {\n console.log(\n [\n \"Usage: canary feature-flag <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" list List all feature flags\",\n \" create <name> [--description <text>] Create a new flag\",\n \" delete <name> Delete a flag and all its gates\",\n \" enable <name> --org <orgId> Enable a flag for an organization\",\n \" disable <name> --org <orgId> Disable a flag for an organization\",\n \"\",\n \"Options:\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --json Output as JSON (list only)\",\n \" --api-url <url> API URL override (takes precedence over --env)\",\n \" --token <key> API token override\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runFeatureFlag(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printFeatureFlagHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"list\":\n await handleList(rest, apiUrl, token);\n break;\n case \"create\":\n await handleCreate(rest, apiUrl, token);\n break;\n case \"delete\":\n await handleDelete(rest, apiUrl, token);\n break;\n case \"enable\":\n await handleEnable(rest, apiUrl, token);\n break;\n case \"disable\":\n await handleDisable(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printFeatureFlagHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,14 @@ import {
|
|
|
4
4
|
createTunnel,
|
|
5
5
|
runLocalTest,
|
|
6
6
|
runTunnel
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VYBCH4ZP.js";
|
|
8
8
|
import {
|
|
9
|
+
ENV_URLS,
|
|
9
10
|
readStoredApiUrl,
|
|
10
11
|
readStoredAuth,
|
|
11
12
|
readStoredToken,
|
|
12
13
|
saveAuth
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HJ2JWIJ7.js";
|
|
14
15
|
import {
|
|
15
16
|
__require
|
|
16
17
|
} from "./chunk-DGUM43GV.js";
|
|
@@ -331,24 +332,12 @@ function countHealed(eventLogPath) {
|
|
|
331
332
|
import process2 from "process";
|
|
332
333
|
import readline from "readline";
|
|
333
334
|
import { spawn as spawn2 } from "child_process";
|
|
334
|
-
|
|
335
|
-
prod
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
api: "https://api.trycanary.ai",
|
|
341
|
-
app: "https://app.trycanary.ai"
|
|
342
|
-
},
|
|
343
|
-
dev: {
|
|
344
|
-
api: "https://api.dev.trycanary.ai",
|
|
345
|
-
app: "https://app.dev.trycanary.ai"
|
|
346
|
-
},
|
|
347
|
-
local: {
|
|
348
|
-
api: "http://localhost:3000",
|
|
349
|
-
app: "http://localhost:5173"
|
|
350
|
-
}
|
|
351
|
-
};
|
|
335
|
+
function envToProfile(env) {
|
|
336
|
+
if (env === "prod" || env === "production") return "production";
|
|
337
|
+
if (env === "dev") return "dev";
|
|
338
|
+
if (env === "local") return "local";
|
|
339
|
+
return env;
|
|
340
|
+
}
|
|
352
341
|
function getArgValue(argv, key) {
|
|
353
342
|
const index = argv.indexOf(key);
|
|
354
343
|
if (index === -1) return void 0;
|
|
@@ -522,9 +511,11 @@ Choice [1-${orgs.length}]: `);
|
|
|
522
511
|
finalOrgName = selectedOrg.name;
|
|
523
512
|
}
|
|
524
513
|
}
|
|
525
|
-
const
|
|
514
|
+
const profileName = env ? envToProfile(env) : void 0;
|
|
515
|
+
const filePath = await saveAuth({ token: finalToken, apiUrl, orgId: finalOrgId, orgName: finalOrgName }, profileName);
|
|
526
516
|
const displayName = finalOrgName ? ` to ${finalOrgName}` : "";
|
|
527
|
-
|
|
517
|
+
const profileLabel = profileName ? ` (profile: ${profileName})` : "";
|
|
518
|
+
console.log(`Login successful${displayName}${profileLabel}. Token saved to ${filePath}`);
|
|
528
519
|
console.log("Set CANARY_API_TOKEN to use the CLI without re-login.");
|
|
529
520
|
}
|
|
530
521
|
|
|
@@ -965,8 +956,8 @@ function isSuperadminToken(token) {
|
|
|
965
956
|
// src/index.ts
|
|
966
957
|
var require2 = createRequire2(import.meta.url);
|
|
967
958
|
var pkg = require2("../package.json");
|
|
968
|
-
var loadMcp = () => import("./mcp-
|
|
969
|
-
var loadLocalBrowser = () => import("./local-browser-
|
|
959
|
+
var loadMcp = () => import("./mcp-I6FCGDDR.js").then((m) => m.runMcp);
|
|
960
|
+
var loadLocalBrowser = () => import("./local-browser-VOBIUIGT.js").then((m) => m.runLocalBrowser);
|
|
970
961
|
var canary = { run };
|
|
971
962
|
var baseDir = typeof __dirname !== "undefined" ? __dirname : path4.dirname(fileURLToPath2(import.meta.url));
|
|
972
963
|
var preloadPath = path4.join(baseDir, "runner", "preload.js");
|
|
@@ -1015,7 +1006,8 @@ function printHelp({ isSuperadmin }) {
|
|
|
1015
1006
|
" canary debug-session [--env dev|local] [--json] Create browser debug session",
|
|
1016
1007
|
" canary psql <query> [--json] Execute read-only SQL",
|
|
1017
1008
|
" canary redis <command> [--json] Execute read-only Redis commands",
|
|
1018
|
-
" canary feature-flag <sub-command> Manage feature flags"
|
|
1009
|
+
" canary feature-flag <sub-command> Manage feature flags",
|
|
1010
|
+
" canary knobs <sub-command> Manage knobs (global config)"
|
|
1019
1011
|
);
|
|
1020
1012
|
}
|
|
1021
1013
|
lines.push(
|
|
@@ -1062,7 +1054,14 @@ function printHelp({ isSuperadmin }) {
|
|
|
1062
1054
|
" create <name> [--description <text>] Create a flag",
|
|
1063
1055
|
" delete <name> Delete a flag and its gates",
|
|
1064
1056
|
" enable <name> --org <orgId> Enable for an org",
|
|
1065
|
-
" disable <name> --org <orgId> Disable for an org"
|
|
1057
|
+
" disable <name> --org <orgId> Disable for an org",
|
|
1058
|
+
"",
|
|
1059
|
+
"Knobs sub-commands:",
|
|
1060
|
+
" list List all knobs",
|
|
1061
|
+
" get <key> Get a knob value",
|
|
1062
|
+
" set <key> <value> --type <type> Set a knob value",
|
|
1063
|
+
" delete <key> Delete a knob",
|
|
1064
|
+
" toggle <key> Toggle a boolean knob"
|
|
1066
1065
|
);
|
|
1067
1066
|
}
|
|
1068
1067
|
lines.push(
|
|
@@ -1180,20 +1179,25 @@ async function main(argv) {
|
|
|
1180
1179
|
return;
|
|
1181
1180
|
}
|
|
1182
1181
|
if (command === "psql") {
|
|
1183
|
-
const { runPsql } = await import("./psql-
|
|
1182
|
+
const { runPsql } = await import("./psql-A3BADRQN.js");
|
|
1184
1183
|
await runPsql(rest);
|
|
1185
1184
|
return;
|
|
1186
1185
|
}
|
|
1187
1186
|
if (command === "redis") {
|
|
1188
|
-
const { runRedis } = await import("./redis-
|
|
1187
|
+
const { runRedis } = await import("./redis-N2DSDDQU.js");
|
|
1189
1188
|
await runRedis(rest);
|
|
1190
1189
|
return;
|
|
1191
1190
|
}
|
|
1192
1191
|
if (command === "feature-flag") {
|
|
1193
|
-
const { runFeatureFlag } = await import("./feature-flag-
|
|
1192
|
+
const { runFeatureFlag } = await import("./feature-flag-PN5IFFQR.js");
|
|
1194
1193
|
await runFeatureFlag(rest);
|
|
1195
1194
|
return;
|
|
1196
1195
|
}
|
|
1196
|
+
if (command === "knobs") {
|
|
1197
|
+
const { runKnobs } = await import("./knobs-DAG7HD2F.js");
|
|
1198
|
+
await runKnobs(rest);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1197
1201
|
console.log(`Unknown command "${command}".`);
|
|
1198
1202
|
const token = await resolveToken();
|
|
1199
1203
|
printHelp({ isSuperadmin: isSuperadminToken(token) });
|