@rallycry/conveyor-k3 0.1.12 → 0.1.13
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/dist/cli.js +1877 -1093
- package/dist/index.d.ts +54 -3
- package/dist/index.js +713 -226
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -215,6 +215,49 @@ var init_safe_arg = __esm({
|
|
|
215
215
|
}
|
|
216
216
|
});
|
|
217
217
|
|
|
218
|
+
// src/lib/env.ts
|
|
219
|
+
function readCliEnv(env = process.env) {
|
|
220
|
+
const apiUrl = env.CONVEYOR_API_URL ?? "";
|
|
221
|
+
const clusterToken = env.CONVEYOR_CLUSTER_TOKEN ?? "";
|
|
222
|
+
const clusterId = env.CONVEYOR_CLUSTER_ID ?? "";
|
|
223
|
+
if (!apiUrl || !clusterToken) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
"This machine is not enrolled. /etc/conveyor/k3.env must set CONVEYOR_API_URL and CONVEYOR_CLUSTER_TOKEN \u2014 re-run the install command from your Conveyor profile."
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return { apiUrl, clusterToken, clusterId };
|
|
229
|
+
}
|
|
230
|
+
async function preflight(exec) {
|
|
231
|
+
const problems = [];
|
|
232
|
+
if (process.platform !== "linux") {
|
|
233
|
+
problems.push(
|
|
234
|
+
`Managed personal compute requires Linux (this machine reports ${process.platform}).`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
if (process.arch !== "x64" && process.arch !== "arm64") {
|
|
238
|
+
problems.push(`Unsupported CPU architecture ${process.arch} \u2014 amd64 or arm64 is required.`);
|
|
239
|
+
}
|
|
240
|
+
if (typeof process.getuid === "function" && process.getuid() !== 0) {
|
|
241
|
+
problems.push(
|
|
242
|
+
"This guest command requires root inside the VM. Use conveyor-k3 vm commands on the host."
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (!await commandExists(exec, "systemctl")) {
|
|
246
|
+
problems.push("systemd is required and `systemctl` was not found.");
|
|
247
|
+
}
|
|
248
|
+
return { ok: problems.length === 0, problems };
|
|
249
|
+
}
|
|
250
|
+
function checkedHostname(hostname2) {
|
|
251
|
+
return assertHostname(hostname2, "tunnel hostname");
|
|
252
|
+
}
|
|
253
|
+
var init_env = __esm({
|
|
254
|
+
"src/lib/env.ts"() {
|
|
255
|
+
"use strict";
|
|
256
|
+
init_safe_arg();
|
|
257
|
+
init_exec();
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
218
261
|
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
|
|
219
262
|
var util, objectUtil, ZodParsedType, getParsedType;
|
|
220
263
|
var init_util = __esm({
|
|
@@ -486,104 +529,104 @@ var init_en = __esm({
|
|
|
486
529
|
init_ZodError();
|
|
487
530
|
init_util();
|
|
488
531
|
errorMap = (issue, _ctx) => {
|
|
489
|
-
let
|
|
532
|
+
let message3;
|
|
490
533
|
switch (issue.code) {
|
|
491
534
|
case ZodIssueCode.invalid_type:
|
|
492
535
|
if (issue.received === ZodParsedType.undefined) {
|
|
493
|
-
|
|
536
|
+
message3 = "Required";
|
|
494
537
|
} else {
|
|
495
|
-
|
|
538
|
+
message3 = `Expected ${issue.expected}, received ${issue.received}`;
|
|
496
539
|
}
|
|
497
540
|
break;
|
|
498
541
|
case ZodIssueCode.invalid_literal:
|
|
499
|
-
|
|
542
|
+
message3 = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`;
|
|
500
543
|
break;
|
|
501
544
|
case ZodIssueCode.unrecognized_keys:
|
|
502
|
-
|
|
545
|
+
message3 = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`;
|
|
503
546
|
break;
|
|
504
547
|
case ZodIssueCode.invalid_union:
|
|
505
|
-
|
|
548
|
+
message3 = `Invalid input`;
|
|
506
549
|
break;
|
|
507
550
|
case ZodIssueCode.invalid_union_discriminator:
|
|
508
|
-
|
|
551
|
+
message3 = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`;
|
|
509
552
|
break;
|
|
510
553
|
case ZodIssueCode.invalid_enum_value:
|
|
511
|
-
|
|
554
|
+
message3 = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`;
|
|
512
555
|
break;
|
|
513
556
|
case ZodIssueCode.invalid_arguments:
|
|
514
|
-
|
|
557
|
+
message3 = `Invalid function arguments`;
|
|
515
558
|
break;
|
|
516
559
|
case ZodIssueCode.invalid_return_type:
|
|
517
|
-
|
|
560
|
+
message3 = `Invalid function return type`;
|
|
518
561
|
break;
|
|
519
562
|
case ZodIssueCode.invalid_date:
|
|
520
|
-
|
|
563
|
+
message3 = `Invalid date`;
|
|
521
564
|
break;
|
|
522
565
|
case ZodIssueCode.invalid_string:
|
|
523
566
|
if (typeof issue.validation === "object") {
|
|
524
567
|
if ("includes" in issue.validation) {
|
|
525
|
-
|
|
568
|
+
message3 = `Invalid input: must include "${issue.validation.includes}"`;
|
|
526
569
|
if (typeof issue.validation.position === "number") {
|
|
527
|
-
|
|
570
|
+
message3 = `${message3} at one or more positions greater than or equal to ${issue.validation.position}`;
|
|
528
571
|
}
|
|
529
572
|
} else if ("startsWith" in issue.validation) {
|
|
530
|
-
|
|
573
|
+
message3 = `Invalid input: must start with "${issue.validation.startsWith}"`;
|
|
531
574
|
} else if ("endsWith" in issue.validation) {
|
|
532
|
-
|
|
575
|
+
message3 = `Invalid input: must end with "${issue.validation.endsWith}"`;
|
|
533
576
|
} else {
|
|
534
577
|
util.assertNever(issue.validation);
|
|
535
578
|
}
|
|
536
579
|
} else if (issue.validation !== "regex") {
|
|
537
|
-
|
|
580
|
+
message3 = `Invalid ${issue.validation}`;
|
|
538
581
|
} else {
|
|
539
|
-
|
|
582
|
+
message3 = "Invalid";
|
|
540
583
|
}
|
|
541
584
|
break;
|
|
542
585
|
case ZodIssueCode.too_small:
|
|
543
586
|
if (issue.type === "array")
|
|
544
|
-
|
|
587
|
+
message3 = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
|
|
545
588
|
else if (issue.type === "string")
|
|
546
|
-
|
|
589
|
+
message3 = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
|
|
547
590
|
else if (issue.type === "number")
|
|
548
|
-
|
|
591
|
+
message3 = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`;
|
|
549
592
|
else if (issue.type === "bigint")
|
|
550
|
-
|
|
593
|
+
message3 = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`;
|
|
551
594
|
else if (issue.type === "date")
|
|
552
|
-
|
|
595
|
+
message3 = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`;
|
|
553
596
|
else
|
|
554
|
-
|
|
597
|
+
message3 = "Invalid input";
|
|
555
598
|
break;
|
|
556
599
|
case ZodIssueCode.too_big:
|
|
557
600
|
if (issue.type === "array")
|
|
558
|
-
|
|
601
|
+
message3 = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
|
|
559
602
|
else if (issue.type === "string")
|
|
560
|
-
|
|
603
|
+
message3 = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
|
|
561
604
|
else if (issue.type === "number")
|
|
562
|
-
|
|
605
|
+
message3 = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
|
|
563
606
|
else if (issue.type === "bigint")
|
|
564
|
-
|
|
607
|
+
message3 = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`;
|
|
565
608
|
else if (issue.type === "date")
|
|
566
|
-
|
|
609
|
+
message3 = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`;
|
|
567
610
|
else
|
|
568
|
-
|
|
611
|
+
message3 = "Invalid input";
|
|
569
612
|
break;
|
|
570
613
|
case ZodIssueCode.custom:
|
|
571
|
-
|
|
614
|
+
message3 = `Invalid input`;
|
|
572
615
|
break;
|
|
573
616
|
case ZodIssueCode.invalid_intersection_types:
|
|
574
|
-
|
|
617
|
+
message3 = `Intersection results could not be merged`;
|
|
575
618
|
break;
|
|
576
619
|
case ZodIssueCode.not_multiple_of:
|
|
577
|
-
|
|
620
|
+
message3 = `Number must be a multiple of ${issue.multipleOf}`;
|
|
578
621
|
break;
|
|
579
622
|
case ZodIssueCode.not_finite:
|
|
580
|
-
|
|
623
|
+
message3 = "Number must be finite";
|
|
581
624
|
break;
|
|
582
625
|
default:
|
|
583
|
-
|
|
626
|
+
message3 = _ctx.defaultError;
|
|
584
627
|
util.assertNever(issue);
|
|
585
628
|
}
|
|
586
|
-
return { message:
|
|
629
|
+
return { message: message3 };
|
|
587
630
|
};
|
|
588
631
|
en_default = errorMap;
|
|
589
632
|
}
|
|
@@ -736,8 +779,8 @@ var init_errorUtil = __esm({
|
|
|
736
779
|
"../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js"() {
|
|
737
780
|
"use strict";
|
|
738
781
|
(function(errorUtil2) {
|
|
739
|
-
errorUtil2.errToObj = (
|
|
740
|
-
errorUtil2.toString = (
|
|
782
|
+
errorUtil2.errToObj = (message3) => typeof message3 === "string" ? { message: message3 } : message3 || {};
|
|
783
|
+
errorUtil2.toString = (message3) => typeof message3 === "string" ? message3 : message3?.message;
|
|
741
784
|
})(errorUtil || (errorUtil = {}));
|
|
742
785
|
}
|
|
743
786
|
});
|
|
@@ -753,16 +796,16 @@ function processCreateParams(params) {
|
|
|
753
796
|
if (errorMap2)
|
|
754
797
|
return { errorMap: errorMap2, description };
|
|
755
798
|
const customMap = (iss, ctx) => {
|
|
756
|
-
const { message:
|
|
799
|
+
const { message: message3 } = params;
|
|
757
800
|
if (iss.code === "invalid_enum_value") {
|
|
758
|
-
return { message:
|
|
801
|
+
return { message: message3 ?? ctx.defaultError };
|
|
759
802
|
}
|
|
760
803
|
if (typeof ctx.data === "undefined") {
|
|
761
|
-
return { message:
|
|
804
|
+
return { message: message3 ?? required_error ?? ctx.defaultError };
|
|
762
805
|
}
|
|
763
806
|
if (iss.code !== "invalid_type")
|
|
764
807
|
return { message: ctx.defaultError };
|
|
765
|
-
return { message:
|
|
808
|
+
return { message: message3 ?? invalid_type_error ?? ctx.defaultError };
|
|
766
809
|
};
|
|
767
810
|
return { errorMap: customMap, description };
|
|
768
811
|
}
|
|
@@ -1103,14 +1146,14 @@ var init_types = __esm({
|
|
|
1103
1146
|
const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult));
|
|
1104
1147
|
return handleResult(ctx, result);
|
|
1105
1148
|
}
|
|
1106
|
-
refine(check,
|
|
1149
|
+
refine(check, message3) {
|
|
1107
1150
|
const getIssueProperties = (val) => {
|
|
1108
|
-
if (typeof
|
|
1109
|
-
return { message:
|
|
1110
|
-
} else if (typeof
|
|
1111
|
-
return
|
|
1151
|
+
if (typeof message3 === "string" || typeof message3 === "undefined") {
|
|
1152
|
+
return { message: message3 };
|
|
1153
|
+
} else if (typeof message3 === "function") {
|
|
1154
|
+
return message3(val);
|
|
1112
1155
|
} else {
|
|
1113
|
-
return
|
|
1156
|
+
return message3;
|
|
1114
1157
|
}
|
|
1115
1158
|
};
|
|
1116
1159
|
return this._refinement((val, ctx) => {
|
|
@@ -1583,11 +1626,11 @@ var init_types = __esm({
|
|
|
1583
1626
|
}
|
|
1584
1627
|
return { status: status.value, value: input.data };
|
|
1585
1628
|
}
|
|
1586
|
-
_regex(regex, validation,
|
|
1629
|
+
_regex(regex, validation, message3) {
|
|
1587
1630
|
return this.refinement((data) => regex.test(data), {
|
|
1588
1631
|
validation,
|
|
1589
1632
|
code: ZodIssueCode.invalid_string,
|
|
1590
|
-
...errorUtil.errToObj(
|
|
1633
|
+
...errorUtil.errToObj(message3)
|
|
1591
1634
|
});
|
|
1592
1635
|
}
|
|
1593
1636
|
_addCheck(check) {
|
|
@@ -1596,37 +1639,37 @@ var init_types = __esm({
|
|
|
1596
1639
|
checks: [...this._def.checks, check]
|
|
1597
1640
|
});
|
|
1598
1641
|
}
|
|
1599
|
-
email(
|
|
1600
|
-
return this._addCheck({ kind: "email", ...errorUtil.errToObj(
|
|
1642
|
+
email(message3) {
|
|
1643
|
+
return this._addCheck({ kind: "email", ...errorUtil.errToObj(message3) });
|
|
1601
1644
|
}
|
|
1602
|
-
url(
|
|
1603
|
-
return this._addCheck({ kind: "url", ...errorUtil.errToObj(
|
|
1645
|
+
url(message3) {
|
|
1646
|
+
return this._addCheck({ kind: "url", ...errorUtil.errToObj(message3) });
|
|
1604
1647
|
}
|
|
1605
|
-
emoji(
|
|
1606
|
-
return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(
|
|
1648
|
+
emoji(message3) {
|
|
1649
|
+
return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message3) });
|
|
1607
1650
|
}
|
|
1608
|
-
uuid(
|
|
1609
|
-
return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(
|
|
1651
|
+
uuid(message3) {
|
|
1652
|
+
return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message3) });
|
|
1610
1653
|
}
|
|
1611
|
-
nanoid(
|
|
1612
|
-
return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(
|
|
1654
|
+
nanoid(message3) {
|
|
1655
|
+
return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message3) });
|
|
1613
1656
|
}
|
|
1614
|
-
cuid(
|
|
1615
|
-
return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(
|
|
1657
|
+
cuid(message3) {
|
|
1658
|
+
return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message3) });
|
|
1616
1659
|
}
|
|
1617
|
-
cuid2(
|
|
1618
|
-
return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(
|
|
1660
|
+
cuid2(message3) {
|
|
1661
|
+
return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message3) });
|
|
1619
1662
|
}
|
|
1620
|
-
ulid(
|
|
1621
|
-
return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(
|
|
1663
|
+
ulid(message3) {
|
|
1664
|
+
return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message3) });
|
|
1622
1665
|
}
|
|
1623
|
-
base64(
|
|
1624
|
-
return this._addCheck({ kind: "base64", ...errorUtil.errToObj(
|
|
1666
|
+
base64(message3) {
|
|
1667
|
+
return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message3) });
|
|
1625
1668
|
}
|
|
1626
|
-
base64url(
|
|
1669
|
+
base64url(message3) {
|
|
1627
1670
|
return this._addCheck({
|
|
1628
1671
|
kind: "base64url",
|
|
1629
|
-
...errorUtil.errToObj(
|
|
1672
|
+
...errorUtil.errToObj(message3)
|
|
1630
1673
|
});
|
|
1631
1674
|
}
|
|
1632
1675
|
jwt(options) {
|
|
@@ -1656,8 +1699,8 @@ var init_types = __esm({
|
|
|
1656
1699
|
...errorUtil.errToObj(options?.message)
|
|
1657
1700
|
});
|
|
1658
1701
|
}
|
|
1659
|
-
date(
|
|
1660
|
-
return this._addCheck({ kind: "date", message:
|
|
1702
|
+
date(message3) {
|
|
1703
|
+
return this._addCheck({ kind: "date", message: message3 });
|
|
1661
1704
|
}
|
|
1662
1705
|
time(options) {
|
|
1663
1706
|
if (typeof options === "string") {
|
|
@@ -1673,14 +1716,14 @@ var init_types = __esm({
|
|
|
1673
1716
|
...errorUtil.errToObj(options?.message)
|
|
1674
1717
|
});
|
|
1675
1718
|
}
|
|
1676
|
-
duration(
|
|
1677
|
-
return this._addCheck({ kind: "duration", ...errorUtil.errToObj(
|
|
1719
|
+
duration(message3) {
|
|
1720
|
+
return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message3) });
|
|
1678
1721
|
}
|
|
1679
|
-
regex(regex,
|
|
1722
|
+
regex(regex, message3) {
|
|
1680
1723
|
return this._addCheck({
|
|
1681
1724
|
kind: "regex",
|
|
1682
1725
|
regex,
|
|
1683
|
-
...errorUtil.errToObj(
|
|
1726
|
+
...errorUtil.errToObj(message3)
|
|
1684
1727
|
});
|
|
1685
1728
|
}
|
|
1686
1729
|
includes(value, options) {
|
|
@@ -1691,46 +1734,46 @@ var init_types = __esm({
|
|
|
1691
1734
|
...errorUtil.errToObj(options?.message)
|
|
1692
1735
|
});
|
|
1693
1736
|
}
|
|
1694
|
-
startsWith(value,
|
|
1737
|
+
startsWith(value, message3) {
|
|
1695
1738
|
return this._addCheck({
|
|
1696
1739
|
kind: "startsWith",
|
|
1697
1740
|
value,
|
|
1698
|
-
...errorUtil.errToObj(
|
|
1741
|
+
...errorUtil.errToObj(message3)
|
|
1699
1742
|
});
|
|
1700
1743
|
}
|
|
1701
|
-
endsWith(value,
|
|
1744
|
+
endsWith(value, message3) {
|
|
1702
1745
|
return this._addCheck({
|
|
1703
1746
|
kind: "endsWith",
|
|
1704
1747
|
value,
|
|
1705
|
-
...errorUtil.errToObj(
|
|
1748
|
+
...errorUtil.errToObj(message3)
|
|
1706
1749
|
});
|
|
1707
1750
|
}
|
|
1708
|
-
min(minLength,
|
|
1751
|
+
min(minLength, message3) {
|
|
1709
1752
|
return this._addCheck({
|
|
1710
1753
|
kind: "min",
|
|
1711
1754
|
value: minLength,
|
|
1712
|
-
...errorUtil.errToObj(
|
|
1755
|
+
...errorUtil.errToObj(message3)
|
|
1713
1756
|
});
|
|
1714
1757
|
}
|
|
1715
|
-
max(maxLength,
|
|
1758
|
+
max(maxLength, message3) {
|
|
1716
1759
|
return this._addCheck({
|
|
1717
1760
|
kind: "max",
|
|
1718
1761
|
value: maxLength,
|
|
1719
|
-
...errorUtil.errToObj(
|
|
1762
|
+
...errorUtil.errToObj(message3)
|
|
1720
1763
|
});
|
|
1721
1764
|
}
|
|
1722
|
-
length(len,
|
|
1765
|
+
length(len, message3) {
|
|
1723
1766
|
return this._addCheck({
|
|
1724
1767
|
kind: "length",
|
|
1725
1768
|
value: len,
|
|
1726
|
-
...errorUtil.errToObj(
|
|
1769
|
+
...errorUtil.errToObj(message3)
|
|
1727
1770
|
});
|
|
1728
1771
|
}
|
|
1729
1772
|
/**
|
|
1730
1773
|
* Equivalent to `.min(1)`
|
|
1731
1774
|
*/
|
|
1732
|
-
nonempty(
|
|
1733
|
-
return this.min(1, errorUtil.errToObj(
|
|
1775
|
+
nonempty(message3) {
|
|
1776
|
+
return this.min(1, errorUtil.errToObj(message3));
|
|
1734
1777
|
}
|
|
1735
1778
|
trim() {
|
|
1736
1779
|
return new _ZodString({
|
|
@@ -1915,19 +1958,19 @@ var init_types = __esm({
|
|
|
1915
1958
|
}
|
|
1916
1959
|
return { status: status.value, value: input.data };
|
|
1917
1960
|
}
|
|
1918
|
-
gte(value,
|
|
1919
|
-
return this.setLimit("min", value, true, errorUtil.toString(
|
|
1961
|
+
gte(value, message3) {
|
|
1962
|
+
return this.setLimit("min", value, true, errorUtil.toString(message3));
|
|
1920
1963
|
}
|
|
1921
|
-
gt(value,
|
|
1922
|
-
return this.setLimit("min", value, false, errorUtil.toString(
|
|
1964
|
+
gt(value, message3) {
|
|
1965
|
+
return this.setLimit("min", value, false, errorUtil.toString(message3));
|
|
1923
1966
|
}
|
|
1924
|
-
lte(value,
|
|
1925
|
-
return this.setLimit("max", value, true, errorUtil.toString(
|
|
1967
|
+
lte(value, message3) {
|
|
1968
|
+
return this.setLimit("max", value, true, errorUtil.toString(message3));
|
|
1926
1969
|
}
|
|
1927
|
-
lt(value,
|
|
1928
|
-
return this.setLimit("max", value, false, errorUtil.toString(
|
|
1970
|
+
lt(value, message3) {
|
|
1971
|
+
return this.setLimit("max", value, false, errorUtil.toString(message3));
|
|
1929
1972
|
}
|
|
1930
|
-
setLimit(kind, value, inclusive,
|
|
1973
|
+
setLimit(kind, value, inclusive, message3) {
|
|
1931
1974
|
return new _ZodNumber({
|
|
1932
1975
|
...this._def,
|
|
1933
1976
|
checks: [
|
|
@@ -1936,7 +1979,7 @@ var init_types = __esm({
|
|
|
1936
1979
|
kind,
|
|
1937
1980
|
value,
|
|
1938
1981
|
inclusive,
|
|
1939
|
-
message: errorUtil.toString(
|
|
1982
|
+
message: errorUtil.toString(message3)
|
|
1940
1983
|
}
|
|
1941
1984
|
]
|
|
1942
1985
|
});
|
|
@@ -1947,68 +1990,68 @@ var init_types = __esm({
|
|
|
1947
1990
|
checks: [...this._def.checks, check]
|
|
1948
1991
|
});
|
|
1949
1992
|
}
|
|
1950
|
-
int(
|
|
1993
|
+
int(message3) {
|
|
1951
1994
|
return this._addCheck({
|
|
1952
1995
|
kind: "int",
|
|
1953
|
-
message: errorUtil.toString(
|
|
1996
|
+
message: errorUtil.toString(message3)
|
|
1954
1997
|
});
|
|
1955
1998
|
}
|
|
1956
|
-
positive(
|
|
1999
|
+
positive(message3) {
|
|
1957
2000
|
return this._addCheck({
|
|
1958
2001
|
kind: "min",
|
|
1959
2002
|
value: 0,
|
|
1960
2003
|
inclusive: false,
|
|
1961
|
-
message: errorUtil.toString(
|
|
2004
|
+
message: errorUtil.toString(message3)
|
|
1962
2005
|
});
|
|
1963
2006
|
}
|
|
1964
|
-
negative(
|
|
2007
|
+
negative(message3) {
|
|
1965
2008
|
return this._addCheck({
|
|
1966
2009
|
kind: "max",
|
|
1967
2010
|
value: 0,
|
|
1968
2011
|
inclusive: false,
|
|
1969
|
-
message: errorUtil.toString(
|
|
2012
|
+
message: errorUtil.toString(message3)
|
|
1970
2013
|
});
|
|
1971
2014
|
}
|
|
1972
|
-
nonpositive(
|
|
2015
|
+
nonpositive(message3) {
|
|
1973
2016
|
return this._addCheck({
|
|
1974
2017
|
kind: "max",
|
|
1975
2018
|
value: 0,
|
|
1976
2019
|
inclusive: true,
|
|
1977
|
-
message: errorUtil.toString(
|
|
2020
|
+
message: errorUtil.toString(message3)
|
|
1978
2021
|
});
|
|
1979
2022
|
}
|
|
1980
|
-
nonnegative(
|
|
2023
|
+
nonnegative(message3) {
|
|
1981
2024
|
return this._addCheck({
|
|
1982
2025
|
kind: "min",
|
|
1983
2026
|
value: 0,
|
|
1984
2027
|
inclusive: true,
|
|
1985
|
-
message: errorUtil.toString(
|
|
2028
|
+
message: errorUtil.toString(message3)
|
|
1986
2029
|
});
|
|
1987
2030
|
}
|
|
1988
|
-
multipleOf(value,
|
|
2031
|
+
multipleOf(value, message3) {
|
|
1989
2032
|
return this._addCheck({
|
|
1990
2033
|
kind: "multipleOf",
|
|
1991
2034
|
value,
|
|
1992
|
-
message: errorUtil.toString(
|
|
2035
|
+
message: errorUtil.toString(message3)
|
|
1993
2036
|
});
|
|
1994
2037
|
}
|
|
1995
|
-
finite(
|
|
2038
|
+
finite(message3) {
|
|
1996
2039
|
return this._addCheck({
|
|
1997
2040
|
kind: "finite",
|
|
1998
|
-
message: errorUtil.toString(
|
|
2041
|
+
message: errorUtil.toString(message3)
|
|
1999
2042
|
});
|
|
2000
2043
|
}
|
|
2001
|
-
safe(
|
|
2044
|
+
safe(message3) {
|
|
2002
2045
|
return this._addCheck({
|
|
2003
2046
|
kind: "min",
|
|
2004
2047
|
inclusive: true,
|
|
2005
2048
|
value: Number.MIN_SAFE_INTEGER,
|
|
2006
|
-
message: errorUtil.toString(
|
|
2049
|
+
message: errorUtil.toString(message3)
|
|
2007
2050
|
})._addCheck({
|
|
2008
2051
|
kind: "max",
|
|
2009
2052
|
inclusive: true,
|
|
2010
2053
|
value: Number.MAX_SAFE_INTEGER,
|
|
2011
|
-
message: errorUtil.toString(
|
|
2054
|
+
message: errorUtil.toString(message3)
|
|
2012
2055
|
});
|
|
2013
2056
|
}
|
|
2014
2057
|
get minValue() {
|
|
@@ -2131,19 +2174,19 @@ var init_types = __esm({
|
|
|
2131
2174
|
});
|
|
2132
2175
|
return INVALID;
|
|
2133
2176
|
}
|
|
2134
|
-
gte(value,
|
|
2135
|
-
return this.setLimit("min", value, true, errorUtil.toString(
|
|
2177
|
+
gte(value, message3) {
|
|
2178
|
+
return this.setLimit("min", value, true, errorUtil.toString(message3));
|
|
2136
2179
|
}
|
|
2137
|
-
gt(value,
|
|
2138
|
-
return this.setLimit("min", value, false, errorUtil.toString(
|
|
2180
|
+
gt(value, message3) {
|
|
2181
|
+
return this.setLimit("min", value, false, errorUtil.toString(message3));
|
|
2139
2182
|
}
|
|
2140
|
-
lte(value,
|
|
2141
|
-
return this.setLimit("max", value, true, errorUtil.toString(
|
|
2183
|
+
lte(value, message3) {
|
|
2184
|
+
return this.setLimit("max", value, true, errorUtil.toString(message3));
|
|
2142
2185
|
}
|
|
2143
|
-
lt(value,
|
|
2144
|
-
return this.setLimit("max", value, false, errorUtil.toString(
|
|
2186
|
+
lt(value, message3) {
|
|
2187
|
+
return this.setLimit("max", value, false, errorUtil.toString(message3));
|
|
2145
2188
|
}
|
|
2146
|
-
setLimit(kind, value, inclusive,
|
|
2189
|
+
setLimit(kind, value, inclusive, message3) {
|
|
2147
2190
|
return new _ZodBigInt({
|
|
2148
2191
|
...this._def,
|
|
2149
2192
|
checks: [
|
|
@@ -2152,7 +2195,7 @@ var init_types = __esm({
|
|
|
2152
2195
|
kind,
|
|
2153
2196
|
value,
|
|
2154
2197
|
inclusive,
|
|
2155
|
-
message: errorUtil.toString(
|
|
2198
|
+
message: errorUtil.toString(message3)
|
|
2156
2199
|
}
|
|
2157
2200
|
]
|
|
2158
2201
|
});
|
|
@@ -2163,43 +2206,43 @@ var init_types = __esm({
|
|
|
2163
2206
|
checks: [...this._def.checks, check]
|
|
2164
2207
|
});
|
|
2165
2208
|
}
|
|
2166
|
-
positive(
|
|
2209
|
+
positive(message3) {
|
|
2167
2210
|
return this._addCheck({
|
|
2168
2211
|
kind: "min",
|
|
2169
2212
|
value: BigInt(0),
|
|
2170
2213
|
inclusive: false,
|
|
2171
|
-
message: errorUtil.toString(
|
|
2214
|
+
message: errorUtil.toString(message3)
|
|
2172
2215
|
});
|
|
2173
2216
|
}
|
|
2174
|
-
negative(
|
|
2217
|
+
negative(message3) {
|
|
2175
2218
|
return this._addCheck({
|
|
2176
2219
|
kind: "max",
|
|
2177
2220
|
value: BigInt(0),
|
|
2178
2221
|
inclusive: false,
|
|
2179
|
-
message: errorUtil.toString(
|
|
2222
|
+
message: errorUtil.toString(message3)
|
|
2180
2223
|
});
|
|
2181
2224
|
}
|
|
2182
|
-
nonpositive(
|
|
2225
|
+
nonpositive(message3) {
|
|
2183
2226
|
return this._addCheck({
|
|
2184
2227
|
kind: "max",
|
|
2185
2228
|
value: BigInt(0),
|
|
2186
2229
|
inclusive: true,
|
|
2187
|
-
message: errorUtil.toString(
|
|
2230
|
+
message: errorUtil.toString(message3)
|
|
2188
2231
|
});
|
|
2189
2232
|
}
|
|
2190
|
-
nonnegative(
|
|
2233
|
+
nonnegative(message3) {
|
|
2191
2234
|
return this._addCheck({
|
|
2192
2235
|
kind: "min",
|
|
2193
2236
|
value: BigInt(0),
|
|
2194
2237
|
inclusive: true,
|
|
2195
|
-
message: errorUtil.toString(
|
|
2238
|
+
message: errorUtil.toString(message3)
|
|
2196
2239
|
});
|
|
2197
2240
|
}
|
|
2198
|
-
multipleOf(value,
|
|
2241
|
+
multipleOf(value, message3) {
|
|
2199
2242
|
return this._addCheck({
|
|
2200
2243
|
kind: "multipleOf",
|
|
2201
2244
|
value,
|
|
2202
|
-
message: errorUtil.toString(
|
|
2245
|
+
message: errorUtil.toString(message3)
|
|
2203
2246
|
});
|
|
2204
2247
|
}
|
|
2205
2248
|
get minValue() {
|
|
@@ -2322,18 +2365,18 @@ var init_types = __esm({
|
|
|
2322
2365
|
checks: [...this._def.checks, check]
|
|
2323
2366
|
});
|
|
2324
2367
|
}
|
|
2325
|
-
min(minDate,
|
|
2368
|
+
min(minDate, message3) {
|
|
2326
2369
|
return this._addCheck({
|
|
2327
2370
|
kind: "min",
|
|
2328
2371
|
value: minDate.getTime(),
|
|
2329
|
-
message: errorUtil.toString(
|
|
2372
|
+
message: errorUtil.toString(message3)
|
|
2330
2373
|
});
|
|
2331
2374
|
}
|
|
2332
|
-
max(maxDate,
|
|
2375
|
+
max(maxDate, message3) {
|
|
2333
2376
|
return this._addCheck({
|
|
2334
2377
|
kind: "max",
|
|
2335
2378
|
value: maxDate.getTime(),
|
|
2336
|
-
message: errorUtil.toString(
|
|
2379
|
+
message: errorUtil.toString(message3)
|
|
2337
2380
|
});
|
|
2338
2381
|
}
|
|
2339
2382
|
get minDate() {
|
|
@@ -2565,26 +2608,26 @@ var init_types = __esm({
|
|
|
2565
2608
|
get element() {
|
|
2566
2609
|
return this._def.type;
|
|
2567
2610
|
}
|
|
2568
|
-
min(minLength,
|
|
2611
|
+
min(minLength, message3) {
|
|
2569
2612
|
return new _ZodArray({
|
|
2570
2613
|
...this._def,
|
|
2571
|
-
minLength: { value: minLength, message: errorUtil.toString(
|
|
2614
|
+
minLength: { value: minLength, message: errorUtil.toString(message3) }
|
|
2572
2615
|
});
|
|
2573
2616
|
}
|
|
2574
|
-
max(maxLength,
|
|
2617
|
+
max(maxLength, message3) {
|
|
2575
2618
|
return new _ZodArray({
|
|
2576
2619
|
...this._def,
|
|
2577
|
-
maxLength: { value: maxLength, message: errorUtil.toString(
|
|
2620
|
+
maxLength: { value: maxLength, message: errorUtil.toString(message3) }
|
|
2578
2621
|
});
|
|
2579
2622
|
}
|
|
2580
|
-
length(len,
|
|
2623
|
+
length(len, message3) {
|
|
2581
2624
|
return new _ZodArray({
|
|
2582
2625
|
...this._def,
|
|
2583
|
-
exactLength: { value: len, message: errorUtil.toString(
|
|
2626
|
+
exactLength: { value: len, message: errorUtil.toString(message3) }
|
|
2584
2627
|
});
|
|
2585
2628
|
}
|
|
2586
|
-
nonempty(
|
|
2587
|
-
return this.min(1,
|
|
2629
|
+
nonempty(message3) {
|
|
2630
|
+
return this.min(1, message3);
|
|
2588
2631
|
}
|
|
2589
2632
|
};
|
|
2590
2633
|
ZodArray.create = (schema, params) => {
|
|
@@ -2701,17 +2744,17 @@ var init_types = __esm({
|
|
|
2701
2744
|
get shape() {
|
|
2702
2745
|
return this._def.shape();
|
|
2703
2746
|
}
|
|
2704
|
-
strict(
|
|
2747
|
+
strict(message3) {
|
|
2705
2748
|
errorUtil.errToObj;
|
|
2706
2749
|
return new _ZodObject({
|
|
2707
2750
|
...this._def,
|
|
2708
2751
|
unknownKeys: "strict",
|
|
2709
|
-
...
|
|
2752
|
+
...message3 !== void 0 ? {
|
|
2710
2753
|
errorMap: (issue, ctx) => {
|
|
2711
2754
|
const defaultError = this._def.errorMap?.(issue, ctx).message ?? ctx.defaultError;
|
|
2712
2755
|
if (issue.code === "unrecognized_keys")
|
|
2713
2756
|
return {
|
|
2714
|
-
message: errorUtil.errToObj(
|
|
2757
|
+
message: errorUtil.errToObj(message3).message ?? defaultError
|
|
2715
2758
|
};
|
|
2716
2759
|
return {
|
|
2717
2760
|
message: defaultError
|
|
@@ -3429,23 +3472,23 @@ var init_types = __esm({
|
|
|
3429
3472
|
return finalizeSet(elements);
|
|
3430
3473
|
}
|
|
3431
3474
|
}
|
|
3432
|
-
min(minSize,
|
|
3475
|
+
min(minSize, message3) {
|
|
3433
3476
|
return new _ZodSet({
|
|
3434
3477
|
...this._def,
|
|
3435
|
-
minSize: { value: minSize, message: errorUtil.toString(
|
|
3478
|
+
minSize: { value: minSize, message: errorUtil.toString(message3) }
|
|
3436
3479
|
});
|
|
3437
3480
|
}
|
|
3438
|
-
max(maxSize,
|
|
3481
|
+
max(maxSize, message3) {
|
|
3439
3482
|
return new _ZodSet({
|
|
3440
3483
|
...this._def,
|
|
3441
|
-
maxSize: { value: maxSize, message: errorUtil.toString(
|
|
3484
|
+
maxSize: { value: maxSize, message: errorUtil.toString(message3) }
|
|
3442
3485
|
});
|
|
3443
3486
|
}
|
|
3444
|
-
size(size,
|
|
3445
|
-
return this.min(size,
|
|
3487
|
+
size(size, message3) {
|
|
3488
|
+
return this.min(size, message3).max(size, message3);
|
|
3446
3489
|
}
|
|
3447
|
-
nonempty(
|
|
3448
|
-
return this.min(1,
|
|
3490
|
+
nonempty(message3) {
|
|
3491
|
+
return this.min(1, message3);
|
|
3449
3492
|
}
|
|
3450
3493
|
};
|
|
3451
3494
|
ZodSet.create = (valueType, params) => {
|
|
@@ -4331,7 +4374,7 @@ var init_zod = __esm({
|
|
|
4331
4374
|
}
|
|
4332
4375
|
});
|
|
4333
4376
|
|
|
4334
|
-
// ../shared/dist/chunk-
|
|
4377
|
+
// ../shared/dist/chunk-VSUBG7GR.js
|
|
4335
4378
|
function formatModelId(provider, model) {
|
|
4336
4379
|
return `${provider}/${model}`;
|
|
4337
4380
|
}
|
|
@@ -4349,8 +4392,8 @@ function uniqueSortedArray(item, minimum = 0) {
|
|
|
4349
4392
|
}).transform((values) => [...values].sort());
|
|
4350
4393
|
}
|
|
4351
4394
|
var DEFAULT_SONNET_MODEL, DEFAULT_OPUS_MODEL, DEFAULT_HAIKU_MODEL, FABLE_MODEL, PREVIOUS_SONNET_MODEL, PREVIOUS_OPUS_MODEL, JEV_MODEL, JEV_MODEL_ID, JEV_ZEN_MODEL, JEV_ZEN_MODEL_ID, JEV_INPUT_PRICE_PER_TOKEN, JEV_STATE_MAX_CHARS, PTY_STREAM_PORT_BASE, PTY_STREAM_PORT_ATTEMPTS, PREVIEW_PORT_DENY_LIST, checkpointPathSchema, secretNameSchema, checkpointKeySchema, checkpointDigestRefSchema, ACTIONS_PREBAKE_REGISTRY_PATTERN, actionsPrebakeRegistrySchema, projectCheckpointSettingsSchema, CARD_DESCRIPTION_MAX, CARD_DESCRIPTION_LIMIT_MESSAGE, CARD_DESCRIPTION_FIELD_HINT, MAX_MANUAL_TESTS_PER_TASK, MAX_MANUAL_TEST_TITLE_LENGTH, ManualTestItemsSchema, MAX_CI_WAIT_TIMEOUT_MINUTES;
|
|
4352
|
-
var
|
|
4353
|
-
"../shared/dist/chunk-
|
|
4395
|
+
var init_chunk_VSUBG7GR = __esm({
|
|
4396
|
+
"../shared/dist/chunk-VSUBG7GR.js"() {
|
|
4354
4397
|
"use strict";
|
|
4355
4398
|
init_zod();
|
|
4356
4399
|
init_zod();
|
|
@@ -4476,11 +4519,11 @@ function localBakeSudoersCommands(k3sPath) {
|
|
|
4476
4519
|
return `${k3sPath} ctr ${command.args.join(" ")}${suffix}`;
|
|
4477
4520
|
});
|
|
4478
4521
|
}
|
|
4479
|
-
var ACHIEVEMENT_RARITIES, RISK_LEVELS, riskLevelSchema, DEFAULT_RISK_LEVELS, LEVEL_BY_VALUE, ACTIVE_WORK_STATUSES, IDENTIFIED_WORK_STATUSES, DEFAULT_TASK_STATUS_COLOR, DEFAULT_TASK_STATUS_COLOR_INT, MAX_FILE_SIZE_BYTES, MAX_FILE_TAGS, MAX_FILE_TAG_LENGTH, ALLOWED_FILE_MIME_TYPES, ALLOWED_FILE_ACCEPT, EMBED_THRESHOLD_IMAGES, EMBED_THRESHOLD_TEXT, IDLE_HEARTBEAT_MS, TurnEndToolCallSchema, AgentTurnUsageSchema, KnownAgentEventSchema, AgentEventSchema, PtyChatAtSchema, PtyChatTurnUsageSchema, PtyChatTurnFileSchema, PTY_CHAT_TURN_FILES_MAX, PtyChatEventPayloadSchema, PtyChatEventRequestSchema, PtyChatAttachRequestSchema, PTY_EFFORT_LEVELS, SetPtyModelEffortRequestSchema, cardDescription, AgentHeartbeatSchema, CreatePRInputSchema, PostToChatInputSchema, GetTaskContextRequestSchema, GetChatMessagesRequestSchema, GetTaskFilesRequestSchema, GetTaskFileRequestSchema, DeleteTaskFileRequestSchema, SetTaskFileShowcaseRequestSchema, GetTaskRequestSchema, GetCliHistoryRequestSchema, ListSubtasksRequestSchema, GetDependenciesRequestSchema, GetSuggestionsRequestSchema, ListManualTestsRequestSchema, QueryManualTestsRequestSchema, CreatePullRequestRequestSchema, RequestFileUploadRequestSchema, ConfirmFileUploadRequestSchema, UpdateTaskStatusRequestSchema, StoreSessionIdRequestSchema, SetManualTestsRequestSchema, EditManualTestRequestSchema, RemoveManualTestRequestSchema, ApproveManualTestRequestSchema, RejectManualTestRequestSchema, SessionStartRequestSchema, SessionStopRequestSchema, EndReviewSessionRequestSchema, ConnectAgentRequestSchema, ReportAgentStatusRequestSchema, NotifyAgentVersionRequestSchema, DiscoveredPortSchema, ReportDiscoveredPortsRequestSchema, ReportBootMilestoneRequestSchema, CreateSubtaskRequestSchema, UpdateSubtaskRequestSchema, DeleteSubtaskRequestSchema, SetSubtaskParentRequestSchema, GetTaskPropertiesRequestSchema, UpdateTaskFieldsRequestSchema, UpdateTaskPropertiesRequestSchema, ListIconsRequestSchema, GenerateTaskIconRequestSchema, SearchFaIconsRequestSchema, PickFaIconRequestSchema, CreateFollowUpTaskRequestSchema, AddDependencyRequestSchema, RemoveDependencyRequestSchema, CreateSuggestionRequestSchema, VoteSuggestionRequestSchema, TriggerIdentificationRequestSchema, HandoffToImplementerRequestSchema, SubmitCodeReviewResultRequestSchema, CycleCodingAgentKeyRequestSchema, PostChildChatMessageRequestSchema, UpdateChildStatusRequestSchema, GetAgentStatusRequestSchema, GetUiCliHistoryRequestSchema, GetActivePtySessionRequestSchema, ListActivePtySessionsRequestSchema, SendSoftStopRequestSchema, StopTaskSessionRequestSchema, FlushTaskQueueRequestSchema, CancelTaskQueuedMessageRequestSchema, FlushSingleQueuedMessageRequestSchema, AnswerAgentQuestionRequestSchema, ClearAgentTodosRequestSchema, AgentQuestionOptionSchema, AgentQuestionSchema, AskUserQuestionRequestSchema, PostAgentMessageRequestSchema, EmitAgentEventRequestSchema, RefreshGithubTokenRequestSchema, ReportCredentialFailureRequestSchema, ReportReviewSpawnFailureRequestSchema, ReportBuilderSpawnFailureRequestSchema, SpawnTaskSessionRequestSchema, StartCodeReviewRequestSchema, StopCodeReviewRequestSchema, ReportSessionSpawnFailureRequestSchema, RefreshGithubTokenResponseSchema, PTY_FRAME_MAX_CHARS, PTY_MAX_DIMENSION, PtyOutputRequestSchema, PtyEndedRequestSchema, PtyInputRequestSchema, PtyResizeRequestSchema, PtyAttachRequestSchema, ReportPtyStreamRequestSchema, ReportPtyInputRequestSchema, GetPtyStreamEndpointRequestSchema, CreatePRResponseSchema, PostToChatResponseSchema, UpdateTaskStatusResponseSchema, StoreSessionIdResponseSchema, HeartbeatResponseSchema, SessionStartResponseSchema, SessionStopResponseSchema, DeleteSubtaskResponseSchema, ParkOnCheckResultRequestSchema, GIT_BRANCH_NAME_MAX, GIT_BRANCH_NAME_MESSAGE, ALLOWED_REF, cardDescription2, ListAccessibleProjectsRequestSchema, ListProjectTasksRequestSchema, GetProjectTaskRequestSchema, SearchProjectTasksRequestSchema, ListProjectTagsRequestSchema, GetProjectTagRequestSchema, ListProjectTagAttachmentsRequestSchema, SetProjectFileTagsRequestSchema, GetProjectSummaryRequestSchema, GetProjectOnboardingStatusRequestSchema, GetProjectOnboardingStepRequestSchema, GetProjectConnectUrlsRequestSchema, conveyorCapabilitySchema, GetConnectionContextRequestSchema, VerifyConnectionRequestSchema, ListAccessibleSubprojectsRequestSchema, CreateProjectTaskRequestSchema, SetProjectTaskParentRequestSchema, UpdateProjectTaskRequestSchema, TransitionProjectTaskStatusRequestSchema, MoveProjectCardRequestSchema, PostToProjectTaskChatRequestSchema, GetProjectTaskCliRequestSchema, GetProjectTaskSessionsRequestSchema, QueryProjectGcpLogsRequestSchema, QueryProjectGrafanaLogsRequestSchema, driveFileNameSchema, DRIVE_MAX_CONTENT_CHARS, ListProjectDriveFilesRequestSchema, ReadProjectDriveFileRequestSchema, CreateProjectDriveFileRequestSchema, UpdateProjectDriveFileRequestSchema, DeleteProjectDriveFileRequestSchema, CreateProjectDriveFolderRequestSchema, StartProjectBuildRequestSchema, StopProjectBuildRequestSchema, StartProjectWorkspaceRequestSchema, StopProjectWorkspaceRequestSchema, ListMyLiveSessionsRequestSchema, ListProjectSessionGroupsRequestSchema, ListMyLiveSessionsAcrossProjectsRequestSchema, ListSessionGroupsAcrossProjectsRequestSchema, GetProjectAvailableTuisRequestSchema, StartAdhocSessionRequestSchema, StopAdhocSessionRequestSchema, ResumeAdhocSessionRequestSchema, RefreshCodingAgentKeyUsageRequestSchema, ListKeysToProbeRequestSchema, CreateProjectReleaseRequestSchema, AddTasksToProjectReleaseRequestSchema, ApproveProjectMergePRRequestSchema, ListProjectSubtasksRequestSchema, CreateProjectSubtaskRequestSchema, UpdateProjectSubtaskRequestSchema, DeleteProjectSubtaskRequestSchema, GetProjectTaskChatRequestSchema, AddProjectTaskDependencyRequestSchema, RemoveProjectTaskDependencyRequestSchema, VoteProjectSuggestionRequestSchema, GetProjectTaskDependenciesRequestSchema, CreateProjectPullRequestRequestSchema, ListProjectMembersRequestSchema, AddProjectTaskReviewerRequestSchema, RemoveProjectTaskReviewerRequestSchema, ListProjectManualTestsRequestSchema, QueryProjectManualTestsRequestSchema, SetProjectManualTestsRequestSchema, EditProjectManualTestRequestSchema, RemoveProjectManualTestRequestSchema, ApproveProjectManualTestRequestSchema, RejectProjectManualTestRequestSchema, CreateProjectSuggestionRequestSchema, ListProjectChannelsRequestSchema, READ_CHANNEL_MESSAGES_MAX_LIMIT, ReadChannelMessagesRequestSchema, POST_CHANNEL_MESSAGE_MAX_CHARS, PostChannelMessageRequestSchema, GET_ANALYTICS_SUMMARY_MAX_RANGE_DAYS, GetProjectAnalyticsSummaryRequestSchema, ListProjectTaskFilesRequestSchema, GetProjectAttachmentRequestSchema, DeleteProjectTaskFileRequestSchema, SetProjectTaskFileShowcaseRequestSchema, RequestProjectFileUploadRequestSchema, ConfirmProjectFileUploadRequestSchema, RequestWorkspaceRecycleRequestSchema, ReportApiOutageRequestSchema, CONTEXT_LINK_LOCATOR_MAX, TAG_DESCRIPTION_MAX, TAG_OVERVIEW_MAX, TAG_REASON_MAX, ProjectTagContextPathSchema, hexColor, overviewPathSchema, CreateProjectTagRequestSchema, UpdateProjectTagRequestSchema, PostToProjectChatRequestSchema, StartTagAuditRequestSchema, StartTaskAuditRequestSchema, GetActiveAuditSessionsRequestSchema, ReportTaskAuditResultRequestSchema, GetTaskAuditsRequestSchema, GetTaskAuditRequestSchema, GetTaskAuditAggregatesRequestSchema, DeleteTaskAuditRequestSchema, MarkInitialPromptSubmittedRequestSchema, PERSONAL_CLUSTER_EVENTS, PERSONAL_CLUSTER_HEARTBEAT_INTERVAL_MS, DECISION_STATUS_LABELS, DECISION_OPTION_KINDS, DECISION_VOTE_SOURCES, DECISION_OPTIONS_MIN, DECISION_OPTIONS_MAX, DECISION_OPTION_LABEL_MAX, DECISION_QUESTION_MAX, DECISION_RESOLUTION_MAX, DECISION_VOTE_REASON_MAX, DECISION_DEADLINE_MIN_HOURS, DECISION_DEADLINE_MAX_DAYS, DECISION_DEADLINE_MAX_HOURS, DECISION_LIST_MAX_LIMIT, decisionIdSchema, optionInputSchema, optionsArraySchema, CreateDecisionRequestSchema, VoteDecisionRequestSchema, RemoveDecisionVoteRequestSchema, ResolveDecisionRequestSchema, CancelDecisionRequestSchema, BulkDecisionIdsRequestSchema, ReopenDecisionRequestSchema, UpdateDecisionRequestSchema, GetDecisionRequestSchema, ListDecisionsRequestSchema, optionInput, optionsInput, deadlineHours, CreateProjectDecisionRequestSchema, ListProjectDecisionsRequestSchema, GetProjectDecisionRequestSchema, VoteProjectDecisionRequestSchema, ResolveProjectDecisionRequestSchema, UpdateProjectDecisionRequestSchema, EVALUATE_MAX_QUESTIONS, EVALUATE_MAX_ITEMS, EVALUATE_ITEM_MAX_CHARS, EVALUATE_REQUEST_MAX_CHARS, TEXT_MAX, QUESTION_KEY, EvaluateQuestionSchema, EvaluateQuestionsRequestSchema, MEETING_CHECKLIST_TITLE_MAX, MEETING_TRANSCRIPT_MAX_CHARS, MEETING_TITLE_MAX, MEETING_OCCURRED_AT_MIN_YEAR, MEETING_OCCURRED_AT_MAX_FUTURE_MS, OCCURRED_AT_RANGE_MESSAGE, MeetingOccurredAtSchema, CreateMeetingFromTranscriptRequestSchema, GetMeetingRequestSchema, UpdateMeetingRequestSchema, RegenerateMeetingSummaryRequestSchema, DeleteMeetingRequestSchema, checklistTitle, ListMeetingChecklistRequestSchema, AddMeetingChecklistItemsRequestSchema, UpdateMeetingChecklistItemRequestSchema, DeleteMeetingChecklistItemRequestSchema, SetMeetingChecklistItemCheckedRequestSchema, ListMeetingsRequestSchema, ReadMeetingTranscriptRequestSchema, MEETING_SUMMARY_MAX_CHARS, AddProjectMeetingChecklistItemsRequestSchema, CheckProjectMeetingChecklistItemRequestSchema, EditProjectMeetingChecklistItemRequestSchema, RemoveProjectMeetingChecklistItemRequestSchema, CreateProjectMeetingRequestSchema, UpdateProjectMeetingRequestSchema, TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT, AGENT_CHAT_HISTORY_FETCH_LIMIT, ANTHROPIC_CATALOG, CLAUDESPACE_WORKLOAD_LABEL, CLAUDESPACE_WORKLOAD_VALUE, CONVEYOR_POD_SELECTOR, CONTAINERD_K8S_NAMESPACE, CONTAINERD_PIN_LABEL, CONVEYOR_CONTENT_HASH_LABEL, VM_IMPORT_DISCOVERY_DIR, LOCAL_BAKE_ROOT_COMMANDS, PERSONAL_LOCAL_REGISTRY_NAMESPACE, PERSONAL_LOCAL_REGISTRY_NAME, PERSONAL_LOCAL_REGISTRY_PORT, PERSONAL_LOCAL_REGISTRY_HOST, PERSONAL_LOCAL_REGISTRY_NODE_ENDPOINT, BAKE_COMPONENT_LABEL, BAKE_COMPONENT_PREBAKE, BAKE_COMPONENT_STATE_BAKE, BAKE_COMPONENT_VALUES, MS_PER_DAY, POSTGRES_ENV, FIREBASE_EMULATOR_COMMAND, FIREBASE_EMULATOR_ENV, CATALOG, SERVICE_DEFINITIONS, SUPPORTED_CLAUDESPACE_DEPS, STATEFUL_BAKE_DEPS, MIRRORABLE_SIDECARS, LEVELS_PER_BAND, PRESTIGE_TIERS, TOP_PRESTIGE_BAND, CARD_TYPE_SURFACE, surfaceTypes, BOARD_CARD_TYPES, REPORT_CARD_TYPES, DECISION_CARD_TYPES;
|
|
4522
|
+
var ACHIEVEMENT_RARITIES, RISK_LEVELS, riskLevelSchema, DEFAULT_RISK_LEVELS, LEVEL_BY_VALUE, ACTIVE_WORK_STATUSES, IDENTIFIED_WORK_STATUSES, DEFAULT_TASK_STATUS_COLOR, DEFAULT_TASK_STATUS_COLOR_INT, MAX_FILE_SIZE_BYTES, MAX_FILE_TAGS, MAX_FILE_TAG_LENGTH, ALLOWED_FILE_MIME_TYPES, ALLOWED_FILE_ACCEPT, EMBED_THRESHOLD_IMAGES, EMBED_THRESHOLD_TEXT, IDLE_HEARTBEAT_MS, TurnEndToolCallSchema, AgentTurnUsageSchema, KnownAgentEventSchema, AgentEventSchema, PtyChatAtSchema, PtyChatTurnUsageSchema, PtyChatTurnFileSchema, PTY_CHAT_TURN_FILES_MAX, PtyChatEventPayloadSchema, PtyChatEventRequestSchema, PtyChatAttachRequestSchema, PTY_EFFORT_LEVELS, SetPtyModelEffortRequestSchema, cardDescription, AgentHeartbeatSchema, CreatePRInputSchema, PostToChatInputSchema, GetTaskContextRequestSchema, GetChatMessagesRequestSchema, GetTaskFilesRequestSchema, GetTaskFileRequestSchema, DeleteTaskFileRequestSchema, SetTaskFileShowcaseRequestSchema, GetTaskRequestSchema, GetCliHistoryRequestSchema, ListSubtasksRequestSchema, GetDependenciesRequestSchema, GetSuggestionsRequestSchema, ListManualTestsRequestSchema, QueryManualTestsRequestSchema, CreatePullRequestRequestSchema, RequestFileUploadRequestSchema, ConfirmFileUploadRequestSchema, UpdateTaskStatusRequestSchema, StoreSessionIdRequestSchema, SetManualTestsRequestSchema, EditManualTestRequestSchema, RemoveManualTestRequestSchema, ApproveManualTestRequestSchema, RejectManualTestRequestSchema, SessionStartRequestSchema, SessionStopRequestSchema, EndReviewSessionRequestSchema, ConnectAgentRequestSchema, ReportAgentStatusRequestSchema, NotifyAgentVersionRequestSchema, DiscoveredPortSchema, ReportDiscoveredPortsRequestSchema, ReportBootMilestoneRequestSchema, CreateSubtaskRequestSchema, UpdateSubtaskRequestSchema, DeleteSubtaskRequestSchema, SetSubtaskParentRequestSchema, GetTaskPropertiesRequestSchema, UpdateTaskFieldsRequestSchema, UpdateTaskPropertiesRequestSchema, ListIconsRequestSchema, GenerateTaskIconRequestSchema, SearchFaIconsRequestSchema, PickFaIconRequestSchema, CreateFollowUpTaskRequestSchema, AddDependencyRequestSchema, RemoveDependencyRequestSchema, CreateSuggestionRequestSchema, VoteSuggestionRequestSchema, TriggerIdentificationRequestSchema, HandoffToImplementerRequestSchema, SubmitCodeReviewResultRequestSchema, CycleCodingAgentKeyRequestSchema, PostChildChatMessageRequestSchema, UpdateChildStatusRequestSchema, GetAgentStatusRequestSchema, GetUiCliHistoryRequestSchema, GetActivePtySessionRequestSchema, ListActivePtySessionsRequestSchema, SendSoftStopRequestSchema, StopTaskSessionRequestSchema, FlushTaskQueueRequestSchema, CancelTaskQueuedMessageRequestSchema, FlushSingleQueuedMessageRequestSchema, AnswerAgentQuestionRequestSchema, ClearAgentTodosRequestSchema, AgentQuestionOptionSchema, AgentQuestionSchema, AskUserQuestionRequestSchema, PostAgentMessageRequestSchema, EmitAgentEventRequestSchema, RefreshGithubTokenRequestSchema, ReportCredentialFailureRequestSchema, ReportReviewSpawnFailureRequestSchema, ReportBuilderSpawnFailureRequestSchema, SpawnTaskSessionRequestSchema, StartCodeReviewRequestSchema, StopCodeReviewRequestSchema, ReportSessionSpawnFailureRequestSchema, RefreshGithubTokenResponseSchema, PTY_FRAME_MAX_CHARS, PTY_MAX_DIMENSION, PtyOutputRequestSchema, PtyEndedRequestSchema, PtyInputRequestSchema, PtyResizeRequestSchema, PtyAttachRequestSchema, ReportPtyStreamRequestSchema, ReportPtyInputRequestSchema, GetPtyStreamEndpointRequestSchema, CreatePRResponseSchema, PostToChatResponseSchema, UpdateTaskStatusResponseSchema, StoreSessionIdResponseSchema, HeartbeatResponseSchema, SessionStartResponseSchema, SessionStopResponseSchema, DeleteSubtaskResponseSchema, ParkOnCheckResultRequestSchema, GIT_BRANCH_NAME_MAX, GIT_BRANCH_NAME_MESSAGE, ALLOWED_REF, cardDescription2, ListAccessibleProjectsRequestSchema, ListProjectTasksRequestSchema, GetProjectTaskRequestSchema, SearchProjectTasksRequestSchema, ListProjectTagsRequestSchema, GetProjectTagRequestSchema, ListProjectTagAttachmentsRequestSchema, SetProjectFileTagsRequestSchema, GetProjectSummaryRequestSchema, GetProjectOnboardingStatusRequestSchema, GetProjectOnboardingStepRequestSchema, GetProjectConnectUrlsRequestSchema, conveyorCapabilitySchema, GetConnectionContextRequestSchema, VerifyConnectionRequestSchema, ListAccessibleSubprojectsRequestSchema, CreateProjectTaskRequestSchema, SetProjectTaskParentRequestSchema, UpdateProjectTaskRequestSchema, TransitionProjectTaskStatusRequestSchema, MoveProjectCardRequestSchema, PostToProjectTaskChatRequestSchema, GetProjectTaskCliRequestSchema, GetProjectTaskSessionsRequestSchema, QueryProjectGcpLogsRequestSchema, QueryProjectGrafanaLogsRequestSchema, driveFileNameSchema, DRIVE_MAX_CONTENT_CHARS, ListProjectDriveFilesRequestSchema, ReadProjectDriveFileRequestSchema, CreateProjectDriveFileRequestSchema, UpdateProjectDriveFileRequestSchema, DeleteProjectDriveFileRequestSchema, CreateProjectDriveFolderRequestSchema, StartProjectBuildRequestSchema, StopProjectBuildRequestSchema, StartProjectWorkspaceRequestSchema, StopProjectWorkspaceRequestSchema, ListMyLiveSessionsRequestSchema, ListProjectSessionGroupsRequestSchema, ListMyLiveSessionsAcrossProjectsRequestSchema, ListSessionGroupsAcrossProjectsRequestSchema, GetProjectAvailableTuisRequestSchema, StartAdhocSessionRequestSchema, StopAdhocSessionRequestSchema, ResumeAdhocSessionRequestSchema, RefreshCodingAgentKeyUsageRequestSchema, ListKeysToProbeRequestSchema, CreateProjectReleaseRequestSchema, AddTasksToProjectReleaseRequestSchema, ApproveProjectMergePRRequestSchema, ListProjectSubtasksRequestSchema, CreateProjectSubtaskRequestSchema, UpdateProjectSubtaskRequestSchema, DeleteProjectSubtaskRequestSchema, GetProjectTaskChatRequestSchema, AddProjectTaskDependencyRequestSchema, RemoveProjectTaskDependencyRequestSchema, VoteProjectSuggestionRequestSchema, GetProjectTaskDependenciesRequestSchema, CreateProjectPullRequestRequestSchema, ListProjectMembersRequestSchema, AddProjectTaskReviewerRequestSchema, RemoveProjectTaskReviewerRequestSchema, ListProjectManualTestsRequestSchema, QueryProjectManualTestsRequestSchema, SetProjectManualTestsRequestSchema, EditProjectManualTestRequestSchema, RemoveProjectManualTestRequestSchema, ApproveProjectManualTestRequestSchema, RejectProjectManualTestRequestSchema, CreateProjectSuggestionRequestSchema, ListProjectChannelsRequestSchema, READ_CHANNEL_MESSAGES_MAX_LIMIT, ReadChannelMessagesRequestSchema, POST_CHANNEL_MESSAGE_MAX_CHARS, PostChannelMessageRequestSchema, GET_ANALYTICS_SUMMARY_MAX_RANGE_DAYS, GetProjectAnalyticsSummaryRequestSchema, ListProjectTaskFilesRequestSchema, GetProjectAttachmentRequestSchema, DeleteProjectTaskFileRequestSchema, SetProjectTaskFileShowcaseRequestSchema, RequestProjectFileUploadRequestSchema, ConfirmProjectFileUploadRequestSchema, RequestWorkspaceRecycleRequestSchema, ReportApiOutageRequestSchema, CONTEXT_LINK_LOCATOR_MAX, TAG_DESCRIPTION_MAX, TAG_OVERVIEW_MAX, TAG_REASON_MAX, ProjectTagContextPathSchema, hexColor, overviewPathSchema, CreateProjectTagRequestSchema, UpdateProjectTagRequestSchema, PostToProjectChatRequestSchema, StartTagAuditRequestSchema, StartTaskAuditRequestSchema, GetActiveAuditSessionsRequestSchema, ReportTaskAuditResultRequestSchema, GetTaskAuditsRequestSchema, GetTaskAuditRequestSchema, GetTaskAuditAggregatesRequestSchema, DeleteTaskAuditRequestSchema, MarkInitialPromptSubmittedRequestSchema, PERSONAL_CLUSTER_EVENTS, PERSONAL_CLUSTER_HEARTBEAT_INTERVAL_MS, DECISION_STATUS_LABELS, DECISION_OPTION_KINDS, DECISION_VOTE_SOURCES, DECISION_OPTIONS_MIN, DECISION_OPTIONS_MAX, DECISION_OPTION_LABEL_MAX, DECISION_QUESTION_MAX, DECISION_RESOLUTION_MAX, DECISION_VOTE_REASON_MAX, DECISION_DEADLINE_MIN_HOURS, DECISION_DEADLINE_MAX_DAYS, DECISION_DEADLINE_MAX_HOURS, DECISION_LIST_MAX_LIMIT, decisionIdSchema, optionInputSchema, optionsArraySchema, CreateDecisionRequestSchema, VoteDecisionRequestSchema, RemoveDecisionVoteRequestSchema, ResolveDecisionRequestSchema, CancelDecisionRequestSchema, BulkDecisionIdsRequestSchema, ReopenDecisionRequestSchema, UpdateDecisionRequestSchema, GetDecisionRequestSchema, ListDecisionsRequestSchema, optionInput, optionsInput, deadlineHours, CreateProjectDecisionRequestSchema, ListProjectDecisionsRequestSchema, GetProjectDecisionRequestSchema, VoteProjectDecisionRequestSchema, ResolveProjectDecisionRequestSchema, UpdateProjectDecisionRequestSchema, EVALUATE_MAX_QUESTIONS, EVALUATE_MAX_ITEMS, EVALUATE_ITEM_MAX_CHARS, EVALUATE_REQUEST_MAX_CHARS, TEXT_MAX, QUESTION_KEY, EvaluateQuestionSchema, EvaluateQuestionsRequestSchema, MEETING_CHECKLIST_TITLE_MAX, MEETING_TRANSCRIPT_MAX_CHARS, MEETING_TITLE_MAX, MEETING_OCCURRED_AT_MIN_YEAR, MEETING_OCCURRED_AT_MAX_FUTURE_MS, OCCURRED_AT_RANGE_MESSAGE, MeetingOccurredAtSchema, CreateMeetingFromTranscriptRequestSchema, GetMeetingRequestSchema, UpdateMeetingRequestSchema, RegenerateMeetingSummaryRequestSchema, DeleteMeetingRequestSchema, checklistTitle, ListMeetingChecklistRequestSchema, AddMeetingChecklistItemsRequestSchema, UpdateMeetingChecklistItemRequestSchema, DeleteMeetingChecklistItemRequestSchema, SetMeetingChecklistItemCheckedRequestSchema, ListMeetingsRequestSchema, ReadMeetingTranscriptRequestSchema, MEETING_SUMMARY_MAX_CHARS, AddProjectMeetingChecklistItemsRequestSchema, CheckProjectMeetingChecklistItemRequestSchema, EditProjectMeetingChecklistItemRequestSchema, RemoveProjectMeetingChecklistItemRequestSchema, CreateProjectMeetingRequestSchema, UpdateProjectMeetingRequestSchema, TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT, AGENT_CHAT_HISTORY_FETCH_LIMIT, ANTHROPIC_CATALOG, CLAUDESPACE_WORKLOAD_LABEL, CLAUDESPACE_WORKLOAD_VALUE, CONVEYOR_POD_SELECTOR, CONTAINERD_K8S_NAMESPACE, CONTAINERD_PIN_LABEL, CONVEYOR_CONTENT_HASH_LABEL, VM_IMPORT_DISCOVERY_DIR, LOCAL_BAKE_ROOT_COMMANDS, PERSONAL_LOCAL_REGISTRY_NAMESPACE, PERSONAL_LOCAL_REGISTRY_NAME, PERSONAL_LOCAL_REGISTRY_PORT, PERSONAL_LOCAL_REGISTRY_HOST, PERSONAL_LOCAL_REGISTRY_NODE_ENDPOINT, BAKE_COMPONENT_LABEL, BAKE_COMPONENT_PREBAKE, BAKE_COMPONENT_STATE_BAKE, BAKE_COMPONENT_VALUES, PERSONAL_BAKE_NAMESPACE, MS_PER_DAY, POSTGRES_ENV, FIREBASE_EMULATOR_COMMAND, FIREBASE_EMULATOR_ENV, CATALOG, SERVICE_DEFINITIONS, SUPPORTED_CLAUDESPACE_DEPS, STATEFUL_BAKE_DEPS, MIRRORABLE_SIDECARS, LEVELS_PER_BAND, PRESTIGE_TIERS, TOP_PRESTIGE_BAND, CARD_TYPE_SURFACE, surfaceTypes, BOARD_CARD_TYPES, REPORT_CARD_TYPES, DECISION_CARD_TYPES;
|
|
4480
4523
|
var init_dist = __esm({
|
|
4481
4524
|
"../shared/dist/index.js"() {
|
|
4482
4525
|
"use strict";
|
|
4483
|
-
|
|
4526
|
+
init_chunk_VSUBG7GR();
|
|
4484
4527
|
init_zod();
|
|
4485
4528
|
init_zod();
|
|
4486
4529
|
init_zod();
|
|
@@ -5026,7 +5069,8 @@ var init_dist = __esm({
|
|
|
5026
5069
|
port: external_exports.number().int().min(1).max(65535),
|
|
5027
5070
|
label: external_exports.string().min(1).max(64).optional(),
|
|
5028
5071
|
protocol: external_exports.enum(["http", "tcp"]).optional(),
|
|
5029
|
-
detectedAt: external_exports.string()
|
|
5072
|
+
detectedAt: external_exports.string(),
|
|
5073
|
+
hidden: external_exports.boolean().optional()
|
|
5030
5074
|
});
|
|
5031
5075
|
ReportDiscoveredPortsRequestSchema = external_exports.object({
|
|
5032
5076
|
sessionId: external_exports.string(),
|
|
@@ -5173,7 +5217,11 @@ var init_dist = __esm({
|
|
|
5173
5217
|
CycleCodingAgentKeyRequestSchema = external_exports.object({
|
|
5174
5218
|
sessionId: external_exports.string(),
|
|
5175
5219
|
rateLimitType: external_exports.string(),
|
|
5176
|
-
resetsAt: external_exports.string().optional()
|
|
5220
|
+
resetsAt: external_exports.string().optional(),
|
|
5221
|
+
// The key the pod is actually running (CONVEYOR_CODING_AGENT_KEY_ID). Wins
|
|
5222
|
+
// over the session's attribution stamp after owner validation; optional so
|
|
5223
|
+
// older agents keep the session fallback.
|
|
5224
|
+
codingAgentKeyId: external_exports.string().optional()
|
|
5177
5225
|
});
|
|
5178
5226
|
PostChildChatMessageRequestSchema = external_exports.object({
|
|
5179
5227
|
sessionId: external_exports.string(),
|
|
@@ -5578,11 +5626,12 @@ var init_dist = __esm({
|
|
|
5578
5626
|
sqlInstances: external_exports.array(external_exports.string().min(1).max(200)).max(25).optional(),
|
|
5579
5627
|
allServices: external_exports.boolean().optional(),
|
|
5580
5628
|
search: external_exports.string().max(256).optional(),
|
|
5581
|
-
filter: external_exports.string().max(
|
|
5629
|
+
filter: external_exports.string().max(2e3).optional(),
|
|
5582
5630
|
startTime: external_exports.string().optional(),
|
|
5583
5631
|
endTime: external_exports.string().optional(),
|
|
5584
5632
|
limit: external_exports.number().int().min(1).max(200).optional().default(50),
|
|
5585
|
-
pageToken: external_exports.string().max(4096).optional()
|
|
5633
|
+
pageToken: external_exports.string().max(4096).optional(),
|
|
5634
|
+
full: external_exports.boolean().optional()
|
|
5586
5635
|
});
|
|
5587
5636
|
QueryProjectGrafanaLogsRequestSchema = external_exports.object({
|
|
5588
5637
|
projectId: external_exports.string(),
|
|
@@ -6511,6 +6560,7 @@ var init_dist = __esm({
|
|
|
6511
6560
|
BAKE_COMPONENT_PREBAKE = "prebake";
|
|
6512
6561
|
BAKE_COMPONENT_STATE_BAKE = "state-bake";
|
|
6513
6562
|
BAKE_COMPONENT_VALUES = [BAKE_COMPONENT_PREBAKE, BAKE_COMPONENT_STATE_BAKE];
|
|
6563
|
+
PERSONAL_BAKE_NAMESPACE = "conveyor-bakes";
|
|
6514
6564
|
MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
6515
6565
|
POSTGRES_ENV = {
|
|
6516
6566
|
POSTGRES_HOST_AUTH_METHOD: "trust",
|
|
@@ -6830,48 +6880,6 @@ exec firebase emulators:start --only=auth --project=rally-cry-dev`
|
|
|
6830
6880
|
}
|
|
6831
6881
|
});
|
|
6832
6882
|
|
|
6833
|
-
// src/lib/journald.ts
|
|
6834
|
-
function renderJournaldDropIn() {
|
|
6835
|
-
return [
|
|
6836
|
-
"# Written by conveyor-k3. Caps the journal so kubelet and container logs",
|
|
6837
|
-
"# cannot fill the disk the image store needs. Removed by uninstall.",
|
|
6838
|
-
"[Journal]",
|
|
6839
|
-
"SystemMaxUse=1G",
|
|
6840
|
-
"SystemKeepFree=2G",
|
|
6841
|
-
"MaxRetentionSec=14day",
|
|
6842
|
-
""
|
|
6843
|
-
].join("\n");
|
|
6844
|
-
}
|
|
6845
|
-
async function ensureJournaldCap(ports) {
|
|
6846
|
-
const content = renderJournaldDropIn();
|
|
6847
|
-
if (await ports.fs.readFile(JOURNALD_DROPIN) === content) return false;
|
|
6848
|
-
await ports.fs.writeFile(JOURNALD_DROPIN, content, 420);
|
|
6849
|
-
await runOrThrow(ports.exec, "systemctl", ["restart", "systemd-journald"], {
|
|
6850
|
-
timeoutMs: 6e4
|
|
6851
|
-
});
|
|
6852
|
-
return true;
|
|
6853
|
-
}
|
|
6854
|
-
function journaldCapScript() {
|
|
6855
|
-
const encoded = Buffer.from(renderJournaldDropIn(), "utf8").toString("base64");
|
|
6856
|
-
return [
|
|
6857
|
-
"set -eu",
|
|
6858
|
-
`dropin=${JOURNALD_DROPIN}`,
|
|
6859
|
-
'tmp="$(mktemp)"',
|
|
6860
|
-
`trap 'rm -f "$tmp"' EXIT`,
|
|
6861
|
-
`printf '%s' '${encoded}' | base64 -d > "$tmp"`,
|
|
6862
|
-
'if cmp -s "$tmp" "$dropin"; then exit 0; fi',
|
|
6863
|
-
'install -D -m 0644 "$tmp" "$dropin"',
|
|
6864
|
-
"systemctl restart systemd-journald"
|
|
6865
|
-
].join("\n");
|
|
6866
|
-
}
|
|
6867
|
-
var init_journald = __esm({
|
|
6868
|
-
"src/lib/journald.ts"() {
|
|
6869
|
-
"use strict";
|
|
6870
|
-
init_exec();
|
|
6871
|
-
init_paths();
|
|
6872
|
-
}
|
|
6873
|
-
});
|
|
6874
|
-
|
|
6875
6883
|
// src/vm/capacity.ts
|
|
6876
6884
|
function inBounds(key, value) {
|
|
6877
6885
|
const { min, max } = CAPACITY_BOUNDS[key];
|
|
@@ -6975,117 +6983,357 @@ var init_capacity = __esm({
|
|
|
6975
6983
|
}
|
|
6976
6984
|
});
|
|
6977
6985
|
|
|
6978
|
-
// src/
|
|
6979
|
-
function
|
|
6980
|
-
return
|
|
6986
|
+
// src/lib/journald.ts
|
|
6987
|
+
function renderJournaldDropIn() {
|
|
6988
|
+
return [
|
|
6989
|
+
"# Written by conveyor-k3. Caps the journal so kubelet and container logs",
|
|
6990
|
+
"# cannot fill the disk the image store needs. Removed by uninstall.",
|
|
6991
|
+
"[Journal]",
|
|
6992
|
+
"SystemMaxUse=1G",
|
|
6993
|
+
"SystemKeepFree=2G",
|
|
6994
|
+
"MaxRetentionSec=14day",
|
|
6995
|
+
""
|
|
6996
|
+
].join("\n");
|
|
6981
6997
|
}
|
|
6982
|
-
function
|
|
6983
|
-
|
|
6984
|
-
if (
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
);
|
|
6991
|
-
return value;
|
|
6998
|
+
async function ensureJournaldCap(ports) {
|
|
6999
|
+
const content = renderJournaldDropIn();
|
|
7000
|
+
if (await ports.fs.readFile(JOURNALD_DROPIN) === content) return false;
|
|
7001
|
+
await ports.fs.writeFile(JOURNALD_DROPIN, content, 420);
|
|
7002
|
+
await runOrThrow(ports.exec, "systemctl", ["restart", "systemd-journald"], {
|
|
7003
|
+
timeoutMs: 6e4
|
|
7004
|
+
});
|
|
7005
|
+
return true;
|
|
6992
7006
|
}
|
|
6993
|
-
function
|
|
6994
|
-
|
|
7007
|
+
function journaldCapScript() {
|
|
7008
|
+
const encoded = Buffer.from(renderJournaldDropIn(), "utf8").toString("base64");
|
|
7009
|
+
return [
|
|
7010
|
+
"set -eu",
|
|
7011
|
+
`dropin=${JOURNALD_DROPIN}`,
|
|
7012
|
+
'tmp="$(mktemp)"',
|
|
7013
|
+
`trap 'rm -f "$tmp"' EXIT`,
|
|
7014
|
+
`printf '%s' '${encoded}' | base64 -d > "$tmp"`,
|
|
7015
|
+
'if cmp -s "$tmp" "$dropin"; then exit 0; fi',
|
|
7016
|
+
'install -D -m 0644 "$tmp" "$dropin"',
|
|
7017
|
+
"systemctl restart systemd-journald"
|
|
7018
|
+
].join("\n");
|
|
6995
7019
|
}
|
|
6996
|
-
var
|
|
6997
|
-
|
|
6998
|
-
"src/vm/instance-name.ts"() {
|
|
7020
|
+
var init_journald = __esm({
|
|
7021
|
+
"src/lib/journald.ts"() {
|
|
6999
7022
|
"use strict";
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
NAMED_INSTANCE = /^(?=.{1,12}$)[a-z](?:-?[a-z0-9])*$/;
|
|
7023
|
+
init_exec();
|
|
7024
|
+
init_paths();
|
|
7003
7025
|
}
|
|
7004
7026
|
});
|
|
7005
7027
|
|
|
7006
|
-
// src/vm/
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
function verifiedLink(state) {
|
|
7012
|
-
return state.enrolled && (state.linkVerified ?? !!(state.linkedAccountLabel || state.linkedDisplayName));
|
|
7013
|
-
}
|
|
7014
|
-
function vmProfile() {
|
|
7015
|
-
const profile = process.env.CONVEYOR_VM_TEST_PROFILE;
|
|
7016
|
-
if (profile !== void 0 && !/^acceptance-[a-z0-9-]{1,32}$/.test(profile)) {
|
|
7017
|
-
throw new Error("Test VM profiles must match acceptance-[a-z0-9-]{1,32}.");
|
|
7018
|
-
}
|
|
7019
|
-
return profile;
|
|
7028
|
+
// src/vm/device-link.ts
|
|
7029
|
+
function isTransientPairingError(error) {
|
|
7030
|
+
if (error instanceof DevicePairingRequestError)
|
|
7031
|
+
return error.status === 429 || error.status !== void 0 && error.status >= 500;
|
|
7032
|
+
return error instanceof Error && /timed out|network|fetch failed/i.test(error.message);
|
|
7020
7033
|
}
|
|
7021
|
-
function
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
"share",
|
|
7027
|
-
profile ? `conveyor-k3-${profile}-vms` : "conveyor-k3-vms"
|
|
7028
|
-
);
|
|
7034
|
+
function validPublicLabel(value) {
|
|
7035
|
+
return typeof value === "string" && value.length > 0 && value.length <= 160 && [...value].every((character) => {
|
|
7036
|
+
const code = character.charCodeAt(0);
|
|
7037
|
+
return code >= 32 && code !== 127;
|
|
7038
|
+
});
|
|
7029
7039
|
}
|
|
7030
|
-
function
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
const profile = vmProfile();
|
|
7034
|
-
return pathsForRoot(
|
|
7035
|
-
join(homedir(), ".local", "share", profile ? `conveyor-k3-${profile}` : "conveyor-k3-vm")
|
|
7040
|
+
function validDevicePublicKey(value) {
|
|
7041
|
+
return typeof value === "string" && Buffer.byteLength(value) <= 1024 && /^-----BEGIN PUBLIC KEY-----\n(?:[A-Za-z0-9+/=]{1,128}\n)+-----END PUBLIC KEY-----\n?$/.test(
|
|
7042
|
+
value
|
|
7036
7043
|
);
|
|
7037
7044
|
}
|
|
7038
|
-
function
|
|
7039
|
-
const
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
wrapper: join(root, "qemu-wrapper"),
|
|
7048
|
-
wrapperJs: join(root, "vm-qemu.js"),
|
|
7049
|
-
applied: join(root, "applied-network.json"),
|
|
7050
|
-
guard: join(root, "guard-state.json"),
|
|
7051
|
-
startRequest: join(root, "start-request.json"),
|
|
7052
|
-
policyTransaction: join(root, "policy-transaction.json"),
|
|
7053
|
-
lock: join(root, "operation.lock"),
|
|
7054
|
-
/** Written by an owner stop, so a supervising service does not restart the VM. */
|
|
7055
|
-
stopIntent: join(root, "stop-intent.json"),
|
|
7056
|
-
instance,
|
|
7057
|
-
vmName,
|
|
7058
|
-
instanceDir,
|
|
7059
|
-
limaConfig: join(instanceDir, "lima.yaml")
|
|
7060
|
-
};
|
|
7045
|
+
function devOrigin() {
|
|
7046
|
+
const value = process.env.CONVEYOR_DEVICE_LINK_DEV_ORIGIN;
|
|
7047
|
+
if (!value) return void 0;
|
|
7048
|
+
const url = new URL(value);
|
|
7049
|
+
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
|
|
7050
|
+
throw new Error("CONVEYOR_DEVICE_LINK_DEV_ORIGIN must be an origin.");
|
|
7051
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && url.hostname === "localhost"))
|
|
7052
|
+
throw new Error("The explicit development pairing origin must use HTTPS or localhost HTTP.");
|
|
7053
|
+
return url.origin;
|
|
7061
7054
|
}
|
|
7062
|
-
function
|
|
7063
|
-
|
|
7055
|
+
function devWebOrigin() {
|
|
7056
|
+
const value = process.env.CONVEYOR_DEVICE_LINK_DEV_WEB_ORIGIN;
|
|
7057
|
+
if (!value) return void 0;
|
|
7058
|
+
const url = new URL(value);
|
|
7059
|
+
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
|
|
7060
|
+
throw new Error("CONVEYOR_DEVICE_LINK_DEV_WEB_ORIGIN must be an origin.");
|
|
7061
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && url.hostname === "localhost"))
|
|
7062
|
+
throw new Error("The explicit development browser origin must use HTTPS or localhost HTTP.");
|
|
7063
|
+
return url.origin;
|
|
7064
7064
|
}
|
|
7065
|
-
|
|
7066
|
-
const
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
)
|
|
7073
|
-
|
|
7074
|
-
return editing.some(Boolean);
|
|
7065
|
+
function trustedDeviceApiUrl(value) {
|
|
7066
|
+
const requested = new URL(value ?? PRODUCTION_API);
|
|
7067
|
+
const development = devOrigin();
|
|
7068
|
+
const allowed = /* @__PURE__ */ new Set([PRODUCTION_API, ...development ? [development] : []]);
|
|
7069
|
+
if (requested.pathname !== "/" || requested.search || requested.hash || !allowed.has(requested.origin))
|
|
7070
|
+
throw new Error(
|
|
7071
|
+
"Pairing API must be the production origin or the explicitly configured development origin."
|
|
7072
|
+
);
|
|
7073
|
+
return requested.origin;
|
|
7075
7074
|
}
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7075
|
+
function validVerificationUri(value) {
|
|
7076
|
+
if (typeof value !== "string") return false;
|
|
7077
|
+
try {
|
|
7078
|
+
const url = new URL(value);
|
|
7079
|
+
const development = devWebOrigin();
|
|
7080
|
+
const allowed = /* @__PURE__ */ new Set([PRODUCTION_WEB, ...development ? [development] : []]);
|
|
7081
|
+
return allowed.has(url.origin) && url.pathname === "/personal-compute/link" && url.searchParams.size === 1 && /^[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$/.test(url.searchParams.get("code") ?? "") && !url.username && !url.password && !url.hash;
|
|
7082
|
+
} catch {
|
|
7083
|
+
return false;
|
|
7081
7084
|
}
|
|
7082
|
-
await chmod2(path, 448);
|
|
7083
7085
|
}
|
|
7084
|
-
async function
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7086
|
+
async function responseJson(response) {
|
|
7087
|
+
const length = Number(response.headers.get("content-length"));
|
|
7088
|
+
if (Number.isFinite(length) && (length < 0 || length > MAX_RESPONSE_BYTES))
|
|
7089
|
+
throw new Error("Pairing API response is too large.");
|
|
7090
|
+
if (!response.body) throw new Error("Pairing API returned an empty response.");
|
|
7091
|
+
const reader = response.body.getReader();
|
|
7092
|
+
const chunks = [];
|
|
7093
|
+
let size = 0;
|
|
7094
|
+
try {
|
|
7095
|
+
while (true) {
|
|
7096
|
+
const next = await reader.read();
|
|
7097
|
+
if (next.done) break;
|
|
7098
|
+
size += next.value.byteLength;
|
|
7099
|
+
if (size > MAX_RESPONSE_BYTES) throw new Error("Pairing API response is too large.");
|
|
7100
|
+
chunks.push(next.value);
|
|
7101
|
+
}
|
|
7102
|
+
} finally {
|
|
7103
|
+
reader.releaseLock();
|
|
7104
|
+
}
|
|
7105
|
+
const bytes = new Uint8Array(size);
|
|
7106
|
+
let offset = 0;
|
|
7107
|
+
for (const chunk of chunks) {
|
|
7108
|
+
bytes.set(chunk, offset);
|
|
7109
|
+
offset += chunk.byteLength;
|
|
7110
|
+
}
|
|
7111
|
+
try {
|
|
7112
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
7113
|
+
} catch {
|
|
7114
|
+
throw new Error("Pairing API returned invalid JSON.");
|
|
7115
|
+
}
|
|
7116
|
+
}
|
|
7117
|
+
async function request(api, path, body, signal) {
|
|
7118
|
+
const timeout = AbortSignal.timeout(15e3);
|
|
7119
|
+
const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
7120
|
+
let response;
|
|
7121
|
+
try {
|
|
7122
|
+
response = await fetch(`${api}${path}`, {
|
|
7123
|
+
method: "POST",
|
|
7124
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
7125
|
+
body: JSON.stringify(body),
|
|
7126
|
+
redirect: "error",
|
|
7127
|
+
signal: combined
|
|
7128
|
+
});
|
|
7129
|
+
} catch (error) {
|
|
7130
|
+
if (combined.aborted)
|
|
7131
|
+
throw new Error("Pairing request timed out or was cancelled.", { cause: error });
|
|
7132
|
+
throw error;
|
|
7133
|
+
}
|
|
7134
|
+
if (!response.ok) {
|
|
7135
|
+
const failure = await responseJson(response).catch(() => null);
|
|
7136
|
+
const detail2 = typeof failure?.error === "string" && failure.error.length <= 300 && [...failure.error].every(
|
|
7137
|
+
(character) => character.charCodeAt(0) >= 32 && character.charCodeAt(0) !== 127
|
|
7138
|
+
) ? failure.error : void 0;
|
|
7139
|
+
throw new DevicePairingRequestError(
|
|
7140
|
+
detail2 ?? `Pairing request failed (${response.status}).`,
|
|
7141
|
+
response.status
|
|
7142
|
+
);
|
|
7143
|
+
}
|
|
7144
|
+
return responseJson(response);
|
|
7145
|
+
}
|
|
7146
|
+
async function startPairing(api, name2, devicePublicKey, signal, options) {
|
|
7147
|
+
if (!validDevicePublicKey(devicePublicKey))
|
|
7148
|
+
throw new Error("Guest device public key is invalid.");
|
|
7149
|
+
const value = await request(
|
|
7150
|
+
api,
|
|
7151
|
+
"/api/personal-compute/device/start",
|
|
7152
|
+
{
|
|
7153
|
+
name: name2,
|
|
7154
|
+
platform: process.platform,
|
|
7155
|
+
arch: process.arch,
|
|
7156
|
+
devicePublicKey,
|
|
7157
|
+
...options?.runtime ? { runtime: options.runtime } : {}
|
|
7158
|
+
},
|
|
7159
|
+
signal
|
|
7160
|
+
);
|
|
7161
|
+
const interval = value.interval;
|
|
7162
|
+
if (typeof value.deviceCode !== "string" || value.deviceCode.length < 24 || typeof value.userCode !== "string" || !/^[A-Z0-9-]{4,32}$/.test(value.userCode) || !validVerificationUri(value.verificationUri) || typeof value.expiresAt !== "string" || typeof interval !== "number" || !Number.isInteger(interval) || interval < 5 || interval > 60)
|
|
7163
|
+
throw new Error("Pairing API returned an invalid authorization response.");
|
|
7164
|
+
return value;
|
|
7165
|
+
}
|
|
7166
|
+
async function pollPairing(api, deviceCode, signal) {
|
|
7167
|
+
const value = await request(
|
|
7168
|
+
api,
|
|
7169
|
+
"/api/personal-compute/device/poll",
|
|
7170
|
+
{ deviceCode },
|
|
7171
|
+
signal
|
|
7172
|
+
);
|
|
7173
|
+
if (!["pending", "approved", "completed", "denied", "expired", "cancelled"].includes(
|
|
7174
|
+
value.status ?? ""
|
|
7175
|
+
))
|
|
7176
|
+
throw new Error("Pairing API returned an invalid status.");
|
|
7177
|
+
if (value.status === "approved" && (typeof value.bootstrapScript !== "string" || value.bootstrapScript.length > 1e6))
|
|
7178
|
+
throw new Error("Approved pairing did not include a safe bootstrap payload.");
|
|
7179
|
+
if (value.accountLabel !== void 0 && !validPublicLabel(value.accountLabel) || value.displayName !== void 0 && !validPublicLabel(value.displayName))
|
|
7180
|
+
throw new Error("Pairing API returned invalid public device metadata.");
|
|
7181
|
+
return value;
|
|
7182
|
+
}
|
|
7183
|
+
async function cancelPairing(api, deviceCode, signal) {
|
|
7184
|
+
await request(api, "/api/personal-compute/device/cancel", { deviceCode }, signal);
|
|
7185
|
+
}
|
|
7186
|
+
function pairingExpired(expiresAt) {
|
|
7187
|
+
const time = Date.parse(expiresAt);
|
|
7188
|
+
return !Number.isFinite(time) || time <= Date.now();
|
|
7189
|
+
}
|
|
7190
|
+
async function pollUntilResolved(poll, expiresAt, intervalSeconds, retry, sleep4 = (milliseconds) => new Promise((resolve) => {
|
|
7191
|
+
setTimeout(resolve, milliseconds);
|
|
7192
|
+
}), accept = (result) => result.status !== "pending") {
|
|
7193
|
+
let delay2 = intervalSeconds * 1e3;
|
|
7194
|
+
while (!pairingExpired(expiresAt)) {
|
|
7195
|
+
try {
|
|
7196
|
+
const result = await poll();
|
|
7197
|
+
delay2 = intervalSeconds * 1e3;
|
|
7198
|
+
if (accept(result)) return result;
|
|
7199
|
+
} catch (error) {
|
|
7200
|
+
if (!isTransientPairingError(error)) throw error;
|
|
7201
|
+
retry();
|
|
7202
|
+
delay2 = Math.min(delay2 * 2, 3e4);
|
|
7203
|
+
}
|
|
7204
|
+
await sleep4(delay2);
|
|
7205
|
+
}
|
|
7206
|
+
throw new Error("Pairing expired. Start a new link request.");
|
|
7207
|
+
}
|
|
7208
|
+
var PRODUCTION_API, PRODUCTION_WEB, MAX_RESPONSE_BYTES, DevicePairingRequestError;
|
|
7209
|
+
var init_device_link = __esm({
|
|
7210
|
+
"src/vm/device-link.ts"() {
|
|
7211
|
+
"use strict";
|
|
7212
|
+
PRODUCTION_API = "https://api.conveyor.rallycryapp.com";
|
|
7213
|
+
PRODUCTION_WEB = "https://conveyor.rallycryapp.com";
|
|
7214
|
+
MAX_RESPONSE_BYTES = 64 * 1024;
|
|
7215
|
+
DevicePairingRequestError = class extends Error {
|
|
7216
|
+
constructor(message3, status, options) {
|
|
7217
|
+
super(message3, options);
|
|
7218
|
+
this.status = status;
|
|
7219
|
+
this.name = "DevicePairingRequestError";
|
|
7220
|
+
}
|
|
7221
|
+
status;
|
|
7222
|
+
};
|
|
7223
|
+
}
|
|
7224
|
+
});
|
|
7225
|
+
|
|
7226
|
+
// src/vm/instance-name.ts
|
|
7227
|
+
function isNamedInstance(value) {
|
|
7228
|
+
return typeof value === "string" && NAMED_INSTANCE.test(value) && value !== DEFAULT_INSTANCE && value !== VM_NAME;
|
|
7229
|
+
}
|
|
7230
|
+
function parseInstanceName(value) {
|
|
7231
|
+
if (value === void 0 || value === DEFAULT_INSTANCE) return DEFAULT_INSTANCE;
|
|
7232
|
+
if (value === true) throw new Error("Use --instance=<name>.");
|
|
7233
|
+
if (value === VM_NAME)
|
|
7234
|
+
throw new Error(`"${VM_NAME}" is reserved for the default VM. Omit --instance to use it.`);
|
|
7235
|
+
if (!isNamedInstance(value))
|
|
7236
|
+
throw new Error(
|
|
7237
|
+
"Use an instance name of 1-12 lowercase letters, digits, or single hyphens, starting with a letter."
|
|
7238
|
+
);
|
|
7239
|
+
return value;
|
|
7240
|
+
}
|
|
7241
|
+
function vmNameFor(instance) {
|
|
7242
|
+
return instance === DEFAULT_INSTANCE ? VM_NAME : instance;
|
|
7243
|
+
}
|
|
7244
|
+
var VM_NAME, DEFAULT_INSTANCE, NAMED_INSTANCE;
|
|
7245
|
+
var init_instance_name = __esm({
|
|
7246
|
+
"src/vm/instance-name.ts"() {
|
|
7247
|
+
"use strict";
|
|
7248
|
+
VM_NAME = "conveyor";
|
|
7249
|
+
DEFAULT_INSTANCE = "default";
|
|
7250
|
+
NAMED_INSTANCE = /^(?=.{1,12}$)[a-z](?:-?[a-z0-9])*$/;
|
|
7251
|
+
}
|
|
7252
|
+
});
|
|
7253
|
+
|
|
7254
|
+
// src/vm/host-state.ts
|
|
7255
|
+
import { access, chmod as chmod3, lstat, mkdir as mkdir4, readFile as readFile2, rename as rename2, rm as rm3, writeFile as writeFile4 } from "fs/promises";
|
|
7256
|
+
import { homedir, uptime } from "os";
|
|
7257
|
+
import { join } from "path";
|
|
7258
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
7259
|
+
function verifiedLink(state) {
|
|
7260
|
+
return state.enrolled && (state.linkVerified ?? !!(state.linkedAccountLabel || state.linkedDisplayName));
|
|
7261
|
+
}
|
|
7262
|
+
function vmProfile() {
|
|
7263
|
+
const profile = process.env.CONVEYOR_VM_TEST_PROFILE;
|
|
7264
|
+
if (profile !== void 0 && !/^acceptance-[a-z0-9-]{1,32}$/.test(profile)) {
|
|
7265
|
+
throw new Error("Test VM profiles must match acceptance-[a-z0-9-]{1,32}.");
|
|
7266
|
+
}
|
|
7267
|
+
return profile;
|
|
7268
|
+
}
|
|
7269
|
+
function instancesBase() {
|
|
7270
|
+
const profile = vmProfile();
|
|
7271
|
+
return join(
|
|
7272
|
+
homedir(),
|
|
7273
|
+
".local",
|
|
7274
|
+
"share",
|
|
7275
|
+
profile ? `conveyor-k3-${profile}-vms` : "conveyor-k3-vms"
|
|
7276
|
+
);
|
|
7277
|
+
}
|
|
7278
|
+
function hostPaths(instance = DEFAULT_INSTANCE) {
|
|
7279
|
+
const name2 = parseInstanceName(instance);
|
|
7280
|
+
if (name2 !== DEFAULT_INSTANCE) return pathsForRoot(join(instancesBase(), name2), name2);
|
|
7281
|
+
const profile = vmProfile();
|
|
7282
|
+
return pathsForRoot(
|
|
7283
|
+
join(homedir(), ".local", "share", profile ? `conveyor-k3-${profile}` : "conveyor-k3-vm")
|
|
7284
|
+
);
|
|
7285
|
+
}
|
|
7286
|
+
function pathsForRoot(root, instance = DEFAULT_INSTANCE) {
|
|
7287
|
+
const vmName = vmNameFor(parseInstanceName(instance));
|
|
7288
|
+
const instanceDir = join(root, "lima", vmName);
|
|
7289
|
+
return {
|
|
7290
|
+
root,
|
|
7291
|
+
limaHome: join(root, "lima"),
|
|
7292
|
+
state: join(root, "state.json"),
|
|
7293
|
+
network: join(root, "network.json"),
|
|
7294
|
+
template: join(root, "conveyor.yaml"),
|
|
7295
|
+
wrapper: join(root, "qemu-wrapper"),
|
|
7296
|
+
wrapperJs: join(root, "vm-qemu.js"),
|
|
7297
|
+
applied: join(root, "applied-network.json"),
|
|
7298
|
+
guard: join(root, "guard-state.json"),
|
|
7299
|
+
startRequest: join(root, "start-request.json"),
|
|
7300
|
+
policyTransaction: join(root, "policy-transaction.json"),
|
|
7301
|
+
lock: join(root, "operation.lock"),
|
|
7302
|
+
/** Written by an owner stop, so a supervising service does not restart the VM. */
|
|
7303
|
+
stopIntent: join(root, "stop-intent.json"),
|
|
7304
|
+
instance,
|
|
7305
|
+
vmName,
|
|
7306
|
+
instanceDir,
|
|
7307
|
+
limaConfig: join(instanceDir, "lima.yaml")
|
|
7308
|
+
};
|
|
7309
|
+
}
|
|
7310
|
+
function missingVm(paths, defaultCopy = "No Conveyor VM exists. Run conveyor-k3 vm setup first.") {
|
|
7311
|
+
return paths.instance === DEFAULT_INSTANCE ? defaultCopy : `No Conveyor VM named ${paths.instance} exists. Run conveyor-k3 vm setup --instance=${paths.instance} first.`;
|
|
7312
|
+
}
|
|
7313
|
+
async function vmOperationInProgress(paths) {
|
|
7314
|
+
const editing = await Promise.all(
|
|
7315
|
+
[paths.lock, paths.policyTransaction].map(
|
|
7316
|
+
(path) => access(path).then(
|
|
7317
|
+
() => true,
|
|
7318
|
+
() => false
|
|
7319
|
+
)
|
|
7320
|
+
)
|
|
7321
|
+
);
|
|
7322
|
+
return editing.some(Boolean);
|
|
7323
|
+
}
|
|
7324
|
+
async function privateDirectory(path) {
|
|
7325
|
+
await mkdir4(path, { recursive: true, mode: 448 });
|
|
7326
|
+
const stat2 = await lstat(path);
|
|
7327
|
+
if (!stat2.isDirectory() || stat2.isSymbolicLink() || stat2.uid !== process.getuid?.()) {
|
|
7328
|
+
throw new Error(`Refusing unsafe VM directory: ${path}`);
|
|
7329
|
+
}
|
|
7330
|
+
await chmod3(path, 448);
|
|
7331
|
+
}
|
|
7332
|
+
async function atomicPrivateWrite(path, content, mode = 384) {
|
|
7333
|
+
await assertSafeExistingFile(path);
|
|
7334
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
7335
|
+
await writeFile4(temporary, content, { mode, flag: "wx" });
|
|
7336
|
+
try {
|
|
7089
7337
|
await rename2(temporary, path);
|
|
7090
7338
|
} finally {
|
|
7091
7339
|
await rm3(temporary, { force: true });
|
|
@@ -7212,13 +7460,13 @@ async function reclaimStaleLock(paths, options) {
|
|
|
7212
7460
|
}
|
|
7213
7461
|
async function acquireHostLock(paths, options) {
|
|
7214
7462
|
try {
|
|
7215
|
-
await
|
|
7463
|
+
await mkdir4(paths.lock, { mode: 448 });
|
|
7216
7464
|
return true;
|
|
7217
7465
|
} catch (error) {
|
|
7218
7466
|
if (error.code !== "EEXIST") throw error;
|
|
7219
7467
|
if (!options.reclaimStale) return false;
|
|
7220
7468
|
await reclaimStaleLock(paths, options);
|
|
7221
|
-
return await
|
|
7469
|
+
return await mkdir4(paths.lock, { mode: 448 }).then(
|
|
7222
7470
|
() => true,
|
|
7223
7471
|
() => false
|
|
7224
7472
|
);
|
|
@@ -7235,7 +7483,7 @@ async function withHostLock(paths, fn, options = {}) {
|
|
|
7235
7483
|
await sleep(500, void 0, { signal: options.signal });
|
|
7236
7484
|
}
|
|
7237
7485
|
try {
|
|
7238
|
-
await
|
|
7486
|
+
await writeFile4(join(paths.lock, "pid"), String(process.pid), { mode: 384 });
|
|
7239
7487
|
return await fn();
|
|
7240
7488
|
} finally {
|
|
7241
7489
|
await rm3(paths.lock, { recursive: true, force: true });
|
|
@@ -7913,7 +8161,7 @@ var init_host_packages = __esm({
|
|
|
7913
8161
|
import { constants } from "fs";
|
|
7914
8162
|
import { access as access2, statfs as statfs2 } from "fs/promises";
|
|
7915
8163
|
import { userInfo } from "os";
|
|
7916
|
-
import { dirname, join as join4 } from "path";
|
|
8164
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
7917
8165
|
function realHost() {
|
|
7918
8166
|
return {
|
|
7919
8167
|
platform: process.platform,
|
|
@@ -7985,11 +8233,11 @@ async function assertLimaVersion(exec, paths, lima, host) {
|
|
|
7985
8233
|
}
|
|
7986
8234
|
async function findFirmware(qemu, arch, host, os) {
|
|
7987
8235
|
const candidates = arch === "arm64" ? [
|
|
7988
|
-
join4(
|
|
8236
|
+
join4(dirname2(qemu), "../share/qemu/edk2-aarch64-code.fd"),
|
|
7989
8237
|
"/usr/share/AAVMF/AAVMF_CODE.fd",
|
|
7990
8238
|
"/usr/share/qemu-efi-aarch64/QEMU_EFI.fd"
|
|
7991
8239
|
] : [
|
|
7992
|
-
join4(
|
|
8240
|
+
join4(dirname2(qemu), "../share/qemu/edk2-x86_64-code.fd"),
|
|
7993
8241
|
"/usr/share/edk2/x64/OVMF_CODE.4m.fd",
|
|
7994
8242
|
"/usr/share/OVMF/OVMF_CODE_4M.fd",
|
|
7995
8243
|
"/usr/share/OVMF/OVMF_CODE.fd"
|
|
@@ -9352,208 +9600,11 @@ var init_dev_artifact = __esm({
|
|
|
9352
9600
|
}
|
|
9353
9601
|
});
|
|
9354
9602
|
|
|
9355
|
-
// src/vm/
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
return
|
|
9360
|
-
}
|
|
9361
|
-
function validPublicLabel(value) {
|
|
9362
|
-
return typeof value === "string" && value.length > 0 && value.length <= 160 && [...value].every((character) => {
|
|
9363
|
-
const code = character.charCodeAt(0);
|
|
9364
|
-
return code >= 32 && code !== 127;
|
|
9365
|
-
});
|
|
9366
|
-
}
|
|
9367
|
-
function validDevicePublicKey(value) {
|
|
9368
|
-
return typeof value === "string" && Buffer.byteLength(value) <= 1024 && /^-----BEGIN PUBLIC KEY-----\n(?:[A-Za-z0-9+/=]{1,128}\n)+-----END PUBLIC KEY-----\n?$/.test(
|
|
9369
|
-
value
|
|
9370
|
-
);
|
|
9371
|
-
}
|
|
9372
|
-
function devOrigin() {
|
|
9373
|
-
const value = process.env.CONVEYOR_DEVICE_LINK_DEV_ORIGIN;
|
|
9374
|
-
if (!value) return void 0;
|
|
9375
|
-
const url = new URL(value);
|
|
9376
|
-
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
|
|
9377
|
-
throw new Error("CONVEYOR_DEVICE_LINK_DEV_ORIGIN must be an origin.");
|
|
9378
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && url.hostname === "localhost"))
|
|
9379
|
-
throw new Error("The explicit development pairing origin must use HTTPS or localhost HTTP.");
|
|
9380
|
-
return url.origin;
|
|
9381
|
-
}
|
|
9382
|
-
function devWebOrigin() {
|
|
9383
|
-
const value = process.env.CONVEYOR_DEVICE_LINK_DEV_WEB_ORIGIN;
|
|
9384
|
-
if (!value) return void 0;
|
|
9385
|
-
const url = new URL(value);
|
|
9386
|
-
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
|
|
9387
|
-
throw new Error("CONVEYOR_DEVICE_LINK_DEV_WEB_ORIGIN must be an origin.");
|
|
9388
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && url.hostname === "localhost"))
|
|
9389
|
-
throw new Error("The explicit development browser origin must use HTTPS or localhost HTTP.");
|
|
9390
|
-
return url.origin;
|
|
9391
|
-
}
|
|
9392
|
-
function trustedDeviceApiUrl(value) {
|
|
9393
|
-
const requested = new URL(value ?? PRODUCTION_API);
|
|
9394
|
-
const development = devOrigin();
|
|
9395
|
-
const allowed = /* @__PURE__ */ new Set([PRODUCTION_API, ...development ? [development] : []]);
|
|
9396
|
-
if (requested.pathname !== "/" || requested.search || requested.hash || !allowed.has(requested.origin))
|
|
9397
|
-
throw new Error(
|
|
9398
|
-
"Pairing API must be the production origin or the explicitly configured development origin."
|
|
9399
|
-
);
|
|
9400
|
-
return requested.origin;
|
|
9401
|
-
}
|
|
9402
|
-
function validVerificationUri(value) {
|
|
9403
|
-
if (typeof value !== "string") return false;
|
|
9404
|
-
try {
|
|
9405
|
-
const url = new URL(value);
|
|
9406
|
-
const development = devWebOrigin();
|
|
9407
|
-
const allowed = /* @__PURE__ */ new Set([PRODUCTION_WEB, ...development ? [development] : []]);
|
|
9408
|
-
return allowed.has(url.origin) && url.pathname === "/personal-compute/link" && url.searchParams.size === 1 && /^[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$/.test(url.searchParams.get("code") ?? "") && !url.username && !url.password && !url.hash;
|
|
9409
|
-
} catch {
|
|
9410
|
-
return false;
|
|
9411
|
-
}
|
|
9412
|
-
}
|
|
9413
|
-
async function responseJson(response) {
|
|
9414
|
-
const length = Number(response.headers.get("content-length"));
|
|
9415
|
-
if (Number.isFinite(length) && (length < 0 || length > MAX_RESPONSE_BYTES))
|
|
9416
|
-
throw new Error("Pairing API response is too large.");
|
|
9417
|
-
if (!response.body) throw new Error("Pairing API returned an empty response.");
|
|
9418
|
-
const reader = response.body.getReader();
|
|
9419
|
-
const chunks = [];
|
|
9420
|
-
let size = 0;
|
|
9421
|
-
try {
|
|
9422
|
-
while (true) {
|
|
9423
|
-
const next = await reader.read();
|
|
9424
|
-
if (next.done) break;
|
|
9425
|
-
size += next.value.byteLength;
|
|
9426
|
-
if (size > MAX_RESPONSE_BYTES) throw new Error("Pairing API response is too large.");
|
|
9427
|
-
chunks.push(next.value);
|
|
9428
|
-
}
|
|
9429
|
-
} finally {
|
|
9430
|
-
reader.releaseLock();
|
|
9431
|
-
}
|
|
9432
|
-
const bytes = new Uint8Array(size);
|
|
9433
|
-
let offset = 0;
|
|
9434
|
-
for (const chunk of chunks) {
|
|
9435
|
-
bytes.set(chunk, offset);
|
|
9436
|
-
offset += chunk.byteLength;
|
|
9437
|
-
}
|
|
9438
|
-
try {
|
|
9439
|
-
return JSON.parse(new TextDecoder().decode(bytes));
|
|
9440
|
-
} catch {
|
|
9441
|
-
throw new Error("Pairing API returned invalid JSON.");
|
|
9442
|
-
}
|
|
9443
|
-
}
|
|
9444
|
-
async function request(api, path, body, signal) {
|
|
9445
|
-
const timeout = AbortSignal.timeout(15e3);
|
|
9446
|
-
const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
9447
|
-
let response;
|
|
9448
|
-
try {
|
|
9449
|
-
response = await fetch(`${api}${path}`, {
|
|
9450
|
-
method: "POST",
|
|
9451
|
-
headers: { "content-type": "application/json", accept: "application/json" },
|
|
9452
|
-
body: JSON.stringify(body),
|
|
9453
|
-
redirect: "error",
|
|
9454
|
-
signal: combined
|
|
9455
|
-
});
|
|
9456
|
-
} catch (error) {
|
|
9457
|
-
if (combined.aborted)
|
|
9458
|
-
throw new Error("Pairing request timed out or was cancelled.", { cause: error });
|
|
9459
|
-
throw error;
|
|
9460
|
-
}
|
|
9461
|
-
if (!response.ok) {
|
|
9462
|
-
const failure = await responseJson(response).catch(() => null);
|
|
9463
|
-
const detail2 = typeof failure?.error === "string" && failure.error.length <= 300 && [...failure.error].every(
|
|
9464
|
-
(character) => character.charCodeAt(0) >= 32 && character.charCodeAt(0) !== 127
|
|
9465
|
-
) ? failure.error : void 0;
|
|
9466
|
-
throw new DevicePairingRequestError(
|
|
9467
|
-
detail2 ?? `Pairing request failed (${response.status}).`,
|
|
9468
|
-
response.status
|
|
9469
|
-
);
|
|
9470
|
-
}
|
|
9471
|
-
return responseJson(response);
|
|
9472
|
-
}
|
|
9473
|
-
async function startPairing(api, name2, devicePublicKey, signal) {
|
|
9474
|
-
if (!validDevicePublicKey(devicePublicKey))
|
|
9475
|
-
throw new Error("Guest device public key is invalid.");
|
|
9476
|
-
const value = await request(
|
|
9477
|
-
api,
|
|
9478
|
-
"/api/personal-compute/device/start",
|
|
9479
|
-
{
|
|
9480
|
-
name: name2,
|
|
9481
|
-
platform: process.platform,
|
|
9482
|
-
arch: process.arch,
|
|
9483
|
-
devicePublicKey
|
|
9484
|
-
},
|
|
9485
|
-
signal
|
|
9486
|
-
);
|
|
9487
|
-
const interval = value.interval;
|
|
9488
|
-
if (typeof value.deviceCode !== "string" || value.deviceCode.length < 24 || typeof value.userCode !== "string" || !/^[A-Z0-9-]{4,32}$/.test(value.userCode) || !validVerificationUri(value.verificationUri) || typeof value.expiresAt !== "string" || typeof interval !== "number" || !Number.isInteger(interval) || interval < 5 || interval > 60)
|
|
9489
|
-
throw new Error("Pairing API returned an invalid authorization response.");
|
|
9490
|
-
return value;
|
|
9491
|
-
}
|
|
9492
|
-
async function pollPairing(api, deviceCode, signal) {
|
|
9493
|
-
const value = await request(
|
|
9494
|
-
api,
|
|
9495
|
-
"/api/personal-compute/device/poll",
|
|
9496
|
-
{ deviceCode },
|
|
9497
|
-
signal
|
|
9498
|
-
);
|
|
9499
|
-
if (!["pending", "approved", "completed", "denied", "expired", "cancelled"].includes(
|
|
9500
|
-
value.status ?? ""
|
|
9501
|
-
))
|
|
9502
|
-
throw new Error("Pairing API returned an invalid status.");
|
|
9503
|
-
if (value.status === "approved" && (typeof value.bootstrapScript !== "string" || value.bootstrapScript.length > 1e6))
|
|
9504
|
-
throw new Error("Approved pairing did not include a safe bootstrap payload.");
|
|
9505
|
-
if (value.accountLabel !== void 0 && !validPublicLabel(value.accountLabel) || value.displayName !== void 0 && !validPublicLabel(value.displayName))
|
|
9506
|
-
throw new Error("Pairing API returned invalid public device metadata.");
|
|
9507
|
-
return value;
|
|
9508
|
-
}
|
|
9509
|
-
async function cancelPairing(api, deviceCode, signal) {
|
|
9510
|
-
await request(api, "/api/personal-compute/device/cancel", { deviceCode }, signal);
|
|
9511
|
-
}
|
|
9512
|
-
function pairingExpired(expiresAt) {
|
|
9513
|
-
const time = Date.parse(expiresAt);
|
|
9514
|
-
return !Number.isFinite(time) || time <= Date.now();
|
|
9515
|
-
}
|
|
9516
|
-
async function pollUntilResolved(poll, expiresAt, intervalSeconds, retry, sleep4 = (milliseconds) => new Promise((resolve) => {
|
|
9517
|
-
setTimeout(resolve, milliseconds);
|
|
9518
|
-
}), accept = (result) => result.status !== "pending") {
|
|
9519
|
-
let delay2 = intervalSeconds * 1e3;
|
|
9520
|
-
while (!pairingExpired(expiresAt)) {
|
|
9521
|
-
try {
|
|
9522
|
-
const result = await poll();
|
|
9523
|
-
delay2 = intervalSeconds * 1e3;
|
|
9524
|
-
if (accept(result)) return result;
|
|
9525
|
-
} catch (error) {
|
|
9526
|
-
if (!isTransientPairingError(error)) throw error;
|
|
9527
|
-
retry();
|
|
9528
|
-
delay2 = Math.min(delay2 * 2, 3e4);
|
|
9529
|
-
}
|
|
9530
|
-
await sleep4(delay2);
|
|
9531
|
-
}
|
|
9532
|
-
throw new Error("Pairing expired. Start a new link request.");
|
|
9533
|
-
}
|
|
9534
|
-
var PRODUCTION_API, PRODUCTION_WEB, MAX_RESPONSE_BYTES, DevicePairingRequestError;
|
|
9535
|
-
var init_device_link = __esm({
|
|
9536
|
-
"src/vm/device-link.ts"() {
|
|
9537
|
-
"use strict";
|
|
9538
|
-
PRODUCTION_API = "https://api.conveyor.rallycryapp.com";
|
|
9539
|
-
PRODUCTION_WEB = "https://conveyor.rallycryapp.com";
|
|
9540
|
-
MAX_RESPONSE_BYTES = 64 * 1024;
|
|
9541
|
-
DevicePairingRequestError = class extends Error {
|
|
9542
|
-
constructor(message2, status, options) {
|
|
9543
|
-
super(message2, options);
|
|
9544
|
-
this.status = status;
|
|
9545
|
-
this.name = "DevicePairingRequestError";
|
|
9546
|
-
}
|
|
9547
|
-
status;
|
|
9548
|
-
};
|
|
9549
|
-
}
|
|
9550
|
-
});
|
|
9551
|
-
|
|
9552
|
-
// src/vm/instances.ts
|
|
9553
|
-
import { lstat as lstat2, readdir } from "fs/promises";
|
|
9554
|
-
import { join as join7 } from "path";
|
|
9555
|
-
function instanceLayout() {
|
|
9556
|
-
return { defaultRoot: hostPaths().root, base: instancesBase() };
|
|
9603
|
+
// src/vm/instances.ts
|
|
9604
|
+
import { lstat as lstat2, readdir } from "fs/promises";
|
|
9605
|
+
import { join as join7 } from "path";
|
|
9606
|
+
function instanceLayout() {
|
|
9607
|
+
return { defaultRoot: hostPaths().root, base: instancesBase() };
|
|
9557
9608
|
}
|
|
9558
9609
|
function instancePaths(layout, instance) {
|
|
9559
9610
|
return instance === DEFAULT_INSTANCE ? pathsForRoot(layout.defaultRoot) : pathsForRoot(join7(layout.base, instance), instance);
|
|
@@ -9594,7 +9645,7 @@ var init_instances = __esm({
|
|
|
9594
9645
|
});
|
|
9595
9646
|
|
|
9596
9647
|
// src/vm/ledger.ts
|
|
9597
|
-
import { mkdir as
|
|
9648
|
+
import { mkdir as mkdir5, rm as rm6, writeFile as writeFile5 } from "fs/promises";
|
|
9598
9649
|
import { cpus as cpus2, totalmem as totalmem2 } from "os";
|
|
9599
9650
|
import { join as join8 } from "path";
|
|
9600
9651
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
@@ -9651,7 +9702,7 @@ async function withAdmissionLock(fn, base = instancesBase(), wait = ADMISSION_WA
|
|
|
9651
9702
|
await rm6(lock, { recursive: true, force: true });
|
|
9652
9703
|
};
|
|
9653
9704
|
try {
|
|
9654
|
-
await
|
|
9705
|
+
await writeFile5(join8(lock, "pid"), String(process.pid), { mode: 384 });
|
|
9655
9706
|
return await fn(release);
|
|
9656
9707
|
} finally {
|
|
9657
9708
|
await release();
|
|
@@ -9659,7 +9710,7 @@ async function withAdmissionLock(fn, base = instancesBase(), wait = ADMISSION_WA
|
|
|
9659
9710
|
}
|
|
9660
9711
|
async function tryLock(path) {
|
|
9661
9712
|
try {
|
|
9662
|
-
await
|
|
9713
|
+
await mkdir5(path, { mode: 448 });
|
|
9663
9714
|
return true;
|
|
9664
9715
|
} catch (error) {
|
|
9665
9716
|
if (error.code === "EEXIST") return false;
|
|
@@ -9683,8 +9734,8 @@ import { Transform } from "stream";
|
|
|
9683
9734
|
function isBakeRef(ref, prefix) {
|
|
9684
9735
|
return ref.startsWith(prefix) && REF_AFTER_PREFIX.test(ref.slice(prefix.length));
|
|
9685
9736
|
}
|
|
9686
|
-
function gateError(
|
|
9687
|
-
const error = new Error(
|
|
9737
|
+
function gateError(message3) {
|
|
9738
|
+
const error = new Error(message3);
|
|
9688
9739
|
error.name = GATE_ERROR;
|
|
9689
9740
|
return error;
|
|
9690
9741
|
}
|
|
@@ -9694,8 +9745,8 @@ function isTarGateError(error) {
|
|
|
9694
9745
|
function padding(size) {
|
|
9695
9746
|
return (BLOCK - size % BLOCK) % BLOCK;
|
|
9696
9747
|
}
|
|
9697
|
-
function fail(
|
|
9698
|
-
throw gateError(
|
|
9748
|
+
function fail(message3) {
|
|
9749
|
+
throw gateError(message3);
|
|
9699
9750
|
}
|
|
9700
9751
|
function textField(block, start, length) {
|
|
9701
9752
|
const field = block.subarray(start, start + length);
|
|
@@ -10073,7 +10124,7 @@ var init_tar_gate = __esm({
|
|
|
10073
10124
|
});
|
|
10074
10125
|
|
|
10075
10126
|
// src/vm/image-import.ts
|
|
10076
|
-
import { spawn as
|
|
10127
|
+
import { spawn as spawn3 } from "child_process";
|
|
10077
10128
|
import { statfs as statfs4 } from "fs/promises";
|
|
10078
10129
|
import { join as join9 } from "path";
|
|
10079
10130
|
import { Transform as Transform2 } from "stream";
|
|
@@ -10207,7 +10258,7 @@ function stallMeter(limits, stall) {
|
|
|
10207
10258
|
return { meter, stop: () => clearInterval(timer) };
|
|
10208
10259
|
}
|
|
10209
10260
|
function spawnGuestImport(ports, state) {
|
|
10210
|
-
const child =
|
|
10261
|
+
const child = spawn3(state.lima, ["--tty=false", ...guestCtr(ports, "import")], {
|
|
10211
10262
|
env: hostEnvironment(ports.paths),
|
|
10212
10263
|
detached: true,
|
|
10213
10264
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -10324,7 +10375,7 @@ var init_image_import = __esm({
|
|
|
10324
10375
|
});
|
|
10325
10376
|
|
|
10326
10377
|
// src/vm/runner-grant.ts
|
|
10327
|
-
import { spawn as
|
|
10378
|
+
import { spawn as spawn4 } from "child_process";
|
|
10328
10379
|
import { existsSync as existsSync2, promises as fs, realpathSync } from "fs";
|
|
10329
10380
|
import { tmpdir, userInfo as userInfo2 } from "os";
|
|
10330
10381
|
import { join as join10 } from "path";
|
|
@@ -10408,7 +10459,7 @@ async function readGrantRecord(root) {
|
|
|
10408
10459
|
}
|
|
10409
10460
|
function runSudo(args) {
|
|
10410
10461
|
return new Promise((resolve, reject) => {
|
|
10411
|
-
const child =
|
|
10462
|
+
const child = spawn4("sudo", ["--", "/bin/sh", "-c", INSTALL_SCRIPT, "conveyor-k3", ...args], {
|
|
10412
10463
|
stdio: "inherit"
|
|
10413
10464
|
});
|
|
10414
10465
|
child.once("error", reject);
|
|
@@ -10704,10 +10755,12 @@ async function setupVm(ports, parsed, state) {
|
|
|
10704
10755
|
if (!state) state = await createInstance(ports, parsed, url);
|
|
10705
10756
|
await startVm(ports, state);
|
|
10706
10757
|
if (url) await enrollVm(ports, state, url);
|
|
10707
|
-
else
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10758
|
+
else ports.log(setupNextStep(state, ports.paths.instance));
|
|
10759
|
+
}
|
|
10760
|
+
function setupNextStep(state, instance) {
|
|
10761
|
+
const link = instance === DEFAULT_INSTANCE ? "conveyor-k3 vm link" : `conveyor-k3 vm link --instance=${instance}`;
|
|
10762
|
+
if (state.enrolled) return "The VM is running and linked to Conveyor.";
|
|
10763
|
+
return state.enrollmentAttempted ? `Linking may be incomplete. Run ${link} to link the VM again.` : `VM created but not linked yet. Run ${link} to link it to your account.`;
|
|
10711
10764
|
}
|
|
10712
10765
|
function defaultMachineName(paths) {
|
|
10713
10766
|
const base = `${process.platform}-${process.arch}`;
|
|
@@ -10743,6 +10796,7 @@ async function linkVm(ports, state, parsed) {
|
|
|
10743
10796
|
ports.log(
|
|
10744
10797
|
`PAIRING ${JSON.stringify({ userCode: pairing.userCode, verificationUri: pairing.verificationUri })}`
|
|
10745
10798
|
);
|
|
10799
|
+
ports.log(`Open ${pairing.verificationUri} on any device and confirm code ${pairing.userCode}.`);
|
|
10746
10800
|
ports.log("Waiting for browser approval\u2026");
|
|
10747
10801
|
const terminate = () => {
|
|
10748
10802
|
void cancelPairing(api, pairing.deviceCode).finally(() => process.exit(143));
|
|
@@ -10879,9 +10933,9 @@ var init_service_unit = __esm({
|
|
|
10879
10933
|
|
|
10880
10934
|
// src/vm/service-control.ts
|
|
10881
10935
|
import { constants as constants2, readFileSync as readFileSync4 } from "fs";
|
|
10882
|
-
import { access as access5, lstat as lstat3, mkdir as
|
|
10936
|
+
import { access as access5, lstat as lstat3, mkdir as mkdir6, readFile as readFile8, readdir as readdir2, rename as rename3, rm as rm7, writeFile as writeFile6 } from "fs/promises";
|
|
10883
10937
|
import { tmpdir as tmpdir2, userInfo as userInfo3 } from "os";
|
|
10884
|
-
import { dirname as
|
|
10938
|
+
import { dirname as dirname3, join as join12 } from "path";
|
|
10885
10939
|
import { setTimeout as sleep3 } from "timers/promises";
|
|
10886
10940
|
function realServiceHost(exec, log2) {
|
|
10887
10941
|
const uid = process.getuid?.() ?? -1;
|
|
@@ -10992,9 +11046,9 @@ async function writeManagedUnit(path, text) {
|
|
|
10992
11046
|
if (existing !== void 0 && !existing.startsWith(MANAGED_UNIT_HEADER))
|
|
10993
11047
|
throw new Error(`Refusing to replace ${path}: conveyor-k3 did not write it.`);
|
|
10994
11048
|
if (existing === text) return;
|
|
10995
|
-
await
|
|
11049
|
+
await mkdir6(dirname3(path), { recursive: true, mode: 493 });
|
|
10996
11050
|
const temporary = `${path}.${process.pid}.tmp`;
|
|
10997
|
-
await
|
|
11051
|
+
await writeFile6(temporary, text, { mode: 420 });
|
|
10998
11052
|
await rename3(temporary, path);
|
|
10999
11053
|
}
|
|
11000
11054
|
async function assertLinger(host) {
|
|
@@ -11199,7 +11253,7 @@ var init_service_control = __esm({
|
|
|
11199
11253
|
|
|
11200
11254
|
// src/vm/supervisor.ts
|
|
11201
11255
|
import { setTimeout as delay } from "timers/promises";
|
|
11202
|
-
function
|
|
11256
|
+
function message2(error) {
|
|
11203
11257
|
return error instanceof Error ? error.message : String(error);
|
|
11204
11258
|
}
|
|
11205
11259
|
async function pause(ms, deps) {
|
|
@@ -11223,7 +11277,7 @@ async function readyToStart(ports) {
|
|
|
11223
11277
|
);
|
|
11224
11278
|
return state;
|
|
11225
11279
|
} catch (error) {
|
|
11226
|
-
return
|
|
11280
|
+
return message2(error);
|
|
11227
11281
|
}
|
|
11228
11282
|
}
|
|
11229
11283
|
async function forceStop(ports, state) {
|
|
@@ -11234,7 +11288,7 @@ async function forceStop(ports, state) {
|
|
|
11234
11288
|
["stop", "--force", ports.paths.vmName],
|
|
11235
11289
|
void 0,
|
|
11236
11290
|
15e3
|
|
11237
|
-
).catch((error) => ports.log(`The forced stop failed: ${
|
|
11291
|
+
).catch((error) => ports.log(`The forced stop failed: ${message2(error)}`));
|
|
11238
11292
|
}
|
|
11239
11293
|
async function startOrAbort(ports, state, deps) {
|
|
11240
11294
|
let booted = false;
|
|
@@ -11289,7 +11343,7 @@ async function startSupervised(ports, deps) {
|
|
|
11289
11343
|
ports.log("Stopped before the VM started.");
|
|
11290
11344
|
return SERVICE_EXIT.stopped;
|
|
11291
11345
|
}
|
|
11292
|
-
ports.log(`VM start failed: ${
|
|
11346
|
+
ports.log(`VM start failed: ${message2(error)}`);
|
|
11293
11347
|
return SERVICE_EXIT.startFailed;
|
|
11294
11348
|
});
|
|
11295
11349
|
}
|
|
@@ -11304,7 +11358,7 @@ async function watch(ports, state, deps) {
|
|
|
11304
11358
|
failures = 0;
|
|
11305
11359
|
} catch (error) {
|
|
11306
11360
|
failures += 1;
|
|
11307
|
-
ports.log(`Could not read the VM status (${failures} in a row): ${
|
|
11361
|
+
ports.log(`Could not read the VM status (${failures} in a row): ${message2(error)}`);
|
|
11308
11362
|
if (failures <= TOLERATED_STATUS_ERRORS) continue;
|
|
11309
11363
|
await shutdown(ports, state);
|
|
11310
11364
|
return SERVICE_EXIT.unexpectedStop;
|
|
@@ -11348,7 +11402,7 @@ async function shutdownVm(ports, state) {
|
|
|
11348
11402
|
);
|
|
11349
11403
|
ports.log("VM stopped. Its disks and workspaces are preserved.");
|
|
11350
11404
|
} catch (error) {
|
|
11351
|
-
ports.log(`The graceful stop failed (${
|
|
11405
|
+
ports.log(`The graceful stop failed (${message2(error)}); forcing it.`);
|
|
11352
11406
|
await limaCommand(
|
|
11353
11407
|
ports.exec,
|
|
11354
11408
|
state,
|
|
@@ -11356,7 +11410,7 @@ async function shutdownVm(ports, state) {
|
|
|
11356
11410
|
["stop", "--force", ports.paths.vmName],
|
|
11357
11411
|
void 0,
|
|
11358
11412
|
2e4
|
|
11359
|
-
).catch((forced) => ports.log(`The forced stop failed: ${
|
|
11413
|
+
).catch((forced) => ports.log(`The forced stop failed: ${message2(forced)}`));
|
|
11360
11414
|
}
|
|
11361
11415
|
}
|
|
11362
11416
|
async function runSupervisor(ports, deps) {
|
|
@@ -11703,7 +11757,8 @@ var init_command = __esm({
|
|
|
11703
11757
|
init_command_setup();
|
|
11704
11758
|
init_service_command();
|
|
11705
11759
|
init_instance_list();
|
|
11706
|
-
VM_HELP = `
|
|
11760
|
+
VM_HELP = `Personal computer (VM) runtime.
|
|
11761
|
+
Usage: conveyor-k3 vm <setup|start|stop|status|list|link|internet|logout|pods|pod-logs|disk-usage|configure|update|allow|deny|refresh|uninstall|image import|runners grant>
|
|
11707
11762
|
Every command but list acts on one VM: --instance=<name> selects a named VM,
|
|
11708
11763
|
and omitting it selects this user's default VM.
|
|
11709
11764
|
setup [--install-url-stdin] [--relink] [--cpus=4] [--memory=8] [--disk=64]
|
|
@@ -11736,6 +11791,173 @@ Run without sudo. Requires Lima 2.2+ and QEMU with HVF (macOS) or KVM (Linux).
|
|
|
11736
11791
|
}
|
|
11737
11792
|
});
|
|
11738
11793
|
|
|
11794
|
+
// src/host/command.ts
|
|
11795
|
+
var command_exports2 = {};
|
|
11796
|
+
__export(command_exports2, {
|
|
11797
|
+
HOST_HELP: () => HOST_HELP,
|
|
11798
|
+
runHostCommand: () => runHostCommand
|
|
11799
|
+
});
|
|
11800
|
+
import { hostname } from "os";
|
|
11801
|
+
function validateFlags2(parsed) {
|
|
11802
|
+
const action = parsed.positional.length === 1 ? parsed.positional[0] : void 0;
|
|
11803
|
+
const allowed = action === void 0 ? void 0 : ALLOWED_FLAGS.get(action);
|
|
11804
|
+
if (action === void 0 || !allowed) throw new Error(HOST_HELP);
|
|
11805
|
+
for (const [flag, value] of Object.entries(parsed.flags)) {
|
|
11806
|
+
if (!allowed.includes(flag)) throw new Error(`Unknown flag for host ${action}: --${flag}`);
|
|
11807
|
+
const boolean = BOOLEAN_FLAGS.has(flag);
|
|
11808
|
+
if (boolean && value !== true) throw new Error(`Use --${flag} without a value.`);
|
|
11809
|
+
if (!boolean && value === true) throw new Error(`Use --${flag}=<value>.`);
|
|
11810
|
+
}
|
|
11811
|
+
return action;
|
|
11812
|
+
}
|
|
11813
|
+
async function requireHostRoot(exec) {
|
|
11814
|
+
if (process.getuid?.() !== 0)
|
|
11815
|
+
throw new Error(
|
|
11816
|
+
"Host commands change this machine's k3s and systemd units. Run them with sudo."
|
|
11817
|
+
);
|
|
11818
|
+
const checks = await preflight(exec);
|
|
11819
|
+
if (!checks.ok) throw new Error(checks.problems.join("\n"));
|
|
11820
|
+
}
|
|
11821
|
+
async function requireApt(exec) {
|
|
11822
|
+
if (await commandExists(exec, "apt-get")) return;
|
|
11823
|
+
throw new Error(
|
|
11824
|
+
"The host runtime installs its prerequisites with apt-get. Use Ubuntu 24.04 or Debian 12, or newer."
|
|
11825
|
+
);
|
|
11826
|
+
}
|
|
11827
|
+
async function markHostRuntime(ports) {
|
|
11828
|
+
const state = await ports.readState();
|
|
11829
|
+
if (state.runtime !== "host") await ports.writeState({ ...state, runtime: "host" });
|
|
11830
|
+
}
|
|
11831
|
+
async function prepareHostLink(parsed, ports, log2) {
|
|
11832
|
+
const api = trustedDeviceApiUrl(
|
|
11833
|
+
typeof parsed.flags["api-url"] === "string" ? parsed.flags["api-url"] : void 0
|
|
11834
|
+
);
|
|
11835
|
+
const name2 = typeof parsed.flags.name === "string" ? parsed.flags.name.trim() : hostname();
|
|
11836
|
+
if (!MACHINE_NAME.test(name2))
|
|
11837
|
+
throw new Error("Use a machine name of 1-80 letters, numbers, spaces, dots, or hyphens.");
|
|
11838
|
+
if (process.env.CONVEYOR_DEV_ARTIFACT || process.env.CONVEYOR_DEV_ARTIFACT_SHA256)
|
|
11839
|
+
throw new Error("Development artifacts are not supported by host link.");
|
|
11840
|
+
const state = await ports.readState();
|
|
11841
|
+
if (state.enrolled) {
|
|
11842
|
+
const current = state.hostname ?? "a Conveyor instance";
|
|
11843
|
+
if (parsed.flags.relink !== true)
|
|
11844
|
+
throw new Error(
|
|
11845
|
+
`This server is already linked as ${current}. Re-run with --relink to move it to the account that approves the new link (its old instance stays until you Disconnect it).`
|
|
11846
|
+
);
|
|
11847
|
+
log2(`Relinking: this server will leave ${current}.`);
|
|
11848
|
+
}
|
|
11849
|
+
return { api, name: name2 };
|
|
11850
|
+
}
|
|
11851
|
+
async function completeHostLink(api, pairing, log2, ports) {
|
|
11852
|
+
const poll = () => pollPairing(api, pairing.deviceCode);
|
|
11853
|
+
const retry = () => log2("Securing connection\u2026 retrying pairing status.");
|
|
11854
|
+
const result = await pollUntilResolved(poll, pairing.expiresAt, pairing.interval, retry);
|
|
11855
|
+
if (result.status === "completed") {
|
|
11856
|
+
await markHostRuntime(ports);
|
|
11857
|
+
log2(
|
|
11858
|
+
`This server is already linked${result.accountLabel ? ` to ${result.accountLabel}` : ""}${result.displayName ? ` as ${result.displayName}` : ""}.`
|
|
11859
|
+
);
|
|
11860
|
+
return;
|
|
11861
|
+
}
|
|
11862
|
+
if (result.status !== "approved")
|
|
11863
|
+
throw new Error(`Pairing ${result.status}. Start a new link request to try again.`);
|
|
11864
|
+
if (typeof result.bootstrapScript !== "string")
|
|
11865
|
+
throw new Error("Approved pairing did not include a bootstrap payload.");
|
|
11866
|
+
log2("Approved. Installing and enrolling this server\u2026");
|
|
11867
|
+
await ports.runBootstrap(result.bootstrapScript);
|
|
11868
|
+
log2("Verifying Conveyor access\u2026");
|
|
11869
|
+
const completed = await pollUntilResolved(
|
|
11870
|
+
poll,
|
|
11871
|
+
pairing.expiresAt,
|
|
11872
|
+
pairing.interval,
|
|
11873
|
+
retry,
|
|
11874
|
+
void 0,
|
|
11875
|
+
(next) => next.status !== "pending" && next.status !== "approved"
|
|
11876
|
+
);
|
|
11877
|
+
if (completed.status !== "completed")
|
|
11878
|
+
throw new Error(`Pairing ${completed.status}. Conveyor did not verify this server.`);
|
|
11879
|
+
await markHostRuntime(ports);
|
|
11880
|
+
log2(
|
|
11881
|
+
`Linked to ${completed.accountLabel ?? "Conveyor"}${completed.displayName ? ` as ${completed.displayName}` : ""}.`
|
|
11882
|
+
);
|
|
11883
|
+
}
|
|
11884
|
+
async function hostLink(parsed, log2, ports) {
|
|
11885
|
+
const { api, name: name2 } = await prepareHostLink(parsed, ports, log2);
|
|
11886
|
+
log2("Preparing this server: k3s, cloudflared, and the cluster CA\u2026");
|
|
11887
|
+
await ports.prepare();
|
|
11888
|
+
const devicePublicKey = await ports.ensureDevicePublicKey();
|
|
11889
|
+
const pairing = await startPairing(api, name2, devicePublicKey, void 0, { runtime: "host" });
|
|
11890
|
+
log2(
|
|
11891
|
+
`PAIRING ${JSON.stringify({ userCode: pairing.userCode, verificationUri: pairing.verificationUri })}`
|
|
11892
|
+
);
|
|
11893
|
+
log2(`Open ${pairing.verificationUri} on any device and confirm code ${pairing.userCode}.`);
|
|
11894
|
+
log2("Waiting for browser approval\u2026");
|
|
11895
|
+
const terminate = () => {
|
|
11896
|
+
void cancelPairing(api, pairing.deviceCode).finally(() => process.exit(143));
|
|
11897
|
+
};
|
|
11898
|
+
process.once("SIGTERM", terminate);
|
|
11899
|
+
try {
|
|
11900
|
+
await completeHostLink(api, pairing, log2, ports);
|
|
11901
|
+
} catch (error) {
|
|
11902
|
+
await cancelPairing(api, pairing.deviceCode).catch(() => void 0);
|
|
11903
|
+
throw error;
|
|
11904
|
+
} finally {
|
|
11905
|
+
process.removeListener("SIGTERM", terminate);
|
|
11906
|
+
}
|
|
11907
|
+
}
|
|
11908
|
+
async function runHostCommand(parsed, exec, log2, ports) {
|
|
11909
|
+
if (parsed.positional.length === 0 || parsed.flags.help === true) {
|
|
11910
|
+
log2(HOST_HELP);
|
|
11911
|
+
return;
|
|
11912
|
+
}
|
|
11913
|
+
const action = validateFlags2(parsed);
|
|
11914
|
+
if (action === "status") {
|
|
11915
|
+
const asJson = parsed.flags.json === true;
|
|
11916
|
+
if (!asJson && process.getuid?.() !== 0)
|
|
11917
|
+
log2("Not root: this server's state file is root-only. Run sudo conveyor-k3 host status.");
|
|
11918
|
+
return ports.status(asJson);
|
|
11919
|
+
}
|
|
11920
|
+
await requireHostRoot(exec);
|
|
11921
|
+
if (action === "setup" || action === "link") await requireApt(exec);
|
|
11922
|
+
if (action === "setup") return ports.setup();
|
|
11923
|
+
if (action === "link") return hostLink(parsed, log2, ports);
|
|
11924
|
+
await ports.uninstall({
|
|
11925
|
+
purgeK3s: parsed.flags["purge-k3s"] === true,
|
|
11926
|
+
confirmed: parsed.flags.yes === true
|
|
11927
|
+
});
|
|
11928
|
+
await ports.removeDeviceKey();
|
|
11929
|
+
}
|
|
11930
|
+
var HOST_HELP, ALLOWED_FLAGS, BOOLEAN_FLAGS, MACHINE_NAME;
|
|
11931
|
+
var init_command2 = __esm({
|
|
11932
|
+
"src/host/command.ts"() {
|
|
11933
|
+
"use strict";
|
|
11934
|
+
init_env();
|
|
11935
|
+
init_exec();
|
|
11936
|
+
init_device_link();
|
|
11937
|
+
HOST_HELP = `Dedicated server (host) runtime.
|
|
11938
|
+
Usage: conveyor-k3 host <setup|link|status|uninstall>
|
|
11939
|
+
setup Install k3s, or adopt the k3s 1.29+ already here, and
|
|
11940
|
+
cloudflared. Safe to re-run.
|
|
11941
|
+
link [--name=machine] [--api-url=trusted-origin] [--relink]
|
|
11942
|
+
Link this server in a browser: prints a URL and a code.
|
|
11943
|
+
status [--json] What is running, and what Conveyor knows.
|
|
11944
|
+
uninstall [--purge-k3s --yes] Remove Conveyor's units, state, and device key. k3s,
|
|
11945
|
+
your config, and your workloads stay unless you purge.
|
|
11946
|
+
Run with sudo on a dedicated Linux server. Builds run on this server's own k3s with
|
|
11947
|
+
no VM boundary; Pod Security and an egress policy stand in for one. See the guide's
|
|
11948
|
+
"Link a dedicated server (host)" section.
|
|
11949
|
+
`;
|
|
11950
|
+
ALLOWED_FLAGS = /* @__PURE__ */ new Map([
|
|
11951
|
+
["setup", []],
|
|
11952
|
+
["link", ["name", "api-url", "relink"]],
|
|
11953
|
+
["status", ["json"]],
|
|
11954
|
+
["uninstall", ["purge-k3s", "yes"]]
|
|
11955
|
+
]);
|
|
11956
|
+
BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["relink", "json", "purge-k3s", "yes"]);
|
|
11957
|
+
MACHINE_NAME = /^[\w .-]{1,80}$/;
|
|
11958
|
+
}
|
|
11959
|
+
});
|
|
11960
|
+
|
|
11739
11961
|
// src/daemon/workload-cache.ts
|
|
11740
11962
|
init_paths();
|
|
11741
11963
|
import { mkdir, rename, rm, writeFile } from "fs/promises";
|
|
@@ -11830,43 +12052,8 @@ var nodeFileSystem = {
|
|
|
11830
12052
|
}
|
|
11831
12053
|
};
|
|
11832
12054
|
|
|
11833
|
-
// src/
|
|
11834
|
-
|
|
11835
|
-
init_exec();
|
|
11836
|
-
function readCliEnv(env = process.env) {
|
|
11837
|
-
const apiUrl = env.CONVEYOR_API_URL ?? "";
|
|
11838
|
-
const clusterToken = env.CONVEYOR_CLUSTER_TOKEN ?? "";
|
|
11839
|
-
const clusterId = env.CONVEYOR_CLUSTER_ID ?? "";
|
|
11840
|
-
if (!apiUrl || !clusterToken) {
|
|
11841
|
-
throw new Error(
|
|
11842
|
-
"This machine is not enrolled. /etc/conveyor/k3.env must set CONVEYOR_API_URL and CONVEYOR_CLUSTER_TOKEN \u2014 re-run the install command from your Conveyor profile."
|
|
11843
|
-
);
|
|
11844
|
-
}
|
|
11845
|
-
return { apiUrl, clusterToken, clusterId };
|
|
11846
|
-
}
|
|
11847
|
-
async function preflight(exec) {
|
|
11848
|
-
const problems = [];
|
|
11849
|
-
if (process.platform !== "linux") {
|
|
11850
|
-
problems.push(
|
|
11851
|
-
`Managed personal compute requires Linux (this machine reports ${process.platform}).`
|
|
11852
|
-
);
|
|
11853
|
-
}
|
|
11854
|
-
if (process.arch !== "x64" && process.arch !== "arm64") {
|
|
11855
|
-
problems.push(`Unsupported CPU architecture ${process.arch} \u2014 amd64 or arm64 is required.`);
|
|
11856
|
-
}
|
|
11857
|
-
if (typeof process.getuid === "function" && process.getuid() !== 0) {
|
|
11858
|
-
problems.push(
|
|
11859
|
-
"This guest command requires root inside the VM. Use conveyor-k3 vm commands on the host."
|
|
11860
|
-
);
|
|
11861
|
-
}
|
|
11862
|
-
if (!await commandExists(exec, "systemctl")) {
|
|
11863
|
-
problems.push("systemd is required and `systemctl` was not found.");
|
|
11864
|
-
}
|
|
11865
|
-
return { ok: problems.length === 0, problems };
|
|
11866
|
-
}
|
|
11867
|
-
function checkedHostname(hostname) {
|
|
11868
|
-
return assertHostname(hostname, "tunnel hostname");
|
|
11869
|
-
}
|
|
12055
|
+
// src/cli.ts
|
|
12056
|
+
init_env();
|
|
11870
12057
|
|
|
11871
12058
|
// src/lib/state.ts
|
|
11872
12059
|
init_paths();
|
|
@@ -11891,6 +12078,9 @@ async function writeState(fs2, state) {
|
|
|
11891
12078
|
await fs2.writeFile(STATE_FILE, `${JSON.stringify(state, null, 2)}
|
|
11892
12079
|
`, 384);
|
|
11893
12080
|
}
|
|
12081
|
+
function managesK3s(desired, state) {
|
|
12082
|
+
return desired.isolation === void 0 || state.k3sInstalledByConveyor === true;
|
|
12083
|
+
}
|
|
11894
12084
|
|
|
11895
12085
|
// src/cli.ts
|
|
11896
12086
|
init_paths();
|
|
@@ -12775,6 +12965,255 @@ var GRANTED_COMMAND_NAMES = LOCAL_BAKE_ROOT_COMMANDS.map((command) => command.na
|
|
|
12775
12965
|
|
|
12776
12966
|
// src/commands/uninstall.ts
|
|
12777
12967
|
init_dist();
|
|
12968
|
+
|
|
12969
|
+
// src/lib/isolation.ts
|
|
12970
|
+
init_dist();
|
|
12971
|
+
|
|
12972
|
+
// src/lib/k3s-config.ts
|
|
12973
|
+
init_safe_arg();
|
|
12974
|
+
var IMAGE_GC_HIGH = "image-gc-high-threshold";
|
|
12975
|
+
var IMAGE_GC_LOW = "image-gc-low-threshold";
|
|
12976
|
+
var SYSTEM_RESERVED = "system-reserved";
|
|
12977
|
+
var K3S_DISABLED_COMPONENTS = ["traefik", "servicelb"];
|
|
12978
|
+
var WRITE_KUBECONFIG_MODE = "write-kubeconfig-mode";
|
|
12979
|
+
var KUBECONFIG_MODE = "600";
|
|
12980
|
+
var K3S_INSTALL_EXEC = [
|
|
12981
|
+
...K3S_DISABLED_COMPONENTS.flatMap((component) => ["--disable", component]),
|
|
12982
|
+
`--${WRITE_KUBECONFIG_MODE}`,
|
|
12983
|
+
KUBECONFIG_MODE
|
|
12984
|
+
].join(" ");
|
|
12985
|
+
function bareToken(token) {
|
|
12986
|
+
let bare = token.trim();
|
|
12987
|
+
if (bare.endsWith("\\")) bare = bare.slice(0, -1).trim();
|
|
12988
|
+
const quote = bare[0];
|
|
12989
|
+
if (bare.length >= 2 && (quote === "'" || quote === '"') && bare.endsWith(quote)) {
|
|
12990
|
+
bare = bare.slice(1, -1);
|
|
12991
|
+
}
|
|
12992
|
+
return bare;
|
|
12993
|
+
}
|
|
12994
|
+
function ownerConfiguredKubeletFlags(ownerConfig) {
|
|
12995
|
+
const found = /* @__PURE__ */ new Set();
|
|
12996
|
+
if (!ownerConfig) return found;
|
|
12997
|
+
for (const flag of [IMAGE_GC_HIGH, IMAGE_GC_LOW, SYSTEM_RESERVED]) {
|
|
12998
|
+
if (new RegExp(`(^|[\\s"'-])${flag}\\s*=`, "m").test(ownerConfig)) found.add(flag);
|
|
12999
|
+
}
|
|
13000
|
+
return found;
|
|
13001
|
+
}
|
|
13002
|
+
function ownerConfiguredKeys(ownerConfig) {
|
|
13003
|
+
const found = /* @__PURE__ */ new Set();
|
|
13004
|
+
if (!ownerConfig) return found;
|
|
13005
|
+
for (const key of [WRITE_KUBECONFIG_MODE]) {
|
|
13006
|
+
if (new RegExp(`^${key}\\s*:`, "m").test(ownerConfig)) found.add(key);
|
|
13007
|
+
}
|
|
13008
|
+
return found;
|
|
13009
|
+
}
|
|
13010
|
+
function ownerImageGcHighThreshold(ownerConfig) {
|
|
13011
|
+
if (!ownerConfig) return null;
|
|
13012
|
+
const match = new RegExp(`(^|[\\s"'-])${IMAGE_GC_HIGH}\\s*=\\s*(\\d{1,3})`, "m").exec(
|
|
13013
|
+
ownerConfig
|
|
13014
|
+
);
|
|
13015
|
+
if (!match) return null;
|
|
13016
|
+
const value = Number.parseInt(match[2] ?? "", 10);
|
|
13017
|
+
return Number.isInteger(value) && value >= 0 && value <= 100 ? value : null;
|
|
13018
|
+
}
|
|
13019
|
+
function renderK3sDropIn(kubelet, ownerConfig, options = {}) {
|
|
13020
|
+
const ownerFlags = ownerConfiguredKubeletFlags(ownerConfig);
|
|
13021
|
+
const ownerKeys = ownerConfiguredKeys(ownerConfig);
|
|
13022
|
+
const args = [];
|
|
13023
|
+
const skipped = [];
|
|
13024
|
+
const add = (flag, value) => {
|
|
13025
|
+
if (ownerFlags.has(flag)) {
|
|
13026
|
+
skipped.push(flag);
|
|
13027
|
+
return;
|
|
13028
|
+
}
|
|
13029
|
+
args.push(` - "${flag}=${value}"`);
|
|
13030
|
+
};
|
|
13031
|
+
add(IMAGE_GC_HIGH, String(assertInteger(kubelet.imageGcHighThreshold, 1, 100, IMAGE_GC_HIGH)));
|
|
13032
|
+
add(IMAGE_GC_LOW, String(assertInteger(kubelet.imageGcLowThreshold, 0, 99, IMAGE_GC_LOW)));
|
|
13033
|
+
if (kubelet.systemReserved) {
|
|
13034
|
+
const cpu = assertQuantity(kubelet.systemReserved.cpu, "reserveCpu");
|
|
13035
|
+
const memory = assertQuantity(kubelet.systemReserved.memory, "reserveMemory");
|
|
13036
|
+
add(SYSTEM_RESERVED, `cpu=${cpu},memory=${memory}`);
|
|
13037
|
+
}
|
|
13038
|
+
const lines2 = [
|
|
13039
|
+
"# Managed by Conveyor (@rallycry/conveyor-k3). Do not edit.",
|
|
13040
|
+
"# Your own /etc/rancher/k3s/config.yaml is never modified; k3s merges both.",
|
|
13041
|
+
"# `kubelet-arg+` and `disable+` append rather than replacing a list you set."
|
|
13042
|
+
];
|
|
13043
|
+
if (options.disableComponents !== false) {
|
|
13044
|
+
lines2.push("disable+:");
|
|
13045
|
+
for (const component of K3S_DISABLED_COMPONENTS) lines2.push(` - ${component}`);
|
|
13046
|
+
}
|
|
13047
|
+
if (ownerKeys.has(WRITE_KUBECONFIG_MODE)) {
|
|
13048
|
+
skipped.push(WRITE_KUBECONFIG_MODE);
|
|
13049
|
+
} else {
|
|
13050
|
+
lines2.push(`${WRITE_KUBECONFIG_MODE}: "${KUBECONFIG_MODE}"`);
|
|
13051
|
+
}
|
|
13052
|
+
if (args.length > 0) {
|
|
13053
|
+
lines2.push("kubelet-arg+:", ...args);
|
|
13054
|
+
}
|
|
13055
|
+
return { content: [...lines2, ""].join("\n"), skipped };
|
|
13056
|
+
}
|
|
13057
|
+
|
|
13058
|
+
// src/lib/isolation.ts
|
|
13059
|
+
init_safe_arg();
|
|
13060
|
+
var EGRESS_POLICY_NAME = "conveyor-k3-egress";
|
|
13061
|
+
var MANAGED_BY2 = { "app.kubernetes.io/managed-by": "conveyor-k3" };
|
|
13062
|
+
var NON_ROUTABLE_IPV4 = [
|
|
13063
|
+
"0.0.0.0/8",
|
|
13064
|
+
"10.0.0.0/8",
|
|
13065
|
+
"100.64.0.0/10",
|
|
13066
|
+
"127.0.0.0/8",
|
|
13067
|
+
"169.254.0.0/16",
|
|
13068
|
+
"172.16.0.0/12",
|
|
13069
|
+
"192.0.0.0/24",
|
|
13070
|
+
"192.0.2.0/24",
|
|
13071
|
+
"192.88.99.0/24",
|
|
13072
|
+
"192.168.0.0/16",
|
|
13073
|
+
"198.18.0.0/15",
|
|
13074
|
+
"198.51.100.0/24",
|
|
13075
|
+
"203.0.113.0/24",
|
|
13076
|
+
"224.0.0.0/4",
|
|
13077
|
+
"240.0.0.0/4"
|
|
13078
|
+
];
|
|
13079
|
+
var KUBE_DNS = { namespace: "kube-system", labels: { "k8s-app": "kube-dns" } };
|
|
13080
|
+
var PSA_PREFIX = "pod-security.kubernetes.io";
|
|
13081
|
+
function psaLabels(level) {
|
|
13082
|
+
const advisory = level === "baseline" ? "restricted" : level;
|
|
13083
|
+
return {
|
|
13084
|
+
[`${PSA_PREFIX}/enforce`]: level,
|
|
13085
|
+
[`${PSA_PREFIX}/warn`]: advisory,
|
|
13086
|
+
[`${PSA_PREFIX}/audit`]: advisory
|
|
13087
|
+
};
|
|
13088
|
+
}
|
|
13089
|
+
var PSA_LABEL_KEYS = Object.keys(psaLabels("baseline"));
|
|
13090
|
+
function ipv4Value(address) {
|
|
13091
|
+
const parts = address.split(".");
|
|
13092
|
+
if (parts.length !== 4) return null;
|
|
13093
|
+
let value = 0;
|
|
13094
|
+
for (const part of parts) {
|
|
13095
|
+
if (!/^\d{1,3}$/.test(part)) return null;
|
|
13096
|
+
const octet = Number(part);
|
|
13097
|
+
if (octet > 255) return null;
|
|
13098
|
+
value = value * 256 + octet;
|
|
13099
|
+
}
|
|
13100
|
+
return value;
|
|
13101
|
+
}
|
|
13102
|
+
function inNonRoutableRange(address) {
|
|
13103
|
+
const value = ipv4Value(address);
|
|
13104
|
+
if (value === null) return false;
|
|
13105
|
+
return NON_ROUTABLE_IPV4.some((cidr) => {
|
|
13106
|
+
const [base = "", bits = "32"] = cidr.split("/");
|
|
13107
|
+
const start = ipv4Value(base) ?? 0;
|
|
13108
|
+
return value >= start && value < start + 2 ** (32 - Number(bits));
|
|
13109
|
+
});
|
|
13110
|
+
}
|
|
13111
|
+
function hostAddressExceptions(addresses) {
|
|
13112
|
+
const kept = /* @__PURE__ */ new Set();
|
|
13113
|
+
for (const raw of addresses) {
|
|
13114
|
+
const address = raw.trim();
|
|
13115
|
+
if (ipv4Value(address) === null || inNonRoutableRange(address)) continue;
|
|
13116
|
+
kept.add(`${address}/32`);
|
|
13117
|
+
}
|
|
13118
|
+
return [...kept].sort();
|
|
13119
|
+
}
|
|
13120
|
+
function renderEgressNetworkPolicy(namespace, hostAddresses) {
|
|
13121
|
+
const policy = {
|
|
13122
|
+
apiVersion: "networking.k8s.io/v1",
|
|
13123
|
+
kind: "NetworkPolicy",
|
|
13124
|
+
metadata: {
|
|
13125
|
+
name: EGRESS_POLICY_NAME,
|
|
13126
|
+
namespace: assertK8sName(namespace, "namespace"),
|
|
13127
|
+
labels: MANAGED_BY2
|
|
13128
|
+
},
|
|
13129
|
+
spec: {
|
|
13130
|
+
podSelector: {},
|
|
13131
|
+
policyTypes: ["Egress"],
|
|
13132
|
+
egress: [
|
|
13133
|
+
{
|
|
13134
|
+
to: [
|
|
13135
|
+
{
|
|
13136
|
+
namespaceSelector: {
|
|
13137
|
+
matchLabels: { "kubernetes.io/metadata.name": KUBE_DNS.namespace }
|
|
13138
|
+
},
|
|
13139
|
+
podSelector: { matchLabels: KUBE_DNS.labels }
|
|
13140
|
+
}
|
|
13141
|
+
],
|
|
13142
|
+
ports: [
|
|
13143
|
+
{ protocol: "UDP", port: 53 },
|
|
13144
|
+
{ protocol: "TCP", port: 53 }
|
|
13145
|
+
]
|
|
13146
|
+
},
|
|
13147
|
+
{
|
|
13148
|
+
// kube-router enforces after the Service's DNAT, so the registry is
|
|
13149
|
+
// reached by its pods' labels, never by the service range.
|
|
13150
|
+
to: [
|
|
13151
|
+
{
|
|
13152
|
+
namespaceSelector: {
|
|
13153
|
+
matchLabels: { "kubernetes.io/metadata.name": PERSONAL_LOCAL_REGISTRY_NAMESPACE }
|
|
13154
|
+
},
|
|
13155
|
+
podSelector: { matchLabels: LOCAL_REGISTRY_SELECTOR }
|
|
13156
|
+
}
|
|
13157
|
+
],
|
|
13158
|
+
ports: [{ protocol: "TCP", port: PERSONAL_LOCAL_REGISTRY_PORT }]
|
|
13159
|
+
},
|
|
13160
|
+
{
|
|
13161
|
+
to: [
|
|
13162
|
+
{
|
|
13163
|
+
ipBlock: {
|
|
13164
|
+
cidr: "0.0.0.0/0",
|
|
13165
|
+
except: [...NON_ROUTABLE_IPV4, ...hostAddressExceptions(hostAddresses)]
|
|
13166
|
+
}
|
|
13167
|
+
}
|
|
13168
|
+
]
|
|
13169
|
+
}
|
|
13170
|
+
]
|
|
13171
|
+
}
|
|
13172
|
+
};
|
|
13173
|
+
return `${JSON.stringify(policy, null, 2)}
|
|
13174
|
+
`;
|
|
13175
|
+
}
|
|
13176
|
+
var DISABLE_KEY = /(?:^|[\s{,])(["']?)disable-network-policy\1\s*:([^,}]*)/g;
|
|
13177
|
+
var DISABLE_FLAGS = /* @__PURE__ */ new Set(["--disable-network-policy", "-disable-network-policy"]);
|
|
13178
|
+
var FALSE_VALUES = /* @__PURE__ */ new Set(["false", "no", "off", "n", "f", "0"]);
|
|
13179
|
+
function setsTrue(raw) {
|
|
13180
|
+
const value = bareToken(raw).trim().toLowerCase();
|
|
13181
|
+
return !FALSE_VALUES.has(value);
|
|
13182
|
+
}
|
|
13183
|
+
function uncommented(line) {
|
|
13184
|
+
return /^\s*#/.test(line) ? "" : line.replace(/\s#.*$/, "");
|
|
13185
|
+
}
|
|
13186
|
+
function configSetsKey(text) {
|
|
13187
|
+
return text.split("\n").map(uncommented).some((line) => [...line.matchAll(DISABLE_KEY)].some((match) => setsTrue(match[2] ?? "")));
|
|
13188
|
+
}
|
|
13189
|
+
function unitLines(unitText) {
|
|
13190
|
+
const lines2 = [];
|
|
13191
|
+
let pending = "";
|
|
13192
|
+
for (const raw of unitText.split("\n")) {
|
|
13193
|
+
const line = raw.trimEnd();
|
|
13194
|
+
if (/^\s*[#;]/.test(line)) continue;
|
|
13195
|
+
if (line.endsWith("\\")) {
|
|
13196
|
+
pending += `${line.slice(0, -1)} `;
|
|
13197
|
+
continue;
|
|
13198
|
+
}
|
|
13199
|
+
lines2.push(pending + line);
|
|
13200
|
+
pending = "";
|
|
13201
|
+
}
|
|
13202
|
+
return pending ? [...lines2, pending] : lines2;
|
|
13203
|
+
}
|
|
13204
|
+
function flagSetIn(line) {
|
|
13205
|
+
return line.split(/\s+/).some((raw) => {
|
|
13206
|
+
const token = bareToken(raw).replace(/^["']+|["']+$/g, "");
|
|
13207
|
+
const [name2 = "", ...value] = token.split("=");
|
|
13208
|
+
return DISABLE_FLAGS.has(name2) && (value.length === 0 || setsTrue(value.join("=")));
|
|
13209
|
+
});
|
|
13210
|
+
}
|
|
13211
|
+
function networkPolicyControllerDisabled(sources) {
|
|
13212
|
+
if (sources.configs.some((text) => text !== null && configSetsKey(text))) return true;
|
|
13213
|
+
return unitLines(sources.unitText).some(flagSetIn);
|
|
13214
|
+
}
|
|
13215
|
+
|
|
13216
|
+
// src/commands/uninstall.ts
|
|
12778
13217
|
init_paths();
|
|
12779
13218
|
async function removeLocalRegistry(ports) {
|
|
12780
13219
|
const removed = await ports.exec(
|
|
@@ -12808,6 +13247,47 @@ async function removeLocalRegistry(ports) {
|
|
|
12808
13247
|
`Removed the local image registry's mirror from ${REGISTRIES_FILE} (backup: ${backup}).`
|
|
12809
13248
|
);
|
|
12810
13249
|
}
|
|
13250
|
+
async function removeIsolation(ports) {
|
|
13251
|
+
const [key, value] = Object.entries(MANAGED_BY2)[0] ?? [];
|
|
13252
|
+
const removed = await ports.exec(
|
|
13253
|
+
"k3s",
|
|
13254
|
+
["kubectl", "delete", "networkpolicy", "-A", "-l", `${key}=${value}`, "--ignore-not-found"],
|
|
13255
|
+
{ timeoutMs: 12e4 }
|
|
13256
|
+
);
|
|
13257
|
+
if (removed.exitCode !== 0) {
|
|
13258
|
+
ports.log(
|
|
13259
|
+
`Could not remove Conveyor's network policies: ${removed.stderr.trim().slice(0, 200)}`
|
|
13260
|
+
);
|
|
13261
|
+
}
|
|
13262
|
+
const listed = await ports.exec("k3s", [
|
|
13263
|
+
"kubectl",
|
|
13264
|
+
"get",
|
|
13265
|
+
"namespaces",
|
|
13266
|
+
"-o",
|
|
13267
|
+
"jsonpath={.items[*].metadata.name}"
|
|
13268
|
+
]);
|
|
13269
|
+
if (listed.exitCode !== 0) {
|
|
13270
|
+
ports.log(
|
|
13271
|
+
`Could not list namespaces to remove Conveyor's Pod Security labels: ${listed.stderr.trim().slice(0, 200)}`
|
|
13272
|
+
);
|
|
13273
|
+
return;
|
|
13274
|
+
}
|
|
13275
|
+
const namespaces = listed.stdout.split(/\s+/).filter((name2) => name2.startsWith("claudespace-") || name2 === PERSONAL_BAKE_NAMESPACE);
|
|
13276
|
+
for (const namespace of namespaces) {
|
|
13277
|
+
const unlabelled = await ports.exec("k3s", [
|
|
13278
|
+
"kubectl",
|
|
13279
|
+
"label",
|
|
13280
|
+
"namespace",
|
|
13281
|
+
namespace,
|
|
13282
|
+
...PSA_LABEL_KEYS.map((labelKey) => `${labelKey}-`)
|
|
13283
|
+
]);
|
|
13284
|
+
if (unlabelled.exitCode !== 0) {
|
|
13285
|
+
ports.log(
|
|
13286
|
+
`Could not remove Conveyor's Pod Security labels from ${namespace}: ${unlabelled.stderr.trim().slice(0, 200)}`
|
|
13287
|
+
);
|
|
13288
|
+
}
|
|
13289
|
+
}
|
|
13290
|
+
}
|
|
12811
13291
|
async function uninstall(ports, options = {}) {
|
|
12812
13292
|
const result = { stoppedUnits: [], removedFiles: [], purgedK3s: false };
|
|
12813
13293
|
if (options.purgeK3s && !options.confirmed) {
|
|
@@ -12820,6 +13300,7 @@ async function uninstall(ports, options = {}) {
|
|
|
12820
13300
|
result.stoppedUnits.push(unit);
|
|
12821
13301
|
}
|
|
12822
13302
|
await removeLocalRegistry(ports);
|
|
13303
|
+
await removeIsolation(ports);
|
|
12823
13304
|
for (const path of OWNED_FILES) {
|
|
12824
13305
|
await ports.fs.removeFile(path);
|
|
12825
13306
|
result.removedFiles.push(path);
|
|
@@ -12872,121 +13353,46 @@ async function uninstall(ports, options = {}) {
|
|
|
12872
13353
|
return result;
|
|
12873
13354
|
}
|
|
12874
13355
|
|
|
12875
|
-
// src/
|
|
12876
|
-
|
|
12877
|
-
var
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
].join(" ");
|
|
12888
|
-
function ownerConfiguredKubeletFlags(ownerConfig) {
|
|
12889
|
-
const found = /* @__PURE__ */ new Set();
|
|
12890
|
-
if (!ownerConfig) return found;
|
|
12891
|
-
for (const flag of [IMAGE_GC_HIGH, IMAGE_GC_LOW, SYSTEM_RESERVED]) {
|
|
12892
|
-
if (new RegExp(`(^|[\\s"'-])${flag}\\s*=`, "m").test(ownerConfig)) found.add(flag);
|
|
13356
|
+
// src/commands/update.ts
|
|
13357
|
+
init_paths();
|
|
13358
|
+
var CLOUDFLARED_UPDATED_EXIT = 11;
|
|
13359
|
+
function readExecStart(unit) {
|
|
13360
|
+
const lines2 = unit.split("\n");
|
|
13361
|
+
const start = lines2.findIndex((line) => line.trimStart().startsWith("ExecStart="));
|
|
13362
|
+
if (start < 0) return "";
|
|
13363
|
+
const collected = [];
|
|
13364
|
+
for (let index = start; index < lines2.length; index += 1) {
|
|
13365
|
+
const line = lines2[index] ?? "";
|
|
13366
|
+
collected.push(line);
|
|
13367
|
+
if (!line.trimEnd().endsWith("\\")) break;
|
|
12893
13368
|
}
|
|
12894
|
-
return
|
|
13369
|
+
return collected.join(" ");
|
|
12895
13370
|
}
|
|
12896
|
-
function
|
|
12897
|
-
const
|
|
12898
|
-
|
|
12899
|
-
for (const
|
|
12900
|
-
|
|
13371
|
+
function parseForeignExecFlags(execStart) {
|
|
13372
|
+
const ours = new Set(K3S_INSTALL_EXEC.split(/\s+/).filter((token) => token.startsWith("--")));
|
|
13373
|
+
const foreign = [];
|
|
13374
|
+
for (const raw of execStart.split(/\s+/)) {
|
|
13375
|
+
const token = bareToken(raw);
|
|
13376
|
+
if (!token.startsWith("--")) continue;
|
|
13377
|
+
const name2 = token.split("=")[0] ?? token;
|
|
13378
|
+
if (!ours.has(name2) && !foreign.includes(name2)) foreign.push(name2);
|
|
12901
13379
|
}
|
|
12902
|
-
return
|
|
13380
|
+
return foreign;
|
|
12903
13381
|
}
|
|
12904
|
-
function
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
ownerConfig
|
|
12908
|
-
);
|
|
12909
|
-
if (!match) return null;
|
|
12910
|
-
const value = Number.parseInt(match[2] ?? "", 10);
|
|
12911
|
-
return Number.isInteger(value) && value >= 0 && value <= 100 ? value : null;
|
|
12912
|
-
}
|
|
12913
|
-
function renderK3sDropIn(kubelet, ownerConfig) {
|
|
12914
|
-
const ownerFlags = ownerConfiguredKubeletFlags(ownerConfig);
|
|
12915
|
-
const ownerKeys = ownerConfiguredKeys(ownerConfig);
|
|
12916
|
-
const args = [];
|
|
12917
|
-
const skipped = [];
|
|
12918
|
-
const add = (flag, value) => {
|
|
12919
|
-
if (ownerFlags.has(flag)) {
|
|
12920
|
-
skipped.push(flag);
|
|
12921
|
-
return;
|
|
12922
|
-
}
|
|
12923
|
-
args.push(` - "${flag}=${value}"`);
|
|
12924
|
-
};
|
|
12925
|
-
add(IMAGE_GC_HIGH, String(assertInteger(kubelet.imageGcHighThreshold, 1, 100, IMAGE_GC_HIGH)));
|
|
12926
|
-
add(IMAGE_GC_LOW, String(assertInteger(kubelet.imageGcLowThreshold, 0, 99, IMAGE_GC_LOW)));
|
|
12927
|
-
if (kubelet.systemReserved) {
|
|
12928
|
-
const cpu = assertQuantity(kubelet.systemReserved.cpu, "reserveCpu");
|
|
12929
|
-
const memory = assertQuantity(kubelet.systemReserved.memory, "reserveMemory");
|
|
12930
|
-
add(SYSTEM_RESERVED, `cpu=${cpu},memory=${memory}`);
|
|
12931
|
-
}
|
|
12932
|
-
const lines2 = [
|
|
12933
|
-
"# Managed by Conveyor (@rallycry/conveyor-k3). Do not edit.",
|
|
12934
|
-
"# Your own /etc/rancher/k3s/config.yaml is never modified; k3s merges both.",
|
|
12935
|
-
"# `kubelet-arg+` and `disable+` append rather than replacing a list you set."
|
|
12936
|
-
];
|
|
12937
|
-
lines2.push("disable+:");
|
|
12938
|
-
for (const component of K3S_DISABLED_COMPONENTS) lines2.push(` - ${component}`);
|
|
12939
|
-
if (ownerKeys.has(WRITE_KUBECONFIG_MODE)) {
|
|
12940
|
-
skipped.push(WRITE_KUBECONFIG_MODE);
|
|
12941
|
-
} else {
|
|
12942
|
-
lines2.push(`${WRITE_KUBECONFIG_MODE}: "${KUBECONFIG_MODE}"`);
|
|
12943
|
-
}
|
|
12944
|
-
if (args.length > 0) {
|
|
12945
|
-
lines2.push("kubelet-arg+:", ...args);
|
|
12946
|
-
}
|
|
12947
|
-
return { content: [...lines2, ""].join("\n"), skipped };
|
|
12948
|
-
}
|
|
12949
|
-
|
|
12950
|
-
// src/commands/update.ts
|
|
12951
|
-
init_paths();
|
|
12952
|
-
var CLOUDFLARED_UPDATED_EXIT = 11;
|
|
12953
|
-
function readExecStart(unit) {
|
|
12954
|
-
const lines2 = unit.split("\n");
|
|
12955
|
-
const start = lines2.findIndex((line) => line.trimStart().startsWith("ExecStart="));
|
|
12956
|
-
if (start < 0) return "";
|
|
12957
|
-
const collected = [];
|
|
12958
|
-
for (let index = start; index < lines2.length; index += 1) {
|
|
12959
|
-
const line = lines2[index] ?? "";
|
|
12960
|
-
collected.push(line);
|
|
12961
|
-
if (!line.trimEnd().endsWith("\\")) break;
|
|
12962
|
-
}
|
|
12963
|
-
return collected.join(" ");
|
|
12964
|
-
}
|
|
12965
|
-
function bareToken(token) {
|
|
12966
|
-
let bare = token.trim();
|
|
12967
|
-
if (bare.endsWith("\\")) bare = bare.slice(0, -1).trim();
|
|
12968
|
-
if (bare.length >= 2 && bare.startsWith("'") && bare.endsWith("'")) bare = bare.slice(1, -1);
|
|
12969
|
-
return bare;
|
|
12970
|
-
}
|
|
12971
|
-
function parseForeignExecFlags(execStart) {
|
|
12972
|
-
const ours = new Set(K3S_INSTALL_EXEC.split(/\s+/).filter((token) => token.startsWith("--")));
|
|
12973
|
-
const foreign = [];
|
|
12974
|
-
for (const raw of execStart.split(/\s+/)) {
|
|
12975
|
-
const token = bareToken(raw);
|
|
12976
|
-
if (!token.startsWith("--")) continue;
|
|
12977
|
-
const name2 = token.split("=")[0] ?? token;
|
|
12978
|
-
if (!ours.has(name2) && !foreign.includes(name2)) foreign.push(name2);
|
|
12979
|
-
}
|
|
12980
|
-
return foreign;
|
|
12981
|
-
}
|
|
12982
|
-
async function foreignK3sExecFlags(exec) {
|
|
12983
|
-
const unit = await exec("systemctl", ["cat", "k3s.service"], { timeoutMs: 1e4 }).catch(
|
|
12984
|
-
() => null
|
|
13382
|
+
async function foreignK3sExecFlags(exec) {
|
|
13383
|
+
const unit = await exec("systemctl", ["cat", "k3s.service"], { timeoutMs: 1e4 }).catch(
|
|
13384
|
+
() => null
|
|
12985
13385
|
);
|
|
12986
13386
|
if (!unit || unit.exitCode !== 0) return [];
|
|
12987
13387
|
return parseForeignExecFlags(readExecStart(unit.stdout));
|
|
12988
13388
|
}
|
|
12989
13389
|
function decideK3sUpgrade(input) {
|
|
13390
|
+
if (input.managesK3s === false) {
|
|
13391
|
+
return {
|
|
13392
|
+
upgrade: false,
|
|
13393
|
+
reason: "Conveyor did not install this server's k3s, so its owner upgrades it"
|
|
13394
|
+
};
|
|
13395
|
+
}
|
|
12990
13396
|
if (!input.autoUpgradeK3s) {
|
|
12991
13397
|
return { upgrade: false, reason: "automatic k3s upgrades are turned off for this machine" };
|
|
12992
13398
|
}
|
|
@@ -13083,6 +13489,7 @@ async function runUpdate(ports, decision, channel, targetVersion) {
|
|
|
13083
13489
|
|
|
13084
13490
|
// src/commands/setup.ts
|
|
13085
13491
|
init_exec();
|
|
13492
|
+
init_env();
|
|
13086
13493
|
init_paths();
|
|
13087
13494
|
|
|
13088
13495
|
// src/lib/systemd.ts
|
|
@@ -13221,6 +13628,8 @@ async function ensureK3s(ports) {
|
|
|
13221
13628
|
}
|
|
13222
13629
|
});
|
|
13223
13630
|
ports.log("k3s installed.");
|
|
13631
|
+
await ports.fs.ensureDir(STATE_DIR);
|
|
13632
|
+
await writeState(ports.fs, { ...await readState(ports.fs), k3sInstalledByConveyor: true });
|
|
13224
13633
|
}
|
|
13225
13634
|
async function ensureCloudflared(ports) {
|
|
13226
13635
|
if (await commandExists(ports.exec, "cloudflared")) {
|
|
@@ -13306,11 +13715,12 @@ async function installUnits(ports, bundle, cloudflaredBin, maintenanceHour) {
|
|
|
13306
13715
|
await runOrThrow(ports.exec, "systemctl", ["enable", DAEMON_UNIT]);
|
|
13307
13716
|
await runOrThrow(ports.exec, "systemctl", ["enable", UPDATE_TIMER]);
|
|
13308
13717
|
}
|
|
13309
|
-
async function recordSetupState(ports, bundle, appliedVersion, bins) {
|
|
13718
|
+
async function recordSetupState(ports, bundle, appliedVersion, bins, recorded = {}) {
|
|
13310
13719
|
await ports.fs.ensureDir(STATE_DIR);
|
|
13311
13720
|
const state = await readState(ports.fs);
|
|
13312
13721
|
await writeState(ports.fs, {
|
|
13313
13722
|
...state,
|
|
13723
|
+
...recorded,
|
|
13314
13724
|
hostname: checkedHostname(bundle.hostname),
|
|
13315
13725
|
enrolled: true,
|
|
13316
13726
|
appliedDesiredStateVersion: appliedVersion,
|
|
@@ -13323,86 +13733,6 @@ async function resolveBunBin(ports) {
|
|
|
13323
13733
|
return found?.startsWith("/") ? found : "/root/.bun/bin/bun";
|
|
13324
13734
|
}
|
|
13325
13735
|
|
|
13326
|
-
// src/lib/bins.ts
|
|
13327
|
-
var INSTALLER_PATHS = {
|
|
13328
|
-
bun: "/root/.bun/bin/bun",
|
|
13329
|
-
cli: "/root/.bun/bin/conveyor-k3",
|
|
13330
|
-
cloudflared: "/usr/local/bin/cloudflared"
|
|
13331
|
-
};
|
|
13332
|
-
async function whichOrNull(exec, bin) {
|
|
13333
|
-
const result = await exec("which", [bin]).catch(() => null);
|
|
13334
|
-
if (!result || result.exitCode !== 0) return null;
|
|
13335
|
-
const path = result.stdout.trim().split("\n")[0]?.trim();
|
|
13336
|
-
return path && path.startsWith("/") ? path : null;
|
|
13337
|
-
}
|
|
13338
|
-
async function resolveBinaries(exec, state, selfPath) {
|
|
13339
|
-
const recorded = state.bins;
|
|
13340
|
-
const cliSelf = selfPath?.startsWith("/") ? selfPath : null;
|
|
13341
|
-
return {
|
|
13342
|
-
bun: recorded?.bun ?? await whichOrNull(exec, "bun") ?? INSTALLER_PATHS.bun,
|
|
13343
|
-
// `cli` prefers the running process's own path: if this code is executing,
|
|
13344
|
-
// that path is by definition correct.
|
|
13345
|
-
cli: cliSelf ?? recorded?.cli ?? await whichOrNull(exec, "conveyor-k3") ?? INSTALLER_PATHS.cli,
|
|
13346
|
-
cloudflared: recorded?.cloudflared ?? await whichOrNull(exec, "cloudflared") ?? INSTALLER_PATHS.cloudflared
|
|
13347
|
-
};
|
|
13348
|
-
}
|
|
13349
|
-
|
|
13350
|
-
// src/cli.ts
|
|
13351
|
-
init_journald();
|
|
13352
|
-
|
|
13353
|
-
// src/commands/setup-session.ts
|
|
13354
|
-
init_exec();
|
|
13355
|
-
init_paths();
|
|
13356
|
-
async function withSetupConnection(ports, connection, enroll) {
|
|
13357
|
-
for (const unit of [UPDATE_TIMER, UPDATE_UNIT, DAEMON_UNIT]) {
|
|
13358
|
-
if (await ports.fs.readFile(`${SYSTEMD_DIR}/${unit}`))
|
|
13359
|
-
await runOrThrow(ports.exec, "systemctl", ["stop", unit]);
|
|
13360
|
-
}
|
|
13361
|
-
let result;
|
|
13362
|
-
try {
|
|
13363
|
-
await connection.connect();
|
|
13364
|
-
result = await enroll();
|
|
13365
|
-
} finally {
|
|
13366
|
-
connection.disconnect();
|
|
13367
|
-
}
|
|
13368
|
-
await runOrThrow(ports.exec, "systemctl", ["start", DAEMON_UNIT]);
|
|
13369
|
-
await runOrThrow(ports.exec, "systemctl", ["start", UPDATE_TIMER]);
|
|
13370
|
-
return result;
|
|
13371
|
-
}
|
|
13372
|
-
|
|
13373
|
-
// src/lib/guest-vm.ts
|
|
13374
|
-
async function requireGuestVm(exec) {
|
|
13375
|
-
try {
|
|
13376
|
-
const result = await exec("systemd-detect-virt", ["--vm", "--quiet"]);
|
|
13377
|
-
if (result.exitCode === 0) return;
|
|
13378
|
-
} catch {
|
|
13379
|
-
}
|
|
13380
|
-
throw new Error("Refusing to run guest setup on a physical host; use `conveyor-k3 vm setup`");
|
|
13381
|
-
}
|
|
13382
|
-
|
|
13383
|
-
// src/lib/k3s-channel.ts
|
|
13384
|
-
var CHANNELS_URL = "https://update.k3s.io/v1-release/channels";
|
|
13385
|
-
var REQUEST_TIMEOUT_MS2 = 15e3;
|
|
13386
|
-
function parseChannelVersion(version) {
|
|
13387
|
-
const match = /v?(\d+)\.(\d+)\./.exec(version);
|
|
13388
|
-
if (!match) return null;
|
|
13389
|
-
return [Number(match[1]), Number(match[2])];
|
|
13390
|
-
}
|
|
13391
|
-
async function resolveChannelLatest(channel, fetchImpl = fetch) {
|
|
13392
|
-
try {
|
|
13393
|
-
const response = await fetchImpl(CHANNELS_URL, {
|
|
13394
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2),
|
|
13395
|
-
headers: { Accept: "application/json" }
|
|
13396
|
-
});
|
|
13397
|
-
if (!response.ok) return null;
|
|
13398
|
-
const body = await response.json();
|
|
13399
|
-
const match = body.data?.find((entry) => entry.id === channel || entry.name === channel);
|
|
13400
|
-
return match?.latest ? parseChannelVersion(match.latest) : null;
|
|
13401
|
-
} catch {
|
|
13402
|
-
return null;
|
|
13403
|
-
}
|
|
13404
|
-
}
|
|
13405
|
-
|
|
13406
13736
|
// src/daemon/connection.ts
|
|
13407
13737
|
import { io } from "socket.io-client";
|
|
13408
13738
|
|
|
@@ -13435,27 +13765,27 @@ function readControlFile(path) {
|
|
|
13435
13765
|
}
|
|
13436
13766
|
function controlAlias(apiUrl, text) {
|
|
13437
13767
|
if (!text) return void 0;
|
|
13438
|
-
let
|
|
13768
|
+
let hostname2;
|
|
13439
13769
|
let aliases;
|
|
13440
13770
|
try {
|
|
13441
|
-
|
|
13771
|
+
hostname2 = new URL(apiUrl).hostname.toLowerCase();
|
|
13442
13772
|
aliases = JSON.parse(text);
|
|
13443
13773
|
} catch {
|
|
13444
13774
|
return void 0;
|
|
13445
13775
|
}
|
|
13446
13776
|
if (typeof aliases !== "object" || aliases === null || Array.isArray(aliases)) return void 0;
|
|
13447
|
-
const alias = aliases[
|
|
13448
|
-
return typeof alias === "string" && CONTROL_ALIAS_IP.test(alias) ? { hostname, alias } : void 0;
|
|
13777
|
+
const alias = aliases[hostname2];
|
|
13778
|
+
return typeof alias === "string" && CONTROL_ALIAS_IP.test(alias) ? { hostname: hostname2, alias } : void 0;
|
|
13449
13779
|
}
|
|
13450
13780
|
function controlPlaneRoute(apiUrl, readFile9 = readControlFile, fileExists = existsSync) {
|
|
13451
13781
|
if (!fileExists(CONTROL_FILE)) return void 0;
|
|
13452
13782
|
let skipAlias = false;
|
|
13453
13783
|
let usedAlias = false;
|
|
13454
|
-
const lookup = (
|
|
13784
|
+
const lookup = (hostname2, options, callback) => {
|
|
13455
13785
|
const target2 = skipAlias ? void 0 : controlAlias(apiUrl, readFile9(CONTROL_FILE));
|
|
13456
|
-
usedAlias = target2 !== void 0 &&
|
|
13786
|
+
usedAlias = target2 !== void 0 && hostname2.toLowerCase() === target2.hostname;
|
|
13457
13787
|
if (!target2 || !usedAlias) {
|
|
13458
|
-
dnsLookup(
|
|
13788
|
+
dnsLookup(hostname2, options, callback);
|
|
13459
13789
|
return;
|
|
13460
13790
|
}
|
|
13461
13791
|
if (options.all) {
|
|
@@ -13500,7 +13830,7 @@ function createClusterProof(apiUrl, clusterToken, key) {
|
|
|
13500
13830
|
const origin = new URL(apiUrl).origin;
|
|
13501
13831
|
const timestamp = Date.now();
|
|
13502
13832
|
const nonce = randomBytes(32).toString("base64url");
|
|
13503
|
-
const
|
|
13833
|
+
const message3 = `conveyor-device-v1
|
|
13504
13834
|
${origin}
|
|
13505
13835
|
${clusterToken}
|
|
13506
13836
|
${timestamp}
|
|
@@ -13508,7 +13838,7 @@ ${nonce}`;
|
|
|
13508
13838
|
return {
|
|
13509
13839
|
timestamp,
|
|
13510
13840
|
nonce,
|
|
13511
|
-
signature: sign(null, Buffer.from(
|
|
13841
|
+
signature: sign(null, Buffer.from(message3, "utf8"), key).toString("base64url")
|
|
13512
13842
|
};
|
|
13513
13843
|
}
|
|
13514
13844
|
function deviceClusterAuth(apiUrl, clusterToken, keyPath = DEVICE_KEY_PATH, ownerUid = 0) {
|
|
@@ -13522,6 +13852,8 @@ function deviceClusterAuth(apiUrl, clusterToken, keyPath = DEVICE_KEY_PATH, owne
|
|
|
13522
13852
|
|
|
13523
13853
|
// src/daemon/connection.ts
|
|
13524
13854
|
init_dist();
|
|
13855
|
+
var REDIAL_FIRST_MS = 5e3;
|
|
13856
|
+
var REDIAL_MAX_MS = 5 * 6e4;
|
|
13525
13857
|
function unwrap(ack, what) {
|
|
13526
13858
|
if (!ack?.ok || ack.data === void 0) {
|
|
13527
13859
|
throw new Error(`${what} failed: ${ack?.error ?? "no response"}`);
|
|
@@ -13531,6 +13863,8 @@ function unwrap(ack, what) {
|
|
|
13531
13863
|
var ClusterConnection = class {
|
|
13532
13864
|
socket = null;
|
|
13533
13865
|
config;
|
|
13866
|
+
redialDelayMs = REDIAL_FIRST_MS;
|
|
13867
|
+
redialTimer = null;
|
|
13534
13868
|
/** Called when Conveyor says the desired state changed. */
|
|
13535
13869
|
onSyncRequested = null;
|
|
13536
13870
|
/** Called when Conveyor asks the machine to remove itself. */
|
|
@@ -13566,6 +13900,7 @@ var ClusterConnection = class {
|
|
|
13566
13900
|
});
|
|
13567
13901
|
this.socket = socket;
|
|
13568
13902
|
socket.on("connect", () => {
|
|
13903
|
+
this.redialDelayMs = REDIAL_FIRST_MS;
|
|
13569
13904
|
if (!settled) {
|
|
13570
13905
|
settled = true;
|
|
13571
13906
|
resolve();
|
|
@@ -13584,6 +13919,9 @@ var ClusterConnection = class {
|
|
|
13584
13919
|
socket.on("disconnect", (reason) => {
|
|
13585
13920
|
process.stderr.write(`[conveyor-k3] disconnected: ${reason}
|
|
13586
13921
|
`);
|
|
13922
|
+
if (reason === "io server disconnect" && this.config.reconnectWhenReplaced) {
|
|
13923
|
+
this.redial(socket);
|
|
13924
|
+
}
|
|
13587
13925
|
});
|
|
13588
13926
|
socket.on(PERSONAL_CLUSTER_EVENTS.syncRequested, () => {
|
|
13589
13927
|
this.onSyncRequested?.();
|
|
@@ -13645,217 +13983,68 @@ var ClusterConnection = class {
|
|
|
13645
13983
|
} catch {
|
|
13646
13984
|
}
|
|
13647
13985
|
}
|
|
13986
|
+
/** Dial again after the server closed the socket, waiting longer each time. */
|
|
13987
|
+
redial(socket) {
|
|
13988
|
+
if (this.redialTimer) return;
|
|
13989
|
+
const delay2 = this.redialDelayMs;
|
|
13990
|
+
this.redialDelayMs = Math.min(delay2 * 2, REDIAL_MAX_MS);
|
|
13991
|
+
this.redialTimer = setTimeout(() => {
|
|
13992
|
+
this.redialTimer = null;
|
|
13993
|
+
if (this.socket === socket && !socket.connected) socket.connect();
|
|
13994
|
+
}, delay2);
|
|
13995
|
+
this.redialTimer.unref?.();
|
|
13996
|
+
}
|
|
13648
13997
|
disconnect() {
|
|
13998
|
+
if (this.redialTimer) clearTimeout(this.redialTimer);
|
|
13999
|
+
this.redialTimer = null;
|
|
13649
14000
|
this.socket?.disconnect();
|
|
13650
14001
|
this.socket = null;
|
|
13651
14002
|
}
|
|
13652
14003
|
};
|
|
13653
14004
|
|
|
13654
|
-
// src/
|
|
13655
|
-
|
|
13656
|
-
|
|
13657
|
-
|
|
13658
|
-
|
|
13659
|
-
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
|
|
13663
|
-
|
|
13664
|
-
|
|
13665
|
-
|
|
13666
|
-
|
|
13667
|
-
|
|
13668
|
-
|
|
13669
|
-
const wanted = new Set(wantedImages);
|
|
13670
|
-
const candidates = [];
|
|
13671
|
-
for (const image of inventory.images) {
|
|
13672
|
-
const tags = image.repoTags ?? [];
|
|
13673
|
-
if (tags.length > 0) continue;
|
|
13674
|
-
if (inventory.referencedIds.has(image.id)) continue;
|
|
13675
|
-
if (refsOf(image).some((ref) => wanted.has(ref))) continue;
|
|
13676
|
-
candidates.push({ id: image.id, sizeBytes: parseSize(image.size) });
|
|
13677
|
-
}
|
|
13678
|
-
return candidates;
|
|
13679
|
-
}
|
|
13680
|
-
async function readImageInventory(exec) {
|
|
13681
|
-
const imagesResult = await exec("crictl", ["images", "-o", "json"]);
|
|
13682
|
-
const containersResult = await exec("crictl", ["ps", "-a", "-o", "json"]);
|
|
13683
|
-
if (imagesResult.exitCode !== 0) {
|
|
13684
|
-
throw new Error(
|
|
13685
|
-
`could not read the image list: ${imagesResult.stderr.trim().slice(0, 120) || `crictl images exited ${imagesResult.exitCode}`}`
|
|
13686
|
-
);
|
|
13687
|
-
}
|
|
13688
|
-
let images = [];
|
|
14005
|
+
// src/lib/manifest-guard.ts
|
|
14006
|
+
import { isMap, isScalar, parseAllDocuments } from "yaml";
|
|
14007
|
+
var ALLOWED_MANIFEST_KINDS = [
|
|
14008
|
+
"Namespace",
|
|
14009
|
+
"Role",
|
|
14010
|
+
"RoleBinding",
|
|
14011
|
+
"ClusterRole",
|
|
14012
|
+
"ClusterRoleBinding",
|
|
14013
|
+
"PriorityClass"
|
|
14014
|
+
];
|
|
14015
|
+
var ALLOWED = new Set(ALLOWED_MANIFEST_KINDS);
|
|
14016
|
+
var MISSING_KIND = "(missing kind)";
|
|
14017
|
+
var UNPARSEABLE = "(unparseable)";
|
|
14018
|
+
function findDisallowedKinds(manifest) {
|
|
14019
|
+
let documents;
|
|
13689
14020
|
try {
|
|
13690
|
-
|
|
13691
|
-
images = parsed.images ?? [];
|
|
14021
|
+
documents = parseAllDocuments(manifest);
|
|
13692
14022
|
} catch {
|
|
13693
|
-
|
|
14023
|
+
return [UNPARSEABLE];
|
|
13694
14024
|
}
|
|
13695
|
-
const
|
|
13696
|
-
|
|
13697
|
-
|
|
13698
|
-
|
|
13699
|
-
|
|
13700
|
-
|
|
14025
|
+
const disallowed = [];
|
|
14026
|
+
const record2 = (kind) => {
|
|
14027
|
+
if (!disallowed.includes(kind)) disallowed.push(kind);
|
|
14028
|
+
};
|
|
14029
|
+
for (const doc of documents) {
|
|
14030
|
+
if (doc.errors.length > 0) {
|
|
14031
|
+
record2(UNPARSEABLE);
|
|
14032
|
+
continue;
|
|
13701
14033
|
}
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
if (removal.exitCode === 0) {
|
|
13713
|
-
result.removed += 1;
|
|
13714
|
-
result.bytesReclaimed += candidate.sizeBytes;
|
|
13715
|
-
} else {
|
|
13716
|
-
result.failures.push(`${candidate.id}: ${removal.stderr.trim().slice(0, 120)}`);
|
|
14034
|
+
if (doc.contents === null || doc.contents === void 0) continue;
|
|
14035
|
+
if (isScalar(doc.contents) && doc.contents.value === null) continue;
|
|
14036
|
+
if (!isMap(doc.contents)) {
|
|
14037
|
+
record2(MISSING_KIND);
|
|
14038
|
+
continue;
|
|
14039
|
+
}
|
|
14040
|
+
const kind = doc.get("kind");
|
|
14041
|
+
if (typeof kind !== "string" || kind.length === 0) {
|
|
14042
|
+
record2(MISSING_KIND);
|
|
14043
|
+
continue;
|
|
13717
14044
|
}
|
|
14045
|
+
if (!ALLOWED.has(kind)) record2(kind);
|
|
13718
14046
|
}
|
|
13719
|
-
return
|
|
13720
|
-
}
|
|
13721
|
-
async function warmImages(exec, wantedImages, unauthenticatedHosts) {
|
|
13722
|
-
const blocked = new Set(unauthenticatedHosts);
|
|
13723
|
-
const result = { pulled: [], skipped: [], failures: [], skippedArtifactRegistry: 0 };
|
|
13724
|
-
for (const ref of wantedImages) {
|
|
13725
|
-
const host = ref.split("/")[0] ?? "";
|
|
13726
|
-
if (host === "localhost" || host.startsWith("localhost:")) {
|
|
13727
|
-
result.skipped.push(ref);
|
|
13728
|
-
continue;
|
|
13729
|
-
}
|
|
13730
|
-
if (isArtifactRegistryHost(host)) {
|
|
13731
|
-
result.skipped.push(ref);
|
|
13732
|
-
result.skippedArtifactRegistry += 1;
|
|
13733
|
-
continue;
|
|
13734
|
-
}
|
|
13735
|
-
if (blocked.has(host)) {
|
|
13736
|
-
result.skipped.push(ref);
|
|
13737
|
-
continue;
|
|
13738
|
-
}
|
|
13739
|
-
try {
|
|
13740
|
-
assertImageRef(ref);
|
|
13741
|
-
} catch {
|
|
13742
|
-
result.failures.push(`${ref}: not a valid image reference`);
|
|
13743
|
-
continue;
|
|
13744
|
-
}
|
|
13745
|
-
const pull = await exec("crictl", ["pull", ref], { timeoutMs: 15 * 6e4 });
|
|
13746
|
-
if (pull.exitCode === 0) {
|
|
13747
|
-
result.pulled.push(ref);
|
|
13748
|
-
} else {
|
|
13749
|
-
result.failures.push(`${ref}: ${pull.stderr.trim().slice(0, 120)}`);
|
|
13750
|
-
}
|
|
13751
|
-
}
|
|
13752
|
-
return result;
|
|
13753
|
-
}
|
|
13754
|
-
var PIN_LABEL = "io.cri-containerd.pinned";
|
|
13755
|
-
async function resolveImageLabelTool(exec) {
|
|
13756
|
-
if (await commandExists(exec, "k3s")) {
|
|
13757
|
-
return { bin: "k3s", args: ["ctr", "--namespace", "k8s.io", "images", "label"] };
|
|
13758
|
-
}
|
|
13759
|
-
if (await commandExists(exec, "ctr")) {
|
|
13760
|
-
return { bin: "ctr", args: ["--namespace", "k8s.io", "images", "label"] };
|
|
13761
|
-
}
|
|
13762
|
-
return null;
|
|
13763
|
-
}
|
|
13764
|
-
function refsPresent(inventory) {
|
|
13765
|
-
const present = /* @__PURE__ */ new Set();
|
|
13766
|
-
for (const image of inventory.images) {
|
|
13767
|
-
for (const ref of refsOf(image)) present.add(ref);
|
|
13768
|
-
}
|
|
13769
|
-
return present;
|
|
13770
|
-
}
|
|
13771
|
-
async function pinWantedImages(exec, wantedImages, inventory, previouslyPinned = []) {
|
|
13772
|
-
const result = { pinned: [], unpinned: [], absent: [], failures: [] };
|
|
13773
|
-
const wanted = new Set(wantedImages);
|
|
13774
|
-
const present = refsPresent(inventory);
|
|
13775
|
-
const toPin = wantedImages.filter((ref) => present.has(ref));
|
|
13776
|
-
const toUnpin = previouslyPinned.filter((ref) => !wanted.has(ref) && present.has(ref));
|
|
13777
|
-
for (const ref of wantedImages) if (!present.has(ref)) result.absent.push(ref);
|
|
13778
|
-
if (toPin.length === 0 && toUnpin.length === 0) return result;
|
|
13779
|
-
const tool = await resolveImageLabelTool(exec);
|
|
13780
|
-
if (!tool) {
|
|
13781
|
-
result.warning = "No `k3s ctr` or `ctr` on this machine, so Conveyor cannot pin its images against kubelet garbage collection. A freshly baked image can be deleted before the first pod runs it.";
|
|
13782
|
-
return result;
|
|
13783
|
-
}
|
|
13784
|
-
const label2 = async (ref, value) => {
|
|
13785
|
-
try {
|
|
13786
|
-
assertImageRef(ref);
|
|
13787
|
-
} catch {
|
|
13788
|
-
result.failures.push(`${ref}: not a valid image reference`);
|
|
13789
|
-
return false;
|
|
13790
|
-
}
|
|
13791
|
-
const run = await exec(tool.bin, [...tool.args, ref, `${PIN_LABEL}=${value}`], {
|
|
13792
|
-
timeoutMs: 3e4
|
|
13793
|
-
});
|
|
13794
|
-
if (run.exitCode === 0) return true;
|
|
13795
|
-
result.failures.push(`${ref}: ${run.stderr.trim().slice(0, 120)}`);
|
|
13796
|
-
return false;
|
|
13797
|
-
};
|
|
13798
|
-
for (const ref of toPin) {
|
|
13799
|
-
if (await label2(ref, "pinned")) result.pinned.push(ref);
|
|
13800
|
-
}
|
|
13801
|
-
for (const ref of toUnpin) {
|
|
13802
|
-
if (await label2(ref, "")) result.unpinned.push(ref);
|
|
13803
|
-
}
|
|
13804
|
-
return result;
|
|
13805
|
-
}
|
|
13806
|
-
function countPinnedWanted(inventory, wantedImages) {
|
|
13807
|
-
const wanted = new Set(wantedImages);
|
|
13808
|
-
return inventory.images.filter(
|
|
13809
|
-
(image) => image.pinned === true && refsOf(image).some((ref) => wanted.has(ref))
|
|
13810
|
-
).length;
|
|
13811
|
-
}
|
|
13812
|
-
|
|
13813
|
-
// src/daemon/runner.ts
|
|
13814
|
-
init_paths();
|
|
13815
|
-
|
|
13816
|
-
// src/lib/manifest-guard.ts
|
|
13817
|
-
import { isMap, isScalar, parseAllDocuments } from "yaml";
|
|
13818
|
-
var ALLOWED_MANIFEST_KINDS = [
|
|
13819
|
-
"Namespace",
|
|
13820
|
-
"Role",
|
|
13821
|
-
"RoleBinding",
|
|
13822
|
-
"ClusterRole",
|
|
13823
|
-
"ClusterRoleBinding",
|
|
13824
|
-
"PriorityClass"
|
|
13825
|
-
];
|
|
13826
|
-
var ALLOWED = new Set(ALLOWED_MANIFEST_KINDS);
|
|
13827
|
-
var MISSING_KIND = "(missing kind)";
|
|
13828
|
-
var UNPARSEABLE = "(unparseable)";
|
|
13829
|
-
function findDisallowedKinds(manifest) {
|
|
13830
|
-
let documents;
|
|
13831
|
-
try {
|
|
13832
|
-
documents = parseAllDocuments(manifest);
|
|
13833
|
-
} catch {
|
|
13834
|
-
return [UNPARSEABLE];
|
|
13835
|
-
}
|
|
13836
|
-
const disallowed = [];
|
|
13837
|
-
const record2 = (kind) => {
|
|
13838
|
-
if (!disallowed.includes(kind)) disallowed.push(kind);
|
|
13839
|
-
};
|
|
13840
|
-
for (const doc of documents) {
|
|
13841
|
-
if (doc.errors.length > 0) {
|
|
13842
|
-
record2(UNPARSEABLE);
|
|
13843
|
-
continue;
|
|
13844
|
-
}
|
|
13845
|
-
if (doc.contents === null || doc.contents === void 0) continue;
|
|
13846
|
-
if (isScalar(doc.contents) && doc.contents.value === null) continue;
|
|
13847
|
-
if (!isMap(doc.contents)) {
|
|
13848
|
-
record2(MISSING_KIND);
|
|
13849
|
-
continue;
|
|
13850
|
-
}
|
|
13851
|
-
const kind = doc.get("kind");
|
|
13852
|
-
if (typeof kind !== "string" || kind.length === 0) {
|
|
13853
|
-
record2(MISSING_KIND);
|
|
13854
|
-
continue;
|
|
13855
|
-
}
|
|
13856
|
-
if (!ALLOWED.has(kind)) record2(kind);
|
|
13857
|
-
}
|
|
13858
|
-
return disallowed;
|
|
14047
|
+
return disallowed;
|
|
13859
14048
|
}
|
|
13860
14049
|
|
|
13861
14050
|
// src/daemon/reconcile.ts
|
|
@@ -13933,6 +14122,80 @@ async function loadRegistryMirror(ctx) {
|
|
|
13933
14122
|
return { restarted: true, warning: null };
|
|
13934
14123
|
}
|
|
13935
14124
|
|
|
14125
|
+
// src/daemon/namespace-upkeep.ts
|
|
14126
|
+
init_dist();
|
|
14127
|
+
init_exec();
|
|
14128
|
+
init_safe_arg();
|
|
14129
|
+
|
|
14130
|
+
// src/daemon/isolation-probes.ts
|
|
14131
|
+
init_paths();
|
|
14132
|
+
var CONTROLLER_DISABLED_WARNING = "k3s runs with its network policy controller disabled, so the egress policy Conveyor applies here is not enforced. Remove disable-network-policy from the k3s config or unit and restart k3s. Until then this server is not isolated; Conveyor checks again within 10 minutes.";
|
|
14133
|
+
var CONTROLLER_UNKNOWN_WARNING = "Could not read the k3s unit or its config files to check that its network policy controller is on, so this server does not count as isolated. Check `systemctl cat k3s.service` and /etc/rancher/k3s; Conveyor checks again within 10 minutes.";
|
|
14134
|
+
var NODE_ADDRESSES_JSONPATH = '{range .items[*]}{range .status.addresses[?(@.type=="InternalIP")]}{.address}{"\\n"}{end}{range .status.addresses[?(@.type=="ExternalIP")]}{.address}{"\\n"}{end}{end}';
|
|
14135
|
+
var API_ENDPOINTS_JSONPATH = "{.items[*].endpoints[*].addresses[*]}";
|
|
14136
|
+
var IPV4_ON_INTERFACE = /\binet (\d{1,3}(?:\.\d{1,3}){3})(?:\/| peer )/g;
|
|
14137
|
+
async function stdoutOf(exec, bin, args) {
|
|
14138
|
+
const run = await exec(bin, args, { timeoutMs: 1e4 }).catch(() => null);
|
|
14139
|
+
return run && run.exitCode === 0 ? run.stdout : null;
|
|
14140
|
+
}
|
|
14141
|
+
async function readK3sConfigs(ctx) {
|
|
14142
|
+
const listed = await stdoutOf(ctx.exec, "find", [
|
|
14143
|
+
"-L",
|
|
14144
|
+
K3S_CONFIG_DROPIN_DIR,
|
|
14145
|
+
"-maxdepth",
|
|
14146
|
+
"1",
|
|
14147
|
+
"-type",
|
|
14148
|
+
"f",
|
|
14149
|
+
"(",
|
|
14150
|
+
"-iname",
|
|
14151
|
+
"*.yaml",
|
|
14152
|
+
"-o",
|
|
14153
|
+
"-iname",
|
|
14154
|
+
"*.yml",
|
|
14155
|
+
")"
|
|
14156
|
+
]);
|
|
14157
|
+
if (listed === null) return null;
|
|
14158
|
+
const paths = listed.split("\n").map((path) => path.trim()).filter(Boolean).sort();
|
|
14159
|
+
const dropIns = await Promise.all(paths.map((path) => ctx.fs.readFile(path)));
|
|
14160
|
+
if (dropIns.some((text) => text === null)) return null;
|
|
14161
|
+
return [await ctx.fs.readFile(K3S_OWNER_CONFIG), ...dropIns];
|
|
14162
|
+
}
|
|
14163
|
+
async function controllerProblem(ctx) {
|
|
14164
|
+
const unitText = await stdoutOf(ctx.exec, "systemctl", ["cat", "k3s.service"]);
|
|
14165
|
+
const configs = unitText === null ? null : await readK3sConfigs(ctx);
|
|
14166
|
+
if (unitText === null || configs === null) return CONTROLLER_UNKNOWN_WARNING;
|
|
14167
|
+
return networkPolicyControllerDisabled({ configs, unitText }) ? CONTROLLER_DISABLED_WARNING : null;
|
|
14168
|
+
}
|
|
14169
|
+
async function readHostAddresses(exec) {
|
|
14170
|
+
const nodes = await stdoutOf(exec, "k3s", [
|
|
14171
|
+
"kubectl",
|
|
14172
|
+
"get",
|
|
14173
|
+
"nodes",
|
|
14174
|
+
"-o",
|
|
14175
|
+
`jsonpath=${NODE_ADDRESSES_JSONPATH}`
|
|
14176
|
+
]);
|
|
14177
|
+
const interfaces = await stdoutOf(exec, "ip", ["-o", "-4", "addr", "show"]);
|
|
14178
|
+
const api = await stdoutOf(exec, "k3s", [
|
|
14179
|
+
"kubectl",
|
|
14180
|
+
"get",
|
|
14181
|
+
"endpointslices",
|
|
14182
|
+
"-n",
|
|
14183
|
+
"default",
|
|
14184
|
+
"-l",
|
|
14185
|
+
"kubernetes.io/service-name=kubernetes",
|
|
14186
|
+
"-o",
|
|
14187
|
+
`jsonpath=${API_ENDPOINTS_JSONPATH}`
|
|
14188
|
+
]);
|
|
14189
|
+
if (nodes === null || interfaces === null || api === null) return null;
|
|
14190
|
+
const onInterfaces = [...interfaces.matchAll(IPV4_ON_INTERFACE)].map((match) => match[1] ?? "");
|
|
14191
|
+
return [...nodes.split(/\s+/), ...onInterfaces, ...api.split(/\s+/)].filter(Boolean);
|
|
14192
|
+
}
|
|
14193
|
+
async function recheckIsolation(ctx, reconciled) {
|
|
14194
|
+
if (reconciled?.applied !== true) return { isolation: reconciled, warnings: [] };
|
|
14195
|
+
const problem = await controllerProblem(ctx);
|
|
14196
|
+
return problem ? { isolation: { applied: false }, warnings: [problem] } : { isolation: reconciled, warnings: [] };
|
|
14197
|
+
}
|
|
14198
|
+
|
|
13936
14199
|
// src/daemon/prune-namespaces.ts
|
|
13937
14200
|
init_safe_arg();
|
|
13938
14201
|
var POOL_NAMESPACE_PREFIX = "claudespace-";
|
|
@@ -13996,6 +14259,143 @@ async function pruneStaleNamespaces(ctx, desired, result) {
|
|
|
13996
14259
|
}
|
|
13997
14260
|
}
|
|
13998
14261
|
|
|
14262
|
+
// src/daemon/namespace-upkeep.ts
|
|
14263
|
+
function message(error) {
|
|
14264
|
+
return error instanceof Error ? error.message : String(error);
|
|
14265
|
+
}
|
|
14266
|
+
function renderLimitRange(namespace, limits) {
|
|
14267
|
+
const ns = assertK8sName(namespace, "namespace");
|
|
14268
|
+
const values = [
|
|
14269
|
+
assertQuantity(limits.defaultRequest.cpu, "defaultRequest.cpu"),
|
|
14270
|
+
assertQuantity(limits.defaultRequest.memory, "defaultRequest.memory"),
|
|
14271
|
+
assertQuantity(limits.default.cpu, "default.cpu"),
|
|
14272
|
+
assertQuantity(limits.default.memory, "default.memory")
|
|
14273
|
+
];
|
|
14274
|
+
return [
|
|
14275
|
+
"apiVersion: v1",
|
|
14276
|
+
"kind: LimitRange",
|
|
14277
|
+
"metadata:",
|
|
14278
|
+
" name: conveyor-k3-defaults",
|
|
14279
|
+
` namespace: ${ns}`,
|
|
14280
|
+
"spec:",
|
|
14281
|
+
" limits:",
|
|
14282
|
+
" - type: Container",
|
|
14283
|
+
" defaultRequest:",
|
|
14284
|
+
` cpu: "${values[0]}"`,
|
|
14285
|
+
` memory: "${values[1]}"`,
|
|
14286
|
+
" default:",
|
|
14287
|
+
` cpu: "${values[2]}"`,
|
|
14288
|
+
` memory: "${values[3]}"`,
|
|
14289
|
+
""
|
|
14290
|
+
].join("\n");
|
|
14291
|
+
}
|
|
14292
|
+
async function reconcileNamespaces(ctx, desired, result) {
|
|
14293
|
+
for (const namespace of desired.namespaces) {
|
|
14294
|
+
try {
|
|
14295
|
+
await kubectlApply(ctx.exec, renderLimitRange(namespace.name, namespace.limitRange));
|
|
14296
|
+
} catch (error) {
|
|
14297
|
+
result.warnings.push(
|
|
14298
|
+
`Could not apply resource defaults to ${namespace.name} (${namespace.projectName}): ${message(error)}`
|
|
14299
|
+
);
|
|
14300
|
+
}
|
|
14301
|
+
}
|
|
14302
|
+
}
|
|
14303
|
+
async function ensureBakeNamespace(ctx, desired) {
|
|
14304
|
+
const found = await ctx.exec(
|
|
14305
|
+
"k3s",
|
|
14306
|
+
["kubectl", "get", "namespace", PERSONAL_BAKE_NAMESPACE],
|
|
14307
|
+
KUBECTL_TIMEOUT2
|
|
14308
|
+
);
|
|
14309
|
+
if (found.exitCode !== 0) {
|
|
14310
|
+
await runOrThrow(
|
|
14311
|
+
ctx.exec,
|
|
14312
|
+
"k3s",
|
|
14313
|
+
["kubectl", "create", "namespace", PERSONAL_BAKE_NAMESPACE],
|
|
14314
|
+
KUBECTL_TIMEOUT2
|
|
14315
|
+
);
|
|
14316
|
+
}
|
|
14317
|
+
const limitRange = desired.namespaces[0]?.limitRange;
|
|
14318
|
+
if (limitRange) {
|
|
14319
|
+
await kubectlApply(ctx.exec, renderLimitRange(PERSONAL_BAKE_NAMESPACE, limitRange));
|
|
14320
|
+
}
|
|
14321
|
+
}
|
|
14322
|
+
function knownStance(isolation) {
|
|
14323
|
+
const { podSecurity, egress, ...rest } = { ...isolation };
|
|
14324
|
+
const extra = Object.keys(rest);
|
|
14325
|
+
if (podSecurity === "baseline" && egress === "no-private" && extra.length === 0) return null;
|
|
14326
|
+
const named = `Pod Security "${String(podSecurity).slice(0, 40)}", egress "${String(egress).slice(0, 40)}"`;
|
|
14327
|
+
return extra.length === 0 ? named : `${named}, and ${extra.slice(0, 3).join(", ").slice(0, 80)}`;
|
|
14328
|
+
}
|
|
14329
|
+
var KUBECTL_TIMEOUT2 = { timeoutMs: 3e4 };
|
|
14330
|
+
var RECHECK = "Conveyor checks again within 10 minutes.";
|
|
14331
|
+
async function isolateNamespace(ctx, namespace, isolation, fail2) {
|
|
14332
|
+
const ns = assertK8sName(namespace, "namespace");
|
|
14333
|
+
const labels = Object.entries(psaLabels(isolation.podSecurity)).map(
|
|
14334
|
+
([key, value]) => `${key}=${value}`
|
|
14335
|
+
);
|
|
14336
|
+
const labelled = await ctx.exec(
|
|
14337
|
+
"k3s",
|
|
14338
|
+
["kubectl", "label", "namespace", ns, "--overwrite", ...labels],
|
|
14339
|
+
KUBECTL_TIMEOUT2
|
|
14340
|
+
);
|
|
14341
|
+
if (labelled.exitCode !== 0) {
|
|
14342
|
+
fail2(
|
|
14343
|
+
`Could not apply Pod Security labels to ${ns}: ${labelled.stderr.trim().slice(0, 200)} ${RECHECK}`
|
|
14344
|
+
);
|
|
14345
|
+
}
|
|
14346
|
+
if (isolation.hostAddresses === null) return;
|
|
14347
|
+
try {
|
|
14348
|
+
await kubectlApply(ctx.exec, renderEgressNetworkPolicy(ns, isolation.hostAddresses));
|
|
14349
|
+
} catch (error) {
|
|
14350
|
+
fail2(`Could not apply the egress policy to ${ns}: ${message(error).slice(0, 200)} ${RECHECK}`);
|
|
14351
|
+
}
|
|
14352
|
+
}
|
|
14353
|
+
async function reconcileIsolation(ctx, desired, result) {
|
|
14354
|
+
try {
|
|
14355
|
+
await ensureBakeNamespace(ctx, desired);
|
|
14356
|
+
} catch (error) {
|
|
14357
|
+
result.warnings.push(
|
|
14358
|
+
`Could not prepare the bake namespace ${PERSONAL_BAKE_NAMESPACE}: ${message(error)}`
|
|
14359
|
+
);
|
|
14360
|
+
}
|
|
14361
|
+
const isolation = desired.isolation;
|
|
14362
|
+
if (!isolation) {
|
|
14363
|
+
result.isolation = void 0;
|
|
14364
|
+
return;
|
|
14365
|
+
}
|
|
14366
|
+
const unknown = knownStance(isolation);
|
|
14367
|
+
if (unknown) {
|
|
14368
|
+
result.warnings.push(
|
|
14369
|
+
`Conveyor asks for pod isolation this version of conveyor-k3 does not know (${unknown}), so nothing was changed and this server does not count as isolated. Run \`sudo conveyor-k3 update\`.`
|
|
14370
|
+
);
|
|
14371
|
+
result.isolation = { applied: false };
|
|
14372
|
+
return;
|
|
14373
|
+
}
|
|
14374
|
+
let applied = true;
|
|
14375
|
+
const fail2 = (warning) => {
|
|
14376
|
+
applied = false;
|
|
14377
|
+
result.warnings.push(warning);
|
|
14378
|
+
};
|
|
14379
|
+
const problem = await controllerProblem(ctx);
|
|
14380
|
+
if (problem) fail2(problem);
|
|
14381
|
+
const hostAddresses = await readHostAddresses(ctx.exec);
|
|
14382
|
+
if (hostAddresses === null) {
|
|
14383
|
+
fail2(
|
|
14384
|
+
`Could not read every address this server answers on, so the egress policies in place were left as they are and this server does not count as isolated. ${RECHECK}`
|
|
14385
|
+
);
|
|
14386
|
+
}
|
|
14387
|
+
for (const namespace of desired.namespaces) {
|
|
14388
|
+
await isolateNamespace(ctx, namespace.name, { podSecurity: "baseline", hostAddresses }, fail2);
|
|
14389
|
+
}
|
|
14390
|
+
await isolateNamespace(
|
|
14391
|
+
ctx,
|
|
14392
|
+
PERSONAL_BAKE_NAMESPACE,
|
|
14393
|
+
{ podSecurity: "privileged", hostAddresses },
|
|
14394
|
+
fail2
|
|
14395
|
+
);
|
|
14396
|
+
result.isolation = { applied };
|
|
14397
|
+
}
|
|
14398
|
+
|
|
13999
14399
|
// src/daemon/telemetry.ts
|
|
14000
14400
|
init_dist();
|
|
14001
14401
|
import { statfs } from "fs/promises";
|
|
@@ -14028,30 +14428,186 @@ async function inspectContentHash(exec, imageId) {
|
|
|
14028
14428
|
return void 0;
|
|
14029
14429
|
}
|
|
14030
14430
|
}
|
|
14031
|
-
async function reportLocalImages(exec, inventory, wantedImages, cache) {
|
|
14032
|
-
if (!inventory) return void 0;
|
|
14431
|
+
async function reportLocalImages(exec, inventory, wantedImages, cache) {
|
|
14432
|
+
if (!inventory) return void 0;
|
|
14433
|
+
const wanted = new Set(wantedImages);
|
|
14434
|
+
const held = inventory.images.flatMap(
|
|
14435
|
+
(image) => (image.repoTags ?? []).filter((ref) => ref.startsWith("localhost/") && isImageRef(ref)).map((ref) => ({ ref, imageId: image.id }))
|
|
14436
|
+
);
|
|
14437
|
+
held.sort(
|
|
14438
|
+
(a, b) => Number(wanted.has(b.ref)) - Number(wanted.has(a.ref)) || (a.ref < b.ref ? -1 : a.ref > b.ref ? 1 : 0)
|
|
14439
|
+
);
|
|
14440
|
+
const reported = held.slice(0, MAX_LOCAL_IMAGES);
|
|
14441
|
+
const live = new Set(reported.map((entry) => entry.imageId));
|
|
14442
|
+
for (const id2 of [...cache.keys()]) if (!live.has(id2)) cache.delete(id2);
|
|
14443
|
+
const images = [];
|
|
14444
|
+
let inspections = 0;
|
|
14445
|
+
for (const entry of reported) {
|
|
14446
|
+
let contentHash = cache.get(entry.imageId);
|
|
14447
|
+
if (contentHash === void 0 && inspections < MAX_INSPECTIONS_PER_BEAT) {
|
|
14448
|
+
inspections += 1;
|
|
14449
|
+
contentHash = await inspectContentHash(exec, entry.imageId);
|
|
14450
|
+
if (contentHash !== void 0) cache.set(entry.imageId, contentHash);
|
|
14451
|
+
}
|
|
14452
|
+
images.push({ ...entry, contentHash: contentHash ?? null });
|
|
14453
|
+
}
|
|
14454
|
+
return images;
|
|
14455
|
+
}
|
|
14456
|
+
|
|
14457
|
+
// src/lib/images.ts
|
|
14458
|
+
init_dist();
|
|
14459
|
+
init_exec();
|
|
14460
|
+
init_safe_arg();
|
|
14461
|
+
function parseSize(size) {
|
|
14462
|
+
const n = Number.parseInt(size ?? "0", 10);
|
|
14463
|
+
return Number.isFinite(n) ? n : 0;
|
|
14464
|
+
}
|
|
14465
|
+
function refsOf(image) {
|
|
14466
|
+
return [...image.repoTags ?? [], ...image.repoDigests ?? []];
|
|
14467
|
+
}
|
|
14468
|
+
function selectStrandedImages(inventory, wantedImages) {
|
|
14469
|
+
const wanted = new Set(wantedImages);
|
|
14470
|
+
const candidates = [];
|
|
14471
|
+
for (const image of inventory.images) {
|
|
14472
|
+
const tags = image.repoTags ?? [];
|
|
14473
|
+
if (tags.length > 0) continue;
|
|
14474
|
+
if (inventory.referencedIds.has(image.id)) continue;
|
|
14475
|
+
if (refsOf(image).some((ref) => wanted.has(ref))) continue;
|
|
14476
|
+
candidates.push({ id: image.id, sizeBytes: parseSize(image.size) });
|
|
14477
|
+
}
|
|
14478
|
+
return candidates;
|
|
14479
|
+
}
|
|
14480
|
+
async function readImageInventory(exec) {
|
|
14481
|
+
const imagesResult = await exec("crictl", ["images", "-o", "json"]);
|
|
14482
|
+
const containersResult = await exec("crictl", ["ps", "-a", "-o", "json"]);
|
|
14483
|
+
if (imagesResult.exitCode !== 0) {
|
|
14484
|
+
throw new Error(
|
|
14485
|
+
`could not read the image list: ${imagesResult.stderr.trim().slice(0, 120) || `crictl images exited ${imagesResult.exitCode}`}`
|
|
14486
|
+
);
|
|
14487
|
+
}
|
|
14488
|
+
let images = [];
|
|
14489
|
+
try {
|
|
14490
|
+
const parsed = JSON.parse(imagesResult.stdout || "{}");
|
|
14491
|
+
images = parsed.images ?? [];
|
|
14492
|
+
} catch {
|
|
14493
|
+
images = [];
|
|
14494
|
+
}
|
|
14495
|
+
const referencedIds = /* @__PURE__ */ new Set();
|
|
14496
|
+
try {
|
|
14497
|
+
const parsed = JSON.parse(containersResult.stdout || "{}");
|
|
14498
|
+
for (const container of parsed.containers ?? []) {
|
|
14499
|
+
if (container.imageRef) referencedIds.add(container.imageRef);
|
|
14500
|
+
if (container.image?.image) referencedIds.add(container.image.image);
|
|
14501
|
+
}
|
|
14502
|
+
} catch {
|
|
14503
|
+
throw new Error("could not read the container list; skipping prune this cycle");
|
|
14504
|
+
}
|
|
14505
|
+
return { images, referencedIds };
|
|
14506
|
+
}
|
|
14507
|
+
async function pruneStrandedImages(exec, inventory, wantedImages) {
|
|
14508
|
+
const candidates = selectStrandedImages(inventory, wantedImages);
|
|
14509
|
+
const result = { removed: 0, bytesReclaimed: 0, failures: [] };
|
|
14510
|
+
for (const candidate of candidates) {
|
|
14511
|
+
const removal = await exec("crictl", ["rmi", candidate.id]);
|
|
14512
|
+
if (removal.exitCode === 0) {
|
|
14513
|
+
result.removed += 1;
|
|
14514
|
+
result.bytesReclaimed += candidate.sizeBytes;
|
|
14515
|
+
} else {
|
|
14516
|
+
result.failures.push(`${candidate.id}: ${removal.stderr.trim().slice(0, 120)}`);
|
|
14517
|
+
}
|
|
14518
|
+
}
|
|
14519
|
+
return result;
|
|
14520
|
+
}
|
|
14521
|
+
async function warmImages(exec, wantedImages, unauthenticatedHosts) {
|
|
14522
|
+
const blocked = new Set(unauthenticatedHosts);
|
|
14523
|
+
const result = { pulled: [], skipped: [], failures: [], skippedArtifactRegistry: 0 };
|
|
14524
|
+
for (const ref of wantedImages) {
|
|
14525
|
+
const host = ref.split("/")[0] ?? "";
|
|
14526
|
+
if (host === "localhost" || host.startsWith("localhost:")) {
|
|
14527
|
+
result.skipped.push(ref);
|
|
14528
|
+
continue;
|
|
14529
|
+
}
|
|
14530
|
+
if (isArtifactRegistryHost(host)) {
|
|
14531
|
+
result.skipped.push(ref);
|
|
14532
|
+
result.skippedArtifactRegistry += 1;
|
|
14533
|
+
continue;
|
|
14534
|
+
}
|
|
14535
|
+
if (blocked.has(host)) {
|
|
14536
|
+
result.skipped.push(ref);
|
|
14537
|
+
continue;
|
|
14538
|
+
}
|
|
14539
|
+
try {
|
|
14540
|
+
assertImageRef(ref);
|
|
14541
|
+
} catch {
|
|
14542
|
+
result.failures.push(`${ref}: not a valid image reference`);
|
|
14543
|
+
continue;
|
|
14544
|
+
}
|
|
14545
|
+
const pull = await exec("crictl", ["pull", ref], { timeoutMs: 15 * 6e4 });
|
|
14546
|
+
if (pull.exitCode === 0) {
|
|
14547
|
+
result.pulled.push(ref);
|
|
14548
|
+
} else {
|
|
14549
|
+
result.failures.push(`${ref}: ${pull.stderr.trim().slice(0, 120)}`);
|
|
14550
|
+
}
|
|
14551
|
+
}
|
|
14552
|
+
return result;
|
|
14553
|
+
}
|
|
14554
|
+
var PIN_LABEL = "io.cri-containerd.pinned";
|
|
14555
|
+
async function resolveImageLabelTool(exec) {
|
|
14556
|
+
if (await commandExists(exec, "k3s")) {
|
|
14557
|
+
return { bin: "k3s", args: ["ctr", "--namespace", "k8s.io", "images", "label"] };
|
|
14558
|
+
}
|
|
14559
|
+
if (await commandExists(exec, "ctr")) {
|
|
14560
|
+
return { bin: "ctr", args: ["--namespace", "k8s.io", "images", "label"] };
|
|
14561
|
+
}
|
|
14562
|
+
return null;
|
|
14563
|
+
}
|
|
14564
|
+
function refsPresent(inventory) {
|
|
14565
|
+
const present = /* @__PURE__ */ new Set();
|
|
14566
|
+
for (const image of inventory.images) {
|
|
14567
|
+
for (const ref of refsOf(image)) present.add(ref);
|
|
14568
|
+
}
|
|
14569
|
+
return present;
|
|
14570
|
+
}
|
|
14571
|
+
async function pinWantedImages(exec, wantedImages, inventory, previouslyPinned = []) {
|
|
14572
|
+
const result = { pinned: [], unpinned: [], absent: [], failures: [] };
|
|
14573
|
+
const wanted = new Set(wantedImages);
|
|
14574
|
+
const present = refsPresent(inventory);
|
|
14575
|
+
const toPin = wantedImages.filter((ref) => present.has(ref));
|
|
14576
|
+
const toUnpin = previouslyPinned.filter((ref) => !wanted.has(ref) && present.has(ref));
|
|
14577
|
+
for (const ref of wantedImages) if (!present.has(ref)) result.absent.push(ref);
|
|
14578
|
+
if (toPin.length === 0 && toUnpin.length === 0) return result;
|
|
14579
|
+
const tool = await resolveImageLabelTool(exec);
|
|
14580
|
+
if (!tool) {
|
|
14581
|
+
result.warning = "No `k3s ctr` or `ctr` on this machine, so Conveyor cannot pin its images against kubelet garbage collection. A freshly baked image can be deleted before the first pod runs it.";
|
|
14582
|
+
return result;
|
|
14583
|
+
}
|
|
14584
|
+
const label2 = async (ref, value) => {
|
|
14585
|
+
try {
|
|
14586
|
+
assertImageRef(ref);
|
|
14587
|
+
} catch {
|
|
14588
|
+
result.failures.push(`${ref}: not a valid image reference`);
|
|
14589
|
+
return false;
|
|
14590
|
+
}
|
|
14591
|
+
const run = await exec(tool.bin, [...tool.args, ref, `${PIN_LABEL}=${value}`], {
|
|
14592
|
+
timeoutMs: 3e4
|
|
14593
|
+
});
|
|
14594
|
+
if (run.exitCode === 0) return true;
|
|
14595
|
+
result.failures.push(`${ref}: ${run.stderr.trim().slice(0, 120)}`);
|
|
14596
|
+
return false;
|
|
14597
|
+
};
|
|
14598
|
+
for (const ref of toPin) {
|
|
14599
|
+
if (await label2(ref, "pinned")) result.pinned.push(ref);
|
|
14600
|
+
}
|
|
14601
|
+
for (const ref of toUnpin) {
|
|
14602
|
+
if (await label2(ref, "")) result.unpinned.push(ref);
|
|
14603
|
+
}
|
|
14604
|
+
return result;
|
|
14605
|
+
}
|
|
14606
|
+
function countPinnedWanted(inventory, wantedImages) {
|
|
14033
14607
|
const wanted = new Set(wantedImages);
|
|
14034
|
-
|
|
14035
|
-
(image) =>
|
|
14036
|
-
);
|
|
14037
|
-
held.sort(
|
|
14038
|
-
(a, b) => Number(wanted.has(b.ref)) - Number(wanted.has(a.ref)) || (a.ref < b.ref ? -1 : a.ref > b.ref ? 1 : 0)
|
|
14039
|
-
);
|
|
14040
|
-
const reported = held.slice(0, MAX_LOCAL_IMAGES);
|
|
14041
|
-
const live = new Set(reported.map((entry) => entry.imageId));
|
|
14042
|
-
for (const id2 of [...cache.keys()]) if (!live.has(id2)) cache.delete(id2);
|
|
14043
|
-
const images = [];
|
|
14044
|
-
let inspections = 0;
|
|
14045
|
-
for (const entry of reported) {
|
|
14046
|
-
let contentHash = cache.get(entry.imageId);
|
|
14047
|
-
if (contentHash === void 0 && inspections < MAX_INSPECTIONS_PER_BEAT) {
|
|
14048
|
-
inspections += 1;
|
|
14049
|
-
contentHash = await inspectContentHash(exec, entry.imageId);
|
|
14050
|
-
if (contentHash !== void 0) cache.set(entry.imageId, contentHash);
|
|
14051
|
-
}
|
|
14052
|
-
images.push({ ...entry, contentHash: contentHash ?? null });
|
|
14053
|
-
}
|
|
14054
|
-
return images;
|
|
14608
|
+
return inventory.images.filter(
|
|
14609
|
+
(image) => image.pinned === true && refsOf(image).some((ref) => wanted.has(ref))
|
|
14610
|
+
).length;
|
|
14055
14611
|
}
|
|
14056
14612
|
|
|
14057
14613
|
// src/daemon/telemetry.ts
|
|
@@ -14189,14 +14745,22 @@ async function collectHeartbeat(ports, state, inventory) {
|
|
|
14189
14745
|
...runnerUsers.length > 0 ? { runnerUsers } : {},
|
|
14190
14746
|
...localImages ? { localImages } : {},
|
|
14191
14747
|
...registry.fields,
|
|
14192
|
-
|
|
14748
|
+
...state.isolation ? { isolation: state.isolation } : {},
|
|
14749
|
+
localWarnings: withinHeartbeatBounds([
|
|
14193
14750
|
...state.localWarnings,
|
|
14194
14751
|
...gcWarning ? [gcWarning] : [],
|
|
14195
14752
|
...ungranted.length > 0 ? [ungrantedRunnersWarning(ungranted)] : [],
|
|
14196
14753
|
...registry.warnings
|
|
14197
|
-
]
|
|
14754
|
+
])
|
|
14198
14755
|
};
|
|
14199
14756
|
}
|
|
14757
|
+
var HEARTBEAT_LIST_MAX = 64;
|
|
14758
|
+
var HEARTBEAT_STRING_MAX = 512;
|
|
14759
|
+
function withinHeartbeatBounds(warnings) {
|
|
14760
|
+
return warnings.slice(0, HEARTBEAT_LIST_MAX).map(
|
|
14761
|
+
(warning) => warning.length > HEARTBEAT_STRING_MAX ? `${warning.slice(0, HEARTBEAT_STRING_MAX - 1)}\u2026` : warning
|
|
14762
|
+
);
|
|
14763
|
+
}
|
|
14200
14764
|
async function readPodIdentities(exec) {
|
|
14201
14765
|
const result = await exec(
|
|
14202
14766
|
"k3s",
|
|
@@ -14278,9 +14842,12 @@ async function runRegistryUpkeep(ports, lastGcAt, maintenanceHour) {
|
|
|
14278
14842
|
}
|
|
14279
14843
|
|
|
14280
14844
|
// src/daemon/reconcile.ts
|
|
14281
|
-
|
|
14845
|
+
function ownerRestartWarning(change) {
|
|
14846
|
+
return `${change} Conveyor did not install this server's k3s, so it never restarts it: run \`sudo systemctl restart k3s\` when no build runs.`;
|
|
14847
|
+
}
|
|
14848
|
+
async function reconcileKubelet(ctx, desired, result, managed) {
|
|
14282
14849
|
const ownerConfig = await ctx.fs.readFile(K3S_OWNER_CONFIG);
|
|
14283
|
-
const rendered = renderK3sDropIn(desired.kubelet, ownerConfig);
|
|
14850
|
+
const rendered = renderK3sDropIn(desired.kubelet, ownerConfig, { disableComponents: managed });
|
|
14284
14851
|
result.skippedKubeletFlags = rendered.skipped;
|
|
14285
14852
|
result.effectiveImageGcHighThreshold = rendered.skipped.includes("image-gc-high-threshold") ? ownerImageGcHighThreshold(ownerConfig) : desired.kubelet.imageGcHighThreshold;
|
|
14286
14853
|
for (const flag of rendered.skipped) {
|
|
@@ -14296,6 +14863,10 @@ async function reconcileKubelet(ctx, desired, result) {
|
|
|
14296
14863
|
if (current === rendered.content) return;
|
|
14297
14864
|
await ctx.fs.ensureDir(K3S_CONFIG_DROPIN.slice(0, K3S_CONFIG_DROPIN.lastIndexOf("/")));
|
|
14298
14865
|
await ctx.fs.writeFile(K3S_CONFIG_DROPIN, rendered.content, 420);
|
|
14866
|
+
if (!managed) {
|
|
14867
|
+
result.warnings.push(ownerRestartWarning("Kubelet settings changed."));
|
|
14868
|
+
return;
|
|
14869
|
+
}
|
|
14299
14870
|
if (!mayDisrupt(ctx.runningConveyorPods)) {
|
|
14300
14871
|
result.warnings.push(
|
|
14301
14872
|
`Kubelet settings changed but ${busyReason(ctx.runningConveyorPods)} \u2014 the restart is deferred until the machine is idle.`
|
|
@@ -14346,43 +14917,6 @@ async function reconcilePriorityClass(ctx, desired, result) {
|
|
|
14346
14917
|
);
|
|
14347
14918
|
}
|
|
14348
14919
|
}
|
|
14349
|
-
function renderLimitRange(namespace, limits) {
|
|
14350
|
-
const ns = assertK8sName(namespace, "namespace");
|
|
14351
|
-
const values = [
|
|
14352
|
-
assertQuantity(limits.defaultRequest.cpu, "defaultRequest.cpu"),
|
|
14353
|
-
assertQuantity(limits.defaultRequest.memory, "defaultRequest.memory"),
|
|
14354
|
-
assertQuantity(limits.default.cpu, "default.cpu"),
|
|
14355
|
-
assertQuantity(limits.default.memory, "default.memory")
|
|
14356
|
-
];
|
|
14357
|
-
return [
|
|
14358
|
-
"apiVersion: v1",
|
|
14359
|
-
"kind: LimitRange",
|
|
14360
|
-
"metadata:",
|
|
14361
|
-
" name: conveyor-k3-defaults",
|
|
14362
|
-
` namespace: ${ns}`,
|
|
14363
|
-
"spec:",
|
|
14364
|
-
" limits:",
|
|
14365
|
-
" - type: Container",
|
|
14366
|
-
" defaultRequest:",
|
|
14367
|
-
` cpu: "${values[0]}"`,
|
|
14368
|
-
` memory: "${values[1]}"`,
|
|
14369
|
-
" default:",
|
|
14370
|
-
` cpu: "${values[2]}"`,
|
|
14371
|
-
` memory: "${values[3]}"`,
|
|
14372
|
-
""
|
|
14373
|
-
].join("\n");
|
|
14374
|
-
}
|
|
14375
|
-
async function reconcileNamespaces(ctx, desired, result) {
|
|
14376
|
-
for (const namespace of desired.namespaces) {
|
|
14377
|
-
try {
|
|
14378
|
-
await kubectlApply(ctx.exec, renderLimitRange(namespace.name, namespace.limitRange));
|
|
14379
|
-
} catch (error) {
|
|
14380
|
-
result.warnings.push(
|
|
14381
|
-
`Could not apply resource defaults to ${namespace.name} (${namespace.projectName}): ${error instanceof Error ? error.message : String(error)}`
|
|
14382
|
-
);
|
|
14383
|
-
}
|
|
14384
|
-
}
|
|
14385
|
-
}
|
|
14386
14920
|
async function step(result, label2, run) {
|
|
14387
14921
|
try {
|
|
14388
14922
|
await run();
|
|
@@ -14419,6 +14953,12 @@ async function reconcile(ctx, desired) {
|
|
|
14419
14953
|
"Could not apply namespace defaults",
|
|
14420
14954
|
() => reconcileNamespaces(ctx, desired, result)
|
|
14421
14955
|
);
|
|
14956
|
+
await step(
|
|
14957
|
+
result,
|
|
14958
|
+
"Could not apply pod isolation",
|
|
14959
|
+
() => reconcileIsolation(ctx, desired, result)
|
|
14960
|
+
);
|
|
14961
|
+
if (desired.isolation && result.isolation === void 0) result.isolation = { applied: false };
|
|
14422
14962
|
if (manifestAccepted) {
|
|
14423
14963
|
await step(
|
|
14424
14964
|
result,
|
|
@@ -14427,6 +14967,7 @@ async function reconcile(ctx, desired) {
|
|
|
14427
14967
|
);
|
|
14428
14968
|
}
|
|
14429
14969
|
const registry = desired.localRegistry?.enabled === true;
|
|
14970
|
+
const managed = managesK3s(desired, await readState(ctx.fs).catch(() => ({})));
|
|
14430
14971
|
if (registry) {
|
|
14431
14972
|
await step(result, "Could not reconcile the local image registry", async () => {
|
|
14432
14973
|
await deployLocalRegistry(ctx);
|
|
@@ -14435,10 +14976,15 @@ async function reconcile(ctx, desired) {
|
|
|
14435
14976
|
await step(
|
|
14436
14977
|
result,
|
|
14437
14978
|
"Could not apply kubelet settings",
|
|
14438
|
-
() => reconcileKubelet(ctx, desired, result)
|
|
14979
|
+
() => reconcileKubelet(ctx, desired, result, managed)
|
|
14439
14980
|
);
|
|
14440
14981
|
if (registry && !result.restartedK3s) {
|
|
14441
14982
|
await step(result, "Could not load the local image registry mirror", async () => {
|
|
14983
|
+
if (!managed) {
|
|
14984
|
+
if (await registryMirrorPending(ctx.fs))
|
|
14985
|
+
result.warnings.push(ownerRestartWarning("The local image registry's mirror is new."));
|
|
14986
|
+
return;
|
|
14987
|
+
}
|
|
14442
14988
|
const loaded = await loadRegistryMirror(ctx);
|
|
14443
14989
|
result.restartedK3s = loaded.restarted;
|
|
14444
14990
|
if (loaded.warning) result.warnings.push(loaded.warning);
|
|
@@ -14447,10 +14993,138 @@ async function reconcile(ctx, desired) {
|
|
|
14447
14993
|
return result;
|
|
14448
14994
|
}
|
|
14449
14995
|
|
|
14996
|
+
// src/commands/setup-run.ts
|
|
14997
|
+
init_journald();
|
|
14998
|
+
init_paths();
|
|
14999
|
+
|
|
15000
|
+
// src/commands/setup-session.ts
|
|
15001
|
+
init_exec();
|
|
15002
|
+
init_paths();
|
|
15003
|
+
async function withSetupConnection(ports, connection, enroll) {
|
|
15004
|
+
for (const unit of [UPDATE_TIMER, UPDATE_UNIT, DAEMON_UNIT]) {
|
|
15005
|
+
if (await ports.fs.readFile(`${SYSTEMD_DIR}/${unit}`))
|
|
15006
|
+
await runOrThrow(ports.exec, "systemctl", ["stop", unit]);
|
|
15007
|
+
}
|
|
15008
|
+
let result;
|
|
15009
|
+
try {
|
|
15010
|
+
await connection.connect();
|
|
15011
|
+
result = await enroll();
|
|
15012
|
+
} finally {
|
|
15013
|
+
connection.disconnect();
|
|
15014
|
+
}
|
|
15015
|
+
await runOrThrow(ports.exec, "systemctl", ["start", DAEMON_UNIT]);
|
|
15016
|
+
await runOrThrow(ports.exec, "systemctl", ["start", UPDATE_TIMER]);
|
|
15017
|
+
return result;
|
|
15018
|
+
}
|
|
15019
|
+
|
|
15020
|
+
// src/commands/setup-run.ts
|
|
15021
|
+
function requiredAuthHosts(namespaces) {
|
|
15022
|
+
return [...new Set(namespaces.flatMap((ns) => ns.authRequiredHosts))].sort();
|
|
15023
|
+
}
|
|
15024
|
+
async function prepareMachine(ports) {
|
|
15025
|
+
await ensureK3s(ports);
|
|
15026
|
+
await ensureJournaldCap(ports).catch((error) => {
|
|
15027
|
+
ports.log(
|
|
15028
|
+
`Could not cap the system journal; setup continues: ${error instanceof Error ? error.message : String(error)}`
|
|
15029
|
+
);
|
|
15030
|
+
});
|
|
15031
|
+
const cloudflaredBin = await ensureCloudflared(ports);
|
|
15032
|
+
await ensureCaPool(ports);
|
|
15033
|
+
return { cloudflaredBin };
|
|
15034
|
+
}
|
|
15035
|
+
async function enrollMachine(ports, env, cloudflaredBin, recorded = {}) {
|
|
15036
|
+
const { exec, fs: fs2, log: log2 } = ports;
|
|
15037
|
+
const connection = new ClusterConnection({ apiUrl: env.apiUrl, clusterToken: env.clusterToken });
|
|
15038
|
+
const bundle = await withSetupConnection(ports, connection, async () => {
|
|
15039
|
+
const enrollment = await connection.enroll();
|
|
15040
|
+
log2(`Enrolled as ${enrollment.hostname}.`);
|
|
15041
|
+
await installUnits(ports, enrollment, cloudflaredBin, enrollment.desiredState.maintenance.hour);
|
|
15042
|
+
const runningConveyorPods = await countRunningConveyorPods(exec);
|
|
15043
|
+
const result = await reconcile({ exec, fs: fs2, runningConveyorPods }, enrollment.desiredState);
|
|
15044
|
+
for (const warning of result.warnings) log2(warning);
|
|
15045
|
+
const saToken = await mintServiceAccountToken(ports);
|
|
15046
|
+
await connection.completeEnrollment({ saToken });
|
|
15047
|
+
await recordSetupState(
|
|
15048
|
+
ports,
|
|
15049
|
+
enrollment,
|
|
15050
|
+
result.appliedVersion,
|
|
15051
|
+
{ bun: await resolveBunBin(ports), cli: ports.cliBin, cloudflared: cloudflaredBin },
|
|
15052
|
+
recorded
|
|
15053
|
+
);
|
|
15054
|
+
return enrollment;
|
|
15055
|
+
});
|
|
15056
|
+
const registries = parseRegistries(await fs2.readFile(REGISTRIES_FILE));
|
|
15057
|
+
const missing = missingAuthHosts(registries, requiredAuthHosts(bundle.desiredState.namespaces));
|
|
15058
|
+
log2("");
|
|
15059
|
+
log2(`This machine is ready. Conveyor reaches it at ${bundle.hostname}.`);
|
|
15060
|
+
for (const line of renderRegistryAuthSummary(missing)) log2(line);
|
|
15061
|
+
}
|
|
15062
|
+
|
|
15063
|
+
// src/lib/bins.ts
|
|
15064
|
+
var INSTALLER_PATHS = {
|
|
15065
|
+
bun: "/root/.bun/bin/bun",
|
|
15066
|
+
cli: "/root/.bun/bin/conveyor-k3",
|
|
15067
|
+
cloudflared: "/usr/local/bin/cloudflared"
|
|
15068
|
+
};
|
|
15069
|
+
async function whichOrNull(exec, bin) {
|
|
15070
|
+
const result = await exec("which", [bin]).catch(() => null);
|
|
15071
|
+
if (!result || result.exitCode !== 0) return null;
|
|
15072
|
+
const path = result.stdout.trim().split("\n")[0]?.trim();
|
|
15073
|
+
return path && path.startsWith("/") ? path : null;
|
|
15074
|
+
}
|
|
15075
|
+
async function resolveBinaries(exec, state, selfPath) {
|
|
15076
|
+
const recorded = state.bins;
|
|
15077
|
+
const cliSelf = selfPath?.startsWith("/") ? selfPath : null;
|
|
15078
|
+
return {
|
|
15079
|
+
bun: recorded?.bun ?? await whichOrNull(exec, "bun") ?? INSTALLER_PATHS.bun,
|
|
15080
|
+
// `cli` prefers the running process's own path: if this code is executing,
|
|
15081
|
+
// that path is by definition correct.
|
|
15082
|
+
cli: cliSelf ?? recorded?.cli ?? await whichOrNull(exec, "conveyor-k3") ?? INSTALLER_PATHS.cli,
|
|
15083
|
+
cloudflared: recorded?.cloudflared ?? await whichOrNull(exec, "cloudflared") ?? INSTALLER_PATHS.cloudflared
|
|
15084
|
+
};
|
|
15085
|
+
}
|
|
15086
|
+
|
|
15087
|
+
// src/lib/guest-vm.ts
|
|
15088
|
+
async function requireGuestVm(exec) {
|
|
15089
|
+
try {
|
|
15090
|
+
const result = await exec("systemd-detect-virt", ["--vm", "--quiet"]);
|
|
15091
|
+
if (result.exitCode === 0) return;
|
|
15092
|
+
} catch {
|
|
15093
|
+
}
|
|
15094
|
+
throw new Error("Refusing to run guest setup on a physical host; use `conveyor-k3 vm setup`");
|
|
15095
|
+
}
|
|
15096
|
+
|
|
15097
|
+
// src/lib/k3s-channel.ts
|
|
15098
|
+
var CHANNELS_URL = "https://update.k3s.io/v1-release/channels";
|
|
15099
|
+
var REQUEST_TIMEOUT_MS2 = 15e3;
|
|
15100
|
+
function parseChannelVersion(version) {
|
|
15101
|
+
const match = /v?(\d+)\.(\d+)\./.exec(version);
|
|
15102
|
+
if (!match) return null;
|
|
15103
|
+
return [Number(match[1]), Number(match[2])];
|
|
15104
|
+
}
|
|
15105
|
+
async function resolveChannelLatest(channel, fetchImpl = fetch) {
|
|
15106
|
+
try {
|
|
15107
|
+
const response = await fetchImpl(CHANNELS_URL, {
|
|
15108
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2),
|
|
15109
|
+
headers: { Accept: "application/json" }
|
|
15110
|
+
});
|
|
15111
|
+
if (!response.ok) return null;
|
|
15112
|
+
const body = await response.json();
|
|
15113
|
+
const match = body.data?.find((entry) => entry.id === channel || entry.name === channel);
|
|
15114
|
+
return match?.latest ? parseChannelVersion(match.latest) : null;
|
|
15115
|
+
} catch {
|
|
15116
|
+
return null;
|
|
15117
|
+
}
|
|
15118
|
+
}
|
|
15119
|
+
|
|
14450
15120
|
// src/daemon/runner.ts
|
|
15121
|
+
init_dist();
|
|
15122
|
+
init_paths();
|
|
14451
15123
|
var HOUR_MS2 = 60 * 6e4;
|
|
14452
15124
|
var DAY_MS = 24 * HOUR_MS2;
|
|
14453
15125
|
var STORE_MEASURE_INTERVAL_MS = 55 * 6e4;
|
|
15126
|
+
var ISOLATION_RETRY_FIRST_MS = 6e4;
|
|
15127
|
+
var ISOLATION_RETRY_MAX_MS = 10 * 6e4;
|
|
14454
15128
|
var DISK_PRESSURE_FREE_FRACTION = 0.2;
|
|
14455
15129
|
var STRANDED_PRUNE_BYTES = 20 * 1024 ** 3;
|
|
14456
15130
|
function flattenWanted(desired) {
|
|
@@ -14491,12 +15165,22 @@ var ClusterDaemon = class {
|
|
|
14491
15165
|
localImageHashes = /* @__PURE__ */ new Map();
|
|
14492
15166
|
/** How long the in-cluster registry has been not ready, across beats. */
|
|
14493
15167
|
registryWatch = { notReadySince: null };
|
|
15168
|
+
/**
|
|
15169
|
+
* Whether pod isolation is in force, from the last reconcile; undefined
|
|
15170
|
+
* while desired state does not ask for it. Every heartbeat checks it again
|
|
15171
|
+
* against k3s's own settings before reporting it.
|
|
15172
|
+
*/
|
|
15173
|
+
isolation = void 0;
|
|
14494
15174
|
timers = [];
|
|
14495
15175
|
/** When the last store walk started, so one that failed is not retried on every reconcile. */
|
|
14496
15176
|
storeWalkStartedAt = 0;
|
|
14497
15177
|
/** Serializes reconcile against itself — a sync nudge during a reconcile
|
|
14498
15178
|
* must queue, not interleave two kubectl applies of the same manifests. */
|
|
14499
15179
|
reconciling = null;
|
|
15180
|
+
/** The run queued behind the running one, until it starts. At most one waits. */
|
|
15181
|
+
queuedRun = null;
|
|
15182
|
+
/** The next reconcile while pod isolation is not applied; `dueAt` 0 = none. */
|
|
15183
|
+
isolationRetry = { delayMs: ISOLATION_RETRY_FIRST_MS, dueAt: 0 };
|
|
14500
15184
|
constructor(ports) {
|
|
14501
15185
|
this.ports = ports;
|
|
14502
15186
|
}
|
|
@@ -14532,10 +15216,27 @@ var ClusterDaemon = class {
|
|
|
14532
15216
|
}
|
|
14533
15217
|
/** Fetch the desired state and converge to it. Queued, never concurrent. */
|
|
14534
15218
|
syncAndReconcile() {
|
|
14535
|
-
|
|
15219
|
+
if (this.queuedRun) return this.queuedRun;
|
|
15220
|
+
const run = (this.reconciling ?? Promise.resolve()).then(() => {
|
|
15221
|
+
this.queuedRun = null;
|
|
15222
|
+
return this.runReconcile();
|
|
15223
|
+
});
|
|
15224
|
+
this.queuedRun = run;
|
|
14536
15225
|
this.reconciling = run.catch(() => void 0);
|
|
14537
15226
|
return run;
|
|
14538
15227
|
}
|
|
15228
|
+
/** Reconcile again later while pod isolation is not applied; stop once it is. */
|
|
15229
|
+
scheduleIsolationRetry() {
|
|
15230
|
+
if (this.isolation?.applied !== false) {
|
|
15231
|
+
this.isolationRetry = { delayMs: ISOLATION_RETRY_FIRST_MS, dueAt: 0 };
|
|
15232
|
+
return;
|
|
15233
|
+
}
|
|
15234
|
+
const { delayMs } = this.isolationRetry;
|
|
15235
|
+
this.isolationRetry = {
|
|
15236
|
+
delayMs: Math.min(delayMs * 2, ISOLATION_RETRY_MAX_MS),
|
|
15237
|
+
dueAt: Date.now() + delayMs
|
|
15238
|
+
};
|
|
15239
|
+
}
|
|
14539
15240
|
async runReconcile() {
|
|
14540
15241
|
try {
|
|
14541
15242
|
this.desired = await this.ports.connection.requestDesiredState();
|
|
@@ -14551,6 +15252,8 @@ var ClusterDaemon = class {
|
|
|
14551
15252
|
this.desired
|
|
14552
15253
|
);
|
|
14553
15254
|
this.warnings = result.warnings;
|
|
15255
|
+
this.isolation = result.isolation;
|
|
15256
|
+
this.scheduleIsolationRetry();
|
|
14554
15257
|
for (const warning of result.warnings) this.ports.log(warning);
|
|
14555
15258
|
if (result.prunedNamespaces.length > 0) {
|
|
14556
15259
|
this.ports.log(
|
|
@@ -14721,6 +15424,10 @@ var ClusterDaemon = class {
|
|
|
14721
15424
|
const inventory = await readImageInventory(this.ports.exec).catch(() => null);
|
|
14722
15425
|
const registryOn = this.desired?.localRegistry?.enabled === true;
|
|
14723
15426
|
if (!registryOn) this.registryWatch.notReadySince = null;
|
|
15427
|
+
const isolation = await recheckIsolation(
|
|
15428
|
+
{ exec: this.ports.exec, fs: this.ports.fs },
|
|
15429
|
+
this.isolation
|
|
15430
|
+
);
|
|
14724
15431
|
return await collectHeartbeat(
|
|
14725
15432
|
{
|
|
14726
15433
|
exec: this.ports.exec,
|
|
@@ -14735,10 +15442,11 @@ var ClusterDaemon = class {
|
|
|
14735
15442
|
lastPruneAt: this.state?.lastPruneAt ?? null,
|
|
14736
15443
|
lastWarmAt: this.state?.lastWarmAt ?? null,
|
|
14737
15444
|
lastUpdateAt: this.state?.lastUpdateAt ?? null,
|
|
14738
|
-
localWarnings: [...this.warnings, ...this.warmWarnings],
|
|
15445
|
+
localWarnings: [...this.warnings, ...this.warmWarnings, ...isolation.warnings],
|
|
14739
15446
|
imageGcHighThreshold: this.effectiveImageGcHigh,
|
|
14740
15447
|
localImageHashes: this.localImageHashes,
|
|
14741
15448
|
...registryOn ? { localRegistry: this.registryWatch } : {},
|
|
15449
|
+
...isolation.isolation ? { isolation: isolation.isolation } : {},
|
|
14742
15450
|
k3sStoreBytes: this.measuredStoreBytes
|
|
14743
15451
|
},
|
|
14744
15452
|
inventory
|
|
@@ -14763,6 +15471,10 @@ var ClusterDaemon = class {
|
|
|
14763
15471
|
return this.beating;
|
|
14764
15472
|
}
|
|
14765
15473
|
async runBeat() {
|
|
15474
|
+
if (this.isolationRetry.dueAt > 0 && Date.now() >= this.isolationRetry.dueAt) {
|
|
15475
|
+
this.isolationRetry.dueAt = 0;
|
|
15476
|
+
void this.syncAndReconcile();
|
|
15477
|
+
}
|
|
14766
15478
|
try {
|
|
14767
15479
|
const workloads = await this.ports.connection.sendHeartbeat({
|
|
14768
15480
|
...await this.buildHeartbeat(),
|
|
@@ -14776,12 +15488,64 @@ var ClusterDaemon = class {
|
|
|
14776
15488
|
}
|
|
14777
15489
|
};
|
|
14778
15490
|
|
|
14779
|
-
// src/
|
|
14780
|
-
|
|
15491
|
+
// src/host/bootstrap.ts
|
|
15492
|
+
import { spawn as spawn2 } from "child_process";
|
|
15493
|
+
var RUN_BOOTSTRAP = `set -euo pipefail; umask 077; installer=$(mktemp /run/conveyor-pairing.XXXXXX); trap 'rm -f "$installer"' EXIT; cat > "$installer"; bash "$installer"`;
|
|
15494
|
+
var DEV_ARTIFACT_ENV = ["CONVEYOR_DEV_ARTIFACT", "CONVEYOR_DEV_ARTIFACT_SHA256"];
|
|
15495
|
+
function runHostBootstrap(script, command = ["bash", "-c", RUN_BOOTSTRAP]) {
|
|
15496
|
+
if (script.length === 0 || script.length > 1e6)
|
|
15497
|
+
return Promise.reject(new Error("Pairing bootstrap payload is invalid."));
|
|
15498
|
+
const env = { ...process.env };
|
|
15499
|
+
for (const name2 of DEV_ARTIFACT_ENV) delete env[name2];
|
|
15500
|
+
const [bin, ...args] = command;
|
|
15501
|
+
return new Promise((resolve, reject) => {
|
|
15502
|
+
const child = spawn2(bin, args, { stdio: ["pipe", "inherit", "inherit"], env });
|
|
15503
|
+
child.on("error", reject);
|
|
15504
|
+
child.on("close", (code, signal) => {
|
|
15505
|
+
if (code === 0) {
|
|
15506
|
+
resolve();
|
|
15507
|
+
return;
|
|
15508
|
+
}
|
|
15509
|
+
reject(
|
|
15510
|
+
new Error(
|
|
15511
|
+
`The installer stopped (${signal ?? `exit ${code}`}). Fix the error above, then run sudo conveyor-k3 host link again.`
|
|
15512
|
+
)
|
|
15513
|
+
);
|
|
15514
|
+
});
|
|
15515
|
+
child.stdin.on("error", () => void 0);
|
|
15516
|
+
child.stdin.end(script);
|
|
15517
|
+
});
|
|
15518
|
+
}
|
|
15519
|
+
|
|
15520
|
+
// src/host/device-key.ts
|
|
15521
|
+
import { createPublicKey, generateKeyPairSync } from "crypto";
|
|
15522
|
+
import { chmod as chmod2, mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
15523
|
+
import { dirname } from "path";
|
|
15524
|
+
init_device_link();
|
|
15525
|
+
async function createDeviceKey(keyPath) {
|
|
15526
|
+
const directory = dirname(keyPath);
|
|
15527
|
+
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
15528
|
+
await chmod2(directory, 448);
|
|
15529
|
+
const { privateKey } = generateKeyPairSync("ed25519");
|
|
15530
|
+
await writeFile3(keyPath, privateKey.export({ type: "pkcs8", format: "pem" }), {
|
|
15531
|
+
mode: 384,
|
|
15532
|
+
flag: "wx"
|
|
15533
|
+
});
|
|
15534
|
+
await chmod2(keyPath, 384);
|
|
15535
|
+
return privateKey;
|
|
15536
|
+
}
|
|
15537
|
+
async function ensureHostDevicePublicKey(keyPath = DEVICE_KEY_PATH, ownerUid = 0) {
|
|
15538
|
+
const key = privateDeviceKey(keyPath, ownerUid) ?? await createDeviceKey(keyPath);
|
|
15539
|
+
const publicKey = createPublicKey(key).export({ type: "spki", format: "pem" }).toString();
|
|
15540
|
+
if (!validDevicePublicKey(publicKey))
|
|
15541
|
+
throw new Error("This server's device key produced an invalid public key.");
|
|
15542
|
+
return publicKey;
|
|
15543
|
+
}
|
|
14781
15544
|
|
|
14782
15545
|
// src/verbs.ts
|
|
14783
15546
|
var VERBS = [
|
|
14784
15547
|
"vm",
|
|
15548
|
+
"host",
|
|
14785
15549
|
"setup",
|
|
14786
15550
|
"start",
|
|
14787
15551
|
"stop",
|
|
@@ -14814,14 +15578,22 @@ function parseArgs(argv) {
|
|
|
14814
15578
|
}
|
|
14815
15579
|
return { verb, positional, flags };
|
|
14816
15580
|
}
|
|
14817
|
-
var HELP = `conveyor-k3 \u2014
|
|
15581
|
+
var HELP = `conveyor-k3 \u2014 Personal Compute for Conveyor
|
|
14818
15582
|
|
|
14819
15583
|
Usage: conveyor-k3 <command> [options]
|
|
14820
15584
|
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
|
|
15585
|
+
One product, two runtimes. Pick the one that fits the machine:
|
|
15586
|
+
|
|
15587
|
+
On your computer or server:
|
|
15588
|
+
vm Personal computer runtime: an isolated VM on macOS or
|
|
15589
|
+
Linux. Run conveyor-k3 vm for its commands.
|
|
15590
|
+
host Dedicated server runtime: k3s and the daemon on this
|
|
15591
|
+
Linux machine, with no VM. Root only. Run
|
|
15592
|
+
conveyor-k3 host for its commands.
|
|
15593
|
+
|
|
15594
|
+
Inside a linked machine (the VM guest, or the server itself):
|
|
15595
|
+
setup VM guest only: install and enrol this machine. Safe
|
|
15596
|
+
to re-run. On a server, use conveyor-k3 host setup.
|
|
14825
15597
|
start Start the tunnel and the daemon.
|
|
14826
15598
|
stop Stop both, and tell Conveyor at once so your next
|
|
14827
15599
|
build goes straight to project compute.
|
|
@@ -14846,10 +15618,12 @@ Commands:
|
|
|
14846
15618
|
into this node and pin it. Writes one file under
|
|
14847
15619
|
/etc/sudoers.d, checked with visudo first. Needed only
|
|
14848
15620
|
when a project has Bake Locally turned on.
|
|
14849
|
-
uninstall
|
|
14850
|
-
registry. Your k3s config, your own
|
|
14851
|
-
entries, your workloads, and the
|
|
14852
|
-
left alone.
|
|
15621
|
+
uninstall VM guest only: remove Conveyor's units, environment,
|
|
15622
|
+
state, and image registry. Your k3s config, your own
|
|
15623
|
+
registries.yaml entries, your workloads, and the
|
|
15624
|
+
registry's data are left alone. On a server, use
|
|
15625
|
+
conveyor-k3 host uninstall: it also removes the
|
|
15626
|
+
device key.
|
|
14853
15627
|
--purge-k3s --yes also remove k3s itself and the
|
|
14854
15628
|
registry's data
|
|
14855
15629
|
daemon Run in the foreground (what the systemd unit calls).
|
|
@@ -14882,56 +15656,56 @@ async function requireRoot(exec) {
|
|
|
14882
15656
|
`);
|
|
14883
15657
|
process.exit(1);
|
|
14884
15658
|
}
|
|
14885
|
-
function
|
|
14886
|
-
return
|
|
15659
|
+
function setupPorts(exec) {
|
|
15660
|
+
return { exec, fs: nodeFileSystem, cliBin: process.argv[1] ?? "conveyor-k3", log };
|
|
14887
15661
|
}
|
|
14888
15662
|
async function runSetup(exec) {
|
|
14889
15663
|
await requireRoot(exec);
|
|
14890
15664
|
await requireGuestVm(exec);
|
|
14891
15665
|
const env = readCliEnv();
|
|
14892
|
-
const ports =
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
await
|
|
14899
|
-
|
|
15666
|
+
const ports = setupPorts(exec);
|
|
15667
|
+
const { cloudflaredBin } = await prepareMachine(ports);
|
|
15668
|
+
await enrollMachine(ports, env, cloudflaredBin);
|
|
15669
|
+
}
|
|
15670
|
+
async function runHostSetup(exec) {
|
|
15671
|
+
const ports = setupPorts(exec);
|
|
15672
|
+
const { cloudflaredBin } = await prepareMachine(ports);
|
|
15673
|
+
let env;
|
|
15674
|
+
try {
|
|
15675
|
+
env = readCliEnv();
|
|
15676
|
+
} catch {
|
|
15677
|
+
const state = await readState(nodeFileSystem);
|
|
14900
15678
|
log(
|
|
14901
|
-
`
|
|
14902
|
-
);
|
|
14903
|
-
});
|
|
14904
|
-
const cloudflaredBin = await ensureCloudflared(ports);
|
|
14905
|
-
await ensureCaPool(ports);
|
|
14906
|
-
const connection = new ClusterConnection({ apiUrl: env.apiUrl, clusterToken: env.clusterToken });
|
|
14907
|
-
const bundle = await withSetupConnection(ports, connection, async () => {
|
|
14908
|
-
const enrollment = await connection.enroll();
|
|
14909
|
-
log(`Enrolled as ${enrollment.hostname}.`);
|
|
14910
|
-
await installUnits(ports, enrollment, cloudflaredBin, enrollment.desiredState.maintenance.hour);
|
|
14911
|
-
const runningConveyorPods = await countRunningConveyorPods(exec);
|
|
14912
|
-
const result = await reconcile(
|
|
14913
|
-
{ exec, fs: nodeFileSystem, runningConveyorPods },
|
|
14914
|
-
enrollment.desiredState
|
|
15679
|
+
state.enrolled ? `Prepared. This server is already linked as ${state.hostname}; its daemon keeps it current. To move it to another account, run: sudo conveyor-k3 host link --relink` : "Prepared. This server is not linked yet \u2014 run: sudo conveyor-k3 host link --name=<name>"
|
|
14915
15680
|
);
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
15681
|
+
return;
|
|
15682
|
+
}
|
|
15683
|
+
await enrollMachine(ports, env, cloudflaredBin, { runtime: "host" });
|
|
15684
|
+
}
|
|
15685
|
+
function hostPorts(exec) {
|
|
15686
|
+
return {
|
|
15687
|
+
prepare: async () => {
|
|
15688
|
+
await prepareMachine(setupPorts(exec));
|
|
15689
|
+
},
|
|
15690
|
+
setup: () => runHostSetup(exec),
|
|
15691
|
+
status: (asJson) => runStatus(exec, asJson, "sudo conveyor-k3 host link --name=<name>"),
|
|
15692
|
+
uninstall: async (options) => {
|
|
15693
|
+
await uninstall({ exec, fs: nodeFileSystem, log }, options);
|
|
15694
|
+
},
|
|
15695
|
+
runBootstrap: (script) => runHostBootstrap(script),
|
|
15696
|
+
ensureDevicePublicKey: () => ensureHostDevicePublicKey(),
|
|
15697
|
+
removeDeviceKey: () => nodeFileSystem.removeFile(DEVICE_KEY_PATH),
|
|
15698
|
+
readState: () => readState(nodeFileSystem),
|
|
15699
|
+
writeState: (state) => writeState(nodeFileSystem, state)
|
|
15700
|
+
};
|
|
14931
15701
|
}
|
|
14932
15702
|
async function runDaemon(exec) {
|
|
14933
15703
|
const env = readCliEnv();
|
|
14934
|
-
const connection = new ClusterConnection({
|
|
15704
|
+
const connection = new ClusterConnection({
|
|
15705
|
+
apiUrl: env.apiUrl,
|
|
15706
|
+
clusterToken: env.clusterToken,
|
|
15707
|
+
reconnectWhenReplaced: true
|
|
15708
|
+
});
|
|
14935
15709
|
const daemon = new ClusterDaemon({
|
|
14936
15710
|
exec,
|
|
14937
15711
|
fs: nodeFileSystem,
|
|
@@ -14972,7 +15746,7 @@ async function runStop(exec) {
|
|
|
14972
15746
|
}
|
|
14973
15747
|
log("Stopped. Your builds go to project compute until you run `conveyor-k3 start`.");
|
|
14974
15748
|
}
|
|
14975
|
-
async function runStatus(exec, asJson) {
|
|
15749
|
+
async function runStatus(exec, asJson, notEnrolledHint = "conveyor-k3 setup") {
|
|
14976
15750
|
const state = await readState(nodeFileSystem);
|
|
14977
15751
|
const units = {};
|
|
14978
15752
|
for (const unit of ALL_UNITS) {
|
|
@@ -14990,14 +15764,18 @@ async function runStatus(exec, asJson) {
|
|
|
14990
15764
|
units,
|
|
14991
15765
|
lastWarmAt: state.lastWarmAt,
|
|
14992
15766
|
lastPruneAt: state.lastPruneAt,
|
|
14993
|
-
runningConveyorPods: await countRunningConveyorPods(exec)
|
|
15767
|
+
runningConveyorPods: await countRunningConveyorPods(exec),
|
|
15768
|
+
...state.runtime ? { runtime: state.runtime } : {}
|
|
14994
15769
|
};
|
|
14995
15770
|
if (asJson) {
|
|
14996
15771
|
log(JSON.stringify(report, null, 2));
|
|
14997
15772
|
return;
|
|
14998
15773
|
}
|
|
14999
15774
|
log(`conveyor-k3 ${report.cliVersion}`);
|
|
15000
|
-
log(
|
|
15775
|
+
log(
|
|
15776
|
+
report.enrolled ? `Enrolled as ${report.hostname}` : `Not enrolled \u2014 run: ${notEnrolledHint}`
|
|
15777
|
+
);
|
|
15778
|
+
if (state.runtime) log(`runtime: ${state.runtime}`);
|
|
15001
15779
|
log(`k3s: ${report.k3sVersion ?? "not detected"}`);
|
|
15002
15780
|
for (const [unit, active] of Object.entries(units)) log(`${unit}: ${active}`);
|
|
15003
15781
|
log(`Settings version applied: ${report.appliedSettingsVersion}`);
|
|
@@ -15009,9 +15787,9 @@ async function runSync(exec) {
|
|
|
15009
15787
|
const connection = new ClusterConnection({ apiUrl: env.apiUrl, clusterToken: env.clusterToken });
|
|
15010
15788
|
await connection.connect();
|
|
15011
15789
|
const desired = await connection.requestDesiredState();
|
|
15790
|
+
connection.disconnect();
|
|
15012
15791
|
const runningConveyorPods = await countRunningConveyorPods(exec);
|
|
15013
15792
|
const result = await reconcile({ exec, fs: nodeFileSystem, runningConveyorPods }, desired);
|
|
15014
|
-
connection.disconnect();
|
|
15015
15793
|
log(`Applied settings version ${result.appliedVersion}.`);
|
|
15016
15794
|
for (const warning of result.warnings) log(warning);
|
|
15017
15795
|
}
|
|
@@ -15023,8 +15801,10 @@ async function runUpdateVerb(exec) {
|
|
|
15023
15801
|
const desired = await connection.requestDesiredState();
|
|
15024
15802
|
connection.disconnect();
|
|
15025
15803
|
const installed = await exec("k3s", ["--version"]).catch(() => null);
|
|
15804
|
+
const state = await readState(nodeFileSystem);
|
|
15026
15805
|
const decision = decideK3sUpgrade({
|
|
15027
15806
|
autoUpgradeK3s: desired.maintenance.autoUpgradeK3s,
|
|
15807
|
+
managesK3s: managesK3s(desired, state),
|
|
15028
15808
|
runningConveyorPods: await countRunningConveyorPods(exec),
|
|
15029
15809
|
installedK3s: installed ? parseK3sVersion(installed.stdout || installed.stderr) : null,
|
|
15030
15810
|
// The channel's REAL current version, from Rancher. Comparing against the
|
|
@@ -15032,7 +15812,7 @@ async function runUpdateVerb(exec) {
|
|
|
15032
15812
|
// which left the profile's auto-upgrade switch controlling nothing.
|
|
15033
15813
|
channelLatestK3s: await resolveChannelLatest(desired.k3s.channel)
|
|
15034
15814
|
});
|
|
15035
|
-
const bins = await resolveBinaries(exec,
|
|
15815
|
+
const bins = await resolveBinaries(exec, state, process.argv[1]);
|
|
15036
15816
|
const result = await runUpdate(
|
|
15037
15817
|
{ exec, log, bins },
|
|
15038
15818
|
decision,
|
|
@@ -15118,6 +15898,10 @@ async function dispatch(verb, parsed, exec) {
|
|
|
15118
15898
|
const { runVmCommand: runVmCommand2 } = await Promise.resolve().then(() => (init_command(), command_exports));
|
|
15119
15899
|
return runVmCommand2(parsed, exec, log);
|
|
15120
15900
|
}
|
|
15901
|
+
case "host": {
|
|
15902
|
+
const { runHostCommand: runHostCommand2 } = await Promise.resolve().then(() => (init_command2(), command_exports2));
|
|
15903
|
+
return runHostCommand2(parsed, exec, log, hostPorts(exec));
|
|
15904
|
+
}
|
|
15121
15905
|
case "setup":
|
|
15122
15906
|
return await runSetup(exec);
|
|
15123
15907
|
case "daemon":
|