approval-box 0.1.23 → 0.1.24

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.
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
28
  SOFTWARE.
29
29
  ```
30
30
 
31
- ## aiterm-steer-delivery@0.4.4 (MIT)
31
+ ## aiterm-steer-delivery@0.4.5 (MIT)
32
32
 
33
33
  ```
34
34
  MIT License
@@ -2,7 +2,7 @@ import { createRequire as __kbCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  PROFILE,
4
4
  runClaudeHookMain
5
- } from "./chunks/chunk-SN7I52NL.mjs";
5
+ } from "./chunks/chunk-HBSDWG62.mjs";
6
6
 
7
7
  // src/hooks/claude.ts
8
8
  await runClaudeHookMain(PROFILE);
@@ -5,7 +5,7 @@ import {
5
5
  handleCursorChannelHook,
6
6
  handleCursorHook,
7
7
  withoutBom
8
- } from "./chunks/chunk-SN7I52NL.mjs";
8
+ } from "./chunks/chunk-HBSDWG62.mjs";
9
9
 
10
10
  // src/cursor-binding.ts
11
11
  async function bindCursorResult(profile, raw) {
@@ -2,7 +2,7 @@ import { createRequire as __kbCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  PROFILE,
4
4
  runChannelReceiveMain
5
- } from "./chunks/chunk-SN7I52NL.mjs";
5
+ } from "./chunks/chunk-HBSDWG62.mjs";
6
6
 
7
7
  // src/hooks/receive.ts
8
8
  await runChannelReceiveMain(PROFILE);
@@ -20200,6 +20200,136 @@ function hookOwnerProcess(rows, pid) {
20200
20200
  current = byPid.get(current.parent_pid);
20201
20201
  return current;
20202
20202
  }
20203
+ function processExists(pid) {
20204
+ if (!Number.isSafeInteger(pid) || pid <= 0)
20205
+ return false;
20206
+ try {
20207
+ process.kill(pid, 0);
20208
+ return true;
20209
+ } catch (error62) {
20210
+ return error62.code === "EPERM";
20211
+ }
20212
+ }
20213
+ function windowsJson(script, failure2) {
20214
+ const source = ["$ErrorActionPreference='Stop'", "[Console]::OutputEncoding=[Text.UTF8Encoding]::new($false)", script].join("\n");
20215
+ const result = spawnSync3(resolveWindowsPowerShell7(), ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(source, "utf16le").toString("base64")], {
20216
+ encoding: "utf8",
20217
+ timeout: 15e3,
20218
+ maxBuffer: 16 * 1024 * 1024,
20219
+ windowsHide: true
20220
+ });
20221
+ if (result.error || result.status !== 0)
20222
+ throw new ProcessTableError(failure2);
20223
+ try {
20224
+ return JSON.parse(result.stdout);
20225
+ } catch {
20226
+ throw new ProcessTableError(failure2);
20227
+ }
20228
+ }
20229
+ function assertPids(pids) {
20230
+ const wanted = [...new Set(pids)];
20231
+ if (wanted.some((pid) => !Number.isSafeInteger(pid) || pid < 0))
20232
+ throw new ProcessTableError("process\u7167\u4F1A\u306Epid\u304C\u4E0D\u6B63\u3067\u3059");
20233
+ return wanted;
20234
+ }
20235
+ var WINDOWS_STARTED = [
20236
+ "function Started($p) {",
20237
+ " try { return $p.StartTime.ToUniversalTime().ToString('o') }",
20238
+ " catch [InvalidOperationException] { return '' }",
20239
+ ` catch { $w = Get-CimInstance Win32_Process -Filter "ProcessId=$($p.Id)" -Property CreationDate; if ($null -eq $w) { return '' }`,
20240
+ " if ($null -eq $w.CreationDate) { return $null }; return $w.CreationDate.ToUniversalTime().ToString('o') }",
20241
+ "}"
20242
+ ].join("\n");
20243
+ function readProcessIdentities(pids) {
20244
+ const wanted = assertPids(pids).filter(processExists);
20245
+ if (wanted.length === 0)
20246
+ return [];
20247
+ if (!isWin2) {
20248
+ const result = spawnSync3("/bin/ps", ["-o", "pid=,lstart=", "-p", wanted.join(",")], {
20249
+ encoding: "utf8",
20250
+ env: { ...process.env, LC_ALL: "C" },
20251
+ timeout: 1e4
20252
+ });
20253
+ if (result.error || result.status !== 0 && !(result.status === 1 && !result.stdout.trim())) {
20254
+ throw new ProcessTableError("OS\u306Eprocess\u4E00\u89A7\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093");
20255
+ }
20256
+ return result.stdout.split("\n").filter((line) => line.trim()).map((line) => {
20257
+ const match = /^\s*(\d+)\s+(\S+\s+\S+\s+\d+\s+\S+\s+\d+)\s*$/.exec(line);
20258
+ if (!match)
20259
+ throw new ProcessTableError("process\u4E00\u89A7\u306E\u5F62\u5F0F\u3092\u8A8D\u8B58\u3067\u304D\u307E\u305B\u3093");
20260
+ return { pid: Number(match[1]), started_identity: match[2].trim() };
20261
+ });
20262
+ }
20263
+ const rows = windowsJson([
20264
+ WINDOWS_STARTED,
20265
+ "$rows=@()",
20266
+ `foreach ($id in @(${wanted.join(",")})) {`,
20267
+ " try { $p=[Diagnostics.Process]::GetProcessById($id) } catch [ArgumentException] { continue }",
20268
+ " $started=Started $p",
20269
+ " if ($started -eq '') { continue }",
20270
+ " $rows += [ordered]@{ pid=[int]$id; started_identity=$started }",
20271
+ "}",
20272
+ "ConvertTo-Json -Compress -InputObject $rows"
20273
+ ].join("\n"), "Windows\u306Eprocess\u306E\u958B\u59CB\u6642\u523B\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093");
20274
+ if (!Array.isArray(rows))
20275
+ throw new ProcessTableError("Windows process\u306E\u8AAD\u307F\u304C\u914D\u5217\u3067\u306F\u3042\u308A\u307E\u305B\u3093");
20276
+ return rows.map((row) => {
20277
+ if (!row || !Number.isSafeInteger(row.pid))
20278
+ throw new ProcessTableError("Windows process\u306E\u8AAD\u307F\u306Efield\u304C\u4E0D\u6B63\u3067\u3059");
20279
+ if (row.started_identity === null)
20280
+ return { pid: row.pid, started_identity: null };
20281
+ if (typeof row.started_identity !== "string" || !Number.isFinite(Date.parse(row.started_identity)))
20282
+ throw new ProcessTableError("Windows process\u306E\u8AAD\u307F\u306Efield\u304C\u4E0D\u6B63\u3067\u3059");
20283
+ return { pid: row.pid, started_identity: new Date(row.started_identity).toISOString() };
20284
+ });
20285
+ }
20286
+ function processIsRunning(pid, startedIdentity) {
20287
+ return processExists(pid) && readProcessIdentities([pid])[0]?.started_identity === startedIdentity;
20288
+ }
20289
+ function readProcessLineage(pid, depth = 8) {
20290
+ if (!isWin2)
20291
+ return readRuntimeProcesses();
20292
+ if (!Number.isSafeInteger(pid) || pid <= 0 || !Number.isSafeInteger(depth) || depth <= 0)
20293
+ throw new ProcessTableError("process\u7167\u4F1A\u306Epid\u304C\u4E0D\u6B63\u3067\u3059");
20294
+ let rows;
20295
+ try {
20296
+ rows = windowsJson([
20297
+ "$rows=@(); $child=$null",
20298
+ `$id=${pid}`,
20299
+ `for ($i=0; $i -lt ${depth} -and $id -gt 0; $i++) {`,
20300
+ " try { $p=Get-Process -Id $id; $started=$p.StartTime.ToUniversalTime() } catch { break }",
20301
+ // Windowsの親PIDは親の終了後も残り、別のprocessへ使い回される。子より後に始まったprocessは親でない。
20302
+ " if ($null -ne $child -and $started -gt $child) { break }",
20303
+ " $parent=0; try { if ($null -ne $p.Parent) { $parent=[int]$p.Parent.Id } } catch { }",
20304
+ " $exe=''; try { $exe=[string]$p.Path } catch { }",
20305
+ " if (-not $exe) { $exe=[string]$p.ProcessName }",
20306
+ " $cpu=0.0; try { $cpu=[double]$p.TotalProcessorTime.TotalSeconds } catch { }",
20307
+ " $rows += [ordered]@{ pid=[int]$p.Id; parent_pid=$parent; executable=$exe; started_identity=$started.ToString('o'); cpu_seconds=$cpu }",
20308
+ " $child=$started; $id=$parent",
20309
+ "}",
20310
+ "ConvertTo-Json -Compress -InputObject $rows"
20311
+ ].join("\n"), "Windows\u306Eprocess\u306E\u5148\u7956\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093");
20312
+ } catch {
20313
+ return readRuntimeProcesses();
20314
+ }
20315
+ if (!Array.isArray(rows) || rows[0]?.pid !== pid)
20316
+ return readRuntimeProcesses();
20317
+ return rows.map((row) => {
20318
+ if (!row || !Number.isSafeInteger(row.pid) || !Number.isSafeInteger(row.parent_pid) || typeof row.executable !== "string" || !Number.isFinite(row.cpu_seconds) || typeof row.started_identity !== "string" || !Number.isFinite(Date.parse(row.started_identity)))
20319
+ throw new ProcessTableError("Windows process\u306E\u8AAD\u307F\u306Efield\u304C\u4E0D\u6B63\u3067\u3059");
20320
+ return {
20321
+ pid: row.pid,
20322
+ parent_pid: row.parent_pid,
20323
+ process_group_id: null,
20324
+ stopped: null,
20325
+ executable: row.executable,
20326
+ started_identity: new Date(row.started_identity).toISOString(),
20327
+ command: row.executable,
20328
+ cpu_seconds: row.cpu_seconds,
20329
+ argv_digest: createHash2("sha256").update(row.executable).digest("hex")
20330
+ };
20331
+ });
20332
+ }
20203
20333
  function readRuntimeProcesses() {
20204
20334
  if (!isWin2) {
20205
20335
  const result2 = spawnSync3("/bin/ps", ["-axww", "-o", "pid=,ppid=,pgid=,stat=,lstart=,time=,command="], {
@@ -20506,7 +20636,7 @@ var claudeParentSchema = external_exports.object({
20506
20636
  hook_root: external_exports.string()
20507
20637
  }).strict();
20508
20638
  function processIdentity(pid) {
20509
- return readRuntimeProcesses().find((entry) => entry.pid === pid)?.started_identity;
20639
+ return readProcessIdentities([pid])[0]?.started_identity ?? void 0;
20510
20640
  }
20511
20641
  function directory(parent) {
20512
20642
  return path7.join(parent.hook_root, parent.request_id);
@@ -20530,8 +20660,7 @@ function assertOpen(parent) {
20530
20660
  }
20531
20661
  function prepareClaudeHookRequest(input2, root) {
20532
20662
  const event = external_exports.object({ session_id: external_exports.uuid(), tool_use_id: requestId, agent_id: external_exports.string().optional() }).parse(input2);
20533
- const rows = readRuntimeProcesses();
20534
- const owner = hookOwnerProcess(rows, process.ppid);
20663
+ const owner = hookOwnerProcess(readProcessLineage(process.ppid), process.ppid);
20535
20664
  if (!owner)
20536
20665
  throw new ClaudeDeliveryError("CLAUDE_PARENT_PROCESS_UNAVAILABLE", "hook\u3092\u8D77\u52D5\u3057\u305F\u89AAprocess\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093");
20537
20666
  const dir = path7.join(root, event.tool_use_id);
@@ -20544,13 +20673,14 @@ function prepareClaudeHookRequest(input2, root) {
20544
20673
  parent_started_identity: owner.started_identity
20545
20674
  });
20546
20675
  try {
20547
- sweepClaudeHookRequests(root, rows);
20676
+ sweepClaudeHookRequests(root, processIsRunning);
20548
20677
  } catch {
20549
20678
  }
20550
20679
  }
20551
20680
  var STALE_REQUEST_MS = 24 * 60 * 60 * 1e3;
20552
20681
  var REQUEST_FILES = /* @__PURE__ */ new Set(["request.json", "delivery.json", "answer.json", "hook.json", "sending.json", "emitted.json", "failed.json", "closed.json"]);
20553
20682
  function sweepClaudeHookRequests(root, rows, now = Date.now()) {
20683
+ const running = typeof rows === "function" ? rows : (pid, startedIdentity) => rows.find((row) => row.pid === pid)?.started_identity === startedIdentity;
20554
20684
  let entries;
20555
20685
  try {
20556
20686
  entries = fs5.readdirSync(root, { withFileTypes: true });
@@ -20576,7 +20706,7 @@ function sweepClaudeHookRequests(root, rows, now = Date.now()) {
20576
20706
  invocation = invocationSchema.parse(JSON.parse(fs5.readFileSync(path7.join(dir, "request.json"), "utf8")));
20577
20707
  } catch {
20578
20708
  }
20579
- if (invocation && rows.find((row) => row.pid === invocation.parent_pid)?.started_identity === invocation.parent_started_identity)
20709
+ if (invocation && running(invocation.parent_pid, invocation.parent_started_identity))
20580
20710
  continue;
20581
20711
  }
20582
20712
  fs5.rmSync(dir, { recursive: true, force: true });
@@ -20635,7 +20765,7 @@ function closeClaudeParentSession(input2, root) {
20635
20765
  throw failure2;
20636
20766
  }
20637
20767
  function assertParentAlive(invocation) {
20638
- if (processIdentity(invocation.parent_pid) !== invocation.parent_started_identity) {
20768
+ if (!processIsRunning(invocation.parent_pid, invocation.parent_started_identity)) {
20639
20769
  throw new ClaudeDeliveryError("CLAUDE_PARENT_PROCESS_CLOSED", "\u4F9D\u983C\u5143\u306EClaude process\u306F\u7D42\u4E86\u3057\u307E\u3057\u305F\u3002\u56DE\u7B54\u306F\u4FDD\u5B58\u3057\u305F\u307E\u307E\u3067\u3059");
20640
20770
  }
20641
20771
  }
@@ -21146,7 +21276,7 @@ function channelDir(profile, channelId) {
21146
21276
  return path9.join(channelRoot(profile), uuid3.parse(channelId));
21147
21277
  }
21148
21278
  function processIdentity2(pid) {
21149
- return readRuntimeProcesses().find((entry) => entry.pid === pid)?.started_identity;
21279
+ return readProcessIdentities([pid])[0]?.started_identity ?? void 0;
21150
21280
  }
21151
21281
  function mkdirs(...dirs) {
21152
21282
  for (const dir of dirs)
@@ -21255,7 +21385,7 @@ function channelDeliveryState(profile, channelId, deliveryId) {
21255
21385
  return "unknown";
21256
21386
  if (value.state === "withdrawn")
21257
21387
  return "withdrawn";
21258
- if (typeof value.pid === "number" && typeof value.started_identity === "string" && processIdentity2(value.pid) !== value.started_identity)
21388
+ if (typeof value.pid === "number" && typeof value.started_identity === "string" && !processIsRunning(value.pid, value.started_identity))
21259
21389
  return "unknown";
21260
21390
  return "sending";
21261
21391
  }
@@ -21380,7 +21510,7 @@ function acquireWaiter(index) {
21380
21510
  owner = JSON.parse(fs7.readFileSync(file2, "utf8"));
21381
21511
  } catch {
21382
21512
  }
21383
- if (typeof owner.pid === "number" && typeof owner.started_identity === "string" && processIdentity2(owner.pid) === owner.started_identity)
21513
+ if (typeof owner.pid === "number" && typeof owner.started_identity === "string" && processIsRunning(owner.pid, owner.started_identity))
21384
21514
  return null;
21385
21515
  try {
21386
21516
  fs7.renameSync(file2, path9.join(index, `waiter.${randomUUID3()}.stale`));
@@ -21401,9 +21531,10 @@ function releaseWaiter(index, identity) {
21401
21531
  }
21402
21532
  }
21403
21533
  function currentHookOwner() {
21404
- const owner = hookOwnerProcess(readRuntimeProcesses(), process.ppid);
21534
+ const owner = hookOwnerProcess(readProcessLineage(process.ppid), process.ppid);
21405
21535
  return owner ? { pid: owner.pid, started_identity: owner.started_identity } : null;
21406
21536
  }
21537
+ var WAITER_OWNER_IDENTITY_RECHECK_MS = 6e4;
21407
21538
  async function runClaudeChannelWaiter(profile, input2, emit, options = {}) {
21408
21539
  const event = external_exports.object({ session_id: external_exports.uuid() }).loose().parse(input2);
21409
21540
  if (!openClaudeChannels(profile, event.session_id).length)
@@ -21414,15 +21545,22 @@ async function runClaudeChannelWaiter(profile, input2, emit, options = {}) {
21414
21545
  if (!identity)
21415
21546
  return 0;
21416
21547
  const deadline = Date.now() + (options.wait_ms ?? 8634e4);
21548
+ const runtime = options.runtime ?? { exists: processExists, identity: processIdentity2 };
21549
+ const recheckMs = options.identity_recheck_ms ?? WAITER_OWNER_IDENTITY_RECHECK_MS;
21417
21550
  let lastParentCheck = 0;
21551
+ let lastIdentityCheck = null;
21418
21552
  try {
21419
21553
  for (; ; ) {
21420
21554
  const channels = openClaudeChannels(profile, event.session_id);
21421
21555
  if (!channels.length)
21422
21556
  return 0;
21423
- if (Date.now() - lastParentCheck >= 5e3) {
21557
+ if (Date.now() - lastParentCheck >= (options.owner_check_ms ?? 5e3)) {
21424
21558
  lastParentCheck = Date.now();
21425
- const alive = owner ? processIdentity2(owner.pid) === owner.started_identity : channels.some((channel) => processIdentity2(channel.claude.parent_pid) === channel.claude.parent_started_identity);
21559
+ const verify = lastIdentityCheck === null || lastParentCheck - lastIdentityCheck >= recheckMs;
21560
+ if (verify)
21561
+ lastIdentityCheck = lastParentCheck;
21562
+ const running = (pid, identity2) => runtime.exists(pid) && (!verify || runtime.identity(pid) === identity2);
21563
+ const alive = owner ? running(owner.pid, owner.started_identity) : channels.some((channel) => running(channel.claude.parent_pid, channel.claude.parent_started_identity));
21426
21564
  if (!alive)
21427
21565
  return 0;
21428
21566
  }
package/dist/cli.mjs CHANGED
@@ -56,7 +56,7 @@ import {
56
56
  waitProcessCommandLine,
57
57
  withCodexReceiver,
58
58
  withdrawFromChannel
59
- } from "./chunks/chunk-SN7I52NL.mjs";
59
+ } from "./chunks/chunk-HBSDWG62.mjs";
60
60
 
61
61
  // ../../node_modules/qrcode/lib/can-promise.js
62
62
  var require_can_promise = __commonJS({
@@ -15907,7 +15907,7 @@ function claudeChannelIsCurrent(profile, channelId, parent) {
15907
15907
  }
15908
15908
 
15909
15909
  // src/version.ts
15910
- var VERSION = true ? "0.1.23" : "0.0.0-dev";
15910
+ var VERSION = true ? "0.1.24" : "0.0.0-dev";
15911
15911
  function osName() {
15912
15912
  return process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : process.platform;
15913
15913
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "approval-box",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
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",
@@ -32,7 +32,7 @@
32
32
  "@modelcontextprotocol/sdk": "^1.31.0",
33
33
  "@types/node": "^25.9.1",
34
34
  "@types/qrcode": "^1.5.5",
35
- "aiterm-steer-delivery": "0.4.4",
35
+ "aiterm-steer-delivery": "0.4.5",
36
36
  "esbuild": "^0.28.2",
37
37
  "qrcode": "^1.5.4",
38
38
  "typescript": "^6.0.3",