feishu-codex-connector 0.1.6

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.
@@ -0,0 +1,994 @@
1
+ import { registerApp } from '@larksuiteoapi/node-sdk';
2
+ import { ThreadEvent } from '@openai/codex-sdk';
3
+
4
+ type Tenant = 'feishu' | 'lark';
5
+ type BridgeAccessMode = 'read-only' | 'workspace' | 'full';
6
+ type CodexSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
7
+ type CodexBackend = 'sdk' | 'exec-json' | 'app-server';
8
+ type CodexAuthMode = 'api-key' | 'codex-home' | 'inherit-user';
9
+ type ReplyMode = 'card' | 'markdown' | 'text';
10
+ type LarkCliIdentityPreset = 'bot-only' | 'user-default' | 'disabled';
11
+ type SecretRef = {
12
+ source: 'keystore';
13
+ id: string;
14
+ } | {
15
+ source: 'env';
16
+ name: string;
17
+ } | {
18
+ source: 'file';
19
+ path: string;
20
+ } | {
21
+ source: 'exec';
22
+ command: string;
23
+ args?: string[];
24
+ };
25
+ interface ProfileAccess {
26
+ allowedUsers: string[];
27
+ allowedChats: string[];
28
+ admins: string[];
29
+ requireMentionInGroup: boolean;
30
+ allowUserCliInGroups: boolean;
31
+ }
32
+ interface PermissionConfig {
33
+ defaultAccess: BridgeAccessMode;
34
+ maxAccess: BridgeAccessMode;
35
+ networkAccess: boolean;
36
+ }
37
+ interface AppConfig {
38
+ id: string;
39
+ secret: SecretRef;
40
+ }
41
+ interface CodexAuthConfig {
42
+ mode: CodexAuthMode;
43
+ apiKey?: SecretRef;
44
+ }
45
+ interface CodexConfig {
46
+ backend: CodexBackend;
47
+ auth: CodexAuthConfig;
48
+ codexHome: string;
49
+ inheritUserCodexHome: boolean;
50
+ codexPath?: string;
51
+ baseUrl?: string;
52
+ config?: Record<string, unknown>;
53
+ }
54
+ interface FeishuCliConfig {
55
+ enabled: boolean;
56
+ identityPreset: LarkCliIdentityPreset;
57
+ configDir: string;
58
+ }
59
+ interface AttachmentConfig {
60
+ maxCount: number;
61
+ maxBytes: number;
62
+ maxFileBytes: number;
63
+ imageMaxBytes: number;
64
+ cacheTtlMs: number;
65
+ }
66
+ interface PreferencesConfig {
67
+ replyMode: ReplyMode;
68
+ showToolCalls: boolean;
69
+ maxConcurrency: number;
70
+ debounceMs: number;
71
+ idleTimeoutMinutes?: number;
72
+ }
73
+ interface ProfileConfig {
74
+ schemaVersion: 1;
75
+ name: string;
76
+ tenant: Tenant;
77
+ app: AppConfig;
78
+ codex: CodexConfig;
79
+ feishuCli: FeishuCliConfig;
80
+ permissions: PermissionConfig;
81
+ access: ProfileAccess;
82
+ preferences: PreferencesConfig;
83
+ attachments: AttachmentConfig;
84
+ workspaces: {
85
+ default?: string;
86
+ };
87
+ }
88
+ interface CreateProfileInput {
89
+ name: string;
90
+ tenant: Tenant;
91
+ appId: string;
92
+ appSecret: SecretRef;
93
+ codexAuth: CodexAuthConfig;
94
+ workspace?: string;
95
+ home?: string;
96
+ codexBackend?: CodexBackend;
97
+ codexPath?: string;
98
+ }
99
+ declare function createDefaultProfileConfig(input: CreateProfileInput): ProfileConfig;
100
+ declare function normalizeProfileConfig(value: unknown): ProfileConfig;
101
+ declare function isSecretRef(value: unknown): value is SecretRef;
102
+ declare function redactProfileConfig(profile: ProfileConfig): ProfileConfig;
103
+
104
+ type BridgeSource = 'im' | 'card' | 'comment';
105
+ type ActorType = 'user' | 'bot';
106
+ type ChatType = 'private' | 'group' | 'topic';
107
+ interface BridgeMention {
108
+ id: string;
109
+ name?: string;
110
+ isBot?: boolean;
111
+ isAll?: boolean;
112
+ }
113
+ interface BridgeResource {
114
+ kind: 'image' | 'file' | 'audio' | 'video' | 'sticker';
115
+ fileKey?: string;
116
+ messageId?: string;
117
+ name?: string;
118
+ mime?: string;
119
+ size?: number;
120
+ required?: boolean;
121
+ }
122
+ interface BridgeResourceData {
123
+ bytes: Buffer;
124
+ name?: string;
125
+ mime?: string;
126
+ }
127
+ interface BridgeCommentTarget {
128
+ fileToken: string;
129
+ fileType: string;
130
+ commentId: string;
131
+ replyId?: string;
132
+ isWhole?: boolean;
133
+ }
134
+ interface BridgeMessage {
135
+ source: BridgeSource;
136
+ scopeId: string;
137
+ chatId?: string;
138
+ threadId?: string;
139
+ commentScopeId?: string;
140
+ commentTarget?: BridgeCommentTarget;
141
+ messageId: string;
142
+ actorId: string;
143
+ actorName?: string;
144
+ actorType?: ActorType;
145
+ chatType: ChatType;
146
+ text: string;
147
+ mentions: BridgeMention[];
148
+ resources: BridgeResource[];
149
+ raw: unknown;
150
+ }
151
+ interface BridgeAction {
152
+ source: 'card';
153
+ token: string;
154
+ value?: Record<string, unknown>;
155
+ scopeId: string;
156
+ chatId: string;
157
+ messageId: string;
158
+ operatorOpenId: string;
159
+ raw: unknown;
160
+ formValues?: Record<string, unknown>;
161
+ }
162
+ interface BridgeReply {
163
+ mode: 'card' | 'markdown' | 'text' | 'comment';
164
+ content: unknown;
165
+ }
166
+ interface ChannelGateway {
167
+ onMessage?(handler: (message: BridgeMessage) => Promise<void> | void): void;
168
+ onAction?(handler: (action: BridgeAction) => Promise<void> | void): void;
169
+ connect(): Promise<void>;
170
+ disconnect(): Promise<void>;
171
+ sendMessage(target: {
172
+ chatId?: string;
173
+ messageId?: string;
174
+ commentScopeId?: string;
175
+ commentTarget?: BridgeCommentTarget;
176
+ }, reply: BridgeReply): Promise<{
177
+ messageId?: string;
178
+ } | void>;
179
+ updateMessage?(messageId: string, reply: BridgeReply): Promise<void>;
180
+ downloadResource?(message: BridgeMessage, resource: BridgeResource): Promise<BridgeResourceData>;
181
+ createGroupChat?(input: {
182
+ name: string;
183
+ inviteOpenId: string;
184
+ description?: string;
185
+ }): Promise<{
186
+ chatId: string;
187
+ name: string;
188
+ }>;
189
+ listJoinedChats?(): Promise<Array<{
190
+ chatId: string;
191
+ name?: string;
192
+ }>>;
193
+ }
194
+
195
+ type ActorRole = 'owner' | 'admin' | 'allowed-user' | 'group-member' | 'comment-mention' | 'denied';
196
+ interface AccessDecision {
197
+ ok: boolean;
198
+ role: ActorRole;
199
+ isAdmin: boolean;
200
+ isOwner: boolean;
201
+ reason?: 'unauthorized-user' | 'unauthorized-chat' | 'mention-required' | 'bot-actor';
202
+ }
203
+ interface AccessContext {
204
+ profile: ProfileConfig;
205
+ message: Pick<BridgeMessage, 'source' | 'actorId' | 'actorType' | 'chatId' | 'chatType' | 'mentions'>;
206
+ ownerOpenId?: string;
207
+ botOpenId?: string;
208
+ }
209
+ declare function evaluateAccess(context: AccessContext): AccessDecision;
210
+ declare function canRunAdminCommand(decision: AccessDecision): boolean;
211
+
212
+ interface ApprovalRequest {
213
+ id: string;
214
+ runId: string;
215
+ scopeId: string;
216
+ cwd: string;
217
+ sandbox: string;
218
+ action: string;
219
+ command?: string;
220
+ riskSummary?: string;
221
+ }
222
+ type CodexBridgeEvent = {
223
+ type: 'thread';
224
+ threadId: string;
225
+ } | {
226
+ type: 'text';
227
+ delta: string;
228
+ } | {
229
+ type: 'tool_start';
230
+ id: string;
231
+ name: string;
232
+ input: unknown;
233
+ } | {
234
+ type: 'tool_end';
235
+ id: string;
236
+ output: string;
237
+ isError: boolean;
238
+ } | {
239
+ type: 'usage';
240
+ inputTokens?: number;
241
+ outputTokens?: number;
242
+ cachedInputTokens?: number;
243
+ reasoningOutputTokens?: number;
244
+ } | {
245
+ type: 'approval_request';
246
+ request: ApprovalRequest;
247
+ } | {
248
+ type: 'done';
249
+ reason: 'normal' | 'interrupted' | 'timeout';
250
+ } | {
251
+ type: 'error';
252
+ message: string;
253
+ };
254
+ interface CodexRunInput {
255
+ runId: string;
256
+ prompt: string;
257
+ cwdRealpath: string;
258
+ threadId?: string;
259
+ sandbox: CodexSandboxMode;
260
+ networkAccess: boolean;
261
+ imagePaths?: string[];
262
+ env?: Record<string, string>;
263
+ apiKey?: string;
264
+ baseUrl?: string;
265
+ codexHome?: string;
266
+ codexPath?: string;
267
+ config?: Record<string, unknown>;
268
+ idleTimeoutMs?: number;
269
+ }
270
+ interface CodexThreadListInput {
271
+ cwdRealpath?: string;
272
+ }
273
+ interface CodexThreadSummary {
274
+ threadId: string;
275
+ updatedAt?: number;
276
+ summary?: string;
277
+ }
278
+ interface RunnerAvailability {
279
+ ok: boolean;
280
+ label: string;
281
+ detail?: string;
282
+ }
283
+ interface CodexRunHandle {
284
+ runId: string;
285
+ threadId?: string;
286
+ events: AsyncIterable<CodexBridgeEvent>;
287
+ stop(): Promise<void>;
288
+ }
289
+ interface CodexRunner {
290
+ start(input: CodexRunInput): Promise<CodexRunHandle>;
291
+ resume(threadId: string, input: CodexRunInput): Promise<CodexRunHandle>;
292
+ listThreads(input: CodexThreadListInput): Promise<CodexThreadSummary[]>;
293
+ stop(runId: string): Promise<void>;
294
+ checkAvailability(): Promise<RunnerAvailability>;
295
+ }
296
+
297
+ type NonceState = 'fresh' | 'used' | 'revoked';
298
+ interface CallbackNonceStore {
299
+ state(nonce: string): NonceState;
300
+ consume(nonce: string): boolean;
301
+ revoke(nonce: string): void;
302
+ }
303
+ declare class MemoryCallbackNonceStore implements CallbackNonceStore {
304
+ private readonly used;
305
+ private readonly revoked;
306
+ state(nonce: string): NonceState;
307
+ consume(nonce: string): boolean;
308
+ revoke(nonce: string): void;
309
+ }
310
+
311
+ interface CallbackKey {
312
+ version: number;
313
+ secret: string;
314
+ retired?: boolean;
315
+ }
316
+ interface CallbackSignInput {
317
+ runId: string;
318
+ scopeId: string;
319
+ chatId: string;
320
+ operatorOpenId: string;
321
+ action: string;
322
+ policyFingerprint: string;
323
+ ttlMs: number;
324
+ }
325
+ interface CallbackVerifyExpected {
326
+ runId: string;
327
+ scopeId: string;
328
+ chatId: string;
329
+ operatorOpenId: string;
330
+ action: string;
331
+ policyFingerprint: string;
332
+ }
333
+ interface CallbackPayload {
334
+ r: string;
335
+ s: string;
336
+ c: string;
337
+ o: string;
338
+ a: string;
339
+ exp: number;
340
+ fp: string;
341
+ n: string;
342
+ kv: number;
343
+ }
344
+ type CallbackVerifyResult = {
345
+ ok: true;
346
+ payload: CallbackPayload;
347
+ } | {
348
+ ok: false;
349
+ reason: 'malformed' | 'unknown-key' | 'bad-signature' | 'expired' | 'context-mismatch' | 'nonce-replay' | 'nonce-revoked';
350
+ };
351
+ declare class CallbackAuth {
352
+ private readonly nonceStore;
353
+ private readonly keys;
354
+ private readonly now;
355
+ private readonly createNonce;
356
+ constructor(keys: CallbackKey[], nonceStore: CallbackNonceStore, opts?: {
357
+ now?: () => number;
358
+ createNonce?: () => string;
359
+ });
360
+ sign(input: CallbackSignInput): string;
361
+ verify(token: string, expected: CallbackVerifyExpected): CallbackVerifyResult;
362
+ private signingKey;
363
+ }
364
+
365
+ interface SessionCatalogEntry {
366
+ key: string;
367
+ scopeId: string;
368
+ cwdRealpath: string;
369
+ policyFingerprint: string;
370
+ codexThreadId: string;
371
+ status: 'active' | 'archived';
372
+ updatedAt: number;
373
+ summary?: string;
374
+ }
375
+ declare class SessionCatalog {
376
+ private readonly path;
377
+ private readonly now;
378
+ constructor(path: string, now?: () => number);
379
+ activeFor(input: Pick<SessionCatalogEntry, 'scopeId' | 'cwdRealpath' | 'policyFingerprint'>): Promise<SessionCatalogEntry | undefined>;
380
+ upsertActive(input: Omit<SessionCatalogEntry, 'key' | 'status' | 'updatedAt'>): Promise<SessionCatalogEntry>;
381
+ archiveScope(scopeId: string): Promise<void>;
382
+ recentCompatible(input: Pick<SessionCatalogEntry, 'scopeId' | 'cwdRealpath' | 'policyFingerprint'>, limit?: number): Promise<SessionCatalogEntry[]>;
383
+ recentForScopeWorkspace(scopeId: string, cwdRealpath: string, limit?: number): Promise<SessionCatalogEntry[]>;
384
+ byKey(key: string): Promise<SessionCatalogEntry | undefined>;
385
+ restoreActive(key: string): Promise<SessionCatalogEntry | undefined>;
386
+ private read;
387
+ private write;
388
+ }
389
+ declare function catalogKey(scopeId: string, cwdRealpath: string, policyFingerprint: string): string;
390
+
391
+ declare class ResumeNonceStore {
392
+ private readonly now;
393
+ private readonly entries;
394
+ constructor(now?: () => number);
395
+ issue(key: string, ttlMs?: number): string;
396
+ consume(nonce: string): string | undefined;
397
+ private gc;
398
+ }
399
+
400
+ interface SessionRecord {
401
+ threadId?: string;
402
+ cwdRealpath?: string;
403
+ idleTimeoutMinutes?: number;
404
+ updatedAt: number;
405
+ }
406
+ declare class SessionStore {
407
+ private readonly path;
408
+ private readonly now;
409
+ constructor(path: string, now?: () => number);
410
+ get(scopeId: string): Promise<SessionRecord | undefined>;
411
+ set(scopeId: string, threadId: string, cwdRealpath: string): Promise<void>;
412
+ setTimeout(scopeId: string, minutes: number | undefined): Promise<void>;
413
+ clear(scopeId: string): Promise<void>;
414
+ private read;
415
+ }
416
+
417
+ declare class WorkspaceStore {
418
+ private readonly path;
419
+ constructor(path: string);
420
+ cwdFor(scopeId: string): Promise<string | undefined>;
421
+ setCwd(scopeId: string, cwd: string): Promise<void>;
422
+ clearCwd(scopeId: string): Promise<void>;
423
+ saveAlias(name: string, cwd: string): Promise<void>;
424
+ removeAlias(name: string): Promise<void>;
425
+ alias(name: string): Promise<string | undefined>;
426
+ aliases(): Promise<Record<string, string>>;
427
+ private read;
428
+ private write;
429
+ }
430
+
431
+ interface CommandContext {
432
+ profile: ProfileConfig;
433
+ access: AccessDecision;
434
+ message: BridgeMessage;
435
+ sessions: SessionStore;
436
+ sessionCatalog: SessionCatalog;
437
+ workspaces: WorkspaceStore;
438
+ runner: CodexRunner;
439
+ stopScope: (scopeId: string) => Promise<boolean>;
440
+ queueSnapshot: (scopeId: string) => unknown;
441
+ processSnapshot?: () => Promise<unknown> | unknown;
442
+ saveProfile?: (profile: ProfileConfig) => Promise<void>;
443
+ callbackAuth?: CallbackAuth;
444
+ formValues?: Record<string, unknown>;
445
+ resumeNonces?: ResumeNonceStore;
446
+ gateway?: ChannelGateway;
447
+ lifecycle?: {
448
+ exit?: (target?: string) => Promise<void>;
449
+ reconnect?: () => Promise<void>;
450
+ };
451
+ }
452
+ type CommandResult = {
453
+ handled: false;
454
+ } | {
455
+ handled: true;
456
+ reply?: BridgeReply;
457
+ sensitive?: boolean;
458
+ };
459
+ declare function routeCommand(context: CommandContext): Promise<CommandResult>;
460
+ declare function parseCommand(text: string): {
461
+ name: string;
462
+ args: string[];
463
+ } | undefined;
464
+
465
+ interface RegistrationResult {
466
+ appId: string;
467
+ appSecret: string;
468
+ tenant: Tenant;
469
+ operatorOpenId?: string;
470
+ }
471
+ interface BootstrapRunOptions {
472
+ profile?: string;
473
+ tenant?: Tenant;
474
+ appId?: string;
475
+ appSecret?: string;
476
+ workspace?: string;
477
+ codexAuth?: CodexAuthMode;
478
+ codexApiKey?: string;
479
+ codexPath?: string;
480
+ home?: string;
481
+ }
482
+ interface RegistrationWizardOptions {
483
+ register?: typeof registerApp;
484
+ renderQr?: (url: string) => void;
485
+ qrImagePath?: string;
486
+ log?: Pick<Console, 'log'>;
487
+ }
488
+ declare function runRegistrationWizard(options?: RegistrationWizardOptions): Promise<RegistrationResult>;
489
+ declare function ensureProfileForRun(options?: BootstrapRunOptions): Promise<string>;
490
+ declare function bootstrapProfile(options?: BootstrapRunOptions): Promise<string>;
491
+
492
+ declare const DEFAULT_HOME_DIR = "~/.feishu-codex";
493
+ interface BridgePaths {
494
+ home: string;
495
+ configFile: string;
496
+ activeProfileFile: string;
497
+ registryDir: string;
498
+ profilesDir: string;
499
+ }
500
+ interface ProfilePaths {
501
+ root: string;
502
+ config: string;
503
+ sessions: string;
504
+ sessionCatalog: string;
505
+ workspaces: string;
506
+ mediaDir: string;
507
+ logsDir: string;
508
+ secretsKey: string;
509
+ secretsStore: string;
510
+ codexHome: string;
511
+ feishuCliDir: string;
512
+ }
513
+ declare function expandTilde(path: string, home?: string): string;
514
+ declare function getBridgeHome(env?: NodeJS.ProcessEnv): string;
515
+ declare function bridgePaths(home?: string): BridgePaths;
516
+ declare function profilePaths(profile: string, home?: string): ProfilePaths;
517
+ declare function parentDir(path: string): string;
518
+ declare function tempRoot(): string;
519
+
520
+ declare class LocalKeystore {
521
+ private readonly paths;
522
+ constructor(paths: Pick<ProfilePaths, 'secretsKey' | 'secretsStore'>);
523
+ set(id: string, value: string): Promise<void>;
524
+ get(id: string): Promise<string | undefined>;
525
+ delete(id: string): Promise<void>;
526
+ export(includeSecrets?: boolean): Promise<Record<string, string>>;
527
+ private key;
528
+ private readPlain;
529
+ private writePlain;
530
+ }
531
+
532
+ interface RootConfigFile {
533
+ schemaVersion: 1;
534
+ activeProfile?: string;
535
+ profiles: Record<string, ProfileConfig>;
536
+ }
537
+ interface ProfileSummary {
538
+ name: string;
539
+ active: boolean;
540
+ tenant: Tenant;
541
+ appIdSuffix: string;
542
+ workspaceStatus: 'configured' | 'missing';
543
+ backend: string;
544
+ codexAuthMode: CodexAuthMode;
545
+ feishuCliIdentity: string;
546
+ }
547
+ interface CreateProfileWithSecretsInput {
548
+ name: string;
549
+ tenant: Tenant;
550
+ appId: string;
551
+ appSecret: string;
552
+ codexAuthMode: CodexAuthMode;
553
+ codexApiKey?: string;
554
+ workspace?: string;
555
+ codexPath?: string;
556
+ codexBackend?: CreateProfileInput['codexBackend'];
557
+ setActive?: boolean;
558
+ }
559
+ declare class ProfileStore {
560
+ private readonly home;
561
+ readonly paths: BridgePaths;
562
+ constructor(home?: string);
563
+ profilePaths(name: string): ProfilePaths;
564
+ keystore(name: string): LocalKeystore;
565
+ init(): Promise<void>;
566
+ create(input: CreateProfileWithSecretsInput): Promise<ProfileConfig>;
567
+ read(name?: string): Promise<ProfileConfig>;
568
+ write(profile: ProfileConfig): Promise<void>;
569
+ list(): Promise<ProfileSummary[]>;
570
+ use(name: string): Promise<void>;
571
+ activeProfile(): Promise<string | undefined>;
572
+ remove(name: string, opts?: {
573
+ purge?: boolean;
574
+ yes?: boolean;
575
+ }): Promise<string>;
576
+ export(name: string, opts?: {
577
+ includeSecrets?: boolean;
578
+ }): Promise<unknown>;
579
+ importFilesFrom(name: string, sourceDir: string): Promise<void>;
580
+ private assertExists;
581
+ private writeRootConfig;
582
+ private clearActiveIf;
583
+ }
584
+ declare function profileExists(path: string): Promise<boolean>;
585
+ declare function profileNameFromPath(path: string): string;
586
+ declare function profileLogPath(paths: ProfilePaths, kind: string): string;
587
+
588
+ interface SecretResolverOptions {
589
+ keystore?: LocalKeystore;
590
+ env?: NodeJS.ProcessEnv;
591
+ }
592
+ declare class SecretResolver {
593
+ private readonly options;
594
+ private readonly env;
595
+ constructor(options?: SecretResolverOptions);
596
+ resolve(ref: SecretRef): Promise<string>;
597
+ }
598
+
599
+ interface CommentRunInput {
600
+ profile: ProfileConfig;
601
+ runner: CodexRunner;
602
+ gateway: ChannelGateway;
603
+ commentScopeId: string;
604
+ prompt: string;
605
+ cwdRealpath: string;
606
+ apiKey?: string;
607
+ }
608
+ declare function runDocumentComment(input: CommentRunInput): Promise<string>;
609
+ declare function stripMarkdown(value: string): string;
610
+
611
+ declare function scopeForChat(input: {
612
+ chatId?: string;
613
+ threadId?: string;
614
+ chatType?: ChatType;
615
+ fileToken?: string;
616
+ commentId?: string;
617
+ nonce?: string;
618
+ documentLevel?: boolean;
619
+ }): string;
620
+ declare function messageRequiresMention(message: Pick<BridgeMessage, 'chatType' | 'mentions'>, botOpenId?: string): boolean;
621
+ declare function hasRealBotMention(mentions: readonly {
622
+ id: string;
623
+ isBot?: boolean;
624
+ isAll?: boolean;
625
+ }[], botOpenId?: string): boolean;
626
+ declare function hashId(value: string): string;
627
+
628
+ interface LarkCliProjection {
629
+ preset: FeishuCliConfig['identityPreset'];
630
+ env: Record<string, string>;
631
+ configDigest: string;
632
+ health: 'disabled' | 'app' | 'user-ready' | 'missing-config';
633
+ }
634
+ declare function projectLarkCliIdentity(profile: ProfileConfig): Promise<LarkCliProjection>;
635
+
636
+ interface AgentAttachment {
637
+ kind: 'image' | 'file';
638
+ path?: string;
639
+ name?: string;
640
+ bytes?: number;
641
+ mime?: string;
642
+ required?: boolean;
643
+ decision?: 'accepted' | 'rejected';
644
+ reason?: string;
645
+ }
646
+ interface RunPolicyInput {
647
+ profile: ProfileConfig;
648
+ scopeId: string;
649
+ message: Pick<BridgeMessage, 'chatType'>;
650
+ access: AccessDecision;
651
+ cwdRealpath: string;
652
+ prompt: string;
653
+ attachments: AgentAttachment[];
654
+ requestedAccess?: BridgeAccessMode;
655
+ feishuCliConfigDigest?: string;
656
+ }
657
+ type RunPolicyDecision = {
658
+ ok: true;
659
+ access: BridgeAccessMode;
660
+ sandbox: CodexSandboxMode;
661
+ networkAccess: boolean;
662
+ policyFingerprint: string;
663
+ attachments: AgentAttachment[];
664
+ runScopedEnv: Record<string, string>;
665
+ } | {
666
+ ok: false;
667
+ rejectReason: {
668
+ code: 'access-denied' | 'user-cli-group-blocked' | 'attachment-policy' | 'prompt-empty';
669
+ userVisible: string;
670
+ };
671
+ };
672
+ declare function evaluateRunPolicy(input: RunPolicyInput): RunPolicyDecision;
673
+
674
+ interface CachedMedia {
675
+ path: string;
676
+ hash: string;
677
+ bytes: number;
678
+ mime?: string;
679
+ }
680
+ declare class MediaCache {
681
+ private readonly root;
682
+ constructor(root: string);
683
+ putBytes(bytes: Buffer, opts?: {
684
+ name?: string;
685
+ mime?: string;
686
+ }): Promise<CachedMedia>;
687
+ fromFile(path: string, opts?: {
688
+ mime?: string;
689
+ kind?: AgentAttachment['kind'];
690
+ required?: boolean;
691
+ }): Promise<AgentAttachment>;
692
+ gc(ttlMs: number, now?: number): Promise<number>;
693
+ }
694
+ declare function hashFile(path: string): Promise<string>;
695
+
696
+ interface PolicyFingerprintInput {
697
+ scopeId: string;
698
+ cwdRealpath: string;
699
+ sandbox: string;
700
+ networkAccess: boolean;
701
+ accessDigest: unknown;
702
+ resourceDigest: unknown;
703
+ attachmentDigest: unknown;
704
+ codexAuthMode: string;
705
+ codexHome?: string;
706
+ runtimeIdentity?: string;
707
+ feishuCliIdentityPreset: string;
708
+ feishuCliConfigDigest?: string;
709
+ }
710
+ declare function policyFingerprint(input: PolicyFingerprintInput): string;
711
+ declare function accessPolicyDigest(profile: ProfileConfig): string;
712
+ declare function stableJson(value: unknown): string;
713
+
714
+ declare function clampAccess(requested: BridgeAccessMode, max: BridgeAccessMode): BridgeAccessMode;
715
+ declare function accessToSandbox(access: BridgeAccessMode): CodexSandboxMode;
716
+ declare function decideAccess(config: PermissionConfig, requested?: BridgeAccessMode): BridgeAccessMode;
717
+
718
+ type WorkspaceRejectReason = 'empty-cwd' | 'relative-cwd' | 'missing-cwd' | 'not-directory' | 'broad-cwd' | 'inaccessible-cwd';
719
+ type WorkspaceResult = {
720
+ ok: true;
721
+ cwdRealpath: string;
722
+ } | {
723
+ ok: false;
724
+ reason: WorkspaceRejectReason;
725
+ userVisible: string;
726
+ };
727
+ declare function resolveWorkspace(input: string | undefined): Promise<WorkspaceResult>;
728
+ declare function isBroadWorkspace(path: string): boolean;
729
+
730
+ interface PromptBuilderInput {
731
+ message: BridgeMessage;
732
+ botOpenId?: string;
733
+ quotedMessages?: Array<{
734
+ actorId: string;
735
+ text: string;
736
+ messageId: string;
737
+ }>;
738
+ interactiveCards?: unknown[];
739
+ attachments?: AgentAttachment[];
740
+ text?: string;
741
+ }
742
+ declare function buildPrompt(input: PromptBuilderInput): string;
743
+ declare const BRIDGE_INSTRUCTIONS: string;
744
+
745
+ interface ToolBlock {
746
+ id: string;
747
+ name: string;
748
+ input: unknown;
749
+ output?: string;
750
+ isError?: boolean;
751
+ status: 'running' | 'done';
752
+ }
753
+ interface RunState {
754
+ runId: string;
755
+ status: 'queued' | 'running' | 'done' | 'interrupted' | 'timeout' | 'error';
756
+ text: string;
757
+ tools: ToolBlock[];
758
+ usage?: {
759
+ inputTokens?: number;
760
+ outputTokens?: number;
761
+ cachedInputTokens?: number;
762
+ reasoningOutputTokens?: number;
763
+ };
764
+ error?: string;
765
+ threadId?: string;
766
+ }
767
+ declare function createRunState(runId: string): RunState;
768
+ declare function applyRunEvent(state: RunState, event: CodexBridgeEvent, secrets?: readonly string[]): RunState;
769
+
770
+ interface RunCardContext {
771
+ scopeId: string;
772
+ chatId: string;
773
+ operatorOpenId: string;
774
+ policyFingerprint: string;
775
+ workspaceLabel: string;
776
+ sandbox: string;
777
+ authMode: string;
778
+ callbackAuth?: CallbackAuth;
779
+ }
780
+ interface FeishuCard {
781
+ config: {
782
+ wide_screen_mode: boolean;
783
+ update_multi?: boolean;
784
+ };
785
+ header: {
786
+ title: {
787
+ tag: 'plain_text';
788
+ content: string;
789
+ };
790
+ template: string;
791
+ };
792
+ elements: unknown[];
793
+ }
794
+ declare function buildRunCard(state: RunState, context: RunCardContext): FeishuCard;
795
+ declare function renderMarkdownFallback(state: RunState): string;
796
+
797
+ declare function redactSecrets(text: string, explicitSecrets?: readonly string[]): string;
798
+
799
+ declare class ExecJsonRunner implements CodexRunner {
800
+ private readonly command;
801
+ private readonly args;
802
+ private readonly active;
803
+ constructor(command: string, args?: string[]);
804
+ start(input: CodexRunInput): Promise<CodexRunHandle>;
805
+ resume(threadId: string, input: CodexRunInput): Promise<CodexRunHandle>;
806
+ stop(runId: string): Promise<void>;
807
+ listThreads(_input: CodexThreadListInput): Promise<CodexThreadSummary[]>;
808
+ checkAvailability(): Promise<RunnerAvailability>;
809
+ private spawn;
810
+ }
811
+
812
+ declare class CodexSdkRunner implements CodexRunner {
813
+ private readonly active;
814
+ start(input: CodexRunInput): Promise<CodexRunHandle>;
815
+ resume(threadId: string, input: CodexRunInput): Promise<CodexRunHandle>;
816
+ stop(runId: string): Promise<void>;
817
+ listThreads(_input: CodexThreadListInput): Promise<CodexThreadSummary[]>;
818
+ checkAvailability(): Promise<RunnerAvailability>;
819
+ private run;
820
+ private streamEvents;
821
+ }
822
+ declare function codexBinaryLooksAvailable(codexPath: string): Promise<boolean>;
823
+
824
+ declare class SdkEventTranslator {
825
+ private readonly textByItem;
826
+ translate(event: ThreadEvent): CodexBridgeEvent[];
827
+ private itemStarted;
828
+ private itemUpdated;
829
+ private itemCompleted;
830
+ private textDelta;
831
+ }
832
+
833
+ interface RunBridgeOptions {
834
+ profile?: string;
835
+ workspace?: string;
836
+ home?: string;
837
+ preflight?: boolean;
838
+ bootstrapOwner?: boolean;
839
+ appId?: string;
840
+ appSecret?: string;
841
+ tenant?: Tenant;
842
+ codexAuth?: CodexAuthMode;
843
+ codexApiKey?: string;
844
+ codexPath?: string;
845
+ pollChat?: string[];
846
+ }
847
+ declare function runBridge(options?: RunBridgeOptions): Promise<void>;
848
+ declare function runnerForProfile(profile: {
849
+ codex: {
850
+ backend: string;
851
+ codexPath?: string;
852
+ };
853
+ }): CodexRunner;
854
+
855
+ declare class ProcessPool {
856
+ private readonly maxConcurrency;
857
+ private active;
858
+ private readonly waiters;
859
+ constructor(maxConcurrency: number);
860
+ snapshot(): {
861
+ active: number;
862
+ queued: number;
863
+ maxConcurrency: number;
864
+ };
865
+ acquire(opts?: {
866
+ wait?: boolean;
867
+ }): Promise<() => void>;
868
+ private release;
869
+ }
870
+
871
+ interface StartRunInput {
872
+ profile: ProfileConfig;
873
+ runner: CodexRunner;
874
+ secretResolver: SecretResolver;
875
+ sessions: SessionStore;
876
+ sessionCatalog: SessionCatalog;
877
+ workspaces: WorkspaceStore;
878
+ message: BridgeMessage;
879
+ access: AccessDecision;
880
+ prompt: string;
881
+ attachments: AgentAttachment[];
882
+ runId: string;
883
+ now?: number;
884
+ onEvent?: (event: CodexBridgeEvent) => void | Promise<void>;
885
+ }
886
+ type StartRunResult = {
887
+ ok: true;
888
+ handle: CodexRunHandle;
889
+ cwdRealpath: string;
890
+ policy: Extract<RunPolicyDecision, {
891
+ ok: true;
892
+ }>;
893
+ resumeFrom?: string;
894
+ } | {
895
+ ok: false;
896
+ workspace?: WorkspaceResult;
897
+ policy?: Extract<RunPolicyDecision, {
898
+ ok: false;
899
+ }>;
900
+ userVisible: string;
901
+ code: string;
902
+ };
903
+ declare function startRun(input: StartRunInput): Promise<StartRunResult>;
904
+
905
+ interface OrchestratorInput {
906
+ profile: ProfileConfig;
907
+ runner: CodexRunner;
908
+ secretResolver: SecretResolver;
909
+ sessions: SessionStore;
910
+ sessionCatalog: SessionCatalog;
911
+ workspaces: WorkspaceStore;
912
+ pool?: ProcessPool;
913
+ runId?: () => string;
914
+ onEvent?: (scopeId: string, event: CodexBridgeEvent) => void | Promise<void>;
915
+ }
916
+ interface SubmitInput {
917
+ message: BridgeMessage;
918
+ access: AccessDecision;
919
+ prompt: string;
920
+ attachments?: AgentAttachment[];
921
+ }
922
+ declare class RunOrchestrator {
923
+ private readonly input;
924
+ private readonly scopes;
925
+ private readonly pool;
926
+ private readonly runId;
927
+ constructor(input: OrchestratorInput);
928
+ snapshot(scopeId?: string): unknown;
929
+ isActive(scopeId: string): boolean;
930
+ submit(input: SubmitInput): Promise<StartRunResult | {
931
+ ok: true;
932
+ queued: true;
933
+ }>;
934
+ stop(scopeId: string): Promise<boolean>;
935
+ private startInScope;
936
+ private state;
937
+ }
938
+
939
+ interface BindBridgeRuntimeInput {
940
+ profile: ProfileConfig;
941
+ paths: ProfilePaths;
942
+ gateway: ChannelGateway;
943
+ runner: CodexRunner;
944
+ secretResolver: SecretResolver;
945
+ appSecret: string;
946
+ ownerOpenId?: string;
947
+ botOpenId?: string;
948
+ bootstrapOwner?: boolean;
949
+ saveProfile?: (profile: ProfileConfig) => Promise<void>;
950
+ processSnapshot?: () => Promise<unknown> | unknown;
951
+ lifecycle?: {
952
+ exit?: (target?: string) => Promise<void>;
953
+ reconnect?: () => Promise<void>;
954
+ };
955
+ }
956
+ declare function bindBridgeRuntime(input: BindBridgeRuntimeInput): RunOrchestrator;
957
+
958
+ declare class RuntimeLock {
959
+ private readonly path;
960
+ private locked;
961
+ constructor(path: string);
962
+ acquire(): Promise<void>;
963
+ release(): Promise<void>;
964
+ private isStale;
965
+ }
966
+ declare function profileLock(profile: string, home?: string): RuntimeLock;
967
+ declare function appLock(appId: string, home?: string): RuntimeLock;
968
+
969
+ interface ProcessRecord {
970
+ id: string;
971
+ pid: number;
972
+ profile: string;
973
+ tenant: string;
974
+ appId: string;
975
+ botName?: string;
976
+ startedAt: number;
977
+ host: string;
978
+ platform: string;
979
+ logPath?: string;
980
+ }
981
+ declare class RuntimeRegistry {
982
+ private readonly path;
983
+ constructor(home?: string);
984
+ register(record: Omit<ProcessRecord, 'id' | 'pid' | 'startedAt' | 'host' | 'platform'> & Partial<Pick<ProcessRecord, 'id' | 'pid' | 'startedAt'>>): Promise<ProcessRecord>;
985
+ unregister(idOrPid: string | number): Promise<void>;
986
+ list(): Promise<ProcessRecord[]>;
987
+ find(idOrPid: string | number): Promise<ProcessRecord | undefined>;
988
+ kill(idOrPid: string | number, signal?: NodeJS.Signals): Promise<ProcessRecord | undefined>;
989
+ private read;
990
+ private write;
991
+ }
992
+ declare function isAlive(pid: number): boolean;
993
+
994
+ export { type AccessContext, type AccessDecision, type ActorRole, type ActorType, type AgentAttachment, type AppConfig, type ApprovalRequest, type AttachmentConfig, BRIDGE_INSTRUCTIONS, type BindBridgeRuntimeInput, type BootstrapRunOptions, type BridgeAccessMode, type BridgeAction, type BridgeCommentTarget, type BridgeMention, type BridgeMessage, type BridgePaths, type BridgeReply, type BridgeResource, type BridgeResourceData, type BridgeSource, type CachedMedia, CallbackAuth, type CallbackKey, type CallbackNonceStore, type CallbackPayload, type CallbackSignInput, type CallbackVerifyExpected, type CallbackVerifyResult, type ChannelGateway, type ChatType, type CodexAuthConfig, type CodexAuthMode, type CodexBackend, type CodexBridgeEvent, type CodexConfig, type CodexRunHandle, type CodexRunInput, type CodexRunner, type CodexSandboxMode, CodexSdkRunner, type CodexThreadListInput, type CodexThreadSummary, type CommandContext, type CommandResult, type CommentRunInput, type CreateProfileInput, type CreateProfileWithSecretsInput, DEFAULT_HOME_DIR, ExecJsonRunner, type FeishuCard, type FeishuCliConfig, type LarkCliIdentityPreset, type LarkCliProjection, LocalKeystore, MediaCache, MemoryCallbackNonceStore, type NonceState, type OrchestratorInput, type PermissionConfig, type PolicyFingerprintInput, type PreferencesConfig, ProcessPool, type ProcessRecord, type ProfileAccess, type ProfileConfig, type ProfilePaths, ProfileStore, type ProfileSummary, type PromptBuilderInput, type RegistrationResult, type RegistrationWizardOptions, type ReplyMode, type RootConfigFile, type RunBridgeOptions, type RunCardContext, RunOrchestrator, type RunPolicyDecision, type RunPolicyInput, type RunState, type RunnerAvailability, RuntimeLock, RuntimeRegistry, SdkEventTranslator, type SecretRef, SecretResolver, type SecretResolverOptions, SessionCatalog, type SessionCatalogEntry, SessionStore, type StartRunInput, type StartRunResult, type SubmitInput, type Tenant, type ToolBlock, type WorkspaceRejectReason, type WorkspaceResult, WorkspaceStore, accessPolicyDigest, accessToSandbox, appLock, applyRunEvent, bindBridgeRuntime, bootstrapProfile, bridgePaths, buildPrompt, buildRunCard, canRunAdminCommand, catalogKey, clampAccess, codexBinaryLooksAvailable, createDefaultProfileConfig, createRunState, decideAccess, ensureProfileForRun, evaluateAccess, evaluateRunPolicy, expandTilde, getBridgeHome, hasRealBotMention, hashFile, hashId, isAlive, isBroadWorkspace, isSecretRef, messageRequiresMention, normalizeProfileConfig, parentDir, parseCommand, policyFingerprint, profileExists, profileLock, profileLogPath, profileNameFromPath, profilePaths, projectLarkCliIdentity, redactProfileConfig, redactSecrets, renderMarkdownFallback, resolveWorkspace, routeCommand, runBridge, runDocumentComment, runRegistrationWizard, runnerForProfile, scopeForChat, stableJson, startRun, stripMarkdown, tempRoot };