321 lines
10 KiB
TypeScript

import fs from 'node:fs';
import path from 'node:path';
import {
readMatrixCliConfig,
resetMatrixCliConfig,
resolvePackageRegistry,
setMatrixCliConfigValue,
type IMatrixCliConfig,
} from '../utils/config-store.js';
export interface IConfigCommandOptions {
readonly cwd?: string;
readonly configPath?: string;
}
export interface IConfigGetResult {
readonly key: string;
readonly value: string | undefined;
readonly source?: 'explicit' | 'env' | 'config' | 'default';
readonly configPath: string;
}
export interface IConfigListResult {
readonly configPath: string;
readonly values: IMatrixCliConfig;
readonly effective: {
readonly registry: string;
readonly registrySource: 'explicit' | 'env' | 'config' | 'default';
};
}
export interface IConfigSetResult {
readonly key: string;
readonly value: string;
readonly configPath: string;
}
export interface IConfigResetResult {
readonly configPath: string;
readonly values: IMatrixCliConfig;
}
export type ConfigClassificationKind =
| 'direct-actor-input'
| 'package-declaration'
| 'runtime-host-desired-state'
| 'credential-link-state'
| 'runtime-host-generated-state'
| 'runtime-host-preset'
| 'platform-deployment'
| 'deletion-candidate'
| 'unknown';
export interface IConfigExplainResult {
readonly path: string;
readonly relativePath: string;
readonly exists: boolean;
readonly kind: ConfigClassificationKind;
readonly owner: string;
readonly authoredBy: string;
readonly directRunReads: boolean;
readonly managedHostReads: boolean;
readonly generated: boolean;
readonly recommendedAction: string;
readonly notes: readonly string[];
}
function assertSupportedKey(key: string): 'registry' {
const normalized = key.trim();
if (normalized !== 'registry') {
throw new Error(`MX_CONFIG_INVALID: unsupported config key "${key}"`);
}
return normalized;
}
export async function configGetCommand(
key: string,
options: IConfigCommandOptions = {},
): Promise<IConfigGetResult> {
const normalizedKey = assertSupportedKey(key);
const stored = readMatrixCliConfig(options);
const effective = resolvePackageRegistry(undefined, options);
return {
key: normalizedKey,
value: stored.values.registry ?? effective.registry,
source: stored.values.registry ? 'config' : effective.source,
configPath: stored.configPath,
};
}
export async function configListCommand(options: IConfigCommandOptions = {}): Promise<IConfigListResult> {
const stored = readMatrixCliConfig(options);
const effective = resolvePackageRegistry(undefined, options);
return {
configPath: stored.configPath,
values: stored.values,
effective: {
registry: effective.registry,
registrySource: effective.source,
},
};
}
export async function configSetCommand(
key: string,
value: string,
options: IConfigCommandOptions = {},
): Promise<IConfigSetResult> {
const normalizedKey = assertSupportedKey(key);
const result = setMatrixCliConfigValue(normalizedKey, value, options);
return {
key: normalizedKey,
value: result.values.registry ?? '',
configPath: result.configPath,
};
}
export async function configResetCommand(options: IConfigCommandOptions = {}): Promise<IConfigResetResult> {
return resetMatrixCliConfig(options);
}
export async function configExplainCommand(
targetPath: string,
options: IConfigCommandOptions = {},
): Promise<IConfigExplainResult> {
const cwd = options.cwd ?? process.cwd();
const absolutePath = path.resolve(cwd, targetPath);
const relativePath = normalizePath(path.relative(cwd, absolutePath) || path.basename(absolutePath));
const exists = fs.existsSync(absolutePath);
return {
path: absolutePath,
relativePath,
exists,
...classifyConfigPath(relativePath),
};
}
function classifyConfigPath(relativePath: string): Omit<IConfigExplainResult, 'path' | 'relativePath' | 'exists'> {
const normalized = normalizePath(relativePath);
const basename = path.posix.basename(normalized);
const segments = normalized.split('/').filter(Boolean);
if (basename === 'ActorRunnerSmokeActor.mjs' || basename.endsWith('Actor.ts') || basename.endsWith('Actor.mjs')) {
return classification({
kind: 'direct-actor-input',
owner: 'Standalone Actor Runner',
authoredBy: 'CLI caller or package author',
directRunReads: true,
managedHostReads: false,
generated: false,
recommendedAction: 'keep as direct actor input; do not register Host-managed state',
notes: ['Direct actor execution receives this path from the CLI caller.'],
});
}
if (basename === 'matrix.json') {
return classification({
kind: 'package-declaration',
owner: 'Package',
authoredBy: 'package author or scaffold',
directRunReads: true,
managedHostReads: true,
generated: false,
recommendedAction: 'keep; package declarations are shared by direct and managed execution',
notes: [`${basename} is package-authored declaration state.`],
});
}
if (basename === 'package.json.matrix') {
return classification({
kind: 'package-declaration',
owner: 'Package',
authoredBy: 'package author or scaffold',
directRunReads: true,
managedHostReads: true,
generated: false,
recommendedAction: 'migrate to matrix.json when a package-local matrix.json exists',
notes: ['This is a duplicate package manifest candidate, not Runtime Host state.'],
});
}
if (basename === 'host.json' || basename === 'workspace.json' || basename === 'daemon.json') {
return classification({
kind: 'runtime-host-desired-state',
owner: 'Runtime Host',
authoredBy: 'developer, operator, or Host setup tool',
directRunReads: false,
managedHostReads: true,
generated: false,
recommendedAction: 'normalize behind the Runtime Host Plan before changing shape',
notes: ['Direct actor/package execution must not read this file.'],
});
}
if (basename.endsWith('.environment.json') && segments.includes('.matrix')) {
return classification({
kind: 'runtime-host-desired-state',
owner: 'Runtime Host',
authoredBy: 'developer, operator, or package environment author',
directRunReads: false,
managedHostReads: true,
generated: false,
recommendedAction: 'normalize into Runtime Host desired-state model',
notes: ['Package environment manifests belong to managed execution, not direct cloud-bus run.'],
});
}
if (segments.includes('credentials') || basename === 'matrix-api-key.json' || basename === 'hivecast-link.json') {
return classification({
kind: 'credential-link-state',
owner: 'Credentials layer',
authoredBy: 'CLI, cloud exchange, or operator',
directRunReads: true,
managedHostReads: true,
generated: false,
recommendedAction: 'keep secret; never commit; use only through configured credential resolvers',
notes: ['Direct-run may read stored API keys; Runtime Host may read Host/link credentials.'],
});
}
if (
basename === 'host.status.json'
|| basename === 'metadata.webapp'
|| segments.includes('runtimes')
|| segments.includes('runtime-env')
|| segments.includes('package-records')
) {
return classification({
kind: 'runtime-host-generated-state',
owner: 'Runtime Host reconciler',
authoredBy: 'generated by Host runtime',
directRunReads: false,
managedHostReads: true,
generated: true,
recommendedAction: 'keep out of authored config; safe to regenerate from managed Host state',
notes: ['Direct actor/package execution must not create this state.'],
});
}
if (
normalized.includes('/deployments/nodes/')
|| normalized.startsWith('deployments/nodes/')
|| normalized.includes('/dist/profiles/')
|| normalized.startsWith('dist/profiles/')
|| basename.endsWith('.profile.json')
|| basename.endsWith('.node.json')
) {
return classification({
kind: 'runtime-host-preset',
owner: 'Distribution profile resolver',
authoredBy: 'release engineer',
directRunReads: false,
managedHostReads: true,
generated: false,
recommendedAction: 'keep until profile/preset cleanup unifies resolver semantics',
notes: ['Profile and node presets are managed Host inputs, not direct-run inputs.'],
});
}
if (
normalized.includes('/deployments/environments/')
|| normalized.startsWith('deployments/environments/')
|| normalized.includes('/deployments/topologies/')
|| normalized.startsWith('deployments/topologies/')
|| normalized.endsWith('/deployments/schema.json')
|| normalized === 'deployments/schema.json'
|| normalized.includes('/packages/') && normalized.includes('-deployment/')
|| normalized.startsWith('packages/') && normalized.includes('-deployment/')
) {
return classification({
kind: 'deletion-candidate',
owner: 'Historical deployment layer',
authoredBy: 'historical deployment tooling',
directRunReads: false,
managedHostReads: false,
generated: false,
recommendedAction: 'delete only after grep/test proof shows no live reader',
notes: ['This path matches Phase 6 deletion candidates.'],
});
}
if (
basename === 'docker-compose.yml'
|| basename === 'Dockerfile'
|| normalized.includes('/systemd/')
|| normalized.includes('/native-dist/')
) {
return classification({
kind: 'platform-deployment',
owner: 'Platform operator',
authoredBy: 'operator or release engineer',
directRunReads: false,
managedHostReads: false,
generated: false,
recommendedAction: 'treat as deployment packaging, not package/runtime config',
notes: ['Platform deployment files sit outside direct and managed package execution.'],
});
}
return classification({
kind: 'unknown',
owner: 'unknown',
authoredBy: 'unknown',
directRunReads: false,
managedHostReads: false,
generated: false,
recommendedAction: 'inspect before changing; add a classifier case if this becomes a recurring config path',
notes: ['No current config simplification rule matched this path.'],
});
}
function classification(
result: Omit<IConfigExplainResult, 'path' | 'relativePath' | 'exists'>,
): Omit<IConfigExplainResult, 'path' | 'relativePath' | 'exists'> {
return result;
}
function normalizePath(value: string): string {
return value.replace(/\\/g, '/');
}