@standardagents/code 0.14.1-issue.658.a1599e → 0.14.1-issue.663.d242d1
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/lib/artifact.mjs +18 -0
- package/lib/install.mjs +93 -0
- package/lib/launcher.mjs +29 -2
- package/package.json +15 -4
- package/postinstall.mjs +8 -1
package/lib/artifact.mjs
CHANGED
|
@@ -84,6 +84,24 @@ export function verifyArtifactTree(root, files, metadata = ['manifest.json', 'ma
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// Check a native signed archive's listing before extraction: `names` from
|
|
88
|
+
// `tar -tzf` and the entry type letters from `tar -tvzf`. The archive must be
|
|
89
|
+
// exactly one of the signed layouts, with only regular files and the
|
|
90
|
+
// directories those files need. Returns the matching file list.
|
|
91
|
+
export function nativeArchiveLayout(names, types) {
|
|
92
|
+
const listed = names.map(name => name.replace(/^\.\//, '')).sort()
|
|
93
|
+
const layout = [ARTIFACT_FILES, BUNDLE_ARTIFACT_FILES, RUNNER_ARTIFACT_FILES, BUNDLE_RUNNER_ARTIFACT_FILES].find(files => {
|
|
94
|
+
const expected = [...artifactDirectories(files).map(path => `${path}/`), ...files, 'manifest.json', 'manifest.sig'].sort()
|
|
95
|
+
return listed.join('\n') === expected.join('\n')
|
|
96
|
+
})
|
|
97
|
+
if (!layout) throw new Error('native artifact archive has unexpected paths')
|
|
98
|
+
const expectedTypes = '-'.repeat(layout.length + 2) + 'd'.repeat(artifactDirectories(layout).length)
|
|
99
|
+
if ([...types].sort().join('') !== expectedTypes) {
|
|
100
|
+
throw new Error('native artifact archive must contain only regular files and expected directories')
|
|
101
|
+
}
|
|
102
|
+
return layout
|
|
103
|
+
}
|
|
104
|
+
|
|
87
105
|
// The four native targets the launcher has ever installed. The two deferred
|
|
88
106
|
// ones stay so their published history verifies.
|
|
89
107
|
export const NATIVE_ARTIFACT_TARGETS = Object.freeze([
|
package/lib/install.mjs
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto'
|
|
3
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
import { artifactBinaryPath, nativeArchiveLayout, verifyNativeArtifact } from './artifact.mjs'
|
|
8
|
+
|
|
9
|
+
// A release's root package names each native target's signed archive in
|
|
10
|
+
// `standardArtifacts`; npm only selects the platform. The archive downloads
|
|
11
|
+
// from the build coordinator, which serves it from R2, and installs under this
|
|
12
|
+
// package's `native/<target>/` once its size, digest, layout, and signed
|
|
13
|
+
// manifest all check out. The daemon's launcher repair finds it there too.
|
|
14
|
+
export const ARTIFACT_ORIGIN = 'https://builds.standardcode.ai'
|
|
15
|
+
const MAX_ARCHIVE = 128 * 1024 * 1024
|
|
16
|
+
const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url))
|
|
17
|
+
|
|
18
|
+
export function nativeDirectory(target, root = PACKAGE_ROOT) {
|
|
19
|
+
return join(root, 'native', target)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function packageMetadata(root = PACKAGE_ROOT) {
|
|
23
|
+
return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// The signed archive a package names for `target`, or null when its binaries
|
|
27
|
+
// install as an npm platform package instead.
|
|
28
|
+
export function artifactPointer(metadata, target) {
|
|
29
|
+
const artifacts = metadata.standardArtifacts
|
|
30
|
+
if (artifacts === undefined) return null
|
|
31
|
+
const pointer = artifacts?.schema === 1 ? artifacts.targets?.[target] : undefined
|
|
32
|
+
if (!pointer
|
|
33
|
+
|| pointer.url !== `${ARTIFACT_ORIGIN}/artifacts/${metadata.version}/${target}.tar.gz`
|
|
34
|
+
|| !/^[0-9a-f]{64}$/.test(pointer.sha256 ?? '')
|
|
35
|
+
|| !Number.isSafeInteger(pointer.size) || pointer.size < 1 || pointer.size > MAX_ARCHIVE) {
|
|
36
|
+
throw new Error(`This package names no valid native archive for ${target}`)
|
|
37
|
+
}
|
|
38
|
+
return pointer
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The installed, verified artifact directory for `target`, or null.
|
|
42
|
+
export function installedArtifact(target, root = PACKAGE_ROOT, publicKey = undefined) {
|
|
43
|
+
const directory = nativeDirectory(target, root)
|
|
44
|
+
try {
|
|
45
|
+
return { directory, manifest: verifyNativeArtifact(directory, { version: packageMetadata(root).version, target }, publicKey) }
|
|
46
|
+
} catch {
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function tar(run, args) {
|
|
52
|
+
const result = run('tar', args, { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 })
|
|
53
|
+
if (result.error) throw result.error
|
|
54
|
+
if (result.status !== 0) throw new Error(`tar ${args[0]} failed: ${result.stderr}`)
|
|
55
|
+
return result.stdout
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function installNativeArtifact({ target, root = PACKAGE_ROOT, fetchImplementation = fetch, run = spawnSync, publicKey = undefined }) {
|
|
59
|
+
const installed = installedArtifact(target, root, publicKey)
|
|
60
|
+
if (installed) return installed.directory
|
|
61
|
+
const metadata = packageMetadata(root)
|
|
62
|
+
const pointer = artifactPointer(metadata, target)
|
|
63
|
+
if (!pointer) throw new Error(`This package carries no native archive for ${target}`)
|
|
64
|
+
const response = await fetchImplementation(pointer.url)
|
|
65
|
+
if (!response.ok) throw new Error(`Downloading the native archive failed with HTTP ${response.status}`)
|
|
66
|
+
const bytes = Buffer.from(await response.arrayBuffer())
|
|
67
|
+
if (bytes.length !== pointer.size || createHash('sha256').update(bytes).digest('hex') !== pointer.sha256) {
|
|
68
|
+
throw new Error('The downloaded native archive does not match its published size and digest')
|
|
69
|
+
}
|
|
70
|
+
const work = join(root, 'native', `.install-${randomUUID()}`)
|
|
71
|
+
mkdirSync(work, { recursive: true, mode: 0o700 })
|
|
72
|
+
try {
|
|
73
|
+
const archive = join(work, 'archive.tar.gz')
|
|
74
|
+
writeFileSync(archive, bytes, { mode: 0o600, flag: 'wx' })
|
|
75
|
+
nativeArchiveLayout(
|
|
76
|
+
tar(run, ['-tzf', archive]).trim().split('\n'),
|
|
77
|
+
tar(run, ['-tvzf', archive]).trim().split('\n').map(line => line[0]),
|
|
78
|
+
)
|
|
79
|
+
const extracted = join(work, 'artifact')
|
|
80
|
+
mkdirSync(extracted, { mode: 0o700 })
|
|
81
|
+
tar(run, ['-xzf', archive, '-C', extracted])
|
|
82
|
+
const manifest = verifyNativeArtifact(extracted, { version: metadata.version, target }, publicKey)
|
|
83
|
+
for (const command of ['standard', 'standardd', 'tmux']) {
|
|
84
|
+
chmodSync(join(extracted, artifactBinaryPath(manifest, command)), 0o755)
|
|
85
|
+
}
|
|
86
|
+
const destination = nativeDirectory(target, root)
|
|
87
|
+
rmSync(destination, { recursive: true, force: true })
|
|
88
|
+
renameSync(extracted, destination)
|
|
89
|
+
return destination
|
|
90
|
+
} finally {
|
|
91
|
+
rmSync(work, { recursive: true, force: true })
|
|
92
|
+
}
|
|
93
|
+
}
|
package/lib/launcher.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import process from 'node:process'
|
|
|
6
6
|
|
|
7
7
|
import { platformPackage } from './platform.mjs'
|
|
8
8
|
import { artifactBinaryPath, requireCompatibleGlibc, verifyNativeArtifact } from './artifact.mjs'
|
|
9
|
+
import { artifactPointer, installNativeArtifact, installedArtifact, packageMetadata } from './install.mjs'
|
|
9
10
|
|
|
10
11
|
const require = createRequire(import.meta.url)
|
|
11
12
|
const COMMANDS = new Set(['standard', 'standardd'])
|
|
@@ -33,11 +34,18 @@ function resolvePackagedBinary(command, options) {
|
|
|
33
34
|
options.report,
|
|
34
35
|
)
|
|
35
36
|
const resolve = options.resolve ?? require.resolve
|
|
37
|
+
const report = options.report ?? process.report
|
|
38
|
+
// A release that carries no binaries on npm installs its signed R2 archive
|
|
39
|
+
// under this package instead (see install.mjs).
|
|
40
|
+
const downloaded = installedArtifact(platform.rustTarget, options.packageRoot)
|
|
41
|
+
if (downloaded) {
|
|
42
|
+
requireCompatibleGlibc(downloaded.manifest, report?.getReport()?.header?.glibcVersionRuntime)
|
|
43
|
+
return join(downloaded.directory, artifactBinaryPath(downloaded.manifest, command))
|
|
44
|
+
}
|
|
36
45
|
try {
|
|
37
46
|
const root = dirname(realpathSync(resolve(`${platform.packageName}/manifest.json`)))
|
|
38
47
|
const version = JSON.parse(readFileSync(new URL('../package.json', import.meta.url))).version
|
|
39
48
|
const artifact = verifyNativeArtifact(root, { version, target: platform.rustTarget })
|
|
40
|
-
const report = options.report ?? process.report
|
|
41
49
|
requireCompatibleGlibc(artifact, report?.getReport()?.header?.glibcVersionRuntime)
|
|
42
50
|
return join(root, artifactBinaryPath(artifact, command))
|
|
43
51
|
} catch (error) {
|
|
@@ -57,8 +65,27 @@ export function spawnBinary(command, args, options = {}) {
|
|
|
57
65
|
})
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
// Install this platform's signed R2 archive when the package names one and
|
|
69
|
+
// has no binaries installed yet. Package managers that skip install scripts
|
|
70
|
+
// (pnpm by default) reach this on the first launch instead of postinstall.
|
|
71
|
+
export async function ensureNativeBinaries(options = {}) {
|
|
72
|
+
if (options.environment?.STANDARD_CODE_BINARY_DIR ?? process.env.STANDARD_CODE_BINARY_DIR) return
|
|
73
|
+
const platform = platformPackage(options.platform, options.architecture, options.report)
|
|
74
|
+
if (installedArtifact(platform.rustTarget, options.packageRoot)) return
|
|
75
|
+
if (!artifactPointer(packageMetadata(options.packageRoot), platform.rustTarget)) return
|
|
76
|
+
try {
|
|
77
|
+
const resolve = options.resolve ?? require.resolve
|
|
78
|
+
resolve(`${platform.packageName}/manifest.json`)
|
|
79
|
+
return
|
|
80
|
+
} catch {
|
|
81
|
+
// No npm platform package: download the archive.
|
|
82
|
+
}
|
|
83
|
+
await installNativeArtifact({ target: platform.rustTarget, root: options.packageRoot, fetchImplementation: options.fetchImplementation })
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function main(command) {
|
|
61
87
|
try {
|
|
88
|
+
await ensureNativeBinaries()
|
|
62
89
|
const result = spawnBinary(command, process.argv.slice(2))
|
|
63
90
|
if (result.error) throw result.error
|
|
64
91
|
if (result.signal) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@standardagents/code",
|
|
3
|
-
"version": "0.14.1-issue.
|
|
3
|
+
"version": "0.14.1-issue.663.d242d1",
|
|
4
4
|
"description": "Platform launcher for the Standard universal terminal network.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -27,9 +27,20 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node postinstall.mjs"
|
|
29
29
|
},
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
30
|
+
"standardArtifacts": {
|
|
31
|
+
"schema": 1,
|
|
32
|
+
"targets": {
|
|
33
|
+
"aarch64-apple-darwin": {
|
|
34
|
+
"url": "https://builds.standardcode.ai/artifacts/0.14.1-issue.663.d242d1/aarch64-apple-darwin.tar.gz",
|
|
35
|
+
"sha256": "d58162d7f9026e37502fcc2e621027015288840f6c4973a53377fad0f61cc8bd",
|
|
36
|
+
"size": 22595079
|
|
37
|
+
},
|
|
38
|
+
"x86_64-unknown-linux-gnu": {
|
|
39
|
+
"url": "https://builds.standardcode.ai/artifacts/0.14.1-issue.663.d242d1/x86_64-unknown-linux-gnu.tar.gz",
|
|
40
|
+
"sha256": "af952fb65cb663f69e120884d85546502f4a8725e5ce85c41c72b423cb1ed13f",
|
|
41
|
+
"size": 28080928
|
|
42
|
+
}
|
|
43
|
+
}
|
|
33
44
|
},
|
|
34
45
|
"standardUpgrade": {
|
|
35
46
|
"schema": 1,
|
package/postinstall.mjs
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { chmodSync } from 'node:fs'
|
|
2
2
|
|
|
3
|
-
import { resolveBinary } from './lib/launcher.mjs'
|
|
3
|
+
import { ensureNativeBinaries, resolveBinary } from './lib/launcher.mjs'
|
|
4
4
|
|
|
5
|
+
try {
|
|
6
|
+
await ensureNativeBinaries()
|
|
7
|
+
} catch (error) {
|
|
8
|
+
// The first launch retries the download.
|
|
9
|
+
const detail = error instanceof Error ? error.message : String(error)
|
|
10
|
+
console.warn(`@standardagents/code: ${detail}`)
|
|
11
|
+
}
|
|
5
12
|
for (const command of ['standard', 'standardd']) {
|
|
6
13
|
try {
|
|
7
14
|
chmodSync(resolveBinary(command), 0o755)
|