maka-agent 0.2.0-dev.4.20260830 → 0.2.0-dev.6.20260830
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/dist/eval-bundle-path.js +1 -1
- package/dist/pi-tui-runner.js +3 -1
- package/dist/runtime-host-onboarding.js +10 -1
- package/dist/runtime-host-tui-context.js +2 -6
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/node_modules/@maka/eval/dist/external-subject.js +1 -1
- package/node_modules/@maka/eval/dist/harbor-external-subject.js +2 -2
- package/package.json +1 -2
- package/packages/eval/dist/attempt-store.js +0 -138
- package/packages/eval/dist/cli.js +0 -119
- package/packages/eval/dist/experiment-directory.js +0 -59
- package/packages/eval/dist/experiment.js +0 -49
- package/packages/eval/dist/external-subject.js +0 -488
- package/packages/eval/dist/harbor-external-subject.js +0 -929
- package/packages/eval/dist/harbor-maka-subject.js +0 -83
- package/packages/eval/dist/harness-environment.js +0 -80
- package/packages/eval/dist/harness-executor.js +0 -1013
- package/packages/eval/dist/index.js +0 -28
- package/packages/eval/dist/install-preflight.js +0 -201
- package/packages/eval/dist/maka-artifacts.js +0 -126
- package/packages/eval/dist/maka-runtime-policy.js +0 -42
- package/packages/eval/dist/maka-subject.js +0 -268
- package/packages/eval/dist/metering-checkpoint.js +0 -80
- package/packages/eval/dist/provider-admission.js +0 -39
- package/packages/eval/dist/provider-metering.js +0 -67
- package/packages/eval/dist/provider-web-tool-surface.js +0 -74
- package/packages/eval/dist/relay-result-frame.js +0 -38
- package/packages/eval/dist/result.js +0 -94
- package/packages/eval/dist/runner.js +0 -290
- package/packages/eval/dist/spec.js +0 -135
- package/packages/eval/dist/toolchain-verification.js +0 -130
- package/packages/eval/harbor/deepseek-codex-models.json +0 -69
- package/packages/eval/harbor/deepseek-harness-profile/cordis.patch.yml +0 -140
- package/packages/eval/harbor/deepseek-harness-profile/cordis.yml +0 -18
- package/packages/eval/harbor/deepseek-harness-profile/package.json +0 -10
- package/packages/eval/harbor/docker-compose-egress-proxy.yaml +0 -57
- package/packages/eval/harbor/egress-proxy/Dockerfile +0 -25
- package/packages/eval/harbor/egress-proxy/entrypoint.sh +0 -62
- package/packages/eval/harbor/egress-proxy/network-policy +0 -148
- package/packages/eval/harbor/egress_filter.py +0 -435
- package/packages/eval/harbor/eval_framework.py +0 -40
- package/packages/eval/harbor/relay_agent.py +0 -686
- package/packages/eval/harbor/run_trial.py +0 -223
- package/packages/eval/package.json +0 -20
package/dist/eval-bundle-path.js
CHANGED
|
@@ -23,7 +23,7 @@ export function configureInstalledEvalBundle(environment = process.env, packageR
|
|
|
23
23
|
if (Object.hasOwn(environment, MAKA_EVAL_BUNDLE_ENV))
|
|
24
24
|
return;
|
|
25
25
|
try {
|
|
26
|
-
if (!statSync(resolve(packageRoot, '
|
|
26
|
+
if (!statSync(resolve(packageRoot, 'node_modules/@maka/eval')).isDirectory())
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
catch {
|
package/dist/pi-tui-runner.js
CHANGED
|
@@ -870,6 +870,7 @@ export async function runMakaPiTui(input) {
|
|
|
870
870
|
// in place after `/setup` saves so newly configured models are immediately
|
|
871
871
|
// available — the single source the picker and connection/model lookups read.
|
|
872
872
|
let modelChoices = input.modelChoices;
|
|
873
|
+
let connectionIdentities = input.connectionIdentities;
|
|
873
874
|
// Monotonic attempt id: each setup submit captures one, and any transition
|
|
874
875
|
// that abandons the in-flight attempt (back, re-pick, close) increments it so
|
|
875
876
|
// a late verify/save settlement cannot clobber a newer attempt.
|
|
@@ -1142,7 +1143,7 @@ export async function runMakaPiTui(input) {
|
|
|
1142
1143
|
model = summary.model;
|
|
1143
1144
|
connectionId = summary.llmConnectionId;
|
|
1144
1145
|
connectionSlug = summary.llmConnectionSlug;
|
|
1145
|
-
const identityNotice = sessionConnectionIdentityNotice(summary,
|
|
1146
|
+
const identityNotice = sessionConnectionIdentityNotice(summary, connectionIdentities, input.locale ?? 'en');
|
|
1146
1147
|
if (announceIdentity && identityNotice && identityNotice !== connectionIdentityNotice) {
|
|
1147
1148
|
state.entries.push({ kind: 'notice', level: 'error', text: identityNotice });
|
|
1148
1149
|
}
|
|
@@ -1832,6 +1833,7 @@ export async function runMakaPiTui(input) {
|
|
|
1832
1833
|
// attempt must not overwrite choices refreshed by a newer save.
|
|
1833
1834
|
if (result.refresh.kind === 'ok') {
|
|
1834
1835
|
modelChoices = result.refresh.modelChoices;
|
|
1836
|
+
connectionIdentities = result.refresh.connectionIdentities;
|
|
1835
1837
|
}
|
|
1836
1838
|
if (input.firstRun) {
|
|
1837
1839
|
beginClose();
|
|
@@ -50,12 +50,14 @@ export function createRuntimeHostOnboardingSurface(connection) {
|
|
|
50
50
|
return result;
|
|
51
51
|
}
|
|
52
52
|
try {
|
|
53
|
+
const catalog = await readRuntimeHostConnectionCatalog(connection);
|
|
53
54
|
return {
|
|
54
55
|
kind: 'ok',
|
|
55
56
|
connection: result.connection,
|
|
56
57
|
refresh: {
|
|
57
58
|
kind: 'ok',
|
|
58
|
-
modelChoices: projectRuntimeHostModelChoices(
|
|
59
|
+
modelChoices: projectRuntimeHostModelChoices(catalog),
|
|
60
|
+
connectionIdentities: projectRuntimeHostConnectionIdentities(catalog),
|
|
59
61
|
},
|
|
60
62
|
};
|
|
61
63
|
}
|
|
@@ -108,6 +110,13 @@ export function projectRuntimeHostModelChoices(catalog) {
|
|
|
108
110
|
}
|
|
109
111
|
return choices;
|
|
110
112
|
}
|
|
113
|
+
export function projectRuntimeHostConnectionIdentities(catalog) {
|
|
114
|
+
return catalog.connections.map((connection) => ({
|
|
115
|
+
connectionId: connection.connectionId,
|
|
116
|
+
connectionSlug: connection.slug,
|
|
117
|
+
enabled: connection.enabled,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
111
120
|
export function projectProviders(catalog) {
|
|
112
121
|
const entries = [];
|
|
113
122
|
for (const provider of listApiKeyOnboardableProviders()) {
|
|
@@ -26,7 +26,7 @@ import { readRuntimeHostAgentGraphEpochs, readRuntimeHostInvocableSkills, readRu
|
|
|
26
26
|
import { runtimeHostProfileUsesHostWorkspace } from '@maka/runtime-host/profile-kind';
|
|
27
27
|
import { connectRuntimeHostCli, readHostChatDefaultPermissionMode, resolveRuntimeHostCliTarget, } from './runtime-host-cli-context.js';
|
|
28
28
|
import { createRuntimeHostMakaSessionDriver, } from './runtime-host-session-driver.js';
|
|
29
|
-
import { createRuntimeHostOnboardingSurface, projectRuntimeHostModelChoices, } from './runtime-host-onboarding.js';
|
|
29
|
+
import { createRuntimeHostOnboardingSurface, projectRuntimeHostConnectionIdentities, projectRuntimeHostModelChoices, } from './runtime-host-onboarding.js';
|
|
30
30
|
import { createTuiMcpController, } from './tui-mcp-control.js';
|
|
31
31
|
export async function createRuntimeHostTuiContext(input) {
|
|
32
32
|
const connected = await connectRuntimeHostCli({
|
|
@@ -88,11 +88,7 @@ export async function createRuntimeHostTuiContext(input) {
|
|
|
88
88
|
...(selectedTarget.connectionId === undefined
|
|
89
89
|
? {}
|
|
90
90
|
: { connectionId: selectedTarget.connectionId }),
|
|
91
|
-
connectionIdentities: catalog
|
|
92
|
-
connectionId: entry.connectionId,
|
|
93
|
-
connectionSlug: entry.slug,
|
|
94
|
-
enabled: entry.enabled,
|
|
95
|
-
})),
|
|
91
|
+
connectionIdentities: projectRuntimeHostConnectionIdentities(catalog),
|
|
96
92
|
connectionName: selectedTarget.connection?.name ?? selectedTarget.connectionSlug,
|
|
97
93
|
...(selectedTarget.connection
|
|
98
94
|
? { providerType: selectedTarget.connection.providerType }
|
|
Binary file
|
|
@@ -21,7 +21,7 @@ import { join } from 'node:path';
|
|
|
21
21
|
import { meteringCheckpointMacMatches, readBoundedRegularFile } from './metering-checkpoint.js';
|
|
22
22
|
import { deriveMetering } from './provider-metering.js';
|
|
23
23
|
import { TOOLCHAIN_IDENTITIES, TOOLCHAIN_IDENTITY_ENV, isExternalProfile, verifyToolchainDirectory, } from './toolchain-verification.js';
|
|
24
|
-
const BUNDLED_EXTERNAL_WRAPPER = '/opt/maka-agent/
|
|
24
|
+
const BUNDLED_EXTERNAL_WRAPPER = '/opt/maka-agent/node_modules/@maka/eval/dist/harbor-external-subject.js';
|
|
25
25
|
export function createExternalSubjectAdapter() {
|
|
26
26
|
const verifiedToolchains = new Map();
|
|
27
27
|
return {
|
|
@@ -44,7 +44,7 @@ const PROFILE_PREPARERS = {
|
|
|
44
44
|
codex: async ({ env, home, root, proxyBaseUrl }) => {
|
|
45
45
|
env.OPENAI_API_KEY = 'maka-eval-local';
|
|
46
46
|
env.CODEX_HOME = home;
|
|
47
|
-
const catalog = rooted(root, '/opt/maka-agent/
|
|
47
|
+
const catalog = rooted(root, '/opt/maka-agent/node_modules/@maka/eval/harbor/deepseek-codex-models.json');
|
|
48
48
|
await writeFile(join(home, 'config.toml'), [
|
|
49
49
|
'model_provider = "maka-http"',
|
|
50
50
|
`model_catalog_json = ${JSON.stringify(catalog)}`,
|
|
@@ -198,7 +198,7 @@ const PROFILE_PREPARERS = {
|
|
|
198
198
|
env.DSH_HOME = join(home, 'dsh');
|
|
199
199
|
const profile = join(env.DSH_HOME, 'profiles', DEEPSEEK_HARNESS_PROFILE);
|
|
200
200
|
await mkdir(profile, { recursive: true, mode: 0o700 });
|
|
201
|
-
const source = rooted(root, '/opt/maka-agent/
|
|
201
|
+
const source = rooted(root, '/opt/maka-agent/node_modules/@maka/eval/harbor/deepseek-harness-profile');
|
|
202
202
|
for (const file of ['package.json', 'cordis.yml', 'cordis.patch.yml']) {
|
|
203
203
|
await copyFile(join(source, file), join(profile, file));
|
|
204
204
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maka-agent",
|
|
3
|
-
"version": "0.2.0-dev.
|
|
3
|
+
"version": "0.2.0-dev.6.20260830",
|
|
4
4
|
"description": "Apache Maka (Incubating) developer snapshot; not an Apache release.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"dist",
|
|
41
41
|
"native",
|
|
42
|
-
"packages/eval",
|
|
43
42
|
"README.md",
|
|
44
43
|
"README.zh-CN.md",
|
|
45
44
|
"LICENSE",
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
* or more contributor license agreements. See the NOTICE file
|
|
4
|
-
* distributed with this work for additional information
|
|
5
|
-
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
* to you under the Apache License, Version 2.0 (the
|
|
7
|
-
* "License"); you may not use this file except in compliance
|
|
8
|
-
* with the License. You may obtain a copy of the License at
|
|
9
|
-
*
|
|
10
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
*
|
|
12
|
-
* Unless required by applicable law or agreed to in writing,
|
|
13
|
-
* software distributed under the License is distributed on an
|
|
14
|
-
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
* KIND, either express or implied. See the License for the
|
|
16
|
-
* specific language governing permissions and limitations
|
|
17
|
-
* under the License.
|
|
18
|
-
*/
|
|
19
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
20
|
-
import { link, mkdir, open, readFile, readdir, unlink } from 'node:fs/promises';
|
|
21
|
-
import { join } from 'node:path';
|
|
22
|
-
import { decodeEvalResult } from './result.js';
|
|
23
|
-
export class FileAttemptStore {
|
|
24
|
-
path;
|
|
25
|
-
constructor(path) {
|
|
26
|
-
this.path = path;
|
|
27
|
-
}
|
|
28
|
-
async runExclusive(operation) {
|
|
29
|
-
await mkdir(this.path, { recursive: true });
|
|
30
|
-
const path = join(this.path, '.writer.lock');
|
|
31
|
-
let lock;
|
|
32
|
-
try {
|
|
33
|
-
lock = await open(path, 'wx', 0o600);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
if (error.code === 'EEXIST') {
|
|
37
|
-
throw new Error(`${path}: experiment already has an active writer`);
|
|
38
|
-
}
|
|
39
|
-
throw error;
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
return await operation();
|
|
43
|
-
}
|
|
44
|
-
finally {
|
|
45
|
-
await lock.close();
|
|
46
|
-
await unlink(path);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
async list(cellId) {
|
|
50
|
-
const directory = this.#cellDirectory(cellId);
|
|
51
|
-
let names;
|
|
52
|
-
try {
|
|
53
|
-
names = await readdir(directory);
|
|
54
|
-
}
|
|
55
|
-
catch (error) {
|
|
56
|
-
if (error.code === 'ENOENT')
|
|
57
|
-
return [];
|
|
58
|
-
throw error;
|
|
59
|
-
}
|
|
60
|
-
const attempts = await Promise.all(names
|
|
61
|
-
.filter((name) => /^\d{6}\.json$/u.test(name))
|
|
62
|
-
.sort()
|
|
63
|
-
.map(async (name) => {
|
|
64
|
-
const attempt = decodeAttempt(JSON.parse(await readFile(join(directory, name), 'utf8')));
|
|
65
|
-
const sequence = Number(name.slice(0, 6));
|
|
66
|
-
if (attempt.cellId !== cellId || attempt.sequence !== sequence) {
|
|
67
|
-
throw new Error('attempt file does not match its immutable identity');
|
|
68
|
-
}
|
|
69
|
-
return attempt;
|
|
70
|
-
}));
|
|
71
|
-
return attempts;
|
|
72
|
-
}
|
|
73
|
-
async append(attempt) {
|
|
74
|
-
const record = decodeAttempt(attempt);
|
|
75
|
-
const expected = ((await this.list(record.cellId)).at(-1)?.sequence ?? 0) + 1;
|
|
76
|
-
if (record.sequence !== expected)
|
|
77
|
-
throw new Error(`attempt sequence must be ${expected}`);
|
|
78
|
-
const directory = this.#cellDirectory(record.cellId);
|
|
79
|
-
await mkdir(directory, { recursive: true });
|
|
80
|
-
const finalPath = join(directory, `${String(record.sequence).padStart(6, '0')}.json`);
|
|
81
|
-
const temporaryPath = `${finalPath}.${randomUUID()}.tmp`;
|
|
82
|
-
const temporary = await open(temporaryPath, 'wx', 0o600);
|
|
83
|
-
try {
|
|
84
|
-
await temporary.writeFile(`${JSON.stringify(record)}\n`);
|
|
85
|
-
await temporary.sync();
|
|
86
|
-
await temporary.close();
|
|
87
|
-
await link(temporaryPath, finalPath);
|
|
88
|
-
}
|
|
89
|
-
finally {
|
|
90
|
-
await temporary.close().catch(() => undefined);
|
|
91
|
-
await unlink(temporaryPath).catch(() => undefined);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
#cellDirectory(cellId) {
|
|
95
|
-
return join(this.path, createHash('sha256').update(cellId).digest('hex'));
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function decodeAttempt(value) {
|
|
99
|
-
const attempt = exact(value, 'attempt', [
|
|
100
|
-
'cellId',
|
|
101
|
-
'sequence',
|
|
102
|
-
'startedAt',
|
|
103
|
-
'completedAt',
|
|
104
|
-
'result',
|
|
105
|
-
]);
|
|
106
|
-
return {
|
|
107
|
-
cellId: nonempty(attempt.cellId, 'attempt.cellId'),
|
|
108
|
-
sequence: positiveInteger(attempt.sequence, 'attempt.sequence'),
|
|
109
|
-
startedAt: nonnegative(attempt.startedAt, 'attempt.startedAt'),
|
|
110
|
-
completedAt: nonnegative(attempt.completedAt, 'attempt.completedAt'),
|
|
111
|
-
result: decodeEvalResult(attempt.result, 'attempt.result'),
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function exact(value, where, fields) {
|
|
115
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
116
|
-
throw new Error(`${where} must be an object`);
|
|
117
|
-
const record = value;
|
|
118
|
-
if (Object.keys(record).length !== fields.length ||
|
|
119
|
-
fields.some((field) => !Object.hasOwn(record, field))) {
|
|
120
|
-
throw new Error(`${where} fields are invalid`);
|
|
121
|
-
}
|
|
122
|
-
return record;
|
|
123
|
-
}
|
|
124
|
-
function nonempty(value, where) {
|
|
125
|
-
if (typeof value !== 'string' || value.length === 0)
|
|
126
|
-
throw new Error(`${where} is invalid`);
|
|
127
|
-
return value;
|
|
128
|
-
}
|
|
129
|
-
function positiveInteger(value, where) {
|
|
130
|
-
if (!Number.isSafeInteger(value) || value < 1)
|
|
131
|
-
throw new Error(`${where} is invalid`);
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
function nonnegative(value, where) {
|
|
135
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0)
|
|
136
|
-
throw new Error(`${where} is invalid`);
|
|
137
|
-
return value;
|
|
138
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
* or more contributor license agreements. See the NOTICE file
|
|
4
|
-
* distributed with this work for additional information
|
|
5
|
-
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
* to you under the Apache License, Version 2.0 (the
|
|
7
|
-
* "License"); you may not use this file except in compliance
|
|
8
|
-
* with the License. You may obtain a copy of the License at
|
|
9
|
-
*
|
|
10
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
*
|
|
12
|
-
* Unless required by applicable law or agreed to in writing,
|
|
13
|
-
* software distributed under the License is distributed on an
|
|
14
|
-
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
* KIND, either express or implied. See the License for the
|
|
16
|
-
* specific language governing permissions and limitations
|
|
17
|
-
* under the License.
|
|
18
|
-
*/
|
|
19
|
-
import { readFile } from 'node:fs/promises';
|
|
20
|
-
import { resolve } from 'node:path';
|
|
21
|
-
import { createExternalSubjectAdapter } from './external-subject.js';
|
|
22
|
-
import { createHarborExecutor, createPierExecutor, } from './harness-executor.js';
|
|
23
|
-
import { openExperimentDirectory } from './experiment-directory.js';
|
|
24
|
-
import { createMakaSubjectAdapter } from './maka-subject.js';
|
|
25
|
-
import { runExperiment } from './runner.js';
|
|
26
|
-
import { parseExperimentSpec } from './spec.js';
|
|
27
|
-
const USAGE = 'usage: maka eval run <spec.json> --out <directory> [--cell <cell-id>]';
|
|
28
|
-
export async function runMakaEvalCli(argv, overrides = {}) {
|
|
29
|
-
const controller = overrides.signal ? undefined : new AbortController();
|
|
30
|
-
const signal = overrides.signal ?? controller?.signal;
|
|
31
|
-
let interrupted;
|
|
32
|
-
const interrupt = () => {
|
|
33
|
-
interrupted = 130;
|
|
34
|
-
controller?.abort();
|
|
35
|
-
};
|
|
36
|
-
const terminate = () => {
|
|
37
|
-
interrupted = 143;
|
|
38
|
-
controller?.abort();
|
|
39
|
-
};
|
|
40
|
-
if (controller) {
|
|
41
|
-
process.once('SIGINT', interrupt);
|
|
42
|
-
process.once('SIGTERM', terminate);
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const command = parseArgs(argv);
|
|
46
|
-
if (command.kind === 'help') {
|
|
47
|
-
(overrides.stdout ?? process.stdout.write.bind(process.stdout))(`${USAGE}\n`);
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
const specPath = resolve(command.specPath);
|
|
51
|
-
const spec = parseExperimentSpec(JSON.parse(await readFile(specPath, 'utf8')));
|
|
52
|
-
const directory = await openExperimentDirectory(resolve(command.outDir), spec);
|
|
53
|
-
let executor;
|
|
54
|
-
if (overrides.loadExecutor) {
|
|
55
|
-
executor = overrides.loadExecutor(spec, specPath);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
const builtin = builtinExecutor(spec, specPath);
|
|
59
|
-
await builtin.preflight({
|
|
60
|
-
subjectCredentialNames: [
|
|
61
|
-
...new Set(spec.subjects.flatMap((subject) => subject.credentials)),
|
|
62
|
-
],
|
|
63
|
-
...(signal ? { signal } : {}),
|
|
64
|
-
});
|
|
65
|
-
executor = builtin;
|
|
66
|
-
}
|
|
67
|
-
const results = await runExperiment({
|
|
68
|
-
spec,
|
|
69
|
-
store: directory.attempts,
|
|
70
|
-
executor,
|
|
71
|
-
subjects: overrides.subjects ?? [createMakaSubjectAdapter(), createExternalSubjectAdapter()],
|
|
72
|
-
...(command.cellIds.length > 0 ? { cellIds: command.cellIds } : {}),
|
|
73
|
-
...(signal ? { signal } : {}),
|
|
74
|
-
});
|
|
75
|
-
const incomplete = spec.tasks.length * spec.repetitions * spec.subjects.length - results.size;
|
|
76
|
-
(overrides.stdout ?? process.stdout.write.bind(process.stdout))(`${JSON.stringify({ experimentId: spec.id, cells: results.size, incomplete })}\n`);
|
|
77
|
-
return interrupted ?? (incomplete === 0 ? 0 : 1);
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
(overrides.stderr ?? process.stderr.write.bind(process.stderr))(`maka eval: ${error instanceof Error ? error.message : String(error)}\n${USAGE}\n`);
|
|
81
|
-
return interrupted ?? 2;
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
if (controller) {
|
|
85
|
-
process.removeListener('SIGINT', interrupt);
|
|
86
|
-
process.removeListener('SIGTERM', terminate);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
function builtinExecutor(spec, specPath) {
|
|
91
|
-
if (spec.executor.kind === 'harbor')
|
|
92
|
-
return createHarborExecutor(spec.executor.config, specPath);
|
|
93
|
-
if (spec.executor.kind === 'pier')
|
|
94
|
-
return createPierExecutor(spec.executor.config, specPath);
|
|
95
|
-
throw new Error(`unsupported executor: ${spec.executor.kind}`);
|
|
96
|
-
}
|
|
97
|
-
function parseArgs(argv) {
|
|
98
|
-
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h'))
|
|
99
|
-
return { kind: 'help' };
|
|
100
|
-
if (argv[0] !== 'run' || !argv[1] || argv[1].startsWith('-'))
|
|
101
|
-
throw new Error(USAGE);
|
|
102
|
-
let outDir;
|
|
103
|
-
const cellIds = [];
|
|
104
|
-
for (let index = 2; index < argv.length; index += 2) {
|
|
105
|
-
const flag = argv[index];
|
|
106
|
-
const value = argv[index + 1];
|
|
107
|
-
if (!value || value.startsWith('-'))
|
|
108
|
-
throw new Error(`${flag} requires a value`);
|
|
109
|
-
if (flag === '--out')
|
|
110
|
-
outDir = value;
|
|
111
|
-
else if (flag === '--cell')
|
|
112
|
-
cellIds.push(value);
|
|
113
|
-
else
|
|
114
|
-
throw new Error(`unexpected argument: ${flag}`);
|
|
115
|
-
}
|
|
116
|
-
if (!outDir)
|
|
117
|
-
throw new Error('--out is required');
|
|
118
|
-
return { kind: 'run', specPath: argv[1], outDir, cellIds };
|
|
119
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
* or more contributor license agreements. See the NOTICE file
|
|
4
|
-
* distributed with this work for additional information
|
|
5
|
-
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
* to you under the Apache License, Version 2.0 (the
|
|
7
|
-
* "License"); you may not use this file except in compliance
|
|
8
|
-
* with the License. You may obtain a copy of the License at
|
|
9
|
-
*
|
|
10
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
*
|
|
12
|
-
* Unless required by applicable law or agreed to in writing,
|
|
13
|
-
* software distributed under the License is distributed on an
|
|
14
|
-
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
* KIND, either express or implied. See the License for the
|
|
16
|
-
* specific language governing permissions and limitations
|
|
17
|
-
* under the License.
|
|
18
|
-
*/
|
|
19
|
-
import { randomUUID } from 'node:crypto';
|
|
20
|
-
import { link, mkdir, open, readFile, unlink } from 'node:fs/promises';
|
|
21
|
-
import { join } from 'node:path';
|
|
22
|
-
import { FileAttemptStore } from './attempt-store.js';
|
|
23
|
-
import { parseExperimentSpec } from './spec.js';
|
|
24
|
-
export async function openExperimentDirectory(root, spec) {
|
|
25
|
-
await mkdir(root, { recursive: true });
|
|
26
|
-
const specPath = join(root, 'experiment.json');
|
|
27
|
-
const temporaryPath = join(root, `.experiment-${randomUUID()}.tmp`);
|
|
28
|
-
const canonical = `${canonicalJson(spec)}\n`;
|
|
29
|
-
const temporary = await open(temporaryPath, 'wx', 0o600);
|
|
30
|
-
try {
|
|
31
|
-
await temporary.writeFile(canonical);
|
|
32
|
-
await temporary.sync();
|
|
33
|
-
await temporary.close();
|
|
34
|
-
await link(temporaryPath, specPath);
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
if (error.code !== 'EEXIST')
|
|
38
|
-
throw error;
|
|
39
|
-
const existing = parseExperimentSpec(JSON.parse(await readFile(specPath, 'utf8')));
|
|
40
|
-
if (canonicalJson(existing) !== canonicalJson(spec))
|
|
41
|
-
throw new Error('experiment spec differs');
|
|
42
|
-
}
|
|
43
|
-
finally {
|
|
44
|
-
await temporary.close().catch(() => undefined);
|
|
45
|
-
await unlink(temporaryPath).catch(() => undefined);
|
|
46
|
-
}
|
|
47
|
-
return { root, specPath, attempts: new FileAttemptStore(join(root, 'attempts')) };
|
|
48
|
-
}
|
|
49
|
-
function canonicalJson(value) {
|
|
50
|
-
if (Array.isArray(value))
|
|
51
|
-
return `[${value.map(canonicalJson).join(',')}]`;
|
|
52
|
-
if (value && typeof value === 'object') {
|
|
53
|
-
return `{${Object.entries(value)
|
|
54
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
55
|
-
.map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`)
|
|
56
|
-
.join(',')}}`;
|
|
57
|
-
}
|
|
58
|
-
return JSON.stringify(value);
|
|
59
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
* or more contributor license agreements. See the NOTICE file
|
|
4
|
-
* distributed with this work for additional information
|
|
5
|
-
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
* to you under the Apache License, Version 2.0 (the
|
|
7
|
-
* "License"); you may not use this file except in compliance
|
|
8
|
-
* with the License. You may obtain a copy of the License at
|
|
9
|
-
*
|
|
10
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
*
|
|
12
|
-
* Unless required by applicable law or agreed to in writing,
|
|
13
|
-
* software distributed under the License is distributed on an
|
|
14
|
-
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
* KIND, either express or implied. See the License for the
|
|
16
|
-
* specific language governing permissions and limitations
|
|
17
|
-
* under the License.
|
|
18
|
-
*/
|
|
19
|
-
export function decodeJsonObject(value, where) {
|
|
20
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
21
|
-
throw new Error(`${where} must be an object`);
|
|
22
|
-
}
|
|
23
|
-
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, decodeJsonValue(child, `${where}.${key}`)]));
|
|
24
|
-
}
|
|
25
|
-
function decodeJsonValue(value, where) {
|
|
26
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
27
|
-
return value;
|
|
28
|
-
if (typeof value === 'number' && Number.isFinite(value))
|
|
29
|
-
return value;
|
|
30
|
-
if (Array.isArray(value)) {
|
|
31
|
-
return value.map((child, index) => decodeJsonValue(child, `${where}[${index}]`));
|
|
32
|
-
}
|
|
33
|
-
if (value && typeof value === 'object')
|
|
34
|
-
return decodeJsonObject(value, where);
|
|
35
|
-
throw new Error(`${where} must be JSON`);
|
|
36
|
-
}
|
|
37
|
-
export function expandExperiment(spec) {
|
|
38
|
-
return spec.tasks.flatMap((task) => Array.from({ length: spec.repetitions }, (_, repetition) => repetition + 1).flatMap((repetition) => spec.subjects.map((subject) => ({
|
|
39
|
-
id: `${task.id}::${repetition}::${subject.id}`,
|
|
40
|
-
experimentId: spec.id,
|
|
41
|
-
benchmark: spec.benchmark,
|
|
42
|
-
executor: spec.executor,
|
|
43
|
-
subject,
|
|
44
|
-
task,
|
|
45
|
-
repetition,
|
|
46
|
-
budget: spec.budget,
|
|
47
|
-
verifier: spec.verifier,
|
|
48
|
-
}))));
|
|
49
|
-
}
|