ework-web 0.10.79 → 0.10.81
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/package.json +1 -1
- package/src/coordination.ts +24 -0
- package/src/index.ts +18 -4
- package/src/views/home.ts +1 -1
- package/src/views/issues.ts +1 -1
- package/src/views/projectAi.ts +1 -1
- package/src/views/projectModel.ts +1 -1
- package/src/views/sessionLog.ts +1 -1
- package/src/views/settings.ts +1 -1
- package/src/views/webhookDeliveries.ts +1 -1
- package/src/views/webhooks.ts +1 -1
package/package.json
CHANGED
package/src/coordination.ts
CHANGED
|
@@ -75,6 +75,30 @@ export interface SessionDaemonInfo {
|
|
|
75
75
|
endpoint: string;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
export interface SessionUidInfo {
|
|
79
|
+
daemonId: number;
|
|
80
|
+
opencodeSessionId: string | null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function resolveSessionUid(uid: string): Promise<SessionUidInfo | null> {
|
|
84
|
+
try {
|
|
85
|
+
const rows = await getDB().all<{ daemon_id: number; opencode_session_id: string | null }>(
|
|
86
|
+
`SELECT i.owner_daemon_id AS daemon_id, s.opencode_session_id
|
|
87
|
+
FROM {{d_op_sessions}} s
|
|
88
|
+
JOIN {{d_issues}} i ON i.uid = s.issue_id
|
|
89
|
+
WHERE s.uid = ?
|
|
90
|
+
LIMIT 1`,
|
|
91
|
+
[uid],
|
|
92
|
+
);
|
|
93
|
+
const r = rows[0];
|
|
94
|
+
if (!r || !r.daemon_id) return null;
|
|
95
|
+
return { daemonId: r.daemon_id, opencodeSessionId: r.opencode_session_id && r.opencode_session_id !== "" ? r.opencode_session_id : null };
|
|
96
|
+
} catch (e) {
|
|
97
|
+
log.info(`coordination: session uid resolve failed (${e instanceof Error ? e.message : String(e)})`);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
78
102
|
export async function getSessionDaemonMap(): Promise<Map<string, SessionDaemonInfo>> {
|
|
79
103
|
try {
|
|
80
104
|
const rows = await getDB().all<{
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { homedir } from "os";
|
|
|
6
6
|
import { loadConfig, DB_OVERRIDABLE, parseOverride, resolveTtsBackend } from "./config";
|
|
7
7
|
import type { Config } from "./config";
|
|
8
8
|
import { setConfig, deleteConfig, getConfigAll, initDB, getDB } from "./db";
|
|
9
|
-
import { getActiveDaemons, listAllDaemons, getSessionDaemonMap, resolveDaemonEndpoint, getRunningSessionsForProject } from "./coordination";
|
|
9
|
+
import { getActiveDaemons, listAllDaemons, getSessionDaemonMap, resolveDaemonEndpoint, getRunningSessionsForProject, resolveSessionUid } from "./coordination";
|
|
10
10
|
import { testMysqlConnection, migrateSqliteToMysql, writeMysqlEnv, migrateMysqlToSqlite, writeSqliteEnv, migrateDaemonSqliteToMysql, generateMysqlDDL } from "./db-admin";
|
|
11
11
|
import type { MysqlTargetOpts } from "./db-admin";
|
|
12
12
|
import { checkAuth, makeAuthCookieHeader, clearAuthCookieHeader, loginHTML, sanitizeNext, ensureBootstrapAdmin, ensureBootstrapSystem, isReservedSystemLogin } from "./auth";
|
|
@@ -661,7 +661,7 @@ async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean
|
|
|
661
661
|
}
|
|
662
662
|
|
|
663
663
|
if (url.pathname === "/") {
|
|
664
|
-
return Response.redirect(`${url.origin}/
|
|
664
|
+
return Response.redirect(`${url.origin}/issues`, 302);
|
|
665
665
|
}
|
|
666
666
|
|
|
667
667
|
if (url.pathname === "/projects") {
|
|
@@ -709,8 +709,22 @@ async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean
|
|
|
709
709
|
|
|
710
710
|
const sessionView = url.pathname.match(SESSION_VIEW_RE);
|
|
711
711
|
if (sessionView) {
|
|
712
|
-
const [,
|
|
713
|
-
if (!
|
|
712
|
+
const [, rawSid] = sessionView;
|
|
713
|
+
if (!rawSid) return html(errorPage("404", "bad session path"), 404);
|
|
714
|
+
// Daemon-internal session UIDs (36-char uuid) appear in pickup acks before
|
|
715
|
+
// the backend reports its opencode id: redirect once known, hold page if not.
|
|
716
|
+
let sid = rawSid;
|
|
717
|
+
if (!sid.startsWith("ses_")) {
|
|
718
|
+
const uidInfo = await resolveSessionUid(rawSid);
|
|
719
|
+
if (uidInfo?.opencodeSessionId) {
|
|
720
|
+
const qs = new URLSearchParams(url.searchParams);
|
|
721
|
+
qs.set("daemon_id", String(uidInfo.daemonId));
|
|
722
|
+
return Response.redirect(`${url.origin}/sessions/${encodeURIComponent(uidInfo.opencodeSessionId)}?${qs}`, 302);
|
|
723
|
+
}
|
|
724
|
+
if (uidInfo) {
|
|
725
|
+
return html(errorPage("等待启动", "会话已创建,后端会话 ID 尚未生成(正在准备工作目录)。稍后刷新此页。"), 200);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
714
728
|
const desc = url.searchParams.get("asc") !== "1";
|
|
715
729
|
const all = url.searchParams.get("all") === "1";
|
|
716
730
|
const limit = Math.min(5000, Math.max(1, Number(url.searchParams.get("limit")) || 30));
|
package/src/views/home.ts
CHANGED
|
@@ -70,7 +70,7 @@ export async function buildHome(
|
|
|
70
70
|
.flash.ok{background:rgba(63,185,80,.12);color:#3fb950;border-color:rgba(63,185,80,.4)}
|
|
71
71
|
.flash.err{background:rgba(248,81,73,.12);color:#f85149;border-color:rgba(248,81,73,.4)}
|
|
72
72
|
</style></head><body>
|
|
73
|
-
<header class="topbar"><a href="/" style="
|
|
73
|
+
<header class="topbar"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a></header>
|
|
74
74
|
${tabNavHTML("projects", viewer ? { login: viewer.login, is_admin: viewer.is_admin } : undefined)}
|
|
75
75
|
<main class="home-wrap">
|
|
76
76
|
${flashHtml}
|
package/src/views/issues.ts
CHANGED
|
@@ -73,7 +73,7 @@ export async function buildIssuesFeed(
|
|
|
73
73
|
.label-filter{font-size:13px;color:var(--text-muted);margin-bottom:.4rem}
|
|
74
74
|
.label-filter strong{color:var(--text)}
|
|
75
75
|
</style></head><body>
|
|
76
|
-
<header class="topbar"><a href="/" style="
|
|
76
|
+
<header class="topbar"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a></header>
|
|
77
77
|
${tabNavHTML("issues", viewer ? { login: viewer.login, is_admin: viewer.is_admin } : undefined)}
|
|
78
78
|
<main class="feed-wrap">
|
|
79
79
|
<form class="search" method="GET" action="/issues">
|
package/src/views/projectAi.ts
CHANGED
|
@@ -71,7 +71,7 @@ button.danger{background:#da3633;color:#fff}
|
|
|
71
71
|
button:disabled{opacity:.5;cursor:not-allowed}
|
|
72
72
|
.future{opacity:.5;pointer-events:none}
|
|
73
73
|
</style></head><body>
|
|
74
|
-
<header class="nav"><a href="/" style="color:var(--header-text)"
|
|
74
|
+
<header class="nav"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a><span style="opacity:.8"> · ${escapeHtml(project.owner + "/" + project.name)}</span></header>
|
|
75
75
|
<main class="wrap">
|
|
76
76
|
${projectSettingsTabsHTML(project.owner, project.name, "ai")}
|
|
77
77
|
<h1>🤖 AI 设置</h1>
|
|
@@ -60,7 +60,7 @@ button{padding:.5rem 1.2rem;border:0;border-radius:6px;background:var(--accent);
|
|
|
60
60
|
.flash.ok{background:#1f6feb;color:#fff}
|
|
61
61
|
.flash.err{background:#da3633;color:#fff}
|
|
62
62
|
</style></head><body>
|
|
63
|
-
<header class="nav"><a href="/" style="color:var(--header-text)"
|
|
63
|
+
<header class="nav"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a><span style="opacity:.8"> · ${escapeHtml(project.owner + "/" + project.name)}</span></header>
|
|
64
64
|
<main class="wrap">
|
|
65
65
|
${projectSettingsTabsHTML(project.owner, project.name, "model")}
|
|
66
66
|
<h1>🤖 模型设置</h1>
|
package/src/views/sessionLog.ts
CHANGED
|
@@ -48,7 +48,7 @@ export async function buildSessionList(
|
|
|
48
48
|
.sd-badge{background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:0 .3rem;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}
|
|
49
49
|
.empty{padding:2rem;text-align:center;color:var(--text-muted)}
|
|
50
50
|
</style></head><body>
|
|
51
|
-
<header class="nav" style="display:flex;align-items:center;gap:.5rem;padding:.55rem 1rem;background:var(--header-bg);color:var(--header-text);font-size:13px"><a href="/" style="color:var(--header-text)"
|
|
51
|
+
<header class="nav" style="display:flex;align-items:center;gap:.5rem;padding:.55rem 1rem;background:var(--header-bg);color:var(--header-text);font-size:13px"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a><span style="opacity:.8"> · OpenCode 会话 ${sessions.length}</span></header>
|
|
52
52
|
${tabNavHTML("sessions", viewer ? { login: viewer.login, is_admin: viewer.is_admin } : undefined)}
|
|
53
53
|
<form class="sbar" method="get" action="/sessions">
|
|
54
54
|
<input name="q" value="${escapeAttr(q)}" placeholder="按标题或会话 ID 搜索…" autocomplete="off">
|
package/src/views/settings.ts
CHANGED
|
@@ -165,7 +165,7 @@ button.secondary{background:transparent;color:var(--text-muted);border:1px solid
|
|
|
165
165
|
.daemon-list button.remove{padding:.25rem .6rem;font-size:11px;background:transparent;color:var(--text-muted);border:1px solid var(--border)}
|
|
166
166
|
.daemon-empty{color:var(--text-muted);font-size:13px;padding:.5rem 0}
|
|
167
167
|
</style></head><body>
|
|
168
|
-
<header class="nav"><a href="/" style="color:var(--header-text)"
|
|
168
|
+
<header class="nav"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a><span style="opacity:.8"> · 设置</span><span style="margin-left:auto;opacity:.85">👤 ${escapeHtml(viewer.login)}</span><form method="post" action="/logout" style="display:inline"><button type="submit" style="background:none;border:1px solid rgba(255,255,255,.3);color:var(--header-text);border-radius:6px;padding:.2rem .7rem;font-size:12px;cursor:pointer;font:inherit">退出</button></form></header>
|
|
169
169
|
<main class="wrap">
|
|
170
170
|
<h1>后台配置</h1>
|
|
171
171
|
<p class="hint">改完保存立即生效,无需重启。密钥/启动项(token、端口等)仍在 <code>.env</code>,不在此处。</p>
|
|
@@ -79,7 +79,7 @@ h1{font-size:18px;margin:0 0 .3rem}
|
|
|
79
79
|
.tab{padding:.3rem .7rem;border-radius:6px 6px 0 0;text-decoration:none;color:var(--text-muted)}
|
|
80
80
|
.tab.active{background:var(--accent);color:#fff}
|
|
81
81
|
</style></head><body>
|
|
82
|
-
<header class="nav"><a href="/" style="color:var(--header-text)"
|
|
82
|
+
<header class="nav"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a></header>
|
|
83
83
|
${tabNavHTML("projects", viewer)}
|
|
84
84
|
<main class="wrap">
|
|
85
85
|
<h1>Webhook 投递记录</h1>
|
package/src/views/webhooks.ts
CHANGED
|
@@ -142,7 +142,7 @@ button[type=submit]{padding:.5rem 1.2rem;border:0;border-radius:6px;background:v
|
|
|
142
142
|
.subtab{padding:.35rem .8rem;border-radius:6px 6px 0 0;font-size:13px;color:var(--text-muted)}
|
|
143
143
|
.subtab.active{background:var(--bg-muted);color:var(--text);font-weight:600}
|
|
144
144
|
</style></head><body>
|
|
145
|
-
<header class="nav"><a href="/" style="color:var(--header-text)"
|
|
145
|
+
<header class="nav"><a href="/" title="ework 主页" style="color:var(--header-text)">🏠</a></header>
|
|
146
146
|
${tabNavHTML("projects")}
|
|
147
147
|
<main class="wrap">
|
|
148
148
|
<div class="crumb"><a href="/projects">项目</a> · <a href="/${encodeURIComponent(project.owner)}/${encodeURIComponent(project.name)}">${escapeHtml(project.owner + "/" + project.name)}</a> · 设置</div>
|