@telorun/kernel 0.41.0 → 0.43.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/files-integrity.d.ts +25 -0
- package/dist/bundle/files-integrity.d.ts.map +1 -0
- package/dist/bundle/files-integrity.js +40 -0
- package/dist/bundle/files-integrity.js.map +1 -0
- package/dist/bundle/module-manifest.d.ts +22 -0
- package/dist/bundle/module-manifest.d.ts.map +1 -0
- package/dist/bundle/module-manifest.js +33 -0
- package/dist/bundle/module-manifest.js.map +1 -0
- package/dist/bundle/tar.d.ts +20 -0
- package/dist/bundle/tar.d.ts.map +1 -0
- package/dist/bundle/tar.js +63 -0
- package/dist/bundle/tar.js.map +1 -0
- package/dist/controller-loaders/bundle-builder.d.ts.map +1 -1
- package/dist/controller-loaders/bundle-builder.js +13 -2
- package/dist/controller-loaders/bundle-builder.js.map +1 -1
- package/dist/controllers/module/import-controller.d.ts +3 -0
- package/dist/controllers/module/import-controller.d.ts.map +1 -1
- package/dist/controllers/module/import-controller.js +7 -2
- package/dist/controllers/module/import-controller.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +8 -2
- package/dist/kernel.js.map +1 -1
- package/dist/manifest-sources/local-manifest-cache-source.d.ts +2 -2
- package/dist/manifest-sources/local-manifest-cache-source.d.ts.map +1 -1
- package/dist/manifest-sources/local-manifest-cache-source.js +24 -90
- package/dist/manifest-sources/local-manifest-cache-source.js.map +1 -1
- package/dist/transports/oci/docker-credentials.d.ts +10 -0
- package/dist/transports/oci/docker-credentials.d.ts.map +1 -0
- package/dist/transports/oci/docker-credentials.js +75 -0
- package/dist/transports/oci/docker-credentials.js.map +1 -0
- package/dist/transports/oci/oci-client.d.ts +51 -0
- package/dist/transports/oci/oci-client.d.ts.map +1 -0
- package/dist/transports/oci/oci-client.js +170 -0
- package/dist/transports/oci/oci-client.js.map +1 -0
- package/dist/transports/oci/oci-ref.d.ts +21 -0
- package/dist/transports/oci/oci-ref.d.ts.map +1 -0
- package/dist/transports/oci/oci-ref.js +36 -0
- package/dist/transports/oci/oci-ref.js.map +1 -0
- package/dist/transports/oci/oci-transport.d.ts +29 -0
- package/dist/transports/oci/oci-transport.d.ts.map +1 -0
- package/dist/transports/oci/oci-transport.js +142 -0
- package/dist/transports/oci/oci-transport.js.map +1 -0
- package/dist/transports/registry-transport.d.ts +23 -0
- package/dist/transports/registry-transport.d.ts.map +1 -0
- package/dist/transports/registry-transport.js +225 -0
- package/dist/transports/registry-transport.js.map +1 -0
- package/dist/transports/transport-registry.d.ts +40 -0
- package/dist/transports/transport-registry.d.ts.map +1 -0
- package/dist/transports/transport-registry.js +70 -0
- package/dist/transports/transport-registry.js.map +1 -0
- package/dist/transports/transport.d.ts +92 -0
- package/dist/transports/transport.d.ts.map +1 -0
- package/dist/transports/transport.js +2 -0
- package/dist/transports/transport.js.map +1 -0
- package/package.json +6 -3
- package/src/bundle/files-integrity.ts +46 -0
- package/src/bundle/module-manifest.ts +49 -0
- package/src/bundle/tar.ts +78 -0
- package/src/controller-loaders/bundle-builder.ts +14 -2
- package/src/controllers/module/import-controller.ts +7 -2
- package/src/index.ts +22 -0
- package/src/kernel.ts +7 -2
- package/src/manifest-sources/local-manifest-cache-source.ts +25 -98
- package/src/transports/oci/docker-credentials.ts +85 -0
- package/src/transports/oci/oci-client.ts +237 -0
- package/src/transports/oci/oci-ref.ts +53 -0
- package/src/transports/oci/oci-transport.ts +192 -0
- package/src/transports/registry-transport.ts +281 -0
- package/src/transports/transport-registry.ts +91 -0
- package/src/transports/transport.ts +112 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { resolveDockerCredential } from "./docker-credentials.js";
|
|
3
|
+
export const TELO_LAYER_MEDIA_TYPE = "application/vnd.telo.module.v1+tar";
|
|
4
|
+
export const OCI_MANIFEST_MEDIA_TYPE = "application/vnd.oci.image.manifest.v1+json";
|
|
5
|
+
export const OCI_EMPTY_CONFIG_MEDIA_TYPE = "application/vnd.oci.empty.v1+json";
|
|
6
|
+
/** The standard OCI empty descriptor — config `{}` (2 bytes). */
|
|
7
|
+
const EMPTY_CONFIG = Buffer.from("{}", "utf-8");
|
|
8
|
+
const EMPTY_CONFIG_DIGEST = "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a";
|
|
9
|
+
const MANIFEST_ACCEPT = [
|
|
10
|
+
OCI_MANIFEST_MEDIA_TYPE,
|
|
11
|
+
"application/vnd.oci.image.index.v1+json",
|
|
12
|
+
"application/vnd.docker.distribution.manifest.v2+json",
|
|
13
|
+
"application/vnd.docker.distribution.manifest.list.v2+json",
|
|
14
|
+
].join(", ");
|
|
15
|
+
function sha256Hex(bytes) {
|
|
16
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
17
|
+
}
|
|
18
|
+
/** Parse a `WWW-Authenticate: Bearer realm="...",service="...",scope="..."` header. */
|
|
19
|
+
function parseBearerChallenge(header) {
|
|
20
|
+
const out = {};
|
|
21
|
+
const params = header.replace(/^Bearer\s+/i, "");
|
|
22
|
+
for (const m of params.matchAll(/([a-z]+)="([^"]*)"/gi)) {
|
|
23
|
+
out[m[1].toLowerCase()] = m[2];
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A minimal OCI distribution (registry v2) client — exactly the surface Telo
|
|
29
|
+
* needs: pull manifest / pull blob / push blob / push manifest / list tags,
|
|
30
|
+
* with the `WWW-Authenticate` bearer-token handshake and the ambient Docker
|
|
31
|
+
* credential chain. Hand-rolled rather than depending on a stale/Deno-only
|
|
32
|
+
* client (see plan Decisions). One instance per `(host, repo)`; tokens are
|
|
33
|
+
* cached per scope for the instance's lifetime.
|
|
34
|
+
*/
|
|
35
|
+
export class OciClient {
|
|
36
|
+
constructor(host, repo) {
|
|
37
|
+
this.host = host;
|
|
38
|
+
this.repo = repo;
|
|
39
|
+
this.tokenByScope = new Map();
|
|
40
|
+
}
|
|
41
|
+
base() {
|
|
42
|
+
return `https://${this.host}/v2/${this.repo}`;
|
|
43
|
+
}
|
|
44
|
+
pullScope() {
|
|
45
|
+
return `repository:${this.repo}:pull`;
|
|
46
|
+
}
|
|
47
|
+
pushScope() {
|
|
48
|
+
return `repository:${this.repo}:pull,push`;
|
|
49
|
+
}
|
|
50
|
+
/** Fetch with the bearer-token dance: try (cached token if any), and on 401
|
|
51
|
+
* resolve a token from the `WWW-Authenticate` challenge and retry once. */
|
|
52
|
+
async authedFetch(url, init, scope) {
|
|
53
|
+
const withToken = (token) => {
|
|
54
|
+
const headers = new Headers(init.headers);
|
|
55
|
+
if (token)
|
|
56
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
57
|
+
return { ...init, headers };
|
|
58
|
+
};
|
|
59
|
+
const cached = this.tokenByScope.get(scope);
|
|
60
|
+
let res = await fetch(url, withToken(cached));
|
|
61
|
+
if (res.status !== 401)
|
|
62
|
+
return res;
|
|
63
|
+
const challenge = res.headers.get("www-authenticate");
|
|
64
|
+
if (!challenge)
|
|
65
|
+
return res;
|
|
66
|
+
const token = await this.fetchToken(challenge, scope);
|
|
67
|
+
if (!token)
|
|
68
|
+
return res;
|
|
69
|
+
this.tokenByScope.set(scope, token);
|
|
70
|
+
// Drain the 401 body so the connection can be reused.
|
|
71
|
+
await res.text().catch(() => { });
|
|
72
|
+
return fetch(url, withToken(token));
|
|
73
|
+
}
|
|
74
|
+
/** Exchange a bearer challenge for a token, authenticating to the token
|
|
75
|
+
* service with Docker credentials when available (else anonymous). */
|
|
76
|
+
async fetchToken(challenge, scope) {
|
|
77
|
+
const params = parseBearerChallenge(challenge);
|
|
78
|
+
if (!params.realm)
|
|
79
|
+
return null;
|
|
80
|
+
const tokenUrl = new URL(params.realm);
|
|
81
|
+
if (params.service)
|
|
82
|
+
tokenUrl.searchParams.set("service", params.service);
|
|
83
|
+
tokenUrl.searchParams.set("scope", params.scope || scope);
|
|
84
|
+
const headers = new Headers();
|
|
85
|
+
const cred = await resolveDockerCredential(this.host);
|
|
86
|
+
if (cred) {
|
|
87
|
+
const basic = Buffer.from(`${cred.username}:${cred.password}`).toString("base64");
|
|
88
|
+
headers.set("authorization", `Basic ${basic}`);
|
|
89
|
+
}
|
|
90
|
+
const res = await fetch(tokenUrl.href, { headers });
|
|
91
|
+
if (!res.ok)
|
|
92
|
+
return null;
|
|
93
|
+
const body = (await res.json());
|
|
94
|
+
return body.token ?? body.access_token ?? null;
|
|
95
|
+
}
|
|
96
|
+
async pullManifest(reference) {
|
|
97
|
+
const res = await this.authedFetch(`${this.base()}/manifests/${reference}`, { headers: { accept: MANIFEST_ACCEPT } }, this.pullScope());
|
|
98
|
+
if (!res.ok) {
|
|
99
|
+
throw new Error(`OCI pull manifest ${this.repo}:${reference} on ${this.host} failed: ${res.status} ${res.statusText}`);
|
|
100
|
+
}
|
|
101
|
+
return (await res.json());
|
|
102
|
+
}
|
|
103
|
+
async pullBlob(digest) {
|
|
104
|
+
const res = await this.authedFetch(`${this.base()}/blobs/${digest}`, {}, this.pullScope());
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
throw new Error(`OCI pull blob ${digest} from ${this.repo} on ${this.host} failed: ${res.status} ${res.statusText}`);
|
|
107
|
+
}
|
|
108
|
+
return Buffer.from(await res.arrayBuffer());
|
|
109
|
+
}
|
|
110
|
+
async listTags() {
|
|
111
|
+
const res = await this.authedFetch(`${this.base()}/tags/list`, {}, this.pullScope());
|
|
112
|
+
if (res.status === 404)
|
|
113
|
+
return [];
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
throw new Error(`OCI list tags for ${this.repo} on ${this.host} failed: ${res.status} ${res.statusText}`);
|
|
116
|
+
}
|
|
117
|
+
const body = (await res.json());
|
|
118
|
+
return Array.isArray(body.tags) ? body.tags : [];
|
|
119
|
+
}
|
|
120
|
+
/** Upload `bytes` as a blob (skipped when already present), returning its
|
|
121
|
+
* `sha256:<hex>` digest. Two-step upload: obtain a session, then PUT with
|
|
122
|
+
* the digest. */
|
|
123
|
+
async pushBlob(bytes) {
|
|
124
|
+
const digest = `sha256:${sha256Hex(bytes)}`;
|
|
125
|
+
const head = await this.authedFetch(`${this.base()}/blobs/${digest}`, { method: "HEAD" }, this.pushScope());
|
|
126
|
+
if (head.ok)
|
|
127
|
+
return digest; // already present
|
|
128
|
+
await head.text().catch(() => { });
|
|
129
|
+
const start = await this.authedFetch(`${this.base()}/blobs/uploads/`, { method: "POST" }, this.pushScope());
|
|
130
|
+
if (start.status !== 202) {
|
|
131
|
+
throw new Error(`OCI blob upload start for ${this.repo} on ${this.host} failed: ${start.status} ${start.statusText}`);
|
|
132
|
+
}
|
|
133
|
+
const location = start.headers.get("location");
|
|
134
|
+
await start.text().catch(() => { });
|
|
135
|
+
if (!location) {
|
|
136
|
+
throw new Error(`OCI blob upload for ${this.repo} returned no Location header`);
|
|
137
|
+
}
|
|
138
|
+
const uploadUrl = new URL(location, `https://${this.host}`);
|
|
139
|
+
uploadUrl.searchParams.set("digest", digest);
|
|
140
|
+
const put = await this.authedFetch(uploadUrl.href, {
|
|
141
|
+
method: "PUT",
|
|
142
|
+
headers: { "content-type": "application/octet-stream" },
|
|
143
|
+
body: bytes,
|
|
144
|
+
}, this.pushScope());
|
|
145
|
+
if (put.status !== 201) {
|
|
146
|
+
const detail = await put.text().catch(() => "");
|
|
147
|
+
throw new Error(`OCI blob PUT for ${this.repo} on ${this.host} failed: ${put.status} ${put.statusText} ${detail}`);
|
|
148
|
+
}
|
|
149
|
+
return digest;
|
|
150
|
+
}
|
|
151
|
+
async pushManifest(reference, manifest) {
|
|
152
|
+
const body = Buffer.from(JSON.stringify(manifest), "utf-8");
|
|
153
|
+
const res = await this.authedFetch(`${this.base()}/manifests/${reference}`, { method: "PUT", headers: { "content-type": OCI_MANIFEST_MEDIA_TYPE }, body }, this.pushScope());
|
|
154
|
+
if (res.status !== 201) {
|
|
155
|
+
const detail = await res.text().catch(() => "");
|
|
156
|
+
throw new Error(`OCI push manifest ${this.repo}:${reference} on ${this.host} failed: ${res.status} ${res.statusText} ${detail}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Push the standard empty config blob and return its descriptor. */
|
|
160
|
+
async pushEmptyConfig() {
|
|
161
|
+
await this.pushBlob(EMPTY_CONFIG);
|
|
162
|
+
return {
|
|
163
|
+
mediaType: OCI_EMPTY_CONFIG_MEDIA_TYPE,
|
|
164
|
+
digest: EMPTY_CONFIG_DIGEST,
|
|
165
|
+
size: EMPTY_CONFIG.length,
|
|
166
|
+
data: EMPTY_CONFIG.toString("base64"),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=oci-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-client.js","sourceRoot":"","sources":["../../../src/transports/oci/oci-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAElE,MAAM,CAAC,MAAM,qBAAqB,GAAG,oCAAoC,CAAC;AAC1E,MAAM,CAAC,MAAM,uBAAuB,GAAG,4CAA4C,CAAC;AACpF,MAAM,CAAC,MAAM,2BAA2B,GAAG,mCAAmC,CAAC;AAE/E,iEAAiE;AACjE,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAChD,MAAM,mBAAmB,GAAG,yEAAyE,CAAC;AAEtG,MAAM,eAAe,GAAG;IACtB,uBAAuB;IACvB,yCAAyC;IACzC,sDAAsD;IACtD,2DAA2D;CAC5D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAkBb,SAAS,SAAS,CAAC,KAAiB;IAClC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,uFAAuF;AACvF,SAAS,oBAAoB,CAAC,MAAc;IAC1C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACxD,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,SAAS;IAGpB,YACmB,IAAY,EACZ,IAAY;QADZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QAJd,iBAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAKvD,CAAC;IAEI,IAAI;QACV,OAAO,WAAW,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IAChD,CAAC;IAEO,SAAS;QACf,OAAO,cAAc,IAAI,CAAC,IAAI,OAAO,CAAC;IACxC,CAAC;IAEO,SAAS;QACf,OAAO,cAAc,IAAI,CAAC,IAAI,YAAY,CAAC;IAC7C,CAAC;IAED;gFAC4E;IACpE,KAAK,CAAC,WAAW,CACvB,GAAW,EACX,IAAiB,EACjB,KAAa;QAEb,MAAM,SAAS,GAAG,CAAC,KAAc,EAAe,EAAE;YAChD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,KAAK;gBAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;YAC3D,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC;QAC9B,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9C,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,GAAG,CAAC;QAEnC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACtD,IAAI,CAAC,SAAS;YAAE,OAAO,GAAG,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC;QACvB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACpC,sDAAsD;QACtD,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACtC,CAAC;IAED;2EACuE;IAC/D,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,KAAa;QACvD,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,MAAM,CAAC,OAAO;YAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACzE,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC;QAE1D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAClF,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA8C,CAAC;QAC7E,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAiB;QAClC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAChC,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,SAAS,EAAE,EACvC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,EACxC,IAAI,CAAC,SAAS,EAAE,CACjB,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,CAAC,IAAI,IAAI,SAAS,OAAO,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CACtG,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAgB,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc;QAC3B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAC3F,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,iBAAiB,MAAM,SAAS,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CACpG,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QACrF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CACzF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+B,CAAC;QAC9D,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,CAAC;IAED;;sBAEkB;IAClB,KAAK,CAAC,QAAQ,CAAC,KAAiB;QAC9B,MAAM,MAAM,GAAG,UAAU,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAE5C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CACjC,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,MAAM,EAAE,EAChC,EAAE,MAAM,EAAE,MAAM,EAAE,EAClB,IAAI,CAAC,SAAS,EAAE,CACjB,CAAC;QACF,IAAI,IAAI,CAAC,EAAE;YAAE,OAAO,MAAM,CAAC,CAAC,kBAAkB;QAC9C,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAElC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAClC,GAAG,IAAI,CAAC,IAAI,EAAE,iBAAiB,EAC/B,EAAE,MAAM,EAAE,MAAM,EAAE,EAClB,IAAI,CAAC,SAAS,EAAE,CACjB,CAAC;QACF,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,6BAA6B,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,YAAY,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,UAAU,EAAE,CACrG,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC/C,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,IAAI,8BAA8B,CAAC,CAAC;QAClF,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5D,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAE7C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAChC,SAAS,CAAC,IAAI,EACd;YACE,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;YACvD,IAAI,EAAE,KAAK;SACZ,EACD,IAAI,CAAC,SAAS,EAAE,CACjB,CAAC;QACF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAChD,MAAM,IAAI,KAAK,CACb,oBAAoB,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,IAAI,MAAM,EAAE,CAClG,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAiB,EAAE,QAAqB;QACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAChC,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,SAAS,EAAE,EACvC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,uBAAuB,EAAE,EAAE,IAAI,EAAE,EAC7E,IAAI,CAAC,SAAS,EAAE,CACjB,CAAC;QACF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAChD,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,CAAC,IAAI,IAAI,SAAS,OAAO,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,IAAI,MAAM,EAAE,CAChH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAClC,OAAO;YACL,SAAS,EAAE,2BAA2B;YACtC,MAAM,EAAE,mBAAmB;YAC3B,IAAI,EAAE,YAAY,CAAC,MAAM;YACzB,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC;SACtC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const OCI_SCHEME = "oci://";
|
|
2
|
+
/** A parsed `oci://host/repo@reference` module ref.
|
|
3
|
+
*
|
|
4
|
+
* - `host` — the registry host (`ghcr.io`, `123.dkr.ecr.us-east-1.amazonaws.com`).
|
|
5
|
+
* - `repo` — the repository path (`aws/telo-s3`), possibly multi-segment.
|
|
6
|
+
* - `reference` — a tag (`1.2.0`) or a digest (`sha256:...`); the OCI address.
|
|
7
|
+
* - `integrity` — Telo's inline `sha256-<base64url>` hash when the ref is pinned
|
|
8
|
+
* (authoritative across transports; the OCI digest is only corroborating). */
|
|
9
|
+
export interface ParsedOciRef {
|
|
10
|
+
host: string;
|
|
11
|
+
repo: string;
|
|
12
|
+
reference: string;
|
|
13
|
+
integrity?: string;
|
|
14
|
+
}
|
|
15
|
+
/** True when `ref` uses the `oci://` scheme (integrity fragment tolerated). */
|
|
16
|
+
export declare function isOciRef(ref: string): boolean;
|
|
17
|
+
/** Parse `oci://host/repo@reference[#sha256-...]`. Throws on a malformed ref.
|
|
18
|
+
* A `reference` may be a tag or a `sha256:` digest; when absent (`@` omitted)
|
|
19
|
+
* it defaults to `latest`, matching OCI tooling. */
|
|
20
|
+
export declare function parseOciRef(ref: string): ParsedOciRef;
|
|
21
|
+
//# sourceMappingURL=oci-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-ref.d.ts","sourceRoot":"","sources":["../../../src/transports/oci/oci-ref.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,WAAW,CAAC;AAEnC;;;;;;iFAMiF;AACjF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;qDAEqD;AACrD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CA0BrD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { splitIntegrity } from "@telorun/analyzer";
|
|
2
|
+
export const OCI_SCHEME = "oci://";
|
|
3
|
+
/** True when `ref` uses the `oci://` scheme (integrity fragment tolerated). */
|
|
4
|
+
export function isOciRef(ref) {
|
|
5
|
+
return splitIntegrity(ref).base.startsWith(OCI_SCHEME);
|
|
6
|
+
}
|
|
7
|
+
/** Parse `oci://host/repo@reference[#sha256-...]`. Throws on a malformed ref.
|
|
8
|
+
* A `reference` may be a tag or a `sha256:` digest; when absent (`@` omitted)
|
|
9
|
+
* it defaults to `latest`, matching OCI tooling. */
|
|
10
|
+
export function parseOciRef(ref) {
|
|
11
|
+
const { base, integrity } = splitIntegrity(ref);
|
|
12
|
+
if (!base.startsWith(OCI_SCHEME)) {
|
|
13
|
+
throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
|
|
14
|
+
}
|
|
15
|
+
const rest = base.slice(OCI_SCHEME.length);
|
|
16
|
+
const slash = rest.indexOf("/");
|
|
17
|
+
if (slash <= 0) {
|
|
18
|
+
throw new Error(`Invalid OCI reference '${ref}', missing repository path after host`);
|
|
19
|
+
}
|
|
20
|
+
const host = rest.slice(0, slash);
|
|
21
|
+
let repoAndRef = rest.slice(slash + 1);
|
|
22
|
+
let reference = "latest";
|
|
23
|
+
const at = repoAndRef.lastIndexOf("@");
|
|
24
|
+
// A digest reference is `repo@sha256:...` — the `@` before `sha256:` is the
|
|
25
|
+
// separator, not part of the repo. A tag reference has no `@`.
|
|
26
|
+
if (at > 0) {
|
|
27
|
+
reference = repoAndRef.slice(at + 1);
|
|
28
|
+
repoAndRef = repoAndRef.slice(0, at);
|
|
29
|
+
}
|
|
30
|
+
const repo = repoAndRef;
|
|
31
|
+
if (!host || !repo || !reference) {
|
|
32
|
+
throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
|
|
33
|
+
}
|
|
34
|
+
return { host, repo, reference, integrity };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=oci-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-ref.js","sourceRoot":"","sources":["../../../src/transports/oci/oci-ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAgBnC,+EAA+E;AAC/E,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AACzD,CAAC;AAED;;qDAEqD;AACrD,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,uCAAuC,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,uCAAuC,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,IAAI,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAEvC,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,MAAM,EAAE,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,4EAA4E;IAC5E,+DAA+D;IAC/D,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACX,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACrC,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC;IACxB,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,uCAAuC,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ManifestSource } from "@telorun/analyzer";
|
|
2
|
+
import type { FetchedArtifact, PublishBundle, PublishOptions, PublishResult, SiblingIdentity, Transport } from "../transport.js";
|
|
3
|
+
/**
|
|
4
|
+
* OCI transport: `oci://host/repo@reference` modules on any OCI distribution
|
|
5
|
+
* registry (GHCR / ECR / Docker Hub / Harbor), over a hand-rolled minimal
|
|
6
|
+
* client. A module is a single artifact — one tar blob carrying `telo.yaml`
|
|
7
|
+
* and the `files:` payload — pushed under a standard OCI artifact manifest.
|
|
8
|
+
*
|
|
9
|
+
* Not browser-reachable (token handshake, Docker credentials, tar extraction),
|
|
10
|
+
* so its resolution `source` is Node-only; the editor resolves `oci://` imports
|
|
11
|
+
* through the discovery hub instead.
|
|
12
|
+
*/
|
|
13
|
+
export declare class OciTransport implements Transport {
|
|
14
|
+
readonly source: ManifestSource;
|
|
15
|
+
constructor();
|
|
16
|
+
supports(ref: string): boolean;
|
|
17
|
+
cacheLocation(ref: string): string[] | null;
|
|
18
|
+
/** Resolve a relative import against an `oci://` base, normalizing the repo to
|
|
19
|
+
* a directory base so `../lib` under `oci://ghcr.io/aws/my-app` →
|
|
20
|
+
* `oci://ghcr.io/aws/lib` (never `oci://ghcr.io/lib`). Non-relative refs pass
|
|
21
|
+
* through. The reference/tag is dropped — the caller re-pins from the
|
|
22
|
+
* sibling's own version. */
|
|
23
|
+
resolveRelative(base: string, relative: string): string;
|
|
24
|
+
listVersions(ref: string): Promise<string[] | null>;
|
|
25
|
+
fetchArtifact(ref: string): Promise<FetchedArtifact>;
|
|
26
|
+
publish(destination: string, bundle: PublishBundle, _opts?: PublishOptions): Promise<PublishResult>;
|
|
27
|
+
canonicalizeSiblingRef(destination: string, relativeSource: string, sibling: SiblingIdentity): string;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=oci-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-transport.d.ts","sourceRoot":"","sources":["../../../src/transports/oci/oci-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACV,MAAM,iBAAiB,CAAC;AAmDzB;;;;;;;;;GASG;AACH,qBAAa,YAAa,YAAW,SAAS;IAC5C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;;IAchC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI9B,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAU3C;;;;iCAI6B;IAC7B,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IASjD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;IAMnD,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAIpD,OAAO,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,EACrB,KAAK,GAAE,cAAmB,GACzB,OAAO,CAAC,aAAa,CAAC;IA+CzB,sBAAsB,CACpB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,eAAe,GACvB,MAAM;CAKV"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { DEFAULT_MANIFEST_FILENAME, IntegrityError, verifyIntegrity, } from "@telorun/analyzer";
|
|
2
|
+
import { computeFilesIntegrity, injectFilesIntegrity } from "../../bundle/files-integrity.js";
|
|
3
|
+
import { readOwnerManifest } from "../../bundle/module-manifest.js";
|
|
4
|
+
import { makeTarGz, readTarGz, toPayloadFiles } from "../../bundle/tar.js";
|
|
5
|
+
import { OciClient, OCI_MANIFEST_MEDIA_TYPE, TELO_LAYER_MEDIA_TYPE, } from "./oci-client.js";
|
|
6
|
+
import { OCI_SCHEME, isOciRef, parseOciRef } from "./oci-ref.js";
|
|
7
|
+
/** Pull the module blob, returning its extracted entries and the `telo.yaml`
|
|
8
|
+
* bytes, verified against the ref's inline hash and its own `filesIntegrity`. */
|
|
9
|
+
async function pullVerified(ref) {
|
|
10
|
+
const { host, repo, reference, integrity } = parseOciRef(ref);
|
|
11
|
+
const client = new OciClient(host, repo);
|
|
12
|
+
const manifest = await client.pullManifest(reference);
|
|
13
|
+
const layer = manifest.layers.find((l) => l.mediaType === TELO_LAYER_MEDIA_TYPE) ?? manifest.layers[0];
|
|
14
|
+
if (!layer) {
|
|
15
|
+
throw new Error(`OCI artifact ${ref} has no layers`);
|
|
16
|
+
}
|
|
17
|
+
const tar = await client.pullBlob(layer.digest);
|
|
18
|
+
const entries = await readTarGz(tar);
|
|
19
|
+
const teloEntry = entries.find((e) => e.name === DEFAULT_MANIFEST_FILENAME);
|
|
20
|
+
if (!teloEntry) {
|
|
21
|
+
throw new Error(`OCI artifact ${ref} blob does not contain ${DEFAULT_MANIFEST_FILENAME}`);
|
|
22
|
+
}
|
|
23
|
+
const manifestText = typeof teloEntry.content === "string" ? teloEntry.content : teloEntry.content.toString("utf-8");
|
|
24
|
+
// Telo's inline hash is authoritative; the OCI content digest only corroborates.
|
|
25
|
+
if (integrity) {
|
|
26
|
+
await verifyIntegrity(new TextEncoder().encode(manifestText), integrity, ref);
|
|
27
|
+
}
|
|
28
|
+
const files = toPayloadFiles(entries);
|
|
29
|
+
const { filesIntegrity } = readOwnerManifest(manifestText);
|
|
30
|
+
if (filesIntegrity) {
|
|
31
|
+
const actual = await computeFilesIntegrity(files);
|
|
32
|
+
if (actual !== filesIntegrity) {
|
|
33
|
+
throw new IntegrityError(`Integrity check failed for ${ref}: filesIntegrity expected ${filesIntegrity}, ` +
|
|
34
|
+
`got ${actual}. The payload does not match the recorded hash.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { manifest: manifestText, files };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* OCI transport: `oci://host/repo@reference` modules on any OCI distribution
|
|
41
|
+
* registry (GHCR / ECR / Docker Hub / Harbor), over a hand-rolled minimal
|
|
42
|
+
* client. A module is a single artifact — one tar blob carrying `telo.yaml`
|
|
43
|
+
* and the `files:` payload — pushed under a standard OCI artifact manifest.
|
|
44
|
+
*
|
|
45
|
+
* Not browser-reachable (token handshake, Docker credentials, tar extraction),
|
|
46
|
+
* so its resolution `source` is Node-only; the editor resolves `oci://` imports
|
|
47
|
+
* through the discovery hub instead.
|
|
48
|
+
*/
|
|
49
|
+
export class OciTransport {
|
|
50
|
+
constructor() {
|
|
51
|
+
this.source = {
|
|
52
|
+
supports: (url) => this.supports(url),
|
|
53
|
+
read: async (url) => {
|
|
54
|
+
const { manifest } = await pullVerified(url);
|
|
55
|
+
const { host, repo, reference } = parseOciRef(url);
|
|
56
|
+
return { text: manifest, source: `${OCI_SCHEME}${host}/${repo}@${reference}` };
|
|
57
|
+
},
|
|
58
|
+
resolveRelative: (base, relative) => this.resolveRelative(base, relative),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
supports(ref) {
|
|
62
|
+
return isOciRef(ref);
|
|
63
|
+
}
|
|
64
|
+
cacheLocation(ref) {
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = parseOciRef(ref);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return ["__oci", parsed.host, ...parsed.repo.split("/"), parsed.reference];
|
|
73
|
+
}
|
|
74
|
+
/** Resolve a relative import against an `oci://` base, normalizing the repo to
|
|
75
|
+
* a directory base so `../lib` under `oci://ghcr.io/aws/my-app` →
|
|
76
|
+
* `oci://ghcr.io/aws/lib` (never `oci://ghcr.io/lib`). Non-relative refs pass
|
|
77
|
+
* through. The reference/tag is dropped — the caller re-pins from the
|
|
78
|
+
* sibling's own version. */
|
|
79
|
+
resolveRelative(base, relative) {
|
|
80
|
+
if (!relative.startsWith(".") && !relative.startsWith("/"))
|
|
81
|
+
return relative;
|
|
82
|
+
const { host, repo } = parseOciRef(base);
|
|
83
|
+
// Resolve the repo path with standard URL semantics against a directory base.
|
|
84
|
+
const resolved = new URL(relative, `https://${host}/${repo}/`);
|
|
85
|
+
const newRepo = resolved.pathname.replace(/^\/+/, "");
|
|
86
|
+
return `${OCI_SCHEME}${host}/${newRepo}`;
|
|
87
|
+
}
|
|
88
|
+
async listVersions(ref) {
|
|
89
|
+
const { host, repo } = parseOciRef(ref);
|
|
90
|
+
const tags = await new OciClient(host, repo).listTags();
|
|
91
|
+
return tags;
|
|
92
|
+
}
|
|
93
|
+
async fetchArtifact(ref) {
|
|
94
|
+
return pullVerified(ref);
|
|
95
|
+
}
|
|
96
|
+
async publish(destination, bundle, _opts = {}) {
|
|
97
|
+
const identity = readOwnerManifest(bundle.manifest);
|
|
98
|
+
if (!identity.version) {
|
|
99
|
+
throw new Error("OCI publish requires metadata.version (used as the tag).");
|
|
100
|
+
}
|
|
101
|
+
// Destination is a full repo (`oci://host/repo`) or host-only
|
|
102
|
+
// (`oci://host`), which defaults the repo to `<namespace>/<name>`.
|
|
103
|
+
const afterScheme = destination.replace(/^oci:\/\//, "").replace(/\/+$/, "");
|
|
104
|
+
const slash = afterScheme.indexOf("/");
|
|
105
|
+
const host = slash > 0 ? afterScheme.slice(0, slash) : afterScheme;
|
|
106
|
+
let repo = slash > 0 ? afterScheme.slice(slash + 1) : "";
|
|
107
|
+
if (!repo) {
|
|
108
|
+
if (!identity.namespace || !identity.name) {
|
|
109
|
+
throw new Error(`OCI publish to host-only '${destination}' needs metadata.namespace and metadata.name to default the repo.`);
|
|
110
|
+
}
|
|
111
|
+
repo = `${identity.namespace}/${identity.name}`;
|
|
112
|
+
}
|
|
113
|
+
const tag = identity.version;
|
|
114
|
+
// Pin the payload, then pack telo.yaml + files into the single module blob.
|
|
115
|
+
let manifestText = bundle.manifest;
|
|
116
|
+
if (bundle.files.length > 0) {
|
|
117
|
+
manifestText = injectFilesIntegrity(manifestText, await computeFilesIntegrity(bundle.files));
|
|
118
|
+
}
|
|
119
|
+
const tar = await makeTarGz([
|
|
120
|
+
{ name: DEFAULT_MANIFEST_FILENAME, content: manifestText },
|
|
121
|
+
...bundle.files.map((f) => ({ name: f.name, content: Buffer.from(f.content) })),
|
|
122
|
+
]);
|
|
123
|
+
const client = new OciClient(host, repo);
|
|
124
|
+
const layerDigest = await client.pushBlob(tar);
|
|
125
|
+
const config = await client.pushEmptyConfig();
|
|
126
|
+
const manifest = {
|
|
127
|
+
schemaVersion: 2,
|
|
128
|
+
mediaType: OCI_MANIFEST_MEDIA_TYPE,
|
|
129
|
+
artifactType: TELO_LAYER_MEDIA_TYPE,
|
|
130
|
+
config,
|
|
131
|
+
layers: [{ mediaType: TELO_LAYER_MEDIA_TYPE, digest: layerDigest, size: tar.length }],
|
|
132
|
+
};
|
|
133
|
+
await client.pushManifest(tag, manifest);
|
|
134
|
+
return { label: `${repo}@${tag}`, url: `${OCI_SCHEME}${host}/${repo}@${tag}` };
|
|
135
|
+
}
|
|
136
|
+
canonicalizeSiblingRef(destination, relativeSource, sibling) {
|
|
137
|
+
// The sibling's repo is the destination repo with the relative applied; the
|
|
138
|
+
// version is its own (identity is the ref, not metadata).
|
|
139
|
+
return `${this.resolveRelative(destination, relativeSource)}@${sibling.version}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=oci-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-transport.js","sourceRoot":"","sources":["../../../src/transports/oci/oci-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAS3E,OAAO,EACL,SAAS,EACT,uBAAuB,EACvB,qBAAqB,GAEtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEjE;kFACkF;AAClF,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,KAAK,GACT,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,qBAAqB,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3F,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,gBAAgB,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IAErC,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,yBAAyB,CAAC,CAAC;IAC5E,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,0BAA0B,yBAAyB,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,YAAY,GAChB,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAElG,iFAAiF;IACjF,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,eAAe,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAEtC,MAAM,EAAE,cAAc,EAAE,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAC3D,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;YAC9B,MAAM,IAAI,cAAc,CACtB,8BAA8B,GAAG,6BAA6B,cAAc,IAAI;gBAC9E,OAAO,MAAM,iDAAiD,CACjE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IAGvB;QACE,IAAI,CAAC,MAAM,GAAG;YACZ,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YACrC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBAClB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;gBAC7C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;gBACnD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,IAAI,IAAI,IAAI,SAAS,EAAE,EAAE,CAAC;YACjF,CAAC;YACD,eAAe,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC;SAC1E,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAED,aAAa,CAAC,GAAW;QACvB,IAAI,MAAsC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7E,CAAC;IAED;;;;iCAI6B;IAC7B,eAAe,CAAC,IAAY,EAAE,QAAgB;QAC5C,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC;QAC5E,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACzC,8EAA8E;QAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,WAAW,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACtD,OAAO,GAAG,UAAU,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,GAAW;QAC5B,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,MAAM,IAAI,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAW;QAC7B,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,OAAO,CACX,WAAmB,EACnB,MAAqB,EACrB,QAAwB,EAAE;QAE1B,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,CAAC;QAED,8DAA8D;QAC9D,mEAAmE;QACnE,MAAM,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC7E,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QACnE,IAAI,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC1C,MAAM,IAAI,KAAK,CACb,6BAA6B,WAAW,mEAAmE,CAC5G,CAAC;YACJ,CAAC;YACD,IAAI,GAAG,GAAG,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;QAClD,CAAC;QACD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC;QAE7B,4EAA4E;QAC5E,IAAI,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC;QACnC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,YAAY,GAAG,oBAAoB,CAAC,YAAY,EAAE,MAAM,qBAAqB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/F,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC;YAC1B,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,YAAY,EAAE;YAC1D,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SAChF,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,eAAe,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAgB;YAC5B,aAAa,EAAE,CAAC;YAChB,SAAS,EAAE,uBAAuB;YAClC,YAAY,EAAE,qBAAqB;YACnC,MAAM;YACN,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;SACtF,CAAC;QACF,MAAM,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAEzC,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC;IACjF,CAAC;IAED,sBAAsB,CACpB,WAAmB,EACnB,cAAsB,EACtB,OAAwB;QAExB,4EAA4E;QAC5E,0DAA0D;QAC1D,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,cAAc,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACnF,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ManifestSource } from "@telorun/analyzer";
|
|
2
|
+
import type { FetchedArtifact, PublishBundle, PublishOptions, PublishResult, SiblingIdentity, Transport } from "./transport.js";
|
|
3
|
+
/** The default HTTP transport: bare `namespace/name@version` registry refs and
|
|
4
|
+
* direct `https://…` URLs, resolving against `registry.telo.run` (or a
|
|
5
|
+
* configured registry). Its resolution `source` composes the browser-safe
|
|
6
|
+
* `RegistrySource` / `HttpSource` from `analyzer`; the Node-only management
|
|
7
|
+
* methods live here. This is the fallback transport for any ref that carries
|
|
8
|
+
* no owning scheme, so `oci://` (or a future `s3://`) never falls through to
|
|
9
|
+
* it — those refs are claimed by their own transport's `supports()`. */
|
|
10
|
+
export declare class RegistryTransport implements Transport {
|
|
11
|
+
private readonly registryUrl;
|
|
12
|
+
private readonly registrySource;
|
|
13
|
+
private readonly httpSource;
|
|
14
|
+
readonly source: ManifestSource;
|
|
15
|
+
constructor(registryUrl?: string);
|
|
16
|
+
supports(ref: string): boolean;
|
|
17
|
+
cacheLocation(ref: string): string[] | null;
|
|
18
|
+
listVersions(ref: string): Promise<string[] | null>;
|
|
19
|
+
fetchArtifact(ref: string): Promise<FetchedArtifact>;
|
|
20
|
+
publish(destination: string, bundle: PublishBundle, opts?: PublishOptions): Promise<PublishResult>;
|
|
21
|
+
canonicalizeSiblingRef(_destination: string, _relativeSource: string, sibling: SiblingIdentity): string;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=registry-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-transport.d.ts","sourceRoot":"","sources":["../../src/transports/registry-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACV,MAAM,gBAAgB,CAAC;AAiDxB;;;;;;yEAMyE;AACzE,qBAAa,iBAAkB,YAAW,SAAS;IAKrC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAJxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;gBAEH,WAAW,GAAE,MAA6B;IAYvE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAK9B,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IA+CrC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;IAenD,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA+BpD,OAAO,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,EACrB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,aAAa,CAAC;IAsEzB,sBAAsB,CACpB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,eAAe,GACvB,MAAM;CASV"}
|