ework-web 0.10.80 → 0.10.82

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-web",
3
- "version": "0.10.80",
3
+ "version": "0.10.82",
4
4
  "type": "module",
5
5
  "description": "ework-web — standalone multi-project issue tracker. Local SQLite-backed, no external API dependency. Bun + TypeScript + SSR HTML.",
6
6
  "license": "MIT",
@@ -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";
@@ -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 [, sid] = sessionView;
713
- if (!sid) return html(errorPage("404", "bad session path"), 404);
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));
@@ -211,3 +211,10 @@ CREATE TABLE IF NOT EXISTS {{project_members}} (
211
211
  CONSTRAINT {{fk_pm_user}} FOREIGN KEY (user_login) REFERENCES {{users}}(login) ON DELETE CASCADE
212
212
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
213
213
  CREATE INDEX project_members_user ON {{project_members}} (user_login);
214
+
215
+ CREATE TABLE IF NOT EXISTS {{config}} (
216
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
217
+ akey VARCHAR(255) NOT NULL UNIQUE,
218
+ value TEXT NOT NULL,
219
+ updated_at VARCHAR(40) NOT NULL
220
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;