@shield-agent/kya 0.1.20 → 0.1.22
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/.mcp.json +1 -1
- package/LIMITATIONS.md +2 -2
- package/README.md +8 -5
- package/claude/claude_desktop_config.example.json +1 -1
- package/dist/commands/dash.d.ts.map +1 -1
- package/dist/commands/dash.js +337 -83
- package/dist/commands/dash.js.map +1 -1
- package/dist/commands/decide.d.ts.map +1 -1
- package/dist/commands/decide.js +5 -1
- package/dist/commands/decide.js.map +1 -1
- package/dist/dash/dash.d.ts +6 -0
- package/dist/dash/dash.d.ts.map +1 -1
- package/dist/dash/dash.js +22 -5
- 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 +11 -0
- package/dist/dash/input.js.map +1 -1
- package/dist/dash/policy-cache.d.ts +18 -0
- package/dist/dash/policy-cache.d.ts.map +1 -0
- package/dist/dash/policy-cache.js +37 -0
- package/dist/dash/policy-cache.js.map +1 -0
- package/dist/dash/render.d.ts +3 -1
- package/dist/dash/render.d.ts.map +1 -1
- package/dist/dash/render.js +25 -11
- package/dist/dash/render.js.map +1 -1
- package/dist/dash/screens/free.d.ts +9 -1
- package/dist/dash/screens/free.d.ts.map +1 -1
- package/dist/dash/screens/free.js +25 -7
- package/dist/dash/screens/free.js.map +1 -1
- package/dist/orr/agentshield.d.ts.map +1 -1
- package/dist/orr/agentshield.js +2 -1
- package/dist/orr/agentshield.js.map +1 -1
- package/dist/orr/scorecard.d.ts +2 -0
- package/dist/orr/scorecard.d.ts.map +1 -1
- package/dist/orr/scorecard.js +20 -1
- package/dist/orr/scorecard.js.map +1 -1
- package/gemini/settings.example.json +1 -1
- package/grok/README.md +1 -1
- package/manifest.json +1 -1
- package/mcp.json +1 -1
- package/openai/codex.config.example.toml +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/.mcp.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"shield-kya": {
|
|
4
4
|
"command": "npx",
|
|
5
|
-
"args": ["--no-install", "@shield-agent/kya@0.1.
|
|
5
|
+
"args": ["--no-install", "@shield-agent/kya@0.1.22", "serve-mcp", "--stdio"],
|
|
6
6
|
"env": {
|
|
7
7
|
"KYA_BASE_URL": "https://shield-agent.com",
|
|
8
8
|
"KYA_API_KEY": "${KYA_API_KEY}",
|
package/LIMITATIONS.md
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
- If a host never wraps tools, session shrink cannot stop it. Spawn without a control plane cannot be gated.
|
|
7
7
|
- `wrap` evaluates (and may open a pending ticket). It never executes the side effect. Offline wrap does not call the approval API.
|
|
8
8
|
- `invoke` authorizes on a live plane after Allow or APPROVED. It never runs the customer write in the CLI. There is no `--offline` invoke.
|
|
9
|
-
- The TUI (`dash`)
|
|
10
|
-
- Human decide is `kya approve --id` / `kya reject --id` against `POST /api/v1/kya/approvals/{id}/approve|reject` (`kya.approve` scope).
|
|
9
|
+
- The TUI (`dash`) can `a`/`x` decide only after `y` confirm, and only with a JWT that has `kya.approve`. Machine `sk_*` keys are refused (print the CLI hint instead).
|
|
10
|
+
- Human decide is also `kya approve --id` / `kya reject --id` against `POST /api/v1/kya/approvals/{id}/approve|reject` (`kya.approve` scope).
|
|
11
11
|
- Shield KYA evaluate/approve is the only policy decision point (`ALLOW` / `DENY` / `REQUIRE_APPROVE`). Scanners, `--scorecard`, `harness.agentshield`, and the ORR CLI write evidence. They never ALLOW a side effect. AgentShield is opt-in (`--producer harness.agentshield` or `--agentshield-json`). The CLI never passes `--fix`, never starts MiniClaw, and never installs `ecc-agentshield`.
|
|
12
12
|
- Dialog safety belongs in a guardrails product.
|
|
13
13
|
- Multi-language in-process runtimes are out of scope.
|
package/README.md
CHANGED
|
@@ -33,10 +33,13 @@ npx @shield-agent/kya register-agent --name solo-builder --version-hash dev-loca
|
|
|
33
33
|
npx @shield-agent/kya eval-tool --tool-id org.sample.never.event --irreversible
|
|
34
34
|
npx @shield-agent/kya serve-mcp --stdio
|
|
35
35
|
|
|
36
|
-
# Terminal
|
|
36
|
+
# Terminal desk (FREE personal panes; --offline works without a key)
|
|
37
37
|
npx @shield-agent/kya dash --once --offline
|
|
38
|
+
# Interactive TTY: 1-8 panes, e force-eval, p auto-refresh, y confirms kill/shrink/decide
|
|
39
|
+
npx @shield-agent/kya dash
|
|
38
40
|
```
|
|
39
41
|
|
|
42
|
+
|
|
40
43
|
Install hub: [https://shield-agent.com/install](https://shield-agent.com/install)
|
|
41
44
|
|
|
42
45
|
## Dual plane
|
|
@@ -79,7 +82,7 @@ MCP Registry entry: `server.json` plus package `mcpName` `io.github.The-Pixel-Bo
|
|
|
79
82
|
"mcpServers": {
|
|
80
83
|
"shield-kya": {
|
|
81
84
|
"command": "npx",
|
|
82
|
-
"args": ["--no-install", "@shield-agent/kya@0.1.
|
|
85
|
+
"args": ["--no-install", "@shield-agent/kya@0.1.22", "serve-mcp", "--stdio"],
|
|
83
86
|
"env": {
|
|
84
87
|
"KYA_BASE_URL": "http://127.0.0.1:8090",
|
|
85
88
|
"KYA_API_KEY": "${KYA_API_KEY}",
|
|
@@ -98,7 +101,7 @@ npx @shield-agent/kya approve --id <approval-id>
|
|
|
98
101
|
npx @shield-agent/kya reject --id <approval-id>
|
|
99
102
|
```
|
|
100
103
|
|
|
101
|
-
`wrap` evaluates and may open a pending ticket. It never executes the side effect. `invoke` asks the live plane to authorize after Allow or APPROVED. It does not run the write on this machine. The TUI (`dash`)
|
|
104
|
+
`wrap` evaluates and may open a pending ticket. It never executes the side effect. `invoke` asks the live plane to authorize after Allow or APPROVED. It does not run the write on this machine. The TUI (`dash`) can `a`/`x` decide only after `y` confirm with a JWT (`sk_*` refused).
|
|
102
105
|
|
|
103
106
|
## Claude connector
|
|
104
107
|
|
|
@@ -106,7 +109,7 @@ npx @shield-agent/kya reject --id <approval-id>
|
|
|
106
109
|
|
|
107
110
|
```bash
|
|
108
111
|
# Prefer a preinstalled package (no registry auto-install):
|
|
109
|
-
npx --no-install @shield-agent/kya@0.1.
|
|
112
|
+
npx --no-install @shield-agent/kya@0.1.22 serve-mcp --stdio
|
|
110
113
|
# Or after npm i -g / local install:
|
|
111
114
|
kya serve-mcp --stdio
|
|
112
115
|
```
|
|
@@ -117,7 +120,7 @@ Copy `claude/claude_desktop_config.example.json` into Claude Desktop MCP setting
|
|
|
117
120
|
|
|
118
121
|
## OpenAI (Codex / Responses)
|
|
119
122
|
|
|
120
|
-
**Codex CLI / IDE:** copy `openai/codex.config.example.toml` into `~/.codex/config.toml`. Local stdio uses `npx --no-install @shield-agent/kya@0.1.
|
|
123
|
+
**Codex CLI / IDE:** copy `openai/codex.config.example.toml` into `~/.codex/config.toml`. Local stdio uses `npx --no-install @shield-agent/kya@0.1.22 serve-mcp --stdio`. Hosted Codex uses `url = "https://shield-agent.com/mcp"` with `bearer_token_env_var = "KYA_API_KEY"`.
|
|
121
124
|
|
|
122
125
|
**Responses API:** see `openai/responses-mcp.example.json` (`server_url` + `Authorization: Bearer <KYA_API_KEY>`).
|
|
123
126
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"shield-kya": {
|
|
4
4
|
"command": "npx",
|
|
5
|
-
"args": ["--no-install", "@shield-agent/kya@0.1.
|
|
5
|
+
"args": ["--no-install", "@shield-agent/kya@0.1.22", "serve-mcp", "--stdio"],
|
|
6
6
|
"env": {
|
|
7
7
|
"KYA_BASE_URL": "https://shield-agent.com",
|
|
8
8
|
"KYA_API_KEY": "${KYA_API_KEY}",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dash.d.ts","sourceRoot":"","sources":["../../src/commands/dash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"dash.d.ts","sourceRoot":"","sources":["../../src/commands/dash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AA0C1D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;CAChB,CAkBA;AASD,wBAAsB,OAAO,CAC3B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,OAAO,CAAC,MAAM,CAAC,CAsZjB"}
|
package/dist/commands/dash.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { stdin as stdinFd, stdout as stdoutFd } from "node:process";
|
|
2
2
|
import { emitKeypressEvents } from "node:readline";
|
|
3
|
-
import { KyaHttpClient } from "../client.js";
|
|
3
|
+
import { isMachineApiKey, KyaHttpClient } from "../client.js";
|
|
4
4
|
import { AuthRequiredError } from "../errors.js";
|
|
5
5
|
import { flagBool, flagString } from "../parse-args.js";
|
|
6
6
|
import { renderDash } from "../dash/dash.js";
|
|
7
7
|
import { mapKey } from "../dash/input.js";
|
|
8
|
+
import { PolicySampleCache } from "../dash/policy-cache.js";
|
|
9
|
+
import { assertNoSecrets } from "../dash/render.js";
|
|
8
10
|
import { runWrap, wrapExitCode } from "./wrap.js";
|
|
9
11
|
import { runInvoke } from "./invoke.js";
|
|
10
12
|
import { runKillAgent, runShrinkSession } from "./ops.js";
|
|
11
13
|
import { runOrr } from "./orr.js";
|
|
14
|
+
const AUTO_REFRESH_MS = 5_000;
|
|
15
|
+
/** TUI ORR stays first_party only — Scorecard spawn is CLI-explicit (no env leak / stdin race). */
|
|
12
16
|
function deskOrr(cwd) {
|
|
13
17
|
return runOrr({
|
|
14
18
|
path: cwd,
|
|
@@ -30,6 +34,7 @@ const PANES = [
|
|
|
30
34
|
"sessions",
|
|
31
35
|
"orr",
|
|
32
36
|
"mcp",
|
|
37
|
+
"sandbox",
|
|
33
38
|
"dashboard",
|
|
34
39
|
"cases",
|
|
35
40
|
"metrics",
|
|
@@ -57,21 +62,14 @@ export async function runDash(config, parsed, io, env) {
|
|
|
57
62
|
const opts = dashOptionsFromArgs(parsed);
|
|
58
63
|
const tty = io.isTty ?? Boolean(stdinFd.isTTY && stdoutFd.isTTY);
|
|
59
64
|
const once = opts.once || !tty;
|
|
60
|
-
let client;
|
|
61
|
-
if (!opts.offline && config.apiKey) {
|
|
62
|
-
client = new KyaHttpClient({
|
|
63
|
-
baseUrl: config.baseUrl,
|
|
64
|
-
apiKey: config.apiKey,
|
|
65
|
-
host: config.host,
|
|
66
|
-
agentId: config.agentId,
|
|
67
|
-
requireApiKey: true,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
const fetchPlane = client
|
|
71
|
-
? async () => client.request("/api/v1/kya/entitlement", { signal: AbortSignal.timeout(5000) })
|
|
72
|
-
: undefined;
|
|
73
|
-
let pane = opts.pane;
|
|
74
65
|
let offline = opts.offline;
|
|
66
|
+
let client = makeClient(config, offline);
|
|
67
|
+
const policyCache = new PolicySampleCache();
|
|
68
|
+
let forcePolicyEval = false;
|
|
69
|
+
let autoRefresh = false;
|
|
70
|
+
let pending;
|
|
71
|
+
let busy = false;
|
|
72
|
+
let pane = opts.pane;
|
|
75
73
|
let cursor = 0;
|
|
76
74
|
let orrSummary;
|
|
77
75
|
let last = {
|
|
@@ -79,48 +77,222 @@ export async function runDash(config, parsed, io, env) {
|
|
|
79
77
|
approvals: [],
|
|
80
78
|
sessions: [],
|
|
81
79
|
};
|
|
82
|
-
const
|
|
80
|
+
const confirmLine = () => pending?.prompt;
|
|
81
|
+
const paint = async (optsPaint) => {
|
|
83
82
|
const snap = await renderDash({ ...config, offline }, {
|
|
84
83
|
once: true,
|
|
85
84
|
offline,
|
|
86
85
|
pane,
|
|
87
86
|
client,
|
|
88
|
-
fetchPlaneEntitlement:
|
|
87
|
+
fetchPlaneEntitlement: client
|
|
88
|
+
? async () => client.request("/api/v1/kya/entitlement", { signal: AbortSignal.timeout(5000) })
|
|
89
|
+
: undefined,
|
|
89
90
|
cursor,
|
|
90
91
|
orrSummary,
|
|
92
|
+
policyCache,
|
|
93
|
+
forcePolicyEval,
|
|
94
|
+
confirmLine: confirmLine(),
|
|
91
95
|
}, env);
|
|
96
|
+
forcePolicyEval = false;
|
|
92
97
|
last = {
|
|
93
98
|
agents: [...snap.agents],
|
|
94
99
|
approvals: [...snap.approvals],
|
|
95
100
|
sessions: [...snap.sessions],
|
|
96
101
|
};
|
|
102
|
+
// Never ANSI-clear while a confirm is armed (keeps the prompt visible).
|
|
103
|
+
if (!once && tty && optsPaint?.clear !== false && !pending) {
|
|
104
|
+
try {
|
|
105
|
+
stdoutFd.write("\x1b[2J\x1b[H");
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* ignore */
|
|
109
|
+
}
|
|
110
|
+
}
|
|
97
111
|
io.log(snap.frame);
|
|
98
112
|
return snap;
|
|
99
113
|
};
|
|
100
114
|
const selected = (rows) => rows.length === 0 ? undefined : rows[Math.min(cursor, rows.length - 1)];
|
|
101
115
|
if (once) {
|
|
102
|
-
await paint();
|
|
116
|
+
await paint({ clear: false });
|
|
103
117
|
return 0;
|
|
104
118
|
}
|
|
105
|
-
await paint();
|
|
119
|
+
await paint({ clear: false });
|
|
106
120
|
emitKeypressEvents(stdinFd);
|
|
107
121
|
stdinFd.setRawMode?.(true);
|
|
108
122
|
stdinFd.resume();
|
|
123
|
+
// Bracketed paste: ignore paste chunks so `ay`/`ky` cannot one-shot confirm.
|
|
124
|
+
try {
|
|
125
|
+
stdoutFd.write("\x1b[?2004h");
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
/* ignore */
|
|
129
|
+
}
|
|
109
130
|
return await new Promise((resolve) => {
|
|
110
|
-
let
|
|
131
|
+
let refreshTimer;
|
|
132
|
+
let pasteBuf = "";
|
|
133
|
+
let inPaste = false;
|
|
134
|
+
const stopRefresh = () => {
|
|
135
|
+
if (refreshTimer) {
|
|
136
|
+
clearInterval(refreshTimer);
|
|
137
|
+
refreshTimer = undefined;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const startRefresh = () => {
|
|
141
|
+
stopRefresh();
|
|
142
|
+
if (!autoRefresh || offline || !client)
|
|
143
|
+
return;
|
|
144
|
+
refreshTimer = setInterval(() => {
|
|
145
|
+
if (busy || pending)
|
|
146
|
+
return;
|
|
147
|
+
busy = true;
|
|
148
|
+
void paint()
|
|
149
|
+
.catch((err) => {
|
|
150
|
+
if (err instanceof AuthRequiredError)
|
|
151
|
+
io.error(err.message);
|
|
152
|
+
else
|
|
153
|
+
io.error(err instanceof Error ? err.message : String(err));
|
|
154
|
+
})
|
|
155
|
+
.finally(() => {
|
|
156
|
+
busy = false;
|
|
157
|
+
});
|
|
158
|
+
}, AUTO_REFRESH_MS);
|
|
159
|
+
};
|
|
160
|
+
const syncClient = () => {
|
|
161
|
+
client = makeClient(config, offline);
|
|
162
|
+
if (offline)
|
|
163
|
+
policyCache.invalidate();
|
|
164
|
+
if (!offline && client && autoRefresh)
|
|
165
|
+
startRefresh();
|
|
166
|
+
else
|
|
167
|
+
stopRefresh();
|
|
168
|
+
};
|
|
169
|
+
const stillLive = () => !offline && Boolean(client);
|
|
170
|
+
const arm = (job) => {
|
|
171
|
+
pending = job;
|
|
172
|
+
void paint({ clear: false });
|
|
173
|
+
};
|
|
174
|
+
const runPaint = () => {
|
|
175
|
+
if (busy)
|
|
176
|
+
return;
|
|
177
|
+
busy = true;
|
|
178
|
+
void paint()
|
|
179
|
+
.catch((err) => {
|
|
180
|
+
if (err instanceof AuthRequiredError)
|
|
181
|
+
io.error(err.message);
|
|
182
|
+
else
|
|
183
|
+
io.error(err instanceof Error ? err.message : String(err));
|
|
184
|
+
})
|
|
185
|
+
.finally(() => {
|
|
186
|
+
busy = false;
|
|
187
|
+
});
|
|
188
|
+
};
|
|
111
189
|
const onKey = (str, key) => {
|
|
190
|
+
const seq = key?.sequence ?? str ?? "";
|
|
191
|
+
// Bracketed paste begin/end
|
|
192
|
+
if (seq.includes("\x1b[200~") || str === "\x1b[200~") {
|
|
193
|
+
inPaste = true;
|
|
194
|
+
pasteBuf = "";
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (inPaste) {
|
|
198
|
+
if (seq.includes("\x1b[201~") || str === "\x1b[201~") {
|
|
199
|
+
inPaste = false;
|
|
200
|
+
pasteBuf = "";
|
|
201
|
+
if (pending) {
|
|
202
|
+
pending = undefined;
|
|
203
|
+
io.log("paste ignored — confirm cancelled");
|
|
204
|
+
void paint({ clear: false });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
pasteBuf += str ?? "";
|
|
209
|
+
}
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
112
212
|
const raw = key?.ctrl && key.name === "c" ? "\u0003" : (str ?? key?.name ?? "");
|
|
113
213
|
const action = mapKey(raw);
|
|
214
|
+
if (pending) {
|
|
215
|
+
if (action.type === "confirm-yes") {
|
|
216
|
+
const job = pending;
|
|
217
|
+
pending = undefined;
|
|
218
|
+
if (!stillLive()) {
|
|
219
|
+
io.log("Need a live plane (drop offline / set KYA_API_KEY).");
|
|
220
|
+
void paint({ clear: false });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const liveClient = client;
|
|
224
|
+
const cfg = { ...config, offline: false };
|
|
225
|
+
busy = true;
|
|
226
|
+
void (async () => {
|
|
227
|
+
try {
|
|
228
|
+
if (!stillLive()) {
|
|
229
|
+
io.log("offline mid-flight — aborted");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (job.kind === "kill") {
|
|
233
|
+
const killed = await runKillAgent(cfg, job.id, liveClient);
|
|
234
|
+
if (!stillLive())
|
|
235
|
+
return;
|
|
236
|
+
io.log(`${killed.status ?? "PAUSED"} ${killed.id}`);
|
|
237
|
+
}
|
|
238
|
+
else if (job.kind === "shrink") {
|
|
239
|
+
const sh = await runShrinkSession(cfg, job.id, job.to, liveClient);
|
|
240
|
+
if (!stillLive())
|
|
241
|
+
return;
|
|
242
|
+
io.log(`clearance ${sh.from} → ${sh.to}`);
|
|
243
|
+
}
|
|
244
|
+
else if (job.kind === "decide") {
|
|
245
|
+
// Machine sk_* keys must not decide — JWT / kya.approve only.
|
|
246
|
+
if (isMachineApiKey(config.apiKey ?? "")) {
|
|
247
|
+
io.log(`Machine API keys cannot ${job.decision} from the TUI. Run: kya ${job.decision} --id ${job.id}`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const res = await liveClient.decideApproval(job.id, job.decision);
|
|
251
|
+
if (!stillLive())
|
|
252
|
+
return;
|
|
253
|
+
io.log(`${job.decision} → ${res.status} ${res.id}`);
|
|
254
|
+
}
|
|
255
|
+
else if (job.kind === "invoke") {
|
|
256
|
+
const result = await runInvoke(cfg, { toolId: job.toolId, irreversible: true }, liveClient);
|
|
257
|
+
if (!stillLive())
|
|
258
|
+
return;
|
|
259
|
+
io.log(`invoke ${result.verdict} dispatched=${result.dispatched} ${result.sideEffect} (does not run the write)`);
|
|
260
|
+
}
|
|
261
|
+
else if (job.kind === "wrap") {
|
|
262
|
+
const result = await runWrap(cfg, { toolId: "org.sample.data.write", irreversible: true }, liveClient);
|
|
263
|
+
if (!stillLive())
|
|
264
|
+
return;
|
|
265
|
+
io.log(`wrap exit ${wrapExitCode(result)} ${result.eval.response.verdict}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
if (err instanceof AuthRequiredError)
|
|
270
|
+
io.error(err.message);
|
|
271
|
+
else
|
|
272
|
+
io.error(err instanceof Error ? err.message : String(err));
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
busy = false;
|
|
276
|
+
await paint();
|
|
277
|
+
}
|
|
278
|
+
})();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
pending = undefined;
|
|
282
|
+
io.log("cancelled");
|
|
283
|
+
void paint({ clear: false });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
114
286
|
if (action.type === "quit") {
|
|
115
287
|
cleanup();
|
|
116
288
|
resolve(0);
|
|
117
289
|
return;
|
|
118
290
|
}
|
|
119
291
|
if (action.type === "help") {
|
|
120
|
-
io.log("1-
|
|
292
|
+
io.log("1-8 panes · j/k · e eval · p auto-refresh · w wrap · k kill · i invoke · b/R shrink · o orr · t passport · a/x decide (y confirm; sk_* refused) · O offline · q");
|
|
121
293
|
return;
|
|
122
294
|
}
|
|
123
|
-
if (action.type === "noop" ||
|
|
295
|
+
if (action.type === "noop" || busy)
|
|
124
296
|
return;
|
|
125
297
|
if (action.type === "up") {
|
|
126
298
|
cursor = Math.max(0, cursor - 1);
|
|
@@ -130,6 +302,22 @@ export async function runDash(config, parsed, io, env) {
|
|
|
130
302
|
}
|
|
131
303
|
else if (action.type === "offline") {
|
|
132
304
|
offline = !offline;
|
|
305
|
+
syncClient();
|
|
306
|
+
io.log(offline ? "offline (sample PEP only)" : "live plane");
|
|
307
|
+
}
|
|
308
|
+
else if (action.type === "force-policy-eval") {
|
|
309
|
+
forcePolicyEval = true;
|
|
310
|
+
policyCache.invalidate();
|
|
311
|
+
pane = "policy";
|
|
312
|
+
}
|
|
313
|
+
else if (action.type === "toggle-auto-refresh") {
|
|
314
|
+
autoRefresh = !autoRefresh;
|
|
315
|
+
io.log(autoRefresh ? `auto-refresh ${AUTO_REFRESH_MS / 1000}s` : "auto-refresh off");
|
|
316
|
+
if (autoRefresh)
|
|
317
|
+
startRefresh();
|
|
318
|
+
else
|
|
319
|
+
stopRefresh();
|
|
320
|
+
return;
|
|
133
321
|
}
|
|
134
322
|
else if (action.type === "pane" && action.pane) {
|
|
135
323
|
pane = action.pane;
|
|
@@ -139,62 +327,108 @@ export async function runDash(config, parsed, io, env) {
|
|
|
139
327
|
io.log("Register: kya register-agent --name solo --version-hash dev-local");
|
|
140
328
|
return;
|
|
141
329
|
}
|
|
142
|
-
else if (
|
|
143
|
-
|
|
144
|
-
|
|
330
|
+
else if (action.type === "refresh") {
|
|
331
|
+
// fall through
|
|
332
|
+
}
|
|
333
|
+
else if (!offline && client) {
|
|
334
|
+
if (action.type === "wrap") {
|
|
335
|
+
arm({ kind: "wrap", prompt: "wrap sample org.sample.data.write (ticket only)?" });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (action.type === "kill") {
|
|
339
|
+
const row = selected(last.agents);
|
|
340
|
+
if (!row)
|
|
341
|
+
return;
|
|
342
|
+
arm({ kind: "kill", id: row.id, prompt: `kill agent ${row.id}?` });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (action.type === "invoke") {
|
|
346
|
+
const row = selected(last.approvals);
|
|
347
|
+
if (!row)
|
|
348
|
+
return;
|
|
349
|
+
const toolId = String(row.action ?? "org.sample.data.write");
|
|
350
|
+
arm({
|
|
351
|
+
kind: "invoke",
|
|
352
|
+
id: row.id,
|
|
353
|
+
toolId,
|
|
354
|
+
prompt: `invoke ${row.id} tool=${toolId}? (does not run the write)`,
|
|
355
|
+
});
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (action.type === "shrink-build" || action.type === "shrink-read") {
|
|
359
|
+
const row = selected(last.sessions);
|
|
360
|
+
if (!row)
|
|
361
|
+
return;
|
|
362
|
+
const to = action.type === "shrink-build" ? "BUILD" : "READ";
|
|
363
|
+
arm({ kind: "shrink", id: row.id, to, prompt: `shrink ${row.id} → ${to}?` });
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (action.type === "orr-run") {
|
|
367
|
+
busy = true;
|
|
145
368
|
try {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
369
|
+
const result = deskOrr(config.cwd);
|
|
370
|
+
orrSummary = {
|
|
371
|
+
overall: result.report.overall,
|
|
372
|
+
disposition: result.report.disposition,
|
|
373
|
+
path: result.reportJsonPath ?? result.reportMdPath,
|
|
374
|
+
};
|
|
375
|
+
io.log("orr producer=sa.first_party (Scorecard: use CLI --producer openssf.scorecard)");
|
|
376
|
+
}
|
|
377
|
+
finally {
|
|
378
|
+
busy = false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
else if (action.type === "passport") {
|
|
382
|
+
const row = selected(last.agents);
|
|
383
|
+
if (!row) {
|
|
384
|
+
io.log("Select an agent (pane 3) then press t");
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
busy = true;
|
|
388
|
+
void (async () => {
|
|
389
|
+
try {
|
|
390
|
+
if (!stillLive())
|
|
167
391
|
return;
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
392
|
+
const passport = await client.getPassport(row.id);
|
|
393
|
+
const dump = JSON.stringify(passport, null, 2);
|
|
394
|
+
assertNoSecrets("", dump);
|
|
395
|
+
io.log(dump);
|
|
171
396
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
path: result.reportJsonPath ?? result.reportMdPath,
|
|
178
|
-
};
|
|
397
|
+
catch (err) {
|
|
398
|
+
if (err instanceof AuthRequiredError)
|
|
399
|
+
io.error(err.message);
|
|
400
|
+
else
|
|
401
|
+
io.error(err instanceof Error ? err.message : String(err));
|
|
179
402
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return;
|
|
184
|
-
const verb = action.type === "decide-approve" ? "approve" : "reject";
|
|
185
|
-
io.log(`kya ${verb} --id ${row.id} (TUI does not decide)`);
|
|
403
|
+
finally {
|
|
404
|
+
busy = false;
|
|
405
|
+
await paint();
|
|
186
406
|
}
|
|
407
|
+
})();
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
else if (action.type === "decide-approve" || action.type === "decide-reject") {
|
|
411
|
+
const row = selected(last.approvals);
|
|
412
|
+
if (!row)
|
|
413
|
+
return;
|
|
414
|
+
const decision = action.type === "decide-approve" ? "approve" : "reject";
|
|
415
|
+
if (isMachineApiKey(config.apiKey ?? "")) {
|
|
416
|
+
io.log(`Machine API keys cannot ${decision} from the TUI. Run: kya ${decision} --id ${row.id}`);
|
|
417
|
+
return;
|
|
187
418
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
419
|
+
arm({
|
|
420
|
+
kind: "decide",
|
|
421
|
+
id: row.id,
|
|
422
|
+
decision,
|
|
423
|
+
prompt: `${decision} ${row.id}? (needs kya.approve JWT scope)`,
|
|
424
|
+
});
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
runPaint();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
runPaint();
|
|
198
432
|
return;
|
|
199
433
|
}
|
|
200
434
|
else if (action.type === "orr-run") {
|
|
@@ -204,20 +438,29 @@ export async function runDash(config, parsed, io, env) {
|
|
|
204
438
|
disposition: result.report.disposition,
|
|
205
439
|
path: result.reportJsonPath ?? result.reportMdPath,
|
|
206
440
|
};
|
|
441
|
+
io.log("orr producer=sa.first_party");
|
|
207
442
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
.
|
|
217
|
-
|
|
218
|
-
}
|
|
443
|
+
else if (action.type === "wrap" ||
|
|
444
|
+
action.type === "kill" ||
|
|
445
|
+
action.type === "invoke" ||
|
|
446
|
+
action.type === "shrink-build" ||
|
|
447
|
+
action.type === "shrink-read" ||
|
|
448
|
+
action.type === "decide-approve" ||
|
|
449
|
+
action.type === "decide-reject" ||
|
|
450
|
+
action.type === "passport") {
|
|
451
|
+
io.log("Need a live plane (O toggles offline; set KYA_API_KEY).");
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
runPaint();
|
|
219
455
|
};
|
|
220
456
|
const cleanup = () => {
|
|
457
|
+
stopRefresh();
|
|
458
|
+
try {
|
|
459
|
+
stdoutFd.write("\x1b[?2004l");
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
/* ignore */
|
|
463
|
+
}
|
|
221
464
|
stdinFd.setRawMode?.(false);
|
|
222
465
|
stdinFd.off("keypress", onKey);
|
|
223
466
|
stdinFd.pause();
|
|
@@ -225,4 +468,15 @@ export async function runDash(config, parsed, io, env) {
|
|
|
225
468
|
stdinFd.on("keypress", onKey);
|
|
226
469
|
});
|
|
227
470
|
}
|
|
471
|
+
function makeClient(config, offline) {
|
|
472
|
+
if (offline || !config.apiKey)
|
|
473
|
+
return undefined;
|
|
474
|
+
return new KyaHttpClient({
|
|
475
|
+
baseUrl: config.baseUrl,
|
|
476
|
+
apiKey: config.apiKey,
|
|
477
|
+
host: config.host,
|
|
478
|
+
agentId: config.agentId,
|
|
479
|
+
requireApiKey: true,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
228
482
|
//# sourceMappingURL=dash.js.map
|