@agent-spaces/server 0.1.2 → 0.2.3
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/adapters/claude-code-runtime/index.js +4 -1
- package/dist/adapters/git.js +2 -21
- package/dist/agents/issue-agent-runner.js +35 -21
- package/dist/agents/issue-task-controller.js +120 -73
- package/dist/agents/planner-agent.js +9 -12
- package/dist/app.js +12 -1
- package/dist/hooks/agent-hooks.js +5 -8
- package/dist/package.json +1 -1
- package/dist/routes/agent.js +40 -17
- package/dist/routes/channel.js +4 -1
- package/dist/routes/command.js +108 -0
- package/dist/routes/folder.js +44 -7
- package/dist/routes/issue.js +57 -23
- package/dist/routes/mcp.js +50 -0
- package/dist/routes/skill.js +57 -0
- package/dist/routes/subscription.js +49 -0
- package/dist/routes/task.js +16 -1
- package/dist/routes/workflow.js +63 -0
- package/dist/routes/workspace.js +2 -21
- package/dist/services/agent.js +140 -76
- package/dist/services/builtin-tools.js +72 -0
- package/dist/services/channel.js +5 -5
- package/dist/services/command-process-manager.js +136 -0
- package/dist/services/command.js +48 -0
- package/dist/services/issue.js +5 -6
- package/dist/services/mcp.js +120 -0
- package/dist/services/notification-hub/bot-commands.js +48 -16
- package/dist/services/notification-hub/helpers.js +12 -0
- package/dist/services/pty.js +10 -3
- package/dist/services/skill.js +171 -0
- package/dist/services/subscription/aicode.js +45 -0
- package/dist/services/subscription/base.js +3 -0
- package/dist/services/subscription/index.js +20 -0
- package/dist/services/subscription/minimax.js +60 -0
- package/dist/services/subscription/zhipu.js +39 -0
- package/dist/services/workflow.js +203 -0
- package/dist/services/workspace.js +0 -1
- package/dist/storage/command-store.js +17 -0
- package/dist/storage/subscription-store.js +44 -0
- package/dist/storage/workflow-store.js +40 -0
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +4 -4
- package/dist/web/__next._full.txt +22 -21
- package/dist/web/__next._head.txt +4 -4
- package/dist/web/__next._index.txt +9 -8
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/7UtFGbIZvN_2yiatRc2g3/_buildManifest.js +11 -0
- package/dist/web/_next/static/chunks/0-245kun-watp.js +2 -0
- package/dist/web/_next/static/chunks/0-ca_fo-yp3~z.js +1 -0
- package/dist/web/_next/static/chunks/0-vgd3j-zh3m8.js +2 -0
- package/dist/web/_next/static/chunks/0.4.g.8yf4rs0.js +3 -0
- package/dist/web/_next/static/chunks/02m_-ngl9w8co.js +1 -0
- package/dist/web/_next/static/chunks/03jbh7ud0jw~g.js +1 -0
- package/dist/web/_next/static/chunks/07kqxmubf5dua.js +1 -0
- package/dist/web/_next/static/chunks/088q-5_51dsrw.js +1 -0
- package/dist/web/_next/static/chunks/08fqgb~~a~4ck.js +1 -0
- package/dist/web/_next/static/chunks/09_ki3dc5cfwv.css +1 -0
- package/dist/web/_next/static/chunks/09jryrjps0vl2.js +1 -0
- package/dist/web/_next/static/chunks/0amzlgoqe50tv.js +8 -0
- package/dist/web/_next/static/chunks/0b2tump5duj9j.js +1 -0
- package/dist/web/_next/static/chunks/0car_w834cbb6.js +1 -0
- package/dist/web/_next/static/chunks/0fwvdy-ml8wpk.js +1 -0
- package/dist/web/_next/static/chunks/0g4vm6.v0o_lt.js +1 -0
- package/dist/web/_next/static/chunks/0g_b4t~000.o~.js +179 -0
- package/dist/web/_next/static/chunks/0gyede80jpy3n.js +4 -0
- package/dist/web/_next/static/chunks/0h256h-tu4e0r.js +2 -0
- package/dist/web/_next/static/chunks/0ib18ul605e~a.js +1 -0
- package/dist/web/_next/static/chunks/0j1g_rd9t5ot1.js +1 -0
- package/dist/web/_next/static/chunks/0jn~llaqcxo4q.js +1 -0
- package/dist/web/_next/static/chunks/0jvmviuftg5e2.css +1 -0
- package/dist/web/_next/static/chunks/0lb8l2~6s_owo.js +1 -0
- package/dist/web/_next/static/chunks/0o4m39hw4fb_j.js +1 -0
- package/dist/web/_next/static/chunks/0pep4mkvt3.rh.js +31 -0
- package/dist/web/_next/static/chunks/0pq2670_ezbcj.js +1 -0
- package/dist/web/_next/static/chunks/0q_scqkk9.t53.js +2 -0
- package/dist/web/_next/static/chunks/0qyjxx0y7rzuu.js +1 -0
- package/dist/web/_next/static/chunks/0rrdur.v1a5r7.js +1 -0
- package/dist/web/_next/static/chunks/0spo.tmfeas-o.js +1 -0
- package/dist/web/_next/static/chunks/0u88ij9dqqh~-.js +1 -0
- package/dist/web/_next/static/chunks/0zl19l5tuoppw.js +1 -0
- package/dist/web/_next/static/chunks/11n16hogah-5..js +1 -0
- package/dist/web/_next/static/chunks/1250wo~-5dgyx.js +1 -0
- package/dist/web/_next/static/chunks/14n8i2xz4_y-e.js +1 -0
- package/dist/web/_next/static/chunks/160ji-.dfvm20.js +1 -0
- package/dist/web/_next/static/chunks/turbopack-0lxiiw.jhevml.js +1 -0
- package/dist/web/_next/static/media/favicon.0~ekuj.zhggpa.ico +0 -0
- package/dist/web/_not-found/__next._full.txt +26 -19
- package/dist/web/_not-found/__next._head.txt +4 -4
- package/dist/web/_not-found/__next._index.txt +9 -8
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dist/web/_not-found/__next._not-found.txt +3 -3
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +26 -19
- package/dist/web/apple-touch-icon.png +0 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/icon-192.png +0 -0
- package/dist/web/icon-512.png +0 -0
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +22 -21
- package/dist/web/login/__next._full.txt +25 -23
- package/dist/web/login/__next._head.txt +4 -4
- package/dist/web/login/__next._index.txt +9 -8
- package/dist/web/login/__next._tree.txt +2 -2
- package/dist/web/login/__next.login.__PAGE__.txt +4 -4
- package/dist/web/login/__next.login.txt +3 -3
- package/dist/web/login.html +1 -1
- package/dist/web/login.txt +25 -23
- package/dist/web/settings/__next._full.txt +36 -0
- package/dist/web/settings/__next._head.txt +6 -0
- package/dist/web/settings/__next._index.txt +11 -0
- package/dist/web/settings/__next._tree.txt +8 -0
- package/dist/web/settings/__next.settings.__PAGE__.txt +9 -0
- package/dist/web/settings/__next.settings.txt +7 -0
- package/dist/web/settings/agents/__next._full.txt +39 -0
- package/dist/web/settings/agents/__next._head.txt +6 -0
- package/dist/web/settings/agents/__next._index.txt +11 -0
- package/dist/web/settings/agents/__next._tree.txt +8 -0
- package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +9 -0
- package/dist/web/settings/agents/__next.settings.agents.txt +5 -0
- package/dist/web/settings/agents/__next.settings.txt +7 -0
- package/dist/web/settings/agents.html +1 -0
- package/dist/web/settings/agents.txt +39 -0
- package/dist/web/settings/mcps/__next._full.txt +39 -0
- package/dist/web/settings/mcps/__next._head.txt +6 -0
- package/dist/web/settings/mcps/__next._index.txt +11 -0
- package/dist/web/settings/mcps/__next._tree.txt +8 -0
- package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +9 -0
- package/dist/web/settings/mcps/__next.settings.mcps.txt +5 -0
- package/dist/web/settings/mcps/__next.settings.txt +7 -0
- package/dist/web/settings/mcps.html +1 -0
- package/dist/web/settings/mcps.txt +39 -0
- package/dist/web/settings/models/__next._full.txt +39 -0
- package/dist/web/settings/models/__next._head.txt +6 -0
- package/dist/web/settings/models/__next._index.txt +11 -0
- package/dist/web/settings/models/__next._tree.txt +8 -0
- package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +9 -0
- package/dist/web/settings/models/__next.settings.models.txt +5 -0
- package/dist/web/settings/models/__next.settings.txt +7 -0
- package/dist/web/settings/models.html +1 -0
- package/dist/web/settings/models.txt +39 -0
- package/dist/web/settings/providers/__next._full.txt +39 -0
- package/dist/web/settings/providers/__next._head.txt +6 -0
- package/dist/web/settings/providers/__next._index.txt +11 -0
- package/dist/web/settings/providers/__next._tree.txt +8 -0
- package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +9 -0
- package/dist/web/settings/providers/__next.settings.providers.txt +5 -0
- package/dist/web/settings/providers/__next.settings.txt +7 -0
- package/dist/web/settings/providers.html +1 -0
- package/dist/web/settings/providers.txt +39 -0
- package/dist/web/settings/skills/__next._full.txt +39 -0
- package/dist/web/settings/skills/__next._head.txt +6 -0
- package/dist/web/settings/skills/__next._index.txt +11 -0
- package/dist/web/settings/skills/__next._tree.txt +8 -0
- package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +9 -0
- package/dist/web/settings/skills/__next.settings.skills.txt +5 -0
- package/dist/web/settings/skills/__next.settings.txt +7 -0
- package/dist/web/settings/skills.html +1 -0
- package/dist/web/settings/skills.txt +39 -0
- package/dist/web/settings.html +1 -0
- package/dist/web/settings.txt +36 -0
- package/dist/web/workflows/__next._full.txt +35 -0
- package/dist/web/workflows/__next._head.txt +6 -0
- package/dist/web/workflows/__next._index.txt +11 -0
- package/dist/web/workflows/__next._tree.txt +9 -0
- package/dist/web/workflows/__next.workflows.__PAGE__.txt +10 -0
- package/dist/web/workflows/__next.workflows.txt +5 -0
- package/dist/web/workflows.html +1 -0
- package/dist/web/workflows.txt +35 -0
- package/dist/web/workspace/_/__next._full.txt +25 -20
- package/dist/web/workspace/_/__next._head.txt +4 -4
- package/dist/web/workspace/_/__next._index.txt +9 -8
- package/dist/web/workspace/_/__next._tree.txt +2 -2
- package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.txt +3 -3
- package/dist/web/workspace/_.html +1 -1
- package/dist/web/workspace/_.txt +25 -20
- package/dist/web/workspaces/__next._full.txt +25 -23
- package/dist/web/workspaces/__next._head.txt +4 -4
- package/dist/web/workspaces/__next._index.txt +9 -8
- package/dist/web/workspaces/__next._tree.txt +2 -2
- package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
- package/dist/web/workspaces/__next.workspaces.txt +3 -3
- package/dist/web/workspaces.html +1 -1
- package/dist/web/workspaces.txt +25 -23
- package/dist/ws/agent-prompt.js +84 -0
- package/dist/ws/agent-runner.js +592 -0
- package/dist/ws/handler.js +9 -1200
- package/dist/ws/html-utils.js +30 -0
- package/dist/ws/message-parts.js +498 -0
- package/package.json +12 -10
- package/dist/web/_next/static/chunks/038whpa0zpnas.js +0 -1
- package/dist/web/_next/static/chunks/06q5go~xoz5a3.js +0 -1
- package/dist/web/_next/static/chunks/095.wizobwtyg.js +0 -2
- package/dist/web/_next/static/chunks/0bfg.w~u-83h5.js +0 -1
- package/dist/web/_next/static/chunks/0ce7i6~sb20rv.js +0 -113
- package/dist/web/_next/static/chunks/0d4~pcva1uk-a.js +0 -1
- package/dist/web/_next/static/chunks/0iq70n_u75nyu.js +0 -1
- package/dist/web/_next/static/chunks/0memz-8zsbxpu.js +0 -1
- package/dist/web/_next/static/chunks/0nw~w.3~twebx.js +0 -2
- package/dist/web/_next/static/chunks/0pyytgz~5vt0f.js +0 -31
- package/dist/web/_next/static/chunks/0ujjcrz~1nq.q.css +0 -1
- package/dist/web/_next/static/chunks/0vdnx9n41dyjl.js +0 -1
- package/dist/web/_next/static/chunks/0yl4mqmxtll6g.js +0 -1
- package/dist/web/_next/static/chunks/0zcbfka5tcle3.js +0 -1
- package/dist/web/_next/static/chunks/0~eo58u99i.fr.js +0 -8
- package/dist/web/_next/static/chunks/13_2vxyccsv84.js +0 -4
- package/dist/web/_next/static/chunks/13wfs~urgxu0w.js +0 -1
- package/dist/web/_next/static/chunks/157~3c6wqkt83.js +0 -1
- package/dist/web/_next/static/chunks/15jvow_z4uq-..js +0 -1
- package/dist/web/_next/static/chunks/15v697nf~r-cy.js +0 -2
- package/dist/web/_next/static/chunks/turbopack-164~7ulq9o9yc.js +0 -1
- package/dist/web/_next/static/media/favicon.0x3dzn~oxb6tn.ico +0 -0
- package/dist/web/_next/static/owMnKmxATbtXK_J_k_uHh/_buildManifest.js +0 -21
- /package/dist/web/_next/static/{owMnKmxATbtXK_J_k_uHh → 7UtFGbIZvN_2yiatRc2g3}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{owMnKmxATbtXK_J_k_uHh → 7UtFGbIZvN_2yiatRc2g3}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import * as workflowService from '../services/workflow.js';
|
|
3
|
+
const router = Router();
|
|
4
|
+
router.get('/', (_req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const workflows = workflowService.listWorkflows();
|
|
7
|
+
res.json(workflows);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
res.status(500).json({ error: error.message });
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
router.get('/:workflowId', (req, res) => {
|
|
14
|
+
try {
|
|
15
|
+
const workflow = workflowService.getWorkflow(req.params.workflowId);
|
|
16
|
+
if (!workflow) {
|
|
17
|
+
res.status(404).json({ error: 'Workflow not found' });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
res.json(workflow);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
res.status(500).json({ error: error.message });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
router.post('/', (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const workflow = workflowService.createWorkflow(req.body);
|
|
29
|
+
res.status(201).json(workflow);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
res.status(400).json({ error: error.message });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
router.put('/:workflowId', (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
const workflow = workflowService.updateWorkflow(req.params.workflowId, req.body);
|
|
38
|
+
res.json(workflow);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
res.status(400).json({ error: error.message });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
router.delete('/:workflowId', (req, res) => {
|
|
45
|
+
try {
|
|
46
|
+
workflowService.deleteWorkflow(req.params.workflowId);
|
|
47
|
+
res.status(204).send();
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
res.status(400).json({ error: error.message });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
router.post('/:workflowId/duplicate', (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const workflow = workflowService.duplicateWorkflow(req.params.workflowId);
|
|
56
|
+
res.status(201).json(workflow);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
res.status(400).json({ error: error.message });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
export default router;
|
|
63
|
+
//# sourceMappingURL=workflow.js.map
|
package/dist/routes/workspace.js
CHANGED
|
@@ -73,27 +73,8 @@ router.put('/:id/prompt', (req, res) => {
|
|
|
73
73
|
}
|
|
74
74
|
res.json({ prompt: saved });
|
|
75
75
|
});
|
|
76
|
-
router.get('/:id/agent-templates', (
|
|
77
|
-
|
|
78
|
-
if (!ws) {
|
|
79
|
-
res.status(404).json({ error: 'Workspace not found' });
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const workspaceAgentIds = new Set((ws.agents || []).map((agent) => agent.id));
|
|
83
|
-
res.json(agentService.listTemplates().filter((agent) => !workspaceAgentIds.has(agent.id)));
|
|
84
|
-
});
|
|
85
|
-
router.post('/:id/agents/from-templates', (req, res) => {
|
|
86
|
-
const { agentIds } = req.body;
|
|
87
|
-
if (!Array.isArray(agentIds) || agentIds.length === 0) {
|
|
88
|
-
res.status(400).json({ error: 'agentIds are required' });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const added = agentService.addTemplatesToWorkspace(req.params.id, agentIds);
|
|
92
|
-
if (!added) {
|
|
93
|
-
res.status(404).json({ error: 'Workspace not found' });
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
res.status(201).json(added);
|
|
76
|
+
router.get('/:id/agent-templates', (_req, res) => {
|
|
77
|
+
res.json(agentService.listTemplates());
|
|
97
78
|
});
|
|
98
79
|
router.put('/:id', (req, res) => {
|
|
99
80
|
const ws = wsService.update(req.params.id, req.body);
|
package/dist/services/agent.js
CHANGED
|
@@ -3,27 +3,23 @@ import { copyFileSync, cpSync, existsSync, readFileSync, readdirSync, rmSync, st
|
|
|
3
3
|
import { basename, extname, isAbsolute, join, normalize, relative } from 'node:path';
|
|
4
4
|
import { BUILT_IN_AGENT_TOOLS } from '@agent-spaces/shared';
|
|
5
5
|
import { listAgentSessions, getAgentSession, createAgentSession, updateAgentSession, deleteAgentSession, getAgentUsageDashboard, recordAgentUsage, } from '../storage/agent-store.js';
|
|
6
|
-
import { getWorkspace
|
|
6
|
+
import { getWorkspace } from '../storage/workspace-store.js';
|
|
7
7
|
import { listIssues, updateIssue } from '../storage/issue-store.js';
|
|
8
8
|
import { listChannels, updateChannel } from './channel.js';
|
|
9
9
|
import { ensureDir, getDataDir } from '../storage/json-store.js';
|
|
10
10
|
import { extractUsageFromOutput } from '../storage/usage.js';
|
|
11
|
-
const
|
|
11
|
+
const DEFAULT_AGENT_ROLE = 'agent';
|
|
12
12
|
const VALID_RUNTIME_KINDS = ['open-agent-sdk', 'claude-code', 'codex'];
|
|
13
13
|
const VALID_TOOL_NAMES = new Set(BUILT_IN_AGENT_TOOLS.map((tool) => tool.name));
|
|
14
14
|
const ANTHROPIC_BRIDGE_PROVIDERS = [
|
|
15
15
|
'openai-responses-to-anthropic-messages',
|
|
16
16
|
'openai-chat-completions-to-anthropic-messages',
|
|
17
17
|
];
|
|
18
|
-
export function listPresets(
|
|
19
|
-
|
|
20
|
-
if (!ws)
|
|
21
|
-
return null;
|
|
22
|
-
return ws.agents || [];
|
|
18
|
+
export function listPresets(_workspaceId) {
|
|
19
|
+
return listTemplates();
|
|
23
20
|
}
|
|
24
|
-
export function
|
|
25
|
-
|
|
26
|
-
return (listPresets(workspaceId) ?? []).find((agent) => members.has(agent.id) && agent.role === role && agent.enabled !== false) ?? null;
|
|
21
|
+
export function isValidRole(role) {
|
|
22
|
+
return typeof role === 'string' && role.trim().length > 0;
|
|
27
23
|
}
|
|
28
24
|
export function listTemplates() {
|
|
29
25
|
const root = getGlobalAgentTemplatesDir();
|
|
@@ -34,39 +30,12 @@ export function listTemplates() {
|
|
|
34
30
|
.map((entry) => readAgentTemplate(entry.name))
|
|
35
31
|
.filter((template) => Boolean(template));
|
|
36
32
|
}
|
|
37
|
-
export function addTemplatesToWorkspace(workspaceId, templateIds) {
|
|
38
|
-
const ws = getWorkspace(workspaceId);
|
|
39
|
-
if (!ws)
|
|
40
|
-
return null;
|
|
41
|
-
const existingIds = new Set((ws.agents || []).map((agent) => agent.id));
|
|
42
|
-
const added = [];
|
|
43
|
-
for (const templateId of templateIds) {
|
|
44
|
-
if (existingIds.has(templateId))
|
|
45
|
-
continue;
|
|
46
|
-
const template = readAgentTemplate(templateId);
|
|
47
|
-
if (!template)
|
|
48
|
-
continue;
|
|
49
|
-
const workspaceAgentDir = getWorkspaceAgentDir(ws.agentspaceDir, templateId);
|
|
50
|
-
const preset = {
|
|
51
|
-
...template,
|
|
52
|
-
id: templateId,
|
|
53
|
-
workingDir: workspaceAgentDir,
|
|
54
|
-
};
|
|
55
|
-
writeWorkspaceAgentCopy(preset, ws.agentspaceDir);
|
|
56
|
-
ws.agents = [...(ws.agents || []), preset];
|
|
57
|
-
existingIds.add(templateId);
|
|
58
|
-
added.push(preset);
|
|
59
|
-
}
|
|
60
|
-
if (added.length > 0) {
|
|
61
|
-
ws.updatedAt = new Date().toISOString();
|
|
62
|
-
updateWorkspace(ws);
|
|
63
|
-
}
|
|
64
|
-
return added;
|
|
65
|
-
}
|
|
66
33
|
export async function testConnection(workspaceId, data) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
34
|
+
if (workspaceId) {
|
|
35
|
+
const ws = getWorkspace(workspaceId);
|
|
36
|
+
if (!ws)
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
70
39
|
const apiBase = data.apiBase?.trim();
|
|
71
40
|
const apiKey = data.apiKey?.trim();
|
|
72
41
|
const model = data.modelId?.trim();
|
|
@@ -277,11 +246,7 @@ async function readErrorMessage(response) {
|
|
|
277
246
|
return { message: body, body: body.slice(0, 2000) };
|
|
278
247
|
}
|
|
279
248
|
}
|
|
280
|
-
export function createPreset(
|
|
281
|
-
const ws = getWorkspace(workspaceId);
|
|
282
|
-
if (!ws)
|
|
283
|
-
return null;
|
|
284
|
-
const now = new Date().toISOString();
|
|
249
|
+
export function createPreset(_workspaceId, data) {
|
|
285
250
|
const id = uuid();
|
|
286
251
|
const workingDir = data.workingDir?.trim();
|
|
287
252
|
const runtimeKind = data.runtimeKind && VALID_RUNTIME_KINDS.includes(data.runtimeKind)
|
|
@@ -292,7 +257,7 @@ export function createPreset(workspaceId, data) {
|
|
|
292
257
|
const preset = {
|
|
293
258
|
id,
|
|
294
259
|
name: data.name?.trim() || 'New Agent',
|
|
295
|
-
role:
|
|
260
|
+
role: isValidRole(data.role) ? data.role.trim() : DEFAULT_AGENT_ROLE,
|
|
296
261
|
description: data.description || '',
|
|
297
262
|
runtimeKind: presetRuntimeKind,
|
|
298
263
|
modelProvider: requestedModelProvider,
|
|
@@ -308,25 +273,17 @@ export function createPreset(workspaceId, data) {
|
|
|
308
273
|
maxTokens: data.maxTokens ?? 4096,
|
|
309
274
|
sandboxDirs: data.sandboxDirs,
|
|
310
275
|
maxRetries: data.maxRetries,
|
|
276
|
+
templateId: data.templateId,
|
|
311
277
|
enabled: data.enabled ?? true,
|
|
312
278
|
};
|
|
313
279
|
writeAgentTemplate(preset, data.skills);
|
|
314
|
-
if (!workingDir)
|
|
315
|
-
writeWorkspaceAgentCopy(preset, ws.agentspaceDir);
|
|
316
|
-
ws.agents = [...(ws.agents || []), preset];
|
|
317
|
-
ws.updatedAt = now;
|
|
318
|
-
updateWorkspace(ws);
|
|
319
280
|
return preset;
|
|
320
281
|
}
|
|
321
|
-
export function updatePreset(
|
|
322
|
-
const
|
|
323
|
-
if (!
|
|
324
|
-
return null;
|
|
325
|
-
const index = (ws.agents || []).findIndex((preset) => preset.id === presetId);
|
|
326
|
-
if (index === -1)
|
|
282
|
+
export function updatePreset(_workspaceId, presetId, data) {
|
|
283
|
+
const existing = readAgentTemplate(presetId);
|
|
284
|
+
if (!existing)
|
|
327
285
|
return null;
|
|
328
|
-
const
|
|
329
|
-
const role = data.role && VALID_ROLES.includes(data.role) ? data.role : existing.role;
|
|
286
|
+
const role = isValidRole(data.role) ? data.role.trim() : existing.role;
|
|
330
287
|
const runtimeKind = data.runtimeKind && VALID_RUNTIME_KINDS.includes(data.runtimeKind)
|
|
331
288
|
? data.runtimeKind
|
|
332
289
|
: existing.runtimeKind || 'open-agent-sdk';
|
|
@@ -346,9 +303,6 @@ export function updatePreset(workspaceId, presetId, data) {
|
|
|
346
303
|
enabled: data.enabled ?? existing.enabled ?? true,
|
|
347
304
|
};
|
|
348
305
|
writeAgentTemplate(updated, data.skills);
|
|
349
|
-
ws.agents[index] = updated;
|
|
350
|
-
ws.updatedAt = new Date().toISOString();
|
|
351
|
-
updateWorkspace(ws);
|
|
352
306
|
return updated;
|
|
353
307
|
}
|
|
354
308
|
export function getAllowedTools(mcps) {
|
|
@@ -464,7 +418,15 @@ function writeAgentTemplate(preset, skillInputs) {
|
|
|
464
418
|
ensureDir(skillsDir);
|
|
465
419
|
writeFileSync(join(dir, 'agent.json'), JSON.stringify(preset, null, 2), 'utf-8');
|
|
466
420
|
writeFileSync(join(dir, 'mcp.json'), JSON.stringify(preset.mcps ?? {}, null, 2), 'utf-8');
|
|
467
|
-
|
|
421
|
+
const hasObjectSkills = skillInputs?.some((skill) => typeof skill !== 'string');
|
|
422
|
+
console.log('[writeAgentTemplate]', {
|
|
423
|
+
agentId: preset.id,
|
|
424
|
+
skillsCount: preset.skills?.length ?? 0,
|
|
425
|
+
skills: preset.skills,
|
|
426
|
+
hasObjectSkills,
|
|
427
|
+
skillInputTypes: skillInputs?.map((s) => typeof s),
|
|
428
|
+
});
|
|
429
|
+
if (hasObjectSkills) {
|
|
468
430
|
rmSync(skillsDir, { recursive: true, force: true });
|
|
469
431
|
ensureDir(skillsDir);
|
|
470
432
|
for (const skill of skillInputs) {
|
|
@@ -474,6 +436,41 @@ function writeAgentTemplate(preset, skillInputs) {
|
|
|
474
436
|
writeFileSync(join(skillsDir, filename), skill.content ?? '', 'utf-8');
|
|
475
437
|
}
|
|
476
438
|
}
|
|
439
|
+
else if (preset.skills?.length) {
|
|
440
|
+
const globalSkillsDir = join(getDataDir(), 'skills');
|
|
441
|
+
const keepFiles = new Set(preset.skills.map((s) => s.endsWith('.md') ? s : `${s}.md`));
|
|
442
|
+
// Remove skill files no longer in the list
|
|
443
|
+
if (existsSync(skillsDir)) {
|
|
444
|
+
for (const existing of readdirSync(skillsDir)) {
|
|
445
|
+
if (existing.endsWith('.md') && !keepFiles.has(existing)) {
|
|
446
|
+
rmSync(join(skillsDir, existing), { force: true });
|
|
447
|
+
console.log('[writeAgentTemplate] removed stale skill:', existing);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// Copy / ensure skill files
|
|
452
|
+
for (const filename of keepFiles) {
|
|
453
|
+
const target = join(skillsDir, filename);
|
|
454
|
+
const globalSource = join(globalSkillsDir, filename);
|
|
455
|
+
console.log('[writeAgentTemplate] skill:', filename, 'globalExists:', existsSync(globalSource), 'targetExists:', existsSync(target));
|
|
456
|
+
if (existsSync(globalSource)) {
|
|
457
|
+
copyFileSync(globalSource, target);
|
|
458
|
+
}
|
|
459
|
+
else if (!existsSync(target)) {
|
|
460
|
+
writeFileSync(target, '', 'utf-8');
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
// No skills — clean skills dir
|
|
466
|
+
if (existsSync(skillsDir)) {
|
|
467
|
+
for (const existing of readdirSync(skillsDir)) {
|
|
468
|
+
if (existing.endsWith('.md')) {
|
|
469
|
+
rmSync(join(skillsDir, existing), { force: true });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
477
474
|
}
|
|
478
475
|
function copyAgentTemplateToWorkspace(agentId, agentspaceDir) {
|
|
479
476
|
const sourceDir = getGlobalAgentTemplateDir(agentId);
|
|
@@ -510,7 +507,7 @@ function ensureWorkspaceAgentCopy(preset, agentspaceDir) {
|
|
|
510
507
|
return;
|
|
511
508
|
writeWorkspaceAgentCopy(preset, agentspaceDir);
|
|
512
509
|
}
|
|
513
|
-
function readAgentTemplate(agentId) {
|
|
510
|
+
export function readAgentTemplate(agentId) {
|
|
514
511
|
const filePath = join(getGlobalAgentTemplateDir(agentId), 'agent.json');
|
|
515
512
|
if (!existsSync(filePath))
|
|
516
513
|
return null;
|
|
@@ -530,32 +527,99 @@ export function deletePreset(workspaceId, presetId) {
|
|
|
530
527
|
const ws = getWorkspace(workspaceId);
|
|
531
528
|
if (!ws)
|
|
532
529
|
return null;
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
if (ws.agents.length === before)
|
|
530
|
+
const template = readAgentTemplate(presetId);
|
|
531
|
+
if (!template)
|
|
536
532
|
return false;
|
|
537
|
-
|
|
538
|
-
|
|
533
|
+
// Delete global template
|
|
534
|
+
const templateDir = getGlobalAgentTemplateDir(presetId);
|
|
535
|
+
if (existsSync(templateDir))
|
|
536
|
+
rmSync(templateDir, { recursive: true, force: true });
|
|
539
537
|
// Remove agent from all channels' members
|
|
540
538
|
for (const ch of listChannels(workspaceId)) {
|
|
541
539
|
if (ch.members.includes(presetId)) {
|
|
542
540
|
updateChannel(workspaceId, ch.id, { members: ch.members.filter((m) => m !== presetId) });
|
|
543
541
|
}
|
|
544
542
|
}
|
|
545
|
-
// Remove agent from all issues' members
|
|
543
|
+
// Remove agent from all issues' members
|
|
546
544
|
for (const issue of listIssues(workspaceId)) {
|
|
547
545
|
const membersChanged = issue.members.includes(presetId);
|
|
548
|
-
|
|
549
|
-
if (membersChanged || assignedChanged) {
|
|
546
|
+
if (membersChanged) {
|
|
550
547
|
updateIssue({
|
|
551
548
|
...issue,
|
|
552
|
-
members:
|
|
553
|
-
assignedAgents: assignedChanged ? issue.assignedAgents.filter((a) => a !== presetId) : issue.assignedAgents,
|
|
549
|
+
members: issue.members.filter((m) => m !== presetId),
|
|
554
550
|
});
|
|
555
551
|
}
|
|
556
552
|
}
|
|
557
553
|
return true;
|
|
558
554
|
}
|
|
555
|
+
export function createGlobalPreset(data) {
|
|
556
|
+
const id = uuid();
|
|
557
|
+
const runtimeKind = data.runtimeKind && VALID_RUNTIME_KINDS.includes(data.runtimeKind)
|
|
558
|
+
? data.runtimeKind
|
|
559
|
+
: 'open-agent-sdk';
|
|
560
|
+
const requestedModelProvider = normalizeModelProvider(data.modelProvider);
|
|
561
|
+
const presetRuntimeKind = isAnthropicBridgeProvider(requestedModelProvider) ? 'claude-code' : runtimeKind;
|
|
562
|
+
const preset = {
|
|
563
|
+
id,
|
|
564
|
+
name: data.name?.trim() || 'New Agent',
|
|
565
|
+
role: isValidRole(data.role) ? data.role.trim() : DEFAULT_AGENT_ROLE,
|
|
566
|
+
description: data.description || '',
|
|
567
|
+
runtimeKind: presetRuntimeKind,
|
|
568
|
+
modelProvider: requestedModelProvider,
|
|
569
|
+
modelId: data.modelId || 'claude-sonnet-4-6',
|
|
570
|
+
apiBase: data.apiBase || '',
|
|
571
|
+
apiKey: data.apiKey || '',
|
|
572
|
+
workingDir: '',
|
|
573
|
+
mcps: normalizeMcpConfig(data.mcps),
|
|
574
|
+
skills: normalizeSkillNames(data.skills),
|
|
575
|
+
tools: normalizeToolNames(data.tools ?? BUILT_IN_AGENT_TOOLS.map((tool) => tool.name)),
|
|
576
|
+
systemPrompt: data.systemPrompt || '',
|
|
577
|
+
temperature: data.temperature ?? 0.3,
|
|
578
|
+
maxTokens: data.maxTokens ?? 4096,
|
|
579
|
+
sandboxDirs: data.sandboxDirs,
|
|
580
|
+
maxRetries: data.maxRetries,
|
|
581
|
+
templateId: data.templateId,
|
|
582
|
+
enabled: data.enabled ?? true,
|
|
583
|
+
};
|
|
584
|
+
writeAgentTemplate(preset, data.skills);
|
|
585
|
+
return preset;
|
|
586
|
+
}
|
|
587
|
+
export function updateGlobalPreset(presetId, data) {
|
|
588
|
+
const existing = readAgentTemplate(presetId);
|
|
589
|
+
if (!existing)
|
|
590
|
+
return null;
|
|
591
|
+
const role = isValidRole(data.role) ? data.role.trim() : existing.role;
|
|
592
|
+
const runtimeKind = data.runtimeKind && VALID_RUNTIME_KINDS.includes(data.runtimeKind)
|
|
593
|
+
? data.runtimeKind
|
|
594
|
+
: existing.runtimeKind || 'open-agent-sdk';
|
|
595
|
+
const requestedModelProvider = normalizeModelProvider(data.modelProvider);
|
|
596
|
+
const updatedRuntimeKind = isAnthropicBridgeProvider(requestedModelProvider) ? 'claude-code' : runtimeKind;
|
|
597
|
+
const updated = {
|
|
598
|
+
...existing,
|
|
599
|
+
...data,
|
|
600
|
+
id: existing.id,
|
|
601
|
+
role,
|
|
602
|
+
runtimeKind: updatedRuntimeKind,
|
|
603
|
+
name: data.name?.trim() || existing.name || 'New Agent',
|
|
604
|
+
modelProvider: requestedModelProvider,
|
|
605
|
+
mcps: normalizeMcpConfig(data.mcps),
|
|
606
|
+
skills: normalizeSkillNames(data.skills),
|
|
607
|
+
tools: normalizeToolNames(data.tools ?? existing.tools),
|
|
608
|
+
enabled: data.enabled ?? existing.enabled ?? true,
|
|
609
|
+
};
|
|
610
|
+
writeAgentTemplate(updated, data.skills);
|
|
611
|
+
return updated;
|
|
612
|
+
}
|
|
613
|
+
export function deleteGlobalPreset(presetId) {
|
|
614
|
+
const templateDir = getGlobalAgentTemplateDir(presetId);
|
|
615
|
+
if (!existsSync(templateDir))
|
|
616
|
+
return false;
|
|
617
|
+
rmSync(templateDir, { recursive: true, force: true });
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
export function listGlobalPresets() {
|
|
621
|
+
return listTemplates();
|
|
622
|
+
}
|
|
559
623
|
export function list(workspaceId) {
|
|
560
624
|
return listAgentSessions(workspaceId);
|
|
561
625
|
}
|
|
@@ -4,6 +4,8 @@ import * as issueCommentService from './issue-comment.js';
|
|
|
4
4
|
import * as channelService from './channel.js';
|
|
5
5
|
import * as taskService from './task.js';
|
|
6
6
|
import * as agentService from './agent.js';
|
|
7
|
+
import * as commandService from './command.js';
|
|
8
|
+
import * as commandProcessManager from './command-process-manager.js';
|
|
7
9
|
const currentChannelInputSchema = {
|
|
8
10
|
type: 'object',
|
|
9
11
|
properties: {
|
|
@@ -181,4 +183,74 @@ function assertCurrentChannelId(channel, input) {
|
|
|
181
183
|
}
|
|
182
184
|
return data;
|
|
183
185
|
}
|
|
186
|
+
export function createCommandFunctionTools(workspaceId) {
|
|
187
|
+
return [
|
|
188
|
+
{
|
|
189
|
+
name: 'ListQuickCommands',
|
|
190
|
+
description: 'List all quick commands for the workspace with running status.',
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: 'object',
|
|
193
|
+
properties: {
|
|
194
|
+
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
195
|
+
},
|
|
196
|
+
required: ['workspaceId'],
|
|
197
|
+
additionalProperties: false,
|
|
198
|
+
},
|
|
199
|
+
annotations: { readOnly: true, openWorld: false },
|
|
200
|
+
execute: async (input) => {
|
|
201
|
+
const data = input;
|
|
202
|
+
if (data.workspaceId !== workspaceId)
|
|
203
|
+
throw new Error('workspaceId mismatch');
|
|
204
|
+
const commands = commandService.listCommands(workspaceId);
|
|
205
|
+
const processes = commandProcessManager.getCommandProcesses(workspaceId);
|
|
206
|
+
const processMap = new Map(processes.map(p => [p.commandId, p]));
|
|
207
|
+
return commands.map(cmd => ({
|
|
208
|
+
...cmd,
|
|
209
|
+
running: processMap.has(cmd.id) ? processMap.get(cmd.id).status : false,
|
|
210
|
+
}));
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: 'RunQuickCommand',
|
|
215
|
+
description: 'Run a quick command by ID. Returns sessionId.',
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: 'object',
|
|
218
|
+
properties: {
|
|
219
|
+
workspaceId: { type: 'string' },
|
|
220
|
+
commandId: { type: 'string' },
|
|
221
|
+
},
|
|
222
|
+
required: ['workspaceId', 'commandId'],
|
|
223
|
+
additionalProperties: false,
|
|
224
|
+
},
|
|
225
|
+
annotations: { destructive: false, openWorld: false },
|
|
226
|
+
execute: async (input) => {
|
|
227
|
+
const data = input;
|
|
228
|
+
if (data.workspaceId !== workspaceId)
|
|
229
|
+
throw new Error('workspaceId mismatch');
|
|
230
|
+
return { sessionId: commandProcessManager.runCommand(workspaceId, data.commandId) };
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'StopQuickCommand',
|
|
235
|
+
description: 'Stop a running quick command by ID.',
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
workspaceId: { type: 'string' },
|
|
240
|
+
commandId: { type: 'string' },
|
|
241
|
+
},
|
|
242
|
+
required: ['workspaceId', 'commandId'],
|
|
243
|
+
additionalProperties: false,
|
|
244
|
+
},
|
|
245
|
+
annotations: { destructive: false, openWorld: false },
|
|
246
|
+
execute: async (input) => {
|
|
247
|
+
const data = input;
|
|
248
|
+
if (data.workspaceId !== workspaceId)
|
|
249
|
+
throw new Error('workspaceId mismatch');
|
|
250
|
+
commandProcessManager.stopCommand(workspaceId, data.commandId);
|
|
251
|
+
return { stopped: true };
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
];
|
|
255
|
+
}
|
|
184
256
|
//# sourceMappingURL=builtin-tools.js.map
|
package/dist/services/channel.js
CHANGED
|
@@ -2,7 +2,7 @@ import { v4 as uuid } from 'uuid';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { readJsonFile, writeJsonFile, ensureDir, getDataDir } from '../storage/json-store.js';
|
|
4
4
|
import { rmSync } from 'node:fs';
|
|
5
|
-
import
|
|
5
|
+
import * as agentService from '../services/agent.js';
|
|
6
6
|
function workspaceDir(workspaceId) {
|
|
7
7
|
return join(getDataDir(), 'workspaces', workspaceId);
|
|
8
8
|
}
|
|
@@ -56,19 +56,19 @@ export function updateChannel(workspaceId, channelId, data) {
|
|
|
56
56
|
writeJsonFile(channelsPath(workspaceId), channels);
|
|
57
57
|
return channels[idx];
|
|
58
58
|
}
|
|
59
|
-
function normalizeMembers(workspaceId, members = [
|
|
60
|
-
const agentIds = new Set((
|
|
59
|
+
function normalizeMembers(workspaceId, members = []) {
|
|
60
|
+
const agentIds = new Set(agentService.listPresets(workspaceId).map((agent) => agent.id));
|
|
61
61
|
const normalized = [];
|
|
62
62
|
const seen = new Set();
|
|
63
63
|
for (const member of members) {
|
|
64
|
-
if (
|
|
64
|
+
if (!agentIds.has(member))
|
|
65
65
|
continue;
|
|
66
66
|
if (seen.has(member))
|
|
67
67
|
continue;
|
|
68
68
|
seen.add(member);
|
|
69
69
|
normalized.push(member);
|
|
70
70
|
}
|
|
71
|
-
return normalized
|
|
71
|
+
return normalized;
|
|
72
72
|
}
|
|
73
73
|
export function deleteChannel(workspaceId, channelId) {
|
|
74
74
|
const channels = listChannels(workspaceId);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as ptyService from './pty.js';
|
|
2
|
+
import * as commandService from './command.js';
|
|
3
|
+
import { getWorkspace } from '../storage/workspace-store.js';
|
|
4
|
+
import { broadcastToWorkspace } from '../ws/connection-manager.js';
|
|
5
|
+
const processes = new Map();
|
|
6
|
+
const sessionIndex = new Map();
|
|
7
|
+
const restartTimers = new Map();
|
|
8
|
+
export function runCommand(workspaceId, commandId) {
|
|
9
|
+
const existing = processes.get(commandId);
|
|
10
|
+
if (existing) {
|
|
11
|
+
console.log(`[command] reuse existing session=${existing.sessionId} for command=${commandId}`);
|
|
12
|
+
return existing.sessionId;
|
|
13
|
+
}
|
|
14
|
+
const command = commandService.getCommand(workspaceId, commandId);
|
|
15
|
+
if (!command)
|
|
16
|
+
throw new Error('Command not found');
|
|
17
|
+
const workspace = getWorkspace(workspaceId);
|
|
18
|
+
const cwd = command.cwd || workspace?.boundDirs[0] || process.env.HOME || '/tmp';
|
|
19
|
+
const shell = command.shell;
|
|
20
|
+
const env = command.env;
|
|
21
|
+
console.log(`[command] runCommand: workspace=${workspaceId} command=${commandId} cwd=${cwd} shell=${shell} cmd=${command.command}`);
|
|
22
|
+
let sessionId;
|
|
23
|
+
try {
|
|
24
|
+
sessionId = ptyService.createSession(workspaceId, cwd, (id, output) => {
|
|
25
|
+
broadcastToWorkspace(workspaceId, 'terminal.output', { sessionId: id, data: output });
|
|
26
|
+
}, (id, exitCode) => {
|
|
27
|
+
handlePtyExit(id, exitCode);
|
|
28
|
+
}, shell, env);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.error(`[command] pty.spawn failed: ${err.message}`);
|
|
32
|
+
throw new Error(`Failed to spawn terminal: ${err.message}`);
|
|
33
|
+
}
|
|
34
|
+
const now = new Date().toISOString();
|
|
35
|
+
const cmdProcess = {
|
|
36
|
+
commandId,
|
|
37
|
+
workspaceId,
|
|
38
|
+
sessionId,
|
|
39
|
+
status: 'running',
|
|
40
|
+
startedAt: now,
|
|
41
|
+
restartCount: 0,
|
|
42
|
+
};
|
|
43
|
+
processes.set(commandId, cmdProcess);
|
|
44
|
+
sessionIndex.set(sessionId, commandId);
|
|
45
|
+
ptyService.write(sessionId, command.command + '\r');
|
|
46
|
+
broadcastToWorkspace(workspaceId, 'terminal.created', { sessionId, cwd, shell });
|
|
47
|
+
console.log(`[command] broadcasted terminal.created: session=${sessionId} cwd=${cwd}`);
|
|
48
|
+
broadcastToWorkspace(workspaceId, 'command.started', {
|
|
49
|
+
commandId,
|
|
50
|
+
sessionId,
|
|
51
|
+
workspaceId,
|
|
52
|
+
});
|
|
53
|
+
return sessionId;
|
|
54
|
+
}
|
|
55
|
+
export function stopCommand(workspaceId, commandId) {
|
|
56
|
+
const cmdProcess = processes.get(commandId);
|
|
57
|
+
if (!cmdProcess)
|
|
58
|
+
throw new Error('Command not running');
|
|
59
|
+
console.log(`[command] stopCommand: command=${commandId} session=${cmdProcess.sessionId}`);
|
|
60
|
+
const timer = restartTimers.get(commandId);
|
|
61
|
+
if (timer) {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
restartTimers.delete(commandId);
|
|
64
|
+
}
|
|
65
|
+
ptyService.write(cmdProcess.sessionId, '\x03');
|
|
66
|
+
cmdProcess.status = 'stopping';
|
|
67
|
+
}
|
|
68
|
+
function handlePtyExit(sessionId, exitCode) {
|
|
69
|
+
const commandId = sessionIndex.get(sessionId);
|
|
70
|
+
if (!commandId)
|
|
71
|
+
return;
|
|
72
|
+
console.log(`[command] handlePtyExit: session=${sessionId} command=${commandId} exitCode=${exitCode}`);
|
|
73
|
+
const cmdProcess = processes.get(commandId);
|
|
74
|
+
if (!cmdProcess)
|
|
75
|
+
return;
|
|
76
|
+
processes.delete(commandId);
|
|
77
|
+
sessionIndex.delete(sessionId);
|
|
78
|
+
const timer = restartTimers.get(commandId);
|
|
79
|
+
if (timer) {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
restartTimers.delete(commandId);
|
|
82
|
+
}
|
|
83
|
+
const { workspaceId } = cmdProcess;
|
|
84
|
+
const command = commandService.getCommand(workspaceId, commandId);
|
|
85
|
+
if (command?.autoRestart === true && cmdProcess.status !== 'stopping') {
|
|
86
|
+
const restartCount = cmdProcess.restartCount + 1;
|
|
87
|
+
broadcastToWorkspace(workspaceId, 'command.restarted', {
|
|
88
|
+
commandId,
|
|
89
|
+
sessionId: cmdProcess.sessionId,
|
|
90
|
+
restartCount,
|
|
91
|
+
workspaceId,
|
|
92
|
+
});
|
|
93
|
+
const t = setTimeout(() => {
|
|
94
|
+
restartTimers.delete(commandId);
|
|
95
|
+
try {
|
|
96
|
+
runCommand(workspaceId, commandId);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Command may have been deleted during delay
|
|
100
|
+
}
|
|
101
|
+
}, 1000);
|
|
102
|
+
restartTimers.set(commandId, t);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
broadcastToWorkspace(workspaceId, 'terminal.closed', { sessionId, exitCode });
|
|
106
|
+
broadcastToWorkspace(workspaceId, 'command.stopped', {
|
|
107
|
+
commandId,
|
|
108
|
+
exitCode,
|
|
109
|
+
workspaceId,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export function getCommandProcess(commandId) {
|
|
114
|
+
return processes.get(commandId);
|
|
115
|
+
}
|
|
116
|
+
export function getCommandProcesses(workspaceId) {
|
|
117
|
+
const result = [];
|
|
118
|
+
for (const cmdProcess of processes.values()) {
|
|
119
|
+
if (cmdProcess.workspaceId === workspaceId)
|
|
120
|
+
result.push(cmdProcess);
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
export function cleanup(workspaceId) {
|
|
125
|
+
for (const [commandId, cmdProcess] of processes) {
|
|
126
|
+
if (cmdProcess.workspaceId === workspaceId) {
|
|
127
|
+
const timer = restartTimers.get(commandId);
|
|
128
|
+
if (timer)
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
restartTimers.delete(commandId);
|
|
131
|
+
sessionIndex.delete(cmdProcess.sessionId);
|
|
132
|
+
processes.delete(commandId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=command-process-manager.js.map
|