@nimbus-sh/core 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/README.md +47 -26
- package/dist/_shared/byte-stream.d.ts +5 -4
- package/dist/_shared/byte-stream.d.ts.map +1 -1
- package/dist/_shared/cache-stats.d.ts +23 -1
- package/dist/_shared/cache-stats.d.ts.map +1 -1
- package/dist/_shared/cache-stats.js +13 -1
- package/dist/_shared/exports-resolver.d.ts +1 -1
- package/dist/_shared/exports-resolver.js +1 -1
- package/dist/_shared/real-node-imports.d.ts +7 -0
- package/dist/_shared/real-node-imports.d.ts.map +1 -1
- package/dist/_shared/real-node-imports.js +8 -0
- package/dist/_shared/retry.js +1 -1
- package/dist/_shared/tarball.d.ts +23 -5
- package/dist/_shared/tarball.d.ts.map +1 -1
- package/dist/_shared/tarball.js +38 -5
- package/dist/_shared/vfs-write-ledger.d.ts.map +1 -1
- package/dist/_shared/vfs-write-ledger.js +22 -16
- package/dist/constants.d.ts +5 -11
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +16 -56
- package/dist/runtime/assets-loader.js +1 -1
- package/dist/runtime/comment-strip.d.ts +0 -35
- package/dist/runtime/comment-strip.d.ts.map +1 -1
- package/dist/runtime/comment-strip.js +48 -20
- package/dist/runtime/esbuild-service.d.ts +38 -42
- package/dist/runtime/esbuild-service.d.ts.map +1 -1
- package/dist/runtime/esbuild-service.js +494 -197
- package/dist/runtime/javascript-ast.d.ts.map +1 -1
- package/dist/runtime/javascript-ast.js +45 -7
- package/dist/runtime/opentui-wasm-backend.d.ts.map +1 -1
- package/dist/runtime/opentui-wasm-backend.js +18 -16
- package/dist/runtime/port-registry.d.ts +4 -2
- package/dist/runtime/port-registry.d.ts.map +1 -1
- package/dist/runtime/port-registry.js +60 -8
- package/dist/runtime/virtual-socket-kernel.generated.d.ts +1 -1
- package/dist/runtime/virtual-socket-kernel.generated.js +1 -1
- package/dist/shell/stdin-adapter.d.ts +11 -0
- package/dist/shell/stdin-adapter.d.ts.map +1 -0
- package/dist/shell/stdin-adapter.js +59 -0
- package/dist/shell/unix-commands.d.ts +2 -1
- package/dist/shell/unix-commands.d.ts.map +1 -1
- package/dist/shell/unix-commands.js +1078 -50
- package/dist/substrate/lifo/commands/io/dd.js +1 -1
- package/dist/substrate/lifo/commands/net/curl.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/net/curl.js +270 -54
- package/dist/substrate/lifo/commands/system/npm.d.ts +18 -1
- package/dist/substrate/lifo/commands/system/npm.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/system/npm.js +26 -43
- package/dist/substrate/lifo/commands/text/head.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/text/head.js +2 -1
- package/dist/substrate/lifo/commands/text/sed.d.ts.map +1 -1
- package/dist/substrate/lifo/commands/text/sed.js +499 -117
- package/dist/substrate/lifo/commands/types.d.ts +8 -4
- package/dist/substrate/lifo/commands/types.d.ts.map +1 -1
- package/dist/substrate/lifo/node-compat/zlib.d.ts +37 -18
- package/dist/substrate/lifo/node-compat/zlib.d.ts.map +1 -1
- package/dist/substrate/lifo/node-compat/zlib.js +122 -23
- package/dist/substrate/lifo/shell/interpreter.d.ts +9 -1
- package/dist/substrate/lifo/shell/interpreter.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/interpreter.js +79 -29
- package/dist/substrate/lifo/shell/pipe.d.ts +16 -0
- package/dist/substrate/lifo/shell/pipe.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/pipe.js +62 -18
- package/dist/substrate/lifo/shell/terminal-stdin.d.ts +20 -3
- package/dist/substrate/lifo/shell/terminal-stdin.d.ts.map +1 -1
- package/dist/substrate/lifo/shell/terminal-stdin.js +60 -24
- package/dist/vfs/seed-project.d.ts.map +1 -1
- package/dist/vfs/seed-project.js +1 -1
- package/dist/vfs/sqlite-vfs.d.ts +21 -30
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
- package/dist/vfs/sqlite-vfs.js +113 -29
- package/dist/workspace/nimbus-workspace.d.ts +15 -0
- package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
- package/dist/workspace/nimbus-workspace.js +20 -3
- package/dist/workspace/supervisor-op.d.ts +23 -0
- package/dist/workspace/supervisor-op.d.ts.map +1 -0
- package/dist/workspace/supervisor-op.js +92 -0
- package/package.json +3 -2
- package/src/_shared/byte-stream.ts +5 -4
- package/src/_shared/cache-stats.ts +27 -1
- package/src/_shared/exports-resolver.ts +1 -1
- package/src/_shared/real-node-imports.ts +8 -0
- package/src/_shared/retry.ts +1 -1
- package/src/_shared/tarball.ts +56 -5
- package/src/_shared/vfs-write-ledger.ts +22 -16
- package/src/constants.ts +30 -60
- package/src/runtime/assets-loader.ts +1 -1
- package/src/runtime/comment-strip.ts +42 -23
- package/src/runtime/esbuild-service.ts +566 -217
- package/src/runtime/javascript-ast.ts +40 -9
- package/src/runtime/opentui-wasm-backend.ts +19 -17
- package/src/runtime/port-registry.ts +66 -8
- package/src/runtime/virtual-socket-kernel.generated.ts +1 -1
- package/src/shell/stdin-adapter.ts +58 -0
- package/src/shell/unix-commands.ts +1054 -50
- package/src/substrate/lifo/commands/io/dd.ts +2 -2
- package/src/substrate/lifo/commands/net/curl.ts +310 -58
- package/src/substrate/lifo/commands/system/npm.ts +58 -38
- package/src/substrate/lifo/commands/text/head.ts +2 -1
- package/src/substrate/lifo/commands/text/sed.ts +519 -109
- package/src/substrate/lifo/commands/types.ts +8 -5
- package/src/substrate/lifo/node-compat/zlib.ts +155 -26
- package/src/substrate/lifo/shell/interpreter.ts +74 -27
- package/src/substrate/lifo/shell/pipe.ts +65 -24
- package/src/substrate/lifo/shell/terminal-stdin.ts +66 -30
- package/src/vfs/seed-project.ts +3 -3
- package/src/vfs/sqlite-vfs.ts +128 -64
- package/src/workspace/nimbus-workspace.ts +26 -2
- package/src/workspace/supervisor-op.ts +117 -0
- package/dist/_shared/install-phase.d.ts +0 -47
- package/dist/_shared/install-phase.d.ts.map +0 -1
- package/dist/_shared/install-phase.js +0 -1
- package/dist/_shared/rpc-dispose.d.ts +0 -4
- package/dist/_shared/rpc-dispose.d.ts.map +0 -1
- package/dist/_shared/rpc-dispose.js +0 -30
- package/dist/_shared/w7-frame.d.ts +0 -79
- package/dist/_shared/w7-frame.d.ts.map +0 -1
- package/dist/_shared/w7-frame.js +0 -840
- package/dist/_shared/weighted-credit-pool.d.ts +0 -61
- package/dist/_shared/weighted-credit-pool.d.ts.map +0 -1
- package/dist/_shared/weighted-credit-pool.js +0 -196
- package/dist/observability/diag-counters.d.ts +0 -236
- package/dist/observability/diag-counters.d.ts.map +0 -1
- package/dist/observability/diag-counters.js +0 -195
- package/dist/observability/heap-estimate.d.ts +0 -225
- package/dist/observability/heap-estimate.d.ts.map +0 -1
- package/dist/observability/heap-estimate.js +0 -185
- package/dist/observability/heavy-alloc-coord.d.ts +0 -93
- package/dist/observability/heavy-alloc-coord.d.ts.map +0 -1
- package/dist/observability/heavy-alloc-coord.js +0 -181
- package/dist/observability/oom-classify.d.ts +0 -138
- package/dist/observability/oom-classify.d.ts.map +0 -1
- package/dist/observability/oom-classify.js +0 -268
- package/dist/observability/oom-discriminator.d.ts +0 -176
- package/dist/observability/oom-discriminator.d.ts.map +0 -1
- package/dist/observability/oom-discriminator.js +0 -272
- package/src/_shared/install-phase.ts +0 -56
- package/src/_shared/rpc-dispose.ts +0 -31
- package/src/_shared/w7-frame.ts +0 -1013
- package/src/_shared/weighted-credit-pool.ts +0 -244
- package/src/observability/diag-counters.ts +0 -343
- package/src/observability/heap-estimate.ts +0 -331
- package/src/observability/heavy-alloc-coord.ts +0 -241
- package/src/observability/oom-classify.ts +0 -260
- package/src/observability/oom-discriminator.ts +0 -405
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/net/curl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"curl.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/net/curl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAq9BpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAGD,QAAA,MAAM,OAAO,EAAE,OAA0B,CAAC;AAE1C,eAAe,OAAO,CAAC"}
|
|
@@ -3,15 +3,96 @@ import { isLoopbackHost } from '../../kernel/index.js';
|
|
|
3
3
|
import { waitForSignalOrTimeout } from '../signal.js';
|
|
4
4
|
import { NIMBUS_AI_TOKEN_ENV, requestCarriesSessionAiToken } from '../../../../_shared/ai-egress.js';
|
|
5
5
|
import { NIMBUS_AI_GATEWAY_PORT } from '../../../../constants.js';
|
|
6
|
-
const SHORT_VALUE_OPTIONS = new Set(['X', 'H', 'd', 'o', 'w']);
|
|
6
|
+
const SHORT_VALUE_OPTIONS = new Set(['X', 'H', 'd', 'o', 'w', 'D']);
|
|
7
7
|
const MAX_REDIRECTS = 20;
|
|
8
|
+
/** curl's exit code for local write failures (-o/-D targets). */
|
|
9
|
+
const CURL_WRITE_ERROR_EXIT = 23;
|
|
10
|
+
/**
|
|
11
|
+
* A dump-target failure is a local write problem, not a transport error: it
|
|
12
|
+
* must surface as exit 23 with the precise cause, never as a rejected
|
|
13
|
+
* virtual call or a generic connection-failure exit 7.
|
|
14
|
+
*/
|
|
15
|
+
class CurlHeaderWriteError extends Error {
|
|
16
|
+
constructor(action, target, cause) {
|
|
17
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
18
|
+
super(`${action} dump-header file '${target}': ${detail}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Transfer-scoped sink for -D/--dump-header, opened once per command before
|
|
23
|
+
* the request. '-' streams straight to stdout with no init; a VFS path is
|
|
24
|
+
* truncated into existence up front — an empty file is the truthful record
|
|
25
|
+
* of a response that never arrived — and every received header block is
|
|
26
|
+
* persisted on arrival, before any body drain. With no -D flag at all the
|
|
27
|
+
* sink is inert; an empty-string target is still a target — it resolves to
|
|
28
|
+
* the cwd, the write is attempted up front, and failing it exits 23.
|
|
29
|
+
*/
|
|
30
|
+
class CurlHeaderSink {
|
|
31
|
+
ctx;
|
|
32
|
+
target;
|
|
33
|
+
contents = [];
|
|
34
|
+
constructor(ctx, target) {
|
|
35
|
+
this.ctx = ctx;
|
|
36
|
+
this.target = target;
|
|
37
|
+
}
|
|
38
|
+
/** True when no -D was given: every sink method is a no-op. */
|
|
39
|
+
get inert() {
|
|
40
|
+
return this.target === null;
|
|
41
|
+
}
|
|
42
|
+
/** Truncate/create the file target up front; '-' needs no init, no -D skips. */
|
|
43
|
+
async open() {
|
|
44
|
+
if (this.target === null || this.target === '-')
|
|
45
|
+
return;
|
|
46
|
+
try {
|
|
47
|
+
this.ctx.vfs.writeFile(resolve(this.ctx.cwd, this.target), '');
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new CurlHeaderWriteError('create', this.target, error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Persist one received response's header block, in arrival order. */
|
|
54
|
+
async writeBlock(response) {
|
|
55
|
+
if (this.target === null)
|
|
56
|
+
return;
|
|
57
|
+
const block = curlHeaderBlock(response);
|
|
58
|
+
try {
|
|
59
|
+
if (this.target === '-') {
|
|
60
|
+
this.ctx.stdout.write(block);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
this.contents.push(block);
|
|
64
|
+
this.ctx.vfs.writeFile(resolve(this.ctx.cwd, this.target), this.contents.join(''));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new CurlHeaderWriteError('write', this.target, error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function reportCurlWriteError(ctx, error) {
|
|
72
|
+
ctx.stderr.write(`curl: (${CURL_WRITE_ERROR_EXIT}) Failed ${error.message}\n`);
|
|
73
|
+
return CURL_WRITE_ERROR_EXIT;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Persist one arrived response block; a failing dump target cancels the
|
|
77
|
+
* undrained body before the write error surfaces, so the abandoned
|
|
78
|
+
* transfer leaks no stream on its way to exit 23.
|
|
79
|
+
*/
|
|
80
|
+
async function writeHeaderDump(sink, response) {
|
|
81
|
+
try {
|
|
82
|
+
await sink.writeBlock(response);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
cancelStreamBody(response.body);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
8
89
|
function createCurlImpl(kernel) {
|
|
9
90
|
return async (ctx) => {
|
|
10
91
|
const options = parseCurlArgs(ctx.args);
|
|
11
92
|
let url = options.url;
|
|
12
93
|
if (!url) {
|
|
13
94
|
ctx.stderr.write('curl: no URL specified\n');
|
|
14
|
-
ctx.stderr.write('Usage: curl [-fsSLI#] [-X method] [-H header] [-d data] [-o file] [-w format] url\n');
|
|
95
|
+
ctx.stderr.write('Usage: curl [-fsSLI#] [-X method] [-H header] [-d data] [-o file] [-D file] [-w format] url\n');
|
|
15
96
|
return 1;
|
|
16
97
|
}
|
|
17
98
|
// Ensure URL has protocol
|
|
@@ -25,49 +106,49 @@ function createCurlImpl(kernel) {
|
|
|
25
106
|
ctx.stderr.write(`curl: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
26
107
|
return 26;
|
|
27
108
|
}
|
|
28
|
-
|
|
29
|
-
const virtual = await resolveVirtualCurlResponse(kernel, ctx, options, url);
|
|
30
|
-
if (virtual?.kind === 'response') {
|
|
31
|
-
return handleCurlResponse(ctx, options, virtual.response);
|
|
32
|
-
}
|
|
33
|
-
if (virtual?.kind === 'external') {
|
|
34
|
-
url = virtual.url;
|
|
35
|
-
}
|
|
36
|
-
if (virtual?.kind === 'error') {
|
|
37
|
-
return virtual.exitCode;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
109
|
+
const headers = new CurlHeaderSink(ctx, options.dumpHeaders ?? null);
|
|
40
110
|
try {
|
|
41
111
|
const requestSignal = createRequestSignal(ctx.signal, options.maxTimeSeconds);
|
|
42
|
-
const fetchOptions = {
|
|
43
|
-
method: options.method,
|
|
44
|
-
headers: Object.keys(options.headers).length > 0 ? options.headers : undefined,
|
|
45
|
-
body: options.data,
|
|
46
|
-
redirect: options.followRedirects ? 'follow' : 'manual',
|
|
47
|
-
signal: requestSignal.signal,
|
|
48
|
-
};
|
|
49
112
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
113
|
+
await headers.open();
|
|
114
|
+
if (kernel?.portRegistry) {
|
|
115
|
+
const virtual = await resolveVirtualCurlResponse(kernel, ctx, options, url, headers);
|
|
116
|
+
if (virtual?.kind === 'response') {
|
|
117
|
+
return handleCurlResponse(ctx, options, virtual.response);
|
|
118
|
+
}
|
|
119
|
+
if (virtual?.kind === 'external') {
|
|
120
|
+
url = virtual.url;
|
|
121
|
+
}
|
|
122
|
+
if (virtual?.kind === 'error') {
|
|
123
|
+
return virtual.exitCode;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// -L with a dump target needs every hop's headers, so redirects are
|
|
127
|
+
// followed manually here; without -D the fetch-native follow keeps
|
|
128
|
+
// its proven semantics.
|
|
129
|
+
if (options.followRedirects && !headers.inert) {
|
|
130
|
+
return await followExternalWithDump(ctx, options, url, headers, requestSignal.signal);
|
|
131
|
+
}
|
|
132
|
+
const response = await fetch(url, {
|
|
133
|
+
method: options.method,
|
|
134
|
+
headers: Object.keys(options.headers).length > 0 ? options.headers : undefined,
|
|
135
|
+
body: options.data,
|
|
136
|
+
redirect: options.followRedirects ? 'follow' : 'manual',
|
|
137
|
+
signal: requestSignal.signal,
|
|
63
138
|
});
|
|
139
|
+
// Headers are dumped on arrival — before any arrayBuffer()/body drain.
|
|
140
|
+
await writeHeaderDump(headers, response);
|
|
141
|
+
return await handleCurlResponse(ctx, options, await drainCurlResponse(options, response, response.url || url));
|
|
64
142
|
}
|
|
65
143
|
finally {
|
|
66
144
|
requestSignal.cleanup();
|
|
67
145
|
}
|
|
68
146
|
}
|
|
69
|
-
catch (
|
|
70
|
-
|
|
147
|
+
catch (error) {
|
|
148
|
+
if (error instanceof CurlHeaderWriteError) {
|
|
149
|
+
return reportCurlWriteError(ctx, error);
|
|
150
|
+
}
|
|
151
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
71
152
|
if (msg === CURL_TIMEOUT_ERROR) {
|
|
72
153
|
ctx.stderr.write(`curl: operation timed out after ${options.maxTimeSeconds} seconds\n`);
|
|
73
154
|
}
|
|
@@ -82,6 +163,96 @@ function createCurlImpl(kernel) {
|
|
|
82
163
|
}
|
|
83
164
|
};
|
|
84
165
|
}
|
|
166
|
+
/** Request headers fetch-follow strips when a redirect crosses origins. */
|
|
167
|
+
const CREDENTIAL_HEADERS = ['authorization', 'proxy-authorization', 'cookie', 'cookie2'];
|
|
168
|
+
/** Content headers that only make sense on a request that carries a body. */
|
|
169
|
+
const CONTENT_HEADERS = ['content-type', 'content-length', 'transfer-encoding', 'expect'];
|
|
170
|
+
/**
|
|
171
|
+
* Bounded manual redirect walk for external -L with a dump target: every
|
|
172
|
+
* hop's header block is written on arrival, and the final response goes
|
|
173
|
+
* through normal handling; exceeding the cap reports curl's redirect error.
|
|
174
|
+
* Request mutation mirrors fetch-follow: 301/302 collapse only POST to a
|
|
175
|
+
* bodyless GET, 303 collapses everything but HEAD, 307/308 preserve method
|
|
176
|
+
* and body; content headers go with the body, and credential headers are
|
|
177
|
+
* stripped when a hop crosses origins.
|
|
178
|
+
*/
|
|
179
|
+
async function followExternalWithDump(ctx, options, startUrl, headers, signal) {
|
|
180
|
+
let current = new URL(startUrl);
|
|
181
|
+
let method = options.method;
|
|
182
|
+
let data = options.data;
|
|
183
|
+
let requestHeaders = new Headers(Object.keys(options.headers).length > 0 ? options.headers : {});
|
|
184
|
+
for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects++) {
|
|
185
|
+
const response = await fetch(current, {
|
|
186
|
+
method,
|
|
187
|
+
headers: [...requestHeaders.keys()].length > 0 ? requestHeaders : undefined,
|
|
188
|
+
body: data,
|
|
189
|
+
redirect: 'manual',
|
|
190
|
+
signal,
|
|
191
|
+
});
|
|
192
|
+
// Dump on arrival — before any drain of this hop's body.
|
|
193
|
+
await writeHeaderDump(headers, response);
|
|
194
|
+
const record = headersToRecord(response.headers);
|
|
195
|
+
if (!isRedirectStatus(response.status)) {
|
|
196
|
+
return await handleCurlResponse(ctx, options, await drainCurlResponse(options, response, response.url || current.toString()));
|
|
197
|
+
}
|
|
198
|
+
const location = getHeader(record, 'location');
|
|
199
|
+
if (!location) {
|
|
200
|
+
// Redirect status without a target: nothing to follow, handle as final.
|
|
201
|
+
return await handleCurlResponse(ctx, options, await drainCurlResponse(options, response, response.url || current.toString()));
|
|
202
|
+
}
|
|
203
|
+
if (redirects === MAX_REDIRECTS) {
|
|
204
|
+
cancelStreamBody(response.body);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
let next;
|
|
208
|
+
try {
|
|
209
|
+
next = new URL(location, current);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Unusable Location: this hop is as final as it gets — the body must
|
|
213
|
+
// still be drainable, so it is only released once we decide to follow.
|
|
214
|
+
return await handleCurlResponse(ctx, options, await drainCurlResponse(options, response, current.toString()));
|
|
215
|
+
}
|
|
216
|
+
// Follow decision made: this hop's body is no longer needed.
|
|
217
|
+
cancelStreamBody(response.body);
|
|
218
|
+
if ([301, 302].includes(response.status) && method === 'POST') {
|
|
219
|
+
method = 'GET';
|
|
220
|
+
data = undefined;
|
|
221
|
+
}
|
|
222
|
+
else if (response.status === 303 && method !== 'HEAD') {
|
|
223
|
+
method = 'GET';
|
|
224
|
+
data = undefined;
|
|
225
|
+
}
|
|
226
|
+
if (data === undefined) {
|
|
227
|
+
for (const name of CONTENT_HEADERS)
|
|
228
|
+
requestHeaders.delete(name);
|
|
229
|
+
}
|
|
230
|
+
if (next.origin !== current.origin) {
|
|
231
|
+
for (const name of CREDENTIAL_HEADERS)
|
|
232
|
+
requestHeaders.delete(name);
|
|
233
|
+
}
|
|
234
|
+
current = next;
|
|
235
|
+
}
|
|
236
|
+
ctx.stderr.write(`curl: (47) Maximum (${MAX_REDIRECTS}) redirects followed\n`);
|
|
237
|
+
return 47;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Shape a native Response for response handling. Body streams to stdout as
|
|
241
|
+
* they arrive (SSE/chunked responses flow live); an -o file needs the whole
|
|
242
|
+
* payload for a single VFS write.
|
|
243
|
+
*/
|
|
244
|
+
async function drainCurlResponse(options, response, url) {
|
|
245
|
+
const body = options.outputFile
|
|
246
|
+
? new Uint8Array(await response.arrayBuffer())
|
|
247
|
+
: response.body ?? '';
|
|
248
|
+
return {
|
|
249
|
+
status: response.status,
|
|
250
|
+
statusText: response.statusText,
|
|
251
|
+
headers: headersToRecord(response.headers),
|
|
252
|
+
body,
|
|
253
|
+
url,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
85
256
|
const CURL_TIMEOUT_ERROR = 'curl request timeout';
|
|
86
257
|
function parseCurlArgs(args) {
|
|
87
258
|
const options = {
|
|
@@ -152,6 +323,11 @@ function parseLongOption(options, arg, args, index) {
|
|
|
152
323
|
options.outputFile = value;
|
|
153
324
|
return consumed;
|
|
154
325
|
}
|
|
326
|
+
case '--dump-header': {
|
|
327
|
+
const { value, consumed } = readValue();
|
|
328
|
+
options.dumpHeaders = value;
|
|
329
|
+
return consumed;
|
|
330
|
+
}
|
|
155
331
|
case '--write-out': {
|
|
156
332
|
const { value, consumed } = readValue();
|
|
157
333
|
options.writeOut = value;
|
|
@@ -247,6 +423,9 @@ function applyShortValueOption(options, flag, value) {
|
|
|
247
423
|
case 'w':
|
|
248
424
|
options.writeOut = value;
|
|
249
425
|
break;
|
|
426
|
+
case 'D':
|
|
427
|
+
options.dumpHeaders = value;
|
|
428
|
+
break;
|
|
250
429
|
}
|
|
251
430
|
}
|
|
252
431
|
/**
|
|
@@ -293,10 +472,10 @@ function addHeader(options, header) {
|
|
|
293
472
|
return;
|
|
294
473
|
options.headers[header.slice(0, colonIdx).trim()] = header.slice(colonIdx + 1).trim();
|
|
295
474
|
}
|
|
296
|
-
async function resolveVirtualCurlResponse(kernel, ctx, options, startUrl) {
|
|
475
|
+
async function resolveVirtualCurlResponse(kernel, ctx, options, startUrl, headers) {
|
|
297
476
|
let currentUrl = startUrl;
|
|
298
477
|
for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects++) {
|
|
299
|
-
const response = await fetchVirtualCurlResponse(kernel, ctx, options, currentUrl);
|
|
478
|
+
const response = await fetchVirtualCurlResponse(kernel, ctx, options, currentUrl, headers);
|
|
300
479
|
if (!response) {
|
|
301
480
|
return redirects === 0 ? null : { kind: 'external', url: currentUrl };
|
|
302
481
|
}
|
|
@@ -325,7 +504,7 @@ async function resolveVirtualCurlResponse(kernel, ctx, options, startUrl) {
|
|
|
325
504
|
* Hand one curl request to the supervisor's loopback router and shape the
|
|
326
505
|
* answer as curl sees it. Null when nothing is listening on `port`.
|
|
327
506
|
*/
|
|
328
|
-
async function routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, port) {
|
|
507
|
+
async function routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, port, headers) {
|
|
329
508
|
if (!kernel.routeLoopback)
|
|
330
509
|
return null;
|
|
331
510
|
const hasBody = options.method !== 'GET' && options.method !== 'HEAD' && options.data !== undefined;
|
|
@@ -337,6 +516,8 @@ async function routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, port
|
|
|
337
516
|
}));
|
|
338
517
|
if (!response)
|
|
339
518
|
return null;
|
|
519
|
+
// Dump on arrival — before the optional arrayBuffer() drain below.
|
|
520
|
+
await writeHeaderDump(headers, response);
|
|
340
521
|
return {
|
|
341
522
|
status: response.status,
|
|
342
523
|
statusText: response.statusText,
|
|
@@ -349,7 +530,7 @@ async function routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, port
|
|
|
349
530
|
url: response.url || url,
|
|
350
531
|
};
|
|
351
532
|
}
|
|
352
|
-
async function fetchVirtualCurlResponse(kernel, ctx, options, url) {
|
|
533
|
+
async function fetchVirtualCurlResponse(kernel, ctx, options, url, headers) {
|
|
353
534
|
let requestUrl;
|
|
354
535
|
try {
|
|
355
536
|
requestUrl = new URL(url);
|
|
@@ -372,7 +553,7 @@ async function fetchVirtualCurlResponse(kernel, ctx, options, url) {
|
|
|
372
553
|
if (!requestCarriesSessionAiToken(new Headers(options.headers), ctx.env[NIMBUS_AI_TOKEN_ENV] || '')) {
|
|
373
554
|
return null;
|
|
374
555
|
}
|
|
375
|
-
return routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, NIMBUS_AI_GATEWAY_PORT);
|
|
556
|
+
return routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, NIMBUS_AI_GATEWAY_PORT, headers);
|
|
376
557
|
}
|
|
377
558
|
const handler = kernel.portRegistry.get(port);
|
|
378
559
|
if (handler) {
|
|
@@ -398,6 +579,11 @@ async function fetchVirtualCurlResponse(kernel, ctx, options, url) {
|
|
|
398
579
|
return { exitCode: 7 };
|
|
399
580
|
}
|
|
400
581
|
}
|
|
582
|
+
await headers.writeBlock({
|
|
583
|
+
status: vRes.statusCode,
|
|
584
|
+
statusText: statusText(vRes.statusCode),
|
|
585
|
+
headers: vRes.headers,
|
|
586
|
+
});
|
|
401
587
|
return {
|
|
402
588
|
status: vRes.statusCode,
|
|
403
589
|
statusText: statusText(vRes.statusCode),
|
|
@@ -406,7 +592,7 @@ async function fetchVirtualCurlResponse(kernel, ctx, options, url) {
|
|
|
406
592
|
url,
|
|
407
593
|
};
|
|
408
594
|
}
|
|
409
|
-
const routed = await routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, port);
|
|
595
|
+
const routed = await routeCurlOverLoopback(kernel, ctx, options, requestUrl, url, port, headers);
|
|
410
596
|
if (routed)
|
|
411
597
|
return routed;
|
|
412
598
|
ctx.stderr.write(`curl: (7) Failed to connect to ${requestUrl.hostname} port ${port}\n`);
|
|
@@ -417,7 +603,7 @@ async function handleCurlResponse(ctx, options, response) {
|
|
|
417
603
|
if (options.headOnly) {
|
|
418
604
|
cancelStreamBody(response.body);
|
|
419
605
|
if (!failed) {
|
|
420
|
-
await writeCurlOutput(ctx, options,
|
|
606
|
+
await writeCurlOutput(ctx, options, curlHeaderBlock(response));
|
|
421
607
|
}
|
|
422
608
|
writeCurlFailure(ctx, options, response);
|
|
423
609
|
writeCurlWriteOut(ctx, options, response);
|
|
@@ -443,11 +629,21 @@ function cancelStreamBody(body) {
|
|
|
443
629
|
body.cancel().catch(() => { });
|
|
444
630
|
}
|
|
445
631
|
}
|
|
632
|
+
/**
|
|
633
|
+
* --fail diagnostic: curl's own wording pairs its exit code with the
|
|
634
|
+
* offending HTTP status.
|
|
635
|
+
*/
|
|
446
636
|
function writeCurlFailure(ctx, options, response) {
|
|
447
637
|
if (options.fail && options.showError && response.status >= 400) {
|
|
448
|
-
ctx.stderr.write(`curl: (
|
|
638
|
+
ctx.stderr.write(`curl: (22) The requested URL returned error: ${response.status}\n`);
|
|
449
639
|
}
|
|
450
640
|
}
|
|
641
|
+
function curlExitCode(options, status) {
|
|
642
|
+
return options.fail && status >= 400 ? 22 : 0;
|
|
643
|
+
}
|
|
644
|
+
function isRedirectStatus(status) {
|
|
645
|
+
return status >= 300 && status < 400;
|
|
646
|
+
}
|
|
451
647
|
async function writeCurlOutput(ctx, options, body) {
|
|
452
648
|
if (!options.outputFile) {
|
|
453
649
|
if (typeof body === 'string') {
|
|
@@ -533,18 +729,38 @@ function headersToRecord(headers) {
|
|
|
533
729
|
});
|
|
534
730
|
return record;
|
|
535
731
|
}
|
|
536
|
-
function
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
732
|
+
function curlHeaderEntries(headers) {
|
|
733
|
+
if (!(headers instanceof Headers))
|
|
734
|
+
return Object.entries(headers);
|
|
735
|
+
// TS's Headers lib type predates getSetCookie; the runtime has it.
|
|
736
|
+
const getSetCookie = headers.getSetCookie;
|
|
737
|
+
const pairs = [];
|
|
738
|
+
for (const [name, value] of headers.entries()) {
|
|
739
|
+
if (typeof getSetCookie === 'function' && name.toLowerCase() === 'set-cookie')
|
|
740
|
+
continue;
|
|
741
|
+
pairs.push([name, value]);
|
|
540
742
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
743
|
+
if (typeof getSetCookie === 'function') {
|
|
744
|
+
for (const value of getSetCookie.call(headers))
|
|
745
|
+
pairs.push(['set-cookie', value]);
|
|
746
|
+
}
|
|
747
|
+
return pairs;
|
|
545
748
|
}
|
|
546
|
-
|
|
547
|
-
|
|
749
|
+
/**
|
|
750
|
+
* The received response as an HTTP/1.1 header block: status line plus each
|
|
751
|
+
* header terminated CRLF, closed by the blank CRLF line that ends every
|
|
752
|
+
* header section. fetch exposes no negotiated wire version, so HTTP/1.1 is
|
|
753
|
+
* the explicit compatibility representation — a bare `HTTP/<code>` is not
|
|
754
|
+
* valid status-line syntax. Both -I's stdout display and -D's dump share
|
|
755
|
+
* this one serializer so the two surfaces cannot drift.
|
|
756
|
+
*/
|
|
757
|
+
function curlHeaderBlock(response) {
|
|
758
|
+
const reason = response.statusText;
|
|
759
|
+
const statusLine = reason
|
|
760
|
+
? `HTTP/1.1 ${response.status} ${reason}`
|
|
761
|
+
: `HTTP/1.1 ${response.status}`;
|
|
762
|
+
const lines = [statusLine, ...curlHeaderEntries(response.headers).map(([n, v]) => `${n}: ${v}`)];
|
|
763
|
+
return lines.map((line) => `${line}\r\n`).join('') + '\r\n';
|
|
548
764
|
}
|
|
549
765
|
function getHeader(headers, name) {
|
|
550
766
|
const wanted = name.toLowerCase();
|
|
@@ -17,9 +17,26 @@ interface PackageJson {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
export type ShellExecuteFn = (cmd: string, ctx: CommandContext) => Promise<number>;
|
|
20
|
+
/** A host may supply a facet-based installer instead of in-process extraction. */
|
|
21
|
+
export interface NpmInstallPort {
|
|
22
|
+
install(projectDir: string, options: {
|
|
23
|
+
packages?: string[];
|
|
24
|
+
production?: boolean;
|
|
25
|
+
pid?: number;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
installed: string[];
|
|
28
|
+
failed: string[];
|
|
29
|
+
totalFiles: number;
|
|
30
|
+
elapsed: number;
|
|
31
|
+
cachedHits?: number;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export interface NpmCommandDeps {
|
|
35
|
+
readonly installer?: NpmInstallPort;
|
|
36
|
+
}
|
|
20
37
|
export declare function getBinEntries(pkg: PackageJson): Record<string, string>;
|
|
21
38
|
export declare function registerBinCommand(registry: CommandRegistry, binName: string, scriptPath: string, kernel?: Kernel): void;
|
|
22
|
-
export declare function createNpmCommand(registry: CommandRegistry, shellExecute?: ShellExecuteFn, kernel?: Kernel): Command;
|
|
39
|
+
export declare function createNpmCommand(registry: CommandRegistry, shellExecute?: ShellExecuteFn, kernel?: Kernel, deps?: NpmCommandDeps): Command;
|
|
23
40
|
export declare function createNpxCommand(registry: CommandRegistry, shellExecute?: ShellExecuteFn): Command;
|
|
24
41
|
export declare function npmInstallGlobal(packageName: string, ctx: CommandContext, registry: CommandRegistry, kernel?: Kernel): Promise<number>;
|
|
25
42
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/system/npm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAapD,eAAO,MAAM,WAAW,WAAW,CAAC;AAIpC,UAAU,WAAW;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC;AAED,MAAM,MAAM,cAAc,GAAG,CAC5B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,cAAc,KACf,OAAO,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/system/npm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAapD,eAAO,MAAM,WAAW,WAAW,CAAC;AAIpC,UAAU,WAAW;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC;AAED,MAAM,MAAM,cAAc,GAAG,CAC5B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,cAAc,KACf,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,OAAO,CACN,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAClE,OAAO,CAAC;QACV,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAC;CACpC;AAuLD,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMtE;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAaxH;AAomBD,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,eAAe,EACzB,YAAY,CAAC,EAAE,cAAc,EAC7B,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,cAAc,GACnB,OAAO,CA+CT;AAiCD,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,eAAe,EACzB,YAAY,CAAC,EAAE,cAAc,GAC3B,OAAO,CAyGT;AAED,wBAAsB,gBAAgB,CACrC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,eAAe,EACzB,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CA2BjB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolve, join } from '../../utils/path.js';
|
|
2
|
-
import {
|
|
2
|
+
import { writeTarballStream } from '../../../../_shared/tarball.js';
|
|
3
3
|
import { RegistryPackumentSchema, RegistrySearchResponseSchema, RegistryVersionInfoSchema, } from './registry-schemas.js';
|
|
4
4
|
const GLOBAL_MODULES = '/usr/lib/node_modules';
|
|
5
5
|
const GLOBAL_BIN = '/usr/bin';
|
|
@@ -132,48 +132,14 @@ async function fetchWithRange(registry, name, range, signal) {
|
|
|
132
132
|
}
|
|
133
133
|
return data.versions[matching[0].version];
|
|
134
134
|
}
|
|
135
|
-
async function
|
|
135
|
+
async function fetchAndStreamPackage(tarballUrl, targetDir, vfs, signal) {
|
|
136
136
|
const response = await fetch(tarballUrl, { signal });
|
|
137
137
|
if (!response.ok) {
|
|
138
138
|
throw new Error(`Failed to download tarball: ${response.status}`);
|
|
139
139
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const decompressed = await decompressGzip(compressed);
|
|
144
|
-
const entries = parseTar(decompressed);
|
|
145
|
-
// Ensure target directory
|
|
146
|
-
try {
|
|
147
|
-
vfs.mkdir(targetDir, { recursive: true });
|
|
148
|
-
}
|
|
149
|
-
catch { /* exists */ }
|
|
150
|
-
// Extract, stripping the first path component (npm tarballs use "package/")
|
|
151
|
-
for (const entry of entries) {
|
|
152
|
-
const slashIdx = entry.path.indexOf('/');
|
|
153
|
-
if (slashIdx === -1)
|
|
154
|
-
continue;
|
|
155
|
-
const relativePath = entry.path.slice(slashIdx + 1);
|
|
156
|
-
if (!relativePath)
|
|
157
|
-
continue;
|
|
158
|
-
const fullPath = join(targetDir, relativePath);
|
|
159
|
-
if (entry.type === 'directory') {
|
|
160
|
-
try {
|
|
161
|
-
vfs.mkdir(fullPath, { recursive: true });
|
|
162
|
-
}
|
|
163
|
-
catch { /* exists */ }
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
// Ensure parent exists
|
|
167
|
-
const parent = fullPath.slice(0, fullPath.lastIndexOf('/'));
|
|
168
|
-
if (parent) {
|
|
169
|
-
try {
|
|
170
|
-
vfs.mkdir(parent, { recursive: true });
|
|
171
|
-
}
|
|
172
|
-
catch { /* exists */ }
|
|
173
|
-
}
|
|
174
|
-
vfs.writeFile(fullPath, entry.data);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
140
|
+
if (!response.body)
|
|
141
|
+
throw new Error(`Registry served no body for ${tarballUrl}`);
|
|
142
|
+
return writeTarballStream(response.body, targetDir, vfs);
|
|
177
143
|
}
|
|
178
144
|
function readProjectPackageJson(vfs, cwd) {
|
|
179
145
|
const pkgPath = join(cwd, 'package.json');
|
|
@@ -220,7 +186,10 @@ async function installSinglePackage(name, version, targetBase, vfs, npmRegistry,
|
|
|
220
186
|
}
|
|
221
187
|
stdout.write(` ${name}${version ? '@' + version : ''}...\n`);
|
|
222
188
|
const info = await fetchPackageInfo(npmRegistry, name, version, signal);
|
|
223
|
-
await
|
|
189
|
+
const written = await fetchAndStreamPackage(info.dist.tarball, targetDir, vfs, signal);
|
|
190
|
+
if (written.files === 0 || !vfs.exists(join(targetDir, 'package.json'))) {
|
|
191
|
+
throw new Error(`${name}: extraction wrote ${written.files} files and left no package.json in ${targetDir}`);
|
|
192
|
+
}
|
|
224
193
|
let installed = 1;
|
|
225
194
|
// Global install: link binaries
|
|
226
195
|
if (isGlobal) {
|
|
@@ -285,7 +254,7 @@ async function npmInit(ctx) {
|
|
|
285
254
|
ctx.stdout.write(JSON.stringify(pkg, null, 2) + '\n');
|
|
286
255
|
return 0;
|
|
287
256
|
}
|
|
288
|
-
async function npmInstall(ctx, registry, kernel) {
|
|
257
|
+
async function npmInstall(ctx, registry, kernel, deps) {
|
|
289
258
|
const args = ctx.args.slice(1);
|
|
290
259
|
let isGlobal = false;
|
|
291
260
|
let saveDev = false;
|
|
@@ -319,6 +288,20 @@ async function npmInstall(ctx, registry, kernel) {
|
|
|
319
288
|
}
|
|
320
289
|
catch { /* exists */ }
|
|
321
290
|
}
|
|
291
|
+
if (deps?.installer) {
|
|
292
|
+
const projectDir = isGlobal ? GLOBAL_MODULES : ctx.cwd;
|
|
293
|
+
const result = await deps.installer.install(projectDir, {
|
|
294
|
+
packages: packages.length > 0 ? packages : undefined,
|
|
295
|
+
production: saveDev ? false : undefined,
|
|
296
|
+
pid: ctx.pid,
|
|
297
|
+
});
|
|
298
|
+
for (const failure of result.failed)
|
|
299
|
+
ctx.stderr.write(`npm ERR! ${failure}\n`);
|
|
300
|
+
ctx.stdout.write(`\nadded ${result.installed.length} package${result.installed.length === 1 ? '' : 's'}`
|
|
301
|
+
+ ` (${result.totalFiles} files) in ${(result.elapsed / 1000).toFixed(1)}s\n`);
|
|
302
|
+
registerLocalBins(ctx.vfs, projectDir, registry, kernel);
|
|
303
|
+
return result.failed.length > 0 ? 1 : 0;
|
|
304
|
+
}
|
|
322
305
|
if (packages.length === 0) {
|
|
323
306
|
// Install from package.json
|
|
324
307
|
if (isGlobal) {
|
|
@@ -734,7 +717,7 @@ async function npmSearch(ctx) {
|
|
|
734
717
|
return 0;
|
|
735
718
|
}
|
|
736
719
|
// ─── Factory ───
|
|
737
|
-
export function createNpmCommand(registry, shellExecute, kernel) {
|
|
720
|
+
export function createNpmCommand(registry, shellExecute, kernel, deps) {
|
|
738
721
|
return async (ctx) => {
|
|
739
722
|
const subcommand = ctx.args[0];
|
|
740
723
|
if (!subcommand || subcommand === '--help' || subcommand === '-h') {
|
|
@@ -747,7 +730,7 @@ export function createNpmCommand(registry, shellExecute, kernel) {
|
|
|
747
730
|
case 'install':
|
|
748
731
|
case 'i':
|
|
749
732
|
case 'add':
|
|
750
|
-
return npmInstall(ctx, registry, kernel);
|
|
733
|
+
return npmInstall(ctx, registry, kernel, deps);
|
|
751
734
|
case 'uninstall':
|
|
752
735
|
case 'remove':
|
|
753
736
|
case 'rm':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/text/head.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAM3C,QAAA,MAAM,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/text/head.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAM3C,QAAA,MAAM,OAAO,EAAE,OAyEd,CAAC;AAgBF,eAAe,OAAO,CAAC"}
|
|
@@ -55,7 +55,8 @@ const command = async (ctx) => {
|
|
|
55
55
|
const chunk = ctx.stdin.readBytes ? await ctx.stdin.readBytes(want) : await ctx.stdin.read();
|
|
56
56
|
if (chunk === null)
|
|
57
57
|
break;
|
|
58
|
-
const
|
|
58
|
+
const raw = typeof chunk === 'string' ? encode(chunk) : chunk;
|
|
59
|
+
const encoded = raw.subarray(0, want);
|
|
59
60
|
writer.write(encoded);
|
|
60
61
|
copied += encoded.length;
|
|
61
62
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sed.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/text/sed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAKhE,KAAK,MAAM,GAAG;IACZ,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;CAC7D,CAAC;AAEF,KAAK,QAAQ,GAAG;IACd,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB,CAAC;
|
|
1
|
+
{"version":3,"file":"sed.d.ts","sourceRoot":"","sources":["../../../../../src/substrate/lifo/commands/text/sed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAKhE,KAAK,MAAM,GAAG;IACZ,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;CAC7D,CAAC;AAEF,KAAK,QAAQ,GAAG;IACd,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB,CAAC;AAoaF,wBAAsB,MAAM,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkHtE;AAoJD,QAAA,MAAM,OAAO,EAAE,OAAoC,CAAC;AAEpD,eAAe,OAAO,CAAC"}
|