@telorun/kernel 0.79.0 → 0.81.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/dist/bundle/module-artifact.d.ts +13 -1
- package/dist/bundle/module-artifact.d.ts.map +1 -1
- package/dist/bundle/module-artifact.js +22 -3
- package/dist/bundle/module-artifact.js.map +1 -1
- package/dist/controller-loader.js +1 -1
- package/dist/controller-loader.js.map +1 -1
- package/dist/controller-loaders/napi-loader.d.ts +20 -0
- package/dist/controller-loaders/napi-loader.d.ts.map +1 -1
- package/dist/controller-loaders/napi-loader.js +31 -4
- package/dist/controller-loaders/napi-loader.js.map +1 -1
- package/dist/controller-loaders/npm-install-root.d.ts +56 -0
- package/dist/controller-loaders/npm-install-root.d.ts.map +1 -0
- package/dist/controller-loaders/npm-install-root.js +109 -0
- package/dist/controller-loaders/npm-install-root.js.map +1 -0
- package/dist/controller-loaders/npm-loader.d.ts +3 -0
- package/dist/controller-loaders/npm-loader.d.ts.map +1 -1
- package/dist/controller-loaders/npm-loader.js +79 -5
- package/dist/controller-loaders/npm-loader.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/kernel.d.ts +6 -2
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +24 -13
- package/dist/kernel.js.map +1 -1
- package/dist/manifest-sources/analysis-stamp.d.ts +12 -8
- package/dist/manifest-sources/analysis-stamp.d.ts.map +1 -1
- package/dist/manifest-sources/analysis-stamp.js +50 -23
- package/dist/manifest-sources/analysis-stamp.js.map +1 -1
- package/dist/manifest-sources/local-manifest-cache-source.d.ts +45 -9
- package/dist/manifest-sources/local-manifest-cache-source.d.ts.map +1 -1
- package/dist/manifest-sources/local-manifest-cache-source.js +65 -12
- package/dist/manifest-sources/local-manifest-cache-source.js.map +1 -1
- package/dist/runtime-seam.d.ts.map +1 -1
- package/dist/runtime-seam.js +9 -1
- package/dist/runtime-seam.js.map +1 -1
- package/dist/workspace-marker.d.ts +37 -0
- package/dist/workspace-marker.d.ts.map +1 -0
- package/dist/workspace-marker.js +68 -0
- package/dist/workspace-marker.js.map +1 -0
- package/package.json +4 -3
- package/src/bundle/module-artifact.ts +22 -7
- package/src/controller-loader.ts +1 -1
- package/src/controller-loaders/napi-loader.ts +36 -3
- package/src/controller-loaders/npm-install-root.ts +131 -0
- package/src/controller-loaders/npm-loader.ts +84 -5
- package/src/index.ts +1 -0
- package/src/kernel.ts +24 -14
- package/src/manifest-sources/analysis-stamp.ts +53 -25
- package/src/manifest-sources/local-manifest-cache-source.ts +68 -13
- package/src/runtime-seam.ts +9 -1
- package/src/workspace-marker.ts +68 -0
package/dist/runtime-seam.js
CHANGED
|
@@ -138,7 +138,15 @@ export class KernelRuntimeSeam {
|
|
|
138
138
|
// child down in its own `finally`, so this only has to close the channels.
|
|
139
139
|
const exitCode = (async () => {
|
|
140
140
|
try {
|
|
141
|
-
|
|
141
|
+
// The child runs a manifest in the SAME workspace as its parent, so it
|
|
142
|
+
// shares the root the parent already resolved instead of deriving one
|
|
143
|
+
// beside the child manifest. Deriving put a `.telo` in every directory
|
|
144
|
+
// holding a test manifest and rebuilt every controller bundle once per
|
|
145
|
+
// test, since `Test.Suite` runs each test through here.
|
|
146
|
+
//
|
|
147
|
+
// `undefined` means "resolve one yourself" and `null` means "no cache" —
|
|
148
|
+
// a parent with no local anchor must yield the first, not the second.
|
|
149
|
+
await child.load(source, { cacheDir: this.kernel.getCacheRoot() ?? undefined });
|
|
142
150
|
await child.start();
|
|
143
151
|
return child.exitCode;
|
|
144
152
|
}
|
package/dist/runtime-seam.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-seam.js","sourceRoot":"","sources":["../src/runtime-seam.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"runtime-seam.js","sourceRoot":"","sources":["../src/runtime-seam.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding `telo-workspace.yaml` on disk.
|
|
3
|
+
*
|
|
4
|
+
* Its LOCATION is a general anchor — the directory workspace-relative paths are
|
|
5
|
+
* measured from, the outer bound on how far a walk-up may look, and the root the
|
|
6
|
+
* `.telo` cache is anchored at — while its `modules:` list is release scope. So
|
|
7
|
+
* the finder is deliberately separate from `release/`: `telo run` bounds its
|
|
8
|
+
* `.env` collection with the marker and `resolveCacheRoot` anchors on it, and
|
|
9
|
+
* neither may reach into the release namespace to do so, where `loadWorkspace`
|
|
10
|
+
* (which parses `modules:` and throws when the file is absent) sits one import
|
|
11
|
+
* away.
|
|
12
|
+
*
|
|
13
|
+
* It lives in the KERNEL rather than the CLI because the cache root is resolved
|
|
14
|
+
* here, on a path a CLI-less embedder also takes (a child kernel started through
|
|
15
|
+
* the runtime seam, a test harness). The CLI re-exports it; nothing is duplicated.
|
|
16
|
+
*
|
|
17
|
+
* Parsing the file is the analyzer's (`release/workspace-config.ts`); finding it
|
|
18
|
+
* is the Node half, and this is all of it.
|
|
19
|
+
*/
|
|
20
|
+
import { WORKSPACE_FILENAME } from "@telorun/analyzer";
|
|
21
|
+
export { WORKSPACE_FILENAME };
|
|
22
|
+
/**
|
|
23
|
+
* Walk up from `from` looking for the marker. Returns the directory holding it,
|
|
24
|
+
* or `undefined` — the file is optional. `telo release` requires one and reports
|
|
25
|
+
* its absence; `telo run` and `resolveCacheRoot` read `undefined` as "no parent
|
|
26
|
+
* lookup", falling back to what they did before the marker existed.
|
|
27
|
+
*
|
|
28
|
+
* `from` is resolved through symlinks first: a manifest reached through a linked
|
|
29
|
+
* directory would otherwise walk the LINK's parents, miss a marker that is right
|
|
30
|
+
* there in the real tree, and silently fall back to a narrower answer.
|
|
31
|
+
*/
|
|
32
|
+
export declare function findWorkspaceRoot(from: string): string | undefined;
|
|
33
|
+
/** `fs.realpathSync`, falling back to the input when the path does not exist —
|
|
34
|
+
* a manifest path that is wrong is the caller's error to report, not this
|
|
35
|
+
* helper's to convert into a different one. */
|
|
36
|
+
export declare function realPath(target: string): string;
|
|
37
|
+
//# sourceMappingURL=workspace-marker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-marker.d.ts","sourceRoot":"","sources":["../src/workspace-marker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQlE;AAcD;;gDAEgD;AAChD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAM/C"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding `telo-workspace.yaml` on disk.
|
|
3
|
+
*
|
|
4
|
+
* Its LOCATION is a general anchor — the directory workspace-relative paths are
|
|
5
|
+
* measured from, the outer bound on how far a walk-up may look, and the root the
|
|
6
|
+
* `.telo` cache is anchored at — while its `modules:` list is release scope. So
|
|
7
|
+
* the finder is deliberately separate from `release/`: `telo run` bounds its
|
|
8
|
+
* `.env` collection with the marker and `resolveCacheRoot` anchors on it, and
|
|
9
|
+
* neither may reach into the release namespace to do so, where `loadWorkspace`
|
|
10
|
+
* (which parses `modules:` and throws when the file is absent) sits one import
|
|
11
|
+
* away.
|
|
12
|
+
*
|
|
13
|
+
* It lives in the KERNEL rather than the CLI because the cache root is resolved
|
|
14
|
+
* here, on a path a CLI-less embedder also takes (a child kernel started through
|
|
15
|
+
* the runtime seam, a test harness). The CLI re-exports it; nothing is duplicated.
|
|
16
|
+
*
|
|
17
|
+
* Parsing the file is the analyzer's (`release/workspace-config.ts`); finding it
|
|
18
|
+
* is the Node half, and this is all of it.
|
|
19
|
+
*/
|
|
20
|
+
import { WORKSPACE_FILENAME } from "@telorun/analyzer";
|
|
21
|
+
import * as fs from "node:fs";
|
|
22
|
+
import * as path from "node:path";
|
|
23
|
+
export { WORKSPACE_FILENAME };
|
|
24
|
+
/**
|
|
25
|
+
* Walk up from `from` looking for the marker. Returns the directory holding it,
|
|
26
|
+
* or `undefined` — the file is optional. `telo release` requires one and reports
|
|
27
|
+
* its absence; `telo run` and `resolveCacheRoot` read `undefined` as "no parent
|
|
28
|
+
* lookup", falling back to what they did before the marker existed.
|
|
29
|
+
*
|
|
30
|
+
* `from` is resolved through symlinks first: a manifest reached through a linked
|
|
31
|
+
* directory would otherwise walk the LINK's parents, miss a marker that is right
|
|
32
|
+
* there in the real tree, and silently fall back to a narrower answer.
|
|
33
|
+
*/
|
|
34
|
+
export function findWorkspaceRoot(from) {
|
|
35
|
+
let dir = realPath(path.resolve(from));
|
|
36
|
+
for (;;) {
|
|
37
|
+
if (isFile(path.join(dir, WORKSPACE_FILENAME)))
|
|
38
|
+
return dir;
|
|
39
|
+
const parent = path.dirname(dir);
|
|
40
|
+
if (parent === dir)
|
|
41
|
+
return undefined;
|
|
42
|
+
dir = parent;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** The marker must be a FILE. `existsSync` would accept a directory of that name
|
|
46
|
+
* and anchor the whole cache on it, and the Rust half tests `is_file()` — a
|
|
47
|
+
* workspace visible to one kernel and not the other is precisely the divergence
|
|
48
|
+
* a shared marker exists to prevent. */
|
|
49
|
+
function isFile(target) {
|
|
50
|
+
try {
|
|
51
|
+
return fs.statSync(target).isFile();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** `fs.realpathSync`, falling back to the input when the path does not exist —
|
|
58
|
+
* a manifest path that is wrong is the caller's error to report, not this
|
|
59
|
+
* helper's to convert into a different one. */
|
|
60
|
+
export function realPath(target) {
|
|
61
|
+
try {
|
|
62
|
+
return fs.realpathSync(target);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return target;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=workspace-marker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-marker.js","sourceRoot":"","sources":["../src/workspace-marker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,SAAS,CAAC;QACR,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QACrC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;yCAGyC;AACzC,SAAS,MAAM,CAAC,MAAc;IAC5B,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;gDAEgD;AAChD,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/kernel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
4
4
|
"description": "Telo Runtime - A lightweight, polyglot execution host.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@marcbachmann/cel-js": "^7.6.1",
|
|
63
63
|
"@sinclair/typebox": "^0.34.48",
|
|
64
|
-
"@telorun/analyzer": "0.
|
|
64
|
+
"@telorun/analyzer": "0.64.0",
|
|
65
65
|
"@telorun/glob": "0.2.0",
|
|
66
66
|
"@telorun/templating": "0.16.0",
|
|
67
67
|
"ajv": "^8.17.1",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@types/tar-stream": "^3.1.3",
|
|
76
76
|
"typescript": "^5.0.0",
|
|
77
77
|
"vitest": "^2.1.8",
|
|
78
|
-
"@telorun/sdk": "0.
|
|
78
|
+
"@telorun/sdk": "0.80.0"
|
|
79
79
|
},
|
|
80
80
|
"optionalDependencies": {
|
|
81
81
|
"esbuild": "^0.25.12"
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
"build": "tsc",
|
|
100
100
|
"dev": "tsc --watch",
|
|
101
101
|
"test": "vitest run",
|
|
102
|
+
"test:integration": "TELO_DOCKER_TESTS=1 vitest run --config vitest.integration.config.ts",
|
|
102
103
|
"test:watch": "vitest"
|
|
103
104
|
}
|
|
104
105
|
}
|
|
@@ -414,6 +414,18 @@ async function readMarker(marker: string): Promise<string[]> {
|
|
|
414
414
|
* identically cold and warm. A genuinely local `source` (development, or a module
|
|
415
415
|
* loaded straight off disk) resolves to its own directory.
|
|
416
416
|
*
|
|
417
|
+
* A module's LAYERS extract beside its cached `telo.yaml`, so this has to follow
|
|
418
|
+
* the manifest cache's own fallback to the pre-workspace-anchor root: when the
|
|
419
|
+
* manifest resolved from `<entry-dir>/.telo/manifests/` because the workspace root
|
|
420
|
+
* is cold, its already-materialized controller layers are there too. Deriving the
|
|
421
|
+
* directory from the new root alone left an offline hermetic upgrade resolving
|
|
422
|
+
* `telo.yaml` from disk and then going to the network for every bundled
|
|
423
|
+
* controller — the fallback covering half a module.
|
|
424
|
+
*
|
|
425
|
+
* The legacy root is chosen only when it HOLDS the manifest and the current root
|
|
426
|
+
* does not; a cold cache still places the module under the current root, so a
|
|
427
|
+
* fresh materialization is never diverted to the old location.
|
|
428
|
+
*
|
|
417
429
|
* Returns `null` when neither route yields a directory — a `memory://` module, or
|
|
418
430
|
* a ref this cache has no coordinates for.
|
|
419
431
|
*/
|
|
@@ -423,14 +435,17 @@ export function moduleDirectoryFor(
|
|
|
423
435
|
entryDir: string,
|
|
424
436
|
registryUrl: string | undefined,
|
|
425
437
|
manifestsDir: string | undefined,
|
|
438
|
+
legacyDir?: string | null,
|
|
426
439
|
): string | null {
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
440
|
+
const pinned = splitIntegrity(requestedUrl).base;
|
|
441
|
+
const cacheFile = cachePathForCanonical(pinned, entryDir, registryUrl, manifestsDir);
|
|
442
|
+
if (cacheFile) {
|
|
443
|
+
if (legacyDir && !existsSync(cacheFile)) {
|
|
444
|
+
const legacyFile = cachePathForCanonical(pinned, entryDir, registryUrl, legacyDir);
|
|
445
|
+
if (legacyFile && existsSync(legacyFile)) return path.dirname(legacyFile);
|
|
446
|
+
}
|
|
447
|
+
return path.dirname(cacheFile);
|
|
448
|
+
}
|
|
434
449
|
if (source.startsWith("file://")) return path.dirname(fileURLToPath(source));
|
|
435
450
|
if (path.isAbsolute(source)) return path.dirname(source);
|
|
436
451
|
return null;
|
package/src/controller-loader.ts
CHANGED
|
@@ -167,7 +167,7 @@ export class ControllerLoader {
|
|
|
167
167
|
installRoot: options.installRoot,
|
|
168
168
|
});
|
|
169
169
|
if (options.log) this.npmLoader.setLogger(options.log);
|
|
170
|
-
this.napiLoader = new NapiControllerLoader();
|
|
170
|
+
this.napiLoader = new NapiControllerLoader(options.cacheRoot);
|
|
171
171
|
this.bundleLoader = new BundleControllerLoader(options.cacheRoot, options.log);
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -106,6 +106,34 @@ export interface NapiLoadResult {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
export class NapiControllerLoader {
|
|
109
|
+
/**
|
|
110
|
+
* The `.telo` cache root this kernel resolved, or `undefined` when it resolved
|
|
111
|
+
* none (a memory-rooted entry), in which case cargo's own default is used.
|
|
112
|
+
*
|
|
113
|
+
* The build is anchored here for the same reason the Rust kernel's is: one
|
|
114
|
+
* shared dependency build per workspace instead of one per crate, and — the
|
|
115
|
+
* load-bearing half — a directory KEYED BY SDK BACKEND. This loader builds a
|
|
116
|
+
* controller crate with `telorun-sdk/napi` while `telo-rs` builds the very same
|
|
117
|
+
* crate with `native`, and a plain `cargo build --workspace` builds it with
|
|
118
|
+
* neither. All three sharing one target directory means each alternation
|
|
119
|
+
* rebuilds the crate and its dependency tree; before this they were kept apart
|
|
120
|
+
* only by the accident that this loader took cargo's default.
|
|
121
|
+
*/
|
|
122
|
+
private readonly cacheRoot?: string;
|
|
123
|
+
|
|
124
|
+
constructor(cacheRoot?: string) {
|
|
125
|
+
this.cacheRoot = cacheRoot;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** `CARGO_TARGET_DIR` for this loader's builds, or `undefined` to leave cargo's
|
|
129
|
+
* own resolution alone. Applied to `cargo metadata` as well as `cargo build`,
|
|
130
|
+
* since `target_directory` is where the dylib is then looked for — setting it
|
|
131
|
+
* on only one of the two would build in one place and search in another. */
|
|
132
|
+
private targetDirEnv(): Record<string, string | undefined> {
|
|
133
|
+
const env = hostEnv();
|
|
134
|
+
if (!this.cacheRoot) return env;
|
|
135
|
+
return { ...env, CARGO_TARGET_DIR: path.join(this.cacheRoot, "cargo", "napi", "target") };
|
|
136
|
+
}
|
|
109
137
|
/**
|
|
110
138
|
* Resolve a `pkg:cargo/...` PURL to a controller module instance by building
|
|
111
139
|
* the crate and loading the resulting native addon.
|
|
@@ -234,7 +262,11 @@ export class NapiControllerLoader {
|
|
|
234
262
|
// Read before building: the metadata answers both "where did the dylib
|
|
235
263
|
// land" and "does this crate use the SDK", and the second decides the build
|
|
236
264
|
// flags.
|
|
237
|
-
const { targetDir, libName, usesSdk } = await resolveCrateMetadata(
|
|
265
|
+
const { targetDir, libName, usesSdk } = await resolveCrateMetadata(
|
|
266
|
+
cratePath,
|
|
267
|
+
fallbackName,
|
|
268
|
+
this.targetDirEnv(),
|
|
269
|
+
);
|
|
238
270
|
|
|
239
271
|
try {
|
|
240
272
|
// The backend is selected as a *dependency* feature of the SDK. A crate
|
|
@@ -253,7 +285,7 @@ export class NapiControllerLoader {
|
|
|
253
285
|
await execFileAsync("cargo", ["build", "--release", ...featureArgs], {
|
|
254
286
|
cwd: cratePath,
|
|
255
287
|
maxBuffer: 32 * 1024 * 1024,
|
|
256
|
-
env:
|
|
288
|
+
env: this.targetDirEnv(),
|
|
257
289
|
});
|
|
258
290
|
} catch (err: any) {
|
|
259
291
|
const stderr = err?.stderr ? `\n${err.stderr}` : "";
|
|
@@ -331,6 +363,7 @@ const SDK_CRATE_NAME = "telorun-sdk";
|
|
|
331
363
|
async function resolveCrateMetadata(
|
|
332
364
|
cratePath: string,
|
|
333
365
|
fallbackName: string,
|
|
366
|
+
env: Record<string, string | undefined>,
|
|
334
367
|
): Promise<{ targetDir: string; libName: string; usesSdk: boolean }> {
|
|
335
368
|
const result = await execFileAsync("cargo", [
|
|
336
369
|
"metadata",
|
|
@@ -339,7 +372,7 @@ async function resolveCrateMetadata(
|
|
|
339
372
|
"--manifest-path",
|
|
340
373
|
path.join(cratePath, "Cargo.toml"),
|
|
341
374
|
"--no-deps",
|
|
342
|
-
], { maxBuffer: 32 * 1024 * 1024, env
|
|
375
|
+
], { maxBuffer: 32 * 1024 * 1024, env });
|
|
343
376
|
const metadata = JSON.parse(result.stdout);
|
|
344
377
|
const cratePackage = metadata.packages?.find(
|
|
345
378
|
(p: any) => p.manifest_path === path.join(cratePath, "Cargo.toml"),
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { hostPlatformTarget } from "../bundle/module-artifact.js";
|
|
5
|
+
import { REALM_COLLAPSE_NAMES } from "./realm.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Which npm install root a runner uses, and why that is keyed on the realm
|
|
9
|
+
* dependency's RELATIVE path rather than on the manifest.
|
|
10
|
+
*
|
|
11
|
+
* The root's `package.json` records the kernel-provided realm package as a
|
|
12
|
+
* `file:` dependency pointing at the running CLI's own copy, and npm rewrites
|
|
13
|
+
* that into a path RELATIVE to the root — in `package.json`, in
|
|
14
|
+
* `package-lock.json`, and as the target of the symlink it materializes under
|
|
15
|
+
* `node_modules`. Once the cache is anchored at the workspace, a second runner
|
|
16
|
+
* meets that tree and reads a path that resolves elsewhere or nowhere: an
|
|
17
|
+
* `EMISSINGTARGET` install failure, or a dangling link whose failure surfaces
|
|
18
|
+
* later at the controller's import. The normal shape is one checkout
|
|
19
|
+
* bind-mounted into a container (`/home/me/app` on the host, `/app` inside it)
|
|
20
|
+
* while the developer also runs telo on the host.
|
|
21
|
+
*
|
|
22
|
+
* So the key is exactly that relative path, plus the host platform. It is not a
|
|
23
|
+
* proxy for the problem — it IS the string npm will write into the tree, so two
|
|
24
|
+
* runners share a root precisely when everything npm records there means the
|
|
25
|
+
* same thing on both sides, and never otherwise:
|
|
26
|
+
*
|
|
27
|
+
* - a different CLI installation (host vs container) → different path → apart;
|
|
28
|
+
* - the same CLI, workspace mounted at another DEPTH (`/w` vs `/deep/a/b/c/w`)
|
|
29
|
+
* → a different number of `..` segments → apart;
|
|
30
|
+
* - the same CLI, workspace copied to another directory at the same depth
|
|
31
|
+
* (`WORKDIR /build` … `COPY --from=build /build /srv`) → identical path → one
|
|
32
|
+
* root, so an image finds the tree `telo install` warmed for it;
|
|
33
|
+
* - a different architecture or libc → apart, since a native controller build
|
|
34
|
+
* is not interchangeable.
|
|
35
|
+
*
|
|
36
|
+
* Keying on the ENTRY MANIFEST instead was the first attempt, and it cost more
|
|
37
|
+
* than it bought: it did separate host from container, but it also gave every
|
|
38
|
+
* manifest in a workspace its own tree — 60 of them after one `pnpm run test`
|
|
39
|
+
* here — which is the one-repo-one-cache property the workspace-anchored cache
|
|
40
|
+
* exists for. It also could not tell a `/build` → `/srv` copy from a bind
|
|
41
|
+
* mount, so it needed a content-keyed alias plus a validity check on top:
|
|
42
|
+
* machinery this key does not need, because a root it selects is usable by
|
|
43
|
+
* construction.
|
|
44
|
+
*/
|
|
45
|
+
export async function resolveInstallRoot(
|
|
46
|
+
npmBase: string,
|
|
47
|
+
realmPackageRoot: RealmPackageResolver,
|
|
48
|
+
): Promise<string> {
|
|
49
|
+
return path.join(npmBase, await installRootKey(npmBase, realmPackageRoot));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** How this kernel resolves a realm package to its own copy. Passed in rather
|
|
53
|
+
* than imported: realm resolution belongs to the loader, whose module graph
|
|
54
|
+
* this one sits underneath, and a parameter is what lets a test present a
|
|
55
|
+
* different CLI's resolution. */
|
|
56
|
+
export type RealmPackageResolver = (name: string) => Promise<string | null>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The directory name for one (runner, filesystem view, platform) triple.
|
|
60
|
+
*
|
|
61
|
+
* Every field is written even when it is empty — an unresolvable realm package,
|
|
62
|
+
* an undetermined libc — so an absent value and a value that happens to be the
|
|
63
|
+
* empty string cannot produce one key.
|
|
64
|
+
*/
|
|
65
|
+
async function installRootKey(
|
|
66
|
+
npmBase: string,
|
|
67
|
+
realmPackageRoot: RealmPackageResolver,
|
|
68
|
+
): Promise<string> {
|
|
69
|
+
const host = hostPlatformTarget();
|
|
70
|
+
const fields = [
|
|
71
|
+
...(await realmRelativePaths(npmBase, realmPackageRoot)),
|
|
72
|
+
host.os ?? "",
|
|
73
|
+
host.arch ?? "",
|
|
74
|
+
host.libc ?? "",
|
|
75
|
+
];
|
|
76
|
+
return createHash("sha256").update(fields.join("\0")).digest("hex").slice(0, 32);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Each realm package as npm will record it: relative to the root, in the
|
|
81
|
+
* declared order so the key does not move with iteration order.
|
|
82
|
+
*
|
|
83
|
+
* Measured from the BASE rather than the root, since the root's name is what
|
|
84
|
+
* this feeds — the two differ by one constant segment. A name this kernel
|
|
85
|
+
* cannot resolve is recorded as empty rather than skipped, mirroring the
|
|
86
|
+
* installer, which omits such a dependency from the root instead of failing;
|
|
87
|
+
* runners that differ only in whether they could resolve it genuinely write
|
|
88
|
+
* different trees.
|
|
89
|
+
*/
|
|
90
|
+
async function realmRelativePaths(
|
|
91
|
+
npmBase: string,
|
|
92
|
+
realmPackageRoot: RealmPackageResolver,
|
|
93
|
+
): Promise<string[]> {
|
|
94
|
+
const out: string[] = [];
|
|
95
|
+
for (const name of REALM_COLLAPSE_NAMES) {
|
|
96
|
+
const resolved = await realmPackageRoot(name);
|
|
97
|
+
out.push(`${name}=${resolved ? path.relative(npmBase, resolved) : ""}`);
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Names what a root was keyed from, for whoever is working out which runner
|
|
103
|
+
* wrote which tree. Read by nothing. */
|
|
104
|
+
export const INSTALL_ROOT_MARKER = ".telo-install-root.json";
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Write the marker once, when the root is created. Rewriting it on later
|
|
108
|
+
* installs would only restate what the directory name already fixes, and the
|
|
109
|
+
* value it carries is the key's own inputs — never an entry manifest, since one
|
|
110
|
+
* root serves every manifest in the workspace and naming one would name a
|
|
111
|
+
* coincidence.
|
|
112
|
+
*/
|
|
113
|
+
export async function writeInstallRootMarker(
|
|
114
|
+
root: string,
|
|
115
|
+
npmBase: string,
|
|
116
|
+
realmPackageRoot: RealmPackageResolver,
|
|
117
|
+
): Promise<void> {
|
|
118
|
+
const target = path.join(root, INSTALL_ROOT_MARKER);
|
|
119
|
+
try {
|
|
120
|
+
await fs.access(target);
|
|
121
|
+
return;
|
|
122
|
+
} catch {
|
|
123
|
+
// Absent — this is the run that created the root.
|
|
124
|
+
}
|
|
125
|
+
const marker = {
|
|
126
|
+
realm: await realmRelativePaths(npmBase, realmPackageRoot),
|
|
127
|
+
...hostPlatformTarget(),
|
|
128
|
+
};
|
|
129
|
+
// A marker nothing reads must never fail an install.
|
|
130
|
+
await fs.writeFile(target, JSON.stringify(marker, null, 2) + "\n").catch(() => {});
|
|
131
|
+
}
|
|
@@ -14,6 +14,7 @@ import { tryBuildControllerBundle } from "./bundle-builder.js";
|
|
|
14
14
|
import { withDirectoryLock } from "../directory-lock.js";
|
|
15
15
|
import { ControllerEnvMissingError } from "./napi-loader.js";
|
|
16
16
|
import { REALM_COLLAPSE_NAMES } from "./realm.js";
|
|
17
|
+
import { resolveInstallRoot, writeInstallRootMarker } from "./npm-install-root.js";
|
|
17
18
|
|
|
18
19
|
const execFileAsync = promisify(execFile);
|
|
19
20
|
const requireFromHere = createRequire(import.meta.url);
|
|
@@ -45,6 +46,21 @@ const PEER_INSTALL_FLAGS: ReadonlyArray<string> =
|
|
|
45
46
|
? ["--no-strict-peer-dependencies"]
|
|
46
47
|
: [];
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Quiet the installer WITHOUT quieting its failures. `--silent` does both: on
|
|
51
|
+
* npm 11 a failed install writes nothing at all — not the `ERESOLVE`/`ETARGET`
|
|
52
|
+
* code, not the offending spec — so the wrapper below could only report that
|
|
53
|
+
* some install failed, with no cause, for every failure class. `--loglevel`
|
|
54
|
+
* error keeps the diagnosis and drops the progress chatter, which is the half
|
|
55
|
+
* that was actually worth suppressing.
|
|
56
|
+
*/
|
|
57
|
+
const QUIET_INSTALL_FLAGS: ReadonlyArray<string> =
|
|
58
|
+
PACKAGE_MANAGER === "npm"
|
|
59
|
+
? ["--loglevel=error", "--no-progress"]
|
|
60
|
+
: PACKAGE_MANAGER === "pnpm"
|
|
61
|
+
? ["--loglevel=error"]
|
|
62
|
+
: [];
|
|
63
|
+
|
|
48
64
|
|
|
49
65
|
/**
|
|
50
66
|
* Tells the dispatcher (and any UI consumer downstream) which branch the
|
|
@@ -247,7 +263,10 @@ export class NpmControllerLoader {
|
|
|
247
263
|
);
|
|
248
264
|
}
|
|
249
265
|
const entryUrlStr = this.entryUrl;
|
|
250
|
-
|
|
266
|
+
// The BASE holds one root per runner — per filesystem view of the realm
|
|
267
|
+
// package and per platform — shared by every manifest in the workspace.
|
|
268
|
+
const installBase = this.installRootOverride ?? computeInstallRoot(entryUrlStr);
|
|
269
|
+
const installRoot = await resolveInstallRoot(installBase, resolveKernelPackageRoot);
|
|
251
270
|
|
|
252
271
|
// Build the install-root package.json: kernel-runtime deps as `file:` refs
|
|
253
272
|
// pointing at the kernel-side realpath. Modules declare these names as
|
|
@@ -328,6 +347,9 @@ export class NpmControllerLoader {
|
|
|
328
347
|
2,
|
|
329
348
|
) + "\n",
|
|
330
349
|
);
|
|
350
|
+
// Names what this hashed directory was keyed from. Written before the
|
|
351
|
+
// install so a root that fails to populate still says whose it is.
|
|
352
|
+
await writeInstallRootMarker(installRoot, installBase, resolveKernelPackageRoot);
|
|
331
353
|
// Announced here rather than at the top of the resolve: everything above
|
|
332
354
|
// this line is a fast path that reuses the existing tree.
|
|
333
355
|
await report?.("npm-install");
|
|
@@ -335,7 +357,7 @@ export class NpmControllerLoader {
|
|
|
335
357
|
"install",
|
|
336
358
|
"--no-audit",
|
|
337
359
|
"--no-fund",
|
|
338
|
-
|
|
360
|
+
...QUIET_INSTALL_FLAGS,
|
|
339
361
|
...PEER_INSTALL_FLAGS,
|
|
340
362
|
]);
|
|
341
363
|
await fs.writeFile(stateFile, JSON.stringify({ rootHash: newHash }, null, 2) + "\n");
|
|
@@ -476,11 +498,21 @@ export class NpmControllerLoader {
|
|
|
476
498
|
// Past every re-check: the package manager is about to run for real,
|
|
477
499
|
// which is the wait a caller wants reported.
|
|
478
500
|
await report?.("npm-install");
|
|
501
|
+
// The installer re-resolves the ROOT's whole dependency set, not just
|
|
502
|
+
// the alias being added, so one dead record left by another app fails
|
|
503
|
+
// an install that has nothing to do with it — and the root is shared
|
|
504
|
+
// workspace-wide, so that blast radius is every app. Prune here rather
|
|
505
|
+
// than only on a realm change (`materializeInstallRoot`), which a root
|
|
506
|
+
// that goes stale afterwards never reaches. Placed past every re-check
|
|
507
|
+
// so it costs 2 stats per recorded dep only on the branch that was
|
|
508
|
+
// already going to spawn the package manager, never on a cache hit.
|
|
509
|
+
const pruned = await pruneDeadControllerDeps(installRoot);
|
|
510
|
+
if (pruned) this.rootDeps = pruned;
|
|
479
511
|
await runPackageManager(installRoot, [
|
|
480
512
|
"install",
|
|
481
513
|
"--no-audit",
|
|
482
514
|
"--no-fund",
|
|
483
|
-
|
|
515
|
+
...QUIET_INSTALL_FLAGS,
|
|
484
516
|
...PEER_INSTALL_FLAGS,
|
|
485
517
|
"--save",
|
|
486
518
|
// `<alias>@<source-spec>` installs the package under the alias folder
|
|
@@ -650,9 +682,15 @@ async function runPackageManager(cwd: string, args: string[]): Promise<void> {
|
|
|
650
682
|
`TELO_PKG_MANAGER to a different binary name.`,
|
|
651
683
|
);
|
|
652
684
|
}
|
|
653
|
-
|
|
685
|
+
// Both streams: npm writes its diagnosis to stderr, pnpm and bun put parts
|
|
686
|
+
// of theirs on stdout. Reporting one of them is how a real cause becomes a
|
|
687
|
+
// failure with no message.
|
|
688
|
+
const output = [err?.stderr, err?.stdout]
|
|
689
|
+
.map((s) => (typeof s === "string" ? s.trim() : ""))
|
|
690
|
+
.filter(Boolean)
|
|
691
|
+
.join("\n");
|
|
654
692
|
throw new Error(
|
|
655
|
-
`[telo] '${PACKAGE_MANAGER} ${args.join(" ")}' failed in ${cwd}:${
|
|
693
|
+
`[telo] '${PACKAGE_MANAGER} ${args.join(" ")}' failed in ${cwd}:${output ? `\n${output}` : ""}`,
|
|
656
694
|
);
|
|
657
695
|
}
|
|
658
696
|
}
|
|
@@ -835,6 +873,43 @@ async function liveControllerDeps(
|
|
|
835
873
|
return live;
|
|
836
874
|
}
|
|
837
875
|
|
|
876
|
+
/**
|
|
877
|
+
* Rewrite the install root's `package.json` with its dead controller records
|
|
878
|
+
* dropped, returning the surviving map — or null when nothing had to change,
|
|
879
|
+
* so the common case writes no file.
|
|
880
|
+
*
|
|
881
|
+
* Realm-collapse names are never candidates: they are wired in by
|
|
882
|
+
* `materializeInstallRoot` from the kernel's own resolved paths and are the one
|
|
883
|
+
* thing whose absence must be reported rather than repaired away.
|
|
884
|
+
*
|
|
885
|
+
* Must be called under the install root's lock — it is a read-modify-write of a
|
|
886
|
+
* file every kernel sharing the root writes to.
|
|
887
|
+
*/
|
|
888
|
+
async function pruneDeadControllerDeps(
|
|
889
|
+
installRoot: string,
|
|
890
|
+
): Promise<Record<string, string> | null> {
|
|
891
|
+
const deps = await readPackageDeps(installRoot);
|
|
892
|
+
if (!deps) return null;
|
|
893
|
+
const realm = new Set<string>(REALM_COLLAPSE_NAMES);
|
|
894
|
+
const controllers: Record<string, string> = {};
|
|
895
|
+
const preserved: Record<string, string> = {};
|
|
896
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
897
|
+
if (realm.has(name)) preserved[name] = spec;
|
|
898
|
+
else controllers[name] = spec;
|
|
899
|
+
}
|
|
900
|
+
const live = await liveControllerDeps(controllers, installRoot);
|
|
901
|
+
if (Object.keys(live).length === Object.keys(controllers).length) return null;
|
|
902
|
+
|
|
903
|
+
const packageJsonPath = path.join(installRoot, "package.json");
|
|
904
|
+
const pkg = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
905
|
+
const merged = { ...live, ...preserved };
|
|
906
|
+
await fs.writeFile(
|
|
907
|
+
packageJsonPath,
|
|
908
|
+
JSON.stringify({ ...pkg, dependencies: merged }, null, 2) + "\n",
|
|
909
|
+
);
|
|
910
|
+
return merged;
|
|
911
|
+
}
|
|
912
|
+
|
|
838
913
|
async function readPackageDeps(installRoot: string): Promise<Record<string, string> | null> {
|
|
839
914
|
try {
|
|
840
915
|
const text = await fs.readFile(path.join(installRoot, "package.json"), "utf8");
|
|
@@ -1075,6 +1150,10 @@ export const __testing__ = {
|
|
|
1075
1150
|
resolveExportTargetValue,
|
|
1076
1151
|
tryResolveFile,
|
|
1077
1152
|
computeInstallRoot,
|
|
1153
|
+
/** The root this kernel uses within the base derived from `entryUrl` — what
|
|
1154
|
+
* a test asserting on-disk layout needs, since the base alone is not it. */
|
|
1155
|
+
installRootIn: (entryUrl: string) =>
|
|
1156
|
+
resolveInstallRoot(computeInstallRoot(entryUrl), resolveKernelPackageRoot),
|
|
1078
1157
|
EXPORTS_MAX_DEPTH,
|
|
1079
1158
|
DEFAULT_RESOLVER_CONDITIONS,
|
|
1080
1159
|
REALM_COLLAPSE_NAMES,
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export {
|
|
|
10
10
|
writeManifestCache,
|
|
11
11
|
} from "./manifest-sources/local-manifest-cache-source.js";
|
|
12
12
|
export { MemorySource } from "./manifest-sources/memory-source.js";
|
|
13
|
+
export { WORKSPACE_FILENAME, findWorkspaceRoot, realPath } from "./workspace-marker.js";
|
|
13
14
|
export type { Transport } from "./transports/transport.js";
|
|
14
15
|
export { RegistryTransport } from "./transports/registry-transport.js";
|
|
15
16
|
export { OciTransport } from "./transports/oci/oci-transport.js";
|