dirac-lang 0.1.24 → 0.1.26
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/cli.js +13 -1
- package/lib/index.di +9 -0
- package/{examples/lib → lib}/math.di +3 -3
- package/package.json +13 -1
- package/.env.example +0 -8
- package/COMMUNITY.md +0 -465
- package/CONDITIONAL-TAGS.md +0 -172
- package/EXCEPTION-HANDLING.md +0 -156
- package/LIBRARIES.md +0 -172
- package/LLM-VALIDATION.md +0 -128
- package/NAMESPACES.md +0 -366
- package/PROMOTION.md +0 -257
- package/QUICKSTART-LIBRARY.md +0 -93
- package/TEST-COVERAGE.md +0 -113
- package/TESTING.md +0 -162
- package/config.test.yml +0 -5
- package/dirac-http/examples/demo.di +0 -9
- package/dirac-http/lib/index.di +0 -12
- package/examples/add-demo.di +0 -74
- package/examples/add.bk +0 -11
- package/examples/advanced-math-demo.di +0 -53
- package/examples/calculator.di +0 -32
- package/examples/compact-test.di +0 -6
- package/examples/comprehensive.bk +0 -29
- package/examples/defvar-variable-demo.di +0 -18
- package/examples/direct-call.di +0 -17
- package/examples/disk-analysis.di +0 -16
- package/examples/exception-demo.di +0 -82
- package/examples/executable-hello.di +0 -7
- package/examples/execute-demo.di +0 -38
- package/examples/file-manager.di +0 -77
- package/examples/file-stats.di +0 -18
- package/examples/hello.bk +0 -1
- package/examples/hello.di +0 -5
- package/examples/if-comparison.di +0 -91
- package/examples/if-cstyle-test.di +0 -149
- package/examples/if-vs-testif.di +0 -56
- package/examples/import-demo.di +0 -31
- package/examples/inline-test.bk +0 -7
- package/examples/llm-agent.di +0 -32
- package/examples/llm-basic.di +0 -12
- package/examples/llm-command-more.di +0 -6
- package/examples/llm-command-no-exec.di +0 -13
- package/examples/llm-command.di +0 -6
- package/examples/llm-complex.di +0 -141
- package/examples/llm-feedback-debug.di +0 -30
- package/examples/llm-feedback-demo.di +0 -19
- package/examples/llm-feedback-math.di +0 -22
- package/examples/llm-feedback-simple.di +0 -16
- package/examples/llm-feedback-sub.di +0 -22
- package/examples/llm-no-feedback.di +0 -10
- package/examples/llm-recursive.di +0 -31
- package/examples/llm-reflection-test.di +0 -19
- package/examples/llm-simple-test.di +0 -12
- package/examples/llm-subs.di +0 -132
- package/examples/llm-use-subs.di +0 -6
- package/examples/llm-validate-test.di +0 -18
- package/examples/loop.di +0 -12
- package/examples/math-test.di +0 -22
- package/examples/minimal-test.di +0 -13
- package/examples/mongodb-context-test.di +0 -27
- package/examples/mongodb-count-events.di +0 -8
- package/examples/mongodb-import-demo.di +0 -25
- package/examples/mongodb-simple-test.di +0 -18
- package/examples/nl-agent.di +0 -47
- package/examples/parameters-demo.di +0 -68
- package/examples/params-meta-test.di +0 -17
- package/examples/params-test.di +0 -10
- package/examples/recipe-chain.di +0 -38
- package/examples/recursive-llm.di +0 -44
- package/examples/sample-library/README.md +0 -152
- package/examples/sample-library/examples/demo.di +0 -34
- package/examples/sample-library/lib/index.di +0 -65
- package/examples/sample-library/package.json +0 -31
- package/examples/scope-test-nested.di +0 -60
- package/examples/scope-test.di +0 -55
- package/examples/seamless.di +0 -45
- package/examples/shell-test.bk +0 -10
- package/examples/simple-import.di +0 -13
- package/examples/simple-recursive.di +0 -26
- package/examples/story-builder.di +0 -45
- package/examples/subroutine.di +0 -23
- package/examples/system-llm.di +0 -21
- package/examples/system-simple.di +0 -3
- package/examples/system-test.di +0 -8
- package/examples/tag-check-test.di +0 -139
- package/examples/task-assistant.di +0 -27
- package/examples/test-if-demo.di +0 -110
- package/examples/test-parameters.di +0 -50
- package/examples/try-catch-test.di +0 -118
- package/examples/two-styles.di +0 -28
- package/examples/var-debug.di +0 -6
- package/examples/var-inline.di +0 -4
- package/examples/var-test2.di +0 -6
- package/examples/variable-replace.di +0 -25
- package/examples/variable-simple.di +0 -16
- package/examples/variable-test.di +0 -22
- package/examples/whitespace-test.di +0 -24
- package/filePath +0 -1
- package/greeting.txt +0 -1
- package/src/cli.ts +0 -140
- package/src/index.ts +0 -33
- package/src/llm/ollama.ts +0 -58
- package/src/runtime/braket-parser.ts +0 -234
- package/src/runtime/interpreter.ts +0 -203
- package/src/runtime/parser.ts +0 -155
- package/src/runtime/session.ts +0 -325
- package/src/tags/assign.ts +0 -37
- package/src/tags/attr.ts +0 -64
- package/src/tags/available-subroutines.ts +0 -70
- package/src/tags/call.ts +0 -259
- package/src/tags/catch.ts +0 -24
- package/src/tags/defvar.ts +0 -115
- package/src/tags/environment.ts +0 -21
- package/src/tags/eval.ts +0 -71
- package/src/tags/exception.ts +0 -20
- package/src/tags/execute.ts +0 -52
- package/src/tags/expr.ts +0 -128
- package/src/tags/foreach.ts +0 -170
- package/src/tags/if.ts +0 -191
- package/src/tags/import.ts +0 -135
- package/src/tags/index.ts +0 -41
- package/src/tags/input.ts +0 -182
- package/src/tags/llm.ts +0 -415
- package/src/tags/loop.ts +0 -43
- package/src/tags/mongodb.ts +0 -70
- package/src/tags/output.ts +0 -53
- package/src/tags/parameters.ts +0 -81
- package/src/tags/require_module.ts +0 -19
- package/src/tags/subroutine.ts +0 -75
- package/src/tags/system.ts +0 -93
- package/src/tags/tag-check.ts +0 -176
- package/src/tags/test-if.ts +0 -112
- package/src/tags/throw.ts +0 -26
- package/src/tags/try.ts +0 -19
- package/src/tags/variable.ts +0 -25
- package/src/test-runner.ts +0 -300
- package/src/types/index.ts +0 -128
- package/src/utils/llm-adapter.ts +0 -113
- package/src/utils/tag-validator.ts +0 -231
- package/test-available-extends.di +0 -28
- package/test-available-simple.di +0 -12
- package/test-available-subroutines.di +0 -16
- package/test-call.di +0 -9
- package/test-extend-basic.di +0 -17
- package/test-extend-chain.di +0 -26
- package/test-extend-debug.di +0 -17
- package/test-extend-debug2.di +0 -15
- package/test-extend-person.di +0 -17
- package/test-extend-simple.di +0 -11
- package/test-extend-zhi.di +0 -23
- package/test-extend.di +0 -19
- package/test-extend2.di +0 -26
- package/test-extend3-fixed.di +0 -23
- package/test-extend3-trouble.di +0 -23
- package/test-extend3.di +0 -21
- package/test-factorial.di +0 -19
- package/test-input-debug.di +0 -19
- package/test-nested-debug.di +0 -7
- package/test-nested-debug2.di +0 -8
- package/test-no-extend.di +0 -16
- package/test-simple-call.di +0 -7
- package/test-simple.di +0 -6
- package/tests/README.md +0 -69
- package/tests/assign-basic.test.di +0 -7
- package/tests/assign-from-eval.test.di +0 -7
- package/tests/available-subroutines-foreach.test.di +0 -32
- package/tests/basic-output.test.di +0 -5
- package/tests/call-basic.test.di +0 -11
- package/tests/call-with-output.test.di +0 -10
- package/tests/comments-before-content.test.di +0 -6
- package/tests/defvar-multiple.test.di +0 -8
- package/tests/environment-basic.test.di +0 -22
- package/tests/environment-nonexistent.test.di +0 -5
- package/tests/environment-with-defvar.test.di +0 -15
- package/tests/eval-basic.test.di +0 -6
- package/tests/eval-with-variables.test.di +0 -8
- package/tests/exception-basic.test.di +0 -10
- package/tests/expr-basic.test.di +0 -11
- package/tests/extend-basic.test.di +0 -19
- package/tests/extend-inheritance-chain.test.di +0 -26
- package/tests/extend-multiple-nested.test.di +0 -24
- package/tests/extend-self.test.di +0 -19
- package/tests/extend-with-parameters.test.di +0 -14
- package/tests/generate-dirac.test.di +0 -22
- package/tests/if-conditional.test.di +0 -17
- package/tests/if-else.test.di +0 -11
- package/tests/if-with-variables.test.di +0 -8
- package/tests/import-basic.test.di +0 -6
- package/tests/input-file-all.test.di +0 -5
- package/tests/input-file-line.test.di +0 -15
- package/tests/input-file-loop.test.di +0 -12
- package/tests/input-stdin-all.test.di +0 -8
- package/tests/input-stdin-all.txt +0 -1
- package/tests/llm-validate.test.di +0 -17
- package/tests/loop-basic.test.di +0 -9
- package/tests/loop-nested.test.di +0 -10
- package/tests/loop-with-eval.test.di +0 -8
- package/tests/no-root-element.test.di +0 -6
- package/tests/output-file.test.di +0 -19
- package/tests/output-multiple.test.di +0 -9
- package/tests/parameters-basic.test.di +0 -6
- package/tests/require-module-basic.test.di +0 -7
- package/tests/subroutine-basic.test.di +0 -9
- package/tests/subroutine-multiple-params.test.di +0 -10
- package/tests/subroutine-nested-calls.test.di +0 -32
- package/tests/subroutine-sequential-calls.test.di +0 -36
- package/tests/system-background.test.di +0 -39
- package/tests/system-basic.test.di +0 -5
- package/tests/tag-check.test.di +0 -27
- package/tests/test-if-basic.test.di +0 -8
- package/tests/test-input.txt +0 -3
- package/tests/try-catch-basic.test.di +0 -10
- package/tests/try-catch-eval-error.test.di +0 -10
- package/tests/variable-basic.test.di +0 -6
- package/tests/variable-interpolation.test.di +0 -7
- package/tools/create-library.sh +0 -175
- package/tsconfig.json +0 -19
- /package/{examples/lib → lib}/advanced-math.di +0 -0
- /package/{examples/lib → lib}/fileops.di +0 -0
- /package/{examples/lib → lib}/mongodb.di +0 -0
package/src/tags/mongodb.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { TagHandler, TagHandlerContext } from '../types';
|
|
2
|
-
|
|
3
|
-
console.log('[mongodb tag] mongodb.ts loaded');
|
|
4
|
-
import { MongoClient, Db, Collection } from 'mongodb';
|
|
5
|
-
|
|
6
|
-
// Utility to expand variables/child tags as Dirac does
|
|
7
|
-
async function expandContent(ctx: TagHandlerContext): Promise<any> {
|
|
8
|
-
if (ctx.body) {
|
|
9
|
-
// If body is present, expand it (could be JSON, array, or object)
|
|
10
|
-
const expanded = await ctx.evaluate(ctx.body);
|
|
11
|
-
try {
|
|
12
|
-
return JSON.parse(expanded);
|
|
13
|
-
} catch {
|
|
14
|
-
return expanded;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const mongodb: TagHandler = async (ctx: TagHandlerContext) => {
|
|
21
|
-
const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
|
22
|
-
const database = ctx.getAttr('database');
|
|
23
|
-
const collection = ctx.getAttr('collection');
|
|
24
|
-
const action = ctx.getAttr('action');
|
|
25
|
-
const client = new MongoClient(uri);
|
|
26
|
-
let result;
|
|
27
|
-
console.log('[mongodb tag] Connecting to:', uri);
|
|
28
|
-
console.log('[mongodb tag] Database:', database, 'Collection:', collection, 'Action:', action);
|
|
29
|
-
try {
|
|
30
|
-
await client.connect();
|
|
31
|
-
let db: Db | undefined = database ? client.db(database) : undefined;
|
|
32
|
-
let col: Collection | undefined = db && collection ? db.collection(collection) : undefined;
|
|
33
|
-
const content = await expandContent(ctx);
|
|
34
|
-
console.log('[mongodb tag] Query content:', content);
|
|
35
|
-
switch (action) {
|
|
36
|
-
case 'find':
|
|
37
|
-
if (!col) throw new Error('Collection required for find');
|
|
38
|
-
result = await col.find(content || {}).toArray();
|
|
39
|
-
console.log('[mongodb tag] Find result:', result);
|
|
40
|
-
break;
|
|
41
|
-
case 'aggregate':
|
|
42
|
-
if (!col) throw new Error('Collection required for aggregate');
|
|
43
|
-
result = await col.aggregate(content).toArray();
|
|
44
|
-
console.log('[mongodb tag] Aggregate result:', result);
|
|
45
|
-
break;
|
|
46
|
-
case 'insert':
|
|
47
|
-
if (!col) throw new Error('Collection required for insert');
|
|
48
|
-
result = await col.insertOne(content);
|
|
49
|
-
console.log('[mongodb tag] Insert result:', result);
|
|
50
|
-
break;
|
|
51
|
-
case 'createDb':
|
|
52
|
-
if (!db) throw new Error('Database required for createDb');
|
|
53
|
-
result = { db: database, created: true };
|
|
54
|
-
console.log('[mongodb tag] CreateDb result:', result);
|
|
55
|
-
break;
|
|
56
|
-
case 'createCollection':
|
|
57
|
-
if (!db || !collection) throw new Error('Database and collection required');
|
|
58
|
-
result = await db.createCollection(collection);
|
|
59
|
-
console.log('[mongodb tag] CreateCollection result:', result);
|
|
60
|
-
break;
|
|
61
|
-
default:
|
|
62
|
-
throw new Error('Unknown action for <mongodb>');
|
|
63
|
-
}
|
|
64
|
-
} finally {
|
|
65
|
-
await client.close();
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export default mongodb;
|
package/src/tags/output.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <output> tag - emit content
|
|
3
|
-
* Maps to mask_tag_output in MASK
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
-
import { emit, substituteVariables, substituteAttribute } from '../runtime/session.js';
|
|
8
|
-
import { integrateChildren } from '../runtime/interpreter.js';
|
|
9
|
-
import * as fs from 'fs';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
|
|
12
|
-
export async function executeOutput(session: DiracSession, element: DiracElement): Promise<void> {
|
|
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
|
|
43
|
-
if (element.children && element.children.length > 0) {
|
|
44
|
-
await integrateChildren(session, element);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (element.text) {
|
|
49
|
-
const content = substituteVariables(session, element.text);
|
|
50
|
-
emit(session, content);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
package/src/tags/parameters.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <parameters> tag - Access parameters passed to subroutine
|
|
3
|
-
* Maps to MASK parameter selection syntax
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
-
import { getCurrentParameters, emit, setVariable } from '../runtime/session.js';
|
|
8
|
-
import { integrate } from '../runtime/interpreter.js';
|
|
9
|
-
|
|
10
|
-
export async function executeParameters(session: DiracSession, element: DiracElement): Promise<string | void> {
|
|
11
|
-
const select = element.attributes.select;
|
|
12
|
-
|
|
13
|
-
if (!select) {
|
|
14
|
-
throw new Error('<parameters> requires select attribute');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Get parameters from current call context
|
|
18
|
-
const params = getCurrentParameters(session);
|
|
19
|
-
|
|
20
|
-
if (!params || params.length === 0) {
|
|
21
|
-
if (session.debug) {
|
|
22
|
-
console.error(`[PARAMETERS] No parameters available`);
|
|
23
|
-
}
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// The caller element is params[0] (the calling tag itself)
|
|
28
|
-
const caller = params[0];
|
|
29
|
-
|
|
30
|
-
if (select === '*') {
|
|
31
|
-
// Select all child elements - execute them and RETURN output
|
|
32
|
-
if (session.debug) {
|
|
33
|
-
console.error(`[PARAMETERS] Selecting all children (${caller.children.length} elements)`);
|
|
34
|
-
}
|
|
35
|
-
// Save current output buffer
|
|
36
|
-
const prevOutput = session.output;
|
|
37
|
-
session.output = [];
|
|
38
|
-
for (const child of caller.children) {
|
|
39
|
-
await integrate(session, child);
|
|
40
|
-
}
|
|
41
|
-
const captured = session.output.join('');
|
|
42
|
-
session.output = prevOutput;
|
|
43
|
-
return captured;
|
|
44
|
-
} else if (select.startsWith('@')) {
|
|
45
|
-
// Select attribute(s)
|
|
46
|
-
const attrName = select.slice(1); // Remove '@'
|
|
47
|
-
|
|
48
|
-
if (attrName === '*') {
|
|
49
|
-
// Select all attributes
|
|
50
|
-
if (session.debug) {
|
|
51
|
-
console.error(`[PARAMETERS] Selecting all attributes`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const attrs = Object.entries(caller.attributes)
|
|
55
|
-
.map(([key, val]) => `${key}="${val}"`)
|
|
56
|
-
.join(' ');
|
|
57
|
-
emit(session, attrs);
|
|
58
|
-
|
|
59
|
-
} else {
|
|
60
|
-
// Select specific attribute - automatically create variable with that name
|
|
61
|
-
const value = caller.attributes[attrName];
|
|
62
|
-
|
|
63
|
-
if (session.debug) {
|
|
64
|
-
console.error(`[PARAMETERS] Setting variable '${attrName}' = '${value}'`);
|
|
65
|
-
}
|
|
66
|
-
if (value !== undefined) {
|
|
67
|
-
// Only set variable if not already set in current boundary
|
|
68
|
-
const alreadySet = session.variables.slice(session.varBoundary).some(v => v.name === attrName);
|
|
69
|
-
if (!alreadySet) {
|
|
70
|
-
setVariable(session, attrName, value, false);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
for (const child of element.children) {
|
|
74
|
-
await integrate(session, child);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
} else {
|
|
79
|
-
throw new Error(`<parameters> invalid select: ${select}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
2
|
-
import { setVariable } from '../runtime/session.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* <require_module name="mongodb" var="mongo" />
|
|
6
|
-
* Loads a Node.js module and stores it in a session variable.
|
|
7
|
-
*/
|
|
8
|
-
export async function executeRequireModule(session: DiracSession, element: DiracElement): Promise<void> {
|
|
9
|
-
const name = element.attributes.name;
|
|
10
|
-
const varName = element.attributes.var || name;
|
|
11
|
-
if (!name) throw new Error('<require_module> missing name attribute');
|
|
12
|
-
try {
|
|
13
|
-
// Dynamically import the module (ESM compatible)
|
|
14
|
-
const mod = await import(name);
|
|
15
|
-
setVariable(session, varName, mod, true); // visible: true
|
|
16
|
-
} catch (err) {
|
|
17
|
-
throw new Error(`<require_module> failed to load module '${name}': ${err}`);
|
|
18
|
-
}
|
|
19
|
-
}
|
package/src/tags/subroutine.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <subroutine> tag - define reusable code block
|
|
3
|
-
* Maps to mask_tag_subroutine in MASK
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { DiracSession, DiracElement, ParameterMetadata } from '../types/index.js';
|
|
7
|
-
import { registerSubroutine } from '../runtime/session.js';
|
|
8
|
-
|
|
9
|
-
export function executeSubroutine(session: DiracSession, element: DiracElement): void {
|
|
10
|
-
// Skip registration if we're in extend mode (nested subroutines already registered)
|
|
11
|
-
if (session.skipSubroutineRegistration) {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const name = element.attributes.name;
|
|
16
|
-
|
|
17
|
-
if (!name) {
|
|
18
|
-
throw new Error('<subroutine> requires name attribute');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Extract metadata from attributes (no structural changes!)
|
|
22
|
-
const description = element.attributes.description;
|
|
23
|
-
const parameters: ParameterMetadata[] = [];
|
|
24
|
-
const meta: Record<string, any> = {};
|
|
25
|
-
|
|
26
|
-
// Parse param- prefixed attributes for metadata
|
|
27
|
-
function parseMetaField(metaString: string) {
|
|
28
|
-
const parts = metaString.split(':');
|
|
29
|
-
return {
|
|
30
|
-
type: parts[0] || 'string',
|
|
31
|
-
required: parts[1] === 'required',
|
|
32
|
-
description: parts[2] || undefined,
|
|
33
|
-
example: parts[3] || undefined
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
for (const [attrName, attrValue] of Object.entries(element.attributes)) {
|
|
37
|
-
if (attrName.startsWith('param-')) {
|
|
38
|
-
const paramName = attrName.substring(6);
|
|
39
|
-
const parts = attrValue.split(':');
|
|
40
|
-
const paramMeta: ParameterMetadata = {
|
|
41
|
-
name: paramName,
|
|
42
|
-
type: parts[0] || 'string',
|
|
43
|
-
required: parts[1] === 'required',
|
|
44
|
-
description: parts[2] || undefined,
|
|
45
|
-
};
|
|
46
|
-
if (parts.length > 3 && parts[3]) {
|
|
47
|
-
paramMeta.enum = parts[3].split('|');
|
|
48
|
-
}
|
|
49
|
-
if (parts.length > 4 && parts[4]) {
|
|
50
|
-
paramMeta.example = parts[4];
|
|
51
|
-
}
|
|
52
|
-
parameters.push(paramMeta);
|
|
53
|
-
} else if (attrName.startsWith('meta-')) {
|
|
54
|
-
const metaName = attrName.substring(5);
|
|
55
|
-
meta[metaName] = parseMetaField(attrValue);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Store subroutine exactly as before (preserves nesting and extends)
|
|
60
|
-
const subroutine: DiracElement = {
|
|
61
|
-
tag: 'subroutine',
|
|
62
|
-
attributes: { ...element.attributes },
|
|
63
|
-
children: element.children,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Pass meta as a field in the subroutine registry, not on the element
|
|
67
|
-
registerSubroutine(
|
|
68
|
-
session,
|
|
69
|
-
name,
|
|
70
|
-
subroutine,
|
|
71
|
-
description,
|
|
72
|
-
parameters.length > 0 ? parameters : undefined,
|
|
73
|
-
Object.keys(meta).length > 0 ? meta : undefined
|
|
74
|
-
);
|
|
75
|
-
}
|
package/src/tags/system.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <system> tag - Execute shell commands
|
|
3
|
-
* Spawns a Unix process and runs the command
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
-
import { substituteVariables, emit } from '../runtime/session.js';
|
|
8
|
-
import { exec, spawn } from 'child_process';
|
|
9
|
-
import { promisify } from 'util';
|
|
10
|
-
import { integrate } from '../runtime/interpreter.js';
|
|
11
|
-
|
|
12
|
-
const execAsync = promisify(exec);
|
|
13
|
-
|
|
14
|
-
export async function executeSystem(session: DiracSession, element: DiracElement): Promise<void> {
|
|
15
|
-
// Build command from text content or children
|
|
16
|
-
let command: string;
|
|
17
|
-
|
|
18
|
-
// Check if has non-text children (actual element children)
|
|
19
|
-
const hasElementChildren = element.children.some(child => child.tag !== '');
|
|
20
|
-
|
|
21
|
-
if (hasElementChildren) {
|
|
22
|
-
// Execute children to build command dynamically
|
|
23
|
-
const beforeOutput = session.output.length;
|
|
24
|
-
|
|
25
|
-
for (const child of element.children) {
|
|
26
|
-
await integrate(session, child);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Collect output from children as the command
|
|
30
|
-
const childOutput = session.output.slice(beforeOutput);
|
|
31
|
-
command = childOutput.join('');
|
|
32
|
-
|
|
33
|
-
// Remove child output from main output
|
|
34
|
-
session.output = session.output.slice(0, beforeOutput);
|
|
35
|
-
} else if (element.text) {
|
|
36
|
-
// Simple text command with variable substitution
|
|
37
|
-
command = substituteVariables(session, element.text);
|
|
38
|
-
} else {
|
|
39
|
-
throw new Error('<system> requires command content');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!command.trim()) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Check for background attribute
|
|
47
|
-
const backgroundAttr = element.attributes?.background;
|
|
48
|
-
const isBackground = backgroundAttr === 'true';
|
|
49
|
-
|
|
50
|
-
if (session.debug) {
|
|
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;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Foreground mode - wait for completion (original behavior)
|
|
73
|
-
try {
|
|
74
|
-
const { stdout, stderr } = await execAsync(command, {
|
|
75
|
-
encoding: 'utf-8',
|
|
76
|
-
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Emit stdout
|
|
80
|
-
if (stdout) {
|
|
81
|
-
emit(session, stdout);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Emit stderr if debug mode
|
|
85
|
-
if (stderr && session.debug) {
|
|
86
|
-
console.error(`[SYSTEM STDERR] ${stderr}`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
} catch (error: any) {
|
|
90
|
-
const errorMsg = error.message || String(error);
|
|
91
|
-
throw new Error(`System command failed: ${errorMsg}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
package/src/tags/tag-check.ts
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <tag-check> tag - validates child tags against allowed subroutines
|
|
3
|
-
* If a tag is not found, uses embedding similarity to suggest the closest match.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
-
import { emit } from '../runtime/session.js';
|
|
8
|
-
// Use global fetch (Node.js 18+)
|
|
9
|
-
import fs from 'fs';
|
|
10
|
-
import yaml from 'js-yaml';
|
|
11
|
-
|
|
12
|
-
// Configurable similarity cutoff
|
|
13
|
-
const SIMILARITY_CUTOFF = 0.75;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// Helper: get embedding server config from config.yml
|
|
17
|
-
function getEmbeddingServerConfig() {
|
|
18
|
-
try {
|
|
19
|
-
const configPath = process.env.DIRAC_CONFIG || 'config.yml';
|
|
20
|
-
const config = yaml.load(fs.readFileSync(configPath, 'utf8')) as any;
|
|
21
|
-
const host = config.embeddingServer?.host || 'localhost';
|
|
22
|
-
const port = config.embeddingServer?.port || 11434;
|
|
23
|
-
const model = config.embeddingServer?.model || 'embeddinggemma';
|
|
24
|
-
return { host, port, model };
|
|
25
|
-
} catch (e) {
|
|
26
|
-
// Fallback to defaults if config file not found
|
|
27
|
-
return { host: 'localhost', port: 11434, model: 'embeddinggemma' };
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Helper: call Ollama embedding API directly
|
|
32
|
-
async function getEmbeddings(tags: string[]): Promise<number[][]> {
|
|
33
|
-
const { host, port, model } = getEmbeddingServerConfig();
|
|
34
|
-
return await Promise.all(tags.map(async tag => {
|
|
35
|
-
const response = await fetch(`http://${host}:${port}/api/embeddings`, {
|
|
36
|
-
method: 'POST',
|
|
37
|
-
headers: { 'Content-Type': 'application/json' },
|
|
38
|
-
body: JSON.stringify({ model, prompt: tag })
|
|
39
|
-
});
|
|
40
|
-
const data = await response.json();
|
|
41
|
-
return data.embedding;
|
|
42
|
-
}));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function cosine(a: number[], b: number[]): number {
|
|
46
|
-
const dot = a.reduce((sum, v, i) => sum + v * b[i], 0);
|
|
47
|
-
const normA = Math.sqrt(a.reduce((sum, v) => sum + v * v, 0));
|
|
48
|
-
const normB = Math.sqrt(b.reduce((sum, v) => sum + v * v, 0));
|
|
49
|
-
return dot / (normA * normB);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function getBestTagMatch(candidate: string, allowed: string[]): Promise<{tag: string, score: number}> {
|
|
53
|
-
const tags = [candidate, ...allowed];
|
|
54
|
-
const embeddings = await getEmbeddings(tags);
|
|
55
|
-
const candidateVec = embeddings[0];
|
|
56
|
-
const allowedVecs = embeddings.slice(1);
|
|
57
|
-
let bestIdx = 0, bestScore = -1;
|
|
58
|
-
allowedVecs.forEach((vec, i) => {
|
|
59
|
-
const score = cosine(candidateVec, vec);
|
|
60
|
-
if (score > bestScore) { bestScore = score; bestIdx = i; }
|
|
61
|
-
});
|
|
62
|
-
return { tag: allowed[bestIdx], score: bestScore };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function executeTagCheck(session: DiracSession, element: DiracElement): Promise<void> {
|
|
66
|
-
// Get allowed subroutine names
|
|
67
|
-
const { getAvailableSubroutines } = await import('../runtime/session.js');
|
|
68
|
-
const subroutines = getAvailableSubroutines(session);
|
|
69
|
-
const allowed = new Set(subroutines.map(s => s.name));
|
|
70
|
-
console.error('[tag-check] Allowed subroutines:', Array.from(allowed));
|
|
71
|
-
|
|
72
|
-
const autocorrect = element.attributes?.autocorrect === "true";
|
|
73
|
-
const shouldExecute = element.attributes?.execute === "true";
|
|
74
|
-
|
|
75
|
-
for (const child of element.children) {
|
|
76
|
-
const tagName = child.tag;
|
|
77
|
-
|
|
78
|
-
// Skip text nodes (empty tag names) and whitespace
|
|
79
|
-
if (!tagName || tagName.trim() === '') {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
console.error(`[tag-check] Checking tag: <${tagName}>`);
|
|
84
|
-
let allValid = false;
|
|
85
|
-
let correctedTag: string | null = null;
|
|
86
|
-
|
|
87
|
-
if (allowed.has(tagName)) {
|
|
88
|
-
// Tag name is valid, now check required and unknown parameters
|
|
89
|
-
const sub = subroutines.find(s => s.name === tagName);
|
|
90
|
-
let missing: string[] = [];
|
|
91
|
-
let unknown: string[] = [];
|
|
92
|
-
let paramNames: string[] = [];
|
|
93
|
-
if (sub && Array.isArray(sub.parameters)) {
|
|
94
|
-
paramNames = sub.parameters.map(p => p.name);
|
|
95
|
-
for (const param of sub.parameters) {
|
|
96
|
-
if (param.required && !(param.name in child.attributes)) {
|
|
97
|
-
missing.push(param.name);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
for (const attr in child.attributes) {
|
|
101
|
-
if (!paramNames.includes(attr)) {
|
|
102
|
-
unknown.push(attr);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (missing.length > 0) {
|
|
107
|
-
emit(session, `<${tagName}/> is missing required parameter(s): ${missing.join(', ')}`);
|
|
108
|
-
console.error(`[tag-check] <${tagName}/> missing required: ${missing.join(', ')}`);
|
|
109
|
-
}
|
|
110
|
-
if (unknown.length > 0) {
|
|
111
|
-
emit(session, `<${tagName}/> has unknown attribute(s): ${unknown.join(', ')}`);
|
|
112
|
-
console.error(`[tag-check] <${tagName}/> unknown attributes: ${unknown.join(', ')}`);
|
|
113
|
-
}
|
|
114
|
-
if (missing.length === 0 && unknown.length === 0) {
|
|
115
|
-
emit(session, `<${tagName}/> is valid.`);
|
|
116
|
-
console.error(`[tag-check] <${tagName}/> is valid.`);
|
|
117
|
-
allValid = true;
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
// Find best semantic match
|
|
121
|
-
console.error(`[tag-check] <${tagName}/> not found, searching for best match...`);
|
|
122
|
-
const best = await getBestTagMatch(tagName, Array.from(allowed));
|
|
123
|
-
console.error(`[tag-check] Best match: <${best.tag}> (score: ${best.score})`);
|
|
124
|
-
if (best.score >= SIMILARITY_CUTOFF) {
|
|
125
|
-
if (autocorrect) {
|
|
126
|
-
emit(session, `The tag <${tagName}> was auto-corrected to <${best.tag}> (similarity: ${best.score.toFixed(2)})`);
|
|
127
|
-
console.error(`[tag-check] Auto-correcting <${tagName}/> to <${best.tag}>`);
|
|
128
|
-
correctedTag = best.tag;
|
|
129
|
-
// Validate parameters for corrected tag
|
|
130
|
-
const sub = subroutines.find(s => s.name === correctedTag);
|
|
131
|
-
let missing: string[] = [];
|
|
132
|
-
let unknown: string[] = [];
|
|
133
|
-
let paramNames: string[] = [];
|
|
134
|
-
if (sub && Array.isArray(sub.parameters)) {
|
|
135
|
-
paramNames = sub.parameters.map(p => p.name);
|
|
136
|
-
for (const param of sub.parameters) {
|
|
137
|
-
if (param.required && !(param.name in child.attributes)) {
|
|
138
|
-
missing.push(param.name);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
for (const attr in child.attributes) {
|
|
142
|
-
if (!paramNames.includes(attr)) {
|
|
143
|
-
unknown.push(attr);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (missing.length > 0) {
|
|
148
|
-
emit(session, `<${correctedTag}/> is missing required parameter(s): ${missing.join(', ')}`);
|
|
149
|
-
console.error(`[tag-check] <${correctedTag}/> missing required: ${missing.join(', ')}`);
|
|
150
|
-
}
|
|
151
|
-
if (unknown.length > 0) {
|
|
152
|
-
emit(session, `<${correctedTag}/> has unknown attribute(s): ${unknown.join(', ')}`);
|
|
153
|
-
console.error(`[tag-check] <${correctedTag}/> unknown attributes: ${unknown.join(', ')}`);
|
|
154
|
-
}
|
|
155
|
-
if (missing.length === 0 && unknown.length === 0) {
|
|
156
|
-
allValid = true;
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
emit(session, `The tag <${tagName}> does not exist. Did you mean <${best.tag}>? (similarity: ${best.score.toFixed(2)})`);
|
|
160
|
-
}
|
|
161
|
-
} else {
|
|
162
|
-
emit(session, `The tag <${tagName}> does not exist and no similar tag was found.`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
// If all checks pass and execute="true" is present, execute the child tag
|
|
166
|
-
if (allValid && shouldExecute) {
|
|
167
|
-
const executeTag = correctedTag || tagName;
|
|
168
|
-
console.error(`[tag-check] Executing <${executeTag}/> as all checks passed and execute=true.`);
|
|
169
|
-
// Create a corrected element if tag was auto-corrected
|
|
170
|
-
const elementToExecute = correctedTag ? { ...child, tag: correctedTag } : child;
|
|
171
|
-
// Dynamically import interpreter and execute the child tag
|
|
172
|
-
const { integrate } = await import('../runtime/interpreter.js');
|
|
173
|
-
await integrate(session, elementToExecute);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
package/src/tags/test-if.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <test-if> tag - conditional execution with test attribute
|
|
3
|
-
* This is the attribute-based conditional (original Dirac style)
|
|
4
|
-
*
|
|
5
|
-
* Usage: <test-if test="$var == value">...</test-if>
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
9
|
-
import { substituteAttribute } from '../runtime/session.js';
|
|
10
|
-
import { integrateChildren } from '../runtime/interpreter.js';
|
|
11
|
-
|
|
12
|
-
export async function executeTestIf(session: DiracSession, element: DiracElement): Promise<void> {
|
|
13
|
-
const test = element.attributes.test;
|
|
14
|
-
|
|
15
|
-
if (!test) {
|
|
16
|
-
throw new Error('<test-if> requires test attribute');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Get variable value
|
|
20
|
-
const value = substituteAttribute(session, test);
|
|
21
|
-
|
|
22
|
-
// Check comparison attributes
|
|
23
|
-
const eq = element.attributes.eq;
|
|
24
|
-
const ne = element.attributes.ne;
|
|
25
|
-
const lt = element.attributes.lt;
|
|
26
|
-
const gt = element.attributes.gt;
|
|
27
|
-
const le = element.attributes.le;
|
|
28
|
-
const ge = element.attributes.ge;
|
|
29
|
-
|
|
30
|
-
let condition = false;
|
|
31
|
-
|
|
32
|
-
if (eq !== undefined) {
|
|
33
|
-
const compareValue = substituteAttribute(session, eq);
|
|
34
|
-
condition = value === compareValue;
|
|
35
|
-
} else if (ne !== undefined) {
|
|
36
|
-
const compareValue = substituteAttribute(session, ne);
|
|
37
|
-
condition = value !== compareValue;
|
|
38
|
-
} else if (lt !== undefined) {
|
|
39
|
-
const compareValue = substituteAttribute(session, lt);
|
|
40
|
-
const valueNum = parseFloat(value);
|
|
41
|
-
const compareNum = parseFloat(compareValue);
|
|
42
|
-
condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum < compareNum;
|
|
43
|
-
} else if (gt !== undefined) {
|
|
44
|
-
const compareValue = substituteAttribute(session, gt);
|
|
45
|
-
const valueNum = parseFloat(value);
|
|
46
|
-
const compareNum = parseFloat(compareValue);
|
|
47
|
-
condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum > compareNum;
|
|
48
|
-
} else if (le !== undefined) {
|
|
49
|
-
const compareValue = substituteAttribute(session, le);
|
|
50
|
-
const valueNum = parseFloat(value);
|
|
51
|
-
const compareNum = parseFloat(compareValue);
|
|
52
|
-
condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum <= compareNum;
|
|
53
|
-
} else if (ge !== undefined) {
|
|
54
|
-
const compareValue = substituteAttribute(session, ge);
|
|
55
|
-
const valueNum = parseFloat(value);
|
|
56
|
-
const compareNum = parseFloat(compareValue);
|
|
57
|
-
condition = !isNaN(valueNum) && !isNaN(compareNum) && valueNum >= compareNum;
|
|
58
|
-
} else {
|
|
59
|
-
// No comparison attribute, evaluate test value as boolean
|
|
60
|
-
condition = evaluateCondition(session, test);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (condition) {
|
|
64
|
-
await integrateChildren(session, element);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function evaluateCondition(session: DiracSession, test: string): boolean {
|
|
69
|
-
// Substitute variables first
|
|
70
|
-
// Use attribute-style substitution for test condition
|
|
71
|
-
const substituted = substituteAttribute(session, test);
|
|
72
|
-
|
|
73
|
-
// Simple condition evaluation
|
|
74
|
-
// Supports: ==, !=, <, >, <=, >=
|
|
75
|
-
|
|
76
|
-
const operators = ['==', '!=', '<=', '>=', '<', '>'];
|
|
77
|
-
|
|
78
|
-
for (const op of operators) {
|
|
79
|
-
const parts = substituted.split(op);
|
|
80
|
-
if (parts.length === 2) {
|
|
81
|
-
let left = parts[0].trim();
|
|
82
|
-
let right = parts[1].trim();
|
|
83
|
-
const leftNum = parseFloat(left);
|
|
84
|
-
const rightNum = parseFloat(right);
|
|
85
|
-
const bothNumbers = !isNaN(leftNum) && !isNaN(rightNum);
|
|
86
|
-
|
|
87
|
-
switch (op) {
|
|
88
|
-
case '==':
|
|
89
|
-
return bothNumbers ? leftNum === rightNum : left === right;
|
|
90
|
-
case '!=':
|
|
91
|
-
return bothNumbers ? leftNum !== rightNum : left !== right;
|
|
92
|
-
case '<':
|
|
93
|
-
return leftNum < rightNum;
|
|
94
|
-
case '>':
|
|
95
|
-
return leftNum > rightNum;
|
|
96
|
-
case '<=':
|
|
97
|
-
return leftNum <= rightNum;
|
|
98
|
-
case '>=':
|
|
99
|
-
return leftNum >= rightNum;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// If no operator found, treat as boolean
|
|
105
|
-
// Empty, "0", or "false" is false
|
|
106
|
-
// Everything else is true
|
|
107
|
-
if (substituted === '' || substituted === '0' || substituted === 'false') {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return true;
|
|
112
|
-
}
|