codex-agent-view 0.4.1 → 0.4.3
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/.codex-plugin/plugin.json +2 -2
- package/README.ko.md +32 -24
- package/README.md +31 -23
- package/bin/codex-agent-view.mjs +63 -1
- package/package.json +1 -1
- package/public/app.js +246 -46
- package/public/index.html +16 -16
- package/public/styles.css +49 -8
- package/scripts/auto-start-monitor.mjs +10 -2
- package/skills/show-agents/SKILL.md +28 -16
- package/skills/show-agents/agents/openai.yaml +1 -1
- package/src/runtime/config.mjs +131 -0
- package/src/runtime/server.mjs +14 -5
package/public/app.js
CHANGED
|
@@ -17,6 +17,42 @@ const STATUS_ORDER = Object.freeze({
|
|
|
17
17
|
completed: 3,
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
const ACTIVITY_LABELS = Object.freeze({
|
|
21
|
+
session_started: "관찰 시작",
|
|
22
|
+
session_ended: "관찰 종료",
|
|
23
|
+
turn_started: "부모 작업 시작",
|
|
24
|
+
turn_stopped: "부모 작업 응답 완료",
|
|
25
|
+
subagent_started: "작업 에이전트 시작",
|
|
26
|
+
subagent_stopped: "작업 에이전트 완료",
|
|
27
|
+
tool_started: "도구 작업",
|
|
28
|
+
tool_completed: "도구 작업",
|
|
29
|
+
permission_requested: "사용자 승인 요청",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const TOOL_LABELS = Object.freeze({
|
|
33
|
+
apply_patch: "파일 수정",
|
|
34
|
+
bash: "터미널 작업",
|
|
35
|
+
collaborationfollowup_task: "에이전트 후속 작업 요청",
|
|
36
|
+
collaborationinterrupt_agent: "에이전트 작업 중단 요청",
|
|
37
|
+
collaborationlist_agents: "에이전트 상태 확인",
|
|
38
|
+
collaborationsend_message: "에이전트에게 메시지 전달",
|
|
39
|
+
collaborationspawn_agent: "작업 에이전트 시작",
|
|
40
|
+
collaborationwait_agent: "에이전트 응답 대기",
|
|
41
|
+
exec_command: "터미널 작업",
|
|
42
|
+
request_user_input: "사용자 입력 요청",
|
|
43
|
+
wait: "작업 완료 대기",
|
|
44
|
+
write_stdin: "터미널 입력",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const AGENT_ROLE_LABELS = Object.freeze({
|
|
48
|
+
explorer: "조사 담당",
|
|
49
|
+
reviewer: "검토 담당",
|
|
50
|
+
worker: "작업 담당",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const HIDDEN_AGENT_ROLES = new Set(["", "default", "unknown"]);
|
|
54
|
+
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
55
|
+
|
|
20
56
|
function consumeAccessToken() {
|
|
21
57
|
const fragment = new URLSearchParams(window.location.hash.slice(1));
|
|
22
58
|
const fragmentToken = fragment.get("token")?.trim() || "";
|
|
@@ -81,6 +117,59 @@ function safeString(value, fallback) {
|
|
|
81
117
|
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
82
118
|
}
|
|
83
119
|
|
|
120
|
+
function sanitizedFallbackLabel(value, fallback) {
|
|
121
|
+
const normalized = safeString(value, "")
|
|
122
|
+
.replace(CONTROL_CHARACTERS, " ")
|
|
123
|
+
.replace(/([a-z\d])([A-Z])/g, "$1 $2")
|
|
124
|
+
.replace(/[_./:-]+/g, " ")
|
|
125
|
+
.replace(/\s+/g, " ")
|
|
126
|
+
.trim()
|
|
127
|
+
.slice(0, 48);
|
|
128
|
+
|
|
129
|
+
if (!normalized) {
|
|
130
|
+
return fallback;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return normalized.replace(/\b[a-z]/g, (character) => character.toUpperCase());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function normalizedLookupKey(value) {
|
|
137
|
+
return safeString(value, "").replace(/[^a-zA-Z0-9_]/g, "").toLocaleLowerCase();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function formatAgentRole(agentType) {
|
|
141
|
+
const key = normalizedLookupKey(agentType);
|
|
142
|
+
if (HIDDEN_AGENT_ROLES.has(key)) {
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
return AGENT_ROLE_LABELS[key] || sanitizedFallbackLabel(agentType, "");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function formatToolLabel(toolName) {
|
|
149
|
+
const key = normalizedLookupKey(toolName);
|
|
150
|
+
return TOOL_LABELS[key] || `도구 · ${sanitizedFallbackLabel(toolName, "이름 미상")}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function formatActivityLabel(activity) {
|
|
154
|
+
if (activity.eventName === "subagent_started" && activity.agentOrdinal !== null) {
|
|
155
|
+
return `작업 에이전트 ${activity.agentOrdinal} 시작`;
|
|
156
|
+
}
|
|
157
|
+
if (activity.eventName === "subagent_stopped" && activity.agentOrdinal !== null) {
|
|
158
|
+
return `작업 에이전트 ${activity.agentOrdinal} 완료`;
|
|
159
|
+
}
|
|
160
|
+
if (activity.eventName === "permission_requested" && activity.toolName) {
|
|
161
|
+
return `${formatToolLabel(activity.toolName)} 승인 요청`;
|
|
162
|
+
}
|
|
163
|
+
if (
|
|
164
|
+
(activity.eventName === "tool_started" || activity.eventName === "tool_completed") &&
|
|
165
|
+
activity.toolName
|
|
166
|
+
) {
|
|
167
|
+
return formatToolLabel(activity.toolName);
|
|
168
|
+
}
|
|
169
|
+
return ACTIVITY_LABELS[activity.eventName] ||
|
|
170
|
+
`활동 · ${sanitizedFallbackLabel(activity.eventName, "알 수 없음")}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
84
173
|
function safeTimestamp(value) {
|
|
85
174
|
return Number.isFinite(value) && value >= 0 && value <= 8_640_000_000_000_000
|
|
86
175
|
? value
|
|
@@ -122,16 +211,72 @@ function normalizeAgent(value, index) {
|
|
|
122
211
|
};
|
|
123
212
|
}
|
|
124
213
|
|
|
214
|
+
function agentOrderTimestamp(agent) {
|
|
215
|
+
return agent.startedAtMs ?? agent.stoppedAtMs ?? agent.lastActivityAtMs ?? Number.MAX_SAFE_INTEGER;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function assignStableAgentOrdinals(agents) {
|
|
219
|
+
const ordinalByAgentId = new Map(
|
|
220
|
+
[...agents]
|
|
221
|
+
.sort((left, right) => (
|
|
222
|
+
agentOrderTimestamp(left) - agentOrderTimestamp(right) ||
|
|
223
|
+
left.agentId.localeCompare(right.agentId)
|
|
224
|
+
))
|
|
225
|
+
.map((agent, index) => [agent.agentId, index + 1]),
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return agents.map((agent) => ({
|
|
229
|
+
...agent,
|
|
230
|
+
ordinal: ordinalByAgentId.get(agent.agentId),
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
|
|
125
234
|
function normalizeActivity(value) {
|
|
126
235
|
const activity = isRecord(value) ? value : {};
|
|
127
236
|
return {
|
|
128
237
|
eventName: safeString(activity.type, "unknown_event"),
|
|
238
|
+
agentId: safeString(activity.agent_id, ""),
|
|
239
|
+
agentOrdinal: null,
|
|
129
240
|
toolName: safeString(activity.tool_name, ""),
|
|
241
|
+
toolUseId: safeString(activity.tool_use_id, ""),
|
|
130
242
|
status: normalizeCoreStatus(activity.status),
|
|
131
243
|
occurredAtMs: safeTimestamp(activity.received_at_ms),
|
|
132
244
|
};
|
|
133
245
|
}
|
|
134
246
|
|
|
247
|
+
function isToolLifecycle(activity) {
|
|
248
|
+
return activity.eventName === "tool_started" || activity.eventName === "tool_completed";
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function shouldReplaceToolActivity(candidate, current) {
|
|
252
|
+
const candidateTime = candidate.occurredAtMs ?? -1;
|
|
253
|
+
const currentTime = current.occurredAtMs ?? -1;
|
|
254
|
+
if (candidateTime !== currentTime) {
|
|
255
|
+
return candidateTime > currentTime;
|
|
256
|
+
}
|
|
257
|
+
return candidate.eventName === "tool_completed" && current.eventName !== "tool_completed";
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function collapseToolActivities(activities) {
|
|
261
|
+
const latestByToolUseId = new Map();
|
|
262
|
+
|
|
263
|
+
for (const activity of activities) {
|
|
264
|
+
if (!activity.toolUseId || !isToolLifecycle(activity)) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const current = latestByToolUseId.get(activity.toolUseId);
|
|
268
|
+
if (!current || shouldReplaceToolActivity(activity, current)) {
|
|
269
|
+
latestByToolUseId.set(activity.toolUseId, activity);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return activities.filter((activity) => (
|
|
274
|
+
!activity.toolUseId ||
|
|
275
|
+
!isToolLifecycle(activity) ||
|
|
276
|
+
latestByToolUseId.get(activity.toolUseId) === activity
|
|
277
|
+
));
|
|
278
|
+
}
|
|
279
|
+
|
|
135
280
|
function normalizeDiagnostic(value) {
|
|
136
281
|
const diagnostic = isRecord(value) ? value : {};
|
|
137
282
|
return {
|
|
@@ -160,10 +305,18 @@ function deriveSessionStatus(session, agents, recentActivities) {
|
|
|
160
305
|
function normalizeSession(value, index) {
|
|
161
306
|
const session = isRecord(value) ? value : {};
|
|
162
307
|
const agents = Array.isArray(session.agents)
|
|
163
|
-
? session.agents.map(normalizeAgent)
|
|
308
|
+
? assignStableAgentOrdinals(session.agents.map(normalizeAgent))
|
|
164
309
|
: [];
|
|
310
|
+
const agentOrdinalById = new Map(
|
|
311
|
+
agents.map((agent) => [agent.agentId, agent.ordinal]),
|
|
312
|
+
);
|
|
165
313
|
const recentActivities = Array.isArray(session.recent_activities)
|
|
166
|
-
? session.recent_activities.map(normalizeActivity)
|
|
314
|
+
? collapseToolActivities(session.recent_activities.map(normalizeActivity)).map(
|
|
315
|
+
(activity) => ({
|
|
316
|
+
...activity,
|
|
317
|
+
agentOrdinal: agentOrdinalById.get(activity.agentId) ?? null,
|
|
318
|
+
}),
|
|
319
|
+
)
|
|
167
320
|
: [];
|
|
168
321
|
|
|
169
322
|
return {
|
|
@@ -203,6 +356,11 @@ function compareAgents(left, right) {
|
|
|
203
356
|
return statusDifference || compareByActivity(left, right);
|
|
204
357
|
}
|
|
205
358
|
|
|
359
|
+
function compareSessions(left, right) {
|
|
360
|
+
const statusDifference = STATUS_ORDER[left.status] - STATUS_ORDER[right.status];
|
|
361
|
+
return statusDifference || compareByActivity(left, right);
|
|
362
|
+
}
|
|
363
|
+
|
|
206
364
|
function formatDateTime(timestampMs) {
|
|
207
365
|
if (timestampMs === null) {
|
|
208
366
|
return "시간 정보 없음";
|
|
@@ -293,21 +451,53 @@ function createTime(timestampMs, prefix) {
|
|
|
293
451
|
return wrapper;
|
|
294
452
|
}
|
|
295
453
|
|
|
454
|
+
function createTechnicalDetails(rows) {
|
|
455
|
+
const details = document.createElement("details");
|
|
456
|
+
details.className = "technical-details";
|
|
457
|
+
|
|
458
|
+
const summary = document.createElement("summary");
|
|
459
|
+
summary.textContent = "기술 정보";
|
|
460
|
+
const list = document.createElement("dl");
|
|
461
|
+
|
|
462
|
+
for (const [label, value] of rows) {
|
|
463
|
+
if (!value) {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
const term = document.createElement("dt");
|
|
467
|
+
term.textContent = label;
|
|
468
|
+
const description = document.createElement("dd");
|
|
469
|
+
const code = document.createElement("code");
|
|
470
|
+
code.textContent = value;
|
|
471
|
+
description.append(code);
|
|
472
|
+
list.append(term, description);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
details.append(summary, list);
|
|
476
|
+
return details;
|
|
477
|
+
}
|
|
478
|
+
|
|
296
479
|
function createAgentItem(agent) {
|
|
297
480
|
const item = document.createElement("li");
|
|
298
481
|
item.className = "agent-item";
|
|
482
|
+
item.dataset.status = agent.status;
|
|
299
483
|
|
|
300
484
|
const heading = document.createElement("div");
|
|
301
485
|
heading.className = "agent-heading";
|
|
302
486
|
|
|
303
487
|
const identity = document.createElement("div");
|
|
304
488
|
identity.className = "agent-identity";
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
489
|
+
const name = document.createElement("strong");
|
|
490
|
+
name.className = "agent-name";
|
|
491
|
+
name.textContent = `작업 에이전트 ${agent.ordinal}`;
|
|
492
|
+
identity.append(name);
|
|
493
|
+
|
|
494
|
+
const roleLabel = formatAgentRole(agent.agentType);
|
|
495
|
+
if (roleLabel) {
|
|
496
|
+
const role = document.createElement("span");
|
|
497
|
+
role.className = "agent-role";
|
|
498
|
+
role.textContent = roleLabel;
|
|
499
|
+
identity.append(role);
|
|
500
|
+
}
|
|
311
501
|
heading.append(identity, createStatusBadge(agent.status));
|
|
312
502
|
|
|
313
503
|
const metadata = document.createElement("div");
|
|
@@ -317,7 +507,12 @@ function createAgentItem(agent) {
|
|
|
317
507
|
document.createTextNode(` · ${formatDuration(agent.startedAtMs, agent.stoppedAtMs)}`),
|
|
318
508
|
);
|
|
319
509
|
|
|
320
|
-
|
|
510
|
+
const technicalRows = [["에이전트 ID", agent.agentId]];
|
|
511
|
+
if (roleLabel) {
|
|
512
|
+
technicalRows.push(["원본 역할", agent.agentType]);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
item.append(heading, metadata, createTechnicalDetails(technicalRows));
|
|
321
516
|
return item;
|
|
322
517
|
}
|
|
323
518
|
|
|
@@ -334,20 +529,19 @@ function createActivityItem(activity) {
|
|
|
334
529
|
const title = document.createElement("div");
|
|
335
530
|
title.className = "activity-title";
|
|
336
531
|
const eventName = document.createElement("strong");
|
|
337
|
-
eventName.textContent = activity
|
|
532
|
+
eventName.textContent = formatActivityLabel(activity);
|
|
338
533
|
title.append(eventName);
|
|
339
534
|
|
|
340
|
-
if (activity.toolName) {
|
|
341
|
-
const tool = document.createElement("code");
|
|
342
|
-
tool.textContent = activity.toolName;
|
|
343
|
-
title.append(tool);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
535
|
const metadata = document.createElement("div");
|
|
347
536
|
metadata.className = "activity-metadata";
|
|
348
537
|
metadata.append(createStatusBadge(activity.status), createTime(activity.occurredAtMs, ""));
|
|
349
538
|
|
|
350
|
-
|
|
539
|
+
const technicalRows = [["원본 이벤트", activity.eventName]];
|
|
540
|
+
if (activity.toolName) {
|
|
541
|
+
technicalRows.push(["원본 도구", activity.toolName]);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
content.append(title, metadata, createTechnicalDetails(technicalRows));
|
|
351
545
|
item.append(marker, content);
|
|
352
546
|
return item;
|
|
353
547
|
}
|
|
@@ -363,6 +557,7 @@ function createSessionCard(session) {
|
|
|
363
557
|
const listItem = document.createElement("li");
|
|
364
558
|
const article = document.createElement("article");
|
|
365
559
|
article.className = "session-card";
|
|
560
|
+
article.dataset.status = session.status;
|
|
366
561
|
|
|
367
562
|
const cardHeader = document.createElement("header");
|
|
368
563
|
cardHeader.className = "session-header";
|
|
@@ -371,13 +566,14 @@ function createSessionCard(session) {
|
|
|
371
566
|
identity.className = "session-identity";
|
|
372
567
|
const eyebrow = document.createElement("span");
|
|
373
568
|
eyebrow.className = "session-kind";
|
|
374
|
-
eyebrow.
|
|
375
|
-
const id = document.createElement("code");
|
|
376
|
-
id.textContent = session.sessionId;
|
|
377
|
-
eyebrow.append(id);
|
|
569
|
+
eyebrow.textContent = "부모 작업";
|
|
378
570
|
const title = document.createElement("h3");
|
|
379
571
|
title.textContent = session.workspaceLabel || "프로젝트 정보 없음";
|
|
380
|
-
identity.append(
|
|
572
|
+
identity.append(
|
|
573
|
+
eyebrow,
|
|
574
|
+
title,
|
|
575
|
+
createTechnicalDetails([["세션 ID", session.sessionId]]),
|
|
576
|
+
);
|
|
381
577
|
|
|
382
578
|
const sessionState = document.createElement("div");
|
|
383
579
|
sessionState.className = "session-state";
|
|
@@ -394,7 +590,7 @@ function createSessionCard(session) {
|
|
|
394
590
|
const agentPanel = document.createElement("section");
|
|
395
591
|
agentPanel.className = "session-panel";
|
|
396
592
|
const agentTitle = document.createElement("h4");
|
|
397
|
-
agentTitle.textContent =
|
|
593
|
+
agentTitle.textContent = `작업 에이전트 · ${session.agents.length}`;
|
|
398
594
|
agentPanel.append(agentTitle);
|
|
399
595
|
|
|
400
596
|
if (session.agents.length) {
|
|
@@ -405,7 +601,7 @@ function createSessionCard(session) {
|
|
|
405
601
|
});
|
|
406
602
|
agentPanel.append(list);
|
|
407
603
|
} else {
|
|
408
|
-
agentPanel.append(createEmptyPanel("이
|
|
604
|
+
agentPanel.append(createEmptyPanel("이 부모 작업에서 관찰된 작업 에이전트가 없습니다."));
|
|
409
605
|
}
|
|
410
606
|
|
|
411
607
|
const activityPanel = document.createElement("section");
|
|
@@ -422,7 +618,7 @@ function createSessionCard(session) {
|
|
|
422
618
|
});
|
|
423
619
|
activityPanel.append(list);
|
|
424
620
|
} else {
|
|
425
|
-
activityPanel.append(createEmptyPanel("표시할
|
|
621
|
+
activityPanel.append(createEmptyPanel("표시할 최근 활동이 없습니다."));
|
|
426
622
|
}
|
|
427
623
|
|
|
428
624
|
panels.append(agentPanel, activityPanel);
|
|
@@ -440,7 +636,11 @@ function sessionMatchesQuery(session, query) {
|
|
|
440
636
|
session.sessionId,
|
|
441
637
|
session.workspaceLabel,
|
|
442
638
|
session.status,
|
|
443
|
-
...session.agents.flatMap((agent) => [
|
|
639
|
+
...session.agents.flatMap((agent) => [
|
|
640
|
+
agent.agentId,
|
|
641
|
+
formatAgentRole(agent.agentType),
|
|
642
|
+
agent.status,
|
|
643
|
+
]),
|
|
444
644
|
...session.recentActivities.flatMap((activity) => [
|
|
445
645
|
activity.eventName,
|
|
446
646
|
activity.toolName,
|
|
@@ -466,7 +666,7 @@ function filteredSessions() {
|
|
|
466
666
|
return [...viewState.sessions]
|
|
467
667
|
.filter((session) => sessionMatchesQuery(session, query))
|
|
468
668
|
.filter((session) => sessionMatchesStatus(session, status))
|
|
469
|
-
.sort(
|
|
669
|
+
.sort(compareSessions);
|
|
470
670
|
}
|
|
471
671
|
|
|
472
672
|
function renderMetrics() {
|
|
@@ -481,7 +681,7 @@ function renderMetrics() {
|
|
|
481
681
|
elements.metricWaiting.textContent = String(waitingCount);
|
|
482
682
|
elements.metricCompleted.textContent = String(countStatus("completed"));
|
|
483
683
|
elements.lastUpdated.textContent = !viewState.updatedAtMs
|
|
484
|
-
? "수신된
|
|
684
|
+
? "수신된 활동 없음"
|
|
485
685
|
: formatDateTime(viewState.updatedAtMs);
|
|
486
686
|
}
|
|
487
687
|
|
|
@@ -513,11 +713,11 @@ function setEmptyObservationMessage() {
|
|
|
513
713
|
const copy = document.createElement("span");
|
|
514
714
|
|
|
515
715
|
if (viewState.diagnostics.length) {
|
|
516
|
-
heading.textContent = "표시 가능한
|
|
517
|
-
copy.textContent =
|
|
716
|
+
heading.textContent = "표시 가능한 부모 작업이 없습니다.";
|
|
717
|
+
copy.textContent = `로컬 모니터가 활동 정보 ${viewState.diagnostics.length}건을 받았지만 표시 가능한 부모 작업으로 적용하지 못했습니다.`;
|
|
518
718
|
} else {
|
|
519
|
-
heading.textContent = "이 관찰
|
|
520
|
-
copy.textContent = "로컬
|
|
719
|
+
heading.textContent = "이 관찰 화면에서 수신된 작업 활동이 없습니다.";
|
|
720
|
+
copy.textContent = "로컬 모니터 연결은 정상입니다. 이 결과만으로 Codex에 실행 중인 부모 작업이나 작업 에이전트가 없다고 판단할 수 없습니다.";
|
|
521
721
|
}
|
|
522
722
|
|
|
523
723
|
const guidance = document.createElement("div");
|
|
@@ -528,13 +728,13 @@ function setEmptyObservationMessage() {
|
|
|
528
728
|
|
|
529
729
|
const automaticTracking = document.createElement("p");
|
|
530
730
|
automaticTracking.className = "automatic-tracking";
|
|
531
|
-
automaticTracking.textContent = "
|
|
731
|
+
automaticTracking.textContent = "작업 ID를 입력하거나 작업별로 등록할 필요가 없습니다. 신뢰한 hook의 작업 활동이 이 목록에 자동으로 추가됩니다.";
|
|
532
732
|
|
|
533
733
|
const steps = document.createElement("ol");
|
|
534
734
|
for (const step of [
|
|
535
|
-
"
|
|
536
|
-
"새
|
|
537
|
-
"
|
|
735
|
+
"플러그인을 설치한 뒤 공식 Codex 앱을 완전히 재시작했는지 확인합니다.",
|
|
736
|
+
"새 작업에서 표시되는 Codex Agent View hook 명령을 검토하고 직접 신뢰합니다.",
|
|
737
|
+
"신뢰 설정 후 새 작업을 시작해 작업 에이전트를 실행합니다. 새 활동은 이 목록에 자동으로 추가됩니다.",
|
|
538
738
|
]) {
|
|
539
739
|
const item = document.createElement("li");
|
|
540
740
|
item.textContent = step;
|
|
@@ -543,7 +743,7 @@ function setEmptyObservationMessage() {
|
|
|
543
743
|
|
|
544
744
|
const boundary = document.createElement("p");
|
|
545
745
|
boundary.className = "observation-boundary";
|
|
546
|
-
boundary.textContent = "관찰
|
|
746
|
+
boundary.textContent = "관찰 화면은 첫 번째로 신뢰한 hook을 받은 시점부터 시작합니다. 그 전에 이미 지나간 활동과 로컬 상태 수집이 중단된 동안의 활동은 재생되지 않으며, 수집이 다시 시작되면 새 관찰 화면이 열립니다.";
|
|
547
747
|
|
|
548
748
|
guidance.append(guidanceTitle, automaticTracking, steps, boundary);
|
|
549
749
|
|
|
@@ -551,7 +751,7 @@ function setEmptyObservationMessage() {
|
|
|
551
751
|
const diagnostics = document.createElement("details");
|
|
552
752
|
diagnostics.className = "diagnostic-details";
|
|
553
753
|
const summary = document.createElement("summary");
|
|
554
|
-
summary.textContent = `검증
|
|
754
|
+
summary.textContent = `검증 정보 ${viewState.diagnostics.length}건`;
|
|
555
755
|
const codes = document.createElement("ul");
|
|
556
756
|
const diagnosticCounts = new Map();
|
|
557
757
|
for (const { code } of viewState.diagnostics) {
|
|
@@ -585,11 +785,11 @@ function renderSessions() {
|
|
|
585
785
|
|
|
586
786
|
elements.resultsSummary.textContent = hasFilters
|
|
587
787
|
? `전체 ${viewState.sessions.length}개 중 ${visibleSessions.length}개 표시`
|
|
588
|
-
: `${viewState.sessions.length}개 부모
|
|
788
|
+
: `${viewState.sessions.length}개 부모 작업`;
|
|
589
789
|
|
|
590
790
|
if (!viewState.hasLoaded) {
|
|
591
791
|
elements.sessionList.hidden = true;
|
|
592
|
-
setStateMessage("loading", "상태를 불러오는 중입니다.", "로컬
|
|
792
|
+
setStateMessage("loading", "상태를 불러오는 중입니다.", "로컬 모니터에 연결하고 있습니다.");
|
|
593
793
|
return;
|
|
594
794
|
}
|
|
595
795
|
|
|
@@ -604,7 +804,7 @@ function renderSessions() {
|
|
|
604
804
|
"error",
|
|
605
805
|
viewState.canRetry
|
|
606
806
|
? "로컬 상태 연결이 끊겨 다시 시도 중입니다."
|
|
607
|
-
: "이
|
|
807
|
+
: "이 실시간 화면을 인증할 수 없습니다.",
|
|
608
808
|
description,
|
|
609
809
|
viewState.canRetry,
|
|
610
810
|
);
|
|
@@ -641,7 +841,7 @@ function setConnectionStatus(status, labelOverride = "") {
|
|
|
641
841
|
elements.connectionStatus.dataset.status = status;
|
|
642
842
|
const labels = {
|
|
643
843
|
connecting: "로컬 상태 연결 중",
|
|
644
|
-
connected: "로컬
|
|
844
|
+
connected: "로컬 모니터 연결됨",
|
|
645
845
|
error: "연결 끊김 · 재시도 중",
|
|
646
846
|
};
|
|
647
847
|
elements.connectionLabel.textContent = labelOverride || labels[status];
|
|
@@ -656,8 +856,8 @@ async function refreshState() {
|
|
|
656
856
|
viewState.hasLoaded = true;
|
|
657
857
|
viewState.canRetry = false;
|
|
658
858
|
viewState.authenticationFailed = true;
|
|
659
|
-
viewState.errorMessage = "이 탭에는 접근
|
|
660
|
-
setConnectionStatus("error", "
|
|
859
|
+
viewState.errorMessage = "이 탭에는 접근 토큰이 없습니다. Codex 앱에서 Codex Agent View의 실시간 화면 열기를 다시 요청하세요.";
|
|
860
|
+
setConnectionStatus("error", "실시간 화면 인증 필요");
|
|
661
861
|
render();
|
|
662
862
|
return;
|
|
663
863
|
}
|
|
@@ -682,8 +882,8 @@ async function refreshState() {
|
|
|
682
882
|
viewState.hasLoaded = true;
|
|
683
883
|
viewState.canRetry = false;
|
|
684
884
|
viewState.authenticationFailed = true;
|
|
685
|
-
viewState.errorMessage = "이
|
|
686
|
-
setConnectionStatus("error", "
|
|
885
|
+
viewState.errorMessage = "이 실시간 화면의 인증이 더 이상 유효하지 않습니다. Codex 앱에서 Codex Agent View의 실시간 화면 열기를 다시 요청하세요.";
|
|
886
|
+
setConnectionStatus("error", "실시간 화면 인증 필요");
|
|
687
887
|
return;
|
|
688
888
|
}
|
|
689
889
|
|
package/public/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="color-scheme" content="light dark">
|
|
7
7
|
<meta
|
|
8
8
|
name="description"
|
|
9
|
-
content="공식 Codex 앱의 부모
|
|
9
|
+
content="공식 Codex 앱의 부모 작업과 작업 에이전트 상태를 로컬에서 읽기 전용으로 확인합니다."
|
|
10
10
|
>
|
|
11
11
|
<title>Codex Agent View</title>
|
|
12
12
|
<link rel="stylesheet" href="/assets/styles.css">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<span class="brand-mark" aria-hidden="true">CA</span>
|
|
22
22
|
<span>
|
|
23
23
|
<strong>Codex Agent View</strong>
|
|
24
|
-
<small
|
|
24
|
+
<small>로컬 읽기 전용 모니터</small>
|
|
25
25
|
</span>
|
|
26
26
|
</a>
|
|
27
27
|
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
<main id="main-content" tabindex="-1">
|
|
42
42
|
<section class="hero" aria-labelledby="page-title">
|
|
43
43
|
<div>
|
|
44
|
-
<p class="eyebrow"
|
|
44
|
+
<p class="eyebrow">로컬 · 읽기 전용</p>
|
|
45
45
|
<h1 id="page-title">작업 흐름을 한눈에</h1>
|
|
46
46
|
<p class="hero-copy">
|
|
47
|
-
공식 Codex 앱은 그대로 두고, 부모
|
|
47
|
+
공식 Codex 앱은 그대로 두고, 부모 작업과 작업 에이전트의 현재 상태만
|
|
48
48
|
로컬에서 확인합니다.
|
|
49
49
|
</p>
|
|
50
50
|
</div>
|
|
@@ -63,32 +63,32 @@
|
|
|
63
63
|
|
|
64
64
|
<section class="metrics" aria-label="작업 상태 요약">
|
|
65
65
|
<article class="metric-card">
|
|
66
|
-
<p>부모
|
|
66
|
+
<p>부모 작업</p>
|
|
67
67
|
<strong id="metric-sessions">0</strong>
|
|
68
68
|
<span>현재 관찰 중</span>
|
|
69
69
|
</article>
|
|
70
70
|
<article class="metric-card metric-running">
|
|
71
|
-
<p>실행 중
|
|
71
|
+
<p>실행 중 에이전트</p>
|
|
72
72
|
<strong id="metric-running">0</strong>
|
|
73
73
|
<span>작업 수행 중</span>
|
|
74
74
|
</article>
|
|
75
75
|
<article class="metric-card metric-waiting">
|
|
76
76
|
<p>대기 상태</p>
|
|
77
77
|
<strong id="metric-waiting">0</strong>
|
|
78
|
-
<span>사용자 또는 다음 작업 대기</span>
|
|
78
|
+
<span>사용자 응답 또는 다음 작업 대기</span>
|
|
79
79
|
</article>
|
|
80
80
|
<article class="metric-card metric-completed">
|
|
81
|
-
<p>완료
|
|
81
|
+
<p>완료 에이전트</p>
|
|
82
82
|
<strong id="metric-completed">0</strong>
|
|
83
|
-
<span>현재
|
|
83
|
+
<span>현재 화면 기준</span>
|
|
84
84
|
</article>
|
|
85
85
|
</section>
|
|
86
86
|
|
|
87
87
|
<section class="workspace" aria-labelledby="sessions-heading">
|
|
88
88
|
<div class="section-heading">
|
|
89
89
|
<div>
|
|
90
|
-
<p class="eyebrow"
|
|
91
|
-
<h2 id="sessions-heading">부모
|
|
90
|
+
<p class="eyebrow">실시간 작업</p>
|
|
91
|
+
<h2 id="sessions-heading">부모 작업과 작업 에이전트</h2>
|
|
92
92
|
</div>
|
|
93
93
|
<p id="results-summary" class="results-summary" role="status" aria-live="polite">
|
|
94
94
|
상태를 불러오는 중입니다.
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
<form
|
|
99
99
|
class="toolbar"
|
|
100
100
|
role="search"
|
|
101
|
-
aria-label="자동 수신된
|
|
101
|
+
aria-label="자동 수신된 부모 작업과 작업 에이전트 목록 필터"
|
|
102
102
|
hidden
|
|
103
103
|
>
|
|
104
104
|
<div class="field field-search">
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
id="session-search"
|
|
108
108
|
name="query"
|
|
109
109
|
type="search"
|
|
110
|
-
placeholder="
|
|
110
|
+
placeholder="부모 작업·작업 에이전트 목록에서 찾기"
|
|
111
111
|
autocomplete="off"
|
|
112
112
|
spellcheck="false"
|
|
113
113
|
>
|
|
@@ -129,13 +129,13 @@
|
|
|
129
129
|
<span>Codex 앱의 로컬 상태에 연결하고 있습니다.</span>
|
|
130
130
|
</div>
|
|
131
131
|
|
|
132
|
-
<ul id="session-list" class="session-list" aria-label="Codex 부모
|
|
132
|
+
<ul id="session-list" class="session-list" aria-label="Codex 부모 작업 목록" hidden></ul>
|
|
133
133
|
</section>
|
|
134
134
|
</main>
|
|
135
135
|
|
|
136
136
|
<footer>
|
|
137
|
-
<p>이 화면은
|
|
138
|
-
<p>데이터는 이 기기의 로컬
|
|
137
|
+
<p>이 화면은 사용자 요청 내용과 도구 입력 내용을 표시하지 않습니다.</p>
|
|
138
|
+
<p>데이터는 이 기기의 로컬 모니터에서만 읽습니다.</p>
|
|
139
139
|
</footer>
|
|
140
140
|
</div>
|
|
141
141
|
</body>
|