agim-cli 1.0.7 → 1.0.9
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 +49 -0
- package/dist/cli.js +51 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/admin-allowlist.d.ts +11 -0
- package/dist/core/admin-allowlist.d.ts.map +1 -0
- package/dist/core/admin-allowlist.js +77 -0
- package/dist/core/admin-allowlist.js.map +1 -0
- package/dist/core/approval-router.d.ts.map +1 -1
- package/dist/core/approval-router.js +15 -0
- package/dist/core/approval-router.js.map +1 -1
- package/dist/core/commands/builtin.d.ts +1 -1
- package/dist/core/commands/builtin.d.ts.map +1 -1
- package/dist/core/commands/builtin.js +6 -4
- package/dist/core/commands/builtin.js.map +1 -1
- package/dist/core/commands/service.d.ts +14 -0
- package/dist/core/commands/service.d.ts.map +1 -0
- package/dist/core/commands/service.js +85 -0
- package/dist/core/commands/service.js.map +1 -0
- package/dist/core/restart-completion.d.ts +5 -0
- package/dist/core/restart-completion.d.ts.map +1 -0
- package/dist/core/restart-completion.js +156 -0
- package/dist/core/restart-completion.js.map +1 -0
- package/dist/core/restart-flow.d.ts +40 -0
- package/dist/core/restart-flow.d.ts.map +1 -0
- package/dist/core/restart-flow.js +177 -0
- package/dist/core/restart-flow.js.map +1 -0
- package/dist/core/restart-preflight.d.ts +7 -0
- package/dist/core/restart-preflight.d.ts.map +1 -0
- package/dist/core/restart-preflight.js +95 -0
- package/dist/core/restart-preflight.js.map +1 -0
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +16 -2
- package/dist/core/router.js.map +1 -1
- package/dist/core/self-protect.d.ts +8 -0
- package/dist/core/self-protect.d.ts.map +1 -0
- package/dist/core/self-protect.js +119 -0
- package/dist/core/self-protect.js.map +1 -0
- package/dist/core/service-intent.d.ts +17 -0
- package/dist/core/service-intent.d.ts.map +1 -0
- package/dist/core/service-intent.js +87 -0
- package/dist/core/service-intent.js.map +1 -0
- package/dist/core/types.d.ts +5 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/index.d.ts.map +1 -1
- package/dist/plugins/agents/claude-code/index.js +44 -14
- package/dist/plugins/agents/claude-code/index.js.map +1 -1
- package/dist/web/public/settings.html +117 -0
- package/dist/web/server.js +15 -39
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/commands/service.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAIhD;wDACwD;AACxD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB3F;AAED;;;;uDAIuD;AACvD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBxF;AAED;yCACyC;AACzC,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CA2B3F"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// `/restart`, `/stop`, `/status` — IM slash commands for service control.
|
|
2
|
+
//
|
|
3
|
+
// Same admin-allowlist gating as the natural-language path (see
|
|
4
|
+
// core/service-intent.ts) so users can't bypass IMHUB_ADMIN_USERS by
|
|
5
|
+
// preferring the slash form.
|
|
6
|
+
//
|
|
7
|
+
// `/status` is read-only and accepts non-admin callers (it surfaces only
|
|
8
|
+
// uptime / agent availability — no secrets). `/restart` and `/stop`
|
|
9
|
+
// require admin.
|
|
10
|
+
import { isAdmin, denialMessage } from '../admin-allowlist.js';
|
|
11
|
+
import { initiateRestart } from '../restart-flow.js';
|
|
12
|
+
/** /restart [-y] — graceful service restart with pre-flight + ack +
|
|
13
|
+
* completion message after the new daemon comes up. */
|
|
14
|
+
export async function handleRestartCommand(args, ctx) {
|
|
15
|
+
if (!isAdmin(ctx.platform, ctx.userId)) {
|
|
16
|
+
return denialMessage(ctx.platform, ctx.userId);
|
|
17
|
+
}
|
|
18
|
+
// Currently `args` is ignored — placeholder for future "-y" skip-confirm
|
|
19
|
+
// or "--force" override flags. Reference it to keep TS happy.
|
|
20
|
+
void args;
|
|
21
|
+
const initiator = {
|
|
22
|
+
source: 'im',
|
|
23
|
+
platform: ctx.platform,
|
|
24
|
+
channelId: ctx.channelId,
|
|
25
|
+
threadId: ctx.threadId,
|
|
26
|
+
userId: ctx.userId,
|
|
27
|
+
initialText: '/restart',
|
|
28
|
+
};
|
|
29
|
+
const result = await initiateRestart(initiator);
|
|
30
|
+
return result.message;
|
|
31
|
+
}
|
|
32
|
+
/** /stop — graceful shutdown. Sends SIGTERM to self after acking the
|
|
33
|
+
* caller; no helper respawn (unlike /restart). Useful only via systemd
|
|
34
|
+
* (which won't restart on clean exit when WantedBy=multi-user.target is
|
|
35
|
+
* removed) or for "I'm done, going to sleep" workflows. For background
|
|
36
|
+
* mode, the user must `agim start` again manually. */
|
|
37
|
+
export async function handleStopCommand(args, ctx) {
|
|
38
|
+
if (!isAdmin(ctx.platform, ctx.userId)) {
|
|
39
|
+
return denialMessage(ctx.platform, ctx.userId);
|
|
40
|
+
}
|
|
41
|
+
void args;
|
|
42
|
+
// No initiate-state-file because there's no new daemon to push a "✅
|
|
43
|
+
// 已停止" message from. The caller just sees SIGTERM in their IM stream
|
|
44
|
+
// (their last reply is what we send here).
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
try {
|
|
47
|
+
process.kill(process.pid, 'SIGTERM');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
}, 300);
|
|
53
|
+
return ('🛑 服务即将停止。\n' +
|
|
54
|
+
' 后台模式:用 `agim start` 重新启动。\n' +
|
|
55
|
+
' systemd 模式:用 `systemctl start agim` 重新启动。');
|
|
56
|
+
}
|
|
57
|
+
/** /status — open to everyone. Surfaces uptime + default agent +
|
|
58
|
+
* registered messengers. No secrets. */
|
|
59
|
+
export async function handleStatusCommand(_args, ctx) {
|
|
60
|
+
const { registry } = await import('../registry.js');
|
|
61
|
+
const { isAgentAvailableCached } = await import('../onboarding.js');
|
|
62
|
+
const { formatUptime, detectService } = await import('../../cli-ui/service.js');
|
|
63
|
+
const lines = ['📊 Agim 服务状态'];
|
|
64
|
+
const svc = detectService();
|
|
65
|
+
if (svc.mode === 'none') {
|
|
66
|
+
// The fact that this command ran means we're alive — paradox.
|
|
67
|
+
// Most likely 'none' = detection mismatch, treat as foreground.
|
|
68
|
+
lines.push(` 模式: foreground (pid ${process.pid})`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const upStr = typeof svc.uptimeSec === 'number' ? `, up ${formatUptime(svc.uptimeSec)}` : '';
|
|
72
|
+
lines.push(` 模式: ${svc.mode} (pid ${svc.pid ?? process.pid}${upStr})`);
|
|
73
|
+
}
|
|
74
|
+
const agent = ctx.defaultAgent;
|
|
75
|
+
let agentOk = false;
|
|
76
|
+
try {
|
|
77
|
+
agentOk = await isAgentAvailableCached(agent);
|
|
78
|
+
}
|
|
79
|
+
catch { /* ignore */ }
|
|
80
|
+
lines.push(` 默认 agent: ${agent} ${agentOk ? '✅' : '⚠️ 不可用'}`);
|
|
81
|
+
const messengers = registry.listMessengers();
|
|
82
|
+
lines.push(` messengers: ${messengers.length ? messengers.join(', ') : '(none — web-console-only)'}`);
|
|
83
|
+
return lines.join('\n');
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/core/commands/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function processRestartPending(defaultAgent: string): Promise<void>;
|
|
2
|
+
/** Returns true if the running process appears to be inside a restart
|
|
3
|
+
* (state file exists, fresh). Useful for log lines / startup ordering. */
|
|
4
|
+
export declare function isInsideRestart(): boolean;
|
|
5
|
+
//# sourceMappingURL=restart-completion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-completion.d.ts","sourceRoot":"","sources":["../../src/core/restart-completion.ts"],"names":[],"mappings":"AA2BA,wBAAsB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD/E;AAuFD;2EAC2E;AAC3E,wBAAgB,eAAe,IAAI,OAAO,CAQzC"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Startup hook — process restart-pending.json left by the previous
|
|
2
|
+
// instance. Runs AFTER cli.ts has registered messengers and agents (so
|
|
3
|
+
// we can push a completion message back to the initiator's IM thread
|
|
4
|
+
// and probe agent health).
|
|
5
|
+
//
|
|
6
|
+
// Cases:
|
|
7
|
+
// - File missing: nothing to do (fresh start, not a restart).
|
|
8
|
+
// - File present, stale (> 5 min): garbage-collect, no push.
|
|
9
|
+
// - File present, fresh, all health checks pass: push "✅ 已重启" + delete file.
|
|
10
|
+
// - File present, restart-failed.log says something blew up: push "⚠️
|
|
11
|
+
// 上次重启 X 失败:<reason>,已自动启动" + delete file + truncate fail log.
|
|
12
|
+
import { existsSync, readFileSync, unlinkSync, statSync } from 'node:fs';
|
|
13
|
+
import { registry } from './registry.js';
|
|
14
|
+
import { isAgentAvailableCached } from './onboarding.js';
|
|
15
|
+
import { logger as rootLogger } from './logger.js';
|
|
16
|
+
import { RESTART_PENDING_FILE, RESTART_FAILED_LOG, } from './restart-flow.js';
|
|
17
|
+
const log = rootLogger.child({ component: 'restart-completion' });
|
|
18
|
+
const STALE_AFTER_MS = 5 * 60_000;
|
|
19
|
+
export async function processRestartPending(defaultAgent) {
|
|
20
|
+
if (!existsSync(RESTART_PENDING_FILE))
|
|
21
|
+
return;
|
|
22
|
+
let state;
|
|
23
|
+
try {
|
|
24
|
+
const raw = readFileSync(RESTART_PENDING_FILE, 'utf-8');
|
|
25
|
+
state = JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
log.warn({ event: 'restart-completion.parse_failed', err: String(err) }, 'restart-pending.json corrupt; deleting');
|
|
29
|
+
safeUnlink(RESTART_PENDING_FILE);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const age = Date.now() - state.started_at;
|
|
33
|
+
if (age > STALE_AFTER_MS) {
|
|
34
|
+
log.warn({ event: 'restart-completion.stale', age_ms: age }, 'restart-pending.json is older than 5 min; assuming abandoned, deleting');
|
|
35
|
+
safeUnlink(RESTART_PENDING_FILE);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Run self-health.
|
|
39
|
+
const health = await selfHealthCheck(defaultAgent);
|
|
40
|
+
const elapsedSec = (age / 1000).toFixed(1);
|
|
41
|
+
// Was the previous helper able to spawn us, or did something go wrong?
|
|
42
|
+
let failHint = '';
|
|
43
|
+
if (existsSync(RESTART_FAILED_LOG)) {
|
|
44
|
+
try {
|
|
45
|
+
const tail = readFileSync(RESTART_FAILED_LOG, 'utf-8').trim().split('\n').slice(-1)[0];
|
|
46
|
+
if (tail)
|
|
47
|
+
failHint = ` 上次出错记录:${tail.slice(0, 200)}`;
|
|
48
|
+
// Truncate so it doesn't pile up.
|
|
49
|
+
safeUnlink(RESTART_FAILED_LOG);
|
|
50
|
+
}
|
|
51
|
+
catch { /* ignore */ }
|
|
52
|
+
}
|
|
53
|
+
let body;
|
|
54
|
+
if (health.ok) {
|
|
55
|
+
body =
|
|
56
|
+
`✅ 已重启完成(耗时 ${elapsedSec}s,pid ${process.pid},版本 ${state.prev_version})。\n` +
|
|
57
|
+
health.summary;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
body =
|
|
61
|
+
`⚠️ 重启完成但有警告(耗时 ${elapsedSec}s,pid ${process.pid}):\n` +
|
|
62
|
+
health.warnings.map((w, i) => ` ${i + 1}. ${w}`).join('\n') +
|
|
63
|
+
failHint;
|
|
64
|
+
}
|
|
65
|
+
await pushToInitiator(state.initiator, body);
|
|
66
|
+
safeUnlink(RESTART_PENDING_FILE);
|
|
67
|
+
}
|
|
68
|
+
async function selfHealthCheck(defaultAgent) {
|
|
69
|
+
const warnings = [];
|
|
70
|
+
const lines = [];
|
|
71
|
+
// Default agent must be available — otherwise the bot can't do its
|
|
72
|
+
// primary job. Use the cached probe so we don't double-spawn.
|
|
73
|
+
let agentOk = false;
|
|
74
|
+
try {
|
|
75
|
+
agentOk = await isAgentAvailableCached(defaultAgent);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
warnings.push(`默认 agent (${defaultAgent}) 检查失败:${err instanceof Error ? err.message : String(err)}`);
|
|
79
|
+
}
|
|
80
|
+
if (!agentOk) {
|
|
81
|
+
warnings.push(`默认 agent (${defaultAgent}) 不可用 — 收到的消息会路由失败`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
lines.push(` · agent ${defaultAgent}: ok`);
|
|
85
|
+
}
|
|
86
|
+
// Messengers — list the ones currently registered. Empty list means
|
|
87
|
+
// web-console-only mode, which is fine but worth flagging when an IM
|
|
88
|
+
// initiator was expecting their adapter back.
|
|
89
|
+
const msgs = registry.listMessengers();
|
|
90
|
+
if (msgs.length === 0) {
|
|
91
|
+
warnings.push('当前未注册任何 messenger(web-console-only 模式)');
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
lines.push(` · messengers: ${msgs.join(', ')}`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
ok: warnings.length === 0,
|
|
98
|
+
summary: lines.join('\n'),
|
|
99
|
+
warnings,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async function pushToInitiator(initiator, body) {
|
|
103
|
+
if (initiator.source !== 'im') {
|
|
104
|
+
// Web / CLI initiator — the caller surface gets its own reply via
|
|
105
|
+
// polling (web) or stdout (cli). Skip the push but log.
|
|
106
|
+
log.info({ event: 'restart-completion.skip_push', source: initiator.source }, 'restart initiator was not an IM thread; skipping completion push');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!initiator.platform || !initiator.threadId) {
|
|
110
|
+
log.warn({ event: 'restart-completion.push_missing_ctx', initiator }, 'IM initiator missing platform/threadId; cannot push completion message');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const adapter = registry.getMessenger(initiator.platform);
|
|
114
|
+
if (!adapter) {
|
|
115
|
+
log.warn({
|
|
116
|
+
event: 'restart-completion.push_no_messenger',
|
|
117
|
+
platform: initiator.platform,
|
|
118
|
+
}, 'IM messenger not registered after restart; completion push dropped');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await adapter.sendMessage(initiator.threadId, body);
|
|
123
|
+
log.info({
|
|
124
|
+
event: 'restart-completion.pushed',
|
|
125
|
+
platform: initiator.platform,
|
|
126
|
+
threadId: initiator.threadId,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
log.warn({
|
|
131
|
+
event: 'restart-completion.push_failed',
|
|
132
|
+
err: err instanceof Error ? err.message : String(err),
|
|
133
|
+
platform: initiator.platform,
|
|
134
|
+
}, 'completion message push threw — dropping');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function safeUnlink(p) {
|
|
138
|
+
try {
|
|
139
|
+
unlinkSync(p);
|
|
140
|
+
}
|
|
141
|
+
catch { /* ignore */ }
|
|
142
|
+
}
|
|
143
|
+
/** Returns true if the running process appears to be inside a restart
|
|
144
|
+
* (state file exists, fresh). Useful for log lines / startup ordering. */
|
|
145
|
+
export function isInsideRestart() {
|
|
146
|
+
if (!existsSync(RESTART_PENDING_FILE))
|
|
147
|
+
return false;
|
|
148
|
+
try {
|
|
149
|
+
const st = statSync(RESTART_PENDING_FILE);
|
|
150
|
+
return Date.now() - st.mtimeMs < STALE_AFTER_MS;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=restart-completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-completion.js","sourceRoot":"","sources":["../../src/core/restart-completion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const RESTART_PENDING_FILE: string;
|
|
2
|
+
export declare const RESTART_FAILED_LOG: string;
|
|
3
|
+
/** Metadata about who triggered the restart, persisted across the
|
|
4
|
+
* parent → child boundary so the new daemon can ack the right thread. */
|
|
5
|
+
export interface RestartInitiator {
|
|
6
|
+
/** Source layer that initiated. Determines who receives the completion
|
|
7
|
+
* message. */
|
|
8
|
+
source: 'im' | 'web' | 'cli';
|
|
9
|
+
/** IM platform name (wechat-ilink / telegram / feishu / dingtalk /
|
|
10
|
+
* discord). Empty when source !== 'im'. */
|
|
11
|
+
platform?: string;
|
|
12
|
+
channelId?: string;
|
|
13
|
+
threadId?: string;
|
|
14
|
+
userId?: string;
|
|
15
|
+
/** Message the user sent that triggered this restart (truncated to 120
|
|
16
|
+
* chars). Helps the completion message say "✅ 已重启" rather than just
|
|
17
|
+
* appearing unprompted. */
|
|
18
|
+
initialText?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PendingState {
|
|
21
|
+
version: 1;
|
|
22
|
+
initiator: RestartInitiator;
|
|
23
|
+
started_at: number;
|
|
24
|
+
prev_pid: number;
|
|
25
|
+
prev_version: string;
|
|
26
|
+
/** Best guess at when the new daemon should be up. Used by stale
|
|
27
|
+
* detection — pending files older than this + 5 minutes get garbage
|
|
28
|
+
* collected on next startup. */
|
|
29
|
+
expected_complete_by: number;
|
|
30
|
+
}
|
|
31
|
+
export interface InitiateResult {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
/** Friendly text the caller should surface back to the user. On success
|
|
34
|
+
* this is a "重启中..." ack; on failure it's an error explanation. */
|
|
35
|
+
message: string;
|
|
36
|
+
/** Why we refused, when ok=false. Programmatic. */
|
|
37
|
+
errors?: string[];
|
|
38
|
+
}
|
|
39
|
+
export declare function initiateRestart(initiator: RestartInitiator): Promise<InitiateResult>;
|
|
40
|
+
//# sourceMappingURL=restart-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-flow.d.ts","sourceRoot":"","sources":["../../src/core/restart-flow.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,oBAAoB,QAA0C,CAAA;AAC3E,eAAO,MAAM,kBAAkB,QAAwC,CAAA;AAIvE;0EAC0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B;mBACe;IACf,MAAM,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,CAAA;IAC5B;gDAC4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;gCAE4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,CAAC,CAAA;IACV,SAAS,EAAE,gBAAgB,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB;;qCAEiC;IACjC,oBAAoB,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAA;IACX;wEACoE;IACpE,OAAO,EAAE,MAAM,CAAA;IACf,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAmBD,wBAAsB,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CA+E1F"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Restart-flow orchestration. Glues together pre-flight checks, the
|
|
2
|
+
// in-flight state file (so the post-restart instance can ack the
|
|
3
|
+
// initiator), and the existing detached-helper respawn mechanism from
|
|
4
|
+
// web/server.ts.
|
|
5
|
+
//
|
|
6
|
+
// Single public entry: initiateRestart(ctx). Caller (slash command
|
|
7
|
+
// handler / web endpoint / cli) decides whether the caller is an admin
|
|
8
|
+
// and only invokes this on success — admin gating lives in
|
|
9
|
+
// admin-allowlist.ts.
|
|
10
|
+
//
|
|
11
|
+
// Sequence:
|
|
12
|
+
// 1. Concurrency guard (existing restart-pending.json blocks dup)
|
|
13
|
+
// 2. Pre-flight checks (refuse if any errors)
|
|
14
|
+
// 3. Write restart-pending.json with initiator metadata
|
|
15
|
+
// 4. ACK to caller (caller does the actual IM reply)
|
|
16
|
+
// 5. Schedule SIGTERM self in ~300 ms; spawn detached helper that
|
|
17
|
+
// waits for parent to die then respawns new daemon
|
|
18
|
+
//
|
|
19
|
+
// The new daemon, on startup, calls processRestartPending() (in
|
|
20
|
+
// restart-completion.ts) which reads the file, runs self-health, pushes
|
|
21
|
+
// the completion message to the initiator's IM thread, and deletes the
|
|
22
|
+
// file.
|
|
23
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
24
|
+
import { spawn } from 'node:child_process';
|
|
25
|
+
import { dirname, join } from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import { AGIM_HOME } from './agim-paths.js';
|
|
28
|
+
import { runPreflightChecks } from './restart-preflight.js';
|
|
29
|
+
import { logger as rootLogger } from './logger.js';
|
|
30
|
+
const log = rootLogger.child({ component: 'restart-flow' });
|
|
31
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
export const RESTART_PENDING_FILE = join(AGIM_HOME, 'restart-pending.json');
|
|
33
|
+
export const RESTART_FAILED_LOG = join(AGIM_HOME, 'restart-failed.log');
|
|
34
|
+
const LOG_FILE = join(AGIM_HOME, 'im-hub.log');
|
|
35
|
+
const PID_FILE = join(AGIM_HOME, 'im-hub.pid');
|
|
36
|
+
/** Best-effort package version. `npm_package_version` is set by `npm start`
|
|
37
|
+
* / `npm run` but not by a bare `node dist/cli.js`; the systemd unit
|
|
38
|
+
* exposes it via Environment lines only when explicitly configured. Fall
|
|
39
|
+
* back to a readSync of the package.json sitting next to dist/. */
|
|
40
|
+
const PKG_VERSION = (() => {
|
|
41
|
+
if (process.env.npm_package_version)
|
|
42
|
+
return process.env.npm_package_version;
|
|
43
|
+
try {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
45
|
+
const { readFileSync } = require('node:fs');
|
|
46
|
+
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
47
|
+
const j = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
48
|
+
return typeof j.version === 'string' ? j.version : 'unknown';
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return 'unknown';
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
54
|
+
export async function initiateRestart(initiator) {
|
|
55
|
+
// 1. Concurrency guard. Pre-flight also looks for this, but checking
|
|
56
|
+
// here first avoids running the full check sequence when we know
|
|
57
|
+
// we'll refuse.
|
|
58
|
+
if (existsSync(RESTART_PENDING_FILE)) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
message: '🛑 已有进行中的重启请求(restart-pending.json 仍在)。等待当前重启完成后再试,或手动删除文件后重试。',
|
|
62
|
+
errors: ['concurrent restart in progress'],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// 2. Pre-flight.
|
|
66
|
+
const pre = await runPreflightChecks();
|
|
67
|
+
if (!pre.ok) {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
message: '🛑 重启取消:pre-flight 检查未通过。\n' +
|
|
71
|
+
pre.errors.map((e, i) => ` ${i + 1}. ${e}`).join('\n'),
|
|
72
|
+
errors: pre.errors,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// 3. Write state file.
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
const state = {
|
|
78
|
+
version: 1,
|
|
79
|
+
initiator,
|
|
80
|
+
started_at: now,
|
|
81
|
+
prev_pid: process.pid,
|
|
82
|
+
prev_version: PKG_VERSION,
|
|
83
|
+
expected_complete_by: now + 30_000,
|
|
84
|
+
};
|
|
85
|
+
try {
|
|
86
|
+
writeFileSync(RESTART_PENDING_FILE, JSON.stringify(state, null, 2));
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
message: `🛑 重启取消:无法写入状态文件 ${RESTART_PENDING_FILE}(${err instanceof Error ? err.message : String(err)})`,
|
|
92
|
+
errors: [String(err)],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// 4. Schedule helper + self-exit. We delay 300 ms so the caller's HTTP /
|
|
96
|
+
// IM response has time to flush before SIGTERM lands.
|
|
97
|
+
const cliJs = join(__dirname, '..', 'cli.js');
|
|
98
|
+
const helperCode = buildHelperCode(process.pid, cliJs);
|
|
99
|
+
try {
|
|
100
|
+
const helper = spawn(process.execPath, ['-e', helperCode], {
|
|
101
|
+
detached: true,
|
|
102
|
+
stdio: 'ignore',
|
|
103
|
+
env: process.env,
|
|
104
|
+
});
|
|
105
|
+
helper.unref();
|
|
106
|
+
log.info({
|
|
107
|
+
event: 'restart-flow.helper_spawned',
|
|
108
|
+
helper_pid: helper.pid,
|
|
109
|
+
source: initiator.source,
|
|
110
|
+
platform: initiator.platform,
|
|
111
|
+
}, 'detached restart helper spawned; SIGTERM scheduled');
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
message: `🛑 重启取消:无法启动 helper 进程(${err instanceof Error ? err.message : String(err)})`,
|
|
117
|
+
errors: [String(err)],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
try {
|
|
122
|
+
process.kill(process.pid, 'SIGTERM');
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
}, 300);
|
|
128
|
+
return {
|
|
129
|
+
ok: true,
|
|
130
|
+
message: pre.warnings.length === 0
|
|
131
|
+
? '🔄 重启中… 预计 5–10 秒内回来。'
|
|
132
|
+
: `🔄 重启中… 预计 5–10 秒内回来。\n 注意:${pre.warnings.join(';')}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build the detached helper script. It:
|
|
137
|
+
* 1. Polls for parent PID to die (200ms × up to 150 = 30s).
|
|
138
|
+
* 2. Spawns the new daemon, redirecting stdout/stderr to im-hub.log.
|
|
139
|
+
* 3. Writes new pid to im-hub.pid.
|
|
140
|
+
* 4. On parent-death-timeout, records the failure into restart-failed.log
|
|
141
|
+
* and exits non-zero (no respawn — manual recovery needed).
|
|
142
|
+
*/
|
|
143
|
+
function buildHelperCode(parentPid, cliJs) {
|
|
144
|
+
const escapedCli = JSON.stringify(cliJs);
|
|
145
|
+
const escapedPid = JSON.stringify(PID_FILE);
|
|
146
|
+
const escapedLog = JSON.stringify(LOG_FILE);
|
|
147
|
+
const escapedFailLog = JSON.stringify(RESTART_FAILED_LOG);
|
|
148
|
+
const escapedExe = JSON.stringify(process.execPath);
|
|
149
|
+
return [
|
|
150
|
+
'const{spawn}=require("child_process");',
|
|
151
|
+
'const fs=require("fs");',
|
|
152
|
+
`const pid=${parentPid};`,
|
|
153
|
+
'let n=0;',
|
|
154
|
+
'const t=setInterval(()=>{',
|
|
155
|
+
' try{process.kill(pid,0)}catch{',
|
|
156
|
+
' clearInterval(t);',
|
|
157
|
+
' try{',
|
|
158
|
+
` const fd=fs.openSync(${escapedLog},"a");`,
|
|
159
|
+
` const c=spawn(${escapedExe},[${escapedCli},"start"],{detached:true,stdio:["ignore",fd,fd],env:process.env});`,
|
|
160
|
+
' c.unref();',
|
|
161
|
+
` fs.writeFileSync(${escapedPid},String(c.pid)+"\\n");`,
|
|
162
|
+
' fs.closeSync(fd);',
|
|
163
|
+
' }catch(err){',
|
|
164
|
+
` try{fs.appendFileSync(${escapedFailLog},new Date().toISOString()+" helper spawn failed: "+err.message+"\\n")}catch{}`,
|
|
165
|
+
' process.exit(1);',
|
|
166
|
+
' }',
|
|
167
|
+
' process.exit(0);',
|
|
168
|
+
' }',
|
|
169
|
+
' if(++n>150){',
|
|
170
|
+
' clearInterval(t);',
|
|
171
|
+
` try{fs.appendFileSync(${escapedFailLog},new Date().toISOString()+" parent pid "+pid+" still alive after 30s — abandoning\\n")}catch{}`,
|
|
172
|
+
' process.exit(2);',
|
|
173
|
+
' }',
|
|
174
|
+
'},200);',
|
|
175
|
+
].join('');
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=restart-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-flow.js","sourceRoot":"","sources":["../../src/core/restart-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-preflight.d.ts","sourceRoot":"","sources":["../../src/core/restart-preflight.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CAiEnE"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Pre-flight checks run BEFORE a restart is initiated.
|
|
2
|
+
//
|
|
3
|
+
// The whole point: catch "this would obviously fail to come back" before
|
|
4
|
+
// we kill the current process. Failing a check returns the list of
|
|
5
|
+
// errors; the caller (restart-flow.ts) refuses to restart and surfaces
|
|
6
|
+
// them to the user via IM. Service stays alive.
|
|
7
|
+
//
|
|
8
|
+
// Each check has a hard fail (errors -> refuse) and soft fail (warnings ->
|
|
9
|
+
// log, proceed). Warnings are surfaced in the completion message so the
|
|
10
|
+
// user knows about them after the new daemon comes up.
|
|
11
|
+
import { access, constants as fsConstants, stat, statfs } from 'node:fs/promises';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { AGIM_HOME } from './agim-paths.js';
|
|
17
|
+
import { loadConfig } from './onboarding.js';
|
|
18
|
+
import { validateConfig } from './config-schema.js';
|
|
19
|
+
import { logger as rootLogger } from './logger.js';
|
|
20
|
+
const log = rootLogger.child({ component: 'restart-preflight' });
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
/** Path to the compiled cli.js that the restart helper will respawn. */
|
|
23
|
+
const CLI_PATH = join(__dirname, '..', 'cli.js');
|
|
24
|
+
/** Minimum free space in AGIM_HOME for the new daemon to be likely able to
|
|
25
|
+
* log, write databases, etc. Generous floor so noisy logs don't surprise. */
|
|
26
|
+
const MIN_FREE_BYTES = 50 * 1024 * 1024; // 50 MiB
|
|
27
|
+
export async function runPreflightChecks() {
|
|
28
|
+
const errors = [];
|
|
29
|
+
const warnings = [];
|
|
30
|
+
// 1. Config schema validates.
|
|
31
|
+
try {
|
|
32
|
+
const cfg = await loadConfig();
|
|
33
|
+
const res = validateConfig(cfg);
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
errors.push(`config.json 校验失败:${res.errors.join('; ')}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
errors.push(`config.json 读取失败:${err instanceof Error ? err.message : String(err)}`);
|
|
40
|
+
}
|
|
41
|
+
// 2. AGIM_HOME writable.
|
|
42
|
+
try {
|
|
43
|
+
await access(AGIM_HOME, fsConstants.W_OK);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
errors.push(`${AGIM_HOME} 不可写:${err instanceof Error ? err.message : String(err)}`);
|
|
47
|
+
}
|
|
48
|
+
// 3. Disk space.
|
|
49
|
+
try {
|
|
50
|
+
const s = await statfs(AGIM_HOME);
|
|
51
|
+
const free = s.bavail * s.bsize;
|
|
52
|
+
if (free < MIN_FREE_BYTES) {
|
|
53
|
+
errors.push(`${AGIM_HOME} 剩余空间 ${(free / 1024 / 1024).toFixed(0)} MiB,低于最小要求 50 MiB`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
// statfs unavailable on some old platforms — warn but don't fail.
|
|
58
|
+
warnings.push(`无法检查磁盘空间:${err instanceof Error ? err.message : String(err)}`);
|
|
59
|
+
}
|
|
60
|
+
// 4. cli.js exists at the expected path (the restart helper will spawn
|
|
61
|
+
// this very file).
|
|
62
|
+
if (!existsSync(CLI_PATH)) {
|
|
63
|
+
errors.push(`dist 入口找不到:${CLI_PATH}(npm 安装未完成?)`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
try {
|
|
67
|
+
const st = await stat(CLI_PATH);
|
|
68
|
+
if (!st.isFile())
|
|
69
|
+
errors.push(`${CLI_PATH} 不是常规文件`);
|
|
70
|
+
}
|
|
71
|
+
catch { /* covered above */ }
|
|
72
|
+
}
|
|
73
|
+
// 5. Lock file from a previous in-progress restart? If yes, refuse.
|
|
74
|
+
// (concurrency guard is implemented as a file in restart-flow.ts,
|
|
75
|
+
// duplicated check here so pre-flight surfaces the friendly error
|
|
76
|
+
// without writing a new state file.)
|
|
77
|
+
const pendingFile = join(AGIM_HOME, 'restart-pending.json');
|
|
78
|
+
if (existsSync(pendingFile)) {
|
|
79
|
+
try {
|
|
80
|
+
const st = await stat(pendingFile);
|
|
81
|
+
const ageSec = (Date.now() - st.mtimeMs) / 1000;
|
|
82
|
+
if (ageSec < 60) {
|
|
83
|
+
errors.push(`60 秒内已有进行中的重启(${ageSec.toFixed(0)} 秒前发起)`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
warnings.push(`发现过期的 restart-pending.json(${ageSec.toFixed(0)} 秒前),将被覆盖`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch { /* ignore */ }
|
|
90
|
+
}
|
|
91
|
+
const ok = errors.length === 0;
|
|
92
|
+
log.info({ event: 'preflight.result', ok, errors, warnings });
|
|
93
|
+
return { ok, errors, warnings };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=restart-preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restart-preflight.js","sourceRoot":"","sources":["../../src/core/restart-preflight.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,EAAE;AACF,yEAAyE;AACzE,mEAAmE;AACnE,uEAAuE;AACvE,gDAAgD;AAChD,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAE,SAAS,IAAI,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAA;AAEhE,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACzD,wEAAwE;AACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;AAEhD;8EAC8E;AAC9E,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA,CAAE,SAAS;AAQlD,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,QAAQ,GAAa,EAAE,CAAA;IAE7B,8BAA8B;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,UAAU,EAAE,CAAA;QAC9B,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,oBAAoB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACrF,CAAC;IAED,yBAAyB;IACzB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,CAAA;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,QAAQ,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACrF,CAAC;IAED,iBAAiB;IACjB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;QACjC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAA;QAC/B,IAAI,IAAI,GAAG,cAAc,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,SAAS,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAA;QACvF,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kEAAkE;QAClE,QAAQ,CAAC,IAAI,CAAC,YAAY,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,uEAAuE;IACvE,sBAAsB;IACtB,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,cAAc,QAAQ,cAAc,CAAC,CAAA;IACnD,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC/B,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,SAAS,CAAC,CAAA;QACrD,CAAC;QAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;IACjC,CAAC;IAED,oEAAoE;IACpE,qEAAqE;IACrE,qEAAqE;IACrE,wCAAwC;IACxC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAA;IAC3D,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAA;YAClC,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;YAC/C,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;gBAChB,MAAM,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;YACzD,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,IAAI,CAAC,8BAA8B,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;YAC3E,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,CAAA;IAC9B,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC7D,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;AACjC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/core/router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AA2BxC,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;+EAC2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;8EAG0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;wDACoD;IACpD,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;wCAGoC;IACpC,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAmBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/core/router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AA2BxC,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;+EAC2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;8EAG0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;wDACoD;IACpD,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;wCAGoC;IACpC,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAmBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAkExD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CA6P1C;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,EAC5C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,WAAW,EAAE,EACtB,GAAG,EAAE,YAAY,EACjB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,OAAO,EAClB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAyIjC;AAED,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,YAAY,EACjB,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,qBAAqB,GAAG,mBAAmB,GAAG,gBAAgB,CAAC,GAC5G,OAAO,CAAC,gBAAgB,CAAC,CA+B3B"}
|