dirac-lang 0.1.19 → 0.1.21

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  integrate
3
- } from "./chunk-GR6KWGCI.js";
3
+ } from "./chunk-OS7DII2I.js";
4
4
  import {
5
5
  DiracParser
6
6
  } from "./chunk-HRHAMPOB.js";
@@ -138,7 +138,7 @@ function serializeToXml(children) {
138
138
  xml += " />";
139
139
  } else {
140
140
  xml += ">";
141
- if (child.text) {
141
+ if (child.text && child.children.length === 0) {
142
142
  xml += escapeXml(child.text);
143
143
  }
144
144
  if (child.children.length > 0) {
@@ -196,7 +196,29 @@ function executeAssign(session, element) {
196
196
  }
197
197
 
198
198
  // src/tags/output.ts
199
+ import * as fs from "fs";
200
+ import * as path from "path";
199
201
  async function executeOutput(session, element) {
202
+ const fileAttr = element.attributes?.file;
203
+ const filePath = fileAttr ? substituteAttribute(session, fileAttr) : null;
204
+ if (filePath) {
205
+ let content = "";
206
+ if (element.children && element.children.length > 0) {
207
+ const prevOutput = session.output;
208
+ session.output = [];
209
+ await integrateChildren(session, element);
210
+ content = session.output.join("");
211
+ session.output = prevOutput;
212
+ } else if (element.text) {
213
+ content = substituteVariables(session, element.text);
214
+ }
215
+ const dir = path.dirname(filePath);
216
+ if (dir !== "." && !fs.existsSync(dir)) {
217
+ fs.mkdirSync(dir, { recursive: true });
218
+ }
219
+ fs.appendFileSync(filePath, content + "\n", "utf8");
220
+ return;
221
+ }
200
222
  if (element.children && element.children.length > 0) {
201
223
  await integrateChildren(session, element);
202
224
  return;
@@ -397,12 +419,12 @@ async function executeIf(session, element) {
397
419
  const condition = await evaluatePredicate(session, conditionElement);
398
420
  if (condition) {
399
421
  if (thenElement) {
400
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-JTOTDC46.js");
422
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-WSRBK3KI.js");
401
423
  await integrateChildren2(session, thenElement);
402
424
  }
403
425
  } else {
404
426
  if (elseElement) {
405
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-JTOTDC46.js");
427
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-WSRBK3KI.js");
406
428
  await integrateChildren2(session, elseElement);
407
429
  }
408
430
  }
@@ -415,7 +437,7 @@ async function evaluatePredicate(session, predicateElement) {
415
437
  return await evaluateCondition(session, predicateElement);
416
438
  }
417
439
  const outputLengthBefore = session.output.length;
418
- const { integrate: integrate2 } = await import("./interpreter-JTOTDC46.js");
440
+ const { integrate: integrate2 } = await import("./interpreter-WSRBK3KI.js");
419
441
  await integrate2(session, predicateElement);
420
442
  const newOutputChunks = session.output.slice(outputLengthBefore);
421
443
  const result = newOutputChunks.join("").trim();
@@ -438,11 +460,11 @@ async function evaluateCondition(session, condElement) {
438
460
  }
439
461
  const outputLengthBefore = session.output.length;
440
462
  const args = [];
441
- const { integrate: integrate2 } = await import("./interpreter-JTOTDC46.js");
463
+ const { integrate: integrate2 } = await import("./interpreter-WSRBK3KI.js");
442
464
  for (const child of condElement.children) {
443
465
  if (child.tag.toLowerCase() === "arg") {
444
466
  const argOutputStart = session.output.length;
445
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-JTOTDC46.js");
467
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-WSRBK3KI.js");
446
468
  await integrateChildren2(session, child);
447
469
  const newChunks = session.output.slice(argOutputStart);
448
470
  const argValue = newChunks.join("");
@@ -857,11 +879,11 @@ ${expr}
857
879
  for (const v of session.variables) {
858
880
  context[v.name] = v.value;
859
881
  }
860
- const { default: fs2 } = await import("fs");
861
- const { default: path } = await import("path");
882
+ const { default: fs3 } = await import("fs");
883
+ const { default: path2 } = await import("path");
862
884
  const { fileURLToPath } = await import("url");
863
- context.fs = fs2;
864
- context.path = path;
885
+ context.fs = fs3;
886
+ context.path = path2;
865
887
  context.__dirname = process.cwd();
866
888
  context.getParams = () => {
867
889
  const params = session.parameterStack[session.parameterStack.length - 1];
@@ -915,13 +937,13 @@ ${diracCode}
915
937
 
916
938
  // src/tags/import.ts
917
939
  import { readFileSync } from "fs";
918
- import { resolve, dirname } from "path";
940
+ import { resolve, dirname as dirname2 } from "path";
919
941
  async function executeImport(session, element) {
920
942
  const src = element.attributes.src;
921
943
  if (!src) {
922
944
  throw new Error("<import> requires src attribute");
923
945
  }
924
- const currentDir = session.currentFile ? dirname(session.currentFile) : process.cwd();
946
+ const currentDir = session.currentFile ? dirname2(session.currentFile) : process.cwd();
925
947
  const importPath = resolve(currentDir, src);
926
948
  if (session.debug) {
927
949
  console.error(`[IMPORT] Loading: ${importPath}`);
@@ -1046,7 +1068,7 @@ async function executeExpr(session, element) {
1046
1068
  }
1047
1069
 
1048
1070
  // src/tags/system.ts
1049
- import { exec } from "child_process";
1071
+ import { exec, spawn } from "child_process";
1050
1072
  import { promisify } from "util";
1051
1073
  var execAsync = promisify(exec);
1052
1074
  async function executeSystem(session, element) {
@@ -1068,8 +1090,22 @@ async function executeSystem(session, element) {
1068
1090
  if (!command.trim()) {
1069
1091
  return;
1070
1092
  }
1093
+ const backgroundAttr = element.attributes?.background;
1094
+ const isBackground = backgroundAttr === "true";
1071
1095
  if (session.debug) {
1072
- console.error(`[SYSTEM] Executing: ${command}`);
1096
+ console.error(`[SYSTEM] Executing${isBackground ? " (background)" : ""}: ${command}`);
1097
+ }
1098
+ if (isBackground) {
1099
+ const child = spawn(command, {
1100
+ detached: true,
1101
+ stdio: "ignore",
1102
+ shell: true
1103
+ });
1104
+ child.unref();
1105
+ if (session.debug) {
1106
+ console.error(`[SYSTEM] Background process started with PID: ${child.pid}`);
1107
+ }
1108
+ return;
1073
1109
  }
1074
1110
  try {
1075
1111
  const { stdout, stderr } = await execAsync(command, {
@@ -1103,13 +1139,13 @@ async function executeRequireModule(session, element) {
1103
1139
  }
1104
1140
 
1105
1141
  // src/tags/tag-check.ts
1106
- import fs from "fs";
1142
+ import fs2 from "fs";
1107
1143
  import yaml from "js-yaml";
1108
1144
  var SIMILARITY_CUTOFF = 0.75;
1109
1145
  function getEmbeddingServerConfig() {
1110
1146
  try {
1111
1147
  const configPath = process.env.DIRAC_CONFIG || "config.yml";
1112
- const config = yaml.load(fs.readFileSync(configPath, "utf8"));
1148
+ const config = yaml.load(fs2.readFileSync(configPath, "utf8"));
1113
1149
  const host = config.embeddingServer?.host || "localhost";
1114
1150
  const port = config.embeddingServer?.port || 11434;
1115
1151
  const model = config.embeddingServer?.model || "embeddinggemma";
@@ -1245,7 +1281,7 @@ async function executeTagCheck(session, element) {
1245
1281
  const executeTag = correctedTag || tagName;
1246
1282
  console.error(`[tag-check] Executing <${executeTag}/> as all checks passed and execute=true.`);
1247
1283
  const elementToExecute = correctedTag ? { ...child, tag: correctedTag } : child;
1248
- const { integrate: integrate2 } = await import("./interpreter-JTOTDC46.js");
1284
+ const { integrate: integrate2 } = await import("./interpreter-WSRBK3KI.js");
1249
1285
  await integrate2(session, elementToExecute);
1250
1286
  }
1251
1287
  }
@@ -1254,7 +1290,7 @@ async function executeTagCheck(session, element) {
1254
1290
  // src/tags/throw.ts
1255
1291
  async function executeThrow(session, element) {
1256
1292
  const exceptionName = element.attributes?.name || "exception";
1257
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-JTOTDC46.js");
1293
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-WSRBK3KI.js");
1258
1294
  const exceptionDom = {
1259
1295
  tag: "exception-content",
1260
1296
  attributes: { name: exceptionName },
@@ -1267,7 +1303,7 @@ async function executeThrow(session, element) {
1267
1303
  // src/tags/try.ts
1268
1304
  async function executeTry(session, element) {
1269
1305
  setExceptionBoundary(session);
1270
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-JTOTDC46.js");
1306
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-WSRBK3KI.js");
1271
1307
  await integrateChildren2(session, element);
1272
1308
  unsetExceptionBoundary(session);
1273
1309
  }
@@ -1277,7 +1313,7 @@ async function executeCatch(session, element) {
1277
1313
  const exceptionName = element.attributes?.name || "exception";
1278
1314
  const caughtCount = lookupException(session, exceptionName);
1279
1315
  if (caughtCount > 0) {
1280
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-JTOTDC46.js");
1316
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-WSRBK3KI.js");
1281
1317
  await integrateChildren2(session, element);
1282
1318
  }
1283
1319
  flushCurrentException(session);
@@ -1286,7 +1322,7 @@ async function executeCatch(session, element) {
1286
1322
  // src/tags/exception.ts
1287
1323
  async function executeException(session, element) {
1288
1324
  const exceptions = getCurrentExceptions(session);
1289
- const { integrateChildren: integrateChildren2 } = await import("./interpreter-JTOTDC46.js");
1325
+ const { integrateChildren: integrateChildren2 } = await import("./interpreter-WSRBK3KI.js");
1290
1326
  for (const exceptionDom of exceptions) {
1291
1327
  await integrateChildren2(session, exceptionDom);
1292
1328
  }
@@ -1426,7 +1462,7 @@ async function executeForeach(session, element) {
1426
1462
  const parser2 = new DiracParser2();
1427
1463
  try {
1428
1464
  const fromElement = parser2.parse(fromAttr);
1429
- const { integrate: integrate2 } = await import("./interpreter-JTOTDC46.js");
1465
+ const { integrate: integrate2 } = await import("./interpreter-WSRBK3KI.js");
1430
1466
  await integrate2(session, fromElement);
1431
1467
  } catch (e) {
1432
1468
  session.output = savedOutput;
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  execute
4
- } from "./chunk-QMFCOZ5D.js";
5
- import "./chunk-GR6KWGCI.js";
4
+ } from "./chunk-JRNDLSQJ.js";
5
+ import "./chunk-OS7DII2I.js";
6
6
  import "./chunk-HRHAMPOB.js";
7
7
  import "./chunk-E7PWEMZA.js";
8
8
  import "./chunk-52ED23DR.js";
@@ -13,7 +13,7 @@ import "dotenv/config";
13
13
  // package.json
14
14
  var package_default = {
15
15
  name: "dirac-lang",
16
- version: "0.1.19",
16
+ version: "0.1.21",
17
17
  description: "LLM-Augmented Declarative Execution",
18
18
  type: "module",
19
19
  main: "dist/index.js",
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@ import {
2
2
  createLLMAdapter,
3
3
  execute,
4
4
  executeUserCommand
5
- } from "./chunk-QMFCOZ5D.js";
5
+ } from "./chunk-JRNDLSQJ.js";
6
6
  import {
7
7
  integrate
8
- } from "./chunk-GR6KWGCI.js";
8
+ } from "./chunk-OS7DII2I.js";
9
9
  import {
10
10
  DiracParser
11
11
  } from "./chunk-HRHAMPOB.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  integrate,
3
3
  integrateChildren
4
- } from "./chunk-GR6KWGCI.js";
4
+ } from "./chunk-OS7DII2I.js";
5
5
  import "./chunk-HRHAMPOB.js";
6
6
  import "./chunk-E7PWEMZA.js";
7
7
  import "./chunk-52ED23DR.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  integrate
3
- } from "./chunk-GR6KWGCI.js";
3
+ } from "./chunk-OS7DII2I.js";
4
4
  import {
5
5
  DiracParser
6
6
  } from "./chunk-HRHAMPOB.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dirac-lang",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "LLM-Augmented Declarative Execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -86,12 +86,13 @@ function serializeToXml(children: DiracElement[]): string {
86
86
  } else {
87
87
  xml += '>';
88
88
 
89
- // Add text content if present
90
- if (child.text) {
89
+ // Only add text content if there are no children
90
+ // (if there are children, text is stored in text node children)
91
+ if (child.text && child.children.length === 0) {
91
92
  xml += escapeXml(child.text);
92
93
  }
93
94
 
94
- // Add children
95
+ // Add children (which may include text nodes)
95
96
  if (child.children.length > 0) {
96
97
  xml += serializeToXml(child.children);
97
98
  }
@@ -4,17 +4,47 @@
4
4
  */
5
5
 
6
6
  import type { DiracSession, DiracElement } from '../types/index.js';
7
- import { emit, substituteVariables } from '../runtime/session.js';
7
+ import { emit, substituteVariables, substituteAttribute } from '../runtime/session.js';
8
8
  import { integrateChildren } from '../runtime/interpreter.js';
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
9
11
 
10
12
  export async function executeOutput(session: DiracSession, element: DiracElement): Promise<void> {
11
- // If has children, process them (handles mixed content)
13
+ const fileAttr = element.attributes?.file;
14
+ const filePath = fileAttr ? substituteAttribute(session, fileAttr) : null;
15
+
16
+ // If writing to a file, collect content first
17
+ if (filePath) {
18
+ let content = '';
19
+
20
+ if (element.children && element.children.length > 0) {
21
+ // Capture output from children
22
+ const prevOutput = session.output;
23
+ session.output = [];
24
+ await integrateChildren(session, element);
25
+ content = session.output.join('');
26
+ session.output = prevOutput;
27
+ } else if (element.text) {
28
+ content = substituteVariables(session, element.text);
29
+ }
30
+
31
+ // Ensure directory exists
32
+ const dir = path.dirname(filePath);
33
+ if (dir !== '.' && !fs.existsSync(dir)) {
34
+ fs.mkdirSync(dir, { recursive: true });
35
+ }
36
+
37
+ // Append to file
38
+ fs.appendFileSync(filePath, content + '\n', 'utf8');
39
+ return;
40
+ }
41
+
42
+ // Normal output to stdout
12
43
  if (element.children && element.children.length > 0) {
13
44
  await integrateChildren(session, element);
14
45
  return;
15
46
  }
16
47
 
17
- // If only text content, use it (with variable substitution)
18
48
  if (element.text) {
19
49
  const content = substituteVariables(session, element.text);
20
50
  emit(session, content);
@@ -5,7 +5,7 @@
5
5
 
6
6
  import type { DiracSession, DiracElement } from '../types/index.js';
7
7
  import { substituteVariables, emit } from '../runtime/session.js';
8
- import { exec } from 'child_process';
8
+ import { exec, spawn } from 'child_process';
9
9
  import { promisify } from 'util';
10
10
  import { integrate } from '../runtime/interpreter.js';
11
11
 
@@ -43,10 +43,33 @@ export async function executeSystem(session: DiracSession, element: DiracElement
43
43
  return;
44
44
  }
45
45
 
46
+ // Check for background attribute
47
+ const backgroundAttr = element.attributes?.background;
48
+ const isBackground = backgroundAttr === 'true';
49
+
46
50
  if (session.debug) {
47
- console.error(`[SYSTEM] Executing: ${command}`);
51
+ console.error(`[SYSTEM] Executing${isBackground ? ' (background)' : ''}: ${command}`);
52
+ }
53
+
54
+ // Background mode - spawn and don't wait
55
+ if (isBackground) {
56
+ const child = spawn(command, {
57
+ detached: true,
58
+ stdio: 'ignore',
59
+ shell: true,
60
+ });
61
+
62
+ // Unref so parent can exit without waiting
63
+ child.unref();
64
+
65
+ if (session.debug) {
66
+ console.error(`[SYSTEM] Background process started with PID: ${child.pid}`);
67
+ }
68
+
69
+ return;
48
70
  }
49
71
 
72
+ // Foreground mode - wait for completion (original behavior)
50
73
  try {
51
74
  const { stdout, stderr } = await execAsync(command, {
52
75
  encoding: 'utf-8',
@@ -0,0 +1,22 @@
1
+ <!-- TEST: generate_and_execute_dirac -->
2
+ <!-- EXPECT: Generated file executed
3
+ Result: 42 -->
4
+ <dirac>
5
+ <!-- Define a DIRAC script as literal -->
6
+ <defvar name="generated_script" literal="true" trim="false">
7
+ <dirac>
8
+ <defvar name="value">42</defvar>
9
+ <output>Result: <variable name="value" /></output>
10
+ </dirac>
11
+ </defvar>
12
+
13
+ <!-- Write to file -->
14
+ <output file="temp-generated.di"><variable name="generated_script" /></output>
15
+
16
+ <!-- Execute the generated script -->
17
+ <output>Generated file executed</output>
18
+ <system shell="bash">
19
+ node dist/cli.js temp-generated.di
20
+ rm temp-generated.di
21
+ </system>
22
+ </dirac>
@@ -0,0 +1,19 @@
1
+ <!-- TEST: output_file_basic -->
2
+ <!-- EXPECT: stdout output
3
+ File written -->
4
+ <dirac>
5
+ <!-- Write to file -->
6
+ <output file="output-test-temp.txt">File line 1</output>
7
+ <output file="output-test-temp.txt">File line 2</output>
8
+
9
+ <!-- Output to stdout to verify test runs -->
10
+ <output>stdout output</output>
11
+
12
+ <!-- Use system to verify file and clean up -->
13
+ <system shell="bash">
14
+ if [ -f output-test-temp.txt ]; then
15
+ echo "File written"
16
+ rm output-test-temp.txt
17
+ fi
18
+ </system>
19
+ </dirac>
@@ -0,0 +1,39 @@
1
+ <dirac>
2
+ <output>Testing background processes with loop...</output>
3
+
4
+ <!-- Setup: Clean up old test files -->
5
+ <system>rm -f /tmp/dirac-bg-counter.txt</system>
6
+
7
+ <!-- Start a background process that writes incrementing numbers -->
8
+ <system background="true">(for i in 1 2 3 4 5; do echo "Count: $i"; sleep 1; done > /tmp/dirac-bg-counter.txt) &</system>
9
+ <output>✓ Background counter started</output>
10
+
11
+ <!-- This executes immediately without waiting -->
12
+ <output>✓ DIRAC continuing without blocking</output>
13
+
14
+ <!-- Wait a moment for background process to start writing -->
15
+ <system>sleep 2</system>
16
+
17
+ <!-- Read the results in a loop (3 times, showing progressive output) -->
18
+ <loop count="3">
19
+ <system>wc -l /tmp/dirac-bg-counter.txt 2>/dev/null || echo "0"</system>
20
+ <output>Check: Background file now has lines</output>
21
+ <system>sleep 1</system>
22
+ </loop>
23
+
24
+ <!-- Give background process time to finish all 5 counts -->
25
+ <system>sleep 2</system>
26
+
27
+ <!-- Verify final result contains all 5 counts -->
28
+ <system>wc -l /tmp/dirac-bg-counter.txt</system>
29
+ <output>✓ Background process completed</output>
30
+
31
+ <!-- Verify content -->
32
+ <system>grep -c "Count:" /tmp/dirac-bg-counter.txt</system>
33
+ <output>✓ Found all count entries</output>
34
+
35
+ <!-- Cleanup -->
36
+ <system>rm -f /tmp/dirac-bg-counter.txt</system>
37
+
38
+ <output>✓ Background process with loop test passed</output>
39
+ </dirac>