@aiwg/cli 2026.9.20 → 2026.9.21
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/agentic/code/providers/capability-matrix.yaml +58 -6
- package/agentic/code/providers/model-capabilities.v1.json +34 -0
- package/agentic/code/providers/model-catalog.v1.json +55 -13
- package/bin/aiwg.mjs +2 -0
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/enrichment/cli.js +1 -1
- package/dist/src/artifacts/views/cli.js +1 -1
- package/dist/src/audit/operator-decision.js +10 -0
- package/dist/src/auth/credential-store.js +127 -27
- package/dist/src/catalog/builtin-models.json +272 -40
- package/dist/src/catalog/cli.js +1 -1
- package/dist/src/catalog/cli.mjs +1 -1
- package/dist/src/cli/handlers/effect.js +872 -0
- package/dist/src/cli/handlers/help.js +2 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/models.js +41 -10
- package/dist/src/cli/handlers/ralph.js +1 -1
- package/dist/src/cli/handlers/sessions.js +113 -37
- package/dist/src/cli/handlers/subcommands.js +31 -0
- package/dist/src/cli/handlers/use.js +46 -9
- package/dist/src/cli/handlers/utilities.js +10 -0
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/scope-resolver.js +112 -9
- package/dist/src/cli/services/deployment-verification.js +5 -0
- package/dist/src/config/aiwg-config.js +3 -0
- package/dist/src/decision/acceptance-rollout.js +93 -0
- package/dist/src/decision/acceptance.js +175 -0
- package/dist/src/decision/adapters/jev.js +234 -13
- package/dist/src/decision/adapters/llm-subagent.js +60 -12
- package/dist/src/decision/admission.js +550 -0
- package/dist/src/decision/batch-benchmark.js +47 -0
- package/dist/src/decision/batch-receipts/accounting.js +75 -0
- package/dist/src/decision/batch-receipts/export.js +40 -0
- package/dist/src/decision/batch-receipts/index.js +9 -0
- package/dist/src/decision/batch-receipts/protection.js +40 -0
- package/dist/src/decision/batch-receipts/receipt.js +44 -0
- package/dist/src/decision/batch-receipts/result-store.js +341 -0
- package/dist/src/decision/batch-receipts/store.js +362 -0
- package/dist/src/decision/batch-receipts/types.js +2 -0
- package/dist/src/decision/batch-receipts/validate.js +127 -0
- package/dist/src/decision/batch.js +68 -0
- package/dist/src/decision/calibration/index.js +4 -0
- package/dist/src/decision/calibration/receipts.js +130 -0
- package/dist/src/decision/calibration/registry.js +258 -0
- package/dist/src/decision/calibration/types.js +2 -0
- package/dist/src/decision/compile-cache/benchmark.js +62 -0
- package/dist/src/decision/compile-cache/compatibility.js +65 -0
- package/dist/src/decision/compile-cache/file-store.js +280 -0
- package/dist/src/decision/compile-cache/identity.js +26 -0
- package/dist/src/decision/compile-cache/index.js +9 -0
- package/dist/src/decision/compile-cache/prefix.js +21 -0
- package/dist/src/decision/compile-cache/records.js +86 -0
- package/dist/src/decision/compile-cache/runtime.js +68 -0
- package/dist/src/decision/compile-cache/store.js +108 -0
- package/dist/src/decision/compile-cache/types.js +2 -0
- package/dist/src/decision/context-plan.js +234 -0
- package/dist/src/decision/context-qualification.js +60 -0
- package/dist/src/decision/ensemble/aggregate.js +215 -0
- package/dist/src/decision/ensemble/contract.js +316 -0
- package/dist/src/decision/ensemble/drift.js +51 -0
- package/dist/src/decision/ensemble/index.js +6 -0
- package/dist/src/decision/ensemble/report.js +71 -0
- package/dist/src/decision/ensemble/types.js +2 -0
- package/dist/src/decision/evaluate.js +1024 -78
- package/dist/src/decision/file-lifecycle-store.js +168 -0
- package/dist/src/decision/graph-beam.js +40 -0
- package/dist/src/decision/graph-budget.js +110 -0
- package/dist/src/decision/graph-decision-bridge.js +93 -0
- package/dist/src/decision/graph-evidence.js +123 -0
- package/dist/src/decision/graph-flow-adapter.js +56 -0
- package/dist/src/decision/graph-flow.js +105 -0
- package/dist/src/decision/graph-parallel.js +79 -0
- package/dist/src/decision/graph-patterns.js +20 -0
- package/dist/src/decision/graph-run-store.js +66 -0
- package/dist/src/decision/graph-run.js +166 -0
- package/dist/src/decision/graph-skill-bridge.js +91 -0
- package/dist/src/decision/graph-templates.js +56 -0
- package/dist/src/decision/graph.js +115 -0
- package/dist/src/decision/index.js +35 -0
- package/dist/src/decision/job-accounting.js +93 -0
- package/dist/src/decision/job-contract.js +186 -0
- package/dist/src/decision/job-effects.js +95 -0
- package/dist/src/decision/job-evaluate.js +80 -0
- package/dist/src/decision/job-gateway.js +121 -0
- package/dist/src/decision/job-http.js +194 -0
- package/dist/src/decision/job-payload-store.js +258 -0
- package/dist/src/decision/job-poll-limiter.js +92 -0
- package/dist/src/decision/job-quota.js +158 -0
- package/dist/src/decision/job-runtime.js +240 -0
- package/dist/src/decision/job-scheduler.js +26 -0
- package/dist/src/decision/job-service.js +117 -0
- package/dist/src/decision/job-store.js +264 -0
- package/dist/src/decision/job-worker.js +141 -0
- package/dist/src/decision/lifecycle.js +148 -0
- package/dist/src/decision/patterns/artifacts.js +173 -0
- package/dist/src/decision/patterns/catalog.js +108 -0
- package/dist/src/decision/patterns/durable-review.js +237 -0
- package/dist/src/decision/patterns/index.js +8 -0
- package/dist/src/decision/patterns/playground.js +401 -0
- package/dist/src/decision/patterns/recorded.js +43 -0
- package/dist/src/decision/patterns/specs.js +174 -0
- package/dist/src/decision/patterns/types.js +2 -0
- package/dist/src/decision/portable-secrets.js +53 -0
- package/dist/src/decision/projection.js +210 -0
- package/dist/src/decision/qualification/capture.js +67 -0
- package/dist/src/decision/qualification/drift.js +68 -0
- package/dist/src/decision/qualification/gate-evidence.js +172 -0
- package/dist/src/decision/qualification/gates.js +83 -0
- package/dist/src/decision/qualification/index.js +12 -0
- package/dist/src/decision/qualification/load.js +275 -0
- package/dist/src/decision/qualification/manifest.js +37 -0
- package/dist/src/decision/qualification/privacy.js +41 -0
- package/dist/src/decision/qualification/quality.js +213 -0
- package/dist/src/decision/qualification/release.js +116 -0
- package/dist/src/decision/qualification/runner.js +408 -0
- package/dist/src/decision/qualification/types.js +2 -0
- package/dist/src/decision/receipts.js +30 -7
- package/dist/src/decision/result-cache/index.js +6 -0
- package/dist/src/decision/result-cache/integrity.js +28 -0
- package/dist/src/decision/result-cache/key.js +28 -0
- package/dist/src/decision/result-cache/service.js +92 -0
- package/dist/src/decision/result-cache/store.js +327 -0
- package/dist/src/decision/result-cache/types.js +3 -0
- package/dist/src/decision/review/authorization.js +110 -0
- package/dist/src/decision/review/effect-ledger-adapter.js +321 -0
- package/dist/src/decision/review/index.js +12 -0
- package/dist/src/decision/review/migrate.js +34 -0
- package/dist/src/decision/review/operator-audit.js +59 -0
- package/dist/src/decision/review/recovery-adapters.js +252 -0
- package/dist/src/decision/review/recovery.js +33 -0
- package/dist/src/decision/review/service.js +498 -0
- package/dist/src/decision/review/store.js +219 -0
- package/dist/src/decision/review/types.js +2 -0
- package/dist/src/decision/review/validate.js +205 -0
- package/dist/src/decision/scheduler.js +115 -0
- package/dist/src/decision/telemetry/context.js +42 -0
- package/dist/src/decision/telemetry/debug-sidecar.js +152 -0
- package/dist/src/decision/telemetry/exporter.js +77 -0
- package/dist/src/decision/telemetry/file-debug-backend.js +112 -0
- package/dist/src/decision/telemetry/index.js +13 -0
- package/dist/src/decision/telemetry/mapping.js +146 -0
- package/dist/src/decision/telemetry/metrics.js +164 -0
- package/dist/src/decision/telemetry/otlp-http.js +183 -0
- package/dist/src/decision/telemetry/redaction.js +93 -0
- package/dist/src/decision/telemetry/retention.js +109 -0
- package/dist/src/decision/telemetry/runtime.js +310 -0
- package/dist/src/decision/telemetry/trace.js +158 -0
- package/dist/src/decision/telemetry/types.js +3 -0
- package/dist/src/decision/validate.js +135 -5
- package/dist/src/effects/checkpoint-sinks.js +94 -0
- package/dist/src/effects/checkpoint.js +76 -0
- package/dist/src/effects/decision-links.js +54 -0
- package/dist/src/effects/errors.js +48 -0
- package/dist/src/effects/identity.js +178 -0
- package/dist/src/effects/index.js +25 -0
- package/dist/src/effects/keyring.js +160 -0
- package/dist/src/effects/keys.js +130 -0
- package/dist/src/effects/ledger.js +435 -0
- package/dist/src/effects/lifecycle.js +110 -0
- package/dist/src/effects/lock-recovery.js +269 -0
- package/dist/src/effects/reader.js +103 -0
- package/dist/src/effects/records.js +114 -0
- package/dist/src/effects/redaction.js +51 -0
- package/dist/src/effects/schema.js +73 -0
- package/dist/src/effects/store.js +239 -0
- package/dist/src/effects/types.js +49 -0
- package/dist/src/effects/verifiers/decision.js +168 -0
- package/dist/src/effects/verifiers/file.js +106 -0
- package/dist/src/effects/verifiers/git.js +206 -0
- package/dist/src/effects/verifiers/index.js +90 -0
- package/dist/src/effects/verifiers/registry.js +148 -0
- package/dist/src/effects/verifiers/review.js +123 -0
- package/dist/src/effects/verifiers/tracker.js +299 -0
- package/dist/src/effects/verifiers/types.js +21 -0
- package/dist/src/effects/verify.js +295 -0
- package/dist/src/evidence/bundle.js +2 -0
- package/dist/src/extensions/commands/definitions.js +28 -0
- package/dist/src/extensions/manifest.js +1 -0
- package/dist/src/marketplace/provenance.js +25 -27
- package/dist/src/mcp/cli.mjs +11 -1
- package/dist/src/mcp/grok-build-config.mjs +189 -0
- package/dist/src/mcp/registry.js +21 -1
- package/dist/src/mcp/registry.mjs +20 -1
- package/dist/src/models/grok-build-models.js +356 -0
- package/dist/src/models/index.js +1 -0
- package/dist/src/models/model-capabilities.v1.json +34 -0
- package/dist/src/models/model-catalog.v1.json +55 -13
- package/dist/src/models/model-discovery.js +41 -5
- package/dist/src/models/provider-policy.js +3 -2
- package/dist/src/plugin/skill-command-translator.js +1 -0
- package/dist/src/providers/capability-matrix.yaml +58 -6
- package/dist/src/providers/grok-build-acp.js +192 -0
- package/dist/src/providers/grok-build-dispatch.js +251 -0
- package/dist/src/providers/grok-build-headless.js +170 -0
- package/dist/src/providers/muse-paths.js +105 -0
- package/dist/src/providers/provider-definitions.js +93 -7
- package/dist/src/providers/provider-definitions.mjs +21 -1
- package/dist/src/providers/provider-inventory.js +3 -0
- package/dist/src/security/artifact-attestation.js +4 -8
- package/dist/src/security/signing.js +119 -0
- package/dist/src/sessions/adapters/grok-build.js +187 -0
- package/dist/src/sessions/adapters/muse.js +412 -0
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/grok-build-cli.js +196 -0
- package/dist/src/sessions/index.js +3 -0
- package/dist/src/sessions/workspace-discovery.js +9 -5
- package/dist/src/skills/deployer.js +33 -5
- package/dist/src/smiths/context-pipeline/claude-hook.js +2 -0
- package/dist/src/smiths/context-pipeline/context-file-safety.js +18 -0
- package/dist/src/smiths/context-pipeline/finalization.js +1 -32
- package/dist/src/smiths/context-pipeline/generator.js +12 -0
- package/dist/src/smiths/context-pipeline/index.js +1 -1
- package/dist/src/smiths/context-pipeline/managed-hook.js +2 -0
- package/dist/src/smiths/context-pipeline/muse-bridge.js +50 -0
- package/dist/src/smiths/context-pipeline/provider-policy.js +5 -0
- package/dist/src/smiths/context-pipeline/workspace-context.js +22 -16
- package/dist/src/smiths/hook-bridge/grok-build-translator.js +64 -0
- package/dist/src/smiths/hook-bridge/index.js +3 -0
- package/dist/src/smiths/hook-bridge/shim.js +2 -0
- package/dist/src/smiths/hook-bridge/types.js +5 -0
- package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
- package/dist/src/storage/protected-files.js +102 -0
- package/dist/src/tracker/forge-http.js +371 -0
- package/dist/src/tracker/project-inputs.js +41 -0
- package/package.json +2 -1
- package/schemas/decision/BatchReceipt.v1.schema.json +50 -0
- package/schemas/decision/CalibrationArtifact.v1.schema.json +76 -0
- package/schemas/decision/CalibrationCompatibility.v1.schema.json +18 -0
- package/schemas/decision/CalibrationGovernanceReceipt.v1.schema.json +44 -0
- package/schemas/decision/DecisionAdmissionEvidence.v1.schema.json +43 -0
- package/schemas/decision/DecisionBinding.schema.json +309 -0
- package/schemas/decision/DecisionBinding.v1alpha2.schema.json +369 -0
- package/schemas/decision/DecisionCacheTelemetry.v1.schema.json +20 -0
- package/schemas/decision/DecisionChampionChallenger.v1.schema.json +100 -0
- package/schemas/decision/DecisionCompileCacheEntry.v1.schema.json +35 -0
- package/schemas/decision/DecisionCompileCacheIdentity.v1.schema.json +26 -0
- package/schemas/decision/DecisionCompileCacheTombstone.v1.schema.json +31 -0
- package/schemas/decision/DecisionContextEvidence.v1.schema.json +136 -0
- package/schemas/decision/DecisionDefinition.schema.json +159 -0
- package/schemas/decision/DecisionDefinition.v1alpha2.schema.json +203 -0
- package/schemas/decision/DecisionDriftResponse.v1.schema.json +55 -0
- package/schemas/decision/DecisionEnsembleAggregate.v1.schema.json +86 -0
- package/schemas/decision/DecisionEnsembleIntegrityReport.v1.schema.json +64 -0
- package/schemas/decision/DecisionEnsemblePolicy.v1.schema.json +130 -0
- package/schemas/decision/DecisionGraph.v1.schema.json +77 -0
- package/schemas/decision/DecisionJob.v1.schema.json +71 -0
- package/schemas/decision/DecisionLifecyclePolicy.v1.schema.json +52 -0
- package/schemas/decision/DecisionProjectionPolicy.v1.schema.json +65 -0
- package/schemas/decision/DecisionProviderPrefixCompatibility.v1.schema.json +45 -0
- package/schemas/decision/DecisionProviderPrefixEvidence.v1.schema.json +17 -0
- package/schemas/decision/DecisionProviderPrefixIdentity.v1.schema.json +36 -0
- package/schemas/decision/DecisionResult.schema.json +524 -0
- package/schemas/decision/DecisionResult.v1alpha2.schema.json +619 -0
- package/schemas/decision/DecisionResultCacheEntry.v1.schema.json +53 -0
- package/schemas/decision/DecisionReview.schema.json +29 -0
- package/schemas/decision/DecisionRuleset.schema.json +313 -0
- package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +313 -0
- package/schemas/decision/DecisionTelemetryTrace.v1.schema.json +95 -0
- package/schemas/decision/RulesetResult.schema.json +721 -0
- package/schemas/decision/RulesetResult.v1alpha2.schema.json +891 -0
- package/schemas/security/pr-evidence-receipt.v1.schema.json +77 -0
- package/schemas/security/threat-assessment-report.v1.schema.json +71 -1
- package/tools/agents/deploy-agents.mjs +21 -6
- package/tools/agents/providers/base.mjs +7 -4
- package/tools/agents/providers/codex.mjs +1 -1
- package/tools/agents/providers/grok-build.d.mts +7 -0
- package/tools/agents/providers/grok-build.mjs +249 -9
- package/tools/agents/providers/model-role.mjs +6 -3
- package/tools/agents/providers/muse-hooks.mjs +785 -0
- package/tools/agents/providers/muse-paths.mjs +112 -0
- package/tools/agents/providers/muse.mjs +336 -0
|
@@ -12,6 +12,7 @@ import { createHash, createPrivateKey, createPublicKey, sign as cryptoSign, veri
|
|
|
12
12
|
import { lstat, readFile, readdir } from 'node:fs/promises';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { promisify } from 'node:util';
|
|
15
|
+
import { createCanonicalSigner } from '../security/signing.js';
|
|
15
16
|
import { MARKETPLACE_ENVELOPE_SCHEMA, MARKETPLACE_LOCK_SCHEMA, MARKETPLACE_RECEIPT_SCHEMA, } from './provenance-types.js';
|
|
16
17
|
const execFileAsync = promisify(execFile);
|
|
17
18
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -55,7 +56,20 @@ function stable(value) {
|
|
|
55
56
|
}
|
|
56
57
|
return value;
|
|
57
58
|
}
|
|
58
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* RFC-8785-style deterministic JSON for the JSON-native protocol values.
|
|
61
|
+
*
|
|
62
|
+
* For JSON-native values (no `undefined`, no non-finite numbers, no `toJSON`,
|
|
63
|
+
* no array-index keys such as "9" or "10") this is byte-identical to the
|
|
64
|
+
* RFC 8785 `artifact-trust.canonicalJson`: both sort keys by UTF-16 code unit
|
|
65
|
+
* and serialize scalars with `JSON.stringify`. It is kept because existing
|
|
66
|
+
* marketplace signatures depend on its handling of the other values:
|
|
67
|
+
* `undefined` object members are dropped, `undefined` array entries and
|
|
68
|
+
* non-finite numbers become `null` (RFC 8785 throws), and array-index keys are
|
|
69
|
+
* emitted first in numeric order because the sorted object is rebuilt.
|
|
70
|
+
* test/unit/security/canonical-json-parity.test.ts pins the parity and the
|
|
71
|
+
* differences.
|
|
72
|
+
*/
|
|
59
73
|
export function canonicalJson(value) {
|
|
60
74
|
return JSON.stringify(stable(value));
|
|
61
75
|
}
|
|
@@ -411,40 +425,24 @@ export function envelopeSigningPayload(envelope) {
|
|
|
411
425
|
const { signatures: _signatures, ...payload } = envelope;
|
|
412
426
|
return payload;
|
|
413
427
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
428
|
+
// Marketplace signatures are made over this module's `canonicalJson`, which is
|
|
429
|
+
// kept (see its comment) so existing envelopes and receipts verify unchanged.
|
|
430
|
+
// The helpers themselves live in src/security/signing.ts.
|
|
431
|
+
const marketplaceSigner = createCanonicalSigner({
|
|
432
|
+
canonicalize: canonicalJson,
|
|
433
|
+
keyTypeErrorMessage: 'Marketplace signing keys must use Ed25519',
|
|
434
|
+
});
|
|
422
435
|
function publicKeyFromBase64(value) {
|
|
423
436
|
return createPublicKey({ key: Buffer.from(value, 'base64'), format: 'der', type: 'spki' });
|
|
424
437
|
}
|
|
425
438
|
export function signCanonicalDocument(document, privateKeyPem, options = {}) {
|
|
426
|
-
|
|
427
|
-
if (privateKey.asymmetricKeyType !== 'ed25519')
|
|
428
|
-
throw new Error('Marketplace signing keys must use Ed25519');
|
|
429
|
-
const publicKey = options.publicKeyPem ? createPublicKey(options.publicKeyPem) : createPublicKey(privateKey);
|
|
430
|
-
const payload = canonicalJson(document);
|
|
431
|
-
return {
|
|
432
|
-
keyId: options.keyId ?? signingKeyId(publicKey),
|
|
433
|
-
algorithm: 'ed25519',
|
|
434
|
-
publicKey: publicKeyDer(publicKey).toString('base64'),
|
|
435
|
-
signedAt: options.signedAt ?? new Date().toISOString(),
|
|
436
|
-
payloadSha256: sha256(payload),
|
|
437
|
-
signature: cryptoSign(null, Buffer.from(payload), privateKey).toString('base64'),
|
|
438
|
-
};
|
|
439
|
+
return marketplaceSigner.signCanonicalDocument(document, privateKeyPem, options);
|
|
439
440
|
}
|
|
440
441
|
export function verifyCanonicalSignature(document, signature) {
|
|
441
|
-
|
|
442
|
-
return signature.algorithm === 'ed25519'
|
|
443
|
-
&& signature.payloadSha256 === sha256(payload)
|
|
444
|
-
&& cryptoVerify(null, Buffer.from(payload), publicKeyFromBase64(signature.publicKey), Buffer.from(signature.signature, 'base64'));
|
|
442
|
+
return marketplaceSigner.verifyCanonicalSignature(document, signature);
|
|
445
443
|
}
|
|
446
444
|
export function signingKeyId(publicKey) {
|
|
447
|
-
return
|
|
445
|
+
return marketplaceSigner.signingKeyId(publicKey);
|
|
448
446
|
}
|
|
449
447
|
export function signProvenanceEnvelope(envelope, privateKeyPem, options = {}) {
|
|
450
448
|
validateProvenanceEnvelope(envelope);
|
package/dist/src/mcp/cli.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
import { McpProfileRegistry } from './profiles.mjs';
|
|
20
20
|
import { getMcpInjectionDefinition } from '../providers/provider-definitions.mjs';
|
|
21
21
|
import { manageOmpMcp } from './omp-config.mjs';
|
|
22
|
+
import { unmanageGrokBuildMcp } from './grok-build-config.mjs';
|
|
22
23
|
|
|
23
24
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
|
|
@@ -1202,12 +1203,21 @@ export async function main(args = process.argv.slice(2)) {
|
|
|
1202
1203
|
|
|
1203
1204
|
case 'uninject': {
|
|
1204
1205
|
const provider = parseFlag(subArgs, '--provider');
|
|
1205
|
-
if (!['omp', 'oh-my-pi'].includes(provider)) throw new Error('uninject
|
|
1206
|
+
if (!['omp', 'oh-my-pi', 'grok-build'].includes(provider)) throw new Error('uninject supports --provider omp or grok-build');
|
|
1206
1207
|
const scope = parseFlag(subArgs, '--scope') || 'project';
|
|
1207
1208
|
if (!['project', 'user'].includes(scope)) throw new Error('Scope must be project or user');
|
|
1208
1209
|
const remove = (parseFlag(subArgs, '--servers') || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
1209
1210
|
if (!remove.length) throw new Error('uninject requires --servers name[,name]');
|
|
1210
1211
|
const configPath = getProviderConfigPath(provider, parseFlag(subArgs, '--project') || '.', { scope });
|
|
1212
|
+
if (provider === 'grok-build') {
|
|
1213
|
+
const projectDir = parseFlag(subArgs, '--project') || '.';
|
|
1214
|
+
const result = await unmanageGrokBuildMcp(configPath, remove, {
|
|
1215
|
+
dryRun: subArgs.includes('--dry-run'),
|
|
1216
|
+
root: scope === 'user' ? path.dirname(path.dirname(configPath)) : path.resolve(projectDir),
|
|
1217
|
+
});
|
|
1218
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1219
|
+
break;
|
|
1220
|
+
}
|
|
1211
1221
|
const result = await manageOmpMcp(configPath, [], { remove, dryRun: subArgs.includes('--dry-run') });
|
|
1212
1222
|
console.log(JSON.stringify(result, null, 2));
|
|
1213
1223
|
break;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { constants } from 'node:fs';
|
|
2
|
+
import { access, lstat, mkdir, open, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, relative, resolve } from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { replaceServer } from './toml-editor.mjs';
|
|
6
|
+
|
|
7
|
+
const START = name => `# >>> AIWG-managed Grok MCP: ${name}`;
|
|
8
|
+
const END = name => `# <<< AIWG-managed Grok MCP: ${name}`;
|
|
9
|
+
const ENV_REF = /^(?:[^$]|\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?\})*$/;
|
|
10
|
+
|
|
11
|
+
function string(value) {
|
|
12
|
+
if (typeof value !== 'string') throw new Error('Grok MCP string fields must be strings');
|
|
13
|
+
return JSON.stringify(value).replace(/\u007f/g, '\\u007f');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function key(value) {
|
|
17
|
+
return /^[A-Za-z0-9_-]+$/.test(value) ? value : string(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function inline(values = {}) {
|
|
21
|
+
return `{ ${Object.entries(values).map(([name, value]) => `${key(name)} = ${string(value)}`).join(', ')} }`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function renderGrokMcpServer(server) {
|
|
25
|
+
if (!server?.name) throw new Error('Grok MCP server name is required');
|
|
26
|
+
const lines = [`[mcp_servers.${key(server.name)}]`];
|
|
27
|
+
if (server.type === 'stdio') {
|
|
28
|
+
if (!server.command) throw new Error(`Grok MCP stdio server "${server.name}" requires command`);
|
|
29
|
+
lines.push(`command = ${string(server.command)}`);
|
|
30
|
+
if (server.args?.length) lines.push(`args = [${server.args.map(string).join(', ')}]`);
|
|
31
|
+
if (server.env && Object.keys(server.env).length) lines.push(`env = ${inline(server.env)}`);
|
|
32
|
+
} else {
|
|
33
|
+
if (!server.url) throw new Error(`Grok MCP HTTP server "${server.name}" requires url`);
|
|
34
|
+
lines.push(`url = ${string(server.url)}`);
|
|
35
|
+
const headers = { ...(server.headers || {}) };
|
|
36
|
+
for (const [header, envName] of Object.entries(server.headerEnv || {})) {
|
|
37
|
+
headers[header] = `\${${envName}}`;
|
|
38
|
+
}
|
|
39
|
+
if (Object.keys(headers).length) lines.push(`headers = ${inline(headers)}`);
|
|
40
|
+
}
|
|
41
|
+
if (server.enabled === false) lines.push('enabled = false');
|
|
42
|
+
for (const value of [server.command, server.url, ...(server.args || []), ...Object.values(server.env || {}), ...Object.values(server.headers || {})]) {
|
|
43
|
+
if (typeof value === 'string' && value.includes('$') && !ENV_REF.test(value)) {
|
|
44
|
+
throw new Error(`Grok MCP server "${server.name}" contains an invalid environment reference`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
lines.push('startup_timeout_sec = 30', 'tool_timeout_sec = 6000');
|
|
48
|
+
return lines.join('\n');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function managedRange(text, name) {
|
|
52
|
+
let start = text.indexOf(START(name));
|
|
53
|
+
const endMarker = END(name);
|
|
54
|
+
const end = text.indexOf(endMarker);
|
|
55
|
+
if (start < 0 && end < 0) return null;
|
|
56
|
+
if (start < 0 || end < start) throw new Error(`Malformed AIWG ownership markers for Grok MCP server "${name}"`);
|
|
57
|
+
// Deployment owns the single blank separator it adds before its marker.
|
|
58
|
+
if (start > 0 && text.slice(start - 2, start) === '\n\n') start -= 1;
|
|
59
|
+
return [start, end + endMarker.length + (text[end + endMarker.length] === '\n' ? 1 : 0)];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function mergeGrokMcpServers(text, servers) {
|
|
63
|
+
let output = text;
|
|
64
|
+
const seen = new Set();
|
|
65
|
+
for (const server of servers) {
|
|
66
|
+
if (seen.has(server.name)) throw new Error(`Duplicate Grok MCP server name "${server.name}"`);
|
|
67
|
+
seen.add(server.name);
|
|
68
|
+
const section = renderGrokMcpServer(server);
|
|
69
|
+
const range = managedRange(output, server.name);
|
|
70
|
+
const probeText = range ? output.slice(0, range[0]) + output.slice(range[1]) : output;
|
|
71
|
+
const probe = replaceServer(probeText, server.name, section);
|
|
72
|
+
if (!range && probe.alreadyPresent) {
|
|
73
|
+
throw new Error(`Grok MCP server "${server.name}" is operator-managed; refusing to replace it`);
|
|
74
|
+
}
|
|
75
|
+
const prefix = range ? output.slice(0, range[0]) : `${output}${output && !output.endsWith('\n') ? '\n' : ''}`;
|
|
76
|
+
const block = `${prefix ? '\n' : ''}${START(server.name)}\n${section}\n${END(server.name)}\n`;
|
|
77
|
+
output = range ? prefix + block + output.slice(range[1]) : prefix + block;
|
|
78
|
+
// Parse and validate the completed document without exposing its content.
|
|
79
|
+
replaceServer(output, '__aiwg_validation_probe__', '[mcp_servers.__aiwg_validation_probe__]\ncommand = "true"');
|
|
80
|
+
}
|
|
81
|
+
return output;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function removeGrokMcpServers(text, names) {
|
|
85
|
+
let output = text;
|
|
86
|
+
const removed = [];
|
|
87
|
+
for (const name of names) {
|
|
88
|
+
const range = managedRange(output, name);
|
|
89
|
+
if (!range) continue;
|
|
90
|
+
output = output.slice(0, range[0]) + output.slice(range[1]);
|
|
91
|
+
removed.push(name);
|
|
92
|
+
}
|
|
93
|
+
if (output) replaceServer(output, '__aiwg_validation_probe__', '[mcp_servers.__aiwg_validation_probe__]\ncommand = "true"');
|
|
94
|
+
return { text: output, removed };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function assertSafeTarget(configPath, root) {
|
|
98
|
+
const absolute = resolve(configPath);
|
|
99
|
+
const base = resolve(root);
|
|
100
|
+
const rel = relative(base, absolute);
|
|
101
|
+
if (!rel || rel.startsWith('..') || rel.startsWith('/') || rel.includes('\0')) {
|
|
102
|
+
throw new Error('Grok MCP config path must be a file beneath the selected configuration root');
|
|
103
|
+
}
|
|
104
|
+
for (let current = dirname(absolute); current !== base; current = dirname(current)) {
|
|
105
|
+
try {
|
|
106
|
+
if ((await lstat(current)).isSymbolicLink()) throw new Error('Refusing Grok MCP path through a symbolic link');
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const info = await lstat(absolute);
|
|
113
|
+
if (info.isSymbolicLink() || !info.isFile()) throw new Error('Refusing unsafe Grok MCP config target');
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function atomicWrite(file, content) {
|
|
120
|
+
await mkdir(dirname(file), { recursive: true });
|
|
121
|
+
const temp = `${file}.aiwg-${process.pid}-${Date.now()}.tmp`;
|
|
122
|
+
try {
|
|
123
|
+
const handle = await open(temp, 'wx', 0o600);
|
|
124
|
+
await handle.writeFile(content, 'utf8');
|
|
125
|
+
await handle.sync();
|
|
126
|
+
await handle.close();
|
|
127
|
+
await rename(temp, file);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
await rm(temp, { force: true });
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function manageGrokBuildMcp(configPath, servers, options = {}) {
|
|
135
|
+
const root = options.root || dirname(dirname(configPath));
|
|
136
|
+
if (options.blockedByPolicy) return { configPath, state: 'blocked-by-policy', serversInjected: [], alreadyPresent: [], error: options.blockedByPolicy };
|
|
137
|
+
await assertSafeTarget(configPath, root);
|
|
138
|
+
let before = '';
|
|
139
|
+
try { before = await readFile(configPath, 'utf8'); } catch (error) { if (error?.code !== 'ENOENT') throw error; }
|
|
140
|
+
const alreadyPresent = servers.filter(server => managedRange(before, server.name)).map(server => server.name);
|
|
141
|
+
const after = mergeGrokMcpServers(before, servers);
|
|
142
|
+
if (!options.dryRun && after !== before) await atomicWrite(configPath, after);
|
|
143
|
+
return { configPath, state: options.disabled ? 'disabled' : 'configured', serversInjected: servers.map(server => server.name), alreadyPresent };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function unmanageGrokBuildMcp(configPath, names, options = {}) {
|
|
147
|
+
const root = options.root || dirname(dirname(configPath));
|
|
148
|
+
await assertSafeTarget(configPath, root);
|
|
149
|
+
let before;
|
|
150
|
+
try { before = await readFile(configPath, 'utf8'); } catch (error) { if (error?.code === 'ENOENT') return { configPath, state: 'absent', removed: [] }; throw error; }
|
|
151
|
+
const result = removeGrokMcpServers(before, names);
|
|
152
|
+
if (!options.dryRun && result.text !== before) await atomicWrite(configPath, result.text);
|
|
153
|
+
return { configPath, state: result.removed.length ? 'configured' : 'absent', removed: result.removed };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function runJson(binary, args, cwd, env) {
|
|
157
|
+
const run = spawnSync(binary, args, { cwd, env, encoding: 'utf8', timeout: 30_000 });
|
|
158
|
+
if (run.error || run.status !== 0) return { ok: false, detail: run.error?.message || run.stderr || `exit ${run.status}` };
|
|
159
|
+
try { return { ok: true, value: JSON.parse(run.stdout || '{}') }; }
|
|
160
|
+
catch { return { ok: false, detail: 'Grok returned malformed JSON' }; }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function inspectGrokBuildNative(options = {}) {
|
|
164
|
+
const configPath = options.configPath || resolve(options.projectDir || '.', '.grok/config.toml');
|
|
165
|
+
let config = '';
|
|
166
|
+
try { config = await readFile(configPath, 'utf8'); } catch (error) { if (error?.code === 'ENOENT') return { mcp: 'absent', hooks: 'absent', diagnostics: [] }; throw error; }
|
|
167
|
+
const disabled = /\benabled\s*=\s*false\b/.test(config);
|
|
168
|
+
const names = [...config.matchAll(/# >>> AIWG-managed Grok MCP: ([^\n]+)/g)].map(match => match[1]);
|
|
169
|
+
let mcp = names.length ? (disabled ? 'disabled' : 'configured') : 'absent';
|
|
170
|
+
const diagnostics = [];
|
|
171
|
+
if (options.blockedByPolicy) mcp = 'blocked-by-policy';
|
|
172
|
+
if (options.binary && names.length) {
|
|
173
|
+
const inspected = runJson(options.binary, ['inspect', '--json'], options.projectDir || '.', options.env || process.env);
|
|
174
|
+
if (!inspected.ok || !names.every(name => JSON.stringify(inspected.value || {}).includes(name))) {
|
|
175
|
+
mcp = 'unhealthy';
|
|
176
|
+
diagnostics.push(inspected.ok ? 'grok inspect did not expose every AIWG-managed MCP server' : inspected.detail);
|
|
177
|
+
}
|
|
178
|
+
const listed = runJson(options.binary, ['mcp', 'list', '--json'], options.projectDir || '.', options.env || process.env);
|
|
179
|
+
if (!listed.ok) { mcp = 'unhealthy'; diagnostics.push(listed.detail); }
|
|
180
|
+
for (const name of names) {
|
|
181
|
+
const doctor = runJson(options.binary, ['mcp', 'doctor', name, '--json'], options.projectDir || '.', options.env || process.env);
|
|
182
|
+
if (!doctor.ok) { mcp = 'unhealthy'; diagnostics.push(`${name}: ${doctor.detail}`); }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const hooksDir = resolve(options.projectDir || '.', '.grok/hooks');
|
|
186
|
+
let hooks = 'absent';
|
|
187
|
+
try { await access(hooksDir, constants.R_OK); hooks = options.trusted === true ? 'configured' : 'untrusted'; } catch { /* absent */ }
|
|
188
|
+
return { mcp, hooks, servers: names, diagnostics };
|
|
189
|
+
}
|
package/dist/src/mcp/registry.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { manageOmpMcp } from './omp-config.mjs';
|
|
2
|
+
import { manageGrokBuildMcp } from './grok-build-config.mjs';
|
|
2
3
|
import { replaceServer } from './toml-editor.mjs';
|
|
3
4
|
import { resolveOmpPaths } from '../providers/omp-paths.mjs';
|
|
4
5
|
/**
|
|
@@ -11,7 +12,7 @@ import { resolveOmpPaths } from '../providers/omp-paths.mjs';
|
|
|
11
12
|
* @implements #554
|
|
12
13
|
*/
|
|
13
14
|
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
14
|
-
import { resolve } from 'path';
|
|
15
|
+
import { dirname, resolve } from 'path';
|
|
15
16
|
import { resolveConfigDir } from '../config/user-config.js';
|
|
16
17
|
import { getProviderDefinition } from '../providers/provider-definitions.js';
|
|
17
18
|
// ============================================
|
|
@@ -300,6 +301,9 @@ export function getProviderConfigPath(provider, projectDir = '.', options = {})
|
|
|
300
301
|
factory: resolve(homeDir, '.factory/mcp.json'),
|
|
301
302
|
codex: resolve(homeDir, '.codex/config.toml'),
|
|
302
303
|
openai: resolve(homeDir, '.codex/config.toml'),
|
|
304
|
+
'grok-build': options.scope === 'user'
|
|
305
|
+
? resolve(process.env.GROK_HOME || resolve(homeDir, '.grok'), 'config.toml')
|
|
306
|
+
: resolve(projectDir, '.grok/config.toml'),
|
|
303
307
|
opencode: resolve(projectDir, 'opencode.json'),
|
|
304
308
|
windsurf: resolve(homeDir, '.codeium/windsurf/mcp_config.json'),
|
|
305
309
|
warp: resolve(homeDir, '.warp/mcp.json'),
|
|
@@ -342,6 +346,21 @@ export async function injectServers(registry, provider, options = {}) {
|
|
|
342
346
|
return { ...result, error: error instanceof Error ? error.message : String(error) };
|
|
343
347
|
}
|
|
344
348
|
}
|
|
349
|
+
if (provider === 'grok-build') {
|
|
350
|
+
try {
|
|
351
|
+
const managed = await manageGrokBuildMcp(configPath, allServers, {
|
|
352
|
+
dryRun,
|
|
353
|
+
root: options.scope === 'user' ? dirname(dirname(configPath)) : resolve(projectDir),
|
|
354
|
+
});
|
|
355
|
+
if (!dryRun && !managed.error)
|
|
356
|
+
for (const server of allServers)
|
|
357
|
+
await registry.recordInjection(server.name, 'grok-build');
|
|
358
|
+
return { ...result, ...managed };
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
return { ...result, error: error instanceof Error ? error.message : String(error) };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
345
364
|
// Handle TOML-based providers (Codex/OpenAI) separately
|
|
346
365
|
if (provider === 'codex' || provider === 'openai') {
|
|
347
366
|
return injectToml(registry, allServers, configPath, provider, dryRun, result);
|
|
@@ -432,6 +451,7 @@ export const SUPPORTED_PROVIDERS = [
|
|
|
432
451
|
'cursor',
|
|
433
452
|
'factory',
|
|
434
453
|
'codex',
|
|
454
|
+
'grok-build',
|
|
435
455
|
'opencode',
|
|
436
456
|
'windsurf',
|
|
437
457
|
'warp',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { manageOmpMcp } from './omp-config.mjs';
|
|
2
|
+
import { manageGrokBuildMcp } from './grok-build-config.mjs';
|
|
2
3
|
import { replaceServer } from './toml-editor.mjs';
|
|
3
4
|
import { resolveOmpPaths } from '../providers/omp-paths.mjs';
|
|
4
5
|
/**
|
|
@@ -15,7 +16,7 @@ import { resolveOmpPaths } from '../providers/omp-paths.mjs';
|
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
18
|
-
import { resolve } from 'path';
|
|
19
|
+
import { dirname, resolve } from 'path';
|
|
19
20
|
import { homedir } from 'os';
|
|
20
21
|
import { existsSync } from 'fs';
|
|
21
22
|
import {
|
|
@@ -345,6 +346,24 @@ export async function injectServers(registry, provider, options = {}) {
|
|
|
345
346
|
}
|
|
346
347
|
}
|
|
347
348
|
|
|
349
|
+
if (normalizedProvider === 'grok-build') {
|
|
350
|
+
try {
|
|
351
|
+
const managed = await manageGrokBuildMcp(configPath, allServers, {
|
|
352
|
+
dryRun,
|
|
353
|
+
root: options.scope === 'user'
|
|
354
|
+
? dirname(dirname(configPath))
|
|
355
|
+
: resolve(projectDir),
|
|
356
|
+
blockedByPolicy: options.blockedByPolicy,
|
|
357
|
+
});
|
|
358
|
+
if (!dryRun && !managed.error) {
|
|
359
|
+
for (const server of allServers) await registry.recordInjection(server.name, 'grok-build');
|
|
360
|
+
}
|
|
361
|
+
return { ...result, ...managed };
|
|
362
|
+
} catch (error) {
|
|
363
|
+
return { ...result, error: error instanceof Error ? error.message : String(error) };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
348
367
|
if (mcpDefinition?.configFormat === 'toml') {
|
|
349
368
|
return injectToml(registry, allServers, configPath, provider, dryRun, result);
|
|
350
369
|
}
|