@cydm/pie 2.7.14 → 2.7.15

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pie/extension-im-forward",
3
3
  "version": "0.1.0",
4
- "description": "Forward assistant body text to Feishu (Lark) via lark-cli, and inject IM replies (to a forwarded message) as user input for the originating session. CLI-only, dormant unless configured.",
4
+ "description": "Provider-aware IM forwarding with guided interactive setup, session-bound replies and atomic image input. Feishu is currently supported; headless hosts stay dormant.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "pie": {
@@ -5,8 +5,8 @@ import {
5
5
  import "../../../chunks/chunk-LZQXQOPJ.js";
6
6
 
7
7
  // builtin/extensions/pie-ipc/index.ts
8
- import * as fs2 from "node:fs";
9
- import * as path2 from "node:path";
8
+ import * as fs3 from "node:fs";
9
+ import * as path4 from "node:path";
10
10
 
11
11
  // builtin/extensions/pie-ipc/pure.ts
12
12
  import { randomBytes } from "node:crypto";
@@ -321,22 +321,257 @@ function assistantBodyText(message) {
321
321
  }
322
322
 
323
323
  // builtin/extensions/pie-ipc/discovery.ts
324
+ import * as fs2 from "node:fs";
325
+
326
+ // builtin/extensions/pie-ipc/windows-security.ts
327
+ import { createRequire } from "node:module";
328
+ import * as path2 from "node:path";
324
329
  import * as fs from "node:fs";
325
- import * as path from "node:path";
326
330
  import { randomBytes as randomBytes2 } from "node:crypto";
331
+
332
+ // builtin/extensions/pie-ipc/windows-path.ts
333
+ import * as path from "node:path";
334
+ function canonicalWindowsDirectory(input) {
335
+ const value = input.replace(/\//g, "\\");
336
+ if (!/^[A-Za-z]:\\/.test(value) || value.slice(2).includes(":")) throw new Error("Unsafe IPC directory path");
337
+ const trimmed = value.replace(/\\+$/, "");
338
+ const pieces = trimmed.slice(3).split("\\");
339
+ if (!pieces.length || pieces.some((p) => !p || /[. ]$/.test(p) || /[<>"|?*\x00-\x1f]/.test(p) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|$)/i.test(p))) throw new Error("Unsafe IPC directory path");
340
+ return path.win32.normalize(trimmed);
341
+ }
342
+
343
+ // builtin/extensions/pie-ipc/windows-security.ts
344
+ var cached;
345
+ function initialize() {
346
+ const k = createRequire(import.meta.url)("koffi");
347
+ const kernel = k.load("kernel32.dll"), adv = k.load("advapi32.dll");
348
+ const fn = (lib, decl) => lib.func(decl);
349
+ const close = fn(kernel, "bool __stdcall CloseHandle(void *h)");
350
+ const open = fn(kernel, "void * __stdcall CreateFileW(str16 name, uint32 access, uint32 share, void *sa, uint32 disposition, uint32 flags, void *templateFile)");
351
+ const info = fn(kernel, "bool __stdcall GetFileInformationByHandle(void *h, void *info)");
352
+ const fileType = fn(kernel, "uint32 __stdcall GetFileType(void *h)");
353
+ const security = fn(adv, "uint32 __stdcall GetSecurityInfo(void *h, uint32 type, uint32 flags, _Out_ void **owner, void *group, _Out_ void **dacl, void *sacl, _Out_ void **sd)");
354
+ const sidString = fn(adv, "bool __stdcall ConvertSidToStringSidW(void *sid, _Out_ void **text)");
355
+ const free = fn(kernel, "void * __stdcall LocalFree(void *p)");
356
+ const ace = fn(adv, "bool __stdcall GetAce(void *acl, uint32 index, _Out_ void **ace)");
357
+ const sddl = fn(adv, "bool __stdcall ConvertStringSecurityDescriptorToSecurityDescriptorW(str16 text, uint32 revision, _Out_ void **sd, void *size)");
358
+ const mkdir = fn(kernel, "bool __stdcall CreateDirectoryW(str16 name, void *sa)");
359
+ const error = fn(kernel, "uint32 __stdcall GetLastError()");
360
+ const read = fn(kernel, "bool __stdcall ReadFile(void *h, void *buffer, uint32 size, _Out_ uint32 *count, void *overlapped)");
361
+ const write = fn(kernel, "bool __stdcall WriteFile(void *h, void *buffer, uint32 size, _Out_ uint32 *count, void *overlapped)");
362
+ const flush = fn(kernel, "bool __stdcall FlushFileBuffers(void *h)");
363
+ const move = fn(kernel, "bool __stdcall MoveFileExW(str16 from, str16 to, uint32 flags)");
364
+ const del = fn(kernel, "bool __stdcall SetFileInformationByHandle(void *h, uint32 cls, void *data, uint32 size)");
365
+ const drive = fn(kernel, "uint32 __stdcall GetDriveTypeW(str16 root)");
366
+ const volume = fn(kernel, "bool __stdcall GetVolumeInformationW(str16 root, void *label, uint32 labelSize, void *serial, void *maxComponent, _Out_ uint32 *flags, void *fsName, uint32 fsSize)");
367
+ const processHandle = fn(kernel, "void * __stdcall GetCurrentProcess()");
368
+ const tokenOpen = fn(adv, "bool __stdcall OpenProcessToken(void *process, uint32 access, _Out_ void **token)");
369
+ const tokenInfo = fn(adv, "bool __stdcall GetTokenInformation(void *token, uint32 cls, void *buffer, uint32 size, _Out_ uint32 *needed)");
370
+ const fail = () => {
371
+ throw new Error("Windows IPC security validation failed");
372
+ };
373
+ const sid = (p) => {
374
+ const out = [null];
375
+ if (!sidString(p, out)) fail();
376
+ try {
377
+ let text = "";
378
+ for (let i = 0; i < 256; i++) {
379
+ const c = k.decode(out[0], i * 2, "uint16");
380
+ if (!c) return text;
381
+ text += String.fromCharCode(c);
382
+ }
383
+ return fail();
384
+ } finally {
385
+ free(out[0]);
386
+ }
387
+ };
388
+ const token = [null];
389
+ if (!tokenOpen(processHandle(), 8, token)) fail();
390
+ let current;
391
+ try {
392
+ const needed = [0];
393
+ tokenInfo(token[0], 1, null, 0, needed);
394
+ if (!needed[0] || needed[0] > 65536) fail();
395
+ const b = Buffer.alloc(needed[0]);
396
+ if (!tokenInfo(token[0], 1, b, b.length, needed)) fail();
397
+ current = sid(k.decode(b, "void *"));
398
+ } finally {
399
+ close(token[0]);
400
+ }
401
+ const trusted = /* @__PURE__ */ new Set([current, "S-1-5-18", "S-1-5-32-544"]);
402
+ const installer = "S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464";
403
+ function check(h, privateObject, directory, systemAncestor = false) {
404
+ const b = Buffer.alloc(52);
405
+ if (fileType(h) !== 1 || !info(h, b)) fail();
406
+ const attrs = b.readUInt32LE(0);
407
+ if (attrs & 1024 || !!(attrs & 16) !== directory) fail();
408
+ if (!directory && (b.readUInt32LE(40) !== 1 || b.readUInt32LE(32) !== 0)) fail();
409
+ const owner = [null], dacl = [null], sd = [null];
410
+ try {
411
+ if (security(h, 1, 5, owner, null, dacl, null, sd) !== 0 || !owner[0] || !dacl[0]) fail();
412
+ const ownerSid = sid(owner[0]);
413
+ if (!trusted.has(ownerSid) && !(!privateObject && systemAncestor && ownerSid === installer)) fail();
414
+ const header = Buffer.from(k.decode(dacl[0], "uint8", 8));
415
+ const count = header.readUInt16LE(4);
416
+ for (let i = 0; i < count; i++) {
417
+ const a = [null];
418
+ if (!ace(dacl[0], i, a)) fail();
419
+ const head = Buffer.from(k.decode(a[0], "uint8", 8));
420
+ if (head[1] & 8) continue;
421
+ if (head[0] === 1) continue;
422
+ if (head[0] !== 0 || head.readUInt16LE(2) < 12) fail();
423
+ const principal = sid(k.address(a[0]) + 8n);
424
+ const mask = head.readUInt32LE(4);
425
+ const dangerousAncestorRights = 1343029586;
426
+ if (!trusted.has(principal) && (privateObject ? mask !== 0 : !!(mask & dangerousAncestorRights))) fail();
427
+ }
428
+ } finally {
429
+ if (sd[0]) free(sd[0]);
430
+ }
431
+ return b.readUInt32LE(36);
432
+ }
433
+ function handle(name, directory, access = 131072, disposition = 3, sa = null) {
434
+ const h = open(name, directory ? access | 1 : access, directory ? 3 : 7, sa, disposition, 2097152 | (directory ? 33554432 : 0), null);
435
+ if (!h || k.address(h) === 0xffffffffffffffffn || k.address(h) === 0xffffffffn) fail();
436
+ return h;
437
+ }
438
+ function withPrivateSecurity(action) {
439
+ const sd = [null];
440
+ if (!sddl(`O:${current}D:P(A;OICI;FA;;;${current})(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)`, 1, sd, null)) fail();
441
+ try {
442
+ const size = k.sizeof("void *") === 8 ? 24 : 12;
443
+ const sa = Buffer.alloc(size);
444
+ sa.writeUInt32LE(size);
445
+ if (size === 24) sa.writeBigUInt64LE(k.address(sd[0]), 8);
446
+ else sa.writeUInt32LE(Number(k.address(sd[0])), 4);
447
+ return action(sa);
448
+ } finally {
449
+ free(sd[0]);
450
+ }
451
+ }
452
+ function createDirectory(name, exclusive = false) {
453
+ withPrivateSecurity((sa) => {
454
+ if (!mkdir(name, sa) && (exclusive || error() !== 183)) fail();
455
+ });
456
+ }
457
+ function pinned(dir, create, action, exclusiveFinal = false) {
458
+ dir = canonicalWindowsDirectory(dir);
459
+ const pieces = dir.slice(3).split("\\");
460
+ const root = dir.slice(0, 3), flags = [0];
461
+ if (drive(root) !== 3 || !volume(root, null, 0, null, null, flags, null, 0) || !(flags[0] & 8)) fail();
462
+ const handles = [];
463
+ try {
464
+ const r = handle(root, true);
465
+ handles.push(r);
466
+ check(r, false, true, true);
467
+ let name = root;
468
+ for (let i = 0; i < pieces.length; i++) {
469
+ name = path2.win32.join(name, pieces[i]);
470
+ if (create) createDirectory(name, exclusiveFinal && i === pieces.length - 1);
471
+ const h = handle(name, true);
472
+ handles.push(h);
473
+ check(h, i === pieces.length - 1, true);
474
+ }
475
+ return action();
476
+ } finally {
477
+ for (const h of handles.reverse()) close(h);
478
+ }
479
+ }
480
+ const fileName = (dir, name) => {
481
+ if (!/^[a-zA-Z0-9._-]+$/.test(name) || /[.]$/.test(name) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|$)/i.test(name)) fail();
482
+ return path2.win32.join(dir, name);
483
+ };
484
+ function remove(dir, name) {
485
+ const h = handle(fileName(dir, name), false, 196608);
486
+ try {
487
+ check(h, true, false);
488
+ if (!del(h, 4, Buffer.from([1]), 1)) fail();
489
+ } finally {
490
+ close(h);
491
+ }
492
+ }
493
+ return {
494
+ ensure: (dir) => pinned(dir, true, () => {
495
+ }),
496
+ createPrivateRoot: (dir) => pinned(dir, true, () => {
497
+ }, true),
498
+ pin: pinned,
499
+ read: (dir, name, max) => pinned(dir, false, () => {
500
+ const h = handle(fileName(dir, name), false, 2147614720);
501
+ try {
502
+ if (check(h, true, false) > max) fail();
503
+ const b = Buffer.alloc(max + 1), count = [0];
504
+ if (!read(h, b, b.length, count, null) || count[0] > max) fail();
505
+ return b.subarray(0, count[0]).toString("utf8");
506
+ } finally {
507
+ close(h);
508
+ }
509
+ }),
510
+ remove: (dir, name) => pinned(dir, false, () => remove(dir, name)),
511
+ write: (dir, name, raw) => pinned(dir, true, () => {
512
+ const target = fileName(dir, name), tempName = `.${randomBytes2(16).toString("hex")}.tmp`, temp = fileName(dir, tempName);
513
+ let created = false;
514
+ try {
515
+ const h = withPrivateSecurity((sa) => handle(temp, false, 1073872896, 1, sa));
516
+ created = true;
517
+ try {
518
+ check(h, true, false);
519
+ const b = Buffer.from(raw), count = [0];
520
+ if (!write(h, b, b.length, count, null) || count[0] !== b.length || !flush(h)) fail();
521
+ } finally {
522
+ close(h);
523
+ }
524
+ try {
525
+ const st = fs.lstatSync(target);
526
+ if (st) {
527
+ const h2 = handle(target, false);
528
+ try {
529
+ check(h2, true, false);
530
+ } finally {
531
+ close(h2);
532
+ }
533
+ }
534
+ } catch (e) {
535
+ if (e.code !== "ENOENT") throw e;
536
+ }
537
+ if (!move(temp, target, 9)) fail();
538
+ created = false;
539
+ } finally {
540
+ if (created) {
541
+ try {
542
+ remove(dir, tempName);
543
+ } catch {
544
+ }
545
+ }
546
+ }
547
+ })
548
+ };
549
+ }
550
+ function windowsSecurity() {
551
+ if (process.platform !== "win32") throw new Error("Windows IPC backend requested off Windows");
552
+ try {
553
+ return cached ??= initialize();
554
+ } catch {
555
+ throw new Error("Windows IPC security backend unavailable");
556
+ }
557
+ }
558
+
559
+ // builtin/extensions/pie-ipc/discovery.ts
560
+ import * as path3 from "node:path";
561
+ import { randomBytes as randomBytes3 } from "node:crypto";
327
562
  function ipcDiscoveryDir(configDir) {
328
- return path.join(configDir, "run", "ipc");
563
+ return process.platform === "win32" ? `${canonicalWindowsDirectory(configDir)}\\run\\ipc` : path3.join(configDir, "run", "ipc");
329
564
  }
330
565
  function statusFilePath(discoveryDir, instanceId) {
331
566
  if (!isValidInstanceId(instanceId)) throw new Error("Invalid instance id");
332
- return path.join(discoveryDir, `${instanceId}.json`);
567
+ return path3.join(discoveryDir, `${instanceId}.json`);
333
568
  }
334
569
  function ensurePrivateDir(discoveryDir) {
335
- if (process.platform === "win32") throw new Error("pie-ipc requires POSIX private-file permissions; Windows is not supported yet");
336
- fs.mkdirSync(discoveryDir, { recursive: true, mode: 448 });
337
- const stat = fs.lstatSync(discoveryDir);
570
+ if (process.platform === "win32") return windowsSecurity().ensure(discoveryDir);
571
+ fs2.mkdirSync(discoveryDir, { recursive: true, mode: 448 });
572
+ const stat = fs2.lstatSync(discoveryDir);
338
573
  if (!stat.isDirectory() || stat.isSymbolicLink() || process.getuid && stat.uid !== process.getuid()) throw new Error("Unsafe IPC directory");
339
- fs.chmodSync(discoveryDir, 448);
574
+ fs2.chmodSync(discoveryDir, 448);
340
575
  }
341
576
  function writeStatusFile(discoveryDir, file, log) {
342
577
  let temp;
@@ -345,14 +580,18 @@ function writeStatusFile(discoveryDir, file, log) {
345
580
  const target = statusFilePath(discoveryDir, file.instanceId);
346
581
  const raw = JSON.stringify(file);
347
582
  if (Buffer.byteLength(raw) > MAX_STATUS_FILE_BYTES || !parseStatusFile(raw)) throw new Error("Invalid status payload");
348
- temp = path.join(discoveryDir, `.${randomBytes2(16).toString("hex")}.tmp`);
349
- const handle = fs.openSync(temp, "wx", 384);
583
+ if (process.platform === "win32") {
584
+ windowsSecurity().write(discoveryDir, `${file.instanceId}.json`, raw);
585
+ return true;
586
+ }
587
+ temp = path3.join(discoveryDir, `.${randomBytes3(16).toString("hex")}.tmp`);
588
+ const handle = fs2.openSync(temp, "wx", 384);
350
589
  try {
351
- fs.writeFileSync(handle, raw);
590
+ fs2.writeFileSync(handle, raw);
352
591
  } finally {
353
- fs.closeSync(handle);
592
+ fs2.closeSync(handle);
354
593
  }
355
- fs.renameSync(temp, target);
594
+ fs2.renameSync(temp, target);
356
595
  return true;
357
596
  } catch (error) {
358
597
  try {
@@ -363,7 +602,7 @@ function writeStatusFile(discoveryDir, file, log) {
363
602
  } finally {
364
603
  if (temp) {
365
604
  try {
366
- fs.unlinkSync(temp);
605
+ fs2.unlinkSync(temp);
367
606
  } catch {
368
607
  }
369
608
  }
@@ -374,26 +613,32 @@ function readStatusFile(discoveryDir, instanceId) {
374
613
  const target = statusFilePath(discoveryDir, instanceId);
375
614
  let fd;
376
615
  try {
377
- if (process.platform === "win32") return null;
378
- const dir = fs.lstatSync(discoveryDir);
616
+ if (process.platform === "win32") {
617
+ const file2 = parseStatusFile(windowsSecurity().read(discoveryDir, `${instanceId}.json`, MAX_STATUS_FILE_BYTES));
618
+ return file2?.instanceId === instanceId ? file2 : null;
619
+ }
620
+ const dir = fs2.lstatSync(discoveryDir);
379
621
  if (!dir.isDirectory() || dir.isSymbolicLink() || process.getuid && (dir.uid !== process.getuid() || (dir.mode & 63) !== 0)) return null;
380
- fd = fs.openSync(target, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
381
- const stats = fs.fstatSync(fd);
622
+ fd = fs2.openSync(target, fs2.constants.O_RDONLY | (fs2.constants.O_NOFOLLOW ?? 0) | (fs2.constants.O_NONBLOCK ?? 0));
623
+ const stats = fs2.fstatSync(fd);
382
624
  if (!stats.isFile() || stats.size > MAX_STATUS_FILE_BYTES || process.getuid && (stats.uid !== process.getuid() || (stats.mode & 63) !== 0)) return null;
383
625
  const bytes = Buffer.alloc(MAX_STATUS_FILE_BYTES + 1);
384
- const count = fs.readSync(fd, bytes, 0, bytes.length, 0);
626
+ const count = fs2.readSync(fd, bytes, 0, bytes.length, 0);
385
627
  if (count > MAX_STATUS_FILE_BYTES) return null;
386
628
  const file = parseStatusFile(bytes.subarray(0, count).toString("utf8"));
387
629
  return file?.instanceId === instanceId ? file : null;
388
630
  } catch {
389
631
  return null;
390
632
  } finally {
391
- if (fd !== void 0) fs.closeSync(fd);
633
+ if (fd !== void 0) fs2.closeSync(fd);
392
634
  }
393
635
  }
394
636
  function removeStatusFile(discoveryDir, instanceId) {
395
637
  try {
396
- fs.rmSync(statusFilePath(discoveryDir, instanceId), { force: true });
638
+ if (process.platform === "win32") {
639
+ statusFilePath(discoveryDir, instanceId);
640
+ windowsSecurity().remove(discoveryDir, `${instanceId}.json`);
641
+ } else fs2.rmSync(statusFilePath(discoveryDir, instanceId), { force: true });
397
642
  } catch {
398
643
  }
399
644
  }
@@ -410,12 +655,21 @@ function listPeers(discoveryDir, options = {}) {
410
655
  const entries = [];
411
656
  let directory;
412
657
  try {
413
- directory = fs.opendirSync(discoveryDir);
414
- for (let n = 0; n < 1024; n++) {
415
- const entry = directory.readSync();
416
- if (!entry) break;
417
- entries.push(entry);
418
- }
658
+ const enumerate = () => {
659
+ directory = fs2.opendirSync(discoveryDir);
660
+ try {
661
+ for (let n = 0; n < 1024; n++) {
662
+ const entry = directory.readSync();
663
+ if (!entry) break;
664
+ entries.push(entry);
665
+ }
666
+ } finally {
667
+ directory.closeSync();
668
+ directory = void 0;
669
+ }
670
+ };
671
+ if (process.platform === "win32") windowsSecurity().pin(discoveryDir, false, enumerate);
672
+ else enumerate();
419
673
  } catch {
420
674
  return [];
421
675
  } finally {
@@ -435,7 +689,7 @@ function listPeers(discoveryDir, options = {}) {
435
689
  const health = peerHealth(file, now, aliveCheck);
436
690
  if (health === "dead" && options.pruneDead !== false) {
437
691
  try {
438
- fs.rmSync(path.join(discoveryDir, entry.name), { force: true });
692
+ removeStatusFile(discoveryDir, instanceId);
439
693
  } catch {
440
694
  }
441
695
  continue;
@@ -468,35 +722,69 @@ function startHeartbeat(discoveryDir, file, intervalMs, write = (next) => writeS
468
722
  function watchDiscoveryDir(discoveryDir, onChange) {
469
723
  let watcher = null;
470
724
  let closed = false;
471
- let lastEventAt = 0;
725
+ const lastEvents = /* @__PURE__ */ new Map();
726
+ let identity = "";
727
+ const directoryIdentity = () => {
728
+ const stats = fs2.lstatSync(discoveryDir);
729
+ return `${stats.dev}:${stats.ino}`;
730
+ };
472
731
  const attach = () => {
473
732
  if (closed) return;
474
733
  try {
475
- watcher = fs.watch(discoveryDir, (_event, filename) => {
734
+ const attachWatch = () => fs2.watch(discoveryDir, (_event, filename) => {
476
735
  const name = String(filename ?? "");
477
736
  if (!name.endsWith(".json")) return;
478
737
  const now = Date.now();
479
- if (now - lastEventAt < 50) return;
480
- lastEventAt = now;
738
+ const id = name.slice(0, -".json".length);
739
+ if (!isValidInstanceId(id) || now - (lastEvents.get(name) ?? 0) < 50) return;
740
+ if (lastEvents.size >= 1024) lastEvents.clear();
741
+ lastEvents.set(name, now);
742
+ if (!readStatusFile(discoveryDir, id)) return;
481
743
  try {
482
- onChange(name.slice(0, -".json".length));
744
+ onChange(id);
483
745
  } catch {
484
746
  }
485
747
  });
748
+ const verifiedAttach = () => {
749
+ identity = directoryIdentity();
750
+ return attachWatch();
751
+ };
752
+ watcher = process.platform === "win32" ? windowsSecurity().pin(discoveryDir, false, verifiedAttach) : verifiedAttach();
753
+ for (const { file } of listPeers(discoveryDir, { pruneDead: false })) {
754
+ try {
755
+ onChange(file.instanceId);
756
+ } catch {
757
+ }
758
+ }
486
759
  } catch {
487
- watcher = null;
488
- }
489
- watcher?.on("error", () => {
490
760
  watcher?.close();
491
761
  watcher = null;
762
+ }
763
+ const attached = watcher;
764
+ const detached = () => {
765
+ if (watcher === attached) watcher = null;
766
+ };
767
+ attached?.on("error", () => {
768
+ attached.close();
769
+ detached();
492
770
  });
771
+ attached?.on("close", detached);
493
772
  watcher?.unref?.();
494
773
  };
495
774
  attach();
496
775
  const retry = setInterval(() => {
497
- if (closed || watcher) return;
776
+ if (closed) return;
777
+ if (watcher) {
778
+ try {
779
+ if (directoryIdentity() === identity) return;
780
+ } catch {
781
+ }
782
+ const stale = watcher;
783
+ watcher = null;
784
+ stale.close();
785
+ }
498
786
  attach();
499
- }, 5e3);
787
+ }, 500);
500
788
  retry.unref?.();
501
789
  return () => {
502
790
  closed = true;
@@ -525,7 +813,7 @@ function buildStatusFile(input) {
525
813
 
526
814
  // builtin/extensions/pie-ipc/transport.ts
527
815
  import * as net from "node:net";
528
- import { createHmac, randomBytes as randomBytes3, randomUUID, timingSafeEqual } from "node:crypto";
816
+ import { createHmac, randomBytes as randomBytes4, randomUUID, timingSafeEqual } from "node:crypto";
529
817
  import { StringDecoder } from "node:string_decoder";
530
818
  function signEnvelope(e, secret) {
531
819
  const payload = [e.v, e.id, e.from, e.to, e.kind, e.createdAt, e.sessionId ?? null, e.text ?? null, e.replyTo ?? null, e.waitMs ?? null];
@@ -539,7 +827,7 @@ var IpcTransport = class {
539
827
  constructor(host) {
540
828
  this.host = host;
541
829
  this.instanceId = host.instanceId;
542
- this.secret = host.secret ?? randomBytes3(32).toString("hex");
830
+ this.secret = host.secret ?? randomBytes4(32).toString("hex");
543
831
  this.server = net.createServer((socket) => this.accept(socket));
544
832
  this.server.on("error", () => {
545
833
  });
@@ -1462,29 +1750,39 @@ ${message.text}`).join("\n---\n");
1462
1750
  };
1463
1751
  function loadConfig(ctx, configDir) {
1464
1752
  const layers = [
1465
- { file: path2.join(configDir, "ipc.json"), content: safeRead(path2.join(configDir, "ipc.json")) },
1466
- { file: path2.join(ctx.cwd, ".pie", "ipc.json"), content: safeRead(path2.join(ctx.cwd, ".pie", "ipc.json")) }
1753
+ { file: path4.join(configDir, "ipc.json"), content: safeRead(path4.join(configDir, "ipc.json")) },
1754
+ { file: path4.join(ctx.cwd, ".pie", "ipc.json"), content: safeRead(path4.join(ctx.cwd, ".pie", "ipc.json")) }
1467
1755
  ];
1468
1756
  return resolveIpcConfig(layers);
1469
1757
  }
1470
1758
  function safeRead(file) {
1471
1759
  try {
1472
- return fs2.readFileSync(file, "utf8");
1760
+ return fs3.readFileSync(file, "utf8");
1473
1761
  } catch {
1474
1762
  return void 0;
1475
1763
  }
1476
1764
  }
1477
1765
  function pieIpcExtension(ctx) {
1478
1766
  const configDir = process.env.PIE_IPC_CONFIG_DIR ?? getConfigDirSafe(ctx);
1479
- const discoveryDir = ipcDiscoveryDir(configDir);
1767
+ let discoveryDir;
1768
+ try {
1769
+ discoveryDir = ipcDiscoveryDir(configDir);
1770
+ } catch {
1771
+ ctx.log("pie-ipc: disabled: Windows discovery security validation failed");
1772
+ return;
1773
+ }
1480
1774
  const { config, problems } = loadConfig(ctx, configDir);
1481
1775
  for (const problem of problems) ctx.log(`pie-ipc: ${problem}`);
1482
1776
  if (!config) {
1483
1777
  return;
1484
1778
  }
1485
1779
  if (process.platform === "win32") {
1486
- ctx.log("pie-ipc: disabled: Windows ACL isolation is not implemented");
1487
- return;
1780
+ try {
1781
+ ensurePrivateDir(discoveryDir);
1782
+ } catch {
1783
+ ctx.log("pie-ipc: disabled: Windows discovery security validation failed");
1784
+ return;
1785
+ }
1488
1786
  }
1489
1787
  const runtime = new Runtime(ctx, config, discoveryDir);
1490
1788
  ctx.log(`pie-ipc: enabled as ${runtime.instanceId} (${ctx.hasUI ? "server+client" : "client-only"})`);
@@ -1658,10 +1956,10 @@ function getConfigDirSafe(_ctx) {
1658
1956
  const envDir = process.env.PIE_CODING_AGENT_DIR;
1659
1957
  if (envDir) {
1660
1958
  if (envDir === "~") return homeDir();
1661
- if (envDir.startsWith("~/")) return path2.join(homeDir(), envDir.slice(2));
1959
+ if (envDir.startsWith("~/")) return path4.join(homeDir(), envDir.slice(2));
1662
1960
  return envDir;
1663
1961
  }
1664
- return path2.join(homeDir(), ".pie");
1962
+ return path4.join(homeDir(), ".pie");
1665
1963
  }
1666
1964
  function homeDir() {
1667
1965
  return process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
@@ -6,9 +6,9 @@ import {
6
6
  extractPlanTodoItems,
7
7
  markCompletedPlanSteps,
8
8
  restoreExecutionState
9
- } from "../../../chunks/chunk-VPTLDLI6.js";
10
- import "../../../chunks/chunk-5YU6MU2N.js";
11
- import "../../../chunks/chunk-N4252GH3.js";
9
+ } from "../../../chunks/chunk-C5UTDL3S.js";
10
+ import "../../../chunks/chunk-TDHVB2FH.js";
11
+ import "../../../chunks/chunk-NM2JLPJ6.js";
12
12
  import {
13
13
  Type
14
14
  } from "../../../chunks/chunk-XXGYUYXI.js";
@@ -6,15 +6,15 @@ import {
6
6
  formatTransportLifecycle,
7
7
  getConfigDir,
8
8
  logTransportLifecycle
9
- } from "../../../chunks/chunk-FOE2AW4L.js";
9
+ } from "../../../chunks/chunk-CLEPSTWH.js";
10
10
  import {
11
11
  SUBAGENT_SESSION_RECORD_VERSION,
12
12
  createPolicyEnforcedTools,
13
13
  createSharedFileSystemTools,
14
14
  createSubagentCapability
15
- } from "../../../chunks/chunk-VPTLDLI6.js";
16
- import "../../../chunks/chunk-5YU6MU2N.js";
17
- import "../../../chunks/chunk-N4252GH3.js";
15
+ } from "../../../chunks/chunk-C5UTDL3S.js";
16
+ import "../../../chunks/chunk-TDHVB2FH.js";
17
+ import "../../../chunks/chunk-NM2JLPJ6.js";
18
18
  import "../../../chunks/chunk-XXGYUYXI.js";
19
19
  import "../../../chunks/chunk-LZQXQOPJ.js";
20
20
 
@@ -8,9 +8,9 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-VPTLDLI6.js";
12
- import "../../../chunks/chunk-5YU6MU2N.js";
13
- import "../../../chunks/chunk-N4252GH3.js";
11
+ } from "../../../chunks/chunk-C5UTDL3S.js";
12
+ import "../../../chunks/chunk-TDHVB2FH.js";
13
+ import "../../../chunks/chunk-NM2JLPJ6.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
15
15
  import "../../../chunks/chunk-LZQXQOPJ.js";
16
16