@kici-dev/compiler 0.1.26 → 0.2.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/auth/headless-detect.d.ts +14 -5
- package/dist/auth/headless-detect.js +50 -9
- package/dist/cli-banner.d.ts +6 -3
- package/dist/cli-banner.js +13 -5
- package/dist/cli.js +180 -48
- package/dist/commands/check-mode.d.ts +1 -1
- package/dist/commands/compile.js +31 -7
- package/dist/commands/diagnostics.js +14 -2
- package/dist/commands/doctor.d.ts +64 -0
- package/dist/commands/doctor.js +413 -0
- package/dist/commands/endpoints.js +6 -0
- package/dist/commands/fixture.js +13 -0
- package/dist/commands/hook.js +3 -2
- package/dist/commands/index.d.ts +13 -1
- package/dist/commands/index.js +10 -3
- package/dist/commands/init-host-os.d.ts +34 -0
- package/dist/commands/init-host-os.js +44 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +160 -37
- package/dist/commands/local-trust-root.d.ts +6 -0
- package/dist/commands/local-trust-root.js +52 -0
- package/dist/commands/local.d.ts +65 -0
- package/dist/commands/local.js +206 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +41 -1
- package/dist/commands/logout.js +7 -0
- package/dist/commands/notifications.d.ts +61 -0
- package/dist/commands/notifications.js +274 -0
- package/dist/commands/preview.js +9 -2
- package/dist/commands/run-banner.d.ts +31 -0
- package/dist/commands/run-banner.js +25 -0
- package/dist/commands/run-routed.d.ts +53 -0
- package/dist/commands/run-routed.js +185 -0
- package/dist/commands/run.d.ts +0 -17
- package/dist/commands/run.js +44 -58
- package/dist/commands/runs/artifacts/download.d.ts +24 -0
- package/dist/commands/runs/artifacts/download.js +154 -0
- package/dist/commands/runs/artifacts/list.d.ts +5 -0
- package/dist/commands/runs/artifacts/list.js +47 -0
- package/dist/commands/runs/cancel.js +2 -2
- package/dist/commands/runs/list.js +8 -2
- package/dist/commands/secrets-list.d.ts +2 -2
- package/dist/commands/secrets-list.js +4 -4
- package/dist/commands/types.d.ts +3 -3
- package/dist/commands/types.js +4 -4
- package/dist/commands/verify-attestation.d.ts +3 -1
- package/dist/commands/verify-attestation.js +18 -7
- package/dist/errors/formatter.d.ts +14 -1
- package/dist/errors/formatter.js +13 -3
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +3 -2
- package/dist/errors/source-location.d.ts +20 -0
- package/dist/errors/source-location.js +45 -0
- package/dist/execution/executor.js +32 -12
- package/dist/fixtures/defaults/index.js +2 -1
- package/dist/generators/secrets-dts.d.ts +9 -9
- package/dist/generators/secrets-dts.js +12 -12
- package/dist/llm-context/llms-architecture.txt +50 -21
- package/dist/llm-context/llms-cli.txt +2667 -1454
- package/dist/llm-context/llms-features.txt +484 -414
- package/dist/llm-context/llms-full.txt +6703 -4687
- package/dist/llm-context/llms-getting-started.txt +311 -55
- package/dist/llm-context/llms-patterns.txt +17 -29
- package/dist/llm-context/llms-providers.txt +10 -1
- package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
- package/dist/llm-context/llms-sdk.txt +596 -2157
- package/dist/llm-context/llms.txt +31 -17
- package/dist/local-plane/orchestrator-process.d.ts +70 -0
- package/dist/local-plane/orchestrator-process.js +129 -0
- package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
- package/dist/local-plane/os-mismatch-hint.js +34 -0
- package/dist/local-plane/paths.d.ts +39 -0
- package/dist/local-plane/paths.js +54 -0
- package/dist/local-plane/plane-liveness.d.ts +68 -0
- package/dist/local-plane/plane-liveness.js +145 -0
- package/dist/local-plane/plane-manager.d.ts +193 -0
- package/dist/local-plane/plane-manager.js +480 -0
- package/dist/local-plane/plane-seed.d.ts +76 -0
- package/dist/local-plane/plane-seed.js +59 -0
- package/dist/local-plane/plane-trigger.d.ts +71 -0
- package/dist/local-plane/plane-trigger.js +110 -0
- package/dist/local-plane/platform-attach.d.ts +53 -0
- package/dist/local-plane/platform-attach.js +107 -0
- package/dist/local-plane/port-holder.d.ts +78 -0
- package/dist/local-plane/port-holder.js +227 -0
- package/dist/local-plane/postgres.d.ts +56 -0
- package/dist/local-plane/postgres.js +187 -0
- package/dist/local-plane/resolve-plane.d.ts +37 -0
- package/dist/local-plane/resolve-plane.js +76 -0
- package/dist/local-plane/run-follow.d.ts +61 -0
- package/dist/local-plane/run-follow.js +151 -0
- package/dist/local-plane/scaler-config.d.ts +68 -0
- package/dist/local-plane/scaler-config.js +132 -0
- package/dist/local-plane/secret-seed.d.ts +59 -0
- package/dist/local-plane/secret-seed.js +112 -0
- package/dist/local-plane/source-provider.d.ts +43 -0
- package/dist/local-plane/source-provider.js +159 -0
- package/dist/local-plane/trusted-routing.d.ts +32 -0
- package/dist/local-plane/trusted-routing.js +47 -0
- package/dist/lockfile/generator.d.ts +14 -1
- package/dist/lockfile/generator.js +103 -37
- package/dist/lockfile/index.d.ts +3 -1
- package/dist/lockfile/index.js +3 -2
- package/dist/lockfile/purity-diagnostics.d.ts +31 -0
- package/dist/lockfile/purity-diagnostics.js +52 -0
- package/dist/postinstall.js +2 -1
- package/dist/remote/artifact-extract.d.ts +8 -0
- package/dist/remote/artifact-extract.js +58 -0
- package/dist/remote/config.d.ts +21 -3
- package/dist/remote/config.js +24 -5
- package/dist/remote/dashboard-client.d.ts +46 -9
- package/dist/remote/dashboard-client.js +54 -11
- package/dist/remote/fs-case.d.ts +21 -0
- package/dist/remote/fs-case.js +45 -0
- package/dist/remote/history.d.ts +9 -3
- package/dist/remote/history.js +79 -10
- package/dist/remote/local-repo-identity.d.ts +1 -1
- package/dist/remote/local-repo-identity.js +2 -1
- package/dist/remote/notifications-client.d.ts +149 -0
- package/dist/remote/notifications-client.js +103 -0
- package/dist/remote/oauth.d.ts +13 -0
- package/dist/remote/oauth.js +31 -5
- package/dist/remote/output/summary.js +76 -14
- package/dist/remote/platform-client.d.ts +1 -1
- package/dist/remote/render.js +30 -9
- package/dist/{local-executor → remote}/secret-loader.d.ts +1 -1
- package/dist/{local-executor → remote}/secret-loader.js +4 -4
- package/dist/remote/secret-upload.d.ts +1 -1
- package/dist/remote/secret-upload.js +2 -2
- package/dist/remote/uploader.d.ts +1 -1
- package/dist/remote/uploader.js +7 -6
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +8 -8
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +2 -2
- package/dist/templates/package-json.d.ts +15 -0
- package/dist/templates/package-json.js +26 -5
- package/dist/test-runner/dry-run.d.ts +2 -1
- package/dist/test-runner/dry-run.js +18 -1
- package/dist/test-runner/event-types.d.ts +2 -0
- package/dist/test-runner/event-types.js +3 -0
- package/dist/test-runner/job-executor.d.ts +1 -0
- package/dist/test-runner/job-executor.js +82 -44
- package/dist/test-runner/output-formatter.d.ts +5 -2
- package/dist/test-runner/output-formatter.js +6 -4
- package/dist/test-runner/payload-builder.d.ts +7 -1
- package/dist/test-runner/payload-builder.js +11 -2
- package/dist/test-runner/rule-evaluator.d.ts +7 -3
- package/dist/test-runner/rule-evaluator.js +13 -12
- package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
- package/dist/test-runner/step-context.d.ts +23 -5
- package/dist/test-runner/step-context.js +29 -119
- package/dist/types.d.ts +26 -7
- package/dist/types.js +4 -2
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.js +2 -1
- package/dist/validation/typecheck.d.ts +28 -0
- package/dist/validation/typecheck.js +110 -0
- package/dist/validation/validator.d.ts +9 -4
- package/dist/validation/validator.js +38 -44
- package/package.json +13 -13
- package/sbom.spdx.json +9311 -1528
- package/dist/assets/api-TJJVHrjC.json +0 -118
- package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
- package/dist/assets/package-BpQF9kR8.json +0 -74
- package/dist/assets/package-Ceo2h27X.json +0 -89
- package/dist/assets/source_context-D0atuL28.json +0 -20
- package/dist/assets/type-BFqO8SCZ.json +0 -202
- package/dist/commands/cancel.d.ts +0 -22
- package/dist/commands/detect-package-manager.d.ts +0 -42
- package/dist/commands/held-run-resolve.d.ts +0 -50
- package/dist/commands/status.d.ts +0 -34
- package/dist/commands/test.d.ts +0 -88
- package/dist/errors/codes.d.ts +0 -25
- package/dist/local-executor/dag-scheduler.d.ts +0 -44
- package/dist/local-executor/dag-scheduler.js +0 -183
- package/dist/local-executor/index.d.ts +0 -23
- package/dist/local-executor/index.js +0 -378
- package/dist/local-executor/job-runner.d.ts +0 -47
- package/dist/local-executor/job-runner.js +0 -411
- package/dist/local-executor/materializer.d.ts +0 -44
- package/dist/local-executor/materializer.js +0 -132
- package/dist/local-executor/output-streamer.d.ts +0 -31
- package/dist/local-executor/output-streamer.js +0 -168
- package/dist/local-executor/payload-generator.d.ts +0 -16
- package/dist/local-executor/payload-generator.js +0 -138
- package/dist/local-executor/picker.d.ts +0 -33
- package/dist/local-executor/picker.js +0 -109
- package/dist/local-executor/to-event-payload.d.ts +0 -16
- package/dist/local-executor/to-event-payload.js +0 -21
- package/dist/local-executor/types.d.ts +0 -93
- package/dist/local-executor/types.js +0 -2
- package/dist/local-executor/workflow-lock.d.ts +0 -82
- package/dist/local-executor/workflow-lock.js +0 -0
- package/dist/package-F7UXSDHW.json +0 -74
- package/dist/remote/client.d.ts +0 -210
- package/dist/remote/observer.d.ts +0 -81
- package/dist/test-runner/summary.d.ts +0 -7
- /package/dist/{local-executor → test-runner}/runs-on-display.d.ts +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { resolveKiciDir } from "../execution/executor.js";
|
|
3
|
+
import "../execution/index.js";
|
|
4
|
+
import { formatSummary } from "../remote/output/summary.js";
|
|
5
|
+
import { detectOsMismatchHints } from "../local-plane/os-mismatch-hint.js";
|
|
6
|
+
import { IN_PLACE_ROUTING_LABEL, TRUSTED_ROUTING_LABEL } from "../local-plane/scaler-config.js";
|
|
7
|
+
import { planeLogPath } from "../local-plane/plane-manager.js";
|
|
8
|
+
import { ensureLocalSource } from "../local-plane/plane-seed.js";
|
|
9
|
+
import { triggerRun } from "../local-plane/plane-trigger.js";
|
|
10
|
+
import { resolvePlaneForRun } from "../local-plane/resolve-plane.js";
|
|
11
|
+
import { followRun } from "../local-plane/run-follow.js";
|
|
12
|
+
import { seedLocalSecrets } from "../local-plane/secret-seed.js";
|
|
13
|
+
import { resolveWorkdir } from "../local-plane/source-provider.js";
|
|
14
|
+
import { injectRunsOnLabel } from "../local-plane/trusted-routing.js";
|
|
15
|
+
import { compileCommand } from "./compile.js";
|
|
16
|
+
import { withStdoutOnStderr } from "./run.js";
|
|
17
|
+
import { renderRunBanner } from "./run-banner.js";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
import pc from "picocolors";
|
|
21
|
+
import { logger, toErrorMessage } from "@kici-dev/core";
|
|
22
|
+
//#region src/commands/run-routed.ts
|
|
23
|
+
/**
|
|
24
|
+
* `kici run --local [event]` — a real routed dispatch with this machine as the
|
|
25
|
+
* ephemeral agent, through the warm local dev plane. In this phase only the
|
|
26
|
+
* offline (independent) plane is wired; Platform attachment (`--connected` /
|
|
27
|
+
* auto-select hybrid) lands with the attachment work.
|
|
28
|
+
*
|
|
29
|
+
* The offline path reuses the real orchestrator + agent pipeline end-to-end, so
|
|
30
|
+
* the execution parity dividend (emit / cache / needs / parallel / `KICI_SOURCE_*`)
|
|
31
|
+
* holds by construction — there is no second engine.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Entry point for the action-bearing `kici run`. Validates the routing flags and
|
|
35
|
+
* drives the offline routed run. Returns true on run success.
|
|
36
|
+
*/
|
|
37
|
+
async function runRoutedCommand(options) {
|
|
38
|
+
if (options.debug) process.env.KICI_DEBUG = "true";
|
|
39
|
+
if (!options.local) {
|
|
40
|
+
logger.error(pc.red("kici run (connected cloud run) is not yet available. Use `kici run --local` for a routed run on this machine, or `kici run remote <fixture>` for the fixture path."));
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (!options.event) {
|
|
44
|
+
logger.error(pc.red("Provide an event to run, e.g. `kici run --local push`."));
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return runRouted({
|
|
48
|
+
...options,
|
|
49
|
+
event: options.event
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Read a dispatch payload file `{ action?, client_payload? }`. Returns the
|
|
54
|
+
* action + client_payload the routed dispatch trigger carries. An absent
|
|
55
|
+
* `--payload` yields an empty action (a bare `dispatch()` workflow still
|
|
56
|
+
* matches); a present file is parsed as JSON.
|
|
57
|
+
*/
|
|
58
|
+
function readDispatchPayload(payloadPath) {
|
|
59
|
+
if (!payloadPath) return {};
|
|
60
|
+
const raw = JSON.parse(readFileSync(payloadPath, "utf-8"));
|
|
61
|
+
return {
|
|
62
|
+
action: raw.action,
|
|
63
|
+
clientPayload: raw.client_payload
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Drive the full routed run against the warm local plane (offline or attached). */
|
|
67
|
+
async function runRouted(options) {
|
|
68
|
+
const quiet = Boolean(options.quiet);
|
|
69
|
+
const kiciDir = resolveKiciDir(options.kiciDir);
|
|
70
|
+
const repoRoot = path.resolve(kiciDir, "..");
|
|
71
|
+
if (!(quiet ? await withStdoutOnStderr(() => compileCommand({
|
|
72
|
+
kiciDir,
|
|
73
|
+
check: false,
|
|
74
|
+
verbose: options.debug ?? false,
|
|
75
|
+
quiet: true
|
|
76
|
+
})) : await compileCommand({
|
|
77
|
+
kiciDir,
|
|
78
|
+
check: false,
|
|
79
|
+
verbose: options.debug ?? false
|
|
80
|
+
}))) return false;
|
|
81
|
+
if (!quiet) try {
|
|
82
|
+
const os = await import("node:os");
|
|
83
|
+
const lockRaw = readFileSync(path.join(kiciDir, "kici.lock.json"), "utf-8");
|
|
84
|
+
const hints = detectOsMismatchHints(JSON.parse(lockRaw), os.platform(), os.arch());
|
|
85
|
+
for (const hint of hints) logger.warn(pc.yellow(hint));
|
|
86
|
+
} catch {}
|
|
87
|
+
let cleanup;
|
|
88
|
+
const restoreLocks = [];
|
|
89
|
+
try {
|
|
90
|
+
if (!quiet) logger.info(pc.dim("Starting the local dev plane…"));
|
|
91
|
+
const resolved = await resolvePlaneForRun({
|
|
92
|
+
offline: options.offline,
|
|
93
|
+
connected: options.connected
|
|
94
|
+
});
|
|
95
|
+
if ("error" in resolved) {
|
|
96
|
+
logger.error(pc.red(resolved.error));
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const plane = resolved.plane;
|
|
100
|
+
if (!plane.url || !plane.adminToken) {
|
|
101
|
+
logger.error(pc.red("Local dev plane did not report a URL + admin token."));
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const trusted = Boolean(options.trusted);
|
|
105
|
+
if (!quiet) logger.info(renderRunBanner(resolved.kind === "attached" ? {
|
|
106
|
+
mode: "attached",
|
|
107
|
+
planeUrl: plane.url,
|
|
108
|
+
orgId: resolved.orgId,
|
|
109
|
+
trusted
|
|
110
|
+
} : resolved.kind === "fallback" ? {
|
|
111
|
+
mode: "fallback",
|
|
112
|
+
planeUrl: plane.url,
|
|
113
|
+
fallbackReason: resolved.reason,
|
|
114
|
+
trusted
|
|
115
|
+
} : {
|
|
116
|
+
mode: "offline",
|
|
117
|
+
planeUrl: plane.url,
|
|
118
|
+
trusted
|
|
119
|
+
}));
|
|
120
|
+
const workdir = await resolveWorkdir({
|
|
121
|
+
inPlace: Boolean(options.inPlace),
|
|
122
|
+
repoRoot
|
|
123
|
+
});
|
|
124
|
+
cleanup = workdir.cleanup;
|
|
125
|
+
if (trusted) {
|
|
126
|
+
const lockPath = path.join(workdir.dir, ".kici", "kici.lock.json");
|
|
127
|
+
restoreLocks.push(injectRunsOnLabel(lockPath, TRUSTED_ROUTING_LABEL).restore);
|
|
128
|
+
if (options.inPlace) restoreLocks.push(injectRunsOnLabel(lockPath, IN_PLACE_ROUTING_LABEL).restore);
|
|
129
|
+
}
|
|
130
|
+
const seeded = await ensureLocalSource(plane.url, plane.adminToken, {
|
|
131
|
+
repoDir: workdir.dir,
|
|
132
|
+
inPlace: Boolean(options.inPlace)
|
|
133
|
+
});
|
|
134
|
+
const seededSecrets = await seedLocalSecrets(plane.url, plane.adminToken, {
|
|
135
|
+
orgId: seeded.orgId,
|
|
136
|
+
kiciDir
|
|
137
|
+
});
|
|
138
|
+
if (!quiet && seededSecrets.contexts.length > 0) logger.info(pc.dim(`Seeded ${seededSecrets.secretCount} local secret(s) across ${seededSecrets.contexts.length} context(s): ${seededSecrets.contexts.join(", ")}`));
|
|
139
|
+
if (!quiet) logger.info(pc.dim(`Triggering ${options.event} …`));
|
|
140
|
+
const dispatch = options.event === "dispatch" ? readDispatchPayload(options.payload) : void 0;
|
|
141
|
+
const runId = await triggerRun(plane.url, plane.adminToken, {
|
|
142
|
+
orgId: seeded.orgId,
|
|
143
|
+
sourceId: seeded.sourceName,
|
|
144
|
+
repoFullName: ".",
|
|
145
|
+
event: options.event === "dispatch" ? "dispatch" : options.event === "pull_request" ? "pull_request" : "push",
|
|
146
|
+
ref: workdir.ref,
|
|
147
|
+
sha: workdir.sha,
|
|
148
|
+
defaultBranch: workdir.branch,
|
|
149
|
+
...dispatch && {
|
|
150
|
+
action: dispatch.action,
|
|
151
|
+
clientPayload: dispatch.clientPayload
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
if (!quiet) logger.info(pc.green(`Run started: ${runId}`));
|
|
155
|
+
const outcome = await followRun(plane.url, plane.adminToken, runId, {
|
|
156
|
+
quiet,
|
|
157
|
+
onLine: (line) => process.stdout.write(line + "\n"),
|
|
158
|
+
hintLogPath: planeLogPath()
|
|
159
|
+
});
|
|
160
|
+
if (!quiet) {
|
|
161
|
+
const result = {
|
|
162
|
+
fixtureId: options.event,
|
|
163
|
+
runId,
|
|
164
|
+
status: outcome.status === "success" ? "success" : "failed",
|
|
165
|
+
totalDurationMs: 0,
|
|
166
|
+
jobs: outcome.jobs
|
|
167
|
+
};
|
|
168
|
+
process.stdout.write("\n" + formatSummary(result) + "\n");
|
|
169
|
+
}
|
|
170
|
+
return outcome.status === "success";
|
|
171
|
+
} catch (error) {
|
|
172
|
+
logger.error(pc.red(`\nError: ${toErrorMessage(error)}\n`));
|
|
173
|
+
if (options.debug && error instanceof Error && error.stack) logger.error(pc.gray(error.stack));
|
|
174
|
+
return false;
|
|
175
|
+
} finally {
|
|
176
|
+
for (const restore of restoreLocks.reverse()) try {
|
|
177
|
+
restore();
|
|
178
|
+
} catch {}
|
|
179
|
+
if (cleanup) await cleanup().catch(() => void 0);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
export { readDispatchPayload, runRoutedCommand };
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=run-routed.js.map
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import { type HostTargetSelector, type InputsDescriptorMap } from '@kici-dev/engine';
|
|
2
|
-
import type { RunLocalOptions } from '../local-executor/types.js';
|
|
3
2
|
import type { RemoteRunOptions } from './preview.js';
|
|
4
|
-
/**
|
|
5
|
-
* Print a usage block for `kici run local` when the event argument is missing.
|
|
6
|
-
*
|
|
7
|
-
* Mirrors the discoverable help `kici preview` prints for a missing event: a
|
|
8
|
-
* bold synopsis line, a one-line description, and a few concrete event
|
|
9
|
-
* examples. The caller exits non-zero after printing — a missing event is a
|
|
10
|
-
* usage error, unlike `kici preview` which treats no-event as help.
|
|
11
|
-
*/
|
|
12
|
-
export declare function printRunLocalUsage(): void;
|
|
13
3
|
/**
|
|
14
4
|
* Compile `--target` selector strings into a {@link HostTargetSelector}. Each
|
|
15
5
|
* string becomes one AND value (its own include set); repeated values
|
|
@@ -42,13 +32,6 @@ export declare function buildDispatchInputs(pairs: string[], descriptor: InputsD
|
|
|
42
32
|
* stderr regardless.
|
|
43
33
|
*/
|
|
44
34
|
export declare function withStdoutOnStderr<T>(fn: () => Promise<T>): Promise<T>;
|
|
45
|
-
/**
|
|
46
|
-
* Run a workflow locally using the local executor.
|
|
47
|
-
* Thin wrapper that delegates to executeLocal from local-executor.
|
|
48
|
-
*
|
|
49
|
-
* Exit codes: 0 = success, 1 = job failure, 2 = config/compilation error
|
|
50
|
-
*/
|
|
51
|
-
export declare function runLocalCommand(options: RunLocalOptions): Promise<boolean>;
|
|
52
35
|
/**
|
|
53
36
|
* Run fixtures remotely, routed through the Platform.
|
|
54
37
|
*/
|
package/dist/commands/run.js
CHANGED
|
@@ -4,51 +4,38 @@ import "../execution/index.js";
|
|
|
4
4
|
import { loadGlobalConfig } from "../remote/config.js";
|
|
5
5
|
import { RunHistory } from "../remote/history.js";
|
|
6
6
|
import { buildLocalRepoIdentity } from "../remote/local-repo-identity.js";
|
|
7
|
-
import { AccessDeniedError, AmbiguousClusterError, AuthenticationError, ConnectionError, NoClusterError, PlatformRunClient } from "../remote/platform-client.js";
|
|
7
|
+
import { AccessDeniedError, AmbiguousClusterError, AuthenticationError, ConnectionError, NoClusterError, NotFoundError, PlatformRunClient } from "../remote/platform-client.js";
|
|
8
8
|
import { buildEncryptedSecrets } from "../remote/secret-upload.js";
|
|
9
9
|
import { createOverlayTarball, getSizeWarning, uploadTarball } from "../remote/uploader.js";
|
|
10
10
|
import { formatJsonResult } from "../remote/output/json.js";
|
|
11
11
|
import { formatJunitResult } from "../remote/output/junit.js";
|
|
12
12
|
import { formatErrorHighlight, formatMultiFixtureSummary, formatSummary } from "../remote/output/summary.js";
|
|
13
|
-
import { compileCommand } from "./compile.js";
|
|
14
13
|
import { compileFixtures, filterFixtures } from "../fixtures/compiler.js";
|
|
15
14
|
import { describeEvent } from "../fixtures/describe-event.js";
|
|
16
15
|
import { FixturePickerCancelledError, runFixturePicker } from "../fixtures/picker.js";
|
|
17
16
|
import { listHeldRunsForRun, resolveHeldRunContext } from "./held-run-client.js";
|
|
17
|
+
import { compileCommand } from "./compile.js";
|
|
18
18
|
import { handleNewHolds } from "./run-hold-watch.js";
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import pc from "picocolors";
|
|
21
21
|
import { readFile, writeFile } from "node:fs/promises";
|
|
22
22
|
import { formatBytes, logger, toErrorMessage } from "@kici-dev/core";
|
|
23
|
-
import { coerceDispatchInputs, parseInputPairs } from "@kici-dev/engine";
|
|
23
|
+
import { TERMINAL_RUN_STATES, coerceDispatchInputs, parseInputPairs, toCanonicalStatus } from "@kici-dev/engine";
|
|
24
24
|
import { normalizeRunsOnToMatchers } from "@kici-dev/engine/labels/compile";
|
|
25
25
|
import { confirm } from "@inquirer/prompts";
|
|
26
26
|
//#region src/commands/run.ts
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* True when the Platform run-status snapshot reports a terminal run status.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
30
|
+
* Resolves through the engine's canonical vocabulary rather than matching a
|
|
31
|
+
* hand-written literal set: that is what makes the legacy `error` spelling
|
|
32
|
+
* terminal without a second copy of the alias, and what stops a newly-added
|
|
33
|
+
* terminal status leaving `kici run` polling a finished run forever.
|
|
34
34
|
*/
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
logger.info(pc.gray("Examples:"));
|
|
39
|
-
logger.info(pc.gray(" kici run local push"));
|
|
40
|
-
logger.info(pc.gray(" kici run local pr:open"));
|
|
41
|
-
logger.info(pc.gray(" kici run local schedule"));
|
|
42
|
-
logger.info(pc.gray(" kici run local lifecycle:workflow_complete\n"));
|
|
43
|
-
logger.info(pc.gray("Or pick a workflow interactively: kici run local --pick\n"));
|
|
35
|
+
function isTerminalRunStatus(status) {
|
|
36
|
+
const canonical = toCanonicalStatus(status.toLowerCase());
|
|
37
|
+
return canonical !== void 0 && TERMINAL_RUN_STATES.has(canonical);
|
|
44
38
|
}
|
|
45
|
-
/** Terminal run statuses returned by the Platform run-status snapshot. */
|
|
46
|
-
const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
47
|
-
"success",
|
|
48
|
-
"failed",
|
|
49
|
-
"cancelled",
|
|
50
|
-
"error"
|
|
51
|
-
]);
|
|
52
39
|
/**
|
|
53
40
|
* Compile `--target` selector strings into a {@link HostTargetSelector}. Each
|
|
54
41
|
* string becomes one AND value (its own include set); repeated values
|
|
@@ -109,6 +96,19 @@ function buildDispatchInputs(pairs, descriptor) {
|
|
|
109
96
|
/** Interval between status/log polls while a run is active. */
|
|
110
97
|
const POLL_INTERVAL_MS = 750;
|
|
111
98
|
/**
|
|
99
|
+
* How long a freshly triggered run may still read as missing before a 404 is
|
|
100
|
+
* reported as a real error.
|
|
101
|
+
*
|
|
102
|
+
* The trigger call answers with the run id as soon as the orchestrator has
|
|
103
|
+
* created the run, while the Platform's view of that run arrives over the
|
|
104
|
+
* relay a moment later. The first poll therefore fires while the run is not
|
|
105
|
+
* yet readable and legitimately 404s. Within this budget — and only until the
|
|
106
|
+
* first successful read — a 404 means "not visible yet" and the poll retries;
|
|
107
|
+
* after it, or once the run has been read at least once, a 404 is surfaced as
|
|
108
|
+
* a genuinely missing run.
|
|
109
|
+
*/
|
|
110
|
+
const RUN_VISIBILITY_GRACE_MS = 3e4;
|
|
111
|
+
/**
|
|
112
112
|
* Run `fn` with everything written to `process.stdout` redirected to
|
|
113
113
|
* `process.stderr`, then restore the original writer. Compiling a repo
|
|
114
114
|
* evaluates every user workflow / fixture module, and a `console.log` at a
|
|
@@ -127,11 +127,11 @@ async function withStdoutOnStderr(fn) {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
|
-
* Recompile `.kici/workflows` → `kici.lock.json` before a remote run
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
130
|
+
* Recompile `.kici/workflows` → `kici.lock.json` before a remote run. The
|
|
131
|
+
* orchestrator matches triggers and dispatches against the inline lock, so a
|
|
132
|
+
* stale lock would route an edited or newly-added workflow incorrectly. Returns
|
|
133
|
+
* false on a compile/validation error so the caller can abort before any upload
|
|
134
|
+
* or dispatch.
|
|
135
135
|
*/
|
|
136
136
|
async function compileBeforeRemoteRun(options) {
|
|
137
137
|
const pureStdout = Boolean(options.json || options.quiet);
|
|
@@ -144,31 +144,6 @@ async function compileBeforeRemoteRun(options) {
|
|
|
144
144
|
return pureStdout ? withStdoutOnStderr(compile) : compile();
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
|
-
* Run a workflow locally using the local executor.
|
|
148
|
-
* Thin wrapper that delegates to executeLocal from local-executor.
|
|
149
|
-
*
|
|
150
|
-
* Exit codes: 0 = success, 1 = job failure, 2 = config/compilation error
|
|
151
|
-
*/
|
|
152
|
-
async function runLocalCommand(options) {
|
|
153
|
-
if (options.debug) {
|
|
154
|
-
process.env.KICI_DEBUG = "true";
|
|
155
|
-
if (!options.quiet) logger.info(pc.gray("Debug mode enabled"));
|
|
156
|
-
}
|
|
157
|
-
try {
|
|
158
|
-
const { executeLocal } = await import("../local-executor/index.js");
|
|
159
|
-
return await executeLocal(options);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
if (error instanceof Error && (error.message.includes("Cannot find module") || error.message.includes("ERR_MODULE_NOT_FOUND"))) {
|
|
162
|
-
logger.error(pc.red("Local executor not yet available. The local execution engine is under development."));
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
const message = toErrorMessage(error);
|
|
166
|
-
logger.error(pc.red(`\nError: ${message}\n`));
|
|
167
|
-
if (options.debug && error instanceof Error && error.stack) logger.error(pc.gray(error.stack));
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
147
|
* Run fixtures remotely, routed through the Platform.
|
|
173
148
|
*/
|
|
174
149
|
async function runRemoteCommand(fixture, options) {
|
|
@@ -509,16 +484,27 @@ async function pollRunToCompletion(ctx, runId, fixtureId, options) {
|
|
|
509
484
|
let lastStatus = null;
|
|
510
485
|
const seenHolds = /* @__PURE__ */ new Set();
|
|
511
486
|
let heldCtx;
|
|
487
|
+
let runSeen = false;
|
|
512
488
|
while (!cancelled) {
|
|
513
|
-
|
|
489
|
+
let logs;
|
|
490
|
+
try {
|
|
491
|
+
logs = await ctx.client.runLogs(ctx.orgId, runId, cursor, ctx.target);
|
|
492
|
+
lastStatus = await ctx.client.runStatus(ctx.orgId, runId, ctx.target);
|
|
493
|
+
} catch (err) {
|
|
494
|
+
if (err instanceof NotFoundError && !runSeen && Date.now() - startTime < RUN_VISIBILITY_GRACE_MS) {
|
|
495
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
throw err;
|
|
499
|
+
}
|
|
500
|
+
runSeen = true;
|
|
514
501
|
for (const line of logs.lines) {
|
|
515
502
|
if (!options.quiet) process.stdout.write(line + "\n");
|
|
516
503
|
tailLines.push(line);
|
|
517
504
|
while (tailLines.length > MAX_TAIL) tailLines.shift();
|
|
518
505
|
}
|
|
519
506
|
cursor = logs.nextCursor;
|
|
520
|
-
|
|
521
|
-
const terminal = lastStatus.done || TERMINAL_STATUSES.has(lastStatus.status);
|
|
507
|
+
const terminal = lastStatus.done || isTerminalRunStatus(lastStatus.status);
|
|
522
508
|
if (terminal && logs.done) return finishRun(fixtureId, runId, lastStatus, startTime, tailLines, options);
|
|
523
509
|
if (!terminal) await watchRunHolds(runId, seenHolds, () => heldCtx, (c) => heldCtx = c, options);
|
|
524
510
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
@@ -694,6 +680,6 @@ function displayRemoteResults(results) {
|
|
|
694
680
|
logger.info("");
|
|
695
681
|
}
|
|
696
682
|
//#endregion
|
|
697
|
-
export { buildDispatchInputs, buildTargetSelector, lookupDispatchInputsDescriptor,
|
|
683
|
+
export { buildDispatchInputs, buildTargetSelector, lookupDispatchInputsDescriptor, runRemoteCommand, withStdoutOnStderr };
|
|
698
684
|
|
|
699
685
|
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface RunsArtifactsDownloadOptions {
|
|
2
|
+
archive?: boolean;
|
|
3
|
+
output?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Basename an artifact takes under the output directory in the given mode. */
|
|
6
|
+
export declare function destBasename(name: string, archive: boolean): string;
|
|
7
|
+
/**
|
|
8
|
+
* Reduce a basename to the form a case-insensitive filesystem looks it up by.
|
|
9
|
+
*
|
|
10
|
+
* The NFC pass is deliberate defence for a widened artifact-name charset:
|
|
11
|
+
* `ArtifactNameSchema` accepts ASCII only today, so no live name can differ by
|
|
12
|
+
* normalisation — but HFS+ stores NFD and APFS folds case while preserving
|
|
13
|
+
* normalisation, so a non-ASCII name would collide on those volumes without it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function foldBasename(basename: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Group basenames that a case-insensitive filesystem resolves to one path.
|
|
18
|
+
*
|
|
19
|
+
* Groups appear in first-appearance order and each group is sorted; a basename
|
|
20
|
+
* with no partner is not reported.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findCaseCollisions(basenames: string[]): string[][];
|
|
23
|
+
export declare function runsArtifactsDownloadCommand(runId: string, name: string | undefined, options?: RunsArtifactsDownloadOptions): Promise<boolean>;
|
|
24
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import "../../../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { extractArtifactTarball } from "../../../remote/artifact-extract.js";
|
|
3
|
+
import { DashboardClient, DashboardClientError } from "../../../remote/dashboard-client.js";
|
|
4
|
+
import { isCaseInsensitiveDir } from "../../../remote/fs-case.js";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
8
|
+
import { logger, sha256, toErrorMessage } from "@kici-dev/core";
|
|
9
|
+
import { ArtifactNameSchema } from "@kici-dev/engine";
|
|
10
|
+
//#region src/commands/runs/artifacts/download.ts
|
|
11
|
+
/** kici runs artifacts download — fetch one artifact (by name) or every artifact of a run. */
|
|
12
|
+
/** Extension of the raw artifact archive written by `--archive`. */
|
|
13
|
+
const ARCHIVE_EXTENSION = ".tar.gz";
|
|
14
|
+
/** Basename an artifact takes under the output directory in the given mode. */
|
|
15
|
+
function destBasename(name, archive) {
|
|
16
|
+
return archive ? `${name}${ARCHIVE_EXTENSION}` : name;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Reduce a basename to the form a case-insensitive filesystem looks it up by.
|
|
20
|
+
*
|
|
21
|
+
* The NFC pass is deliberate defence for a widened artifact-name charset:
|
|
22
|
+
* `ArtifactNameSchema` accepts ASCII only today, so no live name can differ by
|
|
23
|
+
* normalisation — but HFS+ stores NFD and APFS folds case while preserving
|
|
24
|
+
* normalisation, so a non-ASCII name would collide on those volumes without it.
|
|
25
|
+
*/
|
|
26
|
+
function foldBasename(basename) {
|
|
27
|
+
return basename.normalize("NFC").toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Group basenames that a case-insensitive filesystem resolves to one path.
|
|
31
|
+
*
|
|
32
|
+
* Groups appear in first-appearance order and each group is sorted; a basename
|
|
33
|
+
* with no partner is not reported.
|
|
34
|
+
*/
|
|
35
|
+
function findCaseCollisions(basenames) {
|
|
36
|
+
const byFold = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const basename of basenames) {
|
|
38
|
+
const fold = foldBasename(basename);
|
|
39
|
+
const group = byFold.get(fold);
|
|
40
|
+
if (group) group.add(basename);
|
|
41
|
+
else byFold.set(fold, /* @__PURE__ */ new Set([basename]));
|
|
42
|
+
}
|
|
43
|
+
return [...byFold.values()].filter((g) => g.size > 1).map((g) => [...g].sort());
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fetch a presigned artifact URL into a Buffer and verify its sha-256.
|
|
47
|
+
*
|
|
48
|
+
* NO `Authorization` header: `downloadUrl` is a presigned object-storage GET,
|
|
49
|
+
* and the PAT authenticates to the Platform, not to object storage — attaching
|
|
50
|
+
* it would leak the credential to the storage host. The bytes therefore flow
|
|
51
|
+
* CLI <-> object storage directly and never transit the Platform.
|
|
52
|
+
*/
|
|
53
|
+
async function fetchArtifactBytes(entry) {
|
|
54
|
+
if (!entry.downloadUrl) throw new Error(`artifact "${entry.name}" has no download URL (the stored object is missing or expired)`);
|
|
55
|
+
const res = await fetch(entry.downloadUrl);
|
|
56
|
+
if (!res.ok) throw new Error(`download of "${entry.name}" failed (HTTP ${res.status})`);
|
|
57
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
58
|
+
const actual = sha256(buf);
|
|
59
|
+
if (actual !== entry.sha256) throw new Error(`artifact "${entry.name}" checksum mismatch: expected ${entry.sha256}, got ${actual}`);
|
|
60
|
+
return buf;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Refuse a listed name that is not a valid artifact name.
|
|
64
|
+
*
|
|
65
|
+
* Every name is validated against {@link ArtifactNameSchema} at upload, so a
|
|
66
|
+
* response carrying one outside that contract comes from a broken or hostile
|
|
67
|
+
* peer. The name becomes a path component under the output directory, so it is
|
|
68
|
+
* checked before it is joined — an unchecked `../…` would let a list response
|
|
69
|
+
* write anywhere on the caller's filesystem.
|
|
70
|
+
*/
|
|
71
|
+
function assertPathSafeName(name) {
|
|
72
|
+
if (!ArtifactNameSchema.safeParse(name).success) throw new Error(`listed artifact "${name}" is not a valid artifact name — refusing to write it under the output directory`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Refuse the whole invocation when two targets would land on the same path.
|
|
76
|
+
*
|
|
77
|
+
* Artifact names are case-sensitive, so a run can legitimately hold both
|
|
78
|
+
* `bundle` and `Bundle`. On a filesystem that ignores case in path lookups the
|
|
79
|
+
* two resolve to one path — the second extraction merges into the first's
|
|
80
|
+
* directory, or overwrites its archive under `--archive`. The filesystem is
|
|
81
|
+
* probed only once a fold collision is known to exist, so a case-sensitive
|
|
82
|
+
* output directory downloads both exactly as before. A probe that cannot answer
|
|
83
|
+
* is treated as case-insensitive: the collision is already certain, so the
|
|
84
|
+
* unknown half is resolved towards refusing rather than towards losing data.
|
|
85
|
+
*/
|
|
86
|
+
async function assertNoCaseCollisions(targets, outDir, archive) {
|
|
87
|
+
const groups = findCaseCollisions(targets.map((t) => destBasename(t.name, archive)));
|
|
88
|
+
if (groups.length === 0) return;
|
|
89
|
+
let probeError;
|
|
90
|
+
let caseInsensitive = true;
|
|
91
|
+
try {
|
|
92
|
+
caseInsensitive = await isCaseInsensitiveDir(outDir);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
probeError = err;
|
|
95
|
+
}
|
|
96
|
+
if (!caseInsensitive) return;
|
|
97
|
+
const pairs = groups.map((g) => g.map((basename) => `"${basename}"`).join(" and ")).join("; ");
|
|
98
|
+
const consequence = archive ? "writing both would overwrite one file with the other" : "extracting both would merge them into one directory";
|
|
99
|
+
const reason = probeError !== void 0 ? `the output directory ${outDir} could not be checked for case sensitivity (${toErrorMessage(probeError)})` : `the output directory ${outDir} is on a case-insensitive filesystem`;
|
|
100
|
+
throw new Error(`this run holds artifacts whose names differ only in case: ${pairs} — ${consequence}, and ${reason}. Download them one at a time into separate directories: kici runs artifacts download <run-id> <name> -o <dir>`);
|
|
101
|
+
}
|
|
102
|
+
/** Fetch one entry and write it to `outDir`, extracted or as the raw archive. */
|
|
103
|
+
async function writeOne(entry, outDir, archive) {
|
|
104
|
+
assertPathSafeName(entry.name);
|
|
105
|
+
const buf = await fetchArtifactBytes(entry);
|
|
106
|
+
await mkdir(outDir, { recursive: true });
|
|
107
|
+
const dest = join(outDir, destBasename(entry.name, archive));
|
|
108
|
+
if (archive) {
|
|
109
|
+
await writeFile(dest, buf);
|
|
110
|
+
console.log(pc.green(`✓ ${entry.name} → ${dest}`));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
await extractArtifactTarball(buf, dest);
|
|
114
|
+
console.log(pc.green(`✓ ${entry.name} → ${dest}/`));
|
|
115
|
+
}
|
|
116
|
+
/** Resolve which listed artifacts the invocation targets. */
|
|
117
|
+
function resolveTargets(artifacts, name, runId) {
|
|
118
|
+
if (!name) return artifacts;
|
|
119
|
+
const match = artifacts.find((a) => a.name === name);
|
|
120
|
+
if (match) return [match];
|
|
121
|
+
const names = artifacts.map((a) => a.name).join(", ") || "(none)";
|
|
122
|
+
throw new Error(`artifact "${name}" not found in run ${runId}. Available: ${names}`);
|
|
123
|
+
}
|
|
124
|
+
async function runsArtifactsDownloadCommand(runId, name, options = {}) {
|
|
125
|
+
try {
|
|
126
|
+
const { artifacts } = await (await DashboardClient.load()).listArtifacts(runId);
|
|
127
|
+
const outDir = options.output ?? process.cwd();
|
|
128
|
+
const downloadingAll = !name;
|
|
129
|
+
const targets = resolveTargets(artifacts, name, runId);
|
|
130
|
+
if (targets.length === 0) {
|
|
131
|
+
console.log(pc.gray("No artifacts for this run."));
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
for (const entry of targets) assertPathSafeName(entry.name);
|
|
135
|
+
if (downloadingAll) await assertNoCaseCollisions(targets.filter((t) => t.downloadUrl), outDir, options.archive ?? false);
|
|
136
|
+
let failures = 0;
|
|
137
|
+
for (const entry of targets) {
|
|
138
|
+
if (downloadingAll && !entry.downloadUrl) {
|
|
139
|
+
logger.warn(pc.yellow(`! ${entry.name} skipped — no download URL (object missing or expired)`));
|
|
140
|
+
failures++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
await writeOne(entry, outDir, options.archive ?? false);
|
|
144
|
+
}
|
|
145
|
+
return failures < targets.length;
|
|
146
|
+
} catch (err) {
|
|
147
|
+
logger.error(pc.red(err instanceof DashboardClientError ? err.message : toErrorMessage(err)));
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { destBasename, findCaseCollisions, foldBasename, runsArtifactsDownloadCommand };
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import "../../../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { DashboardClient, DashboardClientError } from "../../../remote/dashboard-client.js";
|
|
3
|
+
import { relativeTime, renderTable } from "../../../remote/render.js";
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
import { formatBytes, logger, toErrorMessage } from "@kici-dev/core";
|
|
6
|
+
//#region src/commands/runs/artifacts/list.ts
|
|
7
|
+
/** kici runs artifacts list — the artifacts a run uploaded (the rows the dashboard Artifacts tab shows). */
|
|
8
|
+
/** Number of leading sha-256 hex chars shown in the table (full digest via --json). */
|
|
9
|
+
const SHA_PREFIX_LENGTH = 12;
|
|
10
|
+
async function runsArtifactsListCommand(runId, options = {}) {
|
|
11
|
+
try {
|
|
12
|
+
const result = await (await DashboardClient.load()).listArtifacts(runId);
|
|
13
|
+
if (options.json) {
|
|
14
|
+
console.log(JSON.stringify(result, null, 2));
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (result.artifacts.length === 0) {
|
|
18
|
+
console.log(pc.gray("No artifacts for this run."));
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
const rows = result.artifacts.map((a) => [
|
|
22
|
+
a.name,
|
|
23
|
+
a.jobId,
|
|
24
|
+
formatBytes(a.sizeBytes),
|
|
25
|
+
a.sha256.slice(0, SHA_PREFIX_LENGTH),
|
|
26
|
+
relativeTime(a.createdAt),
|
|
27
|
+
a.downloadUrl ? "" : pc.yellow("unavailable")
|
|
28
|
+
]);
|
|
29
|
+
console.log(renderTable([
|
|
30
|
+
"name",
|
|
31
|
+
"job",
|
|
32
|
+
"size",
|
|
33
|
+
"sha-256",
|
|
34
|
+
"created",
|
|
35
|
+
""
|
|
36
|
+
], rows));
|
|
37
|
+
console.log(pc.gray(`\n${result.artifacts.length} artifact(s). Download with kici runs artifacts download ${runId} <name>`));
|
|
38
|
+
return true;
|
|
39
|
+
} catch (err) {
|
|
40
|
+
logger.error(pc.red(err instanceof DashboardClientError ? err.message : toErrorMessage(err)));
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { runsArtifactsListCommand };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -18,8 +18,8 @@ async function runsCancelCommand(runId, options = {}) {
|
|
|
18
18
|
return false;
|
|
19
19
|
}
|
|
20
20
|
logger.info(`${options.force ? "Force-cancelling" : "Cancelling"} run ${pc.cyan(runId)}...`);
|
|
21
|
-
const { cancelledJobs = 0 } = await client.cancelRun(runId, options.force ?? false);
|
|
22
|
-
console.log(pc.green(`Run ${runId} cancelled (${cancelledJobs} job${cancelledJobs !== 1 ? "s" : ""} affected).`));
|
|
21
|
+
const { cancelledJobs = 0, alreadyTerminal } = await client.cancelRun(runId, options.force ?? false);
|
|
22
|
+
console.log(alreadyTerminal ? pc.yellow(`Run ${runId} had already finished; nothing was cancelled.`) : pc.green(`Run ${runId} cancelled (${cancelledJobs} job${cancelledJobs !== 1 ? "s" : ""} affected).`));
|
|
23
23
|
return true;
|
|
24
24
|
} catch (err) {
|
|
25
25
|
logger.error(pc.red(err instanceof DashboardClientError ? err.message : toErrorMessage(err)));
|
|
@@ -15,7 +15,13 @@ async function runsListCommand(options = {}) {
|
|
|
15
15
|
return true;
|
|
16
16
|
}
|
|
17
17
|
if (page.runs.length === 0) {
|
|
18
|
-
|
|
18
|
+
const activity = await client.getWebhookActivity().catch(() => null);
|
|
19
|
+
if (activity && activity.received > 0) {
|
|
20
|
+
const win = activity.windowMinutes === 60 ? "the last hour" : `the last ${activity.windowMinutes} minutes`;
|
|
21
|
+
const headline = activity.orchestratorUnavailable || activity.matched === void 0 ? `${activity.received} webhooks received in ${win} but none produced a run` : `${activity.received} webhooks received in ${win}, ${activity.matched} matched`;
|
|
22
|
+
console.log(pc.yellow(`${headline}.`));
|
|
23
|
+
console.log(pc.gray(`Run ${pc.cyan("kici preview push")} to test your triggers locally.`));
|
|
24
|
+
} else console.log(pc.gray("No runs found."));
|
|
19
25
|
return true;
|
|
20
26
|
}
|
|
21
27
|
const rows = page.runs.map((r) => [
|
|
@@ -36,7 +42,7 @@ async function runsListCommand(options = {}) {
|
|
|
36
42
|
"started",
|
|
37
43
|
"duration"
|
|
38
44
|
], rows));
|
|
39
|
-
console.log(pc.gray(`\
|
|
45
|
+
console.log(pc.gray(`\n${page.runs.length} of ~${page.approxTotal} runs${page.nextCursor ? ` (more — use --cursor ${page.nextCursor})` : ""}`));
|
|
40
46
|
return true;
|
|
41
47
|
} catch (err) {
|
|
42
48
|
logger.error(pc.red(err instanceof DashboardClientError ? err.message : toErrorMessage(err)));
|