permaweb-deploy 3.4.3 → 3.4.5
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-CyJXNtkl.js +53 -0
- package/dist/chunks/display-CyJXNtkl.js.map +1 -0
- package/dist/chunks/{upload-workflow-zlELdPNp.js → upload-workflow-CcYev67_.js} +204 -21
- package/dist/chunks/upload-workflow-CcYev67_.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 +87 -82
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/upload.js +35 -50
- 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/chalk.d.ts +9 -0
- package/dist/src/utils/chalk.d.ts.map +1 -0
- package/dist/src/utils/display.d.ts +7 -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 -3
- package/dist/workflows/upload-workflow.js.map +1 -1
- package/package.json +26 -28
- 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,53 @@
|
|
|
1
|
+
import { c as chalk } from './upload-workflow-CcYev67_.js';
|
|
2
|
+
|
|
3
|
+
const AO_BASE_UNITS = 1000000000000n;
|
|
4
|
+
function formatUploadSize(size) {
|
|
5
|
+
return `${(size.signedBytes ?? size.payloadBytes).toLocaleString()} bytes`;
|
|
6
|
+
}
|
|
7
|
+
function formatUploadCost(cost) {
|
|
8
|
+
if (cost.token !== "AO") {
|
|
9
|
+
return `${cost.amount.toString()}`;
|
|
10
|
+
}
|
|
11
|
+
const whole = cost.amount / AO_BASE_UNITS;
|
|
12
|
+
const fraction = cost.amount % AO_BASE_UNITS;
|
|
13
|
+
const decimal = fraction === 0n ? whole.toString() : `${whole.toString()}.${fraction.toString().padStart(12, "0").replaceAll(/0+$/g, "")}`;
|
|
14
|
+
return `${decimal} AO`;
|
|
15
|
+
}
|
|
16
|
+
function formatDisplayRows(rows) {
|
|
17
|
+
return rows.map(([label, value]) => `${label}: ${value}`).join("\n");
|
|
18
|
+
}
|
|
19
|
+
function fundingDisplay(section) {
|
|
20
|
+
const fundingLine = section.split("\n").map((line) => line.trim()).find((line) => line.startsWith("- "))?.replace(/^- /, "");
|
|
21
|
+
if (!fundingLine) {
|
|
22
|
+
return section;
|
|
23
|
+
}
|
|
24
|
+
return fundingLine.replace(/^AO: send funds to /, "Sending AO to ").replace(/\. Local ledger:.*$/, "");
|
|
25
|
+
}
|
|
26
|
+
function formatUploadError(message, title = "Upload failed") {
|
|
27
|
+
const rows = [];
|
|
28
|
+
const sections = message.split(/\n{2,}/).map((section) => section.trim()).filter(Boolean);
|
|
29
|
+
for (const [index, section] of sections.entries()) {
|
|
30
|
+
if (index === 0) {
|
|
31
|
+
rows.push(["Error", chalk.red(section)]);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (section.startsWith("Required upload credit:")) {
|
|
35
|
+
rows.push([
|
|
36
|
+
"Required upload credit",
|
|
37
|
+
chalk.blue(section.replace(/^Required upload credit:\s*/, ""))
|
|
38
|
+
]);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (section.startsWith("The HyperBEAM node requires AO")) {
|
|
42
|
+
rows.push(["Funding", fundingDisplay(section)]);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
rows.push(["Note", section]);
|
|
46
|
+
}
|
|
47
|
+
return `${chalk.bold(chalk.red(title))}
|
|
48
|
+
|
|
49
|
+
${formatDisplayRows(rows)}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { formatUploadError as a, formatUploadSize as b, formatUploadCost as c, formatDisplayRows as f };
|
|
53
|
+
//# sourceMappingURL=display-CyJXNtkl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-CyJXNtkl.js","sources":["../../src/utils/display.ts"],"sourcesContent":["import { chalk } from './chalk.js'\nimport type { UploadCost, UploadSize } from './hyperbeam-uploader.js'\n\nconst AO_BASE_UNITS = 1_000_000_000_000n\n\nexport type DisplayRow = [label: string, value: string]\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\nexport function formatDisplayRows(rows: DisplayRow[]): string {\n return rows.map(([label, value]) => `${label}: ${value}`).join('\\n')\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 formatUploadError(message: string, title = 'Upload failed'): string {\n const rows: DisplayRow[] = []\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 rows.push(['Error', chalk.red(section)])\n continue\n }\n\n if (section.startsWith('Required upload credit:')) {\n rows.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 rows.push(['Funding', fundingDisplay(section)])\n continue\n }\n\n rows.push(['Note', section])\n }\n\n return `${chalk.bold(chalk.red(title))}\\n\\n${formatDisplayRows(rows)}`\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { TurboFactory, ETHToTokenAmount, ARIOToTokenAmount, OnDemandFunding } from '@ardrive/turbo-sdk';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
4
|
import ora from 'ora';
|
|
6
|
-
import { l as loadCache, u as uploadFile, c as cleanupCache, s as saveCache, a as uploadFolder } from './uploader-
|
|
5
|
+
import { l as loadCache, u as uploadFile, c as cleanupCache, s as saveCache, a as uploadFolder } from './uploader-CIHu22Fw.js';
|
|
7
6
|
import { createHash } from 'node:crypto';
|
|
8
7
|
import { createRequire } from 'node:module';
|
|
9
8
|
import { Readable } from 'node:stream';
|
|
@@ -11,6 +10,53 @@ import { createDataItemSigner, message } from '@permaweb/aoconnect';
|
|
|
11
10
|
import { expandPath } from '../utils/path.js';
|
|
12
11
|
import { createSigner } from '../utils/signer.js';
|
|
13
12
|
|
|
13
|
+
const RESET = "\x1B[0m";
|
|
14
|
+
const WHITE = "\x1B[37m";
|
|
15
|
+
const BG_BLACK = "\x1B[40m";
|
|
16
|
+
const colorCodes = [
|
|
17
|
+
["bold", "\x1B[1m"],
|
|
18
|
+
["dim", "\x1B[2m"],
|
|
19
|
+
["italic", "\x1B[3m"],
|
|
20
|
+
["underline", "\x1B[4m"],
|
|
21
|
+
["inverse", "\x1B[7m"],
|
|
22
|
+
["strikethrough", "\x1B[9m"],
|
|
23
|
+
["black", "\x1B[30m"],
|
|
24
|
+
["red", "\x1B[31m"],
|
|
25
|
+
["green", "\x1B[32m"],
|
|
26
|
+
["yellow", "\x1B[33m"],
|
|
27
|
+
["blue", "\x1B[34m"],
|
|
28
|
+
["magenta", "\x1B[35m"],
|
|
29
|
+
["cyan", "\x1B[36m"],
|
|
30
|
+
["white", WHITE],
|
|
31
|
+
["gray", "\x1B[90m"],
|
|
32
|
+
["grey", "\x1B[90m"],
|
|
33
|
+
["blackBright", "\x1B[90m"],
|
|
34
|
+
["redBright", "\x1B[91m"],
|
|
35
|
+
["greenBright", "\x1B[92m"],
|
|
36
|
+
["yellowBright", "\x1B[93m"],
|
|
37
|
+
["blueBright", "\x1B[94m"],
|
|
38
|
+
["magentaBright", "\x1B[95m"],
|
|
39
|
+
["cyanBright", "\x1B[96m"],
|
|
40
|
+
["whiteBright", "\x1B[97m"],
|
|
41
|
+
["bgBlack", BG_BLACK],
|
|
42
|
+
["bgRed", "\x1B[41m"],
|
|
43
|
+
["bgGreen", "\x1B[42m"],
|
|
44
|
+
["bgYellow", "\x1B[43m"],
|
|
45
|
+
["bgBlue", "\x1B[44m"],
|
|
46
|
+
["bgMagenta", "\x1B[45m"],
|
|
47
|
+
["bgCyan", "\x1B[46m"],
|
|
48
|
+
["bgWhite", "\x1B[47m"],
|
|
49
|
+
["bgBlackBright", "\x1B[100m"]
|
|
50
|
+
];
|
|
51
|
+
const createColorFunction = (code) => (text) => `${code}${String(text)}${RESET}`;
|
|
52
|
+
const colorFunctions = {};
|
|
53
|
+
for (const [name, code] of colorCodes) {
|
|
54
|
+
colorFunctions[name] = createColorFunction(code);
|
|
55
|
+
}
|
|
56
|
+
const chalk = Object.assign(colorFunctions, {
|
|
57
|
+
highlight: (text) => `${BG_BLACK}${WHITE}${String(text)} ${RESET}`
|
|
58
|
+
});
|
|
59
|
+
|
|
14
60
|
class HyperbalanceError extends Error {
|
|
15
61
|
cause;
|
|
16
62
|
constructor(message, cause) {
|
|
@@ -548,6 +594,8 @@ async function fetchHyperbeamOperatorAddress(fetcher, nodeUrl) {
|
|
|
548
594
|
|
|
549
595
|
const require$1 = createRequire(import.meta.url);
|
|
550
596
|
const { ArweaveSigner, DataItem, createData } = require$1("@dha-team/arbundles");
|
|
597
|
+
const AO_BASE_UNITS = 1000000000000n;
|
|
598
|
+
const ARWEAVE_GATEWAY = "https://arweave.net";
|
|
551
599
|
async function readableToBuffer(stream) {
|
|
552
600
|
const chunks = [];
|
|
553
601
|
for await (const chunk of stream) {
|
|
@@ -593,6 +641,24 @@ function parseHyperbeamFundAmount(value) {
|
|
|
593
641
|
}
|
|
594
642
|
return BigInt(value);
|
|
595
643
|
}
|
|
644
|
+
function formatAoAmount(amount) {
|
|
645
|
+
const whole = amount / AO_BASE_UNITS;
|
|
646
|
+
const fraction = amount % AO_BASE_UNITS;
|
|
647
|
+
if (fraction === 0n) {
|
|
648
|
+
return `${whole.toString()} AO`;
|
|
649
|
+
}
|
|
650
|
+
return `${whole.toString()}.${fraction.toString().padStart(12, "0").replaceAll(/0+$/g, "")} AO`;
|
|
651
|
+
}
|
|
652
|
+
function responsePreview(body) {
|
|
653
|
+
const preview = body.replaceAll(/\s+/g, " ").trim();
|
|
654
|
+
if (!preview) {
|
|
655
|
+
return void 0;
|
|
656
|
+
}
|
|
657
|
+
if (/^(<!doctype html\b|<html\b)/i.test(preview)) {
|
|
658
|
+
return "HTML error response";
|
|
659
|
+
}
|
|
660
|
+
return preview.slice(0, 300);
|
|
661
|
+
}
|
|
596
662
|
async function ensureHyperbeamCredit(options, profile) {
|
|
597
663
|
const jwk = JSON.parse(Buffer.from(options.deployKey, "base64").toString("utf8"));
|
|
598
664
|
const recipient = arweaveAddressFromJwk(jwk);
|
|
@@ -663,9 +729,46 @@ async function autoFundQuotedHyperbeamLedger(options) {
|
|
|
663
729
|
profile
|
|
664
730
|
);
|
|
665
731
|
}
|
|
666
|
-
function
|
|
732
|
+
async function quoteHyperbeamUpload(options) {
|
|
733
|
+
const profile = await discoverHyperbeamAoBundlerProfile({
|
|
734
|
+
ledgerId: options.ledgerId,
|
|
735
|
+
nodeUrl: options.uploader,
|
|
736
|
+
tokenId: options.tokenId
|
|
737
|
+
});
|
|
738
|
+
const quote = await new HyperbalanceClient({ nodeUrl: options.uploader }).quoteAuto({
|
|
739
|
+
action: options.quoteAction ?? "hyperbeam-upload",
|
|
740
|
+
params: { bytes: options.signedBytes },
|
|
741
|
+
profile
|
|
742
|
+
});
|
|
743
|
+
return { amount: quote.amount, ledgerId: quote.ledgerId, tokenId: quote.tokenId };
|
|
744
|
+
}
|
|
745
|
+
function hyperbeamBundlerLink(uploader, id, isManifest = false) {
|
|
667
746
|
const normalizedBase = uploader.endsWith("/") ? uploader : `${uploader}/`;
|
|
668
|
-
return new URL(
|
|
747
|
+
return new URL(`${encodeURIComponent(id)}${isManifest ? "/" : ""}`, normalizedBase).toString();
|
|
748
|
+
}
|
|
749
|
+
async function preflightHyperbeamBundlerArBalance(uploader) {
|
|
750
|
+
const nodeUrl = uploader.replace(/\/+$/, "");
|
|
751
|
+
const addressRes = await fetch(`${nodeUrl}/~meta@1.0/info/address`);
|
|
752
|
+
if (!addressRes.ok) {
|
|
753
|
+
throw new Error(`HyperBEAM bundler address check failed with HTTP ${addressRes.status}`);
|
|
754
|
+
}
|
|
755
|
+
const address = (await addressRes.text()).trim();
|
|
756
|
+
if (!address) {
|
|
757
|
+
throw new Error("HyperBEAM bundler address check returned an empty address");
|
|
758
|
+
}
|
|
759
|
+
const balanceRes = await fetch(`${ARWEAVE_GATEWAY}/wallet/${encodeURIComponent(address)}/balance`);
|
|
760
|
+
if (!balanceRes.ok) {
|
|
761
|
+
throw new Error(`HyperBEAM bundler AR balance check failed with HTTP ${balanceRes.status}`);
|
|
762
|
+
}
|
|
763
|
+
const balance = (await balanceRes.text()).trim();
|
|
764
|
+
if (!/^\d+$/.test(balance)) {
|
|
765
|
+
throw new Error("HyperBEAM bundler AR balance check returned an invalid balance");
|
|
766
|
+
}
|
|
767
|
+
if (BigInt(balance) === 0n) {
|
|
768
|
+
throw new Error(
|
|
769
|
+
`HyperBEAM bundler wallet ${address} has 0 AR; upload aborted because the node cannot seed data to Arweave.`
|
|
770
|
+
);
|
|
771
|
+
}
|
|
669
772
|
}
|
|
670
773
|
function responseId(headers, body) {
|
|
671
774
|
const headerId = headers.get("id");
|
|
@@ -679,30 +782,82 @@ function responseId(headers, body) {
|
|
|
679
782
|
return void 0;
|
|
680
783
|
}
|
|
681
784
|
}
|
|
785
|
+
function cleanAutoFundErrorMessage(message) {
|
|
786
|
+
const jsonStart = message.indexOf("{");
|
|
787
|
+
if (jsonStart >= 0) {
|
|
788
|
+
try {
|
|
789
|
+
const parsed = JSON.parse(message.slice(jsonStart));
|
|
790
|
+
if (parsed.error) {
|
|
791
|
+
return parsed.error.replace(/^Error:\s*/, "");
|
|
792
|
+
}
|
|
793
|
+
} catch {
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return message;
|
|
797
|
+
}
|
|
798
|
+
function autoFundFailureNote(message) {
|
|
799
|
+
if (/rate limit exceeded/i.test(message)) {
|
|
800
|
+
return "AO token transfer was rate limited. Check that the wallet has enough spendable AO before retrying auto-fund.";
|
|
801
|
+
}
|
|
802
|
+
return "Check the wallet or node ledger before retrying auto-fund; the AO transfer may already have been submitted.";
|
|
803
|
+
}
|
|
682
804
|
class HyperbeamBundlerClient {
|
|
683
805
|
autoFund;
|
|
806
|
+
quote;
|
|
807
|
+
seedPreflight;
|
|
684
808
|
signer;
|
|
685
809
|
uploader;
|
|
686
810
|
uploadUrl;
|
|
687
|
-
constructor({ autoFund, deployKey, uploadPath, uploader }) {
|
|
811
|
+
constructor({ autoFund, deployKey, quote, uploadPath, uploader }) {
|
|
688
812
|
const jwk = JSON.parse(Buffer.from(deployKey, "base64").toString("utf8"));
|
|
689
813
|
this.autoFund = autoFund;
|
|
814
|
+
this.quote = quote ?? { uploader };
|
|
690
815
|
this.signer = new ArweaveSigner(jwk);
|
|
691
816
|
this.uploader = uploader;
|
|
692
817
|
this.uploadUrl = normalizeUploadUrl(uploader, uploadPath);
|
|
693
818
|
}
|
|
694
819
|
async uploadFile(args) {
|
|
820
|
+
this.seedPreflight ??= preflightHyperbeamBundlerArBalance(this.uploader);
|
|
821
|
+
await this.seedPreflight;
|
|
695
822
|
const data = args.file ? typeof args.file === "string" ? fs.readFileSync(args.file) : args.file : await streamToBuffer(args.fileStreamFactory?.() ?? Readable.from([]));
|
|
696
823
|
const tags = args.dataItemOpts?.tags ?? [];
|
|
697
824
|
const item = createData(data, this.signer, { tags });
|
|
698
825
|
await item.sign(this.signer);
|
|
699
826
|
const raw = Buffer.from(item.getRaw());
|
|
700
827
|
const localId = item.id || toBase64Url(new DataItem(raw).id);
|
|
828
|
+
const size = { payloadBytes: data.length, signedBytes: raw.length };
|
|
829
|
+
let cost;
|
|
701
830
|
if (this.autoFund) {
|
|
702
|
-
await
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
831
|
+
const quote = await quoteHyperbeamUpload({ ...this.quote, signedBytes: raw.length });
|
|
832
|
+
cost = { amount: quote.amount, token: "AO" };
|
|
833
|
+
try {
|
|
834
|
+
await autoFundQuotedHyperbeamLedger({
|
|
835
|
+
...this.autoFund,
|
|
836
|
+
ledgerId: this.autoFund.ledgerId ?? quote.ledgerId,
|
|
837
|
+
minimumBalance: this.autoFund.minimumBalance ?? quote.amount,
|
|
838
|
+
signedBytes: raw.length,
|
|
839
|
+
tokenId: this.autoFund.tokenId ?? quote.tokenId
|
|
840
|
+
});
|
|
841
|
+
} catch (error) {
|
|
842
|
+
const message = cleanAutoFundErrorMessage(
|
|
843
|
+
error instanceof Error ? error.message : String(error)
|
|
844
|
+
);
|
|
845
|
+
throw new Error(
|
|
846
|
+
[
|
|
847
|
+
`HyperBEAM auto-fund failed: ${message}`,
|
|
848
|
+
`Required upload credit: ${formatAoAmount(cost.amount)}`,
|
|
849
|
+
autoFundFailureNote(message),
|
|
850
|
+
await this.paymentHint(false)
|
|
851
|
+
].filter(Boolean).join("\n\n")
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
} else {
|
|
855
|
+
try {
|
|
856
|
+
const quote = await quoteHyperbeamUpload({ ...this.quote, signedBytes: raw.length });
|
|
857
|
+
cost = { amount: quote.amount, token: "AO" };
|
|
858
|
+
} catch {
|
|
859
|
+
cost = void 0;
|
|
860
|
+
}
|
|
706
861
|
}
|
|
707
862
|
const res = await fetch(this.uploadUrl, {
|
|
708
863
|
body: raw,
|
|
@@ -714,7 +869,7 @@ class HyperbeamBundlerClient {
|
|
|
714
869
|
});
|
|
715
870
|
const body = await res.text();
|
|
716
871
|
if (!res.ok) {
|
|
717
|
-
const preview = body
|
|
872
|
+
const preview = responsePreview(body);
|
|
718
873
|
const paymentHint = res.status === 402 ? await this.paymentHint() : void 0;
|
|
719
874
|
throw new Error(
|
|
720
875
|
[
|
|
@@ -723,23 +878,36 @@ class HyperbeamBundlerClient {
|
|
|
723
878
|
].filter(Boolean).join("\n\n")
|
|
724
879
|
);
|
|
725
880
|
}
|
|
726
|
-
return { id: responseId(res.headers, body) || localId };
|
|
881
|
+
return { cost, id: responseId(res.headers, body) || localId, size };
|
|
727
882
|
}
|
|
728
|
-
async paymentHint() {
|
|
883
|
+
async paymentHint(includeAutoFundInstruction = true) {
|
|
729
884
|
try {
|
|
730
885
|
return hyperbeamAoFundingHint(
|
|
731
|
-
await discoverHyperbeamAoBundlerProfile({ nodeUrl: this.uploader })
|
|
886
|
+
await discoverHyperbeamAoBundlerProfile({ nodeUrl: this.uploader }),
|
|
887
|
+
{ includeAutoFundInstruction }
|
|
732
888
|
);
|
|
733
889
|
} catch {
|
|
734
|
-
|
|
890
|
+
try {
|
|
891
|
+
const operator = await fetch(
|
|
892
|
+
`${this.uploader.replace(/\/+$/, "")}/~meta@1.0/info/address`
|
|
893
|
+
).then((res) => res.ok ? res.text() : void 0);
|
|
894
|
+
if (!operator?.trim()) return void 0;
|
|
895
|
+
return [
|
|
896
|
+
"The HyperBEAM node requires AO in its local ledger:",
|
|
897
|
+
`- AO: send funds to ${operator.trim()}. Local ledger: ${HYPERBEAM_DEFAULT_LEDGER_ID} at ${HYPERBEAM_DEFAULT_LEDGER_ROUTE}.`,
|
|
898
|
+
includeAutoFundInstruction ? "Use --hyperbeam-auto-fund to transfer AO and import the credit automatically before upload." : void 0
|
|
899
|
+
].filter(Boolean).join("\n");
|
|
900
|
+
} catch {
|
|
901
|
+
return void 0;
|
|
902
|
+
}
|
|
735
903
|
}
|
|
736
904
|
}
|
|
737
905
|
}
|
|
738
|
-
function hyperbeamAoFundingHint(profile) {
|
|
906
|
+
function hyperbeamAoFundingHint(profile, options = {}) {
|
|
739
907
|
const lines = profile.tokens.map((token) => {
|
|
740
908
|
const depositAddress = token.depositAddress ?? profile.node?.operator;
|
|
741
909
|
if (!depositAddress) return;
|
|
742
|
-
const label = token.ticker ? `${token.ticker} (${token.id})` : token.id;
|
|
910
|
+
const label = token.ticker === "AO" ? "AO" : token.ticker ? `${token.ticker} (${token.id})` : token.id;
|
|
743
911
|
const ledger = token.ledgerId ? profile.ledgers.find((candidate) => candidate.id === token.ledgerId) : void 0;
|
|
744
912
|
const ledgerInfo = ledger ? ` Local ledger: ${ledger.id}${ledger.route ? ` at ${ledger.route}` : ""}.` : "";
|
|
745
913
|
return `- ${label}: send funds to ${depositAddress}.${ledgerInfo}`;
|
|
@@ -748,8 +916,8 @@ function hyperbeamAoFundingHint(profile) {
|
|
|
748
916
|
return [
|
|
749
917
|
"The HyperBEAM node requires AO in its local ledger:",
|
|
750
918
|
...lines,
|
|
751
|
-
"Use --hyperbeam-auto-fund to transfer AO and import the credit automatically before upload."
|
|
752
|
-
].join("\n");
|
|
919
|
+
options.includeAutoFundInstruction === false ? void 0 : "Use --hyperbeam-auto-fund to transfer AO and import the credit automatically before upload."
|
|
920
|
+
].filter(Boolean).join("\n");
|
|
753
921
|
}
|
|
754
922
|
|
|
755
923
|
function getFolderSize(folderPath) {
|
|
@@ -793,6 +961,11 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
793
961
|
uploadClient = new HyperbeamBundlerClient({
|
|
794
962
|
autoFund,
|
|
795
963
|
deployKey,
|
|
964
|
+
quote: {
|
|
965
|
+
ledgerId: config["hyperbeam-ledger-id"],
|
|
966
|
+
tokenId: config["hyperbeam-token-id"],
|
|
967
|
+
uploader: config.uploader
|
|
968
|
+
},
|
|
796
969
|
uploadPath: config["hyperbeam-upload-path"] ?? "/~bundler@1.0/item?codec-device=ans104@1.0",
|
|
797
970
|
uploader: config.uploader
|
|
798
971
|
});
|
|
@@ -869,6 +1042,8 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
869
1042
|
}
|
|
870
1043
|
}
|
|
871
1044
|
let txOrManifestId;
|
|
1045
|
+
let cost;
|
|
1046
|
+
let size;
|
|
872
1047
|
try {
|
|
873
1048
|
if (config["deploy-file"]) {
|
|
874
1049
|
const filePath = expandPath(config["deploy-file"]);
|
|
@@ -880,6 +1055,8 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
880
1055
|
io.error("File upload failed: no transaction ID returned");
|
|
881
1056
|
}
|
|
882
1057
|
txOrManifestId = uploadResult.transactionId;
|
|
1058
|
+
cost = uploadResult.cost;
|
|
1059
|
+
size = uploadResult.size;
|
|
883
1060
|
if (uploadResult.updatedCache && config["dedupe-cache-max-entries"] > 0) {
|
|
884
1061
|
cache = cleanupCache(uploadResult.updatedCache, config["dedupe-cache-max-entries"]);
|
|
885
1062
|
saveCache(cache);
|
|
@@ -905,6 +1082,8 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
905
1082
|
io.error("Folder upload failed: no transaction ID returned");
|
|
906
1083
|
}
|
|
907
1084
|
txOrManifestId = uploadResult.transactionId;
|
|
1085
|
+
cost = uploadResult.cost;
|
|
1086
|
+
size = uploadResult.size;
|
|
908
1087
|
if (uploadResult.updatedCache && config["dedupe-cache-max-entries"] > 0) {
|
|
909
1088
|
cache = cleanupCache(uploadResult.updatedCache, config["dedupe-cache-max-entries"]);
|
|
910
1089
|
saveCache(cache);
|
|
@@ -923,8 +1102,12 @@ async function runUploadWorkflow(deployKey, config, io) {
|
|
|
923
1102
|
const errorMessage = uploadError instanceof Error ? uploadError.message : String(uploadError);
|
|
924
1103
|
io.error(`Upload failed: ${errorMessage}`);
|
|
925
1104
|
}
|
|
926
|
-
return
|
|
1105
|
+
return {
|
|
1106
|
+
cost,
|
|
1107
|
+
size,
|
|
1108
|
+
transactionId: txOrManifestId
|
|
1109
|
+
};
|
|
927
1110
|
}
|
|
928
1111
|
|
|
929
|
-
export { hyperbeamBundlerLink as h, runUploadWorkflow as r };
|
|
930
|
-
//# sourceMappingURL=upload-workflow-
|
|
1112
|
+
export { chalk as c, hyperbeamBundlerLink as h, runUploadWorkflow as r };
|
|
1113
|
+
//# sourceMappingURL=upload-workflow-CcYev67_.js.map
|