@cydm/magic-shell-agent-node 0.1.3 → 0.1.5
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/pty-adapter.js +39 -8
- package/dist/node.js +3 -0
- package/dist/plugin-loader.js +59 -1
- package/dist/plugins/claude-code.json +7 -0
- package/dist/plugins/codex.json +7 -0
- package/dist/plugins/echo.json +7 -0
- package/dist/plugins/pie-unity.json +8 -0
- package/dist/workbench/index.html +15 -0
- package/dist/workbench/runtime-control.js +18 -0
- package/dist/workbench/runtime-messages.js +3 -0
- package/dist/worker-runtime.js +4 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node-pty";
|
|
2
|
-
import { dirname } from "path";
|
|
2
|
+
import { delimiter, dirname, join } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
const __filename = fileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = dirname(__filename);
|
|
@@ -16,13 +16,26 @@ export class PtyAdapter {
|
|
|
16
16
|
}
|
|
17
17
|
async start(config) {
|
|
18
18
|
const agentId = `pty-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
const spawnCwd = config.cwd || process.cwd();
|
|
20
|
+
const spawnEnv = { ...process.env, ...config.env };
|
|
21
|
+
console.log(`[PtyAdapter] Starting ${agentId}: command=${JSON.stringify(config.command)} args=${JSON.stringify(config.args || [])} cwd=${JSON.stringify(spawnCwd)} platform=${process.platform}`);
|
|
22
|
+
if (process.platform === "win32" && !isAbsoluteOrQualifiedCommand(config.command)) {
|
|
23
|
+
console.log(`[PtyAdapter] Windows bare command probe for ${agentId}: ${JSON.stringify(findWindowsCommandCandidates(config.command, spawnEnv.PATH))}`);
|
|
24
|
+
}
|
|
25
|
+
let pty;
|
|
26
|
+
try {
|
|
27
|
+
pty = spawn(config.command, config.args || [], {
|
|
28
|
+
name: "xterm-256color",
|
|
29
|
+
cols: 80,
|
|
30
|
+
rows: 24,
|
|
31
|
+
cwd: spawnCwd,
|
|
32
|
+
env: spawnEnv,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(`[PtyAdapter] Failed to start ${agentId}:`, error instanceof Error ? error.stack || error.message : error);
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
26
39
|
const agent = {
|
|
27
40
|
id: agentId,
|
|
28
41
|
pty,
|
|
@@ -97,3 +110,21 @@ export class PtyAdapter {
|
|
|
97
110
|
this.exitCallbacks.push(callback);
|
|
98
111
|
}
|
|
99
112
|
}
|
|
113
|
+
function isAbsoluteOrQualifiedCommand(command) {
|
|
114
|
+
return command.includes("/") || command.includes("\\") || /\.(exe|cmd|bat|com|ps1)$/i.test(command);
|
|
115
|
+
}
|
|
116
|
+
function findWindowsCommandCandidates(command, pathValue) {
|
|
117
|
+
if (!pathValue)
|
|
118
|
+
return [];
|
|
119
|
+
const pathext = (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD;.PS1")
|
|
120
|
+
.split(";")
|
|
121
|
+
.map((value) => value.trim())
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
const results = [];
|
|
124
|
+
for (const dir of pathValue.split(delimiter).filter(Boolean)) {
|
|
125
|
+
for (const ext of ["", ...pathext]) {
|
|
126
|
+
results.push(join(dir, ext ? `${command}${ext}` : command));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return results.slice(0, 20);
|
|
130
|
+
}
|
package/dist/node.js
CHANGED
|
@@ -641,11 +641,13 @@ export class AgentNode {
|
|
|
641
641
|
const cwd = this.readString(payload.cwd) || message.cwd;
|
|
642
642
|
const displayName = this.readString(payload.displayName) || message.displayName;
|
|
643
643
|
const sessionId = this.readString(payload.sessionId) || message.target?.sessionId || generateSessionId();
|
|
644
|
+
console.log(`[AgentNode] control spawn_worker start: session=${sessionId} plugin=${pluginName} cwd=${JSON.stringify(cwd || "")} displayName=${JSON.stringify(displayName || "")} task=${JSON.stringify(taskSummary || "")}`);
|
|
644
645
|
try {
|
|
645
646
|
await this.spawnWorker({ sessionId, pluginName, cwd, displayName, taskSummary });
|
|
646
647
|
if (taskSummary) {
|
|
647
648
|
this.rememberNodeSpawn(taskSummary, sessionId);
|
|
648
649
|
}
|
|
650
|
+
console.log(`[AgentNode] control spawn_worker success: session=${sessionId} plugin=${pluginName}`);
|
|
649
651
|
this.sendControlResult(source, message.requestId, name, true, {
|
|
650
652
|
sessionId,
|
|
651
653
|
pluginName,
|
|
@@ -654,6 +656,7 @@ export class AgentNode {
|
|
|
654
656
|
});
|
|
655
657
|
}
|
|
656
658
|
catch (err) {
|
|
659
|
+
console.error(`[AgentNode] control spawn_worker failed: session=${sessionId} plugin=${pluginName}`, err instanceof Error ? err.stack || err.message : err);
|
|
657
660
|
this.sendControlResult(source, message.requestId, name, false, {
|
|
658
661
|
error: err instanceof Error ? err.message : String(err),
|
|
659
662
|
});
|
package/dist/plugin-loader.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, existsSync } from "fs";
|
|
2
2
|
import { join, extname, dirname, isAbsolute, resolve } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
|
+
import { createRequire } from "module";
|
|
4
5
|
/**
|
|
5
6
|
* 加载单个插件配置
|
|
6
7
|
*/
|
|
@@ -29,12 +30,13 @@ export function loadPlugin(path) {
|
|
|
29
30
|
if (!["stdio", "pty", "rpc"].includes(config.type)) {
|
|
30
31
|
throw new Error(`Plugin ${path} invalid type: ${config.type}`);
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
const normalized = {
|
|
33
34
|
...config,
|
|
34
35
|
command: resolveCommandPath(config.command, configDir, repoRoot),
|
|
35
36
|
args: resolveArgPaths(config.args, configDir, repoRoot),
|
|
36
37
|
cwd: resolveOptionalPath(config.cwd, configDir, repoRoot),
|
|
37
38
|
};
|
|
39
|
+
return maybeResolveBundledPieCommand(normalized, configDir, repoRoot);
|
|
38
40
|
}
|
|
39
41
|
function resolveOptionalPath(value, configDir, repoRoot) {
|
|
40
42
|
if (!value)
|
|
@@ -82,6 +84,62 @@ function resolveArgPaths(args, configDir, repoRoot) {
|
|
|
82
84
|
return arg;
|
|
83
85
|
});
|
|
84
86
|
}
|
|
87
|
+
function maybeResolveBundledPieCommand(config, configDir, repoRoot) {
|
|
88
|
+
if (config.name !== "pie" || config.command !== "pie") {
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
const pieEntry = resolveInstalledPackageBin("@cydm/pie", configDir, repoRoot);
|
|
92
|
+
if (!pieEntry) {
|
|
93
|
+
return config;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
...config,
|
|
97
|
+
command: process.execPath,
|
|
98
|
+
args: [pieEntry, ...(config.args || [])],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function resolveInstalledPackageBin(packageName, configDir, repoRoot) {
|
|
102
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
103
|
+
try {
|
|
104
|
+
const packageJsonPath = requireFromHere.resolve(`${packageName}/package.json`);
|
|
105
|
+
const direct = readPackageBin(packageJsonPath);
|
|
106
|
+
if (direct)
|
|
107
|
+
return direct;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Fall through to alternate roots below.
|
|
111
|
+
}
|
|
112
|
+
const searchRoots = [
|
|
113
|
+
configDir,
|
|
114
|
+
repoRoot,
|
|
115
|
+
process.cwd(),
|
|
116
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
117
|
+
];
|
|
118
|
+
for (const root of searchRoots) {
|
|
119
|
+
try {
|
|
120
|
+
const packageJsonPath = requireFromHere.resolve(`${packageName}/package.json`, { paths: [resolve(root)] });
|
|
121
|
+
const resolved = readPackageBin(packageJsonPath);
|
|
122
|
+
if (resolved)
|
|
123
|
+
return resolved;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
function readPackageBin(packageJsonPath) {
|
|
132
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
133
|
+
const packageRoot = dirname(packageJsonPath);
|
|
134
|
+
const binField = packageJson.bin;
|
|
135
|
+
const relativeBin = typeof binField === "string"
|
|
136
|
+
? binField
|
|
137
|
+
: (binField && Object.values(binField)[0]) || null;
|
|
138
|
+
if (!relativeBin)
|
|
139
|
+
return null;
|
|
140
|
+
const binPath = resolve(packageRoot, relativeBin);
|
|
141
|
+
return existsSync(binPath) ? binPath : null;
|
|
142
|
+
}
|
|
85
143
|
/**
|
|
86
144
|
* 扫描目录加载所有插件
|
|
87
145
|
*/
|
|
@@ -2591,6 +2591,17 @@
|
|
|
2591
2591
|
}
|
|
2592
2592
|
}
|
|
2593
2593
|
|
|
2594
|
+
function setSpawnCwd(cwd, { remember = false } = {}) {
|
|
2595
|
+
const next = (cwd || '').trim();
|
|
2596
|
+
const input = document.getElementById('spawnCwd');
|
|
2597
|
+
input.value = next;
|
|
2598
|
+
defaultSpawnCwd = next;
|
|
2599
|
+
localStorage.setItem('spawnCwd', defaultSpawnCwd);
|
|
2600
|
+
if (remember && next) {
|
|
2601
|
+
rememberCwd(next);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2594
2605
|
const searchParams = new URLSearchParams(window.location.search);
|
|
2595
2606
|
const inferredRelayUrl = inferDefaultRelayUrl();
|
|
2596
2607
|
document.getElementById('relayUrl').value = inferredRelayUrl;
|
|
@@ -3658,6 +3669,10 @@
|
|
|
3658
3669
|
pushNodeTranscript,
|
|
3659
3670
|
renderWorkers,
|
|
3660
3671
|
syncWorkerPluginPreference,
|
|
3672
|
+
getSpawnCwd() {
|
|
3673
|
+
return (document.getElementById('spawnCwd').value || '').trim();
|
|
3674
|
+
},
|
|
3675
|
+
setSpawnCwd,
|
|
3661
3676
|
setSessionDisplay(value) {
|
|
3662
3677
|
document.getElementById('session-display').textContent = value;
|
|
3663
3678
|
},
|
|
@@ -105,6 +105,18 @@
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function handleControlMessage(msg, ctx) {
|
|
108
|
+
if (msg.controlKind === "result" && msg.controlName === "spawn_worker") {
|
|
109
|
+
if (msg.ok === false) {
|
|
110
|
+
ctx.pendingSpawnSessionId = null;
|
|
111
|
+
ctx.pendingSpawnAutoAttach = true;
|
|
112
|
+
ctx.resetSpawnButton();
|
|
113
|
+
ctx.writeSystemNotice(`SPAWN FAILED: ${msg.payload?.error || msg.error || "Unknown error"}`);
|
|
114
|
+
ctx.renderWorkers();
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
108
120
|
if (msg.controlKind === "result" && msg.controlName === "get_primary_agent" && msg.payload && msg.payload.primary) {
|
|
109
121
|
ctx.primaryAgent = msg.payload.primary || null;
|
|
110
122
|
ctx.renderNodeHome();
|
|
@@ -145,6 +157,9 @@
|
|
|
145
157
|
ctx.dirBrowserParentPath = msg.payload.parentPath || null;
|
|
146
158
|
ctx.dirBrowserRepoRoot = msg.payload.repoRoot || null;
|
|
147
159
|
ctx.dirBrowserEntries = msg.payload.entries || [];
|
|
160
|
+
if (!ctx.getSpawnCwd?.() && msg.payload.path) {
|
|
161
|
+
ctx.setSpawnCwd?.(msg.payload.path);
|
|
162
|
+
}
|
|
148
163
|
ctx.renderDirBrowser();
|
|
149
164
|
return true;
|
|
150
165
|
}
|
|
@@ -165,6 +180,9 @@
|
|
|
165
180
|
ctx.dirBrowserParentPath = msg.payload.parentPath || null;
|
|
166
181
|
ctx.dirBrowserRepoRoot = msg.payload.repoRoot || null;
|
|
167
182
|
ctx.dirBrowserEntries = msg.payload.entries || [];
|
|
183
|
+
if (!ctx.getSpawnCwd?.() && msg.payload.path) {
|
|
184
|
+
ctx.setSpawnCwd?.(msg.payload.path);
|
|
185
|
+
}
|
|
168
186
|
ctx.renderDirBrowser();
|
|
169
187
|
return true;
|
|
170
188
|
}
|
package/dist/worker-runtime.js
CHANGED
|
@@ -16,6 +16,7 @@ function normalizeWorkerCwd(cwd, pluginCwd) {
|
|
|
16
16
|
return path.resolve(pluginCwd || process.cwd(), value);
|
|
17
17
|
}
|
|
18
18
|
export async function spawnManagedWorker(options, deps) {
|
|
19
|
+
console.log(`[WorkerRuntime] spawnManagedWorker start: session=${options.sessionId} plugin=${options.pluginName} cwd=${JSON.stringify(options.cwd || "")} displayName=${JSON.stringify(options.displayName || "")}`);
|
|
19
20
|
const plugin = deps.plugins.get(options.pluginName);
|
|
20
21
|
if (!plugin) {
|
|
21
22
|
throw new Error(`Plugin not found: ${options.pluginName}`);
|
|
@@ -27,7 +28,9 @@ export async function spawnManagedWorker(options, deps) {
|
|
|
27
28
|
: plugin.args,
|
|
28
29
|
cwd: normalizeWorkerCwd(options.cwd, plugin.cwd),
|
|
29
30
|
};
|
|
31
|
+
console.log(`[WorkerRuntime] resolved plugin: session=${options.sessionId} command=${JSON.stringify(runtimePlugin.command)} args=${JSON.stringify(runtimePlugin.args || [])} cwd=${JSON.stringify(runtimePlugin.cwd || process.cwd())}`);
|
|
30
32
|
await deps.sessionManager.createSession(options.sessionId, runtimePlugin);
|
|
33
|
+
console.log(`[WorkerRuntime] session created: ${options.sessionId}`);
|
|
31
34
|
const session = deps.sessionManager.getSession(options.sessionId);
|
|
32
35
|
if (!session) {
|
|
33
36
|
throw new Error(`Session failed to start: ${options.sessionId}`);
|
|
@@ -60,6 +63,7 @@ export async function spawnManagedWorker(options, deps) {
|
|
|
60
63
|
deps.workerRegistry.updateWorkerStatus(session.agentId, "running", {
|
|
61
64
|
startedAt: Date.now(),
|
|
62
65
|
});
|
|
66
|
+
console.log(`[WorkerRuntime] worker record ready: session=${options.sessionId} agent=${session.agentId}`);
|
|
63
67
|
return {
|
|
64
68
|
type: "session",
|
|
65
69
|
sessionId: options.sessionId,
|