@hunt-labs/bytekit-cli 0.5.0 → 0.7.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.
- package/CHANGELOG.md +203 -3
- package/README.md +74 -6
- package/dist/client.d.ts +126 -6
- package/dist/client.js +248 -30
- package/dist/commands/account.js +2 -1
- package/dist/commands/bulk.js +30 -8
- package/dist/commands/fetch.js +83 -20
- package/dist/commands/monitors.js +46 -8
- package/dist/commands/scrape.js +38 -9
- package/dist/commands/screenshots.js +184 -7
- package/dist/commands/search.js +2 -1
- package/dist/commands/sitemap.js +28 -3
- package/dist/commands/usage.js +6 -3
- package/dist/commands/webhook-deliveries.js +4 -2
- package/dist/lib/download-artifact.d.ts +30 -1
- package/dist/lib/download-artifact.js +90 -5
- package/dist/lib/parse-flags.js +3 -1
- package/dist/lib/url-lines.d.ts +46 -8
- package/dist/lib/url-lines.js +58 -19
- package/dist/output.d.ts +33 -2
- package/dist/output.js +113 -5
- package/package.json +3 -3
package/dist/lib/url-lines.js
CHANGED
|
@@ -1,34 +1,73 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { InvalidArgumentError } from 'commander';
|
|
2
3
|
/**
|
|
3
|
-
* Read a newline-delimited file
|
|
4
|
+
* Read a newline-delimited `--file` and return one {@link BulkLine} per non-blank line.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Line grammar:
|
|
7
|
+
* - a non-JSON line -> the raw line (a plain URL string)
|
|
8
|
+
* - a JSON object whose ONLY key is `url` -> the bare url string
|
|
9
|
+
* - a JSON object carrying other keys too -> the object verbatim (per-item config)
|
|
10
|
+
* - a JSON object with a missing/non-string url -> `InvalidArgumentError` (rejected locally)
|
|
11
|
+
* - a JSON non-object (number, array, string) -> the raw line
|
|
9
12
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
13
|
+
* The "only key is `url`" arm is what keeps the inverse case BYTE-IDENTICAL rather than merely
|
|
14
|
+
* equivalent: a file of url-only lines produces the exact same `{ urls, webhook_url }` body as
|
|
15
|
+
* before per-item config existed (#2596 E2).
|
|
16
|
+
*
|
|
17
|
+
* Rejecting a malformed `url` is a deliberate behavior change (#2596 DoD 8). The previous parser
|
|
18
|
+
* fell through to the raw line, so `{"url": 123}` was sent to the server AS a URL — a confusing
|
|
19
|
+
* 422 for what is a local input mistake. `InvalidArgumentError` surfaces through the same
|
|
20
|
+
* local-usage-error path as every argParser rejection (exit 1), before any request is issued.
|
|
21
|
+
*
|
|
22
|
+
* Throws on a missing file (ENOENT from readFileSync) — callers surface this via their
|
|
23
|
+
* handleError path.
|
|
24
|
+
*
|
|
25
|
+
* The rejection message for a malformed line (#2596 cr-1 / E12) names the 1-based PHYSICAL line
|
|
26
|
+
* NUMBER (i.e. the line's own position in the file, counting blank lines) and the line's own KEY
|
|
27
|
+
* NAMES only — never the raw line or any value. A `--file` line is where per-item `cookies`,
|
|
28
|
+
* `headers`, and auth-bearing `metadata` now live (#2596), so echoing the raw line back would
|
|
29
|
+
* leak session tokens and similar secrets to stderr, CI logs, and shell transcripts. Because the
|
|
30
|
+
* raw line is deliberately never echoed, the line number is the ONLY locator left in the message —
|
|
31
|
+
* it must be computed BEFORE blank lines are filtered out, or it silently mis-locates the bad
|
|
32
|
+
* entry whenever the file contains blank lines above it (#2596 cr-2).
|
|
12
33
|
*/
|
|
13
|
-
export function
|
|
34
|
+
export function parseBulkLines(file) {
|
|
14
35
|
return readFileSync(file, 'utf8')
|
|
15
36
|
.split('\n')
|
|
16
|
-
.map((
|
|
17
|
-
.filter((
|
|
18
|
-
.map((line) => {
|
|
37
|
+
.map((raw, index) => ({ line: raw.trim(), lineNumber: index + 1 }))
|
|
38
|
+
.filter(({ line }) => line.length > 0)
|
|
39
|
+
.map(({ line, lineNumber }) => {
|
|
40
|
+
let parsed;
|
|
19
41
|
try {
|
|
20
|
-
|
|
21
|
-
if (parsed !== null && typeof parsed === 'object' && 'url' in parsed) {
|
|
22
|
-
const url = parsed.url;
|
|
23
|
-
if (typeof url === 'string')
|
|
24
|
-
return url;
|
|
25
|
-
}
|
|
26
|
-
// Parsed as JSON but not a {url:string} object — fall back to the raw line.
|
|
27
|
-
return line;
|
|
42
|
+
parsed = JSON.parse(line);
|
|
28
43
|
}
|
|
29
44
|
catch {
|
|
30
45
|
// Not JSON — treat the whole line as a plain URL string.
|
|
31
46
|
return line;
|
|
32
47
|
}
|
|
48
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
49
|
+
// Parsed as JSON but not an object — fall back to the raw line, as before.
|
|
50
|
+
return line;
|
|
51
|
+
}
|
|
52
|
+
const obj = parsed;
|
|
53
|
+
if (typeof obj['url'] !== 'string' || obj['url'].length === 0) {
|
|
54
|
+
const keys = Object.keys(obj).join(', ') || '(none)';
|
|
55
|
+
throw new InvalidArgumentError(`--file line ${lineNumber} must carry a non-empty string "url" (keys present: ${keys})`);
|
|
56
|
+
}
|
|
57
|
+
// A line whose only key is `url` is indistinguishable from a plain URL line — keep it a
|
|
58
|
+
// bare string so url-only files stay byte-identical on the wire.
|
|
59
|
+
return Object.keys(obj).length === 1 ? obj['url'] : obj;
|
|
33
60
|
});
|
|
34
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Promote every {@link BulkLine} to the `items[]` object shape used by `/v1/bulk` and
|
|
64
|
+
* `/v1/scrape/bulk`.
|
|
65
|
+
*
|
|
66
|
+
* Deliberately stamps NOTHING beyond `url`. `/v1/bulk` owns capture-kind precedence server-side
|
|
67
|
+
* (per-item value, then `defaults`, then the endpoint default) since #2641 made the per-item
|
|
68
|
+
* field optional; a CLI-side default would silently override a caller's `--defaults` and is
|
|
69
|
+
* grep-gated out (#2596 G7).
|
|
70
|
+
*/
|
|
71
|
+
export function toBulkItems(lines) {
|
|
72
|
+
return lines.map((l) => (typeof l === 'string' ? { url: l } : l));
|
|
73
|
+
}
|
package/dist/output.d.ts
CHANGED
|
@@ -72,6 +72,20 @@ export declare function resolveOutputMode(flags: OutputFlags): OutputMode;
|
|
|
72
72
|
*
|
|
73
73
|
* Errors — rather than guessing or stringifying — whenever the request is ambiguous or the body is
|
|
74
74
|
* not text, so `--raw` can never print `[object Object]` or a format the caller did not ask for.
|
|
75
|
+
*
|
|
76
|
+
* Three states have no body but are NOT "the response contains no formats" (#2686 item 2), and each
|
|
77
|
+
* gets its own diagnostic ahead of the generic ones, because the caller's next action differs:
|
|
78
|
+
*
|
|
79
|
+
* - a terminal-FAILED scrape (`ScrapeErrorEnvelope`) → the envelope's own `error.code` + message.
|
|
80
|
+
* This is the scrape twin of the screenshots extractor's error-envelope branch (#2027). It is
|
|
81
|
+
* checked FIRST so a failure envelope that also carries `formats: {}` still reports why it failed
|
|
82
|
+
* rather than a format-selection problem the caller cannot act on.
|
|
83
|
+
* - a completed scrape with an EMPTY `formats: {}` bag and a warning explaining it (the #2675
|
|
84
|
+
* `artifact_unavailable` shape) → the warning's code and message.
|
|
85
|
+
* - an empty bag with no warning → says the bag is empty. It is not "no formats" (the key IS
|
|
86
|
+
* there), and `--format` cannot help, so the multi-format diagnostic would be a false lead.
|
|
87
|
+
*
|
|
88
|
+
* A queued envelope never reaches here — {@link emitResult} diverts it to {@link emitQueuedRaw}.
|
|
75
89
|
*/
|
|
76
90
|
export declare function extractRawBody(response: unknown, requestedFormat?: string): string;
|
|
77
91
|
/**
|
|
@@ -133,6 +147,10 @@ export declare function emitResult(result: unknown, mode: OutputMode, target: Ou
|
|
|
133
147
|
/**
|
|
134
148
|
* Collect the `X-Fetch-*` response headers of a {@link FetchResult} into a Title-Cased object —
|
|
135
149
|
* the documented `--json` payload for a fetch command.
|
|
150
|
+
*
|
|
151
|
+
* Key names are the SPEC names: {@link FETCH_HEADER_CASING} first, {@link titleCaseHeader}
|
|
152
|
+
* otherwise. `fetch-metadata-keys.test.ts` pins the emitted set against the spec's own exhaustive
|
|
153
|
+
* header list. Values are passed through verbatim, and non-`X-Fetch-*` headers stay filtered out.
|
|
136
154
|
*/
|
|
137
155
|
export declare function collectFetchMetadata(headers: Headers): Record<string, string>;
|
|
138
156
|
/**
|
|
@@ -158,7 +176,20 @@ export declare function emitFetchResult(result: unknown, jsonMode: boolean, targ
|
|
|
158
176
|
*/
|
|
159
177
|
export declare function printResult(data: unknown, jsonMode: boolean): void;
|
|
160
178
|
/**
|
|
161
|
-
* Handle a CLI command error: print to stderr and exit 1.
|
|
179
|
+
* Handle a CLI command error: print to stderr and signal exit 1.
|
|
180
|
+
*
|
|
181
|
+
* The signal is a SOFT `process.exitCode`, never a hard `process.exit` (#2686 item 4, restoring the
|
|
182
|
+
* #2544 policy this site contradicted). Node documents `process.stderr` as ASYNCHRONOUS when it
|
|
183
|
+
* points at a pipe: a write that cannot be handed to the kernel in one go is queued in userspace
|
|
184
|
+
* and drained on a later event-loop turn, and `process.exit()` runs before any of those turns and
|
|
185
|
+
* DISCARDS the queue. The one line a script greps was therefore the line most at risk of being
|
|
186
|
+
* thrown away. Setting `process.exitCode` lets the process end naturally once stderr has drained,
|
|
187
|
+
* with the same observable exit code.
|
|
188
|
+
*
|
|
189
|
+
* Returns `void` (not `never`): the hard exit used to provide an implicit stop, so every caller
|
|
190
|
+
* must be the LAST statement of its `catch` block, or `return` immediately after (#2686 E4). Every
|
|
191
|
+
* call site in this package is the final statement of an action's `catch`, so the action returns
|
|
192
|
+
* and nothing further runs.
|
|
162
193
|
*
|
|
163
194
|
* The message always begins `Error: ` (a prefix scripts grep on) followed by the error message and
|
|
164
195
|
* an optional parenthesised suffix built from the two diagnostic axes the ByteKit errors carry:
|
|
@@ -174,5 +205,5 @@ export declare function printResult(data: unknown, jsonMode: boolean): void;
|
|
|
174
205
|
* status is present but no code (a hand-built error), the suffix is `(status: <n>)` with no empty
|
|
175
206
|
* placeholder — never `(, status: <n>)`.
|
|
176
207
|
*/
|
|
177
|
-
export declare function handleError(err: unknown):
|
|
208
|
+
export declare function handleError(err: unknown): void;
|
|
178
209
|
export {};
|
package/dist/output.js
CHANGED
|
@@ -58,13 +58,77 @@ export function resolveOutputMode(flags) {
|
|
|
58
58
|
return { kind: 'raw' };
|
|
59
59
|
return { kind: 'human' };
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The `error` of a spec `ScrapeErrorEnvelope` (`{status:'failed', id, error:{code, message, …}}`),
|
|
63
|
+
* or undefined when the response is not a failure envelope.
|
|
64
|
+
*
|
|
65
|
+
* Keyed on the presence of a well-formed `error` object rather than on `status === 'failed'`: the
|
|
66
|
+
* envelope is what a 200 GET returns for a terminally-failed job, and a producer that ships the
|
|
67
|
+
* error without the discriminator still owes the caller its reason.
|
|
68
|
+
*/
|
|
69
|
+
function scrapeErrorDetail(response) {
|
|
70
|
+
if (typeof response !== 'object' || response === null)
|
|
71
|
+
return undefined;
|
|
72
|
+
const err = response.error;
|
|
73
|
+
if (typeof err !== 'object' || err === null || Array.isArray(err))
|
|
74
|
+
return undefined;
|
|
75
|
+
const { code, message } = err;
|
|
76
|
+
const detail = {};
|
|
77
|
+
if (typeof code === 'string' && code.length > 0)
|
|
78
|
+
detail.code = code;
|
|
79
|
+
if (typeof message === 'string' && message.length > 0)
|
|
80
|
+
detail.message = message;
|
|
81
|
+
return detail;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The warning that best explains an empty `formats` bag — `artifact_unavailable` when present
|
|
85
|
+
* (the #2675 shape: a completed scrape whose persisted artifact could not be rehydrated),
|
|
86
|
+
* otherwise the first warning the envelope carries. Undefined when there are none.
|
|
87
|
+
*/
|
|
88
|
+
function emptyFormatsWarning(response) {
|
|
89
|
+
if (typeof response !== 'object' || response === null)
|
|
90
|
+
return undefined;
|
|
91
|
+
const raw = response.warnings;
|
|
92
|
+
if (!Array.isArray(raw))
|
|
93
|
+
return undefined;
|
|
94
|
+
const parsed = raw.flatMap((w) => {
|
|
95
|
+
if (typeof w !== 'object' || w === null)
|
|
96
|
+
return [];
|
|
97
|
+
const { code, message } = w;
|
|
98
|
+
if (typeof code !== 'string' || code.length === 0)
|
|
99
|
+
return [];
|
|
100
|
+
return typeof message === 'string' && message.length > 0 ? [{ code, message }] : [{ code }];
|
|
101
|
+
});
|
|
102
|
+
return parsed.find((w) => w.code === 'artifact_unavailable') ?? parsed[0];
|
|
103
|
+
}
|
|
61
104
|
/**
|
|
62
105
|
* Narrow an SDK response (typed `unknown` today) down to the single format body to emit raw.
|
|
63
106
|
*
|
|
64
107
|
* Errors — rather than guessing or stringifying — whenever the request is ambiguous or the body is
|
|
65
108
|
* not text, so `--raw` can never print `[object Object]` or a format the caller did not ask for.
|
|
109
|
+
*
|
|
110
|
+
* Three states have no body but are NOT "the response contains no formats" (#2686 item 2), and each
|
|
111
|
+
* gets its own diagnostic ahead of the generic ones, because the caller's next action differs:
|
|
112
|
+
*
|
|
113
|
+
* - a terminal-FAILED scrape (`ScrapeErrorEnvelope`) → the envelope's own `error.code` + message.
|
|
114
|
+
* This is the scrape twin of the screenshots extractor's error-envelope branch (#2027). It is
|
|
115
|
+
* checked FIRST so a failure envelope that also carries `formats: {}` still reports why it failed
|
|
116
|
+
* rather than a format-selection problem the caller cannot act on.
|
|
117
|
+
* - a completed scrape with an EMPTY `formats: {}` bag and a warning explaining it (the #2675
|
|
118
|
+
* `artifact_unavailable` shape) → the warning's code and message.
|
|
119
|
+
* - an empty bag with no warning → says the bag is empty. It is not "no formats" (the key IS
|
|
120
|
+
* there), and `--format` cannot help, so the multi-format diagnostic would be a false lead.
|
|
121
|
+
*
|
|
122
|
+
* A queued envelope never reaches here — {@link emitResult} diverts it to {@link emitQueuedRaw}.
|
|
66
123
|
*/
|
|
67
124
|
export function extractRawBody(response, requestedFormat) {
|
|
125
|
+
const failure = scrapeErrorDetail(response);
|
|
126
|
+
if (failure !== undefined) {
|
|
127
|
+
const reason = failure.message !== undefined && failure.code !== undefined
|
|
128
|
+
? `${failure.message} (${failure.code})`
|
|
129
|
+
: (failure.message ?? failure.code ?? 'unknown error');
|
|
130
|
+
throw new Error(`--raw: scrape failed: ${reason}`);
|
|
131
|
+
}
|
|
68
132
|
const formats = typeof response === 'object' && response !== null
|
|
69
133
|
? response.formats
|
|
70
134
|
: undefined;
|
|
@@ -73,6 +137,13 @@ export function extractRawBody(response, requestedFormat) {
|
|
|
73
137
|
}
|
|
74
138
|
const bag = formats;
|
|
75
139
|
const keys = Object.keys(bag);
|
|
140
|
+
if (keys.length === 0) {
|
|
141
|
+
const warning = emptyFormatsWarning(response);
|
|
142
|
+
throw new Error(warning === undefined
|
|
143
|
+
? '--raw: the response carries an empty formats map — no body to emit'
|
|
144
|
+
: `--raw: the response carries an empty formats map — ${warning.code}` +
|
|
145
|
+
(warning.message !== undefined ? `: ${warning.message}` : ''));
|
|
146
|
+
}
|
|
76
147
|
let key;
|
|
77
148
|
if (requestedFormat !== undefined) {
|
|
78
149
|
key = requestedFormat;
|
|
@@ -197,15 +268,39 @@ function titleCaseHeader(name) {
|
|
|
197
268
|
.map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1).toLowerCase())
|
|
198
269
|
.join('-');
|
|
199
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Spec header names that segment-wise Title-Casing cannot produce (#2686 item 3).
|
|
273
|
+
*
|
|
274
|
+
* `titleCaseHeader` lowercases every non-initial character of a segment, so `x-fetch-id` came out
|
|
275
|
+
* `X-Fetch-Id` and `x-fetch-url` came out `X-Fetch-Url` — while `docs/api/openapi.yaml` declares
|
|
276
|
+
* `X-Fetch-ID` and `X-Fetch-URL` (the gateway's CORS expose list, `lib/cors-config.ts`, mirrors the
|
|
277
|
+
* same spellings). A `jq '."X-Fetch-ID"'` filter written from the API reference therefore got
|
|
278
|
+
* `null` off `bytekit fetch … --json`.
|
|
279
|
+
*
|
|
280
|
+
* The exception lives HERE, in a lookup keyed by the lowercase wire name, and deliberately NOT
|
|
281
|
+
* inside `titleCaseHeader` (#2686 E3). That helper is a generic contract — an acronym rule baked
|
|
282
|
+
* into it would silently re-case names for any future caller, and would have to guess at segments
|
|
283
|
+
* like `Waf` (which the spec really does spell `X-Fetch-Waf-Vendor`, not `WAF`). Every header
|
|
284
|
+
* outside this map keeps the generic Title-Cased spelling it has always had.
|
|
285
|
+
*/
|
|
286
|
+
const FETCH_HEADER_CASING = {
|
|
287
|
+
'x-fetch-id': 'X-Fetch-ID',
|
|
288
|
+
'x-fetch-url': 'X-Fetch-URL',
|
|
289
|
+
};
|
|
200
290
|
/**
|
|
201
291
|
* Collect the `X-Fetch-*` response headers of a {@link FetchResult} into a Title-Cased object —
|
|
202
292
|
* the documented `--json` payload for a fetch command.
|
|
293
|
+
*
|
|
294
|
+
* Key names are the SPEC names: {@link FETCH_HEADER_CASING} first, {@link titleCaseHeader}
|
|
295
|
+
* otherwise. `fetch-metadata-keys.test.ts` pins the emitted set against the spec's own exhaustive
|
|
296
|
+
* header list. Values are passed through verbatim, and non-`X-Fetch-*` headers stay filtered out.
|
|
203
297
|
*/
|
|
204
298
|
export function collectFetchMetadata(headers) {
|
|
205
299
|
const meta = {};
|
|
206
300
|
headers.forEach((value, key) => {
|
|
207
|
-
|
|
208
|
-
|
|
301
|
+
const lower = key.toLowerCase();
|
|
302
|
+
if (lower.startsWith('x-fetch-')) {
|
|
303
|
+
meta[FETCH_HEADER_CASING[lower] ?? titleCaseHeader(key)] = value;
|
|
209
304
|
}
|
|
210
305
|
});
|
|
211
306
|
return meta;
|
|
@@ -293,7 +388,7 @@ const BYTEKIT_ERROR_NAMES = new Set(['ByteKitError', 'ByteKitConnectionError']);
|
|
|
293
388
|
* A non-empty API/connection error `code`, or undefined when absent.
|
|
294
389
|
*
|
|
295
390
|
* Scoped by `err.name` to the ByteKit taxonomy: plenty of OTHER errors also carry a string
|
|
296
|
-
* `.code` — Node fs/system errors (ENOENT from `
|
|
391
|
+
* `.code` — Node fs/system errors (ENOENT from `parseBulkLines`'s readFileSync, EACCES on a `-o`
|
|
297
392
|
* write) — whose message already embeds the code, so suffixing them would render the redundant
|
|
298
393
|
* `Error: ENOENT: … (ENOENT)`. Only `ByteKitError` / `ByteKitConnectionError` codes qualify.
|
|
299
394
|
*/
|
|
@@ -304,7 +399,20 @@ function errorCode(err) {
|
|
|
304
399
|
return typeof raw === 'string' && raw.length > 0 ? raw : undefined;
|
|
305
400
|
}
|
|
306
401
|
/**
|
|
307
|
-
* Handle a CLI command error: print to stderr and exit 1.
|
|
402
|
+
* Handle a CLI command error: print to stderr and signal exit 1.
|
|
403
|
+
*
|
|
404
|
+
* The signal is a SOFT `process.exitCode`, never a hard `process.exit` (#2686 item 4, restoring the
|
|
405
|
+
* #2544 policy this site contradicted). Node documents `process.stderr` as ASYNCHRONOUS when it
|
|
406
|
+
* points at a pipe: a write that cannot be handed to the kernel in one go is queued in userspace
|
|
407
|
+
* and drained on a later event-loop turn, and `process.exit()` runs before any of those turns and
|
|
408
|
+
* DISCARDS the queue. The one line a script greps was therefore the line most at risk of being
|
|
409
|
+
* thrown away. Setting `process.exitCode` lets the process end naturally once stderr has drained,
|
|
410
|
+
* with the same observable exit code.
|
|
411
|
+
*
|
|
412
|
+
* Returns `void` (not `never`): the hard exit used to provide an implicit stop, so every caller
|
|
413
|
+
* must be the LAST statement of its `catch` block, or `return` immediately after (#2686 E4). Every
|
|
414
|
+
* call site in this package is the final statement of an action's `catch`, so the action returns
|
|
415
|
+
* and nothing further runs.
|
|
308
416
|
*
|
|
309
417
|
* The message always begins `Error: ` (a prefix scripts grep on) followed by the error message and
|
|
310
418
|
* an optional parenthesised suffix built from the two diagnostic axes the ByteKit errors carry:
|
|
@@ -337,5 +445,5 @@ export function handleError(err) {
|
|
|
337
445
|
else {
|
|
338
446
|
console.error(`Error: ${String(err)}`);
|
|
339
447
|
}
|
|
340
|
-
process.
|
|
448
|
+
process.exitCode = 1;
|
|
341
449
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hunt-labs/bytekit-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "ByteKit CLI — wraps @hunt-labs/bytekit-sdk as shell subcommands",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"commander": "^12.0.0",
|
|
44
|
-
"@hunt-labs/bytekit-sdk": "^0.
|
|
44
|
+
"@hunt-labs/bytekit-sdk": "^0.6.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
|
-
"build": "tsc",
|
|
47
|
+
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc",
|
|
48
48
|
"typecheck": "tsc --noEmit",
|
|
49
49
|
"test": "vitest run"
|
|
50
50
|
}
|