@le-space/node 0.1.10 → 0.1.12
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/index.d.ts +11 -1
- package/index.js +199 -4
- package/package.json +4 -4
package/index.d.ts
CHANGED
|
@@ -407,4 +407,14 @@ declare function runRootfsMode(env?: NodeJS.ProcessEnv, hooks?: {
|
|
|
407
407
|
}): Promise<void>;
|
|
408
408
|
declare function rootfsMain(): Promise<void>;
|
|
409
409
|
|
|
410
|
-
|
|
410
|
+
interface RunResult {
|
|
411
|
+
stdout: string;
|
|
412
|
+
stderr: string;
|
|
413
|
+
exitCode: number;
|
|
414
|
+
}
|
|
415
|
+
declare function runSitePublishMode(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
416
|
+
declare function runProbeMode(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
417
|
+
declare function runBootstrapEnvMode(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
418
|
+
declare function runSiteMode(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
419
|
+
|
|
420
|
+
export { type DeployConfigurationResult, type DeployExecutorDependencies, type DeployMetadataResult, type DeployOutputResult, type DeployPlan, type ParsedRootfsRunnerInputs, type PrivateKeyIdentity, type RunResult, actionLog, appendGithubOutput, appendGithubSummary, booleanEnv, buildScaffoldDeployResult, createPrivateKeyIdentity, createPrivateKeySigner, emitDeployOutputs, emitGeocodedCrnOutputs, emitRootfsOutputs, executeDeployPlan, integerEnv, jsonEnv, main, optionalEnv, parseDeployPlan, parseRootfsRunnerInputs, requiredEnv, rootfsMain, runActionMode, runBootstrapEnvMode, runLocalCommand, runProbeMode, runRootfsMode, runSiteMode, runSitePublishMode };
|
package/index.js
CHANGED
|
@@ -34,10 +34,20 @@ function jsonEnv(name, fallback, env = process.env) {
|
|
|
34
34
|
|
|
35
35
|
// src/github-outputs.ts
|
|
36
36
|
import { appendFile } from "fs/promises";
|
|
37
|
+
import { randomUUID } from "crypto";
|
|
37
38
|
async function appendGithubOutput(name, value, env = process.env) {
|
|
38
39
|
const outputFile = env.GITHUB_OUTPUT;
|
|
39
40
|
if (!outputFile) return;
|
|
40
|
-
|
|
41
|
+
const normalized = String(value ?? "");
|
|
42
|
+
if (/\r|\n/.test(normalized)) {
|
|
43
|
+
const marker = `__ALEPH_OUTPUT_${randomUUID()}__`;
|
|
44
|
+
await appendFile(outputFile, `${name}<<${marker}
|
|
45
|
+
${normalized}
|
|
46
|
+
${marker}
|
|
47
|
+
`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
await appendFile(outputFile, `${name}=${normalized}
|
|
41
51
|
`);
|
|
42
52
|
}
|
|
43
53
|
async function appendGithubSummary(lines, env = process.env) {
|
|
@@ -607,6 +617,15 @@ function normalizeExecution(item, crnUrl) {
|
|
|
607
617
|
}
|
|
608
618
|
|
|
609
619
|
// ../core/src/deployment-inspection.ts
|
|
620
|
+
function insufficientBalanceMessage(details) {
|
|
621
|
+
const firstError = details && Array.isArray(details.errors) && details.errors[0] && typeof details.errors[0] === "object" ? details.errors[0] : null;
|
|
622
|
+
const accountBalance = firstError?.account_balance;
|
|
623
|
+
const requiredBalance = firstError?.required_balance;
|
|
624
|
+
if (accountBalance != null && requiredBalance != null) {
|
|
625
|
+
return `insufficient Aleph balance: account has ${accountBalance}, required is ${requiredBalance}`;
|
|
626
|
+
}
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
610
629
|
async function fetchMessageEnvelope(itemHash, options) {
|
|
611
630
|
const response = await options.fetch(`${options.apiHost ?? DEFAULT_ALEPH_API_HOST}/api/v0/messages/${itemHash}`, {
|
|
612
631
|
cache: "no-cache"
|
|
@@ -615,6 +634,32 @@ async function fetchMessageEnvelope(itemHash, options) {
|
|
|
615
634
|
if (!response.ok) throw new Error(`Message lookup failed: ${response.status}`);
|
|
616
635
|
return await response.json();
|
|
617
636
|
}
|
|
637
|
+
async function inspectMessageResult(itemHash, options) {
|
|
638
|
+
const label = options.label ?? "Message";
|
|
639
|
+
const payload = await fetchMessageEnvelope(itemHash, options);
|
|
640
|
+
if (!payload) {
|
|
641
|
+
return {
|
|
642
|
+
status: "unknown",
|
|
643
|
+
errorCode: null,
|
|
644
|
+
details: null,
|
|
645
|
+
rejectionReason: `${label} ${itemHash} was not found on Aleph.`
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const status = normalizeMessageStatus(payload.status);
|
|
649
|
+
const errorCode = typeof payload.error_code === "number" ? payload.error_code : null;
|
|
650
|
+
const details = payload.details && typeof payload.details === "object" ? payload.details : null;
|
|
651
|
+
let rejectionReason = null;
|
|
652
|
+
if (status === "rejected") {
|
|
653
|
+
const balanceMessage = errorCode === 5 ? insufficientBalanceMessage(details) : null;
|
|
654
|
+
rejectionReason = balanceMessage ? `${label} ${itemHash} was rejected by Aleph due to ${balanceMessage}.` : `${label} ${itemHash} was rejected by Aleph${errorCode ? ` (error ${errorCode})` : ""}.`;
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
status,
|
|
658
|
+
errorCode,
|
|
659
|
+
details,
|
|
660
|
+
rejectionReason
|
|
661
|
+
};
|
|
662
|
+
}
|
|
618
663
|
async function fetchReference(itemHash, options) {
|
|
619
664
|
const payload = await fetchMessageEnvelope(itemHash, options);
|
|
620
665
|
if (!payload) {
|
|
@@ -1399,11 +1444,11 @@ function proxyHostnameFromUrl(value) {
|
|
|
1399
1444
|
return null;
|
|
1400
1445
|
}
|
|
1401
1446
|
}
|
|
1402
|
-
async function defaultHttpProbe(
|
|
1447
|
+
async function defaultHttpProbe(fetch2, url, timeoutMs = 1e4) {
|
|
1403
1448
|
try {
|
|
1404
1449
|
const controller = new AbortController();
|
|
1405
1450
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1406
|
-
const response = await
|
|
1451
|
+
const response = await fetch2(url, {
|
|
1407
1452
|
method: "GET",
|
|
1408
1453
|
redirect: "follow",
|
|
1409
1454
|
signal: controller.signal
|
|
@@ -2780,6 +2825,152 @@ if (import.meta.url === pathToFileURL2(process.argv[1] ?? "").href) {
|
|
|
2780
2825
|
process.exitCode = 1;
|
|
2781
2826
|
});
|
|
2782
2827
|
}
|
|
2828
|
+
|
|
2829
|
+
// src/site-runner.ts
|
|
2830
|
+
import process2 from "process";
|
|
2831
|
+
import { spawn as spawn2 } from "child_process";
|
|
2832
|
+
async function runCapture(command, args, options = {}) {
|
|
2833
|
+
return await new Promise((resolve, reject) => {
|
|
2834
|
+
const child = spawn2(command, args, {
|
|
2835
|
+
cwd: options.cwd,
|
|
2836
|
+
env: { ...process2.env, ...options.env },
|
|
2837
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2838
|
+
});
|
|
2839
|
+
let stdout = "";
|
|
2840
|
+
let stderr = "";
|
|
2841
|
+
child.stdout.on("data", (chunk) => {
|
|
2842
|
+
stdout += String(chunk);
|
|
2843
|
+
});
|
|
2844
|
+
child.stderr.on("data", (chunk) => {
|
|
2845
|
+
stderr += String(chunk);
|
|
2846
|
+
});
|
|
2847
|
+
child.on("error", reject);
|
|
2848
|
+
child.on("exit", (exitCode) => resolve({ stdout, stderr, exitCode: exitCode ?? 1 }));
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
function parseLastJsonObject(text) {
|
|
2852
|
+
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
2853
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
2854
|
+
const line = lines[index];
|
|
2855
|
+
if (!line.startsWith("{")) continue;
|
|
2856
|
+
return JSON.parse(line);
|
|
2857
|
+
}
|
|
2858
|
+
throw new Error(`Could not parse JSON object from output: ${text}`);
|
|
2859
|
+
}
|
|
2860
|
+
async function waitForAlephMessage(itemHash, env = process2.env) {
|
|
2861
|
+
const apiHost = optionalEnv("ALEPH_SITE_ALEPH_API_HOST", "https://api2.aleph.im", env);
|
|
2862
|
+
const attempts = Number(optionalEnv("ALEPH_SITE_ALEPH_MESSAGE_WAIT_ATTEMPTS", "60", env));
|
|
2863
|
+
const delayMs = Number(optionalEnv("ALEPH_SITE_ALEPH_MESSAGE_WAIT_DELAY_MS", "5000", env));
|
|
2864
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
2865
|
+
const result = await inspectMessageResult(itemHash, {
|
|
2866
|
+
apiHost,
|
|
2867
|
+
fetch,
|
|
2868
|
+
label: "Aleph STORE message"
|
|
2869
|
+
});
|
|
2870
|
+
if (result.status === "processed") return;
|
|
2871
|
+
if (result.status === "rejected") {
|
|
2872
|
+
throw new Error(result.rejectionReason ?? `Aleph STORE message ${itemHash} was rejected.`);
|
|
2873
|
+
}
|
|
2874
|
+
if (attempt < attempts) {
|
|
2875
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
throw new Error(`Aleph STORE message ${itemHash} did not become processed in time.`);
|
|
2879
|
+
}
|
|
2880
|
+
function mergedAddrs(env = process2.env) {
|
|
2881
|
+
const combined = [];
|
|
2882
|
+
for (const key of ["PROBE_MULTIADDRS_JSON", "BROWSER_BOOTSTRAP_MULTIADDRS_JSON"]) {
|
|
2883
|
+
const raw = env[key] ?? "[]";
|
|
2884
|
+
for (const value of JSON.parse(raw)) {
|
|
2885
|
+
if (typeof value === "string") {
|
|
2886
|
+
const trimmed = value.trim();
|
|
2887
|
+
if (trimmed) combined.push(trimmed);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
return Array.from(new Set(combined));
|
|
2892
|
+
}
|
|
2893
|
+
async function runSitePublishMode(env = process2.env) {
|
|
2894
|
+
const projectDir = requiredEnv("ALEPH_SITE_PROJECT_DIR", env);
|
|
2895
|
+
const publishScript = optionalEnv("ALEPH_SITE_PUBLISH_SCRIPT", "go-peer/aleph/publish-static-site.py", env);
|
|
2896
|
+
const siteDirectory = requiredEnv("ALEPH_SITE_DIRECTORY", env);
|
|
2897
|
+
const pythonBin = optionalEnv("ALEPH_SITE_PYTHON", "python3", env);
|
|
2898
|
+
const pin = optionalEnv("ALEPH_SITE_PIN", "true", env) === "true";
|
|
2899
|
+
const publish = await runCapture(pythonBin, [publishScript, siteDirectory], { cwd: projectDir });
|
|
2900
|
+
process2.stdout.write(publish.stdout);
|
|
2901
|
+
if (publish.stderr) process2.stderr.write(publish.stderr);
|
|
2902
|
+
if (publish.exitCode !== 0) {
|
|
2903
|
+
throw new Error(`${publishScript} failed with exit code ${publish.exitCode}`);
|
|
2904
|
+
}
|
|
2905
|
+
const payload = parseLastJsonObject(publish.stdout);
|
|
2906
|
+
const cidV0 = String(payload.cid_v0 ?? "");
|
|
2907
|
+
const cidV1 = String(payload.cid_v1 ?? "");
|
|
2908
|
+
if (!cidV0 || !cidV1 || cidV0 === "null" || cidV1 === "null") {
|
|
2909
|
+
throw new Error(`Failed to extract IPFS CIDs from publish result: ${JSON.stringify(payload)}`);
|
|
2910
|
+
}
|
|
2911
|
+
await appendGithubOutput("ipfs_cid_v0", cidV0, env);
|
|
2912
|
+
await appendGithubOutput("ipfs_cid_v1", cidV1, env);
|
|
2913
|
+
await appendGithubOutput("url", `https://${cidV1}.ipfs.aleph.sh`, env);
|
|
2914
|
+
let itemHash = "";
|
|
2915
|
+
if (pin) {
|
|
2916
|
+
const pinResult = await runCapture("aleph", ["file", "pin", cidV0], { cwd: projectDir });
|
|
2917
|
+
if (pinResult.stdout) process2.stdout.write(pinResult.stdout);
|
|
2918
|
+
if (pinResult.stderr) process2.stderr.write(pinResult.stderr);
|
|
2919
|
+
if (pinResult.exitCode !== 0) {
|
|
2920
|
+
throw new Error(`aleph file pin ${cidV0} failed with exit code ${pinResult.exitCode}`);
|
|
2921
|
+
}
|
|
2922
|
+
const pinPayload = parseLastJsonObject(pinResult.stdout);
|
|
2923
|
+
itemHash = String(pinPayload.item_hash ?? "");
|
|
2924
|
+
if (!itemHash) {
|
|
2925
|
+
throw new Error(`Aleph pin response did not include item_hash: ${JSON.stringify(pinPayload)}`);
|
|
2926
|
+
}
|
|
2927
|
+
await appendGithubOutput("item_hash", itemHash, env);
|
|
2928
|
+
await waitForAlephMessage(itemHash, env);
|
|
2929
|
+
}
|
|
2930
|
+
await appendGithubSummary([
|
|
2931
|
+
"## Shared Site Runner",
|
|
2932
|
+
"",
|
|
2933
|
+
`- Site directory: \`${siteDirectory}\``,
|
|
2934
|
+
`- IPFS CID v0: \`${cidV0}\``,
|
|
2935
|
+
`- IPFS CID v1: \`${cidV1}\``,
|
|
2936
|
+
`- Aleph item hash: \`${itemHash}\``
|
|
2937
|
+
], env);
|
|
2938
|
+
}
|
|
2939
|
+
async function runProbeMode(env = process2.env) {
|
|
2940
|
+
const addrs = mergedAddrs(env);
|
|
2941
|
+
if (addrs.length === 0) throw new Error("No relay probe or browser bootstrap multiaddrs were supplied.");
|
|
2942
|
+
const workdir = requiredEnv("ALEPH_SITE_PROBE_WORKDIR", env);
|
|
2943
|
+
const scriptPath = requiredEnv("ALEPH_SITE_PROBE_SCRIPT", env);
|
|
2944
|
+
const nodeBin = optionalEnv("ALEPH_SITE_NODE", "node", env);
|
|
2945
|
+
const result = await runCapture(nodeBin, [scriptPath, ...addrs], { cwd: workdir });
|
|
2946
|
+
if (result.stdout) process2.stdout.write(result.stdout);
|
|
2947
|
+
if (result.stderr) process2.stderr.write(result.stderr);
|
|
2948
|
+
if (result.exitCode !== 0) {
|
|
2949
|
+
throw new Error(`relay probe failed with exit code ${result.exitCode}`);
|
|
2950
|
+
}
|
|
2951
|
+
const rows = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
2952
|
+
if (rows.length === 0) throw new Error("Relay probe produced no JSON output.");
|
|
2953
|
+
if (rows.some((row) => row.ok !== true)) throw new Error("At least one relay protocol probe failed.");
|
|
2954
|
+
await appendGithubOutput("ok", "true", env);
|
|
2955
|
+
await appendGithubOutput("json", result.stdout.trim(), env);
|
|
2956
|
+
await appendGithubOutput("merged_multiaddrs_json", JSON.stringify(addrs), env);
|
|
2957
|
+
}
|
|
2958
|
+
async function runBootstrapEnvMode(env = process2.env) {
|
|
2959
|
+
const raw = env.BROWSER_BOOTSTRAP_MULTIADDRS_JSON ?? "[]";
|
|
2960
|
+
const addrs = JSON.parse(raw).filter((value) => typeof value === "string" && value.trim().length > 0).map((value) => value.trim());
|
|
2961
|
+
const csv = addrs.join(",");
|
|
2962
|
+
await appendGithubOutput("json", JSON.stringify(addrs), env);
|
|
2963
|
+
await appendGithubOutput("csv", csv, env);
|
|
2964
|
+
await appendGithubOutput("count", String(addrs.length), env);
|
|
2965
|
+
await appendGithubOutput("available", addrs.length > 0 ? "true" : "false", env);
|
|
2966
|
+
}
|
|
2967
|
+
async function runSiteMode(env = process2.env) {
|
|
2968
|
+
const mode = optionalEnv("ALEPH_VM_MODE", "site-publish", env);
|
|
2969
|
+
if (mode === "site-publish") return await runSitePublishMode(env);
|
|
2970
|
+
if (mode === "relay-probe") return await runProbeMode(env);
|
|
2971
|
+
if (mode === "bootstrap-env") return await runBootstrapEnvMode(env);
|
|
2972
|
+
throw new Error(`Unsupported ALEPH_VM_MODE "${mode}" in shared site runner.`);
|
|
2973
|
+
}
|
|
2783
2974
|
export {
|
|
2784
2975
|
actionLog,
|
|
2785
2976
|
appendGithubOutput,
|
|
@@ -2801,6 +2992,10 @@ export {
|
|
|
2801
2992
|
requiredEnv,
|
|
2802
2993
|
rootfsMain,
|
|
2803
2994
|
runActionMode,
|
|
2995
|
+
runBootstrapEnvMode,
|
|
2804
2996
|
runLocalCommand,
|
|
2805
|
-
|
|
2997
|
+
runProbeMode,
|
|
2998
|
+
runRootfsMode,
|
|
2999
|
+
runSiteMode,
|
|
3000
|
+
runSitePublishMode
|
|
2806
3001
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Node and GitHub Actions adapters for shared Aleph tooling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@le-space/core": "0.1.
|
|
20
|
-
"@le-space/shared-types": "0.1.
|
|
21
|
-
"@le-space/rootfs": "0.1.
|
|
19
|
+
"@le-space/core": "0.1.12",
|
|
20
|
+
"@le-space/shared-types": "0.1.12",
|
|
21
|
+
"@le-space/rootfs": "0.1.12",
|
|
22
22
|
"ethers": "^6.15.0"
|
|
23
23
|
}
|
|
24
24
|
}
|