moonlait 0.2.0 → 0.3.0

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.
Files changed (3) hide show
  1. package/README.md +13 -0
  2. package/dist/cli.js +103 -63
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -46,6 +46,19 @@ moonlait service install
46
46
  `moonlait service status` says whether it is running, `moonlait service
47
47
  uninstall` removes it, and its log is `~/.moonlait/bridge.log`.
48
48
 
49
+ ### Two at once
50
+
51
+ A second bridge — a local Moonlait beside the real one — keeps its own
52
+ pairing and its own service, so neither displaces the other:
53
+
54
+ ```sh
55
+ moonlait pair <CODE> --server http://localhost:3100 --config ~/.moonlait/dev.json
56
+ moonlait service install --service dev --config ~/.moonlait/dev.json
57
+ ```
58
+
59
+ `--service dev` goes on `status` and `uninstall` too, and that bridge logs
60
+ to `~/.moonlait/bridge.dev.log`.
61
+
49
62
  To run it just for now instead, in a window you keep open:
50
63
 
51
64
  ```sh
package/dist/cli.js CHANGED
@@ -9549,22 +9549,22 @@ var error32 = () => {
9549
9549
  return `\uC798\uBABB\uB41C \uC635\uC158: ${joinValues(issue2.values, "\uB610\uB294 ")} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4`;
9550
9550
  case "too_big": {
9551
9551
  const adj = issue2.inclusive ? "\uC774\uD558" : "\uBBF8\uB9CC";
9552
- const suffix = adj === "\uBBF8\uB9CC" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
9552
+ const suffix2 = adj === "\uBBF8\uB9CC" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
9553
9553
  const sizing = getSizing(issue2.origin);
9554
9554
  const unit2 = sizing?.unit ?? "\uC694\uC18C";
9555
9555
  if (sizing)
9556
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()}${unit2} ${adj}${suffix}`;
9557
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()} ${adj}${suffix}`;
9556
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()}${unit2} ${adj}${suffix2}`;
9557
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()} ${adj}${suffix2}`;
9558
9558
  }
9559
9559
  case "too_small": {
9560
9560
  const adj = issue2.inclusive ? "\uC774\uC0C1" : "\uCD08\uACFC";
9561
- const suffix = adj === "\uC774\uC0C1" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
9561
+ const suffix2 = adj === "\uC774\uC0C1" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
9562
9562
  const sizing = getSizing(issue2.origin);
9563
9563
  const unit2 = sizing?.unit ?? "\uC694\uC18C";
9564
9564
  if (sizing) {
9565
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()}${unit2} ${adj}${suffix}`;
9565
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()}${unit2} ${adj}${suffix2}`;
9566
9566
  }
9567
- return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()} ${adj}${suffix}`;
9567
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()} ${adj}${suffix2}`;
9568
9568
  }
9569
9569
  case "invalid_format": {
9570
9570
  const _issue = issue2;
@@ -13472,9 +13472,9 @@ function codePointLengthVar(doc, ctx, accessor, inDoubt) {
13472
13472
  doc.write(`const ${v} = typeof ${accessor} === "string" && ${inDoubt} ? ${cpLen}(${accessor}) : ${accessor}.length;`);
13473
13473
  return v;
13474
13474
  }
13475
- function numericOperand(value, label) {
13475
+ function numericOperand(value, label2) {
13476
13476
  if (typeof value !== "number" || !Number.isFinite(value)) {
13477
- throw new ZodCompileUnsupportedError(`${label} bound of type ${typeof value}`);
13477
+ throw new ZodCompileUnsupportedError(`${label2} bound of type ${typeof value}`);
13478
13478
  }
13479
13479
  return `${value}`;
13480
13480
  }
@@ -13749,8 +13749,8 @@ function generateStringFormatCheck(doc, ctx, def, accessor, needsValue = true) {
13749
13749
  break;
13750
13750
  }
13751
13751
  case "ends_with": {
13752
- const suffix = def.suffix;
13753
- doc.write(`if (${accessor}.slice(-${suffix.length}) !== ${esc(suffix)}) return INVALID;`);
13752
+ const suffix2 = def.suffix;
13753
+ doc.write(`if (${accessor}.slice(-${suffix2.length}) !== ${esc(suffix2)}) return INVALID;`);
13754
13754
  break;
13755
13755
  }
13756
13756
  default: {
@@ -15439,12 +15439,12 @@ function _startsWith(prefix, params) {
15439
15439
  });
15440
15440
  }
15441
15441
  // @__NO_SIDE_EFFECTS__
15442
- function _endsWith(suffix, params) {
15442
+ function _endsWith(suffix2, params) {
15443
15443
  return new $ZodCheckEndsWith({
15444
15444
  check: "string_format",
15445
15445
  format: "ends_with",
15446
15446
  ...normalizeParams(params),
15447
- suffix
15447
+ suffix: suffix2
15448
15448
  });
15449
15449
  }
15450
15450
  // @__NO_SIDE_EFFECTS__
@@ -24242,28 +24242,32 @@ import { homedir as homedir2 } from "node:os";
24242
24242
  import path5 from "node:path";
24243
24243
  var LABEL = "com.moonlait.bridge";
24244
24244
  var TASK = "Moonlait bridge";
24245
- var logPath = () => path5.join(bridgeHome(), "bridge.log");
24246
- function plistPath() {
24247
- return path5.join(homedir2(), "Library", "LaunchAgents", `${LABEL}.plist`);
24248
- }
24249
- function unitPath() {
24250
- return path5.join(homedir2(), ".config", "systemd", "user", "moonlait.service");
24251
- }
24252
- var runnerPath = () => path5.join(bridgeHome(), "bridge-service.cmd");
24253
- var launcherPath = () => path5.join(bridgeHome(), "bridge-service.vbs");
24245
+ var suffix = (service) => service === void 0 ? "" : `.${service}`;
24246
+ var label = (service) => `${LABEL}${suffix(service)}`;
24247
+ var taskName = (service) => service === void 0 ? TASK : `${TASK} (${service})`;
24248
+ var unitName = (service) => service === void 0 ? "moonlait.service" : `moonlait-${service}.service`;
24249
+ var logPath = (service) => path5.join(bridgeHome(), `bridge${suffix(service)}.log`);
24250
+ function plistPath(service) {
24251
+ return path5.join(homedir2(), "Library", "LaunchAgents", `${label(service)}.plist`);
24252
+ }
24253
+ function unitPath(service) {
24254
+ return path5.join(homedir2(), ".config", "systemd", "user", unitName(service));
24255
+ }
24256
+ var runnerPath = (service) => path5.join(bridgeHome(), `bridge-service${suffix(service)}.cmd`);
24257
+ var launcherPath = (service) => path5.join(bridgeHome(), `bridge-service${suffix(service)}.vbs`);
24254
24258
  function runnerScript(paths) {
24255
24259
  const config2 = paths.config === void 0 ? "" : ` --config "${paths.config}"`;
24256
24260
  return [
24257
24261
  "@echo off",
24258
24262
  ":loop",
24259
- `"${paths.node}" "${paths.script}" start${config2} >> "${logPath()}" 2>&1`,
24263
+ `"${paths.node}" "${paths.script}" start${config2} >> "${logPath(paths.service)}" 2>&1`,
24260
24264
  "timeout /t 5 /nobreak > nul",
24261
24265
  "goto loop",
24262
24266
  ""
24263
24267
  ].join("\r\n");
24264
24268
  }
24265
- function launcherScript() {
24266
- return ['Set shell = CreateObject("WScript.Shell")', `shell.Run """${runnerPath()}""", 0, False`, ""].join("\r\n");
24269
+ function launcherScript(service) {
24270
+ return ['Set shell = CreateObject("WScript.Shell")', `shell.Run """${runnerPath(service)}""", 0, False`, ""].join("\r\n");
24267
24271
  }
24268
24272
  var escape = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
24269
24273
  function plist(paths) {
@@ -24272,7 +24276,7 @@ function plist(paths) {
24272
24276
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
24273
24277
  <plist version="1.0">
24274
24278
  <dict>
24275
- <key>Label</key><string>${LABEL}</string>
24279
+ <key>Label</key><string>${label(paths.service)}</string>
24276
24280
  <key>ProgramArguments</key>
24277
24281
  <array>${args.map((arg) => `
24278
24282
  <string>${escape(arg)}</string>`).join("")}
@@ -24285,8 +24289,8 @@ function plist(paths) {
24285
24289
  <key>PATH</key><string>${escape(process.env.PATH ?? "/usr/bin:/bin")}</string>
24286
24290
  <key>HOME</key><string>${escape(homedir2())}</string>
24287
24291
  </dict>
24288
- <key>StandardOutPath</key><string>${escape(logPath())}</string>
24289
- <key>StandardErrorPath</key><string>${escape(logPath())}</string>
24292
+ <key>StandardOutPath</key><string>${escape(logPath(paths.service))}</string>
24293
+ <key>StandardErrorPath</key><string>${escape(logPath(paths.service))}</string>
24290
24294
  </dict>
24291
24295
  </plist>
24292
24296
  `;
@@ -24310,53 +24314,76 @@ WantedBy=default.target
24310
24314
  function run(command, args) {
24311
24315
  execFileSync2(command, [...args], { stdio: "ignore" });
24312
24316
  }
24317
+ function waitUntilUnloaded(target, tries = 20) {
24318
+ for (let attempt = 0; attempt < tries; attempt += 1) {
24319
+ try {
24320
+ execFileSync2("launchctl", ["print", target], { stdio: "ignore" });
24321
+ } catch {
24322
+ return;
24323
+ }
24324
+ execFileSync2("sleep", ["0.25"], { stdio: "ignore" });
24325
+ }
24326
+ }
24313
24327
  function install(paths) {
24314
24328
  if (process.platform === "darwin") {
24315
- const file2 = plistPath();
24329
+ const file2 = plistPath(paths.service);
24316
24330
  mkdirSync2(path5.dirname(file2), { recursive: true });
24317
24331
  mkdirSync2(bridgeHome(), { recursive: true });
24318
24332
  writeFileSync2(file2, plist(paths));
24333
+ const domain2 = `gui/${process.getuid?.() ?? 501}`;
24334
+ const name = label(paths.service);
24319
24335
  try {
24320
- run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${LABEL}`]);
24336
+ run("launchctl", ["bootout", `${domain2}/${name}`]);
24321
24337
  } catch {
24322
24338
  }
24323
- run("launchctl", ["bootstrap", `gui/${process.getuid?.() ?? 501}`, file2]);
24339
+ waitUntilUnloaded(`${domain2}/${name}`);
24340
+ try {
24341
+ run("launchctl", ["bootstrap", domain2, file2]);
24342
+ } catch {
24343
+ waitUntilUnloaded(`${domain2}/${name}`);
24344
+ try {
24345
+ run("launchctl", ["bootstrap", domain2, file2]);
24346
+ } catch {
24347
+ run("launchctl", ["kickstart", "-k", `${domain2}/${name}`]);
24348
+ }
24349
+ }
24324
24350
  return file2;
24325
24351
  }
24326
24352
  if (process.platform === "linux") {
24327
- const file2 = unitPath();
24353
+ const file2 = unitPath(paths.service);
24328
24354
  mkdirSync2(path5.dirname(file2), { recursive: true });
24329
24355
  writeFileSync2(file2, unit(paths));
24330
24356
  run("systemctl", ["--user", "daemon-reload"]);
24331
- run("systemctl", ["--user", "enable", "--now", "moonlait.service"]);
24357
+ run("systemctl", ["--user", "enable", "--now", unitName(paths.service)]);
24332
24358
  return file2;
24333
24359
  }
24334
24360
  if (process.platform === "win32") {
24335
24361
  mkdirSync2(bridgeHome(), { recursive: true });
24336
- writeFileSync2(runnerPath(), runnerScript(paths));
24337
- writeFileSync2(launcherPath(), launcherScript());
24338
- run("schtasks", ["/Create", "/TN", TASK, "/TR", `wscript.exe "${launcherPath()}"`, "/SC", "ONLOGON", "/F"]);
24339
- run("schtasks", ["/Run", "/TN", TASK]);
24340
- return launcherPath();
24362
+ writeFileSync2(runnerPath(paths.service), runnerScript(paths));
24363
+ writeFileSync2(launcherPath(paths.service), launcherScript(paths.service));
24364
+ const task = taskName(paths.service);
24365
+ run("schtasks", ["/Create", "/TN", task, "/TR", `wscript.exe "${launcherPath(paths.service)}"`, "/SC", "ONLOGON", "/F"]);
24366
+ run("schtasks", ["/Run", "/TN", task]);
24367
+ return launcherPath(paths.service);
24341
24368
  }
24342
24369
  throw new Error(`Installing as a service is not supported on ${process.platform}. Run \`moonlait start\` in a terminal instead.`);
24343
24370
  }
24344
- function uninstall() {
24371
+ function uninstall(service) {
24345
24372
  if (process.platform === "darwin") {
24346
- const file2 = plistPath();
24373
+ const file2 = plistPath(service);
24347
24374
  if (!existsSync4(file2)) return null;
24348
24375
  try {
24349
- run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${LABEL}`]);
24376
+ run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${label(service)}`]);
24350
24377
  } catch {
24351
24378
  }
24352
24379
  rmSync(file2);
24353
24380
  return file2;
24354
24381
  }
24355
24382
  if (process.platform === "linux") {
24356
- const file2 = unitPath();
24383
+ const file2 = unitPath(service);
24357
24384
  if (!existsSync4(file2)) return null;
24358
24385
  try {
24359
- run("systemctl", ["--user", "disable", "--now", "moonlait.service"]);
24386
+ run("systemctl", ["--user", "disable", "--now", unitName(service)]);
24360
24387
  } catch {
24361
24388
  }
24362
24389
  rmSync(file2);
@@ -24364,24 +24391,24 @@ function uninstall() {
24364
24391
  return file2;
24365
24392
  }
24366
24393
  if (process.platform === "win32") {
24367
- if (!existsSync4(launcherPath())) return null;
24394
+ if (!existsSync4(launcherPath(service))) return null;
24368
24395
  try {
24369
- run("schtasks", ["/End", "/TN", TASK]);
24370
- run("schtasks", ["/Delete", "/TN", TASK, "/F"]);
24396
+ run("schtasks", ["/End", "/TN", taskName(service)]);
24397
+ run("schtasks", ["/Delete", "/TN", taskName(service), "/F"]);
24371
24398
  } catch {
24372
24399
  }
24373
- rmSync(launcherPath());
24374
- rmSync(runnerPath(), { force: true });
24375
- return launcherPath();
24400
+ rmSync(launcherPath(service));
24401
+ rmSync(runnerPath(service), { force: true });
24402
+ return launcherPath(service);
24376
24403
  }
24377
24404
  throw new Error(`Nothing to remove: the service is not installed on ${process.platform}.`);
24378
24405
  }
24379
- function status() {
24406
+ function status(service) {
24380
24407
  if (process.platform === "darwin") {
24381
- const file2 = plistPath();
24408
+ const file2 = plistPath(service);
24382
24409
  let running = false;
24383
24410
  try {
24384
- execFileSync2("launchctl", ["print", `gui/${process.getuid?.() ?? 501}/${LABEL}`], { stdio: "ignore" });
24411
+ execFileSync2("launchctl", ["print", `gui/${process.getuid?.() ?? 501}/${label(service)}`], { stdio: "ignore" });
24385
24412
  running = true;
24386
24413
  } catch {
24387
24414
  running = false;
@@ -24389,10 +24416,10 @@ function status() {
24389
24416
  return { installed: existsSync4(file2), running, file: file2 };
24390
24417
  }
24391
24418
  if (process.platform === "linux") {
24392
- const file2 = unitPath();
24419
+ const file2 = unitPath(service);
24393
24420
  let running = false;
24394
24421
  try {
24395
- execFileSync2("systemctl", ["--user", "is-active", "--quiet", "moonlait.service"], { stdio: "ignore" });
24422
+ execFileSync2("systemctl", ["--user", "is-active", "--quiet", unitName(service)], { stdio: "ignore" });
24396
24423
  running = true;
24397
24424
  } catch {
24398
24425
  running = false;
@@ -24400,10 +24427,10 @@ function status() {
24400
24427
  return { installed: existsSync4(file2), running, file: file2 };
24401
24428
  }
24402
24429
  if (process.platform === "win32") {
24403
- const file2 = launcherPath();
24430
+ const file2 = launcherPath(service);
24404
24431
  let running = false;
24405
24432
  try {
24406
- const output2 = execFileSync2("schtasks", ["/Query", "/TN", TASK, "/FO", "LIST"], { encoding: "utf8" });
24433
+ const output2 = execFileSync2("schtasks", ["/Query", "/TN", taskName(service), "/FO", "LIST"], { encoding: "utf8" });
24407
24434
  running = /\bRunning\b/i.test(output2);
24408
24435
  } catch {
24409
24436
  return { installed: false, running: false, file: file2 };
@@ -24515,21 +24542,25 @@ async function main() {
24515
24542
  }
24516
24543
  if (command === "service") {
24517
24544
  const action = argument ?? "status";
24545
+ const service = option("service");
24546
+ if (service !== void 0 && !/^[a-z0-9-]{1,20}$/.test(service)) {
24547
+ throw new Error("A service name is lowercase letters, numbers and dashes, e.g. --service dev");
24548
+ }
24518
24549
  if (action === "status") {
24519
- const state = status();
24550
+ const state = status(service);
24520
24551
  console.log(
24521
24552
  !state.installed ? `Not installed. Keep the bridge running with: ${RUN} service install` : state.running ? `Installed and running (${state.file}).
24522
- Log: ${logPath()}` : `Installed but not running (${state.file}).
24523
- Log: ${logPath()}`
24553
+ Log: ${logPath(service)}` : `Installed but not running (${state.file}).
24554
+ Log: ${logPath(service)}`
24524
24555
  );
24525
24556
  return;
24526
24557
  }
24527
24558
  if (action === "uninstall") {
24528
- const file3 = uninstall();
24559
+ const file3 = uninstall(service);
24529
24560
  console.log(file3 === null ? "The service was not installed." : `Removed ${file3}. The bridge stops at the next login, or now.`);
24530
24561
  return;
24531
24562
  }
24532
- if (action !== "install") throw new Error(`Usage: ${RUN} service [install|uninstall|status]`);
24563
+ if (action !== "install") throw new Error(`Usage: ${RUN} service [install|uninstall|status] [--service <name>] [--config <file>]`);
24533
24564
  if (import.meta.filename.endsWith(".ts")) {
24534
24565
  throw new Error("Install the published package first: npm install -g moonlait, then `moonlait service install`.");
24535
24566
  }
@@ -24537,14 +24568,23 @@ Log: ${logPath()}`
24537
24568
  throw new Error("This copy lives in npx's cache, which npm can clear.\nInstall it for good first: npm install -g moonlait, then `moonlait service install`.");
24538
24569
  }
24539
24570
  if (loadConfig(file2) === null) throw new Error(`Not paired yet. Run: ${RUN} pair <CODE> --server <url>`);
24571
+ if (service !== void 0 && option("config") === void 0) {
24572
+ throw new Error(
24573
+ `--service ${service} needs its own pairing, or it would be a second copy of the main bridge.
24574
+ ${RUN} pair <CODE> --server <url> --config ~/.moonlait/${service}.json
24575
+ ${RUN} service install --service ${service} --config ~/.moonlait/${service}.json`
24576
+ );
24577
+ }
24540
24578
  const installed2 = install({
24541
24579
  node: process.execPath,
24542
24580
  script: import.meta.filename,
24543
- ...option("config") !== void 0 ? { config: path6.resolve(option("config")) } : {}
24581
+ ...option("config") !== void 0 ? { config: path6.resolve(option("config")) } : {},
24582
+ ...service !== void 0 ? { service } : {}
24544
24583
  });
24584
+ const named = service === void 0 ? "" : ` --service ${service}`;
24545
24585
  console.log(`The bridge now runs in the background and starts at login (${installed2}).`);
24546
- console.log(`Log: ${logPath()}`);
24547
- console.log(`Stop it with: ${RUN} service uninstall`);
24586
+ console.log(`Log: ${logPath(service)}`);
24587
+ console.log(`Stop it with: ${RUN} service uninstall${named}`);
24548
24588
  return;
24549
24589
  }
24550
24590
  console.log(`Usage: ${RUN} pair <CODE> --server <url> | folders [add|remove <folder>\u2026] | start | service [install|uninstall|status] | agents`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moonlait",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Message the coding agents on your computer — Claude Code, Codex and Antigravity — from Moonlait on your phone.",
5
5
  "license": "MIT",
6
6
  "author": "Jorge Bermudez Roldan",