specrails-desktop 2.2.0 → 2.3.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/client/dist/assets/ActivityFeedPage-3Veccrvk.js +1 -0
- package/client/dist/assets/AgentsPage-2mFPghP4.js +86 -0
- package/client/dist/assets/{AnalyticsPage-D6LE6wG2.js → AnalyticsPage-Dyyz1ht3.js} +1 -1
- package/client/dist/assets/{BarChart-B366kDEj.js → BarChart-CMdLa6Es.js} +2 -2
- package/client/dist/assets/CodePage-D7Xwjhut.js +2 -0
- package/client/dist/assets/{DesktopAnalyticsPage-DG5LA_WO.js → DesktopAnalyticsPage-CTNwb639.js} +1 -1
- package/client/dist/assets/{DocsDialog-ChQ1oXLC.js → DocsDialog-D8yoyZDD.js} +2 -2
- package/client/dist/assets/{DocsPage-BfGH8NUf.js → DocsPage-CeO-fAxy.js} +2 -2
- package/client/dist/assets/{ExportDropdown-9tRrlfM7.js → ExportDropdown-DuoZcdYN.js} +1 -1
- package/client/dist/assets/{IntegrationsPage-DANIzihd.js → IntegrationsPage-iIZ0UEzf.js} +3 -3
- package/client/dist/assets/JobDetailPage-DgJHAH2m.js +16 -0
- package/client/dist/assets/JobsPage-Bv_RpRAE.js +1 -0
- package/client/dist/assets/code-BtsmPQLV.js +1 -0
- package/client/dist/assets/code-CY85RXZU.js +1 -0
- package/client/dist/assets/code-Coa8f2Sh.js +1 -0
- package/client/dist/assets/code-D1z-YDt-.js +1 -0
- package/client/dist/assets/code-DDU0CRS0.js +1 -0
- package/client/dist/assets/code-L35Loak_.js +1 -0
- package/client/dist/assets/code-g0qFMzyg.js +1 -0
- package/client/dist/assets/code-zCwBt3Uu.js +1 -0
- package/client/dist/assets/{dist-js-BvQ52Q67.js → dist-js-4UEGaKhD.js} +1 -1
- package/client/dist/assets/{dist-js-XEilFTNz.js → dist-js-H6hyhSuv.js} +1 -1
- package/client/dist/assets/{index-CNiaj7Sj.js → index-CGHKpC-N.js} +13 -13
- package/client/dist/assets/index-D17R4Cjc.css +2 -0
- package/client/dist/assets/{lib-DZJmnErt.js → lib-Cs5FrUJI.js} +1 -1
- package/client/dist/assets/{useProjectCache-H0T8Ot9j.js → useProjectCache-BZWYV-w-.js} +1 -1
- package/client/dist/index.html +3 -3
- package/package.json +1 -1
- package/server/dist/agent-refine-manager.js +128 -153
- package/server/dist/chat-manager.js +246 -0
- package/server/dist/code-explorer-router.js +78 -0
- package/server/dist/command-resolver.js +17 -0
- package/server/dist/contract-refine-runner.js +42 -10
- package/server/dist/db.js +6 -0
- package/server/dist/desktop-db.js +3 -0
- package/server/dist/explore-stdin-session.js +129 -0
- package/server/dist/mobile/mobile-auth.js +16 -0
- package/server/dist/project-router-chat.js +218 -0
- package/server/dist/project-router-helpers.js +275 -0
- package/server/dist/project-router-jobs.js +389 -0
- package/server/dist/project-router-settings.js +312 -0
- package/server/dist/project-router-setup.js +456 -0
- package/server/dist/project-router-spending.js +320 -0
- package/server/dist/project-router-terminals.js +312 -0
- package/server/dist/project-router-tickets.js +1767 -0
- package/server/dist/project-router.js +27 -3943
- package/server/dist/providers/claude-adapter.js +58 -17
- package/server/dist/providers/codex-adapter.js +6 -0
- package/server/dist/spawn-lifecycle.js +117 -0
- package/client/dist/assets/ActivityFeedPage-BupGdGjj.js +0 -1
- package/client/dist/assets/AgentsPage-F3xksiLd.js +0 -86
- package/client/dist/assets/CodePage-DLwCJgQ0.js +0 -2
- package/client/dist/assets/JobDetailPage-1RtejIOB.js +0 -16
- package/client/dist/assets/JobsPage-NuDf5Zbx.js +0 -1
- package/client/dist/assets/code-AL1rVIMb.js +0 -1
- package/client/dist/assets/code-C0BKpkht.js +0 -1
- package/client/dist/assets/code-C0FTS3ew.js +0 -1
- package/client/dist/assets/code-CPcHxzxw.js +0 -1
- package/client/dist/assets/code-D3ryDniw.js +0 -1
- package/client/dist/assets/code-D3zVVQTj.js +0 -1
- package/client/dist/assets/code-PCmfS3dn.js +0 -1
- package/client/dist/assets/code-exI0G5Wd.js +0 -1
- package/client/dist/assets/index-DgFfrrTX.css +0 -2
|
@@ -38,28 +38,46 @@ function normaliseModel(model) {
|
|
|
38
38
|
return model || 'sonnet';
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
/** Default arg block every claude spawn shares.
|
|
41
|
+
/** Default arg block every claude spawn shares. `--setting-sources` is appended
|
|
42
|
+
* separately per-spawn (see `commonFlagsFor`) because its value depends on
|
|
43
|
+
* whether the caller opted into loading the user's full Claude environment. */
|
|
42
44
|
const COMMON_FLAGS = [
|
|
43
45
|
'--dangerously-skip-permissions',
|
|
44
46
|
'--tools', 'default',
|
|
45
47
|
'--output-format', 'stream-json',
|
|
46
48
|
'--verbose',
|
|
47
|
-
// Isolate app-spawned claude from the *user's* global Claude config. Without
|
|
48
|
-
// this, the child loads ~/.claude (user CLAUDE.md memory, plugins like
|
|
49
|
-
// claude-mem, SessionStart hooks). That bled cross-project memory into
|
|
50
|
-
// Explore turns (e.g. an unrelated "fighting game" surfaced for a fresh
|
|
51
|
-
// project) and inflated spec-gen tool usage past --max-turns. `project,local`
|
|
52
|
-
// still loads the target repo's own .claude settings + CLAUDE.md, which is
|
|
53
|
-
// exactly the context an app run should see.
|
|
54
|
-
'--setting-sources', 'project,local',
|
|
55
49
|
];
|
|
50
|
+
/**
|
|
51
|
+
* COMMON_FLAGS + the `--setting-sources` value for this spawn.
|
|
52
|
+
*
|
|
53
|
+
* Default (`project,local`) isolates app-spawned claude from the *user's*
|
|
54
|
+
* global Claude config. Without this, the child loads ~/.claude (user CLAUDE.md
|
|
55
|
+
* memory, plugins like claude-mem, SessionStart hooks). That bled cross-project
|
|
56
|
+
* memory into Explore turns (e.g. an unrelated "fighting game" surfaced for a
|
|
57
|
+
* fresh project) and inflated spec-gen tool usage past --max-turns.
|
|
58
|
+
*
|
|
59
|
+
* When `opts.loadUserEnv` is set (the Add Spec "My approved MCPs" toggle), we
|
|
60
|
+
* switch to `user,project,local` so the developer's user-scope, plugin-bundled,
|
|
61
|
+
* and connector MCP servers are discovered. This is the ONLY way those MCP
|
|
62
|
+
* servers load (verified empirically against claude 2.1.177 — plugin MCP
|
|
63
|
+
* servers are gated by the `user` setting source); it also re-loads user
|
|
64
|
+
* CLAUDE.md + hooks, which is the user's explicit opt-in via the toggle.
|
|
65
|
+
*/
|
|
66
|
+
function commonFlagsFor(opts) {
|
|
67
|
+
return [
|
|
68
|
+
...COMMON_FLAGS,
|
|
69
|
+
'--setting-sources',
|
|
70
|
+
opts.loadUserEnv ? 'user,project,local' : 'project,local',
|
|
71
|
+
];
|
|
72
|
+
}
|
|
56
73
|
function buildClaudeArgs(action, opts) {
|
|
57
74
|
const args = [];
|
|
58
75
|
const model = normaliseModel(opts.model);
|
|
76
|
+
const commonFlags = commonFlagsFor(opts);
|
|
59
77
|
switch (action) {
|
|
60
78
|
case 'chat-turn': {
|
|
61
79
|
args.push('--model', model);
|
|
62
|
-
args.push(...
|
|
80
|
+
args.push(...commonFlags);
|
|
63
81
|
if (opts.systemPrompt)
|
|
64
82
|
args.push('--system-prompt', opts.systemPrompt);
|
|
65
83
|
args.push('-p', opts.prompt);
|
|
@@ -74,7 +92,7 @@ function buildClaudeArgs(action, opts) {
|
|
|
74
92
|
throw new Error('chat-resume requires sessionId');
|
|
75
93
|
}
|
|
76
94
|
args.push('--model', model);
|
|
77
|
-
args.push(...
|
|
95
|
+
args.push(...commonFlags);
|
|
78
96
|
if (opts.systemPrompt)
|
|
79
97
|
args.push('--system-prompt', opts.systemPrompt);
|
|
80
98
|
args.push('--resume', opts.sessionId);
|
|
@@ -85,11 +103,33 @@ function buildClaudeArgs(action, opts) {
|
|
|
85
103
|
args.push(...opts.extraArgs);
|
|
86
104
|
return args;
|
|
87
105
|
}
|
|
106
|
+
case 'chat-stream': {
|
|
107
|
+
// Persistent multi-turn transport: one child stays alive and reads
|
|
108
|
+
// newline-delimited stream-json user messages from stdin (no `-p
|
|
109
|
+
// <prompt>` argument — the prompt arrives over stdin). The system prompt
|
|
110
|
+
// is fixed once at spawn (the Explore lightweight prompt is byte-stable,
|
|
111
|
+
// so this is sound). `--max-turns` is intentionally omitted: it would
|
|
112
|
+
// terminate the whole process after N agentic turns and end the session.
|
|
113
|
+
args.push('--model', model);
|
|
114
|
+
args.push(...commonFlags);
|
|
115
|
+
if (opts.systemPrompt)
|
|
116
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
117
|
+
// When the conversation already has a session (a re-spawn after idle-kill
|
|
118
|
+
// or crash), resume it so the persistent child restores prior context
|
|
119
|
+
// instead of starting a fresh thread. Absent on the very first turn.
|
|
120
|
+
if (opts.sessionId)
|
|
121
|
+
args.push('--resume', opts.sessionId);
|
|
122
|
+
args.push('-p');
|
|
123
|
+
args.push('--input-format', 'stream-json');
|
|
124
|
+
if (opts.extraArgs)
|
|
125
|
+
args.push(...opts.extraArgs);
|
|
126
|
+
return args;
|
|
127
|
+
}
|
|
88
128
|
case 'rail-job': {
|
|
89
129
|
// QueueManager spawns with `--append-system-prompt` (not `--system-prompt`)
|
|
90
130
|
// because the slash command in the prompt brings its own system prompt;
|
|
91
131
|
// we ADD to it rather than overwrite.
|
|
92
|
-
args.push(...
|
|
132
|
+
args.push(...commonFlags);
|
|
93
133
|
args.push('--model', model);
|
|
94
134
|
if (opts.systemPrompt)
|
|
95
135
|
args.push('--append-system-prompt', opts.systemPrompt);
|
|
@@ -99,7 +139,7 @@ function buildClaudeArgs(action, opts) {
|
|
|
99
139
|
return args;
|
|
100
140
|
}
|
|
101
141
|
case 'spec-gen': {
|
|
102
|
-
args.push(...
|
|
142
|
+
args.push(...commonFlags);
|
|
103
143
|
args.push('--model', model);
|
|
104
144
|
if (opts.maxTurns != null)
|
|
105
145
|
args.push('--max-turns', String(opts.maxTurns));
|
|
@@ -113,7 +153,7 @@ function buildClaudeArgs(action, opts) {
|
|
|
113
153
|
return args;
|
|
114
154
|
}
|
|
115
155
|
case 'agent-refine': {
|
|
116
|
-
args.push(...
|
|
156
|
+
args.push(...commonFlags);
|
|
117
157
|
if (opts.sessionId)
|
|
118
158
|
args.push('--resume', opts.sessionId);
|
|
119
159
|
args.push('-p', opts.prompt);
|
|
@@ -123,7 +163,7 @@ function buildClaudeArgs(action, opts) {
|
|
|
123
163
|
}
|
|
124
164
|
case 'setup-enrich': {
|
|
125
165
|
args.push('-p', opts.prompt);
|
|
126
|
-
args.push(...
|
|
166
|
+
args.push(...commonFlags);
|
|
127
167
|
if (opts.extraArgs)
|
|
128
168
|
args.push(...opts.extraArgs);
|
|
129
169
|
return args;
|
|
@@ -133,14 +173,14 @@ function buildClaudeArgs(action, opts) {
|
|
|
133
173
|
throw new Error('setup-enrich-resume requires sessionId');
|
|
134
174
|
}
|
|
135
175
|
args.push('--resume', opts.sessionId);
|
|
136
|
-
args.push(...
|
|
176
|
+
args.push(...commonFlags);
|
|
137
177
|
args.push('-p', opts.prompt);
|
|
138
178
|
if (opts.extraArgs)
|
|
139
179
|
args.push(...opts.extraArgs);
|
|
140
180
|
return args;
|
|
141
181
|
}
|
|
142
182
|
case 'auto-title': {
|
|
143
|
-
args.push(...
|
|
183
|
+
args.push(...commonFlags);
|
|
144
184
|
args.push('-p', opts.prompt);
|
|
145
185
|
return args;
|
|
146
186
|
}
|
|
@@ -270,6 +310,7 @@ exports.claudeAdapter = {
|
|
|
270
310
|
nativeOtelEnv: true,
|
|
271
311
|
profileEnvSupport: true,
|
|
272
312
|
systemPromptArg: true,
|
|
313
|
+
persistentStdin: true,
|
|
273
314
|
},
|
|
274
315
|
modelCatalog: () => CLAUDE_MODELS,
|
|
275
316
|
defaultModel: () => 'sonnet',
|
|
@@ -98,6 +98,12 @@ function buildCodexArgs(action, opts) {
|
|
|
98
98
|
args.push(...opts.extraArgs);
|
|
99
99
|
return args;
|
|
100
100
|
}
|
|
101
|
+
case 'chat-stream': {
|
|
102
|
+
// Codex has no persistent stdin multi-turn transport; the Explore
|
|
103
|
+
// fast-path gates on capabilities.persistentStdin, so this is never
|
|
104
|
+
// reached. Throw defensively rather than emit a broken argv.
|
|
105
|
+
throw new Error('codex does not support persistent stdin streaming (chat-stream)');
|
|
106
|
+
}
|
|
101
107
|
case 'rail-job': {
|
|
102
108
|
// Rail jobs are headless implementation pipelines. They must run repo
|
|
103
109
|
// inspection, edits, tests, and git probes without interactive approval.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared AI-CLI spawn lifecycle.
|
|
3
|
+
//
|
|
4
|
+
// Every AI-CLI invocation in the app (agent-refine, contract-refine, quick-spec,
|
|
5
|
+
// setup-enrich, explore chat, rail jobs) shares the same boilerplate: spawn the
|
|
6
|
+
// provider binary, read stdout line-by-line through `adapter.parseStreamLine`
|
|
7
|
+
// into an AdapterEvent[] accumulator, capture the session id, drain stderr, and
|
|
8
|
+
// settle once on `close` (or a wall-clock timeout / spawn error). This module
|
|
9
|
+
// owns exactly that core and nothing more — the genuinely-unique bits (how a
|
|
10
|
+
// callsite renders deltas, finalizes invocation accounting, validates output,
|
|
11
|
+
// manages crash-respawn / idle-kill) stay with the caller via hooks + the
|
|
12
|
+
// returned result. Adopting it is behaviour-preserving: the caller keeps every
|
|
13
|
+
// post-close decision it had, just without re-implementing the plumbing.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.runAiCliInvocation = runAiCliInvocation;
|
|
16
|
+
const node_readline_1 = require("node:readline");
|
|
17
|
+
const cli_prompt_1 = require("./util/cli-prompt");
|
|
18
|
+
const STDERR_TAIL_CAP = 64 * 1024;
|
|
19
|
+
/**
|
|
20
|
+
* Run one AI-CLI invocation: spawn → stream → settle. Resolves exactly once on
|
|
21
|
+
* child 'close', a spawn 'error', or the optional timeout. Never rejects — a
|
|
22
|
+
* failure is reported via the result (`spawnFailed`/`timedOut`/`code`). The
|
|
23
|
+
* caller does all post-close work (finalise/record/validate) from the returned
|
|
24
|
+
* events.
|
|
25
|
+
*/
|
|
26
|
+
function runAiCliInvocation(hooks) {
|
|
27
|
+
const binary = hooks.binary ?? hooks.adapter.binary;
|
|
28
|
+
const args = hooks.argv ?? hooks.adapter.buildArgs(hooks.action, hooks.buildOpts);
|
|
29
|
+
const spawn = hooks.spawn ?? cli_prompt_1.spawnAiCli;
|
|
30
|
+
const events = [];
|
|
31
|
+
let lastResultEvent = null;
|
|
32
|
+
let sessionId = null;
|
|
33
|
+
let stderrTail = '';
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
let settled = false;
|
|
36
|
+
let timer = null;
|
|
37
|
+
const settle = (partial) => {
|
|
38
|
+
if (settled)
|
|
39
|
+
return;
|
|
40
|
+
settled = true;
|
|
41
|
+
if (timer) {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
timer = null;
|
|
44
|
+
}
|
|
45
|
+
resolve({ ...partial, events, lastResultEvent, sessionId, stderrTail });
|
|
46
|
+
};
|
|
47
|
+
let child;
|
|
48
|
+
try {
|
|
49
|
+
child = spawn(binary, args, {
|
|
50
|
+
env: hooks.env ?? process.env,
|
|
51
|
+
stdio: hooks.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
52
|
+
cwd: hooks.cwd,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
hooks.onSpawnError?.(err);
|
|
57
|
+
settle({ code: null, timedOut: false, spawnFailed: true, child: null });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
hooks.onSpawn?.(child);
|
|
61
|
+
if (hooks.timeoutMs != null) {
|
|
62
|
+
timer = setTimeout(() => {
|
|
63
|
+
hooks.onTimeout?.();
|
|
64
|
+
try {
|
|
65
|
+
child.kill('SIGTERM');
|
|
66
|
+
}
|
|
67
|
+
catch { /* already gone */ }
|
|
68
|
+
settle({ code: null, timedOut: true, spawnFailed: false, child });
|
|
69
|
+
}, hooks.timeoutMs);
|
|
70
|
+
}
|
|
71
|
+
if (child.stdout) {
|
|
72
|
+
const reader = (0, node_readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
|
|
73
|
+
if (hooks.onData)
|
|
74
|
+
child.stdout.on('data', () => hooks.onData('stdout'));
|
|
75
|
+
reader.on('line', (line) => {
|
|
76
|
+
hooks.onStdoutLine?.(line);
|
|
77
|
+
const ev = hooks.adapter.parseStreamLine(line);
|
|
78
|
+
if (!ev)
|
|
79
|
+
return;
|
|
80
|
+
events.push(ev);
|
|
81
|
+
if (ev.kind === 'session-started') {
|
|
82
|
+
sessionId = ev.sessionId;
|
|
83
|
+
}
|
|
84
|
+
else if (ev.kind === 'result') {
|
|
85
|
+
lastResultEvent = ev;
|
|
86
|
+
const sid = ev.payload.session_id;
|
|
87
|
+
if (sid)
|
|
88
|
+
sessionId = sid;
|
|
89
|
+
}
|
|
90
|
+
hooks.onEvent?.(ev);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (child.stderr) {
|
|
94
|
+
if (hooks.onStderrLine) {
|
|
95
|
+
const errReader = (0, node_readline_1.createInterface)({ input: child.stderr, crlfDelay: Infinity });
|
|
96
|
+
if (hooks.onData)
|
|
97
|
+
child.stderr.on('data', () => hooks.onData('stderr'));
|
|
98
|
+
errReader.on('line', (line) => hooks.onStderrLine(line));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
child.stderr.on('data', (chunk) => {
|
|
102
|
+
if (hooks.onData)
|
|
103
|
+
hooks.onData('stderr');
|
|
104
|
+
if (stderrTail.length < STDERR_TAIL_CAP)
|
|
105
|
+
stderrTail += chunk.toString();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
child.on('error', (err) => {
|
|
110
|
+
hooks.onSpawnError?.(err);
|
|
111
|
+
settle({ code: null, timedOut: false, spawnFailed: true, child });
|
|
112
|
+
});
|
|
113
|
+
child.on('close', (code) => {
|
|
114
|
+
settle({ code, timedOut: false, spawnFailed: false, child });
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{K as t,Mt as n,Nt as r,St as i,_t as a,at as o,bt as s,j as c,jt as l,ot as u,qt as d,tn as f,vt as p}from"./index-CNiaj7Sj.js";var m=p(`ban`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M4.929 4.929 19.07 19.071`,key:`196cmz`}]]),h=e(f(),1);function g(e){let t=new Set;return e.filter(e=>{let n=`${e.type}:${e.jobId}`;return t.has(n)?!1:(t.add(n),!0)})}function _({activeProjectId:e,limit:t=50}){let[n,a]=(0,h.useState)([]),[o,s]=(0,h.useState)(!1),[c,u]=(0,h.useState)(!0),d=(0,h.useRef)(e);(0,h.useEffect)(()=>{d.current=e},[e]);let{registerHandler:f,unregisterHandler:p}=l();(0,h.useEffect)(()=>{if(!e){a([]),u(!0);return}let n=!1;s(!0),a([]),u(!0);async function i(){let e=r();try{let r=await fetch(`${e}/activity?limit=${t}`);if(!r.ok||n)return;let i=await r.json();if(n)return;a(i),u(i.length===t)}catch{}finally{n||s(!1)}}return i(),()=>{n=!0}},[e,t]);let m=(0,h.useCallback)(async()=>{if(o||!c)return;s(!0);let e=r();try{a(n=>{let r=n[n.length-1];if(!r)return n;let i=encodeURIComponent(r.timestamp);return fetch(`${e}/activity?limit=${t}&before=${i}`).then(e=>e.json()).then(e=>{a(t=>g([...t,...e])),u(e.length===t),s(!1)}).catch(()=>s(!1)),n})}catch{s(!1)}},[o,c,t]),_=(0,h.useCallback)(e=>{let t=e,n=d.current;if(!(!t||typeof t.type!=`string`)&&!(t.projectId&&t.projectId!==n)){if(t.type===`queue`&&Array.isArray(t.jobs)){let e=[];for(let n of t.jobs){let t=n.status===`completed`?`job_completed`:n.status===`failed`?`job_failed`:n.status===`canceled`?`job_canceled`:`job_started`;e.push({id:`${t}:${n.id}`,type:t,jobId:n.id,jobCommand:n.command??``,timestamp:n.startedAt??new Date().toISOString(),summary:`${t.replace(`_`,` `)}: ${n.command??``}`,costUsd:null})}e.length>0&&a(t=>g([...e,...t]))}if(t.type===`phase`&&t.phase&&t.state&&t.timestamp){let e={id:`phase:${t.phase}:${t.state}:${t.timestamp}`,type:`job_started`,jobId:`phase-${t.phase}`,jobCommand:i.t(`activity:feed.phaseCommand`,{phase:t.phase,state:t.state}),timestamp:t.timestamp,summary:`Phase ${t.phase} is ${t.state}`,costUsd:null};a(t=>g([e,...t]))}}},[]);return(0,h.useLayoutEffect)(()=>(f(`activity`,_),()=>p(`activity`)),[_,f,p]),{items:n,loading:o,hasMore:c,loadMore:m}}var v=n();function y(e,t){let n=Date.now()-new Date(e).getTime(),r=Math.floor(n/1e3);if(r<60)return t(`feed.relativeTime.seconds`,{value:r});let i=Math.floor(r/60);if(i<60)return t(`feed.relativeTime.minutes`,{value:i});let a=Math.floor(i/60);return a<24?t(`feed.relativeTime.hours`,{value:a}):t(`feed.relativeTime.days`,{value:Math.floor(a/24)})}function b({type:e}){switch(e){case`job_completed`:return(0,v.jsx)(u,{className:`w-4 h-4 text-green-500 flex-shrink-0`});case`job_failed`:return(0,v.jsx)(o,{className:`w-4 h-4 text-red-500 flex-shrink-0`});case`job_canceled`:return(0,v.jsx)(m,{className:`w-4 h-4 text-muted-foreground flex-shrink-0`});default:return(0,v.jsx)(c,{className:`w-4 h-4 text-blue-500 flex-shrink-0`})}}function x(e,t){switch(e){case`job_completed`:return t(`common:status.completed`);case`job_failed`:return t(`common:status.failed`);case`job_canceled`:return t(`common:status.canceled`);default:return t(`feed.typeStarted`)}}function S(e){switch(e){case`job_completed`:return`text-green-500`;case`job_failed`:return`text-red-500`;case`job_canceled`:return`text-muted-foreground`;default:return`text-blue-500`}}function C(){let{t:e}=d(`activity`),{activeProjectId:n}=s(),{items:r,loading:i,hasMore:o,loadMore:c}=_({activeProjectId:n}),l=(0,h.useRef)(null);return(0,h.useEffect)(()=>{let e=l.current;if(!e)return;let t=new IntersectionObserver(e=>{e[0].isIntersecting&&o&&!i&&c()},{threshold:.1});return t.observe(e),()=>t.disconnect()},[o,i,c]),(0,v.jsxs)(`div`,{className:`flex flex-col h-full overflow-hidden`,children:[(0,v.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 border-b border-border bg-background/50`,children:[(0,v.jsx)(a,{className:`w-4 h-4 text-muted-foreground`}),(0,v.jsx)(`h1`,{className:`text-sm font-medium`,children:e(`feed.title`)})]}),(0,v.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[i&&r.length===0?(0,v.jsx)(`div`,{className:`flex items-center justify-center h-32`,children:(0,v.jsx)(t,{className:`w-4 h-4 animate-spin text-muted-foreground`})}):r.length===0?(0,v.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-48 gap-2 text-muted-foreground`,children:[(0,v.jsx)(a,{className:`w-8 h-8 opacity-40`}),(0,v.jsx)(`p`,{className:`text-sm`,children:e(`feed.emptyTitle`)}),(0,v.jsx)(`p`,{className:`text-xs opacity-70`,children:e(`feed.emptyHint`)})]}):(0,v.jsx)(`ul`,{className:`divide-y divide-border/50`,children:r.map(t=>(0,v.jsxs)(`li`,{className:`flex items-center gap-3 px-4 py-2.5 hover:bg-accent/30 transition-colors`,children:[(0,v.jsx)(b,{type:t.type}),(0,v.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,v.jsx)(`p`,{className:`text-xs text-foreground truncate`,title:t.jobCommand,children:t.jobCommand}),(0,v.jsxs)(`div`,{className:`flex items-center gap-2 mt-0.5`,children:[(0,v.jsx)(`span`,{className:`text-xs font-medium ${S(t.type)}`,children:x(t.type,e)}),t.costUsd!=null&&(0,v.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`$`,t.costUsd.toFixed(4)]})]})]}),(0,v.jsx)(`span`,{className:`text-xs text-muted-foreground flex-shrink-0 tabular-nums`,children:y(t.timestamp,e)})]},`${t.type}:${t.jobId}`))}),(0,v.jsx)(`div`,{ref:l,className:`h-1`}),i&&r.length>0&&(0,v.jsx)(`div`,{className:`flex justify-center py-3`,children:(0,v.jsx)(t,{className:`w-4 h-4 animate-spin text-muted-foreground`})}),!o&&r.length>0&&(0,v.jsx)(`p`,{className:`text-center text-xs text-muted-foreground py-3`,children:e(`feed.allLoaded`)})]})]})}export{C as default};
|