@mmmbuto/nexuscrew 0.9.24 → 0.9.25
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/CHANGELOG.md +76 -0
- package/frontend/dist/version.json +1 -1
- package/lib/cli/commands.js +33 -1
- package/lib/cli/doctor.js +87 -4
- package/lib/config.js +51 -1
- package/lib/fleet/builtin.js +13 -1
- package/lib/fleet/catalogs/opencode-go.json +200 -16
- package/lib/fleet/cell-exec.js +31 -5
- package/lib/fleet/identity-provision.js +142 -0
- package/lib/fleet/managed.js +140 -14
- package/lib/fleet/runtime.js +30 -1
- package/lib/mcp/identity-provider.js +133 -5
- package/lib/mcp/identity-schema.js +2 -0
- package/lib/mcp/server.js +50 -9
- package/lib/mcp/tools.js +15 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,82 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NexusCrew are tracked here.
|
|
4
4
|
|
|
5
|
+
## 0.9.25 - 2026-09-13
|
|
6
|
+
|
|
7
|
+
- **Compatibility note**: codex-vl >= 0.154.0-vl.3 inside a bound Fleet cell
|
|
8
|
+
requires NexusCrew >= 0.9.25 for the `nc_*` tools; NexusCrew 0.9.24 only
|
|
9
|
+
reads the legacy identity variables, which vl.3 removes from MCP children on
|
|
10
|
+
verified bindings. Standalone (legacy) cells keep working with both.
|
|
11
|
+
|
|
12
|
+
- **MCP identity reads the verified binding metadata first (fail-closed).**
|
|
13
|
+
`nexuscrew mcp` now evaluates the `NEXUSCREW_VERIFIED_*` metadata delivered
|
|
14
|
+
by codex-vl >= 0.154.0-vl.3 launchers before any legacy source: version,
|
|
15
|
+
owner instance, cell, channel and the authority introspection must all be
|
|
16
|
+
coherent (source `verified-env`, verified identity in `nc_identity`), and any
|
|
17
|
+
incoherence fails closed with `NEXUSCREW_MCP_IDENTITY_VERIFIED_ENV_INVALID`
|
|
18
|
+
instead of falling back to the legacy variables. Without the metadata the
|
|
19
|
+
legacy path is unchanged.
|
|
20
|
+
- **`CODEX_APP_SERVER_IDENTITY_REQUIRED=1` is exported only in authority
|
|
21
|
+
mode.** The fleet launcher now exports the flag for codex-vl cells only when
|
|
22
|
+
the fleet identity mode is `authority` and the authority is constructible;
|
|
23
|
+
in legacy mode it exports an explicit `0` and names the reason in a
|
|
24
|
+
structured log line. Explicit user overrides (`engine.env`, `cfg.env`) still win, with a
|
|
25
|
+
warning when they force `1` outside authority mode.
|
|
26
|
+
- **Standalone cells (`CODEX_APP_SERVER_IDENTITY_REQUIRED=0`) no longer
|
|
27
|
+
receive the identity channel.** The launcher created fd 3:4 and
|
|
28
|
+
`NEXUSCREW_IDENTITY_FD` even when it had resolved the flag to `0`, so the
|
|
29
|
+
child started an identity handshake against a legacy lease server that
|
|
30
|
+
answered `revoked` and the cell died at startup. The channel decision now
|
|
31
|
+
comes from the same resolution that produces the flag.
|
|
32
|
+
- **A configured but unavailable fleet identity authority now refuses
|
|
33
|
+
protected cell launches instead of silently falling back to standalone.**
|
|
34
|
+
The launch fails with `IDENTITY_AUTHORITY_UNAVAILABLE` naming the reason
|
|
35
|
+
(never a value), and `nexuscrew doctor` reports the same state.
|
|
36
|
+
- **Upgrade NexusCrew before codex-vl**: `codex-vl >= 0.154.0-vl.3` with
|
|
37
|
+
`NexusCrew <= 0.9.24` fails closed in Fleet cells (the old launcher forces
|
|
38
|
+
identity without an authority).
|
|
39
|
+
- **`nexuscrew identity provision`.** New command that generates the two
|
|
40
|
+
distinct authority credentials (32 random bytes each, files `0600` inside a
|
|
41
|
+
`0700` directory under `~/.nexuscrew/identity-authority/`), never overwrites
|
|
42
|
+
existing credentials without `--force`, activates
|
|
43
|
+
`fleet.identity.mode = "authority"` in the config (or prints the exact keys
|
|
44
|
+
with `--no-write-config`). `loadConfig` reads the credential files
|
|
45
|
+
automatically and `nexuscrew doctor` shows the identity mode and authority
|
|
46
|
+
constructibility (no values).
|
|
47
|
+
- **MCP identity remediation rewritten.** `nc_identity` and `doctor` no longer
|
|
48
|
+
recommend allowlisting `TMUX`/`TMUX_PANE`/`NEXUSCREW_MCP_SESSION` in the MCP
|
|
49
|
+
server `env_vars`: on a shared verified spawn that allowlist fails the launch
|
|
50
|
+
(the names are reserved). The two valid paths are a verified session
|
|
51
|
+
(codex-vl >= 0.154.0-vl.3 + NexusCrew >= 0.9.25 with provisioned authority)
|
|
52
|
+
or a legacy session inheriting the three variables.
|
|
53
|
+
|
|
54
|
+
- **Ollama Cloud catalog refreshed against the official measured sample of
|
|
55
|
+
2026-09-12.** Bare-name `gemma4` and `qwen3.5` enter the catalog (verified
|
|
56
|
+
alive with a 200 on `/v1/chat/completions`; official measured sample:
|
|
57
|
+
22 live ids / 8 dead variants), with context and capabilities taken from
|
|
58
|
+
the channel cards (`gemma4` 128K text+image, `qwen3.5` 256K text+image).
|
|
59
|
+
The 404 variants (`gemma4:12b`/`:e4b`, `qwen3.5:27b`/`:35b`/`:122b`, bare
|
|
60
|
+
`mistral-large-3`, `nemotron-3-super:120b`, `nemotron-3-nano:4b`) and
|
|
61
|
+
community ids stay out on purpose. `glm-5.3` and `deepseek-v4.1-flash`
|
|
62
|
+
were already listed and are not duplicated.
|
|
63
|
+
- **Fixture-based liveness guard for the Ollama Cloud models.** New test
|
|
64
|
+
(`tests/fleet-ollama-liveness.test.js`): every catalog id must belong to
|
|
65
|
+
the official measured sample; when upstream retires an id, the test goes
|
|
66
|
+
red naming it as "to be verified". No network calls in tests; the real
|
|
67
|
+
periodic check remains a separate item.
|
|
68
|
+
- **OpenCode Go catalog refreshed signal-only (401 CreditsError on
|
|
69
|
+
2026-09-12).** The Go plan could not generate (credits exhausted, listing
|
|
70
|
+
OK): the additions are wire signals, not end-to-end measurements.
|
|
71
|
+
`glm-5.3`, `glm-5.3-flash`, `longcat-2.0`, `deepseek-v4.1-flash` and
|
|
72
|
+
`hy4-preview` enter all three wires; `qwen3.8-flash` enters Messages/Chat
|
|
73
|
+
ONLY (Responses rejected, measured). New limits declared by models.dev
|
|
74
|
+
(1M context; `hy4-preview` 1024000), marked "declared, not measured".
|
|
75
|
+
Retired `glm-5`, `kimi-k2.5`, `minimax-m2.5` and `qwen3.5-plus` are
|
|
76
|
+
removed from the lists, LIMITS and the catalog, with a test guard against
|
|
77
|
+
re-introduction. `hy3` unchanged (diverging source on its output limit:
|
|
78
|
+
to be re-checked), default `deepseek-v4-flash` unchanged, VISION
|
|
79
|
+
unchanged (only the PNG-measured vision-exp id).
|
|
80
|
+
|
|
5
81
|
## 0.9.24 — 2026-09-12
|
|
6
82
|
|
|
7
83
|
- **Verifica dell'identità legata alla challenge, con claims completi.** La
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.9.
|
|
1
|
+
{"version": "0.9.25"}
|
package/lib/cli/commands.js
CHANGED
|
@@ -86,7 +86,7 @@ Lifecycle semantics:
|
|
|
86
86
|
|
|
87
87
|
const CLI_VALUE_FLAGS = new Set([
|
|
88
88
|
'label', 'ssh', 'ssh-port', 'autostart', 'visibility', 'selected',
|
|
89
|
-
'name', 'local-name', 'local-label', 'identity-file', 'port',
|
|
89
|
+
'name', 'local-name', 'local-label', 'identity-file', 'port', 'dir',
|
|
90
90
|
]);
|
|
91
91
|
|
|
92
92
|
// valueFlags (Set|array opzionale): flag che consumano il token successivo nella
|
|
@@ -1508,6 +1508,38 @@ function dispatch(argv, opts = {}) {
|
|
|
1508
1508
|
serve({ ...opts, pidfile: flags.pidfile, serverStart: opts.serverStart });
|
|
1509
1509
|
return { code: 0, keepAlive: true }; // server.listen tiene il processo vivo; non exit
|
|
1510
1510
|
}
|
|
1511
|
+
if (cmd === 'identity') {
|
|
1512
|
+
const sub = rest[1];
|
|
1513
|
+
if (sub !== 'provision') {
|
|
1514
|
+
log('usage: nexuscrew identity provision [--force] [--no-write-config] [--dir <path>]');
|
|
1515
|
+
return { code: sub === undefined ? 0 : 1 };
|
|
1516
|
+
}
|
|
1517
|
+
const provision = require('../fleet/identity-provision.js').provisionIdentityAuthority;
|
|
1518
|
+
const home = opts.home || require('node:os').homedir();
|
|
1519
|
+
const result = provision({
|
|
1520
|
+
home,
|
|
1521
|
+
dir: flags.dir,
|
|
1522
|
+
force: flags.force === true,
|
|
1523
|
+
writeConfig: flags['no-write-config'] !== true,
|
|
1524
|
+
});
|
|
1525
|
+
if (!result.ok) {
|
|
1526
|
+
log(`identity provision: ${result.message}`);
|
|
1527
|
+
log(` existing credential files:`);
|
|
1528
|
+
for (const filePath of result.existing) log(` ${filePath}`);
|
|
1529
|
+
return { code: 1 };
|
|
1530
|
+
}
|
|
1531
|
+
log(`identity provision: credentials written under ${result.dir} (0600, dir 0700)`);
|
|
1532
|
+
log('identity provision: fleet.identity.mode = "authority"');
|
|
1533
|
+
if (result.configUpdated) {
|
|
1534
|
+
log(`identity provision: config updated (${result.mode})`);
|
|
1535
|
+
} else {
|
|
1536
|
+
log('identity provision: config NOT written (--no-write-config). Apply manually:');
|
|
1537
|
+
log(' fleet.identity.mode = "authority"');
|
|
1538
|
+
log(` keep both credential files in place under ${result.dir}`);
|
|
1539
|
+
}
|
|
1540
|
+
log('identity provision: restart the NexusCrew service to activate authority mode');
|
|
1541
|
+
return { code: 0 };
|
|
1542
|
+
}
|
|
1511
1543
|
if (cmd === 'doctor') {
|
|
1512
1544
|
if (flags.peers === true) return dispatchNodes(['nodes', 'doctor'], flags, opts);
|
|
1513
1545
|
const r = doctor({ ...opts, log });
|
package/lib/cli/doctor.js
CHANGED
|
@@ -462,6 +462,24 @@ function checkMcpIdentity(env) {
|
|
|
462
462
|
const e = env || process.env;
|
|
463
463
|
const hasTmux = !!(typeof e.TMUX === 'string' && e.TMUX);
|
|
464
464
|
const hasSession = !!(typeof e.NEXUSCREW_MCP_SESSION === 'string' && e.NEXUSCREW_MCP_SESSION.trim());
|
|
465
|
+
const verifiedNames = [
|
|
466
|
+
'NEXUSCREW_VERIFIED_ENV_VERSION',
|
|
467
|
+
'NEXUSCREW_VERIFIED_OWNER_INSTANCE_ID',
|
|
468
|
+
'NEXUSCREW_VERIFIED_CELL_ID',
|
|
469
|
+
'NEXUSCREW_VERIFIED_INCARNATION_ID',
|
|
470
|
+
'NEXUSCREW_VERIFIED_BINDING_ID',
|
|
471
|
+
'NEXUSCREW_VERIFIED_ORIGIN',
|
|
472
|
+
'NEXUSCREW_VERIFIED_THREAD_ID',
|
|
473
|
+
];
|
|
474
|
+
const verifiedPresent = verifiedNames.filter((name) => typeof e[name] === 'string' && e[name]);
|
|
475
|
+
if (verifiedPresent.length) {
|
|
476
|
+
return {
|
|
477
|
+
name: 'MCP identity env',
|
|
478
|
+
ok: true,
|
|
479
|
+
detail: `metadati verified osservabili (${verifiedPresent.length}/7 nomi NEXUSCREW_VERIFIED_*): `
|
|
480
|
+
+ 'identita attribuita solo dopo introspezione authority riuscita',
|
|
481
|
+
};
|
|
482
|
+
}
|
|
465
483
|
if (hasTmux || hasSession) {
|
|
466
484
|
const src = [];
|
|
467
485
|
if (hasTmux) src.push('TMUX');
|
|
@@ -469,16 +487,80 @@ function checkMcpIdentity(env) {
|
|
|
469
487
|
return {
|
|
470
488
|
name: 'MCP identity env',
|
|
471
489
|
ok: true,
|
|
472
|
-
detail: `identita osservabile (${src.join('+')})`,
|
|
490
|
+
detail: `identita legacy osservabile (${src.join('+')})`,
|
|
473
491
|
};
|
|
474
492
|
}
|
|
475
493
|
return {
|
|
476
494
|
name: 'MCP identity env',
|
|
477
495
|
ok: true, // mai FAIL: PWA-only senza MCP configurato non deve rompere il doctor
|
|
478
496
|
warn: true,
|
|
479
|
-
detail: 'nessuna identita MCP osservabile (
|
|
480
|
-
+ '
|
|
497
|
+
detail: 'nessuna identita MCP osservabile (nessun NEXUSCREW_VERIFIED_* e nessuna variabile legacy '
|
|
498
|
+
+ 'nel processo doctor). Percorsi validi: sessione verified (codex-vl >= 0.154.0-vl.3 + NexusCrew '
|
|
499
|
+
+ '>= 0.9.25 con authority mode provisionato) oppure sessione legacy con le tre variabili ereditate '
|
|
500
|
+
+ 'dal client MCP. NON allowlistare i nomi legacy in env_vars del server MCP: con vl.3 lo spawn '
|
|
501
|
+
+ 'verified fallisce — PWA-only: ignorabile',
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Stato dell'authority mode (registro flotta). Mostra mode e costruibilita' dell'
|
|
506
|
+
// authority (file presenti, permessi) SENZA mai esporre valori.
|
|
507
|
+
function checkIdentityAuthorityMode({ home, configPath, env } = {}) {
|
|
508
|
+
const e = env || process.env;
|
|
509
|
+
const fs = require('node:fs');
|
|
510
|
+
const path = require('node:path');
|
|
511
|
+
const cfgPath = configPath
|
|
512
|
+
|| process.env.NEXUSCREW_CONFIG_FILE
|
|
513
|
+
|| path.join(home || os.homedir(), '.nexuscrew', 'config.json');
|
|
514
|
+
let cfg = {};
|
|
515
|
+
try { cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8')) || {}; } catch (_) { cfg = {}; }
|
|
516
|
+
const mode = cfg.fleetIdentityMode
|
|
517
|
+
|| (cfg.fleet && cfg.fleet.identity && cfg.fleet.identity.mode)
|
|
518
|
+
|| 'legacy';
|
|
519
|
+
const dir = cfg.identityAuthorityDir || path.join(home || os.homedir(), '.nexuscrew', 'identity-authority');
|
|
520
|
+
const files = ['daemon.credential', 'launcher.credential'];
|
|
521
|
+
const present = [];
|
|
522
|
+
const values = {};
|
|
523
|
+
const problems = [];
|
|
524
|
+
for (const name of files) {
|
|
525
|
+
const filePath = path.join(dir, name);
|
|
526
|
+
try {
|
|
527
|
+
const st = fs.lstatSync(filePath);
|
|
528
|
+
if (!st.isFile()) { problems.push(`${name}: non regolare`); continue; }
|
|
529
|
+
if ((st.mode & 0o777) !== 0o600) { problems.push(`${name}: permessi non 0600`); continue; }
|
|
530
|
+
const value = fs.readFileSync(filePath, 'utf8').trim();
|
|
531
|
+
if (!value) { problems.push(`${name}: vuoto`); continue; }
|
|
532
|
+
present.push(name);
|
|
533
|
+
values[name] = value;
|
|
534
|
+
} catch (_) { problems.push(`${name}: assente`); }
|
|
535
|
+
}
|
|
536
|
+
// The two credentials must be DISTINCT (constant-time comparison):
|
|
537
|
+
// identical means the authority is not constructible, fail-closed.
|
|
538
|
+
let distinctLine = 'credentials distinct: n/a (una o entrambe assenti)';
|
|
539
|
+
if (present.length === 2) {
|
|
540
|
+
const distinct = Buffer.from(values['daemon.credential'])
|
|
541
|
+
.compare(Buffer.from(values['launcher.credential'])) !== 0;
|
|
542
|
+
distinctLine = `credentials distinct: ${distinct ? 'yes' : 'no'}`;
|
|
543
|
+
if (!distinct) problems.push('credenziali daemon e launcher identiche');
|
|
544
|
+
}
|
|
545
|
+
const constructible = present.length === 2 && problems.length === 0;
|
|
546
|
+
const ok = mode !== 'authority' || constructible;
|
|
547
|
+
const out = {
|
|
548
|
+
name: 'Fleet identity authority mode',
|
|
549
|
+
ok,
|
|
550
|
+
detail: `mode=${mode}; credenziali: ${present.length === 2 ? 'entrambe presenti (0600)' : present.join('+') || 'nessuna'} `
|
|
551
|
+
+ `(${dir}); ${distinctLine}${problems.length ? '; problemi: ' + problems.join(', ') : ''}`,
|
|
481
552
|
};
|
|
553
|
+
if (mode !== 'authority') {
|
|
554
|
+
out.warn = true;
|
|
555
|
+
out.detail += ' — il launcher espone CODEX_APP_SERVER_IDENTITY_REQUIRED=0: provisiona con '
|
|
556
|
+
+ '`nexuscrew identity provision` per attivare l\'authority mode';
|
|
557
|
+
} else if (!constructible) {
|
|
558
|
+
out.detail += ' — authority: configured but unavailable: '
|
|
559
|
+
+ (problems.length ? problems.join(', ') : 'credentials not constructible')
|
|
560
|
+
+ ' — le celle protette (codex-vl) non partono [IDENTITY_AUTHORITY_UNAVAILABLE]';
|
|
561
|
+
}
|
|
562
|
+
void e;
|
|
563
|
+
return out;
|
|
482
564
|
}
|
|
483
565
|
|
|
484
566
|
function checkFleetDefinitions(home, fleetDefsPath, enabled = true) {
|
|
@@ -578,6 +660,7 @@ function doctor(opts = {}) {
|
|
|
578
660
|
checkAutossh(existsImpl),
|
|
579
661
|
checkSshPermitlisten(opts.sshVersion),
|
|
580
662
|
checkMcpIdentity(opts.env),
|
|
663
|
+
checkIdentityAuthorityMode({ home: opts.home, env: opts.env }),
|
|
581
664
|
checkTmuxServerCwd(platform, execImpl, { tmuxBin: opts.tmuxBin, procCwdImpl: opts.procCwdImpl }),
|
|
582
665
|
checkTmuxServerTermuxPreload(opts.env, execImpl, { platform, home, tmuxBin: opts.tmuxBin }),
|
|
583
666
|
];
|
|
@@ -601,6 +684,6 @@ module.exports = {
|
|
|
601
684
|
checkFleetServiceWorkingDirectory,
|
|
602
685
|
checkTmuxSurvival, checkUserLinger,
|
|
603
686
|
checkSshClient, checkAutossh, checkSshPermitlisten,
|
|
604
|
-
checkMcpIdentity, checkTermuxExec,
|
|
687
|
+
checkMcpIdentity, checkIdentityAuthorityMode, checkTermuxExec,
|
|
605
688
|
checkTmuxServerCwd, checkTmuxServerTermuxPreload,
|
|
606
689
|
};
|
package/lib/config.js
CHANGED
|
@@ -135,8 +135,58 @@ function envOverrides() {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// Precedence: baseDefaults < config.json < env < opts. [B2]
|
|
138
|
+
// Le credenziali authority vivono in due file 0600 nella dir
|
|
139
|
+
// dell'authority (default ~/.nexuscrew/identity-authority). Se esistono vengono
|
|
140
|
+
// caricate qui, senza MAI loggare i valori: builtin costruisce l'authority
|
|
141
|
+
// solo quando ENTRAMBE sono presenti e distinte.
|
|
142
|
+
function readIdentityAuthorityCredentials(cfg = {}) {
|
|
143
|
+
const fs = require('node:fs');
|
|
144
|
+
const crypto = require('node:crypto');
|
|
145
|
+
const dir = cfg.identityAuthorityDir
|
|
146
|
+
|| path.join(cfg.home || os.homedir(), '.nexuscrew', 'identity-authority');
|
|
147
|
+
// Enforced AT LOAD. Identical credentials or wrong permissions
|
|
148
|
+
// (files != 0600, dir != 0700) => the credentials are NOT loaded: the
|
|
149
|
+
// authority is not constructed and the protected launch is refused instead.
|
|
150
|
+
// The reason travels in identityAuthorityFault, and never carries a value.
|
|
151
|
+
const faults = [];
|
|
152
|
+
let dirMode = null;
|
|
153
|
+
try { dirMode = fs.lstatSync(dir).mode & 0o777; } catch (_) { faults.push('authority dir missing'); }
|
|
154
|
+
if (dirMode !== null && dirMode !== 0o700) faults.push('authority dir mode is not 0700');
|
|
155
|
+
const readOne = (name) => {
|
|
156
|
+
try {
|
|
157
|
+
const st = fs.lstatSync(path.join(dir, name));
|
|
158
|
+
if (!st.isFile()) { faults.push(`${name}: not a regular file`); return undefined; }
|
|
159
|
+
if ((st.mode & 0o777) !== 0o600) { faults.push(`${name}: mode is not 0600`); return undefined; }
|
|
160
|
+
const value = fs.readFileSync(path.join(dir, name), 'utf8').trim();
|
|
161
|
+
if (!value) { faults.push(`${name}: empty`); return undefined; }
|
|
162
|
+
return value;
|
|
163
|
+
} catch (_) { faults.push(`${name}: missing`); return undefined; }
|
|
164
|
+
};
|
|
165
|
+
const daemon = readOne('daemon.credential');
|
|
166
|
+
const launcher = readOne('launcher.credential');
|
|
167
|
+
if (daemon && launcher) {
|
|
168
|
+
let distinct;
|
|
169
|
+
try {
|
|
170
|
+
distinct = !crypto.timingSafeEqual(Buffer.from(daemon), Buffer.from(launcher));
|
|
171
|
+
} catch (_) { distinct = false; }
|
|
172
|
+
if (!distinct) faults.push('daemon and launcher credentials are identical');
|
|
173
|
+
}
|
|
174
|
+
const out = {};
|
|
175
|
+
if (faults.length) {
|
|
176
|
+
out.identityAuthorityFault = faults.join('; ');
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
if (daemon) out.identityDaemonCredential = daemon;
|
|
180
|
+
if (launcher) out.identityLauncherCredential = launcher;
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
|
|
138
184
|
function loadConfig(opts = {}) {
|
|
139
|
-
|
|
185
|
+
const merged = { ...baseDefaults(), ...readConfigJson(), ...envOverrides(), ...opts };
|
|
186
|
+
// I file di credenziale sono fallback: non sovrascrivono mai valori espliciti
|
|
187
|
+
// di config.json/env/opts (precedence invariata).
|
|
188
|
+
const creds = readIdentityAuthorityCredentials(merged);
|
|
189
|
+
return { ...merged, ...creds };
|
|
140
190
|
}
|
|
141
191
|
|
|
142
192
|
// Retrocompat: defaults() = baseDefaults + env (NO config.json — per test isolati
|
package/lib/fleet/builtin.js
CHANGED
|
@@ -700,11 +700,23 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
700
700
|
// leaseManager e launchBroker. L'authority resta server-side: nessuna
|
|
701
701
|
// credenziale o chiave HMAC attraversa il payload consegnato alla cella.
|
|
702
702
|
const identityMode = cfg.fleetIdentityMode || cfg.fleet?.identity?.mode || 'legacy';
|
|
703
|
+
// Identical credentials => the authority is NOT constructed (consistent with
|
|
704
|
+
// identity-authority, which would reject at runtime anyway), and the reason
|
|
705
|
+
// goes to the log.
|
|
706
|
+
const identityCredentialsDistinct = (typeof cfg.identityDaemonCredential === 'string'
|
|
707
|
+
&& typeof cfg.identityLauncherCredential === 'string')
|
|
708
|
+
? Buffer.from(cfg.identityDaemonCredential).compare(Buffer.from(cfg.identityLauncherCredential)) !== 0
|
|
709
|
+
: !!(cfg.identityDaemonCredential && cfg.identityLauncherCredential);
|
|
710
|
+
if (identityMode === 'authority' && !identityCredentialsDistinct && cfg.log) {
|
|
711
|
+
cfg.log('fleet identity: authority mode requested but daemon and launcher credentials '
|
|
712
|
+
+ 'are identical or missing; identity enforcement stays off (REQUIRED=0)');
|
|
713
|
+
}
|
|
703
714
|
const identityOwnerInstanceId = typeof cfg.identityOwnerInstanceId === 'function'
|
|
704
715
|
? cfg.identityOwnerInstanceId()
|
|
705
716
|
: (cfg.identityOwnerInstanceId || null);
|
|
706
717
|
const identityAuthority = cfg.identityAuthority || (
|
|
707
|
-
|
|
718
|
+
identityCredentialsDistinct
|
|
719
|
+
&& cfg.identityDaemonCredential && cfg.identityLauncherCredential
|
|
708
720
|
? createIdentityAuthority({
|
|
709
721
|
dir: cfg.identityAuthorityDir || path.join(home, '.nexuscrew', 'identity-authority'),
|
|
710
722
|
daemonCredential: cfg.identityDaemonCredential,
|
|
@@ -279,9 +279,102 @@
|
|
|
279
279
|
"supports_search_tool": false
|
|
280
280
|
},
|
|
281
281
|
{
|
|
282
|
-
"slug": "
|
|
283
|
-
"display_name": "
|
|
284
|
-
"description": "OpenCode Go:
|
|
282
|
+
"slug": "deepseek-v4-flash-vision-exp",
|
|
283
|
+
"display_name": "deepseek-v4-flash-vision-exp",
|
|
284
|
+
"description": "OpenCode Go: deepseek-v4-flash-vision-exp (vision)",
|
|
285
|
+
"default_reasoning_level": "high",
|
|
286
|
+
"supported_reasoning_levels": [
|
|
287
|
+
{
|
|
288
|
+
"effort": "low",
|
|
289
|
+
"description": "Fast responses with lighter reasoning"
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"effort": "high",
|
|
293
|
+
"description": "Greater reasoning depth for complex problems"
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"effort": "max",
|
|
297
|
+
"description": "Maximum reasoning depth"
|
|
298
|
+
}
|
|
299
|
+
],
|
|
300
|
+
"shell_type": "default",
|
|
301
|
+
"visibility": "list",
|
|
302
|
+
"supported_in_api": true,
|
|
303
|
+
"priority": 57,
|
|
304
|
+
"availability_nux": null,
|
|
305
|
+
"upgrade": null,
|
|
306
|
+
"base_instructions": "",
|
|
307
|
+
"supports_reasoning_summaries": true,
|
|
308
|
+
"default_reasoning_summary": "none",
|
|
309
|
+
"support_verbosity": false,
|
|
310
|
+
"default_verbosity": null,
|
|
311
|
+
"apply_patch_tool_type": null,
|
|
312
|
+
"web_search_tool_type": "text",
|
|
313
|
+
"truncation_policy": {
|
|
314
|
+
"mode": "tokens",
|
|
315
|
+
"limit": 1000000
|
|
316
|
+
},
|
|
317
|
+
"supports_parallel_tool_calls": false,
|
|
318
|
+
"supports_image_detail_original": true,
|
|
319
|
+
"context_window": 1000000,
|
|
320
|
+
"effective_context_window_percent": 95,
|
|
321
|
+
"experimental_supported_tools": [],
|
|
322
|
+
"input_modalities": [
|
|
323
|
+
"text",
|
|
324
|
+
"image"
|
|
325
|
+
],
|
|
326
|
+
"supports_search_tool": false
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"slug": "deepseek-v4.1-flash",
|
|
330
|
+
"display_name": "deepseek-v4.1-flash",
|
|
331
|
+
"description": "OpenCode Go: deepseek-v4.1-flash",
|
|
332
|
+
"default_reasoning_level": "high",
|
|
333
|
+
"supported_reasoning_levels": [
|
|
334
|
+
{
|
|
335
|
+
"effort": "low",
|
|
336
|
+
"description": "Fast responses with lighter reasoning"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"effort": "high",
|
|
340
|
+
"description": "Greater reasoning depth for complex problems"
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
"effort": "max",
|
|
344
|
+
"description": "Maximum reasoning depth"
|
|
345
|
+
}
|
|
346
|
+
],
|
|
347
|
+
"shell_type": "default",
|
|
348
|
+
"visibility": "list",
|
|
349
|
+
"supported_in_api": true,
|
|
350
|
+
"priority": 58,
|
|
351
|
+
"availability_nux": null,
|
|
352
|
+
"upgrade": null,
|
|
353
|
+
"base_instructions": "",
|
|
354
|
+
"supports_reasoning_summaries": true,
|
|
355
|
+
"default_reasoning_summary": "none",
|
|
356
|
+
"support_verbosity": false,
|
|
357
|
+
"default_verbosity": null,
|
|
358
|
+
"apply_patch_tool_type": null,
|
|
359
|
+
"web_search_tool_type": "text",
|
|
360
|
+
"truncation_policy": {
|
|
361
|
+
"mode": "tokens",
|
|
362
|
+
"limit": 1000000
|
|
363
|
+
},
|
|
364
|
+
"supports_parallel_tool_calls": false,
|
|
365
|
+
"supports_image_detail_original": false,
|
|
366
|
+
"context_window": 1000000,
|
|
367
|
+
"effective_context_window_percent": 95,
|
|
368
|
+
"experimental_supported_tools": [],
|
|
369
|
+
"input_modalities": [
|
|
370
|
+
"text"
|
|
371
|
+
],
|
|
372
|
+
"supports_search_tool": false
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"slug": "glm-5.3",
|
|
376
|
+
"display_name": "glm-5.3",
|
|
377
|
+
"description": "OpenCode Go: glm-5.3",
|
|
285
378
|
"default_reasoning_level": "medium",
|
|
286
379
|
"supported_reasoning_levels": [
|
|
287
380
|
{
|
|
@@ -300,7 +393,7 @@
|
|
|
300
393
|
"shell_type": "default",
|
|
301
394
|
"visibility": "list",
|
|
302
395
|
"supported_in_api": true,
|
|
303
|
-
"priority":
|
|
396
|
+
"priority": 59,
|
|
304
397
|
"availability_nux": null,
|
|
305
398
|
"upgrade": null,
|
|
306
399
|
"base_instructions": "",
|
|
@@ -312,11 +405,11 @@
|
|
|
312
405
|
"web_search_tool_type": "text",
|
|
313
406
|
"truncation_policy": {
|
|
314
407
|
"mode": "tokens",
|
|
315
|
-
"limit":
|
|
408
|
+
"limit": 1000000
|
|
316
409
|
},
|
|
317
410
|
"supports_parallel_tool_calls": false,
|
|
318
411
|
"supports_image_detail_original": false,
|
|
319
|
-
"context_window":
|
|
412
|
+
"context_window": 1000000,
|
|
320
413
|
"effective_context_window_percent": 95,
|
|
321
414
|
"experimental_supported_tools": [],
|
|
322
415
|
"input_modalities": [
|
|
@@ -325,28 +418,74 @@
|
|
|
325
418
|
"supports_search_tool": false
|
|
326
419
|
},
|
|
327
420
|
{
|
|
328
|
-
"slug": "
|
|
329
|
-
"display_name": "
|
|
330
|
-
"description": "OpenCode Go:
|
|
331
|
-
"default_reasoning_level": "
|
|
421
|
+
"slug": "glm-5.3-flash",
|
|
422
|
+
"display_name": "glm-5.3-flash",
|
|
423
|
+
"description": "OpenCode Go: glm-5.3-flash",
|
|
424
|
+
"default_reasoning_level": "medium",
|
|
332
425
|
"supported_reasoning_levels": [
|
|
333
426
|
{
|
|
334
427
|
"effort": "low",
|
|
335
428
|
"description": "Fast responses with lighter reasoning"
|
|
336
429
|
},
|
|
430
|
+
{
|
|
431
|
+
"effort": "medium",
|
|
432
|
+
"description": "Balanced reasoning depth"
|
|
433
|
+
},
|
|
337
434
|
{
|
|
338
435
|
"effort": "high",
|
|
339
436
|
"description": "Greater reasoning depth for complex problems"
|
|
437
|
+
}
|
|
438
|
+
],
|
|
439
|
+
"shell_type": "default",
|
|
440
|
+
"visibility": "list",
|
|
441
|
+
"supported_in_api": true,
|
|
442
|
+
"priority": 60,
|
|
443
|
+
"availability_nux": null,
|
|
444
|
+
"upgrade": null,
|
|
445
|
+
"base_instructions": "",
|
|
446
|
+
"supports_reasoning_summaries": true,
|
|
447
|
+
"default_reasoning_summary": "none",
|
|
448
|
+
"support_verbosity": false,
|
|
449
|
+
"default_verbosity": null,
|
|
450
|
+
"apply_patch_tool_type": null,
|
|
451
|
+
"web_search_tool_type": "text",
|
|
452
|
+
"truncation_policy": {
|
|
453
|
+
"mode": "tokens",
|
|
454
|
+
"limit": 1000000
|
|
455
|
+
},
|
|
456
|
+
"supports_parallel_tool_calls": false,
|
|
457
|
+
"supports_image_detail_original": false,
|
|
458
|
+
"context_window": 1000000,
|
|
459
|
+
"effective_context_window_percent": 95,
|
|
460
|
+
"experimental_supported_tools": [],
|
|
461
|
+
"input_modalities": [
|
|
462
|
+
"text"
|
|
463
|
+
],
|
|
464
|
+
"supports_search_tool": false
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"slug": "longcat-2.0",
|
|
468
|
+
"display_name": "longcat-2.0",
|
|
469
|
+
"description": "OpenCode Go: longcat-2.0",
|
|
470
|
+
"default_reasoning_level": "medium",
|
|
471
|
+
"supported_reasoning_levels": [
|
|
472
|
+
{
|
|
473
|
+
"effort": "low",
|
|
474
|
+
"description": "Fast responses with lighter reasoning"
|
|
340
475
|
},
|
|
341
476
|
{
|
|
342
|
-
"effort": "
|
|
343
|
-
"description": "
|
|
477
|
+
"effort": "medium",
|
|
478
|
+
"description": "Balanced reasoning depth"
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
"effort": "high",
|
|
482
|
+
"description": "Greater reasoning depth for complex problems"
|
|
344
483
|
}
|
|
345
484
|
],
|
|
346
485
|
"shell_type": "default",
|
|
347
486
|
"visibility": "list",
|
|
348
487
|
"supported_in_api": true,
|
|
349
|
-
"priority":
|
|
488
|
+
"priority": 61,
|
|
350
489
|
"availability_nux": null,
|
|
351
490
|
"upgrade": null,
|
|
352
491
|
"base_instructions": "",
|
|
@@ -361,13 +500,58 @@
|
|
|
361
500
|
"limit": 1000000
|
|
362
501
|
},
|
|
363
502
|
"supports_parallel_tool_calls": false,
|
|
364
|
-
"supports_image_detail_original":
|
|
503
|
+
"supports_image_detail_original": false,
|
|
365
504
|
"context_window": 1000000,
|
|
366
505
|
"effective_context_window_percent": 95,
|
|
367
506
|
"experimental_supported_tools": [],
|
|
368
507
|
"input_modalities": [
|
|
369
|
-
"text"
|
|
370
|
-
|
|
508
|
+
"text"
|
|
509
|
+
],
|
|
510
|
+
"supports_search_tool": false
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
"slug": "hy4-preview",
|
|
514
|
+
"display_name": "hy4-preview",
|
|
515
|
+
"description": "OpenCode Go: hy4-preview",
|
|
516
|
+
"default_reasoning_level": "medium",
|
|
517
|
+
"supported_reasoning_levels": [
|
|
518
|
+
{
|
|
519
|
+
"effort": "low",
|
|
520
|
+
"description": "Fast responses with lighter reasoning"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"effort": "medium",
|
|
524
|
+
"description": "Balanced reasoning depth"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
"effort": "high",
|
|
528
|
+
"description": "Greater reasoning depth for complex problems"
|
|
529
|
+
}
|
|
530
|
+
],
|
|
531
|
+
"shell_type": "default",
|
|
532
|
+
"visibility": "list",
|
|
533
|
+
"supported_in_api": true,
|
|
534
|
+
"priority": 62,
|
|
535
|
+
"availability_nux": null,
|
|
536
|
+
"upgrade": null,
|
|
537
|
+
"base_instructions": "",
|
|
538
|
+
"supports_reasoning_summaries": true,
|
|
539
|
+
"default_reasoning_summary": "none",
|
|
540
|
+
"support_verbosity": false,
|
|
541
|
+
"default_verbosity": null,
|
|
542
|
+
"apply_patch_tool_type": null,
|
|
543
|
+
"web_search_tool_type": "text",
|
|
544
|
+
"truncation_policy": {
|
|
545
|
+
"mode": "tokens",
|
|
546
|
+
"limit": 1024000
|
|
547
|
+
},
|
|
548
|
+
"supports_parallel_tool_calls": false,
|
|
549
|
+
"supports_image_detail_original": false,
|
|
550
|
+
"context_window": 1024000,
|
|
551
|
+
"effective_context_window_percent": 95,
|
|
552
|
+
"experimental_supported_tools": [],
|
|
553
|
+
"input_modalities": [
|
|
554
|
+
"text"
|
|
371
555
|
],
|
|
372
556
|
"supports_search_tool": false
|
|
373
557
|
}
|