@foxden-app/foxclaw 0.11.1 → 0.13.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 +18 -1
- package/CHANGELOG.md +36 -0
- package/README.md +4 -3
- package/README_EN.md +4 -3
- package/dist/antigravity/account_ui.d.ts +34 -0
- package/dist/antigravity/account_ui.js +354 -0
- package/dist/antigravity/adapter.js +1 -1
- package/dist/antigravity/auth.d.ts +4 -1
- package/dist/antigravity/auth.js +17 -5
- package/dist/antigravity/controller.d.ts +3 -106
- package/dist/antigravity/controller.js +2 -2083
- package/dist/antigravity/conversation_ui.d.ts +56 -0
- package/dist/antigravity/conversation_ui.js +509 -0
- package/dist/antigravity/ui_callbacks.d.ts +6 -0
- package/dist/antigravity/ui_callbacks.js +6 -0
- package/dist/bridge/unified_bridge.d.ts +114 -0
- package/dist/bridge/unified_bridge.js +1343 -0
- package/dist/channels/bridge_messaging_router.d.ts +8 -4
- package/dist/channels/bridge_messaging_router.js +15 -4
- package/dist/channels/telegram/telegram_messaging_port.d.ts +16 -0
- package/dist/channels/telegram/telegram_messaging_port.js +22 -2
- package/dist/channels/text_actions.d.ts +14 -0
- package/dist/channels/text_actions.js +48 -0
- package/dist/channels/weixin/weixin_channel_adapter.d.ts +3 -4
- package/dist/channels/weixin/weixin_channel_adapter.js +2 -2
- package/dist/channels/weixin/weixin_messaging_port.d.ts +16 -1
- package/dist/channels/weixin/weixin_messaging_port.js +34 -7
- package/dist/codex_app/activity.d.ts +62 -0
- package/dist/codex_app/activity.js +418 -0
- package/dist/codex_app/adapter.d.ts +2 -2
- package/dist/codex_app/adapter.js +55 -25
- package/dist/codex_app/backend_ui.d.ts +4 -0
- package/dist/codex_app/backend_ui.js +48 -0
- package/dist/codex_app/client.js +0 -12
- package/dist/config.d.ts +4 -0
- package/dist/config.js +41 -5
- package/dist/controller/access.d.ts +1 -11
- package/dist/controller/access.js +1 -33
- package/dist/controller/activity.d.ts +1 -62
- package/dist/controller/activity.js +2 -418
- package/dist/controller/auth_errors.d.ts +11 -0
- package/dist/controller/auth_errors.js +124 -0
- package/dist/controller/auth_files.d.ts +13 -0
- package/dist/controller/auth_files.js +156 -0
- package/dist/controller/auth_presentation.d.ts +77 -0
- package/dist/controller/auth_presentation.js +697 -0
- package/dist/controller/auth_quota_service.d.ts +27 -0
- package/dist/controller/auth_quota_service.js +151 -0
- package/dist/controller/controller.d.ts +30 -56
- package/dist/controller/controller.js +181 -3497
- package/dist/controller/interactions.d.ts +74 -0
- package/dist/controller/interactions.js +560 -0
- package/dist/controller/local_usage_service.d.ts +19 -0
- package/dist/controller/local_usage_service.js +62 -0
- package/dist/controller/native_panel_service.d.ts +55 -0
- package/dist/controller/native_panel_service.js +214 -0
- package/dist/controller/native_panels.d.ts +61 -0
- package/dist/controller/native_panels.js +784 -0
- package/dist/controller/shared_helpers.d.ts +53 -0
- package/dist/controller/shared_helpers.js +399 -0
- package/dist/controller/state_types.d.ts +390 -0
- package/dist/controller/state_types.js +82 -0
- package/dist/controller/turn_rendering.d.ts +22 -0
- package/dist/controller/turn_rendering.js +288 -0
- package/dist/core/access.d.ts +11 -0
- package/dist/core/access.js +33 -0
- package/dist/core/attachment_files.d.ts +13 -0
- package/dist/core/attachment_files.js +180 -0
- package/dist/core/attachment_types.d.ts +22 -0
- package/dist/core/attachment_types.js +1 -0
- package/dist/core/attachments.d.ts +5 -3
- package/dist/core/attachments.js +2 -2
- package/dist/core/backend_registry.d.ts +22 -0
- package/dist/core/backend_registry.js +50 -0
- package/dist/core/backend_ui.d.ts +28 -0
- package/dist/core/backend_ui.js +1 -0
- package/dist/core/channel_events.d.ts +48 -0
- package/dist/core/channel_events.js +1 -0
- package/dist/core/channel_port.d.ts +52 -8
- package/dist/core/engine_spi.d.ts +20 -5
- package/dist/core/listener_scope.d.ts +11 -0
- package/dist/core/listener_scope.js +10 -0
- package/dist/core/message_text.d.ts +2 -0
- package/dist/core/message_text.js +28 -0
- package/dist/core/network_errors.d.ts +1 -0
- package/dist/core/network_errors.js +23 -0
- package/dist/core/orchestrator.d.ts +57 -22
- package/dist/core/orchestrator.js +877 -477
- package/dist/core/queued_engine_input.d.ts +12 -0
- package/dist/core/queued_engine_input.js +16 -0
- package/dist/core/runtime_supervisor.d.ts +19 -0
- package/dist/core/runtime_supervisor.js +45 -0
- package/dist/core/scope_operations.d.ts +7 -0
- package/dist/core/scope_operations.js +19 -0
- package/dist/core/stream_preview.js +21 -9
- package/dist/dsh/adapter.d.ts +48 -0
- package/dist/dsh/adapter.js +205 -0
- package/dist/dsh/backend.d.ts +7 -0
- package/dist/dsh/backend.js +21 -0
- package/dist/dsh/client.d.ts +46 -0
- package/dist/dsh/client.js +184 -0
- package/dist/dsh/permissions_plugin.d.ts +30 -0
- package/dist/dsh/permissions_plugin.js +21 -0
- package/dist/dsh/runtime.d.ts +20 -0
- package/dist/dsh/runtime.js +36 -0
- package/dist/dsh/ui.d.ts +38 -0
- package/dist/dsh/ui.js +342 -0
- package/dist/i18n.d.ts +4 -0
- package/dist/i18n.js +12 -0
- package/dist/main.js +144 -72
- package/dist/opencode/adapter.js +28 -9
- package/dist/opencode/controller.js +1 -1
- package/dist/store/channel_inbox.d.ts +14 -0
- package/dist/store/channel_inbox.js +21 -0
- package/dist/store/database.d.ts +10 -4
- package/dist/store/database.js +13 -3
- package/dist/store/task_journal.d.ts +32 -0
- package/dist/store/task_journal.js +70 -0
- package/dist/telegram/api.d.ts +2 -2
- package/dist/telegram/api.js +10 -28
- package/dist/telegram/gateway.d.ts +13 -27
- package/dist/telegram/gateway.js +86 -43
- package/dist/telegram/media.d.ts +2 -34
- package/dist/telegram/media.js +1 -180
- package/dist/telegram/task_preview.d.ts +13 -0
- package/dist/telegram/task_preview.js +92 -0
- package/dist/telegram/task_result.d.ts +2 -0
- package/dist/telegram/task_result.js +12 -0
- package/dist/telegram/text.d.ts +1 -1
- package/dist/telegram/text.js +2 -27
- package/dist/types.d.ts +2 -2
- package/docs/dsh.md +83 -0
- package/docs/release.md +13 -11
- package/docs/zh/architecture-review.md +106 -0
- package/docs/zh/dsh.md +83 -0
- package/docs/zh/release.md +13 -11
- package/docs/zh/telegram-experience.md +40 -0
- package/package.json +7 -5
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { stringOrNull } from './shared_helpers.js';
|
|
2
|
+
export function classifyCodexAuthRotationError(params) {
|
|
3
|
+
const collected = collectCodexErrorText(params);
|
|
4
|
+
if (isChatGptBackendAccessBlocked(collected)) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const code = stringOrNull(params?.error?.codexErrorInfo) ?? stringOrNull(params?.error?.code);
|
|
8
|
+
const message = stringOrNull(params?.error?.message) ?? '';
|
|
9
|
+
const text = `${code ?? ''}\n${message}\n${collected}`;
|
|
10
|
+
if (isCodexAuthInvalidError(text)) {
|
|
11
|
+
return 'auth_invalid';
|
|
12
|
+
}
|
|
13
|
+
if (isCodexQuotaLimitError(text)) {
|
|
14
|
+
return 'quota_limited';
|
|
15
|
+
}
|
|
16
|
+
if (code && /auth|unauthorized|forbidden|login/i.test(code)) {
|
|
17
|
+
return 'auth_invalid';
|
|
18
|
+
}
|
|
19
|
+
return /(not authenticated|unauthorized|forbidden|sign in|log in|login|auth)/i.test(message)
|
|
20
|
+
? 'auth_invalid'
|
|
21
|
+
: null;
|
|
22
|
+
}
|
|
23
|
+
export function isCodexAuthInvalidError(text) {
|
|
24
|
+
return /token[_\s-]?invalidated/i.test(text)
|
|
25
|
+
|| /authentication token has been invalidated/i.test(text)
|
|
26
|
+
|| /\b401\s+unauthorized\b/i.test(text)
|
|
27
|
+
|| /\bunauthorized\b/i.test(text)
|
|
28
|
+
|| /\bnot authenticated\b/i.test(text)
|
|
29
|
+
|| /\bsign in again\b/i.test(text)
|
|
30
|
+
|| /\blog in again\b/i.test(text);
|
|
31
|
+
}
|
|
32
|
+
export function isCodexQuotaLimitError(text) {
|
|
33
|
+
return /usageLimitExceeded/i.test(text)
|
|
34
|
+
|| /you['’]?ve hit your usage limit/i.test(text)
|
|
35
|
+
|| /\busage limit(?:s)?\b/i.test(text)
|
|
36
|
+
|| /\brate limit(?:ed|s)?\b/i.test(text)
|
|
37
|
+
|| /\btoo many requests\b/i.test(text)
|
|
38
|
+
|| /\binsufficient[_\s-]?quota\b/i.test(text)
|
|
39
|
+
|| /\bquota exceeded\b/i.test(text)
|
|
40
|
+
|| /\bbilling hard limit\b/i.test(text)
|
|
41
|
+
|| /\bcredits? exhausted\b/i.test(text)
|
|
42
|
+
|| /\bout of credits?\b/i.test(text)
|
|
43
|
+
|| /\bcredits? limit\b/i.test(text);
|
|
44
|
+
}
|
|
45
|
+
export function formatCodexNotificationError(params) {
|
|
46
|
+
const collected = collectCodexErrorText(params);
|
|
47
|
+
const known = formatKnownCodexAccessError(collected);
|
|
48
|
+
if (known) {
|
|
49
|
+
return known;
|
|
50
|
+
}
|
|
51
|
+
const message = stringOrNull(params?.error?.message);
|
|
52
|
+
if (message) {
|
|
53
|
+
return clipUserFacingError(cleanUserFacingError(message));
|
|
54
|
+
}
|
|
55
|
+
const code = stringOrNull(params?.error?.codexErrorInfo) ?? stringOrNull(params?.error?.code);
|
|
56
|
+
if (code) {
|
|
57
|
+
return clipUserFacingError(cleanUserFacingError(code));
|
|
58
|
+
}
|
|
59
|
+
return clipUserFacingError(cleanUserFacingError(JSON.stringify(params?.error ?? params ?? {})));
|
|
60
|
+
}
|
|
61
|
+
export function isRetryableCodexTransportError(params) {
|
|
62
|
+
if (params?.willRetry !== true) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const errorInfo = params?.error?.codexErrorInfo;
|
|
66
|
+
return Boolean(errorInfo && typeof errorInfo === 'object' && 'responseStreamDisconnected' in errorInfo);
|
|
67
|
+
}
|
|
68
|
+
export function collectCodexErrorText(params) {
|
|
69
|
+
const parts = [
|
|
70
|
+
stringOrNull(params?.error?.message),
|
|
71
|
+
stringOrNull(params?.error?.additionalDetails),
|
|
72
|
+
stringOrNull(params?.additionalDetails),
|
|
73
|
+
stringOrNull(params?.error?.codexErrorInfo),
|
|
74
|
+
stringOrNull(params?.error?.code),
|
|
75
|
+
].filter((part) => part !== null);
|
|
76
|
+
if (parts.length > 0) {
|
|
77
|
+
return parts.join(' ');
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return JSON.stringify(params?.error ?? params ?? {});
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return String(params?.error ?? params ?? '');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function formatKnownCodexAccessError(raw) {
|
|
87
|
+
if (!isChatGptBackendAccessBlocked(raw)) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const ray = raw.match(/\bcf-ray:\s*([A-Za-z0-9-]+)/i)?.[1]
|
|
91
|
+
?? raw.match(/\bRay ID:\s*([A-Za-z0-9-]+)/i)?.[1]
|
|
92
|
+
?? null;
|
|
93
|
+
return `ChatGPT backend 403 Forbidden: service network/proxy/IP is blocked, not necessarily auth.json invalid. Check HTTP_PROXY/HTTPS_PROXY in the FoxClaw env file and restart FoxClaw${ray ? ` (cf-ray: ${ray})` : ''}.`;
|
|
94
|
+
}
|
|
95
|
+
export function isChatGptBackendAccessBlocked(raw) {
|
|
96
|
+
const value = raw.toLowerCase();
|
|
97
|
+
const targetsChatGptBackend = value.includes('chatgpt.com/backend-api')
|
|
98
|
+
|| value.includes('wss://chatgpt.com/backend-api');
|
|
99
|
+
const isForbidden = value.includes('403 forbidden')
|
|
100
|
+
|| value.includes('status 403')
|
|
101
|
+
|| value.includes('httpstatuscode":403')
|
|
102
|
+
|| value.includes('httpstatuscode:403');
|
|
103
|
+
const looksLikeHtmlBlock = value.includes('<html')
|
|
104
|
+
|| value.includes('text/html')
|
|
105
|
+
|| value.includes('cf-ray')
|
|
106
|
+
|| value.includes('unable to load site')
|
|
107
|
+
|| value.includes('if you are using a vpn');
|
|
108
|
+
return targetsChatGptBackend && isForbidden && looksLikeHtmlBlock;
|
|
109
|
+
}
|
|
110
|
+
export function cleanUserFacingError(raw) {
|
|
111
|
+
return raw
|
|
112
|
+
.replace(/<script\b[\s\S]*?<\/script>/gi, ' ')
|
|
113
|
+
.replace(/<style\b[\s\S]*?<\/style>/gi, ' ')
|
|
114
|
+
.replace(/<[^>]+>/g, ' ')
|
|
115
|
+
.replace(/\s+/g, ' ')
|
|
116
|
+
.trim();
|
|
117
|
+
}
|
|
118
|
+
export function clipUserFacingError(raw, limit = 900) {
|
|
119
|
+
const cleaned = raw.trim();
|
|
120
|
+
if (cleaned.length <= limit) {
|
|
121
|
+
return cleaned;
|
|
122
|
+
}
|
|
123
|
+
return `${cleaned.slice(0, limit - 3)}...`;
|
|
124
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CodexAuthCandidateState } from '../store/database.js';
|
|
2
|
+
import { CodexAuthState, CodexAuthSwitchResult } from './state_types.js';
|
|
3
|
+
export declare function codexAuthDir(explicitAuthDir?: string | null): string;
|
|
4
|
+
export declare function listCodexAuthState(disabledNames?: Set<string>, candidateStates?: Map<string, CodexAuthCandidateState>, explicitAuthDir?: string | null): Promise<CodexAuthState>;
|
|
5
|
+
export declare function isCodexAuthCandidateName(name: string): boolean;
|
|
6
|
+
export declare function isCodexApiKeyAuthCandidate(candidatePath: string): Promise<boolean>;
|
|
7
|
+
export declare function codexAuthCandidateNameFromAddName(raw: string): string | null;
|
|
8
|
+
export declare function resolveCurrentAuthTarget(authDir: string, authPath: string): Promise<string | null>;
|
|
9
|
+
export declare function resolveAuthFinalTargetPath(startPath: string): Promise<string>;
|
|
10
|
+
export declare function authPathDisplayLabel(authPath: string): Promise<string>;
|
|
11
|
+
export declare function pointCodexAuthAtTarget(authDir: string, authPath: string, targetPath: string): Promise<void>;
|
|
12
|
+
export declare function restoreCodexAuthTarget(authDir: string, authPath: string, targetPath: string | null, regularAuthContents: string | null): Promise<void>;
|
|
13
|
+
export declare function switchCodexAuth(targetPath: string, explicitAuthDir?: string | null): Promise<CodexAuthSwitchResult>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import { isFileMissingError } from './shared_helpers.js';
|
|
5
|
+
export function codexAuthDir(explicitAuthDir = null) {
|
|
6
|
+
return explicitAuthDir || process.env.CODEX_AUTH_DIR || path.join(os.homedir(), '.codex');
|
|
7
|
+
}
|
|
8
|
+
export async function listCodexAuthState(disabledNames = new Set(), candidateStates = new Map(), explicitAuthDir = null) {
|
|
9
|
+
const authDir = codexAuthDir(explicitAuthDir);
|
|
10
|
+
const authPath = path.join(authDir, 'auth.json');
|
|
11
|
+
const currentTargetPath = await resolveCurrentAuthTarget(authDir, authPath);
|
|
12
|
+
const candidates = [];
|
|
13
|
+
const entries = await fs.readdir(authDir, { withFileTypes: true }).catch((error) => {
|
|
14
|
+
if (isFileMissingError(error)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
});
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
if (!isCodexAuthCandidateName(entry.name)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const candidatePath = path.join(authDir, entry.name);
|
|
24
|
+
const stat = await fs.stat(candidatePath).catch(() => null);
|
|
25
|
+
if (!stat?.isFile()) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
candidates.push({
|
|
29
|
+
name: entry.name,
|
|
30
|
+
path: candidatePath,
|
|
31
|
+
isCurrent: currentTargetPath === candidatePath,
|
|
32
|
+
disabled: disabledNames.has(entry.name),
|
|
33
|
+
state: candidateStates.get(entry.name) ?? 'active',
|
|
34
|
+
mtimeMs: stat.mtimeMs,
|
|
35
|
+
credentialKind: 'invalid',
|
|
36
|
+
credentialLastRefreshMs: null,
|
|
37
|
+
credentialExpiresAtMs: null,
|
|
38
|
+
quota: null,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
candidates.sort((left, right) => left.name.localeCompare(right.name, undefined, { numeric: true }));
|
|
42
|
+
return {
|
|
43
|
+
authDir,
|
|
44
|
+
authPath,
|
|
45
|
+
currentTargetPath,
|
|
46
|
+
currentLabel: currentTargetPath ? await authPathDisplayLabel(currentTargetPath) : null,
|
|
47
|
+
candidates,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function isCodexAuthCandidateName(name) {
|
|
51
|
+
if (name === 'auth.json' || name.startsWith('.auth.json.')) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return name.startsWith('auth.json_') || name.startsWith('auth.json.') || name.startsWith('auth.json-');
|
|
55
|
+
}
|
|
56
|
+
export async function isCodexApiKeyAuthCandidate(candidatePath) {
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(await fs.readFile(candidatePath, 'utf8'));
|
|
59
|
+
return typeof parsed.openai_api_key === 'string' && parsed.openai_api_key.trim().length > 0;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function codexAuthCandidateNameFromAddName(raw) {
|
|
66
|
+
const normalized = raw.trim();
|
|
67
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(normalized)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const name = normalized.startsWith('auth.json_')
|
|
71
|
+
|| normalized.startsWith('auth.json.')
|
|
72
|
+
|| normalized.startsWith('auth.json-')
|
|
73
|
+
? normalized
|
|
74
|
+
: `auth.json_${normalized}`;
|
|
75
|
+
return isCodexAuthCandidateName(name) ? name : null;
|
|
76
|
+
}
|
|
77
|
+
export async function resolveCurrentAuthTarget(authDir, authPath) {
|
|
78
|
+
const stat = await fs.lstat(authPath).catch(() => null);
|
|
79
|
+
if (!stat) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (stat.isSymbolicLink()) {
|
|
83
|
+
const target = await fs.readlink(authPath);
|
|
84
|
+
return path.resolve(authDir, target);
|
|
85
|
+
}
|
|
86
|
+
return authPath;
|
|
87
|
+
}
|
|
88
|
+
export async function resolveAuthFinalTargetPath(startPath) {
|
|
89
|
+
let currentPath = startPath;
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
for (let depth = 0; depth < 20; depth += 1) {
|
|
92
|
+
const absolutePath = path.resolve(currentPath);
|
|
93
|
+
if (seen.has(absolutePath)) {
|
|
94
|
+
return currentPath;
|
|
95
|
+
}
|
|
96
|
+
seen.add(absolutePath);
|
|
97
|
+
const stat = await fs.lstat(currentPath).catch(() => null);
|
|
98
|
+
if (!stat?.isSymbolicLink()) {
|
|
99
|
+
return currentPath;
|
|
100
|
+
}
|
|
101
|
+
const target = await fs.readlink(currentPath);
|
|
102
|
+
currentPath = path.resolve(path.dirname(currentPath), target);
|
|
103
|
+
}
|
|
104
|
+
return currentPath;
|
|
105
|
+
}
|
|
106
|
+
export async function authPathDisplayLabel(authPath) {
|
|
107
|
+
return path.basename(await resolveAuthFinalTargetPath(authPath));
|
|
108
|
+
}
|
|
109
|
+
export async function pointCodexAuthAtTarget(authDir, authPath, targetPath) {
|
|
110
|
+
await fs.mkdir(authDir, { recursive: true });
|
|
111
|
+
const tempLink = path.join(authDir, `.auth.json.${process.pid}.${Date.now()}.tmp`);
|
|
112
|
+
try {
|
|
113
|
+
await fs.symlink(targetPath, tempLink);
|
|
114
|
+
await fs.rename(tempLink, authPath);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
await fs.unlink(tempLink).catch(() => { });
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export async function restoreCodexAuthTarget(authDir, authPath, targetPath, regularAuthContents) {
|
|
122
|
+
if (regularAuthContents !== null) {
|
|
123
|
+
await fs.mkdir(authDir, { recursive: true });
|
|
124
|
+
const temporary = path.join(authDir, `.auth.json.${process.pid}.${Date.now()}.restore`);
|
|
125
|
+
try {
|
|
126
|
+
await fs.writeFile(temporary, regularAuthContents, { mode: 0o600 });
|
|
127
|
+
await fs.rename(temporary, authPath);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
await fs.unlink(temporary).catch(() => undefined);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (targetPath) {
|
|
136
|
+
await pointCodexAuthAtTarget(authDir, authPath, targetPath);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
await fs.unlink(authPath).catch((error) => {
|
|
140
|
+
if (!isFileMissingError(error)) {
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
export async function switchCodexAuth(targetPath, explicitAuthDir = null) {
|
|
146
|
+
const state = await listCodexAuthState(new Set(), new Map(), explicitAuthDir);
|
|
147
|
+
const candidate = state.candidates.find(entry => entry.path === targetPath);
|
|
148
|
+
if (!candidate) {
|
|
149
|
+
throw new Error(`Auth candidate is no longer available: ${path.basename(targetPath)}`);
|
|
150
|
+
}
|
|
151
|
+
await pointCodexAuthAtTarget(state.authDir, state.authPath, candidate.path);
|
|
152
|
+
return {
|
|
153
|
+
fromLabel: state.currentLabel,
|
|
154
|
+
toLabel: await authPathDisplayLabel(candidate.path),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { AppLocale, CodexAccountInfo, CodexAccountRateLimits, CodexRateLimitSnapshot, CodexRateLimitWindow, RuntimeStatus } from '../types.js';
|
|
2
|
+
import type { CodexAuthQuotaSnapshotRecord } from '../store/database.js';
|
|
3
|
+
import { type ChatGptAuthMetadata } from '../auth/mirror.js';
|
|
4
|
+
import { CodexAuthListView, CodexAuthCandidate, PendingAuthChoiceList, CodexAuthListFilter, CodexAuthState, CodexAuthCandidateHealth, CodexAuthRefreshAllResult, CodexAuthClusterAuditOutcome, AuthProactiveRefreshStatus, AuthSyncRuntimeEvent, CodexAuthQuotaSnapshot } from './state_types.js';
|
|
5
|
+
export declare function parseCodexAuthListRequest(action: string, args: string[]): Partial<CodexAuthListView> | null;
|
|
6
|
+
export declare function createPendingAuthChoiceList(chatId: string, candidates: CodexAuthCandidate[], view?: Partial<CodexAuthListView>): PendingAuthChoiceList;
|
|
7
|
+
export declare function clampCodexAuthListOffset(view: PendingAuthChoiceList): void;
|
|
8
|
+
export declare function codexAuthListPage(candidates: CodexAuthCandidate[], view: CodexAuthListView | null): {
|
|
9
|
+
visible: Array<{
|
|
10
|
+
candidate: CodexAuthCandidate;
|
|
11
|
+
index: number;
|
|
12
|
+
}>;
|
|
13
|
+
filteredCount: number;
|
|
14
|
+
offset: number;
|
|
15
|
+
pageIndex: number;
|
|
16
|
+
pageCount: number;
|
|
17
|
+
filter: CodexAuthListFilter;
|
|
18
|
+
searchTerm: string | null;
|
|
19
|
+
};
|
|
20
|
+
export declare function filterCodexAuthCandidates(candidates: CodexAuthCandidate[], view: Pick<CodexAuthListView, 'filter' | 'searchTerm'>): Array<{
|
|
21
|
+
candidate: CodexAuthCandidate;
|
|
22
|
+
index: number;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function renderAuthListMessage(locale: AppLocale, state: CodexAuthState, botLabel?: string | null, includeWeixinCopyPaste?: boolean, view?: CodexAuthListView | null): string;
|
|
25
|
+
export declare function authChoiceKeyboard(locale: AppLocale, record: PendingAuthChoiceList): Array<Array<{
|
|
26
|
+
text: string;
|
|
27
|
+
callback_data: string;
|
|
28
|
+
}>>;
|
|
29
|
+
export declare function formatCodexAuthCandidateDisplayName(name: string): string;
|
|
30
|
+
export declare function authFilterButton(locale: AppLocale, record: PendingAuthChoiceList, filter: CodexAuthListFilter): {
|
|
31
|
+
text: string;
|
|
32
|
+
callback_data: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function codexAuthCandidateHealth(candidate: CodexAuthCandidate): CodexAuthCandidateHealth;
|
|
35
|
+
export declare function codexAuthCandidateNeedsAttention(candidate: CodexAuthCandidate): boolean;
|
|
36
|
+
export declare function formatCodexAuthCandidateStatus(locale: AppLocale, candidate: CodexAuthCandidate): string;
|
|
37
|
+
export declare function formatCodexAuthFilter(locale: AppLocale, filter: CodexAuthListFilter): string;
|
|
38
|
+
export declare function authRefreshAllConfirmKeyboard(locale: AppLocale, record: PendingAuthChoiceList): Array<Array<{
|
|
39
|
+
text: string;
|
|
40
|
+
callback_data: string;
|
|
41
|
+
}>>;
|
|
42
|
+
export declare function authRepairKeyboard(locale: AppLocale, record: PendingAuthChoiceList, index: number): Array<Array<{
|
|
43
|
+
text: string;
|
|
44
|
+
callback_data: string;
|
|
45
|
+
}>>;
|
|
46
|
+
export declare function formatAuthRefreshAllResult(locale: AppLocale, result: CodexAuthRefreshAllResult, mode?: 'manual' | 'proactive'): string;
|
|
47
|
+
export declare function formatAuthClusterAuditResult(locale: AppLocale, outcome: CodexAuthClusterAuditOutcome): string;
|
|
48
|
+
export declare function formatAuthSyncStatus(locale: AppLocale, status: RuntimeStatus['authSync'], proactiveRefresh?: AuthProactiveRefreshStatus | null): string;
|
|
49
|
+
export declare function formatAuthProactiveRefreshStatus(locale: AppLocale, status: AuthProactiveRefreshStatus): string;
|
|
50
|
+
export declare function formatAuthProactiveRefreshState(locale: AppLocale, state: AuthProactiveRefreshStatus['state']): string;
|
|
51
|
+
export declare function formatLimitedList(values: readonly string[], limit: number): string;
|
|
52
|
+
export declare function formatAuthSyncEvents(locale: AppLocale, status: RuntimeStatus['authSync'], filter: string | null): string;
|
|
53
|
+
export declare function formatAuthSyncTrace(locale: AppLocale, status: RuntimeStatus['authSync'], requestId: string): string;
|
|
54
|
+
export declare function filterAuthSyncEvents(events: AuthSyncRuntimeEvent[], filter: string | null): AuthSyncRuntimeEvent[];
|
|
55
|
+
export declare function formatAuthSyncEventLine(event: AuthSyncRuntimeEvent): string;
|
|
56
|
+
export declare function selectCodexRateLimitSnapshot(limits: CodexAccountRateLimits | null): CodexRateLimitSnapshot | null;
|
|
57
|
+
export declare function formatCodexAccountLabel(account: CodexAccountInfo): string;
|
|
58
|
+
export declare function formatPlanTypeLabel(value: string): string;
|
|
59
|
+
export declare function formatRateLimitWindowLabel(locale: AppLocale, window: CodexRateLimitWindow, fallback: 'primary' | 'secondary'): string;
|
|
60
|
+
export declare function formatCompactRateLimitWindowLabel(locale: AppLocale, minutes: number | null, fallback: 'primary' | 'secondary'): string;
|
|
61
|
+
export declare function formatCompactAge(locale: AppLocale, ageMs: number): string;
|
|
62
|
+
export declare function formatUtcDateTime(timestampMs: number): string;
|
|
63
|
+
export declare function formatUsagePercent(value: number): string;
|
|
64
|
+
export declare function formatRemainingUsagePercent(usedPercent: number): string;
|
|
65
|
+
export declare function authQuotaSnapshotFromRateLimit(snapshot: CodexRateLimitSnapshot, accountId?: string | null, quotaIdentityId?: string | null): CodexAuthQuotaSnapshot;
|
|
66
|
+
export declare function codexAuthQuotaSnapshotFromRecord(record: CodexAuthQuotaSnapshotRecord): CodexAuthQuotaSnapshot;
|
|
67
|
+
export declare function mergeCodexAuthQuotaSnapshots(current: CodexAuthQuotaSnapshot | null, incoming: CodexAuthQuotaSnapshot | null): CodexAuthQuotaSnapshot | null;
|
|
68
|
+
export declare function isFiniteCodexAuthQuotaSnapshotRecord(record: CodexAuthQuotaSnapshotRecord): boolean;
|
|
69
|
+
export declare function remainingUsagePercent(usedPercent: number): number | null;
|
|
70
|
+
export declare function formatAuthQuotaPrefix(locale: AppLocale, snapshot: CodexAuthQuotaSnapshot | null): string;
|
|
71
|
+
export declare function formatAuthQuotaButtonPrefix(snapshot: CodexAuthQuotaSnapshot | null): string;
|
|
72
|
+
export declare function formatAuthQuotaButtonValue(value: number | null): string;
|
|
73
|
+
export declare function isCodexAuthQuotaSnapshot(value: unknown): value is CodexAuthQuotaSnapshot;
|
|
74
|
+
export declare function normalizeCodexAuthQuotaSnapshot(snapshot: CodexAuthQuotaSnapshot): CodexAuthQuotaSnapshot;
|
|
75
|
+
export declare function chatGptAuthMetadataCompatible(left: Pick<ChatGptAuthMetadata, 'accountId' | 'quotaIdentityId'>, right: Pick<ChatGptAuthMetadata, 'accountId' | 'quotaIdentityId'>): boolean;
|
|
76
|
+
export declare function isNullableFiniteNumber(value: unknown): value is number | null;
|
|
77
|
+
export declare function isNullableString(value: unknown): value is string | null;
|