@kici-dev/compiler 0.1.27 → 0.3.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.js +120 -11
- package/dist/commands/compile.js +28 -4
- 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 +8 -0
- package/dist/commands/index.js +5 -1
- 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 +159 -36
- package/dist/commands/local.d.ts +28 -2
- package/dist/commands/local.js +74 -5
- package/dist/commands/login.js +1 -1
- package/dist/commands/notifications.d.ts +61 -0
- package/dist/commands/notifications.js +274 -0
- package/dist/commands/preview.js +8 -1
- package/dist/commands/run-routed.js +10 -1
- package/dist/commands/run.js +41 -12
- 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/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 +25 -5
- package/dist/fixtures/defaults/index.js +2 -1
- package/dist/llm-context/llms-architecture.txt +51 -22
- package/dist/llm-context/llms-cli.txt +2461 -1582
- package/dist/llm-context/llms-features.txt +178 -106
- package/dist/llm-context/llms-full.txt +6127 -4426
- package/dist/llm-context/llms-getting-started.txt +306 -42
- package/dist/llm-context/llms-patterns.txt +13 -7
- package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
- package/dist/llm-context/llms-sdk.txt +591 -2152
- package/dist/llm-context/llms.txt +30 -16
- package/dist/local-plane/orchestrator-process.d.ts +12 -7
- package/dist/local-plane/orchestrator-process.js +28 -14
- 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/plane-liveness.d.ts +68 -0
- package/dist/local-plane/plane-liveness.js +145 -0
- package/dist/local-plane/plane-manager.d.ts +60 -18
- package/dist/local-plane/plane-manager.js +210 -70
- package/dist/local-plane/plane-trigger.d.ts +13 -4
- package/dist/local-plane/plane-trigger.js +27 -14
- package/dist/local-plane/platform-attach.js +1 -5
- 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 +15 -3
- package/dist/local-plane/postgres.js +34 -12
- package/dist/local-plane/run-follow.d.ts +20 -0
- package/dist/local-plane/run-follow.js +51 -4
- package/dist/local-plane/scaler-config.d.ts +28 -14
- package/dist/local-plane/scaler-config.js +59 -41
- package/dist/local-plane/source-provider.js +4 -10
- package/dist/lockfile/generator.d.ts +14 -1
- package/dist/lockfile/generator.js +99 -33
- 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 +39 -2
- package/dist/remote/dashboard-client.js +47 -4
- 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/render.js +30 -9
- package/dist/remote/uploader.js +5 -4
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +6 -6
- 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 +81 -43
- 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/test-runner/step-context.d.ts +23 -5
- package/dist/test-runner/step-context.js +28 -118
- package/dist/types.d.ts +21 -2
- 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 +12 -15
- package/sbom.spdx.json +1322 -1964
- 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/index.d.ts +0 -23
- package/dist/local-executor/job-runner.d.ts +0 -47
- package/dist/local-executor/materializer.d.ts +0 -44
- package/dist/local-executor/output-streamer.d.ts +0 -31
- package/dist/local-executor/payload-generator.d.ts +0 -16
- package/dist/local-executor/picker.d.ts +0 -33
- package/dist/local-executor/runs-on-display.d.ts +0 -9
- package/dist/local-executor/secret-loader.d.ts +0 -18
- package/dist/local-executor/to-event-payload.d.ts +0 -16
- package/dist/local-executor/types.d.ts +0 -93
- package/dist/local-executor/workflow-lock.d.ts +0 -82
- 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
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { resolveKiciDir } from "../execution/executor.js";
|
|
3
|
+
import "../execution/index.js";
|
|
4
|
+
import { isLockStaticJob } from "../types.js";
|
|
5
|
+
import { loadGlobalConfig } from "../remote/config.js";
|
|
6
|
+
import { DashboardClient, DashboardClientError } from "../remote/dashboard-client.js";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import pc from "picocolors";
|
|
9
|
+
import fs from "node:fs/promises";
|
|
10
|
+
import { deriveDiagnoseOverall, diagnoseExitCode, toErrorMessage } from "@kici-dev/core";
|
|
11
|
+
import { matcherSatisfiedBy } from "@kici-dev/engine";
|
|
12
|
+
import { execFile } from "node:child_process";
|
|
13
|
+
import { promisify } from "node:util";
|
|
14
|
+
//#region src/commands/doctor.ts
|
|
15
|
+
/**
|
|
16
|
+
* kici doctor — walk the onboarding funnel and diagnose where a developer's
|
|
17
|
+
* setup breaks. Each check reports pass/warn/fail with the exact next command
|
|
18
|
+
* to run. Composed entirely from client-side state (the stored config, one
|
|
19
|
+
* authenticated infrastructure read, and local filesystem/git state) — no new
|
|
20
|
+
* Platform API is required.
|
|
21
|
+
*/
|
|
22
|
+
const execFileAsync = promisify(execFile);
|
|
23
|
+
const SEVEN_DAYS_MS = 10080 * 60 * 1e3;
|
|
24
|
+
/** Is a stored token + endpoint present (regardless of expiry)? */
|
|
25
|
+
function hasCredentials(config) {
|
|
26
|
+
return Boolean((config.pat ?? config.token) && (config.platformEndpoint ?? config.endpoint));
|
|
27
|
+
}
|
|
28
|
+
/** Check 1: logged in with an unexpired token. */
|
|
29
|
+
function checkLogin(config, now) {
|
|
30
|
+
const name = "login";
|
|
31
|
+
const endpoint = config.platformEndpoint ?? config.endpoint;
|
|
32
|
+
if (!hasCredentials(config)) return {
|
|
33
|
+
name,
|
|
34
|
+
status: "fail",
|
|
35
|
+
message: "Not logged in (no stored credentials).",
|
|
36
|
+
nextCommand: "kici login"
|
|
37
|
+
};
|
|
38
|
+
if (config.patExpiresAt) {
|
|
39
|
+
const expiresAt = Date.parse(config.patExpiresAt);
|
|
40
|
+
if (!Number.isNaN(expiresAt)) {
|
|
41
|
+
if (expiresAt <= now) return {
|
|
42
|
+
name,
|
|
43
|
+
status: "fail",
|
|
44
|
+
message: "Access token has expired.",
|
|
45
|
+
nextCommand: "kici login"
|
|
46
|
+
};
|
|
47
|
+
if (expiresAt - now < SEVEN_DAYS_MS) return {
|
|
48
|
+
name,
|
|
49
|
+
status: "warn",
|
|
50
|
+
message: `Access token expires in ${Math.ceil((expiresAt - now) / (1440 * 60 * 1e3))} day(s).`,
|
|
51
|
+
nextCommand: "kici login"
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
name,
|
|
57
|
+
status: "pass",
|
|
58
|
+
message: `Logged in to ${endpoint}.`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Check 2: an active organization is selected. */
|
|
62
|
+
function checkActiveOrg(config) {
|
|
63
|
+
const name = "active-org";
|
|
64
|
+
if (!config.activeOrgId) return {
|
|
65
|
+
name,
|
|
66
|
+
status: "fail",
|
|
67
|
+
message: "No active organization selected.",
|
|
68
|
+
nextCommand: "kici org use <name>"
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
name,
|
|
72
|
+
status: "pass",
|
|
73
|
+
message: `Active organization: ${config.activeOrgId}.`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Check 3: the Platform accepted the stored token (live-token probe). */
|
|
77
|
+
function checkLiveToken(probe) {
|
|
78
|
+
const name = "token-live";
|
|
79
|
+
if (probe === null) return {
|
|
80
|
+
name,
|
|
81
|
+
status: "warn",
|
|
82
|
+
message: "Skipped: requires login and an active organization."
|
|
83
|
+
};
|
|
84
|
+
if (probe.ok) return {
|
|
85
|
+
name,
|
|
86
|
+
status: "pass",
|
|
87
|
+
message: "Platform accepted the access token."
|
|
88
|
+
};
|
|
89
|
+
if (probe.kind === "unauthorized" || probe.kind === "not_logged_in") return {
|
|
90
|
+
name,
|
|
91
|
+
status: "fail",
|
|
92
|
+
message: `Authentication failed: ${probe.message}`,
|
|
93
|
+
nextCommand: "kici login"
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
name,
|
|
97
|
+
status: "warn",
|
|
98
|
+
message: `Could not verify token: ${probe.message}`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Check 4: an orchestrator is connected for this org. */
|
|
102
|
+
function checkOrchestrator(probe) {
|
|
103
|
+
const name = "orchestrator";
|
|
104
|
+
if (probe === null || !probe.ok) return {
|
|
105
|
+
name,
|
|
106
|
+
status: "warn",
|
|
107
|
+
message: "Skipped: Platform not reachable (see token-live)."
|
|
108
|
+
};
|
|
109
|
+
const orchestrators = probe.infra.orchestrators ?? [];
|
|
110
|
+
const connected = orchestrators.filter((o) => o.connected);
|
|
111
|
+
if (orchestrators.length === 0) return {
|
|
112
|
+
name,
|
|
113
|
+
status: "warn",
|
|
114
|
+
message: "No orchestrator has connected for this organization yet.",
|
|
115
|
+
nextCommand: "kici diagnostics"
|
|
116
|
+
};
|
|
117
|
+
if (connected.length === 0) return {
|
|
118
|
+
name,
|
|
119
|
+
status: "fail",
|
|
120
|
+
message: `${orchestrators.length} orchestrator(s) registered but none are currently connected.`,
|
|
121
|
+
nextCommand: "kici diagnostics"
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
name,
|
|
125
|
+
status: "pass",
|
|
126
|
+
message: `${connected.length} orchestrator(s) connected.`
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Check 5: the lock file is present, fresh, and committed. */
|
|
130
|
+
function checkLockFile(state) {
|
|
131
|
+
const name = "lock-file";
|
|
132
|
+
if (!state.exists) return {
|
|
133
|
+
name,
|
|
134
|
+
status: "fail",
|
|
135
|
+
message: "No compiled kici.lock.json found.",
|
|
136
|
+
nextCommand: "kici compile"
|
|
137
|
+
};
|
|
138
|
+
if (!state.fresh) return {
|
|
139
|
+
name,
|
|
140
|
+
status: "warn",
|
|
141
|
+
message: "Lock file is stale — workflow sources changed since the last compile.",
|
|
142
|
+
nextCommand: "kici compile"
|
|
143
|
+
};
|
|
144
|
+
if (state.gitAvailable && !state.committed) return {
|
|
145
|
+
name,
|
|
146
|
+
status: "warn",
|
|
147
|
+
message: "Lock file has uncommitted changes.",
|
|
148
|
+
nextCommand: "git add .kici/kici.lock.json && git commit"
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
name,
|
|
152
|
+
status: "pass",
|
|
153
|
+
message: "Lock file present, fresh, and committed."
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A label group satisfies a requirement only when it matches EVERY runsOn
|
|
158
|
+
* matcher AND matches NONE of the excludeLabels — the same authority the
|
|
159
|
+
* orchestrator's job queue applies (`runsOn.every` && `!exclude.some`).
|
|
160
|
+
*/
|
|
161
|
+
function requirementSatisfiedBy(req, labels) {
|
|
162
|
+
return req.runsOn.every((m) => matcherSatisfiedBy(m, labels)) && !req.exclude.some((m) => matcherSatisfiedBy(m, labels));
|
|
163
|
+
}
|
|
164
|
+
function describeMatcher(m) {
|
|
165
|
+
return m.kind === "regex" ? `/${m.source}/${m.flags}` : m.value;
|
|
166
|
+
}
|
|
167
|
+
function describeRequirement(req) {
|
|
168
|
+
const base = req.runsOn.map(describeMatcher).join(" + ");
|
|
169
|
+
return req.exclude.length > 0 ? `${base} − ${req.exclude.map(describeMatcher).join(", ")}` : base;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Collect the label groups that can satisfy a runsOn set on their own: each
|
|
173
|
+
* connected agent's label set, and each scaler labelSet.
|
|
174
|
+
*/
|
|
175
|
+
function availableLabelGroups(infra) {
|
|
176
|
+
const groups = [];
|
|
177
|
+
for (const o of infra.orchestrators) {
|
|
178
|
+
if (!o.connected) continue;
|
|
179
|
+
for (const a of o.agents ?? []) groups.push(new Set(a.labels ?? []));
|
|
180
|
+
for (const s of o.scalers ?? []) for (const set of s.labelSets ?? []) groups.push(new Set(set));
|
|
181
|
+
}
|
|
182
|
+
return groups;
|
|
183
|
+
}
|
|
184
|
+
/** Check 6: every workflow's runsOn labels are satisfiable by a connected agent or scaler. */
|
|
185
|
+
function checkLabels(lock, probe) {
|
|
186
|
+
const name = "labels";
|
|
187
|
+
if (!lock) return {
|
|
188
|
+
name,
|
|
189
|
+
status: "warn",
|
|
190
|
+
message: "Skipped: no compiled lock file to read labels from."
|
|
191
|
+
};
|
|
192
|
+
if (probe === null || !probe.ok) return {
|
|
193
|
+
name,
|
|
194
|
+
status: "warn",
|
|
195
|
+
message: "Skipped: Platform not reachable (see token-live)."
|
|
196
|
+
};
|
|
197
|
+
const required = [];
|
|
198
|
+
for (const wf of lock.workflows) for (const job of wf.jobs) {
|
|
199
|
+
if (!isLockStaticJob(job)) continue;
|
|
200
|
+
const runsOn = job.runsOn ?? [];
|
|
201
|
+
if (runsOn.length === 0) continue;
|
|
202
|
+
required.push({
|
|
203
|
+
runsOn,
|
|
204
|
+
exclude: job.excludeLabels ?? []
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (required.length === 0) return {
|
|
208
|
+
name,
|
|
209
|
+
status: "pass",
|
|
210
|
+
message: "No workflow label constraints to satisfy."
|
|
211
|
+
};
|
|
212
|
+
const groups = availableLabelGroups(probe.infra);
|
|
213
|
+
if (groups.length === 0) return {
|
|
214
|
+
name,
|
|
215
|
+
status: "warn",
|
|
216
|
+
message: "No connected agents or scalers to satisfy any workflow labels.",
|
|
217
|
+
nextCommand: "kici diagnostics"
|
|
218
|
+
};
|
|
219
|
+
const unsatisfied = /* @__PURE__ */ new Set();
|
|
220
|
+
for (const req of required) if (!groups.some((labels) => requirementSatisfiedBy(req, labels))) unsatisfied.add(describeRequirement(req));
|
|
221
|
+
if (unsatisfied.size > 0) return {
|
|
222
|
+
name,
|
|
223
|
+
status: "warn",
|
|
224
|
+
message: `No connected agent or scaler can satisfy: ${[...unsatisfied].join("; ")}.`,
|
|
225
|
+
details: { unsatisfied: [...unsatisfied] },
|
|
226
|
+
nextCommand: "kici diagnostics"
|
|
227
|
+
};
|
|
228
|
+
return {
|
|
229
|
+
name,
|
|
230
|
+
status: "pass",
|
|
231
|
+
message: "All workflow labels are satisfiable by connected capacity."
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/** Newest mtime (ms) across every *.ts under the workflows directory, or 0. */
|
|
235
|
+
async function newestWorkflowMtime(workflowsDir) {
|
|
236
|
+
let newest = 0;
|
|
237
|
+
let entries;
|
|
238
|
+
try {
|
|
239
|
+
entries = await fs.readdir(workflowsDir);
|
|
240
|
+
} catch {
|
|
241
|
+
return 0;
|
|
242
|
+
}
|
|
243
|
+
for (const entry of entries) {
|
|
244
|
+
if (!entry.endsWith(".ts")) continue;
|
|
245
|
+
try {
|
|
246
|
+
const st = await fs.stat(path.join(workflowsDir, entry));
|
|
247
|
+
if (st.mtimeMs > newest) newest = st.mtimeMs;
|
|
248
|
+
} catch {}
|
|
249
|
+
}
|
|
250
|
+
return newest;
|
|
251
|
+
}
|
|
252
|
+
/** Is the working tree clean for the given path? gitAvailable=false when not a repo. */
|
|
253
|
+
async function gitCleanForPath(filePath) {
|
|
254
|
+
try {
|
|
255
|
+
const { stdout } = await execFileAsync("git", [
|
|
256
|
+
"status",
|
|
257
|
+
"--porcelain",
|
|
258
|
+
"--",
|
|
259
|
+
filePath
|
|
260
|
+
], { cwd: path.dirname(filePath) });
|
|
261
|
+
return {
|
|
262
|
+
gitAvailable: true,
|
|
263
|
+
committed: stdout.trim() === ""
|
|
264
|
+
};
|
|
265
|
+
} catch {
|
|
266
|
+
return {
|
|
267
|
+
gitAvailable: false,
|
|
268
|
+
committed: false
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/** Default gatherer: read the lock file, compute freshness by mtime, check git. */
|
|
273
|
+
async function defaultGatherLockState(kiciDir) {
|
|
274
|
+
const abs = resolveKiciDir(kiciDir);
|
|
275
|
+
const lockPath = path.join(abs, "kici.lock.json");
|
|
276
|
+
let lock = null;
|
|
277
|
+
let lockMtime = 0;
|
|
278
|
+
try {
|
|
279
|
+
lockMtime = (await fs.stat(lockPath)).mtimeMs;
|
|
280
|
+
lock = JSON.parse(await fs.readFile(lockPath, "utf-8"));
|
|
281
|
+
} catch {
|
|
282
|
+
return {
|
|
283
|
+
exists: false,
|
|
284
|
+
fresh: false,
|
|
285
|
+
committed: false,
|
|
286
|
+
gitAvailable: false,
|
|
287
|
+
lock: null
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const newestSrc = await newestWorkflowMtime(path.join(abs, "workflows"));
|
|
291
|
+
const { gitAvailable, committed } = await gitCleanForPath(lockPath);
|
|
292
|
+
return {
|
|
293
|
+
exists: true,
|
|
294
|
+
fresh: lockMtime >= newestSrc,
|
|
295
|
+
committed,
|
|
296
|
+
gitAvailable,
|
|
297
|
+
lock
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
async function probeInfrastructure(client) {
|
|
301
|
+
try {
|
|
302
|
+
return {
|
|
303
|
+
ok: true,
|
|
304
|
+
infra: await client.getInfrastructure()
|
|
305
|
+
};
|
|
306
|
+
} catch (err) {
|
|
307
|
+
if (err instanceof DashboardClientError) return {
|
|
308
|
+
ok: false,
|
|
309
|
+
kind: err.kind,
|
|
310
|
+
message: err.message
|
|
311
|
+
};
|
|
312
|
+
return {
|
|
313
|
+
ok: false,
|
|
314
|
+
kind: "http",
|
|
315
|
+
message: toErrorMessage(err)
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/** Default probe: attempt one authenticated infrastructure read, or null when not eligible. */
|
|
320
|
+
async function defaultProbe(config) {
|
|
321
|
+
if (!hasCredentials(config) || !config.activeOrgId) return null;
|
|
322
|
+
let client;
|
|
323
|
+
try {
|
|
324
|
+
client = DashboardClient.fromConfig(config);
|
|
325
|
+
} catch (err) {
|
|
326
|
+
if (err instanceof DashboardClientError) return {
|
|
327
|
+
ok: false,
|
|
328
|
+
kind: err.kind,
|
|
329
|
+
message: err.message
|
|
330
|
+
};
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
return probeInfrastructure(client);
|
|
334
|
+
}
|
|
335
|
+
const defaultDeps = {
|
|
336
|
+
loadConfig: loadGlobalConfig,
|
|
337
|
+
probe: defaultProbe,
|
|
338
|
+
gatherLockState: defaultGatherLockState,
|
|
339
|
+
now: Date.now
|
|
340
|
+
};
|
|
341
|
+
function colorizeStatus(status) {
|
|
342
|
+
switch (status) {
|
|
343
|
+
case "pass":
|
|
344
|
+
case "healthy": return pc.green(status);
|
|
345
|
+
case "warn":
|
|
346
|
+
case "degraded": return pc.yellow(status);
|
|
347
|
+
case "fail":
|
|
348
|
+
case "unhealthy": return pc.red(status);
|
|
349
|
+
default: return status;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/** Render the funnel as a status table with a trailing next-command hint per row. */
|
|
353
|
+
function renderDoctorTable(response) {
|
|
354
|
+
const lines = [];
|
|
355
|
+
lines.push(`${pc.bold("kici doctor")} ${colorizeStatus(response.status)}`);
|
|
356
|
+
lines.push("");
|
|
357
|
+
for (const c of response.checks) {
|
|
358
|
+
lines.push(`${colorizeStatus(c.status.padEnd(4))} ${pc.bold(c.name.padEnd(14))} ${c.message}`);
|
|
359
|
+
if (c.nextCommand) lines.push(` ${pc.cyan(`→ ${c.nextCommand}`)}`);
|
|
360
|
+
}
|
|
361
|
+
return lines.join("\n");
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Run the onboarding-funnel diagnostic and return the process exit code
|
|
365
|
+
* (0 all pass · 1 any warn · 2 any fail). The single infrastructure probe is
|
|
366
|
+
* timed and attributed to the token-live check; the local checks are instant.
|
|
367
|
+
*/
|
|
368
|
+
async function doctorCommand(options = {}, deps = defaultDeps) {
|
|
369
|
+
const config = await deps.loadConfig();
|
|
370
|
+
const now = deps.now();
|
|
371
|
+
const lockState = await deps.gatherLockState(options.kiciDir ?? ".kici");
|
|
372
|
+
const probeStart = deps.now();
|
|
373
|
+
const probe = await deps.probe(config);
|
|
374
|
+
const probeMs = Math.max(0, deps.now() - probeStart);
|
|
375
|
+
const checks = [
|
|
376
|
+
{
|
|
377
|
+
...checkLogin(config, now),
|
|
378
|
+
durationMs: 0
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
...checkActiveOrg(config),
|
|
382
|
+
durationMs: 0
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
...checkLockFile(lockState),
|
|
386
|
+
durationMs: 0
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
...checkLiveToken(probe),
|
|
390
|
+
durationMs: probeMs
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
...checkOrchestrator(probe),
|
|
394
|
+
durationMs: 0
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
...checkLabels(lockState.lock, probe),
|
|
398
|
+
durationMs: 0
|
|
399
|
+
}
|
|
400
|
+
];
|
|
401
|
+
const response = {
|
|
402
|
+
status: deriveDiagnoseOverall(checks),
|
|
403
|
+
checks,
|
|
404
|
+
timestamp: new Date(now).toISOString()
|
|
405
|
+
};
|
|
406
|
+
if (options.json) console.log(JSON.stringify(response, null, 2));
|
|
407
|
+
else console.log(renderDoctorTable(response));
|
|
408
|
+
return diagnoseExitCode(checks);
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
411
|
+
export { checkActiveOrg, checkLabels, checkLiveToken, checkLockFile, checkLogin, checkOrchestrator, doctorCommand, hasCredentials, renderDoctorTable };
|
|
412
|
+
|
|
413
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -113,6 +113,12 @@ function categorizeTrigger(workflowName, trigger, groups) {
|
|
|
113
113
|
eventName: `workflow_complete${trigger.name ? `:${trigger.name}` : ""}`
|
|
114
114
|
});
|
|
115
115
|
break;
|
|
116
|
+
case "workflows_failed_batch":
|
|
117
|
+
groups.kiciEvent.push({
|
|
118
|
+
workflowName,
|
|
119
|
+
eventName: `workflows_failed_batch${trigger.name ? `:${trigger.name}` : ""}`
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
116
122
|
case "job_complete":
|
|
117
123
|
groups.kiciEvent.push({
|
|
118
124
|
workflowName,
|
package/dist/commands/fixture.js
CHANGED
|
@@ -16,6 +16,7 @@ import { logger } from "@kici-dev/core";
|
|
|
16
16
|
const INTERNAL_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
17
17
|
"kici_event",
|
|
18
18
|
"workflow_complete",
|
|
19
|
+
"workflows_failed_batch",
|
|
19
20
|
"job_complete",
|
|
20
21
|
"generic_webhook",
|
|
21
22
|
"schedule",
|
|
@@ -45,6 +46,18 @@ function generateInternalFixture(eventType) {
|
|
|
45
46
|
status: "status" in eventType ? eventType.status : "success",
|
|
46
47
|
sourceRepo: "owner/repo"
|
|
47
48
|
};
|
|
49
|
+
case "workflows_failed_batch": return {
|
|
50
|
+
total: 2,
|
|
51
|
+
runs: [{
|
|
52
|
+
runId: "run-1",
|
|
53
|
+
repo: "owner/repo",
|
|
54
|
+
workflowName: "ci"
|
|
55
|
+
}, {
|
|
56
|
+
runId: "run-2",
|
|
57
|
+
repo: "owner/repo",
|
|
58
|
+
workflowName: "deploy"
|
|
59
|
+
}]
|
|
60
|
+
};
|
|
48
61
|
case "job_complete": return {
|
|
49
62
|
workflowName: "workflowName" in eventType ? eventType.workflowName : "ci",
|
|
50
63
|
jobName: "jobName" in eventType ? eventType.jobName : "build",
|
package/dist/commands/hook.js
CHANGED
|
@@ -7,6 +7,7 @@ import pc from "picocolors";
|
|
|
7
7
|
import { readFile } from "node:fs/promises";
|
|
8
8
|
import { logger, toErrorMessage } from "@kici-dev/core";
|
|
9
9
|
import { select } from "@inquirer/prompts";
|
|
10
|
+
import { isCiEnvironment } from "@kici-dev/core/ci-env";
|
|
10
11
|
//#region src/commands/hook.ts
|
|
11
12
|
/**
|
|
12
13
|
* kici hook command
|
|
@@ -30,7 +31,7 @@ async function hookInstallCommand(options = {}) {
|
|
|
30
31
|
if (options.git) selectedTool = "git";
|
|
31
32
|
else {
|
|
32
33
|
const tools = await detectHookTools();
|
|
33
|
-
if (tools.length === 0) if (process.stdout.isTTY &&
|
|
34
|
+
if (tools.length === 0) if (process.stdout.isTTY && !isCiEnvironment()) {
|
|
34
35
|
const choice = await select({
|
|
35
36
|
message: "No pre-commit tool detected. How would you like to install the hook?",
|
|
36
37
|
choices: [{
|
|
@@ -53,7 +54,7 @@ async function hookInstallCommand(options = {}) {
|
|
|
53
54
|
else if (tools.length === 1) {
|
|
54
55
|
selectedTool = tools[0].name;
|
|
55
56
|
logger.info(pc.gray(`Detected ${selectedTool}, installing hook...`));
|
|
56
|
-
} else if (process.stdout.isTTY &&
|
|
57
|
+
} else if (process.stdout.isTTY && !isCiEnvironment()) selectedTool = await select({
|
|
57
58
|
message: "Multiple pre-commit tools detected. Which would you like to use?",
|
|
58
59
|
choices: tools.map((t) => ({
|
|
59
60
|
name: t.name,
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -28,12 +28,18 @@ export { runsRerunCommand } from './runs/rerun.js';
|
|
|
28
28
|
export type { RunsRerunOptions } from './runs/rerun.js';
|
|
29
29
|
export { runsCancelCommand } from './runs/cancel.js';
|
|
30
30
|
export type { RunsCancelOptions } from './runs/cancel.js';
|
|
31
|
+
export { runsArtifactsListCommand } from './runs/artifacts/list.js';
|
|
32
|
+
export type { RunsArtifactsListOptions } from './runs/artifacts/list.js';
|
|
33
|
+
export { runsArtifactsDownloadCommand } from './runs/artifacts/download.js';
|
|
34
|
+
export type { RunsArtifactsDownloadOptions } from './runs/artifacts/download.js';
|
|
31
35
|
export { typesCommand } from './types.js';
|
|
32
36
|
export type { TypesOptions } from './types.js';
|
|
33
37
|
export { endpointsCommand } from './endpoints.js';
|
|
34
38
|
export type { EndpointsOptions } from './endpoints.js';
|
|
35
39
|
export { diagnosticsCommand } from './diagnostics.js';
|
|
36
40
|
export type { DiagnosticsOptions } from './diagnostics.js';
|
|
41
|
+
export { doctorCommand } from './doctor.js';
|
|
42
|
+
export type { DoctorOptions } from './doctor.js';
|
|
37
43
|
export { orgListCommand, orgUseCommand, orgCurrentCommand } from './org.js';
|
|
38
44
|
export { orchestratorsListCommand, orchestratorsUseCommand } from './orchestrators.js';
|
|
39
45
|
export type { OrchestratorsOptions } from './orchestrators.js';
|
|
@@ -52,4 +58,6 @@ export { docsCommand, docsLlmCommand } from './docs.js';
|
|
|
52
58
|
export type { DocsOptions, DocsLlmOptions } from './docs.js';
|
|
53
59
|
export { verifyAttestationCommand } from './verify-attestation.js';
|
|
54
60
|
export type { VerifyAttestationOptions } from './verify-attestation.js';
|
|
61
|
+
export { notificationsChannelsListCommand, notificationsChannelsAddCommand, notificationsChannelsRemoveCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsRemoveCommand, notificationsRosterListCommand, notificationsRosterAddCommand, notificationsRosterRemoveCommand, } from './notifications.js';
|
|
62
|
+
export type { NotificationsCommonOptions, NotificationsListOptions, ChannelsAddOptions, SubscriptionsAddOptions, RosterAddOptions, } from './notifications.js';
|
|
55
63
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/commands/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { approveCommand } from "./approve.js";
|
|
|
3
3
|
import { compileCommand } from "./compile.js";
|
|
4
4
|
import { diagnosticsCommand } from "./diagnostics.js";
|
|
5
5
|
import { docsCommand, docsLlmCommand } from "./docs.js";
|
|
6
|
+
import { doctorCommand } from "./doctor.js";
|
|
6
7
|
import { drainWorkerCommand } from "./drain-worker.js";
|
|
7
8
|
import { endpointsCommand } from "./endpoints.js";
|
|
8
9
|
import { fixtureCommand } from "./fixture.js";
|
|
@@ -20,6 +21,8 @@ import { runsShowCommand } from "./runs/show.js";
|
|
|
20
21
|
import { runsLogsCommand } from "./runs/logs.js";
|
|
21
22
|
import { runsRerunCommand } from "./runs/rerun.js";
|
|
22
23
|
import { runsCancelCommand } from "./runs/cancel.js";
|
|
24
|
+
import { runsArtifactsListCommand } from "./runs/artifacts/list.js";
|
|
25
|
+
import { runsArtifactsDownloadCommand } from "./runs/artifacts/download.js";
|
|
23
26
|
import { typesCommand } from "./types.js";
|
|
24
27
|
import { orgCurrentCommand, orgListCommand, orgUseCommand } from "./org.js";
|
|
25
28
|
import { orchestratorsListCommand, orchestratorsUseCommand } from "./orchestrators.js";
|
|
@@ -29,4 +32,5 @@ import { logoutCommand } from "./logout.js";
|
|
|
29
32
|
import { rejectCommand } from "./reject.js";
|
|
30
33
|
import { workflowsListCommand } from "./workflows.js";
|
|
31
34
|
import { verifyAttestationCommand } from "./verify-attestation.js";
|
|
32
|
-
|
|
35
|
+
import { notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand } from "./notifications.js";
|
|
36
|
+
export { approveCommand, compileCommand, diagnosticsCommand, docsCommand, docsLlmCommand, doctorCommand, drainWorkerCommand, endpointsCommand, fixtureCommand, hookInstallCommand, initCommand, localAttachCommand, localDetachCommand, localDownCommand, localLogsCommand, localStatusCommand, localTrustRootCommand, localUpCommand, loginCommand, logoutCommand, notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand, orchestratorsListCommand, orchestratorsUseCommand, orgCurrentCommand, orgListCommand, orgUseCommand, patCreateCommand, previewCommand, previewEvent, rejectCommand, runRemoteCommand, runRoutedCommand, runsArtifactsDownloadCommand, runsArtifactsListCommand, runsCancelCommand, runsListCommand, runsLogsCommand, runsRerunCommand, runsShowCommand, secretsListCommand, typesCommand, verifyAttestationCommand, watchCommand, workflowsListCommand };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-OS helpers for `kici init`.
|
|
3
|
+
*
|
|
4
|
+
* The starter templates target `kici:os:linux`. On a macOS or Windows host that
|
|
5
|
+
* label matches no local dev-plane agent, so the first `kici run push --local`
|
|
6
|
+
* dispatches nothing. These helpers rewrite the scaffolded `runsOn` to the
|
|
7
|
+
* host's own `kici:os:*` label so the first run lands, mirroring how `kici init`
|
|
8
|
+
* already rewrites the default branch.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The primary `kici:os:*` label the host reports (linux→kici:os:linux,
|
|
12
|
+
* darwin→kici:os:macos, win32→kici:os:windows). Derived from the engine's
|
|
13
|
+
* canonical mapping — the same derivation the local dev-plane routing uses — so
|
|
14
|
+
* the value is never hand-spelled here and always matches what the plane can
|
|
15
|
+
* satisfy.
|
|
16
|
+
*/
|
|
17
|
+
export declare function primaryHostOsLabel(platform: string, arch: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Rewrite a template's `'kici:os:linux'` runsOn to the host's primary OS label.
|
|
20
|
+
* A no-op on a Linux host (the templates already target it).
|
|
21
|
+
*/
|
|
22
|
+
export declare function rewriteRunsOnForHost(content: string, platform: string, arch: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Whether to offer the post-init `Run it now?` prompt: interactive (TTY, not CI)
|
|
25
|
+
* and with dependencies installed (not `--mjs`, not `--skip-install`), so the
|
|
26
|
+
* offered `kici run push --local` can compile the workflow it just scaffolded.
|
|
27
|
+
*/
|
|
28
|
+
export declare function shouldOfferFirstRun(env: {
|
|
29
|
+
isTTY: boolean;
|
|
30
|
+
ci: boolean;
|
|
31
|
+
mjs: boolean;
|
|
32
|
+
skipInstall: boolean;
|
|
33
|
+
}): boolean;
|
|
34
|
+
//# sourceMappingURL=init-host-os.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { deriveOsArchLabels } from "@kici-dev/engine";
|
|
3
|
+
//#region src/commands/init-host-os.ts
|
|
4
|
+
/**
|
|
5
|
+
* Host-OS helpers for `kici init`.
|
|
6
|
+
*
|
|
7
|
+
* The starter templates target `kici:os:linux`. On a macOS or Windows host that
|
|
8
|
+
* label matches no local dev-plane agent, so the first `kici run push --local`
|
|
9
|
+
* dispatches nothing. These helpers rewrite the scaffolded `runsOn` to the
|
|
10
|
+
* host's own `kici:os:*` label so the first run lands, mirroring how `kici init`
|
|
11
|
+
* already rewrites the default branch.
|
|
12
|
+
*/
|
|
13
|
+
const OS_LABEL_PREFIX = "kici:os:";
|
|
14
|
+
/**
|
|
15
|
+
* The primary `kici:os:*` label the host reports (linux→kici:os:linux,
|
|
16
|
+
* darwin→kici:os:macos, win32→kici:os:windows). Derived from the engine's
|
|
17
|
+
* canonical mapping — the same derivation the local dev-plane routing uses — so
|
|
18
|
+
* the value is never hand-spelled here and always matches what the plane can
|
|
19
|
+
* satisfy.
|
|
20
|
+
*/
|
|
21
|
+
function primaryHostOsLabel(platform, arch) {
|
|
22
|
+
return deriveOsArchLabels(platform, arch).find((l) => l.startsWith(OS_LABEL_PREFIX)) ?? `${OS_LABEL_PREFIX}${platform}`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Rewrite a template's `'kici:os:linux'` runsOn to the host's primary OS label.
|
|
26
|
+
* A no-op on a Linux host (the templates already target it).
|
|
27
|
+
*/
|
|
28
|
+
function rewriteRunsOnForHost(content, platform, arch) {
|
|
29
|
+
const hostLabel = primaryHostOsLabel(platform, arch);
|
|
30
|
+
if (hostLabel === "kici:os:linux") return content;
|
|
31
|
+
return content.replaceAll(`'kici:os:linux'`, `'${hostLabel}'`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether to offer the post-init `Run it now?` prompt: interactive (TTY, not CI)
|
|
35
|
+
* and with dependencies installed (not `--mjs`, not `--skip-install`), so the
|
|
36
|
+
* offered `kici run push --local` can compile the workflow it just scaffolded.
|
|
37
|
+
*/
|
|
38
|
+
function shouldOfferFirstRun(env) {
|
|
39
|
+
return env.isTTY && !env.ci && !env.mjs && !env.skipInstall;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { primaryHostOsLabel, rewriteRunsOnForHost, shouldOfferFirstRun };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=init-host-os.js.map
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ export interface InitOptions {
|
|
|
29
29
|
privateRegistrySecret?: string;
|
|
30
30
|
/** Skip writing the .kici/AGENTS.md LLM-authoring context file. Default: write it. */
|
|
31
31
|
noAgentsMd?: boolean;
|
|
32
|
+
/** Force integrate mode: join .kici/ to the detected workspace (skip the prompt). */
|
|
33
|
+
workspace?: boolean;
|
|
34
|
+
/** Force standalone mode even inside a workspace (skip the prompt). */
|
|
35
|
+
standalone?: boolean;
|
|
32
36
|
}
|
|
33
37
|
/**
|
|
34
38
|
* Initialize .kici/ directory with workflow templates
|