@gjermundgaraba/upup 0.4.0 → 0.5.1
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 +1 -1
- package/dist/grep-worker.mjs +1 -1
- package/dist/{io-BGJ4Yp7u.mjs → io-ns6xhFdF.mjs} +5 -1
- package/dist/main.mjs +407 -361
- package/native/dist/upup-sparkle.json +1 -1
- package/native/dist/upup-sparkle.zip +0 -0
- package/package.json +17 -19
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,8 +17,7 @@ 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
22
|
import { ChildProcess, ChildProcessSpawner, CommandLine } from "effect/process";
|
|
24
23
|
import { Worker } from "node:worker_threads";
|
|
@@ -38,12 +37,11 @@ import { constVoid } from "effect/Function";
|
|
|
38
37
|
import { applyEdits, modify, parse as parse$1 } from "jsonc-parser";
|
|
39
38
|
import { getStaticTOMLValue, parseTOML, traverseNodes } from "toml-eslint-parser";
|
|
40
39
|
import { XMLParser } from "fast-xml-parser";
|
|
41
|
-
import { isSea } from "node:sea";
|
|
42
40
|
import * as ActionMcp from "@gjermundgaraba/effect-actions/ActionMcp";
|
|
43
41
|
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
44
42
|
import { setup, types } from "xstate/fsm";
|
|
45
43
|
// package.json
|
|
46
|
-
var version$1 = "0.
|
|
44
|
+
var version$1 = "0.5.1";
|
|
47
45
|
// src/ai/Setup.ts
|
|
48
46
|
const Thinking = Schema.Literals([
|
|
49
47
|
"off",
|
|
@@ -427,12 +425,12 @@ const withTarget = (pkg, target) => ({
|
|
|
427
425
|
target
|
|
428
426
|
});
|
|
429
427
|
const isUnaudited = Schema.is(UnauditedEcosystem);
|
|
430
|
-
const isApp = Schema.is(AppEcosystem);
|
|
428
|
+
const isApp$1 = Schema.is(AppEcosystem);
|
|
431
429
|
const isRuntime = Schema.is(RuntimeEcosystem);
|
|
432
430
|
const isUnauditedUpdate = (pkg) => isUnaudited(pkg.ecosystem);
|
|
433
|
-
const isAppUpdate = (pkg) => isApp(pkg.ecosystem);
|
|
431
|
+
const isAppUpdate = (pkg) => isApp$1(pkg.ecosystem);
|
|
434
432
|
const unauditedKind = (pkg) => {
|
|
435
|
-
if (isApp(pkg.ecosystem)) return "app";
|
|
433
|
+
if (isApp$1(pkg.ecosystem)) return "app";
|
|
436
434
|
if (isRuntime(pkg.ecosystem)) return "runtime";
|
|
437
435
|
if (isUnaudited(pkg.ecosystem)) return "os";
|
|
438
436
|
};
|
|
@@ -753,15 +751,15 @@ const Severity = Schema.Literals([
|
|
|
753
751
|
const Finding = Schema.Struct({
|
|
754
752
|
severity: Severity,
|
|
755
753
|
title: Schema.String,
|
|
756
|
-
detail: Schema.String,
|
|
757
|
-
paths: Schema.Array(Schema.String)
|
|
754
|
+
detail: Schema.String.annotate({ description: "Markdown: the evidence and why it matters." }),
|
|
755
|
+
paths: Schema.Array(Schema.String).annotate({ description: "Workspace paths, with line ranges where useful." })
|
|
758
756
|
});
|
|
759
757
|
const Report = Schema.Struct({
|
|
760
758
|
verdict: Verdict,
|
|
761
|
-
rationale: Schema.String,
|
|
762
|
-
summary: Schema.String,
|
|
759
|
+
rationale: Schema.String.annotate({ description: "One concrete sentence justifying the verdict." }),
|
|
760
|
+
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." }),
|
|
763
761
|
findings: Schema.Array(Finding),
|
|
764
|
-
unverified: Schema.Array(Schema.String)
|
|
762
|
+
unverified: Schema.Array(Schema.String).annotate({ description: "Each thing you could not verify." })
|
|
765
763
|
});
|
|
766
764
|
Schema.Struct({
|
|
767
765
|
mode: Mode,
|
|
@@ -844,21 +842,23 @@ const descriptions = {
|
|
|
844
842
|
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.",
|
|
845
843
|
list: "List workspace entries below a directory to a bounded depth."
|
|
846
844
|
};
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
},
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
}
|
|
845
|
+
const count$1 = (maximum) => Schema.Number.pipe(Schema.check(Schema.isInt(), Schema.isBetween({
|
|
846
|
+
minimum: 1,
|
|
847
|
+
maximum
|
|
848
|
+
})));
|
|
849
|
+
const ReadArgs = Schema.Struct({
|
|
850
|
+
path: Schema.String.annotate({ description: "Workspace-relative file path." }),
|
|
851
|
+
offset: Schema.Number.pipe(Schema.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))).annotate({ description: "Zero-based byte offset; must be >= 0." }),
|
|
852
|
+
limit: count$1(limits$1.readBytes).annotate({ description: `Maximum bytes to return; must be between 1 and ${limits$1.readBytes} inclusive.` })
|
|
853
|
+
});
|
|
854
|
+
const GrepArgs = Schema.Struct({
|
|
855
|
+
path: Schema.String.annotate({ description: "Workspace-relative file or directory path." }),
|
|
856
|
+
pattern: Schema.String.annotate({ description: "JavaScript regular expression." })
|
|
857
|
+
});
|
|
858
|
+
const ListArgs = Schema.Struct({
|
|
859
|
+
path: Schema.String.annotate({ description: "Workspace-relative directory path; use . for the root." }),
|
|
860
|
+
depth: count$1(limits$1.listDepth).annotate({ description: `Maximum directory depth; must be between 1 and ${limits$1.listDepth} inclusive.` })
|
|
861
|
+
});
|
|
862
862
|
var ToolError = class extends Error {
|
|
863
863
|
constructor(message) {
|
|
864
864
|
super(message);
|
|
@@ -915,10 +915,6 @@ const locate$4 = async (workspace, name, context) => {
|
|
|
915
915
|
return fail$1(`${context}: ${reason(error)}`);
|
|
916
916
|
}
|
|
917
917
|
};
|
|
918
|
-
const byName = (left, right) => {
|
|
919
|
-
if (left.name < right.name) return -1;
|
|
920
|
-
return left.name > right.name ? 1 : 0;
|
|
921
|
-
};
|
|
922
918
|
const suffixOf = (entry) => {
|
|
923
919
|
if (entry.isDirectory()) return "/";
|
|
924
920
|
return entry.isSymbolicLink() ? "@" : "";
|
|
@@ -1008,7 +1004,7 @@ const list = async (workspace, args, signal) => await respond(signal, async () =
|
|
|
1008
1004
|
return result === "" ? "no entries" : result;
|
|
1009
1005
|
});
|
|
1010
1006
|
const decodeAnswer = Schema.decodeUnknownOption(Schema.Union([Schema.Struct({ text: Schema.String }), Schema.Struct({ error: Schema.String })]));
|
|
1011
|
-
const grepWorker =
|
|
1007
|
+
const grepWorker = new URL(`grep-worker${path.extname(new URL(import.meta.url).pathname)}`, import.meta.url);
|
|
1012
1008
|
const search = async (job, timeoutMs, signal) => {
|
|
1013
1009
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
1014
1010
|
const worker = new Worker(grepWorker);
|
|
@@ -1344,17 +1340,11 @@ const runDiff = (cwd, args, from, to, input) => Effect.callback((resume) => {
|
|
|
1344
1340
|
});
|
|
1345
1341
|
return Effect.sync(() => child.kill());
|
|
1346
1342
|
});
|
|
1347
|
-
const regularFiles =
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
const info = await lstat(path.join(root, file));
|
|
1353
|
-
if (info.isDirectory()) found.push(...await regularFiles(root, file));
|
|
1354
|
-
else if (info.isFile()) found.push(file);
|
|
1355
|
-
}
|
|
1356
|
-
return found;
|
|
1357
|
-
};
|
|
1343
|
+
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) => {
|
|
1344
|
+
const file = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
1345
|
+
if (entry.isDirectory()) return regularFiles(root, file);
|
|
1346
|
+
return Effect.succeed(entry.isFile() ? [file] : []);
|
|
1347
|
+
}, { concurrency: "unbounded" })), Effect.map((found) => found.flat()));
|
|
1358
1348
|
const failure$1 = (from, to) => (cause) => new DiffFailed({
|
|
1359
1349
|
from,
|
|
1360
1350
|
to,
|
|
@@ -1372,15 +1362,25 @@ const plain$1 = (pair) => ({
|
|
|
1372
1362
|
paired: false,
|
|
1373
1363
|
rewritten: false
|
|
1374
1364
|
});
|
|
1375
|
-
const planTree =
|
|
1376
|
-
const
|
|
1365
|
+
const planTree = (workspace, from, to) => Effect.gen(function* () {
|
|
1366
|
+
const attempt = (run) => Effect.tryPromise({
|
|
1367
|
+
try: run,
|
|
1368
|
+
catch: failure$1(from, to)
|
|
1369
|
+
});
|
|
1370
|
+
const [before, after] = yield* Effect.all([regularFiles(path.join(workspace, from)), regularFiles(path.join(workspace, to))], { concurrency: "unbounded" });
|
|
1377
1371
|
const beforeSet = new Set(before);
|
|
1378
1372
|
const afterSet = new Set(after);
|
|
1379
1373
|
const fromOnly = before.filter((file) => !afterSet.has(file));
|
|
1380
1374
|
const toOnly = after.filter((file) => !beforeSet.has(file));
|
|
1381
|
-
const only =
|
|
1382
|
-
|
|
1383
|
-
|
|
1375
|
+
const only = new Map(yield* Effect.forEach([...fromOnly.map((file) => ({
|
|
1376
|
+
side: "from",
|
|
1377
|
+
root: from,
|
|
1378
|
+
file
|
|
1379
|
+
})), ...toOnly.map((file) => ({
|
|
1380
|
+
side: "to",
|
|
1381
|
+
root: to,
|
|
1382
|
+
file
|
|
1383
|
+
}))], ({ side, root, file }) => attempt(async () => [`${side}\0${file}`, await readFile(path.join(workspace, root, file))]), { concurrency: 8 }));
|
|
1384
1384
|
const pairs = matchRenames(fromOnly, toOnly, (side, file) => only.get(`${side}\0${file}`) ?? Buffer.alloc(0));
|
|
1385
1385
|
const renames = renamesOf(pairs);
|
|
1386
1386
|
const pairedFrom = new Set(pairs.map((pair) => pair.from));
|
|
@@ -1391,32 +1391,36 @@ const planTree = async (workspace, from, to) => {
|
|
|
1391
1391
|
from: file,
|
|
1392
1392
|
to: file
|
|
1393
1393
|
}))];
|
|
1394
|
-
|
|
1394
|
+
const classify = async (pair) => {
|
|
1395
1395
|
const oldPath = path.join(workspace, from, pair.from);
|
|
1396
1396
|
const nextPath = path.join(workspace, to, pair.to);
|
|
1397
1397
|
const paired = pair.from !== pair.to;
|
|
1398
|
-
if (!paired && renames.size === 0) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
}
|
|
1402
|
-
const old = only.get(`from\0${pair.from}`) ??
|
|
1403
|
-
|
|
1404
|
-
if (!paired && old.equals(next)) continue;
|
|
1398
|
+
if (!paired && renames.size === 0) return await identical(oldPath, nextPath) ? void 0 : {
|
|
1399
|
+
same: false,
|
|
1400
|
+
change: plain$1(pair)
|
|
1401
|
+
};
|
|
1402
|
+
const [old, next] = await Promise.all([only.get(`from\0${pair.from}`) ?? readFile(oldPath), only.get(`to\0${pair.to}`) ?? readFile(nextPath)]);
|
|
1403
|
+
if (!paired && old.equals(next)) return;
|
|
1405
1404
|
const text = isText(old) ? old.toString("latin1") : void 0;
|
|
1406
1405
|
const rewrittenText = text === void 0 ? void 0 : rewrite(text, renames);
|
|
1407
1406
|
const rewritten = rewrittenText !== void 0 && rewrittenText !== text;
|
|
1408
|
-
if (!paired && !rewritten) {
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
}
|
|
1407
|
+
if (!paired && !rewritten) return {
|
|
1408
|
+
same: false,
|
|
1409
|
+
change: plain$1(pair)
|
|
1410
|
+
};
|
|
1412
1411
|
const expected = rewrittenText === void 0 ? old : Buffer.from(rewrittenText, "latin1");
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1412
|
+
return {
|
|
1413
|
+
same: expected.equals(next),
|
|
1414
|
+
change: {
|
|
1415
|
+
...pair,
|
|
1416
|
+
expected,
|
|
1417
|
+
paired,
|
|
1418
|
+
rewritten
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
};
|
|
1422
|
+
const planned = yield* Effect.forEach(candidates, (pair) => attempt(async () => await classify(pair)), { concurrency: 8 });
|
|
1423
|
+
for (const entry of planned) if (entry !== void 0) (entry.same ? same : changes).push(entry.change);
|
|
1420
1424
|
for (const file of fromOnly) if (!pairedFrom.has(file)) changes.push({
|
|
1421
1425
|
...plain$1({
|
|
1422
1426
|
from: file,
|
|
@@ -1438,7 +1442,7 @@ const planTree = async (workspace, from, to) => {
|
|
|
1438
1442
|
}),
|
|
1439
1443
|
same
|
|
1440
1444
|
};
|
|
1441
|
-
};
|
|
1445
|
+
});
|
|
1442
1446
|
const mebibytes = (bytes) => `${Math.round(bytes / 1024 / 1024)} MiB`;
|
|
1443
1447
|
const shownOmissions = 2e3;
|
|
1444
1448
|
const changeDiff = (workspace, from, to, change) => {
|
|
@@ -1470,10 +1474,7 @@ const partial = (what, omitted) => omitted === "" ? "" : `The ${what} diff is pa
|
|
|
1470
1474
|
const DiffBudget = Context.Reference("upup/DiffBudget", { defaultValue: () => () => maxDiffBytes });
|
|
1471
1475
|
const treeDiff = (workspace, from, to, limit) => Effect.gen(function* () {
|
|
1472
1476
|
const budget = limit ?? (yield* DiffBudget)(from);
|
|
1473
|
-
const { changes, same } = yield*
|
|
1474
|
-
try: async () => await planTree(workspace, from, to),
|
|
1475
|
-
catch: failure$1(from, to)
|
|
1476
|
-
});
|
|
1477
|
+
const { changes, same } = yield* planTree(workspace, from, to);
|
|
1477
1478
|
const parts = [];
|
|
1478
1479
|
const left = [];
|
|
1479
1480
|
let size = 0;
|
|
@@ -1807,12 +1808,16 @@ const majorsOf = (from, to) => {
|
|
|
1807
1808
|
};
|
|
1808
1809
|
const exists$1 = async (file) => await stat(file).then(() => true, () => false);
|
|
1809
1810
|
const emptyTree = async (directory) => await readdir(directory).then((entries) => entries.length === 0, () => false);
|
|
1810
|
-
const
|
|
1811
|
+
const majorsIn = async (workspace) => {
|
|
1812
|
+
const [from, to] = await Promise.all([listed$2(workspace, files.dependencyListFrom), listed$2(workspace, files.dependencyListTo)]);
|
|
1813
|
+
return majorsOf(from, to);
|
|
1814
|
+
};
|
|
1815
|
+
const readMajors = (workspace) => Effect.promise(async () => await majorsIn(workspace));
|
|
1811
1816
|
const read$1 = (workspace) => Effect.gen(function* () {
|
|
1812
1817
|
const meta = yield* readMeta(workspace);
|
|
1813
1818
|
return yield* Effect.tryPromise({
|
|
1814
1819
|
try: async () => {
|
|
1815
|
-
const diff = await readFile(path.join(workspace, files.diff));
|
|
1820
|
+
const [diff, majors] = await Promise.all([readFile(path.join(workspace, files.diff)), majorsIn(workspace)]);
|
|
1816
1821
|
const trees = [];
|
|
1817
1822
|
const titled = [];
|
|
1818
1823
|
const targets = /* @__PURE__ */ new Map();
|
|
@@ -1851,7 +1856,7 @@ const read$1 = (workspace) => Effect.gen(function* () {
|
|
|
1851
1856
|
trees,
|
|
1852
1857
|
files: titled,
|
|
1853
1858
|
sections,
|
|
1854
|
-
majors
|
|
1859
|
+
majors
|
|
1855
1860
|
};
|
|
1856
1861
|
},
|
|
1857
1862
|
catch: failed$1(workspace)
|
|
@@ -1921,28 +1926,18 @@ const BriefDoc = defineDoc({
|
|
|
1921
1926
|
initial: () => ({ brief: null }),
|
|
1922
1927
|
checkpointWhen: () => false
|
|
1923
1928
|
});
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
Type.Literal("danger"),
|
|
1935
|
-
Type.Literal("no-verdict")
|
|
1936
|
-
]),
|
|
1937
|
-
rationale: Type.String({ description: "One concrete sentence justifying the verdict." }),
|
|
1938
|
-
summary: Type.String({ description: "Markdown: what the update changes that bears on the mandate, what you flagged and why you cleared or kept it." }),
|
|
1939
|
-
findings: Type.Array(Type.Object({
|
|
1940
|
-
severity,
|
|
1941
|
-
title: Type.String(),
|
|
1942
|
-
detail: Type.String({ description: "Markdown: the evidence and why it matters." }),
|
|
1943
|
-
paths: Type.Array(Type.String(), { description: "Workspace paths, with line ranges where useful." })
|
|
1929
|
+
const parameters = (schema) => Schema.toJsonSchemaDocument(schema).schema;
|
|
1930
|
+
const decodeRead = Schema.decodeUnknownSync(ReadArgs);
|
|
1931
|
+
const decodeGrep = Schema.decodeUnknownSync(GrepArgs);
|
|
1932
|
+
const decodeList = Schema.decodeUnknownSync(ListArgs);
|
|
1933
|
+
const decodeSubmitted = Schema.decodeUnknownSync(Report);
|
|
1934
|
+
const encodeReport = (submitted) => ({
|
|
1935
|
+
...submitted,
|
|
1936
|
+
findings: submitted.findings.map((finding) => ({
|
|
1937
|
+
...finding,
|
|
1938
|
+
paths: [...finding.paths]
|
|
1944
1939
|
})),
|
|
1945
|
-
unverified:
|
|
1940
|
+
unverified: [...submitted.unverified]
|
|
1946
1941
|
});
|
|
1947
1942
|
const ReportDoc = defineDoc({
|
|
1948
1943
|
kind: "upup.report",
|
|
@@ -1968,42 +1963,33 @@ const auditExtension = (workspace) => {
|
|
|
1968
1963
|
name: "read",
|
|
1969
1964
|
description: descriptions.read,
|
|
1970
1965
|
replay: "safe",
|
|
1971
|
-
parameters:
|
|
1972
|
-
|
|
1973
|
-
offset: Type.Integer({ description: parameters.read.offset }),
|
|
1974
|
-
limit: Type.Integer({ description: parameters.read.limit })
|
|
1975
|
-
}),
|
|
1976
|
-
execute: async (args, _api, invocation) => answer(await read$2(workspace, args, invocation.abortSignal))
|
|
1966
|
+
parameters: parameters(ReadArgs),
|
|
1967
|
+
execute: async (args, _api, invocation) => answer(await read$2(workspace, decodeRead(args), invocation.abortSignal))
|
|
1977
1968
|
});
|
|
1978
1969
|
const grep$1 = defineTool({
|
|
1979
1970
|
name: "grep",
|
|
1980
1971
|
description: descriptions.grep,
|
|
1981
1972
|
replay: "safe",
|
|
1982
|
-
parameters:
|
|
1983
|
-
|
|
1984
|
-
pattern: Type.String({ description: parameters.grep.pattern })
|
|
1985
|
-
}),
|
|
1986
|
-
execute: async (args, _api, invocation) => answer(await grep(workspace, args, invocation.abortSignal === void 0 ? {} : { signal: invocation.abortSignal }))
|
|
1973
|
+
parameters: parameters(GrepArgs),
|
|
1974
|
+
execute: async (args, _api, invocation) => answer(await grep(workspace, decodeGrep(args), invocation.abortSignal === void 0 ? {} : { signal: invocation.abortSignal }))
|
|
1987
1975
|
});
|
|
1988
1976
|
const list$1 = defineTool({
|
|
1989
1977
|
name: "list",
|
|
1990
1978
|
description: descriptions.list,
|
|
1991
1979
|
replay: "safe",
|
|
1992
|
-
parameters:
|
|
1993
|
-
|
|
1994
|
-
depth: Type.Integer({ description: parameters.list.depth })
|
|
1995
|
-
}),
|
|
1996
|
-
execute: async (args, _api, invocation) => answer(await list(workspace, args, invocation.abortSignal))
|
|
1980
|
+
parameters: parameters(ListArgs),
|
|
1981
|
+
execute: async (args, _api, invocation) => answer(await list(workspace, decodeList(args), invocation.abortSignal))
|
|
1997
1982
|
});
|
|
1998
1983
|
const submit = defineTool({
|
|
1999
1984
|
name: "submit_report",
|
|
2000
1985
|
description: "Submit the audit report. Call exactly once, when the investigation is done; the audit ends.",
|
|
2001
1986
|
replay: "safe",
|
|
2002
|
-
parameters:
|
|
1987
|
+
parameters: parameters(Report),
|
|
2003
1988
|
execute: async (args, api, invocation) => {
|
|
1989
|
+
const encoded = encodeReport(decodeSubmitted(args));
|
|
2004
1990
|
await api.commit(async (tx) => {
|
|
2005
1991
|
const doc = await tx.doc(ReportDoc, api.conversationId);
|
|
2006
|
-
doc.report =
|
|
1992
|
+
doc.report = encoded;
|
|
2007
1993
|
}, invocation);
|
|
2008
1994
|
return {
|
|
2009
1995
|
...text$2("Report submitted."),
|
|
@@ -2079,6 +2065,32 @@ const entriesOf$1 = (message) => {
|
|
|
2079
2065
|
}
|
|
2080
2066
|
return [];
|
|
2081
2067
|
};
|
|
2068
|
+
const TextBlock = Schema.Struct({
|
|
2069
|
+
type: Schema.Literal("text"),
|
|
2070
|
+
text: Schema.String
|
|
2071
|
+
});
|
|
2072
|
+
const ThinkingBlock = Schema.Struct({
|
|
2073
|
+
type: Schema.Literal("thinking"),
|
|
2074
|
+
thinking: Schema.String
|
|
2075
|
+
});
|
|
2076
|
+
const isTextBlock = Schema.is(TextBlock);
|
|
2077
|
+
const isThinkingBlock = Schema.is(ThinkingBlock);
|
|
2078
|
+
const decodeLive = Schema.decodeUnknownOption(Schema.Struct({ generation: Schema.optional(Schema.Struct({
|
|
2079
|
+
attempt: Schema.Finite,
|
|
2080
|
+
message: Schema.optional(Schema.Struct({ content: Schema.Array(Schema.Unknown) })),
|
|
2081
|
+
retry: Schema.optional(Schema.Struct({ error: Schema.String }))
|
|
2082
|
+
})) }));
|
|
2083
|
+
const streamed = (block) => {
|
|
2084
|
+
if (isThinkingBlock(block)) return block.thinking.trim() === "" ? [] : [{
|
|
2085
|
+
kind: "thinking",
|
|
2086
|
+
text: block.thinking
|
|
2087
|
+
}];
|
|
2088
|
+
if (isTextBlock(block)) return block.text.trim() === "" ? [] : [{
|
|
2089
|
+
kind: "text",
|
|
2090
|
+
text: block.text
|
|
2091
|
+
}];
|
|
2092
|
+
return [];
|
|
2093
|
+
};
|
|
2082
2094
|
var Transcript = class {
|
|
2083
2095
|
committed = /* @__PURE__ */ new Map();
|
|
2084
2096
|
live = [];
|
|
@@ -2086,41 +2098,20 @@ var Transcript = class {
|
|
|
2086
2098
|
add(entry) {
|
|
2087
2099
|
this.committed.set(entry.id, (entry.model ?? []).flatMap(entriesOf$1));
|
|
2088
2100
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
for (const change of event.changes) if (change.type === "text_delta" || change.type === "thinking_delta") {
|
|
2104
|
-
const kind = change.type === "text_delta" ? "text" : "thinking";
|
|
2105
|
-
const last = this.live.at(-1);
|
|
2106
|
-
if (last?.kind === kind) this.live[this.live.length - 1] = {
|
|
2107
|
-
kind,
|
|
2108
|
-
text: last.text + change.delta
|
|
2109
|
-
};
|
|
2110
|
-
else this.live.push({
|
|
2111
|
-
kind,
|
|
2112
|
-
text: change.delta
|
|
2113
|
-
});
|
|
2114
|
-
}
|
|
2115
|
-
break;
|
|
2116
|
-
case "auto_retry_start":
|
|
2117
|
-
this.retry = {
|
|
2118
|
-
kind: "retry",
|
|
2119
|
-
text: `retrying (attempt ${event.attempt}): ${event.errorMessage}`
|
|
2120
|
-
};
|
|
2121
|
-
break;
|
|
2122
|
-
case "auto_retry_end": this.retry = void 0;
|
|
2123
|
-
}
|
|
2101
|
+
view(view) {
|
|
2102
|
+
for (const entry of view.entries) this.add(entry);
|
|
2103
|
+
const generation = decodeLive(view.docs[LiveDoc.definition.kind]).pipe(Option.flatMapNullishOr((live) => live.generation));
|
|
2104
|
+
this.live = Option.match(generation, {
|
|
2105
|
+
onNone: () => [],
|
|
2106
|
+
onSome: ({ message }) => (message?.content ?? []).flatMap(streamed)
|
|
2107
|
+
});
|
|
2108
|
+
this.retry = Option.match(generation, {
|
|
2109
|
+
onNone: () => void 0,
|
|
2110
|
+
onSome: ({ attempt, retry }) => retry === void 0 ? void 0 : {
|
|
2111
|
+
kind: "retry",
|
|
2112
|
+
text: `retrying (attempt ${attempt}): ${retry.error}`
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2124
2115
|
}
|
|
2125
2116
|
entries() {
|
|
2126
2117
|
return [
|
|
@@ -2135,16 +2126,24 @@ const attempt$1 = (evaluate) => Effect.tryPromise({
|
|
|
2135
2126
|
try: evaluate,
|
|
2136
2127
|
catch: failed
|
|
2137
2128
|
});
|
|
2129
|
+
const watched = (open, onValue) => Effect.acquireRelease(attempt$1(async () => {
|
|
2130
|
+
const watch = await open();
|
|
2131
|
+
watch.start(async (call) => {
|
|
2132
|
+
if (call.kind === "value") onValue(call.value);
|
|
2133
|
+
});
|
|
2134
|
+
return watch;
|
|
2135
|
+
}), (watch) => Effect.promise(async () => await watch.stop()).pipe(Effect.ignore));
|
|
2138
2136
|
const lastAssistant = (entries) => entries.flatMap((entry) => entry.model ?? []).findLast((message) => message.role === "assistant");
|
|
2139
|
-
const openHarness = (file,
|
|
2140
|
-
if (workspace ===
|
|
2141
|
-
const extension = auditExtension(workspace);
|
|
2137
|
+
const openHarness = (file, models, workspace) => Effect.acquireRelease(attempt$1(async () => {
|
|
2138
|
+
if (workspace === void 0 && !existsSync(file)) throw new Error(`no audit at ${file}`);
|
|
2139
|
+
const extension = auditExtension(workspace ?? "");
|
|
2142
2140
|
const registry = createRegistry();
|
|
2143
2141
|
registry.install(extension);
|
|
2144
2142
|
return await Harness.open(await openNodeSqliteStorage(file), {
|
|
2145
2143
|
models,
|
|
2146
2144
|
registry,
|
|
2147
|
-
settings: { extensions: [extension] }
|
|
2145
|
+
settings: { extensions: [extension] },
|
|
2146
|
+
readOnly: workspace === void 0
|
|
2148
2147
|
}, context);
|
|
2149
2148
|
}), (harness) => Effect.promise(async () => {
|
|
2150
2149
|
await harness.close(context);
|
|
@@ -2157,7 +2156,7 @@ const encodeBrief = (brief) => ({
|
|
|
2157
2156
|
artifacts: [...brief.artifacts]
|
|
2158
2157
|
});
|
|
2159
2158
|
const run$5 = (options) => Effect.scoped(Effect.gen(function* () {
|
|
2160
|
-
const harness = yield* openHarness(options.file, options.
|
|
2159
|
+
const harness = yield* openHarness(options.file, options.models, options.brief.workspace);
|
|
2161
2160
|
const root = yield* attempt$1(async () => await harness.root(context, {
|
|
2162
2161
|
agent: {
|
|
2163
2162
|
model: {
|
|
@@ -2172,13 +2171,12 @@ const run$5 = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
2172
2171
|
}
|
|
2173
2172
|
}));
|
|
2174
2173
|
const transcript = new Transcript();
|
|
2175
|
-
const
|
|
2176
|
-
|
|
2177
|
-
options.onTranscript(transcript.entries());
|
|
2178
|
-
events.start(async (batch) => {
|
|
2179
|
-
for (const event of batch) transcript.apply(event);
|
|
2174
|
+
const views = yield* watched(async () => await root.watch(context), (view) => {
|
|
2175
|
+
transcript.view(view);
|
|
2180
2176
|
options.onTranscript(transcript.entries());
|
|
2181
2177
|
});
|
|
2178
|
+
transcript.view(views.value);
|
|
2179
|
+
options.onTranscript(transcript.entries());
|
|
2182
2180
|
harness.resume();
|
|
2183
2181
|
const settled = yield* Effect.tryPromise({
|
|
2184
2182
|
try: async (signal) => {
|
|
@@ -2205,20 +2203,29 @@ const run$5 = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
2205
2203
|
_tag: "Reported",
|
|
2206
2204
|
report: decoded.value
|
|
2207
2205
|
};
|
|
2208
|
-
const final = yield* attempt$1(async () => await
|
|
2209
|
-
yield* attempt$1(async () => await final.stop());
|
|
2206
|
+
const final = yield* attempt$1(async () => await root.context(context));
|
|
2210
2207
|
if (settled.value.status === "unanswered") return {
|
|
2211
2208
|
_tag: "Failed",
|
|
2212
2209
|
issue: Predicate.isString(settled.value.detail) ? settled.value.detail : settled.value.reason
|
|
2213
2210
|
};
|
|
2214
2211
|
return {
|
|
2215
2212
|
_tag: "Failed",
|
|
2216
|
-
issue: lastAssistant(final.
|
|
2213
|
+
issue: lastAssistant(final.entries)?.errorMessage ?? "the model finished without submitting a report"
|
|
2217
2214
|
};
|
|
2218
2215
|
}));
|
|
2216
|
+
const storedRoot = (file, models) => Effect.gen(function* () {
|
|
2217
|
+
const harness = yield* openHarness(file, models);
|
|
2218
|
+
return {
|
|
2219
|
+
harness,
|
|
2220
|
+
root: yield* attempt$1(async () => await harness.conversation(ROOT_CONVERSATION_ID, context))
|
|
2221
|
+
};
|
|
2222
|
+
});
|
|
2219
2223
|
const stored = (file, models) => Effect.scoped(Effect.gen(function* () {
|
|
2220
|
-
const harness = yield*
|
|
2221
|
-
|
|
2224
|
+
const { harness, root } = yield* storedRoot(file, models);
|
|
2225
|
+
if (root === void 0) return {
|
|
2226
|
+
brief: Option.none(),
|
|
2227
|
+
report: Option.none()
|
|
2228
|
+
};
|
|
2222
2229
|
const brief = yield* attempt$1(async () => await harness.snapshot(BriefDoc, root.id, context));
|
|
2223
2230
|
const submitted = yield* attempt$1(async () => await harness.snapshot(ReportDoc, root.id, context));
|
|
2224
2231
|
return {
|
|
@@ -2227,12 +2234,12 @@ const stored = (file, models) => Effect.scoped(Effect.gen(function* () {
|
|
|
2227
2234
|
};
|
|
2228
2235
|
}));
|
|
2229
2236
|
const transcript = (file, models) => Effect.scoped(Effect.gen(function* () {
|
|
2230
|
-
const
|
|
2231
|
-
const root = yield* attempt$1(async () => await harness.root(context));
|
|
2232
|
-
const events = yield* attempt$1(async () => await watchEvents(harness, root.id, context));
|
|
2233
|
-
yield* attempt$1(async () => await events.stop());
|
|
2237
|
+
const { root } = yield* storedRoot(file, models);
|
|
2234
2238
|
const built = new Transcript();
|
|
2235
|
-
|
|
2239
|
+
if (root !== void 0) {
|
|
2240
|
+
const active = yield* attempt$1(async () => await root.context(context));
|
|
2241
|
+
for (const entry of active.entries) built.add(entry);
|
|
2242
|
+
}
|
|
2236
2243
|
return built.entries();
|
|
2237
2244
|
}));
|
|
2238
2245
|
// src/inventory/versions.ts
|
|
@@ -2339,7 +2346,7 @@ const escape = (path) => path.replaceAll(/[A-Z]/gu, (letter) => `!${letter.toLow
|
|
|
2339
2346
|
const deprecation = (mod) => Option.fromNullishOr(/^\/\/\s*Deprecated:\s*(?<reason>.+)$/mu.exec(mod)?.groups?.["reason"]?.trim());
|
|
2340
2347
|
const lookup$5 = Effect.fn("go.lookup")(function* (client, wanted) {
|
|
2341
2348
|
const base = `https://proxy.golang.org/${escape(wanted.name)}/@v`;
|
|
2342
|
-
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:
|
|
2349
|
+
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" });
|
|
2343
2350
|
const mod = yield* getText(client, `${base}/${escape(latest.Version)}.mod`);
|
|
2344
2351
|
const deprecated = deprecation(mod);
|
|
2345
2352
|
const found = infos.flatMap((info) => Option.isSome(info) ? [info.value] : []);
|
|
@@ -4850,30 +4857,36 @@ const resolveFormula = Effect.fn("brew.resolveFormula")(function* (client, sourc
|
|
|
4850
4857
|
digest: createHash("sha256").update(body).digest("hex")
|
|
4851
4858
|
};
|
|
4852
4859
|
});
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
if (complete()) break;
|
|
4860
|
+
const scanHistory = Effect.gen(function* () {
|
|
4861
|
+
let to;
|
|
4862
|
+
let from;
|
|
4863
|
+
let endpoint = `${githubApi}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits?${new URLSearchParams({
|
|
4864
|
+
path: source.rubySourcePath,
|
|
4865
|
+
per_page: `${historyPageSize}`
|
|
4866
|
+
}).toString()}`;
|
|
4867
|
+
let scanned = 0;
|
|
4868
|
+
const complete = () => from !== void 0 && to !== void 0;
|
|
4869
|
+
for (let page = 0; endpoint !== "" && page < maxHistoryPages && !complete(); page += 1) {
|
|
4870
|
+
const { body, link } = yield* githubGet(client, endpoint, token).pipe(Effect.mapError(prefixed("query brew formula history")));
|
|
4871
|
+
const commits = yield* commitsOf(body, endpoint);
|
|
4872
|
+
for (const commit of commits) {
|
|
4873
|
+
scanned += 1;
|
|
4874
|
+
const { snapshot: candidate } = yield* snapshot(commit.sha, commit.message);
|
|
4875
|
+
if (to === void 0 && matchesVersion(candidate.text, toVersion)) to = candidate;
|
|
4876
|
+
if (from === void 0 && matchesVersion(candidate.text, fromVersion)) from = candidate;
|
|
4877
|
+
if (complete()) break;
|
|
4878
|
+
}
|
|
4879
|
+
endpoint = complete() ? "" : nextPage(endpoint, link);
|
|
4874
4880
|
}
|
|
4875
|
-
|
|
4876
|
-
|
|
4881
|
+
return {
|
|
4882
|
+
from,
|
|
4883
|
+
to,
|
|
4884
|
+
scanned
|
|
4885
|
+
};
|
|
4886
|
+
});
|
|
4887
|
+
const [current, history] = yield* Effect.all([source.tapGitHead === "" ? Effect.succeedNone : Effect.option(snapshot(source.tapGitHead, "tap revision reported by brew info")), scanHistory], { concurrency: "unbounded" });
|
|
4888
|
+
const { from, scanned } = history;
|
|
4889
|
+
const to = current._tag === "Some" && matchesVersion(current.value.snapshot.text, toVersion) && (source.rubySourceChecksum === "" || source.rubySourceChecksum.toLowerCase() === current.value.digest) ? current.value.snapshot : history.to;
|
|
4877
4890
|
if (to === void 0) return yield* new EvidenceUnavailable({
|
|
4878
4891
|
source: "brew",
|
|
4879
4892
|
issue: `brew formula history has no snapshot for target version ${toVersion} within the newest ${scanned} commits`
|
|
@@ -5278,13 +5291,14 @@ const payloadDiff = Effect.fn("brew.payloadDiff")(function* (client, workspace,
|
|
|
5278
5291
|
const { name } = shortName(artifact);
|
|
5279
5292
|
const oldStrip = yield* strip(name, plan.old);
|
|
5280
5293
|
const newStrip = yield* strip(name, plan.new);
|
|
5281
|
-
const oldManifest = yield* payloadManifest(client, "installed", plan.old, oldStrip, path.join(workspace, files.payloadTreeFrom));
|
|
5294
|
+
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" });
|
|
5282
5295
|
if (oldManifest === void 0) return yield* unavailablePayload(workspace, "installed Homebrew payload archive format is unsupported");
|
|
5283
|
-
const newManifest = yield* payloadManifest(client, "target", plan.new, newStrip, path.join(workspace, files.payloadTreeTo));
|
|
5284
5296
|
if (newManifest === void 0) return yield* unavailablePayload(workspace, "target Homebrew payload archive format is unsupported");
|
|
5285
|
-
const manifests = yield*
|
|
5286
|
-
|
|
5287
|
-
|
|
5297
|
+
const [manifests, binaries, trees] = yield* Effect.all([
|
|
5298
|
+
writeFileDiff(workspace, files.payloadFrom, files.payloadTo, manifestJson(oldManifest), manifestJson(newManifest)),
|
|
5299
|
+
writeFileDiff(workspace, files.binaryFrom, files.binaryTo, json(binarySummaries(oldManifest)), json(binarySummaries(newManifest))),
|
|
5300
|
+
treeDiff(workspace, files.payloadTreeFrom, files.payloadTreeTo)
|
|
5301
|
+
], { concurrency: "unbounded" });
|
|
5288
5302
|
const contents = trees.diff;
|
|
5289
5303
|
const contentDetail = [" Extracted payload file contents are diffed.", partial("payload", trees.omitted)].filter((sentence) => sentence !== "").join(" ");
|
|
5290
5304
|
const origin = plan.old.origin === "" ? "" : ` (pinned by ${plan.old.origin})`;
|
|
@@ -5763,9 +5777,9 @@ const writeChanges = Effect.fn("git.writeChanges")(function* (from, to, destinat
|
|
|
5763
5777
|
modes
|
|
5764
5778
|
};
|
|
5765
5779
|
const contents = readBatch((yield* git(["cat-file", "--batch"], objects.map((object) => `${object}\n`).join(""))).stdout, objects);
|
|
5766
|
-
yield* Effect.tryPromise({
|
|
5780
|
+
yield* Effect.forEach(changes, (change) => Effect.tryPromise({
|
|
5767
5781
|
try: async () => {
|
|
5768
|
-
for (const
|
|
5782
|
+
for (const [object, root] of [[change.fromObject, destination.from], [change.toObject, destination.to]]) {
|
|
5769
5783
|
if (object === void 0) continue;
|
|
5770
5784
|
const file = path.join(root, change.path);
|
|
5771
5785
|
await mkdir(path.dirname(file), {
|
|
@@ -5779,6 +5793,9 @@ const writeChanges = Effect.fn("git.writeChanges")(function* (from, to, destinat
|
|
|
5779
5793
|
repository,
|
|
5780
5794
|
issue: `write source files: ${String(error)}`
|
|
5781
5795
|
})
|
|
5796
|
+
}), {
|
|
5797
|
+
concurrency: 8,
|
|
5798
|
+
discard: true
|
|
5782
5799
|
});
|
|
5783
5800
|
return {
|
|
5784
5801
|
files: changes.length,
|
|
@@ -5839,9 +5856,9 @@ const moduleChanges = (from, to, released) => {
|
|
|
5839
5856
|
};
|
|
5840
5857
|
};
|
|
5841
5858
|
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);
|
|
5859
|
+
const readBinarySummaries = (workspace) => Effect.all([readSummaries(workspace, files.binaryFrom), readSummaries(workspace, files.binaryTo)], { concurrency: "unbounded" });
|
|
5842
5860
|
const goSourceEvidence = Effect.fn("goSource.evidence")(function* (client, workspace, released) {
|
|
5843
|
-
const from = yield*
|
|
5844
|
-
const to = yield* readSummaries(workspace, files.binaryTo);
|
|
5861
|
+
const [from, to] = yield* readBinarySummaries(workspace);
|
|
5845
5862
|
if (from._tag === "None" || to._tag === "None") return;
|
|
5846
5863
|
const { changes, reason } = moduleChanges(from.value, to.value, released);
|
|
5847
5864
|
if (changes.length === 0) return {
|
|
@@ -5853,7 +5870,7 @@ const goSourceEvidence = Effect.fn("goSource.evidence")(function* (client, works
|
|
|
5853
5870
|
const plan = yield* resolve$3(client, "go", modulePath, version);
|
|
5854
5871
|
return yield* materialize$2(client, plan, path.join(workspace, tree, modulePath));
|
|
5855
5872
|
});
|
|
5856
|
-
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:
|
|
5873
|
+
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 });
|
|
5857
5874
|
const failures = changes.flatMap((change, index) => {
|
|
5858
5875
|
const result = fetched[index];
|
|
5859
5876
|
return result?._tag === "Failure" ? [`${change.path}: ${result.failure.issue}`] : [];
|
|
@@ -5912,8 +5929,7 @@ const buildInfoSources = (from, to) => {
|
|
|
5912
5929
|
return [...sources.values()].slice(0, 4);
|
|
5913
5930
|
};
|
|
5914
5931
|
const readBuildInfoSources = Effect.fn("goSource.buildInfoSources")(function* (workspace) {
|
|
5915
|
-
const from = yield*
|
|
5916
|
-
const to = yield* readSummaries(workspace, files.binaryTo);
|
|
5932
|
+
const [from, to] = yield* readBinarySummaries(workspace);
|
|
5917
5933
|
return from._tag === "None" || to._tag === "None" ? [] : buildInfoSources(from.value, to.value);
|
|
5918
5934
|
});
|
|
5919
5935
|
// src/audit/source-evidence.ts
|
|
@@ -6225,7 +6241,7 @@ const releaseAssetSources = Effect.fn("sourceLink.releaseAssets")(function* (cli
|
|
|
6225
6241
|
} })), Effect.catch((error) => Effect.succeed(gap(error.issue))));
|
|
6226
6242
|
}
|
|
6227
6243
|
return Effect.succeed(gap("no release of the repository publishes this exact binary"));
|
|
6228
|
-
});
|
|
6244
|
+
}, { concurrency: 4 });
|
|
6229
6245
|
});
|
|
6230
6246
|
// src/audit/brew.ts
|
|
6231
6247
|
const caskArchiveUnavailable = "compiled cask payloads are not source archives";
|
|
@@ -6291,7 +6307,7 @@ const sourceDiff = Effect.fn("brew.sourceDiff")(function* (client, workspace, pa
|
|
|
6291
6307
|
const [gap] = yield* Effect.all([sourceTree(client, pair.old, path.join(workspace, files.sourceFrom)).pipe(Effect.as(""), Effect.catch((error) => {
|
|
6292
6308
|
const reason = historicalGap(pair.old.url, error);
|
|
6293
6309
|
return reason === void 0 ? Effect.fail(labelled("download old brew source archive")(error)) : Effect.succeed(reason);
|
|
6294
|
-
})), sourceTree(client, pair.new, path.join(workspace, files.source)).pipe(Effect.mapError(labelled("download new brew source archive")))], { concurrency:
|
|
6310
|
+
})), sourceTree(client, pair.new, path.join(workspace, files.source)).pipe(Effect.mapError(labelled("download new brew source archive")))], { concurrency: "unbounded" });
|
|
6295
6311
|
if (gap !== "") return {
|
|
6296
6312
|
diff: Buffer.alloc(0),
|
|
6297
6313
|
reason: gap,
|
|
@@ -6328,9 +6344,11 @@ const sourceEvidence = Effect.fn("brew.sourceEvidence")(function* (client, works
|
|
|
6328
6344
|
};
|
|
6329
6345
|
});
|
|
6330
6346
|
const materialize$1 = Effect.fn("brew.materialize")(function* (client, workspace, identity, plan, key) {
|
|
6331
|
-
const formula = yield*
|
|
6332
|
-
|
|
6333
|
-
|
|
6347
|
+
const [formula, source, payload] = yield* Effect.all([
|
|
6348
|
+
materializeFormula(workspace, plan.formula),
|
|
6349
|
+
sourceEvidence(client, workspace, archives(plan.formula), plan.sourceGap),
|
|
6350
|
+
materializePayload(client, workspace, identity.delta.artifact, plan.payload)
|
|
6351
|
+
], { concurrency: "unbounded" });
|
|
6334
6352
|
const goSource = source.included ? void 0 : yield* goSourceEvidence(client, workspace, identity.delta);
|
|
6335
6353
|
const sources = goSource?.included === true || source.included ? [] : [...plan.repository === void 0 ? [] : [plan.repository], ...yield* readBuildInfoSources(workspace)];
|
|
6336
6354
|
const repository = sources.length === 0 ? void 0 : yield* materializeSources(workspace, sources);
|
|
@@ -6357,7 +6375,7 @@ const resolve$2 = Effect.fn("brew.resolve")(function* (client, identity) {
|
|
|
6357
6375
|
const formulaGap = formula.fromGap === "" ? archiveUnavailableReason(archives(formula)) : "the installed version's formula snapshot is unavailable";
|
|
6358
6376
|
const sourceGap = kind === "cask" ? caskArchiveUnavailable : formulaGap;
|
|
6359
6377
|
const payload = yield* resolvePayload(info, artifact, from, to, formula.from?.text);
|
|
6360
|
-
const released = sourceGap !== "" && payload.old.url !== "" && payload.new.url !== "" ? yield* Effect.all([releaseTagSource(payload.old.url), releaseTagSource(payload.new.url)], { concurrency:
|
|
6378
|
+
const released = sourceGap !== "" && payload.old.url !== "" && payload.new.url !== "" ? yield* Effect.all([releaseTagSource(payload.old.url), releaseTagSource(payload.new.url)], { concurrency: "unbounded" }) : void 0;
|
|
6361
6379
|
const repository = released === void 0 || released.every((side) => "gap" in side) ? void 0 : {
|
|
6362
6380
|
label: artifact,
|
|
6363
6381
|
from: released[0],
|
|
@@ -6422,7 +6440,7 @@ const workspaceOf$1 = Effect.fn("githubActions.workspace")(function* (workspace,
|
|
|
6422
6440
|
const githubActionPreparer = (repositoryOf, options = {}) => Effect.fn("githubActions.prepare")(function* (_client, identity) {
|
|
6423
6441
|
const { artifact, from, to } = identity.delta;
|
|
6424
6442
|
const repository = repositoryOf(artifact);
|
|
6425
|
-
const [fromCommit, toCommit] = yield* Effect.all([tagCommit(repository, from, options), tagCommit(repository, to, options)], { concurrency:
|
|
6443
|
+
const [fromCommit, toCommit] = yield* Effect.all([tagCommit(repository, from, options), tagCommit(repository, to, options)], { concurrency: "unbounded" }).pipe(Effect.mapError(unavailable$1));
|
|
6426
6444
|
const listed = identity.context["revision"];
|
|
6427
6445
|
if (listed !== toCommit) return yield* new EvidenceUnavailable({
|
|
6428
6446
|
source: repository,
|
|
@@ -6619,12 +6637,14 @@ const pypiWorkspace = Effect.fn("pypi.workspace")(function* (client, workspace,
|
|
|
6619
6637
|
issue: error instanceof Error ? error.message : String(error)
|
|
6620
6638
|
})
|
|
6621
6639
|
});
|
|
6622
|
-
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:
|
|
6623
|
-
const sources = yield*
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6640
|
+
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" });
|
|
6641
|
+
const [sources, records, sourceBinaries, wheels, wheelBinaries] = yield* Effect.all([
|
|
6642
|
+
treeDiff(workspace, files.from, files.to),
|
|
6643
|
+
writeFileDiff(workspace, files.pypiFrom, files.pypiTo, manifestJson(from), manifestJson(to)),
|
|
6644
|
+
writeFileDiff(workspace, files.binaryFrom, files.binaryTo, json(binarySummaries(from.publisher_sdist?.archive_manifest)), json(binarySummaries(to.publisher_sdist?.archive_manifest))),
|
|
6645
|
+
treeDiff(workspace, files.wheelFrom, files.wheelTo),
|
|
6646
|
+
writeFileDiff(workspace, files.binaryWheelFrom, files.binaryWheelTo, json(binarySummaries(from.compatible_universal_wheel?.archive_manifest)), json(binarySummaries(to.compatible_universal_wheel?.archive_manifest)))
|
|
6647
|
+
], { concurrency: "unbounded" });
|
|
6628
6648
|
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.`;
|
|
6629
6649
|
return yield* write(workspace, identity, Buffer.concat([
|
|
6630
6650
|
sources.diff,
|
|
@@ -6641,7 +6661,7 @@ const pypiWorkspace = Effect.fn("pypi.workspace")(function* (client, workspace,
|
|
|
6641
6661
|
const preparePyPI = Effect.fn("pypi.prepare")(function* (client, identity) {
|
|
6642
6662
|
const { artifact, from, to } = identity.delta;
|
|
6643
6663
|
const python = identity.context["python"] ?? "";
|
|
6644
|
-
const [fromPlan, toPlan] = yield* Effect.all([resolve$1(client, artifact, from, python), resolve$1(client, artifact, to, python)], { concurrency:
|
|
6664
|
+
const [fromPlan, toPlan] = yield* Effect.all([resolve$1(client, artifact, from, python), resolve$1(client, artifact, to, python)], { concurrency: "unbounded" });
|
|
6645
6665
|
const plans = {
|
|
6646
6666
|
from: fromPlan,
|
|
6647
6667
|
to: toPlan
|
|
@@ -6734,7 +6754,7 @@ const resolveDependencies = Effect.fn("npmDependencies.resolve")(function* (clie
|
|
|
6734
6754
|
const outcomes = yield* Effect.forEach(changedNames, (dependency) => Effect.gen(function* () {
|
|
6735
6755
|
if (!packageName.test(dependency)) return Result.fail("its name is not a valid registry package name");
|
|
6736
6756
|
const side = (declared) => declared === void 0 ? Effect.undefined : resolveSide(client, host, dependency, declared);
|
|
6737
|
-
const resolved = yield* Effect.result(Effect.all([side(before.get(dependency)), side(after.get(dependency))], { concurrency:
|
|
6757
|
+
const resolved = yield* Effect.result(Effect.all([side(before.get(dependency)), side(after.get(dependency))], { concurrency: "unbounded" }));
|
|
6738
6758
|
return Result.isFailure(resolved) ? Result.fail(resolved.failure.issue) : Result.succeed(resolved.success);
|
|
6739
6759
|
}), { concurrency: 4 });
|
|
6740
6760
|
for (const [index, dependency] of changedNames.entries()) {
|
|
@@ -6841,8 +6861,8 @@ const materializeDependencies = Effect.fn("npmDependencies.materialize")(functio
|
|
|
6841
6861
|
issue: error instanceof Error ? error.message : String(error)
|
|
6842
6862
|
})
|
|
6843
6863
|
});
|
|
6844
|
-
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:
|
|
6845
|
-
const summaries = yield* writeFileDiff(workspace, files.dependencyListFrom, files.dependencyListTo, record(dependencies, dependencies.changed.map((dependency, index) => ({
|
|
6864
|
+
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 });
|
|
6865
|
+
const [summaries, trees] = yield* Effect.all([writeFileDiff(workspace, files.dependencyListFrom, files.dependencyListTo, record(dependencies, dependencies.changed.map((dependency, index) => ({
|
|
6846
6866
|
name: dependency.name,
|
|
6847
6867
|
...dependency.from === void 0 ? {} : { resolved: dependency.from },
|
|
6848
6868
|
...sides[index]?.[0] === void 0 ? {} : { fetched: sides[index][0] }
|
|
@@ -6850,8 +6870,7 @@ const materializeDependencies = Effect.fn("npmDependencies.materialize")(functio
|
|
|
6850
6870
|
name: dependency.name,
|
|
6851
6871
|
...dependency.to === void 0 ? {} : { resolved: dependency.to },
|
|
6852
6872
|
...sides[index]?.[1] === void 0 ? {} : { fetched: sides[index][1] }
|
|
6853
|
-
}))));
|
|
6854
|
-
const trees = yield* treeDiff(workspace, files.dependenciesFrom, files.dependenciesTo);
|
|
6873
|
+
})))), treeDiff(workspace, files.dependenciesFrom, files.dependenciesTo)], { concurrency: "unbounded" });
|
|
6855
6874
|
const failed = dependencies.changed.filter((_, index) => (sides[index] ?? []).some((side) => side?.error !== void 0));
|
|
6856
6875
|
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)" : ""}`);
|
|
6857
6876
|
const noneIncluded = dependencies.omitted.length === 0 ? "No direct dependency's declared version changed." : "No changed direct dependency is compared.";
|
|
@@ -6884,10 +6903,12 @@ const materializeDependencies = Effect.fn("npmDependencies.materialize")(functio
|
|
|
6884
6903
|
// src/audit/registry-evidence.ts
|
|
6885
6904
|
const registryDetail = "Both published packages were downloaded and their checksums verified. File types and modes, and any native binaries, are summarised without running them.";
|
|
6886
6905
|
const registryWorkspace = Effect.fn("evidence.registryWorkspace")(function* (client, workspace, identity, plans, npm, key) {
|
|
6887
|
-
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:
|
|
6888
|
-
const artifacts = yield*
|
|
6889
|
-
|
|
6890
|
-
|
|
6906
|
+
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" });
|
|
6907
|
+
const [artifacts, trees, binaries] = yield* Effect.all([
|
|
6908
|
+
writeFileDiff(workspace, files.artifactFrom, files.artifactTo, manifestJson(from), manifestJson(to)),
|
|
6909
|
+
treeDiff(workspace, files.from, files.to),
|
|
6910
|
+
writeFileDiff(workspace, files.binaryFrom, files.binaryTo, json(binarySummaries(from)), json(binarySummaries(to)))
|
|
6911
|
+
], { concurrency: "unbounded" });
|
|
6891
6912
|
const treeDetail = [registryDetail, partial("artifact", trees.omitted)].filter((sentence) => sentence !== "").join(" ");
|
|
6892
6913
|
if (npm === void 0) return yield* write(workspace, identity, Buffer.concat([
|
|
6893
6914
|
artifacts,
|
|
@@ -6921,7 +6942,7 @@ const npmExtras = Effect.fn("evidence.npmExtras")(function* (client, name, from,
|
|
|
6921
6942
|
cpu: process.arch
|
|
6922
6943
|
});
|
|
6923
6944
|
const verifier = yield* Effect.cached(sigstoreVerifier);
|
|
6924
|
-
const [fromSource, toSource] = yield* Effect.all([npmSource(client, name, from, fromManifest, verifier), npmSource(client, name, to, toManifest, verifier)], { concurrency:
|
|
6945
|
+
const [fromSource, toSource] = yield* Effect.all([npmSource(client, name, from, fromManifest, verifier), npmSource(client, name, to, toManifest, verifier)], { concurrency: "unbounded" });
|
|
6925
6946
|
const { directory } = npmRepository(toManifest);
|
|
6926
6947
|
return {
|
|
6927
6948
|
dependencies,
|
|
@@ -6938,8 +6959,8 @@ const prepareRegistry = Effect.fn("evidence.prepareRegistry")(function* (client,
|
|
|
6938
6959
|
const manifests = ecosystem === "npm" ? yield* Effect.all({
|
|
6939
6960
|
from: npmVersion(client, artifact, from),
|
|
6940
6961
|
to: npmVersion(client, artifact, to)
|
|
6941
|
-
}, { concurrency:
|
|
6942
|
-
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:
|
|
6962
|
+
}, { concurrency: "unbounded" }) : void 0;
|
|
6963
|
+
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" });
|
|
6943
6964
|
const plans = {
|
|
6944
6965
|
from: fromPlan,
|
|
6945
6966
|
to: toPlan
|
|
@@ -7314,10 +7335,9 @@ const layer$5 = (cacheDir) => Layer.effect(Audits, Effect.gen(function* () {
|
|
|
7314
7335
|
}));
|
|
7315
7336
|
const started = yield* Clock.currentTimeMillis;
|
|
7316
7337
|
const base = path.join(root, "workspaces");
|
|
7317
|
-
|
|
7318
|
-
if (name.endsWith(".lock")) continue;
|
|
7338
|
+
yield* Effect.forEach((yield* children(base)).filter((name) => !name.endsWith(".lock")), (name) => {
|
|
7319
7339
|
const workspace = path.join(base, name);
|
|
7320
|
-
|
|
7340
|
+
return Effect.scoped(Effect.gen(function* () {
|
|
7321
7341
|
if (!(yield* tryHold(workspace))) return;
|
|
7322
7342
|
const prepared = yield* Effect.promise(async () => await stat(path.join(workspace, files.meta)).then((info) => info.mtimeMs, () => 0));
|
|
7323
7343
|
if (started - prepared > Duration.toMillis(keptWorkspaces)) {
|
|
@@ -7325,9 +7345,15 @@ const layer$5 = (cacheDir) => Layer.effect(Audits, Effect.gen(function* () {
|
|
|
7325
7345
|
return;
|
|
7326
7346
|
}
|
|
7327
7347
|
const entries = yield* Effect.promise(async () => await readdir(workspace, { withFileTypes: true }).catch(() => []));
|
|
7328
|
-
|
|
7348
|
+
yield* Effect.forEach(entries.filter((entry) => entry.isDirectory()), (entry) => discard(path.join(workspace, entry.name), name), {
|
|
7349
|
+
concurrency: "unbounded",
|
|
7350
|
+
discard: true
|
|
7351
|
+
});
|
|
7329
7352
|
})).pipe(Effect.ignore);
|
|
7330
|
-
}
|
|
7353
|
+
}, {
|
|
7354
|
+
concurrency: 8,
|
|
7355
|
+
discard: true
|
|
7356
|
+
});
|
|
7331
7357
|
yield* Effect.forkScoped(Effect.promise(async () => {
|
|
7332
7358
|
for (const name of await readdir(trash).catch(() => [])) await rm(path.join(trash, name), {
|
|
7333
7359
|
recursive: true,
|
|
@@ -7544,12 +7570,15 @@ const workflowFiles = Effect.fn("workflowFiles")(function* (root) {
|
|
|
7544
7570
|
for (const name of ["action.yml", "action.yaml"]) if (yield* present(path.join(dir, name))) return Option.some(path.join(dir, name));
|
|
7545
7571
|
return Option.none();
|
|
7546
7572
|
});
|
|
7547
|
-
const workflows = yield*
|
|
7548
|
-
|
|
7573
|
+
const [workflows, actions, own] = yield* Effect.all([
|
|
7574
|
+
listed(path.join(".github", "workflows"), false),
|
|
7575
|
+
listed(path.join(".github", "actions"), true),
|
|
7576
|
+
actionFile("")
|
|
7577
|
+
], { concurrency: "unbounded" });
|
|
7549
7578
|
const pending = [
|
|
7550
7579
|
...workflows.filter((file) => /\.ya?ml$/u.test(file)),
|
|
7551
7580
|
...actions.filter((file) => /^action\.ya?ml$/u.test(path.basename(file))),
|
|
7552
|
-
...Option.toArray(
|
|
7581
|
+
...Option.toArray(own)
|
|
7553
7582
|
];
|
|
7554
7583
|
const seen = /* @__PURE__ */ new Set();
|
|
7555
7584
|
const files = [];
|
|
@@ -7874,7 +7903,7 @@ const discover$4 = Effect.fn("GitHubActions.discover")(function* (dir) {
|
|
|
7874
7903
|
".github",
|
|
7875
7904
|
"action.yml",
|
|
7876
7905
|
"action.yaml"
|
|
7877
|
-
], exists)).length === 0) return [];
|
|
7906
|
+
], exists, { concurrency: "unbounded" })).length === 0) return [];
|
|
7878
7907
|
const origin = yield* exec.stdout({
|
|
7879
7908
|
command: "git",
|
|
7880
7909
|
args: [
|
|
@@ -7889,7 +7918,7 @@ const discover$4 = Effect.fn("GitHubActions.discover")(function* (dir) {
|
|
|
7889
7918
|
});
|
|
7890
7919
|
// src/process/on-path.ts
|
|
7891
7920
|
const executable = (file) => Effect.promise(async () => await access(file, constants.X_OK).then(() => true, () => false));
|
|
7892
|
-
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)));
|
|
7921
|
+
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)));
|
|
7893
7922
|
// src/process/Sudo.ts
|
|
7894
7923
|
var Sudo = class extends Context.Service()("upup/Sudo") {};
|
|
7895
7924
|
const asRoot = (invocation) => [
|
|
@@ -8483,8 +8512,7 @@ const read = Effect.fn("bundles.read")(function* (bundle, arch) {
|
|
|
8483
8512
|
const short = nonEmpty(info.value.CFBundleShortVersionString);
|
|
8484
8513
|
const build = nonEmpty(info.value.CFBundleVersion);
|
|
8485
8514
|
const executable = nonEmpty(info.value.CFBundleExecutable);
|
|
8486
|
-
const sparkle = yield* sparkleKeysOf(bundle, info.value);
|
|
8487
|
-
const source = yield* sourceOf(bundle, bundleId, nonEmpty(info.value.SUFeedURL), arch);
|
|
8515
|
+
const [sparkle, source] = yield* Effect.all([sparkleKeysOf(bundle, info.value), sourceOf(bundle, bundleId, nonEmpty(info.value.SUFeedURL), arch)], { concurrency: "unbounded" });
|
|
8488
8516
|
return Option.some({
|
|
8489
8517
|
path: bundle,
|
|
8490
8518
|
name: path.basename(bundle, ".app"),
|
|
@@ -8497,23 +8525,25 @@ const read = Effect.fn("bundles.read")(function* (bundle, arch) {
|
|
|
8497
8525
|
...plist === void 0 ? {} : { plist }
|
|
8498
8526
|
});
|
|
8499
8527
|
});
|
|
8500
|
-
const
|
|
8528
|
+
const isApp = (entry) => entry.endsWith(".app");
|
|
8529
|
+
const isLeftover = (entry) => entry.endsWith(".app.upup-old");
|
|
8530
|
+
const wanted$1 = (entry) => isApp(entry) || isLeftover(entry);
|
|
8531
|
+
const walk = Effect.fn("bundles.walk")(function* (roots) {
|
|
8501
8532
|
const fs = yield* FileSystem.FileSystem;
|
|
8502
8533
|
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))));
|
|
8503
8534
|
const isLink = (entry) => fs.readLink(entry).pipe(Effect.isSuccess);
|
|
8504
8535
|
const isDirectory = (entry) => fs.stat(entry).pipe(Effect.map((info) => info.type === "Directory"), Effect.orElseSucceed(() => false));
|
|
8505
|
-
const
|
|
8506
|
-
|
|
8507
|
-
if (yield* isLink(entry))
|
|
8508
|
-
if (wanted(entry))
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8536
|
+
const nestedIn = (folder) => entries(folder).pipe(Effect.flatMap((listed) => Effect.filter(listed.filter(wanted$1), (nested) => Effect.map(isLink(nested), (link) => !link), { concurrency: "unbounded" })));
|
|
8537
|
+
const walked = (yield* Effect.forEach(roots, (root) => entries(root).pipe(Effect.flatMap((listed) => Effect.forEach(listed, (entry) => Effect.gen(function* () {
|
|
8538
|
+
if (yield* isLink(entry)) return [];
|
|
8539
|
+
if (wanted$1(entry)) return [entry];
|
|
8540
|
+
return !/\.(?:app|localized|upup-old|upup-new)$/u.test(entry) && (yield* isDirectory(entry)) ? yield* nestedIn(entry) : [];
|
|
8541
|
+
}), { concurrency: "unbounded" })), Effect.map((listed) => listed.flat())), { concurrency: "unbounded" })).flat();
|
|
8542
|
+
return {
|
|
8543
|
+
apps: walked.filter(isApp),
|
|
8544
|
+
leftovers: walked.filter(isLeftover)
|
|
8545
|
+
};
|
|
8514
8546
|
});
|
|
8515
|
-
const paths = (roots) => walk(roots, (entry) => entry.endsWith(".app"));
|
|
8516
|
-
const leftovers = (roots) => walk(roots, (entry) => entry.endsWith(".app.upup-old"));
|
|
8517
8547
|
// src/apps/running.ts
|
|
8518
8548
|
const command$4 = (name, args) => ({
|
|
8519
8549
|
command: name,
|
|
@@ -8576,7 +8606,7 @@ const command$3 = (name, args) => ({
|
|
|
8576
8606
|
});
|
|
8577
8607
|
const host = Effect.gen(function* () {
|
|
8578
8608
|
const exec = yield* Exec;
|
|
8579
|
-
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:
|
|
8609
|
+
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" });
|
|
8580
8610
|
return {
|
|
8581
8611
|
arch: arm64 ? "arm64" : "x86_64",
|
|
8582
8612
|
os
|
|
@@ -8654,7 +8684,7 @@ const stage = Effect.fn("install.stage")(function* (client, wanted) {
|
|
|
8654
8684
|
const found = (yield* Effect.forEach(candidates, (candidate) => readInfo(candidate).pipe(Effect.map((info) => ({
|
|
8655
8685
|
path: candidate,
|
|
8656
8686
|
info
|
|
8657
|
-
}))))).find(({ info }) => Option.isSome(info) && info.value.CFBundleIdentifier === wanted.bundleId);
|
|
8687
|
+
}))), { concurrency: 4 })).find(({ info }) => Option.isSome(info) && info.value.CFBundleIdentifier === wanted.bundleId);
|
|
8658
8688
|
if (found === void 0 || Option.isNone(found.info)) return yield* refused(`its download holds no app with the bundle ID ${wanted.bundleId}`);
|
|
8659
8689
|
const app = found.path;
|
|
8660
8690
|
const info = found.info.value;
|
|
@@ -8673,7 +8703,7 @@ const stage = Effect.fn("install.stage")(function* (client, wanted) {
|
|
|
8673
8703
|
])).pipe(Effect.mapError((error) => refused(`codesign failed: ${error.output.trim()}`)), Effect.flatMap((output) => {
|
|
8674
8704
|
const hash = /^CDHash=(?<hash>[0-9a-f]+)$/mu.exec(output.stderr)?.groups?.["hash"];
|
|
8675
8705
|
return hash === void 0 ? Effect.fail(unsigned(output.stderr.trim())) : Effect.succeed(hash);
|
|
8676
|
-
}))).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 ")})`));
|
|
8706
|
+
})), { 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 ")})`));
|
|
8677
8707
|
const signed = yield* exec.run(command$2("codesign", [
|
|
8678
8708
|
"--verify",
|
|
8679
8709
|
"--deep",
|
|
@@ -8726,19 +8756,11 @@ const Manifest$2 = Schema.fromJsonString(Schema.Struct({
|
|
|
8726
8756
|
}));
|
|
8727
8757
|
const zipName = "upup-sparkle.zip";
|
|
8728
8758
|
const manifestName = "upup-sparkle.json";
|
|
8729
|
-
const seaKeys = {
|
|
8730
|
-
zip: `sparkle/${zipName}`,
|
|
8731
|
-
manifest: `sparkle/${manifestName}`
|
|
8732
|
-
};
|
|
8733
8759
|
const decode = (text, zip) => Schema.decodeEffect(Manifest$2)(text).pipe(Effect.map((manifest) => ({
|
|
8734
8760
|
zip,
|
|
8735
8761
|
...manifest
|
|
8736
8762
|
})), Effect.option);
|
|
8737
8763
|
const folder = () => {
|
|
8738
|
-
if (isSea()) {
|
|
8739
|
-
const assets = process.env["OTUI_ASSET_ROOT"];
|
|
8740
|
-
return assets === void 0 ? void 0 : path.join(assets, path.dirname(seaKeys.zip));
|
|
8741
|
-
}
|
|
8742
8764
|
const packageJson = findPackageJSON(import.meta.url);
|
|
8743
8765
|
return packageJson === void 0 ? void 0 : path.join(path.dirname(packageJson), "native", "dist");
|
|
8744
8766
|
};
|
|
@@ -8820,9 +8842,16 @@ ready() {
|
|
|
8820
8842
|
return 1
|
|
8821
8843
|
fi
|
|
8822
8844
|
}
|
|
8823
|
-
# A copy of what upup handed over, where only this user can write; a failure is step $1's.
|
|
8845
|
+
# A copy of what upup handed over, where only this user can write; a failure is step $1's. Others
|
|
8846
|
+
# may also read a shared one ($2): as root, Sparkle starts its progress agent from upup-sparkle's
|
|
8847
|
+
# framework as the user, so it goes in /var/tmp, whose sticky bit keeps others from moving it. An
|
|
8848
|
+
# app's copy stays private, since its owner gets it after it is checked.
|
|
8824
8849
|
secure() {
|
|
8825
|
-
|
|
8850
|
+
if [ "$mode" = root ] && [ "$2" = shared ]; then
|
|
8851
|
+
private=$(mktemp -d /var/tmp/upup.XXXXXX 2>"$log") && chmod 755 "$private" 2>"$log" || { fail "$1"; return 1; }
|
|
8852
|
+
else
|
|
8853
|
+
private=$(mktemp -d 2>"$log") || { fail "$1"; return 1; }
|
|
8854
|
+
fi
|
|
8826
8855
|
payload="$private/$(basename "$source")"
|
|
8827
8856
|
"$ditto" "$source" "$payload" 2>"$log" || { fail "$1"; return 1; }
|
|
8828
8857
|
}
|
|
@@ -8836,7 +8865,7 @@ app() {
|
|
|
8836
8865
|
if [ -e "$old" ] && [ ! -e "$dest" ]; then recover; return; fi
|
|
8837
8866
|
rm -rf "$old" "$new" 2>"$log" || { fail cleanup; return; }
|
|
8838
8867
|
if [ "$mode" = root ]; then
|
|
8839
|
-
secure copy || return
|
|
8868
|
+
secure copy private || return
|
|
8840
8869
|
"$codesign" --verify --deep --strict -R "$check" "$payload" 2>"$log" || { fail recheck; return; }
|
|
8841
8870
|
owner=$("$stat" -f %u:%g "$dest" 2>"$log") && "$chown" -R "$owner" "$payload" 2>"$log" || { fail copy; return; }
|
|
8842
8871
|
staged=$payload
|
|
@@ -8866,7 +8895,7 @@ app() {
|
|
|
8866
8895
|
# upup-sparkle's exit status: 0 installed, 2 nothing to install, 3 Sparkle would install another
|
|
8867
8896
|
# build, 4 the app is open; its last line on standard error says why.
|
|
8868
8897
|
sparkle() {
|
|
8869
|
-
secure helper || return
|
|
8898
|
+
secure helper shared || return
|
|
8870
8899
|
sum=$("$shasum" -a 256 "$payload" 2>"$log") || { fail helper; return; }
|
|
8871
8900
|
[ "\${sum%% *}" = "$check" ] || { echo "its sha256 is \${sum%% *}, not $check" >"$log"; fail helper; return; }
|
|
8872
8901
|
"$ditto" -x -k "$payload" "$private" 2>"$log" || { fail helper; return; }
|
|
@@ -9156,16 +9185,27 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9156
9185
|
}
|
|
9157
9186
|
};
|
|
9158
9187
|
});
|
|
9188
|
+
const leftoverNotice = (old) => {
|
|
9189
|
+
const app = old.slice(0, -9);
|
|
9190
|
+
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)}` })));
|
|
9191
|
+
};
|
|
9192
|
+
const scan = Effect.gen(function* () {
|
|
9193
|
+
const [here, walked] = yield* Effect.all([host, walk(options.roots)], { concurrency: "unbounded" });
|
|
9194
|
+
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" });
|
|
9195
|
+
return {
|
|
9196
|
+
here,
|
|
9197
|
+
found: bundles.flatMap((bundle) => Option.toArray(bundle)),
|
|
9198
|
+
left
|
|
9199
|
+
};
|
|
9200
|
+
});
|
|
9159
9201
|
const list = Effect.gen(function* () {
|
|
9160
|
-
const owners = yield* Effect.result(casks);
|
|
9202
|
+
const [owners, { here, found, left }] = yield* Effect.all([Effect.result(casks), scan], { concurrency: "unbounded" });
|
|
9161
9203
|
if (Result.isFailure(owners)) return {
|
|
9162
9204
|
packages: [],
|
|
9163
9205
|
notices: [{ message: `upup lists no apps while it cannot read which apps Homebrew installed: ${describeError(owners.failure)}` }]
|
|
9164
9206
|
};
|
|
9165
|
-
const here = yield* host;
|
|
9166
9207
|
const locale = yield* Effect.cached(country);
|
|
9167
9208
|
const caskOf = new Map(owners.success.flatMap((cask) => caskApps(cask).map((app) => [app, cask])));
|
|
9168
|
-
const found = (yield* Effect.forEach(yield* paths(options.roots), (app) => read(app, here.arch), { concurrency: inFlight })).flatMap((bundle) => Option.toArray(bundle));
|
|
9169
9209
|
const ids = /* @__PURE__ */ new Set();
|
|
9170
9210
|
const kept = found.filter((bundle) => {
|
|
9171
9211
|
const cask = caskOf.get(path.basename(bundle.path));
|
|
@@ -9193,13 +9233,9 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9193
9233
|
here,
|
|
9194
9234
|
rows: new Map(all.map((row) => [row.pkg.id, row.listed]))
|
|
9195
9235
|
});
|
|
9196
|
-
const leftovers$1 = yield* Effect.forEach(yield* leftovers(options.roots), (old) => {
|
|
9197
|
-
const app = old.slice(0, -9);
|
|
9198
|
-
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)}` })));
|
|
9199
|
-
});
|
|
9200
9236
|
return {
|
|
9201
9237
|
packages: all.map((row) => row.pkg),
|
|
9202
|
-
notices: [...all.flatMap((row) => row.notice), ...
|
|
9238
|
+
notices: [...all.flatMap((row) => row.notice), ...left]
|
|
9203
9239
|
};
|
|
9204
9240
|
}).pipe(Effect.withSpan("Apps.list"), provide);
|
|
9205
9241
|
const observe = Effect.fn("Apps.observe")(function* (pkg) {
|
|
@@ -9234,7 +9270,7 @@ const makeWith = (options) => Effect.gen(function* () {
|
|
|
9234
9270
|
const ids = yield* Effect.forEach(paths, (app) => readInfo(app).pipe(Effect.flatMap((info) => Option.match(info, {
|
|
9235
9271
|
onNone: () => Effect.fail(new Unsupported({ reason: `upup could not read ${app}` })),
|
|
9236
9272
|
onSome: (found) => Effect.succeed(found.CFBundleIdentifier)
|
|
9237
|
-
}))));
|
|
9273
|
+
}))), { concurrency: inFlight });
|
|
9238
9274
|
const found = {
|
|
9239
9275
|
paths,
|
|
9240
9276
|
ids: new Set(ids)
|
|
@@ -9515,7 +9551,7 @@ const nestedDirs = Effect.fn("nestedDirs")(function* (dir, names) {
|
|
|
9515
9551
|
args,
|
|
9516
9552
|
cwd: dir
|
|
9517
9553
|
}).pipe(Effect.orElseSucceed(() => ""));
|
|
9518
|
-
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:
|
|
9554
|
+
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" });
|
|
9519
9555
|
const files = listed.split("\0").filter((file) => {
|
|
9520
9556
|
const folders = file.split("/").slice(0, -1);
|
|
9521
9557
|
return folders.length > 0 && !folders.includes("node_modules") && !(path.basename(file) === "go.mod" && ignoredByGo(folders));
|
|
@@ -9542,8 +9578,7 @@ const failing = (name, error) => ({
|
|
|
9542
9578
|
observe: () => Effect.succeedNone
|
|
9543
9579
|
});
|
|
9544
9580
|
const locateAll = Effect.fn("locateAll")(function* (dir, nested, manifest, locate) {
|
|
9545
|
-
const own = yield* locate(dir);
|
|
9546
|
-
const below = yield* Effect.forEach(nested, (from) => Effect.map(Effect.result(locate(from)), (result) => [from, result]), { concurrency: 8 });
|
|
9581
|
+
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" });
|
|
9547
9582
|
const found = /* @__PURE__ */ new Map();
|
|
9548
9583
|
const claimed = /* @__PURE__ */ new Map();
|
|
9549
9584
|
const failed = [];
|
|
@@ -9706,7 +9741,7 @@ const make$9 = (root, place = "") => Effect.gen(function* () {
|
|
|
9706
9741
|
const entries = (manifest) => manifestOf(manifest).pipe(Effect.map((parsed) => entriesOf(manifest, parsed)));
|
|
9707
9742
|
const workspace = Effect.gen(function* () {
|
|
9708
9743
|
const resolved = resolvedByMember(root, yield* metadata);
|
|
9709
|
-
const manifests = yield* Effect.forEach(/* @__PURE__ */ new Set(["Cargo.toml", ...resolved.keys()]), (manifest) => manifestOf(manifest).pipe(Effect.map((parsed) => [manifest, parsed])));
|
|
9744
|
+
const manifests = yield* Effect.forEach(/* @__PURE__ */ new Set(["Cargo.toml", ...resolved.keys()]), (manifest) => manifestOf(manifest).pipe(Effect.map((parsed) => [manifest, parsed])), { concurrency: 8 });
|
|
9710
9745
|
const inheritors = (key) => manifests.flatMap(([manifest, parsed]) => resolved.has(manifest) && inheritedKeys(parsed).includes(key) ? [manifest] : []);
|
|
9711
9746
|
return {
|
|
9712
9747
|
resolved,
|
|
@@ -10011,9 +10046,11 @@ const make$8 = Effect.gen(function* () {
|
|
|
10011
10046
|
});
|
|
10012
10047
|
const isPlainExecutable = Effect.fn("GoInstall.isPlainExecutable")(function* (dir, file) {
|
|
10013
10048
|
const binary = path.join(dir, file);
|
|
10014
|
-
const info = yield*
|
|
10015
|
-
|
|
10016
|
-
|
|
10049
|
+
const [info, real, realDir] = yield* Effect.all([
|
|
10050
|
+
fs.stat(binary).pipe(Effect.mapError(fileFailed("stat", binary))),
|
|
10051
|
+
fs.realPath(binary).pipe(Effect.mapError(fileFailed("resolve", binary))),
|
|
10052
|
+
fs.realPath(dir).pipe(Effect.mapError(fileFailed("resolve", dir)))
|
|
10053
|
+
], { concurrency: "unbounded" });
|
|
10017
10054
|
return info.type === "File" && (info.mode & 73) !== 0 && real === path.join(realDir, file);
|
|
10018
10055
|
});
|
|
10019
10056
|
const latest = Effect.fn("GoInstall.latest")(function* (modules) {
|
|
@@ -10172,8 +10209,7 @@ const make$7 = (root, moduleDir, place = "", local = /* @__PURE__ */ new Set())
|
|
|
10172
10209
|
]);
|
|
10173
10210
|
const origin = exec.stdout(env).pipe(Effect.flatMap(decodeGoJson(GoProxyEnv, commandLine(env))), Effect.map(withGoOrigin));
|
|
10174
10211
|
const list = Effect.gen(function* () {
|
|
10175
|
-
const direct = yield* requirements;
|
|
10176
|
-
const located = yield* origin;
|
|
10212
|
+
const [direct, located] = yield* Effect.all([requirements, origin], { concurrency: "unbounded" });
|
|
10177
10213
|
const entries = yield* selected(direct.map((requirement) => requirement.Path), true);
|
|
10178
10214
|
const notices = [];
|
|
10179
10215
|
return {
|
|
@@ -10235,17 +10271,17 @@ const locate$1 = Effect.fn("GoModule.locate")(function* (dir) {
|
|
|
10235
10271
|
const discover$2 = Effect.fn("GoModule.discover")(function* (dir, nested = []) {
|
|
10236
10272
|
const exec = yield* Exec;
|
|
10237
10273
|
const { found, failed } = yield* locateAll(dir, nested, "go.mod", locate$1);
|
|
10238
|
-
const
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
if (!work) {
|
|
10242
|
-
listed.add(root);
|
|
10243
|
-
return [{
|
|
10274
|
+
const works = yield* Effect.forEach(found, ({ root, work, claimed }) => Effect.gen(function* () {
|
|
10275
|
+
if (!work) return {
|
|
10276
|
+
modules: [{
|
|
10244
10277
|
root,
|
|
10245
10278
|
moduleDir: root,
|
|
10246
10279
|
local: /* @__PURE__ */ new Set()
|
|
10247
|
-
}]
|
|
10248
|
-
|
|
10280
|
+
}],
|
|
10281
|
+
failed: [],
|
|
10282
|
+
covered: [root]
|
|
10283
|
+
};
|
|
10284
|
+
const covered = [];
|
|
10249
10285
|
const place = placeOf$1(dir, root);
|
|
10250
10286
|
const scanned = yield* Effect.gen(function* () {
|
|
10251
10287
|
const edit = {
|
|
@@ -10259,7 +10295,7 @@ const discover$2 = Effect.fn("GoModule.discover")(function* (dir, nested = []) {
|
|
|
10259
10295
|
};
|
|
10260
10296
|
const file = yield* exec.stdout(edit).pipe(Effect.flatMap(decodeGoJson(WorkFile, commandLine(edit))));
|
|
10261
10297
|
const used = (file.Use ?? []).map((use) => path.resolve(root, use.DiskPath));
|
|
10262
|
-
|
|
10298
|
+
covered.push(...used);
|
|
10263
10299
|
const paths = yield* Effect.forEach(used, (moduleDir) => Effect.gen(function* () {
|
|
10264
10300
|
const mod = yield* readModFile(exec, moduleDir);
|
|
10265
10301
|
if (mod.Module === void 0) return yield* new OutputMalformed({
|
|
@@ -10275,20 +10311,27 @@ const discover$2 = Effect.fn("GoModule.discover")(function* (dir, nested = []) {
|
|
|
10275
10311
|
local
|
|
10276
10312
|
})).filter(({ moduleDir }) => place === "" || placeOf$1(dir, moduleDir) !== "");
|
|
10277
10313
|
}).pipe(Effect.result);
|
|
10278
|
-
if (Result.isSuccess(scanned)) return
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10314
|
+
if (Result.isSuccess(scanned)) return {
|
|
10315
|
+
modules: scanned.success,
|
|
10316
|
+
failed: [],
|
|
10317
|
+
covered
|
|
10318
|
+
};
|
|
10319
|
+
return {
|
|
10320
|
+
modules: [],
|
|
10321
|
+
failed: [failing(path.join(place, "go.work"), scanned.failure)],
|
|
10322
|
+
covered: [...covered, ...claimed]
|
|
10323
|
+
};
|
|
10324
|
+
}), { concurrency: 4 });
|
|
10325
|
+
const listed = new Set(works.flatMap((work) => work.covered));
|
|
10283
10326
|
const outside = nested.flatMap((moduleDir) => listed.has(moduleDir) ? [] : [{
|
|
10284
10327
|
root: moduleDir,
|
|
10285
10328
|
moduleDir,
|
|
10286
10329
|
local: /* @__PURE__ */ new Set()
|
|
10287
10330
|
}]);
|
|
10288
|
-
const providers = yield* Effect.forEach([...
|
|
10331
|
+
const providers = yield* Effect.forEach([...works.flatMap(({ modules }) => modules), ...outside], ({ root, moduleDir, local }) => make$7(root, moduleDir, placeOf$1(dir, root), local));
|
|
10289
10332
|
return [
|
|
10290
10333
|
...failed,
|
|
10291
|
-
...
|
|
10334
|
+
...works.flatMap((work) => work.failed),
|
|
10292
10335
|
...providers
|
|
10293
10336
|
];
|
|
10294
10337
|
});
|
|
@@ -10296,24 +10339,23 @@ const discover$2 = Effect.fn("GoModule.discover")(function* (dir, nested = []) {
|
|
|
10296
10339
|
var ProviderSource = class extends Context.Service()("upup/ProviderSource") {};
|
|
10297
10340
|
// src/node/release-history.ts
|
|
10298
10341
|
const minutesPerDay = 1440;
|
|
10299
|
-
const firstPnpmMajorWithDefaultReleaseAge = 11;
|
|
10300
10342
|
const gateSettings = {
|
|
10301
10343
|
pnpm: {
|
|
10302
10344
|
age: "minimumReleaseAge",
|
|
10303
10345
|
exclude: "minimumReleaseAgeExclude",
|
|
10304
10346
|
unit: 1,
|
|
10305
|
-
unset:
|
|
10347
|
+
unset: minutesPerDay
|
|
10306
10348
|
},
|
|
10307
10349
|
npm: {
|
|
10308
10350
|
age: "min-release-age",
|
|
10309
10351
|
exclude: "min-release-age-exclude",
|
|
10310
10352
|
unit: minutesPerDay,
|
|
10311
|
-
unset:
|
|
10353
|
+
unset: 0
|
|
10312
10354
|
}
|
|
10313
10355
|
};
|
|
10314
|
-
const releaseGate = (manager,
|
|
10356
|
+
const releaseGate = (manager, config) => {
|
|
10315
10357
|
const settings = gateSettings[manager];
|
|
10316
|
-
const minutes = Number(config[settings.age] ?? settings.unset
|
|
10358
|
+
const minutes = Number(config[settings.age] ?? settings.unset) * settings.unit;
|
|
10317
10359
|
return {
|
|
10318
10360
|
minutes: Number.isFinite(minutes) && minutes > 0 ? minutes : 0,
|
|
10319
10361
|
exclude: [config[settings.exclude] ?? []].flat().filter((pattern) => typeof pattern === "string")
|
|
@@ -10399,8 +10441,7 @@ const releaseOffers = Effect.fn("releaseOffers")(function* (pins, manager, run,
|
|
|
10399
10441
|
const reads = yield* Effect.result(Effect.forEach(artifacts, read, { concurrency: 8 }));
|
|
10400
10442
|
const histories = new Map(Result.isSuccess(reads) ? reads.success : []);
|
|
10401
10443
|
const notices = Result.isFailure(reads) ? [{ message: `release histories could not be read, so upup offers nothing: ${describeError(reads.failure)}` }] : [];
|
|
10402
|
-
const
|
|
10403
|
-
const gate = releaseGate(manager, version.trim(), config);
|
|
10444
|
+
const gate = releaseGate(manager, config);
|
|
10404
10445
|
const offer = (pin) => {
|
|
10405
10446
|
const history = histories.get(pin.artifact);
|
|
10406
10447
|
if (history === void 0) return Result.succeed(void 0);
|
|
@@ -10511,7 +10552,7 @@ const makeGlobal = (spec) => Effect.gen(function* () {
|
|
|
10511
10552
|
installed,
|
|
10512
10553
|
latest,
|
|
10513
10554
|
readNpmConfig(spec.config, "/").pipe(Effect.provideService(Exec, exec))
|
|
10514
|
-
], { concurrency:
|
|
10555
|
+
], { concurrency: "unbounded" });
|
|
10515
10556
|
const { offer, notices: unread } = yield* releaseOffers(packages.filter((pkg) => isPrerelease(pkg.current)), spec.config, (args) => ({
|
|
10516
10557
|
command: spec.config,
|
|
10517
10558
|
args,
|
|
@@ -10742,9 +10783,10 @@ const scan = Effect.fn("NodePins.scan")(function* (root, dir, nested) {
|
|
|
10742
10783
|
const fs = yield* FileSystem.FileSystem;
|
|
10743
10784
|
const read = (file) => readText(path.join(root, file));
|
|
10744
10785
|
const exists = (file) => fs.exists(path.join(root, file)).pipe(Effect.mapError(fileFailed("stat", path.join(root, file))));
|
|
10745
|
-
const
|
|
10746
|
-
const
|
|
10747
|
-
const
|
|
10786
|
+
const [workflows, listed] = yield* Effect.all([workflowFiles(root), versionFilesOf(root, dir, nested)], { concurrency: "unbounded" });
|
|
10787
|
+
const steps = workflows.flatMap(({ file, text }) => stepPins(file, text) ?? []);
|
|
10788
|
+
const versioned = yield* Effect.filter(listed, exists, { concurrency: "unbounded" });
|
|
10789
|
+
const files = yield* Effect.forEach(versioned.toSorted(), (file) => Effect.map(read(file), (text) => filePin(file, text)), { concurrency: 8 });
|
|
10748
10790
|
return [...steps, ...files.flat()];
|
|
10749
10791
|
});
|
|
10750
10792
|
const make$6 = (root, dir = root, nested = []) => Effect.gen(function* () {
|
|
@@ -11007,26 +11049,31 @@ const make$5 = (root, manager, place = "", claimed = []) => Effect.gen(function*
|
|
|
11007
11049
|
const unlocked = claimed.filter((dir) => !lockedDirs.has(path.normalize(dir))).toSorted((a, b) => a.localeCompare(b));
|
|
11008
11050
|
if (unlocked.length > 0) notices.push({ message: `not listed: ${unlocked.join(", ")} (not in ${lockfile})` });
|
|
11009
11051
|
const catalogLocked = /* @__PURE__ */ new Map();
|
|
11010
|
-
|
|
11052
|
+
const memberManifests = yield* Effect.forEach(members.toSorted((a, b) => a.localeCompare(b)), (member) => {
|
|
11011
11053
|
const memberDir = member === "." ? "" : member;
|
|
11012
11054
|
const manifest = path.join(memberDir, "package.json");
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11055
|
+
return Effect.map(readJson(Manifest, path.join(root, manifest)), (fields) => ({
|
|
11056
|
+
member,
|
|
11057
|
+
memberDir,
|
|
11058
|
+
manifest,
|
|
11059
|
+
fields
|
|
11060
|
+
}));
|
|
11061
|
+
}, { concurrency: 8 });
|
|
11062
|
+
for (const { member, memberDir, manifest, fields } of memberManifests) for (const section of sections) for (const [name, spec] of Object.entries(fields[section] ?? {})) {
|
|
11063
|
+
const resolved = Option.match(pnpm, {
|
|
11064
|
+
onSome: (lock) => resolvedVersion(lock.importers?.[member]?.[section]?.[name]?.version),
|
|
11065
|
+
onNone: () => Option.match(npm, {
|
|
11066
|
+
onSome: (lock) => lock.packages[path.join(memberDir, "node_modules", name)]?.version ?? lock.packages[path.join("node_modules", name)]?.version,
|
|
11067
|
+
onNone: () => void 0
|
|
11068
|
+
})
|
|
11069
|
+
});
|
|
11070
|
+
if (spec.startsWith("catalog:")) {
|
|
11071
|
+
const catalog = spec.slice(8) || "default";
|
|
11072
|
+
const locked = catalogLocked.get(catalog) ?? /* @__PURE__ */ new Map();
|
|
11073
|
+
if (resolved !== void 0 && !locked.has(name)) catalogLocked.set(catalog, locked.set(name, resolved));
|
|
11074
|
+
continue;
|
|
11029
11075
|
}
|
|
11076
|
+
add(manifest, section, [section, name], name, spec, resolved);
|
|
11030
11077
|
}
|
|
11031
11078
|
if (Option.isSome(pnpm) && (yield* exists(path.join(root, "pnpm-workspace.yaml")))) {
|
|
11032
11079
|
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
@@ -11096,7 +11143,7 @@ const make$5 = (root, manager, place = "", claimed = []) => Effect.gen(function*
|
|
|
11096
11143
|
};
|
|
11097
11144
|
}).pipe(Effect.provideService(FileSystem.FileSystem, fs));
|
|
11098
11145
|
const list = Effect.gen(function* () {
|
|
11099
|
-
const [{ declared, notices }, config] = yield* Effect.all([declarations, readNpmConfig(manager, root).pipe(Effect.provideService(Exec, exec))], { concurrency:
|
|
11146
|
+
const [{ declared, notices }, config] = yield* Effect.all([declarations, readNpmConfig(manager, root).pipe(Effect.provideService(Exec, exec))], { concurrency: "unbounded" });
|
|
11100
11147
|
const origin = withNpmOrigin(npmRegistries(config));
|
|
11101
11148
|
const { offer, notices: unread } = yield* releaseOffers(declared.flatMap((entry) => entry.held === true ? [] : [entry.pkg]), manager, run, config).pipe(Effect.provideService(Exec, exec));
|
|
11102
11149
|
return {
|
|
@@ -11302,7 +11349,7 @@ const discover = Effect.fn("NodeProject.discover")(function* (dir, nested = [])
|
|
|
11302
11349
|
const installed = yield* fs.exists(lockfile).pipe(Effect.mapError(fileFailed("stat", lockfile)));
|
|
11303
11350
|
if (place !== "" && !installed) unlisted.push(`${place} (no ${lockfiles[manager]} yet)`);
|
|
11304
11351
|
else {
|
|
11305
|
-
const manifests = yield* Effect.filter(claimed, (from) => fs.exists(path.join(from, "package.json")).pipe(Effect.mapError(fileFailed("stat", from))));
|
|
11352
|
+
const manifests = yield* Effect.filter(claimed, (from) => fs.exists(path.join(from, "package.json")).pipe(Effect.mapError(fileFailed("stat", from))), { concurrency: "unbounded" });
|
|
11306
11353
|
providers.push(yield* make$5(root, manager, place, manifests.map((from) => path.relative(root, from))));
|
|
11307
11354
|
}
|
|
11308
11355
|
}
|
|
@@ -11401,10 +11448,13 @@ const make$4 = Effect.gen(function* () {
|
|
|
11401
11448
|
});
|
|
11402
11449
|
const list = Effect.gen(function* () {
|
|
11403
11450
|
const found = yield* configured;
|
|
11451
|
+
const offered = yield* Effect.forEach(found.packages, (pkg) => Effect.map(Effect.result(offer(pkg)), (target) => ({
|
|
11452
|
+
pkg,
|
|
11453
|
+
target
|
|
11454
|
+
})), { concurrency: 4 });
|
|
11404
11455
|
const notices = [...found.notices];
|
|
11405
11456
|
return {
|
|
11406
|
-
packages:
|
|
11407
|
-
const target = yield* Effect.result(offer(pkg));
|
|
11457
|
+
packages: offered.map(({ pkg, target }) => {
|
|
11408
11458
|
if (Result.isFailure(target)) {
|
|
11409
11459
|
notices.push({
|
|
11410
11460
|
rowKey: rowKey(pkg),
|
|
@@ -11413,7 +11463,7 @@ const make$4 = Effect.gen(function* () {
|
|
|
11413
11463
|
return pkg;
|
|
11414
11464
|
}
|
|
11415
11465
|
return target.success === void 0 ? pkg : withTarget(pkg, target.success);
|
|
11416
|
-
})
|
|
11466
|
+
}),
|
|
11417
11467
|
notices
|
|
11418
11468
|
};
|
|
11419
11469
|
}).pipe(Effect.withSpan("VpEnv.list"));
|
|
@@ -11545,7 +11595,7 @@ const make$2 = Effect.gen(function* () {
|
|
|
11545
11595
|
exec.stdout(command$1("apt", ["list", "--upgradable"])).pipe(Effect.map(parseUpgradable)),
|
|
11546
11596
|
exec.stdout(command$1("apt-mark", ["showhold"])).pipe(Effect.map((text) => new Set(text.split("\n").filter((line) => line !== "")))),
|
|
11547
11597
|
restart
|
|
11548
|
-
], { concurrency:
|
|
11598
|
+
], { concurrency: "unbounded" });
|
|
11549
11599
|
const packages = upgradable.map((entry) => {
|
|
11550
11600
|
const pkg = packageOf(entry, arch);
|
|
11551
11601
|
return holds.has(entry.name) || holds.has(pkg.id) ? pkg : withTarget(pkg, entry.version);
|
|
@@ -11690,7 +11740,7 @@ const make$1 = Effect.gen(function* () {
|
|
|
11690
11740
|
running,
|
|
11691
11741
|
scan(args),
|
|
11692
11742
|
Ref.get(installed)
|
|
11693
|
-
], { concurrency:
|
|
11743
|
+
], { concurrency: "unbounded" });
|
|
11694
11744
|
return {
|
|
11695
11745
|
packages: packagesOf$1(version, offers.filter((offer) => !done.has(offer.label))),
|
|
11696
11746
|
done
|
|
@@ -11775,7 +11825,7 @@ const make = Effect.gen(function* () {
|
|
|
11775
11825
|
"list",
|
|
11776
11826
|
"--outdated",
|
|
11777
11827
|
"--show-python"
|
|
11778
|
-
])).pipe(Effect.map(parseTools))], { concurrency:
|
|
11828
|
+
])).pipe(Effect.map(parseTools))], { concurrency: "unbounded" });
|
|
11779
11829
|
const targets = new Map(outdated.flatMap((tool) => tool.fromPyPI && tool.latest !== void 0 ? [[tool.pkg.id, tool.latest]] : []));
|
|
11780
11830
|
const index = privatePypiIndex();
|
|
11781
11831
|
return {
|
|
@@ -11824,8 +11874,8 @@ const system$1 = [
|
|
|
11824
11874
|
["go", make$8],
|
|
11825
11875
|
["uv", make]
|
|
11826
11876
|
];
|
|
11827
|
-
const available = (entries) => Effect.filter([...new Set(entries.map(([command]) => command))], onPath).pipe(Effect.map((found) => entries.filter(([command]) => found.includes(command))));
|
|
11828
|
-
const systemProviders = available(system$1).pipe(Effect.flatMap((found) => Effect.forEach(found, ([, make]) => make)));
|
|
11877
|
+
const available = (entries) => Effect.filter([...new Set(entries.map(([command]) => command))], onPath, { concurrency: "unbounded" }).pipe(Effect.map((found) => entries.filter(([command]) => found.includes(command))));
|
|
11878
|
+
const systemProviders = available(system$1).pipe(Effect.flatMap((found) => Effect.forEach(found, ([, make]) => make, { concurrency: "unbounded" })));
|
|
11829
11879
|
const project$1 = [
|
|
11830
11880
|
["go", {
|
|
11831
11881
|
manifests: ["go.mod"],
|
|
@@ -11851,9 +11901,8 @@ const project$1 = [
|
|
|
11851
11901
|
const manifests = project$1.flatMap(([, kind]) => kind.manifests);
|
|
11852
11902
|
const projectProviders = (given) => Effect.gen(function* () {
|
|
11853
11903
|
const dir = yield* (yield* FileSystem.FileSystem).realPath(given).pipe(Effect.orElseSucceed(() => given));
|
|
11854
|
-
const found = yield* available(project$1);
|
|
11855
|
-
|
|
11856
|
-
return (yield* Effect.forEach(found, ([, kind]) => kind.discover(dir, [...new Set(kind.manifests.flatMap(nested))]))).flat();
|
|
11904
|
+
const [found, nested] = yield* Effect.all([available(project$1), nestedDirs(dir, manifests)], { concurrency: "unbounded" });
|
|
11905
|
+
return (yield* Effect.forEach(found, ([, kind]) => kind.discover(dir, [...new Set(kind.manifests.flatMap(nested))]), { concurrency: "unbounded" })).flat();
|
|
11857
11906
|
});
|
|
11858
11907
|
const layer$3 = Layer.effect(ProviderSource, Effect.gen(function* () {
|
|
11859
11908
|
const context = yield* Effect.context();
|
|
@@ -11963,7 +12012,7 @@ const message = (version) => {
|
|
|
11963
12012
|
};
|
|
11964
12013
|
const lookup$1 = Effect.fn("npm.lookup")(function* (client, wanted) {
|
|
11965
12014
|
const path = wanted.name.replace("/", "%2F");
|
|
11966
|
-
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:
|
|
12015
|
+
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" });
|
|
11967
12016
|
const versions = packument.versions ?? {};
|
|
11968
12017
|
const latest = packument["dist-tags"]?.latest;
|
|
11969
12018
|
const repository = Predicate.isString(packument.repository) ? packument.repository : packument.repository?.url;
|
|
@@ -12003,7 +12052,7 @@ const repositoryKeys = [
|
|
|
12003
12052
|
"GitHub"
|
|
12004
12053
|
];
|
|
12005
12054
|
const lookup = Effect.fn("pypi.lookup")(function* (client, wanted) {
|
|
12006
|
-
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:
|
|
12055
|
+
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" });
|
|
12007
12056
|
const urls = project.info.project_urls ?? {};
|
|
12008
12057
|
const repository = repositoryKeys.map((key) => urls[key]).find((url) => url !== void 0);
|
|
12009
12058
|
const homepage = urls["Homepage"] ?? project.info.home_page ?? void 0;
|
|
@@ -12597,12 +12646,10 @@ const layer$1 = (cacheDir) => Layer.succeed(UpdateLog, UpdateLog.of({
|
|
|
12597
12646
|
const directory = path.join(cacheDir, "updates");
|
|
12598
12647
|
const names = (await readdir(directory).catch(() => [])).filter((name) => name.endsWith(".jsonl")).toSorted();
|
|
12599
12648
|
const found = [];
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
if (entry?.scope === scope && Date.parse(entry.at) >= since) found.push(entry);
|
|
12605
|
-
}
|
|
12649
|
+
const texts = await Promise.all(names.map(async (name) => await readFile(path.join(directory, name), "utf-8").catch(() => "")));
|
|
12650
|
+
for (const text of texts) for (const line of text.split("\n")) {
|
|
12651
|
+
const entry = Option.getOrUndefined(decodeLine(line));
|
|
12652
|
+
if (entry?.scope === scope && Date.parse(entry.at) >= since) found.push(entry);
|
|
12606
12653
|
}
|
|
12607
12654
|
return found;
|
|
12608
12655
|
})
|
|
@@ -13346,8 +13393,8 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13346
13393
|
};
|
|
13347
13394
|
});
|
|
13348
13395
|
const recallAll = Effect.gen(function* () {
|
|
13349
|
-
const
|
|
13350
|
-
const logged = yield*
|
|
13396
|
+
const before = yield* SubscriptionRef.get(state);
|
|
13397
|
+
const [found, logged] = yield* Effect.all([earlier(before), fromLog(before)], { concurrency: "unbounded" });
|
|
13351
13398
|
yield* update((latest) => ({
|
|
13352
13399
|
...latest,
|
|
13353
13400
|
installed: {
|
|
@@ -13366,15 +13413,14 @@ const layer = Layer.effect(Session, Effect.gen(function* () {
|
|
|
13366
13413
|
const identity = identityOf(pkg, current.mode);
|
|
13367
13414
|
if (identity !== void 0 && current.audits[jobKey(identity)]?._tag !== "Running") wanted.set(jobKey(identity), identity);
|
|
13368
13415
|
}
|
|
13369
|
-
yield* Effect.forEach(wanted.values(), audits.recall, {
|
|
13370
|
-
concurrency: 4,
|
|
13371
|
-
discard: true
|
|
13372
|
-
});
|
|
13373
13416
|
const offered = packagesOf(current).flatMap((pkg) => {
|
|
13374
13417
|
const identity = pkg.target === void 0 ? void 0 : identityOf(pkg, current.mode);
|
|
13375
13418
|
return identity === void 0 ? [] : [identity];
|
|
13376
13419
|
});
|
|
13377
|
-
const majors = yield* Effect.
|
|
13420
|
+
const [, majors] = yield* Effect.all([Effect.forEach(wanted.values(), audits.recall, {
|
|
13421
|
+
concurrency: 4,
|
|
13422
|
+
discard: true
|
|
13423
|
+
}), Effect.forEach(offered, (identity) => audits.majors(identity).pipe(Effect.map((identityMajors) => [jobKey(identity), identityMajors])), { concurrency: 4 })], { concurrency: "unbounded" });
|
|
13378
13424
|
yield* update((latest) => ({
|
|
13379
13425
|
...latest,
|
|
13380
13426
|
majors: {
|