aegiscode 3.1.10 → 3.4.2
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/bin/aegis.js +165 -0
- package/package.json +18 -86
- package/LICENSE +0 -21
- package/README.md +0 -394
- package/dist/main.js +0 -2171
- package/gui/run-gui.cjs +0 -13
- package/src/agent/Agent.ts +0 -903
- package/src/agent/SimpleAgent.ts +0 -48
- package/src/agent/index.ts +0 -54
- package/src/agent/orchestrator/AppBuilder.ts +0 -443
- package/src/agent/orchestrator/CouncilAgent.ts +0 -310
- package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
- package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
- package/src/agent/orchestrator/index.ts +0 -38
- package/src/agent/orchestrator/utils.ts +0 -397
- package/src/agent/pricing.ts +0 -115
- package/src/agent/router.ts +0 -74
- package/src/agent/routerStats.ts +0 -121
- package/src/agent/types.ts +0 -318
- package/src/auth/login.ts +0 -383
- package/src/cli/config.ts +0 -189
- package/src/cli/index.ts +0 -17
- package/src/cli/middleware.ts +0 -119
- package/src/cli/types.ts +0 -75
- package/src/config/ConfigManager.ts +0 -587
- package/src/config/index.ts +0 -7
- package/src/config/types.ts +0 -584
- package/src/context/CompactionService.ts +0 -300
- package/src/context/ContextManager.ts +0 -450
- package/src/context/FileAnalyzer.ts +0 -267
- package/src/context/TokenCounter.ts +0 -265
- package/src/context/index.ts +0 -27
- package/src/context/storage/CacheStore.ts +0 -176
- package/src/context/storage/JSONLStore.ts +0 -201
- package/src/context/storage/MemoryStore.ts +0 -205
- package/src/context/storage/PersistentStore.ts +0 -327
- package/src/context/storage/index.ts +0 -9
- package/src/context/storage/pathUtils.ts +0 -114
- package/src/context/test.ts +0 -309
- package/src/context/types.ts +0 -268
- package/src/hooks/HookExecutor.ts +0 -434
- package/src/hooks/HookManager.ts +0 -596
- package/src/hooks/HookService.ts +0 -269
- package/src/hooks/Matcher.ts +0 -157
- package/src/hooks/index.ts +0 -63
- package/src/hooks/types.ts +0 -424
- package/src/main.tsx +0 -596
- package/src/mcp/HealthMonitor.ts +0 -150
- package/src/mcp/McpClient.ts +0 -491
- package/src/mcp/McpRegistry.ts +0 -321
- package/src/mcp/createMcpTool.ts +0 -251
- package/src/mcp/index.ts +0 -15
- package/src/mcp/server.ts +0 -334
- package/src/mcp/test-server.ts +0 -88
- package/src/mcp/test.ts +0 -372
- package/src/mcp/types.ts +0 -247
- package/src/memory/AgentMemoryBus.ts +0 -432
- package/src/memory/CloudSync.ts +0 -99
- package/src/memory/DriveSync.ts +0 -106
- package/src/memory/SharedMemory.ts +0 -951
- package/src/memory/index.ts +0 -14
- package/src/memory/machineFingerprint.ts +0 -40
- package/src/orchestrator/SubAgentMetadata.ts +0 -136
- package/src/prompts/builder.ts +0 -213
- package/src/prompts/default.ts +0 -144
- package/src/prompts/index.ts +0 -16
- package/src/prompts/plan.ts +0 -64
- package/src/prompts/test.ts +0 -78
- package/src/services/AnthropicChatService.ts +0 -341
- package/src/services/ChatService.ts +0 -347
- package/src/services/ClaudeCliChatService.ts +0 -256
- package/src/services/CloudSync.ts +0 -168
- package/src/services/CostLedger.ts +0 -211
- package/src/services/Heartbeat.ts +0 -135
- package/src/services/LearningCollector.ts +0 -291
- package/src/services/OllamaInstaller.ts +0 -342
- package/src/services/VersionChecker.ts +0 -445
- package/src/services/index.ts +0 -57
- package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
- package/src/services/streaming/RenderingProfile.ts +0 -90
- package/src/services/streaming/StreamEventParser.ts +0 -181
- package/src/services/streaming/ThrottledRenderer.ts +0 -139
- package/src/services/streaming/TranscriptBuffer.ts +0 -574
- package/src/services/streaming/eventStatusMap.ts +0 -52
- package/src/services/streaming/index.ts +0 -46
- package/src/services/streaming/renderFormatting.ts +0 -79
- package/src/services/streaming/types.ts +0 -234
- package/src/skills/SkillLoader.ts +0 -126
- package/src/skills/SkillRegistry.ts +0 -366
- package/src/skills/index.ts +0 -48
- package/src/skills/types.ts +0 -146
- package/src/slash-commands/billing.ts +0 -70
- package/src/slash-commands/build.ts +0 -413
- package/src/slash-commands/builtinCommands.ts +0 -2733
- package/src/slash-commands/clone.ts +0 -242
- package/src/slash-commands/council.ts +0 -125
- package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
- package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
- package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
- package/src/slash-commands/custom/index.ts +0 -7
- package/src/slash-commands/debate.ts +0 -254
- package/src/slash-commands/gmail.ts +0 -105
- package/src/slash-commands/index.ts +0 -388
- package/src/slash-commands/mcpCommand.ts +0 -205
- package/src/slash-commands/types.ts +0 -201
- package/src/store/index.ts +0 -76
- package/src/store/selectors.ts +0 -246
- package/src/store/slices/appSlice.ts +0 -205
- package/src/store/slices/commandSlice.ts +0 -115
- package/src/store/slices/configSlice.ts +0 -46
- package/src/store/slices/focusSlice.ts +0 -64
- package/src/store/slices/index.ts +0 -9
- package/src/store/slices/sessionSlice.ts +0 -424
- package/src/store/streaming-buffer.ts +0 -425
- package/src/store/test.ts +0 -296
- package/src/store/types.ts +0 -274
- package/src/store/vanilla.ts +0 -186
- package/src/tools/builtin/bash.ts +0 -236
- package/src/tools/builtin/council.ts +0 -105
- package/src/tools/builtin/edit.ts +0 -213
- package/src/tools/builtin/glob.ts +0 -136
- package/src/tools/builtin/grep.ts +0 -263
- package/src/tools/builtin/index.ts +0 -61
- package/src/tools/builtin/memory.ts +0 -66
- package/src/tools/builtin/read.ts +0 -168
- package/src/tools/builtin/skill.ts +0 -97
- package/src/tools/builtin/snapshot.ts +0 -40
- package/src/tools/builtin/task.ts +0 -106
- package/src/tools/builtin/write.ts +0 -134
- package/src/tools/createTool.ts +0 -221
- package/src/tools/execution/ExecutionPipeline.ts +0 -263
- package/src/tools/execution/index.ts +0 -40
- package/src/tools/execution/stages/CacheStage.ts +0 -131
- package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
- package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
- package/src/tools/execution/stages/ExecutionStage.ts +0 -48
- package/src/tools/execution/stages/FormattingStage.ts +0 -44
- package/src/tools/execution/stages/HookStage.ts +0 -72
- package/src/tools/execution/stages/PermissionStage.ts +0 -287
- package/src/tools/execution/stages/PostHookStage.ts +0 -71
- package/src/tools/execution/stages/index.ts +0 -12
- package/src/tools/execution/test.ts +0 -266
- package/src/tools/execution/types.ts +0 -273
- package/src/tools/index.ts +0 -81
- package/src/tools/registry.ts +0 -304
- package/src/tools/schemas.ts +0 -109
- package/src/tools/test.ts +0 -220
- package/src/tools/types.ts +0 -175
- package/src/tools/validation/PermissionChecker.ts +0 -242
- package/src/tools/validation/SensitiveFileDetector.ts +0 -210
- package/src/tools/validation/index.ts +0 -11
- package/src/ui/App.tsx +0 -166
- package/src/ui/components/AegisInterface.tsx +0 -484
- package/src/ui/components/common/ChatSearch.tsx +0 -150
- package/src/ui/components/common/ErrorBoundary.tsx +0 -82
- package/src/ui/components/common/ExitMessage.tsx +0 -120
- package/src/ui/components/common/LoadingIndicator.tsx +0 -49
- package/src/ui/components/common/index.ts +0 -6
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
- package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
- package/src/ui/components/dialog/SetupWizard.tsx +0 -297
- package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
- package/src/ui/components/dialog/index.ts +0 -8
- package/src/ui/components/index.ts +0 -28
- package/src/ui/components/input/CommandSuggestions.tsx +0 -139
- package/src/ui/components/input/CustomTextInput.tsx +0 -220
- package/src/ui/components/input/InputArea.tsx +0 -361
- package/src/ui/components/input/PromptSuggestions.tsx +0 -66
- package/src/ui/components/input/index.ts +0 -6
- package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
- package/src/ui/components/layout/ContextBar.tsx +0 -79
- package/src/ui/components/layout/MessageArea.tsx +0 -96
- package/src/ui/components/layout/MessageList.tsx +0 -647
- package/src/ui/components/layout/MessageSeparator.tsx +0 -26
- package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
- package/src/ui/components/layout/index.ts +0 -7
- package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
- package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
- package/src/ui/components/markdown/index.ts +0 -8
- package/src/ui/components/markdown/parser.ts +0 -336
- package/src/ui/components/markdown/types.ts +0 -66
- package/src/ui/focus/FocusManager.ts +0 -137
- package/src/ui/focus/index.ts +0 -13
- package/src/ui/focus/types.ts +0 -54
- package/src/ui/focus/useFocus.ts +0 -75
- package/src/ui/hooks/index.ts +0 -11
- package/src/ui/hooks/useAgent.ts +0 -284
- package/src/ui/hooks/useCommandHistory.ts +0 -87
- package/src/ui/hooks/useCommandProcessor.ts +0 -443
- package/src/ui/hooks/useConfirmation.ts +0 -99
- package/src/ui/hooks/useCtrlCHandler.ts +0 -100
- package/src/ui/hooks/useInputBuffer.ts +0 -122
- package/src/ui/hooks/useTerminalSize.ts +0 -68
- package/src/ui/hooks/useTerminalWidth.ts +0 -5
- package/src/ui/hooks/useWindowedList.ts +0 -118
- package/src/ui/render-debugger.ts +0 -621
- package/src/ui/test.ts +0 -189
- package/src/ui/themes/ThemeManager.ts +0 -332
- package/src/ui/themes/aegisTheme.ts +0 -87
- package/src/ui/themes/darkTheme.ts +0 -87
- package/src/ui/themes/defaultTheme.ts +0 -85
- package/src/ui/themes/index.ts +0 -10
- package/src/ui/themes/lightTheme.ts +0 -89
- package/src/ui/themes/popularThemes.ts +0 -187
- package/src/ui/themes/types.ts +0 -130
- package/src/utils/clipboard.ts +0 -48
- package/src/utils/debug.ts +0 -43
- package/src/utils/environment.ts +0 -68
- package/src/utils/index.ts +0 -10
package/src/main.tsx
DELETED
|
@@ -1,596 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AEGIS CLI - 主入口
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* 1. 早期解析 --debug 参数(确保日志可用)
|
|
6
|
-
* 2. 启动版本检查(不等待,与后续流程并行)
|
|
7
|
-
* 3. 创建 yargs CLI 实例
|
|
8
|
-
* 4. 注册全局选项和命令
|
|
9
|
-
* 5. 执行中间件链(validatePermissions → loadConfiguration → validateOutput)
|
|
10
|
-
* 6. 执行默认命令 → 启动 React UI(传递 versionCheckPromise)
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* 1. 默认配置
|
|
14
|
-
* 2. 用户配置 (~/.aegiscode/config.json)
|
|
15
|
-
* 3. 项目配置 (./.aegiscode/config.json)
|
|
16
|
-
* 4. 环境变量 (OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL)
|
|
17
|
-
* 5. CLI 参数 (--api-key, --base-url, --model)
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
// RAF polyfill — MUST run before any module that uses requestAnimationFrame.
|
|
21
|
-
// Pass Date.now() as the timestamp so RAF callbacks that throttle by timestamp
|
|
22
|
-
// (e.g. MessageList's RAF_INTERVAL_MS check) work correctly. Without this,
|
|
23
|
-
// `now` is undefined and `undefined - ref < 30` is always false, causing the
|
|
24
|
-
// streaming render loop to fire at the raw setTimeout rate (~16ms) instead of 30ms.
|
|
25
|
-
if (typeof globalThis.requestAnimationFrame === 'undefined') {
|
|
26
|
-
(globalThis as any).requestAnimationFrame = (cb: FrameRequestCallback) => setTimeout(() => cb(Date.now()), 16);
|
|
27
|
-
(globalThis as any).cancelAnimationFrame = (id: number) => clearTimeout(id);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
import { config as dotenvConfig } from 'dotenv';
|
|
31
|
-
import { resolve } from 'path';
|
|
32
|
-
import { homedir } from 'os';
|
|
33
|
-
// --print's whole point is clean, pipeable stdout (text or JSON) — the
|
|
34
|
-
// dotenvx promo banner would otherwise land on stdout ahead of the result.
|
|
35
|
-
const isPrintMode = process.argv.includes('--print') || process.argv.includes('-p');
|
|
36
|
-
// SetupWizard saves keys to ~/.aegiscode/.env (the only place a globally-installed
|
|
37
|
-
// `aegis` binary can reliably find them — process.cwd() is wherever the user happens
|
|
38
|
-
// to invoke it from). Load that first, then ./.env so a project-local file can override it.
|
|
39
|
-
dotenvConfig({ path: resolve(homedir(), '.aegiscode', '.env'), quiet: isPrintMode });
|
|
40
|
-
dotenvConfig({ path: resolve(process.cwd(), '.env'), quiet: isPrintMode, override: true });
|
|
41
|
-
import React from 'react';
|
|
42
|
-
import yargs from 'yargs';
|
|
43
|
-
import { hideBin } from 'yargs/helpers';
|
|
44
|
-
import { App } from './ui/App.js';
|
|
45
|
-
import { configManager } from './config/index.js';
|
|
46
|
-
import { cliConfig, globalOptions, middlewareChain } from './cli/index.js';
|
|
47
|
-
import { checkVersionOnStartup } from './services/index.js';
|
|
48
|
-
import { runCouncil } from './slash-commands/council.js';
|
|
49
|
-
import { getLatestSessionFile } from './context/index.js';
|
|
50
|
-
import { themeManager } from './ui/themes/index.js';
|
|
51
|
-
import { setGlobalDebug } from './utils/debug.js';
|
|
52
|
-
import type { CliArguments } from './cli/types.js';
|
|
53
|
-
import type { VersionCheckResult } from './services/VersionChecker.js';
|
|
54
|
-
import * as path from 'node:path';
|
|
55
|
-
|
|
56
|
-
// Rendering debugger (auto-starts with --debug-rendering flag)
|
|
57
|
-
const ENABLE_RENDER_DEBUG = process.argv.includes('--debug-rendering');
|
|
58
|
-
|
|
59
|
-
// ========== 全局状
|
|
60
|
-
let isDebugMode = false;
|
|
61
|
-
let versionCheckPromise: Promise<VersionCheckResult | null> | undefined;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* - Logger 在各模块中被创建
|
|
68
|
-
* - 如果等 yargs 解析完再设置 debug,部分初始化日志会丢失
|
|
69
|
-
* - 早期解析确保所有日志都能正确输出
|
|
70
|
-
*/
|
|
71
|
-
function parseDebugEarly(): void {
|
|
72
|
-
const rawArgs = hideBin(process.argv)
|
|
73
|
-
const debugIndex = rawArgs.indexOf('--debug')
|
|
74
|
-
const shortDebugIndex = rawArgs.indexOf('-d')
|
|
75
|
-
|
|
76
|
-
if (debugIndex !== -1 || shortDebugIndex !== -1) {
|
|
77
|
-
isDebugMode = true
|
|
78
|
-
setGlobalDebug(true) // 设置全局 debug 状态,供其他模块使
|
|
79
|
-
console.log('[DEBUG] Debug mode enabled via early parsing')
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
*
|
|
85
|
-
*/
|
|
86
|
-
async function main(): Promise<void> {
|
|
87
|
-
// 1. 早期解
|
|
88
|
-
parseDebugEarly();
|
|
89
|
-
|
|
90
|
-
// One-shot memory introspection flags for external callers (e.g. aegiscode-gui's
|
|
91
|
-
// Electron main process) — single source of truth instead of duplicating the
|
|
92
|
-
// SQLite schema/read logic in another codebase. MUST run before anything below
|
|
93
|
-
// imports SharedMemory (its singleton constructor fires init() — TTL eviction +
|
|
94
|
-
// cloud sync — synchronously on first import) or makes network calls (version
|
|
95
|
-
// check, token verification) that a fast read-only call has no business paying for.
|
|
96
|
-
const earlyArgs = process.argv.slice(2);
|
|
97
|
-
// One-shot account re-verification for external callers (aegiscode-gui) — same
|
|
98
|
-
// single-source-of-truth reasoning as the memory flags below.
|
|
99
|
-
if (earlyArgs[0] === '--verify-account-json') {
|
|
100
|
-
const { verifyAccount } = await import('./auth/login.js');
|
|
101
|
-
console.log(JSON.stringify(await verifyAccount()));
|
|
102
|
-
process.exit(0);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (earlyArgs[0] === '--memory-stats-json' || earlyArgs[0] === '--memory-search-json' || earlyArgs[0] === '--memory-clear-json' || earlyArgs[0] === '--memory-upload-json' || earlyArgs[0] === '--memory-download-json') {
|
|
106
|
-
process.env.AEGIS_MEMORY_READONLY = '1';
|
|
107
|
-
const { sharedMemory } = await import('./memory/SharedMemory.js');
|
|
108
|
-
await sharedMemory.whenReady();
|
|
109
|
-
|
|
110
|
-
if (earlyArgs[0] === '--memory-stats-json') {
|
|
111
|
-
console.log(JSON.stringify(sharedMemory.getStats()));
|
|
112
|
-
} else if (earlyArgs[0] === '--memory-search-json') {
|
|
113
|
-
const query = earlyArgs[1] || '';
|
|
114
|
-
const limit = parseInt(earlyArgs[2] || '50', 10);
|
|
115
|
-
const results = query ? await sharedMemory.search(query, limit) : sharedMemory.recent(limit);
|
|
116
|
-
console.log(JSON.stringify(results));
|
|
117
|
-
} else if (earlyArgs[0] === '--memory-upload-json') {
|
|
118
|
-
console.log(JSON.stringify(await sharedMemory.pushAll()));
|
|
119
|
-
} else if (earlyArgs[0] === '--memory-download-json') {
|
|
120
|
-
console.log(JSON.stringify(await sharedMemory.pullAll()));
|
|
121
|
-
} else {
|
|
122
|
-
sharedMemory.clear();
|
|
123
|
-
console.log(JSON.stringify({ ok: true }));
|
|
124
|
-
}
|
|
125
|
-
process.exit(0);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 2. 启动版本检查(不等待,与后续流程并行执
|
|
129
|
-
versionCheckPromise = checkVersionOnStartup();
|
|
130
|
-
|
|
131
|
-
// Memory is available for all logged-in users — no separate token verification needed
|
|
132
|
-
|
|
133
|
-
// Handle --model flag: aegis --model deepseek "question"
|
|
134
|
-
// or aegis --model council "question" for council vote
|
|
135
|
-
const modelArg = process.argv.find(a => a.startsWith('--model='))?.split('=')[1]
|
|
136
|
-
|| (() => { const i = process.argv.indexOf('--model'); return i > -1 ? process.argv[i+1] : null; })();
|
|
137
|
-
|
|
138
|
-
if (modelArg) {
|
|
139
|
-
const { readFileSync } = await import('fs');
|
|
140
|
-
const { homedir } = await import('os');
|
|
141
|
-
try {
|
|
142
|
-
const cfg = JSON.parse(readFileSync(`${homedir()}/.aegiscode/config.json`, 'utf8'));
|
|
143
|
-
const found = (cfg.models || []).find((m: any) => m.id === modelArg);
|
|
144
|
-
if (found) {
|
|
145
|
-
process.env.OPENAI_API_KEY = found.apiKey;
|
|
146
|
-
process.env.OPENAI_BASE_URL = found.baseUrl || found.baseURL;
|
|
147
|
-
// Sätt currentModelId så getDefaultModel() väljer rätt
|
|
148
|
-
cfg.currentModelId = found.id;
|
|
149
|
-
const { writeFileSync } = await import('fs');
|
|
150
|
-
writeFileSync(`${homedir()}/.aegiscode/config.json`, JSON.stringify(cfg, null, 2));
|
|
151
|
-
console.log(`\x1b[38;2;0;229;192m[AEGIS] Model: ${found.name || found.id}\x1b[0m`);
|
|
152
|
-
}
|
|
153
|
-
} catch {}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Handle /council command
|
|
157
|
-
const args = process.argv.slice(2);
|
|
158
|
-
|
|
159
|
-
if (args[0] === '/council' || args[0] === 'council') {
|
|
160
|
-
const question = args.slice(1).join(' ');
|
|
161
|
-
if (!question) {
|
|
162
|
-
console.error('Usage: aegis /council <question>');
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
runCouncil(question).then(() => process.exit(0));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (isDebugMode) {
|
|
169
|
-
console.log('[DEBUG] Version check started (running in parallel)');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// 3. 创建 yargs CLI 实
|
|
173
|
-
const cli = yargs(hideBin(process.argv))
|
|
174
|
-
.scriptName(cliConfig.scriptName)
|
|
175
|
-
.usage(cliConfig.usage)
|
|
176
|
-
.version(cliConfig.version)
|
|
177
|
-
// 注册全局选
|
|
178
|
-
.options(globalOptions)
|
|
179
|
-
|
|
180
|
-
// 注册中间
|
|
181
|
-
.middleware(middlewareChain)
|
|
182
|
-
|
|
183
|
-
// ── auth commands ──────────────────────────────────────────────────────
|
|
184
|
-
.command(
|
|
185
|
-
'login',
|
|
186
|
-
'Log in to aegiscloud (Google or username/password), or Claude Code Pro/Max',
|
|
187
|
-
(y) => y
|
|
188
|
-
.option('password', {
|
|
189
|
-
alias: 'p',
|
|
190
|
-
type: 'boolean',
|
|
191
|
-
describe: 'Log in with username and password instead of browser',
|
|
192
|
-
default: false,
|
|
193
|
-
})
|
|
194
|
-
.option('claude-pro', {
|
|
195
|
-
type: 'boolean',
|
|
196
|
-
describe: 'Authenticate with a Claude Code Pro/Max subscription token instead of an API key',
|
|
197
|
-
default: false,
|
|
198
|
-
}),
|
|
199
|
-
async (argv) => {
|
|
200
|
-
const { runLogin, runLoginPassword, runLoginClaudePro } = await import('./auth/login.js');
|
|
201
|
-
try {
|
|
202
|
-
if ((argv as any).claudePro) {
|
|
203
|
-
await runLoginClaudePro();
|
|
204
|
-
process.exit(0);
|
|
205
|
-
} else if ((argv as any).password) {
|
|
206
|
-
await runLoginPassword();
|
|
207
|
-
} else {
|
|
208
|
-
await runLogin();
|
|
209
|
-
}
|
|
210
|
-
console.log('\n\x1b[32m✓ Logged in successfully.\x1b[0m');
|
|
211
|
-
console.log('\nRun \x1b[1maegis\x1b[0m to start coding.\n');
|
|
212
|
-
} catch (err) {
|
|
213
|
-
console.error('\n\x1b[31m✗ Login failed:\x1b[0m', (err as Error).message);
|
|
214
|
-
process.exit(1);
|
|
215
|
-
}
|
|
216
|
-
process.exit(0);
|
|
217
|
-
},
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
.command(
|
|
221
|
-
'logout',
|
|
222
|
-
'Log out and remove stored aegiscloud credentials',
|
|
223
|
-
() => {},
|
|
224
|
-
async () => {
|
|
225
|
-
const { runLogout } = await import('./auth/login.js');
|
|
226
|
-
runLogout();
|
|
227
|
-
process.exit(0);
|
|
228
|
-
},
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
// 示
|
|
232
|
-
.example('$0', 'Start interactive mode')
|
|
233
|
-
.example('$0 login', 'Log in via browser')
|
|
234
|
-
.example('$0 continue', 'Continue the most recent conversation')
|
|
235
|
-
.example('$0 resume <session-id>', 'Resume a specific conversation by ID')
|
|
236
|
-
.example('$0 "帮我分析这个项目"', 'Start with an initial message')
|
|
237
|
-
.example('$0 --model gpt-4', 'Use a specific model')
|
|
238
|
-
.example('$0 --router', 'Start with the auto-router on')
|
|
239
|
-
.example('$0 --debug', 'Enable debug mode')
|
|
240
|
-
.example('$0 --init', 'Create default config file')
|
|
241
|
-
|
|
242
|
-
// 帮助
|
|
243
|
-
.help()
|
|
244
|
-
.alias('h', 'help')
|
|
245
|
-
|
|
246
|
-
// 7. 错误处
|
|
247
|
-
.fail((msg, err, yargsInstance) => {
|
|
248
|
-
if (err) {
|
|
249
|
-
console.error('💥 An error occurred:')
|
|
250
|
-
console.error(err.message)
|
|
251
|
-
if (isDebugMode && err.stack) {
|
|
252
|
-
console.error('\nStack trace:')
|
|
253
|
-
console.error(err.stack)
|
|
254
|
-
}
|
|
255
|
-
process.exit(1)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (msg) {
|
|
259
|
-
console.error('❌ Invalid arguments:')
|
|
260
|
-
console.error(msg)
|
|
261
|
-
console.error('')
|
|
262
|
-
yargsInstance.showHelp()
|
|
263
|
-
process.exit(1)
|
|
264
|
-
}
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
// 8. 严格模式(禁止未知选
|
|
268
|
-
.strict()
|
|
269
|
-
|
|
270
|
-
// 9. 默认命
|
|
271
|
-
.command(
|
|
272
|
-
'$0 [message..]',
|
|
273
|
-
'Start interactive mode',
|
|
274
|
-
(yargs) => {
|
|
275
|
-
return yargs.positional('message', {
|
|
276
|
-
type: 'string',
|
|
277
|
-
describe: 'Initial message to send (can be multiple words)',
|
|
278
|
-
array: true,
|
|
279
|
-
})
|
|
280
|
-
},
|
|
281
|
-
async (argv) => {
|
|
282
|
-
const args = argv as CliArguments
|
|
283
|
-
|
|
284
|
-
// 处理 --init 命
|
|
285
|
-
if (args.init) {
|
|
286
|
-
const configPath = await configManager.createDefaultConfig()
|
|
287
|
-
console.log(`✅ Created default config at: ${configPath}`)
|
|
288
|
-
console.log('')
|
|
289
|
-
console.log('Please edit the file and add your API key:')
|
|
290
|
-
console.log(` vim ${configPath}`)
|
|
291
|
-
process.exit(0)
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// ── Mandatory login check (Aegiscode Pro) ────────────────────────
|
|
295
|
-
// Aegiscode Pro requires login. No login = no API access.
|
|
296
|
-
const { readFileSync } = await import('node:fs');
|
|
297
|
-
const { homedir } = await import('node:os');
|
|
298
|
-
let hasCredentials = false;
|
|
299
|
-
try {
|
|
300
|
-
const cfg = JSON.parse(readFileSync(`${homedir()}/.aegiscode/config.json`, 'utf8'));
|
|
301
|
-
hasCredentials = !!(cfg?.aegiscloud?.api_key);
|
|
302
|
-
} catch {}
|
|
303
|
-
|
|
304
|
-
const needsLogin = hasCredentials
|
|
305
|
-
? !(await (await import('./auth/login.js')).ensureAccountValid())
|
|
306
|
-
: true;
|
|
307
|
-
|
|
308
|
-
if (needsLogin) {
|
|
309
|
-
const { runLogin } = await import('./auth/login.js');
|
|
310
|
-
try {
|
|
311
|
-
await runLogin();
|
|
312
|
-
console.log('\n\x1b[32m✓ Logged in.\x1b[0m Starting ÆGIS...\n');
|
|
313
|
-
await configManager.initialize(process.cwd());
|
|
314
|
-
} catch (err) {
|
|
315
|
-
console.error('\n\x1b[31m✗ Login failed:\x1b[0m', (err as Error).message);
|
|
316
|
-
console.error('Run \x1b[1maegis login\x1b[0m to try again.');
|
|
317
|
-
process.exit(1);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const modelConfig = configManager.getDefaultModel()
|
|
322
|
-
|
|
323
|
-
// No API key yet — don't exit here. App.tsx already detects this on mount
|
|
324
|
-
// (initializeStoreState) and renders the interactive SetupWizard, which
|
|
325
|
-
// creates ~/.aegiscode/.env for the user. Exiting here with print-only
|
|
326
|
-
// instructions skipped that wizard entirely and left fresh installs
|
|
327
|
-
// stuck telling users to hand-create a hidden folder themselves.
|
|
328
|
-
// --print mode has no UI to fall back on, so it still needs to fail fast.
|
|
329
|
-
if (!modelConfig.apiKey && isPrintMode) {
|
|
330
|
-
process.stderr.write(
|
|
331
|
-
'No API key configured. Run `aegis` once (without --print) to set one up, ' +
|
|
332
|
-
'or set an environment variable: export OPENAI_API_KEY=sk-...\n',
|
|
333
|
-
);
|
|
334
|
-
process.exit(1);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// 获取初始消息(支持多个单
|
|
338
|
-
const messageArray = argv.message as string[] | undefined
|
|
339
|
-
let initialMessage =
|
|
340
|
-
messageArray && messageArray.length > 0
|
|
341
|
-
? messageArray.join(' ')
|
|
342
|
-
: undefined
|
|
343
|
-
|
|
344
|
-
if (isDebugMode && initialMessage) {
|
|
345
|
-
console.log('[DEBUG] Initial message:', initialMessage)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Handle `aegis continue` and `aegis resume <id>` without `--` prefix
|
|
349
|
-
// so they behave like `aegis --continue` and `aegis --resume <id>`.
|
|
350
|
-
if (!args.continue && !args.resume && initialMessage && !initialMessage.startsWith('/')) {
|
|
351
|
-
const trimmed = initialMessage.trim();
|
|
352
|
-
if (trimmed === 'continue') {
|
|
353
|
-
args.continue = true;
|
|
354
|
-
initialMessage = undefined;
|
|
355
|
-
} else if (trimmed.startsWith('resume ') || trimmed.startsWith('resume\t')) {
|
|
356
|
-
const parts = trimmed.split(/\s+/);
|
|
357
|
-
if (parts.length >= 2) {
|
|
358
|
-
args.resume = parts[1];
|
|
359
|
-
initialMessage = undefined;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// 处理 --continue 和 --resume 参
|
|
365
|
-
let resumeSessionId: string | undefined;
|
|
366
|
-
|
|
367
|
-
if (args.continue) {
|
|
368
|
-
// 获取最近的会话文
|
|
369
|
-
const latestSession = await getLatestSessionFile(process.cwd());
|
|
370
|
-
if (latestSession) {
|
|
371
|
-
// 从文件路径提取 sessionId(去掉 .jsonl 扩展
|
|
372
|
-
resumeSessionId = path.basename(latestSession, '.jsonl');
|
|
373
|
-
if (isDebugMode) {
|
|
374
|
-
console.log('[DEBUG] Continuing session:', resumeSessionId);
|
|
375
|
-
}
|
|
376
|
-
} else {
|
|
377
|
-
console.log('No previous session found. Starting a new conversation.');
|
|
378
|
-
}
|
|
379
|
-
} else if (args.resume && typeof args.resume === 'string') {
|
|
380
|
-
resumeSessionId = args.resume;
|
|
381
|
-
if (isDebugMode) {
|
|
382
|
-
console.log('[DEBUG] Resuming session:', resumeSessionId);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// --print: headless mode, no Ink. Runs one turn, writes the result to
|
|
387
|
-
// stdout (text or JSON per --output-format), and exits — no TUI chrome,
|
|
388
|
-
// no spinners, no escape codes, so the output is safe to pipe/script.
|
|
389
|
-
if (args.print) {
|
|
390
|
-
if (!initialMessage) {
|
|
391
|
-
process.stderr.write('Error: --print requires a message, e.g. aegis --print "your question"\n');
|
|
392
|
-
process.exit(1);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const { startCostLedger } = await import('./services/CostLedger.js');
|
|
396
|
-
startCostLedger();
|
|
397
|
-
const { startHeartbeat } = await import('./services/Heartbeat.js');
|
|
398
|
-
startHeartbeat();
|
|
399
|
-
|
|
400
|
-
const { ContextManager } = await import('./context/index.js');
|
|
401
|
-
const { Agent } = await import('./agent/Agent.js');
|
|
402
|
-
|
|
403
|
-
const ctxManager = new ContextManager({ compressionThreshold: 100000 });
|
|
404
|
-
let sessionId: string;
|
|
405
|
-
let priorMessages: { role: 'system' | 'user' | 'assistant' | 'tool'; content: string }[] = [];
|
|
406
|
-
|
|
407
|
-
if (resumeSessionId) {
|
|
408
|
-
const loaded = await ctxManager.loadSession(resumeSessionId);
|
|
409
|
-
sessionId = loaded ? resumeSessionId : await ctxManager.createSession();
|
|
410
|
-
if (loaded) {
|
|
411
|
-
priorMessages = ctxManager.getMessages()
|
|
412
|
-
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
413
|
-
.map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));
|
|
414
|
-
}
|
|
415
|
-
} else {
|
|
416
|
-
sessionId = await ctxManager.createSession();
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
try {
|
|
420
|
-
const agent = await Agent.create({
|
|
421
|
-
apiKey: modelConfig.apiKey ?? '',
|
|
422
|
-
baseURL: modelConfig.baseURL,
|
|
423
|
-
model: modelConfig.model,
|
|
424
|
-
requireConfirmation: false,
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
const result = await agent.chatWithMetadata(initialMessage, {
|
|
428
|
-
sessionId,
|
|
429
|
-
messages: priorMessages,
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
if (!result.success) {
|
|
433
|
-
throw new Error(result.error?.message || 'Agent execution failed');
|
|
434
|
-
}
|
|
435
|
-
const finalMessage = result.finalMessage || '';
|
|
436
|
-
|
|
437
|
-
await ctxManager.addMessage('user', initialMessage);
|
|
438
|
-
await ctxManager.addMessage('assistant', finalMessage);
|
|
439
|
-
await ctxManager.flush();
|
|
440
|
-
|
|
441
|
-
if (args.outputFormat === 'json') {
|
|
442
|
-
process.stdout.write(JSON.stringify({
|
|
443
|
-
result: finalMessage,
|
|
444
|
-
session_id: sessionId,
|
|
445
|
-
num_turns: result.metadata?.turnsCount,
|
|
446
|
-
num_tool_calls: result.metadata?.toolCallsCount,
|
|
447
|
-
total_tokens: result.metadata?.totalTokens,
|
|
448
|
-
}) + '\n');
|
|
449
|
-
} else {
|
|
450
|
-
process.stdout.write(finalMessage + '\n');
|
|
451
|
-
}
|
|
452
|
-
process.exit(0);
|
|
453
|
-
} catch (error) {
|
|
454
|
-
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
455
|
-
process.exit(1);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// 初始化主题(从用户配置加载,或自动检测终端颜色模
|
|
460
|
-
themeManager.initializeFromConfig();
|
|
461
|
-
|
|
462
|
-
// CLI 参数覆盖(如果指定
|
|
463
|
-
if (args.theme && themeManager.hasTheme(args.theme)) {
|
|
464
|
-
themeManager.setTheme(args.theme);
|
|
465
|
-
if (isDebugMode) {
|
|
466
|
-
console.log('[DEBUG] Theme overridden by CLI to:', args.theme);
|
|
467
|
-
}
|
|
468
|
-
} else if (isDebugMode) {
|
|
469
|
-
console.log('[DEBUG] Theme:', themeManager.getCurrentThemeName());
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Check for --plain flag (plain text mode, no Ink)
|
|
473
|
-
const isPlain = args.plain === true;
|
|
474
|
-
if (isDebugMode) console.log('[DEBUG] Plain mode:', isPlain);
|
|
475
|
-
|
|
476
|
-
if (isPlain) {
|
|
477
|
-
// Plain text mode — just relay input/output
|
|
478
|
-
if (initialMessage) {
|
|
479
|
-
console.log(initialMessage);
|
|
480
|
-
}
|
|
481
|
-
const { createInterface } = await import('node:readline');
|
|
482
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
483
|
-
rl.on('line', (line) => {
|
|
484
|
-
if (line.trim()) console.log(line);
|
|
485
|
-
});
|
|
486
|
-
rl.on('close', () => process.exit(0));
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Ink rendering — with fallback for non-TTY environments
|
|
491
|
-
const isTTY = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
492
|
-
if (isDebugMode) console.log('[DEBUG] TTY:', isTTY, '(stdin:', process.stdin.isTTY, 'stdout:', process.stdout.isTTY + ')');
|
|
493
|
-
|
|
494
|
-
// Auto-start rendering debugger if --debug-rendering is set
|
|
495
|
-
if (ENABLE_RENDER_DEBUG) {
|
|
496
|
-
const { startRenderDebugger } = await import('./ui/render-debugger.js');
|
|
497
|
-
startRenderDebugger({ reportInterval: 5000, verbose: false });
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Create proper stdin/stdout for Ink (mock if needed to prevent raw mode errors)
|
|
501
|
-
let renderStdin = process.stdin;
|
|
502
|
-
let renderStdout = process.stdout;
|
|
503
|
-
|
|
504
|
-
// Force isTTY on stdout so Ink renders properly in all terminal environments
|
|
505
|
-
if (!process.stdout.isTTY) {
|
|
506
|
-
(process.stdout as any).isTTY = true;
|
|
507
|
-
if (isDebugMode) console.log('[DEBUG] Forcing isTTY=true on stdout');
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (!isTTY) {
|
|
511
|
-
const { PassThrough } = await import('node:stream');
|
|
512
|
-
const mockStdin = new PassThrough();
|
|
513
|
-
(mockStdin as any).isTTY = true;
|
|
514
|
-
(mockStdin as any).setRawMode = () => {};
|
|
515
|
-
(mockStdin as any).ref = () => {};
|
|
516
|
-
(mockStdin as any).unref = () => {};
|
|
517
|
-
(mockStdin as any).setEncoding = () => {};
|
|
518
|
-
renderStdin = mockStdin as unknown as typeof process.stdin;
|
|
519
|
-
if (isDebugMode) console.log('[DEBUG] Using mock stdin for Ink render');
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Start background services (fire-and-forget, silent on failure)
|
|
523
|
-
const { startCostLedger } = await import('./services/CostLedger.js');
|
|
524
|
-
startCostLedger();
|
|
525
|
-
const { startHeartbeat } = await import('./services/Heartbeat.js');
|
|
526
|
-
startHeartbeat();
|
|
527
|
-
|
|
528
|
-
try {
|
|
529
|
-
const { render } = await import('ink');
|
|
530
|
-
render(
|
|
531
|
-
<App
|
|
532
|
-
apiKey={modelConfig.apiKey ?? ''}
|
|
533
|
-
baseURL={modelConfig.baseURL}
|
|
534
|
-
model={modelConfig.model}
|
|
535
|
-
initialMessage={initialMessage}
|
|
536
|
-
debug={args.debug}
|
|
537
|
-
permissionMode={args.permissionMode}
|
|
538
|
-
versionCheckPromise={versionCheckPromise}
|
|
539
|
-
resumeSessionId={resumeSessionId}
|
|
540
|
-
routerEnabled={args.router}
|
|
541
|
-
/>,
|
|
542
|
-
{
|
|
543
|
-
exitOnCtrlC: false,
|
|
544
|
-
patchConsole: true,
|
|
545
|
-
stdin: renderStdin,
|
|
546
|
-
stdout: process.stdout,
|
|
547
|
-
// Disabled: the alt screen buffer blocks normal mouse text selection/copy
|
|
548
|
-
// in most terminal emulators. Real Claude Code renders inline instead.
|
|
549
|
-
alternateScreen: false,
|
|
550
|
-
maxFps: 30,
|
|
551
|
-
// Without this, Ink's default log-update mode erases and rewrites the
|
|
552
|
-
// ENTIRE terminal output on every re-render, even when only a single
|
|
553
|
-
// line changed (e.g. the input cursor blink, every ~530ms, forever).
|
|
554
|
-
// That full erase+rewrite invalidates any in-progress mouse text
|
|
555
|
-
// selection in terminals like Kitty — selecting a message becomes
|
|
556
|
-
// impossible because the screen keeps getting wiped out from under it.
|
|
557
|
-
// incrementalRendering does real line-level diffing and only rewrites
|
|
558
|
-
// lines that actually changed, leaving untouched lines (and any
|
|
559
|
-
// selection on them) alone.
|
|
560
|
-
incrementalRendering: true,
|
|
561
|
-
},
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
// Handle EOF (Ctrl+D) on real stdin to allow normal terminal closing
|
|
565
|
-
if (isTTY && process.stdin.isTTY) {
|
|
566
|
-
process.stdin.on('end', () => {
|
|
567
|
-
if (isDebugMode) console.log('[DEBUG] EOF received on stdin');
|
|
568
|
-
process.exit(0);
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
} catch (renderError) {
|
|
572
|
-
// Ink rendering failed, fall back to simple text mode
|
|
573
|
-
if (isDebugMode) console.log('[DEBUG] Ink rendering failed:', (renderError as Error).message, '- falling back to text mode');
|
|
574
|
-
if (initialMessage) {
|
|
575
|
-
console.log(initialMessage);
|
|
576
|
-
}
|
|
577
|
-
const { createInterface } = await import('node:readline');
|
|
578
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
579
|
-
rl.on('line', (line) => {
|
|
580
|
-
if (line.trim()) console.log(line);
|
|
581
|
-
});
|
|
582
|
-
rl.on('close', () => process.exit(0));
|
|
583
|
-
}
|
|
584
|
-
})
|
|
585
|
-
await cli.parse()
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// 运行主函
|
|
589
|
-
main().catch((error) => {
|
|
590
|
-
console.error('Fatal error:', error.message)
|
|
591
|
-
if (isDebugMode && error.stack) {
|
|
592
|
-
console.error('\nStack trace:')
|
|
593
|
-
console.error(error.stack)
|
|
594
|
-
}
|
|
595
|
-
process.exit(1)
|
|
596
|
-
})
|