@ainyc/canonry 4.109.0 → 4.111.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/agent-workspace/skills/canonry/references/canonry-cli.md +1 -1
- package/assets/assets/{BacklinksPage-D48kAAiT.js → BacklinksPage-Ccp5dJm2.js} +1 -1
- package/assets/assets/ChartPrimitives-DZI_FVyX.js +1 -0
- package/assets/assets/ProjectPage-feeXXGgl.js +7 -0
- package/assets/assets/{RunRow-DQy-Gy9t.js → RunRow-CPf7aV1o.js} +1 -1
- package/assets/assets/{RunsPage-DRRG4uZS.js → RunsPage-CI8AcUal.js} +1 -1
- package/assets/assets/{SettingsPage-Pf0B9gMT.js → SettingsPage-QI2X5EIb.js} +1 -1
- package/assets/assets/{TrafficPage-Cd2OL96L.js → TrafficPage-CcELR6n1.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CiyttCo2.js → TrafficSourceDetailPage-C-9rc4ly.js} +1 -1
- package/assets/assets/{arrow-left-DaIR4jsx.js → arrow-left-3WT6TImA.js} +1 -1
- package/assets/assets/{extract-error-message-HqopXrW4.js → extract-error-message-43ehRbIG.js} +1 -1
- package/assets/assets/{index-CthlUeHu.js → index-BpnaXO_X.js} +65 -65
- package/assets/assets/index-JD5rv_sY.css +1 -0
- package/assets/assets/{trash-2-DUel-9g8.js → trash-2-Dk-RvBpF.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-YEOLQT5S.js → chunk-4TQOZJPF.js} +39 -19
- package/dist/{chunk-3HKBUYIY.js → chunk-CVXBE3ST.js} +44 -1
- package/dist/{chunk-QZQLZL3C.js → chunk-Y26GYDS6.js} +2 -1
- package/dist/{chunk-WXAHSQOX.js → chunk-ZT7GVLS3.js} +18 -0
- package/dist/cli.js +409 -106
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-NXZXNM7T.js → intelligence-service-4MQUT3OO.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -8
- package/assets/assets/ChartPrimitives-CLSeu1W4.js +0 -1
- package/assets/assets/ProjectPage-D20d2kjG.js +0 -7
- package/assets/assets/index-CjrSrPKf.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
setTelemetrySource,
|
|
30
30
|
showFirstRunNotice,
|
|
31
31
|
trackEvent
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-4TQOZJPF.js";
|
|
33
33
|
import {
|
|
34
34
|
CliError,
|
|
35
35
|
EXIT_SYSTEM_ERROR,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
saveConfig,
|
|
47
47
|
saveConfigPatch,
|
|
48
48
|
usageError
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-Y26GYDS6.js";
|
|
50
50
|
import {
|
|
51
51
|
apiKeys,
|
|
52
52
|
createClient,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
projects,
|
|
55
55
|
queries,
|
|
56
56
|
renderReportHtml
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-CVXBE3ST.js";
|
|
58
58
|
import {
|
|
59
59
|
BacklinkSources,
|
|
60
60
|
CcReleaseSyncStatuses,
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
providerQuotaPolicySchema,
|
|
81
81
|
resolveProviderInput,
|
|
82
82
|
winnabilityClassSchema
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-ZT7GVLS3.js";
|
|
84
84
|
|
|
85
85
|
// src/cli.ts
|
|
86
86
|
import { pathToFileURL } from "url";
|
|
@@ -126,9 +126,9 @@ import { parseArgs } from "util";
|
|
|
126
126
|
function commandId(spec) {
|
|
127
127
|
return spec.path.join(".");
|
|
128
128
|
}
|
|
129
|
-
function matchesPath(args,
|
|
130
|
-
if (args.length <
|
|
131
|
-
return
|
|
129
|
+
function matchesPath(args, path10) {
|
|
130
|
+
if (args.length < path10.length) return false;
|
|
131
|
+
return path10.every((segment, index) => args[index] === segment);
|
|
132
132
|
}
|
|
133
133
|
function withGlobalOptions(options) {
|
|
134
134
|
const base = options ? { ...options } : {};
|
|
@@ -1656,6 +1656,7 @@ function buildRunBody(opts, icpDescription) {
|
|
|
1656
1656
|
const body = {};
|
|
1657
1657
|
if (icpDescription) body.icpDescription = icpDescription;
|
|
1658
1658
|
if (opts.buyer !== void 0 && opts.buyer.trim() !== "") body.buyerDescription = opts.buyer;
|
|
1659
|
+
if (opts.seedProviders && opts.seedProviders.length > 0) body.seedProviders = opts.seedProviders;
|
|
1659
1660
|
if (opts.dedupThreshold !== void 0) body.dedupThreshold = opts.dedupThreshold;
|
|
1660
1661
|
if (opts.maxProbes !== void 0) body.maxProbes = opts.maxProbes;
|
|
1661
1662
|
if (opts.probeConcurrency !== void 0) body.probeConcurrency = opts.probeConcurrency;
|
|
@@ -1972,6 +1973,272 @@ async function pollSession(client, project, sessionId, quiet = false) {
|
|
|
1972
1973
|
}
|
|
1973
1974
|
}
|
|
1974
1975
|
|
|
1976
|
+
// src/commands/discover-eval.ts
|
|
1977
|
+
import fs2 from "fs";
|
|
1978
|
+
import path from "path";
|
|
1979
|
+
|
|
1980
|
+
// src/discovery-eval.ts
|
|
1981
|
+
var DISCOVERY_EVAL_PANEL = [
|
|
1982
|
+
{
|
|
1983
|
+
slug: "eval-local-single-intent",
|
|
1984
|
+
displayName: "Summit Roof Coatings",
|
|
1985
|
+
domain: "summitroofcoatings.com",
|
|
1986
|
+
icp: "commercial roof coating contractor in Phoenix, Arizona",
|
|
1987
|
+
buyer: "commercial property managers responsible for flat-roof maintenance budgets",
|
|
1988
|
+
locations: [{ label: "phoenix", city: "Phoenix", region: "Arizona", country: "US" }]
|
|
1989
|
+
},
|
|
1990
|
+
{
|
|
1991
|
+
slug: "eval-local-multi-intent",
|
|
1992
|
+
displayName: "Peak Comfort HVAC",
|
|
1993
|
+
domain: "peakcomforthvac.com",
|
|
1994
|
+
icp: "residential HVAC installation, repair, and maintenance company in Denver, Colorado",
|
|
1995
|
+
buyer: "homeowners with aging furnaces or AC units comparing replacement and repair options",
|
|
1996
|
+
locations: [{ label: "denver", city: "Denver", region: "Colorado", country: "US" }]
|
|
1997
|
+
},
|
|
1998
|
+
{
|
|
1999
|
+
slug: "eval-b2b-saas",
|
|
2000
|
+
displayName: "QuoteBeam",
|
|
2001
|
+
domain: "quotebeam.io",
|
|
2002
|
+
icp: "quoting and proposal software for residential solar installers",
|
|
2003
|
+
buyer: "solar sales managers evaluating quoting tools for a 10-50 rep team",
|
|
2004
|
+
locations: []
|
|
2005
|
+
},
|
|
2006
|
+
{
|
|
2007
|
+
slug: "eval-national-ecommerce",
|
|
2008
|
+
displayName: "Willow and Sprout",
|
|
2009
|
+
domain: "willowandsprout.com",
|
|
2010
|
+
icp: "organic cotton baby clothing brand sold online across the US",
|
|
2011
|
+
buyer: "expecting parents researching non-toxic baby essentials",
|
|
2012
|
+
locations: []
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
slug: "eval-problem-heavy-consumer",
|
|
2016
|
+
displayName: "SwiftRemit",
|
|
2017
|
+
domain: "swiftremit.app",
|
|
2018
|
+
icp: "mobile app for sending money internationally with low fees",
|
|
2019
|
+
buyer: "immigrants who send money home to family every month",
|
|
2020
|
+
locations: []
|
|
2021
|
+
}
|
|
2022
|
+
];
|
|
2023
|
+
var EVAL_CANONICAL_FLOOR = 8;
|
|
2024
|
+
var EVAL_BANDS = {
|
|
2025
|
+
/** canonicalCount must be >= baseline * this factor (and >= the floor). */
|
|
2026
|
+
canonicalCountFactor: 0.6,
|
|
2027
|
+
/** retention may drop at most this many absolute points below baseline. */
|
|
2028
|
+
retentionDrop: 0.2,
|
|
2029
|
+
/** brandShare may exceed baseline by at most this (and never exceed 0.1). */
|
|
2030
|
+
brandShareSlack: 0.05,
|
|
2031
|
+
brandShareCeiling: 0.1,
|
|
2032
|
+
/** duration must be <= baseline * factor + slack seconds. */
|
|
2033
|
+
durationFactor: 2,
|
|
2034
|
+
durationSlackSeconds: 30
|
|
2035
|
+
};
|
|
2036
|
+
function scoreSession(shape, session) {
|
|
2037
|
+
const raw = session.seedCountRaw ?? 0;
|
|
2038
|
+
const brandFiltered = session.seedBrandFilteredCount ?? 0;
|
|
2039
|
+
const preFilterTotal = raw + brandFiltered;
|
|
2040
|
+
const truncated = session.canonicalCount == null;
|
|
2041
|
+
const canonicalCount = session.canonicalCount ?? session.seedCount ?? 0;
|
|
2042
|
+
const started = session.startedAt ? Date.parse(session.startedAt) : NaN;
|
|
2043
|
+
const finished = session.finishedAt ? Date.parse(session.finishedAt) : NaN;
|
|
2044
|
+
return {
|
|
2045
|
+
shape,
|
|
2046
|
+
seedCountRaw: raw,
|
|
2047
|
+
canonicalCount,
|
|
2048
|
+
canonicalCountTruncated: truncated,
|
|
2049
|
+
retention: raw > 0 ? canonicalCount / raw : 0,
|
|
2050
|
+
brandShare: preFilterTotal > 0 ? brandFiltered / preFilterTotal : 0,
|
|
2051
|
+
groundingShare: raw > 0 ? (session.seedFromGroundingCount ?? 0) / raw : 0,
|
|
2052
|
+
bandPairFraction: session.dedupBandPairFraction ?? null,
|
|
2053
|
+
probeCount: session.probeCount ?? 0,
|
|
2054
|
+
warning: session.warning ?? null,
|
|
2055
|
+
durationSeconds: Number.isFinite(started) && Number.isFinite(finished) ? Math.round((finished - started) / 1e3) : null
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
function compareToBaseline(scorecards, baseline) {
|
|
2059
|
+
const regressions = [];
|
|
2060
|
+
const notes = [];
|
|
2061
|
+
const bySlug = new Map(scorecards.map((c) => [c.shape, c]));
|
|
2062
|
+
for (const base of baseline.scorecards) {
|
|
2063
|
+
const current = bySlug.get(base.shape);
|
|
2064
|
+
if (!current) {
|
|
2065
|
+
regressions.push(`${base.shape}: missing from this run (baseline shape not evaluated)`);
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
bySlug.delete(base.shape);
|
|
2069
|
+
if (current.canonicalCount < EVAL_CANONICAL_FLOOR) {
|
|
2070
|
+
regressions.push(
|
|
2071
|
+
`${base.shape}: canonicalCount ${current.canonicalCount} is below the absolute platform floor (${EVAL_CANONICAL_FLOOR})`
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
if (current.canonicalCount < base.canonicalCount * EVAL_BANDS.canonicalCountFactor) {
|
|
2075
|
+
regressions.push(
|
|
2076
|
+
`${base.shape}: canonicalCount ${current.canonicalCount} vs baseline ${base.canonicalCount} (band: >= ${EVAL_BANDS.canonicalCountFactor}x)`
|
|
2077
|
+
);
|
|
2078
|
+
}
|
|
2079
|
+
if (current.retention < base.retention - EVAL_BANDS.retentionDrop) {
|
|
2080
|
+
regressions.push(
|
|
2081
|
+
`${base.shape}: retention ${current.retention.toFixed(2)} vs baseline ${base.retention.toFixed(2)} (band: -${EVAL_BANDS.retentionDrop})`
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2084
|
+
const brandCeiling = Math.min(base.brandShare + EVAL_BANDS.brandShareSlack, EVAL_BANDS.brandShareCeiling);
|
|
2085
|
+
if (current.brandShare > brandCeiling) {
|
|
2086
|
+
regressions.push(
|
|
2087
|
+
`${base.shape}: brandShare ${current.brandShare.toFixed(2)} exceeds ${brandCeiling.toFixed(2)} (no-brand rule regressed)`
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
if (current.warning) {
|
|
2091
|
+
regressions.push(`${base.shape}: collapse warning fired: ${current.warning.slice(0, 80)}`);
|
|
2092
|
+
}
|
|
2093
|
+
if (current.durationSeconds != null && base.durationSeconds != null && current.durationSeconds > base.durationSeconds * EVAL_BANDS.durationFactor + EVAL_BANDS.durationSlackSeconds) {
|
|
2094
|
+
regressions.push(
|
|
2095
|
+
`${base.shape}: duration ${current.durationSeconds}s vs baseline ${base.durationSeconds}s (band: ${EVAL_BANDS.durationFactor}x + ${EVAL_BANDS.durationSlackSeconds}s)`
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
if (current.canonicalCountTruncated) {
|
|
2099
|
+
notes.push(`${base.shape}: engine predates canonical_count; using truncated seedCount (understates quality)`);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
for (const [slug] of bySlug) {
|
|
2103
|
+
notes.push(`${slug}: new shape with no baseline entry (add it via --update-baseline)`);
|
|
2104
|
+
}
|
|
2105
|
+
return { pass: regressions.length === 0, regressions, notes };
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// src/commands/discover-eval.ts
|
|
2109
|
+
var DEFAULT_BASELINE_PATH = "canonry-discovery-eval-baseline.json";
|
|
2110
|
+
var POLL_INTERVAL_MS2 = 5e3;
|
|
2111
|
+
var SHAPE_TIMEOUT_MS = 10 * 6e4;
|
|
2112
|
+
async function runDiscoveryEvalPanel(client, shapes, opts) {
|
|
2113
|
+
const scorecards = [];
|
|
2114
|
+
const pollInterval = opts.pollIntervalMs ?? POLL_INTERVAL_MS2;
|
|
2115
|
+
const timeout = opts.shapeTimeoutMs ?? SHAPE_TIMEOUT_MS;
|
|
2116
|
+
const now = opts.now ?? Date.now;
|
|
2117
|
+
for (const shape of shapes) {
|
|
2118
|
+
await client.putProject(shape.slug, {
|
|
2119
|
+
displayName: shape.displayName,
|
|
2120
|
+
canonicalDomain: shape.domain,
|
|
2121
|
+
country: "US",
|
|
2122
|
+
language: "en",
|
|
2123
|
+
...shape.locations.length > 0 ? { locations: shape.locations, defaultLocation: shape.locations[0].label } : {}
|
|
2124
|
+
});
|
|
2125
|
+
const started = await client.triggerDiscoveryRun(shape.slug, {
|
|
2126
|
+
icpDescription: shape.icp,
|
|
2127
|
+
buyerDescription: shape.buyer,
|
|
2128
|
+
maxProbes: opts.maxProbes ?? 2,
|
|
2129
|
+
probeConcurrency: opts.probeConcurrency ?? 2,
|
|
2130
|
+
...opts.seedProviders && opts.seedProviders.length > 0 ? { seedProviders: opts.seedProviders } : {}
|
|
2131
|
+
});
|
|
2132
|
+
const sessionId = typeof started.sessionId === "string" ? started.sessionId : "";
|
|
2133
|
+
if (!sessionId) {
|
|
2134
|
+
throw new CliError({
|
|
2135
|
+
code: "EVAL_SESSION_START_FAILED",
|
|
2136
|
+
message: `${shape.slug}: discover run returned no sessionId`,
|
|
2137
|
+
exitCode: EXIT_USER_ERROR
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
const deadline = now() + timeout;
|
|
2141
|
+
let session;
|
|
2142
|
+
for (; ; ) {
|
|
2143
|
+
const rows = await client.listDiscoverySessions(shape.slug, { limit: 10 });
|
|
2144
|
+
session = rows.find((r) => r.id === sessionId);
|
|
2145
|
+
if (session?.status === "completed") break;
|
|
2146
|
+
if (session?.status === "failed") {
|
|
2147
|
+
throw new CliError({
|
|
2148
|
+
code: "EVAL_SESSION_FAILED",
|
|
2149
|
+
message: `${shape.slug}: discovery session failed`,
|
|
2150
|
+
exitCode: EXIT_USER_ERROR,
|
|
2151
|
+
details: { sessionId }
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
if (now() > deadline) {
|
|
2155
|
+
throw new CliError({
|
|
2156
|
+
code: "EVAL_SESSION_TIMEOUT",
|
|
2157
|
+
message: `${shape.slug}: session did not complete within ${Math.round(timeout / 6e4)} minutes`,
|
|
2158
|
+
exitCode: EXIT_USER_ERROR,
|
|
2159
|
+
details: { sessionId }
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
2163
|
+
}
|
|
2164
|
+
scorecards.push(scoreSession(shape.slug, session));
|
|
2165
|
+
}
|
|
2166
|
+
return scorecards;
|
|
2167
|
+
}
|
|
2168
|
+
function renderHuman(scorecards, verdict) {
|
|
2169
|
+
console.log("SHAPE RAW CANON RETENTION BRAND GROUND BAND TIME");
|
|
2170
|
+
for (const c of scorecards) {
|
|
2171
|
+
console.log(
|
|
2172
|
+
[
|
|
2173
|
+
c.shape.padEnd(30),
|
|
2174
|
+
String(c.seedCountRaw).padStart(4),
|
|
2175
|
+
String(c.canonicalCount).padStart(6) + (c.canonicalCountTruncated ? "*" : " "),
|
|
2176
|
+
c.retention.toFixed(2).padStart(9),
|
|
2177
|
+
c.brandShare.toFixed(2).padStart(6),
|
|
2178
|
+
c.groundingShare.toFixed(2).padStart(7),
|
|
2179
|
+
(c.bandPairFraction ?? 0).toFixed(2).padStart(5),
|
|
2180
|
+
c.durationSeconds == null ? " ?" : `${String(c.durationSeconds).padStart(4)}s`
|
|
2181
|
+
].join(" ")
|
|
2182
|
+
);
|
|
2183
|
+
if (c.warning) console.log(` warning: ${c.warning}`);
|
|
2184
|
+
}
|
|
2185
|
+
if (verdict) {
|
|
2186
|
+
for (const note of verdict.notes) console.log(`note: ${note}`);
|
|
2187
|
+
for (const regression of verdict.regressions) console.log(`REGRESSION: ${regression}`);
|
|
2188
|
+
console.log(verdict.pass ? "PASS: no regressions against the baseline." : "FAIL: regressions detected.");
|
|
2189
|
+
} else {
|
|
2190
|
+
console.log("No baseline compared (wrote or missing baseline).");
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
async function discoverEvalWithClient(client, opts) {
|
|
2194
|
+
const selected = opts.shapes && opts.shapes.length > 0 ? DISCOVERY_EVAL_PANEL.filter((s) => opts.shapes.includes(s.slug)) : DISCOVERY_EVAL_PANEL;
|
|
2195
|
+
if (selected.length === 0) {
|
|
2196
|
+
throw new CliError({
|
|
2197
|
+
code: "EVAL_NO_SHAPES",
|
|
2198
|
+
message: `no matching shapes; known: ${DISCOVERY_EVAL_PANEL.map((s) => s.slug).join(", ")}`,
|
|
2199
|
+
exitCode: EXIT_USER_ERROR
|
|
2200
|
+
});
|
|
2201
|
+
}
|
|
2202
|
+
const scorecards = await runDiscoveryEvalPanel(client, selected, opts);
|
|
2203
|
+
const baselinePath = path.resolve(opts.baseline ?? DEFAULT_BASELINE_PATH);
|
|
2204
|
+
if (opts.updateBaseline) {
|
|
2205
|
+
const baseline2 = { capturedAt: (/* @__PURE__ */ new Date()).toISOString(), scorecards };
|
|
2206
|
+
fs2.writeFileSync(baselinePath, JSON.stringify(baseline2, null, 2) + "\n");
|
|
2207
|
+
if (opts.format === "json" || opts.format === "jsonl") {
|
|
2208
|
+
console.log(JSON.stringify({ scorecards, baselineWritten: baselinePath }, null, 2));
|
|
2209
|
+
} else {
|
|
2210
|
+
renderHuman(scorecards, null);
|
|
2211
|
+
console.log(`Baseline written: ${baselinePath}`);
|
|
2212
|
+
}
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
if (!fs2.existsSync(baselinePath)) {
|
|
2216
|
+
throw new CliError({
|
|
2217
|
+
code: "EVAL_NO_BASELINE",
|
|
2218
|
+
message: `no baseline at ${baselinePath}; run with --update-baseline to capture one`,
|
|
2219
|
+
exitCode: EXIT_USER_ERROR
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
const baseline = JSON.parse(fs2.readFileSync(baselinePath, "utf8"));
|
|
2223
|
+
const verdict = compareToBaseline(scorecards, baseline);
|
|
2224
|
+
if (opts.format === "json" || opts.format === "jsonl") {
|
|
2225
|
+
console.log(JSON.stringify({ scorecards, verdict, baseline: { capturedAt: baseline.capturedAt } }, null, 2));
|
|
2226
|
+
} else {
|
|
2227
|
+
renderHuman(scorecards, verdict);
|
|
2228
|
+
}
|
|
2229
|
+
if (!verdict.pass) {
|
|
2230
|
+
throw new CliError({
|
|
2231
|
+
code: "EVAL_REGRESSION",
|
|
2232
|
+
message: `${verdict.regressions.length} regression${verdict.regressions.length === 1 ? "" : "s"} against the baseline`,
|
|
2233
|
+
exitCode: EXIT_USER_ERROR,
|
|
2234
|
+
details: { regressions: verdict.regressions }
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
async function discoverEval(opts) {
|
|
2239
|
+
await discoverEvalWithClient(createApiClient(), opts);
|
|
2240
|
+
}
|
|
2241
|
+
|
|
1975
2242
|
// src/cli-commands/discover.ts
|
|
1976
2243
|
function parseFloatOption(values, key, usage) {
|
|
1977
2244
|
const raw = values[key];
|
|
@@ -2058,10 +2325,11 @@ Usage: ${usage}`,
|
|
|
2058
2325
|
var DISCOVER_CLI_COMMANDS = [
|
|
2059
2326
|
{
|
|
2060
2327
|
path: ["discover", "run"],
|
|
2061
|
-
usage: 'canonry discover run <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2328
|
+
usage: 'canonry discover run <project> [--icp "..."] [--buyer "..."] [--seed-provider gemini --seed-provider openai] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2062
2329
|
options: {
|
|
2063
2330
|
icp: stringOption(),
|
|
2064
2331
|
buyer: stringOption(),
|
|
2332
|
+
"seed-provider": multiStringOption(),
|
|
2065
2333
|
"icp-angle": multiStringOption(),
|
|
2066
2334
|
locations: multiStringOption(),
|
|
2067
2335
|
"dedup-threshold": stringOption(),
|
|
@@ -2070,11 +2338,12 @@ var DISCOVER_CLI_COMMANDS = [
|
|
|
2070
2338
|
wait: { type: "boolean", default: false }
|
|
2071
2339
|
},
|
|
2072
2340
|
run: async (input) => {
|
|
2073
|
-
const usage = 'canonry discover run <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2341
|
+
const usage = 'canonry discover run <project> [--icp "..."] [--buyer "..."] [--seed-provider gemini --seed-provider openai] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2074
2342
|
const project = requireProject(input, "discover.run", usage);
|
|
2075
2343
|
await discoverRun(project, {
|
|
2076
2344
|
icp: getString(input.values, "icp"),
|
|
2077
2345
|
buyer: getString(input.values, "buyer"),
|
|
2346
|
+
seedProviders: getStringArray(input.values, "seed-provider"),
|
|
2078
2347
|
icpAngles: getStringArray(input.values, "icp-angle"),
|
|
2079
2348
|
locations: parseLocationsOption(input.values),
|
|
2080
2349
|
dedupThreshold: parseFloatOption(input.values, "dedup-threshold", usage),
|
|
@@ -2093,12 +2362,45 @@ var DISCOVER_CLI_COMMANDS = [
|
|
|
2093
2362
|
});
|
|
2094
2363
|
}
|
|
2095
2364
|
},
|
|
2365
|
+
{
|
|
2366
|
+
path: ["discover", "eval"],
|
|
2367
|
+
usage: "canonry discover eval [--baseline <path>] [--update-baseline] [--shape eval-b2b-saas ...] [--seed-provider gemini --seed-provider openai] [--max-probes 2] [--probe-concurrency 2] [--format json]",
|
|
2368
|
+
options: {
|
|
2369
|
+
baseline: stringOption(),
|
|
2370
|
+
"update-baseline": { type: "boolean", default: false },
|
|
2371
|
+
shape: multiStringOption(),
|
|
2372
|
+
"seed-provider": multiStringOption(),
|
|
2373
|
+
"max-probes": stringOption(),
|
|
2374
|
+
"probe-concurrency": stringOption()
|
|
2375
|
+
},
|
|
2376
|
+
run: async (input) => {
|
|
2377
|
+
const usage = "canonry discover eval [--baseline <path>] [--update-baseline] [--shape eval-b2b-saas ...] [--seed-provider gemini --seed-provider openai] [--max-probes 2] [--probe-concurrency 2] [--format json]";
|
|
2378
|
+
await discoverEval({
|
|
2379
|
+
baseline: getString(input.values, "baseline"),
|
|
2380
|
+
updateBaseline: getBoolean(input.values, "update-baseline"),
|
|
2381
|
+
shapes: getStringArray(input.values, "shape"),
|
|
2382
|
+
seedProviders: getStringArray(input.values, "seed-provider"),
|
|
2383
|
+
maxProbes: parseIntegerOption(input, "max-probes", {
|
|
2384
|
+
command: "discover.eval",
|
|
2385
|
+
usage,
|
|
2386
|
+
message: "--max-probes must be an integer"
|
|
2387
|
+
}),
|
|
2388
|
+
probeConcurrency: parseIntegerOption(input, "probe-concurrency", {
|
|
2389
|
+
command: "discover.eval",
|
|
2390
|
+
usage,
|
|
2391
|
+
message: "--probe-concurrency must be an integer"
|
|
2392
|
+
}),
|
|
2393
|
+
format: input.format
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
},
|
|
2096
2397
|
{
|
|
2097
2398
|
path: ["discover", "seed"],
|
|
2098
|
-
usage: 'canonry discover seed <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2399
|
+
usage: 'canonry discover seed <project> [--icp "..."] [--buyer "..."] [--seed-provider gemini --seed-provider openai] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2099
2400
|
options: {
|
|
2100
2401
|
icp: stringOption(),
|
|
2101
2402
|
buyer: stringOption(),
|
|
2403
|
+
"seed-provider": multiStringOption(),
|
|
2102
2404
|
"icp-angle": multiStringOption(),
|
|
2103
2405
|
locations: multiStringOption(),
|
|
2104
2406
|
"dedup-threshold": stringOption(),
|
|
@@ -2107,11 +2409,12 @@ var DISCOVER_CLI_COMMANDS = [
|
|
|
2107
2409
|
wait: { type: "boolean", default: false }
|
|
2108
2410
|
},
|
|
2109
2411
|
run: async (input) => {
|
|
2110
|
-
const usage = 'canonry discover seed <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2412
|
+
const usage = 'canonry discover seed <project> [--icp "..."] [--buyer "..."] [--seed-provider gemini --seed-provider openai] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2111
2413
|
const project = requireProject(input, "discover.seed", usage);
|
|
2112
2414
|
await discoverSeed(project, {
|
|
2113
2415
|
icp: getString(input.values, "icp"),
|
|
2114
2416
|
buyer: getString(input.values, "buyer"),
|
|
2417
|
+
seedProviders: getStringArray(input.values, "seed-provider"),
|
|
2115
2418
|
icpAngles: getStringArray(input.values, "icp-angle"),
|
|
2116
2419
|
locations: parseLocationsOption(input.values),
|
|
2117
2420
|
dedupThreshold: parseFloatOption(input.values, "dedup-threshold", usage),
|
|
@@ -2645,9 +2948,9 @@ async function gaConnect(project, opts) {
|
|
|
2645
2948
|
propertyId: opts.propertyId
|
|
2646
2949
|
};
|
|
2647
2950
|
if (opts.keyFile) {
|
|
2648
|
-
const
|
|
2951
|
+
const fs13 = await import("fs");
|
|
2649
2952
|
try {
|
|
2650
|
-
const content =
|
|
2953
|
+
const content = fs13.readFileSync(opts.keyFile, "utf-8");
|
|
2651
2954
|
JSON.parse(content);
|
|
2652
2955
|
body.keyJson = content;
|
|
2653
2956
|
} catch (e) {
|
|
@@ -3855,9 +4158,9 @@ async function getCommand(opts) {
|
|
|
3855
4158
|
console.log(JSON.stringify(leaf, null, 2));
|
|
3856
4159
|
}
|
|
3857
4160
|
}
|
|
3858
|
-
function walkPath(value,
|
|
3859
|
-
if (!
|
|
3860
|
-
const segments =
|
|
4161
|
+
function walkPath(value, path10) {
|
|
4162
|
+
if (!path10 || path10 === ".") return value;
|
|
4163
|
+
const segments = path10.split(".").flatMap((part) => {
|
|
3861
4164
|
const tokens = [];
|
|
3862
4165
|
let i = 0;
|
|
3863
4166
|
const bracketStart = part.indexOf("[");
|
|
@@ -3905,13 +4208,13 @@ var GET_CLI_COMMANDS = [
|
|
|
3905
4208
|
},
|
|
3906
4209
|
run: async (input) => {
|
|
3907
4210
|
const project = requireProject(input, "get", USAGE2);
|
|
3908
|
-
const
|
|
4211
|
+
const path10 = requirePositional(input, 1, {
|
|
3909
4212
|
command: "get",
|
|
3910
4213
|
usage: USAGE2,
|
|
3911
4214
|
message: 'path is required (e.g. "scores.mentionShare.value")'
|
|
3912
4215
|
});
|
|
3913
4216
|
const from = getString(input.values, "from");
|
|
3914
|
-
await getCommand({ project, path:
|
|
4217
|
+
await getCommand({ project, path: path10, from, format: input.format });
|
|
3915
4218
|
}
|
|
3916
4219
|
}
|
|
3917
4220
|
];
|
|
@@ -3950,9 +4253,9 @@ async function trafficConnectWordpress(project, opts) {
|
|
|
3950
4253
|
}
|
|
3951
4254
|
let applicationPassword = opts.appPassword?.trim() ?? "";
|
|
3952
4255
|
if (!applicationPassword && opts.appPasswordFile) {
|
|
3953
|
-
const
|
|
4256
|
+
const fs13 = await import("fs");
|
|
3954
4257
|
try {
|
|
3955
|
-
applicationPassword =
|
|
4258
|
+
applicationPassword = fs13.readFileSync(opts.appPasswordFile, "utf-8").trim();
|
|
3956
4259
|
} catch (e) {
|
|
3957
4260
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3958
4261
|
throw new CliError({
|
|
@@ -4008,10 +4311,10 @@ async function trafficConnectCloudRun(project, opts) {
|
|
|
4008
4311
|
details: { project }
|
|
4009
4312
|
});
|
|
4010
4313
|
}
|
|
4011
|
-
const
|
|
4314
|
+
const fs13 = await import("fs");
|
|
4012
4315
|
let keyJson;
|
|
4013
4316
|
try {
|
|
4014
|
-
keyJson =
|
|
4317
|
+
keyJson = fs13.readFileSync(opts.serviceAccountKey, "utf-8");
|
|
4015
4318
|
JSON.parse(keyJson);
|
|
4016
4319
|
} catch (e) {
|
|
4017
4320
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -4071,9 +4374,9 @@ async function trafficConnectVercel(project, opts) {
|
|
|
4071
4374
|
}
|
|
4072
4375
|
let token = opts.token?.trim() ?? "";
|
|
4073
4376
|
if (!token && opts.tokenFile) {
|
|
4074
|
-
const
|
|
4377
|
+
const fs13 = await import("fs");
|
|
4075
4378
|
try {
|
|
4076
|
-
token =
|
|
4379
|
+
token = fs13.readFileSync(opts.tokenFile, "utf-8").trim();
|
|
4077
4380
|
} catch (e) {
|
|
4078
4381
|
const msg = e instanceof Error ? e.message : String(e);
|
|
4079
4382
|
throw new CliError({
|
|
@@ -5887,7 +6190,7 @@ var KEYS_CLI_COMMANDS = [
|
|
|
5887
6190
|
];
|
|
5888
6191
|
|
|
5889
6192
|
// src/commands/keyword.ts
|
|
5890
|
-
import
|
|
6193
|
+
import fs3 from "fs";
|
|
5891
6194
|
function getClient12() {
|
|
5892
6195
|
return createApiClient();
|
|
5893
6196
|
}
|
|
@@ -5955,7 +6258,7 @@ async function listKeywords(project, format) {
|
|
|
5955
6258
|
}
|
|
5956
6259
|
}
|
|
5957
6260
|
async function importKeywords(project, filePath, format) {
|
|
5958
|
-
if (!
|
|
6261
|
+
if (!fs3.existsSync(filePath)) {
|
|
5959
6262
|
throw new CliError({
|
|
5960
6263
|
code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
|
|
5961
6264
|
message: `File not found: ${filePath}`,
|
|
@@ -5966,7 +6269,7 @@ async function importKeywords(project, filePath, format) {
|
|
|
5966
6269
|
}
|
|
5967
6270
|
});
|
|
5968
6271
|
}
|
|
5969
|
-
const content =
|
|
6272
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5970
6273
|
const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
5971
6274
|
if (keywords.length === 0) {
|
|
5972
6275
|
if (isMachineFormat(format)) {
|
|
@@ -6167,7 +6470,7 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
6167
6470
|
];
|
|
6168
6471
|
|
|
6169
6472
|
// src/commands/query.ts
|
|
6170
|
-
import
|
|
6473
|
+
import fs4 from "fs";
|
|
6171
6474
|
function getClient13() {
|
|
6172
6475
|
return createApiClient();
|
|
6173
6476
|
}
|
|
@@ -6258,7 +6561,7 @@ async function listQueries(project, format) {
|
|
|
6258
6561
|
}
|
|
6259
6562
|
}
|
|
6260
6563
|
async function importQueries(project, filePath, format) {
|
|
6261
|
-
if (!
|
|
6564
|
+
if (!fs4.existsSync(filePath)) {
|
|
6262
6565
|
throw new CliError({
|
|
6263
6566
|
code: "QUERY_IMPORT_FILE_NOT_FOUND",
|
|
6264
6567
|
message: `File not found: ${filePath}`,
|
|
@@ -6269,7 +6572,7 @@ async function importQueries(project, filePath, format) {
|
|
|
6269
6572
|
}
|
|
6270
6573
|
});
|
|
6271
6574
|
}
|
|
6272
|
-
const content =
|
|
6575
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
6273
6576
|
const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
6274
6577
|
if (queries2.length === 0) {
|
|
6275
6578
|
if (isMachineFormat(format)) {
|
|
@@ -6471,16 +6774,16 @@ var QUERY_CLI_COMMANDS = [
|
|
|
6471
6774
|
];
|
|
6472
6775
|
|
|
6473
6776
|
// src/commands/mcp.ts
|
|
6474
|
-
import
|
|
6475
|
-
import
|
|
6777
|
+
import fs5 from "fs";
|
|
6778
|
+
import path3 from "path";
|
|
6476
6779
|
import { createRequire } from "module";
|
|
6477
6780
|
|
|
6478
6781
|
// src/mcp-clients.ts
|
|
6479
6782
|
import os from "os";
|
|
6480
|
-
import
|
|
6783
|
+
import path2 from "path";
|
|
6481
6784
|
var CLAUDE_DESKTOP_CONFIG_FILENAME = "claude_desktop_config.json";
|
|
6482
6785
|
function homeRelative(...segments) {
|
|
6483
|
-
return
|
|
6786
|
+
return path2.join(os.homedir(), ...segments);
|
|
6484
6787
|
}
|
|
6485
6788
|
function claudeDesktopConfigPath() {
|
|
6486
6789
|
switch (process.platform) {
|
|
@@ -6488,7 +6791,7 @@ function claudeDesktopConfigPath() {
|
|
|
6488
6791
|
return homeRelative("Library", "Application Support", "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
|
|
6489
6792
|
case "win32": {
|
|
6490
6793
|
const appData = process.env.APPDATA ?? homeRelative("AppData", "Roaming");
|
|
6491
|
-
return
|
|
6794
|
+
return path2.join(appData, "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
|
|
6492
6795
|
}
|
|
6493
6796
|
default:
|
|
6494
6797
|
return homeRelative(".config", "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
|
|
@@ -6501,7 +6804,7 @@ function codexConfigPath() {
|
|
|
6501
6804
|
return homeRelative(".codex", "config.toml");
|
|
6502
6805
|
}
|
|
6503
6806
|
function claudeCodeProjectConfigPath() {
|
|
6504
|
-
return
|
|
6807
|
+
return path2.join(process.cwd(), ".mcp.json");
|
|
6505
6808
|
}
|
|
6506
6809
|
var SUPPORTED_MCP_CLIENTS = [
|
|
6507
6810
|
{
|
|
@@ -6544,7 +6847,7 @@ function listMcpClientIds() {
|
|
|
6544
6847
|
var _require = createRequire(import.meta.url);
|
|
6545
6848
|
function resolveCanonryMcpBin() {
|
|
6546
6849
|
const packageJsonPath = _require.resolve("../package.json");
|
|
6547
|
-
const packageRoot =
|
|
6850
|
+
const packageRoot = path3.dirname(packageJsonPath);
|
|
6548
6851
|
const pkg = _require("../package.json");
|
|
6549
6852
|
const relativeBin = pkg.bin?.["canonry-mcp"];
|
|
6550
6853
|
if (!relativeBin) {
|
|
@@ -6554,7 +6857,7 @@ function resolveCanonryMcpBin() {
|
|
|
6554
6857
|
exitCode: 2
|
|
6555
6858
|
});
|
|
6556
6859
|
}
|
|
6557
|
-
return
|
|
6860
|
+
return path3.resolve(packageRoot, relativeBin);
|
|
6558
6861
|
}
|
|
6559
6862
|
function buildEntry(opts) {
|
|
6560
6863
|
const target = opts.binPath ?? resolveCanonryMcpBin();
|
|
@@ -6587,8 +6890,8 @@ function renderClientSnippet(client, serverName, entry) {
|
|
|
6587
6890
|
return renderJsonSnippet(serverName, entry, client.format);
|
|
6588
6891
|
}
|
|
6589
6892
|
function readJsonConfig(configPath) {
|
|
6590
|
-
if (!
|
|
6591
|
-
const raw =
|
|
6893
|
+
if (!fs5.existsSync(configPath)) return {};
|
|
6894
|
+
const raw = fs5.readFileSync(configPath, "utf-8").trim();
|
|
6592
6895
|
if (!raw) return {};
|
|
6593
6896
|
try {
|
|
6594
6897
|
const parsed = JSON.parse(raw);
|
|
@@ -6606,14 +6909,14 @@ function readJsonConfig(configPath) {
|
|
|
6606
6909
|
}
|
|
6607
6910
|
}
|
|
6608
6911
|
function writeJsonConfig(configPath, value) {
|
|
6609
|
-
|
|
6610
|
-
|
|
6912
|
+
fs5.mkdirSync(path3.dirname(configPath), { recursive: true });
|
|
6913
|
+
fs5.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
|
|
6611
6914
|
`, "utf-8");
|
|
6612
6915
|
}
|
|
6613
6916
|
function backupConfigIfPresent(configPath) {
|
|
6614
|
-
if (!
|
|
6917
|
+
if (!fs5.existsSync(configPath)) return void 0;
|
|
6615
6918
|
const backupPath = `${configPath}.canonry.bak`;
|
|
6616
|
-
|
|
6919
|
+
fs5.copyFileSync(configPath, backupPath);
|
|
6617
6920
|
return backupPath;
|
|
6618
6921
|
}
|
|
6619
6922
|
function findClientOrThrow(id) {
|
|
@@ -6967,13 +7270,13 @@ var NOTIFY_CLI_COMMANDS = [
|
|
|
6967
7270
|
];
|
|
6968
7271
|
|
|
6969
7272
|
// src/commands/apply.ts
|
|
6970
|
-
import
|
|
7273
|
+
import fs6 from "fs";
|
|
6971
7274
|
import { parseAllDocuments } from "yaml";
|
|
6972
7275
|
async function applyConfigFile(filePath) {
|
|
6973
|
-
if (!
|
|
7276
|
+
if (!fs6.existsSync(filePath)) {
|
|
6974
7277
|
throw new Error(`File not found: ${filePath}`);
|
|
6975
7278
|
}
|
|
6976
|
-
const content =
|
|
7279
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
6977
7280
|
const docs = parseAllDocuments(content);
|
|
6978
7281
|
const client = createApiClient();
|
|
6979
7282
|
const errors = [];
|
|
@@ -7888,11 +8191,11 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
7888
8191
|
];
|
|
7889
8192
|
|
|
7890
8193
|
// src/commands/report.ts
|
|
7891
|
-
import
|
|
7892
|
-
import
|
|
8194
|
+
import fs7 from "fs";
|
|
8195
|
+
import path4 from "path";
|
|
7893
8196
|
function defaultOutputPath(project, audience) {
|
|
7894
8197
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7895
|
-
return
|
|
8198
|
+
return path4.resolve(process.cwd(), `canonry-report-${project}-${audience}-${date}.html`);
|
|
7896
8199
|
}
|
|
7897
8200
|
async function runReportCommand(project, opts = {}) {
|
|
7898
8201
|
const client = createApiClient();
|
|
@@ -7903,12 +8206,12 @@ async function runReportCommand(project, opts = {}) {
|
|
|
7903
8206
|
return;
|
|
7904
8207
|
}
|
|
7905
8208
|
const html = renderReportHtml(report, { audience });
|
|
7906
|
-
const targetPath = opts.output ?
|
|
7907
|
-
const dir =
|
|
7908
|
-
if (!
|
|
7909
|
-
|
|
8209
|
+
const targetPath = opts.output ? path4.resolve(opts.output) : defaultOutputPath(project, audience);
|
|
8210
|
+
const dir = path4.dirname(targetPath);
|
|
8211
|
+
if (!fs7.existsSync(dir)) {
|
|
8212
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
7910
8213
|
}
|
|
7911
|
-
|
|
8214
|
+
fs7.writeFileSync(targetPath, html, "utf-8");
|
|
7912
8215
|
console.log(`Report written to ${targetPath}`);
|
|
7913
8216
|
}
|
|
7914
8217
|
|
|
@@ -8773,12 +9076,12 @@ var SKILLS_CLI_COMMANDS = [
|
|
|
8773
9076
|
];
|
|
8774
9077
|
|
|
8775
9078
|
// src/commands/snapshot.ts
|
|
8776
|
-
import
|
|
8777
|
-
import
|
|
9079
|
+
import fs9 from "fs";
|
|
9080
|
+
import path6 from "path";
|
|
8778
9081
|
|
|
8779
9082
|
// src/snapshot-pdf.ts
|
|
8780
|
-
import
|
|
8781
|
-
import
|
|
9083
|
+
import fs8 from "fs";
|
|
9084
|
+
import path5 from "path";
|
|
8782
9085
|
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
8783
9086
|
var PAGE_WIDTH = 612;
|
|
8784
9087
|
var PAGE_HEIGHT = 792;
|
|
@@ -8990,9 +9293,9 @@ async function writeSnapshotPdf(report, outputPath) {
|
|
|
8990
9293
|
renderCompetitors(pdf, report);
|
|
8991
9294
|
renderQueries(pdf, report);
|
|
8992
9295
|
const bytes = await doc.save();
|
|
8993
|
-
const resolvedPath =
|
|
8994
|
-
|
|
8995
|
-
|
|
9296
|
+
const resolvedPath = path5.resolve(outputPath);
|
|
9297
|
+
fs8.mkdirSync(path5.dirname(resolvedPath), { recursive: true });
|
|
9298
|
+
fs8.writeFileSync(resolvedPath, bytes);
|
|
8996
9299
|
return resolvedPath;
|
|
8997
9300
|
}
|
|
8998
9301
|
function renderCover(pdf, report) {
|
|
@@ -9150,9 +9453,9 @@ Markdown saved: ${savedMdPath}`);
|
|
|
9150
9453
|
PDF saved: ${savedPdfPath}`);
|
|
9151
9454
|
}
|
|
9152
9455
|
function writeSnapshotMarkdown(report, outputPath) {
|
|
9153
|
-
const resolvedPath =
|
|
9154
|
-
|
|
9155
|
-
|
|
9456
|
+
const resolvedPath = path6.resolve(outputPath);
|
|
9457
|
+
fs9.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
|
|
9458
|
+
fs9.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
|
|
9156
9459
|
return resolvedPath;
|
|
9157
9460
|
}
|
|
9158
9461
|
function formatSnapshotMarkdown(report) {
|
|
@@ -9444,7 +9747,7 @@ async function showOverview(project, opts) {
|
|
|
9444
9747
|
console.log(JSON.stringify(overview, null, 2));
|
|
9445
9748
|
return;
|
|
9446
9749
|
}
|
|
9447
|
-
|
|
9750
|
+
renderHuman2(overview);
|
|
9448
9751
|
}
|
|
9449
9752
|
async function showAllOverviews(opts) {
|
|
9450
9753
|
const client = createApiClient();
|
|
@@ -9485,7 +9788,7 @@ function cell(value, width) {
|
|
|
9485
9788
|
if (value.length >= width) return `${value.slice(0, width - 1)} `;
|
|
9486
9789
|
return value.padEnd(width);
|
|
9487
9790
|
}
|
|
9488
|
-
function
|
|
9791
|
+
function renderHuman2(overview) {
|
|
9489
9792
|
const {
|
|
9490
9793
|
project: meta,
|
|
9491
9794
|
latestRun,
|
|
@@ -10135,7 +10438,7 @@ var CONTENT_CLI_COMMANDS = [
|
|
|
10135
10438
|
|
|
10136
10439
|
// src/commands/bootstrap.ts
|
|
10137
10440
|
import crypto from "crypto";
|
|
10138
|
-
import
|
|
10441
|
+
import path7 from "path";
|
|
10139
10442
|
import { eq } from "drizzle-orm";
|
|
10140
10443
|
|
|
10141
10444
|
// ../config/src/index.ts
|
|
@@ -10292,7 +10595,7 @@ async function bootstrapCommand(_opts) {
|
|
|
10292
10595
|
);
|
|
10293
10596
|
}
|
|
10294
10597
|
const configDir = getConfigDir();
|
|
10295
|
-
const databasePath = env.databasePath ||
|
|
10598
|
+
const databasePath = env.databasePath || path7.join(configDir, "data.db");
|
|
10296
10599
|
const existing = configExists();
|
|
10297
10600
|
const existingConfig = existing ? loadConfig() : void 0;
|
|
10298
10601
|
let rawApiKey;
|
|
@@ -10362,10 +10665,10 @@ async function bootstrapCommand(_opts) {
|
|
|
10362
10665
|
|
|
10363
10666
|
// src/commands/daemon.ts
|
|
10364
10667
|
import { spawn } from "child_process";
|
|
10365
|
-
import
|
|
10366
|
-
import
|
|
10668
|
+
import fs10 from "fs";
|
|
10669
|
+
import path8 from "path";
|
|
10367
10670
|
function getPidPath() {
|
|
10368
|
-
return
|
|
10671
|
+
return path8.join(getConfigDir(), "canonry.pid");
|
|
10369
10672
|
}
|
|
10370
10673
|
function isProcessAlive(pid) {
|
|
10371
10674
|
try {
|
|
@@ -10405,8 +10708,8 @@ function buildServeForwardArgs(opts) {
|
|
|
10405
10708
|
async function startDaemon(opts) {
|
|
10406
10709
|
const pidPath = getPidPath();
|
|
10407
10710
|
const format = opts.format ?? "text";
|
|
10408
|
-
if (
|
|
10409
|
-
const existingPid = parseInt(
|
|
10711
|
+
if (fs10.existsSync(pidPath)) {
|
|
10712
|
+
const existingPid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
10410
10713
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
10411
10714
|
throw new CliError({
|
|
10412
10715
|
code: "DAEMON_ALREADY_RUNNING",
|
|
@@ -10417,9 +10720,9 @@ async function startDaemon(opts) {
|
|
|
10417
10720
|
}
|
|
10418
10721
|
});
|
|
10419
10722
|
}
|
|
10420
|
-
|
|
10723
|
+
fs10.unlinkSync(pidPath);
|
|
10421
10724
|
}
|
|
10422
|
-
const cliPath =
|
|
10725
|
+
const cliPath = path8.resolve(new URL(import.meta.url).pathname);
|
|
10423
10726
|
const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
10424
10727
|
const args = inSourceMode ? ["--import", "tsx", cliPath, "serve"] : [cliPath, "serve"];
|
|
10425
10728
|
args.push(...buildServeForwardArgs(opts));
|
|
@@ -10436,10 +10739,10 @@ async function startDaemon(opts) {
|
|
|
10436
10739
|
});
|
|
10437
10740
|
}
|
|
10438
10741
|
const configDir = getConfigDir();
|
|
10439
|
-
if (!
|
|
10440
|
-
|
|
10742
|
+
if (!fs10.existsSync(configDir)) {
|
|
10743
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
10441
10744
|
}
|
|
10442
|
-
|
|
10745
|
+
fs10.writeFileSync(pidPath, String(child.pid), "utf-8");
|
|
10443
10746
|
const port = opts.port ?? "4100";
|
|
10444
10747
|
const host = opts.host ?? "127.0.0.1";
|
|
10445
10748
|
if (!isMachineFormat(format)) {
|
|
@@ -10448,7 +10751,7 @@ async function startDaemon(opts) {
|
|
|
10448
10751
|
const ready = await waitForReady(host, port);
|
|
10449
10752
|
if (!ready) {
|
|
10450
10753
|
try {
|
|
10451
|
-
|
|
10754
|
+
fs10.unlinkSync(pidPath);
|
|
10452
10755
|
} catch {
|
|
10453
10756
|
}
|
|
10454
10757
|
throw new CliError({
|
|
@@ -10480,7 +10783,7 @@ async function startDaemon(opts) {
|
|
|
10480
10783
|
}
|
|
10481
10784
|
function stopDaemon(format = "text") {
|
|
10482
10785
|
const pidPath = getPidPath();
|
|
10483
|
-
if (!
|
|
10786
|
+
if (!fs10.existsSync(pidPath)) {
|
|
10484
10787
|
if (isMachineFormat(format)) {
|
|
10485
10788
|
console.log(JSON.stringify({
|
|
10486
10789
|
stopped: false,
|
|
@@ -10491,7 +10794,7 @@ function stopDaemon(format = "text") {
|
|
|
10491
10794
|
console.log("Canonry is not running (no PID file found)");
|
|
10492
10795
|
return;
|
|
10493
10796
|
}
|
|
10494
|
-
const pid = parseInt(
|
|
10797
|
+
const pid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
10495
10798
|
if (isNaN(pid)) {
|
|
10496
10799
|
if (isMachineFormat(format)) {
|
|
10497
10800
|
console.log(JSON.stringify({
|
|
@@ -10502,7 +10805,7 @@ function stopDaemon(format = "text") {
|
|
|
10502
10805
|
} else {
|
|
10503
10806
|
console.error("Invalid PID file. Removing it.");
|
|
10504
10807
|
}
|
|
10505
|
-
|
|
10808
|
+
fs10.unlinkSync(pidPath);
|
|
10506
10809
|
return;
|
|
10507
10810
|
}
|
|
10508
10811
|
if (!isProcessAlive(pid)) {
|
|
@@ -10516,12 +10819,12 @@ function stopDaemon(format = "text") {
|
|
|
10516
10819
|
} else {
|
|
10517
10820
|
console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
|
|
10518
10821
|
}
|
|
10519
|
-
|
|
10822
|
+
fs10.unlinkSync(pidPath);
|
|
10520
10823
|
return;
|
|
10521
10824
|
}
|
|
10522
10825
|
try {
|
|
10523
10826
|
process.kill(pid, "SIGTERM");
|
|
10524
|
-
|
|
10827
|
+
fs10.unlinkSync(pidPath);
|
|
10525
10828
|
if (isMachineFormat(format)) {
|
|
10526
10829
|
console.log(JSON.stringify({
|
|
10527
10830
|
stopped: true,
|
|
@@ -10545,9 +10848,9 @@ function stopDaemon(format = "text") {
|
|
|
10545
10848
|
|
|
10546
10849
|
// src/commands/init.ts
|
|
10547
10850
|
import crypto2 from "crypto";
|
|
10548
|
-
import
|
|
10851
|
+
import fs11 from "fs";
|
|
10549
10852
|
import readline from "readline";
|
|
10550
|
-
import
|
|
10853
|
+
import path9 from "path";
|
|
10551
10854
|
function prompt(question) {
|
|
10552
10855
|
const rl = readline.createInterface({
|
|
10553
10856
|
input: process.stdin,
|
|
@@ -10568,8 +10871,8 @@ var DEFAULT_QUOTA = {
|
|
|
10568
10871
|
var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
|
|
10569
10872
|
function cwdLooksLikeProject(dir) {
|
|
10570
10873
|
const home = process.env.HOME ?? "";
|
|
10571
|
-
if (home &&
|
|
10572
|
-
return PROJECT_MARKERS.some((marker) =>
|
|
10874
|
+
if (home && path9.resolve(dir) === path9.resolve(home)) return false;
|
|
10875
|
+
return PROJECT_MARKERS.some((marker) => fs11.existsSync(path9.join(dir, marker)));
|
|
10573
10876
|
}
|
|
10574
10877
|
var DEFAULT_AGENT_MODELS = {
|
|
10575
10878
|
anthropic: "anthropic/claude-sonnet-4-6",
|
|
@@ -10600,8 +10903,8 @@ async function initCommand(opts) {
|
|
|
10600
10903
|
return void 0;
|
|
10601
10904
|
}
|
|
10602
10905
|
const configDir = getConfigDir();
|
|
10603
|
-
if (!
|
|
10604
|
-
|
|
10906
|
+
if (!fs11.existsSync(configDir)) {
|
|
10907
|
+
fs11.mkdirSync(configDir, { recursive: true });
|
|
10605
10908
|
}
|
|
10606
10909
|
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
10607
10910
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
@@ -10716,7 +11019,7 @@ async function initCommand(opts) {
|
|
|
10716
11019
|
const rawApiKey = `cnry_${crypto2.randomBytes(16).toString("hex")}`;
|
|
10717
11020
|
const keyHash = crypto2.createHash("sha256").update(rawApiKey).digest("hex");
|
|
10718
11021
|
const keyPrefix = rawApiKey.slice(0, 9);
|
|
10719
|
-
const databasePath =
|
|
11022
|
+
const databasePath = path9.join(configDir, "data.db");
|
|
10720
11023
|
const db = createClient(databasePath);
|
|
10721
11024
|
migrate(db);
|
|
10722
11025
|
db.insert(apiKeys).values({
|
|
@@ -11613,7 +11916,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
|
|
|
11613
11916
|
];
|
|
11614
11917
|
|
|
11615
11918
|
// src/cli-commands/wordpress.ts
|
|
11616
|
-
import
|
|
11919
|
+
import fs12 from "fs";
|
|
11617
11920
|
|
|
11618
11921
|
// src/commands/wordpress.ts
|
|
11619
11922
|
function getClient26() {
|
|
@@ -11852,12 +12155,12 @@ async function wordpressSetMeta(project, body) {
|
|
|
11852
12155
|
printPageDetail(result);
|
|
11853
12156
|
}
|
|
11854
12157
|
async function wordpressBulkSetMeta(project, opts) {
|
|
11855
|
-
const
|
|
11856
|
-
const
|
|
11857
|
-
const filePath =
|
|
12158
|
+
const fs13 = await import("fs/promises");
|
|
12159
|
+
const path10 = await import("path");
|
|
12160
|
+
const filePath = path10.resolve(opts.from);
|
|
11858
12161
|
let raw;
|
|
11859
12162
|
try {
|
|
11860
|
-
raw = await
|
|
12163
|
+
raw = await fs13.readFile(filePath, "utf8");
|
|
11861
12164
|
} catch {
|
|
11862
12165
|
throw new CliError({
|
|
11863
12166
|
code: "FILE_READ_ERROR",
|
|
@@ -11954,13 +12257,13 @@ async function wordpressSetSchema(project, body) {
|
|
|
11954
12257
|
printManualAssist(`Schema update for "${body.slug}"`, result);
|
|
11955
12258
|
}
|
|
11956
12259
|
async function wordpressSchemaDeploy(project, opts) {
|
|
11957
|
-
const
|
|
11958
|
-
const
|
|
12260
|
+
const fs13 = await import("fs/promises");
|
|
12261
|
+
const path10 = await import("path");
|
|
11959
12262
|
const yaml = await loadYamlModule();
|
|
11960
|
-
const filePath =
|
|
12263
|
+
const filePath = path10.resolve(opts.profile);
|
|
11961
12264
|
let raw;
|
|
11962
12265
|
try {
|
|
11963
|
-
raw = await
|
|
12266
|
+
raw = await fs13.readFile(filePath, "utf8");
|
|
11964
12267
|
} catch {
|
|
11965
12268
|
throw new CliError({
|
|
11966
12269
|
code: "FILE_READ_ERROR",
|
|
@@ -12065,13 +12368,13 @@ async function wordpressOnboard(project, opts) {
|
|
|
12065
12368
|
}
|
|
12066
12369
|
let profileData;
|
|
12067
12370
|
if (opts.profile) {
|
|
12068
|
-
const
|
|
12069
|
-
const
|
|
12371
|
+
const fs13 = await import("fs/promises");
|
|
12372
|
+
const path10 = await import("path");
|
|
12070
12373
|
const yaml = await loadYamlModule();
|
|
12071
|
-
const filePath =
|
|
12374
|
+
const filePath = path10.resolve(opts.profile);
|
|
12072
12375
|
let raw;
|
|
12073
12376
|
try {
|
|
12074
|
-
raw = await
|
|
12377
|
+
raw = await fs13.readFile(filePath, "utf8");
|
|
12075
12378
|
} catch {
|
|
12076
12379
|
throw new CliError({
|
|
12077
12380
|
code: "FILE_READ_ERROR",
|
|
@@ -12220,7 +12523,7 @@ function resolveContent(input, command, usage, options) {
|
|
|
12220
12523
|
}
|
|
12221
12524
|
if (contentFile) {
|
|
12222
12525
|
try {
|
|
12223
|
-
return
|
|
12526
|
+
return fs12.readFileSync(contentFile, "utf-8");
|
|
12224
12527
|
} catch (error) {
|
|
12225
12528
|
const message = error instanceof Error ? error.message : String(error);
|
|
12226
12529
|
throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
|