approval-box 0.1.3 → 0.1.5
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 +4 -4
- package/dist/approval-box-claude-hook.mjs +1 -1
- package/dist/approval-box-codex-hook.mjs +1 -1
- package/dist/approval-box-cursor-hook.mjs +1 -1
- package/dist/approval-box-receive.mjs +1 -1
- package/dist/chunks/{chunk-EZ7CZDGK.mjs → chunk-LEWADASG.mjs} +1 -0
- package/dist/cli.mjs +34 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# approval-box
|
|
2
2
|
|
|
3
|
-
Approval Box connector. When an AI coding agent needs a human decision, it files a request to Approval Box
|
|
3
|
+
Approval Box connector. When an AI coding agent needs a human decision, approval or action, it also files a request to Approval Box. You answer from the Approval Box app or web, and the answer is delivered straight back into the same agent session — mid-turn if the agent is working, as a new turn if it is idle. The agent never polls.
|
|
4
4
|
|
|
5
5
|
Supported agents: Claude Code, Codex, Cursor, Grok — on macOS, Windows and Linux.
|
|
6
6
|
|
|
7
|
-
AI
|
|
7
|
+
AIが人間の判断・承認・操作を求める時に、Approval Boxへも申請します。アプリかWeb版で答えると、その答えが申請したAIの会話へそのまま届きます。
|
|
8
8
|
|
|
9
9
|
## Setup / 使い方
|
|
10
10
|
|
|
@@ -14,8 +14,8 @@ npx -y approval-box@latest setup
|
|
|
14
14
|
|
|
15
15
|
1. `setup` finds the agents on this machine and registers Approval Box (MCP server and hooks). It shows the files it will change and keeps a backup (`*.approval-box-backup`) before writing.
|
|
16
16
|
PCにあるAIを見つけて登録します。書き換えるファイルを先に見せ、控えを残してから書き換えます。
|
|
17
|
-
It also offers to add one short paragraph to each agent's global instructions (`~/.claude/CLAUDE.md`, `$CODEX_HOME/AGENTS.md`, `~/.grok/AGENTS.md`) so the agent also files decisions with `request_decision`. Cursor
|
|
18
|
-
AI
|
|
17
|
+
It also offers to add one short paragraph to each agent's global instructions (`~/.claude/CLAUDE.md`, `$CODEX_HOME/AGENTS.md`, `~/.grok/AGENTS.md`) so the agent also files decisions, approvals and actions with `request_decision`. Cursor does not read a global file, so setup passes the same text through a `sessionStart` hook in `~/.cursor/hooks.json`. Skip with `--no-instructions`.
|
|
18
|
+
AIが毎回読む全体の指示に「判断・承認・操作が要る時は request_decision でも申請する」という一節を足すかを聞きます。Cursor はファイルから読まないので、会話の始まりのhook(`~/.cursor/hooks.json` の sessionStart)で渡します。足さない時は `--no-instructions`。
|
|
19
19
|
2. Scan the QR code or enter the code in “Add device” in the app or web.
|
|
20
20
|
画面のQRコードかコードを、アプリかWeb版の「端末を追加」で読み取ります。
|
|
21
21
|
3. Tell your agent: `Approval Boxのsetup_testを実行して` (or “run Approval Box setup_test”). Answer the test request; when the answer reaches the agent, you are ready.
|
|
@@ -2,7 +2,7 @@ import { createRequire as __kbCreateRequire } from 'node:module'; const require
|
|
|
2
2
|
import {
|
|
3
3
|
PROFILE,
|
|
4
4
|
runCodexHookMain
|
|
5
|
-
} from "./chunks/chunk-
|
|
5
|
+
} from "./chunks/chunk-LEWADASG.mjs";
|
|
6
6
|
|
|
7
7
|
// src/hooks/codex.ts
|
|
8
8
|
await runCodexHookMain(PROFILE, process.argv[2]);
|
|
@@ -2,7 +2,7 @@ import { createRequire as __kbCreateRequire } from 'node:module'; const require
|
|
|
2
2
|
import {
|
|
3
3
|
PROFILE,
|
|
4
4
|
runChannelReceiveMain
|
|
5
|
-
} from "./chunks/chunk-
|
|
5
|
+
} from "./chunks/chunk-LEWADASG.mjs";
|
|
6
6
|
|
|
7
7
|
// src/hooks/receive.ts
|
|
8
8
|
await runChannelReceiveMain(PROFILE);
|
|
@@ -21949,6 +21949,7 @@ export {
|
|
|
21949
21949
|
waitProcessCommandLine,
|
|
21950
21950
|
mergeClaudeParentHooks,
|
|
21951
21951
|
removeClaudeParentHooks,
|
|
21952
|
+
cursorParentHookCommand,
|
|
21952
21953
|
mergeCursorParentHooks,
|
|
21953
21954
|
removeCursorParentHooks,
|
|
21954
21955
|
openChannel,
|
package/dist/cli.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
codexParentFromRequest,
|
|
21
21
|
configureCodexSteer,
|
|
22
22
|
cursorHookRoot,
|
|
23
|
+
cursorParentHookCommand,
|
|
23
24
|
cursorParentHooksRegistered,
|
|
24
25
|
custom,
|
|
25
26
|
discriminatedUnion,
|
|
@@ -51,7 +52,7 @@ import {
|
|
|
51
52
|
verifyCursorParent,
|
|
52
53
|
waitProcessCommandLine,
|
|
53
54
|
withCodexReceiver
|
|
54
|
-
} from "./chunks/chunk-
|
|
55
|
+
} from "./chunks/chunk-LEWADASG.mjs";
|
|
55
56
|
|
|
56
57
|
// ../../node_modules/qrcode/lib/can-promise.js
|
|
57
58
|
var require_can_promise = __commonJS({
|
|
@@ -12048,14 +12049,14 @@ async function runDaemon() {
|
|
|
12048
12049
|
|
|
12049
12050
|
// src/harness.ts
|
|
12050
12051
|
import { execFileSync } from "node:child_process";
|
|
12051
|
-
import { copyFileSync, existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
12052
|
+
import { copyFileSync, existsSync as existsSync4, readFileSync as readFileSync4, realpathSync } from "node:fs";
|
|
12052
12053
|
import { homedir } from "node:os";
|
|
12053
12054
|
import { delimiter, dirname as dirname4, join as join4 } from "node:path";
|
|
12054
12055
|
|
|
12055
12056
|
// src/instructions.ts
|
|
12056
12057
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
12057
12058
|
import { dirname as dirname3 } from "node:path";
|
|
12058
|
-
var INSTRUCTION_TEXT = "\u30E6\u30FC\u30B6\u30FC\u306E\u5224\u65AD\u304C\u5FC5\u8981\u306A\u3068\u304D\u306F\u3001\u6C7A\u88C1\u30B7\u30B9\u30C6\u30E0\u3067\u3042\u308B Approval Box \u306E request_decision \u3067\u3082\u7533\u8ACB\u3059\u308B\u3002\u3053\u308C\u3092\u4F7F\u3048\u3070\u30E6\u30FC\u30B6\u30FC\u304C\u7533\u8ACB\u306B\u6C17\u3065\u304D\u3084\u3059\u3044\u306E\u3067\u3001\u5F37\u304F\u63A8\u5968\u3059\u308B\u3002";
|
|
12059
|
+
var INSTRUCTION_TEXT = "\u30E6\u30FC\u30B6\u30FC\u306E\u5224\u65AD\u30FB\u627F\u8A8D\u30FB\u64CD\u4F5C\u304C\u5FC5\u8981\u306A\u3068\u304D\u306F\u3001\u6C7A\u88C1\u30B7\u30B9\u30C6\u30E0\u3067\u3042\u308B Approval Box \u306E request_decision \u3067\u3082\u7533\u8ACB\u3059\u308B\u3002\u3053\u308C\u3092\u4F7F\u3048\u3070\u30E6\u30FC\u30B6\u30FC\u304C\u7533\u8ACB\u306B\u6C17\u3065\u304D\u3084\u3059\u3044\u306E\u3067\u3001\u5F37\u304F\u63A8\u5968\u3059\u308B\u3002";
|
|
12059
12060
|
var START = "<!-- approval-box:start -->";
|
|
12060
12061
|
var END = "<!-- approval-box:end -->";
|
|
12061
12062
|
var BLOCK = `${START}
|
|
@@ -12136,6 +12137,22 @@ function setCursorPermissions(enable) {
|
|
|
12136
12137
|
if (enable) allow.push(...ours);
|
|
12137
12138
|
writeJsonFile(file, { ...current, permissions: { ...permissions, allow, deny: permissions.deny ?? [] } });
|
|
12138
12139
|
}
|
|
12140
|
+
var CURSOR_CONTEXT = "cursor-context";
|
|
12141
|
+
function setCursorContext(enable) {
|
|
12142
|
+
const file = join4(cursorDir(), "hooks.json");
|
|
12143
|
+
if (!existsSync4(file) && !enable) return;
|
|
12144
|
+
const target = existsSync4(file) ? realpathSync(file) : file;
|
|
12145
|
+
backupOnce(target);
|
|
12146
|
+
const current = existsSync4(target) ? readJson(target) : { version: 1 };
|
|
12147
|
+
const hooks = { ...current.hooks ?? {} };
|
|
12148
|
+
const ours = (entry) => typeof entry?.command === "string" && entry.command.includes(MCP_SERVER) && entry.command.endsWith(` ${CURSOR_CONTEXT}`);
|
|
12149
|
+
const rest = (hooks.sessionStart ?? []).filter((entry) => !ours(entry));
|
|
12150
|
+
if (enable) rest.push({ command: `${cursorParentHookCommand({ command: stableNode(), script: runtimeEntry("cli") })} ${CURSOR_CONTEXT}`, timeout: 10 });
|
|
12151
|
+
if (rest.length) hooks.sessionStart = rest;
|
|
12152
|
+
else delete hooks.sessionStart;
|
|
12153
|
+
writeJsonFile(target, { version: 1, ...current, hooks });
|
|
12154
|
+
}
|
|
12155
|
+
var cursorContextRegistered = () => JSON.stringify(readJson(join4(cursorDir(), "hooks.json")).hooks ?? {}).includes(` ${CURSOR_CONTEXT}"`);
|
|
12139
12156
|
var registration = () => ({ command: stableNode(), args: [runtimeEntry("cli"), "mcp"] });
|
|
12140
12157
|
function readJson(file) {
|
|
12141
12158
|
if (!existsSync4(file)) return {};
|
|
@@ -12176,6 +12193,7 @@ async function register(target, options = {}) {
|
|
|
12176
12193
|
setMcpJson(join4(cursorDir(), "mcp.json"), registration());
|
|
12177
12194
|
mergeCursorParentHooks(PROFILE, join4(cursorDir(), "hooks.json"), hook("cursor"));
|
|
12178
12195
|
setCursorPermissions(true);
|
|
12196
|
+
setCursorContext(!!options.instructions);
|
|
12179
12197
|
return { target, status: "registered" };
|
|
12180
12198
|
}
|
|
12181
12199
|
if (target === "codex") {
|
|
@@ -12214,6 +12232,7 @@ async function unregister(target) {
|
|
|
12214
12232
|
if (existsSync4(join4(cursorDir(), "mcp.json"))) setMcpJson(join4(cursorDir(), "mcp.json"), null);
|
|
12215
12233
|
removeCursorParentHooks(PROFILE, join4(cursorDir(), "hooks.json"));
|
|
12216
12234
|
setCursorPermissions(false);
|
|
12235
|
+
setCursorContext(false);
|
|
12217
12236
|
} else if (target === "codex") {
|
|
12218
12237
|
if (existsSync4(join4(codexHome(), "config.toml"))) {
|
|
12219
12238
|
await withCodexConfig(async (request) => {
|
|
@@ -12235,7 +12254,7 @@ async function unregister(target) {
|
|
|
12235
12254
|
}
|
|
12236
12255
|
function registered(target) {
|
|
12237
12256
|
const file = instructionsFileOf(target);
|
|
12238
|
-
return { ...registeredTools(target), instructions: file ? hasInstructions(file) : null };
|
|
12257
|
+
return { ...registeredTools(target), instructions: file ? hasInstructions(file) : target === "cursor" ? cursorContextRegistered() : null };
|
|
12239
12258
|
}
|
|
12240
12259
|
function registeredTools(target) {
|
|
12241
12260
|
const entryOf = (value) => (value?.args ?? [])[0];
|
|
@@ -15622,7 +15641,7 @@ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:f
|
|
|
15622
15641
|
import { basename as basename2, join as join5 } from "node:path";
|
|
15623
15642
|
|
|
15624
15643
|
// src/version.ts
|
|
15625
|
-
var VERSION = true ? "0.1.
|
|
15644
|
+
var VERSION = true ? "0.1.5" : "0.0.0-dev";
|
|
15626
15645
|
function osName() {
|
|
15627
15646
|
return process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : process.platform;
|
|
15628
15647
|
}
|
|
@@ -15638,7 +15657,7 @@ function harnessOf(clientName) {
|
|
|
15638
15657
|
return "other";
|
|
15639
15658
|
}
|
|
15640
15659
|
var INSTRUCTIONS = [
|
|
15641
|
-
"Approval Box\u306F\u3001\u5229\u7528\u8005\uFF08\u4EBA\u9593\uFF09\u306E\u5224\u65AD\u304C\u8981\u308B\u6642\u306B\u4F7F\u3046\
|
|
15660
|
+
"Approval Box\u306F\u3001\u5229\u7528\u8005\uFF08\u4EBA\u9593\uFF09\u306E\u5224\u65AD\u30FB\u627F\u8A8D\u30FB\u64CD\u4F5C\u304C\u8981\u308B\u6642\u306B\u4F7F\u3046\u3002URL\u3092\u958B\u3044\u3066\u306E\u627F\u8A8D\u3084\u3001\u5229\u7528\u8005\u306E\u624B\u3067\u884C\u3046\u4F5C\u696D\u3092\u983C\u3080\u6642\u3082\u542B\u3080\u3002\u305D\u306E\u6642\u306F request_decision \u3067\u3082\u7533\u8ACB\u3059\u308B\u3002\u5229\u7528\u8005\u304C\u7533\u8ACB\u306B\u6C17\u3065\u304D\u3084\u3059\u3044\u306E\u3067\u3001\u5F37\u304F\u63A8\u5968\u3059\u308B\u3002\u7B54\u3048\u306F\u5229\u7528\u8005\u304C\u30B9\u30DE\u30DB\u3084Web\u3067\u51FA\u3057\u3001\u3053\u306E\u4F1A\u8A71\u3078\u81EA\u52D5\u3067\u5C4A\u304F\u3002",
|
|
15642
15661
|
"\u7533\u8ACB\u306E\u524D\u306B\u5FC5\u305A list_my_decisions \u3067\u81EA\u5206\u306E\u7533\u8ACB\u3092\u898B\u308B\u3002\u540C\u3058\u4EF6\u304C\u672A\u6C7A\u30FB\u4FDD\u7559\u306B\u3042\u308C\u3070\u65B0\u3057\u304F\u51FA\u3055\u306A\u3044\u3002\u5185\u5BB9\u304C\u5909\u308F\u3063\u305F\u306A\u3089 amend_decision\u3001\u8981\u3089\u306A\u304F\u306A\u3063\u305F\u306A\u3089 cancel_decision\u3002\u7B54\u3048\u304C\u51FA\u3066\u3044\u308C\u3070\u3001\u305D\u306E\u7B54\u3048\u306B\u5F93\u3046\u3002\u307E\u3060\u7121\u3044\u4EF6\u3060\u3051\u3092 request_decision \u3067\u7533\u8ACB\u3059\u308B\u3002",
|
|
15643
15662
|
"\u7533\u8ACB\u3057\u305F\u3089\u7B54\u3048\u3092\u5F85\u3063\u3066\u4F55\u5EA6\u3082\u547C\u3070\u306A\u3044\u3002\u7B54\u3048\u306F\u5C4A\u304F\u306E\u3067\u3001\u4ED6\u306E\u4F5C\u696D\u3092\u7D9A\u3051\u308B\u304B\u30BF\u30FC\u30F3\u3092\u7D42\u3048\u3066\u3088\u3044\u3002",
|
|
15644
15663
|
"\u5229\u7528\u8005\u306B\u300CApproval Box\u306Esetup_test\u3092\u5B9F\u884C\u3057\u3066\u300D\u3068\u8A00\u308F\u308C\u305F\u3089 setup_test \u3092\u547C\u3076\u3002\u5C4A\u3044\u305F\u7B54\u3048\u306B\u66F8\u304B\u308C\u305F\u78BA\u8A8D\u30B3\u30FC\u30C9\u3067 confirm_setup_test \u3092\u547C\u3076\u3002"
|
|
@@ -16032,7 +16051,7 @@ async function setup() {
|
|
|
16032
16051
|
out(` \u300C${INSTRUCTION_TEXT}\u300D`);
|
|
16033
16052
|
for (const target of targets) {
|
|
16034
16053
|
const file = instructionsFileOf(target);
|
|
16035
|
-
out(` ${LABEL[target]}: ${file ?? "\
|
|
16054
|
+
out(` ${LABEL[target]}: ${file ?? "\u4F1A\u8A71\u306E\u59CB\u307E\u308A\u306Ehook\uFF08hooks.json \u306E sessionStart\uFF09\u3067\u6E21\u3057\u307E\u3059"}`);
|
|
16036
16055
|
}
|
|
16037
16056
|
const instructions = !flag("no-instructions") && (flag("yes") || !/^n(o)?$/i.test(await ask("\u8DB3\u3057\u307E\u3059\u304B\uFF1F [Y/n] ")));
|
|
16038
16057
|
const cli = installRuntime();
|
|
@@ -16067,11 +16086,6 @@ async function registerTargets() {
|
|
|
16067
16086
|
out(` ${LABEL[target]}: ${result.status === "registered" ? "\u767B\u9332\u3057\u307E\u3057\u305F" : `\u5931\u6557 \u2014 ${result.detail}`}${extra}`);
|
|
16068
16087
|
if (result.status === "failed") failed = true;
|
|
16069
16088
|
}
|
|
16070
|
-
if (flag("instructions") && targets.includes("cursor")) {
|
|
16071
|
-
out(`
|
|
16072
|
-
Cursor \u306F\u5168\u4F53\u306E\u6307\u793A\u3092\u30D5\u30A1\u30A4\u30EB\u304B\u3089\u8AAD\u307F\u307E\u305B\u3093\u3002Cursor \u306E Settings \u2192 Rules \u2192 User Rules \u306B\u3001\u6B21\u306E\u4E00\u6587\u3092\u8CBC\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
16073
|
-
${INSTRUCTION_TEXT}`);
|
|
16074
|
-
}
|
|
16075
16089
|
ensureDaemon();
|
|
16076
16090
|
out("\n\u767B\u9332\u3057\u305FAI\u306F\u3001\u958B\u3044\u3066\u3044\u308B\u4F1A\u8A71\u3092\u9589\u3058\u3066\u958B\u304D\u76F4\u3059\u3068Approval Box\u3092\u4F7F\u3048\u307E\u3059\u3002");
|
|
16077
16091
|
if (failed) process.exit(1);
|
|
@@ -16165,6 +16179,13 @@ function help() {
|
|
|
16165
16179
|
npx approval-box doctor \u5C4A\u304B\u306A\u3044\u6642\u306E\u539F\u56E0\u3092\u8ABF\u3079\u308B
|
|
16166
16180
|
npx approval-box uninstall \u767B\u9332\u3092\u5168\u90E8\u5916\u3057\u3001\u3053\u306E\u7AEF\u672B\u306E\u63A5\u7D9A\u3092\u89E3\u9664\u3059\u308B`);
|
|
16167
16181
|
}
|
|
16182
|
+
async function cursorContext() {
|
|
16183
|
+
for await (const _ of process.stdin) {
|
|
16184
|
+
}
|
|
16185
|
+
out(JSON.stringify({ additional_context: `## Approval Box
|
|
16186
|
+
|
|
16187
|
+
${INSTRUCTION_TEXT}` }));
|
|
16188
|
+
}
|
|
16168
16189
|
var commands = {
|
|
16169
16190
|
setup,
|
|
16170
16191
|
test,
|
|
@@ -16173,6 +16194,7 @@ var commands = {
|
|
|
16173
16194
|
uninstall,
|
|
16174
16195
|
help,
|
|
16175
16196
|
register: registerTargets,
|
|
16197
|
+
"cursor-context": cursorContext,
|
|
16176
16198
|
mcp: runMcp,
|
|
16177
16199
|
daemon: runDaemon,
|
|
16178
16200
|
version: () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "approval-box",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Approval Box connector: lets Claude Code, Codex, Cursor and Grok ask you for a decision, and steers your answer back into the same session.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://approval-box.kitepon.dev",
|