@vinihcrosa/lumem-os 0.5.0 → 0.7.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.
package/bin/lumem.mjs CHANGED
@@ -539,18 +539,18 @@ var ParseStatus = class _ParseStatus {
539
539
  if (this.value !== "aborted")
540
540
  this.value = "aborted";
541
541
  }
542
- static mergeArray(status, results) {
542
+ static mergeArray(status2, results) {
543
543
  const arrayValue = [];
544
544
  for (const s of results) {
545
545
  if (s.status === "aborted")
546
546
  return INVALID;
547
547
  if (s.status === "dirty")
548
- status.dirty();
548
+ status2.dirty();
549
549
  arrayValue.push(s.value);
550
550
  }
551
- return { status: status.value, value: arrayValue };
551
+ return { status: status2.value, value: arrayValue };
552
552
  }
553
- static async mergeObjectAsync(status, pairs) {
553
+ static async mergeObjectAsync(status2, pairs) {
554
554
  const syncPairs = [];
555
555
  for (const pair of pairs) {
556
556
  const key = await pair.key;
@@ -560,9 +560,9 @@ var ParseStatus = class _ParseStatus {
560
560
  value
561
561
  });
562
562
  }
563
- return _ParseStatus.mergeObjectSync(status, syncPairs);
563
+ return _ParseStatus.mergeObjectSync(status2, syncPairs);
564
564
  }
565
- static mergeObjectSync(status, pairs) {
565
+ static mergeObjectSync(status2, pairs) {
566
566
  const finalObject = {};
567
567
  for (const pair of pairs) {
568
568
  const { key, value } = pair;
@@ -571,14 +571,14 @@ var ParseStatus = class _ParseStatus {
571
571
  if (value.status === "aborted")
572
572
  return INVALID;
573
573
  if (key.status === "dirty")
574
- status.dirty();
574
+ status2.dirty();
575
575
  if (value.status === "dirty")
576
- status.dirty();
576
+ status2.dirty();
577
577
  if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
578
578
  finalObject[key.value] = value.value;
579
579
  }
580
580
  }
581
- return { status: status.value, value: finalObject };
581
+ return { status: status2.value, value: finalObject };
582
582
  }
583
583
  };
584
584
  var INVALID = Object.freeze({
@@ -1038,7 +1038,7 @@ var ZodString = class _ZodString extends ZodType {
1038
1038
  });
1039
1039
  return INVALID;
1040
1040
  }
1041
- const status = new ParseStatus();
1041
+ const status2 = new ParseStatus();
1042
1042
  let ctx = void 0;
1043
1043
  for (const check of this._def.checks) {
1044
1044
  if (check.kind === "min") {
@@ -1052,7 +1052,7 @@ var ZodString = class _ZodString extends ZodType {
1052
1052
  exact: false,
1053
1053
  message: check.message
1054
1054
  });
1055
- status.dirty();
1055
+ status2.dirty();
1056
1056
  }
1057
1057
  } else if (check.kind === "max") {
1058
1058
  if (input.data.length > check.value) {
@@ -1065,7 +1065,7 @@ var ZodString = class _ZodString extends ZodType {
1065
1065
  exact: false,
1066
1066
  message: check.message
1067
1067
  });
1068
- status.dirty();
1068
+ status2.dirty();
1069
1069
  }
1070
1070
  } else if (check.kind === "length") {
1071
1071
  const tooBig = input.data.length > check.value;
@@ -1091,7 +1091,7 @@ var ZodString = class _ZodString extends ZodType {
1091
1091
  message: check.message
1092
1092
  });
1093
1093
  }
1094
- status.dirty();
1094
+ status2.dirty();
1095
1095
  }
1096
1096
  } else if (check.kind === "email") {
1097
1097
  if (!emailRegex.test(input.data)) {
@@ -1101,7 +1101,7 @@ var ZodString = class _ZodString extends ZodType {
1101
1101
  code: ZodIssueCode.invalid_string,
1102
1102
  message: check.message
1103
1103
  });
1104
- status.dirty();
1104
+ status2.dirty();
1105
1105
  }
1106
1106
  } else if (check.kind === "emoji") {
1107
1107
  if (!emojiRegex) {
@@ -1114,7 +1114,7 @@ var ZodString = class _ZodString extends ZodType {
1114
1114
  code: ZodIssueCode.invalid_string,
1115
1115
  message: check.message
1116
1116
  });
1117
- status.dirty();
1117
+ status2.dirty();
1118
1118
  }
1119
1119
  } else if (check.kind === "uuid") {
1120
1120
  if (!uuidRegex.test(input.data)) {
@@ -1124,7 +1124,7 @@ var ZodString = class _ZodString extends ZodType {
1124
1124
  code: ZodIssueCode.invalid_string,
1125
1125
  message: check.message
1126
1126
  });
1127
- status.dirty();
1127
+ status2.dirty();
1128
1128
  }
1129
1129
  } else if (check.kind === "nanoid") {
1130
1130
  if (!nanoidRegex.test(input.data)) {
@@ -1134,7 +1134,7 @@ var ZodString = class _ZodString extends ZodType {
1134
1134
  code: ZodIssueCode.invalid_string,
1135
1135
  message: check.message
1136
1136
  });
1137
- status.dirty();
1137
+ status2.dirty();
1138
1138
  }
1139
1139
  } else if (check.kind === "cuid") {
1140
1140
  if (!cuidRegex.test(input.data)) {
@@ -1144,7 +1144,7 @@ var ZodString = class _ZodString extends ZodType {
1144
1144
  code: ZodIssueCode.invalid_string,
1145
1145
  message: check.message
1146
1146
  });
1147
- status.dirty();
1147
+ status2.dirty();
1148
1148
  }
1149
1149
  } else if (check.kind === "cuid2") {
1150
1150
  if (!cuid2Regex.test(input.data)) {
@@ -1154,7 +1154,7 @@ var ZodString = class _ZodString extends ZodType {
1154
1154
  code: ZodIssueCode.invalid_string,
1155
1155
  message: check.message
1156
1156
  });
1157
- status.dirty();
1157
+ status2.dirty();
1158
1158
  }
1159
1159
  } else if (check.kind === "ulid") {
1160
1160
  if (!ulidRegex.test(input.data)) {
@@ -1164,7 +1164,7 @@ var ZodString = class _ZodString extends ZodType {
1164
1164
  code: ZodIssueCode.invalid_string,
1165
1165
  message: check.message
1166
1166
  });
1167
- status.dirty();
1167
+ status2.dirty();
1168
1168
  }
1169
1169
  } else if (check.kind === "url") {
1170
1170
  try {
@@ -1176,7 +1176,7 @@ var ZodString = class _ZodString extends ZodType {
1176
1176
  code: ZodIssueCode.invalid_string,
1177
1177
  message: check.message
1178
1178
  });
1179
- status.dirty();
1179
+ status2.dirty();
1180
1180
  }
1181
1181
  } else if (check.kind === "regex") {
1182
1182
  check.regex.lastIndex = 0;
@@ -1188,7 +1188,7 @@ var ZodString = class _ZodString extends ZodType {
1188
1188
  code: ZodIssueCode.invalid_string,
1189
1189
  message: check.message
1190
1190
  });
1191
- status.dirty();
1191
+ status2.dirty();
1192
1192
  }
1193
1193
  } else if (check.kind === "trim") {
1194
1194
  input.data = input.data.trim();
@@ -1200,7 +1200,7 @@ var ZodString = class _ZodString extends ZodType {
1200
1200
  validation: { includes: check.value, position: check.position },
1201
1201
  message: check.message
1202
1202
  });
1203
- status.dirty();
1203
+ status2.dirty();
1204
1204
  }
1205
1205
  } else if (check.kind === "toLowerCase") {
1206
1206
  input.data = input.data.toLowerCase();
@@ -1214,7 +1214,7 @@ var ZodString = class _ZodString extends ZodType {
1214
1214
  validation: { startsWith: check.value },
1215
1215
  message: check.message
1216
1216
  });
1217
- status.dirty();
1217
+ status2.dirty();
1218
1218
  }
1219
1219
  } else if (check.kind === "endsWith") {
1220
1220
  if (!input.data.endsWith(check.value)) {
@@ -1224,7 +1224,7 @@ var ZodString = class _ZodString extends ZodType {
1224
1224
  validation: { endsWith: check.value },
1225
1225
  message: check.message
1226
1226
  });
1227
- status.dirty();
1227
+ status2.dirty();
1228
1228
  }
1229
1229
  } else if (check.kind === "datetime") {
1230
1230
  const regex = datetimeRegex(check);
@@ -1235,7 +1235,7 @@ var ZodString = class _ZodString extends ZodType {
1235
1235
  validation: "datetime",
1236
1236
  message: check.message
1237
1237
  });
1238
- status.dirty();
1238
+ status2.dirty();
1239
1239
  }
1240
1240
  } else if (check.kind === "date") {
1241
1241
  const regex = dateRegex;
@@ -1246,7 +1246,7 @@ var ZodString = class _ZodString extends ZodType {
1246
1246
  validation: "date",
1247
1247
  message: check.message
1248
1248
  });
1249
- status.dirty();
1249
+ status2.dirty();
1250
1250
  }
1251
1251
  } else if (check.kind === "time") {
1252
1252
  const regex = timeRegex(check);
@@ -1257,7 +1257,7 @@ var ZodString = class _ZodString extends ZodType {
1257
1257
  validation: "time",
1258
1258
  message: check.message
1259
1259
  });
1260
- status.dirty();
1260
+ status2.dirty();
1261
1261
  }
1262
1262
  } else if (check.kind === "duration") {
1263
1263
  if (!durationRegex.test(input.data)) {
@@ -1267,7 +1267,7 @@ var ZodString = class _ZodString extends ZodType {
1267
1267
  code: ZodIssueCode.invalid_string,
1268
1268
  message: check.message
1269
1269
  });
1270
- status.dirty();
1270
+ status2.dirty();
1271
1271
  }
1272
1272
  } else if (check.kind === "ip") {
1273
1273
  if (!isValidIP(input.data, check.version)) {
@@ -1277,7 +1277,7 @@ var ZodString = class _ZodString extends ZodType {
1277
1277
  code: ZodIssueCode.invalid_string,
1278
1278
  message: check.message
1279
1279
  });
1280
- status.dirty();
1280
+ status2.dirty();
1281
1281
  }
1282
1282
  } else if (check.kind === "jwt") {
1283
1283
  if (!isValidJWT(input.data, check.alg)) {
@@ -1287,7 +1287,7 @@ var ZodString = class _ZodString extends ZodType {
1287
1287
  code: ZodIssueCode.invalid_string,
1288
1288
  message: check.message
1289
1289
  });
1290
- status.dirty();
1290
+ status2.dirty();
1291
1291
  }
1292
1292
  } else if (check.kind === "cidr") {
1293
1293
  if (!isValidCidr(input.data, check.version)) {
@@ -1297,7 +1297,7 @@ var ZodString = class _ZodString extends ZodType {
1297
1297
  code: ZodIssueCode.invalid_string,
1298
1298
  message: check.message
1299
1299
  });
1300
- status.dirty();
1300
+ status2.dirty();
1301
1301
  }
1302
1302
  } else if (check.kind === "base64") {
1303
1303
  if (!base64Regex.test(input.data)) {
@@ -1307,7 +1307,7 @@ var ZodString = class _ZodString extends ZodType {
1307
1307
  code: ZodIssueCode.invalid_string,
1308
1308
  message: check.message
1309
1309
  });
1310
- status.dirty();
1310
+ status2.dirty();
1311
1311
  }
1312
1312
  } else if (check.kind === "base64url") {
1313
1313
  if (!base64urlRegex.test(input.data)) {
@@ -1317,13 +1317,13 @@ var ZodString = class _ZodString extends ZodType {
1317
1317
  code: ZodIssueCode.invalid_string,
1318
1318
  message: check.message
1319
1319
  });
1320
- status.dirty();
1320
+ status2.dirty();
1321
1321
  }
1322
1322
  } else {
1323
1323
  util.assertNever(check);
1324
1324
  }
1325
1325
  }
1326
- return { status: status.value, value: input.data };
1326
+ return { status: status2.value, value: input.data };
1327
1327
  }
1328
1328
  _regex(regex, validation, message2) {
1329
1329
  return this.refinement((data) => regex.test(data), {
@@ -1599,7 +1599,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1599
1599
  return INVALID;
1600
1600
  }
1601
1601
  let ctx = void 0;
1602
- const status = new ParseStatus();
1602
+ const status2 = new ParseStatus();
1603
1603
  for (const check of this._def.checks) {
1604
1604
  if (check.kind === "int") {
1605
1605
  if (!util.isInteger(input.data)) {
@@ -1610,7 +1610,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1610
1610
  received: "float",
1611
1611
  message: check.message
1612
1612
  });
1613
- status.dirty();
1613
+ status2.dirty();
1614
1614
  }
1615
1615
  } else if (check.kind === "min") {
1616
1616
  const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value;
@@ -1624,7 +1624,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1624
1624
  exact: false,
1625
1625
  message: check.message
1626
1626
  });
1627
- status.dirty();
1627
+ status2.dirty();
1628
1628
  }
1629
1629
  } else if (check.kind === "max") {
1630
1630
  const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value;
@@ -1638,7 +1638,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1638
1638
  exact: false,
1639
1639
  message: check.message
1640
1640
  });
1641
- status.dirty();
1641
+ status2.dirty();
1642
1642
  }
1643
1643
  } else if (check.kind === "multipleOf") {
1644
1644
  if (floatSafeRemainder(input.data, check.value) !== 0) {
@@ -1648,7 +1648,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
1648
1648
  multipleOf: check.value,
1649
1649
  message: check.message
1650
1650
  });
1651
- status.dirty();
1651
+ status2.dirty();
1652
1652
  }
1653
1653
  } else if (check.kind === "finite") {
1654
1654
  if (!Number.isFinite(input.data)) {
@@ -1657,13 +1657,13 @@ var ZodNumber = class _ZodNumber extends ZodType {
1657
1657
  code: ZodIssueCode.not_finite,
1658
1658
  message: check.message
1659
1659
  });
1660
- status.dirty();
1660
+ status2.dirty();
1661
1661
  }
1662
1662
  } else {
1663
1663
  util.assertNever(check);
1664
1664
  }
1665
1665
  }
1666
- return { status: status.value, value: input.data };
1666
+ return { status: status2.value, value: input.data };
1667
1667
  }
1668
1668
  gte(value, message2) {
1669
1669
  return this.setLimit("min", value, true, errorUtil.toString(message2));
@@ -1828,7 +1828,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1828
1828
  return this._getInvalidInput(input);
1829
1829
  }
1830
1830
  let ctx = void 0;
1831
- const status = new ParseStatus();
1831
+ const status2 = new ParseStatus();
1832
1832
  for (const check of this._def.checks) {
1833
1833
  if (check.kind === "min") {
1834
1834
  const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value;
@@ -1841,7 +1841,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1841
1841
  inclusive: check.inclusive,
1842
1842
  message: check.message
1843
1843
  });
1844
- status.dirty();
1844
+ status2.dirty();
1845
1845
  }
1846
1846
  } else if (check.kind === "max") {
1847
1847
  const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value;
@@ -1854,7 +1854,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1854
1854
  inclusive: check.inclusive,
1855
1855
  message: check.message
1856
1856
  });
1857
- status.dirty();
1857
+ status2.dirty();
1858
1858
  }
1859
1859
  } else if (check.kind === "multipleOf") {
1860
1860
  if (input.data % check.value !== BigInt(0)) {
@@ -1864,13 +1864,13 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
1864
1864
  multipleOf: check.value,
1865
1865
  message: check.message
1866
1866
  });
1867
- status.dirty();
1867
+ status2.dirty();
1868
1868
  }
1869
1869
  } else {
1870
1870
  util.assertNever(check);
1871
1871
  }
1872
1872
  }
1873
- return { status: status.value, value: input.data };
1873
+ return { status: status2.value, value: input.data };
1874
1874
  }
1875
1875
  _getInvalidInput(input) {
1876
1876
  const ctx = this._getOrReturnCtx(input);
@@ -2028,7 +2028,7 @@ var ZodDate = class _ZodDate extends ZodType {
2028
2028
  });
2029
2029
  return INVALID;
2030
2030
  }
2031
- const status = new ParseStatus();
2031
+ const status2 = new ParseStatus();
2032
2032
  let ctx = void 0;
2033
2033
  for (const check of this._def.checks) {
2034
2034
  if (check.kind === "min") {
@@ -2042,7 +2042,7 @@ var ZodDate = class _ZodDate extends ZodType {
2042
2042
  minimum: check.value,
2043
2043
  type: "date"
2044
2044
  });
2045
- status.dirty();
2045
+ status2.dirty();
2046
2046
  }
2047
2047
  } else if (check.kind === "max") {
2048
2048
  if (input.data.getTime() > check.value) {
@@ -2055,14 +2055,14 @@ var ZodDate = class _ZodDate extends ZodType {
2055
2055
  maximum: check.value,
2056
2056
  type: "date"
2057
2057
  });
2058
- status.dirty();
2058
+ status2.dirty();
2059
2059
  }
2060
2060
  } else {
2061
2061
  util.assertNever(check);
2062
2062
  }
2063
2063
  }
2064
2064
  return {
2065
- status: status.value,
2065
+ status: status2.value,
2066
2066
  value: new Date(input.data.getTime())
2067
2067
  };
2068
2068
  }
@@ -2248,7 +2248,7 @@ ZodVoid.create = (params) => {
2248
2248
  };
2249
2249
  var ZodArray = class _ZodArray extends ZodType {
2250
2250
  _parse(input) {
2251
- const { ctx, status } = this._processInputParams(input);
2251
+ const { ctx, status: status2 } = this._processInputParams(input);
2252
2252
  const def = this._def;
2253
2253
  if (ctx.parsedType !== ZodParsedType.array) {
2254
2254
  addIssueToContext(ctx, {
@@ -2271,7 +2271,7 @@ var ZodArray = class _ZodArray extends ZodType {
2271
2271
  exact: true,
2272
2272
  message: def.exactLength.message
2273
2273
  });
2274
- status.dirty();
2274
+ status2.dirty();
2275
2275
  }
2276
2276
  }
2277
2277
  if (def.minLength !== null) {
@@ -2284,7 +2284,7 @@ var ZodArray = class _ZodArray extends ZodType {
2284
2284
  exact: false,
2285
2285
  message: def.minLength.message
2286
2286
  });
2287
- status.dirty();
2287
+ status2.dirty();
2288
2288
  }
2289
2289
  }
2290
2290
  if (def.maxLength !== null) {
@@ -2297,20 +2297,20 @@ var ZodArray = class _ZodArray extends ZodType {
2297
2297
  exact: false,
2298
2298
  message: def.maxLength.message
2299
2299
  });
2300
- status.dirty();
2300
+ status2.dirty();
2301
2301
  }
2302
2302
  }
2303
2303
  if (ctx.common.async) {
2304
2304
  return Promise.all([...ctx.data].map((item, i) => {
2305
2305
  return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));
2306
2306
  })).then((result2) => {
2307
- return ParseStatus.mergeArray(status, result2);
2307
+ return ParseStatus.mergeArray(status2, result2);
2308
2308
  });
2309
2309
  }
2310
2310
  const result = [...ctx.data].map((item, i) => {
2311
2311
  return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));
2312
2312
  });
2313
- return ParseStatus.mergeArray(status, result);
2313
+ return ParseStatus.mergeArray(status2, result);
2314
2314
  }
2315
2315
  get element() {
2316
2316
  return this._def.type;
@@ -2399,7 +2399,7 @@ var ZodObject = class _ZodObject extends ZodType {
2399
2399
  });
2400
2400
  return INVALID;
2401
2401
  }
2402
- const { status, ctx } = this._processInputParams(input);
2402
+ const { status: status2, ctx } = this._processInputParams(input);
2403
2403
  const { shape, keys: shapeKeys } = this._getCached();
2404
2404
  const extraKeys = [];
2405
2405
  if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
@@ -2434,7 +2434,7 @@ var ZodObject = class _ZodObject extends ZodType {
2434
2434
  code: ZodIssueCode.unrecognized_keys,
2435
2435
  keys: extraKeys
2436
2436
  });
2437
- status.dirty();
2437
+ status2.dirty();
2438
2438
  }
2439
2439
  } else if (unknownKeys === "strip") {
2440
2440
  } else {
@@ -2468,10 +2468,10 @@ var ZodObject = class _ZodObject extends ZodType {
2468
2468
  }
2469
2469
  return syncPairs;
2470
2470
  }).then((syncPairs) => {
2471
- return ParseStatus.mergeObjectSync(status, syncPairs);
2471
+ return ParseStatus.mergeObjectSync(status2, syncPairs);
2472
2472
  });
2473
2473
  } else {
2474
- return ParseStatus.mergeObjectSync(status, pairs);
2474
+ return ParseStatus.mergeObjectSync(status2, pairs);
2475
2475
  }
2476
2476
  }
2477
2477
  get shape() {
@@ -2949,7 +2949,7 @@ function mergeValues(a, b) {
2949
2949
  }
2950
2950
  var ZodIntersection = class extends ZodType {
2951
2951
  _parse(input) {
2952
- const { status, ctx } = this._processInputParams(input);
2952
+ const { status: status2, ctx } = this._processInputParams(input);
2953
2953
  const handleParsed = (parsedLeft, parsedRight) => {
2954
2954
  if (isAborted(parsedLeft) || isAborted(parsedRight)) {
2955
2955
  return INVALID;
@@ -2962,9 +2962,9 @@ var ZodIntersection = class extends ZodType {
2962
2962
  return INVALID;
2963
2963
  }
2964
2964
  if (isDirty(parsedLeft) || isDirty(parsedRight)) {
2965
- status.dirty();
2965
+ status2.dirty();
2966
2966
  }
2967
- return { status: status.value, value: merged.data };
2967
+ return { status: status2.value, value: merged.data };
2968
2968
  };
2969
2969
  if (ctx.common.async) {
2970
2970
  return Promise.all([
@@ -3002,7 +3002,7 @@ ZodIntersection.create = (left, right, params) => {
3002
3002
  };
3003
3003
  var ZodTuple = class _ZodTuple extends ZodType {
3004
3004
  _parse(input) {
3005
- const { status, ctx } = this._processInputParams(input);
3005
+ const { status: status2, ctx } = this._processInputParams(input);
3006
3006
  if (ctx.parsedType !== ZodParsedType.array) {
3007
3007
  addIssueToContext(ctx, {
3008
3008
  code: ZodIssueCode.invalid_type,
@@ -3030,7 +3030,7 @@ var ZodTuple = class _ZodTuple extends ZodType {
3030
3030
  exact: false,
3031
3031
  type: "array"
3032
3032
  });
3033
- status.dirty();
3033
+ status2.dirty();
3034
3034
  }
3035
3035
  const items = [...ctx.data].map((item, itemIndex) => {
3036
3036
  const schema = this._def.items[itemIndex] || this._def.rest;
@@ -3040,10 +3040,10 @@ var ZodTuple = class _ZodTuple extends ZodType {
3040
3040
  }).filter((x) => !!x);
3041
3041
  if (ctx.common.async) {
3042
3042
  return Promise.all(items).then((results) => {
3043
- return ParseStatus.mergeArray(status, results);
3043
+ return ParseStatus.mergeArray(status2, results);
3044
3044
  });
3045
3045
  } else {
3046
- return ParseStatus.mergeArray(status, items);
3046
+ return ParseStatus.mergeArray(status2, items);
3047
3047
  }
3048
3048
  }
3049
3049
  get items() {
@@ -3075,7 +3075,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
3075
3075
  return this._def.valueType;
3076
3076
  }
3077
3077
  _parse(input) {
3078
- const { status, ctx } = this._processInputParams(input);
3078
+ const { status: status2, ctx } = this._processInputParams(input);
3079
3079
  if (ctx.parsedType !== ZodParsedType.object) {
3080
3080
  addIssueToContext(ctx, {
3081
3081
  code: ZodIssueCode.invalid_type,
@@ -3095,9 +3095,9 @@ var ZodRecord = class _ZodRecord extends ZodType {
3095
3095
  });
3096
3096
  }
3097
3097
  if (ctx.common.async) {
3098
- return ParseStatus.mergeObjectAsync(status, pairs);
3098
+ return ParseStatus.mergeObjectAsync(status2, pairs);
3099
3099
  } else {
3100
- return ParseStatus.mergeObjectSync(status, pairs);
3100
+ return ParseStatus.mergeObjectSync(status2, pairs);
3101
3101
  }
3102
3102
  }
3103
3103
  get element() {
@@ -3128,7 +3128,7 @@ var ZodMap = class extends ZodType {
3128
3128
  return this._def.valueType;
3129
3129
  }
3130
3130
  _parse(input) {
3131
- const { status, ctx } = this._processInputParams(input);
3131
+ const { status: status2, ctx } = this._processInputParams(input);
3132
3132
  if (ctx.parsedType !== ZodParsedType.map) {
3133
3133
  addIssueToContext(ctx, {
3134
3134
  code: ZodIssueCode.invalid_type,
@@ -3155,11 +3155,11 @@ var ZodMap = class extends ZodType {
3155
3155
  return INVALID;
3156
3156
  }
3157
3157
  if (key.status === "dirty" || value.status === "dirty") {
3158
- status.dirty();
3158
+ status2.dirty();
3159
3159
  }
3160
3160
  finalMap.set(key.value, value.value);
3161
3161
  }
3162
- return { status: status.value, value: finalMap };
3162
+ return { status: status2.value, value: finalMap };
3163
3163
  });
3164
3164
  } else {
3165
3165
  const finalMap = /* @__PURE__ */ new Map();
@@ -3170,11 +3170,11 @@ var ZodMap = class extends ZodType {
3170
3170
  return INVALID;
3171
3171
  }
3172
3172
  if (key.status === "dirty" || value.status === "dirty") {
3173
- status.dirty();
3173
+ status2.dirty();
3174
3174
  }
3175
3175
  finalMap.set(key.value, value.value);
3176
3176
  }
3177
- return { status: status.value, value: finalMap };
3177
+ return { status: status2.value, value: finalMap };
3178
3178
  }
3179
3179
  }
3180
3180
  };
@@ -3188,7 +3188,7 @@ ZodMap.create = (keyType, valueType, params) => {
3188
3188
  };
3189
3189
  var ZodSet = class _ZodSet extends ZodType {
3190
3190
  _parse(input) {
3191
- const { status, ctx } = this._processInputParams(input);
3191
+ const { status: status2, ctx } = this._processInputParams(input);
3192
3192
  if (ctx.parsedType !== ZodParsedType.set) {
3193
3193
  addIssueToContext(ctx, {
3194
3194
  code: ZodIssueCode.invalid_type,
@@ -3208,7 +3208,7 @@ var ZodSet = class _ZodSet extends ZodType {
3208
3208
  exact: false,
3209
3209
  message: def.minSize.message
3210
3210
  });
3211
- status.dirty();
3211
+ status2.dirty();
3212
3212
  }
3213
3213
  }
3214
3214
  if (def.maxSize !== null) {
@@ -3221,7 +3221,7 @@ var ZodSet = class _ZodSet extends ZodType {
3221
3221
  exact: false,
3222
3222
  message: def.maxSize.message
3223
3223
  });
3224
- status.dirty();
3224
+ status2.dirty();
3225
3225
  }
3226
3226
  }
3227
3227
  const valueType = this._def.valueType;
@@ -3231,10 +3231,10 @@ var ZodSet = class _ZodSet extends ZodType {
3231
3231
  if (element.status === "aborted")
3232
3232
  return INVALID;
3233
3233
  if (element.status === "dirty")
3234
- status.dirty();
3234
+ status2.dirty();
3235
3235
  parsedSet.add(element.value);
3236
3236
  }
3237
- return { status: status.value, value: parsedSet };
3237
+ return { status: status2.value, value: parsedSet };
3238
3238
  }
3239
3239
  const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));
3240
3240
  if (ctx.common.async) {
@@ -3565,15 +3565,15 @@ var ZodEffects = class extends ZodType {
3565
3565
  return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema;
3566
3566
  }
3567
3567
  _parse(input) {
3568
- const { status, ctx } = this._processInputParams(input);
3568
+ const { status: status2, ctx } = this._processInputParams(input);
3569
3569
  const effect = this._def.effect || null;
3570
3570
  const checkCtx = {
3571
3571
  addIssue: (arg) => {
3572
3572
  addIssueToContext(ctx, arg);
3573
3573
  if (arg.fatal) {
3574
- status.abort();
3574
+ status2.abort();
3575
3575
  } else {
3576
- status.dirty();
3576
+ status2.dirty();
3577
3577
  }
3578
3578
  },
3579
3579
  get path() {
@@ -3585,7 +3585,7 @@ var ZodEffects = class extends ZodType {
3585
3585
  const processed = effect.transform(ctx.data, checkCtx);
3586
3586
  if (ctx.common.async) {
3587
3587
  return Promise.resolve(processed).then(async (processed2) => {
3588
- if (status.value === "aborted")
3588
+ if (status2.value === "aborted")
3589
3589
  return INVALID;
3590
3590
  const result = await this._def.schema._parseAsync({
3591
3591
  data: processed2,
@@ -3596,12 +3596,12 @@ var ZodEffects = class extends ZodType {
3596
3596
  return INVALID;
3597
3597
  if (result.status === "dirty")
3598
3598
  return DIRTY(result.value);
3599
- if (status.value === "dirty")
3599
+ if (status2.value === "dirty")
3600
3600
  return DIRTY(result.value);
3601
3601
  return result;
3602
3602
  });
3603
3603
  } else {
3604
- if (status.value === "aborted")
3604
+ if (status2.value === "aborted")
3605
3605
  return INVALID;
3606
3606
  const result = this._def.schema._parseSync({
3607
3607
  data: processed,
@@ -3612,7 +3612,7 @@ var ZodEffects = class extends ZodType {
3612
3612
  return INVALID;
3613
3613
  if (result.status === "dirty")
3614
3614
  return DIRTY(result.value);
3615
- if (status.value === "dirty")
3615
+ if (status2.value === "dirty")
3616
3616
  return DIRTY(result.value);
3617
3617
  return result;
3618
3618
  }
@@ -3637,17 +3637,17 @@ var ZodEffects = class extends ZodType {
3637
3637
  if (inner.status === "aborted")
3638
3638
  return INVALID;
3639
3639
  if (inner.status === "dirty")
3640
- status.dirty();
3640
+ status2.dirty();
3641
3641
  executeRefinement(inner.value);
3642
- return { status: status.value, value: inner.value };
3642
+ return { status: status2.value, value: inner.value };
3643
3643
  } else {
3644
3644
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {
3645
3645
  if (inner.status === "aborted")
3646
3646
  return INVALID;
3647
3647
  if (inner.status === "dirty")
3648
- status.dirty();
3648
+ status2.dirty();
3649
3649
  return executeRefinement(inner.value).then(() => {
3650
- return { status: status.value, value: inner.value };
3650
+ return { status: status2.value, value: inner.value };
3651
3651
  });
3652
3652
  });
3653
3653
  }
@@ -3665,13 +3665,13 @@ var ZodEffects = class extends ZodType {
3665
3665
  if (result instanceof Promise) {
3666
3666
  throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
3667
3667
  }
3668
- return { status: status.value, value: result };
3668
+ return { status: status2.value, value: result };
3669
3669
  } else {
3670
3670
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
3671
3671
  if (!isValid(base))
3672
3672
  return INVALID;
3673
3673
  return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
3674
- status: status.value,
3674
+ status: status2.value,
3675
3675
  value: result
3676
3676
  }));
3677
3677
  });
@@ -3850,7 +3850,7 @@ var ZodBranded = class extends ZodType {
3850
3850
  };
3851
3851
  var ZodPipeline = class _ZodPipeline extends ZodType {
3852
3852
  _parse(input) {
3853
- const { status, ctx } = this._processInputParams(input);
3853
+ const { status: status2, ctx } = this._processInputParams(input);
3854
3854
  if (ctx.common.async) {
3855
3855
  const handleAsync = async () => {
3856
3856
  const inResult = await this._def.in._parseAsync({
@@ -3861,7 +3861,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
3861
3861
  if (inResult.status === "aborted")
3862
3862
  return INVALID;
3863
3863
  if (inResult.status === "dirty") {
3864
- status.dirty();
3864
+ status2.dirty();
3865
3865
  return DIRTY(inResult.value);
3866
3866
  } else {
3867
3867
  return this._def.out._parseAsync({
@@ -3881,7 +3881,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
3881
3881
  if (inResult.status === "aborted")
3882
3882
  return INVALID;
3883
3883
  if (inResult.status === "dirty") {
3884
- status.dirty();
3884
+ status2.dirty();
3885
3885
  return {
3886
3886
  status: "dirty",
3887
3887
  value: inResult.value
@@ -4047,6 +4047,7 @@ var coerce = {
4047
4047
  var NEVER = INVALID;
4048
4048
 
4049
4049
  // ../shared/src/acp-protocol.ts
4050
+ var ACP_MAX_FRAME_BYTES = 1024 * 1024;
4050
4051
  var acpSessionStateSchema = external_exports.enum(["running", "exited"]);
4051
4052
  var acpToolStatusSchema = external_exports.enum(["pending", "running", "ok", "failed", "cancelled"]);
4052
4053
  var acpToolKindSchema = external_exports.enum([
@@ -4161,7 +4162,9 @@ var acpEventSchema = external_exports.discriminatedUnion("type", [
4161
4162
  name: external_exports.string().nullish(),
4162
4163
  kind: acpToolKindSchema,
4163
4164
  status: acpToolStatusSchema,
4164
- locations: external_exports.array(acpToolLocationSchema).default([])
4165
+ locations: external_exports.array(acpToolLocationSchema).default([]),
4166
+ /** O plano do plan mode chega aqui (`035`); ausente quando o adaptador não mandou. */
4167
+ content: external_exports.array(acpToolContentSchema).optional()
4165
4168
  }),
4166
4169
  /**
4167
4170
  * A change to a call already announced. Every field but the id is optional:
@@ -4315,6 +4318,54 @@ var acpEventSchema = external_exports.discriminatedUnion("type", [
4315
4318
  * at.
4316
4319
  */
4317
4320
  external_exports.object({ type: external_exports.literal("resumed"), fromSessionId: external_exports.string() }),
4321
+ /**
4322
+ * O modelo de ontem não voltou na retomada (`033` F6.2).
4323
+ *
4324
+ * `session/load` não restaura o modelo trocado — medido na M2 —, e o daemon o
4325
+ * reaplica. Quando o agente de hoje não o oferece mais, a conversa continua e
4326
+ * **diz**: trocar calada é a pessoa gastando num modelo que não escolheu sem
4327
+ * saber. `current` vai junto, e não é lido da pílula, porque a linha tem de
4328
+ * dizer o mesmo numa transcrição relida depois de outra troca. Vazio quando o
4329
+ * agente não relata modelo nenhum.
4330
+ */
4331
+ external_exports.object({ type: external_exports.literal("model_unavailable"), model: external_exports.string(), current: external_exports.string() }),
4332
+ /**
4333
+ * O modelo padrão da conta não está na lista desta sessão (`034` T9, Q9).
4334
+ *
4335
+ * Irmã da de cima, e separada dela: aquela é a **retomada** que não trouxe o
4336
+ * modelo de ontem; esta é a **conta** cujo trio guardado envelheceu — o
4337
+ * adaptador atualizou, ou a conta perdeu o plano. A sessão abre no que o
4338
+ * adaptador escolheu (`got`) em vez de falhar ou trocar em silêncio.
4339
+ */
4340
+ external_exports.object({
4341
+ type: external_exports.literal("account_default_unavailable"),
4342
+ requested: external_exports.string(),
4343
+ got: external_exports.string()
4344
+ }),
4345
+ /**
4346
+ * Esta conversa continuou noutra conta (`034` T11, Q3b) — a linha no fim da
4347
+ * **origem**.
4348
+ *
4349
+ * Navegação e nada mais: a origem continua viva e aceitando prompt. `label` é
4350
+ * o `agente · conta` de destino, montado pelo daemon, porque a linha tem de
4351
+ * dizer o mesmo depois que alguém renomear a conta.
4352
+ */
4353
+ external_exports.object({ type: external_exports.literal("continued_in"), sessionId: external_exports.string(), label: external_exports.string() }),
4354
+ /**
4355
+ * Esta conversa é a continuação de outra (`034` T11, Q3a e Q3b) — a linha no
4356
+ * começo da **nova**.
4357
+ *
4358
+ * `messages` e `approxTokens` são do corte que ela levou: a passagem não pode
4359
+ * ser invisível, e *"levou 40 mensagens, ~18 mil tokens"* é o que diz quanto
4360
+ * a conta nova pagou para saber do que se falava.
4361
+ */
4362
+ external_exports.object({
4363
+ type: external_exports.literal("continued_from"),
4364
+ sessionId: external_exports.string(),
4365
+ label: external_exports.string(),
4366
+ messages: external_exports.number().int().nonnegative(),
4367
+ approxTokens: external_exports.number().int().nonnegative()
4368
+ }),
4318
4369
  /**
4319
4370
  * O núcleo da memória entrou no prompt (workspace-memory, D2).
4320
4371
  *
@@ -4356,6 +4407,45 @@ var acpEventSchema = external_exports.discriminatedUnion("type", [
4356
4407
  /** A frase pronta, porque quem monta a frase é quem sabe a unidade. */
4357
4408
  message: external_exports.string()
4358
4409
  }),
4410
+ /**
4411
+ * A conta desta sessão bateu no limite do agente (`028` T17).
4412
+ *
4413
+ * **Traduzido, e não repassado.** O que chega do adaptador é um
4414
+ * `session/prompt` recusado com `-32603` e um `data.errorKind` que só a `spec`
4415
+ * dele sabe ler; o que sai daqui é o conceito do Lumem — *esta conta não pode
4416
+ * mais responder agora* —, com a conta nomeada, porque desde a
4417
+ * [`034`](../../../docs/features/034-agent-accounts/prd.md) *"o Claude bateu no
4418
+ * limite"* não diz qual.
4419
+ *
4420
+ * **Fecha o turno.** O adaptador não manda `turn_end` numa recusa, e sem este
4421
+ * evento a conversa ficaria dizendo que o agente ainda está respondendo. Ele
4422
+ * não é um `turn_end` de propósito: o turno não aconteceu, e contar um turno
4423
+ * aqui gastaria o teto de `turnsPerSession` num turno que a conta recusou.
4424
+ *
4425
+ * `message` é o texto do adaptador, **só para mostrar** — *"resets 7pm"* está
4426
+ * nele e em nenhum outro campo, e nada no daemon o interpreta. `accountId` e
4427
+ * `accountLabel` são nulos quando a sessão não sabe a conta; `agent` é o
4428
+ * rótulo do catálogo.
4429
+ */
4430
+ external_exports.object({
4431
+ type: external_exports.literal("quota_refused"),
4432
+ accountId: external_exports.string().nullable(),
4433
+ accountLabel: external_exports.string().nullable(),
4434
+ agent: external_exports.string(),
4435
+ message: external_exports.string()
4436
+ }),
4437
+ /**
4438
+ * O `session/prompt` foi recusado, e não por cota (irmão do `quota_refused`).
4439
+ *
4440
+ * **Fecha o turno**, pelo mesmo motivo: numa recusa o adaptador não manda
4441
+ * `turn_end`, e sem este evento a conversa ficava em `streaming` para sempre —
4442
+ * o botão de interromper aceso sobre um turno morto, e um `internal error`
4443
+ * genérico por cima. E **não** é um `turn_end`, que é onde o contador de turnos
4444
+ * vira: um turno que não aconteceu não gasta o teto de `turnsPerSession`.
4445
+ *
4446
+ * `message` é a frase do erro, **só para mostrar** — nada decide por ela.
4447
+ */
4448
+ external_exports.object({ type: external_exports.literal("turn_failed"), message: external_exports.string() }),
4359
4449
  /**
4360
4450
  * An event the daemon received and could not name.
4361
4451
  *
@@ -4475,6 +4565,50 @@ var acpServerMessageSchema = external_exports.discriminatedUnion("type", [
4475
4565
  })
4476
4566
  ]);
4477
4567
 
4568
+ // ../shared/src/adapter-catalog.ts
4569
+ var adapterCatalogEntrySchema = external_exports.object({
4570
+ /** `spec.id` — `claude`, `codex`. */
4571
+ adapterId: external_exports.string().min(1),
4572
+ /**
4573
+ * De qual conta é a leitura (`034` T9). A lista de modelos **é** por conta
4574
+ * (§3.3 do estudo). Nulo é a leitura de antes de haver conta — o primeiro
4575
+ * acesso, ou o arquivo de antes da `034`, que o `load` migra para a conta
4576
+ * padrão do agente.
4577
+ */
4578
+ accountId: external_exports.string().nullable().default(null),
4579
+ /**
4580
+ * O `spec.pinnedVersion` no instante da captura, e a chave de validade: trocar
4581
+ * o pino invalida a entrada (Q5).
4582
+ *
4583
+ * `null` quando o id não tem spec em `ADAPTERS` (a config fake dos e2e): não
4584
+ * há pino contra o que conferir, e o dia em que o id ganhar spec a entrada cai
4585
+ * sozinha, porque `null` não é versão nenhuma.
4586
+ */
4587
+ adapterVersion: external_exports.string().nullable(),
4588
+ /**
4589
+ * As opções do `session/new`: o `currentValue` é o **padrão do ACP** (Q8), e
4590
+ * por isso nunca vem de `set_config_option`.
4591
+ */
4592
+ configOptions: external_exports.array(acpConfigOptionSchema),
4593
+ /**
4594
+ * As opções **depois** de escolher cada modelo, pelo valor do modelo (M1a).
4595
+ *
4596
+ * Existe porque a opção de *effort* depende do modelo — `haiku` não tem, o
4597
+ * `gpt-6-astra` vai até `ultra` —, e o `session/new` só conta a do padrão.
4598
+ * Vazio quer dizer *ninguém percorreu os modelos ainda*.
4599
+ */
4600
+ optionsByModel: external_exports.record(external_exports.string(), external_exports.array(acpConfigOptionSchema)),
4601
+ /** Do último probe; `false` quando veio de sessão real; `null` se nunca houve opção gravada. */
4602
+ authRequired: external_exports.boolean().nullable(),
4603
+ /**
4604
+ * `projectId` → os últimos comandos vistos (Q4). Por projeto porque as skills
4605
+ * do repositório entram na lista e dependem do `cwd`.
4606
+ */
4607
+ commandsByProject: external_exports.record(external_exports.string(), external_exports.array(acpCommandSchema)),
4608
+ /** Epoch ms da última mudança de conteúdo. */
4609
+ capturedAt: external_exports.number().int()
4610
+ });
4611
+
4478
4612
  // ../shared/src/adapters.ts
4479
4613
  var CLAUDE_ADAPTER = {
4480
4614
  id: "claude",
@@ -4522,12 +4656,69 @@ var CLAUDE_ADAPTER = {
4522
4656
  apiKeyEnv: ["ANTHROPIC_API_KEY"],
4523
4657
  // Medido na Q43, e é o único dos cinco que fecha o laço: `default`, `plan`,
4524
4658
  // `acceptEdits` e `dontAsk` param no primeiro `Edit` esperando alguém.
4525
- autonomousMode: "bypassPermissions"
4659
+ autonomousMode: "bypassPermissions",
4660
+ // Medido na fase 0 da `034` (§2.1): com a variável, a entrada do Keychain
4661
+ // ganha o sufixo `-<sha256(dir)[0:8]>` e não colide com a de hoje.
4662
+ accountEnv: "CLAUDE_CONFIG_DIR",
4663
+ defaultConfigDir: ".claude",
4664
+ /*
4665
+ * A lista da Q10 mais `agents/`: os subagentes do usuário moram lá, e a Q10
4666
+ * os conta entre o que a conta nova perde (§2.5 do estudo). Os MCPs de
4667
+ * usuário ficam **fora** — moram no `.claude.json`, junto do `oauthAccount`.
4668
+ */
4669
+ inheritLinks: ["settings.json", "CLAUDE.md", "rules", "skills", "plugins", "agents"],
4670
+ // §2.5 do estudo: os `mcpServers` do usuário moram no `~/.claude.json`, junto
4671
+ // do `oauthAccount` — nenhum link traz um sem trazer o outro.
4672
+ notInherited: ["os servidores MCP do usu\xE1rio, que moram no ~/.claude.json junto da identidade"],
4673
+ // §4 do estudo: 0,57 s, zero token, com `email` e `subscriptionType` no JSON.
4674
+ // O `session/new` não serve: o do `0.75.1` fecha sem credencial nenhuma.
4675
+ identity: "cli-auth-status",
4676
+ /*
4677
+ * Medido em 2026-09-28, com uma cota semanal esgotada de verdade no `0.75.1`
4678
+ * (`docs/project/agent-accounts-measurements.md` §7): `session/prompt` recusado
4679
+ * com `{ code: -32603, message: "Internal error: You've hit your weekly limit ·
4680
+ * …", data: { errorKind: "rate_limit" } }`, depois de um `agent_message_chunk`
4681
+ * com o mesmo texto e de um `usage_update` zerado, e sem `turn_end`.
4682
+ */
4683
+ quotaRefusalKind: "rate_limit",
4684
+ /*
4685
+ * Medido em 2026-09-29 contra o `0.75.1` (`036`): o SDK de dentro
4686
+ * (`0.3.257`) nasce com `thinking.display: "omitted"`, e o adaptador só emite
4687
+ * `agent_thought_chunk` quando o bloco tem texto (`acp-agent.js:7742`) — então,
4688
+ * desde 2026-09-08, nenhum pensamento chegava. `_meta.claudeCode.options` é
4689
+ * espalhado direto nas opções do SDK (`:5963`), no `session/new` e no
4690
+ * `session/load`. A cobrança é a mesma com `summarized` e `omitted`.
4691
+ *
4692
+ * Não `MAX_THINKING_TOKENS`: o adaptador a traduz em `enabled` com orçamento e
4693
+ * sem `display`, e o texto continua vazio.
4694
+ */
4695
+ reasoningMeta: { claudeCode: { options: { thinking: { type: "adaptive", display: "summarized" } } } }
4526
4696
  };
4527
4697
  var DEFAULT_ADAPTER_ID = CLAUDE_ADAPTER.id;
4528
4698
 
4529
4699
  // ../shared/src/constants.ts
4530
- var LUMEM_VERSION = "0.5.0";
4700
+ var LUMEM_VERSION = "0.7.0";
4701
+
4702
+ // ../shared/src/desktop.ts
4703
+ var DESKTOP_PLATFORMS = ["darwin-arm64", "darwin-x64", "linux-x64", "linux-arm64"];
4704
+ var DESKTOP_APP_NAME = "Lumem";
4705
+ var DESKTOP_CONFIG_FILE = "lumem-desktop.json";
4706
+ function desktopPlatformOf(platform, arch) {
4707
+ const key = `${platform}-${arch}`;
4708
+ return DESKTOP_PLATFORMS.find((known) => known === key) ?? null;
4709
+ }
4710
+ function desktopPackageName(platform) {
4711
+ return `@vinihcrosa/lumem-desktop-${platform}`;
4712
+ }
4713
+ function desktopDataDir({
4714
+ platform,
4715
+ home,
4716
+ env
4717
+ }) {
4718
+ if (platform === "darwin") return `${home}/Library/Application Support/${DESKTOP_APP_NAME}`;
4719
+ const config = env.XDG_CONFIG_HOME;
4720
+ return `${config !== void 0 && config !== "" ? config : `${home}/.config`}/${DESKTOP_APP_NAME}`;
4721
+ }
4531
4722
 
4532
4723
  // ../shared/src/pty-protocol.ts
4533
4724
  var ptySessionStateSchema = external_exports.enum(["running", "exited"]);
@@ -4574,16 +4765,94 @@ var ptyServerMessageSchema = external_exports.discriminatedUnion("type", [
4574
4765
  external_exports.object({ type: external_exports.literal("error"), code: ptyErrorCodeSchema, message: external_exports.string() })
4575
4766
  ]);
4576
4767
 
4768
+ // ../shared/src/update.ts
4769
+ var PACKAGE_NAME = "@vinihcrosa/lumem-os";
4770
+ var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME.replace("/", "%2F")}/latest`;
4771
+ function detectPackageManager(installPath) {
4772
+ const path = installPath.replace(/\\/g, "/");
4773
+ if (/\/\.?pnpm[/-]/.test(path)) return "pnpm";
4774
+ if (path.includes("/.bun/")) return "bun";
4775
+ if (/\/\.?yarn\//.test(path)) return "yarn";
4776
+ return "npm";
4777
+ }
4778
+ function installCommand(manager, spec) {
4779
+ switch (manager) {
4780
+ case "pnpm":
4781
+ return { command: "pnpm", args: ["add", "--global", spec] };
4782
+ case "yarn":
4783
+ return { command: "yarn", args: ["global", "add", spec] };
4784
+ case "bun":
4785
+ return { command: "bun", args: ["add", "--global", spec] };
4786
+ case "npm":
4787
+ return { command: "npm", args: ["install", "--global", spec] };
4788
+ }
4789
+ }
4790
+ function uninstallCommand(manager, name) {
4791
+ switch (manager) {
4792
+ case "pnpm":
4793
+ return { command: "pnpm", args: ["remove", "--global", name] };
4794
+ case "yarn":
4795
+ return { command: "yarn", args: ["global", "remove", name] };
4796
+ case "bun":
4797
+ return { command: "bun", args: ["remove", "--global", name] };
4798
+ case "npm":
4799
+ return { command: "npm", args: ["uninstall", "--global", name] };
4800
+ }
4801
+ }
4802
+ function compareVersions(a, b) {
4803
+ const split = (version) => {
4804
+ const [core = "", pre = ""] = version.trim().replace(/^v/, "").split("-", 2);
4805
+ return { parts: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
4806
+ };
4807
+ const left = split(a);
4808
+ const right = split(b);
4809
+ for (let i = 0; i < 3; i += 1) {
4810
+ const l = left.parts[i] ?? 0;
4811
+ const r = right.parts[i] ?? 0;
4812
+ if (l !== r) return l < r ? -1 : 1;
4813
+ }
4814
+ if (left.pre === right.pre) return 0;
4815
+ if (left.pre === "") return 1;
4816
+ if (right.pre === "") return -1;
4817
+ return left.pre < right.pre ? -1 : 1;
4818
+ }
4819
+ async function fetchLatestVersion({
4820
+ request = fetch,
4821
+ timeoutMs = 1e4
4822
+ } = {}) {
4823
+ const response = await request(REGISTRY_URL, {
4824
+ signal: AbortSignal.timeout(timeoutMs),
4825
+ headers: { accept: "application/json" }
4826
+ });
4827
+ if (!response.ok) throw new Error(`o registry respondeu ${String(response.status)}`);
4828
+ const body = await response.json();
4829
+ if (typeof body.version !== "string" || body.version === "") {
4830
+ throw new Error("o registry respondeu sem vers\xE3o");
4831
+ }
4832
+ return body.version;
4833
+ }
4834
+
4577
4835
  // src/run.ts
4578
- import { fileURLToPath as fileURLToPath2 } from "node:url";
4836
+ import { realpathSync } from "node:fs";
4837
+ import { isAbsolute, join as join3, resolve } from "node:path";
4838
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
4579
4839
 
4580
4840
  // src/args.ts
4581
4841
  import { parseArgs } from "node:util";
4842
+ var MENUBAR_ACTIONS = ["install", "open", "uninstall"];
4582
4843
  var HELP = `lumem \u2014 harness local de agentes de c\xF3digo
4583
4844
 
4584
4845
  Uso:
4585
- lumem [start] [op\xE7\xF5es] sobe o daemon e serve a interface
4846
+ lumem start [op\xE7\xF5es] instala o Lumem como servi\xE7o, sobe e serve a interface
4847
+ (\xE9 o que \`lumem\`, sem verbo, faz)
4848
+ lumem run [op\xE7\xF5es] roda o daemon em primeiro plano, neste terminal
4849
+ lumem stop para o servi\xE7o e o tira do login
4850
+ lumem status diz se est\xE1 rodando (sa\xEDda 0) ou parado (sa\xEDda 3)
4851
+ lumem logs [-f] as \xFAltimas 200 linhas do log do daemon; -f segue
4586
4852
  lumem upgrade [--check] atualiza o daemon para a \xFAltima vers\xE3o do npm
4853
+ lumem menubar install instala o app do Lumem na barra do sistema (macOS e Linux)
4854
+ lumem menubar open abre o painel do app como janela (GNOME sem AppIndicator)
4855
+ lumem menubar uninstall remove o app, o item de login e os arquivos dele
4587
4856
  lumem version imprime a vers\xE3o
4588
4857
  lumem help imprime esta ajuda
4589
4858
 
@@ -4592,6 +4861,7 @@ Op\xE7\xF5es:
4592
4861
  --host <endere\xE7o> interface de escuta (padr\xE3o: 127.0.0.1)
4593
4862
  --state-dir <caminho> onde o Lumem guarda tudo (padr\xE3o: ~/.lumem)
4594
4863
  --open abre o navegador quando subir
4864
+ -f, --follow em \`logs\`, segue o que for escrito
4595
4865
  --check em \`upgrade\`, s\xF3 diz se tem vers\xE3o nova
4596
4866
  -v, --version o mesmo que \`lumem version\`
4597
4867
  -h, --help o mesmo que \`lumem help\`
@@ -4603,6 +4873,7 @@ function toPort(raw) {
4603
4873
  const port = Number.parseInt(raw.trim(), 10);
4604
4874
  return port >= 0 && port <= 65535 ? port : null;
4605
4875
  }
4876
+ var VERBS_WITH_WHERE = /* @__PURE__ */ new Set(["start", "run", "stop", "status", "logs", "menubar"]);
4606
4877
  function parseCommand(argv) {
4607
4878
  let parsed;
4608
4879
  try {
@@ -4616,6 +4887,7 @@ function parseCommand(argv) {
4616
4887
  "state-dir": { type: "string" },
4617
4888
  open: { type: "boolean", default: false },
4618
4889
  check: { type: "boolean", default: false },
4890
+ follow: { type: "boolean", short: "f", default: false },
4619
4891
  version: { type: "boolean", short: "v", default: false },
4620
4892
  help: { type: "boolean", short: "h", default: false }
4621
4893
  }
@@ -4627,24 +4899,98 @@ function parseCommand(argv) {
4627
4899
  if (values.help === true) return { kind: "help" };
4628
4900
  if (values.version === true) return { kind: "version" };
4629
4901
  const verb = positionals[0] ?? "start";
4630
- if (positionals.length > 1) {
4631
- return { kind: "invalid", message: `comando desconhecido: ${positionals.slice(1).join(" ")}` };
4902
+ if (positionals.length > (verb === "menubar" ? 2 : 1)) {
4903
+ return { kind: "invalid", message: `comando desconhecido: ${positionals.slice(verb === "menubar" ? 2 : 1).join(" ")}` };
4632
4904
  }
4633
4905
  if (verb === "help") return { kind: "help" };
4634
4906
  if (verb === "version") return { kind: "version" };
4635
4907
  if (verb === "upgrade") return { kind: "upgrade", check: values.check === true };
4636
- if (verb !== "start") return { kind: "invalid", message: `comando desconhecido: ${verb}` };
4908
+ if (!VERBS_WITH_WHERE.has(verb)) return { kind: "invalid", message: `comando desconhecido: ${verb}` };
4909
+ if (values.follow === true && verb !== "logs") {
4910
+ return { kind: "invalid", message: "-f s\xF3 existe em `lumem logs`" };
4911
+ }
4637
4912
  const port = values.port === void 0 ? null : toPort(values.port);
4638
4913
  if (values.port !== void 0 && port === null) {
4639
4914
  return { kind: "invalid", message: `--port tem que ser um n\xFAmero entre 0 e 65535, e veio: ${values.port}` };
4640
4915
  }
4641
- return {
4642
- kind: "start",
4643
- port,
4644
- host: values.host ?? null,
4645
- stateDir: values["state-dir"] ?? null,
4646
- open: values.open === true
4647
- };
4916
+ const where = { port, host: values.host ?? null, stateDir: values["state-dir"] ?? null };
4917
+ if (verb === "menubar") {
4918
+ const action = MENUBAR_ACTIONS.find((known) => known === positionals[1]);
4919
+ if (action === void 0) {
4920
+ return {
4921
+ kind: "invalid",
4922
+ message: `menubar pede ${MENUBAR_ACTIONS.join(", ")}${positionals[1] === void 0 ? "" : `, e veio: ${positionals[1]}`}`
4923
+ };
4924
+ }
4925
+ return { kind: "menubar", action, ...where };
4926
+ }
4927
+ if (verb === "stop" || verb === "status") return { kind: verb, ...where };
4928
+ if (verb === "logs") return { kind: "logs", ...where, follow: values.follow === true };
4929
+ return { kind: verb === "run" ? "run" : "start", ...where, open: values.open === true };
4930
+ }
4931
+
4932
+ // src/logs.ts
4933
+ import { closeSync, openSync, readFileSync, readSync, statSync } from "node:fs";
4934
+ import { StringDecoder } from "node:string_decoder";
4935
+ var LOG_TAIL_LINES = 200;
4936
+ function lastLines(text, count) {
4937
+ const lines = text.split("\n");
4938
+ if (lines.at(-1) === "") lines.pop();
4939
+ return lines.slice(-count);
4940
+ }
4941
+ async function logs({
4942
+ path,
4943
+ follow,
4944
+ out,
4945
+ err,
4946
+ interrupt,
4947
+ sleep,
4948
+ pollMs = 250
4949
+ }) {
4950
+ let content;
4951
+ try {
4952
+ content = readFileSync(path);
4953
+ } catch {
4954
+ err(`n\xE3o achei o log do daemon em ${path}. O Lumem j\xE1 subiu como servi\xE7o (\`lumem start\`)?`);
4955
+ return 1;
4956
+ }
4957
+ for (const line of lastLines(content.toString("utf8"), LOG_TAIL_LINES)) out(line);
4958
+ if (!follow) return 0;
4959
+ let offset = content.length;
4960
+ let carry = "";
4961
+ const decoder = new StringDecoder("utf8");
4962
+ while (!interrupt.aborted) {
4963
+ await sleep(pollMs);
4964
+ const size = safeSize(path);
4965
+ if (size < offset) {
4966
+ offset = 0;
4967
+ carry = "";
4968
+ }
4969
+ if (size === offset) continue;
4970
+ carry += decoder.write(readRange(path, offset, size));
4971
+ offset = size;
4972
+ const lines = carry.split("\n");
4973
+ carry = lines.pop() ?? "";
4974
+ for (const line of lines) out(line);
4975
+ }
4976
+ return 0;
4977
+ }
4978
+ function safeSize(path) {
4979
+ try {
4980
+ return statSync(path).size;
4981
+ } catch {
4982
+ return 0;
4983
+ }
4984
+ }
4985
+ function readRange(path, start2, end) {
4986
+ const buffer = Buffer.alloc(end - start2);
4987
+ const fd = openSync(path, "r");
4988
+ try {
4989
+ readSync(fd, buffer, 0, buffer.length, start2);
4990
+ } finally {
4991
+ closeSync(fd);
4992
+ }
4993
+ return buffer;
4648
4994
  }
4649
4995
 
4650
4996
  // src/open.ts
@@ -4666,7 +5012,7 @@ function openInBrowser({
4666
5012
  }
4667
5013
 
4668
5014
  // src/port.ts
4669
- async function probePort({
5015
+ async function readHealth({
4670
5016
  origin,
4671
5017
  request = fetch,
4672
5018
  timeoutMs = 1500
@@ -4683,68 +5029,432 @@ async function probePort({
4683
5029
  const body = await response.json();
4684
5030
  const data = body.result?.data;
4685
5031
  if (data?.ok !== true) return { kind: "other" };
4686
- return { kind: "lumem", version: data.version ?? "?" };
5032
+ return { kind: "lumem", version: data.version ?? "?", supervised: data.supervised === true };
4687
5033
  } catch {
4688
5034
  return { kind: "other" };
4689
5035
  }
4690
5036
  }
5037
+ async function probePort(options) {
5038
+ const health = await readHealth(options);
5039
+ return health.kind === "lumem" ? { kind: "lumem", version: health.version } : health;
5040
+ }
4691
5041
 
4692
- // src/upgrade.ts
5042
+ // src/service.ts
5043
+ import { execFile } from "node:child_process";
5044
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "node:fs";
5045
+ import { homedir } from "node:os";
5046
+ import { dirname, join } from "node:path";
5047
+ var SERVICE_LABEL = "tech.cazimi.lumem";
5048
+ var SYSTEMD_UNIT = "lumem.service";
5049
+ var START_TIMEOUT_MS = 15e3;
5050
+ var STOP_TIMEOUT_MS = 1e4;
5051
+ var POLL_MS = 250;
5052
+ function serviceIdentity(env) {
5053
+ return {
5054
+ label: env["LUMEM_SERVICE_LABEL"] || SERVICE_LABEL,
5055
+ unit: env["LUMEM_SERVICE_UNIT"] || SYSTEMD_UNIT
5056
+ };
5057
+ }
5058
+ function supervisorOf(platform) {
5059
+ if (platform === "darwin") return "launchd";
5060
+ if (platform === "linux") return "systemd";
5061
+ return null;
5062
+ }
5063
+ function daemonLogPath(stateDir) {
5064
+ return join(stateDir, "daemon.log");
5065
+ }
5066
+ function serviceFilePath(host, spec) {
5067
+ return supervisorOf(host.platform) === "launchd" ? join(host.home, "Library", "LaunchAgents", `${spec.label}.plist`) : join(host.home, ".config", "systemd", "user", spec.unit);
5068
+ }
5069
+ function recordedLumemPath(host) {
5070
+ const versioned = /^(.*)\/\.pnpm\/[^/]+\/node_modules\/(@vinihcrosa\/lumem-os\/bin\/lumem\.mjs)$/.exec(host.lumemPath);
5071
+ if (versioned === null) return host.lumemPath;
5072
+ const stable = `${versioned[1] ?? ""}/node_modules/${versioned[2] ?? ""}`;
5073
+ return host.exists(stable) ? stable : host.lumemPath;
5074
+ }
5075
+ function xml(text) {
5076
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
5077
+ }
5078
+ function renderPlist(host, spec) {
5079
+ const environment = { PATH: host.path, ...spec.env, LUMEM_SUPERVISOR: "launchd" };
5080
+ const log = xml(daemonLogPath(spec.stateDir));
5081
+ return `<?xml version="1.0" encoding="UTF-8"?>
5082
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
5083
+ <plist version="1.0">
5084
+ <dict>
5085
+ <key>Label</key>
5086
+ <string>${xml(spec.label)}</string>
5087
+ <key>ProgramArguments</key>
5088
+ <array>
5089
+ <string>${xml(host.nodePath)}</string>
5090
+ <string>${xml(recordedLumemPath(host))}</string>
5091
+ <string>run</string>
5092
+ </array>
5093
+ <key>EnvironmentVariables</key>
5094
+ <dict>
5095
+ ${Object.entries(environment).map(([key, value]) => ` <key>${xml(key)}</key>
5096
+ <string>${xml(value)}</string>`).join("\n")}
5097
+ </dict>
5098
+ <key>KeepAlive</key>
5099
+ <true/>
5100
+ <key>RunAtLoad</key>
5101
+ <true/>
5102
+ <key>StandardOutPath</key>
5103
+ <string>${log}</string>
5104
+ <key>StandardErrorPath</key>
5105
+ <string>${log}</string>
5106
+ </dict>
5107
+ </plist>
5108
+ `;
5109
+ }
5110
+ function unitWord(word) {
5111
+ const escaped = word.replace(/%/g, "%%").replace(/\$/g, "$$$$");
5112
+ return /[\s"'\\]/.test(escaped) ? `"${escaped.replace(/(["\\])/g, "\\$1")}"` : escaped;
5113
+ }
5114
+ function unitEnvironment(key, value) {
5115
+ const escaped = value.replace(/%/g, "%%");
5116
+ const assignment = `${key}=${escaped}`;
5117
+ return /[\s"'\\]/.test(assignment) ? `Environment="${assignment.replace(/(["\\])/g, "\\$1")}"` : `Environment=${assignment}`;
5118
+ }
5119
+ function renderUnit(host, spec) {
5120
+ const environment = { PATH: host.path, ...spec.env, LUMEM_SUPERVISOR: "systemd" };
5121
+ const log = `append:${daemonLogPath(spec.stateDir)}`;
5122
+ return `[Unit]
5123
+ Description=Lumem
5124
+
5125
+ [Service]
5126
+ ExecStart=${unitWord(host.nodePath)} ${unitWord(recordedLumemPath(host))} run
5127
+ ${Object.entries(environment).map(([key, value]) => unitEnvironment(key, value)).join("\n")}
5128
+ Restart=always
5129
+ RestartSec=2
5130
+ StandardOutput=${log}
5131
+ StandardError=${log}
5132
+
5133
+ [Install]
5134
+ WantedBy=default.target
5135
+ `;
5136
+ }
5137
+ function renderService(host, spec) {
5138
+ return supervisorOf(host.platform) === "launchd" ? renderPlist(host, spec) : renderUnit(host, spec);
5139
+ }
5140
+ function isCurrent(host, spec) {
5141
+ return host.read(serviceFilePath(host, spec)) === renderService(host, spec);
5142
+ }
5143
+ var domain = (host) => `gui/${String(host.uid)}`;
5144
+ async function supervisorAnswers(host) {
5145
+ const supervisor = supervisorOf(host.platform);
5146
+ if (supervisor === "launchd") return (await host.exec("launchctl", ["print", domain(host)])).code === 0;
5147
+ if (supervisor === "systemd") {
5148
+ return (await host.exec("systemctl", ["--user", "show-environment"])).code === 0;
5149
+ }
5150
+ return false;
5151
+ }
5152
+ async function isLoaded(host, identity) {
5153
+ const supervisor = supervisorOf(host.platform);
5154
+ if (supervisor === "launchd") {
5155
+ return (await host.exec("launchctl", ["print", `${domain(host)}/${identity.label}`])).code === 0;
5156
+ }
5157
+ if (supervisor === "systemd") {
5158
+ return (await host.exec("systemctl", ["--user", "is-active", identity.unit])).code === 0;
5159
+ }
5160
+ return false;
5161
+ }
5162
+ function failure(step, result) {
5163
+ const detail = result.stderr.trim() || result.stdout.trim() || `saiu com ${String(result.code)}`;
5164
+ return { ok: false, reason: `${step} falhou: ${detail}` };
5165
+ }
5166
+ async function installService(host, spec) {
5167
+ const supervisor = supervisorOf(host.platform);
5168
+ if (supervisor === null) {
5169
+ return {
5170
+ ok: false,
5171
+ reason: `n\xE3o h\xE1 supervisor para ${host.platform}; use \`lumem run\` para rodar em primeiro plano.`
5172
+ };
5173
+ }
5174
+ if (!await supervisorAnswers(host)) {
5175
+ const which = supervisor === "launchd" ? "o launchd n\xE3o responde nesta sess\xE3o (\xE9 preciso uma sess\xE3o gr\xE1fica de usu\xE1rio)" : "o `systemctl --user` n\xE3o responde";
5176
+ return { ok: false, reason: `${which}; use \`lumem run\` para rodar em primeiro plano.` };
5177
+ }
5178
+ const loaded = await isLoaded(host, spec);
5179
+ const file = serviceFilePath(host, spec);
5180
+ host.mkdir(spec.stateDir);
5181
+ host.write(file, renderService(host, spec));
5182
+ if (supervisor === "launchd") {
5183
+ if (loaded) await host.exec("launchctl", ["bootout", `${domain(host)}/${spec.label}`]);
5184
+ const bootstrap = await host.exec("launchctl", ["bootstrap", domain(host), file]);
5185
+ return bootstrap.code === 0 ? { ok: true, file } : failure("`launchctl bootstrap`", bootstrap);
5186
+ }
5187
+ const reload = await host.exec("systemctl", ["--user", "daemon-reload"]);
5188
+ if (reload.code !== 0) return failure("`systemctl --user daemon-reload`", reload);
5189
+ const enable = await host.exec("systemctl", ["--user", "enable", "--now", spec.unit]);
5190
+ if (enable.code !== 0) return failure("`systemctl --user enable --now`", enable);
5191
+ if (loaded) {
5192
+ const restart = await host.exec("systemctl", ["--user", "restart", spec.unit]);
5193
+ if (restart.code !== 0) return failure("`systemctl --user restart`", restart);
5194
+ }
5195
+ return { ok: true, file };
5196
+ }
5197
+ async function restartService(host, identity) {
5198
+ const supervisor = supervisorOf(host.platform);
5199
+ if (supervisor === null) return { ok: false, reason: `n\xE3o h\xE1 supervisor para ${host.platform}` };
5200
+ const [command, args] = supervisor === "launchd" ? ["launchctl", ["kickstart", "-k", `${domain(host)}/${identity.label}`]] : ["systemctl", ["--user", "restart", identity.unit]];
5201
+ const result = await host.exec(command, args);
5202
+ if (result.code === 0) return { ok: true };
5203
+ return failure(`\`${command} ${args.join(" ")}\``, result);
5204
+ }
5205
+ async function waitUntil(host, check, timeoutMs) {
5206
+ const deadline = host.now() + timeoutMs;
5207
+ for (; ; ) {
5208
+ if (await check()) return true;
5209
+ if (host.now() >= deadline) return false;
5210
+ await host.sleep(POLL_MS);
5211
+ }
5212
+ }
5213
+ async function stopService(host, spec, isUp) {
5214
+ const supervisor = supervisorOf(host.platform);
5215
+ const loaded = await isLoaded(host, spec);
5216
+ if (!loaded && await isUp()) {
5217
+ return {
5218
+ ok: false,
5219
+ reason: "o Lumem que responde n\xE3o roda como servi\xE7o; pare-o onde ele roda (Ctrl-C no terminal do `lumem run`)."
5220
+ };
5221
+ }
5222
+ if (supervisor === "launchd") {
5223
+ await host.exec("launchctl", ["bootout", `${domain(host)}/${spec.label}`]);
5224
+ host.remove(serviceFilePath(host, spec));
5225
+ } else if (supervisor === "systemd") {
5226
+ await host.exec("systemctl", ["--user", "disable", "--now", spec.unit]);
5227
+ }
5228
+ const stopped = await waitUntil(host, async () => !await isUp(), STOP_TIMEOUT_MS);
5229
+ return stopped ? { ok: true } : { ok: false, reason: `o Lumem ainda responde depois de ${String(STOP_TIMEOUT_MS / 1e3)} s.` };
5230
+ }
5231
+ function nodeServiceHost(env, lumemPath) {
5232
+ return {
5233
+ platform: process.platform,
5234
+ uid: process.getuid?.() ?? 0,
5235
+ home: homedir(),
5236
+ nodePath: process.execPath,
5237
+ lumemPath,
5238
+ path: env["PATH"] ?? "",
5239
+ exec: (command, args) => new Promise((resolve2) => {
5240
+ execFile(command, [...args], { encoding: "utf8" }, (error, stdout, stderr) => {
5241
+ if (error === null) return resolve2({ code: 0, stdout, stderr });
5242
+ const code2 = typeof error.code === "number" ? error.code : 127;
5243
+ resolve2({ code: code2, stdout, stderr: stderr === "" ? error.message : stderr });
5244
+ });
5245
+ }),
5246
+ read: (path) => {
5247
+ try {
5248
+ return readFileSync2(path, "utf8");
5249
+ } catch {
5250
+ return null;
5251
+ }
5252
+ },
5253
+ exists: (path) => existsSync(path),
5254
+ write: (path, content) => {
5255
+ mkdirSync(dirname(path), { recursive: true });
5256
+ writeFileSync(path, content);
5257
+ },
5258
+ mkdir: (path) => {
5259
+ mkdirSync(path, { recursive: true });
5260
+ },
5261
+ remove: (path) => {
5262
+ rmSync(path, { force: true });
5263
+ },
5264
+ now: () => Date.now(),
5265
+ sleep: (ms) => new Promise((resolve2) => setTimeout(resolve2, ms))
5266
+ };
5267
+ }
5268
+
5269
+ // src/menubar.ts
4693
5270
  import { spawn as spawn2 } from "node:child_process";
5271
+ import { dirname as dirname2, join as join2 } from "node:path";
4694
5272
  import { fileURLToPath } from "node:url";
4695
- var PACKAGE_NAME = "@vinihcrosa/lumem-os";
4696
- var REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`;
4697
- function detectPackageManager(installPath) {
4698
- const path = installPath.replace(/\\/g, "/");
4699
- if (/\/\.?pnpm[/-]/.test(path)) return "pnpm";
4700
- if (path.includes("/.bun/")) return "bun";
4701
- if (/\/\.?yarn\//.test(path)) return "yarn";
4702
- return "npm";
5273
+ var APP_BUNDLE = "Lumem.app";
5274
+ var APP_ZIP = "Lumem.zip";
5275
+ var LINUX_EXECUTABLE = ["app", "lumem-desktop"];
5276
+ var USERNS_CLONE = "/proc/sys/kernel/unprivileged_userns_clone";
5277
+ var APPARMOR_RESTRICT = "/proc/sys/kernel/apparmor_restrict_unprivileged_userns";
5278
+ function probeSandbox(read) {
5279
+ const usernsClone = read(USERNS_CLONE)?.trim() ?? null;
5280
+ const apparmorRestrict = read(APPARMOR_RESTRICT)?.trim() ?? null;
5281
+ return { usernsClone, apparmorRestrict, refused: usernsClone === "0" || apparmorRestrict === "1" };
4703
5282
  }
4704
- function installCommand(manager, spec) {
4705
- switch (manager) {
4706
- case "pnpm":
4707
- return { command: "pnpm", args: ["add", "--global", spec] };
4708
- case "yarn":
4709
- return { command: "yarn", args: ["global", "add", spec] };
4710
- case "bun":
4711
- return { command: "bun", args: ["add", "--global", spec] };
4712
- case "npm":
4713
- return { command: "npm", args: ["install", "--global", spec] };
4714
- }
5283
+ function sandboxReason({ usernsClone }) {
5284
+ const why = usernsClone === "0" ? "unprivileged_userns_clone=0" : "apparmor_restrict_unprivileged_userns=1";
5285
+ return `o kernel n\xE3o deixa o Chromium criar o sandbox (${why}); os .desktop levam --no-sandbox.`;
4715
5286
  }
4716
- function compareVersions(a, b) {
4717
- const split = (version) => {
4718
- const [core = "", pre = ""] = version.trim().replace(/^v/, "").split("-", 2);
4719
- return { parts: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
5287
+ function layoutOf(deps) {
5288
+ const { host, arch, env } = deps;
5289
+ const platform = desktopPlatformOf(host.platform, arch);
5290
+ if (platform === null) return null;
5291
+ const scope = dirname2(dirname2(dirname2(recordedLumemPath(host))));
5292
+ const packageDir = join2(scope, `lumem-desktop-${platform}`);
5293
+ const macApp = join2(host.home, "Applications", APP_BUNDLE);
5294
+ return {
5295
+ platform,
5296
+ packageName: desktopPackageName(platform),
5297
+ packageDir,
5298
+ dataDir: desktopDataDir({ platform: host.platform, home: host.home, env }),
5299
+ macApp,
5300
+ executable: host.platform === "darwin" ? join2(macApp, "Contents", "MacOS", "Lumem") : join2(packageDir, ...LINUX_EXECUTABLE),
5301
+ applicationsEntry: join2(host.home, ".local", "share", "applications", "lumem.desktop"),
5302
+ autostartEntry: join2(host.home, ".config", "autostart", "lumem.desktop")
4720
5303
  };
4721
- const left = split(a);
4722
- const right = split(b);
4723
- for (let i = 0; i < 3; i += 1) {
4724
- const l = left.parts[i] ?? 0;
4725
- const r = right.parts[i] ?? 0;
4726
- if (l !== r) return l < r ? -1 : 1;
5304
+ }
5305
+ function refuse({ host, arch, err }) {
5306
+ err(`o app de desktop n\xE3o existe para ${host.platform}-${arch}. Plataformas com app:`);
5307
+ for (const platform of DESKTOP_PLATFORMS) err(` ${platform}`);
5308
+ return 1;
5309
+ }
5310
+ async function menubar(action, deps) {
5311
+ const layout = layoutOf(deps);
5312
+ if (layout === null) return refuse(deps);
5313
+ if (action === "install") return await installDesktop(deps, layout);
5314
+ if (action === "open") return openDesktop(deps, layout);
5315
+ return await uninstallDesktop(deps, layout);
5316
+ }
5317
+ async function installDesktop(deps, layout) {
5318
+ const { out, err, host, version, where, install } = deps;
5319
+ const command = installCommand(managerOf(deps), `${layout.packageName}@${version}`);
5320
+ out(`instalando o app do Lumem: v${version}`);
5321
+ out(`$ ${command.command} ${command.args.join(" ")}`);
5322
+ const code2 = await runManager(install, command, err);
5323
+ if (code2 !== 0) {
5324
+ err(`o ${command.command} saiu com ${String(code2)}; o app n\xE3o foi instalado.`);
5325
+ return code2;
4727
5326
  }
4728
- if (left.pre === right.pre) return 0;
4729
- if (left.pre === "") return 1;
4730
- if (right.pre === "") return -1;
4731
- return left.pre < right.pre ? -1 : 1;
5327
+ if (!host.exists(layout.packageDir)) {
5328
+ err(`o ${command.command} instalou, mas o pacote n\xE3o est\xE1 em ${layout.packageDir}.`);
5329
+ return 1;
5330
+ }
5331
+ const config = {
5332
+ node: host.nodePath,
5333
+ lumem: recordedLumemPath(host),
5334
+ stateDir: where.stateDir,
5335
+ origin: where.origin,
5336
+ path: host.path
5337
+ };
5338
+ host.write(join2(layout.dataDir, DESKTOP_CONFIG_FILE), `${JSON.stringify(config, null, 2)}
5339
+ `);
5340
+ const placed = await placeApp(host, layout, out);
5341
+ if (placed !== null) {
5342
+ err(placed);
5343
+ return 1;
5344
+ }
5345
+ const launch = deps.launch ?? spawnDetached;
5346
+ if (host.platform === "darwin") launch("open", [layout.macApp]);
5347
+ else launch(layout.executable, []);
5348
+ out(
5349
+ host.platform === "darwin" ? `app em ${layout.macApp}; o \xEDcone est\xE1 na barra de menus.` : `app instalado; o \xEDcone est\xE1 na bandeja e o autostart est\xE1 ligado.`
5350
+ );
5351
+ return 0;
4732
5352
  }
4733
- async function fetchLatestVersion({
4734
- request = fetch,
4735
- timeoutMs = 1e4
4736
- } = {}) {
4737
- const response = await request(REGISTRY_URL, {
4738
- signal: AbortSignal.timeout(timeoutMs),
4739
- headers: { accept: "application/json" }
4740
- });
4741
- if (!response.ok) throw new Error(`o registry respondeu ${String(response.status)}`);
4742
- const body = await response.json();
4743
- if (typeof body.version !== "string" || body.version === "") {
4744
- throw new Error("o registry respondeu sem vers\xE3o");
5353
+ async function takeDesktopAlong(deps) {
5354
+ const layout = layoutOf(deps);
5355
+ const { out, err, host, version } = deps;
5356
+ if (layout === null || !host.exists(layout.packageDir)) return 0;
5357
+ const command = installCommand(managerOf(deps), `${layout.packageName}@${version}`);
5358
+ out(`o app do Lumem vai junto: v${version}`);
5359
+ out(`$ ${command.command} ${command.args.join(" ")}`);
5360
+ const code2 = await runManager(deps.install, command, err);
5361
+ if (code2 !== 0) {
5362
+ err(`o ${command.command} saiu com ${String(code2)}; o app continua na vers\xE3o de antes.`);
5363
+ return code2;
5364
+ }
5365
+ const placed = await placeApp(host, layout, out);
5366
+ if (placed !== null) {
5367
+ err(placed);
5368
+ return 1;
5369
+ }
5370
+ out("app atualizado.");
5371
+ return 0;
5372
+ }
5373
+ async function placeApp(host, layout, out) {
5374
+ if (host.platform !== "darwin") {
5375
+ const sandbox = probeSandbox(host.read);
5376
+ if (sandbox.refused) out(sandboxReason(sandbox));
5377
+ const flags = sandbox.refused ? ["--no-sandbox"] : [];
5378
+ host.write(layout.applicationsEntry, desktopEntry(layout, [...flags, "--panel"]));
5379
+ host.write(layout.autostartEntry, desktopEntry(layout, flags, "X-GNOME-Autostart-enabled=true\n"));
5380
+ return null;
5381
+ }
5382
+ const applications = dirname2(layout.macApp);
5383
+ host.mkdir(applications);
5384
+ await host.exec("rm", ["-rf", layout.macApp]);
5385
+ const unzip = await host.exec("ditto", ["-x", "-k", join2(layout.packageDir, APP_ZIP), applications]);
5386
+ if (unzip.code === 0) return null;
5387
+ return `n\xE3o consegui copiar o app para ${layout.macApp}: ${unzip.stderr.trim() || `ditto saiu com ${String(unzip.code)}`}`;
5388
+ }
5389
+ function execWord(word) {
5390
+ return `"${word.replace(/(["`$\\])/g, "\\$1")}"`;
5391
+ }
5392
+ function desktopEntry(layout, args, extra = "") {
5393
+ const exec = [execWord(layout.executable), ...args].join(" ");
5394
+ return `[Desktop Entry]
5395
+ Type=Application
5396
+ Name=Lumem
5397
+ Comment=O painel do Lumem
5398
+ Exec=${exec}
5399
+ Icon=${join2(layout.packageDir, "icon.png")}
5400
+ Terminal=false
5401
+ Categories=Development;
5402
+ StartupWMClass=Lumem
5403
+ ${extra}`;
5404
+ }
5405
+ function openDesktop(deps, layout) {
5406
+ const { err, host } = deps;
5407
+ if (!host.exists(layout.packageDir) || host.platform === "darwin" && !host.exists(layout.macApp)) {
5408
+ err("o app de desktop n\xE3o est\xE1 instalado. Rode `lumem menubar install`.");
5409
+ return 1;
5410
+ }
5411
+ (deps.launch ?? spawnDetached)(layout.executable, ["--panel"]);
5412
+ return 0;
5413
+ }
5414
+ async function uninstallDesktop(deps, layout) {
5415
+ const { out, err, host, install } = deps;
5416
+ const command = uninstallCommand(managerOf(deps), layout.packageName);
5417
+ out(`$ ${command.command} ${command.args.join(" ")}`);
5418
+ const code2 = await runManager(install, command, err);
5419
+ if (code2 !== 0) {
5420
+ err(`o ${command.command} saiu com ${String(code2)}; o app continua instalado.`);
5421
+ return code2;
5422
+ }
5423
+ if (host.platform === "darwin") {
5424
+ if (host.exists(layout.macApp)) await host.exec(layout.executable, ["--uninstall"]);
5425
+ await host.exec("rm", ["-rf", layout.macApp]);
5426
+ } else {
5427
+ host.remove(layout.applicationsEntry);
5428
+ host.remove(layout.autostartEntry);
5429
+ }
5430
+ await host.exec("rm", ["-rf", layout.dataDir]);
5431
+ out("app do Lumem removido.");
5432
+ return 0;
5433
+ }
5434
+ function managerOf(deps) {
5435
+ return deps.manager ?? detectPackageManager(fileURLToPath(import.meta.url));
5436
+ }
5437
+ async function runManager(install, command, err) {
5438
+ try {
5439
+ return await install(command);
5440
+ } catch (error) {
5441
+ err(`n\xE3o consegui rodar o ${command.command}: ${error instanceof Error ? error.message : String(error)}`);
5442
+ return 1;
5443
+ }
5444
+ }
5445
+ function spawnDetached(command, args) {
5446
+ try {
5447
+ const child = spawn2(command, args, { stdio: "ignore", detached: true });
5448
+ child.on("error", () => {
5449
+ });
5450
+ child.unref();
5451
+ } catch {
4745
5452
  }
4746
- return body.version;
4747
5453
  }
5454
+
5455
+ // src/upgrade.ts
5456
+ import { spawn as spawn3 } from "node:child_process";
5457
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
4748
5458
  async function upgrade(deps) {
4749
5459
  const {
4750
5460
  out,
@@ -4754,8 +5464,10 @@ async function upgrade(deps) {
4754
5464
  origin,
4755
5465
  fetchLatest = () => fetchLatestVersion(),
4756
5466
  install = runInstall,
4757
- manager = detectPackageManager(fileURLToPath(import.meta.url)),
4758
- probe = probePort
5467
+ manager = detectPackageManager(fileURLToPath2(import.meta.url)),
5468
+ probe = probePort,
5469
+ service,
5470
+ desktop
4759
5471
  } = deps;
4760
5472
  let latest;
4761
5473
  try {
@@ -4793,21 +5505,69 @@ async function upgrade(deps) {
4793
5505
  return code2;
4794
5506
  }
4795
5507
  out(`pronto: v${latest} instalado.`);
5508
+ const app = desktop === void 0 ? 0 : await takeDesktopAlong({ out, err, ...desktop, version: latest, manager, install });
5509
+ const daemon = await reachTheNewDaemon({ service, probe, origin, latest, out, err });
5510
+ return daemon !== 0 ? daemon : app;
5511
+ }
5512
+ async function reachTheNewDaemon({
5513
+ service,
5514
+ probe,
5515
+ origin,
5516
+ latest,
5517
+ out,
5518
+ err
5519
+ }) {
5520
+ if (service !== void 0 && await isLoaded(service.host, service.identity)) {
5521
+ return await restartLoadedService({ service, probe, origin, latest, out, err });
5522
+ }
4796
5523
  const occupant = await probe({ origin });
4797
5524
  if (occupant.kind === "lumem") {
4798
5525
  out(`o daemon em ${origin} ainda est\xE1 na v${occupant.version}: pare e suba de novo para valer.`);
4799
5526
  }
4800
5527
  return 0;
4801
5528
  }
5529
+ async function restartLoadedService({
5530
+ service,
5531
+ probe,
5532
+ origin,
5533
+ latest,
5534
+ out,
5535
+ err
5536
+ }) {
5537
+ const restarted = await restartService(service.host, service.identity);
5538
+ if (!restarted.ok) {
5539
+ err(`a vers\xE3o nova est\xE1 instalada, mas o servi\xE7o n\xE3o reiniciou: ${restarted.reason}`);
5540
+ err("rode `lumem stop` e `lumem start` para subir a vers\xE3o nova.");
5541
+ return 1;
5542
+ }
5543
+ let seen = "nenhuma";
5544
+ const came = await waitUntil(
5545
+ service.host,
5546
+ async () => {
5547
+ const occupant = await probe({ origin });
5548
+ if (occupant.kind !== "lumem") return false;
5549
+ seen = `v${occupant.version}`;
5550
+ return occupant.version === latest;
5551
+ },
5552
+ START_TIMEOUT_MS
5553
+ );
5554
+ if (!came) {
5555
+ err(`o servi\xE7o reiniciou, mas o Lumem em ${origin} ainda responde ${seen} e n\xE3o v${latest}.`);
5556
+ err("`lumem logs` mostra o que o daemon escreveu.");
5557
+ return 1;
5558
+ }
5559
+ out(`o servi\xE7o reiniciou: o Lumem em ${origin} agora responde v${latest}.`);
5560
+ return 0;
5561
+ }
4802
5562
  function message(error) {
4803
5563
  return error instanceof Error ? error.message : String(error);
4804
5564
  }
4805
5565
  async function runInstall({ command, args }) {
4806
- return await new Promise((resolve, reject) => {
4807
- const child = spawn2(command, args, { stdio: "inherit" });
5566
+ return await new Promise((resolve2, reject) => {
5567
+ const child = spawn3(command, args, { stdio: "inherit" });
4808
5568
  child.on("error", reject);
4809
5569
  child.on("close", (code2) => {
4810
- resolve(code2 ?? 1);
5570
+ resolve2(code2 ?? 1);
4811
5571
  });
4812
5572
  });
4813
5573
  }
@@ -4817,18 +5577,7 @@ var DEFAULT_PORT = 4317;
4817
5577
  var DEFAULT_HOST = "127.0.0.1";
4818
5578
  var DAEMON_ENTRY = "../dist/server/main.mjs";
4819
5579
  async function run(argv, deps) {
4820
- const {
4821
- out,
4822
- err,
4823
- env,
4824
- version,
4825
- startDaemon = async () => {
4826
- await import(fileURLToPath2(new URL(DAEMON_ENTRY, import.meta.url)));
4827
- },
4828
- probe = probePort,
4829
- open = openInBrowser,
4830
- upgrade: upgrade2 = upgrade
4831
- } = deps;
5580
+ const { out, err, env, version } = deps;
4832
5581
  const command = parseCommand(argv);
4833
5582
  if (command.kind === "help") {
4834
5583
  out(HELP);
@@ -4843,17 +5592,104 @@ async function run(argv, deps) {
4843
5592
  err("`lumem help` lista o que existe.");
4844
5593
  return 2;
4845
5594
  }
4846
- const originOf = (port2, host2) => `http://${host2 === "0.0.0.0" ? "127.0.0.1" : host2}:${String(port2)}`;
5595
+ const probe = deps.probe ?? probePort;
4847
5596
  if (command.kind === "upgrade") {
4848
- return await upgrade2({
5597
+ return await (deps.upgrade ?? upgrade)({
4849
5598
  out,
4850
5599
  err,
4851
5600
  current: version,
4852
5601
  check: command.check,
4853
5602
  origin: originOf(Number(env["LUMEM_PORT"] ?? DEFAULT_PORT), env["LUMEM_HOST"] ?? DEFAULT_HOST),
4854
- probe
5603
+ probe,
5604
+ service: {
5605
+ host: deps.service ?? nodeServiceHost(env, ownPath()),
5606
+ identity: serviceIdentity(env)
5607
+ },
5608
+ desktop: { host: deps.service ?? nodeServiceHost(env, ownPath()), arch: process.arch, env }
5609
+ });
5610
+ }
5611
+ if (command.kind === "menubar") {
5612
+ const host2 = deps.service ?? nodeServiceHost(env, ownPath());
5613
+ const where2 = resolveWhere(command, env, host2.home);
5614
+ return await (deps.menubar ?? menubar)(command.action, {
5615
+ out,
5616
+ err,
5617
+ host: host2,
5618
+ arch: process.arch,
5619
+ env,
5620
+ version,
5621
+ where: { stateDir: where2.stateDir, origin: where2.origin },
5622
+ install: runInstall
5623
+ });
5624
+ }
5625
+ if (command.kind === "run") return await runForeground(command, deps);
5626
+ const host = deps.service ?? nodeServiceHost(env, ownPath());
5627
+ const where = resolveWhere(command, env, host.home);
5628
+ switch (command.kind) {
5629
+ case "start":
5630
+ return await start(command, deps, host, where);
5631
+ case "stop":
5632
+ return await stop(deps, host, where);
5633
+ case "status":
5634
+ return await status(deps, where);
5635
+ case "logs":
5636
+ return await logs({
5637
+ path: daemonLogPath(where.stateDir),
5638
+ follow: command.follow,
5639
+ out,
5640
+ err,
5641
+ interrupt: deps.interrupt ?? (command.follow ? untilInterrupted() : new AbortController().signal),
5642
+ sleep: host.sleep
5643
+ });
5644
+ }
5645
+ }
5646
+ function untilInterrupted() {
5647
+ const controller = new AbortController();
5648
+ for (const signal of ["SIGINT", "SIGTERM"]) {
5649
+ process.once(signal, () => {
5650
+ controller.abort();
4855
5651
  });
4856
5652
  }
5653
+ return controller.signal;
5654
+ }
5655
+ function originOf(port, host) {
5656
+ return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${String(port)}`;
5657
+ }
5658
+ function ownPath() {
5659
+ const invoked = process.argv[1] ?? "";
5660
+ try {
5661
+ return realpathSync(invoked);
5662
+ } catch {
5663
+ return invoked;
5664
+ }
5665
+ }
5666
+ function resolveWhere(command, env, home) {
5667
+ const port = command.port ?? Number(env["LUMEM_PORT"] ?? DEFAULT_PORT);
5668
+ const host = command.host ?? env["LUMEM_HOST"] ?? DEFAULT_HOST;
5669
+ const rawStateDir = command.stateDir ?? env["LUMEM_STATE_DIR"];
5670
+ const requested = {};
5671
+ if (command.port !== null || env["LUMEM_PORT"] !== void 0) requested["LUMEM_PORT"] = String(port);
5672
+ if (command.host !== null || env["LUMEM_HOST"] !== void 0) requested["LUMEM_HOST"] = host;
5673
+ const stateDir = absolute(rawStateDir ?? join3(home, ".lumem"), home);
5674
+ if (rawStateDir !== void 0) requested["LUMEM_STATE_DIR"] = stateDir;
5675
+ return { port, host, origin: originOf(port, host), stateDir, requested };
5676
+ }
5677
+ function absolute(raw, home) {
5678
+ const expanded = raw === "~" || raw.startsWith("~/") ? join3(home, raw.slice(1)) : raw;
5679
+ return isAbsolute(expanded) ? join3(expanded) : resolve(expanded);
5680
+ }
5681
+ async function runForeground(command, deps) {
5682
+ const {
5683
+ out,
5684
+ err,
5685
+ env,
5686
+ version,
5687
+ startDaemon = async () => {
5688
+ await import(fileURLToPath3(new URL(DAEMON_ENTRY, import.meta.url)));
5689
+ },
5690
+ probe = probePort,
5691
+ open = openInBrowser
5692
+ } = deps;
4857
5693
  const port = command.port ?? Number(env["LUMEM_PORT"] ?? DEFAULT_PORT);
4858
5694
  const host = command.host ?? env["LUMEM_HOST"] ?? DEFAULT_HOST;
4859
5695
  const origin = originOf(port, host);
@@ -4875,6 +5711,99 @@ async function run(argv, deps) {
4875
5711
  if (command.open) open({ url: origin });
4876
5712
  return 0;
4877
5713
  }
5714
+ var START_FAILURE_LOG_LINES = 20;
5715
+ async function start(command, deps, host, where) {
5716
+ const { out, err, env } = deps;
5717
+ const probe = deps.probe ?? probePort;
5718
+ const open = deps.open ?? openInBrowser;
5719
+ const spec = {
5720
+ ...serviceIdentity(env),
5721
+ stateDir: where.stateDir,
5722
+ env: where.requested
5723
+ };
5724
+ const occupant = await probe({ origin: where.origin });
5725
+ if (occupant.kind === "other") {
5726
+ err(`a porta ${String(where.port)} est\xE1 ocupada por outra coisa. Use \`lumem start --port <outra>\`.`);
5727
+ return 1;
5728
+ }
5729
+ if (occupant.kind === "lumem") {
5730
+ if (!await isLoaded(host, spec)) {
5731
+ err(
5732
+ `outro Lumem (v${occupant.version}) roda fora do servi\xE7o em ${where.origin}. Pare-o (Ctrl-C no terminal do \`lumem run\`) e rode \`lumem start\` de novo, ou use --port.`
5733
+ );
5734
+ return 1;
5735
+ }
5736
+ if (isCurrent(host, spec)) {
5737
+ out(`o Lumem j\xE1 roda como servi\xE7o em ${where.origin} (v${occupant.version})`);
5738
+ if (command.open) open({ url: where.origin });
5739
+ return 0;
5740
+ }
5741
+ out("o arquivo do servi\xE7o mudou (PATH, node ou lumem); reiniciando o servi\xE7o.");
5742
+ }
5743
+ const installed = await installService(host, spec);
5744
+ if (!installed.ok) {
5745
+ err(installed.reason);
5746
+ return 1;
5747
+ }
5748
+ let running = "?";
5749
+ const answered = await waitUntil(
5750
+ host,
5751
+ async () => {
5752
+ const now = await probe({ origin: where.origin });
5753
+ if (now.kind === "lumem") running = now.version;
5754
+ return now.kind === "lumem";
5755
+ },
5756
+ START_TIMEOUT_MS
5757
+ );
5758
+ if (!answered) {
5759
+ reportStartFailure(deps, host, where);
5760
+ return 1;
5761
+ }
5762
+ out(`Lumem v${running} rodando como servi\xE7o \u2014 ${where.origin}`);
5763
+ out("`lumem logs` mostra o log; `lumem stop` para o servi\xE7o.");
5764
+ if (command.open) open({ url: where.origin });
5765
+ return 0;
5766
+ }
5767
+ function reportStartFailure(deps, host, where) {
5768
+ const { err } = deps;
5769
+ const path = daemonLogPath(where.stateDir);
5770
+ err(`o Lumem n\xE3o respondeu em ${where.origin} em ${String(START_TIMEOUT_MS / 1e3)} s.`);
5771
+ const log = host.read(path);
5772
+ if (log === null) {
5773
+ err(`(sem log em ${path})`);
5774
+ return;
5775
+ }
5776
+ err(`\xFAltimas linhas de ${path}:`);
5777
+ for (const line of lastLines(log, START_FAILURE_LOG_LINES)) err(line);
5778
+ }
5779
+ async function stop(deps, host, where) {
5780
+ const { out, err, env } = deps;
5781
+ const probe = deps.probe ?? probePort;
5782
+ const spec = { ...serviceIdentity(env), stateDir: where.stateDir, env: {} };
5783
+ const result = await stopService(
5784
+ host,
5785
+ spec,
5786
+ async () => (await probe({ origin: where.origin })).kind === "lumem"
5787
+ );
5788
+ if (!result.ok) {
5789
+ err(result.reason);
5790
+ return 1;
5791
+ }
5792
+ out("Lumem parado.");
5793
+ return 0;
5794
+ }
5795
+ async function status(deps, where) {
5796
+ const { out } = deps;
5797
+ const health = await (deps.health ?? readHealth)({ origin: where.origin });
5798
+ if (health.kind === "lumem") {
5799
+ const how = health.supervised ? "supervisionado" : "em primeiro plano";
5800
+ out(`rodando \xB7 v${health.version} \xB7 ${where.origin} \xB7 ${how}`);
5801
+ return 0;
5802
+ }
5803
+ const stranger = health.kind === "other" ? " \xB7 a porta responde, mas n\xE3o \xE9 um Lumem" : "";
5804
+ out(`parado \xB7 ${where.origin}${stranger}`);
5805
+ return 3;
5806
+ }
4878
5807
 
4879
5808
  // src/main.ts
4880
5809
  var code = await run(process.argv.slice(2), {