@ainyc/canonry 1.26.0 → 1.27.1
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-L6TL4UQI.js → chunk-UTRZ3UB5.js} +193 -39
- package/dist/cli.js +75 -40
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1 -1
- package/package.json +6 -5
|
@@ -72,6 +72,10 @@ Do not write config.yaml by hand; use "canonry init", "canonry settings", or "ca
|
|
|
72
72
|
} catch {
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
+
if ("CANONRY_BASE_PATH" in process.env) {
|
|
76
|
+
const val = process.env.CANONRY_BASE_PATH.trim();
|
|
77
|
+
parsed.basePath = val || void 0;
|
|
78
|
+
}
|
|
75
79
|
if (parsed.basePath) {
|
|
76
80
|
const normalizedBase = "/" + parsed.basePath.replace(/^\/|\/$/g, "");
|
|
77
81
|
try {
|
|
@@ -5047,6 +5051,14 @@ async function settingsRoutes(app, opts) {
|
|
|
5047
5051
|
const err = validationError("baseUrl is required for local provider");
|
|
5048
5052
|
return reply.status(err.statusCode).send(err.toJSON());
|
|
5049
5053
|
}
|
|
5054
|
+
} else if (name === "gemini" && !apiKey) {
|
|
5055
|
+
const geminiSummary = (opts.providerSummary ?? []).find((p) => p.name === "gemini");
|
|
5056
|
+
if (!geminiSummary?.vertexConfigured) {
|
|
5057
|
+
const err = validationError(
|
|
5058
|
+
"apiKey is required for Gemini unless Vertex AI is configured (set GEMINI_VERTEX_PROJECT env var or vertexProject in config file)"
|
|
5059
|
+
);
|
|
5060
|
+
return reply.status(err.statusCode).send(err.toJSON());
|
|
5061
|
+
}
|
|
5050
5062
|
} else {
|
|
5051
5063
|
if (!apiKey || typeof apiKey !== "string") {
|
|
5052
5064
|
const err = validationError("apiKey is required");
|
|
@@ -7627,18 +7639,58 @@ async function apiRoutes(app, opts) {
|
|
|
7627
7639
|
|
|
7628
7640
|
// ../provider-gemini/src/normalize.ts
|
|
7629
7641
|
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
7642
|
+
import { VertexAI } from "@google-cloud/vertexai";
|
|
7630
7643
|
var DEFAULT_MODEL = "gemini-3-flash";
|
|
7631
7644
|
var VALIDATION_PATTERN = /^gemini-/;
|
|
7645
|
+
function isVertexConfig(config) {
|
|
7646
|
+
return !!config.vertexProject;
|
|
7647
|
+
}
|
|
7632
7648
|
function resolveModel(config) {
|
|
7633
7649
|
const m = config.model;
|
|
7634
7650
|
if (!m) return DEFAULT_MODEL;
|
|
7635
7651
|
if (VALIDATION_PATTERN.test(m)) return m;
|
|
7652
|
+
const backend = isVertexConfig(config) ? "Vertex AI" : "AI Studio";
|
|
7636
7653
|
console.warn(
|
|
7637
|
-
`[provider-gemini] Invalid model name "${m}" \u2014 this provider uses the Gemini
|
|
7654
|
+
`[provider-gemini] Invalid model name "${m}" \u2014 this provider uses the Gemini ${backend} API which only accepts "gemini-*" model names. Falling back to ${DEFAULT_MODEL}.`
|
|
7638
7655
|
);
|
|
7639
7656
|
return DEFAULT_MODEL;
|
|
7640
7657
|
}
|
|
7658
|
+
function wrapVertexResponse(response) {
|
|
7659
|
+
return {
|
|
7660
|
+
text: () => {
|
|
7661
|
+
const parts = response.candidates?.[0]?.content?.parts;
|
|
7662
|
+
return parts?.map((p) => p.text ?? "").join("") ?? "";
|
|
7663
|
+
},
|
|
7664
|
+
candidates: response.candidates?.map((c) => ({
|
|
7665
|
+
content: c.content,
|
|
7666
|
+
finishReason: c.finishReason,
|
|
7667
|
+
groundingMetadata: c.groundingMetadata
|
|
7668
|
+
})),
|
|
7669
|
+
usageMetadata: response.usageMetadata
|
|
7670
|
+
};
|
|
7671
|
+
}
|
|
7672
|
+
function createVertexModel(config, model, tools) {
|
|
7673
|
+
const vertexOpts = {
|
|
7674
|
+
project: config.vertexProject,
|
|
7675
|
+
location: config.vertexRegion || "us-central1",
|
|
7676
|
+
googleAuthOptions: config.vertexCredentials ? { keyFilename: config.vertexCredentials } : void 0
|
|
7677
|
+
};
|
|
7678
|
+
const vertexAI = new VertexAI(vertexOpts);
|
|
7679
|
+
return vertexAI.getGenerativeModel({
|
|
7680
|
+
model,
|
|
7681
|
+
...tools ? { tools } : {}
|
|
7682
|
+
});
|
|
7683
|
+
}
|
|
7641
7684
|
function validateConfig(config) {
|
|
7685
|
+
if (isVertexConfig(config)) {
|
|
7686
|
+
const model2 = resolveModel(config);
|
|
7687
|
+
return {
|
|
7688
|
+
ok: true,
|
|
7689
|
+
provider: "gemini",
|
|
7690
|
+
message: "config valid (Vertex AI)",
|
|
7691
|
+
model: model2
|
|
7692
|
+
};
|
|
7693
|
+
}
|
|
7642
7694
|
if (!config.apiKey || config.apiKey.length === 0) {
|
|
7643
7695
|
return { ok: false, provider: "gemini", message: "missing api key" };
|
|
7644
7696
|
}
|
|
@@ -7655,16 +7707,29 @@ async function healthcheck(config) {
|
|
|
7655
7707
|
const validation = validateConfig(config);
|
|
7656
7708
|
if (!validation.ok) return validation;
|
|
7657
7709
|
try {
|
|
7658
|
-
const
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7710
|
+
const model = resolveModel(config);
|
|
7711
|
+
if (isVertexConfig(config)) {
|
|
7712
|
+
const generativeModel = createVertexModel(config, model);
|
|
7713
|
+
const result = await generativeModel.generateContent('Say "ok"');
|
|
7714
|
+
const text2 = result.response.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join("") ?? "";
|
|
7715
|
+
return {
|
|
7716
|
+
ok: text2.length > 0,
|
|
7717
|
+
provider: "gemini",
|
|
7718
|
+
message: text2.length > 0 ? "gemini vertex ai verified" : "empty response from gemini vertex ai",
|
|
7719
|
+
model
|
|
7720
|
+
};
|
|
7721
|
+
} else {
|
|
7722
|
+
const genAI = new GoogleGenerativeAI(config.apiKey);
|
|
7723
|
+
const generativeModel = genAI.getGenerativeModel({ model });
|
|
7724
|
+
const result = await generativeModel.generateContent('Say "ok"');
|
|
7725
|
+
const text2 = result.response.text();
|
|
7726
|
+
return {
|
|
7727
|
+
ok: text2.length > 0,
|
|
7728
|
+
provider: "gemini",
|
|
7729
|
+
message: text2.length > 0 ? "gemini api key verified" : "empty response from gemini",
|
|
7730
|
+
model
|
|
7731
|
+
};
|
|
7732
|
+
}
|
|
7668
7733
|
} catch (err) {
|
|
7669
7734
|
return {
|
|
7670
7735
|
ok: false,
|
|
@@ -7676,23 +7741,41 @@ async function healthcheck(config) {
|
|
|
7676
7741
|
}
|
|
7677
7742
|
async function executeTrackedQuery(input) {
|
|
7678
7743
|
const model = resolveModel(input.config);
|
|
7679
|
-
const genAI = new GoogleGenerativeAI(input.config.apiKey);
|
|
7680
|
-
const generativeModel = genAI.getGenerativeModel({
|
|
7681
|
-
model,
|
|
7682
|
-
tools: [{ googleSearch: {} }]
|
|
7683
|
-
});
|
|
7684
7744
|
const prompt = buildPrompt(input.keyword, input.location);
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7745
|
+
if (isVertexConfig(input.config)) {
|
|
7746
|
+
const vertexSearchTool = { googleSearchRetrieval: {} };
|
|
7747
|
+
const generativeModel = createVertexModel(input.config, model, [vertexSearchTool]);
|
|
7748
|
+
const result = await generativeModel.generateContent(prompt);
|
|
7749
|
+
const response = result.response;
|
|
7750
|
+
const unified = wrapVertexResponse(response);
|
|
7751
|
+
const groundingMetadata = extractGroundingMetadataFromUnified(unified);
|
|
7752
|
+
const searchQueries = extractSearchQueriesFromUnified(unified);
|
|
7753
|
+
return {
|
|
7754
|
+
provider: "gemini",
|
|
7755
|
+
rawResponse: unifiedToRecord(unified),
|
|
7756
|
+
model,
|
|
7757
|
+
groundingSources: groundingMetadata,
|
|
7758
|
+
searchQueries
|
|
7759
|
+
};
|
|
7760
|
+
} else {
|
|
7761
|
+
const searchTool = { googleSearch: {} };
|
|
7762
|
+
const genAI = new GoogleGenerativeAI(input.config.apiKey);
|
|
7763
|
+
const generativeModel = genAI.getGenerativeModel({
|
|
7764
|
+
model,
|
|
7765
|
+
tools: [searchTool]
|
|
7766
|
+
});
|
|
7767
|
+
const result = await generativeModel.generateContent(prompt);
|
|
7768
|
+
const response = result.response;
|
|
7769
|
+
const groundingMetadata = extractGroundingMetadata(response);
|
|
7770
|
+
const searchQueries = extractSearchQueries(response);
|
|
7771
|
+
return {
|
|
7772
|
+
provider: "gemini",
|
|
7773
|
+
rawResponse: responseToRecord(response),
|
|
7774
|
+
model,
|
|
7775
|
+
groundingSources: groundingMetadata,
|
|
7776
|
+
searchQueries
|
|
7777
|
+
};
|
|
7778
|
+
}
|
|
7696
7779
|
}
|
|
7697
7780
|
function normalizeResult(raw) {
|
|
7698
7781
|
const answerText = extractAnswerText(raw.rawResponse);
|
|
@@ -7738,6 +7821,22 @@ function extractGroundingMetadata(response) {
|
|
|
7738
7821
|
return [];
|
|
7739
7822
|
}
|
|
7740
7823
|
}
|
|
7824
|
+
function extractGroundingMetadataFromUnified(response) {
|
|
7825
|
+
try {
|
|
7826
|
+
const candidate = response.candidates?.[0];
|
|
7827
|
+
if (!candidate) return [];
|
|
7828
|
+
const metadata = candidate.groundingMetadata;
|
|
7829
|
+
if (!metadata) return [];
|
|
7830
|
+
const chunks = metadata.groundingChunks;
|
|
7831
|
+
if (!chunks) return [];
|
|
7832
|
+
return chunks.filter((chunk) => chunk.web?.uri).map((chunk) => ({
|
|
7833
|
+
uri: chunk.web.uri,
|
|
7834
|
+
title: chunk.web?.title ?? ""
|
|
7835
|
+
}));
|
|
7836
|
+
} catch {
|
|
7837
|
+
return [];
|
|
7838
|
+
}
|
|
7839
|
+
}
|
|
7741
7840
|
function extractSearchQueries(response) {
|
|
7742
7841
|
try {
|
|
7743
7842
|
const candidate = response.candidates?.[0];
|
|
@@ -7746,6 +7845,14 @@ function extractSearchQueries(response) {
|
|
|
7746
7845
|
return [];
|
|
7747
7846
|
}
|
|
7748
7847
|
}
|
|
7848
|
+
function extractSearchQueriesFromUnified(response) {
|
|
7849
|
+
try {
|
|
7850
|
+
const candidate = response.candidates?.[0];
|
|
7851
|
+
return candidate?.groundingMetadata?.webSearchQueries ?? [];
|
|
7852
|
+
} catch {
|
|
7853
|
+
return [];
|
|
7854
|
+
}
|
|
7855
|
+
}
|
|
7749
7856
|
function extractCitedDomains(raw) {
|
|
7750
7857
|
const domains = /* @__PURE__ */ new Set();
|
|
7751
7858
|
for (const source of raw.groundingSources) {
|
|
@@ -7793,10 +7900,16 @@ function extractDomainFromUri(uri) {
|
|
|
7793
7900
|
}
|
|
7794
7901
|
async function generateText(prompt, config) {
|
|
7795
7902
|
const model = resolveModel(config);
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7903
|
+
if (isVertexConfig(config)) {
|
|
7904
|
+
const generativeModel = createVertexModel(config, model);
|
|
7905
|
+
const result = await generativeModel.generateContent(prompt);
|
|
7906
|
+
return result.response.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join("") ?? "";
|
|
7907
|
+
} else {
|
|
7908
|
+
const genAI = new GoogleGenerativeAI(config.apiKey);
|
|
7909
|
+
const generativeModel = genAI.getGenerativeModel({ model });
|
|
7910
|
+
const result = await generativeModel.generateContent(prompt);
|
|
7911
|
+
return result.response.text();
|
|
7912
|
+
}
|
|
7800
7913
|
}
|
|
7801
7914
|
function responseToRecord(response) {
|
|
7802
7915
|
try {
|
|
@@ -7816,13 +7929,34 @@ function responseToRecord(response) {
|
|
|
7816
7929
|
return { error: "failed to serialize response" };
|
|
7817
7930
|
}
|
|
7818
7931
|
}
|
|
7932
|
+
function unifiedToRecord(response) {
|
|
7933
|
+
try {
|
|
7934
|
+
const candidates = response.candidates?.map((c) => ({
|
|
7935
|
+
content: c.content,
|
|
7936
|
+
finishReason: c.finishReason,
|
|
7937
|
+
groundingMetadata: c.groundingMetadata ? {
|
|
7938
|
+
webSearchQueries: c.groundingMetadata.webSearchQueries,
|
|
7939
|
+
groundingChunks: c.groundingMetadata.groundingChunks
|
|
7940
|
+
} : void 0
|
|
7941
|
+
}));
|
|
7942
|
+
return {
|
|
7943
|
+
candidates: candidates ?? [],
|
|
7944
|
+
usageMetadata: response.usageMetadata ?? null
|
|
7945
|
+
};
|
|
7946
|
+
} catch {
|
|
7947
|
+
return { error: "failed to serialize response" };
|
|
7948
|
+
}
|
|
7949
|
+
}
|
|
7819
7950
|
|
|
7820
7951
|
// ../provider-gemini/src/adapter.ts
|
|
7821
7952
|
function toGeminiConfig(config) {
|
|
7822
7953
|
return {
|
|
7823
7954
|
apiKey: config.apiKey ?? "",
|
|
7824
7955
|
model: config.model,
|
|
7825
|
-
quotaPolicy: config.quotaPolicy
|
|
7956
|
+
quotaPolicy: config.quotaPolicy,
|
|
7957
|
+
vertexProject: config.vertexProject,
|
|
7958
|
+
vertexRegion: config.vertexRegion,
|
|
7959
|
+
vertexCredentials: config.vertexCredentials
|
|
7826
7960
|
};
|
|
7827
7961
|
}
|
|
7828
7962
|
var geminiAdapter = {
|
|
@@ -10872,19 +11006,22 @@ async function createServer(opts) {
|
|
|
10872
11006
|
}
|
|
10873
11007
|
log6.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
|
|
10874
11008
|
const p = providers[k];
|
|
10875
|
-
return p?.apiKey || p?.baseUrl;
|
|
11009
|
+
return p?.apiKey || p?.baseUrl || p?.vertexProject;
|
|
10876
11010
|
}) });
|
|
10877
11011
|
for (const adapter of API_ADAPTERS) {
|
|
10878
11012
|
const entry = providers[adapter.name];
|
|
10879
11013
|
if (!entry) continue;
|
|
10880
|
-
const isConfigured = adapter.name === "local" ? !!entry.baseUrl : !!entry.apiKey;
|
|
11014
|
+
const isConfigured = adapter.name === "local" ? !!entry.baseUrl : adapter.name === "gemini" ? !!(entry.apiKey || entry.vertexProject) : !!entry.apiKey;
|
|
10881
11015
|
if (isConfigured) {
|
|
10882
11016
|
registry.register(adapter, {
|
|
10883
11017
|
provider: adapter.name,
|
|
10884
11018
|
apiKey: entry.apiKey,
|
|
10885
11019
|
baseUrl: entry.baseUrl,
|
|
10886
11020
|
model: entry.model,
|
|
10887
|
-
quotaPolicy: entry.quota ?? DEFAULT_QUOTA
|
|
11021
|
+
quotaPolicy: entry.quota ?? DEFAULT_QUOTA,
|
|
11022
|
+
vertexProject: entry.vertexProject,
|
|
11023
|
+
vertexRegion: entry.vertexRegion,
|
|
11024
|
+
vertexCredentials: entry.vertexCredentials
|
|
10888
11025
|
});
|
|
10889
11026
|
}
|
|
10890
11027
|
}
|
|
@@ -10918,7 +11055,8 @@ async function createServer(opts) {
|
|
|
10918
11055
|
modelHint: `e.g. ${adapter.modelRegistry.defaultModel}`,
|
|
10919
11056
|
model: registry.get(adapter.name)?.config.model,
|
|
10920
11057
|
configured: !!registry.get(adapter.name),
|
|
10921
|
-
quota: registry.get(adapter.name)?.config.quotaPolicy
|
|
11058
|
+
quota: registry.get(adapter.name)?.config.quotaPolicy,
|
|
11059
|
+
vertexConfigured: adapter.name === "gemini" ? !!opts.config.providers?.gemini?.vertexProject : void 0
|
|
10922
11060
|
}));
|
|
10923
11061
|
const googleSettingsSummary = {
|
|
10924
11062
|
configured: Boolean(opts.config.google?.clientId && opts.config.google?.clientSecret)
|
|
@@ -11212,7 +11350,12 @@ async function createServer(opts) {
|
|
|
11212
11350
|
apiKey: apiKey || existing?.apiKey,
|
|
11213
11351
|
baseUrl: baseUrl || existing?.baseUrl,
|
|
11214
11352
|
model: model || existing?.model,
|
|
11215
|
-
quota: mergedQuota
|
|
11353
|
+
quota: mergedQuota,
|
|
11354
|
+
// Preserve Vertex AI config (Gemini provider) — these are set via
|
|
11355
|
+
// config file or env vars, not through the dashboard update payload
|
|
11356
|
+
vertexProject: existing?.vertexProject,
|
|
11357
|
+
vertexRegion: existing?.vertexRegion,
|
|
11358
|
+
vertexCredentials: existing?.vertexCredentials
|
|
11216
11359
|
};
|
|
11217
11360
|
try {
|
|
11218
11361
|
saveConfig(opts.config);
|
|
@@ -11226,13 +11369,19 @@ async function createServer(opts) {
|
|
|
11226
11369
|
apiKey: apiKey || existing?.apiKey,
|
|
11227
11370
|
baseUrl: baseUrl || existing?.baseUrl,
|
|
11228
11371
|
model: model || existing?.model,
|
|
11229
|
-
quotaPolicy: quota
|
|
11372
|
+
quotaPolicy: quota,
|
|
11373
|
+
vertexProject: existing?.vertexProject,
|
|
11374
|
+
vertexRegion: existing?.vertexRegion,
|
|
11375
|
+
vertexCredentials: existing?.vertexCredentials
|
|
11230
11376
|
});
|
|
11231
11377
|
const entry = providerSummary.find((p) => p.name === name);
|
|
11232
11378
|
if (entry) {
|
|
11233
11379
|
entry.configured = true;
|
|
11234
11380
|
entry.model = model || registry.get(name)?.config.model;
|
|
11235
11381
|
entry.quota = quota;
|
|
11382
|
+
if (name === "gemini") {
|
|
11383
|
+
entry.vertexConfigured = !!opts.config.providers?.[name]?.vertexProject;
|
|
11384
|
+
}
|
|
11236
11385
|
}
|
|
11237
11386
|
const afterConfig = summarizeProviderConfig(name, opts.config.providers[name]);
|
|
11238
11387
|
if (JSON.stringify(beforeConfig) !== JSON.stringify(afterConfig)) {
|
|
@@ -11428,7 +11577,12 @@ async function createServer(opts) {
|
|
|
11428
11577
|
return reply.status(404).send({ error: "Not found" });
|
|
11429
11578
|
});
|
|
11430
11579
|
}
|
|
11431
|
-
const healthHandler = async () => ({
|
|
11580
|
+
const healthHandler = async () => ({
|
|
11581
|
+
status: "ok",
|
|
11582
|
+
service: "canonry",
|
|
11583
|
+
version: PKG_VERSION,
|
|
11584
|
+
...basePath ? { basePath: basePath.replace(/\/$/, "") } : {}
|
|
11585
|
+
});
|
|
11432
11586
|
app.get("/health", healthHandler);
|
|
11433
11587
|
if (basePath) {
|
|
11434
11588
|
app.get(`${basePath}health`, healthHandler);
|
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
setGoogleAuthConfig,
|
|
20
20
|
showFirstRunNotice,
|
|
21
21
|
trackEvent
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-UTRZ3UB5.js";
|
|
23
23
|
|
|
24
24
|
// src/cli.ts
|
|
25
25
|
import { pathToFileURL } from "url";
|
|
@@ -148,14 +148,56 @@ Usage: ${spec.usage}`, {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// src/client.ts
|
|
151
|
+
function createApiClient() {
|
|
152
|
+
const config = loadConfig();
|
|
153
|
+
const basePathResolved = !!config.basePath || "CANONRY_BASE_PATH" in process.env;
|
|
154
|
+
return new ApiClient(config.apiUrl, config.apiKey, { skipProbe: basePathResolved });
|
|
155
|
+
}
|
|
151
156
|
var ApiClient = class {
|
|
152
157
|
baseUrl;
|
|
158
|
+
originUrl;
|
|
153
159
|
apiKey;
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
probePromise = null;
|
|
161
|
+
probeSkipped;
|
|
162
|
+
constructor(baseUrl, apiKey, opts) {
|
|
163
|
+
this.originUrl = baseUrl.replace(/\/$/, "");
|
|
164
|
+
this.baseUrl = this.originUrl + "/api/v1";
|
|
156
165
|
this.apiKey = apiKey;
|
|
166
|
+
this.probeSkipped = opts?.skipProbe ?? false;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* On first API call, probe /health to auto-discover basePath when the user
|
|
170
|
+
* hasn't configured one locally. This lets `canonry run` in a separate shell
|
|
171
|
+
* discover that the server is running at e.g. /canonry/ without requiring
|
|
172
|
+
* config.yaml edits or CANONRY_BASE_PATH in every shell.
|
|
173
|
+
*/
|
|
174
|
+
probeBasePath() {
|
|
175
|
+
if (this.probeSkipped) return Promise.resolve();
|
|
176
|
+
if (!this.probePromise) {
|
|
177
|
+
this.probePromise = (async () => {
|
|
178
|
+
try {
|
|
179
|
+
const origin = new URL(this.originUrl).origin;
|
|
180
|
+
const res = await fetch(`${origin}/health`, {
|
|
181
|
+
signal: AbortSignal.timeout(2e3)
|
|
182
|
+
});
|
|
183
|
+
if (res.ok) {
|
|
184
|
+
const body = await res.json();
|
|
185
|
+
if (body.basePath && typeof body.basePath === "string") {
|
|
186
|
+
const normalized = "/" + body.basePath.replace(/^\/|\/$/g, "");
|
|
187
|
+
if (normalized !== "/") {
|
|
188
|
+
this.originUrl = origin + normalized;
|
|
189
|
+
this.baseUrl = this.originUrl + "/api/v1";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
})();
|
|
196
|
+
}
|
|
197
|
+
return this.probePromise;
|
|
157
198
|
}
|
|
158
199
|
async request(method, path4, body) {
|
|
200
|
+
await this.probeBasePath();
|
|
159
201
|
const url = `${this.baseUrl}${path4}`;
|
|
160
202
|
const serializedBody = body != null ? JSON.stringify(body) : void 0;
|
|
161
203
|
const headers = {
|
|
@@ -440,8 +482,7 @@ var ApiClient = class {
|
|
|
440
482
|
|
|
441
483
|
// src/commands/bing.ts
|
|
442
484
|
function getClient() {
|
|
443
|
-
|
|
444
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
485
|
+
return createApiClient();
|
|
445
486
|
}
|
|
446
487
|
async function bingConnect(project, opts) {
|
|
447
488
|
let apiKey = opts?.apiKey;
|
|
@@ -963,8 +1004,7 @@ var BING_CLI_COMMANDS = [
|
|
|
963
1004
|
|
|
964
1005
|
// src/commands/cdp.ts
|
|
965
1006
|
function getClient2() {
|
|
966
|
-
|
|
967
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
1007
|
+
return createApiClient();
|
|
968
1008
|
}
|
|
969
1009
|
async function cdpConnect(opts) {
|
|
970
1010
|
const config = loadConfig();
|
|
@@ -1155,8 +1195,7 @@ var CDP_CLI_COMMANDS = [
|
|
|
1155
1195
|
|
|
1156
1196
|
// src/commands/ga.ts
|
|
1157
1197
|
function getClient3() {
|
|
1158
|
-
|
|
1159
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
1198
|
+
return createApiClient();
|
|
1160
1199
|
}
|
|
1161
1200
|
async function gaConnect(project, opts) {
|
|
1162
1201
|
if (!opts.propertyId) {
|
|
@@ -1398,8 +1437,7 @@ var GA_CLI_COMMANDS = [
|
|
|
1398
1437
|
|
|
1399
1438
|
// src/commands/competitor.ts
|
|
1400
1439
|
function getClient4() {
|
|
1401
|
-
|
|
1402
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
1440
|
+
return createApiClient();
|
|
1403
1441
|
}
|
|
1404
1442
|
async function addCompetitors(project, domains, format) {
|
|
1405
1443
|
const client = getClient4();
|
|
@@ -1480,8 +1518,7 @@ var COMPETITOR_CLI_COMMANDS = [
|
|
|
1480
1518
|
|
|
1481
1519
|
// src/commands/google.ts
|
|
1482
1520
|
function getClient5() {
|
|
1483
|
-
|
|
1484
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
1521
|
+
return createApiClient();
|
|
1485
1522
|
}
|
|
1486
1523
|
async function waitForRunStatus(client, runId, config) {
|
|
1487
1524
|
const start = Date.now();
|
|
@@ -2308,8 +2345,7 @@ var GOOGLE_CLI_COMMANDS = [
|
|
|
2308
2345
|
// src/commands/keyword.ts
|
|
2309
2346
|
import fs from "fs";
|
|
2310
2347
|
function getClient6() {
|
|
2311
|
-
|
|
2312
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
2348
|
+
return createApiClient();
|
|
2313
2349
|
}
|
|
2314
2350
|
async function addKeywords(project, keywords, format) {
|
|
2315
2351
|
const client = getClient6();
|
|
@@ -2553,8 +2589,7 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
2553
2589
|
|
|
2554
2590
|
// src/commands/notify.ts
|
|
2555
2591
|
function getClient7() {
|
|
2556
|
-
|
|
2557
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
2592
|
+
return createApiClient();
|
|
2558
2593
|
}
|
|
2559
2594
|
async function addNotification(project, opts) {
|
|
2560
2595
|
const client = getClient7();
|
|
@@ -2726,8 +2761,7 @@ async function applyConfigFile(filePath) {
|
|
|
2726
2761
|
}
|
|
2727
2762
|
const content = fs2.readFileSync(filePath, "utf-8");
|
|
2728
2763
|
const docs = parseAllDocuments(content);
|
|
2729
|
-
const
|
|
2730
|
-
const client = new ApiClient(clientConfig.apiUrl, clientConfig.apiKey);
|
|
2764
|
+
const client = createApiClient();
|
|
2731
2765
|
const errors = [];
|
|
2732
2766
|
const applied = [];
|
|
2733
2767
|
for (let i = 0; i < docs.length; i++) {
|
|
@@ -2789,8 +2823,7 @@ async function applyConfigs(filePaths, format) {
|
|
|
2789
2823
|
|
|
2790
2824
|
// src/commands/analytics.ts
|
|
2791
2825
|
function getClient8() {
|
|
2792
|
-
|
|
2793
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
2826
|
+
return createApiClient();
|
|
2794
2827
|
}
|
|
2795
2828
|
async function showAnalytics(project, options) {
|
|
2796
2829
|
const client = getClient8();
|
|
@@ -2897,8 +2930,7 @@ Source Origin Breakdown`);
|
|
|
2897
2930
|
|
|
2898
2931
|
// src/commands/evidence.ts
|
|
2899
2932
|
function getClient9() {
|
|
2900
|
-
|
|
2901
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
2933
|
+
return createApiClient();
|
|
2902
2934
|
}
|
|
2903
2935
|
async function showEvidence(project, format) {
|
|
2904
2936
|
const client = getClient9();
|
|
@@ -2933,8 +2965,7 @@ async function showEvidence(project, format) {
|
|
|
2933
2965
|
// src/commands/export-cmd.ts
|
|
2934
2966
|
import { stringify } from "yaml";
|
|
2935
2967
|
async function exportProject(project, opts) {
|
|
2936
|
-
const
|
|
2937
|
-
const client = new ApiClient(config.apiUrl, config.apiKey);
|
|
2968
|
+
const client = createApiClient();
|
|
2938
2969
|
const data = await client.getExport(project);
|
|
2939
2970
|
if (opts.includeResults) {
|
|
2940
2971
|
try {
|
|
@@ -2955,8 +2986,7 @@ async function exportProject(project, opts) {
|
|
|
2955
2986
|
|
|
2956
2987
|
// src/commands/history.ts
|
|
2957
2988
|
function getClient10() {
|
|
2958
|
-
|
|
2959
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
2989
|
+
return createApiClient();
|
|
2960
2990
|
}
|
|
2961
2991
|
async function showHistory(project, format) {
|
|
2962
2992
|
const client = getClient10();
|
|
@@ -2995,8 +3025,7 @@ async function showHistory(project, format) {
|
|
|
2995
3025
|
|
|
2996
3026
|
// src/commands/status.ts
|
|
2997
3027
|
function getClient11() {
|
|
2998
|
-
|
|
2999
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
3028
|
+
return createApiClient();
|
|
3000
3029
|
}
|
|
3001
3030
|
async function showStatus(project, format) {
|
|
3002
3031
|
const client = getClient11();
|
|
@@ -3108,8 +3137,7 @@ var OPERATOR_CLI_COMMANDS = [
|
|
|
3108
3137
|
|
|
3109
3138
|
// src/commands/project.ts
|
|
3110
3139
|
function getClient12() {
|
|
3111
|
-
|
|
3112
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
3140
|
+
return createApiClient();
|
|
3113
3141
|
}
|
|
3114
3142
|
async function createProject(name, opts) {
|
|
3115
3143
|
const client = getClient12();
|
|
@@ -3445,8 +3473,7 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
3445
3473
|
|
|
3446
3474
|
// src/commands/run.ts
|
|
3447
3475
|
function getClient13() {
|
|
3448
|
-
|
|
3449
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
3476
|
+
return createApiClient();
|
|
3450
3477
|
}
|
|
3451
3478
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
|
|
3452
3479
|
async function triggerRun(project, opts) {
|
|
@@ -3775,8 +3802,7 @@ var RUN_CLI_COMMANDS = [
|
|
|
3775
3802
|
|
|
3776
3803
|
// src/commands/schedule.ts
|
|
3777
3804
|
function getClient14() {
|
|
3778
|
-
|
|
3779
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
3805
|
+
return createApiClient();
|
|
3780
3806
|
}
|
|
3781
3807
|
async function setSchedule(project, opts) {
|
|
3782
3808
|
const client = getClient14();
|
|
@@ -3939,8 +3965,7 @@ var SCHEDULE_CLI_COMMANDS = [
|
|
|
3939
3965
|
|
|
3940
3966
|
// src/commands/settings.ts
|
|
3941
3967
|
function getClient15() {
|
|
3942
|
-
|
|
3943
|
-
return new ApiClient(config.apiUrl, config.apiKey);
|
|
3968
|
+
return createApiClient();
|
|
3944
3969
|
}
|
|
3945
3970
|
async function setProvider(name, opts) {
|
|
3946
3971
|
const client = getClient15();
|
|
@@ -4149,6 +4174,10 @@ var envSchema = z.object({
|
|
|
4149
4174
|
GEMINI_MAX_CONCURRENCY: z.coerce.number().int().positive().default(2),
|
|
4150
4175
|
GEMINI_MAX_REQUESTS_PER_MINUTE: z.coerce.number().int().positive().default(10),
|
|
4151
4176
|
GEMINI_MAX_REQUESTS_PER_DAY: z.coerce.number().int().positive().default(1e3),
|
|
4177
|
+
// Gemini Vertex AI (alternative to API key auth)
|
|
4178
|
+
GEMINI_VERTEX_PROJECT: z.string().optional(),
|
|
4179
|
+
GEMINI_VERTEX_REGION: z.string().optional(),
|
|
4180
|
+
GEMINI_VERTEX_CREDENTIALS: z.string().optional(),
|
|
4152
4181
|
// OpenAI
|
|
4153
4182
|
OPENAI_API_KEY: z.string().optional(),
|
|
4154
4183
|
OPENAI_MODEL: z.string().optional(),
|
|
@@ -4174,6 +4203,9 @@ var bootstrapEnvSchema = z.object({
|
|
|
4174
4203
|
CANONRY_DATABASE_PATH: z.string().optional(),
|
|
4175
4204
|
GEMINI_API_KEY: z.string().optional(),
|
|
4176
4205
|
GEMINI_MODEL: z.string().optional(),
|
|
4206
|
+
GEMINI_VERTEX_PROJECT: z.string().optional(),
|
|
4207
|
+
GEMINI_VERTEX_REGION: z.string().optional(),
|
|
4208
|
+
GEMINI_VERTEX_CREDENTIALS: z.string().optional(),
|
|
4177
4209
|
OPENAI_API_KEY: z.string().optional(),
|
|
4178
4210
|
OPENAI_MODEL: z.string().optional(),
|
|
4179
4211
|
ANTHROPIC_API_KEY: z.string().optional(),
|
|
@@ -4190,15 +4222,18 @@ function getBootstrapEnv(source, overrides) {
|
|
|
4190
4222
|
const filtered = overrides ? Object.fromEntries(Object.entries(overrides).filter(([, v]) => v != null)) : {};
|
|
4191
4223
|
const parsed = bootstrapEnvSchema.parse({ ...source, ...filtered });
|
|
4192
4224
|
const providers = {};
|
|
4193
|
-
if (parsed.GEMINI_API_KEY) {
|
|
4225
|
+
if (parsed.GEMINI_API_KEY || parsed.GEMINI_VERTEX_PROJECT) {
|
|
4194
4226
|
providers.gemini = {
|
|
4195
|
-
apiKey: parsed.GEMINI_API_KEY,
|
|
4227
|
+
apiKey: parsed.GEMINI_API_KEY ?? "",
|
|
4196
4228
|
model: parsed.GEMINI_MODEL || "gemini-3-flash",
|
|
4197
4229
|
quota: providerQuotaPolicySchema.parse({
|
|
4198
4230
|
maxConcurrency: 2,
|
|
4199
4231
|
maxRequestsPerMinute: 10,
|
|
4200
4232
|
maxRequestsPerDay: 500
|
|
4201
|
-
})
|
|
4233
|
+
}),
|
|
4234
|
+
vertexProject: parsed.GEMINI_VERTEX_PROJECT,
|
|
4235
|
+
vertexRegion: parsed.GEMINI_VERTEX_REGION,
|
|
4236
|
+
vertexCredentials: parsed.GEMINI_VERTEX_CREDENTIALS
|
|
4202
4237
|
};
|
|
4203
4238
|
}
|
|
4204
4239
|
if (parsed.OPENAI_API_KEY) {
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,12 @@ interface ProviderConfigEntry {
|
|
|
8
8
|
baseUrl?: string;
|
|
9
9
|
model?: string;
|
|
10
10
|
quota?: ProviderQuotaPolicy;
|
|
11
|
+
/** Vertex AI GCP project ID (Gemini provider only) */
|
|
12
|
+
vertexProject?: string;
|
|
13
|
+
/** Vertex AI region, e.g. "us-central1" (Gemini provider only) */
|
|
14
|
+
vertexRegion?: string;
|
|
15
|
+
/** Path to service account JSON for Vertex AI auth (falls back to ADC) */
|
|
16
|
+
vertexCredentials?: string;
|
|
11
17
|
}
|
|
12
18
|
interface CdpConfigEntry {
|
|
13
19
|
host?: string;
|
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.27.1",
|
|
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",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
38
38
|
"@fastify/static": "^8.1.0",
|
|
39
|
+
"@google-cloud/vertexai": "^1.10.3",
|
|
39
40
|
"@google/generative-ai": "^0.24.1",
|
|
40
41
|
"better-sqlite3": "^12.6.2",
|
|
41
42
|
"drizzle-orm": "^0.45.1",
|
|
@@ -52,17 +53,17 @@
|
|
|
52
53
|
"@types/node-cron": "^3.0.11",
|
|
53
54
|
"tsup": "^8.5.1",
|
|
54
55
|
"tsx": "^4.19.0",
|
|
55
|
-
"@ainyc/canonry-api-routes": "0.0.0",
|
|
56
|
-
"@ainyc/canonry-contracts": "0.0.0",
|
|
57
56
|
"@ainyc/canonry-config": "0.0.0",
|
|
58
|
-
"@ainyc/canonry-
|
|
57
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
59
58
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
59
|
+
"@ainyc/canonry-api-routes": "0.0.0",
|
|
60
|
+
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
61
|
+
"@ainyc/canonry-db": "0.0.0",
|
|
60
62
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
61
63
|
"@ainyc/canonry-provider-local": "0.0.0",
|
|
62
64
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
63
65
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
64
66
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
65
|
-
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
66
67
|
"@ainyc/canonry-provider-perplexity": "0.0.0"
|
|
67
68
|
},
|
|
68
69
|
"scripts": {
|