pikiloom 0.4.15 → 0.4.16
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/dashboard/dist/assets/{AgentTab-CKoy_-w4.js → AgentTab-B5tmLxa7.js} +1 -1
- package/dashboard/dist/assets/{DirBrowser-DpbuN0OL.js → DirBrowser-CBp5nyfS.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-ymr7K8dU.js → ExtensionsTab-w4pkrNas.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-CaTtCn3l.js → IMAccessTab-37Po5LP1.js} +1 -1
- package/dashboard/dist/assets/{Modal-DA-9kJxp.js → Modal-CBMO5UcS.js} +1 -1
- package/dashboard/dist/assets/{Modals-BkLIRnNK.js → Modals-DMlEjJUG.js} +1 -1
- package/dashboard/dist/assets/{Select-B0pZtuzF.js → Select-BiSTkS_t.js} +1 -1
- package/dashboard/dist/assets/{SessionPanel-CYQtZZNX.js → SessionPanel-BVC7kwlX.js} +1 -1
- package/dashboard/dist/assets/{SystemTab-B9TcGMzc.js → SystemTab-Brzt5wTT.js} +1 -1
- package/dashboard/dist/assets/index-5Q-Q7ByM.js +3 -0
- package/dashboard/dist/assets/index-Dw3ty4QY.js +23 -0
- package/dashboard/dist/assets/{shared-i_XUH0xm.js → shared-P-W1OYQ6.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/agent/mcp/bridge.js +201 -7
- package/dist/agent/mcp/extensions.js +20 -9
- package/dist/agent/stream.js +3 -2
- package/dist/dashboard/routes/config.js +134 -12
- package/package.json +1 -1
- package/dashboard/dist/assets/index-BCYshErN.js +0 -3
- package/dashboard/dist/assets/index-C5irxzzD.js +0 -23
|
@@ -17,6 +17,7 @@ import os from 'node:os';
|
|
|
17
17
|
import path from 'node:path';
|
|
18
18
|
import { spawn } from 'node:child_process';
|
|
19
19
|
import { loadUserConfig, saveUserConfig } from '../../core/config/user-config.js';
|
|
20
|
+
import { terminateProcessTree } from '../../core/process-control.js';
|
|
20
21
|
import { getRecommendedMcpServers, } from './registry.js';
|
|
21
22
|
import { hasValidMcpToken, injectOAuthHeaders } from './oauth.js';
|
|
22
23
|
// ---------------------------------------------------------------------------
|
|
@@ -548,6 +549,7 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
|
|
|
548
549
|
return new Promise((resolve) => {
|
|
549
550
|
const start = Date.now();
|
|
550
551
|
let checkInterval = null;
|
|
552
|
+
let settled = false;
|
|
551
553
|
const cleanup = () => {
|
|
552
554
|
if (checkInterval) {
|
|
553
555
|
clearInterval(checkInterval);
|
|
@@ -555,20 +557,29 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
|
|
|
555
557
|
}
|
|
556
558
|
clearTimeout(timer);
|
|
557
559
|
};
|
|
560
|
+
const stopChildTree = () => {
|
|
561
|
+
terminateProcessTree(child, { signal: 'SIGTERM', forceSignal: 'SIGKILL', forceAfterMs: 1500 });
|
|
562
|
+
};
|
|
563
|
+
const finish = (result) => {
|
|
564
|
+
if (settled)
|
|
565
|
+
return;
|
|
566
|
+
settled = true;
|
|
567
|
+
cleanup();
|
|
568
|
+
resolve(result);
|
|
569
|
+
};
|
|
558
570
|
const child = spawn(config.command, config.args || [], {
|
|
559
571
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
560
572
|
env: { ...process.env, ...config.env },
|
|
573
|
+
detached: process.platform !== 'win32',
|
|
561
574
|
});
|
|
562
575
|
const timer = setTimeout(() => {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
resolve({ ok: false, error: `timeout after ${timeoutMs}ms`, elapsedMs: Date.now() - start });
|
|
576
|
+
stopChildTree();
|
|
577
|
+
finish({ ok: false, error: `timeout after ${timeoutMs}ms`, elapsedMs: Date.now() - start });
|
|
566
578
|
}, timeoutMs);
|
|
567
579
|
let stdout = '';
|
|
568
580
|
child.stdout?.on('data', (data) => { stdout += data.toString(); });
|
|
569
581
|
child.on('error', (err) => {
|
|
570
|
-
|
|
571
|
-
resolve({ ok: false, error: err.message, elapsedMs: Date.now() - start });
|
|
582
|
+
finish({ ok: false, error: err.message, elapsedMs: Date.now() - start });
|
|
572
583
|
});
|
|
573
584
|
const initRequest = JSON.stringify({
|
|
574
585
|
jsonrpc: '2.0',
|
|
@@ -585,8 +596,8 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
|
|
|
585
596
|
child.stdin?.write(header + initRequest);
|
|
586
597
|
}
|
|
587
598
|
catch {
|
|
588
|
-
|
|
589
|
-
|
|
599
|
+
stopChildTree();
|
|
600
|
+
finish({ ok: false, error: 'failed to write to stdin', elapsedMs: Date.now() - start });
|
|
590
601
|
return;
|
|
591
602
|
}
|
|
592
603
|
checkInterval = setInterval(() => {
|
|
@@ -606,7 +617,7 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
|
|
|
606
617
|
}
|
|
607
618
|
catch { /* best-effort */ }
|
|
608
619
|
setTimeout(() => {
|
|
609
|
-
|
|
620
|
+
stopChildTree();
|
|
610
621
|
const tools = [];
|
|
611
622
|
try {
|
|
612
623
|
const jsonMatches = stdout.match(/\{[^{}]*"tools"\s*:\s*\[[\s\S]*?\]\s*[^{}]*\}/g);
|
|
@@ -630,7 +641,7 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
|
|
|
630
641
|
}
|
|
631
642
|
}
|
|
632
643
|
catch { /* best effort */ }
|
|
633
|
-
|
|
644
|
+
finish({ ok: true, tools: tools.length ? tools : undefined, elapsedMs: Date.now() - start });
|
|
634
645
|
}, 1500);
|
|
635
646
|
}, 100);
|
|
636
647
|
});
|
package/dist/agent/stream.js
CHANGED
|
@@ -473,7 +473,7 @@ export async function doStream(opts) {
|
|
|
473
473
|
// Start MCP bridge for IM tools (when sendFile is available) and/or supplemental servers (browser, etc.)
|
|
474
474
|
let bridge = null;
|
|
475
475
|
try {
|
|
476
|
-
const { startMcpBridge } = await import('./mcp/bridge.js');
|
|
476
|
+
const { startMcpBridge, redactMcpConfigForLog } = await import('./mcp/bridge.js');
|
|
477
477
|
const sessionDir = path.dirname(session.workspacePath);
|
|
478
478
|
bridge = await startMcpBridge({
|
|
479
479
|
sessionDir,
|
|
@@ -498,7 +498,8 @@ export async function doStream(opts) {
|
|
|
498
498
|
else
|
|
499
499
|
agentLog('[mcp] bridge registered with codex');
|
|
500
500
|
try {
|
|
501
|
-
|
|
501
|
+
if (bridge.configPath)
|
|
502
|
+
agentLog(`[mcp] config content:\n${redactMcpConfigForLog(bridge.configPath)}`);
|
|
502
503
|
}
|
|
503
504
|
catch { }
|
|
504
505
|
;
|
|
@@ -6,8 +6,9 @@ import fs from 'node:fs';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import os from 'node:os';
|
|
8
8
|
import { spawn, spawnSync } from 'node:child_process';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
9
10
|
import { loadUserConfig, saveUserConfig, applyUserConfig, hasUserConfigFile } from '../../core/config/user-config.js';
|
|
10
|
-
import { expandTilde } from '../../core/platform.js';
|
|
11
|
+
import { expandTilde, whichSync } from '../../core/platform.js';
|
|
11
12
|
import { readGitStatus } from '../../core/git.js';
|
|
12
13
|
import { isSetupReady } from '../../cli/onboarding.js';
|
|
13
14
|
import { validateDingtalkConfig, validateDiscordConfig, validateFeishuConfig, validateSlackConfig, validateTelegramConfig, validateWecomConfig, validateWeixinConfig, } from '../../core/config/validation.js';
|
|
@@ -66,7 +67,96 @@ function runOpenCommand(command, args) {
|
|
|
66
67
|
throw new Error(detail || `Failed to run ${command} ${args.join(' ')}`);
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
|
-
function
|
|
70
|
+
function stripOpenPathWrapping(value) {
|
|
71
|
+
let text = value.trim();
|
|
72
|
+
const pairs = [['`', '`'], ['"', '"'], ["'", "'"], ['<', '>']];
|
|
73
|
+
let changed = true;
|
|
74
|
+
while (changed && text.length >= 2) {
|
|
75
|
+
changed = false;
|
|
76
|
+
for (const [left, right] of pairs) {
|
|
77
|
+
if (text.startsWith(left) && text.endsWith(right)) {
|
|
78
|
+
text = text.slice(left.length, -right.length).trim();
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return text;
|
|
84
|
+
}
|
|
85
|
+
function decodeOpenPathInput(raw) {
|
|
86
|
+
const text = stripOpenPathWrapping(raw);
|
|
87
|
+
if (text.startsWith('file://')) {
|
|
88
|
+
try {
|
|
89
|
+
return fileURLToPath(text);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return decodeURI(text.slice('file://'.length));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (text.startsWith('vscode://file/')) {
|
|
96
|
+
return decodeURI(`/${text.slice('vscode://file/'.length)}`);
|
|
97
|
+
}
|
|
98
|
+
return text;
|
|
99
|
+
}
|
|
100
|
+
function resolveOpenBasePath(basePath) {
|
|
101
|
+
const base = typeof basePath === 'string' && basePath.trim()
|
|
102
|
+
? basePath.trim()
|
|
103
|
+
: runtime.getRuntimeWorkdir(loadUserConfig());
|
|
104
|
+
return path.resolve(expandTilde(base || process.cwd()));
|
|
105
|
+
}
|
|
106
|
+
function splitExistingLineSuffix(candidate) {
|
|
107
|
+
const normalized = path.normalize(candidate);
|
|
108
|
+
if (fs.existsSync(normalized))
|
|
109
|
+
return { filePath: normalized, line: null, column: null };
|
|
110
|
+
const match = /^(.*?)(?::(\d+)(?::(\d+))?)$/.exec(normalized);
|
|
111
|
+
if (!match || !match[1])
|
|
112
|
+
return { filePath: normalized, line: null, column: null };
|
|
113
|
+
const filePath = path.normalize(match[1]);
|
|
114
|
+
if (!fs.existsSync(filePath))
|
|
115
|
+
return { filePath: normalized, line: null, column: null };
|
|
116
|
+
return {
|
|
117
|
+
filePath,
|
|
118
|
+
line: Number(match[2]),
|
|
119
|
+
column: match[3] ? Number(match[3]) : null,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function resolveOpenPathLocator(rawPath, basePath) {
|
|
123
|
+
const decoded = decodeOpenPathInput(rawPath);
|
|
124
|
+
const expanded = expandTilde(decoded);
|
|
125
|
+
const absolute = path.isAbsolute(expanded)
|
|
126
|
+
? path.resolve(expanded)
|
|
127
|
+
: path.resolve(resolveOpenBasePath(basePath), expanded);
|
|
128
|
+
return splitExistingLineSuffix(absolute);
|
|
129
|
+
}
|
|
130
|
+
function editorGotoArg(filePath, location) {
|
|
131
|
+
if (!location?.line)
|
|
132
|
+
return null;
|
|
133
|
+
return `${filePath}:${location.line}${location.column ? `:${location.column}` : ''}`;
|
|
134
|
+
}
|
|
135
|
+
function tryOpenCommand(command, args) {
|
|
136
|
+
if (!whichSync(command))
|
|
137
|
+
return false;
|
|
138
|
+
try {
|
|
139
|
+
runOpenCommand(command, args);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function tryOpenVSCodeUrl(filePath, location) {
|
|
147
|
+
if (!location?.line)
|
|
148
|
+
return false;
|
|
149
|
+
const suffix = `:${location.line}${location.column ? `:${location.column}` : ''}`;
|
|
150
|
+
try {
|
|
151
|
+
runOpenCommand('open', [`vscode://file${encodeURI(filePath)}${suffix}`]);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function openPathWithTarget(filePath, target, isDirectory, location) {
|
|
159
|
+
const gotoArg = isDirectory ? null : editorGotoArg(filePath, location);
|
|
70
160
|
if (process.platform === 'darwin') {
|
|
71
161
|
switch (target) {
|
|
72
162
|
case 'finder':
|
|
@@ -76,13 +166,21 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
76
166
|
runOpenCommand('open', [filePath]);
|
|
77
167
|
return;
|
|
78
168
|
case 'cursor':
|
|
169
|
+
if (gotoArg && tryOpenCommand('cursor', ['-g', gotoArg]))
|
|
170
|
+
return;
|
|
79
171
|
runOpenCommand('open', ['-a', 'Cursor', filePath]);
|
|
80
172
|
return;
|
|
81
173
|
case 'windsurf':
|
|
174
|
+
if (gotoArg && tryOpenCommand('windsurf', ['-g', gotoArg]))
|
|
175
|
+
return;
|
|
82
176
|
runOpenCommand('open', ['-a', 'Windsurf', filePath]);
|
|
83
177
|
return;
|
|
84
178
|
case 'vscode':
|
|
85
179
|
default:
|
|
180
|
+
if (gotoArg && tryOpenCommand('code', ['-g', gotoArg]))
|
|
181
|
+
return;
|
|
182
|
+
if (gotoArg && tryOpenVSCodeUrl(filePath, location))
|
|
183
|
+
return;
|
|
86
184
|
runOpenCommand('open', ['-a', 'Visual Studio Code', filePath]);
|
|
87
185
|
return;
|
|
88
186
|
}
|
|
@@ -90,10 +188,16 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
90
188
|
if (process.platform === 'win32') {
|
|
91
189
|
switch (target) {
|
|
92
190
|
case 'cursor':
|
|
93
|
-
|
|
191
|
+
if (gotoArg)
|
|
192
|
+
runOpenCommand('cursor', ['-g', gotoArg]);
|
|
193
|
+
else
|
|
194
|
+
runOpenCommand('cursor', [filePath]);
|
|
94
195
|
return;
|
|
95
196
|
case 'windsurf':
|
|
96
|
-
|
|
197
|
+
if (gotoArg)
|
|
198
|
+
runOpenCommand('windsurf', ['-g', gotoArg]);
|
|
199
|
+
else
|
|
200
|
+
runOpenCommand('windsurf', [filePath]);
|
|
97
201
|
return;
|
|
98
202
|
case 'finder':
|
|
99
203
|
case 'default':
|
|
@@ -101,16 +205,25 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
101
205
|
return;
|
|
102
206
|
case 'vscode':
|
|
103
207
|
default:
|
|
104
|
-
|
|
208
|
+
if (gotoArg)
|
|
209
|
+
runOpenCommand('code', ['-g', gotoArg]);
|
|
210
|
+
else
|
|
211
|
+
runOpenCommand('code', [filePath]);
|
|
105
212
|
return;
|
|
106
213
|
}
|
|
107
214
|
}
|
|
108
215
|
switch (target) {
|
|
109
216
|
case 'cursor':
|
|
110
|
-
|
|
217
|
+
if (gotoArg)
|
|
218
|
+
runOpenCommand('cursor', ['-g', gotoArg]);
|
|
219
|
+
else
|
|
220
|
+
runOpenCommand('cursor', [filePath]);
|
|
111
221
|
return;
|
|
112
222
|
case 'windsurf':
|
|
113
|
-
|
|
223
|
+
if (gotoArg)
|
|
224
|
+
runOpenCommand('windsurf', ['-g', gotoArg]);
|
|
225
|
+
else
|
|
226
|
+
runOpenCommand('windsurf', [filePath]);
|
|
114
227
|
return;
|
|
115
228
|
case 'finder':
|
|
116
229
|
case 'default':
|
|
@@ -118,7 +231,10 @@ function openPathWithTarget(filePath, target, isDirectory) {
|
|
|
118
231
|
return;
|
|
119
232
|
case 'vscode':
|
|
120
233
|
default:
|
|
121
|
-
|
|
234
|
+
if (gotoArg)
|
|
235
|
+
runOpenCommand('code', ['-g', gotoArg]);
|
|
236
|
+
else
|
|
237
|
+
runOpenCommand('code', [filePath]);
|
|
122
238
|
return;
|
|
123
239
|
}
|
|
124
240
|
}
|
|
@@ -453,14 +569,20 @@ app.post('/api/open-in-editor', async (c) => {
|
|
|
453
569
|
try {
|
|
454
570
|
const body = await c.req.json();
|
|
455
571
|
const filePath = typeof body?.filePath === 'string' ? body.filePath.trim() : '';
|
|
572
|
+
const basePath = typeof body?.basePath === 'string' && body.basePath.trim()
|
|
573
|
+
? body.basePath.trim()
|
|
574
|
+
: typeof body?.workdir === 'string' && body.workdir.trim()
|
|
575
|
+
? body.workdir.trim()
|
|
576
|
+
: null;
|
|
456
577
|
const target = isOpenTarget(body?.target) ? body.target : 'vscode';
|
|
457
578
|
if (!filePath)
|
|
458
579
|
return c.json({ ok: false, error: 'filePath is required' }, 400);
|
|
459
|
-
|
|
580
|
+
const resolved = resolveOpenPathLocator(filePath, basePath);
|
|
581
|
+
if (!fs.existsSync(resolved.filePath))
|
|
460
582
|
return c.json({ ok: false, error: 'Path not found' }, 404);
|
|
461
|
-
const stat = fs.statSync(filePath);
|
|
462
|
-
openPathWithTarget(filePath, target, stat.isDirectory());
|
|
463
|
-
return c.json({ ok: true });
|
|
583
|
+
const stat = fs.statSync(resolved.filePath);
|
|
584
|
+
openPathWithTarget(resolved.filePath, target, stat.isDirectory(), resolved);
|
|
585
|
+
return c.json({ ok: true, filePath: resolved.filePath, line: resolved.line, column: resolved.column });
|
|
464
586
|
}
|
|
465
587
|
catch (err) {
|
|
466
588
|
const detail = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED