pi-mono-all 1.2.3 → 1.2.4
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 +6 -0
- package/node_modules/pi-mono-sentinel/CHANGELOG.md +6 -0
- package/node_modules/pi-mono-sentinel/README.md +3 -1
- package/node_modules/pi-mono-sentinel/__tests__/config.test.ts +18 -4
- package/node_modules/pi-mono-sentinel/__tests__/path-access.test.ts +6 -0
- package/node_modules/pi-mono-sentinel/config.ts +22 -3
- package/node_modules/pi-mono-sentinel/package.json +1 -1
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# pi-mono-sentinel
|
|
2
2
|
|
|
3
|
+
## 1.12.0
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Store project-scoped Sentinel config under the Pi agent directory (`~/.pi/agent/extensions/sentinel/projects/`) instead of creating `.pi/extensions/sentinel.json` in the current working directory. Existing cwd-local config files are still read for compatibility.
|
|
8
|
+
|
|
3
9
|
## 1.11.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -14,11 +14,13 @@ It addresses cross-cutting security gaps that pure command-based guardrails miss
|
|
|
14
14
|
Sentinel reads and merges optional JSON config from three scopes:
|
|
15
15
|
|
|
16
16
|
1. Global: `$PI_CODING_AGENT_DIR/extensions/sentinel.json` or `~/.pi/agent/extensions/sentinel.json`
|
|
17
|
-
2. Local/project:
|
|
17
|
+
2. Local/project: a current-working-directory scoped file under `$PI_CODING_AGENT_DIR/extensions/sentinel/projects/` or `~/.pi/agent/extensions/sentinel/projects/`
|
|
18
18
|
3. Memory: session-only grants written internally while Pi is running
|
|
19
19
|
|
|
20
20
|
Merge priority is `memory > local > global > defaults`.
|
|
21
21
|
|
|
22
|
+
Local/project config is stored in Pi's agent directory instead of the user's working directory, so Sentinel does not create `.pi/` files in arbitrary project folders. Existing legacy `.pi/extensions/sentinel.json` files are still read for compatibility, but new local/project writes go to the agent directory.
|
|
23
|
+
|
|
22
24
|
```json
|
|
23
25
|
{
|
|
24
26
|
"enabled": true,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
5
|
import { afterEach, beforeEach, describe, test } from "node:test";
|
|
6
6
|
|
|
7
7
|
import { SentinelConfigLoader } from "../config.ts";
|
|
@@ -39,7 +39,7 @@ describe("SentinelConfigLoader", () => {
|
|
|
39
39
|
mkdirSync(join(agentDir, "extensions"), { recursive: true });
|
|
40
40
|
writeFileSync(loader.getConfigPath("global"), JSON.stringify({ pathAccess: { allowedPaths: ["/global"] } }), { flag: "w" });
|
|
41
41
|
loader.load(cwd);
|
|
42
|
-
mkdirSync(
|
|
42
|
+
mkdirSync(dirname(loader.getConfigPath("local")), { recursive: true });
|
|
43
43
|
writeFileSync(loader.getConfigPath("local"), JSON.stringify({ features: { pathAccess: true }, pathAccess: { allowedPaths: ["/local"] } }), { flag: "w" });
|
|
44
44
|
loader.load(cwd);
|
|
45
45
|
loader.save("memory", { pathAccess: { mode: "block", allowedPaths: ["/memory"] } });
|
|
@@ -49,12 +49,26 @@ describe("SentinelConfigLoader", () => {
|
|
|
49
49
|
assert.deepEqual(config.pathAccess.allowedPaths, ["/memory"]);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
test("save writes global and local config files", () => {
|
|
52
|
+
test("save writes global and cwd-scoped local config files under the agent dir", () => {
|
|
53
53
|
const loader = new SentinelConfigLoader();
|
|
54
54
|
loader.load(cwd);
|
|
55
55
|
loader.save("global", { enabled: false });
|
|
56
56
|
loader.save("local", { features: { pathAccess: true } });
|
|
57
57
|
assert.deepEqual(loader.getRawConfig("global"), { enabled: false });
|
|
58
58
|
assert.deepEqual(loader.getRawConfig("local"), { features: { pathAccess: true } });
|
|
59
|
+
assert.equal(loader.getConfigPath("local").startsWith(join(agentDir, "extensions", "sentinel", "projects")), true);
|
|
60
|
+
assert.equal(existsSync(join(cwd, ".pi", "extensions", "sentinel.json")), false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("reads legacy cwd-local config without writing back to cwd", () => {
|
|
64
|
+
const loader = new SentinelConfigLoader();
|
|
65
|
+
mkdirSync(join(cwd, ".pi", "extensions"), { recursive: true });
|
|
66
|
+
writeFileSync(join(cwd, ".pi", "extensions", "sentinel.json"), JSON.stringify({ features: { pathAccess: true } }), { flag: "w" });
|
|
67
|
+
|
|
68
|
+
loader.load(cwd);
|
|
69
|
+
assert.equal(loader.getConfig().features.pathAccess, true);
|
|
70
|
+
|
|
71
|
+
loader.save("local", { pathAccess: { allowedPaths: ["/outside"] } });
|
|
72
|
+
assert.equal(existsSync(loader.getConfigPath("local")), true);
|
|
59
73
|
});
|
|
60
74
|
});
|
|
@@ -44,7 +44,10 @@ describe("path-access helpers", () => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
test("derives and persists selected path-access grants with the correct scope and target", () => {
|
|
47
|
+
const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
|
|
48
|
+
const agentDir = mkdtempSync(join(tmpdir(), "sentinel-path-access-agent-"));
|
|
47
49
|
const cwd = mkdtempSync(join(tmpdir(), "sentinel-path-access-cwd-"));
|
|
50
|
+
process.env.PI_CODING_AGENT_DIR = agentDir;
|
|
48
51
|
try {
|
|
49
52
|
configLoader.load(cwd);
|
|
50
53
|
configLoader.save("memory", { features: { pathAccess: true }, pathAccess: { mode: "ask", allowedPaths: [] } });
|
|
@@ -69,6 +72,9 @@ describe("path-access helpers", () => {
|
|
|
69
72
|
configLoader.addAllowedPath(localFileGrant.scope, localFileGrant.grant);
|
|
70
73
|
assert.ok(configLoader.getRawConfig("local")?.pathAccess?.allowedPaths?.includes("/tmp/outside-file.txt"));
|
|
71
74
|
} finally {
|
|
75
|
+
if (originalAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR;
|
|
76
|
+
else process.env.PI_CODING_AGENT_DIR = originalAgentDir;
|
|
77
|
+
rmSync(agentDir, { recursive: true, force: true });
|
|
72
78
|
rmSync(cwd, { recursive: true, force: true });
|
|
73
79
|
}
|
|
74
80
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
4
5
|
|
|
5
6
|
export type SentinelConfigScope = "global" | "local" | "memory";
|
|
6
7
|
export type SentinelPathAccessMode = "allow" | "ask" | "block";
|
|
@@ -92,6 +93,20 @@ function writeJson(path: string, value: SentinelConfig): void {
|
|
|
92
93
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
function localScopeId(cwd: string): string {
|
|
97
|
+
const normalizedCwd = resolve(cwd);
|
|
98
|
+
const slug = (basename(normalizedCwd) || "root")
|
|
99
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "-")
|
|
100
|
+
.replace(/^-+|-+$/g, "")
|
|
101
|
+
.slice(0, 48) || "root";
|
|
102
|
+
const hash = createHash("sha256").update(normalizedCwd).digest("hex").slice(0, 12);
|
|
103
|
+
return `${slug}-${hash}.json`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function legacyLocalConfigPath(cwd: string): string {
|
|
107
|
+
return join(resolve(cwd), ".pi", "extensions", "sentinel.json");
|
|
108
|
+
}
|
|
109
|
+
|
|
95
110
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
96
111
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
97
112
|
}
|
|
@@ -127,7 +142,11 @@ export class SentinelConfigLoader {
|
|
|
127
142
|
load(cwd = process.cwd()): void {
|
|
128
143
|
this.localCwd = cwd;
|
|
129
144
|
this.globalConfig = readJson(this.getConfigPath("global"));
|
|
130
|
-
|
|
145
|
+
const legacyLocalConfig = readJson(legacyLocalConfigPath(this.localCwd));
|
|
146
|
+
const scopedLocalConfig = readJson(this.getConfigPath("local"));
|
|
147
|
+
this.localConfig = legacyLocalConfig && scopedLocalConfig
|
|
148
|
+
? mergeConfig(legacyLocalConfig, scopedLocalConfig)
|
|
149
|
+
: scopedLocalConfig ?? legacyLocalConfig;
|
|
131
150
|
this.resolvedConfig = undefined;
|
|
132
151
|
}
|
|
133
152
|
|
|
@@ -152,7 +171,7 @@ export class SentinelConfigLoader {
|
|
|
152
171
|
getConfigPath(scope: Exclude<SentinelConfigScope, "memory">): string {
|
|
153
172
|
return scope === "global"
|
|
154
173
|
? join(getAgentDir(), "extensions", "sentinel.json")
|
|
155
|
-
: join(
|
|
174
|
+
: join(getAgentDir(), "extensions", "sentinel", "projects", localScopeId(this.localCwd));
|
|
156
175
|
}
|
|
157
176
|
|
|
158
177
|
save(scope: SentinelConfigScope, partial: SentinelConfig): void {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mono-all",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "All pi-mono extensions and bundled skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -9,24 +9,24 @@
|
|
|
9
9
|
"pi-skill"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"pi-mono-ask-user-question": "1.7.4",
|
|
13
12
|
"pi-mono-auto-fix": "0.3.1",
|
|
13
|
+
"pi-mono-ask-user-question": "1.7.4",
|
|
14
14
|
"pi-mono-btw": "1.7.4",
|
|
15
|
-
"pi-mono-context": "0.1.1",
|
|
16
15
|
"pi-mono-clear": "1.7.3",
|
|
17
|
-
"pi-mono-
|
|
16
|
+
"pi-mono-context": "0.1.1",
|
|
18
17
|
"pi-mono-context-guard": "1.7.3",
|
|
19
18
|
"pi-mono-linear": "0.2.3",
|
|
20
|
-
"pi-
|
|
21
|
-
"pi-mono-
|
|
22
|
-
"pi-mono-
|
|
19
|
+
"pi-common": "0.1.1",
|
|
20
|
+
"pi-mono-figma": "0.2.2",
|
|
21
|
+
"pi-mono-multi-edit": "1.7.3",
|
|
22
|
+
"pi-mono-sentinel": "1.12.0",
|
|
23
23
|
"pi-mono-simplify": "1.7.3",
|
|
24
|
+
"pi-mono-review": "1.8.2",
|
|
24
25
|
"pi-mono-team-mode": "2.3.2",
|
|
25
|
-
"pi-mono-multi-edit": "1.7.3",
|
|
26
|
-
"pi-common": "0.1.1",
|
|
27
|
-
"pi-mono-web-search": "0.1.0",
|
|
28
26
|
"pi-mono-status-line": "1.7.3",
|
|
29
|
-
"pi-mono-usage": "0.1.1"
|
|
27
|
+
"pi-mono-usage": "0.1.1",
|
|
28
|
+
"pi-mono-web-search": "0.1.0",
|
|
29
|
+
"pi-mono-loop": "1.7.3"
|
|
30
30
|
},
|
|
31
31
|
"bundledDependencies": [
|
|
32
32
|
"pi-mono-ask-user-question",
|