@kici-dev/compiler 0.5.0 → 0.6.1

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 (73) hide show
  1. package/dist/cli.js +46 -8
  2. package/dist/commands/approve.d.ts +12 -0
  3. package/dist/commands/approve.js +5 -2
  4. package/dist/commands/compile.js +4 -2
  5. package/dist/commands/doctor.js +2 -2
  6. package/dist/commands/endpoints.js +4 -6
  7. package/dist/commands/feedback.d.ts +53 -0
  8. package/dist/commands/feedback.js +142 -0
  9. package/dist/commands/held-run-client.d.ts +21 -1
  10. package/dist/commands/held-run-client.js +34 -15
  11. package/dist/commands/hook.js +22 -20
  12. package/dist/commands/index.d.ts +4 -0
  13. package/dist/commands/index.js +3 -1
  14. package/dist/commands/init.d.ts +9 -2
  15. package/dist/commands/init.js +43 -16
  16. package/dist/commands/login.js +1 -1
  17. package/dist/commands/orchestrators.js +3 -2
  18. package/dist/commands/reject.d.ts +12 -0
  19. package/dist/commands/reject.js +5 -2
  20. package/dist/commands/report/collect.d.ts +82 -0
  21. package/dist/commands/report/collect.js +234 -0
  22. package/dist/commands/report/identity.d.ts +48 -0
  23. package/dist/commands/report/identity.js +49 -0
  24. package/dist/commands/report/index.d.ts +63 -0
  25. package/dist/commands/report/index.js +119 -0
  26. package/dist/commands/report/upload.d.ts +38 -0
  27. package/dist/commands/report/upload.js +64 -0
  28. package/dist/commands/run-hold-watch.js +2 -2
  29. package/dist/commands/run.js +6 -3
  30. package/dist/commands/runs/show.js +80 -1
  31. package/dist/commands/types.js +51 -8
  32. package/dist/execution/sdk-alias.js +4 -2
  33. package/dist/fixtures/compiler.js +2 -1
  34. package/dist/format.js +3 -3
  35. package/dist/generators/secrets-dts.d.ts +8 -2
  36. package/dist/generators/secrets-dts.js +3 -2
  37. package/dist/hooks/installer.js +2 -1
  38. package/dist/llm-context/llms-architecture.txt +35 -13
  39. package/dist/llm-context/llms-cli-remote.txt +2347 -0
  40. package/dist/llm-context/llms-cli.txt +284 -2470
  41. package/dist/llm-context/llms-features-execution.txt +2028 -0
  42. package/dist/llm-context/llms-features.txt +298 -1483
  43. package/dist/llm-context/llms-full.txt +6153 -4639
  44. package/dist/llm-context/llms-getting-started.txt +292 -12
  45. package/dist/llm-context/llms-patterns.txt +176 -1
  46. package/dist/llm-context/llms-providers.txt +11 -27
  47. package/dist/llm-context/llms-sdk-runtime.txt +25 -4
  48. package/dist/llm-context/llms-sdk.txt +31 -1
  49. package/dist/llm-context/llms.txt +33 -18
  50. package/dist/local-plane/paths.d.ts +15 -0
  51. package/dist/local-plane/paths.js +22 -1
  52. package/dist/local-plane/plane-manager.js +2 -2
  53. package/dist/local-plane/port-holder.js +1 -1
  54. package/dist/local-plane/postgres.d.ts +3 -16
  55. package/dist/local-plane/postgres.js +10 -15
  56. package/dist/lockfile/generator.d.ts +12 -0
  57. package/dist/lockfile/generator.js +47 -14
  58. package/dist/postinstall.js +2 -1
  59. package/dist/remote/config.d.ts +2 -15
  60. package/dist/remote/config.js +2 -16
  61. package/dist/remote/dashboard-client.d.ts +39 -0
  62. package/dist/remote/dashboard-client.js +41 -0
  63. package/dist/remote/oauth.js +7 -5
  64. package/dist/remote/uploader.js +2 -2
  65. package/dist/templates/package-json.js +1 -1
  66. package/dist/test-runner/dry-run.js +4 -2
  67. package/dist/test-runner/git-detector.js +2 -1
  68. package/dist/test-runner/job-executor.js +2 -1
  69. package/dist/test-runner/payload-builder.js +11 -17
  70. package/dist/types.d.ts +33 -3
  71. package/dist/validation/validator.js +23 -6
  72. package/package.json +16 -11
  73. package/sbom.spdx.json +953 -901
@@ -76,7 +76,8 @@ async function initCommand(options = {}) {
76
76
  logger.info(pc.gray("Writing .kiciignore"));
77
77
  await writeFile(kiciIgnorePath, kiciIgnoreTemplate, "utf-8");
78
78
  }
79
- const devMode = options.useVerdaccioLocal || await detectDevelopmentMode();
79
+ await writeKiciGitignore(kiciDir);
80
+ const devMode = await detectDevelopmentMode();
80
81
  const mode = await resolveScaffoldMode(options);
81
82
  const useVerdaccio = devMode;
82
83
  if (mode.kind === "integrate") {
@@ -204,7 +205,8 @@ async function detectDefaultBranch() {
204
205
  async function detectDevelopmentMode() {
205
206
  if (process.env.KICI_DEV === "true") return true;
206
207
  try {
207
- const content = await readFile(path.resolve(process.cwd(), "package.json"), "utf-8");
208
+ const rootPkgPath = path.resolve(process.cwd(), "package.json");
209
+ const content = await readFile(rootPkgPath, "utf-8");
208
210
  return JSON.parse(content).kici?.development === true;
209
211
  } catch {
210
212
  return false;
@@ -234,7 +236,8 @@ const VERDACCIO_NPMRC = "@kici-dev:registry=http://verdaccio.local:4873\n";
234
236
  async function writeTsConfigAndTypesDir(kiciDir) {
235
237
  logger.info(pc.gray("Writing .kici/tsconfig.json"));
236
238
  await writeFile(path.join(kiciDir, "tsconfig.json"), await generateTsConfig(), "utf-8");
237
- await mkdir(path.join(kiciDir, "types"), { recursive: true });
239
+ const typesDir = path.join(kiciDir, "types");
240
+ await mkdir(typesDir, { recursive: true });
238
241
  logger.info(pc.gray("Created .kici/types/ for generated type declarations"));
239
242
  }
240
243
  /** Run the given package manager's install in `dir`, with pnpm's build-gate flag. */
@@ -363,6 +366,28 @@ async function generateTsConfig() {
363
366
  baseConfig.compilerOptions.paths = typeScriptPaths;
364
367
  return JSON.stringify(baseConfig, null, 2) + "\n";
365
368
  }
369
+ /** The single entry `kici init` scaffolds into `.kici/.gitignore`. */
370
+ const KICI_GITIGNORE_TEMPLATE = `# Generated type declarations (\`kici types\` / authenticated \`kici compile\`).
371
+ # A snapshot of one org's secret keys — a local development aid, not source.
372
+ types/
373
+ `;
374
+ /**
375
+ * Scaffold `.kici/.gitignore` so the generated `types/` declarations stay
376
+ * untracked. Never overwrites an existing file — the customer may have edited
377
+ * it. Deliberately ignores only `types/`, NOT `kici.lock.json` (the
378
+ * orchestrator fetches the lock from the repo, so it genuinely is source).
379
+ *
380
+ * @param kiciDir - The resolved `.kici` directory path.
381
+ */
382
+ async function writeKiciGitignore(kiciDir) {
383
+ const gitignorePath = path.join(kiciDir, ".gitignore");
384
+ if (await checkExists(gitignorePath)) {
385
+ logger.info(pc.gray("Skipping .kici/.gitignore (already exists)"));
386
+ return;
387
+ }
388
+ logger.info(pc.gray("Writing .kici/.gitignore"));
389
+ await writeFile(gitignorePath, KICI_GITIGNORE_TEMPLATE, "utf-8");
390
+ }
366
391
  /**
367
392
  * Update .gitignore with .kici/ entries
368
393
  *
@@ -504,17 +529,18 @@ async function writeAgentsMd(kiciDir) {
504
529
  async function offerHookInstallation(useVerdaccio) {
505
530
  const tools = await detectHookTools();
506
531
  let selectedTool;
507
- if (tools.length === 0) if (!await confirm({
508
- message: "No pre-commit tool found. Install husky for git hooks?",
509
- default: false
510
- })) {
532
+ if (tools.length === 0) {
511
533
  if (!await confirm({
512
- message: "Add kici compile to .git/hooks/pre-commit instead?",
534
+ message: "No pre-commit tool found. Install husky for git hooks?",
513
535
  default: false
514
- })) return;
515
- selectedTool = "git";
516
- } else selectedTool = "husky";
517
- else if (tools.length === 1) {
536
+ })) {
537
+ if (!await confirm({
538
+ message: "Add kici compile to .git/hooks/pre-commit instead?",
539
+ default: false
540
+ })) return;
541
+ selectedTool = "git";
542
+ } else selectedTool = "husky";
543
+ } else if (tools.length === 1) {
518
544
  if (!await confirm({
519
545
  message: `Found ${tools[0].name}. Add kici compile hook?`,
520
546
  default: true
@@ -528,11 +554,12 @@ async function offerHookInstallation(useVerdaccio) {
528
554
  }))
529
555
  });
530
556
  const result = await installHook(selectedTool, { useVerdaccio });
531
- if (result.success) if (result.action === "skipped") logger.info(pc.yellow(`Hook already installed: ${result.message}`));
532
- else logger.info(pc.green(`Hook installed: ${result.message}`));
533
- else logger.warn(pc.yellow(`Warning: ${result.message}`));
557
+ if (result.success) {
558
+ if (result.action === "skipped") logger.info(pc.yellow(`Hook already installed: ${result.message}`));
559
+ else logger.info(pc.green(`Hook installed: ${result.message}`));
560
+ } else logger.warn(pc.yellow(`Warning: ${result.message}`));
534
561
  }
535
562
  //#endregion
536
- export { initCommand };
563
+ export { initCommand, writeKiciGitignore };
537
564
 
538
565
  //# sourceMappingURL=init.js.map
@@ -31,7 +31,7 @@ async function promptInput(prompt) {
31
31
  function checkPatExpiry(expiresAt) {
32
32
  const expiryDate = new Date(expiresAt);
33
33
  const now = /* @__PURE__ */ new Date();
34
- const daysUntilExpiry = (expiryDate.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24);
34
+ const daysUntilExpiry = (expiryDate.getTime() - now.getTime()) / 864e5;
35
35
  if (daysUntilExpiry <= 7 && daysUntilExpiry > 0) {
36
36
  console.log(pc.yellow(`\n Warning: your personal access token expires in ${Math.ceil(daysUntilExpiry)} day(s) (${expiryDate.toLocaleDateString()}).`));
37
37
  console.log(pc.yellow(" Run `kici login` again to refresh it."));
@@ -102,10 +102,11 @@ async function orchestratorsUseCommand(clusterName, options) {
102
102
  }
103
103
  return false;
104
104
  }
105
- await mergeGlobalConfig({ defaultClusters: {
105
+ const defaultClusters = {
106
106
  ...ctx.config.defaultClusters ?? {},
107
107
  [ctx.orgId]: clusterName
108
- } });
108
+ };
109
+ await mergeGlobalConfig({ defaultClusters });
109
110
  console.log(pc.green(`Default orchestrator for ${ctx.orgId} set to: ${clusterName}`));
110
111
  return true;
111
112
  }
@@ -11,6 +11,18 @@ export interface RejectOptions {
11
11
  job?: string;
12
12
  /** Match a step-scoped hold by its step index. */
13
13
  step?: string;
14
+ /**
15
+ * Match one hold by its own id. The escape hatch for an ambiguity nothing
16
+ * else resolves; the error listing prints the ids when it needs to.
17
+ */
18
+ hold?: string;
19
+ /**
20
+ * Narrow to holds of one type (`reviewer` / `timer` / `concurrency` /
21
+ * `security`). A job carrying an SDK `requireApproval` AND a security-typed
22
+ * context gate has two pending holds under one job name, and this is what
23
+ * separates them.
24
+ */
25
+ holdType?: string;
14
26
  /** Required rejection reason. */
15
27
  reason?: string;
16
28
  }
@@ -26,9 +26,12 @@ async function rejectCommand(runId, options = {}) {
26
26
  }
27
27
  const ctx = await resolveHeldRunContext();
28
28
  if (!ctx) return false;
29
- const resolution = resolveHeldRunId(await listHeldRunsForRun(ctx, runId), {
29
+ const holds = await listHeldRunsForRun(ctx, runId);
30
+ const resolution = resolveHeldRunId(holds, {
30
31
  job: options.job,
31
- step: options.step
32
+ step: options.step,
33
+ holdId: options.hold,
34
+ holdType: options.holdType
32
35
  });
33
36
  if (!resolution.ok) {
34
37
  logger.error(pc.red(resolution.error));
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Build the `kici report` bundle.
3
+ *
4
+ * Writes the same archive layout the orchestrator's debug bundle uses, so
5
+ * `kici-admin inspect-bundle` reads either one, but collects from the workflow
6
+ * author's side: their config, their project, and the run they are reporting
7
+ * about — rather than an orchestrator's own logs and state.
8
+ *
9
+ * Every collector runs through `runCollector`, which turns a throw into a
10
+ * recorded `error` entry instead of losing the whole bundle. That matters twice
11
+ * over: a customer with one broken collector still gets a shareable report, and
12
+ * the per-collector outcome is itself a product feature — the manifest's
13
+ * `collectionReport` tells the reader which sections are trustworthy.
14
+ */
15
+ import { z } from 'zod';
16
+ import type { ProbeOutcome } from '../doctor.js';
17
+ /** Outcome of one collector. */
18
+ export declare const CollectionStatus: z.ZodEnum<{
19
+ empty: "empty";
20
+ error: "error";
21
+ ok: "ok";
22
+ skipped: "skipped";
23
+ }>;
24
+ export type CollectionStatus = z.infer<typeof CollectionStatus>;
25
+ export interface CollectionEntry {
26
+ collector: string;
27
+ status: CollectionStatus;
28
+ note?: string;
29
+ }
30
+ /** Bundle-format version, bumped when the archive layout changes. */
31
+ export declare const REPORT_BUNDLE_VERSION = "1.0";
32
+ export interface ReportManifest {
33
+ version: string;
34
+ generated_at: string;
35
+ bundle_id: string;
36
+ node_version: string;
37
+ platform: string;
38
+ metadata: Record<string, string>;
39
+ redacted: boolean;
40
+ collectionReport: CollectionEntry[];
41
+ }
42
+ /** One run's diagnostic material, as the dashboard client returns it. */
43
+ export interface RunMaterial {
44
+ detail: unknown;
45
+ logs: string;
46
+ }
47
+ /**
48
+ * Injectable seams, mirroring `DoctorDeps`, so every path is unit-testable
49
+ * without real IO or network.
50
+ */
51
+ export interface ReportBundleDeps {
52
+ loadConfig: () => Promise<Record<string, unknown>>;
53
+ probe: () => Promise<ProbeOutcome | null>;
54
+ readProject: (kiciDir: string) => Promise<Record<string, unknown>>;
55
+ fetchRun: (runId: string) => Promise<RunMaterial>;
56
+ now: () => Date;
57
+ }
58
+ export interface ReportBundleOptions {
59
+ outputPath: string;
60
+ kiciDir: string;
61
+ runId?: string;
62
+ metadata: Record<string, string>;
63
+ /** When false the bundle is assembled without scrubbing free text. */
64
+ redact: boolean;
65
+ deps?: Partial<ReportBundleDeps>;
66
+ }
67
+ export interface ReportBundleResult {
68
+ path: string;
69
+ sha256: string;
70
+ bundleId: string;
71
+ fileCount: number;
72
+ collectionReport: CollectionEntry[];
73
+ }
74
+ /**
75
+ * Collect and write one report bundle.
76
+ *
77
+ * The body stays a narrative caller: each section is one `runCollector` call
78
+ * whose result is appended to the archive, so adding a collector never grows
79
+ * the control flow.
80
+ */
81
+ export declare function createReportBundle(options: ReportBundleOptions): Promise<ReportBundleResult>;
82
+ //# sourceMappingURL=collect.d.ts.map
@@ -0,0 +1,234 @@
1
+ import "../../rolldown-runtime-ClRpJifh.js";
2
+ import { collectIdentity } from "./identity.js";
3
+ import * as path$1 from "node:path";
4
+ import * as fs$1 from "node:fs";
5
+ import * as os$1 from "node:os";
6
+ import { z } from "zod";
7
+ import { createHash, randomUUID } from "node:crypto";
8
+ import { ZipArchive } from "archiver";
9
+ import { redactConfig, scrubText } from "@kici-dev/core/diagnostics-redaction";
10
+ //#region src/commands/report/collect.ts
11
+ /**
12
+ * Build the `kici report` bundle.
13
+ *
14
+ * Writes the same archive layout the orchestrator's debug bundle uses, so
15
+ * `kici-admin inspect-bundle` reads either one, but collects from the workflow
16
+ * author's side: their config, their project, and the run they are reporting
17
+ * about — rather than an orchestrator's own logs and state.
18
+ *
19
+ * Every collector runs through `runCollector`, which turns a throw into a
20
+ * recorded `error` entry instead of losing the whole bundle. That matters twice
21
+ * over: a customer with one broken collector still gets a shareable report, and
22
+ * the per-collector outcome is itself a product feature — the manifest's
23
+ * `collectionReport` tells the reader which sections are trustworthy.
24
+ */
25
+ /** Outcome of one collector. */
26
+ const CollectionStatus = z.enum([
27
+ "ok",
28
+ "empty",
29
+ "error",
30
+ "skipped"
31
+ ]);
32
+ /** Bundle-format version, bumped when the archive layout changes. */
33
+ const REPORT_BUNDLE_VERSION = "1.0";
34
+ /**
35
+ * Read the project's own KiCI state: which workflows exist and what the lock
36
+ * file says. Deliberately reads metadata rather than workflow source — the
37
+ * source is the customer's code, and a diagnostic bundle should not ship it.
38
+ */
39
+ async function defaultReadProject(kiciDir) {
40
+ const workflowsDir = path$1.join(kiciDir, "workflows");
41
+ const workflows = fs$1.existsSync(workflowsDir) ? fs$1.readdirSync(workflowsDir).sort() : [];
42
+ const lockPath = path$1.join(kiciDir, "kici.lock.json");
43
+ let lock = null;
44
+ if (fs$1.existsSync(lockPath)) {
45
+ const parsed = JSON.parse(fs$1.readFileSync(lockPath, "utf-8"));
46
+ lock = {
47
+ schemaVersion: parsed.schemaVersion ?? null,
48
+ generatedAt: parsed.generatedAt ?? null,
49
+ workflowCount: Array.isArray(parsed.workflows) ? parsed.workflows.length : null
50
+ };
51
+ }
52
+ return {
53
+ kiciDir,
54
+ exists: fs$1.existsSync(kiciDir),
55
+ workflows,
56
+ lock
57
+ };
58
+ }
59
+ const DEFAULT_DEPS = {
60
+ loadConfig: async () => {
61
+ const { loadGlobalConfig } = await import("../../remote/config.js");
62
+ return await loadGlobalConfig();
63
+ },
64
+ probe: async () => {
65
+ const { loadGlobalConfig } = await import("../../remote/config.js");
66
+ const { DashboardClient, DashboardClientError } = await import("../../remote/dashboard-client.js");
67
+ const config = await loadGlobalConfig();
68
+ if (!config.activeOrgId || !(config.pat ?? config.token)) return null;
69
+ try {
70
+ return {
71
+ ok: true,
72
+ infra: await DashboardClient.fromConfig(config).getInfrastructure()
73
+ };
74
+ } catch (err) {
75
+ if (err instanceof DashboardClientError) return {
76
+ ok: false,
77
+ kind: err.kind,
78
+ message: err.message
79
+ };
80
+ throw err;
81
+ }
82
+ },
83
+ readProject: defaultReadProject,
84
+ fetchRun: async (runId) => {
85
+ const { loadGlobalConfig } = await import("../../remote/config.js");
86
+ const { DashboardClient } = await import("../../remote/dashboard-client.js");
87
+ const detail = await DashboardClient.fromConfig(await loadGlobalConfig()).getRunDetail(runId);
88
+ return {
89
+ detail,
90
+ logs: JSON.stringify(detail, null, 2)
91
+ };
92
+ },
93
+ now: () => /* @__PURE__ */ new Date()
94
+ };
95
+ /**
96
+ * Run one collector, recording its outcome instead of letting it abort the
97
+ * bundle. `empty` and `error` are the two signals the dev-side gap filer reads.
98
+ */
99
+ async function runCollector(report, collector, fn, scrub) {
100
+ try {
101
+ const value = await fn();
102
+ const isEmpty = value === null || value === void 0 || Array.isArray(value) && value.length === 0 || typeof value === "string" && value.length === 0;
103
+ report.push({
104
+ collector,
105
+ status: isEmpty ? "empty" : "ok"
106
+ });
107
+ return value;
108
+ } catch (err) {
109
+ report.push({
110
+ collector,
111
+ status: "error",
112
+ note: scrub(err instanceof Error ? err.message : String(err))
113
+ });
114
+ return;
115
+ }
116
+ }
117
+ /** Machine facts about the host the report was produced on. */
118
+ function systemInfo() {
119
+ return {
120
+ platform: os$1.platform(),
121
+ release: os$1.release(),
122
+ arch: os$1.arch(),
123
+ nodeVersion: process.version,
124
+ cpuCount: os$1.cpus().length,
125
+ totalMemory: os$1.totalmem(),
126
+ freeMemory: os$1.freemem(),
127
+ hostname: os$1.hostname()
128
+ };
129
+ }
130
+ /** sha256 over the finished file, so the customer can quote a digest. */
131
+ async function digestOf(file) {
132
+ const hash = createHash("sha256");
133
+ await new Promise((resolve, reject) => {
134
+ const stream = fs$1.createReadStream(file);
135
+ stream.on("data", (chunk) => hash.update(chunk));
136
+ stream.on("end", resolve);
137
+ stream.on("error", reject);
138
+ });
139
+ return hash.digest("hex");
140
+ }
141
+ /**
142
+ * Collect and write one report bundle.
143
+ *
144
+ * The body stays a narrative caller: each section is one `runCollector` call
145
+ * whose result is appended to the archive, so adding a collector never grows
146
+ * the control flow.
147
+ */
148
+ async function createReportBundle(options) {
149
+ const deps = {
150
+ ...DEFAULT_DEPS,
151
+ ...options.deps
152
+ };
153
+ const scrub = options.redact ? scrubText : (s) => s;
154
+ const report = [];
155
+ const bundleId = randomUUID().replace(/-/g, "").slice(0, 24);
156
+ const dir = path$1.dirname(options.outputPath);
157
+ if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
158
+ const output = fs$1.createWriteStream(options.outputPath);
159
+ const archive = new ZipArchive({ zlib: { level: 6 } });
160
+ const finalized = new Promise((resolve, reject) => {
161
+ output.on("close", resolve);
162
+ archive.on("error", reject);
163
+ output.on("error", reject);
164
+ });
165
+ archive.pipe(output);
166
+ let fileCount = 0;
167
+ const append = (body, name) => {
168
+ archive.append(scrub(JSON.stringify(body, null, 2)), { name });
169
+ fileCount += 1;
170
+ };
171
+ let probe;
172
+ try {
173
+ probe = await deps.probe();
174
+ report.push(probe === null ? {
175
+ collector: "probe",
176
+ status: "skipped",
177
+ note: "not authenticated"
178
+ } : {
179
+ collector: "probe",
180
+ status: "ok"
181
+ });
182
+ } catch (err) {
183
+ report.push({
184
+ collector: "probe",
185
+ status: "error",
186
+ note: scrub(err instanceof Error ? err.message : String(err))
187
+ });
188
+ }
189
+ const identity = await runCollector(report, "identity", () => Promise.resolve(collectIdentity(probe ?? null)), scrub);
190
+ if (identity) append(identity, "identity.json");
191
+ const config = await runCollector(report, "config", () => deps.loadConfig(), scrub);
192
+ if (config !== void 0) append(options.redact ? redactConfig(config) : config, "config/config.json");
193
+ const project = await runCollector(report, "project", () => deps.readProject(options.kiciDir), scrub);
194
+ if (project !== void 0) append(project, "project/project.json");
195
+ const system = await runCollector(report, "system", () => Promise.resolve(systemInfo()), scrub);
196
+ if (system !== void 0) append(system, "system/info.json");
197
+ if (options.runId) {
198
+ const run = await runCollector(report, "run", () => deps.fetchRun(options.runId), scrub);
199
+ if (run !== void 0) {
200
+ append(run.detail, `runs/${options.runId}/detail.json`);
201
+ archive.append(scrub(run.logs), { name: `runs/${options.runId}/logs.txt` });
202
+ fileCount += 1;
203
+ }
204
+ } else report.push({
205
+ collector: "run",
206
+ status: "skipped",
207
+ note: "no --run given"
208
+ });
209
+ const manifest = {
210
+ version: "1.0",
211
+ generated_at: deps.now().toISOString(),
212
+ bundle_id: bundleId,
213
+ node_version: process.version,
214
+ platform: process.platform,
215
+ metadata: options.metadata,
216
+ redacted: options.redact,
217
+ collectionReport: report
218
+ };
219
+ archive.append(scrub(JSON.stringify(manifest, null, 2)), { name: "manifest.json" });
220
+ fileCount += 1;
221
+ await archive.finalize();
222
+ await finalized;
223
+ return {
224
+ path: options.outputPath,
225
+ sha256: await digestOf(options.outputPath),
226
+ bundleId,
227
+ fileCount,
228
+ collectionReport: report
229
+ };
230
+ }
231
+ //#endregion
232
+ export { CollectionStatus, REPORT_BUNDLE_VERSION, createReportBundle };
233
+
234
+ //# sourceMappingURL=collect.js.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Version and environment context for an issue report.
3
+ *
4
+ * A report without version context costs a support round-trip before anyone can
5
+ * even reproduce the problem, so this is collected first and never fails: the
6
+ * client-side half always resolves, and the component versions are folded in
7
+ * only when the authenticated probe actually reached the Platform.
8
+ */
9
+ import type { ProbeOutcome } from '../doctor.js';
10
+ /** One orchestrator the caller's org has connected, and what it is running. */
11
+ export interface OrchestratorIdentity {
12
+ clusterName: string;
13
+ version: string | null;
14
+ mode: string | null;
15
+ connected: boolean;
16
+ }
17
+ export interface ReportIdentity {
18
+ kiciCliVersion: string;
19
+ nodeVersion: string;
20
+ platform: string;
21
+ arch: string;
22
+ /** The wire protocol this CLI speaks. */
23
+ protocolVersion: number;
24
+ /**
25
+ * Every orchestrator the probe saw, not just the first: a report about a
26
+ * routing problem is usually about which of several answered.
27
+ */
28
+ orchestrators: OrchestratorIdentity[];
29
+ /**
30
+ * The newest KiCI version the Platform knows about, when it told us. This is
31
+ * an upgrade hint, NOT the Platform's own version — the diagnostics response
32
+ * carries no such field, and inventing one would put a fabricated value in a
33
+ * bundle a human will read as fact.
34
+ */
35
+ latestKnownVersion?: string;
36
+ /** Why component versions are missing, when the probe could not run. */
37
+ probeError?: string;
38
+ }
39
+ /**
40
+ * Build the identity block.
41
+ *
42
+ * `probe` is the same `ProbeOutcome` `kici doctor` already performs — one
43
+ * authenticated infrastructure read — passed in rather than re-fetched so a
44
+ * report never issues a second round trip for data the caller may already
45
+ * hold, and so every path is testable without network.
46
+ */
47
+ export declare function collectIdentity(probe: ProbeOutcome | null): ReportIdentity;
48
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1,49 @@
1
+ import "../../rolldown-runtime-ClRpJifh.js";
2
+ import { PROTOCOL_VERSION } from "@kici-dev/engine";
3
+ //#region src/commands/report/identity.ts
4
+ /**
5
+ * Version and environment context for an issue report.
6
+ *
7
+ * A report without version context costs a support round-trip before anyone can
8
+ * even reproduce the problem, so this is collected first and never fails: the
9
+ * client-side half always resolves, and the component versions are folded in
10
+ * only when the authenticated probe actually reached the Platform.
11
+ */
12
+ /**
13
+ * Build the identity block.
14
+ *
15
+ * `probe` is the same `ProbeOutcome` `kici doctor` already performs — one
16
+ * authenticated infrastructure read — passed in rather than re-fetched so a
17
+ * report never issues a second round trip for data the caller may already
18
+ * hold, and so every path is testable without network.
19
+ */
20
+ function collectIdentity(probe) {
21
+ const identity = {
22
+ kiciCliVersion: "0.6.1",
23
+ nodeVersion: process.version,
24
+ platform: process.platform,
25
+ arch: process.arch,
26
+ protocolVersion: PROTOCOL_VERSION,
27
+ orchestrators: []
28
+ };
29
+ if (probe === null) {
30
+ identity.probeError = "not authenticated; component versions unavailable";
31
+ return identity;
32
+ }
33
+ if (!probe.ok) {
34
+ identity.probeError = `${probe.kind}: ${probe.message}`;
35
+ return identity;
36
+ }
37
+ identity.orchestrators = probe.infra.orchestrators.map((o) => ({
38
+ clusterName: o.clusterName ?? "(unnamed)",
39
+ version: o.version ?? null,
40
+ mode: o.mode ?? null,
41
+ connected: o.connected
42
+ }));
43
+ if (probe.infra.latestVersion) identity.latestKnownVersion = probe.infra.latestVersion;
44
+ return identity;
45
+ }
46
+ //#endregion
47
+ export { collectIdentity };
48
+
49
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `kici report` — produce a redacted diagnostic bundle to share when reporting
3
+ * an issue, and optionally upload it privately to KiCI.
4
+ *
5
+ * Writing and sending are separate acts. The default writes a ZIP next to you
6
+ * and tells you where it is, so you can open it and see exactly what you would
7
+ * be sharing; `--upload` is the explicit second step.
8
+ */
9
+ import { type ReportBundleDeps } from './collect.js';
10
+ import { type UploadDeps } from './upload.js';
11
+ export interface ReportOptions {
12
+ output?: string;
13
+ run?: string;
14
+ /** Repeatable `key=value` pairs. */
15
+ metadata: string[];
16
+ redact: boolean;
17
+ upload?: boolean;
18
+ message?: string;
19
+ email?: string;
20
+ kiciDir: string;
21
+ deps?: Partial<ReportBundleDeps>;
22
+ uploadDeps?: UploadDeps;
23
+ }
24
+ export interface ReportListOptions {
25
+ json?: boolean;
26
+ deps?: {
27
+ listIssueReports: () => Promise<{
28
+ reports: ReportRow[];
29
+ }>;
30
+ };
31
+ }
32
+ export interface ReportRow {
33
+ ref: string;
34
+ bundleId: string;
35
+ byteSize: number;
36
+ status: string;
37
+ createdAt: string;
38
+ userId: string;
39
+ message: string | null;
40
+ }
41
+ export interface ReportWithdrawOptions {
42
+ ref: string;
43
+ deps?: {
44
+ withdrawIssueReport: (ref: string) => Promise<{
45
+ ref: string;
46
+ deleted: boolean;
47
+ }>;
48
+ };
49
+ }
50
+ /**
51
+ * Parse repeatable `--metadata key=value` into a record.
52
+ *
53
+ * The accumulator is null-prototyped so a `__proto__=x` pair is stored as an
54
+ * ordinary key rather than silently vanishing into the prototype — a dropped
55
+ * pair in a diagnostic bundle is a lie about what the reporter attached.
56
+ */
57
+ export declare function parseMetadata(pairs: string[]): Record<string, string>;
58
+ /** Default bundle path: timestamped, in the working directory. */
59
+ export declare function defaultOutputPath(now: Date): string;
60
+ export declare function reportCommand(options: ReportOptions): Promise<boolean>;
61
+ export declare function reportListCommand(options: ReportListOptions): Promise<boolean>;
62
+ export declare function reportWithdrawCommand(options: ReportWithdrawOptions): Promise<boolean>;
63
+ //# sourceMappingURL=index.d.ts.map