@ainyc/canonry 1.45.3 → 1.46.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/cli.js CHANGED
@@ -28,11 +28,12 @@ import {
28
28
  setGoogleAuthConfig,
29
29
  showFirstRunNotice,
30
30
  trackEvent
31
- } from "./chunk-WNOUK4KA.js";
31
+ } from "./chunk-RMLIF47M.js";
32
32
  import {
33
33
  apiKeys,
34
34
  competitors,
35
35
  createClient,
36
+ createLogger,
36
37
  migrate,
37
38
  parseJsonColumn,
38
39
  projects,
@@ -118,9 +119,9 @@ import { parseArgs } from "util";
118
119
  function commandId(spec) {
119
120
  return spec.path.join(".");
120
121
  }
121
- function matchesPath(args, path6) {
122
- if (args.length < path6.length) return false;
123
- return path6.every((segment, index) => args[index] === segment);
122
+ function matchesPath(args, path9) {
123
+ if (args.length < path9.length) return false;
124
+ return path9.every((segment, index) => args[index] === segment);
124
125
  }
125
126
  function withFormatOption(options) {
126
127
  if (!options) {
@@ -613,9 +614,9 @@ var ApiClient = class {
613
614
  }
614
615
  return this.probePromise;
615
616
  }
616
- async request(method, path6, body) {
617
+ async request(method, path9, body) {
617
618
  await this.probeBasePath();
618
- const url = `${this.baseUrl}${path6}`;
619
+ const url = `${this.baseUrl}${path9}`;
619
620
  const serializedBody = body != null ? JSON.stringify(body) : void 0;
620
621
  const headers = {
621
622
  "Authorization": `Bearer ${this.apiKey}`,
@@ -909,11 +910,13 @@ var ApiClient = class {
909
910
  async gaCoverage(project) {
910
911
  return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/coverage`);
911
912
  }
912
- async gaAiReferralHistory(project) {
913
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/ai-referral-history`);
913
+ async gaAiReferralHistory(project, params) {
914
+ const qs = params ? "?" + new URLSearchParams(params).toString() : "";
915
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/ai-referral-history${qs}`);
914
916
  }
915
- async gaSocialReferralHistory(project) {
916
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-history`);
917
+ async gaSocialReferralHistory(project, params) {
918
+ const qs = params ? "?" + new URLSearchParams(params).toString() : "";
919
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-history${qs}`);
917
920
  }
918
921
  async gaSocialReferralTrend(project) {
919
922
  return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/social-referral-trend`);
@@ -921,8 +924,9 @@ var ApiClient = class {
921
924
  async gaAttributionTrend(project) {
922
925
  return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/attribution-trend`);
923
926
  }
924
- async gaSessionHistory(project) {
925
- return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/session-history`);
927
+ async gaSessionHistory(project, params) {
928
+ const qs = params ? "?" + new URLSearchParams(params).toString() : "";
929
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/session-history${qs}`);
926
930
  }
927
931
  async wordpressConnect(project, body) {
928
932
  return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/connect`, body);
@@ -1691,9 +1695,9 @@ async function gaConnect(project, opts) {
1691
1695
  propertyId: opts.propertyId
1692
1696
  };
1693
1697
  if (opts.keyFile) {
1694
- const fs8 = await import("fs");
1698
+ const fs10 = await import("fs");
1695
1699
  try {
1696
- const content = fs8.readFileSync(opts.keyFile, "utf-8");
1700
+ const content = fs10.readFileSync(opts.keyFile, "utf-8");
1697
1701
  JSON.parse(content);
1698
1702
  body.keyJson = content;
1699
1703
  } catch (e) {
@@ -1781,13 +1785,18 @@ async function gaTraffic(project, opts) {
1781
1785
  const client = getClient3();
1782
1786
  const params = {};
1783
1787
  if (opts?.limit) params.limit = String(opts.limit);
1788
+ if (opts?.window) params.window = opts.window;
1784
1789
  const result = await client.gaTraffic(project, Object.keys(params).length > 0 ? params : void 0);
1785
1790
  if (opts?.format === "json") {
1786
1791
  console.log(JSON.stringify(result, null, 2));
1787
1792
  return;
1788
1793
  }
1789
1794
  if (result.topPages.length === 0 && result.aiReferrals.length === 0 && result.socialReferrals.length === 0) {
1790
- console.log('No GA4 traffic data. Run "canonry ga sync <project>" first.');
1795
+ if (!result.lastSyncedAt) {
1796
+ console.log('No GA4 traffic data. Run "canonry ga sync <project>" first.');
1797
+ } else {
1798
+ console.log(`No GA4 traffic data for the selected period.${opts?.window ? ` Try a wider window or omit --window.` : ""}`);
1799
+ }
1791
1800
  return;
1792
1801
  }
1793
1802
  console.log(`GA4 Traffic for "${project}"
@@ -1847,15 +1856,15 @@ async function gaTraffic(project, opts) {
1847
1856
  Last synced: ${result.lastSyncedAt}`);
1848
1857
  }
1849
1858
  }
1850
- async function gaAiReferralHistory(project, format) {
1859
+ async function gaAiReferralHistory(project, opts) {
1851
1860
  const client = getClient3();
1852
- const result = await client.gaAiReferralHistory(project);
1853
- if (format === "json") {
1861
+ const result = await client.gaAiReferralHistory(project, opts?.window ? { window: opts.window } : void 0);
1862
+ if (opts?.format === "json") {
1854
1863
  console.log(JSON.stringify(result, null, 2));
1855
1864
  return;
1856
1865
  }
1857
1866
  if (result.length === 0) {
1858
- console.log('No AI referral history. Run "canonry ga sync <project>" first.');
1867
+ console.log(`No AI referral history.${opts?.window ? " Try a wider window or omit --window." : ' Run "canonry ga sync <project>" first.'}`);
1859
1868
  return;
1860
1869
  }
1861
1870
  const dateWidth = 12;
@@ -1872,15 +1881,15 @@ async function gaAiReferralHistory(project, format) {
1872
1881
  );
1873
1882
  }
1874
1883
  }
1875
- async function gaSocialReferralHistory(project, format) {
1884
+ async function gaSocialReferralHistory(project, opts) {
1876
1885
  const client = getClient3();
1877
- const result = await client.gaSocialReferralHistory(project);
1878
- if (format === "json") {
1886
+ const result = await client.gaSocialReferralHistory(project, opts?.window ? { window: opts.window } : void 0);
1887
+ if (opts?.format === "json") {
1879
1888
  console.log(JSON.stringify(result, null, 2));
1880
1889
  return;
1881
1890
  }
1882
1891
  if (result.length === 0) {
1883
- console.log('No social referral history. Run "canonry ga sync <project>" first.');
1892
+ console.log(`No social referral history.${opts?.window ? " Try a wider window or omit --window." : ' Run "canonry ga sync <project>" first.'}`);
1884
1893
  return;
1885
1894
  }
1886
1895
  const dateWidth = 12;
@@ -1897,6 +1906,28 @@ async function gaSocialReferralHistory(project, format) {
1897
1906
  );
1898
1907
  }
1899
1908
  }
1909
+ async function gaSessionHistory(project, opts) {
1910
+ const client = getClient3();
1911
+ const result = await client.gaSessionHistory(project, opts?.window ? { window: opts.window } : void 0);
1912
+ if (opts?.format === "json") {
1913
+ console.log(JSON.stringify(result, null, 2));
1914
+ return;
1915
+ }
1916
+ if (result.length === 0) {
1917
+ console.log(`No session history.${opts?.window ? " Try a wider window or omit --window." : ' Run "canonry ga sync <project>" first.'}`);
1918
+ return;
1919
+ }
1920
+ const dateWidth = 12;
1921
+ console.log(`GA4 Session History for "${project}":
1922
+ `);
1923
+ console.log(` ${"DATE".padEnd(dateWidth)} ${"SESSIONS".padEnd(10)}${"ORGANIC".padEnd(10)}${"USERS".padEnd(8)}`);
1924
+ console.log(` ${"\u2500".repeat(dateWidth)} ${"\u2500".repeat(10)}${"\u2500".repeat(10)}${"\u2500".repeat(8)}`);
1925
+ for (const row of result) {
1926
+ console.log(
1927
+ ` ${row.date.padEnd(dateWidth)} ${String(row.sessions).padEnd(10)}${String(row.organicSessions).padEnd(10)}${String(row.users).padEnd(8)}`
1928
+ );
1929
+ }
1930
+ }
1900
1931
  async function gaCoverage(project, format) {
1901
1932
  const client = getClient3();
1902
1933
  const result = await client.gaCoverage(project);
@@ -2034,9 +2065,12 @@ async function gaAttribution(project, opts) {
2034
2065
  const m = trend.socialBiggestMover;
2035
2066
  console.log(` Social Mover: ${m.source} (${m.changePct >= 0 ? "+" : ""}${m.changePct}%, ${m.sessionsPrev7d}\u2192${m.sessions7d} sessions/7d)`);
2036
2067
  }
2037
- if (traffic.lastSyncedAt) {
2068
+ if (traffic.periodStart && traffic.periodEnd) {
2038
2069
  console.log(`
2039
- Last synced: ${traffic.lastSyncedAt}`);
2070
+ Period: ${traffic.periodStart} to ${traffic.periodEnd}`);
2071
+ }
2072
+ if (traffic.lastSyncedAt) {
2073
+ console.log(` Last synced: ${traffic.lastSyncedAt}`);
2040
2074
  }
2041
2075
  return;
2042
2076
  }
@@ -2053,7 +2087,9 @@ async function gaAttribution(project, opts) {
2053
2087
  socialSharePct: traffic.socialSharePct,
2054
2088
  organicSharePct: traffic.organicSharePct,
2055
2089
  aiReferrals: traffic.aiReferrals,
2056
- socialReferrals: traffic.socialReferrals
2090
+ socialReferrals: traffic.socialReferrals,
2091
+ periodStart: traffic.periodStart,
2092
+ periodEnd: traffic.periodEnd
2057
2093
  }, null, 2));
2058
2094
  return;
2059
2095
  }
@@ -2091,9 +2127,12 @@ async function gaAttribution(project, opts) {
2091
2127
  console.log(` ${ref.source.padEnd(25)} ${String(ref.sessions).padEnd(8)} sessions (${chanLabel})`);
2092
2128
  }
2093
2129
  }
2094
- if (traffic.lastSyncedAt) {
2130
+ if (traffic.periodStart && traffic.periodEnd) {
2095
2131
  console.log(`
2096
- Last synced: ${traffic.lastSyncedAt}`);
2132
+ Period: ${traffic.periodStart} to ${traffic.periodEnd}`);
2133
+ }
2134
+ if (traffic.lastSyncedAt) {
2135
+ console.log(` Last synced: ${traffic.lastSyncedAt}`);
2097
2136
  }
2098
2137
  }
2099
2138
 
@@ -2158,16 +2197,19 @@ var GA_CLI_COMMANDS = [
2158
2197
  },
2159
2198
  {
2160
2199
  path: ["ga", "traffic"],
2161
- usage: "canonry ga traffic <project> [--limit 50] [--format json]",
2200
+ usage: "canonry ga traffic <project> [--limit 50] [--window 30d] [--format json]",
2162
2201
  options: {
2163
- limit: stringOption()
2202
+ limit: stringOption(),
2203
+ window: stringOption()
2164
2204
  },
2165
2205
  run: async (input) => {
2166
- const project = requireProject(input, "ga.traffic", "canonry ga traffic <project> [--limit 50] [--format json]");
2206
+ const project = requireProject(input, "ga.traffic", "canonry ga traffic <project> [--limit 50] [--window 30d] [--format json]");
2167
2207
  const limitStr = getString(input.values, "limit");
2168
2208
  const limit = limitStr ? parseInt(limitStr, 10) : void 0;
2209
+ const window = getString(input.values, "window");
2169
2210
  await gaTraffic(project, {
2170
2211
  limit,
2212
+ window,
2171
2213
  format: input.format
2172
2214
  });
2173
2215
  }
@@ -2182,18 +2224,44 @@ var GA_CLI_COMMANDS = [
2182
2224
  },
2183
2225
  {
2184
2226
  path: ["ga", "ai-referral-history"],
2185
- usage: "canonry ga ai-referral-history <project> [--format json]",
2227
+ usage: "canonry ga ai-referral-history <project> [--window 30d] [--format json]",
2228
+ options: {
2229
+ window: stringOption()
2230
+ },
2186
2231
  run: async (input) => {
2187
- const project = requireProject(input, "ga.ai-referral-history", "canonry ga ai-referral-history <project> [--format json]");
2188
- await gaAiReferralHistory(project, input.format);
2232
+ const project = requireProject(input, "ga.ai-referral-history", "canonry ga ai-referral-history <project> [--window 30d] [--format json]");
2233
+ await gaAiReferralHistory(project, {
2234
+ window: getString(input.values, "window"),
2235
+ format: input.format
2236
+ });
2189
2237
  }
2190
2238
  },
2191
2239
  {
2192
2240
  path: ["ga", "social-referral-history"],
2193
- usage: "canonry ga social-referral-history <project> [--format json]",
2241
+ usage: "canonry ga social-referral-history <project> [--window 30d] [--format json]",
2242
+ options: {
2243
+ window: stringOption()
2244
+ },
2194
2245
  run: async (input) => {
2195
- const project = requireProject(input, "ga.social-referral-history", "canonry ga social-referral-history <project> [--format json]");
2196
- await gaSocialReferralHistory(project, input.format);
2246
+ const project = requireProject(input, "ga.social-referral-history", "canonry ga social-referral-history <project> [--window 30d] [--format json]");
2247
+ await gaSocialReferralHistory(project, {
2248
+ window: getString(input.values, "window"),
2249
+ format: input.format
2250
+ });
2251
+ }
2252
+ },
2253
+ {
2254
+ path: ["ga", "session-history"],
2255
+ usage: "canonry ga session-history <project> [--window 30d] [--format json]",
2256
+ options: {
2257
+ window: stringOption()
2258
+ },
2259
+ run: async (input) => {
2260
+ const project = requireProject(input, "ga.session-history", "canonry ga session-history <project> [--window 30d] [--format json]");
2261
+ await gaSessionHistory(project, {
2262
+ window: getString(input.values, "window"),
2263
+ format: input.format
2264
+ });
2197
2265
  }
2198
2266
  },
2199
2267
  {
@@ -2231,7 +2299,7 @@ var GA_CLI_COMMANDS = [
2231
2299
  unknownSubcommand(input.positionals[0], {
2232
2300
  command: "ga",
2233
2301
  usage: "canonry ga <subcommand> <project> [args]",
2234
- available: ["connect", "disconnect", "status", "sync", "traffic", "coverage", "ai-referral-history", "social-referral-history", "social-referral-summary", "attribution"]
2302
+ available: ["connect", "disconnect", "status", "sync", "traffic", "coverage", "ai-referral-history", "social-referral-history", "session-history", "social-referral-summary", "attribution"]
2235
2303
  });
2236
2304
  }
2237
2305
  }
@@ -2387,10 +2455,10 @@ Open this URL in your browser to authorize Google ${opts.type.toUpperCase()} acc
2387
2455
  console.log("(Ensure this URI is listed in your Google Cloud Console OAuth client's authorized redirect URIs)\n");
2388
2456
  }
2389
2457
  try {
2390
- const { spawn: spawn2 } = await import("child_process");
2458
+ const { spawn: spawn3 } = await import("child_process");
2391
2459
  const platform = process.platform;
2392
2460
  const [cmd, ...extraArgs] = platform === "darwin" ? ["open", authUrl] : platform === "win32" ? ["cmd", "/c", "start", "", authUrl] : ["xdg-open", authUrl];
2393
- spawn2(cmd, [...extraArgs], { detached: true, stdio: "ignore" }).unref();
2461
+ spawn3(cmd, [...extraArgs], { detached: true, stdio: "ignore" }).unref();
2394
2462
  console.log("(Browser opened automatically)");
2395
2463
  } catch {
2396
2464
  console.log("(Could not open browser automatically \u2014 please copy the URL above)");
@@ -6075,6 +6143,15 @@ var DEFAULT_QUOTA = {
6075
6143
  maxRequestsPerMinute: 10,
6076
6144
  maxRequestsPerDay: 500
6077
6145
  };
6146
+ var DEFAULT_AGENT_MODELS = {
6147
+ anthropic: "anthropic/claude-sonnet-4-6",
6148
+ openai: "openai/gpt-4o",
6149
+ openrouter: "openrouter/anthropic/claude-sonnet-4-6",
6150
+ groq: "groq/llama-4-scout-17b",
6151
+ google: "google/gemini-2.5-flash",
6152
+ mistral: "mistral/mistral-large-latest",
6153
+ xai: "xai/grok-2"
6154
+ };
6078
6155
  async function initCommand(opts) {
6079
6156
  const format = opts?.format ?? "text";
6080
6157
  if (format !== "json") {
@@ -6087,11 +6164,11 @@ async function initCommand(opts) {
6087
6164
  reason: "config_exists",
6088
6165
  configPath: getConfigPath()
6089
6166
  }, null, 2));
6090
- return;
6167
+ return void 0;
6091
6168
  }
6092
6169
  console.log(`Config already exists at ${getConfigPath()}`);
6093
6170
  console.log('To reinitialize, run "canonry init --force".');
6094
- return;
6171
+ return void 0;
6095
6172
  }
6096
6173
  const configDir = getConfigDir();
6097
6174
  if (!fs6.existsSync(configDir)) {
@@ -6246,6 +6323,29 @@ Config saved to ${getConfigPath()}`);
6246
6323
  console.log(`API key: ${rawApiKey}`);
6247
6324
  console.log(`Providers: ${providerNames.join(", ")}`);
6248
6325
  }
6326
+ let agentLLM;
6327
+ const agentProvider = opts?.agentProvider;
6328
+ const agentKey = opts?.agentKey;
6329
+ const agentModel = opts?.agentModel;
6330
+ if (agentProvider || agentKey || agentModel) {
6331
+ const provider = agentProvider ?? "anthropic";
6332
+ agentLLM = {
6333
+ provider,
6334
+ key: agentKey,
6335
+ model: agentModel ?? DEFAULT_AGENT_MODELS[provider]
6336
+ };
6337
+ } else if (!nonInteractive) {
6338
+ console.log("\nConfigure agent LLM (the model that powers the agent):");
6339
+ console.log("Supported providers: anthropic, openai, openrouter, groq, mistral, xai, google, cerebras\n");
6340
+ const provider = await prompt("Provider [anthropic]: ") || "anthropic";
6341
+ const key = await prompt("API key (press Enter to skip): ");
6342
+ if (key) {
6343
+ const defaultModel = DEFAULT_AGENT_MODELS[provider];
6344
+ const modelText = defaultModel ? `Model [${defaultModel}]: ` : "Model: ";
6345
+ const model = await prompt(modelText) || defaultModel;
6346
+ agentLLM = { provider, key, model };
6347
+ }
6348
+ }
6249
6349
  if (format !== "json") {
6250
6350
  showFirstRunNotice();
6251
6351
  console.log('Run "canonry serve" to start the server.');
@@ -6254,6 +6354,7 @@ Config saved to ${getConfigPath()}`);
6254
6354
  providerCount: providerNames.length,
6255
6355
  providers: providerNames
6256
6356
  });
6357
+ return agentLLM;
6257
6358
  }
6258
6359
 
6259
6360
  // src/commands/serve.ts
@@ -6816,12 +6917,12 @@ async function wordpressSetMeta(project, body) {
6816
6917
  printPageDetail(result);
6817
6918
  }
6818
6919
  async function wordpressBulkSetMeta(project, opts) {
6819
- const fs8 = await import("fs/promises");
6820
- const path6 = await import("path");
6821
- const filePath = path6.resolve(opts.from);
6920
+ const fs10 = await import("fs/promises");
6921
+ const path9 = await import("path");
6922
+ const filePath = path9.resolve(opts.from);
6822
6923
  let raw;
6823
6924
  try {
6824
- raw = await fs8.readFile(filePath, "utf8");
6925
+ raw = await fs10.readFile(filePath, "utf8");
6825
6926
  } catch {
6826
6927
  throw new CliError({
6827
6928
  code: "FILE_READ_ERROR",
@@ -6918,13 +7019,13 @@ async function wordpressSetSchema(project, body) {
6918
7019
  printManualAssist(`Schema update for "${body.slug}"`, result);
6919
7020
  }
6920
7021
  async function wordpressSchemaDeploy(project, opts) {
6921
- const fs8 = await import("fs/promises");
6922
- const path6 = await import("path");
7022
+ const fs10 = await import("fs/promises");
7023
+ const path9 = await import("path");
6923
7024
  const yaml = await import("yaml").catch(() => null);
6924
- const filePath = path6.resolve(opts.profile);
7025
+ const filePath = path9.resolve(opts.profile);
6925
7026
  let raw;
6926
7027
  try {
6927
- raw = await fs8.readFile(filePath, "utf8");
7028
+ raw = await fs10.readFile(filePath, "utf8");
6928
7029
  } catch {
6929
7030
  throw new CliError({
6930
7031
  code: "FILE_READ_ERROR",
@@ -7029,13 +7130,13 @@ async function wordpressOnboard(project, opts) {
7029
7130
  }
7030
7131
  let profileData;
7031
7132
  if (opts.profile) {
7032
- const fs8 = await import("fs/promises");
7033
- const path6 = await import("path");
7133
+ const fs10 = await import("fs/promises");
7134
+ const path9 = await import("path");
7034
7135
  const yaml = await import("yaml").catch(() => null);
7035
- const filePath = path6.resolve(opts.profile);
7136
+ const filePath = path9.resolve(opts.profile);
7036
7137
  let raw;
7037
7138
  try {
7038
- raw = await fs8.readFile(filePath, "utf8");
7139
+ raw = await fs10.readFile(filePath, "utf8");
7039
7140
  } catch {
7040
7141
  throw new CliError({
7041
7142
  code: "FILE_READ_ERROR",
@@ -7616,6 +7717,699 @@ var WORDPRESS_CLI_COMMANDS = [
7616
7717
  }
7617
7718
  ];
7618
7719
 
7720
+ // src/commands/agent.ts
7721
+ import path8 from "path";
7722
+
7723
+ // src/agent-manager.ts
7724
+ import { execFileSync, spawn as spawn2 } from "child_process";
7725
+ import fs8 from "fs";
7726
+ import path6 from "path";
7727
+ var log = createLogger("AgentManager");
7728
+ var PROCESS_MARKER = "canonry-openclaw-gateway";
7729
+ var AgentManager = class {
7730
+ constructor(config, stateDir) {
7731
+ this.config = config;
7732
+ this.stateDir = stateDir;
7733
+ this.processJsonPath = path6.join(stateDir, "process.json");
7734
+ }
7735
+ processJsonPath;
7736
+ /**
7737
+ * Check if the gateway process is running.
7738
+ * Cleans up stale process.json if the process is dead or belongs to a
7739
+ * different process (PID reuse).
7740
+ */
7741
+ status() {
7742
+ const info = this.readProcessInfo();
7743
+ if (!info) {
7744
+ return { state: "stopped" };
7745
+ }
7746
+ if (info.marker !== PROCESS_MARKER) {
7747
+ this.removeProcessJson();
7748
+ return { state: "stopped" };
7749
+ }
7750
+ if (isProcessAlive2(info.pid) && this.verifyProcessIdentity(info.pid)) {
7751
+ return {
7752
+ state: "running",
7753
+ pid: info.pid,
7754
+ port: info.gatewayPort,
7755
+ startedAt: info.startedAt
7756
+ };
7757
+ }
7758
+ this.removeProcessJson();
7759
+ return { state: "stopped" };
7760
+ }
7761
+ /**
7762
+ * Start the OpenClaw gateway as a detached background process.
7763
+ * Idempotent — no-op if already running.
7764
+ * Waits briefly for the process to confirm it hasn't crashed on startup.
7765
+ */
7766
+ async start() {
7767
+ const currentStatus = this.status();
7768
+ if (currentStatus.state === "running") {
7769
+ log.info("already.running", { pid: currentStatus.pid });
7770
+ return;
7771
+ }
7772
+ const binary = this.config.binary ?? "openclaw";
7773
+ const profile = this.config.profile ?? "aero";
7774
+ const port = this.config.gatewayPort ?? 3579;
7775
+ if (!fs8.existsSync(this.stateDir)) {
7776
+ fs8.mkdirSync(this.stateDir, { recursive: true });
7777
+ }
7778
+ const logFile = path6.join(this.stateDir, "gateway.log");
7779
+ const logFd = fs8.openSync(logFile, "a");
7780
+ const dotEnv = this.loadDotEnv();
7781
+ const child = spawn2(binary, ["--profile", profile, "gateway"], {
7782
+ detached: true,
7783
+ stdio: ["ignore", logFd, logFd],
7784
+ env: {
7785
+ ...process.env,
7786
+ ...dotEnv,
7787
+ OPENCLAW_PROFILE: profile,
7788
+ OPENCLAW_GATEWAY_PORT: String(port),
7789
+ OPENCLAW_STATE_DIR: this.stateDir
7790
+ }
7791
+ });
7792
+ const startupResult = await new Promise((resolve) => {
7793
+ let settled = false;
7794
+ const settle = (r) => {
7795
+ if (settled) return;
7796
+ settled = true;
7797
+ resolve(r);
7798
+ };
7799
+ child.on("error", (err) => settle({ error: err }));
7800
+ child.on("exit", (code) => settle({ exitCode: code }));
7801
+ setTimeout(() => settle({}), 500);
7802
+ });
7803
+ child.unref();
7804
+ fs8.closeSync(logFd);
7805
+ if (startupResult.error) {
7806
+ throw new Error(`Failed to start OpenClaw gateway: ${startupResult.error.message}`);
7807
+ }
7808
+ if (startupResult.exitCode != null) {
7809
+ throw new Error(`OpenClaw gateway exited immediately (code ${startupResult.exitCode}). Check ${path6.join(this.stateDir, "gateway.log")} for details.`);
7810
+ }
7811
+ if (child.pid == null) {
7812
+ throw new Error("Failed to start OpenClaw gateway: no PID returned by spawn");
7813
+ }
7814
+ if (!isProcessAlive2(child.pid)) {
7815
+ throw new Error(`OpenClaw gateway exited immediately after spawn. Check ${path6.join(this.stateDir, "gateway.log")} for details.`);
7816
+ }
7817
+ const processInfo = {
7818
+ pid: child.pid,
7819
+ gatewayPort: port,
7820
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
7821
+ marker: PROCESS_MARKER
7822
+ };
7823
+ fs8.writeFileSync(this.processJsonPath, JSON.stringify(processInfo, null, 2), "utf-8");
7824
+ log.info("started", { pid: child.pid, port });
7825
+ }
7826
+ /**
7827
+ * Stop the gateway process.
7828
+ * Uses DenchClaw escalation: SIGTERM → 800ms poll → SIGKILL.
7829
+ * Idempotent — no-op if already stopped.
7830
+ */
7831
+ async stop() {
7832
+ const info = this.readProcessInfo();
7833
+ if (!info) return;
7834
+ if (isProcessAlive2(info.pid) && info.marker === PROCESS_MARKER && this.verifyProcessIdentity(info.pid)) {
7835
+ await terminateWithEscalation(info.pid);
7836
+ }
7837
+ this.removeProcessJson();
7838
+ log.info("stopped", { pid: info.pid });
7839
+ }
7840
+ /**
7841
+ * Stop the gateway, wipe the workspace directory, and prepare for re-seeding.
7842
+ */
7843
+ async reset() {
7844
+ await this.stop();
7845
+ const workspaceDir = path6.join(this.stateDir, "workspace");
7846
+ if (fs8.existsSync(workspaceDir)) {
7847
+ fs8.rmSync(workspaceDir, { recursive: true, force: true });
7848
+ log.info("workspace.wiped", { dir: workspaceDir });
7849
+ }
7850
+ }
7851
+ /**
7852
+ * Verify that the PID actually belongs to an openclaw process by checking
7853
+ * the full command line. Requires "openclaw" in the args to avoid matching
7854
+ * unrelated Node processes after PID reuse.
7855
+ */
7856
+ verifyProcessIdentity(pid) {
7857
+ try {
7858
+ if (process.platform === "darwin") {
7859
+ const out = execFileSync("ps", ["-p", String(pid), "-o", "args="], {
7860
+ encoding: "utf-8",
7861
+ timeout: 2e3
7862
+ }).trim();
7863
+ return out.includes("openclaw");
7864
+ }
7865
+ if (process.platform === "linux") {
7866
+ const cmdline = fs8.readFileSync(`/proc/${pid}/cmdline`, "utf-8");
7867
+ return cmdline.includes("openclaw");
7868
+ }
7869
+ return true;
7870
+ } catch {
7871
+ return false;
7872
+ }
7873
+ }
7874
+ readProcessInfo() {
7875
+ if (!fs8.existsSync(this.processJsonPath)) return null;
7876
+ try {
7877
+ return JSON.parse(fs8.readFileSync(this.processJsonPath, "utf-8"));
7878
+ } catch {
7879
+ return null;
7880
+ }
7881
+ }
7882
+ removeProcessJson() {
7883
+ try {
7884
+ fs8.unlinkSync(this.processJsonPath);
7885
+ } catch {
7886
+ }
7887
+ }
7888
+ /** Parse a simple KEY=value dotenv file from the state dir. */
7889
+ loadDotEnv() {
7890
+ const envFile = path6.join(this.stateDir, ".env");
7891
+ if (!fs8.existsSync(envFile)) return {};
7892
+ const result = {};
7893
+ for (const line of fs8.readFileSync(envFile, "utf-8").split("\n")) {
7894
+ const trimmed = line.trim();
7895
+ if (!trimmed || trimmed.startsWith("#")) continue;
7896
+ const eq3 = trimmed.indexOf("=");
7897
+ if (eq3 < 1) continue;
7898
+ result[trimmed.slice(0, eq3)] = trimmed.slice(eq3 + 1);
7899
+ }
7900
+ return result;
7901
+ }
7902
+ };
7903
+ function isProcessAlive2(pid) {
7904
+ try {
7905
+ process.kill(pid, 0);
7906
+ return true;
7907
+ } catch {
7908
+ return false;
7909
+ }
7910
+ }
7911
+ async function terminateWithEscalation(pid) {
7912
+ try {
7913
+ process.kill(pid, "SIGTERM");
7914
+ } catch {
7915
+ return;
7916
+ }
7917
+ const deadline = Date.now() + 800;
7918
+ while (Date.now() < deadline) {
7919
+ if (!isProcessAlive2(pid)) return;
7920
+ await new Promise((resolve) => setTimeout(resolve, 100));
7921
+ }
7922
+ try {
7923
+ process.kill(pid, "SIGKILL");
7924
+ } catch {
7925
+ }
7926
+ }
7927
+
7928
+ // src/agent-bootstrap.ts
7929
+ import { execFileSync as execFileSync2, execSync } from "child_process";
7930
+ import fs9 from "fs";
7931
+ import os from "os";
7932
+ import path7 from "path";
7933
+ import { fileURLToPath } from "url";
7934
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
7935
+ var cachedResult = null;
7936
+ var cachedAt = 0;
7937
+ function getAeroStateDir(profile = "aero") {
7938
+ return path7.join(os.homedir(), `.openclaw-${profile}`);
7939
+ }
7940
+ async function detectOpenClaw(config) {
7941
+ if (cachedResult && Date.now() - cachedAt < CACHE_TTL_MS) {
7942
+ return cachedResult;
7943
+ }
7944
+ let result;
7945
+ if (config?.binary) {
7946
+ const version = probeVersion(config.binary);
7947
+ if (version) {
7948
+ result = { found: true, path: config.binary, version };
7949
+ cachedResult = result;
7950
+ cachedAt = Date.now();
7951
+ return result;
7952
+ }
7953
+ }
7954
+ const binaryPath = findInPath();
7955
+ if (binaryPath) {
7956
+ const version = probeVersion(binaryPath);
7957
+ if (version) {
7958
+ result = { found: true, path: binaryPath, version };
7959
+ cachedResult = result;
7960
+ cachedAt = Date.now();
7961
+ return result;
7962
+ }
7963
+ }
7964
+ result = { found: false };
7965
+ cachedResult = result;
7966
+ cachedAt = Date.now();
7967
+ return result;
7968
+ }
7969
+ detectOpenClaw.resetCache = () => {
7970
+ cachedResult = null;
7971
+ cachedAt = 0;
7972
+ };
7973
+ function probeVersion(binaryPath) {
7974
+ try {
7975
+ const output = execFileSync2(binaryPath, ["--version"], {
7976
+ timeout: 5e3,
7977
+ encoding: "utf-8"
7978
+ });
7979
+ const match = output.toString().trim().match(/(\d+\.\d+\.\d+)/);
7980
+ return match ? match[1] : output.toString().trim();
7981
+ } catch {
7982
+ return null;
7983
+ }
7984
+ }
7985
+ function findInPath() {
7986
+ const cmd = process.platform === "win32" ? "where" : "which";
7987
+ try {
7988
+ const output = execFileSync2(cmd, ["openclaw"], {
7989
+ timeout: 5e3,
7990
+ encoding: "utf-8"
7991
+ });
7992
+ return output.toString().trim().split("\n")[0] || null;
7993
+ } catch {
7994
+ return null;
7995
+ }
7996
+ }
7997
+ async function installOpenClaw(opts) {
7998
+ try {
7999
+ execSync("npm install -g openclaw", {
8000
+ timeout: 12e4,
8001
+ stdio: opts?.silent ? "pipe" : "inherit"
8002
+ });
8003
+ } catch (err) {
8004
+ return {
8005
+ success: false,
8006
+ error: err instanceof Error ? err.message : String(err)
8007
+ };
8008
+ }
8009
+ detectOpenClaw.resetCache();
8010
+ const detection = await detectOpenClaw();
8011
+ if (!detection.found) {
8012
+ return {
8013
+ success: false,
8014
+ error: "npm install succeeded but openclaw binary was not found in PATH"
8015
+ };
8016
+ }
8017
+ return { success: true, detection };
8018
+ }
8019
+ function seedWorkspace(stateDir) {
8020
+ const workspaceDir = path7.join(stateDir, "workspace");
8021
+ fs9.mkdirSync(workspaceDir, { recursive: true });
8022
+ const __dirname = path7.dirname(fileURLToPath(import.meta.url));
8023
+ const assetsDir = path7.join(__dirname, "..", "assets", "agent-workspace");
8024
+ if (!fs9.existsSync(assetsDir)) {
8025
+ return;
8026
+ }
8027
+ copyDirRecursive(assetsDir, workspaceDir);
8028
+ }
8029
+ function initializeOpenClawProfile(binary, profile, workspaceDir) {
8030
+ try {
8031
+ execFileSync2(binary, [
8032
+ "--profile",
8033
+ profile,
8034
+ "onboard",
8035
+ "--non-interactive",
8036
+ "--accept-risk",
8037
+ "--mode",
8038
+ "local",
8039
+ "--workspace",
8040
+ workspaceDir,
8041
+ "--skip-channels",
8042
+ "--skip-skills",
8043
+ "--skip-health",
8044
+ "--no-install-daemon"
8045
+ ], { timeout: 3e4, stdio: "pipe" });
8046
+ } catch (err) {
8047
+ const stderr = err instanceof Error && "stderr" in err ? String(err.stderr) : "";
8048
+ if (stderr.toLowerCase().includes("already")) return;
8049
+ throw new CliError({
8050
+ code: "AGENT_PROFILE_INIT_FAILED",
8051
+ message: `Failed to initialize OpenClaw profile: ${stderr || (err instanceof Error ? err.message : String(err))}`,
8052
+ displayMessage: `Failed to initialize OpenClaw profile "${profile}".`
8053
+ });
8054
+ }
8055
+ }
8056
+ function configureOpenClawGateway(binary, profile, gatewayPort) {
8057
+ const entries = [
8058
+ ["gateway.mode", "local", false],
8059
+ ["gateway.port", String(gatewayPort), true]
8060
+ ];
8061
+ for (const [key, value, strict] of entries) {
8062
+ try {
8063
+ const args = ["--profile", profile, "config", "set", key, value];
8064
+ if (strict) args.push("--strict-json");
8065
+ execFileSync2(binary, args, { timeout: 1e4, stdio: "pipe" });
8066
+ } catch (err) {
8067
+ throw new CliError({
8068
+ code: "AGENT_GATEWAY_CONFIG_FAILED",
8069
+ message: `Failed to set ${key}=${value}: ${err instanceof Error ? err.message : String(err)}`,
8070
+ displayMessage: `Failed to configure OpenClaw gateway (${key}).`
8071
+ });
8072
+ }
8073
+ }
8074
+ }
8075
+ function setOpenClawModel(binary, profile, model) {
8076
+ try {
8077
+ execFileSync2(binary, [
8078
+ "--profile",
8079
+ profile,
8080
+ "models",
8081
+ "set",
8082
+ model
8083
+ ], { timeout: 1e4, stdio: "pipe" });
8084
+ } catch (err) {
8085
+ throw new CliError({
8086
+ code: "AGENT_MODEL_SET_FAILED",
8087
+ message: `Failed to set agent model to ${model}: ${err instanceof Error ? err.message : String(err)}`,
8088
+ displayMessage: `Failed to set agent model to "${model}".`
8089
+ });
8090
+ }
8091
+ }
8092
+ function providerEnvVar(provider) {
8093
+ const map = {
8094
+ anthropic: "ANTHROPIC_API_KEY",
8095
+ openai: "OPENAI_API_KEY",
8096
+ google: "GOOGLE_API_KEY",
8097
+ "google-vertex": "GOOGLE_API_KEY",
8098
+ groq: "GROQ_API_KEY",
8099
+ mistral: "MISTRAL_API_KEY",
8100
+ xai: "XAI_API_KEY",
8101
+ openrouter: "OPENROUTER_API_KEY",
8102
+ cerebras: "CEREBRAS_API_KEY"
8103
+ };
8104
+ return map[provider] ?? `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
8105
+ }
8106
+ function writeAgentEnv(stateDir, key, value) {
8107
+ const envFile = path7.join(stateDir, ".env");
8108
+ let lines = [];
8109
+ if (fs9.existsSync(envFile)) {
8110
+ lines = fs9.readFileSync(envFile, "utf-8").split("\n");
8111
+ }
8112
+ const prefix = `${key}=`;
8113
+ const idx = lines.findIndex((l) => l.startsWith(prefix));
8114
+ const entry = `${key}=${value}`;
8115
+ if (idx >= 0) {
8116
+ lines[idx] = entry;
8117
+ } else {
8118
+ lines.push(entry);
8119
+ }
8120
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
8121
+ fs9.writeFileSync(envFile, lines.join("\n") + "\n", "utf-8");
8122
+ }
8123
+ function resolveAgentCredentials(opts) {
8124
+ const provider = opts.agentProvider ?? "anthropic";
8125
+ if (opts.agentKey) {
8126
+ return { provider, key: opts.agentKey, model: opts.agentModel };
8127
+ }
8128
+ const envVar = providerEnvVar(provider);
8129
+ const envKey = process.env[envVar];
8130
+ if (envKey) {
8131
+ return { provider, key: envKey, model: opts.agentModel };
8132
+ }
8133
+ const genericKey = process.env.CANONRY_AGENT_KEY;
8134
+ if (genericKey) {
8135
+ return { provider, key: genericKey, model: opts.agentModel };
8136
+ }
8137
+ const envFile = path7.join(opts.stateDir, ".env");
8138
+ if (fs9.existsSync(envFile)) {
8139
+ const hasKey = fs9.readFileSync(envFile, "utf-8").split("\n").some((l) => l.includes("_API_KEY="));
8140
+ if (hasKey) {
8141
+ return { provider, key: void 0, model: opts.agentModel };
8142
+ }
8143
+ }
8144
+ return { provider, key: void 0, model: opts.agentModel };
8145
+ }
8146
+ function copyDirRecursive(src, dest) {
8147
+ fs9.mkdirSync(dest, { recursive: true });
8148
+ for (const entry of fs9.readdirSync(src, { withFileTypes: true })) {
8149
+ const srcPath = path7.join(src, entry.name);
8150
+ const destPath = path7.join(dest, entry.name);
8151
+ if (entry.isDirectory()) {
8152
+ copyDirRecursive(srcPath, destPath);
8153
+ } else {
8154
+ fs9.copyFileSync(srcPath, destPath);
8155
+ }
8156
+ }
8157
+ }
8158
+
8159
+ // src/commands/agent.ts
8160
+ function resolveStateDir(opts) {
8161
+ if (opts?.stateDir) return opts.stateDir;
8162
+ try {
8163
+ const config = loadConfig();
8164
+ const profile = config.agent?.profile ?? "aero";
8165
+ return getAeroStateDir(profile);
8166
+ } catch {
8167
+ return getAeroStateDir();
8168
+ }
8169
+ }
8170
+ function resolveConfig() {
8171
+ try {
8172
+ return loadConfig().agent ?? {};
8173
+ } catch {
8174
+ return {};
8175
+ }
8176
+ }
8177
+ async function agentStatus(opts) {
8178
+ const stateDir = resolveStateDir(opts);
8179
+ const config = resolveConfig();
8180
+ const mgr = new AgentManager(config, stateDir);
8181
+ const status = mgr.status();
8182
+ if (opts?.format === "json") {
8183
+ console.log(JSON.stringify(status, null, 2));
8184
+ return;
8185
+ }
8186
+ if (status.state === "running") {
8187
+ console.log(`Agent: running (PID ${status.pid}, port ${status.port})`);
8188
+ if (status.startedAt) {
8189
+ console.log(`Started: ${status.startedAt}`);
8190
+ }
8191
+ } else {
8192
+ console.log("Agent: stopped");
8193
+ }
8194
+ }
8195
+ async function agentStart(opts) {
8196
+ const stateDir = resolveStateDir(opts);
8197
+ const config = resolveConfig();
8198
+ const mgr = new AgentManager(config, stateDir);
8199
+ await mgr.start();
8200
+ const status = mgr.status();
8201
+ if (opts?.format === "json") {
8202
+ console.log(JSON.stringify(status, null, 2));
8203
+ } else {
8204
+ console.log(`Agent started (PID ${status.pid}, port ${status.port})`);
8205
+ }
8206
+ }
8207
+ async function agentStop(opts) {
8208
+ const stateDir = resolveStateDir(opts);
8209
+ const config = resolveConfig();
8210
+ const mgr = new AgentManager(config, stateDir);
8211
+ await mgr.stop();
8212
+ if (opts?.format === "json") {
8213
+ console.log(JSON.stringify({ state: "stopped" }, null, 2));
8214
+ } else {
8215
+ console.log("Agent stopped");
8216
+ }
8217
+ }
8218
+ async function agentReset(opts) {
8219
+ const stateDir = resolveStateDir(opts);
8220
+ const config = resolveConfig();
8221
+ const mgr = new AgentManager(config, stateDir);
8222
+ await mgr.reset();
8223
+ if (opts?.format === "json") {
8224
+ console.log(JSON.stringify({ state: "reset" }, null, 2));
8225
+ } else {
8226
+ console.log('Agent reset \u2014 workspace wiped. Run "canonry agent setup" to re-initialize.');
8227
+ }
8228
+ }
8229
+ async function agentSetup(opts) {
8230
+ const isJson = opts?.format === "json";
8231
+ let agentLLM;
8232
+ if (!configExists()) {
8233
+ const initOpts = {
8234
+ geminiKey: opts?.geminiKey,
8235
+ openaiKey: opts?.openaiKey,
8236
+ claudeKey: opts?.claudeKey,
8237
+ perplexityKey: opts?.perplexityKey,
8238
+ localUrl: opts?.localUrl,
8239
+ localModel: opts?.localModel,
8240
+ localKey: opts?.localKey,
8241
+ googleClientId: opts?.googleClientId,
8242
+ googleClientSecret: opts?.googleClientSecret,
8243
+ agentProvider: opts?.agentProvider,
8244
+ agentKey: opts?.agentKey,
8245
+ agentModel: opts?.agentModel
8246
+ };
8247
+ if (isJson) {
8248
+ agentLLM = await suppressStdout(() => initCommand(initOpts)) ?? void 0;
8249
+ } else {
8250
+ agentLLM = await initCommand(initOpts) ?? void 0;
8251
+ }
8252
+ }
8253
+ const existingConfig = resolveConfig();
8254
+ let detection = await detectOpenClaw(existingConfig);
8255
+ if (!detection.found) {
8256
+ detection = await autoInstallOrFail(opts?.format);
8257
+ }
8258
+ const profile = existingConfig.profile ?? "aero";
8259
+ const gatewayPort = opts?.gatewayPort ?? existingConfig.gatewayPort ?? 3579;
8260
+ const stateDir = opts?.stateDir ?? getAeroStateDir(profile);
8261
+ saveConfigPatch({
8262
+ agent: {
8263
+ binary: detection.path,
8264
+ profile,
8265
+ gatewayPort,
8266
+ autoStart: existingConfig.autoStart
8267
+ }
8268
+ });
8269
+ initializeOpenClawProfile(detection.path, profile, path8.join(stateDir, "workspace"));
8270
+ configureOpenClawGateway(detection.path, profile, gatewayPort);
8271
+ const creds = agentLLM ?? resolveAgentCredentials({
8272
+ agentProvider: opts?.agentProvider,
8273
+ agentKey: opts?.agentKey,
8274
+ agentModel: opts?.agentModel,
8275
+ stateDir
8276
+ });
8277
+ if (creds.key) {
8278
+ writeAgentEnv(stateDir, providerEnvVar(creds.provider), creds.key);
8279
+ if (opts?.format !== "json") {
8280
+ console.log(`Agent LLM: ${creds.provider} credentials configured`);
8281
+ }
8282
+ }
8283
+ if (creds.model) {
8284
+ setOpenClawModel(detection.path, profile, creds.model);
8285
+ if (opts?.format !== "json") {
8286
+ console.log(`Agent model: ${creds.model}`);
8287
+ }
8288
+ }
8289
+ seedWorkspace(stateDir);
8290
+ if (opts?.format === "json") {
8291
+ console.log(JSON.stringify({
8292
+ state: "configured",
8293
+ binary: detection.path,
8294
+ version: detection.version,
8295
+ profile,
8296
+ gatewayPort,
8297
+ stateDir
8298
+ }, null, 2));
8299
+ } else {
8300
+ console.log(`OpenClaw: ${detection.path} (${detection.version})`);
8301
+ console.log(`Profile: ${profile}`);
8302
+ console.log(`Gateway port: ${gatewayPort}`);
8303
+ console.log(`State dir: ${stateDir}`);
8304
+ console.log("Agent setup complete.");
8305
+ }
8306
+ }
8307
+ async function autoInstallOrFail(format) {
8308
+ if (format !== "json") {
8309
+ console.log("OpenClaw not found, installing via npm...");
8310
+ }
8311
+ const install = await installOpenClaw({ silent: format === "json" });
8312
+ if (!install.success) {
8313
+ const msg = `Failed to install OpenClaw: ${install.error}`;
8314
+ if (format === "json") {
8315
+ console.error(JSON.stringify({ error: { code: "AGENT_INSTALL_FAILED", message: msg } }));
8316
+ } else {
8317
+ console.error(msg);
8318
+ }
8319
+ process.exitCode = 1;
8320
+ throw new Error(msg);
8321
+ }
8322
+ if (format !== "json") {
8323
+ console.log(`Installed OpenClaw ${install.detection.version}`);
8324
+ }
8325
+ return install.detection;
8326
+ }
8327
+ async function suppressStdout(fn) {
8328
+ const original = console.log;
8329
+ console.log = () => {
8330
+ };
8331
+ try {
8332
+ return await fn();
8333
+ } finally {
8334
+ console.log = original;
8335
+ }
8336
+ }
8337
+
8338
+ // src/cli-commands/agent.ts
8339
+ var AGENT_CLI_COMMANDS = [
8340
+ {
8341
+ path: ["agent", "status"],
8342
+ usage: "canonry agent status [--format json]",
8343
+ options: {},
8344
+ run: async (input) => {
8345
+ await agentStatus({ format: input.format });
8346
+ }
8347
+ },
8348
+ {
8349
+ path: ["agent", "start"],
8350
+ usage: "canonry agent start [--format json]",
8351
+ options: {},
8352
+ run: async (input) => {
8353
+ await agentStart({ format: input.format });
8354
+ }
8355
+ },
8356
+ {
8357
+ path: ["agent", "stop"],
8358
+ usage: "canonry agent stop [--format json]",
8359
+ options: {},
8360
+ run: async (input) => {
8361
+ await agentStop({ format: input.format });
8362
+ }
8363
+ },
8364
+ {
8365
+ path: ["agent", "reset"],
8366
+ usage: "canonry agent reset [--format json]",
8367
+ options: {},
8368
+ run: async (input) => {
8369
+ await agentReset({ format: input.format });
8370
+ }
8371
+ },
8372
+ {
8373
+ path: ["agent", "setup"],
8374
+ usage: "canonry agent setup [--agent-provider <id>] [--agent-key <key>] [--agent-model <model>] [--gateway-port <port>] [--gemini-key <key>] [--openai-key <key>] [--claude-key <key>] [--perplexity-key <key>] [--format json]",
8375
+ options: {
8376
+ "agent-provider": stringOption(),
8377
+ "agent-key": stringOption(),
8378
+ "agent-model": stringOption(),
8379
+ "gateway-port": { type: "string" },
8380
+ "gemini-key": stringOption(),
8381
+ "openai-key": stringOption(),
8382
+ "claude-key": stringOption(),
8383
+ "perplexity-key": stringOption(),
8384
+ "local-url": stringOption(),
8385
+ "local-model": stringOption(),
8386
+ "local-key": stringOption(),
8387
+ "google-client-id": stringOption(),
8388
+ "google-client-secret": stringOption()
8389
+ },
8390
+ run: async (input) => {
8391
+ const portStr = input.values["gateway-port"];
8392
+ const gatewayPort = typeof portStr === "string" ? Number.parseInt(portStr, 10) : void 0;
8393
+ await agentSetup({
8394
+ agentProvider: getString(input.values, "agent-provider"),
8395
+ agentKey: getString(input.values, "agent-key"),
8396
+ agentModel: getString(input.values, "agent-model"),
8397
+ gatewayPort,
8398
+ format: input.format,
8399
+ geminiKey: getString(input.values, "gemini-key"),
8400
+ openaiKey: getString(input.values, "openai-key"),
8401
+ claudeKey: getString(input.values, "claude-key"),
8402
+ perplexityKey: getString(input.values, "perplexity-key"),
8403
+ localUrl: getString(input.values, "local-url"),
8404
+ localModel: getString(input.values, "local-model"),
8405
+ localKey: getString(input.values, "local-key"),
8406
+ googleClientId: getString(input.values, "google-client-id"),
8407
+ googleClientSecret: getString(input.values, "google-client-secret")
8408
+ });
8409
+ }
8410
+ }
8411
+ ];
8412
+
7619
8413
  // src/cli-commands.ts
7620
8414
  var REGISTERED_CLI_COMMANDS = [
7621
8415
  ...BACKFILL_CLI_COMMANDS,
@@ -7634,7 +8428,8 @@ var REGISTERED_CLI_COMMANDS = [
7634
8428
  ...WORDPRESS_CLI_COMMANDS,
7635
8429
  ...CDP_CLI_COMMANDS,
7636
8430
  ...GA_CLI_COMMANDS,
7637
- ...INTELLIGENCE_CLI_COMMANDS
8431
+ ...INTELLIGENCE_CLI_COMMANDS,
8432
+ ...AGENT_CLI_COMMANDS
7638
8433
  ];
7639
8434
 
7640
8435
  // src/cli.ts