@le-space/node 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/index.d.ts +1 -0
  2. package/index.js +74 -12
  3. package/package.json +3 -3
package/index.d.ts CHANGED
@@ -133,6 +133,7 @@ interface DeployExecutorDependencies {
133
133
  sender?: string;
134
134
  hasher?: MessageHasher;
135
135
  manifest?: RootfsManifest | null;
136
+ log?: (message: string) => void;
136
137
  }
137
138
  declare function executeDeployPlan(plan: DeployPlan, dependencies?: DeployExecutorDependencies): Promise<DeployOutputResult>;
138
139
 
package/index.js CHANGED
@@ -664,12 +664,14 @@ async function waitForDeploymentResult(itemHash, options) {
664
664
  const delayMs = Math.max(0, Number(options.delayMs ?? 2e3));
665
665
  const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
666
666
  let lastResult = await inspectDeploymentResult(itemHash, options);
667
+ options.onAttempt?.(lastResult, 1, attempts);
667
668
  for (let attempt = 1; attempt < attempts; attempt += 1) {
668
669
  if (lastResult.status === "processed" || lastResult.status === "rejected") {
669
670
  return lastResult;
670
671
  }
671
672
  await sleep2(delayMs);
672
673
  lastResult = await inspectDeploymentResult(itemHash, options);
674
+ options.onAttempt?.(lastResult, attempt + 1, attempts);
673
675
  }
674
676
  return lastResult;
675
677
  }
@@ -1374,12 +1376,14 @@ async function waitForVmRuntime(args) {
1374
1376
  const delayMs = Math.max(0, Number(args.delayMs ?? 4e3));
1375
1377
  const sleep2 = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1376
1378
  let lastRuntime = await fetchVmRuntime(args);
1379
+ args.onAttempt?.(lastRuntime, 1, attempts);
1377
1380
  for (let attempt = 0; attempt < attempts - 1; attempt += 1) {
1378
1381
  if (lastRuntime.hostIpv4 && Object.keys(lastRuntime.mappedPorts ?? {}).length > 0) {
1379
1382
  return lastRuntime;
1380
1383
  }
1381
1384
  await sleep2(delayMs);
1382
1385
  lastRuntime = await fetchVmRuntime(args);
1386
+ args.onAttempt?.(lastRuntime, attempt + 2, attempts);
1383
1387
  }
1384
1388
  if (lastRuntime.diagnostics) {
1385
1389
  lastRuntime.diagnostics.timedOut = true;
@@ -1458,10 +1462,12 @@ async function waitForSetupEndpoint(args) {
1458
1462
  const sleep2 = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1459
1463
  const url = `http://${args.hostIpv4}:${args.setupPort}/health`;
1460
1464
  let result = await defaultHttpProbe(args.fetch, url, Number(args.httpTimeoutMs ?? 1e4));
1465
+ args.onAttempt?.(result, 1, attempts);
1461
1466
  for (let attempt = 1; attempt < attempts; attempt += 1) {
1462
1467
  if (result.ok) return result;
1463
1468
  await sleep2(delayMs);
1464
1469
  result = await defaultHttpProbe(args.fetch, url, Number(args.httpTimeoutMs ?? 1e4));
1470
+ args.onAttempt?.(result, attempt + 1, attempts);
1465
1471
  }
1466
1472
  return result;
1467
1473
  }
@@ -1512,6 +1518,12 @@ async function fetchUcGoPeerMetadata(args) {
1512
1518
  });
1513
1519
  const payload = await response.json().catch(() => null);
1514
1520
  lastPayload = payload;
1521
+ args.onAttempt?.(
1522
+ payload,
1523
+ Boolean(response.ok && payload && typeof payload === "object" && payload.status === "ready"),
1524
+ attempt + 1,
1525
+ attempts
1526
+ );
1515
1527
  if (response.ok && payload && typeof payload === "object" && payload.status === "ready") {
1516
1528
  return payload;
1517
1529
  }
@@ -1653,13 +1665,21 @@ async function executeDeployPlan(plan, dependencies = {}) {
1653
1665
  const hasher = dependencies.hasher ?? defaultHasher;
1654
1666
  const tcpProbe = dependencies.tcpProbe ?? defaultTcpProbe;
1655
1667
  const sleepImpl = dependencies.sleep ?? ((ms) => sleep(ms).then(() => void 0));
1668
+ const log = dependencies.log ?? ((message) => console.log(message));
1656
1669
  const identity = dependencies.sender && dependencies.signer ? { address: dependencies.sender, signer: dependencies.signer } : await createPrivateKeyIdentity(plan.privateKey);
1657
1670
  const candidateCrns = await candidateCrnsForPlan(plan, fetchImpl);
1658
1671
  if (candidateCrns.length === 0) {
1659
1672
  throw new Error("No compatible CRN was available for deployment.");
1660
1673
  }
1674
+ log(`[deploy] profile=${plan.profile} sender=${identity.address}`);
1675
+ log(
1676
+ `[deploy] candidate CRNs=${candidateCrns.map((crn) => `${crn.name ?? crn.hash}:${crn.hash}`).join(", ")}`
1677
+ );
1661
1678
  let lastError = null;
1662
- for (const candidateCrn of candidateCrns) {
1679
+ for (const [candidateIndex, candidateCrn] of candidateCrns.entries()) {
1680
+ log(
1681
+ `[deploy] attempting CRN ${candidateIndex + 1}/${candidateCrns.length}: ${candidateCrn.name ?? candidateCrn.hash} (${candidateCrn.hash})`
1682
+ );
1663
1683
  const content = createInstanceContent({
1664
1684
  address: identity.address,
1665
1685
  name: plan.name,
@@ -1683,21 +1703,33 @@ async function executeDeployPlan(plan, dependencies = {}) {
1683
1703
  channel: plan.channel,
1684
1704
  sync: true
1685
1705
  });
1706
+ log(`[deploy] broadcasted INSTANCE message ${deployment.itemHash} on ${candidateCrn.name ?? candidateCrn.hash}`);
1686
1707
  const inspection = await waitForDeploymentResult(deployment.itemHash, {
1687
1708
  rootfsRef: plan.rootfsItemHash,
1688
1709
  apiHost: plan.apiHost,
1689
1710
  fetch: fetchImpl,
1690
1711
  attempts: plan.waitAttempts,
1691
1712
  delayMs: plan.waitDelayMs,
1692
- sleep: sleepImpl
1713
+ sleep: sleepImpl,
1714
+ onAttempt: (result, attempt, attempts) => {
1715
+ log(
1716
+ `[deploy] Aleph processing ${attempt}/${attempts} for ${deployment.itemHash}: status=${result.status}${result.rejectionReason ? ` reason=${result.rejectionReason}` : ""}`
1717
+ );
1718
+ }
1693
1719
  });
1694
1720
  if (inspection.status === "rejected") {
1721
+ log(
1722
+ `[deploy] CRN ${candidateCrn.name ?? candidateCrn.hash} rejected deployment ${deployment.itemHash}: ${inspection.rejectionReason ?? "no additional reason"}`
1723
+ );
1695
1724
  lastError = new Error(
1696
1725
  `Deployment on ${candidateCrn.name ?? candidateCrn.hash} was rejected: ${inspection.rejectionReason ?? "no additional rejection reason from Aleph"}.`
1697
1726
  );
1698
1727
  continue;
1699
1728
  }
1700
1729
  if (inspection.status !== "processed") {
1730
+ log(
1731
+ `[deploy] deployment ${deployment.itemHash} on ${candidateCrn.name ?? candidateCrn.hash} did not become processed; cleaning up`
1732
+ );
1701
1733
  await cleanupFailedDeployment({
1702
1734
  sender: identity.address,
1703
1735
  instanceItemHash: deployment.itemHash,
@@ -1715,6 +1747,7 @@ async function executeDeployPlan(plan, dependencies = {}) {
1715
1747
  }
1716
1748
  let portForwarding = null;
1717
1749
  if (plan.publishPortForwards && plan.requiredPorts.length > 0) {
1750
+ log(`[deploy] publishing required port-forward aggregate for ${deployment.itemHash}`);
1718
1751
  const aggregate = await ensureInstancePortForwards({
1719
1752
  sender: identity.address,
1720
1753
  instanceItemHash: deployment.itemHash,
@@ -1730,7 +1763,19 @@ async function executeDeployPlan(plan, dependencies = {}) {
1730
1763
  aggregateItemHash: aggregate.aggregateItemHash,
1731
1764
  aggregateStatus: aggregate.aggregateStatus
1732
1765
  };
1766
+ log(
1767
+ `[deploy] port-forward aggregate published: item_hash=${aggregate.aggregateItemHash} status=${aggregate.aggregateStatus}`
1768
+ );
1733
1769
  }
1770
+ if (candidateCrn.address) {
1771
+ log(`[deploy] notifying CRN allocation endpoint ${candidateCrn.address}`);
1772
+ await notifyCrnAllocation({
1773
+ crnUrl: candidateCrn.address,
1774
+ itemHash: deployment.itemHash,
1775
+ fetch: fetchImpl
1776
+ }).catch(() => null);
1777
+ }
1778
+ log(`[deploy] waiting for runtime networking on ${candidateCrn.name ?? candidateCrn.hash}`);
1734
1779
  const runtime = await waitForVmRuntime({
1735
1780
  itemHash: deployment.itemHash,
1736
1781
  fetch: fetchImpl,
@@ -1739,7 +1784,12 @@ async function executeDeployPlan(plan, dependencies = {}) {
1739
1784
  crnListUrl: plan.crnListUrl,
1740
1785
  attempts: plan.runtimeAttempts,
1741
1786
  delayMs: plan.runtimeDelayMs,
1742
- sleep: sleepImpl
1787
+ sleep: sleepImpl,
1788
+ onAttempt: (runtimeAttempt, attempt, attempts) => {
1789
+ log(
1790
+ `[deploy] runtime ${attempt}/${attempts} for ${deployment.itemHash}: state=${runtimeAttempt.diagnostics?.state ?? "unknown"} host=${runtimeAttempt.hostIpv4 ?? "-"} mapped_ports=${Object.keys(runtimeAttempt.mappedPorts ?? {}).length}`
1791
+ );
1792
+ }
1743
1793
  }).catch(() => null);
1744
1794
  const runtimeMetadata = runtime ? {
1745
1795
  allocation: runtime.allocation,
@@ -1763,6 +1813,9 @@ async function executeDeployPlan(plan, dependencies = {}) {
1763
1813
  selectedCrn: { hash: candidateCrn.hash, name: candidateCrn.name ?? "" }
1764
1814
  };
1765
1815
  if (!runtime?.hostIpv4 || Object.keys(runtime?.mappedPorts ?? {}).length === 0) {
1816
+ log(
1817
+ `[deploy] processed deployment ${deployment.itemHash} never exposed usable runtime networking on ${candidateCrn.name ?? candidateCrn.hash}; cleaning up`
1818
+ );
1766
1819
  await cleanupFailedDeployment({
1767
1820
  sender: identity.address,
1768
1821
  instanceItemHash: deployment.itemHash,
@@ -1780,13 +1833,6 @@ async function executeDeployPlan(plan, dependencies = {}) {
1780
1833
  }
1781
1834
  let configuration = null;
1782
1835
  let verification = null;
1783
- if (runtime.selectedCrn?.address) {
1784
- await notifyCrnAllocation({
1785
- crnUrl: runtime.selectedCrn.address,
1786
- itemHash: deployment.itemHash,
1787
- fetch: fetchImpl
1788
- }).catch(() => null);
1789
- }
1790
1836
  if (runtime.hostIpv4 && plan.autoConfigure !== false && plan.profile === "uc-go-peer") {
1791
1837
  const mappedPorts = runtime.mappedPorts ?? {};
1792
1838
  const setupPort = mappedPorts["80"]?.host ?? null;
@@ -1795,6 +1841,7 @@ async function executeDeployPlan(plan, dependencies = {}) {
1795
1841
  const udpPort = mappedPorts["9095"]?.udp === true ? mappedPorts["9095"]?.host ?? null : null;
1796
1842
  const proxyUrl = plan.enableCaddyProxy ? runtime.proxyUrl ?? null : null;
1797
1843
  if (setupPort && runtime.hostIpv4) {
1844
+ log(`[deploy] waiting for temporary setup endpoint on http://${runtime.hostIpv4}:${setupPort}/health`);
1798
1845
  const setupHealth = await waitForSetupEndpoint({
1799
1846
  hostIpv4: runtime.hostIpv4,
1800
1847
  setupPort,
@@ -1802,10 +1849,16 @@ async function executeDeployPlan(plan, dependencies = {}) {
1802
1849
  attempts: plan.setupAttempts,
1803
1850
  delayMs: plan.setupDelayMs,
1804
1851
  httpTimeoutMs: plan.httpTimeoutMs,
1805
- sleep: sleepImpl
1852
+ sleep: sleepImpl,
1853
+ onAttempt: (result, attempt, attempts) => {
1854
+ log(
1855
+ `[deploy] setup endpoint ${attempt}/${attempts}: ok=${result.ok} status=${result.status ?? "-"} error=${result.error ?? "-"}`
1856
+ );
1857
+ }
1806
1858
  });
1807
1859
  runtimeMetadata.setupHealth = setupHealth;
1808
1860
  if (!setupHealth.ok) {
1861
+ log(`[deploy] setup endpoint never became reachable; cleaning up ${deployment.itemHash}`);
1809
1862
  await cleanupFailedDeployment({
1810
1863
  sender: identity.address,
1811
1864
  instanceItemHash: deployment.itemHash,
@@ -1819,6 +1872,7 @@ async function executeDeployPlan(plan, dependencies = {}) {
1819
1872
  lastError = new Error(`Temporary setup endpoint did not become reachable at http://${runtime.hostIpv4}:${setupPort}/health.`);
1820
1873
  continue;
1821
1874
  }
1875
+ log(`[deploy] calling guest /configure for ${deployment.itemHash}`);
1822
1876
  const configureResult = await configureUcGoPeer({
1823
1877
  hostIpv4: runtime.hostIpv4,
1824
1878
  publicIpv6: runtime.ipv6,
@@ -1832,6 +1886,7 @@ async function executeDeployPlan(plan, dependencies = {}) {
1832
1886
  fetch: fetchImpl,
1833
1887
  timeoutMs: plan.configureTimeoutMs
1834
1888
  });
1889
+ log(`[deploy] polling guest /metadata until ready`);
1835
1890
  const metadataResult = await fetchUcGoPeerMetadata({
1836
1891
  hostIpv4: runtime.hostIpv4,
1837
1892
  setupPort,
@@ -1839,7 +1894,10 @@ async function executeDeployPlan(plan, dependencies = {}) {
1839
1894
  attempts: plan.metadataAttempts,
1840
1895
  delayMs: plan.metadataDelayMs,
1841
1896
  timeoutMs: plan.metadataTimeoutMs,
1842
- sleep: sleepImpl
1897
+ sleep: sleepImpl,
1898
+ onAttempt: (_payload, ready, attempt, attempts) => {
1899
+ log(`[deploy] guest metadata ${attempt}/${attempts}: ready=${ready}`);
1900
+ }
1843
1901
  });
1844
1902
  configuration = {
1845
1903
  ...configureResult && typeof configureResult === "object" ? configureResult : {},
@@ -1848,6 +1906,7 @@ async function executeDeployPlan(plan, dependencies = {}) {
1848
1906
  if (plan.verifyReachability !== false) {
1849
1907
  let latestVerification = null;
1850
1908
  for (let attempt = 0; attempt < plan.verifyAttempts; attempt += 1) {
1909
+ log(`[deploy] reachability verification ${attempt + 1}/${plan.verifyAttempts}`);
1851
1910
  latestVerification = await verifyUcGoPeerReachability({
1852
1911
  hostIpv4: runtime.hostIpv4,
1853
1912
  mappedPorts,
@@ -1860,9 +1919,11 @@ async function executeDeployPlan(plan, dependencies = {}) {
1860
1919
  tcpProbe
1861
1920
  });
1862
1921
  if (latestVerification?.ok) {
1922
+ log(`[deploy] reachability verification succeeded`);
1863
1923
  break;
1864
1924
  }
1865
1925
  if (attempt < plan.verifyAttempts - 1) {
1926
+ log(`[deploy] reachability verification not ready yet; sleeping ${plan.verifyDelayMs}ms`);
1866
1927
  await sleepImpl(plan.verifyDelayMs);
1867
1928
  }
1868
1929
  }
@@ -1886,6 +1947,7 @@ async function executeDeployPlan(plan, dependencies = {}) {
1886
1947
  verification
1887
1948
  };
1888
1949
  }
1950
+ log(`[deploy] no candidate CRN succeeded`);
1889
1951
  throw lastError ?? new Error("No compatible CRN deployment attempt succeeded.");
1890
1952
  }
1891
1953
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/node",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Node and GitHub Actions adapters for shared Aleph tooling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,8 +16,8 @@
16
16
  "access": "public"
17
17
  },
18
18
  "dependencies": {
19
- "@le-space/core": "0.1.0",
20
- "@le-space/shared-types": "0.1.0",
19
+ "@le-space/core": "0.1.2",
20
+ "@le-space/shared-types": "0.1.2",
21
21
  "ethers": "^6.15.0"
22
22
  }
23
23
  }