@cydm/magic-shell-agent-node 0.1.2 → 0.1.4
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 +4 -1
- package/dist/plugin-loader.js +59 -1
- 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
|
});
|
|
@@ -858,7 +861,7 @@ export class AgentNode {
|
|
|
858
861
|
this.sendControlResult(source, message.requestId, name, false, { error: "Worker not found" });
|
|
859
862
|
return;
|
|
860
863
|
}
|
|
861
|
-
const detail = this.workerRegistry.getWorkerDetail(worker.agentId, this.
|
|
864
|
+
const detail = this.workerRegistry.getWorkerDetail(worker.agentId, this.sessionManager.getBufferedOutput(worker.sessionId));
|
|
862
865
|
if (!detail) {
|
|
863
866
|
this.sendControlResult(source, message.requestId, name, false, { error: "Worker detail unavailable" });
|
|
864
867
|
return;
|
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
|
*/
|
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,
|