jorgex-stack 1.0.22 → 1.0.23
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/README.md +2 -0
- package/dist/cli.js +63 -11
- package/package.json +1 -1
- package/stack/config/defaults.json +44 -10
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@ For development from a clone, run the same commands through `pnpm cli <command>`
|
|
|
24
24
|
|
|
25
25
|
Every command supports `--dry-run`, `--yes`, and `--target-dir <dir>` for testing without touching the real config. Writes create automatic backups and verify idempotency; merges into user config are surgical (marked markdown sections, JSON/TOML upserts), so user-owned content is never touched.
|
|
26
26
|
|
|
27
|
+
Permission defaults by runtime (what gets installed, what stays custom, and the read-anywhere limitations) are documented in [docs/references/permissions.md](docs/references/permissions.md).
|
|
28
|
+
|
|
27
29
|
### Modes: Human and Programmatic
|
|
28
30
|
|
|
29
31
|
`install` and `sync` accept two mutually-exclusive installation modes. The choice is global (not per runtime) and is saved in `~/.jorgex-stack/install-mode.json` on first run; subsequent `sync` calls reuse it. Re-run `install` with `--mode` to switch.
|
package/dist/cli.js
CHANGED
|
@@ -614,11 +614,17 @@ ${agent.body}`,
|
|
|
614
614
|
planMainConfig(canonical, ctx) {
|
|
615
615
|
const file = path7.join(ctx.configDir, "opencode.json");
|
|
616
616
|
const { pluginsDir } = this.paths(ctx.configDir);
|
|
617
|
-
const
|
|
617
|
+
const original = readTextIfExists(file);
|
|
618
|
+
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
619
|
+
const isFreshConfig = contentSource === null;
|
|
620
|
+
const content = upsertJson(contentSource, (root) => {
|
|
618
621
|
root["$schema"] ??= "https://opencode.ai/config.json";
|
|
619
622
|
const defaults = loadCanonicalDefaults(ctx.stackDir)["opencode"];
|
|
620
|
-
if (defaults?.["permission"] !== void 0
|
|
623
|
+
if (isFreshConfig && defaults?.["permission"] !== void 0) {
|
|
621
624
|
root["permission"] = defaults["permission"];
|
|
625
|
+
ctx.warnings.push(
|
|
626
|
+
"OpenCode: fresh config enables read-anywhere via external_directory:*; edits, web egress and arbitrary bash remain approval-gated, but broad local reads can expose secrets not covered by deny rules."
|
|
627
|
+
);
|
|
622
628
|
}
|
|
623
629
|
const mcp = root["mcp"] ??= {};
|
|
624
630
|
for (const [name, server] of Object.entries(canonical.servers)) {
|
|
@@ -818,12 +824,19 @@ ${agent.body}`,
|
|
|
818
824
|
planHooks(canonical, ctx) {
|
|
819
825
|
const actions = [];
|
|
820
826
|
const { scriptsDir } = this.paths(ctx.configDir);
|
|
827
|
+
const original = readTextIfExists(path8.join(ctx.configDir, "settings.json"));
|
|
828
|
+
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
821
829
|
const settingsFile = path8.join(ctx.configDir, "settings.json");
|
|
822
|
-
let content = upsertNativeHooks(
|
|
830
|
+
let content = upsertNativeHooks(contentSource, canonical, scriptsDir);
|
|
823
831
|
const defaults = loadCanonicalDefaults(ctx.stackDir)["claude-code"];
|
|
824
|
-
if (defaults?.["permissions"] !== void 0) {
|
|
832
|
+
if (contentSource === null && defaults?.["permissions"] !== void 0) {
|
|
825
833
|
content = upsertJson(content, (root) => {
|
|
826
|
-
if (
|
|
834
|
+
if (root["permissions"] === void 0) {
|
|
835
|
+
root["permissions"] = defaults["permissions"];
|
|
836
|
+
ctx.warnings.push(
|
|
837
|
+
"Claude Code: fresh config enables read-anywhere via Read/Grep/Glob allow rules; shell, writes and web egress remain approval-gated, but broad local reads can expose secrets not covered by deny rules."
|
|
838
|
+
);
|
|
839
|
+
}
|
|
827
840
|
});
|
|
828
841
|
}
|
|
829
842
|
actions.push({ kind: "write", target: settingsFile, content });
|
|
@@ -1026,13 +1039,52 @@ ${body}`
|
|
|
1026
1039
|
},
|
|
1027
1040
|
planMainConfig(canonical, ctx) {
|
|
1028
1041
|
const file = path9.join(ctx.configDir, "config.toml");
|
|
1029
|
-
|
|
1030
|
-
const
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
const
|
|
1042
|
+
const original = readTextIfExists(file);
|
|
1043
|
+
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
1044
|
+
let content = contentSource;
|
|
1045
|
+
if (contentSource === null) {
|
|
1046
|
+
const defaults = loadCanonicalDefaults(ctx.stackDir)["codex"] ?? {};
|
|
1047
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
1048
|
+
const line = `${key} = ${tomlString(String(value))}
|
|
1034
1049
|
`;
|
|
1035
|
-
|
|
1050
|
+
content = content === null ? line : line + content;
|
|
1051
|
+
}
|
|
1052
|
+
ctx.warnings.push(
|
|
1053
|
+
"Codex: fresh config enables read-anywhere via the jorgex-read-anywhere permission profile; broad local reads can expose secrets not covered by deny rules."
|
|
1054
|
+
);
|
|
1055
|
+
content = upsertTomlSection(content, "permissions.jorgex-read-anywhere", 'extends = ":workspace"');
|
|
1056
|
+
content = upsertTomlSection(
|
|
1057
|
+
content,
|
|
1058
|
+
"permissions.jorgex-read-anywhere.filesystem",
|
|
1059
|
+
[
|
|
1060
|
+
'":root" = "read"',
|
|
1061
|
+
'"*.env" = "deny"',
|
|
1062
|
+
'"*.env.*" = "deny"',
|
|
1063
|
+
'"~/.ssh/**" = "deny"',
|
|
1064
|
+
'"~/.aws/credentials" = "deny"',
|
|
1065
|
+
'"~/.npmrc" = "deny"',
|
|
1066
|
+
'"~/.git-credentials" = "deny"',
|
|
1067
|
+
'"**/id_rsa" = "deny"',
|
|
1068
|
+
'"**/id_ed25519" = "deny"',
|
|
1069
|
+
'"**/*.pem" = "deny"',
|
|
1070
|
+
'"**/*.key" = "deny"'
|
|
1071
|
+
].join("\n")
|
|
1072
|
+
);
|
|
1073
|
+
content += [
|
|
1074
|
+
'\n[permissions.jorgex-read-anywhere.filesystem.":workspace_roots"]',
|
|
1075
|
+
'"." = "write"',
|
|
1076
|
+
'"*.env" = "deny"',
|
|
1077
|
+
'"*.env.*" = "deny"',
|
|
1078
|
+
'".ssh/**" = "deny"',
|
|
1079
|
+
'".aws/credentials" = "deny"',
|
|
1080
|
+
'".npmrc" = "deny"',
|
|
1081
|
+
'".git-credentials" = "deny"',
|
|
1082
|
+
'"**/id_rsa" = "deny"',
|
|
1083
|
+
'"**/id_ed25519" = "deny"',
|
|
1084
|
+
'"**/*.pem" = "deny"',
|
|
1085
|
+
'"**/*.key" = "deny"',
|
|
1086
|
+
""
|
|
1087
|
+
].join("\n");
|
|
1036
1088
|
}
|
|
1037
1089
|
for (const [name, server] of Object.entries(canonical.servers)) {
|
|
1038
1090
|
const section = `mcp_servers.${name}`;
|
package/package.json
CHANGED
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "Permisos por defecto del stack:
|
|
2
|
+
"$comment": "Permisos por defecto del stack: cada bloque se escribe SOLO en config fresca o vacía; una config existente no se auto-expande jamás. OpenCode fresh default permite leer fuera del cwd, pero deja edit/bash en ask salvo allow explícito para lecturas seguras.",
|
|
3
3
|
"opencode": {
|
|
4
4
|
"permission": {
|
|
5
|
-
"edit": "
|
|
6
|
-
"read":
|
|
5
|
+
"edit": "ask",
|
|
6
|
+
"read": {
|
|
7
|
+
"*": "allow",
|
|
8
|
+
"*.env": "deny",
|
|
9
|
+
"*.env.*": "deny",
|
|
10
|
+
"*.env.example": "allow",
|
|
11
|
+
"*/.ssh/*": "deny",
|
|
12
|
+
"*/.aws/credentials": "deny",
|
|
13
|
+
"*/.npmrc": "deny",
|
|
14
|
+
"*/.git-credentials": "deny",
|
|
15
|
+
"*/id_rsa": "deny",
|
|
16
|
+
"*/id_ed25519": "deny",
|
|
17
|
+
"*.pem": "deny",
|
|
18
|
+
"*.key": "deny"
|
|
19
|
+
},
|
|
20
|
+
"external_directory": {
|
|
21
|
+
"*": "allow"
|
|
22
|
+
},
|
|
7
23
|
"glob": "allow",
|
|
8
24
|
"grep": "allow",
|
|
9
25
|
"list": "allow",
|
|
10
26
|
"lsp": "allow",
|
|
11
|
-
"webfetch": "
|
|
12
|
-
"websearch": "
|
|
27
|
+
"webfetch": "ask",
|
|
28
|
+
"websearch": "ask",
|
|
13
29
|
"bash": {
|
|
14
|
-
"*": "
|
|
30
|
+
"*": "ask",
|
|
31
|
+
"git diff*": "ask",
|
|
32
|
+
"git log*": "allow",
|
|
33
|
+
"git status*": "allow",
|
|
15
34
|
"rm *": "ask",
|
|
16
35
|
"del *": "ask",
|
|
17
36
|
"rmdir *": "ask",
|
|
@@ -25,13 +44,28 @@
|
|
|
25
44
|
},
|
|
26
45
|
"claude-code": {
|
|
27
46
|
"permissions": {
|
|
28
|
-
"allow": ["
|
|
29
|
-
"ask": ["Bash(rm:*)", "Bash(rmdir:*)", "Bash(del:*)", "Bash(git push --force:*)"],
|
|
30
|
-
"deny": [
|
|
47
|
+
"allow": ["Read", "Grep", "Glob"],
|
|
48
|
+
"ask": ["Bash", "Edit", "Write", "WebFetch", "WebSearch", "Bash(rm:*)", "Bash(rmdir:*)", "Bash(del:*)", "Bash(git push --force:*)"],
|
|
49
|
+
"deny": [
|
|
50
|
+
"Bash(format:*)",
|
|
51
|
+
"Bash(mkfs:*)",
|
|
52
|
+
"Bash(dd:*)",
|
|
53
|
+
"Bash(shred:*)",
|
|
54
|
+
"Read(//**/.env)",
|
|
55
|
+
"Read(//**/.env.*)",
|
|
56
|
+
"Read(//**/.ssh/**)",
|
|
57
|
+
"Read(//**/.aws/credentials)",
|
|
58
|
+
"Read(//**/.npmrc)",
|
|
59
|
+
"Read(//**/.git-credentials)",
|
|
60
|
+
"Read(//**/id_rsa)",
|
|
61
|
+
"Read(//**/id_ed25519)",
|
|
62
|
+
"Read(//**/*.pem)",
|
|
63
|
+
"Read(//**/*.key)"
|
|
64
|
+
]
|
|
31
65
|
}
|
|
32
66
|
},
|
|
33
67
|
"codex": {
|
|
34
68
|
"approval_policy": "on-request",
|
|
35
|
-
"
|
|
69
|
+
"default_permissions": "jorgex-read-anywhere"
|
|
36
70
|
}
|
|
37
71
|
}
|