fleetfizz 0.9.1 → 0.10.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 +1048 -139
- package/package.json +3 -2
- package/schema.json +321 -21
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$3(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$3(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$3(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$3(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$3(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$3(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$3(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$3(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$3(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$3(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$3(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$3(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$3(this.failure)})`;
|
|
2864
2864
|
},
|
|
2865
2865
|
toJSON() {
|
|
2866
2866
|
return {
|
|
@@ -4720,7 +4720,10 @@ const bigint0$3 = /*#__PURE__*/ BigInt(0);
|
|
|
4720
4720
|
const bigint1$2 = /*#__PURE__*/ BigInt(1);
|
|
4721
4721
|
const bigint2 = /*#__PURE__*/ BigInt(2);
|
|
4722
4722
|
const bigint10$1 = /*#__PURE__*/ BigInt(10);
|
|
4723
|
+
const bigint24 = /*#__PURE__*/ BigInt(24);
|
|
4724
|
+
const bigint60 = /*#__PURE__*/ BigInt(60);
|
|
4723
4725
|
const bigint1e3 = /*#__PURE__*/ BigInt(1e3);
|
|
4726
|
+
const bigint1e6 = /*#__PURE__*/ BigInt(1e6);
|
|
4724
4727
|
const roundTiesAwayFromZero = (input) => BigInt(input < 0 ? Math.ceil(input - .5) : Math.floor(input + .5));
|
|
4725
4728
|
const roundMillisToNanos = (millis) => roundTiesAwayFromZero(millis * 1e6);
|
|
4726
4729
|
const parseNanos = (input, scale) => {
|
|
@@ -4930,6 +4933,79 @@ const isDuration = (u) => hasProperty(u, TypeId$32);
|
|
|
4930
4933
|
*/
|
|
4931
4934
|
const isFinite$1 = (self) => self.value._tag !== "Infinity" && self.value._tag !== "NegativeInfinity";
|
|
4932
4935
|
/**
|
|
4936
|
+
* Checks whether a Duration is zero.
|
|
4937
|
+
*
|
|
4938
|
+
* **Example** (Checking for zero durations)
|
|
4939
|
+
*
|
|
4940
|
+
* ```ts import.meta.vitest
|
|
4941
|
+
* import { Duration } from "effect"
|
|
4942
|
+
*
|
|
4943
|
+
* Duration.isZero(Duration.zero) // => true
|
|
4944
|
+
* Duration.isZero(Duration.seconds(1)) // => false
|
|
4945
|
+
* ```
|
|
4946
|
+
*
|
|
4947
|
+
* @stability stable
|
|
4948
|
+
* @category predicates
|
|
4949
|
+
* @since 3.5.0
|
|
4950
|
+
*/
|
|
4951
|
+
const isZero$1 = (self) => {
|
|
4952
|
+
switch (self.value._tag) {
|
|
4953
|
+
case "Millis": return self.value.millis === 0;
|
|
4954
|
+
case "Nanos": return self.value.nanos === bigint0$3;
|
|
4955
|
+
case "Infinity":
|
|
4956
|
+
case "NegativeInfinity": return false;
|
|
4957
|
+
}
|
|
4958
|
+
};
|
|
4959
|
+
/**
|
|
4960
|
+
* Returns `true` if the duration is negative (strictly less than zero).
|
|
4961
|
+
*
|
|
4962
|
+
* **Example** (Checking for negative durations)
|
|
4963
|
+
*
|
|
4964
|
+
* ```ts import.meta.vitest
|
|
4965
|
+
* import { Duration } from "effect"
|
|
4966
|
+
*
|
|
4967
|
+
* Duration.isNegative(Duration.seconds(-5)) // => true
|
|
4968
|
+
* Duration.isNegative(Duration.zero) // => false
|
|
4969
|
+
* Duration.isNegative(Duration.negativeInfinity) // => true
|
|
4970
|
+
* ```
|
|
4971
|
+
*
|
|
4972
|
+
* @stability stable
|
|
4973
|
+
* @category predicates
|
|
4974
|
+
* @since 4.0.0
|
|
4975
|
+
*/
|
|
4976
|
+
const isNegative = (self) => {
|
|
4977
|
+
switch (self.value._tag) {
|
|
4978
|
+
case "Millis": return self.value.millis < 0;
|
|
4979
|
+
case "Nanos": return self.value.nanos < bigint0$3;
|
|
4980
|
+
case "NegativeInfinity": return true;
|
|
4981
|
+
case "Infinity": return false;
|
|
4982
|
+
}
|
|
4983
|
+
};
|
|
4984
|
+
/**
|
|
4985
|
+
* Returns the absolute value of the duration.
|
|
4986
|
+
*
|
|
4987
|
+
* **Example** (Taking absolute duration values)
|
|
4988
|
+
*
|
|
4989
|
+
* ```ts import.meta.vitest
|
|
4990
|
+
* import { Duration } from "effect"
|
|
4991
|
+
*
|
|
4992
|
+
* Duration.abs(Duration.seconds(-5)) // => Duration.seconds(5)
|
|
4993
|
+
* Duration.abs(Duration.negativeInfinity) // => Duration.infinity
|
|
4994
|
+
* ```
|
|
4995
|
+
*
|
|
4996
|
+
* @stability stable
|
|
4997
|
+
* @category math
|
|
4998
|
+
* @since 4.0.0
|
|
4999
|
+
*/
|
|
5000
|
+
const abs = (self) => {
|
|
5001
|
+
switch (self.value._tag) {
|
|
5002
|
+
case "Infinity":
|
|
5003
|
+
case "NegativeInfinity": return infinity;
|
|
5004
|
+
case "Millis": return self.value.millis < 0 ? make$27(-self.value.millis) : self;
|
|
5005
|
+
case "Nanos": return self.value.nanos < bigint0$3 ? make$27(-self.value.nanos) : self;
|
|
5006
|
+
}
|
|
5007
|
+
};
|
|
5008
|
+
/**
|
|
4933
5009
|
* A Duration representing zero time.
|
|
4934
5010
|
*
|
|
4935
5011
|
* **Example** (Referencing the zero duration)
|
|
@@ -5245,6 +5321,104 @@ const Equivalence$1 = (self, that) => matchPair(self, that, {
|
|
|
5245
5321
|
* @since 2.0.0
|
|
5246
5322
|
*/
|
|
5247
5323
|
const equals = /*#__PURE__*/ dual(2, (self, that) => Equivalence$1(self, that));
|
|
5324
|
+
/**
|
|
5325
|
+
* Decomposes a `Duration` into normalized signed components.
|
|
5326
|
+
*
|
|
5327
|
+
* **Details**
|
|
5328
|
+
*
|
|
5329
|
+
* Finite durations are returned as `{ days, hours, minutes, seconds, millis,
|
|
5330
|
+
* nanos }`. Infinite durations return every component as `Infinity` or
|
|
5331
|
+
* `-Infinity`.
|
|
5332
|
+
*
|
|
5333
|
+
* **Example** (Decomposing durations into parts)
|
|
5334
|
+
*
|
|
5335
|
+
* ```ts import.meta.vitest
|
|
5336
|
+
* import { Duration } from "effect"
|
|
5337
|
+
*
|
|
5338
|
+
* // Create a complex duration by adding multiple parts
|
|
5339
|
+
* const duration = Duration.sum(
|
|
5340
|
+
* Duration.sum(
|
|
5341
|
+
* Duration.sum(Duration.days(1), Duration.hours(2)),
|
|
5342
|
+
* Duration.sum(Duration.minutes(30), Duration.seconds(45))
|
|
5343
|
+
* ),
|
|
5344
|
+
* Duration.millis(123)
|
|
5345
|
+
* )
|
|
5346
|
+
* Duration.parts(duration) // => ({ days: 1, hours: 2, minutes: 30, seconds: 45, millis: 123, nanos: 0 })
|
|
5347
|
+
*
|
|
5348
|
+
* const complex = Duration.sum(Duration.hours(25), Duration.minutes(90))
|
|
5349
|
+
* Duration.parts(complex) // => ({ days: 1, hours: 2, minutes: 30, seconds: 0, millis: 0, nanos: 0 })
|
|
5350
|
+
* ```
|
|
5351
|
+
*
|
|
5352
|
+
* @stability stable
|
|
5353
|
+
* @category converting
|
|
5354
|
+
* @since 3.8.0
|
|
5355
|
+
*/
|
|
5356
|
+
const parts = (self) => {
|
|
5357
|
+
if (self.value._tag === "Infinity") return {
|
|
5358
|
+
days: Infinity,
|
|
5359
|
+
hours: Infinity,
|
|
5360
|
+
minutes: Infinity,
|
|
5361
|
+
seconds: Infinity,
|
|
5362
|
+
millis: Infinity,
|
|
5363
|
+
nanos: Infinity
|
|
5364
|
+
};
|
|
5365
|
+
if (self.value._tag === "NegativeInfinity") return {
|
|
5366
|
+
days: -Infinity,
|
|
5367
|
+
hours: -Infinity,
|
|
5368
|
+
minutes: -Infinity,
|
|
5369
|
+
seconds: -Infinity,
|
|
5370
|
+
millis: -Infinity,
|
|
5371
|
+
nanos: -Infinity
|
|
5372
|
+
};
|
|
5373
|
+
const n = toNanosUnsafe(self);
|
|
5374
|
+
const neg = n < bigint0$3;
|
|
5375
|
+
const a = neg ? -n : n;
|
|
5376
|
+
const ms = a / bigint1e6;
|
|
5377
|
+
const sec = ms / bigint1e3;
|
|
5378
|
+
const min = sec / bigint60;
|
|
5379
|
+
const hr = min / bigint60;
|
|
5380
|
+
const d = hr / bigint24;
|
|
5381
|
+
const sign = neg ? -1 : 1;
|
|
5382
|
+
return {
|
|
5383
|
+
days: sign * Number(d),
|
|
5384
|
+
hours: sign * Number(hr % bigint24),
|
|
5385
|
+
minutes: sign * Number(min % bigint60),
|
|
5386
|
+
seconds: sign * Number(sec % bigint60),
|
|
5387
|
+
millis: sign * Number(ms % bigint1e3),
|
|
5388
|
+
nanos: sign * Number(a % bigint1e6)
|
|
5389
|
+
};
|
|
5390
|
+
};
|
|
5391
|
+
/**
|
|
5392
|
+
* Converts a `Duration` to a human readable string.
|
|
5393
|
+
*
|
|
5394
|
+
* **Example** (Formatting durations)
|
|
5395
|
+
*
|
|
5396
|
+
* ```ts import.meta.vitest
|
|
5397
|
+
* import { Duration } from "effect"
|
|
5398
|
+
*
|
|
5399
|
+
* Duration.format(Duration.millis(1000)) // => "1s"
|
|
5400
|
+
* Duration.format(Duration.millis(1001)) // => "1s 1ms"
|
|
5401
|
+
* ```
|
|
5402
|
+
*
|
|
5403
|
+
* @stability stable
|
|
5404
|
+
* @category converting
|
|
5405
|
+
* @since 2.0.0
|
|
5406
|
+
*/
|
|
5407
|
+
const format$2 = (self) => {
|
|
5408
|
+
if (self.value._tag === "Infinity") return "Infinity";
|
|
5409
|
+
if (self.value._tag === "NegativeInfinity") return "-Infinity";
|
|
5410
|
+
if (isZero$1(self)) return "0";
|
|
5411
|
+
if (isNegative(self)) return "-" + format$2(abs(self));
|
|
5412
|
+
const fragments = parts(self);
|
|
5413
|
+
const pieces = [];
|
|
5414
|
+
if (fragments.days !== 0) pieces.push(`${fragments.days}d`);
|
|
5415
|
+
if (fragments.hours !== 0) pieces.push(`${fragments.hours}h`);
|
|
5416
|
+
if (fragments.minutes !== 0) pieces.push(`${fragments.minutes}m`);
|
|
5417
|
+
if (fragments.seconds !== 0) pieces.push(`${fragments.seconds}s`);
|
|
5418
|
+
if (fragments.millis !== 0) pieces.push(`${fragments.millis}ms`);
|
|
5419
|
+
if (fragments.nanos !== 0) pieces.push(`${fragments.nanos}ns`);
|
|
5420
|
+
return pieces.join(" ");
|
|
5421
|
+
};
|
|
5248
5422
|
//#endregion
|
|
5249
5423
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Scheduler.js
|
|
5250
5424
|
/**
|
|
@@ -7057,6 +7231,16 @@ const exitPrimitive = /*#__PURE__*/ makePrimitive$1({
|
|
|
7057
7231
|
/** @internal */
|
|
7058
7232
|
const timeoutOrElse$1 = /*#__PURE__*/ dual(2, (self, options) => flatMap$2(timeoutOption(self, options.duration), (option) => isNone(option) ? options.orElse() : succeed$6(option.value)));
|
|
7059
7233
|
/** @internal */
|
|
7234
|
+
const timeoutErrorFromDuration = (duration) => new TimeoutError(`Operation timed out after '${format$2(duration)}'`);
|
|
7235
|
+
/** @internal */
|
|
7236
|
+
const timeout$1 = /*#__PURE__*/ dual(2, (self, duration) => {
|
|
7237
|
+
const decoded = fromInputUnsafe$1(duration);
|
|
7238
|
+
return timeoutOrElse$1(self, {
|
|
7239
|
+
duration: decoded,
|
|
7240
|
+
orElse: () => fail$4(timeoutErrorFromDuration(decoded))
|
|
7241
|
+
});
|
|
7242
|
+
});
|
|
7243
|
+
/** @internal */
|
|
7060
7244
|
const timeoutOption = /*#__PURE__*/ dual(2, (self, duration) => raceFirst$1(asSome(self), as$1(sleep$1(duration), none())));
|
|
7061
7245
|
/** @internal */
|
|
7062
7246
|
const ScopeTypeId = "~effect/Scope";
|
|
@@ -7809,6 +7993,15 @@ const sleep$1 = (duration) => clockWith$1((clock) => clock.sleep(fromInputUnsafe
|
|
|
7809
7993
|
/** @internal */
|
|
7810
7994
|
const currentTimeMillis$1 = /*#__PURE__*/ clockWith$1((clock) => clock.currentTimeMillis);
|
|
7811
7995
|
/** @internal */
|
|
7996
|
+
const TimeoutErrorTypeId = "~effect/Cause/TimeoutError";
|
|
7997
|
+
/** @internal */
|
|
7998
|
+
var TimeoutError = class extends (/*#__PURE__*/ TaggedError$2("TimeoutError")) {
|
|
7999
|
+
[TimeoutErrorTypeId] = TimeoutErrorTypeId;
|
|
8000
|
+
constructor(message) {
|
|
8001
|
+
super({ message });
|
|
8002
|
+
}
|
|
8003
|
+
};
|
|
8004
|
+
/** @internal */
|
|
7812
8005
|
const IllegalArgumentErrorTypeId = "~effect/Cause/IllegalArgumentError";
|
|
7813
8006
|
/** @internal */
|
|
7814
8007
|
var IllegalArgumentError$1 = class extends (/*#__PURE__*/ TaggedError$2("IllegalArgumentError")) {
|
|
@@ -11846,6 +12039,45 @@ const ignore = ignore$1;
|
|
|
11846
12039
|
*/
|
|
11847
12040
|
const orElseSucceed = orElseSucceed$1;
|
|
11848
12041
|
/**
|
|
12042
|
+
* Adds a time limit to an effect, triggering a timeout if the effect exceeds
|
|
12043
|
+
* the duration.
|
|
12044
|
+
*
|
|
12045
|
+
* **When to use**
|
|
12046
|
+
*
|
|
12047
|
+
* Use when you need a timeout of an `Effect` to be represented as a typed
|
|
12048
|
+
* failure.
|
|
12049
|
+
*
|
|
12050
|
+
* **Details**
|
|
12051
|
+
*
|
|
12052
|
+
* The `timeout` function allows you to specify a time limit for an
|
|
12053
|
+
* effect's execution. If the effect does not complete within the given time, a
|
|
12054
|
+
* `TimeoutError` is raised. This can be useful for controlling how long your
|
|
12055
|
+
* program waits for a task to finish, ensuring that it doesn't hang
|
|
12056
|
+
* indefinitely if the task takes too long.
|
|
12057
|
+
*
|
|
12058
|
+
* **Gotchas**
|
|
12059
|
+
*
|
|
12060
|
+
* If the timeout wins, the source effect is interrupted.
|
|
12061
|
+
*
|
|
12062
|
+
* **Example** (Failing when work takes too long)
|
|
12063
|
+
*
|
|
12064
|
+
* ```ts import.meta.vitest
|
|
12065
|
+
* import { Effect } from "effect"
|
|
12066
|
+
*
|
|
12067
|
+
* const timedEffect = Effect.never.pipe(Effect.timeout(0))
|
|
12068
|
+
* const error = await Effect.runPromise(Effect.flip(timedEffect))
|
|
12069
|
+
* error._tag // => "TimeoutError"
|
|
12070
|
+
* ```
|
|
12071
|
+
*
|
|
12072
|
+
* @see {@link timeoutOption} for returning `Option.none` on timeout.
|
|
12073
|
+
* @see {@link timeoutOrElse} for a version that allows specifying both success and timeout handlers.
|
|
12074
|
+
*
|
|
12075
|
+
* @stability stable
|
|
12076
|
+
* @category delays & timeouts
|
|
12077
|
+
* @since 2.0.0
|
|
12078
|
+
*/
|
|
12079
|
+
const timeout = timeout$1;
|
|
12080
|
+
/**
|
|
11849
12081
|
* Applies a timeout to an effect, lazily evaluating `orElse` after interrupting
|
|
11850
12082
|
* the source if the timeout is reached.
|
|
11851
12083
|
*
|
|
@@ -18066,7 +18298,7 @@ const defaultLeafHook = (issue) => {
|
|
|
18066
18298
|
*/
|
|
18067
18299
|
const defaultCheckHook = (issue) => findMessage(issue.issue) ?? findMessage(issue);
|
|
18068
18300
|
function formatInput(issue) {
|
|
18069
|
-
return hasInput(issue) ? format$
|
|
18301
|
+
return hasInput(issue) ? format$3(issue.input) : void 0;
|
|
18070
18302
|
}
|
|
18071
18303
|
function findExpected(issue) {
|
|
18072
18304
|
const expected = issue.annotations?.expected;
|
|
@@ -18978,9 +19210,9 @@ const decodeJsonError = (input) => {
|
|
|
18978
19210
|
const encodeUnknownAsJson = (input) => {
|
|
18979
19211
|
try {
|
|
18980
19212
|
const json = formatJson(input);
|
|
18981
|
-
return json === void 0 ? format$
|
|
19213
|
+
return json === void 0 ? format$3(input) : JSON.parse(json);
|
|
18982
19214
|
} catch {
|
|
18983
|
-
return format$
|
|
19215
|
+
return format$3(input);
|
|
18984
19216
|
}
|
|
18985
19217
|
};
|
|
18986
19218
|
const encodeJsonError = (input, options, encodeDefect) => {
|
|
@@ -19346,7 +19578,7 @@ const Literal$1 = class extends ASTNodeImpl {
|
|
|
19346
19578
|
literal;
|
|
19347
19579
|
constructor(literal, annotations, checks, encoding, context) {
|
|
19348
19580
|
super(annotations, checks, encoding, context);
|
|
19349
|
-
if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw new Error(`A numeric literal must be finite, got ${format$
|
|
19581
|
+
if (typeof literal === "number" && !globalThis.Number.isFinite(literal)) throw new Error(`A numeric literal must be finite, got ${format$3(literal)}`);
|
|
19350
19582
|
this.literal = literal;
|
|
19351
19583
|
}
|
|
19352
19584
|
/** @internal */
|
|
@@ -25686,7 +25918,7 @@ function isPattern(regExp, annotations) {
|
|
|
25686
25918
|
const source = regExp.source;
|
|
25687
25919
|
const flags = regExp.flags;
|
|
25688
25920
|
const canExport = /^[dg]*uy?$/.test(flags);
|
|
25689
|
-
const runtimeRegExp = flags === "" ? `new RegExp(${format$
|
|
25921
|
+
const runtimeRegExp = flags === "" ? `new RegExp(${format$3(source)})` : `new RegExp(${format$3(source)}, ${format$3(flags)})`;
|
|
25690
25922
|
return isPattern$1(regExp, {
|
|
25691
25923
|
toJsonSchema: () => canExport ? { pattern: flags.endsWith("y") ? `^(?:${source})` : source } : [{}, true],
|
|
25692
25924
|
toCode: () => ({ runtime: `Schema.isPattern(${runtimeRegExp})` }),
|
|
@@ -27118,7 +27350,7 @@ const String$5 = /*#__PURE__*/ makePrimitive("String", (value) => succeed$3(valu
|
|
|
27118
27350
|
*/
|
|
27119
27351
|
const Choice = (choices) => {
|
|
27120
27352
|
const choiceMap = new Map(choices);
|
|
27121
|
-
const validChoices = choices.map(([key]) => format$
|
|
27353
|
+
const validChoices = choices.map(([key]) => format$3(key)).join(" | ");
|
|
27122
27354
|
const primitive = makePrimitive("Choice", (value) => {
|
|
27123
27355
|
if (choiceMap.has(value)) return succeed$3(choiceMap.get(value));
|
|
27124
27356
|
return fail$1(validChoices);
|
|
@@ -33032,7 +33264,7 @@ const runWith = (command, config) => {
|
|
|
33032
33264
|
};
|
|
33033
33265
|
//#endregion
|
|
33034
33266
|
//#region package.json
|
|
33035
|
-
var version = "0.
|
|
33267
|
+
var version = "0.10.0";
|
|
33036
33268
|
//#endregion
|
|
33037
33269
|
//#region ../../node_modules/.pnpm/effect@4.0.2/node_modules/effect/dist/Logger.js
|
|
33038
33270
|
const TypeId = "~effect/Logger";
|
|
@@ -33877,7 +34109,7 @@ var ParseOptions;
|
|
|
33877
34109
|
* 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.
|
|
33878
34110
|
* Therefore always check the errors list to find out if the input was valid.
|
|
33879
34111
|
*/
|
|
33880
|
-
function parse$
|
|
34112
|
+
function parse$2(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
33881
34113
|
let currentProperty = null;
|
|
33882
34114
|
let currentParent = [];
|
|
33883
34115
|
const previousParents = [];
|
|
@@ -34453,7 +34685,7 @@ var SyntaxKind;
|
|
|
34453
34685
|
* 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.
|
|
34454
34686
|
* Therefore, always check the errors list to find out if the input was valid.
|
|
34455
34687
|
*/
|
|
34456
|
-
const parse = parse$
|
|
34688
|
+
const parse$1 = parse$2;
|
|
34457
34689
|
/**
|
|
34458
34690
|
* 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.
|
|
34459
34691
|
*/
|
|
@@ -35989,6 +36221,7 @@ const Origin = String$6.check(isPattern(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/, {
|
|
|
35989
36221
|
const Ref = NonEmptyString;
|
|
35990
36222
|
const isPin = (ref) => /^[0-9a-f]{40}$/.test(ref);
|
|
35991
36223
|
const ProviderName = Literals$2([
|
|
36224
|
+
"files",
|
|
35992
36225
|
"vp",
|
|
35993
36226
|
"brew",
|
|
35994
36227
|
"cask",
|
|
@@ -36072,12 +36305,41 @@ const ReposSection = section({ cloneRoot: optionalKey(variants(withShorthand(Str
|
|
|
36072
36305
|
decode: (path) => ({ path }),
|
|
36073
36306
|
encode: (root) => root.path
|
|
36074
36307
|
}))).annotateKey({ description: `Where a Machine keeps its Managed Clones: a path, or a list of \`{ path, platforms?, machines? }\` where a Machine gets the first that targets it. \`~\` expands on that Machine. Default: \`${DEFAULT_CLONE_ROOT}\`.` }) }, Record(NonEmptyString, variants(TrackedRepo)), `Tracked Repos, keyed by name, which names the Managed Clone and the \`repos:<name>\` Item. Each is \`{ origin, ref, install, ... }\`, ${VARIANTS}.`);
|
|
36308
|
+
const HomePath = NonEmptyString.check(isPattern(/^(\/|~\/)./, {
|
|
36309
|
+
title: "path",
|
|
36310
|
+
description: "an absolute path, or one under ~"
|
|
36311
|
+
}));
|
|
36312
|
+
const OWN_DIRS = ["~/.config/fleetfizz", "~/.local/state/fleetfizz"];
|
|
36313
|
+
const SharedFilePath = HomePath.check(makeFilter((path) => {
|
|
36314
|
+
const p = path.replace(/\/+$/, "");
|
|
36315
|
+
const dir = OWN_DIRS.find((d) => p === "~" || d === p || d.startsWith(`${p}/`) || p.startsWith(`${d}/`));
|
|
36316
|
+
return dir === void 0 || `overlaps ${dir}, which a Ship writes itself`;
|
|
36317
|
+
}, {
|
|
36318
|
+
title: "Shared File path",
|
|
36319
|
+
description: "a path outside fleetfizz's own directories"
|
|
36320
|
+
}));
|
|
36321
|
+
const SharedFile = Struct({
|
|
36322
|
+
...Targeting,
|
|
36323
|
+
path: SharedFilePath.annotateKey({ description: "A file or directory, absolute or under `~`, which expands on each Machine." }),
|
|
36324
|
+
exclude: optionalKey(ArraySchema(NonEmptyString)).annotateKey({ description: "Globs inside a directory that are neither copied nor deleted. One without a `/` matches a name at any depth. `.DS_Store` and `._*` are always excluded. Every Variant of one Shared File has the same exclude." })
|
|
36325
|
+
});
|
|
36326
|
+
const sameExclude = (a, b) => [...new Set(a.exclude ?? [])].toSorted().join("\n") === [...new Set(b.exclude ?? [])].toSorted().join("\n");
|
|
36327
|
+
const FilesSection = section({}, Record(NonEmptyString, variants(withShorthand(SharedFile, SharedFilePath, {
|
|
36328
|
+
decode: (path) => ({ path }),
|
|
36329
|
+
encode: (file) => file.path
|
|
36330
|
+
}))).check(makeFilter((items) => {
|
|
36331
|
+
const badName = Object.keys(items).find((n) => !/^[A-Za-z0-9._-]+$/.test(n) || n === "." || n === "..");
|
|
36332
|
+
if (badName !== void 0) return `"${badName}": a Shared File's name uses only letters, digits, ., _ and -, and is not . or ..`;
|
|
36333
|
+
const name = Object.entries(items).find(([, item]) => Array.isArray(item) && item.some((v) => !sameExclude(v, item[0])))?.[0];
|
|
36334
|
+
return name === void 0 || `"${name}": every Variant needs the same exclude`;
|
|
36335
|
+
}, {
|
|
36336
|
+
title: "Shared Files",
|
|
36337
|
+
description: "names of letters, digits, ., _ and -, and one exclude across a Shared File's Variants"
|
|
36338
|
+
})), `Shared Files, keyed by name, which names the \`files:<name>\` Item. Each is a path, \`{ path, exclude?, platforms?, machines? }\`, ${VARIANTS}.`);
|
|
36075
36339
|
const FleetConfig = Struct({
|
|
36076
36340
|
$schema: optionalKey(String$6),
|
|
36077
|
-
shippedAt: optionalKey(String$6),
|
|
36078
|
-
shippedFrom: optionalKey(NonEmptyString),
|
|
36079
|
-
shippedTo: optionalKey(Record(NonEmptyString, String$6)),
|
|
36080
36341
|
machines: ArraySchema(NonEmptyString).check(isMinLength(1, { message: "list at least one Machine" }), isUnique({ message: "list each Machine once" })).annotateKey({ description: "The Fleet: each Machine's tailnet name. A tailnet member that is not listed is not part of the Fleet." }),
|
|
36342
|
+
files: optionalKey(FilesSection).annotateKey({ description: "Files and directories copied from the Starting Machine to every targeted Machine. A directory is mirrored." }),
|
|
36081
36343
|
repos: optionalKey(ReposSection).annotateKey({ description: "Each Tracked Repo is cloned on every targeted Machine and installed from its origin ref." }),
|
|
36082
36344
|
vp: optionalKey(VpSection).annotateKey({ description: "The vite-plus toolchain." }),
|
|
36083
36345
|
brew: optionalKey(BrewSection).annotateKey({ description: "Homebrew Formulae. Needs Homebrew on the Machine; a Ship never installs it." }),
|
|
@@ -36133,10 +36395,17 @@ const repeatedKeys = (text) => {
|
|
|
36133
36395
|
if (tree !== void 0) visit(tree, []);
|
|
36134
36396
|
return problems;
|
|
36135
36397
|
};
|
|
36398
|
+
const STAMPS = [
|
|
36399
|
+
"shippedAt",
|
|
36400
|
+
"shippedFrom",
|
|
36401
|
+
"shippedTo"
|
|
36402
|
+
];
|
|
36136
36403
|
const movedKeys = (parsed) => {
|
|
36137
36404
|
if (typeof parsed !== "object" || parsed === null) return [];
|
|
36138
36405
|
const config = parsed;
|
|
36406
|
+
const stamps = STAMPS.filter((key) => key in parsed);
|
|
36139
36407
|
return [
|
|
36408
|
+
...stamps.length > 0 ? [`${stamps.join(", ")}: no longer written; a Ship keeps them in its Install Record now, so delete them`] : [],
|
|
36140
36409
|
...typeof config.machines === "object" && config.machines !== null && !Array.isArray(config.machines) ? ["machines is now a list of names, and machines.<name>.clones is repos.cloneRoot"] : [],
|
|
36141
36410
|
...Array.isArray(config.repos) ? ["repos is now { items: { <name>: { origin, ref, install } } }, keyed by each repo's name"] : [],
|
|
36142
36411
|
...typeof config.vp === "object" && config.vp !== null && "globals" in config.vp ? ["vp.globals is now vp.items"] : []
|
|
@@ -36144,7 +36413,7 @@ const movedKeys = (parsed) => {
|
|
|
36144
36413
|
};
|
|
36145
36414
|
const parseConfig = fn("parseConfig")(function* (path, text) {
|
|
36146
36415
|
const parseErrors = [];
|
|
36147
|
-
const parsed = parse(text, parseErrors, { allowTrailingComma: true });
|
|
36416
|
+
const parsed = parse$1(text, parseErrors, { allowTrailingComma: true });
|
|
36148
36417
|
if (parseErrors.length > 0) return yield* new ConfigError({
|
|
36149
36418
|
path,
|
|
36150
36419
|
problems: parseErrors.map((e) => `line ${lineOf(text, e.offset)}: ${printParseErrorCode(e.error)}`)
|
|
@@ -36171,49 +36440,26 @@ const loadConfig = fn("loadConfig")(function* (path) {
|
|
|
36171
36440
|
const text = yield* fs.readFileString(path);
|
|
36172
36441
|
return yield* parseConfig(path, text);
|
|
36173
36442
|
});
|
|
36174
|
-
|
|
36175
|
-
|
|
36176
|
-
|
|
36177
|
-
|
|
36178
|
-
};
|
|
36179
|
-
const
|
|
36180
|
-
|
|
36181
|
-
|
|
36182
|
-
|
|
36183
|
-
|
|
36184
|
-
]
|
|
36185
|
-
|
|
36186
|
-
|
|
36187
|
-
|
|
36188
|
-
|
|
36189
|
-
|
|
36190
|
-
|
|
36191
|
-
...options,
|
|
36192
|
-
getInsertionIndex: insertAfter(STAMP_KEYS.slice(0, STAMP_KEYS.indexOf(key)))
|
|
36193
|
-
}));
|
|
36194
|
-
const previous = parse(text, void 0, { allowTrailingComma: true });
|
|
36195
|
-
const fleet = machinesOf(previous);
|
|
36196
|
-
const shippedTo = {
|
|
36197
|
-
...Object.fromEntries(Object.entries(shippedToOf(previous)).filter(([name]) => fleet.includes(name))),
|
|
36198
|
-
...Object.fromEntries([...stamp.reached, ...stamp.own === void 0 ? [] : [stamp.own]].map((name) => [name, stamp.shippedAt]))
|
|
36199
|
-
};
|
|
36200
|
-
let out = set(text, "shippedAt", stamp.shippedAt);
|
|
36201
|
-
out = set(out, "shippedFrom", stamp.shippedFrom);
|
|
36202
|
-
return set(out, "shippedTo", shippedTo);
|
|
36203
|
-
};
|
|
36204
|
-
const machinesOf = (config) => {
|
|
36205
|
-
if (typeof config !== "object" || config === null || !("machines" in config)) return [];
|
|
36206
|
-
const value = config.machines;
|
|
36207
|
-
return Array.isArray(value) ? value.filter((name) => typeof name === "string") : [];
|
|
36208
|
-
};
|
|
36209
|
-
const shippedToOf = (config) => {
|
|
36210
|
-
if (typeof config !== "object" || config === null || !("shippedTo" in config)) return {};
|
|
36211
|
-
const value = config.shippedTo;
|
|
36212
|
-
if (typeof value !== "object" || value === null) return {};
|
|
36213
|
-
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
36443
|
+
const ConfigRecord = Struct({
|
|
36444
|
+
ship: NonEmptyString,
|
|
36445
|
+
sha: String$6,
|
|
36446
|
+
reached: Record(NonEmptyString, String$6)
|
|
36447
|
+
});
|
|
36448
|
+
const decodeConfigRecord = decodeUnknownOption(ConfigRecord);
|
|
36449
|
+
const decodeConfigRecordJson = decodeUnknownOption(fromJsonString(ConfigRecord));
|
|
36450
|
+
const encodeConfigRecord = encodeSync(fromJsonString(ConfigRecord));
|
|
36451
|
+
const shaOf = (text) => promise(() => globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))).pipe(map$5((digest) => [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("")));
|
|
36452
|
+
const reachedAfter = (previous, at, machines, fleet) => ({
|
|
36453
|
+
...Object.fromEntries(Object.entries(previous).filter(([name]) => fleet.includes(name))),
|
|
36454
|
+
...Object.fromEntries(machines.map((name) => [name, at]))
|
|
36455
|
+
});
|
|
36456
|
+
const mergeReached = (all) => {
|
|
36457
|
+
const merged = {};
|
|
36458
|
+
for (const reached of all) for (const [name, at] of Object.entries(reached)) if (isNewer(at, merged[name])) merged[name] = at;
|
|
36459
|
+
return merged;
|
|
36214
36460
|
};
|
|
36215
36461
|
const isNewer = (remote, local) => remote !== void 0 && (local === void 0 || remote > local);
|
|
36216
|
-
const
|
|
36462
|
+
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;
|
|
36217
36463
|
//#endregion
|
|
36218
36464
|
//#region ../../packages/remote/src/shell.ts
|
|
36219
36465
|
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
@@ -36232,6 +36478,7 @@ const prelude = [
|
|
|
36232
36478
|
`export PATH="$PATH:/opt/homebrew/bin:/home/linuxbrew/.linuxbrew/bin"`,
|
|
36233
36479
|
`ff_json() { printf '%s' "$1" | sed -e 's/\\\\/\\\\\\\\/g' -e 's/"/\\\\"/g' | tr -d '\\n'; }`,
|
|
36234
36480
|
`ff_state() { printf 'ff-state %s\\n' "$1"; }`,
|
|
36481
|
+
`ff_sha() { if command -v shasum >/dev/null 2>&1; then shasum -a 256; else sha256sum; fi | cut -d" " -f1; }`,
|
|
36235
36482
|
`ff_at() { ff_state "{\\"provider\\":\\"$(ff_json "$1")\\",\\"fact\\":\\"at\\",\\"item\\":\\"$(ff_json "$2")\\",\\"at\\":\\"$(ff_json "$3")\\"}"; }`
|
|
36236
36483
|
].join("\n");
|
|
36237
36484
|
const fingerprintFn = [
|
|
@@ -36250,6 +36497,7 @@ const LOGS_DIR = `${STATE_DIR}/logs`;
|
|
|
36250
36497
|
const CONFIG_DIR_SH = "\"${XDG_CONFIG_HOME:-$HOME/.config}/fleetfizz\"";
|
|
36251
36498
|
const recordDir = (provider) => `${quotePath(RECORDS_DIR)}/${quote(provider)}`;
|
|
36252
36499
|
const recordFile = (provider, item) => `${recordDir(provider)}/${quote(`${item}.json`)}`;
|
|
36500
|
+
const CONFIG_RECORD = `${RECORDS_DIR}/config.json`;
|
|
36253
36501
|
const LEGACY_RECORD_PROVIDER = "repos";
|
|
36254
36502
|
const recordFact = (provider, item) => [
|
|
36255
36503
|
`ff_r=${recordFile(provider, item)}`,
|
|
@@ -36266,7 +36514,7 @@ const checkScript = (input) => {
|
|
|
36266
36514
|
lines.push(provider.observe(items, input.scope));
|
|
36267
36515
|
if (provider.rule === "record") for (const item of items) lines.push(...recordFact(provider.name, item.name));
|
|
36268
36516
|
}
|
|
36269
|
-
lines.push(`ff_c=${CONFIG_DIR_SH}/${quote(CONFIG_FILE)}`, `if [ -f "$ff_c" ]; then ff_state "{\\"fact\\":\\"config\\",\\"present\\":true,\\"
|
|
36517
|
+
lines.push(`ff_c=${CONFIG_DIR_SH}/${quote(CONFIG_FILE)}`, `if [ -f "$ff_c" ]; then ff_b=$(base64 < "$ff_c" | tr -d '\\n'); ff_state "{\\"fact\\":\\"config\\",\\"present\\":true,\\"sha\\":\\"$(printf '%s' "$ff_b" | base64 -d | ff_sha)\\",\\"base64\\":\\"$ff_b\\"}"; else ff_state '{"fact":"config","present":false}'; fi`, `ff_r=${quotePath(CONFIG_RECORD)}`, `if [ -f "$ff_r" ]; then ff_state "{\\"fact\\":\\"configRecord\\",\\"record\\":$(tr -d '\\n' < "$ff_r")}"; fi`);
|
|
36270
36518
|
lines.push(`ff_state '{"fact":"end"}'`);
|
|
36271
36519
|
return lines.join("\n");
|
|
36272
36520
|
};
|
|
@@ -36283,7 +36531,7 @@ const StepSkip = Struct({
|
|
|
36283
36531
|
id: String$6,
|
|
36284
36532
|
reason: String$6
|
|
36285
36533
|
});
|
|
36286
|
-
const decodeFact$
|
|
36534
|
+
const decodeFact$4 = decodeUnknownResult(fromJsonString(Fact));
|
|
36287
36535
|
const decodeStart = decodeUnknownResult(fromJsonString(StepStart));
|
|
36288
36536
|
const decodeDone = decodeUnknownResult(fromJsonString(StepDone));
|
|
36289
36537
|
const decodeSkip = decodeUnknownResult(fromJsonString(StepSkip));
|
|
@@ -36293,7 +36541,7 @@ const DONE = "ff-done ";
|
|
|
36293
36541
|
const SKIP = "ff-skip ";
|
|
36294
36542
|
const parseLine = (line) => {
|
|
36295
36543
|
if (line.startsWith(STATE)) {
|
|
36296
|
-
const result = decodeFact$
|
|
36544
|
+
const result = decodeFact$4(line.slice(9));
|
|
36297
36545
|
if (result._tag === "Success") return {
|
|
36298
36546
|
kind: "state",
|
|
36299
36547
|
fact: result.success
|
|
@@ -36339,12 +36587,14 @@ const marked = (id, run, failed = []) => [
|
|
|
36339
36587
|
doneLine(id, true),
|
|
36340
36588
|
"fi"
|
|
36341
36589
|
];
|
|
36342
|
-
const configStep = () => {
|
|
36590
|
+
const configStep = (record) => {
|
|
36343
36591
|
const dir = CONFIG_DIR_SH;
|
|
36344
36592
|
const write = (variable, file) => `printf '%s' "$${variable}" | base64 -d > ${dir}/${quote(`${file}.tmp`)} && mv -f ${dir}/${quote(`${file}.tmp`)} ${dir}/${quote(file)}`;
|
|
36345
|
-
|
|
36593
|
+
const recordFile = quotePath(CONFIG_RECORD);
|
|
36594
|
+
const recordTmp = quotePath(`${CONFIG_RECORD}.tmp`);
|
|
36595
|
+
return marked(CONFIG_STEP, [`( mkdir -p ${dir} ${quotePath(RECORDS_DIR)} && ${write("ff_config", CONFIG_FILE)} && ${write("ff_schema", SCHEMA_FILE)} && printf '%s\\n' ${quote(record)} > ${recordTmp} && mv -f ${recordTmp} ${recordFile} )`]);
|
|
36346
36596
|
};
|
|
36347
|
-
const providerSteps = (plan) => {
|
|
36597
|
+
const providerSteps = (plan, offsets) => {
|
|
36348
36598
|
if (plan.decisions.length === 0 && plan.steps.length === 0) return [];
|
|
36349
36599
|
const held = `ff_held_${plan.name}`;
|
|
36350
36600
|
const items = [...new Set(plan.steps.flatMap((step) => step.items))];
|
|
@@ -36353,6 +36603,7 @@ const providerSteps = (plan) => {
|
|
|
36353
36603
|
`if [ -n "${s.items.map((item) => `\${${flag(item)}:-}`).join("")}" ]; then`,
|
|
36354
36604
|
skipLine(s.id, "'an earlier step failed'"),
|
|
36355
36605
|
"else",
|
|
36606
|
+
...(s.input ?? []).map((_, i) => `ff_input_${i}="$ff_in_${(offsets.get(s) ?? 0) + i}"`),
|
|
36356
36607
|
...marked(s.id, s.run, [...s.items.map((item) => `${flag(item)}=1`), `${held}=1`]),
|
|
36357
36608
|
"fi"
|
|
36358
36609
|
];
|
|
@@ -36368,10 +36619,21 @@ const providerSteps = (plan) => {
|
|
|
36368
36619
|
"fi"
|
|
36369
36620
|
];
|
|
36370
36621
|
};
|
|
36371
|
-
const
|
|
36622
|
+
const inputsOf = (plan) => plan.providers.flatMap((p) => p.steps.flatMap((step) => step.input ?? []));
|
|
36623
|
+
const inputOffsets = (plan) => {
|
|
36624
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
36625
|
+
let next = 0;
|
|
36626
|
+
for (const step of plan.providers.flatMap((p) => p.steps)) {
|
|
36627
|
+
offsets.set(step, next);
|
|
36628
|
+
next += step.input?.length ?? 0;
|
|
36629
|
+
}
|
|
36630
|
+
return offsets;
|
|
36631
|
+
};
|
|
36632
|
+
const applyStdin = (token, config, schema, plan) => [
|
|
36372
36633
|
token ?? "",
|
|
36373
36634
|
encodeBase64(config),
|
|
36374
|
-
encodeBase64(schema)
|
|
36635
|
+
encodeBase64(schema),
|
|
36636
|
+
...inputsOf(plan).map((input) => input.reveal())
|
|
36375
36637
|
];
|
|
36376
36638
|
const encodeBase64 = (text) => {
|
|
36377
36639
|
const bytes = new TextEncoder().encode(text);
|
|
@@ -36379,18 +36641,30 @@ const encodeBase64 = (text) => {
|
|
|
36379
36641
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
36380
36642
|
return btoa(binary);
|
|
36381
36643
|
};
|
|
36382
|
-
const applyScript = (plan) =>
|
|
36644
|
+
const applyScript = (plan, configRecord) => {
|
|
36645
|
+
const offsets = inputOffsets(plan);
|
|
36646
|
+
return [
|
|
36647
|
+
prelude,
|
|
36648
|
+
fingerprintFn,
|
|
36649
|
+
`ff_lit() { printf '%s' "$1"; }`,
|
|
36650
|
+
`IFS= read -r ff_token || ff_token=""`,
|
|
36651
|
+
`IFS= read -r ff_config || ff_config=""`,
|
|
36652
|
+
`IFS= read -r ff_schema || ff_schema=""`,
|
|
36653
|
+
...inputsOf(plan).map((_, i) => `IFS= read -r ff_in_${i} || exit 1`),
|
|
36654
|
+
`ff_git() { if [ -n "$ff_token" ]; then FF_TOKEN="$ff_token" git -c credential.helper= -c 'credential.helper=!f() { echo username=x-access-token; echo "password=$FF_TOKEN"; }; f' "$@"; else git "$@"; fi; }`,
|
|
36655
|
+
...configStep(configRecord),
|
|
36656
|
+
...plan.providers.flatMap((p) => providerSteps(p, offsets)),
|
|
36657
|
+
doneLine("end", true)
|
|
36658
|
+
].join("\n");
|
|
36659
|
+
};
|
|
36660
|
+
const stepScript = (step) => [
|
|
36383
36661
|
prelude,
|
|
36384
36662
|
fingerprintFn,
|
|
36385
36663
|
`ff_lit() { printf '%s' "$1"; }`,
|
|
36386
|
-
`IFS= read -r
|
|
36387
|
-
|
|
36388
|
-
`IFS= read -r ff_schema || ff_schema=""`,
|
|
36389
|
-
`ff_git() { if [ -n "$ff_token" ]; then FF_TOKEN="$ff_token" git -c credential.helper= -c 'credential.helper=!f() { echo username=x-access-token; echo "password=$FF_TOKEN"; }; f' "$@"; else git "$@"; fi; }`,
|
|
36390
|
-
...configStep(),
|
|
36391
|
-
...plan.providers.flatMap(providerSteps),
|
|
36392
|
-
doneLine("end", true)
|
|
36664
|
+
...(step.input ?? []).flatMap((_, i) => [`IFS= read -r ff_in_${i} || ff_in_${i}=""`, `ff_input_${i}="$ff_in_${i}"`]),
|
|
36665
|
+
...marked(step.id, step.run)
|
|
36393
36666
|
].join("\n");
|
|
36667
|
+
const stepStdin = (step) => (step.input ?? []).map((i) => i.reveal());
|
|
36394
36668
|
//#endregion
|
|
36395
36669
|
//#region ../../packages/remote/src/tailnet.ts
|
|
36396
36670
|
const StatusNode = Struct({
|
|
@@ -36544,6 +36818,20 @@ const resolveVersions = fn("resolveVersions")(function* (packages) {
|
|
|
36544
36818
|
const entries = yield* forEach$1(unique, (pkg) => resolveOne(pkg).pipe(map$5((v) => [pkg, v])), { concurrency: 8 });
|
|
36545
36819
|
return Object.fromEntries(entries);
|
|
36546
36820
|
});
|
|
36821
|
+
//#endregion
|
|
36822
|
+
//#region src/configRecord.ts
|
|
36823
|
+
const recordPath = () => CONFIG_RECORD.replace(/^~/, process.env.HOME ?? "~");
|
|
36824
|
+
const readConfigRecord = gen(function* () {
|
|
36825
|
+
const text = yield* (yield* FileSystem).readFileString(recordPath()).pipe(option);
|
|
36826
|
+
return getOrUndefined$1(flatMap$3(text, decodeConfigRecordJson));
|
|
36827
|
+
});
|
|
36828
|
+
const writeConfigRecord = fn("writeConfigRecord")(function* (json) {
|
|
36829
|
+
const fs = yield* FileSystem;
|
|
36830
|
+
const path = recordPath();
|
|
36831
|
+
yield* fs.makeDirectory(path.slice(0, path.lastIndexOf("/")), { recursive: true });
|
|
36832
|
+
yield* fs.writeFileString(`${path}.tmp`, `${json}\n`);
|
|
36833
|
+
yield* fs.rename(`${path}.tmp`, path);
|
|
36834
|
+
});
|
|
36547
36835
|
var LogsError = class extends Error {
|
|
36548
36836
|
name = "LogsError";
|
|
36549
36837
|
};
|
|
@@ -36616,7 +36904,7 @@ const logs = fn("logs")(function* (flags) {
|
|
|
36616
36904
|
for (const id of local.toReversed()) yield* log(id);
|
|
36617
36905
|
return;
|
|
36618
36906
|
}
|
|
36619
|
-
const id = flags.id ?? (yield* latestShip(local, self
|
|
36907
|
+
const id = flags.id ?? (yield* latestShip(local, self));
|
|
36620
36908
|
const ship = parseShipId(id);
|
|
36621
36909
|
if (ship === void 0) return yield* fail$1(new LogsError(`not a Ship id: ${id} (fizz logs --list shows them)`));
|
|
36622
36910
|
let missing = false;
|
|
@@ -36627,17 +36915,13 @@ const logs = fn("logs")(function* (flags) {
|
|
|
36627
36915
|
});
|
|
36628
36916
|
if (missing) return yield* fail$1(new LogsError(`no log for ${id} on ${ship.from}; a Machine keeps its last 20`));
|
|
36629
36917
|
});
|
|
36630
|
-
const latestShip = fn("latestShip")(function* (local, self
|
|
36918
|
+
const latestShip = fn("latestShip")(function* (local, self) {
|
|
36631
36919
|
const newest = local.at(-1);
|
|
36632
|
-
const
|
|
36633
|
-
const from =
|
|
36634
|
-
const at = Date.parse(
|
|
36920
|
+
const record = yield* readConfigRecord;
|
|
36921
|
+
const from = parseShipId(record?.ship ?? "")?.from;
|
|
36922
|
+
const at = self === void 0 ? Infinity : Date.parse(record?.reached[self] ?? "");
|
|
36635
36923
|
const newestStarted = parseShipId(newest ?? "")?.started ?? -Infinity;
|
|
36636
|
-
if (from !== void 0 && from !== self && at > newestStarted)
|
|
36637
|
-
const there = (yield* shipIds(yield* targetOf(from))).filter((id) => (parseShipId(id)?.started ?? Infinity) <= at).at(-1);
|
|
36638
|
-
if (there === void 0) return yield* fail$1(new LogsError(`the last Ship to reach this Machine (from ${from} at ${config?.shippedAt}) has no log there`));
|
|
36639
|
-
return there;
|
|
36640
|
-
}
|
|
36924
|
+
if (record !== void 0 && from !== void 0 && from !== self && at > newestStarted) return record.ship;
|
|
36641
36925
|
if (newest === void 0) return yield* fail$1(new LogsError("no Ship Log on this Machine yet"));
|
|
36642
36926
|
return newest;
|
|
36643
36927
|
});
|
|
@@ -64886,9 +65170,25 @@ const QUESTION_CHOICES = {
|
|
|
64886
65170
|
"downgrade"
|
|
64887
65171
|
],
|
|
64888
65172
|
config: ["overwrite", "stop"],
|
|
64889
|
-
mode: ["managed", "skip"]
|
|
65173
|
+
mode: ["managed", "skip"],
|
|
65174
|
+
file: ["overwrite", "skip"]
|
|
64890
65175
|
};
|
|
64891
65176
|
const isQuestionKind = (kind) => kind in QUESTION_CHOICES;
|
|
65177
|
+
var Hidden = class {
|
|
65178
|
+
#value;
|
|
65179
|
+
constructor(value) {
|
|
65180
|
+
this.#value = value;
|
|
65181
|
+
}
|
|
65182
|
+
reveal() {
|
|
65183
|
+
return this.#value;
|
|
65184
|
+
}
|
|
65185
|
+
toJSON() {
|
|
65186
|
+
return "[hidden]";
|
|
65187
|
+
}
|
|
65188
|
+
toString() {
|
|
65189
|
+
return "[hidden]";
|
|
65190
|
+
}
|
|
65191
|
+
};
|
|
64892
65192
|
const rangeQuestion = (subject, installed, range, bumpPath) => ({
|
|
64893
65193
|
kind: "range",
|
|
64894
65194
|
subject,
|
|
@@ -65022,8 +65322,13 @@ const EngineFact = Union([
|
|
|
65022
65322
|
Struct({
|
|
65023
65323
|
fact: Literal("config"),
|
|
65024
65324
|
present: Boolean$4,
|
|
65325
|
+
sha: optionalKey(String$6),
|
|
65025
65326
|
base64: optionalKey(String$6)
|
|
65026
65327
|
}),
|
|
65328
|
+
Struct({
|
|
65329
|
+
fact: Literal("configRecord"),
|
|
65330
|
+
record: Unknown
|
|
65331
|
+
}),
|
|
65027
65332
|
Struct({
|
|
65028
65333
|
fact: Literal("record"),
|
|
65029
65334
|
provider: String$6,
|
|
@@ -65066,9 +65371,18 @@ const assembleState = (facts) => {
|
|
|
65066
65371
|
};
|
|
65067
65372
|
break;
|
|
65068
65373
|
case "config":
|
|
65069
|
-
if (f.present && f.base64 !== void 0) state = {
|
|
65374
|
+
if (f.present && f.base64 !== void 0 && f.sha !== void 0) state = {
|
|
65375
|
+
...state,
|
|
65376
|
+
config: {
|
|
65377
|
+
text: decodeBase64(f.base64),
|
|
65378
|
+
sha: f.sha
|
|
65379
|
+
}
|
|
65380
|
+
};
|
|
65381
|
+
break;
|
|
65382
|
+
case "configRecord":
|
|
65383
|
+
state = {
|
|
65070
65384
|
...state,
|
|
65071
|
-
|
|
65385
|
+
configRecord: f.record
|
|
65072
65386
|
};
|
|
65073
65387
|
break;
|
|
65074
65388
|
case "record": state = {
|
|
@@ -65167,7 +65481,7 @@ const LedgerState = Struct({
|
|
|
65167
65481
|
started: Number$2,
|
|
65168
65482
|
ended: optionalKey(Number$2)
|
|
65169
65483
|
});
|
|
65170
|
-
const itemsOf = (p) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
65484
|
+
const itemsOf = (p, config) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
65171
65485
|
const base = {
|
|
65172
65486
|
name: address(pp.name, d.item),
|
|
65173
65487
|
kind: pp.rule,
|
|
@@ -65210,7 +65524,8 @@ const itemsOf = (p) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
|
65210
65524
|
})), {
|
|
65211
65525
|
name: CONFIG_STEP,
|
|
65212
65526
|
kind: "config",
|
|
65213
|
-
|
|
65527
|
+
...config,
|
|
65528
|
+
changes: config.from !== config.to,
|
|
65214
65529
|
status: "idle",
|
|
65215
65530
|
steps: [{
|
|
65216
65531
|
id: CONFIG_STEP,
|
|
@@ -65306,10 +65621,10 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
65306
65621
|
}
|
|
65307
65622
|
})).pipe(andThen(logInfo(`resolving ${n} items`))),
|
|
65308
65623
|
resolved: update(({ resolving: _, ...state }) => state).pipe(andThen(logInfo("resolved"))),
|
|
65309
|
-
plan: (name, p) => machine(name, (m) => ({
|
|
65624
|
+
plan: (name, p, config) => machine(name, (m) => ({
|
|
65310
65625
|
...m,
|
|
65311
65626
|
status: "planned",
|
|
65312
|
-
items: itemsOf(p)
|
|
65627
|
+
items: itemsOf(p, config)
|
|
65313
65628
|
})).pipe(andThen(forEach$1(p.providers.flatMap((pp) => pp.decisions.map((d) => ({
|
|
65314
65629
|
pp,
|
|
65315
65630
|
d
|
|
@@ -67367,7 +67682,7 @@ const SPINNER = cli_spinners_default.dots;
|
|
|
67367
67682
|
const runGlyph = (live, now) => (live ? SPINNER.frames[Math.floor(now / SPINNER.interval) % SPINNER.frames.length] : void 0) ?? "▸";
|
|
67368
67683
|
const unchanged = (i) => ({
|
|
67369
67684
|
text: "",
|
|
67370
|
-
detail: i.kind === "record" ? "up to date" : void 0
|
|
67685
|
+
detail: i.kind === "record" || i.kind === "config" ? "up to date" : void 0
|
|
67371
67686
|
});
|
|
67372
67687
|
const statusOf = (i, dryRun, glyph) => {
|
|
67373
67688
|
switch (i.status) {
|
|
@@ -67386,11 +67701,11 @@ const statusOf = (i, dryRun, glyph) => {
|
|
|
67386
67701
|
detail: i.detail
|
|
67387
67702
|
};
|
|
67388
67703
|
case "ok":
|
|
67704
|
+
if (!i.changes) return unchanged(i);
|
|
67389
67705
|
if (i.kind === "config") return {
|
|
67390
67706
|
text: "✓ copied",
|
|
67391
|
-
tone: "
|
|
67707
|
+
tone: "ok"
|
|
67392
67708
|
};
|
|
67393
|
-
if (!i.changes) return unchanged(i);
|
|
67394
67709
|
if (i.kind === "record") return i.from !== void 0 && i.from === i.to ? {
|
|
67395
67710
|
text: "✓ rebuilt",
|
|
67396
67711
|
tone: "ok",
|
|
@@ -67794,7 +68109,7 @@ const report = fn("report")(function* () {
|
|
|
67794
68109
|
]
|
|
67795
68110
|
})));
|
|
67796
68111
|
});
|
|
67797
|
-
const decodeFact$
|
|
68112
|
+
const decodeFact$3 = decodeUnknownOption(Union([Struct({
|
|
67798
68113
|
fact: Literal("brew"),
|
|
67799
68114
|
version: NullOr(String$6)
|
|
67800
68115
|
}), AtFact]));
|
|
@@ -67846,7 +68161,7 @@ function homebrew(name) {
|
|
|
67846
68161
|
let brew;
|
|
67847
68162
|
const at = /* @__PURE__ */ new Map();
|
|
67848
68163
|
for (const raw of facts) {
|
|
67849
|
-
const fact = decodeFact$
|
|
68164
|
+
const fact = decodeFact$3(raw);
|
|
67850
68165
|
if (fact._tag === "None") continue;
|
|
67851
68166
|
const f = fact.value;
|
|
67852
68167
|
if (f.fact === "brew") brew = f.version ?? void 0;
|
|
@@ -67923,6 +68238,532 @@ function homebrew(name) {
|
|
|
67923
68238
|
}
|
|
67924
68239
|
const brew = homebrew("brew");
|
|
67925
68240
|
const cask = homebrew("cask");
|
|
68241
|
+
//#endregion
|
|
68242
|
+
//#region ../../packages/providers/src/files/index.ts
|
|
68243
|
+
const ALWAYS_EXCLUDED = [".DS_Store", "._*"];
|
|
68244
|
+
const SIZE_LIMIT = 10485760;
|
|
68245
|
+
const decodeFact$2 = decodeUnknownOption(Union([Struct({
|
|
68246
|
+
fact: Literal("entry"),
|
|
68247
|
+
item: String$6,
|
|
68248
|
+
path: String$6,
|
|
68249
|
+
entry: String$6,
|
|
68250
|
+
size: Number$2
|
|
68251
|
+
}), Struct({
|
|
68252
|
+
fact: Literal("openssl"),
|
|
68253
|
+
present: Boolean$4
|
|
68254
|
+
})]));
|
|
68255
|
+
const FileRecord = Struct({
|
|
68256
|
+
at: String$6,
|
|
68257
|
+
entries: Record(String$6, String$6)
|
|
68258
|
+
});
|
|
68259
|
+
const decodeRecord$1 = decodeUnknownOption(FileRecord);
|
|
68260
|
+
const jsonRecord = encodeSync(fromJsonString(FileRecord));
|
|
68261
|
+
const fingerprint = (entries) => [...entries].toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([rel, entry]) => `${rel}\t${entry}\n`).join("");
|
|
68262
|
+
const short$1 = (entries) => (string$1(fingerprint(entries)) >>> 0).toString(16).padStart(8, "0").slice(0, 7);
|
|
68263
|
+
const recordEntries = (record) => new Map(Object.entries(record.entries));
|
|
68264
|
+
const kindOf = (entries) => ({
|
|
68265
|
+
f: "a file",
|
|
68266
|
+
d: "a directory",
|
|
68267
|
+
l: "a symlink",
|
|
68268
|
+
"?": "something it cannot copy"
|
|
68269
|
+
})[entries.get(".")?.[0] ?? ""] ?? "missing";
|
|
68270
|
+
const depth = (rel) => rel === "." ? 0 : rel.split("/").length;
|
|
68271
|
+
const changedPaths = (a, b) => {
|
|
68272
|
+
const paths = [.../* @__PURE__ */ new Set([...a.keys(), ...b.keys()])].filter((rel) => a.get(rel) !== b.get(rel)).toSorted();
|
|
68273
|
+
const shown = paths.slice(0, 3).join(", ");
|
|
68274
|
+
return paths.length > 3 ? `${shown} and ${paths.length - 3} more` : shown;
|
|
68275
|
+
};
|
|
68276
|
+
const SHELL_FNS = [
|
|
68277
|
+
`ff_mode() { stat -c %a "$1" 2>/dev/null || stat -f %Lp "$1"; }`,
|
|
68278
|
+
`ff_entry() {`,
|
|
68279
|
+
` if [ -L "$3" ]; then ff_e="l $(readlink "$3")"; ff_s=0`,
|
|
68280
|
+
` elif [ -d "$3" ]; then if [ -r "$3" ] && [ -x "$3" ]; then ff_e="d $(ff_mode "$3")"; else ff_e="?"; fi; ff_s=0`,
|
|
68281
|
+
` elif [ -f "$3" ]; then if [ -r "$3" ]; then ff_e="f $(ff_mode "$3") $(ff_sha < "$3")"; ff_s=$(wc -c < "$3" | tr -d " "); else ff_e="?"; ff_s=0; fi`,
|
|
68282
|
+
` else ff_e="?"; ff_s=0; fi`,
|
|
68283
|
+
` ff_state "{\\"provider\\":\\"files\\",\\"fact\\":\\"entry\\",\\"item\\":\\"$(ff_json "$1")\\",\\"path\\":\\"$(ff_json "$2")\\",\\"entry\\":\\"$(ff_json "$ff_e")\\",\\"size\\":$ff_s}"`,
|
|
68284
|
+
`}`
|
|
68285
|
+
].join("\n");
|
|
68286
|
+
const pruneExpr = (exclude) => [...ALWAYS_EXCLUDED, ...exclude].map((pattern) => pattern.includes("/") ? `-path ${quote(`./${pattern.replace(/^(\.?\/)+|\/+$/g, "")}`)}` : `-name ${quote(pattern)}`).join(" -o ");
|
|
68287
|
+
const observe$1 = (items) => {
|
|
68288
|
+
if (items.length === 0) return "";
|
|
68289
|
+
return [
|
|
68290
|
+
SHELL_FNS,
|
|
68291
|
+
`if command -v openssl >/dev/null 2>&1; then ff_o=true; else ff_o=false; fi`,
|
|
68292
|
+
`ff_state "{\\"provider\\":\\"files\\",\\"fact\\":\\"openssl\\",\\"present\\":$ff_o}"`,
|
|
68293
|
+
...items.flatMap((item) => {
|
|
68294
|
+
const name = quote(item.name);
|
|
68295
|
+
return [
|
|
68296
|
+
`ff_p=${quotePath(item.path)}`,
|
|
68297
|
+
`if [ -d "$ff_p" ] && [ ! -L "$ff_p" ]; then`,
|
|
68298
|
+
` ff_entry ${name} . "$ff_p"`,
|
|
68299
|
+
` (cd "$ff_p" && find . -mindepth 1 \\( ${pruneExpr(item.exclude)} \\) -prune -o -print) | while IFS= read -r ff_rel; do ff_rel=\${ff_rel#./}; ff_entry ${name} "$ff_rel" "$ff_p/$ff_rel"; done`,
|
|
68300
|
+
`elif [ -e "$ff_p" ] || [ -L "$ff_p" ]; then`,
|
|
68301
|
+
` ff_entry ${name} . "$ff_p"`,
|
|
68302
|
+
"fi"
|
|
68303
|
+
];
|
|
68304
|
+
})
|
|
68305
|
+
].join("\n");
|
|
68306
|
+
};
|
|
68307
|
+
const parse = (facts, records) => {
|
|
68308
|
+
const items = /* @__PURE__ */ new Map();
|
|
68309
|
+
let openssl = false;
|
|
68310
|
+
for (const raw of facts) {
|
|
68311
|
+
const fact = decodeFact$2(raw);
|
|
68312
|
+
if (fact._tag === "None") continue;
|
|
68313
|
+
const f = fact.value;
|
|
68314
|
+
if (f.fact === "openssl") {
|
|
68315
|
+
openssl = f.present;
|
|
68316
|
+
continue;
|
|
68317
|
+
}
|
|
68318
|
+
const item = items.get(f.item) ?? {
|
|
68319
|
+
entries: /* @__PURE__ */ new Map(),
|
|
68320
|
+
size: 0
|
|
68321
|
+
};
|
|
68322
|
+
item.entries.set(f.path, f.entry);
|
|
68323
|
+
item.size += f.size;
|
|
68324
|
+
items.set(f.item, item);
|
|
68325
|
+
}
|
|
68326
|
+
const decoded = /* @__PURE__ */ new Map();
|
|
68327
|
+
for (const [name, raw] of Object.entries(records)) {
|
|
68328
|
+
const record = decodeRecord$1(raw);
|
|
68329
|
+
if (record._tag === "Some") decoded.set(name, record.value);
|
|
68330
|
+
}
|
|
68331
|
+
return {
|
|
68332
|
+
items,
|
|
68333
|
+
records: decoded,
|
|
68334
|
+
openssl
|
|
68335
|
+
};
|
|
68336
|
+
};
|
|
68337
|
+
const at = (rel) => rel === "." ? `"$ff_p"` : `"$ff_p"/${quote(rel)}`;
|
|
68338
|
+
const readContents = (files) => gen(function* () {
|
|
68339
|
+
const contents = /* @__PURE__ */ new Map();
|
|
68340
|
+
const script = files.map(({ abs }, n) => `ff_b=$(base64 < ${abs}) || exit 1; printf 'ff-file ${n} %s\\n' "$(printf '%s' "$ff_b" | tr -d '\\n')"`).join("\n");
|
|
68341
|
+
return (yield* runScript({
|
|
68342
|
+
name: "local",
|
|
68343
|
+
local: true
|
|
68344
|
+
}, script, [], (line) => sync(() => {
|
|
68345
|
+
const match = /^ff-file (\d+) (.*)$/.exec(line);
|
|
68346
|
+
if (match !== null) contents.set(Number(match[1]), new Hidden(match[2] ?? ""));
|
|
68347
|
+
}))) === 0 && contents.size === files.length ? contents : void 0;
|
|
68348
|
+
});
|
|
68349
|
+
const resolveSources = fn("resolveSources")(function* (items, scope) {
|
|
68350
|
+
const facts = [];
|
|
68351
|
+
yield* runScript({
|
|
68352
|
+
name: scope.machine.name,
|
|
68353
|
+
local: true
|
|
68354
|
+
}, checkScript({
|
|
68355
|
+
scope,
|
|
68356
|
+
providers: [{
|
|
68357
|
+
provider: files,
|
|
68358
|
+
items
|
|
68359
|
+
}]
|
|
68360
|
+
}), [], (line) => sync(() => {
|
|
68361
|
+
const parsed = parseLine(line);
|
|
68362
|
+
if (parsed.kind === "state") facts.push(parsed.fact);
|
|
68363
|
+
}));
|
|
68364
|
+
const state = assembleState(facts);
|
|
68365
|
+
const observed = parse(state.facts.files ?? [], state.records.files ?? {});
|
|
68366
|
+
const sources = /* @__PURE__ */ new Map();
|
|
68367
|
+
for (const item of items) {
|
|
68368
|
+
const copy = observed.items.get(item.name);
|
|
68369
|
+
if (!state.complete) {
|
|
68370
|
+
sources.set(item.name, {
|
|
68371
|
+
item,
|
|
68372
|
+
kind: "unreadable",
|
|
68373
|
+
reason: "could not read it here"
|
|
68374
|
+
});
|
|
68375
|
+
continue;
|
|
68376
|
+
}
|
|
68377
|
+
if (copy === void 0) {
|
|
68378
|
+
sources.set(item.name, {
|
|
68379
|
+
item,
|
|
68380
|
+
kind: "missing"
|
|
68381
|
+
});
|
|
68382
|
+
continue;
|
|
68383
|
+
}
|
|
68384
|
+
const unreadable = [...copy.entries].find(([, entry]) => entry === "?")?.[0];
|
|
68385
|
+
if (unreadable !== void 0) {
|
|
68386
|
+
sources.set(item.name, {
|
|
68387
|
+
item,
|
|
68388
|
+
kind: "unreadable",
|
|
68389
|
+
reason: `cannot read or copy ${unreadable === "." ? item.path : unreadable} here`
|
|
68390
|
+
});
|
|
68391
|
+
continue;
|
|
68392
|
+
}
|
|
68393
|
+
if (copy.size > 10485760) {
|
|
68394
|
+
sources.set(item.name, {
|
|
68395
|
+
item,
|
|
68396
|
+
kind: "unreadable",
|
|
68397
|
+
reason: `${(copy.size / 1024 / 1024).toFixed(1)} MB, over the ${SIZE_LIMIT / 1024 / 1024} MB limit`
|
|
68398
|
+
});
|
|
68399
|
+
continue;
|
|
68400
|
+
}
|
|
68401
|
+
const list = [...copy.entries].flatMap(([rel, entry]) => entry.startsWith("f ") ? [{
|
|
68402
|
+
rel,
|
|
68403
|
+
abs: rel === "." ? quotePath(item.path) : `${quotePath(item.path)}/${quote(rel)}`
|
|
68404
|
+
}] : []);
|
|
68405
|
+
const read = yield* readContents(list);
|
|
68406
|
+
if (read === void 0) {
|
|
68407
|
+
sources.set(item.name, {
|
|
68408
|
+
item,
|
|
68409
|
+
kind: "unreadable",
|
|
68410
|
+
reason: "could not read it here"
|
|
68411
|
+
});
|
|
68412
|
+
continue;
|
|
68413
|
+
}
|
|
68414
|
+
const record = observed.records.get(item.name);
|
|
68415
|
+
sources.set(item.name, {
|
|
68416
|
+
item,
|
|
68417
|
+
kind: "present",
|
|
68418
|
+
entries: copy.entries,
|
|
68419
|
+
...record === void 0 ? {} : { record },
|
|
68420
|
+
contents: new Map(list.map(({ rel }, n) => [rel, read.get(n) ?? new Hidden("")]))
|
|
68421
|
+
});
|
|
68422
|
+
}
|
|
68423
|
+
return {
|
|
68424
|
+
sources,
|
|
68425
|
+
openssl: observed.openssl
|
|
68426
|
+
};
|
|
68427
|
+
});
|
|
68428
|
+
const copiesOf = (checked, from) => {
|
|
68429
|
+
const copies = /* @__PURE__ */ new Map();
|
|
68430
|
+
for (const { machine, items, facts, records } of checked) {
|
|
68431
|
+
if (machine.name === from) continue;
|
|
68432
|
+
const observed = parse(facts, records);
|
|
68433
|
+
for (const item of items) {
|
|
68434
|
+
const copy = observed.items.get(item.name);
|
|
68435
|
+
if (copy === void 0 || copy.size > 10485760 || [...copy.entries.values()].includes("?")) continue;
|
|
68436
|
+
const record = observed.records.get(item.name);
|
|
68437
|
+
copies.set(item.name, [...copies.get(item.name) ?? [], {
|
|
68438
|
+
machine: machine.name,
|
|
68439
|
+
item,
|
|
68440
|
+
entries: copy.entries,
|
|
68441
|
+
...record === void 0 ? {} : { record },
|
|
68442
|
+
openssl: observed.openssl
|
|
68443
|
+
}]);
|
|
68444
|
+
}
|
|
68445
|
+
}
|
|
68446
|
+
return copies;
|
|
68447
|
+
};
|
|
68448
|
+
const PULL = "pull:";
|
|
68449
|
+
const askWhy = (current, record, source, from) => {
|
|
68450
|
+
if (record === void 0) return "differs, and no Ship wrote it";
|
|
68451
|
+
const recorded = recordEntries(record);
|
|
68452
|
+
if (fingerprint(recorded) !== fingerprint(current)) return `was edited there${current.size === 1 && recorded.size === 1 ? "" : ` (${changedPaths(recorded, current)})`}`;
|
|
68453
|
+
if (fingerprint(recorded) !== (source.record === void 0 ? void 0 : fingerprint(recordEntries(source.record))) && record.at > (source.record?.at ?? "")) return `came from a Ship at ${record.at.slice(0, 16).replace("T", " ")} that ${from} missed`;
|
|
68454
|
+
};
|
|
68455
|
+
const fileQuestion = (subject, found, problem, pull, from) => ({
|
|
68456
|
+
kind: "file",
|
|
68457
|
+
subject,
|
|
68458
|
+
found,
|
|
68459
|
+
problem,
|
|
68460
|
+
choices: [
|
|
68461
|
+
{
|
|
68462
|
+
value: "overwrite",
|
|
68463
|
+
title: "overwrite it with this Machine's copy"
|
|
68464
|
+
},
|
|
68465
|
+
...pull === void 0 || !pull.openssl ? [] : [{
|
|
68466
|
+
value: `${PULL}${pull.machine}`,
|
|
68467
|
+
title: `use ${pull.machine}'s copy (${short$1(pull.entries)}) on ${from} instead`
|
|
68468
|
+
}],
|
|
68469
|
+
{
|
|
68470
|
+
value: "skip",
|
|
68471
|
+
title: "skip on these Machines (leave as is, shown as skipped)"
|
|
68472
|
+
}
|
|
68473
|
+
],
|
|
68474
|
+
default: "skip"
|
|
68475
|
+
});
|
|
68476
|
+
const missingQuestion = (subject, found, copies, from) => ({
|
|
68477
|
+
kind: "file",
|
|
68478
|
+
subject,
|
|
68479
|
+
found,
|
|
68480
|
+
problem: `is not on ${from}`,
|
|
68481
|
+
choices: [...Map.groupBy(copies.filter((c) => c.openssl), (c) => fingerprint(c.entries)).values().flatMap((same) => {
|
|
68482
|
+
const [first] = same;
|
|
68483
|
+
return first === void 0 ? [] : [{
|
|
68484
|
+
value: `${PULL}${first.machine}`,
|
|
68485
|
+
title: `pull ${short$1(first.entries)} from ${same.map((c) => c.machine).join(", ")}`
|
|
68486
|
+
}];
|
|
68487
|
+
}), {
|
|
68488
|
+
value: "skip",
|
|
68489
|
+
title: "skip it everywhere"
|
|
68490
|
+
}],
|
|
68491
|
+
default: "skip"
|
|
68492
|
+
});
|
|
68493
|
+
const decideFile = (item, observed, resolved, ctx) => {
|
|
68494
|
+
const { from } = resolved;
|
|
68495
|
+
const source = resolved.sources.get(item.name);
|
|
68496
|
+
if (source === void 0) return {
|
|
68497
|
+
kind: "skip",
|
|
68498
|
+
item: item.name,
|
|
68499
|
+
reason: `${from} has no Variant for it; ship from a Machine it targets`
|
|
68500
|
+
};
|
|
68501
|
+
const subject = address("files", item.name);
|
|
68502
|
+
const answer = ctx.answer("file", subject);
|
|
68503
|
+
const pulling = answer?.startsWith(PULL) === true;
|
|
68504
|
+
const copies = resolved.copies.get(item.name) ?? [];
|
|
68505
|
+
const mine = copies.find((c) => c.machine === ctx.machine.name);
|
|
68506
|
+
if (source.kind === "missing") {
|
|
68507
|
+
const notHere = {
|
|
68508
|
+
kind: "skip",
|
|
68509
|
+
item: item.name,
|
|
68510
|
+
reason: `not on ${from}`
|
|
68511
|
+
};
|
|
68512
|
+
if (mine === void 0) return notHere;
|
|
68513
|
+
if (pulling) return {
|
|
68514
|
+
...notHere,
|
|
68515
|
+
at: short$1(mine.entries),
|
|
68516
|
+
reason: `pulling it from ${answer?.slice(5)}`
|
|
68517
|
+
};
|
|
68518
|
+
if (answer === "skip" || !resolved.openssl || !copies.some((c) => c.openssl)) {
|
|
68519
|
+
const without = resolved.openssl ? copies.filter((c) => !c.openssl).map((c) => c.machine) : [from];
|
|
68520
|
+
return {
|
|
68521
|
+
...notHere,
|
|
68522
|
+
at: short$1(mine.entries),
|
|
68523
|
+
...answer !== "skip" && without.length > 0 ? { reason: `not on ${from}; install openssl on ${without.join(", ")} to pull it` } : {}
|
|
68524
|
+
};
|
|
68525
|
+
}
|
|
68526
|
+
return {
|
|
68527
|
+
kind: "ask",
|
|
68528
|
+
item: item.name,
|
|
68529
|
+
at: short$1(mine.entries),
|
|
68530
|
+
question: missingQuestion(subject, short$1(mine.entries), copies, from)
|
|
68531
|
+
};
|
|
68532
|
+
}
|
|
68533
|
+
if (source.kind === "unreadable") return {
|
|
68534
|
+
kind: "fail",
|
|
68535
|
+
item: item.name,
|
|
68536
|
+
reason: `${from}: ${source.reason}`
|
|
68537
|
+
};
|
|
68538
|
+
const current = observed.items.get(item.name)?.entries;
|
|
68539
|
+
const record = observed.records.get(item.name);
|
|
68540
|
+
const ours = fingerprint(source.entries);
|
|
68541
|
+
const change = {
|
|
68542
|
+
item,
|
|
68543
|
+
entries: source.entries,
|
|
68544
|
+
...current === void 0 ? {} : { current },
|
|
68545
|
+
contents: source.contents,
|
|
68546
|
+
at: resolved.at
|
|
68547
|
+
};
|
|
68548
|
+
const recorded = record === void 0 ? void 0 : fingerprint(recordEntries(record));
|
|
68549
|
+
if (ctx.machine.name === from || current !== void 0 && fingerprint(current) === ours) return {
|
|
68550
|
+
kind: "leave",
|
|
68551
|
+
item: item.name,
|
|
68552
|
+
at: short$1(source.entries),
|
|
68553
|
+
...recorded === ours ? {} : { upkeep: change }
|
|
68554
|
+
};
|
|
68555
|
+
const to = short$1(source.entries);
|
|
68556
|
+
if (current === void 0) return {
|
|
68557
|
+
kind: "change",
|
|
68558
|
+
item: item.name,
|
|
68559
|
+
to,
|
|
68560
|
+
change
|
|
68561
|
+
};
|
|
68562
|
+
if (kindOf(current) !== kindOf(source.entries)) return {
|
|
68563
|
+
kind: "fail",
|
|
68564
|
+
item: item.name,
|
|
68565
|
+
at: short$1(current),
|
|
68566
|
+
reason: `${kindOf(current)} here, ${kindOf(source.entries)} on ${from}`
|
|
68567
|
+
};
|
|
68568
|
+
const fromAt = short$1(current);
|
|
68569
|
+
const why = askWhy(current, record, source, from);
|
|
68570
|
+
if (why === void 0) return {
|
|
68571
|
+
kind: "change",
|
|
68572
|
+
item: item.name,
|
|
68573
|
+
from: fromAt,
|
|
68574
|
+
to,
|
|
68575
|
+
change
|
|
68576
|
+
};
|
|
68577
|
+
if (pulling) return {
|
|
68578
|
+
kind: "skip",
|
|
68579
|
+
item: item.name,
|
|
68580
|
+
at: fromAt,
|
|
68581
|
+
reason: `pulling it from ${answer?.slice(5)}`
|
|
68582
|
+
};
|
|
68583
|
+
if (answer === "skip") return {
|
|
68584
|
+
kind: "skip",
|
|
68585
|
+
item: item.name,
|
|
68586
|
+
at: fromAt,
|
|
68587
|
+
reason: why
|
|
68588
|
+
};
|
|
68589
|
+
if (answer === "overwrite") return {
|
|
68590
|
+
kind: "change",
|
|
68591
|
+
item: item.name,
|
|
68592
|
+
from: fromAt,
|
|
68593
|
+
to,
|
|
68594
|
+
reason: why,
|
|
68595
|
+
change
|
|
68596
|
+
};
|
|
68597
|
+
return {
|
|
68598
|
+
kind: "ask",
|
|
68599
|
+
item: item.name,
|
|
68600
|
+
at: fromAt,
|
|
68601
|
+
question: fileQuestion(subject, fromAt, why, resolved.openssl ? mine : void 0, from)
|
|
68602
|
+
};
|
|
68603
|
+
};
|
|
68604
|
+
const CIPHER = "-aes-256-cbc -pbkdf2 -a -A";
|
|
68605
|
+
const writeRecord = (name, entries, at) => {
|
|
68606
|
+
const file = recordFile("files", name);
|
|
68607
|
+
const temporary = `${recordDir("files")}/${quote(`${name}.json.tmp`)}`;
|
|
68608
|
+
return [
|
|
68609
|
+
`mkdir -p ${recordDir("files")}`,
|
|
68610
|
+
`ff_lit ${quote(jsonRecord({
|
|
68611
|
+
at,
|
|
68612
|
+
entries: Object.fromEntries(entries)
|
|
68613
|
+
}))} > ${temporary}`,
|
|
68614
|
+
`mv -f ${temporary} ${file}`
|
|
68615
|
+
];
|
|
68616
|
+
};
|
|
68617
|
+
const copyStep = (change) => {
|
|
68618
|
+
const { item, entries, current, contents } = change;
|
|
68619
|
+
const typeOf = (entry) => entry?.[0];
|
|
68620
|
+
const deletes = [...current ?? []].filter(([rel, entry]) => {
|
|
68621
|
+
const ours = entries.get(rel);
|
|
68622
|
+
return ours === void 0 || typeOf(ours) !== typeOf(entry);
|
|
68623
|
+
}).toSorted(([a], [b]) => depth(b) - depth(a));
|
|
68624
|
+
const writes = [...entries].filter(([rel, entry]) => current?.get(rel) !== entry).toSorted(([a], [b]) => depth(a) - depth(b));
|
|
68625
|
+
const input = change.key === void 0 ? [] : [change.key];
|
|
68626
|
+
const decode = change.key === void 0 ? "base64 -d" : `FF_KEY="$ff_input_0" openssl enc -d ${CIPHER} -pass env:FF_KEY`;
|
|
68627
|
+
const write = ([rel, entry]) => {
|
|
68628
|
+
const [type, mode = ""] = entry.split(" ");
|
|
68629
|
+
if (type === "d") return [`(umask 077 && mkdir -p ${at(rel)})`];
|
|
68630
|
+
if (type === "l") return [`rm -f ${at(rel)}`, `ln -s -- ${quote(entry.slice(2))} ${at(rel)}`];
|
|
68631
|
+
const n = input.push(contents.get(rel) ?? new Hidden("")) - 1;
|
|
68632
|
+
return [
|
|
68633
|
+
`ff_t=$(mktemp "$(dirname ${at(rel)})/.fftmp.XXXXXX")`,
|
|
68634
|
+
`printf '%s' "$ff_input_${n}" | ${decode} > "$ff_t"`,
|
|
68635
|
+
`chmod ${mode} "$ff_t"`,
|
|
68636
|
+
`mv -f "$ff_t" ${at(rel)}`,
|
|
68637
|
+
`ff_t=""`
|
|
68638
|
+
];
|
|
68639
|
+
};
|
|
68640
|
+
const modes = writes.filter(([, entry]) => entry.startsWith("d ")).toReversed().map(([rel, entry]) => `chmod ${entry.slice(2)} ${at(rel)}`);
|
|
68641
|
+
const links = [...entries].filter(([, entry]) => entry.startsWith("l ")).map(([rel]) => rel);
|
|
68642
|
+
const reached = short$1(entries);
|
|
68643
|
+
return {
|
|
68644
|
+
id: `files:${item.name}:copy`,
|
|
68645
|
+
items: [item.name],
|
|
68646
|
+
doing: "copying",
|
|
68647
|
+
run: [
|
|
68648
|
+
`ff_p=${quotePath(item.path)}`,
|
|
68649
|
+
"(",
|
|
68650
|
+
" set -e",
|
|
68651
|
+
` ff_t=""`,
|
|
68652
|
+
` trap '[ -z "$ff_t" ] || rm -f "$ff_t"' EXIT`,
|
|
68653
|
+
` trap 'exit 1' HUP INT TERM`,
|
|
68654
|
+
...[
|
|
68655
|
+
`mkdir -p "$(dirname "$ff_p")"`,
|
|
68656
|
+
...deletes.map(([rel, entry]) => entry.startsWith("d ") ? `rmdir ${at(rel)}` : `rm -f ${at(rel)}`),
|
|
68657
|
+
...writes.flatMap(write),
|
|
68658
|
+
...modes,
|
|
68659
|
+
...writeRecord(item.name, entries, change.at)
|
|
68660
|
+
].map((l) => ` ${l}`),
|
|
68661
|
+
")",
|
|
68662
|
+
`ff_rc=$?`,
|
|
68663
|
+
`if [ "$ff_rc" -eq 0 ]; then`,
|
|
68664
|
+
` ff_dl=""`,
|
|
68665
|
+
...links.map((rel) => ` [ -e ${at(rel)} ] || ff_dl="$ff_dl, "${quote(rel)}`),
|
|
68666
|
+
` if [ -n "$ff_dl" ]; then ff_at files ${quote(item.name)} "${reached}, dangling link: \${ff_dl#, }"; else ff_at files ${quote(item.name)} ${reached}; fi`,
|
|
68667
|
+
"fi",
|
|
68668
|
+
`(exit "$ff_rc")`
|
|
68669
|
+
],
|
|
68670
|
+
input
|
|
68671
|
+
};
|
|
68672
|
+
};
|
|
68673
|
+
const pull = (name, machine, resolved) => gen(function* () {
|
|
68674
|
+
const source = resolved.sources.get(name);
|
|
68675
|
+
const copy = resolved.copies.get(name)?.find((c) => c.machine === machine);
|
|
68676
|
+
if (source === void 0 || copy === void 0 || source.kind === "unreadable") return { error: `cannot pull ${machine}'s copy here` };
|
|
68677
|
+
const key = [...globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(32))].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
68678
|
+
const rels = [...copy.entries].flatMap(([rel, entry]) => entry.startsWith("f ") ? [rel] : []);
|
|
68679
|
+
const contents = /* @__PURE__ */ new Map();
|
|
68680
|
+
let finished = false;
|
|
68681
|
+
const code = yield* runScript({
|
|
68682
|
+
name: machine,
|
|
68683
|
+
local: false
|
|
68684
|
+
}, [
|
|
68685
|
+
prelude,
|
|
68686
|
+
`IFS= read -r FF_KEY || FF_KEY=""`,
|
|
68687
|
+
"export FF_KEY",
|
|
68688
|
+
`ff_p=${quotePath(copy.item.path)}`,
|
|
68689
|
+
...rels.map((rel, n) => `ff_c=$(openssl enc ${CIPHER} -pass env:FF_KEY -in ${at(rel)}) || exit 1; printf 'ff-file ${n} %s\\n' "$ff_c"`),
|
|
68690
|
+
`printf 'ff-pulled\\n'`
|
|
68691
|
+
].join("\n"), [key], (line) => sync(() => {
|
|
68692
|
+
const match = /^ff-file (\d+) (.*)$/.exec(line);
|
|
68693
|
+
if (match !== null) contents.set(Number(match[1]), new Hidden(match[2] ?? ""));
|
|
68694
|
+
else if (line === "ff-pulled") finished = true;
|
|
68695
|
+
}));
|
|
68696
|
+
if (!finished || contents.size !== rels.length) return { error: code === 255 ? `could not reach ${machine}` : `could not read its copy on ${machine}` };
|
|
68697
|
+
const step = copyStep({
|
|
68698
|
+
item: source.item,
|
|
68699
|
+
entries: copy.entries,
|
|
68700
|
+
...source.kind === "present" ? { current: source.entries } : {},
|
|
68701
|
+
contents: new Map(rels.map((rel, n) => [rel, contents.get(n) ?? new Hidden("")])),
|
|
68702
|
+
at: copy.record?.at ?? resolved.at,
|
|
68703
|
+
key: new Hidden(key)
|
|
68704
|
+
});
|
|
68705
|
+
let written = false;
|
|
68706
|
+
yield* runScript({
|
|
68707
|
+
name: resolved.from,
|
|
68708
|
+
local: true
|
|
68709
|
+
}, stepScript(step), stepStdin(step), (line) => sync(() => {
|
|
68710
|
+
const parsed = parseLine(line);
|
|
68711
|
+
if (parsed.kind === "done" && parsed.done.id === step.id) written = parsed.done.ok;
|
|
68712
|
+
}));
|
|
68713
|
+
return written ? { done: `pulled ${short$1(copy.entries)} from ${machine}` } : { error: `could not write ${machine}'s copy on ${resolved.from}` };
|
|
68714
|
+
}).pipe(catch_((e) => succeed$3({ error: e.message })));
|
|
68715
|
+
const recordStep = (change) => ({
|
|
68716
|
+
id: `files:${change.item.name}:record`,
|
|
68717
|
+
items: [change.item.name],
|
|
68718
|
+
doing: "recording",
|
|
68719
|
+
run: [
|
|
68720
|
+
"(",
|
|
68721
|
+
" set -e",
|
|
68722
|
+
...writeRecord(change.item.name, change.entries, change.at).map((l) => ` ${l}`),
|
|
68723
|
+
")"
|
|
68724
|
+
]
|
|
68725
|
+
});
|
|
68726
|
+
const files = {
|
|
68727
|
+
name: "files",
|
|
68728
|
+
rule: "record",
|
|
68729
|
+
dependsOn: [],
|
|
68730
|
+
items: (_section, listed) => listed.map(({ name, def }) => ({
|
|
68731
|
+
name,
|
|
68732
|
+
path: def.path,
|
|
68733
|
+
exclude: def.exclude ?? []
|
|
68734
|
+
})),
|
|
68735
|
+
observe: (items) => observe$1(items),
|
|
68736
|
+
parse,
|
|
68737
|
+
resolve: (items, { starting: all, checked }) => gen(function* () {
|
|
68738
|
+
const at = formatIso(yield* now);
|
|
68739
|
+
const needed = new Set(items.map((item) => item.name));
|
|
68740
|
+
const starting = {
|
|
68741
|
+
...all,
|
|
68742
|
+
items: all.items.filter((item) => needed.has(item.name))
|
|
68743
|
+
};
|
|
68744
|
+
const { sources, openssl } = starting.items.length === 0 ? {
|
|
68745
|
+
sources: /* @__PURE__ */ new Map(),
|
|
68746
|
+
openssl: false
|
|
68747
|
+
} : yield* resolveSources(starting.items, starting).pipe(timeout("2 minutes"), orElseSucceed(() => ({
|
|
68748
|
+
sources: new Map(starting.items.map((item) => [item.name, {
|
|
68749
|
+
item,
|
|
68750
|
+
kind: "unreadable",
|
|
68751
|
+
reason: "could not read it here"
|
|
68752
|
+
}])),
|
|
68753
|
+
openssl: false
|
|
68754
|
+
})));
|
|
68755
|
+
return {
|
|
68756
|
+
from: starting.machine.name,
|
|
68757
|
+
at,
|
|
68758
|
+
sources,
|
|
68759
|
+
copies: copiesOf(checked, starting.machine.name),
|
|
68760
|
+
openssl
|
|
68761
|
+
};
|
|
68762
|
+
}),
|
|
68763
|
+
decide: ({ items, observed, resolved, ctx }) => items.map((item) => decideFile(item, observed, resolved, ctx)),
|
|
68764
|
+
act: (decisions) => decisions.flatMap((d) => d.kind === "change" ? [copyStep(d.change)] : d.kind === "leave" && d.upkeep !== void 0 ? [recordStep(d.upkeep)] : []),
|
|
68765
|
+
adopt: (item, answer, resolved) => answer.startsWith(PULL) ? pull(item, answer.slice(5), resolved) : void 0
|
|
68766
|
+
};
|
|
67926
68767
|
const decodeFact$1 = decodeUnknownOption(Union([
|
|
67927
68768
|
Struct({
|
|
67928
68769
|
fact: Literal("cloneRoot"),
|
|
@@ -68353,6 +69194,7 @@ const decideRanged = (item, observed, resolved, ctx) => {
|
|
|
68353
69194
|
//#endregion
|
|
68354
69195
|
//#region ../../packages/providers/src/index.ts
|
|
68355
69196
|
const PROVIDERS = [
|
|
69197
|
+
files,
|
|
68356
69198
|
{
|
|
68357
69199
|
name: "vp",
|
|
68358
69200
|
rule: "range",
|
|
@@ -68760,7 +69602,7 @@ const askQuestions = fn("askQuestions")(function* (input) {
|
|
|
68760
69602
|
yield* input.ledger.note("answer", "not asked (dry run)");
|
|
68761
69603
|
continue;
|
|
68762
69604
|
}
|
|
68763
|
-
const choices = question.choices.map((c) => ({
|
|
69605
|
+
const choices = [...new Map(group.flatMap((a) => a.question.choices).map((c) => [c.value, c])).values()].map((c) => ({
|
|
68764
69606
|
value: c.value,
|
|
68765
69607
|
title: c.value === "bump" && bumps.length > 0 ? `bump the config to ${bumps.map((b) => bumpedRange(b.range, b.newest)).join(" and ")}` : c.title
|
|
68766
69608
|
}));
|
|
@@ -68797,27 +69639,43 @@ const unshippable = (config, tailnet, only, source) => {
|
|
|
68797
69639
|
const outsideFleet = only.filter((name) => !fleet.has(name));
|
|
68798
69640
|
if (outsideFleet.length > 0) return `not in machines in ${source}: ${outsideFleet.join(", ")}`;
|
|
68799
69641
|
};
|
|
68800
|
-
const
|
|
69642
|
+
const minute = (iso) => iso?.slice(0, 16).replace("T", " ") ?? "an unknown time";
|
|
69643
|
+
const staleConfigs = fn("staleConfigs")(function* (text, base, own, checked, ledger) {
|
|
68801
69644
|
const copies = [];
|
|
68802
69645
|
for (const { machine, state } of checked) {
|
|
68803
|
-
if (state.config === void 0) continue;
|
|
68804
|
-
const parsed = yield* result(parseConfig(machine.name, state.config));
|
|
68805
|
-
if (parsed._tag === "Failure")
|
|
68806
|
-
|
|
69646
|
+
if (machine.name === own || state.config === void 0 || state.config.text === text) continue;
|
|
69647
|
+
const parsed = yield* result(parseConfig(machine.name, state.config.text));
|
|
69648
|
+
if (parsed._tag === "Failure") {
|
|
69649
|
+
yield* ledger.note("info", `the config on ${machine.name} does not parse; it will be overwritten`);
|
|
69650
|
+
continue;
|
|
69651
|
+
}
|
|
69652
|
+
const record = getOrUndefined$1(decodeConfigRecord(state.configRecord));
|
|
69653
|
+
const why = staleWhy({
|
|
68807
69654
|
machine: machine.name,
|
|
69655
|
+
sha: state.config.sha,
|
|
69656
|
+
record
|
|
69657
|
+
}, base);
|
|
69658
|
+
if (why === void 0) continue;
|
|
69659
|
+
copies.push({
|
|
69660
|
+
machine: machine.name,
|
|
69661
|
+
text: state.config.text,
|
|
69662
|
+
sha: state.config.sha,
|
|
68808
69663
|
config: parsed.success,
|
|
68809
|
-
|
|
69664
|
+
record,
|
|
69665
|
+
why: why === "unrecorded" ? "differs, and no Ship wrote it" : why === "edited" ? "was edited there" : `got a Ship from ${parseShipId(record?.ship ?? "")?.from ?? "another Machine"} at ${minute(record?.reached[machine.name])} that missed this Machine`
|
|
68810
69666
|
});
|
|
68811
69667
|
}
|
|
68812
|
-
return [...Map.groupBy(
|
|
69668
|
+
return [...Map.groupBy(copies, (c) => c.text)].flatMap(([text, group]) => {
|
|
69669
|
+
const [first] = group;
|
|
68813
69670
|
if (first === void 0) return [];
|
|
68814
|
-
const
|
|
68815
|
-
const machines = [machine, ...rest.map((c) => c.machine)];
|
|
69671
|
+
const machines = group.map((c) => c.machine);
|
|
68816
69672
|
return [{
|
|
68817
69673
|
text,
|
|
68818
|
-
|
|
69674
|
+
sha: first.sha,
|
|
69675
|
+
config: first.config,
|
|
68819
69676
|
machines,
|
|
68820
|
-
|
|
69677
|
+
reached: mergeReached(group.flatMap((c) => c.record === void 0 ? [] : [c.record.reached])),
|
|
69678
|
+
description: `the config on ${machines.join(", ")} ${first.why}`
|
|
68821
69679
|
}];
|
|
68822
69680
|
});
|
|
68823
69681
|
});
|
|
@@ -68832,10 +69690,10 @@ const ship = fn("ship")(function* (flags) {
|
|
|
68832
69690
|
yield* logInfo(`Ship ${id}: fleetfizz ${flags.version}, node ${process.version} on ${process.platform}`);
|
|
68833
69691
|
yield* logInfo(`fizz ${process.argv.slice(2).join(" ")}`);
|
|
68834
69692
|
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)" : ""}`);
|
|
68835
|
-
while ((yield* shipPass(flags, tailnet, hidden)) === "adopted");
|
|
69693
|
+
while ((yield* shipPass(flags, tailnet, hidden, id)) === "adopted");
|
|
68836
69694
|
}).pipe(onExit((exit) => isSuccess(exit) ? logInfo("the Ship ended") : logError("the Ship stopped", exit.cause)), provide(layer$1(logger === void 0 ? [] : [logger])), provideService(MinimumLogLevel, "Debug"));
|
|
68837
69695
|
}, scoped);
|
|
68838
|
-
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
69696
|
+
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
68839
69697
|
const fs = yield* FileSystem;
|
|
68840
69698
|
const parsed = parseAnswers(flags.answers);
|
|
68841
69699
|
if ("invalid" in parsed) return yield* fail$1(new ShipError(parsed.invalid));
|
|
@@ -68853,6 +69711,7 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68853
69711
|
if (problem !== void 0) return yield* fail$1(new ShipError(problem));
|
|
68854
69712
|
const fleet = new Set(config.machines);
|
|
68855
69713
|
const own = flags.config === void 0 ? tailnet.self.name : void 0;
|
|
69714
|
+
const base = (own === void 0 ? void 0 : yield* readConfigRecord)?.reached ?? {};
|
|
68856
69715
|
const order = config.machines;
|
|
68857
69716
|
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));
|
|
68858
69717
|
const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
|
|
@@ -68923,13 +69782,13 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68923
69782
|
machine: machine.name,
|
|
68924
69783
|
phase: "check"
|
|
68925
69784
|
})), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
|
|
68926
|
-
const stale = yield* staleConfigs(
|
|
69785
|
+
const stale = yield* staleConfigs(yield* fs.readFileString(path), base, own, checked, ledger);
|
|
68927
69786
|
if (stale.length > 0) {
|
|
68928
|
-
const what =
|
|
69787
|
+
const what = stale.map((s) => s.description).join("; ");
|
|
68929
69788
|
const answers = stale.flatMap((s) => s.machines).map((m) => given("config", m));
|
|
68930
69789
|
if (flags.dryRun) yield* ledger.note("info", `${what}; a Ship would ask before overwriting it`);
|
|
68931
69790
|
else if (answers.every((a) => a === "overwrite")) yield* ledger.note("info", `${what}; overwriting it, as answered`);
|
|
68932
|
-
else if (answers.includes("stop")) return yield* fail$1(new ShipError("
|
|
69791
|
+
else if (answers.includes("stop")) return yield* fail$1(new ShipError("stopped before overwriting the config there"));
|
|
68933
69792
|
else if (live.ask === void 0) return yield* fail$1(new ShipError(`${what}; copy it here first, or pass --overwrite-config`));
|
|
68934
69793
|
else {
|
|
68935
69794
|
const usable = [];
|
|
@@ -68955,12 +69814,17 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68955
69814
|
]);
|
|
68956
69815
|
if (choice === "stop") {
|
|
68957
69816
|
yield* ledger.note("answer", "stop");
|
|
68958
|
-
return yield* fail$1(new ShipError("
|
|
69817
|
+
return yield* fail$1(new ShipError("stopped before overwriting the config there"));
|
|
68959
69818
|
}
|
|
68960
69819
|
if (choice === "overwrite") yield* ledger.note("answer", "overwrite it");
|
|
68961
69820
|
else {
|
|
68962
69821
|
yield* ledger.note("answer", `use the config on ${choice.machines[0]}`);
|
|
68963
69822
|
yield* fs.writeFileString(path, choice.text);
|
|
69823
|
+
if (own !== void 0) yield* writeConfigRecord(encodeConfigRecord({
|
|
69824
|
+
ship: id,
|
|
69825
|
+
sha: choice.sha,
|
|
69826
|
+
reached: reachedAfter(mergeReached([base, choice.reached]), formatIso(yield* now), [own], choice.config.machines)
|
|
69827
|
+
}));
|
|
68964
69828
|
adopted = true;
|
|
68965
69829
|
return "adopted";
|
|
68966
69830
|
}
|
|
@@ -68975,7 +69839,30 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68975
69839
|
if (lent.token !== void 0) hidden.add(lent.token);
|
|
68976
69840
|
yield* logInfo(lent.token !== void 0 ? "gh token: lent" : `gh token: none (${lent.reason ?? "no repos"})`);
|
|
68977
69841
|
if (lent.token === void 0 && lent.reason !== void 0) yield* ledger.note("info", `no gh token (${lent.reason}); private repos will fail`);
|
|
68978
|
-
const
|
|
69842
|
+
const self = {
|
|
69843
|
+
name: tailnet.self.name,
|
|
69844
|
+
platform: tailnet.self.platform
|
|
69845
|
+
};
|
|
69846
|
+
const resolved = Object.fromEntries(yield* forEach$1(targeted, ({ provider, items }) => provider.resolve(items, {
|
|
69847
|
+
...lent.token === void 0 ? {} : { token: lent.token },
|
|
69848
|
+
starting: {
|
|
69849
|
+
machine: self,
|
|
69850
|
+
config,
|
|
69851
|
+
items: targetedItems(provider, config, self)
|
|
69852
|
+
},
|
|
69853
|
+
checked: checked.map(({ machine, state }) => {
|
|
69854
|
+
const identity = {
|
|
69855
|
+
name: machine.name,
|
|
69856
|
+
platform: machine.platform
|
|
69857
|
+
};
|
|
69858
|
+
return {
|
|
69859
|
+
machine: identity,
|
|
69860
|
+
items: targetedItems(provider, config, identity),
|
|
69861
|
+
facts: state.facts[provider.name] ?? [],
|
|
69862
|
+
records: state.records[provider.name] ?? {}
|
|
69863
|
+
};
|
|
69864
|
+
})
|
|
69865
|
+
}).pipe(map$5((r) => [provider.name, r])), { concurrency: "unbounded" }));
|
|
68979
69866
|
yield* ledger.resolved;
|
|
68980
69867
|
const planAll = (cfg, answer) => checked.map((g) => ({
|
|
68981
69868
|
...g,
|
|
@@ -69027,33 +69914,55 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
69027
69914
|
if (!progressed) break;
|
|
69028
69915
|
planned = planAll(cfg, answer);
|
|
69029
69916
|
}
|
|
69030
|
-
|
|
69917
|
+
const adopting = [...decided].flatMap(([key, choice]) => {
|
|
69918
|
+
const subject = key.slice(key.indexOf(":") + 1);
|
|
69919
|
+
const at = parseAddress(subject);
|
|
69920
|
+
const provider = providers.find((p) => p.name === at?.provider);
|
|
69921
|
+
const run = at === void 0 || provider === void 0 ? void 0 : provider.adopt?.(at.item, choice, resolved[provider.name]);
|
|
69922
|
+
return run === void 0 ? [] : [{
|
|
69923
|
+
subject,
|
|
69924
|
+
run
|
|
69925
|
+
}];
|
|
69926
|
+
});
|
|
69927
|
+
if (adopting.length > 0 && !flags.dryRun) {
|
|
69928
|
+
for (const { subject, run } of adopting) {
|
|
69929
|
+
changing = true;
|
|
69930
|
+
const result = yield* run;
|
|
69931
|
+
if ("error" in result) return yield* fail$1(new ShipError(`${subject}: ${result.error}`));
|
|
69932
|
+
yield* ledger.note("info", `${subject}: ${result.done}; starting over`);
|
|
69933
|
+
}
|
|
69934
|
+
adopted = true;
|
|
69935
|
+
return "adopted";
|
|
69936
|
+
}
|
|
69937
|
+
const text = yield* fs.readFileString(path);
|
|
69938
|
+
const sha = yield* shaOf(text);
|
|
69939
|
+
for (const { machine, state, plan: p } of planned) yield* ledger.plan(machine.name, p, {
|
|
69940
|
+
...state.config === void 0 ? {} : { from: state.config.sha.slice(0, 7) },
|
|
69941
|
+
to: sha.slice(0, 7)
|
|
69942
|
+
});
|
|
69031
69943
|
if (flags.dryRun) return;
|
|
69032
|
-
const
|
|
69033
|
-
|
|
69034
|
-
|
|
69035
|
-
|
|
69036
|
-
|
|
69037
|
-
shippedFrom: tailnet.self.name,
|
|
69038
|
-
reached,
|
|
69039
|
-
own
|
|
69944
|
+
const at = formatIso(yield* now);
|
|
69945
|
+
const recordFor = (reached) => encodeConfigRecord({
|
|
69946
|
+
ship: id,
|
|
69947
|
+
sha,
|
|
69948
|
+
reached: reachedAfter(base, at, [...reached, ...own === void 0 ? [] : [own]], cfg.machines)
|
|
69040
69949
|
});
|
|
69041
|
-
|
|
69042
|
-
|
|
69043
|
-
|
|
69950
|
+
changing = true;
|
|
69951
|
+
const record = recordFor(planned.map((g) => g.machine.name));
|
|
69952
|
+
if (own !== void 0) yield* writeConfigRecord(record);
|
|
69953
|
+
yield* logInfo(`the config's record: ${record}`);
|
|
69044
69954
|
const schema = `${schemaJson(fleetConfigJsonSchema())}\n`;
|
|
69045
|
-
const stdin = applyStdin(lent.token, stamped, schema);
|
|
69046
69955
|
const delivered = yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
|
|
69047
69956
|
return (yield* apply({
|
|
69048
69957
|
name: machine.name,
|
|
69049
69958
|
local
|
|
69050
|
-
}, p, applyScript(p),
|
|
69959
|
+
}, p, applyScript(p, record), applyStdin(lent.token, text, schema, p), ledger)).done.includes("config") ? machine.name : void 0;
|
|
69051
69960
|
}, (effect, { machine }) => annotateLogs(effect, {
|
|
69052
69961
|
machine: machine.name,
|
|
69053
69962
|
phase: "apply"
|
|
69054
69963
|
})), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((name) => name !== void 0)));
|
|
69055
69964
|
yield* logInfo(`the config reached: ${delivered.join(", ")}`);
|
|
69056
|
-
yield*
|
|
69965
|
+
if (own !== void 0) yield* writeConfigRecord(recordFor(delivered));
|
|
69057
69966
|
}, scoped);
|
|
69058
69967
|
//#endregion
|
|
69059
69968
|
//#region src/command.ts
|