moonlait 0.1.0 → 0.2.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 +24 -3
  2. package/dist/cli.js +278 -66
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,11 +26,30 @@ one-time code and the command to run with it:
26
26
  npx moonlait@latest pair <CODE> --server <your Moonlait address>
27
27
  ```
28
28
 
29
- Then allow a folder and start the bridge. Your agents are online for as
30
- long as it runs:
29
+ Then allow a folder your agents may work in:
31
30
 
32
31
  ```sh
33
- npx moonlait@latest start --workspace ~/code/my-project
32
+ npx moonlait@latest folders add ~/Desktop
33
+ ```
34
+
35
+ ## Keep it running
36
+
37
+ Installed as a service, the bridge starts when you log in, restarts after
38
+ a crash, and needs no window open — launchd on macOS, systemd on Linux, a
39
+ logon task on Windows:
40
+
41
+ ```sh
42
+ npm install -g moonlait
43
+ moonlait service install
44
+ ```
45
+
46
+ `moonlait service status` says whether it is running, `moonlait service
47
+ uninstall` removes it, and its log is `~/.moonlait/bridge.log`.
48
+
49
+ To run it just for now instead, in a window you keep open:
50
+
51
+ ```sh
52
+ npx moonlait@latest start
34
53
  ```
35
54
 
36
55
  ## Commands
@@ -42,6 +61,8 @@ npx moonlait@latest start --workspace ~/code/my-project
42
61
  | `folders remove <folder>…` | Takes folders off the list |
43
62
  | `folders` | Lists the allowed folders |
44
63
  | `start [--workspace <folder>]` | Runs the bridge; `--workspace` allows a folder for this run only |
64
+ | `service install` | Runs the bridge in the background, from every login |
65
+ | `service status` / `service uninstall` | How it is doing; remove it again |
45
66
  | `agents` | Lists the agents found on this computer |
46
67
 
47
68
  The pairing is kept in `~/.moonlait/bridge.json`, readable by you only.
package/dist/cli.js CHANGED
@@ -8,9 +8,9 @@ var __export = (target, all) => {
8
8
  };
9
9
 
10
10
  // bridge/cli.ts
11
- import { existsSync as existsSync4, realpathSync as realpathSync2, statSync as statSync2, unwatchFile, watchFile } from "node:fs";
12
- import { homedir as homedir2, hostname as hostname3 } from "node:os";
13
- import path5 from "node:path";
11
+ import { existsSync as existsSync5, realpathSync as realpathSync2, statSync as statSync2, unwatchFile, watchFile } from "node:fs";
12
+ import { homedir as homedir3, hostname as hostname3 } from "node:os";
13
+ import path6 from "node:path";
14
14
 
15
15
  // bridge/agents.ts
16
16
  import { execFileSync } from "node:child_process";
@@ -1108,10 +1108,10 @@ function mergeDefs(...defs) {
1108
1108
  function cloneDef(schema) {
1109
1109
  return mergeDefs(schema._zod.def);
1110
1110
  }
1111
- function getElementAtPath(obj, path6) {
1112
- if (!path6)
1111
+ function getElementAtPath(obj, path7) {
1112
+ if (!path7)
1113
1113
  return obj;
1114
- return path6.reduce((acc, key) => acc?.[key], obj);
1114
+ return path7.reduce((acc, key) => acc?.[key], obj);
1115
1115
  }
1116
1116
  function promiseAllObject(promisesObj) {
1117
1117
  const keys = Object.keys(promisesObj);
@@ -1451,11 +1451,11 @@ function explicitlyAborted(x, startIndex = 0) {
1451
1451
  }
1452
1452
  return false;
1453
1453
  }
1454
- function prefixIssues(path6, issues) {
1454
+ function prefixIssues(path7, issues) {
1455
1455
  return issues.map((iss) => {
1456
1456
  var _a3;
1457
1457
  (_a3 = iss).path ?? (_a3.path = []);
1458
- iss.path.unshift(path6);
1458
+ iss.path.unshift(path7);
1459
1459
  return iss;
1460
1460
  });
1461
1461
  }
@@ -1905,16 +1905,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
1905
1905
  }
1906
1906
  function formatError(error62, mapper = (issue2) => issue2.message) {
1907
1907
  const fieldErrors = { _errors: [] };
1908
- const processError = (error63, path6 = []) => {
1908
+ const processError = (error63, path7 = []) => {
1909
1909
  for (const issue2 of error63.issues) {
1910
1910
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1911
- issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
1911
+ issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
1912
1912
  } else if (issue2.code === "invalid_key") {
1913
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1913
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1914
1914
  } else if (issue2.code === "invalid_element") {
1915
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1915
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1916
1916
  } else {
1917
- const fullpath = [...path6, ...issue2.path];
1917
+ const fullpath = [...path7, ...issue2.path];
1918
1918
  if (fullpath.length === 0) {
1919
1919
  fieldErrors._errors.push(mapper(issue2));
1920
1920
  } else {
@@ -1953,17 +1953,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
1953
1953
  }
1954
1954
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
1955
1955
  const result = { errors: [] };
1956
- const processError = (error63, path6 = []) => {
1956
+ const processError = (error63, path7 = []) => {
1957
1957
  var _a3;
1958
1958
  for (const issue2 of error63.issues) {
1959
1959
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1960
- issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
1960
+ issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
1961
1961
  } else if (issue2.code === "invalid_key") {
1962
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1962
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1963
1963
  } else if (issue2.code === "invalid_element") {
1964
- processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
1964
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1965
1965
  } else {
1966
- const fullpath = [...path6, ...issue2.path];
1966
+ const fullpath = [...path7, ...issue2.path];
1967
1967
  if (fullpath.length === 0) {
1968
1968
  result.errors.push(mapper(issue2));
1969
1969
  continue;
@@ -2002,8 +2002,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
2002
2002
  }
2003
2003
  function toDotPath(_path) {
2004
2004
  const segs = [];
2005
- const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2006
- for (const seg of path6) {
2005
+ const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2006
+ for (const seg of path7) {
2007
2007
  if (typeof seg === "number")
2008
2008
  segs.push(`[${seg}]`);
2009
2009
  else if (typeof seg === "symbol")
@@ -5988,8 +5988,8 @@ var error3 = () => {
5988
5988
  const sizing = getSizing(issue2.origin);
5989
5989
  if (sizing) {
5990
5990
  const maxValue = Number(issue2.maximum);
5991
- const unit = getBelarusianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
5992
- return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.maximum.toString()} ${unit}`;
5991
+ const unit2 = getBelarusianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
5992
+ return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.maximum.toString()} ${unit2}`;
5993
5993
  }
5994
5994
  return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${adj}${issue2.maximum.toString()}`;
5995
5995
  }
@@ -5998,8 +5998,8 @@ var error3 = () => {
5998
5998
  const sizing = getSizing(issue2.origin);
5999
5999
  if (sizing) {
6000
6000
  const minValue = Number(issue2.minimum);
6001
- const unit = getBelarusianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
6002
- return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.minimum.toString()} ${unit}`;
6001
+ const unit2 = getBelarusianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
6002
+ return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue2.minimum.toString()} ${unit2}`;
6003
6003
  }
6004
6004
  return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue2.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${adj}${issue2.minimum.toString()}`;
6005
6005
  }
@@ -8626,8 +8626,8 @@ var error24 = () => {
8626
8626
  const sizing = getSizing(issue2.origin);
8627
8627
  if (sizing) {
8628
8628
  const maxValue = Number(issue2.maximum);
8629
- const unit = getArmenianPlural(maxValue, sizing.unit.one, sizing.unit.many);
8630
- return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.maximum.toString()} ${unit}`;
8629
+ const unit2 = getArmenianPlural(maxValue, sizing.unit.one, sizing.unit.many);
8630
+ return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.maximum.toString()} ${unit2}`;
8631
8631
  }
8632
8632
  return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056C\u056B\u0576\u056B ${adj}${issue2.maximum.toString()}`;
8633
8633
  }
@@ -8636,8 +8636,8 @@ var error24 = () => {
8636
8636
  const sizing = getSizing(issue2.origin);
8637
8637
  if (sizing) {
8638
8638
  const minValue = Number(issue2.minimum);
8639
- const unit = getArmenianPlural(minValue, sizing.unit.one, sizing.unit.many);
8640
- return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin)} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.minimum.toString()} ${unit}`;
8639
+ const unit2 = getArmenianPlural(minValue, sizing.unit.one, sizing.unit.many);
8640
+ return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin)} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue2.minimum.toString()} ${unit2}`;
8641
8641
  }
8642
8642
  return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue2.origin)} \u056C\u056B\u0576\u056B ${adj}${issue2.minimum.toString()}`;
8643
8643
  }
@@ -9551,18 +9551,18 @@ var error32 = () => {
9551
9551
  const adj = issue2.inclusive ? "\uC774\uD558" : "\uBBF8\uB9CC";
9552
9552
  const suffix = adj === "\uBBF8\uB9CC" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
9553
9553
  const sizing = getSizing(issue2.origin);
9554
- const unit = sizing?.unit ?? "\uC694\uC18C";
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()}${unit} ${adj}${suffix}`;
9556
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()}${unit2} ${adj}${suffix}`;
9557
9557
  return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue2.maximum.toString()} ${adj}${suffix}`;
9558
9558
  }
9559
9559
  case "too_small": {
9560
9560
  const adj = issue2.inclusive ? "\uC774\uC0C1" : "\uCD08\uACFC";
9561
9561
  const suffix = adj === "\uC774\uC0C1" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
9562
9562
  const sizing = getSizing(issue2.origin);
9563
- const unit = sizing?.unit ?? "\uC694\uC18C";
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()}${unit} ${adj}${suffix}`;
9565
+ return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()}${unit2} ${adj}${suffix}`;
9566
9566
  }
9567
9567
  return `${issue2.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue2.minimum.toString()} ${adj}${suffix}`;
9568
9568
  }
@@ -11375,8 +11375,8 @@ var error46 = () => {
11375
11375
  const sizing = getSizing(issue2.origin);
11376
11376
  if (sizing) {
11377
11377
  const maxValue = Number(issue2.maximum);
11378
- const unit = getRussianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
11379
- return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.maximum.toString()} ${unit}`;
11378
+ const unit2 = getRussianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
11379
+ return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.maximum.toString()} ${unit2}`;
11380
11380
  }
11381
11381
  return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 ${adj}${issue2.maximum.toString()}`;
11382
11382
  }
@@ -11385,8 +11385,8 @@ var error46 = () => {
11385
11385
  const sizing = getSizing(issue2.origin);
11386
11386
  if (sizing) {
11387
11387
  const minValue = Number(issue2.minimum);
11388
- const unit = getRussianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
11389
- return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.minimum.toString()} ${unit}`;
11388
+ const unit2 = getRussianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
11389
+ return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue2.minimum.toString()} ${unit2}`;
11390
11390
  }
11391
11391
  return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue2.origin} \u0431\u0443\u0434\u0435\u0442 ${adj}${issue2.minimum.toString()}`;
11392
11392
  }
@@ -19105,13 +19105,13 @@ function resolveRef(ref, ctx) {
19105
19105
  if (!ref.startsWith("#")) {
19106
19106
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
19107
19107
  }
19108
- const path6 = ref.slice(1).split("/").filter(Boolean);
19109
- if (path6.length === 0) {
19108
+ const path7 = ref.slice(1).split("/").filter(Boolean);
19109
+ if (path7.length === 0) {
19110
19110
  return ctx.rootSchema;
19111
19111
  }
19112
19112
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
19113
- if (path6[0] === defsKey) {
19114
- const key = path6[1] === void 0 ? void 0 : decodeJSONPointerSegment(path6[1]);
19113
+ if (path7[0] === defsKey) {
19114
+ const key = path7[1] === void 0 ? void 0 : decodeJSONPointerSegment(path7[1]);
19115
19115
  if (!key || !ctx.defs[key]) {
19116
19116
  throw new Error(`Reference not found: ${ref}`);
19117
19117
  }
@@ -19746,7 +19746,7 @@ function visit(schema, fnOrHandlers) {
19746
19746
  return h ? h(node2, rewritten) : node2;
19747
19747
  };
19748
19748
  const cache = /* @__PURE__ */ new Map();
19749
- function run(s) {
19749
+ function run2(s) {
19750
19750
  const cached2 = cache.get(s);
19751
19751
  if (cached2 === RESOLVING) {
19752
19752
  return new $ZodLazy({
@@ -19772,21 +19772,21 @@ function visit(schema, fnOrHandlers) {
19772
19772
  let changed = false;
19773
19773
  const newShape = {};
19774
19774
  for (const k of keys) {
19775
- const mapped = run(oldShape[k]);
19775
+ const mapped = run2(oldShape[k]);
19776
19776
  if (mapped !== oldShape[k])
19777
19777
  changed = true;
19778
19778
  newShape[k] = mapped;
19779
19779
  }
19780
19780
  let newCatchall = def.catchall;
19781
19781
  if (def.catchall) {
19782
- newCatchall = run(def.catchall);
19782
+ newCatchall = run2(def.catchall);
19783
19783
  if (newCatchall !== def.catchall)
19784
19784
  changed = true;
19785
19785
  }
19786
19786
  return changed ? clone(s, { ...def, shape: newShape, catchall: newCatchall }) : s;
19787
19787
  }
19788
19788
  case "array": {
19789
- const mapped = run(def.element);
19789
+ const mapped = run2(def.element);
19790
19790
  return mapped === def.element ? s : clone(s, { ...def, element: mapped });
19791
19791
  }
19792
19792
  case "tuple": {
@@ -19794,14 +19794,14 @@ function visit(schema, fnOrHandlers) {
19794
19794
  let changed = false;
19795
19795
  const newItems = [];
19796
19796
  for (const item of oldItems) {
19797
- const mapped = run(item);
19797
+ const mapped = run2(item);
19798
19798
  if (mapped !== item)
19799
19799
  changed = true;
19800
19800
  newItems.push(mapped);
19801
19801
  }
19802
19802
  let newRest = def.rest;
19803
19803
  if (def.rest) {
19804
- newRest = run(def.rest);
19804
+ newRest = run2(def.rest);
19805
19805
  if (newRest !== def.rest)
19806
19806
  changed = true;
19807
19807
  }
@@ -19809,12 +19809,12 @@ function visit(schema, fnOrHandlers) {
19809
19809
  }
19810
19810
  case "record":
19811
19811
  case "map": {
19812
- const newKey = run(def.keyType);
19813
- const newVal = run(def.valueType);
19812
+ const newKey = run2(def.keyType);
19813
+ const newVal = run2(def.valueType);
19814
19814
  return newKey === def.keyType && newVal === def.valueType ? s : clone(s, { ...def, keyType: newKey, valueType: newVal });
19815
19815
  }
19816
19816
  case "set": {
19817
- const newVal = run(def.valueType);
19817
+ const newVal = run2(def.valueType);
19818
19818
  return newVal === def.valueType ? s : clone(s, { ...def, valueType: newVal });
19819
19819
  }
19820
19820
  case "union": {
@@ -19822,7 +19822,7 @@ function visit(schema, fnOrHandlers) {
19822
19822
  let changed = false;
19823
19823
  const newOptions = [];
19824
19824
  for (const opt of oldOptions) {
19825
- const mapped = run(opt);
19825
+ const mapped = run2(opt);
19826
19826
  if (mapped !== opt)
19827
19827
  changed = true;
19828
19828
  newOptions.push(mapped);
@@ -19830,8 +19830,8 @@ function visit(schema, fnOrHandlers) {
19830
19830
  return changed ? clone(s, { ...def, options: newOptions }) : s;
19831
19831
  }
19832
19832
  case "intersection": {
19833
- const newLeft = run(def.left);
19834
- const newRight = run(def.right);
19833
+ const newLeft = run2(def.left);
19834
+ const newRight = run2(def.right);
19835
19835
  return newLeft === def.left && newRight === def.right ? s : clone(s, { ...def, left: newLeft, right: newRight });
19836
19836
  }
19837
19837
  case "optional":
@@ -19843,23 +19843,23 @@ function visit(schema, fnOrHandlers) {
19843
19843
  case "nonoptional":
19844
19844
  case "promise":
19845
19845
  case "success": {
19846
- const newInner = run(def.innerType);
19846
+ const newInner = run2(def.innerType);
19847
19847
  return newInner === def.innerType ? s : clone(s, { ...def, innerType: newInner });
19848
19848
  }
19849
19849
  case "pipe": {
19850
- const newIn = run(def.in);
19851
- const newOut = run(def.out);
19850
+ const newIn = run2(def.in);
19851
+ const newOut = run2(def.out);
19852
19852
  return newIn === def.in && newOut === def.out ? s : clone(s, { ...def, in: newIn, out: newOut });
19853
19853
  }
19854
19854
  case "function": {
19855
- const newInput = run(def.input);
19856
- const newOutput = run(def.output);
19855
+ const newInput = run2(def.input);
19856
+ const newOutput = run2(def.output);
19857
19857
  return newInput === def.input && newOutput === def.output ? s : clone(s, { ...def, input: newInput, output: newOutput });
19858
19858
  }
19859
19859
  case "lazy": {
19860
19860
  const original = def.getter;
19861
19861
  const { _cachedInner, ...rest } = def;
19862
- return clone(s, { ...rest, getter: () => run(original()) });
19862
+ return clone(s, { ...rest, getter: () => run2(original()) });
19863
19863
  }
19864
19864
  // A leaf by choice: `parts` are regex fragments, not data positions.
19865
19865
  case "template_literal":
@@ -19890,7 +19890,7 @@ function visit(schema, fnOrHandlers) {
19890
19890
  }
19891
19891
  }
19892
19892
  }
19893
- return run(schema);
19893
+ return run2(schema);
19894
19894
  }
19895
19895
 
19896
19896
  // node_modules/zod/v4/classic/deep-partial.js
@@ -24235,6 +24235,184 @@ ${name} said: ${reason}`), false);
24235
24235
  }
24236
24236
  };
24237
24237
 
24238
+ // bridge/service.ts
24239
+ import { execFileSync as execFileSync2 } from "node:child_process";
24240
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
24241
+ import { homedir as homedir2 } from "node:os";
24242
+ import path5 from "node:path";
24243
+ var LABEL = "com.moonlait.bridge";
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");
24254
+ function runnerScript(paths) {
24255
+ const config2 = paths.config === void 0 ? "" : ` --config "${paths.config}"`;
24256
+ return [
24257
+ "@echo off",
24258
+ ":loop",
24259
+ `"${paths.node}" "${paths.script}" start${config2} >> "${logPath()}" 2>&1`,
24260
+ "timeout /t 5 /nobreak > nul",
24261
+ "goto loop",
24262
+ ""
24263
+ ].join("\r\n");
24264
+ }
24265
+ function launcherScript() {
24266
+ return ['Set shell = CreateObject("WScript.Shell")', `shell.Run """${runnerPath()}""", 0, False`, ""].join("\r\n");
24267
+ }
24268
+ var escape = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
24269
+ function plist(paths) {
24270
+ const args = [paths.node, paths.script, "start", ...paths.config === void 0 ? [] : ["--config", paths.config]];
24271
+ return `<?xml version="1.0" encoding="UTF-8"?>
24272
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
24273
+ <plist version="1.0">
24274
+ <dict>
24275
+ <key>Label</key><string>${LABEL}</string>
24276
+ <key>ProgramArguments</key>
24277
+ <array>${args.map((arg) => `
24278
+ <string>${escape(arg)}</string>`).join("")}
24279
+ </array>
24280
+ <key>RunAtLoad</key><true/>
24281
+ <key>KeepAlive</key><true/>
24282
+ <key>WorkingDirectory</key><string>${escape(homedir2())}</string>
24283
+ <key>EnvironmentVariables</key>
24284
+ <dict>
24285
+ <key>PATH</key><string>${escape(process.env.PATH ?? "/usr/bin:/bin")}</string>
24286
+ <key>HOME</key><string>${escape(homedir2())}</string>
24287
+ </dict>
24288
+ <key>StandardOutPath</key><string>${escape(logPath())}</string>
24289
+ <key>StandardErrorPath</key><string>${escape(logPath())}</string>
24290
+ </dict>
24291
+ </plist>
24292
+ `;
24293
+ }
24294
+ function unit(paths) {
24295
+ const args = [paths.node, paths.script, "start", ...paths.config === void 0 ? [] : ["--config", paths.config]];
24296
+ return `[Unit]
24297
+ Description=Moonlait bridge
24298
+ After=network-online.target
24299
+
24300
+ [Service]
24301
+ ExecStart=${args.map((arg) => JSON.stringify(arg)).join(" ")}
24302
+ Environment=PATH=${process.env.PATH ?? "/usr/bin:/bin"}
24303
+ Restart=always
24304
+ RestartSec=5
24305
+
24306
+ [Install]
24307
+ WantedBy=default.target
24308
+ `;
24309
+ }
24310
+ function run(command, args) {
24311
+ execFileSync2(command, [...args], { stdio: "ignore" });
24312
+ }
24313
+ function install(paths) {
24314
+ if (process.platform === "darwin") {
24315
+ const file2 = plistPath();
24316
+ mkdirSync2(path5.dirname(file2), { recursive: true });
24317
+ mkdirSync2(bridgeHome(), { recursive: true });
24318
+ writeFileSync2(file2, plist(paths));
24319
+ try {
24320
+ run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${LABEL}`]);
24321
+ } catch {
24322
+ }
24323
+ run("launchctl", ["bootstrap", `gui/${process.getuid?.() ?? 501}`, file2]);
24324
+ return file2;
24325
+ }
24326
+ if (process.platform === "linux") {
24327
+ const file2 = unitPath();
24328
+ mkdirSync2(path5.dirname(file2), { recursive: true });
24329
+ writeFileSync2(file2, unit(paths));
24330
+ run("systemctl", ["--user", "daemon-reload"]);
24331
+ run("systemctl", ["--user", "enable", "--now", "moonlait.service"]);
24332
+ return file2;
24333
+ }
24334
+ if (process.platform === "win32") {
24335
+ 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();
24341
+ }
24342
+ throw new Error(`Installing as a service is not supported on ${process.platform}. Run \`moonlait start\` in a terminal instead.`);
24343
+ }
24344
+ function uninstall() {
24345
+ if (process.platform === "darwin") {
24346
+ const file2 = plistPath();
24347
+ if (!existsSync4(file2)) return null;
24348
+ try {
24349
+ run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 501}/${LABEL}`]);
24350
+ } catch {
24351
+ }
24352
+ rmSync(file2);
24353
+ return file2;
24354
+ }
24355
+ if (process.platform === "linux") {
24356
+ const file2 = unitPath();
24357
+ if (!existsSync4(file2)) return null;
24358
+ try {
24359
+ run("systemctl", ["--user", "disable", "--now", "moonlait.service"]);
24360
+ } catch {
24361
+ }
24362
+ rmSync(file2);
24363
+ run("systemctl", ["--user", "daemon-reload"]);
24364
+ return file2;
24365
+ }
24366
+ if (process.platform === "win32") {
24367
+ if (!existsSync4(launcherPath())) return null;
24368
+ try {
24369
+ run("schtasks", ["/End", "/TN", TASK]);
24370
+ run("schtasks", ["/Delete", "/TN", TASK, "/F"]);
24371
+ } catch {
24372
+ }
24373
+ rmSync(launcherPath());
24374
+ rmSync(runnerPath(), { force: true });
24375
+ return launcherPath();
24376
+ }
24377
+ throw new Error(`Nothing to remove: the service is not installed on ${process.platform}.`);
24378
+ }
24379
+ function status() {
24380
+ if (process.platform === "darwin") {
24381
+ const file2 = plistPath();
24382
+ let running = false;
24383
+ try {
24384
+ execFileSync2("launchctl", ["print", `gui/${process.getuid?.() ?? 501}/${LABEL}`], { stdio: "ignore" });
24385
+ running = true;
24386
+ } catch {
24387
+ running = false;
24388
+ }
24389
+ return { installed: existsSync4(file2), running, file: file2 };
24390
+ }
24391
+ if (process.platform === "linux") {
24392
+ const file2 = unitPath();
24393
+ let running = false;
24394
+ try {
24395
+ execFileSync2("systemctl", ["--user", "is-active", "--quiet", "moonlait.service"], { stdio: "ignore" });
24396
+ running = true;
24397
+ } catch {
24398
+ running = false;
24399
+ }
24400
+ return { installed: existsSync4(file2), running, file: file2 };
24401
+ }
24402
+ if (process.platform === "win32") {
24403
+ const file2 = launcherPath();
24404
+ let running = false;
24405
+ try {
24406
+ const output2 = execFileSync2("schtasks", ["/Query", "/TN", TASK, "/FO", "LIST"], { encoding: "utf8" });
24407
+ running = /\bRunning\b/i.test(output2);
24408
+ } catch {
24409
+ return { installed: false, running: false, file: file2 };
24410
+ }
24411
+ return { installed: true, running, file: file2 };
24412
+ }
24413
+ return { installed: false, running: false, file: "" };
24414
+ }
24415
+
24238
24416
  // bridge/cli.ts
24239
24417
  function option(name) {
24240
24418
  const index = process.argv.indexOf(`--${name}`);
@@ -24286,7 +24464,7 @@ async function main() {
24286
24464
  const config2 = loadConfig(file2);
24287
24465
  if (config2 === null) throw new Error(`Not paired yet. Run: ${RUN} pair <CODE> --server <url>`);
24288
24466
  const given = options("workspace");
24289
- const allowed = () => discoverWorkspaces((given.length > 0 ? given : loadConfig(file2)?.folders ?? []).filter((folder) => existsSync4(folder)));
24467
+ const allowed = () => discoverWorkspaces((given.length > 0 ? given : loadConfig(file2)?.folders ?? []).filter((folder) => existsSync5(folder)));
24290
24468
  let workspaces = allowed();
24291
24469
  if (workspaces.length === 0) {
24292
24470
  throw new Error(`No folders allowed yet. Choose where agents may work, e.g.:
@@ -24335,7 +24513,41 @@ async function main() {
24335
24513
  );
24336
24514
  return;
24337
24515
  }
24338
- console.log(`Usage: ${RUN} pair <CODE> --server <url> | folders [add|remove <folder>\u2026] | start | agents`);
24516
+ if (command === "service") {
24517
+ const action = argument ?? "status";
24518
+ if (action === "status") {
24519
+ const state = status();
24520
+ console.log(
24521
+ !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()}`
24524
+ );
24525
+ return;
24526
+ }
24527
+ if (action === "uninstall") {
24528
+ const file3 = uninstall();
24529
+ console.log(file3 === null ? "The service was not installed." : `Removed ${file3}. The bridge stops at the next login, or now.`);
24530
+ return;
24531
+ }
24532
+ if (action !== "install") throw new Error(`Usage: ${RUN} service [install|uninstall|status]`);
24533
+ if (import.meta.filename.endsWith(".ts")) {
24534
+ throw new Error("Install the published package first: npm install -g moonlait, then `moonlait service install`.");
24535
+ }
24536
+ if (/[\\/]_npx[\\/]/.test(import.meta.filename)) {
24537
+ 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
+ }
24539
+ if (loadConfig(file2) === null) throw new Error(`Not paired yet. Run: ${RUN} pair <CODE> --server <url>`);
24540
+ const installed2 = install({
24541
+ node: process.execPath,
24542
+ script: import.meta.filename,
24543
+ ...option("config") !== void 0 ? { config: path6.resolve(option("config")) } : {}
24544
+ });
24545
+ 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`);
24548
+ return;
24549
+ }
24550
+ console.log(`Usage: ${RUN} pair <CODE> --server <url> | folders [add|remove <folder>\u2026] | start | service [install|uninstall|status] | agents`);
24339
24551
  }
24340
24552
  function sameFolders(a, b) {
24341
24553
  return a.map((folder) => folder.path).join("\n") === b.map((folder) => folder.path).join("\n");
@@ -24347,16 +24559,16 @@ function changeFolders(current, action, given) {
24347
24559
  }
24348
24560
  let next = [...current];
24349
24561
  for (const folder of given) {
24350
- const absolute = path5.resolve(folder);
24562
+ const absolute = path6.resolve(folder);
24351
24563
  if (action === "remove") {
24352
- const real2 = existsSync4(absolute) ? realpathSync2(absolute) : absolute;
24564
+ const real2 = existsSync5(absolute) ? realpathSync2(absolute) : absolute;
24353
24565
  next = next.filter((candidate) => candidate !== real2 && candidate !== absolute);
24354
24566
  continue;
24355
24567
  }
24356
- if (!existsSync4(absolute) || !statSync2(absolute).isDirectory()) throw new Error(`Not a folder: ${folder}`);
24568
+ if (!existsSync5(absolute) || !statSync2(absolute).isDirectory()) throw new Error(`Not a folder: ${folder}`);
24357
24569
  const real = realpathSync2(absolute);
24358
- if (real === path5.parse(real).root) throw new Error("Pick a folder, not the whole disk.");
24359
- if (real === homedir2()) console.log("Note: agents can now work anywhere in your home folder (they still ask before risky steps).");
24570
+ if (real === path6.parse(real).root) throw new Error("Pick a folder, not the whole disk.");
24571
+ if (real === homedir3()) console.log("Note: agents can now work anywhere in your home folder (they still ask before risky steps).");
24360
24572
  if (!next.includes(real)) next.push(real);
24361
24573
  }
24362
24574
  return next.join("\n") === current.join("\n") ? current : next;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moonlait",
3
- "version": "0.1.0",
3
+ "version": "0.2.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",