@kici-dev/compiler 0.1.16 → 0.1.17

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 (136) hide show
  1. package/dist/auth/headless-detect.js +1 -1
  2. package/dist/cli-banner.js +1 -1
  3. package/dist/cli.js +56 -19
  4. package/dist/commands/approve.js +1 -1
  5. package/dist/commands/compile.js +5 -3
  6. package/dist/commands/diagnostics.d.ts +7 -0
  7. package/dist/commands/diagnostics.js +58 -0
  8. package/dist/commands/docs.js +1 -1
  9. package/dist/commands/drain-worker.js +1 -1
  10. package/dist/commands/endpoints.js +1 -1
  11. package/dist/commands/fixture.js +1 -1
  12. package/dist/commands/held-run-client.js +1 -1
  13. package/dist/commands/held-run-resolve.js +1 -1
  14. package/dist/commands/hook.js +1 -1
  15. package/dist/commands/index.d.ts +16 -4
  16. package/dist/commands/index.js +10 -4
  17. package/dist/commands/init.js +2 -2
  18. package/dist/commands/login.d.ts +0 -2
  19. package/dist/commands/login.js +1 -3
  20. package/dist/commands/logout.js +1 -1
  21. package/dist/commands/orchestrators.d.ts +25 -0
  22. package/dist/commands/orchestrators.js +115 -0
  23. package/dist/commands/org.js +1 -1
  24. package/dist/commands/reject.js +1 -1
  25. package/dist/commands/run.d.ts +2 -9
  26. package/dist/commands/run.js +256 -315
  27. package/dist/commands/runs/cancel.d.ts +6 -0
  28. package/dist/commands/runs/cancel.js +32 -0
  29. package/dist/commands/runs/list.d.ts +6 -0
  30. package/dist/commands/runs/list.js +49 -0
  31. package/dist/commands/runs/logs.d.ts +7 -0
  32. package/dist/commands/runs/logs.js +67 -0
  33. package/dist/commands/runs/rerun.d.ts +5 -0
  34. package/dist/commands/runs/rerun.js +21 -0
  35. package/dist/commands/runs/show.d.ts +5 -0
  36. package/dist/commands/runs/show.js +65 -0
  37. package/dist/commands/secrets-list.d.ts +7 -9
  38. package/dist/commands/secrets-list.js +40 -64
  39. package/dist/commands/test.d.ts +4 -0
  40. package/dist/commands/test.js +1 -1
  41. package/dist/commands/types.d.ts +3 -5
  42. package/dist/commands/types.js +13 -37
  43. package/dist/commands/verify-attestation.d.ts +12 -0
  44. package/dist/commands/verify-attestation.js +69 -0
  45. package/dist/commands/watch.js +1 -1
  46. package/dist/commands/workflows.js +14 -34
  47. package/dist/errors/capability-gap.js +1 -1
  48. package/dist/errors/formatter.js +1 -1
  49. package/dist/errors/index.js +1 -1
  50. package/dist/execution/executor.js +1 -1
  51. package/dist/execution/index.js +1 -1
  52. package/dist/execution/sdk-alias.js +1 -1
  53. package/dist/execution/ts-loader.js +1 -1
  54. package/dist/fixtures/compiler.js +1 -1
  55. package/dist/fixtures/defaults/index.js +1 -1
  56. package/dist/format.js +1 -1
  57. package/dist/generators/secrets-dts.js +2 -2
  58. package/dist/hooks/detector.js +1 -1
  59. package/dist/hooks/index.js +1 -1
  60. package/dist/hooks/installer.js +1 -1
  61. package/dist/hooks/templates.js +1 -1
  62. package/dist/index.js +1 -1
  63. package/dist/llm-context/llms-full.txt +1047 -132
  64. package/dist/llm-context/llms.txt +4 -1
  65. package/dist/local-executor/dag-scheduler.js +1 -1
  66. package/dist/local-executor/index.js +1 -1
  67. package/dist/local-executor/job-runner.js +37 -16
  68. package/dist/local-executor/materializer.js +1 -1
  69. package/dist/local-executor/output-streamer.js +1 -1
  70. package/dist/local-executor/payload-generator.js +1 -1
  71. package/dist/local-executor/picker.js +1 -1
  72. package/dist/local-executor/secret-loader.js +1 -1
  73. package/dist/local-executor/to-event-payload.js +1 -1
  74. package/dist/local-executor/types.js +1 -1
  75. package/dist/local-executor/workflow-lock.js +0 -0
  76. package/dist/lockfile/generator.d.ts +7 -5
  77. package/dist/lockfile/generator.js +21 -18
  78. package/dist/lockfile/hash-files.js +1 -1
  79. package/dist/lockfile/hasher.js +1 -1
  80. package/dist/lockfile/index.js +1 -1
  81. package/dist/lockfile/purity-analyzer.js +1 -1
  82. package/dist/postinstall.js +1 -1
  83. package/dist/provenance-trust-root.d.ts +14 -0
  84. package/dist/provenance-trust-root.js +49 -0
  85. package/dist/remote/config.d.ts +6 -2
  86. package/dist/remote/config.js +7 -4
  87. package/dist/remote/dashboard-client.d.ts +92 -0
  88. package/dist/remote/dashboard-client.js +168 -0
  89. package/dist/remote/encryption.js +3 -4
  90. package/dist/remote/history.js +1 -1
  91. package/dist/remote/oauth.js +1 -1
  92. package/dist/remote/oidc-discovery.js +1 -1
  93. package/dist/remote/output/json.js +1 -1
  94. package/dist/remote/output/junit.js +1 -1
  95. package/dist/remote/output/streaming.js +1 -1
  96. package/dist/remote/output/summary.js +1 -1
  97. package/dist/remote/platform-client.d.ts +142 -0
  98. package/dist/remote/platform-client.js +187 -0
  99. package/dist/remote/prod-defaults.js +1 -1
  100. package/dist/remote/render.d.ts +11 -0
  101. package/dist/remote/render.js +48 -0
  102. package/dist/remote/secret-upload.js +1 -1
  103. package/dist/remote/uploader.d.ts +9 -0
  104. package/dist/remote/uploader.js +51 -4
  105. package/dist/templates/agents-md.d.ts +1 -1
  106. package/dist/templates/agents-md.js +8 -3
  107. package/dist/templates/index.js +1 -1
  108. package/dist/templates/package-json.js +4 -5
  109. package/dist/templates/tsconfig-json.js +1 -1
  110. package/dist/templates/workflows/hello-world.js +2 -2
  111. package/dist/templates/workflows/hello-world.ts +1 -1
  112. package/dist/templates/workflows/pr-checks.js +3 -3
  113. package/dist/templates/workflows/pr-checks.ts +2 -2
  114. package/dist/test-runner/dry-run.js +1 -1
  115. package/dist/test-runner/event-types.js +1 -1
  116. package/dist/test-runner/git-detector.js +1 -1
  117. package/dist/test-runner/index.js +1 -1
  118. package/dist/test-runner/job-executor.js +1 -1
  119. package/dist/test-runner/output-formatter.js +1 -1
  120. package/dist/test-runner/payload-builder.js +1 -1
  121. package/dist/test-runner/rule-evaluator.js +1 -1
  122. package/dist/test-runner/secrets-file.js +1 -1
  123. package/dist/test-runner/step-context.js +10 -6
  124. package/dist/types.d.ts +8 -5
  125. package/dist/types.js +2 -1
  126. package/dist/validation/index.js +1 -1
  127. package/dist/validation/validator.js +1 -1
  128. package/dist/workflows/hello-world.ts +1 -1
  129. package/dist/workflows/pr-checks.ts +2 -2
  130. package/package.json +5 -4
  131. package/sbom.spdx.json +68 -38
  132. package/dist/commands/cancel.js +0 -124
  133. package/dist/commands/status.js +0 -212
  134. package/dist/remote/client.js +0 -203
  135. package/dist/remote/observer.js +0 -174
  136. /package/dist/{chunk-gOLHoazu.js → chunk-BTugEXQM.js} +0 -0
@@ -0,0 +1,168 @@
1
+ import "../chunk-BTugEXQM.js";
2
+ import { loadGlobalConfig } from "./config.js";
3
+ import { dashboardRunDetailApiResponseSchema, dashboardStepLogsApiResponseSchema, diagnosticsInfrastructureResponseSchema, diagnosticsSummaryResponseSchema, registrationItemSchema, runListItemSchema, runListResponseSchema } from "@kici-dev/engine";
4
+ import { z } from "zod";
5
+ //#region src/remote/dashboard-client.ts
6
+ /**
7
+ * Typed client for the Platform org-scoped dashboard API (the endpoints the
8
+ * web UI uses). Authenticated with the stored PAT + active org. This is the
9
+ * only place in the CLI that knows these routes.
10
+ */
11
+ /** Typed error raised by every DashboardClient method on a non-2xx response. */
12
+ var DashboardClientError = class extends Error {
13
+ kind;
14
+ status;
15
+ constructor(kind, message, status) {
16
+ super(message);
17
+ this.kind = kind;
18
+ this.status = status;
19
+ this.name = "DashboardClientError";
20
+ }
21
+ };
22
+ const singleRunSchema = z.object({ run: runListItemSchema });
23
+ const rerunResponseSchema = z.object({ newRunId: z.string() });
24
+ const cancelResponseSchema = z.object({ cancelledJobs: z.number().optional() });
25
+ const cancelByBranchResponseSchema = z.object({ cancelledRuns: z.number().optional() });
26
+ const registrationsListSchema = z.object({
27
+ registrations: z.array(registrationItemSchema).default([]),
28
+ registryVersion: z.number(),
29
+ registryUpdatedAt: z.string()
30
+ });
31
+ /**
32
+ * A secret context (environment) as surfaced to the developer CLI's
33
+ * `secrets list` / `types` commands. The orchestrator returns the per-env
34
+ * secret key names (never values) when `includeSecrets` is requested.
35
+ */
36
+ const environmentContextSchema = z.object({
37
+ name: z.string(),
38
+ enabled: z.boolean(),
39
+ allowLocalExecution: z.boolean(),
40
+ secretKeys: z.array(z.string()).default([])
41
+ });
42
+ const environmentsListSchema = z.object({ environments: z.array(environmentContextSchema).default([]) });
43
+ const STATUS_ERROR_MAP = {
44
+ 401: ["unauthorized", "Authentication failed. Run `kici login` to re-authenticate."],
45
+ 403: ["forbidden", "Access denied."],
46
+ 404: ["not_found", "Not found."],
47
+ 409: ["conflict", "Already in a terminal state."],
48
+ 429: ["cooldown", "Rate limited — wait and retry."],
49
+ 503: ["orchestrator_offline", "Orchestrator offline — try again shortly."],
50
+ 504: ["orchestrator_offline", "Orchestrator timed out — try again shortly."]
51
+ };
52
+ var DashboardClient = class DashboardClient {
53
+ endpoint;
54
+ token;
55
+ orgId;
56
+ constructor(endpoint, token, orgId) {
57
+ this.endpoint = endpoint;
58
+ this.token = token;
59
+ this.orgId = orgId;
60
+ }
61
+ static fromConfig(config) {
62
+ const token = config.pat ?? config.token;
63
+ const endpoint = config.platformEndpoint ?? config.endpoint;
64
+ if (!token) throw new DashboardClientError("not_logged_in", "Not logged in. Run `kici login` first.");
65
+ if (!endpoint) throw new DashboardClientError("not_logged_in", "No endpoint configured. Run `kici login` first.");
66
+ if (!config.activeOrgId) throw new DashboardClientError("no_active_org", "No active organization. Run `kici org use <name>` to set one.");
67
+ return new DashboardClient(endpoint.replace(/\/$/, ""), token, config.activeOrgId);
68
+ }
69
+ static async load() {
70
+ return DashboardClient.fromConfig(await loadGlobalConfig());
71
+ }
72
+ orgUrl(path) {
73
+ return `${this.endpoint}/api/v1/orgs/${this.orgId}/${path.replace(/^\//, "")}`;
74
+ }
75
+ async request(method, path, body) {
76
+ const res = await fetch(this.orgUrl(path), {
77
+ method,
78
+ headers: {
79
+ "Content-Type": "application/json",
80
+ Authorization: `Bearer ${this.token}`
81
+ },
82
+ body: body === void 0 ? void 0 : JSON.stringify(body)
83
+ }).catch((err) => {
84
+ throw new DashboardClientError("http", `Network error: ${String(err)}`);
85
+ });
86
+ if (res.ok) {
87
+ const text = await res.text();
88
+ return text ? JSON.parse(text) : {};
89
+ }
90
+ return this.throwForStatus(res);
91
+ }
92
+ async throwForStatus(res) {
93
+ let detail;
94
+ try {
95
+ detail = (await res.json()).error;
96
+ } catch {}
97
+ const [kind, fallback] = STATUS_ERROR_MAP[res.status] ?? ["http", `Request failed (${res.status}).`];
98
+ throw new DashboardClientError(kind, detail ?? fallback, res.status);
99
+ }
100
+ /** Generic GET helper used by typed methods (and tests). */
101
+ async getJson(path) {
102
+ return this.request("GET", path);
103
+ }
104
+ /** Generic POST helper used by typed methods (and tests). */
105
+ async postJson(path, body) {
106
+ return this.request("POST", path, body);
107
+ }
108
+ async getDiagnosticsSummary() {
109
+ return diagnosticsSummaryResponseSchema.parse(await this.getJson("/diagnostics"));
110
+ }
111
+ async getInfrastructure() {
112
+ return diagnosticsInfrastructureResponseSchema.parse(await this.getJson("/diagnostics/infrastructure"));
113
+ }
114
+ async listRuns(filters = {}) {
115
+ const qs = new URLSearchParams();
116
+ if (filters.status) qs.set("status", filters.status);
117
+ if (filters.workflow) qs.set("workflow", filters.workflow);
118
+ if (filters.branch) qs.set("branch", filters.branch);
119
+ if (filters.repo) qs.set("repository", filters.repo);
120
+ if (filters.trigger) qs.set("triggerType", filters.trigger);
121
+ if (filters.source) qs.set("source", filters.source);
122
+ if (filters.since) qs.set("since", filters.since);
123
+ if (filters.page) qs.set("page", String(filters.page));
124
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
125
+ return runListResponseSchema.parse(await this.getJson(`/runs${suffix}`));
126
+ }
127
+ async getRun(runId) {
128
+ return singleRunSchema.parse(await this.getJson(`/runs/${runId}`)).run;
129
+ }
130
+ async getRunDetail(runId) {
131
+ return dashboardRunDetailApiResponseSchema.parse(await this.getJson(`/runs/${runId}/detail`));
132
+ }
133
+ async getStepLogs(runId, jobId, stepIndex) {
134
+ return dashboardStepLogsApiResponseSchema.parse(await this.getJson(`/runs/${runId}/jobs/${jobId}/steps/${stepIndex}/logs`));
135
+ }
136
+ async rerunRun(runId) {
137
+ return rerunResponseSchema.parse(await this.postJson(`/runs/${runId}/rerun`));
138
+ }
139
+ async cancelRun(runId, force) {
140
+ return cancelResponseSchema.parse(await this.postJson(`/runs/${runId}/cancel`, { force }));
141
+ }
142
+ async cancelByBranch(branch) {
143
+ return cancelByBranchResponseSchema.parse(await this.postJson("/runs/cancel-by-branch", { branch }));
144
+ }
145
+ /** List the org's permanently registered workflows (GET /registrations). */
146
+ async listRegistrations(filters = {}) {
147
+ const qs = new URLSearchParams();
148
+ if (filters.triggerType) qs.set("triggerType", filters.triggerType);
149
+ if (filters.repoIdentifier) qs.set("repoIdentifier", filters.repoIdentifier);
150
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
151
+ return registrationsListSchema.parse(await this.getJson(`/registrations${suffix}`));
152
+ }
153
+ /**
154
+ * List the org's environments as secret contexts (GET /environments).
155
+ *
156
+ * Pass `includeSecrets` to have the orchestrator attach each environment's
157
+ * reachable secret key names (never values). Used by `kici secrets list`
158
+ * and `kici types`.
159
+ */
160
+ async listEnvironments(includeSecrets = false) {
161
+ const suffix = includeSecrets ? "?includeSecrets=true" : "";
162
+ return environmentsListSchema.parse(await this.getJson(`/environments${suffix}`)).environments;
163
+ }
164
+ };
165
+ //#endregion
166
+ export { DashboardClient, DashboardClientError };
167
+
168
+ //# sourceMappingURL=dashboard-client.js.map
@@ -1,4 +1,4 @@
1
- import "../chunk-gOLHoazu.js";
1
+ import "../chunk-BTugEXQM.js";
2
2
  import fs from "node:fs/promises";
3
3
  import { deriveSharedSecret } from "@kici-dev/core";
4
4
  import crypto from "node:crypto";
@@ -11,7 +11,6 @@ import crypto from "node:crypto";
11
11
  * Same pattern as engine/src/secrets/crypto.ts.
12
12
  */
13
13
  const IV_LENGTH = 12;
14
- const AUTH_TAG_LENGTH = 16;
15
14
  /**
16
15
  * Generate an ephemeral X25519 keypair.
17
16
  * Keys are exported as DER buffers for compact wire format.
@@ -72,8 +71,8 @@ async function decryptTarball(encryptedPath, cliPublicKey, orchestratorPrivateKe
72
71
  const aesKey = deriveSharedSecret(orchestratorPrivateKey, cliPublicKey);
73
72
  const encrypted = await fs.readFile(encryptedPath);
74
73
  const iv = encrypted.subarray(0, IV_LENGTH);
75
- const authTag = encrypted.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
76
- const ciphertext = encrypted.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
74
+ const authTag = encrypted.subarray(IV_LENGTH, 28);
75
+ const ciphertext = encrypted.subarray(28);
77
76
  const decipher = crypto.createDecipheriv("aes-256-gcm", aesKey, iv);
78
77
  decipher.setAuthTag(authTag);
79
78
  const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
@@ -1,4 +1,4 @@
1
- import "../chunk-gOLHoazu.js";
1
+ import "../chunk-BTugEXQM.js";
2
2
  import { formatRelativeTime } from "../format.js";
3
3
  import { getConfigDir } from "./config.js";
4
4
  import path from "node:path";
@@ -1,4 +1,4 @@
1
- import "../chunk-gOLHoazu.js";
1
+ import "../chunk-BTugEXQM.js";
2
2
  import { discoverOidcEndpoints } from "./oidc-discovery.js";
3
3
  import pc from "picocolors";
4
4
  import { toErrorMessage } from "@kici-dev/core";
@@ -1,4 +1,4 @@
1
- import "../chunk-gOLHoazu.js";
1
+ import "../chunk-BTugEXQM.js";
2
2
  import { toErrorMessage } from "@kici-dev/core";
3
3
  //#region src/remote/oidc-discovery.ts
4
4
  const cache = /* @__PURE__ */ new Map();
@@ -1,4 +1,4 @@
1
- import "../../chunk-gOLHoazu.js";
1
+ import "../../chunk-BTugEXQM.js";
2
2
  //#region src/remote/output/json.ts
3
3
  /**
4
4
  * Format run results as structured JSON with 2-space indentation.
@@ -1,4 +1,4 @@
1
- import "../../chunk-gOLHoazu.js";
1
+ import "../../chunk-BTugEXQM.js";
2
2
  //#region src/remote/output/junit.ts
3
3
  /**
4
4
  * Format run results as JUnit XML following the standard schema.
@@ -1,4 +1,4 @@
1
- import "../../chunk-gOLHoazu.js";
1
+ import "../../chunk-BTugEXQM.js";
2
2
  import pc from "picocolors";
3
3
  import { formatDuration } from "@kici-dev/core";
4
4
  //#region src/remote/output/streaming.ts
@@ -1,4 +1,4 @@
1
- import "../../chunk-gOLHoazu.js";
1
+ import "../../chunk-BTugEXQM.js";
2
2
  import pc from "picocolors";
3
3
  import { formatDuration } from "@kici-dev/core";
4
4
  //#region src/remote/output/summary.ts
@@ -0,0 +1,142 @@
1
+ /**
2
+ * PAT-authenticated client for the Platform's dev-facing run-remote routes.
3
+ *
4
+ * This is the developer CLI's contact point for `kici run remote` and its
5
+ * companion commands. It targets the Platform (`config.platformEndpoint`), which
6
+ * relays each control request over the WS dashboard-proxy to the org's
7
+ * orchestrator cluster — the orchestrator's own HTTP API is never reached
8
+ * directly (it may live on a hidden network).
9
+ *
10
+ * The overlay tarball does NOT flow through here: `initUpload` returns an
11
+ * external presigned URL the CLI PUTs to directly (data plane).
12
+ */
13
+ export declare class AuthenticationError extends Error {
14
+ constructor(message?: string);
15
+ }
16
+ export declare class AccessDeniedError extends Error {
17
+ constructor(message?: string);
18
+ }
19
+ export declare class NotFoundError extends Error {
20
+ constructor(message?: string);
21
+ }
22
+ export declare class ConnectionError extends Error {
23
+ endpoint?: string | undefined;
24
+ constructor(message: string, endpoint?: string | undefined);
25
+ }
26
+ /**
27
+ * Thrown when the org has more than one connected orchestrator cluster and the
28
+ * developer did not pick one (no `--orchestrator` flag, no per-org default).
29
+ * Carries the list of connected cluster names so the CLI can print the choice.
30
+ */
31
+ export declare class AmbiguousClusterError extends Error {
32
+ clusters: string[];
33
+ constructor(clusters: string[], message?: string);
34
+ }
35
+ /** Thrown when the org has no connected orchestrator cluster. */
36
+ export declare class NoClusterError extends Error {
37
+ constructor(message?: string);
38
+ }
39
+ export interface PlatformUploadInitInput {
40
+ sha?: string;
41
+ fileCount?: number;
42
+ compressedSize?: number;
43
+ }
44
+ export interface PlatformUploadInitResponse {
45
+ uploadId: string;
46
+ signedUrl: string;
47
+ publicKey: string;
48
+ expiresIn: number;
49
+ }
50
+ export interface PlatformTriggerInput {
51
+ fixtureId: string;
52
+ event: {
53
+ type: string;
54
+ action?: string;
55
+ targetBranch: string;
56
+ sourceBranch?: string;
57
+ payload: Record<string, unknown>;
58
+ changedFiles?: string[];
59
+ };
60
+ workflowName?: string;
61
+ uploadId?: string;
62
+ /**
63
+ * Ephemeral X25519 public key the CLI used to encrypt the overlay tarball.
64
+ * The orchestrator pairs it with the upload's stored private key to decrypt
65
+ * the overlay. Distinct from `encryptedSecretsKey` (the optional secrets key).
66
+ */
67
+ cliPublicKey?: string;
68
+ inlineLockFile?: string;
69
+ fullRepo?: boolean;
70
+ secrets?: Record<string, string>;
71
+ encryptedSecrets?: string;
72
+ encryptedSecretsKey?: string;
73
+ }
74
+ export interface PlatformTriggerResponse {
75
+ runId: string;
76
+ status: 'accepted' | 'rejected';
77
+ reason?: string;
78
+ jobIds?: string[];
79
+ }
80
+ export interface PlatformRunStatusResponse {
81
+ runId: string;
82
+ status: string;
83
+ jobs: Array<{
84
+ jobId: string;
85
+ jobName: string;
86
+ status: string;
87
+ exitCode?: number | null;
88
+ errorMessage?: string | null;
89
+ }>;
90
+ done: boolean;
91
+ }
92
+ export interface PlatformRunLogsResponse {
93
+ lines: string[];
94
+ nextCursor: number;
95
+ done: boolean;
96
+ }
97
+ export interface PlatformCancelResponse {
98
+ cancelled: boolean;
99
+ }
100
+ interface PlatformRunClientConfig {
101
+ /** Platform base URL, e.g. `https://api.kici.dev`. */
102
+ platformEndpoint: string;
103
+ /** Personal access token (PAT) used as the bearer credential. */
104
+ token: string;
105
+ }
106
+ /** Cluster targeting for a run-remote request. */
107
+ export interface ClusterTarget {
108
+ /** Explicit `--orchestrator <name>` pick. */
109
+ orchestrator?: string;
110
+ /** Per-org default cluster (`config.defaultClusters[orgId]`). */
111
+ defaultCluster?: string;
112
+ }
113
+ /**
114
+ * Client for the Platform's `/api/v1/orgs/:orgId/test/*` relay routes.
115
+ */
116
+ export declare class PlatformRunClient {
117
+ private readonly baseUrl;
118
+ private readonly token;
119
+ constructor(config: PlatformRunClientConfig);
120
+ /** POST /test/uploads/init — mint an external presigned upload URL. */
121
+ initUpload(orgId: string, target: ClusterTarget, body: PlatformUploadInitInput): Promise<PlatformUploadInitResponse>;
122
+ /** POST /test/trigger — trigger a remote run. */
123
+ trigger(orgId: string, target: ClusterTarget, body: PlatformTriggerInput): Promise<PlatformTriggerResponse>;
124
+ /** GET /test/runs/:runId — run-status snapshot. */
125
+ runStatus(orgId: string, runId: string, target?: ClusterTarget): Promise<PlatformRunStatusResponse>;
126
+ /** GET /test/runs/:runId/logs?cursor=<n> — next log chunk from a cursor. */
127
+ runLogs(orgId: string, runId: string, cursor: number, target?: ClusterTarget): Promise<PlatformRunLogsResponse>;
128
+ /** POST /test/runs/:runId/cancel — cancel a run. */
129
+ cancel(orgId: string, runId: string, target?: ClusterTarget): Promise<PlatformCancelResponse>;
130
+ /**
131
+ * Make a PAT-authenticated request to the Platform.
132
+ *
133
+ * Classifies errors: 401 → AuthenticationError, 403 → AccessDeniedError,
134
+ * 404 → NotFoundError, 422 ambiguous_cluster → AmbiguousClusterError,
135
+ * 422 no_cluster → NoClusterError. Network failures → ConnectionError.
136
+ */
137
+ private request;
138
+ /** Map a non-OK Platform response to a typed error. Always throws. */
139
+ private throwForStatus;
140
+ }
141
+ export {};
142
+ //# sourceMappingURL=platform-client.d.ts.map
@@ -0,0 +1,187 @@
1
+ import "../chunk-BTugEXQM.js";
2
+ import { toErrorMessage } from "@kici-dev/core";
3
+ //#region src/remote/platform-client.ts
4
+ /**
5
+ * PAT-authenticated client for the Platform's dev-facing run-remote routes.
6
+ *
7
+ * This is the developer CLI's contact point for `kici run remote` and its
8
+ * companion commands. It targets the Platform (`config.platformEndpoint`), which
9
+ * relays each control request over the WS dashboard-proxy to the org's
10
+ * orchestrator cluster — the orchestrator's own HTTP API is never reached
11
+ * directly (it may live on a hidden network).
12
+ *
13
+ * The overlay tarball does NOT flow through here: `initUpload` returns an
14
+ * external presigned URL the CLI PUTs to directly (data plane).
15
+ */
16
+ var AuthenticationError = class extends Error {
17
+ constructor(message = "Invalid or expired token. Run `kici login` to authenticate.") {
18
+ super(message);
19
+ this.name = "AuthenticationError";
20
+ }
21
+ };
22
+ var AccessDeniedError = class extends Error {
23
+ constructor(message = "Access denied") {
24
+ super(message);
25
+ this.name = "AccessDeniedError";
26
+ }
27
+ };
28
+ var NotFoundError = class extends Error {
29
+ constructor(message = "Resource not found") {
30
+ super(message);
31
+ this.name = "NotFoundError";
32
+ }
33
+ };
34
+ var ConnectionError = class extends Error {
35
+ endpoint;
36
+ constructor(message, endpoint) {
37
+ super(message);
38
+ this.endpoint = endpoint;
39
+ this.name = "ConnectionError";
40
+ }
41
+ };
42
+ /**
43
+ * Thrown when the org has more than one connected orchestrator cluster and the
44
+ * developer did not pick one (no `--orchestrator` flag, no per-org default).
45
+ * Carries the list of connected cluster names so the CLI can print the choice.
46
+ */
47
+ var AmbiguousClusterError = class extends Error {
48
+ clusters;
49
+ constructor(clusters, message = "Multiple orchestrator clusters are connected — pass --orchestrator <name>") {
50
+ super(message);
51
+ this.clusters = clusters;
52
+ this.name = "AmbiguousClusterError";
53
+ }
54
+ };
55
+ /** Thrown when the org has no connected orchestrator cluster. */
56
+ var NoClusterError = class extends Error {
57
+ constructor(message = "No orchestrator is connected for this organization.") {
58
+ super(message);
59
+ this.name = "NoClusterError";
60
+ }
61
+ };
62
+ /**
63
+ * Append the cluster-targeting query params the Platform routes understand:
64
+ * `?orchestrator=<cluster>` (explicit pick) and `?defaultCluster=<cluster>`
65
+ * (the CLI's per-org default). Either may be omitted to let the Platform
66
+ * sole-select or return a 422 with the cluster list.
67
+ */
68
+ function clusterQuery(orchestrator, defaultCluster) {
69
+ const params = new URLSearchParams();
70
+ if (orchestrator) params.set("orchestrator", orchestrator);
71
+ if (defaultCluster) params.set("defaultCluster", defaultCluster);
72
+ const q = params.toString();
73
+ return q ? `?${q}` : "";
74
+ }
75
+ /**
76
+ * Client for the Platform's `/api/v1/orgs/:orgId/test/*` relay routes.
77
+ */
78
+ var PlatformRunClient = class {
79
+ baseUrl;
80
+ token;
81
+ constructor(config) {
82
+ this.baseUrl = config.platformEndpoint.replace(/\/+$/, "");
83
+ this.token = config.token;
84
+ }
85
+ /** POST /test/uploads/init — mint an external presigned upload URL. */
86
+ async initUpload(orgId, target, body) {
87
+ const path = `/api/v1/orgs/${orgId}/test/uploads/init${clusterQuery(target.orchestrator, target.defaultCluster)}`;
88
+ return await (await this.request(path, {
89
+ method: "POST",
90
+ body: JSON.stringify(body)
91
+ })).json();
92
+ }
93
+ /** POST /test/trigger — trigger a remote run. */
94
+ async trigger(orgId, target, body) {
95
+ const path = `/api/v1/orgs/${orgId}/test/trigger${clusterQuery(target.orchestrator, target.defaultCluster)}`;
96
+ return await (await this.request(path, {
97
+ method: "POST",
98
+ body: JSON.stringify(body)
99
+ })).json();
100
+ }
101
+ /** GET /test/runs/:runId — run-status snapshot. */
102
+ async runStatus(orgId, runId, target = {}) {
103
+ const path = `/api/v1/orgs/${orgId}/test/runs/${runId}${clusterQuery(target.orchestrator, target.defaultCluster)}`;
104
+ return await (await this.request(path, { method: "GET" })).json();
105
+ }
106
+ /** GET /test/runs/:runId/logs?cursor=<n> — next log chunk from a cursor. */
107
+ async runLogs(orgId, runId, cursor, target = {}) {
108
+ const params = new URLSearchParams();
109
+ params.set("cursor", String(cursor));
110
+ if (target.orchestrator) params.set("orchestrator", target.orchestrator);
111
+ if (target.defaultCluster) params.set("defaultCluster", target.defaultCluster);
112
+ const path = `/api/v1/orgs/${orgId}/test/runs/${runId}/logs?${params.toString()}`;
113
+ return await (await this.request(path, { method: "GET" })).json();
114
+ }
115
+ /** POST /test/runs/:runId/cancel — cancel a run. */
116
+ async cancel(orgId, runId, target = {}) {
117
+ const path = `/api/v1/orgs/${orgId}/test/runs/${runId}/cancel${clusterQuery(target.orchestrator, target.defaultCluster)}`;
118
+ return await (await this.request(path, {
119
+ method: "POST",
120
+ body: JSON.stringify({})
121
+ })).json();
122
+ }
123
+ /**
124
+ * Make a PAT-authenticated request to the Platform.
125
+ *
126
+ * Classifies errors: 401 → AuthenticationError, 403 → AccessDeniedError,
127
+ * 404 → NotFoundError, 422 ambiguous_cluster → AmbiguousClusterError,
128
+ * 422 no_cluster → NoClusterError. Network failures → ConnectionError.
129
+ */
130
+ async request(path, init) {
131
+ const url = `${this.baseUrl}${path}`;
132
+ let response;
133
+ try {
134
+ response = await fetch(url, {
135
+ ...init,
136
+ headers: {
137
+ "Content-Type": "application/json",
138
+ Authorization: `Bearer ${this.token}`,
139
+ ...init.headers
140
+ }
141
+ });
142
+ } catch (err) {
143
+ throw new ConnectionError(`Failed to connect to the Platform at ${this.baseUrl}: ${toErrorMessage(err)}`, this.baseUrl);
144
+ }
145
+ if (response.ok) return response;
146
+ await this.throwForStatus(response);
147
+ return response;
148
+ }
149
+ /** Map a non-OK Platform response to a typed error. Always throws. */
150
+ async throwForStatus(response) {
151
+ if (response.status === 401) throw new AuthenticationError();
152
+ if (response.status === 403) throw new AccessDeniedError(await readError(response, "Access denied"));
153
+ if (response.status === 404) throw new NotFoundError(await readError(response, "Resource not found"));
154
+ if (response.status === 422) {
155
+ const body = await readJson(response);
156
+ if (body.error === "ambiguous_cluster") throw new AmbiguousClusterError(body.clusters ?? [], body.message);
157
+ if (body.error === "no_cluster") throw new NoClusterError(body.message);
158
+ throw new Error(body.reason ?? body.message ?? body.error ?? "Request rejected (422)");
159
+ }
160
+ throw new Error(`Request failed with status ${response.status}: ${await readError(response, "")}`);
161
+ }
162
+ };
163
+ /** Read a JSON body, returning `{}` on any parse failure. */
164
+ async function readJson(response) {
165
+ try {
166
+ return await response.json();
167
+ } catch {
168
+ return {};
169
+ }
170
+ }
171
+ /** Read the `error` field of a JSON body, falling back to text then a default. */
172
+ async function readError(response, fallback) {
173
+ try {
174
+ const body = await response.clone().json();
175
+ if (body.error) return body.error;
176
+ if (body.message) return body.message;
177
+ } catch {}
178
+ try {
179
+ const text = await response.text();
180
+ if (text) return text;
181
+ } catch {}
182
+ return fallback;
183
+ }
184
+ //#endregion
185
+ export { AccessDeniedError, AmbiguousClusterError, AuthenticationError, ConnectionError, NoClusterError, NotFoundError, PlatformRunClient };
186
+
187
+ //# sourceMappingURL=platform-client.js.map
@@ -1,4 +1,4 @@
1
- import "../chunk-gOLHoazu.js";
1
+ import "../chunk-BTugEXQM.js";
2
2
  //#region src/remote/prod-defaults.ts
3
3
  /**
4
4
  * Production defaults for the `kici login` OAuth flow. When the matching
@@ -0,0 +1,11 @@
1
+ import { formatDuration } from '@kici-dev/core';
2
+ export { formatDuration };
3
+ /** Length of a string ignoring ANSI color codes (for column alignment). */
4
+ export declare function stripAnsi(s: string): string;
5
+ /** Human "Ns/Nm/Nh/Nd ago" from an ISO timestamp; '—' when absent/invalid. */
6
+ export declare function relativeTime(iso: string | undefined, now?: number): string;
7
+ /** Color a run/job/step status. picocolors disables itself for NO_COLOR / non-TTY. */
8
+ export declare function colorStatus(status: string): string;
9
+ /** Width-aligned ASCII table with a bold header row. */
10
+ export declare function renderTable(headers: string[], rows: string[][]): string;
11
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1,48 @@
1
+ import "../chunk-BTugEXQM.js";
2
+ import pc from "picocolors";
3
+ import { formatDuration } from "@kici-dev/core";
4
+ //#region src/remote/render.ts
5
+ /** Shared terminal-render helpers for the diagnostics + runs commands. */
6
+ const ANSI_SGR = /\[[0-9;]*m/g;
7
+ /** Length of a string ignoring ANSI color codes (for column alignment). */
8
+ function stripAnsi(s) {
9
+ return s.replace(ANSI_SGR, "");
10
+ }
11
+ /** Human "Ns/Nm/Nh/Nd ago" from an ISO timestamp; '—' when absent/invalid. */
12
+ function relativeTime(iso, now = Date.now()) {
13
+ if (!iso) return "—";
14
+ const then = Date.parse(iso);
15
+ if (Number.isNaN(then)) return "—";
16
+ const sec = Math.max(0, Math.round((now - then) / 1e3));
17
+ if (sec < 60) return `${sec}s ago`;
18
+ const min = Math.round(sec / 60);
19
+ if (min < 60) return `${min}m ago`;
20
+ const hr = Math.round(min / 60);
21
+ if (hr < 24) return `${hr}h ago`;
22
+ return `${Math.round(hr / 24)}d ago`;
23
+ }
24
+ /** Color a run/job/step status. picocolors disables itself for NO_COLOR / non-TTY. */
25
+ function colorStatus(status) {
26
+ if (!pc.isColorSupported) return status;
27
+ switch (status) {
28
+ case "success": return pc.green(status);
29
+ case "failed":
30
+ case "error":
31
+ case "timed_out": return pc.red(status);
32
+ case "running": return pc.cyan(status);
33
+ case "cancelled": return pc.yellow(status);
34
+ default: return pc.gray(status);
35
+ }
36
+ }
37
+ /** Width-aligned ASCII table with a bold header row. */
38
+ function renderTable(headers, rows) {
39
+ const widths = headers.map((h, i) => Math.max(stripAnsi(h).length, ...rows.map((r) => stripAnsi(r[i] ?? "").length)));
40
+ const pad = (cell, i) => cell + " ".repeat(Math.max(0, widths[i] - stripAnsi(cell).length));
41
+ const header = headers.map((h, i) => pc.bold(pad(h, i))).join(" ");
42
+ const body = rows.map((r) => r.map((c, i) => pad(c ?? "", i)).join(" ")).join("\n");
43
+ return body ? `${header}\n${body}` : header;
44
+ }
45
+ //#endregion
46
+ export { colorStatus, formatDuration, relativeTime, renderTable, stripAnsi };
47
+
48
+ //# sourceMappingURL=render.js.map
@@ -1,4 +1,4 @@
1
- import "../chunk-gOLHoazu.js";
1
+ import "../chunk-BTugEXQM.js";
2
2
  import { loadLocalSecrets } from "../local-executor/secret-loader.js";
3
3
  import { encryptJson } from "@kici-dev/core";
4
4
  //#region src/remote/secret-upload.ts
@@ -79,12 +79,20 @@ export interface OverlaySelection {
79
79
  * files like `.kici/.env.local` — are excluded by `--exclude-standard` and
80
80
  * never appear in the selection.
81
81
  *
82
+ * When `fullWorkingTree` is set (the `kici run remote` path), the entire
83
+ * `.git` directory is additively included so the extracted overlay is a real
84
+ * git repository on the agent — workflow steps that shell out to git then work
85
+ * exactly as they do under `kici run local`. The `.git` files are added after
86
+ * `.kiciignore` filtering (git internals are never subject to working-tree
87
+ * ignore globs).
88
+ *
82
89
  * @param repoRoot - Path to the git repository root
83
90
  * @param options - Optional configuration
84
91
  * @returns HEAD SHA, remote flag, and the existing/deleted file partition
85
92
  */
86
93
  export declare function selectOverlayFiles(repoRoot: string, options?: {
87
94
  kiciIgnorePath?: string;
95
+ fullWorkingTree?: boolean;
88
96
  }): Promise<OverlaySelection>;
89
97
  /**
90
98
  * Create an overlay tarball from a git repo, including only changed files.
@@ -101,6 +109,7 @@ export declare function selectOverlayFiles(repoRoot: string, options?: {
101
109
  */
102
110
  export declare function createOverlayTarball(repoRoot: string, options?: {
103
111
  kiciIgnorePath?: string;
112
+ fullWorkingTree?: boolean;
104
113
  }): Promise<{
105
114
  tarballPath: string;
106
115
  summary: UploadSummary;