Display native OpenCode and Continue checklists alongside scheduled tasks, preserving request ownership, explicit clears and saved progress. Bundle both remote runtimes and support managed Continue installation, model routing, cancellation and restart continuation. Validation: real local and Linux remote model checks, full Electron remote Continue flows, typecheck and lint passed. Full suite: 4707 passed, 67 skipped, one OpenCode compaction failure passed targeted rerun. Release note: Display native execution checklists above conversations and support managed remote Continue projects with saved progress and cancellation recovery.
678 lines
20 KiB
JavaScript
678 lines
20 KiB
JavaScript
const {
|
|
createHash,
|
|
sign
|
|
} = require('node:crypto')
|
|
const {
|
|
closeSync,
|
|
copyFileSync,
|
|
fsyncSync,
|
|
lstatSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
openSync,
|
|
readSync,
|
|
readdirSync,
|
|
rmSync,
|
|
statSync,
|
|
writeSync,
|
|
writeFileSync
|
|
} = require('node:fs')
|
|
const { basename, dirname, join, relative, resolve, sep } = require('node:path')
|
|
const { tmpdir } = require('node:os')
|
|
const { targetName } = require('./agent-build-target.cjs')
|
|
const { crc32 } = require('node:zlib')
|
|
const {
|
|
buildAgentBundle,
|
|
expectedManifestMode: expectedAgentManifestMode,
|
|
preflightProductionSigningKey,
|
|
productionSigningKey,
|
|
publicKeySpkiBase64,
|
|
readRuntimeLock,
|
|
readTrustedKeyRegistry,
|
|
redactSecrets,
|
|
verifyLockedBundle
|
|
} = require('./agent-bundle.cjs')
|
|
const {
|
|
buildRuntimeBundle,
|
|
canonicalJson,
|
|
readRemoteRuntimeLock,
|
|
verifyBundleDirectory: verifyRuntimeBundle
|
|
} = require('./remote-runtime-bundle.cjs')
|
|
const {
|
|
centralDirectoryHeaderSignature: zipCentralHeaderSignature,
|
|
endOfCentralDirectorySignature: zipEndSignature,
|
|
localFileHeaderSignature: zipLocalHeaderSignature
|
|
} = require('./zip-central-directory.cjs')
|
|
|
|
const root = join(__dirname, '..')
|
|
const packageDescriptorName = 'agent-package.json'
|
|
const packageSignatureName = 'agent-package.sig'
|
|
const signatureDomain = Buffer.from(
|
|
'GoodBuddy Agent Package Descriptor Signature v1\0',
|
|
'utf8'
|
|
)
|
|
const zipUtf8Flag = 0x0800
|
|
const zipVersion = 20
|
|
const zipMadeByUnix = (3 << 8) | zipVersion
|
|
const zipDosDate = (1 << 5) | 1
|
|
const semanticVersionPattern =
|
|
/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*)|(?:\d*[A-Za-z-][0-9A-Za-z-]*))(?:\.(?:(?:0|[1-9]\d*)|(?:\d*[A-Za-z-][0-9A-Za-z-]*)))*)?$/u
|
|
|
|
function descriptorBytes(descriptor) {
|
|
return Buffer.from(`${JSON.stringify(descriptor, null, 2)}\n`, 'utf8')
|
|
}
|
|
|
|
function descriptorContentDigest(descriptor) {
|
|
const { contentDigest: _contentDigest, ...content } = descriptor
|
|
void _contentDigest
|
|
return `sha256:${createHash('sha256')
|
|
.update(canonicalJson(content))
|
|
.digest('hex')}`
|
|
}
|
|
|
|
function listFiles(directory) {
|
|
const rootPath = resolve(directory)
|
|
const output = []
|
|
const visit = (current) => {
|
|
const currentStat = lstatSync(current)
|
|
if (currentStat.isSymbolicLink()) {
|
|
throw new Error(`Agent package source contains a symlink: ${current}`)
|
|
}
|
|
if (currentStat.isFile()) {
|
|
output.push(current)
|
|
return
|
|
}
|
|
if (!currentStat.isDirectory()) {
|
|
throw new Error(
|
|
`Agent package source contains an unsupported entry: ${current}`
|
|
)
|
|
}
|
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
visit(join(current, entry.name))
|
|
}
|
|
}
|
|
visit(rootPath)
|
|
return output.sort((left, right) =>
|
|
Buffer.from(relative(rootPath, left).split(sep).join('/')).compare(
|
|
Buffer.from(relative(rootPath, right).split(sep).join('/'))
|
|
)
|
|
)
|
|
}
|
|
|
|
function fileDigests(filePath) {
|
|
const hash = createHash('sha256')
|
|
const handle = openSync(filePath, 'r')
|
|
const buffer = Buffer.allocUnsafe(1024 * 1024)
|
|
let checksum = 0
|
|
try {
|
|
while (true) {
|
|
const bytesRead = readSync(
|
|
handle,
|
|
buffer,
|
|
0,
|
|
buffer.length,
|
|
null
|
|
)
|
|
if (bytesRead === 0) {
|
|
break
|
|
}
|
|
const chunk = buffer.subarray(0, bytesRead)
|
|
hash.update(chunk)
|
|
checksum = crc32(chunk, checksum)
|
|
}
|
|
} finally {
|
|
closeSync(handle)
|
|
}
|
|
return {
|
|
crc32: checksum >>> 0,
|
|
sha256: hash.digest('hex')
|
|
}
|
|
}
|
|
|
|
function copyTree(source, destination) {
|
|
for (const filePath of listFiles(source)) {
|
|
const name = relative(source, filePath).split(sep).join('/')
|
|
const target = join(destination, ...name.split('/'))
|
|
mkdirSync(dirname(target), { recursive: true })
|
|
copyFileSync(filePath, target)
|
|
}
|
|
}
|
|
|
|
function packageFileMode(path) {
|
|
if (path.startsWith('agent/')) {
|
|
return expectedAgentManifestMode(path.slice('agent/'.length))
|
|
}
|
|
return /(?:\/bin\/opencode|\/lib\/continue\/dist\/cn\.js)$/u.test(path) ? '0755' : '0644'
|
|
}
|
|
|
|
function stagePackagePayload(options) {
|
|
const staging = options.staging
|
|
copyTree(options.agentBundle, join(staging, 'agent'))
|
|
copyTree(
|
|
options.runtimeBundle,
|
|
join(
|
|
staging,
|
|
'runtime',
|
|
options.runtimeId ?? 'opencode',
|
|
basename(options.runtimeBundle)
|
|
)
|
|
)
|
|
for (const runtime of options.additionalRuntimes ?? []) {
|
|
copyTree(runtime.bundleDirectory, join(staging, 'runtime', runtime.manifest.runtimeId, basename(runtime.bundleDirectory)))
|
|
}
|
|
for (const [value, destination] of [
|
|
[
|
|
options.registry,
|
|
'agent-release-keys.json'
|
|
],
|
|
[
|
|
options.agentLock,
|
|
'agent-runtime-lock.json'
|
|
],
|
|
[
|
|
options.runtimeLock,
|
|
'remote-runtime-lock.json'
|
|
]
|
|
]) {
|
|
writeFileSync(
|
|
join(staging, destination),
|
|
`${JSON.stringify(value, null, 2)}\n`,
|
|
'utf8'
|
|
)
|
|
}
|
|
}
|
|
|
|
function payloadFiles(staging) {
|
|
const crc32ByPath = new Map()
|
|
const files = listFiles(staging)
|
|
.map((filePath) => {
|
|
const path = relative(staging, filePath).split(sep).join('/')
|
|
const size = statSync(filePath).size
|
|
const digests = fileDigests(filePath)
|
|
crc32ByPath.set(path, digests.crc32)
|
|
return {
|
|
path,
|
|
size,
|
|
sha256: digests.sha256,
|
|
mode: packageFileMode(path)
|
|
}
|
|
})
|
|
.filter(
|
|
(file) =>
|
|
file.path !== packageDescriptorName &&
|
|
file.path !== packageSignatureName
|
|
)
|
|
return { crc32ByPath, files }
|
|
}
|
|
|
|
function createPackageArchive(
|
|
staging,
|
|
output,
|
|
knownCrc32 = new Map()
|
|
) {
|
|
mkdirSync(dirname(output), { recursive: true })
|
|
const entries = listFiles(staging).map((filePath) => {
|
|
const name = relative(staging, filePath).split(sep).join('/')
|
|
const nameBytes = Buffer.from(name, 'utf8')
|
|
const size = statSync(filePath).size
|
|
if (
|
|
nameBytes.length === 0 ||
|
|
nameBytes.length > 0xffff ||
|
|
size > 0xffffffff
|
|
) {
|
|
throw new Error(`Agent package ZIP entry is too large: ${name}`)
|
|
}
|
|
return {
|
|
filePath,
|
|
name,
|
|
nameBytes,
|
|
size,
|
|
crc32:
|
|
knownCrc32.get(name) ?? fileDigests(filePath).crc32,
|
|
mode: packageFileMode(name),
|
|
offset: 0
|
|
}
|
|
})
|
|
if (entries.length === 0 || entries.length > 0xffff) {
|
|
throw new Error('Agent package ZIP entry count is invalid')
|
|
}
|
|
const outputHandle = openSync(output, 'w')
|
|
let outputOffset = 0
|
|
const archiveHash = createHash('sha256')
|
|
const writeBytes = (data) => {
|
|
let offset = 0
|
|
while (offset < data.byteLength) {
|
|
const written = writeSync(
|
|
outputHandle,
|
|
data,
|
|
offset,
|
|
data.byteLength - offset
|
|
)
|
|
if (written <= 0) {
|
|
throw new Error('Agent package archive write made no progress')
|
|
}
|
|
offset += written
|
|
}
|
|
archiveHash.update(data)
|
|
outputOffset += data.byteLength
|
|
}
|
|
const buffer = Buffer.allocUnsafe(1024 * 1024)
|
|
try {
|
|
for (const entry of entries) {
|
|
entry.offset = outputOffset
|
|
const header = Buffer.alloc(30)
|
|
header.writeUInt32LE(zipLocalHeaderSignature, 0)
|
|
header.writeUInt16LE(zipVersion, 4)
|
|
header.writeUInt16LE(zipUtf8Flag, 6)
|
|
header.writeUInt16LE(0, 8)
|
|
header.writeUInt16LE(0, 10)
|
|
header.writeUInt16LE(zipDosDate, 12)
|
|
header.writeUInt32LE(entry.crc32, 14)
|
|
header.writeUInt32LE(entry.size, 18)
|
|
header.writeUInt32LE(entry.size, 22)
|
|
header.writeUInt16LE(entry.nameBytes.length, 26)
|
|
header.writeUInt16LE(0, 28)
|
|
writeBytes(header)
|
|
writeBytes(entry.nameBytes)
|
|
|
|
const inputHandle = openSync(entry.filePath, 'r')
|
|
try {
|
|
let position = 0
|
|
while (position < entry.size) {
|
|
const bytesRead = readSync(
|
|
inputHandle,
|
|
buffer,
|
|
0,
|
|
Math.min(buffer.length, entry.size - position),
|
|
null
|
|
)
|
|
if (bytesRead <= 0) {
|
|
throw new Error(
|
|
`Agent package source read made no progress: ${entry.name}`
|
|
)
|
|
}
|
|
position += bytesRead
|
|
writeBytes(buffer.subarray(0, bytesRead))
|
|
}
|
|
} finally {
|
|
closeSync(inputHandle)
|
|
}
|
|
}
|
|
|
|
const centralOffset = outputOffset
|
|
for (const entry of entries) {
|
|
const record = Buffer.alloc(46)
|
|
record.writeUInt32LE(zipCentralHeaderSignature, 0)
|
|
record.writeUInt16LE(zipMadeByUnix, 4)
|
|
record.writeUInt16LE(zipVersion, 6)
|
|
record.writeUInt16LE(zipUtf8Flag, 8)
|
|
record.writeUInt16LE(0, 10)
|
|
record.writeUInt16LE(0, 12)
|
|
record.writeUInt16LE(zipDosDate, 14)
|
|
record.writeUInt32LE(entry.crc32, 16)
|
|
record.writeUInt32LE(entry.size, 20)
|
|
record.writeUInt32LE(entry.size, 24)
|
|
record.writeUInt16LE(entry.nameBytes.length, 28)
|
|
record.writeUInt16LE(0, 30)
|
|
record.writeUInt16LE(0, 32)
|
|
record.writeUInt16LE(0, 34)
|
|
record.writeUInt16LE(0, 36)
|
|
const unixMode =
|
|
0o100000 | Number.parseInt(entry.mode, 8)
|
|
record.writeUInt32LE((unixMode << 16) >>> 0, 38)
|
|
record.writeUInt32LE(entry.offset, 42)
|
|
writeBytes(record)
|
|
writeBytes(entry.nameBytes)
|
|
}
|
|
|
|
const centralSize = outputOffset - centralOffset
|
|
const end = Buffer.alloc(22)
|
|
end.writeUInt32LE(zipEndSignature, 0)
|
|
end.writeUInt16LE(0, 4)
|
|
end.writeUInt16LE(0, 6)
|
|
end.writeUInt16LE(entries.length, 8)
|
|
end.writeUInt16LE(entries.length, 10)
|
|
end.writeUInt32LE(centralSize, 12)
|
|
end.writeUInt32LE(centralOffset, 16)
|
|
end.writeUInt16LE(0, 20)
|
|
writeBytes(end)
|
|
fsyncSync(outputHandle)
|
|
} finally {
|
|
closeSync(outputHandle)
|
|
}
|
|
return {
|
|
size: outputOffset,
|
|
sha256: archiveHash.digest('hex')
|
|
}
|
|
}
|
|
|
|
function assembleAgentPackage(options) {
|
|
const projectRoot = options.projectRoot ?? root
|
|
const architecture = options.architecture
|
|
const platform = options.platform ?? 'linux'
|
|
const target = targetName(architecture, platform)
|
|
if (
|
|
!['x64', 'arm64'].includes(architecture) ||
|
|
!semanticVersionPattern.test(options.minimumDesktopVersion ?? '') ||
|
|
typeof options.output !== 'string'
|
|
) {
|
|
throw new Error('Agent package identity is invalid')
|
|
}
|
|
const registry =
|
|
options.registry ?? readTrustedKeyRegistry(projectRoot)
|
|
const agentLock =
|
|
options.agentLock ?? readRuntimeLock(projectRoot)
|
|
const runtimeLock =
|
|
options.runtimeLock ?? readRemoteRuntimeLock(projectRoot)
|
|
const signingIdentity =
|
|
options.testSigningIdentity ??
|
|
options.signingIdentity ??
|
|
productionSigningKey(options.environment ?? process.env)
|
|
const signingRecord = options.testSigningIdentity
|
|
? registry.keys.find(
|
|
(key) => key.keyId === signingIdentity.keyId
|
|
)
|
|
: preflightProductionSigningKey({
|
|
projectRoot,
|
|
registry,
|
|
keyId: signingIdentity.keyId
|
|
})
|
|
if (!signingRecord) {
|
|
throw new Error('Agent package signing key is not registered')
|
|
}
|
|
if (
|
|
publicKeySpkiBase64(signingIdentity.privateKey) !==
|
|
signingRecord.publicKeySpkiBase64
|
|
) {
|
|
throw new Error(
|
|
'Agent package signing private key does not match the trusted registry'
|
|
)
|
|
}
|
|
const agent = verifyLockedBundle(
|
|
options.agentBundle,
|
|
architecture,
|
|
{
|
|
platform,
|
|
projectRoot,
|
|
registry,
|
|
lock: agentLock,
|
|
verificationEnvironment: options.testSigningIdentity
|
|
? 'test'
|
|
: 'production'
|
|
}
|
|
)
|
|
const runtime = verifyRuntimeBundle(options.runtimeBundle, {
|
|
platform,
|
|
projectRoot,
|
|
architecture,
|
|
registry,
|
|
lock: runtimeLock,
|
|
verificationEnvironment: options.testSigningIdentity
|
|
? 'test'
|
|
: 'production'
|
|
})
|
|
const expectedArchiveName =
|
|
`goodbuddy-agent-${agent.manifest.agentVersion}` +
|
|
`-${target}.gbagent`
|
|
if (basename(options.output) !== expectedArchiveName) {
|
|
throw new Error(
|
|
`Agent package output must be named ${expectedArchiveName}`
|
|
)
|
|
}
|
|
const staging = mkdtempSync(
|
|
join(tmpdir(), `goodbuddy-agent-package-${target}-`)
|
|
)
|
|
try {
|
|
const additionalRuntimes = (options.additionalRuntimeBundles ?? []).map(directory => verifyRuntimeBundle(directory, {
|
|
platform, projectRoot, architecture, registry, lock: runtimeLock,
|
|
verificationEnvironment: options.testSigningIdentity ? 'test' : 'production'
|
|
}))
|
|
if (new Set([runtime, ...additionalRuntimes].map(value => value.manifest.runtimeId)).size !== additionalRuntimes.length + 1) {
|
|
throw new Error('Duplicate packaged Runtime')
|
|
}
|
|
stagePackagePayload({
|
|
staging,
|
|
registry,
|
|
agentLock,
|
|
runtimeLock,
|
|
agentBundle: resolve(options.agentBundle),
|
|
runtimeBundle: runtime.bundleDirectory,
|
|
runtimeId: runtime.manifest.runtimeId,
|
|
additionalRuntimes
|
|
})
|
|
const payload = payloadFiles(staging)
|
|
const initial = {
|
|
format: 'goodbuddy-agent-package',
|
|
formatVersion: 1,
|
|
product: 'GoodBuddy',
|
|
component: 'agent',
|
|
version: agent.manifest.agentVersion,
|
|
minimumDesktopVersion: options.minimumDesktopVersion,
|
|
platform,
|
|
architecture,
|
|
signingKeyId: signingIdentity.keyId,
|
|
agentProtocol: agent.manifest.protocol,
|
|
remoteRuntime: {
|
|
runtimeId: runtime.manifest.runtimeId,
|
|
provider: runtime.manifest.provider,
|
|
version: runtime.manifest.runtimeVersion,
|
|
bundleDigest: runtime.manifest.bundleDigest,
|
|
protocol: runtime.manifest.protocol
|
|
},
|
|
...(additionalRuntimes.length ? { additionalRuntimes: additionalRuntimes.map(({ manifest }) => ({
|
|
runtimeId: manifest.runtimeId, provider: manifest.provider, version: manifest.runtimeVersion,
|
|
bundleDigest: manifest.bundleDigest, protocol: manifest.protocol
|
|
})) } : {}),
|
|
contentDigest: `sha256:${'0'.repeat(64)}`,
|
|
files: payload.files
|
|
}
|
|
const descriptor = {
|
|
...initial,
|
|
contentDigest: descriptorContentDigest(initial)
|
|
}
|
|
const bytes = descriptorBytes(descriptor)
|
|
const signature = sign(
|
|
null,
|
|
Buffer.concat([signatureDomain, bytes]),
|
|
signingIdentity.privateKey
|
|
)
|
|
writeFileSync(join(staging, packageDescriptorName), bytes)
|
|
writeFileSync(
|
|
join(staging, packageSignatureName),
|
|
`${signature.toString('base64')}\n`,
|
|
'utf8'
|
|
)
|
|
const archive = createPackageArchive(
|
|
staging,
|
|
options.output,
|
|
payload.crc32ByPath
|
|
)
|
|
return {
|
|
descriptor,
|
|
archive: basename(options.output),
|
|
size: archive.size,
|
|
sha256: archive.sha256
|
|
}
|
|
} finally {
|
|
rmSync(staging, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
function buildAgentPackage(options) {
|
|
const projectRoot = options.projectRoot ?? root
|
|
const registry =
|
|
options.registry ?? readTrustedKeyRegistry(projectRoot)
|
|
const signingIdentity =
|
|
options.testSigningIdentity ??
|
|
options.signingIdentity ??
|
|
productionSigningKey(options.environment ?? process.env)
|
|
const agentRoot = mkdtempSync(
|
|
join(tmpdir(), `goodbuddy-agent-build-${options.architecture}-`)
|
|
)
|
|
const runtimeRoot = mkdtempSync(
|
|
join(tmpdir(), `goodbuddy-runtime-build-${options.architecture}-`)
|
|
)
|
|
try {
|
|
const agentBundle = buildAgentBundle({
|
|
platform: options.platform ?? 'linux',
|
|
projectRoot,
|
|
arch: options.architecture,
|
|
runtimeArchive: options.nodeArchive,
|
|
outputDirectory: join(agentRoot, 'agent'),
|
|
registry,
|
|
...(options.agentLock ? { lock: options.agentLock } : {}),
|
|
...(options.testSigningIdentity
|
|
? { testSigningIdentity: options.testSigningIdentity }
|
|
: { signingIdentity })
|
|
})
|
|
const runtime = buildRuntimeBundle({
|
|
runtimeId: options.runtimeId,
|
|
platform: options.platform ?? 'linux',
|
|
projectRoot,
|
|
architecture: options.architecture,
|
|
runtimeArchive: options.runtimeArchive,
|
|
outputRoot: runtimeRoot,
|
|
registry,
|
|
...(options.runtimeLock ? { lock: options.runtimeLock } : {}),
|
|
...(options.testSigningIdentity
|
|
? { testSigningIdentity: options.testSigningIdentity }
|
|
: { signingIdentity })
|
|
})
|
|
const additionalRuntimeBundles = []
|
|
if (!options.runtimeId || options.runtimeId === 'opencode') {
|
|
if (!options.continueArchive) throw new Error('Default Agent package requires the locked Continue archive')
|
|
additionalRuntimeBundles.push(buildRuntimeBundle({
|
|
runtimeId: 'continue', platform: options.platform ?? 'linux', projectRoot,
|
|
architecture: options.architecture, runtimeArchive: options.continueArchive, outputRoot: runtimeRoot,
|
|
registry, ...(options.runtimeLock ? { lock: options.runtimeLock } : {}),
|
|
...(options.testSigningIdentity ? { testSigningIdentity: options.testSigningIdentity } : { signingIdentity })
|
|
}).bundleDirectory)
|
|
}
|
|
return assembleAgentPackage({
|
|
...options,
|
|
projectRoot,
|
|
registry,
|
|
...(options.testSigningIdentity
|
|
? {}
|
|
: { signingIdentity }),
|
|
agentBundle,
|
|
runtimeBundle: runtime.bundleDirectory,
|
|
additionalRuntimeBundles
|
|
})
|
|
} finally {
|
|
rmSync(agentRoot, { recursive: true, force: true })
|
|
rmSync(runtimeRoot, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
function parseArguments(argv) {
|
|
const command = argv[0]
|
|
if (!['build', 'assemble'].includes(command)) {
|
|
throw new Error(
|
|
'Usage: agent-package.cjs <build|assemble> --arch <x64|arm64> --minimum-desktop-version <version> --output <archive>'
|
|
)
|
|
}
|
|
const options = {}
|
|
for (let index = 1; index < argv.length; index += 2) {
|
|
const flag = argv[index]
|
|
const value = argv[index + 1]
|
|
if (
|
|
![
|
|
'--arch',
|
|
'--platform',
|
|
'--minimum-desktop-version',
|
|
'--node-archive',
|
|
'--runtime-archive',
|
|
'--runtime-id',
|
|
'--continue-archive',
|
|
'--continue-bundle',
|
|
'--agent-bundle',
|
|
'--runtime-bundle',
|
|
'--output'
|
|
].includes(flag) ||
|
|
!value
|
|
) {
|
|
throw new Error('Invalid Agent package argument')
|
|
}
|
|
options[
|
|
flag.slice(2).replace(/-([a-z])/gu, (_match, valuePart) =>
|
|
valuePart.toUpperCase()
|
|
)
|
|
] = value
|
|
}
|
|
if (
|
|
!['x64', 'arm64'].includes(options.arch) ||
|
|
!semanticVersionPattern.test(
|
|
options.minimumDesktopVersion ?? ''
|
|
) ||
|
|
!options.output
|
|
) {
|
|
throw new Error('Agent package arguments are incomplete')
|
|
}
|
|
const common = {
|
|
runtimeId: options.runtimeId ?? 'opencode',
|
|
platform: options.platform ?? 'linux',
|
|
architecture: options.arch,
|
|
minimumDesktopVersion: options.minimumDesktopVersion,
|
|
output: resolve(options.output)
|
|
}
|
|
if (!['opencode', 'continue'].includes(common.runtimeId)) {
|
|
throw new Error('Unsupported Agent package Runtime')
|
|
}
|
|
targetName(common.architecture, common.platform)
|
|
if (command === 'build') {
|
|
if (!options.nodeArchive || !options.runtimeArchive) {
|
|
throw new Error('Agent package build inputs are incomplete')
|
|
}
|
|
return {
|
|
command,
|
|
...common,
|
|
nodeArchive: resolve(options.nodeArchive),
|
|
runtimeArchive: resolve(options.runtimeArchive),
|
|
continueArchive: options.continueArchive ? resolve(options.continueArchive) : undefined
|
|
}
|
|
}
|
|
if (!options.agentBundle || !options.runtimeBundle) {
|
|
throw new Error('Agent package assembly inputs are incomplete')
|
|
}
|
|
return {
|
|
command,
|
|
...common,
|
|
agentBundle: resolve(options.agentBundle),
|
|
runtimeBundle: resolve(options.runtimeBundle),
|
|
additionalRuntimeBundles: options.continueBundle ? [resolve(options.continueBundle)] : []
|
|
}
|
|
}
|
|
|
|
function main(argv = process.argv.slice(2)) {
|
|
const options = parseArguments(argv)
|
|
const result =
|
|
options.command === 'build'
|
|
? buildAgentPackage(options)
|
|
: assembleAgentPackage(options)
|
|
process.stdout.write(`${JSON.stringify(result)}\n`)
|
|
}
|
|
|
|
module.exports = {
|
|
assembleAgentPackage,
|
|
buildAgentPackage,
|
|
canonicalJson,
|
|
descriptorBytes,
|
|
descriptorContentDigest,
|
|
parseArguments,
|
|
signatureDomain
|
|
}
|
|
|
|
if (require.main === module) {
|
|
try {
|
|
main()
|
|
} catch (error) {
|
|
process.stderr.write(
|
|
`${redactSecrets(
|
|
error instanceof Error ? error.message : String(error)
|
|
)}\n`
|
|
)
|
|
process.exitCode = 1
|
|
}
|
|
}
|