@lythos/cold-pool 0.11.2 → 0.12.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/package.json +2 -2
- package/src/mirror.test.ts +31 -8
- package/src/mirror.ts +8 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lythos/cold-pool",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Cold pool service layer — dedicated resource holder for skill repositories with intent/plan/execute primitives. Single owner of git side-effects; consumed by deck/curator/arena.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agent",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"homepage": "https://github.com/lythos-labs/lythoskill/tree/main/packages/lythoskill-cold-pool#readme",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@lythos/infra": "^0.
|
|
45
|
+
"@lythos/infra": "^0.12.0",
|
|
46
46
|
"simple-git": "^3.36.0"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
package/src/mirror.test.ts
CHANGED
|
@@ -3,15 +3,23 @@ import { probeConnectivity } from './mirror.js'
|
|
|
3
3
|
|
|
4
4
|
describe('probeConnectivity', () => {
|
|
5
5
|
let originalFetch: typeof fetch
|
|
6
|
+
let originalEnv: string | undefined
|
|
6
7
|
let fetchCalls: Array<{ url: string; options: RequestInit }>
|
|
7
8
|
|
|
8
9
|
beforeEach(() => {
|
|
9
10
|
originalFetch = globalThis.fetch
|
|
11
|
+
originalEnv = process.env.LYTHOSKILL_GH_MIRROR
|
|
12
|
+
delete process.env.LYTHOSKILL_GH_MIRROR
|
|
10
13
|
fetchCalls = []
|
|
11
14
|
})
|
|
12
15
|
|
|
13
16
|
afterEach(() => {
|
|
14
17
|
globalThis.fetch = originalFetch
|
|
18
|
+
if (originalEnv !== undefined) {
|
|
19
|
+
process.env.LYTHOSKILL_GH_MIRROR = originalEnv
|
|
20
|
+
} else {
|
|
21
|
+
delete process.env.LYTHOSKILL_GH_MIRROR
|
|
22
|
+
}
|
|
15
23
|
})
|
|
16
24
|
|
|
17
25
|
function mockFetch(
|
|
@@ -50,11 +58,12 @@ describe('probeConnectivity', () => {
|
|
|
50
58
|
})
|
|
51
59
|
|
|
52
60
|
// ── Vertical Slice 2 ──
|
|
53
|
-
test('direct fails, mirror ok → returns mirror path', async () => {
|
|
61
|
+
test('direct fails, user mirror ok → returns mirror path', async () => {
|
|
62
|
+
process.env.LYTHOSKILL_GH_MIRROR = 'https://my-mirror.example.com'
|
|
54
63
|
mockFetch(
|
|
55
64
|
new Map([
|
|
56
65
|
['https://example.com/skill', new Response(null, { status: 500 })],
|
|
57
|
-
['https://
|
|
66
|
+
['https://my-mirror.example.com/https://example.com/skill', new Response(null, { status: 200 })],
|
|
58
67
|
]),
|
|
59
68
|
)
|
|
60
69
|
|
|
@@ -62,7 +71,7 @@ describe('probeConnectivity', () => {
|
|
|
62
71
|
|
|
63
72
|
expect(result).toMatchObject({
|
|
64
73
|
path: 'mirror',
|
|
65
|
-
url: 'https://
|
|
74
|
+
url: 'https://my-mirror.example.com/https://example.com/skill',
|
|
66
75
|
latencyMs: expect.any(Number),
|
|
67
76
|
})
|
|
68
77
|
})
|
|
@@ -91,14 +100,13 @@ describe('probeConnectivity', () => {
|
|
|
91
100
|
|
|
92
101
|
// ── Vertical Slice 5: Racing behavior ──
|
|
93
102
|
test('probes race concurrently, not sequentially', async () => {
|
|
103
|
+
process.env.LYTHOSKILL_GH_MIRROR = 'https://my-mirror.example.com'
|
|
94
104
|
const start = performance.now()
|
|
95
105
|
|
|
96
106
|
mockFetch(
|
|
97
107
|
new Map([
|
|
98
108
|
['https://example.com/skill', new Response(null, { status: 500 })],
|
|
99
|
-
['https://
|
|
100
|
-
['https://ghproxy.com/https://example.com/skill', new Response(null, { status: 200 })],
|
|
101
|
-
['https://mirror.ghproxy.com/https://example.com/skill', new Response(null, { status: 200 })],
|
|
109
|
+
['https://my-mirror.example.com/https://example.com/skill', new Response(null, { status: 200 })],
|
|
102
110
|
]),
|
|
103
111
|
50, // each mock fetch takes 50ms
|
|
104
112
|
)
|
|
@@ -108,11 +116,26 @@ describe('probeConnectivity', () => {
|
|
|
108
116
|
const elapsed = performance.now() - start
|
|
109
117
|
|
|
110
118
|
expect(result?.path).toBe('mirror')
|
|
111
|
-
// Sequential would take ~
|
|
119
|
+
// Sequential would take ~100ms (2 × 50ms). Racing should be ~50-100ms.
|
|
112
120
|
expect(elapsed).toBeLessThan(150)
|
|
113
121
|
})
|
|
114
122
|
|
|
115
|
-
// ── Vertical Slice 6:
|
|
123
|
+
// ── Vertical Slice 6: No user mirror set → only probes direct ──
|
|
124
|
+
test('without user mirror, only probes direct', async () => {
|
|
125
|
+
mockFetch(
|
|
126
|
+
new Map([
|
|
127
|
+
['https://example.com/skill', new Response(null, { status: 200 })],
|
|
128
|
+
]),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
const result = await probeConnectivity('https://example.com/skill')
|
|
132
|
+
|
|
133
|
+
expect(result?.path).toBe('direct')
|
|
134
|
+
expect(fetchCalls.length).toBe(1)
|
|
135
|
+
expect(fetchCalls[0].url).toBe('https://example.com/skill')
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// ── Vertical Slice 7: Timeout honored ──
|
|
116
139
|
test('timeout aborts slow probes', async () => {
|
|
117
140
|
globalThis.fetch = async (_input, init?) => {
|
|
118
141
|
return new Promise((_, reject) => {
|
package/src/mirror.ts
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mirror URL rewriting for restricted networks.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 1. LYTHOSKILL_GH_MIRROR env var (explicit user choice)
|
|
6
|
-
* 2. HTTPS_PROXY / HTTP_PROXY (standard
|
|
7
|
-
* 3. Known public mirrors (auto-fallback when GitHub is unreachable)
|
|
4
|
+
* Two layers only — the tool never decides which third party to trust:
|
|
5
|
+
* 1. LYTHOSKILL_GH_MIRROR env var (explicit user choice, user bears trust)
|
|
6
|
+
* 2. LYTHOS_SOCKS_PROXY / HTTPS_PROXY / HTTP_PROXY (standard, user's own infra)
|
|
8
7
|
*
|
|
9
|
-
*
|
|
8
|
+
* No hard-coded mirror list. Auto-fallback to "known" third-party mirrors was
|
|
9
|
+
* removed: the tool must not silently delegate trust to an external service
|
|
10
|
+
* that can return tampered skill files (see ADR-202605130...).
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
|
-
// ── Known public mirrors (tried in order when GitHub is unreachable) ──
|
|
13
|
-
|
|
14
|
-
const KNOWN_MIRRORS = [
|
|
15
|
-
'https://ghfast.top', // commonly used in CN
|
|
16
|
-
'https://ghproxy.com', // commonly used in CN
|
|
17
|
-
'https://mirror.ghproxy.com', // ghproxy alternative domain
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
// ── Explicit user mirror ──────────────────────────────────────────────
|
|
21
|
-
|
|
22
13
|
export function getMirror(): string | undefined {
|
|
23
14
|
const v = process.env.LYTHOSKILL_GH_MIRROR?.trim()
|
|
24
15
|
if (!v) return undefined
|
|
@@ -28,18 +19,14 @@ export function getMirror(): string | undefined {
|
|
|
28
19
|
return `https://${v.replace(/\/+$/, '')}`
|
|
29
20
|
}
|
|
30
21
|
|
|
31
|
-
// ── URL rewriting ─────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
22
|
export function rewriteUrl(url: string, mirror?: string): string {
|
|
34
23
|
if (!mirror) return url
|
|
35
24
|
return `${mirror}/${url}`
|
|
36
25
|
}
|
|
37
26
|
|
|
38
27
|
export function mirrorUrls(url: string): string[] {
|
|
39
|
-
const mirrors = [...KNOWN_MIRRORS]
|
|
40
28
|
const explicit = getMirror()
|
|
41
|
-
|
|
42
|
-
return mirrors.map(m => rewriteUrl(url, m))
|
|
29
|
+
return explicit ? [rewriteUrl(url, explicit)] : []
|
|
43
30
|
}
|
|
44
31
|
|
|
45
32
|
export function isLikelyGitHubBlock(err: unknown): boolean {
|
|
@@ -61,7 +48,7 @@ export interface ProbeFailure {
|
|
|
61
48
|
}
|
|
62
49
|
|
|
63
50
|
/**
|
|
64
|
-
* Quick connectivity probe: race direct +
|
|
51
|
+
* Quick connectivity probe: race direct + user mirror (if set) concurrently.
|
|
65
52
|
* Uses short timeout (default 3s) to fail fast instead of waiting for
|
|
66
53
|
* the full git clone / fetch timeout.
|
|
67
54
|
*
|