sverka 0.2.22 → 0.2.24
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/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +266 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/main.ts","../src/output.ts"],"mappings":";;iBAGiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;;iBAIe;EACf,MAAM;EACN,UAAU;EACV,MAAM;EACN,UAAU;;EAEV,MAAM;;;qBAIK;;;;;;YAOD,mBAAmB,uBAAuB;;YAG1C;;qBAWC,iBAAiB;WACnB,MAAM;WACN,UAAU;WACD;EAEhB,YAAA,iBACA,MAAM,cACN,UAAU,UACV;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/main.ts","../src/output.ts"],"mappings":";;iBAGiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;;iBAIe;EACf,MAAM;EACN,UAAU;EACV,MAAM;EACN,UAAU;;EAEV,MAAM;;;qBAIK;;;;;;YAOD,mBAAmB,uBAAuB;;YAG1C;;qBAWC,iBAAiB;WACnB,MAAM;WACN,UAAU;WACD;EAEhB,YAAA,iBACA,MAAM,cACN,UAAU,UACV;;;;;UC9Ba;EACf,SAAS;;;;;;;wBA6cW,KAAK,gBAAgB,OAAO,WAAW;;;;KCrejD,aAAa;;;;;;qBAOZ,+BAA+B;;EAM9B,YAAA,KAAK,WAAW,KAAK,WAAW,iBAAe;EAO3D,MAAM;EAKN,UAAU;EAKV,MAAM;EAIN,UAAU;;EAKV,MAAM;;;;;;wBAwDQ,mBACd,QAAQ,aACR,KAAK,WACL,KAAK,YACJ;;;;;wBAYa,iBACd,QAAQ,aACR,MAAM,eACL"}
|
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { mkdir, open, readFile, realpath, writeFile } from "node:fs/promises";
|
|
|
10
10
|
import { Entry, Pipeline, Project, ShellStep, collectConstructWarnings, manual, synthesize, validateGraph } from "@sverka/workflow";
|
|
11
11
|
import { bindRunPlan, createPlanner } from "@sverka/sdk";
|
|
12
12
|
import { DEFAULT_POLICY, createBuiltinResolver, evaluatePolicy, loadBaseline, normalizeSarif, serializeSarif, synthesizeCheckSteps } from "@sverka/verification";
|
|
13
|
-
import { createDockerDriver, createEngine, createHostDriver } from "@sverka/runtime";
|
|
13
|
+
import { AGENT_ENV, collectAgentWrites, createDockerDriver, createEngine, createHostDriver, noAgentDriverError, resolveAgentDrivers, resolveAgentEngine } from "@sverka/runtime";
|
|
14
14
|
import { spawnSync } from "node:child_process";
|
|
15
15
|
import { watch } from "chokidar";
|
|
16
16
|
import { compileGithub, compileGitlab } from "@sverka/compiler";
|
|
@@ -190,17 +190,31 @@ async function registerConfigHooks() {
|
|
|
190
190
|
/**
|
|
191
191
|
* Find a sverka.config.ts or sverka.config.js file in the given root.
|
|
192
192
|
* Returns the absolute path or null if not found.
|
|
193
|
+
* Spec 37/54: when no config exists, fall back to `.sverka/*.md` files
|
|
194
|
+
* (markdown authoring) — sorted, first file wins.
|
|
193
195
|
*/
|
|
194
196
|
async function findConfig(root) {
|
|
195
197
|
for (const candidate of [
|
|
196
198
|
"sverka.config.ts",
|
|
199
|
+
"sverka.config.mts",
|
|
197
200
|
"sverka.config.js",
|
|
198
201
|
"sverka.config.mjs"
|
|
199
202
|
]) {
|
|
200
203
|
const path = join(root, candidate);
|
|
201
204
|
if (existsSync(path)) return path;
|
|
202
205
|
}
|
|
203
|
-
return
|
|
206
|
+
return findMarkdownConfig(root);
|
|
207
|
+
}
|
|
208
|
+
/** First `.sverka/*.md` file under root (sorted), or null. */
|
|
209
|
+
function findMarkdownConfig(root) {
|
|
210
|
+
const dir = join(root, ".sverka");
|
|
211
|
+
let names;
|
|
212
|
+
try {
|
|
213
|
+
names = readdirSync(dir).filter((n) => n.endsWith(".md")).sort();
|
|
214
|
+
} catch {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
return names.length > 0 ? join(dir, names[0]) : null;
|
|
204
218
|
}
|
|
205
219
|
/**
|
|
206
220
|
* Resolve the effective config path from global flags.
|
|
@@ -236,6 +250,12 @@ function assertConstructLike(value) {
|
|
|
236
250
|
async function loadConfig(configPath) {
|
|
237
251
|
const absPath = isAbsolute(configPath) ? configPath : resolve(process.cwd(), configPath);
|
|
238
252
|
if (!existsSync(absPath)) throw new CliError(`config not found: ${absPath}`, "MISSING_ARG", ExitCode.UsageError);
|
|
253
|
+
if (absPath.endsWith(".md")) try {
|
|
254
|
+
const { loadMarkdownFile } = await import("@sverka/sdk");
|
|
255
|
+
return await loadMarkdownFile(absPath);
|
|
256
|
+
} catch (e) {
|
|
257
|
+
throw new CliError(`failed to load config: ${e instanceof Error ? e.message : String(e)}`, "SDK_ERROR", ExitCode.RuntimeError, e);
|
|
258
|
+
}
|
|
239
259
|
let mod;
|
|
240
260
|
try {
|
|
241
261
|
await registerConfigHooks();
|
|
@@ -975,6 +995,7 @@ async function runCommand(args, global, output, start) {
|
|
|
975
995
|
const artifactDir = join(global.root, ".sverka", "artifacts");
|
|
976
996
|
const { events, runStatus, renderer } = await consumeEvents(createEngine({
|
|
977
997
|
drivers: buildDrivers(executor),
|
|
998
|
+
agentDrivers: resolveAgentDrivers(process$1.env),
|
|
978
999
|
maxConcurrent: args.jobs ?? 4
|
|
979
1000
|
}), plan, {
|
|
980
1001
|
workspace: global.root,
|
|
@@ -2265,6 +2286,218 @@ async function uiCommand(args, global, output, _start) {
|
|
|
2265
2286
|
}
|
|
2266
2287
|
}
|
|
2267
2288
|
//#endregion
|
|
2289
|
+
//#region src/commands/agent.ts
|
|
2290
|
+
const RESULT_FILE = "agent-result.json";
|
|
2291
|
+
const WRITES_FILE$1 = "sverka-writes.json";
|
|
2292
|
+
async function agentCommand(args, global, output, _start) {
|
|
2293
|
+
const env = process$1.env;
|
|
2294
|
+
const engine = resolveAgentEngine(env);
|
|
2295
|
+
const prompt = env[AGENT_ENV.prompt];
|
|
2296
|
+
if (prompt === void 0 || prompt === "") throw new CliError(`sverka agent: ${AGENT_ENV.prompt} is not set — the compiled agent job provides it`, "MISSING_ARG", ExitCode.UsageError);
|
|
2297
|
+
const outDir = args.outputDir ?? global.root;
|
|
2298
|
+
const mentions = parseMentionList(env[AGENT_ENV.mention]);
|
|
2299
|
+
const commentBody = env["COMMENT_BODY"];
|
|
2300
|
+
if (mentions.length > 0 && commentBody !== void 0 && !mentions.some((m) => commentBody.includes(m))) {
|
|
2301
|
+
output.writeLine(`sverka agent: comment does not contain a configured mention (${mentions.join(", ")}) — skipping`);
|
|
2302
|
+
await writeArtifacts(outDir, {
|
|
2303
|
+
text: "",
|
|
2304
|
+
finishReason: "stop"
|
|
2305
|
+
});
|
|
2306
|
+
return ExitCode.Success;
|
|
2307
|
+
}
|
|
2308
|
+
const driver = resolveAgentDrivers(env).find((d) => d.canExecute(engine));
|
|
2309
|
+
if (driver === void 0) throw noAgentDriverError(engine, `sverka agent, engine '${engine}'`);
|
|
2310
|
+
const model = env[AGENT_ENV.model];
|
|
2311
|
+
const maxTokens = env[AGENT_ENV.maxTokens];
|
|
2312
|
+
const request = {
|
|
2313
|
+
engine,
|
|
2314
|
+
prompt,
|
|
2315
|
+
...model !== void 0 ? { model } : {},
|
|
2316
|
+
...maxTokens !== void 0 ? { maxTokens: parseMaxTokens(maxTokens) } : {}
|
|
2317
|
+
};
|
|
2318
|
+
output.debug(`sverka agent: engine=${engine} driver=${driver.name}`);
|
|
2319
|
+
const result = await driver.executeAgent(request);
|
|
2320
|
+
if (result.finishReason === "error") {
|
|
2321
|
+
output.errorLine("sverka agent: agent finished with reason 'error'");
|
|
2322
|
+
return ExitCode.RuntimeError;
|
|
2323
|
+
}
|
|
2324
|
+
await writeArtifacts(outDir, result, collectAgentWrites(result));
|
|
2325
|
+
if (result.text !== "") output.writeLine(result.text);
|
|
2326
|
+
return ExitCode.Success;
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* SVERKA_MENTION carries one mention string, or a JSON array when several
|
|
2330
|
+
* comment entries with different mentions reach the same job.
|
|
2331
|
+
*/
|
|
2332
|
+
function parseMentionList(raw) {
|
|
2333
|
+
if (raw === void 0) return [];
|
|
2334
|
+
try {
|
|
2335
|
+
const parsed = JSON.parse(raw);
|
|
2336
|
+
if (Array.isArray(parsed) && parsed.length > 0 && parsed.every((m) => typeof m === "string")) return parsed;
|
|
2337
|
+
} catch {}
|
|
2338
|
+
return [raw];
|
|
2339
|
+
}
|
|
2340
|
+
function parseMaxTokens(raw) {
|
|
2341
|
+
const n = Number(raw);
|
|
2342
|
+
if (!Number.isInteger(n) || n <= 0) throw new CliError(`sverka agent: ${AGENT_ENV.maxTokens} must be a positive integer, got '${raw}'`, "INVALID_FLAG", ExitCode.UsageError);
|
|
2343
|
+
return n;
|
|
2344
|
+
}
|
|
2345
|
+
async function writeArtifacts(dir, result, writes = []) {
|
|
2346
|
+
await writeFile(join(dir, RESULT_FILE), JSON.stringify(result, null, 2), "utf-8");
|
|
2347
|
+
await writeFile(join(dir, WRITES_FILE$1), JSON.stringify({ writes }, null, 2), "utf-8");
|
|
2348
|
+
}
|
|
2349
|
+
//#endregion
|
|
2350
|
+
//#region src/commands/apply.ts
|
|
2351
|
+
const WRITES_FILE = "sverka-writes.json";
|
|
2352
|
+
const DECLARATIONS_ENV = "SVERKA_WRITE_DECLARATIONS";
|
|
2353
|
+
/** Write kinds `sverka apply` can execute. Everything else fails loudly. */
|
|
2354
|
+
const SUPPORTED_WRITE_KINDS = /* @__PURE__ */ new Set(["comment"]);
|
|
2355
|
+
async function applyCommand(args, global, output, _start) {
|
|
2356
|
+
const provider = args.provider ?? detectProvider();
|
|
2357
|
+
const writes = await loadWrites(args.file ?? join(global.root, WRITES_FILE));
|
|
2358
|
+
const declarations = loadDeclarations();
|
|
2359
|
+
const applied = [];
|
|
2360
|
+
for (const write of writes) {
|
|
2361
|
+
if (!declarations.some((d) => d.kind === write.kind)) throw new CliError(`sverka apply: write kind '${write.kind}' is not declared in permissions.write (declarations: ${declarations.map((d) => d.kind).join(", ") || "none"})`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2362
|
+
if (!SUPPORTED_WRITE_KINDS.has(write.kind)) throw new CliError(`sverka apply: write kind '${write.kind}' is declared but not supported by sverka apply (supported: ${[...SUPPORTED_WRITE_KINDS].join(", ")})`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2363
|
+
applied.push(await applyWrite(provider, write, declarations));
|
|
2364
|
+
}
|
|
2365
|
+
const summary = applied.length > 0 ? ` — ${applied.join("; ")}` : "";
|
|
2366
|
+
output.writeLine(`sverka apply: applied ${applied.length} write(s) via ${provider}${summary}`);
|
|
2367
|
+
return ExitCode.Success;
|
|
2368
|
+
}
|
|
2369
|
+
function detectProvider() {
|
|
2370
|
+
if (process$1.env["GITLAB_CI"] !== void 0 || process$1.env["CI_API_V4_URL"]) return "gitlab";
|
|
2371
|
+
if (process$1.env["GITHUB_ACTIONS"] !== void 0) return "github";
|
|
2372
|
+
throw new CliError("sverka apply: --provider is required when CI provider cannot be detected (gitlab|github)", "MISSING_ARG", ExitCode.UsageError);
|
|
2373
|
+
}
|
|
2374
|
+
async function loadWrites(file) {
|
|
2375
|
+
let raw;
|
|
2376
|
+
try {
|
|
2377
|
+
raw = await readFile(file, "utf-8");
|
|
2378
|
+
} catch {
|
|
2379
|
+
throw new CliError(`sverka apply: writes artifact not found: ${file} — the agent job must produce ${WRITES_FILE}`, "MISSING_ARG", ExitCode.RuntimeError);
|
|
2380
|
+
}
|
|
2381
|
+
let parsed;
|
|
2382
|
+
try {
|
|
2383
|
+
parsed = JSON.parse(raw);
|
|
2384
|
+
} catch (e) {
|
|
2385
|
+
throw new CliError(`sverka apply: ${WRITES_FILE} is not valid JSON: ${e instanceof Error ? e.message : String(e)}`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2386
|
+
}
|
|
2387
|
+
const items = extractWritesItems(parsed);
|
|
2388
|
+
if (items === void 0) throw new CliError(`sverka apply: ${WRITES_FILE} must be an object with a 'writes' array`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2389
|
+
return items.map((item, i) => {
|
|
2390
|
+
if (typeof item !== "object" || item === null || typeof item.kind !== "string") throw new CliError(`sverka apply: writes[${i}] must be an object with a string 'kind'`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2391
|
+
return item;
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
/** The artifact is either a bare array or an object with a 'writes' array. */
|
|
2395
|
+
function extractWritesItems(parsed) {
|
|
2396
|
+
if (Array.isArray(parsed)) return parsed;
|
|
2397
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
2398
|
+
const w = parsed.writes;
|
|
2399
|
+
return Array.isArray(w) ? w : void 0;
|
|
2400
|
+
}
|
|
2401
|
+
function loadDeclarations() {
|
|
2402
|
+
const raw = process$1.env[DECLARATIONS_ENV];
|
|
2403
|
+
if (raw === void 0 || raw === "") throw new CliError(`sverka apply: ${DECLARATIONS_ENV} is not set — the __apply job declares it from permissions.write`, "MISSING_ARG", ExitCode.RuntimeError);
|
|
2404
|
+
let parsed;
|
|
2405
|
+
try {
|
|
2406
|
+
parsed = JSON.parse(raw);
|
|
2407
|
+
} catch (e) {
|
|
2408
|
+
throw new CliError(`sverka apply: ${DECLARATIONS_ENV} is not valid JSON: ${e instanceof Error ? e.message : String(e)}`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2409
|
+
}
|
|
2410
|
+
if (!Array.isArray(parsed)) throw new CliError(`sverka apply: ${DECLARATIONS_ENV} must be a JSON array of write declarations`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2411
|
+
return parsed;
|
|
2412
|
+
}
|
|
2413
|
+
async function applyWrite(provider, write, declarations) {
|
|
2414
|
+
if (write.kind === "comment") return provider === "gitlab" ? applyGitlabComment(write, declarations) : applyGithubComment(write);
|
|
2415
|
+
throw new CliError(`sverka apply: write kind '${write.kind}' is not supported`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2416
|
+
}
|
|
2417
|
+
function requireBody(write) {
|
|
2418
|
+
if (typeof write.body !== "string" || write.body === "") throw new CliError("sverka apply: comment write requires a non-empty 'body' string", "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2419
|
+
return write.body;
|
|
2420
|
+
}
|
|
2421
|
+
/**
|
|
2422
|
+
* The object kind a comment write targets. `write.on` wins when present
|
|
2423
|
+
* (and must be a known kind — anything else fails loudly rather than
|
|
2424
|
+
* silently falling back); otherwise the triggering context decides: a
|
|
2425
|
+
* forwarded MR_IID means the comment fired on a merge request.
|
|
2426
|
+
*/
|
|
2427
|
+
function resolveCommentTarget(write) {
|
|
2428
|
+
if (write.on === "issue" || write.on === "merge_request") return write.on;
|
|
2429
|
+
if (write.on !== void 0) throw new CliError(`sverka apply: comment write 'on' must be issue|merge_request, got '${JSON.stringify(write.on)}'`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2430
|
+
return process$1.env["MR_IID"] !== void 0 ? "merge_request" : "issue";
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* Resolve the target iid for a comment write. When the triggering context
|
|
2434
|
+
* supplies an iid (the webhook forwarded MR_IID/ISSUE_IID), it wins — a
|
|
2435
|
+
* write.iid that disagrees is model output trying to redirect the comment
|
|
2436
|
+
* to another target, which fails loudly.
|
|
2437
|
+
*/
|
|
2438
|
+
function requireIid(write, envKeys) {
|
|
2439
|
+
const contextRaw = envKeys.map((k) => process$1.env[k]).find((v) => v !== void 0 && v !== "");
|
|
2440
|
+
if (contextRaw !== void 0) {
|
|
2441
|
+
const ctx = Number(contextRaw);
|
|
2442
|
+
if (!Number.isInteger(ctx) || ctx <= 0) throw new CliError(`sverka apply: context iid '${contextRaw}' is not a positive integer`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2443
|
+
if (write.iid !== void 0 && Number(write.iid) !== ctx) throw new CliError(`sverka apply: write iid '${JSON.stringify(write.iid)}' does not match the triggering context iid ${ctx}`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2444
|
+
return ctx;
|
|
2445
|
+
}
|
|
2446
|
+
if (write.iid === void 0) throw new CliError(`sverka apply: comment write needs a target iid (${envKeys.join(" or ")} env var, or an 'iid' field)`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2447
|
+
const n = Number(write.iid);
|
|
2448
|
+
if (!Number.isInteger(n) || n <= 0) throw new CliError(`sverka apply: comment write iid must be a positive integer, got '${JSON.stringify(write.iid)}'`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2449
|
+
return n;
|
|
2450
|
+
}
|
|
2451
|
+
async function applyGitlabComment(write, declarations) {
|
|
2452
|
+
const { api, project, token } = gitlabApplyEnv();
|
|
2453
|
+
const body = requireBody(write);
|
|
2454
|
+
const on = resolveCommentTarget(write);
|
|
2455
|
+
if (!declarations.some((d) => d.kind === "comment" && d.target === on)) throw new CliError(`sverka apply: comment write targets '${on}' but no declaration declares it (declared comment targets: ${declarations.filter((d) => d.kind === "comment").map((d) => d.target).join(", ") || "none"})`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2456
|
+
const iid = requireIid(write, on === "merge_request" ? ["MR_IID"] : ["ISSUE_IID", "SVERKA_ISSUE_IID"]);
|
|
2457
|
+
await postJson(`${api}${on === "merge_request" ? `/projects/${encodeURIComponent(project)}/merge_requests/${iid}/notes` : `/projects/${encodeURIComponent(project)}/issues/${iid}/notes`}`, token, { body }, "PRIVATE-TOKEN");
|
|
2458
|
+
return `comment on ${on} !${iid}`;
|
|
2459
|
+
}
|
|
2460
|
+
/** Provider-supplied env for a GitLab apply call — all CI-controlled. */
|
|
2461
|
+
function gitlabApplyEnv() {
|
|
2462
|
+
const env = process$1.env;
|
|
2463
|
+
const api = env["CI_API_V4_URL"] ?? "https://gitlab.com/api/v4";
|
|
2464
|
+
const project = env["CI_PROJECT_ID"];
|
|
2465
|
+
if (project === void 0 || project === "") throw new CliError("sverka apply: CI_PROJECT_ID is not set — run inside a GitLab job or set it explicitly", "MISSING_ARG", ExitCode.RuntimeError);
|
|
2466
|
+
const token = env["SVERKA_APPLY_TOKEN"] || env["GITLAB_TOKEN"];
|
|
2467
|
+
if (token === void 0 || token === "") throw new CliError("sverka apply: SVERKA_APPLY_TOKEN is not set — scope it to the 'sverka-apply' environment (see engdocs/user/gitlab/agentic.md)", "MISSING_ARG", ExitCode.RuntimeError);
|
|
2468
|
+
return {
|
|
2469
|
+
api,
|
|
2470
|
+
project,
|
|
2471
|
+
token
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
async function applyGithubComment(write) {
|
|
2475
|
+
const env = process$1.env;
|
|
2476
|
+
const api = env["GITHUB_API_URL"] ?? "https://api.github.com";
|
|
2477
|
+
const repo = env["GITHUB_REPOSITORY"];
|
|
2478
|
+
if (repo === void 0 || repo === "") throw new CliError("sverka apply: GITHUB_REPOSITORY is not set — run inside a GitHub Actions job or set it explicitly", "MISSING_ARG", ExitCode.RuntimeError);
|
|
2479
|
+
const token = env["SVERKA_APPLY_TOKEN"] || env["GH_TOKEN"] || env["GITHUB_TOKEN"];
|
|
2480
|
+
if (token === void 0 || token === "") throw new CliError("sverka apply: GH_TOKEN/GITHUB_TOKEN is not set", "MISSING_ARG", ExitCode.RuntimeError);
|
|
2481
|
+
const body = requireBody(write);
|
|
2482
|
+
const issue = requireIid(write, ["SVERKA_ISSUE_IID", "ISSUE_IID"]);
|
|
2483
|
+
await postJson(`${api}/repos/${repo}/issues/${issue}/comments`, token, { body }, "Bearer");
|
|
2484
|
+
return `comment on issue #${issue}`;
|
|
2485
|
+
}
|
|
2486
|
+
async function postJson(url, token, body, auth) {
|
|
2487
|
+
const headers = { "content-type": "application/json" };
|
|
2488
|
+
if (auth === "PRIVATE-TOKEN") headers["PRIVATE-TOKEN"] = token;
|
|
2489
|
+
else headers["authorization"] = `Bearer ${token}`;
|
|
2490
|
+
const res = await fetch(url, {
|
|
2491
|
+
method: "POST",
|
|
2492
|
+
headers,
|
|
2493
|
+
body: JSON.stringify(body)
|
|
2494
|
+
});
|
|
2495
|
+
if (!res.ok) {
|
|
2496
|
+
const text = await res.text().catch(() => "");
|
|
2497
|
+
throw new CliError(`sverka apply: provider request failed (${res.status} ${res.url}): ${text.slice(0, 300)}`, "PACKAGE_ERROR", ExitCode.RuntimeError);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
//#endregion
|
|
2268
2501
|
//#region src/main.ts
|
|
2269
2502
|
/** Build GlobalFlags from parsed yargs options. */
|
|
2270
2503
|
function buildGlobalFlags(parsed) {
|
|
@@ -2347,13 +2580,9 @@ function addPolicyCommand(y) {
|
|
|
2347
2580
|
demandOption: true
|
|
2348
2581
|
}).option("baseline", { type: "string" });
|
|
2349
2582
|
}
|
|
2350
|
-
/** Configure the synth subcommand options. */
|
|
2583
|
+
/** Configure the synth subcommand options. Alias for compile — same flags. */
|
|
2351
2584
|
function addSynthCommand(y) {
|
|
2352
|
-
return y
|
|
2353
|
-
type: "string",
|
|
2354
|
-
demandOption: true,
|
|
2355
|
-
choices: ["github", "gitlab"]
|
|
2356
|
-
});
|
|
2585
|
+
return addCompileCommand(y);
|
|
2357
2586
|
}
|
|
2358
2587
|
/** Configure the compile subcommand options. */
|
|
2359
2588
|
function addCompileCommand(y) {
|
|
@@ -2433,7 +2662,17 @@ function buildParser() {
|
|
|
2433
2662
|
type: "boolean",
|
|
2434
2663
|
alias: "v",
|
|
2435
2664
|
default: false
|
|
2436
|
-
}).command("init", "Create a sverka.config.ts", addInitCommand).command("validate", "Validate a sverka config").command("plan", "Bind Entry + inputs → Run Plan", addPlanCommand).command("graph", "Display the Definition Graph").command("run", "Execute the workflow locally", addRunCommand).command("discover", "Discover and display project context").command("check", "Resolve proposed checks → StepDefinitions").command("policy", "Evaluate findings against policy", addPolicyCommand).command("synth", "Alias for compile — compile to a target CI YAML", addSynthCommand).command("compile", "Compile to a target CI YAML", addCompileCommand).command("mcp-server", "Expose Sverka as an MCP server (stdio)").command("doctor", "Diagnose environment and dependencies").command("view [file]", "Open the latest run report, or view SARIF findings (TUI/HTML)", addViewCommand).command("ui", "Start local web dashboard server", addUiCommand).
|
|
2665
|
+
}).command("init", "Create a sverka.config.ts", addInitCommand).command("validate", "Validate a sverka config").command("plan", "Bind Entry + inputs → Run Plan", addPlanCommand).command("graph", "Display the Definition Graph").command("run", "Execute the workflow locally", addRunCommand).command("discover", "Discover and display project context").command("check", "Resolve proposed checks → StepDefinitions").command("policy", "Evaluate findings against policy", addPolicyCommand).command("synth", "Alias for compile — compile to a target CI YAML", addSynthCommand).command("compile", "Compile to a target CI YAML", addCompileCommand).command("mcp-server", "Expose Sverka as an MCP server (stdio)").command("doctor", "Diagnose environment and dependencies").command("view [file]", "Open the latest run report, or view SARIF findings (TUI/HTML)", addViewCommand).command("ui", "Start local web dashboard server", addUiCommand).command("agent", "Run a single agent step from SVERKA_AGENT_* env (CI agent jobs)", (y) => y.option("output-dir", {
|
|
2666
|
+
type: "string",
|
|
2667
|
+
describe: "Directory for agent-result.json + sverka-writes.json (default: root)"
|
|
2668
|
+
})).command("apply", "Validate + apply agent writes artifact (CI __apply jobs)", (y) => y.option("provider", {
|
|
2669
|
+
type: "string",
|
|
2670
|
+
choices: ["gitlab", "github"],
|
|
2671
|
+
describe: "CI provider (auto-detected from CI env when omitted)"
|
|
2672
|
+
}).option("file", {
|
|
2673
|
+
type: "string",
|
|
2674
|
+
describe: "Path to sverka-writes.json (default: <root>/sverka-writes.json)"
|
|
2675
|
+
})).demandCommand(1, "No command given").strict().fail((msg, err) => {
|
|
2437
2676
|
if (err) throw err;
|
|
2438
2677
|
throw new CliError(msg, "UNKNOWN_COMMAND", ExitCode.UsageError);
|
|
2439
2678
|
});
|
|
@@ -2455,6 +2694,8 @@ async function dispatch(command, parsed, global, output, start) {
|
|
|
2455
2694
|
case "doctor": return doctorCommand(global, output, start);
|
|
2456
2695
|
case "view": return dispatchView(parsed, global, output, start);
|
|
2457
2696
|
case "ui": return dispatchUi(parsed, global, output, start);
|
|
2697
|
+
case "agent": return dispatchAgent(parsed, global, output, start);
|
|
2698
|
+
case "apply": return dispatchApply(parsed, global, output, start);
|
|
2458
2699
|
default: throw new CliError(`unknown command: ${command}`, "UNKNOWN_COMMAND", ExitCode.UsageError);
|
|
2459
2700
|
}
|
|
2460
2701
|
}
|
|
@@ -2499,7 +2740,11 @@ function dispatchPolicy(parsed, global, output, start) {
|
|
|
2499
2740
|
return policyCommand(args, global, output, start);
|
|
2500
2741
|
}
|
|
2501
2742
|
function dispatchSynth(parsed, global, output, start) {
|
|
2502
|
-
|
|
2743
|
+
const args = { target: parsed.target === "gitlab" ? "gitlab" : "github" };
|
|
2744
|
+
if (typeof parsed.output === "string") args.output = parsed.output;
|
|
2745
|
+
if (typeof parsed.outputDir === "string") args.outputDir = parsed.outputDir;
|
|
2746
|
+
if (parsed.pin === true) args.pin = true;
|
|
2747
|
+
return synthCommand(args, global, output, start);
|
|
2503
2748
|
}
|
|
2504
2749
|
function dispatchCompile(parsed, global, output, start) {
|
|
2505
2750
|
const args = { target: parsed.target === "gitlab" ? "gitlab" : "github" };
|
|
@@ -2520,6 +2765,17 @@ function dispatchUi(parsed, global, output, start) {
|
|
|
2520
2765
|
if (typeof parsed.host === "string") args.host = parsed.host;
|
|
2521
2766
|
return uiCommand(args, global, output, start);
|
|
2522
2767
|
}
|
|
2768
|
+
function dispatchAgent(parsed, global, output, start) {
|
|
2769
|
+
const args = {};
|
|
2770
|
+
if (typeof parsed.outputDir === "string") args.outputDir = parsed.outputDir;
|
|
2771
|
+
return agentCommand(args, global, output, start);
|
|
2772
|
+
}
|
|
2773
|
+
function dispatchApply(parsed, global, output, start) {
|
|
2774
|
+
const args = {};
|
|
2775
|
+
if (parsed.provider === "gitlab" || parsed.provider === "github") args.provider = parsed.provider;
|
|
2776
|
+
if (typeof parsed.file === "string") args.file = parsed.file;
|
|
2777
|
+
return applyCommand(args, global, output, start);
|
|
2778
|
+
}
|
|
2523
2779
|
/**
|
|
2524
2780
|
* The main CLI entry point.
|
|
2525
2781
|
* @param argv Command-line arguments (excluding node and script path).
|