@agentvault/secure-channel 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,28 +2,21 @@
2
2
 
3
3
  End-to-end encrypted communication channel for AI agents on the [AgentVault](https://agentvault.chat) platform. Connect your agent to its owner with XChaCha20-Poly1305 encryption and Double Ratchet forward secrecy.
4
4
 
5
- ## What's New in v0.4.0
5
+ ## What's New in v0.5.0
6
6
 
7
- **Webhook Notifications** — Your agent can now receive HTTP webhook callbacks when a new message arrives, even when it's not connected via WebSocket. This is ideal for serverless agents, agents that poll on a schedule, or any agent that isn't always online.
7
+ **Desktop Notifications** — Incoming messages now trigger native OS notifications (macOS Notification Center, Windows Toast, Linux notify-send) when using the CLI. Enabled by default disable with `--no-notifications`.
8
8
 
9
- ### Upgrading from v0.3.x
9
+ **Webhook URL CLI Flag** (v0.4.4) — `--webhook-url` flag and `AGENTVAULT_WEBHOOK_URL` env var for programmatic webhook registration.
10
10
 
11
- ```bash
12
- npm install @agentvault/secure-channel@latest
13
- ```
11
+ **Webhook Notifications** (v0.4.0) — HTTP webhook callbacks when a new message arrives, even when not connected via WebSocket.
14
12
 
15
- Add `webhookUrl` to your config to enable:
13
+ ### Upgrading
16
14
 
17
- ```js
18
- const channel = new SecureChannel({
19
- inviteToken: "your-token",
20
- dataDir: "./agentvault-data",
21
- apiUrl: "https://api.agentvault.chat",
22
- webhookUrl: "https://your-server.com/webhook/agentvault", // NEW in 0.4.0
23
- });
15
+ ```bash
16
+ npm install @agentvault/secure-channel@latest
24
17
  ```
25
18
 
26
- No other code changes required — fully backward-compatible.
19
+ No code changes required — fully backward-compatible.
27
20
 
28
21
  ---
29
22
 
@@ -58,8 +51,10 @@ The CLI will:
58
51
  | `--name` | `"CLI Agent"` | Agent display name |
59
52
  | `--data-dir` | `./agentvault-data` | Directory for persistent state |
60
53
  | `--api-url` | `https://api.agentvault.chat` | API endpoint |
54
+ | `--webhook-url` | (none) | URL for HTTP webhook notifications |
55
+ | `--no-notifications` | (notifications on) | Disable OS desktop notifications |
61
56
 
62
- Environment variables (`AGENTVAULT_INVITE_TOKEN`, `AGENTVAULT_AGENT_NAME`, `AGENTVAULT_DATA_DIR`, `AGENTVAULT_API_URL`) work as alternatives to flags.
57
+ Environment variables (`AGENTVAULT_INVITE_TOKEN`, `AGENTVAULT_AGENT_NAME`, `AGENTVAULT_DATA_DIR`, `AGENTVAULT_API_URL`, `AGENTVAULT_WEBHOOK_URL`, `AGENTVAULT_NO_NOTIFICATIONS`) work as alternatives to flags.
63
58
 
64
59
  ### Option 2: SDK (Programmatic)
65
60
 
@@ -216,6 +211,32 @@ AgentVault supports multiple owner devices (e.g., desktop + mobile). The channel
216
211
 
217
212
  No additional configuration needed — multi-device is handled transparently.
218
213
 
214
+ ## Running as a Service
215
+
216
+ The agent process must stay running to receive messages and desktop notifications. Use [pm2](https://pm2.keymetrics.io/) to keep it alive:
217
+
218
+ ```bash
219
+ # Install pm2 globally
220
+ npm install -g pm2
221
+
222
+ # Start your agent as a background service
223
+ pm2 start npx --name "my-agent" -- @agentvault/secure-channel \
224
+ --token=YOUR_TOKEN --name="My Agent"
225
+
226
+ # View logs
227
+ pm2 logs my-agent
228
+
229
+ # Auto-restart on crash
230
+ pm2 save
231
+
232
+ # Stop the agent
233
+ pm2 stop my-agent
234
+ ```
235
+
236
+ pm2 keeps the process running if the terminal closes and auto-restarts on crashes. Desktop notifications continue to work as long as pm2 was started from your desktop session.
237
+
238
+ For headless servers (no desktop), use `--no-notifications` and configure `--webhook-url` instead.
239
+
219
240
  ## Security
220
241
 
221
242
  - **XChaCha20-Poly1305** symmetric encryption (192-bit nonces)
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EAKb,MAAM,YAAY,CAAC;AAiDpB,qBAAa,aAAc,SAAQ,YAAY;IAiBjC,OAAO,CAAC,MAAM;IAhB1B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,sBAAsB,CAAc;IAC5C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,SAAS,CAGH;IACd,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAA+B;gBAE7B,MAAM,EAAE,mBAAmB;IAI/C,IAAI,KAAK,IAAI,YAAY,CAExB;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,iEAAiE;IACjE,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAElC;IAED,2CAA2C;IAC3C,IAAI,eAAe,IAAI,MAAM,EAAE,CAE9B;IAED,6CAA6C;IAC7C,IAAI,YAAY,IAAI,MAAM,CAEzB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAuBtB;;;OAGG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyCtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAsC1F;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;YAiCtE,OAAO;IAgDrB,OAAO,CAAC,KAAK;YAgCC,SAAS;IAyIvB,OAAO,CAAC,QAAQ;IAsEhB;;;;OAIG;YACW,sBAAsB;IAwFpC;;;OAGG;YACW,oBAAoB;IAqClC;;;OAGG;YACW,uBAAuB;IAkCrC;;;;OAIG;YACW,mBAAmB;IAkEjC;;;OAGG;YACW,mBAAmB;IA4FjC,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,YAAY;IAKpB;;;OAGG;YACW,aAAa;CAc5B"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EAKb,MAAM,YAAY,CAAC;AAiDpB,qBAAa,aAAc,SAAQ,YAAY;IAiBjC,OAAO,CAAC,MAAM;IAhB1B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,sBAAsB,CAAc;IAC5C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,SAAS,CAGH;IACd,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAA+B;gBAE7B,MAAM,EAAE,mBAAmB;IAI/C,IAAI,KAAK,IAAI,YAAY,CAExB;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,iEAAiE;IACjE,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAElC;IAED,2CAA2C;IAC3C,IAAI,eAAe,IAAI,MAAM,EAAE,CAE9B;IAED,6CAA6C;IAC7C,IAAI,YAAY,IAAI,MAAM,CAEzB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAuBtB;;;OAGG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyCtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAsC1F;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;YAiCtE,OAAO;IAgDrB,OAAO,CAAC,KAAK;YAsCC,SAAS;IAyIvB,OAAO,CAAC,QAAQ;IAsEhB;;;;OAIG;YACW,sBAAsB;IAwFpC;;;OAGG;YACW,oBAAoB;IAqClC;;;OAGG;YACW,uBAAuB;IAkCrC;;;;OAIG;YACW,mBAAmB;IAkEjC;;;OAGG;YACW,mBAAmB;IA4FjC,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,YAAY;IAKpB;;;OAGG;YACW,aAAa;CAc5B"}
package/dist/cli.js CHANGED
@@ -89,7 +89,7 @@ var randomValuesStandard;
89
89
  var crypto;
90
90
  var randomValueNodeJS;
91
91
  var _Module = Module;
92
- Module.ready = new Promise(function(resolve2, reject) {
92
+ Module.ready = new Promise(function(resolve3, reject) {
93
93
  var Module2 = _Module;
94
94
  Module2.onAbort = reject;
95
95
  Module2.print = function(what) {
@@ -101,13 +101,13 @@ Module.ready = new Promise(function(resolve2, reject) {
101
101
  Module2.onRuntimeInitialized = function() {
102
102
  try {
103
103
  Module2._crypto_secretbox_keybytes();
104
- resolve2();
104
+ resolve3();
105
105
  } catch (err2) {
106
106
  reject(err2);
107
107
  }
108
108
  };
109
109
  Module2.useBackupModule = function() {
110
- return new Promise(function(resolve3, reject2) {
110
+ return new Promise(function(resolve4, reject2) {
111
111
  var Module3 = {};
112
112
  Module3.onAbort = reject2;
113
113
  Module3.getRandomValue = _Module.getRandomValue;
@@ -120,7 +120,7 @@ Module.ready = new Promise(function(resolve2, reject) {
120
120
  Object.keys(Module3).forEach(function(k2) {
121
121
  _Module[k2] = Module3[k2];
122
122
  });
123
- resolve3();
123
+ resolve4();
124
124
  };
125
125
  var Module3 = typeof Module3 != "undefined" ? Module3 : {};
126
126
  var ENVIRONMENT_IS_WEB2 = !!globalThis.window;
@@ -180,13 +180,13 @@ Module.ready = new Promise(function(resolve2, reject) {
180
180
  }
181
181
  readAsync2 = async (url) => {
182
182
  if (isFileURI2(url)) {
183
- return new Promise((resolve4, reject3) => {
183
+ return new Promise((resolve5, reject3) => {
184
184
  var xhr = new XMLHttpRequest();
185
185
  xhr.open("GET", url, true);
186
186
  xhr.responseType = "arraybuffer";
187
187
  xhr.onload = () => {
188
188
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
189
- resolve4(xhr.response);
189
+ resolve5(xhr.response);
190
190
  return;
191
191
  }
192
192
  reject3(xhr.status);
@@ -40099,9 +40099,9 @@ Module.ready = new Promise(function(resolve2, reject) {
40099
40099
  }
40100
40100
  var info = getWasmImports2();
40101
40101
  if (Module3["instantiateWasm"]) {
40102
- return new Promise((resolve4, reject3) => {
40102
+ return new Promise((resolve5, reject3) => {
40103
40103
  Module3["instantiateWasm"](info, (inst, mod) => {
40104
- resolve4(receiveInstance(inst, mod));
40104
+ resolve5(receiveInstance(inst, mod));
40105
40105
  });
40106
40106
  });
40107
40107
  }
@@ -41004,13 +41004,13 @@ Module.ready = new Promise(function(resolve2, reject) {
41004
41004
  }
41005
41005
  readAsync = async (url) => {
41006
41006
  if (isFileURI(url)) {
41007
- return new Promise((resolve3, reject2) => {
41007
+ return new Promise((resolve4, reject2) => {
41008
41008
  var xhr = new XMLHttpRequest();
41009
41009
  xhr.open("GET", url, true);
41010
41010
  xhr.responseType = "arraybuffer";
41011
41011
  xhr.onload = () => {
41012
41012
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
41013
- resolve3(xhr.response);
41013
+ resolve4(xhr.response);
41014
41014
  return;
41015
41015
  }
41016
41016
  reject2(xhr.status);
@@ -41124,9 +41124,9 @@ Module.ready = new Promise(function(resolve2, reject) {
41124
41124
  }
41125
41125
  var info = getWasmImports();
41126
41126
  if (Module2["instantiateWasm"]) {
41127
- return new Promise((resolve3, reject2) => {
41127
+ return new Promise((resolve4, reject2) => {
41128
41128
  Module2["instantiateWasm"](info, (inst, mod) => {
41129
- resolve3(receiveInstance(inst, mod));
41129
+ resolve4(receiveInstance(inst, mod));
41130
41130
  });
41131
41131
  });
41132
41132
  }
@@ -45013,6 +45013,9 @@ async function enrollDevice(apiUrl2, inviteToken, identityPkHex, ephemeralPkHex,
45013
45013
  }
45014
45014
  async function pollDeviceStatus(apiUrl2, deviceId) {
45015
45015
  const res = await fetch(`${apiUrl2}/api/v1/devices/${deviceId}/status`);
45016
+ if (res.status === 429) {
45017
+ return { device_id: deviceId, status: "PENDING", fingerprint: "", rateLimited: true };
45018
+ }
45016
45019
  if (!res.ok) {
45017
45020
  const detail = await res.text();
45018
45021
  throw new Error(`Status poll failed (${res.status}): ${detail}`);
@@ -45033,7 +45036,7 @@ async function activateDevice(apiUrl2, deviceId) {
45033
45036
  }
45034
45037
 
45035
45038
  // src/channel.ts
45036
- var POLL_INTERVAL_MS = 3e3;
45039
+ var POLL_INTERVAL_MS = 6e3;
45037
45040
  var RECONNECT_BASE_MS = 1e3;
45038
45041
  var RECONNECT_MAX_MS = 3e4;
45039
45042
  function migratePersistedState(raw) {
@@ -45318,6 +45321,10 @@ var SecureChannel = class extends EventEmitter {
45318
45321
  this.config.apiUrl,
45319
45322
  this._deviceId
45320
45323
  );
45324
+ if (status.rateLimited) {
45325
+ this._pollTimer = setTimeout(doPoll, POLL_INTERVAL_MS * 2);
45326
+ return;
45327
+ }
45321
45328
  if (status.status === "APPROVED") {
45322
45329
  await this._activate();
45323
45330
  return;
@@ -45768,10 +45775,137 @@ var SecureChannel = class extends EventEmitter {
45768
45775
  };
45769
45776
 
45770
45777
  // src/cli.ts
45771
- import { resolve } from "node:path";
45778
+ import { resolve as resolve2 } from "node:path";
45772
45779
  import { createInterface } from "node:readline";
45773
- import notifier from "node-notifier";
45780
+
45781
+ // src/setup.ts
45782
+ import { execSync } from "node:child_process";
45783
+ import { resolve } from "node:path";
45784
+ async function runSetupCommand(options) {
45785
+ const { token: token2, name: name2, apiUrl: apiUrl2 } = options;
45786
+ const dataDir2 = resolve(options.dataDir.replace(/^~/, process.env.HOME ?? "~"));
45787
+ console.log(`
45788
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
45789
+ \u2551 AgentVault \u2014 First-Time Setup \u2551
45790
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
45791
+
45792
+ Agent name : ${name2}
45793
+ Data dir : ${dataDir2}
45794
+ API : ${apiUrl2}
45795
+ `);
45796
+ let enrollDone = false;
45797
+ const channel2 = new SecureChannel({
45798
+ inviteToken: token2,
45799
+ dataDir: dataDir2,
45800
+ apiUrl: apiUrl2,
45801
+ agentName: name2,
45802
+ onMessage: () => {
45803
+ },
45804
+ // Not handling messages during setup
45805
+ onStateChange: (state) => {
45806
+ switch (state) {
45807
+ case "enrolling":
45808
+ console.log(" Enrolling with AgentVault server...");
45809
+ break;
45810
+ case "polling":
45811
+ console.log(" Waiting for approval in your AgentVault dashboard...");
45812
+ if (channel2.fingerprint) {
45813
+ console.log(`
45814
+ Fingerprint: ${channel2.fingerprint}`);
45815
+ console.log(" Verify this fingerprint matches the one shown in your dashboard");
45816
+ console.log(" before clicking Approve.\n");
45817
+ }
45818
+ break;
45819
+ case "activating":
45820
+ console.log(" Approved! Setting up encryption...");
45821
+ break;
45822
+ case "ready":
45823
+ enrollDone = true;
45824
+ console.log(" \u2705 Enrollment complete! Secure channel established.\n");
45825
+ break;
45826
+ case "error":
45827
+ console.error(" \u274C Setup encountered an error.");
45828
+ break;
45829
+ }
45830
+ }
45831
+ });
45832
+ channel2.on("error", (err) => {
45833
+ console.error(`
45834
+ \u274C Setup failed: ${err.message}
45835
+ `);
45836
+ process.exit(1);
45837
+ });
45838
+ await new Promise((res, rej) => {
45839
+ const check = setInterval(() => {
45840
+ if (enrollDone) {
45841
+ clearInterval(check);
45842
+ res();
45843
+ }
45844
+ }, 500);
45845
+ channel2.start().catch((err) => {
45846
+ clearInterval(check);
45847
+ rej(err);
45848
+ });
45849
+ });
45850
+ await channel2.stop();
45851
+ console.log(" Registering AgentVault channel in OpenClaw config...\n");
45852
+ const patchCommands = [
45853
+ `openclaw config set channels.agentvault.dataDir "${dataDir2}"`,
45854
+ `openclaw config set channels.agentvault.apiUrl "${apiUrl2}"`,
45855
+ `openclaw config set channels.agentvault.agentName "${name2}"`
45856
+ ];
45857
+ let configPatched = false;
45858
+ for (const cmd of patchCommands) {
45859
+ try {
45860
+ execSync(cmd, { stdio: "pipe" });
45861
+ configPatched = true;
45862
+ } catch {
45863
+ configPatched = false;
45864
+ break;
45865
+ }
45866
+ }
45867
+ if (configPatched) {
45868
+ console.log(` \u2705 Channel registered in OpenClaw config.
45869
+
45870
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
45871
+ Restart OpenClaw to activate the secure channel:
45872
+
45873
+ openclaw gateway restart
45874
+
45875
+ After restart, your AgentVault UI will route
45876
+ encrypted messages directly into your agent.
45877
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
45878
+ `);
45879
+ } else {
45880
+ console.log(` \u26A0\uFE0F Could not auto-configure OpenClaw (is 'openclaw' in your PATH?).
45881
+ Add this to your OpenClaw config (openclaw.yaml or openclaw.json) manually:
45882
+
45883
+ channels:
45884
+ agentvault:
45885
+ dataDir: "${dataDir2}"
45886
+ apiUrl: "${apiUrl2}"
45887
+ agentName: "${name2}"
45888
+
45889
+ Then restart OpenClaw:
45890
+ openclaw gateway restart
45891
+ `);
45892
+ }
45893
+ }
45894
+
45895
+ // src/cli.ts
45896
+ var _notifier = null;
45897
+ async function tryNotify(title, message) {
45898
+ try {
45899
+ if (!_notifier) {
45900
+ const mod = await import("node-notifier");
45901
+ _notifier = mod.default ?? mod;
45902
+ }
45903
+ _notifier.notify({ title, message, sound: true });
45904
+ } catch {
45905
+ }
45906
+ }
45774
45907
  var args = process.argv.slice(2);
45908
+ var subcommand = args[0]?.startsWith("--") ? null : args[0] ?? null;
45775
45909
  var flags = {};
45776
45910
  for (const arg of args) {
45777
45911
  const kvMatch = arg.match(/^--(\w[\w-]*)=(.+)$/);
@@ -45787,12 +45921,45 @@ var dataDir = flags["data-dir"] || process.env.AGENTVAULT_DATA_DIR || "./agentva
45787
45921
  var apiUrl = flags["api-url"] || process.env.AGENTVAULT_API_URL || "https://api.agentvault.chat";
45788
45922
  var webhookUrl = flags["webhook-url"] || process.env.AGENTVAULT_WEBHOOK_URL;
45789
45923
  var noNotifications = flags["no-notifications"] === "true" || process.env.AGENTVAULT_NO_NOTIFICATIONS === "1";
45924
+ if (subcommand === "setup") {
45925
+ if (!token) {
45926
+ console.error(`
45927
+ AgentVault Setup
45928
+
45929
+ Usage:
45930
+ npx @agentvault/secure-channel setup --token=YOUR_INVITE_TOKEN
45931
+
45932
+ Options:
45933
+ --token=TOKEN Invite token from the AgentVault dashboard (required)
45934
+ --name=NAME Agent display name (default: "CLI Agent")
45935
+ --data-dir=PATH Directory for persistent state (default: ~/.openclaw/agentvault)
45936
+ --api-url=URL API endpoint (default: https://api.agentvault.chat)
45937
+
45938
+ This command:
45939
+ 1. Enrolls this machine as your agent with AgentVault
45940
+ 2. Waits for your approval in the AgentVault dashboard
45941
+ 3. Saves the encrypted session to --data-dir
45942
+ 4. Registers the agentvault channel in your OpenClaw config
45943
+
45944
+ After setup, restart OpenClaw \u2014 messages will flow automatically.
45945
+ `);
45946
+ process.exit(1);
45947
+ }
45948
+ await runSetupCommand({
45949
+ token,
45950
+ name,
45951
+ apiUrl,
45952
+ dataDir: flags["data-dir"] || process.env.AGENTVAULT_DATA_DIR || "~/.openclaw/agentvault"
45953
+ });
45954
+ process.exit(0);
45955
+ }
45790
45956
  if (!token) {
45791
45957
  console.error(`
45792
45958
  AgentVault Secure Channel CLI
45793
45959
 
45794
45960
  Usage:
45795
- npx @agentvault/secure-channel --token=YOUR_INVITE_TOKEN
45961
+ npx @agentvault/secure-channel setup --token=TOKEN # First-time OpenClaw setup
45962
+ npx @agentvault/secure-channel --token=TOKEN # Standalone interactive CLI
45796
45963
 
45797
45964
  Options:
45798
45965
  --token=TOKEN Invite token from the AgentVault dashboard (required on first run)
@@ -45800,7 +45967,7 @@ Options:
45800
45967
  --data-dir=PATH Directory for persistent state (default: ./agentvault-data)
45801
45968
  --api-url=URL API endpoint (default: https://api.agentvault.chat)
45802
45969
  --webhook-url=URL URL for HTTP webhook notifications on new messages
45803
- --no-notifications Disable OS desktop notifications (enabled by default)
45970
+ --no-notifications Disable OS desktop notifications
45804
45971
 
45805
45972
  Environment variables:
45806
45973
  AGENTVAULT_INVITE_TOKEN Same as --token
@@ -45810,9 +45977,12 @@ Environment variables:
45810
45977
  AGENTVAULT_WEBHOOK_URL Same as --webhook-url
45811
45978
  AGENTVAULT_NO_NOTIFICATIONS Set to "1" to disable desktop notifications
45812
45979
 
45813
- Example:
45980
+ Examples:
45981
+ # One-time OpenClaw setup (recommended):
45982
+ npx @agentvault/secure-channel setup --token=av_tok_abc123
45983
+
45984
+ # Standalone interactive CLI:
45814
45985
  npx @agentvault/secure-channel --token=av_tok_abc123 --name="My Agent"
45815
- npx @agentvault/secure-channel --token=av_tok_abc123 --webhook-url=https://myapp.com/webhook
45816
45986
  `);
45817
45987
  process.exit(1);
45818
45988
  }
@@ -45828,7 +45998,7 @@ var stateMessages = {
45828
45998
  };
45829
45999
  var channel = new SecureChannel({
45830
46000
  inviteToken: token,
45831
- dataDir: resolve(dataDir),
46001
+ dataDir: resolve2(dataDir),
45832
46002
  apiUrl,
45833
46003
  agentName: name,
45834
46004
  webhookUrl,
@@ -45839,11 +46009,7 @@ var channel = new SecureChannel({
45839
46009
  process.stdout.write("\n> ");
45840
46010
  if (!noNotifications) {
45841
46011
  const body = plaintext.length > 100 ? plaintext.slice(0, 100) + "..." : plaintext;
45842
- notifier.notify({
45843
- title: "AgentVault \u2014 New Message",
45844
- message: body,
45845
- sound: true
45846
- });
46012
+ void tryNotify("AgentVault \u2014 New Message", body);
45847
46013
  }
45848
46014
  },
45849
46015
  onStateChange: (state) => {