@shield-agent/kya 0.32.7 → 0.33.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.
- package/.cursor-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/LIMITATIONS.md +2 -2
- package/README.md +232 -17
- package/amp/settings.example.json +1 -1
- package/claude/claude_desktop_config.example.json +1 -1
- package/cline/cline_mcp_settings.example.json +1 -1
- package/copilot/mcp-config.example.json +1 -1
- package/data/iocs.json +17 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +237 -53
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/commands/connect.d.ts +1 -1
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +11 -3
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/dash.d.ts.map +1 -1
- package/dist/commands/dash.js +25 -1
- package/dist/commands/dash.js.map +1 -1
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +26 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/eval-tool.d.ts +3 -0
- package/dist/commands/eval-tool.d.ts.map +1 -1
- package/dist/commands/eval-tool.js +2 -1
- package/dist/commands/eval-tool.js.map +1 -1
- package/dist/commands/export.d.ts +33 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +121 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/hook.d.ts.map +1 -1
- package/dist/commands/hook.js +135 -26
- package/dist/commands/hook.js.map +1 -1
- package/dist/commands/hosted-hook.d.ts +8 -1
- package/dist/commands/hosted-hook.d.ts.map +1 -1
- package/dist/commands/hosted-hook.js +15 -5
- package/dist/commands/hosted-hook.js.map +1 -1
- package/dist/commands/login.d.ts +34 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +74 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/map.d.ts +21 -0
- package/dist/commands/map.d.ts.map +1 -0
- package/dist/commands/map.js +76 -0
- package/dist/commands/map.js.map +1 -0
- package/dist/commands/notify-flush.d.ts.map +1 -1
- package/dist/commands/notify-flush.js +7 -1
- package/dist/commands/notify-flush.js.map +1 -1
- package/dist/commands/posture.d.ts +21 -0
- package/dist/commands/posture.d.ts.map +1 -0
- package/dist/commands/posture.js +177 -0
- package/dist/commands/posture.js.map +1 -0
- package/dist/commands/receipt-serve.d.ts +7 -0
- package/dist/commands/receipt-serve.d.ts.map +1 -1
- package/dist/commands/receipt-serve.js +50 -0
- package/dist/commands/receipt-serve.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +5 -1
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/team.d.ts.map +1 -1
- package/dist/commands/team.js +30 -11
- package/dist/commands/team.js.map +1 -1
- package/dist/commands/wire-hooks.d.ts +5 -1
- package/dist/commands/wire-hooks.d.ts.map +1 -1
- package/dist/commands/wire-hooks.js +23 -8
- package/dist/commands/wire-hooks.js.map +1 -1
- package/dist/commands/wrap.d.ts +38 -1
- package/dist/commands/wrap.d.ts.map +1 -1
- package/dist/commands/wrap.js +82 -6
- package/dist/commands/wrap.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/cta.d.ts +23 -0
- package/dist/cta.d.ts.map +1 -0
- package/dist/cta.js +86 -0
- package/dist/cta.js.map +1 -0
- package/dist/daily-slot.d.ts +2 -0
- package/dist/daily-slot.d.ts.map +1 -0
- package/dist/daily-slot.js +29 -0
- package/dist/daily-slot.js.map +1 -0
- package/dist/dash/dash.d.ts.map +1 -1
- package/dist/dash/dash.js +10 -0
- package/dist/dash/dash.js.map +1 -1
- package/dist/dash/entitlement.d.ts +1 -1
- package/dist/dash/entitlement.d.ts.map +1 -1
- package/dist/dash/entitlement.js +1 -0
- package/dist/dash/entitlement.js.map +1 -1
- package/dist/dash/input.d.ts +1 -1
- package/dist/dash/input.d.ts.map +1 -1
- package/dist/dash/input.js +3 -0
- package/dist/dash/input.js.map +1 -1
- package/dist/dash/render.js +1 -1
- package/dist/dash/render.js.map +1 -1
- package/dist/dash/screens/free.js +1 -1
- package/dist/dash/screens/free.js.map +1 -1
- package/dist/device-id.d.ts +5 -0
- package/dist/device-id.d.ts.map +1 -0
- package/dist/device-id.js +78 -0
- package/dist/device-id.js.map +1 -0
- package/dist/fs-atomic.d.ts +15 -3
- package/dist/fs-atomic.d.ts.map +1 -1
- package/dist/fs-atomic.js +31 -14
- package/dist/fs-atomic.js.map +1 -1
- package/dist/gate/config-gen.d.ts +26 -2
- package/dist/gate/config-gen.d.ts.map +1 -1
- package/dist/gate/config-gen.js +53 -4
- package/dist/gate/config-gen.js.map +1 -1
- package/dist/gate/otlp-receiver.d.ts.map +1 -1
- package/dist/gate/otlp-receiver.js +3 -1
- package/dist/gate/otlp-receiver.js.map +1 -1
- package/dist/guard-pack.d.ts +58 -0
- package/dist/guard-pack.d.ts.map +1 -0
- package/dist/guard-pack.js +753 -0
- package/dist/guard-pack.js.map +1 -0
- package/dist/ioc/exec.d.ts +19 -0
- package/dist/ioc/exec.d.ts.map +1 -0
- package/dist/ioc/exec.js +49 -0
- package/dist/ioc/exec.js.map +1 -0
- package/dist/ioc/format.d.ts +4 -0
- package/dist/ioc/format.d.ts.map +1 -0
- package/dist/ioc/format.js +23 -0
- package/dist/ioc/format.js.map +1 -0
- package/dist/ioc/list.d.ts +29 -0
- package/dist/ioc/list.d.ts.map +1 -0
- package/dist/ioc/list.js +25 -0
- package/dist/ioc/list.js.map +1 -0
- package/dist/ioc/result.d.ts +18 -0
- package/dist/ioc/result.d.ts.map +1 -0
- package/dist/ioc/result.js +26 -0
- package/dist/ioc/result.js.map +1 -0
- package/dist/ioc/scan.d.ts +21 -0
- package/dist/ioc/scan.d.ts.map +1 -0
- package/dist/ioc/scan.js +247 -0
- package/dist/ioc/scan.js.map +1 -0
- package/dist/ioc/spawn.d.ts +26 -0
- package/dist/ioc/spawn.d.ts.map +1 -0
- package/dist/ioc/spawn.js +52 -0
- package/dist/ioc/spawn.js.map +1 -0
- package/dist/login/credentials.d.ts +17 -0
- package/dist/login/credentials.d.ts.map +1 -0
- package/dist/login/credentials.js +64 -0
- package/dist/login/credentials.js.map +1 -0
- package/dist/login/device-flow.d.ts +44 -0
- package/dist/login/device-flow.d.ts.map +1 -0
- package/dist/login/device-flow.js +205 -0
- package/dist/login/device-flow.js.map +1 -0
- package/dist/map/build.d.ts +58 -0
- package/dist/map/build.d.ts.map +1 -0
- package/dist/map/build.js +439 -0
- package/dist/map/build.js.map +1 -0
- package/dist/map/heat.d.ts +44 -0
- package/dist/map/heat.d.ts.map +1 -0
- package/dist/map/heat.js +96 -0
- package/dist/map/heat.js.map +1 -0
- package/dist/map/load.d.ts +38 -0
- package/dist/map/load.d.ts.map +1 -0
- package/dist/map/load.js +38 -0
- package/dist/map/load.js.map +1 -0
- package/dist/map/page.d.ts +90 -0
- package/dist/map/page.d.ts.map +1 -0
- package/dist/map/page.js +374 -0
- package/dist/map/page.js.map +1 -0
- package/dist/map/target-class.d.ts +22 -0
- package/dist/map/target-class.d.ts.map +1 -0
- package/dist/map/target-class.js +151 -0
- package/dist/map/target-class.js.map +1 -0
- package/dist/map/types.d.ts +102 -0
- package/dist/map/types.d.ts.map +1 -0
- package/dist/map/types.js +3 -0
- package/dist/map/types.js.map +1 -0
- package/dist/mcp/protocol.d.ts.map +1 -1
- package/dist/mcp/protocol.js +16 -5
- package/dist/mcp/protocol.js.map +1 -1
- package/dist/never-code.d.ts +7 -0
- package/dist/never-code.d.ts.map +1 -0
- package/dist/never-code.js +9 -0
- package/dist/never-code.js.map +1 -0
- package/dist/never-list-input.d.ts +92 -0
- package/dist/never-list-input.d.ts.map +1 -0
- package/dist/never-list-input.js +1889 -0
- package/dist/never-list-input.js.map +1 -0
- package/dist/notify/dispatch.d.ts +4 -0
- package/dist/notify/dispatch.d.ts.map +1 -1
- package/dist/notify/dispatch.js +3 -1
- package/dist/notify/dispatch.js.map +1 -1
- package/dist/offline-evaluate.d.ts +42 -5
- package/dist/offline-evaluate.d.ts.map +1 -1
- package/dist/offline-evaluate.js +96 -9
- package/dist/offline-evaluate.js.map +1 -1
- package/dist/otel/exporter.d.ts +15 -2
- package/dist/otel/exporter.d.ts.map +1 -1
- package/dist/otel/exporter.js +52 -6
- package/dist/otel/exporter.js.map +1 -1
- package/dist/otel/map-attrs-optin.d.ts +39 -0
- package/dist/otel/map-attrs-optin.d.ts.map +1 -0
- package/dist/otel/map-attrs-optin.js +109 -0
- package/dist/otel/map-attrs-optin.js.map +1 -0
- package/dist/otel.d.ts +8 -2
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +32 -16
- package/dist/otel.js.map +1 -1
- package/dist/parse-args.d.ts +2 -0
- package/dist/parse-args.d.ts.map +1 -1
- package/dist/parse-args.js +4 -2
- package/dist/parse-args.js.map +1 -1
- package/dist/posture/card.d.ts +26 -0
- package/dist/posture/card.d.ts.map +1 -0
- package/dist/posture/card.js +46 -0
- package/dist/posture/card.js.map +1 -0
- package/dist/posture/collect.d.ts +19 -0
- package/dist/posture/collect.d.ts.map +1 -0
- package/dist/posture/collect.js +182 -0
- package/dist/posture/collect.js.map +1 -0
- package/dist/posture/send.d.ts +24 -0
- package/dist/posture/send.d.ts.map +1 -0
- package/dist/posture/send.js +162 -0
- package/dist/posture/send.js.map +1 -0
- package/dist/posture/spawn.d.ts +20 -0
- package/dist/posture/spawn.d.ts.map +1 -0
- package/dist/posture/spawn.js +33 -0
- package/dist/posture/spawn.js.map +1 -0
- package/dist/posture/store.d.ts +34 -0
- package/dist/posture/store.d.ts.map +1 -0
- package/dist/posture/store.js +107 -0
- package/dist/posture/store.js.map +1 -0
- package/dist/posture/validate.d.ts +33 -0
- package/dist/posture/validate.d.ts.map +1 -0
- package/dist/posture/validate.js +232 -0
- package/dist/posture/validate.js.map +1 -0
- package/dist/project-policy.d.ts +23 -0
- package/dist/project-policy.d.ts.map +1 -0
- package/dist/project-policy.js +95 -0
- package/dist/project-policy.js.map +1 -0
- package/dist/receipt/daemon.d.ts +9 -9
- package/dist/receipt/daemon.d.ts.map +1 -1
- package/dist/receipt/daemon.js +26 -11
- package/dist/receipt/daemon.js.map +1 -1
- package/dist/receipt/embeddings.d.ts +29 -7
- package/dist/receipt/embeddings.d.ts.map +1 -1
- package/dist/receipt/embeddings.js +282 -33
- package/dist/receipt/embeddings.js.map +1 -1
- package/dist/receipt/gate-page.d.ts.map +1 -1
- package/dist/receipt/gate-page.js +5 -22
- package/dist/receipt/gate-page.js.map +1 -1
- package/dist/receipt/live-server.d.ts +8 -0
- package/dist/receipt/live-server.d.ts.map +1 -1
- package/dist/receipt/live-server.js +136 -4
- package/dist/receipt/live-server.js.map +1 -1
- package/dist/receipt/render-receipt.d.ts +7 -1
- package/dist/receipt/render-receipt.d.ts.map +1 -1
- package/dist/receipt/render-receipt.js +101 -13
- package/dist/receipt/render-receipt.js.map +1 -1
- package/dist/receipt/search.d.ts +5 -0
- package/dist/receipt/search.d.ts.map +1 -1
- package/dist/receipt/search.js +23 -2
- package/dist/receipt/search.js.map +1 -1
- package/dist/receipt/semantic-setup.d.ts +21 -0
- package/dist/receipt/semantic-setup.d.ts.map +1 -0
- package/dist/receipt/semantic-setup.js +111 -0
- package/dist/receipt/semantic-setup.js.map +1 -0
- package/dist/receipt/semantic-status.d.ts +7 -0
- package/dist/receipt/semantic-status.d.ts.map +1 -0
- package/dist/receipt/semantic-status.js +26 -0
- package/dist/receipt/semantic-status.js.map +1 -0
- package/dist/sdk/govern.d.ts.map +1 -1
- package/dist/sdk/govern.js +7 -3
- package/dist/sdk/govern.js.map +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +3 -23
- package/dist/telemetry.js.map +1 -1
- package/dist/trail-map-fields.d.ts +10 -0
- package/dist/trail-map-fields.d.ts.map +1 -0
- package/dist/trail-map-fields.js +53 -0
- package/dist/trail-map-fields.js.map +1 -0
- package/dist/trail-summary.d.ts.map +1 -1
- package/dist/trail-summary.js +11 -3
- package/dist/trail-summary.js.map +1 -1
- package/dist/trail.d.ts +13 -1
- package/dist/trail.d.ts.map +1 -1
- package/dist/trail.js +38 -1
- package/dist/trail.js.map +1 -1
- package/droid/README.md +1 -1
- package/droid/mcp.example.json +1 -1
- package/gemini/settings.example.json +1 -1
- package/grok/README.md +1 -1
- package/kilo/kilo.example.json +1 -1
- package/kimi/mcp.example.json +1 -1
- package/kiro/mcp.example.json +1 -1
- package/manifest.json +1 -1
- package/mastracode/mcp.example.json +1 -1
- package/mcp.json +1 -1
- package/openai/codex.config.example.toml +1 -1
- package/opencode/opencode.example.json +1 -1
- package/package.json +8 -7
- package/qwen/settings.example.json +1 -1
- package/server.json +2 -2
- package/vendor/cytoscape/LICENSE +19 -0
- package/vendor/cytoscape/README.md +7 -0
- package/vendor/cytoscape/cytoscape.min.js +31 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const IOC_SCAN_INTERVAL_MS: number;
|
|
2
|
+
/** KYA_IOC=0 (or off/false) or ~/.kya/config.json {"ioc":{"enabled":false}} turns the background scan off. */
|
|
3
|
+
export declare function iocEnabled(env: NodeJS.ProcessEnv): boolean;
|
|
4
|
+
export declare function iocStampPath(env: NodeJS.ProcessEnv): string;
|
|
5
|
+
type SpawnLike = (command: string, args: readonly string[], options: {
|
|
6
|
+
detached: boolean;
|
|
7
|
+
stdio: "ignore";
|
|
8
|
+
env: NodeJS.ProcessEnv;
|
|
9
|
+
cwd: string;
|
|
10
|
+
windowsHide: boolean;
|
|
11
|
+
}) => {
|
|
12
|
+
unref(): void;
|
|
13
|
+
on(event: "error", listener: () => void): unknown;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Starts `kya ioc-scan` detached when the 24 h slot is free. Never throws, never waits.
|
|
17
|
+
* Returns whether a scan was started. A failed spawn keeps the slot claimed until tomorrow.
|
|
18
|
+
*/
|
|
19
|
+
export declare function maybeSpawnIocScan(deps: {
|
|
20
|
+
readonly env: NodeJS.ProcessEnv;
|
|
21
|
+
readonly entry?: string;
|
|
22
|
+
readonly spawnImpl?: SpawnLike;
|
|
23
|
+
readonly nowMs?: number;
|
|
24
|
+
}): boolean;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=spawn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../src/ioc/spawn.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,oBAAoB,QAAmB,CAAC;AAErD,8GAA8G;AAC9G,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAS1D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAE3D;AAED,KAAK,SAAS,GAAG,CACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,KACvG;IAAE,KAAK,IAAI,IAAI,CAAC;IAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAA;CAAE,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAkBV"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook-side throttle for the IoC scan. Lean on purpose: `kya hook` imports this module and nothing
|
|
3
|
+
* from ./scan.js, so the hook path loads no scan code. The scan itself runs in a detached child
|
|
4
|
+
* (`kya ioc-scan`) that the hook never waits for.
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { isAbsolute, join } from "node:path";
|
|
9
|
+
import { globalConfigDir } from "../config.js";
|
|
10
|
+
import { claimDailySlot } from "../daily-slot.js";
|
|
11
|
+
import { childCwd, childEnv } from "./exec.js";
|
|
12
|
+
export const IOC_SCAN_INTERVAL_MS = 24 * 60 * 60_000;
|
|
13
|
+
/** KYA_IOC=0 (or off/false) or ~/.kya/config.json {"ioc":{"enabled":false}} turns the background scan off. */
|
|
14
|
+
export function iocEnabled(env) {
|
|
15
|
+
const flag = String(env["KYA_IOC"] ?? "").trim().toLowerCase();
|
|
16
|
+
if (flag === "0" || flag === "off" || flag === "false")
|
|
17
|
+
return false;
|
|
18
|
+
try {
|
|
19
|
+
const cfg = JSON.parse(readFileSync(join(globalConfigDir(env), "config.json"), "utf8"));
|
|
20
|
+
return cfg.ioc?.enabled !== false;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return true; // no config file (the usual case) or unreadable: the default is on
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function iocStampPath(env) {
|
|
27
|
+
return join(globalConfigDir(env), "ioc-scan-stamp");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Starts `kya ioc-scan` detached when the 24 h slot is free. Never throws, never waits.
|
|
31
|
+
* Returns whether a scan was started. A failed spawn keeps the slot claimed until tomorrow.
|
|
32
|
+
*/
|
|
33
|
+
export function maybeSpawnIocScan(deps) {
|
|
34
|
+
try {
|
|
35
|
+
if (!iocEnabled(deps.env))
|
|
36
|
+
return false;
|
|
37
|
+
const entry = deps.entry ?? process.argv[1];
|
|
38
|
+
// Absolute only: the child starts in the home directory, so a relative entry would not resolve.
|
|
39
|
+
if (entry === undefined || !isAbsolute(entry))
|
|
40
|
+
return false; // before claiming: a skipped spawn must not burn the slot
|
|
41
|
+
if (!claimDailySlot(iocStampPath(deps.env), IOC_SCAN_INTERVAL_MS, deps.nowMs))
|
|
42
|
+
return false;
|
|
43
|
+
const child = (deps.spawnImpl ?? spawn)(process.execPath, [entry, "ioc-scan"], { detached: true, stdio: "ignore", env: childEnv(deps.env), cwd: childCwd(), windowsHide: true });
|
|
44
|
+
child.on("error", () => undefined);
|
|
45
|
+
child.unref();
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/ioc/spawn.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;AAErD,8GAA8G;AAC9G,MAAM,UAAU,UAAU,CAAC,GAAsB;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/D,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACrE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAoC,CAAC;QAC3H,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,mEAAmE;IAClF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAsB;IACjD,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAC,CAAC;AACtD,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAKjC;IACC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5C,gGAAgG;QAChG,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,0DAA0D;QACvH,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5F,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,SAAS,IAAK,KAA8B,CAAC,CAC/D,OAAO,CAAC,QAAQ,EAChB,CAAC,KAAK,EAAE,UAAU,CAAC,EACnB,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CACjG,CAAC;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACnC,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface HostedCredentials {
|
|
2
|
+
/** Validated origin of the hosted console, no path, query or fragment. */
|
|
3
|
+
readonly baseUrl: string;
|
|
4
|
+
/** The hosted API key. Never print it. */
|
|
5
|
+
readonly apiKey: string;
|
|
6
|
+
/** Display prefix only (safe to print). */
|
|
7
|
+
readonly keyPrefix: string;
|
|
8
|
+
readonly createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function credentialsPath(env?: NodeJS.ProcessEnv): string;
|
|
11
|
+
/** The saved credentials, or undefined when missing, unreadable or malformed. Never throws. */
|
|
12
|
+
export declare function readCredentials(env?: NodeJS.ProcessEnv): HostedCredentials | undefined;
|
|
13
|
+
/** Write the credentials with mode 0600. Throws on I/O failure; the caller reports it. */
|
|
14
|
+
export declare function writeCredentials(creds: HostedCredentials, env?: NodeJS.ProcessEnv): string;
|
|
15
|
+
/** Delete the local file. Returns whether a file was removed. Never throws. */
|
|
16
|
+
export declare function deleteCredentials(env?: NodeJS.ProcessEnv): boolean;
|
|
17
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/login/credentials.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAID,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE5E;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,iBAAiB,GAAG,SAAS,CAkBnG;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAKvG;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAS/E"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hosted login credentials for `kya login`: one JSON file in the user-level KYA dir
|
|
3
|
+
* (`<KYA_HOME|home>/.kya/hosted-credentials.json`), mode 0600, written crash-safe.
|
|
4
|
+
*
|
|
5
|
+
* The file holds a full-workspace hosted API key (no narrower scope exists yet). It is never printed, never sent to telemetry
|
|
6
|
+
* and never read by the hosted hook path (that path uses its own --plane-config file).
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { globalConfigDir } from "../config.js";
|
|
11
|
+
import { atomicWriteSecretSync } from "../fs-atomic.js";
|
|
12
|
+
import { validateConsoleUrl } from "./device-flow.js";
|
|
13
|
+
const KEY_RE = /^[\x21-\x7e]{8,512}$/;
|
|
14
|
+
export function credentialsPath(env = process.env) {
|
|
15
|
+
return join(globalConfigDir(env), "hosted-credentials.json");
|
|
16
|
+
}
|
|
17
|
+
/** The saved credentials, or undefined when missing, unreadable or malformed. Never throws. */
|
|
18
|
+
export function readCredentials(env = process.env) {
|
|
19
|
+
try {
|
|
20
|
+
const raw = JSON.parse(readFileSync(credentialsPath(env), "utf8"));
|
|
21
|
+
if (typeof raw.baseUrl === "string" &&
|
|
22
|
+
typeof raw.apiKey === "string" &&
|
|
23
|
+
KEY_RE.test(raw.apiKey) &&
|
|
24
|
+
typeof raw.keyPrefix === "string" &&
|
|
25
|
+
typeof raw.createdAt === "string") {
|
|
26
|
+
// Re-validated on every read: a file edited to point at a remote http host (or anything else) is not used.
|
|
27
|
+
validateConsoleUrl(raw.baseUrl);
|
|
28
|
+
return { baseUrl: raw.baseUrl, apiKey: raw.apiKey, keyPrefix: raw.keyPrefix, createdAt: raw.createdAt };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* missing or corrupt */
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Write the credentials with mode 0600. Throws on I/O failure; the caller reports it. */
|
|
37
|
+
export function writeCredentials(creds, env = process.env) {
|
|
38
|
+
const path = credentialsPath(env);
|
|
39
|
+
mkdirSync(globalConfigDir(env), { recursive: true });
|
|
40
|
+
atomicWriteSecretSync(path, `${JSON.stringify(creds, null, 2)}\n`);
|
|
41
|
+
return path;
|
|
42
|
+
}
|
|
43
|
+
/** Delete the local file. Returns whether a file was removed. Never throws. */
|
|
44
|
+
export function deleteCredentials(env = process.env) {
|
|
45
|
+
const path = credentialsPath(env);
|
|
46
|
+
try {
|
|
47
|
+
if (!existsSync(path) && !isLink(path))
|
|
48
|
+
return false;
|
|
49
|
+
rmSync(path, { force: true });
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isLink(path) {
|
|
57
|
+
try {
|
|
58
|
+
return lstatSync(path).isSymbolicLink();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/login/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAYtD,MAAM,MAAM,GAAG,sBAAsB,CAAC;AAEtC,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,yBAAyB,CAAC,CAAC;AAC/D,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAA+B,CAAC;QACjG,IACE,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;YAC/B,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;YAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YACvB,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;YACjC,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,EACjC,CAAC;YACD,2GAA2G;YAC3G,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC;QAC1G,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,KAAwB,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC7F,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,qBAAqB,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,iBAAiB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACpE,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QACrD,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,CAAC;QACH,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type LoginErrorCode = "LOGIN_UNAVAILABLE" | "LOGIN_NETWORK" | "LOGIN_WRONG_STATE" | "LOGIN_EXPIRED" | "LOGIN_DENIED" | "LOGIN_CAP_REACHED" | "LOGIN_RATE_LIMITED" | "LOGIN_BAD_URL";
|
|
2
|
+
/** A login failure. `message` is fixed text chosen here, never server output or key material. */
|
|
3
|
+
export declare class LoginError extends Error {
|
|
4
|
+
readonly code: LoginErrorCode;
|
|
5
|
+
constructor(code: LoginErrorCode, message: string);
|
|
6
|
+
}
|
|
7
|
+
export interface DeviceCode {
|
|
8
|
+
readonly deviceCode: string;
|
|
9
|
+
readonly userCode: string;
|
|
10
|
+
readonly verificationUri: string;
|
|
11
|
+
readonly expiresInSec: number;
|
|
12
|
+
readonly intervalSec: number;
|
|
13
|
+
}
|
|
14
|
+
export interface IssuedToken {
|
|
15
|
+
readonly apiKey: string;
|
|
16
|
+
readonly keyPrefix: string;
|
|
17
|
+
}
|
|
18
|
+
export interface DeviceFlowDeps {
|
|
19
|
+
readonly clientVersion: string;
|
|
20
|
+
readonly fetch?: typeof fetch;
|
|
21
|
+
/** Injected in tests so slow_down and expiry run without real waits. */
|
|
22
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
23
|
+
readonly now?: () => number;
|
|
24
|
+
/** Per-request timeout. A slower answer counts as a transient failure. */
|
|
25
|
+
readonly requestTimeoutMs?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare const DEVICE_CODE_PATH = "/api/v1/kya/device/code";
|
|
28
|
+
export declare const DEVICE_TOKEN_PATH = "/api/v1/kya/device/token";
|
|
29
|
+
/** Consecutive transient failures (network, timeout, 5xx) before the login gives up. */
|
|
30
|
+
export declare const MAX_TRANSIENT_FAILURES = 3;
|
|
31
|
+
/**
|
|
32
|
+
* Validate the console origin. https only, or loopback http (tests and local dev).
|
|
33
|
+
* No credentials, query or fragment. Returns the parsed URL (origin only is used).
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateConsoleUrl(raw: string): URL;
|
|
36
|
+
/** Step 1: ask the server for a device and user code. */
|
|
37
|
+
export declare function requestDeviceCode(base: URL, deps: DeviceFlowDeps): Promise<DeviceCode>;
|
|
38
|
+
/**
|
|
39
|
+
* Step 2: poll until the user approves, denies or the code expires.
|
|
40
|
+
* Transient failures (network, timeout, 5xx) are retried up to MAX_TRANSIENT_FAILURES in a row.
|
|
41
|
+
* An unknown answer aborts at once. Only a validated token is returned.
|
|
42
|
+
*/
|
|
43
|
+
export declare function pollForToken(base: URL, code: DeviceCode, deps: DeviceFlowDeps): Promise<IssuedToken>;
|
|
44
|
+
//# sourceMappingURL=device-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/login/device-flow.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,eAAe,GACf,mBAAmB,GACnB,eAAe,GACf,cAAc,GACd,mBAAmB,GACnB,oBAAoB,GACpB,eAAe,CAAC;AAEpB,iGAAiG;AACjG,qBAAa,UAAW,SAAQ,KAAK;IAEjC,QAAQ,CAAC,IAAI,EAAE,cAAc;gBAApB,IAAI,EAAE,cAAc,EAC7B,OAAO,EAAE,MAAM;CAKlB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAC1D,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAI5D,wFAAwF;AACxF,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAMxC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAiBnD;AAmCD,yDAAyD;AACzD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAiD5F;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CA8D1G"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 device authorization grant (RFC 8628) against the hosted console, for `kya login`.
|
|
3
|
+
*
|
|
4
|
+
* Endpoints (hosted side: web device-login controller, behind a feature flag):
|
|
5
|
+
* POST {base}/api/v1/kya/device/code {"client":"kya-cli","version":"x"} -> device_code, user_code, ...
|
|
6
|
+
* POST {base}/api/v1/kya/device/token {"device_code":"..."} -> 200 access_token | 400 error
|
|
7
|
+
* A 404 on either route means the feature is off on that server.
|
|
8
|
+
*
|
|
9
|
+
* Fails closed for login itself: any unknown answer aborts with a LoginError and nothing is stored.
|
|
10
|
+
* Nothing here runs in the OSS hook or evaluate path.
|
|
11
|
+
*/
|
|
12
|
+
import { isLoopbackUrl } from "../config.js";
|
|
13
|
+
/** A login failure. `message` is fixed text chosen here, never server output or key material. */
|
|
14
|
+
export class LoginError extends Error {
|
|
15
|
+
code;
|
|
16
|
+
constructor(code, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.name = "LoginError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export const DEVICE_CODE_PATH = "/api/v1/kya/device/code";
|
|
23
|
+
export const DEVICE_TOKEN_PATH = "/api/v1/kya/device/token";
|
|
24
|
+
/** RFC 8628 section 3.5: slow_down adds 5 seconds to the polling interval. */
|
|
25
|
+
const SLOW_DOWN_STEP_SEC = 5;
|
|
26
|
+
const MAX_INTERVAL_SEC = 60;
|
|
27
|
+
/** Consecutive transient failures (network, timeout, 5xx) before the login gives up. */
|
|
28
|
+
export const MAX_TRANSIENT_FAILURES = 3;
|
|
29
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
30
|
+
const DEVICE_CODE_RE = /^[\x21-\x7e]{16,512}$/;
|
|
31
|
+
const USER_CODE_RE = /^[A-Za-z0-9-]{4,32}$/;
|
|
32
|
+
const KEY_RE = /^[\x21-\x7e]{8,512}$/;
|
|
33
|
+
/**
|
|
34
|
+
* Validate the console origin. https only, or loopback http (tests and local dev).
|
|
35
|
+
* No credentials, query or fragment. Returns the parsed URL (origin only is used).
|
|
36
|
+
*/
|
|
37
|
+
export function validateConsoleUrl(raw) {
|
|
38
|
+
const bad = (why) => {
|
|
39
|
+
throw new LoginError("LOGIN_BAD_URL", `hosted console URL ${why}`);
|
|
40
|
+
};
|
|
41
|
+
if (!raw || /[^\x21-\x7e]/.test(raw))
|
|
42
|
+
bad("must be a non-empty ASCII URL");
|
|
43
|
+
let url;
|
|
44
|
+
try {
|
|
45
|
+
url = new URL(raw);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return bad("is not a valid URL");
|
|
49
|
+
}
|
|
50
|
+
if (url.username || url.password)
|
|
51
|
+
bad("must not carry credentials");
|
|
52
|
+
if (url.search || url.hash)
|
|
53
|
+
bad("must not carry a query or fragment");
|
|
54
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopbackUrl(raw))) {
|
|
55
|
+
bad("must be https (http is allowed for loopback only)");
|
|
56
|
+
}
|
|
57
|
+
return url;
|
|
58
|
+
}
|
|
59
|
+
function isObject(v) {
|
|
60
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
61
|
+
}
|
|
62
|
+
/** One JSON POST. Returns the status and parsed body (undefined when not JSON). Throws on network or timeout. */
|
|
63
|
+
async function postJson(url, body, deps) {
|
|
64
|
+
const doFetch = deps.fetch ?? fetch;
|
|
65
|
+
const controller = new AbortController();
|
|
66
|
+
const timer = setTimeout(() => controller.abort(), deps.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
67
|
+
try {
|
|
68
|
+
const res = await doFetch(url, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
71
|
+
body: JSON.stringify(body),
|
|
72
|
+
signal: controller.signal,
|
|
73
|
+
redirect: "error",
|
|
74
|
+
});
|
|
75
|
+
let json;
|
|
76
|
+
try {
|
|
77
|
+
json = await res.json();
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
json = undefined;
|
|
81
|
+
}
|
|
82
|
+
return { status: res.status, json };
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Step 1: ask the server for a device and user code. */
|
|
89
|
+
export async function requestDeviceCode(base, deps) {
|
|
90
|
+
let res;
|
|
91
|
+
try {
|
|
92
|
+
res = await postJson(new URL(DEVICE_CODE_PATH, base).toString(), { client: "kya-cli", version: deps.clientVersion }, deps);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
throw new LoginError("LOGIN_NETWORK", "could not reach the hosted console; nothing was saved");
|
|
96
|
+
}
|
|
97
|
+
if (res.status === 404) {
|
|
98
|
+
throw new LoginError("LOGIN_UNAVAILABLE", "hosted login is not available on this server; nothing was saved");
|
|
99
|
+
}
|
|
100
|
+
if (res.status === 429) {
|
|
101
|
+
throw new LoginError("LOGIN_RATE_LIMITED", "too many login starts from this network; wait a minute, then run kya login again");
|
|
102
|
+
}
|
|
103
|
+
if (res.status !== 200 || !isObject(res.json)) {
|
|
104
|
+
throw new LoginError("LOGIN_WRONG_STATE", "the hosted console sent an unexpected answer; nothing was saved");
|
|
105
|
+
}
|
|
106
|
+
const o = res.json;
|
|
107
|
+
const deviceCode = typeof o.device_code === "string" ? o.device_code : "";
|
|
108
|
+
const userCode = typeof o.user_code === "string" ? o.user_code : "";
|
|
109
|
+
const uri = typeof o.verification_uri === "string" ? o.verification_uri : "";
|
|
110
|
+
const expires = typeof o.expires_in === "number" && Number.isInteger(o.expires_in) ? o.expires_in : 0;
|
|
111
|
+
const interval = o.interval === undefined ? 5 : typeof o.interval === "number" && Number.isInteger(o.interval) ? o.interval : 0;
|
|
112
|
+
let verification;
|
|
113
|
+
try {
|
|
114
|
+
verification = new URL(uri);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
verification = undefined;
|
|
118
|
+
}
|
|
119
|
+
// The verification page must live on the same origin the user chose: never open a foreign page.
|
|
120
|
+
const sameOrigin = verification !== undefined && verification.origin === base.origin;
|
|
121
|
+
if (!DEVICE_CODE_RE.test(deviceCode) ||
|
|
122
|
+
!USER_CODE_RE.test(userCode) ||
|
|
123
|
+
!sameOrigin ||
|
|
124
|
+
expires < 1 ||
|
|
125
|
+
expires > 3600 ||
|
|
126
|
+
interval < 1 ||
|
|
127
|
+
interval > MAX_INTERVAL_SEC) {
|
|
128
|
+
throw new LoginError("LOGIN_WRONG_STATE", "the hosted console sent an unexpected answer; nothing was saved");
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
deviceCode,
|
|
132
|
+
userCode,
|
|
133
|
+
verificationUri: verification.toString(),
|
|
134
|
+
expiresInSec: expires,
|
|
135
|
+
intervalSec: interval,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Step 2: poll until the user approves, denies or the code expires.
|
|
140
|
+
* Transient failures (network, timeout, 5xx) are retried up to MAX_TRANSIENT_FAILURES in a row.
|
|
141
|
+
* An unknown answer aborts at once. Only a validated token is returned.
|
|
142
|
+
*/
|
|
143
|
+
export async function pollForToken(base, code, deps) {
|
|
144
|
+
const now = deps.now ?? Date.now;
|
|
145
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
146
|
+
const deadline = now() + code.expiresInSec * 1000;
|
|
147
|
+
const tokenUrl = new URL(DEVICE_TOKEN_PATH, base).toString();
|
|
148
|
+
let interval = code.intervalSec;
|
|
149
|
+
let transient = 0;
|
|
150
|
+
const expired = () => new LoginError("LOGIN_EXPIRED", "the code expired before it was approved; nothing was saved");
|
|
151
|
+
for (;;) {
|
|
152
|
+
if (now() >= deadline)
|
|
153
|
+
throw expired();
|
|
154
|
+
await sleep(interval * 1000);
|
|
155
|
+
if (now() >= deadline)
|
|
156
|
+
throw expired();
|
|
157
|
+
let res;
|
|
158
|
+
try {
|
|
159
|
+
res = await postJson(tokenUrl, { device_code: code.deviceCode }, deps);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
if (++transient >= MAX_TRANSIENT_FAILURES) {
|
|
163
|
+
throw new LoginError("LOGIN_NETWORK", "lost the hosted console while waiting; nothing was saved");
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (res.status === 404) {
|
|
168
|
+
throw new LoginError("LOGIN_UNAVAILABLE", "hosted login is not available on this server; nothing was saved");
|
|
169
|
+
}
|
|
170
|
+
if (res.status >= 500) {
|
|
171
|
+
if (++transient >= MAX_TRANSIENT_FAILURES) {
|
|
172
|
+
throw new LoginError("LOGIN_NETWORK", "lost the hosted console while waiting; nothing was saved");
|
|
173
|
+
}
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
transient = 0;
|
|
177
|
+
if (res.status === 200) {
|
|
178
|
+
const o = isObject(res.json) ? res.json : {};
|
|
179
|
+
const apiKey = typeof o.access_token === "string" ? o.access_token : "";
|
|
180
|
+
if (!KEY_RE.test(apiKey)) {
|
|
181
|
+
throw new LoginError("LOGIN_WRONG_STATE", "the hosted console sent an unexpected answer; nothing was saved");
|
|
182
|
+
}
|
|
183
|
+
const keyPrefix = typeof o.key_prefix === "string" && o.key_prefix ? o.key_prefix : apiKey.slice(0, 12);
|
|
184
|
+
return { apiKey, keyPrefix };
|
|
185
|
+
}
|
|
186
|
+
const error = isObject(res.json) && typeof res.json.error === "string" ? res.json.error : "";
|
|
187
|
+
if (res.status === 429 || error === "slow_down") {
|
|
188
|
+
interval = Math.min(MAX_INTERVAL_SEC, interval + SLOW_DOWN_STEP_SEC);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (error === "authorization_pending")
|
|
192
|
+
continue;
|
|
193
|
+
if (error === "expired_token")
|
|
194
|
+
throw expired();
|
|
195
|
+
if (error === "access_denied") {
|
|
196
|
+
throw new LoginError("LOGIN_DENIED", "the request was denied in the hosted console; nothing was saved");
|
|
197
|
+
}
|
|
198
|
+
if (error === "api_key.cap_reached") {
|
|
199
|
+
// Approved on the console, but the workspace is at its key cap. The request stays approved on the server.
|
|
200
|
+
throw new LoginError("LOGIN_CAP_REACHED", "this workspace has the maximum number of API keys; revoke an unused key in the console, then run kya login again");
|
|
201
|
+
}
|
|
202
|
+
throw new LoginError("LOGIN_WRONG_STATE", "the hosted console sent an unexpected answer; nothing was saved");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=device-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-flow.js","sourceRoot":"","sources":["../../src/login/device-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type TargetFamily } from "./target-class.js";
|
|
2
|
+
import { type MapGraph, type ToolClass, type TrailEventLike, type Verdicts } from "./types.js";
|
|
3
|
+
export interface BuildOptions {
|
|
4
|
+
/** Per-install salt for sessionHash. Tests pass a fixed value. */
|
|
5
|
+
salt: string;
|
|
6
|
+
/** Needed when `days` is set. The builder reads no clock. */
|
|
7
|
+
now?: Date;
|
|
8
|
+
/** Hard node cap per view (schema section 7). Default 100. */
|
|
9
|
+
maxNodes?: number;
|
|
10
|
+
/** Keep only rows at or after now minus `days`. */
|
|
11
|
+
days?: number;
|
|
12
|
+
/** Full export: no grouping, no caps. */
|
|
13
|
+
uncapped?: boolean;
|
|
14
|
+
/** Counts from readTrailFile, added to the view. */
|
|
15
|
+
read?: {
|
|
16
|
+
rowsRead: number;
|
|
17
|
+
rowsSkipped: number;
|
|
18
|
+
rowsOutOfWindow: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface TrailRead {
|
|
22
|
+
events: TrailEventLike[];
|
|
23
|
+
rowsRead: number;
|
|
24
|
+
rowsSkipped: number;
|
|
25
|
+
rowsOutOfWindow: number;
|
|
26
|
+
}
|
|
27
|
+
type Bucket = keyof Verdicts;
|
|
28
|
+
/** Shape check for one row. Bad rows are skipped and counted, never thrown. */
|
|
29
|
+
export declare function isTrailRow(x: unknown): x is TrailEventLike;
|
|
30
|
+
/** Verdict bucket, schema section 3. First match wins. */
|
|
31
|
+
export declare function bucketOf(e: TrailEventLike): Bucket;
|
|
32
|
+
/** Tool class, schema 4.1. First match wins. */
|
|
33
|
+
export declare function toolClassOf(e: TrailEventLike): ToolClass;
|
|
34
|
+
/** A stored class is trusted only when it has the shape the writer produces: short, printable, `family:rest` or `none`. */
|
|
35
|
+
export declare const SAFE_CLASS: RegExp;
|
|
36
|
+
export declare function targetOf(e: TrailEventLike): {
|
|
37
|
+
cls: string;
|
|
38
|
+
family: TargetFamily;
|
|
39
|
+
};
|
|
40
|
+
/** Salted hash of one session (16 hex). The raw session id can be a host product id, so it never leaves the machine. */
|
|
41
|
+
export declare function sessionHash(salt: string, product: string, sessionId: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Builds the map graph from trail rows. Pure: same rows and options give the same JSON.
|
|
44
|
+
* Caps (schema section 7): 25 agents, 20 tools, 40 targets shown; the rest fold into "other" nodes.
|
|
45
|
+
* Deny, hold and secret targets are always shown. The node and edge caps shrink the per-type caps until they fit.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildMap(events: Iterable<TrailEventLike>, opts: BuildOptions): MapGraph;
|
|
48
|
+
/**
|
|
49
|
+
* Streams the whole trail file line by line. Unlike readTrail (tail read, 1 MB), this reads every row.
|
|
50
|
+
* Bad lines (not JSON, or wrong shape) are skipped and counted. Rows older than `days` are counted, not kept.
|
|
51
|
+
* A filter, not a stop: the file is not sorted (import rows dated 2025 come first).
|
|
52
|
+
*/
|
|
53
|
+
export declare function readTrailFile(path: string, opts?: {
|
|
54
|
+
days?: number;
|
|
55
|
+
now?: Date;
|
|
56
|
+
}): Promise<TrailRead>;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/map/build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAIL,KAAK,QAAQ,EAGb,KAAK,SAAS,EAEd,KAAK,cAAc,EACnB,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,IAAI,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAYD,KAAK,MAAM,GAAG,MAAM,QAAQ,CAAC;AAE7B,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,CAa1D;AAED,0DAA0D;AAC1D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAMlD;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,CAAC,EAAE,cAAc,GAAG,SAAS,CAWxD;AAQD,2HAA2H;AAC3H,eAAO,MAAM,UAAU,QAAyC,CAAC;AAEjE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,cAAc,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,CAMjF;AAkGD,wHAAwH;AACxH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpF;AAgOD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,YAAY,GAAG,QAAQ,CA8DvF;AAaD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAA;CAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CA4B9G"}
|