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.
- package/README.md +2 -2
- package/dist/cli.mjs +220 -106
- 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
|
|
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(
|
|
4438
|
+
return new Promise(function(resolve2, reject) {
|
|
4439
4439
|
try {
|
|
4440
4440
|
const data = QRCode2.create(text2, opts);
|
|
4441
|
-
|
|
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(
|
|
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) :
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
11860
|
-
import { join as
|
|
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
|
|
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
|
|
11927
|
+
return join3(runtimeDir(), kind === "cli" ? "cli.mjs" : HOOK_FILES[kind]);
|
|
11871
11928
|
}
|
|
11872
11929
|
|
|
11873
11930
|
// src/daemon.ts
|
|
11874
|
-
var lockFile = () =>
|
|
11875
|
-
var journalFile = () =>
|
|
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 (!
|
|
11943
|
+
if (!existsSync3(lockFile())) return { running: false };
|
|
11887
11944
|
try {
|
|
11888
|
-
const lock = JSON.parse(
|
|
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
|
-
|
|
11906
|
-
const log = openSync(
|
|
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((
|
|
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 =
|
|
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,
|
|
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(
|
|
12052
|
-
if (lock.pid === process.pid)
|
|
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
|
|
12061
|
-
import { homedir } from "node:os";
|
|
12062
|
-
import { delimiter, dirname as dirname4, join as
|
|
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
|
|
12066
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
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 (!
|
|
12089
|
-
const rest =
|
|
12090
|
-
|
|
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 ??
|
|
12099
|
-
var claudeJson = () => process.env.CLAUDE_CONFIG_DIR ?
|
|
12100
|
-
var cursorDir = () => process.env.CURSOR_HOME ??
|
|
12101
|
-
var codexHome = () => process.env.CODEX_HOME ??
|
|
12102
|
-
var grokDir = () => process.env.GROK_HOME ??
|
|
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(),
|
|
12105
|
-
if (target === "cursor") return [
|
|
12106
|
-
if (target === "codex") return [
|
|
12107
|
-
return [
|
|
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
|
|
12111
|
-
if (target === "codex") return
|
|
12112
|
-
if (target === "grok") return
|
|
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 &&
|
|
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
|
|
12124
|
-
if (target === "codex") return
|
|
12125
|
-
if (target === "cursor") return
|
|
12126
|
-
return
|
|
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 = () =>
|
|
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 (!
|
|
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 =
|
|
12151
|
-
if (!
|
|
12152
|
-
const target =
|
|
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 =
|
|
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(
|
|
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 (!
|
|
12167
|
-
return JSON.parse(
|
|
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 (
|
|
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,
|
|
12283
|
+
mergeClaudeParentHooks(PROFILE, join5(claudeDir(), "settings.json"), hook("claude"));
|
|
12198
12284
|
return { target, status: "registered" };
|
|
12199
12285
|
}
|
|
12200
12286
|
if (target === "cursor") {
|
|
12201
|
-
setMcpJson(
|
|
12202
|
-
mergeCursorParentHooks(PROFILE,
|
|
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 =
|
|
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(
|
|
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 (
|
|
12238
|
-
removeClaudeParentHooks(PROFILE,
|
|
12323
|
+
if (existsSync5(claudeJson())) setMcpJson(claudeJson(), null);
|
|
12324
|
+
removeClaudeParentHooks(PROFILE, join5(claudeDir(), "settings.json"));
|
|
12239
12325
|
} else if (target === "cursor") {
|
|
12240
|
-
if (
|
|
12241
|
-
removeCursorParentHooks(PROFILE,
|
|
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 (
|
|
12331
|
+
if (existsSync5(join5(codexHome(), "config.toml"))) {
|
|
12246
12332
|
await withCodexConfig(async (request) => {
|
|
12247
|
-
await request("config/batchWrite", { filePath:
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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(
|
|
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((
|
|
15722
|
+
return new Promise((resolve2) => {
|
|
15637
15723
|
const json = serializeMessage(message);
|
|
15638
15724
|
if (this._stdout.write(json)) {
|
|
15639
|
-
|
|
15725
|
+
resolve2();
|
|
15640
15726
|
} else {
|
|
15641
|
-
this._stdout.once("drain",
|
|
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
|
|
15649
|
-
import { basename as basename2, join as
|
|
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.
|
|
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
|
|
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
|
|
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 =
|
|
16099
|
+
const target = join7(home(), "runtime", VERSION);
|
|
15992
16100
|
if (runtimeDir() !== target) {
|
|
15993
|
-
|
|
15994
|
-
|
|
16101
|
+
rmSync3(target, { recursive: true, force: true });
|
|
16102
|
+
mkdirSync5(target, { recursive: true });
|
|
15995
16103
|
cpSync(runtimeDir(), target, { recursive: true });
|
|
15996
16104
|
}
|
|
15997
|
-
return
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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 && !
|
|
16186
|
-
if (r.mcp && r.instructions === false)
|
|
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.
|
|
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",
|