@kici-dev/compiler 0.4.0 → 0.6.0
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/cli.js +37 -7
- package/dist/commands/approve.d.ts +12 -0
- package/dist/commands/approve.js +5 -2
- package/dist/commands/compile.js +5 -15
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/endpoints.js +4 -6
- package/dist/commands/held-run-client.d.ts +21 -1
- package/dist/commands/held-run-client.js +34 -15
- package/dist/commands/hook.js +22 -20
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +2 -1
- package/dist/commands/init.d.ts +9 -2
- package/dist/commands/init.js +43 -16
- package/dist/commands/login.js +1 -1
- package/dist/commands/orchestrators.js +3 -2
- package/dist/commands/preview.js +1 -8
- package/dist/commands/reject.d.ts +12 -0
- package/dist/commands/reject.js +5 -2
- package/dist/commands/report/collect.d.ts +82 -0
- package/dist/commands/report/collect.js +234 -0
- package/dist/commands/report/identity.d.ts +48 -0
- package/dist/commands/report/identity.js +49 -0
- package/dist/commands/report/index.d.ts +63 -0
- package/dist/commands/report/index.js +119 -0
- package/dist/commands/report/upload.d.ts +38 -0
- package/dist/commands/report/upload.js +64 -0
- package/dist/commands/run-hold-watch.js +2 -2
- package/dist/commands/run.js +6 -3
- package/dist/commands/runs/show.js +80 -1
- package/dist/commands/types.js +51 -9
- package/dist/errors/formatter.d.ts +2 -4
- package/dist/errors/formatter.js +1 -3
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +2 -2
- package/dist/execution/sdk-alias.js +4 -2
- package/dist/fixtures/compiler.js +2 -1
- package/dist/format.js +3 -3
- package/dist/generators/secrets-dts.d.ts +8 -3
- package/dist/generators/secrets-dts.js +3 -3
- package/dist/hooks/installer.js +2 -1
- package/dist/llm-context/llms-architecture.txt +59 -23
- package/dist/llm-context/llms-cli.txt +168 -34
- package/dist/llm-context/llms-features-execution.txt +2017 -0
- package/dist/llm-context/llms-features.txt +96 -1100
- package/dist/llm-context/llms-full.txt +3131 -1407
- package/dist/llm-context/llms-getting-started.txt +164 -31
- package/dist/llm-context/llms-patterns.txt +186 -7
- package/dist/llm-context/llms-providers.txt +15 -33
- package/dist/llm-context/llms-sdk-runtime.txt +62 -40
- package/dist/llm-context/llms-sdk.txt +284 -58
- package/dist/llm-context/llms.txt +23 -15
- package/dist/local-plane/paths.d.ts +15 -0
- package/dist/local-plane/paths.js +22 -1
- package/dist/local-plane/plane-manager.js +2 -2
- package/dist/local-plane/port-holder.js +1 -1
- package/dist/local-plane/postgres.d.ts +3 -16
- package/dist/local-plane/postgres.js +10 -15
- package/dist/lockfile/generator.d.ts +12 -0
- package/dist/lockfile/generator.js +182 -54
- package/dist/lockfile/index.d.ts +0 -2
- package/dist/lockfile/index.js +1 -2
- package/dist/postinstall.js +2 -1
- package/dist/remote/config.d.ts +2 -15
- package/dist/remote/config.js +2 -16
- package/dist/remote/dashboard-client.d.ts +39 -0
- package/dist/remote/dashboard-client.js +41 -0
- package/dist/remote/oauth.js +7 -5
- package/dist/remote/uploader.js +2 -2
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/dry-run.d.ts +1 -2
- package/dist/test-runner/dry-run.js +5 -20
- package/dist/test-runner/git-detector.js +2 -1
- package/dist/test-runner/job-executor.js +2 -1
- package/dist/test-runner/payload-builder.js +11 -17
- package/dist/types.d.ts +64 -10
- package/dist/types.js +7 -1
- package/dist/validation/validator.js +63 -6
- package/package.json +16 -11
- package/sbom.spdx.json +957 -900
- package/dist/lockfile/purity-analyzer.d.ts +0 -25
- package/dist/lockfile/purity-analyzer.js +0 -204
- package/dist/lockfile/purity-diagnostics.d.ts +0 -31
- package/dist/lockfile/purity-diagnostics.js +0 -52
package/dist/cli.js
CHANGED
|
@@ -4,10 +4,10 @@ import { shouldSuppressBanner } from "./cli-banner.js";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { realpathSync } from "node:fs";
|
|
7
|
-
import { Argument, Command
|
|
7
|
+
import { Argument, Command } from "commander";
|
|
8
8
|
import pc from "picocolors";
|
|
9
9
|
//#region src/cli.ts
|
|
10
|
-
const version = "0.
|
|
10
|
+
const version = "0.6.0";
|
|
11
11
|
/**
|
|
12
12
|
* Top-level commands that were removed, mapped to their current equivalent.
|
|
13
13
|
* Consulted when the CLI hits an unknown command so the user gets a precise
|
|
@@ -139,7 +139,7 @@ function buildProgram() {
|
|
|
139
139
|
const success = await previewCommand(event, options);
|
|
140
140
|
process.exit(success ? 0 : 1);
|
|
141
141
|
});
|
|
142
|
-
program.command("init").description("Initialize .kici/ directory with default workflows").option("--force", "Overwrite existing .kici/ directory", false).option("--skip-install", "Create files without installing dependencies", false).option("--package-manager <npm|pnpm|yarn>", "Force a package manager for the install step (default: auto-detect)").option("--mjs", "JavaScript-only mode (no TypeScript, no dependencies)", false).option("--workspace", "Integrate .kici/ into the detected pnpm/npm/yarn workspace so workflows can import sibling packages", false).option("--standalone", "Force a self-contained .kici/ even inside a workspace", false).option("--no-agents-md", "Skip writing .kici/AGENTS.md (LLM authoring context)").option("--private-registry <url>", "Scaffold a workflow registries: entry pointing at <url>").option("--private-registry-scope <scope>", "Optional npm package scope (e.g. @my-org) for the private registry").option("--private-registry-secret <ref>", "Qualified secret reference (env:NAME) the private registry token comes from", "production:NPM_TOKEN").
|
|
142
|
+
program.command("init").description("Initialize .kici/ directory with default workflows").option("--force", "Overwrite existing .kici/ directory", false).option("--skip-install", "Create files without installing dependencies", false).option("--package-manager <npm|pnpm|yarn>", "Force a package manager for the install step (default: auto-detect)").option("--mjs", "JavaScript-only mode (no TypeScript, no dependencies)", false).option("--workspace", "Integrate .kici/ into the detected pnpm/npm/yarn workspace so workflows can import sibling packages", false).option("--standalone", "Force a self-contained .kici/ even inside a workspace", false).option("--no-agents-md", "Skip writing .kici/AGENTS.md (LLM authoring context)").option("--private-registry <url>", "Scaffold a workflow registries: entry pointing at <url>").option("--private-registry-scope <scope>", "Optional npm package scope (e.g. @my-org) for the private registry").option("--private-registry-secret <ref>", "Qualified secret reference (env:NAME) the private registry token comes from", "production:NPM_TOKEN").action(async (options) => {
|
|
143
143
|
const { initCommand } = await import("./commands/index.js");
|
|
144
144
|
const success = await initCommand({
|
|
145
145
|
...options,
|
|
@@ -355,7 +355,7 @@ Environment variables:
|
|
|
355
355
|
const success = await runsListCommand(options);
|
|
356
356
|
process.exit(success ? 0 : 1);
|
|
357
357
|
});
|
|
358
|
-
runsCommand.command("show").argument("<run-id>", "Run ID to inspect").description("Show a run summary with its jobs and steps").option("--json", "Output raw JSON", false).action(async (runId, options) => {
|
|
358
|
+
runsCommand.command("show").argument("<run-id>", "Run ID to inspect").description("Show a run summary with its jobs and steps, why a job did not run, and any approval hold").option("--json", "Output raw JSON", false).action(async (runId, options) => {
|
|
359
359
|
const { runsShowCommand } = await import("./commands/index.js");
|
|
360
360
|
const success = await runsShowCommand(runId, { json: options.json });
|
|
361
361
|
process.exit(success ? 0 : 1);
|
|
@@ -396,19 +396,23 @@ Environment variables:
|
|
|
396
396
|
});
|
|
397
397
|
process.exit(success ? 0 : 1);
|
|
398
398
|
});
|
|
399
|
-
program.command("approve").argument("<run-id>", "Run ID whose approval gate to approve").description("Approve a held approval gate for a run").option("--job <name>", "Approve the hold for a specific job").option("--step <index>", "Approve a step-scoped hold (requires --job)").action(async (runId, options) => {
|
|
399
|
+
program.command("approve").argument("<run-id>", "Run ID whose approval gate to approve").description("Approve a held approval gate for a run").option("--job <name>", "Approve the hold for a specific job").option("--step <index>", "Approve a step-scoped hold (requires --job)").option("--hold-type <type>", "Approve the hold of this type (reviewer, timer, concurrency, security) — a job can carry two").option("--hold <id>", "Approve one hold by its id, as listed when nothing else separates them").action(async (runId, options) => {
|
|
400
400
|
const { approveCommand } = await import("./commands/index.js");
|
|
401
401
|
const success = await approveCommand(runId, {
|
|
402
402
|
job: options.job,
|
|
403
|
-
step: options.step
|
|
403
|
+
step: options.step,
|
|
404
|
+
hold: options.hold,
|
|
405
|
+
holdType: options.holdType
|
|
404
406
|
});
|
|
405
407
|
process.exit(success ? 0 : 1);
|
|
406
408
|
});
|
|
407
|
-
program.command("reject").argument("<run-id>", "Run ID whose approval gate to reject").description("Reject a held approval gate for a run").option("--job <name>", "Reject the hold for a specific job").option("--step <index>", "Reject a step-scoped hold (requires --job)").requiredOption("--reason <text>", "Reason for the rejection").action(async (runId, options) => {
|
|
409
|
+
program.command("reject").argument("<run-id>", "Run ID whose approval gate to reject").description("Reject a held approval gate for a run").option("--job <name>", "Reject the hold for a specific job").option("--step <index>", "Reject a step-scoped hold (requires --job)").option("--hold-type <type>", "Reject the hold of this type (reviewer, timer, concurrency, security) — a job can carry two").option("--hold <id>", "Reject one hold by its id, as listed when nothing else separates them").requiredOption("--reason <text>", "Reason for the rejection").action(async (runId, options) => {
|
|
408
410
|
const { rejectCommand } = await import("./commands/index.js");
|
|
409
411
|
const success = await rejectCommand(runId, {
|
|
410
412
|
job: options.job,
|
|
411
413
|
step: options.step,
|
|
414
|
+
hold: options.hold,
|
|
415
|
+
holdType: options.holdType,
|
|
412
416
|
reason: options.reason
|
|
413
417
|
});
|
|
414
418
|
process.exit(success ? 0 : 1);
|
|
@@ -440,6 +444,32 @@ Environment variables:
|
|
|
440
444
|
});
|
|
441
445
|
process.exit(success ? 0 : 1);
|
|
442
446
|
});
|
|
447
|
+
const reportCmd = program.command("report").description("Gather a redacted diagnostic bundle to share when reporting an issue");
|
|
448
|
+
reportCmd.enablePositionalOptions();
|
|
449
|
+
reportCmd.option("--run <id>", "Scope the bundle to a failing run").option("-o, --output <path>", "Where to write the bundle ZIP").option("--metadata <key=value>", "Attach metadata (repeatable)", (v, p) => [...p, v], []).option("--no-redact", "Do NOT redact secrets (prints a loud warning)").option("--upload", "Upload the bundle privately to KiCI and print a reference id").option("--message <text>", "Describe the problem (sent with --upload)").option("--email <address>", "Contact address for follow-up (sent with --upload)").option("--kici-dir <path>", "Path to the .kici directory", ".kici").action(async (options) => {
|
|
450
|
+
const { reportCommand } = await import("./commands/index.js");
|
|
451
|
+
const ok = await reportCommand({
|
|
452
|
+
output: options.output,
|
|
453
|
+
run: options.run,
|
|
454
|
+
metadata: options.metadata,
|
|
455
|
+
redact: options.redact !== false,
|
|
456
|
+
upload: options.upload,
|
|
457
|
+
message: options.message,
|
|
458
|
+
email: options.email,
|
|
459
|
+
kiciDir: options.kiciDir
|
|
460
|
+
});
|
|
461
|
+
process.exit(ok ? 0 : 1);
|
|
462
|
+
});
|
|
463
|
+
reportCmd.command("list").description("List the issue reports you have uploaded").option("--json", "Output raw JSON", false).action(async (options) => {
|
|
464
|
+
const { reportListCommand } = await import("./commands/index.js");
|
|
465
|
+
const ok = await reportListCommand({ json: options.json });
|
|
466
|
+
process.exit(ok ? 0 : 1);
|
|
467
|
+
});
|
|
468
|
+
reportCmd.command("withdraw").argument("<ref>", "Reference id of the report to withdraw").description("Withdraw an uploaded report and delete its bundle").action(async (ref) => {
|
|
469
|
+
const { reportWithdrawCommand } = await import("./commands/index.js");
|
|
470
|
+
const ok = await reportWithdrawCommand({ ref });
|
|
471
|
+
process.exit(ok ? 0 : 1);
|
|
472
|
+
});
|
|
443
473
|
program.command("workflows").description("Manage workflow registrations").command("list").description("List permanently registered workflows").option("--json", "Output as JSON", false).option("--stale <duration>", "Filter stale registrations (e.g., 30d, 7d)").option("--trigger-type <type>", "Filter by trigger type").option("--repo <repo>", "Filter by repository").action(async (options) => {
|
|
444
474
|
const { workflowsListCommand } = await import("./commands/index.js");
|
|
445
475
|
const success = await workflowsListCommand({
|
|
@@ -11,6 +11,18 @@ export interface ApproveOptions {
|
|
|
11
11
|
job?: string;
|
|
12
12
|
/** Match a step-scoped hold by its step index. */
|
|
13
13
|
step?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Match one hold by its own id. The escape hatch for an ambiguity nothing
|
|
16
|
+
* else resolves; the error listing prints the ids when it needs to.
|
|
17
|
+
*/
|
|
18
|
+
hold?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Narrow to holds of one type (`reviewer` / `timer` / `concurrency` /
|
|
21
|
+
* `security`). A job carrying an SDK `requireApproval` AND a security-typed
|
|
22
|
+
* context gate has two pending holds under one job name, and this is what
|
|
23
|
+
* separates them.
|
|
24
|
+
*/
|
|
25
|
+
holdType?: string;
|
|
14
26
|
}
|
|
15
27
|
/**
|
|
16
28
|
* Approve a held approval gate.
|
package/dist/commands/approve.js
CHANGED
|
@@ -22,9 +22,12 @@ async function approveCommand(runId, options = {}) {
|
|
|
22
22
|
try {
|
|
23
23
|
const ctx = await resolveHeldRunContext();
|
|
24
24
|
if (!ctx) return false;
|
|
25
|
-
const
|
|
25
|
+
const holds = await listHeldRunsForRun(ctx, runId);
|
|
26
|
+
const resolution = resolveHeldRunId(holds, {
|
|
26
27
|
job: options.job,
|
|
27
|
-
step: options.step
|
|
28
|
+
step: options.step,
|
|
29
|
+
holdId: options.hold,
|
|
30
|
+
holdType: options.holdType
|
|
28
31
|
});
|
|
29
32
|
if (!resolution.ok) {
|
|
30
33
|
logger.error(pc.red(resolution.error));
|
package/dist/commands/compile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
-
import {
|
|
2
|
+
import { formatError, isCompilerError } from "../errors/formatter.js";
|
|
3
3
|
import "../errors/index.js";
|
|
4
4
|
import { discoverWorkflows, resolveKiciDir } from "../execution/executor.js";
|
|
5
5
|
import "../execution/index.js";
|
|
@@ -8,7 +8,6 @@ import { runTypecheck } from "../validation/typecheck.js";
|
|
|
8
8
|
import "../validation/index.js";
|
|
9
9
|
import { BREAKING_FLOOR, SCHEMA_VERSION } from "../types.js";
|
|
10
10
|
import { computeLockfileHash, detectGitRoot, generateLockFile, schemaWindowWarning, serializeLockFile } from "../lockfile/generator.js";
|
|
11
|
-
import { collectWorkflowPurityWarnings } from "../lockfile/purity-diagnostics.js";
|
|
12
11
|
import "../lockfile/index.js";
|
|
13
12
|
import path from "node:path";
|
|
14
13
|
import { existsSync } from "node:fs";
|
|
@@ -19,16 +18,6 @@ import { PackageManager, detectPackageManagerSync } from "@kici-dev/core/package
|
|
|
19
18
|
import { execSync } from "node:child_process";
|
|
20
19
|
//#region src/commands/compile.ts
|
|
21
20
|
/**
|
|
22
|
-
* Render an impure dynamic-value function as a `W101` compile warning naming the
|
|
23
|
-
* job, the field, the impurity reason, and the ~5-10s agent-side init-job cost.
|
|
24
|
-
*/
|
|
25
|
-
function formatPurityWarning(w) {
|
|
26
|
-
return formatError(compilerError(PURITY_FALLBACK_CODE, `job "${w.jobName}": ${w.field} function is not pure (${w.reason}). An init job will be required, adding ~5-10s delay.`, {
|
|
27
|
-
severity: "warning",
|
|
28
|
-
suggestion: `Make the ${w.field} function pure (synchronous, referencing only its parameters) to inline it and skip the init job. See https://docs.kici.dev — dynamic values.`
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
21
|
* Read the existing lock file and return its lockfileHash, if present.
|
|
33
22
|
*/
|
|
34
23
|
async function readExistingLockfileHash(lockPath) {
|
|
@@ -71,7 +60,8 @@ async function compileCommand(options) {
|
|
|
71
60
|
if (existsSync(path.join(absoluteKiciDir, "package.json"))) {
|
|
72
61
|
const existingHash = await readExistingLockfileHash(lockPath);
|
|
73
62
|
if (existingHash) {
|
|
74
|
-
const
|
|
63
|
+
const gitRoot = detectGitRoot();
|
|
64
|
+
const currentHash = computeLockfileHash(gitRoot);
|
|
75
65
|
if (currentHash && currentHash !== existingHash) {
|
|
76
66
|
logger.info(pc.yellow("Dependencies changed") + " — reinstalling before compile...");
|
|
77
67
|
await reinstallDeps(absoluteKiciDir);
|
|
@@ -86,8 +76,8 @@ async function compileCommand(options) {
|
|
|
86
76
|
return false;
|
|
87
77
|
}
|
|
88
78
|
if (options.verbose) logger.debug(pc.dim("Validation passed"));
|
|
89
|
-
const
|
|
90
|
-
|
|
79
|
+
const lockFile = generateLockFile(workflowsWithSource);
|
|
80
|
+
const lockJson = serializeLockFile(lockFile);
|
|
91
81
|
const windowWarning = schemaWindowWarning(BREAKING_FLOOR, SCHEMA_VERSION);
|
|
92
82
|
if (windowWarning) logger.warn(pc.yellow(windowWarning));
|
|
93
83
|
if (!options.check) {
|
package/dist/commands/doctor.js
CHANGED
|
@@ -20,7 +20,7 @@ import { promisify } from "node:util";
|
|
|
20
20
|
* Platform API is required.
|
|
21
21
|
*/
|
|
22
22
|
const execFileAsync = promisify(execFile);
|
|
23
|
-
const SEVEN_DAYS_MS =
|
|
23
|
+
const SEVEN_DAYS_MS = 6048e5;
|
|
24
24
|
/** Is a stored token + endpoint present (regardless of expiry)? */
|
|
25
25
|
function hasCredentials(config) {
|
|
26
26
|
return Boolean((config.pat ?? config.token) && (config.platformEndpoint ?? config.endpoint));
|
|
@@ -47,7 +47,7 @@ function checkLogin(config, now) {
|
|
|
47
47
|
if (expiresAt - now < SEVEN_DAYS_MS) return {
|
|
48
48
|
name,
|
|
49
49
|
status: "warn",
|
|
50
|
-
message: `Access token expires in ${Math.ceil((expiresAt - now) /
|
|
50
|
+
message: `Access token expires in ${Math.ceil((expiresAt - now) / 864e5)} day(s).`,
|
|
51
51
|
nextCommand: "kici login"
|
|
52
52
|
};
|
|
53
53
|
}
|
|
@@ -119,12 +119,10 @@ function categorizeTrigger(workflowName, trigger, groups) {
|
|
|
119
119
|
eventName: `workflows_failed_batch${trigger.name ? `:${trigger.name}` : ""}`
|
|
120
120
|
});
|
|
121
121
|
break;
|
|
122
|
-
case "job_complete":
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
});
|
|
127
|
-
break;
|
|
122
|
+
case "job_complete": groups.kiciEvent.push({
|
|
123
|
+
workflowName,
|
|
124
|
+
eventName: `job_complete${trigger.workflow ? `:${trigger.workflow}` : ""}${trigger.job ? `.${trigger.job}` : ""}`
|
|
125
|
+
});
|
|
128
126
|
}
|
|
129
127
|
}
|
|
130
128
|
/**
|
|
@@ -6,6 +6,19 @@
|
|
|
6
6
|
* API.
|
|
7
7
|
*/
|
|
8
8
|
import type { HeldRunSummary } from '@kici-dev/engine';
|
|
9
|
+
/**
|
|
10
|
+
* A failed held-runs request, carrying the HTTP status so a caller can tell a
|
|
11
|
+
* caller who will NEVER have the data (401 / 403 — no permission on the
|
|
12
|
+
* held-runs surface) from a transport or server failure that may succeed next
|
|
13
|
+
* time. `kici runs show` reads holds as optional detail, and reminding such a
|
|
14
|
+
* caller on every single invocation is noise, not information.
|
|
15
|
+
*/
|
|
16
|
+
export declare class HeldRunRequestError extends Error {
|
|
17
|
+
readonly status: number;
|
|
18
|
+
constructor(message: string, status: number);
|
|
19
|
+
/** True when the caller lacks permission — retrying changes nothing. */
|
|
20
|
+
get isPermissionDenied(): boolean;
|
|
21
|
+
}
|
|
9
22
|
/** Resolved auth context for a held-run command. */
|
|
10
23
|
export interface HeldRunContext {
|
|
11
24
|
endpoint: string;
|
|
@@ -15,8 +28,15 @@ export interface HeldRunContext {
|
|
|
15
28
|
/**
|
|
16
29
|
* Resolve the auth context, printing a clear error and returning null when the
|
|
17
30
|
* CLI is not authenticated / no active org is set.
|
|
31
|
+
*
|
|
32
|
+
* `quiet` suppresses the error output for a caller whose own command does not
|
|
33
|
+
* depend on the held-run surface — `kici runs show` reads holds as extra
|
|
34
|
+
* detail on a run it can already display, so an unresolvable context must not
|
|
35
|
+
* make it print an authentication error for a command that worked.
|
|
18
36
|
*/
|
|
19
|
-
export declare function resolveHeldRunContext(
|
|
37
|
+
export declare function resolveHeldRunContext(options?: {
|
|
38
|
+
quiet?: boolean;
|
|
39
|
+
}): Promise<HeldRunContext | null>;
|
|
20
40
|
/** List the held runs for a single run id. */
|
|
21
41
|
export declare function listHeldRunsForRun(ctx: HeldRunContext, runId: string): Promise<HeldRunSummary[]>;
|
|
22
42
|
/**
|
|
@@ -11,25 +11,44 @@ import { logger } from "@kici-dev/core";
|
|
|
11
11
|
* API.
|
|
12
12
|
*/
|
|
13
13
|
/**
|
|
14
|
+
* A failed held-runs request, carrying the HTTP status so a caller can tell a
|
|
15
|
+
* caller who will NEVER have the data (401 / 403 — no permission on the
|
|
16
|
+
* held-runs surface) from a transport or server failure that may succeed next
|
|
17
|
+
* time. `kici runs show` reads holds as optional detail, and reminding such a
|
|
18
|
+
* caller on every single invocation is noise, not information.
|
|
19
|
+
*/
|
|
20
|
+
var HeldRunRequestError = class extends Error {
|
|
21
|
+
status;
|
|
22
|
+
constructor(message, status) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.name = "HeldRunRequestError";
|
|
26
|
+
}
|
|
27
|
+
/** True when the caller lacks permission — retrying changes nothing. */
|
|
28
|
+
get isPermissionDenied() {
|
|
29
|
+
return this.status === 401 || this.status === 403;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
14
33
|
* Resolve the auth context, printing a clear error and returning null when the
|
|
15
34
|
* CLI is not authenticated / no active org is set.
|
|
35
|
+
*
|
|
36
|
+
* `quiet` suppresses the error output for a caller whose own command does not
|
|
37
|
+
* depend on the held-run surface — `kici runs show` reads holds as extra
|
|
38
|
+
* detail on a run it can already display, so an unresolvable context must not
|
|
39
|
+
* make it print an authentication error for a command that worked.
|
|
16
40
|
*/
|
|
17
|
-
async function resolveHeldRunContext() {
|
|
41
|
+
async function resolveHeldRunContext(options = {}) {
|
|
18
42
|
const config = await loadGlobalConfig();
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
logger.error(pc.red("Not authenticated. Run `kici login` to get started."));
|
|
43
|
+
const fail = (message) => {
|
|
44
|
+
if (!options.quiet) logger.error(pc.red(message));
|
|
22
45
|
return null;
|
|
23
|
-
}
|
|
46
|
+
};
|
|
47
|
+
const token = config.pat ?? config.token;
|
|
48
|
+
if (!token) return fail("Not authenticated. Run `kici login` to get started.");
|
|
24
49
|
const endpoint = config.platformEndpoint ?? config.endpoint;
|
|
25
|
-
if (!endpoint)
|
|
26
|
-
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
if (!config.activeOrgId) {
|
|
30
|
-
logger.error(pc.red("No active organization. Run `kici org use <name>` to set one."));
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
50
|
+
if (!endpoint) return fail("No endpoint configured. Run `kici login` to configure.");
|
|
51
|
+
if (!config.activeOrgId) return fail("No active organization. Run `kici org use <name>` to set one.");
|
|
33
52
|
return {
|
|
34
53
|
endpoint,
|
|
35
54
|
token,
|
|
@@ -49,7 +68,7 @@ async function listHeldRunsForRun(ctx, runId) {
|
|
|
49
68
|
method: "GET",
|
|
50
69
|
headers: authHeaders(ctx.token)
|
|
51
70
|
});
|
|
52
|
-
if (!response.ok) throw new
|
|
71
|
+
if (!response.ok) throw new HeldRunRequestError(await describeError(response), response.status);
|
|
53
72
|
return (await response.json()).heldRuns ?? [];
|
|
54
73
|
}
|
|
55
74
|
/**
|
|
@@ -99,6 +118,6 @@ async function describeError(response) {
|
|
|
99
118
|
}
|
|
100
119
|
}
|
|
101
120
|
//#endregion
|
|
102
|
-
export { listHeldRunsForRun, postApprove, postReject, resolveHeldRunContext };
|
|
121
|
+
export { HeldRunRequestError, listHeldRunsForRun, postApprove, postReject, resolveHeldRunContext };
|
|
103
122
|
|
|
104
123
|
//# sourceMappingURL=held-run-client.js.map
|
package/dist/commands/hook.js
CHANGED
|
@@ -31,27 +31,28 @@ async function hookInstallCommand(options = {}) {
|
|
|
31
31
|
if (options.git) selectedTool = "git";
|
|
32
32
|
else {
|
|
33
33
|
const tools = await detectHookTools();
|
|
34
|
-
if (tools.length === 0)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
if (tools.length === 0) {
|
|
35
|
+
if (process.stdout.isTTY && !isCiEnvironment()) {
|
|
36
|
+
const choice = await select({
|
|
37
|
+
message: "No pre-commit tool detected. How would you like to install the hook?",
|
|
38
|
+
choices: [{
|
|
39
|
+
name: "Raw git hook (.git/hooks/pre-commit)",
|
|
40
|
+
value: "git"
|
|
41
|
+
}, {
|
|
42
|
+
name: "Skip installation",
|
|
43
|
+
value: "skip"
|
|
44
|
+
}]
|
|
45
|
+
});
|
|
46
|
+
if (choice === "skip") {
|
|
47
|
+
logger.info(pc.yellow("Hook installation skipped."));
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
selectedTool = choice;
|
|
51
|
+
} else {
|
|
52
|
+
logger.info(pc.yellow("No pre-commit tool detected. Use --git to install raw git hook."));
|
|
47
53
|
return true;
|
|
48
54
|
}
|
|
49
|
-
|
|
50
|
-
} else {
|
|
51
|
-
logger.info(pc.yellow("No pre-commit tool detected. Use --git to install raw git hook."));
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
else if (tools.length === 1) {
|
|
55
|
+
} else if (tools.length === 1) {
|
|
55
56
|
selectedTool = tools[0].name;
|
|
56
57
|
logger.info(pc.gray(`Detected ${selectedTool}, installing hook...`));
|
|
57
58
|
} else if (process.stdout.isTTY && !isCiEnvironment()) selectedTool = await select({
|
|
@@ -87,7 +88,8 @@ async function hookInstallCommand(options = {}) {
|
|
|
87
88
|
*/
|
|
88
89
|
async function detectVerdaccioNpmrc() {
|
|
89
90
|
try {
|
|
90
|
-
|
|
91
|
+
const npmrcPath = path.resolve(".kici", ".npmrc");
|
|
92
|
+
return (await readFile(npmrcPath, "utf-8")).includes("verdaccio");
|
|
91
93
|
} catch {
|
|
92
94
|
return false;
|
|
93
95
|
}
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ export { diagnosticsCommand } from './diagnostics.js';
|
|
|
40
40
|
export type { DiagnosticsOptions } from './diagnostics.js';
|
|
41
41
|
export { doctorCommand } from './doctor.js';
|
|
42
42
|
export type { DoctorOptions } from './doctor.js';
|
|
43
|
+
export { reportCommand, reportListCommand, reportWithdrawCommand } from './report/index.js';
|
|
44
|
+
export type { ReportOptions } from './report/index.js';
|
|
43
45
|
export { orgListCommand, orgUseCommand, orgCurrentCommand } from './org.js';
|
|
44
46
|
export { orchestratorsListCommand, orchestratorsUseCommand } from './orchestrators.js';
|
|
45
47
|
export type { OrchestratorsOptions } from './orchestrators.js';
|
package/dist/commands/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { runsCancelCommand } from "./runs/cancel.js";
|
|
|
24
24
|
import { runsArtifactsListCommand } from "./runs/artifacts/list.js";
|
|
25
25
|
import { runsArtifactsDownloadCommand } from "./runs/artifacts/download.js";
|
|
26
26
|
import { typesCommand } from "./types.js";
|
|
27
|
+
import { reportCommand, reportListCommand, reportWithdrawCommand } from "./report/index.js";
|
|
27
28
|
import { orgCurrentCommand, orgListCommand, orgUseCommand } from "./org.js";
|
|
28
29
|
import { orchestratorsListCommand, orchestratorsUseCommand } from "./orchestrators.js";
|
|
29
30
|
import { localAttachCommand, localDetachCommand, localDownCommand, localLogsCommand, localStatusCommand, localUpCommand } from "./local.js";
|
|
@@ -33,4 +34,4 @@ import { rejectCommand } from "./reject.js";
|
|
|
33
34
|
import { workflowsListCommand } from "./workflows.js";
|
|
34
35
|
import { verifyAttestationCommand } from "./verify-attestation.js";
|
|
35
36
|
import { notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand } from "./notifications.js";
|
|
36
|
-
export { approveCommand, compileCommand, diagnosticsCommand, docsCommand, docsLlmCommand, doctorCommand, drainWorkerCommand, endpointsCommand, fixtureCommand, hookInstallCommand, initCommand, localAttachCommand, localDetachCommand, localDownCommand, localLogsCommand, localStatusCommand, localTrustRootCommand, localUpCommand, loginCommand, logoutCommand, notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand, orchestratorsListCommand, orchestratorsUseCommand, orgCurrentCommand, orgListCommand, orgUseCommand, patCreateCommand, previewCommand, previewEvent, rejectCommand, runRemoteCommand, runRoutedCommand, runsArtifactsDownloadCommand, runsArtifactsListCommand, runsCancelCommand, runsListCommand, runsLogsCommand, runsRerunCommand, runsShowCommand, secretsListCommand, typesCommand, verifyAttestationCommand, watchCommand, workflowsListCommand };
|
|
37
|
+
export { approveCommand, compileCommand, diagnosticsCommand, docsCommand, docsLlmCommand, doctorCommand, drainWorkerCommand, endpointsCommand, fixtureCommand, hookInstallCommand, initCommand, localAttachCommand, localDetachCommand, localDownCommand, localLogsCommand, localStatusCommand, localTrustRootCommand, localUpCommand, loginCommand, logoutCommand, notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand, orchestratorsListCommand, orchestratorsUseCommand, orgCurrentCommand, orgListCommand, orgUseCommand, patCreateCommand, previewCommand, previewEvent, rejectCommand, reportCommand, reportListCommand, reportWithdrawCommand, runRemoteCommand, runRoutedCommand, runsArtifactsDownloadCommand, runsArtifactsListCommand, runsCancelCommand, runsListCommand, runsLogsCommand, runsRerunCommand, runsShowCommand, secretsListCommand, typesCommand, verifyAttestationCommand, watchCommand, workflowsListCommand };
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -19,8 +19,6 @@ export interface InitOptions {
|
|
|
19
19
|
packageManager?: string;
|
|
20
20
|
/** JavaScript-only mode (no TypeScript, no dependencies) */
|
|
21
21
|
mjs?: boolean;
|
|
22
|
-
/** Write .npmrc pointing @kici-dev scope to local Verdaccio */
|
|
23
|
-
useVerdaccioLocal?: boolean;
|
|
24
22
|
/** Private registry URL to scaffold a `registries:` entry for. */
|
|
25
23
|
privateRegistry?: string;
|
|
26
24
|
/** Optional npm scope (e.g. `@my-org`) the private registry serves. */
|
|
@@ -41,4 +39,13 @@ export interface InitOptions {
|
|
|
41
39
|
* @returns true on success, false on error
|
|
42
40
|
*/
|
|
43
41
|
export declare function initCommand(options?: InitOptions): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Scaffold `.kici/.gitignore` so the generated `types/` declarations stay
|
|
44
|
+
* untracked. Never overwrites an existing file — the customer may have edited
|
|
45
|
+
* it. Deliberately ignores only `types/`, NOT `kici.lock.json` (the
|
|
46
|
+
* orchestrator fetches the lock from the repo, so it genuinely is source).
|
|
47
|
+
*
|
|
48
|
+
* @param kiciDir - The resolved `.kici` directory path.
|
|
49
|
+
*/
|
|
50
|
+
export declare function writeKiciGitignore(kiciDir: string): Promise<void>;
|
|
44
51
|
//# sourceMappingURL=init.d.ts.map
|
package/dist/commands/init.js
CHANGED
|
@@ -76,7 +76,8 @@ async function initCommand(options = {}) {
|
|
|
76
76
|
logger.info(pc.gray("Writing .kiciignore"));
|
|
77
77
|
await writeFile(kiciIgnorePath, kiciIgnoreTemplate, "utf-8");
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
await writeKiciGitignore(kiciDir);
|
|
80
|
+
const devMode = await detectDevelopmentMode();
|
|
80
81
|
const mode = await resolveScaffoldMode(options);
|
|
81
82
|
const useVerdaccio = devMode;
|
|
82
83
|
if (mode.kind === "integrate") {
|
|
@@ -204,7 +205,8 @@ async function detectDefaultBranch() {
|
|
|
204
205
|
async function detectDevelopmentMode() {
|
|
205
206
|
if (process.env.KICI_DEV === "true") return true;
|
|
206
207
|
try {
|
|
207
|
-
const
|
|
208
|
+
const rootPkgPath = path.resolve(process.cwd(), "package.json");
|
|
209
|
+
const content = await readFile(rootPkgPath, "utf-8");
|
|
208
210
|
return JSON.parse(content).kici?.development === true;
|
|
209
211
|
} catch {
|
|
210
212
|
return false;
|
|
@@ -234,7 +236,8 @@ const VERDACCIO_NPMRC = "@kici-dev:registry=http://verdaccio.local:4873\n";
|
|
|
234
236
|
async function writeTsConfigAndTypesDir(kiciDir) {
|
|
235
237
|
logger.info(pc.gray("Writing .kici/tsconfig.json"));
|
|
236
238
|
await writeFile(path.join(kiciDir, "tsconfig.json"), await generateTsConfig(), "utf-8");
|
|
237
|
-
|
|
239
|
+
const typesDir = path.join(kiciDir, "types");
|
|
240
|
+
await mkdir(typesDir, { recursive: true });
|
|
238
241
|
logger.info(pc.gray("Created .kici/types/ for generated type declarations"));
|
|
239
242
|
}
|
|
240
243
|
/** Run the given package manager's install in `dir`, with pnpm's build-gate flag. */
|
|
@@ -363,6 +366,28 @@ async function generateTsConfig() {
|
|
|
363
366
|
baseConfig.compilerOptions.paths = typeScriptPaths;
|
|
364
367
|
return JSON.stringify(baseConfig, null, 2) + "\n";
|
|
365
368
|
}
|
|
369
|
+
/** The single entry `kici init` scaffolds into `.kici/.gitignore`. */
|
|
370
|
+
const KICI_GITIGNORE_TEMPLATE = `# Generated type declarations (\`kici types\` / authenticated \`kici compile\`).
|
|
371
|
+
# A snapshot of one org's secret keys — a local development aid, not source.
|
|
372
|
+
types/
|
|
373
|
+
`;
|
|
374
|
+
/**
|
|
375
|
+
* Scaffold `.kici/.gitignore` so the generated `types/` declarations stay
|
|
376
|
+
* untracked. Never overwrites an existing file — the customer may have edited
|
|
377
|
+
* it. Deliberately ignores only `types/`, NOT `kici.lock.json` (the
|
|
378
|
+
* orchestrator fetches the lock from the repo, so it genuinely is source).
|
|
379
|
+
*
|
|
380
|
+
* @param kiciDir - The resolved `.kici` directory path.
|
|
381
|
+
*/
|
|
382
|
+
async function writeKiciGitignore(kiciDir) {
|
|
383
|
+
const gitignorePath = path.join(kiciDir, ".gitignore");
|
|
384
|
+
if (await checkExists(gitignorePath)) {
|
|
385
|
+
logger.info(pc.gray("Skipping .kici/.gitignore (already exists)"));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
logger.info(pc.gray("Writing .kici/.gitignore"));
|
|
389
|
+
await writeFile(gitignorePath, KICI_GITIGNORE_TEMPLATE, "utf-8");
|
|
390
|
+
}
|
|
366
391
|
/**
|
|
367
392
|
* Update .gitignore with .kici/ entries
|
|
368
393
|
*
|
|
@@ -504,17 +529,18 @@ async function writeAgentsMd(kiciDir) {
|
|
|
504
529
|
async function offerHookInstallation(useVerdaccio) {
|
|
505
530
|
const tools = await detectHookTools();
|
|
506
531
|
let selectedTool;
|
|
507
|
-
if (tools.length === 0)
|
|
508
|
-
message: "No pre-commit tool found. Install husky for git hooks?",
|
|
509
|
-
default: false
|
|
510
|
-
})) {
|
|
532
|
+
if (tools.length === 0) {
|
|
511
533
|
if (!await confirm({
|
|
512
|
-
message: "
|
|
534
|
+
message: "No pre-commit tool found. Install husky for git hooks?",
|
|
513
535
|
default: false
|
|
514
|
-
}))
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
536
|
+
})) {
|
|
537
|
+
if (!await confirm({
|
|
538
|
+
message: "Add kici compile to .git/hooks/pre-commit instead?",
|
|
539
|
+
default: false
|
|
540
|
+
})) return;
|
|
541
|
+
selectedTool = "git";
|
|
542
|
+
} else selectedTool = "husky";
|
|
543
|
+
} else if (tools.length === 1) {
|
|
518
544
|
if (!await confirm({
|
|
519
545
|
message: `Found ${tools[0].name}. Add kici compile hook?`,
|
|
520
546
|
default: true
|
|
@@ -528,11 +554,12 @@ async function offerHookInstallation(useVerdaccio) {
|
|
|
528
554
|
}))
|
|
529
555
|
});
|
|
530
556
|
const result = await installHook(selectedTool, { useVerdaccio });
|
|
531
|
-
if (result.success)
|
|
532
|
-
|
|
533
|
-
|
|
557
|
+
if (result.success) {
|
|
558
|
+
if (result.action === "skipped") logger.info(pc.yellow(`Hook already installed: ${result.message}`));
|
|
559
|
+
else logger.info(pc.green(`Hook installed: ${result.message}`));
|
|
560
|
+
} else logger.warn(pc.yellow(`Warning: ${result.message}`));
|
|
534
561
|
}
|
|
535
562
|
//#endregion
|
|
536
|
-
export { initCommand };
|
|
563
|
+
export { initCommand, writeKiciGitignore };
|
|
537
564
|
|
|
538
565
|
//# sourceMappingURL=init.js.map
|
package/dist/commands/login.js
CHANGED
|
@@ -31,7 +31,7 @@ async function promptInput(prompt) {
|
|
|
31
31
|
function checkPatExpiry(expiresAt) {
|
|
32
32
|
const expiryDate = new Date(expiresAt);
|
|
33
33
|
const now = /* @__PURE__ */ new Date();
|
|
34
|
-
const daysUntilExpiry = (expiryDate.getTime() - now.getTime()) /
|
|
34
|
+
const daysUntilExpiry = (expiryDate.getTime() - now.getTime()) / 864e5;
|
|
35
35
|
if (daysUntilExpiry <= 7 && daysUntilExpiry > 0) {
|
|
36
36
|
console.log(pc.yellow(`\n Warning: your personal access token expires in ${Math.ceil(daysUntilExpiry)} day(s) (${expiryDate.toLocaleDateString()}).`));
|
|
37
37
|
console.log(pc.yellow(" Run `kici login` again to refresh it."));
|
|
@@ -102,10 +102,11 @@ async function orchestratorsUseCommand(clusterName, options) {
|
|
|
102
102
|
}
|
|
103
103
|
return false;
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
const defaultClusters = {
|
|
106
106
|
...ctx.config.defaultClusters ?? {},
|
|
107
107
|
[ctx.orgId]: clusterName
|
|
108
|
-
}
|
|
108
|
+
};
|
|
109
|
+
await mergeGlobalConfig({ defaultClusters });
|
|
109
110
|
console.log(pc.green(`Default orchestrator for ${ctx.orgId} set to: ${clusterName}`));
|
|
110
111
|
return true;
|
|
111
112
|
}
|
package/dist/commands/preview.js
CHANGED
|
@@ -2,8 +2,6 @@ import "../rolldown-runtime-ClRpJifh.js";
|
|
|
2
2
|
import { discoverWorkflows, resolveKiciDir } from "../execution/executor.js";
|
|
3
3
|
import "../execution/index.js";
|
|
4
4
|
import { transformTriggers } from "../lockfile/generator.js";
|
|
5
|
-
import { analyzeJobPurity } from "../lockfile/purity-diagnostics.js";
|
|
6
|
-
import "../lockfile/index.js";
|
|
7
5
|
import { displayDryRun } from "../test-runner/dry-run.js";
|
|
8
6
|
import { parseEventArg } from "../test-runner/event-types.js";
|
|
9
7
|
import { buildEventPayload } from "../test-runner/payload-builder.js";
|
|
@@ -90,15 +88,10 @@ async function previewEvent(event, options) {
|
|
|
90
88
|
return false;
|
|
91
89
|
}
|
|
92
90
|
}
|
|
93
|
-
const purityWarnings = [];
|
|
94
|
-
for (const w of workflows) for (const j of w.jobs) {
|
|
95
|
-
if (typeof j === "function") continue;
|
|
96
|
-
purityWarnings.push(...analyzeJobPurity(j, w.name));
|
|
97
|
-
}
|
|
98
91
|
displayDryRun(lockWorkflows, decisions, {
|
|
99
92
|
workflow: options.workflow,
|
|
100
93
|
job: options.job
|
|
101
|
-
}
|
|
94
|
+
});
|
|
102
95
|
return true;
|
|
103
96
|
} catch (error) {
|
|
104
97
|
const message = toErrorMessage(error);
|
|
@@ -11,6 +11,18 @@ export interface RejectOptions {
|
|
|
11
11
|
job?: string;
|
|
12
12
|
/** Match a step-scoped hold by its step index. */
|
|
13
13
|
step?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Match one hold by its own id. The escape hatch for an ambiguity nothing
|
|
16
|
+
* else resolves; the error listing prints the ids when it needs to.
|
|
17
|
+
*/
|
|
18
|
+
hold?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Narrow to holds of one type (`reviewer` / `timer` / `concurrency` /
|
|
21
|
+
* `security`). A job carrying an SDK `requireApproval` AND a security-typed
|
|
22
|
+
* context gate has two pending holds under one job name, and this is what
|
|
23
|
+
* separates them.
|
|
24
|
+
*/
|
|
25
|
+
holdType?: string;
|
|
14
26
|
/** Required rejection reason. */
|
|
15
27
|
reason?: string;
|
|
16
28
|
}
|
package/dist/commands/reject.js
CHANGED
|
@@ -26,9 +26,12 @@ async function rejectCommand(runId, options = {}) {
|
|
|
26
26
|
}
|
|
27
27
|
const ctx = await resolveHeldRunContext();
|
|
28
28
|
if (!ctx) return false;
|
|
29
|
-
const
|
|
29
|
+
const holds = await listHeldRunsForRun(ctx, runId);
|
|
30
|
+
const resolution = resolveHeldRunId(holds, {
|
|
30
31
|
job: options.job,
|
|
31
|
-
step: options.step
|
|
32
|
+
step: options.step,
|
|
33
|
+
holdId: options.hold,
|
|
34
|
+
holdType: options.holdType
|
|
32
35
|
});
|
|
33
36
|
if (!resolution.ok) {
|
|
34
37
|
logger.error(pc.red(resolution.error));
|