@phuetz/code-buddy 0.1.0 → 0.1.1
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/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
- package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
- package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
- package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
- package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
- package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
- package/README.md +56 -0
- package/dist/agent/agent-state.d.ts +3 -3
- package/dist/agent/agent-state.js +6 -6
- package/dist/agent/agent-state.js.map +1 -1
- package/dist/agent/base-agent.d.ts +4 -4
- package/dist/agent/base-agent.js +22 -9
- package/dist/agent/base-agent.js.map +1 -1
- package/dist/agent/cache-trace.d.ts +56 -0
- package/dist/agent/cache-trace.js +98 -0
- package/dist/agent/cache-trace.js.map +1 -0
- package/dist/agent/codebuddy-agent.js +3 -2
- package/dist/agent/codebuddy-agent.js.map +1 -1
- package/dist/agent/execution/agent-executor.d.ts +4 -4
- package/dist/agent/execution/agent-executor.js +41 -7
- package/dist/agent/execution/agent-executor.js.map +1 -1
- package/dist/agent/facades/agent-context-facade.js +1 -3
- package/dist/agent/facades/agent-context-facade.js.map +1 -1
- package/dist/agent/facades/message-history-manager.js +14 -12
- package/dist/agent/facades/message-history-manager.js.map +1 -1
- package/dist/agent/facades/session-facade.d.ts +3 -3
- package/dist/agent/facades/session-facade.js +6 -6
- package/dist/agent/facades/session-facade.js.map +1 -1
- package/dist/agent/history-repair.d.ts +37 -0
- package/dist/agent/history-repair.js +124 -0
- package/dist/agent/history-repair.js.map +1 -0
- package/dist/agent/specialized/archive-agent.d.ts +3 -0
- package/dist/agent/specialized/archive-agent.js +71 -31
- package/dist/agent/specialized/archive-agent.js.map +1 -1
- package/dist/agent/specialized/security-review/agent.js +19 -8
- package/dist/agent/specialized/security-review/agent.js.map +1 -1
- package/dist/agent/tool-executor.js +5 -0
- package/dist/agent/tool-executor.js.map +1 -1
- package/dist/agent/turn-diff-tracker.d.ts +79 -0
- package/dist/agent/turn-diff-tracker.js +195 -0
- package/dist/agent/turn-diff-tracker.js.map +1 -0
- package/dist/checkpoints/checkpoint-versioning.js +78 -20
- package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
- package/dist/cli/config-loader.js +2 -4
- package/dist/cli/config-loader.js.map +1 -1
- package/dist/commands/handlers/fcs-handlers.js +1 -1
- package/dist/commands/handlers/fcs-handlers.js.map +1 -1
- package/dist/commands/handlers/memory-handlers.js +2 -1
- package/dist/commands/handlers/memory-handlers.js.map +1 -1
- package/dist/commands/handlers/worktree-handlers.js +11 -0
- package/dist/commands/handlers/worktree-handlers.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +66 -7
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/pipeline.js +25 -13
- package/dist/commands/pipeline.js.map +1 -1
- package/dist/config/model-tools.d.ts +41 -0
- package/dist/config/model-tools.js +194 -0
- package/dist/config/model-tools.js.map +1 -0
- package/dist/context/context-manager-v2.d.ts +2 -1
- package/dist/context/context-manager-v2.js +34 -5
- package/dist/context/context-manager-v2.js.map +1 -1
- package/dist/daemon/daemon-manager.js +23 -19
- package/dist/daemon/daemon-manager.js.map +1 -1
- package/dist/database/database-manager.d.ts +4 -0
- package/dist/database/database-manager.js +16 -7
- package/dist/database/database-manager.js.map +1 -1
- package/dist/desktop-automation/nutjs-provider.js +89 -0
- package/dist/desktop-automation/nutjs-provider.js.map +1 -1
- package/dist/fcs/builtins.d.ts +2 -6
- package/dist/fcs/builtins.js +2 -568
- package/dist/fcs/builtins.js.map +1 -1
- package/dist/fcs/codebuddy-bindings.d.ts +3 -43
- package/dist/fcs/codebuddy-bindings.js +2 -606
- package/dist/fcs/codebuddy-bindings.js.map +1 -1
- package/dist/fcs/index.d.ts +2 -27
- package/dist/fcs/index.js +2 -53
- package/dist/fcs/index.js.map +1 -1
- package/dist/fcs/lexer.d.ts +2 -37
- package/dist/fcs/lexer.js +2 -459
- package/dist/fcs/lexer.js.map +1 -1
- package/dist/fcs/parser.d.ts +2 -68
- package/dist/fcs/parser.js +2 -893
- package/dist/fcs/parser.js.map +1 -1
- package/dist/fcs/runtime.d.ts +2 -59
- package/dist/fcs/runtime.js +2 -623
- package/dist/fcs/runtime.js.map +1 -1
- package/dist/fcs/script-registry.d.ts +3 -69
- package/dist/fcs/script-registry.js +2 -219
- package/dist/fcs/script-registry.js.map +1 -1
- package/dist/fcs/sync-bindings.d.ts +3 -101
- package/dist/fcs/sync-bindings.js +2 -410
- package/dist/fcs/sync-bindings.js.map +1 -1
- package/dist/fcs/types.d.ts +2 -285
- package/dist/fcs/types.js +2 -103
- package/dist/fcs/types.js.map +1 -1
- package/dist/hooks/use-input-handler.d.ts +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/input/voice-control.js +11 -5
- package/dist/input/voice-control.js.map +1 -1
- package/dist/integrations/json-rpc/server.js +5 -5
- package/dist/integrations/json-rpc/server.js.map +1 -1
- package/dist/integrations/mcp/mcp-server.js +1 -1
- package/dist/integrations/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/client.js +2 -1
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/config.js +89 -5
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/mcp-client.js +65 -14
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/transports.d.ts +0 -1
- package/dist/mcp/transports.js +1 -5
- package/dist/mcp/transports.js.map +1 -1
- package/dist/mcp/types.d.ts +2 -0
- package/dist/persistence/session-lock.d.ts +42 -0
- package/dist/persistence/session-lock.js +165 -0
- package/dist/persistence/session-lock.js.map +1 -0
- package/dist/persistence/session-store.d.ts +18 -3
- package/dist/persistence/session-store.js +90 -21
- package/dist/persistence/session-store.js.map +1 -1
- package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
- package/dist/plugins/isolated-plugin-runner.js +19 -1
- package/dist/plugins/isolated-plugin-runner.js.map +1 -1
- package/dist/providers/local-llm-provider.js +21 -4
- package/dist/providers/local-llm-provider.js.map +1 -1
- package/dist/sandbox/docker-sandbox.js +7 -4
- package/dist/sandbox/docker-sandbox.js.map +1 -1
- package/dist/scripting/builtins.d.ts +8 -3
- package/dist/scripting/builtins.js +506 -355
- package/dist/scripting/builtins.js.map +1 -1
- package/dist/scripting/codebuddy-bindings.d.ts +47 -0
- package/dist/scripting/codebuddy-bindings.js +487 -0
- package/dist/scripting/codebuddy-bindings.js.map +1 -0
- package/dist/scripting/index.d.ts +33 -30
- package/dist/scripting/index.js +41 -36
- package/dist/scripting/index.js.map +1 -1
- package/dist/scripting/lexer.d.ts +31 -13
- package/dist/scripting/lexer.js +379 -292
- package/dist/scripting/lexer.js.map +1 -1
- package/dist/scripting/parser.d.ts +63 -44
- package/dist/scripting/parser.js +700 -473
- package/dist/scripting/parser.js.map +1 -1
- package/dist/scripting/runtime.d.ts +55 -24
- package/dist/scripting/runtime.js +600 -288
- package/dist/scripting/runtime.js.map +1 -1
- package/dist/scripting/script-registry.d.ts +54 -0
- package/dist/scripting/script-registry.js +202 -0
- package/dist/scripting/script-registry.js.map +1 -0
- package/dist/scripting/sync-bindings.d.ts +105 -0
- package/dist/scripting/sync-bindings.js +353 -0
- package/dist/scripting/sync-bindings.js.map +1 -0
- package/dist/scripting/types.d.ts +297 -199
- package/dist/scripting/types.js +86 -60
- package/dist/scripting/types.js.map +1 -1
- package/dist/search/usearch-index.js +42 -7
- package/dist/search/usearch-index.js.map +1 -1
- package/dist/security/bash-parser.d.ts +51 -0
- package/dist/security/bash-parser.js +327 -0
- package/dist/security/bash-parser.js.map +1 -0
- package/dist/security/skill-scanner.d.ts +36 -0
- package/dist/security/skill-scanner.js +149 -0
- package/dist/security/skill-scanner.js.map +1 -0
- package/dist/security/trust-folders.d.ts +1 -0
- package/dist/security/trust-folders.js +19 -1
- package/dist/security/trust-folders.js.map +1 -1
- package/dist/server/websocket/handler.js +15 -5
- package/dist/server/websocket/handler.js.map +1 -1
- package/dist/skills/eligibility.js +26 -4
- package/dist/skills/eligibility.js.map +1 -1
- package/dist/tasks/background-tasks.js +5 -1
- package/dist/tasks/background-tasks.js.map +1 -1
- package/dist/tools/apply-patch.d.ts +55 -0
- package/dist/tools/apply-patch.js +273 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/registry/bash-tools.js +6 -3
- package/dist/tools/registry/bash-tools.js.map +1 -1
- package/dist/tools/registry/misc-tools.js +1 -2
- package/dist/tools/registry/misc-tools.js.map +1 -1
- package/dist/tools/registry/search-tools.js +1 -1
- package/dist/tools/registry/search-tools.js.map +1 -1
- package/dist/tools/registry/text-editor-tools.js +1 -1
- package/dist/tools/registry/text-editor-tools.js.map +1 -1
- package/dist/tools/registry/todo-tools.js +37 -5
- package/dist/tools/registry/todo-tools.js.map +1 -1
- package/dist/tools/registry/tool-registry.js +5 -4
- package/dist/tools/registry/tool-registry.js.map +1 -1
- package/dist/tools/registry/web-tools.d.ts +1 -1
- package/dist/tools/registry/web-tools.js +28 -8
- package/dist/tools/registry/web-tools.js.map +1 -1
- package/dist/tools/text-editor.d.ts +1 -1
- package/dist/tools/text-editor.js +23 -5
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/tools/web-search.d.ts +52 -37
- package/dist/tools/web-search.js +368 -163
- package/dist/tools/web-search.js.map +1 -1
- package/dist/ui/components/ChatInterface.d.ts +1 -1
- package/dist/utils/head-tail-truncation.d.ts +34 -0
- package/dist/utils/head-tail-truncation.js +98 -0
- package/dist/utils/head-tail-truncation.js.map +1 -0
- package/dist/utils/sanitize.d.ts +5 -0
- package/dist/utils/sanitize.js +19 -0
- package/dist/utils/sanitize.js.map +1 -1
- package/dist/utils/settings-manager.js +4 -4
- package/dist/utils/settings-manager.js.map +1 -1
- package/package.json +3 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Unified Script Built-in Functions
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Merged from FCS builtins (100+ functions, date/time, format, enumerate/zip)
|
|
5
|
+
* and Buddy Script builtins (ai.*, console.*, lazy agent loading, sandboxing)
|
|
6
|
+
*
|
|
7
|
+
* Extensions: .bs (primary), .fcs (backward-compatible alias)
|
|
5
8
|
*/
|
|
6
9
|
import * as fs from 'fs';
|
|
7
10
|
import * as path from 'path';
|
|
@@ -13,15 +16,12 @@ let cachedAgent = null;
|
|
|
13
16
|
* Get or create the AI agent for script execution
|
|
14
17
|
*/
|
|
15
18
|
async function getOrCreateAgent(config) {
|
|
16
|
-
// Use provided agent if available
|
|
17
19
|
if (config.agent) {
|
|
18
20
|
return config.agent;
|
|
19
21
|
}
|
|
20
|
-
// Return cached agent if already created
|
|
21
22
|
if (cachedAgent) {
|
|
22
23
|
return cachedAgent;
|
|
23
24
|
}
|
|
24
|
-
// Try to create a new agent with lazy loading
|
|
25
25
|
const apiKey = process.env.GROK_API_KEY;
|
|
26
26
|
if (!apiKey) {
|
|
27
27
|
logger.warn('GROK_API_KEY not set, AI operations will not be available');
|
|
@@ -40,12 +40,12 @@ async function getOrCreateAgent(config) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
* Create all built-in functions for the
|
|
43
|
+
* Create all built-in functions for the unified scripting runtime
|
|
44
44
|
*/
|
|
45
45
|
export function createBuiltins(config, print) {
|
|
46
46
|
const builtins = {};
|
|
47
47
|
// ============================================
|
|
48
|
-
// Core
|
|
48
|
+
// Core I/O
|
|
49
49
|
// ============================================
|
|
50
50
|
builtins.print = (...args) => {
|
|
51
51
|
const message = args.map(a => stringify(a)).join(' ');
|
|
@@ -56,13 +56,47 @@ export function createBuiltins(config, print) {
|
|
|
56
56
|
builtins.print(...args);
|
|
57
57
|
return null;
|
|
58
58
|
};
|
|
59
|
-
builtins.
|
|
60
|
-
if (
|
|
61
|
-
|
|
59
|
+
builtins.input = async (prompt) => {
|
|
60
|
+
if (prompt)
|
|
61
|
+
print(prompt);
|
|
62
|
+
return '';
|
|
63
|
+
};
|
|
64
|
+
// ============================================
|
|
65
|
+
// Type Conversion
|
|
66
|
+
// ============================================
|
|
67
|
+
builtins.int = (value) => {
|
|
68
|
+
if (typeof value === 'string') {
|
|
69
|
+
if (value.startsWith('0x') || value.startsWith('0X')) {
|
|
70
|
+
return parseInt(value, 16);
|
|
71
|
+
}
|
|
72
|
+
if (value.startsWith('0b') || value.startsWith('0B')) {
|
|
73
|
+
return parseInt(value.substring(2), 2);
|
|
74
|
+
}
|
|
75
|
+
return parseInt(value, 10);
|
|
76
|
+
}
|
|
77
|
+
return Math.floor(Number(value));
|
|
78
|
+
};
|
|
79
|
+
builtins.float = (value) => parseFloat(String(value));
|
|
80
|
+
builtins.num = (value) => Number(value);
|
|
81
|
+
builtins.str = (value) => stringify(value);
|
|
82
|
+
builtins.bool = (value) => {
|
|
83
|
+
if (value === null || value === undefined || value === false || value === 0 || value === '') {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
};
|
|
88
|
+
builtins.array = (value) => {
|
|
62
89
|
if (Array.isArray(value))
|
|
63
|
-
return
|
|
64
|
-
|
|
90
|
+
return value;
|
|
91
|
+
if (typeof value === 'string')
|
|
92
|
+
return value.split('');
|
|
93
|
+
if (typeof value === 'object' && value !== null)
|
|
94
|
+
return Object.values(value);
|
|
95
|
+
return [value];
|
|
65
96
|
};
|
|
97
|
+
// ============================================
|
|
98
|
+
// Collection Functions
|
|
99
|
+
// ============================================
|
|
66
100
|
builtins.len = (value) => {
|
|
67
101
|
if (typeof value === 'string')
|
|
68
102
|
return value.length;
|
|
@@ -72,81 +106,47 @@ export function createBuiltins(config, print) {
|
|
|
72
106
|
return Object.keys(value).length;
|
|
73
107
|
return 0;
|
|
74
108
|
};
|
|
75
|
-
builtins.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
builtins.range = (...args) => {
|
|
110
|
+
let start = 0, end = 0, step = 1;
|
|
111
|
+
if (args.length === 1) {
|
|
112
|
+
end = args[0];
|
|
113
|
+
}
|
|
114
|
+
else if (args.length === 2) {
|
|
115
|
+
start = args[0];
|
|
116
|
+
end = args[1];
|
|
117
|
+
}
|
|
118
|
+
else if (args.length >= 3) {
|
|
119
|
+
start = args[0];
|
|
120
|
+
end = args[1];
|
|
121
|
+
step = args[2];
|
|
122
|
+
}
|
|
88
123
|
const result = [];
|
|
89
|
-
if (
|
|
90
|
-
for (let i =
|
|
124
|
+
if (step > 0) {
|
|
125
|
+
for (let i = start; i < end; i += step)
|
|
91
126
|
result.push(i);
|
|
92
127
|
}
|
|
93
|
-
else if (
|
|
94
|
-
for (let i =
|
|
128
|
+
else if (step < 0) {
|
|
129
|
+
for (let i = start; i > end; i += step)
|
|
95
130
|
result.push(i);
|
|
96
131
|
}
|
|
97
132
|
return result;
|
|
98
133
|
};
|
|
99
|
-
builtins.
|
|
100
|
-
|
|
101
|
-
return Object.keys(obj);
|
|
102
|
-
}
|
|
103
|
-
return [];
|
|
104
|
-
};
|
|
105
|
-
builtins.values = (obj) => {
|
|
106
|
-
if (typeof obj === 'object' && obj !== null) {
|
|
107
|
-
return Object.values(obj);
|
|
108
|
-
}
|
|
109
|
-
return [];
|
|
110
|
-
};
|
|
111
|
-
builtins.entries = (obj) => {
|
|
112
|
-
if (typeof obj === 'object' && obj !== null) {
|
|
113
|
-
return Object.entries(obj);
|
|
114
|
-
}
|
|
115
|
-
return [];
|
|
116
|
-
};
|
|
117
|
-
builtins.push = (arr, ...items) => {
|
|
118
|
-
if (Array.isArray(arr)) {
|
|
119
|
-
arr.push(...items);
|
|
120
|
-
return arr; // Return array for chaining
|
|
121
|
-
}
|
|
122
|
-
throw new Error('push() requires an array as first argument. Got: ' + (typeof arr));
|
|
134
|
+
builtins.enumerate = (arr) => {
|
|
135
|
+
return arr.map((item, index) => [index, item]);
|
|
123
136
|
};
|
|
124
|
-
builtins.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
};
|
|
130
|
-
builtins.join = (arr, separator) => {
|
|
131
|
-
if (Array.isArray(arr)) {
|
|
132
|
-
return arr.map(stringify).join(separator !== undefined ? String(separator) : ',');
|
|
133
|
-
}
|
|
134
|
-
throw new Error('join() requires an array as first argument. Got: ' + (typeof arr));
|
|
135
|
-
};
|
|
136
|
-
builtins.split = (str, separator) => {
|
|
137
|
-
return String(str).split(separator !== undefined ? String(separator) : '');
|
|
138
|
-
};
|
|
139
|
-
builtins.slice = (arr, start, end) => {
|
|
140
|
-
if (Array.isArray(arr) || typeof arr === 'string') {
|
|
141
|
-
return arr.slice(start !== undefined ? Number(start) : undefined, end !== undefined ? Number(end) : undefined);
|
|
137
|
+
builtins.zip = (...arrays) => {
|
|
138
|
+
const minLen = Math.min(...arrays.map(a => a.length));
|
|
139
|
+
const result = [];
|
|
140
|
+
for (let i = 0; i < minLen; i++) {
|
|
141
|
+
result.push(arrays.map(a => a[i]));
|
|
142
142
|
}
|
|
143
|
-
|
|
143
|
+
return result;
|
|
144
144
|
};
|
|
145
145
|
builtins.map = async (arr, fn) => {
|
|
146
146
|
if (!Array.isArray(arr))
|
|
147
|
-
throw new Error('map() requires an array as first argument
|
|
147
|
+
throw new Error('map() requires an array as first argument');
|
|
148
148
|
if (typeof fn !== 'function')
|
|
149
|
-
throw new Error('map() requires a function as second argument
|
|
149
|
+
throw new Error('map() requires a function as second argument');
|
|
150
150
|
const results = [];
|
|
151
151
|
for (let i = 0; i < arr.length; i++) {
|
|
152
152
|
results.push(await fn(arr[i], i));
|
|
@@ -155,29 +155,82 @@ export function createBuiltins(config, print) {
|
|
|
155
155
|
};
|
|
156
156
|
builtins.filter = async (arr, fn) => {
|
|
157
157
|
if (!Array.isArray(arr))
|
|
158
|
-
throw new Error('filter() requires an array as first argument
|
|
158
|
+
throw new Error('filter() requires an array as first argument');
|
|
159
159
|
if (typeof fn !== 'function')
|
|
160
|
-
throw new Error('filter() requires a function as second argument
|
|
160
|
+
throw new Error('filter() requires a function as second argument');
|
|
161
161
|
const results = [];
|
|
162
162
|
for (let i = 0; i < arr.length; i++) {
|
|
163
|
-
if (await fn(arr[i], i))
|
|
163
|
+
if (await fn(arr[i], i))
|
|
164
164
|
results.push(arr[i]);
|
|
165
|
-
}
|
|
166
165
|
}
|
|
167
166
|
return results;
|
|
168
167
|
};
|
|
168
|
+
builtins.reduce = async (arr, fn, initial) => {
|
|
169
|
+
let acc = initial !== undefined ? initial : arr[0];
|
|
170
|
+
const startIdx = initial !== undefined ? 0 : 1;
|
|
171
|
+
for (let i = startIdx; i < arr.length; i++) {
|
|
172
|
+
acc = await fn(acc, arr[i], i);
|
|
173
|
+
}
|
|
174
|
+
return acc;
|
|
175
|
+
};
|
|
169
176
|
builtins.find = async (arr, fn) => {
|
|
170
177
|
if (!Array.isArray(arr))
|
|
171
|
-
throw new Error('find() requires an array as first argument
|
|
178
|
+
throw new Error('find() requires an array as first argument');
|
|
172
179
|
if (typeof fn !== 'function')
|
|
173
|
-
throw new Error('find() requires a function as second argument
|
|
180
|
+
throw new Error('find() requires a function as second argument');
|
|
174
181
|
for (let i = 0; i < arr.length; i++) {
|
|
175
|
-
if (await fn(arr[i], i))
|
|
182
|
+
if (await fn(arr[i], i))
|
|
176
183
|
return arr[i];
|
|
177
|
-
}
|
|
178
184
|
}
|
|
179
185
|
return null;
|
|
180
186
|
};
|
|
187
|
+
builtins.every = async (arr, fn) => {
|
|
188
|
+
for (const item of arr) {
|
|
189
|
+
if (!(await fn(item)))
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
};
|
|
194
|
+
builtins.some = async (arr, fn) => {
|
|
195
|
+
for (const item of arr) {
|
|
196
|
+
if (await fn(item))
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
};
|
|
201
|
+
builtins.sort = (arr, fn) => {
|
|
202
|
+
const copy = [...arr];
|
|
203
|
+
if (fn) {
|
|
204
|
+
copy.sort((a, b) => {
|
|
205
|
+
const result = fn(a, b);
|
|
206
|
+
return typeof result === 'number' ? result : 0;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
copy.sort((a, b) => {
|
|
211
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
212
|
+
return a - b;
|
|
213
|
+
return String(a).localeCompare(String(b));
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return copy;
|
|
217
|
+
};
|
|
218
|
+
builtins.reverse = (arr) => [...arr].reverse();
|
|
219
|
+
builtins.slice = (arr, start, end) => arr.slice(start, end);
|
|
220
|
+
builtins.concat = (...arrays) => arrays.flat();
|
|
221
|
+
builtins.push = (arr, ...items) => {
|
|
222
|
+
if (!Array.isArray(arr))
|
|
223
|
+
throw new Error('push() requires an array as first argument');
|
|
224
|
+
arr.push(...items);
|
|
225
|
+
return arr;
|
|
226
|
+
};
|
|
227
|
+
builtins.pop = (arr) => {
|
|
228
|
+
if (!Array.isArray(arr))
|
|
229
|
+
throw new Error('pop() requires an array as first argument');
|
|
230
|
+
return arr.pop();
|
|
231
|
+
};
|
|
232
|
+
builtins.shift = (arr) => arr.shift();
|
|
233
|
+
builtins.unshift = (arr, ...items) => { arr.unshift(...items); return arr; };
|
|
181
234
|
builtins.includes = (arr, item) => {
|
|
182
235
|
if (Array.isArray(arr))
|
|
183
236
|
return arr.includes(item);
|
|
@@ -185,28 +238,71 @@ export function createBuiltins(config, print) {
|
|
|
185
238
|
return arr.includes(String(item));
|
|
186
239
|
return false;
|
|
187
240
|
};
|
|
241
|
+
builtins.indexOf = (arr, item) => arr.indexOf(item);
|
|
242
|
+
builtins.join = (arr, separator = ',') => {
|
|
243
|
+
if (!Array.isArray(arr))
|
|
244
|
+
throw new Error('join() requires an array as first argument');
|
|
245
|
+
return arr.map(a => stringify(a)).join(separator);
|
|
246
|
+
};
|
|
247
|
+
builtins.split = (str, separator) => String(str).split(separator);
|
|
248
|
+
builtins.keys = (obj) => {
|
|
249
|
+
if (typeof obj === 'object' && obj !== null)
|
|
250
|
+
return Object.keys(obj);
|
|
251
|
+
return [];
|
|
252
|
+
};
|
|
253
|
+
builtins.values = (obj) => {
|
|
254
|
+
if (typeof obj === 'object' && obj !== null)
|
|
255
|
+
return Object.values(obj);
|
|
256
|
+
return [];
|
|
257
|
+
};
|
|
258
|
+
builtins.entries = (obj) => {
|
|
259
|
+
if (typeof obj === 'object' && obj !== null)
|
|
260
|
+
return Object.entries(obj);
|
|
261
|
+
return [];
|
|
262
|
+
};
|
|
188
263
|
// ============================================
|
|
189
264
|
// String Functions
|
|
190
265
|
// ============================================
|
|
191
|
-
builtins.trim = (str) => String(str).trim();
|
|
192
|
-
builtins.lower = (str) => String(str).toLowerCase();
|
|
193
266
|
builtins.upper = (str) => String(str).toUpperCase();
|
|
194
|
-
builtins.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
builtins.
|
|
198
|
-
|
|
267
|
+
builtins.lower = (str) => String(str).toLowerCase();
|
|
268
|
+
builtins.trim = (str) => String(str).trim();
|
|
269
|
+
builtins.ltrim = (str) => String(str).trimStart();
|
|
270
|
+
builtins.rtrim = (str) => String(str).trimEnd();
|
|
271
|
+
builtins.startsWith = (str, prefix) => String(str).startsWith(prefix);
|
|
272
|
+
builtins.endsWith = (str, suffix) => String(str).endsWith(suffix);
|
|
273
|
+
builtins.contains = (str, substr) => String(str).includes(substr);
|
|
274
|
+
builtins.replace = (str, search, replace) => String(str).split(search).join(replace);
|
|
275
|
+
builtins.replaceAll = (str, search, replace) => String(str).split(search).join(replace);
|
|
276
|
+
builtins.substr = (str, start, length) => {
|
|
277
|
+
if (length !== undefined)
|
|
278
|
+
return String(str).substring(start, start + length);
|
|
279
|
+
return String(str).substring(start);
|
|
280
|
+
};
|
|
281
|
+
builtins.charAt = (str, index) => String(str).charAt(index);
|
|
282
|
+
builtins.repeat = (str, count) => String(str).repeat(count);
|
|
283
|
+
builtins.padStart = (str, length, char = ' ') => String(str).padStart(length, char);
|
|
284
|
+
builtins.padEnd = (str, length, char = ' ') => String(str).padEnd(length, char);
|
|
285
|
+
builtins.format = (template, ...args) => {
|
|
286
|
+
let result = template;
|
|
287
|
+
for (let i = 0; i < args.length; i++) {
|
|
288
|
+
result = result.replace(`{${i}}`, stringify(args[i]));
|
|
289
|
+
result = result.replace('{}', stringify(args[i]));
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
199
292
|
};
|
|
200
|
-
builtins.startsWith = (str, prefix) => String(str).startsWith(String(prefix));
|
|
201
|
-
builtins.endsWith = (str, suffix) => String(str).endsWith(String(suffix));
|
|
202
|
-
builtins.contains = (str, search) => String(str).includes(String(search));
|
|
203
293
|
builtins.match = (str, pattern) => {
|
|
204
|
-
const
|
|
205
|
-
return
|
|
294
|
+
const m = String(str).match(new RegExp(String(pattern)));
|
|
295
|
+
return m ? Array.from(m) : null;
|
|
206
296
|
};
|
|
207
297
|
// ============================================
|
|
208
298
|
// Math Functions
|
|
209
299
|
// ============================================
|
|
300
|
+
builtins.abs = Math.abs;
|
|
301
|
+
builtins.ceil = Math.ceil;
|
|
302
|
+
builtins.floor = Math.floor;
|
|
303
|
+
builtins.round = Math.round;
|
|
304
|
+
builtins.sqrt = Math.sqrt;
|
|
305
|
+
builtins.pow = Math.pow;
|
|
210
306
|
builtins.min = (...args) => {
|
|
211
307
|
const nums = args.flat().map(Number);
|
|
212
308
|
return Math.min(...nums);
|
|
@@ -215,323 +311,363 @@ export function createBuiltins(config, print) {
|
|
|
215
311
|
const nums = args.flat().map(Number);
|
|
216
312
|
return Math.max(...nums);
|
|
217
313
|
};
|
|
218
|
-
builtins.
|
|
219
|
-
builtins.
|
|
220
|
-
builtins.
|
|
221
|
-
builtins.
|
|
222
|
-
builtins.
|
|
223
|
-
builtins.
|
|
314
|
+
builtins.sin = Math.sin;
|
|
315
|
+
builtins.cos = Math.cos;
|
|
316
|
+
builtins.tan = Math.tan;
|
|
317
|
+
builtins.log = Math.log;
|
|
318
|
+
builtins.log10 = Math.log10;
|
|
319
|
+
builtins.exp = Math.exp;
|
|
224
320
|
builtins.random = (max) => {
|
|
225
|
-
if (max !== undefined)
|
|
321
|
+
if (max !== undefined)
|
|
226
322
|
return Math.floor(Math.random() * Number(max));
|
|
227
|
-
}
|
|
228
323
|
return Math.random();
|
|
229
324
|
};
|
|
325
|
+
builtins.randomInt = (min, max) => {
|
|
326
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
327
|
+
};
|
|
328
|
+
builtins.sum = (arr) => arr.reduce((a, b) => a + b, 0);
|
|
329
|
+
builtins.avg = (arr) => arr.length > 0 ? arr.reduce((a, b) => a + b, 0) / arr.length : 0;
|
|
330
|
+
// Constants
|
|
331
|
+
builtins.PI = Math.PI;
|
|
332
|
+
builtins.E = Math.E;
|
|
230
333
|
// ============================================
|
|
231
|
-
// Time Functions
|
|
334
|
+
// Date/Time Functions
|
|
232
335
|
// ============================================
|
|
233
|
-
builtins.
|
|
234
|
-
builtins.
|
|
235
|
-
builtins.
|
|
236
|
-
|
|
237
|
-
|
|
336
|
+
builtins.now = () => Date.now();
|
|
337
|
+
builtins.date = () => new Date().toISOString().split('T')[0];
|
|
338
|
+
builtins.time = () => new Date().toISOString().split('T')[1].split('.')[0];
|
|
339
|
+
builtins.datetime = () => new Date().toISOString();
|
|
340
|
+
builtins.timestamp = () => Math.floor(Date.now() / 1000);
|
|
341
|
+
builtins.formatDate = (timestamp, format) => {
|
|
342
|
+
const d = new Date(timestamp);
|
|
343
|
+
if (!format)
|
|
344
|
+
return d.toISOString();
|
|
345
|
+
return format
|
|
346
|
+
.replace('YYYY', String(d.getFullYear()))
|
|
347
|
+
.replace('MM', String(d.getMonth() + 1).padStart(2, '0'))
|
|
348
|
+
.replace('DD', String(d.getDate()).padStart(2, '0'))
|
|
349
|
+
.replace('HH', String(d.getHours()).padStart(2, '0'))
|
|
350
|
+
.replace('mm', String(d.getMinutes()).padStart(2, '0'))
|
|
351
|
+
.replace('ss', String(d.getSeconds()).padStart(2, '0'));
|
|
238
352
|
};
|
|
239
353
|
// ============================================
|
|
240
|
-
//
|
|
354
|
+
// JSON Functions
|
|
241
355
|
// ============================================
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
356
|
+
builtins.jsonEncode = (value) => JSON.stringify(value, null, 2);
|
|
357
|
+
builtins.jsonDecode = (str) => JSON.parse(str);
|
|
358
|
+
const json = {
|
|
359
|
+
parse: (str) => JSON.parse(String(str)),
|
|
360
|
+
stringify: (value, indent) => {
|
|
361
|
+
return JSON.stringify(value, null, indent ? Number(indent) : undefined);
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
builtins.json = json;
|
|
365
|
+
builtins.JSON = json;
|
|
366
|
+
// ============================================
|
|
367
|
+
// Console (from Buddy Script)
|
|
368
|
+
// ============================================
|
|
369
|
+
builtins.console = {
|
|
370
|
+
log: builtins.print,
|
|
371
|
+
info: builtins.print,
|
|
372
|
+
warn: (...args) => { print('[WARN] ' + args.map(stringify).join(' ')); return null; },
|
|
373
|
+
error: (...args) => { print('[ERROR] ' + args.map(stringify).join(' ')); return null; },
|
|
374
|
+
};
|
|
375
|
+
// ============================================
|
|
376
|
+
// Math namespace (from Buddy Script)
|
|
377
|
+
// ============================================
|
|
378
|
+
builtins.Math = {
|
|
379
|
+
min: builtins.min,
|
|
380
|
+
max: builtins.max,
|
|
381
|
+
abs: builtins.abs,
|
|
382
|
+
floor: builtins.floor,
|
|
383
|
+
ceil: builtins.ceil,
|
|
384
|
+
round: builtins.round,
|
|
385
|
+
sqrt: builtins.sqrt,
|
|
386
|
+
pow: builtins.pow,
|
|
387
|
+
random: () => Math.random(),
|
|
388
|
+
PI: Math.PI,
|
|
389
|
+
E: Math.E,
|
|
390
|
+
};
|
|
391
|
+
builtins.Date = {
|
|
392
|
+
now: () => Date.now(),
|
|
393
|
+
parse: (str) => Date.parse(String(str)),
|
|
394
|
+
};
|
|
395
|
+
// ============================================
|
|
396
|
+
// File Operations (merged FCS flat + BS namespaced)
|
|
397
|
+
// ============================================
|
|
398
|
+
if (config.enableFileOps) {
|
|
399
|
+
// Flat FCS-style file functions
|
|
400
|
+
builtins.readFile = (filePath) => {
|
|
246
401
|
const fullPath = resolvePath(String(filePath), config.workdir);
|
|
402
|
+
return fs.readFileSync(fullPath, 'utf-8');
|
|
403
|
+
};
|
|
404
|
+
builtins.writeFile = (filePath, content) => {
|
|
247
405
|
if (config.dryRun) {
|
|
248
|
-
print(`[DRY RUN]
|
|
249
|
-
return
|
|
406
|
+
print(`[DRY RUN] Would write to: ${filePath}`);
|
|
407
|
+
return true;
|
|
250
408
|
}
|
|
251
|
-
return fs.readFileSync(fullPath, 'utf-8');
|
|
252
|
-
},
|
|
253
|
-
write: (filePath, content) => {
|
|
254
|
-
if (!config.enableFileOps)
|
|
255
|
-
throw new Error('File operations are disabled in the current script configuration. Enable them with { enableFileOps: true }.');
|
|
256
409
|
const fullPath = resolvePath(String(filePath), config.workdir);
|
|
410
|
+
fs.writeFileSync(fullPath, content);
|
|
411
|
+
return true;
|
|
412
|
+
};
|
|
413
|
+
builtins.appendFile = (filePath, content) => {
|
|
257
414
|
if (config.dryRun) {
|
|
258
|
-
print(`[DRY RUN]
|
|
415
|
+
print(`[DRY RUN] Would append to: ${filePath}`);
|
|
259
416
|
return true;
|
|
260
417
|
}
|
|
261
|
-
fs.writeFileSync(fullPath, String(content));
|
|
262
|
-
return true;
|
|
263
|
-
},
|
|
264
|
-
append: (filePath, content) => {
|
|
265
|
-
if (!config.enableFileOps)
|
|
266
|
-
throw new Error('File operations are disabled in the current script configuration. Enable them with { enableFileOps: true }.');
|
|
267
418
|
const fullPath = resolvePath(String(filePath), config.workdir);
|
|
419
|
+
fs.appendFileSync(fullPath, content);
|
|
420
|
+
return true;
|
|
421
|
+
};
|
|
422
|
+
builtins.fileExists = (filePath) => fs.existsSync(resolvePath(String(filePath), config.workdir));
|
|
423
|
+
builtins.isFile = (filePath) => {
|
|
424
|
+
try {
|
|
425
|
+
return fs.statSync(resolvePath(String(filePath), config.workdir)).isFile();
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
builtins.isDir = (filePath) => {
|
|
432
|
+
try {
|
|
433
|
+
return fs.statSync(resolvePath(String(filePath), config.workdir)).isDirectory();
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
builtins.mkdir = (dirPath) => {
|
|
268
440
|
if (config.dryRun) {
|
|
269
|
-
print(`[DRY RUN]
|
|
441
|
+
print(`[DRY RUN] Would create directory: ${dirPath}`);
|
|
270
442
|
return true;
|
|
271
443
|
}
|
|
272
|
-
fs.
|
|
444
|
+
fs.mkdirSync(resolvePath(String(dirPath), config.workdir), { recursive: true });
|
|
273
445
|
return true;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const fullPath = resolvePath(String(filePath), config.workdir);
|
|
277
|
-
return fs.existsSync(fullPath);
|
|
278
|
-
},
|
|
279
|
-
delete: (filePath) => {
|
|
280
|
-
if (!config.enableFileOps)
|
|
281
|
-
throw new Error('File operations are disabled in the current script configuration. Enable them with { enableFileOps: true }.');
|
|
282
|
-
const fullPath = resolvePath(String(filePath), config.workdir);
|
|
446
|
+
};
|
|
447
|
+
builtins.rmdir = (dirPath) => {
|
|
283
448
|
if (config.dryRun) {
|
|
284
|
-
print(`[DRY RUN]
|
|
449
|
+
print(`[DRY RUN] Would remove directory: ${dirPath}`);
|
|
285
450
|
return true;
|
|
286
451
|
}
|
|
287
|
-
fs.
|
|
452
|
+
fs.rmSync(resolvePath(String(dirPath), config.workdir), { recursive: true });
|
|
288
453
|
return true;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (!config.enableFileOps)
|
|
292
|
-
throw new Error('File operations are disabled in the current script configuration. Enable them with { enableFileOps: true }.');
|
|
293
|
-
const srcPath = resolvePath(String(src), config.workdir);
|
|
294
|
-
const destPath = resolvePath(String(dest), config.workdir);
|
|
454
|
+
};
|
|
455
|
+
builtins.remove = (filePath) => {
|
|
295
456
|
if (config.dryRun) {
|
|
296
|
-
print(`[DRY RUN]
|
|
457
|
+
print(`[DRY RUN] Would remove: ${filePath}`);
|
|
297
458
|
return true;
|
|
298
459
|
}
|
|
299
|
-
fs.
|
|
460
|
+
fs.unlinkSync(resolvePath(String(filePath), config.workdir));
|
|
300
461
|
return true;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (!config.enableFileOps)
|
|
304
|
-
throw new Error('File operations are disabled in the current script configuration. Enable them with { enableFileOps: true }.');
|
|
305
|
-
const srcPath = resolvePath(String(src), config.workdir);
|
|
306
|
-
const destPath = resolvePath(String(dest), config.workdir);
|
|
462
|
+
};
|
|
463
|
+
builtins.rename = (oldPath, newPath) => {
|
|
307
464
|
if (config.dryRun) {
|
|
308
|
-
print(`[DRY RUN]
|
|
465
|
+
print(`[DRY RUN] Would rename: ${oldPath} -> ${newPath}`);
|
|
309
466
|
return true;
|
|
310
467
|
}
|
|
311
|
-
fs.renameSync(
|
|
468
|
+
fs.renameSync(resolvePath(String(oldPath), config.workdir), resolvePath(String(newPath), config.workdir));
|
|
312
469
|
return true;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const fullPath = resolvePath(String(dirPath), config.workdir);
|
|
316
|
-
return fs.readdirSync(fullPath);
|
|
317
|
-
},
|
|
318
|
-
mkdir: (dirPath) => {
|
|
319
|
-
if (!config.enableFileOps)
|
|
320
|
-
throw new Error('File operations are disabled in the current script configuration. Enable them with { enableFileOps: true }.');
|
|
321
|
-
const fullPath = resolvePath(String(dirPath), config.workdir);
|
|
470
|
+
};
|
|
471
|
+
builtins.copy = (src, dest) => {
|
|
322
472
|
if (config.dryRun) {
|
|
323
|
-
print(`[DRY RUN]
|
|
473
|
+
print(`[DRY RUN] Would copy: ${src} -> ${dest}`);
|
|
324
474
|
return true;
|
|
325
475
|
}
|
|
326
|
-
fs.
|
|
476
|
+
fs.copyFileSync(resolvePath(String(src), config.workdir), resolvePath(String(dest), config.workdir));
|
|
327
477
|
return true;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
isDirectory: stats.isDirectory(),
|
|
336
|
-
created: stats.birthtime.toISOString(),
|
|
337
|
-
modified: stats.mtime.toISOString(),
|
|
338
|
-
};
|
|
339
|
-
},
|
|
340
|
-
glob: (pattern) => {
|
|
341
|
-
// Simple glob implementation
|
|
342
|
-
const fullPath = resolvePath(String(pattern), config.workdir);
|
|
343
|
-
const dir = path.dirname(fullPath);
|
|
344
|
-
const base = path.basename(fullPath);
|
|
345
|
-
if (!fs.existsSync(dir))
|
|
478
|
+
};
|
|
479
|
+
builtins.listDir = (dirPath) => fs.readdirSync(resolvePath(String(dirPath), config.workdir));
|
|
480
|
+
builtins.glob = (pattern) => {
|
|
481
|
+
const dir = path.dirname(pattern);
|
|
482
|
+
const filePattern = path.basename(pattern);
|
|
483
|
+
const fullDir = resolvePath(dir, config.workdir);
|
|
484
|
+
if (!fs.existsSync(fullDir))
|
|
346
485
|
return [];
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
486
|
+
const regex = new RegExp('^' + filePattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
|
|
487
|
+
return fs.readdirSync(fullDir).filter(f => regex.test(f)).map(f => path.join(dir, f));
|
|
488
|
+
};
|
|
489
|
+
builtins.fileSize = (filePath) => fs.statSync(resolvePath(String(filePath), config.workdir)).size;
|
|
490
|
+
builtins.fileMtime = (filePath) => fs.statSync(resolvePath(String(filePath), config.workdir)).mtimeMs;
|
|
491
|
+
// Path utilities
|
|
492
|
+
builtins.basename = path.basename;
|
|
493
|
+
builtins.dirname = path.dirname;
|
|
494
|
+
builtins.extname = path.extname;
|
|
495
|
+
builtins.joinPath = (...parts) => path.join(...parts);
|
|
496
|
+
builtins.resolvePath = (...parts) => path.resolve(config.workdir, ...parts);
|
|
497
|
+
// BS-style namespaced file object
|
|
498
|
+
builtins.file = {
|
|
499
|
+
read: (filePath) => builtins.readFile(String(filePath)),
|
|
500
|
+
write: (filePath, content) => builtins.writeFile(String(filePath), String(content)),
|
|
501
|
+
append: (filePath, content) => builtins.appendFile(String(filePath), String(content)),
|
|
502
|
+
exists: (filePath) => builtins.fileExists(String(filePath)),
|
|
503
|
+
delete: (filePath) => builtins.remove(String(filePath)),
|
|
504
|
+
copy: (src, dest) => builtins.copy(String(src), String(dest)),
|
|
505
|
+
move: (src, dest) => builtins.rename(String(src), String(dest)),
|
|
506
|
+
list: (dirPath) => builtins.listDir(String(dirPath)),
|
|
507
|
+
mkdir: (dirPath) => builtins.mkdir(String(dirPath)),
|
|
508
|
+
stat: (filePath) => {
|
|
509
|
+
const stats = fs.statSync(resolvePath(String(filePath), config.workdir));
|
|
510
|
+
return { size: stats.size, isFile: stats.isFile(), isDirectory: stats.isDirectory(), created: stats.birthtime.toISOString(), modified: stats.mtime.toISOString() };
|
|
511
|
+
},
|
|
512
|
+
glob: (pattern) => builtins.glob(String(pattern)),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
353
515
|
// ============================================
|
|
354
|
-
// Bash
|
|
516
|
+
// Shell/Bash Functions (merged FCS flat + BS namespaced)
|
|
355
517
|
// ============================================
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if (!config.enableBash)
|
|
359
|
-
throw new Error('Bash/shell commands are disabled in the current script configuration. Enable them with { enableBash: true }.');
|
|
360
|
-
const opts = options || {};
|
|
518
|
+
if (config.enableBash) {
|
|
519
|
+
builtins.exec = (command) => {
|
|
361
520
|
if (config.dryRun) {
|
|
362
|
-
print(`[DRY RUN]
|
|
363
|
-
return
|
|
521
|
+
print(`[DRY RUN] Would execute: ${command}`);
|
|
522
|
+
return '';
|
|
364
523
|
}
|
|
365
524
|
try {
|
|
366
|
-
|
|
367
|
-
cwd: opts.cwd ? String(opts.cwd) : config.workdir,
|
|
368
|
-
encoding: 'utf-8',
|
|
369
|
-
timeout: opts.timeout ? Number(opts.timeout) : 30000,
|
|
370
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
371
|
-
});
|
|
372
|
-
return {
|
|
373
|
-
stdout: stdout.trim(),
|
|
374
|
-
stderr: '',
|
|
375
|
-
code: 0,
|
|
376
|
-
};
|
|
525
|
+
return execSync(command, { cwd: config.workdir, encoding: 'utf-8', timeout: config.timeout });
|
|
377
526
|
}
|
|
378
|
-
catch (
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
stdout: execError.stdout || '',
|
|
382
|
-
stderr: execError.stderr || String(error),
|
|
383
|
-
code: execError.status || 1,
|
|
384
|
-
};
|
|
527
|
+
catch (err) {
|
|
528
|
+
throw new Error(`Command failed: ${err.message}`);
|
|
385
529
|
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
530
|
+
};
|
|
531
|
+
builtins.shell = async (command) => {
|
|
532
|
+
if (config.dryRun) {
|
|
533
|
+
print(`[DRY RUN] Would execute: ${command}`);
|
|
534
|
+
return { code: 0, stdout: '', stderr: '' };
|
|
391
535
|
}
|
|
392
|
-
return
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return new Promise((resolve, reject) => {
|
|
399
|
-
const proc = spawn(String(command), argList, {
|
|
400
|
-
cwd: config.workdir,
|
|
401
|
-
shell: true,
|
|
402
|
-
});
|
|
403
|
-
let stdout = '';
|
|
404
|
-
let stderr = '';
|
|
405
|
-
proc.stdout?.on('data', (data) => {
|
|
406
|
-
stdout += data.toString();
|
|
407
|
-
if (config.verbose)
|
|
408
|
-
print(data.toString());
|
|
409
|
-
});
|
|
410
|
-
proc.stderr?.on('data', (data) => {
|
|
411
|
-
stderr += data.toString();
|
|
412
|
-
});
|
|
413
|
-
proc.on('close', (code) => {
|
|
414
|
-
resolve({ stdout, stderr, code });
|
|
415
|
-
});
|
|
416
|
-
proc.on('error', reject);
|
|
536
|
+
return new Promise((resolve) => {
|
|
537
|
+
const child = spawn(command, { shell: true, cwd: config.workdir });
|
|
538
|
+
let stdout = '', stderr = '';
|
|
539
|
+
child.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
540
|
+
child.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
541
|
+
child.on('close', (code) => { resolve({ code: code ?? 0, stdout, stderr }); });
|
|
417
542
|
});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
|
|
543
|
+
};
|
|
544
|
+
// BS-style namespaced bash object
|
|
545
|
+
builtins.bash = {
|
|
546
|
+
run: (command, options) => {
|
|
547
|
+
if (!config.enableBash)
|
|
548
|
+
throw new Error('Bash commands are disabled');
|
|
549
|
+
const opts = options || {};
|
|
550
|
+
if (config.dryRun) {
|
|
551
|
+
print(`[DRY RUN] bash: ${command}`);
|
|
552
|
+
return { stdout: '', stderr: '', code: 0 };
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
const stdout = execSync(String(command), {
|
|
556
|
+
cwd: opts.cwd ? String(opts.cwd) : config.workdir,
|
|
557
|
+
encoding: 'utf-8',
|
|
558
|
+
timeout: opts.timeout ? Number(opts.timeout) : 30000,
|
|
559
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
560
|
+
});
|
|
561
|
+
return { stdout: stdout.trim(), stderr: '', code: 0 };
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
const execError = error;
|
|
565
|
+
return { stdout: execError.stdout || '', stderr: execError.stderr || String(error), code: execError.status || 1 };
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
exec: (command) => {
|
|
569
|
+
const result = builtins.bash.run(command);
|
|
570
|
+
if (result.code !== 0)
|
|
571
|
+
throw new Error(`Command failed with code ${result.code}`);
|
|
572
|
+
return result.stdout;
|
|
573
|
+
},
|
|
574
|
+
spawn: async (command, args) => {
|
|
575
|
+
if (!config.enableBash)
|
|
576
|
+
throw new Error('Bash commands are disabled');
|
|
577
|
+
const argList = Array.isArray(args) ? args.map(String) : [];
|
|
578
|
+
return new Promise((resolve, reject) => {
|
|
579
|
+
const proc = spawn(String(command), argList, { cwd: config.workdir, shell: true });
|
|
580
|
+
let stdout = '', stderr = '';
|
|
581
|
+
proc.stdout?.on('data', (data) => { stdout += data.toString(); if (config.verbose)
|
|
582
|
+
print(data.toString()); });
|
|
583
|
+
proc.stderr?.on('data', (data) => { stderr += data.toString(); });
|
|
584
|
+
proc.on('close', (code) => { resolve({ stdout, stderr, code }); });
|
|
585
|
+
proc.on('error', reject);
|
|
586
|
+
});
|
|
587
|
+
},
|
|
588
|
+
};
|
|
589
|
+
}
|
|
421
590
|
// ============================================
|
|
422
|
-
// AI Operations (
|
|
591
|
+
// AI Operations (from Buddy Script with lazy agent)
|
|
423
592
|
// ============================================
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
return
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
},
|
|
468
|
-
};
|
|
469
|
-
builtins.ai = ai;
|
|
593
|
+
if (config.enableAI) {
|
|
594
|
+
const ai = {
|
|
595
|
+
ask: async (prompt) => {
|
|
596
|
+
if (config.dryRun) {
|
|
597
|
+
print(`[DRY RUN] ai.ask: ${prompt}`);
|
|
598
|
+
return '[AI Response Placeholder]';
|
|
599
|
+
}
|
|
600
|
+
const agent = await getOrCreateAgent(config);
|
|
601
|
+
if (!agent)
|
|
602
|
+
throw new Error('AI agent not available. Ensure GROK_API_KEY is set.');
|
|
603
|
+
const result = await agent.processUserInput(String(prompt));
|
|
604
|
+
return result.content || '';
|
|
605
|
+
},
|
|
606
|
+
chat: async (message) => {
|
|
607
|
+
if (config.dryRun) {
|
|
608
|
+
print(`[DRY RUN] ai.chat: ${message}`);
|
|
609
|
+
return '[AI Response Placeholder]';
|
|
610
|
+
}
|
|
611
|
+
const agent = await getOrCreateAgent(config);
|
|
612
|
+
if (!agent)
|
|
613
|
+
throw new Error('AI agent not available. Ensure GROK_API_KEY is set.');
|
|
614
|
+
const result = await agent.processUserInput(String(message));
|
|
615
|
+
return result.content || '';
|
|
616
|
+
},
|
|
617
|
+
complete: async (prompt, options) => {
|
|
618
|
+
if (config.dryRun) {
|
|
619
|
+
print(`[DRY RUN] ai.complete: ${prompt}`);
|
|
620
|
+
return '[AI Response Placeholder]';
|
|
621
|
+
}
|
|
622
|
+
const agent = await getOrCreateAgent(config);
|
|
623
|
+
if (!agent)
|
|
624
|
+
throw new Error('AI agent not available. Ensure GROK_API_KEY is set.');
|
|
625
|
+
const opts = typeof options === 'object' && options !== null ? options : {};
|
|
626
|
+
const result = await agent.processUserInput(String(prompt), opts);
|
|
627
|
+
return result.content || '';
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
builtins.ai = ai;
|
|
631
|
+
// FCS-style flat ai/grok function
|
|
632
|
+
builtins.grok = async (prompt) => {
|
|
633
|
+
return ai.ask(prompt);
|
|
634
|
+
};
|
|
635
|
+
}
|
|
470
636
|
// ============================================
|
|
471
637
|
// Environment & Config
|
|
472
638
|
// ============================================
|
|
473
|
-
|
|
639
|
+
builtins.env = {
|
|
474
640
|
get: (name) => process.env[String(name)] || null,
|
|
475
|
-
set: (name, value) => {
|
|
476
|
-
process.env[String(name)] = String(value);
|
|
477
|
-
return true;
|
|
478
|
-
},
|
|
641
|
+
set: (name, value) => { process.env[String(name)] = String(value); return true; },
|
|
479
642
|
all: () => ({ ...process.env }),
|
|
480
643
|
};
|
|
481
|
-
builtins.
|
|
482
|
-
const grok = {
|
|
483
|
-
workdir: () => config.workdir,
|
|
484
|
-
verbose: () => config.verbose,
|
|
485
|
-
dryRun: () => config.dryRun,
|
|
486
|
-
version: () => '1.0.0',
|
|
487
|
-
};
|
|
488
|
-
builtins.grok = grok;
|
|
489
|
-
// ============================================
|
|
490
|
-
// JSON
|
|
491
|
-
// ============================================
|
|
492
|
-
const json = {
|
|
493
|
-
parse: (str) => JSON.parse(String(str)),
|
|
494
|
-
stringify: (value, indent) => {
|
|
495
|
-
return JSON.stringify(value, null, indent ? Number(indent) : undefined);
|
|
496
|
-
},
|
|
497
|
-
};
|
|
498
|
-
builtins.json = json;
|
|
499
|
-
builtins.JSON = json;
|
|
500
|
-
// ============================================
|
|
501
|
-
// Console (alias for print functions)
|
|
502
|
-
// ============================================
|
|
503
|
-
const console = {
|
|
504
|
-
log: builtins.print,
|
|
505
|
-
info: builtins.print,
|
|
506
|
-
warn: (...args) => {
|
|
507
|
-
print('[WARN] ' + args.map(stringify).join(' '));
|
|
508
|
-
return null;
|
|
509
|
-
},
|
|
510
|
-
error: (...args) => {
|
|
511
|
-
print('[ERROR] ' + args.map(stringify).join(' '));
|
|
512
|
-
return null;
|
|
513
|
-
},
|
|
514
|
-
};
|
|
515
|
-
builtins.console = console;
|
|
644
|
+
builtins.cwd = () => config.workdir;
|
|
516
645
|
// ============================================
|
|
517
|
-
//
|
|
646
|
+
// Utility Functions
|
|
518
647
|
// ============================================
|
|
519
|
-
builtins.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
round: builtins.round,
|
|
526
|
-
sqrt: builtins.sqrt,
|
|
527
|
-
pow: builtins.pow,
|
|
528
|
-
random: () => Math.random(),
|
|
529
|
-
PI: Math.PI,
|
|
530
|
-
E: Math.E,
|
|
648
|
+
builtins.typeof = (value) => {
|
|
649
|
+
if (value === null)
|
|
650
|
+
return 'null';
|
|
651
|
+
if (Array.isArray(value))
|
|
652
|
+
return 'array';
|
|
653
|
+
return typeof value;
|
|
531
654
|
};
|
|
532
|
-
builtins.
|
|
533
|
-
|
|
534
|
-
|
|
655
|
+
builtins.isNull = (value) => value === null;
|
|
656
|
+
builtins.isNumber = (value) => typeof value === 'number';
|
|
657
|
+
builtins.isString = (value) => typeof value === 'string';
|
|
658
|
+
builtins.isBool = (value) => typeof value === 'boolean';
|
|
659
|
+
builtins.isArray = (value) => Array.isArray(value);
|
|
660
|
+
builtins.isDict = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
661
|
+
builtins.isFunction = (value) => typeof value === 'function';
|
|
662
|
+
builtins.sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
663
|
+
builtins.exit = (code = 0) => process.exit(code);
|
|
664
|
+
builtins.assert = (condition, message) => {
|
|
665
|
+
if (!condition)
|
|
666
|
+
throw new Error(message || 'Assertion failed');
|
|
667
|
+
};
|
|
668
|
+
builtins.expect = (actual, expected, message) => {
|
|
669
|
+
if (actual !== expected)
|
|
670
|
+
throw new Error(message || `Expected ${stringify(expected)}, got ${stringify(actual)}`);
|
|
535
671
|
};
|
|
536
672
|
return builtins;
|
|
537
673
|
}
|
|
@@ -543,8 +679,21 @@ function stringify(value) {
|
|
|
543
679
|
return 'null';
|
|
544
680
|
if (value === undefined)
|
|
545
681
|
return 'undefined';
|
|
546
|
-
if (typeof value === '
|
|
547
|
-
return
|
|
682
|
+
if (typeof value === 'string')
|
|
683
|
+
return value;
|
|
684
|
+
if (typeof value === 'function')
|
|
685
|
+
return '[Function]';
|
|
686
|
+
if (Array.isArray(value)) {
|
|
687
|
+
return '[' + value.map(v => stringify(v)).join(', ') + ']';
|
|
688
|
+
}
|
|
689
|
+
if (typeof value === 'object') {
|
|
690
|
+
try {
|
|
691
|
+
return JSON.stringify(value);
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
return '[Object]';
|
|
695
|
+
}
|
|
696
|
+
}
|
|
548
697
|
return String(value);
|
|
549
698
|
}
|
|
550
699
|
function resolvePath(filePath, workdir) {
|
|
@@ -552,4 +701,6 @@ function resolvePath(filePath, workdir) {
|
|
|
552
701
|
return filePath;
|
|
553
702
|
return path.resolve(workdir, filePath);
|
|
554
703
|
}
|
|
704
|
+
/** @deprecated Use createBuiltins instead */
|
|
705
|
+
export const createFCSBuiltins = createBuiltins;
|
|
555
706
|
//# sourceMappingURL=builtins.js.map
|