@ainyc/canonry 1.5.1 → 1.7.0

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/assets/index.html CHANGED
@@ -8,7 +8,7 @@
8
8
  content="Canonry is the AINYC monitoring dashboard for answer visibility and technical readiness."
9
9
  />
10
10
  <title>Canonry</title>
11
- <script type="module" crossorigin src="/assets/index-OtrziEv9.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-Cgb-VMub.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-BxWGYuSH.css">
13
13
  </head>
14
14
  <body>
@@ -23,14 +23,23 @@ function loadConfig() {
23
23
  const configPath = getConfigPath();
24
24
  if (!fs.existsSync(configPath)) {
25
25
  throw new Error(
26
- `Config not found at ${configPath}. Run "canonry init" to create one.`
26
+ `Config not found at ${configPath}.
27
+ Run "canonry init" to set up interactively, or "canonry init --gemini-key <key>" for non-interactive setup.
28
+ For CI/Docker, use "canonry bootstrap" with env vars (GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY).`
27
29
  );
28
30
  }
29
31
  const raw = fs.readFileSync(configPath, "utf-8");
30
32
  const parsed = parse(raw);
31
33
  if (!parsed.apiUrl || !parsed.database || !parsed.apiKey) {
34
+ const missing = [
35
+ !parsed.apiUrl && "apiUrl",
36
+ !parsed.database && "database",
37
+ !parsed.apiKey && "apiKey"
38
+ ].filter(Boolean).join(", ");
32
39
  throw new Error(
33
- `Invalid config at ${configPath}. Required fields: apiUrl, database, apiKey`
40
+ `Invalid config at ${configPath} \u2014 missing: ${missing}.
41
+ These fields are auto-generated. Run "canonry init" (or "canonry init --gemini-key <key>" for non-interactive setup) to create a valid config.
42
+ Do not write config.yaml by hand; use "canonry init" or "canonry bootstrap" instead.`
34
43
  );
35
44
  }
36
45
  if (parsed.geminiApiKey && !parsed.providers?.gemini) {
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  saveConfig,
16
16
  showFirstRunNotice,
17
17
  trackEvent
18
- } from "./chunk-CKC26GOH.js";
18
+ } from "./chunk-2QG7TZ4A.js";
19
19
 
20
20
  // src/cli.ts
21
21
  import { parseArgs } from "util";
@@ -66,8 +66,9 @@ var bootstrapEnvSchema = z.object({
66
66
  LOCAL_API_KEY: z.string().optional(),
67
67
  LOCAL_MODEL: z.string().optional()
68
68
  });
69
- function getBootstrapEnv(source) {
70
- const parsed = bootstrapEnvSchema.parse(source);
69
+ function getBootstrapEnv(source, overrides) {
70
+ const filtered = overrides ? Object.fromEntries(Object.entries(overrides).filter(([, v]) => v != null)) : {};
71
+ const parsed = bootstrapEnvSchema.parse({ ...source, ...filtered });
71
72
  const providers = {};
72
73
  if (parsed.GEMINI_API_KEY) {
73
74
  providers.gemini = {
@@ -210,29 +211,45 @@ async function initCommand(opts) {
210
211
  if (!fs.existsSync(configDir)) {
211
212
  fs.mkdirSync(configDir, { recursive: true });
212
213
  }
214
+ const envProviders = getBootstrapEnv(process.env, {
215
+ GEMINI_API_KEY: opts?.geminiKey,
216
+ OPENAI_API_KEY: opts?.openaiKey,
217
+ ANTHROPIC_API_KEY: opts?.claudeKey,
218
+ LOCAL_BASE_URL: opts?.localUrl,
219
+ LOCAL_MODEL: opts?.localModel,
220
+ LOCAL_API_KEY: opts?.localKey
221
+ }).providers;
222
+ const nonInteractive = !!(envProviders.gemini || envProviders.openai || envProviders.claude || envProviders.local);
213
223
  const providers = {};
214
- console.log("Configure AI providers (at least one required):\n");
215
- const geminiApiKey = await prompt("Gemini API key (press Enter to skip): ");
216
- if (geminiApiKey) {
217
- const geminiModel = await prompt(" Gemini model [gemini-2.5-flash]: ") || "gemini-2.5-flash";
218
- providers.gemini = { apiKey: geminiApiKey, model: geminiModel, quota: DEFAULT_QUOTA };
219
- }
220
- const openaiApiKey = await prompt("OpenAI API key (press Enter to skip): ");
221
- if (openaiApiKey) {
222
- const openaiModel = await prompt(" OpenAI model [gpt-4o]: ") || "gpt-4o";
223
- providers.openai = { apiKey: openaiApiKey, model: openaiModel, quota: DEFAULT_QUOTA };
224
- }
225
- const claudeApiKey = await prompt("Anthropic API key (press Enter to skip): ");
226
- if (claudeApiKey) {
227
- const claudeModel = await prompt(" Claude model [claude-sonnet-4-6]: ") || "claude-sonnet-4-6";
228
- providers.claude = { apiKey: claudeApiKey, model: claudeModel, quota: DEFAULT_QUOTA };
229
- }
230
- console.log("\nLocal LLM (Ollama, LM Studio, llama.cpp, vLLM \u2014 any OpenAI-compatible API):");
231
- const localBaseUrl = await prompt("Local LLM base URL (press Enter to skip, e.g. http://localhost:11434/v1): ");
232
- if (localBaseUrl) {
233
- const localModel = await prompt(" Model name [llama3]: ") || "llama3";
234
- const localApiKey = await prompt(" API key (press Enter if not needed): ") || void 0;
235
- providers.local = { baseUrl: localBaseUrl, apiKey: localApiKey, model: localModel, quota: DEFAULT_QUOTA };
224
+ if (nonInteractive) {
225
+ Object.assign(providers, envProviders);
226
+ } else {
227
+ console.log("Configure AI providers (at least one required):\n");
228
+ console.log("Tip: For non-interactive setup, pass --gemini-key, --openai-key,");
229
+ console.log("--claude-key flags or set GEMINI_API_KEY, OPENAI_API_KEY,");
230
+ console.log('ANTHROPIC_API_KEY env vars. Or use "canonry bootstrap".\n');
231
+ const geminiApiKey = await prompt("Gemini API key (press Enter to skip): ");
232
+ if (geminiApiKey) {
233
+ const geminiModel = await prompt(" Gemini model [gemini-2.5-flash]: ") || "gemini-2.5-flash";
234
+ providers.gemini = { apiKey: geminiApiKey, model: geminiModel, quota: DEFAULT_QUOTA };
235
+ }
236
+ const openaiApiKey = await prompt("OpenAI API key (press Enter to skip): ");
237
+ if (openaiApiKey) {
238
+ const openaiModel = await prompt(" OpenAI model [gpt-4o]: ") || "gpt-4o";
239
+ providers.openai = { apiKey: openaiApiKey, model: openaiModel, quota: DEFAULT_QUOTA };
240
+ }
241
+ const claudeApiKey = await prompt("Anthropic API key (press Enter to skip): ");
242
+ if (claudeApiKey) {
243
+ const claudeModel = await prompt(" Claude model [claude-sonnet-4-6]: ") || "claude-sonnet-4-6";
244
+ providers.claude = { apiKey: claudeApiKey, model: claudeModel, quota: DEFAULT_QUOTA };
245
+ }
246
+ console.log("\nLocal LLM (Ollama, LM Studio, llama.cpp, vLLM \u2014 any OpenAI-compatible API):");
247
+ const localBaseUrl = await prompt("Local LLM base URL (press Enter to skip, e.g. http://localhost:11434/v1): ");
248
+ if (localBaseUrl) {
249
+ const localModel = await prompt(" Model name [llama3]: ") || "llama3";
250
+ const localApiKey = await prompt(" API key (press Enter if not needed): ") || void 0;
251
+ providers.local = { baseUrl: localBaseUrl, apiKey: localApiKey, model: localModel, quota: DEFAULT_QUOTA };
252
+ }
236
253
  }
237
254
  const hasProvider = providers.gemini || providers.openai || providers.claude || providers.local;
238
255
  if (!hasProvider) {
@@ -313,11 +330,22 @@ var ApiClient = class {
313
330
  "Authorization": `Bearer ${this.apiKey}`,
314
331
  "Content-Type": "application/json"
315
332
  };
316
- const res = await fetch(url, {
317
- method,
318
- headers,
319
- body: body != null ? JSON.stringify(body) : void 0
320
- });
333
+ let res;
334
+ try {
335
+ res = await fetch(url, {
336
+ method,
337
+ headers,
338
+ body: body != null ? JSON.stringify(body) : void 0
339
+ });
340
+ } catch (err) {
341
+ const msg = err instanceof Error ? err.message : String(err);
342
+ if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
343
+ throw new Error(
344
+ `Could not connect to canonry server at ${this.baseUrl.replace("/api/v1", "")}. Start it with "canonry serve" (or "canonry serve &" to run in background).`
345
+ );
346
+ }
347
+ throw err;
348
+ }
321
349
  if (!res.ok) {
322
350
  let errorBody;
323
351
  try {
@@ -956,7 +984,8 @@ var USAGE = `
956
984
  canonry \u2014 AEO monitoring CLI
957
985
 
958
986
  Usage:
959
- canonry init [--force] Initialize config and database
987
+ canonry init [--force] Initialize config and database (interactive)
988
+ canonry init --gemini-key <key> Initialize non-interactively (also reads env vars)
960
989
  canonry bootstrap [--force] Bootstrap config/database from env vars
961
990
  canonry serve Start the local server
962
991
  canonry project create <name> Create a project
@@ -995,6 +1024,12 @@ Usage:
995
1024
  canonry --version Show version
996
1025
 
997
1026
  Options:
1027
+ --gemini-key <key> Gemini API key (or GEMINI_API_KEY env var)
1028
+ --openai-key <key> OpenAI API key (or OPENAI_API_KEY env var)
1029
+ --claude-key <key> Anthropic API key (or ANTHROPIC_API_KEY env var)
1030
+ --local-url <url> Local LLM base URL (or LOCAL_BASE_URL env var)
1031
+ --local-model <name> Local LLM model name (default: llama3)
1032
+ --local-key <key> Local LLM API key (or LOCAL_API_KEY env var)
998
1033
  --port <port> Server port (default: 4100)
999
1034
  --host <host> Server bind address (default: 127.0.0.1)
1000
1035
  --domain <domain> Canonical domain for project create
@@ -1033,8 +1068,28 @@ async function main() {
1033
1068
  try {
1034
1069
  switch (command) {
1035
1070
  case "init": {
1036
- const initForce = args.includes("--force") || args.includes("-f");
1037
- await initCommand({ force: initForce });
1071
+ const { values: initValues } = parseArgs({
1072
+ args: args.slice(1),
1073
+ options: {
1074
+ force: { type: "boolean", short: "f", default: false },
1075
+ "gemini-key": { type: "string" },
1076
+ "openai-key": { type: "string" },
1077
+ "claude-key": { type: "string" },
1078
+ "local-url": { type: "string" },
1079
+ "local-model": { type: "string" },
1080
+ "local-key": { type: "string" }
1081
+ },
1082
+ allowPositionals: false
1083
+ });
1084
+ await initCommand({
1085
+ force: initValues.force,
1086
+ geminiKey: initValues["gemini-key"],
1087
+ openaiKey: initValues["openai-key"],
1088
+ claudeKey: initValues["claude-key"],
1089
+ localUrl: initValues["local-url"],
1090
+ localModel: initValues["local-model"],
1091
+ localKey: initValues["local-key"]
1092
+ });
1038
1093
  break;
1039
1094
  }
1040
1095
  case "bootstrap": {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createServer,
3
3
  loadConfig
4
- } from "./chunk-CKC26GOH.js";
4
+ } from "./chunk-2QG7TZ4A.js";
5
5
  export {
6
6
  createServer,
7
7
  loadConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "description": "The ultimate open-source AEO monitoring tool - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -52,12 +52,12 @@
52
52
  "tsup": "^8.5.1",
53
53
  "tsx": "^4.19.0",
54
54
  "@ainyc/canonry-api-routes": "0.0.0",
55
- "@ainyc/canonry-config": "0.0.0",
56
55
  "@ainyc/canonry-contracts": "0.0.0",
57
- "@ainyc/canonry-db": "0.0.0",
56
+ "@ainyc/canonry-config": "0.0.0",
58
57
  "@ainyc/canonry-provider-claude": "0.0.0",
59
- "@ainyc/canonry-provider-openai": "0.0.0",
60
58
  "@ainyc/canonry-provider-gemini": "0.0.0",
59
+ "@ainyc/canonry-db": "0.0.0",
60
+ "@ainyc/canonry-provider-openai": "0.0.0",
61
61
  "@ainyc/canonry-provider-local": "0.0.0"
62
62
  },
63
63
  "scripts": {