dirac-lang 0.1.60 → 0.1.61
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/agent-OXQZBLNQ.js +226 -0
- package/dist/chunk-AU3YU63U.js +208 -0
- package/dist/cli.js +104 -1
- package/dist/session-client-3VTC5MLO.js +177 -0
- package/dist/session-server-QZN2RMYD.js +14 -0
- package/dist/{shell-WFLXZW3I.js → shell-OFBL57OA.js} +72 -19
- package/package.json +1 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SessionServer,
|
|
3
|
+
getSocketPath,
|
|
4
|
+
isSessionRunning
|
|
5
|
+
} from "./chunk-AU3YU63U.js";
|
|
6
|
+
import "./chunk-ZAA6R2TC.js";
|
|
7
|
+
import "./chunk-HRHAMPOB.js";
|
|
8
|
+
import "./chunk-NKA6ZJDV.js";
|
|
9
|
+
import "./chunk-3UW6GWYQ.js";
|
|
10
|
+
|
|
11
|
+
// src/agent.ts
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import os from "os";
|
|
15
|
+
import { spawn } from "child_process";
|
|
16
|
+
var AGENT_DIR = path.join(os.homedir(), ".dirac");
|
|
17
|
+
var PID_FILE = path.join(AGENT_DIR, "session.pid");
|
|
18
|
+
var LOG_FILE = path.join(AGENT_DIR, "agent.log");
|
|
19
|
+
var AgentCLI = class {
|
|
20
|
+
async start() {
|
|
21
|
+
if (isSessionRunning()) {
|
|
22
|
+
console.log("Agent is already running");
|
|
23
|
+
const pid = this.getRunningPid();
|
|
24
|
+
if (pid) {
|
|
25
|
+
console.log(`PID: ${pid}`);
|
|
26
|
+
console.log(`Socket: ${getSocketPath()}`);
|
|
27
|
+
console.log(`Logs: ${LOG_FILE}`);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!fs.existsSync(AGENT_DIR)) {
|
|
32
|
+
fs.mkdirSync(AGENT_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
console.log("Starting DIRAC agent...");
|
|
35
|
+
const logFd = fs.openSync(LOG_FILE, "a");
|
|
36
|
+
const child = spawn(
|
|
37
|
+
process.argv[0],
|
|
38
|
+
// node executable
|
|
39
|
+
[process.argv[1], "agent", "daemon"],
|
|
40
|
+
// dirac agent daemon
|
|
41
|
+
{
|
|
42
|
+
detached: true,
|
|
43
|
+
stdio: ["ignore", logFd, logFd],
|
|
44
|
+
// Redirect stdout/stderr to log file
|
|
45
|
+
cwd: process.cwd()
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
fs.closeSync(logFd);
|
|
49
|
+
child.unref();
|
|
50
|
+
let attempts = 0;
|
|
51
|
+
const maxAttempts = 10;
|
|
52
|
+
while (attempts < maxAttempts) {
|
|
53
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
54
|
+
if (isSessionRunning()) {
|
|
55
|
+
console.log("\u2713 Agent started successfully");
|
|
56
|
+
console.log(` PID: ${child.pid}`);
|
|
57
|
+
console.log(` Socket: ${getSocketPath()}`);
|
|
58
|
+
console.log(` Logs: ${LOG_FILE}`);
|
|
59
|
+
console.log("");
|
|
60
|
+
console.log("Connect with: dirac shell --agent");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
attempts++;
|
|
64
|
+
}
|
|
65
|
+
console.error("\u2717 Failed to start agent");
|
|
66
|
+
console.error("Check logs:", LOG_FILE);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
async stop() {
|
|
70
|
+
const pid = this.getRunningPid();
|
|
71
|
+
if (!pid) {
|
|
72
|
+
console.log("Agent is not running");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(`Stopping agent (PID: ${pid})...`);
|
|
76
|
+
try {
|
|
77
|
+
process.kill(pid, "SIGTERM");
|
|
78
|
+
let attempts = 0;
|
|
79
|
+
while (attempts < 10) {
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
81
|
+
try {
|
|
82
|
+
process.kill(pid, 0);
|
|
83
|
+
attempts++;
|
|
84
|
+
} catch {
|
|
85
|
+
console.log("\u2713 Agent stopped");
|
|
86
|
+
this.cleanup();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
console.log("Agent did not stop gracefully, forcing...");
|
|
91
|
+
process.kill(pid, "SIGKILL");
|
|
92
|
+
this.cleanup();
|
|
93
|
+
console.log("\u2713 Agent stopped (forced)");
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error.code === "ESRCH") {
|
|
96
|
+
console.log("Agent was not running");
|
|
97
|
+
this.cleanup();
|
|
98
|
+
} else {
|
|
99
|
+
console.error("Error stopping agent:", error.message);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async status() {
|
|
105
|
+
const pid = this.getRunningPid();
|
|
106
|
+
if (!pid) {
|
|
107
|
+
console.log("Status: NOT RUNNING");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
process.kill(pid, 0);
|
|
112
|
+
console.log("Status: RUNNING");
|
|
113
|
+
console.log(`PID: ${pid}`);
|
|
114
|
+
console.log(`Socket: ${getSocketPath()}`);
|
|
115
|
+
console.log(`Logs: ${LOG_FILE}`);
|
|
116
|
+
if (fs.existsSync(PID_FILE)) {
|
|
117
|
+
const stats = fs.statSync(PID_FILE);
|
|
118
|
+
const uptime = Date.now() - stats.mtimeMs;
|
|
119
|
+
console.log(`Uptime: ${this.formatUptime(uptime)}`);
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error.code === "ESRCH") {
|
|
123
|
+
console.log("Status: NOT RUNNING (stale PID file)");
|
|
124
|
+
this.cleanup();
|
|
125
|
+
} else {
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async restart() {
|
|
131
|
+
console.log("Restarting agent...");
|
|
132
|
+
await this.stop();
|
|
133
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
134
|
+
await this.start();
|
|
135
|
+
}
|
|
136
|
+
async logs(follow = false) {
|
|
137
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
138
|
+
console.log("No logs found");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (follow) {
|
|
142
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
143
|
+
const tail = spawn2("tail", ["-f", LOG_FILE], {
|
|
144
|
+
stdio: "inherit"
|
|
145
|
+
});
|
|
146
|
+
process.on("SIGINT", () => {
|
|
147
|
+
tail.kill();
|
|
148
|
+
process.exit(0);
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
const content = fs.readFileSync(LOG_FILE, "utf-8");
|
|
152
|
+
const lines = content.split("\n");
|
|
153
|
+
const lastLines = lines.slice(-50);
|
|
154
|
+
console.log(lastLines.join("\n"));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
getRunningPid() {
|
|
158
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const pidStr = fs.readFileSync(PID_FILE, "utf-8").trim();
|
|
163
|
+
return parseInt(pidStr, 10);
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
cleanup() {
|
|
169
|
+
if (fs.existsSync(PID_FILE)) {
|
|
170
|
+
fs.unlinkSync(PID_FILE);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
formatUptime(ms) {
|
|
174
|
+
const seconds = Math.floor(ms / 1e3);
|
|
175
|
+
const minutes = Math.floor(seconds / 60);
|
|
176
|
+
const hours = Math.floor(minutes / 60);
|
|
177
|
+
const days = Math.floor(hours / 24);
|
|
178
|
+
if (days > 0) return `${days}d ${hours % 24}h`;
|
|
179
|
+
if (hours > 0) return `${hours}h ${minutes % 60}m`;
|
|
180
|
+
if (minutes > 0) return `${minutes}m`;
|
|
181
|
+
return `${seconds}s`;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
async function runAgentDaemon() {
|
|
185
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Starting DIRAC agent daemon`);
|
|
186
|
+
let config = {};
|
|
187
|
+
try {
|
|
188
|
+
const fs2 = await import("fs");
|
|
189
|
+
const path2 = await import("path");
|
|
190
|
+
const yaml = await import("js-yaml");
|
|
191
|
+
const configPath = path2.default.resolve(process.cwd(), "config.yml");
|
|
192
|
+
if (fs2.default.existsSync(configPath)) {
|
|
193
|
+
const configData = yaml.default.load(fs2.default.readFileSync(configPath, "utf-8"));
|
|
194
|
+
config = {
|
|
195
|
+
llmProvider: configData.llmProvider,
|
|
196
|
+
llmModel: configData.llmModel,
|
|
197
|
+
customLLMUrl: configData.customLLMUrl,
|
|
198
|
+
initScript: configData.initScript
|
|
199
|
+
};
|
|
200
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Loaded config: LLM=${config.llmProvider}/${config.llmModel}`);
|
|
201
|
+
}
|
|
202
|
+
} catch (err) {
|
|
203
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] No config.yml found, using defaults`);
|
|
204
|
+
}
|
|
205
|
+
const server = new SessionServer(config);
|
|
206
|
+
try {
|
|
207
|
+
await server.start();
|
|
208
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Agent daemon started successfully`);
|
|
209
|
+
const shutdown = async () => {
|
|
210
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] Shutting down agent daemon`);
|
|
211
|
+
await server.shutdown();
|
|
212
|
+
process.exit(0);
|
|
213
|
+
};
|
|
214
|
+
process.on("SIGTERM", shutdown);
|
|
215
|
+
process.on("SIGINT", shutdown);
|
|
216
|
+
await new Promise(() => {
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] Failed to start agent:`, error);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
export {
|
|
224
|
+
AgentCLI,
|
|
225
|
+
runAgentDaemon
|
|
226
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
integrate
|
|
3
|
+
} from "./chunk-ZAA6R2TC.js";
|
|
4
|
+
import {
|
|
5
|
+
DiracParser
|
|
6
|
+
} from "./chunk-HRHAMPOB.js";
|
|
7
|
+
import {
|
|
8
|
+
createSession
|
|
9
|
+
} from "./chunk-3UW6GWYQ.js";
|
|
10
|
+
|
|
11
|
+
// src/session-server.ts
|
|
12
|
+
import net from "net";
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import os from "os";
|
|
16
|
+
var SOCKET_DIR = path.join(os.homedir(), ".dirac");
|
|
17
|
+
var SOCKET_PATH = path.join(SOCKET_DIR, "session.sock");
|
|
18
|
+
var PID_PATH = path.join(SOCKET_DIR, "session.pid");
|
|
19
|
+
var SessionServer = class {
|
|
20
|
+
server;
|
|
21
|
+
session;
|
|
22
|
+
clients = /* @__PURE__ */ new Map();
|
|
23
|
+
clientIdCounter = 0;
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.session = createSession(config || {});
|
|
26
|
+
this.server = net.createServer(this.handleConnection.bind(this));
|
|
27
|
+
}
|
|
28
|
+
async start() {
|
|
29
|
+
if (!fs.existsSync(SOCKET_DIR)) {
|
|
30
|
+
fs.mkdirSync(SOCKET_DIR, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
if (fs.existsSync(SOCKET_PATH)) {
|
|
33
|
+
fs.unlinkSync(SOCKET_PATH);
|
|
34
|
+
}
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
this.server.listen(SOCKET_PATH, () => {
|
|
37
|
+
console.log(`[session-server] Listening on ${SOCKET_PATH}`);
|
|
38
|
+
fs.writeFileSync(PID_PATH, process.pid.toString());
|
|
39
|
+
fs.chmodSync(SOCKET_PATH, 384);
|
|
40
|
+
resolve();
|
|
41
|
+
});
|
|
42
|
+
this.server.on("error", (error) => {
|
|
43
|
+
console.error("[session-server] Error:", error);
|
|
44
|
+
reject(error);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
handleConnection(socket) {
|
|
49
|
+
const clientId = `client-${++this.clientIdCounter}`;
|
|
50
|
+
const client = { id: clientId, socket };
|
|
51
|
+
this.clients.set(clientId, client);
|
|
52
|
+
console.log(`[session-server] Client connected: ${clientId}`);
|
|
53
|
+
let buffer = "";
|
|
54
|
+
socket.on("data", async (data) => {
|
|
55
|
+
buffer += data.toString();
|
|
56
|
+
const lines = buffer.split("\n");
|
|
57
|
+
buffer = lines.pop() || "";
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
if (!line.trim()) continue;
|
|
60
|
+
try {
|
|
61
|
+
const message = JSON.parse(line);
|
|
62
|
+
await this.handleMessage(client, message);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.sendError(client, `Invalid message: ${error}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
socket.on("end", () => {
|
|
69
|
+
console.log(`[session-server] Client disconnected: ${clientId}`);
|
|
70
|
+
this.clients.delete(clientId);
|
|
71
|
+
});
|
|
72
|
+
socket.on("error", (error) => {
|
|
73
|
+
console.error(`[session-server] Client error (${clientId}):`, error);
|
|
74
|
+
this.clients.delete(clientId);
|
|
75
|
+
});
|
|
76
|
+
this.sendResponse(client, {
|
|
77
|
+
type: "welcome",
|
|
78
|
+
sessionId: clientId,
|
|
79
|
+
message: "Connected to DIRAC session"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async handleMessage(client, message) {
|
|
83
|
+
const { type, data } = message;
|
|
84
|
+
switch (type) {
|
|
85
|
+
case "execute":
|
|
86
|
+
await this.executeCommand(client, data.command);
|
|
87
|
+
break;
|
|
88
|
+
case "getVars":
|
|
89
|
+
this.sendResponse(client, {
|
|
90
|
+
type: "vars",
|
|
91
|
+
data: Object.keys(this.session.variables)
|
|
92
|
+
});
|
|
93
|
+
break;
|
|
94
|
+
case "getState":
|
|
95
|
+
this.sendResponse(client, {
|
|
96
|
+
type: "state",
|
|
97
|
+
data: {
|
|
98
|
+
variables: this.session.variables,
|
|
99
|
+
subroutines: this.session.subroutines,
|
|
100
|
+
varBoundary: this.session.varBoundary,
|
|
101
|
+
subBoundary: this.session.subBoundary
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
case "shutdown":
|
|
106
|
+
this.sendResponse(client, {
|
|
107
|
+
type: "shutdown",
|
|
108
|
+
message: "Server shutting down"
|
|
109
|
+
});
|
|
110
|
+
await this.shutdown();
|
|
111
|
+
break;
|
|
112
|
+
default:
|
|
113
|
+
this.sendError(client, `Unknown message type: ${type}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async executeCommand(client, command) {
|
|
117
|
+
try {
|
|
118
|
+
this.session.output = [];
|
|
119
|
+
const parser = new DiracParser();
|
|
120
|
+
const ast = parser.parse(command);
|
|
121
|
+
await integrate(this.session, ast);
|
|
122
|
+
const output = this.session.output.join("");
|
|
123
|
+
this.sendResponse(client, {
|
|
124
|
+
type: "output",
|
|
125
|
+
data: output
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
this.sendError(client, error.message);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
sendResponse(client, response) {
|
|
132
|
+
try {
|
|
133
|
+
client.socket.write(JSON.stringify(response) + "\n");
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error(`[session-server] Failed to send response:`, error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
sendError(client, message) {
|
|
139
|
+
this.sendResponse(client, {
|
|
140
|
+
type: "error",
|
|
141
|
+
message
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async shutdown() {
|
|
145
|
+
console.log("[session-server] Shutting down...");
|
|
146
|
+
for (const client of this.clients.values()) {
|
|
147
|
+
client.socket.end();
|
|
148
|
+
}
|
|
149
|
+
this.clients.clear();
|
|
150
|
+
return new Promise((resolve) => {
|
|
151
|
+
this.server.close(() => {
|
|
152
|
+
if (fs.existsSync(SOCKET_PATH)) {
|
|
153
|
+
fs.unlinkSync(SOCKET_PATH);
|
|
154
|
+
}
|
|
155
|
+
if (fs.existsSync(PID_PATH)) {
|
|
156
|
+
fs.unlinkSync(PID_PATH);
|
|
157
|
+
}
|
|
158
|
+
console.log("[session-server] Shutdown complete");
|
|
159
|
+
resolve();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
function isSessionRunning() {
|
|
165
|
+
if (!fs.existsSync(PID_PATH)) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const pid = parseInt(fs.readFileSync(PID_PATH, "utf-8").trim(), 10);
|
|
170
|
+
process.kill(pid, 0);
|
|
171
|
+
return true;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (fs.existsSync(PID_PATH)) fs.unlinkSync(PID_PATH);
|
|
174
|
+
try {
|
|
175
|
+
if (fs.existsSync(SOCKET_PATH)) fs.unlinkSync(SOCKET_PATH);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function getSocketPath() {
|
|
182
|
+
return SOCKET_PATH;
|
|
183
|
+
}
|
|
184
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
185
|
+
const server = new SessionServer();
|
|
186
|
+
server.start().then(() => {
|
|
187
|
+
console.log("[session-server] Server started successfully");
|
|
188
|
+
process.on("SIGINT", async () => {
|
|
189
|
+
console.log("\n[session-server] Received SIGINT, shutting down...");
|
|
190
|
+
await server.shutdown();
|
|
191
|
+
process.exit(0);
|
|
192
|
+
});
|
|
193
|
+
process.on("SIGTERM", async () => {
|
|
194
|
+
console.log("\n[session-server] Received SIGTERM, shutting down...");
|
|
195
|
+
await server.shutdown();
|
|
196
|
+
process.exit(0);
|
|
197
|
+
});
|
|
198
|
+
}).catch((error) => {
|
|
199
|
+
console.error("[session-server] Failed to start:", error);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export {
|
|
205
|
+
SessionServer,
|
|
206
|
+
isSessionRunning,
|
|
207
|
+
getSocketPath
|
|
208
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -75,9 +75,22 @@ async function main() {
|
|
|
75
75
|
if (args.includes("--help") || args.includes("-h")) {
|
|
76
76
|
console.log("Usage: dirac <file.di|file.bk>");
|
|
77
77
|
console.log(" dirac shell [options]");
|
|
78
|
+
console.log(" dirac agent <command>");
|
|
78
79
|
console.log("");
|
|
79
80
|
console.log("Commands:");
|
|
80
81
|
console.log(" shell Start interactive shell (REPL)");
|
|
82
|
+
console.log(" shell --agent Connect shell to running agent daemon");
|
|
83
|
+
console.log(" agent start Start persistent agent daemon");
|
|
84
|
+
console.log(" agent stop Stop agent daemon");
|
|
85
|
+
console.log(" agent status Check agent status");
|
|
86
|
+
console.log(" agent restart Restart agent daemon");
|
|
87
|
+
console.log(" agent logs Show agent logs");
|
|
88
|
+
console.log(" shell --daemon Start shell with persistent daemon (experimental)");
|
|
89
|
+
console.log("");
|
|
90
|
+
console.log("Shell tips:");
|
|
91
|
+
console.log(" Press Ctrl-Z to suspend shell, then `bg` to run in background");
|
|
92
|
+
console.log(" Use `fg` to bring it back to foreground");
|
|
93
|
+
console.log(" Cron jobs and run-at tasks continue running in background");
|
|
81
94
|
console.log("");
|
|
82
95
|
console.log("File formats:");
|
|
83
96
|
console.log(" .di XML notation (verbose)");
|
|
@@ -98,13 +111,54 @@ async function main() {
|
|
|
98
111
|
console.log(package_default.version);
|
|
99
112
|
process.exit(0);
|
|
100
113
|
}
|
|
114
|
+
if (args[0] === "agent") {
|
|
115
|
+
const subcommand = args[1];
|
|
116
|
+
if (subcommand === "daemon") {
|
|
117
|
+
const { runAgentDaemon } = await import("./agent-OXQZBLNQ.js");
|
|
118
|
+
await runAgentDaemon();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const { AgentCLI } = await import("./agent-OXQZBLNQ.js");
|
|
122
|
+
const agent = new AgentCLI();
|
|
123
|
+
switch (subcommand) {
|
|
124
|
+
case "start":
|
|
125
|
+
await agent.start();
|
|
126
|
+
break;
|
|
127
|
+
case "stop":
|
|
128
|
+
await agent.stop();
|
|
129
|
+
break;
|
|
130
|
+
case "status":
|
|
131
|
+
await agent.status();
|
|
132
|
+
break;
|
|
133
|
+
case "restart":
|
|
134
|
+
await agent.restart();
|
|
135
|
+
break;
|
|
136
|
+
case "logs":
|
|
137
|
+
const follow = args.includes("-f") || args.includes("--follow");
|
|
138
|
+
await agent.logs(follow);
|
|
139
|
+
break;
|
|
140
|
+
default:
|
|
141
|
+
console.error("Unknown agent command:", subcommand);
|
|
142
|
+
console.error("Available commands: start, stop, status, restart, logs");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
101
147
|
if (args[0] === "shell") {
|
|
102
|
-
const { DiracShell } = await import("./shell-
|
|
148
|
+
const { DiracShell } = await import("./shell-OFBL57OA.js");
|
|
149
|
+
const { SessionServer, isSessionRunning, getSocketPath } = await import("./session-server-QZN2RMYD.js");
|
|
150
|
+
const { SessionClient } = await import("./session-client-3VTC5MLO.js");
|
|
151
|
+
const daemonMode = args.includes("--daemon") || args.includes("-d");
|
|
152
|
+
const agentMode = args.includes("--agent") || args.includes("-a");
|
|
103
153
|
const shellConfig = { debug: false };
|
|
104
154
|
for (let i = 1; i < args.length; i++) {
|
|
105
155
|
const arg = args[i];
|
|
106
156
|
if (arg === "--debug") {
|
|
107
157
|
shellConfig.debug = true;
|
|
158
|
+
} else if (arg === "--daemon" || arg === "-d") {
|
|
159
|
+
continue;
|
|
160
|
+
} else if (arg === "--agent" || arg === "-a") {
|
|
161
|
+
continue;
|
|
108
162
|
} else if ((arg === "-f" || arg === "--config") && i + 1 < args.length) {
|
|
109
163
|
const configPath = resolve(args[++i]);
|
|
110
164
|
if (fs.existsSync(configPath)) {
|
|
@@ -131,10 +185,59 @@ async function main() {
|
|
|
131
185
|
}
|
|
132
186
|
}
|
|
133
187
|
}
|
|
188
|
+
if (agentMode) {
|
|
189
|
+
if (!isSessionRunning()) {
|
|
190
|
+
console.error("Error: No agent is running");
|
|
191
|
+
console.error("Start the agent first with: dirac agent start");
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
console.log("Connecting to agent...");
|
|
195
|
+
const client = new SessionClient({ socketPath: getSocketPath() });
|
|
196
|
+
client.on("error", (error) => {
|
|
197
|
+
console.error("Agent error:", error);
|
|
198
|
+
});
|
|
199
|
+
try {
|
|
200
|
+
await client.connect();
|
|
201
|
+
console.log("Connected to agent at", getSocketPath());
|
|
202
|
+
console.log("Session state is persistent across disconnects");
|
|
203
|
+
console.log("");
|
|
204
|
+
const shell2 = new DiracShell(shellConfig);
|
|
205
|
+
shell2.setClient(client);
|
|
206
|
+
process.on("SIGINT", async () => {
|
|
207
|
+
await client.disconnect();
|
|
208
|
+
process.exit(0);
|
|
209
|
+
});
|
|
210
|
+
await shell2.start();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error("Failed to connect to agent:", error);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (daemonMode) {
|
|
218
|
+
console.log("Note: Daemon mode is under development.");
|
|
219
|
+
console.log("For now, use Ctrl-Z + bg to background the shell.");
|
|
220
|
+
console.log("");
|
|
221
|
+
}
|
|
134
222
|
const shell = new DiracShell(shellConfig);
|
|
135
223
|
await shell.start();
|
|
136
224
|
return;
|
|
137
225
|
}
|
|
226
|
+
if (args[0] === "daemon") {
|
|
227
|
+
const { SessionServer } = await import("./session-server-QZN2RMYD.js");
|
|
228
|
+
const server = new SessionServer();
|
|
229
|
+
await server.start();
|
|
230
|
+
console.log("Session daemon started");
|
|
231
|
+
process.on("SIGINT", async () => {
|
|
232
|
+
await server.shutdown();
|
|
233
|
+
process.exit(0);
|
|
234
|
+
});
|
|
235
|
+
process.on("SIGTERM", async () => {
|
|
236
|
+
await server.shutdown();
|
|
237
|
+
process.exit(0);
|
|
238
|
+
});
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
138
241
|
if (args.length === 0) {
|
|
139
242
|
console.error("Usage: dirac <file.di|file.bk>");
|
|
140
243
|
console.error(" dirac shell [options]");
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// src/session-client.ts
|
|
2
|
+
import net from "net";
|
|
3
|
+
import { EventEmitter } from "events";
|
|
4
|
+
var SessionClient = class extends EventEmitter {
|
|
5
|
+
socket = null;
|
|
6
|
+
buffer = "";
|
|
7
|
+
connected = false;
|
|
8
|
+
options;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super();
|
|
11
|
+
this.options = {
|
|
12
|
+
autoReconnect: false,
|
|
13
|
+
reconnectDelay: 1e3,
|
|
14
|
+
...options
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async connect() {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
this.socket = net.connect(this.options.socketPath);
|
|
20
|
+
this.socket.on("connect", () => {
|
|
21
|
+
this.emit("socket-connected");
|
|
22
|
+
});
|
|
23
|
+
this.socket.on("data", (data) => {
|
|
24
|
+
this.buffer += data.toString();
|
|
25
|
+
const lines = this.buffer.split("\n");
|
|
26
|
+
this.buffer = lines.pop() || "";
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
if (!line.trim()) continue;
|
|
29
|
+
try {
|
|
30
|
+
const message = JSON.parse(line);
|
|
31
|
+
this.handleMessage(message);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
this.emit("error", new Error(`Invalid message: ${error}`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
this.socket.on("end", () => {
|
|
38
|
+
this.connected = false;
|
|
39
|
+
this.emit("disconnected");
|
|
40
|
+
if (this.options.autoReconnect) {
|
|
41
|
+
setTimeout(() => this.connect(), this.options.reconnectDelay);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
this.socket.on("error", (error) => {
|
|
45
|
+
this.connected = false;
|
|
46
|
+
reject(error);
|
|
47
|
+
this.emit("error", error);
|
|
48
|
+
});
|
|
49
|
+
this.once("welcome", () => {
|
|
50
|
+
this.connected = true;
|
|
51
|
+
resolve();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
handleMessage(message) {
|
|
56
|
+
const { type, data, message: msg } = message;
|
|
57
|
+
switch (type) {
|
|
58
|
+
case "welcome":
|
|
59
|
+
this.emit("welcome", msg);
|
|
60
|
+
break;
|
|
61
|
+
case "output":
|
|
62
|
+
this.emit("output", data);
|
|
63
|
+
break;
|
|
64
|
+
case "error":
|
|
65
|
+
this.emit("error", new Error(msg || data));
|
|
66
|
+
break;
|
|
67
|
+
case "vars":
|
|
68
|
+
this.emit("vars", data);
|
|
69
|
+
break;
|
|
70
|
+
case "state":
|
|
71
|
+
this.emit("state", data);
|
|
72
|
+
break;
|
|
73
|
+
case "shutdown":
|
|
74
|
+
this.emit("shutdown");
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
this.emit("message", message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async execute(command) {
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
if (!this.socket) {
|
|
83
|
+
reject(new Error("Not connected to session: socket is null"));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!this.connected) {
|
|
87
|
+
reject(new Error("Not connected to session: connected flag is false"));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.send({ type: "execute", data: { command } });
|
|
91
|
+
const timeout = setTimeout(() => {
|
|
92
|
+
reject(new Error("Execution timeout"));
|
|
93
|
+
}, 3e4);
|
|
94
|
+
const onOutput = (output) => {
|
|
95
|
+
clearTimeout(timeout);
|
|
96
|
+
this.off("error", onError);
|
|
97
|
+
resolve(output);
|
|
98
|
+
};
|
|
99
|
+
const onError = (error) => {
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
this.off("output", onOutput);
|
|
102
|
+
reject(error);
|
|
103
|
+
};
|
|
104
|
+
this.once("output", onOutput);
|
|
105
|
+
this.once("error", onError);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async getVars() {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
if (!this.connected || !this.socket) {
|
|
111
|
+
reject(new Error("Not connected to session"));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.send({ type: "getVars" });
|
|
115
|
+
const timeout = setTimeout(() => {
|
|
116
|
+
reject(new Error("Timeout getting variables"));
|
|
117
|
+
}, 5e3);
|
|
118
|
+
const onVars = (vars) => {
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
resolve(vars);
|
|
121
|
+
};
|
|
122
|
+
this.once("vars", onVars);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async getState() {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
if (!this.socket) {
|
|
128
|
+
reject(new Error("Not connected to session: socket is null"));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!this.connected) {
|
|
132
|
+
reject(new Error("Not connected to session: connected flag is false"));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.send({ type: "getState" });
|
|
136
|
+
const timeout = setTimeout(() => {
|
|
137
|
+
reject(new Error("Timeout getting state"));
|
|
138
|
+
}, 5e3);
|
|
139
|
+
const onState = (state) => {
|
|
140
|
+
clearTimeout(timeout);
|
|
141
|
+
resolve(state);
|
|
142
|
+
};
|
|
143
|
+
this.once("state", onState);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async shutdown() {
|
|
147
|
+
return new Promise((resolve) => {
|
|
148
|
+
if (!this.connected || !this.socket) {
|
|
149
|
+
resolve();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.send({ type: "shutdown" });
|
|
153
|
+
this.once("shutdown", () => {
|
|
154
|
+
resolve();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
send(message) {
|
|
159
|
+
if (!this.socket || !this.connected) {
|
|
160
|
+
throw new Error("Not connected to session");
|
|
161
|
+
}
|
|
162
|
+
this.socket.write(JSON.stringify(message) + "\n");
|
|
163
|
+
}
|
|
164
|
+
disconnect() {
|
|
165
|
+
if (this.socket) {
|
|
166
|
+
this.socket.end();
|
|
167
|
+
this.socket = null;
|
|
168
|
+
}
|
|
169
|
+
this.connected = false;
|
|
170
|
+
}
|
|
171
|
+
isConnected() {
|
|
172
|
+
return this.connected;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
export {
|
|
176
|
+
SessionClient
|
|
177
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SessionServer,
|
|
3
|
+
getSocketPath,
|
|
4
|
+
isSessionRunning
|
|
5
|
+
} from "./chunk-AU3YU63U.js";
|
|
6
|
+
import "./chunk-ZAA6R2TC.js";
|
|
7
|
+
import "./chunk-HRHAMPOB.js";
|
|
8
|
+
import "./chunk-NKA6ZJDV.js";
|
|
9
|
+
import "./chunk-3UW6GWYQ.js";
|
|
10
|
+
export {
|
|
11
|
+
SessionServer,
|
|
12
|
+
getSocketPath,
|
|
13
|
+
isSessionRunning
|
|
14
|
+
};
|
|
@@ -23,6 +23,7 @@ var HISTORY_FILE = path.join(os.homedir(), ".dirac_history");
|
|
|
23
23
|
var MAX_HISTORY = 1e3;
|
|
24
24
|
var DiracShell = class {
|
|
25
25
|
session;
|
|
26
|
+
client = null;
|
|
26
27
|
braketParser;
|
|
27
28
|
xmlParser;
|
|
28
29
|
rl;
|
|
@@ -45,6 +46,12 @@ var DiracShell = class {
|
|
|
45
46
|
this.loadHistory();
|
|
46
47
|
this.setupHandlers();
|
|
47
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Set client for daemon mode
|
|
51
|
+
*/
|
|
52
|
+
setClient(client) {
|
|
53
|
+
this.client = client;
|
|
54
|
+
}
|
|
48
55
|
completer(line) {
|
|
49
56
|
const attrMatch = line.match(/\|([a-z0-9_-]+)\s+.*?([a-z0-9_-]*)$/i);
|
|
50
57
|
if (attrMatch) {
|
|
@@ -150,6 +157,9 @@ var DiracShell = class {
|
|
|
150
157
|
});
|
|
151
158
|
this.rl.on("close", () => {
|
|
152
159
|
this.saveHistory();
|
|
160
|
+
if (this.client) {
|
|
161
|
+
this.client.disconnect();
|
|
162
|
+
}
|
|
153
163
|
import("./schedule-QZ5U3YBS.js").then(({ stopAllScheduledTasks }) => {
|
|
154
164
|
stopAllScheduledTasks();
|
|
155
165
|
console.log("\nGoodbye!");
|
|
@@ -249,6 +259,17 @@ var DiracShell = class {
|
|
|
249
259
|
this.inputBuffer = [];
|
|
250
260
|
this.baseIndent = null;
|
|
251
261
|
try {
|
|
262
|
+
if (this.client) {
|
|
263
|
+
const xml2 = this.braketParser.parse(input);
|
|
264
|
+
if (this.config.debug) {
|
|
265
|
+
console.log("[Debug] Sending to agent:\n", xml2);
|
|
266
|
+
}
|
|
267
|
+
const output = await this.client.execute(xml2);
|
|
268
|
+
if (output) {
|
|
269
|
+
console.log(output);
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
252
273
|
this.session.output = [];
|
|
253
274
|
const xml = this.braketParser.parse(input);
|
|
254
275
|
if (this.config.debug) {
|
|
@@ -321,29 +342,56 @@ Examples:
|
|
|
321
342
|
`);
|
|
322
343
|
break;
|
|
323
344
|
case "vars":
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
345
|
+
try {
|
|
346
|
+
let variables;
|
|
347
|
+
if (this.client) {
|
|
348
|
+
const state = await this.client.getState();
|
|
349
|
+
variables = state.variables || [];
|
|
350
|
+
} else {
|
|
351
|
+
variables = this.session.variables;
|
|
352
|
+
}
|
|
353
|
+
if (variables.length === 0) {
|
|
354
|
+
console.log("No variables defined");
|
|
355
|
+
} else {
|
|
356
|
+
console.log("Variables:");
|
|
357
|
+
for (const v of variables) {
|
|
358
|
+
if (v.visible) {
|
|
359
|
+
console.log(` ${v.name} = ${JSON.stringify(v.value)}`);
|
|
360
|
+
}
|
|
331
361
|
}
|
|
332
362
|
}
|
|
363
|
+
} catch (error) {
|
|
364
|
+
console.error("Error getting variables:", error instanceof Error ? error.message : String(error));
|
|
333
365
|
}
|
|
334
366
|
break;
|
|
335
367
|
case "subs":
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
368
|
+
try {
|
|
369
|
+
let subroutines;
|
|
370
|
+
if (this.client) {
|
|
371
|
+
const state = await this.client.getState();
|
|
372
|
+
console.log("State received:", JSON.stringify(state, null, 2).substring(0, 500));
|
|
373
|
+
subroutines = state.subroutines || [];
|
|
374
|
+
} else {
|
|
375
|
+
subroutines = this.session.subroutines;
|
|
376
|
+
}
|
|
377
|
+
if (!Array.isArray(subroutines)) {
|
|
378
|
+
console.error("Error: subroutines is not an array, it is:", typeof subroutines);
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
if (subroutines.length === 0) {
|
|
382
|
+
console.log("No subroutines defined");
|
|
383
|
+
} else {
|
|
384
|
+
console.log("Subroutines:");
|
|
385
|
+
for (const s of subroutines) {
|
|
386
|
+
const params = s.parameters?.map((p) => p.name).join(", ") || "";
|
|
387
|
+
console.log(` ${s.name}(${params})`);
|
|
388
|
+
if (s.description) {
|
|
389
|
+
console.log(` ${s.description}`);
|
|
390
|
+
}
|
|
345
391
|
}
|
|
346
392
|
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
console.error("Error getting subroutines:", error instanceof Error ? error.message : String(error));
|
|
347
395
|
}
|
|
348
396
|
break;
|
|
349
397
|
case "clear":
|
|
@@ -709,9 +757,14 @@ Examples:
|
|
|
709
757
|
}
|
|
710
758
|
console.log(`Loading init script: ${scriptPath}`);
|
|
711
759
|
const scriptContent = fs.readFileSync(resolvedPath, "utf-8");
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
760
|
+
if (this.client) {
|
|
761
|
+
const xml = this.braketParser.parse(scriptContent);
|
|
762
|
+
await this.client.execute(xml);
|
|
763
|
+
} else {
|
|
764
|
+
const xml = this.braketParser.parse(scriptContent);
|
|
765
|
+
const ast = this.xmlParser.parse(xml);
|
|
766
|
+
await integrate(this.session, ast);
|
|
767
|
+
}
|
|
715
768
|
console.log(`Init script loaded.
|
|
716
769
|
`);
|
|
717
770
|
} catch (err) {
|