salidium 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/native/SalidiumMenuBar.swift +669 -91
- package/bundle/salidium.mjs +272 -9
- package/bundle/ui/assets/{index-oV3BVDrO.css → index-BR7P08_S.css} +1 -1
- package/bundle/ui/assets/index-DIfdilCQ.js +78 -0
- package/bundle/ui/index.html +2 -2
- package/package.json +1 -1
- package/bundle/ui/assets/index-YXUyKAPE.js +0 -78
package/bundle/salidium.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
existsSync as existsSync21,
|
|
14
14
|
mkdirSync as mkdirSync12,
|
|
15
15
|
openSync as openSync7,
|
|
16
|
+
readFileSync as readFileSync13,
|
|
16
17
|
statfsSync as statfsSync3,
|
|
17
18
|
statSync as statSync15
|
|
18
19
|
} from "node:fs";
|
|
@@ -21,6 +22,7 @@ import { arch, homedir as homedir5, platform as platform2, release } from "node:
|
|
|
21
22
|
import { join as join23, resolve as resolve6, sep as sep3 } from "node:path";
|
|
22
23
|
import { createInterface } from "node:readline/promises";
|
|
23
24
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
25
|
+
import { parentPort } from "node:worker_threads";
|
|
24
26
|
|
|
25
27
|
// ../core/dist/claims/markdown.js
|
|
26
28
|
var FENCE = /(^|\n)\s*(```|~~~)/;
|
|
@@ -18989,6 +18991,30 @@ var OperationsStoreMeasurementSchema = external_exports.object({
|
|
|
18989
18991
|
retention: RetentionDaysSchema.nullable(),
|
|
18990
18992
|
lastIngestAt: CanonicalTimestampSchema.nullable()
|
|
18991
18993
|
}).strict();
|
|
18994
|
+
var StorageCompositionPartSchema = external_exports.object({
|
|
18995
|
+
key: external_exports.enum(["sessions", "checkpoints", "provenance", "structure", "reusable"]),
|
|
18996
|
+
bytes: external_exports.number().int().nonnegative()
|
|
18997
|
+
}).strict();
|
|
18998
|
+
var StorageCompositionProjectSchema = external_exports.object({
|
|
18999
|
+
/** Repository root, else working directory, else empty when the session recorded neither. */
|
|
19000
|
+
path: external_exports.string(),
|
|
19001
|
+
sessions: external_exports.number().int().nonnegative(),
|
|
19002
|
+
bytes: external_exports.number().int().nonnegative()
|
|
19003
|
+
}).strict();
|
|
19004
|
+
var StorageCompositionSchema = external_exports.object({
|
|
19005
|
+
contractVersion: external_exports.literal(OPERATIONS_CONTRACT_VERSION),
|
|
19006
|
+
state: external_exports.enum(["absent", "running", "ready", "failed"]),
|
|
19007
|
+
computedAt: CanonicalTimestampSchema.nullable(),
|
|
19008
|
+
elapsedMs: external_exports.number().int().nonnegative().nullable(),
|
|
19009
|
+
/** The database file at the moment of measurement, from page count and page size. */
|
|
19010
|
+
fileBytes: external_exports.number().int().nonnegative().nullable(),
|
|
19011
|
+
sessions: external_exports.number().int().nonnegative().nullable(),
|
|
19012
|
+
parts: external_exports.array(StorageCompositionPartSchema),
|
|
19013
|
+
projects: external_exports.array(StorageCompositionProjectSchema),
|
|
19014
|
+
/** Projects measured but not listed, so a truncated list can say so rather than imply totality. */
|
|
19015
|
+
projectsOmitted: external_exports.number().int().nonnegative(),
|
|
19016
|
+
failure: external_exports.string().max(500).nullable()
|
|
19017
|
+
}).strict();
|
|
18992
19018
|
var MaintenancePhaseSchema = external_exports.enum([
|
|
18993
19019
|
"idle",
|
|
18994
19020
|
"pause",
|
|
@@ -26912,6 +26938,87 @@ var SqliteStore = class {
|
|
|
26912
26938
|
});
|
|
26913
26939
|
return true;
|
|
26914
26940
|
}
|
|
26941
|
+
/*
|
|
26942
|
+
* What the store is made of, measured rather than estimated.
|
|
26943
|
+
*
|
|
26944
|
+
* Every part here is one pass over a table. The events pass is the expensive one: `length(json)`
|
|
26945
|
+
* does not read a blob's payload, but it does read the header of every row, so on a store with
|
|
26946
|
+
* 1.3 million events that is a ten second scan and the reason this never runs on the health path
|
|
26947
|
+
* or on a timer. The daemon runs it on a worker with its own read-only connection for the same
|
|
26948
|
+
* reason it runs usage backfill there: a ten second synchronous scan on the main loop is ten
|
|
26949
|
+
* seconds of unanswered hooks.
|
|
26950
|
+
*
|
|
26951
|
+
* `structure` is deliberately a subtraction. Naming what each index costs needs `dbstat`, which
|
|
26952
|
+
* is a further nine to fifteen seconds because it walks every page in the file, and the answer
|
|
26953
|
+
* it adds is "your indexes are large" rather than anything a reader can act on. The difference
|
|
26954
|
+
* is honest, arrives free, and is labelled as a remainder wherever it is shown.
|
|
26955
|
+
*/
|
|
26956
|
+
storageComposition(projectLimit = 12) {
|
|
26957
|
+
const started = Date.now();
|
|
26958
|
+
const scalar = (sql) => {
|
|
26959
|
+
const row = this.db.prepare(sql).get();
|
|
26960
|
+
const value2 = row ? Object.values(row)[0] : 0;
|
|
26961
|
+
return typeof value2 === "number" ? value2 : Number(value2 ?? 0);
|
|
26962
|
+
};
|
|
26963
|
+
const pageSize = scalar("PRAGMA page_size");
|
|
26964
|
+
const fileBytes = scalar("PRAGMA page_count") * pageSize;
|
|
26965
|
+
const reusable = scalar("PRAGMA freelist_count") * pageSize;
|
|
26966
|
+
const sessions = this.db.prepare(`SELECT id, COALESCE(NULLIF(repo_root, ''), NULLIF(cwd, ''), '') AS project FROM sessions`).all();
|
|
26967
|
+
const projectOf = new Map(sessions.map((row) => [row.id, row.project]));
|
|
26968
|
+
const perProject = /* @__PURE__ */ new Map();
|
|
26969
|
+
for (const row of sessions) {
|
|
26970
|
+
const entry2 = perProject.get(row.project) ?? { sessions: 0, bytes: 0 };
|
|
26971
|
+
entry2.sessions += 1;
|
|
26972
|
+
perProject.set(row.project, entry2);
|
|
26973
|
+
}
|
|
26974
|
+
let sessionBytes = 0;
|
|
26975
|
+
let checkpointBytes = 0;
|
|
26976
|
+
const accumulate = (sql, into) => {
|
|
26977
|
+
for (const row of this.db.prepare(sql).all()) {
|
|
26978
|
+
const bytes = Number(row.b ?? 0);
|
|
26979
|
+
into(bytes);
|
|
26980
|
+
const project = projectOf.get(row.id);
|
|
26981
|
+
if (project === void 0)
|
|
26982
|
+
continue;
|
|
26983
|
+
const entry2 = perProject.get(project);
|
|
26984
|
+
if (entry2)
|
|
26985
|
+
entry2.bytes += bytes;
|
|
26986
|
+
}
|
|
26987
|
+
};
|
|
26988
|
+
accumulate("SELECT session_id AS id, SUM(length(json)) AS b FROM events GROUP BY session_id", (b) => {
|
|
26989
|
+
sessionBytes += b;
|
|
26990
|
+
});
|
|
26991
|
+
accumulate("SELECT session_id AS id, SUM(length(json)) AS b FROM changes GROUP BY session_id", (b) => {
|
|
26992
|
+
sessionBytes += b;
|
|
26993
|
+
});
|
|
26994
|
+
accumulate("SELECT session_id AS id, SUM(length(state_json)) AS b FROM checkpoints GROUP BY session_id", (b) => {
|
|
26995
|
+
checkpointBytes += b;
|
|
26996
|
+
});
|
|
26997
|
+
const provenanceBytes = scalar(`SELECT COALESCE(SUM(length(session_id) + length(event_id) + length(path)
|
|
26998
|
+
+ length(record_hash) + length(captured_at) + length(COALESCE(origin, ''))), 0) AS b
|
|
26999
|
+
FROM raw_record_fingerprints`);
|
|
27000
|
+
const named = sessionBytes + checkpointBytes + provenanceBytes + reusable;
|
|
27001
|
+
const ranked = [...perProject.entries()].map(([path, value2]) => ({ path, sessions: value2.sessions, bytes: value2.bytes })).sort((a, b) => b.bytes - a.bytes || b.sessions - a.sessions);
|
|
27002
|
+
return {
|
|
27003
|
+
contractVersion: OPERATIONS_CONTRACT_VERSION,
|
|
27004
|
+
state: "ready",
|
|
27005
|
+
computedAt: new Date(started).toISOString(),
|
|
27006
|
+
elapsedMs: Date.now() - started,
|
|
27007
|
+
fileBytes,
|
|
27008
|
+
sessions: sessions.length,
|
|
27009
|
+
parts: [
|
|
27010
|
+
{ key: "sessions", bytes: sessionBytes },
|
|
27011
|
+
{ key: "checkpoints", bytes: checkpointBytes },
|
|
27012
|
+
{ key: "provenance", bytes: provenanceBytes },
|
|
27013
|
+
// A store measured while it is being written to can name more than the file holds.
|
|
27014
|
+
{ key: "structure", bytes: Math.max(fileBytes - named, 0) },
|
|
27015
|
+
{ key: "reusable", bytes: reusable }
|
|
27016
|
+
],
|
|
27017
|
+
projects: ranked.slice(0, projectLimit),
|
|
27018
|
+
projectsOmitted: Math.max(ranked.length - projectLimit, 0),
|
|
27019
|
+
failure: null
|
|
27020
|
+
};
|
|
27021
|
+
}
|
|
26915
27022
|
/** Offline space reclamation after bounded cleanup batches. */
|
|
26916
27023
|
compact() {
|
|
26917
27024
|
if (!this.usageBackfillProgress().complete)
|
|
@@ -28756,6 +28863,13 @@ function createHttpServer(deps) {
|
|
|
28756
28863
|
}
|
|
28757
28864
|
if (req.method === "POST" && url2.pathname === "/api/operations/maintenance/drain" && deps.operations)
|
|
28758
28865
|
return json2(res, 200, deps.operations.drainQueue());
|
|
28866
|
+
if (url2.pathname === "/api/operations/storage" && deps.operations) {
|
|
28867
|
+
if (req.method === "GET")
|
|
28868
|
+
return json2(res, 200, deps.operations.storageComposition());
|
|
28869
|
+
if (req.method === "POST")
|
|
28870
|
+
return json2(res, 200, deps.operations.analyzeStorage());
|
|
28871
|
+
return json2(res, 405, { error: "method not allowed" });
|
|
28872
|
+
}
|
|
28759
28873
|
const acknowledge = /^\/api\/operations\/alerts\/([^/]+)\/acknowledge$/.exec(url2.pathname);
|
|
28760
28874
|
if (req.method === "POST" && acknowledge?.[1] && deps.operations) {
|
|
28761
28875
|
try {
|
|
@@ -29495,6 +29609,67 @@ async function startDaemon(overrides = {}) {
|
|
|
29495
29609
|
refreshHealthSampling?.();
|
|
29496
29610
|
return effective2;
|
|
29497
29611
|
};
|
|
29612
|
+
let composition = {
|
|
29613
|
+
contractVersion: OPERATIONS_CONTRACT_VERSION,
|
|
29614
|
+
state: "absent",
|
|
29615
|
+
computedAt: null,
|
|
29616
|
+
elapsedMs: null,
|
|
29617
|
+
fileBytes: null,
|
|
29618
|
+
sessions: null,
|
|
29619
|
+
parts: [],
|
|
29620
|
+
projects: [],
|
|
29621
|
+
projectsOmitted: 0,
|
|
29622
|
+
failure: null
|
|
29623
|
+
};
|
|
29624
|
+
let compositionWorker;
|
|
29625
|
+
const analyzeStorage = () => {
|
|
29626
|
+
if (compositionWorker)
|
|
29627
|
+
return composition;
|
|
29628
|
+
const sibling = new URL("./storage/compositionWorker.js", import.meta.url);
|
|
29629
|
+
const worker = existsSync16(fileURLToPath(sibling)) ? new Worker(sibling, { workerData: paths.db }) : (() => {
|
|
29630
|
+
const runtime = process.argv[1];
|
|
29631
|
+
if (!runtime)
|
|
29632
|
+
return void 0;
|
|
29633
|
+
return new Worker(resolve4(runtime), {
|
|
29634
|
+
argv: ["__storage-composition", paths.db]
|
|
29635
|
+
});
|
|
29636
|
+
})();
|
|
29637
|
+
if (!worker) {
|
|
29638
|
+
composition = {
|
|
29639
|
+
...composition,
|
|
29640
|
+
state: "failed",
|
|
29641
|
+
failure: "Salidium could not locate a runtime to measure the store with."
|
|
29642
|
+
};
|
|
29643
|
+
return composition;
|
|
29644
|
+
}
|
|
29645
|
+
compositionWorker = worker;
|
|
29646
|
+
worker.unref();
|
|
29647
|
+
composition = { ...composition, state: "running", failure: null };
|
|
29648
|
+
worker.once("message", (message) => {
|
|
29649
|
+
composition = message;
|
|
29650
|
+
});
|
|
29651
|
+
worker.once("error", (error51) => {
|
|
29652
|
+
log.warn("storage composition worker failed", { err: String(error51) });
|
|
29653
|
+
composition = { ...composition, state: "failed", failure: String(error51).slice(0, 500) };
|
|
29654
|
+
});
|
|
29655
|
+
worker.once("exit", (code) => {
|
|
29656
|
+
if (compositionWorker === worker)
|
|
29657
|
+
compositionWorker = void 0;
|
|
29658
|
+
if (code !== 0 && composition.state === "running")
|
|
29659
|
+
composition = {
|
|
29660
|
+
...composition,
|
|
29661
|
+
state: "failed",
|
|
29662
|
+
failure: `The measurement stopped with status ${code}.`
|
|
29663
|
+
};
|
|
29664
|
+
else if (composition.state === "running")
|
|
29665
|
+
composition = {
|
|
29666
|
+
...composition,
|
|
29667
|
+
state: "failed",
|
|
29668
|
+
failure: "The measurement produced no result."
|
|
29669
|
+
};
|
|
29670
|
+
});
|
|
29671
|
+
return composition;
|
|
29672
|
+
};
|
|
29498
29673
|
let port = config2.port;
|
|
29499
29674
|
const server = createHttpServer({
|
|
29500
29675
|
registry: registry2,
|
|
@@ -29530,7 +29705,9 @@ async function startDaemon(overrides = {}) {
|
|
|
29530
29705
|
return inspectQueue(config2.home, { entryLimit: limit });
|
|
29531
29706
|
},
|
|
29532
29707
|
drainQueue: () => runQueueDrainMaintenance(config2.home, () => hooks.drainSpool()),
|
|
29533
|
-
acknowledgeAlert: (id) => acknowledgeLocalAlert(config2.home, id)
|
|
29708
|
+
acknowledgeAlert: (id) => acknowledgeLocalAlert(config2.home, id),
|
|
29709
|
+
storageComposition: () => composition,
|
|
29710
|
+
analyzeStorage
|
|
29534
29711
|
},
|
|
29535
29712
|
settings: {
|
|
29536
29713
|
explainer: explainerSettings,
|
|
@@ -29621,6 +29798,8 @@ async function startDaemon(overrides = {}) {
|
|
|
29621
29798
|
clearTimeout(usageBackfillRetryTimer);
|
|
29622
29799
|
if (usageBackfillWorker)
|
|
29623
29800
|
void usageBackfillWorker.terminate();
|
|
29801
|
+
if (compositionWorker)
|
|
29802
|
+
void compositionWorker.terminate();
|
|
29624
29803
|
if (healthTimer)
|
|
29625
29804
|
clearTimeout(healthTimer);
|
|
29626
29805
|
if (hookTrustTimer)
|
|
@@ -31567,6 +31746,15 @@ async function runFirstRunOnboarding(context, io, options = {}) {
|
|
|
31567
31746
|
};
|
|
31568
31747
|
}
|
|
31569
31748
|
|
|
31749
|
+
// src/pauseOnRun.ts
|
|
31750
|
+
function clearsPauseOnRun(command, argument, argv) {
|
|
31751
|
+
if (argv.includes("--no-resume")) return false;
|
|
31752
|
+
if (["pause", "resume", "stop", "service"].includes(command)) return false;
|
|
31753
|
+
if (command.startsWith("__")) return false;
|
|
31754
|
+
if (command === "storage" && argument === "optimize") return false;
|
|
31755
|
+
return true;
|
|
31756
|
+
}
|
|
31757
|
+
|
|
31570
31758
|
// src/render.ts
|
|
31571
31759
|
var H = "\u2500";
|
|
31572
31760
|
var CONTROL = /[\u0000-\u001F\u007F-\u009F]/g;
|
|
@@ -31797,6 +31985,7 @@ Usage:
|
|
|
31797
31985
|
salidium retention apply Apply one cleanup batch now (daemon must be stopped)
|
|
31798
31986
|
salidium retention compact Return reusable SQLite pages to the OS (daemon must be stopped)
|
|
31799
31987
|
salidium storage Show the lossless storage layout and page size
|
|
31988
|
+
salidium storage composition Measure what is using the space, by part and by project
|
|
31800
31989
|
salidium storage optimize Coordinate, copy, verify, and install the compact layout
|
|
31801
31990
|
salidium pin [session] Exempt a session from automatic retention
|
|
31802
31991
|
salidium unpin [session] Remove the retention exemption
|
|
@@ -31818,7 +32007,9 @@ Environment:
|
|
|
31818
32007
|
|
|
31819
32008
|
Native Windows imports transcript history but does not install the POSIX live-hook relay.
|
|
31820
32009
|
Ordinary commands resume an expired or manual pause. pause, stop, service commands, and coordinated
|
|
31821
|
-
storage optimize do not; resume changes collection state explicitly.
|
|
32010
|
+
storage optimize do not; resume changes collection state explicitly. Add --no-resume to any command
|
|
32011
|
+
to leave a pause in place, for a caller that has already seen the daemon answer and so cannot be
|
|
32012
|
+
recovering a marker its dead owner left behind.
|
|
31822
32013
|
`;
|
|
31823
32014
|
var require3 = createRequire2(import.meta.url);
|
|
31824
32015
|
var VERSION2 = (() => {
|
|
@@ -31838,11 +32029,10 @@ async function main(argv) {
|
|
|
31838
32029
|
const jsonOutput = argv.includes("--json");
|
|
31839
32030
|
const quiet = argv.includes("--quiet");
|
|
31840
32031
|
const positional = argv.filter(
|
|
31841
|
-
(value2) => !["--yes", "-y", "--no-open", "--json", "--quiet"].includes(value2)
|
|
32032
|
+
(value2) => !["--yes", "-y", "--no-open", "--json", "--quiet", "--no-resume"].includes(value2)
|
|
31842
32033
|
);
|
|
31843
32034
|
const [cmd = "up", arg, ...args] = positional;
|
|
31844
|
-
if (
|
|
31845
|
-
await implicitlyResumeCollection();
|
|
32035
|
+
if (clearsPauseOnRun(cmd, arg, argv)) await implicitlyResumeCollection();
|
|
31846
32036
|
switch (cmd) {
|
|
31847
32037
|
case "help":
|
|
31848
32038
|
case "--help":
|
|
@@ -31885,6 +32075,24 @@ async function main(argv) {
|
|
|
31885
32075
|
store.close();
|
|
31886
32076
|
}
|
|
31887
32077
|
}
|
|
32078
|
+
/*
|
|
32079
|
+
* The other private worker entrypoint. Measuring what the store is made of reads the header of
|
|
32080
|
+
* every stored event, which is ten seconds on a three gigabyte store, and `node:sqlite` is
|
|
32081
|
+
* synchronous: run on the daemon's loop that is ten seconds of hooks going unanswered and
|
|
32082
|
+
* spooling to disk. It runs here instead, on its own event loop and its own connection, and
|
|
32083
|
+
* posts one message back.
|
|
32084
|
+
*/
|
|
32085
|
+
case "__storage-composition": {
|
|
32086
|
+
if (!arg || !resolve6(arg).startsWith(`${resolve6(salidiumHome)}${sep3}`))
|
|
32087
|
+
throw new Error("storage composition store must be inside the Salidium state directory");
|
|
32088
|
+
const store = new SqliteStore(resolve6(arg), { concurrentWriter: true });
|
|
32089
|
+
try {
|
|
32090
|
+
parentPort?.postMessage(store.storageComposition());
|
|
32091
|
+
return 0;
|
|
32092
|
+
} finally {
|
|
32093
|
+
store.close();
|
|
32094
|
+
}
|
|
32095
|
+
}
|
|
31888
32096
|
case "start": {
|
|
31889
32097
|
const running = await ensureDaemon();
|
|
31890
32098
|
const explanations = await currentExplanationState(running, "reachable");
|
|
@@ -32521,10 +32729,54 @@ Change with: salidium explanations off|when-done|each-reply
|
|
|
32521
32729
|
`);
|
|
32522
32730
|
return 1;
|
|
32523
32731
|
}
|
|
32524
|
-
if (arg !== void 0 && arg !== "optimize") {
|
|
32525
|
-
process.stderr.write("storage accepts only `optimize`\n");
|
|
32732
|
+
if (arg !== void 0 && arg !== "optimize" && arg !== "composition") {
|
|
32733
|
+
process.stderr.write("storage accepts only `composition` or `optimize`\n");
|
|
32526
32734
|
return 2;
|
|
32527
32735
|
}
|
|
32736
|
+
if (arg === "composition") {
|
|
32737
|
+
const store = new SqliteStore(db, { concurrentWriter: true });
|
|
32738
|
+
try {
|
|
32739
|
+
const measured = store.storageComposition();
|
|
32740
|
+
if (jsonOutput) {
|
|
32741
|
+
process.stdout.write(`${JSON.stringify(measured)}
|
|
32742
|
+
`);
|
|
32743
|
+
return 0;
|
|
32744
|
+
}
|
|
32745
|
+
const label = {
|
|
32746
|
+
sessions: "Recorded sessions",
|
|
32747
|
+
checkpoints: "Replay checkpoints",
|
|
32748
|
+
provenance: "Provenance records",
|
|
32749
|
+
structure: "Indexes and internal structure",
|
|
32750
|
+
reusable: "Reusable space"
|
|
32751
|
+
};
|
|
32752
|
+
process.stdout.write(`On this Mac: ${formatBytes(measured.fileBytes ?? 0)}
|
|
32753
|
+
`);
|
|
32754
|
+
for (const part of measured.parts)
|
|
32755
|
+
process.stdout.write(
|
|
32756
|
+
` ${(label[part.key] ?? part.key).padEnd(30)} ${formatBytes(part.bytes)}
|
|
32757
|
+
`
|
|
32758
|
+
);
|
|
32759
|
+
process.stdout.write(
|
|
32760
|
+
`
|
|
32761
|
+
${measured.sessions ?? 0} sessions across ${measured.projects.length + measured.projectsOmitted} projects.
|
|
32762
|
+
`
|
|
32763
|
+
);
|
|
32764
|
+
for (const project of measured.projects)
|
|
32765
|
+
process.stdout.write(
|
|
32766
|
+
` ${formatBytes(project.bytes).padStart(9)} ${String(project.sessions).padStart(4)} ${project.sessions === 1 ? "session " : "sessions"} ${project.path || "No project recorded"}
|
|
32767
|
+
`
|
|
32768
|
+
);
|
|
32769
|
+
if (measured.projectsOmitted > 0)
|
|
32770
|
+
process.stdout.write(` ${measured.projectsOmitted} more not listed.
|
|
32771
|
+
`);
|
|
32772
|
+
process.stdout.write(
|
|
32773
|
+
"\nIndexes and internal structure is the remainder after the named parts, not a separate measurement.\n"
|
|
32774
|
+
);
|
|
32775
|
+
return 0;
|
|
32776
|
+
} finally {
|
|
32777
|
+
store.close();
|
|
32778
|
+
}
|
|
32779
|
+
}
|
|
32528
32780
|
if (arg === void 0) {
|
|
32529
32781
|
const layout = inspectStoreLayout(db);
|
|
32530
32782
|
if (quiet) return layout.optimized ? 0 : 3;
|
|
@@ -33579,11 +33831,22 @@ async function ensureDaemon() {
|
|
|
33579
33831
|
for (let i = 0; i < 100; i++) {
|
|
33580
33832
|
await sleep(100);
|
|
33581
33833
|
if (childFailure)
|
|
33582
|
-
throw new Error(
|
|
33834
|
+
throw new Error(
|
|
33835
|
+
`daemon ${childFailure} before it became ready: ${startupFailureReason(paths.startupLogFile)}`
|
|
33836
|
+
);
|
|
33583
33837
|
const d = readDaemonJson(salidiumHome);
|
|
33584
33838
|
if (d && d.pid === child.pid && await alive(d)) return d;
|
|
33585
33839
|
}
|
|
33586
|
-
throw new Error(`daemon did not start
|
|
33840
|
+
throw new Error(`daemon did not start: ${startupFailureReason(paths.startupLogFile)}`);
|
|
33841
|
+
}
|
|
33842
|
+
function startupFailureReason(logFile) {
|
|
33843
|
+
try {
|
|
33844
|
+
const lines = readFileSync13(logFile, "utf8").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
33845
|
+
const last = lines.at(-1);
|
|
33846
|
+
if (last) return `${last.slice(0, 200)} (see ${logFile})`;
|
|
33847
|
+
} catch {
|
|
33848
|
+
}
|
|
33849
|
+
return `see ${logFile}`;
|
|
33587
33850
|
}
|
|
33588
33851
|
function compareSemver(left, right) {
|
|
33589
33852
|
const parse3 = (value2) => {
|