@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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { existsSync, realpathSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
4
|
+
import { readWorkspacePrompt } from './workspace-prompt.js';
|
|
5
|
+
const INSTRUCTION_FILENAMES = ['CLAUDE.md', 'claude.md', 'AGENTS.md', 'agents.md'];
|
|
6
|
+
const MAX_FILE_CHARS = 48_000;
|
|
7
|
+
const MAX_TOTAL_CHARS = 120_000;
|
|
8
|
+
export function prependPersistentAgentContext(prompt, options) {
|
|
9
|
+
const context = buildPersistentAgentContext(options);
|
|
10
|
+
if (!context)
|
|
11
|
+
return prompt;
|
|
12
|
+
return `${context}\n\n${prompt}`;
|
|
13
|
+
}
|
|
14
|
+
export function buildPersistentAgentContext(options) {
|
|
15
|
+
const sections = [];
|
|
16
|
+
const instructionContext = buildInstructionFileContext(options.workingDir, options.boundDirs, {
|
|
17
|
+
excludeNativeClaudeMd: options.excludeNativeClaudeMd,
|
|
18
|
+
});
|
|
19
|
+
if (instructionContext)
|
|
20
|
+
sections.push(instructionContext);
|
|
21
|
+
if (options.includeWorkspacePrompt !== false) {
|
|
22
|
+
const workspacePrompt = readWorkspacePrompt(options.workspaceId).trim();
|
|
23
|
+
if (workspacePrompt) {
|
|
24
|
+
sections.push([
|
|
25
|
+
'Workspace prompt:',
|
|
26
|
+
workspacePrompt,
|
|
27
|
+
].join('\n'));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return sections.join('\n\n');
|
|
31
|
+
}
|
|
32
|
+
function buildInstructionFileContext(workingDir, boundDirs, options = {}) {
|
|
33
|
+
const files = collectInstructionFiles(workingDir, boundDirs, options);
|
|
34
|
+
if (files.length === 0)
|
|
35
|
+
return '';
|
|
36
|
+
const lines = [
|
|
37
|
+
'Persistent agent instructions:',
|
|
38
|
+
'The following CLAUDE.md and AGENTS.md files were auto-loaded. Earlier files are lower priority; later files are closer to the current working directory and take precedence.',
|
|
39
|
+
];
|
|
40
|
+
let usedChars = 0;
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
if (usedChars >= MAX_TOTAL_CHARS) {
|
|
43
|
+
lines.push('', '[Additional instruction files omitted because the persistent context budget was reached.]');
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
const remaining = MAX_TOTAL_CHARS - usedChars;
|
|
47
|
+
const content = truncateForBudget(file.content, Math.min(MAX_FILE_CHARS, remaining));
|
|
48
|
+
usedChars += content.length;
|
|
49
|
+
lines.push('', `--- ${file.label} ---`, content);
|
|
50
|
+
}
|
|
51
|
+
return lines.join('\n').trim();
|
|
52
|
+
}
|
|
53
|
+
function collectInstructionFiles(workingDir, boundDirs, options = {}) {
|
|
54
|
+
const cwd = resolve(workingDir || process.cwd());
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
const files = [];
|
|
57
|
+
const filenames = getInstructionFilenames(options);
|
|
58
|
+
for (const globalPath of globalInstructionPaths(options)) {
|
|
59
|
+
addInstructionFile(files, seen, globalPath, compactPath(globalPath));
|
|
60
|
+
}
|
|
61
|
+
for (const dir of ancestorDirs(cwd, resolveInstructionRoot(cwd, boundDirs))) {
|
|
62
|
+
for (const filename of filenames) {
|
|
63
|
+
const fullPath = join(dir, filename);
|
|
64
|
+
addInstructionFile(files, seen, fullPath, relativeOrAbsolute(cwd, fullPath));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return files;
|
|
68
|
+
}
|
|
69
|
+
function globalInstructionPaths(options) {
|
|
70
|
+
const claudeDir = join(homedir(), '.claude');
|
|
71
|
+
return getInstructionFilenames(options).map((filename) => join(claudeDir, filename));
|
|
72
|
+
}
|
|
73
|
+
function getInstructionFilenames(options) {
|
|
74
|
+
if (!options.excludeNativeClaudeMd)
|
|
75
|
+
return INSTRUCTION_FILENAMES;
|
|
76
|
+
return INSTRUCTION_FILENAMES.filter((filename) => filename !== 'CLAUDE.md');
|
|
77
|
+
}
|
|
78
|
+
function resolveInstructionRoot(cwd, boundDirs) {
|
|
79
|
+
const roots = (boundDirs ?? [])
|
|
80
|
+
.map((dir) => safeResolve(dir))
|
|
81
|
+
.filter((dir) => Boolean(dir))
|
|
82
|
+
.filter((dir) => isPathWithin(cwd, dir))
|
|
83
|
+
.sort((a, b) => a.length - b.length);
|
|
84
|
+
if (roots.length > 0)
|
|
85
|
+
return roots[0];
|
|
86
|
+
return findNearestProjectRoot(cwd);
|
|
87
|
+
}
|
|
88
|
+
function findNearestProjectRoot(cwd) {
|
|
89
|
+
let current = cwd;
|
|
90
|
+
while (true) {
|
|
91
|
+
if (existsSync(join(current, '.git')) || existsSync(join(current, 'package.json'))) {
|
|
92
|
+
return current;
|
|
93
|
+
}
|
|
94
|
+
const parent = dirname(current);
|
|
95
|
+
if (parent === current)
|
|
96
|
+
return cwd;
|
|
97
|
+
current = parent;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function ancestorDirs(cwd, root) {
|
|
101
|
+
const dirs = [];
|
|
102
|
+
let current = cwd;
|
|
103
|
+
while (true) {
|
|
104
|
+
dirs.push(current);
|
|
105
|
+
if (current === root)
|
|
106
|
+
break;
|
|
107
|
+
const parent = dirname(current);
|
|
108
|
+
if (parent === current)
|
|
109
|
+
break;
|
|
110
|
+
current = parent;
|
|
111
|
+
}
|
|
112
|
+
return dirs.reverse();
|
|
113
|
+
}
|
|
114
|
+
function addInstructionFile(files, seen, path, label) {
|
|
115
|
+
if (!existsSync(path))
|
|
116
|
+
return;
|
|
117
|
+
const stat = statSync(path);
|
|
118
|
+
if (!stat.isFile())
|
|
119
|
+
return;
|
|
120
|
+
const realPath = realpathSync(path);
|
|
121
|
+
if (seen.has(realPath))
|
|
122
|
+
return;
|
|
123
|
+
seen.add(realPath);
|
|
124
|
+
files.push({
|
|
125
|
+
path: realPath,
|
|
126
|
+
label,
|
|
127
|
+
content: readFileSync(path, 'utf-8').trim(),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function truncateForBudget(content, maxChars) {
|
|
131
|
+
if (content.length <= maxChars)
|
|
132
|
+
return content;
|
|
133
|
+
const suffix = '\n\n[Instruction file truncated because the persistent context budget was reached.]';
|
|
134
|
+
return `${content.slice(0, Math.max(0, maxChars - suffix.length)).trimEnd()}${suffix}`;
|
|
135
|
+
}
|
|
136
|
+
function safeResolve(path) {
|
|
137
|
+
if (!path.trim())
|
|
138
|
+
return null;
|
|
139
|
+
return resolve(isAbsolute(path) ? path : join(process.cwd(), path));
|
|
140
|
+
}
|
|
141
|
+
function isPathWithin(path, root) {
|
|
142
|
+
const rel = relative(root, path);
|
|
143
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
144
|
+
}
|
|
145
|
+
function relativeOrAbsolute(fromDir, path) {
|
|
146
|
+
const rel = relative(fromDir, path);
|
|
147
|
+
if (!rel.startsWith('..') && !isAbsolute(rel))
|
|
148
|
+
return rel || path;
|
|
149
|
+
return path;
|
|
150
|
+
}
|
|
151
|
+
function compactPath(path) {
|
|
152
|
+
const home = homedir();
|
|
153
|
+
return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=persistent-agent-context.js.map
|
package/dist/services/pty.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pty from 'node-pty';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
|
-
const MAX_BUFFER_LINES =
|
|
3
|
+
const MAX_BUFFER_LINES = 1000;
|
|
4
|
+
const DEFAULT_READ_LIMIT = 100;
|
|
4
5
|
const sessions = new Map();
|
|
5
6
|
export function createSession(workspaceId, cwd, onOutput, onExit, shell, env) {
|
|
6
7
|
const id = uuid();
|
|
@@ -19,12 +20,13 @@ export function createSession(workspaceId, cwd, onOutput, onExit, shell, env) {
|
|
|
19
20
|
cwd,
|
|
20
21
|
env: ptyEnv,
|
|
21
22
|
});
|
|
22
|
-
const session = { id, pty: ptyProcess, workspaceId, cwd, buffer: [] };
|
|
23
|
+
const session = { id, pty: ptyProcess, workspaceId, cwd, buffer: [], output: '' };
|
|
23
24
|
ptyProcess.onData((data) => {
|
|
24
25
|
session.buffer.push(data);
|
|
25
26
|
if (session.buffer.length > MAX_BUFFER_LINES) {
|
|
26
27
|
session.buffer = session.buffer.slice(-MAX_BUFFER_LINES);
|
|
27
28
|
}
|
|
29
|
+
session.output = trimOutputLines(session.output + data, MAX_BUFFER_LINES);
|
|
28
30
|
onOutput(id, data);
|
|
29
31
|
});
|
|
30
32
|
ptyProcess.onExit(({ exitCode }) => onExit(id, exitCode ?? 0));
|
|
@@ -64,4 +66,47 @@ export function getSessionsByWorkspace(workspaceId) {
|
|
|
64
66
|
}
|
|
65
67
|
return result;
|
|
66
68
|
}
|
|
69
|
+
export function readSessionOutput(workspaceId, sessionId, options = {}) {
|
|
70
|
+
const session = sessions.get(sessionId);
|
|
71
|
+
if (!session || session.workspaceId !== workspaceId) {
|
|
72
|
+
throw new Error(`Terminal session not found: ${sessionId}`);
|
|
73
|
+
}
|
|
74
|
+
const lines = normalizeOutput(session.output).split('\n');
|
|
75
|
+
const totalLines = lines.length === 1 && lines[0] === '' ? 0 : lines.length;
|
|
76
|
+
const limit = normalizePositiveInteger(options.limit, DEFAULT_READ_LIMIT);
|
|
77
|
+
const offset = normalizeNonNegativeInteger(options.offset);
|
|
78
|
+
const start = Math.max(totalLines - offset - limit, 0);
|
|
79
|
+
const end = Math.max(totalLines - offset, 0);
|
|
80
|
+
const page = totalLines === 0 ? [] : lines.slice(start, end);
|
|
81
|
+
return {
|
|
82
|
+
sessionId,
|
|
83
|
+
offset,
|
|
84
|
+
limit,
|
|
85
|
+
totalLines,
|
|
86
|
+
lines: page,
|
|
87
|
+
text: page.join('\n'),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function normalizeOutput(output) {
|
|
91
|
+
return stripAnsi(output).replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n$/, '');
|
|
92
|
+
}
|
|
93
|
+
function trimOutputLines(output, maxLines) {
|
|
94
|
+
const lines = output.split(/\r\n|\n|\r/);
|
|
95
|
+
if (lines.length <= maxLines)
|
|
96
|
+
return output;
|
|
97
|
+
return lines.slice(-maxLines).join('\n');
|
|
98
|
+
}
|
|
99
|
+
function normalizePositiveInteger(value, fallback) {
|
|
100
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
101
|
+
return fallback;
|
|
102
|
+
return Math.min(MAX_BUFFER_LINES, Math.max(1, Math.floor(value)));
|
|
103
|
+
}
|
|
104
|
+
function normalizeNonNegativeInteger(value) {
|
|
105
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
106
|
+
return 0;
|
|
107
|
+
return Math.max(0, Math.floor(value));
|
|
108
|
+
}
|
|
109
|
+
function stripAnsi(value) {
|
|
110
|
+
return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g, '');
|
|
111
|
+
}
|
|
67
112
|
//# sourceMappingURL=pty.js.map
|
package/dist/services/search.js
CHANGED
|
@@ -3,6 +3,7 @@ import { execSync } from 'node:child_process';
|
|
|
3
3
|
import { readdir, stat, readFile } from 'node:fs/promises';
|
|
4
4
|
import { join, relative } from 'node:path';
|
|
5
5
|
import { resolvePath } from './file.js';
|
|
6
|
+
import { createGitignoreFilter } from './gitignore.js';
|
|
6
7
|
const IGNORED_DIRS = new Set(['.git', 'node_modules', '.next', '.DS_Store', '__pycache__', '.turbo', 'dist', 'build', '.cache']);
|
|
7
8
|
const BINARY_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg', '.woff', '.woff2', '.ttf', '.eot', '.mp3', '.mp4', '.zip', '.tar', '.gz', '.wasm']);
|
|
8
9
|
const MAX_FILE_SIZE = 1024 * 1024; // 1MB
|
|
@@ -80,6 +81,7 @@ async function searchWithNodeJs(basePath, options) {
|
|
|
80
81
|
catch {
|
|
81
82
|
return [];
|
|
82
83
|
}
|
|
84
|
+
const ig = await createGitignoreFilter(basePath);
|
|
83
85
|
async function walk(dir) {
|
|
84
86
|
if (results.length >= maxResults)
|
|
85
87
|
return;
|
|
@@ -90,6 +92,9 @@ async function searchWithNodeJs(basePath, options) {
|
|
|
90
92
|
if (IGNORED_DIRS.has(entry.name))
|
|
91
93
|
continue;
|
|
92
94
|
const fullPath = join(dir, entry.name);
|
|
95
|
+
const relPath = relative(basePath, fullPath);
|
|
96
|
+
if (ig.isIgnored(relPath, entry.name, entry.isDirectory()))
|
|
97
|
+
continue;
|
|
93
98
|
if (entry.isDirectory()) {
|
|
94
99
|
await walk(fullPath);
|
|
95
100
|
}
|
|
@@ -134,7 +139,7 @@ async function searchWithNodeJs(basePath, options) {
|
|
|
134
139
|
return results;
|
|
135
140
|
}
|
|
136
141
|
// --- file name search ---
|
|
137
|
-
async function walkForFiles(dir, query, results, basePath, limit) {
|
|
142
|
+
async function walkForFiles(dir, query, results, basePath, limit, ig) {
|
|
138
143
|
if (results.length >= limit)
|
|
139
144
|
return;
|
|
140
145
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -145,6 +150,8 @@ async function walkForFiles(dir, query, results, basePath, limit) {
|
|
|
145
150
|
continue;
|
|
146
151
|
const fullPath = join(dir, entry.name);
|
|
147
152
|
const relPath = relative(basePath, fullPath);
|
|
153
|
+
if (ig.isIgnored(relPath, entry.name, entry.isDirectory()))
|
|
154
|
+
continue;
|
|
148
155
|
if (entry.name.toLowerCase().includes(query.toLowerCase())) {
|
|
149
156
|
results.push({
|
|
150
157
|
path: relPath,
|
|
@@ -153,7 +160,7 @@ async function walkForFiles(dir, query, results, basePath, limit) {
|
|
|
153
160
|
});
|
|
154
161
|
}
|
|
155
162
|
if (entry.isDirectory()) {
|
|
156
|
-
await walkForFiles(fullPath, query, results, basePath, limit);
|
|
163
|
+
await walkForFiles(fullPath, query, results, basePath, limit, ig);
|
|
157
164
|
}
|
|
158
165
|
}
|
|
159
166
|
}
|
|
@@ -174,8 +181,9 @@ export async function searchFiles(workspace, query) {
|
|
|
174
181
|
const basePath = resolvePath(workspace, '');
|
|
175
182
|
if (!basePath)
|
|
176
183
|
return [];
|
|
184
|
+
const ig = await createGitignoreFilter(basePath);
|
|
177
185
|
const results = [];
|
|
178
|
-
await walkForFiles(basePath, query, results, basePath, 100);
|
|
186
|
+
await walkForFiles(basePath, query, results, basePath, 100, ig);
|
|
179
187
|
return results;
|
|
180
188
|
}
|
|
181
189
|
export async function getDirectoryFiles(basePath, dir, extensions, maxFiles, maxSize) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SpeechRecognitionProviderBase } from './base.js';
|
|
2
|
+
import { TencentSpeechProvider } from './tencent.js';
|
|
3
|
+
const providers = [
|
|
4
|
+
new TencentSpeechProvider(),
|
|
5
|
+
];
|
|
6
|
+
function getProvider(provider) {
|
|
7
|
+
const p = providers.find(p => p.provider === provider);
|
|
8
|
+
if (!p)
|
|
9
|
+
throw new Error(`Unknown speech recognition provider: ${provider}`);
|
|
10
|
+
return p;
|
|
11
|
+
}
|
|
12
|
+
export async function createSpeechSession(config) {
|
|
13
|
+
return getProvider(config.provider).createSession(config);
|
|
14
|
+
}
|
|
15
|
+
export { SpeechRecognitionProviderBase };
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createHmac } from 'node:crypto';
|
|
3
|
+
import WebSocket from 'ws';
|
|
4
|
+
import { SpeechRecognitionProviderBase } from './base.js';
|
|
5
|
+
export class TencentSpeechProvider extends SpeechRecognitionProviderBase {
|
|
6
|
+
provider = 'tencent';
|
|
7
|
+
async createSession(config) {
|
|
8
|
+
const { appId, secretId, secretKey } = this.extractCredentials(config);
|
|
9
|
+
const voiceId = randomUUID();
|
|
10
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
11
|
+
const expired = timestamp + 86400;
|
|
12
|
+
const nonce = Math.floor(Math.random() * 100000000);
|
|
13
|
+
const params = {
|
|
14
|
+
secretid: secretId,
|
|
15
|
+
timestamp: String(timestamp),
|
|
16
|
+
expired: String(expired),
|
|
17
|
+
nonce: String(nonce),
|
|
18
|
+
engine_model_type: '16k_zh',
|
|
19
|
+
voice_id: voiceId,
|
|
20
|
+
voice_format: '1', // PCM
|
|
21
|
+
needvad: '1',
|
|
22
|
+
filter_dirty: '1',
|
|
23
|
+
filter_modal: '1',
|
|
24
|
+
filter_punc: '1',
|
|
25
|
+
};
|
|
26
|
+
const signature = this.generateSignature(appId, params, secretKey);
|
|
27
|
+
params.signature = signature;
|
|
28
|
+
const url = `wss://asr.cloud.tencent.com/asr/v2/${appId}?${new URLSearchParams(params).toString()}`;
|
|
29
|
+
console.log('[tencent-asr] connecting to Tencent ASR, appId:', appId);
|
|
30
|
+
const ws = new WebSocket(url);
|
|
31
|
+
const resultCallbacks = [];
|
|
32
|
+
const errorCallbacks = [];
|
|
33
|
+
const closeCallbacks = [];
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const handshakeTimeout = setTimeout(() => {
|
|
36
|
+
ws.close();
|
|
37
|
+
reject(new Error('Tencent ASR handshake timeout'));
|
|
38
|
+
}, 10000);
|
|
39
|
+
ws.on('open', () => {
|
|
40
|
+
console.log('[tencent-asr] WebSocket opened, waiting for handshake...');
|
|
41
|
+
});
|
|
42
|
+
ws.on('message', (data) => {
|
|
43
|
+
clearTimeout(handshakeTimeout);
|
|
44
|
+
const msg = JSON.parse(data.toString());
|
|
45
|
+
console.log('[tencent-asr] message:', JSON.stringify(msg).slice(0, 200));
|
|
46
|
+
if (msg.code !== 0) {
|
|
47
|
+
const err = new Error(`Tencent ASR error [${msg.code}]: ${msg.message}`);
|
|
48
|
+
errorCallbacks.forEach(cb => cb(err));
|
|
49
|
+
ws.close();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (msg.final === 1) {
|
|
53
|
+
closeCallbacks.forEach(cb => cb());
|
|
54
|
+
ws.close();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (msg.result) {
|
|
58
|
+
const result = {
|
|
59
|
+
text: msg.result.voice_text_str,
|
|
60
|
+
isFinal: msg.result.slice_type === 2,
|
|
61
|
+
sliceType: msg.result.slice_type,
|
|
62
|
+
};
|
|
63
|
+
resultCallbacks.forEach(cb => cb(result));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
ws.on('error', (err) => {
|
|
67
|
+
clearTimeout(handshakeTimeout);
|
|
68
|
+
console.error('[tencent-asr] WebSocket error:', err.message);
|
|
69
|
+
errorCallbacks.forEach(cb => cb(err));
|
|
70
|
+
});
|
|
71
|
+
ws.on('close', () => {
|
|
72
|
+
clearTimeout(handshakeTimeout);
|
|
73
|
+
console.log('[tencent-asr] WebSocket closed');
|
|
74
|
+
closeCallbacks.forEach(cb => cb());
|
|
75
|
+
});
|
|
76
|
+
// Wait for first message (handshake confirmation) to resolve
|
|
77
|
+
ws.once('message', (data) => {
|
|
78
|
+
clearTimeout(handshakeTimeout);
|
|
79
|
+
const msg = JSON.parse(data.toString());
|
|
80
|
+
console.log('[tencent-asr] handshake response:', JSON.stringify(msg).slice(0, 200));
|
|
81
|
+
if (msg.code !== 0) {
|
|
82
|
+
reject(new Error(`Tencent ASR handshake failed [${msg.code}]: ${msg.message}`));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
resolve({
|
|
86
|
+
sendAudio: (data) => {
|
|
87
|
+
if (ws.readyState === WebSocket.OPEN)
|
|
88
|
+
ws.send(data);
|
|
89
|
+
},
|
|
90
|
+
end: () => {
|
|
91
|
+
if (ws.readyState === WebSocket.OPEN)
|
|
92
|
+
ws.send(JSON.stringify({ type: 'end' }));
|
|
93
|
+
},
|
|
94
|
+
onResult: (cb) => resultCallbacks.push(cb),
|
|
95
|
+
onError: (cb) => errorCallbacks.push(cb),
|
|
96
|
+
onClose: (cb) => closeCallbacks.push(cb),
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
extractCredentials(config) {
|
|
102
|
+
const { appId, secretId, secretKey } = config.credentials;
|
|
103
|
+
if (!appId || !secretId || !secretKey) {
|
|
104
|
+
throw new Error('Tencent speech recognition requires appId, secretId, and secretKey');
|
|
105
|
+
}
|
|
106
|
+
return { appId, secretId, secretKey };
|
|
107
|
+
}
|
|
108
|
+
generateSignature(appId, params, secretKey) {
|
|
109
|
+
const sortedKeys = Object.keys(params).sort();
|
|
110
|
+
const queryString = sortedKeys.map(k => `${k}=${params[k]}`).join('&');
|
|
111
|
+
const signStr = `asr.cloud.tencent.com/asr/v2/${appId}?${queryString}`;
|
|
112
|
+
const hmac = createHmac('sha1', secretKey);
|
|
113
|
+
hmac.update(signStr);
|
|
114
|
+
return hmac.digest('base64');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=tencent.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readJsonFile, writeJsonFile, ensureDir, getDataDir } from './json-store.js';
|
|
3
|
+
function filePath() {
|
|
4
|
+
const dir = join(getDataDir());
|
|
5
|
+
ensureDir(dir);
|
|
6
|
+
return join(dir, 'iframe-bookmarks.json');
|
|
7
|
+
}
|
|
8
|
+
export function listBookmarks() {
|
|
9
|
+
return readJsonFile(filePath()) ?? [];
|
|
10
|
+
}
|
|
11
|
+
export function addBookmark(data) {
|
|
12
|
+
const bookmarks = listBookmarks();
|
|
13
|
+
const bookmark = {
|
|
14
|
+
id: `bm-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
15
|
+
title: data.title.trim(),
|
|
16
|
+
url: data.url.trim(),
|
|
17
|
+
size: data.size || 'full',
|
|
18
|
+
createdAt: new Date().toISOString(),
|
|
19
|
+
};
|
|
20
|
+
bookmarks.push(bookmark);
|
|
21
|
+
writeJsonFile(filePath(), bookmarks);
|
|
22
|
+
return bookmark;
|
|
23
|
+
}
|
|
24
|
+
export function updateBookmark(id, data) {
|
|
25
|
+
const bookmarks = listBookmarks();
|
|
26
|
+
const idx = bookmarks.findIndex((b) => b.id === id);
|
|
27
|
+
if (idx === -1)
|
|
28
|
+
return null;
|
|
29
|
+
if (data.title !== undefined)
|
|
30
|
+
bookmarks[idx].title = data.title.trim();
|
|
31
|
+
if (data.url !== undefined)
|
|
32
|
+
bookmarks[idx].url = data.url.trim();
|
|
33
|
+
if (data.size !== undefined)
|
|
34
|
+
bookmarks[idx].size = data.size;
|
|
35
|
+
writeJsonFile(filePath(), bookmarks);
|
|
36
|
+
return bookmarks[idx];
|
|
37
|
+
}
|
|
38
|
+
export function removeBookmark(id) {
|
|
39
|
+
const bookmarks = listBookmarks();
|
|
40
|
+
const next = bookmarks.filter((b) => b.id !== id);
|
|
41
|
+
if (next.length === bookmarks.length)
|
|
42
|
+
return false;
|
|
43
|
+
writeJsonFile(filePath(), next);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=iframe-bookmarks-store.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readJsonFile, writeJsonFile, getDataDir } from './json-store.js';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
const FILE = () => join(getDataDir(), 'speech-recognition.json');
|
|
5
|
+
function load() {
|
|
6
|
+
return readJsonFile(FILE()) ?? [];
|
|
7
|
+
}
|
|
8
|
+
function save(items) {
|
|
9
|
+
writeJsonFile(FILE(), items);
|
|
10
|
+
}
|
|
11
|
+
export function listSpeechConfigs() {
|
|
12
|
+
return load();
|
|
13
|
+
}
|
|
14
|
+
export function getSpeechConfig(id) {
|
|
15
|
+
return load().find(c => c.id === id);
|
|
16
|
+
}
|
|
17
|
+
export function getDefaultSpeechConfig() {
|
|
18
|
+
const items = load();
|
|
19
|
+
return items.find(c => c.enabled !== false) ?? undefined;
|
|
20
|
+
}
|
|
21
|
+
export function createSpeechConfig(input) {
|
|
22
|
+
const items = load();
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
const item = {
|
|
25
|
+
id: randomUUID(),
|
|
26
|
+
provider: input.provider,
|
|
27
|
+
label: input.label || input.provider,
|
|
28
|
+
enabled: true,
|
|
29
|
+
credentials: input.credentials,
|
|
30
|
+
createdAt: now,
|
|
31
|
+
updatedAt: now,
|
|
32
|
+
};
|
|
33
|
+
items.push(item);
|
|
34
|
+
save(items);
|
|
35
|
+
return item;
|
|
36
|
+
}
|
|
37
|
+
export function updateSpeechConfig(id, patch) {
|
|
38
|
+
const items = load();
|
|
39
|
+
const idx = items.findIndex(c => c.id === id);
|
|
40
|
+
if (idx === -1)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (patch.label !== undefined)
|
|
43
|
+
items[idx].label = patch.label;
|
|
44
|
+
if (patch.credentials !== undefined)
|
|
45
|
+
items[idx].credentials = patch.credentials;
|
|
46
|
+
if (patch.enabled !== undefined)
|
|
47
|
+
items[idx].enabled = patch.enabled;
|
|
48
|
+
items[idx].updatedAt = new Date().toISOString();
|
|
49
|
+
save(items);
|
|
50
|
+
return items[idx];
|
|
51
|
+
}
|
|
52
|
+
export function deleteSpeechConfig(id) {
|
|
53
|
+
const items = load();
|
|
54
|
+
const idx = items.findIndex(c => c.id === id);
|
|
55
|
+
if (idx === -1)
|
|
56
|
+
return false;
|
|
57
|
+
items.splice(idx, 1);
|
|
58
|
+
save(items);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=speech-recognition-store.js.map
|