m3triq 0.2.5 → 0.2.7
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/cli.js +32 -2
- package/dist/commands/config-cmd.js +22 -4
- package/dist/commands/design.js +2 -2
- package/dist/commands/predict.js +3 -3
- package/dist/config.d.ts +1 -0
- package/dist/errorReporter.d.ts +5 -0
- package/dist/errorReporter.js +77 -0
- package/package.json +14 -4
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { setJsonMode } from './output.js';
|
|
4
4
|
import { getEffectiveConfig, requireApiKey } from './config.js';
|
|
5
5
|
import { M3triqClient, AgentsClient } from './client.js';
|
|
6
|
+
import { reportCliError } from './errorReporter.js';
|
|
6
7
|
import { registerConfigCommands } from './commands/config-cmd.js';
|
|
7
8
|
import { registerProjectCommands } from './commands/projects.js';
|
|
8
9
|
import { registerSessionCommands } from './commands/sessions.js';
|
|
@@ -23,7 +24,7 @@ const program = new Command();
|
|
|
23
24
|
program
|
|
24
25
|
.name('m3t')
|
|
25
26
|
.description('M3TRIQ — protein-ligand analysis from the terminal')
|
|
26
|
-
.version('0.2.
|
|
27
|
+
.version('0.2.7')
|
|
27
28
|
.option('--json', 'Output as JSON (machine-readable)')
|
|
28
29
|
.hook('preAction', (thisCommand) => {
|
|
29
30
|
const opts = thisCommand.optsWithGlobals();
|
|
@@ -46,8 +47,37 @@ registerMdCommands(program);
|
|
|
46
47
|
registerZincCommands(program);
|
|
47
48
|
registerCreditsCommands(program);
|
|
48
49
|
registerPricingCommands(program);
|
|
50
|
+
// Last-line-of-defense catches: anything that escapes a command handler.
|
|
51
|
+
// These fire BEFORE process.exit(), so the reporter has time to flush
|
|
52
|
+
// (it has its own 3-second timeout to bound the wait).
|
|
53
|
+
process.on('uncaughtException', async (err) => {
|
|
54
|
+
try {
|
|
55
|
+
await reportCliError(err, { kind: 'uncaughtException' });
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
process.stderr.write(`Error: ${err?.message || String(err)}\n`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
});
|
|
61
|
+
process.on('unhandledRejection', async (reason) => {
|
|
62
|
+
try {
|
|
63
|
+
await reportCliError(reason, { kind: 'unhandledRejection' });
|
|
64
|
+
}
|
|
65
|
+
catch { }
|
|
66
|
+
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
67
|
+
process.stderr.write(`Error: ${msg}\n`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
49
70
|
// Catch async errors from all command actions
|
|
50
|
-
program.parseAsync().catch((err) => {
|
|
71
|
+
program.parseAsync().catch(async (err) => {
|
|
72
|
+
// Don't beacon commander's own user-facing exits (--help, --version, bad args).
|
|
73
|
+
const code = err.code;
|
|
74
|
+
const isCommanderExit = typeof code === 'string' && code.startsWith('commander.');
|
|
75
|
+
if (!isCommanderExit) {
|
|
76
|
+
try {
|
|
77
|
+
await reportCliError(err, { kind: 'command' });
|
|
78
|
+
}
|
|
79
|
+
catch { }
|
|
80
|
+
}
|
|
51
81
|
const message = err.message || String(err);
|
|
52
82
|
if (message.includes('API error') || message.includes('MCP')) {
|
|
53
83
|
process.stderr.write(`Error: ${message}\n`);
|
|
@@ -7,6 +7,7 @@ export function registerConfigCommands(program) {
|
|
|
7
7
|
.option('--key <api-key>', 'Set API key')
|
|
8
8
|
.option('--url <api-url>', 'Set API URL')
|
|
9
9
|
.option('--console <console-url>', 'Set console URL')
|
|
10
|
+
.option('--error-reporting <on|off>', 'Toggle anonymous error reporting (default: on)')
|
|
10
11
|
.action((opts) => {
|
|
11
12
|
const config = loadConfig();
|
|
12
13
|
// Update fields if provided
|
|
@@ -23,6 +24,21 @@ export function registerConfigCommands(program) {
|
|
|
23
24
|
config.console_url = opts.console;
|
|
24
25
|
updated = true;
|
|
25
26
|
}
|
|
27
|
+
if (opts.errorReporting !== undefined) {
|
|
28
|
+
const v = String(opts.errorReporting).toLowerCase();
|
|
29
|
+
if (['off', 'false', '0', 'no'].includes(v)) {
|
|
30
|
+
config.error_reporting_disabled = true;
|
|
31
|
+
updated = true;
|
|
32
|
+
}
|
|
33
|
+
else if (['on', 'true', '1', 'yes'].includes(v)) {
|
|
34
|
+
config.error_reporting_disabled = false;
|
|
35
|
+
updated = true;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
process.stderr.write(`Error: --error-reporting expects on|off (got "${opts.errorReporting}")\n`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
26
42
|
if (updated) {
|
|
27
43
|
saveConfig(config);
|
|
28
44
|
process.stderr.write('Config saved.\n');
|
|
@@ -38,14 +54,16 @@ export function registerConfigCommands(program) {
|
|
|
38
54
|
console_url: effective.console_url,
|
|
39
55
|
active_project: effective.active_project || null,
|
|
40
56
|
active_project_name: effective.active_project_name || null,
|
|
57
|
+
error_reporting: effective.error_reporting_disabled ? 'off' : 'on',
|
|
41
58
|
};
|
|
42
59
|
const local = loadLocalProject();
|
|
43
60
|
const projectSource = local ? 'local .m3triq' : effective.active_project ? 'global' : '';
|
|
44
61
|
const human = [
|
|
45
|
-
`API Key:
|
|
46
|
-
`API URL:
|
|
47
|
-
`Console URL:
|
|
48
|
-
`Project:
|
|
62
|
+
`API Key: ${maskedKey}`,
|
|
63
|
+
`API URL: ${effective.api_url}`,
|
|
64
|
+
`Console URL: ${effective.console_url}`,
|
|
65
|
+
`Project: ${effective.active_project_name || '(none)'} ${effective.active_project ? `(${effective.active_project.substring(0, 8)})` : ''} ${projectSource ? `[${projectSource}]` : ''}`,
|
|
66
|
+
`Error reporting: ${effective.error_reporting_disabled ? 'off' : 'on'}`,
|
|
49
67
|
].join('\n');
|
|
50
68
|
output(data, human);
|
|
51
69
|
});
|
package/dist/commands/design.js
CHANGED
|
@@ -67,8 +67,8 @@ export function registerDesignCommands(program) {
|
|
|
67
67
|
.argument('<target>', 'Target PDB ID (e.g., 6VXX) or path to .pdb file')
|
|
68
68
|
.requiredOption('--hotspots <residues>', 'Binding hotspot residues (e.g., A100,A105,A110)')
|
|
69
69
|
.option('--type <type>', 'Antibody type: nanobody or scfv', 'nanobody')
|
|
70
|
-
.option('--designs <n>', 'Number of designs (1-10)', parseInt, 3)
|
|
71
|
-
.option('--seqs <n>', 'Sequences per backbone (1-8)', parseInt, 1)
|
|
70
|
+
.option('--designs <n>', 'Number of designs (1-10)', (v) => parseInt(v, 10), 3)
|
|
71
|
+
.option('--seqs <n>', 'Sequences per backbone (1-8)', (v) => parseInt(v, 10), 1)
|
|
72
72
|
.option('--cdr <loops>', 'CDR loop specs (e.g., H1:7,H2:6,H3:5-13)')
|
|
73
73
|
.description('Design de novo antibodies with RFAntibody (Azure T4 GPU, ~3-5 min)')
|
|
74
74
|
.action(async (target, opts) => {
|
package/dist/commands/predict.js
CHANGED
|
@@ -28,9 +28,9 @@ export function registerPredictCommands(program) {
|
|
|
28
28
|
.option('--dna <seq>', 'DNA sequence (or @path to file). Repeatable.', collectArg, [])
|
|
29
29
|
.option('--rna <seq>', 'RNA sequence (or @path to file). Repeatable.', collectArg, [])
|
|
30
30
|
.option('--ligand <smiles_or_ccd>', 'Ligand as SMILES or CCD code (e.g. ATP). Repeatable.', collectArg, [])
|
|
31
|
-
.option('--samples <n>', 'Number of structure samples (1-25, default: 1)', parseInt, 1)
|
|
32
|
-
.option('--recycling <n>', 'Recycling steps (1-10, default: 3)', parseInt, 3)
|
|
33
|
-
.option('--sampling <n>', 'Diffusion sampling steps (10-1000, default: 50)', parseInt, 50)
|
|
31
|
+
.option('--samples <n>', 'Number of structure samples (1-25, default: 1)', (v) => parseInt(v, 10), 1)
|
|
32
|
+
.option('--recycling <n>', 'Recycling steps (1-10, default: 3)', (v) => parseInt(v, 10), 3)
|
|
33
|
+
.option('--sampling <n>', 'Diffusion sampling steps (10-1000, default: 50)', (v) => parseInt(v, 10), 50)
|
|
34
34
|
.option('--name <name>', 'Custom job title')
|
|
35
35
|
.action(async (opts) => {
|
|
36
36
|
await runBoltz2(opts);
|
package/dist/config.d.ts
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Beacon CLI errors to Django POST /api/errors/.
|
|
2
|
+
// Best-effort: never throws, never blocks the exit beyond ~3 seconds.
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { getEffectiveConfig } from './config.js';
|
|
5
|
+
const REPORT_TIMEOUT_MS = 3000;
|
|
6
|
+
export async function reportCliError(err, context = {}) {
|
|
7
|
+
let config;
|
|
8
|
+
try {
|
|
9
|
+
config = getEffectiveConfig();
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
// Opt-out gate. Either the config flag or env var disables reporting.
|
|
15
|
+
if (config.error_reporting_disabled === true)
|
|
16
|
+
return;
|
|
17
|
+
if (process.env.M3TRIQ_NO_ERROR_REPORTING === '1')
|
|
18
|
+
return;
|
|
19
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20
|
+
const stack = err instanceof Error ? (err.stack || '') : '';
|
|
21
|
+
const argv = (context.argv || process.argv).slice(1);
|
|
22
|
+
// Replace any token-like value (long hex/base64 strings) with a placeholder.
|
|
23
|
+
const sanitizedArgv = argv.map(a => /^[A-Za-z0-9_\-+/=]{32,}$/.test(a) ? '<redacted>' : a);
|
|
24
|
+
const url_or_cmd = sanitizedArgv.join(' ').slice(0, 500);
|
|
25
|
+
const cliVersion = await readCliVersion();
|
|
26
|
+
const payload = {
|
|
27
|
+
source: 'cli',
|
|
28
|
+
session_id: '',
|
|
29
|
+
url_or_cmd,
|
|
30
|
+
message: message.slice(0, 5000),
|
|
31
|
+
stack: stack.slice(0, 10000),
|
|
32
|
+
metadata: {
|
|
33
|
+
kind: context.kind || 'command',
|
|
34
|
+
cli_version: cliVersion,
|
|
35
|
+
node_version: process.version,
|
|
36
|
+
platform: process.platform,
|
|
37
|
+
arch: process.arch,
|
|
38
|
+
os_release: os.release(),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const url = `${config.api_url || 'https://server.m3triq.com'}/api/errors/`;
|
|
42
|
+
const ctrl = new AbortController();
|
|
43
|
+
const timer = setTimeout(() => ctrl.abort(), REPORT_TIMEOUT_MS);
|
|
44
|
+
try {
|
|
45
|
+
await fetch(url, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
body: JSON.stringify(payload),
|
|
49
|
+
signal: ctrl.signal,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Best-effort: swallow network errors, timeouts, anything.
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let _cachedVersion = null;
|
|
60
|
+
async function readCliVersion() {
|
|
61
|
+
if (_cachedVersion)
|
|
62
|
+
return _cachedVersion;
|
|
63
|
+
try {
|
|
64
|
+
const fs = await import('node:fs');
|
|
65
|
+
const path = await import('node:path');
|
|
66
|
+
const url = await import('node:url');
|
|
67
|
+
const here = path.dirname(url.fileURLToPath(import.meta.url));
|
|
68
|
+
const pkgPath = path.join(here, '..', 'package.json');
|
|
69
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
70
|
+
_cachedVersion = pkg.version || 'unknown';
|
|
71
|
+
return _cachedVersion;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
_cachedVersion = 'unknown';
|
|
75
|
+
return _cachedVersion;
|
|
76
|
+
}
|
|
77
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "m3triq",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "M3TRIQ
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"description": "M3TRIQ \u2014 protein-ligand analysis from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"m3t": "./dist/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"main": "dist/cli.js",
|
|
10
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"bioinformatics",
|
|
12
|
+
"chembl",
|
|
13
|
+
"docking",
|
|
14
|
+
"protein",
|
|
15
|
+
"ligand",
|
|
16
|
+
"drug-discovery",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
11
19
|
"repository": {
|
|
12
20
|
"type": "git",
|
|
13
21
|
"url": "https://github.com/M3TRIQ/m3t.git"
|
|
@@ -28,6 +36,8 @@
|
|
|
28
36
|
"@types/node": "^22.0.0",
|
|
29
37
|
"typescript": "^5.7.0"
|
|
30
38
|
},
|
|
31
|
-
"files": [
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
32
42
|
"license": "MIT"
|
|
33
43
|
}
|