fleetfizz 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fleetfizz.mjs +3029 -1134
- package/package.json +1 -1
package/dist/fleetfizz.mjs
CHANGED
|
@@ -1849,7 +1849,7 @@ const emptyContext$1 = {
|
|
|
1849
1849
|
* @category formatting
|
|
1850
1850
|
* @since 2.0.0
|
|
1851
1851
|
*/
|
|
1852
|
-
function format$
|
|
1852
|
+
function format$4(input, options) {
|
|
1853
1853
|
const space = options?.space ?? 0;
|
|
1854
1854
|
const ancestors = /* @__PURE__ */ new WeakSet();
|
|
1855
1855
|
const gap = !space ? "" : typeof space === "number" ? " ".repeat(space) : space;
|
|
@@ -2012,7 +2012,7 @@ function formatJson(input, options) {
|
|
|
2012
2012
|
return JSON.stringify(input, function(key, value) {
|
|
2013
2013
|
const original = Object.getOwnPropertyDescriptor(this, key)?.value;
|
|
2014
2014
|
const redacted = hasProperty(original, symbolRedactable) ? redact(original) : redact(value);
|
|
2015
|
-
if (typeof redacted === "bigint") return format$
|
|
2015
|
+
if (typeof redacted === "bigint") return format$4(redacted);
|
|
2016
2016
|
if (typeof redacted !== "object" || redacted === null) return redacted;
|
|
2017
2017
|
const current = redacted instanceof Error && !hasProperty(redacted, "toJSON") ? {
|
|
2018
2018
|
...redacted,
|
|
@@ -2148,7 +2148,7 @@ const toJson = (input) => {
|
|
|
2148
2148
|
const toStringUnknown = (u, whitespace = 2) => {
|
|
2149
2149
|
if (typeof u === "string") return u;
|
|
2150
2150
|
try {
|
|
2151
|
-
return typeof u === "object" ? formatJson(u, { space: whitespace }) : format$
|
|
2151
|
+
return typeof u === "object" ? formatJson(u, { space: whitespace }) : format$4(u, { space: whitespace });
|
|
2152
2152
|
} catch {
|
|
2153
2153
|
return String(u);
|
|
2154
2154
|
}
|
|
@@ -2198,7 +2198,7 @@ const BaseProto = {
|
|
|
2198
2198
|
return this.toJSON();
|
|
2199
2199
|
},
|
|
2200
2200
|
toString() {
|
|
2201
|
-
return format$
|
|
2201
|
+
return format$4(this.toJSON());
|
|
2202
2202
|
}
|
|
2203
2203
|
};
|
|
2204
2204
|
//#endregion
|
|
@@ -2361,7 +2361,7 @@ const PipeInspectableProto = {
|
|
|
2361
2361
|
return { ...this };
|
|
2362
2362
|
},
|
|
2363
2363
|
toString() {
|
|
2364
|
-
return format$
|
|
2364
|
+
return format$4(this.toJSON(), {
|
|
2365
2365
|
ignoreToString: true,
|
|
2366
2366
|
space: 2
|
|
2367
2367
|
});
|
|
@@ -2411,7 +2411,7 @@ var CauseImpl = class {
|
|
|
2411
2411
|
};
|
|
2412
2412
|
}
|
|
2413
2413
|
toString() {
|
|
2414
|
-
return `Cause(${format$
|
|
2414
|
+
return `Cause(${format$4(this.reasons)})`;
|
|
2415
2415
|
}
|
|
2416
2416
|
[NodeInspectSymbol]() {
|
|
2417
2417
|
return this.toJSON();
|
|
@@ -2454,7 +2454,7 @@ var ReasonBase = class {
|
|
|
2454
2454
|
return pipeArguments(this, arguments);
|
|
2455
2455
|
}
|
|
2456
2456
|
toString() {
|
|
2457
|
-
return format$
|
|
2457
|
+
return format$4(this);
|
|
2458
2458
|
}
|
|
2459
2459
|
[NodeInspectSymbol]() {
|
|
2460
2460
|
return this.toString();
|
|
@@ -2469,7 +2469,7 @@ var Fail = class extends ReasonBase {
|
|
|
2469
2469
|
this.error = error;
|
|
2470
2470
|
}
|
|
2471
2471
|
toString() {
|
|
2472
|
-
return `Fail(${format$
|
|
2472
|
+
return `Fail(${format$4(this.error)})`;
|
|
2473
2473
|
}
|
|
2474
2474
|
toJSON() {
|
|
2475
2475
|
return {
|
|
@@ -2517,7 +2517,7 @@ var Die = class extends ReasonBase {
|
|
|
2517
2517
|
this.defect = defect;
|
|
2518
2518
|
}
|
|
2519
2519
|
toString() {
|
|
2520
|
-
return `Die(${format$
|
|
2520
|
+
return `Die(${format$4(this.defect)})`;
|
|
2521
2521
|
}
|
|
2522
2522
|
toJSON() {
|
|
2523
2523
|
return {
|
|
@@ -2578,7 +2578,7 @@ const makeExit = (options) => {
|
|
|
2578
2578
|
},
|
|
2579
2579
|
...makePrimitiveProto(options),
|
|
2580
2580
|
toString() {
|
|
2581
|
-
return `${options.op}(${format$
|
|
2581
|
+
return `${options.op}(${format$4(this[args])})`;
|
|
2582
2582
|
},
|
|
2583
2583
|
toJSON() {
|
|
2584
2584
|
return {
|
|
@@ -2768,7 +2768,7 @@ const SomeProto = /*#__PURE__*/ Object.defineProperty(/*#__PURE__*/ Object.assig
|
|
|
2768
2768
|
return combine$1(hash(this._tag))(hash(this.value));
|
|
2769
2769
|
},
|
|
2770
2770
|
toString() {
|
|
2771
|
-
return `some(${format$
|
|
2771
|
+
return `some(${format$4(this.value)})`;
|
|
2772
2772
|
},
|
|
2773
2773
|
toJSON() {
|
|
2774
2774
|
return {
|
|
@@ -2840,7 +2840,7 @@ const SuccessProto = /*#__PURE__*/ Object.assign(/*#__PURE__*/ Object.create(Com
|
|
|
2840
2840
|
return combine$1(hash(this._tag))(hash(this.success));
|
|
2841
2841
|
},
|
|
2842
2842
|
toString() {
|
|
2843
|
-
return `success(${format$
|
|
2843
|
+
return `success(${format$4(this.success)})`;
|
|
2844
2844
|
},
|
|
2845
2845
|
toJSON() {
|
|
2846
2846
|
return {
|
|
@@ -2860,7 +2860,7 @@ const FailureProto = /*#__PURE__*/ Object.assign(/*#__PURE__*/ Object.create(Com
|
|
|
2860
2860
|
return combine$1(hash(this._tag))(hash(this.failure));
|
|
2861
2861
|
},
|
|
2862
2862
|
toString() {
|
|
2863
|
-
return `failure(${format$
|
|
2863
|
+
return `failure(${format$4(this.failure)})`;
|
|
2864
2864
|
},
|
|
2865
2865
|
toJSON() {
|
|
2866
2866
|
return {
|
|
@@ -5404,11 +5404,11 @@ const parts = (self) => {
|
|
|
5404
5404
|
* @category converting
|
|
5405
5405
|
* @since 2.0.0
|
|
5406
5406
|
*/
|
|
5407
|
-
const format$
|
|
5407
|
+
const format$3 = (self) => {
|
|
5408
5408
|
if (self.value._tag === "Infinity") return "Infinity";
|
|
5409
5409
|
if (self.value._tag === "NegativeInfinity") return "-Infinity";
|
|
5410
5410
|
if (isZero$1(self)) return "0";
|
|
5411
|
-
if (isNegative(self)) return "-" + format$
|
|
5411
|
+
if (isNegative(self)) return "-" + format$3(abs(self));
|
|
5412
5412
|
const fragments = parts(self);
|
|
5413
5413
|
const pieces = [];
|
|
5414
5414
|
if (fragments.days !== 0) pieces.push(`${fragments.days}d`);
|
|
@@ -7231,7 +7231,7 @@ const exitPrimitive = /*#__PURE__*/ makePrimitive$1({
|
|
|
7231
7231
|
/** @internal */
|
|
7232
7232
|
const timeoutOrElse$1 = /*#__PURE__*/ dual(2, (self, options) => flatMap$2(timeoutOption(self, options.duration), (option) => isNone(option) ? options.orElse() : succeed$6(option.value)));
|
|
7233
7233
|
/** @internal */
|
|
7234
|
-
const timeoutErrorFromDuration = (duration) => new TimeoutError(`Operation timed out after '${format$
|
|
7234
|
+
const timeoutErrorFromDuration = (duration) => new TimeoutError(`Operation timed out after '${format$3(duration)}'`);
|
|
7235
7235
|
/** @internal */
|
|
7236
7236
|
const timeout$1 = /*#__PURE__*/ dual(2, (self, duration) => {
|
|
7237
7237
|
const decoded = fromInputUnsafe$1(duration);
|
|
@@ -7714,6 +7714,23 @@ const forEachConcurrent = /*#__PURE__*/ iterateConcurrentImpl({
|
|
|
7714
7714
|
}
|
|
7715
7715
|
});
|
|
7716
7716
|
/** @internal */
|
|
7717
|
+
const filter$1 = /*#__PURE__*/ dual((args) => isIterable(args[0]) && !isEffect$1(args[0]), (elements, predicate, options) => suspend$3(() => {
|
|
7718
|
+
const out = [];
|
|
7719
|
+
return as$1(forEach$2(elements, (a, i) => {
|
|
7720
|
+
const result = predicate(a, i);
|
|
7721
|
+
if (typeof result === "boolean") {
|
|
7722
|
+
if (result) out.push(a);
|
|
7723
|
+
return void_$2;
|
|
7724
|
+
}
|
|
7725
|
+
return map$7(result, (keep) => {
|
|
7726
|
+
if (keep) out.push(a);
|
|
7727
|
+
});
|
|
7728
|
+
}, {
|
|
7729
|
+
discard: true,
|
|
7730
|
+
concurrency: options?.concurrency
|
|
7731
|
+
}), out);
|
|
7732
|
+
}));
|
|
7733
|
+
/** @internal */
|
|
7717
7734
|
const forkUnsafe$1 = (parent, effect, immediate = false, daemon = false, uninterruptible = false) => {
|
|
7718
7735
|
const parentRuntime = parent;
|
|
7719
7736
|
const interruptible = uninterruptible === "inherit" ? parentRuntime.interruptible : !uninterruptible;
|
|
@@ -9910,6 +9927,8 @@ const hasZone = (input) => /Z|GMT|[+-]\d{2}$|[+-]\d{2}:?\d{2}$|\]$/.test(input);
|
|
|
9910
9927
|
const now$1 = /*#__PURE__*/ map$7(currentTimeMillis, makeUtc);
|
|
9911
9928
|
/** @internal */
|
|
9912
9929
|
const toDateUtc$1 = (self) => new Date(self.epochMilliseconds);
|
|
9930
|
+
/** @internal */
|
|
9931
|
+
const toEpochMillis$1 = (self) => self.epochMilliseconds;
|
|
9913
9932
|
const setPartsDate = (date, parts) => {
|
|
9914
9933
|
if (parts.year !== void 0 || parts.month !== void 0 || parts.day !== void 0) date.setUTCFullYear(parts.year ?? date.getUTCFullYear(), parts.month !== void 0 ? parts.month - 1 : date.getUTCMonth(), parts.day ?? date.getUTCDate());
|
|
9915
9934
|
if (parts.weekDay !== void 0) {
|
|
@@ -9927,6 +9946,29 @@ const formatLocal$1 = /*#__PURE__*/ dual(isDateTimeArgs, (self, options) => new
|
|
|
9927
9946
|
const formatIso$1 = (self) => toDateUtc$1(self).toISOString();
|
|
9928
9947
|
globalThis.Number;
|
|
9929
9948
|
/**
|
|
9949
|
+
* Order instance for `number` values.
|
|
9950
|
+
*
|
|
9951
|
+
* **When to use**
|
|
9952
|
+
*
|
|
9953
|
+
* Use when you need to sort or compare numbers through APIs that accept an
|
|
9954
|
+
* ordering instance.
|
|
9955
|
+
*
|
|
9956
|
+
* **Example** (Comparing numbers)
|
|
9957
|
+
*
|
|
9958
|
+
* ```ts import.meta.vitest
|
|
9959
|
+
* import { Number } from "effect"
|
|
9960
|
+
*
|
|
9961
|
+
* Number.Order(1, 2) // => -1
|
|
9962
|
+
* Number.Order(2, 1) // => 1
|
|
9963
|
+
* Number.Order(1, 1) // => 0
|
|
9964
|
+
* ```
|
|
9965
|
+
*
|
|
9966
|
+
* @stability stable
|
|
9967
|
+
* @category instances
|
|
9968
|
+
* @since 2.0.0
|
|
9969
|
+
*/
|
|
9970
|
+
const Order$1 = Number$6;
|
|
9971
|
+
/**
|
|
9930
9972
|
* Returns the number rounded with the given precision.
|
|
9931
9973
|
*
|
|
9932
9974
|
* **When to use**
|
|
@@ -12173,6 +12215,32 @@ const sleep = sleep$1;
|
|
|
12173
12215
|
*/
|
|
12174
12216
|
const raceFirst = raceFirst$1;
|
|
12175
12217
|
/**
|
|
12218
|
+
* Filters elements of an iterable using a predicate, refinement, or effectful
|
|
12219
|
+
* predicate.
|
|
12220
|
+
*
|
|
12221
|
+
* **Example** (Filtering success values)
|
|
12222
|
+
*
|
|
12223
|
+
* ```ts import.meta.vitest
|
|
12224
|
+
* import { Effect } from "effect"
|
|
12225
|
+
* const output: Array<unknown> = []
|
|
12226
|
+
*
|
|
12227
|
+
* // Sync predicate
|
|
12228
|
+
* const evens = Effect.filter([1, 2, 3, 4], (n) => n % 2 === 0)
|
|
12229
|
+
*
|
|
12230
|
+
* // Effectful predicate
|
|
12231
|
+
* const checked = Effect.filter([1, 2, 3], (n) => Effect.succeed(n > 1))
|
|
12232
|
+
*
|
|
12233
|
+
* void output.push(Effect.runSync(evens))
|
|
12234
|
+
* void output.push(Effect.runSync(checked))
|
|
12235
|
+
* output // => [[2, 4], [2, 3]]
|
|
12236
|
+
* ```
|
|
12237
|
+
*
|
|
12238
|
+
* @stability stable
|
|
12239
|
+
* @category collecting
|
|
12240
|
+
* @since 2.0.0
|
|
12241
|
+
*/
|
|
12242
|
+
const filter = filter$1;
|
|
12243
|
+
/**
|
|
12176
12244
|
* Handles both success and failure cases of an effect without performing side
|
|
12177
12245
|
* effects.
|
|
12178
12246
|
*
|
|
@@ -18376,7 +18444,7 @@ const defaultLeafHook = (issue) => {
|
|
|
18376
18444
|
*/
|
|
18377
18445
|
const defaultCheckHook = (issue) => findMessage(issue.issue) ?? findMessage(issue);
|
|
18378
18446
|
function formatInput(issue) {
|
|
18379
|
-
return hasInput(issue) ? format$
|
|
18447
|
+
return hasInput(issue) ? format$4(issue.input) : void 0;
|
|
18380
18448
|
}
|
|
18381
18449
|
function findExpected(issue) {
|
|
18382
18450
|
const expected = issue.annotations?.expected;
|
|
@@ -18540,6 +18608,27 @@ const makeUnsafe = makeUnsafe$2;
|
|
|
18540
18608
|
*/
|
|
18541
18609
|
const now = now$1;
|
|
18542
18610
|
/**
|
|
18611
|
+
* Gets the milliseconds since the Unix epoch of a `DateTime`.
|
|
18612
|
+
*
|
|
18613
|
+
* **Details**
|
|
18614
|
+
*
|
|
18615
|
+
* This returns the UTC timestamp regardless of any time zone information.
|
|
18616
|
+
*
|
|
18617
|
+
* **Example** (Reading epoch milliseconds)
|
|
18618
|
+
*
|
|
18619
|
+
* ```ts import.meta.vitest
|
|
18620
|
+
* import { DateTime } from "effect"
|
|
18621
|
+
*
|
|
18622
|
+
* const dt = DateTime.makeUnsafe("2024-01-01T00:00:00Z")
|
|
18623
|
+
* DateTime.toEpochMillis(dt) // => 1704067200000
|
|
18624
|
+
* ```
|
|
18625
|
+
*
|
|
18626
|
+
* @stability stable
|
|
18627
|
+
* @category converting
|
|
18628
|
+
* @since 3.6.0
|
|
18629
|
+
*/
|
|
18630
|
+
const toEpochMillis = toEpochMillis$1;
|
|
18631
|
+
/**
|
|
18543
18632
|
* Formats a `DateTime` with `Intl.DateTimeFormat` using the system local time
|
|
18544
18633
|
* zone and locale.
|
|
18545
18634
|
*
|
|
@@ -18820,7 +18909,7 @@ const BigDecimalProto = {
|
|
|
18820
18909
|
return isBigDecimal(that) && compare(this, that) === 0;
|
|
18821
18910
|
},
|
|
18822
18911
|
toString() {
|
|
18823
|
-
return `BigDecimal(${format$
|
|
18912
|
+
return `BigDecimal(${format$2(this)})`;
|
|
18824
18913
|
},
|
|
18825
18914
|
toJSON() {
|
|
18826
18915
|
return {
|
|
@@ -19020,7 +19109,7 @@ const sign = (n) => n.value === bigint0 ? 0 : n.value < bigint0 ? -1 : 1;
|
|
|
19020
19109
|
* @category converting
|
|
19021
19110
|
* @since 2.0.0
|
|
19022
19111
|
*/
|
|
19023
|
-
const format$
|
|
19112
|
+
const format$2 = (n) => {
|
|
19024
19113
|
const normalized = normalize(n);
|
|
19025
19114
|
if (Math.abs(normalized.scale) >= 16) return toExponential(normalized);
|
|
19026
19115
|
const negative = normalized.value < bigint0;
|
|
@@ -19288,9 +19377,9 @@ const decodeJsonError = (input) => {
|
|
|
19288
19377
|
const encodeUnknownAsJson = (input) => {
|
|
19289
19378
|
try {
|
|
19290
19379
|
const json = formatJson(input);
|
|
19291
|
-
return json === void 0 ? format$
|
|
19380
|
+
return json === void 0 ? format$4(input) : JSON.parse(json);
|
|
19292
19381
|
} catch {
|
|
19293
|
-
return format$
|
|
19382
|
+
return format$4(input);
|
|
19294
19383
|
}
|
|
19295
19384
|
};
|
|
19296
19385
|
const encodeJsonError = (input, options, encodeDefect) => {
|
|
@@ -19656,7 +19745,7 @@ const Literal$1 = class extends ASTNodeImpl {
|
|
|
19656
19745
|
literal;
|
|
19657
19746
|
constructor(literal, annotations, checks, encoding, context) {
|
|
19658
19747
|
super(annotations, checks, encoding, context);
|
|
19659
|
-
if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw new Error(`A numeric literal must be finite, got ${format$
|
|
19748
|
+
if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw new Error(`A numeric literal must be finite, got ${format$4(literal)}`);
|
|
19660
19749
|
this.literal = literal;
|
|
19661
19750
|
}
|
|
19662
19751
|
/** @internal */
|
|
@@ -23106,7 +23195,7 @@ const layer$5 = /* @__PURE__ */ succeed$4(Stdio, /*#__PURE__*/ make$9({
|
|
|
23106
23195
|
/** @internal */
|
|
23107
23196
|
const redactedRegistry = /*#__PURE__*/ new WeakMap();
|
|
23108
23197
|
/** @internal */
|
|
23109
|
-
const value$
|
|
23198
|
+
const value$3 = (self) => {
|
|
23110
23199
|
if (redactedRegistry.has(self)) return redactedRegistry.get(self);
|
|
23111
23200
|
else throw new Error("Unable to get redacted value" + (self.label ? ` with label: "${self.label}"` : ""));
|
|
23112
23201
|
};
|
|
@@ -23227,7 +23316,7 @@ const Proto$4 = {
|
|
|
23227
23316
|
* @category getters
|
|
23228
23317
|
* @since 3.3.0
|
|
23229
23318
|
*/
|
|
23230
|
-
const value$
|
|
23319
|
+
const value$2 = value$3;
|
|
23231
23320
|
({ ...PipeInspectableProto });
|
|
23232
23321
|
//#endregion
|
|
23233
23322
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/internal/schema/interpreter.js
|
|
@@ -23574,6 +23663,8 @@ function decodeUnknownEffect$1(schema, options) {
|
|
|
23574
23663
|
function decodeUnknownOption$1(schema, options) {
|
|
23575
23664
|
return asOption(decodeUnknownEffect$1(schema, options));
|
|
23576
23665
|
}
|
|
23666
|
+
/** @internal */
|
|
23667
|
+
const decodeOption$1 = decodeUnknownOption$1;
|
|
23577
23668
|
/**
|
|
23578
23669
|
* Creates a decoder for `unknown` input that reports failure safely as a
|
|
23579
23670
|
* `Result`.
|
|
@@ -25218,6 +25309,33 @@ const decodeEffect = decodeUnknownEffect;
|
|
|
25218
25309
|
*/
|
|
25219
25310
|
const decodeUnknownOption = decodeUnknownOption$1;
|
|
25220
25311
|
/**
|
|
25312
|
+
* Decodes a typed input (the schema's `Encoded` type) against a schema,
|
|
25313
|
+
* returning an `Option` that is `Some` with the decoded value on success or
|
|
25314
|
+
* `None` for schema mismatches.
|
|
25315
|
+
*
|
|
25316
|
+
* **When to use**
|
|
25317
|
+
*
|
|
25318
|
+
* Use when you already have input typed as the schema's `Encoded` type and
|
|
25319
|
+
* only need to know whether decoding succeeded.
|
|
25320
|
+
*
|
|
25321
|
+
* **Details**
|
|
25322
|
+
*
|
|
25323
|
+
* For `unknown` input use {@link decodeUnknownOption}.
|
|
25324
|
+
* Options may be provided either when creating the decoder or when applying it;
|
|
25325
|
+
* application options override creation options.
|
|
25326
|
+
*
|
|
25327
|
+
* **Gotchas**
|
|
25328
|
+
*
|
|
25329
|
+
* Only causes made entirely of schema issues are converted to `None`. Causes
|
|
25330
|
+
* that contain defects, interruptions, or other non-schema reasons throw
|
|
25331
|
+
* instead.
|
|
25332
|
+
*
|
|
25333
|
+
* @stability stable
|
|
25334
|
+
* @category decoding
|
|
25335
|
+
* @since 3.10.0
|
|
25336
|
+
*/
|
|
25337
|
+
const decodeOption = decodeOption$1;
|
|
25338
|
+
/**
|
|
25221
25339
|
* Decodes an `unknown` input against a schema, returning a `Result` that
|
|
25222
25340
|
* succeeds with the decoded value or fails with a {@link SchemaError} for schema
|
|
25223
25341
|
* mismatches.
|
|
@@ -25996,7 +26114,7 @@ function isPattern(regExp, annotations) {
|
|
|
25996
26114
|
const source = regExp.source;
|
|
25997
26115
|
const flags = regExp.flags;
|
|
25998
26116
|
const canExport = /^[dg]*uy?$/.test(flags);
|
|
25999
|
-
const runtimeRegExp = flags === "" ? `new RegExp(${format$
|
|
26117
|
+
const runtimeRegExp = flags === "" ? `new RegExp(${format$4(source)})` : `new RegExp(${format$4(source)}, ${format$4(flags)})`;
|
|
26000
26118
|
return isPattern$1(regExp, {
|
|
26001
26119
|
toJsonSchema: () => canExport ? { pattern: flags.endsWith("y") ? `^(?:${source})` : source } : [{}, true],
|
|
26002
26120
|
toCode: () => ({ runtime: `Schema.isPattern(${runtimeRegExp})` }),
|
|
@@ -27428,7 +27546,7 @@ const String$5 = /*#__PURE__*/ makePrimitive("String", (value) => succeed$3(valu
|
|
|
27428
27546
|
*/
|
|
27429
27547
|
const Choice = (choices) => {
|
|
27430
27548
|
const choiceMap = new Map(choices);
|
|
27431
|
-
const validChoices = choices.map(([key]) => format$
|
|
27549
|
+
const validChoices = choices.map(([key]) => format$4(key)).join(" | ");
|
|
27432
27550
|
const primitive = makePrimitive("Choice", (value) => {
|
|
27433
27551
|
if (choiceMap.has(value)) return succeed$3(choiceMap.get(value));
|
|
27434
27552
|
return fail$1(validChoices);
|
|
@@ -27978,6 +28096,40 @@ const Select = (options) => {
|
|
|
27978
28096
|
});
|
|
27979
28097
|
};
|
|
27980
28098
|
/**
|
|
28099
|
+
* Creates a prompt that lets the user select multiple choices and returns their
|
|
28100
|
+
* values as an array.
|
|
28101
|
+
*
|
|
28102
|
+
* **Details**
|
|
28103
|
+
*
|
|
28104
|
+
* The prompt supports default selected choices, bulk-selection commands, and
|
|
28105
|
+
* minimum or maximum selection counts.
|
|
28106
|
+
*
|
|
28107
|
+
* @stability unstable
|
|
28108
|
+
* @category constructors
|
|
28109
|
+
* @since 4.0.0
|
|
28110
|
+
*/
|
|
28111
|
+
const MultiSelect = (options) => {
|
|
28112
|
+
const opts = {
|
|
28113
|
+
maxPerPage: 10,
|
|
28114
|
+
...options
|
|
28115
|
+
};
|
|
28116
|
+
const initialSelected = /* @__PURE__ */ new Set();
|
|
28117
|
+
for (let i = 0; i < opts.choices.length; i++) {
|
|
28118
|
+
const choice = opts.choices[i];
|
|
28119
|
+
if (choice.selected === true && !choice.disabled) initialSelected.add(i);
|
|
28120
|
+
}
|
|
28121
|
+
const initialState = {
|
|
28122
|
+
index: 0,
|
|
28123
|
+
selectedIndices: initialSelected,
|
|
28124
|
+
error: none()
|
|
28125
|
+
};
|
|
28126
|
+
return Custom(initialState, {
|
|
28127
|
+
render: handleMultiSelectRender(opts),
|
|
28128
|
+
process: handleMultiSelectProcess(opts),
|
|
28129
|
+
clear: handleMultiSelectClear(opts)
|
|
28130
|
+
});
|
|
28131
|
+
};
|
|
28132
|
+
/**
|
|
27981
28133
|
* Creates a `Prompt` which immediately succeeds with the specified value.
|
|
27982
28134
|
*
|
|
27983
28135
|
* **Details**
|
|
@@ -28090,12 +28242,12 @@ const eraseText = (text, columns) => {
|
|
|
28090
28242
|
for (const line of lines) rows += 1 + Math.floor(Math.max(line.length - 1, 0) / columns);
|
|
28091
28243
|
return eraseLines$1(rows);
|
|
28092
28244
|
};
|
|
28093
|
-
const lines = (prompt, columns) => {
|
|
28245
|
+
const lines$1 = (prompt, columns) => {
|
|
28094
28246
|
const lines = prompt.split(NEWLINE_REGEXP);
|
|
28095
28247
|
return columns === 0 ? lines.length : pipe$1(map$8(lines, (line) => Math.ceil(line.length / columns)), reduce(0, (left, right) => left + right));
|
|
28096
28248
|
};
|
|
28097
28249
|
const clearOutputWithError = (outputText, columns, errorText) => {
|
|
28098
|
-
if (errorText !== void 0 && errorText.length > 0) return cursorDown$1(lines(errorText, columns)) + eraseText(`\n${errorText}`, columns) + eraseText(outputText, columns);
|
|
28250
|
+
if (errorText !== void 0 && errorText.length > 0) return cursorDown$1(lines$1(errorText, columns)) + eraseText(`\n${errorText}`, columns) + eraseText(outputText, columns);
|
|
28099
28251
|
return eraseText(outputText, columns);
|
|
28100
28252
|
};
|
|
28101
28253
|
const renderBeep = "\x07";
|
|
@@ -28582,6 +28734,18 @@ const renderPrompt = (confirm, message, leadingSymbol, trailingSymbol, options)
|
|
|
28582
28734
|
}
|
|
28583
28735
|
});
|
|
28584
28736
|
};
|
|
28737
|
+
const renderMultiSelectError = (state, pointer, theme, renderOptions) => {
|
|
28738
|
+
if (isSome(state.error)) return match$3(state.error.value.split(NEWLINE_REGEXP), {
|
|
28739
|
+
onEmpty: () => "",
|
|
28740
|
+
onNonEmpty: (errorLines) => {
|
|
28741
|
+
if (renderOptions?.plain === true) return separateSymbol(pointer, errorLines.join("\n"));
|
|
28742
|
+
const prefix = annotateSymbol(pointer, theme.errorColor);
|
|
28743
|
+
const lines = map$8(errorLines, (str) => annotateErrorLine(str, theme.errorColor));
|
|
28744
|
+
return cursorSavePosition + "\n" + separateSymbol(prefix, lines.join("\n")) + cursorRestorePosition;
|
|
28745
|
+
}
|
|
28746
|
+
});
|
|
28747
|
+
return "";
|
|
28748
|
+
};
|
|
28585
28749
|
const renderChoiceDescription = (choice, isActive, theme, renderOptions) => {
|
|
28586
28750
|
if (!choice.disabled && choice.description && isActive) {
|
|
28587
28751
|
const text = theme.descriptionSeparator + choice.description;
|
|
@@ -28589,6 +28753,143 @@ const renderChoiceDescription = (choice, isActive, theme, renderOptions) => {
|
|
|
28589
28753
|
}
|
|
28590
28754
|
return "";
|
|
28591
28755
|
};
|
|
28756
|
+
const metaOptionsCount = 2;
|
|
28757
|
+
const renderMultiSelectTitle = (title, isHighlighted, theme, renderOptions) => {
|
|
28758
|
+
if (renderOptions?.plain === true || !isHighlighted) return title;
|
|
28759
|
+
return annotate(title, combine(underlined, theme.primaryColor));
|
|
28760
|
+
};
|
|
28761
|
+
const renderMultiSelectChoices = (state, options, figures, renderOptions) => {
|
|
28762
|
+
const choices = options.choices;
|
|
28763
|
+
const checkboxWidth = Math.max(figures.checkboxOn.length, figures.checkboxOff.length);
|
|
28764
|
+
const selectableCount = choices.filter((choice) => !choice.disabled).length;
|
|
28765
|
+
const selectAllText = Array.from(state.selectedIndices).filter((index) => !choices[index].disabled).length === selectableCount ? options?.selectNone ?? "Select None" : options?.selectAll ?? "Select All";
|
|
28766
|
+
const inverseSelectionText = options?.inverseSelection ?? "Inverse Selection";
|
|
28767
|
+
const metaOptions = [{ title: selectAllText }, { title: inverseSelectionText }];
|
|
28768
|
+
const allChoices = [...metaOptions, ...choices];
|
|
28769
|
+
const toDisplay = entriesToDisplay(state.index, allChoices.length, options.maxPerPage);
|
|
28770
|
+
const documents = [];
|
|
28771
|
+
for (let index = toDisplay.startIndex; index < toDisplay.endIndex; index++) {
|
|
28772
|
+
const choice = allChoices[index];
|
|
28773
|
+
const isHighlighted = state.index === index;
|
|
28774
|
+
const prefix = renderPagingPrefix(figures, index === toDisplay.startIndex && toDisplay.startIndex > 0, index === toDisplay.endIndex - 1 && toDisplay.endIndex < allChoices.length);
|
|
28775
|
+
if (index < metaOptions.length) {
|
|
28776
|
+
const title = renderMultiSelectTitle(choice.title, isHighlighted, figures, renderOptions);
|
|
28777
|
+
documents.push(prefix + (prefix.length === 0 ? "" : " ") + title);
|
|
28778
|
+
} else {
|
|
28779
|
+
const choiceIndex = index - metaOptions.length;
|
|
28780
|
+
const checkbox = state.selectedIndices.has(choiceIndex) ? figures.checkboxOn : figures.checkboxOff;
|
|
28781
|
+
const annotatedCheckbox = isHighlighted && renderOptions?.plain !== true ? annotate(checkbox, figures.primaryColor) : checkbox;
|
|
28782
|
+
const selectChoice = choice;
|
|
28783
|
+
const title = renderChoiceTitle(selectChoice, isHighlighted, figures, renderOptions);
|
|
28784
|
+
const description = renderChoiceDescription(selectChoice, isHighlighted, figures, renderOptions);
|
|
28785
|
+
const checkboxPrefix = checkboxWidth === 0 ? "" : annotatedCheckbox + " ".repeat(checkboxWidth - checkbox.length + 1);
|
|
28786
|
+
documents.push(prefix + (prefix.length === 0 ? "" : " ") + checkboxPrefix + title + " " + description);
|
|
28787
|
+
}
|
|
28788
|
+
}
|
|
28789
|
+
return documents.join("\n");
|
|
28790
|
+
};
|
|
28791
|
+
const renderMultiSelectNextFrame = /*#__PURE__*/ fnUntraced(function* (state, options) {
|
|
28792
|
+
const figures = yield* getTheme(options);
|
|
28793
|
+
const choices = renderMultiSelectChoices(state, options, figures);
|
|
28794
|
+
const leadingSymbol = annotateSymbol(figures.prefix, figures.primaryColor);
|
|
28795
|
+
const trailingSymbol = annotateSymbol(figures.pointerSmall, figures.mutedColor);
|
|
28796
|
+
const promptMsg = renderSelectOutput(leadingSymbol, trailingSymbol, options);
|
|
28797
|
+
const error = renderMultiSelectError(state, figures.pointer, figures);
|
|
28798
|
+
return cursorHide + withSelectHeader(promptMsg, choices) + error;
|
|
28799
|
+
});
|
|
28800
|
+
const renderMultiSelectSubmission = /*#__PURE__*/ fnUntraced(function* (state, options) {
|
|
28801
|
+
const figures = yield* getTheme(options);
|
|
28802
|
+
const selectedText = Array.from(state.selectedIndices).sort(Order$1).map((index) => options.choices[index].title).join(", ");
|
|
28803
|
+
const leadingSymbol = annotateSymbol(figures.tick, figures.successColor);
|
|
28804
|
+
const trailingSymbol = annotateSymbol(figures.ellipsis, figures.mutedColor);
|
|
28805
|
+
return (renderSelectOutput(leadingSymbol, trailingSymbol, options) ?? leadingSymbol) + " " + annotate(selectedText, figures.submittedColor) + "\n";
|
|
28806
|
+
});
|
|
28807
|
+
const processMultiSelectCursorUp = (state, totalChoices) => {
|
|
28808
|
+
const newIndex = state.index === 0 ? totalChoices - 1 : state.index - 1;
|
|
28809
|
+
return succeed$3(Action$1.NextFrame({ state: {
|
|
28810
|
+
...state,
|
|
28811
|
+
index: newIndex
|
|
28812
|
+
} }));
|
|
28813
|
+
};
|
|
28814
|
+
const processMultiSelectCursorDown = (state, totalChoices) => {
|
|
28815
|
+
const newIndex = (state.index + 1) % totalChoices;
|
|
28816
|
+
return succeed$3(Action$1.NextFrame({ state: {
|
|
28817
|
+
...state,
|
|
28818
|
+
index: newIndex
|
|
28819
|
+
} }));
|
|
28820
|
+
};
|
|
28821
|
+
const processSpace = (state, options) => {
|
|
28822
|
+
const selectedIndices = new Set(state.selectedIndices);
|
|
28823
|
+
if (state.index === 0) {
|
|
28824
|
+
const selectableCount = options.choices.filter((choice) => !choice.disabled).length;
|
|
28825
|
+
if (Array.from(state.selectedIndices).filter((index) => !options.choices[index].disabled).length === selectableCount) selectedIndices.clear();
|
|
28826
|
+
else for (let i = 0; i < options.choices.length; i++) if (!options.choices[i].disabled) selectedIndices.add(i);
|
|
28827
|
+
} else if (state.index === 1) for (let i = 0; i < options.choices.length; i++) if (options.choices[i].disabled || state.selectedIndices.has(i)) selectedIndices.delete(i);
|
|
28828
|
+
else selectedIndices.add(i);
|
|
28829
|
+
else {
|
|
28830
|
+
const choiceIndex = state.index - metaOptionsCount;
|
|
28831
|
+
if (options.choices[choiceIndex].disabled) return succeed$3(Action$1.Beep());
|
|
28832
|
+
else if (selectedIndices.has(choiceIndex)) selectedIndices.delete(choiceIndex);
|
|
28833
|
+
else selectedIndices.add(choiceIndex);
|
|
28834
|
+
}
|
|
28835
|
+
return succeed$3(Action$1.NextFrame({ state: {
|
|
28836
|
+
...state,
|
|
28837
|
+
selectedIndices
|
|
28838
|
+
} }));
|
|
28839
|
+
};
|
|
28840
|
+
const handleMultiSelectClear = (options) => fnUntraced(function* (state, _) {
|
|
28841
|
+
const columns = yield* (yield* Terminal).columns;
|
|
28842
|
+
const figures = yield* getTheme(options);
|
|
28843
|
+
const clearPrompt = eraseLine$1 + cursorLeft$1;
|
|
28844
|
+
const promptText = renderSelectOutput(figures.prefix, figures.pointerSmall, options, { plain: true });
|
|
28845
|
+
const choicesText = renderMultiSelectChoices(state, options, figures, { plain: true });
|
|
28846
|
+
const errorText = renderMultiSelectError(state, figures.pointer, figures, { plain: true });
|
|
28847
|
+
return clearOutputWithError(withSelectHeader(promptText, choicesText), columns, errorText) + clearPrompt;
|
|
28848
|
+
});
|
|
28849
|
+
const handleMultiSelectProcess = (options) => {
|
|
28850
|
+
return (input, state) => {
|
|
28851
|
+
const totalChoices = options.choices.length + metaOptionsCount;
|
|
28852
|
+
switch (input.key.name) {
|
|
28853
|
+
case "k":
|
|
28854
|
+
case "up": return processMultiSelectCursorUp({
|
|
28855
|
+
...state,
|
|
28856
|
+
error: none()
|
|
28857
|
+
}, totalChoices);
|
|
28858
|
+
case "j":
|
|
28859
|
+
case "down":
|
|
28860
|
+
case "tab": return processMultiSelectCursorDown({
|
|
28861
|
+
...state,
|
|
28862
|
+
error: none()
|
|
28863
|
+
}, totalChoices);
|
|
28864
|
+
case "space": return processSpace(state, options);
|
|
28865
|
+
case "enter":
|
|
28866
|
+
case "return": {
|
|
28867
|
+
const selectedIndices = Array.from(state.selectedIndices).filter((index) => !options.choices[index].disabled);
|
|
28868
|
+
const selectedCount = selectedIndices.length;
|
|
28869
|
+
if (options.min !== void 0 && selectedCount < options.min) return succeed$3(Action$1.NextFrame({ state: {
|
|
28870
|
+
...state,
|
|
28871
|
+
error: some(`At least ${options.min} are required`)
|
|
28872
|
+
} }));
|
|
28873
|
+
if (options.max !== void 0 && selectedCount > options.max) return succeed$3(Action$1.NextFrame({ state: {
|
|
28874
|
+
...state,
|
|
28875
|
+
error: some(`At most ${options.max} choices are allowed`)
|
|
28876
|
+
} }));
|
|
28877
|
+
const selectedValues = selectedIndices.sort(Order$1).map((index) => options.choices[index].value);
|
|
28878
|
+
return succeed$3(Action$1.Submit({ value: selectedValues }));
|
|
28879
|
+
}
|
|
28880
|
+
default: return succeed$3(Action$1.Beep());
|
|
28881
|
+
}
|
|
28882
|
+
};
|
|
28883
|
+
};
|
|
28884
|
+
const handleMultiSelectRender = (options) => {
|
|
28885
|
+
return (state, action) => {
|
|
28886
|
+
return Action$1.$match(action, {
|
|
28887
|
+
Beep: () => succeed$3(renderBeep),
|
|
28888
|
+
NextFrame: ({ state }) => renderMultiSelectNextFrame(state, options),
|
|
28889
|
+
Submit: () => renderMultiSelectSubmission(state, options)
|
|
28890
|
+
});
|
|
28891
|
+
};
|
|
28892
|
+
};
|
|
28592
28893
|
const handleNumberClear = (options) => {
|
|
28593
28894
|
return fnUntraced(function* (state, _) {
|
|
28594
28895
|
const columns = yield* (yield* Terminal).columns;
|
|
@@ -29597,7 +29898,7 @@ const withDefault$1 = /*#__PURE__*/ dual(2, (self, defaultValue) => {
|
|
|
29597
29898
|
* @category combinators
|
|
29598
29899
|
* @since 4.0.0
|
|
29599
29900
|
*/
|
|
29600
|
-
const variadic = (self, options) => {
|
|
29901
|
+
const variadic$1 = (self, options) => {
|
|
29601
29902
|
const single = getUnderlyingSingleOrThrow(self);
|
|
29602
29903
|
const parse = (args) => {
|
|
29603
29904
|
if (single.kind === "argument") return parsePositionalVariadic(self, single, args, options);
|
|
@@ -29642,7 +29943,7 @@ const variadic = (self, options) => {
|
|
|
29642
29943
|
*/
|
|
29643
29944
|
const atLeast$1 = /*#__PURE__*/ dual(2, (self, min) => {
|
|
29644
29945
|
if (min < 0) throw new Error("atLeast: min must be non-negative");
|
|
29645
|
-
return variadic(self, { min });
|
|
29946
|
+
return variadic$1(self, { min });
|
|
29646
29947
|
});
|
|
29647
29948
|
/**
|
|
29648
29949
|
* Sets a custom metavar (placeholder name) for the param in help documentation.
|
|
@@ -29771,7 +30072,7 @@ const transformSingle = (param, f) => {
|
|
|
29771
30072
|
Map: (mapped) => map$1(transformSingle(mapped.param, f), mapped.f),
|
|
29772
30073
|
Transform: (mapped) => transform(transformSingle(mapped.param, f), mapped.f, mapped.alternatives.map((alternative) => () => transformSingle(alternative(), f))),
|
|
29773
30074
|
Optional: (p) => optional$2(transformSingle(p.param, f)),
|
|
29774
|
-
Variadic: (p) => variadic(transformSingle(p.param, f), {
|
|
30075
|
+
Variadic: (p) => variadic$1(transformSingle(p.param, f), {
|
|
29775
30076
|
min: getOrUndefined$1(p.min),
|
|
29776
30077
|
max: getOrUndefined$1(p.max)
|
|
29777
30078
|
})
|
|
@@ -29886,6 +30187,35 @@ const optional$1 = (arg) => optional$2(arg);
|
|
|
29886
30187
|
* @since 4.0.0
|
|
29887
30188
|
*/
|
|
29888
30189
|
const withDescription$2 = /*#__PURE__*/ dual(2, (self, description) => withDescription$3(self, description));
|
|
30190
|
+
/**
|
|
30191
|
+
* Creates a variadic positional argument that accepts multiple values.
|
|
30192
|
+
*
|
|
30193
|
+
* **Example** (Accepting multiple values)
|
|
30194
|
+
*
|
|
30195
|
+
* ```ts import.meta.vitest
|
|
30196
|
+
* import { Argument } from "effect/cli"
|
|
30197
|
+
*
|
|
30198
|
+
* // Accept any number of files
|
|
30199
|
+
* const anyFiles = Argument.String("files").pipe(Argument.variadic)
|
|
30200
|
+
*
|
|
30201
|
+
* // Accept at least 1 file
|
|
30202
|
+
* const atLeastOneFile = Argument.String("files").pipe(
|
|
30203
|
+
* Argument.variadic({ min: 1 })
|
|
30204
|
+
* )
|
|
30205
|
+
*
|
|
30206
|
+
* // Accept between 1 and 5 files
|
|
30207
|
+
* const limitedFiles = Argument.String("files").pipe(
|
|
30208
|
+
* Argument.variadic({ min: 1, max: 5 })
|
|
30209
|
+
* )
|
|
30210
|
+
*
|
|
30211
|
+
* const kinds = [anyFiles.kind, atLeastOneFile.kind, limitedFiles.kind] // => ["argument", "argument", "argument"]
|
|
30212
|
+
* ```
|
|
30213
|
+
*
|
|
30214
|
+
* @stability unstable
|
|
30215
|
+
* @category combinators
|
|
30216
|
+
* @since 4.0.0
|
|
30217
|
+
*/
|
|
30218
|
+
const variadic = /*#__PURE__*/ dual((args) => isParam(args[0]), (self, options) => variadic$1(self, options));
|
|
29889
30219
|
//#endregion
|
|
29890
30220
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Console.js
|
|
29891
30221
|
/**
|
|
@@ -30183,7 +30513,7 @@ const visualLength = (text) => {
|
|
|
30183
30513
|
* Helper function to pad strings to a specified width.
|
|
30184
30514
|
* @internal
|
|
30185
30515
|
*/
|
|
30186
|
-
const pad = (s, width) => {
|
|
30516
|
+
const pad$1 = (s, width) => {
|
|
30187
30517
|
const actualLength = visualLength(s);
|
|
30188
30518
|
const padding = Math.max(0, width - actualLength);
|
|
30189
30519
|
return s + " ".repeat(padding);
|
|
@@ -30195,7 +30525,7 @@ const pad = (s, width) => {
|
|
|
30195
30525
|
const renderTable = (rows, widthCap) => {
|
|
30196
30526
|
const maxColumn = Math.max(...rows.map((r) => visualLength(r.left))) + 4;
|
|
30197
30527
|
const col = widthCap === void 0 ? maxColumn : Math.min(maxColumn, widthCap);
|
|
30198
|
-
return rows.map(({ left, right }) => ` ${pad(left, Math.max(col, visualLength(left) + 1))}${right}`).join("\n");
|
|
30528
|
+
return rows.map(({ left, right }) => ` ${pad$1(left, Math.max(col, visualLength(left) + 1))}${right}`).join("\n");
|
|
30199
30529
|
};
|
|
30200
30530
|
const formatSubcommandName = (name, alias) => alias ? `${name}, ${alias}` : name;
|
|
30201
30531
|
/**
|
|
@@ -31273,6 +31603,40 @@ const parseConfig$1 = (config) => {
|
|
|
31273
31603
|
* @internal
|
|
31274
31604
|
*/
|
|
31275
31605
|
const emptyConfig = /*#__PURE__*/ parseConfig$1({});
|
|
31606
|
+
const shiftNodeIndexes = (node, offset) => {
|
|
31607
|
+
switch (node._tag) {
|
|
31608
|
+
case "Param": return {
|
|
31609
|
+
_tag: "Param",
|
|
31610
|
+
index: node.index + offset
|
|
31611
|
+
};
|
|
31612
|
+
case "Array": return {
|
|
31613
|
+
_tag: "Array",
|
|
31614
|
+
children: node.children.map((child) => shiftNodeIndexes(child, offset))
|
|
31615
|
+
};
|
|
31616
|
+
case "Nested": return {
|
|
31617
|
+
_tag: "Nested",
|
|
31618
|
+
tree: shiftTreeIndexes(node.tree, offset)
|
|
31619
|
+
};
|
|
31620
|
+
}
|
|
31621
|
+
};
|
|
31622
|
+
const shiftTreeIndexes = (tree, offset) => {
|
|
31623
|
+
const output = Object.create(null);
|
|
31624
|
+
for (const key of Object.keys(tree)) output[key] = shiftNodeIndexes(tree[key], offset);
|
|
31625
|
+
return output;
|
|
31626
|
+
};
|
|
31627
|
+
/**
|
|
31628
|
+
* @internal
|
|
31629
|
+
*/
|
|
31630
|
+
const mergeConfig = (left, right) => {
|
|
31631
|
+
const offset = left.orderedParams.length;
|
|
31632
|
+
return {
|
|
31633
|
+
[ConfigInternalTypeId]: ConfigInternalTypeId,
|
|
31634
|
+
flags: [...left.flags, ...right.flags],
|
|
31635
|
+
arguments: [...left.arguments, ...right.arguments],
|
|
31636
|
+
orderedParams: [...left.orderedParams, ...right.orderedParams],
|
|
31637
|
+
tree: Object.assign(Object.create(null), left.tree, shiftTreeIndexes(right.tree, offset))
|
|
31638
|
+
};
|
|
31639
|
+
};
|
|
31276
31640
|
/**
|
|
31277
31641
|
* Reconstructs the original nested config shape from parsed values.
|
|
31278
31642
|
*
|
|
@@ -32652,7 +33016,7 @@ const promptSingle = (single) => {
|
|
|
32652
33016
|
case "Date": return map$5(run$2(Date$1({ message })), (date) => commandLineArg(date.toISOString()));
|
|
32653
33017
|
case "Finite": return map$5(run$2(Number$1({ message })), (value) => commandLineArg(String(value)));
|
|
32654
33018
|
case "Int": return map$5(run$2(Int({ message })), (value) => commandLineArg(String(value)));
|
|
32655
|
-
case "Redacted": return map$5(run$2(Password({ message })), (value) => commandLineArg(value$
|
|
33019
|
+
case "Redacted": return map$5(run$2(Password({ message })), (value) => commandLineArg(value$2(value), "<redacted>"));
|
|
32656
33020
|
default: return map$5(run$2(String$4({ message })), commandLineArg);
|
|
32657
33021
|
}
|
|
32658
33022
|
};
|
|
@@ -33033,6 +33397,64 @@ const withSubcommands = /*#__PURE__*/ dual(2, (self, subcommands) => {
|
|
|
33033
33397
|
});
|
|
33034
33398
|
});
|
|
33035
33399
|
/**
|
|
33400
|
+
* Adds flags that are inherited by subcommands.
|
|
33401
|
+
*
|
|
33402
|
+
* **Details**
|
|
33403
|
+
*
|
|
33404
|
+
* Shared flags are available to this command's handler and to descendant
|
|
33405
|
+
* handlers via `yield* parentCommand`. Shared flags are accepted both before
|
|
33406
|
+
* and after a selected subcommand name (npm-style).
|
|
33407
|
+
*
|
|
33408
|
+
* @stability unstable
|
|
33409
|
+
* @category combinators
|
|
33410
|
+
* @since 4.0.0
|
|
33411
|
+
*/
|
|
33412
|
+
const withSharedFlags = /*#__PURE__*/ dual(2, (self, sharedFlags) => {
|
|
33413
|
+
const impl = toImpl(self);
|
|
33414
|
+
const sharedConfig = parseConfig$1(sharedFlags);
|
|
33415
|
+
const mergedConfig = mergeConfig(impl.config, sharedConfig);
|
|
33416
|
+
const mergedContextConfig = mergeConfig(impl.contextConfig, sharedConfig);
|
|
33417
|
+
if (impl.subcommands.length > 0) {
|
|
33418
|
+
const flatSubcommands = impl.subcommands.flatMap((group) => group.commands);
|
|
33419
|
+
checkForDuplicateFlags(self, flatSubcommands, { contextConfig: mergedContextConfig });
|
|
33420
|
+
}
|
|
33421
|
+
const parseShared = makeParser(sharedConfig, { allowLeftovers: true });
|
|
33422
|
+
const parse = fnUntraced(function* (raw) {
|
|
33423
|
+
const base = yield* impl.parse(raw);
|
|
33424
|
+
const shared = yield* parseShared(raw);
|
|
33425
|
+
return {
|
|
33426
|
+
...base,
|
|
33427
|
+
...shared
|
|
33428
|
+
};
|
|
33429
|
+
});
|
|
33430
|
+
const parseContext = fnUntraced(function* (raw) {
|
|
33431
|
+
const base = yield* impl.parseContext(raw);
|
|
33432
|
+
const shared = yield* parseShared(raw);
|
|
33433
|
+
return {
|
|
33434
|
+
...base,
|
|
33435
|
+
...shared
|
|
33436
|
+
};
|
|
33437
|
+
});
|
|
33438
|
+
const handle = (input, commandPath) => impl.handle(input, commandPath);
|
|
33439
|
+
return makeCommand({
|
|
33440
|
+
name: impl.name,
|
|
33441
|
+
config: mergedConfig,
|
|
33442
|
+
contextConfig: mergedContextConfig,
|
|
33443
|
+
description: impl.description,
|
|
33444
|
+
shortDescription: impl.shortDescription,
|
|
33445
|
+
alias: impl.alias,
|
|
33446
|
+
unlisted: impl.unlisted,
|
|
33447
|
+
annotations: impl.annotations,
|
|
33448
|
+
globalFlags: impl.globalFlags,
|
|
33449
|
+
examples: impl.examples,
|
|
33450
|
+
service: impl.service,
|
|
33451
|
+
subcommands: impl.subcommands,
|
|
33452
|
+
parse,
|
|
33453
|
+
parseContext,
|
|
33454
|
+
handle
|
|
33455
|
+
});
|
|
33456
|
+
});
|
|
33457
|
+
/**
|
|
33036
33458
|
* Sets the description for a command.
|
|
33037
33459
|
*
|
|
33038
33460
|
* **Details**
|
|
@@ -33342,264 +33764,14 @@ const runWith = (command, config) => {
|
|
|
33342
33764
|
};
|
|
33343
33765
|
//#endregion
|
|
33344
33766
|
//#region package.json
|
|
33345
|
-
var version = "0.
|
|
33346
|
-
//#endregion
|
|
33347
|
-
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Logger.js
|
|
33348
|
-
const TypeId = "~effect/Logger";
|
|
33349
|
-
/**
|
|
33350
|
-
* Returns `true` if the specified value is a `Logger`, otherwise returns `false`.
|
|
33351
|
-
*
|
|
33352
|
-
* **Example** (Checking logger values)
|
|
33353
|
-
*
|
|
33354
|
-
* ```ts import.meta.vitest
|
|
33355
|
-
* import { Logger } from "effect"
|
|
33356
|
-
*
|
|
33357
|
-
* const myLogger = Logger.make(() => undefined)
|
|
33358
|
-
*
|
|
33359
|
-
* Logger.isLogger(myLogger) // => true
|
|
33360
|
-
* Logger.isLogger("not a logger") // => false
|
|
33361
|
-
* Logger.isLogger({ log: () => {} }) // => false
|
|
33362
|
-
* ```
|
|
33363
|
-
*
|
|
33364
|
-
* @stability stable
|
|
33365
|
-
* @category guards
|
|
33366
|
-
* @since 4.0.0
|
|
33367
|
-
*/
|
|
33368
|
-
const isLogger = (u) => hasProperty(u, TypeId);
|
|
33369
|
-
/**
|
|
33370
|
-
* Context reference containing the active loggers for the current fiber.
|
|
33371
|
-
*
|
|
33372
|
-
* **Details**
|
|
33373
|
-
*
|
|
33374
|
-
* By default this set includes the default logger and the tracer logger.
|
|
33375
|
-
* Providing `Logger.layer` replaces or merges with this set depending on its
|
|
33376
|
-
* options.
|
|
33377
|
-
*
|
|
33378
|
-
* **Example** (Accessing current loggers)
|
|
33379
|
-
*
|
|
33380
|
-
* ```ts import.meta.vitest
|
|
33381
|
-
* import { Effect, Logger } from "effect"
|
|
33382
|
-
*
|
|
33383
|
-
* const messages: Array<unknown> = []
|
|
33384
|
-
* const customLogger = Logger.make((options) => {
|
|
33385
|
-
* messages.push(options.message)
|
|
33386
|
-
* })
|
|
33387
|
-
* const program = Effect.gen(function*() {
|
|
33388
|
-
* const currentLoggers = yield* Effect.service(Logger.CurrentLoggers)
|
|
33389
|
-
* yield* Effect.log("Hello from custom logger").pipe(
|
|
33390
|
-
* Effect.provide(Logger.layer([customLogger]))
|
|
33391
|
-
* )
|
|
33392
|
-
* return currentLoggers.has(Logger.defaultLogger)
|
|
33393
|
-
* })
|
|
33394
|
-
*
|
|
33395
|
-
* Effect.runSync(program) // => true
|
|
33396
|
-
* messages // => [["Hello from custom logger"]]
|
|
33397
|
-
* ```
|
|
33398
|
-
*
|
|
33399
|
-
* @stability stable
|
|
33400
|
-
* @category services
|
|
33401
|
-
* @since 4.0.0
|
|
33402
|
-
*/
|
|
33403
|
-
const CurrentLoggers = CurrentLoggers$1;
|
|
33404
|
-
/**
|
|
33405
|
-
* Creates a new `Logger` from a log function.
|
|
33406
|
-
*
|
|
33407
|
-
* **Details**
|
|
33408
|
-
*
|
|
33409
|
-
* The log function receives an options object containing the message, log level,
|
|
33410
|
-
* cause, fiber information, and timestamp, and should return the desired output.
|
|
33411
|
-
*
|
|
33412
|
-
* **Example** (Creating loggers from functions)
|
|
33413
|
-
*
|
|
33414
|
-
* ```ts import.meta.vitest
|
|
33415
|
-
* import { Effect, Logger } from "effect"
|
|
33416
|
-
*
|
|
33417
|
-
* const outputs: Array<string> = []
|
|
33418
|
-
* const textLogger = Logger.make((options) =>
|
|
33419
|
-
* `${options.logLevel}: ${options.message}`
|
|
33420
|
-
* )
|
|
33421
|
-
* const collector = Logger.make((options) => outputs.push(textLogger.log(options)))
|
|
33422
|
-
*
|
|
33423
|
-
* const program = Effect.log("Hello World").pipe(
|
|
33424
|
-
* Effect.provide(Logger.layer([collector]))
|
|
33425
|
-
* )
|
|
33426
|
-
* Effect.runSync(program)
|
|
33427
|
-
* outputs // => ["Info: Hello World"]
|
|
33428
|
-
* ```
|
|
33429
|
-
*
|
|
33430
|
-
* @stability stable
|
|
33431
|
-
* @category constructors
|
|
33432
|
-
* @since 2.0.0
|
|
33433
|
-
*/
|
|
33434
|
-
const make = loggerMake;
|
|
33435
|
-
/**
|
|
33436
|
-
* Creates a scoped logger that batches the output of another logger.
|
|
33437
|
-
*
|
|
33438
|
-
* **Details**
|
|
33439
|
-
*
|
|
33440
|
-
* The returned effect starts a scoped background process that periodically
|
|
33441
|
-
* passes buffered outputs to `flush`. When the scope closes, the background
|
|
33442
|
-
* process is interrupted and any remaining buffered entries are flushed.
|
|
33443
|
-
*
|
|
33444
|
-
* **Example** (Batching logger output)
|
|
33445
|
-
*
|
|
33446
|
-
* ```ts import.meta.vitest
|
|
33447
|
-
* import { Effect, Logger } from "effect"
|
|
33448
|
-
*
|
|
33449
|
-
* const flushed: Array<ReadonlyArray<string>> = []
|
|
33450
|
-
* const messageLogger = Logger.make((options) => String(options.message))
|
|
33451
|
-
* const batchedLogger = Logger.batched(messageLogger, {
|
|
33452
|
-
* window: "1 hour",
|
|
33453
|
-
* flush: (messages) =>
|
|
33454
|
-
* Effect.sync(() => {
|
|
33455
|
-
* flushed.push(messages)
|
|
33456
|
-
* })
|
|
33457
|
-
* })
|
|
33458
|
-
*
|
|
33459
|
-
* const program = Effect.scoped(Effect.gen(function*() {
|
|
33460
|
-
* const logger = yield* batchedLogger
|
|
33461
|
-
* yield* Effect.log("Event 1").pipe(Effect.provide(Logger.layer([logger])))
|
|
33462
|
-
* yield* Effect.log("Event 2").pipe(Effect.provide(Logger.layer([logger])))
|
|
33463
|
-
* }))
|
|
33464
|
-
* await Effect.runPromise(program)
|
|
33465
|
-
* flushed // => [["Event 1", "Event 2"]]
|
|
33466
|
-
* ```
|
|
33467
|
-
*
|
|
33468
|
-
* @stability stable
|
|
33469
|
-
* @category constructors
|
|
33470
|
-
* @since 2.0.0
|
|
33471
|
-
*/
|
|
33472
|
-
const batched = /*#__PURE__*/ dual(2, (self, options) => suspend$3(() => {
|
|
33473
|
-
let buffer = [];
|
|
33474
|
-
const flush = uninterruptible$1(suspend$3(() => {
|
|
33475
|
-
if (buffer.length === 0) return void_$2;
|
|
33476
|
-
const arr = buffer;
|
|
33477
|
-
buffer = [];
|
|
33478
|
-
return options.flush(arr);
|
|
33479
|
-
}));
|
|
33480
|
-
return uninterruptibleMask$1((restore) => restore(sleep$1(options.window).pipe(andThen$1(flush), forever$2)).pipe(forkDetach, flatMap$2((fiber) => addFinalizer$2(() => andThen$1(fiberInterrupt(fiber), flush))), as$1(loggerMake((options) => {
|
|
33481
|
-
buffer.push(self.log(options));
|
|
33482
|
-
}))));
|
|
33483
|
-
}));
|
|
33484
|
-
/**
|
|
33485
|
-
* Creates a `Layer` which will overwrite the current set of loggers with the
|
|
33486
|
-
* specified array of `loggers`.
|
|
33487
|
-
*
|
|
33488
|
-
* **Details**
|
|
33489
|
-
*
|
|
33490
|
-
* If the specified array of `loggers` should be _merged_ with the current set
|
|
33491
|
-
* of loggers (instead of overwriting them), set `mergeWithExisting` to `true`.
|
|
33492
|
-
*
|
|
33493
|
-
* **Example** (Providing logger layers)
|
|
33494
|
-
*
|
|
33495
|
-
* ```ts import.meta.vitest
|
|
33496
|
-
* import { Effect, Logger } from "effect"
|
|
33497
|
-
*
|
|
33498
|
-
* const messages: Array<unknown> = []
|
|
33499
|
-
* const customLogger = Logger.make((options) => {
|
|
33500
|
-
* messages.push(options.message)
|
|
33501
|
-
* })
|
|
33502
|
-
* const CustomLoggerLayer = Logger.layer([customLogger])
|
|
33503
|
-
*
|
|
33504
|
-
* const program = Effect.log("Application started").pipe(
|
|
33505
|
-
* Effect.provide(CustomLoggerLayer)
|
|
33506
|
-
* )
|
|
33507
|
-
* Effect.runSync(program)
|
|
33508
|
-
* messages // => [["Application started"]]
|
|
33509
|
-
* ```
|
|
33510
|
-
*
|
|
33511
|
-
* @stability stable
|
|
33512
|
-
* @category layers
|
|
33513
|
-
* @since 4.0.0
|
|
33514
|
-
*/
|
|
33515
|
-
const layer$1 = (loggers, options) => effect(CurrentLoggers, withFiber$1(fnUntraced$1(function* (fiber) {
|
|
33516
|
-
const currentLoggers = new Set(options?.mergeWithExisting === true ? fiber.getRef(CurrentLoggers$1) : []);
|
|
33517
|
-
for (const logger of loggers) currentLoggers.add(isEffect$1(logger) ? yield* logger : logger);
|
|
33518
|
-
return currentLoggers;
|
|
33519
|
-
})));
|
|
33520
|
-
/**
|
|
33521
|
-
* Creates a scoped logger that writes string logger output to a file.
|
|
33522
|
-
*
|
|
33523
|
-
* **Details**
|
|
33524
|
-
*
|
|
33525
|
-
* The returned effect requires `FileSystem` and `Scope`. The file logger batches
|
|
33526
|
-
* string output, writes each batch to the specified path, and flushes remaining
|
|
33527
|
-
* entries when the scope closes.
|
|
33528
|
-
*
|
|
33529
|
-
* **Example** (Writing JSON logs to a file)
|
|
33530
|
-
*
|
|
33531
|
-
* ```ts import.meta.vitest
|
|
33532
|
-
* import { Effect, FileSystem, Logger } from "effect"
|
|
33533
|
-
*
|
|
33534
|
-
* const writes: Array<string> = []
|
|
33535
|
-
* const file = {
|
|
33536
|
-
* writeAll: (buffer: Uint8Array) => Effect.sync(() => {
|
|
33537
|
-
* writes.push(new TextDecoder().decode(buffer).trim())
|
|
33538
|
-
* })
|
|
33539
|
-
* } as unknown as FileSystem.File
|
|
33540
|
-
* const fileSystem = FileSystem.makeNoop({ open: () => Effect.succeed(file) })
|
|
33541
|
-
* const messageLogger = Logger.make((options) => String(options.message))
|
|
33542
|
-
*
|
|
33543
|
-
* const program = Effect.scoped(Effect.gen(function*() {
|
|
33544
|
-
* const fileLogger = yield* Logger.toFile(messageLogger, "/tmp/log.txt")
|
|
33545
|
-
* yield* Effect.log("a").pipe(Effect.provide(Logger.layer([fileLogger])))
|
|
33546
|
-
* yield* Effect.log("b").pipe(Effect.provide(Logger.layer([fileLogger])))
|
|
33547
|
-
* yield* Effect.log("c").pipe(Effect.provide(Logger.layer([fileLogger])))
|
|
33548
|
-
* })).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem))
|
|
33549
|
-
*
|
|
33550
|
-
* await Effect.runPromise(program)
|
|
33551
|
-
* writes // => ["a\nb\nc"]
|
|
33552
|
-
* ```
|
|
33553
|
-
*
|
|
33554
|
-
* **Example** (Writing logs to files)
|
|
33555
|
-
*
|
|
33556
|
-
* ```ts import.meta.vitest
|
|
33557
|
-
* import { Effect, FileSystem, Logger } from "effect"
|
|
33558
|
-
*
|
|
33559
|
-
* const writes: Array<string> = []
|
|
33560
|
-
* const file = {
|
|
33561
|
-
* writeAll: (buffer: Uint8Array) => Effect.sync(() => {
|
|
33562
|
-
* writes.push(new TextDecoder().decode(buffer).trim())
|
|
33563
|
-
* })
|
|
33564
|
-
* } as unknown as FileSystem.File
|
|
33565
|
-
* const fileSystem = FileSystem.makeNoop({ open: () => Effect.succeed(file) })
|
|
33566
|
-
* const messageLogger = Logger.make((options) => String(options.message))
|
|
33567
|
-
*
|
|
33568
|
-
* const program = Effect.scoped(Effect.gen(function*() {
|
|
33569
|
-
* const fileLogger = yield* Logger.toFile(messageLogger, "/tmp/app.log", {
|
|
33570
|
-
* batchWindow: "1 hour"
|
|
33571
|
-
* })
|
|
33572
|
-
* yield* Effect.log("Application started").pipe(
|
|
33573
|
-
* Effect.provide(Logger.layer([fileLogger]))
|
|
33574
|
-
* )
|
|
33575
|
-
* })).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem))
|
|
33576
|
-
*
|
|
33577
|
-
* await Effect.runPromise(program)
|
|
33578
|
-
* writes // => ["Application started"]
|
|
33579
|
-
* ```
|
|
33580
|
-
*
|
|
33581
|
-
* @stability unstable
|
|
33582
|
-
* @category logging
|
|
33583
|
-
* @since 4.0.0
|
|
33584
|
-
*/
|
|
33585
|
-
const toFile = /*#__PURE__*/ dual((args) => isLogger(args[0]), (self, path, options) => gen$1(function* () {
|
|
33586
|
-
const logFile = yield* (yield* FileSystem).open(path, {
|
|
33587
|
-
flag: "a+",
|
|
33588
|
-
...options
|
|
33589
|
-
});
|
|
33590
|
-
const encoder = new TextEncoder();
|
|
33591
|
-
return yield* batched(self, {
|
|
33592
|
-
window: options?.batchWindow ?? 1e3,
|
|
33593
|
-
flush: (output) => ignore$1(logFile.writeAll(encoder.encode(output.join("\n") + "\n")))
|
|
33594
|
-
});
|
|
33595
|
-
}));
|
|
33767
|
+
var version = "0.12.0";
|
|
33596
33768
|
//#endregion
|
|
33597
33769
|
//#region ../../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
33598
33770
|
/**
|
|
33599
33771
|
* Creates a JSON scanner on the given text.
|
|
33600
33772
|
* If ignoreTrivia is set, whitespaces or comments are ignored.
|
|
33601
33773
|
*/
|
|
33602
|
-
function createScanner(text, ignoreTrivia = false) {
|
|
33774
|
+
function createScanner$1(text, ignoreTrivia = false) {
|
|
33603
33775
|
const len = text.length;
|
|
33604
33776
|
let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
|
|
33605
33777
|
function scanHexDigits(count, exact) {
|
|
@@ -34010,7 +34182,7 @@ const supportedEols = [
|
|
|
34010
34182
|
];
|
|
34011
34183
|
//#endregion
|
|
34012
34184
|
//#region ../../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/format.js
|
|
34013
|
-
function format(documentText, range, options) {
|
|
34185
|
+
function format$1(documentText, range, options) {
|
|
34014
34186
|
let initialIndentLevel;
|
|
34015
34187
|
let formatText;
|
|
34016
34188
|
let formatTextStart;
|
|
@@ -34040,7 +34212,7 @@ function format(documentText, range, options) {
|
|
|
34040
34212
|
if (options.insertSpaces) indentValue = cachedSpaces[options.tabSize || 4] ?? repeat(cachedSpaces[1], options.tabSize || 4);
|
|
34041
34213
|
else indentValue = " ";
|
|
34042
34214
|
const indentType = indentValue === " " ? " " : " ";
|
|
34043
|
-
let scanner = createScanner(formatText, false);
|
|
34215
|
+
let scanner = createScanner$1(formatText, false);
|
|
34044
34216
|
let hasError = false;
|
|
34045
34217
|
function newLinesAndIndent() {
|
|
34046
34218
|
if (numberLineBreaks > 1) return repeat(eol, numberLineBreaks) + repeat(indentValue, initialIndentLevel + indentLevel);
|
|
@@ -34187,7 +34359,7 @@ var ParseOptions;
|
|
|
34187
34359
|
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
34188
34360
|
* Therefore always check the errors list to find out if the input was valid.
|
|
34189
34361
|
*/
|
|
34190
|
-
function parse$
|
|
34362
|
+
function parse$3(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
34191
34363
|
let currentProperty = null;
|
|
34192
34364
|
let currentParent = [];
|
|
34193
34365
|
const previousParents = [];
|
|
@@ -34347,11 +34519,27 @@ function findNodeAtLocation$1(root, path) {
|
|
|
34347
34519
|
}
|
|
34348
34520
|
return node;
|
|
34349
34521
|
}
|
|
34522
|
+
function contains(node, offset, includeRightBound = false) {
|
|
34523
|
+
return offset >= node.offset && offset < node.offset + node.length || includeRightBound && offset === node.offset + node.length;
|
|
34524
|
+
}
|
|
34525
|
+
/**
|
|
34526
|
+
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
|
|
34527
|
+
*/
|
|
34528
|
+
function findNodeAtOffset$1(node, offset, includeRightBound = false) {
|
|
34529
|
+
if (contains(node, offset, includeRightBound)) {
|
|
34530
|
+
const children = node.children;
|
|
34531
|
+
if (Array.isArray(children)) for (let i = 0; i < children.length && children[i].offset <= offset; i++) {
|
|
34532
|
+
const item = findNodeAtOffset$1(children[i], offset, includeRightBound);
|
|
34533
|
+
if (item) return item;
|
|
34534
|
+
}
|
|
34535
|
+
return node;
|
|
34536
|
+
}
|
|
34537
|
+
}
|
|
34350
34538
|
/**
|
|
34351
34539
|
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
|
|
34352
34540
|
*/
|
|
34353
34541
|
function visit(text, visitor, options = ParseOptions.DEFAULT) {
|
|
34354
|
-
const _scanner = createScanner(text, false);
|
|
34542
|
+
const _scanner = createScanner$1(text, false);
|
|
34355
34543
|
const _jsonPath = [];
|
|
34356
34544
|
let suppressedCallbacks = 0;
|
|
34357
34545
|
function toNoArgVisit(visitFunction) {
|
|
@@ -34703,7 +34891,7 @@ function withFormatting(text, edit, options) {
|
|
|
34703
34891
|
while (begin > 0 && !isEOL(newText, begin - 1)) begin--;
|
|
34704
34892
|
while (end < newText.length && !isEOL(newText, end)) end++;
|
|
34705
34893
|
}
|
|
34706
|
-
const edits = format(newText, {
|
|
34894
|
+
const edits = format$1(newText, {
|
|
34707
34895
|
offset: begin,
|
|
34708
34896
|
length: end - begin
|
|
34709
34897
|
}, {
|
|
@@ -34729,6 +34917,11 @@ function applyEdit(text, edit) {
|
|
|
34729
34917
|
}
|
|
34730
34918
|
//#endregion
|
|
34731
34919
|
//#region ../../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js
|
|
34920
|
+
/**
|
|
34921
|
+
* Creates a JSON scanner on the given text.
|
|
34922
|
+
* If ignoreTrivia is set, whitespaces or comments are ignored.
|
|
34923
|
+
*/
|
|
34924
|
+
const createScanner = createScanner$1;
|
|
34732
34925
|
var ScanError;
|
|
34733
34926
|
(function(ScanError) {
|
|
34734
34927
|
ScanError[ScanError["None"] = 0] = "None";
|
|
@@ -34763,7 +34956,7 @@ var SyntaxKind;
|
|
|
34763
34956
|
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
34764
34957
|
* Therefore, always check the errors list to find out if the input was valid.
|
|
34765
34958
|
*/
|
|
34766
|
-
const parse$
|
|
34959
|
+
const parse$2 = parse$3;
|
|
34767
34960
|
/**
|
|
34768
34961
|
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
|
|
34769
34962
|
*/
|
|
@@ -34772,6 +34965,10 @@ const parseTree = parseTree$1;
|
|
|
34772
34965
|
* Finds the node at the given path in a JSON DOM.
|
|
34773
34966
|
*/
|
|
34774
34967
|
const findNodeAtLocation = findNodeAtLocation$1;
|
|
34968
|
+
/**
|
|
34969
|
+
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
|
|
34970
|
+
*/
|
|
34971
|
+
const findNodeAtOffset = findNodeAtOffset$1;
|
|
34775
34972
|
var ParseErrorCode;
|
|
34776
34973
|
(function(ParseErrorCode) {
|
|
34777
34974
|
ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 1] = "InvalidSymbol";
|
|
@@ -36257,6 +36454,10 @@ var BumpError = class extends TaggedError()("BumpError", {
|
|
|
36257
36454
|
}
|
|
36258
36455
|
};
|
|
36259
36456
|
const bumpedRange = (current, installed) => import_semver.valid(current) !== null ? installed : `^${installed}`;
|
|
36457
|
+
const addedRange = (installed, published) => {
|
|
36458
|
+
const version = installed ?? import_semver.maxSatisfying([...published], "*") ?? void 0;
|
|
36459
|
+
return version === void 0 ? void 0 : `^${version}`;
|
|
36460
|
+
};
|
|
36260
36461
|
const bumpText = (text, path, installed) => {
|
|
36261
36462
|
const tree = parseTree(text) ?? {
|
|
36262
36463
|
type: "null",
|
|
@@ -36298,13 +36499,14 @@ const Origin = String$6.check(isPattern(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/, {
|
|
|
36298
36499
|
}));
|
|
36299
36500
|
const Ref = NonEmptyString;
|
|
36300
36501
|
const isPin = (ref) => /^[0-9a-f]{40}$/.test(ref);
|
|
36301
|
-
const
|
|
36502
|
+
const PROVIDER_NAMES = [
|
|
36302
36503
|
"files",
|
|
36303
36504
|
"vp",
|
|
36304
36505
|
"brew",
|
|
36305
36506
|
"cask",
|
|
36306
36507
|
"repos"
|
|
36307
|
-
]
|
|
36508
|
+
];
|
|
36509
|
+
const ProviderName = Literals$2(PROVIDER_NAMES);
|
|
36308
36510
|
const Range = NonEmptyString.check(makeFilter((range) => range === "latest" || import_semver.validRange(range) !== null, {
|
|
36309
36511
|
title: "Range",
|
|
36310
36512
|
description: "a semver range (leave inside it, install below it, ask above it), \"*\" (install when missing, never upgrade), or \"latest\" (upgrade to the newest on every Ship)"
|
|
@@ -36347,6 +36549,7 @@ const NOT_GLOBALS = {
|
|
|
36347
36549
|
"vite-plus": "set vp with vp.version",
|
|
36348
36550
|
fleetfizz: "a Ship installs the fleetfizz that ships"
|
|
36349
36551
|
};
|
|
36552
|
+
const isGlobalName = (name) => !Object.hasOwn(NOT_GLOBALS, name);
|
|
36350
36553
|
const VpSection = section({
|
|
36351
36554
|
version: optionalKey(Range).annotateKey({ description: "The vp version. Pin it exactly." }),
|
|
36352
36555
|
node: optionalKey(Range).annotateKey({ description: "The default Node version. Pin it exactly." }),
|
|
@@ -36355,7 +36558,7 @@ const VpSection = section({
|
|
|
36355
36558
|
yarn: optionalKey(Range).annotateKey({ description: "The default Yarn version." }),
|
|
36356
36559
|
bun: optionalKey(Range).annotateKey({ description: "The default Bun version." })
|
|
36357
36560
|
}, Record(NonEmptyString, variants(Global)).check(makeFilter((globals) => {
|
|
36358
|
-
const name = Object.keys(globals).find((n) => n
|
|
36561
|
+
const name = Object.keys(globals).find((n) => !isGlobalName(n));
|
|
36359
36562
|
return name === void 0 || `"${name}" is not a Global: ${NOT_GLOBALS[name]}`;
|
|
36360
36563
|
}, {
|
|
36361
36564
|
title: "Globals",
|
|
@@ -36425,17 +36628,6 @@ const FleetConfig = Struct({
|
|
|
36425
36628
|
});
|
|
36426
36629
|
const SELF_PACKAGE = "fleetfizz";
|
|
36427
36630
|
//#endregion
|
|
36428
|
-
//#region ../../packages/config/src/jsonSchema.ts
|
|
36429
|
-
const fleetConfigJsonSchema = () => {
|
|
36430
|
-
const document = toJsonSchemaDocument(FleetConfig, { onExcessProperty: "error" });
|
|
36431
|
-
return {
|
|
36432
|
-
$schema: META_SCHEMA_URI_DRAFT_2020_12,
|
|
36433
|
-
title: "fleetfizz config",
|
|
36434
|
-
...document.schema,
|
|
36435
|
-
$defs: document.definitions
|
|
36436
|
-
};
|
|
36437
|
-
};
|
|
36438
|
-
//#endregion
|
|
36439
36631
|
//#region ../../packages/config/src/load.ts
|
|
36440
36632
|
const CONFIG_FILE = "config.jsonc";
|
|
36441
36633
|
const SCHEMA_FILE = "schema.json";
|
|
@@ -36451,7 +36643,7 @@ var ConfigError = class extends TaggedError()("ConfigError", {
|
|
|
36451
36643
|
};
|
|
36452
36644
|
var ConfigNotFound = class extends TaggedError()("ConfigNotFound", { path: String$6 }) {
|
|
36453
36645
|
get message() {
|
|
36454
|
-
return `no config at ${this.path};
|
|
36646
|
+
return `no config at ${this.path}; run fizz init to write one, or pass --config`;
|
|
36455
36647
|
}
|
|
36456
36648
|
};
|
|
36457
36649
|
const lineOf = (text, offset) => text.slice(0, offset).split("\n").length;
|
|
@@ -36491,7 +36683,7 @@ const movedKeys = (parsed) => {
|
|
|
36491
36683
|
};
|
|
36492
36684
|
const parseConfig = fn("parseConfig")(function* (path, text) {
|
|
36493
36685
|
const parseErrors = [];
|
|
36494
|
-
const parsed = parse$
|
|
36686
|
+
const parsed = parse$2(text, parseErrors, { allowTrailingComma: true });
|
|
36495
36687
|
if (parseErrors.length > 0) return yield* new ConfigError({
|
|
36496
36688
|
path,
|
|
36497
36689
|
problems: parseErrors.map((e) => `line ${lineOf(text, e.offset)}: ${printParseErrorCode(e.error)}`)
|
|
@@ -36518,6 +36710,239 @@ const loadConfig = fn("loadConfig")(function* (path) {
|
|
|
36518
36710
|
const text = yield* fs.readFileString(path);
|
|
36519
36711
|
return yield* parseConfig(path, text);
|
|
36520
36712
|
});
|
|
36713
|
+
//#endregion
|
|
36714
|
+
//#region ../../packages/config/src/edit.ts
|
|
36715
|
+
const WIDTH = 100;
|
|
36716
|
+
const parse$1 = (text) => parseTree(text, void 0, { allowTrailingComma: true }) ?? {
|
|
36717
|
+
type: "object",
|
|
36718
|
+
offset: 0,
|
|
36719
|
+
length: 0,
|
|
36720
|
+
children: []
|
|
36721
|
+
};
|
|
36722
|
+
const lineStart = (text, offset) => text.lastIndexOf("\n", offset - 1) + 1;
|
|
36723
|
+
const indentAt = (text, offset) => /^[ \t]*/.exec(text.slice(lineStart(text, offset)))?.[0] ?? "";
|
|
36724
|
+
const format = (value, indent, column) => {
|
|
36725
|
+
const inline = formatInline(value);
|
|
36726
|
+
if (column + inline.length <= WIDTH || typeof value !== "object" || value === null) return inline;
|
|
36727
|
+
const inner = `${indent} `;
|
|
36728
|
+
if (Array.isArray(value)) return `[\n${value.map((v) => `${inner}${format(v, inner, inner.length)},\n`).join("")}${indent}]`;
|
|
36729
|
+
return `{\n${Object.entries(value).map(([k, v]) => {
|
|
36730
|
+
const key = `${JSON.stringify(k)}: `;
|
|
36731
|
+
return `${inner}${key}${format(v, inner, inner.length + key.length)},\n`;
|
|
36732
|
+
}).join("")}${indent}}`;
|
|
36733
|
+
};
|
|
36734
|
+
const formatInline = (value) => {
|
|
36735
|
+
if (Array.isArray(value)) return `[${value.map(formatInline).join(", ")}]`;
|
|
36736
|
+
if (typeof value === "object" && value !== null) {
|
|
36737
|
+
const entries = Object.entries(value).map(([k, v]) => `${JSON.stringify(k)}: ${formatInline(v)}`);
|
|
36738
|
+
return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }`;
|
|
36739
|
+
}
|
|
36740
|
+
return JSON.stringify(value);
|
|
36741
|
+
};
|
|
36742
|
+
const BLOCK_COMMENTS = /^(?:[ \t]*\/\*(?:(?!\*\/)[^\n])*\*\/)*/;
|
|
36743
|
+
const tail = (text, end) => {
|
|
36744
|
+
const close = end + (BLOCK_COMMENTS.exec(text.slice(end))?.[0].length ?? 0);
|
|
36745
|
+
let at = close;
|
|
36746
|
+
while (text[at] === " " || text[at] === " ") at++;
|
|
36747
|
+
const comma = text[at] === ",";
|
|
36748
|
+
if (comma) at++;
|
|
36749
|
+
const blocks = BLOCK_COMMENTS.exec(text.slice(at))?.[0].length ?? 0;
|
|
36750
|
+
const rest = /^[ \t]*(\/\/[^\n]*)?(?=\n|$)/.exec(text.slice(at + blocks));
|
|
36751
|
+
return {
|
|
36752
|
+
comma,
|
|
36753
|
+
close,
|
|
36754
|
+
end: comma ? at : end,
|
|
36755
|
+
lineEnd: rest === null ? void 0 : at + blocks + rest[0].length
|
|
36756
|
+
};
|
|
36757
|
+
};
|
|
36758
|
+
const memberOf = (node) => node.parent?.type === "property" ? node.parent : node;
|
|
36759
|
+
const insertMember = (text, container, render) => {
|
|
36760
|
+
const open = container.offset;
|
|
36761
|
+
const close = container.offset + container.length - 1;
|
|
36762
|
+
const last = (container.children ?? []).at(-1);
|
|
36763
|
+
const inside = text.slice(open + 1, close);
|
|
36764
|
+
if (last === void 0 && inside.trim() !== "") {
|
|
36765
|
+
const indent = indentAt(text, open);
|
|
36766
|
+
if (!inside.includes("\n")) return `${text.slice(0, close).trimEnd()} ${render(indent, close - lineStart(text, close) + 1)} ${text.slice(close)}`;
|
|
36767
|
+
const inner = `${indent} `;
|
|
36768
|
+
const at = lineStart(text, close);
|
|
36769
|
+
if (text.slice(at, close).trim() !== "") return `${text.slice(0, close).trimEnd()}\n${inner}${render(inner, inner.length)},\n${indent}${text.slice(close)}`;
|
|
36770
|
+
return `${text.slice(0, at)}${inner}${render(inner, inner.length)},\n${text.slice(at)}`;
|
|
36771
|
+
}
|
|
36772
|
+
if (last === void 0) {
|
|
36773
|
+
const member = render(indentAt(text, open), open - lineStart(text, open) + 2);
|
|
36774
|
+
const [l, r] = container.type === "array" ? ["[", "]"] : ["{ ", " }"];
|
|
36775
|
+
const body = `${l}${member}${r}`;
|
|
36776
|
+
return text.slice(0, open) + body + text.slice(close + 1);
|
|
36777
|
+
}
|
|
36778
|
+
const multiline = text.slice(open, close).includes("\n");
|
|
36779
|
+
const after = tail(text, last.offset + last.length);
|
|
36780
|
+
if (!multiline || after.lineEnd === void 0) {
|
|
36781
|
+
const at = after.close;
|
|
36782
|
+
return `${text.slice(0, at)}, ${render("", at - lineStart(text, at) + 2)}${text.slice(at)}`;
|
|
36783
|
+
}
|
|
36784
|
+
const indent = indentAt(text, last.offset);
|
|
36785
|
+
const member = `\n${indent}${render(indent, indent.length)}${after.comma ? "," : ""}`;
|
|
36786
|
+
const comma = after.comma ? "" : ",";
|
|
36787
|
+
const at = last.offset + last.length;
|
|
36788
|
+
return text.slice(0, at) + comma + text.slice(at, after.lineEnd) + member + text.slice(after.lineEnd);
|
|
36789
|
+
};
|
|
36790
|
+
const spread = (text, container) => {
|
|
36791
|
+
const open = container.offset;
|
|
36792
|
+
const close = container.offset + container.length - 1;
|
|
36793
|
+
const indent = indentAt(text, open);
|
|
36794
|
+
const [l, r] = container.type === "array" ? ["[", "]"] : ["{", "}"];
|
|
36795
|
+
const body = (container.children ?? []).map((m) => `${indent} ${text.slice(m.offset, m.offset + m.length)},\n`).join("");
|
|
36796
|
+
return `${text.slice(0, open)}${l}\n${body}${indent}${r}${text.slice(close + 1)}`;
|
|
36797
|
+
};
|
|
36798
|
+
const oneLineAround = (text, tree, offset) => {
|
|
36799
|
+
const start = lineStart(text, offset);
|
|
36800
|
+
let found;
|
|
36801
|
+
for (let node = findNodeAtOffset(tree, offset, true); node !== void 0; node = node.parent) if ((node.type === "object" || node.type === "array") && node.offset >= start && !text.slice(node.offset, node.offset + node.length).includes("\n")) found = node;
|
|
36802
|
+
return found;
|
|
36803
|
+
};
|
|
36804
|
+
const lineLength = (text, offset) => {
|
|
36805
|
+
const end = text.indexOf("\n", offset);
|
|
36806
|
+
return (end < 0 ? text.length : end) - lineStart(text, offset);
|
|
36807
|
+
};
|
|
36808
|
+
const valueOf = (text) => parse$2(text, void 0, { allowTrailingComma: true });
|
|
36809
|
+
const commentsOf = (text) => {
|
|
36810
|
+
const scanner = createScanner(text, false);
|
|
36811
|
+
const found = [];
|
|
36812
|
+
for (let kind = scanner.scan(); kind !== SyntaxKind.EOF; kind = scanner.scan()) if (kind === SyntaxKind.LineCommentTrivia || kind === SyntaxKind.BlockCommentTrivia) found.push(scanner.getTokenValue());
|
|
36813
|
+
return found;
|
|
36814
|
+
};
|
|
36815
|
+
const sameComments = (a, b) => commentsOf(a).join("\0") === commentsOf(b).join("\0");
|
|
36816
|
+
const changedAt = (root, path, change) => {
|
|
36817
|
+
const copy = structuredClone(root);
|
|
36818
|
+
let parent = copy;
|
|
36819
|
+
for (const key of path.slice(0, -1)) {
|
|
36820
|
+
parent[key] ??= {};
|
|
36821
|
+
parent = parent[key];
|
|
36822
|
+
}
|
|
36823
|
+
const key = path.at(-1);
|
|
36824
|
+
if (key !== void 0) change(parent, key);
|
|
36825
|
+
return copy;
|
|
36826
|
+
};
|
|
36827
|
+
const checked = (text, result, expected, comments) => "reason" in result || JSON.stringify(valueOf(result.text)) === JSON.stringify(expected) && (!comments || sameComments(text, result.text)) ? result : { reason: "this part of the config is written in a way fizz cannot edit; use fizz config edit" };
|
|
36828
|
+
const insertAt = (text, path, value) => checked(text, insertSpread(text, path, value), changedAt(valueOf(text), path, (parent, key) => {
|
|
36829
|
+
if (Array.isArray(parent)) parent.push(value);
|
|
36830
|
+
else parent[key] = value;
|
|
36831
|
+
}), true);
|
|
36832
|
+
const insertSpread = (text, path, value) => {
|
|
36833
|
+
const result = insertText(text, path, value);
|
|
36834
|
+
if (!("text" in result)) return result;
|
|
36835
|
+
const at = firstDifference(text, result.text);
|
|
36836
|
+
if (lineLength(result.text, at) <= WIDTH) return result;
|
|
36837
|
+
const container = oneLineAround(text, parse$1(text), Math.min(at, text.length - 1));
|
|
36838
|
+
if (container === void 0) return result;
|
|
36839
|
+
const spreadText = spread(text, container);
|
|
36840
|
+
return sameComments(text, spreadText) ? insertSpread(spreadText, path, value) : result;
|
|
36841
|
+
};
|
|
36842
|
+
const firstDifference = (a, b) => {
|
|
36843
|
+
let i = 0;
|
|
36844
|
+
while (i < a.length && a[i] === b[i]) i++;
|
|
36845
|
+
return i;
|
|
36846
|
+
};
|
|
36847
|
+
const insertText = (text, path, value) => {
|
|
36848
|
+
const tree = parse$1(text);
|
|
36849
|
+
let depth = 0;
|
|
36850
|
+
let node = tree;
|
|
36851
|
+
while (depth < path.length) {
|
|
36852
|
+
const next = findNodeAtLocation(tree, path.slice(0, depth + 1));
|
|
36853
|
+
if (next === void 0) break;
|
|
36854
|
+
node = next;
|
|
36855
|
+
depth++;
|
|
36856
|
+
}
|
|
36857
|
+
const key = path[depth];
|
|
36858
|
+
if (key === void 0) return { reason: `${path.join(".")} is already in the config` };
|
|
36859
|
+
const nested = path.slice(depth + 1).reduceRight((inner, k) => ({ [String(k)]: inner }), value);
|
|
36860
|
+
if (typeof key === "number") {
|
|
36861
|
+
if (node.type !== "array") return { reason: `${path.slice(0, depth).join(".")} is not a list` };
|
|
36862
|
+
return { text: insertMember(text, node, (indent, column) => format(nested, indent, column)) };
|
|
36863
|
+
}
|
|
36864
|
+
if (node.type !== "object") return { reason: `${path.slice(0, depth).join(".")} is not an object` };
|
|
36865
|
+
const name = `${JSON.stringify(key)}: `;
|
|
36866
|
+
return { text: insertMember(text, node, (indent, column) => `${name}${format(nested, indent, column + name.length)}`) };
|
|
36867
|
+
};
|
|
36868
|
+
const removeAt = (text, path) => checked(text, removeText(text, path), changedAt(valueOf(text), path, (parent, key) => {
|
|
36869
|
+
if (Array.isArray(parent)) parent.splice(Number(key), 1);
|
|
36870
|
+
else delete parent[key];
|
|
36871
|
+
}), false);
|
|
36872
|
+
const removeText = (text, path) => {
|
|
36873
|
+
const found = findNodeAtLocation(parse$1(text), [...path]);
|
|
36874
|
+
if (found === void 0) return { reason: `${path.join(".")} is not in the config` };
|
|
36875
|
+
const member = memberOf(found);
|
|
36876
|
+
const siblings = member.parent?.children ?? [];
|
|
36877
|
+
const index = siblings.indexOf(member);
|
|
36878
|
+
const start = member.offset;
|
|
36879
|
+
const after = tail(text, start + member.length);
|
|
36880
|
+
if (text.slice(lineStart(text, start), start).trim() === "" && after.lineEnd !== void 0) {
|
|
36881
|
+
const from = lineStart(text, start);
|
|
36882
|
+
const to = text[after.lineEnd] === "\n" ? after.lineEnd + 1 : after.lineEnd;
|
|
36883
|
+
return { text: text.slice(0, from) + text.slice(to) };
|
|
36884
|
+
}
|
|
36885
|
+
if (after.comma && index < siblings.length - 1) {
|
|
36886
|
+
let to = after.end;
|
|
36887
|
+
while (text[to] === " ") to++;
|
|
36888
|
+
return { text: text.slice(0, start) + text.slice(to) };
|
|
36889
|
+
}
|
|
36890
|
+
const previous = siblings[index - 1];
|
|
36891
|
+
if (previous === void 0) {
|
|
36892
|
+
const parent = member.parent;
|
|
36893
|
+
if (parent === void 0) return { text };
|
|
36894
|
+
const kept = `${text.slice(parent.offset + 1, start)}${text.slice(after.end, parent.offset + parent.length - 1)}`;
|
|
36895
|
+
const empty = parent.type === "array" ? "[]" : "{}";
|
|
36896
|
+
return { text: kept.trim() === "" ? text.slice(0, parent.offset) + empty + text.slice(parent.offset + parent.length) : text.slice(0, start) + text.slice(after.end) };
|
|
36897
|
+
}
|
|
36898
|
+
const before = tail(text, previous.offset + previous.length);
|
|
36899
|
+
const comma = before.comma ? before.end - 1 : previous.offset + previous.length;
|
|
36900
|
+
return { text: text.slice(0, comma) + text.slice(before.comma ? before.end : comma, start).trimEnd() + text.slice(after.end) };
|
|
36901
|
+
};
|
|
36902
|
+
const targetingLists = (text, machine) => {
|
|
36903
|
+
const found = [];
|
|
36904
|
+
const visit = (node, path) => {
|
|
36905
|
+
if (node.type === "array") node.children?.forEach((child, i) => visit(child, [...path, i]));
|
|
36906
|
+
if (node.type !== "object") return;
|
|
36907
|
+
for (const [key, value] of (node.children ?? []).map((p) => p.children ?? [])) {
|
|
36908
|
+
if (key === void 0 || value === void 0) continue;
|
|
36909
|
+
const at = [...path, String(key.value)];
|
|
36910
|
+
if (key.value === "machines" && path.length > 0 && value.type === "array") {
|
|
36911
|
+
const index = (value.children ?? []).findIndex((c) => c.value === machine);
|
|
36912
|
+
if (index >= 0) found.push({
|
|
36913
|
+
path: at,
|
|
36914
|
+
size: value.children?.length ?? 0,
|
|
36915
|
+
index
|
|
36916
|
+
});
|
|
36917
|
+
} else visit(value, at);
|
|
36918
|
+
}
|
|
36919
|
+
};
|
|
36920
|
+
visit(parse$1(text), []);
|
|
36921
|
+
return found;
|
|
36922
|
+
};
|
|
36923
|
+
const saveConfig = fn("saveConfig")(function* (path, text) {
|
|
36924
|
+
const config = yield* parseConfig(path, text);
|
|
36925
|
+
const fs = yield* FileSystem;
|
|
36926
|
+
const target = (yield* fs.exists(path)) ? yield* fs.realPath(path) : path;
|
|
36927
|
+
yield* fs.makeDirectory((yield* Path$1).dirname(target), { recursive: true });
|
|
36928
|
+
yield* fs.writeFileString(`${target}.tmp`, text);
|
|
36929
|
+
if (yield* fs.exists(target)) yield* fs.chmod(`${target}.tmp`, (yield* fs.stat(target)).mode & 4095);
|
|
36930
|
+
yield* fs.rename(`${target}.tmp`, target);
|
|
36931
|
+
return config;
|
|
36932
|
+
});
|
|
36933
|
+
//#endregion
|
|
36934
|
+
//#region ../../packages/config/src/jsonSchema.ts
|
|
36935
|
+
const fleetConfigJsonSchema = () => {
|
|
36936
|
+
const document = toJsonSchemaDocument(FleetConfig, { onExcessProperty: "error" });
|
|
36937
|
+
return {
|
|
36938
|
+
$schema: META_SCHEMA_URI_DRAFT_2020_12,
|
|
36939
|
+
title: "fleetfizz config",
|
|
36940
|
+
...document.schema,
|
|
36941
|
+
$defs: document.definitions
|
|
36942
|
+
};
|
|
36943
|
+
};
|
|
36944
|
+
const encodeJson = encodeSync(fromJsonString(Unknown, { space: 2 }));
|
|
36945
|
+
const fleetConfigSchemaText = () => `${encodeJson(fleetConfigJsonSchema())}\n`;
|
|
36521
36946
|
const ConfigRecord = Struct({
|
|
36522
36947
|
ship: NonEmptyString,
|
|
36523
36948
|
sha: String$6,
|
|
@@ -36539,6 +36964,380 @@ const mergeReached = (all) => {
|
|
|
36539
36964
|
const isNewer = (remote, local) => remote !== void 0 && (local === void 0 || remote > local);
|
|
36540
36965
|
const staleWhy = (copy, base) => copy.record === void 0 ? "unrecorded" : copy.record.sha !== copy.sha ? "edited" : isNewer(copy.record.reached[copy.machine], base[copy.machine]) ? "newer" : void 0;
|
|
36541
36966
|
//#endregion
|
|
36967
|
+
//#region ../../packages/core/src/targeting.ts
|
|
36968
|
+
const targets = (targeting, machine) => (targeting.platforms?.includes(machine.platform) ?? true) && (targeting.machines?.includes(machine.name) ?? true);
|
|
36969
|
+
const isVariantList = (entry) => Array.isArray(entry);
|
|
36970
|
+
const variantFor = (entry, machine) => {
|
|
36971
|
+
if (!isVariantList(entry)) return targets(entry, machine) ? { variant: entry } : void 0;
|
|
36972
|
+
const index = entry.findIndex((variant) => targets(variant, machine));
|
|
36973
|
+
const variant = entry[index];
|
|
36974
|
+
return variant === void 0 ? void 0 : {
|
|
36975
|
+
variant,
|
|
36976
|
+
index
|
|
36977
|
+
};
|
|
36978
|
+
};
|
|
36979
|
+
//#endregion
|
|
36980
|
+
//#region ../../packages/core/src/engine.ts
|
|
36981
|
+
const dependenciesOf = (provider, config) => [.../* @__PURE__ */ new Set([...provider.dependsOn, ...config[provider.name]?.dependsOn ?? []])];
|
|
36982
|
+
const withoutTargeting = ({ platforms: _platforms, machines: _machines, ...def }) => def;
|
|
36983
|
+
const targetedItems = (provider, config, machine) => {
|
|
36984
|
+
const section = config[provider.name];
|
|
36985
|
+
if (section === void 0 || !targets(section, machine)) return provider.items(void 0, []);
|
|
36986
|
+
const listed = Object.entries(section.items ?? {}).flatMap(([name, entry]) => {
|
|
36987
|
+
const chosen = variantFor(entry, machine);
|
|
36988
|
+
if (chosen === void 0) return [];
|
|
36989
|
+
const at = chosen.index === void 0 ? [] : [chosen.index];
|
|
36990
|
+
return [{
|
|
36991
|
+
name,
|
|
36992
|
+
def: withoutTargeting(chosen.variant),
|
|
36993
|
+
path: [
|
|
36994
|
+
provider.name,
|
|
36995
|
+
"items",
|
|
36996
|
+
name,
|
|
36997
|
+
...at
|
|
36998
|
+
]
|
|
36999
|
+
}];
|
|
37000
|
+
});
|
|
37001
|
+
return provider.items(section, listed);
|
|
37002
|
+
};
|
|
37003
|
+
const machineNames = (value) => {
|
|
37004
|
+
if (Array.isArray(value)) return value.flatMap(machineNames);
|
|
37005
|
+
if (typeof value !== "object" || value === null) return [];
|
|
37006
|
+
return Object.entries(value).flatMap(([key, v]) => key === "machines" && Array.isArray(v) && v.every((name) => typeof name === "string") ? v : machineNames(v));
|
|
37007
|
+
};
|
|
37008
|
+
const machinesNamed = (provider, config) => machineNames(config[provider.name]);
|
|
37009
|
+
const hold = (decisions, by) => decisions.map((d) => d.kind === "change" || d.kind === "ask" ? {
|
|
37010
|
+
kind: "skip",
|
|
37011
|
+
item: d.item,
|
|
37012
|
+
...d.kind === "change" && d.from !== void 0 ? { at: d.from } : {},
|
|
37013
|
+
...d.kind === "ask" && d.at !== void 0 ? { at: d.at } : {},
|
|
37014
|
+
reason: `depends on ${by}`
|
|
37015
|
+
} : d);
|
|
37016
|
+
const planMachine = (input) => {
|
|
37017
|
+
const ctx = {
|
|
37018
|
+
...input.ctx,
|
|
37019
|
+
home: input.state.home ?? "~"
|
|
37020
|
+
};
|
|
37021
|
+
const plans = /* @__PURE__ */ new Map();
|
|
37022
|
+
const held = /* @__PURE__ */ new Set();
|
|
37023
|
+
for (const provider of input.providers) {
|
|
37024
|
+
const dependsOn = dependenciesOf(provider, ctx.config);
|
|
37025
|
+
const items = targetedItems(provider, ctx.config, ctx.machine);
|
|
37026
|
+
const decisions = provider.decide({
|
|
37027
|
+
items,
|
|
37028
|
+
observed: provider.parse(input.state.facts[provider.name] ?? [], input.state.records[provider.name] ?? {}),
|
|
37029
|
+
resolved: input.resolved[provider.name],
|
|
37030
|
+
ctx
|
|
37031
|
+
});
|
|
37032
|
+
const by = dependsOn.find((name) => {
|
|
37033
|
+
const dep = plans.get(name);
|
|
37034
|
+
return dep !== void 0 && dep.decisions.length > 0 && (held.has(name) || dep.decisions.some((d) => d.kind === "fail"));
|
|
37035
|
+
});
|
|
37036
|
+
if (by !== void 0) held.add(provider.name);
|
|
37037
|
+
plans.set(provider.name, {
|
|
37038
|
+
name: provider.name,
|
|
37039
|
+
rule: provider.rule,
|
|
37040
|
+
dependsOn,
|
|
37041
|
+
decisions: by === void 0 ? decisions : hold(decisions, by),
|
|
37042
|
+
steps: by === void 0 ? provider.act(decisions, ctx) : []
|
|
37043
|
+
});
|
|
37044
|
+
}
|
|
37045
|
+
return {
|
|
37046
|
+
machine: ctx.machine,
|
|
37047
|
+
providers: [...plans.values()]
|
|
37048
|
+
};
|
|
37049
|
+
};
|
|
37050
|
+
//#endregion
|
|
37051
|
+
//#region ../../packages/core/src/range.ts
|
|
37052
|
+
const newest = (available) => available.filter((v) => import_semver.prerelease(v) === null).sort(import_semver.rcompare)[0];
|
|
37053
|
+
const decideRange = (range, installed, available) => {
|
|
37054
|
+
if (range === "latest") {
|
|
37055
|
+
const version = newest(available);
|
|
37056
|
+
if (version === void 0) return { kind: "unavailable" };
|
|
37057
|
+
return installed === version ? { kind: "leave" } : {
|
|
37058
|
+
kind: "install",
|
|
37059
|
+
version
|
|
37060
|
+
};
|
|
37061
|
+
}
|
|
37062
|
+
if (range === "*") {
|
|
37063
|
+
if (installed !== void 0) return { kind: "leave" };
|
|
37064
|
+
const version = newest(available);
|
|
37065
|
+
return version === void 0 ? { kind: "unavailable" } : {
|
|
37066
|
+
kind: "install",
|
|
37067
|
+
version
|
|
37068
|
+
};
|
|
37069
|
+
}
|
|
37070
|
+
if (installed !== void 0 && import_semver.satisfies(installed, range)) return { kind: "leave" };
|
|
37071
|
+
if (installed !== void 0 && import_semver.gtr(installed, range)) return {
|
|
37072
|
+
kind: "ask",
|
|
37073
|
+
installed
|
|
37074
|
+
};
|
|
37075
|
+
const version = import_semver.maxSatisfying([...available], range);
|
|
37076
|
+
return version === null ? { kind: "unavailable" } : {
|
|
37077
|
+
kind: "install",
|
|
37078
|
+
version
|
|
37079
|
+
};
|
|
37080
|
+
};
|
|
37081
|
+
//#endregion
|
|
37082
|
+
//#region ../../packages/core/src/provider.ts
|
|
37083
|
+
const address = (provider, item) => `${provider}:${item}`;
|
|
37084
|
+
const parseAddress = (text) => {
|
|
37085
|
+
const at = text.indexOf(":");
|
|
37086
|
+
return at <= 0 || at === text.length - 1 ? void 0 : {
|
|
37087
|
+
provider: text.slice(0, at),
|
|
37088
|
+
item: text.slice(at + 1)
|
|
37089
|
+
};
|
|
37090
|
+
};
|
|
37091
|
+
const QUESTION_CHOICES = {
|
|
37092
|
+
range: [
|
|
37093
|
+
"bump",
|
|
37094
|
+
"skip",
|
|
37095
|
+
"downgrade"
|
|
37096
|
+
],
|
|
37097
|
+
config: ["overwrite", "stop"],
|
|
37098
|
+
mode: ["managed", "skip"],
|
|
37099
|
+
file: ["overwrite", "skip"]
|
|
37100
|
+
};
|
|
37101
|
+
const isQuestionKind = (kind) => kind in QUESTION_CHOICES;
|
|
37102
|
+
var Hidden = class {
|
|
37103
|
+
#value;
|
|
37104
|
+
constructor(value) {
|
|
37105
|
+
this.#value = value;
|
|
37106
|
+
}
|
|
37107
|
+
reveal() {
|
|
37108
|
+
return this.#value;
|
|
37109
|
+
}
|
|
37110
|
+
toJSON() {
|
|
37111
|
+
return "[hidden]";
|
|
37112
|
+
}
|
|
37113
|
+
toString() {
|
|
37114
|
+
return "[hidden]";
|
|
37115
|
+
}
|
|
37116
|
+
};
|
|
37117
|
+
const rangeQuestion = (subject, installed, range, bumpPath) => ({
|
|
37118
|
+
kind: "range",
|
|
37119
|
+
subject,
|
|
37120
|
+
found: installed,
|
|
37121
|
+
problem: `is above ${range}`,
|
|
37122
|
+
choices: [
|
|
37123
|
+
{
|
|
37124
|
+
value: "bump",
|
|
37125
|
+
title: "bump the config"
|
|
37126
|
+
},
|
|
37127
|
+
{
|
|
37128
|
+
value: "skip",
|
|
37129
|
+
title: "skip on these Machines (leave as is, shown as skipped)"
|
|
37130
|
+
},
|
|
37131
|
+
{
|
|
37132
|
+
value: "downgrade",
|
|
37133
|
+
title: "downgrade into the range"
|
|
37134
|
+
}
|
|
37135
|
+
],
|
|
37136
|
+
default: "skip",
|
|
37137
|
+
bump: {
|
|
37138
|
+
path: bumpPath,
|
|
37139
|
+
range
|
|
37140
|
+
}
|
|
37141
|
+
});
|
|
37142
|
+
const decideRangeItem = (input) => {
|
|
37143
|
+
const { item, range, installed, available } = input;
|
|
37144
|
+
const at = installed === void 0 ? {} : { at: installed };
|
|
37145
|
+
const unavailable = {
|
|
37146
|
+
kind: "fail",
|
|
37147
|
+
item,
|
|
37148
|
+
...at,
|
|
37149
|
+
reason: `no published version satisfies ${range}`
|
|
37150
|
+
};
|
|
37151
|
+
const install = (version) => ({
|
|
37152
|
+
kind: "change",
|
|
37153
|
+
item,
|
|
37154
|
+
...installed === void 0 ? {} : { from: installed },
|
|
37155
|
+
to: version,
|
|
37156
|
+
change: version
|
|
37157
|
+
});
|
|
37158
|
+
const decision = decideRange(range, installed, available);
|
|
37159
|
+
switch (decision.kind) {
|
|
37160
|
+
case "leave": return {
|
|
37161
|
+
kind: "leave",
|
|
37162
|
+
item,
|
|
37163
|
+
...at
|
|
37164
|
+
};
|
|
37165
|
+
case "install": return install(decision.version);
|
|
37166
|
+
case "unavailable": return unavailable;
|
|
37167
|
+
case "ask": {
|
|
37168
|
+
const subject = address(input.provider, item);
|
|
37169
|
+
const answer = input.ctx.answer("range", subject);
|
|
37170
|
+
if (answer === "skip") return {
|
|
37171
|
+
kind: "skip",
|
|
37172
|
+
item,
|
|
37173
|
+
...at,
|
|
37174
|
+
reason: `is above ${range}`
|
|
37175
|
+
};
|
|
37176
|
+
if (answer === "downgrade") {
|
|
37177
|
+
const target = decideRange(range, void 0, available);
|
|
37178
|
+
return target.kind === "install" ? install(target.version) : unavailable;
|
|
37179
|
+
}
|
|
37180
|
+
return {
|
|
37181
|
+
kind: "ask",
|
|
37182
|
+
item,
|
|
37183
|
+
...at,
|
|
37184
|
+
question: rangeQuestion(subject, decision.installed, range, input.bumpPath)
|
|
37185
|
+
};
|
|
37186
|
+
}
|
|
37187
|
+
}
|
|
37188
|
+
};
|
|
37189
|
+
const orderProviders = (all, dependsOn) => {
|
|
37190
|
+
const byName = new Map(all.map((p) => [p.name, p]));
|
|
37191
|
+
const order = [];
|
|
37192
|
+
const done = /* @__PURE__ */ new Set();
|
|
37193
|
+
const visit = (p, path) => {
|
|
37194
|
+
if (done.has(p.name)) return void 0;
|
|
37195
|
+
if (path.includes(p.name)) return [...path.slice(path.indexOf(p.name)), p.name];
|
|
37196
|
+
for (const name of dependsOn(p)) {
|
|
37197
|
+
const dep = byName.get(name);
|
|
37198
|
+
const cycle = dep === void 0 ? void 0 : visit(dep, [...path, p.name]);
|
|
37199
|
+
if (cycle !== void 0) return cycle;
|
|
37200
|
+
}
|
|
37201
|
+
done.add(p.name);
|
|
37202
|
+
order.push(p);
|
|
37203
|
+
};
|
|
37204
|
+
for (const p of all) {
|
|
37205
|
+
const cycle = visit(p, []);
|
|
37206
|
+
if (cycle !== void 0) return { cycle };
|
|
37207
|
+
}
|
|
37208
|
+
return { order };
|
|
37209
|
+
};
|
|
37210
|
+
//#endregion
|
|
37211
|
+
//#region ../../packages/core/src/record.ts
|
|
37212
|
+
const decideInstall = (record, now, rebuild) => {
|
|
37213
|
+
if (rebuild) return {
|
|
37214
|
+
run: true,
|
|
37215
|
+
reason: "--rebuild"
|
|
37216
|
+
};
|
|
37217
|
+
if (record === void 0) return {
|
|
37218
|
+
run: true,
|
|
37219
|
+
reason: "never installed"
|
|
37220
|
+
};
|
|
37221
|
+
if (record.commit !== now.commit) return {
|
|
37222
|
+
run: true,
|
|
37223
|
+
reason: `new commit ${now.commit.slice(0, 7)}`
|
|
37224
|
+
};
|
|
37225
|
+
if (record.path !== now.path) return {
|
|
37226
|
+
run: true,
|
|
37227
|
+
reason: "clone moved"
|
|
37228
|
+
};
|
|
37229
|
+
if (record.install !== now.install) return {
|
|
37230
|
+
run: true,
|
|
37231
|
+
reason: "install command changed"
|
|
37232
|
+
};
|
|
37233
|
+
for (const [path, fingerprint] of Object.entries(now.outputs)) if (fingerprint === void 0 || record.outputs[path] !== fingerprint) return {
|
|
37234
|
+
run: true,
|
|
37235
|
+
reason: `drift: ${path}`
|
|
37236
|
+
};
|
|
37237
|
+
return { run: false };
|
|
37238
|
+
};
|
|
37239
|
+
//#endregion
|
|
37240
|
+
//#region ../../packages/core/src/state.ts
|
|
37241
|
+
const EngineFact = Union([
|
|
37242
|
+
Struct({
|
|
37243
|
+
fact: Literal("home"),
|
|
37244
|
+
path: String$6
|
|
37245
|
+
}),
|
|
37246
|
+
Struct({ fact: Literal("end") }),
|
|
37247
|
+
Struct({
|
|
37248
|
+
fact: Literal("config"),
|
|
37249
|
+
present: Boolean$4,
|
|
37250
|
+
sha: optionalKey(String$6),
|
|
37251
|
+
base64: optionalKey(String$6)
|
|
37252
|
+
}),
|
|
37253
|
+
Struct({
|
|
37254
|
+
fact: Literal("configRecord"),
|
|
37255
|
+
record: Unknown
|
|
37256
|
+
}),
|
|
37257
|
+
Struct({
|
|
37258
|
+
fact: Literal("record"),
|
|
37259
|
+
provider: String$6,
|
|
37260
|
+
item: String$6,
|
|
37261
|
+
record: Unknown
|
|
37262
|
+
})
|
|
37263
|
+
]);
|
|
37264
|
+
const ProviderFact = Struct({ provider: String$6 });
|
|
37265
|
+
const AtFact = Struct({
|
|
37266
|
+
provider: String$6,
|
|
37267
|
+
fact: Literal("at"),
|
|
37268
|
+
item: String$6,
|
|
37269
|
+
at: String$6
|
|
37270
|
+
});
|
|
37271
|
+
const decodeEngineFact = decodeUnknownOption(EngineFact);
|
|
37272
|
+
const decodeProviderFact = decodeUnknownOption(ProviderFact);
|
|
37273
|
+
const emptyState = {
|
|
37274
|
+
complete: false,
|
|
37275
|
+
records: {},
|
|
37276
|
+
facts: {}
|
|
37277
|
+
};
|
|
37278
|
+
const decodeBase64 = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
|
|
37279
|
+
const assembleState = (facts) => {
|
|
37280
|
+
let state = emptyState;
|
|
37281
|
+
for (const raw of facts) {
|
|
37282
|
+
const fact = decodeEngineFact(raw);
|
|
37283
|
+
if (fact._tag === "Some") {
|
|
37284
|
+
const f = fact.value;
|
|
37285
|
+
switch (f.fact) {
|
|
37286
|
+
case "home":
|
|
37287
|
+
state = {
|
|
37288
|
+
...state,
|
|
37289
|
+
home: f.path
|
|
37290
|
+
};
|
|
37291
|
+
break;
|
|
37292
|
+
case "end":
|
|
37293
|
+
state = {
|
|
37294
|
+
...state,
|
|
37295
|
+
complete: true
|
|
37296
|
+
};
|
|
37297
|
+
break;
|
|
37298
|
+
case "config":
|
|
37299
|
+
if (f.present && f.base64 !== void 0 && f.sha !== void 0) state = {
|
|
37300
|
+
...state,
|
|
37301
|
+
config: {
|
|
37302
|
+
text: decodeBase64(f.base64),
|
|
37303
|
+
sha: f.sha
|
|
37304
|
+
}
|
|
37305
|
+
};
|
|
37306
|
+
break;
|
|
37307
|
+
case "configRecord":
|
|
37308
|
+
state = {
|
|
37309
|
+
...state,
|
|
37310
|
+
configRecord: f.record
|
|
37311
|
+
};
|
|
37312
|
+
break;
|
|
37313
|
+
case "record": state = {
|
|
37314
|
+
...state,
|
|
37315
|
+
records: {
|
|
37316
|
+
...state.records,
|
|
37317
|
+
[f.provider]: {
|
|
37318
|
+
...state.records[f.provider],
|
|
37319
|
+
[f.item]: f.record
|
|
37320
|
+
}
|
|
37321
|
+
}
|
|
37322
|
+
};
|
|
37323
|
+
}
|
|
37324
|
+
continue;
|
|
37325
|
+
}
|
|
37326
|
+
const owned = decodeProviderFact(raw);
|
|
37327
|
+
if (owned._tag === "Some") {
|
|
37328
|
+
const { provider } = owned.value;
|
|
37329
|
+
state = {
|
|
37330
|
+
...state,
|
|
37331
|
+
facts: {
|
|
37332
|
+
...state.facts,
|
|
37333
|
+
[provider]: [...state.facts[provider] ?? [], raw]
|
|
37334
|
+
}
|
|
37335
|
+
};
|
|
37336
|
+
}
|
|
37337
|
+
}
|
|
37338
|
+
return state;
|
|
37339
|
+
};
|
|
37340
|
+
//#endregion
|
|
36542
37341
|
//#region ../../packages/remote/src/shell.ts
|
|
36543
37342
|
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
36544
37343
|
const quotePath = (path) => {
|
|
@@ -36897,113 +37696,6 @@ const resolveVersions = fn("resolveVersions")(function* (packages) {
|
|
|
36897
37696
|
return Object.fromEntries(entries);
|
|
36898
37697
|
});
|
|
36899
37698
|
//#endregion
|
|
36900
|
-
//#region src/configRecord.ts
|
|
36901
|
-
const recordPath = () => CONFIG_RECORD.replace(/^~/, process.env.HOME ?? "~");
|
|
36902
|
-
const readConfigRecord = gen(function* () {
|
|
36903
|
-
const text = yield* (yield* FileSystem).readFileString(recordPath()).pipe(option);
|
|
36904
|
-
return getOrUndefined$1(flatMap$3(text, decodeConfigRecordJson));
|
|
36905
|
-
});
|
|
36906
|
-
const writeConfigRecord = fn("writeConfigRecord")(function* (json) {
|
|
36907
|
-
const fs = yield* FileSystem;
|
|
36908
|
-
const path = recordPath();
|
|
36909
|
-
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
36910
|
-
yield* fs.writeFileString(`${path}.tmp`, `${json}\n`);
|
|
36911
|
-
yield* fs.rename(`${path}.tmp`, path);
|
|
36912
|
-
});
|
|
36913
|
-
var LogsError = class extends Error {
|
|
36914
|
-
name = "LogsError";
|
|
36915
|
-
};
|
|
36916
|
-
const shipId = (started, from) => `${formatIso(started).slice(0, 19).replaceAll(":", "-")}Z-${from}`;
|
|
36917
|
-
const parseShipId = (id) => {
|
|
36918
|
-
const [, day, h, m, s, from] = /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})Z-(.+)$/.exec(id) ?? [];
|
|
36919
|
-
if (from === void 0) return void 0;
|
|
36920
|
-
return {
|
|
36921
|
-
started: Date.parse(`${day}T${h}:${m}:${s}Z`),
|
|
36922
|
-
from
|
|
36923
|
-
};
|
|
36924
|
-
};
|
|
36925
|
-
const lineFormat = (hidden) => make(({ message, logLevel, date, cause, fiber }) => {
|
|
36926
|
-
const { machine, phase } = fiber.getRef(CurrentLogAnnotations);
|
|
36927
|
-
const scope = [machine, phase].filter((s) => typeof s === "string").join(" ");
|
|
36928
|
-
const prefix = `${date.toISOString()} ${logLevel.toUpperCase().padEnd(5)}${scope === "" ? "" : ` [${scope}]`}`;
|
|
36929
|
-
const parts = (Array.isArray(message) ? message : [message]).map((m) => typeof m === "string" ? m : JSON.stringify(m));
|
|
36930
|
-
if (cause.reasons.length > 0) parts.push(pretty(cause));
|
|
36931
|
-
let text = parts.join(" ");
|
|
36932
|
-
for (const secret of hidden) text = text.replaceAll(secret, "[hidden]");
|
|
36933
|
-
return text.split("\n").map((line) => `${prefix} ${line}`).join("\n");
|
|
36934
|
-
});
|
|
36935
|
-
const machineOf = (line) => /^\S+ +\S+ +\[([^\] ]+)/.exec(line)?.[1];
|
|
36936
|
-
const toPrune = (names) => {
|
|
36937
|
-
const logs = names.filter((name) => name.endsWith(".log")).toSorted();
|
|
36938
|
-
return logs.slice(0, Math.max(0, logs.length - 19));
|
|
36939
|
-
};
|
|
36940
|
-
const logsDir = () => LOGS_DIR.replace(/^~/, process.env.HOME ?? "~");
|
|
36941
|
-
const openShipLog = fn("openShipLog")(function* (id, hidden) {
|
|
36942
|
-
const fs = yield* FileSystem;
|
|
36943
|
-
const dir = logsDir();
|
|
36944
|
-
yield* fs.makeDirectory(dir, { recursive: true });
|
|
36945
|
-
for (const name of toPrune(yield* fs.readDirectory(dir))) yield* fs.remove(`${dir}/${name}`);
|
|
36946
|
-
return yield* toFile(lineFormat(hidden), `${dir}/${id}.log`);
|
|
36947
|
-
});
|
|
36948
|
-
const read = fn("read")(function* (target, script, onLine) {
|
|
36949
|
-
const code = yield* runScript(target, script, [], onLine);
|
|
36950
|
-
if (!target.local && code === 255) return yield* fail$1(new LogsError(`could not reach ${target.name} over tailscale ssh`));
|
|
36951
|
-
});
|
|
36952
|
-
const shipIds = fn("shipIds")(function* (target) {
|
|
36953
|
-
const ids = [];
|
|
36954
|
-
yield* read(target, `ls -1 ${quotePath(LOGS_DIR)} 2>/dev/null`, (line) => sync(() => {
|
|
36955
|
-
if (line.endsWith(".log")) ids.push(line.slice(0, -4));
|
|
36956
|
-
}));
|
|
36957
|
-
return ids.toSorted();
|
|
36958
|
-
});
|
|
36959
|
-
const MISSING = "ff-missing";
|
|
36960
|
-
const logs = fn("logs")(function* (flags) {
|
|
36961
|
-
const here = {
|
|
36962
|
-
name: "this Machine",
|
|
36963
|
-
local: true
|
|
36964
|
-
};
|
|
36965
|
-
const local = yield* shipIds(here);
|
|
36966
|
-
const self = parseShipId(local.at(-1) ?? "")?.from;
|
|
36967
|
-
const tailnet = yield* cached(readTailnet());
|
|
36968
|
-
const targetOf = fn("targetOf")(function* (name) {
|
|
36969
|
-
if (name === self) return here;
|
|
36970
|
-
const { self: me, fleet } = yield* tailnet;
|
|
36971
|
-
if (name === me.name) return here;
|
|
36972
|
-
const machine = fleet.find((m) => m.name === name);
|
|
36973
|
-
if (machine === void 0) return yield* fail$1(new LogsError(`no Machine ${name} on the tailnet`));
|
|
36974
|
-
if (!machine.online) return yield* fail$1(new LogsError(`${name} is offline; its logs are there`));
|
|
36975
|
-
return {
|
|
36976
|
-
name,
|
|
36977
|
-
local: false
|
|
36978
|
-
};
|
|
36979
|
-
});
|
|
36980
|
-
if (flags.list) {
|
|
36981
|
-
if (local.length === 0) return yield* log("no Ship Log on this Machine yet");
|
|
36982
|
-
for (const id of local.toReversed()) yield* log(id);
|
|
36983
|
-
return;
|
|
36984
|
-
}
|
|
36985
|
-
const id = flags.id ?? (yield* latestShip(local, self));
|
|
36986
|
-
const ship = parseShipId(id);
|
|
36987
|
-
if (ship === void 0) return yield* fail$1(new LogsError(`not a Ship id: ${id} (fizz logs --list shows them)`));
|
|
36988
|
-
let missing = false;
|
|
36989
|
-
yield* read(local.includes(id) ? here : yield* targetOf(ship.from), `ff_log=${quotePath(LOGS_DIR)}/${quote(`${id}.log`)}\nif [ -f "$ff_log" ]; then cat "$ff_log"; else echo ${MISSING}; fi`, (line) => {
|
|
36990
|
-
if (line === MISSING) return sync(() => void (missing = true));
|
|
36991
|
-
if (flags.machine !== void 0 && machineOf(line) !== flags.machine) return void_;
|
|
36992
|
-
return log(line);
|
|
36993
|
-
});
|
|
36994
|
-
if (missing) return yield* fail$1(new LogsError(`no log for ${id} on ${ship.from}; a Machine keeps its last 20`));
|
|
36995
|
-
});
|
|
36996
|
-
const latestShip = fn("latestShip")(function* (local, self) {
|
|
36997
|
-
const newest = local.at(-1);
|
|
36998
|
-
const record = yield* readConfigRecord;
|
|
36999
|
-
const from = parseShipId(record?.ship ?? "")?.from;
|
|
37000
|
-
const at = self === void 0 ? Infinity : Date.parse(record?.reached[self] ?? "");
|
|
37001
|
-
const newestStarted = parseShipId(newest ?? "")?.started ?? -Infinity;
|
|
37002
|
-
if (record !== void 0 && from !== void 0 && from !== self && at > newestStarted) return record.ship;
|
|
37003
|
-
if (newest === void 0) return yield* fail$1(new LogsError("no Ship Log on this Machine yet"));
|
|
37004
|
-
return newest;
|
|
37005
|
-
});
|
|
37006
|
-
//#endregion
|
|
37007
37699
|
//#region ../../node_modules/.pnpm/react@19.3.0/node_modules/react/cjs/react.production.js
|
|
37008
37700
|
/**
|
|
37009
37701
|
* @license React
|
|
@@ -65125,679 +65817,54 @@ const useWindowSize = () => {
|
|
|
65125
65817
|
return size;
|
|
65126
65818
|
};
|
|
65127
65819
|
//#endregion
|
|
65128
|
-
//#region
|
|
65129
|
-
const
|
|
65130
|
-
const
|
|
65131
|
-
const
|
|
65132
|
-
|
|
65133
|
-
|
|
65134
|
-
|
|
65135
|
-
|
|
65136
|
-
|
|
65137
|
-
|
|
65138
|
-
};
|
|
65139
|
-
};
|
|
65140
|
-
//#endregion
|
|
65141
|
-
//#region ../../packages/core/src/engine.ts
|
|
65142
|
-
const dependenciesOf = (provider, config) => [.../* @__PURE__ */ new Set([...provider.dependsOn, ...config[provider.name]?.dependsOn ?? []])];
|
|
65143
|
-
const withoutTargeting = ({ platforms: _platforms, machines: _machines, ...def }) => def;
|
|
65144
|
-
const targetedItems = (provider, config, machine) => {
|
|
65145
|
-
const section = config[provider.name];
|
|
65146
|
-
if (section === void 0 || !targets(section, machine)) return provider.items(void 0, []);
|
|
65147
|
-
const listed = Object.entries(section.items ?? {}).flatMap(([name, entry]) => {
|
|
65148
|
-
const chosen = variantFor(entry, machine);
|
|
65149
|
-
if (chosen === void 0) return [];
|
|
65150
|
-
const at = chosen.index === void 0 ? [] : [chosen.index];
|
|
65151
|
-
return [{
|
|
65152
|
-
name,
|
|
65153
|
-
def: withoutTargeting(chosen.variant),
|
|
65154
|
-
path: [
|
|
65155
|
-
provider.name,
|
|
65156
|
-
"items",
|
|
65157
|
-
name,
|
|
65158
|
-
...at
|
|
65159
|
-
]
|
|
65160
|
-
}];
|
|
65161
|
-
});
|
|
65162
|
-
return provider.items(section, listed);
|
|
65163
|
-
};
|
|
65164
|
-
const machineNames = (value) => {
|
|
65165
|
-
if (Array.isArray(value)) return value.flatMap(machineNames);
|
|
65166
|
-
if (typeof value !== "object" || value === null) return [];
|
|
65167
|
-
return Object.entries(value).flatMap(([key, v]) => key === "machines" && Array.isArray(v) && v.every((name) => typeof name === "string") ? v : machineNames(v));
|
|
65168
|
-
};
|
|
65169
|
-
const machinesNamed = (provider, config) => machineNames(config[provider.name]);
|
|
65170
|
-
const hold = (decisions, by) => decisions.map((d) => d.kind === "change" || d.kind === "ask" ? {
|
|
65171
|
-
kind: "skip",
|
|
65172
|
-
item: d.item,
|
|
65173
|
-
...d.kind === "change" && d.from !== void 0 ? { at: d.from } : {},
|
|
65174
|
-
...d.kind === "ask" && d.at !== void 0 ? { at: d.at } : {},
|
|
65175
|
-
reason: `depends on ${by}`
|
|
65176
|
-
} : d);
|
|
65177
|
-
const planMachine = (input) => {
|
|
65178
|
-
const ctx = {
|
|
65179
|
-
...input.ctx,
|
|
65180
|
-
home: input.state.home ?? "~"
|
|
65181
|
-
};
|
|
65182
|
-
const plans = /* @__PURE__ */ new Map();
|
|
65183
|
-
const held = /* @__PURE__ */ new Set();
|
|
65184
|
-
for (const provider of input.providers) {
|
|
65185
|
-
const dependsOn = dependenciesOf(provider, ctx.config);
|
|
65186
|
-
const items = targetedItems(provider, ctx.config, ctx.machine);
|
|
65187
|
-
const decisions = provider.decide({
|
|
65188
|
-
items,
|
|
65189
|
-
observed: provider.parse(input.state.facts[provider.name] ?? [], input.state.records[provider.name] ?? {}),
|
|
65190
|
-
resolved: input.resolved[provider.name],
|
|
65191
|
-
ctx
|
|
65192
|
-
});
|
|
65193
|
-
const by = dependsOn.find((name) => {
|
|
65194
|
-
const dep = plans.get(name);
|
|
65195
|
-
return dep !== void 0 && dep.decisions.length > 0 && (held.has(name) || dep.decisions.some((d) => d.kind === "fail"));
|
|
65196
|
-
});
|
|
65197
|
-
if (by !== void 0) held.add(provider.name);
|
|
65198
|
-
plans.set(provider.name, {
|
|
65199
|
-
name: provider.name,
|
|
65200
|
-
rule: provider.rule,
|
|
65201
|
-
dependsOn,
|
|
65202
|
-
decisions: by === void 0 ? decisions : hold(decisions, by),
|
|
65203
|
-
steps: by === void 0 ? provider.act(decisions, ctx) : []
|
|
65204
|
-
});
|
|
65205
|
-
}
|
|
65206
|
-
return {
|
|
65207
|
-
machine: ctx.machine,
|
|
65208
|
-
providers: [...plans.values()]
|
|
65209
|
-
};
|
|
65210
|
-
};
|
|
65211
|
-
//#endregion
|
|
65212
|
-
//#region ../../packages/core/src/range.ts
|
|
65213
|
-
const newest = (available) => available.filter((v) => import_semver.prerelease(v) === null).sort(import_semver.rcompare)[0];
|
|
65214
|
-
const decideRange = (range, installed, available) => {
|
|
65215
|
-
if (range === "latest") {
|
|
65216
|
-
const version = newest(available);
|
|
65217
|
-
if (version === void 0) return { kind: "unavailable" };
|
|
65218
|
-
return installed === version ? { kind: "leave" } : {
|
|
65219
|
-
kind: "install",
|
|
65220
|
-
version
|
|
65221
|
-
};
|
|
65222
|
-
}
|
|
65223
|
-
if (range === "*") {
|
|
65224
|
-
if (installed !== void 0) return { kind: "leave" };
|
|
65225
|
-
const version = newest(available);
|
|
65226
|
-
return version === void 0 ? { kind: "unavailable" } : {
|
|
65227
|
-
kind: "install",
|
|
65228
|
-
version
|
|
65229
|
-
};
|
|
65230
|
-
}
|
|
65231
|
-
if (installed !== void 0 && import_semver.satisfies(installed, range)) return { kind: "leave" };
|
|
65232
|
-
if (installed !== void 0 && import_semver.gtr(installed, range)) return {
|
|
65233
|
-
kind: "ask",
|
|
65234
|
-
installed
|
|
65235
|
-
};
|
|
65236
|
-
const version = import_semver.maxSatisfying([...available], range);
|
|
65237
|
-
return version === null ? { kind: "unavailable" } : {
|
|
65238
|
-
kind: "install",
|
|
65239
|
-
version
|
|
65240
|
-
};
|
|
65241
|
-
};
|
|
65242
|
-
//#endregion
|
|
65243
|
-
//#region ../../packages/core/src/provider.ts
|
|
65244
|
-
const address = (provider, item) => `${provider}:${item}`;
|
|
65245
|
-
const parseAddress = (text) => {
|
|
65246
|
-
const at = text.indexOf(":");
|
|
65247
|
-
return at <= 0 || at === text.length - 1 ? void 0 : {
|
|
65248
|
-
provider: text.slice(0, at),
|
|
65249
|
-
item: text.slice(at + 1)
|
|
65250
|
-
};
|
|
65251
|
-
};
|
|
65252
|
-
const QUESTION_CHOICES = {
|
|
65253
|
-
range: [
|
|
65254
|
-
"bump",
|
|
65255
|
-
"skip",
|
|
65256
|
-
"downgrade"
|
|
65257
|
-
],
|
|
65258
|
-
config: ["overwrite", "stop"],
|
|
65259
|
-
mode: ["managed", "skip"],
|
|
65260
|
-
file: ["overwrite", "skip"]
|
|
65261
|
-
};
|
|
65262
|
-
const isQuestionKind = (kind) => kind in QUESTION_CHOICES;
|
|
65263
|
-
var Hidden = class {
|
|
65264
|
-
#value;
|
|
65265
|
-
constructor(value) {
|
|
65266
|
-
this.#value = value;
|
|
65267
|
-
}
|
|
65268
|
-
reveal() {
|
|
65269
|
-
return this.#value;
|
|
65270
|
-
}
|
|
65271
|
-
toJSON() {
|
|
65272
|
-
return "[hidden]";
|
|
65273
|
-
}
|
|
65274
|
-
toString() {
|
|
65275
|
-
return "[hidden]";
|
|
65276
|
-
}
|
|
65277
|
-
};
|
|
65278
|
-
const rangeQuestion = (subject, installed, range, bumpPath) => ({
|
|
65279
|
-
kind: "range",
|
|
65280
|
-
subject,
|
|
65281
|
-
found: installed,
|
|
65282
|
-
problem: `is above ${range}`,
|
|
65283
|
-
choices: [
|
|
65284
|
-
{
|
|
65285
|
-
value: "bump",
|
|
65286
|
-
title: "bump the config"
|
|
65287
|
-
},
|
|
65288
|
-
{
|
|
65289
|
-
value: "skip",
|
|
65290
|
-
title: "skip on these Machines (leave as is, shown as skipped)"
|
|
65291
|
-
},
|
|
65292
|
-
{
|
|
65293
|
-
value: "downgrade",
|
|
65294
|
-
title: "downgrade into the range"
|
|
65295
|
-
}
|
|
65296
|
-
],
|
|
65297
|
-
default: "skip",
|
|
65298
|
-
bump: {
|
|
65299
|
-
path: bumpPath,
|
|
65300
|
-
range
|
|
65301
|
-
}
|
|
65820
|
+
//#region src/configRecord.ts
|
|
65821
|
+
const recordPath = () => CONFIG_RECORD.replace(/^~/, process.env.HOME ?? "~");
|
|
65822
|
+
const readConfigRecord = gen(function* () {
|
|
65823
|
+
const text = yield* (yield* FileSystem).readFileString(recordPath()).pipe(option);
|
|
65824
|
+
return getOrUndefined$1(flatMap$3(text, decodeConfigRecordJson));
|
|
65825
|
+
});
|
|
65826
|
+
const writeConfigRecord = fn("writeConfigRecord")(function* (json) {
|
|
65827
|
+
const fs = yield* FileSystem;
|
|
65828
|
+
const path = recordPath();
|
|
65829
|
+
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
65830
|
+
yield* fs.writeFileString(`${path}.tmp`, `${json}\n`);
|
|
65831
|
+
yield* fs.rename(`${path}.tmp`, path);
|
|
65302
65832
|
});
|
|
65303
|
-
const decideRangeItem = (input) => {
|
|
65304
|
-
const { item, range, installed, available } = input;
|
|
65305
|
-
const at = installed === void 0 ? {} : { at: installed };
|
|
65306
|
-
const unavailable = {
|
|
65307
|
-
kind: "fail",
|
|
65308
|
-
item,
|
|
65309
|
-
...at,
|
|
65310
|
-
reason: `no published version satisfies ${range}`
|
|
65311
|
-
};
|
|
65312
|
-
const install = (version) => ({
|
|
65313
|
-
kind: "change",
|
|
65314
|
-
item,
|
|
65315
|
-
...installed === void 0 ? {} : { from: installed },
|
|
65316
|
-
to: version,
|
|
65317
|
-
change: version
|
|
65318
|
-
});
|
|
65319
|
-
const decision = decideRange(range, installed, available);
|
|
65320
|
-
switch (decision.kind) {
|
|
65321
|
-
case "leave": return {
|
|
65322
|
-
kind: "leave",
|
|
65323
|
-
item,
|
|
65324
|
-
...at
|
|
65325
|
-
};
|
|
65326
|
-
case "install": return install(decision.version);
|
|
65327
|
-
case "unavailable": return unavailable;
|
|
65328
|
-
case "ask": {
|
|
65329
|
-
const subject = address(input.provider, item);
|
|
65330
|
-
const answer = input.ctx.answer("range", subject);
|
|
65331
|
-
if (answer === "skip") return {
|
|
65332
|
-
kind: "skip",
|
|
65333
|
-
item,
|
|
65334
|
-
...at,
|
|
65335
|
-
reason: `is above ${range}`
|
|
65336
|
-
};
|
|
65337
|
-
if (answer === "downgrade") {
|
|
65338
|
-
const target = decideRange(range, void 0, available);
|
|
65339
|
-
return target.kind === "install" ? install(target.version) : unavailable;
|
|
65340
|
-
}
|
|
65341
|
-
return {
|
|
65342
|
-
kind: "ask",
|
|
65343
|
-
item,
|
|
65344
|
-
...at,
|
|
65345
|
-
question: rangeQuestion(subject, decision.installed, range, input.bumpPath)
|
|
65346
|
-
};
|
|
65347
|
-
}
|
|
65348
|
-
}
|
|
65349
|
-
};
|
|
65350
|
-
const orderProviders = (all, dependsOn) => {
|
|
65351
|
-
const byName = new Map(all.map((p) => [p.name, p]));
|
|
65352
|
-
const order = [];
|
|
65353
|
-
const done = /* @__PURE__ */ new Set();
|
|
65354
|
-
const visit = (p, path) => {
|
|
65355
|
-
if (done.has(p.name)) return void 0;
|
|
65356
|
-
if (path.includes(p.name)) return [...path.slice(path.indexOf(p.name)), p.name];
|
|
65357
|
-
for (const name of dependsOn(p)) {
|
|
65358
|
-
const dep = byName.get(name);
|
|
65359
|
-
const cycle = dep === void 0 ? void 0 : visit(dep, [...path, p.name]);
|
|
65360
|
-
if (cycle !== void 0) return cycle;
|
|
65361
|
-
}
|
|
65362
|
-
done.add(p.name);
|
|
65363
|
-
order.push(p);
|
|
65364
|
-
};
|
|
65365
|
-
for (const p of all) {
|
|
65366
|
-
const cycle = visit(p, []);
|
|
65367
|
-
if (cycle !== void 0) return { cycle };
|
|
65368
|
-
}
|
|
65369
|
-
return { order };
|
|
65370
|
-
};
|
|
65371
|
-
//#endregion
|
|
65372
|
-
//#region ../../packages/core/src/record.ts
|
|
65373
|
-
const decideInstall = (record, now, rebuild) => {
|
|
65374
|
-
if (rebuild) return {
|
|
65375
|
-
run: true,
|
|
65376
|
-
reason: "--rebuild"
|
|
65377
|
-
};
|
|
65378
|
-
if (record === void 0) return {
|
|
65379
|
-
run: true,
|
|
65380
|
-
reason: "never installed"
|
|
65381
|
-
};
|
|
65382
|
-
if (record.commit !== now.commit) return {
|
|
65383
|
-
run: true,
|
|
65384
|
-
reason: `new commit ${now.commit.slice(0, 7)}`
|
|
65385
|
-
};
|
|
65386
|
-
if (record.path !== now.path) return {
|
|
65387
|
-
run: true,
|
|
65388
|
-
reason: "clone moved"
|
|
65389
|
-
};
|
|
65390
|
-
if (record.install !== now.install) return {
|
|
65391
|
-
run: true,
|
|
65392
|
-
reason: "install command changed"
|
|
65393
|
-
};
|
|
65394
|
-
for (const [path, fingerprint] of Object.entries(now.outputs)) if (fingerprint === void 0 || record.outputs[path] !== fingerprint) return {
|
|
65395
|
-
run: true,
|
|
65396
|
-
reason: `drift: ${path}`
|
|
65397
|
-
};
|
|
65398
|
-
return { run: false };
|
|
65399
|
-
};
|
|
65400
65833
|
//#endregion
|
|
65401
|
-
//#region
|
|
65402
|
-
const
|
|
65403
|
-
|
|
65404
|
-
|
|
65405
|
-
|
|
65406
|
-
|
|
65407
|
-
|
|
65408
|
-
|
|
65409
|
-
|
|
65410
|
-
|
|
65411
|
-
|
|
65412
|
-
|
|
65413
|
-
|
|
65414
|
-
|
|
65415
|
-
|
|
65416
|
-
|
|
65417
|
-
|
|
65418
|
-
|
|
65419
|
-
|
|
65420
|
-
|
|
65421
|
-
|
|
65422
|
-
|
|
65423
|
-
})
|
|
65424
|
-
|
|
65425
|
-
|
|
65426
|
-
|
|
65427
|
-
provider: String$6,
|
|
65428
|
-
fact: Literal("at"),
|
|
65429
|
-
item: String$6,
|
|
65430
|
-
at: String$6
|
|
65834
|
+
//#region src/local.ts
|
|
65835
|
+
const runLocal = fn("runLocal")(function* (command, args, env) {
|
|
65836
|
+
const spawner = yield* ChildProcessSpawner;
|
|
65837
|
+
return yield* scoped(gen(function* () {
|
|
65838
|
+
const handle = yield* spawner.spawn(make$13(command, [...args], {
|
|
65839
|
+
stdin: "ignore",
|
|
65840
|
+
stdout: "pipe",
|
|
65841
|
+
stderr: "pipe",
|
|
65842
|
+
...env === void 0 ? {} : { env: {
|
|
65843
|
+
...process.env,
|
|
65844
|
+
...env
|
|
65845
|
+
} }
|
|
65846
|
+
}));
|
|
65847
|
+
const [stdout, , code] = yield* all([
|
|
65848
|
+
runCollect(decodeText(handle.stdout)),
|
|
65849
|
+
runDrain(handle.stderr),
|
|
65850
|
+
handle.exitCode
|
|
65851
|
+
], { concurrency: "unbounded" });
|
|
65852
|
+
return {
|
|
65853
|
+
code,
|
|
65854
|
+
stdout: stdout.join("")
|
|
65855
|
+
};
|
|
65856
|
+
})).pipe(orElseSucceed(() => ({
|
|
65857
|
+
code: 127,
|
|
65858
|
+
stdout: ""
|
|
65859
|
+
})));
|
|
65431
65860
|
});
|
|
65432
|
-
const
|
|
65433
|
-
const
|
|
65434
|
-
const
|
|
65435
|
-
|
|
65436
|
-
|
|
65437
|
-
facts: {}
|
|
65438
|
-
};
|
|
65439
|
-
const decodeBase64 = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
|
|
65440
|
-
const assembleState = (facts) => {
|
|
65441
|
-
let state = emptyState;
|
|
65442
|
-
for (const raw of facts) {
|
|
65443
|
-
const fact = decodeEngineFact(raw);
|
|
65444
|
-
if (fact._tag === "Some") {
|
|
65445
|
-
const f = fact.value;
|
|
65446
|
-
switch (f.fact) {
|
|
65447
|
-
case "home":
|
|
65448
|
-
state = {
|
|
65449
|
-
...state,
|
|
65450
|
-
home: f.path
|
|
65451
|
-
};
|
|
65452
|
-
break;
|
|
65453
|
-
case "end":
|
|
65454
|
-
state = {
|
|
65455
|
-
...state,
|
|
65456
|
-
complete: true
|
|
65457
|
-
};
|
|
65458
|
-
break;
|
|
65459
|
-
case "config":
|
|
65460
|
-
if (f.present && f.base64 !== void 0 && f.sha !== void 0) state = {
|
|
65461
|
-
...state,
|
|
65462
|
-
config: {
|
|
65463
|
-
text: decodeBase64(f.base64),
|
|
65464
|
-
sha: f.sha
|
|
65465
|
-
}
|
|
65466
|
-
};
|
|
65467
|
-
break;
|
|
65468
|
-
case "configRecord":
|
|
65469
|
-
state = {
|
|
65470
|
-
...state,
|
|
65471
|
-
configRecord: f.record
|
|
65472
|
-
};
|
|
65473
|
-
break;
|
|
65474
|
-
case "record": state = {
|
|
65475
|
-
...state,
|
|
65476
|
-
records: {
|
|
65477
|
-
...state.records,
|
|
65478
|
-
[f.provider]: {
|
|
65479
|
-
...state.records[f.provider],
|
|
65480
|
-
[f.item]: f.record
|
|
65481
|
-
}
|
|
65482
|
-
}
|
|
65483
|
-
};
|
|
65484
|
-
}
|
|
65485
|
-
continue;
|
|
65486
|
-
}
|
|
65487
|
-
const owned = decodeProviderFact(raw);
|
|
65488
|
-
if (owned._tag === "Some") {
|
|
65489
|
-
const { provider } = owned.value;
|
|
65490
|
-
state = {
|
|
65491
|
-
...state,
|
|
65492
|
-
facts: {
|
|
65493
|
-
...state.facts,
|
|
65494
|
-
[provider]: [...state.facts[provider] ?? [], raw]
|
|
65495
|
-
}
|
|
65496
|
-
};
|
|
65497
|
-
}
|
|
65498
|
-
}
|
|
65499
|
-
return state;
|
|
65500
|
-
};
|
|
65501
|
-
//#endregion
|
|
65502
|
-
//#region src/ledger.ts
|
|
65503
|
-
const Note = Struct({
|
|
65504
|
-
kind: Literals$2([
|
|
65505
|
-
"ask",
|
|
65506
|
-
"answer",
|
|
65507
|
-
"info"
|
|
65508
|
-
]),
|
|
65509
|
-
text: String$6
|
|
65510
|
-
});
|
|
65511
|
-
const Item = Struct({
|
|
65512
|
-
name: String$6,
|
|
65513
|
-
kind: Literals$2([
|
|
65514
|
-
"range",
|
|
65515
|
-
"record",
|
|
65516
|
-
"config"
|
|
65517
|
-
]),
|
|
65518
|
-
from: optionalKey(String$6),
|
|
65519
|
-
to: optionalKey(String$6),
|
|
65520
|
-
reason: optionalKey(String$6),
|
|
65521
|
-
changes: Boolean$4,
|
|
65522
|
-
status: Literals$2([
|
|
65523
|
-
"idle",
|
|
65524
|
-
"running",
|
|
65525
|
-
"ok",
|
|
65526
|
-
"failed",
|
|
65527
|
-
"skipped"
|
|
65528
|
-
]),
|
|
65529
|
-
doing: optionalKey(String$6),
|
|
65530
|
-
detail: optionalKey(String$6),
|
|
65531
|
-
output: optionalKey(ArraySchema(String$6)),
|
|
65532
|
-
steps: ArraySchema(Struct({
|
|
65533
|
-
id: String$6,
|
|
65534
|
-
doing: String$6
|
|
65535
|
-
})),
|
|
65536
|
-
started: optionalKey(Number$2),
|
|
65537
|
-
ended: optionalKey(Number$2)
|
|
65538
|
-
});
|
|
65539
|
-
const MachineEntry = Struct({
|
|
65540
|
-
name: String$6,
|
|
65541
|
-
status: Literals$2([
|
|
65542
|
-
"checking",
|
|
65543
|
-
"waiting",
|
|
65544
|
-
"planned",
|
|
65545
|
-
"running",
|
|
65546
|
-
"ok",
|
|
65547
|
-
"skipped",
|
|
65548
|
-
"failed"
|
|
65549
|
-
]),
|
|
65550
|
-
detail: optionalKey(String$6),
|
|
65551
|
-
output: optionalKey(ArraySchema(String$6)),
|
|
65552
|
-
items: ArraySchema(Item),
|
|
65553
|
-
connected: optionalKey(Boolean$4),
|
|
65554
|
-
checked: optionalKey(Number$2),
|
|
65555
|
-
started: optionalKey(Number$2),
|
|
65556
|
-
ended: optionalKey(Number$2)
|
|
65557
|
-
});
|
|
65558
|
-
const LedgerState = Struct({
|
|
65559
|
-
from: String$6,
|
|
65560
|
-
dryRun: Boolean$4,
|
|
65561
|
-
notes: ArraySchema(Note),
|
|
65562
|
-
machines: ArraySchema(MachineEntry),
|
|
65563
|
-
resolving: optionalKey(Struct({
|
|
65564
|
-
started: Number$2,
|
|
65565
|
-
items: Number$2
|
|
65566
|
-
})),
|
|
65567
|
-
started: Number$2,
|
|
65568
|
-
ended: optionalKey(Number$2)
|
|
65569
|
-
});
|
|
65570
|
-
const itemsOf = (p, config) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
65571
|
-
const base = {
|
|
65572
|
-
name: address(pp.name, d.item),
|
|
65573
|
-
kind: pp.rule,
|
|
65574
|
-
steps: pp.steps.filter((step) => step.items.includes(d.item)).map(({ id, doing }) => ({
|
|
65575
|
-
id,
|
|
65576
|
-
doing
|
|
65577
|
-
}))
|
|
65578
|
-
};
|
|
65579
|
-
switch (d.kind) {
|
|
65580
|
-
case "change": return {
|
|
65581
|
-
...base,
|
|
65582
|
-
...d.from === void 0 ? {} : { from: d.from },
|
|
65583
|
-
to: d.to,
|
|
65584
|
-
...d.reason === void 0 ? {} : { reason: d.reason },
|
|
65585
|
-
changes: true,
|
|
65586
|
-
status: "idle"
|
|
65587
|
-
};
|
|
65588
|
-
case "leave": return {
|
|
65589
|
-
...base,
|
|
65590
|
-
...d.at === void 0 ? {} : { from: d.at },
|
|
65591
|
-
changes: false,
|
|
65592
|
-
status: "idle"
|
|
65593
|
-
};
|
|
65594
|
-
case "skip":
|
|
65595
|
-
case "fail": return {
|
|
65596
|
-
...base,
|
|
65597
|
-
...d.at === void 0 ? {} : { from: d.at },
|
|
65598
|
-
changes: d.kind === "fail",
|
|
65599
|
-
status: d.kind === "fail" ? "failed" : "skipped",
|
|
65600
|
-
detail: d.reason
|
|
65601
|
-
};
|
|
65602
|
-
case "ask": return {
|
|
65603
|
-
...base,
|
|
65604
|
-
...d.at === void 0 ? {} : { from: d.at },
|
|
65605
|
-
changes: false,
|
|
65606
|
-
status: "skipped",
|
|
65607
|
-
detail: d.question.problem
|
|
65608
|
-
};
|
|
65609
|
-
}
|
|
65610
|
-
})), {
|
|
65611
|
-
name: CONFIG_STEP,
|
|
65612
|
-
kind: "config",
|
|
65613
|
-
...config,
|
|
65614
|
-
changes: config.from !== config.to,
|
|
65615
|
-
status: "idle",
|
|
65616
|
-
steps: [{
|
|
65617
|
-
id: CONFIG_STEP,
|
|
65618
|
-
doing: "copying"
|
|
65619
|
-
}]
|
|
65620
|
-
}];
|
|
65621
|
-
const outcomeOf = (m, dryRun) => m.status === "skipped" ? "skipped" : m.status === "failed" || m.items.some((i) => i.status === "failed") ? "failed" : m.status === "ok" || m.status === "planned" && dryRun ? "ok" : void 0;
|
|
65622
|
-
const anyFailed = (state) => state.machines.some((m) => outcomeOf(m, state.dryRun) === "failed");
|
|
65623
|
-
const makeLedger = fn("makeLedger")(function* (input) {
|
|
65624
|
-
const started = yield* currentTimeMillis;
|
|
65625
|
-
let snapshot = {
|
|
65626
|
-
state: {
|
|
65627
|
-
from: input.from,
|
|
65628
|
-
dryRun: input.dryRun,
|
|
65629
|
-
notes: [],
|
|
65630
|
-
machines: input.machines.map((name) => ({
|
|
65631
|
-
name,
|
|
65632
|
-
status: "checking",
|
|
65633
|
-
items: []
|
|
65634
|
-
})),
|
|
65635
|
-
started
|
|
65636
|
-
},
|
|
65637
|
-
now: started
|
|
65638
|
-
};
|
|
65639
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
65640
|
-
const update = (change) => gen(function* () {
|
|
65641
|
-
const now = yield* currentTimeMillis;
|
|
65642
|
-
snapshot = {
|
|
65643
|
-
state: change(snapshot.state, now),
|
|
65644
|
-
now
|
|
65645
|
-
};
|
|
65646
|
-
for (const listener of listeners) listener();
|
|
65647
|
-
});
|
|
65648
|
-
const machine = (name, change) => update((state, now) => ({
|
|
65649
|
-
...state,
|
|
65650
|
-
machines: state.machines.map((m) => m.name === name ? change(m, now) : m)
|
|
65651
|
-
}));
|
|
65652
|
-
const items = (name, id, change) => machine(name, (m, now) => ({
|
|
65653
|
-
...m,
|
|
65654
|
-
items: m.items.map((i) => i.steps.some((step) => step.id === id) ? change(i, now) : i)
|
|
65655
|
-
}));
|
|
65656
|
-
const store = {
|
|
65657
|
-
get: () => snapshot,
|
|
65658
|
-
subscribe: (listener) => {
|
|
65659
|
-
listeners.add(listener);
|
|
65660
|
-
return () => listeners.delete(listener);
|
|
65661
|
-
},
|
|
65662
|
-
tick: update((state) => state)
|
|
65663
|
-
};
|
|
65664
|
-
const log = (name, entry) => annotateLogs(entry, { machine: name });
|
|
65665
|
-
return {
|
|
65666
|
-
ledger: {
|
|
65667
|
-
note: fn("ledger.note")(function* (kind, text) {
|
|
65668
|
-
yield* update((state) => ({
|
|
65669
|
-
...state,
|
|
65670
|
-
notes: [...state.notes, {
|
|
65671
|
-
kind,
|
|
65672
|
-
text
|
|
65673
|
-
}]
|
|
65674
|
-
}));
|
|
65675
|
-
yield* input.print({
|
|
65676
|
-
kind,
|
|
65677
|
-
text
|
|
65678
|
-
});
|
|
65679
|
-
yield* logInfo(`${kind}: ${text}`);
|
|
65680
|
-
}),
|
|
65681
|
-
connected: (name) => machine(name, (m) => ({
|
|
65682
|
-
...m,
|
|
65683
|
-
connected: true
|
|
65684
|
-
})).pipe(andThen(log(name, logInfo("connected")))),
|
|
65685
|
-
checked: (name) => machine(name, (m, now) => ({
|
|
65686
|
-
...m,
|
|
65687
|
-
status: "waiting",
|
|
65688
|
-
checked: now
|
|
65689
|
-
})).pipe(andThen(log(name, logInfo("checked")))),
|
|
65690
|
-
skip: (name, reason) => machine(name, (m) => ({
|
|
65691
|
-
...m,
|
|
65692
|
-
status: "skipped",
|
|
65693
|
-
detail: reason
|
|
65694
|
-
})).pipe(andThen(log(name, logWarning(`skipped: ${reason}`)))),
|
|
65695
|
-
fail: (name, [reason = "failed", ...output]) => machine(name, (m, now) => ({
|
|
65696
|
-
...m,
|
|
65697
|
-
status: "failed",
|
|
65698
|
-
detail: reason,
|
|
65699
|
-
...output.length === 0 ? {} : { output },
|
|
65700
|
-
ended: now
|
|
65701
|
-
})).pipe(andThen(log(name, logError(`failed: ${reason}`)))),
|
|
65702
|
-
resolving: (n) => update((state, now) => ({
|
|
65703
|
-
...state,
|
|
65704
|
-
resolving: {
|
|
65705
|
-
started: now,
|
|
65706
|
-
items: n
|
|
65707
|
-
}
|
|
65708
|
-
})).pipe(andThen(logInfo(`resolving ${n} items`))),
|
|
65709
|
-
resolved: update(({ resolving: _, ...state }) => state).pipe(andThen(logInfo("resolved"))),
|
|
65710
|
-
plan: (name, p, config) => machine(name, (m) => ({
|
|
65711
|
-
...m,
|
|
65712
|
-
status: "planned",
|
|
65713
|
-
items: itemsOf(p, config)
|
|
65714
|
-
})).pipe(andThen(forEach$1(p.providers.flatMap((pp) => pp.decisions.map((d) => ({
|
|
65715
|
-
pp,
|
|
65716
|
-
d
|
|
65717
|
-
}))), ({ pp, d }) => log(name, logInfo(`plan ${address(pp.name, d.item)}:`, d)), { discard: true }))),
|
|
65718
|
-
start: (name) => machine(name, (m, now) => ({
|
|
65719
|
-
...m,
|
|
65720
|
-
status: "running",
|
|
65721
|
-
connected: false,
|
|
65722
|
-
started: now
|
|
65723
|
-
})).pipe(andThen(log(name, logInfo("applying")))),
|
|
65724
|
-
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" || item.status === "skipped" ? item : {
|
|
65725
|
-
...item,
|
|
65726
|
-
status: "running",
|
|
65727
|
-
doing: item.steps.find((step) => step.id === id)?.doing ?? "running",
|
|
65728
|
-
started: item.started ?? now
|
|
65729
|
-
}).pipe(andThen(log(name, logInfo(`step ${id}`)))),
|
|
65730
|
-
stepDone: (name, id, result) => items(name, id, (item, now) => {
|
|
65731
|
-
if (item.status === "failed" || item.status === "skipped") return item;
|
|
65732
|
-
if (!result.ok) return {
|
|
65733
|
-
...item,
|
|
65734
|
-
changes: true,
|
|
65735
|
-
status: "failed",
|
|
65736
|
-
detail: result.detail,
|
|
65737
|
-
...result.output.length === 0 ? {} : { output: result.output },
|
|
65738
|
-
ended: now
|
|
65739
|
-
};
|
|
65740
|
-
if (item.steps.at(-1)?.id !== id) return item;
|
|
65741
|
-
const { doing: _, ...rest } = item;
|
|
65742
|
-
return {
|
|
65743
|
-
...rest,
|
|
65744
|
-
status: "ok",
|
|
65745
|
-
ended: now
|
|
65746
|
-
};
|
|
65747
|
-
}).pipe(andThen(log(name, result.ok ? logInfo(`step ${id}: done`) : logWarning(`step ${id}: ${result.detail}`)))),
|
|
65748
|
-
stepSkipped: (name, id, reason) => items(name, id, (item, now) => {
|
|
65749
|
-
if (item.status === "failed") return item;
|
|
65750
|
-
const { doing: _, ...rest } = item;
|
|
65751
|
-
return {
|
|
65752
|
-
...rest,
|
|
65753
|
-
status: "skipped",
|
|
65754
|
-
detail: reason,
|
|
65755
|
-
ended: now
|
|
65756
|
-
};
|
|
65757
|
-
}).pipe(andThen(log(name, logInfo(`step ${id}: skipped, ${reason}`)))),
|
|
65758
|
-
reached: (name, item, at) => machine(name, (m) => ({
|
|
65759
|
-
...m,
|
|
65760
|
-
items: m.items.map((i) => i.name !== item ? i : {
|
|
65761
|
-
...i,
|
|
65762
|
-
to: at,
|
|
65763
|
-
changes: i.status === "failed" || i.from !== at
|
|
65764
|
-
})
|
|
65765
|
-
})).pipe(andThen(log(name, logInfo(`${item} reached ${at}`)))),
|
|
65766
|
-
finish: (name, outcome) => machine(name, (m, now) => ({
|
|
65767
|
-
...m,
|
|
65768
|
-
status: m.status === "failed" ? "failed" : outcome,
|
|
65769
|
-
ended: m.ended ?? now
|
|
65770
|
-
})).pipe(andThen(log(name, logInfo(`finished: ${outcome}`)))),
|
|
65771
|
-
end: gen(function* () {
|
|
65772
|
-
yield* update(({ resolving: _, ...state }, now) => ({
|
|
65773
|
-
...state,
|
|
65774
|
-
ended: now,
|
|
65775
|
-
machines: state.machines.map((m) => m.status === "running" || m.status === "checking" ? {
|
|
65776
|
-
...m,
|
|
65777
|
-
status: "failed",
|
|
65778
|
-
detail: "stopped",
|
|
65779
|
-
ended: now,
|
|
65780
|
-
items: m.items.map((i) => i.status === "running" ? {
|
|
65781
|
-
...i,
|
|
65782
|
-
status: "failed",
|
|
65783
|
-
detail: "stopped",
|
|
65784
|
-
ended: now
|
|
65785
|
-
} : i)
|
|
65786
|
-
} : m)
|
|
65787
|
-
}));
|
|
65788
|
-
return snapshot.state;
|
|
65789
|
-
})
|
|
65790
|
-
},
|
|
65791
|
-
store
|
|
65792
|
-
};
|
|
65793
|
-
});
|
|
65794
|
-
const duration = (ms) => {
|
|
65795
|
-
const s = ms / 1e3;
|
|
65796
|
-
if (s < 60) return `${s.toFixed(1)}s`;
|
|
65797
|
-
const m = Math.floor(s / 60);
|
|
65798
|
-
if (m < 60) return `${m}m ${String(Math.floor(s % 60)).padStart(2, "0")}s`;
|
|
65799
|
-
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
65861
|
+
const interactive = () => Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
65862
|
+
const homeRelative = (path) => {
|
|
65863
|
+
const home = process.env.HOME;
|
|
65864
|
+
if (home === void 0 || home === "") return path;
|
|
65865
|
+
return path === home ? "~" : path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
|
|
65800
65866
|
};
|
|
65867
|
+
const expandHome$1 = (path) => path === "~" || path.startsWith("~/") ? `${process.env.HOME ?? "~"}${path.slice(1)}` : path;
|
|
65801
65868
|
//#endregion
|
|
65802
65869
|
//#region ../../node_modules/.pnpm/cli-spinners@3.4.0/node_modules/cli-spinners/spinners.json
|
|
65803
65870
|
var spinners_default = {
|
|
@@ -67460,6 +67527,318 @@ var spinners_default = {
|
|
|
67460
67527
|
var cli_spinners_default = spinners_default;
|
|
67461
67528
|
Object.keys(spinners_default);
|
|
67462
67529
|
//#endregion
|
|
67530
|
+
//#region src/ledger.ts
|
|
67531
|
+
const Note = Struct({
|
|
67532
|
+
kind: Literals$2([
|
|
67533
|
+
"ask",
|
|
67534
|
+
"answer",
|
|
67535
|
+
"info"
|
|
67536
|
+
]),
|
|
67537
|
+
text: String$6
|
|
67538
|
+
});
|
|
67539
|
+
const Item = Struct({
|
|
67540
|
+
name: String$6,
|
|
67541
|
+
kind: Literals$2([
|
|
67542
|
+
"range",
|
|
67543
|
+
"record",
|
|
67544
|
+
"config"
|
|
67545
|
+
]),
|
|
67546
|
+
from: optionalKey(String$6),
|
|
67547
|
+
to: optionalKey(String$6),
|
|
67548
|
+
reason: optionalKey(String$6),
|
|
67549
|
+
changes: Boolean$4,
|
|
67550
|
+
status: Literals$2([
|
|
67551
|
+
"idle",
|
|
67552
|
+
"running",
|
|
67553
|
+
"ok",
|
|
67554
|
+
"failed",
|
|
67555
|
+
"skipped"
|
|
67556
|
+
]),
|
|
67557
|
+
doing: optionalKey(String$6),
|
|
67558
|
+
detail: optionalKey(String$6),
|
|
67559
|
+
asked: optionalKey(Boolean$4),
|
|
67560
|
+
output: optionalKey(ArraySchema(String$6)),
|
|
67561
|
+
steps: ArraySchema(Struct({
|
|
67562
|
+
id: String$6,
|
|
67563
|
+
doing: String$6
|
|
67564
|
+
})),
|
|
67565
|
+
started: optionalKey(Number$2),
|
|
67566
|
+
ended: optionalKey(Number$2)
|
|
67567
|
+
});
|
|
67568
|
+
const MachineEntry = Struct({
|
|
67569
|
+
name: String$6,
|
|
67570
|
+
status: Literals$2([
|
|
67571
|
+
"checking",
|
|
67572
|
+
"waiting",
|
|
67573
|
+
"planned",
|
|
67574
|
+
"running",
|
|
67575
|
+
"ok",
|
|
67576
|
+
"skipped",
|
|
67577
|
+
"failed"
|
|
67578
|
+
]),
|
|
67579
|
+
detail: optionalKey(String$6),
|
|
67580
|
+
output: optionalKey(ArraySchema(String$6)),
|
|
67581
|
+
items: ArraySchema(Item),
|
|
67582
|
+
connected: optionalKey(Boolean$4),
|
|
67583
|
+
checked: optionalKey(Number$2),
|
|
67584
|
+
started: optionalKey(Number$2),
|
|
67585
|
+
ended: optionalKey(Number$2)
|
|
67586
|
+
});
|
|
67587
|
+
const LedgerState = Struct({
|
|
67588
|
+
from: String$6,
|
|
67589
|
+
dryRun: Boolean$4,
|
|
67590
|
+
notes: ArraySchema(Note),
|
|
67591
|
+
machines: ArraySchema(MachineEntry),
|
|
67592
|
+
resolving: optionalKey(Struct({
|
|
67593
|
+
started: Number$2,
|
|
67594
|
+
items: Number$2
|
|
67595
|
+
})),
|
|
67596
|
+
started: Number$2,
|
|
67597
|
+
ended: optionalKey(Number$2)
|
|
67598
|
+
});
|
|
67599
|
+
const itemsOf = (p, config) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
67600
|
+
const base = {
|
|
67601
|
+
name: address(pp.name, d.item),
|
|
67602
|
+
kind: pp.rule,
|
|
67603
|
+
steps: pp.steps.filter((step) => step.items.includes(d.item)).map(({ id, doing }) => ({
|
|
67604
|
+
id,
|
|
67605
|
+
doing
|
|
67606
|
+
}))
|
|
67607
|
+
};
|
|
67608
|
+
switch (d.kind) {
|
|
67609
|
+
case "change": return {
|
|
67610
|
+
...base,
|
|
67611
|
+
...d.from === void 0 ? {} : { from: d.from },
|
|
67612
|
+
to: d.to,
|
|
67613
|
+
...d.reason === void 0 ? {} : { reason: d.reason },
|
|
67614
|
+
changes: true,
|
|
67615
|
+
status: "idle"
|
|
67616
|
+
};
|
|
67617
|
+
case "leave": return {
|
|
67618
|
+
...base,
|
|
67619
|
+
...d.at === void 0 ? {} : { from: d.at },
|
|
67620
|
+
changes: false,
|
|
67621
|
+
status: "idle"
|
|
67622
|
+
};
|
|
67623
|
+
case "skip":
|
|
67624
|
+
case "fail": return {
|
|
67625
|
+
...base,
|
|
67626
|
+
...d.at === void 0 ? {} : { from: d.at },
|
|
67627
|
+
changes: d.kind === "fail",
|
|
67628
|
+
status: d.kind === "fail" ? "failed" : "skipped",
|
|
67629
|
+
detail: d.reason
|
|
67630
|
+
};
|
|
67631
|
+
case "ask": return {
|
|
67632
|
+
...base,
|
|
67633
|
+
...d.at === void 0 ? {} : { from: d.at },
|
|
67634
|
+
changes: false,
|
|
67635
|
+
status: "skipped",
|
|
67636
|
+
detail: d.question.problem,
|
|
67637
|
+
asked: true
|
|
67638
|
+
};
|
|
67639
|
+
}
|
|
67640
|
+
})), (() => {
|
|
67641
|
+
const { asked, ...change } = config;
|
|
67642
|
+
return {
|
|
67643
|
+
name: CONFIG_STEP,
|
|
67644
|
+
kind: "config",
|
|
67645
|
+
...change,
|
|
67646
|
+
...asked === void 0 ? {
|
|
67647
|
+
changes: change.from !== change.to,
|
|
67648
|
+
status: "idle"
|
|
67649
|
+
} : {
|
|
67650
|
+
changes: false,
|
|
67651
|
+
status: "skipped",
|
|
67652
|
+
asked: true,
|
|
67653
|
+
detail: asked
|
|
67654
|
+
},
|
|
67655
|
+
steps: [{
|
|
67656
|
+
id: CONFIG_STEP,
|
|
67657
|
+
doing: "copying"
|
|
67658
|
+
}]
|
|
67659
|
+
};
|
|
67660
|
+
})()];
|
|
67661
|
+
const outcomeOf = (m, dryRun) => m.status === "skipped" ? "skipped" : m.status === "failed" || m.items.some((i) => i.status === "failed") ? "failed" : m.status === "ok" || m.status === "planned" && dryRun ? "ok" : void 0;
|
|
67662
|
+
const anyFailed = (state) => state.machines.some((m) => outcomeOf(m, state.dryRun) === "failed");
|
|
67663
|
+
const makeLedger = fn("makeLedger")(function* (input) {
|
|
67664
|
+
const started = yield* currentTimeMillis;
|
|
67665
|
+
let snapshot = {
|
|
67666
|
+
state: {
|
|
67667
|
+
from: input.from,
|
|
67668
|
+
dryRun: input.dryRun,
|
|
67669
|
+
notes: [],
|
|
67670
|
+
machines: input.machines.map((name) => ({
|
|
67671
|
+
name,
|
|
67672
|
+
status: "checking",
|
|
67673
|
+
items: []
|
|
67674
|
+
})),
|
|
67675
|
+
started
|
|
67676
|
+
},
|
|
67677
|
+
now: started
|
|
67678
|
+
};
|
|
67679
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
67680
|
+
const update = (change) => gen(function* () {
|
|
67681
|
+
const now = yield* currentTimeMillis;
|
|
67682
|
+
snapshot = {
|
|
67683
|
+
state: change(snapshot.state, now),
|
|
67684
|
+
now
|
|
67685
|
+
};
|
|
67686
|
+
for (const listener of listeners) listener();
|
|
67687
|
+
});
|
|
67688
|
+
const machine = (name, change) => update((state, now) => ({
|
|
67689
|
+
...state,
|
|
67690
|
+
machines: state.machines.map((m) => m.name === name ? change(m, now) : m)
|
|
67691
|
+
}));
|
|
67692
|
+
const items = (name, id, change) => machine(name, (m, now) => ({
|
|
67693
|
+
...m,
|
|
67694
|
+
items: m.items.map((i) => i.steps.some((step) => step.id === id) ? change(i, now) : i)
|
|
67695
|
+
}));
|
|
67696
|
+
const store = {
|
|
67697
|
+
get: () => snapshot,
|
|
67698
|
+
subscribe: (listener) => {
|
|
67699
|
+
listeners.add(listener);
|
|
67700
|
+
return () => listeners.delete(listener);
|
|
67701
|
+
},
|
|
67702
|
+
tick: update((state) => state)
|
|
67703
|
+
};
|
|
67704
|
+
const log = (name, entry) => annotateLogs(entry, { machine: name });
|
|
67705
|
+
return {
|
|
67706
|
+
ledger: {
|
|
67707
|
+
note: fn("ledger.note")(function* (kind, text) {
|
|
67708
|
+
yield* update((state) => ({
|
|
67709
|
+
...state,
|
|
67710
|
+
notes: [...state.notes, {
|
|
67711
|
+
kind,
|
|
67712
|
+
text
|
|
67713
|
+
}]
|
|
67714
|
+
}));
|
|
67715
|
+
yield* input.print({
|
|
67716
|
+
kind,
|
|
67717
|
+
text
|
|
67718
|
+
});
|
|
67719
|
+
yield* logInfo(`${kind}: ${text}`);
|
|
67720
|
+
}),
|
|
67721
|
+
connected: (name) => machine(name, (m) => ({
|
|
67722
|
+
...m,
|
|
67723
|
+
connected: true
|
|
67724
|
+
})).pipe(andThen(log(name, logInfo("connected")))),
|
|
67725
|
+
checked: (name) => machine(name, (m, now) => ({
|
|
67726
|
+
...m,
|
|
67727
|
+
status: "waiting",
|
|
67728
|
+
checked: now
|
|
67729
|
+
})).pipe(andThen(log(name, logInfo("checked")))),
|
|
67730
|
+
skip: (name, reason) => machine(name, (m) => ({
|
|
67731
|
+
...m,
|
|
67732
|
+
status: "skipped",
|
|
67733
|
+
detail: reason
|
|
67734
|
+
})).pipe(andThen(log(name, logWarning(`skipped: ${reason}`)))),
|
|
67735
|
+
fail: (name, [reason = "failed", ...output]) => machine(name, (m, now) => ({
|
|
67736
|
+
...m,
|
|
67737
|
+
status: "failed",
|
|
67738
|
+
detail: reason,
|
|
67739
|
+
...output.length === 0 ? {} : { output },
|
|
67740
|
+
ended: now
|
|
67741
|
+
})).pipe(andThen(log(name, logError(`failed: ${reason}`)))),
|
|
67742
|
+
resolving: (n) => update((state, now) => ({
|
|
67743
|
+
...state,
|
|
67744
|
+
resolving: {
|
|
67745
|
+
started: now,
|
|
67746
|
+
items: n
|
|
67747
|
+
}
|
|
67748
|
+
})).pipe(andThen(logInfo(`resolving ${n} items`))),
|
|
67749
|
+
resolved: update(({ resolving: _, ...state }) => state).pipe(andThen(logInfo("resolved"))),
|
|
67750
|
+
plan: (name, p, config) => machine(name, (m) => ({
|
|
67751
|
+
...m,
|
|
67752
|
+
status: "planned",
|
|
67753
|
+
items: itemsOf(p, config)
|
|
67754
|
+
})).pipe(andThen(forEach$1(p.providers.flatMap((pp) => pp.decisions.map((d) => ({
|
|
67755
|
+
pp,
|
|
67756
|
+
d
|
|
67757
|
+
}))), ({ pp, d }) => log(name, logInfo(`plan ${address(pp.name, d.item)}:`, d)), { discard: true }))),
|
|
67758
|
+
start: (name) => machine(name, (m, now) => ({
|
|
67759
|
+
...m,
|
|
67760
|
+
status: "running",
|
|
67761
|
+
connected: false,
|
|
67762
|
+
started: now
|
|
67763
|
+
})).pipe(andThen(log(name, logInfo("applying")))),
|
|
67764
|
+
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" || item.status === "skipped" ? item : {
|
|
67765
|
+
...item,
|
|
67766
|
+
status: "running",
|
|
67767
|
+
doing: item.steps.find((step) => step.id === id)?.doing ?? "running",
|
|
67768
|
+
started: item.started ?? now
|
|
67769
|
+
}).pipe(andThen(log(name, logInfo(`step ${id}`)))),
|
|
67770
|
+
stepDone: (name, id, result) => items(name, id, (item, now) => {
|
|
67771
|
+
if (item.status === "failed" || item.status === "skipped") return item;
|
|
67772
|
+
if (!result.ok) return {
|
|
67773
|
+
...item,
|
|
67774
|
+
changes: true,
|
|
67775
|
+
status: "failed",
|
|
67776
|
+
detail: result.detail,
|
|
67777
|
+
...result.output.length === 0 ? {} : { output: result.output },
|
|
67778
|
+
ended: now
|
|
67779
|
+
};
|
|
67780
|
+
if (item.steps.at(-1)?.id !== id) return item;
|
|
67781
|
+
const { doing: _, ...rest } = item;
|
|
67782
|
+
return {
|
|
67783
|
+
...rest,
|
|
67784
|
+
status: "ok",
|
|
67785
|
+
ended: now
|
|
67786
|
+
};
|
|
67787
|
+
}).pipe(andThen(log(name, result.ok ? logInfo(`step ${id}: done`) : logWarning(`step ${id}: ${result.detail}`)))),
|
|
67788
|
+
stepSkipped: (name, id, reason) => items(name, id, (item, now) => {
|
|
67789
|
+
if (item.status === "failed") return item;
|
|
67790
|
+
const { doing: _, ...rest } = item;
|
|
67791
|
+
return {
|
|
67792
|
+
...rest,
|
|
67793
|
+
status: "skipped",
|
|
67794
|
+
detail: reason,
|
|
67795
|
+
ended: now
|
|
67796
|
+
};
|
|
67797
|
+
}).pipe(andThen(log(name, logInfo(`step ${id}: skipped, ${reason}`)))),
|
|
67798
|
+
reached: (name, item, at) => machine(name, (m) => ({
|
|
67799
|
+
...m,
|
|
67800
|
+
items: m.items.map((i) => i.name !== item ? i : {
|
|
67801
|
+
...i,
|
|
67802
|
+
to: at,
|
|
67803
|
+
changes: i.status === "failed" || i.from !== at
|
|
67804
|
+
})
|
|
67805
|
+
})).pipe(andThen(log(name, logInfo(`${item} reached ${at}`)))),
|
|
67806
|
+
finish: (name, outcome) => machine(name, (m, now) => ({
|
|
67807
|
+
...m,
|
|
67808
|
+
status: m.status === "failed" ? "failed" : outcome,
|
|
67809
|
+
ended: m.ended ?? now
|
|
67810
|
+
})).pipe(andThen(log(name, logInfo(`finished: ${outcome}`)))),
|
|
67811
|
+
end: gen(function* () {
|
|
67812
|
+
yield* update(({ resolving: _, ...state }, now) => ({
|
|
67813
|
+
...state,
|
|
67814
|
+
ended: now,
|
|
67815
|
+
machines: state.machines.map((m) => m.status === "running" || m.status === "checking" ? {
|
|
67816
|
+
...m,
|
|
67817
|
+
status: "failed",
|
|
67818
|
+
detail: "stopped",
|
|
67819
|
+
ended: now,
|
|
67820
|
+
items: m.items.map((i) => i.status === "running" ? {
|
|
67821
|
+
...i,
|
|
67822
|
+
status: "failed",
|
|
67823
|
+
detail: "stopped",
|
|
67824
|
+
ended: now
|
|
67825
|
+
} : i)
|
|
67826
|
+
} : m)
|
|
67827
|
+
}));
|
|
67828
|
+
return snapshot.state;
|
|
67829
|
+
})
|
|
67830
|
+
},
|
|
67831
|
+
store
|
|
67832
|
+
};
|
|
67833
|
+
});
|
|
67834
|
+
const duration = (ms) => {
|
|
67835
|
+
const s = ms / 1e3;
|
|
67836
|
+
if (s < 60) return `${s.toFixed(1)}s`;
|
|
67837
|
+
const m = Math.floor(s / 60);
|
|
67838
|
+
if (m < 60) return `${m}m ${String(Math.floor(s % 60)).padStart(2, "0")}s`;
|
|
67839
|
+
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
67840
|
+
};
|
|
67841
|
+
//#endregion
|
|
67463
67842
|
//#region ../../node_modules/.pnpm/react@19.3.0/node_modules/react/cjs/react-jsx-runtime.production.js
|
|
67464
67843
|
/**
|
|
67465
67844
|
* @license React
|
|
@@ -67763,7 +68142,7 @@ const Cell = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
|
67763
68142
|
})
|
|
67764
68143
|
})
|
|
67765
68144
|
});
|
|
67766
|
-
const value = (i) => i.to === void 0 ? i.from ?? "" : i.from === void 0 ? `+ ${i.to}` : i.from === i.to ? i.to : `${i.from} → ${i.to}`;
|
|
68145
|
+
const value$1 = (i) => i.to === void 0 ? i.from ?? "" : i.from === void 0 ? `+ ${i.to}` : i.from === i.to ? i.to : `${i.from} → ${i.to}`;
|
|
67767
68146
|
const SPINNER = cli_spinners_default.dots;
|
|
67768
68147
|
const runGlyph = (live, now) => (live ? SPINNER.frames[Math.floor(now / SPINNER.interval) % SPINNER.frames.length] : void 0) ?? "▸";
|
|
67769
68148
|
const UNCHANGED = {
|
|
@@ -67829,7 +68208,7 @@ const upToDate = (m, dryRun) => {
|
|
|
67829
68208
|
const c = countsOf(m);
|
|
67830
68209
|
return c.changes === 0 && c.skipped === 0 && settled(m, dryRun);
|
|
67831
68210
|
};
|
|
67832
|
-
const summaryOf = (m, dryRun, glyph) => {
|
|
68211
|
+
const summaryOf$1 = (m, dryRun, glyph) => {
|
|
67833
68212
|
if (m.status === "checking") return [{
|
|
67834
68213
|
text: `${glyph} ${m.connected === true ? "checking" : "connecting"}`,
|
|
67835
68214
|
tone: "run"
|
|
@@ -67955,7 +68334,7 @@ const MachineLine = (props) => {
|
|
|
67955
68334
|
tone: "dim",
|
|
67956
68335
|
children: upToDate(m, props.dryRun) ? "" : machineTime(m, props.shipStarted, props.now)
|
|
67957
68336
|
}),
|
|
67958
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: summaryOf(m, props.dryRun, props.glyph) })
|
|
68337
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: summaryOf$1(m, props.dryRun, props.glyph) })
|
|
67959
68338
|
]
|
|
67960
68339
|
});
|
|
67961
68340
|
};
|
|
@@ -67979,7 +68358,7 @@ const ItemLine = (props) => {
|
|
|
67979
68358
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
67980
68359
|
width: w.value,
|
|
67981
68360
|
tone: quiet,
|
|
67982
|
-
children: value(i)
|
|
68361
|
+
children: value$1(i)
|
|
67983
68362
|
}),
|
|
67984
68363
|
w.time > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Cell, {
|
|
67985
68364
|
width: w.time,
|
|
@@ -68018,7 +68397,7 @@ const LedgerView = (props) => {
|
|
|
68018
68397
|
const w = {
|
|
68019
68398
|
status,
|
|
68020
68399
|
item,
|
|
68021
|
-
value: Math.max(width(visible.map(value)), width(state.machines.map((m) => m.name)) - (status + GAP + INDENT + item + GAP)),
|
|
68400
|
+
value: Math.max(width(visible.map(value$1)), width(state.machines.map((m) => m.name)) - (status + GAP + INDENT + item + GAP)),
|
|
68022
68401
|
time: width([...visible.map((i) => elapsed(i.started, i.ended, now)), ...state.machines.map((m) => machineTime(m, state.started, now))])
|
|
68023
68402
|
};
|
|
68024
68403
|
const blocks = state.machines.map((m) => ({
|
|
@@ -68158,44 +68537,928 @@ const hintText = (text, color) => toText(/* @__PURE__ */ (0, import_jsx_runtime.
|
|
|
68158
68537
|
children: text
|
|
68159
68538
|
}), color);
|
|
68160
68539
|
//#endregion
|
|
68161
|
-
//#region src/
|
|
68162
|
-
const
|
|
68163
|
-
|
|
68164
|
-
|
|
68165
|
-
|
|
68166
|
-
|
|
68540
|
+
//#region src/configView.tsx
|
|
68541
|
+
const SHOWN = [
|
|
68542
|
+
"vp",
|
|
68543
|
+
"brew",
|
|
68544
|
+
"cask",
|
|
68545
|
+
"repos",
|
|
68546
|
+
"files"
|
|
68547
|
+
];
|
|
68548
|
+
const whereOf = (targeting) => {
|
|
68549
|
+
const parts = [...targeting.platforms ?? [], ...targeting.machines ?? []];
|
|
68550
|
+
return parts.length === 0 ? void 0 : parts.join(", ");
|
|
68551
|
+
};
|
|
68552
|
+
const rowsOf$1 = (items, wants) => Object.entries(items ?? {}).flatMap(([name, entry]) => {
|
|
68553
|
+
const one = (e, variant) => {
|
|
68554
|
+
const where = whereOf(e);
|
|
68555
|
+
return {
|
|
68556
|
+
name,
|
|
68557
|
+
wants: wants(e),
|
|
68558
|
+
...variant === void 0 ? {} : { variant },
|
|
68559
|
+
...where === void 0 ? {} : { where }
|
|
68560
|
+
};
|
|
68561
|
+
};
|
|
68562
|
+
return Array.isArray(entry) ? entry.map((e, i) => one(e, i)) : [one(entry)];
|
|
68563
|
+
});
|
|
68564
|
+
const sectionsOf = (config) => SHOWN.flatMap((provider) => {
|
|
68565
|
+
const section = config[provider];
|
|
68566
|
+
if (section === void 0) return [];
|
|
68567
|
+
const where = whereOf(section);
|
|
68568
|
+
const rows = (() => {
|
|
68569
|
+
switch (provider) {
|
|
68570
|
+
case "vp": {
|
|
68571
|
+
const vp = config.vp ?? {};
|
|
68572
|
+
return [...[
|
|
68573
|
+
["vp", vp.version],
|
|
68574
|
+
["node", vp.node],
|
|
68575
|
+
...PACKAGE_MANAGERS.map((pm) => [pm, vp[pm]])
|
|
68576
|
+
].flatMap(([name, range]) => range === void 0 ? [] : [{
|
|
68577
|
+
name,
|
|
68578
|
+
wants: range
|
|
68579
|
+
}]), ...rowsOf$1(vp.items, (g) => g.range)];
|
|
68580
|
+
}
|
|
68581
|
+
case "brew": return rowsOf$1(config.brew?.items, (f) => f.range);
|
|
68582
|
+
case "cask": return rowsOf$1(config.cask?.items, (c) => c.range);
|
|
68583
|
+
case "repos": return rowsOf$1(config.repos?.items, (r) => isPin(r.ref) ? r.ref.slice(0, 7) : r.ref);
|
|
68584
|
+
case "files": return rowsOf$1(config.files?.items, (f) => f.path);
|
|
68585
|
+
}
|
|
68586
|
+
})();
|
|
68587
|
+
return [{
|
|
68588
|
+
provider,
|
|
68589
|
+
...where === void 0 ? {} : { where },
|
|
68590
|
+
rows
|
|
68591
|
+
}];
|
|
68592
|
+
});
|
|
68593
|
+
const WANTS_WIDTH = 32;
|
|
68594
|
+
const shorten = (text, width) => text.length <= width ? text : `${text.slice(0, Math.ceil((width - 1) / 2))}…${text.slice(text.length - Math.floor((width - 1) / 2))}`;
|
|
68595
|
+
const pad = (text, width) => text.padEnd(width);
|
|
68596
|
+
const Sections = (props) => {
|
|
68597
|
+
const rows = props.sections.flatMap((s) => s.rows);
|
|
68598
|
+
const variants = rows.some((r) => r.variant !== void 0);
|
|
68599
|
+
const nameWidth = Math.max(0, ...rows.map((r) => r.name.length)) + 2;
|
|
68600
|
+
const wantsWidth = Math.min(WANTS_WIDTH, Math.max(0, ...rows.map((r) => r.wants.length))) + (variants ? 2 : 0) + 2;
|
|
68601
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: props.sections.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
68602
|
+
flexDirection: "column",
|
|
68603
|
+
marginTop: 1,
|
|
68604
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68605
|
+
tone: "bold",
|
|
68606
|
+
children: pad(s.provider, 2 + nameWidth + wantsWidth)
|
|
68607
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68608
|
+
tone: "skip",
|
|
68609
|
+
children: s.where ?? ""
|
|
68610
|
+
})] }), s.rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68611
|
+
tone: "dim",
|
|
68612
|
+
children: " no Items"
|
|
68613
|
+
}) }) : s.rows.map((r, i) => {
|
|
68614
|
+
const repeated = r.variant !== void 0 && r.variant > 0;
|
|
68615
|
+
const index = r.variant === void 0 ? variants ? " " : "" : `${r.variant} `;
|
|
68616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
68617
|
+
` ${pad(repeated ? "" : r.name, nameWidth)}`,
|
|
68618
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68619
|
+
tone: "ask",
|
|
68620
|
+
children: index
|
|
68621
|
+
}),
|
|
68622
|
+
pad(shorten(r.wants, WANTS_WIDTH), wantsWidth - (variants ? 2 : 0)),
|
|
68623
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68624
|
+
tone: "skip",
|
|
68625
|
+
children: r.where ?? ""
|
|
68626
|
+
})
|
|
68627
|
+
] }, i);
|
|
68628
|
+
})]
|
|
68629
|
+
}, s.provider)) });
|
|
68630
|
+
};
|
|
68631
|
+
const Machines = (props) => {
|
|
68632
|
+
const width = Math.max(...props.config.machines.map((m) => m.length)) + 2;
|
|
68633
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
68634
|
+
flexDirection: "column",
|
|
68635
|
+
marginTop: 1,
|
|
68636
|
+
children: props.config.machines.map((name, i) => {
|
|
68637
|
+
const machine = props.tailnet.fleet.find((m) => m.name === name);
|
|
68638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
68639
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68640
|
+
tone: "bold",
|
|
68641
|
+
children: i === 0 ? "machines " : " "
|
|
68642
|
+
}),
|
|
68643
|
+
pad(name, width),
|
|
68644
|
+
machine === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68645
|
+
tone: "fail",
|
|
68646
|
+
children: "not on the tailnet"
|
|
68647
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [machine.platform, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68648
|
+
tone: "dim",
|
|
68649
|
+
children: [name === props.tailnet.self.name ? " this Machine" : "", machine.online ? "" : " offline"].join("")
|
|
68650
|
+
})] })
|
|
68651
|
+
] }, name);
|
|
68652
|
+
})
|
|
68653
|
+
});
|
|
68654
|
+
};
|
|
68655
|
+
const ago = (ms) => {
|
|
68656
|
+
const minutes = Math.floor(ms / 6e4);
|
|
68657
|
+
if (minutes < 1) return "just now";
|
|
68658
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
68659
|
+
const hours = Math.floor(minutes / 60);
|
|
68660
|
+
return hours < 24 ? `${hours}h ago` : `${Math.floor(hours / 24)}d ago`;
|
|
68661
|
+
};
|
|
68662
|
+
const shippedLine = (record, machines, sha, now) => {
|
|
68663
|
+
const times = machines.map((m) => {
|
|
68664
|
+
const at = record?.reached[m];
|
|
68665
|
+
return {
|
|
68666
|
+
m,
|
|
68667
|
+
at: at === void 0 ? void 0 : Date.parse(at)
|
|
68668
|
+
};
|
|
68669
|
+
});
|
|
68670
|
+
const last = Math.max(...times.flatMap((t) => t.at === void 0 ? [] : [t.at]));
|
|
68671
|
+
if (record === void 0 || !Number.isFinite(last)) return "No Ship has reached it yet.";
|
|
68672
|
+
const missed = times.filter((t) => t.at !== last);
|
|
68673
|
+
const reached = machines.length - missed.length;
|
|
68674
|
+
const others = missed.map((t) => `${t.m} ${t.at === void 0 ? "never" : ago(now - t.at)}`).join(", ");
|
|
68675
|
+
return [`Last Ship ${ago(now - last)} reached ${reached} of ${machines.length} Machine${machines.length === 1 ? "" : "s"}${others === "" ? "" : `; ${others}`}.`, ...record.sha === sha ? [] : ["Edited since."]].join(" ");
|
|
68676
|
+
};
|
|
68677
|
+
const configText = (input) => toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
68678
|
+
flexDirection: "column",
|
|
68679
|
+
children: [
|
|
68680
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68681
|
+
tone: "dim",
|
|
68682
|
+
children: homeRelative(input.path)
|
|
68683
|
+
}),
|
|
68684
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Machines, {
|
|
68685
|
+
config: input.config,
|
|
68686
|
+
tailnet: input.tailnet
|
|
68687
|
+
}),
|
|
68688
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sections, { sections: sectionsOf(input.config) }),
|
|
68689
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
68690
|
+
flexDirection: "column",
|
|
68691
|
+
marginTop: 1,
|
|
68692
|
+
children: input.footer.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68693
|
+
tone: "dim",
|
|
68694
|
+
children: line
|
|
68695
|
+
}, i))
|
|
68696
|
+
})
|
|
68697
|
+
]
|
|
68698
|
+
}));
|
|
68699
|
+
const showConfig = fn("showConfig")(function* (flags) {
|
|
68700
|
+
const path = flags.config ?? configPath(process.env);
|
|
68167
68701
|
const fs = yield* FileSystem;
|
|
68168
|
-
const
|
|
68169
|
-
yield*
|
|
68170
|
-
|
|
68702
|
+
const config = yield* loadConfig(path);
|
|
68703
|
+
const tailnet = yield* readTailnet();
|
|
68704
|
+
const record = flags.config === void 0 ? yield* readConfigRecord : void 0;
|
|
68705
|
+
const sha = yield* shaOf(yield* fs.readFileString(path));
|
|
68706
|
+
const now$3 = toEpochMillis(yield* now);
|
|
68707
|
+
yield* log(configText({
|
|
68708
|
+
path,
|
|
68709
|
+
config,
|
|
68710
|
+
tailnet,
|
|
68711
|
+
footer: [shippedLine(record, config.machines, sha, now$3), "fizz status for where each Machine stands."]
|
|
68712
|
+
}));
|
|
68171
68713
|
});
|
|
68172
|
-
const
|
|
68714
|
+
const changedLines = (before, after, context = 3) => {
|
|
68715
|
+
const a = before.split("\n");
|
|
68716
|
+
const b = after.split("\n");
|
|
68717
|
+
let start = 0;
|
|
68718
|
+
while (start < a.length && start < b.length && a[start] === b[start]) start++;
|
|
68719
|
+
let end = 0;
|
|
68720
|
+
while (end < a.length - start && end < b.length - start && a[a.length - 1 - end] === b[b.length - 1 - end]) end++;
|
|
68721
|
+
const line = (sign) => (text) => ({
|
|
68722
|
+
sign,
|
|
68723
|
+
text
|
|
68724
|
+
});
|
|
68725
|
+
return [
|
|
68726
|
+
...a.slice(Math.max(0, start - context), start).map(line(" ")),
|
|
68727
|
+
...a.slice(start, a.length - end).map(line("-")),
|
|
68728
|
+
...b.slice(start, b.length - end).map(line("+")),
|
|
68729
|
+
...b.slice(b.length - end, b.length - end + 1).map(line(" "))
|
|
68730
|
+
];
|
|
68731
|
+
};
|
|
68732
|
+
const changeText = (path, before, after, summary) => toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
68733
|
+
flexDirection: "column",
|
|
68734
|
+
children: [
|
|
68735
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68736
|
+
tone: "dim",
|
|
68737
|
+
children: homeRelative(path)
|
|
68738
|
+
}),
|
|
68739
|
+
changedLines(before, after).map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68740
|
+
tone: line.sign === "+" ? "ok" : line.sign === "-" ? "fail" : "dim",
|
|
68741
|
+
children: `${line.sign} ${line.text}`
|
|
68742
|
+
}, i)),
|
|
68743
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: summary })
|
|
68744
|
+
]
|
|
68745
|
+
}));
|
|
68746
|
+
//#endregion
|
|
68747
|
+
//#region src/configAdd.tsx
|
|
68748
|
+
var ConfigAddError = class extends TaggedError()("ConfigAddError", { reason: String$6 }) {
|
|
68749
|
+
get message() {
|
|
68750
|
+
return this.reason;
|
|
68751
|
+
}
|
|
68752
|
+
};
|
|
68753
|
+
const targetingOf = (flags) => ({
|
|
68754
|
+
...flags.platforms.length > 0 ? { platforms: [...new Set(flags.platforms)] } : {},
|
|
68755
|
+
...flags.machines.length > 0 ? { machines: [...new Set(flags.machines)] } : {}
|
|
68756
|
+
});
|
|
68757
|
+
const untargeted = (targeting) => targeting.platforms === void 0 && targeting.machines === void 0;
|
|
68758
|
+
const rangeEntry = (range, targeting) => untargeted(targeting) ? range : {
|
|
68759
|
+
range,
|
|
68760
|
+
...targeting
|
|
68761
|
+
};
|
|
68762
|
+
const fileEntry = (path, exclude, targeting) => untargeted(targeting) && exclude.length === 0 ? path : {
|
|
68763
|
+
path,
|
|
68764
|
+
...exclude.length > 0 ? { exclude } : {},
|
|
68765
|
+
...targeting
|
|
68766
|
+
};
|
|
68767
|
+
const parseSpec = (spec) => {
|
|
68768
|
+
const at = spec.lastIndexOf("@");
|
|
68769
|
+
return at > 0 ? {
|
|
68770
|
+
name: spec.slice(0, at),
|
|
68771
|
+
range: spec.slice(at + 1)
|
|
68772
|
+
} : { name: spec };
|
|
68773
|
+
};
|
|
68774
|
+
const fileName = (path) => (path.replace(/\/+$/, "").split("/").at(-1) ?? "").replace(/^\.+/, "").replace(/[^A-Za-z0-9._-]/g, "-");
|
|
68775
|
+
const openConfig = fn("openConfig")(function* (flag) {
|
|
68776
|
+
const path = flag ?? configPath(process.env);
|
|
68173
68777
|
const fs = yield* FileSystem;
|
|
68174
|
-
|
|
68175
|
-
|
|
68176
|
-
|
|
68177
|
-
|
|
68178
|
-
|
|
68179
|
-
|
|
68180
|
-
|
|
68181
|
-
|
|
68778
|
+
if (!(yield* fs.exists(path))) return yield* new ConfigAddError({ reason: `no config at ${homeRelative(path)}; run fizz init to write one, or pass --config` });
|
|
68779
|
+
const text = yield* fs.readFileString(path);
|
|
68780
|
+
return {
|
|
68781
|
+
path,
|
|
68782
|
+
text,
|
|
68783
|
+
config: yield* parseConfig(path, text)
|
|
68784
|
+
};
|
|
68785
|
+
});
|
|
68786
|
+
const reach = (config, tailnet, provider, own) => {
|
|
68787
|
+
const section = config[provider] ?? {};
|
|
68788
|
+
const names = config.machines.filter((name) => {
|
|
68789
|
+
const machine = tailnet.fleet.find((m) => m.name === name);
|
|
68790
|
+
return machine !== void 0 && targets(section, machine) && targets(own, machine);
|
|
68182
68791
|
});
|
|
68183
|
-
|
|
68184
|
-
|
|
68185
|
-
|
|
68186
|
-
|
|
68187
|
-
|
|
68188
|
-
|
|
68189
|
-
|
|
68190
|
-
|
|
68191
|
-
|
|
68192
|
-
|
|
68193
|
-
|
|
68194
|
-
|
|
68195
|
-
|
|
68196
|
-
|
|
68197
|
-
|
|
68792
|
+
return names.length === config.machines.length ? "every Machine" : names.length === 0 ? void 0 : names.join(", ");
|
|
68793
|
+
};
|
|
68794
|
+
const addItem = fn("addItem")(function* (input) {
|
|
68795
|
+
const { path, text, config } = yield* openConfig(input.config);
|
|
68796
|
+
const where = address(input.provider, input.name);
|
|
68797
|
+
if (config[input.provider]?.items?.[input.name] !== void 0) return yield* new ConfigAddError({ reason: `${where} is already in the config; change it with fizz config edit` });
|
|
68798
|
+
const unknown = (input.targeting.machines ?? []).filter((m) => !config.machines.includes(m));
|
|
68799
|
+
if (unknown.length > 0) return yield* new ConfigAddError({ reason: `not in machines: ${unknown.join(", ")} (add it with fizz config add machines)` });
|
|
68800
|
+
const tailnet = yield* readTailnet();
|
|
68801
|
+
const linux = config.machines.some((m) => tailnet.fleet.find((f) => f.name === m)?.platform === "linux");
|
|
68802
|
+
const start = input.provider === "cask" && linux && config.cask?.platforms === void 0 ? insertAt(text, ["cask", "platforms"], ["darwin"]) : { text };
|
|
68803
|
+
if ("reason" in start) return yield* new ConfigAddError({ reason: start.reason });
|
|
68804
|
+
const at = [
|
|
68805
|
+
input.provider,
|
|
68806
|
+
"items",
|
|
68807
|
+
input.name
|
|
68808
|
+
];
|
|
68809
|
+
const result = insertAt(start.text, at, input.entry);
|
|
68810
|
+
if ("reason" in result) return yield* new ConfigAddError({ reason: result.reason });
|
|
68811
|
+
const saved = yield* saveConfig(path, result.text);
|
|
68812
|
+
const to = reach(saved, tailnet, input.provider, input.targeting);
|
|
68813
|
+
yield* log(changeText(path, text, result.text, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
68814
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68815
|
+
tone: "ok",
|
|
68816
|
+
children: "added"
|
|
68817
|
+
}),
|
|
68818
|
+
` ${where}`,
|
|
68819
|
+
to === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68820
|
+
tone: "skip",
|
|
68821
|
+
children: " · no Machine in the Fleet matches its Targeting yet"
|
|
68822
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [` for ${to}`, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68823
|
+
tone: "dim",
|
|
68824
|
+
children: " · fizz ship to install it"
|
|
68825
|
+
})] })
|
|
68826
|
+
] })));
|
|
68827
|
+
for (const note of input.notes ?? []) yield* log(note);
|
|
68828
|
+
});
|
|
68829
|
+
const needs = (what) => new ConfigAddError({ reason: `${what} is needed (no terminal to ask at)` });
|
|
68830
|
+
const addGlobal = fn("addGlobal")(function* (input) {
|
|
68831
|
+
const { name, range: given } = parseSpec(input.spec);
|
|
68832
|
+
const range = given ?? (yield* gen(function* () {
|
|
68833
|
+
const listed = yield* runLocal("vp", [
|
|
68834
|
+
"list",
|
|
68835
|
+
"--global",
|
|
68836
|
+
"--json"
|
|
68837
|
+
]);
|
|
68838
|
+
const installed = decodeOption(fromJsonString(ArraySchema(Struct({
|
|
68839
|
+
name: String$6,
|
|
68840
|
+
version: String$6
|
|
68841
|
+
}))))(listed.stdout);
|
|
68842
|
+
const here = installed._tag === "Some" ? installed.value.find((g) => g.name === name)?.version : void 0;
|
|
68843
|
+
const published = here === void 0 ? (yield* resolveVersions([name]))[name] ?? [] : [];
|
|
68844
|
+
const range = addedRange(here, published);
|
|
68845
|
+
if (range === void 0) return yield* new ConfigAddError({ reason: `${name} is not installed here and npm has no version of it; check the name, or give a Range (${name}@^1.0.0)` });
|
|
68846
|
+
return range;
|
|
68847
|
+
}));
|
|
68848
|
+
const targeting = targetingOf(input.targeting);
|
|
68849
|
+
yield* addItem({
|
|
68850
|
+
config: input.config,
|
|
68851
|
+
provider: "vp",
|
|
68852
|
+
name,
|
|
68853
|
+
entry: rangeEntry(range, targeting),
|
|
68854
|
+
targeting
|
|
68855
|
+
});
|
|
68856
|
+
});
|
|
68857
|
+
const addHomebrew = fn("addHomebrew")(function* (input) {
|
|
68858
|
+
const noun = input.provider === "brew" ? "Formula" : "Cask";
|
|
68859
|
+
if (input.provider === "cask" && input.targeting.platforms.includes("linux")) return yield* new ConfigAddError({ reason: "a Cask is macOS only; leave out --platform linux" });
|
|
68860
|
+
const notes = [];
|
|
68861
|
+
if (input.name.includes("/")) notes.push(`${input.name} is a tapped name, so it was not checked`);
|
|
68862
|
+
else {
|
|
68863
|
+
const info = yield* runLocal("brew", [
|
|
68864
|
+
"info",
|
|
68865
|
+
input.provider === "brew" ? "--formula" : "--cask",
|
|
68866
|
+
input.name
|
|
68867
|
+
]);
|
|
68868
|
+
if (info.code === 127) notes.push(`Homebrew is not installed here, so ${input.name} was not checked`);
|
|
68869
|
+
else if (info.code !== 0) return yield* new ConfigAddError({ reason: `Homebrew has no ${noun} named ${input.name}` });
|
|
68870
|
+
}
|
|
68871
|
+
const targeting = targetingOf(input.targeting);
|
|
68872
|
+
yield* addItem({
|
|
68873
|
+
config: input.config,
|
|
68874
|
+
provider: input.provider,
|
|
68875
|
+
name: input.name,
|
|
68876
|
+
entry: rangeEntry(input.latest ? "latest" : "*", targeting),
|
|
68877
|
+
targeting,
|
|
68878
|
+
notes
|
|
68879
|
+
});
|
|
68880
|
+
});
|
|
68881
|
+
const defaultBranch = fn("defaultBranch")(function* (origin) {
|
|
68882
|
+
const remote = yield* runLocal("git", [
|
|
68883
|
+
"ls-remote",
|
|
68884
|
+
"--symref",
|
|
68885
|
+
`https://github.com/${origin}`,
|
|
68886
|
+
"HEAD"
|
|
68887
|
+
], { GIT_TERMINAL_PROMPT: "0" });
|
|
68888
|
+
const head = /^ref: refs\/heads\/(\S+)\s+HEAD/m.exec(remote.stdout)?.[1];
|
|
68889
|
+
if (head !== void 0) return head;
|
|
68890
|
+
const gh = yield* runLocal("gh", [
|
|
68891
|
+
"repo",
|
|
68892
|
+
"view",
|
|
68893
|
+
origin,
|
|
68894
|
+
"--json",
|
|
68895
|
+
"defaultBranchRef",
|
|
68896
|
+
"--jq",
|
|
68897
|
+
".defaultBranchRef.name"
|
|
68898
|
+
]);
|
|
68899
|
+
return gh.code === 0 && gh.stdout.trim() !== "" ? gh.stdout.trim() : void 0;
|
|
68900
|
+
});
|
|
68901
|
+
const addRepo = fn("addRepo")(function* (input) {
|
|
68902
|
+
const name = input.name ?? input.origin.split("/").at(-1) ?? input.origin;
|
|
68903
|
+
const ask = interactive();
|
|
68904
|
+
const found = input.ref === void 0 ? yield* defaultBranch(input.origin) : void 0;
|
|
68905
|
+
const ref = input.ref ?? (ask ? yield* String$4({
|
|
68906
|
+
message: "Ref",
|
|
68907
|
+
...found === void 0 ? {} : { default: found },
|
|
68908
|
+
validate: (v) => v.trim() === "" ? fail$1("a Ref is needed") : succeed$3(v.trim())
|
|
68909
|
+
}) : found ?? (yield* new ConfigAddError({ reason: `could not find ${input.origin}'s default branch; pass --ref` })));
|
|
68910
|
+
const install = input.install ?? (ask ? yield* String$4({
|
|
68911
|
+
message: "Install Command (runs in the Managed Clone)",
|
|
68912
|
+
validate: (v) => v.trim() === "" ? fail$1("an Install Command is needed") : succeed$3(v.trim())
|
|
68913
|
+
}) : yield* needs("--install"));
|
|
68914
|
+
const outputs = (input.outputs.length > 0 || input.install !== void 0 || !ask ? input.outputs : (yield* String$4({
|
|
68915
|
+
message: "Outputs (paths outside the clone it produces, comma-separated, optional)",
|
|
68916
|
+
default: ""
|
|
68917
|
+
})).split(",").map((o) => o.trim()).filter((o) => o !== "")).map(homeRelative);
|
|
68918
|
+
const targeting = targetingOf(input.targeting);
|
|
68919
|
+
yield* addItem({
|
|
68920
|
+
config: input.config,
|
|
68921
|
+
provider: "repos",
|
|
68922
|
+
name,
|
|
68923
|
+
entry: {
|
|
68924
|
+
origin: input.origin,
|
|
68925
|
+
ref,
|
|
68926
|
+
install,
|
|
68927
|
+
...outputs.length > 0 ? { outputs } : {},
|
|
68928
|
+
...targeting
|
|
68929
|
+
},
|
|
68930
|
+
targeting
|
|
68931
|
+
});
|
|
68932
|
+
});
|
|
68933
|
+
const addFile = fn("addFile")(function* (input) {
|
|
68934
|
+
const fs = yield* FileSystem;
|
|
68935
|
+
const local = (yield* Path$1).resolve(expandHome$1(input.path));
|
|
68936
|
+
if (!(yield* fs.exists(local))) return yield* new ConfigAddError({ reason: `${input.path} does not exist here; a Ship copies it from this Machine` });
|
|
68937
|
+
const path = homeRelative(local);
|
|
68938
|
+
const name = input.name ?? fileName(path);
|
|
68939
|
+
const targeting = targetingOf(input.targeting);
|
|
68940
|
+
yield* addItem({
|
|
68941
|
+
config: input.config,
|
|
68942
|
+
provider: "files",
|
|
68943
|
+
name,
|
|
68944
|
+
entry: fileEntry(path, input.exclude, targeting),
|
|
68945
|
+
targeting
|
|
68946
|
+
});
|
|
68947
|
+
});
|
|
68948
|
+
const addMachines = fn("addMachines")(function* (input) {
|
|
68949
|
+
const { path, text, config } = yield* openConfig(input.config);
|
|
68950
|
+
const tailnet = yield* readTailnet();
|
|
68951
|
+
const listed = new Set(config.machines);
|
|
68952
|
+
const candidates = tailnet.fleet.filter((m) => !listed.has(m.name));
|
|
68953
|
+
const names = input.names.length > 0 ? input.names : !interactive() ? yield* needs("a Machine name") : candidates.length === 0 ? yield* new ConfigAddError({ reason: "every macOS and linux Machine on the tailnet is already listed" }) : yield* MultiSelect({
|
|
68954
|
+
message: "Which Machines?",
|
|
68955
|
+
choices: candidates.map((m) => {
|
|
68956
|
+
const note = machineNote(m, tailnet);
|
|
68957
|
+
return {
|
|
68958
|
+
title: `${m.name} ${m.platform}`,
|
|
68959
|
+
value: m.name,
|
|
68960
|
+
...note === void 0 ? {} : { description: note }
|
|
68961
|
+
};
|
|
68962
|
+
}),
|
|
68963
|
+
min: 1
|
|
68964
|
+
});
|
|
68965
|
+
const again = names.filter((n) => listed.has(n));
|
|
68966
|
+
if (again.length > 0) return yield* new ConfigAddError({ reason: `already in machines: ${again.join(", ")}` });
|
|
68967
|
+
const unknown = names.filter((n) => !tailnet.fleet.some((m) => m.name === n));
|
|
68968
|
+
if (unknown.length > 0) return yield* new ConfigAddError({ reason: `not a macOS or linux Machine on the tailnet: ${unknown.join(", ")} (it has: ${tailnet.fleet.map((m) => m.name).join(", ")})` });
|
|
68969
|
+
let next = text;
|
|
68970
|
+
for (const [i, name] of names.entries()) {
|
|
68971
|
+
const result = insertAt(next, ["machines", config.machines.length + i], name);
|
|
68972
|
+
if ("reason" in result) return yield* new ConfigAddError({ reason: result.reason });
|
|
68973
|
+
next = result.text;
|
|
68974
|
+
}
|
|
68975
|
+
yield* saveConfig(path, next);
|
|
68976
|
+
const notes = names.flatMap((n) => {
|
|
68977
|
+
const m = tailnet.fleet.find((f) => f.name === n);
|
|
68978
|
+
const note = m === void 0 ? void 0 : machineNote(m, tailnet);
|
|
68979
|
+
return note === void 0 ? [] : [`${n}: ${note}`];
|
|
68980
|
+
});
|
|
68981
|
+
yield* log(changeText(path, text, next, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
68982
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68983
|
+
tone: "ok",
|
|
68984
|
+
children: "added"
|
|
68985
|
+
}),
|
|
68986
|
+
` ${names.join(", ")} to the Fleet`,
|
|
68987
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
68988
|
+
tone: "dim",
|
|
68989
|
+
children: " · fizz ship to set it up"
|
|
68990
|
+
})
|
|
68991
|
+
] })));
|
|
68992
|
+
for (const note of notes) yield* log(note);
|
|
68993
|
+
});
|
|
68994
|
+
const machineNote = (m, tailnet) => m.name === tailnet.self.name ? void 0 : !m.online ? "offline; a Ship skips it until it is online" : !m.ssh ? "Tailscale SSH is off there: run sudo tailscale set --ssh on it" : void 0;
|
|
68995
|
+
//#endregion
|
|
68996
|
+
//#region src/configEdit.tsx
|
|
68997
|
+
const pathOf = (provider, item) => {
|
|
68998
|
+
if (provider === "vp" && item === "vp") return ["vp", "version"];
|
|
68999
|
+
if (provider === "vp" && (item === "node" || PACKAGE_MANAGERS.some((pm) => pm === item))) return ["vp", item];
|
|
69000
|
+
return PROVIDER_NAMES.some((p) => p === provider) ? [
|
|
69001
|
+
provider,
|
|
69002
|
+
"items",
|
|
69003
|
+
item
|
|
69004
|
+
] : void 0;
|
|
69005
|
+
};
|
|
69006
|
+
const owner = (path) => path.slice(0, -1).join(".");
|
|
69007
|
+
const removeMachine = fn("removeMachine")(function* (flag, name) {
|
|
69008
|
+
const { path, text, config } = yield* openConfig(flag);
|
|
69009
|
+
const index = config.machines.indexOf(name);
|
|
69010
|
+
if (index < 0) return yield* new ConfigAddError({ reason: `${name} is not in machines` });
|
|
69011
|
+
const lists = targetingLists(text, name);
|
|
69012
|
+
const only = lists.filter((l) => l.size === 1);
|
|
69013
|
+
if (only.length > 0) return yield* new ConfigAddError({ reason: `${name} is the only Machine ${only.map((l) => owner(l.path)).join(", ")} targets; change ${only.length === 1 ? "it" : "them"} with fizz config edit first` });
|
|
69014
|
+
let next = text;
|
|
69015
|
+
for (const at of [...lists.map((l) => [...l.path, l.index]), ["machines", index]].toReversed()) {
|
|
69016
|
+
const result = removeAt(next, at);
|
|
69017
|
+
if ("reason" in result) return yield* new ConfigAddError({ reason: result.reason });
|
|
69018
|
+
next = result.text;
|
|
69019
|
+
}
|
|
69020
|
+
yield* saveConfig(path, next);
|
|
69021
|
+
yield* log(changeText(path, text, next, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
69022
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
69023
|
+
tone: "ok",
|
|
69024
|
+
children: "removed"
|
|
69025
|
+
}),
|
|
69026
|
+
` ${name} from the Fleet`,
|
|
69027
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
69028
|
+
tone: "dim",
|
|
69029
|
+
children: " · a Ship no longer checks or changes it"
|
|
69030
|
+
})
|
|
69031
|
+
] })));
|
|
69032
|
+
});
|
|
69033
|
+
const removeItem = fn("removeItem")(function* (input) {
|
|
69034
|
+
const at = parseAddress(input.address);
|
|
69035
|
+
if (at === void 0) return yield* new ConfigAddError({ reason: `${input.address}: expected provider:item, as the Ledger writes it (brew:tmux)` });
|
|
69036
|
+
if (at.provider === "machines") return yield* removeMachine(input.config, at.item);
|
|
69037
|
+
const path = pathOf(at.provider, at.item);
|
|
69038
|
+
if (path === void 0) return yield* new ConfigAddError({ reason: `${at.provider}: expected machines or a Provider (${PROVIDER_NAMES.join(", ")})` });
|
|
69039
|
+
const { path: file, text } = yield* openConfig(input.config);
|
|
69040
|
+
const result = removeAt(text, path);
|
|
69041
|
+
if ("reason" in result) return yield* new ConfigAddError({ reason: `${input.address} is not in the config` });
|
|
69042
|
+
yield* saveConfig(file, result.text);
|
|
69043
|
+
yield* log(changeText(file, text, result.text, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
69044
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
69045
|
+
tone: "ok",
|
|
69046
|
+
children: "removed"
|
|
69047
|
+
}),
|
|
69048
|
+
` ${input.address}`,
|
|
69049
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
69050
|
+
tone: "dim",
|
|
69051
|
+
children: " · a Ship never uninstalls it, so it stays where it is"
|
|
69052
|
+
})
|
|
69053
|
+
] })));
|
|
69054
|
+
});
|
|
69055
|
+
const runEditor = fn("runEditor")(function* (file) {
|
|
69056
|
+
const spawner = yield* ChildProcessSpawner;
|
|
69057
|
+
const editor = process.env.VISUAL || process.env.EDITOR || "vi";
|
|
69058
|
+
return yield* scoped(gen(function* () {
|
|
69059
|
+
return yield* (yield* spawner.spawn(make$13("/bin/sh", [
|
|
69060
|
+
"-c",
|
|
69061
|
+
`${editor} "$1"`,
|
|
69062
|
+
"fizz",
|
|
69063
|
+
file
|
|
69064
|
+
], {
|
|
69065
|
+
stdin: "inherit",
|
|
69066
|
+
stdout: "inherit",
|
|
69067
|
+
stderr: "inherit"
|
|
69068
|
+
}))).exitCode;
|
|
69069
|
+
}));
|
|
69070
|
+
});
|
|
69071
|
+
const editConfig = fn("editConfig")(function* (flags) {
|
|
69072
|
+
if (!interactive()) return yield* new ConfigAddError({ reason: "fizz config edit needs a terminal" });
|
|
69073
|
+
const { path, text } = yield* openConfig(flags.config);
|
|
69074
|
+
const fs = yield* FileSystem;
|
|
69075
|
+
const dir = yield* fs.makeTempDirectoryScoped();
|
|
69076
|
+
const copy = `${dir}/${path.split("/").at(-1) ?? "config.jsonc"}`;
|
|
69077
|
+
yield* fs.writeFileString(copy, text);
|
|
69078
|
+
const paths = yield* Path$1;
|
|
69079
|
+
const schema = paths.join(paths.dirname(path), SCHEMA_FILE);
|
|
69080
|
+
if (yield* fs.exists(schema)) yield* fs.copyFile(schema, `${dir}/${SCHEMA_FILE}`);
|
|
69081
|
+
for (;;) {
|
|
69082
|
+
const code = yield* runEditor(copy);
|
|
69083
|
+
if (code !== 0) return yield* log(`the editor exited with ${code}; the config is unchanged`);
|
|
69084
|
+
const edited = yield* fs.readFileString(copy);
|
|
69085
|
+
if (edited === text) return yield* log("no changes");
|
|
69086
|
+
const parsed = yield* result(parseConfig(homeRelative(path), edited));
|
|
69087
|
+
if (parsed._tag === "Success") {
|
|
69088
|
+
if ((yield* fs.readFileString(path)) !== text) {
|
|
69089
|
+
yield* log(edited);
|
|
69090
|
+
return yield* new ConfigAddError({ reason: `${homeRelative(path)} changed while the editor was open, so it is unchanged; your edit is printed above` });
|
|
69091
|
+
}
|
|
69092
|
+
yield* saveConfig(path, edited);
|
|
69093
|
+
return yield* log(`saved ${homeRelative(path)} · fizz ship to ship it`);
|
|
69094
|
+
}
|
|
69095
|
+
yield* error(parsed.failure.message);
|
|
69096
|
+
if (!(yield* Select({
|
|
69097
|
+
message: "The config does not load",
|
|
69098
|
+
choices: [{
|
|
69099
|
+
title: "Edit it again",
|
|
69100
|
+
value: true
|
|
69101
|
+
}, {
|
|
69102
|
+
title: "Discard the changes",
|
|
69103
|
+
value: false
|
|
69104
|
+
}]
|
|
69105
|
+
}))) return yield* log("discarded; the config is unchanged");
|
|
69106
|
+
}
|
|
69107
|
+
}, scoped);
|
|
69108
|
+
const printPath = (flags) => log(flags.config ?? configPath(process.env));
|
|
69109
|
+
//#endregion
|
|
69110
|
+
//#region src/prerequisites.ts
|
|
69111
|
+
const SERVICE_MACOS = "brew install --formula tailscale && sudo brew services start tailscale && sudo tailscale up && sudo tailscale set --ssh";
|
|
69112
|
+
const checksOf = (facts) => {
|
|
69113
|
+
const darwin = facts.platform === "darwin";
|
|
69114
|
+
const vp = facts.vp === void 0 ? {
|
|
69115
|
+
ok: false,
|
|
69116
|
+
required: true,
|
|
69117
|
+
text: "vp is not installed",
|
|
69118
|
+
fix: "curl -fsSL https://vite.plus | bash"
|
|
69119
|
+
} : {
|
|
69120
|
+
ok: true,
|
|
69121
|
+
required: true,
|
|
69122
|
+
text: `vp ${facts.vp}`
|
|
69123
|
+
};
|
|
69124
|
+
const ts = facts.tailscale;
|
|
69125
|
+
const tailscale = ts === void 0 ? [{
|
|
69126
|
+
ok: false,
|
|
69127
|
+
required: true,
|
|
69128
|
+
text: "Tailscale is not installed",
|
|
69129
|
+
fix: darwin ? SERVICE_MACOS : "curl -fsSL https://tailscale.com/install.sh | sh && sudo tailscale up && sudo tailscale set --ssh"
|
|
69130
|
+
}] : ts.app && !ts.ssh ? [{
|
|
69131
|
+
ok: false,
|
|
69132
|
+
required: true,
|
|
69133
|
+
text: "Tailscale is the macOS app, which cannot accept Tailscale SSH. Quit and remove it, then use Homebrew's tailscaled",
|
|
69134
|
+
fix: SERVICE_MACOS
|
|
69135
|
+
}] : ts.state === void 0 ? [{
|
|
69136
|
+
ok: false,
|
|
69137
|
+
required: true,
|
|
69138
|
+
text: "Tailscale is installed, but tailscaled is not running",
|
|
69139
|
+
fix: darwin ? "sudo brew services start tailscale && sudo tailscale up" : "sudo systemctl enable --now tailscaled && sudo tailscale up"
|
|
69140
|
+
}] : ts.state !== "Running" ? [{
|
|
69141
|
+
ok: false,
|
|
69142
|
+
required: true,
|
|
69143
|
+
text: `Tailscale is not connected (${ts.state})`,
|
|
69144
|
+
fix: "sudo tailscale up"
|
|
69145
|
+
}] : [{
|
|
69146
|
+
ok: true,
|
|
69147
|
+
required: true,
|
|
69148
|
+
text: "Tailscale, connected"
|
|
69149
|
+
}, ts.ssh ? {
|
|
69150
|
+
ok: true,
|
|
69151
|
+
required: true,
|
|
69152
|
+
text: "Tailscale SSH is on"
|
|
69153
|
+
} : {
|
|
69154
|
+
ok: false,
|
|
69155
|
+
required: true,
|
|
69156
|
+
text: "Tailscale SSH is off",
|
|
69157
|
+
fix: "sudo tailscale set --ssh"
|
|
69158
|
+
}];
|
|
69159
|
+
const brewForTailscale = darwin && (ts === void 0 || ts.app && !ts.ssh);
|
|
69160
|
+
const brew = !darwin ? [] : facts.brew ? [{
|
|
69161
|
+
ok: true,
|
|
69162
|
+
required: false,
|
|
69163
|
+
text: "Homebrew"
|
|
69164
|
+
}] : [{
|
|
69165
|
+
ok: false,
|
|
69166
|
+
required: brewForTailscale,
|
|
69167
|
+
text: brewForTailscale ? "Homebrew is not installed (Tailscale's fix needs it)" : "Homebrew is not installed (only brew and cask Items need it)",
|
|
69168
|
+
fix: "/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
|
|
69169
|
+
}];
|
|
69170
|
+
const gh = facts.gh === "logged in" ? {
|
|
69171
|
+
ok: true,
|
|
69172
|
+
required: false,
|
|
69173
|
+
text: "gh, logged in"
|
|
69174
|
+
} : {
|
|
69175
|
+
ok: false,
|
|
69176
|
+
required: false,
|
|
69177
|
+
text: `gh is ${facts.gh === "missing" ? "not installed" : "not logged in"} (only private Tracked Repos need it)`,
|
|
69178
|
+
fix: facts.gh === "logged out" ? "gh auth login" : darwin ? "brew install gh && gh auth login" : "install gh from https://cli.github.com, then gh auth login"
|
|
69179
|
+
};
|
|
69180
|
+
return [
|
|
69181
|
+
vp,
|
|
69182
|
+
...brew,
|
|
69183
|
+
...tailscale,
|
|
69184
|
+
gh
|
|
69185
|
+
];
|
|
69186
|
+
};
|
|
69187
|
+
const Status = fromJsonString(Struct({ BackendState: optionalKey(String$6) }));
|
|
69188
|
+
const Prefs = fromJsonString(Struct({ RunSSH: optionalKey(Boolean$4) }));
|
|
69189
|
+
const readFacts = fn("readFacts")(function* () {
|
|
69190
|
+
const fs = yield* FileSystem;
|
|
69191
|
+
const vp = yield* runLocal("vp", ["--version"]);
|
|
69192
|
+
const version = /^vp v?(\S+)/m.exec(vp.stdout)?.[1];
|
|
69193
|
+
const status = yield* runLocal("tailscale", ["status", "--json"]);
|
|
69194
|
+
const prefs = yield* runLocal("tailscale", ["debug", "prefs"]);
|
|
69195
|
+
const state = decodeOption(Status)(status.stdout);
|
|
69196
|
+
const ssh = decodeOption(Prefs)(prefs.stdout);
|
|
69197
|
+
const brew = yield* runLocal("brew", ["--version"]);
|
|
69198
|
+
const gh = yield* runLocal("gh", ["auth", "status"]);
|
|
69199
|
+
const app = process.platform === "darwin" && (yield* fs.exists("/Applications/Tailscale.app"));
|
|
69200
|
+
return {
|
|
69201
|
+
platform: process.platform,
|
|
69202
|
+
vp: vp.code === 0 ? version : void 0,
|
|
69203
|
+
tailscale: status.code === 127 && !app ? void 0 : {
|
|
69204
|
+
app,
|
|
69205
|
+
state: state._tag === "Some" ? state.value.BackendState : void 0,
|
|
69206
|
+
ssh: ssh._tag === "Some" && ssh.value.RunSSH === true
|
|
69207
|
+
},
|
|
69208
|
+
brew: brew.code === 0,
|
|
69209
|
+
gh: gh.code === 127 ? "missing" : gh.code === 0 ? "logged in" : "logged out"
|
|
69210
|
+
};
|
|
68198
69211
|
});
|
|
69212
|
+
//#endregion
|
|
69213
|
+
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Logger.js
|
|
69214
|
+
const TypeId = "~effect/Logger";
|
|
69215
|
+
/**
|
|
69216
|
+
* Returns `true` if the specified value is a `Logger`, otherwise returns `false`.
|
|
69217
|
+
*
|
|
69218
|
+
* **Example** (Checking logger values)
|
|
69219
|
+
*
|
|
69220
|
+
* ```ts import.meta.vitest
|
|
69221
|
+
* import { Logger } from "effect"
|
|
69222
|
+
*
|
|
69223
|
+
* const myLogger = Logger.make(() => undefined)
|
|
69224
|
+
*
|
|
69225
|
+
* Logger.isLogger(myLogger) // => true
|
|
69226
|
+
* Logger.isLogger("not a logger") // => false
|
|
69227
|
+
* Logger.isLogger({ log: () => {} }) // => false
|
|
69228
|
+
* ```
|
|
69229
|
+
*
|
|
69230
|
+
* @stability stable
|
|
69231
|
+
* @category guards
|
|
69232
|
+
* @since 4.0.0
|
|
69233
|
+
*/
|
|
69234
|
+
const isLogger = (u) => hasProperty(u, TypeId);
|
|
69235
|
+
/**
|
|
69236
|
+
* Context reference containing the active loggers for the current fiber.
|
|
69237
|
+
*
|
|
69238
|
+
* **Details**
|
|
69239
|
+
*
|
|
69240
|
+
* By default this set includes the default logger and the tracer logger.
|
|
69241
|
+
* Providing `Logger.layer` replaces or merges with this set depending on its
|
|
69242
|
+
* options.
|
|
69243
|
+
*
|
|
69244
|
+
* **Example** (Accessing current loggers)
|
|
69245
|
+
*
|
|
69246
|
+
* ```ts import.meta.vitest
|
|
69247
|
+
* import { Effect, Logger } from "effect"
|
|
69248
|
+
*
|
|
69249
|
+
* const messages: Array<unknown> = []
|
|
69250
|
+
* const customLogger = Logger.make((options) => {
|
|
69251
|
+
* messages.push(options.message)
|
|
69252
|
+
* })
|
|
69253
|
+
* const program = Effect.gen(function*() {
|
|
69254
|
+
* const currentLoggers = yield* Effect.service(Logger.CurrentLoggers)
|
|
69255
|
+
* yield* Effect.log("Hello from custom logger").pipe(
|
|
69256
|
+
* Effect.provide(Logger.layer([customLogger]))
|
|
69257
|
+
* )
|
|
69258
|
+
* return currentLoggers.has(Logger.defaultLogger)
|
|
69259
|
+
* })
|
|
69260
|
+
*
|
|
69261
|
+
* Effect.runSync(program) // => true
|
|
69262
|
+
* messages // => [["Hello from custom logger"]]
|
|
69263
|
+
* ```
|
|
69264
|
+
*
|
|
69265
|
+
* @stability stable
|
|
69266
|
+
* @category services
|
|
69267
|
+
* @since 4.0.0
|
|
69268
|
+
*/
|
|
69269
|
+
const CurrentLoggers = CurrentLoggers$1;
|
|
69270
|
+
/**
|
|
69271
|
+
* Creates a new `Logger` from a log function.
|
|
69272
|
+
*
|
|
69273
|
+
* **Details**
|
|
69274
|
+
*
|
|
69275
|
+
* The log function receives an options object containing the message, log level,
|
|
69276
|
+
* cause, fiber information, and timestamp, and should return the desired output.
|
|
69277
|
+
*
|
|
69278
|
+
* **Example** (Creating loggers from functions)
|
|
69279
|
+
*
|
|
69280
|
+
* ```ts import.meta.vitest
|
|
69281
|
+
* import { Effect, Logger } from "effect"
|
|
69282
|
+
*
|
|
69283
|
+
* const outputs: Array<string> = []
|
|
69284
|
+
* const textLogger = Logger.make((options) =>
|
|
69285
|
+
* `${options.logLevel}: ${options.message}`
|
|
69286
|
+
* )
|
|
69287
|
+
* const collector = Logger.make((options) => outputs.push(textLogger.log(options)))
|
|
69288
|
+
*
|
|
69289
|
+
* const program = Effect.log("Hello World").pipe(
|
|
69290
|
+
* Effect.provide(Logger.layer([collector]))
|
|
69291
|
+
* )
|
|
69292
|
+
* Effect.runSync(program)
|
|
69293
|
+
* outputs // => ["Info: Hello World"]
|
|
69294
|
+
* ```
|
|
69295
|
+
*
|
|
69296
|
+
* @stability stable
|
|
69297
|
+
* @category constructors
|
|
69298
|
+
* @since 2.0.0
|
|
69299
|
+
*/
|
|
69300
|
+
const make = loggerMake;
|
|
69301
|
+
/**
|
|
69302
|
+
* Creates a scoped logger that batches the output of another logger.
|
|
69303
|
+
*
|
|
69304
|
+
* **Details**
|
|
69305
|
+
*
|
|
69306
|
+
* The returned effect starts a scoped background process that periodically
|
|
69307
|
+
* passes buffered outputs to `flush`. When the scope closes, the background
|
|
69308
|
+
* process is interrupted and any remaining buffered entries are flushed.
|
|
69309
|
+
*
|
|
69310
|
+
* **Example** (Batching logger output)
|
|
69311
|
+
*
|
|
69312
|
+
* ```ts import.meta.vitest
|
|
69313
|
+
* import { Effect, Logger } from "effect"
|
|
69314
|
+
*
|
|
69315
|
+
* const flushed: Array<ReadonlyArray<string>> = []
|
|
69316
|
+
* const messageLogger = Logger.make((options) => String(options.message))
|
|
69317
|
+
* const batchedLogger = Logger.batched(messageLogger, {
|
|
69318
|
+
* window: "1 hour",
|
|
69319
|
+
* flush: (messages) =>
|
|
69320
|
+
* Effect.sync(() => {
|
|
69321
|
+
* flushed.push(messages)
|
|
69322
|
+
* })
|
|
69323
|
+
* })
|
|
69324
|
+
*
|
|
69325
|
+
* const program = Effect.scoped(Effect.gen(function*() {
|
|
69326
|
+
* const logger = yield* batchedLogger
|
|
69327
|
+
* yield* Effect.log("Event 1").pipe(Effect.provide(Logger.layer([logger])))
|
|
69328
|
+
* yield* Effect.log("Event 2").pipe(Effect.provide(Logger.layer([logger])))
|
|
69329
|
+
* }))
|
|
69330
|
+
* await Effect.runPromise(program)
|
|
69331
|
+
* flushed // => [["Event 1", "Event 2"]]
|
|
69332
|
+
* ```
|
|
69333
|
+
*
|
|
69334
|
+
* @stability stable
|
|
69335
|
+
* @category constructors
|
|
69336
|
+
* @since 2.0.0
|
|
69337
|
+
*/
|
|
69338
|
+
const batched = /*#__PURE__*/ dual(2, (self, options) => suspend$3(() => {
|
|
69339
|
+
let buffer = [];
|
|
69340
|
+
const flush = uninterruptible$1(suspend$3(() => {
|
|
69341
|
+
if (buffer.length === 0) return void_$2;
|
|
69342
|
+
const arr = buffer;
|
|
69343
|
+
buffer = [];
|
|
69344
|
+
return options.flush(arr);
|
|
69345
|
+
}));
|
|
69346
|
+
return uninterruptibleMask$1((restore) => restore(sleep$1(options.window).pipe(andThen$1(flush), forever$2)).pipe(forkDetach, flatMap$2((fiber) => addFinalizer$2(() => andThen$1(fiberInterrupt(fiber), flush))), as$1(loggerMake((options) => {
|
|
69347
|
+
buffer.push(self.log(options));
|
|
69348
|
+
}))));
|
|
69349
|
+
}));
|
|
69350
|
+
/**
|
|
69351
|
+
* Creates a `Layer` which will overwrite the current set of loggers with the
|
|
69352
|
+
* specified array of `loggers`.
|
|
69353
|
+
*
|
|
69354
|
+
* **Details**
|
|
69355
|
+
*
|
|
69356
|
+
* If the specified array of `loggers` should be _merged_ with the current set
|
|
69357
|
+
* of loggers (instead of overwriting them), set `mergeWithExisting` to `true`.
|
|
69358
|
+
*
|
|
69359
|
+
* **Example** (Providing logger layers)
|
|
69360
|
+
*
|
|
69361
|
+
* ```ts import.meta.vitest
|
|
69362
|
+
* import { Effect, Logger } from "effect"
|
|
69363
|
+
*
|
|
69364
|
+
* const messages: Array<unknown> = []
|
|
69365
|
+
* const customLogger = Logger.make((options) => {
|
|
69366
|
+
* messages.push(options.message)
|
|
69367
|
+
* })
|
|
69368
|
+
* const CustomLoggerLayer = Logger.layer([customLogger])
|
|
69369
|
+
*
|
|
69370
|
+
* const program = Effect.log("Application started").pipe(
|
|
69371
|
+
* Effect.provide(CustomLoggerLayer)
|
|
69372
|
+
* )
|
|
69373
|
+
* Effect.runSync(program)
|
|
69374
|
+
* messages // => [["Application started"]]
|
|
69375
|
+
* ```
|
|
69376
|
+
*
|
|
69377
|
+
* @stability stable
|
|
69378
|
+
* @category layers
|
|
69379
|
+
* @since 4.0.0
|
|
69380
|
+
*/
|
|
69381
|
+
const layer = (loggers, options) => effect(CurrentLoggers, withFiber$1(fnUntraced$1(function* (fiber) {
|
|
69382
|
+
const currentLoggers = new Set(options?.mergeWithExisting === true ? fiber.getRef(CurrentLoggers$1) : []);
|
|
69383
|
+
for (const logger of loggers) currentLoggers.add(isEffect$1(logger) ? yield* logger : logger);
|
|
69384
|
+
return currentLoggers;
|
|
69385
|
+
})));
|
|
69386
|
+
/**
|
|
69387
|
+
* Creates a scoped logger that writes string logger output to a file.
|
|
69388
|
+
*
|
|
69389
|
+
* **Details**
|
|
69390
|
+
*
|
|
69391
|
+
* The returned effect requires `FileSystem` and `Scope`. The file logger batches
|
|
69392
|
+
* string output, writes each batch to the specified path, and flushes remaining
|
|
69393
|
+
* entries when the scope closes.
|
|
69394
|
+
*
|
|
69395
|
+
* **Example** (Writing JSON logs to a file)
|
|
69396
|
+
*
|
|
69397
|
+
* ```ts import.meta.vitest
|
|
69398
|
+
* import { Effect, FileSystem, Logger } from "effect"
|
|
69399
|
+
*
|
|
69400
|
+
* const writes: Array<string> = []
|
|
69401
|
+
* const file = {
|
|
69402
|
+
* writeAll: (buffer: Uint8Array) => Effect.sync(() => {
|
|
69403
|
+
* writes.push(new TextDecoder().decode(buffer).trim())
|
|
69404
|
+
* })
|
|
69405
|
+
* } as unknown as FileSystem.File
|
|
69406
|
+
* const fileSystem = FileSystem.makeNoop({ open: () => Effect.succeed(file) })
|
|
69407
|
+
* const messageLogger = Logger.make((options) => String(options.message))
|
|
69408
|
+
*
|
|
69409
|
+
* const program = Effect.scoped(Effect.gen(function*() {
|
|
69410
|
+
* const fileLogger = yield* Logger.toFile(messageLogger, "/tmp/log.txt")
|
|
69411
|
+
* yield* Effect.log("a").pipe(Effect.provide(Logger.layer([fileLogger])))
|
|
69412
|
+
* yield* Effect.log("b").pipe(Effect.provide(Logger.layer([fileLogger])))
|
|
69413
|
+
* yield* Effect.log("c").pipe(Effect.provide(Logger.layer([fileLogger])))
|
|
69414
|
+
* })).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem))
|
|
69415
|
+
*
|
|
69416
|
+
* await Effect.runPromise(program)
|
|
69417
|
+
* writes // => ["a\nb\nc"]
|
|
69418
|
+
* ```
|
|
69419
|
+
*
|
|
69420
|
+
* **Example** (Writing logs to files)
|
|
69421
|
+
*
|
|
69422
|
+
* ```ts import.meta.vitest
|
|
69423
|
+
* import { Effect, FileSystem, Logger } from "effect"
|
|
69424
|
+
*
|
|
69425
|
+
* const writes: Array<string> = []
|
|
69426
|
+
* const file = {
|
|
69427
|
+
* writeAll: (buffer: Uint8Array) => Effect.sync(() => {
|
|
69428
|
+
* writes.push(new TextDecoder().decode(buffer).trim())
|
|
69429
|
+
* })
|
|
69430
|
+
* } as unknown as FileSystem.File
|
|
69431
|
+
* const fileSystem = FileSystem.makeNoop({ open: () => Effect.succeed(file) })
|
|
69432
|
+
* const messageLogger = Logger.make((options) => String(options.message))
|
|
69433
|
+
*
|
|
69434
|
+
* const program = Effect.scoped(Effect.gen(function*() {
|
|
69435
|
+
* const fileLogger = yield* Logger.toFile(messageLogger, "/tmp/app.log", {
|
|
69436
|
+
* batchWindow: "1 hour"
|
|
69437
|
+
* })
|
|
69438
|
+
* yield* Effect.log("Application started").pipe(
|
|
69439
|
+
* Effect.provide(Logger.layer([fileLogger]))
|
|
69440
|
+
* )
|
|
69441
|
+
* })).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem))
|
|
69442
|
+
*
|
|
69443
|
+
* await Effect.runPromise(program)
|
|
69444
|
+
* writes // => ["Application started"]
|
|
69445
|
+
* ```
|
|
69446
|
+
*
|
|
69447
|
+
* @stability unstable
|
|
69448
|
+
* @category logging
|
|
69449
|
+
* @since 4.0.0
|
|
69450
|
+
*/
|
|
69451
|
+
const toFile = /*#__PURE__*/ dual((args) => isLogger(args[0]), (self, path, options) => gen$1(function* () {
|
|
69452
|
+
const logFile = yield* (yield* FileSystem).open(path, {
|
|
69453
|
+
flag: "a+",
|
|
69454
|
+
...options
|
|
69455
|
+
});
|
|
69456
|
+
const encoder = new TextEncoder();
|
|
69457
|
+
return yield* batched(self, {
|
|
69458
|
+
window: options?.batchWindow ?? 1e3,
|
|
69459
|
+
flush: (output) => ignore$1(logFile.writeAll(encoder.encode(output.join("\n") + "\n")))
|
|
69460
|
+
});
|
|
69461
|
+
}));
|
|
68199
69462
|
const decodeFact$3 = decodeUnknownOption(Union([Struct({
|
|
68200
69463
|
fact: Literal("brew"),
|
|
68201
69464
|
version: NullOr(String$6)
|
|
@@ -69662,6 +70925,99 @@ const noLive = {
|
|
|
69662
70925
|
aside: (effect) => effect
|
|
69663
70926
|
};
|
|
69664
70927
|
const canRedraw = () => Boolean(process.stdout.isTTY) && process.env.TERM !== "dumb";
|
|
70928
|
+
var LogsError = class extends Error {
|
|
70929
|
+
name = "LogsError";
|
|
70930
|
+
};
|
|
70931
|
+
const shipId = (started, from) => `${formatIso(started).slice(0, 19).replaceAll(":", "-")}Z-${from}`;
|
|
70932
|
+
const parseShipId = (id) => {
|
|
70933
|
+
const [, day, h, m, s, from] = /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})Z-(.+)$/.exec(id) ?? [];
|
|
70934
|
+
if (from === void 0) return void 0;
|
|
70935
|
+
return {
|
|
70936
|
+
started: Date.parse(`${day}T${h}:${m}:${s}Z`),
|
|
70937
|
+
from
|
|
70938
|
+
};
|
|
70939
|
+
};
|
|
70940
|
+
const lineFormat = (hidden) => make(({ message, logLevel, date, cause, fiber }) => {
|
|
70941
|
+
const { machine, phase } = fiber.getRef(CurrentLogAnnotations);
|
|
70942
|
+
const scope = [machine, phase].filter((s) => typeof s === "string").join(" ");
|
|
70943
|
+
const prefix = `${date.toISOString()} ${logLevel.toUpperCase().padEnd(5)}${scope === "" ? "" : ` [${scope}]`}`;
|
|
70944
|
+
const parts = (Array.isArray(message) ? message : [message]).map((m) => typeof m === "string" ? m : JSON.stringify(m));
|
|
70945
|
+
if (cause.reasons.length > 0) parts.push(pretty(cause));
|
|
70946
|
+
let text = parts.join(" ");
|
|
70947
|
+
for (const secret of hidden) text = text.replaceAll(secret, "[hidden]");
|
|
70948
|
+
return text.split("\n").map((line) => `${prefix} ${line}`).join("\n");
|
|
70949
|
+
});
|
|
70950
|
+
const machineOf = (line) => /^\S+ +\S+ +\[([^\] ]+)/.exec(line)?.[1];
|
|
70951
|
+
const toPrune = (names) => {
|
|
70952
|
+
const logs = names.filter((name) => name.endsWith(".log")).toSorted();
|
|
70953
|
+
return logs.slice(0, Math.max(0, logs.length - 19));
|
|
70954
|
+
};
|
|
70955
|
+
const logsDir = () => LOGS_DIR.replace(/^~/, process.env.HOME ?? "~");
|
|
70956
|
+
const openShipLog = fn("openShipLog")(function* (id, hidden) {
|
|
70957
|
+
const fs = yield* FileSystem;
|
|
70958
|
+
const dir = logsDir();
|
|
70959
|
+
yield* fs.makeDirectory(dir, { recursive: true });
|
|
70960
|
+
for (const name of toPrune(yield* fs.readDirectory(dir))) yield* fs.remove(`${dir}/${name}`);
|
|
70961
|
+
return yield* toFile(lineFormat(hidden), `${dir}/${id}.log`);
|
|
70962
|
+
});
|
|
70963
|
+
const read = fn("read")(function* (target, script, onLine) {
|
|
70964
|
+
const code = yield* runScript(target, script, [], onLine);
|
|
70965
|
+
if (!target.local && code === 255) return yield* fail$1(new LogsError(`could not reach ${target.name} over tailscale ssh`));
|
|
70966
|
+
});
|
|
70967
|
+
const shipIds = fn("shipIds")(function* (target) {
|
|
70968
|
+
const ids = [];
|
|
70969
|
+
yield* read(target, `ls -1 ${quotePath(LOGS_DIR)} 2>/dev/null`, (line) => sync(() => {
|
|
70970
|
+
if (line.endsWith(".log")) ids.push(line.slice(0, -4));
|
|
70971
|
+
}));
|
|
70972
|
+
return ids.toSorted();
|
|
70973
|
+
});
|
|
70974
|
+
const MISSING = "ff-missing";
|
|
70975
|
+
const logs = fn("logs")(function* (flags) {
|
|
70976
|
+
const here = {
|
|
70977
|
+
name: "this Machine",
|
|
70978
|
+
local: true
|
|
70979
|
+
};
|
|
70980
|
+
const local = yield* shipIds(here);
|
|
70981
|
+
const self = parseShipId(local.at(-1) ?? "")?.from;
|
|
70982
|
+
const tailnet = yield* cached(readTailnet());
|
|
70983
|
+
const targetOf = fn("targetOf")(function* (name) {
|
|
70984
|
+
if (name === self) return here;
|
|
70985
|
+
const { self: me, fleet } = yield* tailnet;
|
|
70986
|
+
if (name === me.name) return here;
|
|
70987
|
+
const machine = fleet.find((m) => m.name === name);
|
|
70988
|
+
if (machine === void 0) return yield* fail$1(new LogsError(`no Machine ${name} on the tailnet`));
|
|
70989
|
+
if (!machine.online) return yield* fail$1(new LogsError(`${name} is offline; its logs are there`));
|
|
70990
|
+
return {
|
|
70991
|
+
name,
|
|
70992
|
+
local: false
|
|
70993
|
+
};
|
|
70994
|
+
});
|
|
70995
|
+
if (flags.list) {
|
|
70996
|
+
if (local.length === 0) return yield* log("no Ship Log on this Machine yet");
|
|
70997
|
+
for (const id of local.toReversed()) yield* log(id);
|
|
70998
|
+
return;
|
|
70999
|
+
}
|
|
71000
|
+
const id = flags.id ?? (yield* latestShip(local, self));
|
|
71001
|
+
const ship = parseShipId(id);
|
|
71002
|
+
if (ship === void 0) return yield* fail$1(new LogsError(`not a Ship id: ${id} (fizz logs --list shows them)`));
|
|
71003
|
+
let missing = false;
|
|
71004
|
+
yield* read(local.includes(id) ? here : yield* targetOf(ship.from), `ff_log=${quotePath(LOGS_DIR)}/${quote(`${id}.log`)}\nif [ -f "$ff_log" ]; then cat "$ff_log"; else echo ${MISSING}; fi`, (line) => {
|
|
71005
|
+
if (line === MISSING) return sync(() => void (missing = true));
|
|
71006
|
+
if (flags.machine !== void 0 && machineOf(line) !== flags.machine) return void_;
|
|
71007
|
+
return log(line);
|
|
71008
|
+
});
|
|
71009
|
+
if (missing) return yield* fail$1(new LogsError(`no log for ${id} on ${ship.from}; a Machine keeps its last 20`));
|
|
71010
|
+
});
|
|
71011
|
+
const latestShip = fn("latestShip")(function* (local, self) {
|
|
71012
|
+
const newest = local.at(-1);
|
|
71013
|
+
const record = yield* readConfigRecord;
|
|
71014
|
+
const from = parseShipId(record?.ship ?? "")?.from;
|
|
71015
|
+
const at = self === void 0 ? Infinity : Date.parse(record?.reached[self] ?? "");
|
|
71016
|
+
const newestStarted = parseShipId(newest ?? "")?.started ?? -Infinity;
|
|
71017
|
+
if (record !== void 0 && from !== void 0 && from !== self && at > newestStarted) return record.ship;
|
|
71018
|
+
if (newest === void 0) return yield* fail$1(new LogsError("no Ship Log on this Machine yet"));
|
|
71019
|
+
return newest;
|
|
71020
|
+
});
|
|
69665
71021
|
//#endregion
|
|
69666
71022
|
//#region src/machine.ts
|
|
69667
71023
|
const decodeAt = decodeUnknownOption(AtFact);
|
|
@@ -69838,6 +71194,45 @@ const askQuestions = fn("askQuestions")(function* (input) {
|
|
|
69838
71194
|
return answered;
|
|
69839
71195
|
});
|
|
69840
71196
|
//#endregion
|
|
71197
|
+
//#region src/report.tsx
|
|
71198
|
+
const reportPath = () => `${STATE_DIR.replace(/^~/, process.env.HOME ?? "~")}/last-ship.json`;
|
|
71199
|
+
const LedgerJson = fromJsonString(LedgerState);
|
|
71200
|
+
const encode = encodeEffect(LedgerJson);
|
|
71201
|
+
const decode = decodeEffect(LedgerJson);
|
|
71202
|
+
const saveReport = fn("saveReport")(function* (state) {
|
|
71203
|
+
const fs = yield* FileSystem;
|
|
71204
|
+
const path = reportPath();
|
|
71205
|
+
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
71206
|
+
yield* fs.writeFileString(path, yield* encode(state));
|
|
71207
|
+
});
|
|
71208
|
+
const report = fn("report")(function* () {
|
|
71209
|
+
const fs = yield* FileSystem;
|
|
71210
|
+
const path = reportPath();
|
|
71211
|
+
if (!(yield* fs.exists(path))) return yield* log("no Ship from this Machine yet");
|
|
71212
|
+
const decoded = yield* decode(yield* fs.readFileString(path)).pipe(option);
|
|
71213
|
+
if (isNone(decoded)) return yield* log(`the last report (${path}) is from another version of fleetfizz; the next Ship replaces it`);
|
|
71214
|
+
const state = decoded.value;
|
|
71215
|
+
const when = formatLocal(makeUnsafe(state.started), {
|
|
71216
|
+
dateStyle: "medium",
|
|
71217
|
+
timeStyle: "short"
|
|
71218
|
+
});
|
|
71219
|
+
yield* log(toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
71220
|
+
flexDirection: "column",
|
|
71221
|
+
children: [
|
|
71222
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71223
|
+
tone: "dim",
|
|
71224
|
+
children: `last Ship ${when} from ${state.from}`
|
|
71225
|
+
}),
|
|
71226
|
+
state.notes.map((note, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NoteLine, { note }, i)),
|
|
71227
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LedgerView, {
|
|
71228
|
+
state,
|
|
71229
|
+
full: true,
|
|
71230
|
+
now: state.ended ?? state.started
|
|
71231
|
+
})
|
|
71232
|
+
]
|
|
71233
|
+
})));
|
|
71234
|
+
});
|
|
71235
|
+
//#endregion
|
|
69841
71236
|
//#region src/ship.ts
|
|
69842
71237
|
var ShipError = class extends Error {
|
|
69843
71238
|
name = "ShipError";
|
|
@@ -69899,7 +71294,6 @@ const staleConfigs = fn("staleConfigs")(function* (text, base, own, checked, led
|
|
|
69899
71294
|
}];
|
|
69900
71295
|
});
|
|
69901
71296
|
});
|
|
69902
|
-
const schemaJson = encodeSync(fromJsonString(Unknown, { space: 2 }));
|
|
69903
71297
|
const ship = fn("ship")(function* (flags) {
|
|
69904
71298
|
const tailnet = yield* readTailnet();
|
|
69905
71299
|
const id = shipId(yield* now, tailnet.self.name);
|
|
@@ -69911,9 +71305,9 @@ const ship = fn("ship")(function* (flags) {
|
|
|
69911
71305
|
yield* logInfo(`fizz ${process.argv.slice(2).join(" ")}`);
|
|
69912
71306
|
for (const m of tailnet.fleet) yield* logInfo(`tailnet: ${m.name} ${m.platform}${m.online ? "" : " offline"}${m.ssh ? "" : " no-ssh"}${m.name === tailnet.self.name ? " (this Machine)" : ""}`);
|
|
69913
71307
|
while ((yield* shipPass(flags, tailnet, hidden, id)) === "adopted");
|
|
69914
|
-
}).pipe(onExit((exit) => isSuccess(exit) ? logInfo("the Ship ended") : logError("the Ship stopped", exit.cause)), provide(layer
|
|
71308
|
+
}).pipe(onExit((exit) => isSuccess(exit) ? logInfo("the Ship ended") : logError("the Ship stopped", exit.cause)), provide(layer(logger === void 0 ? [] : [logger])), provideService(MinimumLogLevel, "Debug"));
|
|
69915
71309
|
}, scoped);
|
|
69916
|
-
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
71310
|
+
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id, show = "ledger") {
|
|
69917
71311
|
const fs = yield* FileSystem;
|
|
69918
71312
|
const parsed = parseAnswers(flags.answers);
|
|
69919
71313
|
if ("invalid" in parsed) return yield* fail$1(new ShipError(parsed.invalid));
|
|
@@ -69936,20 +71330,22 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
|
69936
71330
|
const targetsList = tailnet.fleet.filter((m) => fleet.has(m.name) && (flags.only.length === 0 || flags.only.includes(m.name))).toSorted((a, b) => order.indexOf(a.name) - order.indexOf(b.name));
|
|
69937
71331
|
const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
|
|
69938
71332
|
yield* logInfo(`${flags.dryRun ? "dry run, " : ""}targets: ${targetsList.map((m) => m.name).join(", ")}`);
|
|
69939
|
-
|
|
71333
|
+
const quiet = show !== "ledger";
|
|
71334
|
+
if (!quiet) yield* log(`${flags.dryRun ? "dry run, " : ""}${count} from ${tailnet.self.name}`);
|
|
69940
71335
|
const color = colorful();
|
|
69941
71336
|
const { ledger, store } = yield* makeLedger({
|
|
69942
71337
|
from: tailnet.self.name,
|
|
69943
71338
|
dryRun: flags.dryRun,
|
|
69944
71339
|
machines: targetsList.map((m) => m.name),
|
|
69945
|
-
print: (note) => log(noteText(note, color))
|
|
71340
|
+
print: (note) => quiet ? void_ : log(noteText(note, color))
|
|
69946
71341
|
});
|
|
69947
|
-
const redraw = canRedraw();
|
|
71342
|
+
const redraw = !quiet && canRedraw();
|
|
69948
71343
|
let changing = false;
|
|
69949
71344
|
let adopted = false;
|
|
69950
71345
|
const finish = fn("finish")(function* () {
|
|
69951
71346
|
const state = yield* ledger.end;
|
|
69952
71347
|
if (adopted) return;
|
|
71348
|
+
if (show !== "ledger") return show(state);
|
|
69953
71349
|
const saved = changing && (yield* saveReport(state).pipe(as(true), catch_((e) => error(`could not save the report for fizz report: ${e.message}`).pipe(as(false)))));
|
|
69954
71350
|
yield* log(ledgerText(state, redraw ? {
|
|
69955
71351
|
full: false,
|
|
@@ -70003,6 +71399,7 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
|
70003
71399
|
phase: "check"
|
|
70004
71400
|
})), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
|
|
70005
71401
|
const stale = yield* staleConfigs(yield* fs.readFileString(path), base, own, checked, ledger);
|
|
71402
|
+
const asking = new Map(flags.dryRun ? stale.flatMap((s) => s.machines.filter((m) => given("config", m) !== "overwrite").map((m) => [m, s.description])) : []);
|
|
70006
71403
|
if (stale.length > 0) {
|
|
70007
71404
|
const what = stale.map((s) => s.description).join("; ");
|
|
70008
71405
|
const answers = stale.flatMap((s) => s.machines).map((m) => given("config", m));
|
|
@@ -70169,10 +71566,14 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
|
70169
71566
|
}
|
|
70170
71567
|
const text = yield* fs.readFileString(path);
|
|
70171
71568
|
const sha = yield* shaOf(text);
|
|
70172
|
-
for (const { machine, state, plan: p } of planned)
|
|
70173
|
-
|
|
70174
|
-
|
|
70175
|
-
|
|
71569
|
+
for (const { machine, state, plan: p } of planned) {
|
|
71570
|
+
const asked = asking.get(machine.name);
|
|
71571
|
+
yield* ledger.plan(machine.name, p, {
|
|
71572
|
+
...state.config === void 0 ? {} : { from: state.config.sha.slice(0, 7) },
|
|
71573
|
+
to: sha.slice(0, 7),
|
|
71574
|
+
...asked === void 0 ? {} : { asked }
|
|
71575
|
+
});
|
|
71576
|
+
}
|
|
70176
71577
|
if (flags.dryRun) return;
|
|
70177
71578
|
const at = formatIso(yield* now);
|
|
70178
71579
|
const recordFor = (reached) => encodeConfigRecord({
|
|
@@ -70184,7 +71585,7 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
|
70184
71585
|
const record = recordFor(planned.map((g) => g.machine.name));
|
|
70185
71586
|
if (own !== void 0) yield* writeConfigRecord(record);
|
|
70186
71587
|
yield* logInfo(`the config's record: ${record}`);
|
|
70187
|
-
const schema =
|
|
71588
|
+
const schema = fleetConfigSchemaText();
|
|
70188
71589
|
const delivered = yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
|
|
70189
71590
|
return (yield* apply({
|
|
70190
71591
|
name: machine.name,
|
|
@@ -70198,6 +71599,402 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
|
70198
71599
|
if (own !== void 0) yield* writeConfigRecord(recordFor(delivered));
|
|
70199
71600
|
}, scoped);
|
|
70200
71601
|
//#endregion
|
|
71602
|
+
//#region src/init.tsx
|
|
71603
|
+
const DOTFILES = [
|
|
71604
|
+
"~/.gitconfig",
|
|
71605
|
+
"~/.zshrc",
|
|
71606
|
+
"~/.bashrc",
|
|
71607
|
+
"~/.profile",
|
|
71608
|
+
"~/.vimrc"
|
|
71609
|
+
];
|
|
71610
|
+
const draftText = (picks) => {
|
|
71611
|
+
const edits = [
|
|
71612
|
+
...picks.vp === void 0 ? [] : [[["vp", "version"], picks.vp.version], ...picks.vp.node === void 0 ? [] : [[["vp", "node"], picks.vp.node]]],
|
|
71613
|
+
...picks.globals.map((g) => [[
|
|
71614
|
+
"vp",
|
|
71615
|
+
"items",
|
|
71616
|
+
g.name
|
|
71617
|
+
], `^${g.version}`]),
|
|
71618
|
+
...picks.formulae.map((f) => [[
|
|
71619
|
+
"brew",
|
|
71620
|
+
"items",
|
|
71621
|
+
f
|
|
71622
|
+
], "*"]),
|
|
71623
|
+
...picks.casks.length > 0 && picks.linux ? [[["cask", "platforms"], ["darwin"]]] : [],
|
|
71624
|
+
...picks.casks.map((c) => [[
|
|
71625
|
+
"cask",
|
|
71626
|
+
"items",
|
|
71627
|
+
c
|
|
71628
|
+
], "*"]),
|
|
71629
|
+
...picks.files.map((f) => [[
|
|
71630
|
+
"files",
|
|
71631
|
+
"items",
|
|
71632
|
+
fileName(f)
|
|
71633
|
+
], f])
|
|
71634
|
+
];
|
|
71635
|
+
const start = `{\n "$schema": "./${SCHEMA_FILE}",\n "machines": [],\n}\n`;
|
|
71636
|
+
return [...picks.machines.map((m, i) => [["machines", i], m]), ...edits].reduce((text, [path, value]) => {
|
|
71637
|
+
const result = insertAt(text, path, value);
|
|
71638
|
+
return "text" in result ? result.text : text;
|
|
71639
|
+
}, start);
|
|
71640
|
+
};
|
|
71641
|
+
const CheckLine = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71642
|
+
tone: props.check.ok ? "ok" : props.check.required ? "fail" : "skip",
|
|
71643
|
+
children: props.check.ok ? "✓" : props.check.required ? "✗" : "!"
|
|
71644
|
+
}), ` ${props.check.text}`] });
|
|
71645
|
+
const checksText = (checks) => {
|
|
71646
|
+
const required = checks.filter((c) => !c.ok && c.required && c.fix !== void 0);
|
|
71647
|
+
const optional = checks.filter((c) => !c.ok && !c.required && c.fix !== void 0);
|
|
71648
|
+
return toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
71649
|
+
flexDirection: "column",
|
|
71650
|
+
children: [
|
|
71651
|
+
checks.map((check, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckLine, { check }, i)),
|
|
71652
|
+
required.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
71653
|
+
flexDirection: "column",
|
|
71654
|
+
marginTop: 1,
|
|
71655
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Fix these, then run fizz init again:" }), required.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: ` ${c.fix}` }, i))]
|
|
71656
|
+
}),
|
|
71657
|
+
optional.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
71658
|
+
flexDirection: "column",
|
|
71659
|
+
marginTop: 1,
|
|
71660
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: required.length > 0 ? "Optional:" : "Optional, now or later:" }), optional.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: ` ${c.fix}` }, i))]
|
|
71661
|
+
})
|
|
71662
|
+
]
|
|
71663
|
+
}));
|
|
71664
|
+
};
|
|
71665
|
+
const Globals = fromJsonString(ArraySchema(Struct({
|
|
71666
|
+
name: String$6,
|
|
71667
|
+
version: String$6
|
|
71668
|
+
})));
|
|
71669
|
+
const lines = fn("lines")(function* (command, args) {
|
|
71670
|
+
const ran = yield* runLocal(command, args);
|
|
71671
|
+
return ran.code === 0 ? ran.stdout.split("\n").filter((l) => l.trim() !== "") : [];
|
|
71672
|
+
});
|
|
71673
|
+
const pickSome = (message, choices) => choices.length === 0 ? succeed$3([]) : MultiSelect({
|
|
71674
|
+
message,
|
|
71675
|
+
choices,
|
|
71676
|
+
maxPerPage: 15
|
|
71677
|
+
});
|
|
71678
|
+
const pickMachines = (tailnet) => MultiSelect({
|
|
71679
|
+
message: "Which Machines are in the Fleet?",
|
|
71680
|
+
choices: tailnet.fleet.map((m) => {
|
|
71681
|
+
const self = m.name === tailnet.self.name;
|
|
71682
|
+
const note = self ? "this Machine" : machineNote(m, tailnet);
|
|
71683
|
+
return {
|
|
71684
|
+
title: `${m.name} ${m.platform}`,
|
|
71685
|
+
value: m.name,
|
|
71686
|
+
selected: self || note === void 0,
|
|
71687
|
+
...note === void 0 ? {} : { description: note }
|
|
71688
|
+
};
|
|
71689
|
+
}),
|
|
71690
|
+
min: 1
|
|
71691
|
+
});
|
|
71692
|
+
const init = fn("init")(function* (flags) {
|
|
71693
|
+
if (!interactive()) return yield* new ConfigAddError({ reason: "fizz init asks questions, so it needs a terminal; write the config by hand instead" });
|
|
71694
|
+
const fs = yield* FileSystem;
|
|
71695
|
+
const path = flags.config ?? configPath(process.env);
|
|
71696
|
+
const checks = checksOf(yield* readFacts());
|
|
71697
|
+
yield* log(checksText(checks));
|
|
71698
|
+
if (checks.some((c) => !c.ok && c.required)) {
|
|
71699
|
+
process.exitCode = 1;
|
|
71700
|
+
return;
|
|
71701
|
+
}
|
|
71702
|
+
const tailnet = yield* readTailnet();
|
|
71703
|
+
if (yield* fs.exists(path)) {
|
|
71704
|
+
const config = yield* loadConfig(path);
|
|
71705
|
+
yield* log(`\n${configText({
|
|
71706
|
+
path,
|
|
71707
|
+
config,
|
|
71708
|
+
tailnet,
|
|
71709
|
+
footer: ["This Machine already has a config. Add to it with fizz config add, and see where each Machine stands with fizz status."]
|
|
71710
|
+
})}`);
|
|
71711
|
+
return;
|
|
71712
|
+
}
|
|
71713
|
+
yield* log("");
|
|
71714
|
+
const picked = yield* pickMachines(tailnet);
|
|
71715
|
+
const machines = picked.includes(tailnet.self.name) ? picked : [tailnet.self.name, ...picked];
|
|
71716
|
+
if (machines !== picked) yield* log(`${tailnet.self.name} is in the Fleet too: a Ship starts from it.`);
|
|
71717
|
+
const vpVersion = /^vp v?(\S+)/m.exec((yield* runLocal("vp", ["--version"])).stdout)?.[1];
|
|
71718
|
+
const node = /^v(\S+)/.exec((yield* runLocal("node", ["--version"])).stdout.trim())?.[1];
|
|
71719
|
+
const pin = vpVersion !== void 0 && (yield* Confirm({
|
|
71720
|
+
message: `Pin vp ${vpVersion}${node === void 0 ? "" : ` and Node ${node}`}, the versions on this Machine?`,
|
|
71721
|
+
initial: true
|
|
71722
|
+
}));
|
|
71723
|
+
const installed = getOrElse(decodeOption(Globals)((yield* runLocal("vp", [
|
|
71724
|
+
"list",
|
|
71725
|
+
"--global",
|
|
71726
|
+
"--json"
|
|
71727
|
+
])).stdout), () => []).filter((g) => isGlobalName(g.name));
|
|
71728
|
+
const globals = yield* pickSome(`Which Globals from this Machine? (${installed.length} installed)`, installed.map((g) => ({
|
|
71729
|
+
title: `${g.name} ${g.version}`,
|
|
71730
|
+
value: g
|
|
71731
|
+
})));
|
|
71732
|
+
const leaves = yield* lines("brew", ["leaves", "--installed-on-request"]);
|
|
71733
|
+
const formulae = yield* pickSome(`Which Formulae from this Machine? (${leaves.length} installed on request)`, leaves.map((f) => ({
|
|
71734
|
+
title: f,
|
|
71735
|
+
value: f
|
|
71736
|
+
})));
|
|
71737
|
+
const installedCasks = process.platform === "darwin" ? yield* lines("brew", ["list", "--cask"]) : [];
|
|
71738
|
+
const casks = yield* pickSome(`Which Casks from this Machine? (${installedCasks.length} installed)`, installedCasks.map((c) => ({
|
|
71739
|
+
title: c,
|
|
71740
|
+
value: c
|
|
71741
|
+
})));
|
|
71742
|
+
const present = yield* filter(DOTFILES, (f) => fs.exists(expandHome$1(f)));
|
|
71743
|
+
const files = yield* pickSome("Which Shared Files? A Ship copies each from the Machine it starts on.", present.map((f) => ({
|
|
71744
|
+
title: f,
|
|
71745
|
+
value: f
|
|
71746
|
+
})));
|
|
71747
|
+
const text = draftText({
|
|
71748
|
+
machines,
|
|
71749
|
+
...pin && vpVersion !== void 0 ? { vp: {
|
|
71750
|
+
version: vpVersion,
|
|
71751
|
+
node
|
|
71752
|
+
} } : {},
|
|
71753
|
+
globals,
|
|
71754
|
+
formulae,
|
|
71755
|
+
casks,
|
|
71756
|
+
files,
|
|
71757
|
+
linux: machines.some((m) => tailnet.fleet.find((f) => f.name === m)?.platform === "linux")
|
|
71758
|
+
});
|
|
71759
|
+
const config = yield* saveConfig(path, text);
|
|
71760
|
+
const paths = yield* Path$1;
|
|
71761
|
+
yield* fs.writeFileString(paths.join(paths.dirname(path), SCHEMA_FILE), fleetConfigSchemaText());
|
|
71762
|
+
yield* log(`\n${toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71763
|
+
tone: "ok",
|
|
71764
|
+
children: "Wrote"
|
|
71765
|
+
}), ` ${homeRelative(path)} and ${SCHEMA_FILE}`] }))}`);
|
|
71766
|
+
yield* log(configText({
|
|
71767
|
+
path,
|
|
71768
|
+
config,
|
|
71769
|
+
tailnet,
|
|
71770
|
+
footer: ["Add more with fizz config add repos|files|vp|brew|cask."]
|
|
71771
|
+
}));
|
|
71772
|
+
if (yield* Confirm({
|
|
71773
|
+
message: "Run fizz ship --dry-run now?",
|
|
71774
|
+
initial: true
|
|
71775
|
+
})) yield* ship({
|
|
71776
|
+
dryRun: true,
|
|
71777
|
+
only: [],
|
|
71778
|
+
rebuild: [],
|
|
71779
|
+
answers: [],
|
|
71780
|
+
config: flags.config,
|
|
71781
|
+
version: flags.version
|
|
71782
|
+
});
|
|
71783
|
+
});
|
|
71784
|
+
//#endregion
|
|
71785
|
+
//#region src/status.tsx
|
|
71786
|
+
const cellOf = (m, address) => {
|
|
71787
|
+
if (m.status === "skipped") return {
|
|
71788
|
+
glyph: "○",
|
|
71789
|
+
tone: "dim"
|
|
71790
|
+
};
|
|
71791
|
+
if (m.status === "failed") return {
|
|
71792
|
+
glyph: "✗",
|
|
71793
|
+
tone: "fail"
|
|
71794
|
+
};
|
|
71795
|
+
const item = m.items.find((i) => i.name === address);
|
|
71796
|
+
if (item === void 0) return {
|
|
71797
|
+
glyph: "·",
|
|
71798
|
+
tone: "dim"
|
|
71799
|
+
};
|
|
71800
|
+
if (item.status === "failed") return {
|
|
71801
|
+
glyph: "✗",
|
|
71802
|
+
tone: "fail"
|
|
71803
|
+
};
|
|
71804
|
+
if (item.asked === true) return {
|
|
71805
|
+
glyph: "?",
|
|
71806
|
+
tone: "ask"
|
|
71807
|
+
};
|
|
71808
|
+
if (item.status === "skipped") return {
|
|
71809
|
+
glyph: "–",
|
|
71810
|
+
tone: "skip"
|
|
71811
|
+
};
|
|
71812
|
+
return item.changes ? {
|
|
71813
|
+
glyph: "↑",
|
|
71814
|
+
tone: "run"
|
|
71815
|
+
} : {
|
|
71816
|
+
glyph: "✓",
|
|
71817
|
+
tone: "ok"
|
|
71818
|
+
};
|
|
71819
|
+
};
|
|
71820
|
+
const rowsOf = (state, config) => {
|
|
71821
|
+
const sections = sectionsOf(config);
|
|
71822
|
+
const planned = state.machines.flatMap((m) => m.items.map((i) => parseAddress(i.name))).filter((at) => at !== void 0);
|
|
71823
|
+
return SHOWN.flatMap((provider) => {
|
|
71824
|
+
const rows = sections.find((s) => s.provider === provider)?.rows ?? [];
|
|
71825
|
+
const names = [.../* @__PURE__ */ new Set([...rows.map((r) => r.name), ...planned.filter((at) => at.provider === provider).map((at) => at.item)])];
|
|
71826
|
+
if (names.length === 0) return [];
|
|
71827
|
+
return [{
|
|
71828
|
+
provider,
|
|
71829
|
+
items: names.map((name) => {
|
|
71830
|
+
const wants = [...new Set(rows.filter((r) => r.name === name).map((r) => r.wants))];
|
|
71831
|
+
return {
|
|
71832
|
+
name,
|
|
71833
|
+
address: `${provider}:${name}`,
|
|
71834
|
+
wants: wants.length === 1 ? wants[0] : ""
|
|
71835
|
+
};
|
|
71836
|
+
})
|
|
71837
|
+
}];
|
|
71838
|
+
});
|
|
71839
|
+
};
|
|
71840
|
+
const differs = (m) => m.items.filter((i) => i.changes || i.status === "failed" || i.status === "skipped");
|
|
71841
|
+
const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
71842
|
+
const summaryOf = (m, record, now) => {
|
|
71843
|
+
if (m.status === "skipped") {
|
|
71844
|
+
const at = record?.reached[m.name];
|
|
71845
|
+
return {
|
|
71846
|
+
text: `${m.detail ?? "skipped"} · ${at === void 0 ? "never reached" : `last reached ${ago(now - Date.parse(at))}`}`,
|
|
71847
|
+
tone: "dim"
|
|
71848
|
+
};
|
|
71849
|
+
}
|
|
71850
|
+
if (m.status === "failed") return {
|
|
71851
|
+
text: `not checked: ${m.detail ?? "failed"}`,
|
|
71852
|
+
tone: "fail"
|
|
71853
|
+
};
|
|
71854
|
+
const items = differs(m);
|
|
71855
|
+
const counts = [
|
|
71856
|
+
[items.filter((i) => i.status === "failed").length, "failing"],
|
|
71857
|
+
[items.filter((i) => i.changes && i.status !== "failed").length, "to change"],
|
|
71858
|
+
[items.filter((i) => i.asked === true).length, "Question"],
|
|
71859
|
+
[items.filter((i) => i.status === "skipped" && i.asked !== true).length, "skipped"]
|
|
71860
|
+
];
|
|
71861
|
+
const parts = counts.flatMap(([n, what]) => n === 0 ? [] : [what === "Question" ? plural(n, what) : `${n} ${what}`]);
|
|
71862
|
+
return parts.length === 0 ? {
|
|
71863
|
+
text: "in line",
|
|
71864
|
+
tone: "ok"
|
|
71865
|
+
} : {
|
|
71866
|
+
text: parts.join(" · "),
|
|
71867
|
+
tone: counts[0][0] > 0 ? "fail" : "run"
|
|
71868
|
+
};
|
|
71869
|
+
};
|
|
71870
|
+
const value = (i) => i.status === "failed" || i.status === "skipped" ? i.detail ?? "" : i.from === void 0 ? `+ ${i.to ?? ""}` : `${i.from} → ${i.to ?? ""}`;
|
|
71871
|
+
const statusText = (input) => {
|
|
71872
|
+
const { state } = input;
|
|
71873
|
+
const machines = state.machines;
|
|
71874
|
+
const checked = machines.filter((m) => m.status !== "skipped" && m.status !== "failed");
|
|
71875
|
+
const sections = rowsOf(state, input.config);
|
|
71876
|
+
const nameWidth = Math.max(...machines.map((m) => m.name.length)) + 2;
|
|
71877
|
+
const itemWidth = Math.max(0, ...sections.flatMap((s) => s.items.map((i) => i.name.length))) + 2;
|
|
71878
|
+
const wantsWidth = Math.min(32, Math.max(0, ...sections.flatMap((s) => s.items.map((i) => (i.wants ?? "").length)))) + 2;
|
|
71879
|
+
const index = (n) => String(n + 1).padEnd(String(machines.length).length + 1);
|
|
71880
|
+
const anything = machines.some((m) => differs(m).length > 0);
|
|
71881
|
+
return toText(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
71882
|
+
flexDirection: "column",
|
|
71883
|
+
children: [
|
|
71884
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71885
|
+
tone: "dim",
|
|
71886
|
+
children: `Checked ${checked.length} of ${plural(machines.length, "Machine")} from ${state.from}`
|
|
71887
|
+
}),
|
|
71888
|
+
state.notes.filter((n) => n.kind === "info").map((n, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71889
|
+
tone: "dim",
|
|
71890
|
+
children: n.text
|
|
71891
|
+
}, i)),
|
|
71892
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
71893
|
+
flexDirection: "column",
|
|
71894
|
+
marginTop: 1,
|
|
71895
|
+
children: machines.map((m, i) => {
|
|
71896
|
+
const summary = summaryOf(m, input.record, input.now);
|
|
71897
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
71898
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71899
|
+
tone: "bold",
|
|
71900
|
+
children: index(i)
|
|
71901
|
+
}),
|
|
71902
|
+
m.name.padEnd(nameWidth),
|
|
71903
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71904
|
+
tone: summary.tone,
|
|
71905
|
+
children: summary.text
|
|
71906
|
+
})
|
|
71907
|
+
] }, m.name);
|
|
71908
|
+
})
|
|
71909
|
+
}),
|
|
71910
|
+
sections.map((s, n) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
71911
|
+
flexDirection: "column",
|
|
71912
|
+
marginTop: n === 0 ? 1 : 0,
|
|
71913
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71914
|
+
tone: "bold",
|
|
71915
|
+
children: s.provider.padEnd(2 + itemWidth + wantsWidth)
|
|
71916
|
+
}), n === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71917
|
+
tone: "bold",
|
|
71918
|
+
children: machines.map((_, i) => i + 1).join(" ")
|
|
71919
|
+
}) : ""] }), s.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
71920
|
+
` ${item.name.padEnd(itemWidth)}`,
|
|
71921
|
+
(item.wants ?? "").slice(0, 32).padEnd(wantsWidth),
|
|
71922
|
+
machines.map((m, i) => {
|
|
71923
|
+
const cell = cellOf(m, item.address);
|
|
71924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71925
|
+
tone: cell.tone,
|
|
71926
|
+
children: `${cell.glyph}${i < machines.length - 1 ? " ".repeat(String(i + 2).length) : ""}`
|
|
71927
|
+
}, m.name);
|
|
71928
|
+
})
|
|
71929
|
+
] }, item.name))]
|
|
71930
|
+
}, s.provider)),
|
|
71931
|
+
machines.flatMap((m) => {
|
|
71932
|
+
const items = m.status === "skipped" || m.status === "failed" ? [] : differs(m);
|
|
71933
|
+
if (items.length === 0) return [];
|
|
71934
|
+
const width = Math.max(...items.map((i) => i.name.length)) + 2;
|
|
71935
|
+
return [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
71936
|
+
flexDirection: "column",
|
|
71937
|
+
marginTop: 1,
|
|
71938
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71939
|
+
tone: "bold",
|
|
71940
|
+
children: m.name
|
|
71941
|
+
}), items.map((i) => {
|
|
71942
|
+
const cell = cellOf(m, i.name);
|
|
71943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
71944
|
+
" ",
|
|
71945
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71946
|
+
tone: cell.tone,
|
|
71947
|
+
children: cell.glyph
|
|
71948
|
+
}),
|
|
71949
|
+
` ${i.name.padEnd(width)}${value(i)}`,
|
|
71950
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71951
|
+
tone: "dim",
|
|
71952
|
+
children: i.reason === void 0 ? "" : ` ${i.reason}`
|
|
71953
|
+
})
|
|
71954
|
+
] }, i.name);
|
|
71955
|
+
})]
|
|
71956
|
+
}, m.name)];
|
|
71957
|
+
}),
|
|
71958
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
71959
|
+
marginTop: 1,
|
|
71960
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
71961
|
+
tone: "dim",
|
|
71962
|
+
children: anything ? "fizz ship to bring them in line" : "Every Machine checked is in line."
|
|
71963
|
+
})
|
|
71964
|
+
})
|
|
71965
|
+
]
|
|
71966
|
+
}));
|
|
71967
|
+
};
|
|
71968
|
+
const status = fn("status")(function* (flags) {
|
|
71969
|
+
const tailnet = yield* readTailnet();
|
|
71970
|
+
const path = flags.config ?? configPath(process.env);
|
|
71971
|
+
const config = yield* loadConfig(path);
|
|
71972
|
+
const record = flags.config === void 0 ? yield* readConfigRecord : void 0;
|
|
71973
|
+
const now$2 = now;
|
|
71974
|
+
const id = shipId(yield* now$2, tailnet.self.name);
|
|
71975
|
+
const tty = Boolean(process.stdout.isTTY);
|
|
71976
|
+
if (tty) process.stdout.write(`checking ${plural(config.machines.length, "Machine")}...`);
|
|
71977
|
+
let state;
|
|
71978
|
+
yield* shipPass({
|
|
71979
|
+
dryRun: true,
|
|
71980
|
+
only: [],
|
|
71981
|
+
rebuild: [],
|
|
71982
|
+
answers: [],
|
|
71983
|
+
config: flags.config,
|
|
71984
|
+
version: flags.version
|
|
71985
|
+
}, tailnet, /* @__PURE__ */ new Set(), id, (end) => {
|
|
71986
|
+
state = end;
|
|
71987
|
+
}).pipe(scoped, provide(layer([])), ensuring(sync(() => tty && process.stdout.write("\r\x1B[2K"))));
|
|
71988
|
+
if (state === void 0) return;
|
|
71989
|
+
if (anyFailed(state)) process.exitCode = 1;
|
|
71990
|
+
yield* log(statusText({
|
|
71991
|
+
state,
|
|
71992
|
+
config,
|
|
71993
|
+
record,
|
|
71994
|
+
now: toEpochMillis(yield* now$2)
|
|
71995
|
+
}));
|
|
71996
|
+
});
|
|
71997
|
+
//#endregion
|
|
70201
71998
|
//#region src/command.ts
|
|
70202
71999
|
const printProblems = (effect) => effect.pipe(catch_((e) => gen(function* () {
|
|
70203
72000
|
yield* error(e.message);
|
|
@@ -70225,6 +72022,14 @@ const shipCommand = make$1("ship", {
|
|
|
70225
72022
|
config: getOrUndefined$1(flags.config),
|
|
70226
72023
|
version
|
|
70227
72024
|
}).pipe(printProblems)));
|
|
72025
|
+
const initCommand = make$1("init", { config: String$1("config").pipe(optional, withMetavar("path"), withDescription$1("Where to write the config. Default: `$XDG_CONFIG_HOME/fleetfizz/config.jsonc`.")) }).pipe(withDescription("Set up this Machine to ship: check what fleetfizz needs here and print any fixes, then pick the Fleet from the tailnet and what to keep in line, and write the config."), withHandler((flags) => init({
|
|
72026
|
+
config: getOrUndefined$1(flags.config),
|
|
72027
|
+
version
|
|
72028
|
+
}).pipe(printProblems)));
|
|
72029
|
+
const statusCommand = make$1("status", { config: String$1("config").pipe(optional, withMetavar("path"), withDescription$1("The config file. Default: `$XDG_CONFIG_HOME/fleetfizz/config.jsonc`.")) }).pipe(withDescription("Check every reachable Machine and show where each Item stands: in line, to change, a Question, or failing. Changes nothing and asks nothing."), withHandler((flags) => status({
|
|
72030
|
+
config: getOrUndefined$1(flags.config),
|
|
72031
|
+
version
|
|
72032
|
+
}).pipe(printProblems)));
|
|
70228
72033
|
const reportCommand = make$1("report", {}).pipe(withDescription("Print the full Ledger of the last Ship from this Machine."), withHandler(() => report()));
|
|
70229
72034
|
const logsCommand = make$1("logs", {
|
|
70230
72035
|
ship: String$2("ship").pipe(optional$1, withDescription$2("The Ship id, printed at the end of every Ship.")),
|
|
@@ -70235,12 +72040,102 @@ const logsCommand = make$1("logs", {
|
|
|
70235
72040
|
machine: getOrUndefined$1(flags.machine),
|
|
70236
72041
|
list: flags.list
|
|
70237
72042
|
}).pipe(printProblems)));
|
|
72043
|
+
const configFlags = { config: String$1("config").pipe(optional, withMetavar("path"), withDescription$1("The config file. Default: `$XDG_CONFIG_HOME/fleetfizz/config.jsonc`.")) };
|
|
72044
|
+
const targetingFlags = {
|
|
72045
|
+
platform: Literals("platform", ["darwin", "linux"]).pipe(atLeast(0), withDescription$1("Only Machines on this Platform. Repeat the flag for both.")),
|
|
72046
|
+
machine: String$1("machine").pipe(atLeast(0), withMetavar("machine"), withDescription$1("Only this Machine. Repeat the flag for more."))
|
|
72047
|
+
};
|
|
72048
|
+
const latestFlag = Boolean$1("latest").pipe(withDefault(false), withDescription$1("Upgrade it on every Ship (`\"latest\"`) instead of installing it once (`\"*\"`)."));
|
|
72049
|
+
const configCommand = make$1("config").pipe(withSharedFlags(configFlags), withDescription("Show the config: each Provider's Items and the Machines they target, and when a Ship last reached each Machine."), withHandler((flags) => showConfig({ config: getOrUndefined$1(flags.config) }).pipe(printProblems)));
|
|
72050
|
+
const configFile = map$5(configCommand, (flags) => getOrUndefined$1(flags.config));
|
|
72051
|
+
const homebrewCommand = (provider) => make$1(provider, {
|
|
72052
|
+
name: String$2("name").pipe(withDescription$2(provider === "brew" ? "The Formula, or a tapped name (`owner/tap/name`)." : "The Cask, or a tapped name.")),
|
|
72053
|
+
latest: latestFlag,
|
|
72054
|
+
...targetingFlags
|
|
72055
|
+
}).pipe(withDescription(provider === "brew" ? "Add a Formula." : "Add a Cask."), withHandler((flags) => flatMap$1(configFile, (config) => addHomebrew({
|
|
72056
|
+
config,
|
|
72057
|
+
provider,
|
|
72058
|
+
name: flags.name,
|
|
72059
|
+
latest: flags.latest,
|
|
72060
|
+
targeting: {
|
|
72061
|
+
platforms: flags.platform,
|
|
72062
|
+
machines: flags.machine
|
|
72063
|
+
}
|
|
72064
|
+
})).pipe(printProblems)));
|
|
72065
|
+
const addCommand = make$1("add").pipe(withDescription("Add an Item or a Machine to the config, then validate and save it."), withSubcommands([
|
|
72066
|
+
make$1("machines", { names: String$2("name").pipe(variadic(), withDescription$2("Machine names. Without any, pick from the tailnet.")) }).pipe(withDescription("Add Machines from the tailnet to the Fleet."), withHandler((flags) => flatMap$1(configFile, (config) => addMachines({
|
|
72067
|
+
config,
|
|
72068
|
+
names: flags.names
|
|
72069
|
+
})).pipe(printProblems))),
|
|
72070
|
+
make$1("vp", {
|
|
72071
|
+
spec: String$2("package").pipe(withDescription$2("An npm package, with a Range (`vercel@^59`) or without one: then `^` the version installed here, or the newest published.")),
|
|
72072
|
+
...targetingFlags
|
|
72073
|
+
}).pipe(withDescription("Add a Global."), withHandler((flags) => flatMap$1(configFile, (config) => addGlobal({
|
|
72074
|
+
config,
|
|
72075
|
+
spec: flags.spec,
|
|
72076
|
+
targeting: {
|
|
72077
|
+
platforms: flags.platform,
|
|
72078
|
+
machines: flags.machine
|
|
72079
|
+
}
|
|
72080
|
+
})).pipe(printProblems))),
|
|
72081
|
+
homebrewCommand("brew"),
|
|
72082
|
+
homebrewCommand("cask"),
|
|
72083
|
+
make$1("repos", {
|
|
72084
|
+
origin: String$2("origin").pipe(withDescription$2("The GitHub `owner/repo`.")),
|
|
72085
|
+
name: String$1("name").pipe(optional, withDescription$1("The Item's name. Default: the repo's name.")),
|
|
72086
|
+
ref: String$1("ref").pipe(optional, withDescription$1("A branch or a full commit SHA. Default: the origin's default branch.")),
|
|
72087
|
+
install: String$1("install").pipe(optional, withDescription$1("The Install Command. Asked for when missing.")),
|
|
72088
|
+
output: String$1("output").pipe(atLeast(0), withMetavar("path"), withDescription$1("A path outside the clone the Install Command produces. Repeat the flag for more.")),
|
|
72089
|
+
...targetingFlags
|
|
72090
|
+
}).pipe(withDescription("Add a Tracked Repo."), withHandler((flags) => flatMap$1(configFile, (config) => addRepo({
|
|
72091
|
+
config,
|
|
72092
|
+
origin: flags.origin,
|
|
72093
|
+
name: getOrUndefined$1(flags.name),
|
|
72094
|
+
ref: getOrUndefined$1(flags.ref),
|
|
72095
|
+
install: getOrUndefined$1(flags.install),
|
|
72096
|
+
outputs: flags.output,
|
|
72097
|
+
targeting: {
|
|
72098
|
+
platforms: flags.platform,
|
|
72099
|
+
machines: flags.machine
|
|
72100
|
+
}
|
|
72101
|
+
})).pipe(printProblems))),
|
|
72102
|
+
make$1("files", {
|
|
72103
|
+
path: String$2("path").pipe(withDescription$2("A file or directory on this Machine.")),
|
|
72104
|
+
name: String$1("name").pipe(optional, withDescription$1("The Item's name. Default: the last part of the path, without a leading dot.")),
|
|
72105
|
+
exclude: String$1("exclude").pipe(atLeast(0), withMetavar("glob"), withDescription$1("A glob inside a directory that is not copied. Repeat the flag for more.")),
|
|
72106
|
+
...targetingFlags
|
|
72107
|
+
}).pipe(withDescription("Add a Shared File."), withHandler((flags) => flatMap$1(configFile, (config) => addFile({
|
|
72108
|
+
config,
|
|
72109
|
+
path: flags.path,
|
|
72110
|
+
name: getOrUndefined$1(flags.name),
|
|
72111
|
+
exclude: flags.exclude,
|
|
72112
|
+
targeting: {
|
|
72113
|
+
platforms: flags.platform,
|
|
72114
|
+
machines: flags.machine
|
|
72115
|
+
}
|
|
72116
|
+
})).pipe(printProblems)))
|
|
72117
|
+
]));
|
|
72118
|
+
const removeCommand = make$1("remove", { address: String$2("item").pipe(withDescription$2("`provider:item` (`brew:tmux`, `vp:node`), or `machines:<name>` to take a Machine out of the Fleet.")) }).pipe(withDescription("Remove an Item or a Machine from the config. A Ship never uninstalls what it removes."), withHandler((flags) => flatMap$1(configFile, (config) => removeItem({
|
|
72119
|
+
config,
|
|
72120
|
+
address: flags.address
|
|
72121
|
+
})).pipe(printProblems)));
|
|
72122
|
+
const editCommand = make$1("edit").pipe(withDescription("Edit the config in `$VISUAL` or `$EDITOR`. It is saved only when it loads."), withHandler(() => flatMap$1(configFile, (config) => editConfig({ config })).pipe(printProblems)));
|
|
72123
|
+
const pathCommand = make$1("path").pipe(withDescription("Print the config's path."), withHandler(() => flatMap$1(configFile, (config) => printPath({ config }))));
|
|
72124
|
+
const configTree = configCommand.pipe(withSubcommands([
|
|
72125
|
+
addCommand,
|
|
72126
|
+
removeCommand,
|
|
72127
|
+
editCommand,
|
|
72128
|
+
pathCommand
|
|
72129
|
+
]));
|
|
70238
72130
|
//#endregion
|
|
70239
72131
|
//#region src/bin.ts
|
|
70240
72132
|
run(make$1("fizz").pipe(withDescription("Keep every Machine on your tailnet in line with one config."), withSubcommands([
|
|
72133
|
+
initCommand,
|
|
70241
72134
|
shipCommand,
|
|
72135
|
+
statusCommand,
|
|
70242
72136
|
reportCommand,
|
|
70243
|
-
logsCommand
|
|
72137
|
+
logsCommand,
|
|
72138
|
+
configTree
|
|
70244
72139
|
])), { version }).pipe(provide(layer$2), runMain);
|
|
70245
72140
|
//#endregion
|
|
70246
72141
|
export {};
|