permaweb-deploy 3.4.3 → 3.4.4
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 +18 -14
- package/bin/run.js +0 -0
- package/dist/chunks/display-BgIiyBIu.js +60 -0
- package/dist/chunks/display-BgIiyBIu.js.map +1 -0
- package/dist/chunks/{upload-workflow-zlELdPNp.js → upload-workflow-DMKlwZve.js} +156 -19
- package/dist/chunks/upload-workflow-DMKlwZve.js.map +1 -0
- package/dist/chunks/{uploader-DDS_d-O_.js → uploader-CIHu22Fw.js} +5 -1
- package/dist/chunks/uploader-CIHu22Fw.js.map +1 -0
- package/dist/commands/deploy.js +99 -30
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/upload.js +28 -5
- package/dist/commands/upload.js.map +1 -1
- package/dist/constants/flags.js +9 -0
- package/dist/constants/flags.js.map +1 -1
- package/dist/src/commands/deploy.d.ts.map +1 -1
- package/dist/src/commands/upload.d.ts.map +1 -1
- package/dist/src/constants/flags.d.ts +5 -1
- package/dist/src/constants/flags.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/__tests__/display.test.d.ts +2 -0
- package/dist/src/utils/__tests__/display.test.d.ts.map +1 -0
- package/dist/src/utils/display.d.ts +5 -0
- package/dist/src/utils/display.d.ts.map +1 -0
- package/dist/src/utils/hyperbeam-uploader.d.ts +36 -7
- package/dist/src/utils/hyperbeam-uploader.d.ts.map +1 -1
- package/dist/src/utils/uploader.d.ts +3 -1
- package/dist/src/utils/uploader.d.ts.map +1 -1
- package/dist/src/workflows/upload-workflow.d.ts +7 -1
- package/dist/src/workflows/upload-workflow.d.ts.map +1 -1
- package/dist/tests/setup.d.ts +1 -1
- package/dist/tests/setup.d.ts.map +1 -1
- package/dist/utils/uploader.js +1 -1
- package/dist/workflows/upload-workflow.js +2 -2
- package/package.json +25 -24
- package/dist/chunks/upload-workflow-zlELdPNp.js.map +0 -1
- package/dist/chunks/uploader-DDS_d-O_.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Permaweb Deploy
|
|
2
2
|
|
|
3
|
-
Inspired by the [cookbook github action deployment guide](https://cookbook.arweave.dev/guides/deployment/github-action.html), `permaweb-deploy` is a Node.js command-line tool designed to streamline the deployment of web applications to the permaweb using Arweave. It uploads your build folder or a single file, creates Arweave manifests, and
|
|
3
|
+
Inspired by the [cookbook github action deployment guide](https://cookbook.arweave.dev/guides/deployment/github-action.html), `permaweb-deploy` is a Node.js command-line tool designed to streamline the deployment of web applications to the permaweb using Arweave. It uploads your build folder or a single file, creates Arweave manifests, and can optionally update ArNS (Arweave Name Service) records via ANT (Arweave Name Token) with the transaction ID.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Turbo SDK Integration:** Uses Turbo SDK for fast, reliable file uploads to Arweave
|
|
8
8
|
- **On-Demand Payment:** Pay with ARIO or Base-ETH tokens on-demand during upload
|
|
9
9
|
- **Arweave Manifest v0.2.0:** Creates manifests with fallback support for SPAs
|
|
10
|
-
- **ArNS Updates:** Updates ArNS records via ANT with new transaction IDs and metadata
|
|
10
|
+
- **Optional ArNS Updates:** Updates ArNS records via ANT with new transaction IDs and metadata
|
|
11
11
|
- **Automated Workflow:** Integrates with GitHub Actions for continuous deployment
|
|
12
12
|
- **Git Hash Tagging:** Automatically tags deployments with Git commit hashes
|
|
13
13
|
- **404 Fallback Detection:** Automatically detects and sets 404.html as fallback
|
|
@@ -77,7 +77,7 @@ Run the deploy command without arguments to be guided through all deployment opt
|
|
|
77
77
|
permaweb-deploy deploy
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
This will prompt you for:
|
|
80
|
+
This uploads to the permaweb by default. Use `--use-arns` or `--arns-name` to run the guided ArNS update flow, which will prompt you for:
|
|
81
81
|
|
|
82
82
|
- ArNS name
|
|
83
83
|
- Wallet method (file, string, or environment variable)
|
|
@@ -91,7 +91,10 @@ Use flags for faster, scriptable deployments:
|
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
93
|
# Basic deployment with wallet file
|
|
94
|
-
permaweb-deploy deploy --
|
|
94
|
+
permaweb-deploy deploy --wallet ./wallet.json
|
|
95
|
+
|
|
96
|
+
# Deployment with ArNS update
|
|
97
|
+
permaweb-deploy deploy --use-arns --arns-name my-app --wallet ./wallet.json
|
|
95
98
|
```
|
|
96
99
|
|
|
97
100
|
Deploy using private key directly:
|
|
@@ -118,11 +121,12 @@ Deploy a single file:
|
|
|
118
121
|
permaweb-deploy deploy --arns-name my-app --wallet ./wallet.json --deploy-file ./path/to/file.txt
|
|
119
122
|
```
|
|
120
123
|
|
|
121
|
-
### Upload
|
|
124
|
+
### Upload/deploy without ArNS
|
|
122
125
|
|
|
123
|
-
|
|
126
|
+
`deploy` uploads without updating ArNS by default. You can also use the `upload` command explicitly for the same Turbo upload, dedupe cache, and payment options as deploy, minus ArNS flags:
|
|
124
127
|
|
|
125
128
|
```bash
|
|
129
|
+
permaweb-deploy deploy --wallet ./wallet.json --deploy-folder ./dist
|
|
126
130
|
permaweb-deploy upload --wallet ./wallet.json --deploy-folder ./dist
|
|
127
131
|
permaweb-deploy upload --wallet ./wallet.json --deploy-file ./dist/index.html
|
|
128
132
|
DEPLOY_KEY=$(base64 -i wallet.json) permaweb-deploy upload --deploy-folder ./dist
|
|
@@ -217,14 +221,13 @@ permaweb-deploy upload \
|
|
|
217
221
|
--uploader https://hyperbeam.example.com
|
|
218
222
|
|
|
219
223
|
permaweb-deploy deploy \
|
|
220
|
-
--arns-name my-app \
|
|
221
224
|
--wallet ./wallet.json \
|
|
222
225
|
--deploy-folder ./dist \
|
|
223
226
|
--uploader-type hyperbeam \
|
|
224
227
|
--uploader https://hyperbeam.example.com
|
|
225
228
|
```
|
|
226
229
|
|
|
227
|
-
If the node follows the standard AO-paid HyperBEAM bundler flow,
|
|
230
|
+
If the node follows the standard AO-paid HyperBEAM bundler flow, either command can fund the uploader wallet before uploading:
|
|
228
231
|
|
|
229
232
|
```bash
|
|
230
233
|
permaweb-deploy upload \
|
|
@@ -238,15 +241,16 @@ permaweb-deploy upload \
|
|
|
238
241
|
**Notes:**
|
|
239
242
|
|
|
240
243
|
- Turbo billing and signer behavior follow Turbo.
|
|
241
|
-
- HyperBEAM uploads require an Arweave JWK signer. With `--hyperbeam-auto-fund`, the CLI signs each data item, asks the node's
|
|
242
|
-
- `--hyperbeam-fund-amount` is an optional override for the minimum local ledger balance to ensure, in AO base units. Without it, `--hyperbeam-auto-fund` uses the node's
|
|
244
|
+
- HyperBEAM uploads require an Arweave JWK signer. Before uploading, the CLI checks the node address from `/~meta@1.0/info/address` against `https://arweave.net/wallet/<address>/balance` and aborts if the bundler wallet has 0 AR. With `--hyperbeam-auto-fund`, the CLI signs each data item, asks the node's byte-pricing profile for a quote, sends AO to the node deposit address, imports that deposit through `/~ao-payment@1.0/ingest`, and waits for the uploader's balance at `/ledger~node-process@1.0/now/balance/<address>` before uploading. The default route is `/~bundler@1.0/item?codec-device=ans104@1.0`; override it with `--hyperbeam-upload-path` if your node exposes a different bundler route.
|
|
245
|
+
- `--hyperbeam-fund-amount` is an optional override for the minimum local ledger balance to ensure, in AO base units. Without it, `--hyperbeam-auto-fund` uses the node's byte-pricing quote for the signed byte count. Use `--hyperbeam-token-id` only for a non-default AO token process, and `--hyperbeam-ledger-id` only for a non-default local ledger profile.
|
|
243
246
|
- Use a **base URL only** (e.g. `https://up.arweave.net` or `https://hyperbeam.example.com`), not a path to a specific file or route.
|
|
244
247
|
|
|
245
248
|
### Command Options
|
|
246
249
|
|
|
247
|
-
**`deploy`** (ArNS update):
|
|
250
|
+
**`deploy`** (upload by default, optional ArNS update):
|
|
248
251
|
|
|
249
|
-
- `--arns
|
|
252
|
+
- `--use-arns`: Update an ArNS/ANT record after upload
|
|
253
|
+
- `--arns-name, -n`: The ArNS name to update. Required when using `--use-arns`; also implies ArNS mode for backwards compatibility.
|
|
250
254
|
- `--ario-process, -p`: ARIO process to use (`mainnet`, `testnet`, or a custom process ID). Default: `mainnet`
|
|
251
255
|
- `--deploy-folder, -d`: Folder to deploy. Default: `./dist`
|
|
252
256
|
- `--deploy-file, -f`: Deploy a single file instead of a folder
|
|
@@ -268,7 +272,7 @@ permaweb-deploy upload \
|
|
|
268
272
|
- `--hyperbeam-ledger-id`: Advanced local HyperBEAM ledger ID override
|
|
269
273
|
- `--hyperbeam-ao-state-url`: AO state endpoint used while waiting for auto-fund transfer assignment. Default: `https://state.forward.computer`
|
|
270
274
|
|
|
271
|
-
**`upload`** (
|
|
275
|
+
**`upload`** (explicit upload without ArNS): accepts `--deploy-folder`, `--deploy-file`, wallet/signer flags, uploader flags, `--on-demand` / `--max-token-amount`, and dedupe flags only.
|
|
272
276
|
|
|
273
277
|
### Deduplication
|
|
274
278
|
|
|
@@ -617,7 +621,7 @@ permaweb-deploy/
|
|
|
617
621
|
|
|
618
622
|
- **Dedicated Wallet:** Always use a dedicated wallet for deployments to minimize security risks
|
|
619
623
|
- **Wallet Encoding:** Arweave wallets must be base64 encoded to be used in the deployment script
|
|
620
|
-
- **ArNS Name:**
|
|
624
|
+
- **ArNS Name:** Required only when updating an ANT/ArNS target undername or root record
|
|
621
625
|
- **Turbo Credits:** Ensure your wallet has sufficient Turbo Credits, or use on-demand payment for automatic funding
|
|
622
626
|
- **On-Demand Limits:** Set reasonable `--max-token-amount` limits to prevent unexpected costs
|
|
623
627
|
- **Secret Management:** Keep your `DEPLOY_KEY` secret secure and never commit it to your repository
|
package/bin/run.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import boxen from 'boxen';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
|
+
|
|
5
|
+
const AO_BASE_UNITS = 1000000000000n;
|
|
6
|
+
function formatUploadSize(size) {
|
|
7
|
+
return `${(size.signedBytes ?? size.payloadBytes).toLocaleString()} bytes`;
|
|
8
|
+
}
|
|
9
|
+
function formatUploadCost(cost) {
|
|
10
|
+
if (cost.token !== "AO") {
|
|
11
|
+
return `${cost.amount.toString()}`;
|
|
12
|
+
}
|
|
13
|
+
const whole = cost.amount / AO_BASE_UNITS;
|
|
14
|
+
const fraction = cost.amount % AO_BASE_UNITS;
|
|
15
|
+
const decimal = fraction === 0n ? whole.toString() : `${whole.toString()}.${fraction.toString().padStart(12, "0").replaceAll(/0+$/g, "")}`;
|
|
16
|
+
return `${decimal} AO`;
|
|
17
|
+
}
|
|
18
|
+
function fundingDisplay(section) {
|
|
19
|
+
const fundingLine = section.split("\n").map((line) => line.trim()).find((line) => line.startsWith("- "))?.replace(/^- /, "");
|
|
20
|
+
if (!fundingLine) {
|
|
21
|
+
return section;
|
|
22
|
+
}
|
|
23
|
+
return fundingLine.replace(/^AO: send funds to /, "Sending AO to ").replace(/\. Local ledger:.*$/, "");
|
|
24
|
+
}
|
|
25
|
+
function uploadErrorTable(message, title = "Upload failed") {
|
|
26
|
+
const table = new Table({
|
|
27
|
+
style: { head: [] }
|
|
28
|
+
});
|
|
29
|
+
const sections = message.split(/\n{2,}/).map((section) => section.trim()).filter(Boolean);
|
|
30
|
+
for (const [index, section] of sections.entries()) {
|
|
31
|
+
if (index === 0) {
|
|
32
|
+
table.push(["Error", chalk.red(section)]);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (section.startsWith("Required upload credit:")) {
|
|
36
|
+
table.push([
|
|
37
|
+
"Required upload credit",
|
|
38
|
+
chalk.blue(section.replace(/^Required upload credit:\s*/, ""))
|
|
39
|
+
]);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (section.startsWith("The HyperBEAM node requires AO")) {
|
|
43
|
+
table.push(["Funding", fundingDisplay(section)]);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
table.push(["Note", section]);
|
|
47
|
+
}
|
|
48
|
+
return boxen(`${chalk.red.bold(title)}
|
|
49
|
+
|
|
50
|
+
${table.toString()}`, {
|
|
51
|
+
borderColor: "red",
|
|
52
|
+
borderStyle: "round",
|
|
53
|
+
padding: 1,
|
|
54
|
+
title: chalk.bold("Permaweb Deploy"),
|
|
55
|
+
titleAlignment: "center"
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { formatUploadCost as a, formatUploadSize as f, uploadErrorTable as u };
|
|
60
|
+
//# sourceMappingURL=display-BgIiyBIu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-BgIiyBIu.js","sources":["../../src/utils/display.ts"],"sourcesContent":["import boxen from 'boxen'\nimport chalk from 'chalk'\n// eslint-disable-next-line import/no-named-as-default\nimport Table from 'cli-table3'\n\nimport type { UploadCost, UploadSize } from './hyperbeam-uploader.js'\n\nconst AO_BASE_UNITS = 1_000_000_000_000n\n\nexport function formatUploadSize(size: UploadSize): string {\n return `${(size.signedBytes ?? size.payloadBytes).toLocaleString()} bytes`\n}\n\nexport function formatUploadCost(cost: UploadCost): string {\n if (cost.token !== 'AO') {\n return `${cost.amount.toString()}`\n }\n\n const whole = cost.amount / AO_BASE_UNITS\n const fraction = cost.amount % AO_BASE_UNITS\n const decimal =\n fraction === 0n\n ? whole.toString()\n : `${whole.toString()}.${fraction.toString().padStart(12, '0').replaceAll(/0+$/g, '')}`\n\n return `${decimal} AO`\n}\n\nfunction fundingDisplay(section: string): string {\n const fundingLine = section\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line.startsWith('- '))\n ?.replace(/^- /, '')\n\n if (!fundingLine) {\n return section\n }\n\n return fundingLine\n .replace(/^AO: send funds to /, 'Sending AO to ')\n .replace(/\\. Local ledger:.*$/, '')\n}\n\nexport function uploadErrorTable(message: string, title = 'Upload failed'): string {\n const table = new Table({\n style: { head: [] },\n })\n const sections = message\n .split(/\\n{2,}/)\n .map((section) => section.trim())\n .filter(Boolean)\n\n for (const [index, section] of sections.entries()) {\n if (index === 0) {\n table.push(['Error', chalk.red(section)])\n continue\n }\n\n if (section.startsWith('Required upload credit:')) {\n table.push([\n 'Required upload credit',\n chalk.blue(section.replace(/^Required upload credit:\\s*/, '')),\n ])\n continue\n }\n\n if (section.startsWith('The HyperBEAM node requires AO')) {\n table.push(['Funding', fundingDisplay(section)])\n continue\n }\n\n table.push(['Note', section])\n }\n\n return boxen(`${chalk.red.bold(title)}\\n\\n${table.toString()}`, {\n borderColor: 'red',\n borderStyle: 'round',\n padding: 1,\n title: chalk.bold('Permaweb Deploy'),\n titleAlignment: 'center',\n })\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { TurboFactory, ETHToTokenAmount, ARIOToTokenAmount, OnDemandFunding } from '@ardrive/turbo-sdk';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import ora from 'ora';
|
|
6
|
-
import { l as loadCache, u as uploadFile, c as cleanupCache, s as saveCache, a as uploadFolder } from './uploader-
|
|
6
|
+
import { l as loadCache, u as uploadFile, c as cleanupCache, s as saveCache, a as uploadFolder } from './uploader-CIHu22Fw.js';
|
|
7
7
|
import { createHash } from 'node:crypto';
|
|
8
8
|
import { createRequire } from 'node:module';
|
|
9
9
|
import { Readable } from 'node:stream';
|
|
@@ -548,6 +548,8 @@ async function fetchHyperbeamOperatorAddress(fetcher, nodeUrl) {
|
|
|
548
548
|
|
|
549
549
|
const require$1 = createRequire(import.meta.url);
|
|
550
550
|
const { ArweaveSigner, DataItem, createData } = require$1("@dha-team/arbundles");
|
|
551
|
+
const AO_BASE_UNITS = 1000000000000n;
|
|
552
|
+
const ARWEAVE_GATEWAY = "https://arweave.net";
|
|
551
553
|
async function readableToBuffer(stream) {
|
|
552
554
|
const chunks = [];
|
|
553
555
|
for await (const chunk of stream) {
|
|
@@ -593,6 +595,24 @@ function parseHyperbeamFundAmount(value) {
|
|
|
593
595
|
}
|
|
594
596
|
return BigInt(value);
|
|
595
597
|
}
|
|
598
|
+
function formatAoAmount(amount) {
|
|
599
|
+
const whole = amount / AO_BASE_UNITS;
|
|
600
|
+
const fraction = amount % AO_BASE_UNITS;
|
|
601
|
+
if (fraction === 0n) {
|
|
602
|
+
return `${whole.toString()} AO`;
|
|
603
|
+
}
|
|
604
|
+
return `${whole.toString()}.${fraction.toString().padStart(12, "0").replaceAll(/0+$/g, "")} AO`;
|
|
605
|
+
}
|
|
606
|
+
function responsePreview(body) {
|
|
607
|
+
const preview = body.replaceAll(/\s+/g, " ").trim();
|
|
608
|
+
if (!preview) {
|
|
609
|
+
return void 0;
|
|
610
|
+
}
|
|
611
|
+
if (/^(<!doctype html\b|<html\b)/i.test(preview)) {
|
|
612
|
+
return "HTML error response";
|
|
613
|
+
}
|
|
614
|
+
return preview.slice(0, 300);
|
|
615
|
+
}
|
|
596
616
|
async function ensureHyperbeamCredit(options, profile) {
|
|
597
617
|
const jwk = JSON.parse(Buffer.from(options.deployKey, "base64").toString("utf8"));
|
|
598
618
|
const recipient = arweaveAddressFromJwk(jwk);
|
|
@@ -663,9 +683,46 @@ async function autoFundQuotedHyperbeamLedger(options) {
|
|
|
663
683
|
profile
|
|
664
684
|
);
|
|
665
685
|
}
|
|
666
|
-
function
|
|
686
|
+
async function quoteHyperbeamUpload(options) {
|
|
687
|
+
const profile = await discoverHyperbeamAoBundlerProfile({
|
|
688
|
+
ledgerId: options.ledgerId,
|
|
689
|
+
nodeUrl: options.uploader,
|
|
690
|
+
tokenId: options.tokenId
|
|
691
|
+
});
|
|
692
|
+
const quote = await new HyperbalanceClient({ nodeUrl: options.uploader }).quoteAuto({
|
|
693
|
+
action: options.quoteAction ?? "hyperbeam-upload",
|
|
694
|
+
params: { bytes: options.signedBytes },
|
|
695
|
+
profile
|
|
696
|
+
});
|
|
697
|
+
return { amount: quote.amount, ledgerId: quote.ledgerId, tokenId: quote.tokenId };
|
|
698
|
+
}
|
|
699
|
+
function hyperbeamBundlerLink(uploader, id, isManifest = false) {
|
|
667
700
|
const normalizedBase = uploader.endsWith("/") ? uploader : `${uploader}/`;
|
|
668
|
-
return new URL(
|
|
701
|
+
return new URL(`${encodeURIComponent(id)}${isManifest ? "/" : ""}`, normalizedBase).toString();
|
|
702
|
+
}
|
|
703
|
+
async function preflightHyperbeamBundlerArBalance(uploader) {
|
|
704
|
+
const nodeUrl = uploader.replace(/\/+$/, "");
|
|
705
|
+
const addressRes = await fetch(`${nodeUrl}/~meta@1.0/info/address`);
|
|
706
|
+
if (!addressRes.ok) {
|
|
707
|
+
throw new Error(`HyperBEAM bundler address check failed with HTTP ${addressRes.status}`);
|
|
708
|
+
}
|
|
709
|
+
const address = (await addressRes.text()).trim();
|
|
710
|
+
if (!address) {
|
|
711
|
+
throw new Error("HyperBEAM bundler address check returned an empty address");
|
|
712
|
+
}
|
|
713
|
+
const balanceRes = await fetch(`${ARWEAVE_GATEWAY}/wallet/${encodeURIComponent(address)}/balance`);
|
|
714
|
+
if (!balanceRes.ok) {
|
|
715
|
+
throw new Error(`HyperBEAM bundler AR balance check failed with HTTP ${balanceRes.status}`);
|
|
716
|
+
}
|
|
717
|
+
const balance = (await balanceRes.text()).trim();
|
|
718
|
+
if (!/^\d+$/.test(balance)) {
|
|
719
|
+
throw new Error("HyperBEAM bundler AR balance check returned an invalid balance");
|
|
720
|
+
}
|
|
721
|
+
if (BigInt(balance) === 0n) {
|
|
722
|
+
throw new Error(
|
|
723
|
+
`HyperBEAM bundler wallet ${address} has 0 AR; upload aborted because the node cannot seed data to Arweave.`
|
|
724
|
+
);
|
|
725
|
+
}
|
|
669
726
|
}
|
|
670
727
|
function responseId(headers, body) {
|
|
671
728
|
const headerId = headers.get("id");
|
|
@@ -679,30 +736,82 @@ function responseId(headers, body) {
|
|
|
679
736
|
return void 0;
|
|
680
737
|
}
|
|
681
738
|
}
|
|
739
|
+
function cleanAutoFundErrorMessage(message) {
|
|
740
|
+
const jsonStart = message.indexOf("{");
|
|
741
|
+
if (jsonStart >= 0) {
|
|
742
|
+
try {
|
|
743
|
+
const parsed = JSON.parse(message.slice(jsonStart));
|
|
744
|
+
if (parsed.error) {
|
|
745
|
+
return parsed.error.replace(/^Error:\s*/, "");
|
|
746
|
+
}
|
|
747
|
+
} catch {
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return message;
|
|
751
|
+
}
|
|
752
|
+
function autoFundFailureNote(message) {
|
|
753
|
+
if (/rate limit exceeded/i.test(message)) {
|
|
754
|
+
return "AO token transfer was rate limited. Check that the wallet has enough spendable AO before retrying auto-fund.";
|
|
755
|
+
}
|
|
756
|
+
return "Check the wallet or node ledger before retrying auto-fund; the AO transfer may already have been submitted.";
|
|
757
|
+
}
|
|
682
758
|
class HyperbeamBundlerClient {
|
|
683
759
|
autoFund;
|
|
760
|
+
quote;
|
|
761
|
+
seedPreflight;
|
|
684
762
|
signer;
|
|
685
763
|
uploader;
|
|
686
764
|
uploadUrl;
|
|
687
|
-
constructor({ autoFund, deployKey, uploadPath, uploader }) {
|
|
765
|
+
constructor({ autoFund, deployKey, quote, uploadPath, uploader }) {
|
|
688
766
|
const jwk = JSON.parse(Buffer.from(deployKey, "base64").toString("utf8"));
|
|
689
767
|
this.autoFund = autoFund;
|
|
768
|
+
this.quote = quote ?? { uploader };
|
|
690
769
|
this.signer = new ArweaveSigner(jwk);
|
|
691
770
|
this.uploader = uploader;
|
|
692
771
|
this.uploadUrl = normalizeUploadUrl(uploader, uploadPath);
|
|
693
772
|
}
|
|
694
773
|
async uploadFile(args) {
|
|
774
|
+
this.seedPreflight ??= preflightHyperbeamBundlerArBalance(this.uploader);
|
|
775
|
+
await this.seedPreflight;
|
|
695
776
|
const data = args.file ? typeof args.file === "string" ? fs.readFileSync(args.file) : args.file : await streamToBuffer(args.fileStreamFactory?.() ?? Readable.from([]));
|
|
696
777
|
const tags = args.dataItemOpts?.tags ?? [];
|
|
697
778
|
const item = createData(data, this.signer, { tags });
|
|
698
779
|
await item.sign(this.signer);
|
|
699
780
|
const raw = Buffer.from(item.getRaw());
|
|
700
781
|
const localId = item.id || toBase64Url(new DataItem(raw).id);
|
|
782
|
+
const size = { payloadBytes: data.length, signedBytes: raw.length };
|
|
783
|
+
let cost;
|
|
701
784
|
if (this.autoFund) {
|
|
702
|
-
await
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
785
|
+
const quote = await quoteHyperbeamUpload({ ...this.quote, signedBytes: raw.length });
|
|
786
|
+
cost = { amount: quote.amount, token: "AO" };
|
|
787
|
+
try {
|
|
788
|
+
await autoFundQuotedHyperbeamLedger({
|
|
789
|
+
...this.autoFund,
|
|
790
|
+
ledgerId: this.autoFund.ledgerId ?? quote.ledgerId,
|
|
791
|
+
minimumBalance: this.autoFund.minimumBalance ?? quote.amount,
|
|
792
|
+
signedBytes: raw.length,
|
|
793
|
+
tokenId: this.autoFund.tokenId ?? quote.tokenId
|
|
794
|
+
});
|
|
795
|
+
} catch (error) {
|
|
796
|
+
const message = cleanAutoFundErrorMessage(
|
|
797
|
+
error instanceof Error ? error.message : String(error)
|
|
798
|
+
);
|
|
799
|
+
throw new Error(
|
|
800
|
+
[
|
|
801
|
+
`HyperBEAM auto-fund failed: ${message}`,
|
|
802
|
+
`Required upload credit: ${formatAoAmount(cost.amount)}`,
|
|
803
|
+
autoFundFailureNote(message),
|
|
804
|
+
await this.paymentHint(false)
|
|
805
|
+
].filter(Boolean).join("\n\n")
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
} else {
|
|
809
|
+
try {
|
|
810
|
+
const quote = await quoteHyperbeamUpload({ ...this.quote, signedBytes: raw.length });
|
|
811
|
+
cost = { amount: quote.amount, token: "AO" };
|
|
812
|
+
} catch {
|
|
813
|
+
cost = void 0;
|
|
814
|
+
}
|
|
706
815
|
}
|
|
707
816
|
const res = await fetch(this.uploadUrl, {
|
|
708
817
|
body: raw,
|
|
@@ -714,7 +823,7 @@ class HyperbeamBundlerClient {
|
|
|
714
823
|
});
|
|
715
824
|
const body = await res.text();
|
|
716
825
|
if (!res.ok) {
|
|
717
|
-
const preview = body
|
|
826
|
+
const preview = responsePreview(body);
|
|
718
827
|
const paymentHint = res.status === 402 ? await this.paymentHint() : void 0;
|
|
719
828
|
throw new Error(
|
|
720
829
|
[
|
|
@@ -723,23 +832,36 @@ class HyperbeamBundlerClient {
|
|
|
723
832
|
].filter(Boolean).join("\n\n")
|
|
724
833
|
);
|
|
725
834
|
}
|
|
726
|
-
return { id: responseId(res.headers, body) || localId };
|
|
835
|
+
return { cost, id: responseId(res.headers, body) || localId, size };
|
|
727
836
|
}
|
|
728
|
-
async paymentHint() {
|
|
837
|
+
async paymentHint(includeAutoFundInstruction = true) {
|
|
729
838
|
try {
|
|
730
839
|
return hyperbeamAoFundingHint(
|
|
731
|
-
await discoverHyperbeamAoBundlerProfile({ nodeUrl: this.uploader })
|
|
840
|
+
await discoverHyperbeamAoBundlerProfile({ nodeUrl: this.uploader }),
|
|
841
|
+
{ includeAutoFundInstruction }
|
|
732
842
|
);
|
|
733
843
|
} catch {
|
|
734
|
-
|
|
844
|
+
try {
|
|
845
|
+
const operator = await fetch(
|
|
846
|
+
`${this.uploader.replace(/\/+$/, "")}/~meta@1.0/info/address`
|
|
847
|
+
).then((res) => res.ok ? res.text() : void 0);
|
|
848
|
+
if (!operator?.trim()) return void 0;
|
|
849
|
+
return [
|
|
850
|
+
"The HyperBEAM node requires AO in its local ledger:",
|
|
851
|
+
`- AO: send funds to ${operator.trim()}. Local ledger: ${HYPERBEAM_DEFAULT_LEDGER_ID} at ${HYPERBEAM_DEFAULT_LEDGER_ROUTE}.`,
|
|
852
|
+
includeAutoFundInstruction ? "Use --hyperbeam-auto-fund to transfer AO and import the credit automatically before upload." : void 0
|
|
853
|
+
].filter(Boolean).join("\n");
|
|
854
|
+
} catch {
|
|
855
|
+
return void 0;
|
|
856
|
+
}
|
|
735
857
|
}
|
|
736
858
|
}
|
|
737
859
|
}
|
|
738
|
-
function hyperbeamAoFundingHint(profile) {
|
|
860
|
+
function hyperbeamAoFundingHint(profile, options = {}) {
|
|
739
861
|
const lines = profile.tokens.map((token) => {
|
|
740
862
|
const depositAddress = token.depositAddress ?? profile.node?.operator;
|
|
741
863
|
if (!depositAddress) return;
|
|
742
|
-
const label = token.ticker ? `${token.ticker} (${token.id})` : token.id;
|
|
864
|
+
const label = token.ticker === "AO" ? "AO" : token.ticker ? `${token.ticker} (${token.id})` : token.id;
|
|
743
865
|
const ledger = token.ledgerId ? profile.ledgers.find((candidate) => candidate.id === token.ledgerId) : void 0;
|
|
744
866
|
const ledgerInfo = ledger ? ` Local ledger: ${ledger.id}${ledger.route ? ` at ${ledger.route}` : ""}.` : "";
|
|
745
867
|
return `- ${label}: send funds to ${depositAddress}.${ledgerInfo}`;
|
|
@@ -748,8 +870,8 @@ function hyperbeamAoFundingHint(profile) {
|
|
|
748
870
|
return [
|
|
749
871
|
"The HyperBEAM node requires AO in its local ledger:",
|
|
750
872
|
...lines,
|
|
751
|
-
"Use --hyperbeam-auto-fund to transfer AO and import the credit automatically before upload."
|
|
752
|
-
].join("\n");
|
|
873
|
+
options.includeAutoFundInstruction === false ? void 0 : "Use --hyperbeam-auto-fund to transfer AO and import the credit automatically before upload."
|
|
874
|
+
].filter(Boolean).join("\n");
|
|
753
875
|
}
|
|
754
876
|
|
|
755
877
|
function getFolderSize(folderPath) {
|
|
@@ -793,6 +915,11 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
793
915
|
uploadClient = new HyperbeamBundlerClient({
|
|
794
916
|
autoFund,
|
|
795
917
|
deployKey,
|
|
918
|
+
quote: {
|
|
919
|
+
ledgerId: config["hyperbeam-ledger-id"],
|
|
920
|
+
tokenId: config["hyperbeam-token-id"],
|
|
921
|
+
uploader: config.uploader
|
|
922
|
+
},
|
|
796
923
|
uploadPath: config["hyperbeam-upload-path"] ?? "/~bundler@1.0/item?codec-device=ans104@1.0",
|
|
797
924
|
uploader: config.uploader
|
|
798
925
|
});
|
|
@@ -869,6 +996,8 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
869
996
|
}
|
|
870
997
|
}
|
|
871
998
|
let txOrManifestId;
|
|
999
|
+
let cost;
|
|
1000
|
+
let size;
|
|
872
1001
|
try {
|
|
873
1002
|
if (config["deploy-file"]) {
|
|
874
1003
|
const filePath = expandPath(config["deploy-file"]);
|
|
@@ -880,6 +1009,8 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
880
1009
|
io.error("File upload failed: no transaction ID returned");
|
|
881
1010
|
}
|
|
882
1011
|
txOrManifestId = uploadResult.transactionId;
|
|
1012
|
+
cost = uploadResult.cost;
|
|
1013
|
+
size = uploadResult.size;
|
|
883
1014
|
if (uploadResult.updatedCache && config["dedupe-cache-max-entries"] > 0) {
|
|
884
1015
|
cache = cleanupCache(uploadResult.updatedCache, config["dedupe-cache-max-entries"]);
|
|
885
1016
|
saveCache(cache);
|
|
@@ -905,6 +1036,8 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
905
1036
|
io.error("Folder upload failed: no transaction ID returned");
|
|
906
1037
|
}
|
|
907
1038
|
txOrManifestId = uploadResult.transactionId;
|
|
1039
|
+
cost = uploadResult.cost;
|
|
1040
|
+
size = uploadResult.size;
|
|
908
1041
|
if (uploadResult.updatedCache && config["dedupe-cache-max-entries"] > 0) {
|
|
909
1042
|
cache = cleanupCache(uploadResult.updatedCache, config["dedupe-cache-max-entries"]);
|
|
910
1043
|
saveCache(cache);
|
|
@@ -923,8 +1056,12 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
923
1056
|
const errorMessage = uploadError instanceof Error ? uploadError.message : String(uploadError);
|
|
924
1057
|
io.error(`Upload failed: ${errorMessage}`);
|
|
925
1058
|
}
|
|
926
|
-
return
|
|
1059
|
+
return {
|
|
1060
|
+
cost,
|
|
1061
|
+
size,
|
|
1062
|
+
transactionId: txOrManifestId
|
|
1063
|
+
};
|
|
927
1064
|
}
|
|
928
1065
|
|
|
929
1066
|
export { hyperbeamBundlerLink as h, runUploadWorkflow as r };
|
|
930
|
-
//# sourceMappingURL=upload-workflow-
|
|
1067
|
+
//# sourceMappingURL=upload-workflow-DMKlwZve.js.map
|