Skip to content

Commit 780efa9

Browse files
committed
feat(hosted-runtime): pin reviewed OpenCode artifact contract
1 parent 958bc57 commit 780efa9

9 files changed

Lines changed: 1077 additions & 0 deletions

File tree

opencode-hosted-runtime.lock.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"schemaVersion": 2,
3+
"runtime": "opencode",
4+
"version": "1.18.4-agentteams.1",
5+
"tag": "v1.18.4-agentteams.1",
6+
"productionEligible": false,
7+
"source": {
8+
"repository": "777genius/opencode-anomaly",
9+
"baseCommit": "49c69c5ed3ccf706b61b3febb43c8aaff7f8325e",
10+
"commit": "1554487639c28df9eb294c93257ed52114aa24c5",
11+
"reviewedPatchSha256": "1c80d32f7ad745e97abb7298b69a01062e22c88a3ccd5837cfbcff84e8edc506"
12+
},
13+
"releaseRepository": "777genius/opencode-anomaly",
14+
"platforms": {
15+
"darwin-arm64": { "status": "unavailable", "reason": "artifact_digests_pending" },
16+
"darwin-x64": { "status": "unavailable", "reason": "artifact_digests_pending" },
17+
"linux-arm64": { "status": "unavailable", "reason": "artifact_digests_pending" },
18+
"linux-x64": { "status": "unavailable", "reason": "artifact_digests_pending" },
19+
"win32-arm64": { "status": "unavailable", "reason": "artifact_digests_pending" },
20+
"win32-x64": { "status": "unavailable", "reason": "artifact_digests_pending" }
21+
}
22+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
export const HOSTED_OPENCODE_RUNTIME_LOCK_SCHEMA_VERSION = 2 as const;
2+
export const HOSTED_OPENCODE_RUNTIME_VERSION = '1.18.4-agentteams.1' as const;
3+
export const HOSTED_OPENCODE_RUNTIME_SOURCE = Object.freeze({
4+
repository: '777genius/opencode-anomaly',
5+
baseCommit: '49c69c5ed3ccf706b61b3febb43c8aaff7f8325e',
6+
commit: '1554487639c28df9eb294c93257ed52114aa24c5',
7+
reviewedPatchSha256: '1c80d32f7ad745e97abb7298b69a01062e22c88a3ccd5837cfbcff84e8edc506',
8+
} as const);
9+
export const HOSTED_OPENCODE_RUNTIME_PLATFORM_KEYS = [
10+
'darwin-arm64',
11+
'darwin-x64',
12+
'linux-arm64',
13+
'linux-x64',
14+
'win32-arm64',
15+
'win32-x64',
16+
] as const;
17+
18+
export type HostedOpenCodeRuntimePlatformKey =
19+
(typeof HOSTED_OPENCODE_RUNTIME_PLATFORM_KEYS)[number];
20+
21+
export interface HostedOpenCodeRuntimeAvailableArtifact {
22+
readonly status: 'available';
23+
readonly file: string;
24+
readonly archiveKind: 'tar.gz' | 'zip';
25+
readonly binaryName: 'opencode' | 'opencode.exe';
26+
readonly archiveSha256: string;
27+
readonly binarySha256: string;
28+
readonly assetUrl: string;
29+
}
30+
31+
export interface HostedOpenCodeRuntimeUnavailableArtifact {
32+
readonly status: 'unavailable';
33+
readonly reason: 'artifact_digests_pending';
34+
}
35+
36+
export type HostedOpenCodeRuntimeArtifact =
37+
| HostedOpenCodeRuntimeAvailableArtifact
38+
| HostedOpenCodeRuntimeUnavailableArtifact;
39+
40+
export interface HostedOpenCodeRuntimeLockV2 {
41+
readonly schemaVersion: 2;
42+
readonly runtime: 'opencode';
43+
readonly version: string;
44+
readonly tag: string;
45+
readonly productionEligible: false;
46+
readonly source: {
47+
readonly repository: string;
48+
readonly baseCommit: string;
49+
readonly commit: string;
50+
readonly reviewedPatchSha256: string;
51+
};
52+
readonly releaseRepository: string;
53+
readonly platforms: Readonly<
54+
Record<HostedOpenCodeRuntimePlatformKey, HostedOpenCodeRuntimeArtifact>
55+
>;
56+
}
57+
58+
const SHA256 = /^[0-9a-f]{64}$/u;
59+
const REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
60+
61+
function record(value: unknown): Record<string, unknown> | null {
62+
return value !== null && typeof value === 'object' && !Array.isArray(value)
63+
? (value as Record<string, unknown>)
64+
: null;
65+
}
66+
67+
function exactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
68+
const actual = Object.keys(value).toSorted();
69+
return (
70+
actual.length === keys.length && actual.every((key, index) => key === keys.toSorted()[index])
71+
);
72+
}
73+
74+
function parseArtifact(
75+
value: unknown,
76+
platform: HostedOpenCodeRuntimePlatformKey,
77+
releaseRepository: string,
78+
tag: string
79+
): HostedOpenCodeRuntimeArtifact {
80+
const artifact = record(value);
81+
if (!artifact) throw new Error(`hosted_opencode_lock_artifact_invalid:${platform}`);
82+
if (artifact.status === 'unavailable') {
83+
if (
84+
!exactKeys(artifact, ['status', 'reason']) ||
85+
artifact.reason !== 'artifact_digests_pending'
86+
) {
87+
throw new Error(`hosted_opencode_lock_unavailable_invalid:${platform}`);
88+
}
89+
return { status: 'unavailable', reason: 'artifact_digests_pending' };
90+
}
91+
if (
92+
artifact.status !== 'available' ||
93+
!exactKeys(artifact, [
94+
'status',
95+
'file',
96+
'archiveKind',
97+
'binaryName',
98+
'archiveSha256',
99+
'binarySha256',
100+
'assetUrl',
101+
]) ||
102+
typeof artifact.file !== 'string' ||
103+
!/^[A-Za-z0-9][A-Za-z0-9._-]*\.(?:tar\.gz|zip)$/u.test(artifact.file) ||
104+
(artifact.archiveKind !== 'tar.gz' && artifact.archiveKind !== 'zip') ||
105+
!artifact.file.endsWith(artifact.archiveKind === 'tar.gz' ? '.tar.gz' : '.zip') ||
106+
artifact.binaryName !== (platform.startsWith('win32-') ? 'opencode.exe' : 'opencode') ||
107+
typeof artifact.archiveSha256 !== 'string' ||
108+
!SHA256.test(artifact.archiveSha256) ||
109+
typeof artifact.binarySha256 !== 'string' ||
110+
!SHA256.test(artifact.binarySha256)
111+
) {
112+
throw new Error(`hosted_opencode_lock_available_invalid:${platform}`);
113+
}
114+
const expectedUrl = `https://github.com/${releaseRepository}/releases/download/${tag}/${artifact.file}`;
115+
if (artifact.assetUrl !== expectedUrl || artifact.assetUrl.includes('/releases/latest')) {
116+
throw new Error(`hosted_opencode_lock_asset_url_invalid:${platform}`);
117+
}
118+
return artifact as unknown as HostedOpenCodeRuntimeAvailableArtifact;
119+
}
120+
121+
export function parseHostedOpenCodeRuntimeLock(value: unknown): HostedOpenCodeRuntimeLockV2 {
122+
const lock = record(value);
123+
if (
124+
!lock ||
125+
!exactKeys(lock, [
126+
'schemaVersion',
127+
'runtime',
128+
'version',
129+
'tag',
130+
'productionEligible',
131+
'source',
132+
'releaseRepository',
133+
'platforms',
134+
]) ||
135+
lock.schemaVersion !== HOSTED_OPENCODE_RUNTIME_LOCK_SCHEMA_VERSION ||
136+
lock.runtime !== 'opencode' ||
137+
lock.version !== HOSTED_OPENCODE_RUNTIME_VERSION ||
138+
lock.tag !== `v${lock.version}` ||
139+
lock.productionEligible !== false ||
140+
typeof lock.releaseRepository !== 'string' ||
141+
!REPOSITORY.test(lock.releaseRepository)
142+
) {
143+
throw new Error('hosted_opencode_lock_invalid');
144+
}
145+
const source = record(lock.source);
146+
if (
147+
!source ||
148+
!exactKeys(source, ['repository', 'baseCommit', 'commit', 'reviewedPatchSha256']) ||
149+
source.repository !== HOSTED_OPENCODE_RUNTIME_SOURCE.repository ||
150+
source.repository !== lock.releaseRepository ||
151+
source.baseCommit !== HOSTED_OPENCODE_RUNTIME_SOURCE.baseCommit ||
152+
source.commit !== HOSTED_OPENCODE_RUNTIME_SOURCE.commit ||
153+
source.reviewedPatchSha256 !== HOSTED_OPENCODE_RUNTIME_SOURCE.reviewedPatchSha256
154+
) {
155+
throw new Error('hosted_opencode_lock_source_invalid');
156+
}
157+
const platforms = record(lock.platforms);
158+
if (!platforms || !exactKeys(platforms, HOSTED_OPENCODE_RUNTIME_PLATFORM_KEYS)) {
159+
throw new Error('hosted_opencode_lock_platforms_invalid');
160+
}
161+
const parsedPlatforms = Object.fromEntries(
162+
HOSTED_OPENCODE_RUNTIME_PLATFORM_KEYS.map((platform) => [
163+
platform,
164+
parseArtifact(
165+
platforms[platform],
166+
platform,
167+
lock.releaseRepository as string,
168+
lock.tag as string
169+
),
170+
])
171+
) as unknown as HostedOpenCodeRuntimeLockV2['platforms'];
172+
return {
173+
schemaVersion: 2,
174+
runtime: 'opencode',
175+
version: lock.version,
176+
tag: lock.tag as string,
177+
productionEligible: false,
178+
source: source as unknown as HostedOpenCodeRuntimeLockV2['source'],
179+
releaseRepository: lock.releaseRepository,
180+
platforms: parsedPlatforms,
181+
};
182+
}
183+
184+
export function hostedOpenCodeRuntimePlatformKey(
185+
platform: NodeJS.Platform = process.platform,
186+
arch: string = process.arch
187+
): HostedOpenCodeRuntimePlatformKey {
188+
const key = `${platform}-${arch}`;
189+
if (!HOSTED_OPENCODE_RUNTIME_PLATFORM_KEYS.includes(key as HostedOpenCodeRuntimePlatformKey)) {
190+
throw new Error(`hosted_opencode_platform_unsupported:${key}`);
191+
}
192+
return key as HostedOpenCodeRuntimePlatformKey;
193+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export {
2+
HOSTED_OPENCODE_RUNTIME_LOCK_SCHEMA_VERSION,
3+
HOSTED_OPENCODE_RUNTIME_PLATFORM_KEYS,
4+
HOSTED_OPENCODE_RUNTIME_SOURCE,
5+
HOSTED_OPENCODE_RUNTIME_VERSION,
6+
hostedOpenCodeRuntimePlatformKey,
7+
parseHostedOpenCodeRuntimeLock,
8+
type HostedOpenCodeRuntimeArtifact,
9+
type HostedOpenCodeRuntimeAvailableArtifact,
10+
type HostedOpenCodeRuntimeLockV2,
11+
type HostedOpenCodeRuntimePlatformKey,
12+
} from './core/domain/hostedOpenCodeRuntimeLock';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export {
2+
HOSTED_OPENCODE_CURRENT_MANIFEST_SCHEMA_VERSION,
3+
installHostedOpenCodeRuntime,
4+
parseHostedOpenCodeCurrentManifest,
5+
resolveHostedOpenCodeRuntimeBinary,
6+
type HostedOpenCodeCurrentManifestV2,
7+
type HostedOpenCodeRuntimeInstallerOptions,
8+
} from './infrastructure/HostedOpenCodeRuntimeInstaller';

0 commit comments

Comments
 (0)