@cydm/magic-shell-agent-node 0.1.0

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.
Files changed (58) hide show
  1. package/dist/adapters/pty-adapter.d.ts +18 -0
  2. package/dist/adapters/pty-adapter.js +99 -0
  3. package/dist/adapters/registry.d.ts +28 -0
  4. package/dist/adapters/registry.js +64 -0
  5. package/dist/adapters/rpc-adapter.d.ts +19 -0
  6. package/dist/adapters/rpc-adapter.js +182 -0
  7. package/dist/adapters/stdio-adapter.d.ts +17 -0
  8. package/dist/adapters/stdio-adapter.js +107 -0
  9. package/dist/adapters/types.d.ts +17 -0
  10. package/dist/adapters/types.js +2 -0
  11. package/dist/claude-exec.d.ts +11 -0
  12. package/dist/claude-exec.js +54 -0
  13. package/dist/claude-worker.d.ts +12 -0
  14. package/dist/claude-worker.js +163 -0
  15. package/dist/codex-exec.d.ts +12 -0
  16. package/dist/codex-exec.js +84 -0
  17. package/dist/codex-worker.d.ts +12 -0
  18. package/dist/codex-worker.js +179 -0
  19. package/dist/directory-browser.d.ts +3 -0
  20. package/dist/directory-browser.js +48 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +2 -0
  23. package/dist/local-direct-server.d.ts +38 -0
  24. package/dist/local-direct-server.js +266 -0
  25. package/dist/node-conversation.d.ts +21 -0
  26. package/dist/node-conversation.js +28 -0
  27. package/dist/node-intent.d.ts +2 -0
  28. package/dist/node-intent.js +40 -0
  29. package/dist/node-reply.d.ts +30 -0
  30. package/dist/node-reply.js +77 -0
  31. package/dist/node.d.ts +132 -0
  32. package/dist/node.js +1954 -0
  33. package/dist/pie-session-control.d.ts +21 -0
  34. package/dist/pie-session-control.js +28 -0
  35. package/dist/plugin-loader.d.ts +19 -0
  36. package/dist/plugin-loader.js +144 -0
  37. package/dist/plugins/pie.json +7 -0
  38. package/dist/primary-agent-bridge.d.ts +69 -0
  39. package/dist/primary-agent-bridge.js +282 -0
  40. package/dist/session-manager.d.ts +66 -0
  41. package/dist/session-manager.js +197 -0
  42. package/dist/terminal-metadata.d.ts +7 -0
  43. package/dist/terminal-metadata.js +52 -0
  44. package/dist/types.d.ts +1 -0
  45. package/dist/types.js +1 -0
  46. package/dist/worker-control.d.ts +15 -0
  47. package/dist/worker-control.js +89 -0
  48. package/dist/worker-narration.d.ts +25 -0
  49. package/dist/worker-narration.js +90 -0
  50. package/dist/worker-output.d.ts +6 -0
  51. package/dist/worker-output.js +72 -0
  52. package/dist/worker-registry.d.ts +45 -0
  53. package/dist/worker-registry.js +501 -0
  54. package/dist/worker-runtime.d.ts +18 -0
  55. package/dist/worker-runtime.js +69 -0
  56. package/dist/ws-client.d.ts +68 -0
  57. package/dist/ws-client.js +193 -0
  58. package/package.json +38 -0
@@ -0,0 +1,163 @@
1
+ import { stripAnsi } from "./worker-output.js";
2
+ function normalizeWhitespace(value) {
3
+ return stripAnsi(value || "")
4
+ .replace(/\r/g, "\n")
5
+ .replace(/[ \t]+\n/g, "\n")
6
+ .replace(/\n{3,}/g, "\n\n")
7
+ .trim();
8
+ }
9
+ function normalizeComparable(value) {
10
+ return normalizeWhitespace(value)
11
+ .replace(/\s+/g, " ")
12
+ .trim()
13
+ .toLowerCase();
14
+ }
15
+ function isMostlyDecoration(line) {
16
+ return /^[\u2500-\u257f\u2580-\u259f\s]+$/u.test(line)
17
+ || /^[`~!@#$%^&*()_+=\-[\]{};:'",.<>/?\\|]+$/.test(line);
18
+ }
19
+ function isClaudeNoiseLine(line) {
20
+ const normalized = line.trim();
21
+ if (!normalized)
22
+ return true;
23
+ if (normalized.length < 2)
24
+ return true;
25
+ if (normalized === "rupt")
26
+ return true;
27
+ if (isMostlyDecoration(normalized))
28
+ return true;
29
+ if (/^\?+\s*for shortcuts$/i.test(normalized))
30
+ return true;
31
+ if (/^(esc|ctrl\+c|enter)\b/i.test(normalized))
32
+ return true;
33
+ if (/^claude code$/i.test(normalized))
34
+ return true;
35
+ if (/^claude$/i.test(normalized))
36
+ return true;
37
+ if (/^╭.*claude\s*code/i.test(normalized))
38
+ return true;
39
+ if (/^╰[─-]+╯$/u.test(normalized))
40
+ return true;
41
+ if (/^model:/i.test(normalized))
42
+ return true;
43
+ if (/^cwd:/i.test(normalized))
44
+ return true;
45
+ if (/^status:/i.test(normalized))
46
+ return true;
47
+ if (/^opus now defaults/i.test(normalized))
48
+ return true;
49
+ if (/^↑\s*opus now defaults/i.test(normalized))
50
+ return true;
51
+ if (/^[│┌┐└┘├┤┬┴┼].*/u.test(normalized))
52
+ return true;
53
+ if (/^[❯›»>]\s*/u.test(normalized))
54
+ return true;
55
+ if (/^[◐◑◒◓].*/u.test(normalized))
56
+ return true;
57
+ if (/^[✢✣✤✥✦✧✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽].*/u.test(normalized))
58
+ return true;
59
+ if (/^⏵⏵\s*bypass permissions on/i.test(normalized))
60
+ return true;
61
+ if (/(shift\+tab to cycle|esc to interrupt)/i.test(normalized))
62
+ return true;
63
+ if (/\/effort\b/i.test(normalized))
64
+ return true;
65
+ if (/(percolating|thinking|pondering|brainstorming)/i.test(normalized))
66
+ return true;
67
+ if (/^▋|^▐|^▝|^▘|^█|^▛|^▜|^▌/u.test(normalized))
68
+ return true;
69
+ if (/^(thinking|working|ready|waiting)$/i.test(normalized))
70
+ return true;
71
+ if (/^(claude-|gpt-|qwen-|kimi-|local-|company-)/i.test(normalized))
72
+ return true;
73
+ return false;
74
+ }
75
+ function removeInputEcho(lines, input) {
76
+ const comparableInput = normalizeComparable(input);
77
+ if (!comparableInput)
78
+ return lines;
79
+ return lines.filter((line) => {
80
+ const comparable = normalizeComparable(line);
81
+ if (!comparable)
82
+ return false;
83
+ if (comparable === comparableInput)
84
+ return false;
85
+ if (comparableInput.includes(comparable) || comparable.includes(comparableInput))
86
+ return false;
87
+ return true;
88
+ });
89
+ }
90
+ export function isClaudeCodeWorker(agentType) {
91
+ return agentType === "claude-code";
92
+ }
93
+ export function claudeNeedsTrustConfirmation(bufferedOutput) {
94
+ const cleaned = normalizeWhitespace(bufferedOutput);
95
+ if (!cleaned)
96
+ return false;
97
+ return /quick safety check/i.test(cleaned)
98
+ || /accessing workspace:/i.test(cleaned)
99
+ || /yes,\s*i trust this folder/i.test(cleaned)
100
+ || /enter to confirm/i.test(cleaned);
101
+ }
102
+ export function formatClaudeTurnInput(message) {
103
+ if (!message)
104
+ return "\r";
105
+ if (message.endsWith("\r") || message.endsWith("\n"))
106
+ return message;
107
+ return `${message}\r`;
108
+ }
109
+ export function summarizeClaudeWorkerOutput(bufferedOutput, input = "") {
110
+ const cleaned = normalizeWhitespace(bufferedOutput);
111
+ if (!cleaned)
112
+ return "";
113
+ const lines = removeInputEcho(cleaned
114
+ .split("\n")
115
+ .map((line) => line.trim())
116
+ .filter((line) => !isClaudeNoiseLine(line)), input);
117
+ if (!lines.length)
118
+ return "";
119
+ return Array.from(new Set(lines)).slice(-4).join("\n").slice(-600);
120
+ }
121
+ export function getLastClaudeWorkerMessage(bufferedOutput, input = "") {
122
+ const summary = summarizeClaudeWorkerOutput(bufferedOutput, input);
123
+ if (!summary)
124
+ return "";
125
+ const lines = summary.split("\n").map((line) => line.trim()).filter(Boolean);
126
+ return lines[lines.length - 1] || "";
127
+ }
128
+ export function getFreshClaudeTurnResult(beforeOutput, afterOutput, input) {
129
+ const delta = afterOutput.startsWith(beforeOutput) ? afterOutput.slice(beforeOutput.length) : afterOutput;
130
+ const deltaSummary = summarizeClaudeWorkerOutput(delta, input);
131
+ if (deltaSummary) {
132
+ const deltaLines = deltaSummary.split("\n").map((line) => line.trim()).filter(Boolean);
133
+ return {
134
+ message: deltaLines[deltaLines.length - 1] || "",
135
+ summary: deltaSummary,
136
+ changed: true,
137
+ };
138
+ }
139
+ const wholeSummary = summarizeClaudeWorkerOutput(afterOutput, input);
140
+ if (!wholeSummary) {
141
+ return { message: "", summary: "", changed: false };
142
+ }
143
+ const beforeSummary = summarizeClaudeWorkerOutput(beforeOutput, input);
144
+ if (wholeSummary === beforeSummary) {
145
+ return { message: "", summary: wholeSummary, changed: false };
146
+ }
147
+ const wholeLines = wholeSummary.split("\n").map((line) => line.trim()).filter(Boolean);
148
+ return {
149
+ message: wholeLines[wholeLines.length - 1] || "",
150
+ summary: wholeSummary,
151
+ changed: true,
152
+ };
153
+ }
154
+ export function isClaudeReadyForTask(bufferedOutput) {
155
+ const cleaned = normalizeWhitespace(bufferedOutput);
156
+ if (!cleaned)
157
+ return false;
158
+ if (claudeNeedsTrustConfirmation(cleaned))
159
+ return false;
160
+ return /for shortcuts/i.test(cleaned)
161
+ || /claude code/i.test(cleaned)
162
+ || cleaned.length > 80;
163
+ }
@@ -0,0 +1,12 @@
1
+ export interface CodexExecOptions {
2
+ cwd: string;
3
+ message: string;
4
+ timeoutMs: number;
5
+ }
6
+ export interface CodexExecResult {
7
+ message: string;
8
+ summary: string;
9
+ rawOutput: string;
10
+ }
11
+ export declare function extractCodexExecResult(rawOutput: string): CodexExecResult;
12
+ export declare function runCodexExec(options: CodexExecOptions): Promise<CodexExecResult>;
@@ -0,0 +1,84 @@
1
+ import { spawn } from "node:child_process";
2
+ function isRecord(value) {
3
+ return !!value && typeof value === "object";
4
+ }
5
+ export function extractCodexExecResult(rawOutput) {
6
+ const messages = [];
7
+ for (const line of rawOutput.split("\n")) {
8
+ const trimmed = line.trim();
9
+ if (!trimmed.startsWith("{"))
10
+ continue;
11
+ try {
12
+ const parsed = JSON.parse(trimmed);
13
+ if (parsed.type === "item.completed"
14
+ && isRecord(parsed.item)
15
+ && parsed.item.type === "agent_message"
16
+ && typeof parsed.item.text === "string"
17
+ && parsed.item.text.trim()) {
18
+ messages.push(parsed.item.text.trim());
19
+ }
20
+ }
21
+ catch {
22
+ continue;
23
+ }
24
+ }
25
+ const uniqueMessages = Array.from(new Set(messages));
26
+ const summary = uniqueMessages.join("\n").trim();
27
+ return {
28
+ message: uniqueMessages[uniqueMessages.length - 1] || "",
29
+ summary,
30
+ rawOutput,
31
+ };
32
+ }
33
+ export async function runCodexExec(options) {
34
+ return new Promise((resolve, reject) => {
35
+ const child = spawn("codex", [
36
+ "exec",
37
+ "--json",
38
+ "--skip-git-repo-check",
39
+ "--dangerously-bypass-approvals-and-sandbox",
40
+ "-C",
41
+ options.cwd,
42
+ options.message,
43
+ ], {
44
+ cwd: options.cwd,
45
+ env: process.env,
46
+ stdio: ["ignore", "pipe", "pipe"],
47
+ });
48
+ let stdout = "";
49
+ let stderr = "";
50
+ let settled = false;
51
+ const finish = (handler) => {
52
+ if (settled)
53
+ return;
54
+ settled = true;
55
+ clearTimeout(timeoutHandle);
56
+ handler();
57
+ };
58
+ child.stdout.on("data", (chunk) => {
59
+ stdout += chunk.toString();
60
+ });
61
+ child.stderr.on("data", (chunk) => {
62
+ stderr += chunk.toString();
63
+ });
64
+ child.on("error", (error) => {
65
+ finish(() => reject(error));
66
+ });
67
+ child.on("close", (code) => {
68
+ finish(() => {
69
+ const rawOutput = [stdout, stderr].filter(Boolean).join("");
70
+ if (code !== 0) {
71
+ reject(new Error(rawOutput.trim() || `codex exec exited with code ${code}`));
72
+ return;
73
+ }
74
+ resolve(extractCodexExecResult(rawOutput));
75
+ });
76
+ });
77
+ const timeoutHandle = setTimeout(() => {
78
+ child.kill("SIGTERM");
79
+ const error = new Error(`codex exec timed out after ${options.timeoutMs}ms`);
80
+ error.code = "CODEX_EXEC_TIMEOUT";
81
+ finish(() => reject(error));
82
+ }, Math.max(options.timeoutMs, 250));
83
+ });
84
+ }
@@ -0,0 +1,12 @@
1
+ export interface CodexTurnResult {
2
+ message: string;
3
+ summary: string;
4
+ changed: boolean;
5
+ }
6
+ export declare function isCodexWorker(agentType: string | undefined): boolean;
7
+ export declare function codexNeedsTrustConfirmation(bufferedOutput: string): boolean;
8
+ export declare function isCodexReadyForTask(bufferedOutput: string): boolean;
9
+ export declare function formatCodexTurnInput(message: string): string;
10
+ export declare function summarizeCodexWorkerOutput(bufferedOutput: string, input?: string): string;
11
+ export declare function getLastCodexWorkerMessage(bufferedOutput: string, input?: string): string;
12
+ export declare function getFreshCodexTurnResult(beforeOutput: string, afterOutput: string, input: string): CodexTurnResult;
@@ -0,0 +1,179 @@
1
+ import { stripAnsi } from "./worker-output.js";
2
+ function normalizeWhitespace(value) {
3
+ return stripAnsi(value || "")
4
+ .replace(/\r/g, "\n")
5
+ .replace(/[ \t]+\n/g, "\n")
6
+ .replace(/\n{3,}/g, "\n\n")
7
+ .trim();
8
+ }
9
+ function normalizeComparable(value) {
10
+ return normalizeWhitespace(value)
11
+ .replace(/\s+/g, " ")
12
+ .trim()
13
+ .toLowerCase();
14
+ }
15
+ function isCodexNoiseLine(line) {
16
+ const normalized = line.trim();
17
+ if (!normalized)
18
+ return true;
19
+ if (normalized.length < 2)
20
+ return true;
21
+ if (/^\d+\.?$/i.test(normalized))
22
+ return true;
23
+ if (/^v?\d+\.\d+\.\d+$/i.test(normalized))
24
+ return true;
25
+ if (/openai codex/i.test(normalized))
26
+ return true;
27
+ if (/^>_\s*openai codex/i.test(normalized))
28
+ return true;
29
+ if (/^model:\s*/i.test(normalized))
30
+ return true;
31
+ if (/^directory:\s*/i.test(normalized))
32
+ return true;
33
+ if (/^tip:\s*/i.test(normalized))
34
+ return true;
35
+ if (/^press enter to continue$/i.test(normalized))
36
+ return true;
37
+ if (/^do you trust the contents of this directory\?/i.test(normalized))
38
+ return true;
39
+ if (/^working with untrusted contents/i.test(normalized))
40
+ return true;
41
+ if (/prompt injection/i.test(normalized))
42
+ return true;
43
+ if (/^1\.\s*yes,\s*continue/i.test(normalized))
44
+ return true;
45
+ if (/^2\.\s*no,\s*quit/i.test(normalized))
46
+ return true;
47
+ if (/^find and fix a bug in @filename$/i.test(normalized))
48
+ return true;
49
+ if (/^gpt-[\w.-]+\s+.+% left/i.test(normalized))
50
+ return true;
51
+ if (/^\d+% left\b/i.test(normalized))
52
+ return true;
53
+ if (/^working \(\d+s/i.test(normalized))
54
+ return true;
55
+ if (/^(esc to interrupt|press enter to continue)$/i.test(normalized))
56
+ return true;
57
+ if (/^(you are in|continue anyway\? \[y\/n\]:)/i.test(normalized))
58
+ return true;
59
+ if (/^https?:\/\//i.test(normalized))
60
+ return true;
61
+ if (/^[╭╰│─]+$/u.test(normalized))
62
+ return true;
63
+ return false;
64
+ }
65
+ function removeInputEcho(lines, input) {
66
+ const comparableInput = normalizeComparable(input);
67
+ if (!comparableInput)
68
+ return lines;
69
+ return lines.filter((line) => {
70
+ const comparable = normalizeComparable(line);
71
+ if (!comparable)
72
+ return false;
73
+ if (comparable === comparableInput)
74
+ return false;
75
+ if (comparableInput.includes(comparable) || comparable.includes(comparableInput))
76
+ return false;
77
+ return true;
78
+ });
79
+ }
80
+ function cleanCodexLine(line) {
81
+ return line
82
+ .replace(/^[•●◦▪▸▹►▻]\s*/u, "")
83
+ .replace(/^[›>]\s*/u, "")
84
+ .trim();
85
+ }
86
+ function stripInlineCodexNoise(value) {
87
+ return value
88
+ .replace(/│\s*model:[^│]*│/gi, " ")
89
+ .replace(/│\s*directory:[^│]*│/gi, " ")
90
+ .replace(/[╭╰│─]+/gu, " ")
91
+ .replace(/working\s*\(\d+s\s*•\s*esc to interrupt\)/gi, " ")
92
+ .replace(/gpt-[\w.-]+\s+medium\s+·\s+\d+% left\s+·\s+[^\n]+/gi, " ")
93
+ .replace(/[›>]\s*implement \{feature\}/gi, " ")
94
+ .replace(/find and fix a bug in @filename/gi, " ")
95
+ .replace(/[•◦]\d+/g, " ")
96
+ .replace(/W[◦•]?(?:W|o|r|k|i|n|g|\d|•|◦)+/g, " ")
97
+ .replace(/\s{2,}/g, " ")
98
+ .trim();
99
+ }
100
+ function extractSentenceCandidates(line) {
101
+ const normalized = stripInlineCodexNoise(line);
102
+ if (!normalized)
103
+ return [];
104
+ const matches = normalized.match(/[^.!?。!?\n]+[.!?。!?]/g);
105
+ if (matches?.length) {
106
+ return [matches.map((item) => item.trim()).filter(Boolean).join(" ").trim()].filter(Boolean);
107
+ }
108
+ return [normalized];
109
+ }
110
+ export function isCodexWorker(agentType) {
111
+ return agentType === "codex";
112
+ }
113
+ export function codexNeedsTrustConfirmation(bufferedOutput) {
114
+ const cleaned = normalizeWhitespace(bufferedOutput);
115
+ if (!cleaned)
116
+ return false;
117
+ return /do you trust the contents of this directory\?/i.test(cleaned)
118
+ || /press enter to continue/i.test(cleaned);
119
+ }
120
+ export function isCodexReadyForTask(bufferedOutput) {
121
+ const cleaned = normalizeWhitespace(bufferedOutput);
122
+ if (!cleaned)
123
+ return false;
124
+ return /openai codex/i.test(cleaned)
125
+ || /find and fix a bug in @filename/i.test(cleaned)
126
+ || /gpt-[\w.-]+.+% left/i.test(cleaned);
127
+ }
128
+ export function formatCodexTurnInput(message) {
129
+ if (!message)
130
+ return "\r";
131
+ if (message.endsWith("\r") || message.endsWith("\n"))
132
+ return message;
133
+ return `${message}\r`;
134
+ }
135
+ export function summarizeCodexWorkerOutput(bufferedOutput, input = "") {
136
+ const cleaned = normalizeWhitespace(bufferedOutput);
137
+ if (!cleaned)
138
+ return "";
139
+ const lines = removeInputEcho(cleaned
140
+ .split("\n")
141
+ .flatMap((line) => extractSentenceCandidates(cleanCodexLine(line)))
142
+ .filter((line) => !isCodexNoiseLine(line)), input);
143
+ if (!lines.length)
144
+ return "";
145
+ return Array.from(new Set(lines)).slice(-4).join("\n").slice(-600);
146
+ }
147
+ export function getLastCodexWorkerMessage(bufferedOutput, input = "") {
148
+ const summary = summarizeCodexWorkerOutput(bufferedOutput, input);
149
+ if (!summary)
150
+ return "";
151
+ const lines = summary.split("\n").map((line) => line.trim()).filter(Boolean);
152
+ return lines[lines.length - 1] || "";
153
+ }
154
+ export function getFreshCodexTurnResult(beforeOutput, afterOutput, input) {
155
+ const delta = afterOutput.startsWith(beforeOutput) ? afterOutput.slice(beforeOutput.length) : afterOutput;
156
+ const deltaSummary = summarizeCodexWorkerOutput(delta, input);
157
+ if (deltaSummary) {
158
+ const deltaLines = deltaSummary.split("\n").map((line) => line.trim()).filter(Boolean);
159
+ return {
160
+ message: deltaLines[deltaLines.length - 1] || "",
161
+ summary: deltaSummary,
162
+ changed: true,
163
+ };
164
+ }
165
+ const wholeSummary = summarizeCodexWorkerOutput(afterOutput, input);
166
+ if (!wholeSummary) {
167
+ return { message: "", summary: "", changed: false };
168
+ }
169
+ const beforeSummary = summarizeCodexWorkerOutput(beforeOutput, input);
170
+ if (wholeSummary === beforeSummary) {
171
+ return { message: "", summary: wholeSummary, changed: false };
172
+ }
173
+ const wholeLines = wholeSummary.split("\n").map((line) => line.trim()).filter(Boolean);
174
+ return {
175
+ message: wholeLines[wholeLines.length - 1] || "",
176
+ summary: wholeSummary,
177
+ changed: true,
178
+ };
179
+ }
@@ -0,0 +1,3 @@
1
+ import type { ServerMessage } from "./types.js";
2
+ export declare function buildDirectoryList(requestedPath?: string): Promise<ServerMessage>;
3
+ export declare function findRepoRoot(startPath: string): Promise<string | null>;
@@ -0,0 +1,48 @@
1
+ import { access, readdir } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ export async function buildDirectoryList(requestedPath) {
5
+ const normalizedPath = path.resolve(requestedPath || os.homedir());
6
+ try {
7
+ const entries = await readdir(normalizedPath, { withFileTypes: true });
8
+ const directories = entries
9
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
10
+ .map((entry) => ({
11
+ name: entry.name,
12
+ path: path.join(normalizedPath, entry.name),
13
+ }))
14
+ .sort((a, b) => a.name.localeCompare(b.name));
15
+ const parentPath = path.dirname(normalizedPath);
16
+ const repoRoot = await findRepoRoot(normalizedPath);
17
+ return {
18
+ type: "dir_list",
19
+ path: normalizedPath,
20
+ parentPath: parentPath === normalizedPath ? null : parentPath,
21
+ repoRoot,
22
+ entries: directories,
23
+ };
24
+ }
25
+ catch (err) {
26
+ return {
27
+ type: "error",
28
+ error: err instanceof Error ? err.message : String(err),
29
+ };
30
+ }
31
+ }
32
+ export async function findRepoRoot(startPath) {
33
+ let currentPath = startPath;
34
+ while (true) {
35
+ try {
36
+ await access(path.join(currentPath, ".git"));
37
+ return currentPath;
38
+ }
39
+ catch {
40
+ // keep walking up
41
+ }
42
+ const parentPath = path.dirname(currentPath);
43
+ if (parentPath === currentPath) {
44
+ return null;
45
+ }
46
+ currentPath = parentPath;
47
+ }
48
+ }
@@ -0,0 +1,2 @@
1
+ export { AgentNode, type NodeOptions, main as runAgentNode } from "./node.js";
2
+ export { getDefaultPluginDir } from "./plugin-loader.js";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { AgentNode, main as runAgentNode } from "./node.js";
2
+ export { getDefaultPluginDir } from "./plugin-loader.js";
@@ -0,0 +1,38 @@
1
+ import type { ClientMessage, ServerMessage } from "@cydm/magic-shell-protocol";
2
+ export interface LocalDirectServerOptions {
3
+ host: string;
4
+ port: number;
5
+ nodeId: string;
6
+ password: string;
7
+ }
8
+ export interface LocalDirectServerDelegate {
9
+ handleIncomingMessage(message: ClientMessage | ServerMessage): Promise<void>;
10
+ handleSubscriptionChange(sessionId: string, attachedBrowserCount: number): Promise<void> | void;
11
+ }
12
+ export declare class LocalDirectServer {
13
+ private readonly options;
14
+ private readonly delegate;
15
+ private httpServer;
16
+ private wsServer;
17
+ private connections;
18
+ private sessionToConnections;
19
+ private workbenchRoot;
20
+ constructor(options: LocalDirectServerOptions, delegate: LocalDirectServerDelegate);
21
+ start(): Promise<{
22
+ host: string;
23
+ port: number;
24
+ webUrl: string;
25
+ wsUrl: string;
26
+ }>;
27
+ stop(): Promise<void>;
28
+ handleNodeMessage(message: ServerMessage): void;
29
+ private handleHttpRequest;
30
+ private handleSocketConnection;
31
+ private handleBrowserMessage;
32
+ private attachConnectionToSession;
33
+ private detachConnectionFromSession;
34
+ private cleanupConnection;
35
+ private sendToSession;
36
+ private broadcast;
37
+ private send;
38
+ }