approval-box 0.1.8 → 0.1.10

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 +220 -106
  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,
@@ -11755,9 +11755,9 @@ var require_dist = __commonJS({
11755
11755
 
11756
11756
  // src/cli.ts
11757
11757
  var import_qrcode = __toESM(require_lib(), 1);
11758
- import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync4, rmSync as rmSync2 } from "node:fs";
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 join6 } from "node:path";
11760
+ import { join as join7 } from "node:path";
11761
11761
  import { createInterface } from "node:readline/promises";
11762
11762
 
11763
11763
  // src/api.ts
@@ -11810,6 +11810,18 @@ var Api = class {
11810
11810
  }
11811
11811
  return json;
11812
11812
  }
11813
+ /** ファイルを取る(添付)。誤りはJSONの本文から読む。 */
11814
+ async download(path) {
11815
+ let response;
11816
+ try {
11817
+ response = await fetch(this.url(path), { headers: this.token ? { authorization: `Bearer ${this.token}` } : {} });
11818
+ } catch (error) {
11819
+ throw new ServerError(0, "network", `Approval Box\u30B5\u30FC\u30D0\u30FC\uFF08${this.server}\uFF09\u306B\u3064\u306A\u304C\u308A\u307E\u305B\u3093: ${error.message}`, {});
11820
+ }
11821
+ if (response.ok) return Buffer.from(await response.arrayBuffer());
11822
+ const json = await response.json().catch(() => ({}));
11823
+ throw new ServerError(response.status, json.error?.code ?? "internal", json.error?.message ?? `HTTP ${response.status}`, json.error ?? {});
11824
+ }
11813
11825
  /** コネクタ用SSE。届いたイベント名ごとに onEvent を呼ぶ。切れたら戻る。 */
11814
11826
  async stream(onEvent, signal) {
11815
11827
  const response = await fetch(this.url("/stream"), { headers: { authorization: `Bearer ${this.token}`, accept: "text/event-stream" }, signal });
@@ -11856,23 +11868,68 @@ function writeConfig(config) {
11856
11868
 
11857
11869
  // src/daemon.ts
11858
11870
  import { spawn } from "node:child_process";
11859
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync } from "node:fs";
11860
- import { join as join3 } from "node:path";
11871
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync3, rmSync as rmSync2 } from "node:fs";
11872
+ import { join as join4 } from "node:path";
11873
+
11874
+ // src/attachments.ts
11875
+ import { createHash } from "node:crypto";
11876
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync, statSync, writeFileSync as writeFileSync2 } from "node:fs";
11877
+ import { join as join2 } from "node:path";
11878
+ var KEEP_DAYS = 30;
11879
+ var attachmentsRoot = () => join2(home(), "attachments");
11880
+ function safeFileName(name) {
11881
+ const cleaned = name.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_").replace(/[. ]+$/, "").trim();
11882
+ return cleaned.slice(0, 150) || "file";
11883
+ }
11884
+ async function saveAttachment(api, decisionId, meta, index) {
11885
+ const dir = join2(attachmentsRoot(), safeFileName(decisionId));
11886
+ const file = join2(dir, `${index + 1}_${safeFileName(meta.name)}`);
11887
+ if (existsSync2(file) && sha256(readFileSync2(file)) === meta.sha256) return file;
11888
+ const data = await api.download(`/decisions/${encodeURIComponent(decisionId)}/attachments/${encodeURIComponent(meta.id)}`);
11889
+ if (sha256(data) !== meta.sha256) throw new Error(`${meta.name} \u306E\u4E2D\u8EAB\u304C\u30B5\u30FC\u30D0\u30FC\u306E\u8A18\u9332\u3068\u5408\u3044\u307E\u305B\u3093\uFF08\u53D6\u308A\u76F4\u3057\u3066\u304F\u3060\u3055\u3044\uFF09`);
11890
+ mkdirSync2(dir, { recursive: true, mode: 448 });
11891
+ writeFileSync2(file, data, { mode: 384 });
11892
+ return file;
11893
+ }
11894
+ async function saveAll(api, decisionId, items) {
11895
+ const lines = ["", "\u6DFB\u4ED8\u306F\u3053\u306E\u7AEF\u672B\u306B\u4FDD\u5B58\u3057\u307E\u3057\u305F:"];
11896
+ for (const [i, meta] of items.entries()) {
11897
+ try {
11898
+ lines.push(`${i + 1}. ${await saveAttachment(api, decisionId, meta, i)}`);
11899
+ } catch (error) {
11900
+ lines.push(`${i + 1}. ${meta.name}: \u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08${error.message}\uFF09\u3002get_attachment \u3067\u53D6\u3063\u3066\u304F\u3060\u3055\u3044\u3002`);
11901
+ }
11902
+ }
11903
+ return lines.join("\n");
11904
+ }
11905
+ function pruneAttachments() {
11906
+ const root = attachmentsRoot();
11907
+ if (!existsSync2(root)) return;
11908
+ const before = Date.now() - KEEP_DAYS * 864e5;
11909
+ for (const name of readdirSync(root)) {
11910
+ const dir = join2(root, name);
11911
+ try {
11912
+ if (statSync(dir).mtimeMs < before) rmSync(dir, { recursive: true, force: true });
11913
+ } catch {
11914
+ }
11915
+ }
11916
+ }
11917
+ var sha256 = (data) => createHash("sha256").update(data).digest("hex");
11861
11918
 
11862
11919
  // src/runtime.ts
11863
- import { basename, dirname as dirname2, join as join2 } from "node:path";
11920
+ import { basename, dirname as dirname2, join as join3 } from "node:path";
11864
11921
  import { fileURLToPath } from "node:url";
11865
11922
  var runtimeDir = () => {
11866
11923
  const dir = dirname2(fileURLToPath(import.meta.url));
11867
11924
  return basename(dir) === "chunks" ? dirname2(dir) : dir;
11868
11925
  };
11869
11926
  function runtimeEntry(kind) {
11870
- return join2(runtimeDir(), kind === "cli" ? "cli.mjs" : HOOK_FILES[kind]);
11927
+ return join3(runtimeDir(), kind === "cli" ? "cli.mjs" : HOOK_FILES[kind]);
11871
11928
  }
11872
11929
 
11873
11930
  // src/daemon.ts
11874
- var lockFile = () => join3(stateRoot(), "daemon.json");
11875
- var journalFile = () => join3(stateRoot(), "deliveries.json");
11931
+ var lockFile = () => join4(stateRoot(), "daemon.json");
11932
+ var journalFile = () => join4(stateRoot(), "deliveries.json");
11876
11933
  var IDLE_EXIT_MS = 30 * 6e4;
11877
11934
  function alive(pid) {
11878
11935
  try {
@@ -11883,9 +11940,9 @@ function alive(pid) {
11883
11940
  }
11884
11941
  }
11885
11942
  function daemonStatus() {
11886
- if (!existsSync2(lockFile())) return { running: false };
11943
+ if (!existsSync3(lockFile())) return { running: false };
11887
11944
  try {
11888
- const lock = JSON.parse(readFileSync2(lockFile(), "utf8"));
11945
+ const lock = JSON.parse(readFileSync3(lockFile(), "utf8"));
11889
11946
  return alive(lock.pid) ? { running: true, pid: lock.pid, entry: lock.entry } : { running: false };
11890
11947
  } catch {
11891
11948
  return { running: false };
@@ -11902,21 +11959,21 @@ function ensureDaemon() {
11902
11959
  } catch {
11903
11960
  }
11904
11961
  }
11905
- mkdirSync2(stateRoot(), { recursive: true, mode: 448 });
11906
- const log = openSync(join3(stateRoot(), "daemon.log"), "a", 384);
11962
+ mkdirSync3(stateRoot(), { recursive: true, mode: 448 });
11963
+ const log = openSync(join4(stateRoot(), "daemon.log"), "a", 384);
11907
11964
  spawn(process.execPath, [runtimeEntry("cli"), "daemon"], { detached: true, stdio: ["ignore", "ignore", log], windowsHide: true }).unref();
11908
11965
  } catch (error) {
11909
11966
  process.stderr.write(`approval-box: \u914D\u9001\u30C7\u30FC\u30E2\u30F3\u3092\u8D77\u52D5\u3067\u304D\u307E\u305B\u3093: ${error.message}
11910
11967
  `);
11911
11968
  }
11912
11969
  }
11913
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
11970
+ var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
11914
11971
  async function runDaemon() {
11915
11972
  const status2 = daemonStatus();
11916
11973
  if (status2.running && status2.pid !== process.pid) return;
11917
11974
  writeJsonFile(lockFile(), { pid: process.pid, entry: runtimeEntry("cli"), started_at: (/* @__PURE__ */ new Date()).toISOString() });
11918
11975
  const api = new Api(requireConfig());
11919
- let journal = existsSync2(journalFile()) ? JSON.parse(readFileSync2(journalFile(), "utf8")) : {};
11976
+ let journal = existsSync3(journalFile()) ? JSON.parse(readFileSync3(journalFile(), "utf8")) : {};
11920
11977
  const save = () => writeJsonFile(journalFile(), journal);
11921
11978
  const log = (message) => process.stderr.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${message}
11922
11979
  `);
@@ -11928,6 +11985,11 @@ async function runDaemon() {
11928
11985
  journal[id] = entry;
11929
11986
  }
11930
11987
  save();
11988
+ try {
11989
+ pruneAttachments();
11990
+ } catch (error) {
11991
+ log(`\u53E4\u3044\u6DFB\u4ED8\u306E\u7247\u4ED8\u3051\u306B\u5931\u6557: ${error.message}`);
11992
+ }
11931
11993
  async function report(id) {
11932
11994
  const entry = journal[id];
11933
11995
  try {
@@ -11941,12 +12003,13 @@ async function runDaemon() {
11941
12003
  async function deliver(item) {
11942
12004
  const existing = journal[item.decision_id];
11943
12005
  if (existing) return;
12006
+ const text2 = item.attachments?.length ? item.text + await saveAll(api, item.decision_id, item.attachments) : item.text;
11944
12007
  journal[item.decision_id] = { delivery_id: item.delivery_id, channel_id: item.route.channel_id, harness: item.route.harness, state: "sending", at: (/* @__PURE__ */ new Date()).toISOString() };
11945
12008
  save();
11946
12009
  const entry = journal[item.decision_id];
11947
12010
  try {
11948
12011
  if (channelClosed(PROFILE, item.route.channel_id)) throw Object.assign(new Error("\u7533\u8ACB\u3092\u51FA\u3057\u305FAI\u306E\u4F1A\u8A71\u304C\u7D42\u308F\u3063\u3066\u3044\u307E\u3059"), { closed: true });
11949
- const result = await sendToChannel(PROFILE, item.route.channel_id, item.delivery_id, item.text);
12012
+ const result = await sendToChannel(PROFILE, item.route.channel_id, item.delivery_id, text2);
11950
12013
  if (result.state === "submitted") Object.assign(entry, { state: "report", result: "delivered" });
11951
12014
  else entry.state = "queued";
11952
12015
  } catch (error) {
@@ -12048,8 +12111,8 @@ async function runDaemon() {
12048
12111
  }
12049
12112
  }
12050
12113
  try {
12051
- const lock = JSON.parse(readFileSync2(lockFile(), "utf8"));
12052
- if (lock.pid === process.pid) rmSync(lockFile(), { force: true });
12114
+ const lock = JSON.parse(readFileSync3(lockFile(), "utf8"));
12115
+ if (lock.pid === process.pid) rmSync2(lockFile(), { force: true });
12053
12116
  } catch {
12054
12117
  }
12055
12118
  process.exit(0);
@@ -12057,13 +12120,14 @@ async function runDaemon() {
12057
12120
 
12058
12121
  // src/harness.ts
12059
12122
  import { execFileSync } from "node:child_process";
12060
- import { copyFileSync, existsSync as existsSync4, readFileSync as readFileSync4, realpathSync } from "node:fs";
12061
- import { homedir } from "node:os";
12062
- import { delimiter, dirname as dirname4, join as join4 } from "node:path";
12123
+ import { copyFileSync, existsSync as existsSync5, readFileSync as readFileSync5, realpathSync } from "node:fs";
12124
+ import { homedir as homedir2 } from "node:os";
12125
+ import { delimiter, dirname as dirname4, join as join5 } from "node:path";
12063
12126
 
12064
12127
  // src/instructions.ts
12065
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
12066
- import { dirname as dirname3 } from "node:path";
12128
+ import { existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync4, readlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
12129
+ import { homedir } from "node:os";
12130
+ import { dirname as dirname3, resolve } from "node:path";
12067
12131
  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";
12068
12132
  var START = "<!-- approval-box:start -->";
12069
12133
  var END = "<!-- approval-box:end -->";
@@ -12074,20 +12138,37 @@ ${INSTRUCTION_TEXT}
12074
12138
  ${END}
12075
12139
  `;
12076
12140
  var PATTERN = new RegExp(`\\n*${START}[\\s\\S]*?${END}\\n?`, "g");
12141
+ var SAME_MEANING = /Approval Box\s*の\s*`?request_decision`?\s*でも申請/;
12077
12142
  function hasInstructions(file) {
12078
- return existsSync3(file) && readFileSync3(file, "utf8").includes(START);
12143
+ if (!existsSync4(file)) return false;
12144
+ const text2 = readFileSync4(file, "utf8");
12145
+ if (text2.includes(START) || SAME_MEANING.test(text2)) return true;
12146
+ for (const [, ref] of text2.matchAll(/^@(\S+)\s*$/gm)) {
12147
+ const target = ref.startsWith("~/") ? resolve(homedir(), ref.slice(2)) : resolve(dirname3(file), ref);
12148
+ if (existsSync4(target) && SAME_MEANING.test(readFileSync4(target, "utf8"))) return true;
12149
+ }
12150
+ return false;
12151
+ }
12152
+ function managedElsewhere(file) {
12153
+ try {
12154
+ return lstatSync(file).isSymbolicLink() ? readlinkSync(file) : null;
12155
+ } catch {
12156
+ return null;
12157
+ }
12079
12158
  }
12080
12159
  function writeInstructions(file) {
12081
- const current = existsSync3(file) ? readFileSync3(file, "utf8").replace(PATTERN, "\n").trimEnd() : "";
12082
- mkdirSync3(dirname3(file), { recursive: true, mode: 448 });
12083
- writeFileSync2(file, current ? `${current}
12160
+ if (managedElsewhere(file)) return "managed_elsewhere";
12161
+ const current = existsSync4(file) ? readFileSync4(file, "utf8").replace(PATTERN, "\n").trimEnd() : "";
12162
+ mkdirSync4(dirname3(file), { recursive: true, mode: 448 });
12163
+ writeFileSync3(file, current ? `${current}
12084
12164
 
12085
12165
  ${BLOCK}` : BLOCK);
12166
+ return "written";
12086
12167
  }
12087
12168
  function removeInstructions(file) {
12088
- if (!hasInstructions(file)) return;
12089
- const rest = readFileSync3(file, "utf8").replace(PATTERN, "\n").trimEnd();
12090
- writeFileSync2(file, rest ? `${rest}
12169
+ if (!existsSync4(file) || !readFileSync4(file, "utf8").includes(START)) return;
12170
+ const rest = readFileSync4(file, "utf8").replace(PATTERN, "\n").trimEnd();
12171
+ writeFileSync3(file, rest ? `${rest}
12091
12172
  ` : "");
12092
12173
  }
12093
12174
 
@@ -12095,35 +12176,35 @@ function removeInstructions(file) {
12095
12176
  var HARNESSES = ["claude", "codex", "cursor", "grok"];
12096
12177
  var LABEL = { claude: "Claude Code", codex: "Codex", cursor: "Cursor", grok: "Grok" };
12097
12178
  var CLIENT = { claude: "claude-code", codex: "codex", cursor: "cursor", grok: "grok" };
12098
- var claudeDir = () => process.env.CLAUDE_CONFIG_DIR ?? join4(homedir(), ".claude");
12099
- var claudeJson = () => process.env.CLAUDE_CONFIG_DIR ? join4(process.env.CLAUDE_CONFIG_DIR, ".claude.json") : join4(homedir(), ".claude.json");
12100
- var cursorDir = () => process.env.CURSOR_HOME ?? join4(homedir(), ".cursor");
12101
- var codexHome = () => process.env.CODEX_HOME ?? join4(homedir(), ".codex");
12102
- var grokDir = () => process.env.GROK_HOME ?? join4(homedir(), ".grok");
12179
+ var claudeDir = () => process.env.CLAUDE_CONFIG_DIR ?? join5(homedir2(), ".claude");
12180
+ var claudeJson = () => process.env.CLAUDE_CONFIG_DIR ? join5(process.env.CLAUDE_CONFIG_DIR, ".claude.json") : join5(homedir2(), ".claude.json");
12181
+ var cursorDir = () => process.env.CURSOR_HOME ?? join5(homedir2(), ".cursor");
12182
+ var codexHome = () => process.env.CODEX_HOME ?? join5(homedir2(), ".codex");
12183
+ var grokDir = () => process.env.GROK_HOME ?? join5(homedir2(), ".grok");
12103
12184
  function filesOf(target) {
12104
- if (target === "claude") return [claudeJson(), join4(claudeDir(), "settings.json")];
12105
- if (target === "cursor") return [join4(cursorDir(), "mcp.json"), join4(cursorDir(), "hooks.json"), cursorCliConfig()];
12106
- if (target === "codex") return [join4(codexHome(), "config.toml"), join4(codexHome(), "hooks.json")];
12107
- return [join4(grokDir(), "config.toml")];
12185
+ if (target === "claude") return [claudeJson(), join5(claudeDir(), "settings.json")];
12186
+ if (target === "cursor") return [join5(cursorDir(), "mcp.json"), join5(cursorDir(), "hooks.json"), cursorCliConfig()];
12187
+ if (target === "codex") return [join5(codexHome(), "config.toml"), join5(codexHome(), "hooks.json")];
12188
+ return [join5(grokDir(), "config.toml")];
12108
12189
  }
12109
12190
  function instructionsFileOf(target) {
12110
- if (target === "claude") return join4(claudeDir(), "CLAUDE.md");
12111
- if (target === "codex") return join4(codexHome(), "AGENTS.md");
12112
- if (target === "grok") return join4(grokDir(), "AGENTS.md");
12191
+ if (target === "claude") return join5(claudeDir(), "CLAUDE.md");
12192
+ if (target === "codex") return join5(codexHome(), "AGENTS.md");
12193
+ if (target === "grok") return join5(grokDir(), "AGENTS.md");
12113
12194
  return null;
12114
12195
  }
12115
12196
  function onPath(command2) {
12116
12197
  const names = process.platform === "win32" ? [`${command2}.exe`, `${command2}.cmd`, command2] : [command2];
12117
12198
  for (const dir of (process.env.PATH ?? "").split(delimiter)) {
12118
- for (const name of names) if (dir && existsSync4(join4(dir, name))) return join4(dir, name);
12199
+ for (const name of names) if (dir && existsSync5(join5(dir, name))) return join5(dir, name);
12119
12200
  }
12120
12201
  return null;
12121
12202
  }
12122
12203
  function detect(target) {
12123
- if (target === "claude") return existsSync4(claudeDir()) || !!onPath("claude");
12124
- if (target === "codex") return existsSync4(codexHome()) || !!onPath("codex");
12125
- if (target === "cursor") return existsSync4(cursorDir()) || !!onPath("cursor-agent");
12126
- return existsSync4(grokDir()) || !!onPath("grok");
12204
+ if (target === "claude") return existsSync5(claudeDir()) || !!onPath("claude");
12205
+ if (target === "codex") return existsSync5(codexHome()) || !!onPath("codex");
12206
+ if (target === "cursor") return existsSync5(cursorDir()) || !!onPath("cursor-agent");
12207
+ return existsSync5(grokDir()) || !!onPath("grok");
12127
12208
  }
12128
12209
  function stableNode() {
12129
12210
  try {
@@ -12132,11 +12213,11 @@ function stableNode() {
12132
12213
  return process.execPath;
12133
12214
  }
12134
12215
  }
12135
- var CURSOR_TOOLS = ["list_my_decisions", "request_decision", "amend_decision", "cancel_decision", "get_decision", "setup_test", "confirm_setup_test"];
12136
- var cursorCliConfig = () => join4(cursorDir(), "cli-config.json");
12216
+ var CURSOR_TOOLS = ["list_my_decisions", "request_decision", "amend_decision", "cancel_decision", "get_decision", "get_attachment", "setup_test", "confirm_setup_test"];
12217
+ var cursorCliConfig = () => join5(cursorDir(), "cli-config.json");
12137
12218
  function setCursorPermissions(enable) {
12138
12219
  const file = cursorCliConfig();
12139
- if (!existsSync4(file) && !enable) return;
12220
+ if (!existsSync5(file) && !enable) return;
12140
12221
  backupOnce(file);
12141
12222
  const current = readJson(file);
12142
12223
  const permissions = current.permissions ?? {};
@@ -12147,11 +12228,11 @@ function setCursorPermissions(enable) {
12147
12228
  }
12148
12229
  var CURSOR_CONTEXT = "cursor-context";
12149
12230
  function setCursorContext(enable) {
12150
- const file = join4(cursorDir(), "hooks.json");
12151
- if (!existsSync4(file) && !enable) return;
12152
- const target = existsSync4(file) ? realpathSync(file) : file;
12231
+ const file = join5(cursorDir(), "hooks.json");
12232
+ if (!existsSync5(file) && !enable) return;
12233
+ const target = existsSync5(file) ? realpathSync(file) : file;
12153
12234
  backupOnce(target);
12154
- const current = existsSync4(target) ? readJson(target) : { version: 1 };
12235
+ const current = existsSync5(target) ? readJson(target) : { version: 1 };
12155
12236
  const hooks = { ...current.hooks ?? {} };
12156
12237
  const ours = (entry) => typeof entry?.command === "string" && entry.command.includes(MCP_SERVER) && entry.command.endsWith(` ${CURSOR_CONTEXT}`);
12157
12238
  const rest = (hooks.sessionStart ?? []).filter((entry) => !ours(entry));
@@ -12160,15 +12241,15 @@ function setCursorContext(enable) {
12160
12241
  else delete hooks.sessionStart;
12161
12242
  writeJsonFile(target, { version: 1, ...current, hooks });
12162
12243
  }
12163
- var cursorContextRegistered = () => JSON.stringify(readJson(join4(cursorDir(), "hooks.json")).hooks ?? {}).includes(` ${CURSOR_CONTEXT}"`);
12244
+ var cursorContextRegistered = () => JSON.stringify(readJson(join5(cursorDir(), "hooks.json")).hooks ?? {}).includes(` ${CURSOR_CONTEXT}"`);
12164
12245
  var registration = () => ({ command: stableNode(), args: [runtimeEntry("cli"), "mcp"] });
12165
12246
  function readJson(file) {
12166
- if (!existsSync4(file)) return {};
12167
- return JSON.parse(readFileSync4(file, "utf8").replace(/^/, ""));
12247
+ if (!existsSync5(file)) return {};
12248
+ return JSON.parse(readFileSync5(file, "utf8").replace(/^/, ""));
12168
12249
  }
12169
12250
  function backupOnce(file) {
12170
12251
  const copy = `${file}${PROFILE.backup_suffix}`;
12171
- if (existsSync4(file) && !existsSync4(copy)) copyFileSync(file, copy);
12252
+ if (existsSync5(file) && !existsSync5(copy)) copyFileSync(file, copy);
12172
12253
  }
12173
12254
  function setMcpJson(file, value) {
12174
12255
  backupOnce(file);
@@ -12183,29 +12264,34 @@ async function withCodexConfig(fn) {
12183
12264
  }
12184
12265
  async function register(target, options = {}) {
12185
12266
  const file = instructionsFileOf(target);
12267
+ let note;
12186
12268
  if (options.instructions && file) {
12187
12269
  try {
12188
- writeInstructions(file);
12270
+ 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`;
12189
12271
  } catch (error) {
12190
12272
  return { target, status: "failed", detail: `${file}: ${error.message}` };
12191
12273
  }
12192
12274
  }
12275
+ const result = await registerTools(target, options);
12276
+ return note && result.status === "registered" ? { ...result, note } : result;
12277
+ }
12278
+ async function registerTools(target, options) {
12193
12279
  const hook = (kind) => ({ command: stableNode(), script: runtimeEntry(kind) });
12194
12280
  try {
12195
12281
  if (target === "claude") {
12196
12282
  setMcpJson(claudeJson(), { type: "stdio", ...registration() });
12197
- mergeClaudeParentHooks(PROFILE, join4(claudeDir(), "settings.json"), hook("claude"));
12283
+ mergeClaudeParentHooks(PROFILE, join5(claudeDir(), "settings.json"), hook("claude"));
12198
12284
  return { target, status: "registered" };
12199
12285
  }
12200
12286
  if (target === "cursor") {
12201
- setMcpJson(join4(cursorDir(), "mcp.json"), registration());
12202
- mergeCursorParentHooks(PROFILE, join4(cursorDir(), "hooks.json"), hook("cursor"));
12287
+ setMcpJson(join5(cursorDir(), "mcp.json"), registration());
12288
+ mergeCursorParentHooks(PROFILE, join5(cursorDir(), "hooks.json"), hook("cursor"));
12203
12289
  setCursorPermissions(true);
12204
12290
  setCursorContext(!!options.instructions);
12205
12291
  return { target, status: "registered" };
12206
12292
  }
12207
12293
  if (target === "codex") {
12208
- const config = join4(codexHome(), "config.toml");
12294
+ const config = join5(codexHome(), "config.toml");
12209
12295
  backupOnce(config);
12210
12296
  await withCodexConfig(async (request) => {
12211
12297
  await request("config/batchWrite", { filePath: config, edits: [{ keyPath: `mcp_servers.${MCP_SERVER}`, value: { ...registration(), env_vars: ["CODEX_HOME"] }, mergeStrategy: "replace" }] });
@@ -12221,7 +12307,7 @@ async function register(target, options = {}) {
12221
12307
  }
12222
12308
  const grok = onPath("grok");
12223
12309
  if (!grok) throw new Error("grok \u30B3\u30DE\u30F3\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093");
12224
- backupOnce(join4(grokDir(), "config.toml"));
12310
+ backupOnce(join5(grokDir(), "config.toml"));
12225
12311
  const reg = registration();
12226
12312
  execFileSync(grok, ["mcp", "add", MCP_SERVER, "--scope", "user", reg.command, "--", ...reg.args], { stdio: "pipe" });
12227
12313
  return { target, status: "registered" };
@@ -12234,17 +12320,17 @@ async function unregister(target) {
12234
12320
  const file = instructionsFileOf(target);
12235
12321
  if (file) removeInstructions(file);
12236
12322
  if (target === "claude") {
12237
- if (existsSync4(claudeJson())) setMcpJson(claudeJson(), null);
12238
- removeClaudeParentHooks(PROFILE, join4(claudeDir(), "settings.json"));
12323
+ if (existsSync5(claudeJson())) setMcpJson(claudeJson(), null);
12324
+ removeClaudeParentHooks(PROFILE, join5(claudeDir(), "settings.json"));
12239
12325
  } else if (target === "cursor") {
12240
- if (existsSync4(join4(cursorDir(), "mcp.json"))) setMcpJson(join4(cursorDir(), "mcp.json"), null);
12241
- removeCursorParentHooks(PROFILE, join4(cursorDir(), "hooks.json"));
12326
+ if (existsSync5(join5(cursorDir(), "mcp.json"))) setMcpJson(join5(cursorDir(), "mcp.json"), null);
12327
+ removeCursorParentHooks(PROFILE, join5(cursorDir(), "hooks.json"));
12242
12328
  setCursorPermissions(false);
12243
12329
  setCursorContext(false);
12244
12330
  } else if (target === "codex") {
12245
- if (existsSync4(join4(codexHome(), "config.toml"))) {
12331
+ if (existsSync5(join5(codexHome(), "config.toml"))) {
12246
12332
  await withCodexConfig(async (request) => {
12247
- await request("config/batchWrite", { filePath: join4(codexHome(), "config.toml"), edits: [{ keyPath: `mcp_servers.${MCP_SERVER}`, value: null, mergeStrategy: "replace" }] });
12333
+ await request("config/batchWrite", { filePath: join5(codexHome(), "config.toml"), edits: [{ keyPath: `mcp_servers.${MCP_SERVER}`, value: null, mergeStrategy: "replace" }] });
12248
12334
  });
12249
12335
  }
12250
12336
  try {
@@ -12268,20 +12354,20 @@ function registeredTools(target) {
12268
12354
  const entryOf = (value) => (value?.args ?? [])[0];
12269
12355
  if (target === "claude") {
12270
12356
  const entry = readJson(claudeJson()).mcpServers?.[MCP_SERVER];
12271
- const settings = JSON.stringify(readJson(join4(claudeDir(), "settings.json")));
12357
+ const settings = JSON.stringify(readJson(join5(claudeDir(), "settings.json")));
12272
12358
  const e = entryOf(entry);
12273
12359
  return { mcp: !!entry, hooks: settings.includes(PROFILE.hooks.claude), ...e ? { entry: e } : {} };
12274
12360
  }
12275
12361
  if (target === "cursor") {
12276
- const entry = readJson(join4(cursorDir(), "mcp.json")).mcpServers?.[MCP_SERVER];
12362
+ const entry = readJson(join5(cursorDir(), "mcp.json")).mcpServers?.[MCP_SERVER];
12277
12363
  const e = entryOf(entry);
12278
- return { mcp: !!entry, hooks: cursorParentHooksRegistered(PROFILE, readJson(join4(cursorDir(), "hooks.json"))), ...e ? { entry: e } : {} };
12364
+ return { mcp: !!entry, hooks: cursorParentHooksRegistered(PROFILE, readJson(join5(cursorDir(), "hooks.json"))), ...e ? { entry: e } : {} };
12279
12365
  }
12280
12366
  if (target === "codex") {
12281
- const toml2 = existsSync4(join4(codexHome(), "config.toml")) ? readFileSync4(join4(codexHome(), "config.toml"), "utf8") : "";
12367
+ const toml2 = existsSync5(join5(codexHome(), "config.toml")) ? readFileSync5(join5(codexHome(), "config.toml"), "utf8") : "";
12282
12368
  return { mcp: toml2.includes(`[mcp_servers.${MCP_SERVER}]`), hooks: null };
12283
12369
  }
12284
- const toml = existsSync4(join4(grokDir(), "config.toml")) ? readFileSync4(join4(grokDir(), "config.toml"), "utf8") : "";
12370
+ const toml = existsSync5(join5(grokDir(), "config.toml")) ? readFileSync5(join5(grokDir(), "config.toml"), "utf8") : "";
12285
12371
  return { mcp: toml.includes(MCP_SERVER), hooks: null };
12286
12372
  }
12287
12373
 
@@ -14396,7 +14482,7 @@ var Protocol = class {
14396
14482
  return;
14397
14483
  }
14398
14484
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
14399
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
14485
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
14400
14486
  options?.signal?.throwIfAborted();
14401
14487
  }
14402
14488
  } catch (error) {
@@ -14413,7 +14499,7 @@ var Protocol = class {
14413
14499
  */
14414
14500
  request(request, resultSchema, options) {
14415
14501
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
14416
- return new Promise((resolve, reject) => {
14502
+ return new Promise((resolve2, reject) => {
14417
14503
  const earlyReject = (error) => {
14418
14504
  reject(error);
14419
14505
  };
@@ -14491,7 +14577,7 @@ var Protocol = class {
14491
14577
  if (!parseResult.success) {
14492
14578
  reject(parseResult.error);
14493
14579
  } else {
14494
- resolve(parseResult.data);
14580
+ resolve2(parseResult.data);
14495
14581
  }
14496
14582
  } catch (error) {
14497
14583
  reject(error);
@@ -14752,12 +14838,12 @@ var Protocol = class {
14752
14838
  }
14753
14839
  } catch {
14754
14840
  }
14755
- return new Promise((resolve, reject) => {
14841
+ return new Promise((resolve2, reject) => {
14756
14842
  if (signal.aborted) {
14757
14843
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
14758
14844
  return;
14759
14845
  }
14760
- const timeoutId = setTimeout(resolve, interval);
14846
+ const timeoutId = setTimeout(resolve2, interval);
14761
14847
  signal.addEventListener("abort", () => {
14762
14848
  clearTimeout(timeoutId);
14763
14849
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -15633,23 +15719,23 @@ var StdioServerTransport = class {
15633
15719
  this.onclose?.();
15634
15720
  }
15635
15721
  send(message) {
15636
- return new Promise((resolve) => {
15722
+ return new Promise((resolve2) => {
15637
15723
  const json = serializeMessage(message);
15638
15724
  if (this._stdout.write(json)) {
15639
- resolve();
15725
+ resolve2();
15640
15726
  } else {
15641
- this._stdout.once("drain", resolve);
15727
+ this._stdout.once("drain", resolve2);
15642
15728
  }
15643
15729
  });
15644
15730
  }
15645
15731
  };
15646
15732
 
15647
15733
  // src/mcp.ts
15648
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
15649
- import { basename as basename2, join as join5 } from "node:path";
15734
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
15735
+ import { basename as basename2, join as join6 } from "node:path";
15650
15736
 
15651
15737
  // src/version.ts
15652
- var VERSION = true ? "0.1.8" : "0.0.0-dev";
15738
+ var VERSION = true ? "0.1.10" : "0.0.0-dev";
15653
15739
  function osName() {
15654
15740
  return process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : process.platform;
15655
15741
  }
@@ -15733,6 +15819,11 @@ var TOOLS = [
15733
15819
  description: "\u7533\u8ACB\u306E\u4ECA\u306E\u72B6\u614B\u3068\u7B54\u3048\u3092\u898B\u308B\u3002\u7B54\u3048\u306F\u3075\u3064\u3046\u81EA\u52D5\u3067\u5C4A\u304F\u306E\u3067\u3001\u5C4A\u3044\u305F\u7B54\u3048\u3092\u898B\u5931\u3063\u305F\u6642\u3060\u3051\u4F7F\u3046\u3002",
15734
15820
  inputSchema: { type: "object", properties: { decision_id: { type: "string" } }, required: ["decision_id"], additionalProperties: false }
15735
15821
  },
15822
+ {
15823
+ name: "get_attachment",
15824
+ description: "\u5229\u7528\u8005\u304C\u7B54\u3048\u306B\u4ED8\u3051\u305F\u6DFB\u4ED8\uFF08\u753B\u50CF\u30FB\u66F8\u985E\uFF09\u3092\u53D6\u308B\u3002\u3053\u306E\u7AEF\u672B\u306B\u4FDD\u5B58\u3057\u3066\u5834\u6240\u3092\u8FD4\u3057\u3001\u753B\u50CF\u306F\u305D\u306E\u307E\u307E\u898B\u3089\u308C\u308B\u5F62\u3067\u3082\u8FD4\u3059\u3002attachment_id \u306F\u5C4A\u3044\u305F\u7B54\u3048\u304B get_decision \u306E answer.attachments \u306B\u3042\u308B\u3002",
15825
+ inputSchema: { type: "object", properties: { decision_id: { type: "string" }, attachment_id: { type: "string" } }, required: ["decision_id", "attachment_id"], additionalProperties: false }
15826
+ },
15736
15827
  {
15737
15828
  name: "setup_test",
15738
15829
  description: 'Approval Box\u306E\u63A5\u7D9A\u30C6\u30B9\u30C8\uFF08\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u78BA\u8A8D\uFF09\u3092\u59CB\u3081\u308B\u3002\u5229\u7528\u8005\u306B\u300CApproval Box\u306Esetup_test\u3092\u5B9F\u884C\u3057\u3066\u300D\u300CApproval Box\u306E\u30C6\u30B9\u30C8\u3092\u3057\u3066\u300D\u300C\u63A5\u7D9A\u30C6\u30B9\u30C8\u3092\u3057\u3066\u300D\u3068\u8A00\u308F\u308C\u305F\u3089\u3001\u4ED6\u306E\u65B9\u6CD5\u3092\u8ABF\u3079\u305A\u306B\u3053\u306Etool\u3092\u547C\u3076\u3002Start the Approval Box connection test when the user says "test Approval Box". \u30C6\u30B9\u30C8\u306E\u7533\u8ACB\u304C\u5229\u7528\u8005\u306B\u5C4A\u304D\u3001\u7B54\u3048\u304C\u3053\u306E\u4F1A\u8A71\u3078\u5C4A\u304F\u3002',
@@ -15744,12 +15835,14 @@ var TOOLS = [
15744
15835
  inputSchema: { type: "object", properties: { decision_id: { type: "string" }, code: { type: "string" } }, required: ["decision_id", "code"], additionalProperties: false }
15745
15836
  }
15746
15837
  ];
15747
- var parentsFile = () => join5(stateRoot(), "parents.json");
15838
+ var VIEWABLE_IMAGES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
15839
+ var MAX_INLINE_IMAGE = 5 * 1024 * 1024;
15840
+ var parentsFile = () => join6(stateRoot(), "parents.json");
15748
15841
  var processChannels = /* @__PURE__ */ new Map();
15749
15842
  var lastChannel;
15750
15843
  function savedParents() {
15751
15844
  try {
15752
- return existsSync5(parentsFile()) ? JSON.parse(readFileSync5(parentsFile(), "utf8")) : {};
15845
+ return existsSync6(parentsFile()) ? JSON.parse(readFileSync6(parentsFile(), "utf8")) : {};
15753
15846
  } catch {
15754
15847
  return {};
15755
15848
  }
@@ -15920,6 +16013,21 @@ ${JSON.stringify(decision)}`, decision);
15920
16013
 
15921
16014
  ${JSON.stringify(decision)}`, decision);
15922
16015
  }
16016
+ if (name === "get_attachment") {
16017
+ const decisionId = String(args.decision_id);
16018
+ const decision = await api.call("GET", `/decisions/${encodeURIComponent(decisionId)}`);
16019
+ const list = decision.answer?.attachments ?? [];
16020
+ const index = list.findIndex((a) => a.id === args.attachment_id);
16021
+ if (index < 0) return text(`Approval Box: ${decisionId} \u306E\u7B54\u3048\u306B\u3001\u305D\u306E\u6DFB\u4ED8\u306F\u3042\u308A\u307E\u305B\u3093\u3002get_decision \u306E answer.attachments \u3092\u78BA\u304B\u3081\u3066\u304F\u3060\u3055\u3044\u3002`, { error: "not_found" }, true);
16022
+ const meta = list[index];
16023
+ const path = await saveAttachment(api, decisionId, meta, index);
16024
+ const head = `${decisionId} \u306E\u6DFB\u4ED8 ${index + 1}: ${meta.name}\uFF08${meta.content_type}\u3001${meta.size}\u30D0\u30A4\u30C8\uFF09\u3092\u4FDD\u5B58\u3057\u307E\u3057\u305F: ${path}`;
16025
+ const structured = { decision_id: decisionId, attachment: meta, path };
16026
+ if (VIEWABLE_IMAGES.includes(meta.content_type) && meta.size <= MAX_INLINE_IMAGE) {
16027
+ return { content: [{ type: "text", text: head }, { type: "image", data: readFileSync6(path).toString("base64"), mimeType: meta.content_type }], structuredContent: structured };
16028
+ }
16029
+ return text(head, structured);
16030
+ }
15923
16031
  if (name === "setup_test") {
15924
16032
  const channelId = await channelFor(harness, clientName, request.params._meta);
15925
16033
  lastChannel = channelId;
@@ -15988,13 +16096,13 @@ async function ask(question) {
15988
16096
  }
15989
16097
  }
15990
16098
  function installRuntime() {
15991
- const target = join6(home(), "runtime", VERSION);
16099
+ const target = join7(home(), "runtime", VERSION);
15992
16100
  if (runtimeDir() !== target) {
15993
- rmSync2(target, { recursive: true, force: true });
15994
- mkdirSync4(target, { recursive: true });
16101
+ rmSync3(target, { recursive: true, force: true });
16102
+ mkdirSync5(target, { recursive: true });
15995
16103
  cpSync(runtimeDir(), target, { recursive: true });
15996
16104
  }
15997
- return join6(target, "cli.mjs");
16105
+ return join7(target, "cli.mjs");
15998
16106
  }
15999
16107
  function selectedTargets() {
16000
16108
  const only = option("only");
@@ -16025,7 +16133,7 @@ async function pair(server, targets) {
16025
16133
  if (result.status === "claimed" && result.token) return { token: result.token, connection_id: result.connection_id };
16026
16134
  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");
16027
16135
  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");
16028
- await new Promise((resolve) => setTimeout(resolve, 2e3));
16136
+ await new Promise((resolve2) => setTimeout(resolve2, 2e3));
16029
16137
  }
16030
16138
  }
16031
16139
  async function waitForChecks(api, clients) {
@@ -16049,7 +16157,7 @@ async function waitForChecks(api, clients) {
16049
16157
  out("\n\u3059\u3079\u3066\u306EAI\u3067\u78BA\u8A8D\u304C\u6E08\u307F\u307E\u3057\u305F\u3002Approval Box\u3092\u4F7F\u3048\u307E\u3059\u3002");
16050
16158
  return;
16051
16159
  }
16052
- await new Promise((resolve) => setTimeout(resolve, 3e3));
16160
+ await new Promise((resolve2) => setTimeout(resolve2, 3e3));
16053
16161
  }
16054
16162
  }
16055
16163
  async function setup() {
@@ -16076,7 +16184,8 @@ async function setup() {
16076
16184
  out(` \u300C${INSTRUCTION_TEXT}\u300D`);
16077
16185
  for (const target of targets) {
16078
16186
  const file = instructionsFileOf(target);
16079
- out(` ${LABEL[target]}: ${file ?? "\u4F1A\u8A71\u306E\u59CB\u307E\u308A\u306Ehook\uFF08hooks.json \u306E sessionStart\uFF09\u3067\u6E21\u3057\u307E\u3059"}`);
16187
+ const link = file ? managedElsewhere(file) : null;
16188
+ 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}`);
16080
16189
  }
16081
16190
  const instructions = !flag("no-instructions") && (flag("yes") || !/^n(o)?$/i.test(await ask("\u8DB3\u3057\u307E\u3059\u304B\uFF1F [Y/n] ")));
16082
16191
  const cli = installRuntime();
@@ -16109,6 +16218,7 @@ async function registerTargets() {
16109
16218
  const result = await register(target, { instructions: flag("instructions") });
16110
16219
  const extra = result.steer ? `\uFF08${STEER_LABEL[result.steer.split(":")[0] ?? ""] ?? `\u4F5C\u696D\u4E2D\u306E\u5272\u308A\u8FBC\u307F: ${result.steer}`}\uFF09` : "";
16111
16220
  out(` ${LABEL[target]}: ${result.status === "registered" ? "\u767B\u9332\u3057\u307E\u3057\u305F" : `\u5931\u6557 \u2014 ${result.detail}`}${extra}`);
16221
+ if (result.note) out(` \u30FB${result.note}`);
16112
16222
  if (result.status === "failed") failed = true;
16113
16223
  }
16114
16224
  ensureDaemon();
@@ -16138,7 +16248,7 @@ async function uninstall() {
16138
16248
  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}`);
16139
16249
  }
16140
16250
  }
16141
- rmSync2(join6(home(), "config.json"), { force: true });
16251
+ rmSync3(join7(home(), "config.json"), { force: true });
16142
16252
  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");
16143
16253
  }
16144
16254
  async function status() {
@@ -16176,14 +16286,18 @@ async function doctor() {
16176
16286
  problems.push(error instanceof ServerError && error.status === 401 ? "\u3053\u306E\u7AEF\u672B\u306E\u63A5\u7D9A\u304C\u5916\u3055\u308C\u3066\u3044\u307E\u3059 \u2192 npx approval-box setup" : `\u30B5\u30FC\u30D0\u30FC\u306B\u3064\u306A\u304C\u308A\u307E\u305B\u3093: ${error.message}`);
16177
16287
  }
16178
16288
  }
16179
- if (!existsSync6(process.execPath)) problems.push(`node \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\uFF08${process.execPath}\uFF09`);
16289
+ if (!existsSync7(process.execPath)) problems.push(`node \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\uFF08${process.execPath}\uFF09`);
16180
16290
  for (const target of HARNESSES) {
16181
16291
  if (!detect(target)) continue;
16182
16292
  const r = registered(target);
16183
16293
  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}`);
16184
16294
  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}`);
16185
- if (r.entry && !existsSync6(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`);
16186
- 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}`);
16295
+ 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`);
16296
+ if (r.mcp && r.instructions === false) {
16297
+ const file = instructionsFileOf(target);
16298
+ const link = file ? managedElsewhere(file) : null;
16299
+ 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}`);
16300
+ }
16187
16301
  }
16188
16302
  for (const hint of hints) out(`\u30FB${hint}`);
16189
16303
  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.8",
3
+ "version": "0.1.10",
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",