blob: 83bfa341c6f4a86d25ee55502b120dd67b11de44 [file]
import { stat } from 'node:fs/promises';
import {
deriveTaskSubmissionReadiness,
PROVIDER_DEFAULTS,
type LlmConnection,
type TaskSubmissionReadinessDimension,
type TaskSubmissionReadinessSnapshot,
} from '@maka/core';
import { providerAuthRequiresSecret } from '@maka/core';
import type { ConnectionCatalogEntry, ConnectionCatalogSnapshot } from '@maka/core';
import type { RuntimeHostConnection } from '@maka/runtime-host/client';
export interface RuntimeHostCliTaskReadinessInput {
readonly connection: RuntimeHostConnection;
readonly catalog: ConnectionCatalogSnapshot;
readonly cwd: string;
readonly connectionSlug?: string;
readonly model?: string;
}
export async function readRuntimeHostCliTaskReadiness(
input: RuntimeHostCliTaskReadinessInput,
now: () => number = Date.now,
): Promise<TaskSubmissionReadinessSnapshot> {
const checkedAt = now();
const entry = resolveCatalogEntry(input.catalog, input.connectionSlug);
const modelTarget = entry
? {
kind: 'resolved' as const,
connection: catalogEntryAsLlmConnection(entry, input.catalog),
hasSecret: await readHasSecret(input.connection, entry),
requestedModel: input.model,
checkedAt,
}
: input.connectionSlug
? { kind: 'connection_missing' as const, connectionSlug: input.connectionSlug, checkedAt }
: { kind: 'missing_default' as const, checkedAt };
return deriveTaskSubmissionReadiness({
checkedAt,
runtime: { state: 'ready', checkedAt },
modelTarget,
workspace: { state: await inspectWorkspace(input.cwd), checkedAt },
});
}
export function isRuntimeHostCliTaskBlocked(snapshot: TaskSubmissionReadinessSnapshot): boolean {
return snapshot.state === 'repair_required' || snapshot.state === 'unavailable';
}
export function formatRuntimeHostCliTaskBlockers(
snapshot: TaskSubmissionReadinessSnapshot,
): string {
return snapshot.blockers
.filter((blocker) => blocker.state !== 'unknown')
.map((blocker) => `${blocker.blockerCode ?? blocker.id}: ${repairHint(blocker)}`)
.join('\n');
}
function resolveCatalogEntry(
catalog: ConnectionCatalogSnapshot,
requestedSlug: string | undefined,
): ConnectionCatalogEntry | undefined {
if (requestedSlug) {
return catalog.connections.find((candidate) => candidate.slug === requestedSlug);
}
return catalog.connections.find(
(candidate) => candidate.connectionId === catalog.defaultTarget?.connectionId,
);
}
function catalogEntryAsLlmConnection(
entry: ConnectionCatalogEntry,
catalog: ConnectionCatalogSnapshot,
): LlmConnection {
const defaultModel =
entry.connectionId === catalog.defaultTarget?.connectionId
? catalog.defaultTarget.modelId
: (entry.enabledModelIds[0] ?? '');
return {
slug: entry.slug,
name: entry.name,
providerType: entry.providerType,
...(entry.baseUrl ? { baseUrl: entry.baseUrl } : {}),
enabled: entry.enabled,
defaultModel,
enabledModelIds: [...entry.enabledModelIds],
models: [...entry.models],
...(entry.modelSource ? { modelSource: entry.modelSource } : {}),
...(entry.modelsFetchedAt ? { modelsFetchedAt: entry.modelsFetchedAt } : {}),
...(entry.lastTest
? { lastTestStatus: entry.lastTest.status, lastTestAt: entry.lastTest.checkedAt }
: {}),
createdAt: 0,
updatedAt: 0,
};
}
async function readHasSecret(
connection: RuntimeHostConnection,
entry: ConnectionCatalogEntry,
): Promise<boolean | undefined> {
if (!providerAuthRequiresSecret(entry.providerType)) return false;
const authKind = PROVIDER_DEFAULTS[entry.providerType].authKind;
const kind = authKind === 'oauth_token' ? 'oauth_token' : 'api_key';
try {
const result = await connection.request('credential.vault.query', {
locator: { scope: 'connection', connectionId: entry.connectionId, kind },
});
return result.kind === 'status' ? result.status.configured : undefined;
} catch {
return undefined;
}
}
async function inspectWorkspace(
cwd: string,
): Promise<'ready' | 'missing' | 'unavailable' | 'unknown'> {
try {
return (await stat(cwd)).isDirectory() ? 'ready' : 'unavailable';
} catch (error) {
const code = (error as NodeJS.ErrnoException | undefined)?.code;
if (code === 'ENOENT' || code === 'ENOTDIR') return 'missing';
if (code === 'EACCES') return 'unavailable';
return 'unknown';
}
}
function repairHint(blocker: TaskSubmissionReadinessDimension): string {
const target = blocker.repairTarget;
if (!target) return 'retry the command';
switch (target.kind) {
case 'provider_catalog':
return 'run `maka` to configure a model provider';
case 'connection':
return `repair connection "${target.connectionSlug}" in \`maka\``;
case 'models':
return 'choose an available connection with `--connection`';
case 'runtime_restart':
return 'restart Maka and retry';
case 'workspace_picker':
return 'choose an existing directory with `--cwd`';
case 'system_permission':
return `grant system permission "${target.permissionId}"`;
case 'capability_settings':
return `enable capability "${target.capabilityId}"`;
}
}