@cmodjs/core 1.5.1 → 1.6.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/docs/commands.md +1 -1
- package/docs/mod.md +8 -5
- package/docs/permissions.md +3 -3
- package/package.json +1 -1
- package/records.d.ts +3 -1
- package/records.d.ts.map +1 -1
- package/records.js +14 -5
- package/runtime/grants.d.ts +1 -1
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +6 -4
- package/runtime/lifecycle.js +1 -1
- package/src/records.ts +15 -5
- package/src/runtime/grants.ts +4 -3
- package/src/runtime/lifecycle.ts +1 -1
package/docs/commands.md
CHANGED
|
@@ -251,7 +251,7 @@ Turns one permission of a set-up mod on or off, such as `cmod permission ci-watc
|
|
|
251
251
|
cmod settings <mod> <user|project|local> <json> [--project <folder>]
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
-
Changes Claude Code settings for a set-up mod, as its `settings.user.update`, `settings.project.update`, and `settings.local.update` do ([mod.md](mod.md#settings-claude-code-s-settings-files)), such as `cmod settings
|
|
254
|
+
Changes Claude Code settings for a set-up mod, as its `settings.user.update`, `settings.project.update`, and `settings.local.update` do ([mod.md](mod.md#settings-claude-code-s-settings-files)), such as `cmod settings oneclaude user '{"env":{"ANTHROPIC_BASE_URL":"http://127.0.0.1:8317"}}'`. It merges the JSON object into the user, project, or local settings as a JSON Merge Patch: an object merges into the object already there, `null` removes the key, and an array or any other value replaces it. The project and local settings are those of `--project`, the current folder when it is left out, and `mcpServers` in the project settings goes to `.mcp.json`. Each value needs the mod's `config` permission for its key path or a path above it. cmod keeps every other key and the file's formatting, and `cmod teardown` takes out each value the mod set that is still the same, then each object that leaves empty.
|
|
255
255
|
|
|
256
256
|
### cmod rules
|
|
257
257
|
|
package/docs/mod.md
CHANGED
|
@@ -272,14 +272,17 @@ type Setting = { key: string; label: string; description?: string; kind: 'boolea
|
|
|
272
272
|
`settings.user` is the person's own `~/.claude/settings.json`, `settings.project` is the project's `.claude/settings.json`, and `settings.local` is the project's `.claude/settings.local.json`.
|
|
273
273
|
|
|
274
274
|
- `read` answers the values in that one file, such as `(await settings.user.read())['enabledPlugins']`.
|
|
275
|
-
- `update`
|
|
276
|
-
-
|
|
277
|
-
-
|
|
278
|
-
-
|
|
275
|
+
- `update` merges `values` into that file as a JSON Merge Patch ([RFC 7396](https://www.rfc-editor.org/rfc/rfc7396)). `mcpServers` in `settings.project` goes to the project's `.mcp.json`, where Claude Code reads a project's servers.
|
|
276
|
+
- An object merges into the object already at that key, at every depth, so every key it does not name stays as it was. `null` removes the key at its depth. An array or any other value replaces the value. To replace a whole object, read it and set each key you drop to `null`.
|
|
277
|
+
- The file's comments and formatting stay as they were.
|
|
278
|
+
- Each value needs a `config` permission for its key path or a path above it ([permissions.md](permissions.md)): `"config": ["env.ANTHROPIC_BASE_URL"]` covers that one value, and `"config": ["env"]` covers every key in `env`. `update` rejects with the line to add for a value no grant covers, and changes nothing.
|
|
279
|
+
- Removing the mod takes out each value it set that still equals what it set, then each object that leaves empty. A value the person changed since stays.
|
|
279
280
|
- `fs.write` to a Claude Code settings file rejects and names `update`, because a whole-file write loses the person's own edits and leaves the mod's values behind when it is removed.
|
|
280
281
|
|
|
282
|
+
A mod that points Claude Code at its local proxy sets one variable in `env`, and the person's other variables stay:
|
|
283
|
+
|
|
281
284
|
```ts
|
|
282
|
-
await settings.user.update({
|
|
285
|
+
await settings.user.update({ env: { ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317' } })
|
|
283
286
|
```
|
|
284
287
|
|
|
285
288
|
## model: one question outside the conversation
|
package/docs/permissions.md
CHANGED
|
@@ -24,7 +24,7 @@ They sit in the `package.json` `cmod` key, beside `install` and `keys`, because
|
|
|
24
24
|
| `"model": true` | Ask a model, which uses your plan | `model.complete` |
|
|
25
25
|
| `"tasks": true` | Start tasks that work beside Claude | A session's `startTask`, and a `before(TaskStart)` or `before(TaskEnd)` answer |
|
|
26
26
|
| `"tools": true` | Use and change Claude's tool calls | A session's `callTool`, a `before(ToolCall)` answer, and a listener or a job that changes a tool call's input |
|
|
27
|
-
| `"config": ["
|
|
27
|
+
| `"config": ["env.ANTHROPIC_BASE_URL"]` | Change env.ANTHROPIC_BASE_URL in your Claude Code settings | Each value `settings.user.update`, `settings.project.update`, and `settings.local.update` change. A key path covers its own value and every key below it, so `"env"` covers every key in `env`, and `await settings.user.update({ env: { ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317' } })` needs `"env.ANTHROPIC_BASE_URL"` or `"env"`. `mcpServers` covers the project's `.mcp.json` |
|
|
28
28
|
| `"approve": true` | Approve Claude's tool calls for you | an allow or an ask from the mod's permission rules or hooks, and a `retry`. A deny needs no permission |
|
|
29
29
|
|
|
30
30
|
Reading files, drawing panes and renders, toasts, slash commands, tools of the mod's own, and listeners need no permission. Writing a Claude Code settings file with `fs.write` is refused with every permission, and names the `settings` call to use instead ([mod.md](mod.md#settings-claude-code-s-settings-files)).
|
|
@@ -56,11 +56,11 @@ type Permissions = {
|
|
|
56
56
|
readonly network: { allows(host: string): boolean }
|
|
57
57
|
readonly run: { allows(program: string): boolean }
|
|
58
58
|
readonly files: { allows(path: string): boolean }
|
|
59
|
-
readonly config: { allows(
|
|
59
|
+
readonly config: { allows(keyPath: string): boolean }
|
|
60
60
|
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
`mod.permissions` says whether the person has each permission turned on, so a mod hides a feature instead of failing it. A flag is `true` while it is granted, and `allows` answers for one host, program, path, or settings key
|
|
63
|
+
`mod.permissions` says whether the person has each permission turned on, so a mod hides a feature instead of failing it. A flag is `true` while it is granted, and `allows` answers for one host, program, path, or settings key path, such as `env.ANTHROPIC_BASE_URL`:
|
|
64
64
|
|
|
65
65
|
```ts
|
|
66
66
|
if (mod.permissions.model) {
|
package/package.json
CHANGED
package/records.d.ts
CHANGED
|
@@ -63,6 +63,8 @@ export declare function readRecord(read: ReadFile, store: string, name: string):
|
|
|
63
63
|
export declare function writeRecord(write: (path: string, text: string) => Promise<void>, store: string, record: InstallRecord): Promise<void>;
|
|
64
64
|
export declare function readSteps(pkg: unknown): Steps | undefined;
|
|
65
65
|
export declare function permissionWords(item: string): string;
|
|
66
|
+
export declare function settingPaths(values: Readonly<Record<string, unknown>>): string[][];
|
|
67
|
+
export declare function isSettingCovered(granted: Iterable<string>, path: string): boolean;
|
|
66
68
|
export declare const settingsPagesMethod = "cmod:settingsPages";
|
|
67
69
|
export declare const changelogPage = "changelog";
|
|
68
70
|
export declare const openPageMethod = "cmod:openPage";
|
|
@@ -72,7 +74,7 @@ export declare function marketplaceOf(root: string, name: string): string | unde
|
|
|
72
74
|
export declare function updatesToTurnOn(marketplace: string | undefined, settings: unknown, known: unknown): string | undefined;
|
|
73
75
|
export declare function consentPath(store: string): string;
|
|
74
76
|
export declare function parseConsent(value: unknown, path: string): Record<string, string[]>;
|
|
75
|
-
export declare const coreVersion = "1.
|
|
77
|
+
export declare const coreVersion = "1.6.0";
|
|
76
78
|
export declare function oldestCmodFor(steps: Steps): string;
|
|
77
79
|
export declare function keyWords(keys: Readonly<Record<string, string>> | undefined): string;
|
|
78
80
|
export declare function scriptPaths(command: string): string[];
|
package/records.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["src/records.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["src/records.ts"],"names":[],"mappings":"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"}
|
package/records.js
CHANGED
|
@@ -4,12 +4,12 @@ export const flagPermissions = ['conversation', 'prompt', 'model', 'tasks', 'too
|
|
|
4
4
|
const permissionNames = [...listPermissions, ...flagPermissions];
|
|
5
5
|
const hostName = /^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$/;
|
|
6
6
|
const programName = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
|
|
7
|
-
const
|
|
7
|
+
const settingPath = /^[A-Za-z][A-Za-z0-9]*(\.[A-Za-z0-9_@-]+)*$/;
|
|
8
8
|
const listItemHint = {
|
|
9
9
|
network: 'a host alone, such as "api.github.com", or a socket path, such as "/var/run/docker.sock"',
|
|
10
10
|
run: 'a program\'s command, such as "gh"',
|
|
11
11
|
files: 'a path that starts with ~/ or /, such as "~/.zshrc"',
|
|
12
|
-
config: 'a Claude Code setting\'s name, such as "cleanupPeriodDays"',
|
|
12
|
+
config: 'a Claude Code setting\'s name, or a key path inside one, such as "cleanupPeriodDays" or "env.ANTHROPIC_BASE_URL"',
|
|
13
13
|
};
|
|
14
14
|
export const pluginName = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
15
15
|
export function storeFolder(env) {
|
|
@@ -141,7 +141,7 @@ function readPermissions(steps) {
|
|
|
141
141
|
throw new Error(`${at} sets "${name}" to ${JSON.stringify(value)}. Write a list, such as "${name}": ${example}.`);
|
|
142
142
|
for (const each of value) {
|
|
143
143
|
const isPath = /^(~\/|\/)/.test(each) && !each.split('/').includes('..');
|
|
144
|
-
const isValid = { network: hostName.test(each) || isPath, run: programName.test(each), files: isPath, config:
|
|
144
|
+
const isValid = { network: hostName.test(each) || isPath, run: programName.test(each), files: isPath, config: settingPath.test(each) }[name];
|
|
145
145
|
if (isValid !== true)
|
|
146
146
|
throw new Error(`${at} lists "${each}" under "${name}". Write ${listItemHint[name]}.`);
|
|
147
147
|
items.push(`${name}:${each}`);
|
|
@@ -177,6 +177,13 @@ export function permissionWords(item) {
|
|
|
177
177
|
return item;
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
+
export function settingPaths(values) {
|
|
181
|
+
return Object.entries(values).flatMap(([key, value]) => (isObject(value) && Object.keys(value).length > 0 ? settingPaths(value).map((path) => [key, ...path]) : [[key]]));
|
|
182
|
+
}
|
|
183
|
+
export function isSettingCovered(granted, path) {
|
|
184
|
+
const item = `config:${path}`;
|
|
185
|
+
return [...granted].some((each) => item === each || item.startsWith(`${each}.`));
|
|
186
|
+
}
|
|
180
187
|
export const settingsPagesMethod = 'cmod:settingsPages';
|
|
181
188
|
export const changelogPage = 'changelog';
|
|
182
189
|
export const openPageMethod = 'cmod:openPage';
|
|
@@ -220,9 +227,11 @@ function readKeys(keys) {
|
|
|
220
227
|
}
|
|
221
228
|
return { keys: commands };
|
|
222
229
|
}
|
|
223
|
-
export const coreVersion = '1.
|
|
230
|
+
export const coreVersion = '1.6.0';
|
|
224
231
|
export function oldestCmodFor(steps) {
|
|
225
|
-
if (steps.permissions?.some((item) => item
|
|
232
|
+
if (steps.permissions?.some((item) => item.startsWith('config:')) === true)
|
|
233
|
+
return '1.6.0';
|
|
234
|
+
if (steps.permissions?.includes('tasks') === true)
|
|
226
235
|
return '1.0.0';
|
|
227
236
|
if (steps.permissions !== undefined)
|
|
228
237
|
return '0.2.0';
|
package/runtime/grants.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare class PermissionRefused extends Error {
|
|
|
16
16
|
export declare function programAddresses(mod: object): Set<string>;
|
|
17
17
|
export declare function checkGrant(grants: Grants, call: string, item: string | undefined): Promise<void>;
|
|
18
18
|
export declare function checkWrite(grants: Grants, call: string, path: string): Promise<void>;
|
|
19
|
-
export declare function checkSettings(grants: Grants, source: 'user' | 'project' | 'local',
|
|
19
|
+
export declare function checkSettings(grants: Grants, source: 'user' | 'project' | 'local', values: Readonly<Record<string, unknown>>): Promise<void>;
|
|
20
20
|
export declare function checkingGrants(claude: Claude, grants: Grants): Claude;
|
|
21
21
|
export declare function itemOf(name: string, value?: string): string;
|
|
22
22
|
export declare function isCovered(granted: ReadonlySet<string>, item: string, home: string | undefined): boolean;
|
package/runtime/grants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/runtime/grants.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7C,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAA;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,SAAS,MAAM,EAAE,CAAA;IAC7C,QAAQ,CAAC,aAAa,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAA;CAClD,CAAA;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAI/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAIzD;AAkBD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAItG;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/runtime/grants.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7C,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAA;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,SAAS,MAAM,EAAE,CAAA;IAC7C,QAAQ,CAAC,aAAa,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAA;CAClD,CAAA;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAI/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAIzD;AAkBD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAItG;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAElJ;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAkFrE;AAmBD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAWvG;AAiBD,wBAAgB,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9G;AA4BD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1C,CAAA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAa9G;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BxJ"}
|
package/runtime/grants.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isObject, permissionWords } from '../records.js';
|
|
1
|
+
import { isObject, isSettingCovered, permissionWords, settingPaths } from '../records.js';
|
|
2
2
|
import { relativePath } from '../utils/paths.js';
|
|
3
3
|
import { cmodPluginName } from './dependencies.js';
|
|
4
4
|
import { actPermission, conversationMethods, isAct, toolPermission } from './sessions.js';
|
|
@@ -34,9 +34,9 @@ export async function checkGrant(grants, call, item) {
|
|
|
34
34
|
export async function checkWrite(grants, call, path) {
|
|
35
35
|
await checkGrant(grants, call, writeItem(grants, path));
|
|
36
36
|
}
|
|
37
|
-
export async function checkSettings(grants, source,
|
|
38
|
-
for (const
|
|
39
|
-
await checkGrant(grants, `settings.${source}.update(${
|
|
37
|
+
export async function checkSettings(grants, source, values) {
|
|
38
|
+
for (const path of settingPaths(values).map((each) => each.join('.')))
|
|
39
|
+
await checkGrant(grants, `settings.${source}.update(${path})`, `config:${path}`);
|
|
40
40
|
}
|
|
41
41
|
export function checkingGrants(claude, grants) {
|
|
42
42
|
const gate = (call, item) => checkGrant(grants, call, item);
|
|
@@ -147,6 +147,8 @@ export function isCovered(granted, item, home) {
|
|
|
147
147
|
if (split === -1)
|
|
148
148
|
return false;
|
|
149
149
|
const name = item.slice(0, split);
|
|
150
|
+
if (name === 'config')
|
|
151
|
+
return isSettingCovered(granted, item.slice(split + 1));
|
|
150
152
|
const target = expanded(item.slice(split + 1), home);
|
|
151
153
|
const values = [...granted].filter((each) => each.startsWith(`${name}:`)).map((each) => expanded(each.slice(name.length + 1), home));
|
|
152
154
|
if (name === 'run')
|
package/runtime/lifecycle.js
CHANGED
|
@@ -645,7 +645,7 @@ async function createMod(definition, runtime) {
|
|
|
645
645
|
const settingsFile = (source) => ({
|
|
646
646
|
read: () => checked.settings.read({ source }),
|
|
647
647
|
async update(values) {
|
|
648
|
-
await checkSettings(grants, source,
|
|
648
|
+
await checkSettings(grants, source, values);
|
|
649
649
|
await claude.cmod.call({ to: cmodPluginName, method: 'updateSettings', input: { mod: definition.name, source, project: modState.root, values } });
|
|
650
650
|
},
|
|
651
651
|
});
|
package/src/records.ts
CHANGED
|
@@ -26,12 +26,12 @@ export type FlagPermission = (typeof flagPermissions)[number]
|
|
|
26
26
|
const permissionNames: readonly string[] = [...listPermissions, ...flagPermissions]
|
|
27
27
|
const hostName = /^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$/
|
|
28
28
|
const programName = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/
|
|
29
|
-
const
|
|
29
|
+
const settingPath = /^[A-Za-z][A-Za-z0-9]*(\.[A-Za-z0-9_@-]+)*$/
|
|
30
30
|
const listItemHint: Readonly<Record<ListPermission, string>> = {
|
|
31
31
|
network: 'a host alone, such as "api.github.com", or a socket path, such as "/var/run/docker.sock"',
|
|
32
32
|
run: 'a program\'s command, such as "gh"',
|
|
33
33
|
files: 'a path that starts with ~/ or /, such as "~/.zshrc"',
|
|
34
|
-
config: 'a Claude Code setting\'s name, such as "cleanupPeriodDays"',
|
|
34
|
+
config: 'a Claude Code setting\'s name, or a key path inside one, such as "cleanupPeriodDays" or "env.ANTHROPIC_BASE_URL"',
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export type ReadFile = (path: string) => Promise<string | undefined>
|
|
@@ -165,7 +165,7 @@ function readPermissions(steps: Record<string, unknown>): string[] {
|
|
|
165
165
|
if (!Array.isArray(value) || value.length === 0 || !value.every((each) => typeof each === 'string')) throw new Error(`${at} sets "${name}" to ${JSON.stringify(value)}. Write a list, such as "${name}": ${example}.`)
|
|
166
166
|
for (const each of value) {
|
|
167
167
|
const isPath = /^(~\/|\/)/.test(each) && !each.split('/').includes('..')
|
|
168
|
-
const isValid = { network: hostName.test(each) || isPath, run: programName.test(each), files: isPath, config:
|
|
168
|
+
const isValid = { network: hostName.test(each) || isPath, run: programName.test(each), files: isPath, config: settingPath.test(each) }[name]
|
|
169
169
|
if (isValid !== true) throw new Error(`${at} lists "${each}" under "${name}". Write ${listItemHint[name as ListPermission]}.`)
|
|
170
170
|
items.push(`${name}:${each}`)
|
|
171
171
|
}
|
|
@@ -202,6 +202,15 @@ export function permissionWords(item: string): string {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
export function settingPaths(values: Readonly<Record<string, unknown>>): string[][] {
|
|
206
|
+
return Object.entries(values).flatMap(([key, value]) => (isObject(value) && Object.keys(value).length > 0 ? settingPaths(value).map((path) => [key, ...path]) : [[key]]))
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function isSettingCovered(granted: Iterable<string>, path: string): boolean {
|
|
210
|
+
const item = `config:${path}`
|
|
211
|
+
return [...granted].some((each) => item === each || item.startsWith(`${each}.`))
|
|
212
|
+
}
|
|
213
|
+
|
|
205
214
|
export const settingsPagesMethod = 'cmod:settingsPages'
|
|
206
215
|
|
|
207
216
|
export const changelogPage = 'changelog'
|
|
@@ -250,10 +259,11 @@ function readKeys(keys: unknown): Steps {
|
|
|
250
259
|
return { keys: commands }
|
|
251
260
|
}
|
|
252
261
|
|
|
253
|
-
export const coreVersion = '1.
|
|
262
|
+
export const coreVersion = '1.6.0'
|
|
254
263
|
|
|
255
264
|
export function oldestCmodFor(steps: Steps): string {
|
|
256
|
-
if (steps.permissions?.some((item) => item
|
|
265
|
+
if (steps.permissions?.some((item) => item.startsWith('config:')) === true) return '1.6.0'
|
|
266
|
+
if (steps.permissions?.includes('tasks') === true) return '1.0.0'
|
|
257
267
|
if (steps.permissions !== undefined) return '0.2.0'
|
|
258
268
|
return Object.keys(steps.keys ?? {}).length > 0 ? '0.1.12' : '0.0.0'
|
|
259
269
|
}
|
package/src/runtime/grants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isObject, permissionWords } from '../records.js'
|
|
1
|
+
import { isObject, isSettingCovered, permissionWords, settingPaths } from '../records.js'
|
|
2
2
|
import { relativePath } from '../utils/paths.js'
|
|
3
3
|
import type { Claude } from './claude.js'
|
|
4
4
|
import { cmodPluginName } from './dependencies.js'
|
|
@@ -53,8 +53,8 @@ export async function checkWrite(grants: Grants, call: string, path: string): Pr
|
|
|
53
53
|
await checkGrant(grants, call, writeItem(grants, path))
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export async function checkSettings(grants: Grants, source: 'user' | 'project' | 'local',
|
|
57
|
-
for (const
|
|
56
|
+
export async function checkSettings(grants: Grants, source: 'user' | 'project' | 'local', values: Readonly<Record<string, unknown>>): Promise<void> {
|
|
57
|
+
for (const path of settingPaths(values).map((each) => each.join('.'))) await checkGrant(grants, `settings.${source}.update(${path})`, `config:${path}`)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function checkingGrants(claude: Claude, grants: Grants): Claude {
|
|
@@ -167,6 +167,7 @@ export function isCovered(granted: ReadonlySet<string>, item: string, home: stri
|
|
|
167
167
|
const split = item.indexOf(':')
|
|
168
168
|
if (split === -1) return false
|
|
169
169
|
const name = item.slice(0, split)
|
|
170
|
+
if (name === 'config') return isSettingCovered(granted, item.slice(split + 1))
|
|
170
171
|
const target = expanded(item.slice(split + 1), home)
|
|
171
172
|
const values = [...granted].filter((each) => each.startsWith(`${name}:`)).map((each) => expanded(each.slice(name.length + 1), home))
|
|
172
173
|
if (name === 'run') return values.includes('*') || values.includes(target)
|
package/src/runtime/lifecycle.ts
CHANGED
|
@@ -685,7 +685,7 @@ async function createMod<State extends object, Declared extends Options>(definit
|
|
|
685
685
|
const settingsFile = (source: 'user' | 'project' | 'local'): SettingsFile => ({
|
|
686
686
|
read: () => checked.settings.read({ source }),
|
|
687
687
|
async update(values) {
|
|
688
|
-
await checkSettings(grants, source,
|
|
688
|
+
await checkSettings(grants, source, values)
|
|
689
689
|
await claude.cmod.call({ to: cmodPluginName, method: 'updateSettings', input: { mod: definition.name, source, project: modState.root, values } })
|
|
690
690
|
},
|
|
691
691
|
})
|