@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.
Files changed (199) hide show
  1. package/dist/auth/headless-detect.d.ts +14 -5
  2. package/dist/auth/headless-detect.js +50 -9
  3. package/dist/cli-banner.d.ts +6 -3
  4. package/dist/cli-banner.js +13 -5
  5. package/dist/cli.js +180 -48
  6. package/dist/commands/check-mode.d.ts +1 -1
  7. package/dist/commands/compile.js +31 -7
  8. package/dist/commands/diagnostics.js +14 -2
  9. package/dist/commands/doctor.d.ts +64 -0
  10. package/dist/commands/doctor.js +413 -0
  11. package/dist/commands/endpoints.js +6 -0
  12. package/dist/commands/fixture.js +13 -0
  13. package/dist/commands/hook.js +3 -2
  14. package/dist/commands/index.d.ts +13 -1
  15. package/dist/commands/index.js +10 -3
  16. package/dist/commands/init-host-os.d.ts +34 -0
  17. package/dist/commands/init-host-os.js +44 -0
  18. package/dist/commands/init.d.ts +4 -0
  19. package/dist/commands/init.js +160 -37
  20. package/dist/commands/local-trust-root.d.ts +6 -0
  21. package/dist/commands/local-trust-root.js +52 -0
  22. package/dist/commands/local.d.ts +65 -0
  23. package/dist/commands/local.js +206 -0
  24. package/dist/commands/login.d.ts +2 -0
  25. package/dist/commands/login.js +41 -1
  26. package/dist/commands/logout.js +7 -0
  27. package/dist/commands/notifications.d.ts +61 -0
  28. package/dist/commands/notifications.js +274 -0
  29. package/dist/commands/preview.js +9 -2
  30. package/dist/commands/run-banner.d.ts +31 -0
  31. package/dist/commands/run-banner.js +25 -0
  32. package/dist/commands/run-routed.d.ts +53 -0
  33. package/dist/commands/run-routed.js +185 -0
  34. package/dist/commands/run.d.ts +0 -17
  35. package/dist/commands/run.js +44 -58
  36. package/dist/commands/runs/artifacts/download.d.ts +24 -0
  37. package/dist/commands/runs/artifacts/download.js +154 -0
  38. package/dist/commands/runs/artifacts/list.d.ts +5 -0
  39. package/dist/commands/runs/artifacts/list.js +47 -0
  40. package/dist/commands/runs/cancel.js +2 -2
  41. package/dist/commands/runs/list.js +8 -2
  42. package/dist/commands/secrets-list.d.ts +2 -2
  43. package/dist/commands/secrets-list.js +4 -4
  44. package/dist/commands/types.d.ts +3 -3
  45. package/dist/commands/types.js +4 -4
  46. package/dist/commands/verify-attestation.d.ts +3 -1
  47. package/dist/commands/verify-attestation.js +18 -7
  48. package/dist/errors/formatter.d.ts +14 -1
  49. package/dist/errors/formatter.js +13 -3
  50. package/dist/errors/index.d.ts +2 -1
  51. package/dist/errors/index.js +3 -2
  52. package/dist/errors/source-location.d.ts +20 -0
  53. package/dist/errors/source-location.js +45 -0
  54. package/dist/execution/executor.js +32 -12
  55. package/dist/fixtures/defaults/index.js +2 -1
  56. package/dist/generators/secrets-dts.d.ts +9 -9
  57. package/dist/generators/secrets-dts.js +12 -12
  58. package/dist/llm-context/llms-architecture.txt +50 -21
  59. package/dist/llm-context/llms-cli.txt +2667 -1454
  60. package/dist/llm-context/llms-features.txt +484 -414
  61. package/dist/llm-context/llms-full.txt +6703 -4687
  62. package/dist/llm-context/llms-getting-started.txt +311 -55
  63. package/dist/llm-context/llms-patterns.txt +17 -29
  64. package/dist/llm-context/llms-providers.txt +10 -1
  65. package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
  66. package/dist/llm-context/llms-sdk.txt +596 -2157
  67. package/dist/llm-context/llms.txt +31 -17
  68. package/dist/local-plane/orchestrator-process.d.ts +70 -0
  69. package/dist/local-plane/orchestrator-process.js +129 -0
  70. package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
  71. package/dist/local-plane/os-mismatch-hint.js +34 -0
  72. package/dist/local-plane/paths.d.ts +39 -0
  73. package/dist/local-plane/paths.js +54 -0
  74. package/dist/local-plane/plane-liveness.d.ts +68 -0
  75. package/dist/local-plane/plane-liveness.js +145 -0
  76. package/dist/local-plane/plane-manager.d.ts +193 -0
  77. package/dist/local-plane/plane-manager.js +480 -0
  78. package/dist/local-plane/plane-seed.d.ts +76 -0
  79. package/dist/local-plane/plane-seed.js +59 -0
  80. package/dist/local-plane/plane-trigger.d.ts +71 -0
  81. package/dist/local-plane/plane-trigger.js +110 -0
  82. package/dist/local-plane/platform-attach.d.ts +53 -0
  83. package/dist/local-plane/platform-attach.js +107 -0
  84. package/dist/local-plane/port-holder.d.ts +78 -0
  85. package/dist/local-plane/port-holder.js +227 -0
  86. package/dist/local-plane/postgres.d.ts +56 -0
  87. package/dist/local-plane/postgres.js +187 -0
  88. package/dist/local-plane/resolve-plane.d.ts +37 -0
  89. package/dist/local-plane/resolve-plane.js +76 -0
  90. package/dist/local-plane/run-follow.d.ts +61 -0
  91. package/dist/local-plane/run-follow.js +151 -0
  92. package/dist/local-plane/scaler-config.d.ts +68 -0
  93. package/dist/local-plane/scaler-config.js +132 -0
  94. package/dist/local-plane/secret-seed.d.ts +59 -0
  95. package/dist/local-plane/secret-seed.js +112 -0
  96. package/dist/local-plane/source-provider.d.ts +43 -0
  97. package/dist/local-plane/source-provider.js +159 -0
  98. package/dist/local-plane/trusted-routing.d.ts +32 -0
  99. package/dist/local-plane/trusted-routing.js +47 -0
  100. package/dist/lockfile/generator.d.ts +14 -1
  101. package/dist/lockfile/generator.js +103 -37
  102. package/dist/lockfile/index.d.ts +3 -1
  103. package/dist/lockfile/index.js +3 -2
  104. package/dist/lockfile/purity-diagnostics.d.ts +31 -0
  105. package/dist/lockfile/purity-diagnostics.js +52 -0
  106. package/dist/postinstall.js +2 -1
  107. package/dist/remote/artifact-extract.d.ts +8 -0
  108. package/dist/remote/artifact-extract.js +58 -0
  109. package/dist/remote/config.d.ts +21 -3
  110. package/dist/remote/config.js +24 -5
  111. package/dist/remote/dashboard-client.d.ts +46 -9
  112. package/dist/remote/dashboard-client.js +54 -11
  113. package/dist/remote/fs-case.d.ts +21 -0
  114. package/dist/remote/fs-case.js +45 -0
  115. package/dist/remote/history.d.ts +9 -3
  116. package/dist/remote/history.js +79 -10
  117. package/dist/remote/local-repo-identity.d.ts +1 -1
  118. package/dist/remote/local-repo-identity.js +2 -1
  119. package/dist/remote/notifications-client.d.ts +149 -0
  120. package/dist/remote/notifications-client.js +103 -0
  121. package/dist/remote/oauth.d.ts +13 -0
  122. package/dist/remote/oauth.js +31 -5
  123. package/dist/remote/output/summary.js +76 -14
  124. package/dist/remote/platform-client.d.ts +1 -1
  125. package/dist/remote/render.js +30 -9
  126. package/dist/{local-executor → remote}/secret-loader.d.ts +1 -1
  127. package/dist/{local-executor → remote}/secret-loader.js +4 -4
  128. package/dist/remote/secret-upload.d.ts +1 -1
  129. package/dist/remote/secret-upload.js +2 -2
  130. package/dist/remote/uploader.d.ts +1 -1
  131. package/dist/remote/uploader.js +7 -6
  132. package/dist/templates/agents-md.d.ts +1 -1
  133. package/dist/templates/agents-md.js +8 -8
  134. package/dist/templates/index.d.ts +1 -1
  135. package/dist/templates/index.js +2 -2
  136. package/dist/templates/package-json.d.ts +15 -0
  137. package/dist/templates/package-json.js +26 -5
  138. package/dist/test-runner/dry-run.d.ts +2 -1
  139. package/dist/test-runner/dry-run.js +18 -1
  140. package/dist/test-runner/event-types.d.ts +2 -0
  141. package/dist/test-runner/event-types.js +3 -0
  142. package/dist/test-runner/job-executor.d.ts +1 -0
  143. package/dist/test-runner/job-executor.js +82 -44
  144. package/dist/test-runner/output-formatter.d.ts +5 -2
  145. package/dist/test-runner/output-formatter.js +6 -4
  146. package/dist/test-runner/payload-builder.d.ts +7 -1
  147. package/dist/test-runner/payload-builder.js +11 -2
  148. package/dist/test-runner/rule-evaluator.d.ts +7 -3
  149. package/dist/test-runner/rule-evaluator.js +13 -12
  150. package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
  151. package/dist/test-runner/step-context.d.ts +23 -5
  152. package/dist/test-runner/step-context.js +29 -119
  153. package/dist/types.d.ts +26 -7
  154. package/dist/types.js +4 -2
  155. package/dist/validation/index.d.ts +2 -0
  156. package/dist/validation/index.js +2 -1
  157. package/dist/validation/typecheck.d.ts +28 -0
  158. package/dist/validation/typecheck.js +110 -0
  159. package/dist/validation/validator.d.ts +9 -4
  160. package/dist/validation/validator.js +38 -44
  161. package/package.json +13 -13
  162. package/sbom.spdx.json +9311 -1528
  163. package/dist/assets/api-TJJVHrjC.json +0 -118
  164. package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
  165. package/dist/assets/package-BpQF9kR8.json +0 -74
  166. package/dist/assets/package-Ceo2h27X.json +0 -89
  167. package/dist/assets/source_context-D0atuL28.json +0 -20
  168. package/dist/assets/type-BFqO8SCZ.json +0 -202
  169. package/dist/commands/cancel.d.ts +0 -22
  170. package/dist/commands/detect-package-manager.d.ts +0 -42
  171. package/dist/commands/held-run-resolve.d.ts +0 -50
  172. package/dist/commands/status.d.ts +0 -34
  173. package/dist/commands/test.d.ts +0 -88
  174. package/dist/errors/codes.d.ts +0 -25
  175. package/dist/local-executor/dag-scheduler.d.ts +0 -44
  176. package/dist/local-executor/dag-scheduler.js +0 -183
  177. package/dist/local-executor/index.d.ts +0 -23
  178. package/dist/local-executor/index.js +0 -378
  179. package/dist/local-executor/job-runner.d.ts +0 -47
  180. package/dist/local-executor/job-runner.js +0 -411
  181. package/dist/local-executor/materializer.d.ts +0 -44
  182. package/dist/local-executor/materializer.js +0 -132
  183. package/dist/local-executor/output-streamer.d.ts +0 -31
  184. package/dist/local-executor/output-streamer.js +0 -168
  185. package/dist/local-executor/payload-generator.d.ts +0 -16
  186. package/dist/local-executor/payload-generator.js +0 -138
  187. package/dist/local-executor/picker.d.ts +0 -33
  188. package/dist/local-executor/picker.js +0 -109
  189. package/dist/local-executor/to-event-payload.d.ts +0 -16
  190. package/dist/local-executor/to-event-payload.js +0 -21
  191. package/dist/local-executor/types.d.ts +0 -93
  192. package/dist/local-executor/types.js +0 -2
  193. package/dist/local-executor/workflow-lock.d.ts +0 -82
  194. package/dist/local-executor/workflow-lock.js +0 -0
  195. package/dist/package-F7UXSDHW.json +0 -74
  196. package/dist/remote/client.d.ts +0 -210
  197. package/dist/remote/observer.d.ts +0 -81
  198. package/dist/test-runner/summary.d.ts +0 -7
  199. /package/dist/{local-executor → test-runner}/runs-on-display.d.ts +0 -0
@@ -0,0 +1,52 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { analyzePurity } from "./purity-analyzer.js";
3
+ //#region src/lockfile/purity-diagnostics.ts
4
+ /** Which dynamic-value slot on a job carried the impure function. */
5
+ let DynamicValueField = /* @__PURE__ */ function(DynamicValueField) {
6
+ DynamicValueField["Context"] = "context";
7
+ DynamicValueField["Env"] = "env";
8
+ DynamicValueField["ConcurrencyGroup"] = "concurrencyGroup";
9
+ return DynamicValueField;
10
+ }({});
11
+ function pushIfImpure(out, fn, field, base) {
12
+ if (typeof fn !== "function") return;
13
+ const result = analyzePurity(fn.toString());
14
+ if (!result.pure) out.push({
15
+ ...base,
16
+ field,
17
+ reason: result.reason ?? "unknown"
18
+ });
19
+ }
20
+ /**
21
+ * Analyze a single static job's dynamic-value functions (context(s), env,
22
+ * concurrencyGroup) for impurity. Returns one warning per impure function.
23
+ */
24
+ function analyzeJobPurity(job, workflowName, sourceFile) {
25
+ const out = [];
26
+ const base = {
27
+ workflowName,
28
+ sourceFile,
29
+ jobName: job.name
30
+ };
31
+ const contextRefs = job.contexts ?? (job.context !== void 0 ? [job.context] : void 0);
32
+ if (contextRefs) for (const ref of contextRefs) pushIfImpure(out, ref, "context", base);
33
+ pushIfImpure(out, job.env, "env", base);
34
+ pushIfImpure(out, job.concurrencyGroup, "concurrencyGroup", base);
35
+ return out;
36
+ }
37
+ /**
38
+ * Walk every workflow's static jobs and collect impurity warnings. Function-typed
39
+ * jobs (dynamic job generators) are skipped — their purity is analyzed at dispatch.
40
+ */
41
+ function collectWorkflowPurityWarnings(workflows) {
42
+ const out = [];
43
+ for (const { workflow, source } of workflows) for (const job of workflow.jobs) {
44
+ if (typeof job === "function") continue;
45
+ out.push(...analyzeJobPurity(job, workflow.name, source.file));
46
+ }
47
+ return out;
48
+ }
49
+ //#endregion
50
+ export { DynamicValueField, analyzeJobPurity, collectWorkflowPurityWarnings };
51
+
52
+ //# sourceMappingURL=purity-diagnostics.js.map
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { spawn } from "node:child_process";
6
6
  import { confirm } from "@inquirer/prompts";
7
+ import { isCiEnvironment } from "@kici-dev/core/ci-env";
7
8
  //#region src/postinstall.ts
8
9
  /**
9
10
  * Post-install script for @kici-dev/compiler
@@ -33,7 +34,7 @@ async function detectDevelopmentMode() {
33
34
  }
34
35
  async function main() {
35
36
  if (await detectDevelopmentMode()) return;
36
- if (process.env.CI === "true" || !process.stdout.isTTY) return;
37
+ if (isCiEnvironment() || !process.stdout.isTTY) return;
37
38
  const fs = await import("node:fs/promises");
38
39
  try {
39
40
  await fs.access(".kici");
@@ -0,0 +1,8 @@
1
+ /** Sub-directory of the destination that home-anchored entries land under. */
2
+ export declare const HOME_SUBDIR = "~home";
3
+ /**
4
+ * Extract an artifact tarball under `destDir`, relocating the anchor groups so
5
+ * nothing escapes the destination.
6
+ */
7
+ export declare function extractArtifactTarball(tarball: Buffer, destDir: string): Promise<void>;
8
+ //# sourceMappingURL=artifact-extract.d.ts.map
@@ -0,0 +1,58 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { join } from "node:path";
3
+ import { cp, mkdir, readdir } from "node:fs/promises";
4
+ import { HOME_ANCHOR, REPO_ANCHOR } from "@kici-dev/core";
5
+ import { Readable } from "node:stream";
6
+ import { pipeline } from "node:stream/promises";
7
+ import { x } from "tar";
8
+ import { makeTempDir } from "@kici-dev/core/tmp";
9
+ //#region src/remote/artifact-extract.ts
10
+ /**
11
+ * CLI-side artifact extraction.
12
+ *
13
+ * Artifact tarballs are packed by the cache packer, so entries are anchored as
14
+ * `__repo__/<rel>` (repo-root-relative) and `__home__/<rel>` (home-relative).
15
+ * The in-workflow extractor restores those groups to the real repo root / home
16
+ * dir; a CLI must NOT — that would scatter files across the user's filesystem.
17
+ * This helper lands everything under a single destination dir instead:
18
+ *
19
+ * `__repo__/<rel>` -> `<destDir>/<rel>`
20
+ * `__home__/<rel>` -> `<destDir>/~home/<rel>` (kept separate so a
21
+ * home-anchored file cannot clobber a repo-anchored one)
22
+ *
23
+ * Extraction goes through a scratch dir first and only relocates on success, so
24
+ * a failed extraction never leaves a half-written output directory.
25
+ */
26
+ /** Sub-directory of the destination that home-anchored entries land under. */
27
+ const HOME_SUBDIR = "~home";
28
+ /**
29
+ * Extract an artifact tarball under `destDir`, relocating the anchor groups so
30
+ * nothing escapes the destination.
31
+ */
32
+ async function extractArtifactTarball(tarball, destDir) {
33
+ const scratch = await makeTempDir("artifact-extract");
34
+ try {
35
+ await pipeline(Readable.from(tarball), x({
36
+ cwd: scratch.path,
37
+ gzip: true
38
+ }));
39
+ await mkdir(destDir, { recursive: true });
40
+ for (const top of await readdir(scratch.path)) {
41
+ const from = join(scratch.path, top);
42
+ if (top === REPO_ANCHOR) await relocateGroup(from, destDir);
43
+ else if (top === HOME_ANCHOR) await relocateGroup(from, join(destDir, HOME_SUBDIR));
44
+ else await cp(from, join(destDir, top), { recursive: true });
45
+ }
46
+ } finally {
47
+ await scratch.cleanup();
48
+ }
49
+ }
50
+ /** Copy the contents of an anchor group dir into `into`, merging trees. */
51
+ async function relocateGroup(groupDir, into) {
52
+ await mkdir(into, { recursive: true });
53
+ for (const entry of await readdir(groupDir)) await cp(join(groupDir, entry), join(into, entry), { recursive: true });
54
+ }
55
+ //#endregion
56
+ export { HOME_SUBDIR, extractArtifactTarball };
57
+
58
+ //# sourceMappingURL=artifact-extract.js.map
@@ -32,13 +32,31 @@ export interface GlobalConfig {
32
32
  }
33
33
  /**
34
34
  * Returns the global KiCI config directory path.
35
- * Checks KICI_CONFIG_DIR env var first, falls back to ~/.kici.
35
+ *
36
+ * Resolution order:
37
+ * 1. `KICI_CONFIG_DIR`, when set to a non-empty value.
38
+ * 2. A hard refusal when `KICI_TEST_ISOLATION` is present. A developer
39
+ * machine's `~/.kici/config` names a real endpoint and carries a live
40
+ * PAT, so a test process that reads it authenticates against whatever
41
+ * that config points at — in practice, production. Every test that needs
42
+ * a config must name its own isolated directory.
43
+ * 3. `~/.kici`.
44
+ *
45
+ * `KICI_TEST_ISOLATION` is set by this repository's own vitest configs, via
46
+ * `hack/lib/vitest-isolation.ts`. It is deliberately a KiCI-owned name rather
47
+ * than the runner's `VITEST`: `kici` is a compat-protected CLI, and `VITEST`
48
+ * is a third-party marker that propagates into spawned children, so keying off
49
+ * it would break a customer whose vitest test shells out to `kici`.
50
+ *
51
+ * `env` is injectable so this module's own tests can exercise every branch.
52
+ * It is NOT an opt-out: production call sites pass no argument and inherit the
53
+ * guard.
36
54
  */
37
- export declare function getConfigDir(): string;
55
+ export declare function getConfigDir(env?: NodeJS.ProcessEnv): string;
38
56
  /**
39
57
  * Returns the path to the global config file (~/.kici/config).
40
58
  */
41
- export declare function getConfigPath(): string;
59
+ export declare function getConfigPath(env?: NodeJS.ProcessEnv): string;
42
60
  /**
43
61
  * Load the global KiCI config from ~/.kici/config.
44
62
  * Returns an empty object if the file does not exist.
@@ -30,17 +30,36 @@ function sanitizeConfig(raw) {
30
30
  }
31
31
  /**
32
32
  * Returns the global KiCI config directory path.
33
- * Checks KICI_CONFIG_DIR env var first, falls back to ~/.kici.
33
+ *
34
+ * Resolution order:
35
+ * 1. `KICI_CONFIG_DIR`, when set to a non-empty value.
36
+ * 2. A hard refusal when `KICI_TEST_ISOLATION` is present. A developer
37
+ * machine's `~/.kici/config` names a real endpoint and carries a live
38
+ * PAT, so a test process that reads it authenticates against whatever
39
+ * that config points at — in practice, production. Every test that needs
40
+ * a config must name its own isolated directory.
41
+ * 3. `~/.kici`.
42
+ *
43
+ * `KICI_TEST_ISOLATION` is set by this repository's own vitest configs, via
44
+ * `hack/lib/vitest-isolation.ts`. It is deliberately a KiCI-owned name rather
45
+ * than the runner's `VITEST`: `kici` is a compat-protected CLI, and `VITEST`
46
+ * is a third-party marker that propagates into spawned children, so keying off
47
+ * it would break a customer whose vitest test shells out to `kici`.
48
+ *
49
+ * `env` is injectable so this module's own tests can exercise every branch.
50
+ * It is NOT an opt-out: production call sites pass no argument and inherit the
51
+ * guard.
34
52
  */
35
- function getConfigDir() {
36
- if (process.env.KICI_CONFIG_DIR) return process.env.KICI_CONFIG_DIR;
53
+ function getConfigDir(env = process.env) {
54
+ if (env.KICI_CONFIG_DIR) return env.KICI_CONFIG_DIR;
55
+ if (env.KICI_TEST_ISOLATION) throw new Error("Refusing to read the ambient ~/.kici config from a test process. Set KICI_CONFIG_DIR to an isolated directory for this run — the developer machine config may point at production.");
37
56
  return path.join(os.homedir(), ".kici");
38
57
  }
39
58
  /**
40
59
  * Returns the path to the global config file (~/.kici/config).
41
60
  */
42
- function getConfigPath() {
43
- return path.join(getConfigDir(), "config");
61
+ function getConfigPath(env = process.env) {
62
+ return path.join(getConfigDir(env), "config");
44
63
  }
45
64
  /**
46
65
  * Load the global KiCI config from ~/.kici/config.
@@ -4,7 +4,7 @@
4
4
  * only place in the CLI that knows these routes.
5
5
  */
6
6
  import { z } from 'zod';
7
- import { type RunListResponse, type RunListItem, type DiagnosticsInfrastructureResponse, type DiagnosticsSummaryResponse, type DashboardRunDetailApiResponse, type DashboardStepLogsApiResponse, type RegistrationItem } from '@kici-dev/engine';
7
+ import { type ArtifactListItem, type RunListResponse, type RunListItem, type DiagnosticsInfrastructureResponse, type DiagnosticsSummaryResponse, type DashboardRunDetailApiResponse, type DashboardStepLogsApiResponse, type RegistrationItem } from '@kici-dev/engine';
8
8
  import { type GlobalConfig } from './config.js';
9
9
  export type DashboardErrorKind = 'not_logged_in' | 'no_active_org' | 'unauthorized' | 'forbidden' | 'not_found' | 'conflict' | 'cooldown' | 'orchestrator_offline' | 'http';
10
10
  /** Typed error raised by every DashboardClient method on a non-2xx response. */
@@ -22,8 +22,21 @@ export interface RunsListFilters {
22
22
  trigger?: string;
23
23
  source?: string;
24
24
  since?: string;
25
- page?: number;
25
+ /** Opaque keyset cursor (from a prior page's `nextCursor`). Absent = first page. */
26
+ cursor?: string;
26
27
  }
28
+ export declare const webhookActivitySchema: z.ZodObject<{
29
+ windowMinutes: z.ZodNumber;
30
+ received: z.ZodNumber;
31
+ delivered: z.ZodNumber;
32
+ edgeRejected: z.ZodNumber;
33
+ failed: z.ZodNumber;
34
+ matched: z.ZodOptional<z.ZodNumber>;
35
+ unmatched: z.ZodOptional<z.ZodNumber>;
36
+ lockfileMissing: z.ZodOptional<z.ZodNumber>;
37
+ orchestratorUnavailable: z.ZodBoolean;
38
+ }, z.core.$strip>;
39
+ export type WebhookActivity = z.infer<typeof webhookActivitySchema>;
27
40
  /** Result of {@link DashboardClient.listRegistrations}. */
28
41
  export interface RegistrationsListResult {
29
42
  registrations: RegistrationItem[];
@@ -36,18 +49,26 @@ export interface RegistrationsListFilters {
36
49
  repoIdentifier?: string;
37
50
  }
38
51
  /**
39
- * A secret context (environment) as surfaced to the developer CLI's
52
+ * A secret context (context) as surfaced to the developer CLI's
40
53
  * `secrets list` / `types` commands. The orchestrator returns the per-env
41
54
  * secret key names (never values) when `includeSecrets` is requested.
42
55
  */
43
- declare const environmentContextSchema: z.ZodObject<{
56
+ declare const contextContextSchema: z.ZodObject<{
44
57
  name: z.ZodString;
45
58
  enabled: z.ZodBoolean;
46
59
  allowLocalExecution: z.ZodBoolean;
47
60
  secretKeys: z.ZodDefault<z.ZodArray<z.ZodString>>;
48
61
  }, z.core.$strip>;
49
- /** A secret context (environment) returned by {@link DashboardClient.listEnvironments}. */
50
- export type EnvironmentContext = z.infer<typeof environmentContextSchema>;
62
+ /** A secret context (context) returned by {@link DashboardClient.listContexts}. */
63
+ export type ContextContext = z.infer<typeof contextContextSchema>;
64
+ /**
65
+ * Parsed shape of `GET /runs/:runId/artifacts` for the CLI. The per-artifact
66
+ * shape is reused from `@kici-dev/engine` so the CLI and the server cannot drift.
67
+ */
68
+ export interface ArtifactsListResult {
69
+ artifacts: ArtifactListItem[];
70
+ downloadUrlExpiresInSeconds?: number;
71
+ }
51
72
  export declare class DashboardClient {
52
73
  private readonly endpoint;
53
74
  private readonly token;
@@ -62,17 +83,33 @@ export declare class DashboardClient {
62
83
  getJson(path: string): Promise<unknown>;
63
84
  /** Generic POST helper used by typed methods (and tests). */
64
85
  postJson(path: string, body?: unknown): Promise<unknown>;
86
+ /** Generic DELETE helper used by typed methods (and tests). */
87
+ deleteJson(path: string): Promise<unknown>;
65
88
  getDiagnosticsSummary(): Promise<DiagnosticsSummaryResponse>;
66
89
  getInfrastructure(): Promise<DiagnosticsInfrastructureResponse>;
67
90
  listRuns(filters?: RunsListFilters): Promise<RunListResponse>;
68
91
  getRun(runId: string): Promise<RunListItem>;
92
+ /**
93
+ * Fetch the org's recent webhook-activity counts. Returns null on ANY error
94
+ * (older Platform without the route, a network blip, an unexpected shape) so
95
+ * the `kici runs` empty-window hint degrades silently to today's output — the
96
+ * hint is a nicety, never a hard dependency.
97
+ */
98
+ getWebhookActivity(windowMinutes?: number): Promise<WebhookActivity | null>;
69
99
  getRunDetail(runId: string): Promise<DashboardRunDetailApiResponse>;
70
100
  getStepLogs(runId: string, jobId: string, stepIndex: number): Promise<DashboardStepLogsApiResponse>;
101
+ /**
102
+ * List a run's artifacts (`GET /runs/:runId/artifacts`). Each entry carries a
103
+ * presigned `downloadUrl` minted by the customer's orchestrator; the Platform
104
+ * relays it without re-signing, so the artifact bytes never transit Platform.
105
+ */
106
+ listArtifacts(runId: string): Promise<ArtifactsListResult>;
71
107
  rerunRun(runId: string): Promise<{
72
108
  newRunId: string;
73
109
  }>;
74
110
  cancelRun(runId: string, force: boolean): Promise<{
75
111
  cancelledJobs?: number;
112
+ alreadyTerminal?: boolean;
76
113
  }>;
77
114
  cancelByBranch(branch: string): Promise<{
78
115
  cancelledRuns?: number;
@@ -80,13 +117,13 @@ export declare class DashboardClient {
80
117
  /** List the org's permanently registered workflows (GET /registrations). */
81
118
  listRegistrations(filters?: RegistrationsListFilters): Promise<RegistrationsListResult>;
82
119
  /**
83
- * List the org's environments as secret contexts (GET /environments).
120
+ * List the org's contexts as secret contexts (GET /contexts).
84
121
  *
85
- * Pass `includeSecrets` to have the orchestrator attach each environment's
122
+ * Pass `includeSecrets` to have the orchestrator attach each context's
86
123
  * reachable secret key names (never values). Used by `kici secrets list`
87
124
  * and `kici types`.
88
125
  */
89
- listEnvironments(includeSecrets?: boolean): Promise<EnvironmentContext[]>;
126
+ listContexts(includeSecrets?: boolean): Promise<ContextContext[]>;
90
127
  }
91
128
  export {};
92
129
  //# sourceMappingURL=dashboard-client.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import { loadGlobalConfig } from "./config.js";
3
- import { dashboardRunDetailApiResponseSchema, dashboardStepLogsApiResponseSchema, diagnosticsInfrastructureResponseSchema, diagnosticsSummaryResponseSchema, registrationItemSchema, runListItemSchema, runListResponseSchema } from "@kici-dev/engine";
3
+ import { artifactListItemSchema, dashboardRunDetailApiResponseSchema, dashboardStepLogsApiResponseSchema, diagnosticsInfrastructureResponseSchema, diagnosticsSummaryResponseSchema, registrationItemSchema, runListItemSchema, runListResponseSchema } from "@kici-dev/engine";
4
4
  import { z } from "zod";
5
5
  //#region src/remote/dashboard-client.ts
6
6
  /**
@@ -20,8 +20,22 @@ var DashboardClientError = class extends Error {
20
20
  }
21
21
  };
22
22
  const singleRunSchema = z.object({ run: runListItemSchema });
23
+ const webhookActivitySchema = z.object({
24
+ windowMinutes: z.number(),
25
+ received: z.number(),
26
+ delivered: z.number(),
27
+ edgeRejected: z.number(),
28
+ failed: z.number(),
29
+ matched: z.number().optional(),
30
+ unmatched: z.number().optional(),
31
+ lockfileMissing: z.number().optional(),
32
+ orchestratorUnavailable: z.boolean()
33
+ });
23
34
  const rerunResponseSchema = z.object({ newRunId: z.string() });
24
- const cancelResponseSchema = z.object({ cancelledJobs: z.number().optional() });
35
+ const cancelResponseSchema = z.object({
36
+ cancelledJobs: z.number().optional(),
37
+ alreadyTerminal: z.boolean().optional()
38
+ });
25
39
  const cancelByBranchResponseSchema = z.object({ cancelledRuns: z.number().optional() });
26
40
  const registrationsListSchema = z.object({
27
41
  registrations: z.array(registrationItemSchema).default([]),
@@ -29,17 +43,21 @@ const registrationsListSchema = z.object({
29
43
  registryUpdatedAt: z.string()
30
44
  });
31
45
  /**
32
- * A secret context (environment) as surfaced to the developer CLI's
46
+ * A secret context (context) as surfaced to the developer CLI's
33
47
  * `secrets list` / `types` commands. The orchestrator returns the per-env
34
48
  * secret key names (never values) when `includeSecrets` is requested.
35
49
  */
36
- const environmentContextSchema = z.object({
50
+ const contextContextSchema = z.object({
37
51
  name: z.string(),
38
52
  enabled: z.boolean(),
39
53
  allowLocalExecution: z.boolean(),
40
54
  secretKeys: z.array(z.string()).default([])
41
55
  });
42
- const environmentsListSchema = z.object({ environments: z.array(environmentContextSchema).default([]) });
56
+ const contextsListSchema = z.object({ contexts: z.array(contextContextSchema).default([]) });
57
+ const artifactsListResultSchema = z.object({
58
+ artifacts: z.array(artifactListItemSchema),
59
+ downloadUrlExpiresInSeconds: z.number().int().positive().optional()
60
+ });
43
61
  const STATUS_ERROR_MAP = {
44
62
  401: ["unauthorized", "Authentication failed. Run `kici login` to re-authenticate."],
45
63
  403: ["forbidden", "Access denied."],
@@ -105,6 +123,10 @@ var DashboardClient = class DashboardClient {
105
123
  async postJson(path, body) {
106
124
  return this.request("POST", path, body);
107
125
  }
126
+ /** Generic DELETE helper used by typed methods (and tests). */
127
+ async deleteJson(path) {
128
+ return this.request("DELETE", path);
129
+ }
108
130
  async getDiagnosticsSummary() {
109
131
  return diagnosticsSummaryResponseSchema.parse(await this.getJson("/diagnostics"));
110
132
  }
@@ -120,19 +142,40 @@ var DashboardClient = class DashboardClient {
120
142
  if (filters.trigger) qs.set("triggerType", filters.trigger);
121
143
  if (filters.source) qs.set("source", filters.source);
122
144
  if (filters.since) qs.set("since", filters.since);
123
- if (filters.page) qs.set("page", String(filters.page));
145
+ if (filters.cursor) qs.set("cursor", filters.cursor);
124
146
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
125
147
  return runListResponseSchema.parse(await this.getJson(`/runs${suffix}`));
126
148
  }
127
149
  async getRun(runId) {
128
150
  return singleRunSchema.parse(await this.getJson(`/runs/${runId}`)).run;
129
151
  }
152
+ /**
153
+ * Fetch the org's recent webhook-activity counts. Returns null on ANY error
154
+ * (older Platform without the route, a network blip, an unexpected shape) so
155
+ * the `kici runs` empty-window hint degrades silently to today's output — the
156
+ * hint is a nicety, never a hard dependency.
157
+ */
158
+ async getWebhookActivity(windowMinutes = 60) {
159
+ try {
160
+ return webhookActivitySchema.parse(await this.getJson(`/webhook-activity?windowMinutes=${windowMinutes}`));
161
+ } catch {
162
+ return null;
163
+ }
164
+ }
130
165
  async getRunDetail(runId) {
131
166
  return dashboardRunDetailApiResponseSchema.parse(await this.getJson(`/runs/${runId}/detail`));
132
167
  }
133
168
  async getStepLogs(runId, jobId, stepIndex) {
134
169
  return dashboardStepLogsApiResponseSchema.parse(await this.getJson(`/runs/${runId}/jobs/${jobId}/steps/${stepIndex}/logs`));
135
170
  }
171
+ /**
172
+ * List a run's artifacts (`GET /runs/:runId/artifacts`). Each entry carries a
173
+ * presigned `downloadUrl` minted by the customer's orchestrator; the Platform
174
+ * relays it without re-signing, so the artifact bytes never transit Platform.
175
+ */
176
+ async listArtifacts(runId) {
177
+ return artifactsListResultSchema.parse(await this.getJson(`/runs/${runId}/artifacts`));
178
+ }
136
179
  async rerunRun(runId) {
137
180
  return rerunResponseSchema.parse(await this.postJson(`/runs/${runId}/rerun`));
138
181
  }
@@ -151,18 +194,18 @@ var DashboardClient = class DashboardClient {
151
194
  return registrationsListSchema.parse(await this.getJson(`/registrations${suffix}`));
152
195
  }
153
196
  /**
154
- * List the org's environments as secret contexts (GET /environments).
197
+ * List the org's contexts as secret contexts (GET /contexts).
155
198
  *
156
- * Pass `includeSecrets` to have the orchestrator attach each environment's
199
+ * Pass `includeSecrets` to have the orchestrator attach each context's
157
200
  * reachable secret key names (never values). Used by `kici secrets list`
158
201
  * and `kici types`.
159
202
  */
160
- async listEnvironments(includeSecrets = false) {
203
+ async listContexts(includeSecrets = false) {
161
204
  const suffix = includeSecrets ? "?includeSecrets=true" : "";
162
- return environmentsListSchema.parse(await this.getJson(`/environments${suffix}`)).environments;
205
+ return contextsListSchema.parse(await this.getJson(`/contexts${suffix}`)).contexts;
163
206
  }
164
207
  };
165
208
  //#endregion
166
- export { DashboardClient, DashboardClientError };
209
+ export { DashboardClient, DashboardClientError, webhookActivitySchema };
167
210
 
168
211
  //# sourceMappingURL=dashboard-client.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Report whether `dir` lives on a filesystem that ignores case in path lookups.
3
+ *
4
+ * Case sensitivity is a property of the mounted filesystem, not of the platform
5
+ * — a Linux host can mount a case-insensitive volume and macOS can format a
6
+ * case-sensitive one — so it is measured rather than inferred: an empty file is
7
+ * written under a random name and the same name is stat'd upper-cased. The
8
+ * directory is created if it is missing, and the probe file is removed on every
9
+ * path, including a throw.
10
+ *
11
+ * Rejects whenever the probe cannot produce an answer: the probe file cannot be
12
+ * written, or the twin lookup fails for any reason other than the twin's
13
+ * absence. Only `ENOENT` means "case-sensitive" — a permission or I/O error says
14
+ * nothing about case folding, and reporting it as case-sensitive would let a
15
+ * colliding set overwrite itself, so it is surfaced to the caller instead.
16
+ *
17
+ * It is its own module so callers can be tested against both answers with a
18
+ * cross-module spy, rather than against whatever filesystem the test runs on.
19
+ */
20
+ export declare function isCaseInsensitiveDir(dir: string): Promise<boolean>;
21
+ //# sourceMappingURL=fs-case.d.ts.map
@@ -0,0 +1,45 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { join } from "node:path";
3
+ import { mkdir, rm, stat, writeFile } from "node:fs/promises";
4
+ import { randomUUID } from "node:crypto";
5
+ //#region src/remote/fs-case.ts
6
+ /** Filesystem case-sensitivity probe for a target output directory. */
7
+ /**
8
+ * Report whether `dir` lives on a filesystem that ignores case in path lookups.
9
+ *
10
+ * Case sensitivity is a property of the mounted filesystem, not of the platform
11
+ * — a Linux host can mount a case-insensitive volume and macOS can format a
12
+ * case-sensitive one — so it is measured rather than inferred: an empty file is
13
+ * written under a random name and the same name is stat'd upper-cased. The
14
+ * directory is created if it is missing, and the probe file is removed on every
15
+ * path, including a throw.
16
+ *
17
+ * Rejects whenever the probe cannot produce an answer: the probe file cannot be
18
+ * written, or the twin lookup fails for any reason other than the twin's
19
+ * absence. Only `ENOENT` means "case-sensitive" — a permission or I/O error says
20
+ * nothing about case folding, and reporting it as case-sensitive would let a
21
+ * colliding set overwrite itself, so it is surfaced to the caller instead.
22
+ *
23
+ * It is its own module so callers can be tested against both answers with a
24
+ * cross-module spy, rather than against whatever filesystem the test runs on.
25
+ */
26
+ async function isCaseInsensitiveDir(dir) {
27
+ await mkdir(dir, { recursive: true });
28
+ const probe = `.kici-case-probe-${randomUUID()}`;
29
+ const twin = probe.toUpperCase();
30
+ const probePath = join(dir, probe);
31
+ await writeFile(probePath, "");
32
+ try {
33
+ await stat(join(dir, twin));
34
+ return true;
35
+ } catch (err) {
36
+ if (err?.code === "ENOENT") return false;
37
+ throw err;
38
+ } finally {
39
+ await rm(probePath, { force: true });
40
+ }
41
+ }
42
+ //#endregion
43
+ export { isCaseInsensitiveDir };
44
+
45
+ //# sourceMappingURL=fs-case.js.map
@@ -5,8 +5,7 @@
5
5
  * can review past runs (kici run remote --history) and inspect specific runs
6
6
  * (kici status <run-id>).
7
7
  */
8
- /** Status of a test run */
9
- type RunStatus = 'queued' | 'running' | 'success' | 'failed' | 'cancelled';
8
+ import { type CanonicalStatus } from '@kici-dev/engine';
10
9
  /** A single run history entry */
11
10
  export interface HistoryEntry {
12
11
  /** Unique run identifier */
@@ -14,7 +13,7 @@ export interface HistoryEntry {
14
13
  /** Fixture that was run */
15
14
  fixtureId: string;
16
15
  /** Current status of the run */
17
- status: RunStatus;
16
+ status: CanonicalStatus;
18
17
  /** ISO timestamp when the run started */
19
18
  startedAt: string;
20
19
  /** ISO timestamp when the run completed (if finished) */
@@ -87,5 +86,12 @@ export declare class RunHistory {
87
86
  */
88
87
  private save;
89
88
  }
89
+ /** The plain (uncoloured) width of a status cell, for column padding. */
90
+ export declare function statusLabelWidth(status: string): number;
91
+ /**
92
+ * Format a run status with its colour and short label. A status this CLI does
93
+ * not recognise — a history file written by a newer version — renders verbatim.
94
+ */
95
+ export declare function formatStatus(status: string): string;
90
96
  export {};
91
97
  //# sourceMappingURL=history.d.ts.map