@kici-dev/compiler 0.1.26 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/headless-detect.d.ts +14 -5
- package/dist/auth/headless-detect.js +50 -9
- package/dist/cli-banner.d.ts +6 -3
- package/dist/cli-banner.js +13 -5
- package/dist/cli.js +180 -48
- package/dist/commands/check-mode.d.ts +1 -1
- package/dist/commands/compile.js +31 -7
- package/dist/commands/diagnostics.js +14 -2
- package/dist/commands/doctor.d.ts +64 -0
- package/dist/commands/doctor.js +413 -0
- package/dist/commands/endpoints.js +6 -0
- package/dist/commands/fixture.js +13 -0
- package/dist/commands/hook.js +3 -2
- package/dist/commands/index.d.ts +13 -1
- package/dist/commands/index.js +10 -3
- package/dist/commands/init-host-os.d.ts +34 -0
- package/dist/commands/init-host-os.js +44 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +160 -37
- package/dist/commands/local-trust-root.d.ts +6 -0
- package/dist/commands/local-trust-root.js +52 -0
- package/dist/commands/local.d.ts +65 -0
- package/dist/commands/local.js +206 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +41 -1
- package/dist/commands/logout.js +7 -0
- package/dist/commands/notifications.d.ts +61 -0
- package/dist/commands/notifications.js +274 -0
- package/dist/commands/preview.js +9 -2
- package/dist/commands/run-banner.d.ts +31 -0
- package/dist/commands/run-banner.js +25 -0
- package/dist/commands/run-routed.d.ts +53 -0
- package/dist/commands/run-routed.js +185 -0
- package/dist/commands/run.d.ts +0 -17
- package/dist/commands/run.js +44 -58
- package/dist/commands/runs/artifacts/download.d.ts +24 -0
- package/dist/commands/runs/artifacts/download.js +154 -0
- package/dist/commands/runs/artifacts/list.d.ts +5 -0
- package/dist/commands/runs/artifacts/list.js +47 -0
- package/dist/commands/runs/cancel.js +2 -2
- package/dist/commands/runs/list.js +8 -2
- package/dist/commands/secrets-list.d.ts +2 -2
- package/dist/commands/secrets-list.js +4 -4
- package/dist/commands/types.d.ts +3 -3
- package/dist/commands/types.js +4 -4
- package/dist/commands/verify-attestation.d.ts +3 -1
- package/dist/commands/verify-attestation.js +18 -7
- package/dist/errors/formatter.d.ts +14 -1
- package/dist/errors/formatter.js +13 -3
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +3 -2
- package/dist/errors/source-location.d.ts +20 -0
- package/dist/errors/source-location.js +45 -0
- package/dist/execution/executor.js +32 -12
- package/dist/fixtures/defaults/index.js +2 -1
- package/dist/generators/secrets-dts.d.ts +9 -9
- package/dist/generators/secrets-dts.js +12 -12
- package/dist/llm-context/llms-architecture.txt +50 -21
- package/dist/llm-context/llms-cli.txt +2667 -1454
- package/dist/llm-context/llms-features.txt +484 -414
- package/dist/llm-context/llms-full.txt +6703 -4687
- package/dist/llm-context/llms-getting-started.txt +311 -55
- package/dist/llm-context/llms-patterns.txt +17 -29
- package/dist/llm-context/llms-providers.txt +10 -1
- package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
- package/dist/llm-context/llms-sdk.txt +596 -2157
- package/dist/llm-context/llms.txt +31 -17
- package/dist/local-plane/orchestrator-process.d.ts +70 -0
- package/dist/local-plane/orchestrator-process.js +129 -0
- package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
- package/dist/local-plane/os-mismatch-hint.js +34 -0
- package/dist/local-plane/paths.d.ts +39 -0
- package/dist/local-plane/paths.js +54 -0
- package/dist/local-plane/plane-liveness.d.ts +68 -0
- package/dist/local-plane/plane-liveness.js +145 -0
- package/dist/local-plane/plane-manager.d.ts +193 -0
- package/dist/local-plane/plane-manager.js +480 -0
- package/dist/local-plane/plane-seed.d.ts +76 -0
- package/dist/local-plane/plane-seed.js +59 -0
- package/dist/local-plane/plane-trigger.d.ts +71 -0
- package/dist/local-plane/plane-trigger.js +110 -0
- package/dist/local-plane/platform-attach.d.ts +53 -0
- package/dist/local-plane/platform-attach.js +107 -0
- package/dist/local-plane/port-holder.d.ts +78 -0
- package/dist/local-plane/port-holder.js +227 -0
- package/dist/local-plane/postgres.d.ts +56 -0
- package/dist/local-plane/postgres.js +187 -0
- package/dist/local-plane/resolve-plane.d.ts +37 -0
- package/dist/local-plane/resolve-plane.js +76 -0
- package/dist/local-plane/run-follow.d.ts +61 -0
- package/dist/local-plane/run-follow.js +151 -0
- package/dist/local-plane/scaler-config.d.ts +68 -0
- package/dist/local-plane/scaler-config.js +132 -0
- package/dist/local-plane/secret-seed.d.ts +59 -0
- package/dist/local-plane/secret-seed.js +112 -0
- package/dist/local-plane/source-provider.d.ts +43 -0
- package/dist/local-plane/source-provider.js +159 -0
- package/dist/local-plane/trusted-routing.d.ts +32 -0
- package/dist/local-plane/trusted-routing.js +47 -0
- package/dist/lockfile/generator.d.ts +14 -1
- package/dist/lockfile/generator.js +103 -37
- package/dist/lockfile/index.d.ts +3 -1
- package/dist/lockfile/index.js +3 -2
- package/dist/lockfile/purity-diagnostics.d.ts +31 -0
- package/dist/lockfile/purity-diagnostics.js +52 -0
- package/dist/postinstall.js +2 -1
- package/dist/remote/artifact-extract.d.ts +8 -0
- package/dist/remote/artifact-extract.js +58 -0
- package/dist/remote/config.d.ts +21 -3
- package/dist/remote/config.js +24 -5
- package/dist/remote/dashboard-client.d.ts +46 -9
- package/dist/remote/dashboard-client.js +54 -11
- package/dist/remote/fs-case.d.ts +21 -0
- package/dist/remote/fs-case.js +45 -0
- package/dist/remote/history.d.ts +9 -3
- package/dist/remote/history.js +79 -10
- package/dist/remote/local-repo-identity.d.ts +1 -1
- package/dist/remote/local-repo-identity.js +2 -1
- package/dist/remote/notifications-client.d.ts +149 -0
- package/dist/remote/notifications-client.js +103 -0
- package/dist/remote/oauth.d.ts +13 -0
- package/dist/remote/oauth.js +31 -5
- package/dist/remote/output/summary.js +76 -14
- package/dist/remote/platform-client.d.ts +1 -1
- package/dist/remote/render.js +30 -9
- package/dist/{local-executor → remote}/secret-loader.d.ts +1 -1
- package/dist/{local-executor → remote}/secret-loader.js +4 -4
- package/dist/remote/secret-upload.d.ts +1 -1
- package/dist/remote/secret-upload.js +2 -2
- package/dist/remote/uploader.d.ts +1 -1
- package/dist/remote/uploader.js +7 -6
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +8 -8
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +2 -2
- package/dist/templates/package-json.d.ts +15 -0
- package/dist/templates/package-json.js +26 -5
- package/dist/test-runner/dry-run.d.ts +2 -1
- package/dist/test-runner/dry-run.js +18 -1
- package/dist/test-runner/event-types.d.ts +2 -0
- package/dist/test-runner/event-types.js +3 -0
- package/dist/test-runner/job-executor.d.ts +1 -0
- package/dist/test-runner/job-executor.js +82 -44
- package/dist/test-runner/output-formatter.d.ts +5 -2
- package/dist/test-runner/output-formatter.js +6 -4
- package/dist/test-runner/payload-builder.d.ts +7 -1
- package/dist/test-runner/payload-builder.js +11 -2
- package/dist/test-runner/rule-evaluator.d.ts +7 -3
- package/dist/test-runner/rule-evaluator.js +13 -12
- package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
- package/dist/test-runner/step-context.d.ts +23 -5
- package/dist/test-runner/step-context.js +29 -119
- package/dist/types.d.ts +26 -7
- package/dist/types.js +4 -2
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.js +2 -1
- package/dist/validation/typecheck.d.ts +28 -0
- package/dist/validation/typecheck.js +110 -0
- package/dist/validation/validator.d.ts +9 -4
- package/dist/validation/validator.js +38 -44
- package/package.json +13 -13
- package/sbom.spdx.json +9311 -1528
- package/dist/assets/api-TJJVHrjC.json +0 -118
- package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
- package/dist/assets/package-BpQF9kR8.json +0 -74
- package/dist/assets/package-Ceo2h27X.json +0 -89
- package/dist/assets/source_context-D0atuL28.json +0 -20
- package/dist/assets/type-BFqO8SCZ.json +0 -202
- package/dist/commands/cancel.d.ts +0 -22
- package/dist/commands/detect-package-manager.d.ts +0 -42
- package/dist/commands/held-run-resolve.d.ts +0 -50
- package/dist/commands/status.d.ts +0 -34
- package/dist/commands/test.d.ts +0 -88
- package/dist/errors/codes.d.ts +0 -25
- package/dist/local-executor/dag-scheduler.d.ts +0 -44
- package/dist/local-executor/dag-scheduler.js +0 -183
- package/dist/local-executor/index.d.ts +0 -23
- package/dist/local-executor/index.js +0 -378
- package/dist/local-executor/job-runner.d.ts +0 -47
- package/dist/local-executor/job-runner.js +0 -411
- package/dist/local-executor/materializer.d.ts +0 -44
- package/dist/local-executor/materializer.js +0 -132
- package/dist/local-executor/output-streamer.d.ts +0 -31
- package/dist/local-executor/output-streamer.js +0 -168
- package/dist/local-executor/payload-generator.d.ts +0 -16
- package/dist/local-executor/payload-generator.js +0 -138
- package/dist/local-executor/picker.d.ts +0 -33
- package/dist/local-executor/picker.js +0 -109
- package/dist/local-executor/to-event-payload.d.ts +0 -16
- package/dist/local-executor/to-event-payload.js +0 -21
- package/dist/local-executor/types.d.ts +0 -93
- package/dist/local-executor/types.js +0 -2
- package/dist/local-executor/workflow-lock.d.ts +0 -82
- package/dist/local-executor/workflow-lock.js +0 -0
- package/dist/package-F7UXSDHW.json +0 -74
- package/dist/remote/client.d.ts +0 -210
- package/dist/remote/observer.d.ts +0 -81
- package/dist/test-runner/summary.d.ts +0 -7
- /package/dist/{local-executor → test-runner}/runs-on-display.d.ts +0 -0
package/dist/commands/login.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface LoginOptions {
|
|
|
9
9
|
routingKey?: string;
|
|
10
10
|
/** Force device authorization flow regardless of environment */
|
|
11
11
|
device?: boolean;
|
|
12
|
+
/** Skip the post-login "attach the local dev plane?" prompt (scripted logins). */
|
|
13
|
+
noAttachPrompt?: boolean;
|
|
12
14
|
}
|
|
13
15
|
/**
|
|
14
16
|
* Authenticate with KiCI.
|
package/dist/commands/login.js
CHANGED
|
@@ -2,6 +2,7 @@ import "../rolldown-runtime-ClRpJifh.js";
|
|
|
2
2
|
import { getConfigPath, loadGlobalConfig, mergeGlobalConfig, saveGlobalConfig } from "../remote/config.js";
|
|
3
3
|
import { deviceFlow, exchangeTokenForPat, pkceFlow } from "../remote/oauth.js";
|
|
4
4
|
import "../remote/prod-defaults.js";
|
|
5
|
+
import { attachPlane, planeStatus } from "../local-plane/plane-manager.js";
|
|
5
6
|
import { isHeadless } from "../auth/headless-detect.js";
|
|
6
7
|
import pc from "picocolors";
|
|
7
8
|
import { toErrorMessage } from "@kici-dev/core";
|
|
@@ -52,7 +53,7 @@ async function oauthLogin(options) {
|
|
|
52
53
|
const clientId = process.env.KICI_OIDC_CLIENT_ID || "kici-cli";
|
|
53
54
|
console.log(pc.cyan("\n Step 1/4: Detecting environment..."));
|
|
54
55
|
const browserCmdSet = !!process.env.KICI_BROWSER_CMD;
|
|
55
|
-
const useDeviceFlow = options.device || !browserCmdSet && isHeadless();
|
|
56
|
+
const useDeviceFlow = options.device || !browserCmdSet && await isHeadless();
|
|
56
57
|
const flowName = useDeviceFlow ? "device authorization" : "PKCE (browser)";
|
|
57
58
|
console.log(pc.gray(` Using ${flowName} flow`));
|
|
58
59
|
console.log(pc.cyan(`\n Step 2/4: Authenticating with IdP (${flowName})...`));
|
|
@@ -92,9 +93,48 @@ async function oauthLogin(options) {
|
|
|
92
93
|
console.log(pc.gray(` PAT expires: ${expiryDate}`));
|
|
93
94
|
console.log(pc.gray(` Config saved to ${configPath}`));
|
|
94
95
|
checkPatExpiry(patResult.expiresAt);
|
|
96
|
+
await maybePromptAttach(options, {
|
|
97
|
+
apiBase: platformUrl,
|
|
98
|
+
pat: patResult.token,
|
|
99
|
+
orgId: next.activeOrgId
|
|
100
|
+
});
|
|
95
101
|
return true;
|
|
96
102
|
}
|
|
97
103
|
/**
|
|
104
|
+
* After a successful login, prompt an interactive user to attach their local
|
|
105
|
+
* dev plane to the Platform. Skipped entirely for non-TTY / piped input, when
|
|
106
|
+
* `--no-attach` is set, when no org is selected (nothing to attach against), or
|
|
107
|
+
* when a plane is already attached. Any failure here is non-fatal — login has
|
|
108
|
+
* already succeeded.
|
|
109
|
+
*/
|
|
110
|
+
async function maybePromptAttach(options, ctx) {
|
|
111
|
+
try {
|
|
112
|
+
if (options.noAttachPrompt) return;
|
|
113
|
+
if (!process.stdin.isTTY) return;
|
|
114
|
+
if (!ctx.orgId) {
|
|
115
|
+
console.log(pc.gray("\n Tip: select an org (`kici org use <org>`) then `kici local attach` to use real Platform identity for `kici run --local`."));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if ((await planeStatus()).mode === "hybrid") return;
|
|
119
|
+
console.log("\n You don't have your local dev plane attached to the Platform.\n Attach it so `kici run --local` uses real Platform secrets & identity?");
|
|
120
|
+
const answer = (await promptInput(" Attach now? [Y/n] ")).toLowerCase();
|
|
121
|
+
if (answer === "n" || answer === "no") {
|
|
122
|
+
console.log(pc.gray(" Left offline. Re-attach later with `kici local attach`."));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
console.log(pc.cyan("\n Attaching the local dev plane…"));
|
|
126
|
+
const status = await attachPlane({
|
|
127
|
+
apiBase: ctx.apiBase,
|
|
128
|
+
pat: ctx.pat,
|
|
129
|
+
orgId: ctx.orgId
|
|
130
|
+
});
|
|
131
|
+
console.log(pc.green(` ✓ Attached (hybrid) at ${status.url ?? ""} — \`kici run --local\` is now connected.`));
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.log(pc.yellow(` Could not attach the local dev plane: ${toErrorMessage(err)}`));
|
|
134
|
+
console.log(pc.gray(" Login succeeded; attach later with `kici local attach`."));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
98
138
|
* Authenticate with KiCI.
|
|
99
139
|
*
|
|
100
140
|
* - With `--token`, saves the API key directly (legacy flow)
|
package/dist/commands/logout.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { getConfigPath, loadGlobalConfig, saveGlobalConfig } from "../remote/config.js";
|
|
3
|
+
import { detachPlane, planeStatus } from "../local-plane/plane-manager.js";
|
|
3
4
|
import pc from "picocolors";
|
|
4
5
|
//#region src/commands/logout.ts
|
|
5
6
|
/**
|
|
@@ -37,6 +38,12 @@ async function logoutCommand() {
|
|
|
37
38
|
} catch {
|
|
38
39
|
console.log(pc.yellow("Warning: could not revoke PAT on server (will expire automatically)."));
|
|
39
40
|
}
|
|
41
|
+
try {
|
|
42
|
+
if ((await planeStatus()).mode === "hybrid") {
|
|
43
|
+
await detachPlane({ pat: config.pat });
|
|
44
|
+
console.log(pc.gray("Local dev plane detached (back to offline)."));
|
|
45
|
+
}
|
|
46
|
+
} catch {}
|
|
40
47
|
const cleanConfig = { ...config };
|
|
41
48
|
delete cleanConfig.pat;
|
|
42
49
|
delete cleanConfig.patId;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kici notifications commands
|
|
3
|
+
*
|
|
4
|
+
* Customer self-service management of an org's notification configuration —
|
|
5
|
+
* channels, subscriptions, and the Layer 1 Slack-identity roster — under the
|
|
6
|
+
* logged-in user's PAT against the hosted Platform's org-scoped HTTP API. This
|
|
7
|
+
* is a tenant-plane developer operation, so it lives in the public `kici` CLI
|
|
8
|
+
* (like `kici org` / `kici verify-attestation`), never the DB-direct
|
|
9
|
+
* `kici-platform-admin`.
|
|
10
|
+
*/
|
|
11
|
+
/** Options common to every notifications subcommand. */
|
|
12
|
+
export interface NotificationsCommonOptions {
|
|
13
|
+
/** Target organization id (overrides the saved active org). */
|
|
14
|
+
org?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Options for the `list` leaves. */
|
|
17
|
+
export interface NotificationsListOptions extends NotificationsCommonOptions {
|
|
18
|
+
json?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Options for `channels add`. */
|
|
21
|
+
export interface ChannelsAddOptions extends NotificationsCommonOptions {
|
|
22
|
+
type?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
connection?: string;
|
|
25
|
+
slackChannel?: string;
|
|
26
|
+
fromName?: string;
|
|
27
|
+
replyTo?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Options for `subscriptions add`. */
|
|
30
|
+
export interface SubscriptionsAddOptions extends NotificationsCommonOptions {
|
|
31
|
+
channel?: string;
|
|
32
|
+
level?: string;
|
|
33
|
+
scope?: string;
|
|
34
|
+
scopeId?: string;
|
|
35
|
+
onStatus?: string;
|
|
36
|
+
repoGlob?: string;
|
|
37
|
+
workflowGlob?: string;
|
|
38
|
+
jobGlob?: string;
|
|
39
|
+
mentions?: string;
|
|
40
|
+
recipientOverride?: string;
|
|
41
|
+
onFailureClass?: string;
|
|
42
|
+
accumulateFor?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Options for `roster add`. */
|
|
45
|
+
export interface RosterAddOptions extends NotificationsCommonOptions {
|
|
46
|
+
connection?: string;
|
|
47
|
+
subjectKind?: string;
|
|
48
|
+
subject?: string;
|
|
49
|
+
inputForm?: string;
|
|
50
|
+
value?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function notificationsChannelsListCommand(opts: NotificationsListOptions): Promise<boolean>;
|
|
53
|
+
export declare function notificationsChannelsAddCommand(opts: ChannelsAddOptions): Promise<boolean>;
|
|
54
|
+
export declare function notificationsChannelsRemoveCommand(id: string, opts: NotificationsCommonOptions): Promise<boolean>;
|
|
55
|
+
export declare function notificationsSubscriptionsListCommand(opts: NotificationsListOptions): Promise<boolean>;
|
|
56
|
+
export declare function notificationsSubscriptionsAddCommand(opts: SubscriptionsAddOptions): Promise<boolean>;
|
|
57
|
+
export declare function notificationsSubscriptionsRemoveCommand(id: string, opts: NotificationsCommonOptions): Promise<boolean>;
|
|
58
|
+
export declare function notificationsRosterListCommand(opts: NotificationsListOptions): Promise<boolean>;
|
|
59
|
+
export declare function notificationsRosterAddCommand(opts: RosterAddOptions): Promise<boolean>;
|
|
60
|
+
export declare function notificationsRosterRemoveCommand(id: string, opts: NotificationsCommonOptions): Promise<boolean>;
|
|
61
|
+
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { DashboardClientError } from "../remote/dashboard-client.js";
|
|
3
|
+
import { NotificationChannelKind, NotificationScopeKind, NotificationSubLevel, NotificationsClient, RosterInputForm, RosterSubjectKind } from "../remote/notifications-client.js";
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
import { toErrorMessage } from "@kici-dev/core";
|
|
6
|
+
//#region src/commands/notifications.ts
|
|
7
|
+
/**
|
|
8
|
+
* kici notifications commands
|
|
9
|
+
*
|
|
10
|
+
* Customer self-service management of an org's notification configuration —
|
|
11
|
+
* channels, subscriptions, and the Layer 1 Slack-identity roster — under the
|
|
12
|
+
* logged-in user's PAT against the hosted Platform's org-scoped HTTP API. This
|
|
13
|
+
* is a tenant-plane developer operation, so it lives in the public `kici` CLI
|
|
14
|
+
* (like `kici org` / `kici verify-attestation`), never the DB-direct
|
|
15
|
+
* `kici-platform-admin`.
|
|
16
|
+
*/
|
|
17
|
+
/** Split a comma-separated flag value into a trimmed, non-empty list. */
|
|
18
|
+
function parseCsv(value) {
|
|
19
|
+
if (!value) return [];
|
|
20
|
+
return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
21
|
+
}
|
|
22
|
+
/** Print a simple padded table. */
|
|
23
|
+
function printTable(headers, rows) {
|
|
24
|
+
if (rows.length === 0) {
|
|
25
|
+
console.log(pc.gray("None."));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
29
|
+
console.log(pc.bold(headers.map((h, i) => h.padEnd(widths[i])).join(" ")));
|
|
30
|
+
for (const row of rows) console.log(row.map((c, i) => c.padEnd(widths[i])).join(" "));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load the client and run `fn`, turning any {@link DashboardClientError} into a
|
|
34
|
+
* red console message and a `false` return (the CLI exit-code convention).
|
|
35
|
+
*/
|
|
36
|
+
async function withClient(opts, fn) {
|
|
37
|
+
try {
|
|
38
|
+
return await fn(await NotificationsClient.load({ org: opts.org }));
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (err instanceof DashboardClientError) {
|
|
41
|
+
console.error(pc.red(err.message));
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
console.error(pc.red(`Notifications command failed: ${toErrorMessage(err)}`));
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Validate a value against a Zod enum, printing an actionable error on miss. */
|
|
49
|
+
function requireEnum(label, value, options) {
|
|
50
|
+
if (value === void 0) {
|
|
51
|
+
console.error(pc.red(`--${label} is required (one of: ${options.join(", ")})`));
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (!options.includes(value)) {
|
|
55
|
+
console.error(pc.red(`Invalid --${label} "${value}" (one of: ${options.join(", ")})`));
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
async function notificationsChannelsListCommand(opts) {
|
|
61
|
+
return withClient(opts, async (client) => {
|
|
62
|
+
const channels = await client.listChannels();
|
|
63
|
+
if (opts.json) {
|
|
64
|
+
console.log(JSON.stringify(channels, null, 2));
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
printTable([
|
|
68
|
+
"ID",
|
|
69
|
+
"TYPE",
|
|
70
|
+
"NAME",
|
|
71
|
+
"MANAGED"
|
|
72
|
+
], channels.map((c) => [
|
|
73
|
+
c.id,
|
|
74
|
+
c.type,
|
|
75
|
+
c.name,
|
|
76
|
+
c.managed ? "yes" : "no"
|
|
77
|
+
]));
|
|
78
|
+
return true;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function notificationsChannelsAddCommand(opts) {
|
|
82
|
+
const type = requireEnum("type", opts.type, NotificationChannelKind.options);
|
|
83
|
+
if (!type) return false;
|
|
84
|
+
if (!opts.name) {
|
|
85
|
+
console.error(pc.red("--name is required"));
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
let body;
|
|
89
|
+
if (type === NotificationChannelKind.enum.slack) {
|
|
90
|
+
if (!opts.connection || !opts.slackChannel) {
|
|
91
|
+
console.error(pc.red("slack channels require --connection <id> and --slack-channel <id>"));
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
body = {
|
|
95
|
+
type: "slack",
|
|
96
|
+
connection_id: opts.connection,
|
|
97
|
+
slack_channel_id: opts.slackChannel,
|
|
98
|
+
name: opts.name
|
|
99
|
+
};
|
|
100
|
+
} else body = {
|
|
101
|
+
type: "email",
|
|
102
|
+
name: opts.name,
|
|
103
|
+
...opts.fromName ? { from_name: opts.fromName } : {},
|
|
104
|
+
...opts.replyTo ? { reply_to: opts.replyTo } : {}
|
|
105
|
+
};
|
|
106
|
+
return withClient(opts, async (client) => {
|
|
107
|
+
const channel = await client.createChannel(body);
|
|
108
|
+
console.log(pc.green(`Channel created: ${channel.id}`));
|
|
109
|
+
return true;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async function notificationsChannelsRemoveCommand(id, opts) {
|
|
113
|
+
return withClient(opts, async (client) => {
|
|
114
|
+
await client.deleteChannel(id);
|
|
115
|
+
console.log(pc.green(`Channel removed: ${id}`));
|
|
116
|
+
return true;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async function notificationsSubscriptionsListCommand(opts) {
|
|
120
|
+
return withClient(opts, async (client) => {
|
|
121
|
+
const subs = await client.listSubscriptions();
|
|
122
|
+
if (opts.json) {
|
|
123
|
+
console.log(JSON.stringify(subs, null, 2));
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
printTable([
|
|
127
|
+
"ID",
|
|
128
|
+
"LEVEL",
|
|
129
|
+
"SCOPE",
|
|
130
|
+
"CHANNEL",
|
|
131
|
+
"ON STATUS",
|
|
132
|
+
"ENABLED"
|
|
133
|
+
], subs.map((s) => [
|
|
134
|
+
s.id,
|
|
135
|
+
s.level,
|
|
136
|
+
s.scope_id ? `${s.scope_type}:${s.scope_id}` : s.scope_type,
|
|
137
|
+
s.channel_id,
|
|
138
|
+
(s.on_status ?? []).join(","),
|
|
139
|
+
s.enabled ? "yes" : "no"
|
|
140
|
+
]));
|
|
141
|
+
return true;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** Build a create-subscription body from parsed flags (null on validation error). */
|
|
145
|
+
function buildSubscriptionBody(opts) {
|
|
146
|
+
if (!opts.channel) {
|
|
147
|
+
console.error(pc.red("--channel <id> is required"));
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const level = requireEnum("level", opts.level ?? "run", NotificationSubLevel.options);
|
|
151
|
+
if (!level) return null;
|
|
152
|
+
const scope = requireEnum("scope", opts.scope ?? "org", NotificationScopeKind.options);
|
|
153
|
+
if (!scope) return null;
|
|
154
|
+
const onStatus = parseCsv(opts.onStatus);
|
|
155
|
+
if (onStatus.length === 0) {
|
|
156
|
+
console.error(pc.red("--on-status <csv> is required (e.g. failed,success)"));
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const needsScopeId = scope === NotificationScopeKind.enum.team || scope === NotificationScopeKind.enum.user;
|
|
160
|
+
if (needsScopeId && !opts.scopeId) {
|
|
161
|
+
console.error(pc.red(`--scope-id is required for ${scope} scope`));
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
let accumulateFor = null;
|
|
165
|
+
if (opts.accumulateFor !== void 0) {
|
|
166
|
+
accumulateFor = Number(opts.accumulateFor);
|
|
167
|
+
if (!Number.isFinite(accumulateFor)) {
|
|
168
|
+
console.error(pc.red("--accumulate-for must be a number of milliseconds"));
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const mentions = parseCsv(opts.mentions);
|
|
173
|
+
const recipientOverride = parseCsv(opts.recipientOverride);
|
|
174
|
+
const onFailureClass = parseCsv(opts.onFailureClass);
|
|
175
|
+
return {
|
|
176
|
+
level,
|
|
177
|
+
channel_id: opts.channel,
|
|
178
|
+
scope_type: scope,
|
|
179
|
+
scope_id: needsScopeId ? opts.scopeId : null,
|
|
180
|
+
on_status: onStatus,
|
|
181
|
+
repo_glob: opts.repoGlob ?? null,
|
|
182
|
+
workflow_glob: opts.workflowGlob ?? null,
|
|
183
|
+
job_glob: opts.jobGlob ?? null,
|
|
184
|
+
mentions: mentions.length > 0 ? mentions : null,
|
|
185
|
+
recipient_override: recipientOverride.length > 0 ? recipientOverride : null,
|
|
186
|
+
on_failure_class: onFailureClass.length > 0 ? onFailureClass : null,
|
|
187
|
+
accumulate_for: accumulateFor
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async function notificationsSubscriptionsAddCommand(opts) {
|
|
191
|
+
const body = buildSubscriptionBody(opts);
|
|
192
|
+
if (!body) return false;
|
|
193
|
+
return withClient(opts, async (client) => {
|
|
194
|
+
const sub = await client.createSubscription(body);
|
|
195
|
+
console.log(pc.green(`Subscription created: ${sub.id}`));
|
|
196
|
+
return true;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async function notificationsSubscriptionsRemoveCommand(id, opts) {
|
|
200
|
+
return withClient(opts, async (client) => {
|
|
201
|
+
await client.deleteSubscription(id);
|
|
202
|
+
console.log(pc.green(`Subscription removed: ${id}`));
|
|
203
|
+
return true;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function notificationsRosterListCommand(opts) {
|
|
207
|
+
return withClient(opts, async (client) => {
|
|
208
|
+
const identities = await client.listIdentities();
|
|
209
|
+
if (opts.json) {
|
|
210
|
+
console.log(JSON.stringify(identities, null, 2));
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
printTable([
|
|
214
|
+
"ID",
|
|
215
|
+
"SUBJECT KIND",
|
|
216
|
+
"SUBJECT",
|
|
217
|
+
"SLACK MEMBER",
|
|
218
|
+
"STATUS"
|
|
219
|
+
], identities.map((r) => [
|
|
220
|
+
r.id,
|
|
221
|
+
r.subject_kind,
|
|
222
|
+
r.subject_value,
|
|
223
|
+
r.slack_member_id,
|
|
224
|
+
r.status
|
|
225
|
+
]));
|
|
226
|
+
return true;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/** Build an add-identity body from parsed flags (null on validation error). */
|
|
230
|
+
function buildIdentityBody(opts) {
|
|
231
|
+
if (!opts.connection) {
|
|
232
|
+
console.error(pc.red("--connection <id> is required"));
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const subjectKind = requireEnum("subject-kind", opts.subjectKind, RosterSubjectKind.options);
|
|
236
|
+
if (!subjectKind) return null;
|
|
237
|
+
if (!opts.subject) {
|
|
238
|
+
console.error(pc.red("--subject <value> is required (the KiCI user sub, git login, or email)"));
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const inputForm = requireEnum("input-form", opts.inputForm ?? "id", RosterInputForm.options);
|
|
242
|
+
if (!inputForm) return null;
|
|
243
|
+
if (!opts.value) {
|
|
244
|
+
console.error(pc.red("--value <slack id | email | @handle> is required"));
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
connection_id: opts.connection,
|
|
249
|
+
subject_kind: subjectKind,
|
|
250
|
+
subject_value: opts.subject,
|
|
251
|
+
input_form: inputForm,
|
|
252
|
+
value: opts.value
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
async function notificationsRosterAddCommand(opts) {
|
|
256
|
+
const body = buildIdentityBody(opts);
|
|
257
|
+
if (!body) return false;
|
|
258
|
+
return withClient(opts, async (client) => {
|
|
259
|
+
const row = await client.addIdentity(body);
|
|
260
|
+
console.log(pc.green(`Roster identity added: ${row.id} → ${row.slack_member_id}`));
|
|
261
|
+
return true;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
async function notificationsRosterRemoveCommand(id, opts) {
|
|
265
|
+
return withClient(opts, async (client) => {
|
|
266
|
+
await client.deleteIdentity(id);
|
|
267
|
+
console.log(pc.green(`Roster identity removed: ${id}`));
|
|
268
|
+
return true;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
272
|
+
export { notificationsChannelsAddCommand, notificationsChannelsListCommand, notificationsChannelsRemoveCommand, notificationsRosterAddCommand, notificationsRosterListCommand, notificationsRosterRemoveCommand, notificationsSubscriptionsAddCommand, notificationsSubscriptionsListCommand, notificationsSubscriptionsRemoveCommand };
|
|
273
|
+
|
|
274
|
+
//# sourceMappingURL=notifications.js.map
|
package/dist/commands/preview.js
CHANGED
|
@@ -2,6 +2,8 @@ import "../rolldown-runtime-ClRpJifh.js";
|
|
|
2
2
|
import { discoverWorkflows, resolveKiciDir } from "../execution/executor.js";
|
|
3
3
|
import "../execution/index.js";
|
|
4
4
|
import { transformTriggers } from "../lockfile/generator.js";
|
|
5
|
+
import { analyzeJobPurity } from "../lockfile/purity-diagnostics.js";
|
|
6
|
+
import "../lockfile/index.js";
|
|
5
7
|
import { displayDryRun } from "../test-runner/dry-run.js";
|
|
6
8
|
import { parseEventArg } from "../test-runner/event-types.js";
|
|
7
9
|
import { buildEventPayload } from "../test-runner/payload-builder.js";
|
|
@@ -35,7 +37,7 @@ async function previewCommand(event, options) {
|
|
|
35
37
|
logger.info(pc.gray(" kici preview schedule"));
|
|
36
38
|
logger.info(pc.gray(" kici preview lifecycle:workflow_complete\n"));
|
|
37
39
|
logger.info(pc.gray("For remote fixture execution, use: kici run remote [fixture] [options]"));
|
|
38
|
-
logger.info(pc.gray("For local workflow execution, use: kici run
|
|
40
|
+
logger.info(pc.gray("For local workflow execution, use: kici run <event> --local\n"));
|
|
39
41
|
return true;
|
|
40
42
|
}
|
|
41
43
|
if (!isKnownEventArg(event)) {
|
|
@@ -88,10 +90,15 @@ async function previewEvent(event, options) {
|
|
|
88
90
|
return false;
|
|
89
91
|
}
|
|
90
92
|
}
|
|
93
|
+
const purityWarnings = [];
|
|
94
|
+
for (const w of workflows) for (const j of w.jobs) {
|
|
95
|
+
if (typeof j === "function") continue;
|
|
96
|
+
purityWarnings.push(...analyzeJobPurity(j, w.name));
|
|
97
|
+
}
|
|
91
98
|
displayDryRun(lockWorkflows, decisions, {
|
|
92
99
|
workflow: options.workflow,
|
|
93
100
|
job: options.job
|
|
94
|
-
});
|
|
101
|
+
}, purityWarnings);
|
|
95
102
|
return true;
|
|
96
103
|
} catch (error) {
|
|
97
104
|
const message = toErrorMessage(error);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Always-on startup banner for a routed `kici run --local` invocation. States
|
|
3
|
+
* the selected plane, where the agent runs, the secret + identity source, the
|
|
4
|
+
* control commands, and the flags that force a different behavior — so a
|
|
5
|
+
* developer always knows which plane a run used and why (design §5).
|
|
6
|
+
*
|
|
7
|
+
* Three variants:
|
|
8
|
+
* - `offline` — independent plane, local secrets, dev-signed identity.
|
|
9
|
+
* - `attached` — hybrid plane, real Platform-minted OIDC + attestation.
|
|
10
|
+
* - `fallback` — wanted attached but the Platform was unreachable, so the run
|
|
11
|
+
* fell back to offline; a LOUD first line makes the degradation obvious.
|
|
12
|
+
*/
|
|
13
|
+
export type RunBannerMode = 'offline' | 'attached' | 'fallback';
|
|
14
|
+
export interface RunBannerInput {
|
|
15
|
+
mode: RunBannerMode;
|
|
16
|
+
/** Plane orchestrator URL (surfaced on the control line). */
|
|
17
|
+
planeUrl: string;
|
|
18
|
+
/** Org id (attached variant). */
|
|
19
|
+
orgId?: string;
|
|
20
|
+
/** Why the run fell back to offline (fallback variant). */
|
|
21
|
+
fallbackReason?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Trusted fleet-agent profile (`--trusted`): the run routes to the trusted
|
|
24
|
+
* label set, so steps see the ambient host env (minus the agent's own KiCI
|
|
25
|
+
* identity secrets). Surfaced as a loud line so a trusted run is never silent.
|
|
26
|
+
*/
|
|
27
|
+
trusted?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Render the boxed run banner as a plain (uncolored) multi-line string. */
|
|
30
|
+
export declare function renderRunBanner(input: RunBannerInput): string;
|
|
31
|
+
//# sourceMappingURL=run-banner.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/commands/run-banner.ts
|
|
3
|
+
/** Render the boxed run banner as a plain (uncolored) multi-line string. */
|
|
4
|
+
function renderRunBanner(input) {
|
|
5
|
+
const title = input.mode === "attached" ? "kici run --local (connected)" : "kici run --local";
|
|
6
|
+
const rows = [];
|
|
7
|
+
if (input.mode === "fallback") rows.push(["⚠", `Platform unreachable — fell back to OFFLINE${input.fallbackReason ? ` (${input.fallbackReason})` : ""}`]);
|
|
8
|
+
if (input.mode === "attached") rows.push(["plane", "local dev orchestrator (hybrid, attached)"], ["agent", "this machine (bare-metal)"], ["secrets", `REAL scoped${input.orgId ? ` (org: ${input.orgId})` : ""}`], ["identity", "real Platform OIDC + attestation"], ["control", `kici local status | logs | detach (${input.planeUrl})`], ["force", "--offline (local plane) · --in-place (ambient)"]);
|
|
9
|
+
else rows.push(["plane", "local dev orchestrator (independent, offline)"], ["agent", "this machine (bare-metal)"], ["secrets", "LOCAL files (.kici/.secrets, .env.local, --env)"], ["identity", "DEV-SIGNED (iss=kici-local — NOT prod)"], ["control", `kici local status | logs | down (${input.planeUrl})`], ["force", "--connected (attach to Platform) · --in-place (ambient)"]);
|
|
10
|
+
if (input.trusted) rows.push(["execution", "TRUSTED — host env passthrough (NOT sandboxed)"]);
|
|
11
|
+
const labelWidth = Math.max(...rows.map(([label]) => label.length));
|
|
12
|
+
const bodyLines = rows.map(([label, value]) => `${(label + ":").padEnd(labelWidth + 1)} ${value}`);
|
|
13
|
+
const contentWidth = Math.max(title.length + 2, ...bodyLines.map((l) => l.length));
|
|
14
|
+
const top = `┌ ${title} ${"─".repeat(Math.max(0, contentWidth - title.length))}┐`;
|
|
15
|
+
const bottom = `└${"─".repeat(contentWidth + 2)}┘`;
|
|
16
|
+
return [
|
|
17
|
+
top,
|
|
18
|
+
...bodyLines.map((l) => `│ ${l.padEnd(contentWidth)} │`),
|
|
19
|
+
bottom
|
|
20
|
+
].join("\n");
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { renderRunBanner };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=run-banner.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `kici run --local [event]` — a real routed dispatch with this machine as the
|
|
3
|
+
* ephemeral agent, through the warm local dev plane. In this phase only the
|
|
4
|
+
* offline (independent) plane is wired; Platform attachment (`--connected` /
|
|
5
|
+
* auto-select hybrid) lands with the attachment work.
|
|
6
|
+
*
|
|
7
|
+
* The offline path reuses the real orchestrator + agent pipeline end-to-end, so
|
|
8
|
+
* the execution parity dividend (emit / cache / needs / parallel / `KICI_SOURCE_*`)
|
|
9
|
+
* holds by construction — there is no second engine.
|
|
10
|
+
*/
|
|
11
|
+
export interface RunRoutedOptions {
|
|
12
|
+
event?: string;
|
|
13
|
+
local?: boolean;
|
|
14
|
+
offline?: boolean;
|
|
15
|
+
connected?: boolean;
|
|
16
|
+
inPlace?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Trusted fleet-agent profile: route this run to the plane's trusted label
|
|
19
|
+
* set so steps run with the ambient host env passed through (minus the
|
|
20
|
+
* agent's own KiCI identity secrets). Selects a PRE-CONFIGURED trusted scaler
|
|
21
|
+
* label set by appending the `self-hosted` routing label to the run's job
|
|
22
|
+
* `runsOn` — it never injects `KICI_TRUSTED_ENV` onto a dispatch payload.
|
|
23
|
+
*/
|
|
24
|
+
trusted?: boolean;
|
|
25
|
+
/** Flat per-run secrets (`--env KEY=VALUE`); delivery lands with the secret path. */
|
|
26
|
+
env?: string[];
|
|
27
|
+
/**
|
|
28
|
+
* Path to a dispatch payload JSON `{ action?, client_payload? }`. Only read
|
|
29
|
+
* when the event is `dispatch`: `action` becomes the dispatch `types` matcher
|
|
30
|
+
* key and `client_payload` reaches the workflow. This is how `packages/ci`
|
|
31
|
+
* targets a single `dispatch()` workflow (e.g. deploy-stg) on the routed path.
|
|
32
|
+
*/
|
|
33
|
+
payload?: string;
|
|
34
|
+
kiciDir?: string;
|
|
35
|
+
quiet?: boolean;
|
|
36
|
+
debug?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Entry point for the action-bearing `kici run`. Validates the routing flags and
|
|
40
|
+
* drives the offline routed run. Returns true on run success.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runRoutedCommand(options: RunRoutedOptions): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Read a dispatch payload file `{ action?, client_payload? }`. Returns the
|
|
45
|
+
* action + client_payload the routed dispatch trigger carries. An absent
|
|
46
|
+
* `--payload` yields an empty action (a bare `dispatch()` workflow still
|
|
47
|
+
* matches); a present file is parsed as JSON.
|
|
48
|
+
*/
|
|
49
|
+
export declare function readDispatchPayload(payloadPath?: string): {
|
|
50
|
+
action?: string;
|
|
51
|
+
clientPayload?: unknown;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=run-routed.d.ts.map
|