@pat-lewczuk/cezar 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/README.md +90 -0
- package/dist/core/agent-runner.d.ts +95 -0
- package/dist/core/agent-runner.js +8 -0
- package/dist/core/agent-runner.js.map +1 -0
- package/dist/core/backend-detect.d.ts +14 -0
- package/dist/core/backend-detect.js +78 -0
- package/dist/core/backend-detect.js.map +1 -0
- package/dist/core/claude-cli-runner.d.ts +68 -0
- package/dist/core/claude-cli-runner.js +424 -0
- package/dist/core/claude-cli-runner.js.map +1 -0
- package/dist/core/usage.d.ts +11 -0
- package/dist/core/usage.js +15 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +294 -0
- package/dist/index.js.map +1 -0
- package/dist/runs/store.d.ts +218 -0
- package/dist/runs/store.js +286 -0
- package/dist/runs/store.js.map +1 -0
- package/dist/server/git.d.ts +21 -0
- package/dist/server/git.js +54 -0
- package/dist/server/git.js.map +1 -0
- package/dist/server/open-in-terminal.d.ts +11 -0
- package/dist/server/open-in-terminal.js +84 -0
- package/dist/server/open-in-terminal.js.map +1 -0
- package/dist/server/server.d.ts +12 -0
- package/dist/server/server.js +243 -0
- package/dist/server/server.js.map +1 -0
- package/dist/skills.d.ts +31 -0
- package/dist/skills.js +117 -0
- package/dist/skills.js.map +1 -0
- package/dist/workflows/load.d.ts +15 -0
- package/dist/workflows/load.js +58 -0
- package/dist/workflows/load.js.map +1 -0
- package/dist/workflows/run.d.ts +52 -0
- package/dist/workflows/run.js +452 -0
- package/dist/workflows/run.js.map +1 -0
- package/dist/workflows/types.d.ts +202 -0
- package/dist/workflows/types.js +56 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +44 -0
- package/scripts/mock-claude.mjs +106 -0
- package/web/app.js +634 -0
- package/web/index.html +48 -0
- package/web/style.css +317 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
const stepStateSchema = z.object({
|
|
7
|
+
id: z.string(),
|
|
8
|
+
name: z.string(),
|
|
9
|
+
kind: z.enum(['agent', 'check']),
|
|
10
|
+
status: z.enum(['pending', 'running', 'waiting', 'done', 'failed', 'cancelled', 'skipped']),
|
|
11
|
+
iterations: z.number(),
|
|
12
|
+
tokensUsed: z.number(),
|
|
13
|
+
startedAt: z.string().optional(),
|
|
14
|
+
finishedAt: z.string().optional(),
|
|
15
|
+
error: z.string().optional(),
|
|
16
|
+
/** Latest claude session id — the user can `claude --resume <id>` with it. */
|
|
17
|
+
sessionId: z.string().optional(),
|
|
18
|
+
/** Dollar cost reported by the claude CLI for this step's turns. */
|
|
19
|
+
costUsd: z.number().optional(),
|
|
20
|
+
});
|
|
21
|
+
const runRecordSchema = z.object({
|
|
22
|
+
id: z.string(),
|
|
23
|
+
title: z.string(),
|
|
24
|
+
workflow: z.string(),
|
|
25
|
+
task: z.string(),
|
|
26
|
+
model: z.string().optional(),
|
|
27
|
+
status: z.enum(['queued', 'running', 'waiting', 'done', 'failed', 'cancelled']),
|
|
28
|
+
createdAt: z.string(),
|
|
29
|
+
startedAt: z.string().optional(),
|
|
30
|
+
finishedAt: z.string().optional(),
|
|
31
|
+
tokensUsed: z.number(),
|
|
32
|
+
costUsd: z.number().optional(),
|
|
33
|
+
/** First GitHub PR URL spotted in the transcript (the janitor trick). */
|
|
34
|
+
pullRequestUrl: z.string().optional(),
|
|
35
|
+
archived: z.boolean().default(false),
|
|
36
|
+
archivedAt: z.string().optional(),
|
|
37
|
+
currentStepId: z.string().optional(),
|
|
38
|
+
error: z.string().optional(),
|
|
39
|
+
steps: z.array(stepStateSchema),
|
|
40
|
+
});
|
|
41
|
+
const MAX_RUNS_KEPT = 300;
|
|
42
|
+
const MAX_ARCHIVED_KEPT = 500;
|
|
43
|
+
const PR_URL_RE = /https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/\d+/;
|
|
44
|
+
/**
|
|
45
|
+
* File-backed run store: `runs.json` index (atomic tmp+rename writes, the
|
|
46
|
+
* pattern from @cezar/core's IssueStore) plus one append-only NDJSON event
|
|
47
|
+
* file per run. Also the in-process event bus the SSE endpoints subscribe to:
|
|
48
|
+
* emits `('run', RunRecord)` and `('event', { runId, event: RunEvent })`.
|
|
49
|
+
*/
|
|
50
|
+
export class RunStore extends EventEmitter {
|
|
51
|
+
dataDir;
|
|
52
|
+
runs = new Map();
|
|
53
|
+
saveTimer = null;
|
|
54
|
+
constructor(dataDir) {
|
|
55
|
+
super();
|
|
56
|
+
this.dataDir = dataDir;
|
|
57
|
+
this.setMaxListeners(100);
|
|
58
|
+
}
|
|
59
|
+
static open(dataDir) {
|
|
60
|
+
mkdirSync(join(dataDir, 'runs'), { recursive: true });
|
|
61
|
+
const store = new RunStore(dataDir);
|
|
62
|
+
const indexPath = join(dataDir, 'runs.json');
|
|
63
|
+
if (existsSync(indexPath)) {
|
|
64
|
+
try {
|
|
65
|
+
const raw = JSON.parse(readFileSync(indexPath, 'utf8'));
|
|
66
|
+
const parsed = z.array(runRecordSchema).safeParse(raw);
|
|
67
|
+
if (parsed.success) {
|
|
68
|
+
for (const run of parsed.data) {
|
|
69
|
+
// A run that was live when the previous process exited can never
|
|
70
|
+
// finish — surface that instead of a forever-"running" ghost.
|
|
71
|
+
if (run.status === 'running' || run.status === 'queued' || run.status === 'waiting') {
|
|
72
|
+
run.status = 'failed';
|
|
73
|
+
run.error = 'interrupted — cezar process exited during the run';
|
|
74
|
+
run.finishedAt = run.finishedAt ?? new Date().toISOString();
|
|
75
|
+
for (const step of run.steps) {
|
|
76
|
+
if (step.status === 'running' || step.status === 'waiting')
|
|
77
|
+
step.status = 'failed';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
store.runs.set(run.id, run);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// corrupt index — start fresh; event files stay on disk untouched
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return store;
|
|
89
|
+
}
|
|
90
|
+
listRuns() {
|
|
91
|
+
return [...this.runs.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
92
|
+
}
|
|
93
|
+
getRun(id) {
|
|
94
|
+
return this.runs.get(id);
|
|
95
|
+
}
|
|
96
|
+
createRun(input) {
|
|
97
|
+
const run = {
|
|
98
|
+
id: randomUUID(),
|
|
99
|
+
title: input.title,
|
|
100
|
+
workflow: input.workflow,
|
|
101
|
+
task: input.task,
|
|
102
|
+
model: input.model,
|
|
103
|
+
status: 'queued',
|
|
104
|
+
createdAt: new Date().toISOString(),
|
|
105
|
+
tokensUsed: 0,
|
|
106
|
+
archived: false,
|
|
107
|
+
steps: input.steps.map((s) => ({
|
|
108
|
+
...s,
|
|
109
|
+
status: 'pending',
|
|
110
|
+
iterations: 0,
|
|
111
|
+
tokensUsed: 0,
|
|
112
|
+
})),
|
|
113
|
+
};
|
|
114
|
+
this.runs.set(run.id, run);
|
|
115
|
+
this.pruneOldRuns();
|
|
116
|
+
this.touch(run);
|
|
117
|
+
return run;
|
|
118
|
+
}
|
|
119
|
+
updateRun(id, patch) {
|
|
120
|
+
const run = this.runs.get(id);
|
|
121
|
+
if (!run)
|
|
122
|
+
return undefined;
|
|
123
|
+
Object.assign(run, patch);
|
|
124
|
+
this.touch(run);
|
|
125
|
+
return run;
|
|
126
|
+
}
|
|
127
|
+
/** Append a step to an existing run (used by "Continue" — spec 003). */
|
|
128
|
+
addStep(runId, step) {
|
|
129
|
+
const run = this.runs.get(runId);
|
|
130
|
+
if (!run || run.steps.some((s) => s.id === step.id))
|
|
131
|
+
return;
|
|
132
|
+
run.steps.push({ ...step, status: 'pending', iterations: 0, tokensUsed: 0 });
|
|
133
|
+
this.touch(run);
|
|
134
|
+
}
|
|
135
|
+
updateStep(runId, stepId, patch) {
|
|
136
|
+
const run = this.runs.get(runId);
|
|
137
|
+
const step = run?.steps.find((s) => s.id === stepId);
|
|
138
|
+
if (!run || !step)
|
|
139
|
+
return;
|
|
140
|
+
Object.assign(step, patch);
|
|
141
|
+
run.tokensUsed = run.steps.reduce((sum, s) => sum + s.tokensUsed, 0);
|
|
142
|
+
const cost = run.steps.reduce((sum, s) => sum + (s.costUsd ?? 0), 0);
|
|
143
|
+
run.costUsd = cost > 0 ? cost : undefined;
|
|
144
|
+
this.touch(run);
|
|
145
|
+
}
|
|
146
|
+
setArchived(id, archived) {
|
|
147
|
+
const run = this.runs.get(id);
|
|
148
|
+
if (!run)
|
|
149
|
+
return undefined;
|
|
150
|
+
run.archived = archived;
|
|
151
|
+
run.archivedAt = archived ? new Date().toISOString() : undefined;
|
|
152
|
+
this.touch(run);
|
|
153
|
+
return run;
|
|
154
|
+
}
|
|
155
|
+
/** Bulk-archive every finished run; returns how many were archived. */
|
|
156
|
+
archiveFinished() {
|
|
157
|
+
let count = 0;
|
|
158
|
+
for (const run of this.runs.values()) {
|
|
159
|
+
if (!run.archived && ['done', 'failed', 'cancelled'].includes(run.status)) {
|
|
160
|
+
run.archived = true;
|
|
161
|
+
run.archivedAt = new Date().toISOString();
|
|
162
|
+
this.touch(run);
|
|
163
|
+
count++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return count;
|
|
167
|
+
}
|
|
168
|
+
appendEvent(runId, event) {
|
|
169
|
+
const run = this.runs.get(runId);
|
|
170
|
+
if (!run)
|
|
171
|
+
throw new Error(`unknown run: ${runId}`);
|
|
172
|
+
const seq = this.nextSeq(runId);
|
|
173
|
+
const full = { ...event, seq, ts: new Date().toISOString() };
|
|
174
|
+
// Sync append keeps event order without a write queue; local NDJSON
|
|
175
|
+
// appends at agent-event rates are effectively free.
|
|
176
|
+
appendFileSync(this.eventsPath(runId), `${JSON.stringify(full)}\n`, 'utf8');
|
|
177
|
+
this.emit('event', { runId, event: full });
|
|
178
|
+
// The janitor trick: agents print the PR URL after `gh pr create` — the
|
|
179
|
+
// first one spotted in the transcript becomes the run's PR link.
|
|
180
|
+
if (!run.pullRequestUrl) {
|
|
181
|
+
const haystack = [full.text, full.result, full.message]
|
|
182
|
+
.filter((s) => typeof s === 'string')
|
|
183
|
+
.join(' ');
|
|
184
|
+
const match = PR_URL_RE.exec(haystack);
|
|
185
|
+
if (match)
|
|
186
|
+
this.updateRun(runId, { pullRequestUrl: match[0] });
|
|
187
|
+
}
|
|
188
|
+
return full;
|
|
189
|
+
}
|
|
190
|
+
readEvents(runId) {
|
|
191
|
+
try {
|
|
192
|
+
const raw = readFileSync(this.eventsPath(runId), 'utf8');
|
|
193
|
+
return raw
|
|
194
|
+
.split('\n')
|
|
195
|
+
.filter(Boolean)
|
|
196
|
+
.map((line) => {
|
|
197
|
+
try {
|
|
198
|
+
return JSON.parse(line);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
.filter((e) => e !== null);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
deleteRun(id) {
|
|
211
|
+
const existed = this.runs.delete(id);
|
|
212
|
+
if (existed) {
|
|
213
|
+
try {
|
|
214
|
+
rmSync(this.eventsPath(id), { force: true });
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// best effort — the index is authoritative
|
|
218
|
+
}
|
|
219
|
+
this.seqs.delete(id);
|
|
220
|
+
this.scheduleSave();
|
|
221
|
+
this.emit('deleted', id);
|
|
222
|
+
}
|
|
223
|
+
return existed;
|
|
224
|
+
}
|
|
225
|
+
/** Write the index out now (used on shutdown). */
|
|
226
|
+
flush() {
|
|
227
|
+
if (this.saveTimer) {
|
|
228
|
+
clearTimeout(this.saveTimer);
|
|
229
|
+
this.saveTimer = null;
|
|
230
|
+
}
|
|
231
|
+
this.saveNow();
|
|
232
|
+
}
|
|
233
|
+
// ---- internals -----------------------------------------------------------
|
|
234
|
+
seqs = new Map();
|
|
235
|
+
nextSeq(runId) {
|
|
236
|
+
const next = (this.seqs.get(runId) ?? 0) + 1;
|
|
237
|
+
this.seqs.set(runId, next);
|
|
238
|
+
return next;
|
|
239
|
+
}
|
|
240
|
+
eventsPath(runId) {
|
|
241
|
+
return join(this.dataDir, 'runs', `${runId}.ndjson`);
|
|
242
|
+
}
|
|
243
|
+
touch(run) {
|
|
244
|
+
this.scheduleSave();
|
|
245
|
+
this.emit('run', run);
|
|
246
|
+
}
|
|
247
|
+
pruneOldRuns() {
|
|
248
|
+
const all = this.listRuns();
|
|
249
|
+
const stalePool = [
|
|
250
|
+
...all.filter((r) => !r.archived).slice(MAX_RUNS_KEPT),
|
|
251
|
+
...all.filter((r) => r.archived).slice(MAX_ARCHIVED_KEPT),
|
|
252
|
+
];
|
|
253
|
+
for (const stale of stalePool) {
|
|
254
|
+
this.runs.delete(stale.id);
|
|
255
|
+
try {
|
|
256
|
+
rmSync(this.eventsPath(stale.id), { force: true });
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// best effort
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/** Debounced so token-usage updates don't rewrite the index per event. */
|
|
264
|
+
scheduleSave() {
|
|
265
|
+
if (this.saveTimer)
|
|
266
|
+
return;
|
|
267
|
+
this.saveTimer = setTimeout(() => {
|
|
268
|
+
this.saveTimer = null;
|
|
269
|
+
this.saveNow();
|
|
270
|
+
}, 300);
|
|
271
|
+
this.saveTimer.unref?.();
|
|
272
|
+
}
|
|
273
|
+
saveNow() {
|
|
274
|
+
const indexPath = join(this.dataDir, 'runs.json');
|
|
275
|
+
const tmpPath = `${indexPath}.tmp`;
|
|
276
|
+
try {
|
|
277
|
+
writeFileSync(tmpPath, JSON.stringify(this.listRuns(), null, 2), 'utf8');
|
|
278
|
+
renameSync(tmpPath, indexPath);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
282
|
+
console.error(`[cez] failed to save runs.json: ${message}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/runs/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface RepoInfo {
|
|
2
|
+
root: string;
|
|
3
|
+
branch: string;
|
|
4
|
+
remote?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface StatusEntry {
|
|
7
|
+
status: string;
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LogEntry {
|
|
11
|
+
hash: string;
|
|
12
|
+
subject: string;
|
|
13
|
+
author: string;
|
|
14
|
+
when: string;
|
|
15
|
+
}
|
|
16
|
+
/** Null when `dir` isn't inside a git repository. */
|
|
17
|
+
export declare function getRepoInfo(dir: string): Promise<RepoInfo | null>;
|
|
18
|
+
export declare function getStatus(root: string): Promise<StatusEntry[]>;
|
|
19
|
+
/** Working-tree diff vs HEAD (staged + unstaged), capped for the GUI. */
|
|
20
|
+
export declare function getDiff(root: string, cap?: number): Promise<string>;
|
|
21
|
+
export declare function getLog(root: string, count?: number): Promise<LogEntry[]>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const exec = promisify(execFile);
|
|
4
|
+
async function git(root, args) {
|
|
5
|
+
const { stdout } = await exec('git', args, { cwd: root, maxBuffer: 10 * 1024 * 1024 });
|
|
6
|
+
return stdout;
|
|
7
|
+
}
|
|
8
|
+
/** Null when `dir` isn't inside a git repository. */
|
|
9
|
+
export async function getRepoInfo(dir) {
|
|
10
|
+
try {
|
|
11
|
+
const root = (await git(dir, ['rev-parse', '--show-toplevel'])).trim();
|
|
12
|
+
const branch = (await git(root, ['rev-parse', '--abbrev-ref', 'HEAD'])).trim();
|
|
13
|
+
let remote;
|
|
14
|
+
try {
|
|
15
|
+
remote = (await git(root, ['remote', 'get-url', 'origin'])).trim() || undefined;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// no origin — local-only repo
|
|
19
|
+
}
|
|
20
|
+
return { root, branch, remote };
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function getStatus(root) {
|
|
27
|
+
const out = await git(root, ['status', '--porcelain']);
|
|
28
|
+
return out
|
|
29
|
+
.split('\n')
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.map((line) => ({ status: line.slice(0, 2).trim() || '??', path: line.slice(3) }));
|
|
32
|
+
}
|
|
33
|
+
/** Working-tree diff vs HEAD (staged + unstaged), capped for the GUI. */
|
|
34
|
+
export async function getDiff(root, cap = 400_000) {
|
|
35
|
+
const diff = await git(root, ['diff', 'HEAD']);
|
|
36
|
+
if (diff.length > cap)
|
|
37
|
+
return `${diff.slice(0, cap)}\n… (diff truncated)`;
|
|
38
|
+
return diff;
|
|
39
|
+
}
|
|
40
|
+
export async function getLog(root, count = 20) {
|
|
41
|
+
const out = await git(root, [
|
|
42
|
+
'log',
|
|
43
|
+
`-${count}`,
|
|
44
|
+
'--pretty=format:%h%x1f%s%x1f%an%x1f%cr',
|
|
45
|
+
]);
|
|
46
|
+
return out
|
|
47
|
+
.split('\n')
|
|
48
|
+
.filter(Boolean)
|
|
49
|
+
.map((line) => {
|
|
50
|
+
const [hash = '', subject = '', author = '', when = ''] = line.split('\x1f');
|
|
51
|
+
return { hash, subject, author, when };
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/server/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Open a real terminal window at `cwd` running `command` — the "take over the
|
|
3
|
+
* agent session locally" handoff. Cross-platform strategy ported from
|
|
4
|
+
* github-janitor's `openInTerminal.ts`: macOS via osascript, Windows via
|
|
5
|
+
* cmd/start, Linux by probing common emulators with a temp launch script
|
|
6
|
+
* (which sidesteps per-emulator quoting rules).
|
|
7
|
+
*
|
|
8
|
+
* Returns true when a launcher was started without an immediate error; the
|
|
9
|
+
* caller shows a copy-the-command fallback otherwise.
|
|
10
|
+
*/
|
|
11
|
+
export declare function openInTerminal(cwd: string, command: string): Promise<boolean>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { chmodSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
/**
|
|
6
|
+
* Open a real terminal window at `cwd` running `command` — the "take over the
|
|
7
|
+
* agent session locally" handoff. Cross-platform strategy ported from
|
|
8
|
+
* github-janitor's `openInTerminal.ts`: macOS via osascript, Windows via
|
|
9
|
+
* cmd/start, Linux by probing common emulators with a temp launch script
|
|
10
|
+
* (which sidesteps per-emulator quoting rules).
|
|
11
|
+
*
|
|
12
|
+
* Returns true when a launcher was started without an immediate error; the
|
|
13
|
+
* caller shows a copy-the-command fallback otherwise.
|
|
14
|
+
*/
|
|
15
|
+
export async function openInTerminal(cwd, command) {
|
|
16
|
+
if (process.platform === 'darwin') {
|
|
17
|
+
const script = `cd ${shellQuote(cwd)} && ${command}`;
|
|
18
|
+
return runDetached('osascript', [
|
|
19
|
+
'-e',
|
|
20
|
+
'tell application "Terminal" to activate',
|
|
21
|
+
'-e',
|
|
22
|
+
`tell application "Terminal" to do script ${appleScriptQuote(script)}`,
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
if (process.platform === 'win32') {
|
|
26
|
+
const inner = `cd /d "${cwd}" && ${command}`;
|
|
27
|
+
// Windows Terminal first, classic cmd window as fallback.
|
|
28
|
+
if (await runDetached('cmd', ['/c', 'start', '', 'wt', '-d', cwd, 'cmd', '/K', command])) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return runDetached('cmd', ['/c', 'start', '', 'cmd', '/K', inner]);
|
|
32
|
+
}
|
|
33
|
+
// Linux/other: a temp script avoids each emulator's own quoting rules.
|
|
34
|
+
const dir = mkdtempSync(join(tmpdir(), 'cez-term-'));
|
|
35
|
+
const scriptPath = join(dir, 'launch.sh');
|
|
36
|
+
writeFileSync(scriptPath, `#!/usr/bin/env bash\ncd ${shellQuote(cwd)}\n${command}\nexec bash\n`, 'utf8');
|
|
37
|
+
chmodSync(scriptPath, 0o755);
|
|
38
|
+
const candidates = [
|
|
39
|
+
['x-terminal-emulator', ['-e', scriptPath]],
|
|
40
|
+
['gnome-terminal', ['--', scriptPath]],
|
|
41
|
+
['konsole', ['-e', scriptPath]],
|
|
42
|
+
['wezterm', ['start', '--', scriptPath]],
|
|
43
|
+
['kitty', [scriptPath]],
|
|
44
|
+
['alacritty', ['-e', scriptPath]],
|
|
45
|
+
['xterm', ['-e', scriptPath]],
|
|
46
|
+
];
|
|
47
|
+
for (const [bin, args] of candidates) {
|
|
48
|
+
if (await runDetached(bin, args))
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
/** Spawn detached; success = no error within a short settle window. */
|
|
54
|
+
function runDetached(bin, args) {
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
let child;
|
|
57
|
+
try {
|
|
58
|
+
child = spawn(bin, args, { stdio: 'ignore', detached: true });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
resolve(false);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let settled = false;
|
|
65
|
+
const settle = (ok) => {
|
|
66
|
+
if (settled)
|
|
67
|
+
return;
|
|
68
|
+
settled = true;
|
|
69
|
+
resolve(ok);
|
|
70
|
+
};
|
|
71
|
+
child.once('error', () => settle(false));
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
child.unref();
|
|
74
|
+
settle(true);
|
|
75
|
+
}, 250);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function shellQuote(s) {
|
|
79
|
+
return `'${s.replaceAll("'", `'\\''`)}'`;
|
|
80
|
+
}
|
|
81
|
+
function appleScriptQuote(s) {
|
|
82
|
+
return `"${s.replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=open-in-terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-in-terminal.js","sourceRoot":"","sources":["../../src/server/open-in-terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { type ServerType } from '@hono/node-server';
|
|
3
|
+
import type { RunStore } from '../runs/store.js';
|
|
4
|
+
import type { RunManager } from '../workflows/run.js';
|
|
5
|
+
export interface ServerDeps {
|
|
6
|
+
repoRoot: string;
|
|
7
|
+
store: RunStore;
|
|
8
|
+
manager: RunManager;
|
|
9
|
+
version: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createApp(deps: ServerDeps): Hono;
|
|
12
|
+
export declare function startServer(deps: ServerDeps, port: number): ServerType;
|