opencode-supertask 0.1.35 → 0.1.36
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/CHANGELOG.md +15 -0
- package/README.md +7 -4
- package/dist/cli/index.js +410 -51
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/index.js +238 -28
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.js +30 -0
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.d.ts +7 -0
- package/dist/web/index.js +238 -28
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -20955,6 +20955,7 @@ var init_management_lock = __esm({
|
|
|
20955
20955
|
// src/daemon/pm2.ts
|
|
20956
20956
|
var pm2_exports = {};
|
|
20957
20957
|
__export(pm2_exports, {
|
|
20958
|
+
diagnoseOpenCodeRuntime: () => diagnoseOpenCodeRuntime,
|
|
20958
20959
|
ensureGateway: () => ensureGateway,
|
|
20959
20960
|
ensurePm2LogRotation: () => ensurePm2LogRotation,
|
|
20960
20961
|
getGatewayDiagnostic: () => getGatewayDiagnostic,
|
|
@@ -21074,7 +21075,35 @@ function scopesMatch(left, right) {
|
|
|
21074
21075
|
function currentScope() {
|
|
21075
21076
|
return runtimeScope({ cwd: process.cwd(), env: process.env });
|
|
21076
21077
|
}
|
|
21077
|
-
function
|
|
21078
|
+
function diagnoseOpenCodeRuntime(runtime = {
|
|
21079
|
+
cwd: process.cwd(),
|
|
21080
|
+
env: process.env
|
|
21081
|
+
}) {
|
|
21082
|
+
const executable = runtimeScope(runtime).opencodePath;
|
|
21083
|
+
const result = spawnSync2(executable, ["--version"], {
|
|
21084
|
+
cwd: runtime.cwd,
|
|
21085
|
+
env: runtime.env,
|
|
21086
|
+
encoding: "utf8",
|
|
21087
|
+
timeout: 1e4,
|
|
21088
|
+
killSignal: "SIGKILL"
|
|
21089
|
+
});
|
|
21090
|
+
const ok = result.status === 0 && result.error === void 0;
|
|
21091
|
+
return {
|
|
21092
|
+
ok,
|
|
21093
|
+
executable,
|
|
21094
|
+
version: ok ? result.stdout.trim() || result.stderr.trim() || null : null,
|
|
21095
|
+
error: ok ? null : result.error?.message || result.stderr.trim() || result.stdout.trim() || `exit code ${result.status}`
|
|
21096
|
+
};
|
|
21097
|
+
}
|
|
21098
|
+
function assertOpenCodeRuntime(runtime) {
|
|
21099
|
+
const diagnostic = diagnoseOpenCodeRuntime(runtime);
|
|
21100
|
+
if (!diagnostic.ok) {
|
|
21101
|
+
throw new Error(
|
|
21102
|
+
`[supertask] \u76EE\u6807 Gateway \u73AF\u5883\u65E0\u6CD5\u6267\u884C OpenCode (${diagnostic.executable}): ${diagnostic.error}`
|
|
21103
|
+
);
|
|
21104
|
+
}
|
|
21105
|
+
}
|
|
21106
|
+
function getGatewayDiagnostic(options = {}) {
|
|
21078
21107
|
const producerScope = currentScope();
|
|
21079
21108
|
if (!isPm2Installed()) {
|
|
21080
21109
|
return {
|
|
@@ -21090,7 +21119,8 @@ function getGatewayDiagnostic() {
|
|
|
21090
21119
|
startupConfigured: process.platform === "darwin" || process.platform === "linux" ? false : null,
|
|
21091
21120
|
currentScope: producerScope,
|
|
21092
21121
|
gatewayScope: null,
|
|
21093
|
-
scopeMatches: false
|
|
21122
|
+
scopeMatches: false,
|
|
21123
|
+
gatewayOpenCode: null
|
|
21094
21124
|
};
|
|
21095
21125
|
}
|
|
21096
21126
|
const processes = pm2JsonList();
|
|
@@ -21118,7 +21148,8 @@ function getGatewayDiagnostic() {
|
|
|
21118
21148
|
) : process.platform === "linux" ? isLinuxStartupConfigured(gatewayEnv, runtime?.cwd, runtime ?? void 0) : null,
|
|
21119
21149
|
currentScope: producerScope,
|
|
21120
21150
|
gatewayScope: managedScope,
|
|
21121
|
-
scopeMatches: managedScope != null && scopesMatch(producerScope, managedScope)
|
|
21151
|
+
scopeMatches: managedScope != null && scopesMatch(producerScope, managedScope),
|
|
21152
|
+
gatewayOpenCode: runtime === null || !options.probeOpenCode ? null : diagnoseOpenCodeRuntime(runtime)
|
|
21122
21153
|
};
|
|
21123
21154
|
}
|
|
21124
21155
|
function writeRunningVersion(version2, env = process.env, cwd = process.cwd()) {
|
|
@@ -21921,6 +21952,7 @@ function installUnlocked() {
|
|
|
21921
21952
|
assertRuntimeCanControlPm2(oldRuntime, existing);
|
|
21922
21953
|
gatewayKillTimeoutMs(targetRuntime);
|
|
21923
21954
|
gatewayTerminationCommandTimeoutMs(targetRuntime);
|
|
21955
|
+
assertOpenCodeRuntime(targetRuntime);
|
|
21924
21956
|
if (existing) requirePm2Termination("delete", "pm2 delete existing Gateway", oldRuntime);
|
|
21925
21957
|
const version2 = getPackageVersion();
|
|
21926
21958
|
try {
|
|
@@ -22033,6 +22065,7 @@ function upgradeUnlocked(target) {
|
|
|
22033
22065
|
assertRuntimeCanControlPm2(oldRuntime, existing);
|
|
22034
22066
|
gatewayKillTimeoutMs(targetRuntime);
|
|
22035
22067
|
gatewayTerminationCommandTimeoutMs(targetRuntime);
|
|
22068
|
+
assertOpenCodeRuntime(targetRuntime);
|
|
22036
22069
|
if (existing) requirePm2Termination("delete", "pm2 delete old Gateway", oldRuntime);
|
|
22037
22070
|
try {
|
|
22038
22071
|
pm2StartGateway(targetRuntime);
|
|
@@ -22085,6 +22118,7 @@ function ensureGatewayUnlocked() {
|
|
|
22085
22118
|
const before = getRunningVersion(oldRuntime?.env ?? process.env, oldRuntime?.cwd);
|
|
22086
22119
|
gatewayKillTimeoutMs(targetRuntime);
|
|
22087
22120
|
gatewayTerminationCommandTimeoutMs(targetRuntime);
|
|
22121
|
+
assertOpenCodeRuntime(targetRuntime);
|
|
22088
22122
|
if (existing) requirePm2Termination("delete", "pm2 delete stale Gateway", oldRuntime);
|
|
22089
22123
|
try {
|
|
22090
22124
|
pm2StartGateway(targetRuntime);
|
|
@@ -22665,7 +22699,7 @@ import { spawn } from "child_process";
|
|
|
22665
22699
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
22666
22700
|
import { existsSync as existsSync7 } from "fs";
|
|
22667
22701
|
import { dirname as dirname8, join as join6 } from "path";
|
|
22668
|
-
import { randomUUID as
|
|
22702
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
22669
22703
|
function runCommandContext(executable, args, cwd) {
|
|
22670
22704
|
return JSON.stringify({
|
|
22671
22705
|
type: "supertask_command",
|
|
@@ -22806,7 +22840,7 @@ var init_worker = __esm({
|
|
|
22806
22840
|
}
|
|
22807
22841
|
let runId = null;
|
|
22808
22842
|
try {
|
|
22809
|
-
const launchIdentity = `gateway-${process.pid}:launch:${
|
|
22843
|
+
const launchIdentity = `gateway-${process.pid}:launch:${randomUUID3()}`;
|
|
22810
22844
|
const run = await TaskRunService.create({
|
|
22811
22845
|
taskId: task.id,
|
|
22812
22846
|
model: this.resolveModel(task.model),
|
|
@@ -22931,8 +22965,8 @@ var init_worker = __esm({
|
|
|
22931
22965
|
}
|
|
22932
22966
|
});
|
|
22933
22967
|
let spawnError = null;
|
|
22934
|
-
const spawned = new Promise((
|
|
22935
|
-
child.once("spawn",
|
|
22968
|
+
const spawned = new Promise((resolve6, reject) => {
|
|
22969
|
+
child.once("spawn", resolve6);
|
|
22936
22970
|
child.once("error", (error) => {
|
|
22937
22971
|
spawnError = error;
|
|
22938
22972
|
reject(error);
|
|
@@ -22980,10 +23014,10 @@ var init_worker = __esm({
|
|
|
22980
23014
|
return;
|
|
22981
23015
|
}
|
|
22982
23016
|
try {
|
|
22983
|
-
await new Promise((
|
|
23017
|
+
await new Promise((resolve6, reject) => {
|
|
22984
23018
|
child.stdin.end("START\n", (error) => {
|
|
22985
23019
|
if (error) reject(error);
|
|
22986
|
-
else
|
|
23020
|
+
else resolve6();
|
|
22987
23021
|
});
|
|
22988
23022
|
});
|
|
22989
23023
|
} catch (error) {
|
|
@@ -25997,7 +26031,7 @@ function cleanOutput(value) {
|
|
|
25997
26031
|
return value.replace(ANSI_PATTERN, "").replace(/\r/g, "");
|
|
25998
26032
|
}
|
|
25999
26033
|
function runOpenCode(executable, args, cwd, timeoutMs) {
|
|
26000
|
-
return new Promise((
|
|
26034
|
+
return new Promise((resolve6, reject) => {
|
|
26001
26035
|
const child = spawn2(executable, args, {
|
|
26002
26036
|
cwd,
|
|
26003
26037
|
env: process.env,
|
|
@@ -26041,7 +26075,7 @@ function runOpenCode(executable, args, cwd, timeoutMs) {
|
|
|
26041
26075
|
reject(new Error(`OpenCode ${args.join(" ")} \u5931\u8D25\uFF1A${detail}`));
|
|
26042
26076
|
return;
|
|
26043
26077
|
}
|
|
26044
|
-
|
|
26078
|
+
resolve6(cleanOutput(stdout));
|
|
26045
26079
|
});
|
|
26046
26080
|
});
|
|
26047
26081
|
}
|
|
@@ -26178,7 +26212,70 @@ function clientMessages(locale) {
|
|
|
26178
26212
|
"action.refresh",
|
|
26179
26213
|
"action.logs",
|
|
26180
26214
|
"action.hideLogs",
|
|
26215
|
+
"details.title",
|
|
26216
|
+
"details.subtitle",
|
|
26217
|
+
"details.taskTitle",
|
|
26218
|
+
"details.runTitle",
|
|
26219
|
+
"details.templateTitle",
|
|
26220
|
+
"details.raw",
|
|
26181
26221
|
"details.copySuccess",
|
|
26222
|
+
"details.id",
|
|
26223
|
+
"details.project",
|
|
26224
|
+
"details.prompt",
|
|
26225
|
+
"details.result",
|
|
26226
|
+
"details.category",
|
|
26227
|
+
"details.batch",
|
|
26228
|
+
"details.dependency",
|
|
26229
|
+
"details.importance",
|
|
26230
|
+
"details.urgency",
|
|
26231
|
+
"details.retryCount",
|
|
26232
|
+
"details.retryBackoff",
|
|
26233
|
+
"details.timeout",
|
|
26234
|
+
"details.createdAt",
|
|
26235
|
+
"details.updatedAt",
|
|
26236
|
+
"details.startedAt",
|
|
26237
|
+
"details.finishedAt",
|
|
26238
|
+
"details.scheduledAt",
|
|
26239
|
+
"details.enabled",
|
|
26240
|
+
"details.scheduleRule",
|
|
26241
|
+
"details.maxInstances",
|
|
26242
|
+
"details.maxRetries",
|
|
26243
|
+
"details.lastRun",
|
|
26244
|
+
"details.nextRun",
|
|
26245
|
+
"details.taskId",
|
|
26246
|
+
"details.session",
|
|
26247
|
+
"details.heartbeat",
|
|
26248
|
+
"details.process",
|
|
26249
|
+
"details.history",
|
|
26250
|
+
"details.noHistory",
|
|
26251
|
+
"details.none",
|
|
26252
|
+
"details.default",
|
|
26253
|
+
"details.enabledYes",
|
|
26254
|
+
"details.enabledNo",
|
|
26255
|
+
"table.name",
|
|
26256
|
+
"table.agent",
|
|
26257
|
+
"table.model",
|
|
26258
|
+
"table.status",
|
|
26259
|
+
"table.duration",
|
|
26260
|
+
"template.scheduleType",
|
|
26261
|
+
"status.pending",
|
|
26262
|
+
"status.running",
|
|
26263
|
+
"status.done",
|
|
26264
|
+
"status.failed",
|
|
26265
|
+
"status.dead_letter",
|
|
26266
|
+
"status.cancelled",
|
|
26267
|
+
"status.unknown",
|
|
26268
|
+
"runStatus.running",
|
|
26269
|
+
"runStatus.done",
|
|
26270
|
+
"runStatus.failed",
|
|
26271
|
+
"schedule.cron",
|
|
26272
|
+
"schedule.recurring",
|
|
26273
|
+
"schedule.delayed",
|
|
26274
|
+
"schedule.unknown",
|
|
26275
|
+
"duration.seconds",
|
|
26276
|
+
"duration.minutes",
|
|
26277
|
+
"duration.hours",
|
|
26278
|
+
"duration.days",
|
|
26182
26279
|
"feedback.copyFailed",
|
|
26183
26280
|
"dialog.cancelTask",
|
|
26184
26281
|
"dialog.cancelTaskBody",
|
|
@@ -26307,8 +26404,8 @@ function renderLayout(options) {
|
|
|
26307
26404
|
<div class="dialog-actions"><button type="button" class="btn" onclick="document.getElementById('directory-dialog').close()">${t(locale, "action.cancel")}</button><button id="directory-choose" type="button" class="btn btn-primary">${icon("folder")}${t(locale, "action.chooseThisFolder")}</button></div>
|
|
26308
26405
|
</dialog>
|
|
26309
26406
|
<dialog id="detail-dialog">
|
|
26310
|
-
<div class="dialog-head"><div><h2>${t(locale, "details.title")}</h2><p>${t(locale, "details.subtitle")}</p></div><button class="icon-button" onclick="document.getElementById('detail-dialog').close()" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
26311
|
-
<div class="dialog-body"><
|
|
26407
|
+
<div class="dialog-head"><div><h2 id="detail-title">${t(locale, "details.title")}</h2><p>${t(locale, "details.subtitle")}</p></div><button class="icon-button" onclick="document.getElementById('detail-dialog').close()" aria-label="${t(locale, "action.close")}">${icon("close")}</button></div>
|
|
26408
|
+
<div class="dialog-body"><div id="detail-content" class="detail-view"></div><details class="detail-raw"><summary>${t(locale, "details.raw")}</summary><pre id="detail-raw" class="json-view"></pre></details></div>
|
|
26312
26409
|
<div class="dialog-actions"><button class="btn" onclick="copyDetails()">${icon("copy")}${t(locale, "action.copy")}</button><button class="btn btn-primary" onclick="document.getElementById('detail-dialog').close()">${t(locale, "action.close")}</button></div>
|
|
26313
26410
|
</dialog>
|
|
26314
26411
|
<dialog id="confirm-dialog">
|
|
@@ -26340,9 +26437,39 @@ function renderLayout(options) {
|
|
|
26340
26437
|
async function retryTask(id){if(!await ask(text('dialog.retryTask',{id}),text('dialog.retryTaskBody')))return;try{await readJson(await fetch('/api/tasks/'+id+'/retry',{method:'POST'}));location.reload()}catch(error){showToast(text('feedback.retryFailed')+': '+error.message,'error')}}
|
|
26341
26438
|
async function cancelTask(id){if(!await ask(text('dialog.cancelTask',{id}),text('dialog.cancelTaskBody'),true))return;try{await readJson(await fetch('/api/tasks/'+id+'/cancel',{method:'POST'}));location.reload()}catch(error){showToast(text('feedback.cancelFailed')+': '+error.message,'error')}}
|
|
26342
26439
|
async function deleteTask(id){if(!await ask(text('dialog.deleteTask',{id}),text('dialog.deleteTaskBody'),true))return;try{await readJson(await fetch('/api/tasks/'+id,{method:'DELETE'}));location.reload()}catch(error){showToast(text('feedback.deleteFailed')+': '+error.message,'error')}}
|
|
26343
|
-
|
|
26344
|
-
const
|
|
26345
|
-
|
|
26440
|
+
function detailDate(value){if(value===null||value===undefined||value==='')return text('details.none');const epoch=typeof value==='number'&&value<100000000000?value*1000:value;const date=new Date(epoch);return Number.isNaN(date.getTime())?String(value):date.toLocaleString(document.documentElement.lang)}
|
|
26441
|
+
function detailDuration(value){if(value===null||value===undefined)return text('details.default');const milliseconds=Number(value);if(!Number.isFinite(milliseconds))return String(value);if(milliseconds===0)return '0 ms';const units=[[86400000,text('duration.days')],[3600000,text('duration.hours')],[60000,text('duration.minutes')],[1000,text('duration.seconds')]];for(const [size,label] of units){if(milliseconds>=size&&milliseconds%size===0)return String(milliseconds/size)+' '+label}return String(milliseconds)+' ms'}
|
|
26442
|
+
function detailStatus(type,value){const prefix=type==='run'?'runStatus.':'status.';const key=prefix+String(value||'unknown');return Object.prototype.hasOwnProperty.call(UI,key)?text(key):text('status.unknown')}
|
|
26443
|
+
function detailScheduleType(value){const key='schedule.'+String(value||'unknown');return Object.prototype.hasOwnProperty.call(UI,key)?text(key):text('schedule.unknown')}
|
|
26444
|
+
function detailModel(value){return !value||value==='default'?text('details.default'):String(value)}
|
|
26445
|
+
function detailSession(value){if(!value)return text('details.none');const session=String(value);return session.length<=10?session:session.slice(0,6)+'***'+session.slice(-4)}
|
|
26446
|
+
function detailTaskResult(data){const presentation=data._resultPresentation;if(!presentation)return text('details.none');const parts=[];if(Array.isArray(presentation.errors)&&presentation.errors.length)parts.push(presentation.errors.join('\\n'));if(presentation.text)parts.push(presentation.text);return parts.join('\\n\\n')||text('details.none')}
|
|
26447
|
+
function detailField(label,value,options={}){const item=document.createElement('div');item.className='detail-item'+(options.wide?' wide':'');const name=document.createElement('div');name.className='detail-label';name.textContent=label;const content=options.long?document.createElement('pre'):document.createElement('div');content.className='detail-value'+(options.mono?' mono':'')+(options.long?' long':'');content.textContent=value===null||value===undefined||value===''?text('details.none'):String(value);item.append(name,content);return item}
|
|
26448
|
+
function renderDetailHistory(runs){const section=document.createElement('section');section.className='detail-history';const title=document.createElement('h3');title.textContent=text('details.history');section.appendChild(title);if(!Array.isArray(runs)||runs.length===0){const empty=document.createElement('div');empty.className='muted small';empty.textContent=text('details.noHistory');section.appendChild(empty);return section}const list=document.createElement('div');list.className='detail-history-list';for(const run of runs){const item=document.createElement('div');item.className='detail-history-item';const primary=document.createElement('strong');primary.textContent='Run #'+run.id+' \xB7 '+detailStatus('run',run.status);const secondary=document.createElement('span');secondary.className='muted';secondary.textContent=detailDate(run.startedAt)+(run.model?' \xB7 '+detailModel(run.model):'');item.append(primary,secondary);list.appendChild(item)}section.appendChild(list);return section}
|
|
26449
|
+
function detailFields(type,data){if(type==='task')return [
|
|
26450
|
+
[text('details.id'),'#'+data.id],[text('table.name'),data.name],[text('table.status'),detailStatus('task',data.status)],[text('details.project'),data.cwd,{wide:true,mono:true}],
|
|
26451
|
+
[text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.prompt'),data.prompt,{wide:true,long:true}],
|
|
26452
|
+
[text('details.category'),data.category],[text('details.batch'),data.batchId],[text('details.dependency'),data.dependsOn?'#'+data.dependsOn:text('details.none')],
|
|
26453
|
+
[text('details.importance'),data.importance],[text('details.urgency'),data.urgency],[text('details.retryCount'),String(data.retryCount??0)+' / '+String(data.maxRetries??0)],
|
|
26454
|
+
[text('details.retryBackoff'),detailDuration(data.retryBackoffMs)],[text('details.timeout'),detailDuration(data.timeoutMs)],[text('details.scheduledAt'),detailDate(data.scheduledAt)],
|
|
26455
|
+
[text('details.createdAt'),detailDate(data.createdAt)],[text('details.startedAt'),detailDate(data.startedAt)],[text('details.finishedAt'),detailDate(data.finishedAt)],
|
|
26456
|
+
[text('details.result'),detailTaskResult(data),{wide:true,long:true}]
|
|
26457
|
+
];if(type==='run'){const started=data.startedAt?new Date(data.startedAt).getTime():null;const finished=data.finishedAt?new Date(data.finishedAt).getTime():null;return [
|
|
26458
|
+
[text('details.id'),'Run #'+data.id],[text('details.taskId'),'#'+data.taskId],[text('table.status'),detailStatus('run',data.status)],[text('table.model'),detailModel(data.model)],
|
|
26459
|
+
[text('details.session'),detailSession(data.sessionId)],[text('details.startedAt'),detailDate(data.startedAt)],[text('details.finishedAt'),detailDate(data.finishedAt)],
|
|
26460
|
+
[text('table.duration'),started!==null?detailDuration((finished??Date.now())-started):text('details.none')],[text('details.heartbeat'),detailDate(data.heartbeatAt)],
|
|
26461
|
+
[text('details.process'),'Worker PID '+String(data.workerPid??'\u2014')+' \xB7 OpenCode PID '+String(data.childPid??'\u2014'),{wide:true,mono:true}]
|
|
26462
|
+
];}const scheduleRule=data.scheduleType==='cron'?data.cronExpr:data.scheduleType==='recurring'?detailDuration(data.intervalMs):detailDate(data.runAt);return [
|
|
26463
|
+
[text('details.id'),'#'+data.id],[text('table.name'),data.name],[text('details.enabled'),data.enabled?text('details.enabledYes'):text('details.enabledNo')],[text('details.project'),data.cwd,{wide:true,mono:true}],
|
|
26464
|
+
[text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.prompt'),data.prompt,{wide:true,long:true}],
|
|
26465
|
+
[text('template.scheduleType'),detailScheduleType(data.scheduleType)],[text('details.scheduleRule'),scheduleRule],[text('details.category'),data.category],[text('details.batch'),data.batchId],
|
|
26466
|
+
[text('details.importance'),data.importance],[text('details.urgency'),data.urgency],[text('details.maxInstances'),data.maxInstances],[text('details.maxRetries'),data.maxRetries??0],
|
|
26467
|
+
[text('details.retryBackoff'),detailDuration(data.retryBackoffMs)],[text('details.timeout'),detailDuration(data.timeoutMs)],[text('details.lastRun'),detailDate(data.lastRunAt)],[text('details.nextRun'),detailDate(data.nextRunAt)],
|
|
26468
|
+
[text('details.createdAt'),detailDate(data.createdAt)],[text('details.updatedAt'),detailDate(data.updatedAt)]
|
|
26469
|
+
]}
|
|
26470
|
+
async function showRecord(url,type){try{const data=await readJson(await fetch(url));const content=document.getElementById('detail-content');content.replaceChildren();const grid=document.createElement('div');grid.className='detail-grid';for(const [label,value,options] of detailFields(type,data))grid.appendChild(detailField(label,value,options));content.appendChild(grid);if(type==='task')content.appendChild(renderDetailHistory(data._runs));document.getElementById('detail-title').textContent=text(type==='task'?'details.taskTitle':type==='run'?'details.runTitle':'details.templateTitle');document.getElementById('detail-raw').textContent=JSON.stringify(data,null,2);document.querySelector('#detail-dialog .detail-raw').open=false;document.getElementById('detail-dialog').showModal()}catch(error){showToast(error.message,'error')}}
|
|
26471
|
+
const showDetail=id=>showRecord('/api/tasks/'+id,'task');const showRunDetail=id=>showRecord('/api/runs/'+id,'run');const showTemplateDetail=id=>showRecord('/api/templates/'+id,'template');
|
|
26472
|
+
async function copyDetails(){try{await navigator.clipboard.writeText(document.getElementById('detail-raw').textContent);showToast(text('details.copySuccess'))}catch{showToast(text('feedback.copyFailed'),'error')}}
|
|
26346
26473
|
async function copySessionCommand(id){try{const data=await readJson(await fetch('/api/runs/'+id+'/session-command'));await navigator.clipboard.writeText(data.command);showToast(text('feedback.sessionCommandCopied'))}catch(error){showToast(error.message||text('feedback.copyFailed'),'error')}}
|
|
26347
26474
|
async function copyRunCommand(id){try{await navigator.clipboard.writeText(document.getElementById('command-'+id).textContent);showToast(text('feedback.commandCopied'))}catch{showToast(text('feedback.copyFailed'),'error')}}
|
|
26348
26475
|
function taskField(name){return document.getElementById('task-'+name)}
|
|
@@ -26380,7 +26507,7 @@ function renderLayout(options) {
|
|
|
26380
26507
|
async function disableTmpl(id){if(!await ask(text('dialog.disableTemplate'),text('dialog.disableTemplateBody')))return;try{await readJson(await fetch('/api/templates/'+id+'/disable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
26381
26508
|
async function deleteTmpl(id){if(!await ask(text('dialog.deleteTemplate'),text('dialog.deleteTemplateBody'),true))return;try{await readJson(await fetch('/api/templates/'+id,{method:'DELETE'}));location.reload()}catch(error){showToast(error.message,'error')}}
|
|
26382
26509
|
async function triggerTmpl(id){if(!await ask(text('dialog.triggerTemplate'),text('dialog.triggerTemplateBody')))return;try{const data=await readJson(await fetch('/api/templates/'+id+'/trigger',{method:'POST'}));showToast(text('feedback.triggered',{id:data.taskId}));setTimeout(()=>location.reload(),550)}catch(error){showToast(error.message,'error')}}
|
|
26383
|
-
function toggleLog(id,button){const panel=document.getElementById('log-'+id);const hidden=!panel.hidden;panel.hidden=hidden;button.setAttribute('aria-expanded',String(!hidden));button.textContent=text(hidden?'action.logs':'action.hideLogs');}
|
|
26510
|
+
function toggleLog(id,button){const panel=document.getElementById('log-'+id);const hidden=!panel.hidden;panel.hidden=hidden;button.setAttribute('aria-expanded',String(!hidden));button.textContent=text(hidden?'action.logs':'action.hideLogs');if(!hidden)requestAnimationFrame(()=>panel.scrollIntoView({block:'nearest',behavior:'smooth'}));}
|
|
26384
26511
|
function filterTasks(value){const query=value.trim().toLocaleLowerCase();let visible=0;document.querySelectorAll('[data-task-row]').forEach(row=>{const match=!query||row.dataset.search.toLocaleLowerCase().includes(query);row.hidden=!match;if(match)visible++});const empty=document.getElementById('search-empty');if(empty)empty.hidden=visible!==0;}
|
|
26385
26512
|
async function clearDatabase(){if(!await askDanger())return;try{const data=await readJson(await fetch('/api/database/clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({confirmation:'CLEAR'})}));showToast(text('feedback.databaseCleared',{path:data.backupPath}));setTimeout(()=>location.reload(),1000)}catch(error){showToast(error.message,'error')}}
|
|
26386
26513
|
async function confirmGatewayRestart(runningCount=0){const body=runningCount>0?text('dialog.restartGatewayRunningBody',{count:runningCount}):text('dialog.restartGatewayBody');return await ask(text('dialog.restartGateway'),body)}
|
|
@@ -26426,7 +26553,7 @@ var init_ui = __esm({
|
|
|
26426
26553
|
"action.hideLogs": "\u6536\u8D77\u65E5\u5FD7",
|
|
26427
26554
|
"action.save": "\u4FDD\u5B58\u8BBE\u7F6E",
|
|
26428
26555
|
"action.saveAndRestart": "\u4FDD\u5B58\u5E76\u91CD\u542F",
|
|
26429
|
-
"action.copy": "\u590D\u5236
|
|
26556
|
+
"action.copy": "\u590D\u5236\u539F\u59CB\u6570\u636E",
|
|
26430
26557
|
"action.close": "\u5173\u95ED",
|
|
26431
26558
|
"action.confirm": "\u786E\u8BA4",
|
|
26432
26559
|
"action.clearDatabase": "\u6E05\u7A7A\u6570\u636E\u5E93",
|
|
@@ -26618,9 +26745,46 @@ var init_ui = __esm({
|
|
|
26618
26745
|
"theme.light": "\u6D45\u8272",
|
|
26619
26746
|
"theme.dark": "\u6DF1\u8272",
|
|
26620
26747
|
"language.label": "\u8BED\u8A00",
|
|
26621
|
-
"details.title": "\
|
|
26622
|
-
"details.subtitle": "\u539F\u59CB\
|
|
26623
|
-
"details.
|
|
26748
|
+
"details.title": "\u8BE6\u60C5",
|
|
26749
|
+
"details.subtitle": "\u91CD\u70B9\u4FE1\u606F\u5DF2\u6574\u7406\uFF1B\u539F\u59CB\u6570\u636E\u4EC5\u7528\u4E8E\u6392\u969C\u3002",
|
|
26750
|
+
"details.taskTitle": "\u4EFB\u52A1\u8BE6\u60C5",
|
|
26751
|
+
"details.runTitle": "\u6267\u884C\u8BE6\u60C5",
|
|
26752
|
+
"details.templateTitle": "\u5B9A\u65F6\u4EFB\u52A1\u8BE6\u60C5",
|
|
26753
|
+
"details.raw": "\u67E5\u770B\u539F\u59CB\u6570\u636E\uFF08JSON\uFF09",
|
|
26754
|
+
"details.copySuccess": "\u539F\u59CB\u6570\u636E\u5DF2\u590D\u5236",
|
|
26755
|
+
"details.id": "\u7F16\u53F7",
|
|
26756
|
+
"details.project": "\u9879\u76EE\u76EE\u5F55",
|
|
26757
|
+
"details.prompt": "\u63D0\u793A\u8BCD",
|
|
26758
|
+
"details.result": "\u6267\u884C\u7ED3\u679C / \u5931\u8D25\u539F\u56E0",
|
|
26759
|
+
"details.category": "\u5206\u7C7B",
|
|
26760
|
+
"details.batch": "\u6279\u6B21",
|
|
26761
|
+
"details.dependency": "\u4F9D\u8D56\u4EFB\u52A1",
|
|
26762
|
+
"details.importance": "\u91CD\u8981\u7A0B\u5EA6",
|
|
26763
|
+
"details.urgency": "\u7D27\u6025\u7A0B\u5EA6",
|
|
26764
|
+
"details.retryCount": "\u5DF2\u91CD\u8BD5 / \u6700\u591A\u91CD\u8BD5",
|
|
26765
|
+
"details.retryBackoff": "\u91CD\u8BD5\u7B49\u5F85",
|
|
26766
|
+
"details.timeout": "\u6267\u884C\u8D85\u65F6",
|
|
26767
|
+
"details.createdAt": "\u521B\u5EFA\u65F6\u95F4",
|
|
26768
|
+
"details.updatedAt": "\u66F4\u65B0\u65F6\u95F4",
|
|
26769
|
+
"details.startedAt": "\u5F00\u59CB\u65F6\u95F4",
|
|
26770
|
+
"details.finishedAt": "\u7ED3\u675F\u65F6\u95F4",
|
|
26771
|
+
"details.scheduledAt": "\u8BA1\u5212\u65F6\u95F4",
|
|
26772
|
+
"details.enabled": "\u81EA\u52A8\u8FD0\u884C",
|
|
26773
|
+
"details.scheduleRule": "\u8FD0\u884C\u89C4\u5219",
|
|
26774
|
+
"details.maxInstances": "\u81EA\u52A8\u8C03\u5EA6\u4E0A\u9650",
|
|
26775
|
+
"details.maxRetries": "\u5931\u8D25\u91CD\u8BD5\u6B21\u6570",
|
|
26776
|
+
"details.lastRun": "\u4E0A\u6B21\u8FD0\u884C",
|
|
26777
|
+
"details.nextRun": "\u4E0B\u6B21\u8FD0\u884C",
|
|
26778
|
+
"details.taskId": "\u6240\u5C5E\u4EFB\u52A1",
|
|
26779
|
+
"details.session": "OpenCode \u4F1A\u8BDD",
|
|
26780
|
+
"details.heartbeat": "\u6700\u8FD1\u5FC3\u8DF3",
|
|
26781
|
+
"details.process": "\u8FDB\u7A0B",
|
|
26782
|
+
"details.history": "\u6267\u884C\u5386\u53F2",
|
|
26783
|
+
"details.noHistory": "\u8FD8\u6CA1\u6709\u6267\u884C\u8BB0\u5F55",
|
|
26784
|
+
"details.none": "\u65E0",
|
|
26785
|
+
"details.default": "\u8DDF\u968F\u7CFB\u7EDF\u9ED8\u8BA4",
|
|
26786
|
+
"details.enabledYes": "\u5DF2\u542F\u7528",
|
|
26787
|
+
"details.enabledNo": "\u5DF2\u505C\u7528",
|
|
26624
26788
|
"dialog.cancelTask": "\u53D6\u6D88\u4EFB\u52A1 #{id}\uFF1F",
|
|
26625
26789
|
"dialog.cancelTaskBody": "\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1\u4F1A\u5728\u4E0B\u4E00\u4E2A\u8F6E\u8BE2\u5468\u671F\u7EC8\u6B62\u5BF9\u5E94\u8FDB\u7A0B\u6811\u3002",
|
|
26626
26790
|
"dialog.retryTask": "\u91CD\u8BD5\u4EFB\u52A1 #{id}\uFF1F",
|
|
@@ -26690,7 +26854,7 @@ var init_ui = __esm({
|
|
|
26690
26854
|
"action.hideLogs": "Hide log",
|
|
26691
26855
|
"action.save": "Save settings",
|
|
26692
26856
|
"action.saveAndRestart": "Save and restart",
|
|
26693
|
-
"action.copy": "Copy
|
|
26857
|
+
"action.copy": "Copy raw data",
|
|
26694
26858
|
"action.close": "Close",
|
|
26695
26859
|
"action.confirm": "Confirm",
|
|
26696
26860
|
"action.clearDatabase": "Clear database",
|
|
@@ -26882,9 +27046,46 @@ var init_ui = __esm({
|
|
|
26882
27046
|
"theme.light": "Light",
|
|
26883
27047
|
"theme.dark": "Dark",
|
|
26884
27048
|
"language.label": "Language",
|
|
26885
|
-
"details.title": "
|
|
26886
|
-
"details.subtitle": "
|
|
26887
|
-
"details.
|
|
27049
|
+
"details.title": "Details",
|
|
27050
|
+
"details.subtitle": "Key information is organized below; raw data is only for troubleshooting.",
|
|
27051
|
+
"details.taskTitle": "Task details",
|
|
27052
|
+
"details.runTitle": "Run details",
|
|
27053
|
+
"details.templateTitle": "Scheduled task details",
|
|
27054
|
+
"details.raw": "View raw data (JSON)",
|
|
27055
|
+
"details.copySuccess": "Raw data copied",
|
|
27056
|
+
"details.id": "ID",
|
|
27057
|
+
"details.project": "Project directory",
|
|
27058
|
+
"details.prompt": "Prompt",
|
|
27059
|
+
"details.result": "Result / failure reason",
|
|
27060
|
+
"details.category": "Category",
|
|
27061
|
+
"details.batch": "Batch",
|
|
27062
|
+
"details.dependency": "Dependency",
|
|
27063
|
+
"details.importance": "Importance",
|
|
27064
|
+
"details.urgency": "Urgency",
|
|
27065
|
+
"details.retryCount": "Retries used / allowed",
|
|
27066
|
+
"details.retryBackoff": "Retry delay",
|
|
27067
|
+
"details.timeout": "Run timeout",
|
|
27068
|
+
"details.createdAt": "Created",
|
|
27069
|
+
"details.updatedAt": "Updated",
|
|
27070
|
+
"details.startedAt": "Started",
|
|
27071
|
+
"details.finishedAt": "Finished",
|
|
27072
|
+
"details.scheduledAt": "Scheduled for",
|
|
27073
|
+
"details.enabled": "Automatic runs",
|
|
27074
|
+
"details.scheduleRule": "Schedule rule",
|
|
27075
|
+
"details.maxInstances": "Automatic scheduling limit",
|
|
27076
|
+
"details.maxRetries": "Failure retries",
|
|
27077
|
+
"details.lastRun": "Last run",
|
|
27078
|
+
"details.nextRun": "Next run",
|
|
27079
|
+
"details.taskId": "Task",
|
|
27080
|
+
"details.session": "OpenCode session",
|
|
27081
|
+
"details.heartbeat": "Latest heartbeat",
|
|
27082
|
+
"details.process": "Processes",
|
|
27083
|
+
"details.history": "Run history",
|
|
27084
|
+
"details.noHistory": "No runs yet",
|
|
27085
|
+
"details.none": "None",
|
|
27086
|
+
"details.default": "Use system default",
|
|
27087
|
+
"details.enabledYes": "Enabled",
|
|
27088
|
+
"details.enabledNo": "Disabled",
|
|
26888
27089
|
"dialog.cancelTask": "Cancel task #{id}?",
|
|
26889
27090
|
"dialog.cancelTaskBody": "A running task will terminate its process tree on the next worker poll.",
|
|
26890
27091
|
"dialog.retryTask": "Retry task #{id}?",
|
|
@@ -27125,6 +27326,8 @@ var init_ui = __esm({
|
|
|
27125
27326
|
.danger-card h2 .icon { width:17px; height:17px; }
|
|
27126
27327
|
.danger-card p { max-width:800px; margin:0 0 14px; color:var(--text-2); font-size:12px; }
|
|
27127
27328
|
.log-panel { margin:12px 0; animation:reveal .18s ease both; }
|
|
27329
|
+
.run-log-row td { padding:0 16px 16px; background:color-mix(in srgb,var(--surface-2) 64%,var(--surface)); }
|
|
27330
|
+
.run-log-row .log-panel { margin:0; box-shadow:none; }
|
|
27128
27331
|
.log-content { display:grid; gap:14px; padding:16px; }
|
|
27129
27332
|
.log-section-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:7px; }
|
|
27130
27333
|
.run-command,.run-output,.run-error,.run-tools { min-width:0; }
|
|
@@ -27182,8 +27385,21 @@ var init_ui = __esm({
|
|
|
27182
27385
|
.directory-item:active { transform:scale(.99); }
|
|
27183
27386
|
.directory-item .icon { width:17px; height:17px; color:var(--primary); flex:0 0 auto; }
|
|
27184
27387
|
.directory-empty { min-height:220px; display:grid; place-items:center; color:var(--text-3); }
|
|
27185
|
-
.
|
|
27186
|
-
|
|
27388
|
+
.detail-view { display:grid; gap:16px; }
|
|
27389
|
+
.detail-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
|
|
27390
|
+
.detail-item { min-width:0; padding:12px 13px; border:1px solid var(--border); border-radius:10px; background:var(--surface-2); }
|
|
27391
|
+
.detail-item.wide { grid-column:1 / -1; }
|
|
27392
|
+
.detail-label { margin-bottom:5px; color:var(--text-3); font-size:10px; font-weight:750; letter-spacing:.045em; text-transform:uppercase; }
|
|
27393
|
+
.detail-value { color:var(--text); font-size:13px; line-height:1.55; overflow-wrap:anywhere; }
|
|
27394
|
+
.detail-value.mono { font-family:"SFMono-Regular",Consolas,monospace; font-size:11px; }
|
|
27395
|
+
.detail-value.long { max-height:240px; margin:0; overflow:auto; white-space:pre-wrap; }
|
|
27396
|
+
.detail-history h3 { margin:0 0 8px; font-size:13px; }
|
|
27397
|
+
.detail-history-list { display:grid; gap:7px; }
|
|
27398
|
+
.detail-history-item { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 11px; border:1px solid var(--border); border-radius:9px; background:var(--surface-2); font-size:11px; }
|
|
27399
|
+
.detail-raw { padding-top:12px; border-top:1px solid var(--border); }
|
|
27400
|
+
.detail-raw summary { color:var(--text-2); cursor:pointer; font-size:12px; font-weight:650; }
|
|
27401
|
+
.json-view { max-height:320px; margin:10px 0 0; padding:15px; overflow:auto; border:1px solid var(--border); border-radius:10px; color:var(--text-2);
|
|
27402
|
+
background:var(--surface-2); font-family:"SFMono-Regular",Consolas,monospace; font-size:11px; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
27187
27403
|
.confirm-copy { color:var(--text-2); margin:0; }
|
|
27188
27404
|
.confirm-copy strong { display:block; margin-bottom:5px; color:var(--text); font-size:15px; }
|
|
27189
27405
|
.danger-input { width:100%; height:40px; margin-top:14px; padding:0 12px; border:1px solid var(--border); border-radius:9px; outline:none;
|
|
@@ -27247,8 +27463,13 @@ var init_ui = __esm({
|
|
|
27247
27463
|
.responsive-table td[data-primary]::before { display:none; }
|
|
27248
27464
|
.responsive-table .task-prompt { max-width:100%; }
|
|
27249
27465
|
.responsive-table .actions { justify-content:flex-start; }
|
|
27466
|
+
.responsive-table .run-log-row { padding:0; overflow:hidden; }
|
|
27467
|
+
.responsive-table .run-log-cell { display:block; padding:0; }
|
|
27468
|
+
.responsive-table .run-log-cell::before { display:none; }
|
|
27250
27469
|
.project-grid { grid-template-columns:1fr; }
|
|
27251
27470
|
.template-form-grid { grid-template-columns:1fr; }
|
|
27471
|
+
.detail-grid { grid-template-columns:1fr; }
|
|
27472
|
+
.detail-item.wide { grid-column:auto; }
|
|
27252
27473
|
.form-field-wide { grid-column:auto; }
|
|
27253
27474
|
.field-action { align-items:stretch; flex-direction:column; }
|
|
27254
27475
|
.field-action .btn { width:100%; }
|
|
@@ -27568,7 +27789,7 @@ function renderRunLog(runId, taskName, log, locale) {
|
|
|
27568
27789
|
const errors = presentation.errors.length > 0 ? `<div class="run-error"><strong>${t(locale, "logs.error")}</strong><pre>${esc(presentation.errors.join("\n"))}</pre></div>` : "";
|
|
27569
27790
|
const tools = presentation.tools.length > 0 ? `<div class="run-tools"><strong>${t(locale, "logs.tools")}</strong><div class="actions">${presentation.tools.map((tool) => `<span class="tag">${esc(tool)}</span>`).join("")}</div></div>` : "";
|
|
27570
27791
|
const output = `<div class="run-output"><strong>${t(locale, "logs.output")}</strong><pre>${esc(presentation.text || t(locale, "logs.noText"))}</pre></div>`;
|
|
27571
|
-
return `<section
|
|
27792
|
+
return `<section class="panel log-panel"><div class="panel-head"><h3>Run #${runId} \xB7 ${esc(taskName)}</h3></div><div class="log-content">${command}${errors}${output}${tools}<details class="raw-log"><summary>${t(locale, "logs.raw")}</summary><div class="log-box">${esc(log)}</div></details></div></section>`;
|
|
27572
27793
|
}
|
|
27573
27794
|
function esc(value) {
|
|
27574
27795
|
if (!value) return "";
|
|
@@ -28008,14 +28229,11 @@ var init_web = __esm({
|
|
|
28008
28229
|
const total = Number(totalResult[0]?.count ?? 0);
|
|
28009
28230
|
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
28010
28231
|
if (page > totalPages) return c.redirect(`/runs?page=${totalPages}`);
|
|
28011
|
-
const logs = [];
|
|
28012
28232
|
const rows = runs.map((run) => {
|
|
28013
28233
|
const status = safeStatus(run.status);
|
|
28014
28234
|
const resumable = isValidSessionId(run.sessionId);
|
|
28015
|
-
|
|
28016
|
-
|
|
28017
|
-
}
|
|
28018
|
-
return `<tr>
|
|
28235
|
+
const log = run.log ? renderRunLog(run.id, run.taskName, run.log, locale) : "";
|
|
28236
|
+
return `<tr class="run-summary-row">
|
|
28019
28237
|
<td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
|
|
28020
28238
|
<td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(run.taskName)} <span class="faint">#${run.taskId}</span></div>${run.model ? `<div style="margin-top:4px"><span class="tag">${esc(run.model)}</span></div>` : ""}</td>
|
|
28021
28239
|
<td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(run.taskAgent)}</span></td>
|
|
@@ -28026,7 +28244,7 @@ var init_web = __esm({
|
|
|
28026
28244
|
<td data-label="${t(locale, "table.actions")}"><div class="actions"><button type="button" class="btn" onclick="showRunDetail(${run.id})">${t(locale, "action.details")}</button>
|
|
28027
28245
|
${resumable ? `<button type="button" class="btn" onclick="copySessionCommand(${run.id})">${icon("copy")}${t(locale, "action.continueSession")}</button>` : ""}
|
|
28028
28246
|
${run.log ? `<button type="button" class="btn" aria-expanded="false" onclick="toggleLog(${run.id},this)">${t(locale, "action.logs")}</button>` : ""}</div></td>
|
|
28029
|
-
</tr
|
|
28247
|
+
</tr>${log ? `<tr id="log-${run.id}" class="run-log-row" hidden><td class="run-log-cell" colspan="8">${log}</td></tr>` : ""}`;
|
|
28030
28248
|
}).join("");
|
|
28031
28249
|
const body = `
|
|
28032
28250
|
<div class="stats-grid">
|
|
@@ -28036,7 +28254,7 @@ var init_web = __esm({
|
|
|
28036
28254
|
${statCard(runs.filter((run) => run.status === "running").length, t(locale, "stats.pageRunning"), "tone-blue", icon("activity"), "reveal-delay-2")}
|
|
28037
28255
|
</div>
|
|
28038
28256
|
<section class="panel reveal reveal-delay-2">${runs.length === 0 ? emptyState(t(locale, "empty.runs"), "") : `<div class="table-wrap"><table class="responsive-table"><thead><tr><th>${t(locale, "table.run")}</th><th>${t(locale, "table.task")}</th><th>${t(locale, "table.agent")}</th><th>${t(locale, "table.session")}</th><th>${t(locale, "table.status")}</th><th>${t(locale, "table.duration")}</th><th>${t(locale, "table.heartbeat")}</th><th>${t(locale, "table.actions")}</th></tr></thead><tbody>${rows}</tbody></table></div>`}</section>
|
|
28039
|
-
${
|
|
28257
|
+
${pagination(locale, "/runs", page, totalPages, total)}`;
|
|
28040
28258
|
return c.html(renderLayout({ locale, activeTab: "runs", body }));
|
|
28041
28259
|
});
|
|
28042
28260
|
app.get("/system", async (c) => {
|
|
@@ -28143,7 +28361,11 @@ var init_web = __esm({
|
|
|
28143
28361
|
const task = await TaskService.getById(id);
|
|
28144
28362
|
if (!task) return c.json({ error: "not found" }, 404);
|
|
28145
28363
|
const runs = await TaskRunService.listByTaskId(id);
|
|
28146
|
-
return c.json({
|
|
28364
|
+
return c.json({
|
|
28365
|
+
...task,
|
|
28366
|
+
_resultPresentation: task.resultLog ? presentRunLog(task.resultLog) : null,
|
|
28367
|
+
_runs: runs
|
|
28368
|
+
});
|
|
28147
28369
|
});
|
|
28148
28370
|
app.get("/api/runs/:id", async (c) => {
|
|
28149
28371
|
const id = parsePositiveInteger2(c.req.param("id"));
|
|
@@ -28648,7 +28870,6 @@ init_db2();
|
|
|
28648
28870
|
init_duration();
|
|
28649
28871
|
init_pm2();
|
|
28650
28872
|
init_config();
|
|
28651
|
-
import { spawnSync as spawnSync4 } from "child_process";
|
|
28652
28873
|
|
|
28653
28874
|
// src/cli/i18n.ts
|
|
28654
28875
|
function requestedLanguage(argv) {
|
|
@@ -28861,6 +29082,118 @@ function parseTaskStatus(value) {
|
|
|
28861
29082
|
|
|
28862
29083
|
// src/cli/index.ts
|
|
28863
29084
|
init_update2();
|
|
29085
|
+
|
|
29086
|
+
// src/cli/doctor-smoke.ts
|
|
29087
|
+
init_task_service();
|
|
29088
|
+
init_task_run_service();
|
|
29089
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
29090
|
+
import { resolve as resolve5 } from "path";
|
|
29091
|
+
function tail(value, maxLength = 2e3) {
|
|
29092
|
+
if (!value) return null;
|
|
29093
|
+
return value.length <= maxLength ? value : value.slice(-maxLength);
|
|
29094
|
+
}
|
|
29095
|
+
function extractOpenCodeText(log) {
|
|
29096
|
+
if (!log) return "";
|
|
29097
|
+
const parts = [];
|
|
29098
|
+
for (const line of log.split("\n")) {
|
|
29099
|
+
try {
|
|
29100
|
+
const parsed = JSON.parse(line);
|
|
29101
|
+
if (parsed.type === "text" && parsed.part?.type === "text" && typeof parsed.part.text === "string") {
|
|
29102
|
+
parts.push(parsed.part.text);
|
|
29103
|
+
}
|
|
29104
|
+
} catch {
|
|
29105
|
+
}
|
|
29106
|
+
}
|
|
29107
|
+
return parts.join("\n").trim();
|
|
29108
|
+
}
|
|
29109
|
+
async function runDoctorSmoke(options) {
|
|
29110
|
+
if (!options.agent.trim()) throw new Error("smoke agent \u4E0D\u80FD\u4E3A\u7A7A");
|
|
29111
|
+
if (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 1e3 || options.timeoutMs > 6048e5) {
|
|
29112
|
+
throw new Error("smoke timeout \u5FC5\u987B\u662F 1 \u79D2\u5230 7 \u5929\u4E4B\u95F4\u7684\u6574\u6570\u6BEB\u79D2\u503C");
|
|
29113
|
+
}
|
|
29114
|
+
const cwd = resolve5(options.cwd);
|
|
29115
|
+
const marker = options.marker ?? `SUPERTASK_SMOKE_${randomUUID2().replaceAll("-", "").toUpperCase()}`;
|
|
29116
|
+
const model = options.model && options.model !== "default" ? options.model : void 0;
|
|
29117
|
+
const startedAt = Date.now();
|
|
29118
|
+
const task = await TaskService.add({
|
|
29119
|
+
name: "[doctor] Gateway real smoke test",
|
|
29120
|
+
agent: options.agent,
|
|
29121
|
+
model,
|
|
29122
|
+
prompt: `\u4E0D\u8981\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\u3002\u53EA\u56DE\u590D\u8FD9\u4E00\u884C\uFF1A${marker}`,
|
|
29123
|
+
cwd,
|
|
29124
|
+
category: "diagnostic",
|
|
29125
|
+
importance: 5,
|
|
29126
|
+
urgency: 5,
|
|
29127
|
+
batchId: `doctor-smoke-${randomUUID2()}`,
|
|
29128
|
+
maxRetries: 0,
|
|
29129
|
+
retryBackoffMs: 0,
|
|
29130
|
+
timeoutMs: options.timeoutMs
|
|
29131
|
+
});
|
|
29132
|
+
const deadline = startedAt + options.timeoutMs;
|
|
29133
|
+
const pollIntervalMs = Math.max(10, options.pollIntervalMs ?? 250);
|
|
29134
|
+
while (Date.now() < deadline) {
|
|
29135
|
+
const current2 = await TaskService.getById(task.id);
|
|
29136
|
+
const run2 = await TaskRunService.getLatestByTaskId(task.id);
|
|
29137
|
+
if (!current2) {
|
|
29138
|
+
return {
|
|
29139
|
+
ok: false,
|
|
29140
|
+
taskId: task.id,
|
|
29141
|
+
runId: run2?.id ?? null,
|
|
29142
|
+
status: "missing",
|
|
29143
|
+
agent: options.agent,
|
|
29144
|
+
model: model ?? null,
|
|
29145
|
+
cwd,
|
|
29146
|
+
durationMs: Date.now() - startedAt,
|
|
29147
|
+
error: "\u5192\u70DF\u4EFB\u52A1\u5728\u5B8C\u6210\u524D\u88AB\u5220\u9664"
|
|
29148
|
+
};
|
|
29149
|
+
}
|
|
29150
|
+
if (current2.status === "done") {
|
|
29151
|
+
const log = run2?.log ?? current2.resultLog;
|
|
29152
|
+
const markerObserved = extractOpenCodeText(log) === marker;
|
|
29153
|
+
return {
|
|
29154
|
+
ok: markerObserved,
|
|
29155
|
+
taskId: task.id,
|
|
29156
|
+
runId: run2?.id ?? null,
|
|
29157
|
+
status: current2.status,
|
|
29158
|
+
agent: options.agent,
|
|
29159
|
+
model: model ?? null,
|
|
29160
|
+
cwd,
|
|
29161
|
+
durationMs: Date.now() - startedAt,
|
|
29162
|
+
error: markerObserved ? null : "OpenCode \u5DF2\u9000\u51FA\u6210\u529F\uFF0C\u4F46\u6A21\u578B\u6587\u672C\u4E0D\u662F\u9884\u671F\u7684\u7CBE\u786E\u6807\u8BB0"
|
|
29163
|
+
};
|
|
29164
|
+
}
|
|
29165
|
+
if (current2.status === "dead_letter" || current2.status === "cancelled") {
|
|
29166
|
+
return {
|
|
29167
|
+
ok: false,
|
|
29168
|
+
taskId: task.id,
|
|
29169
|
+
runId: run2?.id ?? null,
|
|
29170
|
+
status: current2.status,
|
|
29171
|
+
agent: options.agent,
|
|
29172
|
+
model: model ?? null,
|
|
29173
|
+
cwd,
|
|
29174
|
+
durationMs: Date.now() - startedAt,
|
|
29175
|
+
error: tail(run2?.log ?? current2.resultLog) ?? `\u4EFB\u52A1\u8FDB\u5165 ${current2.status}`
|
|
29176
|
+
};
|
|
29177
|
+
}
|
|
29178
|
+
await Bun.sleep(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())));
|
|
29179
|
+
}
|
|
29180
|
+
const current = await TaskService.getById(task.id);
|
|
29181
|
+
const run = await TaskRunService.getLatestByTaskId(task.id);
|
|
29182
|
+
await TaskService.cancel(task.id);
|
|
29183
|
+
return {
|
|
29184
|
+
ok: false,
|
|
29185
|
+
taskId: task.id,
|
|
29186
|
+
runId: run?.id ?? null,
|
|
29187
|
+
status: current?.status ?? "missing",
|
|
29188
|
+
agent: options.agent,
|
|
29189
|
+
model: model ?? null,
|
|
29190
|
+
cwd,
|
|
29191
|
+
durationMs: Date.now() - startedAt,
|
|
29192
|
+
error: `\u7B49\u5F85 Gateway \u6267\u884C\u8D85\u8FC7 ${options.timeoutMs}ms\uFF0C\u5192\u70DF\u4EFB\u52A1\u5DF2\u8BF7\u6C42\u53D6\u6D88`
|
|
29193
|
+
};
|
|
29194
|
+
}
|
|
29195
|
+
|
|
29196
|
+
// src/cli/index.ts
|
|
28864
29197
|
var cliLocale = resolveCliLocale();
|
|
28865
29198
|
var t2 = (zh, en) => cliText(cliLocale, zh, en);
|
|
28866
29199
|
async function withDb(fn, formatError = (error) => JSON.stringify({
|
|
@@ -29194,25 +29527,15 @@ program2.command("config").description(t2("\u663E\u793A\u5F53\u524D\u914D\u7F6E"
|
|
|
29194
29527
|
const cfg = loadConfig2();
|
|
29195
29528
|
console.log(JSON.stringify(cfg, null, 2));
|
|
29196
29529
|
});
|
|
29197
|
-
program2.command("doctor").description(t2("\u68C0\u67E5 OpenCode\u3001\u6570\u636E\u5E93\u3001Gateway\u3001Web \u754C\u9762\u548C\u65E5\u5FD7\u8F6E\u8F6C", "diagnose OpenCode, database, Gateway, Dashboard, and log rotation")).option("--json", t2("\u5F3A\u5236\u8F93\u51FA JSON", "force JSON output")).action(async (options) => withDb(async () => {
|
|
29530
|
+
program2.command("doctor").description(t2("\u68C0\u67E5 OpenCode\u3001\u6570\u636E\u5E93\u3001Gateway\u3001Web \u754C\u9762\u548C\u65E5\u5FD7\u8F6E\u8F6C", "diagnose OpenCode, database, Gateway, Dashboard, and log rotation")).option("--json", t2("\u5F3A\u5236\u8F93\u51FA JSON", "force JSON output")).option("--smoke", t2("\u901A\u8FC7 Gateway \u63D0\u4EA4\u4E00\u4E2A\u771F\u5B9E OpenCode \u4EFB\u52A1\u5E76\u9A8C\u8BC1\u8F93\u51FA", "queue a real OpenCode task through Gateway and verify its output")).option("--smoke-agent <agent>", t2("\u771F\u5B9E\u5192\u70DF\u4EFB\u52A1\u4F7F\u7528\u7684 Agent", "Agent used by the real smoke task"), "build").option("--smoke-model <model>", t2("\u771F\u5B9E\u5192\u70DF\u4EFB\u52A1\u4F7F\u7528\u7684\u6A21\u578B\uFF1B\u9ED8\u8BA4\u8DDF\u968F Agent \u914D\u7F6E", "model used by the real smoke task; defaults to the Agent configuration")).option("--smoke-cwd <path>", t2("\u771F\u5B9E\u5192\u70DF\u4EFB\u52A1\u7684\u9879\u76EE\u76EE\u5F55\uFF1B\u9ED8\u8BA4\u4E3A\u5F53\u524D\u76EE\u5F55", "project directory for the real smoke task; defaults to the current directory")).option("--smoke-timeout <duration>", t2("\u771F\u5B9E\u5192\u70DF\u4EFB\u52A1\u7B49\u5F85\u4E0A\u9650\uFF0C\u5982 2min / 5min", "real smoke task timeout, e.g. 2min / 5min"), "3min").action(async (options) => withDb(async () => {
|
|
29198
29531
|
const config = loadConfig();
|
|
29199
29532
|
const database = DatabaseMaintenanceService.check();
|
|
29200
29533
|
const legacyQuarantinedRuns = await TaskRunService.listLegacyQuarantinedRuns(
|
|
29201
29534
|
config.watchdog.heartbeatTimeoutMs
|
|
29202
29535
|
);
|
|
29203
|
-
const gateway = getGatewayDiagnostic();
|
|
29536
|
+
const gateway = getGatewayDiagnostic({ probeOpenCode: true });
|
|
29204
29537
|
const packageVersion2 = getPackageVersion();
|
|
29205
|
-
const
|
|
29206
|
-
const opencodeResult = spawnSync4(opencodeBin2, ["--version"], {
|
|
29207
|
-
encoding: "utf8",
|
|
29208
|
-
env: process.env
|
|
29209
|
-
});
|
|
29210
|
-
const opencode = {
|
|
29211
|
-
ok: opencodeResult.status === 0,
|
|
29212
|
-
executable: opencodeBin2,
|
|
29213
|
-
version: opencodeResult.status === 0 ? opencodeResult.stdout.trim() : null,
|
|
29214
|
-
error: opencodeResult.status === 0 ? null : opencodeResult.error?.message || opencodeResult.stderr.trim() || t2(`\u9000\u51FA\u7801 ${opencodeResult.status}`, `exit code ${opencodeResult.status}`)
|
|
29215
|
-
};
|
|
29538
|
+
const opencode = diagnoseOpenCodeRuntime();
|
|
29216
29539
|
const plugin = getOpenCodePluginDiagnostic();
|
|
29217
29540
|
let dashboard = {
|
|
29218
29541
|
enabled: config.dashboard.enabled,
|
|
@@ -29284,6 +29607,12 @@ program2.command("doctor").description(t2("\u68C0\u67E5 OpenCode\u3001\u6570\u63
|
|
|
29284
29607
|
"The current CLI/OpenCode database, config, or OpenCode executable scope does not match the PM2 Gateway"
|
|
29285
29608
|
));
|
|
29286
29609
|
}
|
|
29610
|
+
if (gateway.processFound && gateway.gatewayOpenCode?.ok !== true) {
|
|
29611
|
+
warnings.push(t2(
|
|
29612
|
+
`PM2 \u4FDD\u5B58\u7684 Gateway \u73AF\u5883\u65E0\u6CD5\u6267\u884C OpenCode\uFF1A${gateway.gatewayOpenCode?.error ?? "\u65E0\u6CD5\u8BFB\u53D6\u8FD0\u884C\u73AF\u5883"}`,
|
|
29613
|
+
`The Gateway environment saved by PM2 cannot execute OpenCode: ${gateway.gatewayOpenCode?.error ?? "runtime unavailable"}`
|
|
29614
|
+
));
|
|
29615
|
+
}
|
|
29287
29616
|
for (const run of legacyQuarantinedRuns) {
|
|
29288
29617
|
const cwdHint = run.taskCwd == null ? t2("\uFF08\u65E7\u4EFB\u52A1\u6CA1\u6709 cwd\uFF0C\u8BF7\u5148\u5728 Dashboard \u53D6\u6D88\uFF09", " (the legacy task has no cwd; cancel it in the Dashboard first)") : t2(`\uFF08\u5728 ${run.taskCwd} \u6267\u884C\uFF09`, ` (run in ${run.taskCwd})`);
|
|
29289
29618
|
const cancel = run.taskStatus === "cancelled" ? "" : t2(`\u5148${cwdHint} supertask cancel --id ${run.taskId}\uFF1B`, `first${cwdHint}: supertask cancel --id ${run.taskId}; `);
|
|
@@ -29293,7 +29622,31 @@ program2.command("doctor").description(t2("\u68C0\u67E5 OpenCode\u3001\u6570\u63
|
|
|
29293
29622
|
`Legacy quarantined run #${run.runId}: ${owner}${cancel}after confirming no OpenCode process remains, run supertask run abandon --id ${run.runId} --confirm ABANDON`
|
|
29294
29623
|
));
|
|
29295
29624
|
}
|
|
29296
|
-
|
|
29625
|
+
let smoke = null;
|
|
29626
|
+
if (options.smoke) {
|
|
29627
|
+
const gatewayCanExecute = gateway.status === "online" && gateway.ready && gateway.scopeMatches && gateway.gatewayOpenCode?.ok === true;
|
|
29628
|
+
if (!gatewayCanExecute) {
|
|
29629
|
+
smoke = {
|
|
29630
|
+
ok: false,
|
|
29631
|
+
skipped: true,
|
|
29632
|
+
error: t2(
|
|
29633
|
+
"Gateway \u5C1A\u672A\u5C31\u7EEA\uFF0C\u6216\u5176 PM2 \u73AF\u5883\u65E0\u6CD5\u6267\u884C OpenCode\uFF1B\u5DF2\u8DF3\u8FC7\u771F\u5B9E\u4EFB\u52A1",
|
|
29634
|
+
"Gateway is not ready or its PM2 environment cannot execute OpenCode; the real task was skipped"
|
|
29635
|
+
)
|
|
29636
|
+
};
|
|
29637
|
+
} else {
|
|
29638
|
+
const smokeTimeoutMs = parseDuration(options.smokeTimeout);
|
|
29639
|
+
if (smokeTimeoutMs === null) throw new Error("smoke-timeout \u683C\u5F0F\u65E0\u6548");
|
|
29640
|
+
smoke = await runDoctorSmoke({
|
|
29641
|
+
agent: options.smokeAgent,
|
|
29642
|
+
model: options.smokeModel,
|
|
29643
|
+
cwd: options.smokeCwd ?? process.cwd(),
|
|
29644
|
+
timeoutMs: smokeTimeoutMs
|
|
29645
|
+
});
|
|
29646
|
+
}
|
|
29647
|
+
if (!smoke.ok) warnings.push(smoke.error ?? t2("\u771F\u5B9E\u5192\u70DF\u4EFB\u52A1\u5931\u8D25", "Real smoke task failed"));
|
|
29648
|
+
}
|
|
29649
|
+
const ok = opencode.ok && plugin.ok && database.ok && legacyQuarantinedRuns.length === 0 && gateway.pm2Installed && gateway.status === "online" && gateway.ready && gatewayEntryPinned && gatewayVersionMatchesPackage && configuredVersionsMatch && cliVersionMatchesPlugin && gateway.scopeMatches && gateway.gatewayOpenCode?.ok === true && gateway.logRotationInstalled && gateway.startupConfigured !== false && dashboard.ok && (!options.smoke || smoke?.ok === true);
|
|
29297
29650
|
const report = {
|
|
29298
29651
|
ok,
|
|
29299
29652
|
packageVersion: packageVersion2,
|
|
@@ -29305,6 +29658,7 @@ program2.command("doctor").description(t2("\u68C0\u67E5 OpenCode\u3001\u6570\u63
|
|
|
29305
29658
|
legacyQuarantinedRuns,
|
|
29306
29659
|
gateway,
|
|
29307
29660
|
dashboard,
|
|
29661
|
+
smoke,
|
|
29308
29662
|
warnings
|
|
29309
29663
|
};
|
|
29310
29664
|
const json = options.json || !process.stdout.isTTY;
|
|
@@ -29314,10 +29668,15 @@ program2.command("doctor").description(t2("\u68C0\u67E5 OpenCode\u3001\u6570\u63
|
|
|
29314
29668
|
const mark = (value) => value ? "\u2713" : "\u2717";
|
|
29315
29669
|
console.log(`SuperTask doctor: ${ok ? t2("\u6B63\u5E38", "healthy") : t2("\u5F02\u5E38", "unhealthy")}`);
|
|
29316
29670
|
console.log(`${mark(opencode.ok)} OpenCode ${opencode.version ?? opencode.error ?? t2("\u4E0D\u53EF\u7528", "unavailable")}`);
|
|
29671
|
+
console.log(`${mark(gateway.gatewayOpenCode?.ok === true)} ${t2("Gateway \u73AF\u5883\u4E2D\u7684 OpenCode", "OpenCode in Gateway environment")} ${gateway.gatewayOpenCode?.version ?? gateway.gatewayOpenCode?.error ?? t2("\u4E0D\u53EF\u7528", "unavailable")}${gateway.gatewayOpenCode?.executable ? `\uFF0C${gateway.gatewayOpenCode.executable}` : ""}`);
|
|
29317
29672
|
console.log(`${mark(plugin.ok)} ${t2("OpenCode \u63D2\u4EF6", "OpenCode plugin")} ${plugin.spec || plugin.error || t2("\u672A\u914D\u7F6E", "not configured")}${plugin.cachedVersion ? t2(`\uFF08\u7F13\u5B58 v${plugin.cachedVersion}\uFF09`, ` (cached v${plugin.cachedVersion})`) : ""}`);
|
|
29318
29673
|
console.log(`${mark(database.ok)} ${t2("\u6570\u636E\u5E93", "Database")} ${database.path}${t2(`\uFF08\u4EFB\u52A1 ${database.counts.tasks}\uFF0C\u8FD0\u884C\u4E2D ${database.runningTasks}\uFF09`, ` (tasks ${database.counts.tasks}, running ${database.runningTasks})`)}`);
|
|
29319
29674
|
console.log(`${mark(gateway.status === "online" && gateway.ready && gatewayEntryPinned && gatewayVersionMatchesPackage)} Gateway ${gateway.status ?? "missing"}${gateway.pid ? `\uFF0CPID ${gateway.pid}` : ""}${gateway.runningVersion ? `\uFF0Cv${gateway.runningVersion}` : ""}${gateway.gatewayEntry ? `\uFF0C${gateway.gatewayEntry}` : ""}`);
|
|
29320
29675
|
console.log(`${mark(dashboard.ok)} Dashboard ${dashboard.enabled ? dashboard.url : t2("\u5DF2\u7981\u7528", "disabled")}`);
|
|
29676
|
+
if (options.smoke) {
|
|
29677
|
+
const smokeSummary = smoke && "taskId" in smoke ? `task #${smoke.taskId}${smoke.runId ? ` / run #${smoke.runId}` : ""}\uFF0C${smoke.durationMs}ms` : smoke?.error ?? t2("\u672A\u6267\u884C", "not run");
|
|
29678
|
+
console.log(`${mark(smoke?.ok === true)} ${t2("\u771F\u5B9E Gateway \u5192\u70DF\u4EFB\u52A1", "Real Gateway smoke task")} ${smokeSummary}`);
|
|
29679
|
+
}
|
|
29321
29680
|
for (const warning of warnings) console.log(`! ${warning}`);
|
|
29322
29681
|
}
|
|
29323
29682
|
if (!ok) process.exitCode = 1;
|