@jmanuelcorral/openteam 0.16.0 → 0.16.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.
- package/README.es.md +1 -1
- package/README.md +1 -1
- package/dist/certificates/graph-release-certificate.json +45 -0
- package/dist/certificates/graph-shadow-certificate.json +39 -0
- package/dist/cli/graphOperator.d.ts +3 -3
- package/dist/cli/graphOperator.d.ts.map +1 -1
- package/dist/cli/opencodeVersion.d.ts +44 -0
- package/dist/cli/opencodeVersion.d.ts.map +1 -0
- package/dist/cli.js +2038 -1021
- package/dist/commands/dispatch.d.ts +23 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/doctor.d.ts +19 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/graph.d.ts +42 -2
- package/dist/commands/graph.d.ts.map +1 -1
- package/dist/config/graphFeatureGate.d.ts +28 -6
- package/dist/config/graphFeatureGate.d.ts.map +1 -1
- package/dist/contract/opencode.d.ts +49 -3
- package/dist/contract/opencode.d.ts.map +1 -1
- package/dist/graph/certificate.d.ts.map +1 -1
- package/dist/index.d.ts +47 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +177 -36
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,8 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
|
-
import {
|
|
5
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
6
|
+
import { basename as basename2, dirname as dirname4 } from "node:path";
|
|
6
7
|
import {
|
|
7
8
|
tool as tool6
|
|
8
9
|
} from "@opencode-ai/plugin";
|
|
@@ -2449,7 +2450,15 @@ function splitVirtualPath(path) {
|
|
|
2449
2450
|
}
|
|
2450
2451
|
|
|
2451
2452
|
// src/cli/graphOperator.ts
|
|
2452
|
-
var ALLOWED_GATE = {
|
|
2453
|
+
var ALLOWED_GATE = {
|
|
2454
|
+
allowed: true,
|
|
2455
|
+
violations: [],
|
|
2456
|
+
advisories: []
|
|
2457
|
+
};
|
|
2458
|
+
var ALLOWED_EVALUATION = {
|
|
2459
|
+
kind: "evaluated",
|
|
2460
|
+
gate: ALLOWED_GATE
|
|
2461
|
+
};
|
|
2453
2462
|
function journalRoot(config) {
|
|
2454
2463
|
return config.graph?.journalRoot ?? DEFAULT_GRAPH_JOURNAL_ROOT;
|
|
2455
2464
|
}
|
|
@@ -2539,7 +2548,7 @@ async function listWorktreeObservations(config, options) {
|
|
|
2539
2548
|
})));
|
|
2540
2549
|
}
|
|
2541
2550
|
function createGraphOperator(options) {
|
|
2542
|
-
const evaluateCutoverGate = options.evaluateCutoverGate ?? (async () =>
|
|
2551
|
+
const evaluateCutoverGate = options.evaluateCutoverGate ?? (async () => ALLOWED_EVALUATION);
|
|
2543
2552
|
const worktreeOptions = options.exec === undefined ? undefined : {
|
|
2544
2553
|
exec: options.exec,
|
|
2545
2554
|
repoRoot: options.repoRoot ?? process.cwd(),
|
|
@@ -4181,6 +4190,7 @@ function checkCertificate(status, name) {
|
|
|
4181
4190
|
}
|
|
4182
4191
|
function evaluateGraphGate(input) {
|
|
4183
4192
|
const violations = [];
|
|
4193
|
+
const advisories = [];
|
|
4184
4194
|
const shadowV = checkCertificate(input.shadow, "shadow");
|
|
4185
4195
|
if (shadowV !== undefined)
|
|
4186
4196
|
violations.push(shadowV);
|
|
@@ -4189,7 +4199,7 @@ function evaluateGraphGate(input) {
|
|
|
4189
4199
|
violations.push(releaseV);
|
|
4190
4200
|
const soakV = checkCertificate(input.soak, "soak");
|
|
4191
4201
|
if (soakV !== undefined)
|
|
4192
|
-
|
|
4202
|
+
advisories.push(soakV);
|
|
4193
4203
|
if (input.migration === "pending") {
|
|
4194
4204
|
violations.push({
|
|
4195
4205
|
code: "migration-pending",
|
|
@@ -4209,7 +4219,8 @@ function evaluateGraphGate(input) {
|
|
|
4209
4219
|
}
|
|
4210
4220
|
return {
|
|
4211
4221
|
allowed: violations.length === 0,
|
|
4212
|
-
violations
|
|
4222
|
+
violations,
|
|
4223
|
+
advisories
|
|
4213
4224
|
};
|
|
4214
4225
|
}
|
|
4215
4226
|
function isGateApplicable(mode) {
|
|
@@ -4298,6 +4309,36 @@ var SUPPORTED_OPENCODE_VERSIONS = [
|
|
|
4298
4309
|
"1.18.18",
|
|
4299
4310
|
"1.18.19"
|
|
4300
4311
|
];
|
|
4312
|
+
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
|
|
4313
|
+
function parseSemver(version) {
|
|
4314
|
+
const normalized = version.trim().replace(/^[=^~v]+/, "");
|
|
4315
|
+
const match = SEMVER_RE.exec(normalized);
|
|
4316
|
+
if (match === null) {
|
|
4317
|
+
throw new Error(`invalid semver: "${version}"`);
|
|
4318
|
+
}
|
|
4319
|
+
return {
|
|
4320
|
+
major: Number(match[1]),
|
|
4321
|
+
minor: Number(match[2]),
|
|
4322
|
+
patch: Number(match[3])
|
|
4323
|
+
};
|
|
4324
|
+
}
|
|
4325
|
+
function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
|
|
4326
|
+
if (certifiedVersion === liveVersion) {
|
|
4327
|
+
return true;
|
|
4328
|
+
}
|
|
4329
|
+
let certified;
|
|
4330
|
+
let live;
|
|
4331
|
+
try {
|
|
4332
|
+
certified = parseSemver(certifiedVersion);
|
|
4333
|
+
live = parseSemver(liveVersion);
|
|
4334
|
+
} catch {
|
|
4335
|
+
return false;
|
|
4336
|
+
}
|
|
4337
|
+
if (certified.major !== live.major || certified.minor !== live.minor) {
|
|
4338
|
+
return false;
|
|
4339
|
+
}
|
|
4340
|
+
return live.patch >= certified.patch;
|
|
4341
|
+
}
|
|
4301
4342
|
|
|
4302
4343
|
// src/graph/soakLedger.ts
|
|
4303
4344
|
import { z as z15 } from "zod";
|
|
@@ -4556,8 +4597,8 @@ function parseShadowCertificate(value, opencodeVersion) {
|
|
|
4556
4597
|
if (sha256Hex(canonicalCertificate(unsigned)) !== digest) {
|
|
4557
4598
|
throw new ShadowCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
|
|
4558
4599
|
}
|
|
4559
|
-
if (certificate.opencodeVersion
|
|
4560
|
-
throw new ShadowCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}",
|
|
4600
|
+
if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
|
|
4601
|
+
throw new ShadowCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
|
|
4561
4602
|
}
|
|
4562
4603
|
return certificate;
|
|
4563
4604
|
}
|
|
@@ -4741,8 +4782,8 @@ function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
|
|
|
4741
4782
|
if (sha256Hex(canonicalReleaseCertificate(unsigned)) !== digest) {
|
|
4742
4783
|
throw new ReleaseCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
|
|
4743
4784
|
}
|
|
4744
|
-
if (certificate.opencodeVersion
|
|
4745
|
-
throw new ReleaseCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}",
|
|
4785
|
+
if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
|
|
4786
|
+
throw new ReleaseCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
|
|
4746
4787
|
}
|
|
4747
4788
|
return certificate;
|
|
4748
4789
|
}
|
|
@@ -8344,7 +8385,7 @@ function legacyRosterFindings(audit) {
|
|
|
8344
8385
|
}
|
|
8345
8386
|
return findings;
|
|
8346
8387
|
}
|
|
8347
|
-
function rosterHealthSection(audit, loadError, rosterRoleCount) {
|
|
8388
|
+
function rosterHealthSection(audit, loadError, rosterRoleCount, rosterMissing) {
|
|
8348
8389
|
const auditFindings = audit?.findings ?? legacyRosterFindings(audit ?? {});
|
|
8349
8390
|
const unparseable = auditFindings.find((finding) => finding.kind === "unparseable-roster");
|
|
8350
8391
|
const unparseableMessage = loadError ?? unparseable?.message;
|
|
@@ -8357,6 +8398,14 @@ function rosterHealthSection(audit, loadError, rosterRoleCount) {
|
|
|
8357
8398
|
];
|
|
8358
8399
|
}
|
|
8359
8400
|
if (audit === undefined) {
|
|
8401
|
+
if (rosterMissing === true) {
|
|
8402
|
+
return [
|
|
8403
|
+
" roster health:",
|
|
8404
|
+
` ✗ ${OPENTEAM_ROSTER_PATH} does not exist — no team roster has been cast.`,
|
|
8405
|
+
" consequence: without a roster openteam cannot distribute work across roles, so every request is handled by a single agent and no work splitting happens.",
|
|
8406
|
+
` remedy: run \`openteam setup\` to cast a team and write the roster (${OPENTEAM_ROSTER_PATH}), then re-run \`openteam doctor\`.`
|
|
8407
|
+
];
|
|
8408
|
+
}
|
|
8360
8409
|
return [];
|
|
8361
8410
|
}
|
|
8362
8411
|
const missing = auditFindings.filter((finding) => finding.kind === "missing-guaranteed-role").map((finding) => finding.roleID);
|
|
@@ -8392,6 +8441,14 @@ function rosterHealthSection(audit, loadError, rosterRoleCount) {
|
|
|
8392
8441
|
}
|
|
8393
8442
|
return lines;
|
|
8394
8443
|
}
|
|
8444
|
+
function orchestratorAgentSection() {
|
|
8445
|
+
return [
|
|
8446
|
+
" orchestrator agent:",
|
|
8447
|
+
` ✗ ${ORCHESTRATOR_AGENT_PATH} does not exist — opencode has no openteam coordinator agent.`,
|
|
8448
|
+
" consequence: this is the primary agent that reads the roster and calls `openteam-orchestrate` to fan work out across roles; without it opencode has no openteam coordinator, so every request is handled by a single agent and no work splitting happens.",
|
|
8449
|
+
` remedy: run \`openteam setup\` to generate ${ORCHESTRATOR_AGENT_PATH}, then re-run \`openteam doctor\`.`
|
|
8450
|
+
];
|
|
8451
|
+
}
|
|
8395
8452
|
function gitignoreSection(gitignore) {
|
|
8396
8453
|
if (gitignore.missingRules.length === 0) {
|
|
8397
8454
|
return [
|
|
@@ -8455,7 +8512,10 @@ function renderDoctor(input) {
|
|
|
8455
8512
|
if (input.diagnostics !== undefined) {
|
|
8456
8513
|
lines.push(...diagnosticsSection(input.diagnostics));
|
|
8457
8514
|
}
|
|
8458
|
-
lines.push(...rosterHealthSection(input.rosterAudit, input.rosterLoadError, input.rosterRoleCount));
|
|
8515
|
+
lines.push(...rosterHealthSection(input.rosterAudit, input.rosterLoadError, input.rosterRoleCount, input.rosterMissing));
|
|
8516
|
+
if (input.orchestratorAgentMissing === true) {
|
|
8517
|
+
lines.push(...orchestratorAgentSection());
|
|
8518
|
+
}
|
|
8459
8519
|
if (input.gitignore !== undefined) {
|
|
8460
8520
|
lines.push(...gitignoreSection(input.gitignore));
|
|
8461
8521
|
}
|
|
@@ -8539,14 +8599,38 @@ function leaseLine(lease) {
|
|
|
8539
8599
|
const state = lease.released ? "released" : "active";
|
|
8540
8600
|
return ` lease: ${lease.holder} → ${lease.runID} (${state})`;
|
|
8541
8601
|
}
|
|
8542
|
-
function
|
|
8602
|
+
function appendAdvisoryLines(lines, advisories) {
|
|
8603
|
+
for (const advisory of advisories ?? []) {
|
|
8604
|
+
lines.push(` ⚠ advisory (non-blocking) ${advisory.code}: ${advisory.detail}`);
|
|
8605
|
+
}
|
|
8606
|
+
}
|
|
8607
|
+
function appendEffectiveLines(lines, gate) {
|
|
8608
|
+
switch (gate.kind) {
|
|
8609
|
+
case "not-applicable":
|
|
8610
|
+
return;
|
|
8611
|
+
case "passed":
|
|
8612
|
+
lines.push(" effective: active");
|
|
8613
|
+
appendAdvisoryLines(lines, gate.advisories);
|
|
8614
|
+
return;
|
|
8615
|
+
case "unevaluable":
|
|
8616
|
+
lines.push(" effective: unknown — could not evaluate the cutover gate");
|
|
8617
|
+
lines.push(` ${gate.reason}`);
|
|
8618
|
+
return;
|
|
8619
|
+
case "denied":
|
|
8620
|
+
lines.push(" effective: off — cutover gate denied");
|
|
8621
|
+
for (const violation of gate.violations) {
|
|
8622
|
+
lines.push(` • ${violation.code}: ${violation.detail}`);
|
|
8623
|
+
}
|
|
8624
|
+
appendAdvisoryLines(lines, gate.advisories);
|
|
8625
|
+
}
|
|
8626
|
+
}
|
|
8627
|
+
function renderGraphStatus(snapshot, gate = { kind: "not-applicable" }) {
|
|
8543
8628
|
const lines = [
|
|
8544
8629
|
"openteam graph status:",
|
|
8545
|
-
` config: ${snapshot.effectiveMode} (source: ${snapshot.source})
|
|
8546
|
-
leaseLine(snapshot.lease),
|
|
8547
|
-
` journal: ${snapshot.journal}`,
|
|
8548
|
-
` effects: ${snapshot.pendingEffects} pending, ${snapshot.unknownEffects} unknown`
|
|
8630
|
+
` config: ${snapshot.effectiveMode} (source: ${snapshot.source})`
|
|
8549
8631
|
];
|
|
8632
|
+
appendEffectiveLines(lines, gate);
|
|
8633
|
+
lines.push(leaseLine(snapshot.lease), ` journal: ${snapshot.journal}`, ` effects: ${snapshot.pendingEffects} pending, ${snapshot.unknownEffects} unknown`);
|
|
8550
8634
|
const needsReconciliation = isRecoveryRequired(snapshot.journal, snapshot.unknownEffects);
|
|
8551
8635
|
if (needsReconciliation) {
|
|
8552
8636
|
lines.push(" ⚠ Reconciliation required: journal or effects in an uncertain state.");
|
|
@@ -9990,8 +10074,7 @@ async function runAgents(deps, configPath, opencodeConfigPaths, configResolution
|
|
|
9990
10074
|
async function runGraph(positionals, deps, configPath, configResolution) {
|
|
9991
10075
|
const sub = positionals[1];
|
|
9992
10076
|
if (sub === "status") {
|
|
9993
|
-
|
|
9994
|
-
return { exitCode: 0, stdout: renderGraphStatus(snapshot) };
|
|
10077
|
+
return runGraphStatus(deps, configPath, configResolution);
|
|
9995
10078
|
}
|
|
9996
10079
|
if (sub === "cancel") {
|
|
9997
10080
|
return runGraphCancel(positionals, deps, configPath, configResolution);
|
|
@@ -10046,7 +10129,9 @@ function renderGraphOutcome(outcome) {
|
|
|
10046
10129
|
async function resolveOperatorGraphPolicy(config, operator) {
|
|
10047
10130
|
let graph = resolveGraphConfig(config, "file");
|
|
10048
10131
|
if (isGateApplicable(graph.effectiveMode)) {
|
|
10049
|
-
|
|
10132
|
+
const evaluation = await operator.evaluateCutoverGate(config);
|
|
10133
|
+
const gate = evaluation.kind === "evaluated" ? evaluation.gate : { allowed: false, violations: [], advisories: [] };
|
|
10134
|
+
graph = applyGraphGate(graph, gate);
|
|
10050
10135
|
}
|
|
10051
10136
|
return {
|
|
10052
10137
|
graph,
|
|
@@ -10064,6 +10149,36 @@ function checkOperatorGate(policy) {
|
|
|
10064
10149
|
stdout: renderLegacyGraphReason(decision2)
|
|
10065
10150
|
};
|
|
10066
10151
|
}
|
|
10152
|
+
async function runGraphStatus(deps, configPath, configResolution) {
|
|
10153
|
+
const snapshot = await deps.readGraphSnapshot(configPath, configResolution);
|
|
10154
|
+
if (!isGateApplicable(snapshot.effectiveMode)) {
|
|
10155
|
+
return { exitCode: 0, stdout: renderGraphStatus(snapshot) };
|
|
10156
|
+
}
|
|
10157
|
+
const operator = deps.graphOperator;
|
|
10158
|
+
if (operator === undefined) {
|
|
10159
|
+
const gate2 = {
|
|
10160
|
+
kind: "unevaluable",
|
|
10161
|
+
reason: "graph operator ports are not configured"
|
|
10162
|
+
};
|
|
10163
|
+
return { exitCode: 0, stdout: renderGraphStatus(snapshot, gate2) };
|
|
10164
|
+
}
|
|
10165
|
+
const config = await deps.loadConfig(configPath, configResolution);
|
|
10166
|
+
const evaluation = await operator.evaluateCutoverGate(config);
|
|
10167
|
+
if (evaluation.kind === "unevaluable") {
|
|
10168
|
+
const gate2 = {
|
|
10169
|
+
kind: "unevaluable",
|
|
10170
|
+
reason: evaluation.reason
|
|
10171
|
+
};
|
|
10172
|
+
return { exitCode: 0, stdout: renderGraphStatus(snapshot, gate2) };
|
|
10173
|
+
}
|
|
10174
|
+
const resolved = applyGraphGate(resolveGraphConfig(config, "file"), evaluation.gate);
|
|
10175
|
+
const gate = resolved.gateDenied === true ? {
|
|
10176
|
+
kind: "denied",
|
|
10177
|
+
violations: resolved.gateViolations ?? [],
|
|
10178
|
+
advisories: evaluation.gate.advisories
|
|
10179
|
+
} : { kind: "passed", advisories: evaluation.gate.advisories };
|
|
10180
|
+
return { exitCode: 0, stdout: renderGraphStatus(snapshot, gate) };
|
|
10181
|
+
}
|
|
10067
10182
|
async function runGraphCancel(positionals, deps, configPath, configResolution) {
|
|
10068
10183
|
const runID = positionals[2];
|
|
10069
10184
|
if (runID === undefined || runID.trim() === "") {
|
|
@@ -10510,6 +10625,9 @@ async function runCli(argv, deps) {
|
|
|
10510
10625
|
const summary = summarizeQueueWaits(queueEvents);
|
|
10511
10626
|
queueWait = summary !== undefined ? { summary } : {};
|
|
10512
10627
|
}
|
|
10628
|
+
const rosterMissing = deps.loadRosterForDoctor !== undefined && roster === undefined && rosterLoadError === undefined;
|
|
10629
|
+
const orchestratorAgentName = ORCHESTRATOR_AGENT_PATH.slice(ORCHESTRATOR_AGENT_PATH.lastIndexOf("/") + 1).replace(/\.md$/, "");
|
|
10630
|
+
const orchestratorAgentMissing = !agentFiles.some((file) => file.name === orchestratorAgentName);
|
|
10513
10631
|
return {
|
|
10514
10632
|
exitCode: 0,
|
|
10515
10633
|
stdout: renderDoctor({
|
|
@@ -10523,6 +10641,8 @@ async function runCli(argv, deps) {
|
|
|
10523
10641
|
...rosterAudit !== undefined ? { rosterAudit } : {},
|
|
10524
10642
|
...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
|
|
10525
10643
|
...rosterLoadError !== undefined ? { rosterLoadError } : {},
|
|
10644
|
+
...rosterMissing ? { rosterMissing } : {},
|
|
10645
|
+
...orchestratorAgentMissing ? { orchestratorAgentMissing } : {},
|
|
10526
10646
|
...diagnostics !== undefined && diagnostics.length > 0 ? { diagnostics } : {},
|
|
10527
10647
|
...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
|
|
10528
10648
|
...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
|
|
@@ -13387,7 +13507,7 @@ var createOtlpSpanExporter = (connection, config) => {
|
|
|
13387
13507
|
// package.json
|
|
13388
13508
|
var package_default = {
|
|
13389
13509
|
name: "@jmanuelcorral/openteam",
|
|
13390
|
-
version: "0.16.
|
|
13510
|
+
version: "0.16.2",
|
|
13391
13511
|
packageManager: "bun@1.3.14",
|
|
13392
13512
|
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
13393
13513
|
license: "MIT",
|
|
@@ -13446,7 +13566,7 @@ var package_default = {
|
|
|
13446
13566
|
sideEffects: false,
|
|
13447
13567
|
scripts: {
|
|
13448
13568
|
prebuild: "bun run clean",
|
|
13449
|
-
build: "bun run build:js && bun run build:cli && bun run build:types",
|
|
13569
|
+
build: "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
|
|
13450
13570
|
"build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
|
|
13451
13571
|
"build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph',
|
|
13452
13572
|
"build:types": "tsc -p tsconfig.build.json",
|
|
@@ -13455,6 +13575,7 @@ var package_default = {
|
|
|
13455
13575
|
"test:cov": "bun test --coverage",
|
|
13456
13576
|
"certify:shadow": "bun run scripts/certify-artifact.ts shadow",
|
|
13457
13577
|
"certify:release": "bun run scripts/certify-artifact.ts release",
|
|
13578
|
+
"build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
|
|
13458
13579
|
"coverage:check": "node scripts/check-coverage.mjs",
|
|
13459
13580
|
typecheck: "tsc --noEmit",
|
|
13460
13581
|
lint: "biome check .",
|
|
@@ -13563,9 +13684,16 @@ function warnSoakEvidenceRejections(result, warn) {
|
|
|
13563
13684
|
var SHADOW_CERT_PATH = "artifacts/graph-shadow-certificate.json";
|
|
13564
13685
|
var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
|
|
13565
13686
|
var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
|
|
13566
|
-
async function
|
|
13687
|
+
async function readPackagedCertificate(path4) {
|
|
13688
|
+
try {
|
|
13689
|
+
return await readFile2(new URL(`./certificates/${basename2(path4)}`, import.meta.url), "utf8");
|
|
13690
|
+
} catch {
|
|
13691
|
+
return;
|
|
13692
|
+
}
|
|
13693
|
+
}
|
|
13694
|
+
async function readAndParseCertificate(storage, path4, parse2, digestExtractor, fallbackRead) {
|
|
13567
13695
|
try {
|
|
13568
|
-
const content = await storage.read(path4);
|
|
13696
|
+
const content = await storage.read(path4) ?? (fallbackRead === undefined ? undefined : await fallbackRead(path4));
|
|
13569
13697
|
if (content === undefined) {
|
|
13570
13698
|
return { status: "absent" };
|
|
13571
13699
|
}
|
|
@@ -13733,24 +13861,30 @@ function createLazyOpencodeVersionReader(serverUrl, options = {}) {
|
|
|
13733
13861
|
}
|
|
13734
13862
|
};
|
|
13735
13863
|
}
|
|
13736
|
-
async function buildGateInput(config, storage, opencodeVersion, warn) {
|
|
13737
|
-
const shadow = await readAndParseCertificate(storage, SHADOW_CERT_PATH, (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence));
|
|
13864
|
+
async function buildGateInput(config, storage, opencodeVersion, warn, packagedRead = readPackagedCertificate) {
|
|
13865
|
+
const shadow = await readAndParseCertificate(storage, SHADOW_CERT_PATH, (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
|
|
13738
13866
|
const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
|
|
13739
|
-
const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest);
|
|
13867
|
+
const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
|
|
13740
13868
|
let soakLedgerDigest = "0".repeat(64);
|
|
13741
13869
|
try {
|
|
13742
13870
|
const recorderPorts = recorderPortsFromStorage(storage);
|
|
13743
13871
|
const chainRead = await readAllChains(recorderPorts, SOAK_EVIDENCE_DIR);
|
|
13744
13872
|
warnSoakEvidenceRejections(chainRead, warn);
|
|
13745
|
-
|
|
13746
|
-
soakLedgerDigest = canonicalLedger(chainRead.chains);
|
|
13747
|
-
}
|
|
13873
|
+
soakLedgerDigest = canonicalLedger(chainRead.chains);
|
|
13748
13874
|
} catch {}
|
|
13749
13875
|
const soak = await readAndParseCertificate(storage, SOAK_CERT_PATH, (raw) => parseSoakCertificate(raw, DEFAULT_SOAK_POLICY, soakLedgerDigest), (cert) => cert.digest);
|
|
13750
13876
|
const migration = "complete";
|
|
13751
13877
|
const operatorApproval = config.graph?.operatorApproval ?? false;
|
|
13752
13878
|
return { shadow, release, soak, migration, operatorApproval };
|
|
13753
13879
|
}
|
|
13880
|
+
var OPENCODE_VERSION_UNRESOLVED_REASON = "the opencode version is not determinable outside a running opencode session, " + "so the cutover gate cannot be verified here — the running plugin logs the " + "authoritative gate result and warns loudly on a real denial";
|
|
13881
|
+
async function evaluateCutoverGateForBoundary(config, storage, version) {
|
|
13882
|
+
if (version === undefined) {
|
|
13883
|
+
return { kind: "unevaluable", reason: OPENCODE_VERSION_UNRESOLVED_REASON };
|
|
13884
|
+
}
|
|
13885
|
+
const gateInput = await buildGateInput(config, storage, version, () => {});
|
|
13886
|
+
return { kind: "evaluated", gate: evaluateGraphGate(gateInput) };
|
|
13887
|
+
}
|
|
13754
13888
|
function recorderPortsFromStorage(storage) {
|
|
13755
13889
|
return {
|
|
13756
13890
|
fs: {
|
|
@@ -13838,7 +13972,7 @@ async function readStoredRosterForDoctor(storage) {
|
|
|
13838
13972
|
}
|
|
13839
13973
|
}
|
|
13840
13974
|
var auditStoredRosterForDoctor = (roster, configuredRoles) => typeof roster === "string" ? auditRoster(roster, configuredRoles) : auditRoster(roster, configuredRoles);
|
|
13841
|
-
function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SESSIONS_DIR, storage = createFsStorageProvider(process.cwd()), fallbackSource = "options", rawOptions) {
|
|
13975
|
+
function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SESSIONS_DIR, storage = createFsStorageProvider(process.cwd()), fallbackSource = "options", rawOptions, resolveOpencodeVersion) {
|
|
13842
13976
|
return {
|
|
13843
13977
|
loadConfig: async (path4, resolution = { explicit: false }) => {
|
|
13844
13978
|
const content = await readCliConfigFromStorage(storage, path4, resolution);
|
|
@@ -13912,7 +14046,8 @@ function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SE
|
|
|
13912
14046
|
}),
|
|
13913
14047
|
graphOperator: createGraphOperator({
|
|
13914
14048
|
storage,
|
|
13915
|
-
holder: "openteam-cli"
|
|
14049
|
+
holder: "openteam-cli",
|
|
14050
|
+
evaluateCutoverGate: async (gateConfig) => evaluateCutoverGateForBoundary(gateConfig, storage, resolveOpencodeVersion === undefined ? undefined : await resolveOpencodeVersion())
|
|
13916
14051
|
}),
|
|
13917
14052
|
configPath: DEFAULT_CONFIG_PATH,
|
|
13918
14053
|
telemetryPath,
|
|
@@ -14042,7 +14177,14 @@ var server = async (ctx, rawOptions) => {
|
|
|
14042
14177
|
injectMemory,
|
|
14043
14178
|
onRoute: (sid, model) => sessionTracker.trackRoutedModel(sid, model)
|
|
14044
14179
|
});
|
|
14045
|
-
const
|
|
14180
|
+
const getOpencodeVersion = createLazyOpencodeVersionReader(ctx.serverUrl, {
|
|
14181
|
+
client: ctx.client,
|
|
14182
|
+
onFailure: createOpencodeVersionReadFailureLogger(MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, diagnostics.warn("opencode-version-read-failed", { toast: false }))
|
|
14183
|
+
});
|
|
14184
|
+
const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage, runtimeConfig.source, rawOptions, async () => {
|
|
14185
|
+
const version = await getOpencodeVersion();
|
|
14186
|
+
return version === "unknown" ? undefined : version;
|
|
14187
|
+
});
|
|
14046
14188
|
const toolcalls = createToolcallTracker({ sink });
|
|
14047
14189
|
const announcedSessions = new Set;
|
|
14048
14190
|
const announceEndpoint = (sessionID) => {
|
|
@@ -14065,10 +14207,6 @@ var server = async (ctx, rawOptions) => {
|
|
|
14065
14207
|
if (config.privacyMode === "forceLocalOnSensitive" && localDefault(config) === null) {
|
|
14066
14208
|
warnInertPrivacySetting(runtimeConfig.searchedPaths, diagnostics.warn("privacy-inert"));
|
|
14067
14209
|
}
|
|
14068
|
-
const getOpencodeVersion = createLazyOpencodeVersionReader(ctx.serverUrl, {
|
|
14069
|
-
client: ctx.client,
|
|
14070
|
-
onFailure: createOpencodeVersionReadFailureLogger(MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, diagnostics.warn("opencode-version-read-failed", { toast: false }))
|
|
14071
|
-
});
|
|
14072
14210
|
if (isGateApplicable(graphConfig.effectiveMode)) {
|
|
14073
14211
|
const opencodeVersion = await getOpencodeVersion();
|
|
14074
14212
|
const gateInput = await buildGateInput(config, storage, opencodeVersion, diagnostics.warn("soak-evidence-rejected", { toast: false }));
|
|
@@ -14227,12 +14365,14 @@ export {
|
|
|
14227
14365
|
warnInertPrivacySetting,
|
|
14228
14366
|
server,
|
|
14229
14367
|
recorderPortsFromStorage,
|
|
14368
|
+
readPackagedCertificate,
|
|
14230
14369
|
readOpencodeVersion,
|
|
14231
14370
|
logTelemetryWarning,
|
|
14232
14371
|
logTelemetryError,
|
|
14233
14372
|
logOpencodeServerUrl,
|
|
14234
14373
|
logAvailabilityRefreshError,
|
|
14235
14374
|
isMissingFile,
|
|
14375
|
+
evaluateCutoverGateForBoundary,
|
|
14236
14376
|
src_default as default,
|
|
14237
14377
|
createOpencodeVersionReadFailureLogger,
|
|
14238
14378
|
createLazyOpencodeVersionReader,
|
|
@@ -14240,5 +14380,6 @@ export {
|
|
|
14240
14380
|
createCliDeps,
|
|
14241
14381
|
buildGateInput,
|
|
14242
14382
|
PACKAGE_VERSION,
|
|
14383
|
+
OPENCODE_VERSION_UNRESOLVED_REASON,
|
|
14243
14384
|
MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES
|
|
14244
14385
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmanuelcorral/openteam",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"packageManager": "bun@1.3.14",
|
|
5
5
|
"description": "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"sideEffects": false,
|
|
60
60
|
"scripts": {
|
|
61
61
|
"prebuild": "bun run clean",
|
|
62
|
-
"build": "bun run build:js && bun run build:cli && bun run build:types",
|
|
62
|
+
"build": "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
|
|
63
63
|
"build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
|
|
64
64
|
"build:cli": "bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner \"#!/usr/bin/env node\" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
|
|
65
65
|
"build:types": "tsc -p tsconfig.build.json",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"test:cov": "bun test --coverage",
|
|
69
69
|
"certify:shadow": "bun run scripts/certify-artifact.ts shadow",
|
|
70
70
|
"certify:release": "bun run scripts/certify-artifact.ts release",
|
|
71
|
+
"build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
|
|
71
72
|
"coverage:check": "node scripts/check-coverage.mjs",
|
|
72
73
|
"typecheck": "tsc --noEmit",
|
|
73
74
|
"lint": "biome check .",
|