@agent-spaces/server 0.3.0 → 0.3.2
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 +48 -6
- package/dist/adapters/claude-code-runtime/message-format.js +3 -3
- package/dist/adapters/git.js +140 -2
- package/dist/agents/agent-designer.js +334 -0
- package/dist/agents/agent-message-parts.js +8 -0
- package/dist/agents/commit-agent.js +9 -2
- package/dist/agents/issue-agent-runner.js +0 -1
- package/dist/agents/issue-task-controller.js +14 -4
- package/dist/app.js +51 -1
- package/dist/package.json +1 -1
- package/dist/routes/agent-sse.js +4 -1
- package/dist/routes/agent.js +15 -0
- package/dist/routes/channel.js +1 -1
- package/dist/routes/file.js +97 -1
- package/dist/routes/git.js +122 -1
- package/dist/routes/iframe-bookmarks.js +33 -0
- package/dist/routes/issue.js +12 -0
- package/dist/routes/speech-recognition.js +99 -0
- package/dist/services/agent.js +48 -5
- package/dist/services/builtin-tools.js +67 -1
- package/dist/services/channel.js +2 -0
- package/dist/services/command-process-manager.js +14 -1
- package/dist/services/file.js +86 -6
- package/dist/services/gitignore.js +65 -0
- package/dist/services/issue-retry.js +2 -1
- package/dist/services/issue.js +13 -4
- package/dist/services/notification-hub/bot-agent.js +9 -1
- package/dist/services/persistent-agent-context.js +155 -0
- package/dist/services/pty.js +47 -2
- package/dist/services/search.js +11 -3
- package/dist/services/speech-recognition/base.js +3 -0
- package/dist/services/speech-recognition/index.js +16 -0
- package/dist/services/speech-recognition/tencent.js +117 -0
- package/dist/storage/iframe-bookmarks-store.js +46 -0
- package/dist/storage/speech-recognition-store.js +61 -0
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +4 -4
- package/dist/web/__next._full.txt +31 -26
- package/dist/web/__next._head.txt +4 -4
- package/dist/web/__next._index.txt +17 -13
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/00amm3dvu3po9.js +31 -0
- package/dist/web/_next/static/chunks/01hrib8z5bn-6.js +5 -0
- package/dist/web/_next/static/chunks/08nbjuc5i-ewu.js +3 -0
- package/dist/web/_next/static/chunks/0anuxr11yn5iv.js +2 -0
- package/dist/web/_next/static/chunks/0cxrev-hb8gkk.js +2 -0
- package/dist/web/_next/static/chunks/0egot8y~i-ge3.js +1 -0
- package/dist/web/_next/static/chunks/0e~0o6v_awcod.js +1 -0
- package/dist/web/_next/static/chunks/0f_dcx0nu5z7o.js +1 -0
- package/dist/web/_next/static/chunks/0fpf.rso9mhc0.js +1 -0
- package/dist/web/_next/static/chunks/0jc-4igj54z00.js +189 -0
- package/dist/web/_next/static/chunks/0plreh4_gkxf9.js +1 -0
- package/dist/web/_next/static/chunks/0u7_qd9hzqwac.js +1 -0
- package/dist/web/_next/static/chunks/0uo5x.bu.5u2-.css +1 -0
- package/dist/web/_next/static/chunks/0utxlfbckz4f5.js +1 -0
- package/dist/web/_next/static/chunks/0vesz93a98px4.js +1 -0
- package/dist/web/_next/static/chunks/0w1iudd1d46sw.js +8 -0
- package/dist/web/_next/static/chunks/0w3o.7vi.dlm6.js +1 -0
- package/dist/web/_next/static/chunks/0xtmr0y4vo-~p.js +1 -0
- package/dist/web/_next/static/chunks/11ozf700niy2z.js +1 -0
- package/dist/web/_next/static/chunks/{0jmsjlawtg6oa.js → 145gbwitjxamg.js} +1 -1
- package/dist/web/_next/static/chunks/{turbopack-0lxiiw.jhevml.js → turbopack-0kcwat8.leugk.js} +1 -1
- package/dist/web/_not-found/__next._full.txt +34 -30
- package/dist/web/_not-found/__next._head.txt +4 -4
- package/dist/web/_not-found/__next._index.txt +17 -13
- 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 +34 -30
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +31 -26
- package/dist/web/login/__next._full.txt +35 -29
- package/dist/web/login/__next._head.txt +4 -4
- package/dist/web/login/__next._index.txt +17 -13
- 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 +35 -29
- package/dist/web/settings/__next._full.txt +36 -34
- package/dist/web/settings/__next._head.txt +4 -4
- package/dist/web/settings/__next._index.txt +17 -13
- package/dist/web/settings/__next._tree.txt +2 -2
- package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
- package/dist/web/settings/__next.settings.txt +5 -5
- package/dist/web/settings/agents/__next._full.txt +38 -35
- package/dist/web/settings/agents/__next._head.txt +4 -4
- package/dist/web/settings/agents/__next._index.txt +17 -13
- package/dist/web/settings/agents/__next._tree.txt +2 -2
- package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
- package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
- package/dist/web/settings/agents/__next.settings.txt +5 -5
- package/dist/web/settings/agents.html +1 -1
- package/dist/web/settings/agents.txt +38 -35
- package/dist/web/settings/mcps/__next._full.txt +38 -35
- package/dist/web/settings/mcps/__next._head.txt +4 -4
- package/dist/web/settings/mcps/__next._index.txt +17 -13
- package/dist/web/settings/mcps/__next._tree.txt +2 -2
- package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
- package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
- package/dist/web/settings/mcps/__next.settings.txt +5 -5
- package/dist/web/settings/mcps.html +1 -1
- package/dist/web/settings/mcps.txt +38 -35
- package/dist/web/settings/models/__next._full.txt +38 -35
- package/dist/web/settings/models/__next._head.txt +4 -4
- package/dist/web/settings/models/__next._index.txt +17 -13
- package/dist/web/settings/models/__next._tree.txt +2 -2
- package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
- package/dist/web/settings/models/__next.settings.models.txt +3 -3
- package/dist/web/settings/models/__next.settings.txt +5 -5
- package/dist/web/settings/models.html +1 -1
- package/dist/web/settings/models.txt +38 -35
- package/dist/web/settings/providers/__next._full.txt +38 -35
- package/dist/web/settings/providers/__next._head.txt +4 -4
- package/dist/web/settings/providers/__next._index.txt +17 -13
- package/dist/web/settings/providers/__next._tree.txt +2 -2
- package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
- package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
- package/dist/web/settings/providers/__next.settings.txt +5 -5
- package/dist/web/settings/providers.html +1 -1
- package/dist/web/settings/providers.txt +38 -35
- package/dist/web/settings/skills/__next._full.txt +38 -35
- package/dist/web/settings/skills/__next._head.txt +4 -4
- package/dist/web/settings/skills/__next._index.txt +17 -13
- package/dist/web/settings/skills/__next._tree.txt +2 -2
- package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
- package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
- package/dist/web/settings/skills/__next.settings.txt +5 -5
- package/dist/web/settings/skills.html +1 -1
- package/dist/web/settings/skills.txt +38 -35
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +36 -34
- package/dist/web/workflows/__next._full.txt +36 -31
- package/dist/web/workflows/__next._head.txt +4 -4
- package/dist/web/workflows/__next._index.txt +17 -13
- package/dist/web/workflows/__next._tree.txt +2 -2
- package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
- package/dist/web/workflows/__next.workflows.txt +3 -3
- package/dist/web/workflows.html +1 -1
- package/dist/web/workflows.txt +36 -31
- package/dist/web/workspace/_/__next._full.txt +39 -34
- package/dist/web/workspace/_/__next._head.txt +4 -4
- package/dist/web/workspace/_/__next._index.txt +17 -13
- 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 +39 -34
- package/dist/web/workspaces/__next._full.txt +35 -30
- package/dist/web/workspaces/__next._head.txt +4 -4
- package/dist/web/workspaces/__next._index.txt +17 -13
- 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 +35 -30
- package/dist/ws/agent-prompt.js +48 -18
- package/dist/ws/agent-runner.js +10 -3
- package/dist/ws/message-parts.js +8 -0
- package/dist/ws/terminal-handler.js +1 -1
- package/package.json +1 -1
- package/public/avatars/1778953228323-5je54x-user.jpg +0 -0
- package/public/avatars/1778954057880-g01q6y-user.jpg +0 -0
- package/dist/web/_next/static/chunks/002-dxhags4dj.js +0 -2
- package/dist/web/_next/static/chunks/00fu7sjtftd3f.js +0 -1
- package/dist/web/_next/static/chunks/00s_1ft2-qz0r.js +0 -1
- package/dist/web/_next/static/chunks/02wkg1tjv~_t2.js +0 -1
- package/dist/web/_next/static/chunks/03jy~o.gx._e-.js +0 -3
- package/dist/web/_next/static/chunks/07bs5ds68tg38.js +0 -2
- package/dist/web/_next/static/chunks/0b1w9_ne4c2ez.js +0 -1
- package/dist/web/_next/static/chunks/0bzechrx76-ix.js +0 -1
- package/dist/web/_next/static/chunks/0csqa76ud3h4d.js +0 -1
- package/dist/web/_next/static/chunks/0fxdvo63ywaa~.js +0 -1
- package/dist/web/_next/static/chunks/0k5bmi-es-2nj.js +0 -1
- package/dist/web/_next/static/chunks/0pep4mkvt3.rh.js +0 -31
- package/dist/web/_next/static/chunks/0tqwdxb-8t_n1.js +0 -1
- package/dist/web/_next/static/chunks/0y5ipuqgqfrob.js +0 -8
- package/dist/web/_next/static/chunks/0yc41k980zabo.js +0 -1
- package/dist/web/_next/static/chunks/0zhhkzv4.0koq.js +0 -183
- package/dist/web/_next/static/chunks/0zl19l5tuoppw.js +0 -1
- package/dist/web/_next/static/chunks/102lz3yanx8i~.css +0 -1
- package/dist/web/_next/static/chunks/13wm9.vry~.lh.js +0 -5
- /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_ssgManifest.js +0 -0
package/dist/services/agent.js
CHANGED
|
@@ -9,6 +9,7 @@ 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
11
|
const DEFAULT_AGENT_ROLE = 'agent';
|
|
12
|
+
export const AGENT_GENERATOR_PRESET_ID = 'agent-generator';
|
|
12
13
|
const VALID_RUNTIME_KINDS = ['open-agent-sdk', 'claude-code', 'codex', 'langchain'];
|
|
13
14
|
const VALID_TOOL_NAMES = new Set(BUILT_IN_AGENT_TOOLS.map((tool) => tool.name));
|
|
14
15
|
const ANTHROPIC_BRIDGE_PROVIDERS = [
|
|
@@ -24,11 +25,15 @@ export function isValidRole(role) {
|
|
|
24
25
|
export function listTemplates() {
|
|
25
26
|
const root = getGlobalAgentTemplatesDir();
|
|
26
27
|
if (!existsSync(root))
|
|
27
|
-
return [];
|
|
28
|
-
|
|
28
|
+
return [getDefaultAgentGeneratorPreset()];
|
|
29
|
+
const templates = readdirSync(root, { withFileTypes: true })
|
|
29
30
|
.filter((entry) => entry.isDirectory())
|
|
30
31
|
.map((entry) => readAgentTemplate(entry.name))
|
|
31
32
|
.filter((template) => Boolean(template));
|
|
33
|
+
if (!templates.some((template) => template.id === AGENT_GENERATOR_PRESET_ID)) {
|
|
34
|
+
templates.unshift(getDefaultAgentGeneratorPreset());
|
|
35
|
+
}
|
|
36
|
+
return templates;
|
|
32
37
|
}
|
|
33
38
|
export async function testConnection(workspaceId, data) {
|
|
34
39
|
if (workspaceId) {
|
|
@@ -509,21 +514,57 @@ function ensureWorkspaceAgentCopy(preset, agentspaceDir) {
|
|
|
509
514
|
}
|
|
510
515
|
export function readAgentTemplate(agentId) {
|
|
511
516
|
const filePath = join(getGlobalAgentTemplateDir(agentId), 'agent.json');
|
|
512
|
-
if (!existsSync(filePath))
|
|
513
|
-
return null;
|
|
517
|
+
if (!existsSync(filePath)) {
|
|
518
|
+
return agentId === AGENT_GENERATOR_PRESET_ID ? getDefaultAgentGeneratorPreset() : null;
|
|
519
|
+
}
|
|
514
520
|
try {
|
|
515
521
|
return JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
516
522
|
}
|
|
517
523
|
catch {
|
|
518
|
-
return null;
|
|
524
|
+
return agentId === AGENT_GENERATOR_PRESET_ID ? getDefaultAgentGeneratorPreset() : null;
|
|
519
525
|
}
|
|
520
526
|
}
|
|
527
|
+
function getDefaultAgentGeneratorPreset() {
|
|
528
|
+
return {
|
|
529
|
+
id: AGENT_GENERATOR_PRESET_ID,
|
|
530
|
+
name: 'Agent Generator',
|
|
531
|
+
role: 'agent',
|
|
532
|
+
description: '根据提示词生成其他 Agent 的名称、描述和 Markdown 系统提示。',
|
|
533
|
+
runtimeKind: 'claude-code',
|
|
534
|
+
modelProvider: undefined,
|
|
535
|
+
modelId: '',
|
|
536
|
+
apiBase: '',
|
|
537
|
+
apiKey: '',
|
|
538
|
+
workingDir: '',
|
|
539
|
+
mcps: {},
|
|
540
|
+
skills: [],
|
|
541
|
+
tools: [],
|
|
542
|
+
systemPrompt: [
|
|
543
|
+
'# Role',
|
|
544
|
+
'You are an Agent Spaces agent designer.',
|
|
545
|
+
'',
|
|
546
|
+
'# Responsibilities',
|
|
547
|
+
'- Convert user requirements into production-ready Agent preset metadata.',
|
|
548
|
+
'- Return only valid JSON with name, description, and systemPrompt.',
|
|
549
|
+
'- Write the systemPrompt in clear Markdown with role, responsibilities, workflow, constraints, and output expectations.',
|
|
550
|
+
'',
|
|
551
|
+
'# Constraints',
|
|
552
|
+
'- Do not include Markdown code fences around JSON.',
|
|
553
|
+
'- Keep names concise and descriptions practical.',
|
|
554
|
+
].join('\n'),
|
|
555
|
+
temperature: 0.2,
|
|
556
|
+
maxTokens: 4096,
|
|
557
|
+
enabled: true,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
521
560
|
function sanitizeMarkdownFilename(name) {
|
|
522
561
|
const raw = basename(name).replace(/\.md$/i, '');
|
|
523
562
|
const safe = raw.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'skill';
|
|
524
563
|
return `${safe}.md`;
|
|
525
564
|
}
|
|
526
565
|
export function deletePreset(workspaceId, presetId) {
|
|
566
|
+
if (presetId === AGENT_GENERATOR_PRESET_ID)
|
|
567
|
+
return false;
|
|
527
568
|
const ws = getWorkspace(workspaceId);
|
|
528
569
|
if (!ws)
|
|
529
570
|
return null;
|
|
@@ -611,6 +652,8 @@ export function updateGlobalPreset(presetId, data) {
|
|
|
611
652
|
return updated;
|
|
612
653
|
}
|
|
613
654
|
export function deleteGlobalPreset(presetId) {
|
|
655
|
+
if (presetId === AGENT_GENERATOR_PRESET_ID)
|
|
656
|
+
return false;
|
|
614
657
|
const templateDir = getGlobalAgentTemplateDir(presetId);
|
|
615
658
|
if (!existsSync(templateDir))
|
|
616
659
|
return false;
|
|
@@ -6,6 +6,17 @@ import * as taskService from './task.js';
|
|
|
6
6
|
import * as agentService from './agent.js';
|
|
7
7
|
import * as commandService from './command.js';
|
|
8
8
|
import * as commandProcessManager from './command-process-manager.js';
|
|
9
|
+
const issueStatuses = [
|
|
10
|
+
'draft',
|
|
11
|
+
'planned',
|
|
12
|
+
'in_progress',
|
|
13
|
+
'review_pending',
|
|
14
|
+
'changes_requested',
|
|
15
|
+
'approved',
|
|
16
|
+
'completed',
|
|
17
|
+
'archived',
|
|
18
|
+
'error',
|
|
19
|
+
];
|
|
9
20
|
const currentChannelInputSchema = {
|
|
10
21
|
type: 'object',
|
|
11
22
|
properties: {
|
|
@@ -32,6 +43,11 @@ const createIssueInputSchema = {
|
|
|
32
43
|
type: 'string',
|
|
33
44
|
description: 'Issue description to create for the current channel.',
|
|
34
45
|
},
|
|
46
|
+
status: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
enum: issueStatuses,
|
|
49
|
+
description: 'Issue status to create. Use completed when creating a finished issue report. Defaults to draft.',
|
|
50
|
+
},
|
|
35
51
|
},
|
|
36
52
|
required: ['channelId', 'title'],
|
|
37
53
|
additionalProperties: false,
|
|
@@ -51,6 +67,32 @@ const addCommentInputSchema = {
|
|
|
51
67
|
required: ['channelId', 'content'],
|
|
52
68
|
additionalProperties: false,
|
|
53
69
|
};
|
|
70
|
+
const readTerminalOutputInputSchema = {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
workspaceId: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Optional workspace ID. If omitted, the current workspace is used.',
|
|
76
|
+
},
|
|
77
|
+
sessionId: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
description: 'Terminal session ID to read.',
|
|
80
|
+
},
|
|
81
|
+
offset: {
|
|
82
|
+
type: 'integer',
|
|
83
|
+
minimum: 0,
|
|
84
|
+
description: 'Number of newest lines to skip before reading. Defaults to 0.',
|
|
85
|
+
},
|
|
86
|
+
limit: {
|
|
87
|
+
type: 'integer',
|
|
88
|
+
minimum: 1,
|
|
89
|
+
maximum: 1000,
|
|
90
|
+
description: 'Maximum number of lines to read. Defaults to 100.',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
required: ['sessionId'],
|
|
94
|
+
additionalProperties: false,
|
|
95
|
+
};
|
|
54
96
|
export function createIssueFunctionTools(workspaceId, channel, actor, allowedTools) {
|
|
55
97
|
if (!channel)
|
|
56
98
|
return [];
|
|
@@ -94,9 +136,10 @@ function createCurrentChannelIssue(workspaceId, channel, input) {
|
|
|
94
136
|
throw new Error(`Current channel already has a bound issue: ${currentChannel.issueId}`);
|
|
95
137
|
const title = typeof data.title === 'string' ? data.title.trim() : '';
|
|
96
138
|
const description = typeof data.description === 'string' ? data.description.trim() : '';
|
|
139
|
+
const status = parseIssueStatus(data.status);
|
|
97
140
|
if (!title)
|
|
98
141
|
throw new Error('title is required.');
|
|
99
|
-
const issue = issueService.createForChannel(workspaceId, currentChannel.id, { title, description });
|
|
142
|
+
const issue = issueService.createForChannel(workspaceId, currentChannel.id, { title, description, status });
|
|
100
143
|
if (!issue)
|
|
101
144
|
throw new Error(`Current channel not found: ${currentChannel.id}`);
|
|
102
145
|
channel.issueId = issue.id;
|
|
@@ -104,6 +147,14 @@ function createCurrentChannelIssue(workspaceId, channel, input) {
|
|
|
104
147
|
channel.name = title;
|
|
105
148
|
return issue;
|
|
106
149
|
}
|
|
150
|
+
function parseIssueStatus(status) {
|
|
151
|
+
if (status === undefined)
|
|
152
|
+
return undefined;
|
|
153
|
+
if (typeof status !== 'string' || !issueStatuses.includes(status)) {
|
|
154
|
+
throw new Error(`status must be one of: ${issueStatuses.join(', ')}.`);
|
|
155
|
+
}
|
|
156
|
+
return status;
|
|
157
|
+
}
|
|
107
158
|
function viewCurrentChannelIssue(workspaceId, channel, input) {
|
|
108
159
|
assertCurrentChannelId(channel, input);
|
|
109
160
|
const issue = getBoundIssue(workspaceId, channel);
|
|
@@ -185,6 +236,21 @@ function assertCurrentChannelId(channel, input) {
|
|
|
185
236
|
}
|
|
186
237
|
export function createCommandFunctionTools(workspaceId) {
|
|
187
238
|
return [
|
|
239
|
+
{
|
|
240
|
+
name: 'ReadTerminalOutput',
|
|
241
|
+
description: 'Read paginated terminal output by terminal session ID. Defaults to the newest 100 lines.',
|
|
242
|
+
inputSchema: readTerminalOutputInputSchema,
|
|
243
|
+
annotations: { readOnly: true, openWorld: false },
|
|
244
|
+
execute: async (input) => {
|
|
245
|
+
const data = input;
|
|
246
|
+
if (data.workspaceId && data.workspaceId !== workspaceId)
|
|
247
|
+
throw new Error('workspaceId mismatch');
|
|
248
|
+
return commandProcessManager.readTerminalOutput(workspaceId, data.sessionId, {
|
|
249
|
+
offset: data.offset,
|
|
250
|
+
limit: data.limit,
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
},
|
|
188
254
|
{
|
|
189
255
|
name: 'ListQuickCommands',
|
|
190
256
|
description: 'List all quick commands for the workspace with running status.',
|
package/dist/services/channel.js
CHANGED
|
@@ -53,6 +53,8 @@ export function updateChannel(workspaceId, channelId, data) {
|
|
|
53
53
|
channels[idx].draft = data.draft;
|
|
54
54
|
if (Object.hasOwn(data, 'todos'))
|
|
55
55
|
channels[idx].todos = data.todos;
|
|
56
|
+
if (Object.hasOwn(data, 'archived'))
|
|
57
|
+
channels[idx].archived = data.archived;
|
|
56
58
|
writeJsonFile(channelsPath(workspaceId), channels);
|
|
57
59
|
return channels[idx];
|
|
58
60
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// packages/server/src/services/command-process-manager.ts
|
|
2
|
+
import { statSync } from 'node:fs';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
1
4
|
import * as ptyService from './pty.js';
|
|
2
5
|
import * as commandService from './command.js';
|
|
3
6
|
import { getWorkspace } from '../storage/workspace-store.js';
|
|
@@ -15,7 +18,14 @@ export function runCommand(workspaceId, commandId) {
|
|
|
15
18
|
if (!command)
|
|
16
19
|
throw new Error('Command not found');
|
|
17
20
|
const workspace = getWorkspace(workspaceId);
|
|
18
|
-
const
|
|
21
|
+
const rawCwd = command.cwd || workspace?.boundDirs[0] || process.env.HOME || process.env.USERPROFILE || (process.platform === 'win32' ? process.env.SYSTEMROOT || 'C:\\' : '/tmp');
|
|
22
|
+
let cwd;
|
|
23
|
+
try {
|
|
24
|
+
cwd = statSync(rawCwd).isFile() ? dirname(rawCwd) : rawCwd;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
cwd = rawCwd;
|
|
28
|
+
}
|
|
19
29
|
const shell = command.shell;
|
|
20
30
|
const env = command.env;
|
|
21
31
|
console.log(`[command] runCommand: workspace=${workspaceId} command=${commandId} cwd=${cwd} shell=${shell} cmd=${command.command}`);
|
|
@@ -121,6 +131,9 @@ export function getCommandProcesses(workspaceId) {
|
|
|
121
131
|
}
|
|
122
132
|
return result;
|
|
123
133
|
}
|
|
134
|
+
export function readTerminalOutput(workspaceId, sessionId, options) {
|
|
135
|
+
return ptyService.readSessionOutput(workspaceId, sessionId, options);
|
|
136
|
+
}
|
|
124
137
|
export function cleanup(workspaceId) {
|
|
125
138
|
for (const [commandId, cmdProcess] of processes) {
|
|
126
139
|
if (cmdProcess.workspaceId === workspaceId) {
|
package/dist/services/file.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { readdir, stat, readFile, writeFile, mkdir, rm } from 'node:fs/promises';
|
|
2
|
-
import { join, resolve, relative, isAbsolute } from 'node:path';
|
|
1
|
+
import { readdir, stat, readFile, writeFile, mkdir, rm, rename as fsRename } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve, relative, isAbsolute, dirname } from 'node:path';
|
|
3
3
|
import { getWorkspace } from '../storage/workspace-store.js';
|
|
4
|
+
import { createGitignoreFilter } from './gitignore.js';
|
|
4
5
|
const IGNORED_DIRS = new Set(['.git', 'node_modules', '.next', '.DS_Store']);
|
|
5
6
|
export function resolvePath(workspace, relPath) {
|
|
6
7
|
const base = resolve(workspace.boundDirs[0]);
|
|
@@ -10,25 +11,33 @@ export function resolvePath(workspace, relPath) {
|
|
|
10
11
|
return null;
|
|
11
12
|
return abs;
|
|
12
13
|
}
|
|
13
|
-
export async function readTree(workspace, relPath = '') {
|
|
14
|
+
export async function readTree(workspace, relPath = '', depth = Infinity) {
|
|
14
15
|
const dirPath = resolvePath(workspace, relPath);
|
|
15
16
|
if (!dirPath)
|
|
16
17
|
return [];
|
|
17
|
-
const
|
|
18
|
+
const baseDir = resolve(workspace.boundDirs[0]);
|
|
19
|
+
const ig = await createGitignoreFilter(baseDir);
|
|
20
|
+
try {
|
|
21
|
+
var entries = await readdir(dirPath, { withFileTypes: true });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
18
26
|
const nodes = [];
|
|
19
27
|
for (const entry of entries) {
|
|
20
28
|
if (IGNORED_DIRS.has(entry.name))
|
|
21
29
|
continue;
|
|
22
30
|
const fullPath = join(dirPath, entry.name);
|
|
23
31
|
const entryRelPath = relPath ? `${relPath}/${entry.name}` : entry.name;
|
|
32
|
+
const isIgnored = ig.isIgnored(entryRelPath, entry.name, entry.isDirectory());
|
|
24
33
|
const s = await stat(fullPath);
|
|
25
34
|
if (entry.isDirectory()) {
|
|
26
|
-
const children = await readTree(workspace, entryRelPath);
|
|
27
35
|
nodes.push({
|
|
28
36
|
name: entry.name,
|
|
29
37
|
path: entryRelPath,
|
|
30
38
|
type: 'directory',
|
|
31
|
-
|
|
39
|
+
ignored: isIgnored || undefined,
|
|
40
|
+
children: depth > 1 ? await readTree(workspace, entryRelPath, depth - 1) : undefined,
|
|
32
41
|
});
|
|
33
42
|
}
|
|
34
43
|
else {
|
|
@@ -36,6 +45,7 @@ export async function readTree(workspace, relPath = '') {
|
|
|
36
45
|
name: entry.name,
|
|
37
46
|
path: entryRelPath,
|
|
38
47
|
type: 'file',
|
|
48
|
+
ignored: isIgnored || undefined,
|
|
39
49
|
size: s.size,
|
|
40
50
|
modifiedAt: s.mtime.toISOString(),
|
|
41
51
|
});
|
|
@@ -85,5 +95,75 @@ export async function deletePath(workspace, relPath) {
|
|
|
85
95
|
return false;
|
|
86
96
|
}
|
|
87
97
|
}
|
|
98
|
+
export async function writeFileBinary(workspace, relPath, buffer) {
|
|
99
|
+
const abs = resolvePath(workspace, relPath);
|
|
100
|
+
if (!abs)
|
|
101
|
+
return false;
|
|
102
|
+
try {
|
|
103
|
+
await mkdir(join(abs, '..'), { recursive: true });
|
|
104
|
+
await writeFile(abs, buffer);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export async function importFromUrl(workspace, url, targetDir) {
|
|
112
|
+
try {
|
|
113
|
+
const res = await fetch(url);
|
|
114
|
+
if (!res.ok)
|
|
115
|
+
return null;
|
|
116
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
117
|
+
const urlPath = new URL(url).pathname;
|
|
118
|
+
const filename = decodeURIComponent(urlPath.split('/').pop() || 'downloaded-file');
|
|
119
|
+
const relPath = targetDir ? `${targetDir}/${filename}` : filename;
|
|
120
|
+
const ok = await writeFileBinary(workspace, relPath, buffer);
|
|
121
|
+
return ok ? relPath : null;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export async function importFromAbsPath(workspace, absPath, targetDir) {
|
|
128
|
+
try {
|
|
129
|
+
const content = await readFile(absPath);
|
|
130
|
+
const filename = absPath.split(/[/\\]/).pop() || 'imported-file';
|
|
131
|
+
const relPath = targetDir ? `${targetDir}/${filename}` : filename;
|
|
132
|
+
const ok = await writeFileBinary(workspace, relPath, content);
|
|
133
|
+
return ok ? relPath : null;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export async function renamePath(workspace, oldRelPath, newRelPath) {
|
|
140
|
+
const oldAbs = resolvePath(workspace, oldRelPath);
|
|
141
|
+
const newAbs = resolvePath(workspace, newRelPath);
|
|
142
|
+
if (!oldAbs || !newAbs)
|
|
143
|
+
return false;
|
|
144
|
+
try {
|
|
145
|
+
await mkdir(dirname(newAbs), { recursive: true });
|
|
146
|
+
await fsRename(oldAbs, newAbs);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export async function copyPath(workspace, srcRelPath, destRelPath) {
|
|
154
|
+
const srcAbs = resolvePath(workspace, srcRelPath);
|
|
155
|
+
const destAbs = resolvePath(workspace, destRelPath);
|
|
156
|
+
if (!srcAbs || !destAbs)
|
|
157
|
+
return false;
|
|
158
|
+
try {
|
|
159
|
+
const { cp } = await import('node:fs/promises');
|
|
160
|
+
await mkdir(dirname(destAbs), { recursive: true });
|
|
161
|
+
await cp(srcAbs, destAbs, { recursive: true });
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
88
168
|
export { getWorkspace };
|
|
89
169
|
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const cache = new Map();
|
|
4
|
+
function matchPattern(pattern, name) {
|
|
5
|
+
if (pattern.startsWith('*.')) {
|
|
6
|
+
return name.endsWith(pattern.slice(1));
|
|
7
|
+
}
|
|
8
|
+
if (pattern.startsWith('*.') && pattern.includes('/')) {
|
|
9
|
+
return name.endsWith(pattern.split('/').pop());
|
|
10
|
+
}
|
|
11
|
+
if (pattern.includes('*')) {
|
|
12
|
+
const re = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
|
|
13
|
+
return re.test(name);
|
|
14
|
+
}
|
|
15
|
+
return name === pattern;
|
|
16
|
+
}
|
|
17
|
+
export async function createGitignoreFilter(rootDir) {
|
|
18
|
+
const gitignorePath = join(rootDir, '.gitignore');
|
|
19
|
+
let mtime = 0;
|
|
20
|
+
try {
|
|
21
|
+
const { stat } = await import('node:fs/promises');
|
|
22
|
+
mtime = (await stat(gitignorePath)).mtimeMs;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// no .gitignore
|
|
26
|
+
}
|
|
27
|
+
const cached = cache.get(rootDir);
|
|
28
|
+
if (cached && cached.mtime === mtime)
|
|
29
|
+
return cached.filter;
|
|
30
|
+
let patterns = [];
|
|
31
|
+
try {
|
|
32
|
+
const content = await readFile(gitignorePath, 'utf-8');
|
|
33
|
+
patterns = content.split('\n')
|
|
34
|
+
.map(l => l.trim())
|
|
35
|
+
.filter(l => l && !l.startsWith('#'))
|
|
36
|
+
.map(raw => {
|
|
37
|
+
const negated = raw.startsWith('!');
|
|
38
|
+
let p = negated ? raw.slice(1) : raw;
|
|
39
|
+
const dirOnly = p.endsWith('/');
|
|
40
|
+
if (dirOnly)
|
|
41
|
+
p = p.slice(0, -1);
|
|
42
|
+
// extract the last segment for name matching
|
|
43
|
+
const namePart = p.includes('/') ? p.split('/').filter(Boolean).pop() : p;
|
|
44
|
+
return { raw: namePart, dirOnly, negated, fullPath: p };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// no .gitignore
|
|
49
|
+
}
|
|
50
|
+
const filter = {
|
|
51
|
+
isIgnored(relPath, name, isDir) {
|
|
52
|
+
for (const p of patterns) {
|
|
53
|
+
if (p.dirOnly && !isDir)
|
|
54
|
+
continue;
|
|
55
|
+
if (matchPattern(p.raw, name) || matchPattern(p.fullPath, relPath)) {
|
|
56
|
+
return !p.negated;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
cache.set(rootDir, { mtime, filter });
|
|
63
|
+
return filter;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=gitignore.js.map
|
|
@@ -38,9 +38,10 @@ export async function retryIssue(workspaceId, issueId, ctx, options = {}) {
|
|
|
38
38
|
ctx.broadcast('issue.updated', paused);
|
|
39
39
|
return { issue: paused, retried: false, reason: 'issue retry limit reached' };
|
|
40
40
|
}
|
|
41
|
+
const RETRYABLE_STATUSES = ['failed', 'running', 'reviewing', 'retrying', 'waiting_review'];
|
|
41
42
|
const issueTasks = taskService.list(workspaceId, issueId);
|
|
42
43
|
for (const task of issueTasks) {
|
|
43
|
-
if (task.status
|
|
44
|
+
if (!RETRYABLE_STATUSES.includes(task.status))
|
|
44
45
|
continue;
|
|
45
46
|
const reset = taskService.resetForRetry(workspaceId, task.id, { resetRetryCount: true });
|
|
46
47
|
if (!reset)
|
package/dist/services/issue.js
CHANGED
|
@@ -4,7 +4,14 @@ import * as channelService from '../services/channel.js';
|
|
|
4
4
|
import * as taskService from '../services/task.js';
|
|
5
5
|
function ensureChannel(workspaceId, issue) {
|
|
6
6
|
ensureRetryDefaults(workspaceId, issue);
|
|
7
|
-
const
|
|
7
|
+
const existingChannel = issue.channelId ? channelService.getChannel(workspaceId, issue.channelId) : undefined;
|
|
8
|
+
const issueMembers = issue.members || [];
|
|
9
|
+
const fallbackChannelMembers = existingChannel?.members ?? [];
|
|
10
|
+
const channelMembers = issueMembers.length > 0 ? issueMembers : fallbackChannelMembers;
|
|
11
|
+
if (issueMembers.length === 0 && fallbackChannelMembers.length > 0) {
|
|
12
|
+
issue.members = [...fallbackChannelMembers];
|
|
13
|
+
updateIssue(issue);
|
|
14
|
+
}
|
|
8
15
|
if (issue.channelId) {
|
|
9
16
|
channelService.updateChannel(workspaceId, issue.channelId, { type: 'issue', issueId: issue.id, members: channelMembers });
|
|
10
17
|
return;
|
|
@@ -59,7 +66,7 @@ export function create(workspaceId, input) {
|
|
|
59
66
|
channelId: channel.id,
|
|
60
67
|
title: input.title,
|
|
61
68
|
description: input.description,
|
|
62
|
-
status: 'draft',
|
|
69
|
+
status: input.status ?? 'draft',
|
|
63
70
|
tasks: [],
|
|
64
71
|
members: input.members || [],
|
|
65
72
|
workflowId: input.workflowId,
|
|
@@ -77,15 +84,16 @@ export function createForChannel(workspaceId, channelId, input) {
|
|
|
77
84
|
return null;
|
|
78
85
|
const now = new Date().toISOString();
|
|
79
86
|
const issueId = uuid();
|
|
87
|
+
const issueMembers = input.members ?? channel.members ?? [];
|
|
80
88
|
const issue = {
|
|
81
89
|
id: issueId,
|
|
82
90
|
workspaceId,
|
|
83
91
|
channelId,
|
|
84
92
|
title: input.title,
|
|
85
93
|
description: input.description,
|
|
86
|
-
status: 'draft',
|
|
94
|
+
status: input.status ?? 'draft',
|
|
87
95
|
tasks: [],
|
|
88
|
-
members: [],
|
|
96
|
+
members: [...issueMembers],
|
|
89
97
|
retryCount: 0,
|
|
90
98
|
maxRetries: 3,
|
|
91
99
|
createdAt: now,
|
|
@@ -96,6 +104,7 @@ export function createForChannel(workspaceId, channelId, input) {
|
|
|
96
104
|
name: input.title,
|
|
97
105
|
type: 'issue',
|
|
98
106
|
issueId,
|
|
107
|
+
members: issueMembers,
|
|
99
108
|
});
|
|
100
109
|
return issue;
|
|
101
110
|
}
|
|
@@ -4,6 +4,7 @@ import { createAgentRuntime } from '../../adapters/agent-runtime.js';
|
|
|
4
4
|
import { getThinkingRuntimeConfig } from '../llm-model-config.js';
|
|
5
5
|
import { runIssueAutomation } from '../../agents/issue-agent-runner.js';
|
|
6
6
|
import { publishWorkspaceEvent } from './events.js';
|
|
7
|
+
import { prependPersistentAgentContext } from '../persistent-agent-context.js';
|
|
7
8
|
export function getConfiguredBotAgent(workspaceId) {
|
|
8
9
|
const workspace = workspaceService.getById(workspaceId);
|
|
9
10
|
const botAgentId = workspace?.notificationSettings?.botAgentId;
|
|
@@ -25,8 +26,15 @@ export async function runBotAgent(workspaceId, preset, message) {
|
|
|
25
26
|
...getThinkingRuntimeConfig(preset),
|
|
26
27
|
});
|
|
27
28
|
const workingDir = agentService.resolveWorkingDir(workspaceId, preset);
|
|
29
|
+
const workspace = workspaceService.getById(workspaceId);
|
|
28
30
|
try {
|
|
29
|
-
const result = await runtime.execute(buildBotPrompt(message),
|
|
31
|
+
const result = await runtime.execute(prependPersistentAgentContext(buildBotPrompt(message), {
|
|
32
|
+
workspaceId,
|
|
33
|
+
workingDir,
|
|
34
|
+
boundDirs: workspace?.boundDirs,
|
|
35
|
+
includeWorkspacePrompt: false,
|
|
36
|
+
excludeNativeClaudeMd: preset.runtimeKind === 'claude-code',
|
|
37
|
+
}), workingDir, {
|
|
30
38
|
maxTurns: 20,
|
|
31
39
|
mcpServers: agentService.getMcpServers(preset.mcps),
|
|
32
40
|
skills: agentService.getAvailableSkillNames(agentService.getAgentConfigDir(workspaceId, preset), preset.skills),
|