@onepointfour-packs/flaredeck-agent 0.1.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/LICENSE.md +14 -0
- package/README.md +102 -0
- package/dist/agent.d.ts +104 -0
- package/dist/agent.js +329 -0
- package/dist/agent.js.map +1 -0
- package/dist/approval.d.ts +72 -0
- package/dist/approval.js +161 -0
- package/dist/approval.js.map +1 -0
- package/dist/approvals.d.ts +2 -0
- package/dist/approvals.js +148 -0
- package/dist/approvals.js.map +1 -0
- package/dist/auth.d.ts +79 -0
- package/dist/auth.js +181 -0
- package/dist/auth.js.map +1 -0
- package/dist/automode.d.ts +33 -0
- package/dist/automode.js +69 -0
- package/dist/automode.js.map +1 -0
- package/dist/branches.d.ts +25 -0
- package/dist/branches.js +81 -0
- package/dist/branches.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -0
- package/dist/codesearch.d.ts +27 -0
- package/dist/codesearch.js +111 -0
- package/dist/codesearch.js.map +1 -0
- package/dist/concurrency.d.ts +14 -0
- package/dist/concurrency.js +52 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config.d.ts +106 -0
- package/dist/config.js +119 -0
- package/dist/config.js.map +1 -0
- package/dist/engine-antigravity.d.ts +23 -0
- package/dist/engine-antigravity.js +357 -0
- package/dist/engine-antigravity.js.map +1 -0
- package/dist/engine-claude.d.ts +18 -0
- package/dist/engine-claude.js +64 -0
- package/dist/engine-claude.js.map +1 -0
- package/dist/engine.d.ts +122 -0
- package/dist/engine.js +36 -0
- package/dist/engine.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/environment.d.ts +39 -0
- package/dist/environment.js +100 -0
- package/dist/environment.js.map +1 -0
- package/dist/flaredeck-check.d.ts +21 -0
- package/dist/flaredeck-check.js +12 -0
- package/dist/flaredeck-check.js.map +1 -0
- package/dist/flaredeck-proxy.d.ts +60 -0
- package/dist/flaredeck-proxy.js +186 -0
- package/dist/flaredeck-proxy.js.map +1 -0
- package/dist/flaredeck.d.ts +132 -0
- package/dist/flaredeck.js +106 -0
- package/dist/flaredeck.js.map +1 -0
- package/dist/gemini.d.ts +21 -0
- package/dist/gemini.js +59 -0
- package/dist/gemini.js.map +1 -0
- package/dist/gitguard.d.ts +2 -0
- package/dist/gitguard.js +90 -0
- package/dist/gitguard.js.map +1 -0
- package/dist/github.d.ts +38 -0
- package/dist/github.js +153 -0
- package/dist/github.js.map +1 -0
- package/dist/githubauth.d.ts +21 -0
- package/dist/githubauth.js +90 -0
- package/dist/githubauth.js.map +1 -0
- package/dist/intake.d.ts +124 -0
- package/dist/intake.js +383 -0
- package/dist/intake.js.map +1 -0
- package/dist/jam.d.ts +45 -0
- package/dist/jam.js +385 -0
- package/dist/jam.js.map +1 -0
- package/dist/jev.d.ts +31 -0
- package/dist/jev.js +75 -0
- package/dist/jev.js.map +1 -0
- package/dist/jevguard.d.ts +28 -0
- package/dist/jevguard.js +213 -0
- package/dist/jevguard.js.map +1 -0
- package/dist/mailbox.d.ts +22 -0
- package/dist/mailbox.js +51 -0
- package/dist/mailbox.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +764 -0
- package/dist/main.js.map +1 -0
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +81 -0
- package/dist/markdown.js.map +1 -0
- package/dist/oauth.d.ts +30 -0
- package/dist/oauth.js +216 -0
- package/dist/oauth.js.map +1 -0
- package/dist/permissions.d.ts +43 -0
- package/dist/permissions.js +57 -0
- package/dist/permissions.js.map +1 -0
- package/dist/preflight.d.ts +71 -0
- package/dist/preflight.js +116 -0
- package/dist/preflight.js.map +1 -0
- package/dist/providers.d.ts +26 -0
- package/dist/providers.js +27 -0
- package/dist/providers.js.map +1 -0
- package/dist/repopicker.d.ts +3 -0
- package/dist/repopicker.js +111 -0
- package/dist/repopicker.js.map +1 -0
- package/dist/runs.d.ts +59 -0
- package/dist/runs.js +211 -0
- package/dist/runs.js.map +1 -0
- package/dist/sandbox.d.ts +13 -0
- package/dist/sandbox.js +110 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/schedules.d.ts +11 -0
- package/dist/schedules.js +85 -0
- package/dist/schedules.js.map +1 -0
- package/dist/secretguard.d.ts +4 -0
- package/dist/secretguard.js +115 -0
- package/dist/secretguard.js.map +1 -0
- package/dist/secrets.d.ts +27 -0
- package/dist/secrets.js +51 -0
- package/dist/secrets.js.map +1 -0
- package/dist/serve.d.ts +17 -0
- package/dist/serve.js +1206 -0
- package/dist/serve.js.map +1 -0
- package/dist/status.d.ts +73 -0
- package/dist/status.js +98 -0
- package/dist/status.js.map +1 -0
- package/dist/stream.d.ts +91 -0
- package/dist/stream.js +103 -0
- package/dist/stream.js.map +1 -0
- package/dist/streamtoken.d.ts +2 -0
- package/dist/streamtoken.js +30 -0
- package/dist/streamtoken.js.map +1 -0
- package/dist/team.d.ts +61 -0
- package/dist/team.js +136 -0
- package/dist/team.js.map +1 -0
- package/dist/ui.d.ts +110 -0
- package/dist/ui.js +348 -0
- package/dist/ui.js.map +1 -0
- package/dist/updater.d.ts +15 -0
- package/dist/updater.js +178 -0
- package/dist/updater.js.map +1 -0
- package/dist/workspace.d.ts +115 -0
- package/dist/workspace.js +247 -0
- package/dist/workspace.js.map +1 -0
- package/dist/worktree.d.ts +42 -0
- package/dist/worktree.js +140 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +52 -0
- package/python/sidecar.py +178 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { parse } from 'yaml';
|
|
5
|
+
/**
|
|
6
|
+
* Where work goes. `origin/HEAD` is the wrong answer for any project whose default branch is not
|
|
7
|
+
* where features land — a project may merge to `dev` while the repo's default is something else, so
|
|
8
|
+
* guessing from git would open every pull request against the wrong base.
|
|
9
|
+
*
|
|
10
|
+
* branches:
|
|
11
|
+
* base: dev
|
|
12
|
+
*/
|
|
13
|
+
export function loadBaseBranch(repo) {
|
|
14
|
+
const path = join(repo, '.flaredeck', 'agent.yaml');
|
|
15
|
+
if (!existsSync(path))
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
const b = (parse(readFileSync(path, 'utf8')) ?? {}).branches;
|
|
19
|
+
return typeof b?.base === 'string' && b.base.trim() ? b.base.trim() : null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function loadMergeConfig(repo) {
|
|
26
|
+
const path = join(repo, '.flaredeck', 'agent.yaml');
|
|
27
|
+
if (!existsSync(path))
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
const m = (parse(readFileSync(path, 'utf8')) ?? {}).merge;
|
|
31
|
+
return m && typeof m.command === 'string'
|
|
32
|
+
? { command: m.command, dryRun: m.dryRun, confirm: m.confirm }
|
|
33
|
+
: null;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const TIMEOUT_MS = 60_000;
|
|
40
|
+
export function loadChecks(repo) {
|
|
41
|
+
const path = join(repo, '.flaredeck', 'agent.yaml');
|
|
42
|
+
if (!existsSync(path))
|
|
43
|
+
return [];
|
|
44
|
+
try {
|
|
45
|
+
const doc = parse(readFileSync(path, 'utf8')) ?? {};
|
|
46
|
+
const checks = Array.isArray(doc.checks) ? doc.checks : [];
|
|
47
|
+
return checks
|
|
48
|
+
.filter((c) => c && typeof c.run === 'string')
|
|
49
|
+
.map((c) => ({ name: String(c.name || c.run), run: String(c.run), ...(c.fix ? { fix: String(c.fix) } : {}) }));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// A malformed file must not stop the agent working — it just cannot be checked against.
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function run(command, cwd) {
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const child = spawn(command, { cwd, shell: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
59
|
+
let out = '';
|
|
60
|
+
const cap = (d) => { if (out.length < 2000)
|
|
61
|
+
out += String(d); };
|
|
62
|
+
child.stdout.on('data', cap);
|
|
63
|
+
child.stderr.on('data', cap);
|
|
64
|
+
// A check that hangs is a failed check: the point is to be quick about it.
|
|
65
|
+
const timer = setTimeout(() => { child.kill('SIGKILL'); resolve({ code: 124, out: `${out}\n(timed out)` }); }, TIMEOUT_MS);
|
|
66
|
+
child.on('close', (code) => { clearTimeout(timer); resolve({ code: code ?? 1, out }); });
|
|
67
|
+
child.on('error', (e) => { clearTimeout(timer); resolve({ code: 1, out: String(e) }); });
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export async function preflight(repo, onStep) {
|
|
71
|
+
const checks = loadChecks(repo);
|
|
72
|
+
const results = [];
|
|
73
|
+
for (const check of checks) {
|
|
74
|
+
let { code, out } = await run(check.run, repo);
|
|
75
|
+
let fixed = false;
|
|
76
|
+
if (code !== 0 && check.fix) {
|
|
77
|
+
// Something the harness can put right itself should not wake anybody up.
|
|
78
|
+
onStep(`${check.name}: failed — trying to fix it`);
|
|
79
|
+
await run(check.fix, repo);
|
|
80
|
+
({ code, out } = await run(check.run, repo));
|
|
81
|
+
fixed = code === 0;
|
|
82
|
+
}
|
|
83
|
+
results.push({
|
|
84
|
+
name: check.name,
|
|
85
|
+
ok: code === 0,
|
|
86
|
+
detail: out.trim().split('\n').slice(-3).join(' ').slice(0, 300),
|
|
87
|
+
fixed,
|
|
88
|
+
});
|
|
89
|
+
onStep(`${check.name}: ${code === 0 ? (fixed ? 'fixed' : 'ok') : 'FAILED'}`);
|
|
90
|
+
}
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* What the agent is told about the state of its environment.
|
|
95
|
+
*
|
|
96
|
+
* On failure it is told NOT to attempt the work. An agent that tries anyway produces a confident
|
|
97
|
+
* account of why the code is broken when the truth is that nothing was running — which is worse
|
|
98
|
+
* than no answer, because someone believes it.
|
|
99
|
+
*/
|
|
100
|
+
export function preflightBrief(results) {
|
|
101
|
+
if (results.length === 0)
|
|
102
|
+
return '';
|
|
103
|
+
const failed = results.filter((r) => !r.ok);
|
|
104
|
+
const fixed = results.filter((r) => r.fixed);
|
|
105
|
+
if (failed.length === 0) {
|
|
106
|
+
return fixed.length
|
|
107
|
+
? `\n\nEnvironment checked and ready (${fixed.map((r) => r.name).join(', ')} needed fixing first — already done).`
|
|
108
|
+
: '\n\nEnvironment checked and ready, so you can get straight to the work.';
|
|
109
|
+
}
|
|
110
|
+
return (`\n\nSTOP — the environment is not ready, so do not attempt the work:\n` +
|
|
111
|
+
failed.map((r) => ` · ${r.name} — ${r.detail || 'check failed'}`).join('\n') +
|
|
112
|
+
`\n\nDo not try to work around this or diagnose the code: nothing is running, so anything you ` +
|
|
113
|
+
`conclude about it would be wrong. Comment on the card saying exactly which check failed, call ` +
|
|
114
|
+
`set-needs-human, and stop.`);
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../src/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keys for the OPTIONAL small models the intake step may use, kept once per machine.
|
|
3
|
+
*
|
|
4
|
+
* Not per repo: a key is a billing relationship with a provider, not a fact about a checkout,
|
|
5
|
+
* and every agent on the box should classify the same way. Not in the repo config either, so
|
|
6
|
+
* copying an agent's config file carries no third-party credential with it. Absent keys simply
|
|
7
|
+
* switch that provider off — the subscription's own small model still writes the brief.
|
|
8
|
+
*/
|
|
9
|
+
export type Providers = {
|
|
10
|
+
/** OpenRouter: serves TypeSafe's Jev on its Decisions endpoint. */
|
|
11
|
+
openrouter?: {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
};
|
|
14
|
+
/** Google AI Studio key for the Gemini Developer API (the one with the free tier). */
|
|
15
|
+
gemini?: {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Who writes the brief for live cards. Haiku unless told otherwise: it needs no key. The
|
|
21
|
+
* dry-run command always shows both when both are available, so this can be chosen on evidence.
|
|
22
|
+
*/
|
|
23
|
+
brief?: 'haiku' | 'gemini';
|
|
24
|
+
};
|
|
25
|
+
export declare function loadProviders(): Providers;
|
|
26
|
+
export declare function saveProviders(p: Providers): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
const providersPath = () => join(process.env['XDG_CONFIG_HOME'] || join(homedir(), '.config'), 'flaredeck-agent', 'providers.json');
|
|
5
|
+
export function loadProviders() {
|
|
6
|
+
try {
|
|
7
|
+
if (!existsSync(providersPath()))
|
|
8
|
+
return {};
|
|
9
|
+
const raw = JSON.parse(readFileSync(providersPath(), 'utf8'));
|
|
10
|
+
return {
|
|
11
|
+
...(raw.openrouter?.apiKey ? { openrouter: { apiKey: raw.openrouter.apiKey } } : {}),
|
|
12
|
+
...(raw.gemini?.apiKey ? { gemini: { apiKey: raw.gemini.apiKey, ...(raw.gemini.model ? { model: raw.gemini.model } : {}) } } : {}),
|
|
13
|
+
...(raw.brief === 'gemini' || raw.brief === 'haiku' ? { brief: raw.brief } : {}),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function saveProviders(p) {
|
|
21
|
+
const path = providersPath();
|
|
22
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
23
|
+
writeFileSync(path, `${JSON.stringify(p, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
24
|
+
chmodSync(path, 0o600);
|
|
25
|
+
return path;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAsB1C,MAAM,aAAa,GAAG,GAAW,EAAE,CACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAE1G,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;YAAE,OAAO,EAAE,CAAC;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,CAAc,CAAC;QAC3E,OAAO;YACL,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpF,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClI,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjF,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,CAAY;IACxC,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1F,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Box, Text, render, useApp, useInput } from 'ink';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Tick the repositories an agent may work in.
|
|
5
|
+
*
|
|
6
|
+
* This was a numbered list you typed numbers at, and the numbers TOGGLED. Everything started
|
|
7
|
+
* ticked, so typing the four you wanted turned those four off — the list said "to toggle" and it
|
|
8
|
+
* still read as "to choose". A prompt whose plainest reading is the opposite of what it does is
|
|
9
|
+
* not a prompt worth keeping, and the fix is not better wording: it is to stop asking people to
|
|
10
|
+
* hold the current state in their head. Move, press space, see the tick change.
|
|
11
|
+
*/
|
|
12
|
+
const MAX_ROWS = 16;
|
|
13
|
+
function Picker({ root, repos, initial, onDone, }) {
|
|
14
|
+
const { exit } = useApp();
|
|
15
|
+
const [on, setOn] = useState(new Set(initial));
|
|
16
|
+
const [at, setAt] = useState(0);
|
|
17
|
+
const done = (picked) => { onDone(picked); exit(); };
|
|
18
|
+
useInput((input, key) => {
|
|
19
|
+
if (key.upArrow || input === 'k')
|
|
20
|
+
setAt((i) => (i - 1 + repos.length) % repos.length);
|
|
21
|
+
else if (key.downArrow || input === 'j')
|
|
22
|
+
setAt((i) => (i + 1) % repos.length);
|
|
23
|
+
else if (input === ' ') {
|
|
24
|
+
const name = repos[at]?.name;
|
|
25
|
+
if (name)
|
|
26
|
+
setOn((s) => { const n = new Set(s); if (n.has(name))
|
|
27
|
+
n.delete(name);
|
|
28
|
+
else
|
|
29
|
+
n.add(name); return n; });
|
|
30
|
+
}
|
|
31
|
+
else if (input === 'a')
|
|
32
|
+
setOn(new Set(repos.map((r) => r.name)));
|
|
33
|
+
else if (input === 'n')
|
|
34
|
+
setOn(new Set());
|
|
35
|
+
else if (key.return)
|
|
36
|
+
done(repos.filter((r) => on.has(r.name)).map((r) => r.name));
|
|
37
|
+
// Escape leaves the config alone rather than saving an empty choice — a cancelled prompt
|
|
38
|
+
// should change nothing, and "none of them" is a thing you say with n then enter.
|
|
39
|
+
else if (key.escape || input === 'q')
|
|
40
|
+
done(null);
|
|
41
|
+
});
|
|
42
|
+
// A window, so a folder of forty repos does not scroll the question off the screen.
|
|
43
|
+
const half = Math.floor(MAX_ROWS / 2);
|
|
44
|
+
const first = Math.max(0, Math.min(at - half, repos.length - MAX_ROWS));
|
|
45
|
+
const shown = repos.slice(first, first + MAX_ROWS);
|
|
46
|
+
const width = Math.max(...repos.map((r) => r.name.length));
|
|
47
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
48
|
+
React.createElement(Text, null,
|
|
49
|
+
React.createElement(Text, { bold: true }, "Which of these may this agent work in?"),
|
|
50
|
+
React.createElement(Text, { dimColor: true },
|
|
51
|
+
" \u00B7 ",
|
|
52
|
+
repos.length,
|
|
53
|
+
" found in ",
|
|
54
|
+
root)),
|
|
55
|
+
React.createElement(Box, { height: 1 }),
|
|
56
|
+
first > 0 ? React.createElement(Text, { dimColor: true },
|
|
57
|
+
" \u2191 ",
|
|
58
|
+
first,
|
|
59
|
+
" more") : null,
|
|
60
|
+
shown.map((r, i) => {
|
|
61
|
+
const here = first + i === at;
|
|
62
|
+
const ticked = on.has(r.name);
|
|
63
|
+
return (React.createElement(Text, { key: r.path, color: here ? 'cyan' : undefined },
|
|
64
|
+
here ? '❯ ' : ' ',
|
|
65
|
+
React.createElement(Text, { color: ticked ? 'green' : undefined }, ticked ? '◉' : '◯'),
|
|
66
|
+
' ',
|
|
67
|
+
React.createElement(Text, { bold: ticked }, r.name.padEnd(width)),
|
|
68
|
+
React.createElement(Text, { dimColor: true },
|
|
69
|
+
" ",
|
|
70
|
+
r.origin ? shortRemote(r.origin) : 'no remote — nothing it pushes can be received')));
|
|
71
|
+
}),
|
|
72
|
+
first + MAX_ROWS < repos.length ? React.createElement(Text, { dimColor: true },
|
|
73
|
+
" \u2193 ",
|
|
74
|
+
repos.length - first - MAX_ROWS,
|
|
75
|
+
" more") : null,
|
|
76
|
+
React.createElement(Box, { height: 1 }),
|
|
77
|
+
React.createElement(Text, { dimColor: true },
|
|
78
|
+
"\u2191\u2193 move \u00B7 space tick \u00B7 a all \u00B7 n none \u00B7 enter accept (",
|
|
79
|
+
on.size,
|
|
80
|
+
" selected) \u00B7 esc cancel")));
|
|
81
|
+
}
|
|
82
|
+
const shortRemote = (url) => url.trim().toLowerCase()
|
|
83
|
+
.replace(/^git@([^:]+):/, '$1/')
|
|
84
|
+
.replace(/^[a-z]+:\/\//, '')
|
|
85
|
+
.replace(/\.git$/, '')
|
|
86
|
+
.split('/')
|
|
87
|
+
.slice(-2)
|
|
88
|
+
.join('/');
|
|
89
|
+
/** Resolves to the ticked names, or null when the person backed out without choosing. */
|
|
90
|
+
export function pickRepos(root, repos, initial) {
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
// Resolved from the callback AND from the unmount, whichever happens first, because this sits
|
|
93
|
+
// in the middle of login: a picker that renders but never returns is a login that hangs with
|
|
94
|
+
// no way forward. startApprovals settles the same two ways for the same reason.
|
|
95
|
+
let settled = false;
|
|
96
|
+
let app = null;
|
|
97
|
+
const finish = (picked) => {
|
|
98
|
+
if (settled)
|
|
99
|
+
return;
|
|
100
|
+
settled = true;
|
|
101
|
+
try {
|
|
102
|
+
app?.unmount();
|
|
103
|
+
}
|
|
104
|
+
catch { /* already gone */ }
|
|
105
|
+
resolve(picked);
|
|
106
|
+
};
|
|
107
|
+
app = render(React.createElement(Picker, { root: root, repos: repos, initial: initial, onDone: finish }));
|
|
108
|
+
void app.waitUntilExit().then(() => finish(null));
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=repopicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repopicker.js","sourceRoot":"","sources":["../src/repopicker.tsx"],"names":[],"mappings":"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"}
|
package/dist/runs.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { RunTarget } from './config.js';
|
|
2
|
+
import type { Flaredeck } from './flaredeck.js';
|
|
3
|
+
/**
|
|
4
|
+
* Production runs: the agent asks, a person approves, the HARNESS runs it.
|
|
5
|
+
*
|
|
6
|
+
* The agent never holds a credential and never executes anything itself. It hands over a script,
|
|
7
|
+
* a name and what it believes the script does; that goes to the card as an approval request —
|
|
8
|
+
* the same one the permission broker uses, so it renders with Approve/Deny and raises a push —
|
|
9
|
+
* and only a person's verdict makes the harness run it. The script is bound to the approval by
|
|
10
|
+
* its hash, so what was approved is what runs.
|
|
11
|
+
*
|
|
12
|
+
* The description is the agent's own account of its own script, so it is never enough on its own.
|
|
13
|
+
* Where the script is an UPDATE or DELETE with a WHERE clause, the harness derives a COUNT from
|
|
14
|
+
* that clause and runs it first: "would touch N rows" sits next to the script when the approver
|
|
15
|
+
* reads it, and a script that says one row and previews thousands is caught before anyone taps.
|
|
16
|
+
* That count is written by the harness from the agent's clause, not by the agent, and it is the
|
|
17
|
+
* only thing that runs before approval.
|
|
18
|
+
*/
|
|
19
|
+
export declare const RUN_TOOL_NAMES: string[];
|
|
20
|
+
export type Current = {
|
|
21
|
+
card: string;
|
|
22
|
+
cwd: string;
|
|
23
|
+
};
|
|
24
|
+
/** What the harness is told while a request waits, so the pane can show and answer it. */
|
|
25
|
+
export type AskEvent = {
|
|
26
|
+
kind: 'asking';
|
|
27
|
+
requestId: string;
|
|
28
|
+
commentId: string;
|
|
29
|
+
name: string;
|
|
30
|
+
target: string;
|
|
31
|
+
purpose: string;
|
|
32
|
+
preview?: string;
|
|
33
|
+
script: string;
|
|
34
|
+
withLead: boolean;
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'answered';
|
|
37
|
+
requestId: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The read-only preview the harness derives from a write. Only the simplest shapes: one UPDATE
|
|
41
|
+
* or DELETE, one table, a WHERE clause, nothing after it. Anything else previews nothing rather
|
|
42
|
+
* than something wrong.
|
|
43
|
+
*/
|
|
44
|
+
export declare function previewFor(script: string): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* What a restarted harness owes: requests of its own that a person approved while it was not
|
|
47
|
+
* waiting — closed on idle, out of context, or simply down. Each is run now and reported, so an
|
|
48
|
+
* approval is never silently lost to a restart. The agent is not involved; it reads the result
|
|
49
|
+
* on the card next time it looks.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resumeApprovedRuns(targets: Record<string, RunTarget>, deck: Flaredeck, cwd: string, log: (text: string) => void): Promise<number>;
|
|
52
|
+
export declare function runTools(targets: Record<string, RunTarget>, current: () => Current | null, deck: Flaredeck | null, log: (text: string) => void,
|
|
53
|
+
/** This agent's lead, when it has one: the request goes there first, and a person is asked only if the lead endorses it. */
|
|
54
|
+
lead?: () => string | null,
|
|
55
|
+
/** The pane, told when a request is posted and when it is answered. */
|
|
56
|
+
onAsk?: (e: AskEvent) => void): {
|
|
57
|
+
server: unknown;
|
|
58
|
+
toolNames: string[];
|
|
59
|
+
};
|
package/dist/runs.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { flaredeckDecider } from './approval.js';
|
|
6
|
+
/**
|
|
7
|
+
* Production runs: the agent asks, a person approves, the HARNESS runs it.
|
|
8
|
+
*
|
|
9
|
+
* The agent never holds a credential and never executes anything itself. It hands over a script,
|
|
10
|
+
* a name and what it believes the script does; that goes to the card as an approval request —
|
|
11
|
+
* the same one the permission broker uses, so it renders with Approve/Deny and raises a push —
|
|
12
|
+
* and only a person's verdict makes the harness run it. The script is bound to the approval by
|
|
13
|
+
* its hash, so what was approved is what runs.
|
|
14
|
+
*
|
|
15
|
+
* The description is the agent's own account of its own script, so it is never enough on its own.
|
|
16
|
+
* Where the script is an UPDATE or DELETE with a WHERE clause, the harness derives a COUNT from
|
|
17
|
+
* that clause and runs it first: "would touch N rows" sits next to the script when the approver
|
|
18
|
+
* reads it, and a script that says one row and previews thousands is caught before anyone taps.
|
|
19
|
+
* That count is written by the harness from the agent's clause, not by the agent, and it is the
|
|
20
|
+
* only thing that runs before approval.
|
|
21
|
+
*/
|
|
22
|
+
export const RUN_TOOL_NAMES = ['mcp__runs__request_production_run'];
|
|
23
|
+
/** Long enough for a person on a phone; a run that nobody answers is not run. */
|
|
24
|
+
const RUN_APPROVAL_MS = 60 * 60 * 1000;
|
|
25
|
+
const OUTPUT_CAP = 6000;
|
|
26
|
+
const say = (text, isError = false) => ({ content: [{ type: 'text', text }], isError });
|
|
27
|
+
function exec(target, script, cwd) {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const [command, ...rest] = target.command;
|
|
30
|
+
// `{script}` in the target's own argument list is where the script goes; without it, the
|
|
31
|
+
// script is piped to stdin. The command comes from the machine's config, never from the
|
|
32
|
+
// agent, so the agent chooses what runs but not how or where.
|
|
33
|
+
const inArgs = rest.some((a) => a.includes('{script}'));
|
|
34
|
+
const args = rest.map((a) => a.split('{script}').join(script));
|
|
35
|
+
const child = spawn(command, args, {
|
|
36
|
+
cwd: target.cwd ?? cwd,
|
|
37
|
+
stdio: [inArgs ? 'ignore' : 'pipe', 'pipe', 'pipe'],
|
|
38
|
+
env: process.env,
|
|
39
|
+
});
|
|
40
|
+
let out = '';
|
|
41
|
+
const cap = (d) => { if (out.length < OUTPUT_CAP)
|
|
42
|
+
out += String(d); };
|
|
43
|
+
child.stdout?.on('data', cap);
|
|
44
|
+
child.stderr?.on('data', cap);
|
|
45
|
+
child.on('close', (c) => resolve({ code: c ?? 1, out: out.trim() }));
|
|
46
|
+
child.on('error', (e) => resolve({ code: 1, out: String(e) }));
|
|
47
|
+
if (!inArgs)
|
|
48
|
+
child.stdin?.end(script);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The read-only preview the harness derives from a write. Only the simplest shapes: one UPDATE
|
|
53
|
+
* or DELETE, one table, a WHERE clause, nothing after it. Anything else previews nothing rather
|
|
54
|
+
* than something wrong.
|
|
55
|
+
*/
|
|
56
|
+
export function previewFor(script) {
|
|
57
|
+
const s = script.trim().replace(/;\s*$/, '');
|
|
58
|
+
if (/;/.test(s))
|
|
59
|
+
return null;
|
|
60
|
+
const m = /^update\s+([`\w.]+)\s+set\s+[\s\S]+?\s+where\s+([\s\S]+)$/i.exec(s) ??
|
|
61
|
+
/^delete\s+from\s+([`\w.]+)\s+where\s+([\s\S]+)$/i.exec(s);
|
|
62
|
+
if (!m)
|
|
63
|
+
return null;
|
|
64
|
+
const where = m[2].trim();
|
|
65
|
+
if (/\b(limit|order\s+by|union|;)\b/i.test(where))
|
|
66
|
+
return null;
|
|
67
|
+
return `SELECT COUNT(*) AS would_touch FROM ${m[1]} WHERE ${where}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Execute an approved request and put the outcome where it belongs: a comment on the card for
|
|
71
|
+
* the people, and a `ran` stamp on the request so no harness — this one restarted, or another
|
|
72
|
+
* holding the same account — runs the same approval twice.
|
|
73
|
+
*/
|
|
74
|
+
async function runAndReport(t, job, deck, log) {
|
|
75
|
+
log(`running ${job.name} on ${job.target}`);
|
|
76
|
+
const r = await exec(t, job.script, job.cwd);
|
|
77
|
+
const outcome = r.code === 0 ? 'ran' : `FAILED (exit ${r.code})`;
|
|
78
|
+
if (job.commentId)
|
|
79
|
+
await deck.markRan(job.commentId, outcome).catch((e) => log(`could not stamp the run: ${String(e)}`));
|
|
80
|
+
await deck
|
|
81
|
+
.comment(job.card, `**Production run \`${job.name}\` on ${job.target} ${outcome}** [${job.hash}]\n\n\`\`\`\n${r.out || '(no output)'}\n\`\`\``)
|
|
82
|
+
.catch((e) => log(`could not post the run result: ${String(e)}`));
|
|
83
|
+
return r;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* What a restarted harness owes: requests of its own that a person approved while it was not
|
|
87
|
+
* waiting — closed on idle, out of context, or simply down. Each is run now and reported, so an
|
|
88
|
+
* approval is never silently lost to a restart. The agent is not involved; it reads the result
|
|
89
|
+
* on the card next time it looks.
|
|
90
|
+
*/
|
|
91
|
+
export async function resumeApprovedRuns(targets, deck, cwd, log) {
|
|
92
|
+
let n = 0;
|
|
93
|
+
let owed;
|
|
94
|
+
try {
|
|
95
|
+
owed = await deck.approvedUnrun();
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
log(`could not check for approved runs: ${e instanceof Error ? e.message : String(e)}`);
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
for (const a of owed) {
|
|
102
|
+
if (a.tool_name !== 'request_production_run')
|
|
103
|
+
continue;
|
|
104
|
+
const desc = a.description || '';
|
|
105
|
+
const name = desc.split('"')[1] ?? a.request_id;
|
|
106
|
+
const target = desc.split(' on ')[1]?.split(' — ')[0] ?? '';
|
|
107
|
+
const t = targets[target];
|
|
108
|
+
const script = (a.input_preview || '').split('\n').filter((l) => !l.startsWith('-- ')).join('\n').trim();
|
|
109
|
+
const hash = /\[([0-9a-f]{12})\]/.exec(desc)?.[1] ?? '';
|
|
110
|
+
if (!t || !script) {
|
|
111
|
+
log(`approved run ${name} on ${target} cannot be resumed here (${!t ? 'no such target on this machine' : 'no script'})`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// What was approved is what runs: the stored script must still hash to what the request named.
|
|
115
|
+
const check = createHash('sha256').update(script).digest('hex').slice(0, 12);
|
|
116
|
+
if (hash && check !== hash) {
|
|
117
|
+
log(`approved run ${name}: stored script no longer matches its hash — not running it`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
log(`${a.decided_by_name ?? 'someone'} approved ${name} on ${target} while this harness was down — running it now`);
|
|
121
|
+
await runAndReport(t, { commentId: a.comment_id, card: a.card.key, cwd, name, target, script, hash }, deck, log);
|
|
122
|
+
n++;
|
|
123
|
+
}
|
|
124
|
+
return n;
|
|
125
|
+
}
|
|
126
|
+
export function runTools(targets, current, deck, log,
|
|
127
|
+
/** This agent's lead, when it has one: the request goes there first, and a person is asked only if the lead endorses it. */
|
|
128
|
+
lead = () => null,
|
|
129
|
+
/** The pane, told when a request is posted and when it is answered. */
|
|
130
|
+
onAsk = () => { }) {
|
|
131
|
+
const names = Object.keys(targets);
|
|
132
|
+
const list = names.map((n) => ` · ${n}${targets[n].about ? ` — ${targets[n].about}` : ''}`).join('\n');
|
|
133
|
+
const server = createSdkMcpServer({
|
|
134
|
+
name: 'runs',
|
|
135
|
+
version: '1.0.0',
|
|
136
|
+
tools: [
|
|
137
|
+
tool('request_production_run', 'Ask a person to approve a script against a production system, and if they do, the harness ' +
|
|
138
|
+
'runs it and returns the output. YOU do not run anything and cannot: this call blocks until ' +
|
|
139
|
+
'it is answered on the card (up to an hour) or refused. If you report to a lead, the lead ' +
|
|
140
|
+
'reviews the script first and only an endorsed request reaches a person. Use it for the one script that ' +
|
|
141
|
+
'needs running, described honestly — the approver reads the script itself, and for an UPDATE ' +
|
|
142
|
+
'or DELETE the harness shows them how many rows it would touch before they decide. A denial ' +
|
|
143
|
+
'or a timeout is final for that script: do not resubmit it unchanged.\n\nTargets on this machine:\n' + list, {
|
|
144
|
+
target: z.string().describe(`Which system. One of: ${names.join(', ')}`),
|
|
145
|
+
name: z.string().describe('A short name for this script, e.g. "enrol-sofia-fsr-course".'),
|
|
146
|
+
purpose: z.string().describe('What it does and why, in one or two plain sentences, for the person approving it.'),
|
|
147
|
+
script: z.string().describe('The exact script to run. What is approved is what runs, byte for byte.'),
|
|
148
|
+
expected_effect: z.string().describe('What you expect it to change or return, e.g. "1 row inserted" or "returns ~5 rows".'),
|
|
149
|
+
}, async ({ target, name, purpose, script, expected_effect }) => {
|
|
150
|
+
const t = targets[target];
|
|
151
|
+
if (!t)
|
|
152
|
+
return say(`No target called "${target}" on this machine. Targets: ${names.join(', ') || 'none'}.`, true);
|
|
153
|
+
const cur = current();
|
|
154
|
+
if (!cur)
|
|
155
|
+
return say('There is no card being worked, so there is nowhere to ask for approval.', true);
|
|
156
|
+
if (!deck)
|
|
157
|
+
return say('This harness has no FlareDeck credentials of its own, so it cannot post an approval request.', true);
|
|
158
|
+
const body = script.trim();
|
|
159
|
+
if (!body)
|
|
160
|
+
return say('The script is empty.', true);
|
|
161
|
+
if (body.length > 18_000)
|
|
162
|
+
return say(`The script is ${body.length} characters; the approval record holds 18,000. Split it into smaller runs, each reviewed on its own.`, true);
|
|
163
|
+
const hash = createHash('sha256').update(body).digest('hex').slice(0, 12);
|
|
164
|
+
// The harness's own check, before anyone is asked. Read-only by construction.
|
|
165
|
+
let previewNote = '';
|
|
166
|
+
let previewLine = '';
|
|
167
|
+
const preview = previewFor(body);
|
|
168
|
+
if (preview) {
|
|
169
|
+
log(`preview for ${name}: ${preview}`);
|
|
170
|
+
const p = await exec(t, preview, cur.cwd);
|
|
171
|
+
previewLine = p.code === 0
|
|
172
|
+
? `would touch: ${p.out.split('\n').slice(-1)[0]?.trim() || '?'}`
|
|
173
|
+
: `preview failed (exit ${p.code})`;
|
|
174
|
+
previewNote = p.code === 0
|
|
175
|
+
? `-- harness preview (${preview}):\n-- ${p.out.split('\n').slice(-3).join(' | ')}\n\n`
|
|
176
|
+
: `-- harness preview FAILED (exit ${p.code}): ${p.out.split('\n').slice(0, 2).join(' ')}\n\n`;
|
|
177
|
+
}
|
|
178
|
+
// Same request shape and same wait loop as every other approval: renders with
|
|
179
|
+
// Approve/Deny, raises the push, and accepts a typed `approve <id>`.
|
|
180
|
+
const leadId = lead();
|
|
181
|
+
let posted;
|
|
182
|
+
const ask = flaredeckDecider(deck, cur.card, RUN_APPROVAL_MS, {
|
|
183
|
+
...(leadId ? { review_by: leadId } : {}),
|
|
184
|
+
...(t.approvers?.length ? { approvers: t.approvers } : {}),
|
|
185
|
+
}, (p) => {
|
|
186
|
+
posted = p;
|
|
187
|
+
onAsk({
|
|
188
|
+
kind: 'asking', requestId: p.request_id, commentId: p.comment_id, name, target,
|
|
189
|
+
purpose: purpose.trim(), ...(previewLine ? { preview: previewLine } : {}), script: body, withLead: !!leadId,
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
log(leadId ? `asking the lead to review ${name}` : `asking for approval of ${name}`);
|
|
193
|
+
const verdict = await ask({
|
|
194
|
+
toolName: 'request_production_run',
|
|
195
|
+
input: {
|
|
196
|
+
description: `PRODUCTION RUN "${name}" on ${target} — ${purpose.trim()} Expected: ${expected_effect.trim()} [${hash}]`,
|
|
197
|
+
command: `${previewNote}${body}`,
|
|
198
|
+
whole: true,
|
|
199
|
+
},
|
|
200
|
+
}, { signal: new AbortController().signal });
|
|
201
|
+
onAsk({ kind: 'answered', requestId: hash });
|
|
202
|
+
if (!verdict.allow)
|
|
203
|
+
return say(`Not run. ${verdict.reason}`, true);
|
|
204
|
+
const r = await runAndReport(t, { commentId: posted?.comment_id ?? '', card: cur.card, cwd: cur.cwd, name, target, script: body, hash }, deck, log);
|
|
205
|
+
return say(`${r.code === 0 ? 'Ran' : `FAILED (exit ${r.code})`} "${name}" on ${target}. Output:\n${r.out || '(no output)'}`, r.code !== 0);
|
|
206
|
+
}),
|
|
207
|
+
],
|
|
208
|
+
});
|
|
209
|
+
return { server, toolNames: RUN_TOOL_NAMES };
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=runs.js.map
|
package/dist/runs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HookCallbackMatcher, HookEvent } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* True when this command looks like it writes somewhere outside `root`.
|
|
4
|
+
*
|
|
5
|
+
* Reads are never refused, so the question is only ever asked of a line that changes something.
|
|
6
|
+
*/
|
|
7
|
+
export declare function writesOutside(command: string, root: string): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* `job()` returns the directory this card owns, or null when there is no job — in which case
|
|
10
|
+
* nothing is enforced here, because a turn with no checkout has a different rule (it is told it
|
|
11
|
+
* may change nothing at all) and the publishing tools already refuse it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function sandboxGuard(job: () => string | null, onNote: (text: string) => void): Partial<Record<HookEvent, HookCallbackMatcher[]>>;
|