ework-web 0.10.62 → 0.10.64
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 +36 -0
- package/src/giteaApi.ts +1 -1
- package/src/index.ts +13 -8
- package/src/webhooks.ts +1 -1
package/package.json
CHANGED
package/src/coordination.ts
CHANGED
|
@@ -118,3 +118,39 @@ export async function resolveDaemonEndpoint(daemonId: number): Promise<string |
|
|
|
118
118
|
return null;
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
export interface RunningSessionInfo {
|
|
123
|
+
issueNumber: string;
|
|
124
|
+
sessionId: string;
|
|
125
|
+
daemonId: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Query daemon DB for sessions with state='running' belonging to a specific project scope.
|
|
130
|
+
* Uses REAL daemon session state instead of the display-only ai_status field.
|
|
131
|
+
*/
|
|
132
|
+
export async function getRunningSessionsForProject(scopeKey: string): Promise<RunningSessionInfo[]> {
|
|
133
|
+
try {
|
|
134
|
+
const rows = await getDB().all<{
|
|
135
|
+
tracker_issue_id: string;
|
|
136
|
+
opencode_session_id: string;
|
|
137
|
+
daemon_id: number;
|
|
138
|
+
}>(
|
|
139
|
+
`SELECT i.tracker_issue_id, s.opencode_session_id, i.owner_daemon_id AS daemon_id
|
|
140
|
+
FROM {{d_op_sessions}} s
|
|
141
|
+
JOIN {{d_issues}} i ON i.uid = s.issue_id
|
|
142
|
+
WHERE s.state = 'running'
|
|
143
|
+
AND i.tracker_scope_key = ?
|
|
144
|
+
AND s.opencode_session_id IS NOT NULL`,
|
|
145
|
+
[scopeKey],
|
|
146
|
+
);
|
|
147
|
+
return rows.map((r) => ({
|
|
148
|
+
issueNumber: r.tracker_issue_id,
|
|
149
|
+
sessionId: r.opencode_session_id,
|
|
150
|
+
daemonId: r.daemon_id,
|
|
151
|
+
}));
|
|
152
|
+
} catch (e) {
|
|
153
|
+
log.info(`coordination: running-sessions query failed (${e instanceof Error ? e.message : String(e)})`);
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/giteaApi.ts
CHANGED
|
@@ -153,7 +153,7 @@ export async function handleGiteaApi(
|
|
|
153
153
|
const limitRaw = Number(url.searchParams.get("limit") ?? 50);
|
|
154
154
|
const limit = Number.isFinite(limitRaw) && limitRaw > 0 ? Math.min(limitRaw, 200) : 50;
|
|
155
155
|
try {
|
|
156
|
-
const rows = await listAllIssues({ q, state, limit, viewerLogin: user.login, viewerIsAdmin: user.is_admin === 1 });
|
|
156
|
+
const rows = await listAllIssues({ q, state, limit, viewerLogin: user.login, viewerIsAdmin: caller.is_admin === 1 && user.is_admin === 1 });
|
|
157
157
|
const body = [];
|
|
158
158
|
for (const row of rows) {
|
|
159
159
|
const project = await getProject(row.project_owner, row.project_name);
|
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 } from "./coordination";
|
|
9
|
+
import { getActiveDaemons, listAllDaemons, getSessionDaemonMap, resolveDaemonEndpoint, getRunningSessionsForProject } 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";
|
|
@@ -1994,12 +1994,17 @@ async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean
|
|
|
1994
1994
|
return Response.redirect(`${back}?err=${encodeURIComponent("无权限")}`, 303);
|
|
1995
1995
|
}
|
|
1996
1996
|
const issues = await listIssues(project.id, { state: "all" });
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
1997
|
+
const running = await getRunningSessionsForProject(`${owner}/${repo}`);
|
|
1998
|
+
const issueIds = new Set<number>();
|
|
1999
|
+
for (const r of running) {
|
|
2000
|
+
const it = issues.find((i) => String(i.number) === r.issueNumber);
|
|
2001
|
+
if (it) issueIds.add(it.id);
|
|
2001
2002
|
}
|
|
2002
|
-
|
|
2003
|
+
for (const id of issueIds) {
|
|
2004
|
+
await updateIssueAiStatus(id, "halted");
|
|
2005
|
+
void emitStatusChanged(project.id, id, "processing", "halted", ctx.user!.login, url.origin);
|
|
2006
|
+
}
|
|
2007
|
+
return Response.redirect(`${back}?ok=1&ok_msg=${encodeURIComponent(`已停止 ${issueIds.size} 个运行中AI会话`)}`, 303);
|
|
2003
2008
|
}
|
|
2004
2009
|
|
|
2005
2010
|
const settingsDispatchMatch = url.pathname.match(SETTINGS_DISPATCH_RE);
|
|
@@ -2255,8 +2260,8 @@ async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean
|
|
|
2255
2260
|
const dispatchCfg = await getConfigAll();
|
|
2256
2261
|
const dispatchOff = dispatchCfg[`dispatchOff:${owner}/${repo}`] === "1";
|
|
2257
2262
|
const globalDispatchOff = dispatchCfg["dispatchEnabled"] === "false";
|
|
2258
|
-
const
|
|
2259
|
-
const processingCount =
|
|
2263
|
+
const running = await getRunningSessionsForProject(`${owner}/${repo}`);
|
|
2264
|
+
const processingCount = new Set(running.map((r) => r.issueNumber)).size;
|
|
2260
2265
|
return html(buildProjectAiPage(project, dispatchOff, globalDispatchOff, processingCount).html);
|
|
2261
2266
|
}
|
|
2262
2267
|
|
package/src/webhooks.ts
CHANGED
|
@@ -756,7 +756,7 @@ export async function emitStatusChanged(
|
|
|
756
756
|
const project = await getProjectById(projectId);
|
|
757
757
|
if (!project) return;
|
|
758
758
|
const issue = await getIssueById(issueId);
|
|
759
|
-
if (!issue) return;
|
|
759
|
+
if (!issue) { log.warn(`emitStatusChanged: issue ${issueId} not found (project=${projectId}) — silent skip prevented`); return; }
|
|
760
760
|
const commentCount = await countCommentsSafe(issueId);
|
|
761
761
|
const repo = buildRepository(project, origin);
|
|
762
762
|
const sender = buildUser(actor, origin);
|