@joe-broadhead/messaging-bin-linux-x64-gnu 0.1.0 → 0.1.1

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/bin/messagingd CHANGED
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/bin/messagingd
3
- size: 91498464 bytes
4
- sha256: 276a9658a3c88b8a331dfb55bf31afff12e1bbe0bd9c6050cfed4b1b3685883f
3
+ size: 91514848 bytes
4
+ sha256: c9b193f0158963091819417f416abfe4df1e6661f86650aa0e19641899c9bc41
@@ -6756,7 +6756,9 @@ var init_ipc = __esm(() => {
6756
6756
  SessionKey = string2().min(1).max(200);
6757
6757
  SessionHost = strictObject({
6758
6758
  kind: literal("opencode"),
6759
- location: string2().min(2).max(1024).refine((path) => path.startsWith("/") && !/[\u0000-\u001f\u007f]/.test(path), { message: "an absolute path without control characters" }),
6759
+ location: string2().min(2).max(1024).refine((path) => (path.startsWith("/") || /^[A-Za-z]:[\\/]./s.test(path)) && !/[\u0000-\u001f\u007f]/.test(path), {
6760
+ message: "an absolute path without control characters"
6761
+ }),
6760
6762
  sessionID: string2().min(1).max(200),
6761
6763
  pid: number2().int().min(1).max(2147483647)
6762
6764
  });
@@ -7218,7 +7220,7 @@ var init_b64u = __esm(() => {
7218
7220
  });
7219
7221
 
7220
7222
  // packages/core/dist/version.js
7221
- var VERSION = "0.1.0";
7223
+ var VERSION = "0.1.1";
7222
7224
 
7223
7225
  // packages/core/dist/jcs.js
7224
7226
  function hasLoneSurrogate(text) {
@@ -10237,19 +10239,21 @@ function messagingHome(env = process.env) {
10237
10239
  return configured ? resolve(configured) : join(homedir(), ".messaging");
10238
10240
  }
10239
10241
  function socketPath(home, tier) {
10240
- return join(home, "run", `${tier}.sock`);
10242
+ return join(home, "run", `${tier}.${IS_WINDOWS ? "pipe" : "sock"}`);
10241
10243
  }
10242
- function configuredDaemonUid(env = process.env) {
10244
+ function configuredDaemonUid(env = process.env, windows = IS_WINDOWS) {
10243
10245
  const raw = env.MESSAGING_DAEMON_UID;
10244
10246
  if (raw === undefined || raw === "")
10245
10247
  return;
10248
+ if (windows)
10249
+ throw new MessagingError("invalid_request", "MESSAGING_DAEMON_UID is not supported on Windows: messagingd runs as the user of its clients there");
10246
10250
  if (!/^\d{1,10}$/.test(raw) || Number(raw) > 4294967294)
10247
10251
  throw new MessagingError("invalid_request", "MESSAGING_DAEMON_UID must be a numeric uid");
10248
10252
  return Number(raw);
10249
10253
  }
10250
- function daemonEnv(env, home) {
10254
+ function daemonEnv(env, home, windows = IS_WINDOWS) {
10251
10255
  const out = {};
10252
- for (const name of DAEMON_ENV) {
10256
+ for (const name of windows ? [...DAEMON_ENV, ...DAEMON_ENV_WINDOWS] : DAEMON_ENV) {
10253
10257
  const value = env[name];
10254
10258
  if (value !== undefined)
10255
10259
  out[name] = value;
@@ -10257,9 +10261,10 @@ function daemonEnv(env, home) {
10257
10261
  out.MESSAGING_HOME = home;
10258
10262
  return out;
10259
10263
  }
10260
- var MAX_SOCKET_PATH_BYTES = 103, DAEMON_ENV;
10264
+ var MAX_SOCKET_PATH_BYTES = 103, IS_WINDOWS, DAEMON_ENV, DAEMON_ENV_WINDOWS;
10261
10265
  var init_paths = __esm(() => {
10262
10266
  init_dist();
10267
+ IS_WINDOWS = process.platform === "win32";
10263
10268
  DAEMON_ENV = [
10264
10269
  "PATH",
10265
10270
  "HOME",
@@ -10285,6 +10290,74 @@ var init_paths = __esm(() => {
10285
10290
  "MESSAGING_LOG_LEVEL",
10286
10291
  "MESSAGING_IDENTITY_STORE"
10287
10292
  ];
10293
+ DAEMON_ENV_WINDOWS = [
10294
+ "SystemRoot",
10295
+ "SystemDrive",
10296
+ "windir",
10297
+ "ComSpec",
10298
+ "PATHEXT",
10299
+ "USERPROFILE",
10300
+ "USERNAME",
10301
+ "USERDOMAIN",
10302
+ "HOMEDRIVE",
10303
+ "HOMEPATH",
10304
+ "APPDATA",
10305
+ "LOCALAPPDATA",
10306
+ "ProgramData",
10307
+ "TEMP",
10308
+ "TMP",
10309
+ "NUMBER_OF_PROCESSORS",
10310
+ "PROCESSOR_ARCHITECTURE"
10311
+ ];
10312
+ });
10313
+
10314
+ // packages/ipc/dist/pipes.js
10315
+ import { randomBytes as randomBytes5 } from "crypto";
10316
+ import { readFileSync, renameSync, rmSync, writeFileSync } from "fs";
10317
+ function newPipeName(tier) {
10318
+ return `\\\\.\\pipe\\messaging-${randomBytes5(16).toString("hex")}-${tier}`;
10319
+ }
10320
+ function writePointer(path, pointer) {
10321
+ const temp = `${path}.${process.pid}.${randomBytes5(4).toString("hex")}.tmp`;
10322
+ try {
10323
+ writeFileSync(temp, `${JSON.stringify(pointer)}
10324
+ `, { flag: "wx" });
10325
+ renameSync(temp, path);
10326
+ } catch (error) {
10327
+ rmSync(temp, { force: true });
10328
+ throw error;
10329
+ }
10330
+ }
10331
+ function readPointer(path) {
10332
+ let text;
10333
+ try {
10334
+ const bytes = readFileSync(path);
10335
+ if (bytes.length > POINTER_BYTES)
10336
+ throw new MessagingError("forbidden", "the Messaging daemon's pipe file is too large");
10337
+ text = bytes.toString("utf8");
10338
+ } catch (error) {
10339
+ if (error instanceof MessagingError)
10340
+ throw error;
10341
+ if (error.code === "ENOENT")
10342
+ throw new MessagingError("daemon_unavailable", "the Messaging daemon's pipe file does not exist");
10343
+ throw new MessagingError("forbidden", "cannot read the Messaging daemon's pipe file");
10344
+ }
10345
+ let value;
10346
+ try {
10347
+ value = parseWire(text);
10348
+ } catch {
10349
+ throw new MessagingError("forbidden", "the Messaging daemon's pipe file is not valid JSON");
10350
+ }
10351
+ const { pipe, pid, ...rest } = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
10352
+ if (typeof pipe !== "string" || !PIPE_NAME.test(pipe) || typeof pid !== "number" || !Number.isSafeInteger(pid) || pid < 1 || Object.keys(rest).length > 0) {
10353
+ throw new MessagingError("forbidden", "the Messaging daemon's pipe file does not name a Messaging pipe");
10354
+ }
10355
+ return { pipe, pid };
10356
+ }
10357
+ var PIPE_NAME, POINTER_BYTES = 1024;
10358
+ var init_pipes = __esm(() => {
10359
+ init_dist();
10360
+ PIPE_NAME = /^\\\\\.\\pipe\\messaging-[0-9a-f]{32}-(?:agent|admin)$/;
10288
10361
  });
10289
10362
 
10290
10363
  // packages/ipc/dist/rpc.js
@@ -10299,8 +10372,227 @@ var init_rpc = __esm(() => {
10299
10372
  };
10300
10373
  });
10301
10374
 
10375
+ // packages/ipc/dist/windows-acl.js
10376
+ import { spawnSync } from "child_process";
10377
+ import { randomBytes as randomBytes6 } from "crypto";
10378
+ import { existsSync, readFileSync as readFileSync2, rmSync as rmSync2 } from "fs";
10379
+ import { tmpdir } from "os";
10380
+ import { join as join2 } from "path";
10381
+ function parseSddlDacl(sddl) {
10382
+ const text = sddl.trim();
10383
+ let i = 0;
10384
+ let dacl;
10385
+ while (i < text.length) {
10386
+ const tag = text[i];
10387
+ if (text[i + 1] !== ":" || tag !== "O" && tag !== "G" && tag !== "D" && tag !== "S")
10388
+ throw new Error("not SDDL");
10389
+ i += 2;
10390
+ if (tag === "O" || tag === "G") {
10391
+ const sid = /^(S-1-[0-9-]+|[A-Z]{2})/.exec(text.slice(i));
10392
+ if (!sid)
10393
+ throw new Error("not SDDL: owner or group");
10394
+ i += sid[0].length;
10395
+ continue;
10396
+ }
10397
+ let flagText = /^[A-Z_]*/.exec(text.slice(i))[0];
10398
+ if (text[i + flagText.length] === ":")
10399
+ flagText = flagText.slice(0, -1);
10400
+ i += flagText.length;
10401
+ const aces = [];
10402
+ while (text[i] === "(") {
10403
+ let depth = 0;
10404
+ let end = i;
10405
+ for (;end < text.length; end++) {
10406
+ if (text[end] === "(")
10407
+ depth++;
10408
+ else if (text[end] === ")" && --depth === 0)
10409
+ break;
10410
+ }
10411
+ if (end >= text.length)
10412
+ throw new Error("not SDDL: an unclosed entry");
10413
+ aces.push(parseAce(text.slice(i + 1, end)));
10414
+ i = end + 1;
10415
+ }
10416
+ if (tag === "D")
10417
+ dacl = { nullDacl: flagText.includes("NO_ACCESS_CONTROL"), protected: flagText.replace("NO_ACCESS_CONTROL", "").includes("P"), aces };
10418
+ }
10419
+ return dacl;
10420
+ }
10421
+ function parseAce(body) {
10422
+ const fields = body.split(";");
10423
+ if (fields.length < 6)
10424
+ throw new Error("not SDDL: an entry");
10425
+ const [type = "", flagText = "", rightsText = "", , , sidText = ""] = fields;
10426
+ const flags = flagText.match(/.{1,2}/g) ?? [];
10427
+ return { type, flags, rights: parseRights(rightsText), sid: ALIASES[sidText] ?? sidText };
10428
+ }
10429
+ function parseRights(text) {
10430
+ if (/^0[xX][0-9a-fA-F]+$/.test(text))
10431
+ return Number.parseInt(text.slice(2), 16);
10432
+ if (/^\d+$/.test(text))
10433
+ return Number(text);
10434
+ let mask = 0;
10435
+ for (const code of text.match(/.{1,2}/g) ?? [])
10436
+ mask |= RIGHTS[code] ?? 4294967295;
10437
+ return mask >>> 0;
10438
+ }
10439
+ function resolveSid(sid, self) {
10440
+ const rid = DOMAIN_RIDS[sid];
10441
+ const domain = /^(S-1-5-21-\d+-\d+-\d+)-\d+$/.exec(self)?.[1];
10442
+ return rid !== undefined && domain !== undefined ? `${domain}-${rid}` : sid;
10443
+ }
10444
+ function applies(ace) {
10445
+ return !ace.flags.includes("IO");
10446
+ }
10447
+ function daclProblem(dacl, self, { pinnedOwner }) {
10448
+ if (dacl === undefined || dacl.nullDacl)
10449
+ return "has no access control list, so anyone may write it";
10450
+ const trusted = new Set([self, SID_SYSTEM, SID_ADMINISTRATORS]);
10451
+ let ownerPinned = false;
10452
+ for (const ace of dacl.aces) {
10453
+ if (!ALLOW_TYPES.has(ace.type) || !applies(ace))
10454
+ continue;
10455
+ const writes = (ace.rights & WRITE_RIGHTS) !== 0;
10456
+ const sid = resolveSid(ace.sid, self);
10457
+ if (sid === SID_OWNER_RIGHTS) {
10458
+ if (writes)
10459
+ return "lets its owner write it";
10460
+ ownerPinned = true;
10461
+ continue;
10462
+ }
10463
+ if (writes && !trusted.has(sid))
10464
+ return `is writable by ${/^S-1-[0-9-]+$/.test(sid) ? sid : `another principal (${sid.slice(0, 8)})`}`;
10465
+ }
10466
+ if (pinnedOwner && !ownerPinned)
10467
+ return "does not pin its owner's rights (no OWNER RIGHTS entry)";
10468
+ return;
10469
+ }
10470
+ function system32(exe) {
10471
+ return join2(process.env.SystemRoot || process.env.windir || "C:\\Windows", "System32", exe);
10472
+ }
10473
+ function run(exe, args) {
10474
+ const result = spawnSync(system32(exe), args, { encoding: "utf8", windowsHide: true, timeout: 15000 });
10475
+ if (result.error)
10476
+ throw new ToolFailed(exe, null, result.error.code ?? "error");
10477
+ if (result.status !== 0)
10478
+ throw new ToolFailed(exe, result.status, `exit ${result.status}`);
10479
+ return result.stdout;
10480
+ }
10481
+ function currentUserSid() {
10482
+ if (userSid !== undefined)
10483
+ return userSid;
10484
+ const out = run("whoami.exe", ["/user", "/fo", "csv", "/nh"]);
10485
+ const sid = /"(S-1-[0-9-]+)"\s*$/.exec(out.trim())?.[1];
10486
+ if (!sid)
10487
+ throw new MessagingError("forbidden", "cannot tell this user's SID");
10488
+ userSid = sid;
10489
+ return sid;
10490
+ }
10491
+ function readDacl(path) {
10492
+ const out = join2(tmpdir(), `messaging-acl-${process.pid}-${randomBytes6(8).toString("hex")}.txt`);
10493
+ try {
10494
+ run("icacls.exe", [path, "/save", out, "/q"]);
10495
+ return parseSddlDacl(sddlLine(readFileSync2(out)));
10496
+ } catch (error) {
10497
+ if (error instanceof ToolFailed && (error.status === 2 || error.status === 3 || !existsSync(path))) {
10498
+ throw new MessagingError("daemon_unavailable", `${path} does not exist`);
10499
+ }
10500
+ if (error instanceof MessagingError)
10501
+ throw error;
10502
+ throw new MessagingError("forbidden", "cannot read an access control list");
10503
+ } finally {
10504
+ rmSync2(out, { force: true });
10505
+ }
10506
+ }
10507
+ function sddlLine(bytes) {
10508
+ const utf16 = bytes.length >= 2 && (bytes[0] === 255 && bytes[1] === 254 ? true : bytes.includes(0));
10509
+ const text = (utf16 ? bytes.toString("utf16le") : bytes.toString("utf8")).replace(/^\uFEFF/, "");
10510
+ const line = text.split(/\r?\n/).find((l) => /^(?:[OGDS]:)/.test(l.trim()));
10511
+ if (line === undefined)
10512
+ throw new MessagingError("forbidden", "cannot read an access control list");
10513
+ return line.trim();
10514
+ }
10515
+ function aclProblem(path, options) {
10516
+ return daclProblem(readDacl(path), currentUserSid(), options);
10517
+ }
10518
+ function securePrivate(path, what) {
10519
+ const self = currentUserSid();
10520
+ let others;
10521
+ try {
10522
+ others = [...new Set((readDacl(path)?.aces ?? []).map((ace) => resolveSid(ace.sid, self)))].filter((sid) => sid !== self && sid !== SID_OWNER_RIGHTS && /^S-1-[0-9-]+$/.test(sid));
10523
+ run("icacls.exe", [path, "/inheritance:r", "/grant:r", `*${self}:(OI)(CI)(F)`, `*${SID_OWNER_RIGHTS}:(OI)(CI)(RC)`, "/q"]);
10524
+ if (others.length > 0)
10525
+ run("icacls.exe", [path, "/remove", ...others.map((sid) => `*${sid}`), "/q"]);
10526
+ } catch (error) {
10527
+ throw new MessagingError("forbidden", `cannot make ${what} private: ${error instanceof Error ? error.message : "icacls failed"}`);
10528
+ }
10529
+ const dacl = readDacl(path);
10530
+ const problem = daclProblem(dacl, self, { pinnedOwner: true });
10531
+ if (problem !== undefined)
10532
+ throw new MessagingError("forbidden", `${what} is not private: it ${problem}`);
10533
+ const strangers = (dacl?.aces ?? []).map((ace) => resolveSid(ace.sid, self)).filter((sid) => sid !== self && sid !== SID_OWNER_RIGHTS);
10534
+ if (strangers.length > 0)
10535
+ throw new MessagingError("forbidden", `${what} is not private: other entries remain in its access control list`);
10536
+ }
10537
+ var SID_SYSTEM = "S-1-5-18", SID_ADMINISTRATORS = "S-1-5-32-544", SID_OWNER_RIGHTS = "S-1-3-4", ALIASES, RIGHTS, WRITE_RIGHTS, ALLOW_TYPES, DOMAIN_RIDS, ToolFailed, userSid;
10538
+ var init_windows_acl = __esm(() => {
10539
+ init_dist();
10540
+ ALIASES = {
10541
+ SY: SID_SYSTEM,
10542
+ BA: SID_ADMINISTRATORS,
10543
+ OW: SID_OWNER_RIGHTS,
10544
+ CO: "S-1-3-0",
10545
+ WD: "S-1-1-0",
10546
+ AU: "S-1-5-11",
10547
+ BU: "S-1-5-32-545",
10548
+ AN: "S-1-5-7",
10549
+ NU: "S-1-5-2",
10550
+ IU: "S-1-5-4"
10551
+ };
10552
+ RIGHTS = {
10553
+ GA: 268435456,
10554
+ GR: 2147483648,
10555
+ GW: 1073741824,
10556
+ GX: 536870912,
10557
+ RC: 131072,
10558
+ SD: 65536,
10559
+ WD: 262144,
10560
+ WO: 524288,
10561
+ RP: 16,
10562
+ WP: 32,
10563
+ CC: 1,
10564
+ DC: 2,
10565
+ LC: 4,
10566
+ SW: 8,
10567
+ LO: 128,
10568
+ DT: 64,
10569
+ CR: 256,
10570
+ FA: 2032127,
10571
+ FR: 1179785,
10572
+ FW: 1179926,
10573
+ FX: 1179808,
10574
+ KA: 983103,
10575
+ KR: 131097,
10576
+ KW: 131078,
10577
+ KX: 131097,
10578
+ NR: 0,
10579
+ NW: 0,
10580
+ NX: 0
10581
+ };
10582
+ WRITE_RIGHTS = 2 | 4 | 16 | 64 | 256 | 65536 | 262144 | 524288 | 1073741824 | 268435456;
10583
+ ALLOW_TYPES = new Set(["A", "OA", "XA", "ZA"]);
10584
+ DOMAIN_RIDS = { LA: 500, LG: 501 };
10585
+ ToolFailed = class ToolFailed extends MessagingError {
10586
+ status;
10587
+ constructor(exe, status, why) {
10588
+ super("forbidden", `${exe} failed (${why})`);
10589
+ this.status = status;
10590
+ }
10591
+ };
10592
+ });
10593
+
10302
10594
  // packages/ipc/dist/trust.js
10303
- import { lstatSync, mkdirSync } from "fs";
10595
+ import { constants, lstatSync, mkdirSync, openSync } from "fs";
10304
10596
  function checkPrivateDir(path, what, owners = selfOnly()) {
10305
10597
  let stats;
10306
10598
  try {
@@ -10310,19 +10602,51 @@ function checkPrivateDir(path, what, owners = selfOnly()) {
10310
10602
  throw new MessagingError("daemon_unavailable", `${what} does not exist`);
10311
10603
  throw new MessagingError("forbidden", `cannot inspect ${what}`);
10312
10604
  }
10313
- const problem = privacyProblem(stats, owners);
10605
+ const problem = IS_WINDOWS ? windowsProblem(stats, path, "directory") : privacyProblem(stats, owners);
10314
10606
  if (problem !== undefined)
10315
10607
  throw new MessagingError("forbidden", `${what} is not private: it ${problem}`);
10316
10608
  }
10317
10609
  function ensurePrivateDir(path, what) {
10610
+ let created = false;
10318
10611
  try {
10319
10612
  mkdirSync(path, { mode: 448 });
10613
+ created = true;
10320
10614
  } catch (error) {
10321
10615
  if (error.code !== "EEXIST")
10322
10616
  throw new MessagingError("forbidden", `cannot create ${what}`);
10323
10617
  }
10618
+ if (IS_WINDOWS && created)
10619
+ secureCreated(path, what);
10324
10620
  checkPrivateDir(path, what);
10325
10621
  }
10622
+ function secureCreated(path, what) {
10623
+ if (aclProblem(path, { pinnedOwner: true }) !== undefined)
10624
+ securePrivate(path, what);
10625
+ }
10626
+ function adoptPrivateDir(path, what, created) {
10627
+ if (created) {
10628
+ secureCreated(path, what);
10629
+ return;
10630
+ }
10631
+ let stats;
10632
+ try {
10633
+ stats = lstatSync(path);
10634
+ } catch {
10635
+ throw new MessagingError("forbidden", `cannot inspect ${what}`);
10636
+ }
10637
+ if (stats.isSymbolicLink())
10638
+ throw new MessagingError("forbidden", `${what} is not private: it is a symbolic link`);
10639
+ if (!stats.isDirectory())
10640
+ throw new MessagingError("forbidden", `${what} is not private: it is not a directory`);
10641
+ const self = currentUserSid();
10642
+ const dacl = readDacl(path);
10643
+ const problem = daclProblem(dacl, self, { pinnedOwner: false });
10644
+ if (problem !== undefined)
10645
+ throw new MessagingError("forbidden", `${what} is not private: it ${problem}`);
10646
+ const exact = daclProblem(dacl, self, { pinnedOwner: true }) === undefined && (dacl?.aces ?? []).every((ace) => [self, SID_OWNER_RIGHTS].includes(resolveSid(ace.sid, self)));
10647
+ if (!exact)
10648
+ securePrivate(path, what);
10649
+ }
10326
10650
  function privacyProblem(stats, owners = selfOnly()) {
10327
10651
  if (stats.isSymbolicLink())
10328
10652
  return "is a symbolic link";
@@ -10334,6 +10658,13 @@ function privacyProblem(stats, owners = selfOnly()) {
10334
10658
  return "is writable by group or others";
10335
10659
  return;
10336
10660
  }
10661
+ function windowsProblem(stats, path, kind) {
10662
+ if (stats.isSymbolicLink())
10663
+ return "is a symbolic link";
10664
+ if (kind === "directory" ? !stats.isDirectory() : !stats.isFile())
10665
+ return `is not a ${kind}`;
10666
+ return aclProblem(path, { pinnedOwner: true });
10667
+ }
10337
10668
  function checkSocket(path, owners = selfOnly()) {
10338
10669
  let stats;
10339
10670
  try {
@@ -10343,6 +10674,12 @@ function checkSocket(path, owners = selfOnly()) {
10343
10674
  throw new MessagingError("daemon_unavailable", "the Messaging daemon socket does not exist");
10344
10675
  throw new MessagingError("forbidden", "cannot inspect the Messaging daemon socket");
10345
10676
  }
10677
+ if (IS_WINDOWS) {
10678
+ const problem = windowsProblem(stats, path, "file");
10679
+ if (problem !== undefined)
10680
+ throw new MessagingError("forbidden", `the Messaging daemon's pipe file is not private: it ${problem}`);
10681
+ return;
10682
+ }
10346
10683
  if (!stats.isSocket())
10347
10684
  throw new MessagingError("forbidden", "the Messaging daemon socket path is not a socket");
10348
10685
  if (owners.length > 0 && !owners.includes(stats.uid))
@@ -10358,8 +10695,24 @@ function selfOnly() {
10358
10695
  const euid = process.geteuid?.();
10359
10696
  return euid === undefined ? [] : [euid];
10360
10697
  }
10698
+ function openNoFollow(path, flags, mode) {
10699
+ if (!IS_WINDOWS)
10700
+ return openSync(path, flags | constants.O_NOFOLLOW, mode);
10701
+ let link = false;
10702
+ try {
10703
+ link = lstatSync(path).isSymbolicLink();
10704
+ } catch (error) {
10705
+ if (error.code !== "ENOENT")
10706
+ throw error;
10707
+ }
10708
+ if (link)
10709
+ throw Object.assign(new Error(`ELOOP: a symbolic link, open '${path}'`), { code: "ELOOP" });
10710
+ return openSync(path, flags, mode);
10711
+ }
10361
10712
  var init_trust = __esm(() => {
10362
10713
  init_dist();
10714
+ init_paths();
10715
+ init_windows_acl();
10363
10716
  });
10364
10717
 
10365
10718
  // packages/ipc/dist/client.js
@@ -10406,16 +10759,21 @@ class MessagingClient {
10406
10759
  static async connect(options = {}) {
10407
10760
  const path = options.socketPath ?? socketPath(options.home ?? messagingHome(), options.tier ?? "agent");
10408
10761
  const owners = trustedOwners(options.daemonUid ?? configuredDaemonUid());
10762
+ let pointer;
10409
10763
  try {
10410
10764
  checkPrivateDir(dirname(path), "the Messaging daemon's socket directory", owners);
10411
10765
  checkSocket(path, owners);
10766
+ if (IS_WINDOWS)
10767
+ pointer = readPointer(path);
10412
10768
  } catch (error) {
10413
10769
  if (!(error instanceof MessagingError) || error.code !== "daemon_unavailable")
10414
10770
  throw error;
10415
10771
  }
10772
+ if (IS_WINDOWS && pointer === undefined)
10773
+ throw new MessagingError("daemon_unavailable", "cannot reach the Messaging daemon (ENOENT)");
10416
10774
  const timeout = options.connectTimeoutMs ?? 2000;
10417
10775
  const socket = await new Promise((resolve, reject) => {
10418
- const s = net.connect(path);
10776
+ const s = net.connect(pointer?.pipe ?? path);
10419
10777
  const timer = setTimeout(() => {
10420
10778
  s.destroy();
10421
10779
  reject(new MessagingError("daemon_unavailable", "timed out connecting to the Messaging daemon"));
@@ -10439,6 +10797,9 @@ class MessagingClient {
10439
10797
  timer = setTimeout(() => reject(new DaemonMismatchError(`the process on the Messaging daemon socket did not say which version it is within ${wait} ms: it is a messagingd from before this version, or it is stuck. ${REMEDY}`)), wait);
10440
10798
  })
10441
10799
  ]);
10800
+ if (pointer !== undefined && hello.pid !== pointer.pid) {
10801
+ throw new MessagingError("forbidden", "the process on the Messaging daemon's pipe is not the daemon that published it");
10802
+ }
10442
10803
  const expected = options.version ?? VERSION;
10443
10804
  if (hello.protocol !== IPC_PROTOCOL || hello.version !== expected) {
10444
10805
  throw new DaemonMismatchError(`the Messaging daemon for this home is version ${shown(hello.version)} (IPC protocol ${hello.protocol}), and this client is version ${shown(expected)} (IPC protocol ${IPC_PROTOCOL}). ${REMEDY}`, hello);
@@ -10589,6 +10950,7 @@ var init_client = __esm(() => {
10589
10950
  init_dist();
10590
10951
  init_framing();
10591
10952
  init_paths();
10953
+ init_pipes();
10592
10954
  init_rpc();
10593
10955
  init_trust();
10594
10956
  DaemonMismatchError = class DaemonMismatchError extends MessagingError {
@@ -10627,15 +10989,20 @@ async function createIpcServer(options) {
10627
10989
  throw new MessagingError("invalid_request", "an IPC server needs a tier: agent or admin");
10628
10990
  if (options.group !== undefined && options.tier !== "agent")
10629
10991
  throw new MessagingError("invalid_request", "only the agent socket may be shared with a group");
10630
- if (Buffer.byteLength(path) > MAX_SOCKET_PATH_BYTES) {
10992
+ if (options.group !== undefined && IS_WINDOWS)
10993
+ throw new MessagingError("invalid_request", "an agent socket shared with a group is not supported on Windows");
10994
+ if (!IS_WINDOWS && Buffer.byteLength(path) > MAX_SOCKET_PATH_BYTES) {
10631
10995
  throw new MessagingError("invalid_request", `socket path is longer than ${MAX_SOCKET_PATH_BYTES} bytes`);
10632
10996
  }
10633
10997
  const run = dirname2(path);
10634
10998
  const home = dirname2(run);
10635
- mkdirSync2(home, { recursive: true, mode: 448 });
10999
+ const createdHome = mkdirSync2(home, { recursive: true, mode: 448 }) !== undefined;
11000
+ if (IS_WINDOWS && createdHome)
11001
+ secureCreated(home, "the Messaging home directory");
10636
11002
  checkPrivateDir(home, "the Messaging home directory");
10637
11003
  ensurePrivateDir(run, "the Messaging run directory");
10638
- await removeStaleSocket(path);
11004
+ await (IS_WINDOWS ? removeStalePointer(path) : removeStaleSocket(path));
11005
+ const listenPath = IS_WINDOWS ? newPipeName(options.tier) : path;
10639
11006
  const contexts = new Map;
10640
11007
  const hello = { jsonrpc: "2.0", method: "hello", params: { daemon: "messagingd", version: options.version ?? VERSION, protocol: IPC_PROTOCOL, pid: process.pid } };
10641
11008
  const server = net2.createServer((socket) => {
@@ -10718,20 +11085,25 @@ async function createIpcServer(options) {
10718
11085
  }
10719
11086
  await new Promise((resolve, reject) => {
10720
11087
  server.once("error", reject);
10721
- const umask = process.umask(127);
11088
+ const umask = IS_WINDOWS ? undefined : process.umask(127);
10722
11089
  try {
10723
- server.listen(path, () => {
11090
+ server.listen(listenPath, () => {
10724
11091
  server.off("error", reject);
10725
11092
  resolve();
10726
11093
  });
10727
11094
  } finally {
10728
- process.umask(umask);
11095
+ if (umask !== undefined)
11096
+ process.umask(umask);
10729
11097
  }
10730
11098
  });
10731
11099
  try {
10732
- if (options.group !== undefined)
10733
- await chown(path, process.geteuid?.() ?? -1, options.group);
10734
- await chmod(path, options.group === undefined ? 384 : 432);
11100
+ if (IS_WINDOWS)
11101
+ writePointer(path, { pipe: listenPath, pid: process.pid });
11102
+ else {
11103
+ if (options.group !== undefined)
11104
+ await chown(path, process.geteuid?.() ?? -1, options.group);
11105
+ await chmod(path, options.group === undefined ? 384 : 432);
11106
+ }
10735
11107
  } catch (error) {
10736
11108
  await new Promise((resolve) => server.close(() => resolve()));
10737
11109
  await rm(path, { force: true });
@@ -10757,7 +11129,17 @@ async function createIpcServer(options) {
10757
11129
  for (const socket of contexts.keys())
10758
11130
  socket.destroy();
10759
11131
  await new Promise((resolve) => server.close(() => resolve()));
10760
- await rm(path, { force: true });
11132
+ if (IS_WINDOWS) {
11133
+ let ours = false;
11134
+ try {
11135
+ ours = readPointer(path).pipe === listenPath;
11136
+ } catch {
11137
+ ours = false;
11138
+ }
11139
+ if (ours)
11140
+ await rm(path, { force: true });
11141
+ } else
11142
+ await rm(path, { force: true });
10761
11143
  }
10762
11144
  };
10763
11145
  }
@@ -10811,6 +11193,34 @@ function toRpcError(error) {
10811
11193
  function describeIssues(issues) {
10812
11194
  return `invalid params: ${issues.slice(0, 5).map((i) => `${i.path.map(String).join(".") || "(root)"}: ${i.message}`).join("; ")}`.slice(0, 500);
10813
11195
  }
11196
+ async function removeStalePointer(path) {
11197
+ const exists = await stat(path).then((s) => s, () => {
11198
+ return;
11199
+ });
11200
+ if (!exists)
11201
+ return;
11202
+ if (!exists.isFile())
11203
+ throw new MessagingError("invalid_request", "pipe file path exists and is not a file");
11204
+ let pipe;
11205
+ try {
11206
+ pipe = readPointer(path).pipe;
11207
+ } catch {
11208
+ pipe = undefined;
11209
+ }
11210
+ if (pipe !== undefined && await answers(pipe))
11211
+ throw new MessagingError("invalid_request", "another daemon is already listening on this socket");
11212
+ await rm(path, { force: true });
11213
+ }
11214
+ function answers(path) {
11215
+ return new Promise((resolve) => {
11216
+ const probe = net2.connect(path);
11217
+ probe.once("connect", () => {
11218
+ probe.destroy();
11219
+ resolve(true);
11220
+ });
11221
+ probe.once("error", () => resolve(false));
11222
+ });
11223
+ }
10814
11224
  async function removeStaleSocket(path) {
10815
11225
  const exists = await stat(path).then((s) => s, () => {
10816
11226
  return;
@@ -10836,6 +11246,7 @@ var init_server = __esm(() => {
10836
11246
  init_dist();
10837
11247
  init_framing();
10838
11248
  init_paths();
11249
+ init_pipes();
10839
11250
  init_rpc();
10840
11251
  init_trust();
10841
11252
  MAX_BUFFERED_BYTES = 8 * 1024 * 1024;
@@ -10849,6 +11260,7 @@ var init_dist2 = __esm(() => {
10849
11260
  init_paths();
10850
11261
  init_rpc();
10851
11262
  init_trust();
11263
+ init_windows_acl();
10852
11264
  });
10853
11265
 
10854
11266
  // packages/daemon/dist/direct-config.js
@@ -10932,7 +11344,7 @@ var init_direct_config = __esm(() => {
10932
11344
 
10933
11345
  // packages/daemon/dist/identity/record.js
10934
11346
  import { createPrivateKey as createPrivateKey2 } from "crypto";
10935
- import { closeSync as closeSync2, constants as constants2, fstatSync, fsyncSync, openSync as openSync2, readFileSync as readFileSync2, renameSync, unlinkSync, writeSync } from "fs";
11347
+ import { closeSync as closeSync2, constants as constants3, existsSync as existsSync3, fstatSync, fsyncSync, linkSync, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync3, statSync, unlinkSync, writeSync } from "fs";
10936
11348
  function isQuotable(value) {
10937
11349
  return QUOTABLE.test(value);
10938
11350
  }
@@ -10971,9 +11383,9 @@ function decodeSecret(secret) {
10971
11383
  function readIdentityJson(path) {
10972
11384
  let text;
10973
11385
  try {
10974
- const fd = openSync2(path, constants2.O_RDONLY | constants2.O_NOFOLLOW);
11386
+ const fd = openNoFollow(path, constants3.O_RDONLY);
10975
11387
  try {
10976
- text = readFileSync2(fd, "utf8");
11388
+ text = readFileSync4(fd, "utf8");
10977
11389
  } finally {
10978
11390
  closeSync2(fd);
10979
11391
  }
@@ -10997,7 +11409,7 @@ function isPointer(raw) {
10997
11409
  function replaceIdentityFile(path, text) {
10998
11410
  let old;
10999
11411
  try {
11000
- old = openSync2(path, constants2.O_RDWR | constants2.O_NOFOLLOW);
11412
+ old = openNoFollow(path, constants3.O_RDWR);
11001
11413
  } catch (error) {
11002
11414
  const code = error.code;
11003
11415
  if (code === "ELOOP")
@@ -11005,27 +11417,56 @@ function replaceIdentityFile(path, text) {
11005
11417
  if (code !== "ENOENT")
11006
11418
  throw error;
11007
11419
  }
11420
+ let aside;
11421
+ if (IS_WINDOWS && old !== undefined) {
11422
+ closeSync2(old);
11423
+ old = undefined;
11424
+ aside = `${path}.${process.pid}.old`;
11425
+ dropAside(aside, path);
11426
+ linkSync(path, aside);
11427
+ }
11428
+ let renamed = false;
11008
11429
  try {
11009
11430
  const temp = `${path}.${process.pid}.tmp`;
11010
- const fd = openSync2(temp, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_TRUNC | constants2.O_NOFOLLOW, 384);
11431
+ const fd = openNoFollow(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_TRUNC, 384);
11011
11432
  try {
11012
11433
  writeSync(fd, text);
11013
11434
  fsyncSync(fd);
11014
11435
  } finally {
11015
11436
  closeSync2(fd);
11016
11437
  }
11017
- renameSync(temp, path);
11438
+ renameSync2(temp, path);
11439
+ renamed = true;
11018
11440
  if (old !== undefined)
11019
11441
  scrub(old);
11442
+ if (aside !== undefined)
11443
+ scrubFile(aside);
11020
11444
  } finally {
11021
11445
  if (old !== undefined)
11022
11446
  closeSync2(old);
11447
+ if (aside !== undefined && !renamed)
11448
+ rmSync3(aside, { force: true });
11023
11449
  }
11024
11450
  }
11451
+ function dropAside(aside, path) {
11452
+ let same = false;
11453
+ try {
11454
+ const a = statSync(aside, { bigint: true });
11455
+ const b = statSync(path, { bigint: true });
11456
+ same = a.ino === b.ino && a.dev === b.dev;
11457
+ } catch (error) {
11458
+ if (error.code === "ENOENT" && !existsSync3(aside))
11459
+ return;
11460
+ }
11461
+ if (same)
11462
+ rmSync3(aside, { force: true });
11463
+ else
11464
+ scrubFile(aside);
11465
+ }
11025
11466
  function scrubFile(path) {
11026
11467
  let fd;
11027
11468
  try {
11028
- fd = openSync2(path, constants2.O_RDWR | constants2.O_NOFOLLOW);
11469
+ fd = openNoFollow(path, constants3.O_RDWR);
11029
11470
  } catch (error) {
11030
11471
  if (error.code === "ENOENT")
11031
11472
  return;
@@ -11055,6 +11496,7 @@ function x25519(d) {
11055
11496
  }
11056
11497
  var MAX_RETAINED = 8, PrivateKey322, KeyRecord, QUOTABLE, KeychainPath, KeychainPointer, X25519_PKCS8;
11057
11498
  var init_record = __esm(() => {
11499
+ init_dist2();
11058
11500
  init_zod();
11059
11501
  init_dist();
11060
11502
  PrivateKey322 = string2().regex(/^[A-Za-z0-9_-]{43}$/, "32 bytes, base64url without padding");
@@ -11082,35 +11524,41 @@ var init_record = __esm(() => {
11082
11524
  });
11083
11525
 
11084
11526
  // packages/daemon/dist/home.js
11085
- import { chmodSync, chownSync, existsSync as existsSync2, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync as renameSync2, statSync, writeFileSync } from "fs";
11086
- import { join as join3, resolve as resolve2 } from "path";
11527
+ import { chmodSync, chownSync, existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync3, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
11528
+ import { join as join4, resolve as resolve2 } from "path";
11087
11529
  function homePaths(home) {
11088
11530
  const root = resolve2(home);
11089
11531
  return {
11090
11532
  home: root,
11091
- identity: join3(root, "identity.json"),
11092
- config: join3(root, "config.json"),
11093
- db: join3(root, "messaging.db"),
11094
- run: join3(root, "run"),
11533
+ identity: join4(root, "identity.json"),
11534
+ config: join4(root, "config.json"),
11535
+ db: join4(root, "messaging.db"),
11536
+ run: join4(root, "run"),
11095
11537
  agentSocket: socketPath(root, "agent"),
11096
11538
  adminSocket: socketPath(root, "admin"),
11097
- lock: join3(root, "run", "messagingd.lock"),
11098
- logs: join3(root, "logs"),
11099
- log: join3(root, "logs", "messagingd.log")
11539
+ lock: join4(root, "run", "messagingd.lock"),
11540
+ logs: join4(root, "logs"),
11541
+ log: join4(root, "logs", "messagingd.log")
11100
11542
  };
11101
11543
  }
11102
11544
  function ensureHome(paths) {
11103
11545
  for (const dir of [paths.home, paths.run, paths.logs]) {
11546
+ let created = false;
11104
11547
  if (dir === paths.home)
11105
- mkdirSync4(dir, { recursive: true, mode: 448 });
11548
+ created = mkdirSync4(dir, { recursive: true, mode: 448 }) !== undefined;
11106
11549
  else {
11107
11550
  try {
11108
11551
  mkdirSync4(dir, { mode: 448 });
11552
+ created = true;
11109
11553
  } catch (error) {
11110
11554
  if (error.code !== "EEXIST")
11111
11555
  throw error;
11112
11556
  }
11113
11557
  }
11558
+ if (IS_WINDOWS) {
11559
+ adoptPrivateDir(dir, dir, created);
11560
+ continue;
11561
+ }
11114
11562
  const problem = privacyProblem(lstatSync2(dir));
11115
11563
  if (problem !== undefined)
11116
11564
  throw new MessagingError("forbidden", `${dir} is not private: it ${problem}`);
@@ -11118,6 +11566,8 @@ function ensureHome(paths) {
11118
11566
  }
11119
11567
  }
11120
11568
  function shareAgentSocket(paths, group) {
11569
+ if (IS_WINDOWS)
11570
+ throw new MessagingError("invalid_request", "ipc.agentGroup is not supported on Windows: messagingd and its clients run as one user there");
11121
11571
  for (const dir of [paths.home, paths.run]) {
11122
11572
  try {
11123
11573
  chownSync(dir, process.geteuid?.() ?? -1, group);
@@ -11128,11 +11578,11 @@ function shareAgentSocket(paths, group) {
11128
11578
  }
11129
11579
  }
11130
11580
  function readConfig(path) {
11131
- if (!existsSync2(path))
11581
+ if (!existsSync4(path))
11132
11582
  return structuredClone(DEFAULT_CONFIG);
11133
11583
  let raw;
11134
11584
  try {
11135
- raw = parseWire(readFileSync3(path, "utf8"));
11585
+ raw = parseWire(readFileSync5(path, "utf8"));
11136
11586
  } catch {
11137
11587
  throw new MessagingError("invalid_request", `${path} is not valid JSON`);
11138
11588
  }
@@ -11150,16 +11600,16 @@ function writeConfig(path, config) {
11150
11600
  }
11151
11601
  function configStamp(path) {
11152
11602
  try {
11153
- return statSync(path).mtimeMs;
11603
+ return statSync2(path).mtimeMs;
11154
11604
  } catch {
11155
11605
  return 0;
11156
11606
  }
11157
11607
  }
11158
11608
  function writeAtomic(path, text) {
11159
11609
  const temp = `${path}.${process.pid}.tmp`;
11160
- writeFileSync(temp, text, { mode: 384 });
11610
+ writeFileSync2(temp, text, { mode: 384 });
11161
11611
  chmodSync(temp, 384);
11162
- renameSync2(temp, path);
11612
+ renameSync3(temp, path);
11163
11613
  }
11164
11614
  var PRESENCE_FIELDS, PresenceField, Config, DEFAULT_CONFIG, RETENTION_DEFAULTS, ROTATE_ENC_DAYS = 7, QUOTA_DEFAULTS;
11165
11615
  var init_home = __esm(() => {
@@ -11204,10 +11654,10 @@ var init_home = __esm(() => {
11204
11654
  });
11205
11655
 
11206
11656
  // packages/daemon/dist/lock.js
11207
- import { chmodSync as chmodSync2, closeSync as closeSync3, openSync as openSync3, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
11657
+ import { chmodSync as chmodSync2, closeSync as closeSync3, openSync as openSync2, readFileSync as readFileSync6, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
11208
11658
  function readPid(path) {
11209
11659
  try {
11210
- const text = readFileSync4(path, "utf8").trim();
11660
+ const text = readFileSync6(path, "utf8").trim();
11211
11661
  return /^\d+$/.test(text) ? Number(text) : undefined;
11212
11662
  } catch {
11213
11663
  return;
@@ -11220,7 +11670,7 @@ function isBusy(error) {
11220
11670
  function acquireLock(path) {
11221
11671
  const dbPath = `${path}.db`;
11222
11672
  try {
11223
- closeSync3(openSync3(dbPath, "wx", 384));
11673
+ closeSync3(openSync2(dbPath, "wx", 384));
11224
11674
  } catch (error) {
11225
11675
  if (error.code !== "EEXIST")
11226
11676
  throw error;
@@ -11238,10 +11688,10 @@ function acquireLock(path) {
11238
11688
  }
11239
11689
  try {
11240
11690
  const temp = `${path}.${process.pid}.tmp`;
11241
- writeFileSync2(temp, `${process.pid}
11691
+ writeFileSync3(temp, `${process.pid}
11242
11692
  `, { mode: 384 });
11243
11693
  chmodSync2(temp, 384);
11244
- renameSync3(temp, path);
11694
+ renameSync4(temp, path);
11245
11695
  } catch (error) {
11246
11696
  db.close();
11247
11697
  throw error;
@@ -11281,14 +11731,14 @@ var init_lock = __esm(() => {
11281
11731
  });
11282
11732
 
11283
11733
  // packages/daemon/dist/identity/keychain.js
11284
- import { spawnSync } from "child_process";
11285
- import { existsSync as existsSync3 } from "fs";
11734
+ import { spawnSync as spawnSync2 } from "child_process";
11735
+ import { existsSync as existsSync5 } from "fs";
11286
11736
  function keychainAvailable(run = runSecurity) {
11287
- if (process.platform !== "darwin" || !existsSync3(SECURITY))
11737
+ if (process.platform !== "darwin" || !existsSync5(SECURITY))
11288
11738
  return false;
11289
11739
  const result = run(["default-keychain", "-d", "user"], { timeoutMs: 5000 });
11290
11740
  const path = result.status === 0 ? /"(.+)"/.exec(result.stdout)?.[1] : undefined;
11291
- return path !== undefined && existsSync3(path);
11741
+ return path !== undefined && existsSync5(path);
11292
11742
  }
11293
11743
 
11294
11744
  class KeychainStore {
@@ -11457,7 +11907,7 @@ function keychainError(op, result) {
11457
11907
  }
11458
11908
  }
11459
11909
  var SECURITY = "/usr/bin/security", KEYCHAIN_SERVICE = "messaging-identity", LABEL = "messaging-identity", COMMENT = "Messaging machine keys: see messaging identity show", MAX_SECRET = 2000, READ_TIMEOUT_MS = 120000, WRITE_TIMEOUT_MS = 60000, runSecurity = (args, { input, timeoutMs }) => {
11460
- const result = spawnSync(SECURITY, args, {
11910
+ const result = spawnSync2(SECURITY, args, {
11461
11911
  input: input ?? "",
11462
11912
  encoding: "utf8",
11463
11913
  timeout: timeoutMs,
@@ -11604,18 +12054,18 @@ var init_identity2 = __esm(() => {
11604
12054
  });
11605
12055
 
11606
12056
  // packages/daemon/dist/pairing.js
11607
- import { chmodSync as chmodSync3, closeSync as closeSync4, constants as constants3, fstatSync as fstatSync2, openSync as openSync4, readFileSync as readFileSync5, renameSync as renameSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
11608
- import { join as join5 } from "path";
12057
+ import { chmodSync as chmodSync3, closeSync as closeSync4, constants as constants4, fstatSync as fstatSync2, readFileSync as readFileSync7, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
12058
+ import { join as join6 } from "path";
11609
12059
  function pairingPath(home) {
11610
- return join5(home, "pairing.json");
12060
+ return join6(home, "pairing.json");
11611
12061
  }
11612
12062
  function readPairing(home) {
11613
12063
  const path = pairingPath(home);
11614
12064
  let text;
11615
12065
  try {
11616
- const fd = openSync4(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
12066
+ const fd = openNoFollow(path, constants4.O_RDONLY);
11617
12067
  try {
11618
- text = readFileSync5(fd, "utf8");
12068
+ text = readFileSync7(fd, "utf8");
11619
12069
  } finally {
11620
12070
  closeSync4(fd);
11621
12071
  }
@@ -11644,15 +12094,15 @@ function writePairing(home, pending) {
11644
12094
  const { identity, ...rest } = pending;
11645
12095
  const body = JSON.stringify({ v: PROTOCOL, ...rest, identity: exportIdentity(identity) });
11646
12096
  const temp = `${path}.${process.pid}.tmp`;
11647
- writeFileSync3(temp, body, { mode: 384 });
12097
+ writeFileSync4(temp, body, { mode: 384 });
11648
12098
  chmodSync3(temp, 384);
11649
- renameSync4(temp, path);
12099
+ renameSync5(temp, path);
11650
12100
  }
11651
12101
  function removePairing(home) {
11652
12102
  const path = pairingPath(home);
11653
12103
  let fd;
11654
12104
  try {
11655
- fd = openSync4(path, constants3.O_WRONLY | constants3.O_NOFOLLOW);
12105
+ fd = openNoFollow(path, constants4.O_WRONLY);
11656
12106
  } catch (error) {
11657
12107
  const code = error.code;
11658
12108
  if (code === "ENOENT")
@@ -11662,7 +12112,7 @@ function removePairing(home) {
11662
12112
  throw error;
11663
12113
  }
11664
12114
  try {
11665
- writeFileSync3(fd, Buffer.alloc(fstatSync2(fd).size));
12115
+ writeFileSync4(fd, Buffer.alloc(fstatSync2(fd).size));
11666
12116
  } finally {
11667
12117
  closeSync4(fd);
11668
12118
  }
@@ -11670,6 +12120,7 @@ function removePairing(home) {
11670
12120
  }
11671
12121
  var PairingFile;
11672
12122
  var init_pairing = __esm(() => {
12123
+ init_dist2();
11673
12124
  init_zod();
11674
12125
  init_dist();
11675
12126
  PairingFile = discriminatedUnion("kind", [
@@ -12240,7 +12691,7 @@ var init_chat = __esm(() => {
12240
12691
  });
12241
12692
 
12242
12693
  // packages/daemon/dist/log.js
12243
- import { closeSync as closeSync5, constants as constants4, lstatSync as lstatSync3, openSync as openSync5, renameSync as renameSync5, writeSync as writeSync2 } from "fs";
12694
+ import { closeSync as closeSync5, constants as constants5, lstatSync as lstatSync3, renameSync as renameSync6, writeSync as writeSync2 } from "fs";
12244
12695
  function formatLine(level, event, fields = {}) {
12245
12696
  const record = { t: new Date().toISOString(), level, event };
12246
12697
  for (const [name, value] of Object.entries(fields))
@@ -12267,7 +12718,7 @@ function createLogger(options) {
12267
12718
  rotate2(file, keep);
12268
12719
  size = 0;
12269
12720
  }
12270
- const fd = openSync5(file, constants4.O_WRONLY | constants4.O_APPEND | constants4.O_CREAT | constants4.O_NOFOLLOW, 384);
12721
+ const fd = openNoFollow(file, constants5.O_WRONLY | constants5.O_APPEND | constants5.O_CREAT, 384);
12271
12722
  try {
12272
12723
  writeSync2(fd, line);
12273
12724
  } finally {
@@ -12292,10 +12743,10 @@ function currentSize(file) {
12292
12743
  function rotate2(file, keep) {
12293
12744
  for (let n = keep - 1;n >= 1; n--) {
12294
12745
  try {
12295
- renameSync5(`${file}.${n}`, `${file}.${n + 1}`);
12746
+ renameSync6(`${file}.${n}`, `${file}.${n + 1}`);
12296
12747
  } catch {}
12297
12748
  }
12298
- renameSync5(file, `${file}.1`);
12749
+ renameSync6(file, `${file}.1`);
12299
12750
  }
12300
12751
 
12301
12752
  class LogBudget {
@@ -12330,6 +12781,7 @@ function codeOf(error) {
12330
12781
  }
12331
12782
  var RANK, LOG_MAX_BYTES, LOG_KEEP = 3;
12332
12783
  var init_log = __esm(() => {
12784
+ init_dist2();
12333
12785
  RANK = { debug: 0, info: 1, warn: 2, error: 3 };
12334
12786
  LOG_MAX_BYTES = 10 * 1024 * 1024;
12335
12787
  });
@@ -13401,7 +13853,7 @@ var init_storage_errors = __esm(() => {
13401
13853
  });
13402
13854
 
13403
13855
  // packages/daemon/dist/store.js
13404
- import { chmodSync as chmodSync4, closeSync as closeSync6, openSync as openSync6 } from "fs";
13856
+ import { chmodSync as chmodSync4, closeSync as closeSync6, openSync as openSync3 } from "fs";
13405
13857
  import { DatabaseSync } from "node:sqlite";
13406
13858
  function additiveColumn(decl) {
13407
13859
  if (/\b(PRIMARY|UNIQUE|CHECK|REFERENCES|COLLATE|GENERATED|AS)\b/i.test(decl))
@@ -13454,7 +13906,7 @@ class Store {
13454
13906
  #depth = 0;
13455
13907
  constructor(path) {
13456
13908
  if (path !== ":memory:") {
13457
- closeSync6(openSync6(path, "a", 384));
13909
+ closeSync6(openSync3(path, "a", 384));
13458
13910
  chmodSync4(path, 384);
13459
13911
  }
13460
13912
  this.#db = new DatabaseSync(path, { timeout: 5000 });
@@ -14374,13 +14826,14 @@ var init_version = __esm(() => {
14374
14826
  });
14375
14827
 
14376
14828
  // packages/daemon/dist/wake-bridge.js
14377
- import { closeSync as closeSync7, constants as constants5, fstatSync as fstatSync3, openSync as openSync7, readSync } from "fs";
14829
+ import { closeSync as closeSync7, constants as constants6, fstatSync as fstatSync3, lstatSync as lstatSync4, openSync as openSync4, readSync } from "fs";
14378
14830
  import { request } from "http";
14379
14831
  import { homedir as homedir2 } from "os";
14380
- import { isAbsolute, join as join6 } from "path";
14381
- function serviceFile(env = process.env) {
14382
- const state = env.XDG_STATE_HOME && isAbsolute(env.XDG_STATE_HOME) ? env.XDG_STATE_HOME : join6(env.HOME || homedir2(), ".local", "state");
14383
- return join6(state, "opencode", "service.json");
14832
+ import { isAbsolute, join as join7 } from "path";
14833
+ function serviceFile(env = process.env, windows = IS_WINDOWS) {
14834
+ const home = (windows ? env.USERPROFILE : env.HOME) || homedir2();
14835
+ const state = env.XDG_STATE_HOME && isAbsolute(env.XDG_STATE_HOME) ? env.XDG_STATE_HOME : join7(home, ".local", "state");
14836
+ return join7(state, "opencode", "service.json");
14384
14837
  }
14385
14838
  function loopbackOrigin(url) {
14386
14839
  let parsed;
@@ -14397,16 +14850,27 @@ function loopbackOrigin(url) {
14397
14850
  return parsed.origin;
14398
14851
  }
14399
14852
  function readService(file) {
14853
+ if (IS_WINDOWS) {
14854
+ try {
14855
+ const stats = lstatSync4(file);
14856
+ if (stats.isSymbolicLink() || !stats.isFile() || aclProblem(file, { pinnedOwner: false }) !== undefined)
14857
+ return "service_unsafe";
14858
+ } catch (error) {
14859
+ if (error instanceof MessagingError && error.code === "daemon_unavailable")
14860
+ return "no_service";
14861
+ return error.code === "ENOENT" ? "no_service" : "service_unsafe";
14862
+ }
14863
+ }
14400
14864
  let fd;
14401
14865
  try {
14402
- fd = openSync7(file, constants5.O_RDONLY | constants5.O_NOFOLLOW);
14866
+ fd = openSync4(file, constants6.O_RDONLY | constants6.O_NOFOLLOW);
14403
14867
  } catch (error) {
14404
14868
  return error.code === "ENOENT" ? "no_service" : "service_unsafe";
14405
14869
  }
14406
14870
  try {
14407
14871
  const stats = fstatSync3(fd);
14408
14872
  const euid = process.geteuid?.();
14409
- if (!stats.isFile() || euid !== undefined && stats.uid !== euid || (stats.mode & 18) !== 0)
14873
+ if (!stats.isFile() || !IS_WINDOWS && (euid !== undefined && stats.uid !== euid || (stats.mode & 18) !== 0))
14410
14874
  return "service_unsafe";
14411
14875
  const buffer = Buffer.alloc(SERVICE_BYTES + 1);
14412
14876
  const length = readSync(fd, buffer, 0, buffer.length, 0);
@@ -14621,6 +15085,7 @@ function post2(url, headers, body, signal) {
14621
15085
  var WAKE_RPC, BRIDGE_TIMING, SERVICE_BYTES = 4096, ANSWER_BYTES = 65536, AnswerTooLarge;
14622
15086
  var init_wake_bridge = __esm(() => {
14623
15087
  init_dist();
15088
+ init_dist2();
14624
15089
  WAKE_RPC = { id: "messaging.opencode", method: "wake" };
14625
15090
  BRIDGE_TIMING = { timeoutMs: 1e4, retryMs: [1000, 2000, 4000], concurrency: 4, batch: 16 };
14626
15091
  AnswerTooLarge = class AnswerTooLarge extends Error {
@@ -18479,20 +18944,20 @@ var CHAT_COMMANDS = [list, read, post, search];
18479
18944
  init_dist();
18480
18945
  init_dist2();
18481
18946
  import { spawn } from "child_process";
18482
- import { closeSync, constants, existsSync, mkdirSync as mkdirSync3, openSync, readFileSync } from "fs";
18483
- import { join as join2 } from "path";
18947
+ import { closeSync, constants as constants2, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "fs";
18948
+ import { join as join3 } from "path";
18484
18949
  function logPath(home) {
18485
- return join2(home, "logs", "messagingd.log");
18950
+ return join3(home, "logs", "messagingd.log");
18486
18951
  }
18487
18952
  function outputPath(home) {
18488
- return join2(home, "logs", "messagingd.out");
18953
+ return join3(home, "logs", "messagingd.out");
18489
18954
  }
18490
18955
  function lockPath(home) {
18491
- return join2(home, "run", "messagingd.lock");
18956
+ return join3(home, "run", "messagingd.lock");
18492
18957
  }
18493
18958
  function openOutput(path) {
18494
18959
  try {
18495
- return openSync(path, constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_NOFOLLOW, 384);
18960
+ return openNoFollow(path, constants2.O_WRONLY | constants2.O_APPEND | constants2.O_CREAT, 384);
18496
18961
  } catch (error) {
18497
18962
  const code = error.code ?? "error";
18498
18963
  const why = code === "ELOOP" ? "it is a symlink" : code;
@@ -18508,7 +18973,7 @@ function positive(n) {
18508
18973
  }
18509
18974
  function readLockPid(home) {
18510
18975
  try {
18511
- return parseLockPid(readFileSync(lockPath(home), "utf8"));
18976
+ return parseLockPid(readFileSync3(lockPath(home), "utf8"));
18512
18977
  } catch {
18513
18978
  return;
18514
18979
  }
@@ -18568,7 +19033,7 @@ var start = {
18568
19033
  return;
18569
19034
  }
18570
19035
  const command = daemonCommand(ctx.io);
18571
- mkdirSync3(join2(ctx.home, "logs"), { recursive: true, mode: 448 });
19036
+ mkdirSync3(join3(ctx.home, "logs"), { recursive: true, mode: 448 });
18572
19037
  const output = outputPath(ctx.home);
18573
19038
  const fd = openOutput(output);
18574
19039
  let exited;
@@ -18576,6 +19041,7 @@ var start = {
18576
19041
  const child = spawn(command[0], [...command.slice(1), "--home", ctx.home], {
18577
19042
  cwd: ctx.home,
18578
19043
  detached: true,
19044
+ windowsHide: true,
18579
19045
  env: daemonEnv(ctx.io.env, ctx.home),
18580
19046
  stdio: ["ignore", fd, fd]
18581
19047
  });
@@ -18603,12 +19069,12 @@ var start = {
18603
19069
  };
18604
19070
  function daemonCommand(io) {
18605
19071
  if (io.daemonCommand) {
18606
- if (!existsSync(io.daemonCommand[0]))
19072
+ if (!existsSync2(io.daemonCommand[0]))
18607
19073
  throw new MessagingError("internal", "cannot find the messagingd executable");
18608
19074
  return io.daemonCommand;
18609
19075
  }
18610
19076
  const script = io.messagingd;
18611
- if (!script || !existsSync(script))
19077
+ if (!script || !existsSync2(script))
18612
19078
  throw new MessagingError("internal", "cannot find messagingd.js next to messaging.js");
18613
19079
  return [io.execPath ?? process.execPath, script];
18614
19080
  }
@@ -18658,8 +19124,10 @@ var stop = {
18658
19124
  details: [
18659
19125
  "Asks messagingd to shut down (daemon.stop) and waits for its socket to go away.",
18660
19126
  "If messagingd does not answer it, sends SIGTERM instead, but only to the pid the daemon on the",
18661
- "socket reports, and only when <home>/run/messagingd.lock names the same pid.",
18662
- "A messagingd of another Messaging version gets SIGTERM the same way, at the pid its hello reports."
19127
+ "socket reports, and only when <home>/run/messagingd.lock names the same pid, then and at the moment of the signal.",
19128
+ "A messagingd of another Messaging version gets SIGTERM the same way, at the pid its hello reports.",
19129
+ `On Windows, which has no signals, "SIGTERM" ends the process at once (TerminateProcess); the home's`,
19130
+ "lock is released and the store stays consistent."
18663
19131
  ].join(`
18664
19132
  `),
18665
19133
  args: { min: 0, max: 0 },
@@ -18672,8 +19140,8 @@ var stop = {
18672
19140
  throw new MessagingError("daemon_mismatch", `the process on the messagingd socket does not say which version it is${locked !== undefined ? `, and its lock names pid ${locked}` : ""}; stop it with the \`messaging daemon stop\` of its own version, or end that process yourself`);
18673
19141
  }
18674
19142
  const pid = verifiedPid(locked, hello.pid);
18675
- ctx.out.note(`messagingd for this home is version ${inline(hello.version, 64)}, not this messaging's; sending SIGTERM to pid ${pid}.`);
18676
- await terminate(ctx, pid, "SIGTERM");
19143
+ ctx.out.note(`messagingd for this home is version ${inline(hello.version, 64)}, not this messaging's; ${SIGNAL_NOTE} pid ${pid}.`);
19144
+ await terminate(ctx, pid, "SIGTERM", locked);
18677
19145
  return;
18678
19146
  }
18679
19147
  if (!running) {
@@ -18690,23 +19158,30 @@ var stop = {
18690
19158
  const request = await requestStop(ctx);
18691
19159
  if (request !== "accepted") {
18692
19160
  pid = verifiedPid(locked, pid);
18693
- ctx.out.note(`messagingd did not answer daemon.stop; sending SIGTERM to pid ${pid}.`);
19161
+ ctx.out.note(`messagingd did not answer daemon.stop; ${SIGNAL_NOTE} pid ${pid}.`);
18694
19162
  }
18695
- await terminate(ctx, pid, request === "accepted" ? "daemon.stop" : "SIGTERM");
19163
+ await terminate(ctx, pid, request === "accepted" ? "daemon.stop" : "SIGTERM", locked);
18696
19164
  }
18697
19165
  };
18698
- async function terminate(ctx, pid, method) {
19166
+ var SIGNAL_NOTE = IS_WINDOWS ? "ending (TerminateProcess)" : "sending SIGTERM to";
19167
+ async function terminate(ctx, pid, method, locked) {
18699
19168
  if (method === "SIGTERM" && pid !== undefined) {
18700
- try {
18701
- process.kill(pid, "SIGTERM");
18702
- } catch (error) {
18703
- if (error.code !== "ESRCH")
18704
- throw new MessagingError("forbidden", `cannot signal messagingd (pid ${pid})`);
18705
- }
19169
+ const now = locked === undefined ? pid : readLockPid(ctx.home);
19170
+ if (now !== pid) {
19171
+ if (now !== undefined)
19172
+ throw new MessagingError("internal", `the lock now names pid ${now}, not ${pid}; not sending a signal`);
19173
+ ctx.out.note(`messagingd (pid ${pid}) released its lock; not sending a signal.`);
19174
+ } else
19175
+ try {
19176
+ process.kill(pid, "SIGTERM");
19177
+ } catch (error) {
19178
+ if (error.code !== "ESRCH")
19179
+ throw new MessagingError("forbidden", `cannot signal messagingd (pid ${pid})`);
19180
+ }
18706
19181
  }
18707
19182
  const which = pid !== undefined ? `messagingd (pid ${pid})` : "messagingd";
18708
19183
  const deadline = Date.now() + ctx.timing.stopTimeoutMs;
18709
- while (existsSync(ctx.socketPath) && (pid === undefined || alive(pid))) {
19184
+ while (existsSync2(ctx.socketPath) && (pid === undefined || alive(pid))) {
18710
19185
  if (Date.now() >= deadline) {
18711
19186
  throw new MessagingError("internal", `${which} ${method === "daemon.stop" ? "accepted daemon.stop but " : ""}did not stop within ${duration2(ctx.timing.stopTimeoutMs)}`);
18712
19187
  }
@@ -19462,7 +19937,7 @@ var invite = {
19462
19937
  ctx.out.note(`${person} joins with: messaging team join - --label <label> (then paste the ticket and press Ctrl-D)`);
19463
19938
  }
19464
19939
  };
19465
- var join4 = {
19940
+ var join5 = {
19466
19941
  name: "team join",
19467
19942
  summary: "Join a team with an invite ticket",
19468
19943
  usage: "<ticket|-> --label <label>",
@@ -19752,7 +20227,7 @@ var prepare = {
19752
20227
  ctx.out.note("Give this machine id to the relay operator for --allow-team-create, then run `messaging team create`.");
19753
20228
  }
19754
20229
  };
19755
- var TEAM_COMMANDS = [prepare, create, invite, join4, members2, remove, role, revokeInvite, card, add, pair, claim2, pairCancel];
20230
+ var TEAM_COMMANDS = [prepare, create, invite, join5, members2, remove, role, revokeInvite, card, add, pair, claim2, pairCancel];
19756
20231
 
19757
20232
  // packages/daemon/dist/cli/version.js
19758
20233
  init_dist();
@@ -19815,7 +20290,7 @@ function resolveTarget(words) {
19815
20290
  const spec = BY_NAME.get(first);
19816
20291
  return spec ? { kind: "command", spec, rest: words.slice(1) } : { kind: "unknown", name: first };
19817
20292
  }
19818
- async function run(argv, io) {
20293
+ async function run2(argv, io) {
19819
20294
  const args = [...argv];
19820
20295
  let values;
19821
20296
  let positionals;
@@ -19982,7 +20457,7 @@ function cliMain(argv, start) {
19982
20457
  process.exit(process.exitCode ?? 0);
19983
20458
  });
19984
20459
  const env = process.env;
19985
- return run([...argv], {
20460
+ return run2([...argv], {
19986
20461
  stdout: process.stdout,
19987
20462
  stderr: process.stderr,
19988
20463
  get stdin() {
@@ -20074,17 +20549,18 @@ ${USAGE}`);
20074
20549
  `);
20075
20550
  return 1;
20076
20551
  }
20552
+ const signals = process.platform === "win32" ? ["SIGINT", "SIGBREAK"] : ["SIGTERM", "SIGINT"];
20077
20553
  const signal = await Promise.race([
20078
20554
  new Promise((done) => {
20079
- process.once("SIGTERM", () => done("SIGTERM"));
20080
- process.once("SIGINT", () => done("SIGINT"));
20555
+ for (const name of signals)
20556
+ process.once(name, () => done(name));
20081
20557
  }),
20082
20558
  daemon.closed.then(() => {
20083
20559
  return;
20084
20560
  })
20085
20561
  ]);
20086
20562
  if (signal !== undefined) {
20087
- for (const name of ["SIGTERM", "SIGINT"])
20563
+ for (const name of signals)
20088
20564
  process.once(name, () => process.exit(1));
20089
20565
  log("info", "daemon.signal", { signal });
20090
20566
  }
@@ -20093,9 +20569,10 @@ ${USAGE}`);
20093
20569
  }
20094
20570
 
20095
20571
  // packages/daemon/dist/bin/dispatch.js
20096
- import { basename } from "path";
20097
- function dispatch(argv0, argv) {
20098
- if (basename(argv0) === "messaging")
20572
+ import { basename, win32 } from "path";
20573
+ function dispatch(argv0, argv, windows = process.platform === "win32") {
20574
+ const name = windows ? win32.basename(argv0).toLowerCase().replace(/\.exe$/, "") : basename(argv0);
20575
+ if (name === "messaging")
20099
20576
  return { mode: "cli", args: argv };
20100
20577
  if (argv[0] === "cli")
20101
20578
  return { mode: "cli", args: argv.slice(1) };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joe-broadhead/messaging-bin-linux-x64-gnu",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The self-contained messagingd executable for one platform: the Messaging daemon and its CLI (`messagingd cli …`, or invoked as `messaging`), with no Node.js, npm install or install script on the machine",
5
5
  "license": "MIT",
6
6
  "author": "Joe Broadhead",