@tiangong-lca/cli 0.1.8 → 0.1.10
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/README.md +18 -5
- package/assets/runtime/cli-runtime-descriptor.schema.json +138 -0
- package/assets/runtime/cli-runtime-expectation.schema.json +38 -0
- package/assets/runtime/runtime-bootstrap-lock.schema.json +254 -0
- package/assets/runtime/runtime-command-result.schema.json +95 -0
- package/assets/runtime/runtime-manifest.schema.json +380 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +48 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-import-lca.js +2 -2
- package/dist/src/lib/dataset-import-lca.js.map +1 -1
- package/dist/src/lib/dataset-support-cache.d.ts +48 -0
- package/dist/src/lib/dataset-support-cache.js +265 -0
- package/dist/src/lib/dataset-support-cache.js.map +1 -0
- package/dist/src/lib/runtime/archive-writer.d.ts +7 -0
- package/dist/src/lib/runtime/archive-writer.js +89 -0
- package/dist/src/lib/runtime/archive-writer.js.map +1 -0
- package/dist/src/lib/runtime/archive.d.ts +2 -0
- package/dist/src/lib/runtime/archive.js +122 -0
- package/dist/src/lib/runtime/archive.js.map +1 -0
- package/dist/src/lib/runtime/command.d.ts +8 -0
- package/dist/src/lib/runtime/command.js +57 -0
- package/dist/src/lib/runtime/command.js.map +1 -0
- package/dist/src/lib/runtime/descriptor.d.ts +11 -0
- package/dist/src/lib/runtime/descriptor.js +126 -0
- package/dist/src/lib/runtime/descriptor.js.map +1 -0
- package/dist/src/lib/runtime/download.d.ts +9 -0
- package/dist/src/lib/runtime/download.js +118 -0
- package/dist/src/lib/runtime/download.js.map +1 -0
- package/dist/src/lib/runtime/exec-command.d.ts +7 -0
- package/dist/src/lib/runtime/exec-command.js +83 -0
- package/dist/src/lib/runtime/exec-command.js.map +1 -0
- package/dist/src/lib/runtime/execute.d.ts +11 -0
- package/dist/src/lib/runtime/execute.js +125 -0
- package/dist/src/lib/runtime/execute.js.map +1 -0
- package/dist/src/lib/runtime/files.d.ts +7 -0
- package/dist/src/lib/runtime/files.js +91 -0
- package/dist/src/lib/runtime/files.js.map +1 -0
- package/dist/src/lib/runtime/host.d.ts +8 -0
- package/dist/src/lib/runtime/host.js +37 -0
- package/dist/src/lib/runtime/host.js.map +1 -0
- package/dist/src/lib/runtime/leases.d.ts +12 -0
- package/dist/src/lib/runtime/leases.js +72 -0
- package/dist/src/lib/runtime/leases.js.map +1 -0
- package/dist/src/lib/runtime/managed-command.d.ts +6 -0
- package/dist/src/lib/runtime/managed-command.js +99 -0
- package/dist/src/lib/runtime/managed-command.js.map +1 -0
- package/dist/src/lib/runtime/manager.d.ts +33 -0
- package/dist/src/lib/runtime/manager.js +196 -0
- package/dist/src/lib/runtime/manager.js.map +1 -0
- package/dist/src/lib/runtime/manifest-types.d.ts +73 -0
- package/dist/src/lib/runtime/manifest-types.js +4 -0
- package/dist/src/lib/runtime/manifest-types.js.map +1 -0
- package/dist/src/lib/runtime/manifest-values.d.ts +15 -0
- package/dist/src/lib/runtime/manifest-values.js +124 -0
- package/dist/src/lib/runtime/manifest-values.js.map +1 -0
- package/dist/src/lib/runtime/manifest.d.ts +7 -0
- package/dist/src/lib/runtime/manifest.js +220 -0
- package/dist/src/lib/runtime/manifest.js.map +1 -0
- package/dist/src/lib/runtime/process.d.ts +3 -0
- package/dist/src/lib/runtime/process.js +55 -0
- package/dist/src/lib/runtime/process.js.map +1 -0
- package/dist/src/lib/runtime/storage.d.ts +8 -0
- package/dist/src/lib/runtime/storage.js +142 -0
- package/dist/src/lib/runtime/storage.js.map +1 -0
- package/dist/src/lib/runtime/types.d.ts +45 -0
- package/dist/src/lib/runtime/types.js +9 -0
- package/dist/src/lib/runtime/types.js.map +1 -0
- package/dist/src/lib/supabase-data-api-contract.d.ts +4 -4
- package/dist/src/lib/supabase-data-api-contract.js +2 -0
- package/dist/src/lib/supabase-data-api-contract.js.map +1 -1
- package/dist/src/main.js +13 -1
- package/dist/src/main.js.map +1 -1
- package/dist/src/runtime.d.ts +15 -0
- package/dist/src/runtime.js +22 -0
- package/dist/src/runtime.js.map +1 -0
- package/package.json +8 -2
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import { CliError } from '../errors.js';
|
|
3
|
+
import { loadTrustedRuntimeManifest } from './manifest.js';
|
|
4
|
+
import { executeRuntimeLaunch } from './execute.js';
|
|
5
|
+
export async function runRuntimeExecCommand(args, fetchImpl, env = process.env, run = executeRuntimeLaunch) {
|
|
6
|
+
const separator = args.indexOf('--'), options = separator < 0 ? args : args.slice(0, separator), argv = separator < 0 ? [] : args.slice(separator + 1);
|
|
7
|
+
let flags;
|
|
8
|
+
try {
|
|
9
|
+
flags = parseArgs({
|
|
10
|
+
args: options,
|
|
11
|
+
strict: true,
|
|
12
|
+
allowPositionals: false,
|
|
13
|
+
options: {
|
|
14
|
+
help: { type: 'boolean', short: 'h' },
|
|
15
|
+
manifest: { type: 'string' },
|
|
16
|
+
'manifest-sha256': { type: 'string' },
|
|
17
|
+
'cache-dir': { type: 'string' },
|
|
18
|
+
entry: { type: 'string' },
|
|
19
|
+
cwd: { type: 'string' },
|
|
20
|
+
lease: { type: 'string' },
|
|
21
|
+
'lease-owner': { type: 'string' },
|
|
22
|
+
},
|
|
23
|
+
}).values;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new CliError('Invalid runtime exec arguments.', {
|
|
27
|
+
code: 'RUNTIME_ARGUMENT_INVALID',
|
|
28
|
+
exitCode: 2,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (flags.help)
|
|
32
|
+
return {
|
|
33
|
+
exitCode: 0,
|
|
34
|
+
stdout: 'Usage: tiangong-lca runtime exec --manifest <file> --manifest-sha256 <trusted-sha256> --entry <id> --cwd <work-directory> [--cache-dir <dir>] [--lease <id> --lease-owner <owner>] -- <application argv>\n',
|
|
35
|
+
stderr: '',
|
|
36
|
+
};
|
|
37
|
+
if (!flags.manifest ||
|
|
38
|
+
!flags['manifest-sha256'] ||
|
|
39
|
+
!flags.entry ||
|
|
40
|
+
!flags.cwd ||
|
|
41
|
+
Boolean(flags.lease) !== Boolean(flags['lease-owner']))
|
|
42
|
+
throw new CliError('Runtime exec requires manifest, trust digest, entry and explicit work directory.', { code: 'RUNTIME_ARGUMENT_INVALID', exitCode: 2 });
|
|
43
|
+
const value = loadTrustedRuntimeManifest(flags.manifest, flags['manifest-sha256']);
|
|
44
|
+
const abort = new AbortController();
|
|
45
|
+
const stop = () => abort.abort();
|
|
46
|
+
process.once('SIGINT', stop);
|
|
47
|
+
process.once('SIGTERM', stop);
|
|
48
|
+
try {
|
|
49
|
+
const result = await run(value, {
|
|
50
|
+
cacheDir: flags['cache-dir'],
|
|
51
|
+
entry: flags.entry,
|
|
52
|
+
cwd: flags.cwd,
|
|
53
|
+
argv,
|
|
54
|
+
env,
|
|
55
|
+
fetchImpl,
|
|
56
|
+
signal: abort.signal,
|
|
57
|
+
...(flags.lease ? { lease: { id: flags.lease, owner: flags['lease-owner'] } } : {}),
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
exitCode: abort.signal.aborted
|
|
61
|
+
? 130
|
|
62
|
+
: result.error
|
|
63
|
+
? 1
|
|
64
|
+
: (result.status ?? (result.signal ? 130 : 1)),
|
|
65
|
+
stdout: result.stdout,
|
|
66
|
+
stderr: result.stderr,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (abort.signal.aborted)
|
|
71
|
+
return {
|
|
72
|
+
exitCode: 130,
|
|
73
|
+
stdout: '',
|
|
74
|
+
stderr: 'Runtime execution interrupted; application recovery must preserve existing attempts.\n',
|
|
75
|
+
};
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
process.removeListener('SIGINT', stop);
|
|
80
|
+
process.removeListener('SIGTERM', stop);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=exec-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-command.js","sourceRoot":"","sources":["../../../../src/lib/runtime/exec-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseArgs } from 'node:util';\nimport { CliError } from '../errors.js';\nimport type { FetchLike } from '../http.js';\nimport { loadTrustedRuntimeManifest } from './manifest.js';\nimport { executeRuntimeLaunch } from './execute.js';\nexport async function runRuntimeExecCommand(\n args: string[],\n fetchImpl?: FetchLike,\n env: NodeJS.ProcessEnv = process.env,\n run: typeof executeRuntimeLaunch = executeRuntimeLaunch,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n const separator = args.indexOf('--'),\n options = separator < 0 ? args : args.slice(0, separator),\n argv = separator < 0 ? [] : args.slice(separator + 1);\n let flags;\n try {\n flags = parseArgs({\n args: options,\n strict: true,\n allowPositionals: false,\n options: {\n help: { type: 'boolean', short: 'h' },\n manifest: { type: 'string' },\n 'manifest-sha256': { type: 'string' },\n 'cache-dir': { type: 'string' },\n entry: { type: 'string' },\n cwd: { type: 'string' },\n lease: { type: 'string' },\n 'lease-owner': { type: 'string' },\n },\n }).values;\n } catch {\n throw new CliError('Invalid runtime exec arguments.', {\n code: 'RUNTIME_ARGUMENT_INVALID',\n exitCode: 2,\n });\n }\n if (flags.help)\n return {\n exitCode: 0,\n stdout:\n 'Usage: tiangong-lca runtime exec --manifest <file> --manifest-sha256 <trusted-sha256> --entry <id> --cwd <work-directory> [--cache-dir <dir>] [--lease <id> --lease-owner <owner>] -- <application argv>\\n',\n stderr: '',\n };\n if (\n !flags.manifest ||\n !flags['manifest-sha256'] ||\n !flags.entry ||\n !flags.cwd ||\n Boolean(flags.lease) !== Boolean(flags['lease-owner'])\n )\n throw new CliError(\n 'Runtime exec requires manifest, trust digest, entry and explicit work directory.',\n { code: 'RUNTIME_ARGUMENT_INVALID', exitCode: 2 },\n );\n const value = loadTrustedRuntimeManifest(flags.manifest, flags['manifest-sha256']);\n const abort = new AbortController();\n const stop = () => abort.abort();\n process.once('SIGINT', stop);\n process.once('SIGTERM', stop);\n try {\n const result = await run(value, {\n cacheDir: flags['cache-dir'],\n entry: flags.entry,\n cwd: flags.cwd,\n argv,\n env,\n fetchImpl,\n signal: abort.signal,\n ...(flags.lease ? { lease: { id: flags.lease, owner: flags['lease-owner']! } } : {}),\n });\n return {\n exitCode: abort.signal.aborted\n ? 130\n : result.error\n ? 1\n : (result.status ?? (result.signal ? 130 : 1)),\n stdout: result.stdout,\n stderr: result.stderr,\n };\n } catch (error) {\n if (abort.signal.aborted)\n return {\n exitCode: 130,\n stdout: '',\n stderr:\n 'Runtime execution interrupted; application recovery must preserve existing attempts.\\n',\n };\n throw error;\n } finally {\n process.removeListener('SIGINT', stop);\n process.removeListener('SIGTERM', stop);\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { executeFoundryCommandSpec, type FoundryCommandSpecSpawnResult } from '../../command-spec.js';
|
|
2
|
+
import { type RuntimeManagerOptions } from './manager.js';
|
|
3
|
+
import type { TrustedRuntimeManifest } from './manifest-types.js';
|
|
4
|
+
export declare function runtimeChildEnvironment(source: NodeJS.ProcessEnv, mode: 'isolated' | 'cli-auth'): NodeJS.ProcessEnv;
|
|
5
|
+
export type RuntimeExecutionOptions = RuntimeManagerOptions & {
|
|
6
|
+
entry: string;
|
|
7
|
+
cwd: string;
|
|
8
|
+
argv: readonly string[];
|
|
9
|
+
env?: Record<string, string | undefined>;
|
|
10
|
+
};
|
|
11
|
+
export declare function executeRuntimeLaunch(value: TrustedRuntimeManifest, options: RuntimeExecutionOptions, spawnImpl?: Parameters<typeof executeFoundryCommandSpec>[1]['spawnImpl']): Promise<FoundryCommandSpecSpawnResult>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { createFoundryCommandSpec, executeFoundryCommandSpec, } from '../../command-spec.js';
|
|
5
|
+
import { ensureRuntimeComponents, inspectRuntimeComponents, } from './manager.js';
|
|
6
|
+
import { spawnRuntimeProcess } from './process.js';
|
|
7
|
+
import { acquireRuntimeLease, releaseRuntimeLease } from './leases.js';
|
|
8
|
+
import { cachePath, defaultRuntimeCache, openRuntimeCache } from './storage.js';
|
|
9
|
+
import { assertTrustedManifest, componentKey } from './manifest.js';
|
|
10
|
+
import { inspectRuntimeHost } from './host.js';
|
|
11
|
+
import { runtimeError } from './files.js';
|
|
12
|
+
const systemKeys = [
|
|
13
|
+
'PATH',
|
|
14
|
+
'Path',
|
|
15
|
+
'PATHEXT',
|
|
16
|
+
'SystemRoot',
|
|
17
|
+
'SYSTEMROOT',
|
|
18
|
+
'WINDIR',
|
|
19
|
+
'ComSpec',
|
|
20
|
+
'COMSPEC',
|
|
21
|
+
'HOME',
|
|
22
|
+
'USERPROFILE',
|
|
23
|
+
'APPDATA',
|
|
24
|
+
'LOCALAPPDATA',
|
|
25
|
+
'XDG_CONFIG_HOME',
|
|
26
|
+
'XDG_CACHE_HOME',
|
|
27
|
+
'TMPDIR',
|
|
28
|
+
'TEMP',
|
|
29
|
+
'TMP',
|
|
30
|
+
'LANG',
|
|
31
|
+
'LC_ALL',
|
|
32
|
+
'TZ',
|
|
33
|
+
];
|
|
34
|
+
const authKeys = [
|
|
35
|
+
'TIANGONG_LCA_API_BASE_URL',
|
|
36
|
+
'TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY',
|
|
37
|
+
'TIANGONG_LCA_OAUTH_CLIENT_ID',
|
|
38
|
+
'TIANGONG_LCA_OAUTH_REDIRECT_URI',
|
|
39
|
+
'TIANGONG_LCA_REGION',
|
|
40
|
+
'TIANGONG_LCA_AUTH_MODE',
|
|
41
|
+
'TIANGONG_LCA_SESSION_FILE',
|
|
42
|
+
'TIANGONG_LCA_DISABLE_SESSION_CACHE',
|
|
43
|
+
'TIANGONG_LCA_FORCE_REAUTH',
|
|
44
|
+
'TIANGONG_LCA_ACCESS_TOKEN',
|
|
45
|
+
];
|
|
46
|
+
export function runtimeChildEnvironment(source, mode) {
|
|
47
|
+
const env = {};
|
|
48
|
+
for (const key of [...systemKeys, ...(mode === 'cli-auth' ? authKeys : [])])
|
|
49
|
+
if (source[key] !== undefined)
|
|
50
|
+
env[key] = source[key];
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
export async function executeRuntimeLaunch(value, options, spawnImpl) {
|
|
54
|
+
assertTrustedManifest(value);
|
|
55
|
+
const host = options.host ?? inspectRuntimeHost();
|
|
56
|
+
const launch = value.manifest.launches.find((item) => item.id === options.entry && item.platform === host.platform);
|
|
57
|
+
if (!launch)
|
|
58
|
+
runtimeError('RUNTIME_LAUNCH_UNKNOWN', 'Selected runtime entry is not declared for this host.');
|
|
59
|
+
if (options.argv.length > 512 ||
|
|
60
|
+
options.argv.some((arg) => typeof arg !== 'string' ||
|
|
61
|
+
arg.includes('\0') ||
|
|
62
|
+
arg.length > 32768 ||
|
|
63
|
+
/^--(?:username|password|api-key|access-token|refresh-token|authorization-code|oauth-code)(?:=|$)/iu.test(arg)))
|
|
64
|
+
runtimeError('RUNTIME_LAUNCH_ARGUMENT', 'Runtime argv must be bounded and contain no credential arguments.');
|
|
65
|
+
if (!path.isAbsolute(options.cwd) || !fs.statSync(options.cwd).isDirectory())
|
|
66
|
+
runtimeError('RUNTIME_LAUNCH_CWD', 'Runtime launch requires an explicit existing work directory.');
|
|
67
|
+
const cwd = fs.realpathSync(options.cwd), cache = openRuntimeCache(options.cacheDir ?? defaultRuntimeCache(), false);
|
|
68
|
+
const relative = path.relative(cache, cwd);
|
|
69
|
+
if (relative === '' ||
|
|
70
|
+
(!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative)) ||
|
|
71
|
+
/\/(?:\.agents|\.codex)\/skills(?:\/|$)/u.test(cwd.split(path.sep).join('/')))
|
|
72
|
+
runtimeError('RUNTIME_LAUNCH_CWD', 'A runtime cache or installed skill cannot be used as the work directory.');
|
|
73
|
+
const lease = { id: `execution-${randomUUID()}`, owner: `runtime-execution:${process.pid}` };
|
|
74
|
+
let drain;
|
|
75
|
+
try {
|
|
76
|
+
await ensureRuntimeComponents(value, { ...options, cacheDir: cache, host, lease });
|
|
77
|
+
if (options.lease)
|
|
78
|
+
await acquireRuntimeLease(cache, options.lease.id, options.lease.owner, value.manifest.components
|
|
79
|
+
.filter((item) => item.platform === host.platform)
|
|
80
|
+
.map(componentKey));
|
|
81
|
+
if (inspectRuntimeComponents(value, { ...options, cacheDir: cache, host }).status !== 'ready')
|
|
82
|
+
runtimeError('RUNTIME_LAUNCH_CHANGED', 'Runtime changed before launch.');
|
|
83
|
+
const artifact = (ref) => {
|
|
84
|
+
const component = value.manifest.components.find((item) => item.id === ref.component && item.platform === host.platform);
|
|
85
|
+
const file = component.files.find((item) => item.path === ref.path);
|
|
86
|
+
const filePath = cachePath(cache, `components/${componentKey(component)}/root/${ref.path}`);
|
|
87
|
+
return {
|
|
88
|
+
role: `runtime:${ref.component}:${ref.path}`,
|
|
89
|
+
path: filePath,
|
|
90
|
+
bytes: file.bytes,
|
|
91
|
+
sha256: file.sha256,
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
const program = artifact(launch.executable);
|
|
95
|
+
const references = launch.argv
|
|
96
|
+
.filter((arg) => 'component' in arg)
|
|
97
|
+
.map(artifact);
|
|
98
|
+
const argv = launch.argv
|
|
99
|
+
.map((arg) => ('literal' in arg ? arg.literal : artifact(arg).path))
|
|
100
|
+
.concat([...options.argv]);
|
|
101
|
+
const spec = createFoundryCommandSpec({
|
|
102
|
+
executable: program.path,
|
|
103
|
+
argv,
|
|
104
|
+
binding: { artifacts: [program, ...references] },
|
|
105
|
+
});
|
|
106
|
+
const result = await executeFoundryCommandSpec(spec, {
|
|
107
|
+
cwd,
|
|
108
|
+
env: runtimeChildEnvironment(options.env ?? process.env, launch.environment),
|
|
109
|
+
signal: options.signal,
|
|
110
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
111
|
+
resolveArtifactPath: (file) => file,
|
|
112
|
+
spawnImpl: (executable, argv, childOptions) => {
|
|
113
|
+
drain = (spawnImpl ?? spawnRuntimeProcess)(executable, argv, childOptions);
|
|
114
|
+
return drain;
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
await drain?.catch(() => undefined);
|
|
121
|
+
if (fs.existsSync(cachePath(cache, '.runtime-cache.json')))
|
|
122
|
+
await releaseRuntimeLease(cache, lease.id, lease.owner);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../../src/lib/runtime/execute.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport {\n createFoundryCommandSpec,\n executeFoundryCommandSpec,\n type FoundryCommandSpecSpawnResult,\n} from '../../command-spec.js';\nimport {\n ensureRuntimeComponents,\n inspectRuntimeComponents,\n type RuntimeManagerOptions,\n} from './manager.js';\nimport { spawnRuntimeProcess } from './process.js';\nimport { acquireRuntimeLease, releaseRuntimeLease } from './leases.js';\nimport { cachePath, defaultRuntimeCache, openRuntimeCache } from './storage.js';\nimport { assertTrustedManifest, componentKey } from './manifest.js';\nimport { inspectRuntimeHost } from './host.js';\nimport { runtimeError } from './files.js';\nimport type { TrustedRuntimeManifest, ComponentPath } from './manifest-types.js';\n\nconst systemKeys = [\n 'PATH',\n 'Path',\n 'PATHEXT',\n 'SystemRoot',\n 'SYSTEMROOT',\n 'WINDIR',\n 'ComSpec',\n 'COMSPEC',\n 'HOME',\n 'USERPROFILE',\n 'APPDATA',\n 'LOCALAPPDATA',\n 'XDG_CONFIG_HOME',\n 'XDG_CACHE_HOME',\n 'TMPDIR',\n 'TEMP',\n 'TMP',\n 'LANG',\n 'LC_ALL',\n 'TZ',\n] as const;\nconst authKeys = [\n 'TIANGONG_LCA_API_BASE_URL',\n 'TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY',\n 'TIANGONG_LCA_OAUTH_CLIENT_ID',\n 'TIANGONG_LCA_OAUTH_REDIRECT_URI',\n 'TIANGONG_LCA_REGION',\n 'TIANGONG_LCA_AUTH_MODE',\n 'TIANGONG_LCA_SESSION_FILE',\n 'TIANGONG_LCA_DISABLE_SESSION_CACHE',\n 'TIANGONG_LCA_FORCE_REAUTH',\n 'TIANGONG_LCA_ACCESS_TOKEN',\n] as const;\nexport function runtimeChildEnvironment(\n source: NodeJS.ProcessEnv,\n mode: 'isolated' | 'cli-auth',\n): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const key of [...systemKeys, ...(mode === 'cli-auth' ? authKeys : [])])\n if (source[key] !== undefined) env[key] = source[key];\n return env;\n}\nexport type RuntimeExecutionOptions = RuntimeManagerOptions & {\n entry: string;\n cwd: string;\n argv: readonly string[];\n env?: Record<string, string | undefined>;\n};\nexport async function executeRuntimeLaunch(\n value: TrustedRuntimeManifest,\n options: RuntimeExecutionOptions,\n spawnImpl?: Parameters<typeof executeFoundryCommandSpec>[1]['spawnImpl'],\n): Promise<FoundryCommandSpecSpawnResult> {\n assertTrustedManifest(value);\n const host = options.host ?? inspectRuntimeHost();\n const launch = value.manifest.launches.find(\n (item) => item.id === options.entry && item.platform === host.platform,\n );\n if (!launch)\n runtimeError('RUNTIME_LAUNCH_UNKNOWN', 'Selected runtime entry is not declared for this host.');\n if (\n options.argv.length > 512 ||\n options.argv.some(\n (arg) =>\n typeof arg !== 'string' ||\n arg.includes('\\0') ||\n arg.length > 32768 ||\n /^--(?:username|password|api-key|access-token|refresh-token|authorization-code|oauth-code)(?:=|$)/iu.test(\n arg,\n ),\n )\n )\n runtimeError(\n 'RUNTIME_LAUNCH_ARGUMENT',\n 'Runtime argv must be bounded and contain no credential arguments.',\n );\n if (!path.isAbsolute(options.cwd) || !fs.statSync(options.cwd).isDirectory())\n runtimeError(\n 'RUNTIME_LAUNCH_CWD',\n 'Runtime launch requires an explicit existing work directory.',\n );\n const cwd = fs.realpathSync(options.cwd),\n cache = openRuntimeCache(options.cacheDir ?? defaultRuntimeCache(), false);\n const relative = path.relative(cache, cwd);\n if (\n relative === '' ||\n (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative)) ||\n /\\/(?:\\.agents|\\.codex)\\/skills(?:\\/|$)/u.test(cwd.split(path.sep).join('/'))\n )\n runtimeError(\n 'RUNTIME_LAUNCH_CWD',\n 'A runtime cache or installed skill cannot be used as the work directory.',\n );\n const lease = { id: `execution-${randomUUID()}`, owner: `runtime-execution:${process.pid}` };\n let drain: Promise<FoundryCommandSpecSpawnResult> | undefined;\n try {\n await ensureRuntimeComponents(value, { ...options, cacheDir: cache, host, lease });\n if (options.lease)\n await acquireRuntimeLease(\n cache,\n options.lease.id,\n options.lease.owner,\n value.manifest.components\n .filter((item) => item.platform === host.platform)\n .map(componentKey),\n );\n if (inspectRuntimeComponents(value, { ...options, cacheDir: cache, host }).status !== 'ready')\n runtimeError('RUNTIME_LAUNCH_CHANGED', 'Runtime changed before launch.');\n const artifact = (ref: ComponentPath) => {\n const component = value.manifest.components.find(\n (item) => item.id === ref.component && item.platform === host.platform,\n )!;\n const file = component.files.find((item) => item.path === ref.path)!;\n const filePath = cachePath(cache, `components/${componentKey(component)}/root/${ref.path}`);\n return {\n role: `runtime:${ref.component}:${ref.path}`,\n path: filePath,\n bytes: file.bytes,\n sha256: file.sha256,\n };\n };\n const program = artifact(launch.executable);\n const references = launch.argv\n .filter((arg): arg is ComponentPath => 'component' in arg)\n .map(artifact);\n const argv = launch.argv\n .map((arg) => ('literal' in arg ? arg.literal : artifact(arg).path))\n .concat([...options.argv]);\n const spec = createFoundryCommandSpec({\n executable: program.path,\n argv,\n binding: { artifacts: [program, ...references] },\n });\n const result = await executeFoundryCommandSpec(spec, {\n cwd,\n env: runtimeChildEnvironment(options.env ?? process.env, launch.environment),\n signal: options.signal,\n maxBuffer: 16 * 1024 * 1024,\n resolveArtifactPath: (file) => file,\n spawnImpl: (executable, argv, childOptions) => {\n drain = (spawnImpl ?? spawnRuntimeProcess)(executable, argv, childOptions);\n return drain;\n },\n });\n return result;\n } finally {\n await drain?.catch(() => undefined);\n if (fs.existsSync(cachePath(cache, '.runtime-cache.json')))\n await releaseRuntimeLease(cache, lease.id, lease.owner);\n }\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RuntimeFileFact } from './types.js';
|
|
2
|
+
export declare function runtimeError(code: string, message: string): never;
|
|
3
|
+
export declare function contentHash(value: unknown): string;
|
|
4
|
+
/** Hash one descriptor without loading the whole executable or following a selected symlink. */
|
|
5
|
+
export declare function hashRuntimeFile(file: string, label: string): RuntimeFileFact;
|
|
6
|
+
export declare function assertInventoryBudget(count: number, bytes: number): void;
|
|
7
|
+
export declare function listRuntimeFiles(root: string): readonly RuntimeFileFact[];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { CliError } from '../errors.js';
|
|
5
|
+
export function runtimeError(code, message) {
|
|
6
|
+
throw new CliError(message, { code, exitCode: 69 });
|
|
7
|
+
}
|
|
8
|
+
export function contentHash(value) {
|
|
9
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
10
|
+
}
|
|
11
|
+
/** Hash one descriptor without loading the whole executable or following a selected symlink. */
|
|
12
|
+
export function hashRuntimeFile(file, label) {
|
|
13
|
+
const before = fs.lstatSync(file, { bigint: true });
|
|
14
|
+
if (before.isSymbolicLink() || !before.isFile() || before.size > 512 * 1024 * 1024) {
|
|
15
|
+
runtimeError('RUNTIME_FILE_INVALID', 'Runtime files must be bounded regular files.');
|
|
16
|
+
}
|
|
17
|
+
const fd = fs.openSync(file, 'r');
|
|
18
|
+
try {
|
|
19
|
+
const opened = fs.fstatSync(fd, { bigint: true });
|
|
20
|
+
if (opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size) {
|
|
21
|
+
runtimeError('RUNTIME_FILE_CHANGED', 'Runtime file changed before it could be inspected.');
|
|
22
|
+
}
|
|
23
|
+
const hash = createHash('sha256');
|
|
24
|
+
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
25
|
+
let bytes = 0;
|
|
26
|
+
while (true) {
|
|
27
|
+
const read = fs.readSync(fd, buffer, 0, buffer.length, null);
|
|
28
|
+
if (read === 0)
|
|
29
|
+
break;
|
|
30
|
+
bytes += read;
|
|
31
|
+
if (bytes > before.size)
|
|
32
|
+
runtimeError('RUNTIME_FILE_CHANGED', 'Runtime file grew during inspection.');
|
|
33
|
+
hash.update(buffer.subarray(0, read));
|
|
34
|
+
}
|
|
35
|
+
const after = fs.fstatSync(fd, { bigint: true });
|
|
36
|
+
const selected = fs.lstatSync(file, { bigint: true });
|
|
37
|
+
if (BigInt(bytes) !== before.size ||
|
|
38
|
+
after.size !== before.size ||
|
|
39
|
+
after.mtimeNs !== before.mtimeNs ||
|
|
40
|
+
selected.dev !== before.dev ||
|
|
41
|
+
selected.ino !== before.ino ||
|
|
42
|
+
selected.isSymbolicLink() ||
|
|
43
|
+
!selected.isFile()) {
|
|
44
|
+
runtimeError('RUNTIME_FILE_CHANGED', 'Runtime file changed during inspection.');
|
|
45
|
+
}
|
|
46
|
+
return Object.freeze({ path: label, bytes, sha256: hash.digest('hex') });
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
fs.closeSync(fd);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function assertInventoryBudget(count, bytes) {
|
|
53
|
+
if (count > 50_000 || bytes > 2 * 1024 * 1024 * 1024) {
|
|
54
|
+
runtimeError('RUNTIME_INVENTORY_LIMIT', 'Runtime inventory exceeds its bounded file or byte budget.');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function listRuntimeFiles(root) {
|
|
58
|
+
const files = [
|
|
59
|
+
hashRuntimeFile(path.join(root, 'package.json'), 'package.json'),
|
|
60
|
+
];
|
|
61
|
+
let totalBytes = files[0].bytes;
|
|
62
|
+
const directoryPath = (relative) => {
|
|
63
|
+
const directory = path.join(root, relative);
|
|
64
|
+
const stat = fs.lstatSync(directory);
|
|
65
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
66
|
+
runtimeError('RUNTIME_DIRECTORY_INVALID', 'Runtime directories cannot be symlinks.');
|
|
67
|
+
return directory;
|
|
68
|
+
};
|
|
69
|
+
// The initial subtree skips this container; inspect it before following dist/src.
|
|
70
|
+
directoryPath('dist');
|
|
71
|
+
const walk = (relative) => {
|
|
72
|
+
const directory = directoryPath(relative);
|
|
73
|
+
for (const name of fs.readdirSync(directory).sort()) {
|
|
74
|
+
const item = `${relative}/${name}`;
|
|
75
|
+
const file = path.join(root, item);
|
|
76
|
+
const child = fs.lstatSync(file);
|
|
77
|
+
if (child.isDirectory())
|
|
78
|
+
walk(item);
|
|
79
|
+
else {
|
|
80
|
+
const fact = hashRuntimeFile(file, item);
|
|
81
|
+
files.push(fact);
|
|
82
|
+
totalBytes += fact.bytes;
|
|
83
|
+
assertInventoryBudget(files.length, totalBytes);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
for (const directory of ['bin', 'dist/src', 'assets'])
|
|
88
|
+
walk(directory);
|
|
89
|
+
return Object.freeze(files.sort((left, right) => (left.path < right.path ? -1 : 1)));
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../../../src/lib/runtime/files.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { CliError } from '../errors.js';\nimport type { RuntimeFileFact } from './types.js';\n\nexport function runtimeError(code: string, message: string): never {\n throw new CliError(message, { code, exitCode: 69 });\n}\n\nexport function contentHash(value: unknown): string {\n return createHash('sha256').update(JSON.stringify(value)).digest('hex');\n}\n\n/** Hash one descriptor without loading the whole executable or following a selected symlink. */\nexport function hashRuntimeFile(file: string, label: string): RuntimeFileFact {\n const before = fs.lstatSync(file, { bigint: true });\n if (before.isSymbolicLink() || !before.isFile() || before.size > 512 * 1024 * 1024) {\n runtimeError('RUNTIME_FILE_INVALID', 'Runtime files must be bounded regular files.');\n }\n const fd = fs.openSync(file, 'r');\n try {\n const opened = fs.fstatSync(fd, { bigint: true });\n if (opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size) {\n runtimeError('RUNTIME_FILE_CHANGED', 'Runtime file changed before it could be inspected.');\n }\n const hash = createHash('sha256');\n const buffer = Buffer.allocUnsafe(1024 * 1024);\n let bytes = 0;\n while (true) {\n const read = fs.readSync(fd, buffer, 0, buffer.length, null);\n if (read === 0) break;\n bytes += read;\n if (bytes > before.size)\n runtimeError('RUNTIME_FILE_CHANGED', 'Runtime file grew during inspection.');\n hash.update(buffer.subarray(0, read));\n }\n const after = fs.fstatSync(fd, { bigint: true });\n const selected = fs.lstatSync(file, { bigint: true });\n if (\n BigInt(bytes) !== before.size ||\n after.size !== before.size ||\n after.mtimeNs !== before.mtimeNs ||\n selected.dev !== before.dev ||\n selected.ino !== before.ino ||\n selected.isSymbolicLink() ||\n !selected.isFile()\n ) {\n runtimeError('RUNTIME_FILE_CHANGED', 'Runtime file changed during inspection.');\n }\n return Object.freeze({ path: label, bytes, sha256: hash.digest('hex') });\n } finally {\n fs.closeSync(fd);\n }\n}\n\nexport function assertInventoryBudget(count: number, bytes: number): void {\n if (count > 50_000 || bytes > 2 * 1024 * 1024 * 1024) {\n runtimeError(\n 'RUNTIME_INVENTORY_LIMIT',\n 'Runtime inventory exceeds its bounded file or byte budget.',\n );\n }\n}\n\nexport function listRuntimeFiles(root: string): readonly RuntimeFileFact[] {\n const files: RuntimeFileFact[] = [\n hashRuntimeFile(path.join(root, 'package.json'), 'package.json'),\n ];\n let totalBytes = files[0]!.bytes;\n const directoryPath = (relative: string): string => {\n const directory = path.join(root, relative);\n const stat = fs.lstatSync(directory);\n if (stat.isSymbolicLink() || !stat.isDirectory())\n runtimeError('RUNTIME_DIRECTORY_INVALID', 'Runtime directories cannot be symlinks.');\n return directory;\n };\n // The initial subtree skips this container; inspect it before following dist/src.\n directoryPath('dist');\n const walk = (relative: string): void => {\n const directory = directoryPath(relative);\n for (const name of fs.readdirSync(directory).sort()) {\n const item = `${relative}/${name}`;\n const file = path.join(root, item);\n const child = fs.lstatSync(file);\n if (child.isDirectory()) walk(item);\n else {\n const fact = hashRuntimeFile(file, item);\n files.push(fact);\n totalBytes += fact.bytes;\n assertInventoryBudget(files.length, totalBytes);\n }\n }\n };\n for (const directory of ['bin', 'dist/src', 'assets']) walk(directory);\n return Object.freeze(files.sort((left, right) => (left.path < right.path ? -1 : 1)));\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RuntimeHost, TrustedRuntimeManifest } from './manifest-types.js';
|
|
2
|
+
declare function compareVersions(left: string, right: string): number;
|
|
3
|
+
export declare function inspectRuntimeHost(): RuntimeHost;
|
|
4
|
+
export declare function assertRuntimeHost(value: TrustedRuntimeManifest, host: RuntimeHost): void;
|
|
5
|
+
export declare const runtimeHostInternals: {
|
|
6
|
+
compareVersions: typeof compareVersions;
|
|
7
|
+
};
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { runtimePlatform } from './descriptor.js';
|
|
3
|
+
import { assertTrustedManifest } from './manifest.js';
|
|
4
|
+
import { runtimeError } from './files.js';
|
|
5
|
+
function compareVersions(left, right) {
|
|
6
|
+
const a = left.split(/[.-]/u).slice(0, 3).map(Number), b = right.split('.').map(Number);
|
|
7
|
+
if (a.some((value) => !Number.isSafeInteger(value) || value < 0))
|
|
8
|
+
return -1;
|
|
9
|
+
for (let i = 0; i < 3; i++) {
|
|
10
|
+
const delta = (a[i] ?? 0) - (b[i] ?? 0);
|
|
11
|
+
if (delta !== 0)
|
|
12
|
+
return delta;
|
|
13
|
+
}
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
export function inspectRuntimeHost() {
|
|
17
|
+
const platform = runtimePlatform(process.platform, process.arch);
|
|
18
|
+
let glibc = null;
|
|
19
|
+
if (process.platform === 'linux') {
|
|
20
|
+
// Project only the ABI header; never serialize a diagnostic report or its environment.
|
|
21
|
+
const report = process.report.getReport();
|
|
22
|
+
if (typeof report.header?.glibcVersionRuntime === 'string')
|
|
23
|
+
glibc = report.header.glibcVersionRuntime;
|
|
24
|
+
}
|
|
25
|
+
return Object.freeze({ platform, osRelease: os.release(), glibc });
|
|
26
|
+
}
|
|
27
|
+
export function assertRuntimeHost(value, host) {
|
|
28
|
+
assertTrustedManifest(value);
|
|
29
|
+
const minimum = value.manifest.minimum_hosts[host.platform];
|
|
30
|
+
if (!minimum ||
|
|
31
|
+
compareVersions(host.osRelease, minimum.os_release) < 0 ||
|
|
32
|
+
(host.platform.startsWith('linux-') &&
|
|
33
|
+
(!host.glibc || !minimum.glibc || compareVersions(host.glibc, minimum.glibc) < 0)))
|
|
34
|
+
runtimeError('RUNTIME_HOST_UNSUPPORTED', 'The selected release does not support this host OS/architecture/ABI.');
|
|
35
|
+
}
|
|
36
|
+
export const runtimeHostInternals = { compareVersions };
|
|
37
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../../../src/lib/runtime/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,SAAS,eAAe,CAAC,IAAY,EAAE,KAAa;IAClD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EACnD,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC;IAC5E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AACD,MAAM,UAAU,kBAAkB;IAChC,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,uFAAuF;QACvF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAoD,CAAC;QAC5F,IAAI,OAAO,MAAM,CAAC,MAAM,EAAE,mBAAmB,KAAK,QAAQ;YACxD,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AACrE,CAAC;AACD,MAAM,UAAU,iBAAiB,CAAC,KAA6B,EAAE,IAAiB;IAChF,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,IACE,CAAC,OAAO;QACR,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QACvD,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;YACjC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAEpF,YAAY,CACV,0BAA0B,EAC1B,sEAAsE,CACvE,CAAC;AACN,CAAC;AACD,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,eAAe,EAAE,CAAC","sourcesContent":["import os from 'node:os';\nimport { runtimePlatform } from './descriptor.js';\nimport { assertTrustedManifest } from './manifest.js';\nimport { runtimeError } from './files.js';\nimport type { RuntimeHost, TrustedRuntimeManifest } from './manifest-types.js';\n\nfunction compareVersions(left: string, right: string): number {\n const a = left.split(/[.-]/u).slice(0, 3).map(Number),\n b = right.split('.').map(Number);\n if (a.some((value) => !Number.isSafeInteger(value) || value < 0)) return -1;\n for (let i = 0; i < 3; i++) {\n const delta = (a[i] ?? 0) - (b[i] ?? 0);\n if (delta !== 0) return delta;\n }\n return 0;\n}\nexport function inspectRuntimeHost(): RuntimeHost {\n const platform = runtimePlatform(process.platform, process.arch);\n let glibc: string | null = null;\n if (process.platform === 'linux') {\n // Project only the ABI header; never serialize a diagnostic report or its environment.\n const report = process.report.getReport() as { header?: { glibcVersionRuntime?: unknown } };\n if (typeof report.header?.glibcVersionRuntime === 'string')\n glibc = report.header.glibcVersionRuntime;\n }\n return Object.freeze({ platform, osRelease: os.release(), glibc });\n}\nexport function assertRuntimeHost(value: TrustedRuntimeManifest, host: RuntimeHost): void {\n assertTrustedManifest(value);\n const minimum = value.manifest.minimum_hosts[host.platform];\n if (\n !minimum ||\n compareVersions(host.osRelease, minimum.os_release) < 0 ||\n (host.platform.startsWith('linux-') &&\n (!host.glibc || !minimum.glibc || compareVersions(host.glibc, minimum.glibc) < 0))\n )\n runtimeError(\n 'RUNTIME_HOST_UNSUPPORTED',\n 'The selected release does not support this host OS/architecture/ABI.',\n );\n}\nexport const runtimeHostInternals = { compareVersions };\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type RuntimeLease = Readonly<{
|
|
2
|
+
schema: 'tiangong-lca.runtime-lease.v1';
|
|
3
|
+
id: string;
|
|
4
|
+
owner: string;
|
|
5
|
+
components: readonly string[];
|
|
6
|
+
}>;
|
|
7
|
+
export declare function runtimeLeaseKey(id: string): string;
|
|
8
|
+
export declare function withRuntimeLeaseLock<T>(root: string, operation: () => Promise<T> | T): Promise<T>;
|
|
9
|
+
export declare function readRuntimeLease(root: string, key: string): RuntimeLease;
|
|
10
|
+
export declare function acquireRuntimeLease(root: string, id: string, owner: string, components: readonly string[]): Promise<RuntimeLease>;
|
|
11
|
+
export declare function releaseRuntimeLease(root: string, id: string, owner: string): Promise<boolean>;
|
|
12
|
+
export declare function leasedRuntimeKeys(root: string): Set<string>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { withBatchRunLock } from '../../batch.js';
|
|
3
|
+
import { contentHash, runtimeError } from './files.js';
|
|
4
|
+
import { array, exact, record, sha, text, unique } from './manifest-values.js';
|
|
5
|
+
import { cachePath, readCacheJson, writeOnce } from './storage.js';
|
|
6
|
+
export function runtimeLeaseKey(id) {
|
|
7
|
+
return contentHash(text(id, 'lease id', 256));
|
|
8
|
+
}
|
|
9
|
+
export async function withRuntimeLeaseLock(root, operation) {
|
|
10
|
+
return withBatchRunLock({
|
|
11
|
+
runPath: cachePath(root, 'locks/leases.json'),
|
|
12
|
+
identity: { schema: 'runtime-lease-lock.v1' },
|
|
13
|
+
reason: 'Runtime component lease mutation',
|
|
14
|
+
}, operation);
|
|
15
|
+
}
|
|
16
|
+
export function readRuntimeLease(root, key) {
|
|
17
|
+
sha(key);
|
|
18
|
+
const value = record(readCacheJson(root, `leases/${key}.json`), 'lease');
|
|
19
|
+
exact(value, ['schema', 'id', 'owner', 'components'], 'lease');
|
|
20
|
+
if (value.schema !== 'tiangong-lca.runtime-lease.v1')
|
|
21
|
+
runtimeError('RUNTIME_LEASE_INVALID', 'Unknown runtime lease schema.');
|
|
22
|
+
const id = text(value.id, 'lease id', 256), owner = text(value.owner, 'lease owner', 4096), components = array(value.components, 128, 1).map(sha);
|
|
23
|
+
unique(components, 'lease component');
|
|
24
|
+
if (runtimeLeaseKey(id) !== key)
|
|
25
|
+
runtimeError('RUNTIME_LEASE_INVALID', 'Runtime lease identity changed.');
|
|
26
|
+
return { schema: 'tiangong-lca.runtime-lease.v1', id, owner, components };
|
|
27
|
+
}
|
|
28
|
+
export async function acquireRuntimeLease(root, id, owner, components) {
|
|
29
|
+
const lease = {
|
|
30
|
+
schema: 'tiangong-lca.runtime-lease.v1',
|
|
31
|
+
id: text(id, 'lease id', 256),
|
|
32
|
+
owner: text(owner, 'lease owner', 4096),
|
|
33
|
+
components: [...components].sort(),
|
|
34
|
+
};
|
|
35
|
+
array(components, 128, 1).forEach(sha);
|
|
36
|
+
unique(components, 'lease component');
|
|
37
|
+
return withRuntimeLeaseLock(root, () => {
|
|
38
|
+
const key = runtimeLeaseKey(id), file = cachePath(root, `leases/${key}.json`);
|
|
39
|
+
if (fs.existsSync(file) && contentHash(readRuntimeLease(root, key)) !== contentHash(lease))
|
|
40
|
+
runtimeError('RUNTIME_LEASE_CONFLICT', 'Existing lease pins another owner or component set; release it explicitly before replacement.');
|
|
41
|
+
writeOnce(root, `leases/${key}.json`, Buffer.from(JSON.stringify(lease) + '\n'));
|
|
42
|
+
return Object.freeze(lease);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export async function releaseRuntimeLease(root, id, owner) {
|
|
46
|
+
return withRuntimeLeaseLock(root, () => {
|
|
47
|
+
const key = runtimeLeaseKey(id), file = cachePath(root, `leases/${key}.json`);
|
|
48
|
+
if (!fs.existsSync(file))
|
|
49
|
+
return false;
|
|
50
|
+
if (readRuntimeLease(root, key).owner !== owner)
|
|
51
|
+
runtimeError('RUNTIME_LEASE_OWNER', 'Only the same explicit lease owner can release a runtime pin.');
|
|
52
|
+
fs.unlinkSync(file);
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function leasedRuntimeKeys(root) {
|
|
57
|
+
const directory = cachePath(root, 'leases');
|
|
58
|
+
if (!fs.existsSync(directory))
|
|
59
|
+
return new Set();
|
|
60
|
+
const names = fs.readdirSync(directory);
|
|
61
|
+
if (names.length > 10_000)
|
|
62
|
+
runtimeError('RUNTIME_LEASE_LIMIT', 'Lease inventory exceeds its bound.');
|
|
63
|
+
const keys = new Set();
|
|
64
|
+
for (const name of names) {
|
|
65
|
+
if (!/^[0-9a-f]{64}\.json$/u.test(name))
|
|
66
|
+
runtimeError('RUNTIME_LEASE_INVALID', 'Unknown lease record prevents cache pruning.');
|
|
67
|
+
for (const key of readRuntimeLease(root, name.slice(0, -5)).components)
|
|
68
|
+
keys.add(key);
|
|
69
|
+
}
|
|
70
|
+
return keys;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=leases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leases.js","sourceRoot":"","sources":["../../../../src/lib/runtime/leases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQnE,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAY,EACZ,SAA+B;IAE/B,OAAO,gBAAgB,CACrB;QACE,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,mBAAmB,CAAC;QAC7C,QAAQ,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE;QAC7C,MAAM,EAAE,kCAAkC;KAC3C,EACD,SAAS,CACV,CAAC;AACJ,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,GAAW;IACxD,GAAG,CAAC,GAAG,CAAC,CAAC;IACT,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IACzE,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,+BAA+B;QAClD,YAAY,CAAC,uBAAuB,EAAE,+BAA+B,CAAC,CAAC;IACzE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,EACxC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC,EAC9C,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxD,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;IACtC,IAAI,eAAe,CAAC,EAAE,CAAC,KAAK,GAAG;QAC7B,YAAY,CAAC,uBAAuB,EAAE,iCAAiC,CAAC,CAAC;IAC3E,OAAO,EAAE,MAAM,EAAE,+BAA+B,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AAC5E,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAY,EACZ,EAAU,EACV,KAAa,EACb,UAA6B;IAE7B,MAAM,KAAK,GAAiB;QAC1B,MAAM,EAAE,+BAA+B;QACvC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC;QAC7B,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC;QACvC,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE;KACnC,CAAC;IACF,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;IACtC,OAAO,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE;QACrC,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,CAAC,EAC7B,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC;QAC/C,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,WAAW,CAAC,KAAK,CAAC;YACxF,YAAY,CACV,wBAAwB,EACxB,+FAA+F,CAChG,CAAC;QACJ,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACjF,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAY,EACZ,EAAU,EACV,KAAa;IAEb,OAAO,oBAAoB,CAAC,IAAI,EAAE,GAAG,EAAE;QACrC,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,CAAC,EAC7B,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QACvC,IAAI,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK;YAC7C,YAAY,CACV,qBAAqB,EACrB,+DAA+D,CAChE,CAAC;QACJ,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AACD,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,GAAG,EAAE,CAAC;IAChD,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM;QACvB,YAAY,CAAC,qBAAqB,EAAE,oCAAoC,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC;YACrC,YAAY,CAAC,uBAAuB,EAAE,8CAA8C,CAAC,CAAC;QACxF,KAAK,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import fs from 'node:fs';\nimport { withBatchRunLock } from '../../batch.js';\nimport { contentHash, runtimeError } from './files.js';\nimport { array, exact, record, sha, text, unique } from './manifest-values.js';\nimport { cachePath, readCacheJson, writeOnce } from './storage.js';\n\nexport type RuntimeLease = Readonly<{\n schema: 'tiangong-lca.runtime-lease.v1';\n id: string;\n owner: string;\n components: readonly string[];\n}>;\nexport function runtimeLeaseKey(id: string): string {\n return contentHash(text(id, 'lease id', 256));\n}\nexport async function withRuntimeLeaseLock<T>(\n root: string,\n operation: () => Promise<T> | T,\n): Promise<T> {\n return withBatchRunLock(\n {\n runPath: cachePath(root, 'locks/leases.json'),\n identity: { schema: 'runtime-lease-lock.v1' },\n reason: 'Runtime component lease mutation',\n },\n operation,\n );\n}\nexport function readRuntimeLease(root: string, key: string): RuntimeLease {\n sha(key);\n const value = record(readCacheJson(root, `leases/${key}.json`), 'lease');\n exact(value, ['schema', 'id', 'owner', 'components'], 'lease');\n if (value.schema !== 'tiangong-lca.runtime-lease.v1')\n runtimeError('RUNTIME_LEASE_INVALID', 'Unknown runtime lease schema.');\n const id = text(value.id, 'lease id', 256),\n owner = text(value.owner, 'lease owner', 4096),\n components = array(value.components, 128, 1).map(sha);\n unique(components, 'lease component');\n if (runtimeLeaseKey(id) !== key)\n runtimeError('RUNTIME_LEASE_INVALID', 'Runtime lease identity changed.');\n return { schema: 'tiangong-lca.runtime-lease.v1', id, owner, components };\n}\nexport async function acquireRuntimeLease(\n root: string,\n id: string,\n owner: string,\n components: readonly string[],\n): Promise<RuntimeLease> {\n const lease: RuntimeLease = {\n schema: 'tiangong-lca.runtime-lease.v1',\n id: text(id, 'lease id', 256),\n owner: text(owner, 'lease owner', 4096),\n components: [...components].sort(),\n };\n array(components, 128, 1).forEach(sha);\n unique(components, 'lease component');\n return withRuntimeLeaseLock(root, () => {\n const key = runtimeLeaseKey(id),\n file = cachePath(root, `leases/${key}.json`);\n if (fs.existsSync(file) && contentHash(readRuntimeLease(root, key)) !== contentHash(lease))\n runtimeError(\n 'RUNTIME_LEASE_CONFLICT',\n 'Existing lease pins another owner or component set; release it explicitly before replacement.',\n );\n writeOnce(root, `leases/${key}.json`, Buffer.from(JSON.stringify(lease) + '\\n'));\n return Object.freeze(lease);\n });\n}\nexport async function releaseRuntimeLease(\n root: string,\n id: string,\n owner: string,\n): Promise<boolean> {\n return withRuntimeLeaseLock(root, () => {\n const key = runtimeLeaseKey(id),\n file = cachePath(root, `leases/${key}.json`);\n if (!fs.existsSync(file)) return false;\n if (readRuntimeLease(root, key).owner !== owner)\n runtimeError(\n 'RUNTIME_LEASE_OWNER',\n 'Only the same explicit lease owner can release a runtime pin.',\n );\n fs.unlinkSync(file);\n return true;\n });\n}\nexport function leasedRuntimeKeys(root: string): Set<string> {\n const directory = cachePath(root, 'leases');\n if (!fs.existsSync(directory)) return new Set();\n const names = fs.readdirSync(directory);\n if (names.length > 10_000)\n runtimeError('RUNTIME_LEASE_LIMIT', 'Lease inventory exceeds its bound.');\n const keys = new Set<string>();\n for (const name of names) {\n if (!/^[0-9a-f]{64}\\.json$/u.test(name))\n runtimeError('RUNTIME_LEASE_INVALID', 'Unknown lease record prevents cache pruning.');\n for (const key of readRuntimeLease(root, name.slice(0, -5)).components) keys.add(key);\n }\n return keys;\n}\n"]}
|