killeros 2.1.29 → 2.1.31

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.
@@ -1,122 +1,122 @@
1
- import { closeSync, fstatSync, lstatSync, openSync, readSync, realpathSync } from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { StringDecoder } from "node:string_decoder";
5
- import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
-
7
- const PERSONAL_INSTRUCTIONS_FILE = "AGENTS.local.md";
8
- const PERSONAL_INSTRUCTIONS_LIMIT = 32 * 1024;
9
-
10
- function readBoundedText(filePath: string, limit = PERSONAL_INSTRUCTIONS_LIMIT): string | undefined {
11
- let descriptor: number | undefined;
12
- try {
13
- const pathStat = lstatSync(filePath);
14
- if (!pathStat.isFile() || pathStat.isSymbolicLink() || pathStat.nlink !== 1) return undefined;
15
- descriptor = openSync(filePath, "r");
16
- const openedStat = fstatSync(descriptor);
17
- if (!openedStat.isFile() || openedStat.nlink !== 1
18
- || openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) return undefined;
19
- const buffer = Buffer.alloc(limit + 1);
20
- const bytesRead = readSync(descriptor, buffer, 0, buffer.length, 0);
21
- const decoder = new StringDecoder("utf8");
22
- const content = decoder.write(buffer.subarray(0, Math.min(bytesRead, limit)));
23
- if (!content.trim()) return undefined;
24
- return bytesRead > limit
25
- ? `${content}\n\n[Personal instructions truncated by KillerOS]`
26
- : content;
27
- } catch {
28
- return undefined;
29
- } finally {
30
- if (descriptor !== undefined) {
31
- try {
32
- closeSync(descriptor);
33
- } catch {
34
- // Ignore cleanup failures after a bounded best-effort read.
35
- }
36
- }
37
- }
38
- }
39
-
40
- /** Checks path containment using host filesystem casing rules. */
41
- function pathInside(root: string, candidate: string): boolean {
42
- const normalizedRoot = process.platform === "win32" ? root.toLowerCase() : root;
43
- const normalizedCandidate = process.platform === "win32" ? candidate.toLowerCase() : candidate;
44
- const relative = path.relative(normalizedRoot, normalizedCandidate);
45
- return Boolean(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
46
- }
47
-
48
- /** Reads one stable, unlinked import from inside Pi's agent directory. */
49
- function readImportedText(filePath: string): string | undefined {
50
- let descriptor: number | undefined;
51
- try {
52
- const allowedPath = path.resolve(getAgentDir());
53
- const requestedPath = path.resolve(filePath);
54
- if (!pathInside(allowedPath, requestedPath)) return undefined;
55
-
56
- const allowedRealPath = realpathSync(allowedPath);
57
- const requestedRealPath = realpathSync(requestedPath);
58
- if (!pathInside(allowedRealPath, requestedRealPath)) return undefined;
59
-
60
- let current = allowedPath;
61
- for (const segment of path.relative(allowedPath, requestedPath).split(path.sep)) {
62
- current = path.join(current, segment);
63
- if (lstatSync(current).isSymbolicLink()) return undefined;
64
- }
65
-
66
- const pathStat = lstatSync(requestedPath);
67
- if (!pathStat.isFile() || pathStat.isSymbolicLink() || pathStat.nlink !== 1) return undefined;
68
- descriptor = openSync(requestedPath, "r");
69
- const openedStat = fstatSync(descriptor);
70
- if (!openedStat.isFile() || openedStat.nlink !== 1
71
- || openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) return undefined;
72
-
73
- const buffer = Buffer.alloc(PERSONAL_INSTRUCTIONS_LIMIT + 1);
74
- const bytesRead = readSync(descriptor, buffer, 0, buffer.length, 0);
75
- if (bytesRead > PERSONAL_INSTRUCTIONS_LIMIT) return undefined;
76
- const content = new StringDecoder("utf8").write(buffer.subarray(0, bytesRead));
77
- return content.trim() ? content : undefined;
78
- } catch {
79
- return undefined;
80
- } finally {
81
- if (descriptor !== undefined) {
82
- try {
83
- closeSync(descriptor);
84
- } catch {
85
- // Ignore cleanup failures after rejecting or reading an import.
86
- }
87
- }
88
- }
89
- }
90
-
91
- export function resolvePersonalInstructions(cwd: string): string | undefined {
92
- const local = readBoundedText(path.join(cwd, PERSONAL_INSTRUCTIONS_FILE));
93
- if (!local) return undefined;
94
-
95
- const importMatch = local.trim().match(/^@(.+)$/u);
96
- let content = local;
97
- if (importMatch) {
98
- const requestedPath = (importMatch[1] ?? "").trim();
99
- const importedPath = requestedPath.startsWith("~/") || requestedPath.startsWith("~\\")
100
- ? path.join(os.homedir(), requestedPath.slice(2))
101
- : path.resolve(cwd, requestedPath);
102
- const imported = readImportedText(importedPath);
103
- if (!imported) return undefined;
104
- content = imported;
105
- }
106
- return `<personal_instructions>\n${content}\n</personal_instructions>`;
107
- }
108
-
109
- export function registerPersonalInstructions(pi: ExtensionAPI): void {
110
- pi.on("before_agent_start", (event, ctx) => {
111
- if (!ctx.isProjectTrusted()) return;
112
- const personal = resolvePersonalInstructions(ctx.cwd);
113
- if (!personal) return;
114
- return {
115
- systemPrompt: [
116
- event.systemPrompt,
117
- "",
118
- personal,
119
- ].join("\n"),
120
- };
121
- });
122
- }
1
+ import { closeSync, fstatSync, lstatSync, openSync, readSync, realpathSync } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { StringDecoder } from "node:string_decoder";
5
+ import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+
7
+ const PERSONAL_INSTRUCTIONS_FILE = "AGENTS.local.md";
8
+ const PERSONAL_INSTRUCTIONS_LIMIT = 32 * 1024;
9
+
10
+ function readBoundedText(filePath: string, limit = PERSONAL_INSTRUCTIONS_LIMIT): string | undefined {
11
+ let descriptor: number | undefined;
12
+ try {
13
+ const pathStat = lstatSync(filePath);
14
+ if (!pathStat.isFile() || pathStat.isSymbolicLink() || pathStat.nlink !== 1) return undefined;
15
+ descriptor = openSync(filePath, "r");
16
+ const openedStat = fstatSync(descriptor);
17
+ if (!openedStat.isFile() || openedStat.nlink !== 1
18
+ || openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) return undefined;
19
+ const buffer = Buffer.alloc(limit + 1);
20
+ const bytesRead = readSync(descriptor, buffer, 0, buffer.length, 0);
21
+ const decoder = new StringDecoder("utf8");
22
+ const content = decoder.write(buffer.subarray(0, Math.min(bytesRead, limit)));
23
+ if (!content.trim()) return undefined;
24
+ return bytesRead > limit
25
+ ? `${content}\n\n[Personal instructions truncated by KillerOS]`
26
+ : content;
27
+ } catch {
28
+ return undefined;
29
+ } finally {
30
+ if (descriptor !== undefined) {
31
+ try {
32
+ closeSync(descriptor);
33
+ } catch {
34
+ // Ignore cleanup failures after a bounded best-effort read.
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ /** Checks path containment using host filesystem casing rules. */
41
+ function pathInside(root: string, candidate: string): boolean {
42
+ const normalizedRoot = process.platform === "win32" ? root.toLowerCase() : root;
43
+ const normalizedCandidate = process.platform === "win32" ? candidate.toLowerCase() : candidate;
44
+ const relative = path.relative(normalizedRoot, normalizedCandidate);
45
+ return Boolean(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
46
+ }
47
+
48
+ /** Reads one stable, unlinked import from inside Pi's agent directory. */
49
+ function readImportedText(filePath: string): string | undefined {
50
+ let descriptor: number | undefined;
51
+ try {
52
+ const allowedPath = path.resolve(getAgentDir());
53
+ const requestedPath = path.resolve(filePath);
54
+ if (!pathInside(allowedPath, requestedPath)) return undefined;
55
+
56
+ const allowedRealPath = realpathSync(allowedPath);
57
+ const requestedRealPath = realpathSync(requestedPath);
58
+ if (!pathInside(allowedRealPath, requestedRealPath)) return undefined;
59
+
60
+ let current = allowedPath;
61
+ for (const segment of path.relative(allowedPath, requestedPath).split(path.sep)) {
62
+ current = path.join(current, segment);
63
+ if (lstatSync(current).isSymbolicLink()) return undefined;
64
+ }
65
+
66
+ const pathStat = lstatSync(requestedPath);
67
+ if (!pathStat.isFile() || pathStat.isSymbolicLink() || pathStat.nlink !== 1) return undefined;
68
+ descriptor = openSync(requestedPath, "r");
69
+ const openedStat = fstatSync(descriptor);
70
+ if (!openedStat.isFile() || openedStat.nlink !== 1
71
+ || openedStat.dev !== pathStat.dev || openedStat.ino !== pathStat.ino) return undefined;
72
+
73
+ const buffer = Buffer.alloc(PERSONAL_INSTRUCTIONS_LIMIT + 1);
74
+ const bytesRead = readSync(descriptor, buffer, 0, buffer.length, 0);
75
+ if (bytesRead > PERSONAL_INSTRUCTIONS_LIMIT) return undefined;
76
+ const content = new StringDecoder("utf8").write(buffer.subarray(0, bytesRead));
77
+ return content.trim() ? content : undefined;
78
+ } catch {
79
+ return undefined;
80
+ } finally {
81
+ if (descriptor !== undefined) {
82
+ try {
83
+ closeSync(descriptor);
84
+ } catch {
85
+ // Ignore cleanup failures after rejecting or reading an import.
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ export function resolvePersonalInstructions(cwd: string): string | undefined {
92
+ const local = readBoundedText(path.join(cwd, PERSONAL_INSTRUCTIONS_FILE));
93
+ if (!local) return undefined;
94
+
95
+ const importMatch = local.trim().match(/^@(.+)$/u);
96
+ let content = local;
97
+ if (importMatch) {
98
+ const requestedPath = (importMatch[1] ?? "").trim();
99
+ const importedPath = requestedPath.startsWith("~/") || requestedPath.startsWith("~\\")
100
+ ? path.join(os.homedir(), requestedPath.slice(2))
101
+ : path.resolve(cwd, requestedPath);
102
+ const imported = readImportedText(importedPath);
103
+ if (!imported) return undefined;
104
+ content = imported;
105
+ }
106
+ return `<personal_instructions>\n${content}\n</personal_instructions>`;
107
+ }
108
+
109
+ export function registerPersonalInstructions(pi: ExtensionAPI): void {
110
+ pi.on("before_agent_start", (event, ctx) => {
111
+ if (!ctx.isProjectTrusted()) return;
112
+ const personal = resolvePersonalInstructions(ctx.cwd);
113
+ if (!personal) return;
114
+ return {
115
+ systemPrompt: [
116
+ event.systemPrompt,
117
+ "",
118
+ personal,
119
+ ].join("\n"),
120
+ };
121
+ });
122
+ }