agentapprove 0.1.1 → 0.1.3

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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +143 -21
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -48,6 +48,7 @@ agentapprove --no-e2e # Skip end-to-end encryption setup
48
48
  - **Gemini CLI** - Google's CLI
49
49
  - **VS Code Agent** - VS Code agent hooks
50
50
  - **GitHub Copilot CLI** - GitHub's coding agent
51
+ - **OpenCode** - Open-source AI coding agent
51
52
  - **OpenClaw** - Open-source AI agent platform
52
53
  - **OpenAI Codex** - coming soon
53
54
  - And more
@@ -58,7 +59,7 @@ agentapprove --no-e2e # Skip end-to-end encryption setup
58
59
  - **Push notifications** - Never miss an approval request. Avoid wasted idle time with your agents
59
60
  - **Apple Watch** - One-tap approvals from your wrist. Review context and respond without reaching for your phone
60
61
  - **Voice commands** - Send follow-up commands to your agents using the microphone when they finish a task
61
- - **Agent observability** - Full visibility into what all your agents are doing, especially autonomous agents like OpenClaw or long-running loops
62
+ - **Agent observability** - Full visibility into what all your agents are doing, especially autonomous agents like OpenClaw or long-running loops (Ralph loops)
62
63
  - **Centralized policies** - Set auto-approve and deny rules across all your agents from a single policy
63
64
  - **End-to-end encryption** - You own the keys. Approval requests are encrypted on your machine and decrypted only on your device
64
65
  - **Privacy tiers** - Choose minimal, summary, or full data logging. Control retention from 1 to 365 days
package/dist/cli.js CHANGED
@@ -2318,10 +2318,19 @@ import { homedir, hostname, platform } from "os";
2318
2318
  import { join, dirname, basename } from "path";
2319
2319
  import { execSync, spawnSync } from "child_process";
2320
2320
  import { randomBytes, createHash } from "crypto";
2321
- var VERSION = "0.1.1";
2321
+ var VERSION = "0.1.3";
2322
2322
  function getApiUrl() {
2323
2323
  return process.env.AGENTAPPROVE_API || "https://api.agentapprove.com";
2324
2324
  }
2325
+ function getXdgConfigHome() {
2326
+ return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
2327
+ }
2328
+ function getOpenCodeConfigDir() {
2329
+ return join(getXdgConfigHome(), "opencode");
2330
+ }
2331
+ function getOpenCodeConfigPath() {
2332
+ return join(getOpenCodeConfigDir(), "opencode.json");
2333
+ }
2325
2334
  var API_URL = getApiUrl();
2326
2335
  var API_VERSION = process.env.AGENTAPPROVE_API_VERSION || "v001";
2327
2336
  function hasFlag2(flag) {
@@ -2446,6 +2455,14 @@ var AGENTS = {
2446
2455
  hooks: [
2447
2456
  { name: "agentapprove", file: "@agentapprove/openclaw", description: "Tool approval + event monitoring", isApprovalHook: true, isPlugin: true }
2448
2457
  ]
2458
+ },
2459
+ opencode: {
2460
+ name: "OpenCode",
2461
+ configPath: getOpenCodeConfigPath(),
2462
+ hooksKey: "plugin",
2463
+ hooks: [
2464
+ { name: "agentapprove", file: "@agentapprove/opencode", description: "Tool approval + event monitoring", isApprovalHook: true, isPlugin: true }
2465
+ ]
2449
2466
  }
2450
2467
  };
2451
2468
  function findGitBash() {
@@ -2619,6 +2636,10 @@ function detectInstalledAgents() {
2619
2636
  if (existsSync(join(homedir(), ".openclaw"))) {
2620
2637
  installed.push(id);
2621
2638
  }
2639
+ } else if (id === "opencode") {
2640
+ if (existsSync(getOpenCodeConfigDir())) {
2641
+ installed.push(id);
2642
+ }
2622
2643
  } else {
2623
2644
  const configDir = dirname(agent.configPath);
2624
2645
  if (existsSync(configDir)) {
@@ -3055,6 +3076,39 @@ async function installHooksForAgent(agentId, hooksDir, mode = "approval") {
3055
3076
  installedHooks.push("agentapprove");
3056
3077
  return { success: true, backupPath, hooks: installedHooks };
3057
3078
  }
3079
+ if (agentId === "opencode") {
3080
+ if (!Array.isArray(config.plugin)) {
3081
+ config.plugin = [];
3082
+ }
3083
+ const pluginArray = config.plugin;
3084
+ if (!pluginArray.includes("@agentapprove/opencode")) {
3085
+ pluginArray.push("@agentapprove/opencode");
3086
+ }
3087
+ writeJsonConfig(agent.configPath, config);
3088
+ const opencodePkgDir = join(getOpenCodeConfigDir(), ".opencode");
3089
+ const opencodePkgPath = join(opencodePkgDir, "package.json");
3090
+ try {
3091
+ if (!existsSync(opencodePkgDir)) {
3092
+ mkdirSync(opencodePkgDir, { recursive: true });
3093
+ }
3094
+ let pkgJson = {};
3095
+ if (existsSync(opencodePkgPath)) {
3096
+ pkgJson = JSON.parse(readFileSync(opencodePkgPath, "utf-8"));
3097
+ }
3098
+ if (!pkgJson.dependencies) {
3099
+ pkgJson.dependencies = {};
3100
+ }
3101
+ pkgJson.dependencies["@agentapprove/opencode"] = "latest";
3102
+ writeFileSync(opencodePkgPath, JSON.stringify(pkgJson, null, 2) + `
3103
+ `);
3104
+ } catch (err) {
3105
+ if (err instanceof Error) {
3106
+ console.warn(`Warning: Could not update .opencode/package.json: ${err.message}`);
3107
+ }
3108
+ }
3109
+ installedHooks.push("agentapprove");
3110
+ return { success: true, backupPath, hooks: installedHooks };
3111
+ }
3058
3112
  const hooksToInstall = mode === "observe" ? agent.hooks.filter((h2) => !h2.isApprovalHook) : agent.hooks;
3059
3113
  for (const hook of hooksToInstall) {
3060
3114
  const hookPath = join(hooksDir, hook.file);
@@ -3364,6 +3418,23 @@ function getManualInstructions(agentId, hooksDir) {
3364
3418
  "",
3365
3419
  "Restart the OpenClaw gateway to activate."
3366
3420
  ].join(`
3421
+ `);
3422
+ } else if (agentId === "opencode") {
3423
+ const configJson = JSON.stringify({
3424
+ plugin: ["@agentapprove/opencode"]
3425
+ }, null, 2);
3426
+ const opencodeConfigPath = process.env.XDG_CONFIG_HOME ? `${process.env.XDG_CONFIG_HOME}/opencode/opencode.json` : "~/.config/opencode/opencode.json";
3427
+ return [
3428
+ `Add the Agent Approve plugin to your OpenCode config (${opencodeConfigPath}):`,
3429
+ "",
3430
+ configJson,
3431
+ "",
3432
+ "Then add the dependency to .opencode/package.json:",
3433
+ "",
3434
+ ' { "dependencies": { "@agentapprove/opencode": "latest" } }',
3435
+ "",
3436
+ "OpenCode will auto-install the plugin on next start."
3437
+ ].join(`
3367
3438
  `);
3368
3439
  }
3369
3440
  return "";
@@ -3987,6 +4058,9 @@ AGENTAPPROVE_E2E_MODE=${installMode}
3987
4058
  for (const agentId of selectedAgents) {
3988
4059
  const agent = AGENTS[agentId];
3989
4060
  filesToModify.push(agent.configPath);
4061
+ if (agentId === "opencode") {
4062
+ filesToModify.push(join(getOpenCodeConfigDir(), ".opencode", "package.json"));
4063
+ }
3990
4064
  if (agentId === "vscode-agent") {
3991
4065
  const vsCodeVariants = findInstalledVSCodeVariants();
3992
4066
  for (const { path: settingsPath, variant } of vsCodeVariants) {
@@ -4100,6 +4174,20 @@ async function statusCommand() {
4100
4174
  for (const [agentId, agent] of Object.entries(AGENTS)) {
4101
4175
  if (existsSync(agent.configPath)) {
4102
4176
  const config = readJsonConfig(agent.configPath);
4177
+ if (agentId === "opencode") {
4178
+ const pluginConfig = config.plugin;
4179
+ if (Array.isArray(pluginConfig) && pluginConfig.some((entry) => entry === "@agentapprove/opencode")) {
4180
+ console.log(` ${source_default.green("✓")} ${agent.name}: agentapprove plugin`);
4181
+ }
4182
+ continue;
4183
+ }
4184
+ if (agentId === "openclaw") {
4185
+ const entries = config.plugins?.entries;
4186
+ if (entries?.agentapprove) {
4187
+ console.log(` ${source_default.green("✓")} ${agent.name}: agentapprove plugin`);
4188
+ }
4189
+ continue;
4190
+ }
4103
4191
  const hooksConfig = config[agent.hooksKey];
4104
4192
  if (hooksConfig) {
4105
4193
  const installedHooks = agent.hooks.filter((h2) => {
@@ -4165,35 +4253,69 @@ async function uninstallCommand() {
4165
4253
  continue;
4166
4254
  const config = readJsonConfig(agent.configPath);
4167
4255
  const hooksConfig = config[agent.hooksKey];
4168
- if (!hooksConfig)
4256
+ if (!hooksConfig && agentId !== "opencode")
4169
4257
  continue;
4170
4258
  let modified = false;
4171
- for (const hook of agent.hooks) {
4172
- const hookEntry = hooksConfig[hook.name];
4173
- if (!hookEntry)
4174
- continue;
4175
- if (Array.isArray(hookEntry)) {
4176
- const filtered = hookEntry.filter((e2) => {
4177
- const str = JSON.stringify(e2);
4178
- return !str.includes("agentapprove");
4259
+ if (agentId === "opencode") {
4260
+ const pluginArray = config.plugin;
4261
+ if (Array.isArray(pluginArray)) {
4262
+ const filtered = pluginArray.filter((p) => {
4263
+ if (typeof p !== "string")
4264
+ return true;
4265
+ return !p.includes("agentapprove");
4179
4266
  });
4180
- if (filtered.length !== hookEntry.length) {
4181
- if (filtered.length === 0) {
4182
- delete hooksConfig[hook.name];
4183
- } else {
4184
- hooksConfig[hook.name] = filtered;
4267
+ if (filtered.length !== pluginArray.length) {
4268
+ config.plugin = filtered;
4269
+ modified = true;
4270
+ }
4271
+ }
4272
+ const opencodePkgDir = join(getOpenCodeConfigDir(), ".opencode");
4273
+ const opencodePkgPath = join(opencodePkgDir, "package.json");
4274
+ try {
4275
+ if (existsSync(opencodePkgPath)) {
4276
+ const pkgJson = JSON.parse(readFileSync(opencodePkgPath, "utf-8"));
4277
+ const deps = pkgJson.dependencies;
4278
+ if (deps && deps["@agentapprove/opencode"]) {
4279
+ delete deps["@agentapprove/opencode"];
4280
+ writeFileSync(opencodePkgPath, JSON.stringify(pkgJson, null, 2) + `
4281
+ `);
4185
4282
  }
4283
+ }
4284
+ } catch {}
4285
+ } else if (agentId === "openclaw") {
4286
+ const entries = hooksConfig.entries;
4287
+ if (entries && entries.agentapprove) {
4288
+ delete entries.agentapprove;
4289
+ modified = true;
4290
+ }
4291
+ } else {
4292
+ for (const hook of agent.hooks) {
4293
+ const hookEntry = hooksConfig[hook.name];
4294
+ if (!hookEntry)
4295
+ continue;
4296
+ if (Array.isArray(hookEntry)) {
4297
+ const filtered = hookEntry.filter((e2) => {
4298
+ const str = JSON.stringify(e2);
4299
+ return !str.includes("agentapprove");
4300
+ });
4301
+ if (filtered.length !== hookEntry.length) {
4302
+ if (filtered.length === 0) {
4303
+ delete hooksConfig[hook.name];
4304
+ } else {
4305
+ hooksConfig[hook.name] = filtered;
4306
+ }
4307
+ modified = true;
4308
+ }
4309
+ } else if (typeof hookEntry === "string" && hookEntry.includes("agentapprove")) {
4310
+ delete hooksConfig[hook.name];
4186
4311
  modified = true;
4187
4312
  }
4188
- } else if (typeof hookEntry === "string" && hookEntry.includes("agentapprove")) {
4189
- delete hooksConfig[hook.name];
4313
+ }
4314
+ if (hooksConfig["Prompt"]) {
4315
+ delete hooksConfig["Prompt"];
4190
4316
  modified = true;
4191
4317
  }
4192
4318
  }
4193
- if (hooksConfig["Prompt"]) {
4194
- delete hooksConfig["Prompt"];
4195
- modified = true;
4196
- }
4197
4319
  if (modified) {
4198
4320
  writeJsonConfig(agent.configPath, config);
4199
4321
  console.log(` ${source_default.green("✓")} Removed hooks from ${agent.name}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentapprove",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Approve AI agent actions from your iPhone or Apple Watch",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,7 @@
22
22
  "cursor",
23
23
  "gemini",
24
24
  "openclaw",
25
+ "opencode",
25
26
  "ai",
26
27
  "agent",
27
28
  "approval",