approval-box 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/cli.mjs +79 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,3 +50,7 @@ Requires Node.js 20 or later. License: MIT.
|
|
|
50
50
|
|
|
51
51
|
この機能はコネクタ0.1.13以降と対応サーバーを使います。会話を開き直しても、
|
|
52
52
|
再開を指定するまでは元の会話が配送先です。答えは `get_decision` でも取得できます。
|
|
53
|
+
|
|
54
|
+
### 申請に画像を添える
|
|
55
|
+
|
|
56
|
+
`request_decision` に `image_paths: ["/absolute/path/screenshot.png"]` を指定すると、AIの画像を本文下に添えて申請します。10件、単体20MiB、合計50MiBまで。全画像を上げてから申請と通知を確定します。最初の確認の札を付けた出し直しでも同じ画像パスを渡してください。`amend_decision.changes.image_paths` は全置換、空配列で削除です。回答添付とは別の欄です。リモートMCPは `upload_request_image` のIDを `request_attachment_ids` に渡します。
|
package/dist/cli.mjs
CHANGED
|
@@ -1336,11 +1336,11 @@ var require_dijkstra = __commonJS({
|
|
|
1336
1336
|
var predecessors = {};
|
|
1337
1337
|
var costs = {};
|
|
1338
1338
|
costs[s] = 0;
|
|
1339
|
-
var
|
|
1340
|
-
|
|
1339
|
+
var open2 = dijkstra.PriorityQueue.make();
|
|
1340
|
+
open2.push(s, 0);
|
|
1341
1341
|
var closest, u, v, cost_of_s_to_u, adjacent_nodes, cost_of_e, cost_of_s_to_u_plus_cost_of_e, cost_of_s_to_v, first_visit;
|
|
1342
|
-
while (!
|
|
1343
|
-
closest =
|
|
1342
|
+
while (!open2.empty()) {
|
|
1343
|
+
closest = open2.pop();
|
|
1344
1344
|
u = closest.value;
|
|
1345
1345
|
cost_of_s_to_u = closest.cost;
|
|
1346
1346
|
adjacent_nodes = graph[u] || {};
|
|
@@ -1352,7 +1352,7 @@ var require_dijkstra = __commonJS({
|
|
|
1352
1352
|
first_visit = typeof costs[v] === "undefined";
|
|
1353
1353
|
if (first_visit || cost_of_s_to_v > cost_of_s_to_u_plus_cost_of_e) {
|
|
1354
1354
|
costs[v] = cost_of_s_to_u_plus_cost_of_e;
|
|
1355
|
-
|
|
1355
|
+
open2.push(v, cost_of_s_to_u_plus_cost_of_e);
|
|
1356
1356
|
predecessors[v] = u;
|
|
1357
1357
|
}
|
|
1358
1358
|
}
|
|
@@ -11814,6 +11814,17 @@ var Api = class {
|
|
|
11814
11814
|
}
|
|
11815
11815
|
return json;
|
|
11816
11816
|
}
|
|
11817
|
+
/** 申請前画像をバイナリで上げる。 */
|
|
11818
|
+
async uploadRequestImage(data, name, contentType) {
|
|
11819
|
+
const response = await fetch(this.url(`/request-images?name=${encodeURIComponent(name)}`), {
|
|
11820
|
+
method: "POST",
|
|
11821
|
+
headers: { authorization: `Bearer ${this.token}`, "content-type": contentType },
|
|
11822
|
+
body: new Uint8Array(data)
|
|
11823
|
+
});
|
|
11824
|
+
const json = await response.json();
|
|
11825
|
+
if (!response.ok) throw new ServerError(response.status, json.error?.code ?? "internal", json.error?.message ?? `HTTP ${response.status}`, json);
|
|
11826
|
+
return json;
|
|
11827
|
+
}
|
|
11817
11828
|
/** ファイルを取る(添付)。誤りはJSONの本文から読む。 */
|
|
11818
11829
|
async download(path) {
|
|
11819
11830
|
let response;
|
|
@@ -12192,8 +12203,8 @@ async function runDaemon() {
|
|
|
12192
12203
|
await sync();
|
|
12193
12204
|
try {
|
|
12194
12205
|
const { items } = await api.call("GET", "/decisions");
|
|
12195
|
-
const
|
|
12196
|
-
if (
|
|
12206
|
+
const open2 = items.some((d) => d.status === "pending" || d.status === "held");
|
|
12207
|
+
if (open2 || Object.keys(journal).length) idleSince = Date.now();
|
|
12197
12208
|
else if (Date.now() - idleSince > IDLE_EXIT_MS) stop();
|
|
12198
12209
|
} catch {
|
|
12199
12210
|
idleSince = Date.now();
|
|
@@ -15826,10 +15837,53 @@ var StdioServerTransport = class {
|
|
|
15826
15837
|
|
|
15827
15838
|
// src/mcp.ts
|
|
15828
15839
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
|
|
15829
|
-
import { basename as
|
|
15840
|
+
import { basename as basename3, join as join8 } from "node:path";
|
|
15841
|
+
|
|
15842
|
+
// src/request-images.ts
|
|
15843
|
+
import { open } from "node:fs/promises";
|
|
15844
|
+
import { basename as basename2 } from "node:path";
|
|
15845
|
+
var MAX_FILE = 20 * 1024 * 1024;
|
|
15846
|
+
var MAX_TOTAL = 50 * 1024 * 1024;
|
|
15847
|
+
function imageType(b) {
|
|
15848
|
+
if (b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) return "image/png";
|
|
15849
|
+
if (b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255) return "image/jpeg";
|
|
15850
|
+
if (b.subarray(0, 4).toString() === "GIF8") return "image/gif";
|
|
15851
|
+
if (b.subarray(0, 4).toString() === "RIFF" && b.subarray(8, 12).toString() === "WEBP") return "image/webp";
|
|
15852
|
+
if (b.subarray(4, 8).toString() === "ftyp" && ["heic", "heix", "hevc", "hevx", "heim", "heis", "mif1", "msf1", "heif"].includes(b.subarray(8, 12).toString())) return "image/heic";
|
|
15853
|
+
throw new Error("\u7533\u8ACB\u306B\u4ED8\u3051\u3089\u308C\u308B\u306E\u306FJPEG\u30FBPNG\u30FBGIF\u30FBWebP\u30FBHEIC\u753B\u50CF\u3067\u3059\u3002");
|
|
15854
|
+
}
|
|
15855
|
+
async function uploadImagePaths(api, paths) {
|
|
15856
|
+
if (!Array.isArray(paths) || paths.length > 10 || paths.some((p) => typeof p !== "string" || !p.trim())) throw new Error("image_paths\u306F10\u4EF6\u307E\u3067\u306E\u753B\u50CF\u30D5\u30A1\u30A4\u30EB\u30D1\u30B9\u914D\u5217\u3067\u3059\u3002");
|
|
15857
|
+
const images = [];
|
|
15858
|
+
let total = 0;
|
|
15859
|
+
for (const path of paths) {
|
|
15860
|
+
const file = await open(path, "r");
|
|
15861
|
+
try {
|
|
15862
|
+
const stat = await file.stat();
|
|
15863
|
+
if (!stat.isFile() || !stat.size || stat.size > MAX_FILE) throw new Error(`${basename2(path)}: \u7A7A\u3067\u306A\u3044\u901A\u5E38\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF0820MiB\u307E\u3067\uFF09\u3002`);
|
|
15864
|
+
total += stat.size;
|
|
15865
|
+
if (total > MAX_TOTAL) throw new Error("\u7533\u8ACB\u753B\u50CF\u306F\u5408\u8A0850MiB\u307E\u3067\u3067\u3059\u3002");
|
|
15866
|
+
const data = Buffer.alloc(stat.size + 1);
|
|
15867
|
+
let n = 0;
|
|
15868
|
+
while (n < data.length) {
|
|
15869
|
+
const r = await file.read(data, n, data.length - n, n);
|
|
15870
|
+
if (!r.bytesRead) break;
|
|
15871
|
+
n += r.bytesRead;
|
|
15872
|
+
}
|
|
15873
|
+
if (n !== stat.size) throw new Error(`${basename2(path)}: \u8AAD\u307F\u53D6\u308A\u4E2D\u306B\u753B\u50CF\u304C\u5909\u308F\u308A\u307E\u3057\u305F\u3002`);
|
|
15874
|
+
const bytes = data.subarray(0, n);
|
|
15875
|
+
images.push({ name: basename2(path), data: bytes, type: imageType(bytes) });
|
|
15876
|
+
} finally {
|
|
15877
|
+
await file.close();
|
|
15878
|
+
}
|
|
15879
|
+
}
|
|
15880
|
+
const ids = [];
|
|
15881
|
+
for (const image of images) ids.push((await api.uploadRequestImage(image.data, image.name, image.type)).id);
|
|
15882
|
+
return ids;
|
|
15883
|
+
}
|
|
15830
15884
|
|
|
15831
15885
|
// src/version.ts
|
|
15832
|
-
var VERSION = true ? "0.1.
|
|
15886
|
+
var VERSION = true ? "0.1.20" : "0.0.0-dev";
|
|
15833
15887
|
function osName() {
|
|
15834
15888
|
return process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : process.platform;
|
|
15835
15889
|
}
|
|
@@ -15872,6 +15926,8 @@ var TOOLS = [
|
|
|
15872
15926
|
deadline: { type: "string", description: "\u671F\u9650\uFF08ISO 8601\uFF09" },
|
|
15873
15927
|
session_label: { type: "string", description: "\u3069\u306E\u4F5C\u696D\u306E\u8A71\u304B\uFF08\u4F8B: \u30EA\u30DD\u30B8\u30C8\u30EA\u540D / \u4F5C\u696D\u540D\uFF09\u3002\u7701\u7565\u3059\u308B\u3068\u4F5C\u696D\u30D5\u30A9\u30EB\u30C0\u540D" },
|
|
15874
15928
|
distinct_reason: { type: "string", description: "\u540C\u3058\u4EF6\u540D\u306E\u672A\u6C7A\u304C\u3042\u308B\u306E\u306B\u5225\u4EF6\u3068\u3057\u3066\u51FA\u3059\u6642\u3060\u3051\u3001\u305D\u306E\u7406\u7531" },
|
|
15929
|
+
image_paths: { type: "array", items: { type: "string" }, maxItems: 10, description: "\u7533\u8ACB\u306B\u6DFB\u3048\u308B\u30ED\u30FC\u30AB\u30EB\u753B\u50CF\u30D1\u30B9\u300210\u4EF6/\u5358\u4F5320MiB/\u5408\u8A0850MiB\u307E\u3067\u3002\u30D1\u30B9\u81EA\u4F53\u306F\u4EBA\u3078\u9001\u3089\u306A\u3044" },
|
|
15930
|
+
request_attachment_ids: { type: "array", items: { type: "string" }, maxItems: 10 },
|
|
15875
15931
|
check_token: { type: "string", description: "1\u56DE\u76EE\u306E request_decision \u3067\u8FD4\u3063\u305F\u78BA\u8A8D\u306E\u672D\u3002\u4E00\u89A7\u3092\u78BA\u304B\u3081\u3001\u76F4\u3059\u30FB\u53D6\u308A\u4E0B\u3052\u308B\u7533\u8ACB\u304C\u7121\u3044\u3068\u5224\u65AD\u3057\u305F\u6642\u306B\u4ED8\u3051\u308B" }
|
|
15876
15932
|
},
|
|
15877
15933
|
required: ["title", "options"],
|
|
@@ -15890,6 +15946,8 @@ var TOOLS = [
|
|
|
15890
15946
|
changes: {
|
|
15891
15947
|
type: "object",
|
|
15892
15948
|
properties: {
|
|
15949
|
+
image_paths: { type: "array", items: { type: "string" }, maxItems: 10 },
|
|
15950
|
+
request_attachment_ids: { type: "array", items: { type: "string" }, maxItems: 10 },
|
|
15893
15951
|
title: { type: "string" },
|
|
15894
15952
|
context: { type: "string" },
|
|
15895
15953
|
options: { type: "array", items: optionSchema, minItems: 2, maxItems: 6 },
|
|
@@ -16046,10 +16104,14 @@ ${JSON.stringify({ items })}`, { items });
|
|
|
16046
16104
|
if (name === "request_decision") {
|
|
16047
16105
|
const channelId = await channelFor(harness, clientName, request.params._meta);
|
|
16048
16106
|
lastChannel = channelId;
|
|
16107
|
+
const { image_paths, ...rest } = args;
|
|
16108
|
+
if (image_paths !== void 0 && rest.request_attachment_ids !== void 0) throw new Error("image_paths\u3068request_attachment_ids\u306F\u7247\u65B9\u3060\u3051\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
16109
|
+
const request_attachment_ids = image_paths !== void 0 ? await uploadImagePaths(api, image_paths) : rest.request_attachment_ids;
|
|
16049
16110
|
const body = {
|
|
16050
|
-
...
|
|
16111
|
+
...rest,
|
|
16112
|
+
...request_attachment_ids !== void 0 ? { request_attachment_ids } : {},
|
|
16051
16113
|
client: CLIENT_OF[harness],
|
|
16052
|
-
session_label: args.session_label || `${
|
|
16114
|
+
session_label: args.session_label || `${basename3(process.cwd())} (${CLIENT_OF[harness]})`,
|
|
16053
16115
|
route: { channel_id: channelId, harness }
|
|
16054
16116
|
};
|
|
16055
16117
|
try {
|
|
@@ -16088,6 +16150,10 @@ ${JSON.stringify({ error: "duplicate_suspected", existing })}`, { error: "duplic
|
|
|
16088
16150
|
}
|
|
16089
16151
|
if (name === "amend_decision") {
|
|
16090
16152
|
const { decision_id, ...rest } = args;
|
|
16153
|
+
const { image_paths, ...changes } = rest.changes ?? {};
|
|
16154
|
+
if (image_paths !== void 0 && changes.request_attachment_ids !== void 0) throw new Error("image_paths\u3068request_attachment_ids\u306F\u7247\u65B9\u3060\u3051\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
16155
|
+
if (image_paths !== void 0) changes.request_attachment_ids = await uploadImagePaths(api, image_paths);
|
|
16156
|
+
rest.changes = changes;
|
|
16091
16157
|
const channelId = await channelFor(harness, clientName, request.params._meta);
|
|
16092
16158
|
const decision = await api.call("POST", `/decisions/${encodeURIComponent(String(decision_id))}/amend`, { ...rest, route: { channel_id: channelId, harness } });
|
|
16093
16159
|
lastChannel = channelId;
|
|
@@ -16122,7 +16188,7 @@ ${JSON.stringify(decision)}`, decision);
|
|
|
16122
16188
|
if (name === "get_attachment") {
|
|
16123
16189
|
const decisionId = String(args.decision_id);
|
|
16124
16190
|
const decision = await api.call("GET", `/decisions/${encodeURIComponent(decisionId)}`);
|
|
16125
|
-
const list = decision.answer?.attachments ?? [];
|
|
16191
|
+
const list = [...decision.request_attachments ?? [], ...decision.answer?.attachments ?? []];
|
|
16126
16192
|
const index = list.findIndex((a) => a.id === args.attachment_id);
|
|
16127
16193
|
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);
|
|
16128
16194
|
const meta = list[index];
|
|
@@ -16140,7 +16206,7 @@ ${JSON.stringify(decision)}`, decision);
|
|
|
16140
16206
|
const decision = await api.call("POST", "/setup-test", {
|
|
16141
16207
|
client: CLIENT_OF[harness],
|
|
16142
16208
|
os: osName(),
|
|
16143
|
-
session_label: `\u63A5\u7D9A\u30C6\u30B9\u30C8 ${
|
|
16209
|
+
session_label: `\u63A5\u7D9A\u30C6\u30B9\u30C8 ${basename3(process.cwd())} (${CLIENT_OF[harness]})`,
|
|
16144
16210
|
route: { channel_id: channelId, harness }
|
|
16145
16211
|
});
|
|
16146
16212
|
const message = `\u63A5\u7D9A\u30C6\u30B9\u30C8\u306E\u7533\u8ACB\u3092\u9001\u308A\u307E\u3057\u305F\uFF08${decision.decision_id}\uFF09\u3002\u5229\u7528\u8005\u306B\u30A2\u30D7\u30EA\u304BWeb\u7248\u3067\u7B54\u3048\u308B\u3088\u3046\u4F1D\u3048\u3066\u304F\u3060\u3055\u3044\u3002\u7B54\u3048\u304C\u3053\u306E\u4F1A\u8A71\u3078\u5C4A\u3044\u305F\u3089\u3001\u305D\u3053\u306B\u66F8\u304B\u308C\u305F\u78BA\u8A8D\u30B3\u30FC\u30C9\u3067 confirm_setup_test \u3092\u547C\u3093\u3067\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.20",
|
|
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",
|