@clawchatsai/connector 0.0.68 → 0.0.70
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/dist/index.js +1 -1
- package/package.json +1 -1
- package/server.js +138 -22
package/dist/index.js
CHANGED
|
@@ -417,7 +417,7 @@ function normalizeGatewayPayload(raw) {
|
|
|
417
417
|
const sk = parsed.params.sessionKey || '';
|
|
418
418
|
if (sk.includes(':chat:') && !_hintedSessions.has(sk)) {
|
|
419
419
|
_hintedSessions.add(sk);
|
|
420
|
-
parsed.params.message += '\n[ClawChats:
|
|
420
|
+
parsed.params.message += '\n[ClawChats: after using Write tool, run exec with: echo "MEDIA:/path/to/file" to surface it in chat]';
|
|
421
421
|
console.log(`[clawchats] capability-note injected for session ${sk}`);
|
|
422
422
|
return JSON.stringify(parsed);
|
|
423
423
|
}
|
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) {
|
|
@@ -861,7 +914,8 @@ function handleDeleteWorkspace(req, res, params) {
|
|
|
861
914
|
try { fs.unlinkSync(dbPath + '-shm'); } catch { /* ok */ }
|
|
862
915
|
|
|
863
916
|
// Clean all gateway sessions for this workspace
|
|
864
|
-
const
|
|
917
|
+
const wsAgentForDelete = ws.workspaces[params.name]?.agent || 'main';
|
|
918
|
+
const cleaned = cleanGatewaySessionsByPrefix(`agent:${wsAgentForDelete}:${params.name}:chat:`);
|
|
865
919
|
if (cleaned > 0) {
|
|
866
920
|
console.log(`Cleaned ${cleaned} gateway sessions for workspace: ${params.name}`);
|
|
867
921
|
}
|
|
@@ -1004,7 +1058,8 @@ async function handleCreateThread(req, res) {
|
|
|
1004
1058
|
const ws = getWorkspaces();
|
|
1005
1059
|
const id = body.id || uuid();
|
|
1006
1060
|
const now = Date.now();
|
|
1007
|
-
const
|
|
1061
|
+
const workspaceAgent = ws.workspaces[ws.active]?.agent || 'main';
|
|
1062
|
+
const sessionKey = `agent:${workspaceAgent}:${ws.active}:chat:${id}`;
|
|
1008
1063
|
|
|
1009
1064
|
try {
|
|
1010
1065
|
db.prepare(
|
|
@@ -1065,9 +1120,11 @@ function handleDeleteThread(req, res, params) {
|
|
|
1065
1120
|
|
|
1066
1121
|
// Look up sessionId from SQLite or sessions.json as fallback
|
|
1067
1122
|
let sessionIdToDelete = thread.last_session_id;
|
|
1123
|
+
const threadAgentMatch = (thread.session_key || '').match(/^agent:([^:]+):/);
|
|
1124
|
+
const threadSessionsDir = getSessionsDirForAgent(threadAgentMatch?.[1]);
|
|
1068
1125
|
if (!sessionIdToDelete) {
|
|
1069
1126
|
try {
|
|
1070
|
-
const raw = fs.readFileSync(path.join(
|
|
1127
|
+
const raw = fs.readFileSync(path.join(threadSessionsDir, 'sessions.json'), 'utf8');
|
|
1071
1128
|
const store = JSON.parse(raw);
|
|
1072
1129
|
const entry = store[thread.session_key];
|
|
1073
1130
|
if (entry?.sessionId) sessionIdToDelete = entry.sessionId;
|
|
@@ -1079,7 +1136,7 @@ function handleDeleteThread(req, res, params) {
|
|
|
1079
1136
|
|
|
1080
1137
|
// If cleanGatewaySession didn't find it but we have a sessionId, delete transcript directly
|
|
1081
1138
|
if (sessionIdToDelete) {
|
|
1082
|
-
const jsonlPath = path.join(
|
|
1139
|
+
const jsonlPath = path.join(threadSessionsDir, `${sessionIdToDelete}.jsonl`);
|
|
1083
1140
|
try { fs.unlinkSync(jsonlPath); } catch { /* ok */ }
|
|
1084
1141
|
}
|
|
1085
1142
|
|
|
@@ -1205,7 +1262,7 @@ function handleContextFill(req, res, params) {
|
|
|
1205
1262
|
const thread = db.prepare('SELECT * FROM threads WHERE id = ?').get(params.id);
|
|
1206
1263
|
if (!thread) return sendError(res, 404, 'Thread not found');
|
|
1207
1264
|
|
|
1208
|
-
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id);
|
|
1265
|
+
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id, thread.session_key);
|
|
1209
1266
|
send(res, 200, { preamble, method });
|
|
1210
1267
|
}
|
|
1211
1268
|
|
|
@@ -1300,6 +1357,7 @@ async function handleImport(req, res) {
|
|
|
1300
1357
|
const importAll = db.transaction(() => {
|
|
1301
1358
|
for (const t of body.threads) {
|
|
1302
1359
|
if (!t.id) continue;
|
|
1360
|
+
// TODO: per-project agent — import uses agent:main for now
|
|
1303
1361
|
const sessionKey = t.session_key || `agent:main:${ws.active}:chat:${t.id}`;
|
|
1304
1362
|
const result = insertThread.run(
|
|
1305
1363
|
t.id, sessionKey, t.title || 'Imported chat',
|
|
@@ -2409,6 +2467,19 @@ async function _handleRequestImpl(req, res) {
|
|
|
2409
2467
|
return await handleTranscribe(req, res);
|
|
2410
2468
|
}
|
|
2411
2469
|
|
|
2470
|
+
// --- Agents ---
|
|
2471
|
+
if (method === 'GET' && urlPath === '/api/agents') {
|
|
2472
|
+
try {
|
|
2473
|
+
const agentsDir = path.join(HOME, '.openclaw', 'agents');
|
|
2474
|
+
const agents = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
2475
|
+
.filter(e => e.isDirectory())
|
|
2476
|
+
.map(e => e.name);
|
|
2477
|
+
return send(res, 200, { agents });
|
|
2478
|
+
} catch {
|
|
2479
|
+
return send(res, 200, { agents: ['main'] });
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2412
2483
|
// --- Workspaces ---
|
|
2413
2484
|
if (method === 'GET' && urlPath === '/api/workspaces') {
|
|
2414
2485
|
return handleGetWorkspaces(req, res);
|
|
@@ -3199,9 +3270,9 @@ function syncThreadUnreadCount(db, threadId) {
|
|
|
3199
3270
|
// Helper: Parse session key
|
|
3200
3271
|
function parseSessionKey(sessionKey) {
|
|
3201
3272
|
if (!sessionKey) return null;
|
|
3202
|
-
const match = sessionKey.match(/^agent:
|
|
3273
|
+
const match = sessionKey.match(/^agent:([^:]+):([^:]+):chat:([^:]+)$/);
|
|
3203
3274
|
if (!match) return null; // Non-ClawChats keys — silently ignore
|
|
3204
|
-
return {
|
|
3275
|
+
return { agent: match[1], workspace: match[2], threadId: match[3] };
|
|
3205
3276
|
}
|
|
3206
3277
|
|
|
3207
3278
|
// Helper: Extract content from message
|
|
@@ -3541,7 +3612,9 @@ export function createApp(config = {}) {
|
|
|
3541
3612
|
}
|
|
3542
3613
|
const ws = _getWorkspaces();
|
|
3543
3614
|
if (ws.workspaces[name]) return sendError(res, 409, 'Workspace already exists');
|
|
3544
|
-
|
|
3615
|
+
let agent = 'main';
|
|
3616
|
+
try { agent = validateAgent(body.agent || 'main'); } catch { agent = 'main'; }
|
|
3617
|
+
const workspace = { name, label: label || name, color: body.color || null, icon: body.icon || null, agent, createdAt: Date.now() };
|
|
3545
3618
|
ws.workspaces[name] = workspace;
|
|
3546
3619
|
_setWorkspaces(ws);
|
|
3547
3620
|
_getDb(name);
|
|
@@ -3556,8 +3629,38 @@ export function createApp(config = {}) {
|
|
|
3556
3629
|
if (body.color !== undefined) ws.workspaces[params.name].color = body.color;
|
|
3557
3630
|
if (body.icon !== undefined) ws.workspaces[params.name].icon = body.icon;
|
|
3558
3631
|
if (body.lastThread !== undefined) ws.workspaces[params.name].lastThread = body.lastThread;
|
|
3632
|
+
let migratedThreads = 0;
|
|
3633
|
+
if (body.agent !== undefined) {
|
|
3634
|
+
let newAgent;
|
|
3635
|
+
try { newAgent = validateAgent(body.agent); } catch (e) { return sendError(res, 400, e.message); }
|
|
3636
|
+
const oldAgent = ws.workspaces[params.name].agent || 'main';
|
|
3637
|
+
if (newAgent !== oldAgent) {
|
|
3638
|
+
const db = _getDb(params.name);
|
|
3639
|
+
const threads = db.prepare(
|
|
3640
|
+
`SELECT id, session_key FROM threads WHERE session_key LIKE ?`
|
|
3641
|
+
).all(`agent:${oldAgent}:${params.name}:chat:%`);
|
|
3642
|
+
db.prepare(`
|
|
3643
|
+
UPDATE threads
|
|
3644
|
+
SET session_key = replace(
|
|
3645
|
+
session_key,
|
|
3646
|
+
'agent:' || ? || ':' || ? || ':chat:',
|
|
3647
|
+
'agent:' || ? || ':' || ? || ':chat:'
|
|
3648
|
+
)
|
|
3649
|
+
WHERE session_key LIKE 'agent:' || ? || ':' || ? || ':chat:%'
|
|
3650
|
+
`).run(oldAgent, params.name, newAgent, params.name, oldAgent, params.name);
|
|
3651
|
+
for (const t of threads) cleanGatewaySession(t.session_key);
|
|
3652
|
+
ws.workspaces[params.name].agent = newAgent;
|
|
3653
|
+
migratedThreads = threads.length;
|
|
3654
|
+
_gatewayClient.broadcastToBrowsers(JSON.stringify({
|
|
3655
|
+
type: 'clawchats',
|
|
3656
|
+
event: 'workspace-agent-changed',
|
|
3657
|
+
workspace: params.name,
|
|
3658
|
+
agent: newAgent
|
|
3659
|
+
}));
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3559
3662
|
_setWorkspaces(ws);
|
|
3560
|
-
send(res, 200, { workspace: ws.workspaces[params.name] });
|
|
3663
|
+
send(res, 200, { workspace: ws.workspaces[params.name], migratedThreads });
|
|
3561
3664
|
}
|
|
3562
3665
|
|
|
3563
3666
|
function _handleDeleteWorkspace(req, res, params) {
|
|
@@ -3570,7 +3673,8 @@ export function createApp(config = {}) {
|
|
|
3570
3673
|
try { fs.unlinkSync(dbPath); } catch { /* ok */ }
|
|
3571
3674
|
try { fs.unlinkSync(dbPath + '-wal'); } catch { /* ok */ }
|
|
3572
3675
|
try { fs.unlinkSync(dbPath + '-shm'); } catch { /* ok */ }
|
|
3573
|
-
const
|
|
3676
|
+
const wsAgentForDelete = ws.workspaces[params.name]?.agent || 'main';
|
|
3677
|
+
const cleaned = cleanGatewaySessionsByPrefix(`agent:${wsAgentForDelete}:${params.name}:chat:`);
|
|
3574
3678
|
if (cleaned > 0) console.log(`Cleaned ${cleaned} gateway sessions for workspace: ${params.name}`);
|
|
3575
3679
|
delete ws.workspaces[params.name];
|
|
3576
3680
|
_setWorkspaces(ws);
|
|
@@ -3664,7 +3768,8 @@ export function createApp(config = {}) {
|
|
|
3664
3768
|
const ws = _getWorkspaces();
|
|
3665
3769
|
const id = body.id || uuid();
|
|
3666
3770
|
const now = Date.now();
|
|
3667
|
-
const
|
|
3771
|
+
const workspaceAgent = ws.workspaces[ws.active]?.agent || 'main';
|
|
3772
|
+
const sessionKey = `agent:${workspaceAgent}:${ws.active}:chat:${id}`;
|
|
3668
3773
|
try {
|
|
3669
3774
|
db.prepare('INSERT INTO threads (id, session_key, title, created_at, updated_at) VALUES (?, ?, ?, ?, ?)').run(id, sessionKey, 'New chat', now, now);
|
|
3670
3775
|
} catch (e) {
|
|
@@ -3709,9 +3814,11 @@ export function createApp(config = {}) {
|
|
|
3709
3814
|
if (!thread) return sendError(res, 404, 'Thread not found');
|
|
3710
3815
|
db.prepare('DELETE FROM threads WHERE id = ?').run(params.id);
|
|
3711
3816
|
let sessionIdToDelete = thread.last_session_id;
|
|
3817
|
+
const tAgentMatch = (thread.session_key || '').match(/^agent:([^:]+):/);
|
|
3818
|
+
const tSessionsDir = getSessionsDirForAgent(tAgentMatch?.[1]);
|
|
3712
3819
|
if (!sessionIdToDelete) {
|
|
3713
3820
|
try {
|
|
3714
|
-
const raw = fs.readFileSync(path.join(
|
|
3821
|
+
const raw = fs.readFileSync(path.join(tSessionsDir, 'sessions.json'), 'utf8');
|
|
3715
3822
|
const store = JSON.parse(raw);
|
|
3716
3823
|
const entry = store[thread.session_key];
|
|
3717
3824
|
if (entry?.sessionId) sessionIdToDelete = entry.sessionId;
|
|
@@ -3719,7 +3826,7 @@ export function createApp(config = {}) {
|
|
|
3719
3826
|
}
|
|
3720
3827
|
cleanGatewaySession(thread.session_key);
|
|
3721
3828
|
if (sessionIdToDelete) {
|
|
3722
|
-
const jsonlPath = path.join(
|
|
3829
|
+
const jsonlPath = path.join(tSessionsDir, `${sessionIdToDelete}.jsonl`);
|
|
3723
3830
|
try { fs.unlinkSync(jsonlPath); } catch { /* ok */ }
|
|
3724
3831
|
}
|
|
3725
3832
|
const uploadDir = path.join(_UPLOADS_DIR, params.id);
|
|
@@ -3810,7 +3917,7 @@ export function createApp(config = {}) {
|
|
|
3810
3917
|
const db = _getActiveDb();
|
|
3811
3918
|
const thread = db.prepare('SELECT * FROM threads WHERE id = ?').get(params.id);
|
|
3812
3919
|
if (!thread) return sendError(res, 404, 'Thread not found');
|
|
3813
|
-
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id);
|
|
3920
|
+
const { preamble, method } = buildContextPreamble(db, params.id, thread.last_session_id, thread.session_key);
|
|
3814
3921
|
send(res, 200, { preamble, method });
|
|
3815
3922
|
}
|
|
3816
3923
|
|
|
@@ -3862,6 +3969,7 @@ export function createApp(config = {}) {
|
|
|
3862
3969
|
const importAll = db.transaction(() => {
|
|
3863
3970
|
for (const t of body.threads) {
|
|
3864
3971
|
if (!t.id) continue;
|
|
3972
|
+
// TODO: per-project agent — import uses agent:main for now
|
|
3865
3973
|
const sessionKey = t.session_key || `agent:main:${ws.active}:chat:${t.id}`;
|
|
3866
3974
|
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
3975
|
if (result.changes > 0) threadsImported++;
|
|
@@ -4612,6 +4720,14 @@ export function createApp(config = {}) {
|
|
|
4612
4720
|
if (method === 'GET' && urlPath === '/api/settings') return _handleGetSettings(req, res);
|
|
4613
4721
|
if (method === 'PUT' && urlPath === '/api/settings') return await _handleSaveSettings(req, res);
|
|
4614
4722
|
if (method === 'POST' && urlPath === '/api/transcribe') return await handleTranscribe(req, res);
|
|
4723
|
+
if (method === 'GET' && urlPath === '/api/agents') {
|
|
4724
|
+
try {
|
|
4725
|
+
const agentsDir = path.join(HOME, '.openclaw', 'agents');
|
|
4726
|
+
const agents = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
4727
|
+
.filter(e => e.isDirectory()).map(e => e.name);
|
|
4728
|
+
return send(res, 200, { agents });
|
|
4729
|
+
} catch { return send(res, 200, { agents: ['main'] }); }
|
|
4730
|
+
}
|
|
4615
4731
|
if (method === 'GET' && urlPath === '/api/workspaces') return _handleGetWorkspaces(req, res);
|
|
4616
4732
|
if (method === 'POST' && urlPath === '/api/workspaces') return await _handleCreateWorkspace(req, res);
|
|
4617
4733
|
if ((p = matchRoute(method, urlPath, 'PATCH /api/workspaces/:name'))) return await _handleUpdateWorkspace(req, res, p);
|