@ainyc/canonry 1.46.0 → 1.48.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/dist/cli.js CHANGED
@@ -1,34 +1,53 @@
1
1
  #!/usr/bin/env node --import tsx
2
2
  import {
3
+ AGENT_WEBHOOK_EVENTS,
4
+ AgentManager,
5
+ CliError,
6
+ EXIT_SYSTEM_ERROR,
7
+ EXIT_USER_ERROR,
3
8
  ProviderNames,
4
9
  RunKinds,
10
+ attachAgentWebhookDirect,
11
+ buildAgentWebhookUrl,
5
12
  computeCompetitorOverlap,
6
13
  configExists,
14
+ configureOpenClawGateway,
7
15
  createServer,
16
+ detectOpenClaw,
8
17
  determineAnswerMentioned,
9
18
  determineCitationState,
10
19
  effectiveDomains,
11
20
  extractRecommendedCompetitors,
12
21
  formatAuditFactorScore,
22
+ getAeroStateDir,
13
23
  getConfigDir,
14
24
  getConfigPath,
15
25
  getOrCreateAnonymousId,
26
+ initializeOpenClawProfile,
27
+ installOpenClaw,
16
28
  isFirstRun,
17
29
  isTelemetryEnabled,
18
30
  loadConfig,
19
31
  notificationEventSchema,
32
+ printCliError,
33
+ providerEnvVar,
20
34
  providerQuotaPolicySchema,
21
35
  reparseStoredResult,
22
36
  reparseStoredResult2,
23
37
  reparseStoredResult3,
24
38
  reparseStoredResult4,
39
+ resolveAgentCredentials,
25
40
  resolveProviderInput,
26
41
  saveConfig,
27
42
  saveConfigPatch,
43
+ seedWorkspace,
28
44
  setGoogleAuthConfig,
45
+ setOpenClawModel,
29
46
  showFirstRunNotice,
30
- trackEvent
31
- } from "./chunk-22RIKNII.js";
47
+ trackEvent,
48
+ usageError,
49
+ writeAgentEnv
50
+ } from "./chunk-25QLMK4F.js";
32
51
  import {
33
52
  apiKeys,
34
53
  competitors,
@@ -43,84 +62,14 @@ import {
43
62
  // src/cli.ts
44
63
  import { pathToFileURL } from "url";
45
64
 
46
- // src/cli-error.ts
47
- var EXIT_USER_ERROR = 1;
48
- var EXIT_SYSTEM_ERROR = 2;
49
- var CliError = class extends Error {
50
- code;
51
- displayMessage;
52
- details;
53
- exitCode;
54
- constructor(options) {
55
- super(options.message);
56
- this.name = "CliError";
57
- this.code = options.code;
58
- this.displayMessage = options.displayMessage;
59
- this.details = options.details;
60
- this.exitCode = options.exitCode ?? EXIT_USER_ERROR;
61
- }
62
- };
63
- function usageError(displayMessage, options) {
64
- const firstLine = displayMessage.split("\n", 1)[0] ?? "Error: invalid command usage";
65
- return new CliError({
66
- code: "CLI_USAGE_ERROR",
67
- message: options?.message ?? firstLine.replace(/^Error:\s*/, ""),
68
- displayMessage,
69
- details: options?.details
70
- });
71
- }
72
- function printCliError(err, format) {
73
- if (format === "json") {
74
- if (err instanceof CliError) {
75
- console.error(
76
- JSON.stringify(
77
- {
78
- error: {
79
- code: err.code,
80
- message: err.message,
81
- ...err.details ? { details: err.details } : {}
82
- }
83
- },
84
- null,
85
- 2
86
- )
87
- );
88
- return;
89
- }
90
- const message = err instanceof Error ? err.message : "An unexpected error occurred";
91
- console.error(
92
- JSON.stringify(
93
- {
94
- error: {
95
- code: "CLI_ERROR",
96
- message
97
- }
98
- },
99
- null,
100
- 2
101
- )
102
- );
103
- return;
104
- }
105
- if (err instanceof CliError && err.displayMessage) {
106
- console.error(err.displayMessage);
107
- return;
108
- }
109
- if (err instanceof Error) {
110
- console.error(`Error: ${err.message}`);
111
- return;
112
- }
113
- console.error("An unexpected error occurred");
114
- }
115
-
116
65
  // src/cli-dispatch.ts
117
66
  import { parseArgs } from "util";
118
67
  function commandId(spec) {
119
68
  return spec.path.join(".");
120
69
  }
121
- function matchesPath(args, path6) {
122
- if (args.length < path6.length) return false;
123
- return path6.every((segment, index) => args[index] === segment);
70
+ function matchesPath(args, path7) {
71
+ if (args.length < path7.length) return false;
72
+ return path7.every((segment, index) => args[index] === segment);
124
73
  }
125
74
  function withFormatOption(options) {
126
75
  if (!options) {
@@ -613,9 +562,9 @@ var ApiClient = class {
613
562
  }
614
563
  return this.probePromise;
615
564
  }
616
- async request(method, path6, body) {
565
+ async request(method, path7, body) {
617
566
  await this.probeBasePath();
618
- const url = `${this.baseUrl}${path6}`;
567
+ const url = `${this.baseUrl}${path7}`;
619
568
  const serializedBody = body != null ? JSON.stringify(body) : void 0;
620
569
  const headers = {
621
570
  "Authorization": `Bearer ${this.apiKey}`,
@@ -3523,7 +3472,9 @@ var EVENT_DESCRIPTIONS = {
3523
3472
  "citation.lost": "A keyword lost its citation status",
3524
3473
  "citation.gained": "A keyword gained citation status",
3525
3474
  "run.completed": "A visibility run completed successfully",
3526
- "run.failed": "A visibility run failed"
3475
+ "run.failed": "A visibility run failed",
3476
+ "insight.critical": "A critical-severity insight was generated",
3477
+ "insight.high": "A high-severity insight was generated"
3527
3478
  };
3528
3479
  function listEvents(format) {
3529
3480
  const events = notificationEventSchema.options;
@@ -6142,6 +6093,15 @@ var DEFAULT_QUOTA = {
6142
6093
  maxRequestsPerMinute: 10,
6143
6094
  maxRequestsPerDay: 500
6144
6095
  };
6096
+ var DEFAULT_AGENT_MODELS = {
6097
+ anthropic: "anthropic/claude-sonnet-4-6",
6098
+ openai: "openai/gpt-4o",
6099
+ openrouter: "openrouter/anthropic/claude-sonnet-4-6",
6100
+ groq: "groq/llama-4-scout-17b",
6101
+ google: "google/gemini-2.5-flash",
6102
+ mistral: "mistral/mistral-large-latest",
6103
+ xai: "xai/grok-2"
6104
+ };
6145
6105
  async function initCommand(opts) {
6146
6106
  const format = opts?.format ?? "text";
6147
6107
  if (format !== "json") {
@@ -6154,11 +6114,11 @@ async function initCommand(opts) {
6154
6114
  reason: "config_exists",
6155
6115
  configPath: getConfigPath()
6156
6116
  }, null, 2));
6157
- return;
6117
+ return void 0;
6158
6118
  }
6159
6119
  console.log(`Config already exists at ${getConfigPath()}`);
6160
6120
  console.log('To reinitialize, run "canonry init --force".');
6161
- return;
6121
+ return void 0;
6162
6122
  }
6163
6123
  const configDir = getConfigDir();
6164
6124
  if (!fs6.existsSync(configDir)) {
@@ -6313,6 +6273,29 @@ Config saved to ${getConfigPath()}`);
6313
6273
  console.log(`API key: ${rawApiKey}`);
6314
6274
  console.log(`Providers: ${providerNames.join(", ")}`);
6315
6275
  }
6276
+ let agentLLM;
6277
+ const agentProvider = opts?.agentProvider;
6278
+ const agentKey = opts?.agentKey;
6279
+ const agentModel = opts?.agentModel;
6280
+ if (agentProvider || agentKey || agentModel) {
6281
+ const provider = agentProvider ?? "anthropic";
6282
+ agentLLM = {
6283
+ provider,
6284
+ key: agentKey,
6285
+ model: agentModel ?? DEFAULT_AGENT_MODELS[provider]
6286
+ };
6287
+ } else if (!nonInteractive) {
6288
+ console.log("\nConfigure agent LLM (the model that powers the agent):");
6289
+ console.log("Supported providers: anthropic, openai, openrouter, groq, mistral, xai, google, cerebras\n");
6290
+ const provider = await prompt("Provider [anthropic]: ") || "anthropic";
6291
+ const key = await prompt("API key (press Enter to skip): ");
6292
+ if (key) {
6293
+ const defaultModel = DEFAULT_AGENT_MODELS[provider];
6294
+ const modelText = defaultModel ? `Model [${defaultModel}]: ` : "Model: ";
6295
+ const model = await prompt(modelText) || defaultModel;
6296
+ agentLLM = { provider, key, model };
6297
+ }
6298
+ }
6316
6299
  if (format !== "json") {
6317
6300
  showFirstRunNotice();
6318
6301
  console.log('Run "canonry serve" to start the server.');
@@ -6321,6 +6304,7 @@ Config saved to ${getConfigPath()}`);
6321
6304
  providerCount: providerNames.length,
6322
6305
  providers: providerNames
6323
6306
  });
6307
+ return agentLLM;
6324
6308
  }
6325
6309
 
6326
6310
  // src/commands/serve.ts
@@ -6884,8 +6868,8 @@ async function wordpressSetMeta(project, body) {
6884
6868
  }
6885
6869
  async function wordpressBulkSetMeta(project, opts) {
6886
6870
  const fs8 = await import("fs/promises");
6887
- const path6 = await import("path");
6888
- const filePath = path6.resolve(opts.from);
6871
+ const path7 = await import("path");
6872
+ const filePath = path7.resolve(opts.from);
6889
6873
  let raw;
6890
6874
  try {
6891
6875
  raw = await fs8.readFile(filePath, "utf8");
@@ -6986,9 +6970,9 @@ async function wordpressSetSchema(project, body) {
6986
6970
  }
6987
6971
  async function wordpressSchemaDeploy(project, opts) {
6988
6972
  const fs8 = await import("fs/promises");
6989
- const path6 = await import("path");
6973
+ const path7 = await import("path");
6990
6974
  const yaml = await import("yaml").catch(() => null);
6991
- const filePath = path6.resolve(opts.profile);
6975
+ const filePath = path7.resolve(opts.profile);
6992
6976
  let raw;
6993
6977
  try {
6994
6978
  raw = await fs8.readFile(filePath, "utf8");
@@ -7097,9 +7081,9 @@ async function wordpressOnboard(project, opts) {
7097
7081
  let profileData;
7098
7082
  if (opts.profile) {
7099
7083
  const fs8 = await import("fs/promises");
7100
- const path6 = await import("path");
7084
+ const path7 = await import("path");
7101
7085
  const yaml = await import("yaml").catch(() => null);
7102
- const filePath = path6.resolve(opts.profile);
7086
+ const filePath = path7.resolve(opts.profile);
7103
7087
  let raw;
7104
7088
  try {
7105
7089
  raw = await fs8.readFile(filePath, "utf8");
@@ -7683,6 +7667,391 @@ var WORDPRESS_CLI_COMMANDS = [
7683
7667
  }
7684
7668
  ];
7685
7669
 
7670
+ // src/commands/agent.ts
7671
+ import path6 from "path";
7672
+ function resolveStateDir(opts) {
7673
+ if (opts?.stateDir) return opts.stateDir;
7674
+ try {
7675
+ const config = loadConfig();
7676
+ const profile = config.agent?.profile ?? "aero";
7677
+ return getAeroStateDir(profile);
7678
+ } catch {
7679
+ return getAeroStateDir();
7680
+ }
7681
+ }
7682
+ function resolveConfig() {
7683
+ try {
7684
+ return loadConfig().agent ?? {};
7685
+ } catch {
7686
+ return {};
7687
+ }
7688
+ }
7689
+ async function agentStatus(opts) {
7690
+ const stateDir = resolveStateDir(opts);
7691
+ const config = resolveConfig();
7692
+ const mgr = new AgentManager(config, stateDir);
7693
+ const status = mgr.status();
7694
+ if (opts?.format === "json") {
7695
+ console.log(JSON.stringify(status, null, 2));
7696
+ return;
7697
+ }
7698
+ if (status.state === "running") {
7699
+ console.log(`Agent: running (PID ${status.pid}, port ${status.port})`);
7700
+ if (status.startedAt) {
7701
+ console.log(`Started: ${status.startedAt}`);
7702
+ }
7703
+ } else {
7704
+ console.log("Agent: stopped");
7705
+ }
7706
+ }
7707
+ async function agentStart(opts) {
7708
+ const stateDir = resolveStateDir(opts);
7709
+ const config = resolveConfig();
7710
+ const mgr = new AgentManager(config, stateDir);
7711
+ await mgr.start();
7712
+ const status = mgr.status();
7713
+ if (opts?.format === "json") {
7714
+ console.log(JSON.stringify(status, null, 2));
7715
+ } else {
7716
+ console.log(`Agent started (PID ${status.pid}, port ${status.port})`);
7717
+ }
7718
+ }
7719
+ async function agentStop(opts) {
7720
+ const stateDir = resolveStateDir(opts);
7721
+ const config = resolveConfig();
7722
+ const mgr = new AgentManager(config, stateDir);
7723
+ await mgr.stop();
7724
+ if (opts?.format === "json") {
7725
+ console.log(JSON.stringify({ state: "stopped" }, null, 2));
7726
+ } else {
7727
+ console.log("Agent stopped");
7728
+ }
7729
+ }
7730
+ async function agentReset(opts) {
7731
+ const stateDir = resolveStateDir(opts);
7732
+ const config = resolveConfig();
7733
+ const mgr = new AgentManager(config, stateDir);
7734
+ await mgr.reset();
7735
+ if (opts?.format === "json") {
7736
+ console.log(JSON.stringify({ state: "reset" }, null, 2));
7737
+ } else {
7738
+ console.log('Agent reset \u2014 workspace wiped. Run "canonry agent setup" to re-initialize.');
7739
+ }
7740
+ }
7741
+ async function agentSetup(opts) {
7742
+ const isJson = opts?.format === "json";
7743
+ let agentLLM;
7744
+ if (!configExists()) {
7745
+ const initOpts = {
7746
+ geminiKey: opts?.geminiKey,
7747
+ openaiKey: opts?.openaiKey,
7748
+ claudeKey: opts?.claudeKey,
7749
+ perplexityKey: opts?.perplexityKey,
7750
+ localUrl: opts?.localUrl,
7751
+ localModel: opts?.localModel,
7752
+ localKey: opts?.localKey,
7753
+ googleClientId: opts?.googleClientId,
7754
+ googleClientSecret: opts?.googleClientSecret,
7755
+ agentProvider: opts?.agentProvider,
7756
+ agentKey: opts?.agentKey,
7757
+ agentModel: opts?.agentModel
7758
+ };
7759
+ if (isJson) {
7760
+ agentLLM = await suppressStdout(() => initCommand(initOpts)) ?? void 0;
7761
+ } else {
7762
+ agentLLM = await initCommand(initOpts) ?? void 0;
7763
+ }
7764
+ }
7765
+ const existingConfig = resolveConfig();
7766
+ let detection = await detectOpenClaw(existingConfig);
7767
+ if (!detection.found) {
7768
+ detection = await autoInstallOrFail(opts?.format);
7769
+ }
7770
+ const profile = existingConfig.profile ?? "aero";
7771
+ const gatewayPort = opts?.gatewayPort ?? existingConfig.gatewayPort ?? 3579;
7772
+ const stateDir = opts?.stateDir ?? getAeroStateDir(profile);
7773
+ saveConfigPatch({
7774
+ agent: {
7775
+ binary: detection.path,
7776
+ profile,
7777
+ gatewayPort,
7778
+ autoStart: existingConfig.autoStart
7779
+ }
7780
+ });
7781
+ initializeOpenClawProfile(detection.path, profile, path6.join(stateDir, "workspace"));
7782
+ configureOpenClawGateway(detection.path, profile, gatewayPort);
7783
+ const creds = agentLLM ?? resolveAgentCredentials({
7784
+ agentProvider: opts?.agentProvider,
7785
+ agentKey: opts?.agentKey,
7786
+ agentModel: opts?.agentModel,
7787
+ stateDir
7788
+ });
7789
+ if (creds.key) {
7790
+ writeAgentEnv(stateDir, providerEnvVar(creds.provider), creds.key);
7791
+ if (opts?.format !== "json") {
7792
+ console.log(`Agent LLM: ${creds.provider} credentials configured`);
7793
+ }
7794
+ }
7795
+ if (creds.model) {
7796
+ setOpenClawModel(detection.path, profile, creds.model);
7797
+ if (opts?.format !== "json") {
7798
+ console.log(`Agent model: ${creds.model}`);
7799
+ }
7800
+ }
7801
+ seedWorkspace(stateDir);
7802
+ const attachSummary = await attachAgentWebhookToAllProjects(gatewayPort);
7803
+ if (opts?.format === "json") {
7804
+ console.log(JSON.stringify({
7805
+ state: "configured",
7806
+ binary: detection.path,
7807
+ version: detection.version,
7808
+ profile,
7809
+ gatewayPort,
7810
+ stateDir,
7811
+ attached: attachSummary
7812
+ }, null, 2));
7813
+ } else {
7814
+ console.log(`OpenClaw: ${detection.path} (${detection.version})`);
7815
+ console.log(`Profile: ${profile}`);
7816
+ console.log(`Gateway port: ${gatewayPort}`);
7817
+ console.log(`State dir: ${stateDir}`);
7818
+ if (attachSummary.attached > 0 || attachSummary.alreadyAttached > 0) {
7819
+ console.log(
7820
+ `Agent webhook: ${attachSummary.attached} attached, ${attachSummary.alreadyAttached} already present (via ${attachSummary.path})`
7821
+ );
7822
+ }
7823
+ console.log("Agent setup complete.");
7824
+ }
7825
+ }
7826
+ async function attachAgentWebhookToAllProjects(gatewayPort) {
7827
+ let config;
7828
+ try {
7829
+ config = loadConfig();
7830
+ } catch {
7831
+ return { path: "skipped", attached: 0, alreadyAttached: 0 };
7832
+ }
7833
+ try {
7834
+ const client = createApiClient();
7835
+ const projectList = await client.listProjects();
7836
+ const agentUrl = buildAgentWebhookUrl(gatewayPort);
7837
+ let attached2 = 0;
7838
+ let alreadyAttached2 = 0;
7839
+ for (const project of projectList) {
7840
+ const existing = await client.listNotifications(project.name);
7841
+ if (existing.some((n) => n.source === "agent")) {
7842
+ alreadyAttached2++;
7843
+ continue;
7844
+ }
7845
+ await client.createNotification(project.name, {
7846
+ channel: "webhook",
7847
+ url: agentUrl,
7848
+ events: [...AGENT_WEBHOOK_EVENTS],
7849
+ source: "agent"
7850
+ });
7851
+ attached2++;
7852
+ }
7853
+ return { path: "api", attached: attached2, alreadyAttached: alreadyAttached2 };
7854
+ } catch (err) {
7855
+ if (!isConnectionError(err)) throw err;
7856
+ }
7857
+ const db = createClient(config.database);
7858
+ migrate(db);
7859
+ const rows = db.select({ id: projects.id }).from(projects).all();
7860
+ let attached = 0;
7861
+ let alreadyAttached = 0;
7862
+ for (const row of rows) {
7863
+ const result = attachAgentWebhookDirect(db, row.id, gatewayPort);
7864
+ if (result === "attached") attached++;
7865
+ else alreadyAttached++;
7866
+ }
7867
+ return { path: "db", attached, alreadyAttached };
7868
+ }
7869
+ function isConnectionError(err) {
7870
+ if (!(err instanceof Error)) return false;
7871
+ const msg = err.message.toLowerCase();
7872
+ const code = err.code ?? "";
7873
+ return code === "CONNECTION_ERROR" || code === "ECONNREFUSED" || code === "ENOTFOUND" || msg.includes("could not connect") || msg.includes("econnrefused") || msg.includes("fetch failed") || msg.includes("connection refused");
7874
+ }
7875
+ async function agentAttach(opts) {
7876
+ const config = loadConfig();
7877
+ const gatewayPort = config.agent?.gatewayPort ?? 3579;
7878
+ const agentUrl = buildAgentWebhookUrl(gatewayPort);
7879
+ const client = createApiClient();
7880
+ const existing = await client.listNotifications(opts.project);
7881
+ const hasAgent = existing.some((n) => n.source === "agent");
7882
+ if (hasAgent) {
7883
+ if (opts.format === "json") {
7884
+ console.log(JSON.stringify({ status: "already-attached", project: opts.project }));
7885
+ } else {
7886
+ console.log(`Agent webhook already attached to "${opts.project}"`);
7887
+ }
7888
+ return;
7889
+ }
7890
+ const result = await client.createNotification(opts.project, {
7891
+ channel: "webhook",
7892
+ url: agentUrl,
7893
+ events: [...AGENT_WEBHOOK_EVENTS],
7894
+ source: "agent"
7895
+ });
7896
+ if (opts.format === "json") {
7897
+ console.log(JSON.stringify({ status: "attached", project: opts.project, notificationId: result.id }));
7898
+ } else {
7899
+ console.log(`Agent webhook attached to "${opts.project}"`);
7900
+ }
7901
+ }
7902
+ async function agentDetach(opts) {
7903
+ const client = createApiClient();
7904
+ const existing = await client.listNotifications(opts.project);
7905
+ const agentNotif = existing.find((n) => n.source === "agent");
7906
+ if (!agentNotif) {
7907
+ if (opts.format === "json") {
7908
+ console.log(JSON.stringify({ status: "not-attached", project: opts.project }));
7909
+ } else {
7910
+ console.log(`No agent webhook found on "${opts.project}"`);
7911
+ }
7912
+ return;
7913
+ }
7914
+ await client.deleteNotification(opts.project, agentNotif.id);
7915
+ if (opts.format === "json") {
7916
+ console.log(JSON.stringify({ status: "detached", project: opts.project }));
7917
+ } else {
7918
+ console.log(`Agent webhook detached from "${opts.project}"`);
7919
+ }
7920
+ }
7921
+ async function autoInstallOrFail(format) {
7922
+ if (format !== "json") {
7923
+ console.log("OpenClaw not found, installing via npm...");
7924
+ }
7925
+ const install = await installOpenClaw({ silent: format === "json" });
7926
+ if (!install.success) {
7927
+ const msg = `Failed to install OpenClaw: ${install.error}`;
7928
+ if (format === "json") {
7929
+ console.error(JSON.stringify({ error: { code: "AGENT_INSTALL_FAILED", message: msg } }));
7930
+ } else {
7931
+ console.error(msg);
7932
+ }
7933
+ process.exitCode = 1;
7934
+ throw new Error(msg);
7935
+ }
7936
+ if (format !== "json") {
7937
+ console.log(`Installed OpenClaw ${install.detection.version}`);
7938
+ }
7939
+ return install.detection;
7940
+ }
7941
+ async function suppressStdout(fn) {
7942
+ const original = console.log;
7943
+ console.log = () => {
7944
+ };
7945
+ try {
7946
+ return await fn();
7947
+ } finally {
7948
+ console.log = original;
7949
+ }
7950
+ }
7951
+
7952
+ // src/cli-commands/agent.ts
7953
+ var AGENT_CLI_COMMANDS = [
7954
+ {
7955
+ path: ["agent", "status"],
7956
+ usage: "canonry agent status [--format json]",
7957
+ options: {},
7958
+ run: async (input) => {
7959
+ await agentStatus({ format: input.format });
7960
+ }
7961
+ },
7962
+ {
7963
+ path: ["agent", "start"],
7964
+ usage: "canonry agent start [--format json]",
7965
+ options: {},
7966
+ run: async (input) => {
7967
+ await agentStart({ format: input.format });
7968
+ }
7969
+ },
7970
+ {
7971
+ path: ["agent", "stop"],
7972
+ usage: "canonry agent stop [--format json]",
7973
+ options: {},
7974
+ run: async (input) => {
7975
+ await agentStop({ format: input.format });
7976
+ }
7977
+ },
7978
+ {
7979
+ path: ["agent", "reset"],
7980
+ usage: "canonry agent reset [--format json]",
7981
+ options: {},
7982
+ run: async (input) => {
7983
+ await agentReset({ format: input.format });
7984
+ }
7985
+ },
7986
+ {
7987
+ path: ["agent", "attach"],
7988
+ usage: "canonry agent attach <project> [--format json]",
7989
+ options: {},
7990
+ run: async (input) => {
7991
+ const project = input.positionals[0];
7992
+ if (!project) {
7993
+ console.error("Usage: canonry agent attach <project>");
7994
+ process.exitCode = 1;
7995
+ return;
7996
+ }
7997
+ await agentAttach({ project, format: input.format });
7998
+ }
7999
+ },
8000
+ {
8001
+ path: ["agent", "detach"],
8002
+ usage: "canonry agent detach <project> [--format json]",
8003
+ options: {},
8004
+ run: async (input) => {
8005
+ const project = input.positionals[0];
8006
+ if (!project) {
8007
+ console.error("Usage: canonry agent detach <project>");
8008
+ process.exitCode = 1;
8009
+ return;
8010
+ }
8011
+ await agentDetach({ project, format: input.format });
8012
+ }
8013
+ },
8014
+ {
8015
+ path: ["agent", "setup"],
8016
+ 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]",
8017
+ options: {
8018
+ "agent-provider": stringOption(),
8019
+ "agent-key": stringOption(),
8020
+ "agent-model": stringOption(),
8021
+ "gateway-port": { type: "string" },
8022
+ "gemini-key": stringOption(),
8023
+ "openai-key": stringOption(),
8024
+ "claude-key": stringOption(),
8025
+ "perplexity-key": stringOption(),
8026
+ "local-url": stringOption(),
8027
+ "local-model": stringOption(),
8028
+ "local-key": stringOption(),
8029
+ "google-client-id": stringOption(),
8030
+ "google-client-secret": stringOption()
8031
+ },
8032
+ run: async (input) => {
8033
+ const portStr = input.values["gateway-port"];
8034
+ const gatewayPort = typeof portStr === "string" ? Number.parseInt(portStr, 10) : void 0;
8035
+ await agentSetup({
8036
+ agentProvider: getString(input.values, "agent-provider"),
8037
+ agentKey: getString(input.values, "agent-key"),
8038
+ agentModel: getString(input.values, "agent-model"),
8039
+ gatewayPort,
8040
+ format: input.format,
8041
+ geminiKey: getString(input.values, "gemini-key"),
8042
+ openaiKey: getString(input.values, "openai-key"),
8043
+ claudeKey: getString(input.values, "claude-key"),
8044
+ perplexityKey: getString(input.values, "perplexity-key"),
8045
+ localUrl: getString(input.values, "local-url"),
8046
+ localModel: getString(input.values, "local-model"),
8047
+ localKey: getString(input.values, "local-key"),
8048
+ googleClientId: getString(input.values, "google-client-id"),
8049
+ googleClientSecret: getString(input.values, "google-client-secret")
8050
+ });
8051
+ }
8052
+ }
8053
+ ];
8054
+
7686
8055
  // src/cli-commands.ts
7687
8056
  var REGISTERED_CLI_COMMANDS = [
7688
8057
  ...BACKFILL_CLI_COMMANDS,
@@ -7701,7 +8070,8 @@ var REGISTERED_CLI_COMMANDS = [
7701
8070
  ...WORDPRESS_CLI_COMMANDS,
7702
8071
  ...CDP_CLI_COMMANDS,
7703
8072
  ...GA_CLI_COMMANDS,
7704
- ...INTELLIGENCE_CLI_COMMANDS
8073
+ ...INTELLIGENCE_CLI_COMMANDS,
8074
+ ...AGENT_CLI_COMMANDS
7705
8075
  ];
7706
8076
 
7707
8077
  // src/cli.ts
package/dist/index.d.ts CHANGED
@@ -73,6 +73,16 @@ interface WordpressConnectionConfigEntry {
73
73
  interface WordpressConfigEntry {
74
74
  connections?: WordpressConnectionConfigEntry[];
75
75
  }
76
+ interface AgentConfigEntry {
77
+ /** Path to openclaw binary (auto-detected, persisted after bootstrap) */
78
+ binary?: string;
79
+ /** OpenClaw profile name (default: 'aero') */
80
+ profile?: string;
81
+ /** Start gateway automatically with `canonry serve` */
82
+ autoStart?: boolean;
83
+ /** Gateway port (persisted so restarts reuse same port) */
84
+ gatewayPort?: number;
85
+ }
76
86
  interface CanonryConfig {
77
87
  apiUrl: string;
78
88
  publicUrl?: string;
@@ -93,6 +103,7 @@ interface CanonryConfig {
93
103
  dashboardPasswordHash?: string;
94
104
  telemetry?: boolean;
95
105
  anonymousId?: string;
106
+ agent?: AgentConfigEntry;
96
107
  }
97
108
  declare function loadConfig(): CanonryConfig;
98
109
 
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createServer,
3
3
  loadConfig
4
- } from "./chunk-22RIKNII.js";
4
+ } from "./chunk-25QLMK4F.js";
5
5
  import "./chunk-HO22LHTY.js";
6
6
  export {
7
7
  createServer,