@csdwd/ai-teams-server 0.3.4 → 0.3.6
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/dist/index.js
CHANGED
|
@@ -36,7 +36,9 @@ function parseLeaderToServerMessage(value) {
|
|
|
36
36
|
atAgents: agentTargetField(message, "atAgents"),
|
|
37
37
|
prompt: nonEmptyStringField(message, "prompt"),
|
|
38
38
|
workspace: optionalStringField(message, "workspace"),
|
|
39
|
-
timeoutSec: optionalPositiveNumberField(message, "timeoutSec")
|
|
39
|
+
timeoutSec: optionalPositiveNumberField(message, "timeoutSec"),
|
|
40
|
+
priority: optionalNonNegativeNumberField(message, "priority"),
|
|
41
|
+
requiredLabels: optionalStringArrayField(message, "requiredLabels")
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
if (type === "command.send") {
|
|
@@ -175,6 +177,15 @@ function stringArrayField(record, key) {
|
|
|
175
177
|
}
|
|
176
178
|
return value;
|
|
177
179
|
}
|
|
180
|
+
function optionalStringArrayField(record, key) {
|
|
181
|
+
const value = record[key];
|
|
182
|
+
if (value === void 0)
|
|
183
|
+
return void 0;
|
|
184
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
185
|
+
throw new ProtocolError(`${key} must be a string array.`);
|
|
186
|
+
}
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
178
189
|
function agentTargetField(record, key) {
|
|
179
190
|
const value = record[key];
|
|
180
191
|
if (value === "queue") {
|
|
@@ -508,7 +519,8 @@ async function initDb(db) {
|
|
|
508
519
|
usage_input_tokens INTEGER,
|
|
509
520
|
usage_output_tokens INTEGER,
|
|
510
521
|
usage_cache_read_tokens INTEGER,
|
|
511
|
-
usage_cache_creation_tokens INTEGER
|
|
522
|
+
usage_cache_creation_tokens INTEGER,
|
|
523
|
+
retry_count INTEGER NOT NULL DEFAULT 0
|
|
512
524
|
)
|
|
513
525
|
`);
|
|
514
526
|
await db.run(`
|
|
@@ -559,6 +571,10 @@ async function initDb(db) {
|
|
|
559
571
|
updated_at TEXT NOT NULL
|
|
560
572
|
)
|
|
561
573
|
`);
|
|
574
|
+
try {
|
|
575
|
+
await db.run("ALTER TABLE tasks ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0");
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
562
578
|
}
|
|
563
579
|
async function hydrateState(db, state, defaultTimeoutSec, maxLogChunksPerTask) {
|
|
564
580
|
const employeeRows = await db.all("SELECT payload_json FROM employees");
|
|
@@ -575,13 +591,13 @@ async function hydrateState(db, state, defaultTimeoutSec, maxLogChunksPerTask) {
|
|
|
575
591
|
if (task.targetMode === "queue" || !task.employeeId) {
|
|
576
592
|
state.sharedTaskQueue.push(task.id);
|
|
577
593
|
} else {
|
|
578
|
-
const queue = state.
|
|
594
|
+
const queue = state.mainTaskQueues.get(task.employeeId) ?? [];
|
|
579
595
|
queue.push(task.id);
|
|
580
|
-
state.
|
|
596
|
+
state.mainTaskQueues.set(task.employeeId, queue);
|
|
581
597
|
}
|
|
582
598
|
} else if (task.employeeId && !TERMINAL_STATUSES.has(task.status)) {
|
|
583
599
|
task.status = "queued";
|
|
584
|
-
persistTask(db, task);
|
|
600
|
+
await persistTask(db, task);
|
|
585
601
|
if (task.targetMode === "queue") {
|
|
586
602
|
state.sharedTaskQueue.push(task.id);
|
|
587
603
|
} else {
|
|
@@ -637,6 +653,7 @@ var TASK_COLUMNS = [
|
|
|
637
653
|
"cli_config",
|
|
638
654
|
"priority",
|
|
639
655
|
"required_labels",
|
|
656
|
+
"retry_count",
|
|
640
657
|
"created_at",
|
|
641
658
|
"started_at",
|
|
642
659
|
"finished_at",
|
|
@@ -731,11 +748,38 @@ async function deleteTask(db, taskId) {
|
|
|
731
748
|
await db.run("DELETE FROM tasks WHERE id = $1", [taskId]);
|
|
732
749
|
return true;
|
|
733
750
|
}
|
|
751
|
+
var UPDATABLE_TASK_FIELDS = /* @__PURE__ */ new Set([
|
|
752
|
+
"status",
|
|
753
|
+
"timeoutSec",
|
|
754
|
+
"cliConfig",
|
|
755
|
+
"priority",
|
|
756
|
+
"requiredLabels",
|
|
757
|
+
"prompt",
|
|
758
|
+
"workspace",
|
|
759
|
+
"employeeId",
|
|
760
|
+
"sessionId",
|
|
761
|
+
"exitCode",
|
|
762
|
+
"summary",
|
|
763
|
+
"error",
|
|
764
|
+
"durationMs",
|
|
765
|
+
"durationApiMs",
|
|
766
|
+
"numTurns",
|
|
767
|
+
"totalCostUsd",
|
|
768
|
+
"usageInputTokens",
|
|
769
|
+
"usageOutputTokens",
|
|
770
|
+
"usageCacheReadTokens",
|
|
771
|
+
"usageCacheCreationTokens"
|
|
772
|
+
]);
|
|
734
773
|
async function updateTaskFields(db, taskId, fields) {
|
|
735
|
-
const entries = Object.entries(fields);
|
|
774
|
+
const entries = Object.entries(fields).filter(([key]) => UPDATABLE_TASK_FIELDS.has(key));
|
|
736
775
|
if (entries.length === 0) return getTaskById(db, taskId);
|
|
737
776
|
const setClauses = entries.map(([key], i) => `${toSnakeCase(key)} = $${i + 2}`).join(", ");
|
|
738
|
-
const values = entries.map(([, val]) =>
|
|
777
|
+
const values = entries.map(([key, val]) => {
|
|
778
|
+
if ((key === "cliConfig" || key === "requiredLabels") && val != null) {
|
|
779
|
+
return JSON.stringify(val);
|
|
780
|
+
}
|
|
781
|
+
return val;
|
|
782
|
+
});
|
|
739
783
|
await db.run(
|
|
740
784
|
`UPDATE tasks SET ${setClauses} WHERE id = $1`,
|
|
741
785
|
[taskId, ...values]
|
|
@@ -756,6 +800,7 @@ function dbRowToTask(row, defaultTimeoutSec) {
|
|
|
756
800
|
priority: row.priority ?? 1,
|
|
757
801
|
requiredLabels: row.required_labels ? JSON.parse(row.required_labels) : null,
|
|
758
802
|
status: row.status || "queued",
|
|
803
|
+
retryCount: row.retry_count ?? 0,
|
|
759
804
|
createdAt: row.created_at,
|
|
760
805
|
startedAt: row.started_at,
|
|
761
806
|
finishedAt: row.finished_at,
|
|
@@ -786,6 +831,7 @@ function taskToDbValues(task) {
|
|
|
786
831
|
task.cliConfig ? JSON.stringify(task.cliConfig) : null,
|
|
787
832
|
task.priority,
|
|
788
833
|
task.requiredLabels ? JSON.stringify(task.requiredLabels) : null,
|
|
834
|
+
task.retryCount,
|
|
789
835
|
task.createdAt,
|
|
790
836
|
task.startedAt,
|
|
791
837
|
task.finishedAt,
|
|
@@ -1212,12 +1258,12 @@ var scheduleResponseSchema = {
|
|
|
1212
1258
|
targetMode: { type: "string", enum: ["queue", "direct", "broadcast"] },
|
|
1213
1259
|
targetAgents: { type: "array", items: { type: "string" } },
|
|
1214
1260
|
prompt: { type: "string" },
|
|
1215
|
-
workspace:
|
|
1216
|
-
timeoutSec:
|
|
1261
|
+
workspace: nullableString,
|
|
1262
|
+
timeoutSec: nullableNumber,
|
|
1217
1263
|
priority: { type: "integer" },
|
|
1218
|
-
requiredLabels: { type: "array", items: { type: "string" } },
|
|
1219
|
-
lastRunAt:
|
|
1220
|
-
nextRunAt:
|
|
1264
|
+
requiredLabels: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "null" }] },
|
|
1265
|
+
lastRunAt: nullableString,
|
|
1266
|
+
nextRunAt: nullableString,
|
|
1221
1267
|
createdAt: { type: "string" },
|
|
1222
1268
|
updatedAt: { type: "string" }
|
|
1223
1269
|
}
|
|
@@ -1248,6 +1294,7 @@ function extractDoneSessionId(content) {
|
|
|
1248
1294
|
}
|
|
1249
1295
|
function createDispatch(ctx) {
|
|
1250
1296
|
const { state, db, log } = ctx;
|
|
1297
|
+
const retryDelays = /* @__PURE__ */ new Map();
|
|
1251
1298
|
function broadcastToLeaders(payload) {
|
|
1252
1299
|
for (const socket of state.leaderSockets) {
|
|
1253
1300
|
sendJson(socket, payload, ctx.encryptor);
|
|
@@ -1515,13 +1562,14 @@ function createDispatch(ctx) {
|
|
|
1515
1562
|
if (!employee || employee.queueTaskId || employee.status !== "online") {
|
|
1516
1563
|
return;
|
|
1517
1564
|
}
|
|
1518
|
-
|
|
1519
|
-
const taskId = state.sharedTaskQueue
|
|
1565
|
+
for (let i = 0; i < state.sharedTaskQueue.length; i++) {
|
|
1566
|
+
const taskId = state.sharedTaskQueue[i];
|
|
1520
1567
|
const task = state.tasks.get(taskId);
|
|
1521
|
-
if (task
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1568
|
+
if (!task || task.status !== "queued" || task.targetMode !== "queue") continue;
|
|
1569
|
+
if (retryDelays.has(taskId)) continue;
|
|
1570
|
+
state.sharedTaskQueue.splice(i, 1);
|
|
1571
|
+
dispatchTask(task, employeeId);
|
|
1572
|
+
return;
|
|
1525
1573
|
}
|
|
1526
1574
|
}
|
|
1527
1575
|
function dispatchSharedQueuedTasks() {
|
|
@@ -1639,6 +1687,7 @@ function createDispatch(ctx) {
|
|
|
1639
1687
|
priority: priority ?? 1,
|
|
1640
1688
|
requiredLabels: requiredLabels ?? null,
|
|
1641
1689
|
status: "queued",
|
|
1690
|
+
retryCount: 0,
|
|
1642
1691
|
createdAt: nowIso(),
|
|
1643
1692
|
startedAt: null,
|
|
1644
1693
|
finishedAt: null,
|
|
@@ -1679,11 +1728,13 @@ function createDispatch(ctx) {
|
|
|
1679
1728
|
}
|
|
1680
1729
|
function dispatchLeaderCommand(message, webhookUrl, cliConfig, priority, requiredLabels) {
|
|
1681
1730
|
const leaderCommandId = randomUUID();
|
|
1731
|
+
const resolvedPriority = message.priority ?? priority;
|
|
1732
|
+
const resolvedRequiredLabels = message.requiredLabels ?? requiredLabels;
|
|
1682
1733
|
if (message.atAgents === "queue") {
|
|
1683
1734
|
return {
|
|
1684
1735
|
ok: true,
|
|
1685
1736
|
leaderCommandId,
|
|
1686
|
-
tasks: [createTask(null, message.prompt, message.workspace, message.timeoutSec, leaderCommandId, "queue", webhookUrl, cliConfig,
|
|
1737
|
+
tasks: [createTask(null, message.prompt, message.workspace, message.timeoutSec, leaderCommandId, "queue", webhookUrl, cliConfig, resolvedPriority, resolvedRequiredLabels)]
|
|
1687
1738
|
};
|
|
1688
1739
|
}
|
|
1689
1740
|
const targetIds = resolveTargetIds(message.atAgents);
|
|
@@ -1699,7 +1750,7 @@ function createDispatch(ctx) {
|
|
|
1699
1750
|
ok: true,
|
|
1700
1751
|
leaderCommandId,
|
|
1701
1752
|
tasks: targetIds.map(
|
|
1702
|
-
(employeeId) => createTask(employeeId, message.prompt, message.workspace, message.timeoutSec, leaderCommandId, targetMode, webhookUrl, cliConfig,
|
|
1753
|
+
(employeeId) => createTask(employeeId, message.prompt, message.workspace, message.timeoutSec, leaderCommandId, targetMode, webhookUrl, cliConfig, resolvedPriority, resolvedRequiredLabels)
|
|
1703
1754
|
)
|
|
1704
1755
|
};
|
|
1705
1756
|
}
|
|
@@ -1714,6 +1765,15 @@ function createDispatch(ctx) {
|
|
|
1714
1765
|
clearTimeout(disconnectTimer);
|
|
1715
1766
|
state.disconnectTimers.delete(message.employeeId);
|
|
1716
1767
|
}
|
|
1768
|
+
for (const prevTaskId of [previousMainTaskId, previousQueueTaskId]) {
|
|
1769
|
+
if (prevTaskId) {
|
|
1770
|
+
const timer = retryDelays.get(prevTaskId);
|
|
1771
|
+
if (timer) {
|
|
1772
|
+
clearTimeout(timer);
|
|
1773
|
+
retryDelays.delete(prevTaskId);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1717
1777
|
state.agentSockets.set(message.employeeId, socket);
|
|
1718
1778
|
state.socketToEmployeeId.set(socket, message.employeeId);
|
|
1719
1779
|
let mainTaskId = null;
|
|
@@ -1757,6 +1817,7 @@ function createDispatch(ctx) {
|
|
|
1757
1817
|
if (!queueTaskId) {
|
|
1758
1818
|
dispatchNextQueuedTask(message.employeeId);
|
|
1759
1819
|
}
|
|
1820
|
+
dispatchSharedQueuedTasks();
|
|
1760
1821
|
resetHeartbeatTimer(message.employeeId);
|
|
1761
1822
|
}
|
|
1762
1823
|
function taskBelongsToSocket(taskId, employeeId, socket) {
|
|
@@ -1983,10 +2044,19 @@ function createDispatch(ctx) {
|
|
|
1983
2044
|
const task = state.tasks.get(taskId);
|
|
1984
2045
|
if (!task || TERMINAL_STATUSES.has(task.status)) continue;
|
|
1985
2046
|
clearTaskTimeout(taskId);
|
|
1986
|
-
task.status = "queued";
|
|
1987
2047
|
task.employeeId = null;
|
|
2048
|
+
task.retryCount += 1;
|
|
2049
|
+
if (task.retryCount > 3) {
|
|
2050
|
+
task.status = "failed";
|
|
2051
|
+
task.error = `\u4EFB\u52A1\u91CD\u8BD5\u6B21\u6570\u8D85\u8FC7\u4E0A\u9650\uFF083\u6B21\uFF09\uFF0C\u5DF2\u7EC8\u6B62\u3002`;
|
|
2052
|
+
task.finishedAt = nowIso();
|
|
2053
|
+
upsertTask(task);
|
|
2054
|
+
log.warn({ taskId, employeeId, retryCount: task.retryCount }, "Task exceeded max retry count, marked failed");
|
|
2055
|
+
continue;
|
|
2056
|
+
}
|
|
2057
|
+
task.status = "queued";
|
|
1988
2058
|
upsertTask(task);
|
|
1989
|
-
log.info({ taskId, employeeId }, "Task re-queued after disconnect grace period");
|
|
2059
|
+
log.info({ taskId, employeeId, retryCount: task.retryCount }, "Task re-queued after disconnect grace period");
|
|
1990
2060
|
if (task.targetMode === "queue") {
|
|
1991
2061
|
enqueueSharedTask(taskId);
|
|
1992
2062
|
} else {
|
|
@@ -1994,6 +2064,16 @@ function createDispatch(ctx) {
|
|
|
1994
2064
|
if (!queue.includes(taskId)) queue.push(taskId);
|
|
1995
2065
|
state.mainTaskQueues.set(employeeId, queue);
|
|
1996
2066
|
}
|
|
2067
|
+
if (task.retryCount > 1) {
|
|
2068
|
+
const retryDelay = (task.retryCount - 1) * 5e3;
|
|
2069
|
+
const existingDelay = retryDelays.get(taskId);
|
|
2070
|
+
if (existingDelay) clearTimeout(existingDelay);
|
|
2071
|
+
retryDelays.set(taskId, setTimeout(() => {
|
|
2072
|
+
retryDelays.delete(taskId);
|
|
2073
|
+
dispatchSharedQueuedTasks();
|
|
2074
|
+
dispatchNextMainQueuedTask(employeeId);
|
|
2075
|
+
}, retryDelay));
|
|
2076
|
+
}
|
|
1997
2077
|
}
|
|
1998
2078
|
const emp = state.employees.get(employeeId);
|
|
1999
2079
|
if (emp) {
|
|
@@ -2014,7 +2094,13 @@ function createDispatch(ctx) {
|
|
|
2014
2094
|
handleAgentMessage,
|
|
2015
2095
|
handleLeaderMessage,
|
|
2016
2096
|
cancelTaskById,
|
|
2017
|
-
startDisconnectRecovery
|
|
2097
|
+
startDisconnectRecovery,
|
|
2098
|
+
cleanup() {
|
|
2099
|
+
for (const timer of retryDelays.values()) {
|
|
2100
|
+
clearTimeout(timer);
|
|
2101
|
+
}
|
|
2102
|
+
retryDelays.clear();
|
|
2103
|
+
}
|
|
2018
2104
|
};
|
|
2019
2105
|
}
|
|
2020
2106
|
|
|
@@ -2121,6 +2207,25 @@ function stopScheduleJob(jobs, scheduleId) {
|
|
|
2121
2207
|
}
|
|
2122
2208
|
|
|
2123
2209
|
// src/index.ts
|
|
2210
|
+
function scheduleToResponse(s) {
|
|
2211
|
+
return {
|
|
2212
|
+
id: s.id,
|
|
2213
|
+
name: s.name,
|
|
2214
|
+
cron: s.cronExpr,
|
|
2215
|
+
enabled: s.enabled,
|
|
2216
|
+
targetMode: s.targetMode,
|
|
2217
|
+
targetAgents: s.targetAgents,
|
|
2218
|
+
prompt: s.prompt,
|
|
2219
|
+
workspace: s.workspace,
|
|
2220
|
+
timeoutSec: s.timeoutSec,
|
|
2221
|
+
priority: s.priority,
|
|
2222
|
+
requiredLabels: s.requiredLabels,
|
|
2223
|
+
lastRunAt: s.lastRunAt,
|
|
2224
|
+
nextRunAt: s.nextRunAt,
|
|
2225
|
+
createdAt: s.createdAt,
|
|
2226
|
+
updatedAt: s.updatedAt
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2124
2229
|
var DEFAULT_PORT = 3789;
|
|
2125
2230
|
function isAuthorized(authToken, rawUrl, headers) {
|
|
2126
2231
|
const bearer = typeof headers.authorization === "string" ? headers.authorization : "";
|
|
@@ -2208,7 +2313,7 @@ async function createAiTeamsServer(options) {
|
|
|
2208
2313
|
disconnectGraceMs,
|
|
2209
2314
|
encryptor: createEncryptor(process.env.AI_TEAMS_ENCRYPTION_KEY)
|
|
2210
2315
|
};
|
|
2211
|
-
const { dispatchLeaderCommand, handleAgentMessage, handleLeaderMessage, cancelTaskById, startDisconnectRecovery } = createDispatch(dispatchCtx);
|
|
2316
|
+
const { dispatchLeaderCommand, handleAgentMessage, handleLeaderMessage, cancelTaskById, startDisconnectRecovery, cleanup: dispatchCleanup } = createDispatch(dispatchCtx);
|
|
2212
2317
|
const scheduleDispatchFn = (message, webhookUrl, cliConfig, priority, requiredLabels) => {
|
|
2213
2318
|
return dispatchLeaderCommand(message, webhookUrl, cliConfig, priority, requiredLabels);
|
|
2214
2319
|
};
|
|
@@ -2653,7 +2758,7 @@ async function createAiTeamsServer(options) {
|
|
|
2653
2758
|
},
|
|
2654
2759
|
async () => {
|
|
2655
2760
|
const schedules = await getAllSchedules(db);
|
|
2656
|
-
return { schedules };
|
|
2761
|
+
return { schedules: schedules.map(scheduleToResponse) };
|
|
2657
2762
|
}
|
|
2658
2763
|
);
|
|
2659
2764
|
app.get(
|
|
@@ -2669,7 +2774,7 @@ async function createAiTeamsServer(options) {
|
|
|
2669
2774
|
async (request, reply) => {
|
|
2670
2775
|
const schedule = await getScheduleById(db, request.params.scheduleId);
|
|
2671
2776
|
if (!schedule) return reply.code(404).send({ error: "Schedule not found." });
|
|
2672
|
-
return schedule;
|
|
2777
|
+
return scheduleToResponse(schedule);
|
|
2673
2778
|
}
|
|
2674
2779
|
);
|
|
2675
2780
|
app.post(
|
|
@@ -2709,7 +2814,7 @@ async function createAiTeamsServer(options) {
|
|
|
2709
2814
|
schedule.nextRunAt = state.scheduleJobs.get(id)?.nextDate()?.toISO() ?? null;
|
|
2710
2815
|
}
|
|
2711
2816
|
await upsertSchedule(db, schedule);
|
|
2712
|
-
return reply.code(201).send(schedule);
|
|
2817
|
+
return reply.code(201).send(scheduleToResponse(schedule));
|
|
2713
2818
|
} catch (err) {
|
|
2714
2819
|
stopScheduleJob(state.scheduleJobs, id);
|
|
2715
2820
|
return reply.code(400).send({ error: err instanceof Error ? err.message : "Invalid schedule." });
|
|
@@ -2724,7 +2829,7 @@ async function createAiTeamsServer(options) {
|
|
|
2724
2829
|
summary: "Update a schedule",
|
|
2725
2830
|
params: { type: "object", required: ["scheduleId"], properties: { scheduleId: { type: "string", minLength: 1 } } },
|
|
2726
2831
|
body: updateScheduleRequestSchema,
|
|
2727
|
-
response: { 200: scheduleResponseSchema, 404: errorResponseSchema, 401: errorResponseSchema }
|
|
2832
|
+
response: { 200: scheduleResponseSchema, 400: errorResponseSchema, 404: errorResponseSchema, 401: errorResponseSchema }
|
|
2728
2833
|
}
|
|
2729
2834
|
},
|
|
2730
2835
|
async (request, reply) => {
|
|
@@ -2743,13 +2848,18 @@ async function createAiTeamsServer(options) {
|
|
|
2743
2848
|
if (request.body.requiredLabels !== void 0) fields.requiredLabels = request.body.requiredLabels;
|
|
2744
2849
|
const updated = await updateScheduleFields(db, request.params.scheduleId, fields);
|
|
2745
2850
|
if (!updated) return reply.code(404).send({ error: "Schedule not found." });
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2851
|
+
try {
|
|
2852
|
+
stopScheduleJob(state.scheduleJobs, request.params.scheduleId);
|
|
2853
|
+
if (updated.enabled) {
|
|
2854
|
+
startScheduleJob(updated, scheduleDispatchFn, state.scheduleJobs, onScheduleFire);
|
|
2855
|
+
updated.nextRunAt = state.scheduleJobs.get(request.params.scheduleId)?.nextDate()?.toISO() ?? null;
|
|
2856
|
+
await updateScheduleFields(db, request.params.scheduleId, { nextRunAt: updated.nextRunAt });
|
|
2857
|
+
}
|
|
2858
|
+
return scheduleToResponse(updated);
|
|
2859
|
+
} catch (err) {
|
|
2860
|
+
stopScheduleJob(state.scheduleJobs, request.params.scheduleId);
|
|
2861
|
+
return reply.code(400).send({ error: err instanceof Error ? err.message : "Invalid schedule." });
|
|
2751
2862
|
}
|
|
2752
|
-
return updated;
|
|
2753
2863
|
}
|
|
2754
2864
|
);
|
|
2755
2865
|
app.delete(
|
|
@@ -2780,7 +2890,7 @@ async function createAiTeamsServer(options) {
|
|
|
2780
2890
|
tags: ["schedules"],
|
|
2781
2891
|
summary: "Manually trigger a schedule",
|
|
2782
2892
|
params: { type: "object", required: ["scheduleId"], properties: { scheduleId: { type: "string", minLength: 1 } } },
|
|
2783
|
-
response: { 200: scheduleResponseSchema, 404: errorResponseSchema, 401: errorResponseSchema }
|
|
2893
|
+
response: { 200: scheduleResponseSchema, 400: errorResponseSchema, 404: errorResponseSchema, 401: errorResponseSchema }
|
|
2784
2894
|
}
|
|
2785
2895
|
},
|
|
2786
2896
|
async (request, reply) => {
|
|
@@ -2796,7 +2906,9 @@ async function createAiTeamsServer(options) {
|
|
|
2796
2906
|
);
|
|
2797
2907
|
if (!result.ok) return reply.code(400).send({ error: result.message });
|
|
2798
2908
|
await onScheduleFire(request.params.scheduleId);
|
|
2799
|
-
|
|
2909
|
+
const updated = await getScheduleById(db, request.params.scheduleId);
|
|
2910
|
+
if (!updated) return reply.code(404).send({ error: "Schedule not found." });
|
|
2911
|
+
return scheduleToResponse(updated);
|
|
2800
2912
|
}
|
|
2801
2913
|
);
|
|
2802
2914
|
app.get("/ws/agent", { websocket: true }, (socket, request) => {
|
|
@@ -2886,8 +2998,9 @@ async function createAiTeamsServer(options) {
|
|
|
2886
2998
|
for (const timer of state.heartbeatTimers.values()) {
|
|
2887
2999
|
clearTimeout(timer);
|
|
2888
3000
|
}
|
|
3001
|
+
dispatchCleanup();
|
|
2889
3002
|
await app.close();
|
|
2890
|
-
db.close();
|
|
3003
|
+
await db.close();
|
|
2891
3004
|
}
|
|
2892
3005
|
};
|
|
2893
3006
|
}
|
|
@@ -2965,7 +3078,7 @@ if (isCli) {
|
|
|
2965
3078
|
getArgValue2 = getArgValue, loadServerConfig2 = loadServerConfig, saveServerConfig2 = saveServerConfig, resolveDataDir2 = resolveDataDir, resolvePidFile2 = resolvePidFile, resolveLogDir2 = resolveLogDir, applyCliArgsToEnv2 = applyCliArgsToEnv;
|
|
2966
3079
|
const args = process.argv.slice(2);
|
|
2967
3080
|
if (args.includes("--version") || args.includes("-v")) {
|
|
2968
|
-
console.log("0.3.
|
|
3081
|
+
console.log("0.3.6");
|
|
2969
3082
|
process.exit(0);
|
|
2970
3083
|
}
|
|
2971
3084
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -3000,19 +3113,21 @@ if (isCli) {
|
|
|
3000
3113
|
const subcommand = args[0];
|
|
3001
3114
|
if (subcommand === "start" || subcommand === "restart") {
|
|
3002
3115
|
void (async () => {
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
if (
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3116
|
+
applyCliArgsToEnv();
|
|
3117
|
+
if (!process.env.__AI_TEAMS_DAEMON_WATCHDOG && !process.env.__AI_TEAMS_DAEMON_WORKER) {
|
|
3118
|
+
if (subcommand === "restart") {
|
|
3119
|
+
const status = getDaemonStatus(resolvePidFile());
|
|
3120
|
+
if (status.running) {
|
|
3121
|
+
await stopDaemon(resolvePidFile());
|
|
3122
|
+
}
|
|
3123
|
+
} else {
|
|
3124
|
+
const status = getDaemonStatus(resolvePidFile());
|
|
3125
|
+
if (status.running) {
|
|
3126
|
+
console.log(`Already running (PID ${status.pid}).`);
|
|
3127
|
+
process.exit(0);
|
|
3128
|
+
}
|
|
3013
3129
|
}
|
|
3014
3130
|
}
|
|
3015
|
-
applyCliArgsToEnv();
|
|
3016
3131
|
if (!process.env.AI_TEAMS_AUTH_TOKEN) {
|
|
3017
3132
|
console.error("\u9519\u8BEF: \u9700\u8981\u8BA4\u8BC1 Token\u3002\u4F7F\u7528 --token <token> \u6216\u8BBE\u7F6E AI_TEAMS_AUTH_TOKEN \u73AF\u5883\u53D8\u91CF\u3002");
|
|
3018
3133
|
process.exit(1);
|