@riotprompt/riotprompt 0.0.20 → 1.0.0
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/CHANGELOG.md +74 -0
- package/MIGRATION.md +235 -0
- package/README.md +2 -0
- package/SECURITY.md +132 -0
- package/dist/builder.js +6 -0
- package/dist/builder.js.map +1 -1
- package/dist/{cli.cjs → cli.js} +658 -216
- package/dist/context-manager.js +1 -1
- package/dist/conversation-logger.d.ts +17 -1
- package/dist/conversation-logger.js +21 -17
- package/dist/conversation-logger.js.map +1 -1
- package/dist/conversation.js +1 -1
- package/dist/error-handling.d.ts +52 -0
- package/dist/error-handling.js +132 -0
- package/dist/error-handling.js.map +1 -0
- package/dist/formatter.js +1 -1
- package/dist/iteration-strategy.js +1 -1
- package/dist/loader.js +60 -12
- package/dist/loader.js.map +1 -1
- package/dist/logger.d.ts +52 -0
- package/dist/logger.js +114 -14
- package/dist/logger.js.map +1 -1
- package/dist/logging-config.d.ts +84 -0
- package/dist/logging-config.js +116 -0
- package/dist/logging-config.js.map +1 -0
- package/dist/message-builder.js +1 -1
- package/dist/model-config.js +1 -1
- package/dist/override.js +10 -4
- package/dist/override.js.map +1 -1
- package/dist/recipes.js +6 -0
- package/dist/recipes.js.map +1 -1
- package/dist/reflection.js +1 -1
- package/dist/riotprompt.d.ts +9 -0
- package/dist/riotprompt.js +8 -0
- package/dist/riotprompt.js.map +1 -1
- package/dist/security/audit-logger.d.ts +61 -0
- package/dist/security/audit-logger.js +281 -0
- package/dist/security/audit-logger.js.map +1 -0
- package/dist/security/cli-security.d.ts +143 -0
- package/dist/security/cli-security.js +302 -0
- package/dist/security/cli-security.js.map +1 -0
- package/dist/security/defaults.d.ts +31 -0
- package/dist/security/defaults.js +72 -0
- package/dist/security/defaults.js.map +1 -0
- package/dist/security/events.d.ts +8 -0
- package/dist/security/index.d.ts +27 -0
- package/dist/security/index.js +22 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/path-guard.d.ts +161 -0
- package/dist/security/path-guard.js +327 -0
- package/dist/security/path-guard.js.map +1 -0
- package/dist/security/rate-limiter.d.ts +117 -0
- package/dist/security/rate-limiter.js +165 -0
- package/dist/security/rate-limiter.js.map +1 -0
- package/dist/security/serialization-schemas.d.ts +183 -0
- package/dist/security/serialization-schemas.js +174 -0
- package/dist/security/serialization-schemas.js.map +1 -0
- package/dist/security/timeout-guard.d.ts +123 -0
- package/dist/security/timeout-guard.js +223 -0
- package/dist/security/timeout-guard.js.map +1 -0
- package/dist/security/types.d.ts +86 -0
- package/dist/security/types.js +80 -0
- package/dist/security/types.js.map +1 -0
- package/dist/token-budget.js +1 -1
- package/dist/tools.js +1 -1
- package/dist/util/storage.js.map +1 -1
- package/guide/index.md +2 -0
- package/guide/integration.md +1109 -0
- package/guide/security.md +237 -0
- package/package.json +23 -17
- package/vite.config.cli.ts +9 -18
- package/dist/riotprompt.cjs +0 -6169
- package/dist/riotprompt.cjs.map +0 -1
package/dist/{cli.cjs → cli.js}
RENAMED
|
@@ -1,46 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
26
|
-
enumerable: true,
|
|
27
|
-
get: () => e[k]
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
n.default = e;
|
|
33
|
-
return Object.freeze(n);
|
|
34
|
-
}
|
|
35
|
-
const fs__namespace$1 = /* @__PURE__ */ _interopNamespaceDefault(fs$1);
|
|
36
|
-
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
|
|
37
|
-
const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
|
|
38
|
-
const ConfigSchema = zod.z.object({
|
|
39
|
-
defaultModel: zod.z.string().default("gpt-4").describe("Default model to use for formatting"),
|
|
40
|
-
promptsDir: zod.z.string().default(".").describe("Directory containing prompts"),
|
|
41
|
-
outputDir: zod.z.string().optional().describe("Directory to output formatted prompts")
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { create as create$7 } from "@theunwalked/cardigantime";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import Logging from "@fjell/logging";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import path__default from "path";
|
|
9
|
+
import { SafeRegex } from "@theunwalked/pressurelid";
|
|
10
|
+
import "zod-to-json-schema";
|
|
11
|
+
import "tiktoken";
|
|
12
|
+
import { XMLParser } from "fast-xml-parser";
|
|
13
|
+
import OpenAI from "openai";
|
|
14
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
15
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
16
|
+
import { createSafeError as createSafeError$1 } from "@theunwalked/spotclean";
|
|
17
|
+
import { glob } from "glob";
|
|
18
|
+
import * as fs from "fs";
|
|
19
|
+
import crypto from "crypto";
|
|
20
|
+
import * as fs$1 from "fs/promises";
|
|
21
|
+
const ConfigSchema = z.object({
|
|
22
|
+
defaultModel: z.string().default("gpt-4").describe("Default model to use for formatting"),
|
|
23
|
+
promptsDir: z.string().default(".").describe("Directory containing prompts"),
|
|
24
|
+
outputDir: z.string().optional().describe("Directory to output formatted prompts")
|
|
42
25
|
});
|
|
43
|
-
const ParametersSchema =
|
|
26
|
+
const ParametersSchema = z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.array(z.union([z.string(), z.number(), z.boolean()]))]));
|
|
44
27
|
const apply = (text, parameters) => {
|
|
45
28
|
if (!parameters) {
|
|
46
29
|
return text;
|
|
@@ -67,12 +50,12 @@ const apply = (text, parameters) => {
|
|
|
67
50
|
}
|
|
68
51
|
});
|
|
69
52
|
};
|
|
70
|
-
|
|
71
|
-
text:
|
|
72
|
-
weight:
|
|
53
|
+
z.object({
|
|
54
|
+
text: z.string(),
|
|
55
|
+
weight: z.number().optional()
|
|
73
56
|
});
|
|
74
|
-
const WeightedOptionsSchema =
|
|
75
|
-
weight:
|
|
57
|
+
const WeightedOptionsSchema = z.object({
|
|
58
|
+
weight: z.number().optional(),
|
|
76
59
|
parameters: ParametersSchema.optional()
|
|
77
60
|
});
|
|
78
61
|
const create$6 = (text, options = {}) => {
|
|
@@ -87,10 +70,10 @@ const create$5 = (text, options = {}) => {
|
|
|
87
70
|
const weightedOptions = WeightedOptionsSchema.parse(options);
|
|
88
71
|
return create$6(text, weightedOptions);
|
|
89
72
|
};
|
|
90
|
-
const SectionOptionsSchema =
|
|
91
|
-
title:
|
|
92
|
-
weight:
|
|
93
|
-
itemWeight:
|
|
73
|
+
const SectionOptionsSchema = z.object({
|
|
74
|
+
title: z.string().optional(),
|
|
75
|
+
weight: z.number().optional(),
|
|
76
|
+
itemWeight: z.number().optional(),
|
|
94
77
|
parameters: ParametersSchema.optional().default({})
|
|
95
78
|
});
|
|
96
79
|
const create$4 = (options = {}) => {
|
|
@@ -245,19 +228,60 @@ const DEFAULT_FORMAT_OPTIONS = {
|
|
|
245
228
|
sectionTitleProperty: DEFAULT_SECTION_TITLE_PROPERTY,
|
|
246
229
|
sectionDepth: 0
|
|
247
230
|
};
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
231
|
+
const LibLogger = Logging.getLogger("@theunwalked/riotprompt");
|
|
232
|
+
function createSilentLogger(name) {
|
|
233
|
+
return {
|
|
234
|
+
name,
|
|
235
|
+
debug: () => {
|
|
236
|
+
},
|
|
237
|
+
info: () => {
|
|
238
|
+
},
|
|
239
|
+
warn: () => {
|
|
240
|
+
},
|
|
241
|
+
error: () => {
|
|
242
|
+
},
|
|
243
|
+
verbose: () => {
|
|
244
|
+
},
|
|
245
|
+
silly: () => {
|
|
246
|
+
},
|
|
247
|
+
get: (...components) => createSilentLogger(`${name}:${components.join(":")}`)
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const SILENT_LOGGER = createSilentLogger("silent");
|
|
251
|
+
const isLoggingEnabled = () => {
|
|
252
|
+
return process.env.RIOTPROMPT_LOGGING === "true" || process.env.DEBUG?.includes("riotprompt") || process.env.NODE_ENV === "development";
|
|
253
|
+
};
|
|
254
|
+
function createLoggerFromFjell(fjellLogger, name) {
|
|
255
|
+
return {
|
|
256
|
+
name,
|
|
257
|
+
debug: (message, ...args) => fjellLogger.debug(message, ...args),
|
|
258
|
+
info: (message, ...args) => fjellLogger.info(message, ...args),
|
|
259
|
+
warn: (message, ...args) => fjellLogger.warning(message, ...args),
|
|
260
|
+
error: (message, ...args) => fjellLogger.error(message, ...args),
|
|
261
|
+
verbose: (message, ...args) => fjellLogger.debug(message, ...args),
|
|
262
|
+
// Map to debug
|
|
263
|
+
silly: (message, ...args) => fjellLogger.debug(message, ...args),
|
|
264
|
+
// Map to debug
|
|
265
|
+
get: (...components) => {
|
|
266
|
+
const childLogger = fjellLogger.get(...components);
|
|
267
|
+
return createLoggerFromFjell(childLogger, `${name}:${components.join(":")}`);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const FJELL_LOGGER = {
|
|
272
|
+
name: "fjell",
|
|
273
|
+
debug: (message, ...args) => LibLogger.debug(message, ...args),
|
|
274
|
+
info: (message, ...args) => LibLogger.info(message, ...args),
|
|
275
|
+
warn: (message, ...args) => LibLogger.warning(message, ...args),
|
|
276
|
+
error: (message, ...args) => LibLogger.error(message, ...args),
|
|
277
|
+
verbose: (message, ...args) => LibLogger.debug(message, ...args),
|
|
278
|
+
silly: (message, ...args) => LibLogger.debug(message, ...args),
|
|
279
|
+
get: (...components) => {
|
|
280
|
+
const childLogger = LibLogger.get(...components);
|
|
281
|
+
return createLoggerFromFjell(childLogger, components.join(":"));
|
|
259
282
|
}
|
|
260
283
|
};
|
|
284
|
+
const DEFAULT_LOGGER = isLoggingEnabled() ? FJELL_LOGGER : SILENT_LOGGER;
|
|
261
285
|
const wrapLogger = (toWrap, name) => {
|
|
262
286
|
const requiredMethods = ["debug", "info", "warn", "error", "verbose", "silly"];
|
|
263
287
|
const missingMethods = requiredMethods.filter((method) => typeof toWrap[method] !== "function");
|
|
@@ -274,13 +298,14 @@ const wrapLogger = (toWrap, name) => {
|
|
|
274
298
|
else if (level === "silly") toWrap.silly(message, ...args);
|
|
275
299
|
};
|
|
276
300
|
return {
|
|
277
|
-
name: "wrapped",
|
|
301
|
+
name: name || "wrapped",
|
|
278
302
|
debug: (message, ...args) => log("debug", message, ...args),
|
|
279
303
|
info: (message, ...args) => log("info", message, ...args),
|
|
280
304
|
warn: (message, ...args) => log("warn", message, ...args),
|
|
281
305
|
error: (message, ...args) => log("error", message, ...args),
|
|
282
306
|
verbose: (message, ...args) => log("verbose", message, ...args),
|
|
283
|
-
silly: (message, ...args) => log("silly", message, ...args)
|
|
307
|
+
silly: (message, ...args) => log("silly", message, ...args),
|
|
308
|
+
get: (...components) => wrapLogger(toWrap, name ? `${name}:${components.join(":")}` : components.join(":"))
|
|
284
309
|
};
|
|
285
310
|
};
|
|
286
311
|
class ModelRegistry {
|
|
@@ -486,18 +511,18 @@ const stringifyJSON = function(obj, visited = /* @__PURE__ */ new Set()) {
|
|
|
486
511
|
}
|
|
487
512
|
return "";
|
|
488
513
|
};
|
|
489
|
-
const SectionSeparatorSchema =
|
|
490
|
-
const SectionTitlePropertySchema =
|
|
491
|
-
const FormatOptionsSchema =
|
|
514
|
+
const SectionSeparatorSchema = z.enum(["tag", "markdown"]);
|
|
515
|
+
const SectionTitlePropertySchema = z.enum(["title", "name"]);
|
|
516
|
+
const FormatOptionsSchema = z.object({
|
|
492
517
|
sectionSeparator: SectionSeparatorSchema,
|
|
493
|
-
sectionIndentation:
|
|
518
|
+
sectionIndentation: z.boolean(),
|
|
494
519
|
sectionTitleProperty: SectionTitlePropertySchema,
|
|
495
|
-
sectionTitlePrefix:
|
|
496
|
-
sectionTitleSeparator:
|
|
497
|
-
sectionDepth:
|
|
520
|
+
sectionTitlePrefix: z.string().optional(),
|
|
521
|
+
sectionTitleSeparator: z.string().optional(),
|
|
522
|
+
sectionDepth: z.number().default(0)
|
|
498
523
|
});
|
|
499
|
-
const OptionSchema =
|
|
500
|
-
logger:
|
|
524
|
+
const OptionSchema = z.object({
|
|
525
|
+
logger: z.any().optional().default(DEFAULT_LOGGER),
|
|
501
526
|
formatOptions: FormatOptionsSchema.partial().optional().default(DEFAULT_FORMAT_OPTIONS)
|
|
502
527
|
});
|
|
503
528
|
function isSection(obj) {
|
|
@@ -622,22 +647,22 @@ ${formattedItems}`;
|
|
|
622
647
|
formatArray
|
|
623
648
|
};
|
|
624
649
|
};
|
|
625
|
-
|
|
626
|
-
logger:
|
|
650
|
+
z.object({
|
|
651
|
+
logger: z.any().optional().default(DEFAULT_LOGGER),
|
|
627
652
|
parameters: ParametersSchema.optional().default({})
|
|
628
653
|
});
|
|
629
654
|
const create$1 = (params) => {
|
|
630
655
|
const log = params.log || console.log;
|
|
631
656
|
const exists = async (path2) => {
|
|
632
657
|
try {
|
|
633
|
-
await
|
|
658
|
+
await fs.promises.stat(path2);
|
|
634
659
|
return true;
|
|
635
660
|
} catch (error) {
|
|
636
661
|
return false;
|
|
637
662
|
}
|
|
638
663
|
};
|
|
639
664
|
const isDirectory2 = async (path2) => {
|
|
640
|
-
const stats = await
|
|
665
|
+
const stats = await fs.promises.stat(path2);
|
|
641
666
|
if (!stats.isDirectory()) {
|
|
642
667
|
log(`${path2} is not a directory`);
|
|
643
668
|
return false;
|
|
@@ -645,7 +670,7 @@ const create$1 = (params) => {
|
|
|
645
670
|
return true;
|
|
646
671
|
};
|
|
647
672
|
const isFile = async (path2) => {
|
|
648
|
-
const stats = await
|
|
673
|
+
const stats = await fs.promises.stat(path2);
|
|
649
674
|
if (!stats.isFile()) {
|
|
650
675
|
log(`${path2} is not a file`);
|
|
651
676
|
return false;
|
|
@@ -654,7 +679,7 @@ const create$1 = (params) => {
|
|
|
654
679
|
};
|
|
655
680
|
const isReadable = async (path2) => {
|
|
656
681
|
try {
|
|
657
|
-
await
|
|
682
|
+
await fs.promises.access(path2, fs.constants.R_OK);
|
|
658
683
|
} catch (error) {
|
|
659
684
|
log(`${path2} is not readable: %s %s`, error.message, error.stack);
|
|
660
685
|
return false;
|
|
@@ -663,7 +688,7 @@ const create$1 = (params) => {
|
|
|
663
688
|
};
|
|
664
689
|
const isWritable = async (path2) => {
|
|
665
690
|
try {
|
|
666
|
-
await
|
|
691
|
+
await fs.promises.access(path2, fs.constants.W_OK);
|
|
667
692
|
} catch (error) {
|
|
668
693
|
log(`${path2} is not writable: %s %s`, error.message, error.stack);
|
|
669
694
|
return false;
|
|
@@ -681,23 +706,23 @@ const create$1 = (params) => {
|
|
|
681
706
|
};
|
|
682
707
|
const createDirectory = async (path2) => {
|
|
683
708
|
try {
|
|
684
|
-
await
|
|
709
|
+
await fs.promises.mkdir(path2, { recursive: true });
|
|
685
710
|
} catch (mkdirError) {
|
|
686
711
|
throw new Error(`Failed to create output directory ${path2}: ${mkdirError.message} ${mkdirError.stack}`);
|
|
687
712
|
}
|
|
688
713
|
};
|
|
689
714
|
const readFile = async (path2, encoding) => {
|
|
690
|
-
return await
|
|
715
|
+
return await fs.promises.readFile(path2, { encoding });
|
|
691
716
|
};
|
|
692
717
|
const writeFile = async (path2, data, encoding) => {
|
|
693
|
-
await
|
|
718
|
+
await fs.promises.writeFile(path2, data, { encoding });
|
|
694
719
|
};
|
|
695
720
|
const forEachFileIn = async (directory, callback, options = { pattern: "*.*" }) => {
|
|
696
721
|
try {
|
|
697
722
|
let filesProcessed = 0;
|
|
698
|
-
const files = await glob
|
|
723
|
+
const files = await glob(options.pattern, { cwd: directory, nodir: true });
|
|
699
724
|
for (const file of files) {
|
|
700
|
-
await callback(
|
|
725
|
+
await callback(path__default.join(directory, file));
|
|
701
726
|
filesProcessed++;
|
|
702
727
|
if (options.limit && filesProcessed >= options.limit) {
|
|
703
728
|
log(`Reached limit of ${options.limit} files, stopping`);
|
|
@@ -709,14 +734,14 @@ const create$1 = (params) => {
|
|
|
709
734
|
}
|
|
710
735
|
};
|
|
711
736
|
const readStream = async (path2) => {
|
|
712
|
-
return
|
|
737
|
+
return fs.createReadStream(path2);
|
|
713
738
|
};
|
|
714
739
|
const hashFile = async (path2, length) => {
|
|
715
740
|
const file = await readFile(path2, "utf8");
|
|
716
741
|
return crypto.createHash("sha256").update(file).digest("hex").slice(0, length);
|
|
717
742
|
};
|
|
718
743
|
const listFiles = async (directory) => {
|
|
719
|
-
return await
|
|
744
|
+
return await fs.promises.readdir(directory);
|
|
720
745
|
};
|
|
721
746
|
return {
|
|
722
747
|
exists,
|
|
@@ -736,11 +761,268 @@ const create$1 = (params) => {
|
|
|
736
761
|
listFiles
|
|
737
762
|
};
|
|
738
763
|
};
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
764
|
+
const DEFAULT_CONFIG = {
|
|
765
|
+
enabled: true,
|
|
766
|
+
logLevel: "warning",
|
|
767
|
+
includeContext: true,
|
|
768
|
+
maxContextSize: 1e3
|
|
769
|
+
};
|
|
770
|
+
class SecurityAuditLogger {
|
|
771
|
+
config;
|
|
772
|
+
logger;
|
|
773
|
+
eventCount = /* @__PURE__ */ new Map();
|
|
774
|
+
constructor(config = {}, logger) {
|
|
775
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
776
|
+
this.logger = wrapLogger(logger || DEFAULT_LOGGER, "SecurityAudit");
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Log a security event
|
|
780
|
+
*/
|
|
781
|
+
log(event) {
|
|
782
|
+
if (!this.config.enabled) return;
|
|
783
|
+
const fullEvent = {
|
|
784
|
+
...event,
|
|
785
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
786
|
+
context: this.sanitizeContext(event.context)
|
|
787
|
+
};
|
|
788
|
+
const count = this.eventCount.get(event.type) || 0;
|
|
789
|
+
this.eventCount.set(event.type, count + 1);
|
|
790
|
+
this.config.onEvent?.(fullEvent);
|
|
791
|
+
if (!this.shouldLog(event.severity)) return;
|
|
792
|
+
const logMessage = this.formatEvent(fullEvent);
|
|
793
|
+
switch (event.severity) {
|
|
794
|
+
case "critical":
|
|
795
|
+
this.logger.error(`[CRITICAL] ${logMessage}`);
|
|
796
|
+
break;
|
|
797
|
+
case "error":
|
|
798
|
+
this.logger.error(logMessage);
|
|
799
|
+
break;
|
|
800
|
+
case "warning":
|
|
801
|
+
this.logger.warn(logMessage);
|
|
802
|
+
break;
|
|
803
|
+
case "info":
|
|
804
|
+
this.logger.info(logMessage);
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Convenience methods for common events
|
|
810
|
+
*/
|
|
811
|
+
pathTraversalBlocked(path2, reason) {
|
|
812
|
+
this.log({
|
|
813
|
+
type: "path_traversal_blocked",
|
|
814
|
+
severity: "warning",
|
|
815
|
+
message: `Path traversal attempt blocked: ${reason}`,
|
|
816
|
+
context: { attemptedPath: this.sanitizePath(path2) }
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
pathValidationFailed(path2, reason) {
|
|
820
|
+
this.log({
|
|
821
|
+
type: "path_validation_failed",
|
|
822
|
+
severity: "warning",
|
|
823
|
+
message: `Path validation failed: ${reason}`,
|
|
824
|
+
context: { attemptedPath: this.sanitizePath(path2) }
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
toolValidationFailed(toolName, reason) {
|
|
828
|
+
this.log({
|
|
829
|
+
type: "tool_validation_failed",
|
|
830
|
+
severity: "warning",
|
|
831
|
+
message: `Tool parameter validation failed for "${toolName}": ${reason}`,
|
|
832
|
+
context: { toolName }
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
toolExecutionBlocked(toolName, reason) {
|
|
836
|
+
this.log({
|
|
837
|
+
type: "tool_execution_blocked",
|
|
838
|
+
severity: "error",
|
|
839
|
+
message: `Tool execution blocked for "${toolName}": ${reason}`,
|
|
840
|
+
context: { toolName }
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
toolTimeout(toolName, timeoutMs) {
|
|
844
|
+
this.log({
|
|
845
|
+
type: "tool_timeout",
|
|
846
|
+
severity: "warning",
|
|
847
|
+
message: `Tool "${toolName}" timed out after ${timeoutMs}ms`,
|
|
848
|
+
context: { toolName, timeoutMs }
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
secretRedacted(source) {
|
|
852
|
+
this.log({
|
|
853
|
+
type: "secret_redacted",
|
|
854
|
+
severity: "info",
|
|
855
|
+
message: `Sensitive data redacted from ${source}`,
|
|
856
|
+
context: { source }
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
apiKeyUsed(provider) {
|
|
860
|
+
this.log({
|
|
861
|
+
type: "api_key_used",
|
|
862
|
+
severity: "info",
|
|
863
|
+
message: `API key accessed for provider: ${provider}`,
|
|
864
|
+
context: { provider }
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
deserializationFailed(source, reason) {
|
|
868
|
+
this.log({
|
|
869
|
+
type: "deserialization_failed",
|
|
870
|
+
severity: "warning",
|
|
871
|
+
message: `Deserialization failed from ${source}: ${reason}`,
|
|
872
|
+
context: { source }
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
regexTimeout(pattern, timeoutMs) {
|
|
876
|
+
this.log({
|
|
877
|
+
type: "regex_timeout",
|
|
878
|
+
severity: "warning",
|
|
879
|
+
message: `Regex operation timed out after ${timeoutMs}ms`,
|
|
880
|
+
context: { patternLength: pattern.length, timeoutMs }
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
requestTimeout(operation, timeoutMs) {
|
|
884
|
+
this.log({
|
|
885
|
+
type: "request_timeout",
|
|
886
|
+
severity: "warning",
|
|
887
|
+
message: `Operation "${operation}" timed out after ${timeoutMs}ms`,
|
|
888
|
+
context: { operation, timeoutMs }
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
rateLimitExceeded(resource, limit) {
|
|
892
|
+
this.log({
|
|
893
|
+
type: "rate_limit_exceeded",
|
|
894
|
+
severity: "warning",
|
|
895
|
+
message: `Rate limit exceeded for ${resource}: limit is ${limit}`,
|
|
896
|
+
context: { resource, limit }
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Get event statistics
|
|
901
|
+
*/
|
|
902
|
+
getStats() {
|
|
903
|
+
return new Map(this.eventCount);
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Get total event count
|
|
907
|
+
*/
|
|
908
|
+
getTotalEventCount() {
|
|
909
|
+
let total = 0;
|
|
910
|
+
for (const count of this.eventCount.values()) {
|
|
911
|
+
total += count;
|
|
912
|
+
}
|
|
913
|
+
return total;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Reset statistics
|
|
917
|
+
*/
|
|
918
|
+
resetStats() {
|
|
919
|
+
this.eventCount.clear();
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Check if any events of a specific type have been logged
|
|
923
|
+
*/
|
|
924
|
+
hasEventsOfType(type) {
|
|
925
|
+
return (this.eventCount.get(type) || 0) > 0;
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Get count for a specific event type
|
|
929
|
+
*/
|
|
930
|
+
getEventCount(type) {
|
|
931
|
+
return this.eventCount.get(type) || 0;
|
|
932
|
+
}
|
|
933
|
+
shouldLog(severity) {
|
|
934
|
+
const levels = ["info", "warning", "error", "critical"];
|
|
935
|
+
const configLevel = levels.indexOf(this.config.logLevel === "all" ? "info" : this.config.logLevel);
|
|
936
|
+
const eventLevel = levels.indexOf(severity);
|
|
937
|
+
return eventLevel >= configLevel;
|
|
938
|
+
}
|
|
939
|
+
formatEvent(event) {
|
|
940
|
+
let message = `[${event.type}] ${event.message}`;
|
|
941
|
+
if (this.config.includeContext && event.context) {
|
|
942
|
+
message += ` | Context: ${JSON.stringify(event.context)}`;
|
|
943
|
+
}
|
|
944
|
+
return message;
|
|
945
|
+
}
|
|
946
|
+
sanitizeContext(context) {
|
|
947
|
+
if (!context || !this.config.includeContext) return void 0;
|
|
948
|
+
const sanitized = {};
|
|
949
|
+
let size = 0;
|
|
950
|
+
for (const [key, value] of Object.entries(context)) {
|
|
951
|
+
const stringValue = String(value);
|
|
952
|
+
if (size + stringValue.length > this.config.maxContextSize) break;
|
|
953
|
+
if (this.looksLikeSensitiveKey(key)) {
|
|
954
|
+
sanitized[key] = "[REDACTED]";
|
|
955
|
+
} else {
|
|
956
|
+
sanitized[key] = value;
|
|
957
|
+
}
|
|
958
|
+
size += stringValue.length;
|
|
959
|
+
}
|
|
960
|
+
return sanitized;
|
|
961
|
+
}
|
|
962
|
+
sanitizePath(path2) {
|
|
963
|
+
const parts = path2.split(/[/\\]/);
|
|
964
|
+
const lastPart = parts[parts.length - 1];
|
|
965
|
+
return `.../${lastPart} (${path2.length} chars)`;
|
|
966
|
+
}
|
|
967
|
+
looksLikeSensitiveKey(key) {
|
|
968
|
+
const sensitivePatterns = [
|
|
969
|
+
/key/i,
|
|
970
|
+
/secret/i,
|
|
971
|
+
/password/i,
|
|
972
|
+
/token/i,
|
|
973
|
+
/auth/i,
|
|
974
|
+
/credential/i
|
|
975
|
+
];
|
|
976
|
+
return sensitivePatterns.some((p) => p.test(key));
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
let globalAuditLogger = null;
|
|
980
|
+
function getAuditLogger() {
|
|
981
|
+
if (!globalAuditLogger) {
|
|
982
|
+
globalAuditLogger = new SecurityAuditLogger();
|
|
983
|
+
}
|
|
984
|
+
return globalAuditLogger;
|
|
985
|
+
}
|
|
986
|
+
function createSafeError(error, context) {
|
|
987
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
988
|
+
return createSafeError$1(err, context);
|
|
989
|
+
}
|
|
990
|
+
const OptionsSchema = z.object({
|
|
991
|
+
logger: z.any().optional().default(DEFAULT_LOGGER),
|
|
992
|
+
ignorePatterns: z.array(z.string()).optional().default(DEFAULT_IGNORE_PATTERNS),
|
|
742
993
|
parameters: ParametersSchema.optional().default({})
|
|
743
994
|
});
|
|
995
|
+
function createSafeIgnorePatterns(patterns, logger) {
|
|
996
|
+
const auditLogger = getAuditLogger();
|
|
997
|
+
const safeRegex = new SafeRegex({
|
|
998
|
+
maxLength: 500,
|
|
999
|
+
timeoutMs: 1e3,
|
|
1000
|
+
onBlock: (message, pattern) => {
|
|
1001
|
+
logger.warn(`Blocked unsafe ignore pattern: ${message}`, { patternLength: pattern.length });
|
|
1002
|
+
auditLogger.log({
|
|
1003
|
+
type: "regex_blocked",
|
|
1004
|
+
severity: "warning",
|
|
1005
|
+
message: `Blocked unsafe regex pattern: ${message}`,
|
|
1006
|
+
context: { patternLength: pattern.length }
|
|
1007
|
+
});
|
|
1008
|
+
},
|
|
1009
|
+
onWarning: (message, _pattern) => {
|
|
1010
|
+
logger.debug(`Regex warning: ${message}`);
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
return patterns.map((pattern) => {
|
|
1014
|
+
const result = safeRegex.create(pattern, "i");
|
|
1015
|
+
if (result.safe && result.regex) {
|
|
1016
|
+
return result.regex;
|
|
1017
|
+
}
|
|
1018
|
+
const globResult = safeRegex.globToRegex(pattern);
|
|
1019
|
+
if (globResult.safe && globResult.regex) {
|
|
1020
|
+
return globResult.regex;
|
|
1021
|
+
}
|
|
1022
|
+
logger.warn(`Invalid or unsafe ignore pattern "${pattern}": ${result.error || globResult.error}`);
|
|
1023
|
+
return null;
|
|
1024
|
+
}).filter((regex) => regex !== null);
|
|
1025
|
+
}
|
|
744
1026
|
function extractFirstHeader(markdownText) {
|
|
745
1027
|
const headerRegex = /^(#{1,6})\s+(.+?)(?:\n|$)/m;
|
|
746
1028
|
const match = markdownText.match(headerRegex);
|
|
@@ -783,10 +1065,10 @@ const create = (loaderOptions) => {
|
|
|
783
1065
|
const storage = create$1({ log: logger.debug });
|
|
784
1066
|
for (const contextDir of contextDirectories) {
|
|
785
1067
|
try {
|
|
786
|
-
const dirName =
|
|
1068
|
+
const dirName = path__default.basename(contextDir);
|
|
787
1069
|
logger.debug(`Processing context directory ${dirName}`);
|
|
788
1070
|
let mainContextSection;
|
|
789
|
-
const contextFile =
|
|
1071
|
+
const contextFile = path__default.join(contextDir, "context.md");
|
|
790
1072
|
if (await storage.exists(contextFile)) {
|
|
791
1073
|
logger.debug(`Found context.md file in ${contextDir}`);
|
|
792
1074
|
const mainContextContent = await storage.readFile(contextFile, "utf8");
|
|
@@ -802,22 +1084,15 @@ const create = (loaderOptions) => {
|
|
|
802
1084
|
mainContextSection = create$4({ ...sectionOptions, title: dirName });
|
|
803
1085
|
}
|
|
804
1086
|
const files = await storage.listFiles(contextDir);
|
|
805
|
-
const ignorePatternsRegex = ignorePatterns
|
|
806
|
-
try {
|
|
807
|
-
return new RegExp(pattern, "i");
|
|
808
|
-
} catch (error) {
|
|
809
|
-
logger.error(`Invalid ignore pattern: ${pattern}`, { error });
|
|
810
|
-
return /(?!)/;
|
|
811
|
-
}
|
|
812
|
-
});
|
|
1087
|
+
const ignorePatternsRegex = createSafeIgnorePatterns(ignorePatterns, logger);
|
|
813
1088
|
const filteredFiles = files.filter((file) => {
|
|
814
|
-
const fullPath =
|
|
1089
|
+
const fullPath = path__default.join(contextDir, file);
|
|
815
1090
|
return !ignorePatternsRegex.some((regex) => regex.test(file) || regex.test(fullPath));
|
|
816
1091
|
});
|
|
817
1092
|
for (const file of filteredFiles) {
|
|
818
1093
|
if (file === "context.md") continue;
|
|
819
1094
|
logger.debug(`Processing file ${file} in ${contextDir}`);
|
|
820
|
-
const filePath =
|
|
1095
|
+
const filePath = path__default.join(contextDir, file);
|
|
821
1096
|
if (await storage.isFile(filePath)) {
|
|
822
1097
|
const fileContent = await storage.readFile(filePath, "utf8");
|
|
823
1098
|
let sectionName = file;
|
|
@@ -836,7 +1111,8 @@ const create = (loaderOptions) => {
|
|
|
836
1111
|
}
|
|
837
1112
|
contextSections.push(mainContextSection);
|
|
838
1113
|
} catch (error) {
|
|
839
|
-
|
|
1114
|
+
const safeError = createSafeError(error, { operation: "load", directory: path__default.basename(contextDir) });
|
|
1115
|
+
logger.error(`Error processing context directory: ${safeError.message}`);
|
|
840
1116
|
}
|
|
841
1117
|
}
|
|
842
1118
|
return contextSections;
|
|
@@ -845,107 +1121,108 @@ const create = (loaderOptions) => {
|
|
|
845
1121
|
load
|
|
846
1122
|
};
|
|
847
1123
|
};
|
|
848
|
-
|
|
849
|
-
logger:
|
|
850
|
-
configDirs:
|
|
851
|
-
overrides:
|
|
1124
|
+
z.object({
|
|
1125
|
+
logger: z.any().optional().default(DEFAULT_LOGGER),
|
|
1126
|
+
configDirs: z.array(z.string()).default(["./overrides"]),
|
|
1127
|
+
overrides: z.boolean().default(false),
|
|
852
1128
|
parameters: ParametersSchema.optional().default({})
|
|
853
1129
|
});
|
|
854
|
-
|
|
855
|
-
logger:
|
|
856
|
-
basePath:
|
|
857
|
-
overridePaths:
|
|
858
|
-
overrides:
|
|
1130
|
+
z.object({
|
|
1131
|
+
logger: z.any().optional().default(DEFAULT_LOGGER),
|
|
1132
|
+
basePath: z.string(),
|
|
1133
|
+
overridePaths: z.array(z.string()).optional().default(["./"]),
|
|
1134
|
+
overrides: z.boolean().optional().default(false),
|
|
859
1135
|
parameters: ParametersSchema.optional().default({})
|
|
860
1136
|
});
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1137
|
+
Logging.getLogger("@theunwalked/riotprompt");
|
|
1138
|
+
z.object({
|
|
1139
|
+
model: z.string(),
|
|
1140
|
+
formatter: z.any().optional(),
|
|
1141
|
+
trackContext: z.boolean().optional().default(true),
|
|
1142
|
+
deduplicateContext: z.boolean().optional().default(true)
|
|
866
1143
|
});
|
|
867
|
-
|
|
868
|
-
name:
|
|
869
|
-
description:
|
|
870
|
-
parameters:
|
|
871
|
-
type:
|
|
872
|
-
properties:
|
|
1144
|
+
z.object({
|
|
1145
|
+
name: z.string().min(1),
|
|
1146
|
+
description: z.string().min(1),
|
|
1147
|
+
parameters: z.object({
|
|
1148
|
+
type: z.literal("object"),
|
|
1149
|
+
properties: z.record(z.string(), z.any()).default({}),
|
|
873
1150
|
// Allow any parameter structure
|
|
874
|
-
required:
|
|
1151
|
+
required: z.array(z.string()).optional()
|
|
875
1152
|
}).passthrough(),
|
|
876
1153
|
// Allow additional fields
|
|
877
|
-
execute:
|
|
1154
|
+
execute: z.custom(
|
|
878
1155
|
(val) => typeof val === "function",
|
|
879
1156
|
{ message: "execute must be a function" }
|
|
880
1157
|
),
|
|
881
|
-
category:
|
|
882
|
-
cost:
|
|
883
|
-
examples:
|
|
884
|
-
scenario:
|
|
885
|
-
params:
|
|
886
|
-
expectedResult:
|
|
1158
|
+
category: z.string().optional(),
|
|
1159
|
+
cost: z.enum(["cheap", "moderate", "expensive"]).optional(),
|
|
1160
|
+
examples: z.array(z.object({
|
|
1161
|
+
scenario: z.string(),
|
|
1162
|
+
params: z.any(),
|
|
1163
|
+
expectedResult: z.string()
|
|
887
1164
|
})).optional()
|
|
888
1165
|
}).passthrough();
|
|
889
|
-
const ContentItemSchema =
|
|
890
|
-
|
|
1166
|
+
const ContentItemSchema = z.union([
|
|
1167
|
+
z.string(),
|
|
891
1168
|
// Simple string content
|
|
892
|
-
|
|
893
|
-
content:
|
|
894
|
-
title:
|
|
895
|
-
weight:
|
|
1169
|
+
z.object({
|
|
1170
|
+
content: z.string(),
|
|
1171
|
+
title: z.string().optional(),
|
|
1172
|
+
weight: z.number().optional()
|
|
896
1173
|
}),
|
|
897
|
-
|
|
898
|
-
path:
|
|
899
|
-
title:
|
|
900
|
-
weight:
|
|
1174
|
+
z.object({
|
|
1175
|
+
path: z.string(),
|
|
1176
|
+
title: z.string().optional(),
|
|
1177
|
+
weight: z.number().optional()
|
|
901
1178
|
}),
|
|
902
|
-
|
|
903
|
-
directories:
|
|
904
|
-
title:
|
|
905
|
-
weight:
|
|
1179
|
+
z.object({
|
|
1180
|
+
directories: z.array(z.string()),
|
|
1181
|
+
title: z.string().optional(),
|
|
1182
|
+
weight: z.number().optional()
|
|
906
1183
|
})
|
|
907
1184
|
]);
|
|
908
|
-
|
|
1185
|
+
z.object({
|
|
909
1186
|
// Core settings
|
|
910
|
-
basePath:
|
|
911
|
-
logger:
|
|
912
|
-
overridePaths:
|
|
913
|
-
overrides:
|
|
1187
|
+
basePath: z.string(),
|
|
1188
|
+
logger: z.any().optional().default(DEFAULT_LOGGER),
|
|
1189
|
+
overridePaths: z.array(z.string()).optional().default(["./"]),
|
|
1190
|
+
overrides: z.boolean().optional().default(false),
|
|
914
1191
|
parameters: ParametersSchema.optional().default({}),
|
|
915
1192
|
// Content sections
|
|
916
1193
|
persona: ContentItemSchema.optional(),
|
|
917
|
-
instructions:
|
|
918
|
-
content:
|
|
919
|
-
context:
|
|
1194
|
+
instructions: z.array(ContentItemSchema).optional().default([]),
|
|
1195
|
+
content: z.array(ContentItemSchema).optional().default([]),
|
|
1196
|
+
context: z.array(ContentItemSchema).optional().default([]),
|
|
920
1197
|
// Advanced prompting sections
|
|
921
|
-
constraints:
|
|
922
|
-
tone:
|
|
923
|
-
examples:
|
|
924
|
-
reasoning:
|
|
925
|
-
responseFormat:
|
|
926
|
-
recap:
|
|
927
|
-
safeguards:
|
|
928
|
-
schema:
|
|
1198
|
+
constraints: z.array(ContentItemSchema).optional().default([]),
|
|
1199
|
+
tone: z.array(ContentItemSchema).optional().default([]),
|
|
1200
|
+
examples: z.array(ContentItemSchema).optional().default([]),
|
|
1201
|
+
reasoning: z.array(ContentItemSchema).optional().default([]),
|
|
1202
|
+
responseFormat: z.array(ContentItemSchema).optional().default([]),
|
|
1203
|
+
recap: z.array(ContentItemSchema).optional().default([]),
|
|
1204
|
+
safeguards: z.array(ContentItemSchema).optional().default([]),
|
|
1205
|
+
schema: z.any().optional(),
|
|
929
1206
|
// Can be string path, JSON object, or Zod schema
|
|
930
1207
|
// Templates and inheritance
|
|
931
|
-
extends:
|
|
1208
|
+
extends: z.string().optional(),
|
|
932
1209
|
// Extend another recipe
|
|
933
|
-
template:
|
|
1210
|
+
template: z.string().optional(),
|
|
934
1211
|
// Generic template name
|
|
935
1212
|
// Tool integration
|
|
936
|
-
tools:
|
|
1213
|
+
tools: z.any().optional(),
|
|
937
1214
|
// Tool[] | ToolRegistry
|
|
938
|
-
toolGuidance:
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
strategy:
|
|
942
|
-
includeExamples:
|
|
943
|
-
explainWhenToUse:
|
|
944
|
-
includeCategories:
|
|
945
|
-
customInstructions:
|
|
1215
|
+
toolGuidance: z.union([
|
|
1216
|
+
z.enum(["auto", "minimal", "detailed"]),
|
|
1217
|
+
z.object({
|
|
1218
|
+
strategy: z.enum(["adaptive", "prescriptive", "minimal"]),
|
|
1219
|
+
includeExamples: z.boolean().optional(),
|
|
1220
|
+
explainWhenToUse: z.boolean().optional(),
|
|
1221
|
+
includeCategories: z.boolean().optional(),
|
|
1222
|
+
customInstructions: z.string().optional()
|
|
946
1223
|
})
|
|
947
1224
|
]).optional(),
|
|
948
|
-
toolCategories:
|
|
1225
|
+
toolCategories: z.array(z.string()).optional()
|
|
949
1226
|
});
|
|
950
1227
|
const toJSON = (prompt) => {
|
|
951
1228
|
return JSON.stringify(prompt, null, 2);
|
|
@@ -1087,7 +1364,7 @@ const parseNodeToSection = (node) => {
|
|
|
1087
1364
|
return section;
|
|
1088
1365
|
};
|
|
1089
1366
|
const fromXML = (xmlString) => {
|
|
1090
|
-
const parser = new
|
|
1367
|
+
const parser = new XMLParser({
|
|
1091
1368
|
ignoreAttributes: false,
|
|
1092
1369
|
attributeNamePrefix: "@_",
|
|
1093
1370
|
preserveOrder: true,
|
|
@@ -1129,18 +1406,18 @@ const fromXML = (xmlString) => {
|
|
|
1129
1406
|
});
|
|
1130
1407
|
};
|
|
1131
1408
|
const saveToDirectory = async (prompt, basePath) => {
|
|
1132
|
-
await
|
|
1409
|
+
await fs$1.mkdir(basePath, { recursive: true });
|
|
1133
1410
|
if (prompt.persona) {
|
|
1134
|
-
await saveSection(prompt.persona,
|
|
1411
|
+
await saveSection(prompt.persona, path.join(basePath, "persona"));
|
|
1135
1412
|
}
|
|
1136
1413
|
if (prompt.instructions) {
|
|
1137
|
-
await saveSection(prompt.instructions,
|
|
1414
|
+
await saveSection(prompt.instructions, path.join(basePath, "instructions"));
|
|
1138
1415
|
}
|
|
1139
1416
|
if (prompt.contexts) {
|
|
1140
|
-
await saveSection(prompt.contexts,
|
|
1417
|
+
await saveSection(prompt.contexts, path.join(basePath, "context"));
|
|
1141
1418
|
}
|
|
1142
1419
|
if (prompt.contents) {
|
|
1143
|
-
await saveSection(prompt.contents,
|
|
1420
|
+
await saveSection(prompt.contents, path.join(basePath, "content"));
|
|
1144
1421
|
}
|
|
1145
1422
|
};
|
|
1146
1423
|
const saveSection = async (section, targetPath) => {
|
|
@@ -1152,20 +1429,20 @@ const saveSection = async (section, targetPath) => {
|
|
|
1152
1429
|
if (typeof item === "string") return item;
|
|
1153
1430
|
return item.text;
|
|
1154
1431
|
}).join("\n\n");
|
|
1155
|
-
await
|
|
1432
|
+
await fs$1.writeFile(`${targetPath}.md`, content);
|
|
1156
1433
|
return;
|
|
1157
1434
|
}
|
|
1158
|
-
await
|
|
1435
|
+
await fs$1.mkdir(targetPath, { recursive: true });
|
|
1159
1436
|
for (let i = 0; i < section.items.length; i++) {
|
|
1160
1437
|
const item = section.items[i];
|
|
1161
1438
|
if (typeof item === "object" && "items" in item) {
|
|
1162
1439
|
const subTitle = item.title || `part-${i + 1}`;
|
|
1163
|
-
const subPath =
|
|
1440
|
+
const subPath = path.join(targetPath, subTitle);
|
|
1164
1441
|
await saveSection(item, subPath);
|
|
1165
1442
|
} else {
|
|
1166
1443
|
const fileName = `item-${i + 1}.md`;
|
|
1167
1444
|
const content = typeof item === "string" ? item : item.text;
|
|
1168
|
-
await
|
|
1445
|
+
await fs$1.writeFile(path.join(targetPath, fileName), content);
|
|
1169
1446
|
}
|
|
1170
1447
|
}
|
|
1171
1448
|
};
|
|
@@ -1258,7 +1535,7 @@ class GeminiProvider {
|
|
|
1258
1535
|
async execute(request, options = {}) {
|
|
1259
1536
|
const apiKey = options.apiKey || process.env.GEMINI_API_KEY;
|
|
1260
1537
|
if (!apiKey) throw new Error("Gemini API key is required");
|
|
1261
|
-
const genAI = new
|
|
1538
|
+
const genAI = new GoogleGenerativeAI(apiKey);
|
|
1262
1539
|
const modelName = options.model || request.model || "gemini-1.5-pro";
|
|
1263
1540
|
const generationConfig = {};
|
|
1264
1541
|
if (request.responseFormat?.type === "json_schema") {
|
|
@@ -1364,8 +1641,170 @@ const execute = async (request, options = {}) => {
|
|
|
1364
1641
|
const manager = new ExecutionManager();
|
|
1365
1642
|
return manager.execute(request, options);
|
|
1366
1643
|
};
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1644
|
+
const PathSecurityConfigSchema = z.object({
|
|
1645
|
+
enabled: z.boolean().optional().default(true),
|
|
1646
|
+
basePaths: z.array(z.string()).optional().default([]),
|
|
1647
|
+
allowAbsolute: z.boolean().optional().default(false),
|
|
1648
|
+
allowSymlinks: z.boolean().optional().default(false),
|
|
1649
|
+
denyPatterns: z.array(z.string()).optional().default([
|
|
1650
|
+
"\\.\\.",
|
|
1651
|
+
// Parent directory
|
|
1652
|
+
"~",
|
|
1653
|
+
// Home directory expansion
|
|
1654
|
+
"\\$\\{"
|
|
1655
|
+
// Variable expansion
|
|
1656
|
+
])
|
|
1657
|
+
});
|
|
1658
|
+
const ToolSecurityConfigSchema = z.object({
|
|
1659
|
+
enabled: z.boolean().optional().default(true),
|
|
1660
|
+
validateParams: z.boolean().optional().default(true),
|
|
1661
|
+
sandboxExecution: z.boolean().optional().default(false),
|
|
1662
|
+
allowedTools: z.array(z.string()).optional(),
|
|
1663
|
+
deniedTools: z.array(z.string()).optional().default([]),
|
|
1664
|
+
maxExecutionTime: z.number().optional().default(3e4),
|
|
1665
|
+
// 30 seconds
|
|
1666
|
+
maxConcurrentCalls: z.number().optional().default(10)
|
|
1667
|
+
});
|
|
1668
|
+
const SecretSecurityConfigSchema = z.object({
|
|
1669
|
+
enabled: z.boolean().optional().default(true),
|
|
1670
|
+
redactInLogs: z.boolean().optional().default(true),
|
|
1671
|
+
redactInErrors: z.boolean().optional().default(true),
|
|
1672
|
+
patterns: z.array(z.instanceof(RegExp)).optional().default([
|
|
1673
|
+
/api[_-]?key[\s:="']+[\w-]+/gi,
|
|
1674
|
+
/password[\s:="']+[\w-]+/gi,
|
|
1675
|
+
/Bearer\s+[\w-]+/gi,
|
|
1676
|
+
/sk-[a-zA-Z0-9]{48,}/g,
|
|
1677
|
+
/AKIA[0-9A-Z]{16}/g
|
|
1678
|
+
// AWS Access Key
|
|
1679
|
+
]),
|
|
1680
|
+
customPatterns: z.array(z.instanceof(RegExp)).optional().default([])
|
|
1681
|
+
});
|
|
1682
|
+
const LogSecurityConfigSchema = z.object({
|
|
1683
|
+
enabled: z.boolean().optional().default(true),
|
|
1684
|
+
auditSecurityEvents: z.boolean().optional().default(true),
|
|
1685
|
+
sanitizeStackTraces: z.boolean().optional().default(true),
|
|
1686
|
+
maxContentLength: z.number().optional().default(1e4)
|
|
1687
|
+
});
|
|
1688
|
+
const TimeoutConfigSchema = z.object({
|
|
1689
|
+
enabled: z.boolean().optional().default(true),
|
|
1690
|
+
defaultTimeout: z.number().optional().default(3e4),
|
|
1691
|
+
llmTimeout: z.number().optional().default(12e4),
|
|
1692
|
+
// 2 minutes for LLM calls
|
|
1693
|
+
toolTimeout: z.number().optional().default(3e4),
|
|
1694
|
+
fileTimeout: z.number().optional().default(5e3)
|
|
1695
|
+
});
|
|
1696
|
+
function createDefaultPathSecurityConfig() {
|
|
1697
|
+
return PathSecurityConfigSchema.parse({});
|
|
1698
|
+
}
|
|
1699
|
+
function createDefaultToolSecurityConfig() {
|
|
1700
|
+
return ToolSecurityConfigSchema.parse({});
|
|
1701
|
+
}
|
|
1702
|
+
function createDefaultSecretSecurityConfig() {
|
|
1703
|
+
return SecretSecurityConfigSchema.parse({});
|
|
1704
|
+
}
|
|
1705
|
+
function createDefaultLogSecurityConfig() {
|
|
1706
|
+
return LogSecurityConfigSchema.parse({});
|
|
1707
|
+
}
|
|
1708
|
+
function createDefaultTimeoutConfig() {
|
|
1709
|
+
return TimeoutConfigSchema.parse({});
|
|
1710
|
+
}
|
|
1711
|
+
const SecurityConfigSchema = z.object({
|
|
1712
|
+
paths: PathSecurityConfigSchema.optional().default(createDefaultPathSecurityConfig),
|
|
1713
|
+
tools: ToolSecurityConfigSchema.optional().default(createDefaultToolSecurityConfig),
|
|
1714
|
+
secrets: SecretSecurityConfigSchema.optional().default(createDefaultSecretSecurityConfig),
|
|
1715
|
+
logging: LogSecurityConfigSchema.optional().default(createDefaultLogSecurityConfig),
|
|
1716
|
+
timeouts: TimeoutConfigSchema.optional().default(createDefaultTimeoutConfig)
|
|
1717
|
+
});
|
|
1718
|
+
SecurityConfigSchema.parse({});
|
|
1719
|
+
const SERIALIZATION_LIMITS = {
|
|
1720
|
+
maxContentLength: 1e6,
|
|
1721
|
+
// 1MB per message
|
|
1722
|
+
maxArgumentsLength: 1e5,
|
|
1723
|
+
// 100KB for tool arguments
|
|
1724
|
+
maxMessages: 1e4,
|
|
1725
|
+
// 10k messages per conversation
|
|
1726
|
+
maxContextItems: 1e3,
|
|
1727
|
+
// 1k context items
|
|
1728
|
+
maxStringLength: 100,
|
|
1729
|
+
// 100 chars for names/ids
|
|
1730
|
+
maxToolCalls: 100
|
|
1731
|
+
// 100 tool calls per message
|
|
1732
|
+
};
|
|
1733
|
+
const ToolCallSchema = z.object({
|
|
1734
|
+
id: z.string().max(SERIALIZATION_LIMITS.maxStringLength),
|
|
1735
|
+
type: z.literal("function"),
|
|
1736
|
+
function: z.object({
|
|
1737
|
+
name: z.string().max(SERIALIZATION_LIMITS.maxStringLength),
|
|
1738
|
+
arguments: z.string().max(SERIALIZATION_LIMITS.maxArgumentsLength)
|
|
1739
|
+
})
|
|
1740
|
+
});
|
|
1741
|
+
const ConversationMessageSchema = z.object({
|
|
1742
|
+
role: z.enum(["system", "user", "assistant", "tool"]),
|
|
1743
|
+
content: z.string().nullable().refine(
|
|
1744
|
+
(val) => val === null || val.length <= SERIALIZATION_LIMITS.maxContentLength,
|
|
1745
|
+
{ message: `Content exceeds maximum length of ${SERIALIZATION_LIMITS.maxContentLength}` }
|
|
1746
|
+
),
|
|
1747
|
+
name: z.string().max(SERIALIZATION_LIMITS.maxStringLength).optional(),
|
|
1748
|
+
tool_calls: z.array(ToolCallSchema).max(SERIALIZATION_LIMITS.maxToolCalls).optional(),
|
|
1749
|
+
tool_call_id: z.string().max(SERIALIZATION_LIMITS.maxStringLength).optional()
|
|
1750
|
+
});
|
|
1751
|
+
const ConversationMetadataSchema = z.object({
|
|
1752
|
+
model: z.string().max(SERIALIZATION_LIMITS.maxStringLength),
|
|
1753
|
+
created: z.string().datetime(),
|
|
1754
|
+
lastModified: z.string().datetime(),
|
|
1755
|
+
messageCount: z.number().int().nonnegative(),
|
|
1756
|
+
toolCallCount: z.number().int().nonnegative()
|
|
1757
|
+
});
|
|
1758
|
+
z.object({
|
|
1759
|
+
// Optional version for forward compatibility
|
|
1760
|
+
version: z.string().optional(),
|
|
1761
|
+
messages: z.array(ConversationMessageSchema).max(SERIALIZATION_LIMITS.maxMessages),
|
|
1762
|
+
metadata: ConversationMetadataSchema,
|
|
1763
|
+
contextProvided: z.array(z.string().max(1e3)).max(SERIALIZATION_LIMITS.maxContextItems).optional()
|
|
1764
|
+
});
|
|
1765
|
+
z.object({
|
|
1766
|
+
version: z.string().optional(),
|
|
1767
|
+
persona: z.any().optional(),
|
|
1768
|
+
instructions: z.any().optional(),
|
|
1769
|
+
contexts: z.any().optional(),
|
|
1770
|
+
content: z.any().optional()
|
|
1771
|
+
});
|
|
1772
|
+
z.object({
|
|
1773
|
+
id: z.string().max(200),
|
|
1774
|
+
metadata: z.object({
|
|
1775
|
+
startTime: z.union([z.string().datetime(), z.date()]),
|
|
1776
|
+
endTime: z.union([z.string().datetime(), z.date()]).optional(),
|
|
1777
|
+
duration: z.number().nonnegative().optional(),
|
|
1778
|
+
model: z.string().max(SERIALIZATION_LIMITS.maxStringLength),
|
|
1779
|
+
template: z.string().max(SERIALIZATION_LIMITS.maxStringLength).optional(),
|
|
1780
|
+
userContext: z.record(z.string(), z.any()).optional()
|
|
1781
|
+
}),
|
|
1782
|
+
prompt: z.object({
|
|
1783
|
+
persona: z.string().optional(),
|
|
1784
|
+
instructions: z.string().optional(),
|
|
1785
|
+
content: z.array(z.string()).optional(),
|
|
1786
|
+
context: z.array(z.string()).optional()
|
|
1787
|
+
}).optional(),
|
|
1788
|
+
messages: z.array(z.object({
|
|
1789
|
+
index: z.number().int().nonnegative(),
|
|
1790
|
+
timestamp: z.string(),
|
|
1791
|
+
role: z.string(),
|
|
1792
|
+
content: z.string().nullable(),
|
|
1793
|
+
tool_calls: z.array(ToolCallSchema).optional(),
|
|
1794
|
+
tool_call_id: z.string().optional(),
|
|
1795
|
+
metadata: z.record(z.string(), z.any()).optional()
|
|
1796
|
+
})).max(SERIALIZATION_LIMITS.maxMessages),
|
|
1797
|
+
summary: z.object({
|
|
1798
|
+
totalMessages: z.number().int().nonnegative(),
|
|
1799
|
+
totalTokens: z.number().int().nonnegative().optional(),
|
|
1800
|
+
toolCallsExecuted: z.number().int().nonnegative(),
|
|
1801
|
+
iterations: z.number().int().nonnegative(),
|
|
1802
|
+
finalOutput: z.string().optional(),
|
|
1803
|
+
success: z.boolean()
|
|
1804
|
+
})
|
|
1805
|
+
});
|
|
1806
|
+
const program = new Command();
|
|
1807
|
+
const configManager = create$7({
|
|
1369
1808
|
configShape: ConfigSchema.shape,
|
|
1370
1809
|
defaults: {
|
|
1371
1810
|
configDirectory: process.cwd(),
|
|
@@ -1375,7 +1814,7 @@ const configManager = cardigantime.create({
|
|
|
1375
1814
|
program.name("riotprompt").description("CLI tool for analyzing and processing prompts").version("0.0.1");
|
|
1376
1815
|
async function isDirectory(path2) {
|
|
1377
1816
|
try {
|
|
1378
|
-
const stat = await
|
|
1817
|
+
const stat = await fs$1.stat(path2);
|
|
1379
1818
|
return stat.isDirectory();
|
|
1380
1819
|
} catch {
|
|
1381
1820
|
return false;
|
|
@@ -1383,7 +1822,7 @@ async function isDirectory(path2) {
|
|
|
1383
1822
|
}
|
|
1384
1823
|
async function fileExists(path2) {
|
|
1385
1824
|
try {
|
|
1386
|
-
await
|
|
1825
|
+
await fs$1.access(path2);
|
|
1387
1826
|
return true;
|
|
1388
1827
|
} catch {
|
|
1389
1828
|
return false;
|
|
@@ -1394,8 +1833,8 @@ async function loadPromptFromDirectory(absolutePromptPath) {
|
|
|
1394
1833
|
let instructionsSection;
|
|
1395
1834
|
let contextSection;
|
|
1396
1835
|
const loader = create();
|
|
1397
|
-
const personaDir =
|
|
1398
|
-
const personaFile =
|
|
1836
|
+
const personaDir = path.join(absolutePromptPath, "persona");
|
|
1837
|
+
const personaFile = path.join(absolutePromptPath, "persona.md");
|
|
1399
1838
|
if (await isDirectory(personaDir)) {
|
|
1400
1839
|
console.log("Loading persona from directory...");
|
|
1401
1840
|
const personaSections = await loader.load([personaDir]);
|
|
@@ -1407,14 +1846,14 @@ async function loadPromptFromDirectory(absolutePromptPath) {
|
|
|
1407
1846
|
}
|
|
1408
1847
|
} else if (await fileExists(personaFile)) {
|
|
1409
1848
|
console.log("Loading persona from file...");
|
|
1410
|
-
const personaContent = await
|
|
1849
|
+
const personaContent = await fs$1.readFile(personaFile, "utf-8");
|
|
1411
1850
|
personaSection = create$4({ title: "Persona" });
|
|
1412
1851
|
personaSection.add(create$5(personaContent));
|
|
1413
1852
|
} else {
|
|
1414
1853
|
console.log("No persona found, skipping.");
|
|
1415
1854
|
}
|
|
1416
|
-
const instructionsDir =
|
|
1417
|
-
const instructionsFile =
|
|
1855
|
+
const instructionsDir = path.join(absolutePromptPath, "instructions");
|
|
1856
|
+
const instructionsFile = path.join(absolutePromptPath, "instructions.md");
|
|
1418
1857
|
if (await isDirectory(instructionsDir)) {
|
|
1419
1858
|
console.log("Loading instructions from directory...");
|
|
1420
1859
|
const instructionSections = await loader.load([instructionsDir]);
|
|
@@ -1426,14 +1865,14 @@ async function loadPromptFromDirectory(absolutePromptPath) {
|
|
|
1426
1865
|
}
|
|
1427
1866
|
} else if (await fileExists(instructionsFile)) {
|
|
1428
1867
|
console.log("Loading instructions from file...");
|
|
1429
|
-
const instructionsContent = await
|
|
1868
|
+
const instructionsContent = await fs$1.readFile(instructionsFile, "utf-8");
|
|
1430
1869
|
instructionsSection = create$4({ title: "Instructions" });
|
|
1431
1870
|
instructionsSection.add(create$5(instructionsContent));
|
|
1432
1871
|
}
|
|
1433
1872
|
if (!instructionsSection) {
|
|
1434
1873
|
throw new Error("instructions (directory or .md file) is required.");
|
|
1435
1874
|
}
|
|
1436
|
-
const contextDir =
|
|
1875
|
+
const contextDir = path.join(absolutePromptPath, "context");
|
|
1437
1876
|
if (await isDirectory(contextDir)) {
|
|
1438
1877
|
console.log("Loading context from directory...");
|
|
1439
1878
|
const contextSections = await loader.load([contextDir]);
|
|
@@ -1454,14 +1893,14 @@ async function loadPromptFromDirectory(absolutePromptPath) {
|
|
|
1454
1893
|
}
|
|
1455
1894
|
async function createAction(promptName, options) {
|
|
1456
1895
|
try {
|
|
1457
|
-
const basePath =
|
|
1896
|
+
const basePath = path.resolve(options.path, promptName);
|
|
1458
1897
|
if (await fileExists(basePath)) {
|
|
1459
1898
|
console.error(`Error: Directory ${basePath} already exists.`);
|
|
1460
1899
|
process.exit(1);
|
|
1461
1900
|
}
|
|
1462
1901
|
if (options.import) {
|
|
1463
1902
|
console.log(`Importing prompt from ${options.import} to ${basePath}...`);
|
|
1464
|
-
const content = await
|
|
1903
|
+
const content = await fs$1.readFile(options.import, "utf-8");
|
|
1465
1904
|
let prompt;
|
|
1466
1905
|
if (options.import.endsWith(".json")) {
|
|
1467
1906
|
prompt = fromJSON(content);
|
|
@@ -1474,23 +1913,23 @@ async function createAction(promptName, options) {
|
|
|
1474
1913
|
console.log(`Successfully imported to ${basePath}`);
|
|
1475
1914
|
} else {
|
|
1476
1915
|
console.log(`Creating prompt structure at ${basePath}...`);
|
|
1477
|
-
await
|
|
1916
|
+
await fs$1.mkdir(basePath, { recursive: true });
|
|
1478
1917
|
const personaText = options.persona || "You are a helpful AI assistant.";
|
|
1479
|
-
await
|
|
1918
|
+
await fs$1.writeFile(path.join(basePath, "persona.md"), personaText);
|
|
1480
1919
|
console.log("Created persona.md");
|
|
1481
1920
|
const instructionsText = options.instructions || "Please analyze the following request.";
|
|
1482
|
-
await
|
|
1921
|
+
await fs$1.writeFile(path.join(basePath, "instructions.md"), instructionsText);
|
|
1483
1922
|
console.log("Created instructions.md");
|
|
1484
1923
|
if (options.context) {
|
|
1485
|
-
const contextDir =
|
|
1486
|
-
await
|
|
1487
|
-
await
|
|
1924
|
+
const contextDir = path.join(basePath, "context");
|
|
1925
|
+
await fs$1.mkdir(contextDir);
|
|
1926
|
+
await fs$1.writeFile(path.join(contextDir, "README.md"), "Place context files (json, md, txt) in this directory.");
|
|
1488
1927
|
console.log("Created context directory");
|
|
1489
1928
|
}
|
|
1490
1929
|
console.log(`
|
|
1491
1930
|
Prompt '${promptName}' created successfully!`);
|
|
1492
1931
|
}
|
|
1493
|
-
console.log(`Run 'riotprompt process ${
|
|
1932
|
+
console.log(`Run 'riotprompt process ${path.join(options.path, promptName)}' to test it.`);
|
|
1494
1933
|
} catch (error) {
|
|
1495
1934
|
console.error("Error creating prompt:", error);
|
|
1496
1935
|
process.exit(1);
|
|
@@ -1502,7 +1941,7 @@ async function processAction(promptPath, options) {
|
|
|
1502
1941
|
const modelName = options.model || config.defaultModel;
|
|
1503
1942
|
console.log(`Processing prompt from: ${promptPath}`);
|
|
1504
1943
|
console.log(`Using model: ${modelName}`);
|
|
1505
|
-
const absolutePromptPath =
|
|
1944
|
+
const absolutePromptPath = path.resolve(promptPath);
|
|
1506
1945
|
if (!await fileExists(absolutePromptPath)) {
|
|
1507
1946
|
console.error(`Error: Prompt path not found at ${absolutePromptPath}`);
|
|
1508
1947
|
process.exit(1);
|
|
@@ -1511,7 +1950,7 @@ async function processAction(promptPath, options) {
|
|
|
1511
1950
|
if (await isDirectory(absolutePromptPath)) {
|
|
1512
1951
|
prompt = await loadPromptFromDirectory(absolutePromptPath);
|
|
1513
1952
|
} else {
|
|
1514
|
-
const content = await
|
|
1953
|
+
const content = await fs$1.readFile(absolutePromptPath, "utf-8");
|
|
1515
1954
|
if (absolutePromptPath.endsWith(".json")) {
|
|
1516
1955
|
prompt = fromJSON(content);
|
|
1517
1956
|
} else if (absolutePromptPath.endsWith(".xml")) {
|
|
@@ -1538,7 +1977,7 @@ ${m.content}`).join("\n\n");
|
|
|
1538
1977
|
}
|
|
1539
1978
|
}
|
|
1540
1979
|
if (options.output) {
|
|
1541
|
-
await
|
|
1980
|
+
await fs$1.writeFile(options.output, output);
|
|
1542
1981
|
console.log(`Output written to ${options.output}`);
|
|
1543
1982
|
} else {
|
|
1544
1983
|
console.log("\n--- Result ---\n");
|
|
@@ -1555,7 +1994,7 @@ async function executeAction(promptPath, options) {
|
|
|
1555
1994
|
const modelName = options.model || config.defaultModel;
|
|
1556
1995
|
console.log(`Executing prompt from: ${promptPath}`);
|
|
1557
1996
|
console.log(`Using model: ${modelName}`);
|
|
1558
|
-
const absolutePromptPath =
|
|
1997
|
+
const absolutePromptPath = path.resolve(promptPath);
|
|
1559
1998
|
if (!await fileExists(absolutePromptPath)) {
|
|
1560
1999
|
console.error(`Error: Prompt path not found at ${absolutePromptPath}`);
|
|
1561
2000
|
process.exit(1);
|
|
@@ -1564,7 +2003,7 @@ async function executeAction(promptPath, options) {
|
|
|
1564
2003
|
if (await isDirectory(absolutePromptPath)) {
|
|
1565
2004
|
prompt = await loadPromptFromDirectory(absolutePromptPath);
|
|
1566
2005
|
} else {
|
|
1567
|
-
const content = await
|
|
2006
|
+
const content = await fs$1.readFile(absolutePromptPath, "utf-8");
|
|
1568
2007
|
if (absolutePromptPath.endsWith(".json")) {
|
|
1569
2008
|
prompt = fromJSON(content);
|
|
1570
2009
|
} else if (absolutePromptPath.endsWith(".xml")) {
|
|
@@ -1602,16 +2041,19 @@ async function main() {
|
|
|
1602
2041
|
program.command("execute <promptPath>").description("Execute a prompt using an LLM provider").option("-m, --model <model>", "Model to use (e.g., gpt-4, claude-3-opus, gemini-1.5-pro)").option("-k, --key <key>", "API Key (overrides env vars)").option("-t, --temperature <number>", "Temperature (0-1)", parseFloat).option("--max-tokens <number>", "Max tokens", parseInt).action(executeAction);
|
|
1603
2042
|
await program.parseAsync();
|
|
1604
2043
|
}
|
|
1605
|
-
|
|
2044
|
+
const isRunningAsCLI = process.argv[1] && (process.argv[1].endsWith("cli.js") || process.argv[1].endsWith("cli.ts")) && !process.argv[1].includes("vitest") && !process.argv[1].includes("node_modules");
|
|
2045
|
+
if (isRunningAsCLI) {
|
|
1606
2046
|
main().catch((err) => {
|
|
1607
2047
|
console.error(err);
|
|
1608
2048
|
process.exit(1);
|
|
1609
2049
|
});
|
|
1610
2050
|
}
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2051
|
+
export {
|
|
2052
|
+
createAction,
|
|
2053
|
+
executeAction,
|
|
2054
|
+
fileExists,
|
|
2055
|
+
isDirectory,
|
|
2056
|
+
loadPromptFromDirectory,
|
|
2057
|
+
main,
|
|
2058
|
+
processAction
|
|
2059
|
+
};
|