@foxden-app/foxclaw 0.7.3 → 0.11.0
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/.env.example +22 -8
- package/CHANGELOG.md +134 -0
- package/README.md +21 -1
- package/README_EN.md +21 -1
- package/dist/antigravity/adapter.d.ts +18 -0
- package/dist/antigravity/adapter.js +394 -0
- package/dist/antigravity/auth.d.ts +126 -0
- package/dist/antigravity/auth.js +933 -0
- package/dist/antigravity/client.d.ts +32 -0
- package/dist/antigravity/client.js +244 -0
- package/dist/antigravity/controller.d.ts +101 -0
- package/dist/antigravity/controller.js +2007 -0
- package/dist/antigravity/conversations.d.ts +29 -0
- package/dist/antigravity/conversations.js +289 -0
- package/dist/antigravity/events.d.ts +102 -0
- package/dist/antigravity/events.js +143 -0
- package/dist/antigravity/runtime.d.ts +49 -0
- package/dist/antigravity/runtime.js +82 -0
- package/dist/antigravity/subagents.d.ts +66 -0
- package/dist/antigravity/subagents.js +459 -0
- package/dist/channels/telegram/telegram_channel_adapter.d.ts +10 -3
- package/dist/channels/telegram/telegram_channel_adapter.js +2 -1
- package/dist/channels/weixin/weixin_channel_adapter.d.ts +5 -1
- package/dist/codex_app/adapter.d.ts +20 -0
- package/dist/codex_app/adapter.js +213 -0
- package/dist/codex_app/client.d.ts +1 -1
- package/dist/codex_app/client.js +12 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +65 -16
- package/dist/controller/controller.d.ts +12 -5
- package/dist/controller/controller.js +109 -8
- package/dist/core/attachments.d.ts +11 -0
- package/dist/core/attachments.js +56 -0
- package/dist/core/engine_spi.d.ts +77 -0
- package/dist/core/engine_spi.js +1 -0
- package/dist/core/orchestrator.d.ts +106 -0
- package/dist/core/orchestrator.js +1556 -0
- package/dist/core/stream_preview.d.ts +29 -0
- package/dist/core/stream_preview.js +146 -0
- package/dist/core/turn_queue.d.ts +19 -0
- package/dist/core/turn_queue.js +41 -0
- package/dist/i18n.d.ts +6 -0
- package/dist/i18n.js +49 -0
- package/dist/main.js +375 -446
- package/dist/opencode/adapter.d.ts +11 -0
- package/dist/opencode/adapter.js +169 -0
- package/dist/opencode/controller.js +2 -2
- package/dist/store/database.d.ts +44 -0
- package/dist/store/database.js +275 -43
- package/dist/store/token_usage.d.ts +23 -0
- package/dist/store/token_usage.js +61 -0
- package/dist/telegram/api.d.ts +1 -0
- package/dist/telegram/api.js +80 -3
- package/dist/telegram/bot_home.js +5 -4
- package/dist/telegram/gateway.d.ts +4 -0
- package/dist/telegram/gateway.js +11 -0
- package/dist/types.d.ts +3 -0
- package/dist/update.d.ts +6 -0
- package/dist/update.js +95 -7
- package/dist/voice/tts.js +4 -2
- package/docs/devlogs//346/212/200/346/234/257/351/200/232/350/256/257/02./347/273/237/344/270/200/351/200/232/351/201/223/350/260/203/345/272/246/345/231/250/344/270/216/345/217/257/346/217/222/346/213/224/345/274/225/346/223/216SPI/357/274/232FoxClaw/344/273/216/345/215/225/344/275/223/350/265/260/345/220/221/345/244/232Agent/345/272/225/345/272/247.md +192 -0
- package/docs/devlogs//347/254/254/344/270/200/344/272/272/347/247/260/347/211/210/02./347/257/207/344/272/214_/345/275/223/344/270/200/344/270/252/346/241/245/346/216/245/345/231/250/345/274/200/345/247/213/345/255/246/344/274/232/345/220/254/346/207/202/346/211/200/346/234/211Agent.md +45 -0
- package/package.json +2 -2
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { normalizeTurnActivityEvent } from '../controller/activity.js';
|
|
3
|
+
import { buildAttachmentPrompt } from '../telegram/media.js';
|
|
4
|
+
export class CodexEngineAdapter {
|
|
5
|
+
client;
|
|
6
|
+
id;
|
|
7
|
+
name;
|
|
8
|
+
defaultModel;
|
|
9
|
+
defaultApprovalPolicy;
|
|
10
|
+
defaultSandboxMode;
|
|
11
|
+
constructor(client, options = {}) {
|
|
12
|
+
this.client = client;
|
|
13
|
+
this.id = options.id ?? 'codex';
|
|
14
|
+
this.name = options.name ?? 'OpenAI Codex (App Server)';
|
|
15
|
+
this.defaultModel = options.defaultModel;
|
|
16
|
+
this.defaultApprovalPolicy = options.defaultApprovalPolicy ?? 'on-request';
|
|
17
|
+
this.defaultSandboxMode = options.defaultSandboxMode ?? 'workspace-write';
|
|
18
|
+
}
|
|
19
|
+
async listModels() {
|
|
20
|
+
try {
|
|
21
|
+
const models = await this.client.listModels();
|
|
22
|
+
const list = models.map((m) => ({
|
|
23
|
+
id: m.id,
|
|
24
|
+
name: m.displayName || m.id,
|
|
25
|
+
description: m.description,
|
|
26
|
+
isDefault: m.isDefault || m.id === this.defaultModel,
|
|
27
|
+
}));
|
|
28
|
+
if (!list.some((m) => m.id === 'gpt-6-sol')) {
|
|
29
|
+
list.push({
|
|
30
|
+
id: 'gpt-6-sol',
|
|
31
|
+
name: 'GPT-6-Sol',
|
|
32
|
+
description: 'Next-generation flagship model with deep reasoning',
|
|
33
|
+
isDefault: false,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return list;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
executeTurn(request) {
|
|
43
|
+
const emitter = new EventEmitter();
|
|
44
|
+
let threadId = request.threadId;
|
|
45
|
+
let turnId = null;
|
|
46
|
+
let cancelled = false;
|
|
47
|
+
let promptText = request.prompt;
|
|
48
|
+
if (request.stagedAttachments && request.stagedAttachments.length > 0 && !request.prompt.includes('Telegram attachments:')) {
|
|
49
|
+
promptText = buildAttachmentPrompt(request.prompt, request.stagedAttachments);
|
|
50
|
+
}
|
|
51
|
+
let accumulatedResponse = '';
|
|
52
|
+
let finalResult = null;
|
|
53
|
+
let turnError = null;
|
|
54
|
+
const resultWaiters = [];
|
|
55
|
+
const resolveWaiters = (res) => {
|
|
56
|
+
while (resultWaiters.length > 0) {
|
|
57
|
+
resultWaiters.shift()(res);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const onNotification = (msg) => {
|
|
61
|
+
try {
|
|
62
|
+
const ev = normalizeTurnActivityEvent(msg);
|
|
63
|
+
if (!ev)
|
|
64
|
+
return;
|
|
65
|
+
if (ev.turnId && turnId && ev.turnId !== turnId)
|
|
66
|
+
return;
|
|
67
|
+
if (ev.kind === 'agent_message_delta') {
|
|
68
|
+
accumulatedResponse += ev.delta;
|
|
69
|
+
emitter.emit('delta', ev.delta);
|
|
70
|
+
}
|
|
71
|
+
else if (ev.kind === 'tool_started') {
|
|
72
|
+
const cmdName = Array.isArray(ev.exec?.command) ? ev.exec.command.join(' ') : 'command';
|
|
73
|
+
emitter.emit('tool', {
|
|
74
|
+
name: cmdName,
|
|
75
|
+
status: 'running',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else if (ev.kind === 'tool_completed') {
|
|
79
|
+
const cmdName = Array.isArray(ev.exec?.command) ? ev.exec.command.join(' ') : 'command';
|
|
80
|
+
emitter.emit('tool', {
|
|
81
|
+
name: cmdName,
|
|
82
|
+
status: 'completed',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else if (ev.kind === 'turn_completed') {
|
|
86
|
+
cleanup();
|
|
87
|
+
finalResult = {
|
|
88
|
+
kind: 'result',
|
|
89
|
+
status: ev.state === 'interrupted' ? 'INTERRUPTED' : 'SUCCESS',
|
|
90
|
+
response: accumulatedResponse,
|
|
91
|
+
conversationId: threadId,
|
|
92
|
+
};
|
|
93
|
+
emitter.emit('result', finalResult);
|
|
94
|
+
resolveWaiters(finalResult);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Ignore parsing errors for unrelated notifications
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const cleanup = () => {
|
|
102
|
+
this.client.off('notification', onNotification);
|
|
103
|
+
};
|
|
104
|
+
this.client.on('notification', onNotification);
|
|
105
|
+
const effectiveModel = request.model && request.model !== 'default'
|
|
106
|
+
? request.model
|
|
107
|
+
: (this.defaultModel && this.defaultModel !== 'default' ? this.defaultModel : null);
|
|
108
|
+
const run = async () => {
|
|
109
|
+
try {
|
|
110
|
+
if (!threadId) {
|
|
111
|
+
const session = await this.client.startThread({
|
|
112
|
+
cwd: request.cwd,
|
|
113
|
+
model: effectiveModel,
|
|
114
|
+
approvalPolicy: this.defaultApprovalPolicy,
|
|
115
|
+
sandboxMode: this.defaultSandboxMode,
|
|
116
|
+
});
|
|
117
|
+
threadId = session.thread.threadId;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
try {
|
|
121
|
+
await this.client.resumeThread({
|
|
122
|
+
threadId,
|
|
123
|
+
cwd: request.cwd,
|
|
124
|
+
approvalPolicy: this.defaultApprovalPolicy,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// thread might already be active on server
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (cancelled) {
|
|
132
|
+
cleanup();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!threadId) {
|
|
136
|
+
throw new Error('Failed to resolve or create Codex thread for turn');
|
|
137
|
+
}
|
|
138
|
+
let turn;
|
|
139
|
+
try {
|
|
140
|
+
turn = await this.client.startTurn({
|
|
141
|
+
threadId,
|
|
142
|
+
input: [{ type: 'text', text: promptText, text_elements: [] }],
|
|
143
|
+
cwd: request.cwd,
|
|
144
|
+
model: effectiveModel,
|
|
145
|
+
effort: request.effort ?? null,
|
|
146
|
+
serviceTier: request.serviceTier ?? undefined,
|
|
147
|
+
collaborationMode: null,
|
|
148
|
+
approvalPolicy: this.defaultApprovalPolicy,
|
|
149
|
+
sandboxMode: this.defaultSandboxMode,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (turnErr) {
|
|
153
|
+
const errMsg = String(turnErr);
|
|
154
|
+
if (errMsg.includes('not found') ||
|
|
155
|
+
errMsg.includes('No thread') ||
|
|
156
|
+
errMsg.includes('stale') ||
|
|
157
|
+
errMsg.includes('invalid thread')) {
|
|
158
|
+
const session = await this.client.startThread({
|
|
159
|
+
cwd: request.cwd,
|
|
160
|
+
model: effectiveModel,
|
|
161
|
+
approvalPolicy: this.defaultApprovalPolicy,
|
|
162
|
+
sandboxMode: this.defaultSandboxMode,
|
|
163
|
+
});
|
|
164
|
+
threadId = session.thread.threadId;
|
|
165
|
+
turn = await this.client.startTurn({
|
|
166
|
+
threadId,
|
|
167
|
+
input: [{ type: 'text', text: promptText, text_elements: [] }],
|
|
168
|
+
cwd: request.cwd,
|
|
169
|
+
model: effectiveModel,
|
|
170
|
+
effort: request.effort ?? null,
|
|
171
|
+
serviceTier: request.serviceTier ?? undefined,
|
|
172
|
+
collaborationMode: null,
|
|
173
|
+
approvalPolicy: this.defaultApprovalPolicy,
|
|
174
|
+
sandboxMode: this.defaultSandboxMode,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
throw turnErr;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
turnId = turn.id;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
cleanup();
|
|
185
|
+
turnError = err instanceof Error ? err : new Error(String(err));
|
|
186
|
+
emitter.emit('error', turnError);
|
|
187
|
+
resolveWaiters(null);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
void run();
|
|
191
|
+
return {
|
|
192
|
+
turnId: turnId ?? undefined,
|
|
193
|
+
cancel: () => {
|
|
194
|
+
cancelled = true;
|
|
195
|
+
if (threadId && turnId) {
|
|
196
|
+
void this.client.interruptTurn(threadId, turnId).catch(() => { });
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
waitForResult: async () => {
|
|
200
|
+
if (finalResult)
|
|
201
|
+
return finalResult;
|
|
202
|
+
if (turnError)
|
|
203
|
+
return null;
|
|
204
|
+
return new Promise((resolve) => {
|
|
205
|
+
resultWaiters.push(resolve);
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
on: (event, listener) => {
|
|
209
|
+
emitter.on(event, listener);
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -49,7 +49,7 @@ interface StartTurnOptions {
|
|
|
49
49
|
serviceTier?: string | null | undefined;
|
|
50
50
|
collaborationMode: CodexCollaborationMode | null;
|
|
51
51
|
}
|
|
52
|
-
interface CodexAppServerRuntimeStatus {
|
|
52
|
+
export interface CodexAppServerRuntimeStatus {
|
|
53
53
|
pid: number | null;
|
|
54
54
|
port: number | null;
|
|
55
55
|
running: boolean;
|
package/dist/codex_app/client.js
CHANGED
|
@@ -284,6 +284,18 @@ export class CodexAppClient extends EventEmitter {
|
|
|
284
284
|
models.push(...rows.map(mapModel));
|
|
285
285
|
cursor = typeof result.nextCursor === 'string' ? result.nextCursor : null;
|
|
286
286
|
} while (cursor);
|
|
287
|
+
if (!models.some((m) => m.id === 'gpt-6-sol' || m.model === 'gpt-6-sol')) {
|
|
288
|
+
models.push({
|
|
289
|
+
id: 'gpt-6-sol',
|
|
290
|
+
model: 'gpt-6-sol',
|
|
291
|
+
displayName: 'GPT-6-Sol',
|
|
292
|
+
description: 'Next-generation flagship model with deep reasoning',
|
|
293
|
+
isDefault: false,
|
|
294
|
+
supportedReasoningEfforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
295
|
+
defaultReasoningEffort: 'medium',
|
|
296
|
+
serviceTiers: [],
|
|
297
|
+
});
|
|
298
|
+
}
|
|
287
299
|
return models;
|
|
288
300
|
}
|
|
289
301
|
async readAccount(refreshToken = false) {
|
package/dist/config.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ export declare const DEFAULT_CODEX_APP_SERVER_STATE_PATH: string;
|
|
|
9
9
|
export declare const DEFAULT_CODEX_APP_SERVER_LOG_PATH: string;
|
|
10
10
|
export declare const DEFAULT_OPENCODE_SERVER_STATE_PATH: string;
|
|
11
11
|
export declare const DEFAULT_OPENCODE_SERVER_LOG_PATH: string;
|
|
12
|
+
export declare const DEFAULT_ANTIGRAVITY_AUTH_DIR: string;
|
|
12
13
|
export declare const DEFAULT_CODEX_TELEGRAM_HOME: string;
|
|
14
|
+
export declare const DEFAULT_ANTIGRAVITY_TELEGRAM_HOME: string;
|
|
13
15
|
export declare const DEFAULT_AUTH_SYNC_STATE_PATH: string;
|
|
14
16
|
export declare const DEFAULT_AUTH_SYNC_TEMP_DIR: string;
|
|
15
17
|
export declare const DEFAULT_ENV_PATH: string;
|
|
@@ -17,6 +19,8 @@ export declare function resolveEnvPath(): string;
|
|
|
17
19
|
export declare function getLoadedEnvPath(): string | null;
|
|
18
20
|
export declare function loadEnv(): void;
|
|
19
21
|
export interface AppConfig {
|
|
22
|
+
codexBotTokens: string[];
|
|
23
|
+
antigravityBotTokens: string[];
|
|
20
24
|
tgBotToken: string;
|
|
21
25
|
tgBotTokens: string[];
|
|
22
26
|
tgMultiBotMode: boolean;
|
|
@@ -42,6 +46,13 @@ export interface AppConfig {
|
|
|
42
46
|
opencodeServerPassword: string | null;
|
|
43
47
|
opencodeServerStatePath: string;
|
|
44
48
|
opencodeServerLogPath: string;
|
|
49
|
+
antigravityBotToken: string | null;
|
|
50
|
+
antigravityMultiBotMode: boolean;
|
|
51
|
+
antigravityDefaultRuntimeBotToken: string | null;
|
|
52
|
+
antigravityHome: string | null;
|
|
53
|
+
antigravityCliBin: string;
|
|
54
|
+
antigravityAuthDir: string;
|
|
55
|
+
antigravityDefaultModel: string;
|
|
45
56
|
storePath: string;
|
|
46
57
|
logLevel: LogLevel;
|
|
47
58
|
defaultCwd: string;
|
|
@@ -100,7 +111,10 @@ export interface CodexApiProviderConfig {
|
|
|
100
111
|
}
|
|
101
112
|
export declare function loadConfig(): AppConfig;
|
|
102
113
|
export declare function buildCodexApiProviderOverrides(providers: readonly CodexApiProviderConfig[], defaultProviderId?: string | null): string[];
|
|
114
|
+
export declare function validateCodexBotTokens(tokens: readonly string[]): void;
|
|
103
115
|
export declare function validateOpencodeBotToken(token: string | null, codexTokens: readonly string[]): void;
|
|
116
|
+
export declare function validateAntigravityBotTokens(tokens: readonly string[], codexTokens: readonly string[], opencodeToken: string | null): void;
|
|
117
|
+
export declare function validateAntigravityBotToken(token: string | null, codexTokens: readonly string[], opencodeToken: string | null): void;
|
|
104
118
|
export declare function parseCodexApiProviders(raw: string | undefined): CodexApiProviderConfig[];
|
|
105
119
|
export declare function selectDefaultRuntimeBotToken(configuredTokens: string[], legacyToken: string | null): string | null;
|
|
106
120
|
export declare function ensureAppDirs(config: AppConfig): void;
|
package/dist/config.js
CHANGED
|
@@ -12,7 +12,9 @@ export const DEFAULT_CODEX_APP_SERVER_STATE_PATH = path.join(APP_HOME, 'runtime'
|
|
|
12
12
|
export const DEFAULT_CODEX_APP_SERVER_LOG_PATH = path.join(APP_HOME, 'logs', 'codex-app-server.log');
|
|
13
13
|
export const DEFAULT_OPENCODE_SERVER_STATE_PATH = path.join(APP_HOME, 'runtime', 'opencode-server.json');
|
|
14
14
|
export const DEFAULT_OPENCODE_SERVER_LOG_PATH = path.join(APP_HOME, 'logs', 'opencode-server.log');
|
|
15
|
+
export const DEFAULT_ANTIGRAVITY_AUTH_DIR = path.join(os.homedir(), '.gemini', 'antigravity-cli');
|
|
15
16
|
export const DEFAULT_CODEX_TELEGRAM_HOME = path.join(APP_HOME, 'codex', 'telegram');
|
|
17
|
+
export const DEFAULT_ANTIGRAVITY_TELEGRAM_HOME = path.join(APP_HOME, 'antigravity', 'telegram');
|
|
16
18
|
export const DEFAULT_AUTH_SYNC_STATE_PATH = path.join(APP_HOME, 'runtime', 'auth-sync.json');
|
|
17
19
|
export const DEFAULT_AUTH_SYNC_TEMP_DIR = path.join(APP_HOME, 'runtime', 'auth-sync');
|
|
18
20
|
export const DEFAULT_ENV_PATH = path.join(APP_HOME, '.env');
|
|
@@ -42,26 +44,35 @@ export function loadEnv() {
|
|
|
42
44
|
}
|
|
43
45
|
export function loadConfig() {
|
|
44
46
|
loadEnv();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const tgDefaultRuntimeBotToken = selectDefaultRuntimeBotToken(configuredTokens, legacyToken);
|
|
48
|
-
const tgBotTokens = configuredTokens.length > 0
|
|
49
|
-
? configuredTokens
|
|
50
|
-
: legacyToken
|
|
51
|
-
? [legacyToken]
|
|
52
|
-
: [];
|
|
53
|
-
if (tgBotTokens.length === 0) {
|
|
54
|
-
throw new Error('TG_BOT_TOKENS or TG_BOT_TOKEN is required');
|
|
47
|
+
if (process.env.TG_BOT_TOKEN?.trim() || process.env.TG_BOT_TOKENS?.trim()) {
|
|
48
|
+
throw new Error('Configuration error: TG_BOT_TOKEN and TG_BOT_TOKENS have been removed. Please update .env to use CODEX_BOT_TOKENS and/or ANTIGRAVITY_BOT_TOKENS.');
|
|
55
49
|
}
|
|
50
|
+
const codexBotTokens = parseCommaSeparatedIds(process.env.CODEX_BOT_TOKENS || process.env.CODEX_BOT_TOKEN);
|
|
51
|
+
const antigravityBotTokens = parseCommaSeparatedIds(process.env.ANTIGRAVITY_BOT_TOKENS || process.env.ANTIGRAVITY_BOT_TOKEN);
|
|
56
52
|
const opencodeBotToken = optional('OPENCODE_BOT_TOKEN');
|
|
57
|
-
|
|
53
|
+
if (codexBotTokens.length === 0 && antigravityBotTokens.length === 0) {
|
|
54
|
+
throw new Error('At least one Telegram bot token must be configured via CODEX_BOT_TOKENS or ANTIGRAVITY_BOT_TOKENS');
|
|
55
|
+
}
|
|
56
|
+
validateCodexBotTokens(codexBotTokens);
|
|
57
|
+
validateAntigravityBotTokens(antigravityBotTokens, codexBotTokens, opencodeBotToken);
|
|
58
|
+
validateOpencodeBotToken(opencodeBotToken, [...codexBotTokens, ...antigravityBotTokens]);
|
|
59
|
+
const codexDefaultRuntimeBotToken = selectDefaultRuntimeBotToken(codexBotTokens, optional('CODEX_DEFAULT_RUNTIME_BOT_TOKEN')) || codexBotTokens[0] || null;
|
|
60
|
+
const antigravityDefaultRuntimeBotToken = selectDefaultRuntimeBotToken(antigravityBotTokens, optional('ANTIGRAVITY_DEFAULT_RUNTIME_BOT_TOKEN')) ||
|
|
61
|
+
antigravityBotTokens[0] ||
|
|
62
|
+
null;
|
|
63
|
+
const totalBots = codexBotTokens.length + antigravityBotTokens.length;
|
|
64
|
+
const tgBotTokens = codexBotTokens.length > 0 ? codexBotTokens : antigravityBotTokens;
|
|
65
|
+
const tgBotToken = (codexBotTokens[0] || antigravityBotTokens[0]);
|
|
66
|
+
const antigravityBotToken = antigravityBotTokens[0] || null;
|
|
58
67
|
const config = {
|
|
59
|
-
|
|
68
|
+
codexBotTokens,
|
|
69
|
+
antigravityBotTokens,
|
|
70
|
+
tgBotToken,
|
|
60
71
|
tgBotTokens,
|
|
61
|
-
tgMultiBotMode:
|
|
62
|
-
tgDefaultRuntimeBotToken,
|
|
72
|
+
tgMultiBotMode: totalBots > 1,
|
|
73
|
+
tgDefaultRuntimeBotToken: codexDefaultRuntimeBotToken,
|
|
63
74
|
tgScopeBotId: null,
|
|
64
|
-
tgRequireExplicitGroupAddressing:
|
|
75
|
+
tgRequireExplicitGroupAddressing: totalBots > 1,
|
|
65
76
|
tgAllowedUserId: required('TG_ALLOWED_USER_ID'),
|
|
66
77
|
tgAllowedChatId: optional('TG_ALLOWED_CHAT_ID'),
|
|
67
78
|
tgAllowedTopicId: nullableIntEnv('TG_ALLOWED_TOPIC_ID'),
|
|
@@ -81,6 +92,13 @@ export function loadConfig() {
|
|
|
81
92
|
opencodeServerPassword: optional('OPENCODE_SERVER_PASSWORD'),
|
|
82
93
|
opencodeServerStatePath: process.env.OPENCODE_SERVER_STATE_PATH || DEFAULT_OPENCODE_SERVER_STATE_PATH,
|
|
83
94
|
opencodeServerLogPath: process.env.OPENCODE_SERVER_LOG_PATH || DEFAULT_OPENCODE_SERVER_LOG_PATH,
|
|
95
|
+
antigravityBotToken,
|
|
96
|
+
antigravityMultiBotMode: antigravityBotTokens.length > 0,
|
|
97
|
+
antigravityDefaultRuntimeBotToken,
|
|
98
|
+
antigravityHome: process.env.ANTIGRAVITY_HOME?.trim() || null,
|
|
99
|
+
antigravityCliBin: process.env.ANTIGRAVITY_CLI_BIN || resolveCommand('agy') || '/home/wuya/.local/bin/agy',
|
|
100
|
+
antigravityAuthDir: process.env.ANTIGRAVITY_AUTH_DIR?.trim() || DEFAULT_ANTIGRAVITY_AUTH_DIR,
|
|
101
|
+
antigravityDefaultModel: process.env.ANTIGRAVITY_DEFAULT_MODEL?.trim() || 'gemini-3.8-flash-high',
|
|
84
102
|
storePath: process.env.STORE_PATH || DEFAULT_STORE_PATH,
|
|
85
103
|
logLevel: parseLogLevel(process.env.LOG_LEVEL || 'info'),
|
|
86
104
|
defaultCwd: process.env.DEFAULT_CWD || process.cwd(),
|
|
@@ -149,13 +167,44 @@ export function buildCodexApiProviderOverrides(providers, defaultProviderId = nu
|
|
|
149
167
|
}
|
|
150
168
|
return overrides;
|
|
151
169
|
}
|
|
170
|
+
export function validateCodexBotTokens(tokens) {
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
for (const token of tokens) {
|
|
173
|
+
if (seen.has(token)) {
|
|
174
|
+
throw new Error(`CODEX_BOT_TOKENS contains duplicate Telegram bot token: ${token}`);
|
|
175
|
+
}
|
|
176
|
+
seen.add(token);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
152
179
|
export function validateOpencodeBotToken(token, codexTokens) {
|
|
153
180
|
if (!token)
|
|
154
181
|
return;
|
|
155
182
|
if (codexTokens.includes(token)) {
|
|
156
|
-
throw new Error('OPENCODE_BOT_TOKEN must use a different bot from
|
|
183
|
+
throw new Error('OPENCODE_BOT_TOKEN must use a different bot from CODEX_BOT_TOKENS');
|
|
157
184
|
}
|
|
158
185
|
}
|
|
186
|
+
export function validateAntigravityBotTokens(tokens, codexTokens, opencodeToken) {
|
|
187
|
+
if (tokens.length === 0)
|
|
188
|
+
return;
|
|
189
|
+
const seen = new Set();
|
|
190
|
+
for (const token of tokens) {
|
|
191
|
+
if (seen.has(token)) {
|
|
192
|
+
throw new Error(`ANTIGRAVITY_BOT_TOKENS contains duplicate Telegram bot token: ${token}`);
|
|
193
|
+
}
|
|
194
|
+
seen.add(token);
|
|
195
|
+
if (codexTokens.includes(token)) {
|
|
196
|
+
throw new Error('ANTIGRAVITY_BOT_TOKENS must use a different bot from CODEX_BOT_TOKENS');
|
|
197
|
+
}
|
|
198
|
+
if (opencodeToken && token === opencodeToken) {
|
|
199
|
+
throw new Error('ANTIGRAVITY_BOT_TOKENS must use a different bot from OPENCODE_BOT_TOKEN');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export function validateAntigravityBotToken(token, codexTokens, opencodeToken) {
|
|
204
|
+
if (!token)
|
|
205
|
+
return;
|
|
206
|
+
validateAntigravityBotTokens([token], codexTokens, opencodeToken);
|
|
207
|
+
}
|
|
159
208
|
export function parseCodexApiProviders(raw) {
|
|
160
209
|
if (!raw?.trim())
|
|
161
210
|
return [];
|
|
@@ -2,8 +2,8 @@ import type { AppConfig } from '../config.js';
|
|
|
2
2
|
import type { Logger } from '../logger.js';
|
|
3
3
|
import type { BridgeStore } from '../store/database.js';
|
|
4
4
|
import { type AuthSyncClusterAuditResult } from '../auth/cross_node_sync.js';
|
|
5
|
-
import type { RuntimeStatus } from '../types.js';
|
|
6
|
-
import type { TelegramGateway, TelegramTextEvent } from '../telegram/gateway.js';
|
|
5
|
+
import type { AppLocale, RuntimeStatus } from '../types.js';
|
|
6
|
+
import type { TelegramGateway, TelegramTextEvent, TelegramCallbackEvent } from '../telegram/gateway.js';
|
|
7
7
|
import { BridgeMessagingRouter } from '../channels/bridge_messaging_router.js';
|
|
8
8
|
import type { CodexAppClient } from '../codex_app/client.js';
|
|
9
9
|
import type { SelfUpdateRuntime, SelfUpdateStatus } from '../update.js';
|
|
@@ -71,6 +71,7 @@ export declare class BridgeSessionCore {
|
|
|
71
71
|
private pendingForceTakeovers;
|
|
72
72
|
private pendingLoginScopesById;
|
|
73
73
|
private pendingAuthAddsByLoginId;
|
|
74
|
+
private authLoginRecoveryTimers;
|
|
74
75
|
private latestTurnDiffs;
|
|
75
76
|
private threadTokenUsageAlerts;
|
|
76
77
|
private pendingAuthChoiceLists;
|
|
@@ -119,6 +120,7 @@ export declare class BridgeSessionCore {
|
|
|
119
120
|
* (used by the Weixin adapter).
|
|
120
121
|
*/
|
|
121
122
|
dispatchInboundLikeTelegramText(event: TelegramTextEvent): void;
|
|
123
|
+
hasPendingInteraction(scopeId: string): boolean;
|
|
122
124
|
/** Start Codex app-server transport and attach RPC listeners. */
|
|
123
125
|
startCodexApp(): Promise<void>;
|
|
124
126
|
/** Begin Telegram Bot API long-polling after handlers and Codex are ready. */
|
|
@@ -135,7 +137,7 @@ export declare class BridgeSessionCore {
|
|
|
135
137
|
private handleWatchCommand;
|
|
136
138
|
private handleThreadArchiveIndexCommand;
|
|
137
139
|
private handleThreadRenameIndexCommand;
|
|
138
|
-
|
|
140
|
+
handleCallback(event: TelegramCallbackEvent): Promise<void>;
|
|
139
141
|
private handleApprovalTextCommand;
|
|
140
142
|
private handleNotification;
|
|
141
143
|
private handleServerRequest;
|
|
@@ -155,6 +157,9 @@ export declare class BridgeSessionCore {
|
|
|
155
157
|
private handleServerRequestResolved;
|
|
156
158
|
private handleAccountLoginCompleted;
|
|
157
159
|
private restorePendingAuthAdd;
|
|
160
|
+
private scheduleAuthLoginRecovery;
|
|
161
|
+
private recoverAuthLogin;
|
|
162
|
+
private reconcileAuthLoginCredential;
|
|
158
163
|
private restoreAuthAfterAddFailure;
|
|
159
164
|
private handleAccountUpdated;
|
|
160
165
|
private handleRateLimitsUpdated;
|
|
@@ -327,7 +332,7 @@ export declare class BridgeSessionCore {
|
|
|
327
332
|
private clearProactiveAuthRefreshTimer;
|
|
328
333
|
private runProactiveAuthRefresh;
|
|
329
334
|
private recordProactiveAuthRefreshStatus;
|
|
330
|
-
|
|
335
|
+
handleAuthCommand(scopeId: string, locale: AppLocale, args: string[]): Promise<void>;
|
|
331
336
|
private handleAuthSyncCommand;
|
|
332
337
|
private runAuthSafeSyncAll;
|
|
333
338
|
private runAuthClusterAudit;
|
|
@@ -444,7 +449,9 @@ export declare class BridgeSessionCore {
|
|
|
444
449
|
private handleNavigationCallback;
|
|
445
450
|
private showWherePanel;
|
|
446
451
|
private handleThreadListNavigationCallback;
|
|
447
|
-
|
|
452
|
+
showThreadsPanel(scopeId: string, messageId?: number, searchTerm?: string | null, locale?: AppLocale, options?: {
|
|
453
|
+
offset?: number;
|
|
454
|
+
}, archived?: boolean): Promise<void>;
|
|
448
455
|
private showModelSettingsPanel;
|
|
449
456
|
private showSetupPanel;
|
|
450
457
|
private showAccessSettingsPanel;
|