08919752e3
Checkpoint multi-chantiers (arbre vert : tsc 0 erreur, 70 tests, build OK). - tâche 1.9 Phase 1 : schéma socle (machine_state/events/reports/raw_artifacts/ hardware/metrics + colonnes étendues) + wiring refresh/execute. Migration 0002. - tâche 1.9 Phase 2 : machine_credentials + machine_host_keys (non destructif, dual-read + backfill). Migration 0003. Fix séquence journal de migration. - tâche 2 : SJ-0 (types étendus rétro-compatibles, réducteur Docker, resolveTemplate), SJ-1 (update-analyze enrichi), SJ-2 (apply + diff dpkg + timeout inactivité SSH), SJ-3 (reboot vérifié boot_id). - WIP parallèle inclus : /api/capabilities, auth/apiTokens/apiClients, system metrics, scaffold app_rust, ajustements frontend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
// server/cli/createApiClient.ts
|
|
import { pathToFileURL } from "node:url";
|
|
import type { ApiClientScope } from "@shared/types.js";
|
|
import { runMigrations } from "../db/migrate.js";
|
|
import { createApiClient } from "../services/apiClients.js";
|
|
|
|
export interface CreateApiClientCliOptions {
|
|
name: string;
|
|
scopes: ApiClientScope[];
|
|
}
|
|
|
|
const ALLOWED_SCOPES: ApiClientScope[] = ["read", "operate", "admin", "debug"];
|
|
|
|
export function parseCreateApiClientArgs(args: string[]): CreateApiClientCliOptions {
|
|
let name = "";
|
|
let scopes: ApiClientScope[] = ["read"];
|
|
|
|
for (let i = 0; i < args.length; i += 1) {
|
|
const arg = args[i];
|
|
if (arg === "--name") {
|
|
i += 1;
|
|
name = args[i] ?? "";
|
|
} else if (arg === "--scopes") {
|
|
i += 1;
|
|
scopes = parseScopes(args[i] ?? "");
|
|
} else if (arg === "--help" || arg === "-h") {
|
|
throw new Error(helpText());
|
|
} else {
|
|
throw new Error(`Argument inconnu: ${arg}\n\n${helpText()}`);
|
|
}
|
|
}
|
|
|
|
if (!name.trim()) throw new Error(`--name est obligatoire\n\n${helpText()}`);
|
|
return { name: name.trim(), scopes };
|
|
}
|
|
|
|
function parseScopes(raw: string): ApiClientScope[] {
|
|
const scopes = raw
|
|
.split(",")
|
|
.map((scope) => scope.trim())
|
|
.filter(Boolean) as ApiClientScope[];
|
|
|
|
if (scopes.length === 0) return ["read"];
|
|
for (const scope of scopes) {
|
|
if (!ALLOWED_SCOPES.includes(scope)) {
|
|
throw new Error(`Scope invalide: ${scope}. Scopes valides: ${ALLOWED_SCOPES.join(", ")}`);
|
|
}
|
|
}
|
|
return [...new Set(scopes)];
|
|
}
|
|
|
|
function helpText(): string {
|
|
return [
|
|
"Usage:",
|
|
" pnpm api-client:create -- --name \"App Rust\" --scopes read,operate",
|
|
"",
|
|
"Variables requises:",
|
|
" SU_MASTER_KEY clé hex 64 caractères",
|
|
" SU_DB_PATH chemin SQLite optionnel",
|
|
].join("\n");
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const options = parseCreateApiClientArgs(process.argv.slice(2));
|
|
runMigrations();
|
|
const created = createApiClient(options);
|
|
console.log(JSON.stringify(created, null, 2));
|
|
}
|
|
|
|
const entrypoint = process.argv[1] ? pathToFileURL(process.argv[1]).href : "";
|
|
if (import.meta.url === entrypoint) {
|
|
main().catch((err) => {
|
|
console.error((err as Error).message);
|
|
process.exitCode = 1;
|
|
});
|
|
}
|
|
|
|
export const createApiClientCliInternals = { parseScopes, helpText };
|