@matterbridge/thread 3.5.5-dev-20260219-7ee6be2 → 3.5.5-dev-20260220-779b495

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.
@@ -0,0 +1 @@
1
+ export declare function getDockerVersion(owner: string, repo: string, tag?: string, timeoutMs?: number): Promise<string | undefined>;
@@ -0,0 +1,107 @@
1
+ import { isValidString } from '@matterbridge/utils';
2
+ async function httpsGetJson(url, headers, timeoutMs) {
3
+ const https = await import('node:https');
4
+ return new Promise((resolve, reject) => {
5
+ const controller = new AbortController();
6
+ const timeoutId = setTimeout(() => {
7
+ controller.abort();
8
+ reject(new Error(`Request timed out after ${timeoutMs / 1000} seconds`));
9
+ }, timeoutMs);
10
+ const req = https.get(url, { headers, signal: controller.signal }, (res) => {
11
+ let data = '';
12
+ const statusCode = res.statusCode ?? 0;
13
+ if (statusCode >= 300 && statusCode < 400) {
14
+ const locationHeader = Array.isArray(res.headers.location) ? res.headers.location[0] : res.headers.location;
15
+ if (locationHeader) {
16
+ clearTimeout(timeoutId);
17
+ res.resume();
18
+ const redirectedUrl = new URL(locationHeader, url).toString();
19
+ let redirectedHeaders = headers;
20
+ try {
21
+ const fromHost = new URL(url).host;
22
+ const toHost = new URL(redirectedUrl).host;
23
+ if (fromHost !== toHost && redirectedHeaders?.Authorization) {
24
+ const { Authorization: _authorization, ...rest } = redirectedHeaders;
25
+ redirectedHeaders = rest;
26
+ }
27
+ }
28
+ catch {
29
+ }
30
+ httpsGetJson(redirectedUrl, redirectedHeaders, timeoutMs).then(resolve).catch(reject);
31
+ return;
32
+ }
33
+ }
34
+ if (statusCode < 200 || statusCode >= 300) {
35
+ clearTimeout(timeoutId);
36
+ res.resume();
37
+ reject(new Error(`Failed to fetch data. Status code: ${statusCode}`));
38
+ return;
39
+ }
40
+ res.on('data', (chunk) => {
41
+ data += chunk;
42
+ });
43
+ res.on('end', () => {
44
+ clearTimeout(timeoutId);
45
+ try {
46
+ resolve(JSON.parse(data));
47
+ }
48
+ catch (error) {
49
+ reject(new Error(`Failed to parse response JSON: ${error instanceof Error ? error.message : error}`));
50
+ }
51
+ });
52
+ });
53
+ req.on('error', (error) => {
54
+ clearTimeout(timeoutId);
55
+ reject(new Error(`Request failed: ${error instanceof Error ? error.message : error}`));
56
+ });
57
+ });
58
+ }
59
+ function pickPlatformManifestDigest(manifestList) {
60
+ const manifests = manifestList.manifests;
61
+ if (!manifests || manifests.length === 0)
62
+ return undefined;
63
+ const linuxAmd64 = manifests.find((m) => m.platform?.os === 'linux' && m.platform?.architecture === 'amd64');
64
+ return linuxAmd64?.digest ?? manifests[0]?.digest;
65
+ }
66
+ function getOciVersionLabel(config) {
67
+ return (config.config?.Labels?.['org.opencontainers.image.version'] ??
68
+ config.container_config?.Labels?.['org.opencontainers.image.version'] ??
69
+ config.config?.Labels?.['org.label-schema.version'] ??
70
+ config.container_config?.Labels?.['org.label-schema.version']);
71
+ }
72
+ export async function getDockerVersion(owner, repo, tag = 'latest', timeoutMs = 5_000) {
73
+ if (!isValidString(owner, 1) || !isValidString(repo, 1) || !isValidString(tag, 1))
74
+ return undefined;
75
+ try {
76
+ const tokenUrl = `https://auth.docker.io/token?service=registry.docker.io&scope=repository:${owner}/${repo}:pull`;
77
+ const tokenJson = await httpsGetJson(tokenUrl, undefined, timeoutMs);
78
+ const token = tokenJson.token ?? tokenJson.access_token;
79
+ if (!isValidString(token, 10))
80
+ return undefined;
81
+ const baseHeaders = { Authorization: `Bearer ${token}` };
82
+ const manifestUrl = `https://registry-1.docker.io/v2/${owner}/${repo}/manifests/${encodeURIComponent(tag)}`;
83
+ const accept = [
84
+ 'application/vnd.docker.distribution.manifest.v2+json',
85
+ 'application/vnd.oci.image.manifest.v1+json',
86
+ 'application/vnd.docker.distribution.manifest.list.v2+json',
87
+ 'application/vnd.oci.image.index.v1+json',
88
+ ].join(', ');
89
+ let manifest = await httpsGetJson(manifestUrl, { ...baseHeaders, Accept: accept }, timeoutMs);
90
+ if (manifest.config?.digest === undefined && manifest.manifests !== undefined) {
91
+ const digest = pickPlatformManifestDigest(manifest);
92
+ if (!digest)
93
+ return undefined;
94
+ const byDigestUrl = `https://registry-1.docker.io/v2/${owner}/${repo}/manifests/${digest}`;
95
+ manifest = await httpsGetJson(byDigestUrl, { ...baseHeaders, Accept: accept }, timeoutMs);
96
+ }
97
+ const configDigest = manifest.config?.digest;
98
+ if (!isValidString(configDigest, 10))
99
+ return undefined;
100
+ const configUrl = `https://registry-1.docker.io/v2/${owner}/${repo}/blobs/${configDigest}`;
101
+ const config = await httpsGetJson(configUrl, { ...baseHeaders, Accept: 'application/json' }, timeoutMs);
102
+ return getOciVersionLabel(config);
103
+ }
104
+ catch {
105
+ return undefined;
106
+ }
107
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/thread",
3
- "version": "3.5.5-dev-20260219-7ee6be2",
3
+ "version": "3.5.5-dev-20260220-779b495",
4
4
  "description": "Matterbridge thread library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -61,8 +61,8 @@
61
61
  "CHANGELOG.md"
62
62
  ],
63
63
  "dependencies": {
64
- "@matterbridge/types": "3.5.5-dev-20260219-7ee6be2",
65
- "@matterbridge/utils": "3.5.5-dev-20260219-7ee6be2",
64
+ "@matterbridge/types": "3.5.5-dev-20260220-779b495",
65
+ "@matterbridge/utils": "3.5.5-dev-20260220-779b495",
66
66
  "node-ansi-logger": "3.2.0"
67
67
  },
68
68
  "overrides": {