@foxden-app/foxclaw 0.5.9 → 0.5.11
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/CHANGELOG.md +26 -0
- package/README.md +1 -0
- package/README_EN.md +1 -0
- package/dist/auth/mirror.d.ts +6 -0
- package/dist/auth/mirror.js +49 -0
- package/dist/controller/controller.d.ts +21 -2
- package/dist/controller/controller.js +516 -55
- package/dist/i18n.d.ts +36 -6
- package/dist/i18n.js +36 -6
- package/dist/main.js +22 -0
- package/dist/store/database.d.ts +25 -1
- package/dist/store/database.js +321 -0
- package/dist/telegram/gateway.d.ts +1 -0
- package/dist/telegram/gateway.js +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/update.d.ts +3 -0
- package/dist/update.js +72 -3
- package/docs/devlogs//346/212/200/346/234/257/351/200/232/350/256/257/01./346/212/212 Codex /345/270/246/345/210/260/346/234/200/345/220/216/344/270/200/345/205/254/351/207/214/357/274/232FoxClaw /347/232/204 auth /347/224/237/345/221/275/345/221/250/346/234/237/344/270/216/350/267/250/350/212/202/347/202/271/345/220/214/346/255/245.md" +341 -0
- package/docs/release.md +102 -0
- package/docs/zh/release.md +102 -0
- package/package.json +1 -1
package/dist/store/database.js
CHANGED
|
@@ -98,6 +98,61 @@ export class BridgeStore {
|
|
|
98
98
|
created_at INTEGER NOT NULL,
|
|
99
99
|
PRIMARY KEY (input_local_id, question_index, message_kind)
|
|
100
100
|
);
|
|
101
|
+
CREATE TABLE IF NOT EXISTS queued_turn_inputs (
|
|
102
|
+
queue_id TEXT PRIMARY KEY,
|
|
103
|
+
scope_id TEXT NOT NULL,
|
|
104
|
+
chat_id TEXT NOT NULL,
|
|
105
|
+
chat_type TEXT NOT NULL,
|
|
106
|
+
topic_id INTEGER,
|
|
107
|
+
thread_id TEXT NOT NULL,
|
|
108
|
+
input_json TEXT NOT NULL,
|
|
109
|
+
source_summary TEXT NOT NULL,
|
|
110
|
+
message_id INTEGER,
|
|
111
|
+
status TEXT NOT NULL,
|
|
112
|
+
error TEXT,
|
|
113
|
+
created_at INTEGER NOT NULL,
|
|
114
|
+
updated_at INTEGER NOT NULL,
|
|
115
|
+
resolved_at INTEGER
|
|
116
|
+
);
|
|
117
|
+
CREATE INDEX IF NOT EXISTS queued_turn_inputs_scope_status_idx
|
|
118
|
+
ON queued_turn_inputs(scope_id, status, created_at);
|
|
119
|
+
CREATE TABLE IF NOT EXISTS pending_attachment_batches (
|
|
120
|
+
batch_id TEXT PRIMARY KEY,
|
|
121
|
+
scope_id TEXT NOT NULL,
|
|
122
|
+
chat_id TEXT NOT NULL,
|
|
123
|
+
chat_type TEXT NOT NULL,
|
|
124
|
+
topic_id INTEGER,
|
|
125
|
+
thread_id TEXT NOT NULL,
|
|
126
|
+
cwd TEXT,
|
|
127
|
+
media_group_id TEXT,
|
|
128
|
+
attachments_json TEXT NOT NULL,
|
|
129
|
+
caption TEXT NOT NULL,
|
|
130
|
+
message_id INTEGER,
|
|
131
|
+
status TEXT NOT NULL,
|
|
132
|
+
created_at INTEGER NOT NULL,
|
|
133
|
+
updated_at INTEGER NOT NULL,
|
|
134
|
+
resolved_at INTEGER
|
|
135
|
+
);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS pending_attachment_batches_scope_status_idx
|
|
137
|
+
ON pending_attachment_batches(scope_id, status, updated_at);
|
|
138
|
+
CREATE TABLE IF NOT EXISTS guided_plan_sessions (
|
|
139
|
+
session_id TEXT PRIMARY KEY,
|
|
140
|
+
scope_id TEXT NOT NULL,
|
|
141
|
+
chat_id TEXT NOT NULL,
|
|
142
|
+
chat_type TEXT NOT NULL,
|
|
143
|
+
topic_id INTEGER,
|
|
144
|
+
thread_id TEXT NOT NULL,
|
|
145
|
+
turn_id TEXT NOT NULL,
|
|
146
|
+
cwd TEXT,
|
|
147
|
+
plan_markdown TEXT NOT NULL,
|
|
148
|
+
message_id INTEGER,
|
|
149
|
+
state TEXT NOT NULL,
|
|
150
|
+
created_at INTEGER NOT NULL,
|
|
151
|
+
updated_at INTEGER NOT NULL,
|
|
152
|
+
resolved_at INTEGER
|
|
153
|
+
);
|
|
154
|
+
CREATE INDEX IF NOT EXISTS guided_plan_sessions_scope_state_idx
|
|
155
|
+
ON guided_plan_sessions(scope_id, state, updated_at);
|
|
101
156
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
102
157
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
103
158
|
direction TEXT NOT NULL,
|
|
@@ -438,6 +493,204 @@ export class BridgeStore {
|
|
|
438
493
|
const row = this.db.prepare('SELECT COUNT(*) AS count FROM pending_user_inputs WHERE resolved_at IS NULL').get();
|
|
439
494
|
return Number(row?.count ?? 0);
|
|
440
495
|
}
|
|
496
|
+
saveQueuedTurnInput(record) {
|
|
497
|
+
this.db.prepare(`
|
|
498
|
+
INSERT INTO queued_turn_inputs (
|
|
499
|
+
queue_id, scope_id, chat_id, chat_type, topic_id, thread_id, input_json, source_summary,
|
|
500
|
+
message_id, status, error, created_at, updated_at, resolved_at
|
|
501
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
502
|
+
ON CONFLICT(queue_id) DO UPDATE SET
|
|
503
|
+
scope_id = excluded.scope_id,
|
|
504
|
+
chat_id = excluded.chat_id,
|
|
505
|
+
chat_type = excluded.chat_type,
|
|
506
|
+
topic_id = excluded.topic_id,
|
|
507
|
+
thread_id = excluded.thread_id,
|
|
508
|
+
input_json = excluded.input_json,
|
|
509
|
+
source_summary = excluded.source_summary,
|
|
510
|
+
message_id = excluded.message_id,
|
|
511
|
+
status = excluded.status,
|
|
512
|
+
error = excluded.error,
|
|
513
|
+
created_at = excluded.created_at,
|
|
514
|
+
updated_at = excluded.updated_at,
|
|
515
|
+
resolved_at = excluded.resolved_at
|
|
516
|
+
`).run(record.queueId, record.scopeId, record.chatId, record.chatType, record.topicId, record.threadId, record.inputJson, record.sourceSummary, record.messageId, record.status, record.error, record.createdAt, record.updatedAt, record.resolvedAt);
|
|
517
|
+
}
|
|
518
|
+
getQueuedTurnInput(queueId) {
|
|
519
|
+
const row = this.db.prepare('SELECT * FROM queued_turn_inputs WHERE queue_id = ?').get(queueId);
|
|
520
|
+
return row ? this.mapQueuedTurnInput(row) : null;
|
|
521
|
+
}
|
|
522
|
+
peekQueuedTurnInput(scopeId) {
|
|
523
|
+
const row = this.db.prepare(`
|
|
524
|
+
SELECT * FROM queued_turn_inputs
|
|
525
|
+
WHERE scope_id = ? AND status = 'queued'
|
|
526
|
+
ORDER BY created_at ASC
|
|
527
|
+
LIMIT 1
|
|
528
|
+
`).get(scopeId);
|
|
529
|
+
return row ? this.mapQueuedTurnInput(row) : null;
|
|
530
|
+
}
|
|
531
|
+
listQueuedTurnInputs(scopeId) {
|
|
532
|
+
const sql = scopeId
|
|
533
|
+
? `SELECT * FROM queued_turn_inputs WHERE scope_id = ? AND status IN ('queued', 'processing') ORDER BY created_at ASC`
|
|
534
|
+
: `SELECT * FROM queued_turn_inputs WHERE status IN ('queued', 'processing') ORDER BY created_at ASC`;
|
|
535
|
+
const rows = scopeId
|
|
536
|
+
? this.db.prepare(sql).all(scopeId)
|
|
537
|
+
: this.db.prepare(sql).all();
|
|
538
|
+
return rows.map((row) => this.mapQueuedTurnInput(row));
|
|
539
|
+
}
|
|
540
|
+
countQueuedTurnInputs(scopeId) {
|
|
541
|
+
const row = scopeId
|
|
542
|
+
? this.db.prepare(`SELECT COUNT(*) AS count FROM queued_turn_inputs WHERE scope_id = ? AND status IN ('queued', 'processing')`).get(scopeId)
|
|
543
|
+
: this.db.prepare(`SELECT COUNT(*) AS count FROM queued_turn_inputs WHERE status IN ('queued', 'processing')`).get();
|
|
544
|
+
return Number(row?.count ?? 0);
|
|
545
|
+
}
|
|
546
|
+
updateQueuedTurnInputStatus(queueId, status, error = null) {
|
|
547
|
+
const resolvedAt = status === 'queued' || status === 'processing' ? null : Date.now();
|
|
548
|
+
this.db.prepare(`
|
|
549
|
+
UPDATE queued_turn_inputs
|
|
550
|
+
SET status = ?, error = ?, updated_at = ?, resolved_at = ?
|
|
551
|
+
WHERE queue_id = ?
|
|
552
|
+
`).run(status, error, Date.now(), resolvedAt, queueId);
|
|
553
|
+
}
|
|
554
|
+
cancelQueuedTurnInputs(scopeId) {
|
|
555
|
+
const result = this.db.prepare(`
|
|
556
|
+
UPDATE queued_turn_inputs
|
|
557
|
+
SET status = 'cancelled', updated_at = ?, resolved_at = ?
|
|
558
|
+
WHERE scope_id = ? AND status = 'queued'
|
|
559
|
+
`).run(Date.now(), Date.now(), scopeId);
|
|
560
|
+
return Number(result.changes ?? 0);
|
|
561
|
+
}
|
|
562
|
+
requeueInterruptedQueuedTurnInputs() {
|
|
563
|
+
const result = this.db.prepare(`
|
|
564
|
+
UPDATE queued_turn_inputs
|
|
565
|
+
SET status = 'queued', error = NULL, updated_at = ?, resolved_at = NULL
|
|
566
|
+
WHERE status = 'processing'
|
|
567
|
+
`).run(Date.now());
|
|
568
|
+
return Number(result.changes ?? 0);
|
|
569
|
+
}
|
|
570
|
+
savePendingAttachmentBatch(record) {
|
|
571
|
+
this.db.prepare(`
|
|
572
|
+
INSERT INTO pending_attachment_batches (
|
|
573
|
+
batch_id, scope_id, chat_id, chat_type, topic_id, thread_id, cwd, media_group_id,
|
|
574
|
+
attachments_json, caption, message_id, status, created_at, updated_at, resolved_at
|
|
575
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
576
|
+
ON CONFLICT(batch_id) DO UPDATE SET
|
|
577
|
+
scope_id = excluded.scope_id,
|
|
578
|
+
chat_id = excluded.chat_id,
|
|
579
|
+
chat_type = excluded.chat_type,
|
|
580
|
+
topic_id = excluded.topic_id,
|
|
581
|
+
thread_id = excluded.thread_id,
|
|
582
|
+
cwd = excluded.cwd,
|
|
583
|
+
media_group_id = excluded.media_group_id,
|
|
584
|
+
attachments_json = excluded.attachments_json,
|
|
585
|
+
caption = excluded.caption,
|
|
586
|
+
message_id = excluded.message_id,
|
|
587
|
+
status = excluded.status,
|
|
588
|
+
created_at = excluded.created_at,
|
|
589
|
+
updated_at = excluded.updated_at,
|
|
590
|
+
resolved_at = excluded.resolved_at
|
|
591
|
+
`).run(record.batchId, record.scopeId, record.chatId, record.chatType, record.topicId, record.threadId, record.cwd, record.mediaGroupId, record.attachmentsJson, record.caption, record.messageId, record.status, record.createdAt, record.updatedAt, record.resolvedAt);
|
|
592
|
+
}
|
|
593
|
+
getPendingAttachmentBatch(batchId) {
|
|
594
|
+
const row = this.db.prepare('SELECT * FROM pending_attachment_batches WHERE batch_id = ?').get(batchId);
|
|
595
|
+
return row ? this.mapPendingAttachmentBatch(row) : null;
|
|
596
|
+
}
|
|
597
|
+
findPendingAttachmentBatchByMediaGroup(scopeId, mediaGroupId) {
|
|
598
|
+
const row = this.db.prepare(`
|
|
599
|
+
SELECT * FROM pending_attachment_batches
|
|
600
|
+
WHERE scope_id = ? AND media_group_id = ? AND status = 'pending'
|
|
601
|
+
ORDER BY updated_at DESC
|
|
602
|
+
LIMIT 1
|
|
603
|
+
`).get(scopeId, mediaGroupId);
|
|
604
|
+
return row ? this.mapPendingAttachmentBatch(row) : null;
|
|
605
|
+
}
|
|
606
|
+
getLatestPendingAttachmentBatch(scopeId) {
|
|
607
|
+
const row = this.db.prepare(`
|
|
608
|
+
SELECT * FROM pending_attachment_batches
|
|
609
|
+
WHERE scope_id = ? AND status = 'pending'
|
|
610
|
+
ORDER BY updated_at DESC
|
|
611
|
+
LIMIT 1
|
|
612
|
+
`).get(scopeId);
|
|
613
|
+
return row ? this.mapPendingAttachmentBatch(row) : null;
|
|
614
|
+
}
|
|
615
|
+
updatePendingAttachmentBatchMessage(batchId, messageId) {
|
|
616
|
+
this.db.prepare('UPDATE pending_attachment_batches SET message_id = ?, updated_at = ? WHERE batch_id = ?').run(messageId, Date.now(), batchId);
|
|
617
|
+
}
|
|
618
|
+
resolvePendingAttachmentBatch(batchId, status) {
|
|
619
|
+
this.db.prepare(`
|
|
620
|
+
UPDATE pending_attachment_batches
|
|
621
|
+
SET status = ?, updated_at = ?, resolved_at = ?
|
|
622
|
+
WHERE batch_id = ?
|
|
623
|
+
`).run(status, Date.now(), Date.now(), batchId);
|
|
624
|
+
}
|
|
625
|
+
clearPendingAttachmentBatches(scopeId) {
|
|
626
|
+
const result = this.db.prepare(`
|
|
627
|
+
UPDATE pending_attachment_batches
|
|
628
|
+
SET status = 'cleared', updated_at = ?, resolved_at = ?
|
|
629
|
+
WHERE scope_id = ? AND status = 'pending'
|
|
630
|
+
`).run(Date.now(), Date.now(), scopeId);
|
|
631
|
+
return Number(result.changes ?? 0);
|
|
632
|
+
}
|
|
633
|
+
saveGuidedPlanSession(record) {
|
|
634
|
+
this.db.prepare(`
|
|
635
|
+
INSERT INTO guided_plan_sessions (
|
|
636
|
+
session_id, scope_id, chat_id, chat_type, topic_id, thread_id, turn_id, cwd,
|
|
637
|
+
plan_markdown, message_id, state, created_at, updated_at, resolved_at
|
|
638
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
639
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
640
|
+
scope_id = excluded.scope_id,
|
|
641
|
+
chat_id = excluded.chat_id,
|
|
642
|
+
chat_type = excluded.chat_type,
|
|
643
|
+
topic_id = excluded.topic_id,
|
|
644
|
+
thread_id = excluded.thread_id,
|
|
645
|
+
turn_id = excluded.turn_id,
|
|
646
|
+
cwd = excluded.cwd,
|
|
647
|
+
plan_markdown = excluded.plan_markdown,
|
|
648
|
+
message_id = excluded.message_id,
|
|
649
|
+
state = excluded.state,
|
|
650
|
+
created_at = excluded.created_at,
|
|
651
|
+
updated_at = excluded.updated_at,
|
|
652
|
+
resolved_at = excluded.resolved_at
|
|
653
|
+
`).run(record.sessionId, record.scopeId, record.chatId, record.chatType, record.topicId, record.threadId, record.turnId, record.cwd, record.planMarkdown, record.messageId, record.state, record.createdAt, record.updatedAt, record.resolvedAt);
|
|
654
|
+
}
|
|
655
|
+
getGuidedPlanSession(sessionId) {
|
|
656
|
+
const row = this.db.prepare('SELECT * FROM guided_plan_sessions WHERE session_id = ?').get(sessionId);
|
|
657
|
+
return row ? this.mapGuidedPlanSession(row) : null;
|
|
658
|
+
}
|
|
659
|
+
findOpenGuidedPlanSession(scopeId, turnId) {
|
|
660
|
+
const row = turnId
|
|
661
|
+
? this.db.prepare(`
|
|
662
|
+
SELECT * FROM guided_plan_sessions
|
|
663
|
+
WHERE scope_id = ? AND turn_id = ? AND state = 'awaiting_confirmation'
|
|
664
|
+
ORDER BY updated_at DESC
|
|
665
|
+
LIMIT 1
|
|
666
|
+
`).get(scopeId, turnId)
|
|
667
|
+
: this.db.prepare(`
|
|
668
|
+
SELECT * FROM guided_plan_sessions
|
|
669
|
+
WHERE scope_id = ? AND state = 'awaiting_confirmation'
|
|
670
|
+
ORDER BY updated_at DESC
|
|
671
|
+
LIMIT 1
|
|
672
|
+
`).get(scopeId);
|
|
673
|
+
return row ? this.mapGuidedPlanSession(row) : null;
|
|
674
|
+
}
|
|
675
|
+
listOpenGuidedPlanSessions() {
|
|
676
|
+
const rows = this.db.prepare(`
|
|
677
|
+
SELECT * FROM guided_plan_sessions
|
|
678
|
+
WHERE state = 'awaiting_confirmation'
|
|
679
|
+
ORDER BY updated_at ASC
|
|
680
|
+
`).all();
|
|
681
|
+
return rows.map((row) => this.mapGuidedPlanSession(row));
|
|
682
|
+
}
|
|
683
|
+
updateGuidedPlanSessionMessage(sessionId, messageId) {
|
|
684
|
+
this.db.prepare('UPDATE guided_plan_sessions SET message_id = ?, updated_at = ? WHERE session_id = ?').run(messageId, Date.now(), sessionId);
|
|
685
|
+
}
|
|
686
|
+
updateGuidedPlanSessionState(sessionId, state) {
|
|
687
|
+
const resolvedAt = state === 'awaiting_confirmation' || state === 'executing' ? null : Date.now();
|
|
688
|
+
this.db.prepare(`
|
|
689
|
+
UPDATE guided_plan_sessions
|
|
690
|
+
SET state = ?, updated_at = ?, resolved_at = ?
|
|
691
|
+
WHERE session_id = ?
|
|
692
|
+
`).run(state, Date.now(), resolvedAt, sessionId);
|
|
693
|
+
}
|
|
441
694
|
insertAudit(direction, chatId, eventType, summary) {
|
|
442
695
|
this.db.prepare('INSERT INTO audit_logs (direction, chat_id, event_type, summary, created_at) VALUES (?, ?, ?, ?, ?)').run(direction, chatId, eventType, summary, Date.now());
|
|
443
696
|
}
|
|
@@ -482,6 +735,61 @@ export class BridgeStore {
|
|
|
482
735
|
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
483
736
|
};
|
|
484
737
|
}
|
|
738
|
+
mapQueuedTurnInput(row) {
|
|
739
|
+
return {
|
|
740
|
+
queueId: String(row.queue_id),
|
|
741
|
+
scopeId: String(row.scope_id),
|
|
742
|
+
chatId: String(row.chat_id),
|
|
743
|
+
chatType: String(row.chat_type),
|
|
744
|
+
topicId: row.topic_id === null ? null : Number(row.topic_id),
|
|
745
|
+
threadId: String(row.thread_id),
|
|
746
|
+
inputJson: String(row.input_json),
|
|
747
|
+
sourceSummary: String(row.source_summary),
|
|
748
|
+
messageId: row.message_id === null ? null : Number(row.message_id),
|
|
749
|
+
status: normalizeQueuedTurnInputStatus(row.status),
|
|
750
|
+
error: row.error === null ? null : String(row.error),
|
|
751
|
+
createdAt: Number(row.created_at),
|
|
752
|
+
updatedAt: Number(row.updated_at),
|
|
753
|
+
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
mapPendingAttachmentBatch(row) {
|
|
757
|
+
return {
|
|
758
|
+
batchId: String(row.batch_id),
|
|
759
|
+
scopeId: String(row.scope_id),
|
|
760
|
+
chatId: String(row.chat_id),
|
|
761
|
+
chatType: String(row.chat_type),
|
|
762
|
+
topicId: row.topic_id === null ? null : Number(row.topic_id),
|
|
763
|
+
threadId: String(row.thread_id),
|
|
764
|
+
cwd: row.cwd === null ? null : String(row.cwd),
|
|
765
|
+
mediaGroupId: row.media_group_id === null ? null : String(row.media_group_id),
|
|
766
|
+
attachmentsJson: String(row.attachments_json),
|
|
767
|
+
caption: String(row.caption ?? ''),
|
|
768
|
+
messageId: row.message_id === null ? null : Number(row.message_id),
|
|
769
|
+
status: normalizePendingAttachmentBatchStatus(row.status),
|
|
770
|
+
createdAt: Number(row.created_at),
|
|
771
|
+
updatedAt: Number(row.updated_at),
|
|
772
|
+
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
mapGuidedPlanSession(row) {
|
|
776
|
+
return {
|
|
777
|
+
sessionId: String(row.session_id),
|
|
778
|
+
scopeId: String(row.scope_id),
|
|
779
|
+
chatId: String(row.chat_id),
|
|
780
|
+
chatType: String(row.chat_type),
|
|
781
|
+
topicId: row.topic_id === null ? null : Number(row.topic_id),
|
|
782
|
+
threadId: String(row.thread_id),
|
|
783
|
+
turnId: String(row.turn_id),
|
|
784
|
+
cwd: row.cwd === null ? null : String(row.cwd),
|
|
785
|
+
planMarkdown: String(row.plan_markdown),
|
|
786
|
+
messageId: row.message_id === null ? null : Number(row.message_id),
|
|
787
|
+
state: normalizeGuidedPlanSessionState(row.state),
|
|
788
|
+
createdAt: Number(row.created_at),
|
|
789
|
+
updatedAt: Number(row.updated_at),
|
|
790
|
+
resolvedAt: row.resolved_at === null ? null : Number(row.resolved_at),
|
|
791
|
+
};
|
|
792
|
+
}
|
|
485
793
|
writeChatSettings(chatId, model, reasoningEffort, locale, accessPreset, collaborationMode, serviceTier, activeTurnMessageMode) {
|
|
486
794
|
this.db.prepare(`
|
|
487
795
|
INSERT INTO chat_settings (chat_id, model, reasoning_effort, locale, access_preset, collaboration_mode, service_tier, active_turn_message_mode, updated_at)
|
|
@@ -615,3 +923,16 @@ function normalizeCollaborationMode(value) {
|
|
|
615
923
|
function normalizeActiveTurnMessageMode(value) {
|
|
616
924
|
return value === 'steer' || value === 'queue' ? value : null;
|
|
617
925
|
}
|
|
926
|
+
function normalizeQueuedTurnInputStatus(value) {
|
|
927
|
+
return value === 'processing' || value === 'completed' || value === 'cancelled' || value === 'failed'
|
|
928
|
+
? value
|
|
929
|
+
: 'queued';
|
|
930
|
+
}
|
|
931
|
+
function normalizePendingAttachmentBatchStatus(value) {
|
|
932
|
+
return value === 'consumed' || value === 'cleared' ? value : 'pending';
|
|
933
|
+
}
|
|
934
|
+
function normalizeGuidedPlanSessionState(value) {
|
|
935
|
+
return value === 'executing' || value === 'cancelled' || value === 'completed'
|
|
936
|
+
? value
|
|
937
|
+
: 'awaiting_confirmation';
|
|
938
|
+
}
|
package/dist/telegram/gateway.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -25,6 +25,58 @@ export interface ChatSessionSettings {
|
|
|
25
25
|
activeTurnMessageMode: ActiveTurnMessageMode | null;
|
|
26
26
|
updatedAt: number;
|
|
27
27
|
}
|
|
28
|
+
export type QueuedTurnInputStatus = 'queued' | 'processing' | 'completed' | 'cancelled' | 'failed';
|
|
29
|
+
export interface QueuedTurnInputRecord {
|
|
30
|
+
queueId: string;
|
|
31
|
+
scopeId: string;
|
|
32
|
+
chatId: string;
|
|
33
|
+
chatType: string;
|
|
34
|
+
topicId: number | null;
|
|
35
|
+
threadId: string;
|
|
36
|
+
inputJson: string;
|
|
37
|
+
sourceSummary: string;
|
|
38
|
+
messageId: number | null;
|
|
39
|
+
status: QueuedTurnInputStatus;
|
|
40
|
+
error: string | null;
|
|
41
|
+
createdAt: number;
|
|
42
|
+
updatedAt: number;
|
|
43
|
+
resolvedAt: number | null;
|
|
44
|
+
}
|
|
45
|
+
export type PendingAttachmentBatchStatus = 'pending' | 'consumed' | 'cleared';
|
|
46
|
+
export interface PendingAttachmentBatchRecord {
|
|
47
|
+
batchId: string;
|
|
48
|
+
scopeId: string;
|
|
49
|
+
chatId: string;
|
|
50
|
+
chatType: string;
|
|
51
|
+
topicId: number | null;
|
|
52
|
+
threadId: string;
|
|
53
|
+
cwd: string | null;
|
|
54
|
+
mediaGroupId: string | null;
|
|
55
|
+
attachmentsJson: string;
|
|
56
|
+
caption: string;
|
|
57
|
+
messageId: number | null;
|
|
58
|
+
status: PendingAttachmentBatchStatus;
|
|
59
|
+
createdAt: number;
|
|
60
|
+
updatedAt: number;
|
|
61
|
+
resolvedAt: number | null;
|
|
62
|
+
}
|
|
63
|
+
export type GuidedPlanSessionState = 'awaiting_confirmation' | 'executing' | 'cancelled' | 'completed';
|
|
64
|
+
export interface GuidedPlanSessionRecord {
|
|
65
|
+
sessionId: string;
|
|
66
|
+
scopeId: string;
|
|
67
|
+
chatId: string;
|
|
68
|
+
chatType: string;
|
|
69
|
+
topicId: number | null;
|
|
70
|
+
threadId: string;
|
|
71
|
+
turnId: string;
|
|
72
|
+
cwd: string | null;
|
|
73
|
+
planMarkdown: string;
|
|
74
|
+
messageId: number | null;
|
|
75
|
+
state: GuidedPlanSessionState;
|
|
76
|
+
createdAt: number;
|
|
77
|
+
updatedAt: number;
|
|
78
|
+
resolvedAt: number | null;
|
|
79
|
+
}
|
|
28
80
|
export interface CachedThread {
|
|
29
81
|
index: number;
|
|
30
82
|
threadId: string;
|
|
@@ -332,6 +384,7 @@ export interface RuntimeStatus {
|
|
|
332
384
|
currentBindings: number;
|
|
333
385
|
pendingApprovals: number;
|
|
334
386
|
pendingUserInputs: number;
|
|
387
|
+
queuedTurns: number;
|
|
335
388
|
activeTurns: number;
|
|
336
389
|
lastError: string | null;
|
|
337
390
|
updatedAt: string;
|
package/dist/update.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface SelfUpdateStatus {
|
|
|
7
7
|
locale: AppLocale;
|
|
8
8
|
fromVersion: string;
|
|
9
9
|
toVersion: string | null;
|
|
10
|
+
releaseNotes?: string[] | null;
|
|
11
|
+
releaseNotesVersion?: string | null;
|
|
10
12
|
codexUpdate?: string | null;
|
|
11
13
|
codexFromVersion?: string | null;
|
|
12
14
|
codexToVersion?: string | null;
|
|
@@ -74,4 +76,5 @@ export declare function buildSelfUpdateLaunchCommand(options: {
|
|
|
74
76
|
unitName?: string;
|
|
75
77
|
}): SelfUpdateLaunchCommand;
|
|
76
78
|
export declare function performSelfUpdate(options: PerformSelfUpdateOptions): SelfUpdateOutcome;
|
|
79
|
+
export declare function extractReleaseNotes(changelog: string, version: string, locale: AppLocale): string[] | null;
|
|
77
80
|
export {};
|
package/dist/update.js
CHANGED
|
@@ -132,6 +132,10 @@ export function readSelfUpdateStatus(statusFile) {
|
|
|
132
132
|
locale: parsed.locale,
|
|
133
133
|
fromVersion: parsed.fromVersion,
|
|
134
134
|
toVersion: typeof parsed.toVersion === 'string' ? parsed.toVersion : null,
|
|
135
|
+
...(Array.isArray(parsed.releaseNotes) ? {
|
|
136
|
+
releaseNotes: parsed.releaseNotes.filter((entry) => typeof entry === 'string'),
|
|
137
|
+
} : {}),
|
|
138
|
+
...(typeof parsed.releaseNotesVersion === 'string' ? { releaseNotesVersion: parsed.releaseNotesVersion } : {}),
|
|
135
139
|
...(typeof parsed.codexUpdate === 'string' ? { codexUpdate: parsed.codexUpdate } : {}),
|
|
136
140
|
...(typeof parsed.codexFromVersion === 'string' ? { codexFromVersion: parsed.codexFromVersion } : {}),
|
|
137
141
|
...(typeof parsed.codexToVersion === 'string' ? { codexToVersion: parsed.codexToVersion } : {}),
|
|
@@ -185,6 +189,8 @@ export function createSelfUpdateRuntime(options) {
|
|
|
185
189
|
locale,
|
|
186
190
|
fromVersion: options.version,
|
|
187
191
|
toVersion: null,
|
|
192
|
+
releaseNotes: null,
|
|
193
|
+
releaseNotesVersion: null,
|
|
188
194
|
codexUpdate: null,
|
|
189
195
|
codexFromVersion: null,
|
|
190
196
|
codexToVersion: null,
|
|
@@ -229,6 +235,8 @@ export function createSelfUpdateRuntime(options) {
|
|
|
229
235
|
locale,
|
|
230
236
|
fromVersion: options.version,
|
|
231
237
|
toVersion: null,
|
|
238
|
+
releaseNotes: null,
|
|
239
|
+
releaseNotesVersion: null,
|
|
232
240
|
codexUpdate: null,
|
|
233
241
|
codexFromVersion: null,
|
|
234
242
|
codexToVersion: null,
|
|
@@ -296,9 +304,10 @@ export function performSelfUpdate(options) {
|
|
|
296
304
|
runInherited(installer.command, installer.installArgs, installerEnv);
|
|
297
305
|
const updatedEntryPoint = resolveUpdatedEntryPoint(installer, installerEnv);
|
|
298
306
|
toVersion = readInstalledPackageVersion(updatedEntryPoint);
|
|
307
|
+
const releaseNotes = readInstalledReleaseNotes(updatedEntryPoint, toVersion, options.notificationFile);
|
|
299
308
|
console.log('[UPDATE] Running checks and restarting the FoxClaw service...');
|
|
300
309
|
runInherited(options.nodePath, [updatedEntryPoint, 'start'], installerEnv);
|
|
301
|
-
completeNotification(options.notificationFile, 'succeeded', toVersion, codexUpdate, null);
|
|
310
|
+
completeNotification(options.notificationFile, 'succeeded', toVersion, codexUpdate, null, releaseNotes);
|
|
302
311
|
console.log(`[OK] FoxClaw updated and restarted: ${options.version} -> ${toVersion}`);
|
|
303
312
|
return {
|
|
304
313
|
ok: true,
|
|
@@ -309,7 +318,7 @@ export function performSelfUpdate(options) {
|
|
|
309
318
|
}
|
|
310
319
|
catch (error) {
|
|
311
320
|
const message = formatError(error);
|
|
312
|
-
completeNotification(options.notificationFile, 'failed', toVersion, codexUpdate, message);
|
|
321
|
+
completeNotification(options.notificationFile, 'failed', toVersion, codexUpdate, message, null);
|
|
313
322
|
console.error(`[FAIL] FoxClaw update failed: ${message}`);
|
|
314
323
|
return {
|
|
315
324
|
ok: false,
|
|
@@ -456,7 +465,65 @@ function readInstalledPackageVersion(updatedEntryPoint) {
|
|
|
456
465
|
return 'unknown';
|
|
457
466
|
}
|
|
458
467
|
}
|
|
459
|
-
function
|
|
468
|
+
function readInstalledReleaseNotes(updatedEntryPoint, version, notificationFile) {
|
|
469
|
+
if (!version || version === 'unknown') {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
const pending = notificationFile ? readSelfUpdateStatus(notificationFile) : null;
|
|
473
|
+
const locale = pending?.locale ?? 'zh';
|
|
474
|
+
const changelogPath = path.resolve(path.dirname(updatedEntryPoint), '..', 'CHANGELOG.md');
|
|
475
|
+
try {
|
|
476
|
+
return extractReleaseNotes(fs.readFileSync(changelogPath, 'utf8'), version, locale);
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
export function extractReleaseNotes(changelog, version, locale) {
|
|
483
|
+
const escapedVersion = escapeRegExp(version.replace(/^v/i, ''));
|
|
484
|
+
const versionHeadingPattern = new RegExp(`^##\\s+\\[?v?${escapedVersion}\\]?\\b.*$`, 'im');
|
|
485
|
+
const versionMatch = versionHeadingPattern.exec(changelog);
|
|
486
|
+
if (!versionMatch || versionMatch.index === undefined) {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
const sectionStart = versionMatch.index + versionMatch[0].length;
|
|
490
|
+
const nextHeadingMatch = /^##\s+/m.exec(changelog.slice(sectionStart));
|
|
491
|
+
const versionSection = nextHeadingMatch
|
|
492
|
+
? changelog.slice(sectionStart, sectionStart + nextHeadingMatch.index)
|
|
493
|
+
: changelog.slice(sectionStart);
|
|
494
|
+
const localizedSection = extractLocalizedReleaseNoteSection(versionSection, locale) ?? versionSection;
|
|
495
|
+
const bullets = localizedSection
|
|
496
|
+
.split(/\r?\n/)
|
|
497
|
+
.map(line => line.trim())
|
|
498
|
+
.filter(line => /^[-*]\s+/.test(line))
|
|
499
|
+
.map(line => line.replace(/^[-*]\s+/, '').trim())
|
|
500
|
+
.filter(Boolean)
|
|
501
|
+
.slice(0, 8);
|
|
502
|
+
return bullets.length > 0 ? bullets : null;
|
|
503
|
+
}
|
|
504
|
+
function extractLocalizedReleaseNoteSection(section, locale) {
|
|
505
|
+
const headingPattern = /^###\s+(.+)$/gm;
|
|
506
|
+
const headings = [...section.matchAll(headingPattern)];
|
|
507
|
+
if (headings.length === 0) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
const preferred = locale === 'zh' ? ['中文', 'Chinese'] : ['English', '英文'];
|
|
511
|
+
for (let index = 0; index < headings.length; index += 1) {
|
|
512
|
+
const heading = headings[index];
|
|
513
|
+
const headingText = heading[1].trim().toLowerCase();
|
|
514
|
+
if (!preferred.some(label => headingText === label.toLowerCase())) {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const start = heading.index + heading[0].length;
|
|
518
|
+
const next = headings[index + 1];
|
|
519
|
+
return next ? section.slice(start, next.index) : section.slice(start);
|
|
520
|
+
}
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
function escapeRegExp(value) {
|
|
524
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
525
|
+
}
|
|
526
|
+
function completeNotification(notificationFile, state, toVersion, codexUpdate, error, releaseNotes) {
|
|
460
527
|
if (!notificationFile) {
|
|
461
528
|
return;
|
|
462
529
|
}
|
|
@@ -468,6 +535,8 @@ function completeNotification(notificationFile, state, toVersion, codexUpdate, e
|
|
|
468
535
|
...pending,
|
|
469
536
|
state,
|
|
470
537
|
toVersion,
|
|
538
|
+
releaseNotes,
|
|
539
|
+
releaseNotesVersion: releaseNotes && toVersion ? toVersion : null,
|
|
471
540
|
codexUpdate: codexUpdate?.message ?? null,
|
|
472
541
|
codexFromVersion: codexUpdate?.fromVersion ?? null,
|
|
473
542
|
codexToVersion: codexUpdate?.toVersion ?? null,
|