@highlightxyz/cli 0.1.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +72 -81
  2. package/lib/index.js +626 -74
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -1,30 +1,55 @@
1
- # @highlightxyz/cli
1
+ # Highlight CLI
2
2
 
3
- Official command-line interface for the Highlight API. The `hl` binary is built
4
- on `@highlightxyz/sdk`, so CLI operations use the same generated public
5
- contract as TypeScript integrations.
3
+ Use `hl` to manage Highlight from a terminal, script, CI job, or agent. It
4
+ covers collections, deployment, sales, gates, media, tokens, contracts, and
5
+ chain configuration.
6
+
7
+ The CLI is built on `@highlightxyz/sdk`. It calls the same API and follows the
8
+ same validation rules as the SDK and REST API.
9
+
10
+ [Read the CLI documentation](https://docs.highlight.xyz/cli/setup/).
6
11
 
7
12
  ## Install
8
13
 
14
+ ```sh
15
+ npm install --global @highlightxyz/cli
16
+ ```
17
+
18
+ Or use Bun:
19
+
9
20
  ```sh
10
21
  bun add --global @highlightxyz/cli
11
22
  ```
12
23
 
13
- Node.js 20 or later is supported.
24
+ The CLI requires Node.js 20 or later.
25
+
26
+ Verify the installation:
27
+
28
+ ```sh
29
+ hl --version
30
+ hl --help
31
+ ```
32
+
33
+ You can run public commands before signing in:
34
+
35
+ ```sh
36
+ hl chains list --testnet
37
+ ```
14
38
 
15
39
  ## Authenticate
16
40
 
17
- Create an API key in Highlight, then log in:
41
+ Create an API key in Highlight, then save and verify it:
18
42
 
19
43
  ```sh
20
44
  hl auth login
45
+ hl auth status
21
46
  ```
22
47
 
23
- For CI and agents, prefer an environment variable:
48
+ For CI and agents, set the key in the process environment:
24
49
 
25
50
  ```sh
26
51
  export HIGHLIGHT_API_KEY="hl_live_..."
27
- hl auth status
52
+ hl auth status --json
28
53
  ```
29
54
 
30
55
  Named profiles and custom API URLs are supported:
@@ -34,11 +59,11 @@ hl auth login --profile staging --api-url http://localhost:8787
34
59
  hl collections list --profile staging
35
60
  ```
36
61
 
37
- Saved keys live in `~/.config/highlight/config.json` with mode `0600`.
38
- `HIGHLIGHT_API_KEY`, `HIGHLIGHT_API_URL`, and `HIGHLIGHT_CONFIG_DIR` override
39
- saved configuration.
62
+ On macOS and Linux, saved keys live in
63
+ `~/.config/highlight/config.json` with mode `0600`. `HIGHLIGHT_API_KEY`,
64
+ `HIGHLIGHT_API_URL`, and `HIGHLIGHT_CONFIG_DIR` override saved configuration.
40
65
 
41
- ## Collections
66
+ ## Core commands
42
67
 
43
68
  ```sh
44
69
  hl collections list
@@ -47,75 +72,23 @@ hl collections create --input collection.json
47
72
  hl collections deploy <highlight-id> --wait
48
73
  hl collections deploy-status <highlight-id>
49
74
  hl collections deploy-cancel <highlight-id>
50
- ```
51
-
52
- Creation input uses the SDK's `collection.create` shape:
53
-
54
- ```json
55
- {
56
- "name": "My Drop",
57
- "description": "Created with the Highlight CLI",
58
- "type": "OpenEdition",
59
- "logoMediaId": "00000000-0000-0000-0000-000000000000",
60
- "image": "https://assets.example/art.png",
61
- "contract": {
62
- "chainId": 84532,
63
- "name": "My Drop",
64
- "symbol": "DROP",
65
- "standard": "ERC721"
66
- }
67
- }
68
- ```
69
-
70
- Use `--input -` to read JSON from stdin.
71
-
72
- ## Sales
73
75
 
74
- ```sh
75
76
  hl sales list <highlight-id>
76
77
  hl sales create <highlight-id> --input sale.json
77
78
  hl sales update <highlight-id> <sale-id> --input sale-update.json
78
79
  hl sales delete <highlight-id> <sale-id>
79
- ```
80
80
 
81
- Sale input uses the SDK's `collection.addSale` body shape:
82
-
83
- ```json
84
- {
85
- "type": "FIXED_PRICE",
86
- "accessMode": "PUBLIC",
87
- "startAt": "2026-01-01T00:00:00.000Z",
88
- "price": "0.01",
89
- "currency": "ETH",
90
- "maxPerTransaction": 5,
91
- "maxPerWallet": 10,
92
- "maxTotal": 100,
93
- "paymentRecipient": "0x0000000000000000000000000000000000000001"
94
- }
95
- ```
96
-
97
- The API enforces the collection functionality matrices. Unsupported
98
- combinations, such as gated ranked auctions, return the canonical domain error.
99
- Live public sale changes can return a transaction payload that requires a
100
- wallet signature.
101
-
102
- ## Gates
103
-
104
- ```sh
105
81
  hl gates list
106
82
  hl gates get <gate-id>
107
83
  hl gates create --input gate.json
108
84
  hl gates update <gate-id> --input gate-update.json
109
85
  hl gates delete <gate-id>
110
- ```
111
-
112
- Gate JSON uses the SDK `gate.create` or `gate.update` body shape and supports
113
- allowlists, token ownership, specific tokens, token attributes, currency
114
- balances, and Farcaster follows.
115
86
 
116
- ## Contracts, tokens, and chains
87
+ hl media upload ./art.png
88
+ hl media upload ./generative-code.zip --kind directory
89
+ hl media get <media-id>
90
+ hl media publish <media-id>
117
91
 
118
- ```sh
119
92
  hl contracts list
120
93
  hl contracts get <contract-id>
121
94
 
@@ -129,30 +102,48 @@ hl chains list --testnet
129
102
  hl chains system-contract 84532 MintManager
130
103
  ```
131
104
 
132
- Token reads and chain configuration are public and do not require credentials.
133
- Contract queries and token reveal redrives require authentication.
134
-
135
- ## Media
105
+ Run group help for every option:
136
106
 
137
107
  ```sh
138
- hl media upload ./art.png
139
- hl media upload ./generative-code.zip --kind directory
140
- hl media get <media-id>
141
- hl media publish <media-id>
108
+ hl collections --help
109
+ hl sales --help
110
+ hl media --help
142
111
  ```
143
112
 
144
- Uploads wait for processing by default. Use `--no-wait` for asynchronous jobs.
113
+ Collection, sale, and gate writes accept JSON with `--input <file>`. Use
114
+ `--input -` to read it from stdin.
115
+
116
+ ## Current boundaries
117
+
118
+ `hl collections create` creates the base draft. Version `0.1.0` does not
119
+ configure edition, series, or generative details, so complete those fields in
120
+ the dashboard, SDK, or REST API before deployment.
121
+
122
+ The CLI does not read or store wallet keys. When deployment or a live sale
123
+ change needs an on-chain transaction, it prints the payload for a wallet to
124
+ sign. It does not broadcast that transaction or submit its hash for tracking.
125
+
126
+ Auction configuration is available through the generic sale commands. Bids,
127
+ claims, standings, settlement, and earnings actions remain SDK or REST
128
+ operations.
145
129
 
146
130
  ## Automation
147
131
 
148
- Pass `--json` anywhere in the command to receive stable machine-readable
149
- output:
132
+ Pass `--json` anywhere in a command for machine-readable output:
150
133
 
151
134
  ```sh
152
135
  hl collections list --json
153
136
  hl media upload ./art.png --json
154
137
  ```
155
138
 
156
- Errors are written to stderr and commands return non-zero exit codes.
139
+ Successful JSON goes to stdout. Errors go to stderr. Commands return:
140
+
141
+ - `0` on success
142
+ - `1` for API, authentication, filesystem, timeout, or processing failures
143
+ - `2` for invalid commands, options, arguments, or JSON input
157
144
 
158
- Run `hl --help`, `hl collections --help`, or `hl media --help` for all options.
145
+ Pin the package version in production automation:
146
+
147
+ ```sh
148
+ npm install --global @highlightxyz/cli@0.1.0
149
+ ```
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // package.json
3
3
  var package_default = {
4
4
  name: "@highlightxyz/cli",
5
- version: "0.1.0",
5
+ version: "0.3.0",
6
6
  description: "Official command-line interface for the Highlight API",
7
7
  keywords: [
8
8
  "cli",
@@ -18,13 +18,16 @@ var package_default = {
18
18
  url: "https://github.com/highlight-xyz/highlight-app.git",
19
19
  directory: "packages/cli"
20
20
  },
21
+ bin: {
22
+ hl: "./lib/index.js"
23
+ },
21
24
  files: [
22
25
  "lib",
23
26
  "README.md"
24
27
  ],
25
28
  type: "module",
26
- bin: {
27
- hl: "./lib/index.js"
29
+ publishConfig: {
30
+ access: "public"
28
31
  },
29
32
  scripts: {
30
33
  build: "bun build ./src/index.ts --outfile ./lib/index.js --target node --format esm",
@@ -45,9 +48,6 @@ var package_default = {
45
48
  },
46
49
  engines: {
47
50
  node: ">=20"
48
- },
49
- publishConfig: {
50
- access: "public"
51
51
  }
52
52
  };
53
53
 
@@ -209,6 +209,15 @@ var SaleAccessMode;
209
209
  SaleAccessMode2["Public"] = "PUBLIC";
210
210
  SaleAccessMode2["Gated"] = "GATED";
211
211
  })(SaleAccessMode ||= {});
212
+ var MediaPurpose;
213
+ ((MediaPurpose2) => {
214
+ MediaPurpose2["CollectionLogo"] = "CollectionLogo";
215
+ MediaPurpose2["EditionImage"] = "EditionImage";
216
+ MediaPurpose2["EditionAnimation"] = "EditionAnimation";
217
+ MediaPurpose2["SeriesAssets"] = "SeriesAssets";
218
+ MediaPurpose2["GenerativeCode"] = "GenerativeCode";
219
+ MediaPurpose2["UserAvatar"] = "UserAvatar";
220
+ })(MediaPurpose ||= {});
212
221
  var GateMatchMode;
213
222
  ((GateMatchMode2) => {
214
223
  GateMatchMode2["All"] = "ALL";
@@ -1150,8 +1159,6 @@ class Collection extends HeyApiClient {
1150
1159
  { in: "body", key: "description" },
1151
1160
  { in: "body", key: "contractId" },
1152
1161
  { in: "body", key: "contract" },
1153
- { in: "body", key: "logoMediaId" },
1154
- { in: "body", key: "image" },
1155
1162
  { in: "body", key: "type" },
1156
1163
  { in: "body", key: "royalty" },
1157
1164
  { in: "body", key: "tokenManager" }
@@ -1223,14 +1230,58 @@ class Collection extends HeyApiClient {
1223
1230
  ...params
1224
1231
  });
1225
1232
  }
1226
- finalizeBaseUri(parameters, options) {
1233
+ archiveBaseUri(parameters, options) {
1227
1234
  const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "highlightId" }] }]);
1228
1235
  return (options?.client ?? this.client).post({
1229
- url: "/collection/{highlightId}/finalize-base-uri",
1236
+ url: "/collection/{highlightId}/archive-base-uri",
1230
1237
  ...options,
1231
1238
  ...params
1232
1239
  });
1233
1240
  }
1241
+ createMediaUploadSession(parameters, options) {
1242
+ const params = buildClientParams([parameters], [
1243
+ {
1244
+ args: [
1245
+ { in: "path", key: "highlightId" },
1246
+ { in: "body", key: "purpose" },
1247
+ { in: "body", key: "fileName" },
1248
+ { in: "body", key: "mimeType" },
1249
+ { in: "body", key: "fileSize" },
1250
+ { in: "body", key: "contentHash" }
1251
+ ]
1252
+ }
1253
+ ]);
1254
+ return (options?.client ?? this.client).post({
1255
+ url: "/collection/{highlightId}/media/upload-session",
1256
+ ...options,
1257
+ ...params,
1258
+ headers: {
1259
+ "Content-Type": "application/json",
1260
+ ...options?.headers,
1261
+ ...params.headers
1262
+ }
1263
+ });
1264
+ }
1265
+ updateMetadata(parameters, options) {
1266
+ const params = buildClientParams([parameters], [
1267
+ {
1268
+ args: [
1269
+ { in: "path", key: "highlightId" },
1270
+ { key: "collectionUpdateMetadataInput", map: "body" }
1271
+ ]
1272
+ }
1273
+ ]);
1274
+ return (options?.client ?? this.client).patch({
1275
+ url: "/collection/{highlightId}/metadata",
1276
+ ...options,
1277
+ ...params,
1278
+ headers: {
1279
+ "Content-Type": "application/json",
1280
+ ...options?.headers,
1281
+ ...params.headers
1282
+ }
1283
+ });
1284
+ }
1234
1285
  updateGenerativeDetails(parameters, options) {
1235
1286
  const params = buildClientParams([parameters], [
1236
1287
  {
@@ -1316,7 +1367,6 @@ class Collection extends HeyApiClient {
1316
1367
  { in: "body", key: "paymentRecipient" },
1317
1368
  { in: "body", key: "accessMode" },
1318
1369
  { in: "body", key: "gateId" },
1319
- { in: "body", key: "gasSponsored" },
1320
1370
  { in: "body", key: "mechanicId" },
1321
1371
  { in: "body", key: "typeConfig" },
1322
1372
  { in: "body", key: "collectorMessage" },
@@ -1553,6 +1603,41 @@ class Collection extends HeyApiClient {
1553
1603
  ...params
1554
1604
  });
1555
1605
  }
1606
+ dutchAuctionStanding(parameters, options) {
1607
+ const params = buildClientParams([parameters], [
1608
+ {
1609
+ args: [
1610
+ { in: "path", key: "highlightId" },
1611
+ { in: "path", key: "saleId" }
1612
+ ]
1613
+ }
1614
+ ]);
1615
+ return (options?.client ?? this.client).get({
1616
+ url: "/collection/{highlightId}/sales/{saleId}/dutch",
1617
+ ...options,
1618
+ ...params
1619
+ });
1620
+ }
1621
+ claimDutchRebate(parameters, options) {
1622
+ const params = buildClientParams([parameters], [
1623
+ {
1624
+ args: [
1625
+ { in: "path", key: "highlightId" },
1626
+ { in: "body", key: "saleId" }
1627
+ ]
1628
+ }
1629
+ ]);
1630
+ return (options?.client ?? this.client).post({
1631
+ url: "/collection/{highlightId}/sales/dutch/rebate",
1632
+ ...options,
1633
+ ...params,
1634
+ headers: {
1635
+ "Content-Type": "application/json",
1636
+ ...options?.headers,
1637
+ ...params.headers
1638
+ }
1639
+ });
1640
+ }
1556
1641
  deploy(parameters, options) {
1557
1642
  const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "highlightId" }] }]);
1558
1643
  return (options?.client ?? this.client).post({
@@ -1656,6 +1741,12 @@ class Config extends HeyApiClient {
1656
1741
  ...options
1657
1742
  });
1658
1743
  }
1744
+ mediaHosts(options) {
1745
+ return (options?.client ?? this.client).get({
1746
+ url: "/config/media-hosts",
1747
+ ...options
1748
+ });
1749
+ }
1659
1750
  systemContract(parameters, options) {
1660
1751
  const params = buildClientParams([parameters], [
1661
1752
  {
@@ -1967,41 +2058,10 @@ class Mechanic extends HeyApiClient {
1967
2058
  }
1968
2059
 
1969
2060
  class Media extends HeyApiClient {
1970
- createUploadSession(parameters, options) {
1971
- const params = buildClientParams([parameters], [
1972
- {
1973
- args: [
1974
- { in: "body", key: "kind" },
1975
- { in: "body", key: "scope" },
1976
- { in: "body", key: "fileName" },
1977
- { in: "body", key: "mimeType" },
1978
- { in: "body", key: "fileSize" }
1979
- ]
1980
- }
1981
- ]);
1982
- return (options?.client ?? this.client).post({
1983
- url: "/media/upload-session",
1984
- ...options,
1985
- ...params,
1986
- headers: {
1987
- "Content-Type": "application/json",
1988
- ...options?.headers,
1989
- ...params.headers
1990
- }
1991
- });
1992
- }
1993
- upload(parameters, options) {
1994
- const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "mediaId" }] }]);
1995
- return (options?.client ?? this.client).put({
1996
- url: "/media/{mediaId}/upload",
1997
- ...options,
1998
- ...params
1999
- });
2000
- }
2001
- process(parameters, options) {
2061
+ finalizeUpload(parameters, options) {
2002
2062
  const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "mediaId" }] }]);
2003
2063
  return (options?.client ?? this.client).post({
2004
- url: "/media/{mediaId}/process",
2064
+ url: "/media/{mediaId}/finalize",
2005
2065
  ...options,
2006
2066
  ...params
2007
2067
  });
@@ -2103,6 +2163,29 @@ class Siwe extends HeyApiClient {
2103
2163
  }
2104
2164
 
2105
2165
  class User extends HeyApiClient {
2166
+ createMediaUploadSession(parameters, options) {
2167
+ const params = buildClientParams([parameters], [
2168
+ {
2169
+ args: [
2170
+ { in: "body", key: "purpose" },
2171
+ { in: "body", key: "fileName" },
2172
+ { in: "body", key: "mimeType" },
2173
+ { in: "body", key: "fileSize" },
2174
+ { in: "body", key: "contentHash" }
2175
+ ]
2176
+ }
2177
+ ]);
2178
+ return (options?.client ?? this.client).post({
2179
+ url: "/user/media/upload-session",
2180
+ ...options,
2181
+ ...params,
2182
+ headers: {
2183
+ "Content-Type": "application/json",
2184
+ ...options?.headers,
2185
+ ...params.headers
2186
+ }
2187
+ });
2188
+ }
2106
2189
  signin(parameters, options) {
2107
2190
  const params = buildClientParams([parameters], [{ args: [{ key: "body", map: "body" }] }]);
2108
2191
  return (options?.client ?? this.client).post({
@@ -2122,6 +2205,62 @@ class User extends HeyApiClient {
2122
2205
  }
2123
2206
  }
2124
2207
 
2208
+ class Profile extends HeyApiClient {
2209
+ get(options) {
2210
+ return (options?.client ?? this.client).get({ url: "/user/profile", ...options });
2211
+ }
2212
+ update(parameters, options) {
2213
+ const params = buildClientParams([parameters], [
2214
+ {
2215
+ args: [
2216
+ { in: "body", key: "displayName" },
2217
+ { in: "body", key: "bio" },
2218
+ { in: "body", key: "website" },
2219
+ { in: "body", key: "avatarMediaId" }
2220
+ ]
2221
+ }
2222
+ ]);
2223
+ return (options?.client ?? this.client).patch({
2224
+ url: "/user/profile",
2225
+ ...options,
2226
+ ...params,
2227
+ headers: {
2228
+ "Content-Type": "application/json",
2229
+ ...options?.headers,
2230
+ ...params.headers
2231
+ }
2232
+ });
2233
+ }
2234
+ public(parameters, options) {
2235
+ const params = buildClientParams([parameters], [{ args: [{ in: "path", key: "walletAddress" }] }]);
2236
+ return (options?.client ?? this.client).get({
2237
+ url: "/user/{walletAddress}/profile",
2238
+ ...options,
2239
+ ...params
2240
+ });
2241
+ }
2242
+ tokens(parameters, options) {
2243
+ const params = buildClientParams([parameters], [
2244
+ {
2245
+ args: [
2246
+ { in: "path", key: "walletAddress" },
2247
+ { in: "query", key: "page" },
2248
+ { in: "query", key: "limit" },
2249
+ { in: "query", key: "sort" },
2250
+ { in: "query", key: "order" },
2251
+ { in: "query", key: "collectionId" },
2252
+ { in: "query", key: "search" }
2253
+ ]
2254
+ }
2255
+ ]);
2256
+ return (options?.client ?? this.client).get({
2257
+ url: "/user/{walletAddress}/tokens",
2258
+ ...options,
2259
+ ...params
2260
+ });
2261
+ }
2262
+ }
2263
+
2125
2264
  class ApiKey extends HeyApiClient {
2126
2265
  list(options) {
2127
2266
  return (options?.client ?? this.client).get({ url: "/user/api-keys", ...options });
@@ -2243,6 +2382,10 @@ class HighlightClient extends HeyApiClient {
2243
2382
  get user() {
2244
2383
  return this._user ??= new User({ client: this.client });
2245
2384
  }
2385
+ _profile;
2386
+ get profile() {
2387
+ return this._profile ??= new Profile({ client: this.client });
2388
+ }
2246
2389
  _apiKey;
2247
2390
  get apiKey() {
2248
2391
  return this._apiKey ??= new ApiKey({ client: this.client });
@@ -2263,7 +2406,406 @@ function createHighlightClient(config) {
2263
2406
  const client2 = createClient(resolvedConfig);
2264
2407
  return new HighlightClient({ client: client2 });
2265
2408
  }
2409
+ // ../../node_modules/@noble/hashes/esm/utils.js
2410
+ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2411
+ function isBytes(a) {
2412
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
2413
+ }
2414
+ function abytes(b, ...lengths) {
2415
+ if (!isBytes(b))
2416
+ throw new Error("Uint8Array expected");
2417
+ if (lengths.length > 0 && !lengths.includes(b.length))
2418
+ throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
2419
+ }
2420
+ function aexists(instance, checkFinished = true) {
2421
+ if (instance.destroyed)
2422
+ throw new Error("Hash instance has been destroyed");
2423
+ if (checkFinished && instance.finished)
2424
+ throw new Error("Hash#digest() has already been called");
2425
+ }
2426
+ function aoutput(out, instance) {
2427
+ abytes(out);
2428
+ const min = instance.outputLen;
2429
+ if (out.length < min) {
2430
+ throw new Error("digestInto() expects output buffer of length at least " + min);
2431
+ }
2432
+ }
2433
+ function clean(...arrays) {
2434
+ for (let i = 0;i < arrays.length; i++) {
2435
+ arrays[i].fill(0);
2436
+ }
2437
+ }
2438
+ function createView(arr) {
2439
+ return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
2440
+ }
2441
+ function rotr(word, shift) {
2442
+ return word << 32 - shift | word >>> shift;
2443
+ }
2444
+ var hasHexBuiltin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function")();
2445
+ var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
2446
+ function bytesToHex(bytes) {
2447
+ abytes(bytes);
2448
+ if (hasHexBuiltin)
2449
+ return bytes.toHex();
2450
+ let hex = "";
2451
+ for (let i = 0;i < bytes.length; i++) {
2452
+ hex += hexes[bytes[i]];
2453
+ }
2454
+ return hex;
2455
+ }
2456
+ function utf8ToBytes(str) {
2457
+ if (typeof str !== "string")
2458
+ throw new Error("string expected");
2459
+ return new Uint8Array(new TextEncoder().encode(str));
2460
+ }
2461
+ function toBytes(data) {
2462
+ if (typeof data === "string")
2463
+ data = utf8ToBytes(data);
2464
+ abytes(data);
2465
+ return data;
2466
+ }
2467
+ class Hash {
2468
+ }
2469
+ function createHasher(hashCons) {
2470
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
2471
+ const tmp = hashCons();
2472
+ hashC.outputLen = tmp.outputLen;
2473
+ hashC.blockLen = tmp.blockLen;
2474
+ hashC.create = () => hashCons();
2475
+ return hashC;
2476
+ }
2477
+
2478
+ // ../../node_modules/@noble/hashes/esm/_md.js
2479
+ function setBigUint64(view, byteOffset, value, isLE) {
2480
+ if (typeof view.setBigUint64 === "function")
2481
+ return view.setBigUint64(byteOffset, value, isLE);
2482
+ const _32n = BigInt(32);
2483
+ const _u32_max = BigInt(4294967295);
2484
+ const wh = Number(value >> _32n & _u32_max);
2485
+ const wl = Number(value & _u32_max);
2486
+ const h = isLE ? 4 : 0;
2487
+ const l = isLE ? 0 : 4;
2488
+ view.setUint32(byteOffset + h, wh, isLE);
2489
+ view.setUint32(byteOffset + l, wl, isLE);
2490
+ }
2491
+ function Chi(a, b, c) {
2492
+ return a & b ^ ~a & c;
2493
+ }
2494
+ function Maj(a, b, c) {
2495
+ return a & b ^ a & c ^ b & c;
2496
+ }
2497
+
2498
+ class HashMD extends Hash {
2499
+ constructor(blockLen, outputLen, padOffset, isLE) {
2500
+ super();
2501
+ this.finished = false;
2502
+ this.length = 0;
2503
+ this.pos = 0;
2504
+ this.destroyed = false;
2505
+ this.blockLen = blockLen;
2506
+ this.outputLen = outputLen;
2507
+ this.padOffset = padOffset;
2508
+ this.isLE = isLE;
2509
+ this.buffer = new Uint8Array(blockLen);
2510
+ this.view = createView(this.buffer);
2511
+ }
2512
+ update(data) {
2513
+ aexists(this);
2514
+ data = toBytes(data);
2515
+ abytes(data);
2516
+ const { view, buffer, blockLen } = this;
2517
+ const len = data.length;
2518
+ for (let pos = 0;pos < len; ) {
2519
+ const take = Math.min(blockLen - this.pos, len - pos);
2520
+ if (take === blockLen) {
2521
+ const dataView = createView(data);
2522
+ for (;blockLen <= len - pos; pos += blockLen)
2523
+ this.process(dataView, pos);
2524
+ continue;
2525
+ }
2526
+ buffer.set(data.subarray(pos, pos + take), this.pos);
2527
+ this.pos += take;
2528
+ pos += take;
2529
+ if (this.pos === blockLen) {
2530
+ this.process(view, 0);
2531
+ this.pos = 0;
2532
+ }
2533
+ }
2534
+ this.length += data.length;
2535
+ this.roundClean();
2536
+ return this;
2537
+ }
2538
+ digestInto(out) {
2539
+ aexists(this);
2540
+ aoutput(out, this);
2541
+ this.finished = true;
2542
+ const { buffer, view, blockLen, isLE } = this;
2543
+ let { pos } = this;
2544
+ buffer[pos++] = 128;
2545
+ clean(this.buffer.subarray(pos));
2546
+ if (this.padOffset > blockLen - pos) {
2547
+ this.process(view, 0);
2548
+ pos = 0;
2549
+ }
2550
+ for (let i = pos;i < blockLen; i++)
2551
+ buffer[i] = 0;
2552
+ setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);
2553
+ this.process(view, 0);
2554
+ const oview = createView(out);
2555
+ const len = this.outputLen;
2556
+ if (len % 4)
2557
+ throw new Error("_sha2: outputLen should be aligned to 32bit");
2558
+ const outLen = len / 4;
2559
+ const state = this.get();
2560
+ if (outLen > state.length)
2561
+ throw new Error("_sha2: outputLen bigger than state");
2562
+ for (let i = 0;i < outLen; i++)
2563
+ oview.setUint32(4 * i, state[i], isLE);
2564
+ }
2565
+ digest() {
2566
+ const { buffer, outputLen } = this;
2567
+ this.digestInto(buffer);
2568
+ const res = buffer.slice(0, outputLen);
2569
+ this.destroy();
2570
+ return res;
2571
+ }
2572
+ _cloneInto(to) {
2573
+ to || (to = new this.constructor);
2574
+ to.set(...this.get());
2575
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
2576
+ to.destroyed = destroyed;
2577
+ to.finished = finished;
2578
+ to.length = length;
2579
+ to.pos = pos;
2580
+ if (length % blockLen)
2581
+ to.buffer.set(buffer);
2582
+ return to;
2583
+ }
2584
+ clone() {
2585
+ return this._cloneInto();
2586
+ }
2587
+ }
2588
+ var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
2589
+ 1779033703,
2590
+ 3144134277,
2591
+ 1013904242,
2592
+ 2773480762,
2593
+ 1359893119,
2594
+ 2600822924,
2595
+ 528734635,
2596
+ 1541459225
2597
+ ]);
2598
+
2599
+ // ../../node_modules/@noble/hashes/esm/sha2.js
2600
+ var SHA256_K = /* @__PURE__ */ Uint32Array.from([
2601
+ 1116352408,
2602
+ 1899447441,
2603
+ 3049323471,
2604
+ 3921009573,
2605
+ 961987163,
2606
+ 1508970993,
2607
+ 2453635748,
2608
+ 2870763221,
2609
+ 3624381080,
2610
+ 310598401,
2611
+ 607225278,
2612
+ 1426881987,
2613
+ 1925078388,
2614
+ 2162078206,
2615
+ 2614888103,
2616
+ 3248222580,
2617
+ 3835390401,
2618
+ 4022224774,
2619
+ 264347078,
2620
+ 604807628,
2621
+ 770255983,
2622
+ 1249150122,
2623
+ 1555081692,
2624
+ 1996064986,
2625
+ 2554220882,
2626
+ 2821834349,
2627
+ 2952996808,
2628
+ 3210313671,
2629
+ 3336571891,
2630
+ 3584528711,
2631
+ 113926993,
2632
+ 338241895,
2633
+ 666307205,
2634
+ 773529912,
2635
+ 1294757372,
2636
+ 1396182291,
2637
+ 1695183700,
2638
+ 1986661051,
2639
+ 2177026350,
2640
+ 2456956037,
2641
+ 2730485921,
2642
+ 2820302411,
2643
+ 3259730800,
2644
+ 3345764771,
2645
+ 3516065817,
2646
+ 3600352804,
2647
+ 4094571909,
2648
+ 275423344,
2649
+ 430227734,
2650
+ 506948616,
2651
+ 659060556,
2652
+ 883997877,
2653
+ 958139571,
2654
+ 1322822218,
2655
+ 1537002063,
2656
+ 1747873779,
2657
+ 1955562222,
2658
+ 2024104815,
2659
+ 2227730452,
2660
+ 2361852424,
2661
+ 2428436474,
2662
+ 2756734187,
2663
+ 3204031479,
2664
+ 3329325298
2665
+ ]);
2666
+ var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
2667
+
2668
+ class SHA256 extends HashMD {
2669
+ constructor(outputLen = 32) {
2670
+ super(64, outputLen, 8, false);
2671
+ this.A = SHA256_IV[0] | 0;
2672
+ this.B = SHA256_IV[1] | 0;
2673
+ this.C = SHA256_IV[2] | 0;
2674
+ this.D = SHA256_IV[3] | 0;
2675
+ this.E = SHA256_IV[4] | 0;
2676
+ this.F = SHA256_IV[5] | 0;
2677
+ this.G = SHA256_IV[6] | 0;
2678
+ this.H = SHA256_IV[7] | 0;
2679
+ }
2680
+ get() {
2681
+ const { A, B, C, D, E, F, G, H } = this;
2682
+ return [A, B, C, D, E, F, G, H];
2683
+ }
2684
+ set(A, B, C, D, E, F, G, H) {
2685
+ this.A = A | 0;
2686
+ this.B = B | 0;
2687
+ this.C = C | 0;
2688
+ this.D = D | 0;
2689
+ this.E = E | 0;
2690
+ this.F = F | 0;
2691
+ this.G = G | 0;
2692
+ this.H = H | 0;
2693
+ }
2694
+ process(view, offset) {
2695
+ for (let i = 0;i < 16; i++, offset += 4)
2696
+ SHA256_W[i] = view.getUint32(offset, false);
2697
+ for (let i = 16;i < 64; i++) {
2698
+ const W15 = SHA256_W[i - 15];
2699
+ const W2 = SHA256_W[i - 2];
2700
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
2701
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
2702
+ SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
2703
+ }
2704
+ let { A, B, C, D, E, F, G, H } = this;
2705
+ for (let i = 0;i < 64; i++) {
2706
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
2707
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
2708
+ const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
2709
+ const T2 = sigma0 + Maj(A, B, C) | 0;
2710
+ H = G;
2711
+ G = F;
2712
+ F = E;
2713
+ E = D + T1 | 0;
2714
+ D = C;
2715
+ C = B;
2716
+ B = A;
2717
+ A = T1 + T2 | 0;
2718
+ }
2719
+ A = A + this.A | 0;
2720
+ B = B + this.B | 0;
2721
+ C = C + this.C | 0;
2722
+ D = D + this.D | 0;
2723
+ E = E + this.E | 0;
2724
+ F = F + this.F | 0;
2725
+ G = G + this.G | 0;
2726
+ H = H + this.H | 0;
2727
+ this.set(A, B, C, D, E, F, G, H);
2728
+ }
2729
+ roundClean() {
2730
+ clean(SHA256_W);
2731
+ }
2732
+ destroy() {
2733
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
2734
+ clean(this.buffer);
2735
+ }
2736
+ }
2737
+ var sha256 = /* @__PURE__ */ createHasher(() => new SHA256);
2738
+
2266
2739
  // ../sdk/src/v1/media.ts
2740
+ var isReadableStream = (source) => ("getReader" in source);
2741
+ var sha256MediaContent = async (source, options) => {
2742
+ const hasher = sha256.create();
2743
+ let bytesHashed = 0;
2744
+ const update = (chunk) => {
2745
+ if (options?.signal?.aborted) {
2746
+ throw new DOMException("Hashing aborted.", "AbortError");
2747
+ }
2748
+ hasher.update(chunk);
2749
+ bytesHashed += chunk.byteLength;
2750
+ options?.onProgress?.(bytesHashed);
2751
+ };
2752
+ if (isReadableStream(source)) {
2753
+ const reader = source.getReader();
2754
+ try {
2755
+ while (true) {
2756
+ const result = await reader.read();
2757
+ if (result.done)
2758
+ break;
2759
+ update(result.value);
2760
+ }
2761
+ } finally {
2762
+ reader.releaseLock();
2763
+ }
2764
+ } else {
2765
+ for await (const chunk of source)
2766
+ update(chunk);
2767
+ }
2768
+ return `sha256:${bytesToHex(hasher.digest())}`;
2769
+ };
2770
+ var SUPPORTED_IMAGE_CONTENT_TYPES = new Set([
2771
+ "image/avif",
2772
+ "image/gif",
2773
+ "image/heic",
2774
+ "image/heif",
2775
+ "image/jpeg",
2776
+ "image/jpg",
2777
+ "image/png",
2778
+ "image/webp"
2779
+ ]);
2780
+ var DEFAULT_MEDIA_DELIVERY_HOSTS = {
2781
+ assets: "https://assets.highlight.xyz",
2782
+ metadata: "https://metadata.highlight.xyz",
2783
+ legacyAssets: "https://highlight-creator-assets.highlight.xyz",
2784
+ arweaveGateway: "https://arweave.net",
2785
+ ipfsGateway: "https://ipfs.io"
2786
+ };
2787
+ var LEGACY_REFERENCE = /^main\//;
2788
+ var mediaUrl = (media, location, hosts = DEFAULT_MEDIA_DELIVERY_HOSTS) => {
2789
+ switch (location.provider) {
2790
+ case "R2" /* R2 */: {
2791
+ if (LEGACY_REFERENCE.test(location.reference)) {
2792
+ return `${hosts.legacyAssets}/${location.reference}`;
2793
+ }
2794
+ if (media.parentId && media.path) {
2795
+ return `${hosts.assets}/${media.parentId}/${media.path}`;
2796
+ }
2797
+ return media.kind === "Directory" /* Directory */ ? `${hosts.assets}/${media.id}/` : `${hosts.assets}/${media.id}`;
2798
+ }
2799
+ case "Arweave" /* Arweave */:
2800
+ return `${hosts.arweaveGateway}/${location.reference}`;
2801
+ case "IPFS" /* Ipfs */:
2802
+ return `${hosts.ipfsGateway}/ipfs/${location.reference.replace(/^ipfs:\/\//, "").replace(/^\//, "")}`;
2803
+ case "External" /* External */:
2804
+ return location.reference;
2805
+ default:
2806
+ return null;
2807
+ }
2808
+ };
2267
2809
  var DEFAULT_PREFERENCE = [
2268
2810
  "Archive" /* Archive */,
2269
2811
  "Origin" /* Origin */,
@@ -2271,7 +2813,7 @@ var DEFAULT_PREFERENCE = [
2271
2813
  ];
2272
2814
  var pickLocation = (media, preference) => {
2273
2815
  for (const role of preference) {
2274
- const location = media.locations.find((l) => l.role === role && l.status === "Succeeded" /* Succeeded */ && l.url !== null);
2816
+ const location = media.locations.find((l) => l.role === role && l.status === "Succeeded" /* Succeeded */);
2275
2817
  if (location)
2276
2818
  return location;
2277
2819
  }
@@ -2281,12 +2823,13 @@ var resolveMediaUrl = (media, options) => {
2281
2823
  if (!media)
2282
2824
  return null;
2283
2825
  const preference = options?.prefer ? Array.isArray(options.prefer) ? options.prefer : [options.prefer] : DEFAULT_PREFERENCE;
2284
- return pickLocation(media, preference)?.url ?? null;
2826
+ const location = pickLocation(media, preference);
2827
+ return location ? mediaUrl(media, location, options?.hosts) : null;
2285
2828
  };
2286
2829
  // src/config.ts
2287
2830
  import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2288
2831
  import path from "node:path";
2289
- var DEFAULT_API_URL = "https://api.highlightv2.xyz";
2832
+ var DEFAULT_API_URL = "https://api.highlight.xyz";
2290
2833
  var CONFIG_VERSION = 1;
2291
2834
  var defaultConfig = () => ({
2292
2835
  currentProfile: "default",
@@ -2693,7 +3236,7 @@ var requiredInputPath = (inputPath) => {
2693
3236
  var isCollectionCreateInput = (value) => {
2694
3237
  if (!isRecord3(value))
2695
3238
  return false;
2696
- if (typeof value.name !== "string" || typeof value.description !== "string" || typeof value.logoMediaId !== "string" || typeof value.image !== "string" || !Object.values(CollectionType).includes(value.type) || !isOptionalString(value.contractId) || !isOptionalString(value.tokenManager)) {
3239
+ if (typeof value.name !== "string" || typeof value.description !== "string" || typeof value.logoMediaId !== "string" || !Object.values(CollectionType).includes(value.type) || !isOptionalString(value.contractId) || !isOptionalString(value.tokenManager)) {
2697
3240
  return false;
2698
3241
  }
2699
3242
  if (value.contract !== undefined) {
@@ -2782,7 +3325,7 @@ var create = async (args, global, runtime) => {
2782
3325
  });
2783
3326
  assertNoExtraPositionals(parsed.positionals, 0);
2784
3327
  const inputPath = requiredInputPath(parsed.values.input);
2785
- const input = await readTypedJsonInput(inputPath, runtime, isCollectionCreateInput, "a collection.create object containing name, description, logoMediaId, image, type, and valid contract details");
3328
+ const input = await readTypedJsonInput(inputPath, runtime, isCollectionCreateInput, "a collection.create object containing name, description, logoMediaId, type, and valid contract details");
2786
3329
  const context = await createClientContext(runtime, global);
2787
3330
  const collection = unwrap(await context.client.collection.create(input));
2788
3331
  if (global.json) {
@@ -3000,8 +3543,10 @@ var remove = async (args, global, runtime) => {
3000
3543
  };
3001
3544
 
3002
3545
  // src/commands/media.ts
3003
- import { readFile as readFile3, stat } from "node:fs/promises";
3546
+ import { createReadStream } from "node:fs";
3547
+ import { stat } from "node:fs/promises";
3004
3548
  import path3 from "node:path";
3549
+ import { Readable } from "node:stream";
3005
3550
  var MIME_TYPES = {
3006
3551
  ".css": "text/css",
3007
3552
  ".gif": "image/gif",
@@ -3047,11 +3592,11 @@ var waitUntilReady = async (mediaId, initial, timeoutSeconds, pollIntervalSecond
3047
3592
  var upload = async (args, global, runtime) => {
3048
3593
  const parsed = parseCommandArgs(args, {
3049
3594
  options: {
3050
- kind: { type: "string" },
3595
+ collection: { type: "string" },
3051
3596
  "mime-type": { type: "string" },
3052
3597
  "no-wait": { type: "boolean" },
3053
3598
  "poll-interval": { type: "string" },
3054
- scope: { type: "string" },
3599
+ purpose: { type: "string" },
3055
3600
  timeout: { type: "string" }
3056
3601
  }
3057
3602
  });
@@ -3062,40 +3607,45 @@ var upload = async (args, global, runtime) => {
3062
3607
  if (!fileStat.isFile()) {
3063
3608
  throw new CliError(`Media input must be a file: ${inputPath}`, 2);
3064
3609
  }
3065
- const kindValue = parsed.values.kind?.toLowerCase() ?? "file";
3066
- const kind = kindValue === "file" ? "File" /* File */ : kindValue === "directory" ? "Directory" /* Directory */ : undefined;
3067
- if (!kind) {
3068
- throw new CliError('--kind must be either "file" or "directory"', 2);
3610
+ const purposes = Object.values(MediaPurpose);
3611
+ const purpose = purposes.find((value) => value === parsed.values.purpose);
3612
+ if (!purpose) {
3613
+ throw new CliError(`--purpose must be one of: ${purposes.join(", ")}`, 2);
3069
3614
  }
3070
- if (kind === "Directory" /* Directory */ && path3.extname(absolutePath).toLowerCase() !== ".zip") {
3071
- throw new CliError("Directory media must be uploaded as a .zip file", 2);
3615
+ const collectionId = parsed.values.collection;
3616
+ const needsCollection = purpose !== "UserAvatar" /* UserAvatar */;
3617
+ if (needsCollection && !collectionId) {
3618
+ throw new CliError(`--collection is required for ${purpose} uploads`, 2);
3072
3619
  }
3073
- const scopeValue = parsed.values.scope?.toLowerCase() ?? "collection";
3074
- const scope = scopeValue === "collection" ? "Collection" /* Collection */ : scopeValue === "user" ? "User" /* User */ : undefined;
3075
- if (!scope) {
3076
- throw new CliError('--scope must be either "collection" or "user"', 2);
3620
+ if (!needsCollection && collectionId) {
3621
+ throw new CliError(`--collection is not valid for ${purpose} uploads`, 2);
3077
3622
  }
3623
+ const contentHash = await sha256MediaContent(createReadStream(absolutePath));
3078
3624
  const context = await createClientContext(runtime, global);
3079
- const session = unwrap(await context.client.media.createUploadSession({
3625
+ const body = {
3080
3626
  fileName: path3.basename(absolutePath),
3081
3627
  fileSize: fileStat.size,
3082
- kind,
3083
3628
  mimeType: parsed.values["mime-type"] ?? mimeTypeFor(absolutePath),
3084
- scope
3085
- }));
3629
+ purpose,
3630
+ contentHash
3631
+ };
3632
+ const session = unwrap(collectionId ? await context.client.collection.createMediaUploadSession({
3633
+ highlightId: collectionId,
3634
+ ...body
3635
+ }) : await context.client.user.createMediaUploadSession(body));
3086
3636
  const headers = new Headers(session.upload.headers);
3087
- if (!headers.has("authorization")) {
3088
- headers.set("authorization", `Bearer ${context.apiKey}`);
3089
- }
3090
3637
  const response = await runtime.fetch(session.upload.url, {
3091
- body: await readFile3(absolutePath),
3638
+ body: Readable.toWeb(createReadStream(absolutePath)),
3639
+ duplex: "half",
3092
3640
  headers,
3093
3641
  method: session.upload.method
3094
3642
  });
3095
3643
  if (!response.ok) {
3096
3644
  throw new ApiError(await parseResponseError(response), response.status);
3097
3645
  }
3098
- let media2 = unwrap(await context.client.media.get({ mediaId: session.media.id }));
3646
+ let media2 = unwrap(await context.client.media.finalizeUpload({
3647
+ mediaId: session.uploadMediaId
3648
+ }));
3099
3649
  if (!parsed.values["no-wait"]) {
3100
3650
  media2 = await waitUntilReady(media2.id, media2, positiveNumber(parsed.values.timeout, "--timeout") ?? 300, positiveNumber(parsed.values["poll-interval"], "--poll-interval") ?? 2, runtime, async () => unwrap(await context.client.media.get({ mediaId: media2.id })));
3101
3651
  }
@@ -3139,7 +3689,7 @@ var isOptionalBoolean = (value) => value === undefined || typeof value === "bool
3139
3689
  var isSaleCreateInput = (value) => {
3140
3690
  if (!isRecord3(value))
3141
3691
  return false;
3142
- return Object.values(SaleType).includes(value.type) && typeof value.startAt === "string" && typeof value.price === "string" && typeof value.currency === "string" && typeof value.maxPerTransaction === "number" && Number.isFinite(value.maxPerTransaction) && typeof value.maxPerWallet === "number" && Number.isFinite(value.maxPerWallet) && typeof value.maxTotal === "number" && Number.isFinite(value.maxTotal) && typeof value.paymentRecipient === "string" && isOptionalNullableString(value.name) && isOptionalNullableString(value.endAt) && (value.accessMode === undefined || Object.values(SaleAccessMode).includes(value.accessMode)) && isOptionalNullableString(value.gateId) && isOptionalBoolean(value.gasSponsored) && isOptionalNullableString(value.mechanicId) && isOptionalNullableString(value.collectorMessage) && isOptionalNullableString(value.customMintFee);
3692
+ return Object.values(SaleType).includes(value.type) && typeof value.startAt === "string" && typeof value.price === "string" && typeof value.currency === "string" && typeof value.maxPerTransaction === "number" && Number.isFinite(value.maxPerTransaction) && typeof value.maxPerWallet === "number" && Number.isFinite(value.maxPerWallet) && typeof value.maxTotal === "number" && Number.isFinite(value.maxTotal) && typeof value.paymentRecipient === "string" && isOptionalNullableString(value.name) && isOptionalNullableString(value.endAt) && (value.accessMode === undefined || Object.values(SaleAccessMode).includes(value.accessMode)) && isOptionalNullableString(value.gateId) && isOptionalNullableString(value.mechanicId) && isOptionalNullableString(value.collectorMessage) && isOptionalNullableString(value.customMintFee);
3143
3693
  };
3144
3694
  var SALE_UPDATE_FIELDS = [
3145
3695
  "name",
@@ -3490,8 +4040,10 @@ var MEDIA_HELP = `Usage:
3490
4040
  hl media publish <media-id>
3491
4041
 
3492
4042
  Upload options:
3493
- --kind <file|directory> Directory inputs must be .zip files
3494
- --scope <collection|user> Media scope (default: collection)
4043
+ --purpose <purpose> Required. CollectionLogo, EditionImage,
4044
+ EditionAnimation, SeriesAssets,
4045
+ GenerativeCode, or UserAvatar
4046
+ --collection <highlight-id> Required for every purpose except UserAvatar
3495
4047
  --mime-type <type> Override inferred MIME type
3496
4048
  --no-wait Return before processing is ready
3497
4049
  --timeout <seconds> Wait timeout (default: 300)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highlightxyz/cli",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Official command-line interface for the Highlight API",
5
5
  "keywords": [
6
6
  "cli",
@@ -16,13 +16,16 @@
16
16
  "url": "https://github.com/highlight-xyz/highlight-app.git",
17
17
  "directory": "packages/cli"
18
18
  },
19
+ "bin": {
20
+ "hl": "./lib/index.js"
21
+ },
19
22
  "files": [
20
23
  "lib",
21
24
  "README.md"
22
25
  ],
23
26
  "type": "module",
24
- "bin": {
25
- "hl": "./lib/index.js"
27
+ "publishConfig": {
28
+ "access": "public"
26
29
  },
27
30
  "scripts": {
28
31
  "build": "bun build ./src/index.ts --outfile ./lib/index.js --target node --format esm",
@@ -43,8 +46,5 @@
43
46
  },
44
47
  "engines": {
45
48
  "node": ">=20"
46
- },
47
- "publishConfig": {
48
- "access": "public"
49
49
  }
50
50
  }