@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.
- package/README.md +16 -0
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/cron/index.js +669 -0
- package/dist/builtin/extensions/cron/package.json +11 -0
- package/dist/builtin/extensions/im-forward/index.js +1873 -575
- package/dist/builtin/extensions/im-forward/package.json +1 -1
- package/dist/builtin/extensions/pie-ipc/index.js +347 -49
- package/dist/builtin/extensions/plan-mode/index.js +3 -3
- package/dist/builtin/extensions/subagent/index.js +4 -4
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/chunks/{chunk-VPTLDLI6.js → chunk-C5UTDL3S.js} +339 -47
- package/dist/chunks/{chunk-FOE2AW4L.js → chunk-CLEPSTWH.js} +2 -2
- package/dist/chunks/{chunk-N4252GH3.js → chunk-NM2JLPJ6.js} +41 -13
- package/dist/chunks/{chunk-5YU6MU2N.js → chunk-TDHVB2FH.js} +1 -1
- package/dist/chunks/{src-4PG3KZUS.js → src-IY6TEIL7.js} +2 -2
- package/dist/chunks/{test-stream-OTKTTZ72.js → test-stream-EXSL5TIO.js} +1 -1
- package/dist/cli.js +336 -126
- package/package.json +5 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie/extension-im-forward",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"description": "
|
|
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
|
|
9
|
-
import * as
|
|
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
|
|
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
|
|
567
|
+
return path3.join(discoveryDir, `${instanceId}.json`);
|
|
333
568
|
}
|
|
334
569
|
function ensurePrivateDir(discoveryDir) {
|
|
335
|
-
if (process.platform === "win32")
|
|
336
|
-
|
|
337
|
-
const stat =
|
|
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
|
-
|
|
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
|
-
|
|
349
|
-
|
|
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
|
-
|
|
590
|
+
fs2.writeFileSync(handle, raw);
|
|
352
591
|
} finally {
|
|
353
|
-
|
|
592
|
+
fs2.closeSync(handle);
|
|
354
593
|
}
|
|
355
|
-
|
|
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
|
-
|
|
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")
|
|
378
|
-
|
|
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 =
|
|
381
|
-
const stats =
|
|
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 =
|
|
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)
|
|
633
|
+
if (fd !== void 0) fs2.closeSync(fd);
|
|
392
634
|
}
|
|
393
635
|
}
|
|
394
636
|
function removeStatusFile(discoveryDir, instanceId) {
|
|
395
637
|
try {
|
|
396
|
-
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
480
|
-
|
|
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(
|
|
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
|
|
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
|
-
},
|
|
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
|
|
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 ??
|
|
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:
|
|
1466
|
-
{ file:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1487
|
-
|
|
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
|
|
1959
|
+
if (envDir.startsWith("~/")) return path4.join(homeDir(), envDir.slice(2));
|
|
1662
1960
|
return envDir;
|
|
1663
1961
|
}
|
|
1664
|
-
return
|
|
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-
|
|
10
|
-
import "../../../chunks/chunk-
|
|
11
|
-
import "../../../chunks/chunk-
|
|
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-
|
|
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-
|
|
16
|
-
import "../../../chunks/chunk-
|
|
17
|
-
import "../../../chunks/chunk-
|
|
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-
|
|
12
|
-
import "../../../chunks/chunk-
|
|
13
|
-
import "../../../chunks/chunk-
|
|
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
|
|