@csdwd/ai-teams-server 0.2.0 → 0.3.1
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
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import os from "node:os";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import path2 from "node:path";
|
|
6
|
+
import fs2 from "node:fs";
|
|
7
7
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import Fastify from "fastify";
|
|
@@ -231,6 +231,171 @@ function parseEncryptionKey(hex) {
|
|
|
231
231
|
return key;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
// ../../packages/shared/dist/daemon.js
|
|
235
|
+
import fs from "node:fs";
|
|
236
|
+
import path from "node:path";
|
|
237
|
+
import { spawn } from "node:child_process";
|
|
238
|
+
function readPidFile(pidFile) {
|
|
239
|
+
try {
|
|
240
|
+
const content = fs.readFileSync(pidFile, "utf-8").trim();
|
|
241
|
+
const pid = Number(content);
|
|
242
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
243
|
+
} catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function writePidFile(pidFile, pid) {
|
|
248
|
+
fs.mkdirSync(path.dirname(pidFile), { recursive: true });
|
|
249
|
+
fs.writeFileSync(pidFile, String(pid), "utf-8");
|
|
250
|
+
}
|
|
251
|
+
function removePidFile(pidFile) {
|
|
252
|
+
try {
|
|
253
|
+
fs.unlinkSync(pidFile);
|
|
254
|
+
} catch {
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function isProcessRunning(pid) {
|
|
258
|
+
try {
|
|
259
|
+
process.kill(pid, 0);
|
|
260
|
+
return true;
|
|
261
|
+
} catch {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function spawnWorker(script, args, logDir) {
|
|
266
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
267
|
+
const logFile = path.join(logDir, "worker.log");
|
|
268
|
+
const logStream = fs.openSync(logFile, "a");
|
|
269
|
+
const child = spawn(process.execPath, [script, ...args], {
|
|
270
|
+
stdio: ["ignore", logStream, logStream],
|
|
271
|
+
env: {
|
|
272
|
+
...process.env,
|
|
273
|
+
__AI_TEAMS_DAEMON_WORKER: "1",
|
|
274
|
+
__AI_TEAMS_DAEMON_WATCHDOG: void 0
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
fs.closeSync(logStream);
|
|
278
|
+
return child;
|
|
279
|
+
}
|
|
280
|
+
var MAX_RESTARTS_PER_MINUTE = 10;
|
|
281
|
+
var RESTART_DELAY_MS = 3e3;
|
|
282
|
+
async function runWatchdog(opts) {
|
|
283
|
+
const { name, pidFile, logDir, workerScript, workerArgs } = opts;
|
|
284
|
+
writePidFile(pidFile, process.pid);
|
|
285
|
+
let restartTimestamps = [];
|
|
286
|
+
function cleanupAndExit(code) {
|
|
287
|
+
removePidFile(pidFile);
|
|
288
|
+
process.exit(code);
|
|
289
|
+
}
|
|
290
|
+
let worker = null;
|
|
291
|
+
let shuttingDown = false;
|
|
292
|
+
function shutdown() {
|
|
293
|
+
if (shuttingDown)
|
|
294
|
+
return;
|
|
295
|
+
shuttingDown = true;
|
|
296
|
+
if (worker)
|
|
297
|
+
worker.kill("SIGTERM");
|
|
298
|
+
}
|
|
299
|
+
process.on("SIGTERM", shutdown);
|
|
300
|
+
process.on("SIGINT", shutdown);
|
|
301
|
+
while (!shuttingDown) {
|
|
302
|
+
await new Promise((resolve) => {
|
|
303
|
+
worker = spawnWorker(workerScript, workerArgs, logDir);
|
|
304
|
+
worker.on("exit", (code, signal) => {
|
|
305
|
+
worker = null;
|
|
306
|
+
if (shuttingDown) {
|
|
307
|
+
cleanupAndExit(0);
|
|
308
|
+
}
|
|
309
|
+
if (code === 0) {
|
|
310
|
+
console.log(`${name}: worker exited cleanly, daemon stopping.`);
|
|
311
|
+
cleanupAndExit(0);
|
|
312
|
+
}
|
|
313
|
+
const now = Date.now();
|
|
314
|
+
restartTimestamps = restartTimestamps.filter((t) => now - t < 6e4);
|
|
315
|
+
restartTimestamps.push(now);
|
|
316
|
+
if (restartTimestamps.length > MAX_RESTARTS_PER_MINUTE) {
|
|
317
|
+
console.error(`${name}: exceeded ${MAX_RESTARTS_PER_MINUTE} restarts/minute, daemon stopping.`);
|
|
318
|
+
cleanupAndExit(1);
|
|
319
|
+
}
|
|
320
|
+
console.log(`${name}: worker crashed (code=${code}, signal=${signal}), restarting in ${RESTART_DELAY_MS}ms...`);
|
|
321
|
+
setTimeout(resolve, RESTART_DELAY_MS);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
cleanupAndExit(0);
|
|
326
|
+
}
|
|
327
|
+
function getDaemonStatus(pidFile) {
|
|
328
|
+
const pid = readPidFile(pidFile);
|
|
329
|
+
if (pid === null) {
|
|
330
|
+
return { running: false, pid: null };
|
|
331
|
+
}
|
|
332
|
+
if (isProcessRunning(pid)) {
|
|
333
|
+
return { running: true, pid };
|
|
334
|
+
}
|
|
335
|
+
removePidFile(pidFile);
|
|
336
|
+
return { running: false, pid: null };
|
|
337
|
+
}
|
|
338
|
+
async function stopDaemon(pidFile) {
|
|
339
|
+
const pid = readPidFile(pidFile);
|
|
340
|
+
if (pid === null) {
|
|
341
|
+
console.log("Not running (no PID file found).");
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (!isProcessRunning(pid)) {
|
|
345
|
+
removePidFile(pidFile);
|
|
346
|
+
console.log("Not running (stale PID file cleaned).");
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
process.kill(pid, "SIGTERM");
|
|
350
|
+
for (let i = 0; i < 20; i++) {
|
|
351
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
352
|
+
if (!isProcessRunning(pid)) {
|
|
353
|
+
removePidFile(pidFile);
|
|
354
|
+
console.log(`Stopped (PID ${pid}).`);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
process.kill(pid, "SIGKILL");
|
|
359
|
+
removePidFile(pidFile);
|
|
360
|
+
console.log(`Force killed (PID ${pid}).`);
|
|
361
|
+
}
|
|
362
|
+
function daemonize(opts) {
|
|
363
|
+
if (process.env.__AI_TEAMS_DAEMON_WORKER === "1") {
|
|
364
|
+
return opts.run();
|
|
365
|
+
}
|
|
366
|
+
if (process.env.__AI_TEAMS_DAEMON_WATCHDOG === "1") {
|
|
367
|
+
return runWatchdog({
|
|
368
|
+
name: opts.name,
|
|
369
|
+
pidFile: opts.pidFile,
|
|
370
|
+
logDir: path.dirname(opts.logFile),
|
|
371
|
+
workerScript: process.argv[1],
|
|
372
|
+
workerArgs: process.argv.slice(2)
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
fs.mkdirSync(path.dirname(opts.logFile), { recursive: true });
|
|
376
|
+
const child = spawn(process.execPath, [process.argv[1], ...process.argv.slice(2)], {
|
|
377
|
+
detached: true,
|
|
378
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
379
|
+
env: {
|
|
380
|
+
...process.env,
|
|
381
|
+
__AI_TEAMS_DAEMON_WATCHDOG: "1"
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
child.unref();
|
|
385
|
+
return new Promise((resolve) => {
|
|
386
|
+
setTimeout(() => {
|
|
387
|
+
const pid = readPidFile(opts.pidFile);
|
|
388
|
+
if (pid) {
|
|
389
|
+
console.log(`${opts.name} started (PID ${pid})`);
|
|
390
|
+
} else {
|
|
391
|
+
console.error(`${opts.name}: failed to start \u2014 PID file not found.`);
|
|
392
|
+
}
|
|
393
|
+
resolve();
|
|
394
|
+
process.exit(pid ? 0 : 1);
|
|
395
|
+
}, 500);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
234
399
|
// src/db.ts
|
|
235
400
|
import { DatabaseSync } from "node:sqlite";
|
|
236
401
|
import pg from "pg";
|
|
@@ -1919,8 +2084,8 @@ async function createAiTeamsServer(options) {
|
|
|
1919
2084
|
const defaultTimeoutSec = options.defaultTimeoutSec ?? 1800;
|
|
1920
2085
|
const disconnectGraceMs = options.disconnectGraceMs ?? 15e3;
|
|
1921
2086
|
const maxLogChunksPerTask = options.maxLogChunksPerTask ?? 400;
|
|
1922
|
-
const dataDir = options.dataDir ??
|
|
1923
|
-
const dbPath = options.dbPath ??
|
|
2087
|
+
const dataDir = options.dataDir ?? path2.join(process.cwd(), "data");
|
|
2088
|
+
const dbPath = options.dbPath ?? path2.join(dataDir, "ai-teams.db");
|
|
1924
2089
|
let closing = false;
|
|
1925
2090
|
const state = createInMemoryStateStore();
|
|
1926
2091
|
const db = await createDatabaseFromEnv({
|
|
@@ -1935,13 +2100,13 @@ async function createAiTeamsServer(options) {
|
|
|
1935
2100
|
const logDir = options.logDir || process.env.LOG_DIR;
|
|
1936
2101
|
let loggerConfig = { level: logLevel };
|
|
1937
2102
|
if (logDir && options.logger !== false) {
|
|
1938
|
-
|
|
2103
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
1939
2104
|
loggerConfig = {
|
|
1940
2105
|
level: logLevel,
|
|
1941
2106
|
transport: {
|
|
1942
2107
|
targets: [
|
|
1943
2108
|
{ target: "pino/file", options: { destination: 1 }, level: logLevel },
|
|
1944
|
-
{ target: "pino/file", options: { destination:
|
|
2109
|
+
{ target: "pino/file", options: { destination: path2.join(logDir, "server.log") }, level: logLevel }
|
|
1945
2110
|
]
|
|
1946
2111
|
}
|
|
1947
2112
|
};
|
|
@@ -1974,8 +2139,8 @@ async function createAiTeamsServer(options) {
|
|
|
1974
2139
|
},
|
|
1975
2140
|
staticCSP: true
|
|
1976
2141
|
});
|
|
1977
|
-
const webDir =
|
|
1978
|
-
if (
|
|
2142
|
+
const webDir = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "web");
|
|
2143
|
+
if (fs2.existsSync(webDir)) {
|
|
1979
2144
|
await app.register(fastifyStatic, { root: webDir, prefix: "/" });
|
|
1980
2145
|
app.setNotFoundHandler((_, reply) => {
|
|
1981
2146
|
reply.sendFile("index.html");
|
|
@@ -2086,19 +2251,19 @@ async function createAiTeamsServer(options) {
|
|
|
2086
2251
|
return { employeeId, workspace: null, activeSessionId: null, sessions: [] };
|
|
2087
2252
|
}
|
|
2088
2253
|
const encodedPath = workspace.replace(/\//g, "-");
|
|
2089
|
-
const claudeProjectsDir =
|
|
2254
|
+
const claudeProjectsDir = path2.join(os.homedir(), ".claude", "projects", encodedPath);
|
|
2090
2255
|
let entries;
|
|
2091
2256
|
try {
|
|
2092
|
-
entries =
|
|
2257
|
+
entries = fs2.readdirSync(claudeProjectsDir, { withFileTypes: true });
|
|
2093
2258
|
} catch {
|
|
2094
2259
|
return { employeeId, workspace, activeSessionId: null, sessions: [] };
|
|
2095
2260
|
}
|
|
2096
2261
|
const jsonlFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).sort((a, b) => b.name.localeCompare(a.name));
|
|
2097
2262
|
const sessions = jsonlFiles.map((entry) => {
|
|
2098
|
-
const filePath =
|
|
2099
|
-
const stat =
|
|
2263
|
+
const filePath = path2.join(claudeProjectsDir, entry.name);
|
|
2264
|
+
const stat = fs2.statSync(filePath);
|
|
2100
2265
|
const sessionId = entry.name.replace(/\.jsonl$/, "");
|
|
2101
|
-
const content =
|
|
2266
|
+
const content = fs2.readFileSync(filePath, "utf8");
|
|
2102
2267
|
const lines = content.split("\n").filter(Boolean);
|
|
2103
2268
|
const lineCount = lines.length;
|
|
2104
2269
|
let firstUserMessage = null;
|
|
@@ -2111,9 +2276,9 @@ async function createAiTeamsServer(options) {
|
|
|
2111
2276
|
return { id: sessionId, sizeBytes: stat.size, modifiedAt: stat.mtime.toISOString(), lineCount, firstUserMessage, latestUserMessage };
|
|
2112
2277
|
});
|
|
2113
2278
|
let activeSessionId = null;
|
|
2114
|
-
const agentStatePath =
|
|
2279
|
+
const agentStatePath = path2.join(workspace, ".ai-teams", "agents", employeeId, "session-state.json");
|
|
2115
2280
|
try {
|
|
2116
|
-
const raw =
|
|
2281
|
+
const raw = fs2.readFileSync(agentStatePath, "utf8");
|
|
2117
2282
|
activeSessionId = JSON.parse(raw).claudeSessionId ?? null;
|
|
2118
2283
|
} catch {
|
|
2119
2284
|
}
|
|
@@ -2124,7 +2289,7 @@ async function createAiTeamsServer(options) {
|
|
|
2124
2289
|
if (request.url.startsWith("/ws/") || request.url.startsWith("/docs")) {
|
|
2125
2290
|
return;
|
|
2126
2291
|
}
|
|
2127
|
-
const ext =
|
|
2292
|
+
const ext = path2.extname(request.url.split("?")[0]);
|
|
2128
2293
|
if (request.url === "/" || staticExts.has(ext)) {
|
|
2129
2294
|
return;
|
|
2130
2295
|
}
|
|
@@ -2693,23 +2858,52 @@ async function startServer(options = readOptionsFromEnv()) {
|
|
|
2693
2858
|
await server.app.listen({ port: options.port ?? DEFAULT_PORT, host: options.host ?? "0.0.0.0" });
|
|
2694
2859
|
return server;
|
|
2695
2860
|
}
|
|
2696
|
-
var isCli = process.argv[1] &&
|
|
2861
|
+
var isCli = process.argv[1] && fs2.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
2697
2862
|
if (isCli) {
|
|
2698
2863
|
let getArgValue = function(name) {
|
|
2699
2864
|
const idx = args.indexOf(name);
|
|
2700
2865
|
if (idx === -1) return void 0;
|
|
2701
2866
|
return args[idx + 1];
|
|
2867
|
+
}, resolveDataDir = function() {
|
|
2868
|
+
return getArgValue("--data-dir") || process.env.DATA_DIR || path2.join(process.cwd(), "data");
|
|
2869
|
+
}, resolvePidFile = function() {
|
|
2870
|
+
return path2.join(resolveDataDir(), ".ai-teams-server.pid");
|
|
2871
|
+
}, resolveLogDir = function() {
|
|
2872
|
+
return getArgValue("--log-dir") || process.env.LOG_DIR || path2.join(resolveDataDir(), "logs");
|
|
2873
|
+
}, applyCliArgsToEnv = function() {
|
|
2874
|
+
const cliToken = getArgValue("--token");
|
|
2875
|
+
const cliPort = getArgValue("--port");
|
|
2876
|
+
const cliHost = getArgValue("--host");
|
|
2877
|
+
const cliDataDir = getArgValue("--data-dir");
|
|
2878
|
+
const cliDatabaseUrl = getArgValue("--database-url");
|
|
2879
|
+
const cliDbPath = getArgValue("--db-path");
|
|
2880
|
+
const cliLogLevel = getArgValue("--log-level");
|
|
2881
|
+
const cliLogDir = getArgValue("--log-dir");
|
|
2882
|
+
if (cliToken) process.env.AI_TEAMS_AUTH_TOKEN = cliToken;
|
|
2883
|
+
if (cliPort) process.env.AI_TEAMS_SERVER_PORT = cliPort;
|
|
2884
|
+
if (cliHost) process.env.HOST = cliHost;
|
|
2885
|
+
if (cliDataDir) process.env.DATA_DIR = cliDataDir;
|
|
2886
|
+
if (cliDatabaseUrl) process.env.DATABASE_URL = cliDatabaseUrl;
|
|
2887
|
+
if (cliDbPath) process.env.DB_PATH = cliDbPath;
|
|
2888
|
+
if (cliLogLevel) process.env.LOG_LEVEL = cliLogLevel;
|
|
2889
|
+
if (cliLogDir) process.env.LOG_DIR = cliLogDir;
|
|
2702
2890
|
};
|
|
2703
|
-
getArgValue2 = getArgValue;
|
|
2891
|
+
getArgValue2 = getArgValue, resolveDataDir2 = resolveDataDir, resolvePidFile2 = resolvePidFile, resolveLogDir2 = resolveLogDir, applyCliArgsToEnv2 = applyCliArgsToEnv;
|
|
2704
2892
|
const args = process.argv.slice(2);
|
|
2705
2893
|
if (args.includes("--version") || args.includes("-v")) {
|
|
2706
|
-
console.log("0.
|
|
2894
|
+
console.log("0.3.1");
|
|
2707
2895
|
process.exit(0);
|
|
2708
2896
|
}
|
|
2709
2897
|
if (args.includes("--help") || args.includes("-h")) {
|
|
2710
2898
|
console.log(`ai-teams-server \u2014 AI Teams \u4E2D\u592E\u670D\u52A1\u5668
|
|
2711
2899
|
|
|
2712
|
-
\u7528\u6CD5: ai-teams-server [\u9009\u9879]
|
|
2900
|
+
\u7528\u6CD5: ai-teams-server <command> [\u9009\u9879]
|
|
2901
|
+
|
|
2902
|
+
\u547D\u4EE4:
|
|
2903
|
+
start [\u9009\u9879] \u540E\u53F0\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B
|
|
2904
|
+
stop \u505C\u6B62\u5B88\u62A4\u8FDB\u7A0B
|
|
2905
|
+
restart [\u9009\u9879] \u91CD\u542F\u5B88\u62A4\u8FDB\u7A0B
|
|
2906
|
+
status \u67E5\u770B\u8FD0\u884C\u72B6\u6001
|
|
2713
2907
|
|
|
2714
2908
|
\u9009\u9879:
|
|
2715
2909
|
--token <token> \u8BA4\u8BC1 Token (\u5FC5\u586B\uFF0C\u6216\u8BBE AI_TEAMS_AUTH_TOKEN)
|
|
@@ -2722,36 +2916,67 @@ if (isCli) {
|
|
|
2722
2916
|
--log-dir <dir> \u65E5\u5FD7\u6587\u4EF6\u76EE\u5F55 (\u4E0D\u8BBE\u5219\u4EC5\u8F93\u51FA\u5230 stdout)
|
|
2723
2917
|
-v, --version \u663E\u793A\u7248\u672C\u53F7
|
|
2724
2918
|
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
2919
|
+
|
|
2920
|
+
\u4E0D\u5E26\u547D\u4EE4\u76F4\u63A5\u8FD0\u884C\u65F6\u4E3A\u524D\u53F0\u6A21\u5F0F\u3002
|
|
2725
2921
|
`);
|
|
2726
2922
|
process.exit(0);
|
|
2727
2923
|
}
|
|
2728
|
-
const
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2924
|
+
const subcommand = args[0];
|
|
2925
|
+
if (subcommand === "start" || subcommand === "restart") {
|
|
2926
|
+
void (async () => {
|
|
2927
|
+
if (subcommand === "restart") {
|
|
2928
|
+
const status = getDaemonStatus(resolvePidFile());
|
|
2929
|
+
if (status.running) {
|
|
2930
|
+
await stopDaemon(resolvePidFile());
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
applyCliArgsToEnv();
|
|
2934
|
+
if (!process.env.AI_TEAMS_AUTH_TOKEN) {
|
|
2935
|
+
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");
|
|
2936
|
+
process.exit(1);
|
|
2937
|
+
}
|
|
2938
|
+
if (!process.env.LOG_DIR) {
|
|
2939
|
+
process.env.LOG_DIR = resolveLogDir();
|
|
2940
|
+
}
|
|
2941
|
+
await daemonize({
|
|
2942
|
+
name: "ai-teams-server",
|
|
2943
|
+
pidFile: resolvePidFile(),
|
|
2944
|
+
logFile: path2.join(resolveLogDir(), "server.log"),
|
|
2945
|
+
run: async () => {
|
|
2946
|
+
await startServer(readOptionsFromEnv());
|
|
2947
|
+
}
|
|
2948
|
+
});
|
|
2949
|
+
})();
|
|
2950
|
+
} else if (subcommand === "stop") {
|
|
2951
|
+
void (async () => {
|
|
2952
|
+
await stopDaemon(resolvePidFile());
|
|
2953
|
+
})();
|
|
2954
|
+
} else if (subcommand === "status") {
|
|
2955
|
+
const status = getDaemonStatus(resolvePidFile());
|
|
2956
|
+
if (status.running) {
|
|
2957
|
+
console.log(`ai-teams-server is running (PID ${status.pid})`);
|
|
2958
|
+
console.log(`Log: ${path2.join(resolveLogDir(), "server.log")}`);
|
|
2959
|
+
} else {
|
|
2960
|
+
console.log("ai-teams-server is not running.");
|
|
2961
|
+
}
|
|
2962
|
+
} else {
|
|
2963
|
+
applyCliArgsToEnv();
|
|
2964
|
+
const options = readOptionsFromEnv();
|
|
2965
|
+
if (!options.authToken) {
|
|
2966
|
+
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");
|
|
2967
|
+
process.exit(1);
|
|
2968
|
+
}
|
|
2969
|
+
startServer(options).catch((error) => {
|
|
2970
|
+
console.error(error);
|
|
2971
|
+
process.exit(1);
|
|
2972
|
+
});
|
|
2748
2973
|
}
|
|
2749
|
-
startServer(options).catch((error) => {
|
|
2750
|
-
console.error(error);
|
|
2751
|
-
process.exit(1);
|
|
2752
|
-
});
|
|
2753
2974
|
}
|
|
2754
2975
|
var getArgValue2;
|
|
2976
|
+
var resolveDataDir2;
|
|
2977
|
+
var resolvePidFile2;
|
|
2978
|
+
var resolveLogDir2;
|
|
2979
|
+
var applyCliArgsToEnv2;
|
|
2755
2980
|
export {
|
|
2756
2981
|
createAiTeamsServer,
|
|
2757
2982
|
readOptionsFromEnv,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{color:#e8eefc;background:radial-gradient(circle at top left,rgba(105,170,255,.24),transparent 34%),radial-gradient(circle at bottom right,rgba(68,111,196,.28),transparent 32%),#091221;font-family:SF Pro Display,PingFang SC,Microsoft YaHei,sans-serif}*{box-sizing:border-box}html,body,#root{height:100%;overflow:hidden}body{margin:0;min-height:100vh;background:transparent;font-size:13px;line-height:1.42}button,input,select,textarea{font:inherit}.app-shell{display:grid;height:100dvh;min-height:0;overflow:hidden;grid-template-columns:minmax(0,1fr) 500px;grid-template-rows:auto minmax(0,1fr);grid-template-areas:"topbar topbar" "workspace command";align-items:stretch}.auth-screen{display:grid;min-height:100dvh;place-items:center;padding:18px;overflow:auto}.auth-card{width:min(390px,100%);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:18px;background:#0d1727e0;box-shadow:0 14px 32px #0108143d}.auth-brand{margin-bottom:14px}.topbar,.command-panel{padding:12px 14px;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);background:#091221c2}.topbar{grid-area:topbar;display:flex;align-items:center;justify-content:space-between;gap:14px;border-bottom:1px solid rgba(255,255,255,.08);position:relative;z-index:10}.brand{display:flex;gap:10px;align-items:center;flex:0 0 auto}.brand-badge{display:grid;place-items:center;width:32px;height:32px;border-radius:10px;background:linear-gradient(135deg,#63b3ff,#67ffd4);color:#07203f;font-weight:800;font-size:13px}.brand strong{font-size:14px}.brand p{margin:2px 0 0;color:#90a1be;font-size:12px}.nav-list{display:flex;flex:1;flex-direction:row;gap:6px;justify-content:flex-end;overflow-x:auto}.nav-item{flex:0 0 auto;border:0;border-radius:9px;padding:7px 10px;text-align:left;color:#c9d6f2;background:#ffffff0a;cursor:pointer;font-size:12px;white-space:nowrap}.nav-item.active{background:linear-gradient(135deg,#63b3ff52,#67ffd42e);color:#fff}.workspace-panel{grid-area:workspace;display:flex;min-width:0;min-height:0;flex-direction:column;gap:10px;padding:12px 14px;overflow:auto;overscroll-behavior:contain}.board,.task-log-page{min-width:0}.board-header{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:10px}.board-header h1,.panel-card h2,.task-log-panel h1{margin:0}.board-header h1,.task-log-panel h1{font-size:20px;line-height:1.2}.panel-card h2,.section-title-row h2{font-size:15px;line-height:1.25}.board-header p{margin:5px 0 0;color:#92a6c8;font-size:12px}.employee-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,560px),1fr));gap:10px}.employee-card,.panel-card,.task-log-panel,.empty-state{border:1px solid rgba(255,255,255,.08);border-radius:14px;background:#0d1727cc;box-shadow:0 12px 30px #0108143d}.employee-card{padding:10px}.task-log-panel{flex:0 0 auto;margin:0;padding:10px}.section-title-row{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;margin-bottom:10px}.section-title-row h2{margin:0}.section-title-row p{margin:4px 0 0;color:#90a1be;font-size:12px}.filter-row{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.filter-chip,.target-chip{border:1px solid rgba(255,255,255,.08);border-radius:999px;padding:5px 8px;color:#c9d6f2;background:#ffffff0a;cursor:pointer;font-size:12px}.filter-chip.active,.target-chip.active{color:#061626;background:#79ffd1}.employee-card__header{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;margin-bottom:8px}.employee-card__title{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline}.employee-card__header h2{margin:0;font-size:15px}.task-state-badge{display:inline-flex;align-items:center;border-radius:999px;padding:1px 6px;color:#90a1be;background:#ffffff0a;font-size:10px;line-height:1.4;text-transform:uppercase}.task-state-badge.task-running,.task-state-badge.task-accepted,.task-state-badge.task-dispatched{color:#9bc8ff;background:#5da6ff1a}.task-state-badge.task-completed{color:#9df6b5;background:#79ffd114}.task-state-badge.task-failed,.task-state-badge.task-cancelled,.task-state-badge.task-timeout{color:#ff9e9e;background:#ff59591a}.employee-meta,.task-strip,.history-meta,.chat-message__meta{display:flex;justify-content:space-between;gap:10px}.employee-meta{flex-wrap:wrap;justify-content:flex-start;row-gap:4px;margin-bottom:8px;color:#7f93b5;font-size:11px}.task-strip{align-items:center;margin-bottom:8px;padding:6px 8px;border-radius:9px;background:#ffffff0a}.task-strip.task-failed,.task-row.task-failed{border-color:#ff77776b;background:#ff59591a}.task-strip.task-completed,.task-row.task-completed{border-color:#79ffd13d}.task-strip span{flex:1;color:#c9d6f2;font-size:12px}.log-window{margin:0;width:100%;aspect-ratio:16 / 9;min-height:220px;max-height:460px;overflow:auto;border-radius:10px;border:1px solid rgba(121,255,209,.12);padding:10px;background:linear-gradient(180deg,rgba(121,255,209,.03),transparent 34%),#06101b;color:#bfffd4;font-family:SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.55;white-space:pre-wrap;box-shadow:inset 0 0 0 1px #ffffff05}.status-pill{display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:5px 8px;font-size:11px;background:#ffffff0d}.status-pill.online{color:#9df6b5}.status-pill.offline{color:#f5a8a8}.status-pill.agent-presence{flex:0 0 auto}.status-pill.busy{color:#9bc8ff;background:#5da6ff1f}.status-pill.failed{color:#ff9e9e;background:#ff59591f}.status-pill.completed{color:#9df6b5}.status-pill.running,.status-pill.accepted,.status-pill.dispatched{color:#9bc8ff}.command-panel{grid-area:command;display:flex;min-width:0;min-height:0;height:100%;flex-direction:column;gap:10px;border-left:1px solid rgba(255,255,255,.08);overflow:hidden}.panel-card{padding:10px}.chat-panel{display:flex;flex:1;min-height:0;flex-direction:column;overflow:hidden}.chat-header{display:flex;flex:0 0 auto;justify-content:space-between;gap:10px;align-items:flex-start}.chat-header p{margin:3px 0 0;color:#90a1be;font-size:12px}.chat-header code{color:#79ffd1}.chat-list{display:flex;flex:1 1 auto;min-height:0;flex-direction:column;gap:8px;margin-top:10px;overflow:auto;padding-right:4px;overscroll-behavior:contain}.chat-composer{flex:0 0 auto;margin-top:10px;padding-top:10px;border-top:1px solid rgba(255,255,255,.08)}.target-picker{display:flex;flex-wrap:wrap;gap:6px;max-height:78px;overflow:auto;overscroll-behavior:contain}.chat-message{width:fit-content;max-width:88%;border-radius:11px 11px 4px;padding:8px}.leader-message{border-bottom-right-radius:6px;background:linear-gradient(135deg,#68b6ff47,#79ffd11f)}.leader-message-wrapper{display:flex;flex-direction:column;align-items:flex-end;width:fit-content;max-width:88%;align-self:flex-end}.chat-message__time-outside{color:#90a1be;font-size:11px;margin-top:2px;padding-right:4px}.chat-message__executing{display:flex;flex-direction:column;gap:2px;margin-top:4px;padding-top:4px;border-top:1px solid rgba(255,255,255,.1)}.chat-message__executing span{color:#9bc8ff;font-size:11px}.employee-message{align-self:flex-start;border-bottom-left-radius:6px;background:#ffffff0f}.employee-message.task-failed,.employee-message.task-timeout{background:#ff59591f}.chat-message p{margin:6px 0;color:#eef4ff;white-space:pre-wrap}.chat-message small{color:#79ffd1}.chat-message__meta span,.chat-empty{color:#90a1be;font-size:11px}.field{display:flex;flex-direction:column;gap:5px;margin-top:9px}.field span{color:#98abcb;font-size:12px}.field input,.field select,.field textarea{width:100%;border:1px solid rgba(255,255,255,.08);border-radius:9px;padding:8px 10px;background:#ffffff0a;color:#eef4ff;font-size:12px}.field textarea{min-height:82px;max-height:112px;resize:none;overflow:auto}.primary-button,.secondary-button{border:0;border-radius:10px;cursor:pointer}.primary-button{width:100%;margin-top:10px;padding:8px 12px;color:#07203f;background:linear-gradient(135deg,#68b6ff,#79ffd1);font-weight:700;font-size:12px}.secondary-button{padding:6px 8px;background:#ffffff14;color:#eef4ff;font-size:12px}.history-list{display:flex;flex-direction:column;gap:10px;margin-top:14px}.task-table{display:flex;flex-direction:column;gap:6px}.task-row{display:flex;justify-content:space-between;gap:10px;border:1px solid rgba(255,255,255,.08);border-radius:9px;padding:8px 10px;background:#ffffff0a}.task-row__main{min-width:0}.task-row__main p{margin:4px 0 0;color:#c9d6f2}.task-row__side{display:flex;flex-direction:column;align-items:flex-end;gap:6px;white-space:nowrap}.task-row__side small,.error-text{color:#90a1be}.error-text{display:inline-block;margin-top:4px;color:#ffb3b3}.history-item{border-radius:10px;padding:10px;background:#ffffff0a}.history-item p,.history-empty{margin:6px 0 0;color:#c9d6f2}.history-meta span{color:#90a1be;font-size:11px}.empty-state{padding:24px;color:#90a1be;text-align:center}.reconnect-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:200;display:flex;align-items:center;justify-content:center;background:#091221bf;color:#79ffd1;font-size:16px;font-weight:600;letter-spacing:.05em;cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;user-select:none}.mobile-status-bar,.mobile-chat-feed,.mobile-input-bar,.mobile-logout,.mobile-terminal-overlay{display:none}@media(max-width:1100px)and (min-width:641px){.app-shell{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1fr) minmax(min(500px,62dvh),62dvh);grid-template-areas:"topbar" "workspace" "command"}.topbar,.command-panel{border:0}.topbar{align-items:flex-start;flex-direction:column;gap:10px;padding:12px 14px}.nav-list{width:100%;justify-content:flex-start}.command-panel{border-top:1px solid rgba(255,255,255,.08);padding:10px}.command-panel .panel-card{padding:10px}.section-title-row,.task-row{flex-direction:column}.task-row__side{align-items:flex-start}.field textarea{min-height:68px;max-height:78px}}@media(max-width:640px){html,body,#root{position:fixed;top:0;left:0;right:0;bottom:0;overflow:hidden}.app-shell{height:100%;height:100dvh;box-sizing:border-box;padding-bottom:env(safe-area-inset-bottom,0px);grid-template-columns:1fr;grid-template-rows:auto auto 1fr auto;grid-template-areas:"topbar" "mobile-status" "mobile-chat" "mobile-input";align-items:stretch}.topbar{grid-area:topbar;padding:6px 10px;gap:8px;overflow:hidden}.topbar .nav-list,.brand p{display:none}.brand strong{font-size:13px}.brand-badge{width:26px;height:26px;font-size:11px;border-radius:8px}.workspace-panel,.command-panel{display:none}.mobile-status-bar{grid-area:mobile-status;display:flex;flex:0 0 auto;gap:6px;padding:4px 10px;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;background:#09122199;border-bottom:1px solid rgba(255,255,255,.06);-webkit-overflow-scrolling:touch;scrollbar-width:none}.mobile-status-bar::-webkit-scrollbar{display:none}.mobile-agent-pill{display:flex;align-items:center;gap:4px;border-radius:999px;padding:3px 8px;background:#ffffff0d;font-size:11px;white-space:nowrap;flex:0 0 auto}.mobile-agent-pill .dot{width:6px;height:6px;border-radius:50%;background:#6b7a96}.mobile-agent-pill.online .dot{background:#67e8a0;box-shadow:0 0 4px #67e8a080}.mobile-agent-pill.busy .dot{background:#63b3ff;animation:pulse-dot 1.5s infinite}.mobile-agent-pill.offline .dot{background:#6b7a96}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.4}}.mobile-agent-pill .agent-name{color:#c9d6f2;font-weight:500}.mobile-agent-pill .agent-task{color:#7f93b5;max-width:80px;overflow:hidden;text-overflow:ellipsis}.mobile-chat-feed{grid-area:mobile-chat;display:flex;flex-direction:column;gap:6px;padding:8px 10px;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}.mobile-chat-feed .chat-message{max-width:88%;border-radius:10px 10px 4px;padding:6px 8px}.mobile-chat-feed .chat-message p{margin:4px 0;font-size:12px;white-space:pre-wrap;word-break:break-word}.mobile-chat-feed .chat-message__meta{gap:6px}.mobile-chat-feed .chat-message__meta strong{font-size:11px}.mobile-chat-feed .chat-message__meta span,.mobile-chat-feed .chat-message small{font-size:10px}.mobile-chat-feed .leader-message-wrapper{max-width:88%}.mobile-chat-feed .chat-message__time-outside,.mobile-chat-feed .chat-message__executing span{font-size:10px}.mobile-chat-empty{flex:1;display:flex;align-items:center;justify-content:center;color:#6b7a96;font-size:12px}.mobile-input-bar{grid-area:mobile-input;display:flex;flex-direction:column;gap:6px;padding:8px 10px;flex:0 0 auto;background:#091221eb;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border-top:1px solid rgba(255,255,255,.06)}.mobile-target-row{display:flex;gap:4px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;scrollbar-width:none}.mobile-target-row::-webkit-scrollbar{display:none}.mobile-target-row .target-chip{padding:3px 7px;font-size:11px;white-space:nowrap;flex:0 0 auto}.mobile-input-row{display:flex;gap:6px;align-items:flex-end}.mobile-input-row textarea{flex:1;border:1px solid rgba(255,255,255,.08);border-radius:9px;padding:8px 10px;background:#ffffff0a;color:#eef4ff;font-size:14px;font-family:inherit;height:36px;max-height:120px;resize:none;line-height:1.4;overflow-y:auto}.mobile-input-row .send-btn{flex:0 0 auto;width:36px;height:36px;border-radius:50%;border:0;background:linear-gradient(135deg,#68b6ff,#79ffd1);color:#07203f;font-size:16px;cursor:pointer;display:grid;place-items:center}.mobile-input-row .send-btn:disabled{opacity:.4;cursor:default}.auth-card{width:min(340px,100%);padding:14px}.auth-brand{margin-bottom:10px}.mobile-logout{display:block}.mobile-status-bar,.mobile-chat-feed,.mobile-input-bar{display:flex}.mobile-agent-pill.selected{background:#67e8a02e;border:1px solid rgba(103,232,160,.3)}.mobile-terminal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;display:flex;flex-direction:column;justify-content:flex-end;background:#0000008c;-webkit-tap-highlight-color:transparent}.mobile-terminal-card{display:flex;flex-direction:column;max-height:70dvh;margin:0 6px 6px;border-radius:14px;border:1px solid rgba(255,255,255,.08);background:#06101bf7;box-shadow:0 -8px 30px #0006;overflow:hidden}.mobile-terminal-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255,255,255,.06);font-size:12px;color:#c9d6f2;font-weight:600}.mobile-terminal-header button{border:0;background:#ffffff0f;color:#90a1be;border-radius:50%;width:26px;height:26px;font-size:13px;cursor:pointer;display:grid;place-items:center}.mobile-terminal-content{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;color:#bfffd4;font-family:SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.55;white-space:pre-wrap;word-break:break-all}}
|