@ainyc/canonry 1.10.1 → 1.12.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/README.md +27 -3
- package/assets/apple-touch-icon.png +0 -0
- package/assets/assets/index-BEsueXzg.css +1 -0
- package/assets/assets/index-Bol7Z6qk.js +243 -0
- package/assets/favicon-32.png +0 -0
- package/assets/favicon.svg +24 -0
- package/assets/index.html +7 -3
- package/dist/{chunk-2DC7RBXJ.js → chunk-O4HLQBL7.js} +324 -106
- package/dist/cli.js +114 -17
- package/dist/index.d.ts +20 -0
- package/dist/index.js +1 -1
- package/package.json +6 -6
- package/assets/assets/index-BDoQOjXO.js +0 -243
- package/assets/assets/index-Dhvjw6Lo.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -16,9 +16,10 @@ import {
|
|
|
16
16
|
notificationEventSchema,
|
|
17
17
|
providerQuotaPolicySchema,
|
|
18
18
|
saveConfig,
|
|
19
|
+
setGoogleAuthConfig,
|
|
19
20
|
showFirstRunNotice,
|
|
20
21
|
trackEvent
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-O4HLQBL7.js";
|
|
22
23
|
|
|
23
24
|
// src/cli.ts
|
|
24
25
|
import { parseArgs } from "util";
|
|
@@ -159,6 +160,14 @@ async function bootstrapCommand(_opts) {
|
|
|
159
160
|
if (providers?.openai) mergedProviders.openai = providers.openai;
|
|
160
161
|
if (providers?.claude) mergedProviders.claude = providers.claude;
|
|
161
162
|
if (providers?.local) mergedProviders.local = providers.local;
|
|
163
|
+
if (env.googleClientId && !env.googleClientSecret || !env.googleClientId && env.googleClientSecret) {
|
|
164
|
+
console.warn("Warning: GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET must both be set to configure Google OAuth. Skipping Google auth config.");
|
|
165
|
+
}
|
|
166
|
+
const mergedGoogle = env.googleClientId && env.googleClientSecret ? {
|
|
167
|
+
clientId: env.googleClientId,
|
|
168
|
+
clientSecret: env.googleClientSecret,
|
|
169
|
+
connections: existingConfig?.google?.connections ?? []
|
|
170
|
+
} : existingConfig?.google;
|
|
162
171
|
const keyHash = crypto.createHash("sha256").update(rawApiKey).digest("hex");
|
|
163
172
|
const keyPrefix = rawApiKey.slice(0, 9);
|
|
164
173
|
const db = createClient(databasePath);
|
|
@@ -176,7 +185,8 @@ async function bootstrapCommand(_opts) {
|
|
|
176
185
|
apiUrl: env.apiUrl || existingConfig?.apiUrl || `http://localhost:${process.env.CANONRY_PORT || "4100"}`,
|
|
177
186
|
database: databasePath,
|
|
178
187
|
apiKey: rawApiKey,
|
|
179
|
-
providers: mergedProviders
|
|
188
|
+
providers: mergedProviders,
|
|
189
|
+
google: mergedGoogle
|
|
180
190
|
});
|
|
181
191
|
console.log(`Bootstrap complete. Config saved to ${getConfigPath()}`);
|
|
182
192
|
console.log(`SQLite database path: ${databasePath}`);
|
|
@@ -218,23 +228,40 @@ async function initCommand(opts) {
|
|
|
218
228
|
if (!fs.existsSync(configDir)) {
|
|
219
229
|
fs.mkdirSync(configDir, { recursive: true });
|
|
220
230
|
}
|
|
221
|
-
const
|
|
231
|
+
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
222
232
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
223
233
|
OPENAI_API_KEY: opts?.openaiKey,
|
|
224
234
|
ANTHROPIC_API_KEY: opts?.claudeKey,
|
|
225
235
|
LOCAL_BASE_URL: opts?.localUrl,
|
|
226
236
|
LOCAL_MODEL: opts?.localModel,
|
|
227
|
-
LOCAL_API_KEY: opts?.localKey
|
|
228
|
-
|
|
229
|
-
|
|
237
|
+
LOCAL_API_KEY: opts?.localKey,
|
|
238
|
+
GOOGLE_CLIENT_ID: opts?.googleClientId,
|
|
239
|
+
GOOGLE_CLIENT_SECRET: opts?.googleClientSecret
|
|
240
|
+
});
|
|
241
|
+
if (bootstrapEnv.googleClientId && !bootstrapEnv.googleClientSecret || !bootstrapEnv.googleClientId && bootstrapEnv.googleClientSecret) {
|
|
242
|
+
console.error("Google OAuth requires both a client ID and client secret when configured non-interactively.");
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
const envProviders = bootstrapEnv.providers;
|
|
246
|
+
const envGoogleConfigured = !!(bootstrapEnv.googleClientId && bootstrapEnv.googleClientSecret);
|
|
247
|
+
const nonInteractive = !!(envProviders.gemini || envProviders.openai || envProviders.claude || envProviders.local || envGoogleConfigured);
|
|
230
248
|
const providers = {};
|
|
249
|
+
let google;
|
|
231
250
|
if (nonInteractive) {
|
|
232
251
|
Object.assign(providers, envProviders);
|
|
252
|
+
if (envGoogleConfigured) {
|
|
253
|
+
google = {
|
|
254
|
+
clientId: bootstrapEnv.googleClientId,
|
|
255
|
+
clientSecret: bootstrapEnv.googleClientSecret,
|
|
256
|
+
connections: []
|
|
257
|
+
};
|
|
258
|
+
}
|
|
233
259
|
} else {
|
|
234
260
|
console.log("Configure AI providers (at least one required):\n");
|
|
235
|
-
console.log("Tip: For non-interactive setup, pass
|
|
236
|
-
console.log("
|
|
237
|
-
console.log(
|
|
261
|
+
console.log("Tip: For non-interactive setup, pass provider flags or set");
|
|
262
|
+
console.log("GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY,");
|
|
263
|
+
console.log("GOOGLE_CLIENT_ID, and GOOGLE_CLIENT_SECRET env vars.");
|
|
264
|
+
console.log('Or use "canonry bootstrap".\n');
|
|
238
265
|
const geminiApiKey = await prompt("Gemini API key (press Enter to skip): ");
|
|
239
266
|
if (geminiApiKey) {
|
|
240
267
|
const geminiModel = await prompt(" Gemini model [gemini-2.5-flash]: ") || "gemini-2.5-flash";
|
|
@@ -257,6 +284,20 @@ async function initCommand(opts) {
|
|
|
257
284
|
const localApiKey = await prompt(" API key (press Enter if not needed): ") || void 0;
|
|
258
285
|
providers.local = { baseUrl: localBaseUrl, apiKey: localApiKey, model: localModel, quota: DEFAULT_QUOTA };
|
|
259
286
|
}
|
|
287
|
+
console.log("\nGoogle Search Console OAuth (optional):");
|
|
288
|
+
const googleClientId = await prompt("Google OAuth client ID (press Enter to skip): ");
|
|
289
|
+
if (googleClientId) {
|
|
290
|
+
const googleClientSecret = await prompt(" Google OAuth client secret: ");
|
|
291
|
+
if (!googleClientSecret) {
|
|
292
|
+
console.error("\nGoogle OAuth client secret is required when a client ID is provided.");
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
google = {
|
|
296
|
+
clientId: googleClientId,
|
|
297
|
+
clientSecret: googleClientSecret,
|
|
298
|
+
connections: []
|
|
299
|
+
};
|
|
300
|
+
}
|
|
260
301
|
}
|
|
261
302
|
const hasProvider = providers.gemini || providers.openai || providers.claude || providers.local;
|
|
262
303
|
if (!hasProvider) {
|
|
@@ -281,7 +322,8 @@ async function initCommand(opts) {
|
|
|
281
322
|
apiUrl: "http://localhost:4100",
|
|
282
323
|
database: databasePath,
|
|
283
324
|
apiKey: rawApiKey,
|
|
284
|
-
providers
|
|
325
|
+
providers,
|
|
326
|
+
google
|
|
285
327
|
});
|
|
286
328
|
const providerNames = Object.keys(providers);
|
|
287
329
|
console.log(`
|
|
@@ -1109,9 +1151,15 @@ async function setProvider(name, opts) {
|
|
|
1109
1151
|
}
|
|
1110
1152
|
async function showSettings(format) {
|
|
1111
1153
|
const client = getClient8();
|
|
1154
|
+
const config = loadConfig();
|
|
1112
1155
|
const settings = await client.getSettings();
|
|
1113
1156
|
if (format === "json") {
|
|
1114
|
-
console.log(JSON.stringify(
|
|
1157
|
+
console.log(JSON.stringify({
|
|
1158
|
+
...settings,
|
|
1159
|
+
google: {
|
|
1160
|
+
configured: Boolean(config.google?.clientId && config.google?.clientSecret)
|
|
1161
|
+
}
|
|
1162
|
+
}, null, 2));
|
|
1115
1163
|
return;
|
|
1116
1164
|
}
|
|
1117
1165
|
console.log("Provider settings:\n");
|
|
@@ -1125,6 +1173,18 @@ async function showSettings(format) {
|
|
|
1125
1173
|
}
|
|
1126
1174
|
}
|
|
1127
1175
|
}
|
|
1176
|
+
console.log("\nGoogle OAuth:\n");
|
|
1177
|
+
console.log(` ${config.google?.clientId && config.google?.clientSecret ? "configured" : "not configured"}`);
|
|
1178
|
+
}
|
|
1179
|
+
function setGoogleAuth(opts) {
|
|
1180
|
+
const config = loadConfig();
|
|
1181
|
+
setGoogleAuthConfig(config, {
|
|
1182
|
+
clientId: opts.clientId,
|
|
1183
|
+
clientSecret: opts.clientSecret
|
|
1184
|
+
});
|
|
1185
|
+
saveConfig(config);
|
|
1186
|
+
console.log(`Google OAuth credentials saved to ${getConfigPath()}.`);
|
|
1187
|
+
console.log("Restart the local server if it is already running.");
|
|
1128
1188
|
}
|
|
1129
1189
|
|
|
1130
1190
|
// src/commands/schedule.ts
|
|
@@ -1331,12 +1391,19 @@ function getClient11() {
|
|
|
1331
1391
|
}
|
|
1332
1392
|
async function googleConnect(project, opts) {
|
|
1333
1393
|
const client = getClient11();
|
|
1334
|
-
const { authUrl } = await client.googleConnect(project, {
|
|
1394
|
+
const { authUrl, redirectUri } = await client.googleConnect(project, {
|
|
1395
|
+
type: opts.type,
|
|
1396
|
+
publicUrl: opts.publicUrl
|
|
1397
|
+
});
|
|
1335
1398
|
console.log(`
|
|
1336
1399
|
Open this URL in your browser to authorize Google ${opts.type.toUpperCase()} access:
|
|
1337
1400
|
`);
|
|
1338
1401
|
console.log(` ${authUrl}
|
|
1339
1402
|
`);
|
|
1403
|
+
if (redirectUri) {
|
|
1404
|
+
console.log(`Redirect URI: ${redirectUri}`);
|
|
1405
|
+
console.log("(Ensure this URI is listed in your Google Cloud Console OAuth client's authorized redirect URIs)\n");
|
|
1406
|
+
}
|
|
1340
1407
|
try {
|
|
1341
1408
|
const { exec } = await import("child_process");
|
|
1342
1409
|
const platform = process.platform;
|
|
@@ -1578,7 +1645,7 @@ Usage:
|
|
|
1578
1645
|
canonry notify remove <project> <id> Remove notification
|
|
1579
1646
|
canonry notify test <project> <id> Send test webhook
|
|
1580
1647
|
canonry notify events List available notification event types
|
|
1581
|
-
canonry google connect <project> Connect Google Search Console (--type gsc|ga4)
|
|
1648
|
+
canonry google connect <project> Connect Google Search Console (--type gsc|ga4, --public-url <url>)
|
|
1582
1649
|
canonry google disconnect <project> Disconnect Google integration
|
|
1583
1650
|
canonry google status <project> Show Google connection status
|
|
1584
1651
|
canonry google properties <project> List available GSC properties
|
|
@@ -1590,6 +1657,7 @@ Usage:
|
|
|
1590
1657
|
canonry google deindexed <project> Show pages that lost indexing
|
|
1591
1658
|
canonry settings Show active provider and quota settings
|
|
1592
1659
|
canonry settings provider <name> Update a provider config
|
|
1660
|
+
canonry settings google Update Google OAuth credentials
|
|
1593
1661
|
canonry telemetry status Show telemetry status
|
|
1594
1662
|
canonry telemetry enable Enable anonymous telemetry
|
|
1595
1663
|
canonry telemetry disable Disable anonymous telemetry
|
|
@@ -1603,6 +1671,8 @@ Options:
|
|
|
1603
1671
|
--local-url <url> Local LLM base URL (or LOCAL_BASE_URL env var)
|
|
1604
1672
|
--local-model <name> Local LLM model name (default: llama3)
|
|
1605
1673
|
--local-key <key> Local LLM API key (or LOCAL_API_KEY env var)
|
|
1674
|
+
--google-client-id <id> Google OAuth client ID (or GOOGLE_CLIENT_ID env var)
|
|
1675
|
+
--google-client-secret <key> Google OAuth client secret (or GOOGLE_CLIENT_SECRET env var)
|
|
1606
1676
|
--port <port> Server port (default: 4100)
|
|
1607
1677
|
--host <host> Server bind address (default: 127.0.0.1)
|
|
1608
1678
|
--domain <domain> Canonical domain for project create/update
|
|
@@ -1625,6 +1695,8 @@ Options:
|
|
|
1625
1695
|
--api-key <key> Provider API key (settings provider)
|
|
1626
1696
|
--base-url <url> Provider base URL (settings provider)
|
|
1627
1697
|
--model <name> Provider model name (settings provider)
|
|
1698
|
+
--client-id <id> Google OAuth client ID (settings google)
|
|
1699
|
+
--client-secret <key> Google OAuth client secret (settings google)
|
|
1628
1700
|
--max-concurrent <n> Max concurrent requests per provider
|
|
1629
1701
|
--max-per-minute <n> Max requests per minute per provider
|
|
1630
1702
|
--max-per-day <n> Max requests per day per provider
|
|
@@ -1669,7 +1741,9 @@ async function main() {
|
|
|
1669
1741
|
"claude-key": { type: "string" },
|
|
1670
1742
|
"local-url": { type: "string" },
|
|
1671
1743
|
"local-model": { type: "string" },
|
|
1672
|
-
"local-key": { type: "string" }
|
|
1744
|
+
"local-key": { type: "string" },
|
|
1745
|
+
"google-client-id": { type: "string" },
|
|
1746
|
+
"google-client-secret": { type: "string" }
|
|
1673
1747
|
},
|
|
1674
1748
|
allowPositionals: false
|
|
1675
1749
|
});
|
|
@@ -1680,7 +1754,9 @@ async function main() {
|
|
|
1680
1754
|
claudeKey: initValues["claude-key"],
|
|
1681
1755
|
localUrl: initValues["local-url"],
|
|
1682
1756
|
localModel: initValues["local-model"],
|
|
1683
|
-
localKey: initValues["local-key"]
|
|
1757
|
+
localKey: initValues["local-key"],
|
|
1758
|
+
googleClientId: initValues["google-client-id"],
|
|
1759
|
+
googleClientSecret: initValues["google-client-secret"]
|
|
1684
1760
|
});
|
|
1685
1761
|
break;
|
|
1686
1762
|
}
|
|
@@ -2171,6 +2247,23 @@ async function main() {
|
|
|
2171
2247
|
model: values.model,
|
|
2172
2248
|
quota: Object.keys(quota).length > 0 ? quota : void 0
|
|
2173
2249
|
});
|
|
2250
|
+
} else if (subcommand === "google") {
|
|
2251
|
+
const { values } = parseArgs({
|
|
2252
|
+
args: args.slice(2),
|
|
2253
|
+
options: {
|
|
2254
|
+
"client-id": { type: "string" },
|
|
2255
|
+
"client-secret": { type: "string" }
|
|
2256
|
+
},
|
|
2257
|
+
allowPositionals: false
|
|
2258
|
+
});
|
|
2259
|
+
if (!values["client-id"] || !values["client-secret"]) {
|
|
2260
|
+
console.error("Error: --client-id and --client-secret are both required");
|
|
2261
|
+
process.exit(1);
|
|
2262
|
+
}
|
|
2263
|
+
setGoogleAuth({
|
|
2264
|
+
clientId: values["client-id"],
|
|
2265
|
+
clientSecret: values["client-secret"]
|
|
2266
|
+
});
|
|
2174
2267
|
} else {
|
|
2175
2268
|
await showSettings(format);
|
|
2176
2269
|
}
|
|
@@ -2192,11 +2285,15 @@ async function main() {
|
|
|
2192
2285
|
const { values: connectValues } = parseArgs({
|
|
2193
2286
|
args: args.slice(3),
|
|
2194
2287
|
options: {
|
|
2195
|
-
type: { type: "string", default: "gsc" }
|
|
2288
|
+
type: { type: "string", default: "gsc" },
|
|
2289
|
+
"public-url": { type: "string" }
|
|
2196
2290
|
},
|
|
2197
2291
|
allowPositionals: false
|
|
2198
2292
|
});
|
|
2199
|
-
await googleConnect(project, {
|
|
2293
|
+
await googleConnect(project, {
|
|
2294
|
+
type: connectValues.type ?? "gsc",
|
|
2295
|
+
publicUrl: connectValues["public-url"]
|
|
2296
|
+
});
|
|
2200
2297
|
break;
|
|
2201
2298
|
}
|
|
2202
2299
|
case "disconnect": {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,14 +2,32 @@ import { FastifyInstance } from 'fastify';
|
|
|
2
2
|
import { DatabaseClient } from '@ainyc/canonry-db';
|
|
3
3
|
import { ProviderQuotaPolicy } from '@ainyc/canonry-contracts';
|
|
4
4
|
|
|
5
|
+
type GoogleConnectionType = 'gsc' | 'ga4';
|
|
5
6
|
interface ProviderConfigEntry {
|
|
6
7
|
apiKey?: string;
|
|
7
8
|
baseUrl?: string;
|
|
8
9
|
model?: string;
|
|
9
10
|
quota?: ProviderQuotaPolicy;
|
|
10
11
|
}
|
|
12
|
+
interface GoogleConnectionConfigEntry {
|
|
13
|
+
domain: string;
|
|
14
|
+
connectionType: GoogleConnectionType;
|
|
15
|
+
propertyId?: string | null;
|
|
16
|
+
accessToken?: string;
|
|
17
|
+
refreshToken?: string | null;
|
|
18
|
+
tokenExpiresAt?: string | null;
|
|
19
|
+
scopes?: string[];
|
|
20
|
+
createdAt: string;
|
|
21
|
+
updatedAt: string;
|
|
22
|
+
}
|
|
23
|
+
interface GoogleConfigEntry {
|
|
24
|
+
clientId?: string;
|
|
25
|
+
clientSecret?: string;
|
|
26
|
+
connections?: GoogleConnectionConfigEntry[];
|
|
27
|
+
}
|
|
11
28
|
interface CanonryConfig {
|
|
12
29
|
apiUrl: string;
|
|
30
|
+
publicUrl?: string;
|
|
13
31
|
database: string;
|
|
14
32
|
apiKey: string;
|
|
15
33
|
port?: number;
|
|
@@ -22,6 +40,7 @@ interface CanonryConfig {
|
|
|
22
40
|
claude?: ProviderConfigEntry;
|
|
23
41
|
local?: ProviderConfigEntry;
|
|
24
42
|
};
|
|
43
|
+
google?: GoogleConfigEntry;
|
|
25
44
|
telemetry?: boolean;
|
|
26
45
|
anonymousId?: string;
|
|
27
46
|
}
|
|
@@ -31,6 +50,7 @@ declare function createServer(opts: {
|
|
|
31
50
|
config: CanonryConfig;
|
|
32
51
|
db: DatabaseClient;
|
|
33
52
|
open?: boolean;
|
|
53
|
+
logger?: boolean;
|
|
34
54
|
}): Promise<FastifyInstance>;
|
|
35
55
|
|
|
36
56
|
export { type CanonryConfig, createServer, loadConfig };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainyc/canonry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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",
|
|
@@ -51,15 +51,15 @@
|
|
|
51
51
|
"@types/node-cron": "^3.0.11",
|
|
52
52
|
"tsup": "^8.5.1",
|
|
53
53
|
"tsx": "^4.19.0",
|
|
54
|
-
"@ainyc/canonry-config": "0.0.0",
|
|
55
54
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
55
|
+
"@ainyc/canonry-config": "0.0.0",
|
|
56
56
|
"@ainyc/canonry-contracts": "0.0.0",
|
|
57
57
|
"@ainyc/canonry-db": "0.0.0",
|
|
58
|
-
"@ainyc/canonry-provider-local": "0.0.0",
|
|
59
|
-
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
60
58
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
61
|
-
"@ainyc/canonry-
|
|
62
|
-
"@ainyc/canonry-provider-
|
|
59
|
+
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
60
|
+
"@ainyc/canonry-provider-local": "0.0.0",
|
|
61
|
+
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
62
|
+
"@ainyc/canonry-integration-google": "0.0.0"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "tsup && tsx build-web.ts",
|