agentapprove 0.1.8 → 0.1.9
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 +1 -1
- package/dist/cli.js +236 -39
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ agentapprove --no-e2e # Skip end-to-end encryption setup
|
|
|
95
95
|
- Node.js 18+
|
|
96
96
|
- macOS, Linux, or Windows (with Git Bash / Git for Windows)
|
|
97
97
|
- System tools: `curl`, `jq`, `openssl` (included with Git Bash on Windows)
|
|
98
|
-
- Agent Approve iOS app and active subscription ($
|
|
98
|
+
- Agent Approve iOS app and active subscription ($14.99/month, 7-day free trial)
|
|
99
99
|
|
|
100
100
|
## Links
|
|
101
101
|
|
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
33
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
34
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
35
35
|
|
|
36
|
-
// node_modules/sisteransi/src/index.js
|
|
36
|
+
// ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
|
|
37
37
|
var require_src = __commonJS((exports, module) => {
|
|
38
38
|
var ESC = "\x1B";
|
|
39
39
|
var CSI = `${ESC}[`;
|
|
@@ -91,7 +91,7 @@ var require_src = __commonJS((exports, module) => {
|
|
|
91
91
|
module.exports = { cursor, scroll, erase, beep };
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
// node_modules/picocolors/picocolors.js
|
|
94
|
+
// ../../node_modules/.bun/picocolors@1.1.1/node_modules/picocolors/picocolors.js
|
|
95
95
|
var require_picocolors = __commonJS((exports, module) => {
|
|
96
96
|
var p = process || {};
|
|
97
97
|
var argv = p.argv || [];
|
|
@@ -161,7 +161,7 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
161
161
|
module.exports.createColors = createColors;
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
|
|
164
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
|
|
165
165
|
var require_QRMode = __commonJS((exports, module) => {
|
|
166
166
|
module.exports = {
|
|
167
167
|
MODE_NUMBER: 1 << 0,
|
|
@@ -171,7 +171,7 @@ var require_QRMode = __commonJS((exports, module) => {
|
|
|
171
171
|
};
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
|
|
174
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
|
|
175
175
|
var require_QR8bitByte = __commonJS((exports, module) => {
|
|
176
176
|
var QRMode = require_QRMode();
|
|
177
177
|
function QR8bitByte(data) {
|
|
@@ -191,7 +191,7 @@ var require_QR8bitByte = __commonJS((exports, module) => {
|
|
|
191
191
|
module.exports = QR8bitByte;
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
|
|
194
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
|
|
195
195
|
var require_QRMath = __commonJS((exports, module) => {
|
|
196
196
|
var QRMath = {
|
|
197
197
|
glog: function(n) {
|
|
@@ -227,7 +227,7 @@ var require_QRMath = __commonJS((exports, module) => {
|
|
|
227
227
|
module.exports = QRMath;
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
|
|
230
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
|
|
231
231
|
var require_QRPolynomial = __commonJS((exports, module) => {
|
|
232
232
|
var QRMath = require_QRMath();
|
|
233
233
|
function QRPolynomial(num, shift) {
|
|
@@ -277,7 +277,7 @@ var require_QRPolynomial = __commonJS((exports, module) => {
|
|
|
277
277
|
module.exports = QRPolynomial;
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
|
|
280
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
|
|
281
281
|
var require_QRMaskPattern = __commonJS((exports, module) => {
|
|
282
282
|
module.exports = {
|
|
283
283
|
PATTERN000: 0,
|
|
@@ -291,7 +291,7 @@ var require_QRMaskPattern = __commonJS((exports, module) => {
|
|
|
291
291
|
};
|
|
292
292
|
});
|
|
293
293
|
|
|
294
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js
|
|
294
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRUtil.js
|
|
295
295
|
var require_QRUtil = __commonJS((exports, module) => {
|
|
296
296
|
var QRMode = require_QRMode();
|
|
297
297
|
var QRPolynomial = require_QRPolynomial();
|
|
@@ -517,7 +517,7 @@ var require_QRUtil = __commonJS((exports, module) => {
|
|
|
517
517
|
module.exports = QRUtil;
|
|
518
518
|
});
|
|
519
519
|
|
|
520
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js
|
|
520
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js
|
|
521
521
|
var require_QRErrorCorrectLevel = __commonJS((exports, module) => {
|
|
522
522
|
module.exports = {
|
|
523
523
|
L: 1,
|
|
@@ -527,7 +527,7 @@ var require_QRErrorCorrectLevel = __commonJS((exports, module) => {
|
|
|
527
527
|
};
|
|
528
528
|
});
|
|
529
529
|
|
|
530
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js
|
|
530
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRRSBlock.js
|
|
531
531
|
var require_QRRSBlock = __commonJS((exports, module) => {
|
|
532
532
|
var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
|
|
533
533
|
function QRRSBlock(totalCount, dataCount) {
|
|
@@ -730,7 +730,7 @@ var require_QRRSBlock = __commonJS((exports, module) => {
|
|
|
730
730
|
module.exports = QRRSBlock;
|
|
731
731
|
});
|
|
732
732
|
|
|
733
|
-
// node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js
|
|
733
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRBitBuffer.js
|
|
734
734
|
var require_QRBitBuffer = __commonJS((exports, module) => {
|
|
735
735
|
function QRBitBuffer() {
|
|
736
736
|
this.buffer = [];
|
|
@@ -763,7 +763,7 @@ var require_QRBitBuffer = __commonJS((exports, module) => {
|
|
|
763
763
|
module.exports = QRBitBuffer;
|
|
764
764
|
});
|
|
765
765
|
|
|
766
|
-
// node_modules/qrcode-terminal/vendor/QRCode/index.js
|
|
766
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/index.js
|
|
767
767
|
var require_QRCode = __commonJS((exports, module) => {
|
|
768
768
|
var QR8bitByte = require_QR8bitByte();
|
|
769
769
|
var QRUtil = require_QRUtil();
|
|
@@ -1084,7 +1084,7 @@ var require_QRCode = __commonJS((exports, module) => {
|
|
|
1084
1084
|
module.exports = QRCode;
|
|
1085
1085
|
});
|
|
1086
1086
|
|
|
1087
|
-
// node_modules/qrcode-terminal/lib/main.js
|
|
1087
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/lib/main.js
|
|
1088
1088
|
var require_main = __commonJS((exports, module) => {
|
|
1089
1089
|
var QRCode = require_QRCode();
|
|
1090
1090
|
var QRErrorCorrectLevel = require_QRErrorCorrectLevel();
|
|
@@ -1178,7 +1178,7 @@ var require_main = __commonJS((exports, module) => {
|
|
|
1178
1178
|
};
|
|
1179
1179
|
});
|
|
1180
1180
|
|
|
1181
|
-
// node_modules/@clack/core/dist/index.mjs
|
|
1181
|
+
// ../../node_modules/.bun/@clack+core@0.3.5/node_modules/@clack/core/dist/index.mjs
|
|
1182
1182
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
1183
1183
|
import { stdin as $, stdout as k } from "node:process";
|
|
1184
1184
|
import * as f from "node:readline";
|
|
@@ -1592,7 +1592,7 @@ function OD({ input: e = $, output: u = k, overwrite: F = true, hideCursor: t =
|
|
|
1592
1592
|
};
|
|
1593
1593
|
}
|
|
1594
1594
|
|
|
1595
|
-
// node_modules/@clack/prompts/dist/index.mjs
|
|
1595
|
+
// ../../node_modules/.bun/@clack+prompts@0.8.2/node_modules/@clack/prompts/dist/index.mjs
|
|
1596
1596
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
1597
1597
|
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
1598
1598
|
import h from "node:process";
|
|
@@ -1836,7 +1836,7 @@ var ve = async (s, n) => {
|
|
|
1836
1836
|
return t;
|
|
1837
1837
|
};
|
|
1838
1838
|
|
|
1839
|
-
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
1839
|
+
// ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
1840
1840
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
1841
1841
|
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
1842
1842
|
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
@@ -2013,7 +2013,7 @@ function assembleStyles() {
|
|
|
2013
2013
|
var ansiStyles = assembleStyles();
|
|
2014
2014
|
var ansi_styles_default = ansiStyles;
|
|
2015
2015
|
|
|
2016
|
-
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
2016
|
+
// ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
|
|
2017
2017
|
import process2 from "node:process";
|
|
2018
2018
|
import os from "node:os";
|
|
2019
2019
|
import tty from "node:tty";
|
|
@@ -2145,7 +2145,7 @@ var supportsColor = {
|
|
|
2145
2145
|
};
|
|
2146
2146
|
var supports_color_default = supportsColor;
|
|
2147
2147
|
|
|
2148
|
-
// node_modules/chalk/source/utilities.js
|
|
2148
|
+
// ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/utilities.js
|
|
2149
2149
|
function stringReplaceAll(string, substring, replacer) {
|
|
2150
2150
|
let index = string.indexOf(substring);
|
|
2151
2151
|
if (index === -1) {
|
|
@@ -2178,7 +2178,7 @@ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
|
2178
2178
|
return returnValue;
|
|
2179
2179
|
}
|
|
2180
2180
|
|
|
2181
|
-
// node_modules/chalk/source/index.js
|
|
2181
|
+
// ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/index.js
|
|
2182
2182
|
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
|
|
2183
2183
|
var GENERATOR = Symbol("GENERATOR");
|
|
2184
2184
|
var STYLER = Symbol("STYLER");
|
|
@@ -2332,7 +2332,7 @@ import { homedir, hostname, platform } from "os";
|
|
|
2332
2332
|
import { join, dirname, basename } from "path";
|
|
2333
2333
|
import { execSync, spawnSync } from "child_process";
|
|
2334
2334
|
import { randomBytes, createHash } from "crypto";
|
|
2335
|
-
var VERSION = "0.1.
|
|
2335
|
+
var VERSION = "0.1.9";
|
|
2336
2336
|
function getApiUrl() {
|
|
2337
2337
|
return process.env.AGENTAPPROVE_API || "https://api.agentapprove.com";
|
|
2338
2338
|
}
|
|
@@ -2380,6 +2380,9 @@ function getCommand() {
|
|
|
2380
2380
|
}
|
|
2381
2381
|
return filtered[0] || "install";
|
|
2382
2382
|
}
|
|
2383
|
+
var OPENCODE_PLUGIN_VERSION = "0.1.6";
|
|
2384
|
+
var OPENCLAW_PLUGIN_VERSION = "0.2.3";
|
|
2385
|
+
var OPENCLAW_PLUGIN_SPEC = `@agentapprove/openclaw@${OPENCLAW_PLUGIN_VERSION}`;
|
|
2383
2386
|
var AGENTS = {
|
|
2384
2387
|
"claude-code": {
|
|
2385
2388
|
name: "Claude Code",
|
|
@@ -2435,7 +2438,7 @@ var AGENTS = {
|
|
|
2435
2438
|
]
|
|
2436
2439
|
},
|
|
2437
2440
|
"vscode-agent": {
|
|
2438
|
-
name: "VS Code
|
|
2441
|
+
name: "VS Code GitHub Copilot",
|
|
2439
2442
|
configPath: join(homedir(), ".agentapprove", "hooks", "vscode-hooks.json"),
|
|
2440
2443
|
hooksKey: "hooks",
|
|
2441
2444
|
hooks: [
|
|
@@ -2450,7 +2453,7 @@ var AGENTS = {
|
|
|
2450
2453
|
]
|
|
2451
2454
|
},
|
|
2452
2455
|
"copilot-cli": {
|
|
2453
|
-
name: "Copilot CLI",
|
|
2456
|
+
name: "GitHub Copilot CLI",
|
|
2454
2457
|
configPath: join(homedir(), ".agentapprove", "copilot-cli-hooks.json"),
|
|
2455
2458
|
hooksKey: "hooks",
|
|
2456
2459
|
hooks: [
|
|
@@ -2470,6 +2473,18 @@ var AGENTS = {
|
|
|
2470
2473
|
{ name: "agentapprove", file: "@agentapprove/openclaw", description: "Tool approval + event monitoring", isApprovalHook: true, isPlugin: true }
|
|
2471
2474
|
]
|
|
2472
2475
|
},
|
|
2476
|
+
codex: {
|
|
2477
|
+
name: "OpenAI Codex",
|
|
2478
|
+
configPath: join(homedir(), ".codex", "hooks.json"),
|
|
2479
|
+
hooksKey: "hooks",
|
|
2480
|
+
hooks: [
|
|
2481
|
+
{ name: "SessionStart", file: "codex-session-start.sh", description: "Session started", hasMatcher: true },
|
|
2482
|
+
{ name: "PreToolUse", file: "codex-pre-tool.sh", description: "Tool approval", hasMatcher: true, isApprovalHook: true, timeout: 300 },
|
|
2483
|
+
{ name: "PostToolUse", file: "codex-post-tool.sh", description: "Tool completion logging", hasMatcher: true },
|
|
2484
|
+
{ name: "UserPromptSubmit", file: "codex-user-prompt.sh", description: "User prompt submitted", hasMatcher: false },
|
|
2485
|
+
{ name: "Stop", file: "codex-stop.sh", description: "Agent stopped (iOS input)", hasMatcher: false, timeout: 600 }
|
|
2486
|
+
]
|
|
2487
|
+
},
|
|
2473
2488
|
opencode: {
|
|
2474
2489
|
name: "OpenCode",
|
|
2475
2490
|
configPath: getOpenCodeConfigPath(),
|
|
@@ -2594,6 +2609,15 @@ function readJsoncConfig(configPath) {
|
|
|
2594
2609
|
return {};
|
|
2595
2610
|
}
|
|
2596
2611
|
}
|
|
2612
|
+
function getGithubHookEnv(agentId) {
|
|
2613
|
+
if (agentId === "vscode-agent") {
|
|
2614
|
+
return { AGENTAPPROVE_GITHUB_VARIANT: "vscode" };
|
|
2615
|
+
}
|
|
2616
|
+
if (agentId === "copilot-cli") {
|
|
2617
|
+
return { AGENTAPPROVE_GITHUB_VARIANT: "github" };
|
|
2618
|
+
}
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2597
2621
|
function addToVSCodeHookLocations() {
|
|
2598
2622
|
const hookLocation = "~/.agentapprove/hooks";
|
|
2599
2623
|
const modified = [];
|
|
@@ -2650,6 +2674,10 @@ function detectInstalledAgents() {
|
|
|
2650
2674
|
if (existsSync(join(homedir(), ".openclaw"))) {
|
|
2651
2675
|
installed.push(id);
|
|
2652
2676
|
}
|
|
2677
|
+
} else if (id === "codex") {
|
|
2678
|
+
if (existsSync(join(homedir(), ".codex"))) {
|
|
2679
|
+
installed.push(id);
|
|
2680
|
+
}
|
|
2653
2681
|
} else if (id === "opencode") {
|
|
2654
2682
|
if (existsSync(getOpenCodeConfigDir())) {
|
|
2655
2683
|
installed.push(id);
|
|
@@ -2814,6 +2842,87 @@ function writeJsonConfig(configPath, config) {
|
|
|
2814
2842
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + `
|
|
2815
2843
|
`);
|
|
2816
2844
|
}
|
|
2845
|
+
function ensureCodexFeatureFlag(configPath = join(homedir(), ".codex", "config.toml")) {
|
|
2846
|
+
const dir = dirname(configPath);
|
|
2847
|
+
if (!existsSync(dir)) {
|
|
2848
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2849
|
+
}
|
|
2850
|
+
const sectionHeader = "[features]";
|
|
2851
|
+
const desiredLine = "codex_hooks = true";
|
|
2852
|
+
if (!existsSync(configPath)) {
|
|
2853
|
+
writeFileSync(configPath, `${sectionHeader}
|
|
2854
|
+
${desiredLine}
|
|
2855
|
+
`, { mode: 384 });
|
|
2856
|
+
return { updated: true, backupPath: null };
|
|
2857
|
+
}
|
|
2858
|
+
const original = readFileSync(configPath, "utf-8");
|
|
2859
|
+
const lines = original.split(/\r?\n/);
|
|
2860
|
+
const updatedLines = [];
|
|
2861
|
+
let inFeatures = false;
|
|
2862
|
+
let foundFeatures = false;
|
|
2863
|
+
let foundKey = false;
|
|
2864
|
+
let changed = false;
|
|
2865
|
+
for (const line of lines) {
|
|
2866
|
+
const sectionMatch = line.match(/^\s*\[([^\]]+)\]\s*$/);
|
|
2867
|
+
if (sectionMatch) {
|
|
2868
|
+
if (inFeatures && !foundKey) {
|
|
2869
|
+
updatedLines.push(desiredLine);
|
|
2870
|
+
foundKey = true;
|
|
2871
|
+
changed = true;
|
|
2872
|
+
}
|
|
2873
|
+
inFeatures = sectionMatch[1] === "features";
|
|
2874
|
+
foundFeatures ||= inFeatures;
|
|
2875
|
+
updatedLines.push(line);
|
|
2876
|
+
continue;
|
|
2877
|
+
}
|
|
2878
|
+
if (inFeatures && /^\s*codex_hooks\s*=/.test(line)) {
|
|
2879
|
+
if (line.trim() !== desiredLine) {
|
|
2880
|
+
updatedLines.push(desiredLine);
|
|
2881
|
+
changed = true;
|
|
2882
|
+
} else {
|
|
2883
|
+
updatedLines.push(line);
|
|
2884
|
+
}
|
|
2885
|
+
foundKey = true;
|
|
2886
|
+
continue;
|
|
2887
|
+
}
|
|
2888
|
+
updatedLines.push(line);
|
|
2889
|
+
}
|
|
2890
|
+
if (inFeatures && !foundKey) {
|
|
2891
|
+
updatedLines.push(desiredLine);
|
|
2892
|
+
changed = true;
|
|
2893
|
+
}
|
|
2894
|
+
if (!foundFeatures) {
|
|
2895
|
+
const hasContent = updatedLines.some((line) => line.trim() !== "");
|
|
2896
|
+
if (hasContent) {
|
|
2897
|
+
updatedLines.push("");
|
|
2898
|
+
}
|
|
2899
|
+
updatedLines.push(sectionHeader, desiredLine);
|
|
2900
|
+
changed = true;
|
|
2901
|
+
}
|
|
2902
|
+
if (!changed) {
|
|
2903
|
+
return { updated: false, backupPath: null };
|
|
2904
|
+
}
|
|
2905
|
+
const backupPath = backupConfig(configPath);
|
|
2906
|
+
writeFileSync(configPath, `${updatedLines.join(`
|
|
2907
|
+
`).replace(/\n*$/, `
|
|
2908
|
+
`)}`, { mode: 384 });
|
|
2909
|
+
return { updated: true, backupPath };
|
|
2910
|
+
}
|
|
2911
|
+
function disableCodexFeatureFlag(configPath = join(homedir(), ".codex", "config.toml")) {
|
|
2912
|
+
if (!existsSync(configPath)) {
|
|
2913
|
+
return { updated: false, backupPath: null };
|
|
2914
|
+
}
|
|
2915
|
+
const original = readFileSync(configPath, "utf-8");
|
|
2916
|
+
const updated = original.split(/\r?\n/).filter((line) => !/^\s*codex_hooks\s*=/.test(line)).join(`
|
|
2917
|
+
`).replace(/\n*$/, `
|
|
2918
|
+
`);
|
|
2919
|
+
if (updated === original) {
|
|
2920
|
+
return { updated: false, backupPath: null };
|
|
2921
|
+
}
|
|
2922
|
+
const backupPath = backupConfig(configPath);
|
|
2923
|
+
writeFileSync(configPath, updated, { mode: 384 });
|
|
2924
|
+
return { updated: true, backupPath };
|
|
2925
|
+
}
|
|
2817
2926
|
async function createPairingSession(configuredAgents, e2eKeyId) {
|
|
2818
2927
|
try {
|
|
2819
2928
|
const machineHostname = hostname();
|
|
@@ -3147,6 +3256,34 @@ async function installHooksForAgent(agentId, hooksDir, mode = "approval") {
|
|
|
3147
3256
|
hookArray.push(hookEntry);
|
|
3148
3257
|
}
|
|
3149
3258
|
installedHooks.push(hook.name);
|
|
3259
|
+
} else if (agentId === "codex") {
|
|
3260
|
+
if (!hooksConfig[hook.name]) {
|
|
3261
|
+
hooksConfig[hook.name] = [];
|
|
3262
|
+
}
|
|
3263
|
+
const hookArray = hooksConfig[hook.name];
|
|
3264
|
+
const hasMatcher = hook.hasMatcher ?? true;
|
|
3265
|
+
const hookTimeout = hook.timeout;
|
|
3266
|
+
const existingIdx = hookArray.findIndex((h2) => h2.hooks?.some((hookScript) => {
|
|
3267
|
+
if (typeof hookScript === "string")
|
|
3268
|
+
return hookScript.includes("agentapprove");
|
|
3269
|
+
if (typeof hookScript === "object" && hookScript.command)
|
|
3270
|
+
return hookScript.command.includes("agentapprove");
|
|
3271
|
+
return false;
|
|
3272
|
+
}));
|
|
3273
|
+
const hookObject = {
|
|
3274
|
+
type: "command",
|
|
3275
|
+
command: hookCommand
|
|
3276
|
+
};
|
|
3277
|
+
if (hookTimeout) {
|
|
3278
|
+
hookObject.timeout = hookTimeout;
|
|
3279
|
+
}
|
|
3280
|
+
const hookEntry = hasMatcher ? { matcher: "*", hooks: [hookObject] } : { hooks: [hookObject] };
|
|
3281
|
+
if (existingIdx >= 0) {
|
|
3282
|
+
hookArray[existingIdx] = hookEntry;
|
|
3283
|
+
} else {
|
|
3284
|
+
hookArray.push(hookEntry);
|
|
3285
|
+
}
|
|
3286
|
+
installedHooks.push(hook.name);
|
|
3150
3287
|
} else if (agentId === "cursor") {
|
|
3151
3288
|
if (typeof config["version"] !== "number") {
|
|
3152
3289
|
config["version"] = 1;
|
|
@@ -3231,6 +3368,10 @@ async function installHooksForAgent(agentId, hooksDir, mode = "approval") {
|
|
|
3231
3368
|
type: "command",
|
|
3232
3369
|
command: hookCommand
|
|
3233
3370
|
};
|
|
3371
|
+
const hookEnv = getGithubHookEnv(agentId);
|
|
3372
|
+
if (hookEnv) {
|
|
3373
|
+
hookEntry.env = hookEnv;
|
|
3374
|
+
}
|
|
3234
3375
|
if (hook.isApprovalHook) {
|
|
3235
3376
|
hookEntry.timeout = 300;
|
|
3236
3377
|
}
|
|
@@ -3256,6 +3397,10 @@ async function installHooksForAgent(agentId, hooksDir, mode = "approval") {
|
|
|
3256
3397
|
type: "command",
|
|
3257
3398
|
bash: isWindows() ? toGitBashPath(hookPath) : hookPath
|
|
3258
3399
|
};
|
|
3400
|
+
const hookEnv = getGithubHookEnv(agentId);
|
|
3401
|
+
if (hookEnv) {
|
|
3402
|
+
hookEntry.env = hookEnv;
|
|
3403
|
+
}
|
|
3259
3404
|
if (hook.isApprovalHook) {
|
|
3260
3405
|
hookEntry.timeoutSec = 300;
|
|
3261
3406
|
}
|
|
@@ -3268,7 +3413,7 @@ async function installHooksForAgent(agentId, hooksDir, mode = "approval") {
|
|
|
3268
3413
|
const approvalHooks = agent.hooks.filter((h2) => h2.isApprovalHook);
|
|
3269
3414
|
for (const hook of approvalHooks) {
|
|
3270
3415
|
if (hooksConfig[hook.name]) {
|
|
3271
|
-
if (agentId === "claude-code" || agentId === "gemini-cli") {
|
|
3416
|
+
if (agentId === "claude-code" || agentId === "gemini-cli" || agentId === "codex") {
|
|
3272
3417
|
const hookArray = hooksConfig[hook.name];
|
|
3273
3418
|
if (Array.isArray(hookArray)) {
|
|
3274
3419
|
const cleaned = hookArray.filter((h2) => {
|
|
@@ -3340,6 +3485,9 @@ async function installHooksForAgent(agentId, hooksDir, mode = "approval") {
|
|
|
3340
3485
|
}
|
|
3341
3486
|
}
|
|
3342
3487
|
writeJsonConfig(agent.configPath, config);
|
|
3488
|
+
if (agentId === "codex") {
|
|
3489
|
+
ensureCodexFeatureFlag();
|
|
3490
|
+
}
|
|
3343
3491
|
return { success: true, backupPath, hooks: installedHooks };
|
|
3344
3492
|
}
|
|
3345
3493
|
function getManualInstructions(agentId, hooksDir) {
|
|
@@ -3353,13 +3501,39 @@ function getManualInstructions(agentId, hooksDir) {
|
|
|
3353
3501
|
const hookTimeout = hook.timeout;
|
|
3354
3502
|
const hookPath = join(hooksDir, hook.file);
|
|
3355
3503
|
const hookCmd = buildHookCommand(hookPath, agentId);
|
|
3356
|
-
const
|
|
3504
|
+
const hookObject = {
|
|
3505
|
+
type: "command",
|
|
3506
|
+
command: hookCmd
|
|
3507
|
+
};
|
|
3357
3508
|
if (hookTimeout) {
|
|
3358
|
-
|
|
3509
|
+
hookObject.timeout = hookTimeout;
|
|
3359
3510
|
}
|
|
3511
|
+
const entry = hasMatcher ? { matcher: "*", hooks: [hookObject] } : { hooks: [hookObject] };
|
|
3360
3512
|
hooksObj[hook.name] = [entry];
|
|
3361
3513
|
}
|
|
3362
3514
|
return JSON.stringify({ hooks: hooksObj }, null, 2);
|
|
3515
|
+
} else if (agentId === "codex") {
|
|
3516
|
+
const hooksObj = {};
|
|
3517
|
+
for (const hook of agent.hooks) {
|
|
3518
|
+
const hasMatcher = hook.hasMatcher ?? true;
|
|
3519
|
+
const hookTimeout = hook.timeout;
|
|
3520
|
+
const hookPath = join(hooksDir, hook.file);
|
|
3521
|
+
const hookCmd = buildHookCommand(hookPath, agentId);
|
|
3522
|
+
const hookObject = {
|
|
3523
|
+
type: "command",
|
|
3524
|
+
command: hookCmd
|
|
3525
|
+
};
|
|
3526
|
+
if (hookTimeout) {
|
|
3527
|
+
hookObject.timeout = hookTimeout;
|
|
3528
|
+
}
|
|
3529
|
+
const entry = hasMatcher ? { matcher: "*", hooks: [hookObject] } : { hooks: [hookObject] };
|
|
3530
|
+
hooksObj[hook.name] = [entry];
|
|
3531
|
+
}
|
|
3532
|
+
return `${JSON.stringify({ hooks: hooksObj }, null, 2)}
|
|
3533
|
+
|
|
3534
|
+
Also ensure ~/.codex/config.toml contains:
|
|
3535
|
+
[features]
|
|
3536
|
+
codex_hooks = true`;
|
|
3363
3537
|
} else if (agentId === "cursor") {
|
|
3364
3538
|
const hooksObj = {};
|
|
3365
3539
|
for (const hook of agent.hooks) {
|
|
@@ -3397,6 +3571,9 @@ function getManualInstructions(agentId, hooksDir) {
|
|
|
3397
3571
|
type: "command",
|
|
3398
3572
|
command: hookCmd
|
|
3399
3573
|
};
|
|
3574
|
+
const hookEnv = getGithubHookEnv(agentId);
|
|
3575
|
+
if (hookEnv)
|
|
3576
|
+
entry.env = hookEnv;
|
|
3400
3577
|
if (isApproval)
|
|
3401
3578
|
entry.timeout = 300;
|
|
3402
3579
|
if (hookTimeout)
|
|
@@ -3413,6 +3590,9 @@ function getManualInstructions(agentId, hooksDir) {
|
|
|
3413
3590
|
type: "command",
|
|
3414
3591
|
bash: isWindows() ? toGitBashPath(hookPath) : hookPath
|
|
3415
3592
|
};
|
|
3593
|
+
const hookEnv = getGithubHookEnv(agentId);
|
|
3594
|
+
if (hookEnv)
|
|
3595
|
+
entry.env = hookEnv;
|
|
3416
3596
|
if (isApproval)
|
|
3417
3597
|
entry.timeoutSec = 300;
|
|
3418
3598
|
hooksObj[hook.name] = [entry];
|
|
@@ -3440,7 +3620,7 @@ function getManualInstructions(agentId, hooksDir) {
|
|
|
3440
3620
|
return [
|
|
3441
3621
|
"Install the Agent Approve plugin for OpenClaw:",
|
|
3442
3622
|
"",
|
|
3443
|
-
|
|
3623
|
+
` openclaw plugins install ${OPENCLAW_PLUGIN_SPEC}`,
|
|
3444
3624
|
"",
|
|
3445
3625
|
"Then add to your OpenClaw config (~/.openclaw/openclaw.json):",
|
|
3446
3626
|
"",
|
|
@@ -3461,7 +3641,7 @@ function getManualInstructions(agentId, hooksDir) {
|
|
|
3461
3641
|
"",
|
|
3462
3642
|
"Then add the dependency to your OpenCode package.json (same directory):",
|
|
3463
3643
|
"",
|
|
3464
|
-
|
|
3644
|
+
` { "dependencies": { "@agentapprove/opencode": "${OPENCODE_PLUGIN_VERSION}" } }`,
|
|
3465
3645
|
"",
|
|
3466
3646
|
"OpenCode will auto-install the plugin on next start."
|
|
3467
3647
|
].join(`
|
|
@@ -3507,6 +3687,11 @@ var HOOK_FILES = [
|
|
|
3507
3687
|
"gemini-session-start.sh",
|
|
3508
3688
|
"gemini-session-end.sh",
|
|
3509
3689
|
"gemini-stop.sh",
|
|
3690
|
+
"codex-session-start.sh",
|
|
3691
|
+
"codex-pre-tool.sh",
|
|
3692
|
+
"codex-post-tool.sh",
|
|
3693
|
+
"codex-user-prompt.sh",
|
|
3694
|
+
"codex-stop.sh",
|
|
3510
3695
|
"github-session-start.sh",
|
|
3511
3696
|
"github-session-end.sh",
|
|
3512
3697
|
"github-user-prompt.sh",
|
|
@@ -3550,7 +3735,7 @@ async function copyHookScripts(hooksDir, token) {
|
|
|
3550
3735
|
}
|
|
3551
3736
|
function installOpenClawPluginViaCli() {
|
|
3552
3737
|
try {
|
|
3553
|
-
execSync(
|
|
3738
|
+
execSync(`openclaw plugins install ${OPENCLAW_PLUGIN_SPEC}`, { stdio: "pipe" });
|
|
3554
3739
|
return { success: true };
|
|
3555
3740
|
} catch (err) {
|
|
3556
3741
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
@@ -3789,7 +3974,7 @@ E2E Key: ${keyId}`;
|
|
|
3789
3974
|
Privacy: ${existingConfig.privacy || "unknown"}${e2eLine}`, "Existing configuration found");
|
|
3790
3975
|
} else {
|
|
3791
3976
|
me(`Approve AI agent actions from your iPhone or Apple Watch.
|
|
3792
|
-
Installs hooks for Claude Code, Cursor, Gemini CLI, VS Code, and Copilot CLI.`, "About");
|
|
3977
|
+
Installs hooks for Claude Code, Cursor, Gemini CLI, VS Code GitHub Copilot, and GitHub Copilot CLI.`, "About");
|
|
3793
3978
|
}
|
|
3794
3979
|
const installedAgents = detectInstalledAgents();
|
|
3795
3980
|
const agentOptions = Object.entries(AGENTS).map(([id, agent]) => ({
|
|
@@ -4088,6 +4273,9 @@ AGENTAPPROVE_E2E_MODE=${installMode}
|
|
|
4088
4273
|
for (const agentId of selectedAgents) {
|
|
4089
4274
|
const agent = AGENTS[agentId];
|
|
4090
4275
|
filesToModify.push(agent.configPath);
|
|
4276
|
+
if (agentId === "codex") {
|
|
4277
|
+
filesToModify.push(join(homedir(), ".codex", "config.toml"));
|
|
4278
|
+
}
|
|
4091
4279
|
if (agentId === "opencode") {
|
|
4092
4280
|
filesToModify.push(join(getOpenCodeConfigDir(), "package.json"));
|
|
4093
4281
|
}
|
|
@@ -4135,16 +4323,19 @@ Backups will be created with timestamp`, "Files to be modified");
|
|
|
4135
4323
|
}
|
|
4136
4324
|
}
|
|
4137
4325
|
}
|
|
4326
|
+
if (agentId === "codex") {
|
|
4327
|
+
v2.info(source_default.dim(" Updated ~/.codex/config.toml to enable codex_hooks"));
|
|
4328
|
+
}
|
|
4138
4329
|
if (agentId === "copilot-cli") {
|
|
4139
|
-
v2.info(source_default.dim(` To use with Copilot CLI, copy to your repo:
|
|
4330
|
+
v2.info(source_default.dim(` To use with GitHub Copilot CLI, copy to your repo:
|
|
4140
4331
|
` + ` cp ${agent.configPath} .github/hooks/agentapprove.json`));
|
|
4141
4332
|
}
|
|
4142
4333
|
} else {
|
|
4143
4334
|
spinner.stop(`${agent.name} configuration failed`);
|
|
4144
4335
|
if (agentId === "openclaw") {
|
|
4145
|
-
v2.warn(`Could not install
|
|
4336
|
+
v2.warn(`Could not install ${OPENCLAW_PLUGIN_SPEC} via OpenClaw CLI.
|
|
4146
4337
|
` + ` Error: ${result.error || "unknown"}
|
|
4147
|
-
` + ` Install manually: openclaw plugins install
|
|
4338
|
+
` + ` Install manually: openclaw plugins install ${OPENCLAW_PLUGIN_SPEC}
|
|
4148
4339
|
` + ` Then re-run: npx agentapprove`);
|
|
4149
4340
|
}
|
|
4150
4341
|
}
|
|
@@ -4282,9 +4473,7 @@ async function uninstallCommand() {
|
|
|
4282
4473
|
if (!existsSync(agent.configPath))
|
|
4283
4474
|
continue;
|
|
4284
4475
|
const config = readJsonConfig(agent.configPath);
|
|
4285
|
-
const hooksConfig = config[agent.hooksKey];
|
|
4286
|
-
if (!hooksConfig && agentId !== "opencode")
|
|
4287
|
-
continue;
|
|
4476
|
+
const hooksConfig = config[agent.hooksKey] ?? {};
|
|
4288
4477
|
let modified = false;
|
|
4289
4478
|
if (agentId === "opencode") {
|
|
4290
4479
|
const pluginArray = config.plugin;
|
|
@@ -4349,6 +4538,12 @@ async function uninstallCommand() {
|
|
|
4349
4538
|
writeJsonConfig(agent.configPath, config);
|
|
4350
4539
|
console.log(` ${source_default.green("✓")} Removed hooks from ${agent.name}`);
|
|
4351
4540
|
}
|
|
4541
|
+
if (agentId === "codex" && modified && Object.keys(hooksConfig).length === 0) {
|
|
4542
|
+
const codexFlag = disableCodexFeatureFlag();
|
|
4543
|
+
if (codexFlag.updated) {
|
|
4544
|
+
console.log(` ${source_default.green("✓")} Disabled codex_hooks in ~/.codex/config.toml`);
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4352
4547
|
}
|
|
4353
4548
|
const envPath = join(getAgentApproveDir(), "env");
|
|
4354
4549
|
if (existsSync(envPath)) {
|
|
@@ -4407,7 +4602,7 @@ async function initRepoCommand() {
|
|
|
4407
4602
|
const cliHooksPath = join(getAgentApproveDir(), "copilot-cli-hooks.json");
|
|
4408
4603
|
if (!existsSync(cliHooksPath)) {
|
|
4409
4604
|
console.log(source_default.yellow(`
|
|
4410
|
-
Copilot CLI hooks not found. Run the installer first with Copilot CLI selected.`));
|
|
4605
|
+
GitHub Copilot CLI hooks not found. Run the installer first with GitHub Copilot CLI selected.`));
|
|
4411
4606
|
console.log(source_default.dim(` npx agentapprove install
|
|
4412
4607
|
`));
|
|
4413
4608
|
process.exit(1);
|
|
@@ -4430,7 +4625,7 @@ async function initRepoCommand() {
|
|
|
4430
4625
|
const targetDir = join(repoRoot, ".github", "hooks");
|
|
4431
4626
|
const targetFile = join(targetDir, "agentapprove.json");
|
|
4432
4627
|
console.log(source_default.cyan(`
|
|
4433
|
-
Installing Copilot CLI hooks to ${targetFile}
|
|
4628
|
+
Installing GitHub Copilot CLI hooks to ${targetFile}
|
|
4434
4629
|
`));
|
|
4435
4630
|
if (existsSync(targetFile)) {
|
|
4436
4631
|
console.log(source_default.yellow(" agentapprove.json already exists in .github/hooks/"));
|
|
@@ -4447,7 +4642,7 @@ async function initRepoCommand() {
|
|
|
4447
4642
|
mkdirSync(targetDir, { recursive: true });
|
|
4448
4643
|
}
|
|
4449
4644
|
copyFileSync(cliHooksPath, targetFile);
|
|
4450
|
-
console.log(source_default.green(` ✓ Copied Copilot CLI hooks to .github/hooks/agentapprove.json`));
|
|
4645
|
+
console.log(source_default.green(` ✓ Copied GitHub Copilot CLI hooks to .github/hooks/agentapprove.json`));
|
|
4451
4646
|
console.log(source_default.dim(` Commit this file so Copilot coding agent uses your hooks.
|
|
4452
4647
|
`));
|
|
4453
4648
|
}
|
|
@@ -4463,7 +4658,7 @@ ${source_default.yellow("Commands:")}
|
|
|
4463
4658
|
${source_default.green("install")} Run the installation wizard (default)
|
|
4464
4659
|
${source_default.green("pair")} Link a new iOS device with existing E2E keys
|
|
4465
4660
|
${source_default.green("refresh")} Generate a new token (when expired)
|
|
4466
|
-
${source_default.green("init-repo")} Add Copilot CLI hooks to current repo (.github/hooks/)
|
|
4661
|
+
${source_default.green("init-repo")} Add GitHub Copilot CLI hooks to current repo (.github/hooks/)
|
|
4467
4662
|
${source_default.green("status")} Show current configuration and installed hooks
|
|
4468
4663
|
${source_default.green("disable")} Temporarily disable hooks
|
|
4469
4664
|
${source_default.green("enable")} Re-enable hooks after disabling
|
|
@@ -4539,6 +4734,7 @@ Session: ${session.sessionCode}`, "Scan with Agent Approve iOS app");
|
|
|
4539
4734
|
failBehavior,
|
|
4540
4735
|
configSetAt
|
|
4541
4736
|
});
|
|
4737
|
+
await storeTokenInKeychain(token);
|
|
4542
4738
|
pushConfigToCloud({
|
|
4543
4739
|
apiUrl,
|
|
4544
4740
|
token,
|
|
@@ -4711,6 +4907,7 @@ ${noteLabel}`, "Scan with Agent Approve iOS app");
|
|
|
4711
4907
|
failBehavior: existingConfig?.failBehavior || "ask",
|
|
4712
4908
|
configSetAt
|
|
4713
4909
|
});
|
|
4910
|
+
await storeTokenInKeychain(result.token);
|
|
4714
4911
|
const hooksDir = join(getAgentApproveDir(), "hooks");
|
|
4715
4912
|
if (existsSync(hooksDir)) {
|
|
4716
4913
|
const updateSpinner = _2();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentapprove",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Approve AI agent actions from your iPhone or Apple Watch",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"dev": "bun --watch src/cli.ts",
|
|
11
11
|
"build": "bun build src/cli.ts --outdir dist --target node",
|
|
12
|
+
"pack:check": "bun run build && node ../../scripts/check-publish-packlist.mjs",
|
|
12
13
|
"start": "bun src/cli.ts",
|
|
13
14
|
"test": "bun test",
|
|
14
|
-
"prepublishOnly": "bun run
|
|
15
|
+
"prepublishOnly": "bun run pack:check"
|
|
15
16
|
},
|
|
16
17
|
"files": [
|
|
17
18
|
"dist/**/*"
|
|
@@ -43,13 +44,14 @@
|
|
|
43
44
|
"node": ">=18"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@clack/prompts": "
|
|
47
|
-
"chalk": "
|
|
48
|
-
"qrcode-terminal": "
|
|
47
|
+
"@clack/prompts": "0.8.2",
|
|
48
|
+
"chalk": "5.6.2",
|
|
49
|
+
"qrcode-terminal": "0.12.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@
|
|
52
|
-
"@types/
|
|
53
|
-
"
|
|
52
|
+
"@socketsecurity/bun-security-scanner": "1.1.2",
|
|
53
|
+
"@types/node": "22.19.3",
|
|
54
|
+
"@types/qrcode-terminal": "0.12.2",
|
|
55
|
+
"typescript": "5.9.3"
|
|
54
56
|
}
|
|
55
57
|
}
|