@xynogen/pix-runtime 0.1.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.
@@ -0,0 +1,71 @@
1
+ import { boolOr, defineSection, isObj, strArr } from "../schema.ts";
2
+
3
+ export type GateSeverity = "risky" | "dangerous" | "critical";
4
+
5
+ export interface GateRuleConfig {
6
+ pattern: string;
7
+ flags?: string;
8
+ severity?: GateSeverity;
9
+ reason?: string;
10
+ }
11
+
12
+ export interface GateConfig {
13
+ disableDefaults: boolean;
14
+ autoApprove: string[];
15
+ extraRules: GateRuleConfig[];
16
+ }
17
+
18
+ const SEVERITIES: readonly GateSeverity[] = ["risky", "dangerous", "critical"];
19
+
20
+ const DEFAULTS: Readonly<GateConfig> = {
21
+ disableDefaults: false,
22
+ autoApprove: [],
23
+ extraRules: [],
24
+ };
25
+
26
+ /** Validate a regex pattern + flags; return an error message or undefined. */
27
+ function regexError(pattern: string, flags?: string): string | undefined {
28
+ try {
29
+ new RegExp(pattern, flags);
30
+ return undefined;
31
+ } catch (err) {
32
+ return err instanceof Error ? err.message : String(err);
33
+ }
34
+ }
35
+
36
+ export const gateSection = defineSection<"gate", GateConfig>({
37
+ key: "gate",
38
+ defaults: DEFAULTS,
39
+ parse(raw, ctx) {
40
+ if (!isObj(raw)) return { disableDefaults: false, autoApprove: [], extraRules: [] };
41
+ const extraRules: GateRuleConfig[] = [];
42
+ if (Array.isArray(raw.extraRules)) {
43
+ raw.extraRules.forEach((r, i) => {
44
+ if (!isObj(r) || typeof r.pattern !== "string") return;
45
+ const flags = typeof r.flags === "string" ? r.flags : undefined;
46
+ const err = regexError(r.pattern, flags);
47
+ if (err) {
48
+ ctx.diagnostic({
49
+ code: "INVALID_VALUE",
50
+ severity: "warning",
51
+ path: `gate.extraRules[${i}]`,
52
+ message: `invalid regex: ${err}`,
53
+ });
54
+ return;
55
+ }
56
+ const rule: GateRuleConfig = { pattern: r.pattern };
57
+ if (flags) rule.flags = flags;
58
+ if (typeof r.severity === "string" && SEVERITIES.includes(r.severity as GateSeverity)) {
59
+ rule.severity = r.severity as GateSeverity;
60
+ }
61
+ if (typeof r.reason === "string") rule.reason = r.reason;
62
+ extraRules.push(rule);
63
+ });
64
+ }
65
+ return {
66
+ disableDefaults: boolOr(raw.disableDefaults, DEFAULTS.disableDefaults),
67
+ autoApprove: strArr(raw.autoApprove),
68
+ extraRules,
69
+ };
70
+ },
71
+ });
@@ -0,0 +1,34 @@
1
+ export { type CollapseConfig, collapseSection } from "./collapse.ts";
2
+ export {
3
+ type GateConfig,
4
+ type GateRuleConfig,
5
+ type GateSeverity,
6
+ gateSection,
7
+ } from "./gate.ts";
8
+ export {
9
+ type CavemanLevel,
10
+ type OptimizerConfig,
11
+ optimizerSection,
12
+ type PonytailLevel,
13
+ type Toggle,
14
+ } from "./optimizer.ts";
15
+ export {
16
+ type DiffConfig,
17
+ type IconMode,
18
+ type LsStyle,
19
+ type PrettyConfig,
20
+ prettySection,
21
+ } from "./pretty.ts";
22
+
23
+ import { collapseSection } from "./collapse.ts";
24
+ import { gateSection } from "./gate.ts";
25
+ import { optimizerSection } from "./optimizer.ts";
26
+ import { prettySection } from "./pretty.ts";
27
+
28
+ /** All built-in sections, in stable registration order. */
29
+ export const builtinSections = [
30
+ collapseSection,
31
+ prettySection,
32
+ optimizerSection,
33
+ gateSection,
34
+ ] as const;
@@ -0,0 +1,37 @@
1
+ import { defineSection, enumOr, isObj } from "../schema.ts";
2
+
3
+ export type CavemanLevel = "off" | "lite" | "full" | "ultra" | "micro";
4
+ export type PonytailLevel = "off" | "lite" | "full" | "ultra";
5
+ export type Toggle = "off" | "on";
6
+
7
+ export interface OptimizerConfig {
8
+ caveman: CavemanLevel;
9
+ rtk: Toggle;
10
+ toon: Toggle;
11
+ ponytail: PonytailLevel;
12
+ }
13
+
14
+ const CAVEMAN: readonly CavemanLevel[] = ["off", "lite", "full", "ultra", "micro"];
15
+ const PONYTAIL: readonly PonytailLevel[] = ["off", "lite", "full", "ultra"];
16
+ const TOGGLE: readonly Toggle[] = ["off", "on"];
17
+
18
+ const DEFAULTS: Readonly<OptimizerConfig> = {
19
+ caveman: "off",
20
+ rtk: "on",
21
+ toon: "on",
22
+ ponytail: "off",
23
+ };
24
+
25
+ export const optimizerSection = defineSection<"optimizer", OptimizerConfig>({
26
+ key: "optimizer",
27
+ defaults: DEFAULTS,
28
+ parse(raw) {
29
+ if (!isObj(raw)) return { ...DEFAULTS };
30
+ return {
31
+ caveman: enumOr(raw.caveman, CAVEMAN, DEFAULTS.caveman),
32
+ rtk: enumOr(raw.rtk, TOGGLE, DEFAULTS.rtk),
33
+ toon: enumOr(raw.toon, TOGGLE, DEFAULTS.toon),
34
+ ponytail: enumOr(raw.ponytail, PONYTAIL, DEFAULTS.ponytail),
35
+ };
36
+ },
37
+ });
@@ -0,0 +1,53 @@
1
+ import { defineSection, enumOr, isObj, posNumOr } from "../schema.ts";
2
+
3
+ export type IconMode = "nerd" | "unicode" | "ascii";
4
+ export type LsStyle = "grid" | "tree";
5
+
6
+ export interface DiffConfig {
7
+ splitMinWidth: number;
8
+ splitMinCodeWidth: number;
9
+ }
10
+
11
+ export interface PrettyConfig {
12
+ icons: IconMode;
13
+ lsStyle: LsStyle;
14
+ maxPreviewLines: number;
15
+ maxRenderLines: number;
16
+ maxHighlightChars: number;
17
+ cacheLimit: number;
18
+ diff: DiffConfig;
19
+ }
20
+
21
+ const ICON_MODES: readonly IconMode[] = ["nerd", "unicode", "ascii"];
22
+ const LS_STYLES: readonly LsStyle[] = ["grid", "tree"];
23
+
24
+ const DEFAULTS: Readonly<PrettyConfig> = {
25
+ icons: "nerd",
26
+ lsStyle: "grid",
27
+ maxPreviewLines: 80,
28
+ maxRenderLines: 150,
29
+ maxHighlightChars: 80_000,
30
+ cacheLimit: 128,
31
+ diff: { splitMinWidth: 150, splitMinCodeWidth: 60 },
32
+ };
33
+
34
+ export const prettySection = defineSection<"pretty", PrettyConfig>({
35
+ key: "pretty",
36
+ defaults: DEFAULTS,
37
+ parse(raw) {
38
+ if (!isObj(raw)) return { ...DEFAULTS, diff: { ...DEFAULTS.diff } };
39
+ const rawDiff = isObj(raw.diff) ? raw.diff : {};
40
+ return {
41
+ icons: enumOr(raw.icons, ICON_MODES, DEFAULTS.icons),
42
+ lsStyle: enumOr(raw.lsStyle, LS_STYLES, DEFAULTS.lsStyle),
43
+ maxPreviewLines: posNumOr(raw.maxPreviewLines, DEFAULTS.maxPreviewLines),
44
+ maxRenderLines: posNumOr(raw.maxRenderLines, DEFAULTS.maxRenderLines),
45
+ maxHighlightChars: posNumOr(raw.maxHighlightChars, DEFAULTS.maxHighlightChars),
46
+ cacheLimit: posNumOr(raw.cacheLimit, DEFAULTS.cacheLimit),
47
+ diff: {
48
+ splitMinWidth: posNumOr(rawDiff.splitMinWidth, DEFAULTS.diff.splitMinWidth),
49
+ splitMinCodeWidth: posNumOr(rawDiff.splitMinCodeWidth, DEFAULTS.diff.splitMinCodeWidth),
50
+ },
51
+ };
52
+ },
53
+ });
package/src/testing.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * testing.ts — construct isolated runtimes with injected adapters so tests
3
+ * never touch the real agent directory or the process singleton.
4
+ */
5
+
6
+ import { mkdtempSync, rmSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { FileStorage } from "./persistence.ts";
10
+ import { createRuntime, type PixRuntime } from "./runtime.ts";
11
+
12
+ export interface IsolatedRuntime {
13
+ runtime: PixRuntime;
14
+ agentDir: string;
15
+ /** Remove the temp directory. */
16
+ cleanup(): void;
17
+ }
18
+
19
+ /** Create a runtime backed by a fresh temp directory. */
20
+ export function createIsolatedRuntime(): IsolatedRuntime {
21
+ const agentDir = mkdtempSync(join(tmpdir(), "pix-runtime-"));
22
+ const runtime = createRuntime({ agentDir, storage: new FileStorage(agentDir) });
23
+ return {
24
+ runtime,
25
+ agentDir,
26
+ cleanup: () => rmSync(agentDir, { recursive: true, force: true }),
27
+ };
28
+ }