fleetfizz 0.9.0 → 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 +1127 -157
- 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 = {
|
|
@@ -65150,6 +65464,8 @@ const MachineEntry = Struct({
|
|
|
65150
65464
|
detail: optionalKey(String$6),
|
|
65151
65465
|
output: optionalKey(ArraySchema(String$6)),
|
|
65152
65466
|
items: ArraySchema(Item),
|
|
65467
|
+
connected: optionalKey(Boolean$4),
|
|
65468
|
+
checked: optionalKey(Number$2),
|
|
65153
65469
|
started: optionalKey(Number$2),
|
|
65154
65470
|
ended: optionalKey(Number$2)
|
|
65155
65471
|
});
|
|
@@ -65158,10 +65474,14 @@ const LedgerState = Struct({
|
|
|
65158
65474
|
dryRun: Boolean$4,
|
|
65159
65475
|
notes: ArraySchema(Note),
|
|
65160
65476
|
machines: ArraySchema(MachineEntry),
|
|
65477
|
+
resolving: optionalKey(Struct({
|
|
65478
|
+
started: Number$2,
|
|
65479
|
+
items: Number$2
|
|
65480
|
+
})),
|
|
65161
65481
|
started: Number$2,
|
|
65162
65482
|
ended: optionalKey(Number$2)
|
|
65163
65483
|
});
|
|
65164
|
-
const itemsOf = (p) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
65484
|
+
const itemsOf = (p, config) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
65165
65485
|
const base = {
|
|
65166
65486
|
name: address(pp.name, d.item),
|
|
65167
65487
|
kind: pp.rule,
|
|
@@ -65204,7 +65524,8 @@ const itemsOf = (p) => [...p.providers.flatMap((pp) => pp.decisions.map((d) => {
|
|
|
65204
65524
|
})), {
|
|
65205
65525
|
name: CONFIG_STEP,
|
|
65206
65526
|
kind: "config",
|
|
65207
|
-
|
|
65527
|
+
...config,
|
|
65528
|
+
changes: config.from !== config.to,
|
|
65208
65529
|
status: "idle",
|
|
65209
65530
|
steps: [{
|
|
65210
65531
|
id: CONFIG_STEP,
|
|
@@ -65271,9 +65592,14 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
65271
65592
|
});
|
|
65272
65593
|
yield* logInfo(`${kind}: ${text}`);
|
|
65273
65594
|
}),
|
|
65274
|
-
|
|
65595
|
+
connected: (name) => machine(name, (m) => ({
|
|
65596
|
+
...m,
|
|
65597
|
+
connected: true
|
|
65598
|
+
})).pipe(andThen(log(name, logInfo("connected")))),
|
|
65599
|
+
checked: (name) => machine(name, (m, now) => ({
|
|
65275
65600
|
...m,
|
|
65276
|
-
status: "waiting"
|
|
65601
|
+
status: "waiting",
|
|
65602
|
+
checked: now
|
|
65277
65603
|
})).pipe(andThen(log(name, logInfo("checked")))),
|
|
65278
65604
|
skip: (name, reason) => machine(name, (m) => ({
|
|
65279
65605
|
...m,
|
|
@@ -65287,10 +65613,18 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
65287
65613
|
...output.length === 0 ? {} : { output },
|
|
65288
65614
|
ended: now
|
|
65289
65615
|
})).pipe(andThen(log(name, logError(`failed: ${reason}`)))),
|
|
65290
|
-
|
|
65616
|
+
resolving: (n) => update((state, now) => ({
|
|
65617
|
+
...state,
|
|
65618
|
+
resolving: {
|
|
65619
|
+
started: now,
|
|
65620
|
+
items: n
|
|
65621
|
+
}
|
|
65622
|
+
})).pipe(andThen(logInfo(`resolving ${n} items`))),
|
|
65623
|
+
resolved: update(({ resolving: _, ...state }) => state).pipe(andThen(logInfo("resolved"))),
|
|
65624
|
+
plan: (name, p, config) => machine(name, (m) => ({
|
|
65291
65625
|
...m,
|
|
65292
65626
|
status: "planned",
|
|
65293
|
-
items: itemsOf(p)
|
|
65627
|
+
items: itemsOf(p, config)
|
|
65294
65628
|
})).pipe(andThen(forEach$1(p.providers.flatMap((pp) => pp.decisions.map((d) => ({
|
|
65295
65629
|
pp,
|
|
65296
65630
|
d
|
|
@@ -65298,6 +65632,7 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
65298
65632
|
start: (name) => machine(name, (m, now) => ({
|
|
65299
65633
|
...m,
|
|
65300
65634
|
status: "running",
|
|
65635
|
+
connected: false,
|
|
65301
65636
|
started: now
|
|
65302
65637
|
})).pipe(andThen(log(name, logInfo("applying")))),
|
|
65303
65638
|
stepStarted: (name, id) => items(name, id, (item, now) => item.status === "failed" || item.status === "skipped" ? item : {
|
|
@@ -65348,7 +65683,7 @@ const makeLedger = fn("makeLedger")(function* (input) {
|
|
|
65348
65683
|
ended: m.ended ?? now
|
|
65349
65684
|
})).pipe(andThen(log(name, logInfo(`finished: ${outcome}`)))),
|
|
65350
65685
|
end: gen(function* () {
|
|
65351
|
-
yield* update((state, now) => ({
|
|
65686
|
+
yield* update(({ resolving: _, ...state }, now) => ({
|
|
65352
65687
|
...state,
|
|
65353
65688
|
ended: now,
|
|
65354
65689
|
machines: state.machines.map((m) => m.status === "running" || m.status === "checking" ? {
|
|
@@ -67347,7 +67682,7 @@ const SPINNER = cli_spinners_default.dots;
|
|
|
67347
67682
|
const runGlyph = (live, now) => (live ? SPINNER.frames[Math.floor(now / SPINNER.interval) % SPINNER.frames.length] : void 0) ?? "▸";
|
|
67348
67683
|
const unchanged = (i) => ({
|
|
67349
67684
|
text: "",
|
|
67350
|
-
detail: i.kind === "record" ? "up to date" : void 0
|
|
67685
|
+
detail: i.kind === "record" || i.kind === "config" ? "up to date" : void 0
|
|
67351
67686
|
});
|
|
67352
67687
|
const statusOf = (i, dryRun, glyph) => {
|
|
67353
67688
|
switch (i.status) {
|
|
@@ -67366,11 +67701,11 @@ const statusOf = (i, dryRun, glyph) => {
|
|
|
67366
67701
|
detail: i.detail
|
|
67367
67702
|
};
|
|
67368
67703
|
case "ok":
|
|
67704
|
+
if (!i.changes) return unchanged(i);
|
|
67369
67705
|
if (i.kind === "config") return {
|
|
67370
67706
|
text: "✓ copied",
|
|
67371
|
-
tone: "
|
|
67707
|
+
tone: "ok"
|
|
67372
67708
|
};
|
|
67373
|
-
if (!i.changes) return unchanged(i);
|
|
67374
67709
|
if (i.kind === "record") return i.from !== void 0 && i.from === i.to ? {
|
|
67375
67710
|
text: "✓ rebuilt",
|
|
67376
67711
|
tone: "ok",
|
|
@@ -67402,15 +67737,23 @@ const countsOf = (m) => {
|
|
|
67402
67737
|
skipped: m.items.filter((i) => i.status === "skipped").length
|
|
67403
67738
|
};
|
|
67404
67739
|
};
|
|
67405
|
-
const settled = (m, dryRun) => dryRun || m.status === "ok" || m.status === "failed";
|
|
67740
|
+
const settled = (m, dryRun) => dryRun && m.status === "planned" || m.status === "ok" || m.status === "failed";
|
|
67406
67741
|
const upToDate = (m, dryRun) => {
|
|
67407
67742
|
const c = countsOf(m);
|
|
67408
67743
|
return c.changes === 0 && c.skipped === 0 && settled(m, dryRun);
|
|
67409
67744
|
};
|
|
67410
|
-
const summaryOf = (m, dryRun) => {
|
|
67411
|
-
if (m.status === "checking"
|
|
67412
|
-
text: m.
|
|
67413
|
-
tone: "
|
|
67745
|
+
const summaryOf = (m, dryRun, glyph) => {
|
|
67746
|
+
if (m.status === "checking") return [{
|
|
67747
|
+
text: `${glyph} ${m.connected === true ? "checking" : "connecting"}`,
|
|
67748
|
+
tone: "run"
|
|
67749
|
+
}];
|
|
67750
|
+
if (m.status === "waiting") return [{
|
|
67751
|
+
text: "✓ checked",
|
|
67752
|
+
tone: "ok"
|
|
67753
|
+
}];
|
|
67754
|
+
if (m.status === "running" && m.connected !== true) return [{
|
|
67755
|
+
text: `${glyph} connecting`,
|
|
67756
|
+
tone: "run"
|
|
67414
67757
|
}];
|
|
67415
67758
|
if (m.status === "skipped") return [{
|
|
67416
67759
|
text: "– skipped",
|
|
@@ -67421,6 +67764,10 @@ const summaryOf = (m, dryRun) => {
|
|
|
67421
67764
|
tone: "fail"
|
|
67422
67765
|
}, { text: ` ${m.detail}` }];
|
|
67423
67766
|
const { changes, done, failed, skipped } = countsOf(m);
|
|
67767
|
+
if (changes === 0 && skipped === 0 && m.status === "running") return [{
|
|
67768
|
+
text: "applying",
|
|
67769
|
+
tone: "dim"
|
|
67770
|
+
}];
|
|
67424
67771
|
if (changes === 0 && skipped === 0) return [{
|
|
67425
67772
|
text: "up to date",
|
|
67426
67773
|
tone: settled(m, dryRun) ? "ok" : "dim"
|
|
@@ -67478,6 +67825,7 @@ const shipSummary = (state) => {
|
|
|
67478
67825
|
}] : parts;
|
|
67479
67826
|
};
|
|
67480
67827
|
const elapsed = (started, ended, now) => started === void 0 ? "" : duration((ended ?? now) - started);
|
|
67828
|
+
const machineTime = (m, shipStarted, now) => m.status === "checking" || m.status === "waiting" ? elapsed(shipStarted, m.checked, now) : elapsed(m.started, m.ended, now);
|
|
67481
67829
|
const Parts = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, { children: props.parts.map((part, n) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(T, {
|
|
67482
67830
|
tone: part.tone,
|
|
67483
67831
|
children: part.text
|
|
@@ -67518,9 +67866,9 @@ const MachineLine = (props) => {
|
|
|
67518
67866
|
width: w.time,
|
|
67519
67867
|
right: true,
|
|
67520
67868
|
tone: "dim",
|
|
67521
|
-
children: upToDate(m, props.dryRun) ? "" :
|
|
67869
|
+
children: upToDate(m, props.dryRun) ? "" : machineTime(m, props.shipStarted, props.now)
|
|
67522
67870
|
}),
|
|
67523
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: summaryOf(m, props.dryRun) })
|
|
67871
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: summaryOf(m, props.dryRun, props.glyph) })
|
|
67524
67872
|
]
|
|
67525
67873
|
});
|
|
67526
67874
|
};
|
|
@@ -67565,8 +67913,18 @@ const LedgerView = (props) => {
|
|
|
67565
67913
|
const { live } = (0, import_react.useContext)(ModeContext);
|
|
67566
67914
|
const running = live && state.ended === void 0;
|
|
67567
67915
|
const glyph = runGlyph(live, now);
|
|
67568
|
-
const shownItems = (m) => m.items.filter((i) => full || i.changes || i.status === "failed" || i.status === "skipped");
|
|
67569
|
-
const
|
|
67916
|
+
const shownItems = (m) => m.items.filter((i) => full || i.changes || i.status === "failed" || i.status === "skipped" || i.status === "running");
|
|
67917
|
+
const resolve = state.resolving === void 0 || state.ended !== void 0 ? void 0 : {
|
|
67918
|
+
name: "versions and commits",
|
|
67919
|
+
kind: "range",
|
|
67920
|
+
from: `${state.resolving.items} items`,
|
|
67921
|
+
changes: false,
|
|
67922
|
+
status: "running",
|
|
67923
|
+
doing: "resolving",
|
|
67924
|
+
steps: [],
|
|
67925
|
+
started: state.resolving.started
|
|
67926
|
+
};
|
|
67927
|
+
const visible = [...state.machines.flatMap(shownItems), ...resolve === void 0 ? [] : [resolve]];
|
|
67570
67928
|
const width = (texts) => Math.max(0, ...texts.map((t) => t.length));
|
|
67571
67929
|
const item = width(visible.map((i) => i.name));
|
|
67572
67930
|
const status = width(visible.map((i) => statusOf(i, dryRun, glyph).text));
|
|
@@ -67574,7 +67932,7 @@ const LedgerView = (props) => {
|
|
|
67574
67932
|
status,
|
|
67575
67933
|
item,
|
|
67576
67934
|
value: Math.max(width(visible.map(value)), width(state.machines.map((m) => m.name)) - (status + GAP + INDENT + item + GAP)),
|
|
67577
|
-
time: width([...visible.map((i) => elapsed(i.started, i.ended, now)), ...state.machines.map((m) =>
|
|
67935
|
+
time: width([...visible.map((i) => elapsed(i.started, i.ended, now)), ...state.machines.map((m) => machineTime(m, state.started, now))])
|
|
67578
67936
|
};
|
|
67579
67937
|
const blocks = state.machines.map((m) => ({
|
|
67580
67938
|
m,
|
|
@@ -67582,7 +67940,9 @@ const LedgerView = (props) => {
|
|
|
67582
67940
|
m,
|
|
67583
67941
|
w,
|
|
67584
67942
|
dryRun,
|
|
67585
|
-
|
|
67943
|
+
shipStarted: state.started,
|
|
67944
|
+
now,
|
|
67945
|
+
glyph
|
|
67586
67946
|
}, m.name),
|
|
67587
67947
|
body: [...m.output === void 0 ? [] : outputLines(m.name, m.output, nameAt(w), running), ...shownItems(m).flatMap((i) => [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemLine, {
|
|
67588
67948
|
i,
|
|
@@ -67593,7 +67953,7 @@ const LedgerView = (props) => {
|
|
|
67593
67953
|
}, `${m.name}/${i.kind}:${i.name}`), ...i.output === void 0 ? [] : outputLines(`${m.name}/${i.kind}:${i.name}`, i.output, nameAt(w), running)])]
|
|
67594
67954
|
}));
|
|
67595
67955
|
const legend = state.ended === void 0 ? `f ${full ? "changes only" : "full list"} · ctrl-c stop` : void 0;
|
|
67596
|
-
const room = height === void 0 ? Infinity : height - (legend === void 0 ? 1 : 3);
|
|
67956
|
+
const room = height === void 0 ? Infinity : height - (legend === void 0 ? 1 : 3) - (resolve === void 0 ? 0 : 2);
|
|
67597
67957
|
const count = (bs) => bs.reduce((n, b) => n + 1 + b.body.length, Math.max(0, bs.length - 1));
|
|
67598
67958
|
const fits = count(blocks) <= room;
|
|
67599
67959
|
let spare = room - count(blocks.map((b) => ({
|
|
@@ -67642,6 +68002,16 @@ const LedgerView = (props) => {
|
|
|
67642
68002
|
tone: "dim",
|
|
67643
68003
|
children: "…"
|
|
67644
68004
|
}),
|
|
68005
|
+
resolve !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
68006
|
+
marginTop: 1,
|
|
68007
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ItemLine, {
|
|
68008
|
+
i: resolve,
|
|
68009
|
+
w,
|
|
68010
|
+
dryRun,
|
|
68011
|
+
now,
|
|
68012
|
+
glyph
|
|
68013
|
+
})
|
|
68014
|
+
}),
|
|
67645
68015
|
state.ended !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
67646
68016
|
marginTop: 1,
|
|
67647
68017
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Parts, { parts: [...shipSummary(state), ...dryRun ? [] : [{
|
|
@@ -67739,7 +68109,7 @@ const report = fn("report")(function* () {
|
|
|
67739
68109
|
]
|
|
67740
68110
|
})));
|
|
67741
68111
|
});
|
|
67742
|
-
const decodeFact$
|
|
68112
|
+
const decodeFact$3 = decodeUnknownOption(Union([Struct({
|
|
67743
68113
|
fact: Literal("brew"),
|
|
67744
68114
|
version: NullOr(String$6)
|
|
67745
68115
|
}), AtFact]));
|
|
@@ -67791,7 +68161,7 @@ function homebrew(name) {
|
|
|
67791
68161
|
let brew;
|
|
67792
68162
|
const at = /* @__PURE__ */ new Map();
|
|
67793
68163
|
for (const raw of facts) {
|
|
67794
|
-
const fact = decodeFact$
|
|
68164
|
+
const fact = decodeFact$3(raw);
|
|
67795
68165
|
if (fact._tag === "None") continue;
|
|
67796
68166
|
const f = fact.value;
|
|
67797
68167
|
if (f.fact === "brew") brew = f.version ?? void 0;
|
|
@@ -67868,6 +68238,532 @@ function homebrew(name) {
|
|
|
67868
68238
|
}
|
|
67869
68239
|
const brew = homebrew("brew");
|
|
67870
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
|
+
};
|
|
67871
68767
|
const decodeFact$1 = decodeUnknownOption(Union([
|
|
67872
68768
|
Struct({
|
|
67873
68769
|
fact: Literal("cloneRoot"),
|
|
@@ -68001,11 +68897,12 @@ const decideRepo = (repo, root, observed, resolved, ctx) => {
|
|
|
68001
68897
|
};
|
|
68002
68898
|
const fetchStep = ({ item, root, commit }) => {
|
|
68003
68899
|
const url = originUrl(item.origin);
|
|
68900
|
+
const fetch = (what) => `if [ "$(git -C "$ff_d" rev-parse HEAD)" != ${quote(commit)} ]; then ff_git -C "$ff_d" fetch --quiet origin ${quote(what)}; fi`;
|
|
68004
68901
|
const sync = isPin(item.ref) ? [
|
|
68005
|
-
|
|
68902
|
+
fetch(commit),
|
|
68006
68903
|
`git -C "$ff_d" checkout --quiet --detach ${quote(commit)}`,
|
|
68007
68904
|
`git -C "$ff_d" reset --quiet --hard ${quote(commit)}`
|
|
68008
|
-
] : [
|
|
68905
|
+
] : [fetch(item.ref), `git -C "$ff_d" reset --quiet --hard ${quote(commit)}`];
|
|
68009
68906
|
return {
|
|
68010
68907
|
id: `repos:${item.name}:fetch`,
|
|
68011
68908
|
items: [item.name],
|
|
@@ -68297,6 +69194,7 @@ const decideRanged = (item, observed, resolved, ctx) => {
|
|
|
68297
69194
|
//#endregion
|
|
68298
69195
|
//#region ../../packages/providers/src/index.ts
|
|
68299
69196
|
const PROVIDERS = [
|
|
69197
|
+
files,
|
|
68300
69198
|
{
|
|
68301
69199
|
name: "vp",
|
|
68302
69200
|
rule: "range",
|
|
@@ -68556,12 +69454,15 @@ const canRedraw = () => Boolean(process.stdout.isTTY) && process.env.TERM !== "d
|
|
|
68556
69454
|
const decodeAt = decodeUnknownOption(AtFact);
|
|
68557
69455
|
const runOn = fn("runOn")(function* (target, script, secrets, onLine, ledger) {
|
|
68558
69456
|
let first;
|
|
69457
|
+
let connected = false;
|
|
68559
69458
|
yield* logDebug(`script:\n${script}`);
|
|
68560
69459
|
const started = yield* currentTimeMillis;
|
|
68561
69460
|
const run = yield* runScript(target, script, secrets, (line) => {
|
|
68562
69461
|
const parsed = parseLine(line);
|
|
68563
69462
|
if (parsed.kind === "text" && first === void 0 && line.trim() !== "") first = line.trim();
|
|
68564
|
-
|
|
69463
|
+
const connect = connected ? void_ : ledger.connected(target.name);
|
|
69464
|
+
connected = true;
|
|
69465
|
+
return logDebug(`> ${line}`).pipe(andThen(connect), andThen(onLine(parsed)));
|
|
68565
69466
|
}).pipe(map$5((exitCode) => ({
|
|
68566
69467
|
kind: "ran",
|
|
68567
69468
|
exitCode
|
|
@@ -68701,7 +69602,7 @@ const askQuestions = fn("askQuestions")(function* (input) {
|
|
|
68701
69602
|
yield* input.ledger.note("answer", "not asked (dry run)");
|
|
68702
69603
|
continue;
|
|
68703
69604
|
}
|
|
68704
|
-
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) => ({
|
|
68705
69606
|
value: c.value,
|
|
68706
69607
|
title: c.value === "bump" && bumps.length > 0 ? `bump the config to ${bumps.map((b) => bumpedRange(b.range, b.newest)).join(" and ")}` : c.title
|
|
68707
69608
|
}));
|
|
@@ -68738,27 +69639,43 @@ const unshippable = (config, tailnet, only, source) => {
|
|
|
68738
69639
|
const outsideFleet = only.filter((name) => !fleet.has(name));
|
|
68739
69640
|
if (outsideFleet.length > 0) return `not in machines in ${source}: ${outsideFleet.join(", ")}`;
|
|
68740
69641
|
};
|
|
68741
|
-
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) {
|
|
68742
69644
|
const copies = [];
|
|
68743
69645
|
for (const { machine, state } of checked) {
|
|
68744
|
-
if (state.config === void 0) continue;
|
|
68745
|
-
const parsed = yield* result(parseConfig(machine.name, state.config));
|
|
68746
|
-
if (parsed._tag === "Failure")
|
|
68747
|
-
|
|
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({
|
|
69654
|
+
machine: machine.name,
|
|
69655
|
+
sha: state.config.sha,
|
|
69656
|
+
record
|
|
69657
|
+
}, base);
|
|
69658
|
+
if (why === void 0) continue;
|
|
69659
|
+
copies.push({
|
|
68748
69660
|
machine: machine.name,
|
|
69661
|
+
text: state.config.text,
|
|
69662
|
+
sha: state.config.sha,
|
|
68749
69663
|
config: parsed.success,
|
|
68750
|
-
|
|
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`
|
|
68751
69666
|
});
|
|
68752
69667
|
}
|
|
68753
|
-
return [...Map.groupBy(
|
|
69668
|
+
return [...Map.groupBy(copies, (c) => c.text)].flatMap(([text, group]) => {
|
|
69669
|
+
const [first] = group;
|
|
68754
69670
|
if (first === void 0) return [];
|
|
68755
|
-
const
|
|
68756
|
-
const machines = [machine, ...rest.map((c) => c.machine)];
|
|
69671
|
+
const machines = group.map((c) => c.machine);
|
|
68757
69672
|
return [{
|
|
68758
69673
|
text,
|
|
68759
|
-
|
|
69674
|
+
sha: first.sha,
|
|
69675
|
+
config: first.config,
|
|
68760
69676
|
machines,
|
|
68761
|
-
|
|
69677
|
+
reached: mergeReached(group.flatMap((c) => c.record === void 0 ? [] : [c.record.reached])),
|
|
69678
|
+
description: `the config on ${machines.join(", ")} ${first.why}`
|
|
68762
69679
|
}];
|
|
68763
69680
|
});
|
|
68764
69681
|
});
|
|
@@ -68773,10 +69690,10 @@ const ship = fn("ship")(function* (flags) {
|
|
|
68773
69690
|
yield* logInfo(`Ship ${id}: fleetfizz ${flags.version}, node ${process.version} on ${process.platform}`);
|
|
68774
69691
|
yield* logInfo(`fizz ${process.argv.slice(2).join(" ")}`);
|
|
68775
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)" : ""}`);
|
|
68776
|
-
while ((yield* shipPass(flags, tailnet, hidden)) === "adopted");
|
|
69693
|
+
while ((yield* shipPass(flags, tailnet, hidden, id)) === "adopted");
|
|
68777
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"));
|
|
68778
69695
|
}, scoped);
|
|
68779
|
-
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
69696
|
+
const shipPass = fn("shipPass")(function* (flags, tailnet, hidden, id) {
|
|
68780
69697
|
const fs = yield* FileSystem;
|
|
68781
69698
|
const parsed = parseAnswers(flags.answers);
|
|
68782
69699
|
if ("invalid" in parsed) return yield* fail$1(new ShipError(parsed.invalid));
|
|
@@ -68794,6 +69711,7 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68794
69711
|
if (problem !== void 0) return yield* fail$1(new ShipError(problem));
|
|
68795
69712
|
const fleet = new Set(config.machines);
|
|
68796
69713
|
const own = flags.config === void 0 ? tailnet.self.name : void 0;
|
|
69714
|
+
const base = (own === void 0 ? void 0 : yield* readConfigRecord)?.reached ?? {};
|
|
68797
69715
|
const order = config.machines;
|
|
68798
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));
|
|
68799
69717
|
const count = `${targetsList.length} machine${targetsList.length === 1 ? "" : "s"}`;
|
|
@@ -68864,13 +69782,13 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68864
69782
|
machine: machine.name,
|
|
68865
69783
|
phase: "check"
|
|
68866
69784
|
})), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((g) => g !== void 0)));
|
|
68867
|
-
const stale = yield* staleConfigs(
|
|
69785
|
+
const stale = yield* staleConfigs(yield* fs.readFileString(path), base, own, checked, ledger);
|
|
68868
69786
|
if (stale.length > 0) {
|
|
68869
|
-
const what =
|
|
69787
|
+
const what = stale.map((s) => s.description).join("; ");
|
|
68870
69788
|
const answers = stale.flatMap((s) => s.machines).map((m) => given("config", m));
|
|
68871
69789
|
if (flags.dryRun) yield* ledger.note("info", `${what}; a Ship would ask before overwriting it`);
|
|
68872
69790
|
else if (answers.every((a) => a === "overwrite")) yield* ledger.note("info", `${what}; overwriting it, as answered`);
|
|
68873
|
-
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"));
|
|
68874
69792
|
else if (live.ask === void 0) return yield* fail$1(new ShipError(`${what}; copy it here first, or pass --overwrite-config`));
|
|
68875
69793
|
else {
|
|
68876
69794
|
const usable = [];
|
|
@@ -68896,12 +69814,17 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68896
69814
|
]);
|
|
68897
69815
|
if (choice === "stop") {
|
|
68898
69816
|
yield* ledger.note("answer", "stop");
|
|
68899
|
-
return yield* fail$1(new ShipError("
|
|
69817
|
+
return yield* fail$1(new ShipError("stopped before overwriting the config there"));
|
|
68900
69818
|
}
|
|
68901
69819
|
if (choice === "overwrite") yield* ledger.note("answer", "overwrite it");
|
|
68902
69820
|
else {
|
|
68903
69821
|
yield* ledger.note("answer", `use the config on ${choice.machines[0]}`);
|
|
68904
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
|
+
}));
|
|
68905
69828
|
adopted = true;
|
|
68906
69829
|
return "adopted";
|
|
68907
69830
|
}
|
|
@@ -68911,11 +69834,36 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68911
69834
|
provider,
|
|
68912
69835
|
items: checked.flatMap(({ machine }) => targetedItems(provider, config, machine))
|
|
68913
69836
|
}));
|
|
69837
|
+
yield* ledger.resolving(new Set(targeted.flatMap(({ provider, items }) => items.map((i) => `${provider.name}:${i.name}`))).size);
|
|
68914
69838
|
const lent = targeted.some(({ provider, items }) => provider.name === "repos" && items.length > 0) ? yield* readLentToken() : { token: void 0 };
|
|
68915
69839
|
if (lent.token !== void 0) hidden.add(lent.token);
|
|
68916
69840
|
yield* logInfo(lent.token !== void 0 ? "gh token: lent" : `gh token: none (${lent.reason ?? "no repos"})`);
|
|
68917
69841
|
if (lent.token === void 0 && lent.reason !== void 0) yield* ledger.note("info", `no gh token (${lent.reason}); private repos will fail`);
|
|
68918
|
-
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" }));
|
|
69866
|
+
yield* ledger.resolved;
|
|
68919
69867
|
const planAll = (cfg, answer) => checked.map((g) => ({
|
|
68920
69868
|
...g,
|
|
68921
69869
|
plan: planMachine({
|
|
@@ -68966,33 +69914,55 @@ const shipPass = fn("shipPass")(function* (flags, tailnet, hidden) {
|
|
|
68966
69914
|
if (!progressed) break;
|
|
68967
69915
|
planned = planAll(cfg, answer);
|
|
68968
69916
|
}
|
|
68969
|
-
|
|
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
|
+
});
|
|
68970
69943
|
if (flags.dryRun) return;
|
|
68971
|
-
const
|
|
68972
|
-
|
|
68973
|
-
|
|
68974
|
-
|
|
68975
|
-
|
|
68976
|
-
shippedFrom: tailnet.self.name,
|
|
68977
|
-
reached,
|
|
68978
|
-
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)
|
|
68979
69949
|
});
|
|
68980
|
-
|
|
68981
|
-
|
|
68982
|
-
|
|
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}`);
|
|
68983
69954
|
const schema = `${schemaJson(fleetConfigJsonSchema())}\n`;
|
|
68984
|
-
const stdin = applyStdin(lent.token, stamped, schema);
|
|
68985
69955
|
const delivered = yield* forEach$1(planned, fn("applyOne")(function* ({ machine, local, plan: p }) {
|
|
68986
69956
|
return (yield* apply({
|
|
68987
69957
|
name: machine.name,
|
|
68988
69958
|
local
|
|
68989
|
-
}, p, applyScript(p),
|
|
69959
|
+
}, p, applyScript(p, record), applyStdin(lent.token, text, schema, p), ledger)).done.includes("config") ? machine.name : void 0;
|
|
68990
69960
|
}, (effect, { machine }) => annotateLogs(effect, {
|
|
68991
69961
|
machine: machine.name,
|
|
68992
69962
|
phase: "apply"
|
|
68993
69963
|
})), { concurrency: "unbounded" }).pipe(map$5((list) => list.filter((name) => name !== void 0)));
|
|
68994
69964
|
yield* logInfo(`the config reached: ${delivered.join(", ")}`);
|
|
68995
|
-
yield*
|
|
69965
|
+
if (own !== void 0) yield* writeConfigRecord(recordFor(delivered));
|
|
68996
69966
|
}, scoped);
|
|
68997
69967
|
//#endregion
|
|
68998
69968
|
//#region src/command.ts
|