approval-box 0.1.9 → 0.1.11

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 -2
  2. package/dist/cli.mjs +137 -73
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -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, 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`。
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`. If an instructions file is a symlink (for example, generated by a dotfiles repo), setup leaves it alone and asks you to add the paragraph to the source instead. Skip with `--no-instructions`.
18
+ AIが毎回読む全体の指示に「判断・承認・操作が要る時は request_decision でも申請する」という一節を足すかを聞きます。Cursor はファイルから読まないので、会話の始まりのhook(`~/.cursor/hooks.json` の sessionStart)で渡します。指示ファイルが別の所への参照(symlink、dotfilesの生成物など)の時は書かず、管理元の規範に足すよう案内します。足さない時は `--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.
package/dist/cli.mjs CHANGED
@@ -4435,10 +4435,10 @@ var require_browser = __commonJS({
4435
4435
  text2 = canvas;
4436
4436
  canvas = void 0;
4437
4437
  }
4438
- return new Promise(function(resolve, reject) {
4438
+ return new Promise(function(resolve2, reject) {
4439
4439
  try {
4440
4440
  const data = QRCode2.create(text2, opts);
4441
- resolve(renderFunc(data, canvas, opts));
4441
+ resolve2(renderFunc(data, canvas, opts));
4442
4442
  } catch (e) {
4443
4443
  reject(e);
4444
4444
  }
@@ -4519,11 +4519,11 @@ var require_server = __commonJS({
4519
4519
  }
4520
4520
  function render(renderFunc, text2, params) {
4521
4521
  if (!params.cb) {
4522
- return new Promise(function(resolve, reject) {
4522
+ return new Promise(function(resolve2, reject) {
4523
4523
  try {
4524
4524
  const data = QRCode2.create(text2, params.opts);
4525
4525
  return renderFunc(data, params.opts, function(err, data2) {
4526
- return err ? reject(err) : resolve(data2);
4526
+ return err ? reject(err) : resolve2(data2);
4527
4527
  });
4528
4528
  } catch (e) {
4529
4529
  reject(e);
@@ -7538,7 +7538,7 @@ var require_compile = __commonJS({
7538
7538
  const schOrFunc = root.refs[ref];
7539
7539
  if (schOrFunc)
7540
7540
  return schOrFunc;
7541
- let _sch = resolve.call(this, root, ref);
7541
+ let _sch = resolve2.call(this, root, ref);
7542
7542
  if (_sch === void 0) {
7543
7543
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
7544
7544
  const { schemaId } = this.opts;
@@ -7565,7 +7565,7 @@ var require_compile = __commonJS({
7565
7565
  function sameSchemaEnv(s1, s2) {
7566
7566
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
7567
7567
  }
7568
- function resolve(root, ref) {
7568
+ function resolve2(root, ref) {
7569
7569
  let sch;
7570
7570
  while (typeof (sch = this.refs[ref]) == "string")
7571
7571
  ref = sch;
@@ -8395,7 +8395,7 @@ var require_fast_uri = __commonJS({
8395
8395
  }
8396
8396
  return uri;
8397
8397
  }
8398
- function resolve(baseURI, relativeURI, options) {
8398
+ function resolve2(baseURI, relativeURI, options) {
8399
8399
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
8400
8400
  const {
8401
8401
  parsed: baseParsed,
@@ -8764,7 +8764,7 @@ var require_fast_uri = __commonJS({
8764
8764
  var fastUri = {
8765
8765
  SCHEMES,
8766
8766
  normalize,
8767
- resolve,
8767
+ resolve: resolve2,
8768
8768
  resolveComponent,
8769
8769
  equal,
8770
8770
  serialize,
@@ -11757,7 +11757,7 @@ var require_dist = __commonJS({
11757
11757
  var import_qrcode = __toESM(require_lib(), 1);
11758
11758
  import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync5, rmSync as rmSync3 } from "node:fs";
11759
11759
  import { hostname } from "node:os";
11760
- import { join as join7 } from "node:path";
11760
+ import { join as join8 } from "node:path";
11761
11761
  import { createInterface } from "node:readline/promises";
11762
11762
 
11763
11763
  // src/api.ts
@@ -11948,11 +11948,23 @@ function daemonStatus() {
11948
11948
  return { running: false };
11949
11949
  }
11950
11950
  }
11951
+ function versionOfEntry(entry) {
11952
+ const match = /[\\/]runtime[\\/](\d+)\.(\d+)\.(\d+)[\\/]/.exec(entry);
11953
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
11954
+ }
11955
+ function shouldReplaceDaemon(runningEntry, myEntry) {
11956
+ if (runningEntry === myEntry) return false;
11957
+ const running = runningEntry ? versionOfEntry(runningEntry) : null;
11958
+ const mine = versionOfEntry(myEntry);
11959
+ if (!running || !mine) return true;
11960
+ for (let i = 0; i < 3; i++) if (running[i] !== mine[i]) return running[i] < mine[i];
11961
+ return true;
11962
+ }
11951
11963
  function ensureDaemon() {
11952
11964
  try {
11953
11965
  if (!readConfig()?.token) return;
11954
11966
  const status2 = daemonStatus();
11955
- if (status2.running && status2.entry === runtimeEntry("cli")) return;
11967
+ if (status2.running && !shouldReplaceDaemon(status2.entry, runtimeEntry("cli"))) return;
11956
11968
  if (status2.running && status2.pid) {
11957
11969
  try {
11958
11970
  process.kill(status2.pid);
@@ -11967,8 +11979,13 @@ function ensureDaemon() {
11967
11979
  `);
11968
11980
  }
11969
11981
  }
11970
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
11982
+ var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
11971
11983
  async function runDaemon() {
11984
+ for (let i = 0; i < 50; i++) {
11985
+ const other = daemonStatus();
11986
+ if (!other.running || other.pid === process.pid) break;
11987
+ await sleep(200);
11988
+ }
11972
11989
  const status2 = daemonStatus();
11973
11990
  if (status2.running && status2.pid !== process.pid) return;
11974
11991
  writeJsonFile(lockFile(), { pid: process.pid, entry: runtimeEntry("cli"), started_at: (/* @__PURE__ */ new Date()).toISOString() });
@@ -12071,8 +12088,16 @@ async function runDaemon() {
12071
12088
  }
12072
12089
  const controller = new AbortController();
12073
12090
  let stopping = false;
12091
+ const releaseLock = () => {
12092
+ try {
12093
+ const lock = JSON.parse(readFileSync3(lockFile(), "utf8"));
12094
+ if (lock.pid === process.pid) rmSync2(lockFile(), { force: true });
12095
+ } catch {
12096
+ }
12097
+ };
12074
12098
  const stop = () => {
12075
12099
  stopping = true;
12100
+ releaseLock();
12076
12101
  controller.abort();
12077
12102
  };
12078
12103
  process.on("SIGTERM", stop);
@@ -12110,23 +12135,20 @@ async function runDaemon() {
12110
12135
  }
12111
12136
  }
12112
12137
  }
12113
- try {
12114
- const lock = JSON.parse(readFileSync3(lockFile(), "utf8"));
12115
- if (lock.pid === process.pid) rmSync2(lockFile(), { force: true });
12116
- } catch {
12117
- }
12138
+ releaseLock();
12118
12139
  process.exit(0);
12119
12140
  }
12120
12141
 
12121
12142
  // src/harness.ts
12122
12143
  import { execFileSync } from "node:child_process";
12123
12144
  import { copyFileSync, existsSync as existsSync5, readFileSync as readFileSync5, realpathSync } from "node:fs";
12124
- import { homedir } from "node:os";
12125
- import { delimiter, dirname as dirname4, join as join5 } from "node:path";
12145
+ import { homedir as homedir2 } from "node:os";
12146
+ import { delimiter, dirname as dirname4, join as join6 } from "node:path";
12126
12147
 
12127
12148
  // src/instructions.ts
12128
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
12129
- import { dirname as dirname3 } from "node:path";
12149
+ import { existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, readlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
12150
+ import { homedir } from "node:os";
12151
+ import { dirname as dirname3, join as join5, resolve } from "node:path";
12130
12152
  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";
12131
12153
  var START = "<!-- approval-box:start -->";
12132
12154
  var END = "<!-- approval-box:end -->";
@@ -12137,18 +12159,48 @@ ${INSTRUCTION_TEXT}
12137
12159
  ${END}
12138
12160
  `;
12139
12161
  var PATTERN = new RegExp(`\\n*${START}[\\s\\S]*?${END}\\n?`, "g");
12162
+ var SAME_MEANING = /Approval Box\s*の\s*`?request_decision`?\s*でも申請/;
12140
12163
  function hasInstructions(file) {
12141
- return existsSync4(file) && readFileSync4(file, "utf8").includes(START);
12164
+ if (!existsSync4(file)) return false;
12165
+ const text2 = readFileSync4(file, "utf8");
12166
+ if (text2.includes(START) || SAME_MEANING.test(text2)) return true;
12167
+ for (const [, ref] of text2.matchAll(/^@(\S+)\s*$/gm)) {
12168
+ const target = ref.startsWith("~/") ? resolve(homedir(), ref.slice(2)) : resolve(dirname3(file), ref);
12169
+ if (existsSync4(target) && SAME_MEANING.test(readFileSync4(target, "utf8"))) return true;
12170
+ }
12171
+ return false;
12172
+ }
12173
+ function hasInstructionsInDir(dir) {
12174
+ try {
12175
+ return readdirSync2(dir).some((name) => {
12176
+ try {
12177
+ return SAME_MEANING.test(readFileSync4(join5(dir, name), "utf8"));
12178
+ } catch {
12179
+ return false;
12180
+ }
12181
+ });
12182
+ } catch {
12183
+ return false;
12184
+ }
12185
+ }
12186
+ function managedElsewhere(file) {
12187
+ try {
12188
+ return lstatSync(file).isSymbolicLink() ? readlinkSync(file) : null;
12189
+ } catch {
12190
+ return null;
12191
+ }
12142
12192
  }
12143
12193
  function writeInstructions(file) {
12194
+ if (managedElsewhere(file)) return "managed_elsewhere";
12144
12195
  const current = existsSync4(file) ? readFileSync4(file, "utf8").replace(PATTERN, "\n").trimEnd() : "";
12145
12196
  mkdirSync4(dirname3(file), { recursive: true, mode: 448 });
12146
12197
  writeFileSync3(file, current ? `${current}
12147
12198
 
12148
12199
  ${BLOCK}` : BLOCK);
12200
+ return "written";
12149
12201
  }
12150
12202
  function removeInstructions(file) {
12151
- if (!hasInstructions(file)) return;
12203
+ if (!existsSync4(file) || !readFileSync4(file, "utf8").includes(START)) return;
12152
12204
  const rest = readFileSync4(file, "utf8").replace(PATTERN, "\n").trimEnd();
12153
12205
  writeFileSync3(file, rest ? `${rest}
12154
12206
  ` : "");
@@ -12158,27 +12210,27 @@ function removeInstructions(file) {
12158
12210
  var HARNESSES = ["claude", "codex", "cursor", "grok"];
12159
12211
  var LABEL = { claude: "Claude Code", codex: "Codex", cursor: "Cursor", grok: "Grok" };
12160
12212
  var CLIENT = { claude: "claude-code", codex: "codex", cursor: "cursor", grok: "grok" };
12161
- var claudeDir = () => process.env.CLAUDE_CONFIG_DIR ?? join5(homedir(), ".claude");
12162
- var claudeJson = () => process.env.CLAUDE_CONFIG_DIR ? join5(process.env.CLAUDE_CONFIG_DIR, ".claude.json") : join5(homedir(), ".claude.json");
12163
- var cursorDir = () => process.env.CURSOR_HOME ?? join5(homedir(), ".cursor");
12164
- var codexHome = () => process.env.CODEX_HOME ?? join5(homedir(), ".codex");
12165
- var grokDir = () => process.env.GROK_HOME ?? join5(homedir(), ".grok");
12213
+ var claudeDir = () => process.env.CLAUDE_CONFIG_DIR ?? join6(homedir2(), ".claude");
12214
+ var claudeJson = () => process.env.CLAUDE_CONFIG_DIR ? join6(process.env.CLAUDE_CONFIG_DIR, ".claude.json") : join6(homedir2(), ".claude.json");
12215
+ var cursorDir = () => process.env.CURSOR_HOME ?? join6(homedir2(), ".cursor");
12216
+ var codexHome = () => process.env.CODEX_HOME ?? join6(homedir2(), ".codex");
12217
+ var grokDir = () => process.env.GROK_HOME ?? join6(homedir2(), ".grok");
12166
12218
  function filesOf(target) {
12167
- if (target === "claude") return [claudeJson(), join5(claudeDir(), "settings.json")];
12168
- if (target === "cursor") return [join5(cursorDir(), "mcp.json"), join5(cursorDir(), "hooks.json"), cursorCliConfig()];
12169
- if (target === "codex") return [join5(codexHome(), "config.toml"), join5(codexHome(), "hooks.json")];
12170
- return [join5(grokDir(), "config.toml")];
12219
+ if (target === "claude") return [claudeJson(), join6(claudeDir(), "settings.json")];
12220
+ if (target === "cursor") return [join6(cursorDir(), "mcp.json"), join6(cursorDir(), "hooks.json"), cursorCliConfig()];
12221
+ if (target === "codex") return [join6(codexHome(), "config.toml"), join6(codexHome(), "hooks.json")];
12222
+ return [join6(grokDir(), "config.toml")];
12171
12223
  }
12172
12224
  function instructionsFileOf(target) {
12173
- if (target === "claude") return join5(claudeDir(), "CLAUDE.md");
12174
- if (target === "codex") return join5(codexHome(), "AGENTS.md");
12175
- if (target === "grok") return join5(grokDir(), "AGENTS.md");
12225
+ if (target === "claude") return join6(claudeDir(), "CLAUDE.md");
12226
+ if (target === "codex") return join6(codexHome(), "AGENTS.md");
12227
+ if (target === "grok") return join6(grokDir(), "AGENTS.md");
12176
12228
  return null;
12177
12229
  }
12178
12230
  function onPath(command2) {
12179
12231
  const names = process.platform === "win32" ? [`${command2}.exe`, `${command2}.cmd`, command2] : [command2];
12180
12232
  for (const dir of (process.env.PATH ?? "").split(delimiter)) {
12181
- for (const name of names) if (dir && existsSync5(join5(dir, name))) return join5(dir, name);
12233
+ for (const name of names) if (dir && existsSync5(join6(dir, name))) return join6(dir, name);
12182
12234
  }
12183
12235
  return null;
12184
12236
  }
@@ -12196,7 +12248,7 @@ function stableNode() {
12196
12248
  }
12197
12249
  }
12198
12250
  var CURSOR_TOOLS = ["list_my_decisions", "request_decision", "amend_decision", "cancel_decision", "get_decision", "get_attachment", "setup_test", "confirm_setup_test"];
12199
- var cursorCliConfig = () => join5(cursorDir(), "cli-config.json");
12251
+ var cursorCliConfig = () => join6(cursorDir(), "cli-config.json");
12200
12252
  function setCursorPermissions(enable) {
12201
12253
  const file = cursorCliConfig();
12202
12254
  if (!existsSync5(file) && !enable) return;
@@ -12210,7 +12262,7 @@ function setCursorPermissions(enable) {
12210
12262
  }
12211
12263
  var CURSOR_CONTEXT = "cursor-context";
12212
12264
  function setCursorContext(enable) {
12213
- const file = join5(cursorDir(), "hooks.json");
12265
+ const file = join6(cursorDir(), "hooks.json");
12214
12266
  if (!existsSync5(file) && !enable) return;
12215
12267
  const target = existsSync5(file) ? realpathSync(file) : file;
12216
12268
  backupOnce(target);
@@ -12223,7 +12275,7 @@ function setCursorContext(enable) {
12223
12275
  else delete hooks.sessionStart;
12224
12276
  writeJsonFile(target, { version: 1, ...current, hooks });
12225
12277
  }
12226
- var cursorContextRegistered = () => JSON.stringify(readJson(join5(cursorDir(), "hooks.json")).hooks ?? {}).includes(` ${CURSOR_CONTEXT}"`);
12278
+ var cursorContextRegistered = () => JSON.stringify(readJson(join6(cursorDir(), "hooks.json")).hooks ?? {}).includes(` ${CURSOR_CONTEXT}"`);
12227
12279
  var registration = () => ({ command: stableNode(), args: [runtimeEntry("cli"), "mcp"] });
12228
12280
  function readJson(file) {
12229
12281
  if (!existsSync5(file)) return {};
@@ -12246,29 +12298,34 @@ async function withCodexConfig(fn) {
12246
12298
  }
12247
12299
  async function register(target, options = {}) {
12248
12300
  const file = instructionsFileOf(target);
12301
+ let note;
12249
12302
  if (options.instructions && file) {
12250
12303
  try {
12251
- writeInstructions(file);
12304
+ if (writeInstructions(file) === "managed_elsewhere") note = `\u5168\u4F53\u306E\u6307\u793A\uFF08${file} \u2192 ${managedElsewhere(file)}\uFF09\u306F\u5225\u306E\u6240\u3067\u7BA1\u7406\u3055\u308C\u3066\u3044\u308B\u306E\u3067\u66F8\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u7BA1\u7406\u5143\u306E\u898F\u7BC4\u306B\u4E00\u7BC0\u3092\u8DB3\u3057\u3066\u304F\u3060\u3055\u3044`;
12252
12305
  } catch (error) {
12253
12306
  return { target, status: "failed", detail: `${file}: ${error.message}` };
12254
12307
  }
12255
12308
  }
12309
+ const result = await registerTools(target, options);
12310
+ return note && result.status === "registered" ? { ...result, note } : result;
12311
+ }
12312
+ async function registerTools(target, options) {
12256
12313
  const hook = (kind) => ({ command: stableNode(), script: runtimeEntry(kind) });
12257
12314
  try {
12258
12315
  if (target === "claude") {
12259
12316
  setMcpJson(claudeJson(), { type: "stdio", ...registration() });
12260
- mergeClaudeParentHooks(PROFILE, join5(claudeDir(), "settings.json"), hook("claude"));
12317
+ mergeClaudeParentHooks(PROFILE, join6(claudeDir(), "settings.json"), hook("claude"));
12261
12318
  return { target, status: "registered" };
12262
12319
  }
12263
12320
  if (target === "cursor") {
12264
- setMcpJson(join5(cursorDir(), "mcp.json"), registration());
12265
- mergeCursorParentHooks(PROFILE, join5(cursorDir(), "hooks.json"), hook("cursor"));
12321
+ setMcpJson(join6(cursorDir(), "mcp.json"), registration());
12322
+ mergeCursorParentHooks(PROFILE, join6(cursorDir(), "hooks.json"), hook("cursor"));
12266
12323
  setCursorPermissions(true);
12267
12324
  setCursorContext(!!options.instructions);
12268
12325
  return { target, status: "registered" };
12269
12326
  }
12270
12327
  if (target === "codex") {
12271
- const config = join5(codexHome(), "config.toml");
12328
+ const config = join6(codexHome(), "config.toml");
12272
12329
  backupOnce(config);
12273
12330
  await withCodexConfig(async (request) => {
12274
12331
  await request("config/batchWrite", { filePath: config, edits: [{ keyPath: `mcp_servers.${MCP_SERVER}`, value: { ...registration(), env_vars: ["CODEX_HOME"] }, mergeStrategy: "replace" }] });
@@ -12284,7 +12341,7 @@ async function register(target, options = {}) {
12284
12341
  }
12285
12342
  const grok = onPath("grok");
12286
12343
  if (!grok) throw new Error("grok \u30B3\u30DE\u30F3\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093");
12287
- backupOnce(join5(grokDir(), "config.toml"));
12344
+ backupOnce(join6(grokDir(), "config.toml"));
12288
12345
  const reg = registration();
12289
12346
  execFileSync(grok, ["mcp", "add", MCP_SERVER, "--scope", "user", reg.command, "--", ...reg.args], { stdio: "pipe" });
12290
12347
  return { target, status: "registered" };
@@ -12298,16 +12355,16 @@ async function unregister(target) {
12298
12355
  if (file) removeInstructions(file);
12299
12356
  if (target === "claude") {
12300
12357
  if (existsSync5(claudeJson())) setMcpJson(claudeJson(), null);
12301
- removeClaudeParentHooks(PROFILE, join5(claudeDir(), "settings.json"));
12358
+ removeClaudeParentHooks(PROFILE, join6(claudeDir(), "settings.json"));
12302
12359
  } else if (target === "cursor") {
12303
- if (existsSync5(join5(cursorDir(), "mcp.json"))) setMcpJson(join5(cursorDir(), "mcp.json"), null);
12304
- removeCursorParentHooks(PROFILE, join5(cursorDir(), "hooks.json"));
12360
+ if (existsSync5(join6(cursorDir(), "mcp.json"))) setMcpJson(join6(cursorDir(), "mcp.json"), null);
12361
+ removeCursorParentHooks(PROFILE, join6(cursorDir(), "hooks.json"));
12305
12362
  setCursorPermissions(false);
12306
12363
  setCursorContext(false);
12307
12364
  } else if (target === "codex") {
12308
- if (existsSync5(join5(codexHome(), "config.toml"))) {
12365
+ if (existsSync5(join6(codexHome(), "config.toml"))) {
12309
12366
  await withCodexConfig(async (request) => {
12310
- await request("config/batchWrite", { filePath: join5(codexHome(), "config.toml"), edits: [{ keyPath: `mcp_servers.${MCP_SERVER}`, value: null, mergeStrategy: "replace" }] });
12367
+ await request("config/batchWrite", { filePath: join6(codexHome(), "config.toml"), edits: [{ keyPath: `mcp_servers.${MCP_SERVER}`, value: null, mergeStrategy: "replace" }] });
12311
12368
  });
12312
12369
  }
12313
12370
  try {
@@ -12325,26 +12382,27 @@ async function unregister(target) {
12325
12382
  }
12326
12383
  function registered(target) {
12327
12384
  const file = instructionsFileOf(target);
12328
- return { ...registeredTools(target), instructions: file ? hasInstructions(file) : target === "cursor" ? cursorContextRegistered() : null };
12385
+ const instructions = target === "cursor" ? cursorContextRegistered() || hasInstructions(join6(cursorDir(), "AGENTS.md")) || hasInstructionsInDir(join6(cursorDir(), "rules")) : file ? hasInstructions(file) || target === "grok" && hasInstructionsInDir(join6(grokDir(), "rules")) : null;
12386
+ return { ...registeredTools(target), instructions };
12329
12387
  }
12330
12388
  function registeredTools(target) {
12331
12389
  const entryOf = (value) => (value?.args ?? [])[0];
12332
12390
  if (target === "claude") {
12333
12391
  const entry = readJson(claudeJson()).mcpServers?.[MCP_SERVER];
12334
- const settings = JSON.stringify(readJson(join5(claudeDir(), "settings.json")));
12392
+ const settings = JSON.stringify(readJson(join6(claudeDir(), "settings.json")));
12335
12393
  const e = entryOf(entry);
12336
12394
  return { mcp: !!entry, hooks: settings.includes(PROFILE.hooks.claude), ...e ? { entry: e } : {} };
12337
12395
  }
12338
12396
  if (target === "cursor") {
12339
- const entry = readJson(join5(cursorDir(), "mcp.json")).mcpServers?.[MCP_SERVER];
12397
+ const entry = readJson(join6(cursorDir(), "mcp.json")).mcpServers?.[MCP_SERVER];
12340
12398
  const e = entryOf(entry);
12341
- return { mcp: !!entry, hooks: cursorParentHooksRegistered(PROFILE, readJson(join5(cursorDir(), "hooks.json"))), ...e ? { entry: e } : {} };
12399
+ return { mcp: !!entry, hooks: cursorParentHooksRegistered(PROFILE, readJson(join6(cursorDir(), "hooks.json"))), ...e ? { entry: e } : {} };
12342
12400
  }
12343
12401
  if (target === "codex") {
12344
- const toml2 = existsSync5(join5(codexHome(), "config.toml")) ? readFileSync5(join5(codexHome(), "config.toml"), "utf8") : "";
12402
+ const toml2 = existsSync5(join6(codexHome(), "config.toml")) ? readFileSync5(join6(codexHome(), "config.toml"), "utf8") : "";
12345
12403
  return { mcp: toml2.includes(`[mcp_servers.${MCP_SERVER}]`), hooks: null };
12346
12404
  }
12347
- const toml = existsSync5(join5(grokDir(), "config.toml")) ? readFileSync5(join5(grokDir(), "config.toml"), "utf8") : "";
12405
+ const toml = existsSync5(join6(grokDir(), "config.toml")) ? readFileSync5(join6(grokDir(), "config.toml"), "utf8") : "";
12348
12406
  return { mcp: toml.includes(MCP_SERVER), hooks: null };
12349
12407
  }
12350
12408
 
@@ -14459,7 +14517,7 @@ var Protocol = class {
14459
14517
  return;
14460
14518
  }
14461
14519
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
14462
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
14520
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
14463
14521
  options?.signal?.throwIfAborted();
14464
14522
  }
14465
14523
  } catch (error) {
@@ -14476,7 +14534,7 @@ var Protocol = class {
14476
14534
  */
14477
14535
  request(request, resultSchema, options) {
14478
14536
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
14479
- return new Promise((resolve, reject) => {
14537
+ return new Promise((resolve2, reject) => {
14480
14538
  const earlyReject = (error) => {
14481
14539
  reject(error);
14482
14540
  };
@@ -14554,7 +14612,7 @@ var Protocol = class {
14554
14612
  if (!parseResult.success) {
14555
14613
  reject(parseResult.error);
14556
14614
  } else {
14557
- resolve(parseResult.data);
14615
+ resolve2(parseResult.data);
14558
14616
  }
14559
14617
  } catch (error) {
14560
14618
  reject(error);
@@ -14815,12 +14873,12 @@ var Protocol = class {
14815
14873
  }
14816
14874
  } catch {
14817
14875
  }
14818
- return new Promise((resolve, reject) => {
14876
+ return new Promise((resolve2, reject) => {
14819
14877
  if (signal.aborted) {
14820
14878
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
14821
14879
  return;
14822
14880
  }
14823
- const timeoutId = setTimeout(resolve, interval);
14881
+ const timeoutId = setTimeout(resolve2, interval);
14824
14882
  signal.addEventListener("abort", () => {
14825
14883
  clearTimeout(timeoutId);
14826
14884
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -15696,12 +15754,12 @@ var StdioServerTransport = class {
15696
15754
  this.onclose?.();
15697
15755
  }
15698
15756
  send(message) {
15699
- return new Promise((resolve) => {
15757
+ return new Promise((resolve2) => {
15700
15758
  const json = serializeMessage(message);
15701
15759
  if (this._stdout.write(json)) {
15702
- resolve();
15760
+ resolve2();
15703
15761
  } else {
15704
- this._stdout.once("drain", resolve);
15762
+ this._stdout.once("drain", resolve2);
15705
15763
  }
15706
15764
  });
15707
15765
  }
@@ -15709,10 +15767,10 @@ var StdioServerTransport = class {
15709
15767
 
15710
15768
  // src/mcp.ts
15711
15769
  import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
15712
- import { basename as basename2, join as join6 } from "node:path";
15770
+ import { basename as basename2, join as join7 } from "node:path";
15713
15771
 
15714
15772
  // src/version.ts
15715
- var VERSION = true ? "0.1.9" : "0.0.0-dev";
15773
+ var VERSION = true ? "0.1.11" : "0.0.0-dev";
15716
15774
  function osName() {
15717
15775
  return process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : process.platform;
15718
15776
  }
@@ -15814,7 +15872,7 @@ var TOOLS = [
15814
15872
  ];
15815
15873
  var VIEWABLE_IMAGES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
15816
15874
  var MAX_INLINE_IMAGE = 5 * 1024 * 1024;
15817
- var parentsFile = () => join6(stateRoot(), "parents.json");
15875
+ var parentsFile = () => join7(stateRoot(), "parents.json");
15818
15876
  var processChannels = /* @__PURE__ */ new Map();
15819
15877
  var lastChannel;
15820
15878
  function savedParents() {
@@ -16073,13 +16131,13 @@ async function ask(question) {
16073
16131
  }
16074
16132
  }
16075
16133
  function installRuntime() {
16076
- const target = join7(home(), "runtime", VERSION);
16134
+ const target = join8(home(), "runtime", VERSION);
16077
16135
  if (runtimeDir() !== target) {
16078
16136
  rmSync3(target, { recursive: true, force: true });
16079
16137
  mkdirSync5(target, { recursive: true });
16080
16138
  cpSync(runtimeDir(), target, { recursive: true });
16081
16139
  }
16082
- return join7(target, "cli.mjs");
16140
+ return join8(target, "cli.mjs");
16083
16141
  }
16084
16142
  function selectedTargets() {
16085
16143
  const only = option("only");
@@ -16110,7 +16168,7 @@ async function pair(server, targets) {
16110
16168
  if (result.status === "claimed" && result.token) return { token: result.token, connection_id: result.connection_id };
16111
16169
  if (result.status === "rejected") throw new Error("\u30A2\u30D7\u30EA\u3067\u300C\u5FC3\u5F53\u305F\u308A\u304C\u306A\u3044\u300D\u304C\u62BC\u3055\u308C\u307E\u3057\u305F\u3002\u3082\u3046\u4E00\u5EA6 npx approval-box setup \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
16112
16170
  if (result.status === "expired" || result.status === "delivered") throw new Error("\u30DA\u30A2\u30EA\u30F3\u30B0\u306E\u671F\u9650\u304C\u5207\u308C\u307E\u3057\u305F\u3002\u3082\u3046\u4E00\u5EA6 npx approval-box setup \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
16113
- await new Promise((resolve) => setTimeout(resolve, 2e3));
16171
+ await new Promise((resolve2) => setTimeout(resolve2, 2e3));
16114
16172
  }
16115
16173
  }
16116
16174
  async function waitForChecks(api, clients) {
@@ -16134,7 +16192,7 @@ async function waitForChecks(api, clients) {
16134
16192
  out("\n\u3059\u3079\u3066\u306EAI\u3067\u78BA\u8A8D\u304C\u6E08\u307F\u307E\u3057\u305F\u3002Approval Box\u3092\u4F7F\u3048\u307E\u3059\u3002");
16135
16193
  return;
16136
16194
  }
16137
- await new Promise((resolve) => setTimeout(resolve, 3e3));
16195
+ await new Promise((resolve2) => setTimeout(resolve2, 3e3));
16138
16196
  }
16139
16197
  }
16140
16198
  async function setup() {
@@ -16161,7 +16219,8 @@ async function setup() {
16161
16219
  out(` \u300C${INSTRUCTION_TEXT}\u300D`);
16162
16220
  for (const target of targets) {
16163
16221
  const file = instructionsFileOf(target);
16164
- out(` ${LABEL[target]}: ${file ?? "\u4F1A\u8A71\u306E\u59CB\u307E\u308A\u306Ehook\uFF08hooks.json \u306E sessionStart\uFF09\u3067\u6E21\u3057\u307E\u3059"}`);
16222
+ const link = file ? managedElsewhere(file) : null;
16223
+ out(` ${LABEL[target]}: ${!file ? "\u4F1A\u8A71\u306E\u59CB\u307E\u308A\u306Ehook\uFF08hooks.json \u306E sessionStart\uFF09\u3067\u6E21\u3057\u307E\u3059" : link ? `${file} \u306F ${link} \u3078\u306E\u53C2\u7167\u3067\u3001\u5225\u306E\u6240\u3067\u7BA1\u7406\u3055\u308C\u3066\u3044\u308B\u306E\u3067\u66F8\u304D\u307E\u305B\u3093\uFF08\u7BA1\u7406\u5143\u306E\u898F\u7BC4\u306B\u4E00\u7BC0\u3092\u8DB3\u3057\u3066\u304F\u3060\u3055\u3044\uFF09` : file}`);
16165
16224
  }
16166
16225
  const instructions = !flag("no-instructions") && (flag("yes") || !/^n(o)?$/i.test(await ask("\u8DB3\u3057\u307E\u3059\u304B\uFF1F [Y/n] ")));
16167
16226
  const cli = installRuntime();
@@ -16194,6 +16253,7 @@ async function registerTargets() {
16194
16253
  const result = await register(target, { instructions: flag("instructions") });
16195
16254
  const extra = result.steer ? `\uFF08${STEER_LABEL[result.steer.split(":")[0] ?? ""] ?? `\u4F5C\u696D\u4E2D\u306E\u5272\u308A\u8FBC\u307F: ${result.steer}`}\uFF09` : "";
16196
16255
  out(` ${LABEL[target]}: ${result.status === "registered" ? "\u767B\u9332\u3057\u307E\u3057\u305F" : `\u5931\u6557 \u2014 ${result.detail}`}${extra}`);
16256
+ if (result.note) out(` \u30FB${result.note}`);
16197
16257
  if (result.status === "failed") failed = true;
16198
16258
  }
16199
16259
  ensureDaemon();
@@ -16223,7 +16283,7 @@ async function uninstall() {
16223
16283
  out(` \u30B5\u30FC\u30D0\u30FC: \u63A5\u7D9A\u306E\u89E3\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F\uFF08\u30A2\u30D7\u30EA\u306E\u300C\u63A5\u7D9A\u300D\u304B\u3089\u5916\u3057\u3066\u304F\u3060\u3055\u3044\uFF09: ${error.message}`);
16224
16284
  }
16225
16285
  }
16226
- rmSync3(join7(home(), "config.json"), { force: true });
16286
+ rmSync3(join8(home(), "config.json"), { force: true });
16227
16287
  out("\nApproval Box\u3092\u5916\u3057\u307E\u3057\u305F\u3002npm install -g \u3067\u5165\u308C\u305F\u5834\u5408\u306F\u3001npm uninstall -g approval-box \u3067\u672C\u4F53\u3082\u6D88\u305B\u307E\u3059\u3002");
16228
16288
  }
16229
16289
  async function status() {
@@ -16268,7 +16328,11 @@ async function doctor() {
16268
16328
  if (!r.mcp) problems.push(`${LABEL[target]}: MCP\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093 \u2192 npx approval-box setup --only ${target}`);
16269
16329
  if (r.hooks === false) problems.push(`${LABEL[target]}: hook\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093 \u2192 npx approval-box setup --only ${target}`);
16270
16330
  if (r.entry && !existsSync7(r.entry)) problems.push(`${LABEL[target]}: \u767B\u9332\u5148\u306E\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u305B\u3093\uFF08${r.entry}\uFF09\u3002node \u3084Approval Box\u3092\u5165\u308C\u76F4\u3057\u305F\u6642\u306B\u8D77\u304D\u307E\u3059 \u2192 npx approval-box setup`);
16271
- if (r.mcp && r.instructions === false) hints.push(`${LABEL[target]}: \u5168\u4F53\u306E\u6307\u793A\u306BApproval Box\u306E\u4E00\u7BC0\u304C\u3042\u308A\u307E\u305B\u3093\u3002AI\u304C\u7533\u8ACB\u3092\u51FA\u3055\u306A\u3044\u6642\u306F \u2192 npx approval-box setup --only ${target}`);
16331
+ if (r.mcp && r.instructions === false) {
16332
+ const file = instructionsFileOf(target);
16333
+ const link = file ? managedElsewhere(file) : null;
16334
+ hints.push(link ? `${LABEL[target]}: \u5168\u4F53\u306E\u6307\u793A\uFF08${file} \u2192 ${link}\uFF09\u306BApproval Box\u306E\u4E00\u7BC0\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u5225\u306E\u6240\u3067\u7BA1\u7406\u3055\u308C\u3066\u3044\u308B\u306E\u3067\u3001\u7BA1\u7406\u5143\u306E\u898F\u7BC4\u306B\u300C${INSTRUCTION_TEXT}\u300D\u3092\u8DB3\u3057\u3066\u304F\u3060\u3055\u3044` : `${LABEL[target]}: \u5168\u4F53\u306E\u6307\u793A\u306BApproval Box\u306E\u4E00\u7BC0\u304C\u3042\u308A\u307E\u305B\u3093\u3002AI\u304C\u7533\u8ACB\u3092\u51FA\u3055\u306A\u3044\u6642\u306F \u2192 npx approval-box setup --only ${target}`);
16335
+ }
16272
16336
  }
16273
16337
  for (const hint of hints) out(`\u30FB${hint}`);
16274
16338
  if (!problems.length) out("\u554F\u984C\u306F\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u5C4A\u304B\u306A\u3044\u6642\u306F\u3001AI\u306B\u300CApproval Box\u306Esetup_test\u3092\u5B9F\u884C\u3057\u3066\u300D\u3068\u8A00\u3063\u3066\u3001\u3069\u3053\u3067\u6B62\u307E\u308B\u304B\u3092\u78BA\u304B\u3081\u3066\u304F\u3060\u3055\u3044\u3002");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "approval-box",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
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",