nexus-agents 8.74.1 → 8.74.2

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.
Files changed (24) hide show
  1. package/dist/{chunk-3FK67YI4.js → chunk-6Y24JDBR.js} +7 -7
  2. package/dist/{chunk-4XP43CH7.js → chunk-D5ST2Y4W.js} +2 -2
  3. package/dist/{chunk-MPWPST6S.js → chunk-I2KBD37Y.js} +2 -2
  4. package/dist/{chunk-BQMUX3TR.js → chunk-M6DJ6AE3.js} +3 -3
  5. package/dist/{chunk-MYEDID77.js → chunk-PWEGOADT.js} +3 -3
  6. package/dist/{chunk-XJI5YBBR.js → chunk-QW5FSTLJ.js} +2 -2
  7. package/dist/cli.js +9 -9
  8. package/dist/{consensus-vote-5RFJPG5F.js → consensus-vote-YCETKP4M.js} +4 -4
  9. package/dist/{improvement-review-LDBUOL7B.js → improvement-review-CF5SWXUB.js} +4 -4
  10. package/dist/index.js +6 -6
  11. package/dist/{init-opencode-UFYH4SDG.js → init-opencode-UTF4K2LR.js} +3 -2
  12. package/dist/init-opencode-UTF4K2LR.js.map +1 -0
  13. package/dist/{setup-data-dir-I2Q4KJ7M.js → setup-data-dir-6PEJ3YF7.js} +3 -3
  14. package/package.json +1 -1
  15. package/dist/init-opencode-UFYH4SDG.js.map +0 -1
  16. /package/dist/{chunk-3FK67YI4.js.map → chunk-6Y24JDBR.js.map} +0 -0
  17. /package/dist/{chunk-4XP43CH7.js.map → chunk-D5ST2Y4W.js.map} +0 -0
  18. /package/dist/{chunk-MPWPST6S.js.map → chunk-I2KBD37Y.js.map} +0 -0
  19. /package/dist/{chunk-BQMUX3TR.js.map → chunk-M6DJ6AE3.js.map} +0 -0
  20. /package/dist/{chunk-MYEDID77.js.map → chunk-PWEGOADT.js.map} +0 -0
  21. /package/dist/{chunk-XJI5YBBR.js.map → chunk-QW5FSTLJ.js.map} +0 -0
  22. /package/dist/{consensus-vote-5RFJPG5F.js.map → consensus-vote-YCETKP4M.js.map} +0 -0
  23. /package/dist/{improvement-review-LDBUOL7B.js.map → improvement-review-CF5SWXUB.js.map} +0 -0
  24. /package/dist/{setup-data-dir-I2Q4KJ7M.js.map → setup-data-dir-6PEJ3YF7.js.map} +0 -0
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  DEFAULT_TASK_TTL_MS,
23
23
  clampTaskTtl
24
- } from "./chunk-MYEDID77.js";
24
+ } from "./chunk-PWEGOADT.js";
25
25
  import {
26
26
  executeExpert
27
27
  } from "./chunk-ZKEULHX6.js";
@@ -71,7 +71,7 @@ import {
71
71
  updateStage,
72
72
  withWarnings,
73
73
  writeJobCancelled
74
- } from "./chunk-BQMUX3TR.js";
74
+ } from "./chunk-M6DJ6AE3.js";
75
75
  import {
76
76
  allOf,
77
77
  anyOf
@@ -93,7 +93,7 @@ import {
93
93
  } from "./chunk-ADHIHFBA.js";
94
94
  import {
95
95
  withPrerequisite
96
- } from "./chunk-MPWPST6S.js";
96
+ } from "./chunk-I2KBD37Y.js";
97
97
  import {
98
98
  NOOP_NOTIFIER,
99
99
  RateLimiter,
@@ -116,7 +116,7 @@ import {
116
116
  toolSuccessStructured,
117
117
  withProgressHeartbeat,
118
118
  wrapToolWithTimeout
119
- } from "./chunk-4XP43CH7.js";
119
+ } from "./chunk-D5ST2Y4W.js";
120
120
  import {
121
121
  EventTopics,
122
122
  UNRESOLVED_MODEL_ID,
@@ -45863,7 +45863,7 @@ function buildVotingInput(plan, config) {
45863
45863
  }
45864
45864
  async function executeSingleVote(plan, config, log) {
45865
45865
  try {
45866
- const { executeVoting } = await import("./consensus-vote-5RFJPG5F.js");
45866
+ const { executeVoting } = await import("./consensus-vote-YCETKP4M.js");
45867
45867
  const input = buildVotingInput(plan, config);
45868
45868
  const result = await executeVoting(input, log);
45869
45869
  return parseVotingResult(result);
@@ -47667,7 +47667,7 @@ ${contextBlock}`;
47667
47667
  const strategy = config.votingStrategy ?? "higher_order";
47668
47668
  await postProgress(config, "Vote", `Running consensus with ${strategy} strategy...`);
47669
47669
  try {
47670
- const { executeVoting } = await import("./consensus-vote-5RFJPG5F.js");
47670
+ const { executeVoting } = await import("./consensus-vote-YCETKP4M.js");
47671
47671
  const votingResult = await executeVoting(
47672
47672
  {
47673
47673
  proposal: buildVoteProposal(plan, research),
@@ -56917,4 +56917,4 @@ export {
56917
56917
  startPipelineEventBridge,
56918
56918
  shutdownPipelineEventBridge
56919
56919
  };
56920
- //# sourceMappingURL=chunk-3FK67YI4.js.map
56920
+ //# sourceMappingURL=chunk-6Y24JDBR.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-XJI5YBBR.js";
3
+ } from "./chunk-QW5FSTLJ.js";
4
4
  import {
5
5
  CACHE_TIMEOUTS,
6
6
  MCP_TIMEOUTS,
@@ -2186,4 +2186,4 @@ export {
2186
2186
  DEFAULT_CONSENSUS_CONFIG,
2187
2187
  ConsensusMetricsSchema
2188
2188
  };
2189
- //# sourceMappingURL=chunk-4XP43CH7.js.map
2189
+ //# sourceMappingURL=chunk-D5ST2Y4W.js.map
@@ -7,7 +7,7 @@ import {
7
7
  toolStructuredError,
8
8
  toolSuccessStructured,
9
9
  wrapToolWithTimeout
10
- } from "./chunk-4XP43CH7.js";
10
+ } from "./chunk-D5ST2Y4W.js";
11
11
  import {
12
12
  JsonlStore,
13
13
  SECURITY_KEYWORDS,
@@ -2282,4 +2282,4 @@ export {
2282
2282
  runImprovementReview,
2283
2283
  registerImprovementReviewTool
2284
2284
  };
2285
- //# sourceMappingURL=chunk-MPWPST6S.js.map
2285
+ //# sourceMappingURL=chunk-I2KBD37Y.js.map
@@ -25,10 +25,10 @@ import {
25
25
  toolSuccessStructured,
26
26
  withProgressHeartbeat,
27
27
  wrapToolWithTimeout
28
- } from "./chunk-4XP43CH7.js";
28
+ } from "./chunk-D5ST2Y4W.js";
29
29
  import {
30
30
  VERSION
31
- } from "./chunk-XJI5YBBR.js";
31
+ } from "./chunk-QW5FSTLJ.js";
32
32
  import {
33
33
  UNRESOLVED_MODEL_ID,
34
34
  assessPanelIndependence,
@@ -9178,4 +9178,4 @@ export {
9178
9178
  CONSENSUS_VOTE_TOOL_SCHEMA,
9179
9179
  registerConsensusVoteTool
9180
9180
  };
9181
- //# sourceMappingURL=chunk-BQMUX3TR.js.map
9181
+ //# sourceMappingURL=chunk-M6DJ6AE3.js.map
@@ -11,7 +11,7 @@ import {
11
11
  NODE_ENGINE_RANGE,
12
12
  VERSION,
13
13
  isNodeVersionSupported
14
- } from "./chunk-XJI5YBBR.js";
14
+ } from "./chunk-QW5FSTLJ.js";
15
15
  import {
16
16
  claimGlobalRegistry
17
17
  } from "./chunk-TL5NV5JH.js";
@@ -1342,7 +1342,7 @@ async function runDoctorFix(result) {
1342
1342
  writeLine2("\u2500".repeat(40));
1343
1343
  let fixCount = 0;
1344
1344
  if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
1345
- const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-I2Q4KJ7M.js");
1345
+ const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-6PEJ3YF7.js");
1346
1346
  const outcome = describeDataDirFix2(initDataDirectories2());
1347
1347
  writeLine2(outcome.line);
1348
1348
  if (outcome.counted) fixCount++;
@@ -1444,4 +1444,4 @@ export {
1444
1444
  startStdioServer,
1445
1445
  closeServer
1446
1446
  };
1447
- //# sourceMappingURL=chunk-MYEDID77.js.map
1447
+ //# sourceMappingURL=chunk-PWEGOADT.js.map
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import semver from "semver";
3
- var VERSION = true ? "8.74.1" : "dev";
3
+ var VERSION = true ? "8.74.2" : "dev";
4
4
  var NODE_ENGINE_RANGE = ">=22.5.0";
5
5
  function isNodeVersionSupported(version) {
6
6
  return semver.satisfies(version, NODE_ENGINE_RANGE);
@@ -11,4 +11,4 @@ export {
11
11
  NODE_ENGINE_RANGE,
12
12
  isNodeVersionSupported
13
13
  };
14
- //# sourceMappingURL=chunk-XJI5YBBR.js.map
14
+ //# sourceMappingURL=chunk-QW5FSTLJ.js.map
package/dist/cli.js CHANGED
@@ -156,7 +156,7 @@ import {
156
156
  validateNexusEnv,
157
157
  validateWorkflow,
158
158
  wrapInMarkdownFence
159
- } from "./chunk-3FK67YI4.js";
159
+ } from "./chunk-6Y24JDBR.js";
160
160
  import "./chunk-CYZ3DUQV.js";
161
161
  import "./chunk-4GDMHCKX.js";
162
162
  import "./chunk-M7DUOIAK.js";
@@ -176,7 +176,7 @@ import {
176
176
  doctorCommand,
177
177
  initDataDirectories,
178
178
  runDoctor
179
- } from "./chunk-MYEDID77.js";
179
+ } from "./chunk-PWEGOADT.js";
180
180
  import "./chunk-43RJNTUZ.js";
181
181
  import {
182
182
  buildOpenAICompatAdapters,
@@ -202,7 +202,7 @@ import {
202
202
  registerConsensusVoteTool,
203
203
  seatFallbacks,
204
204
  toRecordDecision
205
- } from "./chunk-BQMUX3TR.js";
205
+ } from "./chunk-M6DJ6AE3.js";
206
206
  import {
207
207
  loadUsageEvents,
208
208
  rollupByModel
@@ -243,18 +243,18 @@ import {
243
243
  registerImprovementReviewTool,
244
244
  runImprovementReview,
245
245
  scanForSecrets
246
- } from "./chunk-MPWPST6S.js";
246
+ } from "./chunk-I2KBD37Y.js";
247
247
  import {
248
248
  createDefaultPolicyFirewall,
249
249
  setGlobalPolicyFirewall,
250
250
  setSecureHandlerAuditLogger,
251
251
  stagePolicyFirewallForRollout
252
- } from "./chunk-4XP43CH7.js";
252
+ } from "./chunk-D5ST2Y4W.js";
253
253
  import {
254
254
  NODE_ENGINE_RANGE,
255
255
  VERSION,
256
256
  isNodeVersionSupported
257
- } from "./chunk-XJI5YBBR.js";
257
+ } from "./chunk-QW5FSTLJ.js";
258
258
  import {
259
259
  claimGlobalRegistry,
260
260
  createUnifiedRegistry,
@@ -19679,7 +19679,7 @@ function startImprovementReviewScheduler(options) {
19679
19679
  let running = false;
19680
19680
  const runOnce = async () => {
19681
19681
  try {
19682
- const { runImprovementReview: runImprovementReview2, ImprovementReviewInputSchema: ImprovementReviewInputSchema2 } = await import("./improvement-review-LDBUOL7B.js");
19682
+ const { runImprovementReview: runImprovementReview2, ImprovementReviewInputSchema: ImprovementReviewInputSchema2 } = await import("./improvement-review-CF5SWXUB.js");
19683
19683
  const input = ImprovementReviewInputSchema2.parse({ fileIssues });
19684
19684
  const result = await runImprovementReview2(input, { logger: logger22 });
19685
19685
  logger22.info("Scheduled improvement_review complete", {
@@ -23839,7 +23839,7 @@ async function runInitPortableFlow(args) {
23839
23839
  return cliExit(result.success ? EXIT_CODES.SUCCESS : EXIT_CODES.SERVER_START_FAILED);
23840
23840
  }
23841
23841
  async function runInitOpencodeFlow(args) {
23842
- const { runInitOpencode } = await import("./init-opencode-UFYH4SDG.js");
23842
+ const { runInitOpencode } = await import("./init-opencode-UTF4K2LR.js");
23843
23843
  const opencodePath = args.options.opencode;
23844
23844
  if (opencodePath === void 0 || opencodePath === "") {
23845
23845
  process.stderr.write("Error: --opencode requires a path argument.\n");
@@ -23865,7 +23865,7 @@ async function runInitOpencodeFlow(args) {
23865
23865
  return cliExit(EXIT_CODES.SUCCESS);
23866
23866
  }
23867
23867
  async function renderOpencodeValidate(opencodePath) {
23868
- const { runOpencodeValidate } = await import("./init-opencode-UFYH4SDG.js");
23868
+ const { runOpencodeValidate } = await import("./init-opencode-UTF4K2LR.js");
23869
23869
  const result = await runOpencodeValidate(opencodePath);
23870
23870
  if (!result.ok) {
23871
23871
  process.stderr.write(`init --opencode --validate: ${result.reason ?? "failed"}
@@ -10,11 +10,11 @@ import {
10
10
  resetCorrelationTracker,
11
11
  runConsensusForGoal,
12
12
  unwrapVoteOrThrow
13
- } from "./chunk-BQMUX3TR.js";
13
+ } from "./chunk-M6DJ6AE3.js";
14
14
  import "./chunk-47624RRN.js";
15
15
  import "./chunk-OQB3XC5O.js";
16
- import "./chunk-4XP43CH7.js";
17
- import "./chunk-XJI5YBBR.js";
16
+ import "./chunk-D5ST2Y4W.js";
17
+ import "./chunk-QW5FSTLJ.js";
18
18
  import "./chunk-TL5NV5JH.js";
19
19
  import "./chunk-B227FYGQ.js";
20
20
  import "./chunk-7LP6VJRP.js";
@@ -47,4 +47,4 @@ export {
47
47
  runConsensusForGoal,
48
48
  unwrapVoteOrThrow
49
49
  };
50
- //# sourceMappingURL=consensus-vote-5RFJPG5F.js.map
50
+ //# sourceMappingURL=consensus-vote-YCETKP4M.js.map
@@ -10,9 +10,9 @@ import {
10
10
  loadSelfEvalSignals,
11
11
  registerImprovementReviewTool,
12
12
  runImprovementReview
13
- } from "./chunk-MPWPST6S.js";
14
- import "./chunk-4XP43CH7.js";
15
- import "./chunk-XJI5YBBR.js";
13
+ } from "./chunk-I2KBD37Y.js";
14
+ import "./chunk-D5ST2Y4W.js";
15
+ import "./chunk-QW5FSTLJ.js";
16
16
  import "./chunk-4PWDDMLH.js";
17
17
  import "./chunk-4UHDDCRB.js";
18
18
  import "./chunk-RNDHNRSX.js";
@@ -30,4 +30,4 @@ export {
30
30
  registerImprovementReviewTool,
31
31
  runImprovementReview
32
32
  };
33
- //# sourceMappingURL=improvement-review-LDBUOL7B.js.map
33
+ //# sourceMappingURL=improvement-review-CF5SWXUB.js.map
package/dist/index.js CHANGED
@@ -552,7 +552,7 @@ import {
552
552
  withLogging,
553
553
  withRetry,
554
554
  withRetryWrapper
555
- } from "./chunk-3FK67YI4.js";
555
+ } from "./chunk-6Y24JDBR.js";
556
556
  import {
557
557
  FALLBACK_SCANNER_DATA,
558
558
  buildPlanFromAnalysis,
@@ -585,7 +585,7 @@ import {
585
585
  connectTransport,
586
586
  createServer,
587
587
  startStdioServer
588
- } from "./chunk-MYEDID77.js";
588
+ } from "./chunk-PWEGOADT.js";
589
589
  import "./chunk-43RJNTUZ.js";
590
590
  import {
591
591
  OPENAI_MODELS,
@@ -644,7 +644,7 @@ import {
644
644
  generateProposalId,
645
645
  parseAgentPairKey,
646
646
  registerConsensusVoteTool
647
- } from "./chunk-BQMUX3TR.js";
647
+ } from "./chunk-M6DJ6AE3.js";
648
648
  import "./chunk-47624RRN.js";
649
649
  import {
650
650
  allOf
@@ -711,7 +711,7 @@ import {
711
711
  import {
712
712
  ImprovementReviewInputSchema,
713
713
  registerImprovementReviewTool
714
- } from "./chunk-MPWPST6S.js";
714
+ } from "./chunk-I2KBD37Y.js";
715
715
  import {
716
716
  AgentPerformanceSchema,
717
717
  ConsensusAlgorithmSchema,
@@ -748,10 +748,10 @@ import {
748
748
  toolSuccess,
749
749
  toolSuccessStructured,
750
750
  validateToolInput
751
- } from "./chunk-4XP43CH7.js";
751
+ } from "./chunk-D5ST2Y4W.js";
752
752
  import {
753
753
  VERSION
754
- } from "./chunk-XJI5YBBR.js";
754
+ } from "./chunk-QW5FSTLJ.js";
755
755
  import {
756
756
  RegistryAlreadyInitializedError,
757
757
  UnifiedAdapterRegistry,
@@ -35,7 +35,8 @@ function buildDefaultPermissionBlock() {
35
35
  function buildNexusMcpBlock(opts) {
36
36
  const env = {
37
37
  NEXUS_DATA_DIR: "{env:NEXUS_DATA_DIR}",
38
- NEXUS_OPENCODE_CONFIG: opts.opencodeConfigPath
38
+ NEXUS_OPENCODE_CONFIG: opts.opencodeConfigPath,
39
+ NEXUS_GATEWAY_COST: "openai-compat=free"
39
40
  };
40
41
  if (opts.sandboxFlavor !== void 0 && opts.sandboxFlavor !== "") {
41
42
  env["NEXUS_SANDBOX"] = opts.sandboxFlavor;
@@ -176,4 +177,4 @@ export {
176
177
  runInitOpencode,
177
178
  runOpencodeValidate
178
179
  };
179
- //# sourceMappingURL=init-opencode-UFYH4SDG.js.map
180
+ //# sourceMappingURL=init-opencode-UTF4K2LR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/init-opencode.ts"],"sourcesContent":["/**\n * `nexus-agents init --opencode <path>` setup helper (#2504, child 4 of #2500).\n *\n * Injects the nexus-agents MCP block + recommended environment into an\n * existing `opencode.json` without overwriting other operator-set keys.\n * Pairs with the existing `init --portable --mcp-config` (which targets\n * Claude Code's `.mcp.json`). Different harness, same merge-not-overwrite\n * pattern.\n *\n * Behaviour:\n * - File exists → merge `mcp.nexus-agents` into the existing JSON.\n * Preserve every other key the operator has set (provider config,\n * model lists, theme, …). Idempotent: re-running produces the same\n * final file.\n * - File missing → write a minimal template with the nexus-agents MCP\n * block + a stubbed `providers.openai-compat` shell the operator\n * fills in (placeholder baseURL + `{env:WORKSPACE_PROXY_KEY}`).\n * - `--dry-run` → print the diff (proposed vs existing) without writing.\n * - `--validate` → after merge (or alongside dry-run), probe\n * `providers.openai-compat.options.baseURL/v1/models` with the\n * resolved apiKey, exit non-zero if unreachable.\n *\n * @module cli/init-opencode\n */\n\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nimport { createLogger } from '../core/index.js';\nimport { readOpencodeGateway } from '../config/opencode-bridge.js';\nimport { discoverModels } from '../adapters/openai-compat-adapter.js';\n\nconst logger = createLogger({ component: 'init-opencode' });\n\nexport interface InitOpencodeOptions {\n /** Absolute or relative path to opencode.json. */\n readonly path: string;\n /** Path to the nexus-agents CLI binary that the MCP block will spawn. */\n readonly cliPath: string;\n /** Sandbox flavor written to NEXUS_SANDBOX in the MCP environment. */\n readonly sandboxFlavor?: string;\n /** Print the diff without writing. */\n readonly dryRun?: boolean;\n}\n\nexport interface InitOpencodeResult {\n readonly path: string;\n readonly action: 'created' | 'updated' | 'unchanged' | 'dry-run';\n readonly diff: string;\n}\n\ninterface OpencodeMcpBlock {\n type: 'local';\n command: string[];\n enabled: boolean;\n environment: Record<string, string>;\n}\n\ninterface OpencodeFile {\n $schema?: string;\n providers?: Record<string, unknown>;\n mcp?: Record<string, unknown>;\n permission?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Conservative default OpenCode `permission` block (#2658). OpenCode\n * evaluates a glob map with **last-matching-rule-winning**, so the broad\n * `\"*\"` rule is listed first and the hard `deny` patterns last.\n *\n * - `edit` — everything `ask`s; secrets, keys, and `.git/` are hard-denied\n * regardless of operator confirmation. The deny patterns come after\n * `\"*\"` so last-match-wins makes them stick.\n * - `bash` — `ask` unconditionally. Shell is the highest-risk surface.\n * - `skill` — `allow`. Skills are trusted, in-repo, CI-validated content.\n *\n * Emitted only when the operator has not already set their own\n * `permission` block — never overwrites operator config.\n */\nexport function buildDefaultPermissionBlock(): Record<string, unknown> {\n return {\n edit: {\n '*': 'ask',\n '**/.env': 'deny',\n '**/.env.*': 'deny',\n '**/*.pem': 'deny',\n '**/*.key': 'deny',\n '**/id_rsa*': 'deny',\n '**/secrets/**': 'deny',\n '**/.git/**': 'deny',\n },\n bash: 'ask',\n skill: 'allow',\n };\n}\n\n/**\n * Build the canonical nexus-agents MCP block. Mirrors what\n * `Dockerfile.sandbox` writes today plus the env vars from #2501 + #2503.\n *\n * `NEXUS_GATEWAY_COST` (#4392): the gateway this file configures is the\n * `providers.openai-compat` block, so its arm is declared `free` under\n * that endpoint key. Scoped, never bare — a bare `free` would also declare\n * any second gateway the operator adds later. `free`, not `local` — init\n * cannot know whether the proxy is on-box. A hand-edited value survives a\n * re-run: `mergeNexusBlock` spreads the existing environment over this one.\n */\nexport function buildNexusMcpBlock(opts: {\n readonly cliPath: string;\n readonly sandboxFlavor?: string;\n readonly opencodeConfigPath: string;\n}): OpencodeMcpBlock {\n const env: Record<string, string> = {\n NEXUS_DATA_DIR: '{env:NEXUS_DATA_DIR}',\n NEXUS_OPENCODE_CONFIG: opts.opencodeConfigPath,\n NEXUS_GATEWAY_COST: 'openai-compat=free',\n };\n if (opts.sandboxFlavor !== undefined && opts.sandboxFlavor !== '') {\n env['NEXUS_SANDBOX'] = opts.sandboxFlavor;\n }\n return {\n type: 'local',\n command: ['node', opts.cliPath, '--mode=server'],\n enabled: true,\n environment: env,\n };\n}\n\n/**\n * Execute the merge. Pure with respect to `opts.dryRun: true` — no fs\n * writes. Returns the final-or-proposed JSON + a diff string suitable\n * for printing.\n */\nexport function runInitOpencode(opts: InitOpencodeOptions): InitOpencodeResult {\n const existing = readExisting(opts.path);\n const merged = mergeNexusBlock(existing, opts);\n const before = existing === null ? '' : `${JSON.stringify(existing, null, 2)}\\n`;\n const after = `${JSON.stringify(merged, null, 2)}\\n`;\n const diff = simpleDiff(before, after);\n\n if (opts.dryRun === true) {\n return { path: opts.path, action: 'dry-run', diff };\n }\n\n if (existing !== null && before === after) {\n return { path: opts.path, action: 'unchanged', diff };\n }\n\n writeFileSync(opts.path, after, 'utf8');\n return { path: opts.path, action: existing === null ? 'created' : 'updated', diff };\n}\n\nfunction readExisting(path: string): OpencodeFile | null {\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, 'utf8');\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== 'object' || parsed === null) {\n throw new Error('opencode.json root must be an object');\n }\n return parsed as OpencodeFile;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to parse opencode.json at ${path}: ${msg}`);\n }\n}\n\nfunction mergeNexusBlock(existing: OpencodeFile | null, opts: InitOpencodeOptions): OpencodeFile {\n const block = buildNexusMcpBlock({\n cliPath: opts.cliPath,\n ...(opts.sandboxFlavor !== undefined && { sandboxFlavor: opts.sandboxFlavor }),\n opencodeConfigPath: opts.path,\n });\n\n if (existing === null) {\n return {\n $schema: 'https://opencode.ai/config.json',\n providers: { 'openai-compat': stubOpenaiCompatProvider() },\n permission: buildDefaultPermissionBlock(),\n mcp: { 'nexus-agents': block },\n };\n }\n\n // Idempotent merge: preserve user-customised fields if they exist on the\n // current MCP block (e.g. `enabled: false`). Update anything else.\n const existingMcp: Record<string, unknown> = existing.mcp ?? {};\n const rawNexus = existingMcp['nexus-agents'];\n const existingNexus: Partial<OpencodeMcpBlock> =\n typeof rawNexus === 'object' && rawNexus !== null ? rawNexus : {};\n const mergedBlock: OpencodeMcpBlock = {\n type: 'local',\n command: block.command,\n enabled: existingNexus.enabled ?? block.enabled,\n environment: { ...block.environment, ...(existingNexus.environment ?? {}) },\n };\n\n // Add the default permission block only when the operator has not set\n // their own — never overwrite an existing `permission` key.\n const permission = existing.permission ?? buildDefaultPermissionBlock();\n\n return {\n ...existing,\n permission,\n mcp: { ...existingMcp, 'nexus-agents': mergedBlock },\n };\n}\n\n/**\n * Stub for `providers.openai-compat`. Operators replace the placeholder\n * baseURL + key with their workspace-proxy values. Empty by design — we\n * never overwrite an existing provider block (see `mergeNexusBlock`).\n */\nfunction stubOpenaiCompatProvider(): Record<string, unknown> {\n return {\n npm: '@ai-sdk/openai-compatible',\n options: {\n baseURL: '<replace with workspace proxy URL>',\n apiKey: '{env:WORKSPACE_PROXY_KEY}',\n },\n };\n}\n\n/**\n * Cheap line-based diff. Three-line context, +/- prefixes. Good enough\n * for an operator preview; not a unified-diff replacement.\n */\nfunction simpleDiff(before: string, after: string): string {\n if (before === after) return '(no changes)';\n const beforeLines = before.split('\\n');\n const afterLines = after.split('\\n');\n const out: string[] = [];\n const max = Math.max(beforeLines.length, afterLines.length);\n for (let i = 0; i < max; i += 1) {\n const b = beforeLines[i];\n const a = afterLines[i];\n if (b === a) {\n out.push(` ${b ?? ''}`);\n } else {\n if (b !== undefined) out.push(`- ${b}`);\n if (a !== undefined) out.push(`+ ${a}`);\n }\n }\n return out.join('\\n');\n}\n\nexport function ensureOpencodeDirExists(path: string): void {\n const dir = dirname(path);\n if (!existsSync(dir)) {\n throw new Error(\n `Parent directory does not exist: ${dir}. Create it first or pass a path under an existing dir.`\n );\n }\n logger.debug('opencode.json target directory exists', { dir });\n}\n\n/**\n * --validate flow for `init --opencode` (follow-up to #2504).\n *\n * After the merge step writes the file, optionally probe the gateway it\n * points at. Reuses the same opencode-bridge loader (#2503) that the\n * runtime uses, so what we validate is exactly what the server will see\n * at boot.\n *\n * Returns:\n * - `{ ok: true, models }` when the gateway is reachable and returns ≥1 model\n * - `{ ok: false, reason }` for every failure path (config missing,\n * probe failed, zero models)\n *\n * Caller (cli-commands-handlers) maps to exit codes + stderr / stdout.\n * The API key never reaches the returned object — only model IDs +\n * baseURL appear in the success payload.\n */\nexport interface OpencodeValidateResult {\n readonly ok: boolean;\n readonly baseURL?: string;\n readonly models?: readonly string[];\n readonly reason?: string;\n}\n\nexport async function runOpencodeValidate(opencodePath: string): Promise<OpencodeValidateResult> {\n const config = readOpencodeGateway(opencodePath);\n if (config === null) {\n return {\n ok: false,\n reason:\n 'opencode.json does not resolve a usable gateway. Check that providers.openai-compat.options.{baseURL, apiKey} are set and that any {env:VAR} interpolation references are exported.',\n };\n }\n const result = await discoverModels({ baseUrl: config.baseURL, apiKey: config.apiKey });\n if (!result.ok) {\n return { ok: false, baseURL: config.baseURL, reason: result.error.message };\n }\n if (result.value.length === 0) {\n return {\n ok: false,\n baseURL: config.baseURL,\n reason: 'gateway returned 0 models. Check upstream provider quotas / list filters.',\n };\n }\n return {\n ok: true,\n baseURL: config.baseURL,\n models: result.value.map((m) => m.id),\n };\n}\n"],"mappings":"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","names":[]}
@@ -2,12 +2,12 @@ import {
2
2
  NEXUS_DATA_DIR,
3
3
  describeDataDirFix,
4
4
  initDataDirectories
5
- } from "./chunk-MYEDID77.js";
5
+ } from "./chunk-PWEGOADT.js";
6
6
  import "./chunk-43RJNTUZ.js";
7
7
  import "./chunk-HO2UPJ7T.js";
8
8
  import "./chunk-47624RRN.js";
9
9
  import "./chunk-OQB3XC5O.js";
10
- import "./chunk-XJI5YBBR.js";
10
+ import "./chunk-QW5FSTLJ.js";
11
11
  import "./chunk-TL5NV5JH.js";
12
12
  import "./chunk-7LP6VJRP.js";
13
13
  import "./chunk-TUTS6S2G.js";
@@ -28,4 +28,4 @@ export {
28
28
  describeDataDirFix,
29
29
  initDataDirectories
30
30
  };
31
- //# sourceMappingURL=setup-data-dir-I2Q4KJ7M.js.map
31
+ //# sourceMappingURL=setup-data-dir-6PEJ3YF7.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexus-agents",
3
- "version": "8.74.1",
3
+ "version": "8.74.2",
4
4
  "description": "Governance substrate for AI coding agents — adversarial PR review, drift-detected rules, tamper-evident audit, and closed-loop outcome routing for Claude, Codex, Gemini, and OpenCode",
5
5
  "mcpName": "io.github.nexus-substrate/nexus-agents",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/init-opencode.ts"],"sourcesContent":["/**\n * `nexus-agents init --opencode <path>` setup helper (#2504, child 4 of #2500).\n *\n * Injects the nexus-agents MCP block + recommended environment into an\n * existing `opencode.json` without overwriting other operator-set keys.\n * Pairs with the existing `init --portable --mcp-config` (which targets\n * Claude Code's `.mcp.json`). Different harness, same merge-not-overwrite\n * pattern.\n *\n * Behaviour:\n * - File exists → merge `mcp.nexus-agents` into the existing JSON.\n * Preserve every other key the operator has set (provider config,\n * model lists, theme, …). Idempotent: re-running produces the same\n * final file.\n * - File missing → write a minimal template with the nexus-agents MCP\n * block + a stubbed `providers.openai-compat` shell the operator\n * fills in (placeholder baseURL + `{env:WORKSPACE_PROXY_KEY}`).\n * - `--dry-run` → print the diff (proposed vs existing) without writing.\n * - `--validate` → after merge (or alongside dry-run), probe\n * `providers.openai-compat.options.baseURL/v1/models` with the\n * resolved apiKey, exit non-zero if unreachable.\n *\n * @module cli/init-opencode\n */\n\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nimport { createLogger } from '../core/index.js';\nimport { readOpencodeGateway } from '../config/opencode-bridge.js';\nimport { discoverModels } from '../adapters/openai-compat-adapter.js';\n\nconst logger = createLogger({ component: 'init-opencode' });\n\nexport interface InitOpencodeOptions {\n /** Absolute or relative path to opencode.json. */\n readonly path: string;\n /** Path to the nexus-agents CLI binary that the MCP block will spawn. */\n readonly cliPath: string;\n /** Sandbox flavor written to NEXUS_SANDBOX in the MCP environment. */\n readonly sandboxFlavor?: string;\n /** Print the diff without writing. */\n readonly dryRun?: boolean;\n}\n\nexport interface InitOpencodeResult {\n readonly path: string;\n readonly action: 'created' | 'updated' | 'unchanged' | 'dry-run';\n readonly diff: string;\n}\n\ninterface OpencodeMcpBlock {\n type: 'local';\n command: string[];\n enabled: boolean;\n environment: Record<string, string>;\n}\n\ninterface OpencodeFile {\n $schema?: string;\n providers?: Record<string, unknown>;\n mcp?: Record<string, unknown>;\n permission?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Conservative default OpenCode `permission` block (#2658). OpenCode\n * evaluates a glob map with **last-matching-rule-winning**, so the broad\n * `\"*\"` rule is listed first and the hard `deny` patterns last.\n *\n * - `edit` — everything `ask`s; secrets, keys, and `.git/` are hard-denied\n * regardless of operator confirmation. The deny patterns come after\n * `\"*\"` so last-match-wins makes them stick.\n * - `bash` — `ask` unconditionally. Shell is the highest-risk surface.\n * - `skill` — `allow`. Skills are trusted, in-repo, CI-validated content.\n *\n * Emitted only when the operator has not already set their own\n * `permission` block — never overwrites operator config.\n */\nexport function buildDefaultPermissionBlock(): Record<string, unknown> {\n return {\n edit: {\n '*': 'ask',\n '**/.env': 'deny',\n '**/.env.*': 'deny',\n '**/*.pem': 'deny',\n '**/*.key': 'deny',\n '**/id_rsa*': 'deny',\n '**/secrets/**': 'deny',\n '**/.git/**': 'deny',\n },\n bash: 'ask',\n skill: 'allow',\n };\n}\n\n/**\n * Build the canonical nexus-agents MCP block. Mirrors what\n * `Dockerfile.sandbox` writes today plus the env vars from #2501 + #2503.\n */\nexport function buildNexusMcpBlock(opts: {\n readonly cliPath: string;\n readonly sandboxFlavor?: string;\n readonly opencodeConfigPath: string;\n}): OpencodeMcpBlock {\n const env: Record<string, string> = {\n NEXUS_DATA_DIR: '{env:NEXUS_DATA_DIR}',\n NEXUS_OPENCODE_CONFIG: opts.opencodeConfigPath,\n };\n if (opts.sandboxFlavor !== undefined && opts.sandboxFlavor !== '') {\n env['NEXUS_SANDBOX'] = opts.sandboxFlavor;\n }\n return {\n type: 'local',\n command: ['node', opts.cliPath, '--mode=server'],\n enabled: true,\n environment: env,\n };\n}\n\n/**\n * Execute the merge. Pure with respect to `opts.dryRun: true` — no fs\n * writes. Returns the final-or-proposed JSON + a diff string suitable\n * for printing.\n */\nexport function runInitOpencode(opts: InitOpencodeOptions): InitOpencodeResult {\n const existing = readExisting(opts.path);\n const merged = mergeNexusBlock(existing, opts);\n const before = existing === null ? '' : `${JSON.stringify(existing, null, 2)}\\n`;\n const after = `${JSON.stringify(merged, null, 2)}\\n`;\n const diff = simpleDiff(before, after);\n\n if (opts.dryRun === true) {\n return { path: opts.path, action: 'dry-run', diff };\n }\n\n if (existing !== null && before === after) {\n return { path: opts.path, action: 'unchanged', diff };\n }\n\n writeFileSync(opts.path, after, 'utf8');\n return { path: opts.path, action: existing === null ? 'created' : 'updated', diff };\n}\n\nfunction readExisting(path: string): OpencodeFile | null {\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, 'utf8');\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== 'object' || parsed === null) {\n throw new Error('opencode.json root must be an object');\n }\n return parsed as OpencodeFile;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to parse opencode.json at ${path}: ${msg}`);\n }\n}\n\nfunction mergeNexusBlock(existing: OpencodeFile | null, opts: InitOpencodeOptions): OpencodeFile {\n const block = buildNexusMcpBlock({\n cliPath: opts.cliPath,\n ...(opts.sandboxFlavor !== undefined && { sandboxFlavor: opts.sandboxFlavor }),\n opencodeConfigPath: opts.path,\n });\n\n if (existing === null) {\n return {\n $schema: 'https://opencode.ai/config.json',\n providers: { 'openai-compat': stubOpenaiCompatProvider() },\n permission: buildDefaultPermissionBlock(),\n mcp: { 'nexus-agents': block },\n };\n }\n\n // Idempotent merge: preserve user-customised fields if they exist on the\n // current MCP block (e.g. `enabled: false`). Update anything else.\n const existingMcp: Record<string, unknown> = existing.mcp ?? {};\n const rawNexus = existingMcp['nexus-agents'];\n const existingNexus: Partial<OpencodeMcpBlock> =\n typeof rawNexus === 'object' && rawNexus !== null ? rawNexus : {};\n const mergedBlock: OpencodeMcpBlock = {\n type: 'local',\n command: block.command,\n enabled: existingNexus.enabled ?? block.enabled,\n environment: { ...block.environment, ...(existingNexus.environment ?? {}) },\n };\n\n // Add the default permission block only when the operator has not set\n // their own — never overwrite an existing `permission` key.\n const permission = existing.permission ?? buildDefaultPermissionBlock();\n\n return {\n ...existing,\n permission,\n mcp: { ...existingMcp, 'nexus-agents': mergedBlock },\n };\n}\n\n/**\n * Stub for `providers.openai-compat`. Operators replace the placeholder\n * baseURL + key with their workspace-proxy values. Empty by design — we\n * never overwrite an existing provider block (see `mergeNexusBlock`).\n */\nfunction stubOpenaiCompatProvider(): Record<string, unknown> {\n return {\n npm: '@ai-sdk/openai-compatible',\n options: {\n baseURL: '<replace with workspace proxy URL>',\n apiKey: '{env:WORKSPACE_PROXY_KEY}',\n },\n };\n}\n\n/**\n * Cheap line-based diff. Three-line context, +/- prefixes. Good enough\n * for an operator preview; not a unified-diff replacement.\n */\nfunction simpleDiff(before: string, after: string): string {\n if (before === after) return '(no changes)';\n const beforeLines = before.split('\\n');\n const afterLines = after.split('\\n');\n const out: string[] = [];\n const max = Math.max(beforeLines.length, afterLines.length);\n for (let i = 0; i < max; i += 1) {\n const b = beforeLines[i];\n const a = afterLines[i];\n if (b === a) {\n out.push(` ${b ?? ''}`);\n } else {\n if (b !== undefined) out.push(`- ${b}`);\n if (a !== undefined) out.push(`+ ${a}`);\n }\n }\n return out.join('\\n');\n}\n\nexport function ensureOpencodeDirExists(path: string): void {\n const dir = dirname(path);\n if (!existsSync(dir)) {\n throw new Error(\n `Parent directory does not exist: ${dir}. Create it first or pass a path under an existing dir.`\n );\n }\n logger.debug('opencode.json target directory exists', { dir });\n}\n\n/**\n * --validate flow for `init --opencode` (follow-up to #2504).\n *\n * After the merge step writes the file, optionally probe the gateway it\n * points at. Reuses the same opencode-bridge loader (#2503) that the\n * runtime uses, so what we validate is exactly what the server will see\n * at boot.\n *\n * Returns:\n * - `{ ok: true, models }` when the gateway is reachable and returns ≥1 model\n * - `{ ok: false, reason }` for every failure path (config missing,\n * probe failed, zero models)\n *\n * Caller (cli-commands-handlers) maps to exit codes + stderr / stdout.\n * The API key never reaches the returned object — only model IDs +\n * baseURL appear in the success payload.\n */\nexport interface OpencodeValidateResult {\n readonly ok: boolean;\n readonly baseURL?: string;\n readonly models?: readonly string[];\n readonly reason?: string;\n}\n\nexport async function runOpencodeValidate(opencodePath: string): Promise<OpencodeValidateResult> {\n const config = readOpencodeGateway(opencodePath);\n if (config === null) {\n return {\n ok: false,\n reason:\n 'opencode.json does not resolve a usable gateway. Check that providers.openai-compat.options.{baseURL, apiKey} are set and that any {env:VAR} interpolation references are exported.',\n };\n }\n const result = await discoverModels({ baseUrl: config.baseURL, apiKey: config.apiKey });\n if (!result.ok) {\n return { ok: false, baseURL: config.baseURL, reason: result.error.message };\n }\n if (result.value.length === 0) {\n return {\n ok: false,\n baseURL: config.baseURL,\n reason: 'gateway returned 0 models. Check upstream provider quotas / list filters.',\n };\n }\n return {\n ok: true,\n baseURL: config.baseURL,\n models: result.value.map((m) => m.id),\n };\n}\n"],"mappings":"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","names":[]}