@clawchatsai/connector 0.0.69 → 0.0.71
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/package.json +1 -1
- package/server.js +145 -26
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -277,6 +277,21 @@ const OPENCLAW_SESSIONS_DIR = (() => {
|
|
|
277
277
|
return fallback;
|
|
278
278
|
})();
|
|
279
279
|
|
|
280
|
+
// ─── Agent Helpers ───────────────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
function getSessionsDirForAgent(agentId) {
|
|
283
|
+
if (!agentId || agentId === 'main') return OPENCLAW_SESSIONS_DIR;
|
|
284
|
+
return path.join(HOME, '.openclaw', 'agents', agentId, 'sessions');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function validateAgent(agentId) {
|
|
288
|
+
if (!agentId) return 'main';
|
|
289
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(agentId)) throw new Error('Invalid agent ID');
|
|
290
|
+
const agentDir = path.join(HOME, '.openclaw', 'agents', agentId);
|
|
291
|
+
if (!fs.existsSync(agentDir)) throw new Error(`Agent not found: ${agentId}`);
|
|
292
|
+
return agentId;
|
|
293
|
+
}
|
|
294
|
+
|
|
280
295
|
// ─── Workspace Management ───────────────────────────────────────────────────
|
|
281
296
|
|
|
282
297
|
function ensureDirs() {
|
|
@@ -619,13 +634,15 @@ function parseMultipart(req) {
|
|
|
619
634
|
|
|
620
635
|
// ─── Context Fill Helper ────────────────────────────────────────────────────
|
|
621
636
|
|
|
622
|
-
function buildContextPreamble(db, threadId, lastSessionId) {
|
|
637
|
+
function buildContextPreamble(db, threadId, lastSessionId, sessionKey) {
|
|
623
638
|
let summary = null;
|
|
624
639
|
let method = 'raw';
|
|
625
640
|
|
|
626
641
|
// Try to read old JSONL transcript
|
|
627
642
|
if (lastSessionId) {
|
|
628
|
-
const
|
|
643
|
+
const agentMatch = (sessionKey || '').match(/^agent:([^:]+):/);
|
|
644
|
+
const sessionsDir = getSessionsDirForAgent(agentMatch?.[1]);
|
|
645
|
+
const jsonlPath = path.join(sessionsDir, `${lastSessionId}.jsonl`);
|
|
629
646
|
try {
|
|
630
647
|
const content = fs.readFileSync(jsonlPath, 'utf8');
|
|
631
648
|
const lines = content.split('\n').filter(Boolean);
|
|
@@ -696,7 +713,9 @@ function buildContextPreamble(db, threadId, lastSessionId) {
|
|
|
696
713
|
|
|
697
714
|
function cleanGatewaySession(sessionKey) {
|
|
698
715
|
try {
|
|
699
|
-
const
|
|
716
|
+
const agentMatch = (sessionKey || '').match(/^agent:([^:]+):/);
|
|
717
|
+
const sessionsDir = getSessionsDirForAgent(agentMatch?.[1]);
|
|
718
|
+
const sessionsPath = path.join(sessionsDir, 'sessions.json');
|
|
700
719
|
const raw = fs.readFileSync(sessionsPath, 'utf8');
|
|
701
720
|
const store = JSON.parse(raw);
|
|
702
721
|
const entry = store[sessionKey];
|
|
@@ -704,7 +723,7 @@ function cleanGatewaySession(sessionKey) {
|
|
|
704
723
|
|
|
705
724
|
// Delete .jsonl transcript
|
|
706
725
|
if (entry.sessionId) {
|
|
707
|
-
const jsonlPath = path.join(
|
|
726
|
+
const jsonlPath = path.join(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
708
727
|
try { fs.unlinkSync(jsonlPath); } catch { /* ok */ }
|
|
709
728
|
}
|
|
710
729
|
|
|
@@ -721,7 +740,9 @@ function cleanGatewaySession(sessionKey) {
|
|
|
721
740
|
|
|
722
741
|
function cleanGatewaySessionsByPrefix(prefix) {
|
|
723
742
|
try {
|
|
724
|
-
const
|
|
743
|
+
const agentMatch = (prefix || '').match(/^agent:([^:]+):/);
|
|
744
|
+
const sessionsDir = getSessionsDirForAgent(agentMatch?.[1]);
|
|
745
|
+
const sessionsPath = path.join(sessionsDir, 'sessions.json');
|
|
725
746
|
const raw = fs.readFileSync(sessionsPath, 'utf8');
|
|
726
747
|
const store = JSON.parse(raw);
|
|
727
748
|
let cleaned = 0;
|
|
@@ -730,7 +751,7 @@ function cleanGatewaySessionsByPrefix(prefix) {
|
|
|
730
751
|
if (!key.startsWith(prefix)) continue;
|
|
731
752
|
const entry = store[key];
|
|
732
753
|
if (entry?.sessionId) {
|
|
733
|
-
const jsonlPath = path.join(
|
|
754
|
+
const jsonlPath = path.join(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
734
755
|
try { fs.unlinkSync(jsonlPath); } catch { /* ok */ }
|
|
735
756
|
}
|
|
736
757
|
delete store[key];
|
|
@@ -811,7 +832,9 @@ async function handleCreateWorkspace(req, res) {
|
|
|
811
832
|
if (ws.workspaces[name]) {
|
|
812
833
|
return sendError(res, 409, 'Workspace already exists');
|
|
813
834
|
}
|
|
814
|
-
|
|
835
|
+
let agent = 'main';
|
|
836
|
+
try { agent = validateAgent(body.agent || 'main'); } catch { agent = 'main'; }
|
|
837
|
+
const workspace = { name, label: label || name, color: body.color || null, icon: body.icon || null, agent, createdAt: Date.now() };
|
|
815
838
|
ws.workspaces[name] = workspace;
|
|
816
839
|
setWorkspaces(ws);
|
|
817
840
|
// Initialize DB
|
|
@@ -837,8 +860,38 @@ async function handleUpdateWorkspace(req, res, params) {
|
|
|
837
860
|
if (body.lastThread !== undefined) {
|
|
838
861
|
ws.workspaces[params.name].lastThread = body.lastThread;
|
|
839
862
|
}
|
|
863
|
+
let migratedThreads = 0;
|
|
864
|
+
if (body.agent !== undefined) {
|
|
865
|
+
let newAgent;
|
|
866
|
+
try { newAgent = validateAgent(body.agent); } catch (e) { return sendError(res, 400, e.message); }
|
|
867
|
+
const oldAgent = ws.workspaces[params.name].agent || 'main';
|
|
868
|
+
if (newAgent !== oldAgent) {
|
|
869
|
+
const db = getDb(params.name);
|
|
870
|
+
const threads = db.prepare(
|
|
871
|
+
`SELECT id, session_key FROM threads WHERE session_key LIKE ?`
|
|
872
|
+
).all(`agent:${oldAgent}:${params.name}:chat:%`);
|
|
873
|
+
db.prepare(`
|
|
874
|
+
UPDATE threads
|
|
875
|
+
SET session_key = replace(
|
|
876
|
+
session_key,
|
|
877
|
+
'agent:' || ? || ':' || ? || ':chat:',
|
|
878
|
+
'agent:' || ? || ':' || ? || ':chat:'
|
|
879
|
+
)
|
|
880
|
+
WHERE session_key LIKE 'agent:' || ? || ':' || ? || ':chat:%'
|
|
881
|
+
`).run(oldAgent, params.name, newAgent, params.name, oldAgent, params.name);
|
|
882
|
+
for (const t of threads) cleanGatewaySession(t.session_key);
|
|
883
|
+
ws.workspaces[params.name].agent = newAgent;
|
|
884
|
+
migratedThreads = threads.length;
|
|
885
|
+
gatewayClient.broadcastToBrowsers(JSON.stringify({
|
|
886
|
+
type: 'clawchats',
|
|
887
|
+
event: 'workspace-agent-changed',
|
|
888
|
+
workspace: params.name,
|
|
889
|
+
agent: newAgent
|
|
890
|
+
}));
|
|
891
|
+
}
|
|
892
|
+
}
|
|
840
893
|
setWorkspaces(ws);
|
|
841
|
-
send(res, 200, { workspace: ws.workspaces[params.name] });
|
|
894
|
+
send(res, 200, { workspace: ws.workspaces[params.name], migratedThreads });
|
|
842
895
|
}
|
|
843
896
|
|
|
844
897
|
function handleDeleteWorkspace(req, res, params) {
|
|
@@ -850,9 +903,6 @@ function handleDeleteWorkspace(req, res, params) {
|
|
|
850
903
|
if (Object.keys(ws.workspaces).length <= 1) {
|
|
851
904
|
return sendError(res, 400, 'Cannot delete the only workspace');
|
|
852
905
|
}
|
|
853
|
-
if (ws.active === params.name) {
|
|
854
|
-
return sendError(res, 400, 'Cannot delete the active workspace');
|
|
855
|
-
}
|
|
856
906
|
// Close and remove DB
|
|
857
907
|
closeDb(params.name);
|
|
858
908
|
const dbPath = path.join(DATA_DIR, `${params.name}.db`);
|
|
@@ -861,12 +911,19 @@ function handleDeleteWorkspace(req, res, params) {
|
|
|
861
911
|
try { fs.unlinkSync(dbPath + '-shm'); } catch { /* ok */ }
|
|
862
912
|
|
|
863
913
|
// Clean all gateway sessions for this workspace
|
|
864
|
-
const
|
|
914
|
+
const wsAgentForDelete = ws.workspaces[params.name]?.agent || 'main';
|
|
915
|
+
const cleaned = cleanGatewaySessionsByPrefix(`agent:${wsAgentForDelete}:${params.name}:chat:`);
|
|
865
916
|
if (cleaned > 0) {
|
|
866
917
|
console.log(`Cleaned ${cleaned} gateway sessions for workspace: ${params.name}`);
|
|
867
918
|
}
|
|
868
919
|
|
|
869
920
|
delete ws.workspaces[params.name];
|
|
921
|
+
|
|
922
|
+
// If this was the active workspace, switch active to the first remaining one
|
|
923
|
+
if (ws.active === params.name) {
|
|
924
|
+
ws.active = Object.keys(ws.workspaces)[0] || null;
|
|
925
|
+
}
|
|
926
|
+
|
|
870
927
|
setWorkspaces(ws);
|
|
871
928
|
send(res, 200, { ok: true });
|
|
872
929
|
}
|
|
@@ -1004,7 +1061,8 @@ async function handleCreateThread(req, res) {
|
|
|
1004
1061
|
const ws = getWorkspaces();
|
|
1005
1062
|
const id = body.id || uuid();
|
|
1006
1063
|
const now = Date.now();
|
|
1007
|
-
const
|
|
1064
|
+
const workspaceAgent = ws.workspaces[ws.active]?.agent || 'main';
|
|
1065
|
+
const sessionKey = `agent:${workspaceAgent}:${ws.active}:chat:${id}`;
|
|
1008
1066
|
|
|
1009
1067
|
try {
|
|
1010
1068
|
db.prepare(
|
|
@@ -1065,9 +1123,11 @@ function handleDeleteThread(req, res, params) {
|
|
|
1065
1123
|
|
|
1066
1124
|
// Look up sessionId from SQLite or sessions.json as fallback
|
|
1067
1125
|
let sessionIdToDelete = thread.last_session_id;
|
|
1126
|
+
const threadAgentMatch = (thread.session_key || '').match(/^agent:([^:]+):/);
|
|
1127
|
+
const threadSessionsDir = getSessionsDirForAgent(threadAgentMatch?.[1]);
|
|
1068
1128
|
if (!sessionIdToDelete) {
|
|
1069
1129
|
try {
|
|
1070
|
-
const raw = fs.readFileSync(path.join(
|
|
1130
|
+
const raw = fs.readFileSync(path.join(threadSessionsDir, 'sessions.json'), 'utf8');
|
|
1071
1131
|
const store = JSON.parse(raw);
|
|
1072
1132
|
const entry = store[thread.session_key];
|
|
1073
1133
|
if (entry?.sessionId) sessionIdToDelete = entry.sessionId;
|
|
@@ -1079,7 +1139,7 @@ function handleDeleteThread(req, res, params) {
|
|
|
1079
1139
|
|
|
1080
1140
|
// If cleanGatewaySession didn't find it but we have a sessionId, delete transcript directly
|
|
1081
1141
|
if (sessionIdToDelete) {
|
|
1082
|
-
const jsonlPath = path.join(
|
|
1142
|
+
const jsonlPath = path.join(threadSessionsDir, `${sessionIdToDelete}.jsonl`);
|
|
1083
1143
|
try { fs.unlinkSync(jsonlPath); } catch { /* ok */ }
|
|
1084
1144
|
}
|
|
1085
1145
|
|
|
@@ -1205,7 +1265,7 @@ function handleContextFill(req, res, params) {
|
|
|
1205
1265
|
const thread = db.prepare('SELECT * FROM threads WHERE id = ?').get(params.id);
|
|
1206
1266
|
if (!thread) return sendError(res, 404, 'Thread not found');
|
|
1207
1267
|
|
|
1208
|
-
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id);
|
|
1268
|
+
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id, thread.session_key);
|
|
1209
1269
|
send(res, 200, { preamble, method });
|
|
1210
1270
|
}
|
|
1211
1271
|
|
|
@@ -1300,6 +1360,7 @@ async function handleImport(req, res) {
|
|
|
1300
1360
|
const importAll = db.transaction(() => {
|
|
1301
1361
|
for (const t of body.threads) {
|
|
1302
1362
|
if (!t.id) continue;
|
|
1363
|
+
// TODO: per-project agent — import uses agent:main for now
|
|
1303
1364
|
const sessionKey = t.session_key || `agent:main:${ws.active}:chat:${t.id}`;
|
|
1304
1365
|
const result = insertThread.run(
|
|
1305
1366
|
t.id, sessionKey, t.title || 'Imported chat',
|
|
@@ -2409,6 +2470,19 @@ async function _handleRequestImpl(req, res) {
|
|
|
2409
2470
|
return await handleTranscribe(req, res);
|
|
2410
2471
|
}
|
|
2411
2472
|
|
|
2473
|
+
// --- Agents ---
|
|
2474
|
+
if (method === 'GET' && urlPath === '/api/agents') {
|
|
2475
|
+
try {
|
|
2476
|
+
const agentsDir = path.join(HOME, '.openclaw', 'agents');
|
|
2477
|
+
const agents = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
2478
|
+
.filter(e => e.isDirectory())
|
|
2479
|
+
.map(e => e.name);
|
|
2480
|
+
return send(res, 200, { agents });
|
|
2481
|
+
} catch {
|
|
2482
|
+
return send(res, 200, { agents: ['main'] });
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2412
2486
|
// --- Workspaces ---
|
|
2413
2487
|
if (method === 'GET' && urlPath === '/api/workspaces') {
|
|
2414
2488
|
return handleGetWorkspaces(req, res);
|
|
@@ -3199,9 +3273,9 @@ function syncThreadUnreadCount(db, threadId) {
|
|
|
3199
3273
|
// Helper: Parse session key
|
|
3200
3274
|
function parseSessionKey(sessionKey) {
|
|
3201
3275
|
if (!sessionKey) return null;
|
|
3202
|
-
const match = sessionKey.match(/^agent:
|
|
3276
|
+
const match = sessionKey.match(/^agent:([^:]+):([^:]+):chat:([^:]+)$/);
|
|
3203
3277
|
if (!match) return null; // Non-ClawChats keys — silently ignore
|
|
3204
|
-
return {
|
|
3278
|
+
return { agent: match[1], workspace: match[2], threadId: match[3] };
|
|
3205
3279
|
}
|
|
3206
3280
|
|
|
3207
3281
|
// Helper: Extract content from message
|
|
@@ -3541,7 +3615,9 @@ export function createApp(config = {}) {
|
|
|
3541
3615
|
}
|
|
3542
3616
|
const ws = _getWorkspaces();
|
|
3543
3617
|
if (ws.workspaces[name]) return sendError(res, 409, 'Workspace already exists');
|
|
3544
|
-
|
|
3618
|
+
let agent = 'main';
|
|
3619
|
+
try { agent = validateAgent(body.agent || 'main'); } catch { agent = 'main'; }
|
|
3620
|
+
const workspace = { name, label: label || name, color: body.color || null, icon: body.icon || null, agent, createdAt: Date.now() };
|
|
3545
3621
|
ws.workspaces[name] = workspace;
|
|
3546
3622
|
_setWorkspaces(ws);
|
|
3547
3623
|
_getDb(name);
|
|
@@ -3556,23 +3632,54 @@ export function createApp(config = {}) {
|
|
|
3556
3632
|
if (body.color !== undefined) ws.workspaces[params.name].color = body.color;
|
|
3557
3633
|
if (body.icon !== undefined) ws.workspaces[params.name].icon = body.icon;
|
|
3558
3634
|
if (body.lastThread !== undefined) ws.workspaces[params.name].lastThread = body.lastThread;
|
|
3635
|
+
let migratedThreads = 0;
|
|
3636
|
+
if (body.agent !== undefined) {
|
|
3637
|
+
let newAgent;
|
|
3638
|
+
try { newAgent = validateAgent(body.agent); } catch (e) { return sendError(res, 400, e.message); }
|
|
3639
|
+
const oldAgent = ws.workspaces[params.name].agent || 'main';
|
|
3640
|
+
if (newAgent !== oldAgent) {
|
|
3641
|
+
const db = _getDb(params.name);
|
|
3642
|
+
const threads = db.prepare(
|
|
3643
|
+
`SELECT id, session_key FROM threads WHERE session_key LIKE ?`
|
|
3644
|
+
).all(`agent:${oldAgent}:${params.name}:chat:%`);
|
|
3645
|
+
db.prepare(`
|
|
3646
|
+
UPDATE threads
|
|
3647
|
+
SET session_key = replace(
|
|
3648
|
+
session_key,
|
|
3649
|
+
'agent:' || ? || ':' || ? || ':chat:',
|
|
3650
|
+
'agent:' || ? || ':' || ? || ':chat:'
|
|
3651
|
+
)
|
|
3652
|
+
WHERE session_key LIKE 'agent:' || ? || ':' || ? || ':chat:%'
|
|
3653
|
+
`).run(oldAgent, params.name, newAgent, params.name, oldAgent, params.name);
|
|
3654
|
+
for (const t of threads) cleanGatewaySession(t.session_key);
|
|
3655
|
+
ws.workspaces[params.name].agent = newAgent;
|
|
3656
|
+
migratedThreads = threads.length;
|
|
3657
|
+
_gatewayClient.broadcastToBrowsers(JSON.stringify({
|
|
3658
|
+
type: 'clawchats',
|
|
3659
|
+
event: 'workspace-agent-changed',
|
|
3660
|
+
workspace: params.name,
|
|
3661
|
+
agent: newAgent
|
|
3662
|
+
}));
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3559
3665
|
_setWorkspaces(ws);
|
|
3560
|
-
send(res, 200, { workspace: ws.workspaces[params.name] });
|
|
3666
|
+
send(res, 200, { workspace: ws.workspaces[params.name], migratedThreads });
|
|
3561
3667
|
}
|
|
3562
3668
|
|
|
3563
3669
|
function _handleDeleteWorkspace(req, res, params) {
|
|
3564
3670
|
const ws = _getWorkspaces();
|
|
3565
3671
|
if (!ws.workspaces[params.name]) return sendError(res, 404, 'Workspace not found');
|
|
3566
3672
|
if (Object.keys(ws.workspaces).length <= 1) return sendError(res, 400, 'Cannot delete the only workspace');
|
|
3567
|
-
if (ws.active === params.name) return sendError(res, 400, 'Cannot delete the active workspace');
|
|
3568
3673
|
_closeDb(params.name);
|
|
3569
3674
|
const dbPath = path.join(_DATA_DIR, `${params.name}.db`);
|
|
3570
3675
|
try { fs.unlinkSync(dbPath); } catch { /* ok */ }
|
|
3571
3676
|
try { fs.unlinkSync(dbPath + '-wal'); } catch { /* ok */ }
|
|
3572
3677
|
try { fs.unlinkSync(dbPath + '-shm'); } catch { /* ok */ }
|
|
3573
|
-
const
|
|
3678
|
+
const wsAgentForDelete = ws.workspaces[params.name]?.agent || 'main';
|
|
3679
|
+
const cleaned = cleanGatewaySessionsByPrefix(`agent:${wsAgentForDelete}:${params.name}:chat:`);
|
|
3574
3680
|
if (cleaned > 0) console.log(`Cleaned ${cleaned} gateway sessions for workspace: ${params.name}`);
|
|
3575
3681
|
delete ws.workspaces[params.name];
|
|
3682
|
+
if (ws.active === params.name) ws.active = Object.keys(ws.workspaces)[0] || null;
|
|
3576
3683
|
_setWorkspaces(ws);
|
|
3577
3684
|
send(res, 200, { ok: true });
|
|
3578
3685
|
}
|
|
@@ -3664,7 +3771,8 @@ export function createApp(config = {}) {
|
|
|
3664
3771
|
const ws = _getWorkspaces();
|
|
3665
3772
|
const id = body.id || uuid();
|
|
3666
3773
|
const now = Date.now();
|
|
3667
|
-
const
|
|
3774
|
+
const workspaceAgent = ws.workspaces[ws.active]?.agent || 'main';
|
|
3775
|
+
const sessionKey = `agent:${workspaceAgent}:${ws.active}:chat:${id}`;
|
|
3668
3776
|
try {
|
|
3669
3777
|
db.prepare('INSERT INTO threads (id, session_key, title, created_at, updated_at) VALUES (?, ?, ?, ?, ?)').run(id, sessionKey, 'New chat', now, now);
|
|
3670
3778
|
} catch (e) {
|
|
@@ -3709,9 +3817,11 @@ export function createApp(config = {}) {
|
|
|
3709
3817
|
if (!thread) return sendError(res, 404, 'Thread not found');
|
|
3710
3818
|
db.prepare('DELETE FROM threads WHERE id = ?').run(params.id);
|
|
3711
3819
|
let sessionIdToDelete = thread.last_session_id;
|
|
3820
|
+
const tAgentMatch = (thread.session_key || '').match(/^agent:([^:]+):/);
|
|
3821
|
+
const tSessionsDir = getSessionsDirForAgent(tAgentMatch?.[1]);
|
|
3712
3822
|
if (!sessionIdToDelete) {
|
|
3713
3823
|
try {
|
|
3714
|
-
const raw = fs.readFileSync(path.join(
|
|
3824
|
+
const raw = fs.readFileSync(path.join(tSessionsDir, 'sessions.json'), 'utf8');
|
|
3715
3825
|
const store = JSON.parse(raw);
|
|
3716
3826
|
const entry = store[thread.session_key];
|
|
3717
3827
|
if (entry?.sessionId) sessionIdToDelete = entry.sessionId;
|
|
@@ -3719,7 +3829,7 @@ export function createApp(config = {}) {
|
|
|
3719
3829
|
}
|
|
3720
3830
|
cleanGatewaySession(thread.session_key);
|
|
3721
3831
|
if (sessionIdToDelete) {
|
|
3722
|
-
const jsonlPath = path.join(
|
|
3832
|
+
const jsonlPath = path.join(tSessionsDir, `${sessionIdToDelete}.jsonl`);
|
|
3723
3833
|
try { fs.unlinkSync(jsonlPath); } catch { /* ok */ }
|
|
3724
3834
|
}
|
|
3725
3835
|
const uploadDir = path.join(_UPLOADS_DIR, params.id);
|
|
@@ -3810,7 +3920,7 @@ export function createApp(config = {}) {
|
|
|
3810
3920
|
const db = _getActiveDb();
|
|
3811
3921
|
const thread = db.prepare('SELECT * FROM threads WHERE id = ?').get(params.id);
|
|
3812
3922
|
if (!thread) return sendError(res, 404, 'Thread not found');
|
|
3813
|
-
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id);
|
|
3923
|
+
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id, thread.session_key);
|
|
3814
3924
|
send(res, 200, { preamble, method });
|
|
3815
3925
|
}
|
|
3816
3926
|
|
|
@@ -3862,6 +3972,7 @@ export function createApp(config = {}) {
|
|
|
3862
3972
|
const importAll = db.transaction(() => {
|
|
3863
3973
|
for (const t of body.threads) {
|
|
3864
3974
|
if (!t.id) continue;
|
|
3975
|
+
// TODO: per-project agent — import uses agent:main for now
|
|
3865
3976
|
const sessionKey = t.session_key || `agent:main:${ws.active}:chat:${t.id}`;
|
|
3866
3977
|
const result = insertThread.run(t.id, sessionKey, t.title || 'Imported chat', t.pinned || 0, t.pin_order || 0, t.model || null, t.last_session_id || null, t.created_at || Date.now(), t.updated_at || Date.now());
|
|
3867
3978
|
if (result.changes > 0) threadsImported++;
|
|
@@ -4612,6 +4723,14 @@ export function createApp(config = {}) {
|
|
|
4612
4723
|
if (method === 'GET' && urlPath === '/api/settings') return _handleGetSettings(req, res);
|
|
4613
4724
|
if (method === 'PUT' && urlPath === '/api/settings') return await _handleSaveSettings(req, res);
|
|
4614
4725
|
if (method === 'POST' && urlPath === '/api/transcribe') return await handleTranscribe(req, res);
|
|
4726
|
+
if (method === 'GET' && urlPath === '/api/agents') {
|
|
4727
|
+
try {
|
|
4728
|
+
const agentsDir = path.join(HOME, '.openclaw', 'agents');
|
|
4729
|
+
const agents = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
4730
|
+
.filter(e => e.isDirectory()).map(e => e.name);
|
|
4731
|
+
return send(res, 200, { agents });
|
|
4732
|
+
} catch { return send(res, 200, { agents: ['main'] }); }
|
|
4733
|
+
}
|
|
4615
4734
|
if (method === 'GET' && urlPath === '/api/workspaces') return _handleGetWorkspaces(req, res);
|
|
4616
4735
|
if (method === 'POST' && urlPath === '/api/workspaces') return await _handleCreateWorkspace(req, res);
|
|
4617
4736
|
if ((p = matchRoute(method, urlPath, 'PATCH /api/workspaces/:name'))) return await _handleUpdateWorkspace(req, res, p);
|