import libraries, increase file read limit (still low). verify wip
This commit is contained in:
1 parent
80454f9a2a
commit
ff65f18f80
3 files changed
+60
-15
No files matched your search
@@ -130,9 +130,6 @@ export default class Horde {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendAction(action, body = {}) {
|
sendAction(action, body = {}) {
|
||||||
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
||||||
return Promise.reject(new Error('WebSocket is not open'));
|
|
||||||
}
|
|
||||||
const nonce = this.nextNonce++;
|
const nonce = this.nextNonce++;
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
this.pending.set(nonce, { resolve, reject });
|
this.pending.set(nonce, { resolve, reject });
|
||||||
|
|||||||
+50
-9
@@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { register } from 'node:module';
|
import { registerHooks } from 'node:module';
|
||||||
|
import { pathToFileURL, fileURLToPath } from 'url';
|
||||||
import Horde from './horde.js';
|
import Horde from './horde.js';
|
||||||
import run from './run.js';
|
import run from './run.js';
|
||||||
|
|
||||||
@@ -28,6 +29,9 @@ const CONFIG_FILE = 'config.json';
|
|||||||
const FUNCTIONS_DIR = 'functions';
|
const FUNCTIONS_DIR = 'functions';
|
||||||
const LIB_DIR = 'lib';
|
const LIB_DIR = 'lib';
|
||||||
|
|
||||||
|
// Prefix used by function modules to import shared libraries from '.gral/lib'.
|
||||||
|
const LIB_PREFIX = '@lib/';
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
host: 'wss://hub.hordelab.com',
|
host: 'wss://hub.hordelab.com',
|
||||||
token: null,
|
token: null,
|
||||||
@@ -43,7 +47,6 @@ if (process.env.HORDE_TOKEN) DEFAULT_CONFIG.token = process.env.HORDE_TOKEN;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Global execution context exposed to function modules as `Gral`.
|
* Global execution context exposed to function modules as `Gral`.
|
||||||
* For now it only carries the current working directory.
|
|
||||||
*/
|
*/
|
||||||
const Gral = {
|
const Gral = {
|
||||||
path: process.cwd(),
|
path: process.cwd(),
|
||||||
@@ -53,6 +56,49 @@ const Gral = {
|
|||||||
|
|
||||||
global.Gral = Gral;
|
global.Gral = Gral;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the ESM format for any file living inside the project's
|
||||||
|
* '.gral' directory, so function and library modules load as modules
|
||||||
|
* regardless of the surrounding workdir's 'package.json'.
|
||||||
|
*
|
||||||
|
* @param {string} url A file URL.
|
||||||
|
* @returns {?string} 'module' for Gral-owned files, otherwise null.
|
||||||
|
*/
|
||||||
|
function gralFormat(url) {
|
||||||
|
let filePath;
|
||||||
|
try {
|
||||||
|
filePath = fileURLToPath(url);
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rel = path.relative(path.join(process.cwd(), CONFIG_DIR), filePath);
|
||||||
|
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null;
|
||||||
|
return 'module';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the resolve hook used to load Gral modules.
|
||||||
|
*
|
||||||
|
* @param {string} root Directory the command was launched in.
|
||||||
|
* @param {string} libDir Absolute path to '.gral/lib'.
|
||||||
|
* @returns {object} Argument for `registerHooks`.
|
||||||
|
*/
|
||||||
|
function createGralHooks(root, libDir) {
|
||||||
|
return {
|
||||||
|
resolve(specifier, context, nextResolve) {
|
||||||
|
if (specifier.startsWith(LIB_PREFIX)) {
|
||||||
|
const target = path.join(libDir, specifier.slice(LIB_PREFIX.length));
|
||||||
|
return { url: pathToFileURL(target).href, format: 'module', shortCircuit: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = nextResolve(specifier, context);
|
||||||
|
const format = result && result.url ? gralFormat(result.url) : null;
|
||||||
|
return format ? { ...result, format } : result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ *
|
/* ------------------------------------------------------------------ *
|
||||||
* 3. Config loading
|
* 3. Config loading
|
||||||
* ------------------------------------------------------------------ */
|
* ------------------------------------------------------------------ */
|
||||||
@@ -215,7 +261,7 @@ async function main(argv) {
|
|||||||
|
|
||||||
// Make '.gral/lib/*' importable from function modules via '@lib/<name>'.
|
// Make '.gral/lib/*' importable from function modules via '@lib/<name>'.
|
||||||
const libDir = path.join(root, CONFIG_DIR, LIB_DIR);
|
const libDir = path.join(root, CONFIG_DIR, LIB_DIR);
|
||||||
await register('./loader.js', import.meta.url, { data: { libDir } });
|
registerHooks(createGralHooks(root, libDir));
|
||||||
|
|
||||||
const functions = await loadFunctions(root);
|
const functions = await loadFunctions(root);
|
||||||
Gral.horde = new Horde({host: config.host, token: config.token});
|
Gral.horde = new Horde({host: config.host, token: config.token});
|
||||||
@@ -253,12 +299,7 @@ async function main(argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.prompt === null) {
|
const input = { prompt: command.prompt || null };
|
||||||
console.error("Error: a prompt is required (-p PROMPT or -f PROMPT_FILE).");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const input = { prompt: command.prompt };
|
|
||||||
|
|
||||||
// Connect before executing
|
// Connect before executing
|
||||||
await Gral.horde.connect();
|
await Gral.horde.connect();
|
||||||
|
|||||||
+10
-3
@@ -16,7 +16,7 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
const MAX_READ_BYTES = 1024;
|
const MAX_READ_BYTES = 10*1024;
|
||||||
const MAX_ITERATIONS = 20;
|
const MAX_ITERATIONS = 20;
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ *
|
/* ------------------------------------------------------------------ *
|
||||||
@@ -66,12 +66,13 @@ function readFile(args, { root, scope }) {
|
|||||||
let lines = fs.readFileSync(abs, 'utf8').split('\n');
|
let lines = fs.readFileSync(abs, 'utf8').split('\n');
|
||||||
const from = args.lineStart != null ? args.lineStart - 1 : 0;
|
const from = args.lineStart != null ? args.lineStart - 1 : 0;
|
||||||
const to = args.lineEnd != null ? args.lineEnd : lines.length;
|
const to = args.lineEnd != null ? args.lineEnd : lines.length;
|
||||||
|
const lineCount = lines.length;
|
||||||
lines = lines.slice(from, to);
|
lines = lines.slice(from, to);
|
||||||
|
|
||||||
let content = lines.join('\n');
|
let content = lines.join('\n');
|
||||||
const bytes = Buffer.from(content, 'utf8');
|
const bytes = Buffer.from(content, 'utf8');
|
||||||
if (bytes.length > MAX_READ_BYTES) {
|
if (bytes.length > MAX_READ_BYTES) {
|
||||||
content = bytes.slice(0, MAX_READ_BYTES).toString('utf8');
|
throw new Error(`too large: ${bytes.length} bytes requested (max ${MAX_READ_BYTES}, ${lineCount} lines in ${file})`);
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
@@ -127,7 +128,7 @@ const DEFAULT_TOOLS = {
|
|||||||
'read-file': {
|
'read-file': {
|
||||||
scope: 'read',
|
scope: 'read',
|
||||||
handler: readFile,
|
handler: readFile,
|
||||||
description: 'Read a file.',
|
description: 'Read up to 10KB from a file, with optional line range.',
|
||||||
parameters: {
|
parameters: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@@ -239,6 +240,7 @@ function makeExecutor(bundle, root) {
|
|||||||
try {
|
try {
|
||||||
return tool.handler(args || {}, { root, scope: tool.scope });
|
return tool.handler(args || {}, { root, scope: tool.scope });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
return `Error: ${err.message}`;
|
return `Error: ${err.message}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -355,6 +357,7 @@ async function run({
|
|||||||
open = null,
|
open = null,
|
||||||
tools = null,
|
tools = null,
|
||||||
defaultTools = true,
|
defaultTools = true,
|
||||||
|
verify = null,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const Gral = global.Gral;
|
const Gral = global.Gral;
|
||||||
const root = Gral && Gral.path ? Gral.path : process.cwd();
|
const root = Gral && Gral.path ? Gral.path : process.cwd();
|
||||||
@@ -420,6 +423,10 @@ async function run({
|
|||||||
if (calls.length) {
|
if (calls.length) {
|
||||||
console.log('called: ' + calls.map(c => c.tool_name));
|
console.log('called: ' + calls.map(c => c.tool_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verify && verify()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`run: exceeded ${MAX_ITERATIONS} tool-call iterations`);
|
throw new Error(`run: exceeded ${MAX_ITERATIONS} tool-call iterations`);
|
||||||
|
|||||||
Reference in new issue
Block a user