@spec-box/sdd 0.6.4 → 0.7.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/README.md +28 -1
- package/assets/ci/Dockerfile +2 -0
- package/assets/hosts/claude/agent.md +13 -0
- package/assets/project/testing.md +4 -0
- package/assets/roles/challenger.md +4 -0
- package/assets/roles/distiller.md +4 -0
- package/assets/roles/implementer.md +4 -0
- package/assets/roles/planner.md +4 -0
- package/assets/roles/researcher.md +8 -3
- package/assets/roles/reviewer.md +4 -0
- package/assets/roles/tester.md +5 -1
- package/assets/roles/verifier.md +5 -1
- package/assets/skills/sbox-approve.md +11 -0
- package/assets/skills/sbox-browser.md +31 -0
- package/assets/skills/sbox-run.md +24 -0
- package/bin/sbox-browser.js +5 -0
- package/dist/adapters/host/claude/index.js +27 -85
- package/dist/adapters/host/claude/index.js.map +1 -1
- package/dist/adapters/host/index.js +25 -0
- package/dist/adapters/host/index.js.map +1 -0
- package/dist/adapters/host/skills.js +27 -0
- package/dist/adapters/host/skills.js.map +1 -0
- package/dist/browser/cli.js +488 -0
- package/dist/browser/cli.js.map +1 -0
- package/dist/browser/client.js +182 -0
- package/dist/browser/client.js.map +1 -0
- package/dist/browser/commands.js +598 -0
- package/dist/browser/commands.js.map +1 -0
- package/dist/browser/daemon.js +315 -0
- package/dist/browser/daemon.js.map +1 -0
- package/dist/browser/executable.js +138 -0
- package/dist/browser/executable.js.map +1 -0
- package/dist/browser/install.js +49 -0
- package/dist/browser/install.js.map +1 -0
- package/dist/browser/keys.js +44 -0
- package/dist/browser/keys.js.map +1 -0
- package/dist/browser/paths.js +34 -0
- package/dist/browser/paths.js.map +1 -0
- package/dist/browser/protocol.js +34 -0
- package/dist/browser/protocol.js.map +1 -0
- package/dist/browser/selectors.js +53 -0
- package/dist/browser/selectors.js.map +1 -0
- package/dist/browser/session.js +128 -0
- package/dist/browser/session.js.map +1 -0
- package/dist/browser/settings.js +51 -0
- package/dist/browser/settings.js.map +1 -0
- package/dist/browser/snapshot.js +89 -0
- package/dist/browser/snapshot.js.map +1 -0
- package/dist/cli/args.js +9 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands/change.js +2 -1
- package/dist/cli/commands/change.js.map +1 -1
- package/dist/cli/commands/doctor.js +16 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/host.js +4 -6
- package/dist/cli/commands/host.js.map +1 -1
- package/dist/cli/commands/init.js +9 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/metrics.js +2 -1
- package/dist/cli/commands/metrics.js.map +1 -1
- package/dist/cli/commands/run.js +5 -4
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/main.js +2 -12
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/output.js +7 -3
- package/dist/cli/output.js.map +1 -1
- package/dist/core/async.js +15 -0
- package/dist/core/async.js.map +1 -0
- package/dist/core/config.js +14 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/lock.js +2 -1
- package/dist/core/lock.js.map +1 -1
- package/dist/core/packet.js +1 -0
- package/dist/core/packet.js.map +1 -1
- package/dist/core/paths.js +14 -0
- package/dist/core/paths.js.map +1 -1
- package/dist/core/roles.js +16 -5
- package/dist/core/roles.js.map +1 -1
- package/dist/core/run.js +2 -3
- package/dist/core/run.js.map +1 -1
- package/dist/core/skills.js +60 -0
- package/dist/core/skills.js.map +1 -0
- package/docs/design.md +15 -1
- package/package.json +11 -5
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import net from 'node:net';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { sleep } from '../core/async.js';
|
|
6
|
+
import { SboxError } from '../core/errors.js';
|
|
7
|
+
import { pidAlive } from '../core/lock.js';
|
|
8
|
+
import { packageRoot } from '../core/paths.js';
|
|
9
|
+
import { browserDirs, ensureDir } from './paths.js';
|
|
10
|
+
import { createLineParser, encodeMessage } from './protocol.js';
|
|
11
|
+
import { readSession, removeSession } from './session.js';
|
|
12
|
+
let nextId = 1;
|
|
13
|
+
/**
|
|
14
|
+
* Сколько клиент ждёт ответа: пауза --ms плюс таймаут на каждое условие ожидания плюс запас.
|
|
15
|
+
* Демон исполняет условия wait последовательно, каждое со своим таймаутом.
|
|
16
|
+
*/
|
|
17
|
+
export function budgetFor(cmd, args, baseTimeoutMs) {
|
|
18
|
+
const base = typeof args.timeout === 'number' ? args.timeout : baseTimeoutMs;
|
|
19
|
+
const pause = typeof args.ms === 'number' ? args.ms : 0;
|
|
20
|
+
const conditions = cmd === 'wait' ? Math.max(1, ['target', 'text', 'url', 'fn'].filter((k) => args[k] !== undefined).length) : 1;
|
|
21
|
+
return pause + base * conditions + 10_000;
|
|
22
|
+
}
|
|
23
|
+
/** Отправляет одну команду демону и ждёт ответ. Мёртвый сокет удаляет след сессии. */
|
|
24
|
+
export async function sendCommand(session, cmd, args = {}, opts = {}) {
|
|
25
|
+
const env = opts.env ?? process.env;
|
|
26
|
+
const info = readSession(session, env);
|
|
27
|
+
if (!info)
|
|
28
|
+
throw new SboxError('BROWSER_SESSION_NOT_RUNNING', `Сессия ${session} не запущена.`, 'Любая команда страницы поднимет её сама; явно: `sbox-browser start`.');
|
|
29
|
+
return sendTo(info, cmd, args, opts.timeoutMs ?? budgetFor(cmd, args, 15_000), env);
|
|
30
|
+
}
|
|
31
|
+
export function sendTo(info, cmd, args, timeoutMs, env) {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const id = nextId++;
|
|
34
|
+
const conn = net.createConnection(info.socket);
|
|
35
|
+
let settled = false;
|
|
36
|
+
const finish = (fn) => {
|
|
37
|
+
if (settled)
|
|
38
|
+
return;
|
|
39
|
+
settled = true;
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
conn.destroy();
|
|
42
|
+
fn();
|
|
43
|
+
};
|
|
44
|
+
const timer = setTimeout(() => finish(() => reject(new SboxError('BROWSER_CLIENT_TIMEOUT', `Демон сессии ${info.name} не ответил на ${cmd} за ${timeoutMs} мс.`, `Журнал: ${info.log}. Остановить: \`sbox-browser stop --session ${info.name}\`.`))), timeoutMs);
|
|
45
|
+
conn.on('connect', () => conn.write(encodeMessage({ id, cmd, args })));
|
|
46
|
+
conn.on('data', createLineParser((res) => {
|
|
47
|
+
if (res.id !== id)
|
|
48
|
+
return;
|
|
49
|
+
finish(() => (res.ok ? resolve(res.result) : reject(new SboxError(res.error?.code ?? 'BROWSER_COMMAND_FAILED', res.error?.message ?? 'ошибка без описания', res.error?.fix))));
|
|
50
|
+
}));
|
|
51
|
+
conn.on('error', (e) => {
|
|
52
|
+
if (e.code === 'ECONNREFUSED' || e.code === 'ENOENT') {
|
|
53
|
+
// Сокет мёртв независимо от pid (после перезагрузки pid может принадлежать чужому процессу): след снимается, следующая команда поднимет сессию заново.
|
|
54
|
+
removeSession(info.name, env);
|
|
55
|
+
finish(() => reject(new SboxError('BROWSER_SESSION_NOT_RUNNING', `Сокет сессии ${info.name} не отвечает (${e.code}).`, 'Повторите команду: сессия будет поднята заново.')));
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
finish(() => reject(new SboxError('BROWSER_CLIENT_ERROR', `Ошибка связи с демоном ${info.name}: ${e.message}`)));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
conn.on('close', () => finish(() => reject(new SboxError('BROWSER_CLIENT_ERROR', `Демон сессии ${info.name} закрыл соединение без ответа.`, `Журнал: ${info.log}.`))));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/** Точка входа демона: собранный CLI в dist/ пакета (и при запуске из исходников через tsx). */
|
|
65
|
+
export function daemonEntry() {
|
|
66
|
+
const entry = path.join(packageRoot(), 'dist', 'browser', 'cli.js');
|
|
67
|
+
if (!fs.existsSync(entry)) {
|
|
68
|
+
throw new SboxError('BROWSER_DAEMON_ENTRY', `Не найден файл демона ${entry}.`, 'Соберите проект (`pnpm build`): демон запускается из dist/.');
|
|
69
|
+
}
|
|
70
|
+
return entry;
|
|
71
|
+
}
|
|
72
|
+
/** Поднимает демон в фоне и ждёт, пока он ответит на ping. */
|
|
73
|
+
export async function spawnDaemon(spec, opts = {}) {
|
|
74
|
+
const env = opts.env ?? process.env;
|
|
75
|
+
const session = spec.session;
|
|
76
|
+
const logFile = path.join(ensureDir(browserDirs.logs(env)), `${session}.log`);
|
|
77
|
+
const out = fs.openSync(logFile, 'a', 0o600);
|
|
78
|
+
const child = spawn(process.execPath, [daemonEntry(), 'serve', '--spec', JSON.stringify(spec)], { detached: true, stdio: ['ignore', out, out], env, windowsHide: true });
|
|
79
|
+
const exited = new Promise((resolve) => child.once('exit', (code) => resolve(code)));
|
|
80
|
+
let exitCode;
|
|
81
|
+
void exited.then((code) => {
|
|
82
|
+
exitCode = code;
|
|
83
|
+
});
|
|
84
|
+
child.unref();
|
|
85
|
+
fs.closeSync(out);
|
|
86
|
+
opts.log?.(`запуск демона ${session}, pid ${child.pid}, журнал ${logFile}`);
|
|
87
|
+
const deadline = Date.now() + (opts.startTimeoutMs ?? 60_000);
|
|
88
|
+
while (Date.now() < deadline) {
|
|
89
|
+
if (exitCode !== undefined) {
|
|
90
|
+
// Гонка двух автозапусков: проигравший демон завершился, потому что сессию уже поднял другой процесс.
|
|
91
|
+
const winner = readSession(session, env);
|
|
92
|
+
if (winner && winner.pid !== child.pid) {
|
|
93
|
+
try {
|
|
94
|
+
await sendTo(winner, 'ping', {}, 5_000, env);
|
|
95
|
+
return winner;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* и он мёртв: сообщаем об ошибке запуска */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const tail = tailOf(logFile);
|
|
102
|
+
throw new SboxError('BROWSER_START_FAILED', `Демон сессии ${session} завершился с кодом ${exitCode} до готовности.${tail ? `\n${tail}` : ''}`, `Полный журнал: ${logFile}`);
|
|
103
|
+
}
|
|
104
|
+
const info = readSession(session, env);
|
|
105
|
+
if (info && info.pid === child.pid) {
|
|
106
|
+
try {
|
|
107
|
+
await sendTo(info, 'ping', {}, 5_000, env);
|
|
108
|
+
return info;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
/* сокет ещё не готов */
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
await sleep(150);
|
|
115
|
+
}
|
|
116
|
+
throw new SboxError('BROWSER_START_FAILED', `Демон сессии ${session} не ответил за ${opts.startTimeoutMs ?? 60_000} мс.`, `Журнал: ${logFile}`);
|
|
117
|
+
}
|
|
118
|
+
/** Последние строки журнала: читается только хвост файла, журнал может быть большим. */
|
|
119
|
+
function tailOf(file, lines = 8, bytes = 8_192) {
|
|
120
|
+
try {
|
|
121
|
+
const size = fs.statSync(file).size;
|
|
122
|
+
const fd = fs.openSync(file, 'r');
|
|
123
|
+
try {
|
|
124
|
+
const length = Math.min(size, bytes);
|
|
125
|
+
const buffer = Buffer.alloc(length);
|
|
126
|
+
fs.readSync(fd, buffer, 0, length, size - length);
|
|
127
|
+
return buffer.toString('utf8').trimEnd().split('\n').slice(-lines).join('\n');
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
fs.closeSync(fd);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return '';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Принадлежит ли процесс демону этой сессии: по командной строке, а не по одному pid, который мог достаться чужому процессу. */
|
|
138
|
+
export function isOurDaemon(pid, session) {
|
|
139
|
+
if (process.platform === 'win32')
|
|
140
|
+
return false;
|
|
141
|
+
try {
|
|
142
|
+
const command = execFileSync('ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
143
|
+
return command.includes('browser/cli.js') && command.includes(`"session":"${session}"`);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Останавливает демон и ждёт завершения процесса. Чужой процесс с тем же pid не трогает. */
|
|
150
|
+
export async function stopSession(session, env = process.env, waitMs = 15_000) {
|
|
151
|
+
const info = readSession(session, env);
|
|
152
|
+
if (!info)
|
|
153
|
+
return { stopped: false, pid: null, reason: 'not_running' };
|
|
154
|
+
try {
|
|
155
|
+
await sendTo(info, 'stop', {}, 10_000, env);
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
if (e instanceof SboxError && e.code === 'BROWSER_SESSION_NOT_RUNNING')
|
|
159
|
+
return { stopped: false, pid: info.pid, reason: 'stale' };
|
|
160
|
+
/* демон мог закрыть соединение, завершаясь: ждём выхода процесса */
|
|
161
|
+
}
|
|
162
|
+
const deadline = Date.now() + waitMs;
|
|
163
|
+
while (pidAlive(info.pid) && Date.now() < deadline)
|
|
164
|
+
await sleep(100);
|
|
165
|
+
if (!pidAlive(info.pid)) {
|
|
166
|
+
removeSession(session, env);
|
|
167
|
+
return { stopped: true, pid: info.pid, reason: 'stopped' };
|
|
168
|
+
}
|
|
169
|
+
if (!isOurDaemon(info.pid, session)) {
|
|
170
|
+
removeSession(session, env);
|
|
171
|
+
return { stopped: false, pid: info.pid, reason: 'foreign_pid' };
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
process.kill(info.pid, 'SIGTERM');
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
/* уже завершился */
|
|
178
|
+
}
|
|
179
|
+
removeSession(session, env);
|
|
180
|
+
return { stopped: true, pid: info.pid, reason: 'killed' };
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/browser/client.ts"],"names":[],"mappings":"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"}
|