@foxden-app/foxclaw 0.4.12 → 0.4.14
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 +12 -2
- package/CHANGELOG.md +24 -0
- package/README.md +5 -3
- package/README_EN.md +5 -3
- package/dist/auth/cross_node_sync.d.ts +112 -0
- package/dist/auth/cross_node_sync.js +682 -0
- package/dist/auth/mirror.d.ts +37 -1
- package/dist/auth/mirror.js +136 -3
- package/dist/config.d.ts +10 -0
- package/dist/config.js +14 -0
- package/dist/controller/controller.d.ts +21 -0
- package/dist/controller/controller.js +200 -9
- package/dist/i18n.d.ts +42 -2
- package/dist/i18n.js +42 -2
- package/dist/main.js +192 -8
- package/dist/telegram/api.d.ts +6 -0
- package/dist/telegram/api.js +50 -0
- package/dist/telegram/gateway.d.ts +9 -0
- package/dist/telegram/gateway.js +33 -2
- package/dist/types.d.ts +16 -0
- package/dist/update.d.ts +2 -0
- package/dist/update.js +44 -6
- package/docs/user-manual.md +40 -4
- package/docs/zh/user-manual.md +40 -4
- package/package.json +1 -1
- package/skills/foxclaw/SKILL.md +4 -2
package/dist/auth/mirror.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export interface AuthMirrorStatus {
|
|
|
12
12
|
sourceLabel: string;
|
|
13
13
|
syncedAt: string;
|
|
14
14
|
}
|
|
15
|
+
export interface AuthMirrorExternalStatus extends AuthMirrorStatus {
|
|
16
|
+
remoteNodeId?: string | null;
|
|
17
|
+
}
|
|
15
18
|
export interface AuthMirrorRecovery {
|
|
16
19
|
candidateName: string;
|
|
17
20
|
sourceRuntimeId: string;
|
|
@@ -31,26 +34,55 @@ export interface ChatGptAuthMetadata {
|
|
|
31
34
|
accountId: string;
|
|
32
35
|
lastRefreshMs: number;
|
|
33
36
|
}
|
|
37
|
+
export interface ChatGptAuthRecord extends ChatGptAuthMetadata {
|
|
38
|
+
raw: string;
|
|
39
|
+
}
|
|
40
|
+
export interface AuthMirrorCandidateRecord extends ChatGptAuthRecord {
|
|
41
|
+
candidateName: string;
|
|
42
|
+
sourceRuntimeId: string;
|
|
43
|
+
sourceLabel: string;
|
|
44
|
+
}
|
|
45
|
+
export interface AuthMirrorImportResult {
|
|
46
|
+
ok: boolean;
|
|
47
|
+
imported: boolean;
|
|
48
|
+
reason?: string | null;
|
|
49
|
+
record?: AuthMirrorCandidateRecord;
|
|
50
|
+
}
|
|
51
|
+
export interface AuthMirrorSyncedEvent {
|
|
52
|
+
status: AuthMirrorStatus;
|
|
53
|
+
record: AuthMirrorCandidateRecord;
|
|
54
|
+
}
|
|
55
|
+
export interface AuthMirrorHooks {
|
|
56
|
+
onSynced?: (event: AuthMirrorSyncedEvent) => Promise<void> | void;
|
|
57
|
+
}
|
|
34
58
|
export declare class AuthCandidateMirror {
|
|
35
59
|
private readonly canonicalDir;
|
|
36
60
|
private readonly runtimes;
|
|
37
61
|
private readonly logger;
|
|
38
62
|
private readonly statusPath;
|
|
63
|
+
private readonly hooks;
|
|
39
64
|
private timer;
|
|
40
65
|
private readonly lastSyncedRefresh;
|
|
41
66
|
private readonly lastValidationFailures;
|
|
42
67
|
private readonly activeCandidateSyncs;
|
|
43
68
|
private activeOperations;
|
|
44
69
|
private lastStatus;
|
|
45
|
-
constructor(canonicalDir: string, runtimes: AuthMirrorRuntime[], logger: Logger, statusPath?: string | null);
|
|
70
|
+
constructor(canonicalDir: string, runtimes: AuthMirrorRuntime[], logger: Logger, statusPath?: string | null, hooks?: AuthMirrorHooks);
|
|
46
71
|
initialize(): Promise<void>;
|
|
47
72
|
start(): void;
|
|
48
73
|
stop(): void;
|
|
49
74
|
isIdle(): boolean;
|
|
50
75
|
getStatus(): AuthMirrorStatus | null;
|
|
76
|
+
readNewestCandidate(candidateName: string): Promise<AuthMirrorCandidateRecord | null>;
|
|
77
|
+
readRuntimeCandidate(runtimeId: string, candidateName: string): Promise<AuthMirrorCandidateRecord | null>;
|
|
78
|
+
listNewestCandidates(): Promise<AuthMirrorCandidateRecord[]>;
|
|
51
79
|
syncRuntimeCandidate(runtimeId: string, candidateName: string): Promise<boolean>;
|
|
52
80
|
recoverRuntimeCandidate(runtimeId: string, candidateName: string): Promise<AuthMirrorRecovery | null>;
|
|
53
81
|
private scan;
|
|
82
|
+
importExternalCandidate(candidateName: string, raw: string, source: {
|
|
83
|
+
nodeId: string;
|
|
84
|
+
label?: string | null;
|
|
85
|
+
}): Promise<AuthMirrorImportResult>;
|
|
54
86
|
private propagateValidatedCandidate;
|
|
55
87
|
private validateRuntimeCandidate;
|
|
56
88
|
private withActivity;
|
|
@@ -59,6 +91,10 @@ export declare class AuthCandidateMirror {
|
|
|
59
91
|
private collectAuthRecords;
|
|
60
92
|
private reconcileCandidateAtStartup;
|
|
61
93
|
private resolveCanonicalCurrentCandidate;
|
|
94
|
+
private findNewestRecord;
|
|
95
|
+
private runtimeLabel;
|
|
62
96
|
}
|
|
63
97
|
export declare function isAuthCandidateName(name: string): boolean;
|
|
98
|
+
export declare function readChatGptAuthRecord(filePath: string): Promise<ChatGptAuthRecord | null>;
|
|
64
99
|
export declare function readChatGptAuthMetadata(filePath: string): Promise<ChatGptAuthMetadata | null>;
|
|
100
|
+
export declare function parseChatGptAuthMetadata(raw: string): ChatGptAuthMetadata | null;
|
package/dist/auth/mirror.js
CHANGED
|
@@ -6,17 +6,19 @@ export class AuthCandidateMirror {
|
|
|
6
6
|
runtimes;
|
|
7
7
|
logger;
|
|
8
8
|
statusPath;
|
|
9
|
+
hooks;
|
|
9
10
|
timer = null;
|
|
10
11
|
lastSyncedRefresh = new Map();
|
|
11
12
|
lastValidationFailures = new Map();
|
|
12
13
|
activeCandidateSyncs = new Set();
|
|
13
14
|
activeOperations = 0;
|
|
14
15
|
lastStatus = null;
|
|
15
|
-
constructor(canonicalDir, runtimes, logger, statusPath = null) {
|
|
16
|
+
constructor(canonicalDir, runtimes, logger, statusPath = null, hooks = {}) {
|
|
16
17
|
this.canonicalDir = canonicalDir;
|
|
17
18
|
this.runtimes = runtimes;
|
|
18
19
|
this.logger = logger;
|
|
19
20
|
this.statusPath = statusPath;
|
|
21
|
+
this.hooks = hooks;
|
|
20
22
|
}
|
|
21
23
|
async initialize() {
|
|
22
24
|
this.lastStatus = await readMirrorStatus(this.statusPath);
|
|
@@ -65,6 +67,51 @@ export class AuthCandidateMirror {
|
|
|
65
67
|
getStatus() {
|
|
66
68
|
return this.lastStatus;
|
|
67
69
|
}
|
|
70
|
+
async readNewestCandidate(candidateName) {
|
|
71
|
+
if (!isAuthCandidateName(candidateName))
|
|
72
|
+
return null;
|
|
73
|
+
return this.withActivity(async () => this.findNewestRecord((entry) => entry.candidateName === candidateName));
|
|
74
|
+
}
|
|
75
|
+
async readRuntimeCandidate(runtimeId, candidateName) {
|
|
76
|
+
if (!isAuthCandidateName(candidateName))
|
|
77
|
+
return null;
|
|
78
|
+
return this.withActivity(async () => {
|
|
79
|
+
const runtime = this.runtimes.find((entry) => entry.id === runtimeId);
|
|
80
|
+
const authDir = runtimeId === 'canonical' ? this.canonicalDir : runtime?.authDir;
|
|
81
|
+
if (!authDir)
|
|
82
|
+
return null;
|
|
83
|
+
const record = await readChatGptAuthRecord(path.join(authDir, candidateName));
|
|
84
|
+
if (!record)
|
|
85
|
+
return null;
|
|
86
|
+
return {
|
|
87
|
+
...record,
|
|
88
|
+
candidateName,
|
|
89
|
+
sourceRuntimeId: runtimeId,
|
|
90
|
+
sourceLabel: runtime?.label ?? runtimeId,
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async listNewestCandidates() {
|
|
95
|
+
return this.withActivity(async () => {
|
|
96
|
+
const byName = new Map();
|
|
97
|
+
for (const entry of await this.collectAuthRecords()) {
|
|
98
|
+
if (!isAuthCandidateName(entry.candidateName))
|
|
99
|
+
continue;
|
|
100
|
+
const sourceLabel = this.runtimeLabel(entry.runtimeId);
|
|
101
|
+
const candidate = {
|
|
102
|
+
...entry.record,
|
|
103
|
+
candidateName: entry.candidateName,
|
|
104
|
+
sourceRuntimeId: entry.runtimeId,
|
|
105
|
+
sourceLabel,
|
|
106
|
+
};
|
|
107
|
+
const current = byName.get(entry.candidateName);
|
|
108
|
+
if (!current || candidate.lastRefreshMs > current.lastRefreshMs) {
|
|
109
|
+
byName.set(entry.candidateName, candidate);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [...byName.values()].sort((a, b) => a.candidateName.localeCompare(b.candidateName));
|
|
113
|
+
});
|
|
114
|
+
}
|
|
68
115
|
async syncRuntimeCandidate(runtimeId, candidateName) {
|
|
69
116
|
if (!isAuthCandidateName(candidateName))
|
|
70
117
|
return false;
|
|
@@ -116,6 +163,64 @@ export class AuthCandidateMirror {
|
|
|
116
163
|
}
|
|
117
164
|
});
|
|
118
165
|
}
|
|
166
|
+
async importExternalCandidate(candidateName, raw, source) {
|
|
167
|
+
if (!isAuthCandidateName(candidateName)) {
|
|
168
|
+
return { ok: false, imported: false, reason: 'invalid candidate name' };
|
|
169
|
+
}
|
|
170
|
+
return this.withActivity(async () => {
|
|
171
|
+
if (this.activeCandidateSyncs.has(candidateName)) {
|
|
172
|
+
return { ok: false, imported: false, reason: 'candidate sync already active' };
|
|
173
|
+
}
|
|
174
|
+
this.activeCandidateSyncs.add(candidateName);
|
|
175
|
+
try {
|
|
176
|
+
const metadata = parseChatGptAuthMetadata(raw);
|
|
177
|
+
if (!metadata) {
|
|
178
|
+
return { ok: false, imported: false, reason: 'invalid ChatGPT auth payload' };
|
|
179
|
+
}
|
|
180
|
+
const existing = (await this.collectAuthRecords())
|
|
181
|
+
.filter(entry => entry.candidateName === candidateName);
|
|
182
|
+
const conflicting = existing.find(entry => entry.record.accountId !== metadata.accountId);
|
|
183
|
+
if (conflicting) {
|
|
184
|
+
return { ok: false, imported: false, reason: 'same candidate belongs to a different account' };
|
|
185
|
+
}
|
|
186
|
+
const newest = existing.reduce((current, entry) => (!current || entry.record.lastRefreshMs > current.record.lastRefreshMs ? entry : current), null);
|
|
187
|
+
const previousRefresh = Math.max(newest?.record.lastRefreshMs ?? 0, this.lastSyncedRefresh.get(candidateName) ?? 0);
|
|
188
|
+
if (metadata.lastRefreshMs <= previousRefresh) {
|
|
189
|
+
return { ok: true, imported: false, reason: 'local candidate is already newer or equal' };
|
|
190
|
+
}
|
|
191
|
+
await atomicWrite(path.join(this.canonicalDir, candidateName), raw);
|
|
192
|
+
for (const target of this.runtimes) {
|
|
193
|
+
await atomicWrite(path.join(target.authDir, candidateName), raw);
|
|
194
|
+
}
|
|
195
|
+
this.lastSyncedRefresh.set(candidateName, metadata.lastRefreshMs);
|
|
196
|
+
const sourceLabel = source.label?.trim() || source.nodeId;
|
|
197
|
+
this.lastStatus = {
|
|
198
|
+
candidateName,
|
|
199
|
+
sourceRuntimeId: `remote:${source.nodeId}`,
|
|
200
|
+
sourceLabel,
|
|
201
|
+
syncedAt: new Date().toISOString(),
|
|
202
|
+
};
|
|
203
|
+
await writeMirrorStatus(this.statusPath, this.lastStatus);
|
|
204
|
+
this.logger.info('auth.mirror.remote_imported', { candidateName, sourceNodeId: source.nodeId });
|
|
205
|
+
const message = `${candidateName} has been synchronized from remote node ${sourceLabel}.`;
|
|
206
|
+
await Promise.allSettled(this.runtimes.map((target) => target.notify?.(message)));
|
|
207
|
+
return {
|
|
208
|
+
ok: true,
|
|
209
|
+
imported: true,
|
|
210
|
+
record: {
|
|
211
|
+
raw,
|
|
212
|
+
...metadata,
|
|
213
|
+
candidateName,
|
|
214
|
+
sourceRuntimeId: `remote:${source.nodeId}`,
|
|
215
|
+
sourceLabel,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
this.activeCandidateSyncs.delete(candidateName);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
119
224
|
async propagateValidatedCandidate(runtime, name) {
|
|
120
225
|
if (this.activeCandidateSyncs.has(name)) {
|
|
121
226
|
return false;
|
|
@@ -170,6 +275,15 @@ export class AuthCandidateMirror {
|
|
|
170
275
|
this.logger.info('auth.mirror.synced', { sourceRuntimeId: runtime.id, name });
|
|
171
276
|
const message = `${name} has been refreshed by ${sourceLabel} and synchronized to the other Codex homes.`;
|
|
172
277
|
await Promise.allSettled(this.runtimes.map((target) => target.notify?.(message)));
|
|
278
|
+
await this.hooks.onSynced?.({
|
|
279
|
+
status: this.lastStatus,
|
|
280
|
+
record: {
|
|
281
|
+
...record,
|
|
282
|
+
candidateName: name,
|
|
283
|
+
sourceRuntimeId: runtime.id,
|
|
284
|
+
sourceLabel,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
173
287
|
return true;
|
|
174
288
|
}
|
|
175
289
|
finally {
|
|
@@ -269,6 +383,25 @@ export class AuthCandidateMirror {
|
|
|
269
383
|
const name = path.basename(finalPath);
|
|
270
384
|
return isAuthCandidateName(name) ? name : null;
|
|
271
385
|
}
|
|
386
|
+
async findNewestRecord(predicate) {
|
|
387
|
+
const newest = (await this.collectAuthRecords())
|
|
388
|
+
.filter(predicate)
|
|
389
|
+
.reduce((current, entry) => (!current || entry.record.lastRefreshMs > current.record.lastRefreshMs ? entry : current), null);
|
|
390
|
+
if (!newest)
|
|
391
|
+
return null;
|
|
392
|
+
return {
|
|
393
|
+
...newest.record,
|
|
394
|
+
candidateName: newest.candidateName,
|
|
395
|
+
sourceRuntimeId: newest.runtimeId,
|
|
396
|
+
sourceLabel: this.runtimeLabel(newest.runtimeId),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
runtimeLabel(runtimeId) {
|
|
400
|
+
if (runtimeId === 'canonical')
|
|
401
|
+
return 'canonical';
|
|
402
|
+
const runtime = this.runtimes.find((entry) => entry.id === runtimeId);
|
|
403
|
+
return runtime?.label ?? runtimeId;
|
|
404
|
+
}
|
|
272
405
|
}
|
|
273
406
|
export function isAuthCandidateName(name) {
|
|
274
407
|
return name !== 'auth.json'
|
|
@@ -281,7 +414,7 @@ async function listAuthCandidateNames(dir) {
|
|
|
281
414
|
.filter((entry) => (entry.isFile() || entry.isSymbolicLink()) && isAuthCandidateName(entry.name))
|
|
282
415
|
.map((entry) => entry.name);
|
|
283
416
|
}
|
|
284
|
-
async function readChatGptAuthRecord(filePath) {
|
|
417
|
+
export async function readChatGptAuthRecord(filePath) {
|
|
285
418
|
try {
|
|
286
419
|
const raw = await fs.readFile(filePath, 'utf8');
|
|
287
420
|
const metadata = parseChatGptAuthMetadata(raw);
|
|
@@ -301,7 +434,7 @@ export async function readChatGptAuthMetadata(filePath) {
|
|
|
301
434
|
return null;
|
|
302
435
|
}
|
|
303
436
|
}
|
|
304
|
-
function parseChatGptAuthMetadata(raw) {
|
|
437
|
+
export function parseChatGptAuthMetadata(raw) {
|
|
305
438
|
try {
|
|
306
439
|
const parsed = JSON.parse(raw);
|
|
307
440
|
const accountId = typeof parsed.tokens?.account_id === 'string' ? parsed.tokens.account_id : '';
|
package/dist/config.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare const DEFAULT_LOCK_PATH: string;
|
|
|
8
8
|
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_CODEX_TELEGRAM_HOME: string;
|
|
11
|
+
export declare const DEFAULT_AUTH_SYNC_STATE_PATH: string;
|
|
12
|
+
export declare const DEFAULT_AUTH_SYNC_TEMP_DIR: string;
|
|
11
13
|
export declare const DEFAULT_ENV_PATH: string;
|
|
12
14
|
export declare function resolveEnvPath(): string;
|
|
13
15
|
export declare function getLoadedEnvPath(): string | null;
|
|
@@ -51,6 +53,14 @@ export interface AppConfig {
|
|
|
51
53
|
weixinMediaDir: string;
|
|
52
54
|
/** Optional `SKRouteTag` header for some IDC deployments. */
|
|
53
55
|
wxIlinkRouteTag: string | null;
|
|
56
|
+
authSyncEnabled: boolean;
|
|
57
|
+
authSyncTransport: 'telegram-private';
|
|
58
|
+
authSyncKey: string | null;
|
|
59
|
+
authSyncPeers: string[];
|
|
60
|
+
authSyncNodeId: string | null;
|
|
61
|
+
authSyncClusterId: string;
|
|
62
|
+
authSyncStatePath: string;
|
|
63
|
+
authSyncTempDir: string;
|
|
54
64
|
}
|
|
55
65
|
export declare function loadConfig(): AppConfig;
|
|
56
66
|
export declare function selectDefaultRuntimeBotToken(configuredTokens: string[], legacyToken: string | null): string | null;
|
package/dist/config.js
CHANGED
|
@@ -11,6 +11,8 @@ export const DEFAULT_LOCK_PATH = path.join(APP_HOME, 'runtime', 'bridge.lock');
|
|
|
11
11
|
export const DEFAULT_CODEX_APP_SERVER_STATE_PATH = path.join(APP_HOME, 'runtime', 'codex-app-server.json');
|
|
12
12
|
export const DEFAULT_CODEX_APP_SERVER_LOG_PATH = path.join(APP_HOME, 'logs', 'codex-app-server.log');
|
|
13
13
|
export const DEFAULT_CODEX_TELEGRAM_HOME = path.join(APP_HOME, 'codex', 'telegram');
|
|
14
|
+
export const DEFAULT_AUTH_SYNC_STATE_PATH = path.join(APP_HOME, 'runtime', 'auth-sync.json');
|
|
15
|
+
export const DEFAULT_AUTH_SYNC_TEMP_DIR = path.join(APP_HOME, 'runtime', 'auth-sync');
|
|
14
16
|
export const DEFAULT_ENV_PATH = path.join(APP_HOME, '.env');
|
|
15
17
|
let envLoaded = false;
|
|
16
18
|
let loadedEnvPath = null;
|
|
@@ -85,6 +87,14 @@ export function loadConfig() {
|
|
|
85
87
|
weixinSyncBufDir: process.env.WEIXIN_SYNC_BUF_DIR || path.join(APP_HOME, 'weixin', 'sync-buf'),
|
|
86
88
|
weixinMediaDir: process.env.WEIXIN_MEDIA_DIR || path.join(APP_HOME, 'weixin', 'media'),
|
|
87
89
|
wxIlinkRouteTag: optional('WX_ILINK_ROUTE_TAG'),
|
|
90
|
+
authSyncEnabled: boolEnv('AUTH_SYNC_ENABLED', false),
|
|
91
|
+
authSyncTransport: 'telegram-private',
|
|
92
|
+
authSyncKey: optional('AUTH_SYNC_KEY'),
|
|
93
|
+
authSyncPeers: parseCommaSeparatedIds(process.env.AUTH_SYNC_PEERS),
|
|
94
|
+
authSyncNodeId: optional('AUTH_SYNC_NODE_ID'),
|
|
95
|
+
authSyncClusterId: process.env.AUTH_SYNC_CLUSTER_ID?.trim() || 'default',
|
|
96
|
+
authSyncStatePath: process.env.AUTH_SYNC_STATE_PATH || DEFAULT_AUTH_SYNC_STATE_PATH,
|
|
97
|
+
authSyncTempDir: process.env.AUTH_SYNC_TEMP_DIR || DEFAULT_AUTH_SYNC_TEMP_DIR,
|
|
88
98
|
};
|
|
89
99
|
ensureAppDirs(config);
|
|
90
100
|
return config;
|
|
@@ -102,7 +112,11 @@ export function ensureAppDirs(config) {
|
|
|
102
112
|
path.dirname(config.lockPath),
|
|
103
113
|
path.dirname(config.codexAppServerStatePath),
|
|
104
114
|
path.dirname(config.codexAppServerLogPath),
|
|
115
|
+
path.dirname(config.authSyncStatePath),
|
|
105
116
|
];
|
|
117
|
+
if (config.authSyncEnabled) {
|
|
118
|
+
dirs.push(config.authSyncTempDir);
|
|
119
|
+
}
|
|
106
120
|
if (config.wxEnabled) {
|
|
107
121
|
dirs.push(config.weixinAccountsDir, config.weixinSyncBufDir, config.weixinMediaDir);
|
|
108
122
|
}
|
|
@@ -10,11 +10,26 @@ export interface CoreCoordinator {
|
|
|
10
10
|
canSelfUpdate?: () => boolean;
|
|
11
11
|
authCandidateUpdated?: (runtimeId: string, candidateName: string) => Promise<void>;
|
|
12
12
|
recoverAuthCandidate?: (runtimeId: string, candidateName: string) => Promise<boolean>;
|
|
13
|
+
acquireAuthRefreshLease?: (reason: string) => Promise<{
|
|
14
|
+
ok: boolean;
|
|
15
|
+
leaseId: string | null;
|
|
16
|
+
reason?: string | null;
|
|
17
|
+
}>;
|
|
18
|
+
releaseAuthRefreshLease?: (leaseId: string | null) => Promise<void>;
|
|
19
|
+
getAuthSyncStatus?: () => RuntimeStatus['authSync'];
|
|
20
|
+
authSyncPushAll?: () => Promise<{
|
|
21
|
+
sent: number;
|
|
22
|
+
skipped: number;
|
|
23
|
+
}>;
|
|
24
|
+
authSyncTest?: () => Promise<{
|
|
25
|
+
sent: number;
|
|
26
|
+
}>;
|
|
13
27
|
statusUpdated?: (status: RuntimeStatus) => void;
|
|
14
28
|
getServiceStatus?: () => Promise<{
|
|
15
29
|
bots: NonNullable<RuntimeStatus['bots']>;
|
|
16
30
|
weixinRuntime?: RuntimeStatus['weixinRuntime'];
|
|
17
31
|
authMirror?: RuntimeStatus['authMirror'];
|
|
32
|
+
authSync?: RuntimeStatus['authSync'];
|
|
18
33
|
lastUpdate?: SelfUpdateStatus | null;
|
|
19
34
|
}>;
|
|
20
35
|
selfUpdateCompleted?: (status: SelfUpdateStatus) => void;
|
|
@@ -157,6 +172,10 @@ export declare class BridgeSessionCore {
|
|
|
157
172
|
private registerActiveTurn;
|
|
158
173
|
private createActiveTurnState;
|
|
159
174
|
getCurrentAuthLabel(): Promise<string | null>;
|
|
175
|
+
validateExternalCodexAuthCandidate(candidateName: string, rawAuth: string, expectedAccountId: string): Promise<{
|
|
176
|
+
ok: boolean;
|
|
177
|
+
reason?: string | null;
|
|
178
|
+
}>;
|
|
160
179
|
isIdleForServiceUpdate(): boolean;
|
|
161
180
|
private hasLocalBlockingActivity;
|
|
162
181
|
private authRuntimeId;
|
|
@@ -238,7 +257,9 @@ export declare class BridgeSessionCore {
|
|
|
238
257
|
private clearSelfUpdateStatusPoll;
|
|
239
258
|
private pollSelfUpdateStatus;
|
|
240
259
|
private formatSelfUpdateResult;
|
|
260
|
+
private formatCodexUpdateResult;
|
|
241
261
|
private handleAuthCommand;
|
|
262
|
+
private handleAuthSyncCommand;
|
|
242
263
|
private handleAuthRefreshAllCommand;
|
|
243
264
|
private handleAuthUseCommand;
|
|
244
265
|
private handleAuthToggleCommand;
|