@gjermundgaraba/upup 0.3.0 → 0.5.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/README.md +9 -7
- package/dist/{Tui-BiTtZ1EF.mjs → Tui-DV_6wbrQ.mjs} +42 -37
- package/dist/grep-worker.mjs +1 -1
- package/dist/{io-BGJ4Yp7u.mjs → io-ns6xhFdF.mjs} +5 -1
- package/dist/main.mjs +1080 -613
- package/native/dist/upup-sparkle.json +1 -1
- package/native/dist/upup-sparkle.zip +0 -0
- package/package.json +24 -26
package/dist/main.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as reason, t as
|
|
2
|
+
import { n as openFlags, r as reason, t as byName } from "./io-ns6xhFdF.mjs";
|
|
3
3
|
import { findPackageJSON } from "node:module";
|
|
4
4
|
import { NodeRuntime, NodeServices, NodeStdio } from "@effect/platform-node";
|
|
5
5
|
import { Cause, Clock, Config, Context, Data, Deferred, Duration, Effect, Equal, Exit, Fiber, FiberHandle, FiberSet, FileSystem, Layer, Option, Predicate, Queue, Ref, Result, Schedule, Schema, Semaphore, Stream, SubscriptionRef, identity } from "effect";
|
|
@@ -17,10 +17,9 @@ import { homedir, tmpdir } from "node:os";
|
|
|
17
17
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
18
18
|
import lockfile from "proper-lockfile";
|
|
19
19
|
import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
|
|
20
|
-
import {
|
|
21
|
-
import { GenerationTask, Harness, ToolTask, createRegistry, defineDoc, defineExtension, defineTool, hook, section, watchEvents } from "@earendil-works/pi-durable";
|
|
20
|
+
import { GenerationTask, Harness, LiveDoc, ROOT_CONVERSATION_ID, ToolTask, createRegistry, defineDoc, defineExtension, defineTool, hook, section } from "@earendil-works/pi-durable";
|
|
22
21
|
import { openNodeSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/node";
|
|
23
|
-
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
22
|
+
import { ChildProcess, ChildProcessSpawner, CommandLine } from "effect/process";
|
|
24
23
|
import { Worker } from "node:worker_threads";
|
|
25
24
|
import semver from "semver";
|
|
26
25
|
import tarStream from "tar-stream";
|
|
@@ -41,8 +40,9 @@ import { XMLParser } from "fast-xml-parser";
|
|
|
41
40
|
import { isSea } from "node:sea";
|
|
42
41
|
import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
|
|
43
42
|
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
43
|
+
import { setup, types } from "xstate/fsm";
|
|
44
44
|
// package.json
|
|
45
|
-
var version$1 = "0.
|
|
45
|
+
var version$1 = "0.5.0";
|
|
46
46
|
// src/ai/Setup.ts
|
|
47
47
|
const Thinking = Schema.Literals([
|
|
48
48
|
"off",
|
|
@@ -283,7 +283,7 @@ const ordered = (left, right) => Number(right.signedIn) - Number(left.signedIn)
|
|
|
283
283
|
var AiFailed = class extends Schema.TaggedError()("AiFailed", { issue: Schema.String }) {};
|
|
284
284
|
var Ai = class extends Context.Service()("upup/Ai") {};
|
|
285
285
|
const failed$2 = (cause) => new AiFailed({ issue: cause instanceof Error ? cause.message : String(cause) });
|
|
286
|
-
const make$
|
|
286
|
+
const make$15 = (models, credentials = store()) => Effect.gen(function* () {
|
|
287
287
|
const settings = yield* Settings;
|
|
288
288
|
const catalogue = Effect.promise(async () => {
|
|
289
289
|
const providers = models.getProviders();
|
|
@@ -328,7 +328,7 @@ const make$14 = (models, credentials = store()) => Effect.gen(function* () {
|
|
|
328
328
|
});
|
|
329
329
|
const layer$7 = Layer.effect(Ai, Effect.suspend(() => {
|
|
330
330
|
const credentials = store();
|
|
331
|
-
return make$
|
|
331
|
+
return make$15(builtinModels({ credentials }), credentials);
|
|
332
332
|
}));
|
|
333
333
|
// src/process/lock.ts
|
|
334
334
|
var LockFailed = class extends Schema.TaggedError()("LockFailed", {
|
|
@@ -426,12 +426,12 @@ const withTarget = (pkg, target) => ({
|
|
|
426
426
|
target
|
|
427
427
|
});
|
|
428
428
|
const isUnaudited = Schema.is(UnauditedEcosystem);
|
|
429
|
-
const isApp = Schema.is(AppEcosystem);
|
|
429
|
+
const isApp$1 = Schema.is(AppEcosystem);
|
|
430
430
|
const isRuntime = Schema.is(RuntimeEcosystem);
|
|
431
431
|
const isUnauditedUpdate = (pkg) => isUnaudited(pkg.ecosystem);
|
|
432
|
-
const isAppUpdate = (pkg) => isApp(pkg.ecosystem);
|
|
432
|
+
const isAppUpdate = (pkg) => isApp$1(pkg.ecosystem);
|
|
433
433
|
const unauditedKind = (pkg) => {
|
|
434
|
-
if (isApp(pkg.ecosystem)) return "app";
|
|
434
|
+
if (isApp$1(pkg.ecosystem)) return "app";
|
|
435
435
|
if (isRuntime(pkg.ecosystem)) return "runtime";
|
|
436
436
|
if (isUnaudited(pkg.ecosystem)) return "os";
|
|
437
437
|
};
|
|
@@ -519,7 +519,7 @@ var CommandFailed = class extends Schema.TaggedError()("CommandFailed", {
|
|
|
519
519
|
output: Schema.String
|
|
520
520
|
}) {};
|
|
521
521
|
var Exec = class extends Context.Service()("upup/Exec") {};
|
|
522
|
-
const commandLine = (invocation) => [invocation.command, ...invocation.args]
|
|
522
|
+
const commandLine = (invocation) => CommandLine.display([invocation.command, ...invocation.args]);
|
|
523
523
|
const requireSuccess = (run) => (invocation) => run(invocation).pipe(Effect.filterOrFail((output) => output.exitCode === 0, (output) => new CommandFailed({
|
|
524
524
|
command: commandLine(invocation),
|
|
525
525
|
cwd: invocation.cwd,
|
|
@@ -550,7 +550,7 @@ const layer$6 = Layer.effect(Exec, Effect.gen(function* () {
|
|
|
550
550
|
}, Effect.scoped, (effect, invocation) => Effect.mapError(effect, (cause) => new CommandFailed({
|
|
551
551
|
command: commandLine(invocation),
|
|
552
552
|
cwd: invocation.cwd,
|
|
553
|
-
output: cause.message
|
|
553
|
+
output: Predicate.isError(cause.cause) ? cause.cause.message : cause.reason.description ?? cause.reason._tag
|
|
554
554
|
})));
|
|
555
555
|
return Exec.of({
|
|
556
556
|
run,
|
|
@@ -595,14 +595,14 @@ const Installed = Schema.Struct({
|
|
|
595
595
|
formulae: Schema.Array(Formula$2),
|
|
596
596
|
casks: Schema.Array(Cask$1)
|
|
597
597
|
});
|
|
598
|
-
const OutdatedEntry
|
|
598
|
+
const OutdatedEntry = Schema.Struct({
|
|
599
599
|
name: Schema.String,
|
|
600
600
|
current_version: Schema.String,
|
|
601
601
|
pinned: Schema.Boolean
|
|
602
602
|
});
|
|
603
|
-
const Outdated$
|
|
604
|
-
formulae: Schema.Array(OutdatedEntry
|
|
605
|
-
casks: Schema.Array(OutdatedEntry
|
|
603
|
+
const Outdated$1 = Schema.Struct({
|
|
604
|
+
formulae: Schema.Array(OutdatedEntry),
|
|
605
|
+
casks: Schema.Array(OutdatedEntry)
|
|
606
606
|
});
|
|
607
607
|
const brew$1 = (args, refreshMetadata = false) => ({
|
|
608
608
|
command: "brew",
|
|
@@ -653,7 +653,7 @@ const packages = (installed, outdated) => {
|
|
|
653
653
|
});
|
|
654
654
|
return [...formulae, ...casks];
|
|
655
655
|
};
|
|
656
|
-
const make$
|
|
656
|
+
const make$14 = Effect.gen(function* () {
|
|
657
657
|
const exec = yield* Exec;
|
|
658
658
|
const json = (schema, invocation) => exec.stdout(invocation).pipe(Effect.flatMap(decodeJson(schema, commandLine(invocation))));
|
|
659
659
|
const list = Effect.gen(function* () {
|
|
@@ -662,7 +662,7 @@ const make$13 = Effect.gen(function* () {
|
|
|
662
662
|
"--json=v2",
|
|
663
663
|
"--installed"
|
|
664
664
|
]));
|
|
665
|
-
const outdated = yield* json(Outdated$
|
|
665
|
+
const outdated = yield* json(Outdated$1, brew$1(["outdated", "--json=v2"], true));
|
|
666
666
|
return {
|
|
667
667
|
packages: packages(installed, outdated),
|
|
668
668
|
notices: []
|
|
@@ -695,7 +695,7 @@ const make$13 = Effect.gen(function* () {
|
|
|
695
695
|
exitCode: output.exitCode,
|
|
696
696
|
output: output.stderr
|
|
697
697
|
});
|
|
698
|
-
const outdated = yield* decodeJson(Outdated$
|
|
698
|
+
const outdated = yield* decodeJson(Outdated$1, commandLine(invocation))(output.stdout);
|
|
699
699
|
return [...outdated.formulae, ...outdated.casks].find((entry) => entry.name === pkg.name)?.current_version;
|
|
700
700
|
});
|
|
701
701
|
return {
|
|
@@ -752,15 +752,15 @@ const Severity = Schema.Literals([
|
|
|
752
752
|
const Finding = Schema.Struct({
|
|
753
753
|
severity: Severity,
|
|
754
754
|
title: Schema.String,
|
|
755
|
-
detail: Schema.String,
|
|
756
|
-
paths: Schema.Array(Schema.String)
|
|
755
|
+
detail: Schema.String.annotate({ description: "Markdown: the evidence and why it matters." }),
|
|
756
|
+
paths: Schema.Array(Schema.String).annotate({ description: "Workspace paths, with line ranges where useful." })
|
|
757
757
|
});
|
|
758
758
|
const Report = Schema.Struct({
|
|
759
759
|
verdict: Verdict,
|
|
760
|
-
rationale: Schema.String,
|
|
761
|
-
summary: Schema.String,
|
|
760
|
+
rationale: Schema.String.annotate({ description: "One concrete sentence justifying the verdict." }),
|
|
761
|
+
summary: Schema.String.annotate({ description: "Markdown: what the update changes that bears on the mandate, what you flagged and why you cleared or kept it." }),
|
|
762
762
|
findings: Schema.Array(Finding),
|
|
763
|
-
unverified: Schema.Array(Schema.String)
|
|
763
|
+
unverified: Schema.Array(Schema.String).annotate({ description: "Each thing you could not verify." })
|
|
764
764
|
});
|
|
765
765
|
Schema.Struct({
|
|
766
766
|
mode: Mode,
|
|
@@ -797,7 +797,7 @@ const landingKey = (landing) => sha256(JSON.stringify([
|
|
|
797
797
|
]));
|
|
798
798
|
const unauditedReasons = {
|
|
799
799
|
app: "an app update",
|
|
800
|
-
runtime: "a runtime version
|
|
800
|
+
runtime: "a Node.js runtime version",
|
|
801
801
|
os: "an operating-system update"
|
|
802
802
|
};
|
|
803
803
|
const unauditable = (pkg) => {
|
|
@@ -843,21 +843,23 @@ const descriptions = {
|
|
|
843
843
|
grep: "Find a JavaScript regular expression in UTF-8 regular files below a path. Each line is matched whole. Lines over 1 KiB show a bounded window around each match with its file byte offset; a file stops at a line over 16 MiB.",
|
|
844
844
|
list: "List workspace entries below a directory to a bounded depth."
|
|
845
845
|
};
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
},
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
846
|
+
const count$1 = (maximum) => Schema.Number.pipe(Schema.check(Schema.isInt(), Schema.isBetween({
|
|
847
|
+
minimum: 1,
|
|
848
|
+
maximum
|
|
849
|
+
})));
|
|
850
|
+
const ReadArgs = Schema.Struct({
|
|
851
|
+
path: Schema.String.annotate({ description: "Workspace-relative file path." }),
|
|
852
|
+
offset: Schema.Number.pipe(Schema.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))).annotate({ description: "Zero-based byte offset; must be >= 0." }),
|
|
853
|
+
limit: count$1(limits$1.readBytes).annotate({ description: `Maximum bytes to return; must be between 1 and ${limits$1.readBytes} inclusive.` })
|
|
854
|
+
});
|
|
855
|
+
const GrepArgs = Schema.Struct({
|
|
856
|
+
path: Schema.String.annotate({ description: "Workspace-relative file or directory path." }),
|
|
857
|
+
pattern: Schema.String.annotate({ description: "JavaScript regular expression." })
|
|
858
|
+
});
|
|
859
|
+
const ListArgs = Schema.Struct({
|
|
860
|
+
path: Schema.String.annotate({ description: "Workspace-relative directory path; use . for the root." }),
|
|
861
|
+
depth: count$1(limits$1.listDepth).annotate({ description: `Maximum directory depth; must be between 1 and ${limits$1.listDepth} inclusive.` })
|
|
862
|
+
});
|
|
861
863
|
var ToolError = class extends Error {
|
|
862
864
|
constructor(message) {
|
|
863
865
|
super(message);
|
|
@@ -914,10 +916,6 @@ const locate$4 = async (workspace, name, context) => {
|
|
|
914
916
|
return fail$1(`${context}: ${reason(error)}`);
|
|
915
917
|
}
|
|
916
918
|
};
|
|
917
|
-
const byName = (left, right) => {
|
|
918
|
-
if (left.name < right.name) return -1;
|
|
919
|
-
return left.name > right.name ? 1 : 0;
|
|
920
|
-
};
|
|
921
919
|
const suffixOf = (entry) => {
|
|
922
920
|
if (entry.isDirectory()) return "/";
|
|
923
921
|
return entry.isSymbolicLink() ? "@" : "";
|
|
@@ -1343,17 +1341,11 @@ const runDiff = (cwd, args, from, to, input) => Effect.callback((resume) => {
|
|
|
1343
1341
|
});
|
|
1344
1342
|
return Effect.sync(() => child.kill());
|
|
1345
1343
|
});
|
|
1346
|
-
const regularFiles =
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
const info = await lstat(path.join(root, file));
|
|
1352
|
-
if (info.isDirectory()) found.push(...await regularFiles(root, file));
|
|
1353
|
-
else if (info.isFile()) found.push(file);
|
|
1354
|
-
}
|
|
1355
|
-
return found;
|
|
1356
|
-
};
|
|
1344
|
+
const regularFiles = (root, relative = "") => Effect.promise(async () => await readdir(path.join(root, relative), { withFileTypes: true }).catch(() => [])).pipe(Effect.flatMap((entries) => Effect.forEach(entries.toSorted(byName), (entry) => {
|
|
1345
|
+
const file = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
1346
|
+
if (entry.isDirectory()) return regularFiles(root, file);
|
|
1347
|
+
return Effect.succeed(entry.isFile() ? [file] : []);
|
|
1348
|
+
}, { concurrency: "unbounded" })), Effect.map((found) => found.flat()));
|
|
1357
1349
|
const failure$1 = (from, to) => (cause) => new DiffFailed({
|
|
1358
1350
|
from,
|
|
1359
1351
|
to,
|
|
@@ -1371,15 +1363,25 @@ const plain$1 = (pair) => ({
|
|
|
1371
1363
|
paired: false,
|
|
1372
1364
|
rewritten: false
|
|
1373
1365
|
});
|
|
1374
|
-
const planTree =
|
|
1375
|
-
const
|
|
1366
|
+
const planTree = (workspace, from, to) => Effect.gen(function* () {
|
|
1367
|
+
const attempt = (run) => Effect.tryPromise({
|
|
1368
|
+
try: run,
|
|
1369
|
+
catch: failure$1(from, to)
|
|
1370
|
+
});
|
|
1371
|
+
const [before, after] = yield* Effect.all([regularFiles(path.join(workspace, from)), regularFiles(path.join(workspace, to))], { concurrency: "unbounded" });
|
|
1376
1372
|
const beforeSet = new Set(before);
|
|
1377
1373
|
const afterSet = new Set(after);
|
|
1378
1374
|
const fromOnly = before.filter((file) => !afterSet.has(file));
|
|
1379
1375
|
const toOnly = after.filter((file) => !beforeSet.has(file));
|
|
1380
|
-
const only =
|
|
1381
|
-
|
|
1382
|
-
|
|
1376
|
+
const only = new Map(yield* Effect.forEach([...fromOnly.map((file) => ({
|
|
1377
|
+
side: "from",
|
|
1378
|
+
root: from,
|
|
1379
|
+
file
|
|
1380
|
+
})), ...toOnly.map((file) => ({
|
|
1381
|
+
side: "to",
|
|
1382
|
+
root: to,
|
|
1383
|
+
file
|
|
1384
|
+
}))], ({ side, root, file }) => attempt(async () => [`${side}\0${file}`, await readFile(path.join(workspace, root, file))]), { concurrency: 8 }));
|
|
1383
1385
|
const pairs = matchRenames(fromOnly, toOnly, (side, file) => only.get(`${side}\0${file}`) ?? Buffer.alloc(0));
|
|
1384
1386
|
const renames = renamesOf(pairs);
|
|
1385
1387
|
const pairedFrom = new Set(pairs.map((pair) => pair.from));
|
|
@@ -1390,32 +1392,36 @@ const planTree = async (workspace, from, to) => {
|
|
|
1390
1392
|
from: file,
|
|
1391
1393
|
to: file
|
|
1392
1394
|
}))];
|
|
1393
|
-
|
|
1395
|
+
const classify = async (pair) => {
|
|
1394
1396
|
const oldPath = path.join(workspace, from, pair.from);
|
|
1395
1397
|
const nextPath = path.join(workspace, to, pair.to);
|
|
1396
1398
|
const paired = pair.from !== pair.to;
|
|
1397
|
-
if (!paired && renames.size === 0) {
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1401
|
-
const old = only.get(`from\0${pair.from}`) ??
|
|
1402
|
-
|
|
1403
|
-
if (!paired && old.equals(next)) continue;
|
|
1399
|
+
if (!paired && renames.size === 0) return await identical(oldPath, nextPath) ? void 0 : {
|
|
1400
|
+
same: false,
|
|
1401
|
+
change: plain$1(pair)
|
|
1402
|
+
};
|
|
1403
|
+
const [old, next] = await Promise.all([only.get(`from\0${pair.from}`) ?? readFile(oldPath), only.get(`to\0${pair.to}`) ?? readFile(nextPath)]);
|
|
1404
|
+
if (!paired && old.equals(next)) return;
|
|
1404
1405
|
const text = isText(old) ? old.toString("latin1") : void 0;
|
|
1405
1406
|
const rewrittenText = text === void 0 ? void 0 : rewrite(text, renames);
|
|
1406
1407
|
const rewritten = rewrittenText !== void 0 && rewrittenText !== text;
|
|
1407
|
-
if (!paired && !rewritten) {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1408
|
+
if (!paired && !rewritten) return {
|
|
1409
|
+
same: false,
|
|
1410
|
+
change: plain$1(pair)
|
|
1411
|
+
};
|
|
1411
1412
|
const expected = rewrittenText === void 0 ? old : Buffer.from(rewrittenText, "latin1");
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1413
|
+
return {
|
|
1414
|
+
same: expected.equals(next),
|
|
1415
|
+
change: {
|
|
1416
|
+
...pair,
|
|
1417
|
+
expected,
|
|
1418
|
+
paired,
|
|
1419
|
+
rewritten
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
};
|
|
1423
|
+
const planned = yield* Effect.forEach(candidates, (pair) => attempt(async () => await classify(pair)), { concurrency: 8 });
|
|
1424
|
+
for (const entry of planned) if (entry !== void 0) (entry.same ? same : changes).push(entry.change);
|
|
1419
1425
|
for (const file of fromOnly) if (!pairedFrom.has(file)) changes.push({
|
|
1420
1426
|
...plain$1({
|
|
1421
1427
|
from: file,
|
|
@@ -1437,7 +1443,7 @@ const planTree = async (workspace, from, to) => {
|
|
|
1437
1443
|
}),
|
|
1438
1444
|
same
|
|
1439
1445
|
};
|
|
1440
|
-
};
|
|
1446
|
+
});
|
|
1441
1447
|
const mebibytes = (bytes) => `${Math.round(bytes / 1024 / 1024)} MiB`;
|
|
1442
1448
|
const shownOmissions = 2e3;
|
|
1443
1449
|
const changeDiff = (workspace, from, to, change) => {
|
|
@@ -1469,10 +1475,7 @@ const partial = (what, omitted) => omitted === "" ? "" : `The ${what} diff is pa
|
|
|
1469
1475
|
const DiffBudget = Context.Reference("upup/DiffBudget", { defaultValue: () => () => maxDiffBytes });
|
|
1470
1476
|
const treeDiff = (workspace, from, to, limit) => Effect.gen(function* () {
|
|
1471
1477
|
const budget = limit ?? (yield* DiffBudget)(from);
|
|
1472
|
-
const { changes, same } = yield*
|
|
1473
|
-
try: async () => await planTree(workspace, from, to),
|
|
1474
|
-
catch: failure$1(from, to)
|
|
1475
|
-
});
|
|
1478
|
+
const { changes, same } = yield* planTree(workspace, from, to);
|
|
1476
1479
|
const parts = [];
|
|
1477
1480
|
const left = [];
|
|
1478
1481
|
let size = 0;
|
|
@@ -1806,12 +1809,16 @@ const majorsOf = (from, to) => {
|
|
|
1806
1809
|
};
|
|
1807
1810
|
const exists$1 = async (file) => await stat(file).then(() => true, () => false);
|
|
1808
1811
|
const emptyTree = async (directory) => await readdir(directory).then((entries) => entries.length === 0, () => false);
|
|
1809
|
-
const
|
|
1812
|
+
const majorsIn = async (workspace) => {
|
|
1813
|
+
const [from, to] = await Promise.all([listed$2(workspace, files.dependencyListFrom), listed$2(workspace, files.dependencyListTo)]);
|
|
1814
|
+
return majorsOf(from, to);
|
|
1815
|
+
};
|
|
1816
|
+
const readMajors = (workspace) => Effect.promise(async () => await majorsIn(workspace));
|
|
1810
1817
|
const read$1 = (workspace) => Effect.gen(function* () {
|
|
1811
1818
|
const meta = yield* readMeta(workspace);
|
|
1812
1819
|
return yield* Effect.tryPromise({
|
|
1813
1820
|
try: async () => {
|
|
1814
|
-
const diff = await readFile(path.join(workspace, files.diff));
|
|
1821
|
+
const [diff, majors] = await Promise.all([readFile(path.join(workspace, files.diff)), majorsIn(workspace)]);
|
|
1815
1822
|
const trees = [];
|
|
1816
1823
|
const titled = [];
|
|
1817
1824
|
const targets = /* @__PURE__ */ new Map();
|
|
@@ -1850,7 +1857,7 @@ const read$1 = (workspace) => Effect.gen(function* () {
|
|
|
1850
1857
|
trees,
|
|
1851
1858
|
files: titled,
|
|
1852
1859
|
sections,
|
|
1853
|
-
majors
|
|
1860
|
+
majors
|
|
1854
1861
|
};
|
|
1855
1862
|
},
|
|
1856
1863
|
catch: failed$1(workspace)
|
|
@@ -1920,28 +1927,18 @@ const BriefDoc = defineDoc({
|
|
|
1920
1927
|
initial: () => ({ brief: null }),
|
|
1921
1928
|
checkpointWhen: () => false
|
|
1922
1929
|
});
|
|
1923
|
-
const
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
Type.Literal("danger"),
|
|
1934
|
-
Type.Literal("no-verdict")
|
|
1935
|
-
]),
|
|
1936
|
-
rationale: Type.String({ description: "One concrete sentence justifying the verdict." }),
|
|
1937
|
-
summary: Type.String({ description: "Markdown: what the update changes that bears on the mandate, what you flagged and why you cleared or kept it." }),
|
|
1938
|
-
findings: Type.Array(Type.Object({
|
|
1939
|
-
severity,
|
|
1940
|
-
title: Type.String(),
|
|
1941
|
-
detail: Type.String({ description: "Markdown: the evidence and why it matters." }),
|
|
1942
|
-
paths: Type.Array(Type.String(), { description: "Workspace paths, with line ranges where useful." })
|
|
1930
|
+
const parameters = (schema) => Schema.toJsonSchemaDocument(schema).schema;
|
|
1931
|
+
const decodeRead = Schema.decodeUnknownSync(ReadArgs);
|
|
1932
|
+
const decodeGrep = Schema.decodeUnknownSync(GrepArgs);
|
|
1933
|
+
const decodeList = Schema.decodeUnknownSync(ListArgs);
|
|
1934
|
+
const decodeSubmitted = Schema.decodeUnknownSync(Report);
|
|
1935
|
+
const encodeReport = (submitted) => ({
|
|
1936
|
+
...submitted,
|
|
1937
|
+
findings: submitted.findings.map((finding) => ({
|
|
1938
|
+
...finding,
|
|
1939
|
+
paths: [...finding.paths]
|
|
1943
1940
|
})),
|
|
1944
|
-
unverified:
|
|
1941
|
+
unverified: [...submitted.unverified]
|
|
1945
1942
|
});
|
|
1946
1943
|
const ReportDoc = defineDoc({
|
|
1947
1944
|
kind: "upup.report",
|
|
@@ -1967,42 +1964,33 @@ const auditExtension = (workspace) => {
|
|
|
1967
1964
|
name: "read",
|
|
1968
1965
|
description: descriptions.read,
|
|
1969
1966
|
replay: "safe",
|
|
1970
|
-
parameters:
|
|
1971
|
-
|
|
1972
|
-
offset: Type.Integer({ description: parameters.read.offset }),
|
|
1973
|
-
limit: Type.Integer({ description: parameters.read.limit })
|
|
1974
|
-
}),
|
|
1975
|
-
execute: async (args, _api, invocation) => answer(await read$2(workspace, args, invocation.abortSignal))
|
|
1967
|
+
parameters: parameters(ReadArgs),
|
|
1968
|
+
execute: async (args, _api, invocation) => answer(await read$2(workspace, decodeRead(args), invocation.abortSignal))
|
|
1976
1969
|
});
|
|
1977
1970
|
const grep$1 = defineTool({
|
|
1978
1971
|
name: "grep",
|
|
1979
1972
|
description: descriptions.grep,
|
|
1980
1973
|
replay: "safe",
|
|
1981
|
-
parameters:
|
|
1982
|
-
|
|
1983
|
-
pattern: Type.String({ description: parameters.grep.pattern })
|
|
1984
|
-
}),
|
|
1985
|
-
execute: async (args, _api, invocation) => answer(await grep(workspace, args, invocation.abortSignal === void 0 ? {} : { signal: invocation.abortSignal }))
|
|
1974
|
+
parameters: parameters(GrepArgs),
|
|
1975
|
+
execute: async (args, _api, invocation) => answer(await grep(workspace, decodeGrep(args), invocation.abortSignal === void 0 ? {} : { signal: invocation.abortSignal }))
|
|
1986
1976
|
});
|
|
1987
1977
|
const list$1 = defineTool({
|
|
1988
1978
|
name: "list",
|
|
1989
1979
|
description: descriptions.list,
|
|
1990
1980
|
replay: "safe",
|
|
1991
|
-
parameters:
|
|
1992
|
-
|
|
1993
|
-
depth: Type.Integer({ description: parameters.list.depth })
|
|
1994
|
-
}),
|
|
1995
|
-
execute: async (args, _api, invocation) => answer(await list(workspace, args, invocation.abortSignal))
|
|
1981
|
+
parameters: parameters(ListArgs),
|
|
1982
|
+
execute: async (args, _api, invocation) => answer(await list(workspace, decodeList(args), invocation.abortSignal))
|
|
1996
1983
|
});
|
|
1997
1984
|
const submit = defineTool({
|
|
1998
1985
|
name: "submit_report",
|
|
1999
1986
|
description: "Submit the audit report. Call exactly once, when the investigation is done; the audit ends.",
|
|
2000
1987
|
replay: "safe",
|
|
2001
|
-
parameters:
|
|
1988
|
+
parameters: parameters(Report),
|
|
2002
1989
|
execute: async (args, api, invocation) => {
|
|
1990
|
+
const encoded = encodeReport(decodeSubmitted(args));
|
|
2003
1991
|
await api.commit(async (tx) => {
|
|
2004
1992
|
const doc = await tx.doc(ReportDoc, api.conversationId);
|
|
2005
|
-
doc.report =
|
|
1993
|
+
doc.report = encoded;
|
|
2006
1994
|
}, invocation);
|
|
2007
1995
|
return {
|
|
2008
1996
|
...text$2("Report submitted."),
|
|
@@ -2078,6 +2066,32 @@ const entriesOf$1 = (message) => {
|
|
|
2078
2066
|
}
|
|
2079
2067
|
return [];
|
|
2080
2068
|
};
|
|
2069
|
+
const TextBlock = Schema.Struct({
|
|
2070
|
+
type: Schema.Literal("text"),
|
|
2071
|
+
text: Schema.String
|
|
2072
|
+
});
|
|
2073
|
+
const ThinkingBlock = Schema.Struct({
|
|
2074
|
+
type: Schema.Literal("thinking"),
|
|
2075
|
+
thinking: Schema.String
|
|
2076
|
+
});
|
|
2077
|
+
const isTextBlock = Schema.is(TextBlock);
|
|
2078
|
+
const isThinkingBlock = Schema.is(ThinkingBlock);
|
|
2079
|
+
const decodeLive = Schema.decodeUnknownOption(Schema.Struct({ generation: Schema.optional(Schema.Struct({
|
|
2080
|
+
attempt: Schema.Finite,
|
|
2081
|
+
message: Schema.optional(Schema.Struct({ content: Schema.Array(Schema.Unknown) })),
|
|
2082
|
+
retry: Schema.optional(Schema.Struct({ error: Schema.String }))
|
|
2083
|
+
})) }));
|
|
2084
|
+
const streamed = (block) => {
|
|
2085
|
+
if (isThinkingBlock(block)) return block.thinking.trim() === "" ? [] : [{
|
|
2086
|
+
kind: "thinking",
|
|
2087
|
+
text: block.thinking
|
|
2088
|
+
}];
|
|
2089
|
+
if (isTextBlock(block)) return block.text.trim() === "" ? [] : [{
|
|
2090
|
+
kind: "text",
|
|
2091
|
+
text: block.text
|
|
2092
|
+
}];
|
|
2093
|
+
return [];
|
|
2094
|
+
};
|
|
2081
2095
|
var Transcript = class {
|
|
2082
2096
|
committed = /* @__PURE__ */ new Map();
|
|
2083
2097
|
live = [];
|
|
@@ -2085,41 +2099,20 @@ var Transcript = class {
|
|
|
2085
2099
|
add(entry) {
|
|
2086
2100
|
this.committed.set(entry.id, (entry.model ?? []).flatMap(entriesOf$1));
|
|
2087
2101
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
for (const change of event.changes) if (change.type === "text_delta" || change.type === "thinking_delta") {
|
|
2103
|
-
const kind = change.type === "text_delta" ? "text" : "thinking";
|
|
2104
|
-
const last = this.live.at(-1);
|
|
2105
|
-
if (last?.kind === kind) this.live[this.live.length - 1] = {
|
|
2106
|
-
kind,
|
|
2107
|
-
text: last.text + change.delta
|
|
2108
|
-
};
|
|
2109
|
-
else this.live.push({
|
|
2110
|
-
kind,
|
|
2111
|
-
text: change.delta
|
|
2112
|
-
});
|
|
2113
|
-
}
|
|
2114
|
-
break;
|
|
2115
|
-
case "auto_retry_start":
|
|
2116
|
-
this.retry = {
|
|
2117
|
-
kind: "retry",
|
|
2118
|
-
text: `retrying (attempt ${event.attempt}): ${event.errorMessage}`
|
|
2119
|
-
};
|
|
2120
|
-
break;
|
|
2121
|
-
case "auto_retry_end": this.retry = void 0;
|
|
2122
|
-
}
|
|
2102
|
+
view(view) {
|
|
2103
|
+
for (const entry of view.entries) this.add(entry);
|
|
2104
|
+
const generation = decodeLive(view.docs[LiveDoc.definition.kind]).pipe(Option.flatMapNullishOr((live) => live.generation));
|
|
2105
|
+
this.live = Option.match(generation, {
|
|
2106
|
+
onNone: () => [],
|
|
2107
|
+
onSome: ({ message }) => (message?.content ?? []).flatMap(streamed)
|
|
2108
|
+
});
|
|
2109
|
+
this.retry = Option.match(generation, {
|
|
2110
|
+
onNone: () => void 0,
|
|
2111
|
+
onSome: ({ attempt, retry }) => retry === void 0 ? void 0 : {
|
|
2112
|
+
kind: "retry",
|
|
2113
|
+
text: `retrying (attempt ${attempt}): ${retry.error}`
|
|
2114
|
+
}
|
|
2115
|
+
});
|
|
2123
2116
|
}
|
|
2124
2117
|
entries() {
|
|
2125
2118
|
return [
|
|
@@ -2134,16 +2127,24 @@ const attempt$1 = (evaluate) => Effect.tryPromise({
|
|
|
2134
2127
|
try: evaluate,
|
|
2135
2128
|
catch: failed
|
|
2136
2129
|
});
|
|
2130
|
+
const watched = (open, onValue) => Effect.acquireRelease(attempt$1(async () => {
|
|
2131
|
+
const watch = await open();
|
|
2132
|
+
watch.start(async (call) => {
|
|
2133
|
+
if (call.kind === "value") onValue(call.value);
|
|
2134
|
+
});
|
|
2135
|
+
return watch;
|
|
2136
|
+
}), (watch) => Effect.promise(async () => await watch.stop()).pipe(Effect.ignore));
|
|
2137
2137
|
const lastAssistant = (entries) => entries.flatMap((entry) => entry.model ?? []).findLast((message) => message.role === "assistant");
|
|
2138
|
-
const openHarness = (file,
|
|
2139
|
-
if (workspace ===
|
|
2140
|
-
const extension = auditExtension(workspace);
|
|
2138
|
+
const openHarness = (file, models, workspace) => Effect.acquireRelease(attempt$1(async () => {
|
|
2139
|
+
if (workspace === void 0 && !existsSync(file)) throw new Error(`no audit at ${file}`);
|
|
2140
|
+
const extension = auditExtension(workspace ?? "");
|
|
2141
2141
|
const registry = createRegistry();
|
|
2142
2142
|
registry.install(extension);
|
|
2143
2143
|
return await Harness.open(await openNodeSqliteStorage(file), {
|
|
2144
2144
|
models,
|
|
2145
2145
|
registry,
|
|
2146
|
-
settings: { extensions: [extension] }
|
|
2146
|
+
settings: { extensions: [extension] },
|
|
2147
|
+
readOnly: workspace === void 0
|
|
2147
2148
|
}, context);
|
|
2148
2149
|
}), (harness) => Effect.promise(async () => {
|
|
2149
2150
|
await harness.close(context);
|
|
@@ -2156,7 +2157,7 @@ const encodeBrief = (brief) => ({
|
|
|
2156
2157
|
artifacts: [...brief.artifacts]
|
|
2157
2158
|
});
|
|
2158
2159
|
const run$5 = (options) => Effect.scoped(Effect.gen(function* () {
|
|
2159
|
-
const harness = yield* openHarness(options.file, options.
|
|
2160
|
+
const harness = yield* openHarness(options.file, options.models, options.brief.workspace);
|
|
2160
2161
|
const root = yield* attempt$1(async () => await harness.root(context, {
|
|
2161
2162
|
agent: {
|
|
2162
2163
|
model: {
|
|
@@ -2171,13 +2172,12 @@ const run$5 = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
2171
2172
|
}
|
|
2172
2173
|
}));
|
|
2173
2174
|
const transcript = new Transcript();
|
|
2174
|
-
const
|
|
2175
|
-
|
|
2176
|
-
options.onTranscript(transcript.entries());
|
|
2177
|
-
events.start(async (batch) => {
|
|
2178
|
-
for (const event of batch) transcript.apply(event);
|
|
2175
|
+
const views = yield* watched(async () => await root.watch(context), (view) => {
|
|
2176
|
+
transcript.view(view);
|
|
2179
2177
|
options.onTranscript(transcript.entries());
|
|
2180
2178
|
});
|
|
2179
|
+
transcript.view(views.value);
|
|
2180
|
+
options.onTranscript(transcript.entries());
|
|
2181
2181
|
harness.resume();
|
|
2182
2182
|
const settled = yield* Effect.tryPromise({
|
|
2183
2183
|
try: async (signal) => {
|
|
@@ -2204,20 +2204,29 @@ const run$5 = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
2204
2204
|
_tag: "Reported",
|
|
2205
2205
|
report: decoded.value
|
|
2206
2206
|
};
|
|
2207
|
-
const final = yield* attempt$1(async () => await
|
|
2208
|
-
yield* attempt$1(async () => await final.stop());
|
|
2207
|
+
const final = yield* attempt$1(async () => await root.context(context));
|
|
2209
2208
|
if (settled.value.status === "unanswered") return {
|
|
2210
2209
|
_tag: "Failed",
|
|
2211
2210
|
issue: Predicate.isString(settled.value.detail) ? settled.value.detail : settled.value.reason
|
|
2212
2211
|
};
|
|
2213
2212
|
return {
|
|
2214
2213
|
_tag: "Failed",
|
|
2215
|
-
issue: lastAssistant(final.
|
|
2214
|
+
issue: lastAssistant(final.entries)?.errorMessage ?? "the model finished without submitting a report"
|
|
2216
2215
|
};
|
|
2217
2216
|
}));
|
|
2217
|
+
const storedRoot = (file, models) => Effect.gen(function* () {
|
|
2218
|
+
const harness = yield* openHarness(file, models);
|
|
2219
|
+
return {
|
|
2220
|
+
harness,
|
|
2221
|
+
root: yield* attempt$1(async () => await harness.conversation(ROOT_CONVERSATION_ID, context))
|
|
2222
|
+
};
|
|
2223
|
+
});
|
|
2218
2224
|
const stored = (file, models) => Effect.scoped(Effect.gen(function* () {
|
|
2219
|
-
const harness = yield*
|
|
2220
|
-
|
|
2225
|
+
const { harness, root } = yield* storedRoot(file, models);
|
|
2226
|
+
if (root === void 0) return {
|
|
2227
|
+
brief: Option.none(),
|
|
2228
|
+
report: Option.none()
|
|
2229
|
+
};
|
|
2221
2230
|
const brief = yield* attempt$1(async () => await harness.snapshot(BriefDoc, root.id, context));
|
|
2222
2231
|
const submitted = yield* attempt$1(async () => await harness.snapshot(ReportDoc, root.id, context));
|
|
2223
2232
|
return {
|
|
@@ -2226,12 +2235,12 @@ const stored = (file, models) => Effect.scoped(Effect.gen(function* () {
|
|
|
2226
2235
|
};
|
|
2227
2236
|
}));
|
|
2228
2237
|
const transcript = (file, models) => Effect.scoped(Effect.gen(function* () {
|
|
2229
|
-
const
|
|
2230
|
-
const root = yield* attempt$1(async () => await harness.root(context));
|
|
2231
|
-
const events = yield* attempt$1(async () => await watchEvents(harness, root.id, context));
|
|
2232
|
-
yield* attempt$1(async () => await events.stop());
|
|
2238
|
+
const { root } = yield* storedRoot(file, models);
|
|
2233
2239
|
const built = new Transcript();
|
|
2234
|
-
|
|
2240
|
+
if (root !== void 0) {
|
|
2241
|
+
const active = yield* attempt$1(async () => await root.context(context));
|
|
2242
|
+
for (const entry of active.entries) built.add(entry);
|
|
2243
|
+
}
|
|
2235
2244
|
return built.entries();
|
|
2236
2245
|
}));
|
|
2237
2246
|
// src/inventory/versions.ts
|
|
@@ -2268,7 +2277,7 @@ var LookupFailed = class extends Schema.TaggedError()("LookupFailed", {
|
|
|
2268
2277
|
url: Schema.String,
|
|
2269
2278
|
issue: Schema.String
|
|
2270
2279
|
}) {};
|
|
2271
|
-
const Release$
|
|
2280
|
+
const Release$3 = Schema.fromJsonString(Schema.Struct({
|
|
2272
2281
|
vers: Schema.String,
|
|
2273
2282
|
yanked: Schema.Boolean
|
|
2274
2283
|
}));
|
|
@@ -2285,7 +2294,7 @@ const newestRelease = Effect.fn("CratesIndex.newestRelease")(function* (client,
|
|
|
2285
2294
|
issue: error.message
|
|
2286
2295
|
});
|
|
2287
2296
|
const text = yield* HttpClient.filterStatusOk(client).get(url).pipe(Effect.flatMap((response) => response.text), Effect.mapError(failed));
|
|
2288
|
-
const releases = yield* Effect.forEach(text.split("\n").filter((line) => line.trim().length > 0), (line) => Schema.decodeEffect(Release$
|
|
2297
|
+
const releases = yield* Effect.forEach(text.split("\n").filter((line) => line.trim().length > 0), (line) => Schema.decodeEffect(Release$3)(line).pipe(Effect.mapError(failed)));
|
|
2289
2298
|
return newestOffer(current, releases.filter((release) => !release.yanked).map((release) => release.vers));
|
|
2290
2299
|
});
|
|
2291
2300
|
// src/registry/Metadata.ts
|
|
@@ -2338,7 +2347,7 @@ const escape = (path) => path.replaceAll(/[A-Z]/gu, (letter) => `!${letter.toLow
|
|
|
2338
2347
|
const deprecation = (mod) => Option.fromNullishOr(/^\/\/\s*Deprecated:\s*(?<reason>.+)$/mu.exec(mod)?.groups?.["reason"]?.trim());
|
|
2339
2348
|
const lookup$5 = Effect.fn("go.lookup")(function* (client, wanted) {
|
|
2340
2349
|
const base = `https://proxy.golang.org/${escape(wanted.name)}/@v`;
|
|
2341
|
-
const [latest, infos] = yield* Effect.all([getJson(client, `https://proxy.golang.org/${escape(wanted.name)}/@latest`, Info$2), Effect.forEach(wanted.versions, (version) => getJson(client, `${base}/${escape(version)}.info`, Info$2).pipe(Effect.option), { concurrency: 2 })], { concurrency:
|
|
2350
|
+
const [latest, infos] = yield* Effect.all([getJson(client, `https://proxy.golang.org/${escape(wanted.name)}/@latest`, Info$2), Effect.forEach(wanted.versions, (version) => getJson(client, `${base}/${escape(version)}.info`, Info$2).pipe(Effect.option), { concurrency: 2 })], { concurrency: "unbounded" });
|
|
2342
2351
|
const mod = yield* getText(client, `${base}/${escape(latest.Version)}.mod`);
|
|
2343
2352
|
const deprecated = deprecation(mod);
|
|
2344
2353
|
const found = infos.flatMap((info) => Option.isSome(info) ? [info.value] : []);
|
|
@@ -4849,30 +4858,36 @@ const resolveFormula = Effect.fn("brew.resolveFormula")(function* (client, sourc
|
|
|
4849
4858
|
digest: createHash("sha256").update(body).digest("hex")
|
|
4850
4859
|
};
|
|
4851
4860
|
});
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
if (complete()) break;
|
|
4861
|
+
const scanHistory = Effect.gen(function* () {
|
|
4862
|
+
let to;
|
|
4863
|
+
let from;
|
|
4864
|
+
let endpoint = `${githubApi}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits?${new URLSearchParams({
|
|
4865
|
+
path: source.rubySourcePath,
|
|
4866
|
+
per_page: `${historyPageSize}`
|
|
4867
|
+
}).toString()}`;
|
|
4868
|
+
let scanned = 0;
|
|
4869
|
+
const complete = () => from !== void 0 && to !== void 0;
|
|
4870
|
+
for (let page = 0; endpoint !== "" && page < maxHistoryPages && !complete(); page += 1) {
|
|
4871
|
+
const { body, link } = yield* githubGet(client, endpoint, token).pipe(Effect.mapError(prefixed("query brew formula history")));
|
|
4872
|
+
const commits = yield* commitsOf(body, endpoint);
|
|
4873
|
+
for (const commit of commits) {
|
|
4874
|
+
scanned += 1;
|
|
4875
|
+
const { snapshot: candidate } = yield* snapshot(commit.sha, commit.message);
|
|
4876
|
+
if (to === void 0 && matchesVersion(candidate.text, toVersion)) to = candidate;
|
|
4877
|
+
if (from === void 0 && matchesVersion(candidate.text, fromVersion)) from = candidate;
|
|
4878
|
+
if (complete()) break;
|
|
4879
|
+
}
|
|
4880
|
+
endpoint = complete() ? "" : nextPage(endpoint, link);
|
|
4873
4881
|
}
|
|
4874
|
-
|
|
4875
|
-
|
|
4882
|
+
return {
|
|
4883
|
+
from,
|
|
4884
|
+
to,
|
|
4885
|
+
scanned
|
|
4886
|
+
};
|
|
4887
|
+
});
|
|
4888
|
+
const [current, history] = yield* Effect.all([source.tapGitHead === "" ? Effect.succeedNone : Effect.option(snapshot(source.tapGitHead, "tap revision reported by brew info")), scanHistory], { concurrency: "unbounded" });
|
|
4889
|
+
const { from, scanned } = history;
|
|
4890
|
+
const to = current._tag === "Some" && matchesVersion(current.value.snapshot.text, toVersion) && (source.rubySourceChecksum === "" || source.rubySourceChecksum.toLowerCase() === current.value.digest) ? current.value.snapshot : history.to;
|
|
4876
4891
|
if (to === void 0) return yield* new EvidenceUnavailable({
|
|
4877
4892
|
source: "brew",
|
|
4878
4893
|
issue: `brew formula history has no snapshot for target version ${toVersion} within the newest ${scanned} commits`
|
|
@@ -5277,13 +5292,14 @@ const payloadDiff = Effect.fn("brew.payloadDiff")(function* (client, workspace,
|
|
|
5277
5292
|
const { name } = shortName(artifact);
|
|
5278
5293
|
const oldStrip = yield* strip(name, plan.old);
|
|
5279
5294
|
const newStrip = yield* strip(name, plan.new);
|
|
5280
|
-
const oldManifest = yield* payloadManifest(client, "installed", plan.old, oldStrip, path.join(workspace, files.payloadTreeFrom));
|
|
5295
|
+
const [oldManifest, newManifest] = yield* Effect.all([payloadManifest(client, "installed", plan.old, oldStrip, path.join(workspace, files.payloadTreeFrom)), payloadManifest(client, "target", plan.new, newStrip, path.join(workspace, files.payloadTreeTo))], { concurrency: "unbounded" });
|
|
5281
5296
|
if (oldManifest === void 0) return yield* unavailablePayload(workspace, "installed Homebrew payload archive format is unsupported");
|
|
5282
|
-
const newManifest = yield* payloadManifest(client, "target", plan.new, newStrip, path.join(workspace, files.payloadTreeTo));
|
|
5283
5297
|
if (newManifest === void 0) return yield* unavailablePayload(workspace, "target Homebrew payload archive format is unsupported");
|
|
5284
|
-
const manifests = yield*
|
|
5285
|
-
|
|
5286
|
-
|
|
5298
|
+
const [manifests, binaries, trees] = yield* Effect.all([
|
|
5299
|
+
writeFileDiff(workspace, files.payloadFrom, files.payloadTo, manifestJson(oldManifest), manifestJson(newManifest)),
|
|
5300
|
+
writeFileDiff(workspace, files.binaryFrom, files.binaryTo, json(binarySummaries(oldManifest)), json(binarySummaries(newManifest))),
|
|
5301
|
+
treeDiff(workspace, files.payloadTreeFrom, files.payloadTreeTo)
|
|
5302
|
+
], { concurrency: "unbounded" });
|
|
5287
5303
|
const contents = trees.diff;
|
|
5288
5304
|
const contentDetail = [" Extracted payload file contents are diffed.", partial("payload", trees.omitted)].filter((sentence) => sentence !== "").join(" ");
|
|
5289
5305
|
const origin = plan.old.origin === "" ? "" : ` (pinned by ${plan.old.origin})`;
|
|
@@ -5762,9 +5778,9 @@ const writeChanges = Effect.fn("git.writeChanges")(function* (from, to, destinat
|
|
|
5762
5778
|
modes
|
|
5763
5779
|
};
|
|
5764
5780
|
const contents = readBatch((yield* git(["cat-file", "--batch"], objects.map((object) => `${object}\n`).join(""))).stdout, objects);
|
|
5765
|
-
yield* Effect.tryPromise({
|
|
5781
|
+
yield* Effect.forEach(changes, (change) => Effect.tryPromise({
|
|
5766
5782
|
try: async () => {
|
|
5767
|
-
for (const
|
|
5783
|
+
for (const [object, root] of [[change.fromObject, destination.from], [change.toObject, destination.to]]) {
|
|
5768
5784
|
if (object === void 0) continue;
|
|
5769
5785
|
const file = path.join(root, change.path);
|
|
5770
5786
|
await mkdir(path.dirname(file), {
|
|
@@ -5778,6 +5794,9 @@ const writeChanges = Effect.fn("git.writeChanges")(function* (from, to, destinat
|
|
|
5778
5794
|
repository,
|
|
5779
5795
|
issue: `write source files: ${String(error)}`
|
|
5780
5796
|
})
|
|
5797
|
+
}), {
|
|
5798
|
+
concurrency: 8,
|
|
5799
|
+
discard: true
|
|
5781
5800
|
});
|
|
5782
5801
|
return {
|
|
5783
5802
|
files: changes.length,
|
|
@@ -5838,9 +5857,9 @@ const moduleChanges = (from, to, released) => {
|
|
|
5838
5857
|
};
|
|
5839
5858
|
};
|
|
5840
5859
|
const readSummaries = (workspace, file) => Effect.tryPromise(async () => await readFile(path.join(workspace, file), "utf-8")).pipe(Effect.flatMap((text) => Schema.decodeEffect(Summaries)(text)), Effect.option);
|
|
5860
|
+
const readBinarySummaries = (workspace) => Effect.all([readSummaries(workspace, files.binaryFrom), readSummaries(workspace, files.binaryTo)], { concurrency: "unbounded" });
|
|
5841
5861
|
const goSourceEvidence = Effect.fn("goSource.evidence")(function* (client, workspace, released) {
|
|
5842
|
-
const from = yield*
|
|
5843
|
-
const to = yield* readSummaries(workspace, files.binaryTo);
|
|
5862
|
+
const [from, to] = yield* readBinarySummaries(workspace);
|
|
5844
5863
|
if (from._tag === "None" || to._tag === "None") return;
|
|
5845
5864
|
const { changes, reason } = moduleChanges(from.value, to.value, released);
|
|
5846
5865
|
if (changes.length === 0) return {
|
|
@@ -5852,7 +5871,7 @@ const goSourceEvidence = Effect.fn("goSource.evidence")(function* (client, works
|
|
|
5852
5871
|
const plan = yield* resolve$3(client, "go", modulePath, version);
|
|
5853
5872
|
return yield* materialize$2(client, plan, path.join(workspace, tree, modulePath));
|
|
5854
5873
|
});
|
|
5855
|
-
const fetched = yield* Effect.forEach(changes, (change) => Effect.result(Effect.all([fetch(change.path, change.from, files.goSourceFrom), fetch(change.path, change.to, files.goSourceTo)], { concurrency:
|
|
5874
|
+
const fetched = yield* Effect.forEach(changes, (change) => Effect.result(Effect.all([fetch(change.path, change.from, files.goSourceFrom), fetch(change.path, change.to, files.goSourceTo)], { concurrency: "unbounded" })), { concurrency: 2 });
|
|
5856
5875
|
const failures = changes.flatMap((change, index) => {
|
|
5857
5876
|
const result = fetched[index];
|
|
5858
5877
|
return result?._tag === "Failure" ? [`${change.path}: ${result.failure.issue}`] : [];
|
|
@@ -5911,8 +5930,7 @@ const buildInfoSources = (from, to) => {
|
|
|
5911
5930
|
return [...sources.values()].slice(0, 4);
|
|
5912
5931
|
};
|
|
5913
5932
|
const readBuildInfoSources = Effect.fn("goSource.buildInfoSources")(function* (workspace) {
|
|
5914
|
-
const from = yield*
|
|
5915
|
-
const to = yield* readSummaries(workspace, files.binaryTo);
|
|
5933
|
+
const [from, to] = yield* readBinarySummaries(workspace);
|
|
5916
5934
|
return from._tag === "None" || to._tag === "None" ? [] : buildInfoSources(from.value, to.value);
|
|
5917
5935
|
});
|
|
5918
5936
|
// src/audit/source-evidence.ts
|
|
@@ -6224,7 +6242,7 @@ const releaseAssetSources = Effect.fn("sourceLink.releaseAssets")(function* (cli
|
|
|
6224
6242
|
} })), Effect.catch((error) => Effect.succeed(gap(error.issue))));
|
|
6225
6243
|
}
|
|
6226
6244
|
return Effect.succeed(gap("no release of the repository publishes this exact binary"));
|
|
6227
|
-
});
|
|
6245
|
+
}, { concurrency: 4 });
|
|
6228
6246
|
});
|
|
6229
6247
|
// src/audit/brew.ts
|
|
6230
6248
|
const caskArchiveUnavailable = "compiled cask payloads are not source archives";
|
|
@@ -6290,7 +6308,7 @@ const sourceDiff = Effect.fn("brew.sourceDiff")(function* (client, workspace, pa
|
|
|
6290
6308
|
const [gap] = yield* Effect.all([sourceTree(client, pair.old, path.join(workspace, files.sourceFrom)).pipe(Effect.as(""), Effect.catch((error) => {
|
|
6291
6309
|
const reason = historicalGap(pair.old.url, error);
|
|
6292
6310
|
return reason === void 0 ? Effect.fail(labelled("download old brew source archive")(error)) : Effect.succeed(reason);
|
|
6293
|
-
})), sourceTree(client, pair.new, path.join(workspace, files.source)).pipe(Effect.mapError(labelled("download new brew source archive")))], { concurrency:
|
|
6311
|
+
})), sourceTree(client, pair.new, path.join(workspace, files.source)).pipe(Effect.mapError(labelled("download new brew source archive")))], { concurrency: "unbounded" });
|
|
6294
6312
|
if (gap !== "") return {
|
|
6295
6313
|
diff: Buffer.alloc(0),
|
|
6296
6314
|
reason: gap,
|
|
@@ -6327,9 +6345,11 @@ const sourceEvidence = Effect.fn("brew.sourceEvidence")(function* (client, works
|
|
|
6327
6345
|
};
|
|
6328
6346
|
});
|
|
6329
6347
|
const materialize$1 = Effect.fn("brew.materialize")(function* (client, workspace, identity, plan, key) {
|
|
6330
|
-
const formula = yield*
|
|
6331
|
-
|
|
6332
|
-
|
|
6348
|
+
const [formula, source, payload] = yield* Effect.all([
|
|
6349
|
+
materializeFormula(workspace, plan.formula),
|
|
6350
|
+
sourceEvidence(client, workspace, archives(plan.formula), plan.sourceGap),
|
|
6351
|
+
materializePayload(client, workspace, identity.delta.artifact, plan.payload)
|
|
6352
|
+
], { concurrency: "unbounded" });
|
|
6333
6353
|
const goSource = source.included ? void 0 : yield* goSourceEvidence(client, workspace, identity.delta);
|
|
6334
6354
|
const sources = goSource?.included === true || source.included ? [] : [...plan.repository === void 0 ? [] : [plan.repository], ...yield* readBuildInfoSources(workspace)];
|
|
6335
6355
|
const repository = sources.length === 0 ? void 0 : yield* materializeSources(workspace, sources);
|
|
@@ -6356,7 +6376,7 @@ const resolve$2 = Effect.fn("brew.resolve")(function* (client, identity) {
|
|
|
6356
6376
|
const formulaGap = formula.fromGap === "" ? archiveUnavailableReason(archives(formula)) : "the installed version's formula snapshot is unavailable";
|
|
6357
6377
|
const sourceGap = kind === "cask" ? caskArchiveUnavailable : formulaGap;
|
|
6358
6378
|
const payload = yield* resolvePayload(info, artifact, from, to, formula.from?.text);
|
|
6359
|
-
const released = sourceGap !== "" && payload.old.url !== "" && payload.new.url !== "" ? yield* Effect.all([releaseTagSource(payload.old.url), releaseTagSource(payload.new.url)], { concurrency:
|
|
6379
|
+
const released = sourceGap !== "" && payload.old.url !== "" && payload.new.url !== "" ? yield* Effect.all([releaseTagSource(payload.old.url), releaseTagSource(payload.new.url)], { concurrency: "unbounded" }) : void 0;
|
|
6360
6380
|
const repository = released === void 0 || released.every((side) => "gap" in side) ? void 0 : {
|
|
6361
6381
|
label: artifact,
|
|
6362
6382
|
from: released[0],
|
|
@@ -6421,7 +6441,7 @@ const workspaceOf$1 = Effect.fn("githubActions.workspace")(function* (workspace,
|
|
|
6421
6441
|
const githubActionPreparer = (repositoryOf, options = {}) => Effect.fn("githubActions.prepare")(function* (_client, identity) {
|
|
6422
6442
|
const { artifact, from, to } = identity.delta;
|
|
6423
6443
|
const repository = repositoryOf(artifact);
|
|
6424
|
-
const [fromCommit, toCommit] = yield* Effect.all([tagCommit(repository, from, options), tagCommit(repository, to, options)], { concurrency:
|
|
6444
|
+
const [fromCommit, toCommit] = yield* Effect.all([tagCommit(repository, from, options), tagCommit(repository, to, options)], { concurrency: "unbounded" }).pipe(Effect.mapError(unavailable$1));
|
|
6425
6445
|
const listed = identity.context["revision"];
|
|
6426
6446
|
if (listed !== toCommit) return yield* new EvidenceUnavailable({
|
|
6427
6447
|
source: repository,
|
|
@@ -6447,7 +6467,7 @@ const githubActionPreparer = (repositoryOf, options = {}) => Effect.fn("githubAc
|
|
|
6447
6467
|
const prepareGitHubAction = githubActionPreparer(githubRepository);
|
|
6448
6468
|
// src/audit/pypi.ts
|
|
6449
6469
|
const optional = (schema) => Schema.optionalKey(Schema.NullOr(schema));
|
|
6450
|
-
const Release$
|
|
6470
|
+
const Release$2 = Schema.Struct({ urls: optional(Schema.Array(Schema.Struct({
|
|
6451
6471
|
filename: optional(Schema.String),
|
|
6452
6472
|
url: optional(Schema.String),
|
|
6453
6473
|
packagetype: optional(Schema.String),
|
|
@@ -6538,7 +6558,7 @@ const artifactPlan = ([file, reason]) => {
|
|
|
6538
6558
|
};
|
|
6539
6559
|
const resolve$1 = Effect.fn("pypi.resolve")(function* (client, name, version, pythonVersion) {
|
|
6540
6560
|
const source = releaseUrl(name, version);
|
|
6541
|
-
const files = ((yield* getJson(client, source, Release$
|
|
6561
|
+
const files = ((yield* getJson(client, source, Release$2).pipe(Effect.mapError((error) => new EvidenceUnavailable({
|
|
6542
6562
|
source,
|
|
6543
6563
|
issue: error.issue
|
|
6544
6564
|
})))).urls ?? []).map((file) => ({
|
|
@@ -6618,12 +6638,14 @@ const pypiWorkspace = Effect.fn("pypi.workspace")(function* (client, workspace,
|
|
|
6618
6638
|
issue: error instanceof Error ? error.message : String(error)
|
|
6619
6639
|
})
|
|
6620
6640
|
});
|
|
6621
|
-
const [from, to] = yield* Effect.all([materialize(client, plans.from, path.join(workspace, files.from), path.join(workspace, files.wheelFrom)), materialize(client, plans.to, path.join(workspace, files.to), path.join(workspace, files.wheelTo))], { concurrency:
|
|
6622
|
-
const sources = yield*
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6641
|
+
const [from, to] = yield* Effect.all([materialize(client, plans.from, path.join(workspace, files.from), path.join(workspace, files.wheelFrom)), materialize(client, plans.to, path.join(workspace, files.to), path.join(workspace, files.wheelTo))], { concurrency: "unbounded" });
|
|
6642
|
+
const [sources, records, sourceBinaries, wheels, wheelBinaries] = yield* Effect.all([
|
|
6643
|
+
treeDiff(workspace, files.from, files.to),
|
|
6644
|
+
writeFileDiff(workspace, files.pypiFrom, files.pypiTo, manifestJson(from), manifestJson(to)),
|
|
6645
|
+
writeFileDiff(workspace, files.binaryFrom, files.binaryTo, json(binarySummaries(from.publisher_sdist?.archive_manifest)), json(binarySummaries(to.publisher_sdist?.archive_manifest))),
|
|
6646
|
+
treeDiff(workspace, files.wheelFrom, files.wheelTo),
|
|
6647
|
+
writeFileDiff(workspace, files.binaryWheelFrom, files.binaryWheelTo, json(binarySummaries(from.compatible_universal_wheel?.archive_manifest)), json(binarySummaries(to.compatible_universal_wheel?.archive_manifest)))
|
|
6648
|
+
], { concurrency: "unbounded" });
|
|
6627
6649
|
const detail = `Checksum-verified PyPI publisher artifacts; ${pairedDetail("source distributions", from.publisher_sdist, to.publisher_sdist)}; ${pairedDetail("compatible universal wheel candidates", from.compatible_universal_wheel, to.compatible_universal_wheel)}; the installer’s exact resolver selection is not known.`;
|
|
6628
6650
|
return yield* write(workspace, identity, Buffer.concat([
|
|
6629
6651
|
sources.diff,
|
|
@@ -6640,7 +6662,7 @@ const pypiWorkspace = Effect.fn("pypi.workspace")(function* (client, workspace,
|
|
|
6640
6662
|
const preparePyPI = Effect.fn("pypi.prepare")(function* (client, identity) {
|
|
6641
6663
|
const { artifact, from, to } = identity.delta;
|
|
6642
6664
|
const python = identity.context["python"] ?? "";
|
|
6643
|
-
const [fromPlan, toPlan] = yield* Effect.all([resolve$1(client, artifact, from, python), resolve$1(client, artifact, to, python)], { concurrency:
|
|
6665
|
+
const [fromPlan, toPlan] = yield* Effect.all([resolve$1(client, artifact, from, python), resolve$1(client, artifact, to, python)], { concurrency: "unbounded" });
|
|
6644
6666
|
const plans = {
|
|
6645
6667
|
from: fromPlan,
|
|
6646
6668
|
to: toPlan
|
|
@@ -6733,7 +6755,7 @@ const resolveDependencies = Effect.fn("npmDependencies.resolve")(function* (clie
|
|
|
6733
6755
|
const outcomes = yield* Effect.forEach(changedNames, (dependency) => Effect.gen(function* () {
|
|
6734
6756
|
if (!packageName.test(dependency)) return Result.fail("its name is not a valid registry package name");
|
|
6735
6757
|
const side = (declared) => declared === void 0 ? Effect.undefined : resolveSide(client, host, dependency, declared);
|
|
6736
|
-
const resolved = yield* Effect.result(Effect.all([side(before.get(dependency)), side(after.get(dependency))], { concurrency:
|
|
6758
|
+
const resolved = yield* Effect.result(Effect.all([side(before.get(dependency)), side(after.get(dependency))], { concurrency: "unbounded" }));
|
|
6737
6759
|
return Result.isFailure(resolved) ? Result.fail(resolved.failure.issue) : Result.succeed(resolved.success);
|
|
6738
6760
|
}), { concurrency: 4 });
|
|
6739
6761
|
for (const [index, dependency] of changedNames.entries()) {
|
|
@@ -6840,8 +6862,8 @@ const materializeDependencies = Effect.fn("npmDependencies.materialize")(functio
|
|
|
6840
6862
|
issue: error instanceof Error ? error.message : String(error)
|
|
6841
6863
|
})
|
|
6842
6864
|
});
|
|
6843
|
-
const sides = yield* Effect.forEach(dependencies.changed, (dependency) => Effect.all([fetchSide(client, dependency.from, path.join(workspace, files.dependenciesFrom, dependency.name)), fetchSide(client, dependency.to, path.join(workspace, files.dependenciesTo, dependency.name))], { concurrency:
|
|
6844
|
-
const summaries = yield* writeFileDiff(workspace, files.dependencyListFrom, files.dependencyListTo, record(dependencies, dependencies.changed.map((dependency, index) => ({
|
|
6865
|
+
const sides = yield* Effect.forEach(dependencies.changed, (dependency) => Effect.all([fetchSide(client, dependency.from, path.join(workspace, files.dependenciesFrom, dependency.name)), fetchSide(client, dependency.to, path.join(workspace, files.dependenciesTo, dependency.name))], { concurrency: "unbounded" }), { concurrency: 4 });
|
|
6866
|
+
const [summaries, trees] = yield* Effect.all([writeFileDiff(workspace, files.dependencyListFrom, files.dependencyListTo, record(dependencies, dependencies.changed.map((dependency, index) => ({
|
|
6845
6867
|
name: dependency.name,
|
|
6846
6868
|
...dependency.from === void 0 ? {} : { resolved: dependency.from },
|
|
6847
6869
|
...sides[index]?.[0] === void 0 ? {} : { fetched: sides[index][0] }
|
|
@@ -6849,8 +6871,7 @@ const materializeDependencies = Effect.fn("npmDependencies.materialize")(functio
|
|
|
6849
6871
|
name: dependency.name,
|
|
6850
6872
|
...dependency.to === void 0 ? {} : { resolved: dependency.to },
|
|
6851
6873
|
...sides[index]?.[1] === void 0 ? {} : { fetched: sides[index][1] }
|
|
6852
|
-
}))));
|
|
6853
|
-
const trees = yield* treeDiff(workspace, files.dependenciesFrom, files.dependenciesTo);
|
|
6874
|
+
})))), treeDiff(workspace, files.dependenciesFrom, files.dependenciesTo)], { concurrency: "unbounded" });
|
|
6854
6875
|
const failed = dependencies.changed.filter((_, index) => (sides[index] ?? []).some((side) => side?.error !== void 0));
|
|
6855
6876
|
const included = dependencies.changed.map((dependency) => `${dependency.name} ${dependency.from?.plan.version ?? "(new)"}${dependency.from?.lowest === true ? " (lowest of its range)" : ""} -> ${dependency.to?.plan.version ?? "(removed)"}${dependency.to?.bundled === true ? " (now a devDependency, so probably bundled into the package)" : ""}`);
|
|
6856
6877
|
const noneIncluded = dependencies.omitted.length === 0 ? "No direct dependency's declared version changed." : "No changed direct dependency is compared.";
|
|
@@ -6883,10 +6904,12 @@ const materializeDependencies = Effect.fn("npmDependencies.materialize")(functio
|
|
|
6883
6904
|
// src/audit/registry-evidence.ts
|
|
6884
6905
|
const registryDetail = "Both published packages were downloaded and their checksums verified. File types and modes, and any native binaries, are summarised without running them.";
|
|
6885
6906
|
const registryWorkspace = Effect.fn("evidence.registryWorkspace")(function* (client, workspace, identity, plans, npm, key) {
|
|
6886
|
-
const [from, to] = yield* Effect.all([materialize$2(client, plans.from, path.join(workspace, files.from)), materialize$2(client, plans.to, path.join(workspace, files.to))], { concurrency:
|
|
6887
|
-
const artifacts = yield*
|
|
6888
|
-
|
|
6889
|
-
|
|
6907
|
+
const [from, to] = yield* Effect.all([materialize$2(client, plans.from, path.join(workspace, files.from)), materialize$2(client, plans.to, path.join(workspace, files.to))], { concurrency: "unbounded" });
|
|
6908
|
+
const [artifacts, trees, binaries] = yield* Effect.all([
|
|
6909
|
+
writeFileDiff(workspace, files.artifactFrom, files.artifactTo, manifestJson(from), manifestJson(to)),
|
|
6910
|
+
treeDiff(workspace, files.from, files.to),
|
|
6911
|
+
writeFileDiff(workspace, files.binaryFrom, files.binaryTo, json(binarySummaries(from)), json(binarySummaries(to)))
|
|
6912
|
+
], { concurrency: "unbounded" });
|
|
6890
6913
|
const treeDetail = [registryDetail, partial("artifact", trees.omitted)].filter((sentence) => sentence !== "").join(" ");
|
|
6891
6914
|
if (npm === void 0) return yield* write(workspace, identity, Buffer.concat([
|
|
6892
6915
|
artifacts,
|
|
@@ -6920,7 +6943,7 @@ const npmExtras = Effect.fn("evidence.npmExtras")(function* (client, name, from,
|
|
|
6920
6943
|
cpu: process.arch
|
|
6921
6944
|
});
|
|
6922
6945
|
const verifier = yield* Effect.cached(sigstoreVerifier);
|
|
6923
|
-
const [fromSource, toSource] = yield* Effect.all([npmSource(client, name, from, fromManifest, verifier), npmSource(client, name, to, toManifest, verifier)], { concurrency:
|
|
6946
|
+
const [fromSource, toSource] = yield* Effect.all([npmSource(client, name, from, fromManifest, verifier), npmSource(client, name, to, toManifest, verifier)], { concurrency: "unbounded" });
|
|
6924
6947
|
const { directory } = npmRepository(toManifest);
|
|
6925
6948
|
return {
|
|
6926
6949
|
dependencies,
|
|
@@ -6937,8 +6960,8 @@ const prepareRegistry = Effect.fn("evidence.prepareRegistry")(function* (client,
|
|
|
6937
6960
|
const manifests = ecosystem === "npm" ? yield* Effect.all({
|
|
6938
6961
|
from: npmVersion(client, artifact, from),
|
|
6939
6962
|
to: npmVersion(client, artifact, to)
|
|
6940
|
-
}, { concurrency:
|
|
6941
|
-
const [fromPlan, toPlan] = yield* Effect.all(manifests === void 0 ? [resolve$3(client, ecosystem, artifact, from), resolve$3(client, ecosystem, artifact, to)] : [npmPlan(artifact, from, manifests.from), npmPlan(artifact, to, manifests.to)], { concurrency:
|
|
6963
|
+
}, { concurrency: "unbounded" }) : void 0;
|
|
6964
|
+
const [fromPlan, toPlan] = yield* Effect.all(manifests === void 0 ? [resolve$3(client, ecosystem, artifact, from), resolve$3(client, ecosystem, artifact, to)] : [npmPlan(artifact, from, manifests.from), npmPlan(artifact, to, manifests.to)], { concurrency: "unbounded" });
|
|
6942
6965
|
const plans = {
|
|
6943
6966
|
from: fromPlan,
|
|
6944
6967
|
to: toPlan
|
|
@@ -7313,10 +7336,9 @@ const layer$5 = (cacheDir) => Layer.effect(Audits, Effect.gen(function* () {
|
|
|
7313
7336
|
}));
|
|
7314
7337
|
const started = yield* Clock.currentTimeMillis;
|
|
7315
7338
|
const base = path.join(root, "workspaces");
|
|
7316
|
-
|
|
7317
|
-
if (name.endsWith(".lock")) continue;
|
|
7339
|
+
yield* Effect.forEach((yield* children(base)).filter((name) => !name.endsWith(".lock")), (name) => {
|
|
7318
7340
|
const workspace = path.join(base, name);
|
|
7319
|
-
|
|
7341
|
+
return Effect.scoped(Effect.gen(function* () {
|
|
7320
7342
|
if (!(yield* tryHold(workspace))) return;
|
|
7321
7343
|
const prepared = yield* Effect.promise(async () => await stat(path.join(workspace, files.meta)).then((info) => info.mtimeMs, () => 0));
|
|
7322
7344
|
if (started - prepared > Duration.toMillis(keptWorkspaces)) {
|
|
@@ -7324,9 +7346,15 @@ const layer$5 = (cacheDir) => Layer.effect(Audits, Effect.gen(function* () {
|
|
|
7324
7346
|
return;
|
|
7325
7347
|
}
|
|
7326
7348
|
const entries = yield* Effect.promise(async () => await readdir(workspace, { withFileTypes: true }).catch(() => []));
|
|
7327
|
-
|
|
7349
|
+
yield* Effect.forEach(entries.filter((entry) => entry.isDirectory()), (entry) => discard(path.join(workspace, entry.name), name), {
|
|
7350
|
+
concurrency: "unbounded",
|
|
7351
|
+
discard: true
|
|
7352
|
+
});
|
|
7328
7353
|
})).pipe(Effect.ignore);
|
|
7329
|
-
}
|
|
7354
|
+
}, {
|
|
7355
|
+
concurrency: 8,
|
|
7356
|
+
discard: true
|
|
7357
|
+
});
|
|
7330
7358
|
yield* Effect.forkScoped(Effect.promise(async () => {
|
|
7331
7359
|
for (const name of await readdir(trash).catch(() => [])) await rm(path.join(trash, name), {
|
|
7332
7360
|
recursive: true,
|
|
@@ -7543,12 +7571,15 @@ const workflowFiles = Effect.fn("workflowFiles")(function* (root) {
|
|
|
7543
7571
|
for (const name of ["action.yml", "action.yaml"]) if (yield* present(path.join(dir, name))) return Option.some(path.join(dir, name));
|
|
7544
7572
|
return Option.none();
|
|
7545
7573
|
});
|
|
7546
|
-
const workflows = yield*
|
|
7547
|
-
|
|
7574
|
+
const [workflows, actions, own] = yield* Effect.all([
|
|
7575
|
+
listed(path.join(".github", "workflows"), false),
|
|
7576
|
+
listed(path.join(".github", "actions"), true),
|
|
7577
|
+
actionFile("")
|
|
7578
|
+
], { concurrency: "unbounded" });
|
|
7548
7579
|
const pending = [
|
|
7549
7580
|
...workflows.filter((file) => /\.ya?ml$/u.test(file)),
|
|
7550
7581
|
...actions.filter((file) => /^action\.ya?ml$/u.test(path.basename(file))),
|
|
7551
|
-
...Option.toArray(
|
|
7582
|
+
...Option.toArray(own)
|
|
7552
7583
|
];
|
|
7553
7584
|
const seen = /* @__PURE__ */ new Set();
|
|
7554
7585
|
const files = [];
|
|
@@ -7748,7 +7779,7 @@ const toPackage$1 = ({ uses, resolved, source }) => {
|
|
|
7748
7779
|
};
|
|
7749
7780
|
};
|
|
7750
7781
|
const publicTags = (url) => tags(url, { timeout: lsRemoteTimeout }).pipe(Effect.mapError((error) => error.issue), Effect.result);
|
|
7751
|
-
const make$
|
|
7782
|
+
const make$13 = (root, origin, readPublic = publicTags) => Effect.gen(function* () {
|
|
7752
7783
|
const exec = yield* Exec;
|
|
7753
7784
|
const fs = yield* FileSystem.FileSystem;
|
|
7754
7785
|
const read = (file) => readText(file).pipe(Effect.provideService(FileSystem.FileSystem, fs));
|
|
@@ -7873,7 +7904,7 @@ const discover$4 = Effect.fn("GitHubActions.discover")(function* (dir) {
|
|
|
7873
7904
|
".github",
|
|
7874
7905
|
"action.yml",
|
|
7875
7906
|
"action.yaml"
|
|
7876
|
-
], exists)).length === 0) return [];
|
|
7907
|
+
], exists, { concurrency: "unbounded" })).length === 0) return [];
|
|
7877
7908
|
const origin = yield* exec.stdout({
|
|
7878
7909
|
command: "git",
|
|
7879
7910
|
args: [
|
|
@@ -7884,11 +7915,11 @@ const discover$4 = Effect.fn("GitHubActions.discover")(function* (dir) {
|
|
|
7884
7915
|
cwd: root
|
|
7885
7916
|
}).pipe(Effect.option);
|
|
7886
7917
|
const host = Option.isSome(origin) ? hostOf(origin.value.trim()) : void 0;
|
|
7887
|
-
return [yield* make$
|
|
7918
|
+
return [yield* make$13(root, host === "github.com" ? void 0 : host)];
|
|
7888
7919
|
});
|
|
7889
7920
|
// src/process/on-path.ts
|
|
7890
7921
|
const executable = (file) => Effect.promise(async () => await access(file, constants.X_OK).then(() => true, () => false));
|
|
7891
|
-
const onPath = (command) => Effect.forEach((process.env["PATH"] ?? "").split(path.delimiter).filter((dir) => dir !== ""), (dir) => executable(path.join(dir, command))).pipe(Effect.map((found) => found.includes(true)));
|
|
7922
|
+
const onPath = (command) => Effect.forEach((process.env["PATH"] ?? "").split(path.delimiter).filter((dir) => dir !== ""), (dir) => executable(path.join(dir, command)), { concurrency: "unbounded" }).pipe(Effect.map((found) => found.includes(true)));
|
|
7892
7923
|
// src/process/Sudo.ts
|
|
7893
7924
|
var Sudo = class extends Context.Service()("upup/Sudo") {};
|
|
7894
7925
|
const asRoot = (invocation) => [
|
|
@@ -7898,7 +7929,7 @@ const asRoot = (invocation) => [
|
|
|
7898
7929
|
];
|
|
7899
7930
|
const enablesTouchId = (pam) => pam.split("\n").some((line) => /^\s*auth\s+\S+\s+pam_tid\.so\b/u.test(line));
|
|
7900
7931
|
const pamFiles = ["/etc/pam.d/sudo_local", "/etc/pam.d/sudo"];
|
|
7901
|
-
const make$
|
|
7932
|
+
const make$12 = (interactive) => Effect.gen(function* () {
|
|
7902
7933
|
const exec = yield* Exec;
|
|
7903
7934
|
const fs = yield* FileSystem.FileSystem;
|
|
7904
7935
|
const asking = yield* SubscriptionRef.make(void 0);
|
|
@@ -7957,8 +7988,8 @@ const make$11 = (interactive) => Effect.gen(function* () {
|
|
|
7957
7988
|
answer
|
|
7958
7989
|
});
|
|
7959
7990
|
});
|
|
7960
|
-
const layer$4 = Layer.effect(Sudo, make$
|
|
7961
|
-
const layerNoPrompt = Layer.effect(Sudo, make$
|
|
7991
|
+
const layer$4 = Layer.effect(Sudo, make$12(true));
|
|
7992
|
+
const layerNoPrompt = Layer.effect(Sudo, make$12(false));
|
|
7962
7993
|
// src/apps/app-store.ts
|
|
7963
7994
|
const countryOf = (locale) => (/[@;]rg=(?<region>[a-z]{2})/iu.exec(locale)?.groups?.["region"] ?? /^[a-z]{2,3}[_-](?<region>[a-z]{2})\b/iu.exec(locale)?.groups?.["region"] ?? "us").toLowerCase();
|
|
7964
7995
|
const Lookup = Schema.Struct({ results: Schema.Array(Schema.Struct({
|
|
@@ -8482,8 +8513,7 @@ const read = Effect.fn("bundles.read")(function* (bundle, arch) {
|
|
|
8482
8513
|
const short = nonEmpty(info.value.CFBundleShortVersionString);
|
|
8483
8514
|
const build = nonEmpty(info.value.CFBundleVersion);
|
|
8484
8515
|
const executable = nonEmpty(info.value.CFBundleExecutable);
|
|
8485
|
-
const sparkle = yield* sparkleKeysOf(bundle, info.value);
|
|
8486
|
-
const source = yield* sourceOf(bundle, bundleId, nonEmpty(info.value.SUFeedURL), arch);
|
|
8516
|
+
const [sparkle, source] = yield* Effect.all([sparkleKeysOf(bundle, info.value), sourceOf(bundle, bundleId, nonEmpty(info.value.SUFeedURL), arch)], { concurrency: "unbounded" });
|
|
8487
8517
|
return Option.some({
|
|
8488
8518
|
path: bundle,
|
|
8489
8519
|
name: path.basename(bundle, ".app"),
|
|
@@ -8496,23 +8526,25 @@ const read = Effect.fn("bundles.read")(function* (bundle, arch) {
|
|
|
8496
8526
|
...plist === void 0 ? {} : { plist }
|
|
8497
8527
|
});
|
|
8498
8528
|
});
|
|
8499
|
-
const
|
|
8529
|
+
const isApp = (entry) => entry.endsWith(".app");
|
|
8530
|
+
const isLeftover = (entry) => entry.endsWith(".app.upup-old");
|
|
8531
|
+
const wanted$1 = (entry) => isApp(entry) || isLeftover(entry);
|
|
8532
|
+
const walk = Effect.fn("bundles.walk")(function* (roots) {
|
|
8500
8533
|
const fs = yield* FileSystem.FileSystem;
|
|
8501
8534
|
const entries = (dir) => fs.readDirectory(dir).pipe(Effect.orElseSucceed(() => []), Effect.map((names) => names.toSorted((left, right) => left.localeCompare(right)).map((name) => path.join(dir, name))));
|
|
8502
8535
|
const isLink = (entry) => fs.readLink(entry).pipe(Effect.isSuccess);
|
|
8503
8536
|
const isDirectory = (entry) => fs.stat(entry).pipe(Effect.map((info) => info.type === "Directory"), Effect.orElseSucceed(() => false));
|
|
8504
|
-
const
|
|
8505
|
-
|
|
8506
|
-
if (yield* isLink(entry))
|
|
8507
|
-
if (wanted(entry))
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8537
|
+
const nestedIn = (folder) => entries(folder).pipe(Effect.flatMap((listed) => Effect.filter(listed.filter(wanted$1), (nested) => Effect.map(isLink(nested), (link) => !link), { concurrency: "unbounded" })));
|
|
8538
|
+
const walked = (yield* Effect.forEach(roots, (root) => entries(root).pipe(Effect.flatMap((listed) => Effect.forEach(listed, (entry) => Effect.gen(function* () {
|
|
8539
|
+
if (yield* isLink(entry)) return [];
|
|
8540
|
+
if (wanted$1(entry)) return [entry];
|
|
8541
|
+
return !/\.(?:app|localized|upup-old|upup-new)$/u.test(entry) && (yield* isDirectory(entry)) ? yield* nestedIn(entry) : [];
|
|
8542
|
+
}), { concurrency: "unbounded" })), Effect.map((listed) => listed.flat())), { concurrency: "unbounded" })).flat();
|
|
8543
|
+
return {
|
|
8544
|
+
apps: walked.filter(isApp),
|
|
8545
|
+
leftovers: walked.filter(isLeftover)
|
|
8546
|
+
};
|
|
8513
8547
|
});
|
|
8514
|
-
const paths = (roots) => walk(roots, (entry) => entry.endsWith(".app"));
|
|
8515
|
-
const leftovers = (roots) => walk(roots, (entry) => entry.endsWith(".app.upup-old"));
|
|
8516
8548
|
// src/apps/running.ts
|
|
8517
8549
|
const command$4 = (name, args) => ({
|
|
8518
8550
|
command: name,
|
|
@@ -8575,7 +8607,7 @@ const command$3 = (name, args) => ({
|
|
|
8575
8607
|
});
|
|
8576
8608
|
const host = Effect.gen(function* () {
|
|
8577
8609
|
const exec = yield* Exec;
|
|
8578
|
-
const [arm64, os] = yield* Effect.all([exec.run(command$3("sysctl", ["-n", "hw.optional.arm64"])).pipe(Effect.map((output) => output.exitCode === 0 && output.stdout.trim() === "1"), Effect.orElseSucceed(() => false)), exec.stdout(command$3("sw_vers", ["-productVersion"])).pipe(Effect.map((text) => text.trim()))], { concurrency:
|
|
8610
|
+
const [arm64, os] = yield* Effect.all([exec.run(command$3("sysctl", ["-n", "hw.optional.arm64"])).pipe(Effect.map((output) => output.exitCode === 0 && output.stdout.trim() === "1"), Effect.orElseSucceed(() => false)), exec.stdout(command$3("sw_vers", ["-productVersion"])).pipe(Effect.map((text) => text.trim()))], { concurrency: "unbounded" });
|
|
8579
8611
|
return {
|
|
8580
8612
|
arch: arm64 ? "arm64" : "x86_64",
|
|
8581
8613
|
os
|
|
@@ -8653,7 +8685,7 @@ const stage = Effect.fn("install.stage")(function* (client, wanted) {
|
|
|
8653
8685
|
const found = (yield* Effect.forEach(candidates, (candidate) => readInfo(candidate).pipe(Effect.map((info) => ({
|
|
8654
8686
|
path: candidate,
|
|
8655
8687
|
info
|
|
8656
|
-
}))))).find(({ info }) => Option.isSome(info) && info.value.CFBundleIdentifier === wanted.bundleId);
|
|
8688
|
+
}))), { concurrency: 4 })).find(({ info }) => Option.isSome(info) && info.value.CFBundleIdentifier === wanted.bundleId);
|
|
8657
8689
|
if (found === void 0 || Option.isNone(found.info)) return yield* refused(`its download holds no app with the bundle ID ${wanted.bundleId}`);
|
|
8658
8690
|
const app = found.path;
|
|
8659
8691
|
const info = found.info.value;
|
|
@@ -8672,7 +8704,7 @@ const stage = Effect.fn("install.stage")(function* (client, wanted) {
|
|
|
8672
8704
|
])).pipe(Effect.mapError((error) => refused(`codesign failed: ${error.output.trim()}`)), Effect.flatMap((output) => {
|
|
8673
8705
|
const hash = /^CDHash=(?<hash>[0-9a-f]+)$/mu.exec(output.stderr)?.groups?.["hash"];
|
|
8674
8706
|
return hash === void 0 ? Effect.fail(unsigned(output.stderr.trim())) : Effect.succeed(hash);
|
|
8675
|
-
}))).pipe(Effect.map((hashes) => `${requirement(wanted.bundleId, wanted.team)} and info [CFBundleShortVersionString] = "${wanted.version.replaceAll(/["\\]/gu, "\\$&")}" and (${hashes.map((hash) => `cdhash H"${hash}"`).join(" or ")})`));
|
|
8707
|
+
})), { concurrency: 2 }).pipe(Effect.map((hashes) => `${requirement(wanted.bundleId, wanted.team)} and info [CFBundleShortVersionString] = "${wanted.version.replaceAll(/["\\]/gu, "\\$&")}" and (${hashes.map((hash) => `cdhash H"${hash}"`).join(" or ")})`));
|
|
8676
8708
|
const signed = yield* exec.run(command$2("codesign", [
|
|
8677
8709
|
"--verify",
|
|
8678
8710
|
"--deep",
|
|
@@ -8750,7 +8782,7 @@ const locate$3 = Effect.gen(function* () {
|
|
|
8750
8782
|
return Option.isNone(manifest) || !(yield* fs.exists(zip).pipe(Effect.orElseSucceed(() => false))) ? Option.none() : yield* decode(manifest.value, zip);
|
|
8751
8783
|
});
|
|
8752
8784
|
// src/apps/swap.ts
|
|
8753
|
-
const tools = {
|
|
8785
|
+
const tools$2 = {
|
|
8754
8786
|
ps: "/bin/ps",
|
|
8755
8787
|
ditto: "/usr/bin/ditto",
|
|
8756
8788
|
mv: "/bin/mv",
|
|
@@ -9155,16 +9187,27 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9155
9187
|
}
|
|
9156
9188
|
};
|
|
9157
9189
|
});
|
|
9190
|
+
const leftoverNotice = (old) => {
|
|
9191
|
+
const app = old.slice(0, -9);
|
|
9192
|
+
return fs.exists(app).pipe(Effect.orElseSucceed(() => true), Effect.map((present) => ({ message: present ? `${old} is the old copy of ${path.basename(app)} that an update left behind; move it to the Trash` : `${old} is ${path.basename(app)} from before an update that did not finish; rename it back to ${path.basename(app)}` })));
|
|
9193
|
+
};
|
|
9194
|
+
const scan = Effect.gen(function* () {
|
|
9195
|
+
const [here, walked] = yield* Effect.all([host, walk(options.roots)], { concurrency: "unbounded" });
|
|
9196
|
+
const [bundles, left] = yield* Effect.all([Effect.forEach(walked.apps, (app) => read(app, here.arch), { concurrency: inFlight }), Effect.forEach(walked.leftovers, leftoverNotice, { concurrency: "unbounded" })], { concurrency: "unbounded" });
|
|
9197
|
+
return {
|
|
9198
|
+
here,
|
|
9199
|
+
found: bundles.flatMap((bundle) => Option.toArray(bundle)),
|
|
9200
|
+
left
|
|
9201
|
+
};
|
|
9202
|
+
});
|
|
9158
9203
|
const list = Effect.gen(function* () {
|
|
9159
|
-
const owners = yield* Effect.result(casks);
|
|
9204
|
+
const [owners, { here, found, left }] = yield* Effect.all([Effect.result(casks), scan], { concurrency: "unbounded" });
|
|
9160
9205
|
if (Result.isFailure(owners)) return {
|
|
9161
9206
|
packages: [],
|
|
9162
9207
|
notices: [{ message: `upup lists no apps while it cannot read which apps Homebrew installed: ${describeError(owners.failure)}` }]
|
|
9163
9208
|
};
|
|
9164
|
-
const here = yield* host;
|
|
9165
9209
|
const locale = yield* Effect.cached(country);
|
|
9166
9210
|
const caskOf = new Map(owners.success.flatMap((cask) => caskApps(cask).map((app) => [app, cask])));
|
|
9167
|
-
const found = (yield* Effect.forEach(yield* paths(options.roots), (app) => read(app, here.arch), { concurrency: inFlight })).flatMap((bundle) => Option.toArray(bundle));
|
|
9168
9211
|
const ids = /* @__PURE__ */ new Set();
|
|
9169
9212
|
const kept = found.filter((bundle) => {
|
|
9170
9213
|
const cask = caskOf.get(path.basename(bundle.path));
|
|
@@ -9192,13 +9235,9 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9192
9235
|
here,
|
|
9193
9236
|
rows: new Map(all.map((row) => [row.pkg.id, row.listed]))
|
|
9194
9237
|
});
|
|
9195
|
-
const leftovers$1 = yield* Effect.forEach(yield* leftovers(options.roots), (old) => {
|
|
9196
|
-
const app = old.slice(0, -9);
|
|
9197
|
-
return fs.exists(app).pipe(Effect.orElseSucceed(() => true), Effect.map((present) => ({ message: present ? `${old} is the old copy of ${path.basename(app)} that an update left behind; move it to the Trash` : `${old} is ${path.basename(app)} from before an update that did not finish; rename it back to ${path.basename(app)}` })));
|
|
9198
|
-
});
|
|
9199
9238
|
return {
|
|
9200
9239
|
packages: all.map((row) => row.pkg),
|
|
9201
|
-
notices: [...all.flatMap((row) => row.notice), ...
|
|
9240
|
+
notices: [...all.flatMap((row) => row.notice), ...left]
|
|
9202
9241
|
};
|
|
9203
9242
|
}).pipe(Effect.withSpan("Apps.list"), provide);
|
|
9204
9243
|
const observe = Effect.fn("Apps.observe")(function* (pkg) {
|
|
@@ -9233,7 +9272,7 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9233
9272
|
const ids = yield* Effect.forEach(paths, (app) => readInfo(app).pipe(Effect.flatMap((info) => Option.match(info, {
|
|
9234
9273
|
onNone: () => Effect.fail(new Unsupported({ reason: `upup could not read ${app}` })),
|
|
9235
9274
|
onSome: (found) => Effect.succeed(found.CFBundleIdentifier)
|
|
9236
|
-
}))));
|
|
9275
|
+
}))), { concurrency: inFlight });
|
|
9237
9276
|
const found = {
|
|
9238
9277
|
paths,
|
|
9239
9278
|
ids: new Set(ids)
|
|
@@ -9242,7 +9281,7 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9242
9281
|
return found;
|
|
9243
9282
|
});
|
|
9244
9283
|
const queue = yield* Ref.make([]);
|
|
9245
|
-
const using = options.tools ?? tools;
|
|
9284
|
+
const using = options.tools ?? tools$2;
|
|
9246
9285
|
const helper = options.sparkle === void 0 ? locate$3.pipe(Effect.provideService(FileSystem.FileSystem, fs)) : Effect.succeed(options.sparkle);
|
|
9247
9286
|
const sparkleOf = (pkg, install, build) => {
|
|
9248
9287
|
const refused = (reason) => new InstallRefused({
|
|
@@ -9442,7 +9481,7 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9442
9481
|
})
|
|
9443
9482
|
};
|
|
9444
9483
|
});
|
|
9445
|
-
const make$
|
|
9484
|
+
const make$11 = onPath("brew").pipe(Effect.flatMap((brewInstalled) => makeWith({
|
|
9446
9485
|
roots: ["/Applications", path.join(homedir(), "Applications")],
|
|
9447
9486
|
brew: brewInstalled
|
|
9448
9487
|
})));
|
|
@@ -9468,7 +9507,7 @@ const parseList = (text) => text.split("\n").flatMap((line) => {
|
|
|
9468
9507
|
fromCratesIo: source === void 0
|
|
9469
9508
|
}];
|
|
9470
9509
|
});
|
|
9471
|
-
const make$
|
|
9510
|
+
const make$10 = Effect.gen(function* () {
|
|
9472
9511
|
const exec = yield* Exec;
|
|
9473
9512
|
const client = yield* HttpClient.HttpClient;
|
|
9474
9513
|
const installed = exec.stdout(cargo$1(["install", "--list"])).pipe(Effect.map(parseList));
|
|
@@ -9514,7 +9553,7 @@ const nestedDirs = Effect.fn("nestedDirs")(function* (dir, names) {
|
|
|
9514
9553
|
args,
|
|
9515
9554
|
cwd: dir
|
|
9516
9555
|
}).pipe(Effect.orElseSucceed(() => ""));
|
|
9517
|
-
const [prefix, listed] = yield* Effect.all([git("rev-parse", "--show-prefix"), git("ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...names.map((name) => `:(glob)**/${name}`))], { concurrency:
|
|
9556
|
+
const [prefix, listed] = yield* Effect.all([git("rev-parse", "--show-prefix"), git("ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...names.map((name) => `:(glob)**/${name}`))], { concurrency: "unbounded" });
|
|
9518
9557
|
const files = listed.split("\0").filter((file) => {
|
|
9519
9558
|
const folders = file.split("/").slice(0, -1);
|
|
9520
9559
|
return folders.length > 0 && !folders.includes("node_modules") && !(path.basename(file) === "go.mod" && ignoredByGo(folders));
|
|
@@ -9541,21 +9580,32 @@ const failing = (name, error) => ({
|
|
|
9541
9580
|
observe: () => Effect.succeedNone
|
|
9542
9581
|
});
|
|
9543
9582
|
const locateAll = Effect.fn("locateAll")(function* (dir, nested, manifest, locate) {
|
|
9544
|
-
const own = yield* locate(dir);
|
|
9545
|
-
const below = yield* Effect.forEach(nested, (from) => Effect.map(Effect.result(locate(from)), (result) => [from, result]), { concurrency: 8 });
|
|
9583
|
+
const [own, below] = yield* Effect.all([locate(dir), Effect.forEach(nested, (from) => Effect.map(Effect.result(locate(from)), (result) => [from, result]), { concurrency: 8 })], { concurrency: "unbounded" });
|
|
9546
9584
|
const found = /* @__PURE__ */ new Map();
|
|
9585
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
9547
9586
|
const failed = [];
|
|
9548
|
-
const add = (located) => {
|
|
9549
|
-
if (Option.
|
|
9587
|
+
const add = (from, located) => {
|
|
9588
|
+
if (Option.isNone(located)) return;
|
|
9589
|
+
const { root } = located.value;
|
|
9590
|
+
if (!found.has(root)) found.set(root, located.value);
|
|
9591
|
+
claimed.set(root, [...claimed.get(root) ?? [], from]);
|
|
9550
9592
|
};
|
|
9551
|
-
add(own);
|
|
9593
|
+
add(dir, own);
|
|
9552
9594
|
const ownRoot = Option.map(own, (located) => located.root);
|
|
9553
9595
|
const outside = (located) => Option.isSome(located) && placeOf$1(dir, located.value.root) === "" && !Option.contains(ownRoot, located.value.root);
|
|
9596
|
+
const failures = below.flatMap(([from, result]) => Result.isFailure(result) ? [{
|
|
9597
|
+
from,
|
|
9598
|
+
reason: describeError(result.failure)
|
|
9599
|
+
}] : []);
|
|
9600
|
+
const repeated = (from, reason) => failures.some((other) => other.reason === reason && from.startsWith(`${other.from}${path.sep}`));
|
|
9554
9601
|
for (const [from, result] of below) if (Result.isSuccess(result)) {
|
|
9555
|
-
if (!outside(result.success)) add(result.success);
|
|
9556
|
-
} else failed.push(failing(path.join(path.relative(dir, from), manifest), result.failure));
|
|
9602
|
+
if (!outside(result.success)) add(from, result.success);
|
|
9603
|
+
} else if (!repeated(from, describeError(result.failure))) failed.push(failing(path.join(path.relative(dir, from), manifest), result.failure));
|
|
9557
9604
|
return {
|
|
9558
|
-
found: [...found.values()]
|
|
9605
|
+
found: [...found.values()].map((located) => ({
|
|
9606
|
+
...located,
|
|
9607
|
+
claimed: claimed.get(located.root) ?? []
|
|
9608
|
+
})),
|
|
9559
9609
|
failed
|
|
9560
9610
|
};
|
|
9561
9611
|
});
|
|
@@ -9642,10 +9692,11 @@ const entriesOf = (manifest, parsed) => {
|
|
|
9642
9692
|
for (const [name, spec] of Object.entries(parsed.workspace?.dependencies ?? {})) add(["workspace", "dependencies"], name, spec);
|
|
9643
9693
|
return entries;
|
|
9644
9694
|
};
|
|
9645
|
-
const cargo = (
|
|
9695
|
+
const cargo = (cwd, args) => ({
|
|
9646
9696
|
command: "cargo",
|
|
9647
9697
|
args,
|
|
9648
|
-
cwd
|
|
9698
|
+
cwd,
|
|
9699
|
+
env: { RUSTUP_AUTO_INSTALL: "0" }
|
|
9649
9700
|
});
|
|
9650
9701
|
const parseManifest = (file, text) => Effect.try({
|
|
9651
9702
|
try: () => getStaticTOMLValue(parseTOML(text)),
|
|
@@ -9670,7 +9721,7 @@ const resolvedByMember = (root, meta) => {
|
|
|
9670
9721
|
return [path.relative(root, pkg.manifest_path), deps];
|
|
9671
9722
|
}));
|
|
9672
9723
|
};
|
|
9673
|
-
const make$
|
|
9724
|
+
const make$9 = (root, place = "") => Effect.gen(function* () {
|
|
9674
9725
|
const providerName = placed("cargo", place);
|
|
9675
9726
|
const exec = yield* Exec;
|
|
9676
9727
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -9683,6 +9734,7 @@ const make$8 = (root, place = "") => Effect.gen(function* () {
|
|
|
9683
9734
|
"metadata",
|
|
9684
9735
|
"--format-version",
|
|
9685
9736
|
"1",
|
|
9737
|
+
"--all-features",
|
|
9686
9738
|
"--locked"
|
|
9687
9739
|
]);
|
|
9688
9740
|
return yield* exec.stdout(invocation).pipe(Effect.flatMap(decodeJson(Metadata, commandLine(invocation))));
|
|
@@ -9691,7 +9743,7 @@ const make$8 = (root, place = "") => Effect.gen(function* () {
|
|
|
9691
9743
|
const entries = (manifest) => manifestOf(manifest).pipe(Effect.map((parsed) => entriesOf(manifest, parsed)));
|
|
9692
9744
|
const workspace = Effect.gen(function* () {
|
|
9693
9745
|
const resolved = resolvedByMember(root, yield* metadata);
|
|
9694
|
-
const manifests = yield* Effect.forEach(/* @__PURE__ */ new Set(["Cargo.toml", ...resolved.keys()]), (manifest) => manifestOf(manifest).pipe(Effect.map((parsed) => [manifest, parsed])));
|
|
9746
|
+
const manifests = yield* Effect.forEach(/* @__PURE__ */ new Set(["Cargo.toml", ...resolved.keys()]), (manifest) => manifestOf(manifest).pipe(Effect.map((parsed) => [manifest, parsed])), { concurrency: 8 });
|
|
9695
9747
|
const inheritors = (key) => manifests.flatMap(([manifest, parsed]) => resolved.has(manifest) && inheritedKeys(parsed).includes(key) ? [manifest] : []);
|
|
9696
9748
|
return {
|
|
9697
9749
|
resolved,
|
|
@@ -9731,6 +9783,11 @@ const make$8 = (root, place = "") => Effect.gen(function* () {
|
|
|
9731
9783
|
};
|
|
9732
9784
|
});
|
|
9733
9785
|
const list = Effect.gen(function* () {
|
|
9786
|
+
const lockfile = path.join(root, "Cargo.lock");
|
|
9787
|
+
if (!(yield* fs.exists(lockfile).pipe(Effect.mapError(fileFailed("stat", lockfile))))) return {
|
|
9788
|
+
packages: [],
|
|
9789
|
+
notices: [{ message: "not listed: no Cargo.lock yet" }]
|
|
9790
|
+
};
|
|
9734
9791
|
const { declared, notices } = yield* declarations;
|
|
9735
9792
|
return {
|
|
9736
9793
|
packages: yield* Effect.forEach(declared, ({ pkg }) => newestRelease(client, pkg.artifact, pkg.current).pipe(Effect.map((target) => {
|
|
@@ -9812,21 +9869,17 @@ const locate$2 = Effect.fn("CargoWorkspace.locate")(function* (dir) {
|
|
|
9812
9869
|
const exec = yield* Exec;
|
|
9813
9870
|
const manifest = yield* findUp(dir, "Cargo.toml");
|
|
9814
9871
|
if (Option.isNone(manifest)) return Option.none();
|
|
9815
|
-
const root = yield* exec.stdout(
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
"plain"
|
|
9822
|
-
],
|
|
9823
|
-
cwd: manifest.value
|
|
9824
|
-
});
|
|
9872
|
+
const root = yield* exec.stdout(cargo(manifest.value, [
|
|
9873
|
+
"locate-project",
|
|
9874
|
+
"--workspace",
|
|
9875
|
+
"--message-format",
|
|
9876
|
+
"plain"
|
|
9877
|
+
]));
|
|
9825
9878
|
return Option.some({ root: path.dirname(root.trim()) });
|
|
9826
9879
|
});
|
|
9827
9880
|
const discover$3 = Effect.fn("CargoWorkspace.discover")(function* (dir, nested = []) {
|
|
9828
9881
|
const { found, failed } = yield* locateAll(dir, nested, "Cargo.toml", locate$2);
|
|
9829
|
-
return [...failed, ...yield* Effect.forEach(found, ({ root }) => make$
|
|
9882
|
+
return [...failed, ...yield* Effect.forEach(found, ({ root }) => make$9(root, placeOf$1(dir, root)))];
|
|
9830
9883
|
});
|
|
9831
9884
|
// src/registry/origin.ts
|
|
9832
9885
|
const trailing = (url) => url.endsWith("/") ? url : `${url}/`;
|
|
@@ -9971,14 +10024,15 @@ const fromBuildInfo = (info, file) => {
|
|
|
9971
10024
|
current: main.Version
|
|
9972
10025
|
}) : Option.none();
|
|
9973
10026
|
};
|
|
9974
|
-
const make$
|
|
10027
|
+
const make$8 = Effect.gen(function* () {
|
|
9975
10028
|
const exec = yield* Exec;
|
|
9976
10029
|
const fs = yield* FileSystem.FileSystem;
|
|
9977
|
-
const
|
|
10030
|
+
const envInvocation = go([
|
|
9978
10031
|
"env",
|
|
9979
10032
|
"-json",
|
|
9980
10033
|
...envKeys
|
|
9981
|
-
])
|
|
10034
|
+
]);
|
|
10035
|
+
const goEnv = yield* Effect.cached(exec.stdout(envInvocation).pipe(Effect.flatMap(decodeGoJson(GoEnv, commandLine(envInvocation)))));
|
|
9982
10036
|
const binDir = goEnv.pipe(Effect.map((env) => env.GOBIN || path.join(env.GOPATH.split(":")[0] ?? env.GOPATH, "bin")));
|
|
9983
10037
|
const inspect = Effect.fn("GoInstall.inspect")(function* (dir, file) {
|
|
9984
10038
|
const invocation = go([
|
|
@@ -9994,9 +10048,11 @@ const make$7 = Effect.gen(function* () {
|
|
|
9994
10048
|
});
|
|
9995
10049
|
const isPlainExecutable = Effect.fn("GoInstall.isPlainExecutable")(function* (dir, file) {
|
|
9996
10050
|
const binary = path.join(dir, file);
|
|
9997
|
-
const info = yield*
|
|
9998
|
-
|
|
9999
|
-
|
|
10051
|
+
const [info, real, realDir] = yield* Effect.all([
|
|
10052
|
+
fs.stat(binary).pipe(Effect.mapError(fileFailed("stat", binary))),
|
|
10053
|
+
fs.realPath(binary).pipe(Effect.mapError(fileFailed("resolve", binary))),
|
|
10054
|
+
fs.realPath(dir).pipe(Effect.mapError(fileFailed("resolve", dir)))
|
|
10055
|
+
], { concurrency: "unbounded" });
|
|
10000
10056
|
return info.type === "File" && (info.mode & 73) !== 0 && real === path.join(realDir, file);
|
|
10001
10057
|
});
|
|
10002
10058
|
const latest = Effect.fn("GoInstall.latest")(function* (modules) {
|
|
@@ -10066,6 +10122,7 @@ const make$7 = Effect.gen(function* () {
|
|
|
10066
10122
|
});
|
|
10067
10123
|
// src/go/GoModule.ts
|
|
10068
10124
|
const ModFile = Schema.Struct({
|
|
10125
|
+
Module: Schema.optionalKey(Schema.Struct({ Path: Schema.String })),
|
|
10069
10126
|
Require: Schema.optionalKey(Schema.Array(Schema.Struct({
|
|
10070
10127
|
Path: Schema.String,
|
|
10071
10128
|
Version: Schema.String,
|
|
@@ -10079,7 +10136,10 @@ const Selected = Schema.Struct({
|
|
|
10079
10136
|
Update: Schema.optionalKey(Schema.Struct({ Version: Schema.String })),
|
|
10080
10137
|
Error: Schema.optionalKey(Schema.Struct({ Err: Schema.String }))
|
|
10081
10138
|
});
|
|
10082
|
-
const WorkFile = Schema.Struct({
|
|
10139
|
+
const WorkFile = Schema.Struct({
|
|
10140
|
+
Use: Schema.optionalKey(Schema.Array(Schema.Struct({ DiskPath: Schema.String }))),
|
|
10141
|
+
Replace: Schema.optionalKey(Schema.Array(Schema.Struct({ Old: Schema.Struct({ Path: Schema.String }) })))
|
|
10142
|
+
});
|
|
10083
10143
|
const goEnv = {
|
|
10084
10144
|
GOWORK: "off",
|
|
10085
10145
|
GIT_TERMINAL_PROMPT: "0"
|
|
@@ -10089,26 +10149,30 @@ const GoProxyEnv = Schema.Struct({
|
|
|
10089
10149
|
GOPRIVATE: Schema.optionalKey(Schema.String),
|
|
10090
10150
|
GONOPROXY: Schema.optionalKey(Schema.String)
|
|
10091
10151
|
});
|
|
10092
|
-
const directRequirements = (mod) => {
|
|
10152
|
+
const directRequirements = (mod, local) => {
|
|
10093
10153
|
const replaced = new Set((mod.Replace ?? []).map((replace) => replace.Old.Path));
|
|
10094
|
-
return (mod.Require ?? []).filter((requirement) => requirement.Indirect !== true && !replaced.has(requirement.Path));
|
|
10154
|
+
return (mod.Require ?? []).filter((requirement) => requirement.Indirect !== true && !replaced.has(requirement.Path) && !local.has(requirement.Path));
|
|
10155
|
+
};
|
|
10156
|
+
const goIn = (cwd, args) => ({
|
|
10157
|
+
command: "go",
|
|
10158
|
+
args,
|
|
10159
|
+
cwd,
|
|
10160
|
+
env: goEnv
|
|
10161
|
+
});
|
|
10162
|
+
const readModFile = (exec, cwd) => {
|
|
10163
|
+
const edit = goIn(cwd, [
|
|
10164
|
+
"mod",
|
|
10165
|
+
"edit",
|
|
10166
|
+
"-json"
|
|
10167
|
+
]);
|
|
10168
|
+
return exec.stdout(edit).pipe(Effect.flatMap(decodeGoJson(ModFile, commandLine(edit))));
|
|
10095
10169
|
};
|
|
10096
|
-
const make$
|
|
10170
|
+
const make$7 = (root, moduleDir, place = "", local = /* @__PURE__ */ new Set()) => Effect.gen(function* () {
|
|
10097
10171
|
const exec = yield* Exec;
|
|
10098
10172
|
const manifest = path.join(place, path.relative(root, path.join(moduleDir, "go.mod")));
|
|
10099
|
-
const go = (args) => (
|
|
10100
|
-
command: "go",
|
|
10101
|
-
args,
|
|
10102
|
-
cwd: moduleDir,
|
|
10103
|
-
env: goEnv
|
|
10104
|
-
});
|
|
10173
|
+
const go = (args) => goIn(moduleDir, args);
|
|
10105
10174
|
const requirements = Effect.gen(function* () {
|
|
10106
|
-
|
|
10107
|
-
"mod",
|
|
10108
|
-
"edit",
|
|
10109
|
-
"-json"
|
|
10110
|
-
]);
|
|
10111
|
-
return directRequirements(yield* exec.stdout(edit).pipe(Effect.flatMap(decodeGoJson(ModFile, commandLine(edit)))));
|
|
10175
|
+
return directRequirements(yield* readModFile(exec, moduleDir), local);
|
|
10112
10176
|
});
|
|
10113
10177
|
const selected = Effect.fn("GoModule.selected")(function* (paths, withUpdates) {
|
|
10114
10178
|
if (paths.length === 0) return /* @__PURE__ */ new Map();
|
|
@@ -10147,8 +10211,7 @@ const make$6 = (root, moduleDir, place = "") => Effect.gen(function* () {
|
|
|
10147
10211
|
]);
|
|
10148
10212
|
const origin = exec.stdout(env).pipe(Effect.flatMap(decodeGoJson(GoProxyEnv, commandLine(env))), Effect.map(withGoOrigin));
|
|
10149
10213
|
const list = Effect.gen(function* () {
|
|
10150
|
-
const direct = yield* requirements;
|
|
10151
|
-
const located = yield* origin;
|
|
10214
|
+
const [direct, located] = yield* Effect.all([requirements, origin], { concurrency: "unbounded" });
|
|
10152
10215
|
const entries = yield* selected(direct.map((requirement) => requirement.Path), true);
|
|
10153
10216
|
const notices = [];
|
|
10154
10217
|
return {
|
|
@@ -10210,65 +10273,91 @@ const locate$1 = Effect.fn("GoModule.locate")(function* (dir) {
|
|
|
10210
10273
|
const discover$2 = Effect.fn("GoModule.discover")(function* (dir, nested = []) {
|
|
10211
10274
|
const exec = yield* Exec;
|
|
10212
10275
|
const { found, failed } = yield* locateAll(dir, nested, "go.mod", locate$1);
|
|
10213
|
-
const
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
"work",
|
|
10223
|
-
"edit",
|
|
10224
|
-
"-json"
|
|
10225
|
-
],
|
|
10226
|
-
cwd: root
|
|
10276
|
+
const works = yield* Effect.forEach(found, ({ root, work, claimed }) => Effect.gen(function* () {
|
|
10277
|
+
if (!work) return {
|
|
10278
|
+
modules: [{
|
|
10279
|
+
root,
|
|
10280
|
+
moduleDir: root,
|
|
10281
|
+
local: /* @__PURE__ */ new Set()
|
|
10282
|
+
}],
|
|
10283
|
+
failed: [],
|
|
10284
|
+
covered: [root]
|
|
10227
10285
|
};
|
|
10228
|
-
const
|
|
10286
|
+
const covered = [];
|
|
10229
10287
|
const place = placeOf$1(dir, root);
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10288
|
+
const scanned = yield* Effect.gen(function* () {
|
|
10289
|
+
const edit = {
|
|
10290
|
+
command: "go",
|
|
10291
|
+
args: [
|
|
10292
|
+
"work",
|
|
10293
|
+
"edit",
|
|
10294
|
+
"-json"
|
|
10295
|
+
],
|
|
10296
|
+
cwd: root
|
|
10297
|
+
};
|
|
10298
|
+
const file = yield* exec.stdout(edit).pipe(Effect.flatMap(decodeGoJson(WorkFile, commandLine(edit))));
|
|
10299
|
+
const used = (file.Use ?? []).map((use) => path.resolve(root, use.DiskPath));
|
|
10300
|
+
covered.push(...used);
|
|
10301
|
+
const paths = yield* Effect.forEach(used, (moduleDir) => Effect.gen(function* () {
|
|
10302
|
+
const mod = yield* readModFile(exec, moduleDir);
|
|
10303
|
+
if (mod.Module === void 0) return yield* new OutputMalformed({
|
|
10304
|
+
command: "go mod edit -json",
|
|
10305
|
+
issue: `no module path in ${path.join(moduleDir, "go.mod")}`
|
|
10306
|
+
});
|
|
10307
|
+
return mod.Module.Path;
|
|
10308
|
+
}), { concurrency: 8 });
|
|
10309
|
+
const local = /* @__PURE__ */ new Set([...paths, ...(file.Replace ?? []).map((replace) => replace.Old.Path)]);
|
|
10310
|
+
return used.map((moduleDir) => ({
|
|
10311
|
+
root,
|
|
10312
|
+
moduleDir,
|
|
10313
|
+
local
|
|
10314
|
+
})).filter(({ moduleDir }) => place === "" || placeOf$1(dir, moduleDir) !== "");
|
|
10315
|
+
}).pipe(Effect.result);
|
|
10316
|
+
if (Result.isSuccess(scanned)) return {
|
|
10317
|
+
modules: scanned.success,
|
|
10318
|
+
failed: [],
|
|
10319
|
+
covered
|
|
10320
|
+
};
|
|
10321
|
+
return {
|
|
10322
|
+
modules: [],
|
|
10323
|
+
failed: [failing(path.join(place, "go.work"), scanned.failure)],
|
|
10324
|
+
covered: [...covered, ...claimed]
|
|
10325
|
+
};
|
|
10326
|
+
}), { concurrency: 4 });
|
|
10327
|
+
const listed = new Set(works.flatMap((work) => work.covered));
|
|
10239
10328
|
const outside = nested.flatMap((moduleDir) => listed.has(moduleDir) ? [] : [{
|
|
10240
10329
|
root: moduleDir,
|
|
10241
|
-
moduleDir
|
|
10330
|
+
moduleDir,
|
|
10331
|
+
local: /* @__PURE__ */ new Set()
|
|
10242
10332
|
}]);
|
|
10243
|
-
const providers = yield* Effect.forEach([...
|
|
10333
|
+
const providers = yield* Effect.forEach([...works.flatMap(({ modules }) => modules), ...outside], ({ root, moduleDir, local }) => make$7(root, moduleDir, placeOf$1(dir, root), local));
|
|
10244
10334
|
return [
|
|
10245
10335
|
...failed,
|
|
10246
|
-
...
|
|
10336
|
+
...works.flatMap((work) => work.failed),
|
|
10247
10337
|
...providers
|
|
10248
10338
|
];
|
|
10249
10339
|
});
|
|
10250
10340
|
// src/inventory/ProviderSource.ts
|
|
10251
10341
|
var ProviderSource = class extends Context.Service()("upup/ProviderSource") {};
|
|
10252
|
-
// src/node/
|
|
10342
|
+
// src/node/release-history.ts
|
|
10253
10343
|
const minutesPerDay = 1440;
|
|
10254
|
-
const firstPnpmMajorWithDefaultReleaseAge = 11;
|
|
10255
10344
|
const gateSettings = {
|
|
10256
10345
|
pnpm: {
|
|
10257
10346
|
age: "minimumReleaseAge",
|
|
10258
10347
|
exclude: "minimumReleaseAgeExclude",
|
|
10259
10348
|
unit: 1,
|
|
10260
|
-
unset:
|
|
10349
|
+
unset: minutesPerDay
|
|
10261
10350
|
},
|
|
10262
10351
|
npm: {
|
|
10263
10352
|
age: "min-release-age",
|
|
10264
10353
|
exclude: "min-release-age-exclude",
|
|
10265
10354
|
unit: minutesPerDay,
|
|
10266
|
-
unset:
|
|
10355
|
+
unset: 0
|
|
10267
10356
|
}
|
|
10268
10357
|
};
|
|
10269
|
-
const releaseGate = (manager,
|
|
10358
|
+
const releaseGate = (manager, config) => {
|
|
10270
10359
|
const settings = gateSettings[manager];
|
|
10271
|
-
const minutes = Number(config[settings.age] ?? settings.unset
|
|
10360
|
+
const minutes = Number(config[settings.age] ?? settings.unset) * settings.unit;
|
|
10272
10361
|
return {
|
|
10273
10362
|
minutes: Number.isFinite(minutes) && minutes > 0 ? minutes : 0,
|
|
10274
10363
|
exclude: [config[settings.exclude] ?? []].flat().filter((pattern) => typeof pattern === "string")
|
|
@@ -10287,16 +10376,18 @@ const exempt = (rules, name, version) => {
|
|
|
10287
10376
|
};
|
|
10288
10377
|
const isPrerelease = (version) => semver.prerelease(version) !== null;
|
|
10289
10378
|
const lineOf = (version) => (semver.prerelease(version) ?? []).map((part) => Predicate.isNumber(part) ? "#" : part).join(".");
|
|
10290
|
-
const
|
|
10379
|
+
const releaseSuccessor = (name, current, history, gate, now) => {
|
|
10291
10380
|
const line = lineOf(current);
|
|
10292
10381
|
const following = isPrerelease(current);
|
|
10293
10382
|
const pinned = Date.parse(history.time[current] ?? "");
|
|
10294
10383
|
if (following && Number.isNaN(pinned)) return Result.fail(`${name} ${current} has no publish date, so upup offers it nothing`);
|
|
10295
10384
|
const releasedBeforePrereleasePin = (published) => following && !(published > pinned);
|
|
10385
|
+
const latest = semver.valid(history.latest ?? "");
|
|
10386
|
+
const beyondLatest = (candidate) => latest !== null && semver.gt(candidate, latest);
|
|
10296
10387
|
let newest;
|
|
10297
10388
|
for (const candidate of history.versions) {
|
|
10298
10389
|
if (semver.valid(candidate) === null || !semver.gt(candidate, current)) continue;
|
|
10299
|
-
if (isPrerelease(candidate)
|
|
10390
|
+
if (isPrerelease(candidate) ? lineOf(candidate) !== line : beyondLatest(candidate)) continue;
|
|
10300
10391
|
const published = Date.parse(history.time[candidate] ?? "");
|
|
10301
10392
|
if (Number.isNaN(published) || releasedBeforePrereleasePin(published)) continue;
|
|
10302
10393
|
if (gate.minutes > 0 && now - published < gate.minutes * 6e4 && !exempt(gate.exclude, name, candidate)) continue;
|
|
@@ -10304,35 +10395,63 @@ const prereleaseSuccessor = (name, current, history, gate, now) => {
|
|
|
10304
10395
|
}
|
|
10305
10396
|
return Result.succeed(newest);
|
|
10306
10397
|
};
|
|
10307
|
-
const
|
|
10398
|
+
const ViewedRelease = Schema.Struct({
|
|
10308
10399
|
versions: Schema.Union([Schema.String, Schema.Array(Schema.String)]),
|
|
10309
|
-
time: Schema.Record(Schema.String, Schema.String)
|
|
10310
|
-
})
|
|
10311
|
-
|
|
10400
|
+
time: Schema.Record(Schema.String, Schema.String),
|
|
10401
|
+
"dist-tags": Schema.optionalKey(Schema.Struct({ latest: Schema.optionalKey(Schema.String) }))
|
|
10402
|
+
});
|
|
10403
|
+
const Viewed = Schema.Union([ViewedRelease, Schema.NonEmptyArray(ViewedRelease)]);
|
|
10404
|
+
const ViewFailed = Schema.Struct({ error: Schema.Struct({
|
|
10405
|
+
code: Schema.optionalKey(Schema.String),
|
|
10406
|
+
summary: Schema.optionalKey(Schema.String),
|
|
10407
|
+
message: Schema.optionalKey(Schema.String)
|
|
10408
|
+
}) });
|
|
10409
|
+
const releaseOffers = Effect.fn("releaseOffers")(function* (pins, manager, run, config) {
|
|
10312
10410
|
const exec = yield* Exec;
|
|
10313
10411
|
const now = yield* Clock.currentTimeMillis;
|
|
10314
|
-
const read = (artifact)
|
|
10412
|
+
const read = Effect.fn("releaseOffers.read")(function* (artifact) {
|
|
10315
10413
|
const view = run([
|
|
10316
10414
|
"view",
|
|
10317
10415
|
artifact,
|
|
10318
10416
|
"versions",
|
|
10319
10417
|
"time",
|
|
10418
|
+
"dist-tags",
|
|
10320
10419
|
"--json"
|
|
10321
10420
|
]);
|
|
10322
|
-
|
|
10421
|
+
const output = yield* exec.run(view);
|
|
10422
|
+
if (output.exitCode !== 0) {
|
|
10423
|
+
const reported = Option.getOrUndefined(Schema.decodeOption(Schema.fromJsonString(ViewFailed))(output.stdout))?.error;
|
|
10424
|
+
if (reported?.code?.includes("404") === true) return [artifact, Result.fail(`${artifact} isn't on the registry, or your credentials can't see it, so upup offers it nothing`)];
|
|
10425
|
+
return yield* new CommandFailed({
|
|
10426
|
+
command: commandLine(view),
|
|
10427
|
+
cwd: view.cwd,
|
|
10428
|
+
exitCode: output.exitCode,
|
|
10429
|
+
output: reported?.summary ?? reported?.message ?? output.stderr
|
|
10430
|
+
});
|
|
10431
|
+
}
|
|
10432
|
+
const decoded = yield* decodeJson(Viewed, commandLine(view))(output.stdout);
|
|
10433
|
+
const viewed = "versions" in decoded ? decoded : decoded[0];
|
|
10434
|
+
const latest = viewed["dist-tags"]?.latest;
|
|
10435
|
+
const history = {
|
|
10323
10436
|
versions: [viewed.versions].flat(),
|
|
10324
|
-
time: viewed.time
|
|
10325
|
-
|
|
10437
|
+
time: viewed.time,
|
|
10438
|
+
...latest === void 0 ? {} : { latest }
|
|
10439
|
+
};
|
|
10440
|
+
return [artifact, Result.succeed(history)];
|
|
10441
|
+
});
|
|
10442
|
+
const artifacts = new Set(pins.map((pin) => pin.artifact));
|
|
10443
|
+
const reads = yield* Effect.result(Effect.forEach(artifacts, read, { concurrency: 8 }));
|
|
10444
|
+
const histories = new Map(Result.isSuccess(reads) ? reads.success : []);
|
|
10445
|
+
const notices = Result.isFailure(reads) ? [{ message: `release histories could not be read, so upup offers nothing: ${describeError(reads.failure)}` }] : [];
|
|
10446
|
+
const gate = releaseGate(manager, config);
|
|
10447
|
+
const offer = (pin) => {
|
|
10448
|
+
const history = histories.get(pin.artifact);
|
|
10449
|
+
if (history === void 0) return Result.succeed(void 0);
|
|
10450
|
+
return Result.flatMap(history, (found) => releaseSuccessor(pin.artifact, pin.current, found, gate, now));
|
|
10326
10451
|
};
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
const version = manager === "pnpm" && artifacts.size > 0 ? yield* exec.stdout(run(["--version"])) : "";
|
|
10331
|
-
const gate = releaseGate(manager, version.trim(), config);
|
|
10332
|
-
return (pin) => {
|
|
10333
|
-
const history = followed(pin) ? histories.get(pin.artifact) : void 0;
|
|
10334
|
-
if (history === void 0) return;
|
|
10335
|
-
return Result.isFailure(history) ? Result.fail(`${pin.artifact}'s release history could not be read, so upup offers it nothing: ${describeError(history.failure)}`) : prereleaseSuccessor(pin.artifact, pin.current, history.success, gate, now);
|
|
10452
|
+
return {
|
|
10453
|
+
offer,
|
|
10454
|
+
notices
|
|
10336
10455
|
};
|
|
10337
10456
|
});
|
|
10338
10457
|
// src/node/script-policy.ts
|
|
@@ -10388,7 +10507,7 @@ const Root = Schema.Struct({
|
|
|
10388
10507
|
dependencies: Schema.optionalKey(Schema.Record(Schema.String, Dependency))
|
|
10389
10508
|
});
|
|
10390
10509
|
const Listed$1 = Schema.Union([Root, Schema.Array(Root)]);
|
|
10391
|
-
const Outdated
|
|
10510
|
+
const Outdated = Schema.Record(Schema.String, Schema.Struct({ latest: Schema.optionalKey(Schema.String) }));
|
|
10392
10511
|
const parseListed = (provider, listed) => {
|
|
10393
10512
|
const roots = Array.isArray(listed) ? listed : [listed];
|
|
10394
10513
|
const pkg = (name, version, runtime) => ({
|
|
@@ -10422,7 +10541,7 @@ const makeGlobal = (spec) => Effect.gen(function* () {
|
|
|
10422
10541
|
const latest = Effect.gen(function* () {
|
|
10423
10542
|
const outdated = invocation(spec.outdated);
|
|
10424
10543
|
const output = yield* exec.run(outdated);
|
|
10425
|
-
const parsed = yield* decodeJson(Outdated
|
|
10544
|
+
const parsed = yield* decodeJson(Outdated, commandLine(outdated))(output.stdout).pipe(Effect.catchTag("OutputMalformed", (error) => output.exitCode === 0 ? Effect.fail(error) : Effect.fail(new CommandFailed({
|
|
10426
10545
|
command: commandLine(outdated),
|
|
10427
10546
|
cwd: outdated.cwd,
|
|
10428
10547
|
exitCode: output.exitCode,
|
|
@@ -10435,21 +10554,21 @@ const makeGlobal = (spec) => Effect.gen(function* () {
|
|
|
10435
10554
|
installed,
|
|
10436
10555
|
latest,
|
|
10437
10556
|
readNpmConfig(spec.config, "/").pipe(Effect.provideService(Exec, exec))
|
|
10438
|
-
], { concurrency:
|
|
10439
|
-
const
|
|
10557
|
+
], { concurrency: "unbounded" });
|
|
10558
|
+
const { offer, notices: unread } = yield* releaseOffers(packages.filter((pkg) => isPrerelease(pkg.current)), spec.config, (args) => ({
|
|
10440
10559
|
command: spec.config,
|
|
10441
10560
|
args,
|
|
10442
10561
|
cwd: "/"
|
|
10443
10562
|
}), config).pipe(Effect.provideService(Exec, exec));
|
|
10444
|
-
const notices = [];
|
|
10563
|
+
const notices = [...unread];
|
|
10445
10564
|
return {
|
|
10446
10565
|
packages: packages.map(withNpmOrigin(npmRegistries(config))).map((pkg) => {
|
|
10447
|
-
const line =
|
|
10448
|
-
if (
|
|
10566
|
+
const line = offer(pkg);
|
|
10567
|
+
if (Result.isFailure(line)) notices.push({
|
|
10449
10568
|
rowKey: rowKey(pkg),
|
|
10450
10569
|
message: line.failure
|
|
10451
10570
|
});
|
|
10452
|
-
const target =
|
|
10571
|
+
const target = isPrerelease(pkg.current) ? Result.getOrUndefined(line) : targets.get(pkg.name);
|
|
10453
10572
|
return target !== void 0 && isNewerStable(pkg.current, target) ? withTarget(pkg, target) : pkg;
|
|
10454
10573
|
}),
|
|
10455
10574
|
notices
|
|
@@ -10666,12 +10785,13 @@ const scan = Effect.fn("NodePins.scan")(function* (root, dir, nested) {
|
|
|
10666
10785
|
const fs = yield* FileSystem.FileSystem;
|
|
10667
10786
|
const read = (file) => readText(path.join(root, file));
|
|
10668
10787
|
const exists = (file) => fs.exists(path.join(root, file)).pipe(Effect.mapError(fileFailed("stat", path.join(root, file))));
|
|
10669
|
-
const
|
|
10670
|
-
const
|
|
10671
|
-
const
|
|
10788
|
+
const [workflows, listed] = yield* Effect.all([workflowFiles(root), versionFilesOf(root, dir, nested)], { concurrency: "unbounded" });
|
|
10789
|
+
const steps = workflows.flatMap(({ file, text }) => stepPins(file, text) ?? []);
|
|
10790
|
+
const versioned = yield* Effect.filter(listed, exists, { concurrency: "unbounded" });
|
|
10791
|
+
const files = yield* Effect.forEach(versioned.toSorted(), (file) => Effect.map(read(file), (text) => filePin(file, text)), { concurrency: 8 });
|
|
10672
10792
|
return [...steps, ...files.flat()];
|
|
10673
10793
|
});
|
|
10674
|
-
const make$
|
|
10794
|
+
const make$6 = (root, dir = root, nested = []) => Effect.gen(function* () {
|
|
10675
10795
|
const fs = yield* FileSystem.FileSystem;
|
|
10676
10796
|
const client = yield* HttpClient.HttpClient;
|
|
10677
10797
|
const context = yield* Effect.context();
|
|
@@ -10742,7 +10862,7 @@ const discover$1 = Effect.fn("NodePins.discover")(function* (dir, nested = []) {
|
|
|
10742
10862
|
if (Option.isNone(repository)) return [];
|
|
10743
10863
|
const pins = yield* scan(repository.value, dir, nested).pipe(Effect.result);
|
|
10744
10864
|
if (Result.isSuccess(pins) && pins.success.length === 0) return [];
|
|
10745
|
-
return [yield* make$
|
|
10865
|
+
return [yield* make$6(repository.value, dir, nested)];
|
|
10746
10866
|
});
|
|
10747
10867
|
// src/node/selector.ts
|
|
10748
10868
|
const split = (name, spec) => {
|
|
@@ -10794,20 +10914,21 @@ const Manifest = Schema.Struct({
|
|
|
10794
10914
|
optionalDependencies: Schema.optionalKey(Specs)
|
|
10795
10915
|
});
|
|
10796
10916
|
const Resolved = Schema.Record(Schema.String, Schema.Struct({ version: Schema.String }));
|
|
10797
|
-
const PnpmLock = Schema.Struct({
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
10917
|
+
const PnpmLock = Schema.Struct({
|
|
10918
|
+
overrides: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
10919
|
+
importers: Schema.optionalKey(Schema.Record(Schema.String, Schema.Struct({
|
|
10920
|
+
dependencies: Schema.optionalKey(Resolved),
|
|
10921
|
+
devDependencies: Schema.optionalKey(Resolved),
|
|
10922
|
+
optionalDependencies: Schema.optionalKey(Resolved)
|
|
10923
|
+
})))
|
|
10924
|
+
});
|
|
10802
10925
|
const PnpmManagerLock = Schema.Struct({ importers: Schema.optionalKey(Schema.Record(Schema.String, Schema.Struct({ packageManagerDependencies: Schema.optionalKey(Resolved) }))) });
|
|
10803
10926
|
const NpmLock = Schema.Struct({ packages: Schema.Record(Schema.String, Schema.Struct({ version: Schema.optionalKey(Schema.String) })) });
|
|
10804
10927
|
const Workspace = Schema.Struct({
|
|
10805
10928
|
catalog: Schema.optionalKey(Specs),
|
|
10806
|
-
catalogs: Schema.optionalKey(Schema.Record(Schema.String, Specs))
|
|
10929
|
+
catalogs: Schema.optionalKey(Schema.Record(Schema.String, Specs)),
|
|
10930
|
+
overrides: Schema.optionalKey(Specs)
|
|
10807
10931
|
});
|
|
10808
|
-
const OutdatedEntry = Schema.Struct({ latest: Schema.optionalKey(Schema.String) });
|
|
10809
|
-
const Outdated = Schema.Record(Schema.String, Schema.Union([OutdatedEntry, Schema.Array(OutdatedEntry)]));
|
|
10810
|
-
const NpmError = Schema.Struct({ error: Schema.Struct({ summary: Schema.String }) });
|
|
10811
10932
|
const PackageJsonFields = Schema.Struct({
|
|
10812
10933
|
workspaces: Schema.optionalKey(Schema.Unknown),
|
|
10813
10934
|
packageManager: Schema.optionalKey(Schema.String),
|
|
@@ -10830,25 +10951,22 @@ const managerEntry = "the package manager's declaration is removed by hand, in p
|
|
|
10830
10951
|
const resolvedVersion = (version) => version?.split("(")[0];
|
|
10831
10952
|
const readJson = (schema, file) => readText(file).pipe(Effect.flatMap((text) => decodeFile(schema, file)(parse$1(text))));
|
|
10832
10953
|
const exact = (version) => semver.valid(version) === version ? version : void 0;
|
|
10954
|
+
const overriddenName = (key) => {
|
|
10955
|
+
const target = key.split(">").at(-1) ?? key;
|
|
10956
|
+
const at = target.indexOf("@", target.startsWith("@") ? 1 : 0);
|
|
10957
|
+
return at === -1 ? target : target.slice(0, at);
|
|
10958
|
+
};
|
|
10833
10959
|
const corepackHashed = (name, algorithm) => {
|
|
10834
10960
|
if (algorithm === void 0) return {};
|
|
10835
10961
|
if (algorithm === "sha512") return { hashed: true };
|
|
10836
10962
|
return { held: `${name}'s packageManager entry carries a ${algorithm} hash, which upup can work out only for sha512; move it by hand` };
|
|
10837
10963
|
};
|
|
10838
|
-
const pinOf = ({ pkg, manager: own }) => ({
|
|
10839
|
-
artifact: pkg.artifact,
|
|
10840
|
-
current: pkg.current,
|
|
10841
|
-
...own === true ? { history: true } : {}
|
|
10842
|
-
});
|
|
10843
10964
|
const editDeclaration = (text, manifest, entry, next) => {
|
|
10844
10965
|
if (next === void 0 && entry.pkg.unremovable !== void 0) return Result.fail(entry.pkg.unremovable);
|
|
10845
10966
|
if (manifest.endsWith(".yaml") && next !== void 0) return editYamlScalar(text, entry.path, next);
|
|
10846
10967
|
return editJson(text, entry.path, next);
|
|
10847
10968
|
};
|
|
10848
|
-
const
|
|
10849
|
-
const withoutLockedVersionSuffix = (key) => key.replace(/(?<=.)@[^@/ ]+$/u, "");
|
|
10850
|
-
const outdatedName = (key) => withoutLockedVersionSuffix(withoutDependencyKindMarker(key));
|
|
10851
|
-
const make$4 = (root, manager, place = "") => Effect.gen(function* () {
|
|
10969
|
+
const make$5 = (root, manager, place = "", claimed = []) => Effect.gen(function* () {
|
|
10852
10970
|
const providerName = placed(manager, place);
|
|
10853
10971
|
const exec = yield* Exec;
|
|
10854
10972
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -10929,27 +11047,35 @@ const make$4 = (root, manager, place = "") => Effect.gen(function* () {
|
|
|
10929
11047
|
onNone: () => []
|
|
10930
11048
|
})
|
|
10931
11049
|
});
|
|
11050
|
+
const lockedDirs = new Set(members.map((member) => path.normalize(member)));
|
|
11051
|
+
const unlocked = claimed.filter((dir) => !lockedDirs.has(path.normalize(dir))).toSorted((a, b) => a.localeCompare(b));
|
|
11052
|
+
if (unlocked.length > 0) notices.push({ message: `not listed: ${unlocked.join(", ")} (not in ${lockfile})` });
|
|
10932
11053
|
const catalogLocked = /* @__PURE__ */ new Map();
|
|
10933
|
-
|
|
11054
|
+
const memberManifests = yield* Effect.forEach(members.toSorted((a, b) => a.localeCompare(b)), (member) => {
|
|
10934
11055
|
const memberDir = member === "." ? "" : member;
|
|
10935
11056
|
const manifest = path.join(memberDir, "package.json");
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
|
|
10944
|
-
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
11057
|
+
return Effect.map(readJson(Manifest, path.join(root, manifest)), (fields) => ({
|
|
11058
|
+
member,
|
|
11059
|
+
memberDir,
|
|
11060
|
+
manifest,
|
|
11061
|
+
fields
|
|
11062
|
+
}));
|
|
11063
|
+
}, { concurrency: 8 });
|
|
11064
|
+
for (const { member, memberDir, manifest, fields } of memberManifests) for (const section of sections) for (const [name, spec] of Object.entries(fields[section] ?? {})) {
|
|
11065
|
+
const resolved = Option.match(pnpm, {
|
|
11066
|
+
onSome: (lock) => resolvedVersion(lock.importers?.[member]?.[section]?.[name]?.version),
|
|
11067
|
+
onNone: () => Option.match(npm, {
|
|
11068
|
+
onSome: (lock) => lock.packages[path.join(memberDir, "node_modules", name)]?.version ?? lock.packages[path.join("node_modules", name)]?.version,
|
|
11069
|
+
onNone: () => void 0
|
|
11070
|
+
})
|
|
11071
|
+
});
|
|
11072
|
+
if (spec.startsWith("catalog:")) {
|
|
11073
|
+
const catalog = spec.slice(8) || "default";
|
|
11074
|
+
const locked = catalogLocked.get(catalog) ?? /* @__PURE__ */ new Map();
|
|
11075
|
+
if (resolved !== void 0 && !locked.has(name)) catalogLocked.set(catalog, locked.set(name, resolved));
|
|
11076
|
+
continue;
|
|
10952
11077
|
}
|
|
11078
|
+
add(manifest, section, [section, name], name, spec, resolved);
|
|
10953
11079
|
}
|
|
10954
11080
|
if (Option.isSome(pnpm) && (yield* exists(path.join(root, "pnpm-workspace.yaml")))) {
|
|
10955
11081
|
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
@@ -10963,8 +11089,25 @@ const make$4 = (root, manager, place = "") => Effect.gen(function* () {
|
|
|
10963
11089
|
["catalogs", catalog],
|
|
10964
11090
|
entries
|
|
10965
11091
|
])];
|
|
11092
|
+
const overridden = /* @__PURE__ */ new Map();
|
|
11093
|
+
for (const [key, spec] of Object.entries(workspace.overrides ?? {})) {
|
|
11094
|
+
if (!spec.startsWith("catalog:")) continue;
|
|
11095
|
+
const name = overriddenName(key);
|
|
11096
|
+
const locked = pnpm.value.overrides?.[key];
|
|
11097
|
+
const selector = locked === void 0 ? void 0 : parseSelector(name, locked);
|
|
11098
|
+
if (selector === void 0 || exact(selector.range) === void 0) continue;
|
|
11099
|
+
const id = `${spec.slice(8) || "default"}:${name}`;
|
|
11100
|
+
const versions = overridden.get(id) ?? /* @__PURE__ */ new Set();
|
|
11101
|
+
versions.add(selector.artifact === name ? selector.range : `${selector.artifact}@${selector.range}`);
|
|
11102
|
+
overridden.set(id, versions);
|
|
11103
|
+
}
|
|
10966
11104
|
for (const [catalog, catalogPath, entries] of catalogs) for (const [name, spec] of Object.entries(entries)) {
|
|
10967
|
-
const
|
|
11105
|
+
const overrides = overridden.get(`${catalog}:${name}`);
|
|
11106
|
+
if (overrides !== void 0 && overrides.size > 1) {
|
|
11107
|
+
notices.push({ message: `${catalog} catalog entry ${name} has conflicting locked overrides; run pnpm install` });
|
|
11108
|
+
continue;
|
|
11109
|
+
}
|
|
11110
|
+
const resolved = catalogLocked.get(catalog)?.get(name) ?? overrides?.values().next().value;
|
|
10968
11111
|
if (resolved === void 0) continue;
|
|
10969
11112
|
add("pnpm-workspace.yaml", catalogPath.join("."), [...catalogPath, name], name, spec, resolved);
|
|
10970
11113
|
}
|
|
@@ -11001,53 +11144,20 @@ const make$4 = (root, manager, place = "") => Effect.gen(function* () {
|
|
|
11001
11144
|
notices
|
|
11002
11145
|
};
|
|
11003
11146
|
}).pipe(Effect.provideService(FileSystem.FileSystem, fs));
|
|
11004
|
-
const latest = Effect.gen(function* () {
|
|
11005
|
-
const workspaces = manager === "npm" && (yield* readJson(PackageJsonFields, path.join(root, "package.json"))).workspaces !== void 0;
|
|
11006
|
-
const outdated = run(manager === "pnpm" ? [
|
|
11007
|
-
"outdated",
|
|
11008
|
-
"-r",
|
|
11009
|
-
"--format",
|
|
11010
|
-
"json"
|
|
11011
|
-
] : [
|
|
11012
|
-
"outdated",
|
|
11013
|
-
"--json",
|
|
11014
|
-
...workspaces ? ["--workspaces", "--include-workspace-root"] : []
|
|
11015
|
-
]);
|
|
11016
|
-
const output = yield* exec.run(outdated);
|
|
11017
|
-
const reported = Schema.decodeOption(Schema.fromJsonString(NpmError))(output.stdout);
|
|
11018
|
-
if (output.exitCode !== 0 && (output.stdout.trim() === "" || Option.isSome(reported))) return yield* new CommandFailed({
|
|
11019
|
-
command: commandLine(outdated),
|
|
11020
|
-
cwd: root,
|
|
11021
|
-
exitCode: output.exitCode,
|
|
11022
|
-
output: Option.isSome(reported) ? reported.value.error.summary : output.stderr
|
|
11023
|
-
});
|
|
11024
|
-
const parsed = yield* decodeJson(Outdated, commandLine(outdated))(output.stdout || "{}");
|
|
11025
|
-
const targets = /* @__PURE__ */ new Map();
|
|
11026
|
-
for (const [key, entries] of Object.entries(parsed)) {
|
|
11027
|
-
const name = outdatedName(key);
|
|
11028
|
-
const newest = [entries].flat().find((entry) => entry.latest !== void 0)?.latest;
|
|
11029
|
-
if (newest !== void 0 && !targets.has(name)) targets.set(name, newest);
|
|
11030
|
-
}
|
|
11031
|
-
return targets;
|
|
11032
|
-
}).pipe(Effect.provideService(FileSystem.FileSystem, fs));
|
|
11033
11147
|
const list = Effect.gen(function* () {
|
|
11034
|
-
const [{ declared, notices },
|
|
11035
|
-
declarations,
|
|
11036
|
-
latest,
|
|
11037
|
-
readNpmConfig(manager, root).pipe(Effect.provideService(Exec, exec))
|
|
11038
|
-
], { concurrency: 3 });
|
|
11148
|
+
const [{ declared, notices }, config] = yield* Effect.all([declarations, readNpmConfig(manager, root).pipe(Effect.provideService(Exec, exec))], { concurrency: "unbounded" });
|
|
11039
11149
|
const origin = withNpmOrigin(npmRegistries(config));
|
|
11040
|
-
const
|
|
11150
|
+
const { offer, notices: unread } = yield* releaseOffers(declared.flatMap((entry) => entry.held === true ? [] : [entry.pkg]), manager, run, config).pipe(Effect.provideService(Exec, exec));
|
|
11041
11151
|
return {
|
|
11042
11152
|
packages: declared.map((entry) => {
|
|
11043
11153
|
const pkg = origin(entry.pkg);
|
|
11044
11154
|
if (entry.held === true) return pkg;
|
|
11045
|
-
const line =
|
|
11046
|
-
if (
|
|
11155
|
+
const line = offer(entry.pkg);
|
|
11156
|
+
if (Result.isFailure(line)) notices.push({
|
|
11047
11157
|
rowKey: rowKey(pkg),
|
|
11048
11158
|
message: line.failure
|
|
11049
11159
|
});
|
|
11050
|
-
const target =
|
|
11160
|
+
const target = Result.getOrUndefined(line);
|
|
11051
11161
|
if (target === void 0 || !isNewerStable(pkg.current, target)) return pkg;
|
|
11052
11162
|
const selector = parseSelector(pkg.name, pkg.declaration?.selector ?? "");
|
|
11053
11163
|
if (selector === void 0 || moveTo(selector, target) === void 0) {
|
|
@@ -11059,7 +11169,7 @@ const make$4 = (root, manager, place = "") => Effect.gen(function* () {
|
|
|
11059
11169
|
}
|
|
11060
11170
|
return withTarget(pkg, target);
|
|
11061
11171
|
}),
|
|
11062
|
-
notices
|
|
11172
|
+
notices: [...unread, ...notices]
|
|
11063
11173
|
};
|
|
11064
11174
|
}).pipe(Effect.withSpan(`NodeProject.list`));
|
|
11065
11175
|
const find = (pkg) => declarations.pipe(Effect.map(({ declared }) => declared.find((entry) => entry.pkg.id === pkg.id)));
|
|
@@ -11230,7 +11340,7 @@ const discover = Effect.fn("NodeProject.discover")(function* (dir, nested = [])
|
|
|
11230
11340
|
const { found, failed } = yield* locateAll(dir, nested, "package.json", locate);
|
|
11231
11341
|
const providers = [...failed];
|
|
11232
11342
|
const unlisted = [];
|
|
11233
|
-
for (const { root, manager } of found) {
|
|
11343
|
+
for (const { root, manager, claimed } of found) {
|
|
11234
11344
|
const place = placeOf$1(dir, root);
|
|
11235
11345
|
if (!isManager(manager)) {
|
|
11236
11346
|
if (place === "") providers.push(failing(manager, new Unsupported({ reason: `${path.relative(dir, root) || "This"} project uses ${manager}, which upup does not support` })));
|
|
@@ -11240,11 +11350,147 @@ const discover = Effect.fn("NodeProject.discover")(function* (dir, nested = [])
|
|
|
11240
11350
|
const lockfile = path.join(root, lockfiles[manager]);
|
|
11241
11351
|
const installed = yield* fs.exists(lockfile).pipe(Effect.mapError(fileFailed("stat", lockfile)));
|
|
11242
11352
|
if (place !== "" && !installed) unlisted.push(`${place} (no ${lockfiles[manager]} yet)`);
|
|
11243
|
-
else
|
|
11353
|
+
else {
|
|
11354
|
+
const manifests = yield* Effect.filter(claimed, (from) => fs.exists(path.join(from, "package.json")).pipe(Effect.mapError(fileFailed("stat", from))), { concurrency: "unbounded" });
|
|
11355
|
+
providers.push(yield* make$5(root, manager, place, manifests.map((from) => path.relative(root, from))));
|
|
11356
|
+
}
|
|
11244
11357
|
}
|
|
11245
11358
|
if (unlisted.length > 0) providers.push(notListed(unlisted));
|
|
11246
11359
|
return providers;
|
|
11247
11360
|
});
|
|
11361
|
+
// src/node/VpEnv.ts
|
|
11362
|
+
const vp$1 = (args) => ({
|
|
11363
|
+
command: "vp",
|
|
11364
|
+
args: ["env", ...args],
|
|
11365
|
+
cwd: "/"
|
|
11366
|
+
});
|
|
11367
|
+
const defaults = vp$1(["default"]);
|
|
11368
|
+
const tools$1 = /* @__PURE__ */ new Set([
|
|
11369
|
+
"node",
|
|
11370
|
+
"npm",
|
|
11371
|
+
"pnpm",
|
|
11372
|
+
"yarn",
|
|
11373
|
+
"bun"
|
|
11374
|
+
]);
|
|
11375
|
+
const Release$1 = Schema.Struct({
|
|
11376
|
+
version: Schema.String,
|
|
11377
|
+
latest: Schema.Boolean
|
|
11378
|
+
});
|
|
11379
|
+
const NodeReleases = Schema.Struct({ node: Schema.Array(Release$1) });
|
|
11380
|
+
const ManagerReleases = Schema.Struct({ package_managers: Schema.Record(Schema.String, Schema.Array(Release$1)) });
|
|
11381
|
+
const malformed$1 = (command, issue) => new OutputMalformed({
|
|
11382
|
+
command: commandLine(command),
|
|
11383
|
+
issue
|
|
11384
|
+
});
|
|
11385
|
+
const readDefaults = Effect.fn("VpEnv.readDefaults")(function* (output) {
|
|
11386
|
+
const entries = [];
|
|
11387
|
+
const notices = [];
|
|
11388
|
+
let recognized = false;
|
|
11389
|
+
for (const line of output.split("\n").map((text) => text.trim())) {
|
|
11390
|
+
if (line.startsWith("No default Node.js version configured.") || line === "Package manager defaults: not configured") recognized = true;
|
|
11391
|
+
if (!line.startsWith("Default ")) continue;
|
|
11392
|
+
const fields = /^Default (?<name>.+) version: (?<selector>\S+)$/u.exec(line)?.groups;
|
|
11393
|
+
const label = fields?.["name"];
|
|
11394
|
+
const selector = fields?.["selector"];
|
|
11395
|
+
if (label === void 0 || selector === void 0) return yield* malformed$1(defaults, `unrecognized default: ${line}`);
|
|
11396
|
+
recognized = true;
|
|
11397
|
+
const name = label === "Node.js" ? "node" : label;
|
|
11398
|
+
if (tools$1.has(name)) entries.push({
|
|
11399
|
+
name,
|
|
11400
|
+
selector
|
|
11401
|
+
});
|
|
11402
|
+
else notices.push({ message: `Vite+ default ${name} is not supported` });
|
|
11403
|
+
}
|
|
11404
|
+
if (!recognized) return yield* malformed$1(defaults, "no global defaults found");
|
|
11405
|
+
if (entries.length === 0) return {
|
|
11406
|
+
packages: [],
|
|
11407
|
+
notices
|
|
11408
|
+
};
|
|
11409
|
+
const manifest = /^\s+Set via: (?<path>.+)$/mu.exec(output)?.groups?.["path"];
|
|
11410
|
+
if (manifest === void 0) return yield* malformed$1(defaults, "no configuration path found");
|
|
11411
|
+
return {
|
|
11412
|
+
packages: entries.map(({ name, selector }) => {
|
|
11413
|
+
const version = semver.parse(selector);
|
|
11414
|
+
return {
|
|
11415
|
+
provider: "vp-env",
|
|
11416
|
+
id: name,
|
|
11417
|
+
name,
|
|
11418
|
+
ecosystem: name === "node" ? "node" : "npm",
|
|
11419
|
+
artifact: name === "yarn" && version !== null && version.major >= 2 ? "@yarnpkg/cli-dist" : name,
|
|
11420
|
+
current: version?.version ?? selector,
|
|
11421
|
+
declaration: {
|
|
11422
|
+
manifest,
|
|
11423
|
+
section: `${name} default`,
|
|
11424
|
+
selector
|
|
11425
|
+
},
|
|
11426
|
+
description: version === null ? `Managed automatically by Vite+ (${selector})` : "Vite+ global default",
|
|
11427
|
+
unremovable: "Vite+ defaults are cleared with vp env default --unset"
|
|
11428
|
+
};
|
|
11429
|
+
}),
|
|
11430
|
+
notices
|
|
11431
|
+
};
|
|
11432
|
+
});
|
|
11433
|
+
const make$4 = Effect.gen(function* () {
|
|
11434
|
+
const exec = yield* Exec;
|
|
11435
|
+
const configured = exec.stdout(defaults).pipe(Effect.flatMap(readDefaults));
|
|
11436
|
+
const offer = Effect.fn("VpEnv.offer")(function* (pkg) {
|
|
11437
|
+
const current = semver.parse(pkg.current);
|
|
11438
|
+
if (current === null) return;
|
|
11439
|
+
const withinMajor = pkg.id === "node" || pkg.id === "yarn" && current.major === 1;
|
|
11440
|
+
const command = vp$1([
|
|
11441
|
+
"list-remote",
|
|
11442
|
+
pkg.id,
|
|
11443
|
+
...withinMajor ? [String(current.major)] : [],
|
|
11444
|
+
"--json"
|
|
11445
|
+
]);
|
|
11446
|
+
const output = yield* exec.stdout(command);
|
|
11447
|
+
const releases = pkg.id === "node" ? (yield* decodeJson(NodeReleases, commandLine(command))(output)).node : (yield* decodeJson(ManagerReleases, commandLine(command))(output)).package_managers[pkg.id];
|
|
11448
|
+
if (releases === void 0 || releases.length === 0) return yield* malformed$1(command, `no releases found for ${pkg.name}`);
|
|
11449
|
+
return newestOffer(pkg.current, releases.filter(({ version, latest }) => (withinMajor || latest) && semver.prerelease(version) === null).map(({ version }) => version));
|
|
11450
|
+
});
|
|
11451
|
+
const list = Effect.gen(function* () {
|
|
11452
|
+
const found = yield* configured;
|
|
11453
|
+
const offered = yield* Effect.forEach(found.packages, (pkg) => Effect.map(Effect.result(offer(pkg)), (target) => ({
|
|
11454
|
+
pkg,
|
|
11455
|
+
target
|
|
11456
|
+
})), { concurrency: 4 });
|
|
11457
|
+
const notices = [...found.notices];
|
|
11458
|
+
return {
|
|
11459
|
+
packages: offered.map(({ pkg, target }) => {
|
|
11460
|
+
if (Result.isFailure(target)) {
|
|
11461
|
+
notices.push({
|
|
11462
|
+
rowKey: rowKey(pkg),
|
|
11463
|
+
message: describeError(target.failure)
|
|
11464
|
+
});
|
|
11465
|
+
return pkg;
|
|
11466
|
+
}
|
|
11467
|
+
return target.success === void 0 ? pkg : withTarget(pkg, target.success);
|
|
11468
|
+
}),
|
|
11469
|
+
notices
|
|
11470
|
+
};
|
|
11471
|
+
}).pipe(Effect.withSpan("VpEnv.list"));
|
|
11472
|
+
const observe = Effect.fn("VpEnv.observe")(function* (pkg) {
|
|
11473
|
+
return Option.fromUndefinedOr((yield* configured).packages.find((candidate) => candidate.id === pkg.id));
|
|
11474
|
+
});
|
|
11475
|
+
const unchanged = Effect.fn("VpEnv.unchanged")(function* (pkg) {
|
|
11476
|
+
const current = yield* observe(pkg);
|
|
11477
|
+
if (Option.isNone(current) || current.value.current !== pkg.current || current.value.declaration?.selector !== pkg.declaration?.selector) return yield* new InstallRefused({
|
|
11478
|
+
package: pkg.name,
|
|
11479
|
+
reason: "the Vite+ default changed; refresh before updating"
|
|
11480
|
+
});
|
|
11481
|
+
});
|
|
11482
|
+
return {
|
|
11483
|
+
name: "vp-env",
|
|
11484
|
+
mode: "system",
|
|
11485
|
+
list,
|
|
11486
|
+
observe,
|
|
11487
|
+
update: Effect.fn("VpEnv.update")(function* (pkg, target) {
|
|
11488
|
+
yield* exec.stdout(vp$1(["install", `${pkg.id}@${target}`]));
|
|
11489
|
+
yield* unchanged(pkg);
|
|
11490
|
+
yield* exec.stdout(vp$1(["default", `${pkg.id}@${target}`]));
|
|
11491
|
+
})
|
|
11492
|
+
};
|
|
11493
|
+
});
|
|
11248
11494
|
// src/node/VpSelf.ts
|
|
11249
11495
|
const vp = (args) => ({
|
|
11250
11496
|
command: "vp",
|
|
@@ -11272,6 +11518,10 @@ const make$3 = Effect.gen(function* () {
|
|
|
11272
11518
|
mode: "system",
|
|
11273
11519
|
list: Effect.gen(function* () {
|
|
11274
11520
|
const output = yield* exec.stdout(check$1);
|
|
11521
|
+
if (output.includes("Homebrew manages this installation.")) return {
|
|
11522
|
+
packages: [],
|
|
11523
|
+
notices: []
|
|
11524
|
+
};
|
|
11275
11525
|
const { latest, current } = /found vite-plus@(?<latest>\S+) \(current: (?<current>\S+)\)/u.exec(output)?.groups ?? {};
|
|
11276
11526
|
if (latest === void 0 || current === void 0) return yield* malformed(check$1);
|
|
11277
11527
|
const pkg = vitePlus(current);
|
|
@@ -11347,7 +11597,7 @@ const make$2 = Effect.gen(function* () {
|
|
|
11347
11597
|
exec.stdout(command$1("apt", ["list", "--upgradable"])).pipe(Effect.map(parseUpgradable)),
|
|
11348
11598
|
exec.stdout(command$1("apt-mark", ["showhold"])).pipe(Effect.map((text) => new Set(text.split("\n").filter((line) => line !== "")))),
|
|
11349
11599
|
restart
|
|
11350
|
-
], { concurrency:
|
|
11600
|
+
], { concurrency: "unbounded" });
|
|
11351
11601
|
const packages = upgradable.map((entry) => {
|
|
11352
11602
|
const pkg = packageOf(entry, arch);
|
|
11353
11603
|
return holds.has(entry.name) || holds.has(pkg.id) ? pkg : withTarget(pkg, entry.version);
|
|
@@ -11492,7 +11742,7 @@ const make$1 = Effect.gen(function* () {
|
|
|
11492
11742
|
running,
|
|
11493
11743
|
scan(args),
|
|
11494
11744
|
Ref.get(installed)
|
|
11495
|
-
], { concurrency:
|
|
11745
|
+
], { concurrency: "unbounded" });
|
|
11496
11746
|
return {
|
|
11497
11747
|
packages: packagesOf$1(version, offers.filter((offer) => !done.has(offer.label))),
|
|
11498
11748
|
done
|
|
@@ -11577,7 +11827,7 @@ const make = Effect.gen(function* () {
|
|
|
11577
11827
|
"list",
|
|
11578
11828
|
"--outdated",
|
|
11579
11829
|
"--show-python"
|
|
11580
|
-
])).pipe(Effect.map(parseTools))], { concurrency:
|
|
11830
|
+
])).pipe(Effect.map(parseTools))], { concurrency: "unbounded" });
|
|
11581
11831
|
const targets = new Map(outdated.flatMap((tool) => tool.fromPyPI && tool.latest !== void 0 ? [[tool.pkg.id, tool.latest]] : []));
|
|
11582
11832
|
const index = privatePypiIndex();
|
|
11583
11833
|
return {
|
|
@@ -11615,18 +11865,19 @@ const make = Effect.gen(function* () {
|
|
|
11615
11865
|
const system$1 = [
|
|
11616
11866
|
["softwareupdate", make$1],
|
|
11617
11867
|
["apt-get", make$2],
|
|
11618
|
-
["brew", make$
|
|
11619
|
-
["codesign", make$
|
|
11868
|
+
["brew", make$14],
|
|
11869
|
+
["codesign", make$11],
|
|
11620
11870
|
["npm", makeNpm],
|
|
11621
11871
|
["pnpm", makePnpm],
|
|
11622
11872
|
["vp", makeVp],
|
|
11623
11873
|
["vp", make$3],
|
|
11624
|
-
["
|
|
11625
|
-
["
|
|
11874
|
+
["vp", make$4],
|
|
11875
|
+
["cargo", make$10],
|
|
11876
|
+
["go", make$8],
|
|
11626
11877
|
["uv", make]
|
|
11627
11878
|
];
|
|
11628
|
-
const available = (entries) => Effect.filter([...new Set(entries.map(([command]) => command))], onPath).pipe(Effect.map((found) => entries.filter(([command]) => found.includes(command))));
|
|
11629
|
-
const systemProviders = available(system$1).pipe(Effect.flatMap((found) => Effect.forEach(found, ([, make]) => make)));
|
|
11879
|
+
const available = (entries) => Effect.filter([...new Set(entries.map(([command]) => command))], onPath, { concurrency: "unbounded" }).pipe(Effect.map((found) => entries.filter(([command]) => found.includes(command))));
|
|
11880
|
+
const systemProviders = available(system$1).pipe(Effect.flatMap((found) => Effect.forEach(found, ([, make]) => make, { concurrency: "unbounded" })));
|
|
11630
11881
|
const project$1 = [
|
|
11631
11882
|
["go", {
|
|
11632
11883
|
manifests: ["go.mod"],
|
|
@@ -11652,9 +11903,8 @@ const project$1 = [
|
|
|
11652
11903
|
const manifests = project$1.flatMap(([, kind]) => kind.manifests);
|
|
11653
11904
|
const projectProviders = (given) => Effect.gen(function* () {
|
|
11654
11905
|
const dir = yield* (yield* FileSystem.FileSystem).realPath(given).pipe(Effect.orElseSucceed(() => given));
|
|
11655
|
-
const found = yield* available(project$1);
|
|
11656
|
-
|
|
11657
|
-
return (yield* Effect.forEach(found, ([, kind]) => kind.discover(dir, [...new Set(kind.manifests.flatMap(nested))]))).flat();
|
|
11906
|
+
const [found, nested] = yield* Effect.all([available(project$1), nestedDirs(dir, manifests)], { concurrency: "unbounded" });
|
|
11907
|
+
return (yield* Effect.forEach(found, ([, kind]) => kind.discover(dir, [...new Set(kind.manifests.flatMap(nested))]), { concurrency: "unbounded" })).flat();
|
|
11658
11908
|
});
|
|
11659
11909
|
const layer$3 = Layer.effect(ProviderSource, Effect.gen(function* () {
|
|
11660
11910
|
const context = yield* Effect.context();
|
|
@@ -11764,7 +12014,7 @@ const message = (version) => {
|
|
|
11764
12014
|
};
|
|
11765
12015
|
const lookup$1 = Effect.fn("npm.lookup")(function* (client, wanted) {
|
|
11766
12016
|
const path = wanted.name.replace("/", "%2F");
|
|
11767
|
-
const [packument, downloads] = yield* Effect.all([getJson(client, `https://registry.npmjs.org/${path}`, Packument), getJson(client, `https://api.npmjs.org/downloads/point/last-week/${wanted.name}`, Downloads).pipe(Effect.option)], { concurrency:
|
|
12017
|
+
const [packument, downloads] = yield* Effect.all([getJson(client, `https://registry.npmjs.org/${path}`, Packument), getJson(client, `https://api.npmjs.org/downloads/point/last-week/${wanted.name}`, Downloads).pipe(Effect.option)], { concurrency: "unbounded" });
|
|
11768
12018
|
const versions = packument.versions ?? {};
|
|
11769
12019
|
const latest = packument["dist-tags"]?.latest;
|
|
11770
12020
|
const repository = Predicate.isString(packument.repository) ? packument.repository : packument.repository?.url;
|
|
@@ -11804,7 +12054,7 @@ const repositoryKeys = [
|
|
|
11804
12054
|
"GitHub"
|
|
11805
12055
|
];
|
|
11806
12056
|
const lookup = Effect.fn("pypi.lookup")(function* (client, wanted) {
|
|
11807
|
-
const [project, recent] = yield* Effect.all([getJson(client, `https://pypi.org/pypi/${wanted.name}/json`, Project), getJson(client, `https://pypistats.org/api/packages/${wanted.name.toLowerCase()}/recent`, Recent).pipe(Effect.option)], { concurrency:
|
|
12057
|
+
const [project, recent] = yield* Effect.all([getJson(client, `https://pypi.org/pypi/${wanted.name}/json`, Project), getJson(client, `https://pypistats.org/api/packages/${wanted.name.toLowerCase()}/recent`, Recent).pipe(Effect.option)], { concurrency: "unbounded" });
|
|
11808
12058
|
const urls = project.info.project_urls ?? {};
|
|
11809
12059
|
const repository = repositoryKeys.map((key) => urls[key]).find((url) => url !== void 0);
|
|
11810
12060
|
const homepage = urls["Homepage"] ?? project.info.home_page ?? void 0;
|
|
@@ -12398,12 +12648,10 @@ const layer$1 = (cacheDir) => Layer.succeed(UpdateLog, UpdateLog.of({
|
|
|
12398
12648
|
const directory = path.join(cacheDir, "updates");
|
|
12399
12649
|
const names = (await readdir(directory).catch(() => [])).filter((name) => name.endsWith(".jsonl")).toSorted();
|
|
12400
12650
|
const found = [];
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
if (entry?.scope === scope && Date.parse(entry.at) >= since) found.push(entry);
|
|
12406
|
-
}
|
|
12651
|
+
const texts = await Promise.all(names.map(async (name) => await readFile(path.join(directory, name), "utf-8").catch(() => "")));
|
|
12652
|
+
for (const text of texts) for (const line of text.split("\n")) {
|
|
12653
|
+
const entry = Option.getOrUndefined(decodeLine(line));
|
|
12654
|
+
if (entry?.scope === scope && Date.parse(entry.at) >= since) found.push(entry);
|
|
12407
12655
|
}
|
|
12408
12656
|
return found;
|
|
12409
12657
|
})
|
|
@@ -12491,18 +12739,17 @@ const runProvider = Effect.fn("Engine.runProvider")(function* (provider, targets
|
|
|
12491
12739
|
key: rowKey(target.pkg),
|
|
12492
12740
|
outcome
|
|
12493
12741
|
}))));
|
|
12494
|
-
const
|
|
12742
|
+
const applied = [];
|
|
12495
12743
|
for (const target of targets) {
|
|
12496
12744
|
if (yield* stopped) {
|
|
12497
12745
|
yield* finish(target, Outcome.NotStarted());
|
|
12498
12746
|
continue;
|
|
12499
12747
|
}
|
|
12500
|
-
const
|
|
12501
|
-
if (Result.isFailure(
|
|
12502
|
-
|
|
12503
|
-
|
|
12504
|
-
|
|
12505
|
-
for (const [target, attempt] of checked) {
|
|
12748
|
+
const checked = yield* check(target, emit);
|
|
12749
|
+
if (Result.isFailure(checked)) {
|
|
12750
|
+
yield* finish(target, checked.failure);
|
|
12751
|
+
continue;
|
|
12752
|
+
}
|
|
12506
12753
|
if (yield* stopped) {
|
|
12507
12754
|
yield* finish(target, Outcome.NotStarted());
|
|
12508
12755
|
continue;
|
|
@@ -12511,7 +12758,7 @@ const runProvider = Effect.fn("Engine.runProvider")(function* (provider, targets
|
|
|
12511
12758
|
key: rowKey(target.pkg),
|
|
12512
12759
|
phase: "applying"
|
|
12513
12760
|
}));
|
|
12514
|
-
const result = yield* Effect.result(withDecisions(
|
|
12761
|
+
const result = yield* Effect.result(withDecisions(checked.success));
|
|
12515
12762
|
if (Result.isFailure(result)) yield* finish(target, failedAs(result.failure, false));
|
|
12516
12763
|
else if (provider.reconcile === void 0) yield* finish(target, yield* verify(target, emit));
|
|
12517
12764
|
else applied.push(target);
|
|
@@ -12656,15 +12903,236 @@ const updateTools = (home) => Action.implement(UpdateActions, Effect.gen(functio
|
|
|
12656
12903
|
}));
|
|
12657
12904
|
// src/mcp.ts
|
|
12658
12905
|
const instructions = "upup reads the tools installed on this machine and the registry dependencies of a project, audits offered updates with the user's chosen model, and applies or removes them. Start with list_packages; every other tool takes its package keys. Before updating, look at package_changes or run audit_packages and poll audit_reports until no Audit is running; operating-system updates (ecosystem macos or apt) can't be audited and have no changes to read; app updates (ecosystem sparkle, electron, appstore or app) can't be audited either, and package_changes gives their release notes when the app's feed has any; ask the user to quit an open app before updating it; the Node.js versions a project pins (ecosystem node) can't be audited either and are edited in place. update_packages and remove_packages change the user's machine or project and cannot be undone by upup: confirm them with the user, and ask before passing allowScripts.";
|
|
12659
|
-
const
|
|
12906
|
+
const tools = (home) => [
|
|
12660
12907
|
inventoryTools(home),
|
|
12661
12908
|
auditTools(home),
|
|
12662
12909
|
updateTools(home)
|
|
12663
|
-
]
|
|
12910
|
+
];
|
|
12911
|
+
const serverInfo = {
|
|
12664
12912
|
name: "upup",
|
|
12665
12913
|
version: version$1,
|
|
12666
12914
|
instructions
|
|
12915
|
+
};
|
|
12916
|
+
const serve = (home) => ActionMcp.runStdio(tools(home), serverInfo);
|
|
12917
|
+
// src/session/operation.ts
|
|
12918
|
+
const machine$1 = setup({
|
|
12919
|
+
schemas: { events: {
|
|
12920
|
+
Run: types(),
|
|
12921
|
+
Stop: types(),
|
|
12922
|
+
Ask: types(),
|
|
12923
|
+
Answer: types(),
|
|
12924
|
+
Finish: types()
|
|
12925
|
+
} },
|
|
12926
|
+
states: {
|
|
12927
|
+
Idle: { schemas: { context: types() } },
|
|
12928
|
+
Running: { schemas: { context: types() } },
|
|
12929
|
+
Asking: { schemas: { context: types() } }
|
|
12930
|
+
}
|
|
12931
|
+
}).createFSM({
|
|
12932
|
+
initial: "Idle",
|
|
12933
|
+
context: {
|
|
12934
|
+
stopping: false,
|
|
12935
|
+
decision: void 0
|
|
12936
|
+
},
|
|
12937
|
+
states: {
|
|
12938
|
+
Idle: { on: { Run: { target: "Running" } } },
|
|
12939
|
+
Running: { on: {
|
|
12940
|
+
Stop: { context: { stopping: true } },
|
|
12941
|
+
Ask: ({ event }) => ({
|
|
12942
|
+
target: "Asking",
|
|
12943
|
+
context: { decision: event.decision }
|
|
12944
|
+
}),
|
|
12945
|
+
Finish: {
|
|
12946
|
+
target: "Idle",
|
|
12947
|
+
context: { stopping: false }
|
|
12948
|
+
}
|
|
12949
|
+
} },
|
|
12950
|
+
Asking: { on: {
|
|
12951
|
+
Stop: { context: { stopping: true } },
|
|
12952
|
+
Answer: {
|
|
12953
|
+
target: "Running",
|
|
12954
|
+
context: { decision: void 0 }
|
|
12955
|
+
},
|
|
12956
|
+
Finish: {
|
|
12957
|
+
target: "Idle",
|
|
12958
|
+
context: {
|
|
12959
|
+
stopping: false,
|
|
12960
|
+
decision: void 0
|
|
12961
|
+
}
|
|
12962
|
+
}
|
|
12963
|
+
} }
|
|
12964
|
+
}
|
|
12965
|
+
});
|
|
12966
|
+
const step$1 = (snapshot, event) => {
|
|
12967
|
+
const [next] = machine$1.transition(snapshot, event);
|
|
12968
|
+
return [next, next !== snapshot];
|
|
12969
|
+
};
|
|
12970
|
+
// src/session/sign-in.ts
|
|
12971
|
+
const machine = setup({
|
|
12972
|
+
schemas: { events: {
|
|
12973
|
+
SaveKey: types(),
|
|
12974
|
+
Start: types(),
|
|
12975
|
+
Noticed: types(),
|
|
12976
|
+
Prompt: types(),
|
|
12977
|
+
Answer: types(),
|
|
12978
|
+
Succeed: types(),
|
|
12979
|
+
Fail: types(),
|
|
12980
|
+
Close: types()
|
|
12981
|
+
} },
|
|
12982
|
+
states: {
|
|
12983
|
+
Idle: { schemas: { context: types() } },
|
|
12984
|
+
SavingKey: { schemas: { context: types() } },
|
|
12985
|
+
Working: { schemas: { context: types() } },
|
|
12986
|
+
Prompted: { schemas: { context: types() } },
|
|
12987
|
+
Failed: { schemas: { context: types() } },
|
|
12988
|
+
SignedIn: { schemas: { context: types() } }
|
|
12989
|
+
}
|
|
12990
|
+
}).createFSM({
|
|
12991
|
+
initial: "Idle",
|
|
12992
|
+
context: {
|
|
12993
|
+
attempt: 0,
|
|
12994
|
+
provider: void 0,
|
|
12995
|
+
notices: void 0,
|
|
12996
|
+
prompt: void 0,
|
|
12997
|
+
issue: void 0
|
|
12998
|
+
},
|
|
12999
|
+
states: {
|
|
13000
|
+
Idle: { on: {
|
|
13001
|
+
SaveKey: ({ context, event }) => ({
|
|
13002
|
+
target: "SavingKey",
|
|
13003
|
+
context: {
|
|
13004
|
+
attempt: context.attempt + 1,
|
|
13005
|
+
provider: event.provider
|
|
13006
|
+
}
|
|
13007
|
+
}),
|
|
13008
|
+
Start: ({ context, event }) => ({
|
|
13009
|
+
target: "Working",
|
|
13010
|
+
context: {
|
|
13011
|
+
attempt: context.attempt + 1,
|
|
13012
|
+
provider: event.provider,
|
|
13013
|
+
notices: []
|
|
13014
|
+
}
|
|
13015
|
+
})
|
|
13016
|
+
} },
|
|
13017
|
+
SavingKey: { on: {
|
|
13018
|
+
Succeed: ({ context, event }) => event.attempt === context.attempt ? { target: "SignedIn" } : void 0,
|
|
13019
|
+
Fail: ({ context, event }) => event.attempt === context.attempt ? {
|
|
13020
|
+
target: "Failed",
|
|
13021
|
+
context: { issue: event.issue }
|
|
13022
|
+
} : void 0,
|
|
13023
|
+
Close: {
|
|
13024
|
+
target: "Idle",
|
|
13025
|
+
context: { provider: void 0 }
|
|
13026
|
+
}
|
|
13027
|
+
} },
|
|
13028
|
+
Working: { on: {
|
|
13029
|
+
Noticed: ({ context, event }) => event.attempt === context.attempt ? { context: { notices: [...context.notices, event.notice] } } : void 0,
|
|
13030
|
+
Prompt: ({ context, event }) => event.attempt === context.attempt ? {
|
|
13031
|
+
target: "Prompted",
|
|
13032
|
+
context: { prompt: event.prompt }
|
|
13033
|
+
} : void 0,
|
|
13034
|
+
Succeed: ({ context, event }) => event.attempt === context.attempt ? {
|
|
13035
|
+
target: "SignedIn",
|
|
13036
|
+
context: { notices: void 0 }
|
|
13037
|
+
} : void 0,
|
|
13038
|
+
Fail: ({ context, event }) => event.attempt === context.attempt ? {
|
|
13039
|
+
target: "Failed",
|
|
13040
|
+
context: {
|
|
13041
|
+
notices: void 0,
|
|
13042
|
+
issue: event.issue
|
|
13043
|
+
}
|
|
13044
|
+
} : void 0,
|
|
13045
|
+
Close: {
|
|
13046
|
+
target: "Idle",
|
|
13047
|
+
context: {
|
|
13048
|
+
provider: void 0,
|
|
13049
|
+
notices: void 0
|
|
13050
|
+
}
|
|
13051
|
+
}
|
|
13052
|
+
} },
|
|
13053
|
+
Prompted: { on: {
|
|
13054
|
+
Noticed: ({ context, event }) => event.attempt === context.attempt ? { context: { notices: [...context.notices, event.notice] } } : void 0,
|
|
13055
|
+
Answer: ({ context, event }) => event.attempt === context.attempt ? {
|
|
13056
|
+
target: "Working",
|
|
13057
|
+
context: { prompt: void 0 }
|
|
13058
|
+
} : void 0,
|
|
13059
|
+
Succeed: ({ context, event }) => event.attempt === context.attempt ? {
|
|
13060
|
+
target: "SignedIn",
|
|
13061
|
+
context: {
|
|
13062
|
+
notices: void 0,
|
|
13063
|
+
prompt: void 0
|
|
13064
|
+
}
|
|
13065
|
+
} : void 0,
|
|
13066
|
+
Fail: ({ context, event }) => event.attempt === context.attempt ? {
|
|
13067
|
+
target: "Failed",
|
|
13068
|
+
context: {
|
|
13069
|
+
notices: void 0,
|
|
13070
|
+
prompt: void 0,
|
|
13071
|
+
issue: event.issue
|
|
13072
|
+
}
|
|
13073
|
+
} : void 0,
|
|
13074
|
+
Close: {
|
|
13075
|
+
target: "Idle",
|
|
13076
|
+
context: {
|
|
13077
|
+
provider: void 0,
|
|
13078
|
+
notices: void 0,
|
|
13079
|
+
prompt: void 0
|
|
13080
|
+
}
|
|
13081
|
+
}
|
|
13082
|
+
} },
|
|
13083
|
+
Failed: { on: {
|
|
13084
|
+
SaveKey: ({ context, event }) => ({
|
|
13085
|
+
target: "SavingKey",
|
|
13086
|
+
context: {
|
|
13087
|
+
attempt: context.attempt + 1,
|
|
13088
|
+
provider: event.provider,
|
|
13089
|
+
issue: void 0
|
|
13090
|
+
}
|
|
13091
|
+
}),
|
|
13092
|
+
Start: ({ context, event }) => ({
|
|
13093
|
+
target: "Working",
|
|
13094
|
+
context: {
|
|
13095
|
+
attempt: context.attempt + 1,
|
|
13096
|
+
provider: event.provider,
|
|
13097
|
+
notices: [],
|
|
13098
|
+
issue: void 0
|
|
13099
|
+
}
|
|
13100
|
+
}),
|
|
13101
|
+
Close: {
|
|
13102
|
+
target: "Idle",
|
|
13103
|
+
context: {
|
|
13104
|
+
provider: void 0,
|
|
13105
|
+
issue: void 0
|
|
13106
|
+
}
|
|
13107
|
+
}
|
|
13108
|
+
} },
|
|
13109
|
+
SignedIn: { on: {
|
|
13110
|
+
SaveKey: ({ context, event }) => ({
|
|
13111
|
+
target: "SavingKey",
|
|
13112
|
+
context: {
|
|
13113
|
+
attempt: context.attempt + 1,
|
|
13114
|
+
provider: event.provider
|
|
13115
|
+
}
|
|
13116
|
+
}),
|
|
13117
|
+
Start: ({ context, event }) => ({
|
|
13118
|
+
target: "Working",
|
|
13119
|
+
context: {
|
|
13120
|
+
attempt: context.attempt + 1,
|
|
13121
|
+
provider: event.provider,
|
|
13122
|
+
notices: []
|
|
13123
|
+
}
|
|
13124
|
+
}),
|
|
13125
|
+
Close: {
|
|
13126
|
+
target: "Idle",
|
|
13127
|
+
context: { provider: void 0 }
|
|
13128
|
+
}
|
|
13129
|
+
} }
|
|
13130
|
+
}
|
|
12667
13131
|
});
|
|
13132
|
+
const step = (snapshot, event) => {
|
|
13133
|
+
const [next] = machine.transition(snapshot, event);
|
|
13134
|
+
return [next, next !== snapshot];
|
|
13135
|
+
};
|
|
12668
13136
|
// src/session/Session.ts
|
|
12669
13137
|
const aboutKey = (pkg) => `${rowKey(pkg)}@${pkg.current}..${pkg.target ?? ""}`;
|
|
12670
13138
|
const auditKey = (pkg, mode) => {
|
|
@@ -12677,13 +13145,14 @@ const questionOf = (state) => {
|
|
|
12677
13145
|
_tag: "Password",
|
|
12678
13146
|
request: state.password
|
|
12679
13147
|
};
|
|
12680
|
-
if (state.
|
|
13148
|
+
if (state.operation.value !== "Asking") return;
|
|
13149
|
+
const { decision } = state.operation.context;
|
|
13150
|
+
return decision._tag === "ScriptsBlocked" ? {
|
|
12681
13151
|
_tag: "Scripts",
|
|
12682
|
-
blocked:
|
|
12683
|
-
}
|
|
12684
|
-
if (state.decision?._tag === "AppRunning") return {
|
|
13152
|
+
blocked: decision
|
|
13153
|
+
} : {
|
|
12685
13154
|
_tag: "Quit",
|
|
12686
|
-
running:
|
|
13155
|
+
running: decision
|
|
12687
13156
|
};
|
|
12688
13157
|
};
|
|
12689
13158
|
const initial = (mode, scope, preferences, audits = {}, settingsIssue) => ({
|
|
@@ -12694,14 +13163,12 @@ const initial = (mode, scope, preferences, audits = {}, settingsIssue) => ({
|
|
|
12694
13163
|
discoveryError: void 0,
|
|
12695
13164
|
providers: [],
|
|
12696
13165
|
selected: [],
|
|
12697
|
-
|
|
12698
|
-
stopping: false,
|
|
13166
|
+
operation: machine$1.initialState,
|
|
12699
13167
|
activity: {},
|
|
12700
13168
|
outcomes: {},
|
|
12701
13169
|
actions: {},
|
|
12702
13170
|
installed: {},
|
|
12703
13171
|
removed: {},
|
|
12704
|
-
decision: void 0,
|
|
12705
13172
|
password: void 0,
|
|
12706
13173
|
about: {},
|
|
12707
13174
|
audits,
|
|
@@ -12711,7 +13178,7 @@ const initial = (mode, scope, preferences, audits = {}, settingsIssue) => ({
|
|
|
12711
13178
|
preferences,
|
|
12712
13179
|
settingsIssue,
|
|
12713
13180
|
catalogue: void 0,
|
|
12714
|
-
signIn:
|
|
13181
|
+
signIn: machine.initialState
|
|
12715
13182
|
});
|
|
12716
13183
|
const issueOf = (error) => `${error.file} cannot be read (${error.issue}); upup keeps audit first on and changes no settings until it is fixed or deleted`;
|
|
12717
13184
|
const without = (record, key) => Object.fromEntries(Object.entries(record).filter(([entry]) => entry !== key));
|
|
@@ -12822,9 +13289,15 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
12822
13289
|
const providers = yield* Ref.make([]);
|
|
12823
13290
|
const loading = yield* FiberHandle.make();
|
|
12824
13291
|
const operation = yield* FiberHandle.make();
|
|
12825
|
-
const stopRequested = yield* Ref.make(false);
|
|
12826
13292
|
const pendingDecision = yield* Ref.make(Option.none());
|
|
12827
13293
|
const update = (f) => SubscriptionRef.update(state, f);
|
|
13294
|
+
const send = (event) => SubscriptionRef.modify(state, (current) => {
|
|
13295
|
+
const [next, handled] = step$1(current.operation, event);
|
|
13296
|
+
return [handled, handled ? {
|
|
13297
|
+
...current,
|
|
13298
|
+
operation: next
|
|
13299
|
+
} : current];
|
|
13300
|
+
});
|
|
12828
13301
|
const setProvider = (name, f) => update((current) => ({
|
|
12829
13302
|
...current,
|
|
12830
13303
|
providers: current.providers.map((entry) => entry.name === name ? f(entry) : entry)
|
|
@@ -12922,8 +13395,8 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
12922
13395
|
};
|
|
12923
13396
|
});
|
|
12924
13397
|
const recallAll = Effect.gen(function* () {
|
|
12925
|
-
const
|
|
12926
|
-
const logged = yield*
|
|
13398
|
+
const before = yield* SubscriptionRef.get(state);
|
|
13399
|
+
const [found, logged] = yield* Effect.all([earlier(before), fromLog(before)], { concurrency: "unbounded" });
|
|
12927
13400
|
yield* update((latest) => ({
|
|
12928
13401
|
...latest,
|
|
12929
13402
|
installed: {
|
|
@@ -12942,15 +13415,14 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
12942
13415
|
const identity = identityOf(pkg, current.mode);
|
|
12943
13416
|
if (identity !== void 0 && current.audits[jobKey(identity)]?._tag !== "Running") wanted.set(jobKey(identity), identity);
|
|
12944
13417
|
}
|
|
12945
|
-
yield* Effect.forEach(wanted.values(), audits.recall, {
|
|
12946
|
-
concurrency: 4,
|
|
12947
|
-
discard: true
|
|
12948
|
-
});
|
|
12949
13418
|
const offered = packagesOf(current).flatMap((pkg) => {
|
|
12950
13419
|
const identity = pkg.target === void 0 ? void 0 : identityOf(pkg, current.mode);
|
|
12951
13420
|
return identity === void 0 ? [] : [identity];
|
|
12952
13421
|
});
|
|
12953
|
-
const majors = yield* Effect.
|
|
13422
|
+
const [, majors] = yield* Effect.all([Effect.forEach(wanted.values(), audits.recall, {
|
|
13423
|
+
concurrency: 4,
|
|
13424
|
+
discard: true
|
|
13425
|
+
}), Effect.forEach(offered, (identity) => audits.majors(identity).pipe(Effect.map((identityMajors) => [jobKey(identity), identityMajors])), { concurrency: 4 })], { concurrency: "unbounded" });
|
|
12954
13426
|
yield* update((latest) => ({
|
|
12955
13427
|
...latest,
|
|
12956
13428
|
majors: {
|
|
@@ -12995,11 +13467,12 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
12995
13467
|
return {
|
|
12996
13468
|
...initial(mode, scope, current.preferences, current.audits, current.settingsIssue),
|
|
12997
13469
|
...places.get(`${mode}:${scope}`),
|
|
12998
|
-
catalogue: current.catalogue
|
|
13470
|
+
catalogue: current.catalogue,
|
|
13471
|
+
signIn: current.signIn
|
|
12999
13472
|
};
|
|
13000
13473
|
});
|
|
13001
13474
|
const open = Effect.fn("Session.open")(function* (mode, scope) {
|
|
13002
|
-
if ((yield* SubscriptionRef.get(state)).
|
|
13475
|
+
if ((yield* SubscriptionRef.get(state)).operation.value !== "Idle") return;
|
|
13003
13476
|
yield* FiberHandle.clear(loading);
|
|
13004
13477
|
yield* reset(mode, scope);
|
|
13005
13478
|
yield* FiberHandle.run(loading, discover(mode, scope));
|
|
@@ -13016,7 +13489,7 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13016
13489
|
});
|
|
13017
13490
|
const refresh = Effect.gen(function* () {
|
|
13018
13491
|
const current = yield* SubscriptionRef.get(state);
|
|
13019
|
-
if (current.
|
|
13492
|
+
if (current.operation.value !== "Idle" || current.discovering) return;
|
|
13020
13493
|
yield* FiberHandle.run(loading, discover(current.mode, current.scope));
|
|
13021
13494
|
});
|
|
13022
13495
|
const toggle = Effect.fn("Session.toggle")(function* (key) {
|
|
@@ -13038,16 +13511,11 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13038
13511
|
const ask = (decision) => Effect.gen(function* () {
|
|
13039
13512
|
const answer = yield* Deferred.make();
|
|
13040
13513
|
yield* Ref.set(pendingDecision, Option.some(answer));
|
|
13041
|
-
yield*
|
|
13042
|
-
|
|
13514
|
+
yield* send({
|
|
13515
|
+
type: "Ask",
|
|
13043
13516
|
decision
|
|
13044
|
-
})
|
|
13045
|
-
|
|
13046
|
-
yield* update((current) => ({
|
|
13047
|
-
...current,
|
|
13048
|
-
decision: void 0
|
|
13049
|
-
}));
|
|
13050
|
-
return allowed;
|
|
13517
|
+
});
|
|
13518
|
+
return yield* Deferred.await(answer);
|
|
13051
13519
|
});
|
|
13052
13520
|
const decisions = Decisions.of({
|
|
13053
13521
|
asks: true,
|
|
@@ -13056,16 +13524,9 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13056
13524
|
});
|
|
13057
13525
|
const run = Effect.fn("Session.run")(function* (action, keys, accepted = []) {
|
|
13058
13526
|
const current = yield* SubscriptionRef.get(state);
|
|
13059
|
-
if (current.running) return;
|
|
13060
13527
|
const targets = targetsFor(current, yield* Ref.get(providers), action, keys);
|
|
13061
|
-
if (targets.length === 0) return;
|
|
13062
|
-
yield* Ref.set(stopRequested, false);
|
|
13528
|
+
if (targets.length === 0 || !(yield* send({ type: "Run" }))) return;
|
|
13063
13529
|
const vetting = action === "update" && current.preferences.auditFirst;
|
|
13064
|
-
yield* update((latest) => ({
|
|
13065
|
-
...latest,
|
|
13066
|
-
running: true,
|
|
13067
|
-
stopping: false
|
|
13068
|
-
}));
|
|
13069
13530
|
const touched = [...new Set(targets.map((target) => target.provider))];
|
|
13070
13531
|
const confirmed = targets.filter((target) => accepted.includes(rowKey(target.pkg)));
|
|
13071
13532
|
const chosen = vetting ? vet(targets.filter((target) => !accepted.includes(rowKey(target.pkg))), current.mode).pipe(Effect.flatMap(({ approved, refused }) => update((latest) => ({
|
|
@@ -13085,7 +13546,7 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13085
13546
|
...latest.activity,
|
|
13086
13547
|
...Object.fromEntries(approved.map((target) => [rowKey(target.pkg), "waiting"]))
|
|
13087
13548
|
}
|
|
13088
|
-
}))), Effect.flatMap((approved) => run$1(approved,
|
|
13549
|
+
}))), Effect.flatMap((approved) => run$1(approved, SubscriptionRef.get(state).pipe(Effect.map((latest) => latest.operation.context.stopping))).pipe(Stream.runForEach((event) => update((latest) => event._tag === "Progress" ? {
|
|
13089
13550
|
...latest,
|
|
13090
13551
|
activity: {
|
|
13091
13552
|
...latest.activity,
|
|
@@ -13107,21 +13568,14 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13107
13568
|
selected: event.outcome._tag === "Completed" ? latest.selected.filter((key) => key !== event.key) : latest.selected
|
|
13108
13569
|
})), Effect.provideService(Decisions, decisions), Effect.provideService(UpdateLog, scoped(updateLog, placeOf(current))), Effect.andThen(collect$2(touched)), Effect.andThen(recallAll))), Effect.ensuring(Ref.set(pendingDecision, Option.none()).pipe(Effect.andThen(update((latest) => ({
|
|
13109
13570
|
...latest,
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
activity: {},
|
|
13113
|
-
decision: void 0
|
|
13571
|
+
operation: step$1(latest.operation, { type: "Finish" })[0],
|
|
13572
|
+
activity: {}
|
|
13114
13573
|
})))))));
|
|
13115
13574
|
});
|
|
13116
13575
|
const answerPassword = (password) => sudo.answer(password);
|
|
13117
|
-
const stop = Effect.
|
|
13118
|
-
yield* Ref.set(stopRequested, true);
|
|
13119
|
-
yield* update((current) => current.running ? {
|
|
13120
|
-
...current,
|
|
13121
|
-
stopping: true
|
|
13122
|
-
} : current);
|
|
13123
|
-
});
|
|
13576
|
+
const stop = Effect.asVoid(send({ type: "Stop" }));
|
|
13124
13577
|
const answer = Effect.fn("Session.answer")(function* (allowed) {
|
|
13578
|
+
if (!(yield* send({ type: "Answer" }))) return;
|
|
13125
13579
|
const pending = yield* Ref.getAndSet(pendingDecision, Option.none());
|
|
13126
13580
|
if (Option.isSome(pending)) yield* Deferred.succeed(pending.value, allowed);
|
|
13127
13581
|
});
|
|
@@ -13178,73 +13632,86 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13178
13632
|
...stored,
|
|
13179
13633
|
auditFirst: on
|
|
13180
13634
|
}));
|
|
13181
|
-
const
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13635
|
+
const sendSignIn = (event) => SubscriptionRef.modify(state, (current) => {
|
|
13636
|
+
const stepped = step(current.signIn, event);
|
|
13637
|
+
const [next, handled] = stepped;
|
|
13638
|
+
return [stepped, handled ? {
|
|
13639
|
+
...current,
|
|
13640
|
+
signIn: next
|
|
13641
|
+
} : current];
|
|
13642
|
+
});
|
|
13643
|
+
const takeAnswer = (taken) => Ref.modify(pendingAnswer, (pending) => Option.isSome(pending) && taken(pending.value.attempt) ? [pending, Option.none()] : [Option.none(), pending]);
|
|
13644
|
+
const finish = (attempt) => Effect.matchEffect({
|
|
13645
|
+
onSuccess: () => sendSignIn({
|
|
13646
|
+
type: "Succeed",
|
|
13647
|
+
attempt
|
|
13648
|
+
}).pipe(Effect.andThen(loadCatalogue)),
|
|
13649
|
+
onFailure: (error) => sendSignIn({
|
|
13650
|
+
type: "Fail",
|
|
13651
|
+
attempt,
|
|
13194
13652
|
issue: error.issue
|
|
13195
13653
|
})
|
|
13196
|
-
})
|
|
13654
|
+
});
|
|
13655
|
+
const saveKey = (provider, key) => Effect.gen(function* () {
|
|
13656
|
+
const [saving, handled] = yield* sendSignIn({
|
|
13657
|
+
type: "SaveKey",
|
|
13658
|
+
provider
|
|
13659
|
+
});
|
|
13660
|
+
if (handled) yield* ai.setApiKey(provider, key).pipe(finish(saving.context.attempt));
|
|
13661
|
+
});
|
|
13197
13662
|
const signIn = (provider) => Effect.gen(function* () {
|
|
13198
|
-
yield*
|
|
13199
|
-
|
|
13200
|
-
provider
|
|
13201
|
-
notices: []
|
|
13663
|
+
const [working, handled] = yield* sendSignIn({
|
|
13664
|
+
type: "Start",
|
|
13665
|
+
provider
|
|
13202
13666
|
});
|
|
13667
|
+
if (!handled) return;
|
|
13668
|
+
const { attempt } = working.context;
|
|
13203
13669
|
const context = yield* Effect.context();
|
|
13204
|
-
const working = (change) => update((current) => current.signIn?._tag === "Working" && current.signIn.provider === provider ? {
|
|
13205
|
-
...current,
|
|
13206
|
-
signIn: change(current.signIn)
|
|
13207
|
-
} : current);
|
|
13208
13670
|
yield* FiberHandle.run(signing, ai.login(provider, {
|
|
13209
13671
|
notify: (event) => {
|
|
13210
|
-
|
|
13211
|
-
|
|
13212
|
-
|
|
13213
|
-
|
|
13214
|
-
}))
|
|
13672
|
+
Effect.runForkWith(context)(sendSignIn({
|
|
13673
|
+
type: "Noticed",
|
|
13674
|
+
attempt,
|
|
13675
|
+
notice: noticeOf(event)
|
|
13676
|
+
}));
|
|
13215
13677
|
},
|
|
13216
13678
|
prompt: async (prompt) => await Effect.runPromiseWith(context)(Effect.gen(function* () {
|
|
13217
13679
|
const reply = yield* Deferred.make();
|
|
13218
|
-
yield* Ref.
|
|
13219
|
-
|
|
13220
|
-
|
|
13680
|
+
const replaced = yield* Ref.modify(pendingAnswer, (pending) => Option.isSome(pending) && pending.value.attempt >= attempt ? [void 0, pending] : [pending, Option.some({
|
|
13681
|
+
attempt,
|
|
13682
|
+
reply
|
|
13683
|
+
})]);
|
|
13684
|
+
if (replaced === void 0) return yield* Effect.interrupt;
|
|
13685
|
+
if (Option.isSome(replaced)) yield* Deferred.interrupt(replaced.value.reply);
|
|
13686
|
+
const [, asked] = yield* sendSignIn({
|
|
13687
|
+
type: "Prompt",
|
|
13688
|
+
attempt,
|
|
13221
13689
|
prompt
|
|
13222
|
-
})
|
|
13690
|
+
});
|
|
13691
|
+
if (!asked) {
|
|
13692
|
+
yield* takeAnswer((stored) => stored === attempt);
|
|
13693
|
+
return yield* Effect.interrupt;
|
|
13694
|
+
}
|
|
13223
13695
|
return yield* Deferred.await(reply);
|
|
13224
13696
|
}))
|
|
13225
|
-
}).pipe(
|
|
13226
|
-
onSuccess: () => setSignIn(void 0).pipe(Effect.andThen(loadCatalogue)),
|
|
13227
|
-
onFailure: (error) => setSignIn({
|
|
13228
|
-
_tag: "Failed",
|
|
13229
|
-
provider,
|
|
13230
|
-
issue: error.issue
|
|
13231
|
-
})
|
|
13232
|
-
})));
|
|
13697
|
+
}).pipe(finish(attempt)));
|
|
13233
13698
|
});
|
|
13234
13699
|
const answerSignIn = (reply) => Effect.gen(function* () {
|
|
13235
|
-
const
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13242
|
-
|
|
13243
|
-
|
|
13244
|
-
|
|
13245
|
-
yield*
|
|
13700
|
+
const { attempt } = (yield* SubscriptionRef.get(state)).signIn.context;
|
|
13701
|
+
const [, handled] = yield* sendSignIn({
|
|
13702
|
+
type: "Answer",
|
|
13703
|
+
attempt
|
|
13704
|
+
});
|
|
13705
|
+
if (!handled) return;
|
|
13706
|
+
const pending = yield* takeAnswer((stored) => stored === attempt);
|
|
13707
|
+
if (Option.isSome(pending)) yield* Deferred.succeed(pending.value.reply, reply);
|
|
13708
|
+
});
|
|
13709
|
+
const closeSignIn = Effect.gen(function* () {
|
|
13710
|
+
const [closed] = yield* sendSignIn({ type: "Close" });
|
|
13711
|
+
yield* FiberHandle.clear(signing);
|
|
13712
|
+
const pending = yield* takeAnswer((stored) => stored <= closed.context.attempt);
|
|
13713
|
+
if (Option.isSome(pending)) yield* Deferred.interrupt(pending.value.reply);
|
|
13246
13714
|
});
|
|
13247
|
-
const closeSignIn = FiberHandle.clear(signing).pipe(Effect.andThen(Ref.set(pendingAnswer, Option.none())), Effect.andThen(setSignIn(void 0)));
|
|
13248
13715
|
const awaitIdle = FiberHandle.awaitEmpty(operation);
|
|
13249
13716
|
return Session.of({
|
|
13250
13717
|
changes: SubscriptionRef.changes(state),
|
|
@@ -13284,7 +13751,7 @@ const silenceNodeWarningsWhileTuiDraws = () => {
|
|
|
13284
13751
|
};
|
|
13285
13752
|
const loadTui = Effect.suspend(() => {
|
|
13286
13753
|
silenceNodeWarningsWhileTuiDraws();
|
|
13287
|
-
return process.getBuiltinModule("node:ffi") === void 0 ? Effect.die(`upup's TUI needs Node 26.
|
|
13754
|
+
return process.getBuiltinModule("node:ffi") === void 0 ? Effect.die(`upup's TUI needs Node 26.13 or newer; this is Node ${process.versions.node}`) : Effect.promise(async () => await import("./Tui-DV_6wbrQ.mjs"));
|
|
13288
13755
|
});
|
|
13289
13756
|
const open$1 = (begin, projectDir) => Effect.gen(function* () {
|
|
13290
13757
|
const { launch } = yield* loadTui;
|